Skip to content

Commit 203f7aa

Browse files
gustavoliraclaude
andcommitted
refactor(e2e): single populate script, @cluster-free tag scoping, review fixes
Apply the code-review findings on the cluster-free harness: - Extract the dynamic-plugins-root populate step into e2e-tests/local-harness/populate.sh — the single source of truth used by CI, the docs, and the global-setup error message (they had already diverged: CI installed the harness OCI set pinned to CLI 0.2.0 while the docs/error message pointed at the catalog index, unversioned). - Drop the unused CATALOG_INDEX_IMAGE env from the workflow: with the harness dynamic-plugins.yaml (no includes), the CLI only used it to extract a catalog index that nothing consumed — a wasted :latest quay pull on every run. Fix the workflow header comment accordingly (plugins come from ghcr OCI, not the catalog index). - Scope the harness run by a @cluster-free test tag instead of a title regex; widening coverage is now tagging the validated test and allowlisting its spec file in testMatch. - Count only directories in the global-setup guard: the installer writes its generated global-config file into dynamic-plugins-root even when zero plugins install, which previously satisfied the guard and produced the confusing locator timeout it exists to prevent. - Rewrite the docs populate section: the OCI/populate.sh path (what CI uses, works from a fresh clone) is primary; the catalog-index path is demoted to after-source-build use since its default.yaml references ./dynamic-plugins/dist paths that don't exist otherwise. - Gitignore the root dynamic-plugins.yaml copy the populate script leaves behind (the CLI hardcodes that path). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 9c56ab1 commit 203f7aa

8 files changed

Lines changed: 101 additions & 59 deletions

File tree

.github/workflows/e2e-cluster-free.yaml

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: E2E Cluster-free Harness
22

33
# Runs the cluster-free local E2E harness (RHIDP-13501 / RHIDP-15075): boots the
44
# backend and the legacy app dev server in-process and drives Playwright against
5-
# them, with dynamic plugins pulled from the public catalog index via the
6-
# install-dynamic-plugins CLI (skopeo). No OpenShift/Kubernetes cluster or image.
7-
# See docs/e2e-tests/local-e2e-harness.md.
5+
# them, with dynamic plugins installed from the public OCI registry (ghcr) via
6+
# the install-dynamic-plugins CLI (skopeo). No OpenShift/Kubernetes cluster or
7+
# image. See docs/e2e-tests/local-e2e-harness.md.
88

99
on:
1010
pull_request:
@@ -28,10 +28,6 @@ concurrency:
2828
permissions:
2929
contents: read
3030

31-
env:
32-
# Public index; release branches can override to the matching :1.y tag.
33-
CATALOG_INDEX_IMAGE: quay.io/rhdh/plugin-catalog-index:latest
34-
3531
jobs:
3632
legacy-local:
3733
name: Cluster-free E2E (legacy app)
@@ -77,14 +73,11 @@ jobs:
7773
run: yarn install --mode=skip-build
7874

7975
- name: Populate dynamic-plugins-root (OCI, no source build)
80-
# install-dynamic-plugins pulls the dynamic-home-page frontend plugin from the
81-
# public OCI registry (ghcr) via skopeo — the core plugins in the catalog
82-
# index's default.yaml reference local ./dynamic-plugins/dist paths that only
83-
# exist after a source build, so we install the OCI-published build instead.
84-
run: |
85-
mkdir -p dynamic-plugins-root
86-
cp e2e-tests/local-harness/dynamic-plugins.yaml dynamic-plugins.yaml
87-
npx -y @red-hat-developer-hub/cli-module-install-dynamic-plugins@0.2.0 install dynamic-plugins-root
76+
# install-dynamic-plugins pulls the harness plugin set from the public OCI
77+
# registry (ghcr) via skopeo — the core plugins in the catalog index's
78+
# default.yaml reference local ./dynamic-plugins/dist paths that only exist
79+
# after a source build, so we install the OCI-published builds instead.
80+
run: ./e2e-tests/local-harness/populate.sh
8881

8982
- name: Run cluster-free E2E (legacy app)
9083
working-directory: ./e2e-tests

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ site
6464
# Dynamic plugins root content
6565
dynamic-plugins-root/*
6666
!dynamic-plugins-root/.gitkeep
67+
# install-dynamic-plugins config copied to the repo root by e2e-tests/local-harness/populate.sh
68+
/dynamic-plugins.yaml
6769

6870
#dev caches
6971
.webpack-cache

docs/e2e-tests/local-e2e-harness.md

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,36 @@ sign in."_
2222

2323
### 1. Populate `dynamic-plugins-root` (one-time)
2424

25-
Production-faithful — full plugin set and generated config, the same source CI uses:
25+
Run the same script CI uses — it installs the harness plugin set
26+
(`e2e-tests/local-harness/dynamic-plugins.yaml`) from the public OCI registry (ghcr)
27+
via `install-dynamic-plugins` + skopeo, pinned to the same CLI version as CI. No
28+
source build needed; works from a fresh clone. Requires skopeo (Linux/CI — not
29+
available on macOS):
2630

2731
```bash
28-
# main branch -> :latest; release branches -> the matching :1.y tag
29-
CATALOG_INDEX_IMAGE=quay.io/rhdh/plugin-catalog-index:latest \
30-
npx @red-hat-developer-hub/cli-module-install-dynamic-plugins install dynamic-plugins-root
32+
./e2e-tests/local-harness/populate.sh
3133
```
3234

33-
Offline alternative (frontend plugins only; requires a reconciled workspace —
34-
see "Known issues"):
35+
Alternatives:
3536

36-
```bash
37-
yarn --cwd dynamic-plugins export-dynamic
38-
yarn --cwd dynamic-plugins copy-dynamic-plugins ../dynamic-plugins-root
39-
```
37+
- **Catalog index** — the index's `dynamic-plugins.default.yaml` references the core
38+
plugins by local `./dynamic-plugins/dist/…` paths that only exist after a source
39+
build, so on a fresh clone most plugins are skipped. Use only after building
40+
`dynamic-plugins` from source (main -> `:latest`; release branches -> the matching
41+
`:1.y` tag):
42+
43+
```bash
44+
CATALOG_INDEX_IMAGE=quay.io/rhdh/plugin-catalog-index:latest \
45+
npx @red-hat-developer-hub/cli-module-install-dynamic-plugins install dynamic-plugins-root
46+
```
47+
48+
- **Offline from-source** (frontend plugins only; requires a reconciled workspace —
49+
see "Known issues"):
50+
51+
```bash
52+
yarn --cwd dynamic-plugins export-dynamic
53+
yarn --cwd dynamic-plugins copy-dynamic-plugins ../dynamic-plugins-root
54+
```
4055

4156
### 2. Run
4257

@@ -47,11 +62,12 @@ yarn --cwd e2e-tests e2e:legacy-local
4762
Playwright (`playwright.legacy-local.config.ts`) boots the backend and the legacy app
4863
dev server with `app-config.yaml` + `app-config.dynamic-plugins.yaml` +
4964
`app-config.local-e2e.yaml`. A `globalSetup` first fails fast with the populate command
50-
if `dynamic-plugins-root` is empty.
65+
if `dynamic-plugins-root` has no plugins.
5166

52-
By default the run is scoped (via `grep`) to the one test verified green off-cluster so
53-
far — the `guest-signin-happy-path` home-page test. Widen `testMatch`/`grep` as more
54-
specs are validated (see "Known issues").
67+
The run is scoped to tests tagged `@cluster-free` within the spec files allowlisted in
68+
`testMatch` — today the one test verified green off-cluster, the
69+
`guest-signin-happy-path` home-page test. To widen coverage, tag a validated test with
70+
`@cluster-free` and add its spec file to `testMatch` (see "Known issues").
5571

5672
### Verified
5773

@@ -63,11 +79,11 @@ frontend plugin renders with no cluster.
6379
## CI
6480

6581
`.github/workflows/e2e-cluster-free.yaml` runs this harness on GitHub Actions in a
66-
cluster-free phase: it installs deps + skopeo, populates `dynamic-plugins-root` from the
67-
public catalog index via the `install-dynamic-plugins` CLI (the same mechanism the
68-
nightly sanity check uses), then runs `yarn e2e:legacy-local`. No cluster or container
69-
image is built. It triggers on `e2e-tests/**` and `app-config*.yaml` changes; the scope
70-
can widen to `packages/app/**` / `packages/backend/**` once it is proven stable.
82+
cluster-free phase: it installs deps + skopeo, populates `dynamic-plugins-root` via
83+
`./e2e-tests/local-harness/populate.sh` (the harness plugin set from the public OCI
84+
registry, ghcr), then runs `yarn e2e:legacy-local`. No cluster or container image is
85+
built. It triggers on `e2e-tests/**` and `app-config*.yaml` changes; the scope can
86+
widen to `packages/app/**` / `packages/backend/**` once it is proven stable.
7187

7288
## Why the legacy app, not app-next
7389

e2e-tests/local-harness/dynamic-plugins.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
# Cluster-free harness plugin set — installed via install-dynamic-plugins from OCI
2-
# (ghcr, pulled with skopeo), so no dynamic-plugins/dist source build is needed.
1+
# Cluster-free harness plugin set — installed by ./populate.sh via
2+
# install-dynamic-plugins from OCI (ghcr, pulled with skopeo), so no
3+
# dynamic-plugins/dist source build is needed.
34
#
45
# app-config.dynamic-plugins.yaml (loaded by the harness) already configures these
56
# plugins, so the existing RHDH specs render off-cluster.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/bin/bash
2+
#
3+
# Populates dynamic-plugins-root for the cluster-free E2E harness — the single
4+
# source of truth for the populate step (CI, the docs, and the global-setup
5+
# error message all point here).
6+
#
7+
# Installs the plugin set from e2e-tests/local-harness/dynamic-plugins.yaml
8+
# from the public OCI registry (ghcr) via install-dynamic-plugins + skopeo —
9+
# no dynamic-plugins/dist source build and no cluster. Requires skopeo
10+
# (Linux/CI; not available on macOS — see docs/e2e-tests/local-e2e-harness.md).
11+
set -e
12+
13+
# Pinned so local runs install the exact CLI version CI uses.
14+
CLI_VERSION="0.2.0"
15+
16+
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
17+
cd "$REPO_ROOT"
18+
19+
mkdir -p dynamic-plugins-root
20+
# The CLI hardcodes ./dynamic-plugins.yaml (cwd) as its config file; the copy at
21+
# the repo root is gitignored.
22+
cp e2e-tests/local-harness/dynamic-plugins.yaml dynamic-plugins.yaml
23+
npx -y "@red-hat-developer-hub/cli-module-install-dynamic-plugins@$CLI_VERSION" install dynamic-plugins-root

e2e-tests/playwright.legacy-local.config.ts

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,9 @@ import { defineConfig, devices } from "@playwright/test";
1010
* OpenShift/Kubernetes cluster or container images. Playwright boots the backend and
1111
* the legacy app dev server itself and drives the browser against them.
1212
*
13-
* # one-time: populate dynamic-plugins-root (production-faithful — full plugin set
14-
* # and generated config, same source CI uses):
15-
* # main -> :latest; release branches -> the matching :1.y tag
16-
* CATALOG_INDEX_IMAGE=quay.io/rhdh/plugin-catalog-index:latest \
17-
* npx @red-hat-developer-hub/cli-module-install-dynamic-plugins install dynamic-plugins-root
18-
* # (offline alternative, frontend plugins only, needs reconciled deps:
19-
* # yarn --cwd dynamic-plugins export-dynamic && \
20-
* # yarn --cwd dynamic-plugins copy-dynamic-plugins ../dynamic-plugins-root)
13+
* # one-time: populate dynamic-plugins-root (same script CI uses — OCI, no build;
14+
* # alternatives in docs/e2e-tests/local-e2e-harness.md):
15+
* ./e2e-tests/local-harness/populate.sh
2116
*
2217
* yarn --cwd e2e-tests e2e:legacy-local
2318
*
@@ -42,13 +37,16 @@ export default defineConfig({
4237
testDir: "./playwright",
4338
// Fails fast if dynamic-plugins-root has not been populated.
4439
globalSetup: "./playwright/support/local-harness-global-setup.ts",
45-
// Runs the guest-signin home-page test, verified green off-cluster (the dynamic
46-
// home-page plugin renders from OCI). `grep` scopes to it because the sibling
47-
// Settings/Sign-out tests navigate via the top-right profile dropdown, which needs
48-
// the global-header plugin to *mount* in the layout — it loads off-cluster but
49-
// rendering it needs more config (follow-up). Widen as specs are validated.
40+
// A test runs cluster-free when its spec file is listed in `testMatch` (an
41+
// allowlist, so unvalidated specs are never loaded) AND it carries the
42+
// @cluster-free tag. To widen coverage: tag the test where it lives and add its
43+
// spec file here. Today that is the guest-signin home-page test, verified green
44+
// off-cluster (the dynamic home-page plugin renders from OCI); its sibling
45+
// Settings/Sign-out tests stay untagged because they navigate via the top-right
46+
// profile dropdown, which needs the global-header plugin to *mount* in the
47+
// layout — it loads off-cluster but rendering it needs more config (follow-up).
5048
testMatch: ["e2e/guest-signin-happy-path.spec.ts"],
51-
grep: /Homepage renders with Search Bar/u,
49+
grep: /@cluster-free/u,
5250
timeout: 90 * 1000,
5351
forbidOnly: isCI,
5452
retries: isCI ? 1 : 0,

e2e-tests/playwright/e2e/guest-signin-happy-path.spec.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,16 @@ test.describe("Guest Signing Happy path", () => {
2727
await common.loginAsGuest();
2828
});
2929

30-
test("Verify the Homepage renders with Search Bar, Quick Access and Starred Entities", async () => {
31-
await uiHelper.verifyHeading("Welcome back!");
32-
await uiHelper.openSidebar("Home");
33-
await homePage.verifyQuickAccess("Developer Tools", "Podman Desktop");
34-
});
30+
// @cluster-free: verified green on the cluster-free harness (playwright.legacy-local.config.ts)
31+
test(
32+
"Verify the Homepage renders with Search Bar, Quick Access and Starred Entities",
33+
{ tag: "@cluster-free" },
34+
async () => {
35+
await uiHelper.verifyHeading("Welcome back!");
36+
await uiHelper.openSidebar("Home");
37+
await homePage.verifyQuickAccess("Developer Tools", "Podman Desktop");
38+
},
39+
);
3540

3641
test("Verify Profile is Guest in the Settings page", async () => {
3742
await uiHelper.goToSettingsPage();

e2e-tests/playwright/support/local-harness-global-setup.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,22 @@ export default function requireDynamicPluginsPopulated(): void {
1212
// process.cwd() is e2e-tests when Playwright runs; the plugins root is at repo root.
1313
const root = resolve(process.cwd(), "..", "dynamic-plugins-root");
1414

15+
// Plugins are installed as one directory each; count only directories so the
16+
// installer's generated global-config file (written into the same root even when
17+
// zero plugins install) does not satisfy the guard.
1518
let pluginCount = 0;
1619
try {
17-
pluginCount = readdirSync(root).filter((entry) => entry !== ".gitkeep").length;
20+
pluginCount = readdirSync(root, { withFileTypes: true }).filter((entry) =>
21+
entry.isDirectory(),
22+
).length;
1823
} catch {
1924
// root missing — treated as empty below.
2025
}
2126

2227
if (pluginCount === 0) {
2328
throw new Error(
24-
`dynamic-plugins-root is empty — populate it before running e2e:legacy-local:\n\n` +
25-
` CATALOG_INDEX_IMAGE=quay.io/rhdh/plugin-catalog-index:latest \\\n` +
26-
` npx @red-hat-developer-hub/cli-module-install-dynamic-plugins install dynamic-plugins-root\n\n` +
29+
`dynamic-plugins-root has no plugins — populate it before running e2e:legacy-local:\n\n` +
30+
` ./e2e-tests/local-harness/populate.sh\n\n` +
2731
`See docs/e2e-tests/local-e2e-harness.md.`,
2832
);
2933
}

0 commit comments

Comments
 (0)