Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 31 additions & 23 deletions docs/api/utils/plugin-metadata.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Plugin metadata handling is fully automatic during `rhdh.deploy()`. The function
Extracts the plugin name from a package path or OCI reference.

```typescript
function extractPluginName(packageRef: string): string
function extractPluginName(packageRef: string): string;
```

**Parameters:**
Expand All @@ -25,18 +25,20 @@ function extractPluginName(packageRef: string): string

**Supported Formats:**

| Format | Example | Extracted Name |
|--------|---------|----------------|
| Wrapper path | `./dynamic-plugins/dist/my-plugin` | `my-plugin` |
| OCI with tag | `oci://quay.io/rhdh/my-plugin:1.0.0` | `my-plugin` |
| OCI with digest | `oci://quay.io/rhdh/my-plugin@sha256:abc...` | `my-plugin` |
| OCI with alias | `oci://quay.io/rhdh/my-plugin@sha256:abc!alias` | `my-plugin` |
| GHCR | `ghcr.io/org/repo/my-plugin:tag` | `my-plugin` |
| Format | Example | Extracted Name |
| --------------- | ----------------------------------------------- | -------------- |
| Wrapper path | `./dynamic-plugins/dist/my-plugin` | `my-plugin` |
| OCI with tag | `oci://quay.io/rhdh/my-plugin:1.0.0` | `my-plugin` |
| OCI with digest | `oci://quay.io/rhdh/my-plugin@sha256:abc...` | `my-plugin` |
| OCI with alias | `oci://quay.io/rhdh/my-plugin@sha256:abc!alias` | `my-plugin` |
| GHCR | `ghcr.io/org/repo/my-plugin:tag` | `my-plugin` |

**Example:**

```typescript
const name = extractPluginName("oci://quay.io/rhdh/backstage-community-plugin-tech-radar:1.0.0");
const name = extractPluginName(
"oci://quay.io/rhdh/backstage-community-plugin-tech-radar:1.0.0",
);
// Returns: "backstage-community-plugin-tech-radar"
```

Expand All @@ -47,7 +49,7 @@ const name = extractPluginName("oci://quay.io/rhdh/backstage-community-plugin-te
Returns a stable merge key for a plugin entry so that OCI and local path for the same logical plugin match when merging dynamic-plugins configs. Strips a trailing `-dynamic` suffix so that e.g. `backstage-community-plugin-catalog-backend-module-keycloak-dynamic` (local) and `backstage-community-plugin-catalog-backend-module-keycloak` (from OCI) map to the same key.

```typescript
function getNormalizedPluginMergeKey(entry: { package?: string }): string
function getNormalizedPluginMergeKey(entry: { package?: string }): string;
```

**Parameters:**
Expand All @@ -62,12 +64,14 @@ function getNormalizedPluginMergeKey(entry: { package?: string }): string
```typescript
// OCI and local path for the same plugin yield the same key
getNormalizedPluginMergeKey({
package: "oci://ghcr.io/org/repo/backstage-community-plugin-catalog-backend-module-keycloak:tag!alias",
package:
"oci://ghcr.io/org/repo/backstage-community-plugin-catalog-backend-module-keycloak:tag!alias",
});
// Returns: "backstage-community-plugin-catalog-backend-module-keycloak"

getNormalizedPluginMergeKey({
package: "./dynamic-plugins/dist/backstage-community-plugin-catalog-backend-module-keycloak-dynamic",
package:
"./dynamic-plugins/dist/backstage-community-plugin-catalog-backend-module-keycloak-dynamic",
});
// Returns: "backstage-community-plugin-catalog-backend-module-keycloak"
```
Expand All @@ -79,12 +83,13 @@ getNormalizedPluginMergeKey({
Determines whether the current execution is a nightly/periodic job. Controls whether metadata config injection is enabled and which OCI resolution strategy is used.

```typescript
function isNightlyJob(): boolean
function isNightlyJob(): boolean;
```

**Returns:** `true` if running in nightly mode, `false` for PR/local mode.

**Priority order:**

1. If `GIT_PR_NUMBER` is set → returns `false` (PR mode takes precedence)
2. If `E2E_NIGHTLY_MODE` is `"true"` or `"1"` → returns `true`
3. If `JOB_NAME` contains `periodic-` → returns `true`
Expand All @@ -97,7 +102,7 @@ function isNightlyJob(): boolean
Gets the metadata directory path.

```typescript
function getMetadataDirectory(metadataPath?: string): string | null
function getMetadataDirectory(metadataPath?: string): string | null;
```

**Parameters:**
Expand All @@ -115,8 +120,8 @@ Parses all metadata files in a directory and builds a map of plugin name to conf

```typescript
async function parseAllMetadataFiles(
metadataDir: string
): Promise<Map<string, PluginMetadata>>
metadataDir: string,
): Promise<Map<string, PluginMetadata>>;
```

**Parameters:**
Expand All @@ -134,8 +139,8 @@ Auto-generates plugin entries from workspace metadata files when no user-provide

```typescript
async function generatePluginsFromMetadata(
metadataPath?: string
): Promise<DynamicPluginsConfig>
metadataPath?: string,
): Promise<DynamicPluginsConfig>;
```

**Parameters:**
Expand All @@ -154,22 +159,25 @@ Unified entry point for both PR and nightly plugin resolution flows. Called auto
```typescript
async function processPluginsForDeployment(
config: DynamicPluginsConfig,
metadataPath?: string
): Promise<DynamicPluginsConfig>
metadataPath?: string,
dpdyPackages?: Set<string>,
): Promise<DynamicPluginsConfig>;
```

**Parameters:**
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `config` | [`DynamicPluginsConfig`](#dynamicpluginsconfig) | - | The plugins config to process |
| `metadataPath` | `string` | `"../metadata"` | Path to metadata directory |
| `dpdyPackages` | `Set<string>` | - | Pre-loaded DPDY package set (for testing; fetched automatically if omitted in nightly) |

**Returns:** Processed configuration with resolved OCI references.

**Behavior:**

- **PR mode** (`!isNightlyJob()`): Injects `appConfigExamples` from metadata as base config, then resolves packages to OCI URLs (PR-specific if `GIT_PR_NUMBER` set, metadata refs otherwise)
- **Nightly mode** (`isNightlyJob()`): Resolves packages to OCI refs from metadata only (no config injection)
- Respects `RHDH_SKIP_PLUGIN_METADATA_INJECTION` to skip config injection
- **Nightly mode** (`isNightlyJob()`): Plugins in `default.packages.yaml` whose metadata `spec.dynamicArtifact` is an OCI ref use `{{inherit}}` with configurable registry (default `registry.access.redhat.com/rhdh`, overridable via `NIGHTLY_DPDY_OCI_REGISTRY` or `NIGHTLY_DPDY_OCI_REGISTRY_MAP`) — RHDH resolves both the OCI tag and default config from its built-in DPDY. Plugins NOT in `default.packages.yaml` with OCI metadata use full metadata refs with config injection.
- Respects `RHDH_SKIP_PLUGIN_METADATA_INJECTION` to skip config injection (local only, ignored in CI)

---

Expand All @@ -178,7 +186,7 @@ async function processPluginsForDeployment(
Creates a dynamic plugins config that disables wrapper plugins. Used during PR builds when wrapper plugins would conflict with PR-built OCI images.

```typescript
function disablePluginWrappers(plugins: string[]): DynamicPluginsConfig
function disablePluginWrappers(plugins: string[]): DynamicPluginsConfig;
```

**Parameters:**
Expand Down
10 changes: 9 additions & 1 deletion docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@

All notable changes to this project will be documented in this file.

## [1.1.39] - Current
## [1.1.40] - Current

### Changed

- **Nightly `{{inherit}}` resolution**: In nightly mode, plugins listed in [`default.packages.yaml`](https://github.com/redhat-developer/rhdh/blob/main/default.packages.yaml) whose metadata `spec.dynamicArtifact` is an OCI ref now resolve to `{{inherit}}` tags instead of pinned OCI refs. The `{{inherit}}` tag tells RHDH to resolve both the OCI tag (version) and default config from its built-in DPDY (`dynamic-plugins.default.yaml` in the catalog index image), so no config injection is needed from our side. This tests against the exact versions and configuration shipped in the RC. Plugins NOT in `default.packages.yaml` with OCI metadata continue using full metadata refs with config injection (they aren't in RHDH's built-in defaults). The DPDY list is fetched at runtime from the `rhdh` repo using `RELEASE_BRANCH_NAME` (required in CI, defaults to `main` locally). The `{{inherit}}` registry defaults to `registry.access.redhat.com/rhdh` and can be overridden with `NIGHTLY_DPDY_OCI_REGISTRY` (blanket) or `NIGHTLY_DPDY_OCI_REGISTRY_MAP` (per-plugin JSON: `{"registry": ["pkg1", "pkg2"]}`). This decouples the `{{inherit}}` registry from metadata's `spec.dynamicArtifact`, avoiding mismatches when metadata points to `ghcr.io` but the DPDY uses `registry.access.redhat.com`.
- **`RHDH_SKIP_PLUGIN_METADATA_INJECTION` is local-only**: This env var is now ignored in CI (`CI=true`). It was intended for local development opt-out only — in CI, metadata injection should always run to ensure consistent test behavior.
- **`RELEASE_BRANCH_NAME` required in CI for nightly**: When running nightly mode in CI, `RELEASE_BRANCH_NAME` must be set (exported by the OpenShift CI step registry). Locally it defaults to `main`.

## [1.1.39]

### Changed

Expand Down
37 changes: 23 additions & 14 deletions docs/guide/configuration/config-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,18 +212,24 @@ Your `pluginConfig` in `dynamic-plugins.yaml` overrides values from metadata.
### When Injection is Enabled

Plugin metadata injection is **enabled by default** for:

- Local development
- PR builds in CI

Injection is **disabled** when:
- [`RHDH_SKIP_PLUGIN_METADATA_INJECTION`](/guide/configuration/environment-variables#plugin-metadata-variables) environment variable is set
- `JOB_NAME` contains `periodic-` (nightly/periodic CI builds)
Injection is **disabled locally** when:

- [`RHDH_SKIP_PLUGIN_METADATA_INJECTION`](/guide/configuration/environment-variables#plugin-metadata-variables) is set to `true` (ignored in CI)

In **nightly mode** (`E2E_NIGHTLY_MODE=true` or `JOB_NAME` contains `periodic-`):

- Only plugins NOT in `default.packages.yaml` with OCI metadata get injection; plugins in `default.packages.yaml` use `{{inherit}}` — RHDH resolves both the OCI tag and default config from its built-in DPDY

::: warning
When injection is enabled, deployment will fail if:

- The `metadata/` directory doesn't exist
- No valid metadata files are found in the directory
:::
:::

### OCI URL Replacement for PR Builds

Expand All @@ -244,6 +250,7 @@ This allows E2E tests to run against the actual OCI images built for the PR.
If you want to reproduce OCI URL replacement locally, create the required files at the workspace root:

**source.json**

```json
{
"repo": "https://github.com/redhat-developer/rhdh-plugin-export-overlays",
Expand All @@ -252,6 +259,7 @@ If you want to reproduce OCI URL replacement locally, create the required files
```

**plugins-list.yaml**

```yaml
plugins/tech-radar:
plugins/my-plugin:
Expand All @@ -274,26 +282,27 @@ See [Plugin Metadata - OCI URL Generation](/guide/utilities/plugin-metadata#oci-

The package automatically matches plugins across different reference formats:

| Format | Example |
|--------|---------|
| Wrapper path | `./dynamic-plugins/dist/my-plugin` |
| OCI with tag | `oci://quay.io/rhdh/my-plugin:1.0.0` |
| Format | Example |
| --------------- | -------------------------------------------- |
| Wrapper path | `./dynamic-plugins/dist/my-plugin` |
| OCI with tag | `oci://quay.io/rhdh/my-plugin:1.0.0` |
| OCI with digest | `oci://quay.io/rhdh/my-plugin@sha256:abc...` |
| GHCR | `ghcr.io/org/repo/my-plugin:tag` |
| GHCR | `ghcr.io/org/repo/my-plugin:tag` |

All formats extract the plugin name (`my-plugin`) for matching against metadata.

## Environment Variable Substitution

Use these syntaxes in YAML files:

| Syntax | Description |
|--------|-------------|
| `$VAR` | Simple substitution |
| `${VAR}` | Braced substitution |
| `${VAR:-default}` | Default if unset |
| Syntax | Description |
| ----------------- | ------------------- |
| `$VAR` | Simple substitution |
| `${VAR}` | Braced substitution |
| `${VAR:-default}` | Default if unset |

Example:

```yaml
backend:
baseUrl: https://backstage-${NAMESPACE}.${K8S_CLUSTER_ROUTER_BASE}
Expand Down
Loading
Loading