Skip to content
Merged
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
6 changes: 6 additions & 0 deletions .changeset/fix-start-then-setup-scaffold.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@zitadel/cli": patch
"@zitadel/sdk-nuxt": patch
---

Allow fresh app scaffolding after `zitadel start` creates local runtime ignore files, and load Nuxt runtime config through the Nuxt virtual imports module.
9 changes: 5 additions & 4 deletions .github/instructions/consumer-journey.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ gate, not as a demo-app e2e suite.
- CI must consume the current workflow's GoReleaser image and npm package
tarballs. Do not replace this with public npm packages for Zitadel packages.
- The journey must exercise the customer local runtime flow through `npx`:
`doctor`, `start`, then `setup --framework next --server local` with
`doctor`, `start`, then
`setup --framework <next|nuxt|react|vue|angular> --server local` with
`--non-interactive --json`.
- Produce package artifacts with `corepack pnpm --dir <package> pack` and keep
tarball verification for required package presence plus unresolved
Expand All @@ -19,14 +20,14 @@ gate, not as a demo-app e2e suite.
design tokens must not be uploaded or published to Verdaccio.
- Keep Verdaccio proxying npmjs for third-party dependencies while publishing
Zitadel tarballs under both `alpha` and `latest`.
- Keep generated Next.js apps outside the repo and use `npm` inside the
generated app to match the documented consumer path.
- Keep generated apps outside the repo and use `npm` inside the generated app to
match the documented consumer path.
- Preserve the CLI setup JSON contract: `--non-interactive --json` must parse
from stdout and return `status: "ok"`.
- Browser tests should run serially with one worker, use `localhost` for
WebAuthn, and require passkey coverage in CI. `JOURNEY_ENABLE_PASSKEY=0` is
only a local debugging escape hatch.
- Failure artifacts should include Playwright output/report, doctor/start/setup
JSON and stderr, local runtime metadata/logs, metadata, generated app package
manifests, Verdaccio logs, and Next logs. Do not upload generated
manifests, Verdaccio logs, and generated app logs. Do not upload generated
`node_modules` or `.next`.
76 changes: 52 additions & 24 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ jobs:
retention-days: 7

go-smoke-test-embedded-postgres:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: depot-ubuntu-24.04-4
timeout-minutes: 15
needs: [goreleaser-snapshot]
Expand Down Expand Up @@ -335,6 +336,7 @@ jobs:
node scripts/check-changeset-required.mjs --base "origin/${{ github.base_ref }}"

quickstart-smoke:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: depot-ubuntu-24.04-4
timeout-minutes: 20
needs: [goreleaser-snapshot]
Expand Down Expand Up @@ -423,6 +425,7 @@ jobs:
fi

node-e2e:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: depot-ubuntu-24.04-4
timeout-minutes: 15
steps:
Expand Down Expand Up @@ -548,9 +551,14 @@ jobs:
retention-days: 7

consumer-journey-e2e:
name: consumer-journey-e2e (${{ matrix.framework }})
runs-on: depot-ubuntu-24.04-4
timeout-minutes: 35
needs: [goreleaser-snapshot, npm-pack-smoke]
strategy:
fail-fast: false
matrix:
framework: [next, nuxt, react, vue, angular]
steps:
- uses: actions/checkout@v6

Expand Down Expand Up @@ -667,38 +675,52 @@ jobs:
docker image inspect -f '{{.Os}}/{{.Architecture}}' "$(cat goreleaser-dist/nextgen-image.tag)" | grep -q '^linux/amd64$'
echo "ZITADEL_LOCAL_IMAGE=$(cat goreleaser-dist/nextgen-image.tag)" >> "$GITHUB_ENV"

- name: Prepare fresh Next.js app
- name: Prepare fresh ${{ matrix.framework }} app
id: prepare-app
env:
JOURNEY_APP_URL: http://localhost:3000
JOURNEY_FRAMEWORK: ${{ matrix.framework }}
JOURNEY_REGISTRY_URL: http://127.0.0.1:4873
JOURNEY_WORK_DIR: ${{ runner.temp }}/zitadel-cli-journey
run: node apps/cli-journey-e2e/scripts/prepare-next-app.mjs
JOURNEY_WORK_DIR: ${{ runner.temp }}/zitadel-cli-journey-${{ matrix.framework }}
run: node apps/cli-journey-e2e/scripts/prepare-app.mjs

- name: Start generated Next.js app
- name: Start generated ${{ matrix.framework }} app
run: |
cd "$JOURNEY_APP_DIR"
npm run dev -- --hostname localhost --port 3000 > "$RUNNER_TEMP/next-app.log" 2>&1 &
echo "NEXT_APP_PID=$!" >> "$GITHUB_ENV"
APP_LOG="$RUNNER_TEMP/${{ matrix.framework }}-app.log"
case "${{ matrix.framework }}" in
next)
npm run dev -- --hostname localhost --port 3000 > "$APP_LOG" 2>&1 &
;;
*)
npm run dev -- --host localhost --port 3000 > "$APP_LOG" 2>&1 &
;;
esac
echo "JOURNEY_APP_PID=$!" >> "$GITHUB_ENV"
for _ in $(seq 1 90); do
if curl -fsS http://localhost:3000/login >/dev/null; then
exit 0
fi
sleep 1
done
echo "::error::timed out waiting for generated Next.js app"
cat "$RUNNER_TEMP/next-app.log"
echo "::error::timed out waiting for generated ${{ matrix.framework }} app"
cat "$APP_LOG"
exit 1

- name: Run consumer journey Playwright tests
env:
JOURNEY_FRAMEWORK: ${{ matrix.framework }}
JOURNEY_PLAYWRIGHT_OUTPUT_DIR: apps/cli-journey-e2e/test-output/playwright/${{ matrix.framework }}/output
JOURNEY_PLAYWRIGHT_REPORT_DIR: apps/cli-journey-e2e/test-output/playwright/${{ matrix.framework }}/report
run: >-
corepack pnpm --filter @zitadel/cli-journey-e2e
exec playwright test --config playwright.config.mts

- name: Collect diagnostics
if: failure()
run: |
DIAG_DIR="$RUNNER_TEMP/consumer-journey-diagnostics"
APP_DIR="$RUNNER_TEMP/zitadel-cli-journey/myapp"
DIAG_DIR="$RUNNER_TEMP/consumer-journey-diagnostics-${{ matrix.framework }}"
APP_DIR="$RUNNER_TEMP/zitadel-cli-journey-${{ matrix.framework }}/myapp"
mkdir -p "$DIAG_DIR/generated-app"
if [ -d "$APP_DIR" ]; then
(
Expand All @@ -711,15 +733,15 @@ jobs:
> "$DIAG_DIR/logs.json" 2> "$DIAG_DIR/logs.stderr.log"
) || true
fi
cp "$RUNNER_TEMP/next-app.log" "$DIAG_DIR/" 2>/dev/null || true
cp "$RUNNER_TEMP/${{ matrix.framework }}-app.log" "$DIAG_DIR/" 2>/dev/null || true
cp "$RUNNER_TEMP/verdaccio.log" "$DIAG_DIR/" 2>/dev/null || true
cp "$RUNNER_TEMP/zitadel-cli-journey/doctor.json" "$DIAG_DIR/" 2>/dev/null || true
cp "$RUNNER_TEMP/zitadel-cli-journey/doctor.stderr.log" "$DIAG_DIR/" 2>/dev/null || true
cp "$RUNNER_TEMP/zitadel-cli-journey/start.json" "$DIAG_DIR/" 2>/dev/null || true
cp "$RUNNER_TEMP/zitadel-cli-journey/start.stderr.log" "$DIAG_DIR/" 2>/dev/null || true
cp "$RUNNER_TEMP/zitadel-cli-journey/setup.json" "$DIAG_DIR/" 2>/dev/null || true
cp "$RUNNER_TEMP/zitadel-cli-journey/setup.stderr.log" "$DIAG_DIR/" 2>/dev/null || true
cp "$RUNNER_TEMP/zitadel-cli-journey/metadata.json" "$DIAG_DIR/" 2>/dev/null || true
cp "$RUNNER_TEMP/zitadel-cli-journey-${{ matrix.framework }}/doctor.json" "$DIAG_DIR/" 2>/dev/null || true
cp "$RUNNER_TEMP/zitadel-cli-journey-${{ matrix.framework }}/doctor.stderr.log" "$DIAG_DIR/" 2>/dev/null || true
cp "$RUNNER_TEMP/zitadel-cli-journey-${{ matrix.framework }}/start.json" "$DIAG_DIR/" 2>/dev/null || true
cp "$RUNNER_TEMP/zitadel-cli-journey-${{ matrix.framework }}/start.stderr.log" "$DIAG_DIR/" 2>/dev/null || true
cp "$RUNNER_TEMP/zitadel-cli-journey-${{ matrix.framework }}/setup.json" "$DIAG_DIR/" 2>/dev/null || true
cp "$RUNNER_TEMP/zitadel-cli-journey-${{ matrix.framework }}/setup.stderr.log" "$DIAG_DIR/" 2>/dev/null || true
cp "$RUNNER_TEMP/zitadel-cli-journey-${{ matrix.framework }}/metadata.json" "$DIAG_DIR/" 2>/dev/null || true
cp "$APP_DIR/.zitadel/local/runtime.json" "$DIAG_DIR/runtime.json" 2>/dev/null || true
cp "$APP_DIR/package.json" "$DIAG_DIR/generated-app/" 2>/dev/null || true
cp "$APP_DIR/package-lock.json" "$DIAG_DIR/generated-app/" 2>/dev/null || true
Expand All @@ -728,19 +750,19 @@ jobs:
if: failure()
uses: actions/upload-artifact@v7
with:
name: consumer-journey-diagnostics
name: consumer-journey-diagnostics-${{ matrix.framework }}
path: |
apps/cli-journey-e2e/test-output/playwright/report/**
apps/cli-journey-e2e/test-output/playwright/output/**
${{ runner.temp }}/consumer-journey-diagnostics/**
apps/cli-journey-e2e/test-output/playwright/${{ matrix.framework }}/report/**
apps/cli-journey-e2e/test-output/playwright/${{ matrix.framework }}/output/**
${{ runner.temp }}/consumer-journey-diagnostics-${{ matrix.framework }}/**
if-no-files-found: ignore
retention-days: 7

- name: Tear down consumer journey services
if: always()
run: |
if [ -n "${NEXT_APP_PID:-}" ]; then kill "$NEXT_APP_PID" 2>/dev/null || true; fi
APP_DIR="$RUNNER_TEMP/zitadel-cli-journey/myapp"
if [ -n "${JOURNEY_APP_PID:-}" ]; then kill "$JOURNEY_APP_PID" 2>/dev/null || true; fi
APP_DIR="$RUNNER_TEMP/zitadel-cli-journey-${{ matrix.framework }}/myapp"
if [ -d "$APP_DIR" ]; then
(
cd "$APP_DIR"
Expand Down Expand Up @@ -773,11 +795,17 @@ jobs:
steps:
- name: Check CI gate results
env:
IS_PULL_REQUEST: ${{ github.event_name == 'pull_request' }}
NEEDS_JSON: ${{ toJson(needs) }}
run: |
node <<'NODE'
const needs = JSON.parse(process.env.NEEDS_JSON);
const allowedSkipped = new Set(["changeset-check"]);
if (process.env.IS_PULL_REQUEST === "true") {
allowedSkipped.add("go-smoke-test-embedded-postgres");
allowedSkipped.add("quickstart-smoke");
allowedSkipped.add("node-e2e");
}
const failed = Object.entries(needs).filter(([name, job]) => {
if (job.result === "success") return false;
if (job.result === "skipped" && allowedSkipped.has(name)) return false;
Expand Down
35 changes: 19 additions & 16 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ Secrets").
- `apps/demo-next-e2e/` and `apps/demo-nuxt-e2e/` are the Playwright projects
that exercise each demo through real framework middleware against the
api-mock TCP server.
- `apps/cli-journey-e2e/` contains the fresh Next.js consumer journey
Playwright project. It installs local package tarballs through a temporary
registry and verifies CLI setup plus real registration/login flows.
- `apps/cli-journey-e2e/` contains the fresh consumer journey Playwright
project for every CLI-supported framework. It installs local package tarballs
through a temporary registry and verifies CLI setup plus real
registration/login flows.
- `packages/components/` contains shared Lit components.
- `packages/sdk-core/`, `packages/sdk-next/`, `packages/sdk-nuxt/`,
`packages/sdk-react/`, `packages/sdk-vue/`, and `packages/sdk-angular/`
Expand Down Expand Up @@ -130,9 +131,9 @@ signaled and cleaned up directly.
startup, then runs `go run .`. Direct `go run .` callers must sync the embed
folders themselves or disable both embedded UI surfaces.

End-to-end tests are **opt-in locally** — they're not part of the
default `run-many -t lint,typecheck,build,test` invocation because they
boot real dev servers and need browsers installed:
Checked-in demo end-to-end tests are **opt-in locally** and main-only in CI.
They are not part of the default `run-many -t lint,typecheck,build,test`
invocation because they boot real dev servers and need browsers installed:

```sh
corepack pnpm exec playwright install
Expand All @@ -148,20 +149,22 @@ corepack pnpm run journey
This runner requires Docker for Verdaccio and the CLI-managed local runtime. By
default it builds a local runtime image, ensures the Playwright Chromium
browsers are installed, builds and packs local npm packages with pnpm, creates
an empty app directory outside the repo, runs `npx @zitadel/cli@alpha doctor`,
`start`, and `setup --framework next --server local`, starts the generated app
on `localhost`, and runs Playwright with one worker. Use
`-- --image <docker-tag>` to reuse an existing local runtime image.
empty app directories outside the repo, runs `npx @zitadel/cli@alpha doctor`,
`start`, and `setup --framework <id> --server local`, starts the generated apps
on `localhost`, and runs Playwright with one worker per framework journey. Use
`-- --framework next` to run one framework and `-- --image <docker-tag>` to
reuse an existing local runtime image.

Use `corepack pnpm run journey` for deterministic CI-style proof of the
fresh-app path. Use `corepack pnpm run cli -- ...` for manual browser or agent
experiments against the same local package train.

In CI the dedicated `node-e2e` job (in `.github/workflows/ci.yml`) gates merges
on the checked-in demo integrations. The separate `consumer-journey-e2e` job is
the fresh-app quality gate: it consumes the current workflow's GoReleaser image
and npm package artifacts instead of public Zitadel packages. Browsers are
cached on the runner to reduce install cost.
In CI the `consumer-journey-e2e` matrix is the required PR runtime gate: it
consumes the current workflow's GoReleaser image and npm package artifacts
instead of public Zitadel packages. The checked-in demo integrations
(`node-e2e`), raw binary embedded-postgres smoke, and documented quick-start
compose smoke run on pushes to `main` as release-surface confidence checks.
Browsers are cached on the runner to reduce install cost.

## Testing Layers

Expand Down Expand Up @@ -331,7 +334,7 @@ Standard commands are documented in root `AGENTS.md` → **Local Checks** and

- **Fast local checks:** `corepack pnpm run check`
- **Full local checks:** `corepack pnpm run check -- --full`
- **E2E:** `corepack pnpm nx run-many -t e2e -p @zitadel/demo-next-e2e,@zitadel/demo-nuxt-e2e`
- **Demo E2E (manual/main-only):** `corepack pnpm run check -- --only node:e2e`
- **Consumer journey E2E:** `corepack pnpm run journey`

### Running demo apps manually
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,10 @@ corepack pnpm run journey
This opt-in check ensures the Playwright Chromium browsers are installed, builds
the local npm packages, publishes them to a temporary Verdaccio registry, runs
`npx @zitadel/cli@alpha doctor`, `start`, and
`setup --framework next --server local` in an empty app directory, starts the
generated app, and verifies registration/login journeys.
`setup --framework <id> --server local` in fresh app directories for every
supported framework, starts the generated apps, and verifies registration/login
journeys. Use `corepack pnpm run journey -- --framework next` to run only the
Next.js journey.

Use `corepack pnpm run journey` for deterministic CI-style proof. Use
`corepack pnpm run cli -- ...` when you want to drive the same local package
Expand Down
12 changes: 6 additions & 6 deletions apps/cli-journey-e2e/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ These instructions apply to `apps/cli-journey-e2e/**`. Defer to the root

## Purpose

This project protects the customer local setup journey. Tests must exercise a
fresh app directory that runs the CLI local runtime path (`doctor`, `start`,
`setup --server local`) before starting the generated Next.js app. It must not
test the checked-in demo apps.
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.

## Maintenance Rules

Expand All @@ -20,8 +20,8 @@ test the checked-in demo apps.
- CI must install Zitadel packages from current workflow tarballs through the
temporary Verdaccio registry, not from public npm.
- CI must run `npx @zitadel/cli@alpha doctor`, `start`, and
`setup --framework next --server local` from the fresh app directory with
`--non-interactive --json`.
`setup --framework <next|nuxt|react|vue|angular> --server local` from the
fresh app directory with `--non-interactive --json`.
- Pack and upload only the public packages:
`@zitadel/cli`, `@zitadel/api`, `@zitadel/components`,
`@zitadel/sdk-core`, `@zitadel/sdk-next`, `@zitadel/sdk-nuxt`,
Expand Down
Loading
Loading