From 09ec54bbcf6736d6c1457294729d07cdd0a1f6bc Mon Sep 17 00:00:00 2001 From: Michael Hladky Date: Wed, 27 Aug 2025 22:37:29 +0200 Subject: [PATCH 01/38] chore: add plugin to nx.json --- .../tests/plugin-create-nodes.e2e.test.ts | 2 +- packages/nx-plugin/src/index.ts | 19 ++++++++----------- packages/nx-plugin/src/plugin/index.ts | 10 ++++++++++ packages/nx-plugin/src/plugin/plugin.ts | 5 +++-- project.json | 13 +------------ 5 files changed, 23 insertions(+), 26 deletions(-) diff --git a/e2e/nx-plugin-e2e/tests/plugin-create-nodes.e2e.test.ts b/e2e/nx-plugin-e2e/tests/plugin-create-nodes.e2e.test.ts index 7a69a281d..126bee6e2 100644 --- a/e2e/nx-plugin-e2e/tests/plugin-create-nodes.e2e.test.ts +++ b/e2e/nx-plugin-e2e/tests/plugin-create-nodes.e2e.test.ts @@ -229,7 +229,7 @@ describe('nx-plugin', () => { }); it('should consider plugin option projectPrefix in executor target', async () => { - const cwd = path.join(testFileDir, 'configuration-option-bin'); + const cwd = path.join(testFileDir, 'configuration-option-projectPrefix'); registerPluginInWorkspace(tree, { plugin: '@code-pushup/nx-plugin', options: { diff --git a/packages/nx-plugin/src/index.ts b/packages/nx-plugin/src/index.ts index 356be758c..5582aced5 100644 --- a/packages/nx-plugin/src/index.ts +++ b/packages/nx-plugin/src/index.ts @@ -1,15 +1,7 @@ +import type { CreateNodesV2, NxPlugin } from '@nx/devkit'; import { createNodes, createNodesV2 } from './plugin/index.js'; -// default export for nx.json#plugins -const plugin = { - name: '@code-pushup/nx-plugin', - createNodesV2, - // Keep for backwards compatibility with Nx < 21 - createNodes, -}; - -export default plugin; - +export { createNodes, createNodesV2 } from './plugin/index.js'; export type { AutorunCommandExecutorOptions } from './executors/cli/schema.js'; export { objectToCliArgs } from './executors/internal/cli.js'; export { generateCodePushupConfig } from './generators/configuration/code-pushup-config.js'; @@ -22,4 +14,9 @@ export { type ProcessConfig, } from './internal/execute-process.js'; export * from './internal/versions.js'; -export { createNodes, createNodesV2 } from './plugin/index.js'; + +export default { + name: 'code-pushup', + createNodesV2: createNodesV2 as CreateNodesV2, + createNodes, +} satisfies NxPlugin; diff --git a/packages/nx-plugin/src/plugin/index.ts b/packages/nx-plugin/src/plugin/index.ts index 6af7c1076..01e91a2fa 100644 --- a/packages/nx-plugin/src/plugin/index.ts +++ b/packages/nx-plugin/src/plugin/index.ts @@ -1,2 +1,12 @@ +import { NxPlugin } from '@nx/devkit'; +import { createNodesV2 } from './plugin.js'; + export { createNodes, createNodesV2 } from './plugin.js'; export type { CreateNodesOptions } from './types.js'; + +const plugin = { + createNodesV2, + name: 'code-pushup-nx-plugin', +} satisfies NxPlugin; + +export default plugin; diff --git a/packages/nx-plugin/src/plugin/plugin.ts b/packages/nx-plugin/src/plugin/plugin.ts index 1b4e3e2f1..77c4aff94 100644 --- a/packages/nx-plugin/src/plugin/plugin.ts +++ b/packages/nx-plugin/src/plugin/plugin.ts @@ -43,10 +43,11 @@ export const createNodesV2: CreateNodesV2 = [ `**/${PROJECT_JSON_FILE_NAME}`, async ( projectConfigurationFiles: readonly string[], - createNodesOptions: unknown, + createNodesOptions: CreateNodesOptions | undefined, context: CreateNodesContextV2, ): Promise => { - const parsedCreateNodesOptions = createNodesOptions as CreateNodesOptions; + const parsedCreateNodesOptions = + (createNodesOptions as CreateNodesOptions) ?? {}; return await Promise.all( projectConfigurationFiles.map(async projectConfigurationFile => { diff --git a/project.json b/project.json index 1f19a828e..890e19f04 100644 --- a/project.json +++ b/project.json @@ -1,16 +1,5 @@ { "name": "cli-workspace", "$schema": "node_modules/nx/schemas/project-schema.json", - "targets": { - "code-pushup": { - "executor": "nx:run-commands", - "options": { - "command": "node packages/cli/src/index.ts --no-progress --verbose", - "env": { - "NODE_OPTIONS": "--import tsx", - "TSX_TSCONFIG_PATH": "tsconfig.base.json" - } - } - } - } + "targets": {} } From a449ca5a8d833b2f0fb17a24cdf361bdf64ea9f2 Mon Sep 17 00:00:00 2001 From: Michael Hladky Date: Wed, 27 Aug 2025 22:38:21 +0200 Subject: [PATCH 02/38] chore: add plugin to nx.json plugins --- nx.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nx.json b/nx.json index a472f35ed..c631531ed 100644 --- a/nx.json +++ b/nx.json @@ -150,6 +150,9 @@ "releaseTagPattern": "v{version}" }, "plugins": [ + { + "plugin": "@code-pushup/nx-plugin" + }, { "plugin": "@push-based/nx-verdaccio", "options": { From a2c327857e4ea872a3d8aca1c8289d9bd6f20d10 Mon Sep 17 00:00:00 2001 From: Michael Hladky Date: Thu, 28 Aug 2025 11:18:37 +0200 Subject: [PATCH 03/38] refactor: test use latest version --- nx.json | 2 +- package-lock.json | 93 ++++++++++++++++++++++++- package.json | 2 + packages/nx-plugin/src/plugin/plugin.ts | 4 +- 4 files changed, 97 insertions(+), 4 deletions(-) diff --git a/nx.json b/nx.json index c631531ed..c2cba8d92 100644 --- a/nx.json +++ b/nx.json @@ -151,7 +151,7 @@ }, "plugins": [ { - "plugin": "@code-pushup/nx-plugin" + "plugin": "node_modules/@code-pushup/nx-plugin" }, { "plugin": "@push-based/nx-verdaccio", diff --git a/package-lock.json b/package-lock.json index a08a34481..8196d8d44 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,7 +37,9 @@ }, "devDependencies": { "@beaussan/nx-knip": "^0.0.5-15", + "@code-pushup/cli": "^0.77.0", "@code-pushup/eslint-config": "^0.14.2", + "@code-pushup/nx-plugin": "^0.77.0", "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", "@commitlint/config-nx-scopes": "^19.5.0", @@ -2234,6 +2236,47 @@ "url": "https://github.com/sponsors/Borewit" } }, + "node_modules/@code-pushup/cli": { + "version": "0.77.0", + "resolved": "https://registry.npmjs.org/@code-pushup/cli/-/cli-0.77.0.tgz", + "integrity": "sha512-LlRrFMoY/DndC8dGf1oy5j9q4jwG03EYAflLZovi34LgFoJOpaOTTM/1W/ika8zttqgscSnJTcFOt4eYdYdZ3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@code-pushup/core": "0.77.0", + "@code-pushup/models": "0.77.0", + "@code-pushup/utils": "0.77.0", + "ansis": "^3.3.0", + "simple-git": "^3.20.0", + "yargs": "^17.7.2" + }, + "bin": { + "code-pushup": "src/index.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@code-pushup/core": { + "version": "0.77.0", + "resolved": "https://registry.npmjs.org/@code-pushup/core/-/core-0.77.0.tgz", + "integrity": "sha512-MRBP7OfrRlSK5eVxZVtxnzPeskR/6jvpZd63vJ7sEncowwi0kam2VKcGn77XS7dolku0QEX88hoefOyfVIomqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@code-pushup/models": "0.77.0", + "@code-pushup/utils": "0.77.0", + "ansis": "^3.3.0" + }, + "peerDependencies": { + "@code-pushup/portal-client": "^0.15.0" + }, + "peerDependenciesMeta": { + "@code-pushup/portal-client": { + "optional": true + } + } + }, "node_modules/@code-pushup/eslint-config": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/@code-pushup/eslint-config/-/eslint-config-0.14.2.tgz", @@ -2318,6 +2361,32 @@ } } }, + "node_modules/@code-pushup/models": { + "version": "0.77.0", + "resolved": "https://registry.npmjs.org/@code-pushup/models/-/models-0.77.0.tgz", + "integrity": "sha512-fK1TvpBElaXHrz5SMf3T+SmlebmRMAf67hfdOnb9NO3pEoQW9r3v2EQ5i56J9iwIBQpoUNXiB0no87xue+YXpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-material-icons": "^0.1.0", + "zod": "^4.0.5" + } + }, + "node_modules/@code-pushup/nx-plugin": { + "version": "0.77.0", + "resolved": "https://registry.npmjs.org/@code-pushup/nx-plugin/-/nx-plugin-0.77.0.tgz", + "integrity": "sha512-AySOAbDZXwBnjRbA5ZdBNLT1I5Ey8c5anrC2gDw5dAK66jACDtn1YEFR2JkG4n+/9Wn8YT8HissbFFIlEFWsMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@code-pushup/models": "0.77.0", + "@code-pushup/utils": "0.77.0", + "@nx/devkit": ">=17.0.0", + "ansis": "^3.3.0", + "nx": ">=17.0.0", + "zod": "^4.0.5" + } + }, "node_modules/@code-pushup/portal-client": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/@code-pushup/portal-client/-/portal-client-0.15.0.tgz", @@ -2330,6 +2399,29 @@ "vscode-material-icons": "^0.1.0" } }, + "node_modules/@code-pushup/utils": { + "version": "0.77.0", + "resolved": "https://registry.npmjs.org/@code-pushup/utils/-/utils-0.77.0.tgz", + "integrity": "sha512-wveTiqBoPQs1kUP5msJ+vq3TB3jUOBR1FhSMxSsMo/0KX605oZA3Rx2s9OSmtvMoi7FzChH3mZKNW3MGBnIEZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@code-pushup/models": "0.77.0", + "@isaacs/cliui": "^8.0.2", + "@poppinss/cliui": "^6.4.0", + "ansis": "^3.3.0", + "build-md": "^0.4.2", + "bundle-require": "^5.1.0", + "esbuild": "^0.25.2", + "multi-progress-bars": "^5.0.3", + "semver": "^7.6.0", + "simple-git": "^3.20.0", + "zod": "^4.0.5" + }, + "engines": { + "node": ">=17.0.0" + } + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -8193,7 +8285,6 @@ "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ "win32" diff --git a/package.json b/package.json index e1f97c332..867832fe3 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,9 @@ }, "devDependencies": { "@beaussan/nx-knip": "^0.0.5-15", + "@code-pushup/cli": "^0.77.0", "@code-pushup/eslint-config": "^0.14.2", + "@code-pushup/nx-plugin": "^0.77.0", "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", "@commitlint/config-nx-scopes": "^19.5.0", diff --git a/packages/nx-plugin/src/plugin/plugin.ts b/packages/nx-plugin/src/plugin/plugin.ts index 77c4aff94..489468558 100644 --- a/packages/nx-plugin/src/plugin/plugin.ts +++ b/packages/nx-plugin/src/plugin/plugin.ts @@ -39,11 +39,11 @@ export const createNodes: CreateNodes = [ }, ]; -export const createNodesV2: CreateNodesV2 = [ +export const createNodesV2: CreateNodesV2 = [ `**/${PROJECT_JSON_FILE_NAME}`, async ( projectConfigurationFiles: readonly string[], - createNodesOptions: CreateNodesOptions | undefined, + createNodesOptions: unknown, context: CreateNodesContextV2, ): Promise => { const parsedCreateNodesOptions = From 5ec4034484a760e7464c4355c9c4b52f6ff1e3d7 Mon Sep 17 00:00:00 2001 From: Michael Hladky Date: Thu, 28 Aug 2025 11:23:51 +0200 Subject: [PATCH 04/38] refactor: test use latest version 2 --- nx.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nx.json b/nx.json index c2cba8d92..c631531ed 100644 --- a/nx.json +++ b/nx.json @@ -151,7 +151,7 @@ }, "plugins": [ { - "plugin": "node_modules/@code-pushup/nx-plugin" + "plugin": "@code-pushup/nx-plugin" }, { "plugin": "@push-based/nx-verdaccio", From 93e20e8453b965027585c19948d9940ce7aab2aa Mon Sep 17 00:00:00 2001 From: Michael Hladky Date: Mon, 1 Sep 2025 21:15:07 +0200 Subject: [PATCH 05/38] refactor: fix lint --- nx.json | 2 +- packages/nx-plugin/src/plugin/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nx.json b/nx.json index b97bdc630..0bdaac10e 100644 --- a/nx.json +++ b/nx.json @@ -336,7 +336,7 @@ }, "plugins": [ { - "plugin": "@code-pushup/nx-plugin" + "plugin": "node_modules/@code-pushup/nx-plugin/src/index.js" }, { "plugin": "@push-based/nx-verdaccio", diff --git a/packages/nx-plugin/src/plugin/index.ts b/packages/nx-plugin/src/plugin/index.ts index 01e91a2fa..4f21f2a12 100644 --- a/packages/nx-plugin/src/plugin/index.ts +++ b/packages/nx-plugin/src/plugin/index.ts @@ -1,4 +1,4 @@ -import { NxPlugin } from '@nx/devkit'; +import type { NxPlugin } from '@nx/devkit'; import { createNodesV2 } from './plugin.js'; export { createNodes, createNodesV2 } from './plugin.js'; From 3cb3a8d924ecfb6fcae3e53d37ff72c467e5f9b2 Mon Sep 17 00:00:00 2001 From: Michael Hladky Date: Mon, 1 Sep 2025 21:19:45 +0200 Subject: [PATCH 06/38] refactor: wip --- nx.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nx.json b/nx.json index 0bdaac10e..b97bdc630 100644 --- a/nx.json +++ b/nx.json @@ -336,7 +336,7 @@ }, "plugins": [ { - "plugin": "node_modules/@code-pushup/nx-plugin/src/index.js" + "plugin": "@code-pushup/nx-plugin" }, { "plugin": "@push-based/nx-verdaccio", From 00e6fae16285ee06a6229381776507c8b73acaa4 Mon Sep 17 00:00:00 2001 From: Michael Hladky Date: Mon, 1 Sep 2025 21:51:28 +0200 Subject: [PATCH 07/38] refactor: wip 2 --- code-pushup.config.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code-pushup.config.ts b/code-pushup.config.ts index 4ea96774f..0f0f669fa 100644 --- a/code-pushup.config.ts +++ b/code-pushup.config.ts @@ -20,6 +20,9 @@ const config: CoreConfig = { server: 'https://api.staging.code-pushup.dev/graphql', apiKey: process.env['CP_API_KEY'], }, + persist: { + outputDir: '.code-pushup', + }, }), plugins: [], }; From 99d988ad4f8bb98040788ca7011b87d1d8ee852f Mon Sep 17 00:00:00 2001 From: Michael Hladky Date: Wed, 3 Sep 2025 00:56:29 +0200 Subject: [PATCH 08/38] refactor: update packages --- package-lock.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package-lock.json b/package-lock.json index 60304216a..820e3538f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8285,6 +8285,7 @@ "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" From 0e66ac87cbbb1fdd4e0532fedd875f9179507c43 Mon Sep 17 00:00:00 2001 From: Michael Hladky Date: Wed, 3 Sep 2025 15:45:15 +0200 Subject: [PATCH 09/38] chore: update packages --- package-lock.json | 50 +++++++++++++++++++++++------------------------ package.json | 4 ++-- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/package-lock.json b/package-lock.json index 820e3538f..e5940de2e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,9 +37,9 @@ }, "devDependencies": { "@beaussan/nx-knip": "^0.0.5-15", - "@code-pushup/cli": "^0.77.0", + "@code-pushup/cli": "^0.78.0", "@code-pushup/eslint-config": "^0.14.2", - "@code-pushup/nx-plugin": "^0.77.0", + "@code-pushup/nx-plugin": "^0.78.0", "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", "@commitlint/config-nx-scopes": "^19.5.0", @@ -2237,15 +2237,15 @@ } }, "node_modules/@code-pushup/cli": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@code-pushup/cli/-/cli-0.77.0.tgz", - "integrity": "sha512-LlRrFMoY/DndC8dGf1oy5j9q4jwG03EYAflLZovi34LgFoJOpaOTTM/1W/ika8zttqgscSnJTcFOt4eYdYdZ3w==", + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@code-pushup/cli/-/cli-0.78.0.tgz", + "integrity": "sha512-S7AL3QJUpCSuPYv4Sab14pFP7RQECA8carKnYgixSQtep7+6rTfWt8d0DQXAAAwi2KiJpzCloBt6pXeAnBkA/w==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/core": "0.77.0", - "@code-pushup/models": "0.77.0", - "@code-pushup/utils": "0.77.0", + "@code-pushup/core": "0.78.0", + "@code-pushup/models": "0.78.0", + "@code-pushup/utils": "0.78.0", "ansis": "^3.3.0", "simple-git": "^3.20.0", "yargs": "^17.7.2" @@ -2258,14 +2258,14 @@ } }, "node_modules/@code-pushup/core": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@code-pushup/core/-/core-0.77.0.tgz", - "integrity": "sha512-MRBP7OfrRlSK5eVxZVtxnzPeskR/6jvpZd63vJ7sEncowwi0kam2VKcGn77XS7dolku0QEX88hoefOyfVIomqw==", + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@code-pushup/core/-/core-0.78.0.tgz", + "integrity": "sha512-iuz41y2cx1QaIcI9c9X+qTDxsCGtAGcexieIaX8OX2ldPgKBpf37yrxboBKGe+ekdLZA/KtIDaqTIK3FdRFauA==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/models": "0.77.0", - "@code-pushup/utils": "0.77.0", + "@code-pushup/models": "0.78.0", + "@code-pushup/utils": "0.78.0", "ansis": "^3.3.0" }, "peerDependencies": { @@ -2362,9 +2362,9 @@ } }, "node_modules/@code-pushup/models": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@code-pushup/models/-/models-0.77.0.tgz", - "integrity": "sha512-fK1TvpBElaXHrz5SMf3T+SmlebmRMAf67hfdOnb9NO3pEoQW9r3v2EQ5i56J9iwIBQpoUNXiB0no87xue+YXpQ==", + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@code-pushup/models/-/models-0.78.0.tgz", + "integrity": "sha512-6h4Ix2q0IENz1fDrPcoMZnqPd+Mo2iyOBvKoZRW0HhCISE5qb+t675E3GisPc9XOjlNBliW81b2gMm3oQXihsA==", "dev": true, "license": "MIT", "dependencies": { @@ -2373,14 +2373,14 @@ } }, "node_modules/@code-pushup/nx-plugin": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@code-pushup/nx-plugin/-/nx-plugin-0.77.0.tgz", - "integrity": "sha512-AySOAbDZXwBnjRbA5ZdBNLT1I5Ey8c5anrC2gDw5dAK66jACDtn1YEFR2JkG4n+/9Wn8YT8HissbFFIlEFWsMA==", + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@code-pushup/nx-plugin/-/nx-plugin-0.78.0.tgz", + "integrity": "sha512-9399JJIfWH2Yv4x5V4Ea7v2AqqqchNV90jFiW27P8ja+rsNadd+OCPtFqkp9HNSVfOBZhdV/QuaI7yxNg8Guwg==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/models": "0.77.0", - "@code-pushup/utils": "0.77.0", + "@code-pushup/models": "0.78.0", + "@code-pushup/utils": "0.78.0", "@nx/devkit": ">=17.0.0", "ansis": "^3.3.0", "nx": ">=17.0.0", @@ -2400,13 +2400,13 @@ } }, "node_modules/@code-pushup/utils": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@code-pushup/utils/-/utils-0.77.0.tgz", - "integrity": "sha512-wveTiqBoPQs1kUP5msJ+vq3TB3jUOBR1FhSMxSsMo/0KX605oZA3Rx2s9OSmtvMoi7FzChH3mZKNW3MGBnIEZw==", + "version": "0.78.0", + "resolved": "https://registry.npmjs.org/@code-pushup/utils/-/utils-0.78.0.tgz", + "integrity": "sha512-1Jy3Eep/d/GpSa+ok8GMzBCCmKqKBjGQZLBfP3BWwza3q8rn3h/NS/XUuwv//rqIwa23olqV0ygL6preeU5iqQ==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/models": "0.77.0", + "@code-pushup/models": "0.78.0", "@isaacs/cliui": "^8.0.2", "@poppinss/cliui": "^6.4.0", "ansis": "^3.3.0", diff --git a/package.json b/package.json index ca58b7109..a6cc3c7d7 100644 --- a/package.json +++ b/package.json @@ -50,9 +50,9 @@ }, "devDependencies": { "@beaussan/nx-knip": "^0.0.5-15", - "@code-pushup/cli": "^0.77.0", + "@code-pushup/cli": "^0.78.0", "@code-pushup/eslint-config": "^0.14.2", - "@code-pushup/nx-plugin": "^0.77.0", + "@code-pushup/nx-plugin": "^0.78.0", "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", "@commitlint/config-nx-scopes": "^19.5.0", From f402ecbb99d1766e1dff241aa51e35d59c5ed9bd Mon Sep 17 00:00:00 2001 From: Michael Hladky Date: Wed, 3 Sep 2025 17:51:57 +0200 Subject: [PATCH 10/38] refactor: use process.stdout.write --- packages/nx-plugin/src/executors/internal/cli.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nx-plugin/src/executors/internal/cli.ts b/packages/nx-plugin/src/executors/internal/cli.ts index bab74a8f1..330c67fa5 100644 --- a/packages/nx-plugin/src/executors/internal/cli.ts +++ b/packages/nx-plugin/src/executors/internal/cli.ts @@ -26,7 +26,7 @@ export function createCliCommandObject(options?: { logger.error(error.message); }, onStdout: data => { - logger.log(data); + process.stdout.write(data); }, }, }; From 41bce2b150f48c4ba805341b75b459222738e862 Mon Sep 17 00:00:00 2001 From: Michael Hladky Date: Wed, 3 Sep 2025 18:12:09 +0200 Subject: [PATCH 11/38] refactor: test pkg new --- package-lock.json | 20 ++++++++++---------- package.json | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index e5940de2e..42b172225 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,7 +39,7 @@ "@beaussan/nx-knip": "^0.0.5-15", "@code-pushup/cli": "^0.78.0", "@code-pushup/eslint-config": "^0.14.2", - "@code-pushup/nx-plugin": "^0.78.0", + "@code-pushup/nx-plugin": "https://pkg.pr.new/code-pushup/cli/@code-pushup/nx-plugin@1091", "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", "@commitlint/config-nx-scopes": "^19.5.0", @@ -2363,8 +2363,8 @@ }, "node_modules/@code-pushup/models": { "version": "0.78.0", - "resolved": "https://registry.npmjs.org/@code-pushup/models/-/models-0.78.0.tgz", - "integrity": "sha512-6h4Ix2q0IENz1fDrPcoMZnqPd+Mo2iyOBvKoZRW0HhCISE5qb+t675E3GisPc9XOjlNBliW81b2gMm3oQXihsA==", + "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@f402ecbb99d1766e1dff241aa51e35d59c5ed9bd", + "integrity": "sha512-d5CgMFyRfwQV59ORFZk0LALbWinviisAK+yiyneYRLYIlodlfpLOCEu4vN0dJAXKydJKDREz/4w2YVi2eL+SIA==", "dev": true, "license": "MIT", "dependencies": { @@ -2374,13 +2374,13 @@ }, "node_modules/@code-pushup/nx-plugin": { "version": "0.78.0", - "resolved": "https://registry.npmjs.org/@code-pushup/nx-plugin/-/nx-plugin-0.78.0.tgz", - "integrity": "sha512-9399JJIfWH2Yv4x5V4Ea7v2AqqqchNV90jFiW27P8ja+rsNadd+OCPtFqkp9HNSVfOBZhdV/QuaI7yxNg8Guwg==", + "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/nx-plugin@1091", + "integrity": "sha512-eqddemcqQYSreDAYrsPgCmqDK5Dsj6XWtPwgHe1VU1kwzA84kYdsgO4RvMgE+43YNOisZl1A8uovFj9A7dybzA==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/models": "0.78.0", - "@code-pushup/utils": "0.78.0", + "@code-pushup/models": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@f402ecbb99d1766e1dff241aa51e35d59c5ed9bd", + "@code-pushup/utils": "https://pkg.pr.new/code-pushup/cli/@code-pushup/utils@f402ecbb99d1766e1dff241aa51e35d59c5ed9bd", "@nx/devkit": ">=17.0.0", "ansis": "^3.3.0", "nx": ">=17.0.0", @@ -2401,12 +2401,12 @@ }, "node_modules/@code-pushup/utils": { "version": "0.78.0", - "resolved": "https://registry.npmjs.org/@code-pushup/utils/-/utils-0.78.0.tgz", - "integrity": "sha512-1Jy3Eep/d/GpSa+ok8GMzBCCmKqKBjGQZLBfP3BWwza3q8rn3h/NS/XUuwv//rqIwa23olqV0ygL6preeU5iqQ==", + "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/utils@f402ecbb99d1766e1dff241aa51e35d59c5ed9bd", + "integrity": "sha512-hiwRu+svgBpcslnr1IGq8ix+uxBfJeE4DhoBl4E0nbBRkp9FTmlZtQrqZezXlrvQI87SagfRsnlSY+A8aYcgjw==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/models": "0.78.0", + "@code-pushup/models": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@f402ecbb99d1766e1dff241aa51e35d59c5ed9bd", "@isaacs/cliui": "^8.0.2", "@poppinss/cliui": "^6.4.0", "ansis": "^3.3.0", diff --git a/package.json b/package.json index a6cc3c7d7..9c89e4d21 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "@beaussan/nx-knip": "^0.0.5-15", "@code-pushup/cli": "^0.78.0", "@code-pushup/eslint-config": "^0.14.2", - "@code-pushup/nx-plugin": "^0.78.0", + "@code-pushup/nx-plugin": "https://pkg.pr.new/code-pushup/cli/@code-pushup/nx-plugin@1091", "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", "@commitlint/config-nx-scopes": "^19.5.0", From 59a3f833515a1dc15b63c26fbc94311d80e3a699 Mon Sep 17 00:00:00 2001 From: Michael Hladky Date: Wed, 3 Sep 2025 18:57:47 +0200 Subject: [PATCH 12/38] refactor: add output arg handling to nx-plugin args --- .../nx-plugin/src/executors/cli/schema.json | 4 +++ .../nx-plugin/src/executors/cli/schema.ts | 4 ++- packages/nx-plugin/src/executors/cli/utils.ts | 3 ++- .../src/executors/cli/utils.unit.test.ts | 27 +++++++++++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/packages/nx-plugin/src/executors/cli/schema.json b/packages/nx-plugin/src/executors/cli/schema.json index e494c4cc0..3e64e0c3f 100644 --- a/packages/nx-plugin/src/executors/cli/schema.json +++ b/packages/nx-plugin/src/executors/cli/schema.json @@ -44,6 +44,10 @@ "type": "string", "description": "Prefix for project name" }, + "output": { + "type": "string", + "description": "Config output path" + }, "persist": { "type": "object", "properties": { diff --git a/packages/nx-plugin/src/executors/cli/schema.ts b/packages/nx-plugin/src/executors/cli/schema.ts index d73a394b6..1fa8b8769 100644 --- a/packages/nx-plugin/src/executors/cli/schema.ts +++ b/packages/nx-plugin/src/executors/cli/schema.ts @@ -6,6 +6,7 @@ import type { ProjectExecutorOnlyOptions, } from '../internal/types.js'; +export type PrintConfigOptions = { output?: string }; export type AutorunCommandExecutorOnlyOptions = ProjectExecutorOnlyOptions & CollectExecutorOnlyOptions & GeneralExecutorOnlyOptions; @@ -16,4 +17,5 @@ export type AutorunCommandExecutorOptions = Partial< persist: Partial; } & AutorunCommandExecutorOnlyOptions & GlobalExecutorOptions ->; +> & + PrintConfigOptions; diff --git a/packages/nx-plugin/src/executors/cli/utils.ts b/packages/nx-plugin/src/executors/cli/utils.ts index afcca4542..cc154d9d3 100644 --- a/packages/nx-plugin/src/executors/cli/utils.ts +++ b/packages/nx-plugin/src/executors/cli/utils.ts @@ -24,7 +24,7 @@ export function parseAutorunExecutorOptions( options: Partial, normalizedContext: NormalizedExecutorContext, ): AutorunCommandExecutorOptions { - const { projectPrefix, persist, upload, command } = options; + const { projectPrefix, persist, upload, command, output } = options; const needsUploadParams = command === 'upload' || command === 'autorun' || command === undefined; const uploadCfg = uploadConfig( @@ -35,6 +35,7 @@ export function parseAutorunExecutorOptions( return { ...parseAutorunExecutorOnlyOptions(options), ...globalConfig(options, normalizedContext), + ...(output ? { output } : {}), persist: persistConfig({ projectPrefix, ...persist }, normalizedContext), // @TODO This is a hack to avoid validation errors of upload config for commands that dont need it. // Fix: use utils and execute the core logic directly diff --git a/packages/nx-plugin/src/executors/cli/utils.unit.test.ts b/packages/nx-plugin/src/executors/cli/utils.unit.test.ts index 7a4141eff..dd0dd6235 100644 --- a/packages/nx-plugin/src/executors/cli/utils.unit.test.ts +++ b/packages/nx-plugin/src/executors/cli/utils.unit.test.ts @@ -153,6 +153,33 @@ describe('parseAutorunExecutorOptions', () => { ); }, ); + + it.each(['print-config'])( + 'should include output config for command %s', + command => { + const projectName = 'my-app'; + const executorOptions = parseAutorunExecutorOptions( + { + command, + output: 'code-pushup.config.json', + }, + { + projectName, + workspaceRoot: 'workspaceRoot', + projectConfig: { + name: 'my-app', + root: 'root', + }, + }, + ); + + expect(executorOptions).toEqual( + expect.objectContaining({ + output: 'code-pushup.config.json', + }), + ); + }, + ); }); describe('mergeExecutorOptions', () => { From 06c94d964b69c765cc61ff51d4aa3ca216b893bc Mon Sep 17 00:00:00 2001 From: Michael Hladky Date: Thu, 4 Sep 2025 16:08:05 +0200 Subject: [PATCH 13/38] docs: add release steps --- CONTRIBUTING.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e8c8e23a5..e85042746 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -83,6 +83,20 @@ Therefore, PRs are merged via one of two strategies: - rebase - branch cannot contain merge commits ([rebase instead of merge](https://www.atlassian.com/git/tutorials/merging-vs-rebasing)), - squash - single commit whose message is the PR title (should be in conventional commit format). +## Releases + +We use nx release command to create releases for GitHub as well as publish to npm. + +**Steps:** + +- `git checkout main`, `git pull` +- (recommended optional) `npx nx release --dryRun` +- `npx nx release` and confirm publish prompt + +**Env variable in `.env`:** + +- `GITHUB_TOKEN=ghp_...` - to create a GitHub Release + ## Project tags [Nx tags](https://nx.dev/core-features/enforce-module-boundaries) are used to enforce module boundaries in the project graph when linting. From e7da7ef217f84ab0ff825f7980a107b55600dd31 Mon Sep 17 00:00:00 2001 From: Michael Hladky Date: Thu, 4 Sep 2025 16:22:38 +0200 Subject: [PATCH 14/38] chore: update packages --- package-lock.json | 74 +++++++++++++++++++++++++++-------------------- package.json | 6 ++-- 2 files changed, 45 insertions(+), 35 deletions(-) diff --git a/package-lock.json b/package-lock.json index 98a627c23..d8ae904b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,6 @@ "version": "0.0.0", "license": "MIT", "dependencies": { - "@code-pushup/portal-client": "^0.16.0", "@isaacs/cliui": "^8.0.2", "@nx/devkit": "21.4.1", "@poppinss/cliui": "6.4.1", @@ -37,9 +36,10 @@ }, "devDependencies": { "@beaussan/nx-knip": "^0.0.5-15", - "@code-pushup/cli": "^0.78.0", + "@code-pushup/cli": "^0.79.0", "@code-pushup/eslint-config": "^0.14.2", - "@code-pushup/nx-plugin": "https://pkg.pr.new/code-pushup/cli/@code-pushup/nx-plugin@1091", + "@code-pushup/nx-plugin": "^0.79.0", + "@code-pushup/portal-client": "^0.16.0", "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", "@commitlint/config-nx-scopes": "^19.5.0", @@ -2237,15 +2237,15 @@ } }, "node_modules/@code-pushup/cli": { - "version": "0.78.0", - "resolved": "https://registry.npmjs.org/@code-pushup/cli/-/cli-0.78.0.tgz", - "integrity": "sha512-S7AL3QJUpCSuPYv4Sab14pFP7RQECA8carKnYgixSQtep7+6rTfWt8d0DQXAAAwi2KiJpzCloBt6pXeAnBkA/w==", + "version": "0.79.0", + "resolved": "https://registry.npmjs.org/@code-pushup/cli/-/cli-0.79.0.tgz", + "integrity": "sha512-5CSB3w8kRFr58fW/JwnLQzi0jRSzLfSVvYygGrEbJOPdoOgVEt09wS7A1kzpEBtCLYvCUJmYTSEKA/EF74rI3Q==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/core": "0.78.0", - "@code-pushup/models": "0.78.0", - "@code-pushup/utils": "0.78.0", + "@code-pushup/core": "0.79.0", + "@code-pushup/models": "0.79.0", + "@code-pushup/utils": "0.79.0", "ansis": "^3.3.0", "simple-git": "^3.20.0", "yargs": "^17.7.2" @@ -2258,18 +2258,18 @@ } }, "node_modules/@code-pushup/core": { - "version": "0.78.0", - "resolved": "https://registry.npmjs.org/@code-pushup/core/-/core-0.78.0.tgz", - "integrity": "sha512-iuz41y2cx1QaIcI9c9X+qTDxsCGtAGcexieIaX8OX2ldPgKBpf37yrxboBKGe+ekdLZA/KtIDaqTIK3FdRFauA==", + "version": "0.79.0", + "resolved": "https://registry.npmjs.org/@code-pushup/core/-/core-0.79.0.tgz", + "integrity": "sha512-dX/zxec84/nGo10OODmHNElaCUm5cSM3jIiCCnkNYOcdO/Bk2clJVBZdwb2HaJiTfodKPqUiX9qRneIX4PJ3FA==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/models": "0.78.0", - "@code-pushup/utils": "0.78.0", + "@code-pushup/models": "0.79.0", + "@code-pushup/utils": "0.79.0", "ansis": "^3.3.0" }, "peerDependencies": { - "@code-pushup/portal-client": "^0.15.0" + "@code-pushup/portal-client": "^0.16.0" }, "peerDependenciesMeta": { "@code-pushup/portal-client": { @@ -2362,9 +2362,9 @@ } }, "node_modules/@code-pushup/models": { - "version": "0.78.0", - "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@f402ecbb99d1766e1dff241aa51e35d59c5ed9bd", - "integrity": "sha512-d5CgMFyRfwQV59ORFZk0LALbWinviisAK+yiyneYRLYIlodlfpLOCEu4vN0dJAXKydJKDREz/4w2YVi2eL+SIA==", + "version": "0.79.0", + "resolved": "https://registry.npmjs.org/@code-pushup/models/-/models-0.79.0.tgz", + "integrity": "sha512-0DTca8Eak+jybXLtBDvDDxz+LFsrq1g41daDzAKM6gF+/Vk4elPmHcGNhaMec25L/gowOPXgI9JGJfHqTPZtmg==", "dev": true, "license": "MIT", "dependencies": { @@ -2373,14 +2373,14 @@ } }, "node_modules/@code-pushup/nx-plugin": { - "version": "0.78.0", - "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/nx-plugin@1091", - "integrity": "sha512-eqddemcqQYSreDAYrsPgCmqDK5Dsj6XWtPwgHe1VU1kwzA84kYdsgO4RvMgE+43YNOisZl1A8uovFj9A7dybzA==", + "version": "0.79.0", + "resolved": "https://registry.npmjs.org/@code-pushup/nx-plugin/-/nx-plugin-0.79.0.tgz", + "integrity": "sha512-QqmkeUWOVpuYDGknm7QSxKo6iIsxALmvHYMmIoMbvS9LIxUitW0hjBD6OWzMUhHlxlg6zQQHuMu4G51fY2AtGg==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/models": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@f402ecbb99d1766e1dff241aa51e35d59c5ed9bd", - "@code-pushup/utils": "https://pkg.pr.new/code-pushup/cli/@code-pushup/utils@f402ecbb99d1766e1dff241aa51e35d59c5ed9bd", + "@code-pushup/models": "0.79.0", + "@code-pushup/utils": "0.79.0", "@nx/devkit": ">=17.0.0", "ansis": "^3.3.0", "nx": ">=17.0.0", @@ -2391,6 +2391,7 @@ "version": "0.16.0", "resolved": "https://registry.npmjs.org/@code-pushup/portal-client/-/portal-client-0.16.0.tgz", "integrity": "sha512-JlMRcTKkJygVfLS+IWQxDRDnvF64p4q+QDLIXzQPep6X99C1OH3MnA9jbfGAOew/3xqOILCrifn0y54fyRs8Qg==", + "dev": true, "license": "MIT", "dependencies": { "graphql": "^16.6.0", @@ -2400,13 +2401,13 @@ } }, "node_modules/@code-pushup/utils": { - "version": "0.78.0", - "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/utils@f402ecbb99d1766e1dff241aa51e35d59c5ed9bd", - "integrity": "sha512-hiwRu+svgBpcslnr1IGq8ix+uxBfJeE4DhoBl4E0nbBRkp9FTmlZtQrqZezXlrvQI87SagfRsnlSY+A8aYcgjw==", + "version": "0.79.0", + "resolved": "https://registry.npmjs.org/@code-pushup/utils/-/utils-0.79.0.tgz", + "integrity": "sha512-7QXkdszu+DftpLQcK6H3Gnad/vn3odkz8TMMf8cBCgENMVb6KpJCtiahs10uxFqvINwxXYqIZVktylZAuUnxdg==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/models": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@f402ecbb99d1766e1dff241aa51e35d59c5ed9bd", + "@code-pushup/models": "0.79.0", "@isaacs/cliui": "^8.0.2", "@poppinss/cliui": "^6.4.0", "ansis": "^3.3.0", @@ -3356,6 +3357,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "dev": true, "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } @@ -14086,6 +14088,7 @@ "version": "3.1.8", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", + "dev": true, "dependencies": { "node-fetch": "^2.6.12" } @@ -15068,7 +15071,7 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "iconv-lite": "^0.6.2" @@ -15078,7 +15081,7 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -18138,6 +18141,7 @@ "version": "16.9.0", "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz", "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==", + "dev": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -18146,6 +18150,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-6.1.0.tgz", "integrity": "sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==", + "dev": true, "dependencies": { "@graphql-typed-document-node/core": "^3.2.0", "cross-fetch": "^3.1.5" @@ -18158,6 +18163,7 @@ "version": "2.12.6", "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==", + "dev": true, "dependencies": { "tslib": "^2.1.0" }, @@ -23443,6 +23449,7 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, "dependencies": { "whatwg-url": "^5.0.0" }, @@ -23461,17 +23468,20 @@ "node_modules/node-fetch/node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true }, "node_modules/node-fetch/node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true }, "node_modules/node-fetch/node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -27125,7 +27135,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "devOptional": true + "dev": true }, "node_modules/saxes": { "version": "6.0.0", diff --git a/package.json b/package.json index ddd5ecfd6..2fc790a24 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,6 @@ "node": ">=22.14" }, "dependencies": { - "@code-pushup/portal-client": "^0.16.0", "@isaacs/cliui": "^8.0.2", "@nx/devkit": "21.4.1", "@poppinss/cliui": "6.4.1", @@ -49,10 +48,11 @@ "zod": "^4.0.5" }, "devDependencies": { + "@code-pushup/portal-client": "^0.16.0", "@beaussan/nx-knip": "^0.0.5-15", - "@code-pushup/cli": "^0.78.0", + "@code-pushup/cli": "^0.79.0", "@code-pushup/eslint-config": "^0.14.2", - "@code-pushup/nx-plugin": "https://pkg.pr.new/code-pushup/cli/@code-pushup/nx-plugin@1091", + "@code-pushup/nx-plugin": "^0.79.0", "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", "@commitlint/config-nx-scopes": "^19.5.0", From 8f69a5ebb6745a9b81b192daebcd0c80d83f2ec0 Mon Sep 17 00:00:00 2001 From: Michael Hladky Date: Thu, 4 Sep 2025 16:27:57 +0200 Subject: [PATCH 15/38] docs: update release section --- CONTRIBUTING.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e85042746..b25c3f7c8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -87,6 +87,10 @@ Therefore, PRs are merged via one of two strategies: We use nx release command to create releases for GitHub as well as publish to npm. +**Preconditions:** + +- `npm login` - Only users with write access to [code-pushup](https://www.npmjs.com/org/code-pushup) can publish + **Steps:** - `git checkout main`, `git pull` From 8dced0b692f2683f6d50641efc38dc04bac2ce58 Mon Sep 17 00:00:00 2001 From: Michael Hladky Date: Thu, 4 Sep 2025 16:53:18 +0200 Subject: [PATCH 16/38] docs: update release section --- CONTRIBUTING.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b25c3f7c8..060d789d4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -90,6 +90,7 @@ We use nx release command to create releases for GitHub as well as publish to np **Preconditions:** - `npm login` - Only users with write access to [code-pushup](https://www.npmjs.com/org/code-pushup) can publish +- (optional) `GITHUB_TOKEN=ghp_...` in `.env` - [Personal access token](https://github.com/settings/personal-access-tokens/new) to create a GitHub release. **Steps:** @@ -97,10 +98,6 @@ We use nx release command to create releases for GitHub as well as publish to np - (recommended optional) `npx nx release --dryRun` - `npx nx release` and confirm publish prompt -**Env variable in `.env`:** - -- `GITHUB_TOKEN=ghp_...` - to create a GitHub Release - ## Project tags [Nx tags](https://nx.dev/core-features/enforce-module-boundaries) are used to enforce module boundaries in the project graph when linting. From 0049a643957e1c10061757504040cae1f3cba13a Mon Sep 17 00:00:00 2001 From: Michael Hladky Date: Thu, 4 Sep 2025 17:35:35 +0200 Subject: [PATCH 17/38] chore: update packags --- package-lock.json | 50 +++++++++++++++++++++++------------------------ package.json | 4 ++-- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/package-lock.json b/package-lock.json index d8ae904b4..875e34af7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,9 +36,9 @@ }, "devDependencies": { "@beaussan/nx-knip": "^0.0.5-15", - "@code-pushup/cli": "^0.79.0", + "@code-pushup/cli": "^0.79.1", "@code-pushup/eslint-config": "^0.14.2", - "@code-pushup/nx-plugin": "^0.79.0", + "@code-pushup/nx-plugin": "^0.79.1", "@code-pushup/portal-client": "^0.16.0", "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", @@ -2237,15 +2237,15 @@ } }, "node_modules/@code-pushup/cli": { - "version": "0.79.0", - "resolved": "https://registry.npmjs.org/@code-pushup/cli/-/cli-0.79.0.tgz", - "integrity": "sha512-5CSB3w8kRFr58fW/JwnLQzi0jRSzLfSVvYygGrEbJOPdoOgVEt09wS7A1kzpEBtCLYvCUJmYTSEKA/EF74rI3Q==", + "version": "0.79.1", + "resolved": "https://registry.npmjs.org/@code-pushup/cli/-/cli-0.79.1.tgz", + "integrity": "sha512-rV5IjoAlcV86sFby2t/9mi7Vkwn/CiqA+wPvnQeoWLjPJBDVOuDl9HA1dwUcaynYZjQPB9IpBpThP7tMfBE/vw==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/core": "0.79.0", - "@code-pushup/models": "0.79.0", - "@code-pushup/utils": "0.79.0", + "@code-pushup/core": "0.79.1", + "@code-pushup/models": "0.79.1", + "@code-pushup/utils": "0.79.1", "ansis": "^3.3.0", "simple-git": "^3.20.0", "yargs": "^17.7.2" @@ -2258,14 +2258,14 @@ } }, "node_modules/@code-pushup/core": { - "version": "0.79.0", - "resolved": "https://registry.npmjs.org/@code-pushup/core/-/core-0.79.0.tgz", - "integrity": "sha512-dX/zxec84/nGo10OODmHNElaCUm5cSM3jIiCCnkNYOcdO/Bk2clJVBZdwb2HaJiTfodKPqUiX9qRneIX4PJ3FA==", + "version": "0.79.1", + "resolved": "https://registry.npmjs.org/@code-pushup/core/-/core-0.79.1.tgz", + "integrity": "sha512-RNA8uQ0pFiaH5OFzo3fz4U+rdBdF3EuLkCkoQ3nWY8Q+n9BzLRyiH827+uOUR9dBC22gbvZ1hDK22JeQbUUcrw==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/models": "0.79.0", - "@code-pushup/utils": "0.79.0", + "@code-pushup/models": "0.79.1", + "@code-pushup/utils": "0.79.1", "ansis": "^3.3.0" }, "peerDependencies": { @@ -2362,9 +2362,9 @@ } }, "node_modules/@code-pushup/models": { - "version": "0.79.0", - "resolved": "https://registry.npmjs.org/@code-pushup/models/-/models-0.79.0.tgz", - "integrity": "sha512-0DTca8Eak+jybXLtBDvDDxz+LFsrq1g41daDzAKM6gF+/Vk4elPmHcGNhaMec25L/gowOPXgI9JGJfHqTPZtmg==", + "version": "0.79.1", + "resolved": "https://registry.npmjs.org/@code-pushup/models/-/models-0.79.1.tgz", + "integrity": "sha512-V/8EFG2FNVhsESUnKDsso+BBOjrHO2E5phbA3qOWmM0YG7AnEpIduOlysZMhvU5Y5oakcWueLCxvyFbBGl8PHA==", "dev": true, "license": "MIT", "dependencies": { @@ -2373,14 +2373,14 @@ } }, "node_modules/@code-pushup/nx-plugin": { - "version": "0.79.0", - "resolved": "https://registry.npmjs.org/@code-pushup/nx-plugin/-/nx-plugin-0.79.0.tgz", - "integrity": "sha512-QqmkeUWOVpuYDGknm7QSxKo6iIsxALmvHYMmIoMbvS9LIxUitW0hjBD6OWzMUhHlxlg6zQQHuMu4G51fY2AtGg==", + "version": "0.79.1", + "resolved": "https://registry.npmjs.org/@code-pushup/nx-plugin/-/nx-plugin-0.79.1.tgz", + "integrity": "sha512-5nsdtWcPE3i0a5vB36c5smxYjUaY/ZEghCi1xxEe8dlOFrZQZTzEPOQ+kwc/jSBoI1hA5p+tRuO+IDYcTZoHaQ==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/models": "0.79.0", - "@code-pushup/utils": "0.79.0", + "@code-pushup/models": "0.79.1", + "@code-pushup/utils": "0.79.1", "@nx/devkit": ">=17.0.0", "ansis": "^3.3.0", "nx": ">=17.0.0", @@ -2401,13 +2401,13 @@ } }, "node_modules/@code-pushup/utils": { - "version": "0.79.0", - "resolved": "https://registry.npmjs.org/@code-pushup/utils/-/utils-0.79.0.tgz", - "integrity": "sha512-7QXkdszu+DftpLQcK6H3Gnad/vn3odkz8TMMf8cBCgENMVb6KpJCtiahs10uxFqvINwxXYqIZVktylZAuUnxdg==", + "version": "0.79.1", + "resolved": "https://registry.npmjs.org/@code-pushup/utils/-/utils-0.79.1.tgz", + "integrity": "sha512-n4Ay1T390dAJqEjgP0R/xW8XduZaKEIDI7DLt4YfqeikKc56oPzT6kBApV1rzS0vCS7IUHtO95bNiYW3cteGUA==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/models": "0.79.0", + "@code-pushup/models": "0.79.1", "@isaacs/cliui": "^8.0.2", "@poppinss/cliui": "^6.4.0", "ansis": "^3.3.0", diff --git a/package.json b/package.json index 2fc790a24..2373fa9da 100644 --- a/package.json +++ b/package.json @@ -50,9 +50,9 @@ "devDependencies": { "@code-pushup/portal-client": "^0.16.0", "@beaussan/nx-knip": "^0.0.5-15", - "@code-pushup/cli": "^0.79.0", + "@code-pushup/cli": "^0.79.1", "@code-pushup/eslint-config": "^0.14.2", - "@code-pushup/nx-plugin": "^0.79.0", + "@code-pushup/nx-plugin": "^0.79.1", "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", "@commitlint/config-nx-scopes": "^19.5.0", From 06f0b65ec8ca3b4b947fce71e982a1dd24e3af15 Mon Sep 17 00:00:00 2001 From: John Doe Date: Sun, 9 Nov 2025 16:27:14 +0100 Subject: [PATCH 18/38] refactor: move plugin default exports --- packages/nx-plugin/src/index.ts | 9 ++------- packages/nx-plugin/src/plugin/index.ts | 12 +----------- packages/nx-plugin/src/plugin/plugin.ts | 7 +++++++ 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/packages/nx-plugin/src/index.ts b/packages/nx-plugin/src/index.ts index 9debee7d5..6c6f4b08f 100644 --- a/packages/nx-plugin/src/index.ts +++ b/packages/nx-plugin/src/index.ts @@ -1,5 +1,4 @@ -import type { CreateNodesV2, NxPlugin } from '@nx/devkit'; -import { createNodes, createNodesV2 } from './plugin/index.js'; +import { plugin } from './plugin/index.js'; export { createNodes, createNodesV2 } from './plugin/index.js'; export type { AutorunCommandExecutorOptions } from './executors/cli/schema.js'; @@ -11,8 +10,4 @@ export { initGenerator, initSchematic } from './generators/init/generator.js'; export { type InitGeneratorSchema } from './generators/init/schema.js'; export * from './internal/versions.js'; -export default { - name: 'code-pushup', - createNodesV2: createNodesV2 as CreateNodesV2, - createNodes, -} satisfies NxPlugin; +export default plugin; diff --git a/packages/nx-plugin/src/plugin/index.ts b/packages/nx-plugin/src/plugin/index.ts index 4f21f2a12..450cf2ced 100644 --- a/packages/nx-plugin/src/plugin/index.ts +++ b/packages/nx-plugin/src/plugin/index.ts @@ -1,12 +1,2 @@ -import type { NxPlugin } from '@nx/devkit'; -import { createNodesV2 } from './plugin.js'; - -export { createNodes, createNodesV2 } from './plugin.js'; +export { createNodes, createNodesV2, plugin } from './plugin.js'; export type { CreateNodesOptions } from './types.js'; - -const plugin = { - createNodesV2, - name: 'code-pushup-nx-plugin', -} satisfies NxPlugin; - -export default plugin; diff --git a/packages/nx-plugin/src/plugin/plugin.ts b/packages/nx-plugin/src/plugin/plugin.ts index 489468558..37a178619 100644 --- a/packages/nx-plugin/src/plugin/plugin.ts +++ b/packages/nx-plugin/src/plugin/plugin.ts @@ -5,6 +5,7 @@ import type { CreateNodesResult, CreateNodesResultV2, CreateNodesV2, + NxPlugin, } from '@nx/devkit'; import { PROJECT_JSON_FILE_NAME } from '../internal/constants.js'; import { createTargets } from './target/targets.js'; @@ -70,3 +71,9 @@ export const createNodesV2: CreateNodesV2 = [ ); }, ]; + +export const plugin = { + name: 'code-pushup', + createNodesV2: createNodesV2 as CreateNodesV2, + createNodes, +} satisfies NxPlugin; From 55c022b83b63638e1fca2b9da25866afaf11473f Mon Sep 17 00:00:00 2001 From: John Doe Date: Sun, 9 Nov 2025 16:49:36 +0100 Subject: [PATCH 19/38] refactor: remove bin option from config --- nx.json | 9 ++++++++- packages/nx-plugin/src/executors/internal/cli.ts | 2 -- .../src/generators/configuration/generator.int.test.ts | 1 - .../nx-plugin/src/generators/configuration/schema.d.ts | 1 - .../nx-plugin/src/plugin/target/configuration-target.ts | 4 ++-- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/nx.json b/nx.json index 4de8cc796..d3358301a 100644 --- a/nx.json +++ b/nx.json @@ -348,7 +348,14 @@ }, "plugins": [ { - "plugin": "@code-pushup/nx-plugin" + "plugin": "@code-pushup/nx-plugin", + "options": { + "bin": "packages/cli/src/index.ts", + "env": { + "NODE_OPTIONS": "--import tsx", + "TSX_TSCONFIG_PATH": "tsconfig.base.json" + } + } }, { "plugin": "@push-based/nx-verdaccio", diff --git a/packages/nx-plugin/src/executors/internal/cli.ts b/packages/nx-plugin/src/executors/internal/cli.ts index adbf1627c..6ae34f9c7 100644 --- a/packages/nx-plugin/src/executors/internal/cli.ts +++ b/packages/nx-plugin/src/executors/internal/cli.ts @@ -1,5 +1,3 @@ -import { logger } from '@nx/devkit'; - export function createCliCommandString(options?: { args?: Record; command?: string; diff --git a/packages/nx-plugin/src/generators/configuration/generator.int.test.ts b/packages/nx-plugin/src/generators/configuration/generator.int.test.ts index 25c6cbae2..ba779c955 100644 --- a/packages/nx-plugin/src/generators/configuration/generator.int.test.ts +++ b/packages/nx-plugin/src/generators/configuration/generator.int.test.ts @@ -7,7 +7,6 @@ import { import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import * as path from 'node:path'; import { afterEach, describe, expect, it, vi } from 'vitest'; -import { DEFAULT_TARGET_NAME, PACKAGE_NAME } from '../../internal/constants.js'; import { configurationGenerator } from './generator.js'; describe('configurationGenerator', () => { diff --git a/packages/nx-plugin/src/generators/configuration/schema.d.ts b/packages/nx-plugin/src/generators/configuration/schema.d.ts index b105270c6..0f0cb7b79 100644 --- a/packages/nx-plugin/src/generators/configuration/schema.d.ts +++ b/packages/nx-plugin/src/generators/configuration/schema.d.ts @@ -2,7 +2,6 @@ import type { DynamicTargetOptions } from '../../internal/types.js'; export type ConfigurationGeneratorOptions = { project: string; - bin?: string; skipTarget?: boolean; skipConfig?: boolean; skipFormat?: boolean; diff --git a/packages/nx-plugin/src/plugin/target/configuration-target.ts b/packages/nx-plugin/src/plugin/target/configuration-target.ts index cc9655969..448cb8188 100644 --- a/packages/nx-plugin/src/plugin/target/configuration-target.ts +++ b/packages/nx-plugin/src/plugin/target/configuration-target.ts @@ -7,11 +7,11 @@ export function createConfigurationTarget(options?: { projectName?: string; bin?: string; }): TargetConfiguration { - const { projectName, bin = PACKAGE_NAME } = options ?? {}; + const { projectName } = options ?? {}; const args = objectToCliArgs({ ...(projectName ? { project: projectName } : {}), }); return { - command: `nx g ${bin}:configuration${args.length > 0 ? ` ${args.join(' ')}` : ''}`, + command: `nx g ${PACKAGE_NAME}:configuration${args.length > 0 ? ` ${args.join(' ')}` : ''}`, }; } From e419189bb10b6bd4461ef55e94bc71969eba4a0a Mon Sep 17 00:00:00 2001 From: John Doe Date: Sun, 9 Nov 2025 16:49:46 +0100 Subject: [PATCH 20/38] refactor: update packages --- package-lock.json | 56 +++++++++++++++++++++++------------------------ package.json | 4 ++-- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5e0d3d371..918d6b419 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,9 +39,9 @@ "@actions/core": "^1.11.1", "@actions/github": "^6.0.1", "@beaussan/nx-knip": "^0.0.5-15", - "@code-pushup/cli": "^0.79.1", + "@code-pushup/cli": "^0.85.0", "@code-pushup/eslint-config": "^0.14.2", - "@code-pushup/nx-plugin": "^0.79.1", + "@code-pushup/nx-plugin": "^0.85.0", "@code-pushup/portal-client": "^0.16.0", "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", @@ -2318,15 +2318,15 @@ } }, "node_modules/@code-pushup/cli": { - "version": "0.79.1", - "resolved": "https://registry.npmjs.org/@code-pushup/cli/-/cli-0.79.1.tgz", - "integrity": "sha512-rV5IjoAlcV86sFby2t/9mi7Vkwn/CiqA+wPvnQeoWLjPJBDVOuDl9HA1dwUcaynYZjQPB9IpBpThP7tMfBE/vw==", + "version": "0.85.0", + "resolved": "https://registry.npmjs.org/@code-pushup/cli/-/cli-0.85.0.tgz", + "integrity": "sha512-paeQH4AuAYy33O9+ny16cixsYWXWGpjKwjAEmVq3/iamAYFdLrMMGZeOLaAfxGNCF5a5tVPJSYbmqClJ7BfV6g==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/core": "0.79.1", - "@code-pushup/models": "0.79.1", - "@code-pushup/utils": "0.79.1", + "@code-pushup/core": "0.85.0", + "@code-pushup/models": "0.85.0", + "@code-pushup/utils": "0.85.0", "ansis": "^3.3.0", "simple-git": "^3.20.0", "yargs": "^17.7.2" @@ -2339,14 +2339,14 @@ } }, "node_modules/@code-pushup/core": { - "version": "0.79.1", - "resolved": "https://registry.npmjs.org/@code-pushup/core/-/core-0.79.1.tgz", - "integrity": "sha512-RNA8uQ0pFiaH5OFzo3fz4U+rdBdF3EuLkCkoQ3nWY8Q+n9BzLRyiH827+uOUR9dBC22gbvZ1hDK22JeQbUUcrw==", + "version": "0.85.0", + "resolved": "https://registry.npmjs.org/@code-pushup/core/-/core-0.85.0.tgz", + "integrity": "sha512-ZMiUzlzZ9smJbZvJOkrfk+NELICRYNQQEpVkhrTQe2AAZR8sbeEbT+4eC0r8W04BMQkwVvbiv/eXIlCVsInIYQ==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/models": "0.79.1", - "@code-pushup/utils": "0.79.1", + "@code-pushup/models": "0.85.0", + "@code-pushup/utils": "0.85.0", "ansis": "^3.3.0" }, "peerDependencies": { @@ -2443,29 +2443,28 @@ } }, "node_modules/@code-pushup/models": { - "version": "0.79.1", - "resolved": "https://registry.npmjs.org/@code-pushup/models/-/models-0.79.1.tgz", - "integrity": "sha512-V/8EFG2FNVhsESUnKDsso+BBOjrHO2E5phbA3qOWmM0YG7AnEpIduOlysZMhvU5Y5oakcWueLCxvyFbBGl8PHA==", + "version": "0.85.0", + "resolved": "https://registry.npmjs.org/@code-pushup/models/-/models-0.85.0.tgz", + "integrity": "sha512-aEd4nzioi3Ci/kt93HGzfLfTjX/8KysvxoNrlnCK7aN8Bt1h5n/yDHC8P+Ga3Q9mXs7SFBluA4CxJIv3fZMUdQ==", "dev": true, "license": "MIT", "dependencies": { + "ansis": "^3.3.2", "vscode-material-icons": "^0.1.0", "zod": "^4.0.5" } }, "node_modules/@code-pushup/nx-plugin": { - "version": "0.79.1", - "resolved": "https://registry.npmjs.org/@code-pushup/nx-plugin/-/nx-plugin-0.79.1.tgz", - "integrity": "sha512-5nsdtWcPE3i0a5vB36c5smxYjUaY/ZEghCi1xxEe8dlOFrZQZTzEPOQ+kwc/jSBoI1hA5p+tRuO+IDYcTZoHaQ==", + "version": "0.85.0", + "resolved": "https://registry.npmjs.org/@code-pushup/nx-plugin/-/nx-plugin-0.85.0.tgz", + "integrity": "sha512-YaspOnaXGlFw9RBMHytBIcSoWwLJjnxsF2paV6SbFQJqNMB+gr5yymOPGB7L1SBsDJ+tD8gOC13XkG2vv8cpgg==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/models": "0.79.1", - "@code-pushup/utils": "0.79.1", + "@code-pushup/models": "0.85.0", + "@code-pushup/utils": "0.85.0", "@nx/devkit": ">=17.0.0", - "ansis": "^3.3.0", - "nx": ">=17.0.0", - "zod": "^4.0.5" + "nx": ">=17.0.0" } }, "node_modules/@code-pushup/portal-client": { @@ -2482,13 +2481,13 @@ } }, "node_modules/@code-pushup/utils": { - "version": "0.79.1", - "resolved": "https://registry.npmjs.org/@code-pushup/utils/-/utils-0.79.1.tgz", - "integrity": "sha512-n4Ay1T390dAJqEjgP0R/xW8XduZaKEIDI7DLt4YfqeikKc56oPzT6kBApV1rzS0vCS7IUHtO95bNiYW3cteGUA==", + "version": "0.85.0", + "resolved": "https://registry.npmjs.org/@code-pushup/utils/-/utils-0.85.0.tgz", + "integrity": "sha512-nAfzjY83G2LvMDL9eVq91xSsi0eF22z5HuggrIRWU6NAhzFW7GYW1wXnyN1paclSKwh+DWyjpnlrTyVCBDQjOg==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/models": "0.79.1", + "@code-pushup/models": "0.85.0", "@isaacs/cliui": "^8.0.2", "@poppinss/cliui": "^6.4.0", "ansis": "^3.3.0", @@ -2496,6 +2495,7 @@ "bundle-require": "^5.1.0", "esbuild": "^0.25.2", "multi-progress-bars": "^5.0.3", + "ora": "^9.0.0", "semver": "^7.6.0", "simple-git": "^3.20.0", "zod": "^4.0.5" diff --git a/package.json b/package.json index 61f99da06..4c7504a5f 100644 --- a/package.json +++ b/package.json @@ -50,9 +50,9 @@ "@actions/core": "^1.11.1", "@actions/github": "^6.0.1", "@beaussan/nx-knip": "^0.0.5-15", - "@code-pushup/cli": "^0.79.1", + "@code-pushup/cli": "^0.85.0", "@code-pushup/eslint-config": "^0.14.2", - "@code-pushup/nx-plugin": "^0.79.1", + "@code-pushup/nx-plugin": "^0.85.0", "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", "@commitlint/config-nx-scopes": "^19.5.0", From 3fb67471d20437e38cc29263e3c6d9e13a4a8f75 Mon Sep 17 00:00:00 2001 From: John Doe Date: Sun, 9 Nov 2025 17:03:44 +0100 Subject: [PATCH 21/38] refactor: remove bin options for config generator --- e2e/nx-plugin-e2e/tests/plugin-create-nodes.e2e.test.ts | 3 ++- packages/nx-plugin/src/plugin/target/configuration-target.ts | 1 - packages/nx-plugin/src/plugin/target/targets.ts | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/e2e/nx-plugin-e2e/tests/plugin-create-nodes.e2e.test.ts b/e2e/nx-plugin-e2e/tests/plugin-create-nodes.e2e.test.ts index fb5408812..7a446969c 100644 --- a/e2e/nx-plugin-e2e/tests/plugin-create-nodes.e2e.test.ts +++ b/e2e/nx-plugin-e2e/tests/plugin-create-nodes.e2e.test.ts @@ -3,6 +3,7 @@ import path from 'node:path'; import { readProjectConfiguration } from 'nx/src/generators/utils/project-configuration'; import { afterEach, expect } from 'vitest'; import { generateCodePushupConfig } from '@code-pushup/nx-plugin'; +import { PACKAGE_NAME } from '@code-pushup/nx-plugin/src/internal/constants.js'; import { generateWorkspaceAndProject, materializeTree, @@ -120,7 +121,7 @@ describe('nx-plugin', () => { expect(projectJson.targets).toStrictEqual({ 'code-pushup--configuration': expect.objectContaining({ options: { - command: `nx g XYZ:configuration --project="${project}"`, + command: `nx g ${PACKAGE_NAME}:configuration --project="${project}"`, }, }), }); diff --git a/packages/nx-plugin/src/plugin/target/configuration-target.ts b/packages/nx-plugin/src/plugin/target/configuration-target.ts index 448cb8188..986d3ea4e 100644 --- a/packages/nx-plugin/src/plugin/target/configuration-target.ts +++ b/packages/nx-plugin/src/plugin/target/configuration-target.ts @@ -5,7 +5,6 @@ import { PACKAGE_NAME } from '../../internal/constants.js'; export function createConfigurationTarget(options?: { projectName?: string; - bin?: string; }): TargetConfiguration { const { projectName } = options ?? {}; const args = objectToCliArgs({ diff --git a/packages/nx-plugin/src/plugin/target/targets.ts b/packages/nx-plugin/src/plugin/target/targets.ts index ae192ffd8..261f4ae5f 100644 --- a/packages/nx-plugin/src/plugin/target/targets.ts +++ b/packages/nx-plugin/src/plugin/target/targets.ts @@ -29,7 +29,6 @@ export async function createTargets(normalizedContext: CreateTargetsOptions) { { [`${targetName}--configuration`]: createConfigurationTarget({ projectName: normalizedContext.projectJson.name, - bin, }), }; } From 4ba05a63b54bb4a0e9693da68f3f7bd4a2fa6dab Mon Sep 17 00:00:00 2001 From: John Doe Date: Mon, 10 Nov 2025 16:06:37 +0100 Subject: [PATCH 22/38] refactor: add env options and correct bin --- nx.json | 2 +- package-lock.json | 40 +++++++++---------- package.json | 6 +-- .../nx-plugin/src/executors/cli/executor.ts | 10 ++++- .../nx-plugin/src/executors/cli/schema.json | 7 ++++ .../nx-plugin/src/executors/internal/cli.ts | 40 +++++++++++++++---- .../src/executors/internal/cli.unit.test.ts | 33 +++++++++++++++ .../nx-plugin/src/executors/internal/types.ts | 1 + packages/nx-plugin/src/internal/types.ts | 1 + .../src/plugin/target/executor-target.ts | 10 +++-- .../target/executor.target.unit.test.ts | 19 +++++++++ .../nx-plugin/src/plugin/target/targets.ts | 3 +- 12 files changed, 135 insertions(+), 37 deletions(-) diff --git a/nx.json b/nx.json index d3358301a..4a023bf26 100644 --- a/nx.json +++ b/nx.json @@ -140,7 +140,7 @@ "watch": false } }, - "code-pushup": { + "old-code-pushup": { "cache": false, "outputs": [ "{projectRoot}/.code-pushup/report.md", diff --git a/package-lock.json b/package-lock.json index 918d6b419..ef0c9a261 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,9 +39,9 @@ "@actions/core": "^1.11.1", "@actions/github": "^6.0.1", "@beaussan/nx-knip": "^0.0.5-15", - "@code-pushup/cli": "^0.85.0", + "@code-pushup/cli": "https://pkg.pr.new/code-pushup/cli/@code-pushup/cli@1091", "@code-pushup/eslint-config": "^0.14.2", - "@code-pushup/nx-plugin": "^0.85.0", + "@code-pushup/nx-plugin": "https://pkg.pr.new/code-pushup/cli/@code-pushup/nx-plugin@1091", "@code-pushup/portal-client": "^0.16.0", "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", @@ -2319,14 +2319,14 @@ }, "node_modules/@code-pushup/cli": { "version": "0.85.0", - "resolved": "https://registry.npmjs.org/@code-pushup/cli/-/cli-0.85.0.tgz", - "integrity": "sha512-paeQH4AuAYy33O9+ny16cixsYWXWGpjKwjAEmVq3/iamAYFdLrMMGZeOLaAfxGNCF5a5tVPJSYbmqClJ7BfV6g==", + "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/cli@1091", + "integrity": "sha512-fY/JOxRXCY6qL2n1Kfos0AV+coRRALAZjxisfstquqSb74F0Obh02+dHHnhxR7AaQi39aM2WdpRhc5YFD3lOzA==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/core": "0.85.0", - "@code-pushup/models": "0.85.0", - "@code-pushup/utils": "0.85.0", + "@code-pushup/core": "https://pkg.pr.new/code-pushup/cli/@code-pushup/core@3fb67471d20437e38cc29263e3c6d9e13a4a8f75", + "@code-pushup/models": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@3fb67471d20437e38cc29263e3c6d9e13a4a8f75", + "@code-pushup/utils": "https://pkg.pr.new/code-pushup/cli/@code-pushup/utils@3fb67471d20437e38cc29263e3c6d9e13a4a8f75", "ansis": "^3.3.0", "simple-git": "^3.20.0", "yargs": "^17.7.2" @@ -2340,13 +2340,13 @@ }, "node_modules/@code-pushup/core": { "version": "0.85.0", - "resolved": "https://registry.npmjs.org/@code-pushup/core/-/core-0.85.0.tgz", - "integrity": "sha512-ZMiUzlzZ9smJbZvJOkrfk+NELICRYNQQEpVkhrTQe2AAZR8sbeEbT+4eC0r8W04BMQkwVvbiv/eXIlCVsInIYQ==", + "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/core@3fb67471d20437e38cc29263e3c6d9e13a4a8f75", + "integrity": "sha512-oWpUtPbG+ovzytIacsld9RhTt7gJfuEpDUxV24V7g/iR7GSUvlYNciFQkSlm/OGxkDrY9VztygnwjSfUDSLj8A==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/models": "0.85.0", - "@code-pushup/utils": "0.85.0", + "@code-pushup/models": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@3fb67471d20437e38cc29263e3c6d9e13a4a8f75", + "@code-pushup/utils": "https://pkg.pr.new/code-pushup/cli/@code-pushup/utils@3fb67471d20437e38cc29263e3c6d9e13a4a8f75", "ansis": "^3.3.0" }, "peerDependencies": { @@ -2444,8 +2444,8 @@ }, "node_modules/@code-pushup/models": { "version": "0.85.0", - "resolved": "https://registry.npmjs.org/@code-pushup/models/-/models-0.85.0.tgz", - "integrity": "sha512-aEd4nzioi3Ci/kt93HGzfLfTjX/8KysvxoNrlnCK7aN8Bt1h5n/yDHC8P+Ga3Q9mXs7SFBluA4CxJIv3fZMUdQ==", + "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@3fb67471d20437e38cc29263e3c6d9e13a4a8f75", + "integrity": "sha512-DAkXNfuL6O0ymZ2OZ9NyR5rN0Vgrw3i7tSCKL0ncDDVMNjh8S0FFuJFlPZyjFEZqOVbTVFJ4lVOozPFS21OP1A==", "dev": true, "license": "MIT", "dependencies": { @@ -2456,13 +2456,13 @@ }, "node_modules/@code-pushup/nx-plugin": { "version": "0.85.0", - "resolved": "https://registry.npmjs.org/@code-pushup/nx-plugin/-/nx-plugin-0.85.0.tgz", - "integrity": "sha512-YaspOnaXGlFw9RBMHytBIcSoWwLJjnxsF2paV6SbFQJqNMB+gr5yymOPGB7L1SBsDJ+tD8gOC13XkG2vv8cpgg==", + "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/nx-plugin@1091", + "integrity": "sha512-LUdaqKsfF4IACGOvdC1/57fxbYFMPjEMNH73cM2TRMIpHcO+/WwxqcZeBoMGzVBVLft4U4HEntIpHUVGeuxOYg==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/models": "0.85.0", - "@code-pushup/utils": "0.85.0", + "@code-pushup/models": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@3fb67471d20437e38cc29263e3c6d9e13a4a8f75", + "@code-pushup/utils": "https://pkg.pr.new/code-pushup/cli/@code-pushup/utils@3fb67471d20437e38cc29263e3c6d9e13a4a8f75", "@nx/devkit": ">=17.0.0", "nx": ">=17.0.0" } @@ -2482,12 +2482,12 @@ }, "node_modules/@code-pushup/utils": { "version": "0.85.0", - "resolved": "https://registry.npmjs.org/@code-pushup/utils/-/utils-0.85.0.tgz", - "integrity": "sha512-nAfzjY83G2LvMDL9eVq91xSsi0eF22z5HuggrIRWU6NAhzFW7GYW1wXnyN1paclSKwh+DWyjpnlrTyVCBDQjOg==", + "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/utils@3fb67471d20437e38cc29263e3c6d9e13a4a8f75", + "integrity": "sha512-eyvHJftWoLntmh4PptZxPztvYaePFoiAImBiL7nQhLVZSL5P7qyCM8KfVLsYh7hlNmOCN/l/ThavhYWuB0QGfQ==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/models": "0.85.0", + "@code-pushup/models": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@3fb67471d20437e38cc29263e3c6d9e13a4a8f75", "@isaacs/cliui": "^8.0.2", "@poppinss/cliui": "^6.4.0", "ansis": "^3.3.0", diff --git a/package.json b/package.json index 4c7504a5f..ba15ffa73 100644 --- a/package.json +++ b/package.json @@ -46,13 +46,13 @@ "zod": "^4.0.5" }, "devDependencies": { - "@code-pushup/portal-client": "^0.16.0", "@actions/core": "^1.11.1", "@actions/github": "^6.0.1", "@beaussan/nx-knip": "^0.0.5-15", - "@code-pushup/cli": "^0.85.0", + "@code-pushup/cli": "https://pkg.pr.new/code-pushup/cli/@code-pushup/cli@1091", "@code-pushup/eslint-config": "^0.14.2", - "@code-pushup/nx-plugin": "^0.85.0", + "@code-pushup/nx-plugin": "https://pkg.pr.new/code-pushup/cli/@code-pushup/nx-plugin@1091", + "@code-pushup/portal-client": "^0.16.0", "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", "@commitlint/config-nx-scopes": "^19.5.0", diff --git a/packages/nx-plugin/src/executors/cli/executor.ts b/packages/nx-plugin/src/executors/cli/executor.ts index 2e644f184..b173dc5cc 100644 --- a/packages/nx-plugin/src/executors/cli/executor.ts +++ b/packages/nx-plugin/src/executors/cli/executor.ts @@ -27,11 +27,12 @@ export default async function runAutorunExecutor( mergedOptions, normalizedContext, ); - const { dryRun, verbose, command, bin } = mergedOptions; + const { dryRun, verbose, command, bin, env } = mergedOptions; const commandString = createCliCommandString({ command, args: cliArgumentObject, bin, + env, }); if (verbose) { logger.info(`Run CLI executor ${command ?? ''}`); @@ -42,7 +43,12 @@ export default async function runAutorunExecutor( } else { try { await executeProcess({ - ...createCliCommandObject({ command, args: cliArgumentObject, bin }), + ...createCliCommandObject({ + command, + args: cliArgumentObject, + bin, + env, + }), ...(context.cwd ? { cwd: context.cwd } : {}), }); } catch (error) { diff --git a/packages/nx-plugin/src/executors/cli/schema.json b/packages/nx-plugin/src/executors/cli/schema.json index 85cd0de19..635c43c66 100644 --- a/packages/nx-plugin/src/executors/cli/schema.json +++ b/packages/nx-plugin/src/executors/cli/schema.json @@ -21,6 +21,13 @@ "type": "string", "description": "Path to Code PushUp CLI" }, + "env": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Environment variables to pass to the CLI command" + }, "verbose": { "type": "boolean", "description": "Print additional logs" diff --git a/packages/nx-plugin/src/executors/internal/cli.ts b/packages/nx-plugin/src/executors/internal/cli.ts index 6ae34f9c7..bc2a134bf 100644 --- a/packages/nx-plugin/src/executors/internal/cli.ts +++ b/packages/nx-plugin/src/executors/internal/cli.ts @@ -2,22 +2,48 @@ export function createCliCommandString(options?: { args?: Record; command?: string; bin?: string; + env?: Record; }): string { - const { bin = '@code-pushup/cli', command, args } = options ?? {}; - return `npx ${bin} ${objectToCliArgs({ _: command ?? [], ...args }).join( - ' ', - )}`; + const { bin = '@code-pushup/cli', command, args, env } = options ?? {}; + const isFile = isFilePath(bin); + const envTerminalString = Object.entries(env ?? {}) + .map(([key, value]) => `${key}=${value}`) + .join(' '); + const commandPrefix = isFile ? 'node' : 'npx'; + const envPrefix = envTerminalString ? `${envTerminalString} ` : ''; + return `${envPrefix}${commandPrefix} ${bin} ${objectToCliArgs({ + _: command ?? [], + ...args, + }).join(' ')}`; +} + +function isFilePath(bin: string): boolean { + return ( + /\.(js|ts|mjs|cjs)$/.test(bin) || + bin.startsWith('./') || + bin.startsWith('../') || + (bin.includes('/') && !bin.startsWith('@')) + ); } export function createCliCommandObject(options?: { args?: Record; command?: string; bin?: string; + env?: Record; }): import('@code-pushup/utils').ProcessConfig { - const { bin = '@code-pushup/cli', command, args } = options ?? {}; + const { bin = '@code-pushup/cli', command, args, env } = options ?? {}; + const isFile = isFilePath(bin); + const envTerminalString = Object.entries(env ?? {}) + .map(([key, value]) => `${key}=${value}`) + .join(' '); return { - command: 'npx', - args: [bin, ...objectToCliArgs({ _: command ?? [], ...args })], + command: isFile ? 'node' : 'npx', + args: [ + ...(envTerminalString ? [envTerminalString] : []), + bin, + ...objectToCliArgs({ _: command ?? [], ...args }), + ], }; } diff --git a/packages/nx-plugin/src/executors/internal/cli.unit.test.ts b/packages/nx-plugin/src/executors/internal/cli.unit.test.ts index 279f23505..36f79361f 100644 --- a/packages/nx-plugin/src/executors/internal/cli.unit.test.ts +++ b/packages/nx-plugin/src/executors/internal/cli.unit.test.ts @@ -102,6 +102,27 @@ describe('createCliCommandString', () => { }); expect(result).toBe('npx @code-pushup/cli autorun --verbose'); }); + + it('should use node for file paths', () => { + const result = createCliCommandString({ + bin: 'packages/cli/src/index.ts', + args: { verbose: true }, + }); + expect(result).toBe('node packages/cli/src/index.ts --verbose'); + }); + + it('should include env variables in command string', () => { + const result = createCliCommandString({ + args: { verbose: true }, + env: { + NODE_OPTIONS: '--import tsx', + TSX_TSCONFIG_PATH: 'tsconfig.base.json', + }, + }); + expect(result).toBe( + 'NODE_OPTIONS=--import tsx TSX_TSCONFIG_PATH=tsconfig.base.json npx @code-pushup/cli --verbose', + ); + }); }); describe('createCliCommandObject', () => { @@ -130,6 +151,18 @@ describe('createCliCommandObject', () => { }), ).toStrictEqual({ args: ['node_modules/@code-pushup/cli/src/bin.js'], + command: 'node', + }); + }); + + it('should create command out of object for arguments with env', () => { + expect( + createCliCommandObject({ + args: { verbose: true }, + env: { NODE_ENV: 'production', DEBUG: 'true' }, + }), + ).toStrictEqual({ + args: ['NODE_ENV=production DEBUG=true', '@code-pushup/cli', '--verbose'], command: 'npx', }); }); diff --git a/packages/nx-plugin/src/executors/internal/types.ts b/packages/nx-plugin/src/executors/internal/types.ts index 2f529038a..677d472ce 100644 --- a/packages/nx-plugin/src/executors/internal/types.ts +++ b/packages/nx-plugin/src/executors/internal/types.ts @@ -30,6 +30,7 @@ export type Command = export type GlobalExecutorOptions = { command?: Command; bin?: string; + env?: Record; verbose?: boolean; progress?: boolean; config?: string; diff --git a/packages/nx-plugin/src/internal/types.ts b/packages/nx-plugin/src/internal/types.ts index bf3a2d047..3a34d86d7 100644 --- a/packages/nx-plugin/src/internal/types.ts +++ b/packages/nx-plugin/src/internal/types.ts @@ -1,4 +1,5 @@ export type DynamicTargetOptions = { targetName?: string; bin?: string; + env?: Record; }; diff --git a/packages/nx-plugin/src/plugin/target/executor-target.ts b/packages/nx-plugin/src/plugin/target/executor-target.ts index d8cfab569..df968154b 100644 --- a/packages/nx-plugin/src/plugin/target/executor-target.ts +++ b/packages/nx-plugin/src/plugin/target/executor-target.ts @@ -5,13 +5,17 @@ import type { ProjectPrefixOptions } from '../types.js'; export function createExecutorTarget(options?: { bin?: string; projectPrefix?: string; -}): TargetConfiguration { - const { bin, projectPrefix } = options ?? {}; + env?: Record; +}): TargetConfiguration< + ProjectPrefixOptions & { env?: Record } +> { + const { bin, projectPrefix, env } = options ?? {}; const executor = `${PACKAGE_NAME}:cli`; - const executorOptions = (bin || projectPrefix) && { + const executorOptions = (bin || projectPrefix || env) && { ...(bin && { bin }), ...(projectPrefix && { projectPrefix }), + ...(env && { env }), }; return { executor, ...(executorOptions && { options: executorOptions }) }; } diff --git a/packages/nx-plugin/src/plugin/target/executor.target.unit.test.ts b/packages/nx-plugin/src/plugin/target/executor.target.unit.test.ts index 2926d3367..baec99a6d 100644 --- a/packages/nx-plugin/src/plugin/target/executor.target.unit.test.ts +++ b/packages/nx-plugin/src/plugin/target/executor.target.unit.test.ts @@ -27,4 +27,23 @@ describe('createExecutorTarget', () => { }, }); }); + + it('should use env if provided', () => { + expect( + createExecutorTarget({ + env: { + NODE_OPTIONS: '--import tsx', + TSX_TSCONFIG_PATH: 'tsconfig.base.json', + }, + }), + ).toStrictEqual({ + executor: '@code-pushup/nx-plugin:cli', + options: { + env: { + NODE_OPTIONS: '--import tsx', + TSX_TSCONFIG_PATH: 'tsconfig.base.json', + }, + }, + }); + }); }); diff --git a/packages/nx-plugin/src/plugin/target/targets.ts b/packages/nx-plugin/src/plugin/target/targets.ts index 261f4ae5f..90abaa047 100644 --- a/packages/nx-plugin/src/plugin/target/targets.ts +++ b/packages/nx-plugin/src/plugin/target/targets.ts @@ -19,11 +19,12 @@ export async function createTargets(normalizedContext: CreateTargetsOptions) { targetName = CP_TARGET_NAME, bin, projectPrefix, + env, } = normalizedContext.createOptions; const rootFiles = await readdir(normalizedContext.projectRoot); return rootFiles.some(filename => filename.match(CODE_PUSHUP_CONFIG_REGEX)) ? { - [targetName]: createExecutorTarget({ bin, projectPrefix }), + [targetName]: createExecutorTarget({ bin, projectPrefix, env }), } : // if NO code-pushup.config.*.(ts|js|mjs) is present return configuration target { From 71779ae75b1c836846e16b58ec1206f37662c7c0 Mon Sep 17 00:00:00 2001 From: John Doe Date: Tue, 11 Nov 2025 02:14:02 +0100 Subject: [PATCH 23/38] refactor: refactor plugin options --- package-lock.json | 46 +++---- package.json | 4 +- .../src/executors/cli/executor.int.test.ts | 31 +++-- .../nx-plugin/src/executors/cli/executor.ts | 102 +++++++------- .../src/executors/cli/executor.unit.test.ts | 127 +++++++++++------- .../nx-plugin/src/executors/internal/cli.ts | 49 ------- .../src/executors/internal/cli.unit.test.ts | 78 ----------- .../src/executors/internal/config.ts | 13 +- packages/utils/src/index.ts | 2 +- packages/utils/src/lib/execute-process.ts | 12 +- packages/utils/src/lib/logger.ts | 84 +++++++++++- testing/test-utils/src/lib/utils/string.ts | 5 +- 12 files changed, 276 insertions(+), 277 deletions(-) diff --git a/package-lock.json b/package-lock.json index ef0c9a261..e6ffdcf47 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,9 +39,9 @@ "@actions/core": "^1.11.1", "@actions/github": "^6.0.1", "@beaussan/nx-knip": "^0.0.5-15", - "@code-pushup/cli": "https://pkg.pr.new/code-pushup/cli/@code-pushup/cli@1091", + "@code-pushup/cli": "0.86.0", "@code-pushup/eslint-config": "^0.14.2", - "@code-pushup/nx-plugin": "https://pkg.pr.new/code-pushup/cli/@code-pushup/nx-plugin@1091", + "@code-pushup/nx-plugin": "0.86.0", "@code-pushup/portal-client": "^0.16.0", "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", @@ -2318,15 +2318,15 @@ } }, "node_modules/@code-pushup/cli": { - "version": "0.85.0", + "version": "0.86.0", "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/cli@1091", - "integrity": "sha512-fY/JOxRXCY6qL2n1Kfos0AV+coRRALAZjxisfstquqSb74F0Obh02+dHHnhxR7AaQi39aM2WdpRhc5YFD3lOzA==", + "integrity": "sha512-EYJuIA7ifeKLL9hgXBbORRu3de2BboGJdw1/uSGZzNyC19wr879jysLridwvt9tLlBHv3sReTp/e3Tc/lZQIGw==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/core": "https://pkg.pr.new/code-pushup/cli/@code-pushup/core@3fb67471d20437e38cc29263e3c6d9e13a4a8f75", - "@code-pushup/models": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@3fb67471d20437e38cc29263e3c6d9e13a4a8f75", - "@code-pushup/utils": "https://pkg.pr.new/code-pushup/cli/@code-pushup/utils@3fb67471d20437e38cc29263e3c6d9e13a4a8f75", + "@code-pushup/core": "https://pkg.pr.new/code-pushup/cli/@code-pushup/core@4ba05a63b54bb4a0e9693da68f3f7bd4a2fa6dab", + "@code-pushup/models": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@4ba05a63b54bb4a0e9693da68f3f7bd4a2fa6dab", + "@code-pushup/utils": "https://pkg.pr.new/code-pushup/cli/@code-pushup/utils@4ba05a63b54bb4a0e9693da68f3f7bd4a2fa6dab", "ansis": "^3.3.0", "simple-git": "^3.20.0", "yargs": "^17.7.2" @@ -2339,14 +2339,14 @@ } }, "node_modules/@code-pushup/core": { - "version": "0.85.0", - "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/core@3fb67471d20437e38cc29263e3c6d9e13a4a8f75", - "integrity": "sha512-oWpUtPbG+ovzytIacsld9RhTt7gJfuEpDUxV24V7g/iR7GSUvlYNciFQkSlm/OGxkDrY9VztygnwjSfUDSLj8A==", + "version": "0.86.0", + "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/core@4ba05a63b54bb4a0e9693da68f3f7bd4a2fa6dab", + "integrity": "sha512-XkDkIDzynBGWIXoTGY+4Z6WTYCbODXdqikDTPyoApWyHgFFycoZqLU7scDXrQrD4fCiQ2KC7hIk9H2Na8ld2PA==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/models": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@3fb67471d20437e38cc29263e3c6d9e13a4a8f75", - "@code-pushup/utils": "https://pkg.pr.new/code-pushup/cli/@code-pushup/utils@3fb67471d20437e38cc29263e3c6d9e13a4a8f75", + "@code-pushup/models": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@4ba05a63b54bb4a0e9693da68f3f7bd4a2fa6dab", + "@code-pushup/utils": "https://pkg.pr.new/code-pushup/cli/@code-pushup/utils@4ba05a63b54bb4a0e9693da68f3f7bd4a2fa6dab", "ansis": "^3.3.0" }, "peerDependencies": { @@ -2443,9 +2443,9 @@ } }, "node_modules/@code-pushup/models": { - "version": "0.85.0", - "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@3fb67471d20437e38cc29263e3c6d9e13a4a8f75", - "integrity": "sha512-DAkXNfuL6O0ymZ2OZ9NyR5rN0Vgrw3i7tSCKL0ncDDVMNjh8S0FFuJFlPZyjFEZqOVbTVFJ4lVOozPFS21OP1A==", + "version": "0.86.0", + "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@4ba05a63b54bb4a0e9693da68f3f7bd4a2fa6dab", + "integrity": "sha512-86e6eX2e2oaYPrbS68YP91mTJtNnfNGW86tlzUvObiMbq0pAahaFZFeauJ+pHgMFlzKbTHPLSA2DJfrfnK/j0A==", "dev": true, "license": "MIT", "dependencies": { @@ -2455,14 +2455,14 @@ } }, "node_modules/@code-pushup/nx-plugin": { - "version": "0.85.0", + "version": "0.86.0", "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/nx-plugin@1091", - "integrity": "sha512-LUdaqKsfF4IACGOvdC1/57fxbYFMPjEMNH73cM2TRMIpHcO+/WwxqcZeBoMGzVBVLft4U4HEntIpHUVGeuxOYg==", + "integrity": "sha512-Qn4ktyOHJ7F483b9cKV0Pwr6WljhdFJ3opmv9NeXfOXBRfanB1VjkKhINNnC8vE0uG3bsr1YbfWrzr/qmc6L4g==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/models": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@3fb67471d20437e38cc29263e3c6d9e13a4a8f75", - "@code-pushup/utils": "https://pkg.pr.new/code-pushup/cli/@code-pushup/utils@3fb67471d20437e38cc29263e3c6d9e13a4a8f75", + "@code-pushup/models": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@4ba05a63b54bb4a0e9693da68f3f7bd4a2fa6dab", + "@code-pushup/utils": "https://pkg.pr.new/code-pushup/cli/@code-pushup/utils@4ba05a63b54bb4a0e9693da68f3f7bd4a2fa6dab", "@nx/devkit": ">=17.0.0", "nx": ">=17.0.0" } @@ -2481,13 +2481,13 @@ } }, "node_modules/@code-pushup/utils": { - "version": "0.85.0", - "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/utils@3fb67471d20437e38cc29263e3c6d9e13a4a8f75", - "integrity": "sha512-eyvHJftWoLntmh4PptZxPztvYaePFoiAImBiL7nQhLVZSL5P7qyCM8KfVLsYh7hlNmOCN/l/ThavhYWuB0QGfQ==", + "version": "0.86.0", + "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/utils@4ba05a63b54bb4a0e9693da68f3f7bd4a2fa6dab", + "integrity": "sha512-i6pHmoikxGukJliC9D2SWAHx94NcBuFZwEqHiPDB0s5HvFfCVf2gtx7KCZs172UcMV0Io8JJ69hhn/N3FT0ySw==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/models": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@3fb67471d20437e38cc29263e3c6d9e13a4a8f75", + "@code-pushup/models": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@4ba05a63b54bb4a0e9693da68f3f7bd4a2fa6dab", "@isaacs/cliui": "^8.0.2", "@poppinss/cliui": "^6.4.0", "ansis": "^3.3.0", diff --git a/package.json b/package.json index ba15ffa73..c59f21800 100644 --- a/package.json +++ b/package.json @@ -49,9 +49,9 @@ "@actions/core": "^1.11.1", "@actions/github": "^6.0.1", "@beaussan/nx-knip": "^0.0.5-15", - "@code-pushup/cli": "https://pkg.pr.new/code-pushup/cli/@code-pushup/cli@1091", "@code-pushup/eslint-config": "^0.14.2", - "@code-pushup/nx-plugin": "https://pkg.pr.new/code-pushup/cli/@code-pushup/nx-plugin@1091", + "@code-pushup/cli": "0.86.0", + "@code-pushup/nx-plugin": "0.86.0", "@code-pushup/portal-client": "^0.16.0", "@commitlint/cli": "^19.5.0", "@commitlint/config-conventional": "^19.5.0", diff --git a/packages/nx-plugin/src/executors/cli/executor.int.test.ts b/packages/nx-plugin/src/executors/cli/executor.int.test.ts index fee1e59d8..5a1b5ee70 100644 --- a/packages/nx-plugin/src/executors/cli/executor.int.test.ts +++ b/packages/nx-plugin/src/executors/cli/executor.int.test.ts @@ -1,5 +1,6 @@ import { afterEach, expect, vi } from 'vitest'; import { executorContext } from '@code-pushup/test-nx-utils'; +import { removeColorCodes } from '@code-pushup/test-utils'; import * as executeProcessModule from '../../internal/execute-process.js'; import runAutorunExecutor from './executor.js'; import * as utils from './utils.js'; @@ -26,27 +27,25 @@ describe('runAutorunExecutor', () => { executeProcessSpy.mockReset(); }); - it('should normalize context, parse CLI options and execute command', async () => { - const output = await runAutorunExecutor( + it('should parse CLI options and execute command', async () => { + const { success, command } = await runAutorunExecutor( { verbose: true }, executorContext('utils'), ); - expect(output.success).toBe(true); - - expect(parseAutorunExecutorOptionsSpy).toHaveBeenCalledTimes(1); - - //is context normalized - expect(parseAutorunExecutorOptionsSpy).toHaveBeenCalledWith( - { verbose: true }, + expect(success).toBe(true); + const cleanCommand = removeColorCodes(command || ''); + expect(cleanCommand).toMatch('npx @code-pushup/cli'); + expect(cleanCommand).toMatch('CP_VERBOSE=true'); + expect(executeProcessSpy).toHaveBeenCalledTimes(1); + expect(executeProcessSpy).toHaveBeenCalledWith( expect.objectContaining({ - projectConfig: expect.objectContaining({ name: 'utils' }), + command: 'npx', + args: expect.arrayContaining(['@code-pushup/cli']), + cwd: expect.any(String), + env: expect.objectContaining({ + CP_VERBOSE: 'true', + }), }), ); - expect(executeProcessSpy).toHaveBeenCalledTimes(1); - expect(executeProcessSpy).toHaveBeenCalledWith({ - command: 'npx', - args: expect.arrayContaining(['@code-pushup/cli']), - cwd: process.cwd(), - }); }); }); diff --git a/packages/nx-plugin/src/executors/cli/executor.ts b/packages/nx-plugin/src/executors/cli/executor.ts index b173dc5cc..fa9e34425 100644 --- a/packages/nx-plugin/src/executors/cli/executor.ts +++ b/packages/nx-plugin/src/executors/cli/executor.ts @@ -1,12 +1,7 @@ -import { type ExecutorContext, logger } from '@nx/devkit'; +import { type ExecutorContext } from '@nx/devkit'; import { executeProcess } from '../../internal/execute-process.js'; -import { - createCliCommandObject, - createCliCommandString, -} from '../internal/cli.js'; -import { normalizeContext } from '../internal/context.js'; +import { objectToCliArgs } from '../internal/cli.js'; import type { AutorunCommandExecutorOptions } from './schema.js'; -import { mergeExecutorOptions, parseAutorunExecutorOptions } from './utils.js'; export type ExecutorOutput = { success: boolean; @@ -16,52 +11,67 @@ export type ExecutorOutput = { export default async function runAutorunExecutor( terminalAndExecutorOptions: AutorunCommandExecutorOptions, - context: ExecutorContext, + { cwd }: ExecutorContext, ): Promise { - const normalizedContext = normalizeContext(context); - const mergedOptions = mergeExecutorOptions( - context.target?.options, - terminalAndExecutorOptions, - ); - const cliArgumentObject = parseAutorunExecutorOptions( - mergedOptions, - normalizedContext, - ); - const { dryRun, verbose, command, bin, env } = mergedOptions; - const commandString = createCliCommandString({ - command, - args: cliArgumentObject, - bin, + const { + dryRun, + verbose, + command: cliCommand, env, + bin, + ...argsObj + } = terminalAndExecutorOptions; + const command = bin ? `node` : 'npx'; + const positionals = [ + bin ?? '@code-pushup/cli', + ...(cliCommand ? [cliCommand] : []), + ]; + const args = objectToCliArgs(argsObj); + const envVariables = { + ...env, + ...(verbose && { CP_VERBOSE: 'true' }), + }; + + const { logger, stringifyError, formatCommand } = await import( + '@code-pushup/utils' + ); + const binString = `${command} ${positionals.join(' ')} ${args.join(' ')}`; + const formattedBinString = formatCommand(binString, { + env: envVariables, + cwd, }); - if (verbose) { - logger.info(`Run CLI executor ${command ?? ''}`); - logger.info(`Command: ${commandString}`); - } + if (dryRun) { - logger.warn(`DryRun execution of: ${commandString}`); + logger.warn(`DryRun execution of: \n ${formattedBinString}`); } else { - try { - await executeProcess({ - ...createCliCommandObject({ - command, - args: cliArgumentObject, - bin, - env, - }), - ...(context.cwd ? { cwd: context.cwd } : {}), - }); - } catch (error) { - logger.error(error); - return { - success: false, - command: commandString, - error: error instanceof Error ? error : new Error(`${error}`), - }; - } + logger.command( + binString, + async () => { + try { + await executeProcess({ + command, + args: [...positionals, ...args], + ...(envVariables && { env: envVariables }), + ...(cwd ? { cwd } : {}), + }); + } catch (error) { + logger.error(stringifyError(error)); + return { + success: false, + command: formattedBinString, + error: error instanceof Error ? error : new Error(`${error}`), + }; + } + }, + { + env: envVariables, + cwd, + }, + ); } + return { success: true, - command: commandString, + command: formattedBinString, }; } diff --git a/packages/nx-plugin/src/executors/cli/executor.unit.test.ts b/packages/nx-plugin/src/executors/cli/executor.unit.test.ts index c5684b0ba..ccfb55eeb 100644 --- a/packages/nx-plugin/src/executors/cli/executor.unit.test.ts +++ b/packages/nx-plugin/src/executors/cli/executor.unit.test.ts @@ -1,7 +1,6 @@ -import { logger } from '@nx/devkit'; import { afterAll, afterEach, beforeEach, expect, vi } from 'vitest'; import { executorContext } from '@code-pushup/test-nx-utils'; -import { MEMFS_VOLUME } from '@code-pushup/test-utils'; +import { MEMFS_VOLUME, removeColorCodes } from '@code-pushup/test-utils'; import * as executeProcessModule from '../../internal/execute-process.js'; import runAutorunExecutor from './executor.js'; @@ -9,14 +8,25 @@ describe('runAutorunExecutor', () => { const processEnvCP = Object.fromEntries( Object.entries(process.env).filter(([k]) => k.startsWith('CP_')), ); - const loggerInfoSpy = vi.spyOn(logger, 'info'); - const loggerWarnSpy = vi.spyOn(logger, 'warn'); + let loggerCommandSpy: any; + let loggerWarnSpy: any; const executeProcessSpy = vi.spyOn(executeProcessModule, 'executeProcess'); - beforeAll(() => { + beforeAll(async () => { Object.entries(process.env) .filter(([k]) => k.startsWith('CP_')) .forEach(([k]) => delete process.env[k]); + + const { logger } = await import('@code-pushup/utils'); + loggerCommandSpy = vi + .spyOn(logger, 'command') + .mockImplementation(async (bin, worker, options) => { + // Execute worker immediately since executor doesn't await logger.command + // We await it here to ensure executeProcess is called in tests + await worker(); + return undefined; + }); + loggerWarnSpy = vi.spyOn(logger, 'warn'); }); afterAll(() => { @@ -36,22 +46,32 @@ describe('runAutorunExecutor', () => { afterEach(() => { loggerWarnSpy.mockReset(); - loggerInfoSpy.mockReset(); + loggerCommandSpy.mockReset(); executeProcessSpy.mockReset(); }); it('should call executeProcess with return result', async () => { - const output = await runAutorunExecutor({}, executorContext('utils')); - expect(output.success).toBe(true); - expect(output.command).toMatch('npx @code-pushup/cli'); - expect(executeProcessSpy).toHaveBeenCalledWith({ - command: 'npx', - args: expect.arrayContaining(['@code-pushup/cli']), - cwd: MEMFS_VOLUME, - }); + const { success, command } = await runAutorunExecutor( + {}, + executorContext('utils'), + ); + + expect(success).toBe(true); + expect(removeColorCodes(command || '')).toMatch('npx @code-pushup/cli'); + // The executor doesn't await logger.command, so executeProcess is called asynchronously + // We verify logger.command was called, which will execute executeProcess + expect(loggerCommandSpy).toHaveBeenCalledTimes(1); + expect(loggerCommandSpy).toHaveBeenCalledWith( + expect.stringContaining('npx @code-pushup/cli'), + expect.any(Function), + expect.objectContaining({ + cwd: MEMFS_VOLUME, + env: expect.any(Object), + }), + ); }); - it('should normalize context', async () => { + it('should get CWD from context', async () => { const output = await runAutorunExecutor( {}, { @@ -59,13 +79,26 @@ describe('runAutorunExecutor', () => { cwd: 'cwd-form-context', }, ); + expect(output.success).toBe(true); - expect(output.command).toMatch('utils'); - expect(executeProcessSpy).toHaveBeenCalledWith({ - command: 'npx', - args: expect.arrayContaining(['@code-pushup/cli']), - cwd: 'cwd-form-context', - }); + const commandWithoutAnsi = removeColorCodes(output.command || ''); + expect(commandWithoutAnsi).toMatch('cwd-form-context'); + expect(loggerCommandSpy).toHaveBeenCalledTimes(1); + }); + + it('should get env variables from options', async () => { + const { command } = await runAutorunExecutor( + { + env: { + CP_API_KEY: '123456789', + CP_PROJECT: 'cli', + }, + }, + executorContext('utils'), + ); + const commandWithoutAnsi = removeColorCodes(command || ''); + expect(commandWithoutAnsi).toMatch('CP_API_KEY=123456789'); + expect(commandWithoutAnsi).toMatch('CP_PROJECT=cli'); }); it('should process executorOptions', async () => { @@ -74,8 +107,9 @@ describe('runAutorunExecutor', () => { executorContext('testing-utils'), ); expect(output.success).toBe(true); - expect(output.command).toContain('--output="code-pushup.config.json"'); - expect(output.command).toContain('--persist.filename="REPORT"'); + const commandWithoutAnsi = removeColorCodes(output.command || ''); + expect(commandWithoutAnsi).toContain('--output="code-pushup.config.json"'); + expect(commandWithoutAnsi).toContain('--persist.filename="REPORT"'); }); it('should create command from context and options if no api key is set', async () => { @@ -83,56 +117,53 @@ describe('runAutorunExecutor', () => { { persist: { filename: 'REPORT', format: ['md', 'json'] } }, executorContext('core'), ); - expect(output.command).toMatch('--persist.filename="REPORT"'); - expect(output.command).toMatch( + const commandWithoutAnsi = removeColorCodes(output.command || ''); + expect(commandWithoutAnsi).toMatch('--persist.filename="REPORT"'); + expect(commandWithoutAnsi).toMatch( '--persist.format="md" --persist.format="json"', ); }); it('should create command from context, options and arguments if api key is set', async () => { - vi.stubEnv('CP_API_KEY', 'cp_1234567'); const output = await runAutorunExecutor( { persist: { filename: 'REPORT', format: ['md', 'json'] }, - upload: { project: 'CLI' }, + upload: { apiKey: 'cp_1234567', project: 'CLI' }, }, executorContext('core'), ); - expect(output.command).toMatch('--persist.filename="REPORT"'); - expect(output.command).toMatch( + const commandWithoutAnsi = removeColorCodes(output.command || ''); + expect(commandWithoutAnsi).toMatch('--persist.filename="REPORT"'); + expect(commandWithoutAnsi).toMatch( '--persist.format="md" --persist.format="json"', ); - expect(output.command).toMatch('--upload.apiKey="cp_1234567"'); - expect(output.command).toMatch('--upload.project="CLI"'); + expect(commandWithoutAnsi).toMatch('--upload.apiKey="cp_1234567"'); + expect(commandWithoutAnsi).toMatch('--upload.project="CLI"'); }); - it('should log information if verbose is set', async () => { - const output = await runAutorunExecutor( + it('should log information and set CP_VERBOSE if verbose is set ', async () => { + const { command } = await runAutorunExecutor( { verbose: true }, { ...executorContext('github-action'), cwd: '' }, ); - expect(executeProcessSpy).toHaveBeenCalledTimes(1); - expect(output.command).toMatch('--verbose'); - expect(loggerWarnSpy).toHaveBeenCalledTimes(0); - expect(loggerInfoSpy).toHaveBeenCalledTimes(2); - expect(loggerInfoSpy).toHaveBeenCalledWith( - expect.stringContaining(`Run CLI executor`), - ); - expect(loggerInfoSpy).toHaveBeenCalledWith( - expect.stringContaining('Command: npx @code-pushup/cli'), + expect(removeColorCodes(command || '')).toMatch('CP_VERBOSE=true'); + expect(loggerCommandSpy).toHaveBeenCalledTimes(1); + expect(loggerCommandSpy).toHaveBeenCalledWith( + expect.stringContaining('npx @code-pushup/cli'), + expect.any(Function), + expect.objectContaining({ env: { CP_VERBOSE: 'true' } }), ); }); it('should log command if dryRun is set', async () => { await runAutorunExecutor({ dryRun: true }, executorContext('utils')); - expect(loggerInfoSpy).toHaveBeenCalledTimes(0); + expect(loggerCommandSpy).toHaveBeenCalledTimes(0); expect(loggerWarnSpy).toHaveBeenCalledTimes(1); - expect(loggerWarnSpy).toHaveBeenCalledWith( - expect.stringContaining( - 'DryRun execution of: npx @code-pushup/cli --dryRun', - ), - ); + const warnCall = loggerWarnSpy.mock.calls[0]?.[0] as string | undefined; + const warnMessage = removeColorCodes(warnCall || ''); + expect(warnMessage).toContain('DryRun execution of:'); + expect(warnMessage).toContain('npx @code-pushup/cli'); }); }); diff --git a/packages/nx-plugin/src/executors/internal/cli.ts b/packages/nx-plugin/src/executors/internal/cli.ts index bc2a134bf..6ffaaf183 100644 --- a/packages/nx-plugin/src/executors/internal/cli.ts +++ b/packages/nx-plugin/src/executors/internal/cli.ts @@ -1,52 +1,3 @@ -export function createCliCommandString(options?: { - args?: Record; - command?: string; - bin?: string; - env?: Record; -}): string { - const { bin = '@code-pushup/cli', command, args, env } = options ?? {}; - const isFile = isFilePath(bin); - const envTerminalString = Object.entries(env ?? {}) - .map(([key, value]) => `${key}=${value}`) - .join(' '); - const commandPrefix = isFile ? 'node' : 'npx'; - const envPrefix = envTerminalString ? `${envTerminalString} ` : ''; - return `${envPrefix}${commandPrefix} ${bin} ${objectToCliArgs({ - _: command ?? [], - ...args, - }).join(' ')}`; -} - -function isFilePath(bin: string): boolean { - return ( - /\.(js|ts|mjs|cjs)$/.test(bin) || - bin.startsWith('./') || - bin.startsWith('../') || - (bin.includes('/') && !bin.startsWith('@')) - ); -} - -export function createCliCommandObject(options?: { - args?: Record; - command?: string; - bin?: string; - env?: Record; -}): import('@code-pushup/utils').ProcessConfig { - const { bin = '@code-pushup/cli', command, args, env } = options ?? {}; - const isFile = isFilePath(bin); - const envTerminalString = Object.entries(env ?? {}) - .map(([key, value]) => `${key}=${value}`) - .join(' '); - return { - command: isFile ? 'node' : 'npx', - args: [ - ...(envTerminalString ? [envTerminalString] : []), - bin, - ...objectToCliArgs({ _: command ?? [], ...args }), - ], - }; -} - type ArgumentValue = number | string | boolean | string[]; export type CliArgsObject> = T extends never diff --git a/packages/nx-plugin/src/executors/internal/cli.unit.test.ts b/packages/nx-plugin/src/executors/internal/cli.unit.test.ts index 36f79361f..e0567d150 100644 --- a/packages/nx-plugin/src/executors/internal/cli.unit.test.ts +++ b/packages/nx-plugin/src/executors/internal/cli.unit.test.ts @@ -89,81 +89,3 @@ describe('objectToCliArgs', () => { ); }); }); - -describe('createCliCommandString', () => { - it('should create command out of object for arguments', () => { - const result = createCliCommandString({ args: { verbose: true } }); - expect(result).toBe('npx @code-pushup/cli --verbose'); - }); - - it('should create command out of object for arguments with positional', () => { - const result = createCliCommandString({ - args: { _: 'autorun', verbose: true }, - }); - expect(result).toBe('npx @code-pushup/cli autorun --verbose'); - }); - - it('should use node for file paths', () => { - const result = createCliCommandString({ - bin: 'packages/cli/src/index.ts', - args: { verbose: true }, - }); - expect(result).toBe('node packages/cli/src/index.ts --verbose'); - }); - - it('should include env variables in command string', () => { - const result = createCliCommandString({ - args: { verbose: true }, - env: { - NODE_OPTIONS: '--import tsx', - TSX_TSCONFIG_PATH: 'tsconfig.base.json', - }, - }); - expect(result).toBe( - 'NODE_OPTIONS=--import tsx TSX_TSCONFIG_PATH=tsconfig.base.json npx @code-pushup/cli --verbose', - ); - }); -}); - -describe('createCliCommandObject', () => { - it('should create command out of object for arguments', () => { - expect(createCliCommandObject({ args: { verbose: true } })).toStrictEqual({ - args: ['@code-pushup/cli', '--verbose'], - command: 'npx', - }); - }); - - it('should create command out of object for arguments with positional', () => { - expect( - createCliCommandObject({ - args: { _: 'autorun', verbose: true }, - }), - ).toStrictEqual({ - args: ['@code-pushup/cli', 'autorun', '--verbose'], - command: 'npx', - }); - }); - - it('should create command out of object for arguments with bin', () => { - expect( - createCliCommandObject({ - bin: 'node_modules/@code-pushup/cli/src/bin.js', - }), - ).toStrictEqual({ - args: ['node_modules/@code-pushup/cli/src/bin.js'], - command: 'node', - }); - }); - - it('should create command out of object for arguments with env', () => { - expect( - createCliCommandObject({ - args: { verbose: true }, - env: { NODE_ENV: 'production', DEBUG: 'true' }, - }), - ).toStrictEqual({ - args: ['NODE_ENV=production DEBUG=true', '@code-pushup/cli', '--verbose'], - command: 'npx', - }); - }); -}); diff --git a/packages/nx-plugin/src/executors/internal/config.ts b/packages/nx-plugin/src/executors/internal/config.ts index 1ef022a44..fbe988587 100644 --- a/packages/nx-plugin/src/executors/internal/config.ts +++ b/packages/nx-plugin/src/executors/internal/config.ts @@ -27,17 +27,20 @@ export function persistConfig( options: Partial, context: BaseNormalizedExecutorContext, ): Partial { - const { projectConfig, workspaceRoot } = context; - - const { name: projectName = '' } = projectConfig ?? {}; const { format, - outputDir = path.join(workspaceRoot, '.code-pushup', projectName), // always in /.code-pushup/, + outputDir = '{workspaceRoot}/.code-pushup/{projectName}', filename, } = options; + const { workspaceRoot, projectConfig } = context; + const projectName = projectConfig?.name ?? ''; + const resolvedOutputDir = outputDir + .replace('{workspaceRoot}', workspaceRoot) + .replace('{projectName}', projectName); + return { - outputDir, + outputDir: resolvedOutputDir, ...(format ? { format } : {}), ...(filename ? { filename } : {}), }; diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index edf9f1963..8da647314 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -87,7 +87,7 @@ export { } from './lib/guards.js'; export { interpolate } from './lib/interpolate.js'; export { logMultipleResults } from './lib/log-results.js'; -export { Logger, logger } from './lib/logger.js'; +export { Logger, logger, formatCommand } from './lib/logger.js'; export { link, ui, type CliUi, type Column } from './lib/logging.js'; export { mergeConfigs } from './lib/merge-configs.js'; export { getProgressBar, type ProgressBar } from './lib/progress.js'; diff --git a/packages/utils/src/lib/execute-process.ts b/packages/utils/src/lib/execute-process.ts index e05bbf444..697e9d361 100644 --- a/packages/utils/src/lib/execute-process.ts +++ b/packages/utils/src/lib/execute-process.ts @@ -153,7 +153,14 @@ export type ProcessObserver = { * @param cfg - see {@link ProcessConfig} */ export function executeProcess(cfg: ProcessConfig): Promise { - const { command, args, observer, ignoreExitCode = false, ...options } = cfg; + const { + command, + args, + observer, + ignoreExitCode = false, + env, + ...options + } = cfg; const { onStdout, onStderr, onError, onComplete } = observer ?? {}; const bin = [command, ...(args ?? [])].join(' '); @@ -210,5 +217,8 @@ export function executeProcess(cfg: ProcessConfig): Promise { } }); }), + { + ...(env ? { env: env as Record } : {}), + }, ); } diff --git a/packages/utils/src/lib/logger.ts b/packages/utils/src/lib/logger.ts index c8ba41033..16949cdac 100644 --- a/packages/utils/src/lib/logger.ts +++ b/packages/utils/src/lib/logger.ts @@ -7,6 +7,7 @@ import { isEnvVarEnabled } from './env.js'; import { stringifyError } from './errors.js'; import { formatDuration, indentLines, transformLines } from './formatting.js'; import { settlePromise } from './promises.js'; +import { objectToCliArgs } from './transform'; type GroupColor = Extract; type CiPlatform = 'GitHub Actions' | 'GitLab CI/CD'; @@ -196,6 +197,41 @@ export class Logger { }); } + /** + * Formats a command string for display with status indicator. + * + * @param bin Command string with arguments. + * @param options Command options (cwd, env). + * @param status Command status ('pending' | 'success' | 'failure'). + * @returns Formatted command string with colored status indicator. + */ + #formatCommandStatus( + bin: string, + options?: { + env?: Record; + cwd?: string; + }, + status: 'pending' | 'success' | 'failure' = 'pending', + ): string { + const cwd = options?.cwd && path.relative(process.cwd(), options.cwd); + const cwdPrefix = cwd ? `${ansis.blue(cwd)} ` : ''; + const envString = + options?.env && Object.keys(options.env).length > 0 + ? Object.entries(options.env) + .map(([key, value]) => { + return ansis.gray(`${key}=${value}`); + }) + .join(' ') + : ''; + const statusColor = + status === 'pending' + ? ansis.blue('$') + : status === 'success' + ? ansis.green('$') + : ansis.red('$'); + return `${cwdPrefix}${statusColor} ${envString}${bin}`; + } + /** * Similar to {@link task}, but spinner texts are formatted as shell commands. * @@ -216,14 +252,15 @@ export class Logger { command( bin: string, worker: () => Promise, - options?: { cwd?: string }, + options?: { + env?: Record; + cwd?: string; + }, ): Promise { - const cwd = options?.cwd && path.relative(process.cwd(), options.cwd); - const cwdPrefix = cwd ? `${ansis.blue(cwd)} ` : ''; return this.#spinner(worker, { - pending: `${cwdPrefix}${ansis.blue('$')} ${bin}`, - success: () => `${cwdPrefix}${ansis.green('$')} ${bin}`, - failure: () => `${cwdPrefix}${ansis.red('$')} ${bin}`, + pending: formatCommand(bin, options, 'pending'), + success: () => formatCommand(bin, options, 'success'), + failure: () => formatCommand(bin, options, 'failure'), }); } @@ -491,3 +528,38 @@ export class Logger { * logger.info('Made with ❤️ by Code PushUp'); */ export const logger = new Logger(); + +/** + * Formats a command string for display with status indicator. + * + * @param bin Command string with arguments. + * @param options Command options (cwd, env). + * @param status Command status ('pending' | 'success' | 'failure'). + * @returns Formatted command string with colored status indicator. + */ +export function formatCommand( + bin: string, + options?: { + env?: Record; + cwd?: string; + }, + status: 'pending' | 'success' | 'failure' = 'pending', +): string { + const cwd = options?.cwd && path.relative(process.cwd(), options.cwd); + const cwdPrefix = cwd ? `${ansis.blue(cwd)} ` : ''; + const envString = + options?.env && Object.keys(options.env).length > 0 + ? Object.entries(options.env) + .map(([key, value]) => { + return ansis.gray(`${key}=${value}`); + }) + .join(' ') + : ''; + const statusColor = + status === 'pending' + ? ansis.blue('$') + : status === 'success' + ? ansis.green('$') + : ansis.red('$'); + return `${cwdPrefix}${statusColor} ${envString}${bin}`; +} diff --git a/testing/test-utils/src/lib/utils/string.ts b/testing/test-utils/src/lib/utils/string.ts index 025df0a2c..e81ae8cb4 100644 --- a/testing/test-utils/src/lib/utils/string.ts +++ b/testing/test-utils/src/lib/utils/string.ts @@ -1,5 +1,6 @@ +import ansis from 'ansis'; + // removes all color codes from the output for snapshot readability export function removeColorCodes(stdout: string) { - // eslint-disable-next-line no-control-regex - return stdout.replace(/\u001B\[\d+m/g, ''); + return ansis.strip(stdout); } From 45e33a3ddaed6bbfb33a77a55c13cd21029c3a19 Mon Sep 17 00:00:00 2001 From: John Doe Date: Tue, 11 Nov 2025 02:21:57 +0100 Subject: [PATCH 24/38] refactor: packages update --- package-lock.json | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index e6ffdcf47..6009d621b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2319,14 +2319,14 @@ }, "node_modules/@code-pushup/cli": { "version": "0.86.0", - "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/cli@1091", - "integrity": "sha512-EYJuIA7ifeKLL9hgXBbORRu3de2BboGJdw1/uSGZzNyC19wr879jysLridwvt9tLlBHv3sReTp/e3Tc/lZQIGw==", + "resolved": "https://registry.npmjs.org/@code-pushup/cli/-/cli-0.86.0.tgz", + "integrity": "sha512-9jQs2Z/ToChxLyn/nOYBIDAsBV66YMwV5pFJqPnFhvwG22dbvHs+ptIMPG/S1WG8HeA1KJQuDcULVc0SdI5pdA==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/core": "https://pkg.pr.new/code-pushup/cli/@code-pushup/core@4ba05a63b54bb4a0e9693da68f3f7bd4a2fa6dab", - "@code-pushup/models": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@4ba05a63b54bb4a0e9693da68f3f7bd4a2fa6dab", - "@code-pushup/utils": "https://pkg.pr.new/code-pushup/cli/@code-pushup/utils@4ba05a63b54bb4a0e9693da68f3f7bd4a2fa6dab", + "@code-pushup/core": "0.86.0", + "@code-pushup/models": "0.86.0", + "@code-pushup/utils": "0.86.0", "ansis": "^3.3.0", "simple-git": "^3.20.0", "yargs": "^17.7.2" @@ -2340,13 +2340,13 @@ }, "node_modules/@code-pushup/core": { "version": "0.86.0", - "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/core@4ba05a63b54bb4a0e9693da68f3f7bd4a2fa6dab", - "integrity": "sha512-XkDkIDzynBGWIXoTGY+4Z6WTYCbODXdqikDTPyoApWyHgFFycoZqLU7scDXrQrD4fCiQ2KC7hIk9H2Na8ld2PA==", + "resolved": "https://registry.npmjs.org/@code-pushup/core/-/core-0.86.0.tgz", + "integrity": "sha512-sqmaCxBp/NFqNyyr5lsh7V4R4QtaQGiyLIA8DHFjT947Mj4gaWPJ62lH0wLGQ7q4Z6IT9LNwfLT2siLruAVChA==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/models": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@4ba05a63b54bb4a0e9693da68f3f7bd4a2fa6dab", - "@code-pushup/utils": "https://pkg.pr.new/code-pushup/cli/@code-pushup/utils@4ba05a63b54bb4a0e9693da68f3f7bd4a2fa6dab", + "@code-pushup/models": "0.86.0", + "@code-pushup/utils": "0.86.0", "ansis": "^3.3.0" }, "peerDependencies": { @@ -2444,8 +2444,8 @@ }, "node_modules/@code-pushup/models": { "version": "0.86.0", - "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@4ba05a63b54bb4a0e9693da68f3f7bd4a2fa6dab", - "integrity": "sha512-86e6eX2e2oaYPrbS68YP91mTJtNnfNGW86tlzUvObiMbq0pAahaFZFeauJ+pHgMFlzKbTHPLSA2DJfrfnK/j0A==", + "resolved": "https://registry.npmjs.org/@code-pushup/models/-/models-0.86.0.tgz", + "integrity": "sha512-/mXR5ej19Pcrf+vAZHtHyRn7LeM0oT9RSzksXTrhJsSJG0CdmxmsZoNjg/m3lCzUXX61OA+nc7IzMz3FwSd72A==", "dev": true, "license": "MIT", "dependencies": { @@ -2456,13 +2456,13 @@ }, "node_modules/@code-pushup/nx-plugin": { "version": "0.86.0", - "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/nx-plugin@1091", - "integrity": "sha512-Qn4ktyOHJ7F483b9cKV0Pwr6WljhdFJ3opmv9NeXfOXBRfanB1VjkKhINNnC8vE0uG3bsr1YbfWrzr/qmc6L4g==", + "resolved": "https://registry.npmjs.org/@code-pushup/nx-plugin/-/nx-plugin-0.86.0.tgz", + "integrity": "sha512-3ac2PmMLiHs643Y0Fyj3iO8y/PmbbHkVR7l+Yf3ZDLW1HaeEB81UVHFLlLL1I16Z3QTMRJMEpQGYM13o1rSuAg==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/models": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@4ba05a63b54bb4a0e9693da68f3f7bd4a2fa6dab", - "@code-pushup/utils": "https://pkg.pr.new/code-pushup/cli/@code-pushup/utils@4ba05a63b54bb4a0e9693da68f3f7bd4a2fa6dab", + "@code-pushup/models": "0.86.0", + "@code-pushup/utils": "0.86.0", "@nx/devkit": ">=17.0.0", "nx": ">=17.0.0" } @@ -2482,12 +2482,12 @@ }, "node_modules/@code-pushup/utils": { "version": "0.86.0", - "resolved": "https://pkg.pr.new/code-pushup/cli/@code-pushup/utils@4ba05a63b54bb4a0e9693da68f3f7bd4a2fa6dab", - "integrity": "sha512-i6pHmoikxGukJliC9D2SWAHx94NcBuFZwEqHiPDB0s5HvFfCVf2gtx7KCZs172UcMV0Io8JJ69hhn/N3FT0ySw==", + "resolved": "https://registry.npmjs.org/@code-pushup/utils/-/utils-0.86.0.tgz", + "integrity": "sha512-QB64eyc9uLwvvbHjnYLUc+p1mqAV5sQLqeCqphP/JaihJuE3ZN+R1EWW8gAR8CToHkXlaEXvNqi8qPja22ZHPQ==", "dev": true, "license": "MIT", "dependencies": { - "@code-pushup/models": "https://pkg.pr.new/code-pushup/cli/@code-pushup/models@4ba05a63b54bb4a0e9693da68f3f7bd4a2fa6dab", + "@code-pushup/models": "0.86.0", "@isaacs/cliui": "^8.0.2", "@poppinss/cliui": "^6.4.0", "ansis": "^3.3.0", From c8ab27c5afe26fe5b3655f0ca33e7eab8428bc43 Mon Sep 17 00:00:00 2001 From: John Doe Date: Tue, 11 Nov 2025 02:25:52 +0100 Subject: [PATCH 25/38] refactor: fix text --- packages/utils/src/lib/execute-process.int.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/utils/src/lib/execute-process.int.test.ts b/packages/utils/src/lib/execute-process.int.test.ts index 8c7e5d97c..aa795f694 100644 --- a/packages/utils/src/lib/execute-process.int.test.ts +++ b/packages/utils/src/lib/execute-process.int.test.ts @@ -104,6 +104,7 @@ describe('executeProcess', () => { expect(logger.command).toHaveBeenCalledWith( 'echo hello', expect.any(Function), + {}, ); }); From 4dcbedb17ec470e42efb8fed766977b33e0bd8c8 Mon Sep 17 00:00:00 2001 From: John Doe Date: Tue, 11 Nov 2025 02:30:21 +0100 Subject: [PATCH 26/38] refactor: fix import --- packages/utils/src/lib/logger.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/utils/src/lib/logger.ts b/packages/utils/src/lib/logger.ts index 16949cdac..c0164c79e 100644 --- a/packages/utils/src/lib/logger.ts +++ b/packages/utils/src/lib/logger.ts @@ -7,7 +7,6 @@ import { isEnvVarEnabled } from './env.js'; import { stringifyError } from './errors.js'; import { formatDuration, indentLines, transformLines } from './formatting.js'; import { settlePromise } from './promises.js'; -import { objectToCliArgs } from './transform'; type GroupColor = Extract; type CiPlatform = 'GitHub Actions' | 'GitLab CI/CD'; From 2d12182aaf2b9581cf7d015db192826f615279ca Mon Sep 17 00:00:00 2001 From: John Doe Date: Tue, 11 Nov 2025 02:36:01 +0100 Subject: [PATCH 27/38] refactor: fix logger spacing --- packages/utils/src/lib/logger.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/utils/src/lib/logger.ts b/packages/utils/src/lib/logger.ts index c0164c79e..2d2a0a1e6 100644 --- a/packages/utils/src/lib/logger.ts +++ b/packages/utils/src/lib/logger.ts @@ -548,11 +548,12 @@ export function formatCommand( const cwdPrefix = cwd ? `${ansis.blue(cwd)} ` : ''; const envString = options?.env && Object.keys(options.env).length > 0 - ? Object.entries(options.env) - .map(([key, value]) => { + ? [ + ...Object.entries(options.env).map(([key, value]) => { return ansis.gray(`${key}=${value}`); - }) - .join(' ') + }), + ' ', + ].join(' ') : ''; const statusColor = status === 'pending' From 804319bf934c2e4f3cca79837ab26211840c7a64 Mon Sep 17 00:00:00 2001 From: John Doe Date: Tue, 11 Nov 2025 02:51:28 +0100 Subject: [PATCH 28/38] refactor: add logger helper unit tests --- packages/utils/src/lib/logger.unit.test.ts | 64 ++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 packages/utils/src/lib/logger.unit.test.ts diff --git a/packages/utils/src/lib/logger.unit.test.ts b/packages/utils/src/lib/logger.unit.test.ts new file mode 100644 index 000000000..1bda9f0ad --- /dev/null +++ b/packages/utils/src/lib/logger.unit.test.ts @@ -0,0 +1,64 @@ +import ansis from 'ansis'; +import path from 'node:path'; +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { formatCommand } from './logger.js'; + +describe('formatCommand', () => { + const originalCwd = process.cwd(); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it('should format complex command with cwd, env, and status', () => { + vi.spyOn(path, 'relative').mockReturnValue(''); + expect( + formatCommand( + 'npx eslint . --format=json', + { + cwd: '', + env: { CP_VERBOSE: true }, + }, + 'failure', + ), + ).toBe( + `${ansis.blue('')} ${ansis.red('$')} ${ansis.gray('CP_VERBOSE=true')} npx eslint . --format=json`, + ); + }); + + it.each([ + [undefined, ansis.blue], // default to pending + ['pending', ansis.blue], + ['success', ansis.green], + ['failure', ansis.red], + ])(`should format command status %s explicitly`, (status, color) => { + expect( + formatCommand('npx eslint . --format=json', {}, 'pending'), + ).toContain(`${color('$')}`); + }); + + it('should include cwd prefix when cwd is provided and different from process.cwd()', () => { + const mockCwd = path.join(originalCwd, 'src'); + vi.spyOn(path, 'relative').mockReturnValue('src'); + + expect(formatCommand('npx -v', { cwd: mockCwd })).toStartWith( + `${ansis.blue('src')} `, + ); + }); + + it('should not include cwd prefix when cwd is same as process.cwd()', () => { + vi.spyOn(path, 'relative').mockReturnValue(''); + expect(formatCommand('npx -v', { cwd: originalCwd })).toStartWith( + `${ansis.blue('$')}`, + ); + }); + + it('should format command with single environment variable', () => { + const result = formatCommand('npx eslint .', { + env: { NODE_ENV: 'test' }, + }); + expect(result).toBe( + `${ansis.blue('$')} ${ansis.gray('NODE_ENV=test')} npx eslint .`, + ); + }); +}); From a54e5dcb62f82a3503f78e384ae51b6863985de0 Mon Sep 17 00:00:00 2001 From: John Doe Date: Tue, 11 Nov 2025 03:01:02 +0100 Subject: [PATCH 29/38] refactor: add env and cwd to logger in execProcess --- packages/utils/src/lib/execute-process.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/utils/src/lib/execute-process.ts b/packages/utils/src/lib/execute-process.ts index 697e9d361..7b54f7889 100644 --- a/packages/utils/src/lib/execute-process.ts +++ b/packages/utils/src/lib/execute-process.ts @@ -159,6 +159,7 @@ export function executeProcess(cfg: ProcessConfig): Promise { observer, ignoreExitCode = false, env, + cwd, ...options } = cfg; const { onStdout, onStderr, onError, onComplete } = observer ?? {}; @@ -173,7 +174,8 @@ export function executeProcess(cfg: ProcessConfig): Promise { // shell:true tells Windows to use shell command for spawning a child process // https://stackoverflow.com/questions/60386867/node-spawn-child-process-not-working-in-windows shell: true, - windowsHide: true, + ...(env && { env: env as Record }), + ...(cwd && { cwd: cwd as string }), ...options, }) as ChildProcessByStdio; @@ -218,6 +220,7 @@ export function executeProcess(cfg: ProcessConfig): Promise { }); }), { + ...(cwd && { cwd: cwd as string }), ...(env ? { env: env as Record } : {}), }, ); From 0a8af75b7fe96fcb89d348b4a7662ae1a8d8bb27 Mon Sep 17 00:00:00 2001 From: John Doe Date: Tue, 11 Nov 2025 03:29:58 +0100 Subject: [PATCH 30/38] refactor: fix unit tests --- .../src/executors/cli/executor.int.test.ts | 2 +- .../nx-plugin/src/executors/cli/executor.ts | 39 +++++-------- .../src/executors/cli/executor.unit.test.ts | 42 +++++++++----- packages/utils/src/lib/logger.ts | 56 ++++--------------- packages/utils/src/lib/logger.unit.test.ts | 29 ++++++---- 5 files changed, 74 insertions(+), 94 deletions(-) diff --git a/packages/nx-plugin/src/executors/cli/executor.int.test.ts b/packages/nx-plugin/src/executors/cli/executor.int.test.ts index 5a1b5ee70..0ed775436 100644 --- a/packages/nx-plugin/src/executors/cli/executor.int.test.ts +++ b/packages/nx-plugin/src/executors/cli/executor.int.test.ts @@ -35,7 +35,7 @@ describe('runAutorunExecutor', () => { expect(success).toBe(true); const cleanCommand = removeColorCodes(command || ''); expect(cleanCommand).toMatch('npx @code-pushup/cli'); - expect(cleanCommand).toMatch('CP_VERBOSE=true'); + expect(cleanCommand).toMatch('CP_VERBOSE="true"'); expect(executeProcessSpy).toHaveBeenCalledTimes(1); expect(executeProcessSpy).toHaveBeenCalledWith( expect.objectContaining({ diff --git a/packages/nx-plugin/src/executors/cli/executor.ts b/packages/nx-plugin/src/executors/cli/executor.ts index fa9e34425..c28381253 100644 --- a/packages/nx-plugin/src/executors/cli/executor.ts +++ b/packages/nx-plugin/src/executors/cli/executor.ts @@ -44,30 +44,21 @@ export default async function runAutorunExecutor( if (dryRun) { logger.warn(`DryRun execution of: \n ${formattedBinString}`); } else { - logger.command( - binString, - async () => { - try { - await executeProcess({ - command, - args: [...positionals, ...args], - ...(envVariables && { env: envVariables }), - ...(cwd ? { cwd } : {}), - }); - } catch (error) { - logger.error(stringifyError(error)); - return { - success: false, - command: formattedBinString, - error: error instanceof Error ? error : new Error(`${error}`), - }; - } - }, - { - env: envVariables, - cwd, - }, - ); + try { + await executeProcess({ + command, + args: [...positionals, ...args], + ...(envVariables && { env: envVariables }), + ...(cwd ? { cwd } : {}), + }); + } catch (error) { + logger.error(stringifyError(error)); + return { + success: false, + command: formattedBinString, + error: error instanceof Error ? error : new Error(`${error}`), + }; + } } return { diff --git a/packages/nx-plugin/src/executors/cli/executor.unit.test.ts b/packages/nx-plugin/src/executors/cli/executor.unit.test.ts index ccfb55eeb..c386174f6 100644 --- a/packages/nx-plugin/src/executors/cli/executor.unit.test.ts +++ b/packages/nx-plugin/src/executors/cli/executor.unit.test.ts @@ -58,13 +58,12 @@ describe('runAutorunExecutor', () => { expect(success).toBe(true); expect(removeColorCodes(command || '')).toMatch('npx @code-pushup/cli'); - // The executor doesn't await logger.command, so executeProcess is called asynchronously - // We verify logger.command was called, which will execute executeProcess - expect(loggerCommandSpy).toHaveBeenCalledTimes(1); - expect(loggerCommandSpy).toHaveBeenCalledWith( - expect.stringContaining('npx @code-pushup/cli'), - expect.any(Function), + // The executor now calls executeProcess directly + expect(executeProcessSpy).toHaveBeenCalledTimes(1); + expect(executeProcessSpy).toHaveBeenCalledWith( expect.objectContaining({ + command: 'npx', + args: expect.arrayContaining(['@code-pushup/cli']), cwd: MEMFS_VOLUME, env: expect.any(Object), }), @@ -83,7 +82,12 @@ describe('runAutorunExecutor', () => { expect(output.success).toBe(true); const commandWithoutAnsi = removeColorCodes(output.command || ''); expect(commandWithoutAnsi).toMatch('cwd-form-context'); - expect(loggerCommandSpy).toHaveBeenCalledTimes(1); + expect(executeProcessSpy).toHaveBeenCalledTimes(1); + expect(executeProcessSpy).toHaveBeenCalledWith( + expect.objectContaining({ + cwd: 'cwd-form-context', + }), + ); }); it('should get env variables from options', async () => { @@ -97,8 +101,16 @@ describe('runAutorunExecutor', () => { executorContext('utils'), ); const commandWithoutAnsi = removeColorCodes(command || ''); - expect(commandWithoutAnsi).toMatch('CP_API_KEY=123456789'); - expect(commandWithoutAnsi).toMatch('CP_PROJECT=cli'); + expect(commandWithoutAnsi).toMatch('CP_API_KEY="123456789"'); + expect(commandWithoutAnsi).toMatch('CP_PROJECT="cli"'); + expect(executeProcessSpy).toHaveBeenCalledWith( + expect.objectContaining({ + env: expect.objectContaining({ + CP_API_KEY: '123456789', + CP_PROJECT: 'cli', + }), + }), + ); }); it('should process executorOptions', async () => { @@ -147,12 +159,12 @@ describe('runAutorunExecutor', () => { { ...executorContext('github-action'), cwd: '' }, ); - expect(removeColorCodes(command || '')).toMatch('CP_VERBOSE=true'); - expect(loggerCommandSpy).toHaveBeenCalledTimes(1); - expect(loggerCommandSpy).toHaveBeenCalledWith( - expect.stringContaining('npx @code-pushup/cli'), - expect.any(Function), - expect.objectContaining({ env: { CP_VERBOSE: 'true' } }), + expect(removeColorCodes(command || '')).toMatch('CP_VERBOSE="true"'); + expect(executeProcessSpy).toHaveBeenCalledTimes(1); + expect(executeProcessSpy).toHaveBeenCalledWith( + expect.objectContaining({ + env: expect.objectContaining({ CP_VERBOSE: 'true' }), + }), ); }); diff --git a/packages/utils/src/lib/logger.ts b/packages/utils/src/lib/logger.ts index 2d2a0a1e6..5daf320cb 100644 --- a/packages/utils/src/lib/logger.ts +++ b/packages/utils/src/lib/logger.ts @@ -196,41 +196,6 @@ export class Logger { }); } - /** - * Formats a command string for display with status indicator. - * - * @param bin Command string with arguments. - * @param options Command options (cwd, env). - * @param status Command status ('pending' | 'success' | 'failure'). - * @returns Formatted command string with colored status indicator. - */ - #formatCommandStatus( - bin: string, - options?: { - env?: Record; - cwd?: string; - }, - status: 'pending' | 'success' | 'failure' = 'pending', - ): string { - const cwd = options?.cwd && path.relative(process.cwd(), options.cwd); - const cwdPrefix = cwd ? `${ansis.blue(cwd)} ` : ''; - const envString = - options?.env && Object.keys(options.env).length > 0 - ? Object.entries(options.env) - .map(([key, value]) => { - return ansis.gray(`${key}=${value}`); - }) - .join(' ') - : ''; - const statusColor = - status === 'pending' - ? ansis.blue('$') - : status === 'success' - ? ansis.green('$') - : ansis.red('$'); - return `${cwdPrefix}${statusColor} ${envString}${bin}`; - } - /** * Similar to {@link task}, but spinner texts are formatted as shell commands. * @@ -545,21 +510,24 @@ export function formatCommand( status: 'pending' | 'success' | 'failure' = 'pending', ): string { const cwd = options?.cwd && path.relative(process.cwd(), options.cwd); - const cwdPrefix = cwd ? `${ansis.blue(cwd)} ` : ''; + const cwdPrefix = cwd ? ansis.blue(cwd) : ''; const envString = options?.env && Object.keys(options.env).length > 0 - ? [ - ...Object.entries(options.env).map(([key, value]) => { - return ansis.gray(`${key}=${value}`); - }), - ' ', - ].join(' ') - : ''; + ? Object.entries(options.env).map(([key, value]) => { + return ansis.gray(`${key}="${value}"`); + }) + : []; const statusColor = status === 'pending' ? ansis.blue('$') : status === 'success' ? ansis.green('$') : ansis.red('$'); - return `${cwdPrefix}${statusColor} ${envString}${bin}`; + + return [ + ...(cwdPrefix ? [cwdPrefix] : []), + statusColor, + ...envString, + bin, + ].join(' '); } diff --git a/packages/utils/src/lib/logger.unit.test.ts b/packages/utils/src/lib/logger.unit.test.ts index 1bda9f0ad..7ce28b68f 100644 --- a/packages/utils/src/lib/logger.unit.test.ts +++ b/packages/utils/src/lib/logger.unit.test.ts @@ -22,19 +22,19 @@ describe('formatCommand', () => { 'failure', ), ).toBe( - `${ansis.blue('')} ${ansis.red('$')} ${ansis.gray('CP_VERBOSE=true')} npx eslint . --format=json`, + `${ansis.blue('')} ${ansis.red('$')} ${ansis.gray('CP_VERBOSE="true"')} npx eslint . --format=json`, ); }); it.each([ [undefined, ansis.blue], // default to pending - ['pending', ansis.blue], - ['success', ansis.green], - ['failure', ansis.red], + ['pending' as const, ansis.blue], + ['success' as const, ansis.green], + ['failure' as const, ansis.red], ])(`should format command status %s explicitly`, (status, color) => { - expect( - formatCommand('npx eslint . --format=json', {}, 'pending'), - ).toContain(`${color('$')}`); + expect(formatCommand('npx eslint . --format=json', {}, status)).toContain( + `${color('$')}`, + ); }); it('should include cwd prefix when cwd is provided and different from process.cwd()', () => { @@ -53,12 +53,21 @@ describe('formatCommand', () => { ); }); - it('should format command with single environment variable', () => { + it('should format command with multiple environment variables', () => { const result = formatCommand('npx eslint .', { - env: { NODE_ENV: 'test' }, + env: { NODE_ENV: 'test', NODE_OPTIONS: '--import tsx' }, + }); + expect(result).toStartWith( + `${ansis.blue('$')} ${ansis.gray('NODE_ENV="test"')} ${ansis.gray('NODE_OPTIONS="--import tsx"')}`, + ); + }); + + it('should format command with environment variable containing spaces', () => { + const result = formatCommand('node packages/cli/src/index.ts', { + env: { NODE_OPTIONS: '--import tsx' }, }); expect(result).toBe( - `${ansis.blue('$')} ${ansis.gray('NODE_ENV=test')} npx eslint .`, + `${ansis.blue('$')} ${ansis.gray('NODE_OPTIONS="--import tsx"')} node packages/cli/src/index.ts`, ); }); }); From 50d9f2c224f289620ed76c4a5b8d2e3d1a91eb54 Mon Sep 17 00:00:00 2001 From: John Doe Date: Tue, 11 Nov 2025 03:42:19 +0100 Subject: [PATCH 31/38] refactor: wip --- .../nx-plugin/src/executors/cli/executor.ts | 30 +++++++-- .../src/executors/cli/executor.unit.test.ts | 65 +++++++++++++++++++ 2 files changed, 91 insertions(+), 4 deletions(-) diff --git a/packages/nx-plugin/src/executors/cli/executor.ts b/packages/nx-plugin/src/executors/cli/executor.ts index c28381253..7b881a0c0 100644 --- a/packages/nx-plugin/src/executors/cli/executor.ts +++ b/packages/nx-plugin/src/executors/cli/executor.ts @@ -22,15 +22,37 @@ export default async function runAutorunExecutor( ...argsObj } = terminalAndExecutorOptions; const command = bin ? `node` : 'npx'; + + // Extract --import flags from NODE_OPTIONS if present (Node.js doesn't allow --import in NODE_OPTIONS) + let importArgs: string[] = []; + const envVariables = { ...env }; + + if (bin && env?.NODE_OPTIONS) { + const nodeOptions = env.NODE_OPTIONS; + // Match --import or --import= + const importMatch = nodeOptions.match(/--import(?:\s+|=)(\S+)/); + if (importMatch && importMatch[1]) { + importArgs = ['--import', importMatch[1]]; + // Remove --import flag from NODE_OPTIONS + const cleaned = nodeOptions.replace(/--import(?:\s+|=)\S+/g, '').trim(); + if (cleaned) { + envVariables.NODE_OPTIONS = cleaned; + } else { + delete envVariables.NODE_OPTIONS; + } + } + } + const positionals = [ + ...importArgs, // Add --import flags before the script when using node bin ?? '@code-pushup/cli', ...(cliCommand ? [cliCommand] : []), ]; const args = objectToCliArgs(argsObj); - const envVariables = { - ...env, - ...(verbose && { CP_VERBOSE: 'true' }), - }; + + if (verbose) { + envVariables.CP_VERBOSE = 'true'; + } const { logger, stringifyError, formatCommand } = await import( '@code-pushup/utils' diff --git a/packages/nx-plugin/src/executors/cli/executor.unit.test.ts b/packages/nx-plugin/src/executors/cli/executor.unit.test.ts index c386174f6..ec63c63cb 100644 --- a/packages/nx-plugin/src/executors/cli/executor.unit.test.ts +++ b/packages/nx-plugin/src/executors/cli/executor.unit.test.ts @@ -178,4 +178,69 @@ describe('runAutorunExecutor', () => { expect(warnMessage).toContain('DryRun execution of:'); expect(warnMessage).toContain('npx @code-pushup/cli'); }); + + it('should extract --import from NODE_OPTIONS and pass as direct argument when bin is set', async () => { + const { command } = await runAutorunExecutor( + { + bin: 'packages/cli/src/index.ts', + env: { + NODE_OPTIONS: '--import tsx', + TSX_TSCONFIG_PATH: 'tsconfig.base.json', + }, + }, + executorContext('utils'), + ); + + const commandWithoutAnsi = removeColorCodes(command || ''); + expect(commandWithoutAnsi).toMatch( + 'node --import tsx packages/cli/src/index.ts', + ); + expect(executeProcessSpy).toHaveBeenCalledWith( + expect.objectContaining({ + command: 'node', + args: expect.arrayContaining([ + '--import', + 'tsx', + 'packages/cli/src/index.ts', + ]), + env: expect.objectContaining({ + TSX_TSCONFIG_PATH: 'tsconfig.base.json', + }), + }), + ); + // NODE_OPTIONS should be removed since it only contained --import + expect(executeProcessSpy.mock.calls[0]?.[0]?.env).not.toHaveProperty( + 'NODE_OPTIONS', + ); + }); + + it('should preserve other NODE_OPTIONS when extracting --import', async () => { + const { command } = await runAutorunExecutor( + { + bin: 'packages/cli/src/index.ts', + env: { + NODE_OPTIONS: '--max-old-space-size=4096 --import tsx', + }, + }, + executorContext('utils'), + ); + + const commandWithoutAnsi = removeColorCodes(command || ''); + expect(commandWithoutAnsi).toMatch( + 'node --import tsx packages/cli/src/index.ts', + ); + expect(executeProcessSpy).toHaveBeenCalledWith( + expect.objectContaining({ + command: 'node', + args: expect.arrayContaining([ + '--import', + 'tsx', + 'packages/cli/src/index.ts', + ]), + env: expect.objectContaining({ + NODE_OPTIONS: '--max-old-space-size=4096', + }), + }), + ); + }); }); From 256ea00b2dc1930697eab7cb193488cf5f0aaf39 Mon Sep 17 00:00:00 2001 From: John Doe Date: Tue, 11 Nov 2025 03:48:10 +0100 Subject: [PATCH 32/38] refactor: wip revert --- .../nx-plugin/src/executors/cli/executor.ts | 30 ++------- .../src/executors/cli/executor.unit.test.ts | 65 ------------------- 2 files changed, 4 insertions(+), 91 deletions(-) diff --git a/packages/nx-plugin/src/executors/cli/executor.ts b/packages/nx-plugin/src/executors/cli/executor.ts index 7b881a0c0..c28381253 100644 --- a/packages/nx-plugin/src/executors/cli/executor.ts +++ b/packages/nx-plugin/src/executors/cli/executor.ts @@ -22,37 +22,15 @@ export default async function runAutorunExecutor( ...argsObj } = terminalAndExecutorOptions; const command = bin ? `node` : 'npx'; - - // Extract --import flags from NODE_OPTIONS if present (Node.js doesn't allow --import in NODE_OPTIONS) - let importArgs: string[] = []; - const envVariables = { ...env }; - - if (bin && env?.NODE_OPTIONS) { - const nodeOptions = env.NODE_OPTIONS; - // Match --import or --import= - const importMatch = nodeOptions.match(/--import(?:\s+|=)(\S+)/); - if (importMatch && importMatch[1]) { - importArgs = ['--import', importMatch[1]]; - // Remove --import flag from NODE_OPTIONS - const cleaned = nodeOptions.replace(/--import(?:\s+|=)\S+/g, '').trim(); - if (cleaned) { - envVariables.NODE_OPTIONS = cleaned; - } else { - delete envVariables.NODE_OPTIONS; - } - } - } - const positionals = [ - ...importArgs, // Add --import flags before the script when using node bin ?? '@code-pushup/cli', ...(cliCommand ? [cliCommand] : []), ]; const args = objectToCliArgs(argsObj); - - if (verbose) { - envVariables.CP_VERBOSE = 'true'; - } + const envVariables = { + ...env, + ...(verbose && { CP_VERBOSE: 'true' }), + }; const { logger, stringifyError, formatCommand } = await import( '@code-pushup/utils' diff --git a/packages/nx-plugin/src/executors/cli/executor.unit.test.ts b/packages/nx-plugin/src/executors/cli/executor.unit.test.ts index ec63c63cb..c386174f6 100644 --- a/packages/nx-plugin/src/executors/cli/executor.unit.test.ts +++ b/packages/nx-plugin/src/executors/cli/executor.unit.test.ts @@ -178,69 +178,4 @@ describe('runAutorunExecutor', () => { expect(warnMessage).toContain('DryRun execution of:'); expect(warnMessage).toContain('npx @code-pushup/cli'); }); - - it('should extract --import from NODE_OPTIONS and pass as direct argument when bin is set', async () => { - const { command } = await runAutorunExecutor( - { - bin: 'packages/cli/src/index.ts', - env: { - NODE_OPTIONS: '--import tsx', - TSX_TSCONFIG_PATH: 'tsconfig.base.json', - }, - }, - executorContext('utils'), - ); - - const commandWithoutAnsi = removeColorCodes(command || ''); - expect(commandWithoutAnsi).toMatch( - 'node --import tsx packages/cli/src/index.ts', - ); - expect(executeProcessSpy).toHaveBeenCalledWith( - expect.objectContaining({ - command: 'node', - args: expect.arrayContaining([ - '--import', - 'tsx', - 'packages/cli/src/index.ts', - ]), - env: expect.objectContaining({ - TSX_TSCONFIG_PATH: 'tsconfig.base.json', - }), - }), - ); - // NODE_OPTIONS should be removed since it only contained --import - expect(executeProcessSpy.mock.calls[0]?.[0]?.env).not.toHaveProperty( - 'NODE_OPTIONS', - ); - }); - - it('should preserve other NODE_OPTIONS when extracting --import', async () => { - const { command } = await runAutorunExecutor( - { - bin: 'packages/cli/src/index.ts', - env: { - NODE_OPTIONS: '--max-old-space-size=4096 --import tsx', - }, - }, - executorContext('utils'), - ); - - const commandWithoutAnsi = removeColorCodes(command || ''); - expect(commandWithoutAnsi).toMatch( - 'node --import tsx packages/cli/src/index.ts', - ); - expect(executeProcessSpy).toHaveBeenCalledWith( - expect.objectContaining({ - command: 'node', - args: expect.arrayContaining([ - '--import', - 'tsx', - 'packages/cli/src/index.ts', - ]), - env: expect.objectContaining({ - NODE_OPTIONS: '--max-old-space-size=4096', - }), - }), - ); - }); }); From 2b053078ab257364ad3ed2904c92ceb8a54e2020 Mon Sep 17 00:00:00 2001 From: John Doe Date: Tue, 11 Nov 2025 08:25:53 +0100 Subject: [PATCH 33/38] refactor: wip 1 --- packages/nx-plugin/src/executors/cli/executor.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/nx-plugin/src/executors/cli/executor.ts b/packages/nx-plugin/src/executors/cli/executor.ts index c28381253..0464d32b9 100644 --- a/packages/nx-plugin/src/executors/cli/executor.ts +++ b/packages/nx-plugin/src/executors/cli/executor.ts @@ -48,7 +48,11 @@ export default async function runAutorunExecutor( await executeProcess({ command, args: [...positionals, ...args], - ...(envVariables && { env: envVariables }), + ...(envVariables && { + env: Object.fromEntries( + Object.entries(envVariables).map(([v, k]) => [v, `"${k}"`]), + ), + }), ...(cwd ? { cwd } : {}), }); } catch (error) { From cbf9d19197b5ec5db89bdfdfdd7ffc262bc3c729 Mon Sep 17 00:00:00 2001 From: John Doe Date: Tue, 11 Nov 2025 08:29:42 +0100 Subject: [PATCH 34/38] refactor: move objToCliArgs parts and reuse --- .../nx-plugin/src/executors/cli/executor.ts | 8 +- .../nx-plugin/src/executors/internal/cli.ts | 54 ----------- .../src/executors/internal/cli.unit.test.ts | 91 ------------------- packages/utils/src/lib/transform.ts | 4 + packages/utils/src/lib/transform.unit.test.ts | 6 ++ 5 files changed, 14 insertions(+), 149 deletions(-) delete mode 100644 packages/nx-plugin/src/executors/internal/cli.ts delete mode 100644 packages/nx-plugin/src/executors/internal/cli.unit.test.ts diff --git a/packages/nx-plugin/src/executors/cli/executor.ts b/packages/nx-plugin/src/executors/cli/executor.ts index 0464d32b9..0f7764e76 100644 --- a/packages/nx-plugin/src/executors/cli/executor.ts +++ b/packages/nx-plugin/src/executors/cli/executor.ts @@ -1,6 +1,5 @@ import { type ExecutorContext } from '@nx/devkit'; import { executeProcess } from '../../internal/execute-process.js'; -import { objectToCliArgs } from '../internal/cli.js'; import type { AutorunCommandExecutorOptions } from './schema.js'; export type ExecutorOutput = { @@ -13,6 +12,9 @@ export default async function runAutorunExecutor( terminalAndExecutorOptions: AutorunCommandExecutorOptions, { cwd }: ExecutorContext, ): Promise { + const { logger, stringifyError, formatCommand, objectToCliArgs } = + await import('@code-pushup/utils'); + const { dryRun, verbose, @@ -21,6 +23,7 @@ export default async function runAutorunExecutor( bin, ...argsObj } = terminalAndExecutorOptions; + const command = bin ? `node` : 'npx'; const positionals = [ bin ?? '@code-pushup/cli', @@ -32,9 +35,6 @@ export default async function runAutorunExecutor( ...(verbose && { CP_VERBOSE: 'true' }), }; - const { logger, stringifyError, formatCommand } = await import( - '@code-pushup/utils' - ); const binString = `${command} ${positionals.join(' ')} ${args.join(' ')}`; const formattedBinString = formatCommand(binString, { env: envVariables, diff --git a/packages/nx-plugin/src/executors/internal/cli.ts b/packages/nx-plugin/src/executors/internal/cli.ts deleted file mode 100644 index 6ffaaf183..000000000 --- a/packages/nx-plugin/src/executors/internal/cli.ts +++ /dev/null @@ -1,54 +0,0 @@ -type ArgumentValue = number | string | boolean | string[]; -export type CliArgsObject> = - T extends never - ? Record | { _: string } - : T; - -// @TODO import from @code-pushup/utils => get rid of poppins for cjs support -export function objectToCliArgs< - T extends object = Record, ->(params?: CliArgsObject): string[] { - if (!params) { - return []; - } - - return Object.entries(params).flatMap(([key, value]) => { - // process/file/script - if (key === '_') { - return (Array.isArray(value) ? value : [`${value}`]).filter( - v => v != null, - ); - } - - const prefix = key.length === 1 ? '-' : '--'; - // "-*" arguments (shorthands) - if (Array.isArray(value)) { - return value.map(v => `${prefix}${key}="${v}"`); - } - - if (typeof value === 'object') { - return Object.entries(value as Record).flatMap( - // transform nested objects to the dot notation `key.subkey` - ([k, v]) => objectToCliArgs({ [`${key}.${k}`]: v }), - ); - } - - if (typeof value === 'string') { - return [`${prefix}${key}="${value}"`]; - } - - if (typeof value === 'number') { - return [`${prefix}${key}=${value}`]; - } - - if (typeof value === 'boolean') { - return [`${prefix}${value ? '' : 'no-'}${key}`]; - } - - if (value === undefined) { - return []; - } - - throw new Error(`Unsupported type ${typeof value} for key ${key}`); - }); -} diff --git a/packages/nx-plugin/src/executors/internal/cli.unit.test.ts b/packages/nx-plugin/src/executors/internal/cli.unit.test.ts deleted file mode 100644 index e0567d150..000000000 --- a/packages/nx-plugin/src/executors/internal/cli.unit.test.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { describe, expect, it } from 'vitest'; -import { - createCliCommandObject, - createCliCommandString, - objectToCliArgs, -} from './cli.js'; - -describe('objectToCliArgs', () => { - it('should empty params', () => { - const result = objectToCliArgs(); - expect(result).toEqual([]); - }); - - it('should handle the "_" argument as script', () => { - const params = { _: 'bin.js' }; - const result = objectToCliArgs(params); - expect(result).toEqual(['bin.js']); - }); - - it('should handle the "_" argument with multiple values', () => { - const params = { _: ['bin.js', '--help'] }; - const result = objectToCliArgs(params); - expect(result).toEqual(['bin.js', '--help']); - }); - - it('should handle shorthands arguments', () => { - const params = { - e: `test`, - }; - const result = objectToCliArgs(params); - expect(result).toEqual([`-e="${params.e}"`]); - }); - - it('should handle string arguments', () => { - const params = { name: 'Juanita' }; - const result = objectToCliArgs(params); - expect(result).toEqual(['--name="Juanita"']); - }); - - it('should handle number arguments', () => { - const params = { parallel: 5 }; - const result = objectToCliArgs(params); - expect(result).toEqual(['--parallel=5']); - }); - - it('should handle boolean arguments', () => { - const params = { progress: true }; - const result = objectToCliArgs(params); - expect(result).toEqual(['--progress']); - }); - - it('should handle negated boolean arguments', () => { - const params = { progress: false }; - const result = objectToCliArgs(params); - expect(result).toEqual(['--no-progress']); - }); - - it('should handle array of string arguments', () => { - const params = { format: ['json', 'md'] }; - const result = objectToCliArgs(params); - expect(result).toEqual(['--format="json"', '--format="md"']); - }); - - it('should handle objects', () => { - const params = { format: { json: 'simple' } }; - const result = objectToCliArgs(params); - expect(result).toStrictEqual(['--format.json="simple"']); - }); - - it('should handle nested objects', () => { - const params = { persist: { format: ['json', 'md'], verbose: false } }; - const result = objectToCliArgs(params); - expect(result).toEqual([ - '--persist.format="json"', - '--persist.format="md"', - '--no-persist.verbose', - ]); - }); - - it('should handle objects with undefined', () => { - const params = { format: undefined }; - const result = objectToCliArgs(params); - expect(result).toStrictEqual([]); - }); - - it('should throw error for unsupported type', () => { - expect(() => objectToCliArgs({ param: Symbol('') })).toThrow( - 'Unsupported type', - ); - }); -}); diff --git a/packages/utils/src/lib/transform.ts b/packages/utils/src/lib/transform.ts index 86caf9aef..538b9fd1a 100644 --- a/packages/utils/src/lib/transform.ts +++ b/packages/utils/src/lib/transform.ts @@ -102,6 +102,10 @@ export function objectToCliArgs< return [`${prefix}${value ? '' : 'no-'}${key}`]; } + if (value === null || value === undefined) { + return []; + } + throw new Error(`Unsupported type ${typeof value} for key ${key}`); }); } diff --git a/packages/utils/src/lib/transform.unit.test.ts b/packages/utils/src/lib/transform.unit.test.ts index b72982e3d..3add11f0a 100644 --- a/packages/utils/src/lib/transform.unit.test.ts +++ b/packages/utils/src/lib/transform.unit.test.ts @@ -226,6 +226,12 @@ describe('objectToCliArgs', () => { ]); }); + it('should handle objects with undefined', () => { + const params = { format: undefined }; + const result = objectToCliArgs(params); + expect(result).toStrictEqual([]); + }); + it('should throw error for unsupported type', () => { const params = { unsupported: undefined as any }; expect(() => objectToCliArgs(params)).toThrow('Unsupported type'); From 5559d5f468768cf232a54bb4fb0d54b05124e7e2 Mon Sep 17 00:00:00 2001 From: John Doe Date: Tue, 11 Nov 2025 08:33:27 +0100 Subject: [PATCH 35/38] refactor: fix unit tests --- packages/nx-plugin/src/executors/cli/executor.ts | 6 +----- packages/nx-plugin/src/index.ts | 1 - packages/utils/src/lib/transform.unit.test.ts | 2 +- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/nx-plugin/src/executors/cli/executor.ts b/packages/nx-plugin/src/executors/cli/executor.ts index 0f7764e76..4fe49a283 100644 --- a/packages/nx-plugin/src/executors/cli/executor.ts +++ b/packages/nx-plugin/src/executors/cli/executor.ts @@ -48,11 +48,7 @@ export default async function runAutorunExecutor( await executeProcess({ command, args: [...positionals, ...args], - ...(envVariables && { - env: Object.fromEntries( - Object.entries(envVariables).map(([v, k]) => [v, `"${k}"`]), - ), - }), + ...(envVariables && { env: envVariables }), ...(cwd ? { cwd } : {}), }); } catch (error) { diff --git a/packages/nx-plugin/src/index.ts b/packages/nx-plugin/src/index.ts index 6c6f4b08f..cdf7f48f3 100644 --- a/packages/nx-plugin/src/index.ts +++ b/packages/nx-plugin/src/index.ts @@ -2,7 +2,6 @@ import { plugin } from './plugin/index.js'; export { createNodes, createNodesV2 } from './plugin/index.js'; export type { AutorunCommandExecutorOptions } from './executors/cli/schema.js'; -export { objectToCliArgs } from './executors/internal/cli.js'; export { generateCodePushupConfig } from './generators/configuration/code-pushup-config.js'; export { configurationGenerator } from './generators/configuration/generator.js'; export type { ConfigurationGeneratorOptions } from './generators/configuration/schema.js'; diff --git a/packages/utils/src/lib/transform.unit.test.ts b/packages/utils/src/lib/transform.unit.test.ts index 3add11f0a..deadc162d 100644 --- a/packages/utils/src/lib/transform.unit.test.ts +++ b/packages/utils/src/lib/transform.unit.test.ts @@ -233,7 +233,7 @@ describe('objectToCliArgs', () => { }); it('should throw error for unsupported type', () => { - const params = { unsupported: undefined as any }; + const params = { unsupported: Symbol('test') as any }; expect(() => objectToCliArgs(params)).toThrow('Unsupported type'); }); }); From 0a5170f8a25e24f6ee29896cc24fee2f0cbcf0fd Mon Sep 17 00:00:00 2001 From: John Doe Date: Tue, 11 Nov 2025 08:43:31 +0100 Subject: [PATCH 36/38] refactor: wip --- packages/nx-plugin/src/executors/cli/executor.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/nx-plugin/src/executors/cli/executor.ts b/packages/nx-plugin/src/executors/cli/executor.ts index 4fe49a283..999f011a9 100644 --- a/packages/nx-plugin/src/executors/cli/executor.ts +++ b/packages/nx-plugin/src/executors/cli/executor.ts @@ -31,6 +31,7 @@ export default async function runAutorunExecutor( ]; const args = objectToCliArgs(argsObj); const envVariables = { + ...process.env, ...env, ...(verbose && { CP_VERBOSE: 'true' }), }; From 9a1558efb7feeba988cb639100a5c002b0608b45 Mon Sep 17 00:00:00 2001 From: John Doe Date: Tue, 11 Nov 2025 08:53:15 +0100 Subject: [PATCH 37/38] refactor: wip --- packages/nx-plugin/src/executors/cli/executor.ts | 2 +- packages/utils/src/lib/execute-process.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/nx-plugin/src/executors/cli/executor.ts b/packages/nx-plugin/src/executors/cli/executor.ts index 999f011a9..efee57a1c 100644 --- a/packages/nx-plugin/src/executors/cli/executor.ts +++ b/packages/nx-plugin/src/executors/cli/executor.ts @@ -49,7 +49,7 @@ export default async function runAutorunExecutor( await executeProcess({ command, args: [...positionals, ...args], - ...(envVariables && { env: envVariables }), + env: envVariables, ...(cwd ? { cwd } : {}), }); } catch (error) { diff --git a/packages/utils/src/lib/execute-process.ts b/packages/utils/src/lib/execute-process.ts index 7b54f7889..30346e715 100644 --- a/packages/utils/src/lib/execute-process.ts +++ b/packages/utils/src/lib/execute-process.ts @@ -170,11 +170,12 @@ export function executeProcess(cfg: ProcessConfig): Promise { bin, () => new Promise((resolve, reject) => { + const mergedEnv = env ? { ...process.env, ...env } : undefined; const spawnedProcess = spawn(command, args ?? [], { // shell:true tells Windows to use shell command for spawning a child process // https://stackoverflow.com/questions/60386867/node-spawn-child-process-not-working-in-windows shell: true, - ...(env && { env: env as Record }), + ...(mergedEnv && { env: mergedEnv as Record }), ...(cwd && { cwd: cwd as string }), ...options, }) as ChildProcessByStdio; From b8c46596bcd9e6dbae3855192edb94f2edccee58 Mon Sep 17 00:00:00 2001 From: John Doe Date: Tue, 11 Nov 2025 09:00:34 +0100 Subject: [PATCH 38/38] refactor: wip --- packages/nx-plugin/src/executors/cli/executor.ts | 6 ++++-- .../nx-plugin/src/plugin/target/configuration-target.ts | 6 +++--- .../src/plugin/target/configuration.target.unit.test.ts | 8 ++++---- packages/nx-plugin/src/plugin/target/targets.ts | 2 +- tools/src/debug/debug.plugin.ts | 2 +- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/nx-plugin/src/executors/cli/executor.ts b/packages/nx-plugin/src/executors/cli/executor.ts index efee57a1c..b4b208e63 100644 --- a/packages/nx-plugin/src/executors/cli/executor.ts +++ b/packages/nx-plugin/src/executors/cli/executor.ts @@ -12,8 +12,10 @@ export default async function runAutorunExecutor( terminalAndExecutorOptions: AutorunCommandExecutorOptions, { cwd }: ExecutorContext, ): Promise { - const { logger, stringifyError, formatCommand, objectToCliArgs } = - await import('@code-pushup/utils'); + const { logger, stringifyError, formatCommand } = await import( + '@code-pushup/utils' + ); + const { objectToCliArgs } = await import('@code-pushup/utils'); const { dryRun, diff --git a/packages/nx-plugin/src/plugin/target/configuration-target.ts b/packages/nx-plugin/src/plugin/target/configuration-target.ts index 986d3ea4e..b7af9a723 100644 --- a/packages/nx-plugin/src/plugin/target/configuration-target.ts +++ b/packages/nx-plugin/src/plugin/target/configuration-target.ts @@ -1,12 +1,12 @@ import type { TargetConfiguration } from '@nx/devkit'; import type { RunCommandsOptions } from 'nx/src/executors/run-commands/run-commands.impl'; -import { objectToCliArgs } from '../../executors/internal/cli.js'; import { PACKAGE_NAME } from '../../internal/constants.js'; -export function createConfigurationTarget(options?: { +export async function createConfigurationTarget(options?: { projectName?: string; -}): TargetConfiguration { +}): Promise> { const { projectName } = options ?? {}; + const { objectToCliArgs } = await import('@code-pushup/utils'); const args = objectToCliArgs({ ...(projectName ? { project: projectName } : {}), }); diff --git a/packages/nx-plugin/src/plugin/target/configuration.target.unit.test.ts b/packages/nx-plugin/src/plugin/target/configuration.target.unit.test.ts index f520cc813..e849d7853 100644 --- a/packages/nx-plugin/src/plugin/target/configuration.target.unit.test.ts +++ b/packages/nx-plugin/src/plugin/target/configuration.target.unit.test.ts @@ -3,16 +3,16 @@ import { PACKAGE_NAME } from '../../internal/constants.js'; import { createConfigurationTarget } from './configuration-target.js'; describe('createConfigurationTarget', () => { - it('should return code-pushup--configuration target for given project', () => { + it('should return code-pushup--configuration target for given project', async () => { expect( - createConfigurationTarget({ projectName: 'my-project' }), + await createConfigurationTarget({ projectName: 'my-project' }), ).toStrictEqual({ command: `nx g ${PACKAGE_NAME}:configuration --project="my-project"`, }); }); - it('should return code-pushup--configuration target without project name', () => { - expect(createConfigurationTarget()).toStrictEqual({ + it('should return code-pushup--configuration target without project name', async () => { + expect(await createConfigurationTarget()).toStrictEqual({ command: `nx g ${PACKAGE_NAME}:configuration`, }); }); diff --git a/packages/nx-plugin/src/plugin/target/targets.ts b/packages/nx-plugin/src/plugin/target/targets.ts index 90abaa047..8037c2e98 100644 --- a/packages/nx-plugin/src/plugin/target/targets.ts +++ b/packages/nx-plugin/src/plugin/target/targets.ts @@ -28,7 +28,7 @@ export async function createTargets(normalizedContext: CreateTargetsOptions) { } : // if NO code-pushup.config.*.(ts|js|mjs) is present return configuration target { - [`${targetName}--configuration`]: createConfigurationTarget({ + [`${targetName}--configuration`]: await createConfigurationTarget({ projectName: normalizedContext.projectJson.name, }), }; diff --git a/tools/src/debug/debug.plugin.ts b/tools/src/debug/debug.plugin.ts index f1a6f307b..f13f0300c 100644 --- a/tools/src/debug/debug.plugin.ts +++ b/tools/src/debug/debug.plugin.ts @@ -1,6 +1,6 @@ import type { CreateNodes, CreateNodesContext } from '@nx/devkit'; import { dirname } from 'node:path'; -import { objectToCliArgs } from '../../../packages/nx-plugin/src/executors/internal/cli.js'; +import { objectToCliArgs } from '@code-pushup/utils'; import { TOOLS_TSCONFIG_PATH } from '../constants.js'; import { KILL_PROCESS_BIN, LIST_PROCESS_BIN } from './constants.js';