AGENTS.md@e2e/journey · git:20261019.7f82bfc · 2026-10-19 · sha256 24b98d20c8bc6016
AGENTS.md@e2e/journey git:20261019.7f82bfcA
Immutable. This exact content never changes and is served at /api/v1/blob/24b98d20c8bc6016.
# CLI Journey E2E Instructions These instructions apply to `e2e/journey/**`. Defer to the root `AGENTS.md` for repository-wide rules. ## Purpose This project protects the customer local setup journey across every supported CLI framework. Tests must exercise a fresh app directory that runs the CLI local runtime path (`doctor`, `start`, `setup --server local`) before starting the generated app. It must not test the checked-in demo apps. The fresh directory starts empty (setup scaffolds the skeleton, page posture) or seeded from `fixtures/preexisting/<framework>` (`--preexisting-app`, widget posture per ADR 044) — both shapes run the same CLI path and browser journeys. ## Maintenance Rules - Keep generated apps and temporary registries outside the repo worktree. - Use `npm` inside the generated app to match the documented user path. - Use `moon run release:pack` when producing workspace tarballs; it stages the server platform binaries, packs all public packages, and verifies resolved package metadata. - CI may pass prebuilt release snapshot tarballs to the journey runner with `--tarballs-dir`; that path must skip rebuilding and still verify/publish the provided tarballs through Verdaccio. - Every option CI sets has a `JOURNEY_*` variable (`journeyArgsFromEnv` in `scripts/run-options.mjs`): CI's turbo job passes them that way, because turbo puts arguments after `--` into every task's cache key. A new option CI needs gets a variable there too. - CI runs all journey variants (fresh-app matrix, passkey preset, pre-existing app, testkit, remote https) concurrently in a single `--ci` invocation of the runner (the `e2e:journey-local` lane, run with every other lane by the e2e step in `.github/workflows/ci.yml` through `e2e:all`), which parallelizes them through its own concurrency pool so one process shares one port allocator. The tarballs come from the snapshot step, gated on `snapshot` (a journey surface is affected or the e2e step runs, computed by `scripts/ci-mode.mjs`), and the fresh-app framework set is collapsed per-PR through `JOURNEY_MATRIX=single|full` (the `journey_matrix` output, using `scripts/frameworks.mjs`). New variants belong in the runner's `--ci` set, not as new workflow steps. - `e2e:journey-local` never packs its own tarballs: the lanes beside it read the dist folders that `moon run release:pack` wipes and rebuilds. The lane sets `JOURNEY_TARBALLS_REQUIRED=1`, so the runner refuses to start without `JOURNEY_TARBALLS_DIR`; by hand, `moon run e2e:journey -- --ci` packs its own. - CI must install Zitadel packages from current workflow tarballs through the temporary Verdaccio registry, not from public npm. - CI restores Verdaccio's storage from an earlier run, so the registry serves its cached npmjs metadata for an hour before asking npmjs again (`maxage` in `scripts/local-registry.mjs`). Keep that well under two hours: `nuxi init` downloads its starter template from GitHub, which has picked up a new nuxt release two hours after it was published, and a packument cached longer lacks the version the template asks for, failing the nuxt lanes until it expires. - CI must run `npx @zitadel/cli@alpha doctor --runtime binary`, `start --runtime binary`, and `setup --framework <id> --server local` from the fresh app directory with `--non-interactive --json`, for every id in `frameworkIds` (`scripts/frameworks.mjs`) — the SPA families and the meta-frameworks alike. - The remote https lane (`--remote`; in `--ci`, react with the passkey-first preset) is the one exception to `--server local`: setup points `--server` at `scripts/https-edge.mjs`, a local https edge for `zitadel.test` in front of the same local runtime. Like Vercel's edge it completes TLS only for its own name, routes by `Host` and overwrites `X-Forwarded-*`, so it catches a generated dev proxy that only works over plain http. The CLI and the generated app's dev server reach it through a preloaded resolver and `NODE_EXTRA_CA_CERTS` (`remoteHostEnv`); the browser still only talks to the app on `localhost`. - Docker fallback coverage must opt in with `--runtime docker --image <tag>`. - Pack and upload only the public packages — `PUBLIC_RELEASE_PACKAGES` in `scripts/release-manifest.mjs` is the authoritative list, and `verify-tarballs.mjs` enforces that both the journey registry and release artifact dirs carry exactly that set. Private support packages must stay out of the artifact set. - Keep the generated app on `localhost` for browser tests. WebAuthn rejects IP address relying-party IDs such as `127.0.0.1`. - The pre-existing-app fixtures (`fixtures/preexisting/<framework>`) must stay minimal but real: exactly detectable by the CLI (ADR 044's posture hinge), bootable via `npm run dev`, and carrying a distinctive shell and homepage the specs assert survive setup. Only route-based frameworks (Next, Nuxt) belong here — the SPA families keep the page posture. - The testkit suite (`moon run e2e:journey -- --suite testkit`) is the customer-configuration proof for `@zitadel/testing`: it scaffolds one next app, installs the kit from the journey registry, copies the checked-in consumer suite from `fixtures/testkit/`, and runs it inside the app. It must never set `ZITADEL_SERVER_BINARY` or `NEXTGEN_*` env — proving that the published binary and its embedded UIs work unconfigured is the point. - Keep each framework suite Playwright-serial and one-worker. Framework suites may run in parallel only when each suite gets its own generated app directory, npm cache/tmp directories, app port, Zitadel port, and backend runtime state. - Reserve runner ports through the block allocator in `scripts/ports.mjs`, never via a listen-on-zero probe: reserved ports are bound minutes later, and ephemeral-range ports get taken as outbound source ports by the parallel suites' npm traffic in the meantime (the why and the block layout are documented in that file). - Passkey coverage is required in CI, and rides the `passkey-first` preset lane: the shipped `password-first` default flow offers no passkey action, so the passkey journey is registered only for that preset. `JOURNEY_ENABLE_PASSKEY=0` is only a local debugging escape hatch. - Keep diagnostics focused. Upload logs, setup JSON, lockfiles, Playwright traces/reports, and metadata; do not upload generated `node_modules` or `.next` directories. - Time every lane step that takes seconds with `timePhase` and list a new phase in `PHASES` (`scripts/timings.mjs`): the lane × phase table the runner prints at the end, also in CI's job summary, is how a slow lane is found.