-
Notifications
You must be signed in to change notification settings - Fork 65
Export Tools
This guide covers the CLI tools and workflow options for exporting Backstage plugins as dynamic plugins.
The export process transforms a standard Backstage plugin into an OCI-packaged dynamic plugin that can be loaded at runtime by a Backstage-based host application.
Source Plugin Export Process OCI Artifact
βββββββββββββββ ββββββββββββββββββββββββββββββββββ ββββββββββββββββββββββββ
β package.jsonβ β 1. Clone source repo β β Dynamic plugin β
β src/ βββββΆβ 2. Apply patches βββββΆβ packaged as OCI β
β ... β β 3. Install dependencies β β image β
βββββββββββββββ β 4. Build plugin β ββββββββββββββββββββββββ
β 5. Export as dynamic plugin β
β 6. Package as OCI container β
ββββββββββββββββββββββββββββββββββ
The export tooling is provided by the @red-hat-developer-hub/cli package (specified in versions.json):
| Component | Version |
|---|---|
| Backstage | 1.45.3 |
| Node.js | 22.19.0 |
| CLI | @red-hat-developer-hub/cli@1.9.1 |
# Install/run the CLI
npx @red-hat-developer-hub/cli@1.9.1 plugin exportπ Version Matrix: Ensure CLI version matches your target platform version. See versions.json for current values.
Each plugin entry in plugins-list.yaml can include CLI arguments after the colon:
plugins/my-plugin:
plugins/my-plugin-backend: --embed-package @backstage/some-dependency --suppress-native-package cpu-features| Argument | Description |
|---|---|
--embed-package <pkg> |
Bundle a dependency into the dynamic plugin (for packages not available separately) |
--shared-package <pkg> |
Mark package as shared (provided by host at runtime) |
--shared-package '!<pkg>' |
Force a @backstage/ package to be bundled instead of shared |
--suppress-native-package <pkg> |
Exclude a native Node.js package from the bundle |
π Full CLI Documentation: For comprehensive details on all export flags, shared vs embedded dependencies, and frontend plugin configuration, see: Export Derived Dynamic Plugin Package
# Embed a dependency that isn't available as a separate dynamic plugin
plugins/catalog-backend-module-github-org: --embed-package @backstage/plugin-catalog-backend-module-github
# Suppress a native module that causes build issues
plugins/techdocs-backend: --embed-package @backstage/plugin-search-backend-module-techdocs --suppress-native-package cpu-features
# Force a @backstage package to be bundled (not shared)
plugins/notifications-backend: --shared-package '!/@backstage/plugin-notifications/' --embed-package @backstage/plugin-notifications-backendWhen triggering exports via GitHub Actions, the following inputs are available.
Workflow: export-workspaces-as-dynamic.yaml
| Input | Type | Description |
|---|---|---|
workspace-path |
string | Relative path to a single workspace (e.g., workspaces/backstage) |
overlay-branch |
string | Branch of the overlay structure |
node-version |
string | Node.js version (defaults to versions.json) |
janus-cli-version |
string | CLI package version (defaults to versions.json) |
cli-package |
string | CLI package name (defaults to versions.json) |
publish-container |
boolean | Whether to publish OCI images |
image-repository-prefix |
string | OCI registry prefix |
upload-project-on-error |
boolean | Upload workspace on failure for debugging |
# Export all plugins in a workspace
gh workflow run export-workspaces-as-dynamic.yaml \
-f workspace-path="workspaces/backstage" \
-f overlay-branch="main" \
-f publish-container=true
# Export with custom Node version
gh workflow run export-workspaces-as-dynamic.yaml \
-f workspace-path="workspaces/my-plugin" \
-f overlay-branch="main" \
-f node-version="22.19.0" \
-f publish-container=falseWhen working with Pull Requests, use these comment commands:
| Command | Action |
|---|---|
/publish |
Build and publish test OCI artifacts |
/smoketest |
Re-run smoke tests (requires prior /publish) |
- Checks out the overlay branch
- Clones the source repository at the specified
repo-ref - Applies any patches from
patches/ - Installs dependencies
- Builds the plugins
- Exports as dynamic plugins
- Packages as OCI containers
- Publishes to
ghcr.iowith tagpr_<number>__<version> - Posts OCI references as a PR comment
Frontend plugins often require additional configuration files.
Defines route bindings, dynamic routes, and mount points:
backstage.plugin-techdocs:
routeBindings:
targets:
- importName: techdocsPlugin
bindings:
- bindTarget: catalogPlugin.externalRoutes
bindMap:
viewTechDoc: techdocsPlugin.routes.docRoot
dynamicRoutes:
- path: /docs
importName: TechDocsIndexPage
menuItem:
icon: docs
text: Docs
mountPoints:
- mountPoint: entity.page.docs/cards
importName: EntityTechdocsContentLocation: workspaces/[workspace]/plugins/[plugin-name]/app-config.dynamic.yaml
Defines the Scalprum module configuration:
{
"name": "backstage.plugin-api-docs-module-protoc-gen-doc",
"exposedModules": {
"PluginRoot": "./src/api.ts"
}
}Location: workspaces/[workspace]/plugins/[plugin-name]/scalprum-config.json
| Feature | Overlay | Patch |
|---|---|---|
| Scope | Single plugin | Entire workspace |
| Method | Replace/add files | Line-by-line changes |
| Location | plugins/[name]/overlay/ |
patches/*.patch |
| Use Case | Add new files, replace implementations | Fix bugs, modify configs |
- Adding new source files to a plugin
- Replacing entire modules or implementations
- Adding configuration files
Example structure:
workspaces/backstage/plugins/api-docs-module-protoc-gen-doc/
βββ overlay/
β βββ package.json
β βββ src/
β βββ api.ts
βββ scalprum-config.json
- Fixing build issues
- Modifying package.json fields
- Small, targeted source changes
See 06 - Patch Management for details.
-
Check workflow logs β Look for the specific error message
-
Enable debug upload:
gh workflow run export-workspaces-as-dynamic.yaml \ -f workspace-path="workspaces/failing-plugin" \ -f upload-project-on-error=true -
Download the artifact and inspect locally
Symptom: Integrity check failed for package X
Cause: The package.json in the source repo doesn't match the overlay's expectations.
Solution:
- Verify
source.jsonpoints to the correctrepo-ref - Check if the source
package.jsonwas modified - If intentional, consider using
--no-integrity-check(document why)
Symptom: Cannot find module '@backstage/some-package'
Solution: Add --embed-package @backstage/some-package to the plugin entry in plugins-list.yaml
Symptom: Build fails with native compilation errors
Solution: Add --suppress-native-package [package-name] if the native module isn't needed at runtime
- 03 - Plugin Owner Responsibilities β Understand maintenance obligations
- 06 - Patch Management β Learn to create and maintain patches
- 64 workspaces
- 10 with patches