Skip to content

Commit b2a378a

Browse files
fix: add OCI URL replacement for user-provided dynamic-plugins config
1 parent 37eb4db commit b2a378a

7 files changed

Lines changed: 100 additions & 81 deletions

File tree

docs/.vitepress/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export default defineConfig({
3333
{ text: "Examples", link: "/examples/" },
3434
{ text: "Overlay Testing", link: "/overlay/" },
3535
{
36-
text: "v1.1.8",
36+
text: "v1.1.9",
3737
items: [{ text: "Changelog", link: "/changelog" }],
3838
},
3939
],

docs/changelog.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22

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

5-
## [1.1.8] - Current
5+
## [1.1.9] - Current
6+
7+
### Fixed
8+
- **OCI URL replacement with user-provided `dynamic-plugins.yaml`**: When a workspace provides its own `dynamic-plugins.yaml`, plugin package paths were not replaced with OCI URLs for PR builds. Extracted shared `replaceWithOCIUrls()` function so both `generateDynamicPluginsConfigFromMetadata()` and `loadAndInjectPluginMetadata()` code paths now perform OCI replacement when `GIT_PR_NUMBER` is set.
9+
10+
## [1.1.8]
611

712
### Fixed
813
- Fixed namespace deletion race condition during test retries

docs/overlay/examples/basic-plugin.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ workspaces/<plugin>/e2e-tests/
6767
"eslint-plugin-check-file": "^3.3.1",
6868
"eslint-plugin-playwright": "^2.4.0",
6969
"prettier": "^3.7.4",
70-
"rhdh-e2e-test-utils": "1.1.8",
70+
"rhdh-e2e-test-utils": "1.1.9",
7171
"typescript": "^5.9.3",
7272
"typescript-eslint": "^8.50.0"
7373
}

docs/overlay/examples/tech-radar.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ workspaces/tech-radar/e2e-tests/
7070
"eslint-plugin-check-file": "^3.3.1",
7171
"eslint-plugin-playwright": "^2.4.0",
7272
"prettier": "^3.7.4",
73-
"rhdh-e2e-test-utils": "1.1.8",
73+
"rhdh-e2e-test-utils": "1.1.9",
7474
"typescript": "^5.9.3",
7575
"typescript-eslint": "^8.50.0"
7676
}

docs/overlay/test-structure/directory-layout.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ Defines the test package with dependencies and scripts:
6767
"eslint-plugin-check-file": "^3.3.1",
6868
"eslint-plugin-playwright": "^2.4.0",
6969
"prettier": "^3.7.4",
70-
"rhdh-e2e-test-utils": "1.1.8",
70+
"rhdh-e2e-test-utils": "1.1.9",
7171
"typescript": "^5.9.3",
7272
"typescript-eslint": "^8.50.0"
7373
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "rhdh-e2e-test-utils",
3-
"version": "1.1.8",
3+
"version": "1.1.9",
44
"description": "Test utilities for RHDH E2E tests",
55
"license": "Apache-2.0",
66
"type": "module",

src/utils/plugin-metadata.ts

Lines changed: 89 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -231,34 +231,34 @@ export function getMetadataDirectory(
231231
*/
232232
export async function parseMetadataFile(
233233
filePath: string,
234-
): Promise<PluginMetadata | null> {
235-
try {
236-
const content = await fs.readFile(filePath, "utf8");
237-
const parsed = yaml.load(content) as PackageCRD;
234+
): Promise<PluginMetadata> {
235+
const content = await fs.readFile(filePath, "utf8");
236+
const parsed = yaml.load(content) as PackageCRD;
238237

239-
const packagePath = parsed?.spec?.dynamicArtifact;
240-
const packageName = parsed?.spec?.packageName;
241-
const pluginConfig = parsed?.spec?.appConfigExamples?.[0]?.content;
238+
const packagePath = parsed?.spec?.dynamicArtifact;
239+
const packageName = parsed?.spec?.packageName;
240+
const pluginConfig = parsed?.spec?.appConfigExamples?.[0]?.content;
242241

243-
if (!packagePath) {
244-
console.log(
245-
`[PluginMetadata] Skipping ${filePath}: no spec.dynamicArtifact`,
246-
);
247-
return null;
248-
}
249-
250-
console.log(`[PluginMetadata] Loaded metadata for: ${packagePath}`);
242+
if (!packagePath) {
243+
throw new Error(
244+
`[PluginMetadata] Missing required field spec.dynamicArtifact in ${filePath}`,
245+
);
246+
}
251247

252-
return {
253-
packagePath,
254-
pluginConfig: pluginConfig || {},
255-
packageName: packageName || "",
256-
sourceFile: filePath,
257-
};
258-
} catch (error) {
259-
console.error(`[PluginMetadata] Error parsing ${filePath}:`, error);
260-
return null;
248+
if (!packageName) {
249+
throw new Error(
250+
`[PluginMetadata] Missing required field spec.packageName in ${filePath}`,
251+
);
261252
}
253+
254+
console.log(`[PluginMetadata] Loaded metadata for: ${packagePath}`);
255+
256+
return {
257+
packagePath,
258+
pluginConfig: pluginConfig || {},
259+
packageName,
260+
sourceFile: filePath,
261+
};
262262
}
263263

264264
/**
@@ -282,14 +282,11 @@ export async function parseAllMetadataFiles(
282282

283283
for (const file of files) {
284284
const metadata = await parseMetadataFile(file);
285-
if (metadata) {
286-
// Use extracted plugin name as key for flexible matching
287-
const pluginName = extractPluginName(metadata.packagePath);
288-
metadataMap.set(pluginName, metadata);
289-
console.log(
290-
`[PluginMetadata] Mapped plugin: ${pluginName} <- ${metadata.packagePath}`,
291-
);
292-
}
285+
const pluginName = extractPluginName(metadata.packagePath);
286+
metadataMap.set(pluginName, metadata);
287+
console.log(
288+
`[PluginMetadata] Mapped plugin: ${pluginName} <- ${metadata.packagePath}`,
289+
);
293290
}
294291

295292
console.log(
@@ -318,6 +315,48 @@ export interface DynamicPluginsConfig {
318315
[key: string]: unknown;
319316
}
320317

318+
/**
319+
* Replaces local package paths with OCI URLs for plugins that have matching metadata.
320+
* Only applies to plugins with metadata (workspace plugins). Plugins without metadata
321+
* (e.g., keycloak, bulk-import from auth defaults) keep their original paths.
322+
*
323+
* @param plugins The plugin entries to process
324+
* @param metadataMap Map of plugin names to plugin metadata
325+
* @param metadataPath Path to the metadata directory (used to resolve workspace root)
326+
* @returns Plugin entries with OCI URLs replaced where applicable
327+
*/
328+
async function replaceWithOCIUrls(
329+
plugins: PluginEntry[],
330+
metadataMap: Map<string, PluginMetadata>,
331+
metadataPath: string,
332+
): Promise<PluginEntry[]> {
333+
const prNumber = process.env.GIT_PR_NUMBER;
334+
if (!prNumber) {
335+
return plugins;
336+
}
337+
338+
console.log(
339+
`[PluginMetadata] PR build detected (PR #${prNumber}), fetching OCI URLs...`,
340+
);
341+
const workspacePath = path.resolve(metadataPath, "..");
342+
const ociUrls = await getOCIUrlsForPR(workspacePath, prNumber);
343+
344+
return plugins.map((plugin) => {
345+
const pluginName = extractPluginName(plugin.package);
346+
const metadata = metadataMap.get(pluginName);
347+
if (!metadata?.packageName) return plugin;
348+
349+
const displayName = metadata.packageName
350+
.replace(/^@/, "")
351+
.replace(/\//g, "-");
352+
const ociUrl = ociUrls.get(displayName);
353+
if (!ociUrl) return plugin;
354+
355+
console.log(`[PluginMetadata] Replacing ${plugin.package} with ${ociUrl}`);
356+
return { ...plugin, package: ociUrl };
357+
});
358+
}
359+
321360
/**
322361
* Injects plugin configurations from metadata into a dynamic plugins config.
323362
* Metadata config serves as the base, user-provided pluginConfig overrides it.
@@ -417,60 +456,24 @@ export async function generateDynamicPluginsConfigFromMetadata(
417456
);
418457
}
419458

420-
// If PR number is set, fetch OCI URLs
421-
const prNumber = process.env.GIT_PR_NUMBER;
422-
let ociUrls: Map<string, string> | null = null;
423-
if (prNumber) {
424-
console.log(
425-
`[PluginMetadata] PR build detected (PR #${prNumber}), fetching OCI URLs...`,
426-
);
427-
const workspacePath = path.resolve(metadataPath, "..");
428-
ociUrls = await getOCIUrlsForPR(workspacePath, prNumber);
429-
}
430-
431459
// Build plugin entries from metadata
432-
const plugins: PluginEntry[] = [];
460+
let plugins: PluginEntry[] = [];
433461

434462
for (const [pluginName, metadata] of metadataMap) {
435-
let packageRef = metadata.packagePath;
436-
437-
// Replace with OCI URL if available (required for PR builds)
438-
if (ociUrls) {
439-
if (!metadata.packageName) {
440-
throw new Error(
441-
`[PluginMetadata] PR build requires packageName in metadata but not found for: ${pluginName}\n` +
442-
` Source file: ${metadata.sourceFile}`,
443-
);
444-
}
445-
446-
const displayName = metadata.packageName
447-
.replace(/^@/, "")
448-
.replace(/\//g, "-");
449-
const ociUrl = ociUrls.get(displayName);
450-
451-
if (!ociUrl) {
452-
throw new Error(
453-
`[PluginMetadata] PR build requires OCI URL but not found for: ${displayName}\n` +
454-
` Package name: ${metadata.packageName}\n` +
455-
` Source file: ${metadata.sourceFile}`,
456-
);
457-
}
458-
459-
console.log(`[PluginMetadata] Replacing ${packageRef} with ${ociUrl}`);
460-
packageRef = ociUrl;
461-
}
462-
463463
console.log(
464-
`[PluginMetadata] Adding plugin: ${pluginName} (${packageRef})`,
464+
`[PluginMetadata] Adding plugin: ${pluginName} (${metadata.packagePath})`,
465465
);
466466

467467
plugins.push({
468-
package: packageRef,
468+
package: metadata.packagePath,
469469
disabled: false,
470470
pluginConfig: metadata.pluginConfig,
471471
});
472472
}
473473

474+
// Replace local paths with OCI URLs for PR builds
475+
plugins = await replaceWithOCIUrls(plugins, metadataMap, metadataPath);
476+
474477
console.log(
475478
`[PluginMetadata] Generated dynamic-plugins config with ${plugins.length} plugins`,
476479
);
@@ -517,5 +520,16 @@ export async function loadAndInjectPluginMetadata(
517520
}
518521

519522
// Inject metadata configs into the dynamic plugins config
520-
return injectMetadataConfig(dynamicPluginsConfig, metadataMap);
523+
const result = injectMetadataConfig(dynamicPluginsConfig, metadataMap);
524+
525+
// Replace local paths with OCI URLs for PR builds
526+
if (result.plugins) {
527+
result.plugins = await replaceWithOCIUrls(
528+
result.plugins,
529+
metadataMap,
530+
metadataPath,
531+
);
532+
}
533+
534+
return result;
521535
}

0 commit comments

Comments
 (0)