@red-hat-developer-hub/e2e-test-utils 1.1.40 → 1.1.42

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.
@@ -0,0 +1,5 @@
1
+ plugins:
2
+ - package: oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-app-auth:bs_1.49.4__0.0.1
3
+ disabled: false
4
+ - package: oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-app-integrations:bs_1.49.4__0.0.1
5
+ disabled: false
@@ -0,0 +1,8 @@
1
+ apiVersion: v1
2
+ kind: Secret
3
+ metadata:
4
+ name: rhdh-secrets
5
+ type: Opaque
6
+ stringData:
7
+ APP_CONFIG_app_packageName: app-next
8
+ ENABLE_STANDARD_MODULE_FEDERATION: "true"
@@ -0,0 +1,2 @@
1
+ # Helm values merge layer for the new frontend system (app-next); merged when NFS layers are active (additive; extend as needed).
2
+ {}
@@ -1,4 +1,4 @@
1
- apiVersion: rhdh.redhat.com/v1alpha3
1
+ apiVersion: rhdh.redhat.com/v1alpha5
2
2
  kind: Backstage
3
3
  metadata:
4
4
  name: developer-hub
@@ -4,6 +4,12 @@ export declare const DEFAULT_CONFIG_PATHS: {
4
4
  appConfig: string;
5
5
  secrets: string;
6
6
  dynamicPlugins: string;
7
+ /** New frontend system (app-next): paths merged when the deployment uses NFS layers (explicit flag, `-app-next` namespace, or `USE_NEW_FRONTEND_SYSTEM`). */
8
+ newFrontendSystem: {
9
+ secrets: string;
10
+ dynamicPlugins: string;
11
+ valueFile: string;
12
+ };
7
13
  helm: {
8
14
  valueFile: string;
9
15
  };
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/deployment/rhdh/constants.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAK1D,eAAO,MAAM,oBAAoB;;;;;;;;;;CAyBhC,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,MAAM,CACpC,YAAY,EACZ;IACE,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,YAAY,CAAC;CAC9B,CAoCF,CAAC;AAEF,eAAO,MAAM,SAAS,6BAA6B,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/deployment/rhdh/constants.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAK1D,eAAO,MAAM,oBAAoB;;;;IAa/B,6JAA6J;;;;;;;;;;;;CA2B9J,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,MAAM,CACpC,YAAY,EACZ;IACE,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,YAAY,CAAC;CAC9B,CAoCF,CAAC;AAEF,eAAO,MAAM,SAAS,6BAA6B,CAAC"}
@@ -5,6 +5,12 @@ export const DEFAULT_CONFIG_PATHS = {
5
5
  appConfig: path.join(PACKAGE_ROOT, "dist/deployment/rhdh/config/common/app-config-rhdh.yaml"),
6
6
  secrets: path.join(PACKAGE_ROOT, "dist/deployment/rhdh/config/common/rhdh-secrets.yaml"),
7
7
  dynamicPlugins: path.join(PACKAGE_ROOT, "dist/deployment/rhdh/config/common/dynamic-plugins.yaml"),
8
+ /** New frontend system (app-next): paths merged when the deployment uses NFS layers (explicit flag, `-app-next` namespace, or `USE_NEW_FRONTEND_SYSTEM`). */
9
+ newFrontendSystem: {
10
+ secrets: path.join(PACKAGE_ROOT, "dist/deployment/rhdh/config/new-frontend-system/secrets.yaml"),
11
+ dynamicPlugins: path.join(PACKAGE_ROOT, "dist/deployment/rhdh/config/new-frontend-system/dynamic-plugins.yaml"),
12
+ valueFile: path.join(PACKAGE_ROOT, "dist/deployment/rhdh/config/new-frontend-system/value_file.yaml"),
13
+ },
8
14
  helm: {
9
15
  valueFile: path.join(PACKAGE_ROOT, "dist/deployment/rhdh/config/helm/value_file.yaml"),
10
16
  },
@@ -13,8 +13,8 @@ export declare class RHDHDeployment {
13
13
  /** Shared merge strategy for dynamic plugin arrays. */
14
14
  private static readonly pluginMergeOpts;
15
15
  /**
16
- * Merges package defaults + auth config (+ optional user config) into a
17
- * single dynamic plugins configuration.
16
+ * Merges package defaults + auth + optional new-frontend-system defaults +
17
+ * optional user config into a single dynamic plugins configuration.
18
18
  */
19
19
  private _mergeBaseConfigs;
20
20
  /**
@@ -34,6 +34,7 @@ export declare class RHDHDeployment {
34
34
  private _applyDynamicPlugins;
35
35
  private _deployWithHelm;
36
36
  private _deployWithOperator;
37
+ private get _labelSelector();
37
38
  rolloutRestart(): Promise<void>;
38
39
  /**
39
40
  * Performs a clean restart by scaling down to 0 first, waiting for pods to terminate,
@@ -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;AAwB1E,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;IAKvB,MAAM,CAAC,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YA0CpD,eAAe;YAmBf,aAAa;IAqB3B,uDAAuD;IACvD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAE5B;IAEX;;;OAGG;YACW,iBAAiB;IAY/B;;OAEG;YACW,uBAAuB;IAgBrC;;;;;;;;OAQG;YACW,0BAA0B;YAsC1B,oBAAoB;YAWpB,eAAe;YA6Df,mBAAmB;IAoE3B,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;IA8BpD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YAIjB,iBAAiB;YASjB,oBAAoB;IAsClC;;;OAGG;YACW,0BAA0B;IAwCxC,OAAO,CAAC,sBAAsB;IAoCxB,SAAS,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAUrE,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,SAAS;CAMlB"}
1
+ {"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../../src/deployment/rhdh/deployment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAwB1E,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;IAKvB,MAAM,CAAC,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YA0CpD,eAAe;YAmBf,aAAa;IA2B3B,uDAAuD;IACvD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAE5B;IAEX;;;OAGG;YACW,iBAAiB;IAe/B;;OAEG;YACW,uBAAuB;IAgBrC;;;;;;;;OAQG;YACW,0BAA0B;YAsC1B,oBAAoB;YAWpB,eAAe;YAuEf,mBAAmB;IAmCjC,OAAO,KAAK,cAAc,GAIzB;IAEK,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;IA6BpD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YAIjB,iBAAiB;YASjB,oBAAoB;IAsClC;;;OAGG;YACW,0BAA0B;IAwCxC,OAAO,CAAC,sBAAsB;IA2CxB,SAAS,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAUrE,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,SAAS;CAMlB"}
@@ -62,32 +62,40 @@ export class RHDHDeployment {
62
62
  }
63
63
  async _applySecrets() {
64
64
  const authConfig = AUTH_CONFIG_PATHS[this.deploymentConfig.auth];
65
- const secretsYaml = await mergeYamlFilesIfExists([
65
+ const secretsPaths = [
66
66
  DEFAULT_CONFIG_PATHS.secrets,
67
67
  authConfig.secrets,
68
+ ...(this.deploymentConfig.useNewFrontendSystem
69
+ ? [DEFAULT_CONFIG_PATHS.newFrontendSystem.secrets]
70
+ : []),
68
71
  this.deploymentConfig.secrets,
69
- ]);
72
+ ];
73
+ const secretsYaml = await mergeYamlFilesIfExists(secretsPaths);
70
74
  // Use cloneDeepWith to substitute env vars in-place, avoiding JSON.parse issues
71
75
  // with control characters in secrets (e.g., private keys with newlines)
72
76
  const substituted = cloneDeepWith(secretsYaml, (value) => {
73
77
  if (typeof value === "string")
74
78
  return envsubst(value);
75
79
  });
76
- await this.k8sClient.applySecretFromObject("rhdh-secrets", substituted, this.deploymentConfig.namespace);
80
+ const secretPayload = substituted;
81
+ await this.k8sClient.applySecretFromObject("rhdh-secrets", secretPayload, this.deploymentConfig.namespace);
77
82
  }
78
83
  /** Shared merge strategy for dynamic plugin arrays. */
79
84
  static pluginMergeOpts = {
80
85
  arrayMergeStrategy: { byKey: "package" },
81
86
  };
82
87
  /**
83
- * Merges package defaults + auth config (+ optional user config) into a
84
- * single dynamic plugins configuration.
88
+ * Merges package defaults + auth + optional new-frontend-system defaults +
89
+ * optional user config into a single dynamic plugins configuration.
85
90
  */
86
91
  async _mergeBaseConfigs(userConfigPath) {
87
92
  const authConfig = AUTH_CONFIG_PATHS[this.deploymentConfig.auth];
88
93
  const paths = [
89
94
  DEFAULT_CONFIG_PATHS.dynamicPlugins,
90
95
  authConfig.dynamicPlugins,
96
+ ...(this.deploymentConfig.useNewFrontendSystem
97
+ ? [DEFAULT_CONFIG_PATHS.newFrontendSystem.dynamicPlugins]
98
+ : []),
91
99
  ...(userConfigPath ? [userConfigPath] : []),
92
100
  ];
93
101
  return await mergeYamlFilesIfExists(paths, RHDHDeployment.pluginMergeOpts);
@@ -148,10 +156,18 @@ export class RHDHDeployment {
148
156
  async _deployWithHelm(valueFile) {
149
157
  const chartVersion = await this._resolveChartVersion(this.deploymentConfig.version);
150
158
  this._log(`Helm chart version resolved to: ${chartVersion}`);
151
- const valueFileObject = (await mergeYamlFilesIfExists([
159
+ const helmValuePaths = [
152
160
  DEFAULT_CONFIG_PATHS.helm.valueFile,
161
+ ...(this.deploymentConfig.useNewFrontendSystem
162
+ ? [DEFAULT_CONFIG_PATHS.newFrontendSystem.valueFile]
163
+ : []),
153
164
  valueFile,
154
- ]));
165
+ ...(this.deploymentConfig.useNewFrontendSystem &&
166
+ fs.existsSync(WorkspacePaths.valueFileAppNext)
167
+ ? [WorkspacePaths.valueFileAppNext]
168
+ : []),
169
+ ];
170
+ const valueFileObject = (await mergeYamlFilesIfExists(helmValuePaths));
155
171
  this._logBoxen("Value File", valueFileObject);
156
172
  // Merge dynamic plugins into the values file (including auth-specific plugins)
157
173
  if (!valueFileObject.global) {
@@ -213,44 +229,20 @@ export class RHDHDeployment {
213
229
  });
214
230
  this._log(`Catalog index image: ${catalogIndexImage}`);
215
231
  }
216
- this._logBoxen("Subscription", subscriptionObject);
232
+ this._logBoxen("Backstage CR", subscriptionObject);
217
233
  const subscriptionFilePath = path.join(os.tmpdir(), `${this.deploymentConfig.namespace}-subscription.yaml`);
218
234
  fs.writeFileSync(subscriptionFilePath, yaml.dump(subscriptionObject));
219
- const version = this.deploymentConfig.version;
220
- const isSemanticVersion = /^\d+(\.\d+)?$/.test(version);
221
- // Use main branch for non-semantic versions (e.g., "next", "latest")
222
- const branch = isSemanticVersion ? `release-${version}` : "main";
223
- // Build version argument based on version type
224
- let versionArg;
225
- if (isSemanticVersion) {
226
- versionArg = `-v ${version}`;
227
- }
228
- else if (version === "next") {
229
- versionArg = "--next";
230
- }
231
- else {
232
- throw new Error(`Invalid RHDH version "${version}". Use semantic version (e.g., "1.5") or "next".`);
233
- }
234
- this._log(`Using operator branch: ${branch}, version arg: ${versionArg}`);
235
- await $ `
236
- set -e;
237
- 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
238
-
239
- timeout 300 bash -c '
240
- while ! oc get crd/backstages.rhdh.redhat.com -n "${this.deploymentConfig.namespace}" >/dev/null 2>&1; do
241
- echo "Waiting for Backstage CRD to be created..."
242
- sleep 20
243
- done
244
- echo "Backstage CRD is created."
245
- ' || echo "Error: Timed out waiting for Backstage CRD creation."
246
-
247
- oc apply -f "${subscriptionFilePath}" -n "${this.deploymentConfig.namespace}"
248
- `;
249
- this._log("Operator deployment executed successfully.");
235
+ await $ `oc apply -f "${subscriptionFilePath}" -n "${this.deploymentConfig.namespace}"`;
236
+ this._log("Backstage CR applied successfully.");
237
+ }
238
+ get _labelSelector() {
239
+ return this.deploymentConfig.method === "operator"
240
+ ? "rhdh.redhat.com/app=backstage-developer-hub"
241
+ : "app.kubernetes.io/instance in (redhat-developer-hub,developer-hub)";
250
242
  }
251
243
  async rolloutRestart() {
252
244
  this._log(`Restarting RHDH deployment in namespace ${this.deploymentConfig.namespace}...`);
253
- await $ `oc rollout restart deployment -l 'app.kubernetes.io/instance in (redhat-developer-hub,developer-hub)' -n ${this.deploymentConfig.namespace}`;
245
+ await $ `oc rollout restart deployment -l '${this._labelSelector}' -n ${this.deploymentConfig.namespace}`;
254
246
  this._log(`RHDH deployment restarted successfully in namespace ${this.deploymentConfig.namespace}`);
255
247
  await this.waitUntilReady();
256
248
  }
@@ -261,13 +253,13 @@ export class RHDHDeployment {
261
253
  */
262
254
  async scaleDownAndRestart() {
263
255
  const namespace = this.deploymentConfig.namespace;
264
- await $ `oc scale deployment -l 'app.kubernetes.io/instance in (redhat-developer-hub,developer-hub)' --replicas=0 -n ${namespace}`;
265
- await $ `oc wait --for=delete pod -l 'app.kubernetes.io/instance in (redhat-developer-hub,developer-hub),app.kubernetes.io/name!=postgresql' -n ${namespace} --timeout=120s || true`;
266
- await $ `oc scale deployment -l 'app.kubernetes.io/instance in (redhat-developer-hub,developer-hub)' --replicas=1 -n ${namespace}`;
256
+ await $ `oc scale deployment -l '${this._labelSelector}' --replicas=0 -n ${namespace}`;
257
+ await $ `oc wait --for=delete pod -l '${this._labelSelector}' -n ${namespace} --timeout=120s || true`;
258
+ await $ `oc scale deployment -l '${this._labelSelector}' --replicas=1 -n ${namespace}`;
267
259
  }
268
260
  async waitUntilReady(timeout = 500) {
269
261
  const namespace = this.deploymentConfig.namespace;
270
- const labelSelector = "app.kubernetes.io/instance in (redhat-developer-hub,developer-hub)";
262
+ const labelSelector = this._labelSelector;
271
263
  const startTime = Date.now();
272
264
  try {
273
265
  await this.k8sClient.waitForPodsWithFailureDetection(namespace, labelSelector, timeout);
@@ -358,14 +350,19 @@ export class RHDHDeployment {
358
350
  const method = input.method ??
359
351
  process.env.INSTALLATION_METHOD ??
360
352
  "helm";
353
+ const namespace = input.namespace ?? this.deploymentConfig.namespace;
354
+ const useNewFrontendSystem = input.useNewFrontendSystem ??
355
+ (namespace.endsWith("-app-next") ||
356
+ process.env.USE_NEW_FRONTEND_SYSTEM === "true");
361
357
  const base = {
362
358
  version,
363
- namespace: input.namespace ?? this.deploymentConfig.namespace,
359
+ namespace,
364
360
  auth: input.auth ?? "keycloak",
365
361
  appConfig: input.appConfig ?? WorkspacePaths.appConfig,
366
362
  secrets: input.secrets ?? WorkspacePaths.secrets,
367
363
  dynamicPlugins: input.dynamicPlugins ?? WorkspacePaths.dynamicPlugins,
368
364
  disableWrappers: input.disableWrappers ?? [],
365
+ useNewFrontendSystem,
369
366
  };
370
367
  if (method === "helm") {
371
368
  return {
@@ -38,4 +38,54 @@ describe("dynamic-plugins merge (no user config path)", () => {
38
38
  assert.strictEqual(plugins.length, 1, "merged config must have exactly one keycloak plugin");
39
39
  assert.ok(plugins[0].package?.startsWith("oci://"), "metadata (OCI) must win over auth local path");
40
40
  });
41
+ it("keeps user app-auth OCI ref over NFS defaults for same logical plugin", () => {
42
+ const nfsDefaults = {
43
+ plugins: [
44
+ {
45
+ package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-app-auth:bs_1.49.4__0.0.1",
46
+ disabled: false,
47
+ },
48
+ ],
49
+ };
50
+ const userPlugins = {
51
+ plugins: [
52
+ {
53
+ package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-app-auth:older__0.0.1",
54
+ disabled: false,
55
+ },
56
+ ],
57
+ };
58
+ const merged = deepMerge(nfsDefaults, userPlugins, {
59
+ arrayMergeStrategy: {
60
+ byKey: "package",
61
+ normalizeKey: (item) => getNormalizedPluginMergeKey(item),
62
+ },
63
+ });
64
+ const plugins = merged.plugins;
65
+ assert.strictEqual(plugins.length, 1);
66
+ assert.ok(plugins[0].package?.includes("older__0.0.1"), "user layer must win over NFS defaults for the same logical plugin");
67
+ });
68
+ it("does not duplicate includes when NFS layer omits includes", () => {
69
+ const common = {
70
+ includes: ["dynamic-plugins.default.yaml"],
71
+ plugins: [],
72
+ };
73
+ const nfsLayer = {
74
+ plugins: [
75
+ {
76
+ package: "oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-app-auth:bs_1.49.4__0.0.1",
77
+ disabled: false,
78
+ },
79
+ ],
80
+ };
81
+ const merged = deepMerge(common, nfsLayer, {
82
+ arrayMergeStrategy: {
83
+ byKey: "package",
84
+ normalizeKey: (item) => getNormalizedPluginMergeKey(item),
85
+ },
86
+ });
87
+ const includes = merged.includes;
88
+ assert.strictEqual(includes?.length, 1);
89
+ assert.strictEqual(includes[0], "dynamic-plugins.default.yaml");
90
+ });
41
91
  });
@@ -1,2 +1,3 @@
1
1
  export { RHDHDeployment } from "./deployment.js";
2
+ export * from "./types.js";
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/deployment/rhdh/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/deployment/rhdh/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,cAAc,YAAY,CAAC"}
@@ -1 +1,2 @@
1
1
  export { RHDHDeployment } from "./deployment.js";
2
+ export * from "./types.js";
@@ -0,0 +1,2 @@
1
+ export declare function installRHDHOperator(): Promise<void>;
2
+ //# sourceMappingURL=operator-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operator-setup.d.ts","sourceRoot":"","sources":["../../../src/deployment/rhdh/operator-setup.ts"],"names":[],"mappings":"AAEA,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CA0DzD"}
@@ -0,0 +1,46 @@
1
+ import { $ } from "../../utils/bash.js";
2
+ export async function installRHDHOperator() {
3
+ if (process.env.INSTALLATION_METHOD !== "operator") {
4
+ return;
5
+ }
6
+ if (process.env.SKIP_OPERATOR_INSTALLATION === "true") {
7
+ console.log("Skipping RHDH operator installation");
8
+ return;
9
+ }
10
+ const version = process.env.RHDH_VERSION ?? "next";
11
+ const isSemanticVersion = /^\d+(\.\d+)?$/.test(version);
12
+ const branch = isSemanticVersion ? `release-${version}` : "main";
13
+ let versionArgs;
14
+ if (isSemanticVersion) {
15
+ versionArgs = ["-v", version];
16
+ }
17
+ else if (version === "next") {
18
+ versionArgs = ["--next"];
19
+ }
20
+ else {
21
+ throw new Error(`Invalid RHDH version "${version}". Use semantic version (e.g., "1.5") or "next".`);
22
+ }
23
+ console.log(`Installing RHDH operator (branch: ${branch}, version: ${versionArgs.join(" ")})...`);
24
+ const scriptUrl = `https://raw.githubusercontent.com/redhat-developer/rhdh-operator/refs/heads/${branch}/.rhdh/scripts/install-rhdh-catalog-source.sh`;
25
+ try {
26
+ await $ `curl -sf ${scriptUrl} | bash -s -- ${versionArgs} --install-operator rhdh`;
27
+ }
28
+ catch (error) {
29
+ throw new Error(`install-rhdh-catalog-source.sh failed (branch: ${branch}, args: ${versionArgs.join(" ")} --install-operator rhdh).\nScript URL: ${scriptUrl}\n${error instanceof Error ? error.message : error}`, { cause: error });
30
+ }
31
+ try {
32
+ await $ `
33
+ timeout 300 bash -c '
34
+ while ! oc get crd backstages.rhdh.redhat.com >/dev/null 2>&1; do
35
+ echo "Waiting for Backstage CRD to be created..."
36
+ sleep 10
37
+ done
38
+ echo "Backstage CRD is created."
39
+ '
40
+ `;
41
+ }
42
+ catch (error) {
43
+ throw new Error("Timed out waiting for Backstage CRD (backstages.rhdh.redhat.com) after 300s. The operator may not have installed correctly.", { cause: error });
44
+ }
45
+ console.log("RHDH operator installation completed");
46
+ }
@@ -11,6 +11,8 @@ export type DeploymentOptions = {
11
11
  valueFile?: string;
12
12
  subscription?: string;
13
13
  disableWrappers?: string[];
14
+ /** When true, merge new-frontend-system (app-next) layers. When omitted, auto-detect: namespace ends with `-app-next` or `USE_NEW_FRONTEND_SYSTEM=true`. Pass false to disable. */
15
+ useNewFrontendSystem?: boolean;
14
16
  };
15
17
  export type HelmDeploymentConfig = {
16
18
  method: "helm";
@@ -28,6 +30,8 @@ export type DeploymentConfigBase = {
28
30
  secrets: string;
29
31
  dynamicPlugins: string;
30
32
  disableWrappers: string[];
33
+ /** New frontend system (Backstage app-next / NFS shell). */
34
+ useNewFrontendSystem: boolean;
31
35
  };
32
36
  export type DeploymentConfig = DeploymentConfigBase & (HelmDeploymentConfig | OperatorDeploymentConfig);
33
37
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/deployment/rhdh/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,UAAU,CAAC;AACnD,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE3D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,MAAM,EAAE,UAAU,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,oBAAoB,GACjD,CAAC,oBAAoB,GAAG,wBAAwB,CAAC,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/deployment/rhdh/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,UAAU,CAAC;AACnD,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE3D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,mLAAmL;IACnL,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,MAAM,EAAE,UAAU,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,4DAA4D;IAC5D,oBAAoB,EAAE,OAAO,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,oBAAoB,GACjD,CAAC,oBAAoB,GAAG,wBAAwB,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"global-setup.d.ts","sourceRoot":"","sources":["../../src/playwright/global-setup.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AA6EnD,wBAA8B,WAAW,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ3E"}
1
+ {"version":3,"file":"global-setup.d.ts","sourceRoot":"","sources":["../../src/playwright/global-setup.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AA8EnD,wBAA8B,WAAW,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ3E"}
@@ -7,6 +7,7 @@ import { resolve } from "path";
7
7
  import { KubernetesClientHelper } from "../utils/kubernetes-client.js";
8
8
  import { $ } from "../utils/bash.js";
9
9
  import { KeycloakHelper } from "../deployment/keycloak/index.js";
10
+ import { installRHDHOperator } from "../deployment/rhdh/operator-setup.js";
10
11
  import { DEFAULT_KEYCLOAK_CONFIG, DEFAULT_RHDH_CLIENT, DEFAULT_USERS, } from "../deployment/keycloak/constants.js";
11
12
  import { loadLocalVaultSecrets } from "../utils/vault.js";
12
13
  const REQUIRED_BINARIES = ["oc", "kubectl", "helm"];
@@ -67,7 +68,7 @@ export default async function globalSetup(config) {
67
68
  await loadLocalVaultSecrets();
68
69
  loadDotenvFromProjects(config);
69
70
  await setClusterRouterBaseEnv();
70
- await deployKeycloak();
71
+ await Promise.all([installRHDHOperator(), deployKeycloak()]);
71
72
  console.log("Global setup completed successfully");
72
73
  }
73
74
  /**
@@ -37,6 +37,8 @@ export declare class WorkspacePaths {
37
37
  static get dynamicPlugins(): string;
38
38
  /** Default Helm value file path: `tests/config/value_file.yaml` */
39
39
  static get valueFile(): string;
40
+ /** Optional Helm values merged after `value_file.yaml` when the new frontend system is active (explicit option, namespace suffix `-app-next`, or `USE_NEW_FRONTEND_SYSTEM=true`) and this file exists. */
41
+ static get valueFileAppNext(): string;
40
42
  /** Default operator subscription path: `tests/config/subscription.yaml` */
41
43
  static get subscription(): string;
42
44
  }
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-paths.d.ts","sourceRoot":"","sources":["../../src/utils/workspace-paths.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,cAAc;IACzB,OAAO;IAEP,4FAA4F;IAC5F,MAAM,KAAK,OAAO,IAAI,MAAM,CAE3B;IAED,4DAA4D;IAC5D,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;IAI5C,0DAA0D;IAC1D,MAAM,KAAK,aAAa,IAAI,MAAM,CAEjC;IAED,8DAA8D;IAC9D,MAAM,KAAK,WAAW,IAAI,MAAM,CAE/B;IAED,gFAAgF;IAChF,MAAM,KAAK,SAAS,IAAI,MAAM,CAE7B;IAID,mEAAmE;IACnE,MAAM,KAAK,SAAS,IAAI,MAAM,CAE7B;IAED,6DAA6D;IAC7D,MAAM,KAAK,OAAO,IAAI,MAAM,CAE3B;IAED,wEAAwE;IACxE,MAAM,KAAK,cAAc,IAAI,MAAM,CAElC;IAED,mEAAmE;IACnE,MAAM,KAAK,SAAS,IAAI,MAAM,CAE7B;IAED,2EAA2E;IAC3E,MAAM,KAAK,YAAY,IAAI,MAAM,CAEhC;CACF"}
1
+ {"version":3,"file":"workspace-paths.d.ts","sourceRoot":"","sources":["../../src/utils/workspace-paths.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,cAAc;IACzB,OAAO;IAEP,4FAA4F;IAC5F,MAAM,KAAK,OAAO,IAAI,MAAM,CAE3B;IAED,4DAA4D;IAC5D,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;IAI5C,0DAA0D;IAC1D,MAAM,KAAK,aAAa,IAAI,MAAM,CAEjC;IAED,8DAA8D;IAC9D,MAAM,KAAK,WAAW,IAAI,MAAM,CAE/B;IAED,gFAAgF;IAChF,MAAM,KAAK,SAAS,IAAI,MAAM,CAE7B;IAID,mEAAmE;IACnE,MAAM,KAAK,SAAS,IAAI,MAAM,CAE7B;IAED,6DAA6D;IAC7D,MAAM,KAAK,OAAO,IAAI,MAAM,CAE3B;IAED,wEAAwE;IACxE,MAAM,KAAK,cAAc,IAAI,MAAM,CAElC;IAED,mEAAmE;IACnE,MAAM,KAAK,SAAS,IAAI,MAAM,CAE7B;IAED,0MAA0M;IAC1M,MAAM,KAAK,gBAAgB,IAAI,MAAM,CAEpC;IAED,2EAA2E;IAC3E,MAAM,KAAK,YAAY,IAAI,MAAM,CAEhC;CACF"}
@@ -58,6 +58,10 @@ export class WorkspacePaths {
58
58
  static get valueFile() {
59
59
  return path.resolve(this.e2eRoot, "tests/config/value_file.yaml");
60
60
  }
61
+ /** Optional Helm values merged after `value_file.yaml` when the new frontend system is active (explicit option, namespace suffix `-app-next`, or `USE_NEW_FRONTEND_SYSTEM=true`) and this file exists. */
62
+ static get valueFileAppNext() {
63
+ return path.resolve(this.e2eRoot, "tests/config/value_file-app-next.yaml");
64
+ }
61
65
  /** Default operator subscription path: `tests/config/subscription.yaml` */
62
66
  static get subscription() {
63
67
  return path.resolve(this.e2eRoot, "tests/config/subscription.yaml");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@red-hat-developer-hub/e2e-test-utils",
3
- "version": "1.1.40",
3
+ "version": "1.1.42",
4
4
  "description": "Test utilities for RHDH E2E tests",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {