Merge pull request #29023 from backstage/sennyeya/cli-modularization-next

chore: move migrate to its own CLI module
This commit is contained in:
Aramis Sennyey
2025-03-02 09:18:31 -06:00
committed by GitHub
16 changed files with 245 additions and 85 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
Internal change to move the `migrate` and `version:*` commands into a new migrate module.
+2 -2
View File
@@ -19,10 +19,10 @@ Commands:
config:schema [options]
repo [command]
package [command]
migrate [command]
build-workspace [options] <workspace-dir> [packages...]
versions:bump [options]
versions:migrate [options]
migrate [command]
build-workspace [options] <workspace-dir> [packages...]
create-github-app <github-org>
info
help [command]
+1
View File
@@ -26,5 +26,6 @@ import chalk from 'chalk';
const initializer = new CliInitializer();
initializer.add(import('./modules/config/alpha'));
initializer.add(import('./modules/build/alpha'));
initializer.add(import('./modules/migrate/alpha'));
await initializer.run();
})();
+1 -66
View File
@@ -25,6 +25,7 @@ import {
registerRepoCommands as registerRepoBuildCommands,
registerCommands as registerBuildCommands,
} from '../modules/build';
import { registerCommands as registerMigrateCommand } from '../modules/migrate';
export function registerRepoCommand(program: Command) {
const command = program
@@ -171,41 +172,6 @@ export function registerScriptCommand(program: Command) {
.action(lazy(() => import('./pack'), 'post'));
}
export function registerMigrateCommand(program: Command) {
const command = program
.command('migrate [command]')
.description('Migration utilities');
command
.command('package-roles')
.description(`Add package role field to packages that don't have it`)
.action(lazy(() => import('./migrate/packageRole'), 'default'));
command
.command('package-scripts')
.description('Set package scripts according to each package role')
.action(lazy(() => import('./migrate/packageScripts'), 'command'));
command
.command('package-exports')
.description('Synchronize package subpath export definitions')
.action(lazy(() => import('./migrate/packageExports'), 'command'));
command
.command('package-lint-configs')
.description(
'Migrates all packages to use @backstage/cli/config/eslint-factory',
)
.action(lazy(() => import('./migrate/packageLintConfigs'), 'command'));
command
.command('react-router-deps')
.description(
'Migrates the react-router dependencies for all packages to be peer dependencies',
)
.action(lazy(() => import('./migrate/reactRouterDeps'), 'command'));
}
export function registerCommands(program: Command) {
program
.command('new')
@@ -249,37 +215,6 @@ export function registerCommands(program: Command) {
registerMigrateCommand(program);
registerBuildCommands(program);
program
.command('versions:bump')
.option(
'--pattern <glob>',
'Override glob for matching packages to upgrade',
)
.option(
'--release <version|next|main>',
'Bump to a specific Backstage release line or version',
'main',
)
.option('--skip-install', 'Skips yarn install step')
.option('--skip-migrate', 'Skips migration of any moved packages')
.description('Bump Backstage packages to the latest versions')
.action(lazy(() => import('./versions/bump'), 'default'));
program
.command('versions:migrate')
.option(
'--pattern <glob>',
'Override glob for matching packages to upgrade',
)
.option(
'--skip-code-changes',
'Skip code changes and only update package.json files',
)
.description(
'Migrate any plugins that have been moved to the @backstage-community namespace automatically',
)
.action(lazy(() => import('./versions/migrate'), 'default'));
program
.command('create-github-app <github-org>')
.description('Create new GitHub App in your organization.')
+135
View File
@@ -0,0 +1,135 @@
/*
* Copyright 2024 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.
*/
import { createCliPlugin } from '../../wiring/factory';
import { Command } from 'commander';
import { lazy } from '../../lib/lazy';
export default createCliPlugin({
pluginId: 'migrate',
init: async reg => {
reg.addCommand({
path: ['versions:migrate'],
description:
'Migrate any plugins that have been moved to the @backstage-community namespace automatically',
execute: async ({ args }) => {
const command = new Command();
const defaultCommand = command
.option(
'--pattern <glob>',
'Override glob for matching packages to upgrade',
)
.option(
'--skip-code-changes',
'Skip code changes and only update package.json files',
)
.action(lazy(() => import('./commands/versions/migrate'), 'default'));
await defaultCommand.parseAsync(args, { from: 'user' });
},
});
reg.addCommand({
path: ['versions:bump'],
description: 'Bump Backstage packages to the latest versions',
execute: async ({ args }) => {
const command = new Command();
const defaultCommand = command
.option(
'--pattern <glob>',
'Override glob for matching packages to upgrade',
)
.option(
'--release <version|next|main>',
'Bump to a specific Backstage release line or version',
'main',
)
.option('--skip-install', 'Skips yarn install step')
.option('--skip-migrate', 'Skips migration of any moved packages')
.action(lazy(() => import('./commands/versions/bump'), 'default'));
await defaultCommand.parseAsync(args, { from: 'user' });
},
});
reg.addCommand({
path: ['migrate', 'package-roles'],
description: `Add package role field to packages that don't have it`,
execute: async ({ args }) => {
const command = new Command();
const defaultCommand = command.action(
lazy(() => import('./commands/packageRole'), 'default'),
);
await defaultCommand.parseAsync(args, { from: 'user' });
},
});
reg.addCommand({
path: ['migrate', 'package-scripts'],
description: 'Set package scripts according to each package role',
execute: async ({ args }) => {
const command = new Command();
const defaultCommand = command.action(
lazy(() => import('./commands/packageScripts'), 'command'),
);
await defaultCommand.parseAsync(args, { from: 'user' });
},
});
reg.addCommand({
path: ['migrate', 'package-exports'],
description: 'Synchronize package subpath export definitions',
execute: async ({ args }) => {
const command = new Command();
const defaultCommand = command.action(
lazy(() => import('./commands/packageExports'), 'command'),
);
await defaultCommand.parseAsync(args, { from: 'user' });
},
});
reg.addCommand({
path: ['migrate', 'package-lint-configs'],
description:
'Migrates all packages to use @backstage/cli/config/eslint-factory',
execute: async ({ args }) => {
const command = new Command();
const defaultCommand = command.action(
lazy(() => import('./commands/packageLintConfigs'), 'command'),
);
await defaultCommand.parseAsync(args, { from: 'user' });
},
});
reg.addCommand({
path: ['migrate', 'react-router-deps'],
description:
'Migrates the react-router dependencies for all packages to be peer dependencies',
execute: async ({ args }) => {
const command = new Command();
const defaultCommand = command.action(
lazy(() => import('./commands/reactRouterDeps'), 'command'),
);
await defaultCommand.parseAsync(args, { from: 'user' });
},
});
},
});
@@ -18,7 +18,7 @@ import {
fixPackageExports,
readFixablePackages,
writeFixedPackages,
} from '../repo/fix';
} from '../../../commands/repo/fix';
export async function command() {
console.log(
@@ -17,7 +17,7 @@
import fs from 'fs-extra';
import { resolve as resolvePath } from 'path';
import { PackageGraph } from '@backstage/cli-node';
import { runPlain } from '../../lib/run';
import { runPlain } from '../../../lib/run';
const PREFIX = `module.exports = require('@backstage/cli/config/eslint-factory')`;
@@ -18,7 +18,7 @@ import fs from 'fs-extra';
import { resolve as resolvePath } from 'path';
import { getPackages } from '@manypkg/get-packages';
import { PackageRoles } from '@backstage/cli-node';
import { paths } from '../../lib/paths';
import { paths } from '../../../lib/paths';
export default async () => {
const { packages } = await getPackages(paths.targetDir);
@@ -16,10 +16,10 @@
import fs from 'fs-extra';
import { Command } from 'commander';
import * as runObj from '../../lib/run';
import * as runObj from '../../../../lib/run';
import bump, { bumpBackstageJsonVersion, createVersionFinder } from './bump';
import { registerMswTestHooks, withLogCollector } from '@backstage/test-utils';
import { YarnInfoInspectData } from '../../lib/versioning/packages';
import { YarnInfoInspectData } from '../../../../lib/versioning/packages';
import { setupServer } from 'msw/node';
import { rest } from 'msw';
import { NotFoundError } from '@backstage/errors';
@@ -73,15 +73,15 @@ jest.mock('@backstage/cli-common', () => ({
}),
}));
jest.mock('../../lib/run', () => {
jest.mock('../../../../lib/run', () => {
return {
run: jest.fn(),
};
});
const mockFetchPackageInfo = jest.fn();
jest.mock('../../lib/versioning/packages', () => {
const actual = jest.requireActual('../../lib/versioning/packages');
jest.mock('../../../../lib/versioning/packages', () => {
const actual = jest.requireActual('../../../../lib/versioning/packages');
return {
...actual,
fetchPackageInfo: (name: string) => mockFetchPackageInfo(name),
@@ -24,23 +24,23 @@ import yaml from 'yaml';
import z from 'zod';
import { isError, NotFoundError } from '@backstage/errors';
import { resolve as resolvePath } from 'path';
import { paths } from '../../lib/paths';
import { paths } from '../../../../lib/paths';
import {
mapDependencies,
fetchPackageInfo,
Lockfile,
YarnInfoInspectData,
} from '../../lib/versioning';
} from '../../../../lib/versioning';
import { BACKSTAGE_JSON } from '@backstage/cli-common';
import { runParallelWorkers } from '../../lib/parallel';
import { runParallelWorkers } from '../../../../lib/parallel';
import {
getManifestByReleaseLine,
getManifestByVersion,
ReleaseManifest,
} from '@backstage/release-manifests';
import { migrateMovedPackages } from './migrate';
import { runYarnInstall } from './utils';
import { run } from '../../lib/run';
import { runYarnInstall } from '../../lib/utils';
import { run } from '../../../../lib/run';
function maybeBootstrapProxy() {
// see https://www.npmjs.com/package/global-agent
@@ -17,7 +17,7 @@ import {
MockDirectory,
createMockDirectory,
} from '@backstage/backend-test-utils';
import * as run from '../../lib/run';
import * as run from '../../../../lib/run';
import migrate from './migrate';
import { withLogCollector } from '@backstage/test-utils';
import fs from 'fs-extra';
@@ -45,7 +45,7 @@ jest.mock('@backstage/cli-common', () => ({
}),
}));
jest.mock('../../lib/run', () => {
jest.mock('../../../../lib/run', () => {
return {
run: jest.fn(),
};
@@ -19,7 +19,7 @@ import { resolve as resolvePath, join as joinPath } from 'path';
import { OptionValues } from 'commander';
import { readJson, writeJson } from 'fs-extra';
import { minimatch } from 'minimatch';
import { runYarnInstall } from './utils';
import { runYarnInstall } from '../../lib/utils';
import replace from 'replace-in-file';
declare module 'replace-in-file' {
+84
View File
@@ -0,0 +1,84 @@
/*
* Copyright 2025 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.
*/
import { lazy } from '../../lib/lazy';
import { Command } from 'commander';
export function registerCommands(program: Command) {
program
.command('versions:bump')
.option(
'--pattern <glob>',
'Override glob for matching packages to upgrade',
)
.option(
'--release <version|next|main>',
'Bump to a specific Backstage release line or version',
'main',
)
.option('--skip-install', 'Skips yarn install step')
.option('--skip-migrate', 'Skips migration of any moved packages')
.description('Bump Backstage packages to the latest versions')
.action(lazy(() => import('./commands/versions/bump'), 'default'));
program
.command('versions:migrate')
.option(
'--pattern <glob>',
'Override glob for matching packages to upgrade',
)
.option(
'--skip-code-changes',
'Skip code changes and only update package.json files',
)
.description(
'Migrate any plugins that have been moved to the @backstage-community namespace automatically',
)
.action(lazy(() => import('./commands/versions/migrate'), 'default'));
const command = program
.command('migrate [command]')
.description('Migration utilities');
command
.command('package-roles')
.description(`Add package role field to packages that don't have it`)
.action(lazy(() => import('./commands/packageRole'), 'default'));
command
.command('package-scripts')
.description('Set package scripts according to each package role')
.action(lazy(() => import('./commands/packageScripts'), 'command'));
command
.command('package-exports')
.description('Synchronize package subpath export definitions')
.action(lazy(() => import('./commands/packageExports'), 'command'));
command
.command('package-lint-configs')
.description(
'Migrates all packages to use @backstage/cli/config/eslint-factory',
)
.action(lazy(() => import('./commands/packageLintConfigs'), 'command'));
command
.command('react-router-deps')
.description(
'Migrates the react-router dependencies for all packages to be peer dependencies',
)
.action(lazy(() => import('./commands/reactRouterDeps'), 'command'));
}
@@ -16,7 +16,7 @@
import ora from 'ora';
import chalk from 'chalk';
import { run } from '../../lib/run';
import { run } from '../../../lib/run';
export async function runYarnInstall() {
const spinner = ora({