feat(e2e): /e2e-bootstrapped readiness endpoint + skip-when-unseeded gate (BUNYIP-163) #203

Merged
nrupard merged 1 commit from feat/BUNYIP-163-e2e-bootstrapped-gate into main 2026-06-23 19:00:23 +02:00
Owner

What

Adds a GET /e2e-bootstrapped readiness endpoint to bunyip-api and an e2e.yml gate that skips the suite (job still succeeds) when staging is not seeded - so e2e can be a REQUIRED check on main without a merge lockout (BUNYIP-163, BUNYIP-148 sub-task).

Why

The suite logs in as the seeded accounts (e2e-user@a8n.run / e2e-admin@a8n.run). If staging is not bootstrapped (fresh DB, just e2e-bootstrap --cleanup, a reset), every run fails at login. Making e2e required would then block every PR - including the one that fixes bootstrapping. Same shape as the known deploy-gate deadlock.

How

  • bunyip-api: GET /e2e-bootstrapped at the deployment root (alongside /health, /version) -> { "bootstrapped": bool }, true iff both seeded accounts exist with deleted_at IS NULL, via one indexed COUNT(*) ... WHERE email = ANY($1) (UserRepository::count_active_by_emails). Unauthenticated + information-minimal. Returns false on a production ENVIRONMENT without querying, so it never probes prod user rows.
  • Single source of truth: the two emails are lifted to bunyip_api::E2E_ACCOUNT_EMAILS, used by both the bootstrap binary and the endpoint - the probe can't drift from what's seeded.
  • e2e: scripts/check-bootstrapped.mjs probes the endpoint, writes bootstrapped to $GITHUB_OUTPUT, and fail-opens (any error -> false, exit 0). e2e.yml runs it after the reachability gate and gates the suite on bootstrapped == 'true' (OR a production dispatch, which is reachability-only and always reports false). When unseeded: suite skipped with a clear log line, job succeeds.
  • docs: dev-docs/e2e.md documents the endpoint, the skip, and that e2e can now be required.

Manual follow-up (yours, out of code scope)

The final AC - add e2e to main's required status checks - is a Forgejo branch-protection setting, not code. Flip it once this lands; the skip-and-pass behavior makes it safe.

Verification

cargo clippy --workspace --all-targets -D warnings + fmt --all --check clean; node --check + YAML parse clean.

🤖 Generated with Claude Code

## What Adds a `GET /e2e-bootstrapped` readiness endpoint to bunyip-api and an e2e.yml gate that **skips the suite (job still succeeds) when staging is not seeded** - so `e2e` can be a REQUIRED check on `main` without a merge lockout (BUNYIP-163, BUNYIP-148 sub-task). ## Why The suite logs in as the seeded accounts (`e2e-user@a8n.run` / `e2e-admin@a8n.run`). If staging is not bootstrapped (fresh DB, `just e2e-bootstrap --cleanup`, a reset), every run fails at login. Making `e2e` required would then block **every** PR - including the one that fixes bootstrapping. Same shape as the known deploy-gate deadlock. ## How - **bunyip-api**: `GET /e2e-bootstrapped` at the deployment root (alongside `/health`, `/version`) -> `{ "bootstrapped": bool }`, true iff both seeded accounts exist with `deleted_at IS NULL`, via one indexed `COUNT(*) ... WHERE email = ANY($1)` (`UserRepository::count_active_by_emails`). Unauthenticated + information-minimal. Returns `false` on a production `ENVIRONMENT` **without** querying, so it never probes prod user rows. - **Single source of truth**: the two emails are lifted to `bunyip_api::E2E_ACCOUNT_EMAILS`, used by both the bootstrap binary and the endpoint - the probe can't drift from what's seeded. - **e2e**: `scripts/check-bootstrapped.mjs` probes the endpoint, writes `bootstrapped` to `$GITHUB_OUTPUT`, and fail-opens (any error -> `false`, exit 0). e2e.yml runs it after the reachability gate and gates the suite on `bootstrapped == 'true'` (OR a production dispatch, which is reachability-only and always reports false). When unseeded: suite skipped with a clear log line, **job succeeds**. - **docs**: `dev-docs/e2e.md` documents the endpoint, the skip, and that `e2e` can now be required. ## Manual follow-up (yours, out of code scope) The final AC - *add `e2e` to `main`'s required status checks* - is a Forgejo branch-protection setting, not code. Flip it once this lands; the skip-and-pass behavior makes it safe. ## Verification `cargo clippy --workspace --all-targets -D warnings` + `fmt --all --check` clean; `node --check` + YAML parse clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(e2e): /e2e-bootstrapped readiness endpoint + skip-when-unseeded gate
All checks were successful
E2E / Playwright against deployment (pull_request) Successful in 16s
Check / fmt + clippy + build + tests (pull_request) Successful in 2m3s
Create release / Create release from merged PR (pull_request) Has been skipped
b1475e1bf1
Lets `e2e` become a REQUIRED check on main without a merge lockout: today the suite logs in as the seeded accounts (e2e-user@a8n.run / e2e-admin@a8n.run), and if staging is not bootstrapped (fresh DB, `just e2e-bootstrap --cleanup`, a reset) every run fails at login - so a required suite would block every PR, including the one that fixes bootstrapping (the known deploy-gate deadlock shape).

bunyip-api: new unauthenticated `GET /e2e-bootstrapped` at the deployment root (alongside /health, /version) returning `{ "bootstrapped": bool }` - true iff both seeded accounts exist with `deleted_at IS NULL`, via one indexed `COUNT(*) ... WHERE email = ANY($1)` (UserRepository::count_active_by_emails). Returns false on a production ENVIRONMENT WITHOUT querying, so it never probes prod user rows. The two emails are lifted to a shared `bunyip_api::E2E_ACCOUNT_EMAILS` const that both the bootstrap binary and the endpoint use, so the probe can never drift from what gets seeded.

e2e: scripts/check-bootstrapped.mjs probes the endpoint, writes `bootstrapped` to $GITHUB_OUTPUT, and fail-opens (any error -> false, exit 0). e2e.yml runs it after the reachability gate and gates the suite on `bootstrapped == 'true'` (OR a production dispatch, which is reachability-only and always reports false); when unseeded the suite is skipped with a clear log line and the JOB STILL SUCCEEDS. dev-docs/e2e.md documents the endpoint, the skip, and that `e2e` can now be required.

cargo clippy --workspace --all-targets -D warnings + fmt --check clean; node --check + YAML parse clean.

#BUNYIP-163

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
nrupard deleted branch feat/BUNYIP-163-e2e-bootstrapped-gate 2026-06-23 19:00:23 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/bunyip!203
No description provided.