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.
- package/dist/deployment/keycloak/deployment.d.ts.map +1 -1
- package/dist/deployment/keycloak/deployment.js +2 -1
- package/dist/deployment/rhdh/deployment.d.ts +5 -0
- package/dist/deployment/rhdh/deployment.d.ts.map +1 -1
- package/dist/deployment/rhdh/deployment.js +69 -35
- package/dist/utils/kubernetes-client.d.ts +18 -0
- package/dist/utils/kubernetes-client.d.ts.map +1 -1
- package/dist/utils/kubernetes-client.js +98 -0
- package/dist/utils/plugin-metadata.d.ts.map +1 -1
- package/dist/utils/plugin-metadata.js +99 -7
- package/package.json +1 -2
|
@@ -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;
|
|
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
|
-
|
|
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;
|
|
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 -
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
173
|
-
|
|
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(`${
|
|
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 ${
|
|
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 (
|
|
189
|
-
return
|
|
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
|
-
|
|
194
|
-
const
|
|
195
|
-
if
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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;
|
|
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":"
|
|
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
|
-
|
|
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:
|
|
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.
|
|
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",
|