Plugin Metadata
The plugin metadata utilities handle loading and injecting plugin configurations from Package CRD metadata files. This enables automatic configuration of dynamic plugins during deployment.
How It Works
Plugin metadata is stored in metadata/*.yaml files alongside your plugin source code. These files follow the Package CRD format and contain spec.appConfigExamples with the plugin's default configuration.
workspaces/<plugin-name>/
├── metadata/ # Plugin metadata files
│ ├── plugin-frontend.yaml # Frontend plugin metadata
│ └── plugin-backend.yaml # Backend plugin metadata
├── e2e-tests/ # Your test project
│ └── tests/config/
│ └── dynamic-plugins.yaml # Your plugin config (optional)
└── plugins/ # Plugin source code2
3
4
5
6
7
8
During deployment, the package reads these metadata files and:
- Auto-generates a complete config if
dynamic-plugins.yamldoesn't exist - Injects metadata into existing plugins if
dynamic-plugins.yamlexists
When Metadata Handling is Enabled
Metadata handling is enabled by default for:
- Local development
- PR builds in CI
Metadata handling is disabled when:
RHDH_SKIP_PLUGIN_METADATA_INJECTIONis set totrueE2E_NIGHTLY_MODEis set totrueJOB_NAMEcontainsperiodic-(nightly builds)
Priority
The isNightlyJob() function checks in this order:
- If
GIT_PR_NUMBERis set → PR mode (returnsfalse, metadata injection enabled) - If
E2E_NIGHTLY_MODEis"true"or"1"→ nightly mode (returnstrue) - If
JOB_NAMEcontainsperiodic-→ nightly mode (returnstrue) - Otherwise → PR/local mode (returns
false)
PR mode always takes precedence, preventing broken combinations of PR images with nightly config.
Usage
Plugin metadata handling is fully automatic — it runs inside rhdh.deploy() with no additional code required. You don't need to import or call any metadata functions directly.
test.beforeAll(async ({ rhdh }) => {
await rhdh.configure({ auth: "keycloak" });
await rhdh.deploy(); // Metadata is loaded, injected, and resolved automatically
});2
3
4
What Happens During deploy()
Config assembly:
- If
dynamic-plugins.yamlexists: merged with package defaults + auth config - If
dynamic-plugins.yamldoesn't exist: auto-generated from allmetadata/*.yamlfiles, then merged with defaults/auth (deduplicated by normalized plugin name — OCI wins over local-dynamicpaths)
- If
Metadata injection (PR/local mode only, skipped in nightly):
appConfigExamplesfrom metadata merged as base config- User-provided
pluginConfigoverrides metadata values
Package resolution (both modes) — per plugin, in priority order:
Condition Result Plugin in workspace build + GIT_PR_NUMBERsetPR OCI URL: pr_{number}__{version}Plugin has metadata with OCI dynamicArtifactMetadata's OCI ref (preserves original registry) No metadata match (cross-workspace plugins, npm packages) Kept as-is Wrapper disabling (PR builds only, when
GIT_PR_NUMBERset):- Appends
disabled: trueentries for wrapper plugins listed indisableWrappers
- Appends
Multiple OCI Registries
Plugin OCI references use the actual registry from each plugin's spec.dynamicArtifact — not a single hardcoded registry. Plugins can come from ghcr.io, quay.io/rhdh, registry.access.redhat.com/rhdh, or other registries.
Extract Plugin Name
The utilities support various package reference formats:
import { extractPluginName } from "@red-hat-developer-hub/e2e-test-utils/utils";
// All of these extract "my-plugin"
extractPluginName("./dynamic-plugins/dist/my-plugin");
extractPluginName("oci://quay.io/rhdh/my-plugin:1.0.0");
extractPluginName("oci://quay.io/rhdh/my-plugin@sha256:abc123");
extractPluginName("ghcr.io/org/repo/my-plugin:tag");2
3
4
5
6
7
Parse Metadata Files
For custom handling, you can parse metadata files directly:
import {
getMetadataDirectory,
parseAllMetadataFiles,
} from "@red-hat-developer-hub/e2e-test-utils/utils";
const metadataDir = getMetadataDirectory();
if (metadataDir) {
const metadataMap = await parseAllMetadataFiles(metadataDir);
for (const [pluginName, metadata] of metadataMap) {
console.log(`Plugin: ${pluginName}`);
console.log(` Package: ${metadata.packagePath}`);
console.log(` Config:`, metadata.pluginConfig);
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
Deployment Integration
The RHDHDeployment class automatically uses these utilities during deploy():
If
dynamic-plugins.yamlexists:- Merges package defaults + auth config + your config
- Injects metadata for plugins in your config
If
dynamic-plugins.yamldoesn't exist:- Auto-generates from all metadata files
- Merges with package defaults and auth-specific plugins (e.g. Keycloak)
- Deduplicates by normalized plugin name so the same logical plugin appears once (metadata/OCI wins)
- All plugins enabled with default configurations
See Configuration Files for detailed behavior.
OCI URL Generation for PR Builds
When GIT_PR_NUMBER is set (by OpenShift CI), local plugin paths are automatically replaced with OCI URLs pointing to the PR's built artifacts.
How It Works
- Reads
source.jsonfrom the workspace directory to get the source repo and commit ref - Reads
plugins-list.yamlto get the list of plugin paths - Fetches each plugin's
package.jsonfrom the source repo to get the current version - Generates OCI URLs in the format:
oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/{plugin-name}:pr_{PR_NUMBER}__{version}Example
# Local development
- package: ./dynamic-plugins/dist/backstage-community-plugin-tech-radar
# PR build (GIT_PR_NUMBER=1845)
- package: oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/backstage-community-plugin-tech-radar:pr_1845__1.13.02
3
4
5
Required Files
For OCI URL generation, your workspace must have these files (generated by CI):
| File | Purpose |
|---|---|
source.json | Contains repo (GitHub URL) and repo-ref (commit SHA) |
plugins-list.yaml | Lists plugin paths (e.g., plugins/tech-radar:) |
WARNING
For PR builds, OCI URL generation is strict - deployment will fail if required files are missing or version fetching fails. This ensures PR builds don't silently fall back to local paths.
Mode Comparison
The system operates in three modes based on environment variables:
| PR Check | Nightly | Local Dev | |
|---|---|---|---|
| Trigger | GIT_PR_NUMBER set | E2E_NIGHTLY_MODE=true | No env vars |
| Config injection | Yes — appConfigExamples merged | Skipped | Yes |
| OCI resolution | PR tags (pr_{n}__{v}) for workspace plugins, metadata refs for others | Metadata refs for all | Metadata refs for all |
| Wrapper disabling | Yes (disableWrappers) | No | No |
| Cross-workspace plugins | Kept as-is | Kept as-is | Kept as-is |
Why Metadata Refs (Not )
Metadata files are the most accurate source for latest published plugin versions. The daily update-plugins-repo-refs workflow keeps them current. By contrast, many OCI plugins (~49) are not in the catalog index (DPDY), and some that are have older versions. Using metadata ensures nightly tests run against the latest published artifacts.
processPluginsForDeployment
This is the unified entry point for both PR and nightly plugin resolution flows. It is called automatically during deploy().
Step 1: Inject metadata configs (PR/local mode only)
→ deepMerge(metadata.appConfigExamples, user.pluginConfig)
→ Skipped when: isNightlyJob() OR RHDH_SKIP_PLUGIN_METADATA_INJECTION="true"
Step 2: Resolve packages to OCI (both modes)
→ Per plugin: PR OCI URL > metadata OCI ref > passthrough2
3
4
5
6
Environment Variables
| Variable | Effect |
|---|---|
GIT_PR_NUMBER | Enables OCI URL generation for PR builds |
E2E_NIGHTLY_MODE | When true, activates nightly mode (uses released OCI refs) |
RHDH_SKIP_PLUGIN_METADATA_INJECTION | Disables all metadata handling |
JOB_NAME | If contains periodic-, disables metadata handling |
JOB_MODE | CI-only: nightly or pr-check (set by step registry) |
See Environment Variables for details.
API Reference
For complete API documentation, see Plugin Metadata API.
For Overlay Repository
If you're writing tests in the rhdh-plugin-export-overlays repository, see Overlay Configuration Files for how plugin metadata and OCI URL generation work in that context.