diff --git a/packages/cli-internal/src/InternalCliPlugin.ts b/packages/cli-internal/src/InternalCliPlugin.ts index 62bf5013c8..426f3d23e6 100644 --- a/packages/cli-internal/src/InternalCliPlugin.ts +++ b/packages/cli-internal/src/InternalCliPlugin.ts @@ -21,10 +21,8 @@ export const OpaqueCliPlugin = OpaqueType.create<{ public: CliPlugin; versions: { readonly version: 'v1'; - readonly description: string; - init: (registry: { - addCommand: (command: BackstageCommand) => void; - }) => Promise; + readonly packageName: string; + readonly commands: Promise>; }; }>({ type: '@backstage/CliPlugin', diff --git a/packages/cli-plugin-api/report.api.md b/packages/cli-plugin-api/report.api.md index 3562bb20e6..d72169495f 100644 --- a/packages/cli-plugin-api/report.api.md +++ b/packages/cli-plugin-api/report.api.md @@ -11,10 +11,10 @@ export interface BackstageCommand { description: string; // (undocumented) execute: - | CommandExecuteFn + | ((context: CommandContext) => Promise) | { loader: () => Promise<{ - default: CommandExecuteFn; + default: (context: CommandContext) => Promise; }>; }; // (undocumented) @@ -30,8 +30,6 @@ export type CliFeature = CliPlugin; export interface CliPlugin { // (undocumented) readonly $$type: '@backstage/CliPlugin'; - // (undocumented) - readonly pluginId: string; } // @public @@ -45,12 +43,11 @@ export interface CommandContext { }; } -// @public -export type CommandExecuteFn = (context: CommandContext) => Promise; - // @public export function createCliPlugin(options: { - pluginId: string; + packageJson: { + name: string; + }; init: (registry: { addCommand: (command: BackstageCommand) => void; }) => Promise; diff --git a/packages/cli-plugin-api/src/createCliPlugin.ts b/packages/cli-plugin-api/src/createCliPlugin.ts index 7a8d299394..04a26665a6 100644 --- a/packages/cli-plugin-api/src/createCliPlugin.ts +++ b/packages/cli-plugin-api/src/createCliPlugin.ts @@ -15,7 +15,6 @@ */ import { OpaqueCliPlugin } from '@internal/cli'; -import { describeParentCallSite } from './describeParentCallSite'; import { BackstageCommand, CliPlugin } from './types'; /** @@ -24,14 +23,18 @@ import { BackstageCommand, CliPlugin } from './types'; * @public */ export function createCliPlugin(options: { - pluginId: string; + packageJson: { name: string }; init: (registry: { addCommand: (command: BackstageCommand) => void; }) => Promise; }): CliPlugin { + const commands: BackstageCommand[] = []; + const commandsPromise = options + .init({ addCommand: command => commands.push(command) }) + .then(() => commands); + return OpaqueCliPlugin.createInstance('v1', { - pluginId: options.pluginId, - init: options.init, - description: `created at '${describeParentCallSite()}'`, + packageName: options.packageJson.name, + commands: commandsPromise, }); } diff --git a/packages/cli-plugin-api/src/describeParentCallSite.ts b/packages/cli-plugin-api/src/describeParentCallSite.ts deleted file mode 100644 index 641365bde8..0000000000 --- a/packages/cli-plugin-api/src/describeParentCallSite.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2023 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. - */ - -const MESSAGE_MARKER = 'eHgtF5hmbrXyiEvo'; - -/** - * Internal helper that describes the location of the parent caller. - * @internal - */ -export function describeParentCallSite( - ErrorConstructor: { new (message: string): Error } = Error, -): string { - const { stack } = new ErrorConstructor(MESSAGE_MARKER); - if (!stack) { - return ''; - } - - const startIndex = stack.includes(MESSAGE_MARKER) - ? stack.indexOf('\n') + 1 - : 0; - const secondEntryStart = - stack.indexOf('\n', stack.indexOf('\n', startIndex) + 1) + 1; - const secondEntryEnd = stack.indexOf('\n', secondEntryStart); - - const line = stack.substring(secondEntryStart, secondEntryEnd).trim(); - if (!line) { - return 'unknown'; - } - - // Chrome - if (line.includes('(')) { - return line.substring(line.indexOf('(') + 1, line.indexOf(')')); - } - - // Safari & Firefox - if (line.includes('@')) { - return line.substring(line.indexOf('@') + 1); - } - - return line; -} diff --git a/packages/cli-plugin-api/src/index.ts b/packages/cli-plugin-api/src/index.ts index 15082804ee..988600280c 100644 --- a/packages/cli-plugin-api/src/index.ts +++ b/packages/cli-plugin-api/src/index.ts @@ -18,7 +18,6 @@ export { createCliPlugin } from './createCliPlugin'; export type { BackstageCommand, CommandContext, - CommandExecuteFn, CliPlugin, CliFeature, } from './types'; diff --git a/packages/cli-plugin-api/src/types.ts b/packages/cli-plugin-api/src/types.ts index 42d006ce5c..3bdf026490 100644 --- a/packages/cli-plugin-api/src/types.ts +++ b/packages/cli-plugin-api/src/types.ts @@ -27,19 +27,12 @@ export interface CommandContext { */ usage: string; /** - * The description provided for the command + * The name of the command, for example: "repo test" */ - description: string; + name: string; }; } -/** - * A function that executes a CLI command. - * - * @public - */ -export type CommandExecuteFn = (context: CommandContext) => Promise; - /** * A command definition for a Backstage CLI plugin. * @@ -51,9 +44,11 @@ export interface BackstageCommand { deprecated?: boolean; experimental?: boolean; execute: - | CommandExecuteFn + | ((context: CommandContext) => Promise) | { - loader: () => Promise<{ default: CommandExecuteFn }>; + loader: () => Promise<{ + default: (context: CommandContext) => Promise; + }>; }; } @@ -70,6 +65,5 @@ export type CliFeature = CliPlugin; * @public */ export interface CliPlugin { - readonly pluginId: string; readonly $$type: '@backstage/CliPlugin'; } diff --git a/packages/cli/src/modules/auth/index.ts b/packages/cli/src/modules/auth/index.ts index d5766219b2..6ce849a9b9 100644 --- a/packages/cli/src/modules/auth/index.ts +++ b/packages/cli/src/modules/auth/index.ts @@ -15,9 +15,10 @@ */ import { createCliPlugin } from '../../wiring/factory'; +import packageJson from '../../../package.json'; export default createCliPlugin({ - pluginId: 'auth', + packageJson, init: async reg => { reg.addCommand({ path: ['auth', 'login'], diff --git a/packages/cli/src/modules/build/index.ts b/packages/cli/src/modules/build/index.ts index 2ccf1e3b6e..1c8d2217ed 100644 --- a/packages/cli/src/modules/build/index.ts +++ b/packages/cli/src/modules/build/index.ts @@ -15,9 +15,10 @@ */ import { createCliPlugin } from '@backstage/cli-plugin-api'; +import packageJson from '../../../package.json'; export const buildPlugin = createCliPlugin({ - pluginId: 'build', + packageJson, init: async reg => { reg.addCommand({ path: ['package', 'build'], diff --git a/packages/cli/src/modules/config/index.ts b/packages/cli/src/modules/config/index.ts index c8fb6bca7e..3c59f96948 100644 --- a/packages/cli/src/modules/config/index.ts +++ b/packages/cli/src/modules/config/index.ts @@ -14,6 +14,7 @@ * limitations under the License. */ import { createCliPlugin } from '@backstage/cli-plugin-api'; +import packageJson from '../../../package.json'; export const configOption = [ '--config ', @@ -23,7 +24,7 @@ export const configOption = [ ] as const; export default createCliPlugin({ - pluginId: 'config', + packageJson, init: async reg => { reg.addCommand({ path: ['config:docs'], diff --git a/packages/cli/src/modules/create-github-app/index.ts b/packages/cli/src/modules/create-github-app/index.ts index ea6cbf55be..69fe5869d4 100644 --- a/packages/cli/src/modules/create-github-app/index.ts +++ b/packages/cli/src/modules/create-github-app/index.ts @@ -14,9 +14,10 @@ * limitations under the License. */ import { createCliPlugin } from '@backstage/cli-plugin-api'; +import packageJson from '../../../package.json'; export default createCliPlugin({ - pluginId: 'new', + packageJson, init: async reg => { reg.addCommand({ path: ['create-github-app'], diff --git a/packages/cli/src/modules/info/index.ts b/packages/cli/src/modules/info/index.ts index 11ff207281..df8a848f11 100644 --- a/packages/cli/src/modules/info/index.ts +++ b/packages/cli/src/modules/info/index.ts @@ -14,9 +14,10 @@ * limitations under the License. */ import { createCliPlugin } from '@backstage/cli-plugin-api'; +import packageJson from '../../../package.json'; export default createCliPlugin({ - pluginId: 'info', + packageJson, init: async reg => { reg.addCommand({ path: ['info'], diff --git a/packages/cli/src/modules/lint/index.ts b/packages/cli/src/modules/lint/index.ts index d825740b9f..51e5b981a4 100644 --- a/packages/cli/src/modules/lint/index.ts +++ b/packages/cli/src/modules/lint/index.ts @@ -14,9 +14,10 @@ * limitations under the License. */ import { createCliPlugin } from '@backstage/cli-plugin-api'; +import packageJson from '../../../package.json'; export default createCliPlugin({ - pluginId: 'lint', + packageJson, init: async reg => { reg.addCommand({ path: ['package', 'lint'], diff --git a/packages/cli/src/modules/maintenance/index.ts b/packages/cli/src/modules/maintenance/index.ts index 4356c4654f..142bd543f2 100644 --- a/packages/cli/src/modules/maintenance/index.ts +++ b/packages/cli/src/modules/maintenance/index.ts @@ -14,9 +14,10 @@ * limitations under the License. */ import { createCliPlugin } from '@backstage/cli-plugin-api'; +import packageJson from '../../../package.json'; export default createCliPlugin({ - pluginId: 'maintenance', + packageJson, init: async reg => { reg.addCommand({ path: ['repo', 'fix'], 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 f119a0bedb..c7694f5e09 100644 --- a/packages/cli/src/modules/migrate/commands/versions/bump.test.ts +++ b/packages/cli/src/modules/migrate/commands/versions/bump.test.ts @@ -125,7 +125,7 @@ const expectLogsToMatch = ( expect(receivedLogs.filter(Boolean).sort()).toEqual(expected.sort()); }; -const info = { usage: 'backstage-cli versions:bump', description: '' }; +const info = { usage: 'backstage-cli versions:bump', name: 'versions:bump' }; describe('bump', () => { const mockDir = createMockDirectory(); diff --git a/packages/cli/src/modules/migrate/commands/versions/migrate.test.ts b/packages/cli/src/modules/migrate/commands/versions/migrate.test.ts index fc50a5c491..606fa528b1 100644 --- a/packages/cli/src/modules/migrate/commands/versions/migrate.test.ts +++ b/packages/cli/src/modules/migrate/commands/versions/migrate.test.ts @@ -123,7 +123,7 @@ describe('versions:migrate', () => { }); const { warn, log: logs } = await withLogCollector(async () => { - await migrate({ args: [], info: { usage: 'test', description: 'test' } }); + await migrate({ args: [], info: { usage: 'test', name: 'test' } }); }); expectLogsToMatch(logs, [ @@ -229,7 +229,7 @@ describe('versions:migrate', () => { }); await withLogCollector(async () => { - await migrate({ args: [], info: { usage: 'test', description: 'test' } }); + await migrate({ args: [], info: { usage: 'test', name: 'test' } }); }); expect(runObj.run).toHaveBeenCalledTimes(1); @@ -311,7 +311,7 @@ describe('versions:migrate', () => { }); await withLogCollector(async () => { - await migrate({ args: [], info: { usage: 'test', description: 'test' } }); + await migrate({ args: [], info: { usage: 'test', name: 'test' } }); }); expect(runObj.run).toHaveBeenCalledTimes(1); diff --git a/packages/cli/src/modules/migrate/index.ts b/packages/cli/src/modules/migrate/index.ts index 87c0543e90..603712f8e2 100644 --- a/packages/cli/src/modules/migrate/index.ts +++ b/packages/cli/src/modules/migrate/index.ts @@ -14,9 +14,10 @@ * limitations under the License. */ import { createCliPlugin } from '@backstage/cli-plugin-api'; +import packageJson from '../../../package.json'; export default createCliPlugin({ - pluginId: 'migrate', + packageJson, init: async reg => { reg.addCommand({ path: ['versions:migrate'], diff --git a/packages/cli/src/modules/new/commands/new.test.ts b/packages/cli/src/modules/new/commands/new.test.ts index 0da42e7019..9a490e8f9a 100644 --- a/packages/cli/src/modules/new/commands/new.test.ts +++ b/packages/cli/src/modules/new/commands/new.test.ts @@ -41,7 +41,7 @@ describe.each([ } const context: CommandContext = { args, - info: { usage: 'backstage-cli new', description: 'test' }, + info: { usage: 'backstage-cli new', name: 'new' }, }; await newCommand(context); expect(createNewPackage).toHaveBeenCalledWith( diff --git a/packages/cli/src/modules/new/index.ts b/packages/cli/src/modules/new/index.ts index 2d30fb45bc..076a49f9be 100644 --- a/packages/cli/src/modules/new/index.ts +++ b/packages/cli/src/modules/new/index.ts @@ -15,9 +15,10 @@ */ import { createCliPlugin } from '@backstage/cli-plugin-api'; import { NotImplementedError } from '@backstage/errors'; +import packageJson from '../../../package.json'; export default createCliPlugin({ - pluginId: 'new', + packageJson, init: async reg => { reg.addCommand({ path: ['new'], diff --git a/packages/cli/src/modules/test/index.ts b/packages/cli/src/modules/test/index.ts index f991d23ce2..596572cb9a 100644 --- a/packages/cli/src/modules/test/index.ts +++ b/packages/cli/src/modules/test/index.ts @@ -14,9 +14,10 @@ * limitations under the License. */ import { createCliPlugin } from '@backstage/cli-plugin-api'; +import packageJson from '../../../package.json'; export default createCliPlugin({ - pluginId: 'test', + packageJson, init: async reg => { reg.addCommand({ path: ['repo', 'test'], diff --git a/packages/cli/src/modules/translations/index.ts b/packages/cli/src/modules/translations/index.ts index 9a001bab23..16c19d9345 100644 --- a/packages/cli/src/modules/translations/index.ts +++ b/packages/cli/src/modules/translations/index.ts @@ -14,9 +14,10 @@ * limitations under the License. */ import { createCliPlugin } from '@backstage/cli-plugin-api'; +import packageJson from '../../../package.json'; export default createCliPlugin({ - pluginId: 'translations', + packageJson, init: async reg => { reg.addCommand({ path: ['translations', 'export'], diff --git a/packages/cli/src/wiring/CliInitializer.test.ts b/packages/cli/src/wiring/CliInitializer.test.ts index 10de6d8e09..c5d304db91 100644 --- a/packages/cli/src/wiring/CliInitializer.test.ts +++ b/packages/cli/src/wiring/CliInitializer.test.ts @@ -29,7 +29,7 @@ describe('CliInitializer', () => { const initializer = new CliInitializer(); initializer.add( createCliPlugin({ - pluginId: 'test', + packageJson: { name: '@backstage/test' }, init: async reg => reg.addCommand({ path: ['test'], @@ -51,7 +51,7 @@ describe('CliInitializer', () => { const initializer = new CliInitializer(); initializer.add( createCliPlugin({ - pluginId: 'test', + packageJson: { name: '@backstage/test' }, init: async reg => reg.addCommand({ path: ['test'], @@ -73,7 +73,7 @@ describe('CliInitializer', () => { const initializer = new CliInitializer(); initializer.add( createCliPlugin({ - pluginId: 'test', + packageJson: { name: '@backstage/test' }, init: async reg => reg.addCommand({ path: ['test'], @@ -98,7 +98,7 @@ describe('CliInitializer', () => { const initializer = new CliInitializer(); initializer.add( createCliPlugin({ - pluginId: 'test', + packageJson: { name: '@backstage/test' }, init: async reg => { reg.addCommand({ path: ['visible'], @@ -125,7 +125,7 @@ describe('CliInitializer', () => { const initializer2 = new CliInitializer(); initializer2.add( createCliPlugin({ - pluginId: 'test', + packageJson: { name: '@backstage/test' }, init: async reg => { reg.addCommand({ path: ['visible'], @@ -153,7 +153,7 @@ describe('CliInitializer', () => { const initializer = new CliInitializer(); initializer.add( createCliPlugin({ - pluginId: 'test', + packageJson: { name: '@backstage/test' }, init: async reg => { reg.addCommand({ path: ['visible'], @@ -188,7 +188,7 @@ describe('CliInitializer', () => { const initializer = new CliInitializer(); initializer.add( createCliPlugin({ - pluginId: 'test', + packageJson: { name: '@backstage/test' }, init: async reg => { reg.addCommand({ path: ['group', 'alpha'], @@ -224,7 +224,7 @@ describe('CliInitializer', () => { const initializer = new CliInitializer(); initializer.add( createCliPlugin({ - pluginId: 'test', + packageJson: { name: '@backstage/test' }, init: async reg => reg.addCommand({ path: ['test', 'nested', 'command'], diff --git a/packages/cli/src/wiring/CliInitializer.ts b/packages/cli/src/wiring/CliInitializer.ts index bd31930854..2d1f94ce79 100644 --- a/packages/cli/src/wiring/CliInitializer.ts +++ b/packages/cli/src/wiring/CliInitializer.ts @@ -56,7 +56,9 @@ export class CliInitializer { async #register(feature: CliFeature) { if (OpaqueCliPlugin.isType(feature)) { const internal = OpaqueCliPlugin.toInternal(feature); - await internal.init(this.commandRegistry); + for (const command of await internal.commands) { + this.commandRegistry.addCommand(command); + } } else { throw new Error(`Unsupported feature type: ${(feature as any).$$type}`); } @@ -138,7 +140,7 @@ export class CliInitializer { args: [...positionalArgs, ...args.unknown], info: { usage: [programName, ...node.command.path].join(' '), - description: node.command.description, + name: node.command.path.join(' '), }, }; diff --git a/packages/cli/src/wiring/types.ts b/packages/cli/src/wiring/types.ts index 4fd58a5c73..b635e48ac0 100644 --- a/packages/cli/src/wiring/types.ts +++ b/packages/cli/src/wiring/types.ts @@ -16,7 +16,6 @@ export type { CommandContext, - CommandExecuteFn, BackstageCommand, CliFeature, CliPlugin,