From eb73feac1539197bfe8a230fe841fc28b439cf27 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 28 Feb 2026 11:08:57 +0100 Subject: [PATCH] Address review feedback and regenerate API reports - Handle quoted arguments in createScriptOptionsParser using a proper shell-like splitter instead of naive whitespace splitting - Pass CommandContext directly in bump tests instead of using a helper - Regenerate cli-report.md Signed-off-by: Patrik Oldsberg --- packages/cli/cli-report.md | 210 +++++++++++++----- .../src/modules/build/lib/optionsParser.ts | 36 ++- .../cli/src/modules/lint/lib/optionsParser.ts | 36 ++- .../migrate/commands/versions/bump.test.ts | 62 ++---- 4 files changed, 248 insertions(+), 96 deletions(-) diff --git a/packages/cli/cli-report.md b/packages/cli/cli-report.md index 591b32ead7..74972b901a 100644 --- a/packages/cli/cli-report.md +++ b/packages/cli/cli-report.md @@ -33,10 +33,10 @@ Commands: ### `backstage-cli build-workspace` ``` -Usage: program [options] [packages...] +Usage: backstage-cli build-workspace Options: - --alwaysPack + --always-pack -h, --help ``` @@ -131,7 +131,7 @@ Options: ### `backstage-cli create-github-app` ``` -Usage: program [options] +Usage: backstage-cli create-github-app Options: -h, --help @@ -213,16 +213,16 @@ Options: ### `backstage-cli new` ``` -Usage: program [options] +Usage: backstage-cli new Options: - --baseVersion - --license - --no-private - --npm-registry - --option = - --scope - --select + --base-version + --license + --npm-registry + --option + --private + --scope + --select --skip-install -h, --help ``` @@ -249,13 +249,13 @@ Commands: ### `backstage-cli package build` ``` -Usage: program [options] +Usage: backstage-cli package build Options: - --config + --config --minify --module-federation - --role + --role --skip-build-dependencies --stats -h, --help @@ -273,13 +273,13 @@ Options: ### `backstage-cli package lint` ``` -Usage: program [options] [directories...] +Usage: backstage-cli package lint Options: --fix - --format - --max-warnings - --output-file + --format + --max-warnings + --output-file -h, --help ``` @@ -304,17 +304,17 @@ Options: ### `backstage-cli package start` ``` -Usage: program [options] +Usage: backstage-cli package start Options: --check - --config - --entrypoint - --inspect [host] - --inspect-brk [host] - --link - --require - --role + --config + --entrypoint + --inspect + --inspect-brk + --link + --require + --role -h, --help ``` @@ -453,12 +453,12 @@ Commands: ### `backstage-cli repo build` ``` -Usage: program [options] [command] +Usage: backstage-cli repo build Options: --all --minify - --since + --since -h, --help ``` @@ -474,7 +474,7 @@ Options: ### `backstage-cli repo fix` ``` -Usage: program [options] +Usage: backstage-cli repo fix Options: --check @@ -485,23 +485,23 @@ Options: ### `backstage-cli repo lint` ``` -Usage: program [options] [command] +Usage: backstage-cli repo lint Options: --fix - --format - --max-warnings - --output-file - --since - --successCache - --successCacheDir + --format + --max-warnings + --output-file + --since + --success-cache + --success-cache-dir -h, --help ``` ### `backstage-cli repo list-deprecations` ``` -Usage: program [options] +Usage: backstage-cli repo list-deprecations Options: --json @@ -511,29 +511,129 @@ Options: ### `backstage-cli repo start` ``` -Usage: program [options] [packageNameOrPath...] +Usage: backstage-cli repo start Options: - --config - --inspect [host] - --inspect-brk [host] - --link - --plugin - --require + --config + --inspect + --inspect-brk + --link + --plugin + --require -h, --help ``` ### `backstage-cli repo test` ``` -Usage: program [options] +Usage: Options: - --jest-help - --since - --successCache - --successCacheDir - -h, --help + --all + --automock + --cache + --cacheDirectory + --changedFilesWithAncestor + --changedSince + --ci + --clearCache + --clearMocks + --collectCoverage + --collectCoverageFrom + --color + --colors + --coverage + --coverageDirectory + --coveragePathIgnorePatterns + --coverageProvider + --coverageReporters + --coverageThreshold + --debug + --detectLeaks + --detectOpenHandles + --errorOnDeprecated + --filter + --findRelatedTests + --forceExit + --globalSetup + --globalTeardown + --globals + --haste + --help + --ignoreProjects + --injectGlobals + --json + --lastCommit + --listTests + --logHeapUsage + --maxConcurrency + --moduleDirectories + --moduleFileExtensions + --moduleNameMapper + --modulePathIgnorePatterns + --modulePaths + --noStackTrace + --notify + --notifyMode + --openHandlesTimeout + --outputFile + --passWithNoTests + --preset + --prettierPath + --projects + --randomize + --reporters + --resetMocks + --resetModules + --resolver + --restoreMocks + --rootDir + --roots + --runTestsByPath + --runner + --seed + --selectProjects + --setupFiles + --setupFilesAfterEnv + --shard + --showConfig + --showSeed + --silent + --skipFilter + --snapshotSerializers + --testEnvironment, --env + --testEnvironmentOptions + --testFailureExitCode + --testLocationInResults + --testMatch + --testPathIgnorePatterns + --testPathPatterns + --testRegex + --testResultsProcessor + --testRunner + --testSequencer + --testTimeout + --transform + --transformIgnorePatterns + --unmockedModulePathPatterns + --useStderr + --verbose + --version + --waitForUnhandledRejections + --watch + --watchAll + --watchPathIgnorePatterns + --watchman + --workerThreads + -b, --bail + -c, --config + -e, --expand + -f, --onlyFailures + -i, --runInBand + -o, --onlyChanged + -t, --testNamePattern + -u, --updateSnapshot + -w, --maxWorkers ``` ### `backstage-cli translations` @@ -575,11 +675,11 @@ Options: ### `backstage-cli versions:bump` ``` -Usage: program [options] +Usage: backstage-cli versions:bump Options: - --pattern - --release + --pattern + --release --skip-install --skip-migrate -h, --help @@ -588,10 +688,10 @@ Options: ### `backstage-cli versions:migrate` ``` -Usage: program [options] +Usage: backstage-cli versions:migrate Options: - --pattern + --pattern --skip-code-changes -h, --help ``` diff --git a/packages/cli/src/modules/build/lib/optionsParser.ts b/packages/cli/src/modules/build/lib/optionsParser.ts index 964825830a..ced48b4c91 100644 --- a/packages/cli/src/modules/build/lib/optionsParser.ts +++ b/packages/cli/src/modules/build/lib/optionsParser.ts @@ -15,6 +15,40 @@ */ import { parseArgs, type ParseArgsConfig } from 'node:util'; +// Splits a shell-like argument string, respecting single and double quotes +function splitShellArgs(str: string): string[] { + const args: string[] = []; + let current = ''; + let quote: string | undefined; + + for (let i = 0; i < str.length; i++) { + const ch = str[i]; + + if (quote) { + if (ch === quote) { + quote = undefined; + } else { + current += ch; + } + } else if (ch === '"' || ch === "'") { + quote = ch; + } else if (/\s/.test(ch)) { + if (current) { + args.push(current); + current = ''; + } + } else { + current += ch; + } + } + + if (current) { + args.push(current); + } + + return args; +} + export function createScriptOptionsParser( commandPath: string[], options: ParseArgsConfig['options'], @@ -27,7 +61,7 @@ export function createScriptOptionsParser( } const argsStr = scriptStr.slice(expectedScript.length).trim(); - const args = argsStr ? argsStr.split(/\s+/) : []; + const args = argsStr ? splitShellArgs(argsStr) : []; const { values } = parseArgs({ args, strict: false, options }); return values; diff --git a/packages/cli/src/modules/lint/lib/optionsParser.ts b/packages/cli/src/modules/lint/lib/optionsParser.ts index 964825830a..ced48b4c91 100644 --- a/packages/cli/src/modules/lint/lib/optionsParser.ts +++ b/packages/cli/src/modules/lint/lib/optionsParser.ts @@ -15,6 +15,40 @@ */ import { parseArgs, type ParseArgsConfig } from 'node:util'; +// Splits a shell-like argument string, respecting single and double quotes +function splitShellArgs(str: string): string[] { + const args: string[] = []; + let current = ''; + let quote: string | undefined; + + for (let i = 0; i < str.length; i++) { + const ch = str[i]; + + if (quote) { + if (ch === quote) { + quote = undefined; + } else { + current += ch; + } + } else if (ch === '"' || ch === "'") { + quote = ch; + } else if (/\s/.test(ch)) { + if (current) { + args.push(current); + current = ''; + } + } else { + current += ch; + } + } + + if (current) { + args.push(current); + } + + return args; +} + export function createScriptOptionsParser( commandPath: string[], options: ParseArgsConfig['options'], @@ -27,7 +61,7 @@ export function createScriptOptionsParser( } const argsStr = scriptStr.slice(expectedScript.length).trim(); - const args = argsStr ? argsStr.split(/\s+/) : []; + const args = argsStr ? splitShellArgs(argsStr) : []; const { values } = parseArgs({ args, strict: false, options }); return values; diff --git a/packages/cli/src/modules/migrate/commands/versions/bump.test.ts b/packages/cli/src/modules/migrate/commands/versions/bump.test.ts index 88e10e2607..f119a0bedb 100644 --- a/packages/cli/src/modules/migrate/commands/versions/bump.test.ts +++ b/packages/cli/src/modules/migrate/commands/versions/bump.test.ts @@ -17,7 +17,6 @@ import fs from 'fs-extra'; import * as runObj from '@backstage/cli-common'; import { overrideTargetPaths } from '@backstage/cli-common/testUtils'; import bump, { bumpBackstageJsonVersion, createVersionFinder } from './bump'; -import type { CommandContext } from '../../../../wiring/types'; import { registerMswTestHooks, withLogCollector } from '@backstage/test-utils'; import { YarnInfoInspectData } from '../../lib/versioning/packages'; import { setupServer } from 'msw/node'; @@ -126,27 +125,7 @@ const expectLogsToMatch = ( expect(receivedLogs.filter(Boolean).sort()).toEqual(expected.sort()); }; -function callBump(flags: Record) { - const args: string[] = []; - for (const [key, value] of Object.entries(flags)) { - if (value === null || value === undefined) { - continue; - } - const flag = `--${key.replace(/[A-Z]/g, c => `-${c.toLowerCase()}`)}`; - if (typeof value === 'boolean') { - if (value) { - args.push(flag); - } - } else { - args.push(flag, value); - } - } - const context: CommandContext = { - args, - info: { usage: 'backstage-cli versions:bump', description: 'test' }, - }; - return bump(context); -} +const info = { usage: 'backstage-cli versions:bump', description: '' }; describe('bump', () => { const mockDir = createMockDirectory(); @@ -212,7 +191,7 @@ describe('bump', () => { ), ); const { log: logs } = await withLogCollector(['log', 'warn'], async () => { - await callBump({ release: 'main' }); + await bump({ args: ['--release', 'main'], info }); }); expectLogsToMatch(logs, [ 'Using default pattern glob @backstage/*', @@ -305,7 +284,7 @@ describe('bump', () => { ), ); const { log: logs } = await withLogCollector(['log', 'warn'], async () => { - await callBump({ release: 'main', skipInstall: true }); + await bump({ args: ['--release', 'main', '--skip-install'], info }); }); expectLogsToMatch(logs, [ 'Using default pattern glob @backstage/*', @@ -407,7 +386,7 @@ describe('bump', () => { ), ); const { log: logs } = await withLogCollector(['log', 'warn'], async () => { - await callBump({ release: 'main' }); + await bump({ args: ['--release', 'main'], info }); }); expectLogsToMatch(logs, [ 'Using default pattern glob @backstage/*', @@ -511,7 +490,7 @@ describe('bump', () => { ), ); const { log: logs } = await withLogCollector(['log', 'warn'], async () => { - await callBump({ release: 'main' }); + await bump({ args: ['--release', 'main'], info }); }); expectLogsToMatch(logs, [ 'Using default pattern glob @backstage/*', @@ -606,9 +585,9 @@ describe('bump', () => { ), ); const { log: logs } = await withLogCollector(['log', 'warn'], async () => { - await expect(callBump({ release: '999.0.1' })).rejects.toThrow( - 'No release found for 999.0.1 version', - ); + await expect( + bump({ args: ['--release', '999.0.1'], info }), + ).rejects.toThrow('No release found for 999.0.1 version'); }); expect(logs.filter(Boolean)).toEqual([ 'Using default pattern glob @backstage/*', @@ -712,7 +691,7 @@ describe('bump', () => { ), ); const { log: logs } = await withLogCollector(['log', 'warn'], async () => { - await callBump({ release: 'next' }); + await bump({ args: ['--release', 'next'], info }); }); expectLogsToMatch(logs, [ 'Using default pattern glob @backstage/*', @@ -791,9 +770,14 @@ describe('bump', () => { ), ); const { log: logs } = await withLogCollector(['log', 'warn'], async () => { - await callBump({ - pattern: '@{backstage,backstage-extra}/*', - release: 'main', + await bump({ + args: [ + '--pattern', + '@{backstage,backstage-extra}/*', + '--release', + 'main', + ], + info, }); }); expectLogsToMatch(logs, [ @@ -900,7 +884,7 @@ describe('bump', () => { ), ); const { log: logs } = await withLogCollector(['log', 'warn'], async () => { - await callBump({ release: 'main' }); + await bump({ args: ['--release', 'main'], info }); }); expectLogsToMatch(logs, [ 'Using default pattern glob @backstage/*', @@ -1139,7 +1123,7 @@ describe('environment variables', () => { ); const { log: logs } = await withLogCollector(['log', 'warn'], async () => { - await callBump({ release: 'main' }); + await bump({ args: ['--release', 'main'], info }); }); expectLogsToMatch(logs, [ @@ -1213,7 +1197,7 @@ describe('environment variables', () => { } as any); const { log: logs } = await withLogCollector(['log', 'warn'], async () => { - await callBump({ release: 'main' }); + await bump({ args: ['--release', 'main'], info }); }); expectLogsToMatch(logs, [ @@ -1298,7 +1282,7 @@ describe('environment variables', () => { ); const { log: logs } = await withLogCollector(['log', 'warn'], async () => { - await callBump({ release: 'main' }); + await bump({ args: ['--release', 'main'], info }); }); expectLogsToMatch(logs, [ @@ -1347,7 +1331,7 @@ describe('environment variables', () => { }, }); - await expect(callBump({ release: 'main' })).rejects.toThrow(); + await expect(bump({ args: ['--release', 'main'], info })).rejects.toThrow(); }); it('should handle network errors when using custom base URL', async () => { @@ -1375,6 +1359,6 @@ describe('environment variables', () => { ), ); - await expect(callBump({ release: 'main' })).rejects.toThrow(); + await expect(bump({ args: ['--release', 'main'], info })).rejects.toThrow(); }); });