From 629e9f555847a58c72acd44dd5b690b04b43add5 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 1 Mar 2026 16:54:31 +0100 Subject: [PATCH] Add deprecation warnings for old camelCase flag names Instead of a hard breaking change, the old camelCase flag names (--baseVersion, --successCache, --successCacheDir, --alwaysPack) still work via type-flag's built-in camelCase/kebab-case mapping, but now print a deprecation warning pointing to the new kebab-case spelling. Downgrades the changeset from minor (breaking) to patch. Signed-off-by: Patrik Oldsberg Made-with: Cursor --- .changeset/migrate-cli-commands-to-cleye.md | 6 +- packages/cli/src/lib/warnDeprecatedFlags.ts | 38 ++++++++ .../modules/build/commands/buildWorkspace.ts | 25 +++--- .../src/modules/lint/commands/repo/lint.ts | 81 +++++++++-------- packages/cli/src/modules/new/commands/new.ts | 87 +++++++++---------- .../src/modules/test/commands/repo/test.ts | 42 +++++---- 6 files changed, 160 insertions(+), 119 deletions(-) create mode 100644 packages/cli/src/lib/warnDeprecatedFlags.ts diff --git a/.changeset/migrate-cli-commands-to-cleye.md b/.changeset/migrate-cli-commands-to-cleye.md index 9d52fb2260..34b595e1a5 100644 --- a/.changeset/migrate-cli-commands-to-cleye.md +++ b/.changeset/migrate-cli-commands-to-cleye.md @@ -1,8 +1,8 @@ --- -'@backstage/cli': minor +'@backstage/cli': patch --- -**BREAKING**: Migrated remaining CLI command handlers from `commander` to `cleye` for argument parsing. The following CLI flags have been renamed from camelCase to kebab-case to match standard CLI conventions: +Migrated remaining CLI command handlers from `commander` to `cleye` for argument parsing. The following CLI flags have been renamed from camelCase to kebab-case to match standard CLI conventions: - `--baseVersion` → `--base-version` - `--successCache` → `--success-cache` @@ -10,4 +10,4 @@ - `--alwaysPack` → `--always-pack` - `--alwaysYarnPack` → `--always-pack` (hidden legacy alias preserved) -If you have scripts or CI configurations that use any of the above flags, update them to the new kebab-case spelling. +The old camelCase flag names still work but will print a deprecation warning. Please update any scripts or CI configurations to use the new kebab-case spelling. diff --git a/packages/cli/src/lib/warnDeprecatedFlags.ts b/packages/cli/src/lib/warnDeprecatedFlags.ts new file mode 100644 index 0000000000..5b7b82794f --- /dev/null +++ b/packages/cli/src/lib/warnDeprecatedFlags.ts @@ -0,0 +1,38 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Scans args for deprecated camelCase flag names and logs a warning for each + * match. Since type-flag accepts both camelCase and kebab-case, the old names + * still work — this just nudges users toward the new spelling. + */ +export function warnDeprecatedFlags( + args: string[], + flags: Record, +) { + for (const key of Object.keys(flags)) { + const kebab = key.replace(/[A-Z]/g, c => `-${c.toLowerCase()}`); + if (kebab === key) { + continue; + } + const deprecated = `--${key}`; + if (args.some(a => a === deprecated || a.startsWith(`${deprecated}=`))) { + process.stderr.write( + `DEPRECATION WARNING: ${deprecated} has been renamed to --${kebab}\n`, + ); + } + } +} diff --git a/packages/cli/src/modules/build/commands/buildWorkspace.ts b/packages/cli/src/modules/build/commands/buildWorkspace.ts index 1f9bdff45c..ccb8267503 100644 --- a/packages/cli/src/modules/build/commands/buildWorkspace.ts +++ b/packages/cli/src/modules/build/commands/buildWorkspace.ts @@ -17,28 +17,33 @@ import fs from 'fs-extra'; import { cli } from 'cleye'; import { createDistWorkspace } from '../lib/packager'; +import { warnDeprecatedFlags } from '../../../lib/warnDeprecatedFlags'; import type { CommandContext } from '../../../wiring/types'; export default async ({ args, info }: CommandContext) => { - // Support legacy --alwaysYarnPack alias + // Support legacy --alwaysYarnPack and --alwaysPack aliases const normalizedArgs = args.map(a => - a === '--alwaysYarnPack' ? '--always-pack' : a, + a === '--alwaysYarnPack' || a === '--alwaysPack' ? '--always-pack' : a, ); + const flagDefs = { + alwaysPack: { + type: Boolean, + description: + 'Force workspace output to be a result of running `yarn pack` on each package (warning: very slow)', + }, + }; + + warnDeprecatedFlags(args, flagDefs); + const { flags: { alwaysPack }, _: positionals, } = cli( { - help: info, + help: { ...info, usage: `${info.usage} [packages...]` }, parameters: ['', '[packages...]'], - flags: { - alwaysPack: { - type: Boolean, - description: - 'Force workspace output to be a result of running `yarn pack` on each package (warning: very slow)', - }, - }, + flags: flagDefs, }, undefined, normalizedArgs, diff --git a/packages/cli/src/modules/lint/commands/repo/lint.ts b/packages/cli/src/modules/lint/commands/repo/lint.ts index 513c76e0a5..80df175315 100644 --- a/packages/cli/src/modules/lint/commands/repo/lint.ts +++ b/packages/cli/src/modules/lint/commands/repo/lint.ts @@ -29,6 +29,7 @@ import { import { targetPaths } from '@backstage/cli-common'; import { createScriptOptionsParser } from '../../lib/optionsParser'; +import { warnDeprecatedFlags } from '../../../../lib/warnDeprecatedFlags'; import type { CommandContext } from '../../../../wiring/types'; function depCount(pkg: BackstagePackageJson) { @@ -40,6 +41,43 @@ function depCount(pkg: BackstagePackageJson) { } export default async ({ args, info }: CommandContext) => { + const flagDefs = { + fix: { + type: Boolean, + description: 'Attempt to automatically fix violations', + }, + format: { + type: String, + description: 'Lint report output format', + default: 'eslint-formatter-friendly', + }, + outputFile: { + type: String, + description: 'Write the lint report to a file instead of stdout', + }, + successCache: { + type: Boolean, + description: + 'Enable success caching, which skips running tests for unchanged packages that were successful in the previous run', + }, + successCacheDir: { + type: String, + description: + 'Set the success cache location, (default: node_modules/.cache/backstage-cli)', + }, + since: { + type: String, + description: 'Only lint packages that changed since the specified ref', + }, + maxWarnings: { + type: String, + description: + 'Fail if more than this number of warnings. -1 allows warnings. (default: -1)', + }, + }; + + warnDeprecatedFlags(args, flagDefs); + const { flags: { fix, @@ -50,48 +88,7 @@ export default async ({ args, info }: CommandContext) => { since, maxWarnings, }, - } = cli( - { - help: info, - flags: { - fix: { - type: Boolean, - description: 'Attempt to automatically fix violations', - }, - format: { - type: String, - description: 'Lint report output format', - default: 'eslint-formatter-friendly', - }, - outputFile: { - type: String, - description: 'Write the lint report to a file instead of stdout', - }, - successCache: { - type: Boolean, - description: - 'Enable success caching, which skips running tests for unchanged packages that were successful in the previous run', - }, - successCacheDir: { - type: String, - description: - 'Set the success cache location, (default: node_modules/.cache/backstage-cli)', - }, - since: { - type: String, - description: - 'Only lint packages that changed since the specified ref', - }, - maxWarnings: { - type: String, - description: - 'Fail if more than this number of warnings. -1 allows warnings. (default: -1)', - }, - }, - }, - undefined, - args, - ); + } = cli({ help: info, flags: flagDefs }, undefined, args); let packages = await PackageGraph.listTargetPackages(); diff --git a/packages/cli/src/modules/new/commands/new.ts b/packages/cli/src/modules/new/commands/new.ts index e5c674c50c..837fb3e50e 100644 --- a/packages/cli/src/modules/new/commands/new.ts +++ b/packages/cli/src/modules/new/commands/new.ts @@ -16,9 +16,50 @@ import { cli } from 'cleye'; import { createNewPackage } from '../lib/createNewPackage'; +import { warnDeprecatedFlags } from '../../../lib/warnDeprecatedFlags'; import type { CommandContext } from '../../../wiring/types'; export default async ({ args, info }: CommandContext) => { + const flagDefs = { + select: { + type: String, + description: 'Select the thing you want to be creating upfront', + }, + option: { + type: [String] as const, + description: 'Pre-fill options for the creation process', + default: [] as string[], + }, + skipInstall: { + type: Boolean, + description: `Skips running 'yarn install' and 'yarn lint --fix'`, + }, + scope: { + type: String, + description: 'The scope to use for new packages', + }, + npmRegistry: { + type: String, + description: 'The package registry to use for new packages', + }, + baseVersion: { + type: String, + description: 'The version to use for any new packages (default: 0.1.0)', + }, + license: { + type: String, + description: + 'The license to use for any new packages (default: Apache-2.0)', + }, + private: { + type: Boolean, + description: 'Mark new packages as private', + default: true, + }, + }; + + warnDeprecatedFlags(args, flagDefs); + const { flags: { select, @@ -30,51 +71,7 @@ export default async ({ args, info }: CommandContext) => { license, private: isPrivate, }, - } = cli( - { - help: info, - flags: { - select: { - type: String, - description: 'Select the thing you want to be creating upfront', - }, - option: { - type: [String], - description: 'Pre-fill options for the creation process', - default: [], - }, - skipInstall: { - type: Boolean, - description: `Skips running 'yarn install' and 'yarn lint --fix'`, - }, - scope: { - type: String, - description: 'The scope to use for new packages', - }, - npmRegistry: { - type: String, - description: 'The package registry to use for new packages', - }, - baseVersion: { - type: String, - description: - 'The version to use for any new packages (default: 0.1.0)', - }, - license: { - type: String, - description: - 'The license to use for any new packages (default: Apache-2.0)', - }, - private: { - type: Boolean, - description: 'Mark new packages as private', - default: true, - }, - }, - }, - undefined, - args, - ); + } = cli({ help: info, flags: flagDefs }, undefined, args); const prefilledParams = parseParams(rawArgOptions); diff --git a/packages/cli/src/modules/test/commands/repo/test.ts b/packages/cli/src/modules/test/commands/repo/test.ts index 743aa87c3d..2d639d61e5 100644 --- a/packages/cli/src/modules/test/commands/repo/test.ts +++ b/packages/cli/src/modules/test/commands/repo/test.ts @@ -31,6 +31,7 @@ import { findOwnPaths, isChildPath, } from '@backstage/cli-common'; +import { warnDeprecatedFlags } from '../../../../lib/warnDeprecatedFlags'; import type { CommandContext } from '../../../../wiring/types'; type JestProject = { @@ -136,28 +137,31 @@ export default async ({ args, info }: CommandContext) => { // Parse Backstage-specific flags; unknown flags and arguments are left in // args so they can be forwarded to Jest. + const flagDefs = { + since: { + type: String, + description: 'Only include test packages changed since the specified ref', + }, + successCache: { + type: Boolean, + description: 'Cache and skip tests for unchanged packages', + }, + successCacheDir: { + type: String, + description: 'Directory for the success cache', + }, + jestHelp: { + type: Boolean, + description: "Show Jest's own help output", + }, + }; + + warnDeprecatedFlags(args, flagDefs); + const { flags: opts } = cli( { help: info, - flags: { - since: { - type: String, - description: - 'Only include test packages changed since the specified ref', - }, - successCache: { - type: Boolean, - description: 'Cache and skip tests for unchanged packages', - }, - successCacheDir: { - type: String, - description: 'Directory for the success cache', - }, - jestHelp: { - type: Boolean, - description: "Show Jest's own help output", - }, - }, + flags: flagDefs, ignoreArgv: type => type === 'unknown-flag' || type === 'argument', }, undefined,