Skip to content

Commit 9edea79

Browse files
gustavoliraclaude
andcommitted
feat: bypass {{inherit}} for coverage so DPDY plugins are covered too
Addresses review feedback: the nightly resolves DPDY (default.packages.yaml) plugins via {{inherit}}, which deploys the Konflux catalog image (registry.access.redhat.com/rhdh) — not instrumentable. The previous version only swapped the non-{{inherit}} ghcr plugins, so GA/TP plugins got no coverage. In a coverage run (E2E_NIGHTLY_COVERAGE=true), a rolled-out frontend DPDY plugin now bypasses {{inherit}} and deploys the overlay's instrumented ghcr __coverage build of the same source instead — so no Konflux pipeline changes are needed. The functional nightly (no opt-in) is unchanged: it still uses {{inherit}} and tests the shipped Konflux build. Tests updated accordingly; docs (changelog + resolution reference) reflect the bypass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent a025d41 commit 9edea79

4 files changed

Lines changed: 52 additions & 16 deletions

File tree

docs/changelog.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ All notable changes to this project will be documented in this file.
66

77
### Added
88

9-
- **Nightly coverage image swap** (`E2E_NIGHTLY_COVERAGE`): In nightly plugin resolution, an explicit opt-in swaps a rolled-out frontend plugin (workspace with a `coverage-anchors/` directory) from its released OCI image to the instrumented `__coverage` variant, so a coverage-dedicated nightly run can collect browser coverage. Gated separately from the ambient `E2E_COLLECT_COVERAGE` so the functional nightly's resolution is unchanged; `{{inherit}}` (DPDY) plugins are never swapped.
9+
- **Nightly coverage image swap** (`E2E_NIGHTLY_COVERAGE`): In nightly plugin resolution, an explicit opt-in points a rolled-out frontend plugin (workspace with a `coverage-anchors/` directory) at the overlay's instrumented `__coverage` ghcr build so a coverage-dedicated nightly run can collect browser coverage. This includes DPDY plugins: in a coverage run they bypass `{{inherit}}` (the Konflux catalog image, which can't be instrumented) and use the ghcr build of the same source. Gated separately from the ambient `E2E_COLLECT_COVERAGE`, so the functional nightly's resolution is unchanged — it still deploys the shipped `{{inherit}}`/Konflux builds.
1010

1111
## [2.1.0]
1212

docs/overlay/reference/plugin-metadata-resolution.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,16 +106,20 @@ For each plugin, the resolver checks in order:
106106
No ↓
107107
108108
3. Is nightly mode AND plugin is in default.packages.yaml AND metadata spec.dynamicArtifact is OCI?
109-
Yes → use {{inherit}} tag: oci://{registry}/plugin:{{inherit}}
109+
Yes ↓
110+
In a coverage run (E2E_NIGHTLY_COVERAGE=true), a rolled-out frontend plugin
111+
bypasses {{inherit}} and uses the ghcr __coverage build instead (see step 4),
112+
because the {{inherit}}/Konflux catalog image can't be instrumented.
113+
Otherwise → use {{inherit}} tag: oci://{registry}/plugin:{{inherit}}
110114
RHDH resolves both the OCI tag (version) and default config from its built-in DPDY.
111115
Registry: NIGHTLY_DPDY_OCI_REGISTRY_MAP > NIGHTLY_DPDY_OCI_REGISTRY > default registry.access.redhat.com/rhdh
112116
No ↓
113117
114118
4. Use metadata's dynamicArtifact as-is
115119
(OCI ref → OCI ref, wrapper path → wrapper path)
116-
In a dedicated coverage run (E2E_NIGHTLY_COVERAGE=true), a frontend plugin
117-
whose workspace is rolled out (has a coverage-anchors/ directory) gets its
118-
OCI tag swapped to the instrumented __coverage variant:
120+
In a coverage run (E2E_NIGHTLY_COVERAGE=true), a rolled-out frontend plugin
121+
(workspace has a coverage-anchors/ directory) gets its OCI tag swapped to the
122+
instrumented __coverage variant:
119123
oci://ghcr.io/.../plugin:bs_X__Y!alias → ...:bs_X__Y__coverage!alias
120124
```
121125

@@ -125,7 +129,9 @@ Metadata is the source of truth for the package reference, except for plugins in
125129

126130
A nightly run can only collect browser coverage if RHDH deploys the **instrumented** `__coverage` plugin image (built by the overlay release publish). When `E2E_NIGHTLY_COVERAGE=true`, step 4 above swaps a rolled-out frontend plugin's released OCI tag to its `__coverage` variant.
127131

128-
This is a separate flag from the ambient `E2E_COLLECT_COVERAGE` (which only toggles the collector fixture) on purpose: the functional nightly runs with `E2E_COLLECT_COVERAGE=true` by default, and the `__coverage` variant is built non-fatally, so swapping there could point at a tag that doesn't exist and break the deployment. The explicit `E2E_NIGHTLY_COVERAGE` opt-in keeps the functional nightly's resolution unchanged; only a coverage-dedicated run (which ensures the images exist) sets it. Plugins resolved via `{{inherit}}` are never swapped — those are RHDH's catalog images, which can't be instrumented.
132+
This is a separate flag from the ambient `E2E_COLLECT_COVERAGE` (which only toggles the collector fixture) on purpose: the functional nightly runs with `E2E_COLLECT_COVERAGE=true` by default, and the `__coverage` variant is built non-fatally, so swapping there could point at a tag that doesn't exist and break the deployment. The explicit `E2E_NIGHTLY_COVERAGE` opt-in keeps the functional nightly's resolution unchanged; only a coverage-dedicated run (which ensures the images exist) sets it.
133+
134+
In a coverage run this also applies to **DPDY plugins**: instead of resolving to `{{inherit}}` (the Konflux catalog image at `registry.access.redhat.com/rhdh`, which can't be instrumented), a rolled-out frontend DPDY plugin is pointed at the overlay's instrumented ghcr `__coverage` build of the same plugin source. The functional nightly still uses `{{inherit}}` and tests the shipped Konflux build — the coverage run is a separate measurement that deliberately deploys the instrumentable ghcr build instead. No downstream/Konflux pipeline changes are needed.
129135

130136
## Resolution Scenarios
131137

src/utils/plugin-metadata.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,17 @@ async function resolvePluginPackages(
530530
dpdyPackages?.has(metadata.packageName) &&
531531
metadata.packagePath.startsWith("oci://")
532532
) {
533+
// In a coverage run, a {{inherit}} ref would deploy the Konflux catalog
534+
// image (registry.access.redhat.com/rhdh), which we can't instrument.
535+
// Bypass it and deploy the overlay's instrumented __coverage build from
536+
// ghcr (metadata.packagePath) — same plugin source, just built by us, so
537+
// the run can collect coverage. The functional nightly (no coverage
538+
// opt-in) still uses {{inherit}}, i.e. the shipped Konflux build.
539+
if (coverageSwap && metadata.role === "frontend-plugin") {
540+
const resolved = toCoverageImageRef(metadata.packagePath);
541+
console.log(`[PluginMetadata] DPDY coverage: ${pkg}${resolved}`);
542+
return { ...plugin, package: resolved };
543+
}
533544
const registry = getDpdyRegistry(metadata.packageName);
534545
const inheritRef = `oci://${registry}/${displayName}:{{inherit}}`;
535546
console.log(`[PluginMetadata] DPDY inherit: ${pkg}${inheritRef}`);

src/utils/tests/plugin-metadata.nightly.test.ts

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,11 +1076,10 @@ describe("processPluginsForDeployment — nightly coverage swap", () => {
10761076
}
10771077
});
10781078

1079-
it("does not swap a DPDY plugin (resolves to {{inherit}}) even when opted in", async () => {
1080-
// Safety guarantee: a plugin in default.packages.yaml resolves via the
1081-
// {{inherit}} branch (RHDH's catalog image, which we can't instrument), so
1082-
// the coverage swap must never reach it — otherwise the nightly would point
1083-
// at a __coverage tag that doesn't exist.
1079+
it("swaps a DPDY plugin to the ghcr __coverage build (bypassing {{inherit}}) when opted in", async () => {
1080+
// A {{inherit}} ref would deploy the Konflux catalog image, which can't be
1081+
// instrumented. In a coverage run, a rolled-out frontend DPDY plugin must
1082+
// instead use the overlay's instrumented ghcr build of the same source.
10841083
process.env.E2E_NIGHTLY_COVERAGE = "true";
10851084
const metadataDir = await createCoverageWorkspace({
10861085
rolledOut: true,
@@ -1094,12 +1093,32 @@ describe("processPluginsForDeployment — nightly coverage swap", () => {
10941093
);
10951094
assert.strictEqual(
10961095
result.plugins![0].package,
1097-
"oci://registry.access.redhat.com/rhdh/red-hat-developer-hub-backstage-plugin-theme:{{inherit}}",
1098-
"DPDY plugin must resolve to {{inherit}}, never to a __coverage tag",
1096+
"oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-theme:bs_1.49.4__0.14.5__coverage!red-hat-developer-hub-backstage-plugin-theme",
1097+
"DPDY plugin in a coverage run must use the ghcr __coverage build, not {{inherit}}",
10991098
);
1100-
assert.ok(
1101-
!result.plugins![0].package.includes("__coverage"),
1102-
"{{inherit}} ref must not be swapped to a __coverage image",
1099+
} finally {
1100+
await fs.remove(path.resolve(metadataDir, ".."));
1101+
}
1102+
});
1103+
1104+
it("keeps a DPDY plugin on {{inherit}} in the functional nightly (no opt-in)", async () => {
1105+
// The functional nightly (no E2E_NIGHTLY_COVERAGE) must still deploy the
1106+
// shipped Konflux build via {{inherit}} — unchanged from today.
1107+
delete process.env.E2E_NIGHTLY_COVERAGE;
1108+
const metadataDir = await createCoverageWorkspace({
1109+
rolledOut: true,
1110+
role: "frontend-plugin",
1111+
});
1112+
try {
1113+
const result = await processPluginsForDeployment(
1114+
config,
1115+
metadataDir,
1116+
new Set(["@red-hat-developer-hub/backstage-plugin-theme"]), // in DPDY
1117+
);
1118+
assert.strictEqual(
1119+
result.plugins![0].package,
1120+
"oci://registry.access.redhat.com/rhdh/red-hat-developer-hub-backstage-plugin-theme:{{inherit}}",
1121+
"functional nightly must keep DPDY plugins on {{inherit}}",
11031122
);
11041123
} finally {
11051124
await fs.remove(path.resolve(metadataDir, ".."));

0 commit comments

Comments
 (0)