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
2 changes: 1 addition & 1 deletion .github/workflows/e2e-cluster-free.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
apt-get install -y --no-install-recommends skopeo

- name: Install dependencies (root)
run: yarn install

Check warning on line 73 in .github/workflows/e2e-cluster-free.yaml

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Lifecycle scripts are enabled by default in Yarn v2+.

See more on https://sonarcloud.io/project/issues?id=redhat-developer_rhdh&issues=AZ_pyUhO9zenDAhtFtr0&open=AZ_pyUhO9zenDAhtFtr0&pullRequest=5232

- name: Install dependencies (e2e-tests)
working-directory: ./e2e-tests
Expand All @@ -83,7 +83,7 @@
# after a source build, so we install the OCI-published builds instead.
run: ./e2e-tests/local-harness/populate.sh

- name: Run cluster-free E2E (legacy app)
- name: Run cluster-free E2E (NFS app)
working-directory: ./e2e-tests
run: yarn e2e:legacy-local

Expand Down
2 changes: 1 addition & 1 deletion app-config.local-e2e.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Config overlay for the cluster-free local E2E harness (legacy `packages/app`, Tier B).
# Config overlay for the cluster-free local E2E harness (NFS `packages/app`, Tier B).
#
# Layered on top of app-config.yaml and app-config.dynamic-plugins.yaml to run
# Playwright E2E without an OpenShift/Kubernetes cluster or container images:
Expand Down
1 change: 0 additions & 1 deletion build/containerfiles/Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ COPY $EXTERNAL_SOURCE_NESTED/packages/theme-wrapper/package.json ./packages/them
COPY $EXTERNAL_SOURCE_NESTED/packages/plugin-utils/package.json ./packages/plugin-utils/package.json
COPY $EXTERNAL_SOURCE_NESTED/packages/backend/package.json ./packages/backend/package.json
COPY $EXTERNAL_SOURCE_NESTED/packages/app/package.json ./packages/app/package.json
COPY $EXTERNAL_SOURCE_NESTED/packages/app-next/package.json ./packages/app-next/package.json
COPY $EXTERNAL_SOURCE_NESTED/package.json ./package.json
# END COPY package.json files

Expand Down
19 changes: 4 additions & 15 deletions docs/dynamic-plugins/migrating-config-to-new-frontend-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,20 @@ This guide helps **operators and platform administrators** customize Red Hat Dev
>
> **Plugin authors** → [Migrating Plugins to the New Frontend System](migrating-plugins-to-new-frontend-system.md).

## Transition: the new frontend system is not the default yet
## Default: new frontend system (NFS)

RHDH still ships the legacy `app` frontend package by default. The new frontend system lives in the `app-next` package and will become the default after the app-shell switch. Until then, enable **both** of the following on your RHDH **backend** deployment (OpenShift, Helm, Operator, [rhdh-local](https://github.com/redhat-developer/rhdh-local), or any environment where the backend runs as a container):
RHDH ships the Backstage new frontend system (`packages/app`) by default. Enable standard Module Federation on the **backend** deployment so dynamic frontend plugins load correctly (OpenShift, Helm, Operator, [rhdh-local](https://github.com/redhat-developer/rhdh-local), or any environment where the backend runs as a container):

| Setting | How to apply | Purpose |
| --- | --- | --- |
| `app.packageName: app-next` | Environment variable `APP_CONFIG_app_packageName=app-next`, **or** in `app-config.yaml` under `app.packageName` | Tells the app backend to serve the `app-next` frontend (new frontend system) instead of `app`. |
| `ENABLE_STANDARD_MODULE_FEDERATION=true` | Environment variable on the backend container only | Enables the backend to serve standard Module Federation assets for dynamic frontend plugins. Without this, RHDH disables that service because the legacy frontend does not use it. |
| `ENABLE_STANDARD_MODULE_FEDERATION=true` | Environment variable on the backend container only | Enables the backend to serve standard Module Federation assets for dynamic frontend plugins. Without this, RHDH disables that service. |

Example environment variables for the RHDH backend pod or deployment:
Example environment variable for the RHDH backend pod or deployment:

```bash
APP_CONFIG_app_packageName=app-next
ENABLE_STANDARD_MODULE_FEDERATION=true
```

Equivalent `app-config` fragment (you still need `ENABLE_STANDARD_MODULE_FEDERATION` in the environment):

```yaml
app:
packageName: app-next
```

These requirements are temporary. Once RHDH completes the switch to `app-next`, they will become the default and this transition note can be removed.

## Who should read this

- RHDH administrators who edit `dynamic-plugins.yaml`, Helm values, or Operator configuration.
Expand Down
21 changes: 9 additions & 12 deletions docs/e2e-tests/local-e2e-harness.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ backend dynamic-plugin loader from RHIDP-13508.
## Goal

Run real Playwright E2E against RHDH **without** an OpenShift/Kubernetes cluster or
container images — a single `run` that boots the backend and the legacy frontend dev
container images — a single `run` that boots the backend and the NFS frontend dev
server in-process and drives a browser against them.

The harness targets the legacy frontend (`packages/app`, Tier B): it is what RHDH ships
The harness targets the NFS frontend (`packages/app`, Tier B): it is what RHDH ships
today, and **the existing Playwright specs already target it**, so they run unmodified.
Dynamic frontend plugins load through Scalprum exactly as in-cluster (the legacy
`scalprum-backend` serves the plugin config by default).
Dynamic frontend plugins load via standard Module Federation when the backend is started
with `ENABLE_STANDARD_MODULE_FEDERATION=true`.

The guest-auth + in-memory-SQLite overlay `app-config.local-e2e.yaml` is layered on top
of `app-config.yaml`. Guest sign-in must be configured explicitly — the auth backend
Expand Down Expand Up @@ -126,15 +126,12 @@ registry, ghcr), then runs `yarn e2e:legacy-local`. No cluster or container imag
built. It triggers on `e2e-tests/**` and `app-config*.yaml` changes; the scope can
widen to `packages/app/**` / `packages/backend/**` once it is proven stable.

## Why the legacy app, not app-next
## NFS frontend (`packages/app`)

The harness targets the legacy app because **dynamic frontend plugins do not load on
`packages/app-next` yet**: app-next's `dynamicFrontendFeaturesLoader()` fetches Module
Federation remotes from the backend, but that endpoint is no-op'd unless
`ENABLE_STANDARD_MODULE_FEDERATION=true`, and even then RHDH's exported dynamic frontend
plugins do not contain standard MF assets (see `packages/backend/src/index.ts`). Until
that lands upstream, app-next can only exercise core/static plugin UIs. An app-next
harness is tracked as a follow-up (RHIDP-13501 / spike RHIDP-15075).
The harness targets the NFS frontend (`packages/app`). The backend boots with
`ENABLE_STANDARD_MODULE_FEDERATION=true` so dynamic frontend plugins load via Module
Federation remotes from the backend (see `packages/backend/src/index.ts` and
`playwright.legacy-local.config.ts`).

## vs. rhdh-local

Expand Down
18 changes: 10 additions & 8 deletions e2e-tests/playwright.legacy-local.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { resolve } from "path";
import { defineConfig, devices } from "@playwright/test";

/**
* Cluster-free local E2E harness for the legacy frontend (`packages/app`) — Tier B.
* Cluster-free local E2E harness for the NFS frontend (`packages/app`) — Tier B.
*
* RHIDP-13501 (E2E Test Optimization). Runs the EXISTING Playwright specs against a
* production-faithful RHDH instance with dynamic plugins loaded, without an
* OpenShift/Kubernetes cluster or container images. Playwright boots the backend and
* the legacy app dev server itself and drives the browser against them.
* the NFS app dev server itself and drives the browser against them.
*
* # one-time: populate dynamic-plugins-root (same script CI uses — OCI, no build;
* # alternatives in docs/e2e-tests/local-e2e-harness.md):
Expand Down Expand Up @@ -49,7 +49,7 @@ export default defineConfig({
// spec file here. Validated so far: the full guest-signin spec (home page via the
// dynamic-home-page OCI plugin; Settings/Sign-out via the global-header OCI plugin
// with its canonical pluginConfig), the learning-paths spec (static fallback
// data bundled with packages/app), and the instance health check (/healthcheck is
// data bundled with the legacy OFS frontend), and the instance health check (/healthcheck is
// proxied to the backend by the app dev server — see packages/app package.json —
// mirroring the single-origin production container).
testMatch: [
Expand All @@ -61,6 +61,7 @@ export default defineConfig({
"e2e/plugins/frontend/sidebar.spec.ts",
"e2e/settings.spec.ts",
"e2e/plugins/user-settings-info-card.spec.ts",
"e2e/plugins/tmp-infocard-check.spec.ts",
"e2e/plugins/application-provider.spec.ts",
"e2e/plugins/application-listener.spec.ts",
],
Expand Down Expand Up @@ -94,10 +95,10 @@ export default defineConfig({
expect: {
timeout: 15 * 1000,
},
// backstage-cli / janus-cli live in the repo-root node_modules/.bin, which yarn does
// not surface for these workspaces, so both CLIs are invoked directly with the root
// .bin prepended to PATH and run from their package directory. The backend command
// mirrors packages/backend's `start` script (--require instrumentation) — keep in sync.
// backstage-cli lives in the repo-root node_modules/.bin, which yarn does not surface
// for these workspaces, so the CLI is invoked directly with the root .bin prepended
// to PATH and run from each package directory. The backend command mirrors
// packages/backend's `start` script (--require instrumentation) — keep in sync.
webServer: [
{
command: `backstage-cli package start --require ./src/instrumentation.js ${sharedConfigArgs}`,
Expand All @@ -106,6 +107,7 @@ export default defineConfig({
...process.env,
PATH: pathWithRepoBin,
NODE_OPTIONS: "--no-node-snapshot",
ENABLE_STANDARD_MODULE_FEDERATION: "true",
},
url: backendReadiness,
reuseExistingServer: !isCI,
Expand All @@ -114,7 +116,7 @@ export default defineConfig({
stderr: "pipe",
},
{
command: `janus-cli package start ${sharedConfigArgs}`,
command: `backstage-cli package start ${sharedConfigArgs}`,
cwd: "../packages/app",
env: { ...process.env, PATH: pathWithRepoBin },
url: frontendUrl,
Expand Down
16 changes: 16 additions & 0 deletions e2e-tests/playwright/e2e/plugins/tmp-infocard-check.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { test, expect } from "@playwright/test";

import { Common } from "../../utils/common";

test("tmp: settings general renders InfoCard", { tag: "@cluster-free" }, async ({ page }) => {
const common = new Common(page);
await common.loginAsGuest();

await page.goto("/settings/general");
await expect(page.getByText("RHDH Build info")).toBeVisible({ timeout: 20000 });
await expect(page.getByText("TechDocs builder: local")).toBeVisible();
await expect(page.getByText("Authentication provider: Github")).toBeVisible();

await page.getByTitle("Show more").click();
await expect(page.getByText("RBAC: disabled")).toBeVisible();
});
4 changes: 2 additions & 2 deletions e2e-tests/playwright/utils/ui-helper/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ export async function waitForSideBarVisible(page: Page) {
}

export async function openSidebar(page: Page, navBarText: string) {
// Legacy packages/app sidebar (cluster-free harness) uses plain nav anchors.
// Legacy OFS sidebar uses plain nav anchors.
const legacyLink = page.locator(`nav a:has-text("${navBarText}")`).first();
try {
await expect(legacyLink).toBeVisible({ timeout: 3_000 });
Expand Down Expand Up @@ -231,7 +231,7 @@ export async function openCatalogSidebar(page: Page, kind: string) {
}

export async function openSidebarButton(page: Page, navBarButtonLabel: string) {
// Legacy packages/app sidebar expands groups via aria-label buttons.
// Legacy OFS sidebar expands groups via aria-label buttons.
const legacyButton = page.locator(`nav button[aria-label="${navBarButtonLabel}"]`);
try {
await expect(legacyButton).toBeVisible({ timeout: 3_000 });
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"scripts": {
"prepare": "husky install",
"start": "NODE_OPTIONS=--no-node-snapshot turbo run start --filter=backend",
"dev": "NODE_OPTIONS=--no-node-snapshot turbo run start --filter=backend --filter=app",
"dev:legacy": "NODE_OPTIONS=--no-node-snapshot turbo run start --filter=backend --filter=app-legacy",
"dev": "NODE_OPTIONS=--no-node-snapshot ENABLE_STANDARD_MODULE_FEDERATION=true turbo run start --filter=backend --filter=app",
"build": "turbo run build",
"build:dockerfile": "bash ./scripts/update-Dockerfile.sh",
"tsc": "turbo run tsc",
Expand Down
File renamed without changes.
12 changes: 12 additions & 0 deletions packages/app-legacy/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const backstageConfig = require('@backstage/cli/config/eslint-factory')(
__dirname,
);

module.exports = {
...backstageConfig,
rules: {
...backstageConfig.rules,
'react/react-in-jsx-scope': 'off',
'react/jsx-uses-react': 'off',
},
};
File renamed without changes.
3 changes: 3 additions & 0 deletions packages/app-legacy/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dist
coverage
src/build-metadata.json
File renamed without changes.
File renamed without changes.
Loading
Loading