rhdh-e2e-test-utils 1.1.4 → 1.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../../src/deployment/keycloak/deployment.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAY1E,OAAO,KAAK,EACV,yBAAyB,EACzB,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,wBAAwB,EACzB,MAAM,YAAY,CAAC;AAEpB,qBAAa,cAAc;IAClB,SAAS,yBAAgC;IACzC,gBAAgB,EAAE,wBAAwB,CAAC;IAC3C,WAAW,EAAE,MAAM,CAAM;IACzB,KAAK,EAAE,MAAM,CAAM;IACnB,QAAQ,EAAE,MAAM,CAAM;IACtB,YAAY,EAAE,MAAM,CAAM;IACjC,OAAO,CAAC,YAAY,CAAoC;gBAE5C,OAAO,GAAE,yBAA8B;IAInD;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAa7B;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAUnC;;OAEG;IACG,gBAAgB,CAAC,OAAO,CAAC,EAAE;QAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;QACvC,MAAM,CAAC,EAAE,mBAAmB,EAAE,CAAC;QAC/B,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAC;KAC9B,GAAG,OAAO,CAAC,IAAI,CAAC;IAsCjB;;OAEG;IACG,OAAO,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB9D;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB7D;;OAEG;IACG,YAAY,CAChB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,IAAI,CAAC;IAqChB;;OAEG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB5E;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IA8C1E;;OAEG;IACG,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAe5D;;OAEG;IACG,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAQ9D;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWhE;;OAEG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYlE;;OAEG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW/C;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAK/B;;OAEG;IACG,cAAc,CAAC,OAAO,GAAE,MAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAW1D,OAAO,CAAC,sBAAsB;YAahB,eAAe;YAWf,YAAY;IAwBpB,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;YAO3B,gBAAgB;YAoBhB,sBAAsB;YActB,kBAAkB;YAQlB,0BAA0B;YAmD1B,eAAe;IAqB7B,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,IAAI;CAGb"}
1
+ {"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../../src/deployment/keycloak/deployment.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAY1E,OAAO,KAAK,EACV,yBAAyB,EACzB,wBAAwB,EACxB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,wBAAwB,EACzB,MAAM,YAAY,CAAC;AAEpB,qBAAa,cAAc;IAClB,SAAS,yBAAgC;IACzC,gBAAgB,EAAE,wBAAwB,CAAC;IAC3C,WAAW,EAAE,MAAM,CAAM;IACzB,KAAK,EAAE,MAAM,CAAM;IACnB,QAAQ,EAAE,MAAM,CAAM;IACtB,YAAY,EAAE,MAAM,CAAM;IACjC,OAAO,CAAC,YAAY,CAAoC;gBAE5C,OAAO,GAAE,yBAA8B;IAInD;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAa7B;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAUnC;;OAEG;IACG,gBAAgB,CAAC,OAAO,CAAC,EAAE;QAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;QACvC,MAAM,CAAC,EAAE,mBAAmB,EAAE,CAAC;QAC/B,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAC;KAC9B,GAAG,OAAO,CAAC,IAAI,CAAC;IAsCjB;;OAEG;IACG,OAAO,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB9D;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB7D;;OAEG;IACG,YAAY,CAChB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,IAAI,CAAC;IAqChB;;OAEG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB5E;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IA8C1E;;OAEG;IACG,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAe5D;;OAEG;IACG,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAQ9D;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAWhE;;OAEG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYlE;;OAEG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW/C;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAK/B;;OAEG;IACG,cAAc,CAAC,OAAO,GAAE,MAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAY1D,OAAO,CAAC,sBAAsB;YAahB,eAAe;YAWf,YAAY;IAwBpB,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;YAO3B,gBAAgB;YAoBhB,sBAAsB;YActB,kBAAkB;YAQlB,0BAA0B;YAmD1B,eAAe;IAqB7B,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,IAAI;CAGb"}
@@ -295,7 +295,8 @@ export class KeycloakHelper {
295
295
  */
296
296
  async waitUntilReady(timeout = 300) {
297
297
  this._log(`Waiting for Keycloak to be ready...`);
298
- await this.k8sClient.waitForStatefulSetReady(this.deploymentConfig.namespace, this.deploymentConfig.releaseName, timeout);
298
+ const labelSelector = `app.kubernetes.io/instance=${this.deploymentConfig.releaseName}`;
299
+ await this.k8sClient.waitForPodsWithFailureDetection(this.deploymentConfig.namespace, labelSelector, timeout);
299
300
  }
300
301
  // Private methods
301
302
  _buildDeploymentConfig(options) {
@@ -26,6 +26,11 @@ export declare class RHDHDeployment {
26
26
  waitUntilReady(timeout?: number): Promise<void>;
27
27
  teardown(): Promise<void>;
28
28
  private _resolveChartVersion;
29
+ /**
30
+ * Resolve the semantic version from the "next" tag by looking up the
31
+ * downstream image (rhdh-hub-rhel9) and finding tags with the same digest.
32
+ */
33
+ private _resolveVersionFromNextTag;
29
34
  private _buildDeploymentConfig;
30
35
  configure(deploymentOptions?: DeploymentOptions): Promise<void>;
31
36
  private _buildBaseUrl;
@@ -1 +1 @@
1
- {"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../../src/deployment/rhdh/deployment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAiB1E,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AAEpB,qBAAa,cAAc;IAClB,SAAS,yBAAgC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,gBAAgB,CAAC;gBAE9B,SAAS,EAAE,MAAM;IAUvB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;YAqBf,eAAe;YAgBf,aAAa;IAe3B;;;OAGG;YACW,0BAA0B;YAuC1B,oBAAoB;YAWpB,eAAe;YAmCf,mBAAmB;IA4B3B,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAWrC;;;;OAIG;IACG,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAOpC,cAAc,CAAC,OAAO,GAAE,MAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBpD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YAIjB,oBAAoB;IA6BlC,OAAO,CAAC,sBAAsB;IAoCxB,SAAS,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAcrE,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,SAAS;CAmBlB"}
1
+ {"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../../src/deployment/rhdh/deployment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAgB1E,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AAEpB,qBAAa,cAAc;IAClB,SAAS,yBAAgC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,gBAAgB,CAAC;gBAE9B,SAAS,EAAE,MAAM;IAUvB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;YAqBf,eAAe;YAgBf,aAAa;IAe3B;;;OAGG;YACW,0BAA0B;YAuC1B,oBAAoB;YAWpB,eAAe;YAmCf,mBAAmB;IAiD3B,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAWrC;;;;OAIG;IACG,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAOpC,cAAc,CAAC,OAAO,GAAE,MAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBpD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YAIjB,oBAAoB;IAsClC;;;OAGG;YACW,0BAA0B;IAwCxC,OAAO,CAAC,sBAAsB;IAoCxB,SAAS,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAcrE,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,SAAS;CAMlB"}
@@ -6,7 +6,6 @@ import { mergeYamlFilesIfExists, deepMerge } from "../../utils/merge-yamls.js";
6
6
  import { loadAndInjectPluginMetadata, generateDynamicPluginsConfigFromMetadata, } from "../../utils/plugin-metadata.js";
7
7
  import { envsubst } from "../../utils/common.js";
8
8
  import fs from "fs-extra";
9
- import boxen from "boxen";
10
9
  import { DEFAULT_CONFIG_PATHS, AUTH_CONFIG_PATHS, CHART_URL, } from "./constants.js";
11
10
  export class RHDHDeployment {
12
11
  k8sClient = new KubernetesClientHelper();
@@ -118,9 +117,25 @@ export class RHDHDeployment {
118
117
  ]);
119
118
  this._logBoxen("Subscription", subscriptionObject);
120
119
  fs.writeFileSync(`/tmp/${this.deploymentConfig.namespace}-subscription.yaml`, yaml.dump(subscriptionObject));
120
+ const version = this.deploymentConfig.version;
121
+ const isSemanticVersion = /^\d+(\.\d+)?$/.test(version);
122
+ // Use main branch for non-semantic versions (e.g., "next", "latest")
123
+ const branch = isSemanticVersion ? `release-${version}` : "main";
124
+ // Build version argument based on version type
125
+ let versionArg;
126
+ if (isSemanticVersion) {
127
+ versionArg = `-v ${version}`;
128
+ }
129
+ else if (version === "next") {
130
+ versionArg = "--next";
131
+ }
132
+ else {
133
+ throw new Error(`Invalid RHDH version "${version}". Use semantic version (e.g., "1.5") or "next".`);
134
+ }
135
+ this._log(`Using operator branch: ${branch}, version arg: ${versionArg}`);
121
136
  await $ `
122
137
  set -e;
123
- curl -s https://raw.githubusercontent.com/redhat-developer/rhdh-operator/refs/heads/release-${this.deploymentConfig.version}/.rhdh/scripts/install-rhdh-catalog-source.sh | bash -s -- -v ${this.deploymentConfig.version} --install-operator rhdh
138
+ curl -sf https://raw.githubusercontent.com/redhat-developer/rhdh-operator/refs/heads/${branch}/.rhdh/scripts/install-rhdh-catalog-source.sh | bash -s -- ${versionArg} --install-operator rhdh
124
139
 
125
140
  timeout 300 bash -c '
126
141
  while ! oc get crd/backstages.rhdh.redhat.com -n "${this.deploymentConfig.namespace}" >/dev/null 2>&1; do
@@ -153,49 +168,79 @@ export class RHDHDeployment {
153
168
  }
154
169
  async waitUntilReady(timeout = 300) {
155
170
  this._log(`Waiting for RHDH deployment to be ready in namespace ${this.deploymentConfig.namespace}...`);
171
+ const labelSelector = "app.kubernetes.io/instance in (redhat-developer-hub,developer-hub)";
156
172
  try {
157
- await $ `oc rollout status deployment -l 'app.kubernetes.io/instance in (redhat-developer-hub,developer-hub)' -n ${this.deploymentConfig.namespace} --timeout=${timeout}s`;
173
+ await this.k8sClient.waitForPodsWithFailureDetection(this.deploymentConfig.namespace, labelSelector, timeout);
158
174
  this._log(`RHDH deployment is ready in namespace ${this.deploymentConfig.namespace}`);
159
175
  }
160
176
  catch (error) {
161
- console.log("----------------------------------------------------------------");
162
- console.log("Deployment Failed Logs");
163
- console.log("----------------------------------------------------------------");
164
- await $ `oc logs -l 'app.kubernetes.io/instance in (redhat-developer-hub,developer-hub)' -n ${this.deploymentConfig.namespace} --tail=100`;
165
- throw new Error(`Error waiting for RHDH deployment to be ready in timeout ${timeout}s in namespace ${this.deploymentConfig.namespace}: ${error}`);
177
+ throw new Error(`RHDH deployment failed in namespace ${this.deploymentConfig.namespace}: ${error instanceof Error ? error.message : error}`);
166
178
  }
167
179
  }
168
180
  async teardown() {
169
181
  await this.k8sClient.deleteNamespace(this.deploymentConfig.namespace);
170
182
  }
171
183
  async _resolveChartVersion(version) {
172
- // Semantic versions (e.g., 1.2)
173
- if (/^(\d+(\.\d+)?)$/.test(version)) {
184
+ let resolvedVersion = version;
185
+ // Handle "next" tag by looking up the corresponding version from downstream image
186
+ if (version === "next") {
187
+ resolvedVersion = await this._resolveVersionFromNextTag();
188
+ this._log(`Resolved "next" tag to version: ${resolvedVersion}`);
189
+ }
190
+ // Semantic versions (e.g., 1.2, 1.10)
191
+ if (/^(\d+(\.\d+)?)$/.test(resolvedVersion)) {
174
192
  const response = await fetch("https://quay.io/api/v1/repository/rhdh/chart/tag/?onlyActiveTags=true&limit=600");
175
193
  if (!response.ok)
176
194
  throw new Error(`Failed to fetch chart versions: ${response.statusText}`);
177
195
  const data = (await response.json());
178
196
  const matching = data.tags
179
197
  .map((t) => t.name)
180
- .filter((name) => name.startsWith(`${version}-`))
198
+ .filter((name) => name.startsWith(`${resolvedVersion}-`))
181
199
  .sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
182
200
  const latest = matching.at(-1);
183
201
  if (!latest)
184
- throw new Error(`No chart version found for ${version}`);
202
+ throw new Error(`No chart version found for ${resolvedVersion}`);
185
203
  return latest;
186
204
  }
187
205
  // CI build versions (e.g., 1.2.3-CI)
188
- if (version.endsWith("CI"))
189
- return version;
206
+ if (resolvedVersion.endsWith("CI"))
207
+ return resolvedVersion;
190
208
  throw new Error(`Invalid Helm chart version format: "${version}"`);
191
209
  }
210
+ /**
211
+ * Resolve the semantic version from the "next" tag by looking up the
212
+ * downstream image (rhdh-hub-rhel9) and finding tags with the same digest.
213
+ */
214
+ async _resolveVersionFromNextTag() {
215
+ // Fetch all active tags in a single API call
216
+ const response = await fetch("https://quay.io/api/v1/repository/rhdh/rhdh-hub-rhel9/tag/?onlyActiveTags=true&limit=75");
217
+ if (!response.ok) {
218
+ throw new Error(`Failed to fetch image tags: ${response.statusText}`);
219
+ }
220
+ // Use Record to avoid snake_case linting issues with Quay API response
221
+ const data = (await response.json());
222
+ // Find the "next" tag and get its digest
223
+ const nextTag = data.tags.find((t) => t["name"] === "next");
224
+ if (!nextTag) {
225
+ throw new Error('No "next" tag found in rhdh-hub-rhel9 repository');
226
+ }
227
+ const digest = nextTag["manifest_digest"];
228
+ this._log(`"next" tag digest: ${digest}`);
229
+ // Find semantic version tag (e.g., "1.10") with the same digest
230
+ const semanticVersionTag = data.tags.find((t) => t["manifest_digest"] === digest &&
231
+ /^\d+\.\d+$/.test(t["name"]));
232
+ if (!semanticVersionTag) {
233
+ throw new Error(`Could not find semantic version tag for "next" (digest: ${digest})`);
234
+ }
235
+ return semanticVersionTag["name"];
236
+ }
192
237
  _buildDeploymentConfig(input) {
193
- const version = input.version ?? process.env.RHDH_VERSION;
194
- const method = input.method ?? process.env.INSTALLATION_METHOD;
195
- if (!version)
196
- throw new Error("RHDH version is required");
197
- if (!method)
198
- throw new Error("Installation method (helm/operator) is required");
238
+ // Default to "next" if RHDH_VERSION not set
239
+ const version = input.version ?? process.env.RHDH_VERSION ?? "next";
240
+ // Default to "helm" if INSTALLATION_METHOD not set
241
+ const method = input.method ??
242
+ process.env.INSTALLATION_METHOD ??
243
+ "helm";
199
244
  const base = {
200
245
  version,
201
246
  namespace: input.namespace ?? this.deploymentConfig.namespace,
@@ -243,20 +288,9 @@ export class RHDHDeployment {
243
288
  console.log("[RHDHDeployment]", ...args);
244
289
  }
245
290
  _logBoxen(title, data) {
246
- console.log(boxen(yaml.dump(data, { lineWidth: -1 }), {
247
- title,
248
- padding: 0,
249
- width: 120,
250
- borderStyle: {
251
- topLeft: "┌",
252
- topRight: "┐",
253
- bottomLeft: "└",
254
- bottomRight: "┘",
255
- top: "─",
256
- bottom: "─",
257
- left: "",
258
- right: "",
259
- },
260
- }));
291
+ const content = yaml.dump(data, { lineWidth: -1 });
292
+ console.log(`\n┌─ ${title} ${"─".repeat(60)}`);
293
+ console.log(content);
294
+ console.log(`└${"─".repeat(60 + title.length + 3)}\n`);
261
295
  }
262
296
  }
@@ -65,6 +65,24 @@ declare class KubernetesClientHelper {
65
65
  * Extract the URL from a route object
66
66
  */
67
67
  private _extractRouteUrl;
68
+ /**
69
+ * Failure states that indicate a pod will not recover without intervention
70
+ */
71
+ private static readonly failureReasons;
72
+ /**
73
+ * Wait for pods matching a label selector to be ready, with early failure detection.
74
+ * Fails fast when it detects unrecoverable states like CrashLoopBackOff.
75
+ *
76
+ * @param namespace - Namespace to watch
77
+ * @param labelSelector - Label selector (e.g., "app=myapp")
78
+ * @param timeoutSeconds - Maximum time to wait (default: 300)
79
+ * @param pollIntervalMs - How often to check pod status (default: 5000)
80
+ */
81
+ waitForPodsWithFailureDetection(namespace: string, labelSelector: string, timeoutSeconds?: number, pollIntervalMs?: number): Promise<void>;
82
+ /**
83
+ * Check if a pod is in a failure state. Returns failure info or null if healthy.
84
+ */
85
+ private _checkPodFailure;
68
86
  }
69
87
  export { KubernetesClientHelper };
70
88
  //# sourceMappingURL=kubernetes-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"kubernetes-client.d.ts","sourceRoot":"","sources":["../../src/utils/kubernetes-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,GAAG,MAAM,yBAAyB,CAAC;AAO/C;;GAEG;AACH,cAAM,sBAAsB;IAC1B,OAAO,CAAC,GAAG,CAAiB;IAC5B,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,iBAAiB,CAAuB;;IAoChD;;OAEG;IACG,uBAAuB,CAC3B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;IA8C3B;;OAEG;IACG,0BAA0B,CAC9B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;IA+B3B;;UAEM;IAmBN;;OAEG;IAsBH;;OAEG;YACW,YAAY;IA8B1B;;OAEG;IACG,wBAAwB,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;IA0ChB;;OAEG;IACG,qBAAqB,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,EAC7C,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;IAqChB;;OAEG;IACG,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BvD;;OAEG;IACG,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAc3E;;OAEG;IACG,uBAAuB,CAC3B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,cAAc,GAAE,MAAY,EAC5B,cAAc,GAAE,MAAa,GAC5B,OAAO,CAAC,OAAO,CAAC;IAiBnB;;;OAGG;IACG,uBAAuB,IAAI,OAAO,CAAC,MAAM,CAAC;IAsBhD;;;;;;OAMG;IACG,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAkBxE;;OAEG;IACH,OAAO,CAAC,gBAAgB;CAkBzB;AAED,OAAO,EAAE,sBAAsB,EAAE,CAAC"}
1
+ {"version":3,"file":"kubernetes-client.d.ts","sourceRoot":"","sources":["../../src/utils/kubernetes-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,GAAG,MAAM,yBAAyB,CAAC;AAO/C;;GAEG;AACH,cAAM,sBAAsB;IAC1B,OAAO,CAAC,GAAG,CAAiB;IAC5B,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,iBAAiB,CAAuB;;IAoChD;;OAEG;IACG,uBAAuB,CAC3B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;IA8C3B;;OAEG;IACG,0BAA0B,CAC9B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;IA+B3B;;UAEM;IAmBN;;OAEG;IAsBH;;OAEG;YACW,YAAY;IA8B1B;;OAEG;IACG,wBAAwB,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;IA0ChB;;OAEG;IACG,qBAAqB,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,EAC7C,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;IAqChB;;OAEG;IACG,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA6BvD;;OAEG;IACG,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAc3E;;OAEG;IACG,uBAAuB,CAC3B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,cAAc,GAAE,MAAY,EAC5B,cAAc,GAAE,MAAa,GAC5B,OAAO,CAAC,OAAO,CAAC;IAiBnB;;;OAGG;IACG,uBAAuB,IAAI,OAAO,CAAC,MAAM,CAAC;IAsBhD;;;;;;OAMG;IACG,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAkBxE;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAmBxB;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAQnC;IAEH;;;;;;;;OAQG;IACG,+BAA+B,CACnC,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,cAAc,GAAE,MAAY,EAC5B,cAAc,GAAE,MAAa,GAC5B,OAAO,CAAC,IAAI,CAAC;IAiEhB;;OAEG;IACH,OAAO,CAAC,gBAAgB;CA8BzB;AAED,OAAO,EAAE,sBAAsB,EAAE,CAAC"}
@@ -376,5 +376,103 @@ class KubernetesClientHelper {
376
376
  const protocol = routeObj.spec?.tls ? "https" : "http";
377
377
  return `${protocol}://${host}`;
378
378
  }
379
+ /**
380
+ * Failure states that indicate a pod will not recover without intervention
381
+ */
382
+ static failureReasons = new Set([
383
+ "CrashLoopBackOff",
384
+ "Error",
385
+ "ImagePullBackOff",
386
+ "ErrImagePull",
387
+ "InvalidImageName",
388
+ "CreateContainerConfigError",
389
+ "CreateContainerError",
390
+ ]);
391
+ /**
392
+ * Wait for pods matching a label selector to be ready, with early failure detection.
393
+ * Fails fast when it detects unrecoverable states like CrashLoopBackOff.
394
+ *
395
+ * @param namespace - Namespace to watch
396
+ * @param labelSelector - Label selector (e.g., "app=myapp")
397
+ * @param timeoutSeconds - Maximum time to wait (default: 300)
398
+ * @param pollIntervalMs - How often to check pod status (default: 5000)
399
+ */
400
+ async waitForPodsWithFailureDetection(namespace, labelSelector, timeoutSeconds = 300, pollIntervalMs = 5000) {
401
+ const startTime = Date.now();
402
+ const timeoutMs = timeoutSeconds * 1000;
403
+ console.log(`[K8sHelper] Waiting for pods (${labelSelector}) in ${namespace}...`);
404
+ while (Date.now() - startTime < timeoutMs) {
405
+ let pods;
406
+ try {
407
+ pods = (await this._k8sApi.listNamespacedPod({ namespace, labelSelector })).items;
408
+ }
409
+ catch (err) {
410
+ console.log(`[K8sHelper] API error, retrying: ${err}`);
411
+ await new Promise((r) => setTimeout(r, pollIntervalMs));
412
+ continue;
413
+ }
414
+ if (pods.length === 0) {
415
+ await new Promise((r) => setTimeout(r, pollIntervalMs));
416
+ continue;
417
+ }
418
+ for (const pod of pods) {
419
+ const podName = pod.metadata?.name || "unknown";
420
+ const failure = this._checkPodFailure(pod);
421
+ if (failure) {
422
+ console.log(`[K8sHelper] Pod ${podName} failed: ${failure.reason}`);
423
+ try {
424
+ if (failure.container) {
425
+ await $ `oc logs ${podName} -n ${namespace} -c ${failure.container} --tail=100`;
426
+ }
427
+ else {
428
+ await $ `oc logs ${podName} -n ${namespace} --tail=100`;
429
+ }
430
+ }
431
+ catch {
432
+ // Ignore log fetch errors
433
+ }
434
+ throw new Error(`Pod ${podName} failed: ${failure.reason}`);
435
+ }
436
+ }
437
+ // Check if all pods are ready
438
+ const allReady = pods.every((pod) => {
439
+ const ready = pod.status?.conditions?.find((c) => c.type === "Ready");
440
+ return ready?.status === "True";
441
+ });
442
+ if (allReady) {
443
+ console.log(`[K8sHelper] All ${pods.length} pod(s) ready in ${namespace}`);
444
+ return;
445
+ }
446
+ await new Promise((r) => setTimeout(r, pollIntervalMs));
447
+ }
448
+ throw new Error(`Timeout waiting for pods (${labelSelector}) after ${timeoutSeconds}s`);
449
+ }
450
+ /**
451
+ * Check if a pod is in a failure state. Returns failure info or null if healthy.
452
+ */
453
+ _checkPodFailure(pod) {
454
+ // Check init containers first
455
+ for (const cs of pod.status?.initContainerStatuses || []) {
456
+ const reason = cs.state?.waiting?.reason;
457
+ if (reason && KubernetesClientHelper.failureReasons.has(reason)) {
458
+ return { reason: `Init:${reason}`, container: cs.name };
459
+ }
460
+ if (cs.state?.terminated?.exitCode &&
461
+ cs.state.terminated.exitCode !== 0) {
462
+ return {
463
+ reason: `Init:Error (exit ${cs.state.terminated.exitCode})`,
464
+ container: cs.name,
465
+ };
466
+ }
467
+ }
468
+ // Check main containers
469
+ for (const cs of pod.status?.containerStatuses || []) {
470
+ const reason = cs.state?.waiting?.reason;
471
+ if (reason && KubernetesClientHelper.failureReasons.has(reason)) {
472
+ return { reason, container: cs.name };
473
+ }
474
+ }
475
+ return null;
476
+ }
379
477
  }
380
478
  export { KubernetesClientHelper };
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-metadata.d.ts","sourceRoot":"","sources":["../../src/utils/plugin-metadata.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,2EAA2E;IAC3E,WAAW,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,UAAU,EAAE,MAAM,CAAC;CACpB;AAgBD;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,IAAI,OAAO,CAmBpD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAQ5D;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,gBAAgB,CAAC;AAEnD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,YAAY,GAAE,MAA8B,GAC3C,MAAM,GAAG,IAAI,CAUf;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAmChC;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CA2BtC;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,oBAAoB,EAAE,oBAAoB,EAC1C,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,GACvC,oBAAoB,CAsCtB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,wCAAwC,CAC5D,YAAY,GAAE,MAA8B,GAC3C,OAAO,CAAC,oBAAoB,CAAC,CAmD/B;AAED;;;;;;;;GAQG;AACH,wBAAsB,2BAA2B,CAC/C,oBAAoB,EAAE,oBAAoB,EAC1C,YAAY,GAAE,MAA8B,GAC3C,OAAO,CAAC,oBAAoB,CAAC,CA4B/B"}
1
+ {"version":3,"file":"plugin-metadata.d.ts","sourceRoot":"","sources":["../../src/utils/plugin-metadata.ts"],"names":[],"mappings":"AAuHA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,2EAA2E;IAC3E,WAAW,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAC;IACpB,gCAAgC;IAChC,UAAU,EAAE,MAAM,CAAC;CACpB;AAgBD;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,IAAI,OAAO,CAmBpD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAQ5D;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,gBAAgB,CAAC;AAEnD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,YAAY,GAAE,MAA8B,GAC3C,MAAM,GAAG,IAAI,CAUf;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CA4BhC;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CA2BtC;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,oBAAoB,EAAE,oBAAoB,EAC1C,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,GACvC,oBAAoB,CAsCtB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,wCAAwC,CAC5D,YAAY,GAAE,MAA8B,GAC3C,OAAO,CAAC,oBAAoB,CAAC,CA0F/B;AAED;;;;;;;;GAQG;AACH,wBAAsB,2BAA2B,CAC/C,oBAAoB,EAAE,oBAAoB,EAC1C,YAAY,GAAE,MAA8B,GAC3C,OAAO,CAAC,oBAAoB,CAAC,CA4B/B"}
@@ -3,6 +3,75 @@ import path from "path";
3
3
  import yaml from "js-yaml";
4
4
  import { glob } from "zx";
5
5
  import { deepMerge } from "./merge-yamls.js";
6
+ const OCI_REGISTRY_PREFIX = "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays";
7
+ /**
8
+ * Fetches plugin versions from source repo and builds OCI URL map.
9
+ * Only called when GIT_PR_NUMBER is set.
10
+ */
11
+ async function getOCIUrlsForPR(workspacePath, prNumber) {
12
+ const ociUrls = new Map();
13
+ const sourceJsonPath = path.join(workspacePath, "source.json");
14
+ const pluginsListPath = path.join(workspacePath, "plugins-list.yaml");
15
+ if (!fs.existsSync(sourceJsonPath)) {
16
+ throw new Error(`[PluginMetadata] PR build requires source.json but not found at: ${sourceJsonPath}`);
17
+ }
18
+ if (!fs.existsSync(pluginsListPath)) {
19
+ throw new Error(`[PluginMetadata] PR build requires plugins-list.yaml but not found at: ${pluginsListPath}`);
20
+ }
21
+ const sourceJson = JSON.parse(await fs.readFile(sourceJsonPath, "utf-8"));
22
+ const { repo, "repo-ref": ref, "repo-flat": repoFlat } = sourceJson;
23
+ if (!repo) {
24
+ throw new Error(`[PluginMetadata] source.json is missing required 'repo' field: ${sourceJsonPath}`);
25
+ }
26
+ if (!ref) {
27
+ throw new Error(`[PluginMetadata] source.json is missing required 'repo-ref' field: ${sourceJsonPath}`);
28
+ }
29
+ // Parse owner/repo from URL
30
+ const match = repo.match(/github\.com\/(.+?)(?:\.git)?$/);
31
+ if (!match) {
32
+ throw new Error(`[PluginMetadata] Failed to parse GitHub repo from source.json: ${repo}`);
33
+ }
34
+ const ownerRepo = match[1];
35
+ // Read plugins list
36
+ const pluginsList = await fs.readFile(pluginsListPath, "utf-8");
37
+ const pluginPaths = pluginsList
38
+ .trim()
39
+ .split("\n")
40
+ .map((l) => l.replace(/:$/, "").trim())
41
+ .filter(Boolean);
42
+ const workspaceName = path.basename(workspacePath);
43
+ console.log(`[PluginMetadata] Fetching versions for ${pluginPaths.length} plugins from source...`);
44
+ for (const pluginPath of pluginPaths) {
45
+ const pkgJsonPath = repoFlat
46
+ ? `${pluginPath}/package.json`
47
+ : `workspaces/${workspaceName}/${pluginPath}/package.json`;
48
+ const rawUrl = `https://raw.githubusercontent.com/${ownerRepo}/${ref}/${pkgJsonPath}`;
49
+ const res = await fetch(rawUrl);
50
+ if (!res.ok) {
51
+ throw new Error(`[PluginMetadata] Failed to fetch package.json for ${pluginPath}: ${res.status} ${res.statusText}\n` +
52
+ ` URL: ${rawUrl}`);
53
+ }
54
+ const pkgJson = (await res.json());
55
+ if (!pkgJson.name) {
56
+ throw new Error(`[PluginMetadata] package.json is missing 'name' field for ${pluginPath}\n` +
57
+ ` URL: ${rawUrl}`);
58
+ }
59
+ if (!pkgJson.version) {
60
+ throw new Error(`[PluginMetadata] package.json is missing 'version' field for ${pluginPath}\n` +
61
+ ` URL: ${rawUrl}`);
62
+ }
63
+ const { name, version } = pkgJson;
64
+ // @backstage-community/plugin-tech-radar -> backstage-community-plugin-tech-radar
65
+ const displayName = name.replace(/^@/, "").replace(/\//g, "-");
66
+ // TODO(RHDHBUGS-2530): Remove !alias suffix once Konflux builds include
67
+ // io.backstage.dynamic-packages annotation. The suffix is a workaround
68
+ // because install-dynamic-plugins.py can't auto-detect plugin path without it.
69
+ const ociUrl = `${OCI_REGISTRY_PREFIX}/${displayName}:pr_${prNumber}__${version}!${displayName}`;
70
+ ociUrls.set(displayName, ociUrl);
71
+ console.log(`[PluginMetadata] ${displayName} -> ${ociUrl}`);
72
+ }
73
+ return ociUrls;
74
+ }
6
75
  /**
7
76
  * Checks if plugin metadata handling should be enabled.
8
77
  * This controls both auto-generation and injection of plugin metadata.
@@ -83,14 +152,10 @@ export async function parseMetadataFile(filePath) {
83
152
  console.log(`[PluginMetadata] Skipping ${filePath}: no spec.dynamicArtifact`);
84
153
  return null;
85
154
  }
86
- if (!pluginConfig) {
87
- console.log(`[PluginMetadata] Skipping ${filePath}: no spec.appConfigExamples[0].content`);
88
- return null;
89
- }
90
155
  console.log(`[PluginMetadata] Loaded metadata for: ${packagePath}`);
91
156
  return {
92
157
  packagePath,
93
- pluginConfig,
158
+ pluginConfig: pluginConfig || {},
94
159
  packageName: packageName || "",
95
160
  sourceFile: filePath,
96
161
  };
@@ -190,12 +255,39 @@ export async function generateDynamicPluginsConfigFromMetadata(metadataPath = DE
190
255
  if (metadataMap.size === 0) {
191
256
  throw new Error(`[PluginMetadata] Cannot generate dynamic-plugins config: no valid metadata files found in ${metadataDir}`);
192
257
  }
258
+ // If PR number is set, fetch OCI URLs
259
+ const prNumber = process.env.GIT_PR_NUMBER;
260
+ let ociUrls = null;
261
+ if (prNumber) {
262
+ console.log(`[PluginMetadata] PR build detected (PR #${prNumber}), fetching OCI URLs...`);
263
+ const workspacePath = path.resolve(metadataPath, "..");
264
+ ociUrls = await getOCIUrlsForPR(workspacePath, prNumber);
265
+ }
193
266
  // Build plugin entries from metadata
194
267
  const plugins = [];
195
268
  for (const [pluginName, metadata] of metadataMap) {
196
- console.log(`[PluginMetadata] Adding plugin from metadata: ${pluginName} (${metadata.packagePath})`);
269
+ let packageRef = metadata.packagePath;
270
+ // Replace with OCI URL if available (required for PR builds)
271
+ if (ociUrls) {
272
+ if (!metadata.packageName) {
273
+ throw new Error(`[PluginMetadata] PR build requires packageName in metadata but not found for: ${pluginName}\n` +
274
+ ` Source file: ${metadata.sourceFile}`);
275
+ }
276
+ const displayName = metadata.packageName
277
+ .replace(/^@/, "")
278
+ .replace(/\//g, "-");
279
+ const ociUrl = ociUrls.get(displayName);
280
+ if (!ociUrl) {
281
+ throw new Error(`[PluginMetadata] PR build requires OCI URL but not found for: ${displayName}\n` +
282
+ ` Package name: ${metadata.packageName}\n` +
283
+ ` Source file: ${metadata.sourceFile}`);
284
+ }
285
+ console.log(`[PluginMetadata] Replacing ${packageRef} with ${ociUrl}`);
286
+ packageRef = ociUrl;
287
+ }
288
+ console.log(`[PluginMetadata] Adding plugin: ${pluginName} (${packageRef})`);
197
289
  plugins.push({
198
- package: metadata.packagePath,
290
+ package: packageRef,
199
291
  disabled: false,
200
292
  pluginConfig: metadata.pluginConfig,
201
293
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rhdh-e2e-test-utils",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "description": "Test utilities for RHDH E2E tests",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -82,7 +82,6 @@
82
82
  "@eslint/js": "^9.39.1",
83
83
  "@keycloak/keycloak-admin-client": "^26.0.0",
84
84
  "@kubernetes/client-node": "^1.4.0",
85
- "boxen": "^8.0.1",
86
85
  "eslint": "^9.39.1",
87
86
  "eslint-plugin-check-file": "^3.3.1",
88
87
  "eslint-plugin-playwright": "^2.4.0",