Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ src/
│ │ ├── deployment.ts # Main class (configure, deploy, waitUntilReady, teardown)
│ │ ├── types.ts # DeploymentOptions, DeploymentConfig
│ │ ├── constants.ts # Config paths, auth providers, chart URLs
│ │ └── config/ # YAML templates (common/, auth/, helm/, operator/)
│ │ └── config/ # YAML templates (common/, auth/, new-frontend-system/, helm/, operator/)
│ ├── keycloak/ # KeycloakHelper — Keycloak Helm deployment + OIDC setup
│ └── orchestrator/ # Workflow orchestrator installer
├── playwright/
Expand Down Expand Up @@ -81,18 +81,22 @@ yarn check # typecheck + lint + prettier

## Key Architectural Concepts

### Configuration Merging (3-level cascade)
### Configuration Merging (layered cascade)

```
Package defaults (config/common/)
↓ deep merge
Auth-specific (config/auth/{keycloak|guest|github}/)
↓ deep merge (when useNewFrontendSystem)
New frontend system defaults (config/new-frontend-system/)
↓ deep merge
User config (workspace's tests/config/*.yaml)
= Final merged config
```

`useNewFrontendSystem` is true when set explicitly, when the namespace ends with `-app-next`, or when `USE_NEW_FRONTEND_SYSTEM=true`. Secrets use the same layer order, then a single `envsubst` pass on the merged result.

Array merge uses "replace" strategy by default. Plugin arrays use `byKey: "package"` with normalized keys (strips trailing `-dynamic`).

### Plugin Metadata Resolution
Expand Down
2 changes: 1 addition & 1 deletion docs/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ When documenting, reference these source files:
| YAML Merging | `src/utils/merge-yamls.ts` |
| Bash ($) | `src/utils/bash.ts` |
| envsubst | `src/utils/common.ts` |
| Config Templates | `src/deployment/rhdh/config/{common,auth,helm,operator}/` |
| Config Templates | `src/deployment/rhdh/config/{common,auth,new-frontend-system,helm,operator}/` |

## Common Tasks

Expand Down
6 changes: 6 additions & 0 deletions docs/api/deployment/deployment-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ type DeploymentOptions = {
method?: DeploymentMethod;
valueFile?: string;
subscription?: string;
disableWrappers?: string[];
useNewFrontendSystem?: boolean;
};
```

Expand All @@ -56,6 +58,8 @@ type DeploymentOptions = {
| `method` | `DeploymentMethod` | Installation method |
| `valueFile` | `string` | Helm values file (Helm only) |
| `subscription` | `string` | Backstage CR file (Operator only) |
| `disableWrappers` | `string[]` | Wrapper plugins to disable (PR builds) |
| `useNewFrontendSystem` | `boolean` | New frontend system (app-next / NFS). Omit to auto-detect from namespace suffix `-app-next` or `USE_NEW_FRONTEND_SYSTEM=true`; set `false` to disable. See [RHDH deployment](/guide/deployment/rhdh-deployment#new-frontend-system-usenewfrontendsystem) |

## DeploymentConfigBase

Expand All @@ -67,6 +71,8 @@ type DeploymentConfigBase = {
appConfig: string;
secrets: string;
dynamicPlugins: string;
disableWrappers: string[];
useNewFrontendSystem: boolean;
};
```

Expand Down
6 changes: 6 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

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

## [1.1.41] - Current

### Added

- **`useNewFrontendSystem`** — Backstage **app-next** / new frontend system: merges NFS layers from `config/new-frontend-system/` (secrets, default **app-auth** and **app-integrations** plugins, Helm `value_file.yaml`) into the same merge pipelines as common/auth/user. Secrets are merged with other layers **before** a single `envsubst` pass. NFS dynamic plugins act as **defaults** (workspace `tests/config/dynamic-plugins.yaml` overrides). **Auto-detection:** enabled when the namespace ends with `-app-next` or `USE_NEW_FRONTEND_SYSTEM=true`, unless `useNewFrontendSystem: false` is passed. Optional workspace `tests/config/value_file-app-next.yaml` is still merged last for Helm.

## [1.1.40] - Current

### Changed
Expand Down
4 changes: 4 additions & 0 deletions docs/guide/configuration/config-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ catalog:

Configure dynamic plugins:

::: tip `useNewFrontendSystem`
If you run against the **app-next** shell, enable the new frontend system (pass `useNewFrontendSystem: true`, use a project/namespace suffix **`-app-next`**, or set **`USE_NEW_FRONTEND_SYSTEM=true`** — see [RHDH deployment](/guide/deployment/rhdh-deployment#new-frontend-system-usenewfrontendsystem)). The package merges default **app-auth** and **app-integrations** OCI plugins as **defaults**; override versions in this file when they must differ.
:::

```yaml
includes:
- dynamic-plugins.default.yaml
Expand Down
16 changes: 16 additions & 0 deletions docs/guide/configuration/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ These are set automatically during deployment:
| `CHART_URL` | Custom Helm chart URL | `oci://quay.io/rhdh/chart` |
| `SKIP_KEYCLOAK_DEPLOYMENT` | Skip Keycloak auto-deploy | `false` |
| `RHDH_SKIP_PLUGIN_METADATA_INJECTION` | Disable plugin metadata injection (local only, ignored in CI) | - |
| `USE_NEW_FRONTEND_SYSTEM` | When `"true"`, enables new-frontend-system (app-next) merges when `useNewFrontendSystem` is not set in `configure()` options | - |

## Plugin Metadata Variables

Expand All @@ -53,6 +54,21 @@ These control automatic plugin configuration injection from metadata files.
| `JOB_NAME` | CI job name (set by OpenShift CI/Prow) | If contains `periodic-`, nightly mode is activated |
| `JOB_MODE` | CI-only: `nightly` or `pr-check` (set by step registry) | Informational |

## New frontend system (app-next)

NFS merge layers ship under `config/new-frontend-system/` in the package (secrets, dynamic plugins, Helm values), same layering idea as `auth/`.

**Enabling app-next behavior**

| Mechanism | Effect |
|-----------|--------|
| `useNewFrontendSystem: true` in [`configure()`](/guide/deployment/rhdh-deployment#new-frontend-system-usenewfrontendsystem) | Explicit enable |
| `useNewFrontendSystem: false` | Explicit disable (overrides auto-detection) |
| Playwright project / namespace ends with `-app-next` | Auto-enabled when `useNewFrontendSystem` is omitted |
| `USE_NEW_FRONTEND_SYSTEM=true` | Auto-enabled for all namespaces when `useNewFrontendSystem` is omitted |

**Overriding default OCI refs** for **app-auth** and **app-integrations** uses the same pattern as other plugins: add or adjust entries in your workspace `tests/config/dynamic-plugins.yaml` (merged after package NFS defaults, so your versions win).

### OCI URL Generation

When `GIT_PR_NUMBER` is set, the package replaces local plugin paths with OCI URLs:
Expand Down
4 changes: 3 additions & 1 deletion docs/guide/configuration/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ The package provides configuration tools for ESLint, TypeScript, and RHDH deploy
| [Configuration Files](/guide/configuration/config-files) | YAML configuration structure |
| [ESLint Configuration](/guide/configuration/eslint-config) | Pre-configured ESLint rules |
| [TypeScript Configuration](/guide/configuration/typescript-config) | Base TypeScript settings |
| [Environment Variables](/guide/configuration/environment-variables) | All environment variables |
| [Environment Variables](/guide/configuration/environment-variables) | All environment variables (including NFS / app-next, e.g. `USE_NEW_FRONTEND_SYSTEM`) |
| [Disabling Conflicting Wrappers](/guide/configuration/disable-wrappers) | Disabling pre-enabled wrappers that may cause configuration conflicts |

## Project Configuration
Expand Down Expand Up @@ -69,6 +69,8 @@ RHDH configurations are merged in layers:

This allows you to override only what you need while using sensible defaults.

For tests targeting the **new frontend system** (app-next), see [RHDH deployment — New frontend system](/guide/deployment/rhdh-deployment#new-frontend-system-usenewfrontendsystem) and [NFS-related options](/guide/configuration/environment-variables#new-frontend-system-app-next).

## Plugin Metadata Injection

For PR builds, the package can automatically inject plugin configurations from metadata files. See [Plugin Metadata Injection](/guide/configuration/config-files#plugin-metadata-injection) for details.
52 changes: 45 additions & 7 deletions docs/guide/deployment/rhdh-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,43 @@ test("example", async ({ rhdh }) => {
| `dynamicPlugins` | `string` | Path to dynamic-plugins YAML |
| `valueFile` | `string` | Helm values file (Helm only) |
| `subscription` | `string` | Backstage CR file (Operator only) |
| `disableWrappers` | `string[]` | Wrapper plugin package names to disable (`GIT_PR_NUMBER` flows) |
| `useNewFrontendSystem` | `boolean` | Enables the Backstage **new frontend system** shell (app-next / NFS): merges app-next secrets, default OCI **app-auth** and **app-integrations** plugins (as defaults — override in `tests/config/dynamic-plugins.yaml`), and extra Helm values from `config/new-frontend-system/value_file.yaml` plus optional `tests/config/value_file-app-next.yaml`. Omit to **auto-detect**: on when the namespace ends with `-app-next` or `USE_NEW_FRONTEND_SYSTEM=true`. Pass `false` to force off. |

### New frontend system (`useNewFrontendSystem`)

Use the **app-next** frontend when any of these apply:

- You pass `useNewFrontendSystem: true` in **`configure()`** (explicit), or
- The Playwright project name (which becomes the Kubernetes namespace) ends with **`-app-next`**, or
- You set environment variable **`USE_NEW_FRONTEND_SYSTEM=true`**

Pass **`useNewFrontendSystem: false`** to run in a `-app-next` namespace without NFS layers.

Typical explicit flow:

```typescript
await rhdh.configure({
auth: "keycloak",
useNewFrontendSystem: true,
});
await rhdh.deploy();
```

With a project named e.g. `my-plugin-app-next`, you can omit the flag and still get NFS merges:

```typescript
await rhdh.configure({ auth: "keycloak" });
await rhdh.deploy();
```

What gets merged (same order as other config: package defaults → auth → NFS defaults → your workspace files; later wins; secrets are merged then **envsubst** runs once on the result):

1. **Secrets** — `APP_CONFIG_app_packageName: app-next` and `ENABLE_STANDARD_MODULE_FEDERATION: "true"` (your `rhdh-secrets.yaml` still wins on conflicts and can use `$VAR` substitution).
2. **Dynamic plugins** — Default OCI refs for `red-hat-developer-hub-backstage-plugin-app-auth` and `...-app-integrations` from package YAML; override pins in **`tests/config/dynamic-plugins.yaml`** (same as other plugins).
3. **Helm** — Package `config/new-frontend-system/value_file.yaml`, then your `value_file.yaml`, then optional `tests/config/value_file-app-next.yaml` when that file exists.

Workspace-specific **app-config** (titles, plugin routes, etc.) remains your responsibility.

### Example: Full Configuration

Expand Down Expand Up @@ -113,13 +150,14 @@ await rhdh.deploy({ timeout: null });
`deploy()` automatically skips if the deployment already succeeded in the current test run (e.g., after a worker restart due to test failure). This prevents expensive re-deployments.

This method:
1. Merges configuration files (common → auth → project)
2. [Injects plugin metadata](/guide/configuration/config-files#plugin-metadata-injection) into dynamic plugins config
3. Applies ConfigMaps (app-config, dynamic-plugins)
4. Applies Secrets (with environment variable substitution)
5. Installs RHDH via Helm or Operator
6. Waits for the deployment to be ready
7. Sets `RHDH_BASE_URL` environment variable
1. Merges configuration files (common → auth → optional NFS defaults → project overrides) for app-config, secrets, and dynamic plugins
2. Substitutes environment variables in the merged secrets (`envsubst`)
3. [Injects plugin metadata](/guide/configuration/config-files#plugin-metadata-injection) into dynamic plugins config
4. Applies ConfigMaps (app-config, dynamic-plugins)
5. Applies Secrets
6. Installs RHDH via Helm or Operator
7. Waits for the deployment to be ready
8. Sets `RHDH_BASE_URL` environment variable

#### Base URL format

Expand Down
8 changes: 8 additions & 0 deletions docs/overlay/reference/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ See [Running Locally - Secrets from Vault](/overlay/tutorials/running-locally#se
| `INSTALLATION_METHOD` | Deployment method: `helm` or `operator` | `helm` | No |
| `CHART_URL` | Custom Helm chart URL | `oci://quay.io/rhdh/chart` | No |

### New frontend system (app-next / NFS)

When the new frontend system is active (see [`useNewFrontendSystem`](/guide/deployment/rhdh-deployment#new-frontend-system-usenewfrontendsystem) and [environment variables](/guide/configuration/environment-variables#new-frontend-system-app-next)), the package merges NFS defaults then your workspace files. Pin **app-auth** / **app-integrations** OCI refs in `tests/config/dynamic-plugins.yaml` like any other plugin (your file wins over package defaults).

| Variable | Description |
|----------|-------------|
| `USE_NEW_FRONTEND_SYSTEM` | When `"true"`, enables NFS merges if `useNewFrontendSystem` is not set in `configure()` options |

### Cluster Configuration

| Variable | Description | Default | Required |
Expand Down
12 changes: 12 additions & 0 deletions docs/overlay/test-structure/configuration-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,23 @@ tests/config/
├── rhdh-secrets.yaml # Kubernetes secrets (optional)
├── dynamic-plugins.yaml # Dynamic plugins (optional - usually not needed)
├── value_file.yaml # Helm values override (optional, Helm only)
├── value_file-app-next.yaml # Extra Helm values when new frontend system is active (optional)
└── subscription.yaml # Operator subscription (optional, Operator only)
```

**All of these files are optional.** Only create them when you need to override or extend defaults.

## `useNewFrontendSystem` (app-next / NFS)

When the new frontend system is enabled (see [`configure()`](/guide/deployment/rhdh-deployment#configureoptions) and [New frontend system](/guide/deployment/rhdh-deployment#new-frontend-system-usenewfrontendsystem)), `@red-hat-developer-hub/e2e-test-utils` merges:

- **Secrets** — `APP_CONFIG_app_packageName=app-next` and `ENABLE_STANDARD_MODULE_FEDERATION=true` into the `rhdh-secrets` Secret (merged with common/auth/workspace layers before `envsubst`; you no longer need duplicate keys in a separate `rhdh-secrets-next.yaml` for that).
- **Dynamic plugins** — Default OCI entries for **app-auth** and **app-integrations** as package defaults; override versions in `tests/config/dynamic-plugins.yaml` ([guide](/guide/configuration/config-files#dynamic-pluginsyaml)).

You can **remove** hand-maintained `app-auth` / `app-integrations` lines from `dynamic-plugins.yaml` when the framework defaults match your train.

Optional **`value_file-app-next.yaml`** is merged last when the new frontend system is active and the file exists — use for chart tweaks specific to app-next runs.

## app-config-rhdh.yaml (Optional)

The main RHDH configuration file. This file is merged with default configurations from `@red-hat-developer-hub/e2e-test-utils`.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@red-hat-developer-hub/e2e-test-utils",
"version": "1.1.40",
"version": "1.1.41",
"description": "Test utilities for RHDH E2E tests",
"license": "Apache-2.0",
"repository": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
plugins:
- 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
disabled: false
- 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
disabled: false
8 changes: 8 additions & 0 deletions src/deployment/rhdh/config/new-frontend-system/secrets.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: v1
kind: Secret
metadata:
name: rhdh-secrets
type: Opaque
stringData:
APP_CONFIG_app_packageName: app-next
ENABLE_STANDARD_MODULE_FEDERATION: "true"
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Helm values merge layer for the new frontend system (app-next); merged when NFS layers are active (additive; extend as needed).
{}
15 changes: 15 additions & 0 deletions src/deployment/rhdh/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@ export const DEFAULT_CONFIG_PATHS = {
PACKAGE_ROOT,
"dist/deployment/rhdh/config/common/dynamic-plugins.yaml",
),
/** New frontend system (app-next): paths merged when the deployment uses NFS layers (explicit flag, `-app-next` namespace, or `USE_NEW_FRONTEND_SYSTEM`). */
newFrontendSystem: {
secrets: path.join(
PACKAGE_ROOT,
"dist/deployment/rhdh/config/new-frontend-system/secrets.yaml",
),
dynamicPlugins: path.join(
PACKAGE_ROOT,
"dist/deployment/rhdh/config/new-frontend-system/dynamic-plugins.yaml",
),
valueFile: path.join(
PACKAGE_ROOT,
"dist/deployment/rhdh/config/new-frontend-system/value_file.yaml",
),
},
helm: {
valueFile: path.join(
PACKAGE_ROOT,
Expand Down
60 changes: 60 additions & 0 deletions src/deployment/rhdh/deployment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,64 @@ describe("dynamic-plugins merge (no user config path)", () => {
"metadata (OCI) must win over auth local path",
);
});

it("keeps user app-auth OCI ref over NFS defaults for same logical plugin", () => {
const nfsDefaults: Record<string, unknown> = {
plugins: [
{
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",
disabled: false,
},
],
};
const userPlugins: Record<string, unknown> = {
plugins: [
{
package:
"oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-backstage-plugin-app-auth:older__0.0.1",
disabled: false,
},
],
};
const merged = deepMerge(nfsDefaults, userPlugins, {
arrayMergeStrategy: {
byKey: "package",
normalizeKey: (item) =>
getNormalizedPluginMergeKey(item as Record<string, unknown>),
},
});
const plugins = merged.plugins as Array<{ package?: string }>;
assert.strictEqual(plugins.length, 1);
assert.ok(
plugins[0].package?.includes("older__0.0.1"),
"user layer must win over NFS defaults for the same logical plugin",
);
});

it("does not duplicate includes when NFS layer omits includes", () => {
const common: Record<string, unknown> = {
includes: ["dynamic-plugins.default.yaml"],
plugins: [],
};
const nfsLayer: Record<string, unknown> = {
plugins: [
{
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",
disabled: false,
},
],
};
const merged = deepMerge(common, nfsLayer, {
arrayMergeStrategy: {
byKey: "package",
normalizeKey: (item) =>
getNormalizedPluginMergeKey(item as Record<string, unknown>),
},
});
const includes = merged.includes as unknown[];
assert.strictEqual(includes?.length, 1);
assert.strictEqual(includes[0], "dynamic-plugins.default.yaml");
});
});
Loading
Loading