From 3dd34753262615f76017ce25cc732fcebd239723 Mon Sep 17 00:00:00 2001 From: Subhash Khileri Date: Tue, 21 Apr 2026 12:11:16 +0530 Subject: [PATCH] fix: normalize -dynamic suffix in extractPluginName for PR OCI resolution Plugins with local-path dynamicArtifact (ending in -dynamic) were not resolved to PR OCI images because the metadata map key included the -dynamic suffix while OCI URL lookups did not. extractPluginName now strips the suffix so both paths produce the same key. RHDHBUGS-2987 --- docs/changelog.md | 8 ++- package.json | 2 +- src/utils/plugin-metadata.ts | 8 ++- src/utils/tests/plugin-metadata.pr.test.ts | 77 ++++++++++++++++++++++ src/utils/tests/plugin-metadata.test.ts | 4 +- 5 files changed, 92 insertions(+), 7 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index bf7ced9..ccace52 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,7 +2,13 @@ All notable changes to this project will be documented in this file. -## [1.1.31] - Current +## [1.1.32] - Current + +### Fixed + +- **Normalize `-dynamic` suffix in `extractPluginName`**: Plugins whose metadata `dynamicArtifact` is a local path (ending in `-dynamic`) were not matched during PR OCI resolution or config injection, because the metadata map key included the `-dynamic` suffix while OCI URL lookups did not. `extractPluginName` now strips the `-dynamic` suffix so local paths and OCI refs for the same logical plugin produce the same key. ([RHDHBUGS-2987](https://issues.redhat.com/browse/RHDHBUGS-2987)) + +## [1.1.31] ### Fixed diff --git a/package.json b/package.json index 88a80e3..b380d9c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@red-hat-developer-hub/e2e-test-utils", - "version": "1.1.31", + "version": "1.1.32", "description": "Test utilities for RHDH E2E tests", "license": "Apache-2.0", "repository": { diff --git a/src/utils/plugin-metadata.ts b/src/utils/plugin-metadata.ts index aadef6a..398574e 100644 --- a/src/utils/plugin-metadata.ts +++ b/src/utils/plugin-metadata.ts @@ -79,16 +79,18 @@ export function isNightlyJob(): boolean { /** * Extracts the plugin name from a package path or OCI reference. + * Strips the `-dynamic` suffix so local paths and OCI refs for the same + * logical plugin produce the same key. * * Handles various formats: - * - Local path: ./dynamic-plugins/dist/backstage-community-plugin-tech-radar + * - Local path: ./dynamic-plugins/dist/backstage-community-plugin-tech-radar-dynamic * - OCI with alias: oci://quay.io/rhdh/plugin@sha256:...!backstage-community-plugin-tech-radar * - OCI without alias: oci://quay.io/rhdh/backstage-community-plugin-tech-radar:tag */ export function extractPluginName(packageRef: string): string { const ref = packageRef.includes("!") ? packageRef.split("!")[0] : packageRef; const match = ref.match(/\/([^/:@]+)(?:[:@].*)?$/); - return match?.[1] || packageRef; + return (match?.[1] || packageRef).replace(/-dynamic$/, ""); } /** @@ -346,7 +348,7 @@ export function getNormalizedPluginMergeKey(entry: { if (pkg === undefined || pkg === "") { return ""; } - return extractPluginName(pkg).replace(/-dynamic$/, ""); + return extractPluginName(pkg); } async function resolvePluginPackages( diff --git a/src/utils/tests/plugin-metadata.pr.test.ts b/src/utils/tests/plugin-metadata.pr.test.ts index c4b7fc2..e939ce8 100644 --- a/src/utils/tests/plugin-metadata.pr.test.ts +++ b/src/utils/tests/plugin-metadata.pr.test.ts @@ -362,6 +362,83 @@ describe("processPluginsForDeployment — PR mode", () => { } }); + // ── -dynamic suffix normalization ──────────────────────────────────────── + + describe("-dynamic suffix normalization", () => { + it("resolves OCI plugin to metadata when dynamicArtifact has -dynamic suffix", async () => { + const metadataDir = await createMetadataFixture([ + { + name: "backstage-plugin-catalog-backend-module-github", + packageName: "@backstage/plugin-catalog-backend-module-github", + dynamicArtifact: + "./dynamic-plugins/dist/backstage-plugin-catalog-backend-module-github-dynamic", + appConfigExamples: { + catalog: { providers: { github: { org: "test" } } }, + }, + }, + ]); + + try { + const config: DynamicPluginsConfig = { + plugins: [ + { + package: + "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/backstage-plugin-catalog-backend-module-github:bs_1.45.3__0.11.2", + disabled: false, + }, + ], + }; + + const result = await processPluginsForDeployment(config, metadataDir); + + assert.ok( + result.plugins![0].pluginConfig, + "metadata config must be injected even when dynamicArtifact has -dynamic suffix but OCI URL does not", + ); + assert.deepStrictEqual( + result.plugins![0].pluginConfig, + { catalog: { providers: { github: { org: "test" } } } }, + "injected config must match metadata appConfigExamples", + ); + } finally { + await fs.remove(metadataDir); + } + }); + + it("keeps local -dynamic path unchanged when metadata also has -dynamic", async () => { + const metadataDir = await createMetadataFixture([ + { + name: "backstage-plugin-catalog-backend-module-github", + packageName: "@backstage/plugin-catalog-backend-module-github", + dynamicArtifact: + "./dynamic-plugins/dist/backstage-plugin-catalog-backend-module-github-dynamic", + }, + ]); + + try { + const config: DynamicPluginsConfig = { + plugins: [ + { + package: + "./dynamic-plugins/dist/backstage-plugin-catalog-backend-module-github-dynamic", + disabled: false, + }, + ], + }; + + const result = await processPluginsForDeployment(config, metadataDir); + + assert.strictEqual( + result.plugins![0].package, + "./dynamic-plugins/dist/backstage-plugin-catalog-backend-module-github-dynamic", + "local path with -dynamic must stay unchanged", + ); + } finally { + await fs.remove(metadataDir); + } + }); + }); + // ── PR vs nightly precedence ──────────────────────────────────────────── describe("PR vs nightly precedence", () => { diff --git a/src/utils/tests/plugin-metadata.test.ts b/src/utils/tests/plugin-metadata.test.ts index 7dbb4dc..6267acd 100644 --- a/src/utils/tests/plugin-metadata.test.ts +++ b/src/utils/tests/plugin-metadata.test.ts @@ -43,12 +43,12 @@ describe("extractPluginName", () => { ); }); - it("extracts name from local path", () => { + it("extracts name from local path and strips -dynamic suffix", () => { assert.strictEqual( extractPluginName( "./dynamic-plugins/dist/backstage-community-plugin-keycloak-dynamic", ), - "backstage-community-plugin-keycloak-dynamic", + "backstage-community-plugin-keycloak", ); });