Add @backstage/cli-defaults package

Introduces a new `@backstage/cli-defaults` package that re-exports all
standard CLI modules as a single array, simplifying dependency management
for consumers. The CLI's `CliInitializer` is updated to support array
exports alongside single module exports. The create-app template,
changesets, and CLI fallback are updated to use `@backstage/cli-defaults`
instead of listing 11 individual modules.

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Made-with: Cursor
This commit is contained in:
Patrik Oldsberg
2026-03-15 15:01:35 +01:00
parent 7db7ca5714
commit 7781ae5911
14 changed files with 172 additions and 61 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli-defaults': minor
---
Introduced `@backstage/cli-defaults`, a convenience package that bundles all standard Backstage CLI modules. Install this single package as a `devDependency` to get the full default set of CLI commands without listing each module individually.
+17 -7
View File
@@ -4,7 +4,23 @@
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`:
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 `@backstage/cli-defaults` as a `devDependency` in your root `package.json`:
```json
{
"devDependencies": {
"@backstage/cli-defaults": "backstage:^"
}
}
```
If you are not using the Backstage Yarn plugin, run the following instead:
```sh
yarn workspace root add --dev @backstage/cli-defaults
```
For fine-grained control you can instead install individual CLI modules:
```json
{
@@ -23,9 +39,3 @@ If no CLI modules are found in the project dependencies, the CLI falls back to t
}
}
```
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
```
+1 -1
View File
@@ -2,4 +2,4 @@
'@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.
The create-app templates now include `@backstage/cli-defaults` as a `devDependency`, enabling the CLI's automatic module discovery for newly created projects.
+1
View File
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
+10
View File
@@ -0,0 +1,10 @@
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: backstage-cli-defaults
title: '@backstage/cli-defaults'
description: Default set of CLI modules for the Backstage CLI
spec:
lifecycle: experimental
type: backstage-cli-module
owner: tooling-maintainers
+48
View File
@@ -0,0 +1,48 @@
{
"name": "@backstage/cli-defaults",
"version": "0.0.0",
"description": "Default set of CLI modules for the Backstage CLI",
"backstage": {
"role": "cli-module"
},
"publishConfig": {
"access": "public",
"main": "dist/index.cjs.js",
"types": "dist/index.d.ts"
},
"homepage": "https://backstage.io",
"repository": {
"type": "git",
"url": "https://github.com/backstage/backstage",
"directory": "packages/cli-defaults"
},
"license": "Apache-2.0",
"main": "src/index.ts",
"types": "src/index.ts",
"files": [
"dist"
],
"scripts": {
"build": "backstage-cli package build",
"clean": "backstage-cli package clean",
"lint": "backstage-cli package lint",
"prepack": "backstage-cli package prepack",
"postpack": "backstage-cli package postpack"
},
"dependencies": {
"@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:^"
},
"devDependencies": {
"@backstage/cli": "workspace:^"
}
}
+40
View File
@@ -0,0 +1,40 @@
/*
* 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 auth from '@backstage/cli-module-auth';
import build from '@backstage/cli-module-build';
import config from '@backstage/cli-module-config';
import createGithubApp from '@backstage/cli-module-create-github-app';
import info from '@backstage/cli-module-info';
import lint from '@backstage/cli-module-lint';
import maintenance from '@backstage/cli-module-maintenance';
import migrate from '@backstage/cli-module-migrate';
import newModule from '@backstage/cli-module-new';
import testJest from '@backstage/cli-module-test-jest';
import translations from '@backstage/cli-module-translations';
export default [
auth,
build,
config,
createGithubApp,
info,
lint,
maintenance,
migrate,
newModule,
testJest,
translations,
];
+1 -11
View File
@@ -48,17 +48,7 @@
},
"dependencies": {
"@backstage/cli-common": "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/cli-defaults": "workspace:^",
"@backstage/cli-node": "workspace:^",
"@backstage/errors": "workspace:^",
"@backstage/eslint-plugin": "workspace:^",
+4 -13
View File
@@ -35,22 +35,13 @@ import { discoverCliModules } from './wiring/discoverCliModules';
`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`,
`Please add @backstage/cli-defaults as a devDependency ` +
`in your root package.json, or install individual ` +
`@backstage/cli-module-* packages for fine-grained control.\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'));
initializer.add(import('@backstage/cli-defaults'));
}
await initializer.run();
+21 -7
View File
@@ -39,18 +39,23 @@ function isNodeHidden(node: CommandNode): boolean {
return children.every(child => isNodeHidden(child));
}
type UninitializedFeature = CliModule | Promise<{ default: CliModule }>;
type UninitializedFeature =
| CliModule
| CliModule[]
| Promise<{ default: CliModule | CliModule[] }>;
export class CliInitializer {
private graph = new CommandGraph();
private commandRegistry = new CommandRegistry(this.graph);
#uninitiazedFeatures: Promise<CliModule>[] = [];
#uninitiazedFeatures: Promise<CliModule | CliModule[]>[] = [];
add(feature: UninitializedFeature) {
if (isPromise(feature)) {
this.#uninitiazedFeatures.push(
feature.then(f => unwrapFeature(f.default)),
);
} else if (Array.isArray(feature)) {
this.#uninitiazedFeatures.push(Promise.resolve(feature));
} else {
this.#uninitiazedFeatures.push(Promise.resolve(feature));
}
@@ -68,9 +73,14 @@ export class CliInitializer {
}
async #doInit() {
const features = await Promise.all(this.#uninitiazedFeatures);
for (const feature of features) {
await this.#register(feature);
const resolved = await Promise.all(this.#uninitiazedFeatures);
for (const featureOrArray of resolved) {
const features = Array.isArray(featureOrArray)
? featureOrArray
: [featureOrArray];
for (const feature of features) {
await this.#register(feature);
}
}
}
@@ -186,8 +196,12 @@ export class CliInitializer {
/** @internal */
export function unwrapFeature(
feature: CliModule | { default: CliModule },
): CliModule {
feature: CliModule | CliModule[] | { default: CliModule | CliModule[] },
): CliModule | CliModule[] {
if (Array.isArray(feature)) {
return feature;
}
if ('$$type' in feature) {
return feature;
}
@@ -50,6 +50,7 @@ jest.mock('./versions', () => ({
packageVersions: {
root: '1.2.3',
'@backstage/cli': '1.0.0',
'@backstage/cli-defaults': '1.0.0',
'@backstage/cli-module-auth': '1.0.0',
'@backstage/cli-module-build': '1.0.0',
'@backstage/cli-module-config': '1.0.0',
+2
View File
@@ -38,6 +38,7 @@ import { version as backendDefaults } from '../../../backend-defaults/package.js
import { version as catalogClient } from '../../../catalog-client/package.json';
import { version as catalogModel } from '../../../catalog-model/package.json';
import { version as cli } from '../../../cli/package.json';
import { version as cliDefaults } from '../../../cli-defaults/package.json';
import { version as cliModuleAuth } from '../../../cli-module-auth/package.json';
import { version as cliModuleBuild } from '../../../cli-module-build/package.json';
import { version as cliModuleConfig } from '../../../cli-module-config/package.json';
@@ -118,6 +119,7 @@ export const packageVersions = {
'@backstage/catalog-client': catalogClient,
'@backstage/catalog-model': catalogModel,
'@backstage/cli': cli,
'@backstage/cli-defaults': cliDefaults,
'@backstage/cli-module-auth': cliModuleAuth,
'@backstage/cli-module-build': cliModuleBuild,
'@backstage/cli-module-config': cliModuleConfig,
@@ -28,17 +28,7 @@
],
"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/cli-defaults": "^{{version '@backstage/cli-defaults'}}",
"@backstage/e2e-test-utils": "^{{version '@backstage/e2e-test-utils'}}",
"@jest/environment-jsdom-abstract": "^30.0.0",
"@playwright/test": "^1.32.3",
+20 -11
View File
@@ -2801,6 +2801,25 @@ __metadata:
languageName: unknown
linkType: soft
"@backstage/cli-defaults@workspace:^, @backstage/cli-defaults@workspace:packages/cli-defaults":
version: 0.0.0-use.local
resolution: "@backstage/cli-defaults@workspace:packages/cli-defaults"
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:^"
languageName: unknown
linkType: soft
"@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"
@@ -3121,17 +3140,7 @@ __metadata:
"@backstage/backend-test-utils": "workspace:^"
"@backstage/catalog-client": "workspace:^"
"@backstage/cli-common": "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/cli-defaults": "workspace:^"
"@backstage/cli-node": "workspace:^"
"@backstage/config": "workspace:^"
"@backstage/core-app-api": "workspace:^"