-
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.52.0 |
| Node.js | 24.14.0 |
| CLI | @red-hat-developer-hub/cli@1.10.7 |
# Install/run the CLI
npx @red-hat-developer-hub/cli@1.10.7 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 on the PR (handled by pr-actions.yaml).
Requirements (all commands in this section):
- The PR must touch exactly one workspace (files under
workspaces/<name>/...), or the head branch must matchworkspaces/release-*__<name>so the workspace can be inferred. - Use one slash command per comment (multiple commands in one comment are rejected).
| Command | Action |
|---|---|
/publish |
Build and publish test OCI artifacts |
/smoketest |
Re-run smoke tests with the default branch-derived RHDH image (requires prior /publish) |
/smoketest <tag> |
Re-run smoke tests with quay.io/rhdh-community/rhdh:<tag> (allowlisted tag formats, requires prior /publish) |
/update-versions |
Copy versions.json from the PR base branch onto the PR branch so it matches the release line |
/update-commit |
Re-run automatic plugin repo ref discovery for that workspace and update the PR branch |
Use /update-versions when CI reports that versions.json on your PR does not match the release branch you are targeting. Use /update-commit when you want refreshed upstream refs (for example source.json) from discovery without editing them by hand.
- 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
- Reads the root
versions.jsonfile from the PR base branch (for examplerelease-1.5). - Writes that same content onto your PR head branch so the PRβs
versions.jsonmatches the release line. - Runs with force enabled: if your branch had local edits to
versions.json, they can be overwrittenβresolve intentional differences elsewhere (for example follow up with a separate commit only if policy allows).
Workflow: update-prs-with-release-branch-commits.yaml.
- Resolves the single workspace for the PR (same rules as
/publish). - Invokes the update plugins repository references pipeline for that workspace only, scoped to the PR (same automation family as the scheduled discovery workflow, with
allow-workspace-additiondisabled so new workspaces are not created from the comment path). - Updates discovery-managed inputs on the PR branch (such as
source.jsonrepo-ref / discovery outputs for that workspace)βnotversions.json(use/update-versionsfor that).
Workflow: update-plugins-repo-refs.yaml.
| 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
- 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: Workflow logs show an integrity mismatch for a package.
Cause: The export process verifies that the package.json fields in the checked-out source match the metadata declared in the overlay (e.g., package name, version). A mismatch usually means source.json:repo-ref points to a different version than what metadata/*.yaml:spec.version declares.
Solution:
- Verify
source.json:repo-refpoints to the correct tag or commit - Confirm that
spec.versionandspec.packageNamein your metadata files match the sourcepackage.jsonat that ref - If the mismatch is intentional (e.g., a patch changes the version), document the reason in the PR
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