diff --git a/.changeset/cli-discover-modules.md b/.changeset/cli-discover-modules.md new file mode 100644 index 0000000000..428a6eb883 --- /dev/null +++ b/.changeset/cli-discover-modules.md @@ -0,0 +1,31 @@ +--- +'@backstage/cli': minor +--- + +The CLI now automatically discovers CLI modules from the project root's `dependencies` and `devDependencies`. Any installed package with the `cli-module` Backstage role will be loaded automatically without needing to be hardcoded in the CLI itself. + +If no CLI modules are found in the project dependencies, the CLI falls back to the built-in set of modules and prints a deprecation warning. This fallback will be removed in a future release. To prepare for this, add the following CLI modules as `devDependencies` in your root `package.json`: + +```json +{ + "devDependencies": { + "@backstage/cli-module-auth": "backstage:^", + "@backstage/cli-module-build": "backstage:^", + "@backstage/cli-module-config": "backstage:^", + "@backstage/cli-module-create-github-app": "backstage:^", + "@backstage/cli-module-info": "backstage:^", + "@backstage/cli-module-lint": "backstage:^", + "@backstage/cli-module-maintenance": "backstage:^", + "@backstage/cli-module-migrate": "backstage:^", + "@backstage/cli-module-new": "backstage:^", + "@backstage/cli-module-test-jest": "backstage:^", + "@backstage/cli-module-translations": "backstage:^" + } +} +``` + +If you are not using the Backstage Yarn plugin, run the following instead: + +```sh +yarn workspace root add --dev @backstage/cli-module-auth @backstage/cli-module-build @backstage/cli-module-config @backstage/cli-module-create-github-app @backstage/cli-module-info @backstage/cli-module-lint @backstage/cli-module-maintenance @backstage/cli-module-migrate @backstage/cli-module-new @backstage/cli-module-test-jest @backstage/cli-module-translations +``` diff --git a/.changeset/create-app-cli-modules.md b/.changeset/create-app-cli-modules.md new file mode 100644 index 0000000000..4e01b65260 --- /dev/null +++ b/.changeset/create-app-cli-modules.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +The create-app templates now include all standard `@backstage/cli-module-*` packages as `devDependencies`, enabling the CLI's automatic module discovery for newly created projects. diff --git a/package.json b/package.json index d94bdadcbd..962fe132d4 100644 --- a/package.json +++ b/package.json @@ -125,6 +125,17 @@ }, "devDependencies": { "@backstage/cli": "workspace:*", + "@backstage/cli-module-auth": "workspace:*", + "@backstage/cli-module-build": "workspace:*", + "@backstage/cli-module-config": "workspace:*", + "@backstage/cli-module-create-github-app": "workspace:*", + "@backstage/cli-module-info": "workspace:*", + "@backstage/cli-module-lint": "workspace:*", + "@backstage/cli-module-maintenance": "workspace:*", + "@backstage/cli-module-migrate": "workspace:*", + "@backstage/cli-module-new": "workspace:*", + "@backstage/cli-module-test-jest": "workspace:*", + "@backstage/cli-module-translations": "workspace:*", "@backstage/codemods": "workspace:*", "@backstage/create-app": "workspace:*", "@backstage/e2e-test-utils": "workspace:*", diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 8773c3b112..489287c3f2 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -14,20 +14,44 @@ * limitations under the License. */ +import chalk from 'chalk'; import { CliInitializer } from './wiring/CliInitializer'; +import { discoverCliModules } from './wiring/discoverCliModules'; (async () => { const initializer = new CliInitializer(); - initializer.add(import('@backstage/cli-module-build')); - initializer.add(import('@backstage/cli-module-config')); - initializer.add(import('@backstage/cli-module-create-github-app')); - initializer.add(import('@backstage/cli-module-info')); - initializer.add(import('@backstage/cli-module-lint')); - initializer.add(import('@backstage/cli-module-maintenance')); - initializer.add(import('@backstage/cli-module-migrate')); - initializer.add(import('@backstage/cli-module-new')); - initializer.add(import('@backstage/cli-module-test-jest')); - initializer.add(import('@backstage/cli-module-translations')); - initializer.add(import('@backstage/cli-module-auth')); + + const discoveredModules = discoverCliModules(); + + if (discoveredModules.length > 0) { + for (const moduleName of discoveredModules) { + initializer.add(import(moduleName)); + } + } else { + // No CLI modules found in the project root; fall back to the built-in + // set while printing a deprecation warning. + console.error( + chalk.yellow( + `No CLI modules found in the project root dependencies. ` + + `Falling back to the built-in set of modules.\n` + + `This fallback will be removed in a future release. ` + + `Please add the CLI modules you need as devDependencies ` + + `in your root package.json.\n`, + ), + ); + + initializer.add(import('@backstage/cli-module-build')); + initializer.add(import('@backstage/cli-module-config')); + initializer.add(import('@backstage/cli-module-create-github-app')); + initializer.add(import('@backstage/cli-module-info')); + initializer.add(import('@backstage/cli-module-lint')); + initializer.add(import('@backstage/cli-module-maintenance')); + initializer.add(import('@backstage/cli-module-migrate')); + initializer.add(import('@backstage/cli-module-new')); + initializer.add(import('@backstage/cli-module-test-jest')); + initializer.add(import('@backstage/cli-module-translations')); + initializer.add(import('@backstage/cli-module-auth')); + } + await initializer.run(); })(); diff --git a/packages/cli/src/wiring/discoverCliModules.ts b/packages/cli/src/wiring/discoverCliModules.ts new file mode 100644 index 0000000000..dac80c1344 --- /dev/null +++ b/packages/cli/src/wiring/discoverCliModules.ts @@ -0,0 +1,65 @@ +/* + * 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 { targetPaths } from '@backstage/cli-common'; +import { PackageRoles } from '@backstage/cli-node'; +import fs from 'node:fs'; +import { resolve as resolvePath } from 'node:path'; + +/** + * Scans the target project root's package.json for dependencies that are CLI + * modules (packages with `backstage.role === 'cli-module'`). + * + * Returns the names of discovered CLI module packages, or an empty array if + * none are found or the project root cannot be read. + */ +export function discoverCliModules(): string[] { + const rootDir = targetPaths.rootDir; + const pkgJsonPath = resolvePath(rootDir, 'package.json'); + + let projectPkg: { + dependencies?: Record; + devDependencies?: Record; + }; + try { + projectPkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8')); + } catch { + return []; + } + + const allDeps = { + ...projectPkg.dependencies, + ...projectPkg.devDependencies, + }; + + const modules: string[] = []; + + for (const depName of Object.keys(allDeps)) { + try { + const depPkgPath = require.resolve(`${depName}/package.json`, { + paths: [rootDir], + }); + const depPkg = JSON.parse(fs.readFileSync(depPkgPath, 'utf8')); + if (PackageRoles.getRoleFromPackage(depPkg) === 'cli-module') { + modules.push(depName); + } + } catch { + // Skip packages that can't be resolved or read + } + } + + return modules; +} diff --git a/packages/create-app/templates/default-app/package.json.hbs b/packages/create-app/templates/default-app/package.json.hbs index 229ed5ffa0..ca29f02dc0 100644 --- a/packages/create-app/templates/default-app/package.json.hbs +++ b/packages/create-app/templates/default-app/package.json.hbs @@ -28,6 +28,17 @@ ], "devDependencies": { "@backstage/cli": "^{{version '@backstage/cli'}}", + "@backstage/cli-module-auth": "^{{version '@backstage/cli-module-auth'}}", + "@backstage/cli-module-build": "^{{version '@backstage/cli-module-build'}}", + "@backstage/cli-module-config": "^{{version '@backstage/cli-module-config'}}", + "@backstage/cli-module-create-github-app": "^{{version '@backstage/cli-module-create-github-app'}}", + "@backstage/cli-module-info": "^{{version '@backstage/cli-module-info'}}", + "@backstage/cli-module-lint": "^{{version '@backstage/cli-module-lint'}}", + "@backstage/cli-module-maintenance": "^{{version '@backstage/cli-module-maintenance'}}", + "@backstage/cli-module-migrate": "^{{version '@backstage/cli-module-migrate'}}", + "@backstage/cli-module-new": "^{{version '@backstage/cli-module-new'}}", + "@backstage/cli-module-test-jest": "^{{version '@backstage/cli-module-test-jest'}}", + "@backstage/cli-module-translations": "^{{version '@backstage/cli-module-translations'}}", "@backstage/e2e-test-utils": "^{{version '@backstage/e2e-test-utils'}}", "@jest/environment-jsdom-abstract": "^30.0.0", "@playwright/test": "^1.32.3", diff --git a/packages/create-app/templates/next-app/package.json.hbs b/packages/create-app/templates/next-app/package.json.hbs index 27d5880c11..110ebc160c 100644 --- a/packages/create-app/templates/next-app/package.json.hbs +++ b/packages/create-app/templates/next-app/package.json.hbs @@ -50,6 +50,17 @@ ], "devDependencies": { "@backstage/cli": "^{{version '@backstage/cli'}}", + "@backstage/cli-module-auth": "^{{version '@backstage/cli-module-auth'}}", + "@backstage/cli-module-build": "^{{version '@backstage/cli-module-build'}}", + "@backstage/cli-module-config": "^{{version '@backstage/cli-module-config'}}", + "@backstage/cli-module-create-github-app": "^{{version '@backstage/cli-module-create-github-app'}}", + "@backstage/cli-module-info": "^{{version '@backstage/cli-module-info'}}", + "@backstage/cli-module-lint": "^{{version '@backstage/cli-module-lint'}}", + "@backstage/cli-module-maintenance": "^{{version '@backstage/cli-module-maintenance'}}", + "@backstage/cli-module-migrate": "^{{version '@backstage/cli-module-migrate'}}", + "@backstage/cli-module-new": "^{{version '@backstage/cli-module-new'}}", + "@backstage/cli-module-test-jest": "^{{version '@backstage/cli-module-test-jest'}}", + "@backstage/cli-module-translations": "^{{version '@backstage/cli-module-translations'}}", "@backstage/e2e-test-utils": "^{{version '@backstage/e2e-test-utils'}}", "@jest/environment-jsdom-abstract": "^30.0.0", "@playwright/test": "^1.32.3", diff --git a/yarn.lock b/yarn.lock index d0f471e49a..a5abf933a3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2801,7 +2801,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/cli-module-auth@workspace:^, @backstage/cli-module-auth@workspace:packages/cli-module-auth": +"@backstage/cli-module-auth@workspace:*, @backstage/cli-module-auth@workspace:^, @backstage/cli-module-auth@workspace:packages/cli-module-auth": version: 0.0.0-use.local resolution: "@backstage/cli-module-auth@workspace:packages/cli-module-auth" dependencies: @@ -2824,7 +2824,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/cli-module-build@workspace:^, @backstage/cli-module-build@workspace:packages/cli-module-build": +"@backstage/cli-module-build@workspace:*, @backstage/cli-module-build@workspace:^, @backstage/cli-module-build@workspace:packages/cli-module-build": version: 0.0.0-use.local resolution: "@backstage/cli-module-build@workspace:packages/cli-module-build" dependencies: @@ -2888,7 +2888,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/cli-module-config@workspace:^, @backstage/cli-module-config@workspace:packages/cli-module-config": +"@backstage/cli-module-config@workspace:*, @backstage/cli-module-config@workspace:^, @backstage/cli-module-config@workspace:packages/cli-module-config": version: 0.0.0-use.local resolution: "@backstage/cli-module-config@workspace:packages/cli-module-config" dependencies: @@ -2910,7 +2910,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/cli-module-create-github-app@workspace:^, @backstage/cli-module-create-github-app@workspace:packages/cli-module-create-github-app": +"@backstage/cli-module-create-github-app@workspace:*, @backstage/cli-module-create-github-app@workspace:^, @backstage/cli-module-create-github-app@workspace:packages/cli-module-create-github-app": version: 0.0.0-use.local resolution: "@backstage/cli-module-create-github-app@workspace:packages/cli-module-create-github-app" dependencies: @@ -2932,7 +2932,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/cli-module-info@workspace:^, @backstage/cli-module-info@workspace:packages/cli-module-info": +"@backstage/cli-module-info@workspace:*, @backstage/cli-module-info@workspace:^, @backstage/cli-module-info@workspace:packages/cli-module-info": version: 0.0.0-use.local resolution: "@backstage/cli-module-info@workspace:packages/cli-module-info" dependencies: @@ -2948,7 +2948,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/cli-module-lint@workspace:^, @backstage/cli-module-lint@workspace:packages/cli-module-lint": +"@backstage/cli-module-lint@workspace:*, @backstage/cli-module-lint@workspace:^, @backstage/cli-module-lint@workspace:packages/cli-module-lint": version: 0.0.0-use.local resolution: "@backstage/cli-module-lint@workspace:packages/cli-module-lint" dependencies: @@ -2968,7 +2968,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/cli-module-maintenance@workspace:^, @backstage/cli-module-maintenance@workspace:packages/cli-module-maintenance": +"@backstage/cli-module-maintenance@workspace:*, @backstage/cli-module-maintenance@workspace:^, @backstage/cli-module-maintenance@workspace:packages/cli-module-maintenance": version: 0.0.0-use.local resolution: "@backstage/cli-module-maintenance@workspace:packages/cli-module-maintenance" dependencies: @@ -2985,7 +2985,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/cli-module-migrate@workspace:^, @backstage/cli-module-migrate@workspace:packages/cli-module-migrate": +"@backstage/cli-module-migrate@workspace:*, @backstage/cli-module-migrate@workspace:^, @backstage/cli-module-migrate@workspace:packages/cli-module-migrate": version: 0.0.0-use.local resolution: "@backstage/cli-module-migrate@workspace:packages/cli-module-migrate" dependencies: @@ -3005,7 +3005,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/cli-module-new@workspace:^, @backstage/cli-module-new@workspace:packages/cli-module-new": +"@backstage/cli-module-new@workspace:*, @backstage/cli-module-new@workspace:^, @backstage/cli-module-new@workspace:packages/cli-module-new": version: 0.0.0-use.local resolution: "@backstage/cli-module-new@workspace:packages/cli-module-new" dependencies: @@ -3016,7 +3016,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/cli-module-test-jest@workspace:^, @backstage/cli-module-test-jest@workspace:packages/cli-module-test-jest": +"@backstage/cli-module-test-jest@workspace:*, @backstage/cli-module-test-jest@workspace:^, @backstage/cli-module-test-jest@workspace:packages/cli-module-test-jest": version: 0.0.0-use.local resolution: "@backstage/cli-module-test-jest@workspace:packages/cli-module-test-jest" dependencies: @@ -3027,7 +3027,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/cli-module-translations@workspace:^, @backstage/cli-module-translations@workspace:packages/cli-module-translations": +"@backstage/cli-module-translations@workspace:*, @backstage/cli-module-translations@workspace:^, @backstage/cli-module-translations@workspace:packages/cli-module-translations": version: 0.0.0-use.local resolution: "@backstage/cli-module-translations@workspace:packages/cli-module-translations" dependencies: @@ -45616,6 +45616,17 @@ __metadata: resolution: "root@workspace:." dependencies: "@backstage/cli": "workspace:*" + "@backstage/cli-module-auth": "workspace:*" + "@backstage/cli-module-build": "workspace:*" + "@backstage/cli-module-config": "workspace:*" + "@backstage/cli-module-create-github-app": "workspace:*" + "@backstage/cli-module-info": "workspace:*" + "@backstage/cli-module-lint": "workspace:*" + "@backstage/cli-module-maintenance": "workspace:*" + "@backstage/cli-module-migrate": "workspace:*" + "@backstage/cli-module-new": "workspace:*" + "@backstage/cli-module-test-jest": "workspace:*" + "@backstage/cli-module-translations": "workspace:*" "@backstage/codemods": "workspace:*" "@backstage/create-app": "workspace:*" "@backstage/e2e-test-utils": "workspace:*"