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 <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2026-02-28 11:08:57 +01:00
parent ff4a45ac01
commit eb73feac15
4 changed files with 248 additions and 96 deletions
+155 -55
View File
@@ -33,10 +33,10 @@ Commands:
### `backstage-cli build-workspace`
```
Usage: program [options] <workspace-dir> [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] <github-org>
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 <version>
--license <license>
--no-private
--npm-registry <URL>
--option <name>=<value>
--scope <scope>
--select <name>
--base-version <string>
--license <string>
--npm-registry <string>
--option <string>
--private
--scope <string>
--select <string>
--skip-install
-h, --help
```
@@ -249,13 +249,13 @@ Commands:
### `backstage-cli package build`
```
Usage: program [options]
Usage: backstage-cli package build
Options:
--config <path>
--config <string>
--minify
--module-federation
--role <name>
--role <string>
--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 <format>
--max-warnings <number>
--output-file <path>
--format <string>
--max-warnings <string>
--output-file <string>
-h, --help
```
@@ -304,17 +304,17 @@ Options:
### `backstage-cli package start`
```
Usage: program [options]
Usage: backstage-cli package start
Options:
--check
--config <path>
--entrypoint <path>
--inspect [host]
--inspect-brk [host]
--link <path>
--require <path...>
--role <name>
--config <string>
--entrypoint <string>
--inspect <string>
--inspect-brk <string>
--link <string>
--require <string>
--role <string>
-h, --help
```
@@ -453,12 +453,12 @@ Commands:
### `backstage-cli repo build`
```
Usage: program [options] [command]
Usage: backstage-cli repo build
Options:
--all
--minify
--since <ref>
--since <string>
-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 <format>
--max-warnings <number>
--output-file <path>
--since <ref>
--successCache
--successCacheDir <path>
--format <string>
--max-warnings <string>
--output-file <string>
--since <string>
--success-cache
--success-cache-dir <string>
-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 <path>
--inspect [host]
--inspect-brk [host]
--link <path>
--plugin <pluginId>
--require <path...>
--config <string>
--inspect <string>
--inspect-brk <string>
--link <string>
--plugin <string>
--require <string>
-h, --help
```
### `backstage-cli repo test`
```
Usage: program [options]
Usage: <none>
Options:
--jest-help
--since <ref>
--successCache
--successCacheDir <path>
-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 <glob>
--release <version|next|main>
--pattern <string>
--release <string>
--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 <glob>
--pattern <string>
--skip-code-changes
-h, --help
```
@@ -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;
@@ -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;
@@ -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<string, string | boolean | null>) {
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();
});
});