@@ -231,34 +231,32 @@ export function getMetadataDirectory(
231231 */
232232export 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+ return {
255+ packagePath,
256+ pluginConfig : pluginConfig || { } ,
257+ packageName,
258+ sourceFile : filePath ,
259+ } ;
262260}
263261
264262/**
@@ -282,14 +280,11 @@ export async function parseAllMetadataFiles(
282280
283281 for ( const file of files ) {
284282 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- }
283+ const pluginName = extractPluginName ( metadata . packagePath ) ;
284+ metadataMap . set ( pluginName , metadata ) ;
285+ console . log (
286+ `[PluginMetadata] Mapped plugin: ${ pluginName } <- ${ metadata . packagePath } ` ,
287+ ) ;
293288 }
294289
295290 console . log (
@@ -318,6 +313,48 @@ export interface DynamicPluginsConfig {
318313 [ key : string ] : unknown ;
319314}
320315
316+ /**
317+ * Replaces local package paths with OCI URLs for plugins that have matching metadata.
318+ * Only applies to plugins with metadata (workspace plugins). Plugins without metadata
319+ * (e.g., keycloak, bulk-import from auth defaults) keep their original paths.
320+ *
321+ * @param plugins The plugin entries to process
322+ * @param metadataMap Map of plugin names to plugin metadata
323+ * @param metadataPath Path to the metadata directory (used to resolve workspace root)
324+ * @returns Plugin entries with OCI URLs replaced where applicable
325+ */
326+ async function replaceWithOCIUrls (
327+ plugins : PluginEntry [ ] ,
328+ metadataMap : Map < string , PluginMetadata > ,
329+ metadataPath : string ,
330+ ) : Promise < PluginEntry [ ] > {
331+ const prNumber = process . env . GIT_PR_NUMBER ;
332+ if ( ! prNumber ) {
333+ return plugins ;
334+ }
335+
336+ console . log (
337+ `[PluginMetadata] PR build detected (PR #${ prNumber } ), fetching OCI URLs...` ,
338+ ) ;
339+ const workspacePath = path . resolve ( metadataPath , ".." ) ;
340+ const ociUrls = await getOCIUrlsForPR ( workspacePath , prNumber ) ;
341+
342+ return plugins . map ( ( plugin ) => {
343+ const pluginName = extractPluginName ( plugin . package ) ;
344+ const metadata = metadataMap . get ( pluginName ) ;
345+ if ( ! metadata ?. packageName ) return plugin ;
346+
347+ const displayName = metadata . packageName
348+ . replace ( / ^ @ / , "" )
349+ . replace ( / \/ / g, "-" ) ;
350+ const ociUrl = ociUrls . get ( displayName ) ;
351+ if ( ! ociUrl ) return plugin ;
352+
353+ console . log ( `[PluginMetadata] Replacing ${ plugin . package } with ${ ociUrl } ` ) ;
354+ return { ...plugin , package : ociUrl } ;
355+ } ) ;
356+ }
357+
321358/**
322359 * Injects plugin configurations from metadata into a dynamic plugins config.
323360 * Metadata config serves as the base, user-provided pluginConfig overrides it.
@@ -417,60 +454,24 @@ export async function generateDynamicPluginsConfigFromMetadata(
417454 ) ;
418455 }
419456
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-
431457 // Build plugin entries from metadata
432- const plugins : PluginEntry [ ] = [ ] ;
458+ let plugins : PluginEntry [ ] = [ ] ;
433459
434460 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-
463461 console . log (
464- `[PluginMetadata] Adding plugin: ${ pluginName } (${ packageRef } )` ,
462+ `[PluginMetadata] Adding plugin: ${ pluginName } (${ metadata . packagePath } )` ,
465463 ) ;
466464
467465 plugins . push ( {
468- package : packageRef ,
466+ package : metadata . packagePath ,
469467 disabled : false ,
470468 pluginConfig : metadata . pluginConfig ,
471469 } ) ;
472470 }
473471
472+ // Replace local paths with OCI URLs for PR builds
473+ plugins = await replaceWithOCIUrls ( plugins , metadataMap , metadataPath ) ;
474+
474475 console . log (
475476 `[PluginMetadata] Generated dynamic-plugins config with ${ plugins . length } plugins` ,
476477 ) ;
@@ -517,5 +518,16 @@ export async function loadAndInjectPluginMetadata(
517518 }
518519
519520 // Inject metadata configs into the dynamic plugins config
520- return injectMetadataConfig ( dynamicPluginsConfig , metadataMap ) ;
521+ const result = injectMetadataConfig ( dynamicPluginsConfig , metadataMap ) ;
522+
523+ // Replace local paths with OCI URLs for PR builds
524+ if ( result . plugins ) {
525+ result . plugins = await replaceWithOCIUrls (
526+ result . plugins ,
527+ metadataMap ,
528+ metadataPath ,
529+ ) ;
530+ }
531+
532+ return result ;
521533}
0 commit comments