Merge pull request #33355 from backstage/rugvip/cli-plugin-api

cli: add CLI module plugin API and auto-discovery
This commit is contained in:
Patrik Oldsberg
2026-03-16 14:50:27 +01:00
committed by GitHub
369 changed files with 5707 additions and 1824 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli-node': patch
---
Added a new `cli-module` package role for packages that provide CLI plugin extensions.
+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.
+41
View File
@@ -0,0 +1,41 @@
---
'@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 `@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
{
"devDependencies": {
"@backstage/cli-module-auth": "backstage:^",
"@backstage/cli-module-build": "backstage:^",
"@backstage/cli-module-config": "backstage:^",
"@backstage/cli-module-github": "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:^"
}
}
```
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli-node': minor
---
Added `createCliModule` API and related types for building Backstage CLI plugins.
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
Added support for the new `cli-module` package role in the build system, ESLint configuration, Jest configuration, and maintenance commands.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
Migrated CLI plugin modules to use `createCliModule` from `@backstage/cli-node`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/create-app': patch
---
The create-app templates now include `@backstage/cli-defaults` as a `devDependency`, enabling the CLI's automatic module discovery for newly created projects.
+15
View File
@@ -0,0 +1,15 @@
---
'@backstage/cli-module-auth': minor
'@backstage/cli-module-build': minor
'@backstage/cli-module-config': minor
'@backstage/cli-module-github': minor
'@backstage/cli-module-info': minor
'@backstage/cli-module-lint': minor
'@backstage/cli-module-maintenance': minor
'@backstage/cli-module-migrate': minor
'@backstage/cli-module-new': minor
'@backstage/cli-module-test-jest': minor
'@backstage/cli-module-translations': minor
---
Initial release of the CLI module packages. Each module provides a set of commands that can be discovered automatically by `@backstage/cli` or executed standalone.
+4 -3
View File
@@ -21,8 +21,6 @@
"plugins/*"
],
"scripts": {
"build-storybook": "storybook build --output-dir dist-storybook",
"build-storybook:chromatic": "STORYBOOK_STORY_SET=chromatic storybook build --stats-json --output-dir dist-storybook",
"build:all": "backstage-cli repo build --all",
"build:api-docs": "LANG=en_EN yarn build:api-reports --docs --exclude 'plugins/@(api-docs|api-docs-module-protoc-gen-doc|app-visualizer|catalog-graph|catalog-import|catalog-unprocessed-entities|config-schema|example-todo-list|example-todo-list-backend)'",
"build:api-reports": "yarn build:api-reports:only --tsc",
@@ -30,6 +28,8 @@
"build:backend": "yarn workspace example-backend build",
"build:knip-reports": "backstage-repo-tools knip-reports",
"build:plugins-report": "node ./scripts/build-plugins-report",
"build-storybook": "storybook build --output-dir dist-storybook",
"build-storybook:chromatic": "STORYBOOK_STORY_SET=chromatic storybook build --stats-json --output-dir dist-storybook",
"clean": "backstage-cli repo clean",
"create-plugin": "echo \"use 'yarn new' instead\"",
"dev": "echo \"use 'yarn start' instead\"",
@@ -52,10 +52,10 @@
"snyk:test": "npx snyk test --yarn-workspaces --strict-out-of-sync=false",
"snyk:test:package": "yarn snyk:test --include",
"start": "backstage-cli repo start",
"start-backend": "echo \"Use 'yarn start example-backend' instead\"",
"start:docker": "docker compose -f docker-compose.deps.yml up --wait && BACKSTAGE_ENV=docker yarn start",
"start:legacy": "yarn start example-app-legacy example-backend",
"start:microsite": "cd microsite/ && yarn start",
"start-backend": "echo \"Use 'yarn start example-backend' instead\"",
"storybook": "storybook dev -p 6006",
"sync-issue-templates": "node ./.github/ISSUE_TEMPLATE/sync.js",
"techdocs-cli": "node scripts/techdocs-cli.js",
@@ -125,6 +125,7 @@
},
"devDependencies": {
"@backstage/cli": "workspace:*",
"@backstage/cli-defaults": "workspace:*",
"@backstage/codemods": "workspace:*",
"@backstage/create-app": "workspace:*",
"@backstage/e2e-test-utils": "workspace:*",
+1
View File
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
+26
View File
@@ -0,0 +1,26 @@
# @backstage/cli-defaults
The default set of CLI modules for the Backstage CLI. Installing this single package provides all standard CLI commands without needing to list each module individually.
## Included Modules
| Module | Description |
| :----------------------------------------------------------------- | :--------------------------------------- |
| [`@backstage/cli-module-auth`](../cli-module-auth) | Authentication commands |
| [`@backstage/cli-module-build`](../cli-module-build) | Build, start, and packaging commands |
| [`@backstage/cli-module-config`](../cli-module-config) | Configuration inspection commands |
| [`@backstage/cli-module-github`](../cli-module-github) | GitHub App creation |
| [`@backstage/cli-module-info`](../cli-module-info) | Environment and dependency info |
| [`@backstage/cli-module-lint`](../cli-module-lint) | Linting commands |
| [`@backstage/cli-module-maintenance`](../cli-module-maintenance) | Repository maintenance commands |
| [`@backstage/cli-module-migrate`](../cli-module-migrate) | Migration and version management |
| [`@backstage/cli-module-new`](../cli-module-new) | Scaffolding for new plugins and packages |
| [`@backstage/cli-module-test-jest`](../cli-module-test-jest) | Jest-based testing commands |
| [`@backstage/cli-module-translations`](../cli-module-translations) | Translation management commands |
For fine-grained control over which CLI commands are available, you can install individual modules instead.
## Documentation
- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md)
- [Backstage Documentation](https://backstage.io/docs)
+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-github": "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:^"
}
}
+13
View File
@@ -0,0 +1,13 @@
## API Report File for "@backstage/cli-defaults"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { CliModule } from '@backstage/cli-node';
// @public
const _default: CliModule[];
export default _default;
// (No @packageDocumentation comment for this package)
```
+45
View File
@@ -0,0 +1,45 @@
/*
* 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 github from '@backstage/cli-module-github';
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';
/**
* The default set of CLI modules for the Backstage CLI.
*
* @public
*/
export default [
auth,
build,
config,
github,
info,
lint,
maintenance,
migrate,
newModule,
testJest,
translations,
];
+1
View File
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
+9
View File
@@ -0,0 +1,9 @@
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: internal-cli
title: '@internal/cli'
spec:
lifecycle: experimental
type: backstage-node-library
owner: tooling-maintainers
+30
View File
@@ -0,0 +1,30 @@
{
"name": "@internal/cli",
"version": "0.0.1",
"backstage": {
"role": "node-library",
"inline": true
},
"private": true,
"repository": {
"type": "git",
"url": "https://github.com/backstage/backstage",
"directory": "packages/cli-internal"
},
"license": "Apache-2.0",
"main": "src/index.ts",
"types": "src/index.ts",
"files": [
"dist"
],
"scripts": {
"lint": "backstage-cli package lint",
"test": "backstage-cli package test"
},
"dependencies": {
"@backstage/cli-node": "workspace:^"
},
"devDependencies": {
"@backstage/cli": "workspace:^"
}
}
@@ -0,0 +1,30 @@
/*
* 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 { CliCommand, CliModule } from '@backstage/cli-node';
import { OpaqueType } from '@internal/opaque';
export const OpaqueCliModule = OpaqueType.create<{
public: CliModule;
versions: {
readonly version: 'v1';
readonly packageName: string;
readonly commands: Promise<ReadonlyArray<CliCommand>>;
};
}>({
type: '@backstage/CliModule',
versions: ['v1'],
});
@@ -0,0 +1,69 @@
/*
* 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 { CliCommand, CliModule } from '@backstage/cli-node';
import { OpaqueType } from '@internal/opaque';
/** @internal */
export interface CommandTreeNode {
readonly $$type: '@backstage/CommandTreeNode';
}
/** @internal */
export interface CommandLeafNode {
readonly $$type: '@backstage/CommandLeafNode';
}
export type CommandNode = CommandTreeNode | CommandLeafNode;
export const OpaqueCommandTreeNode = OpaqueType.create<{
public: CommandTreeNode;
versions: {
readonly version: 'v1';
readonly name: string;
readonly children: CommandNode[];
};
}>({
type: '@backstage/CommandTreeNode',
versions: ['v1'],
});
export const OpaqueCommandLeafNode = OpaqueType.create<{
public: CommandLeafNode;
versions: {
readonly version: 'v1';
readonly name: string;
readonly command: CliCommand;
readonly module?: CliModule;
};
}>({
type: '@backstage/CommandLeafNode',
versions: ['v1'],
});
/**
* Checks whether a command node should be hidden from help output.
* Leaf nodes are hidden if they are deprecated or experimental.
* Tree nodes are hidden if all of their children are hidden.
*/
export function isCommandNodeHidden(node: CommandNode): boolean {
if (OpaqueCommandLeafNode.isType(node)) {
const { command } = OpaqueCommandLeafNode.toInternal(node);
return !!command.deprecated || !!command.experimental;
}
const { children } = OpaqueCommandTreeNode.toInternal(node);
return children.every(child => isCommandNodeHidden(child));
}
@@ -1,5 +1,5 @@
/*
* Copyright 2023 The Backstage Authors
* 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.
@@ -14,7 +14,14 @@
* limitations under the License.
*/
// Single re-export to avoid doing this import in multiple places, but still
// avoid duplicate declarations because this one is a bit tricky.
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
export { describeParentCallSite } from '../../../frontend-plugin-api/src/routing/describeParentCallSite';
export { OpaqueCliModule } from './InternalCliModule';
export type {
CommandNode,
CommandTreeNode,
CommandLeafNode,
} from './InternalCommandNode';
export {
OpaqueCommandTreeNode,
OpaqueCommandLeafNode,
isCommandNodeHidden,
} from './InternalCommandNode';
+1
View File
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
+19
View File
@@ -0,0 +1,19 @@
# @backstage/cli-module-auth
CLI module that provides authentication commands for the Backstage CLI, enabling login, logout, and credential management for Backstage instances.
## Commands
| Command | Description |
| :----------------- | :------------------------------------------------------- |
| `auth login` | Log in the CLI to a Backstage instance |
| `auth logout` | Log out the CLI and clear stored credentials |
| `auth show` | Show details of an authenticated instance |
| `auth list` | List authenticated instances |
| `auth print-token` | Print an access token to stdout (auto-refresh if needed) |
| `auth select` | Select the default instance |
## Documentation
- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md)
- [Backstage Documentation](https://backstage.io/docs)
+32
View File
@@ -0,0 +1,32 @@
#!/usr/bin/env node
/*
* 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.
*/
const path = require('node:path');
/* eslint-disable-next-line no-restricted-syntax */
const isLocal = require('node:fs').existsSync(
path.resolve(__dirname, '../src'),
);
if (isLocal) {
require('@backstage/cli-node/config/nodeTransform.cjs');
}
const { runCliModule } = require('@backstage/cli-node');
const cliModule = require(isLocal ? '../src/index' : '..').default;
const pkg = require('../package.json');
runCliModule({ module: cliModule, name: pkg.name, version: pkg.version });
@@ -0,0 +1,10 @@
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: backstage-cli-module-auth
title: '@backstage/cli-module-auth'
description: CLI module for Backstage CLI
spec:
lifecycle: experimental
type: backstage-cli-module
owner: tooling-maintainers
+96
View File
@@ -0,0 +1,96 @@
## CLI Report file for "@backstage/cli-module-auth"
> Do not edit this file. It is a report generated by `yarn build:api-reports`
### `backstage-cli-module-auth`
```
Usage: @backstage/cli-module-auth [options] [command]
Options:
-V, --version
-h, --help
Commands:
auth [command]
help [command]
```
### `backstage-cli-module-auth auth`
```
Usage: @backstage/cli-module-auth auth [options] [command] [command]
Options:
-h, --help
Commands:
help [command]
list
login
logout
print-token
select
show
```
### `backstage-cli-module-auth auth list`
```
Usage: @backstage/cli-module-auth auth list
Options:
-h, --help
```
### `backstage-cli-module-auth auth login`
```
Usage: @backstage/cli-module-auth auth login
Options:
--backend-url <string>
--instance <string>
--no-browser
-h, --help
```
### `backstage-cli-module-auth auth logout`
```
Usage: @backstage/cli-module-auth auth logout
Options:
--instance <string>
-h, --help
```
### `backstage-cli-module-auth auth print-token`
```
Usage: @backstage/cli-module-auth auth print-token
Options:
--instance <string>
-h, --help
```
### `backstage-cli-module-auth auth select`
```
Usage: @backstage/cli-module-auth auth select
Options:
--instance <string>
-h, --help
```
### `backstage-cli-module-auth auth show`
```
Usage: @backstage/cli-module-auth auth show
Options:
--instance <string>
-h, --help
```
+56
View File
@@ -0,0 +1,56 @@
{
"name": "@backstage/cli-module-auth",
"version": "0.0.0",
"description": "CLI module for 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-module-auth"
},
"license": "Apache-2.0",
"main": "src/index.ts",
"types": "src/index.ts",
"files": [
"dist",
"bin"
],
"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",
"test": "backstage-cli package test"
},
"dependencies": {
"@backstage/cli-node": "workspace:^",
"@backstage/errors": "workspace:^",
"cleye": "^2.3.0",
"cross-fetch": "^4.0.0",
"fs-extra": "^11.2.0",
"glob": "^7.1.7",
"inquirer": "^8.2.0",
"proper-lockfile": "^4.1.2",
"yaml": "^2.0.0",
"zod": "^3.25.76"
},
"devDependencies": {
"@backstage/backend-test-utils": "workspace:^",
"@backstage/cli": "workspace:^",
"@types/fs-extra": "^11.0.0",
"@types/proper-lockfile": "^4"
},
"optionalDependencies": {
"keytar": "^7.9.0"
},
"bin": "bin/backstage-cli-module-auth"
}
+13
View File
@@ -0,0 +1,13 @@
## API Report File for "@backstage/cli-module-auth"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { CliModule } from '@backstage/cli-node';
// @public (undocumented)
const _default: CliModule;
export default _default;
// (No @packageDocumentation comment for this package)
```
@@ -15,10 +15,10 @@
*/
import { cli } from 'cleye';
import type { CommandContext } from '../../../wiring/types';
import type { CliCommandContext } from '@backstage/cli-node';
import { getAllInstances } from '../lib/storage';
export default async ({ args, info }: CommandContext) => {
export default async ({ args, info }: CliCommandContext) => {
cli({ help: info }, undefined, args);
const { instances, selected } = await getAllInstances();
@@ -15,7 +15,7 @@
*/
import { cli } from 'cleye';
import type { CommandContext } from '../../../wiring/types';
import type { CliCommandContext } from '@backstage/cli-node';
import { startCallbackServer } from '../lib/localServer';
import { spawn } from 'node:child_process';
import { challengeFromVerifier, generateVerifier } from '../lib/pkce';
@@ -37,7 +37,7 @@ import inquirer from 'inquirer';
const TOKEN_EXCHANGE_TIMEOUT_MS = 30_000;
export default async ({ args, info }: CommandContext) => {
export default async ({ args, info }: CliCommandContext) => {
const {
flags: { backendUrl, noBrowser, instance: instanceFlag },
} = cli(
@@ -15,7 +15,7 @@
*/
import { cli } from 'cleye';
import type { CommandContext } from '../../../wiring/types';
import type { CliCommandContext } from '@backstage/cli-node';
import { getSecretStore } from '../lib/secretStore';
import {
removeInstance,
@@ -25,7 +25,7 @@ import {
import { httpJson } from '../lib/http';
import { pickInstance } from '../lib/prompt';
export default async ({ args, info }: CommandContext) => {
export default async ({ args, info }: CliCommandContext) => {
const {
flags: { instance: instanceFlag },
} = cli(
@@ -15,12 +15,12 @@
*/
import { cli } from 'cleye';
import type { CommandContext } from '../../../wiring/types';
import type { CliCommandContext } from '@backstage/cli-node';
import { accessTokenNeedsRefresh, refreshAccessToken } from '../lib/auth';
import { getSelectedInstance } from '../lib/storage';
import { getSecretStore } from '../lib/secretStore';
export default async ({ args, info }: CommandContext) => {
export default async ({ args, info }: CliCommandContext) => {
const {
flags: { instance: instanceFlag },
} = cli(
@@ -15,11 +15,11 @@
*/
import { cli } from 'cleye';
import type { CommandContext } from '../../../wiring/types';
import type { CliCommandContext } from '@backstage/cli-node';
import { setSelectedInstance } from '../lib/storage';
import { pickInstance } from '../lib/prompt';
export default async ({ args, info }: CommandContext) => {
export default async ({ args, info }: CliCommandContext) => {
const {
flags: { instance: instanceFlag },
} = cli(
@@ -15,13 +15,13 @@
*/
import { cli } from 'cleye';
import type { CommandContext } from '../../../wiring/types';
import type { CliCommandContext } from '@backstage/cli-node';
import { httpJson } from '../lib/http';
import { getSelectedInstance } from '../lib/storage';
import { accessTokenNeedsRefresh, refreshAccessToken } from '../lib/auth';
import { getSecretStore } from '../lib/secretStore';
export default async ({ args, info }: CommandContext) => {
export default async ({ args, info }: CliCommandContext) => {
const {
flags: { instance: instanceFlag },
} = cli(
@@ -14,10 +14,11 @@
* limitations under the License.
*/
import { createCliPlugin } from '../../wiring/factory';
import { createCliModule } from '@backstage/cli-node';
import packageJson from '../package.json';
export default createCliPlugin({
pluginId: 'auth',
export default createCliModule({
packageJson,
init: async reg => {
reg.addCommand({
path: ['auth', 'login'],
+3
View File
@@ -0,0 +1,3 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, {
ignorePatterns: ['config/**', 'templates/**'],
});
+23
View File
@@ -0,0 +1,23 @@
# @backstage/cli-module-build
CLI module that provides build, start, and packaging commands for the Backstage CLI.
## Commands
| Command | Description |
| :----------------- | :------------------------------------------------------------------------ |
| `package build` | Build a package for production deployment or publishing |
| `package start` | Start a package for local development |
| `package clean` | Delete cache directories |
| `package prepack` | Prepares a package for packaging before publishing |
| `package postpack` | Restores the changes made by the prepack command |
| `repo build` | Build packages in the project, excluding bundled app and backend packages |
| `repo start` | Starts packages in the repo for local development |
| `repo clean` | Delete cache and output directories |
| `build-workspace` | Builds a temporary dist workspace from the provided packages |
## Documentation
- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md)
- [Backstage Documentation](https://backstage.io/docs)
- [Build System](https://backstage.io/docs/tooling/cli/build-system)
+32
View File
@@ -0,0 +1,32 @@
#!/usr/bin/env node
/*
* 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.
*/
const path = require('node:path');
/* eslint-disable-next-line no-restricted-syntax */
const isLocal = require('node:fs').existsSync(
path.resolve(__dirname, '../src'),
);
if (isLocal) {
require('@backstage/cli-node/config/nodeTransform.cjs');
}
const { runCliModule } = require('@backstage/cli-node');
const cliModule = require(isLocal ? '../src/index' : '..').default;
const pkg = require('../package.json');
runCliModule({ module: cliModule, name: pkg.name, version: pkg.version });
@@ -0,0 +1,10 @@
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: backstage-cli-module-build
title: '@backstage/cli-module-build'
description: CLI module for Backstage CLI
spec:
lifecycle: experimental
type: backstage-cli-module
owner: tooling-maintainers
+156
View File
@@ -0,0 +1,156 @@
## CLI Report file for "@backstage/cli-module-build"
> Do not edit this file. It is a report generated by `yarn build:api-reports`
### `backstage-cli-module-build`
```
Usage: @backstage/cli-module-build [options] [command]
Options:
-V, --version
-h, --help
Commands:
build-workspace
help [command]
package [command]
repo [command]
```
### `backstage-cli-module-build build-workspace`
```
Usage: @backstage/cli-module-build build-workspace <workspace-dir> [packages...]
Options:
--always-pack
-h, --help
```
### `backstage-cli-module-build package`
```
Usage: @backstage/cli-module-build package [options] [command] [command]
Options:
-h, --help
Commands:
build
clean
help [command]
postpack
prepack
start
```
### `backstage-cli-module-build package build`
```
Usage: @backstage/cli-module-build package build
Options:
--config <string>
--minify
--module-federation
--role <string>
--skip-build-dependencies
--stats
-h, --help
```
### `backstage-cli-module-build package clean`
```
Usage: @backstage/cli-module-build package clean
Options:
-h, --help
```
### `backstage-cli-module-build package postpack`
```
Usage: @backstage/cli-module-build package postpack
Options:
-h, --help
```
### `backstage-cli-module-build package prepack`
```
Usage: @backstage/cli-module-build package prepack
Options:
-h, --help
```
### `backstage-cli-module-build package start`
```
Usage: @backstage/cli-module-build package start
Options:
--check
--config <string>
--entrypoint <string>
--inspect <string>
--inspect-brk <string>
--link <string>
--require <string>
--role <string>
-h, --help
```
### `backstage-cli-module-build repo`
```
Usage: @backstage/cli-module-build repo [options] [command] [command]
Options:
-h, --help
Commands:
build
clean
help [command]
start
```
### `backstage-cli-module-build repo build`
```
Usage: @backstage/cli-module-build repo build
Options:
--all
--minify
--since <string>
-h, --help
```
### `backstage-cli-module-build repo clean`
```
Usage: @backstage/cli-module-build repo clean
Options:
-h, --help
```
### `backstage-cli-module-build repo start`
```
Usage: @backstage/cli-module-build repo start [packages...]
Options:
--config <string>
--inspect <string>
--inspect-brk <string>
--link <string>
--plugin <string>
--require <string>
-h, --help
```
@@ -0,0 +1,31 @@
/*
* 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.
*/
// This script is used to pick up and set the public path of the Webpack bundle
// at runtime. The meta tag is injected by the app build, but only present in
// the `index.html.tmpl` file. The runtime value of the meta tag is populated by
// the app backend, when it templates the final `index.html` file.
//
// This is needed for additional chunks to use the correct public path, and it
// is not possible to set the `__webpack_public_path__` variable outside of the
// build itself. The Webpack output also does not read any <base> tags or
// similar, this seems to be the only way to dynamically configure the public
// path at runtime.
const el = document.querySelector('meta[name="backstage-public-path"]');
const path = el?.getAttribute('content');
if (path) {
__webpack_public_path__ = path;
}
+106
View File
@@ -0,0 +1,106 @@
{
"name": "@backstage/cli-module-build",
"version": "0.0.0",
"description": "CLI module for 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-module-build"
},
"license": "Apache-2.0",
"main": "src/index.ts",
"types": "src/index.ts",
"bin": "bin/backstage-cli-module-build",
"files": [
"dist",
"bin",
"config",
"templates"
],
"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",
"test": "backstage-cli package test"
},
"dependencies": {
"@backstage/cli-common": "workspace:^",
"@backstage/cli-node": "workspace:^",
"@backstage/config": "workspace:^",
"@backstage/config-loader": "workspace:^",
"@backstage/errors": "workspace:^",
"@backstage/module-federation-common": "workspace:^",
"@manypkg/get-packages": "^1.1.3",
"@module-federation/enhanced": "^0.21.6",
"@pmmmwh/react-refresh-webpack-plugin": "^0.6.0",
"@rollup/plugin-commonjs": "^26.0.0",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-node-resolve": "^15.0.0",
"@rollup/plugin-yaml": "^4.0.0",
"@rspack/core": "^1.4.11",
"@rspack/dev-server": "^1.1.4",
"@rspack/plugin-react-refresh": "^1.4.3",
"@swc/core": "^1.15.6",
"bfj": "^9.0.2",
"buffer": "^6.0.3",
"chalk": "^4.0.0",
"chokidar": "^3.3.1",
"cleye": "^2.3.0",
"cross-spawn": "^7.0.3",
"css-loader": "^6.5.1",
"ctrlc-windows": "^2.1.0",
"esbuild-loader": "^4.0.0",
"eslint-rspack-plugin": "^4.2.1",
"eslint-webpack-plugin": "^4.2.0",
"fork-ts-checker-webpack-plugin": "^9.0.0",
"fs-extra": "^11.2.0",
"glob": "^7.1.7",
"html-webpack-plugin": "^5.6.3",
"lodash": "^4.17.21",
"mini-css-extract-plugin": "^2.4.2",
"node-stdlib-browser": "^1.3.1",
"npm-packlist": "^5.0.0",
"p-queue": "^6.6.2",
"postcss": "^8.1.0",
"postcss-import": "^16.1.0",
"process": "^0.11.10",
"raw-loader": "^4.0.2",
"react-dev-utils": "^12.0.0-next.60",
"react-refresh": "^0.18.0",
"rollup": "^4.27.3",
"rollup-plugin-dts": "^6.1.0",
"rollup-plugin-esbuild": "^6.1.1",
"rollup-plugin-postcss": "^4.0.0",
"rollup-pluginutils": "^2.8.2",
"shell-quote": "^1.8.1",
"style-loader": "^3.3.1",
"swc-loader": "^0.2.3",
"tar": "^7.5.6",
"ts-checker-rspack-plugin": "^1.1.5",
"ts-morph": "^24.0.0",
"util": "^0.12.3",
"webpack": "~5.105.0",
"webpack-dev-server": "^5.0.0",
"yml-loader": "^2.1.0",
"yn": "^4.0.0"
},
"devDependencies": {
"@backstage/backend-test-utils": "workspace:^",
"@backstage/cli": "workspace:^",
"@types/fs-extra": "^11.0.0",
"@types/lodash": "^4.14.151",
"@types/npm-packlist": "^3.0.0",
"@types/shell-quote": "^1.7.5"
}
}
+13
View File
@@ -0,0 +1,13 @@
## API Report File for "@backstage/cli-module-build"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { CliModule } from '@backstage/cli-node';
// @public (undocumented)
const _default: CliModule;
export default _default;
// (No @packageDocumentation comment for this package)
```
@@ -17,9 +17,9 @@
import fs from 'fs-extra';
import { cli } from 'cleye';
import { createDistWorkspace } from '../lib/packager';
import type { CommandContext } from '../../../wiring/types';
import type { CliCommandContext } from '@backstage/cli-node';
export default async ({ args, info }: CommandContext) => {
export default async ({ args, info }: CliCommandContext) => {
// Normalize legacy --alwaysYarnPack alias (a genuinely different name, not
// just a casing variant — type-flag handles camelCase/kebab-case natively)
const normalizedArgs = args.map(a => {
@@ -29,9 +29,9 @@ import { buildFrontend } from '../../../lib/buildFrontend';
import { buildBackend } from '../../../lib/buildBackend';
import { isValidUrl } from '../../../lib/urls';
import chalk from 'chalk';
import type { CommandContext } from '../../../../../wiring/types';
import type { CliCommandContext } from '@backstage/cli-node';
export default async ({ args, info }: CommandContext) => {
export default async ({ args, info }: CliCommandContext) => {
const {
flags: {
role,
@@ -17,9 +17,9 @@
import { cli } from 'cleye';
import fs from 'fs-extra';
import { targetPaths } from '@backstage/cli-common';
import type { CommandContext } from '../../../../wiring/types';
import type { CliCommandContext } from '@backstage/cli-node';
export default async ({ args, info }: CommandContext) => {
export default async ({ args, info }: CliCommandContext) => {
cli({ help: info, booleanFlagNegation: true }, undefined, args);
await fs.remove(targetPaths.resolve('dist'));
await fs.remove(targetPaths.resolve('dist-types'));
@@ -17,9 +17,9 @@
import { cli } from 'cleye';
import { targetPaths } from '@backstage/cli-common';
import { revertProductionPack } from '../../lib/packager/productionPack';
import type { CommandContext } from '../../../../wiring/types';
import type { CliCommandContext } from '@backstage/cli-node';
export default async ({ args, info }: CommandContext) => {
export default async ({ args, info }: CliCommandContext) => {
cli({ help: info, booleanFlagNegation: true }, undefined, args);
await revertProductionPack(targetPaths.dir);
};
@@ -20,9 +20,9 @@ import { targetPaths } from '@backstage/cli-common';
import { productionPack } from '../../lib/packager/productionPack';
import { publishPreflightCheck } from '../../lib/publishing';
import { createTypeDistProject } from '../../lib/typeDistProject';
import type { CommandContext } from '../../../../wiring/types';
import type { CliCommandContext } from '@backstage/cli-node';
export default async ({ args, info }: CommandContext) => {
export default async ({ args, info }: CliCommandContext) => {
cli({ help: info, booleanFlagNegation: true }, undefined, args);
publishPreflightCheck({
@@ -19,9 +19,9 @@ import { startPackage } from './startPackage';
import { resolveLinkedWorkspace } from './resolveLinkedWorkspace';
import { findRoleFromCommand } from '../../../lib/role';
import { targetPaths } from '@backstage/cli-common';
import type { CommandContext } from '../../../../../wiring/types';
import type { CliCommandContext } from '@backstage/cli-node';
export default async ({ args, info }: CommandContext) => {
export default async ({ args, info }: CliCommandContext) => {
const {
flags: {
config,
@@ -29,9 +29,9 @@ import {
import { buildFrontend } from '../../lib/buildFrontend';
import { buildBackend } from '../../lib/buildBackend';
import { createScriptOptionsParser } from '../../lib/optionsParser';
import type { CommandContext } from '../../../../wiring/types';
import type { CliCommandContext } from '@backstage/cli-node';
export default async ({ args, info }: CommandContext) => {
export default async ({ args, info }: CliCommandContext) => {
const {
flags: { all, since, minify },
} = cli(
@@ -19,9 +19,9 @@ import fs from 'fs-extra';
import { resolve as resolvePath } from 'node:path';
import { PackageGraph } from '@backstage/cli-node';
import { run, targetPaths } from '@backstage/cli-common';
import type { CommandContext } from '../../../../wiring/types';
import type { CliCommandContext } from '@backstage/cli-node';
export default async ({ args, info }: CommandContext) => {
export default async ({ args, info }: CliCommandContext) => {
cli({ help: info, booleanFlagNegation: true }, undefined, args);
const packages = await PackageGraph.listTargetPackages();
@@ -26,7 +26,7 @@ import { cli } from 'cleye';
import { resolveLinkedWorkspace } from '../package/start/resolveLinkedWorkspace';
import { startPackage } from '../package/start/startPackage';
import { parseArgs } from 'node:util';
import type { CommandContext } from '../../../../wiring/types';
import type { CliCommandContext } from '@backstage/cli-node';
const ACCEPTED_PACKAGE_ROLES: Array<PackageRole | undefined> = [
'frontend',
@@ -35,7 +35,7 @@ const ACCEPTED_PACKAGE_ROLES: Array<PackageRole | undefined> = [
'backend-plugin',
];
export default async ({ args, info }: CommandContext) => {
export default async ({ args, info }: CliCommandContext) => {
const {
flags: { plugin, config, require: requirePath, link, inspect, inspectBrk },
_: namesOrPaths,
@@ -14,10 +14,11 @@
* limitations under the License.
*/
import { createCliPlugin } from '../../wiring/factory';
import { createCliModule } from '@backstage/cli-node';
import packageJson from '../package.json';
export const buildPlugin = createCliPlugin({
pluginId: 'build',
export default createCliModule({
packageJson,
init: async reg => {
reg.addCommand({
path: ['package', 'build'],
@@ -84,5 +85,3 @@ export const buildPlugin = createCliPlugin({
});
},
});
export default buildPlugin;
@@ -29,10 +29,10 @@ import { ModuleFederationPlugin } from '@module-federation/enhanced/rspack';
import fs from 'fs-extra';
import { optimization as optimizationConfig } from './optimization';
import pickBy from 'lodash/pickBy';
import { runOutput, targetPaths } from '@backstage/cli-common';
import { findOwnPaths, runOutput, targetPaths } from '@backstage/cli-common';
import { transforms } from './transforms';
import { version } from '../../../../wiring/version';
import { version } from '../../../package.json';
import yn from 'yn';
import { hasReactDomClient } from './hasReactDomClient';
import { createWorkspaceLinkingPlugins } from './linkWorkspaces';
@@ -330,7 +330,8 @@ export async function createConfig(
devtool: isDev ? 'eval-cheap-module-source-map' : 'source-map',
context: paths.targetPath,
entry: [
require.resolve('@backstage/cli/config/webpack-public-path'),
/* eslint-disable-next-line no-restricted-syntax */
findOwnPaths(__dirname).resolve('config/webpack-public-path'),
...(options.additionalEntryPoints ?? []),
paths.targetEntry,
],

Some files were not shown because too many files have changed in this diff Show More