diff --git a/e2e/nx-plugin-e2e/tests/__snapshots__/plugin-create-nodes.e2e.test.ts.snap b/e2e/nx-plugin-e2e/tests/__snapshots__/plugin-create-nodes.e2e.test.ts.snap index 158dc132d..d1599c84c 100644 --- a/e2e/nx-plugin-e2e/tests/__snapshots__/plugin-create-nodes.e2e.test.ts.snap +++ b/e2e/nx-plugin-e2e/tests/__snapshots__/plugin-create-nodes.e2e.test.ts.snap @@ -4,7 +4,7 @@ exports[`nx-plugin > should NOT add config targets dynamically if the project is { "code-pushup": { "configurations": {}, - "executor": "@code-pushup/nx-plugin:autorun", + "executor": "@code-pushup/nx-plugin:cli", "options": {}, }, } @@ -26,7 +26,7 @@ exports[`nx-plugin > should add executor target dynamically if the project is co { "code-pushup": { "configurations": {}, - "executor": "@code-pushup/nx-plugin:autorun", + "executor": "@code-pushup/nx-plugin:cli", "options": {}, }, } diff --git a/e2e/nx-plugin-e2e/tests/executor-cli.e2e.test.ts b/e2e/nx-plugin-e2e/tests/executor-cli.e2e.test.ts index 7f17f6f67..4f5ae3fdd 100644 --- a/e2e/nx-plugin-e2e/tests/executor-cli.e2e.test.ts +++ b/e2e/nx-plugin-e2e/tests/executor-cli.e2e.test.ts @@ -8,8 +8,8 @@ import { materializeTree, } from '@code-pushup/test-nx-utils'; import { teardownTestFolder } from '@code-pushup/test-setup'; -import { removeColorCodes } from '@code-pushup/test-utils'; -import { executeProcess } from '@code-pushup/utils'; +import { osAgnosticPath, removeColorCodes } from '@code-pushup/test-utils'; +import { executeProcess, readJsonFile } from '@code-pushup/utils'; function relativePathToCwd(testDir: string): string { return relative(join(process.cwd(), testDir), process.cwd()); @@ -27,7 +27,7 @@ async function addTargetToWorkspace( targets: { ...projectCfg.targets, ['code-pushup']: { - executor: '@code-pushup/nx-plugin:autorun', + executor: '@code-pushup/nx-plugin:cli', }, }, }); @@ -37,19 +37,27 @@ async function addTargetToWorkspace( plugins: [ { // @TODO replace with inline plugin - fileImports: `import {customPlugin} from "${join( - relativePathToCwd(cwd), - pathRelativeToPackage, - 'dist/testing/test-utils', + fileImports: `import {customPlugin} from "${osAgnosticPath( + join( + relativePathToCwd(cwd), + pathRelativeToPackage, + 'dist/testing/test-utils', + ), )}";`, codeStrings: 'customPlugin()', }, ], + upload: { + server: 'https://dummy-server.dev', + organization: 'dummy-organization', + apiKey: 'dummy-api-key', + project: 'dummy-project', + }, }); await materializeTree(tree, cwd); } -describe('executor autorun', () => { +describe('executor command', () => { let tree: Tree; const project = 'my-lib'; const baseDir = 'tmp/e2e/nx-plugin-e2e/__test__/executor/cli'; @@ -62,10 +70,9 @@ describe('executor autorun', () => { await teardownTestFolder(baseDir); }); - it('should execute autorun executor', async () => { - const cwd = join(baseDir, 'execute-dynamic-executor'); + it('should execute no specific command by default', async () => { + const cwd = join(baseDir, 'execute-default-command'); await addTargetToWorkspace(tree, { cwd, project }); - const { stdout, code } = await executeProcess({ command: 'npx', args: ['nx', 'run', `${project}:code-pushup`, '--dryRun'], @@ -74,6 +81,72 @@ describe('executor autorun', () => { expect(code).toBe(0); const cleanStdout = removeColorCodes(stdout); - expect(cleanStdout).toContain('nx run my-lib:code-pushup --dryRun'); + expect(cleanStdout).toContain('nx run my-lib:code-pushup'); + }); + + it('should execute print-config executor', async () => { + const cwd = join(baseDir, 'execute-print-config-command'); + await addTargetToWorkspace(tree, { cwd, project }); + + const { stdout, code } = await executeProcess({ + command: 'npx', + args: ['nx', 'run', `${project}:code-pushup`, 'print-config'], + cwd, + }); + + expect(code).toBe(0); + const cleanStdout = removeColorCodes(stdout); + expect(cleanStdout).toContain('nx run my-lib:code-pushup print-config'); + + await expect(() => + readJsonFile(join(cwd, '.code-pushup', project, 'report.json')), + ).rejects.toThrow(''); + }); + + it('should execute collect executor and add report to sub folder named by project', async () => { + const cwd = join(baseDir, 'execute-collect-command'); + await addTargetToWorkspace(tree, { cwd, project }); + + const { stdout, code } = await executeProcess({ + command: 'nx', + args: ['run', `${project}:code-pushup`, 'collect'], + cwd, + }); + + expect(code).toBe(0); + const cleanStdout = removeColorCodes(stdout); + expect(cleanStdout).toContain('nx run my-lib:code-pushup collect'); + + const report = await readJsonFile( + join(cwd, '.code-pushup', project, 'report.json'), + ); + expect(report).toStrictEqual( + expect.objectContaining({ + plugins: [ + expect.objectContaining({ + slug: 'good-feels', + audits: [ + expect.objectContaining({ + displayValue: 'βœ… Perfect! πŸ‘Œ', + slug: 'always-perfect', + }), + ], + }), + ], + }), + ); + }); + + it('should execute upload executor to throw if no report is present', async () => { + const cwd = join(baseDir, 'execute-upload-command'); + await addTargetToWorkspace(tree, { cwd, project }); + + await expect( + executeProcess({ + command: 'npx', + args: ['nx', 'run', `${project}:code-pushup`, 'upload'], + cwd, + }), + ).rejects.toThrow(/report.json/); }); }); diff --git a/e2e/nx-plugin-e2e/tests/generator-configuration.e2e.test.ts b/e2e/nx-plugin-e2e/tests/generator-configuration.e2e.test.ts index bbb5dd023..bff0ec4b2 100644 --- a/e2e/nx-plugin-e2e/tests/generator-configuration.e2e.test.ts +++ b/e2e/nx-plugin-e2e/tests/generator-configuration.e2e.test.ts @@ -65,7 +65,7 @@ describe('nx-plugin g configuration', () => { expect.objectContaining({ targets: expect.objectContaining({ 'code-pushup': { - executor: '@code-pushup/nx-plugin:autorun', + executor: '@code-pushup/nx-plugin:cli', }, }), }), @@ -108,7 +108,7 @@ describe('nx-plugin g configuration', () => { expect.objectContaining({ targets: expect.objectContaining({ 'code-pushup': { - executor: '@code-pushup/nx-plugin:autorun', + executor: '@code-pushup/nx-plugin:cli', }, }), }), @@ -149,7 +149,7 @@ describe('nx-plugin g configuration', () => { expect.objectContaining({ targets: expect.objectContaining({ 'code-pushup': { - executor: '@code-pushup/nx-plugin:autorun', + executor: '@code-pushup/nx-plugin:cli', }, }), }), @@ -193,7 +193,7 @@ describe('nx-plugin g configuration', () => { expect.objectContaining({ targets: expect.not.objectContaining({ 'code-pushup': { - executor: '@code-pushup/nx-plugin:autorun', + executor: '@code-pushup/nx-plugin:cli', }, }), }), 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 ef19f4b7a..2616ec1da 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 @@ -146,7 +146,7 @@ describe('nx-plugin', () => { expect(projectJson.targets).toStrictEqual({ ['code-pushup']: { configurations: {}, - executor: `@code-pushup/nx-plugin:autorun`, + executor: `@code-pushup/nx-plugin:cli`, options: {}, }, }); @@ -174,16 +174,25 @@ describe('nx-plugin', () => { codeStrings: 'customPlugin()', }, ], + upload: { + server: 'http://staging.code-pushup.dev', + organization: 'code-pushup', + apiKey: '12345678', + }, }); await materializeTree(tree, cwd); - const { stdout } = await executeProcess({ + const { stdout, stderr } = await executeProcess({ command: 'npx', args: ['nx', 'run', `${project}:code-pushup`, '--dryRun'], cwd, }); + const cleanStderr = removeColorCodes(stderr); + // @TODO create test environment for working plugin. This here misses package-lock.json to execute correctly + expect(cleanStderr).toContain('DryRun execution of: npx @code-pushup/cli'); + const cleanStdout = removeColorCodes(stdout); expect(cleanStdout).toContain( 'NX Successfully ran target code-pushup for project my-lib', @@ -208,7 +217,7 @@ describe('nx-plugin', () => { expect(projectJson.targets).toStrictEqual({ ['code-pushup']: expect.objectContaining({ - executor: 'XYZ:autorun', + executor: 'XYZ:cli', }), }); }); @@ -231,7 +240,7 @@ describe('nx-plugin', () => { expect(projectJson.targets).toStrictEqual({ ['code-pushup']: expect.objectContaining({ - executor: `@code-pushup/nx-plugin:autorun`, + executor: `@code-pushup/nx-plugin:cli`, options: { projectPrefix: 'cli', }, diff --git a/e2e/nx-plugin-e2e/vite.config.e2e.ts b/e2e/nx-plugin-e2e/vite.config.e2e.ts index 778d67ef9..9184cd3b7 100644 --- a/e2e/nx-plugin-e2e/vite.config.e2e.ts +++ b/e2e/nx-plugin-e2e/vite.config.e2e.ts @@ -6,7 +6,7 @@ export default defineConfig({ cacheDir: '../../node_modules/.vite/nx-plugin-e2e', test: { reporters: ['basic'], - testTimeout: 60_000, + testTimeout: 160_000, globals: true, alias: tsconfigPathAliases(), pool: 'threads', diff --git a/global-setup.e2e.ts b/global-setup.e2e.ts index 73d33148a..2ae31fc30 100644 --- a/global-setup.e2e.ts +++ b/global-setup.e2e.ts @@ -1,3 +1,4 @@ +import { rm, writeFile } from 'node:fs/promises'; import { join } from 'node:path'; import { setup as globalSetup } from './global-setup'; import { setupTestFolder, teardownTestFolder } from './testing/test-setup/src'; @@ -35,6 +36,7 @@ export async function setup() { // package publish const { registry } = activeRegistry.registryData; + await writeFile('.npmrc', `@code-pushup:registry=${registry}`); try { console.info('Publish packages'); nxRunManyPublish({ @@ -64,5 +66,6 @@ export async function teardown() { stopLocalRegistry(stop); nxRunManyNpmUninstall({ parallel: 1 }); } + await rm('.npmrc'); await teardownTestFolder(e2eDir); } diff --git a/global-setup.verdaccio.ts b/global-setup.verdaccio.ts index 10f3d7bf7..e1aa9f958 100644 --- a/global-setup.verdaccio.ts +++ b/global-setup.verdaccio.ts @@ -38,6 +38,6 @@ export async function setup() { export async function teardown() { // NOTICE - Time saving optimization // We skip uninstalling packages as the folder is deleted anyway - // comment out to see the folder and web interface - // await nxStopVerdaccioAndTeardownEnv(activeRegistry); + + await nxStopVerdaccioAndTeardownEnv(activeRegistry); } diff --git a/packages/nx-plugin/README.md b/packages/nx-plugin/README.md index 3999f5d8a..89e8c306e 100644 --- a/packages/nx-plugin/README.md +++ b/packages/nx-plugin/README.md @@ -1,11 +1,30 @@ # @code-pushup/nx-plugin +### Plugin + +Register this plugin in your `nx.json` to leverage a set of generators and executors to integrate Code PushUp into a Nx workspace. + +#### Registration + +```jsonc +// nx.json +{ + //... + "plugins": ["@code-pushup/nx-plugin"] +} +``` + +Resulting targets: + +- `nx run :code-pushup--configuration` (no config file present) +- `nx run :code-pushup` (`code-pushup.config.{ts,mjs,js}` is present) + ### Generators #### Init Install JS packages and register plugin. -See [init docs](./src/generators/init/README.md) for details +See [init generator docs](./src/generators/init/README.md) for details Examples: @@ -15,9 +34,35 @@ Examples: #### Configuration Adds a `code-pushup` target to your `project.json`. -See [configuration docs](./src/generators/configuration/README.md) for details +See [configuration generator docs](./src/generators/configuration/README.md) for details Examples: - `nx g @code-pushup/nx-plugin:configuration --project=` - `nx g @code-pushup/nx-plugin:configuration --project= --targetName=cp` + +### Executor + +#### CLI + +Install JS packages configure a target in your project json. +See [CLI executor docs](./src/executor/cli/README.md) for details + +Examples: + +```json +{ + "name": "my-project", + "targets": { + "code-pushup": { + "executor": "@code-pushup/nx-plugin:cli", + "options": { + "projectPrefix": "workspace-name" + } + } + } +} +``` + +- `nx run :code-pushup` +- `nx run :code-pushup print-config --persist.filename=custom-report` diff --git a/packages/nx-plugin/docs/images/nx-plugin-dynamic-target-configuration.png b/packages/nx-plugin/docs/images/nx-plugin-dynamic-target-configuration.png new file mode 100644 index 000000000..6184f2e39 Binary files /dev/null and b/packages/nx-plugin/docs/images/nx-plugin-dynamic-target-configuration.png differ diff --git a/packages/nx-plugin/executors.json b/packages/nx-plugin/executors.json index 8a28b5c7c..7ec8bbcdc 100644 --- a/packages/nx-plugin/executors.json +++ b/packages/nx-plugin/executors.json @@ -1,9 +1,9 @@ { "executors": { - "autorun": { - "implementation": "./src/executors/autorun/executor", - "schema": "./src/executors/autorun/schema.json", - "description": "CodePushup CLI autorun command executor. Executes the @code-pushup/cli autorun command See: https://github.com/code-pushup/cli/blob/main/packages/cli/README.md#autorun-command" + "cli": { + "implementation": "./src/executors/cli/executor", + "schema": "./src/executors/cli/schema.json", + "description": "CodePushup CLI executor. Executes the @code-pushup/cli command's See: https://github.com/code-pushup/cli/blob/main/packages/cli/README.md#commands" } } } diff --git a/packages/nx-plugin/package.json b/packages/nx-plugin/package.json index 7e6da5246..d6fef75c1 100644 --- a/packages/nx-plugin/package.json +++ b/packages/nx-plugin/package.json @@ -17,7 +17,7 @@ }, "dependencies": { "@nx/devkit": "^17.1.3", - "tslib": "^2.6.2", + "tslib": "2.6.3", "nx": "^17.1.3", "@code-pushup/models": "0.51.0", "zod": "^3.22.4", diff --git a/packages/nx-plugin/src/executors/autorun/constants.ts b/packages/nx-plugin/src/executors/autorun/constants.ts deleted file mode 100644 index 14793f63e..000000000 --- a/packages/nx-plugin/src/executors/autorun/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const AUTORUN_COMMAND = 'autorun'; diff --git a/packages/nx-plugin/src/executors/autorun/README.md b/packages/nx-plugin/src/executors/cli/README.md similarity index 69% rename from packages/nx-plugin/src/executors/autorun/README.md rename to packages/nx-plugin/src/executors/cli/README.md index 05084615d..c5e5e8110 100644 --- a/packages/nx-plugin/src/executors/autorun/README.md +++ b/packages/nx-plugin/src/executors/cli/README.md @@ -1,25 +1,41 @@ -# Autorun Executor +# Command Executor -This executor is used to run the code-pushup CLI autorun command in a Nx workspace. -For details on the CLI command read the [CLI autorun documentation](https://github.com/code-pushup/cli/blob/main/packages/cli/README.md#autorun-command). +This executor is used to run the Code PushUp CLI in an Nx workspace. +For details on the CLI command read the [CLI commands documentation](https://github.com/code-pushup/cli/blob/main/packages/cli/README.md#commands). -#### @code-pushup/nx-plugin:autorun +#### @code-pushup/nx-plugin:cli ## Usage -// project.json +Configure a target in your project json. -```json +```jsonc +// /project.json { "name": "my-project", "targets": { "code-pushup": { - "executor": "@code-pushup/nx-plugin:autorun" + "executor": "@code-pushup/nx-plugin:cli" } } } ``` +Run +`nx run :code-pushup` + +```text +Root/ +β”œβ”€β”€ .code-pushup/ +β”‚ β”œβ”€β”€ report.json πŸ‘ˆ generated +β”‚ └── report.md πŸ‘ˆ generated +β”œβ”€β”€ project-name/ +β”‚ β”œβ”€β”€ project.json +β”‚ β”œβ”€β”€ code-pushup.config.ts πŸ‘ˆ executed +β”‚ └── ... +└── ... +``` + By default, the Nx plugin will derive the options from the executor config. The following things happen: diff --git a/packages/nx-plugin/src/executors/autorun/executor.integration.test.ts b/packages/nx-plugin/src/executors/cli/executor.integration.test.ts similarity index 100% rename from packages/nx-plugin/src/executors/autorun/executor.integration.test.ts rename to packages/nx-plugin/src/executors/cli/executor.integration.test.ts diff --git a/packages/nx-plugin/src/executors/autorun/executor.ts b/packages/nx-plugin/src/executors/cli/executor.ts similarity index 66% rename from packages/nx-plugin/src/executors/autorun/executor.ts rename to packages/nx-plugin/src/executors/cli/executor.ts index 2bf378436..223d6baa4 100644 --- a/packages/nx-plugin/src/executors/autorun/executor.ts +++ b/packages/nx-plugin/src/executors/cli/executor.ts @@ -3,7 +3,6 @@ import { type ExecutorContext, logger } from '@nx/devkit'; import { execSync } from 'node:child_process'; import { createCliCommand } from '../internal/cli'; import { normalizeContext } from '../internal/context'; -import { AUTORUN_COMMAND } from './constants'; import type { AutorunCommandExecutorOptions } from './schema'; import { parseAutorunExecutorOptions } from './utils'; @@ -16,38 +15,39 @@ export type ExecutorOutput = { export default function runAutorunExecutor( terminalAndExecutorOptions: AutorunCommandExecutorOptions, context: ExecutorContext, -) { +): Promise { const normalizedContext = normalizeContext(context); const cliArgumentObject = parseAutorunExecutorOptions( terminalAndExecutorOptions, normalizedContext, ); - const { dryRun, verbose } = terminalAndExecutorOptions; - const command = createCliCommand(AUTORUN_COMMAND, cliArgumentObject); - const commandOptions = context.cwd ? { cwd: context.cwd } : {}; + const { dryRun, verbose, command } = terminalAndExecutorOptions; + + const commandString = createCliCommand({ command, args: cliArgumentObject }); + const commandStringOptions = context.cwd ? { cwd: context.cwd } : {}; if (verbose) { - logger.info(`Run ${AUTORUN_COMMAND} executor`); - logger.info(`Command: ${command}`); + logger.info(`Run CLI executor ${command ?? ''}`); + logger.info(`Command: ${commandString}`); } if (dryRun) { - logger.warn(`DryRun execution of: ${command}`); + logger.warn(`DryRun execution of: ${commandString}`); } else { try { // @TODO use executeProcess instead of execSync -> non blocking, logs #761 // eslint-disable-next-line n/no-sync - execSync(command, commandOptions); + execSync(commandString, commandStringOptions); } catch (error) { logger.error(error); return Promise.resolve({ success: false, - command, - error, + command: commandString, + error: error as Error, }); } } return Promise.resolve({ success: true, - command, - } satisfies ExecutorOutput); + command: commandString, + }); } diff --git a/packages/nx-plugin/src/executors/autorun/executor.unit.test.ts b/packages/nx-plugin/src/executors/cli/executor.unit.test.ts similarity index 89% rename from packages/nx-plugin/src/executors/autorun/executor.unit.test.ts rename to packages/nx-plugin/src/executors/cli/executor.unit.test.ts index 56b21a4a4..f19386933 100644 --- a/packages/nx-plugin/src/executors/autorun/executor.unit.test.ts +++ b/packages/nx-plugin/src/executors/cli/executor.unit.test.ts @@ -33,13 +33,13 @@ describe('runAutorunExecutor', () => { envSpy.mockReset().mockReturnValue({}); }); - it('should call execSync with autorun command and return result', async () => { + it('should call execSync with return result', async () => { const output = await runAutorunExecutor({}, executorContext('utils')); expect(output.success).toBe(true); - expect(output.command).toMatch('npx @code-pushup/cli autorun'); + expect(output.command).toMatch('npx @code-pushup/cli'); // eslint-disable-next-line n/no-sync expect(execSync).toHaveBeenCalledWith( - expect.stringContaining('npx @code-pushup/cli autorun'), + expect.stringContaining('npx @code-pushup/cli'), { cwd: '/test' }, ); }); @@ -94,10 +94,10 @@ describe('runAutorunExecutor', () => { expect(loggerWarnSpy).toHaveBeenCalledTimes(0); expect(loggerInfoSpy).toHaveBeenCalledTimes(2); expect(loggerInfoSpy).toHaveBeenCalledWith( - expect.stringContaining('Run autorun executor'), + expect.stringContaining(`Run CLI executor`), ); expect(loggerInfoSpy).toHaveBeenCalledWith( - expect.stringContaining('Command: npx @code-pushup/cli autorun'), + expect.stringContaining('Command: npx @code-pushup/cli'), ); }); @@ -108,7 +108,7 @@ describe('runAutorunExecutor', () => { expect(loggerWarnSpy).toHaveBeenCalledTimes(1); expect(loggerWarnSpy).toHaveBeenCalledWith( expect.stringContaining( - 'DryRun execution of: npx @code-pushup/cli autorun --dryRun', + 'DryRun execution of: npx @code-pushup/cli --dryRun', ), ); }); diff --git a/packages/nx-plugin/src/executors/autorun/schema.json b/packages/nx-plugin/src/executors/cli/schema.json similarity index 94% rename from packages/nx-plugin/src/executors/autorun/schema.json rename to packages/nx-plugin/src/executors/cli/schema.json index 288c2591e..e494c4cc0 100644 --- a/packages/nx-plugin/src/executors/autorun/schema.json +++ b/packages/nx-plugin/src/executors/cli/schema.json @@ -5,11 +5,9 @@ "description": "Executes the @code-pushup/cli autorun command See: https://github.com/code-pushup/cli/blob/main/packages/cli/README.md#autorun-command", "type": "object", "properties": { - "project": { + "command": { "type": "string", - "description": "The name of the project.", - "x-prompt": "Which project should configure Code Pushup?", - "x-dropdown": "projects", + "description": "The command to run.", "$default": { "$source": "argv", "index": 0 diff --git a/packages/nx-plugin/src/executors/autorun/schema.ts b/packages/nx-plugin/src/executors/cli/schema.ts similarity index 100% rename from packages/nx-plugin/src/executors/autorun/schema.ts rename to packages/nx-plugin/src/executors/cli/schema.ts diff --git a/packages/nx-plugin/src/executors/autorun/utils.integration.test.ts b/packages/nx-plugin/src/executors/cli/utils.integration.test.ts similarity index 100% rename from packages/nx-plugin/src/executors/autorun/utils.integration.test.ts rename to packages/nx-plugin/src/executors/cli/utils.integration.test.ts diff --git a/packages/nx-plugin/src/executors/autorun/utils.ts b/packages/nx-plugin/src/executors/cli/utils.ts similarity index 100% rename from packages/nx-plugin/src/executors/autorun/utils.ts rename to packages/nx-plugin/src/executors/cli/utils.ts diff --git a/packages/nx-plugin/src/executors/autorun/utils.unit.test.ts b/packages/nx-plugin/src/executors/cli/utils.unit.test.ts similarity index 100% rename from packages/nx-plugin/src/executors/autorun/utils.unit.test.ts rename to packages/nx-plugin/src/executors/cli/utils.unit.test.ts diff --git a/packages/nx-plugin/src/executors/internal/cli.ts b/packages/nx-plugin/src/executors/internal/cli.ts index 1783a052b..82e3153d5 100644 --- a/packages/nx-plugin/src/executors/internal/cli.ts +++ b/packages/nx-plugin/src/executors/internal/cli.ts @@ -1,12 +1,12 @@ -export function createCliCommand( - command: string, - args: Record, - options?: { - bin: string; - }, -): string { - const { bin = '@code-pushup/cli' } = options ?? {}; - return `npx ${bin} ${command} ${objectToCliArgs(args).join(' ')}`; +export function createCliCommand(options?: { + args?: Record; + command?: string; + bin?: string; +}): string { + const { bin = '@code-pushup/cli', command, args } = options ?? {}; + return `npx ${bin} ${objectToCliArgs({ _: command ?? [], ...args }).join( + ' ', + )}`; } type ArgumentValue = number | string | boolean | string[]; @@ -29,7 +29,9 @@ export function objectToCliArgs< // process/file/script if (key === '_') { // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return Array.isArray(value) ? value : [`${value}`]; + return (Array.isArray(value) ? value : [`${value}`]).filter( + v => v != null, + ); } const prefix = key.length === 1 ? '-' : '--'; 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 41892097b..ab39b876b 100644 --- a/packages/nx-plugin/src/executors/internal/cli.unit.test.ts +++ b/packages/nx-plugin/src/executors/internal/cli.unit.test.ts @@ -87,8 +87,13 @@ describe('objectToCliArgs', () => { }); describe('createCliCommand', () => { - it('should create command out of command name and an object for arguments', () => { - const result = createCliCommand('autorun', { verbose: true }); + it('should create command out of object for arguments', () => { + const result = createCliCommand({ args: { verbose: true } }); + expect(result).toBe('npx @code-pushup/cli --verbose'); + }); + + it('should create command out of object for arguments with positional', () => { + const result = createCliCommand({ args: { _: 'autorun', verbose: true } }); expect(result).toBe('npx @code-pushup/cli autorun --verbose'); }); }); diff --git a/packages/nx-plugin/src/executors/internal/config.ts b/packages/nx-plugin/src/executors/internal/config.ts index 1e0295aff..f8437f613 100644 --- a/packages/nx-plugin/src/executors/internal/config.ts +++ b/packages/nx-plugin/src/executors/internal/config.ts @@ -56,7 +56,7 @@ export function uploadConfig( return { ...(projectName ? { - project: applyPrefix ? `${prefix}${projectName}` : projectName, // provide correct project + project: applyPrefix ? `${prefix}${projectName}` : projectName, } : {}), ...parseEnv(process.env), diff --git a/packages/nx-plugin/src/executors/internal/types.ts b/packages/nx-plugin/src/executors/internal/types.ts index 8e6c114cc..58dc91ec8 100644 --- a/packages/nx-plugin/src/executors/internal/types.ts +++ b/packages/nx-plugin/src/executors/internal/types.ts @@ -20,6 +20,14 @@ export type ProjectExecutorOnlyOptions = { * CLI types that apply globally for all commands. */ export type GlobalExecutorOptions = { + command?: + | 'collect' + | 'upload' + | 'autorun' + | 'print-config' + | 'compare' + | 'merge-diffs' + | 'history'; bin?: string; verbose?: boolean; progress?: boolean; diff --git a/packages/nx-plugin/src/generators/configuration/README.md b/packages/nx-plugin/src/generators/configuration/README.md index 12615bdda..79ab3ec39 100644 --- a/packages/nx-plugin/src/generators/configuration/README.md +++ b/packages/nx-plugin/src/generators/configuration/README.md @@ -4,13 +4,22 @@ ## Usage -`nx generate configuration ...` +`nx generate @code-pushup/nx-plugin:configuration` By default, the Nx plugin will search for existing configuration files. If they are not present it creates a `code-pushup.config.ts` and adds a target to your `project.json` file. -You can specify the collection explicitly as follows: +You can specify the project explicitly as follows: -`nx g @code-pushup/nx-plugin:configuration ...` +`nx g @code-pushup/nx-plugin:configuration ` + +```text +Root/ +β”œβ”€β”€ project-name/ +β”‚ β”œβ”€β”€ project.json πŸ‘ˆ updated +β”‚ β”œβ”€β”€ code-pushup.config.ts πŸ‘ˆ generated +β”‚ └── ... +└── ... +``` Show what will be generated without writing to disk: diff --git a/packages/nx-plugin/src/generators/configuration/__snapshots__/root-code-pushup.config.ts b/packages/nx-plugin/src/generators/configuration/__snapshots__/root-code-pushup.config.ts index 5056d9071..eae33ec78 100644 --- a/packages/nx-plugin/src/generators/configuration/__snapshots__/root-code-pushup.config.ts +++ b/packages/nx-plugin/src/generators/configuration/__snapshots__/root-code-pushup.config.ts @@ -7,7 +7,7 @@ export default { persist: { filename: 'report-123', }, - update: { + upload: { apiKey: '123', }, plugins: [myPlugin({ timeout: 42 })], diff --git a/packages/nx-plugin/src/generators/configuration/files/code-pushup.config.ts.template b/packages/nx-plugin/src/generators/configuration/files/code-pushup.config.ts.template index 42da5a08d..b354d3764 100644 --- a/packages/nx-plugin/src/generators/configuration/files/code-pushup.config.ts.template +++ b/packages/nx-plugin/src/generators/configuration/files/code-pushup.config.ts.template @@ -3,7 +3,7 @@ // see: https://github.com/code-pushup/cli/blob/main/packages/models/docs/models-reference.md#coreconfig export default { <% if (persist) { %>persist: <%- persist %>,<% } %> - <% if (upload) { %>update: <%- upload %>,<% } %> + <% if (upload) { %>upload: <%- upload %>,<% } %> <% if (plugins) { %>plugins: <%- plugins %>,<% } %> <% if (categories) { %>categories: <%- categories %><% } %> } satisfies CoreConfig; diff --git a/packages/nx-plugin/src/generators/configuration/generator.integration.test.ts b/packages/nx-plugin/src/generators/configuration/generator.integration.test.ts index 32fdced3f..1191c8993 100644 --- a/packages/nx-plugin/src/generators/configuration/generator.integration.test.ts +++ b/packages/nx-plugin/src/generators/configuration/generator.integration.test.ts @@ -45,7 +45,7 @@ describe('addTargetToProject', () => { ); expect(projectConfiguration.targets?.[DEFAULT_TARGET_NAME]).toEqual({ - executor: `${PACKAGE_NAME}:autorun`, + executor: `${PACKAGE_NAME}:cli`, }); }); @@ -70,7 +70,7 @@ describe('addTargetToProject', () => { ); expect(projectConfiguration.targets?.['cp']).toEqual({ - executor: `${PACKAGE_NAME}:autorun`, + executor: `${PACKAGE_NAME}:cli`, }); }); }); @@ -102,7 +102,7 @@ describe('configurationGenerator', () => { ); expect(projectConfiguration.targets?.[DEFAULT_TARGET_NAME]).toEqual({ - executor: `${PACKAGE_NAME}:autorun`, + executor: `${PACKAGE_NAME}:cli`, }); }); diff --git a/packages/nx-plugin/src/generators/configuration/generator.ts b/packages/nx-plugin/src/generators/configuration/generator.ts index 676d3b58c..dc0d49913 100644 --- a/packages/nx-plugin/src/generators/configuration/generator.ts +++ b/packages/nx-plugin/src/generators/configuration/generator.ts @@ -46,7 +46,7 @@ export function addTargetToProject( const { targetName, project } = options; const codePushupTargetConfig = { - executor: `${PACKAGE_NAME}:autorun`, + executor: `${PACKAGE_NAME}:cli`, }; updateProjectConfiguration(tree, project, { diff --git a/packages/nx-plugin/src/generators/init/README.md b/packages/nx-plugin/src/generators/init/README.md index c367aab7c..c4bfa4573 100644 --- a/packages/nx-plugin/src/generators/init/README.md +++ b/packages/nx-plugin/src/generators/init/README.md @@ -4,7 +4,7 @@ ## Usage -`nx generate configuration ...` +`nx generate @code-pushup/nx-plugin:init` By default, the Nx plugin will update your `package.json` with needed dependencies and register the plugin in your `nx.json` configuration. @@ -12,6 +12,14 @@ You can specify the collection explicitly as follows: `nx g @code-pushup/nx-plugin:init` +```text +Root/ +β”œβ”€β”€ ... +β”œβ”€β”€ nx.json πŸ‘ˆ updated +β”œβ”€β”€ package.json πŸ‘ˆ updated +└── ... +``` + Show what will be generated without writing to disk: `nx g @code-pushup/nx-plugin:init --dry-run` diff --git a/packages/nx-plugin/src/plugin/README.md b/packages/nx-plugin/src/plugin/README.md new file mode 100644 index 000000000..89610da1f --- /dev/null +++ b/packages/nx-plugin/src/plugin/README.md @@ -0,0 +1,107 @@ +# @code-pushup/nx-plugin + +The Nx Plugin for [Code PushUp](https://github.com/code-pushup/cli#readme), an open source code quality and conformance tool. + +Why should you use this plugin? + +- Zero setup cost. Just run the `init` generator and you're good to go. +- Smoother CI integration +- Minimal configuration +- Automated setup, migration and maintenance + +## Usage + +```jsonc +// nx.json +{ + //... + "plugins": ["@code-pushup/nx-plugin"] +} +``` + +or with options: + +```jsonc +// nx.json +{ + //... + "plugins": [ + { + "plugin": "@code-pushup/nx-plugin", + "options": { + "projectPrefix": "cli" + } + } + ] +} +``` + +Now every project will have `code-pushup--configuration` target if no `code-pushup.{ts,mjs,js}` is present. + +- `nx run :code-pushup--configuration` +- `nx run :code-pushup--configuration --skipFormat` + +Run it and the project will get automatically configured. + +```text +Root/ +β”œβ”€β”€ project-name/ +β”‚ β”œβ”€β”€ code-pushup.config.ts πŸ‘ˆ generated +β”‚ └── ... +└── ... +``` + +For details visit the [configuration generator docs](../generators/configuration/README.md). + +With the configuration from above a `code-pushup` target is now present. + +- `nx run :code-pushup` + +Run it and the project will get automatically collect the report. + +```text +Root/ +β”œβ”€β”€ .code-pushup/ +β”‚ └── project-name +β”‚ β”œβ”€β”€ report.md πŸ‘ˆ generated +β”‚ └── report.json πŸ‘ˆ generated +β”œβ”€β”€ project-name/ +β”‚ β”œβ”€β”€ code-pushup.config.ts +β”‚ └── ... +└── ... +``` + +Pass positional arguments to execute a specific command, use named arguments to overwrite defaults. + +- `nx run :code-pushup --onlyPlugins=eslint` +- `nx run :code-pushup collect` +- `nx run :code-pushup upload --upload.server=https://staging.code-pushup.dev` + +For a full list of command visit the [Code PushUp CLI documentation](../../../cli/README.md#commands). + +## Options + +| Name | type | description | +| ----------------- | -------------------------------- | ------------------------------------------------------ | +| **projectPrefix** | `string` | prefix for upload.project on non root projects | +| **targetName** | `string` (DEFAULT 'code-pushup') | The id used to identify a target in your project.json. | +| **bin** | `string` | Path to Code PushUp CLI | + +All options are optional and provided in the `nx.json` file. + +```jsonc +// nx.json +{ + //... + "plugins": [ + { + "plugin": "@code-pushup/nx-plugin", + "options": { + "projectPrefix": "cli" + "targetName": "cp" + "bin": "dist/package/code-pushup-custom-build" + } + } + ] +} +``` diff --git a/packages/nx-plugin/src/plugin/plugin.unit.test.ts b/packages/nx-plugin/src/plugin/plugin.unit.test.ts index 05622d102..aa218e617 100644 --- a/packages/nx-plugin/src/plugin/plugin.unit.test.ts +++ b/packages/nx-plugin/src/plugin/plugin.unit.test.ts @@ -94,7 +94,7 @@ describe('@code-pushup/nx-plugin/plugin', () => { [projectRoot]: { targets: { [CP_TARGET_NAME]: { - executor: `${PACKAGE_NAME}:autorun`, + executor: `${PACKAGE_NAME}:cli`, options: { projectPrefix: 'cli', }, @@ -126,7 +126,7 @@ describe('@code-pushup/nx-plugin/plugin', () => { [projectRoot]: { targets: { [CP_TARGET_NAME]: { - executor: `${PACKAGE_NAME}:autorun`, + executor: `${PACKAGE_NAME}:cli`, options: { projectPrefix: 'cli', }, diff --git a/packages/nx-plugin/src/plugin/target/executor-target.ts b/packages/nx-plugin/src/plugin/target/executor-target.ts index db5edd8cb..aeba82ad8 100644 --- a/packages/nx-plugin/src/plugin/target/executor-target.ts +++ b/packages/nx-plugin/src/plugin/target/executor-target.ts @@ -8,7 +8,7 @@ export function createExecutorTarget(options?: { }): TargetConfiguration { const { bin = PACKAGE_NAME, projectPrefix } = options ?? {}; return { - executor: `${bin}:autorun`, + executor: `${bin}:cli`, ...(projectPrefix ? { options: { 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 198027b9f..8ea0799a7 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 @@ -4,19 +4,19 @@ import { createExecutorTarget } from './executor-target'; describe('createExecutorTarget', () => { it('should return executor target without project name', () => { expect(createExecutorTarget()).toStrictEqual({ - executor: '@code-pushup/nx-plugin:autorun', + executor: '@code-pushup/nx-plugin:cli', }); }); it('should use bin if provides', () => { expect(createExecutorTarget({ bin: 'xyz' })).toStrictEqual({ - executor: 'xyz:autorun', + executor: 'xyz:cli', }); }); it('should use projectPrefix if provided', () => { expect(createExecutorTarget({ projectPrefix: 'cli' })).toStrictEqual({ - executor: '@code-pushup/nx-plugin:autorun', + executor: '@code-pushup/nx-plugin:cli', options: { projectPrefix: 'cli', }, diff --git a/packages/nx-plugin/src/plugin/target/targets.unit.test.ts b/packages/nx-plugin/src/plugin/target/targets.unit.test.ts index 2332c5af3..0de7288d2 100644 --- a/packages/nx-plugin/src/plugin/target/targets.unit.test.ts +++ b/packages/nx-plugin/src/plugin/target/targets.unit.test.ts @@ -108,7 +108,7 @@ describe('createTargets', () => { ).resolves.toStrictEqual( expect.objectContaining({ [targetName]: { - executor: `${PACKAGE_NAME}:autorun`, + executor: `${PACKAGE_NAME}:cli`, }, }), ); @@ -132,7 +132,7 @@ describe('createTargets', () => { } as NormalizedCreateNodesContext), ).resolves.toStrictEqual({ [DEFAULT_TARGET_NAME]: { - executor: '@code-pushup/nx-plugin:autorun', + executor: '@code-pushup/nx-plugin:cli', }, }); }); @@ -157,7 +157,7 @@ describe('createTargets', () => { } as NormalizedCreateNodesContext), ).resolves.toStrictEqual({ cp: { - executor: '@code-pushup/nx-plugin:autorun', + executor: '@code-pushup/nx-plugin:cli', }, }); }); diff --git a/tools/src/publish/bin/bump-package.ts b/tools/src/publish/bin/bump-package.ts index d01e1cc98..efd610e06 100644 --- a/tools/src/publish/bin/bump-package.ts +++ b/tools/src/publish/bin/bump-package.ts @@ -26,7 +26,6 @@ try { if (version != null) { if (packageJson.version === version) { console.info(`Package version is already set to ${version}.`); - process.exit(0); } console.info( @@ -34,7 +33,11 @@ try { ); writeFileSync( packageJsonFile, - JSON.stringify({ ...packageJson, version }, null, 2), + JSON.stringify( + { ...packageJson, version, description: 'E2E test' }, + null, + 2, + ), ); process.exit(0); } diff --git a/tools/src/verdaccio/utils.ts b/tools/src/verdaccio/utils.ts index be9c513cd..f72734b2c 100644 --- a/tools/src/verdaccio/utils.ts +++ b/tools/src/verdaccio/utils.ts @@ -10,28 +10,9 @@ export function configureRegistry({ registry, registryNoProtocol, }: RegistryData) { - /** - * Sets environment variables for NPM and Yarn registries, and optionally configures - * Yarn's unsafe HTTP whitelist. - * - * @param {string} registry - The registry URL to set for NPM and Yarn. - * @param {string} host - The hostname to whitelist for Yarn (optional). - * - * Variables Set: - * - `npm_config_registry`: NPM registry. - * - `YARN_REGISTRY`: Yarn v1 registry. - * - `YARN_NPM_REGISTRY_SERVER`: Yarn v2 registry. - * - `YARN_UNSAFE_HTTP_WHITELIST`: Yarn HTTP whitelist. - */ - process.env.npm_config_registry = registry; - process.env.YARN_REGISTRY = registry; - process.env.YARN_NPM_REGISTRY_SERVER = registry; - console.info(`Set NPM and yarn registry process.env`); + console.info(`Set NPM registry under location user to ${registry}`); + execSync(`npm config set registry "${registry}"`); - /** - * Optional: Set Yarn HTTP whitelist for non-HTTPS registries. - */ - process.env.YARN_UNSAFE_HTTP_WHITELIST = host; console.info(`Set yarn whitelΓ­st process.env`); /** @@ -48,6 +29,7 @@ export function configureRegistry({ export function unconfigureRegistry({ registryNoProtocol, }: Pick) { + execSync('npm config delete registry'); execSync(`npm config delete ${registryNoProtocol}/:_authToken`); console.info('delete npm authToken: ' + registryNoProtocol); }