AGENTS.md@e2e/journey · diff
git:20261019.7f82bfc to git:20261020.8d47d7a
74 added, 99 removed. Audit A to A.
- # CLI Journey E2E Instructions
+ # Agent Instructions — `e2e/journey`
- These instructions apply to `e2e/journey/**`. Defer to the root
- `AGENTS.md` for repository-wide rules.
+ The customer's local setup journey, for every CLI framework: a fresh app
+ directory runs `doctor`, `start` and `setup --server local`, then starts the
+ generated app. Never test the checked-in demo apps here. Paths below are from
+ the repo root.
- ## Purpose
+ ## The journey
- 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.
+ - CI runs `npx @zitadel/cli@alpha doctor --runtime binary`,
+ `start --runtime binary` and `setup --framework <id> --server local`, each
+ with `--non-interactive --json`, for every id in `ciFrameworkIds`
+ (`e2e/journey/scripts/frameworks.mjs`). The ids in `ciExcludedFrameworkIds`
+ run only by hand (`--framework <id>`).
+ - The fresh directory starts empty (page posture) or from
+ `e2e/journey/fixtures/preexisting/<framework>` (`--preexisting-app`, widget
+ posture, ADR 044); both run the same CLI path and browser journeys.
+ - Keep each pre-existing fixture minimal but real: detectable by the CLI,
+ bootable with `npm run dev`, with a distinctive shell and homepage the specs
+ assert survive setup. Only route-based frameworks (Next, Nuxt) get one; the
+ SPA families keep the page posture.
+ - Use `npm` in the generated app, as the documented path does.
+ - Keep generated apps and temporary registries outside the worktree, and the
+ app on `localhost`: WebAuthn rejects IP relying-party IDs.
+ - The remote https lane (`--remote`) is the one exception to `--server local`:
+ setup points at `e2e/journey/scripts/https-edge.mjs`, a local https edge for
+ `zitadel.test` that, like Vercel's, completes TLS only for its own name,
+ routes by `Host` and overwrites `X-Forwarded-*`.
+ - Docker fallback coverage opts in with `--runtime docker --image <tag>`.
+ - Passkey coverage is required in CI and rides the `passkey-first` preset
+ lane, because the shipped `password-first` flow offers no passkey.
+ `JOURNEY_ENABLE_PASSKEY=0` is a local debugging escape hatch only.
+ - The testkit suite (`moon run e2e:journey -- --suite testkit`) installs
+ `@zitadel/testing` from the journey registry into one Next app and runs the
+ consumer suite in `e2e/journey/fixtures/testkit/`. It never sets
+ `ZITADEL_SERVER_BINARY` or `NEXTGEN_*`: proving the published binary works
+ unconfigured is the point.
- ## Maintenance Rules
+ ## Packages and the registry
- - 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.
+ - Install `@zitadel/*` from the workflow's tarballs through the temporary
+ Verdaccio registry, never from public npm.
+ - Produce tarballs with `moon run release:pack`, which stages the server
+ binaries, packs every public package and verifies their metadata.
+ `--tarballs-dir` hands prebuilt ones to the runner, which skips building but
+ still verifies and publishes them.
+ - `e2e:journey-local` never packs its own: `release:pack` rewrites the dist
+ folders the lanes beside it read. It sets `JOURNEY_TARBALLS_REQUIRED=1`; by
+ hand, `moon run e2e:journey -- --ci` packs its own.
+ - Pack and upload only `PUBLIC_RELEASE_PACKAGES`
+ (`scripts/release-manifest.mjs`); `e2e/journey/scripts/verify-tarballs.mjs`
+ enforces that set.
+ - Keep Verdaccio's npmjs metadata cache (`maxage` in
+ `e2e/journey/scripts/local-registry.mjs`) well under two hours: `nuxi init`
+ can ask for a nuxt released two hours earlier.
+
+ ## The runner
+
+ - CI runs every variant (fresh-app matrix, passkey preset, pre-existing app,
+ testkit, remote https) in one `--ci` invocation, the `e2e:journey-local`
+ lane, so one process shares one port allocator. A new variant joins the
+ runner's `--ci` set, never a new workflow step. How the lane reaches CI:
+ [`packages/testing/AGENTS.md`](../../packages/testing/AGENTS.md#moon-and-the-e2e-lanes).
+ - `scripts/ci-mode.mjs` gates the tarball snapshot and collapses the
+ fresh-app matrix to one framework (`JOURNEY_MATRIX=single`) when no SDK, CLI
+ or journey surface changed.
- 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.
+ `e2e/journey/scripts/run-options.mjs`): turbo puts arguments after `--` into
+ every task's cache key.
+ - Keep each framework suite Playwright-serial with one worker. Suites run in
+ parallel only with their own app directory, npm cache and tmp directories,
+ app port, Zitadel port and runtime state.
+ - Reserve ports through the block allocator in
+ `e2e/journey/scripts/ports.mjs`, never a listen-on-zero probe: a port
+ reserved minutes before use gets taken as an outbound source port meanwhile.
+ - Time every step that takes seconds with `timePhase` and add its phase to
+ `PHASES` (`e2e/journey/scripts/timings.mjs`): the lane × phase table in the
+ job summary is how a slow lane is found.
+ - Upload logs, setup JSON, lockfiles, Playwright traces and reports and
+ metadata; never generated `node_modules` or `.next` directories.