test(cli-e2e): add live e2e suite covering the CLI command matrix#5588
Conversation
Record the decision that the live e2e mode bypasses the replay server (direct harness wiring to the real Management API + Docker socket), asserts on deployed-function HTTP responses, and runs as a per-target CI matrix (go + ts-legacy). Index updated in docs/adr/README.md. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implements the ADR-0013 live mode: a harness-wiring mode that bypasses the replay server and points the CLI at the real Management API + Docker socket, asserting on deployed-function HTTP responses. - env.ts: CLI_E2E_MODE/isLive/TARGET_API_URL/PROJECT_HOST resolution - tests/staging-project.ts: shared project lifecycle helpers (extracted from setup.ts; createTestProject now takes a name) - tests/live-setup.ts: provisions one ephemeral project per run, resolves the publishable key + functions URL, deletes on teardown - src/tests/live/: testLive context (direct-wired run + HTTP invoke) and the 3-cell functions deploy pilot (default / --use-api / --use-docker) - vitest.live.config.ts + test:e2e:live; default config excludes live tests - .github/workflows/live-e2e.yml: go + ts-legacy matrix, docker preflight, 3x retry, scoped project cleanup. pull_request bootstrap trigger so it runs on this PR; switch to workflow_dispatch + schedule after merge - fixtures/live/functions-project: migrated deploy-e2e-* function fixtures Validated locally against staging (go target): 3/3 pass, project created and deleted, no leak. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Supabase CLI previewnpx --yes https://pkg.pr.new/supabase/cli/supabase@0ac6ef94ce378308253b43f1ed9d5db24f8cd090Preview package for commit |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a0fe8db94a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- Per-mode slugs: each bundler path (default/--use-api/--use-docker) deploys and invokes its own function slug, so the invoke proves that mode's deploy produced a running function rather than serving an earlier mode's upload. - live-setup: delete the project if waitForProjectReady/getPublishableKey throws after creation, so a failed setup never leaks a staging project locally (CI's always() sweep already covered CI). - live-e2e.yml: skip fork PRs (head repo != base repo) since repository secrets are unavailable to them and the live job would call staging with an empty token. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
First non-pilot command in the per-command live matrix. Outcome-based, Management-API-only (no Docker/DB): set a secret, assert it appears in `secrets list`, unset it, assert it is gone. Self-cleaning on the fresh per-run project. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 95b3094a8e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Black-box subprocess tests (legacy entrypoint) for the bundler-flag negatives: the three mutually-exclusive combinations of --use-api/--use-docker/--legacy-bundle, --jobs without --use-api, and the no-link error. This validation lives in the Go CLI today (the legacy command proxies to it); a subprocess e2e keeps the contract pinned through the eventual native TS port. A valid-format token + fake ref clear the auth and project-ref gates so each case reaches the validation under test; all fail before any network call. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- invoke key: prefer the legacy anon JWT over the publishable key. Edge Functions default to verify_jwt=true and a publishable (sb_publishable_) key is not a JWT, so it fails the platform JWT check on a verified function; fall back to publishable only when no anon JWT is issued. - live-e2e.yml: add `set -o pipefail` to the always() cleanup step so a failed project-list surfaces instead of silently skipping the backstop deletion. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 328b88689d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
GitHub runs the step as `bash -e`, so a non-zero `pnpm test:e2e:live` aborted the step before the retry bookkeeping and attempts 2-3 never ran. Use `if pnpm ...; then` (errexit-exempt) so failing attempts exercise the sweep + retry path; make the inter-attempt sweep best-effort. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b2e382445c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Adds a dbUrl fixture (direct postgres connection db.<ref>.<host>:5432, built from the creation password + project host — bypasses the harness profile's localhost project_host) and live tests for the DB-backed commands: - inspect db db-stats, migration list, db dump (read-only, --db-url) - gen types --db-url (introspects via the postgres-meta container; needs Docker) All assert real outcomes against the fresh project's Postgres. Validated against staging (go): full live matrix 8/8. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- live-e2e.yml: also skip Dependabot pull_request runs (github.actor != dependabot[bot]) — GitHub treats them like forks and withholds secrets, so the live job would run with an empty staging token. - cleanup step: capture the project list in a var and fail the step if any DELETE fails, instead of masking it with `|| true` (a masked failure would report success while leaking a real staging project). Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 895abc97f2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The direct --db-url host (db.<ref>.supabase.red) is IPv6-only, but CI runners are IPv4-only, so these connections fail with "no route to host"; a direct --db-url has no pooler fallback to recover. They pass locally (IPv6 available) and are kept as skipped with the intended shape — re-enabling in CI needs the IPv4 session-mode pooler (follow-up). Drop db dump (needs the session pooler specifically) and gen types (also needs a Docker image pull) entirely. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- staging-project: randomise the throwaway project's db password per run (overridable via CLI_E2E_DB_PASSWORD) so no static credential is committed. - env: derive isRecording from MODE and normalise RECORD from it, so CLI_E2E_MODE=record actually records instead of silently replaying. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mark the inspect db / migration list live tests describe.skip: the direct --db-url host (db.<ref>.supabase.red) is IPv6-only but CI runners are IPv4-only, so they fail with "no route to host" and a direct --db-url has no pooler fallback. Kept as the ready shape; re-enabling in CI needs the IPv4 session pooler (follow-up). (Pairs with the prior gen types / db dump removal.) Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- deploy-all: with no slug, assert the CLI deploys every function declared under supabase/functions (each appears in the deploy output) and smoke-invoke one. - update: re-deploy a slug with changed code and assert the new body is served (there is no dedicated `functions update` — deploy upserts). - delete: deploy a slug, confirm it is listed, delete it, confirm it is gone. Validated against staging (go): 7 passed, 2 skipped. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Invoke each declared function after the no-slug deploy and assert HTTP 200, instead of smoke-invoking only one. Bodies vary per fixture, so the loop checks status (the per-mode tests still assert exact bodies). Validated against staging (go): all declared functions respond 200. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diagnostic-only: characterize host + Docker IPv6 egress on the Blacksmith runner to decide whether live DB tests can connect directly (IPv6-only host) or must use the IPv4 pooler. To be removed once answered. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Probe verdict: the Blacksmith runner has no public IPv6 egress (its only v6 address is RFC 3849 documentation space, 2001:db8::1; curl -6 fails host-side and in containers under every Docker IPv6 config). So the IPv6-only direct DB host is unreachable from CI and Docker config cannot fix it — the IPv4 transaction pooler is the required path for live DB tests. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1f8d6408f5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
A free-form `ref` input let a manual run check out arbitrary (e.g. external PR) code while SUPABASE_ACCESS_TOKEN is in the job env, bypassing the fork/Dependabot guard. Dispatched runs now use the selected same-repo branch (github.ref) only. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- delete test: filter out REMOVED-status rows before asserting absence (the Management API can keep deleted functions listed as REMOVED). - env: normalise SUPABASE_STAGING_URL from CLI_E2E_API_URL in record mode so CLI_E2E_MODE=record CLI_E2E_API_URL=… works without the legacy var. - live teardown: deleteTestProject gains throwOnError; live-setup uses it so a leaked staging project fails the run loudly (record setup stays lenient). Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The direct DB host is IPv6-only and unreachable from IPv4-only CI runners (confirmed: the Blacksmith runner has no IPv6 egress, Docker can't fix it). Resolve the project's Supavisor pooler from the Management API and build a session-mode (port 5432) --db-url — IPv4, and session mode supports pg_dump. Un-skips inspect db db-stats + migration list and re-adds db dump. Validated against staging (go): 3/3. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- link --skip-pooler (Management-API only): asserts the project links and that a ref-less command then resolves the ref from the written linked-project cache (the backbone of workflows 1-3). - projects list shows the fresh project; projects api-keys returns its anon key (read paths; create/delete are already exercised by live-setup). Validated against staging (go): 2/2. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A stale RECORD=true shell var combined with CLI_E2E_MODE=replay previously left RECORD set, so the replay server (which reads RECORD directly) would record and wipe fixtures while setup believed it was replaying. Clear RECORD when MODE is not record, so an explicit CLI_E2E_MODE always wins. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- db push/pull round-trip over the session pooler (workflows 1-2): push a local migration, confirm it in `migration list`, then pull the remote schema back. Done in one workspace so local history matches remote (a fresh-workspace pull would mismatch on the shared per-run project). Uses --yes (the prompt aborts in the non-TTY harness otherwise). - config push (workflows 1-3) with --yes. Validated against staging (go): 2/2. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
config push passes on the go target but fails on ts-legacy with "failed to read Storage config: SchemaError(Missing key …)" — the ts-legacy CLI validates config.toml against a stricter storage schema than the Go path. Not worth fighting on the shared fixture here; defer to a focused follow-up. db push/pull (which passed on both targets) stay. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: adf0a3c1cc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- db pull: accept either a diff (exit 0) or "No schema changes found" — both prove connectivity; the exit code is diff-dependent and shouldn't fail the test when the schemas already match. - projects api-keys: accept a legacy anon JWT OR a new-style publishable key, so projects that only issue new keys don't fail the read-path test. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 636b376b81
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- gen types --db-url over the session pooler (workflow 3): introspects the remote schema and emits TypeScript types (pulls the postgres-meta image). - branches create/list/delete (workflow 3): full round-trip on a paid org, or a clean plan-gate assertion on a free one (no leaked branch either way). Validated against staging (go): 2/2. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
getPoolerSessionUrl now reuses the Management API's connection_string verbatim (preserving tenant-routing query params like options=reference=... that a field-reconstructed URL would drop) and only swaps in our password + the session port (5432). Also selects the PRIMARY pooler config instead of index 0, so a replica row can't route db push to a read-only database. Mirrors the Go connector. Validated against staging (go): 4/4. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 829c356ab3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…nches - storage cp/ls/rm (workflow-adjacent): createHarness gains a projectHost option so live mode writes the real project_host (storage --linked derives <ref>.<host>, IPv4-reachable, instead of localhost). live-setup seeds a private bucket via the Storage API; storage commands run with --experimental. - getAnonKey: fail loudly if a project returns no anon JWT instead of falling back to a publishable key, which would 401 on the default verify_jwt=true functions (addresses review). - branches: unique per-attempt name + finally cleanup so vitest retries can't collide on a leftover branch (addresses review). Validated against staging (go): full live matrix 16/16. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… pooler storage --linked opens a DB connection to resolve storage config; the direct host is IPv6-only and unreachable from IPv4-only CI runners. Run `link` first (with the db password) so the IPv4 pooler connection is persisted and reused by storage — the CLI's own remedy for the IPv6 case. Validated in CI (this can't be reproduced locally, where IPv6 is available). Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d58e65adc8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
storage rm prompts PromptYesNo(default=No); without --yes the non-TTY harness takes the default and exits 0 without deleting, so the test passed without removing the object. Add --yes and assert the follow-up ls no longer lists it. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Instead of copying a static config.toml into every live workspace, run `supabase init` so the golden paths exercise a freshly-generated config. The functions deploy tests call seedFunctions() to layer the deploy-e2e-* fixtures + their [functions.*] config (import map, custom entrypoint, static files, no-jwt) onto the init'd config. Other tests run against the bare generated config. Removes the static fixtures/live/functions-project/config.toml; adds fixtures/live/functions-config.toml (the [functions.*] snippet). Validated against staging (go): full live matrix 16/16. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…8966d53) Completes the previous commit, which only captured the config.toml deletion: init-based workspace fixture, seedFunctions helper, functions-config.toml snippet, and the AGENTS.md update. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the bootstrap pull_request trigger with workflow_dispatch (manual; the Actions branch picker selects the ref, no free-form ref input) + an hourly schedule. The scheduled run exercises the @beta channel: develop is the default branch and the beta release source, so it builds develop from source and runs the same [go, ts-legacy] matrix. A gate job skips the scheduled run unless the published supabase@beta version changed since the last green run (actions/cache marker keyed on the version, written by a finalize job only after BOTH matrix legs pass), so a staging project is spent only when there is a new beta to test. Manual dispatch always runs and never writes the marker. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Security: - scope SUPABASE_ACCESS_TOKEN to the run + cleanup steps (off the job env) - route the ephemeral DB password through provide()/inject() instead of a shared TEST_DB_PASSWORD export (generateDbPassword + createTestProject(password)) - validate the npm @beta version (semver) before using it as a cache key Bugs: - waitForProjectReady fast-fails on terminal statuses (INIT_FAILED/RESTORE_FAILED/ REMOVED) and the API pollers drain the response body on non-success branches - branches test guards the finally delete (only when the in-try delete didn't run) - db-sync pull now positively asserts pooler connectivity (no connection-error) DX: - live mode fails fast with a clear message when no staging token is set - self-contained AGENTS.md live run snippet (build go binary + SUPABASE_GO_BINARY) - ship apps/cli-e2e/.env.example (matches the .gitignore !.env.example un-ignore) Quality: - ADR-0013 status -> accepted - deploy-all asserts {case, ok} per fixture (proves each feature ran, not just booted) - centralize ProvidedContext (one augmentation) and drop redundant inject() casts - extract the project-sweep curl into .github/scripts/sweep-live-projects.sh - document gate/finalize chronic-failure behavior Verified: live suite 16/16 green (go) against staging; token-guard path; check:all. Refs: CLI-1630 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
What
Adds a live e2e mode to
apps/cli-e2eand a real-staging command matrix on top of it, per CLI-1630 and ADR-0013.Live mode is a third mode (
CLI_E2E_MODE=live) that, unlike replay/record, does not use the replay server. The harness points the CLI straight at the real Management API (CLI_E2E_API_URL) and the real Docker socket; tests assert on real outcomes — process exit codes, the HTTP responses of deployed functions (status + JSON body), and real DB/Storage state. This is ID-agnostic, so there are no snapshots/normalization by default.Changes
env.ts—CLI_E2E_MODE(replay/record/live),isLive,TARGET_API_URL,CLI_E2E_PROJECT_HOST; back-compatRECORD=true→record.tests/staging-project.ts— project-lifecycle helpers extracted fromsetup.ts: create/delete an ephemeral project, resolve the anon JWT, the IPv4 session-poolerdbUrl, the service-role key, and seed a Storage bucket. Record behavior is unchanged.tests/live-setup.ts— global setup that provisions one ephemeral project per run (cli-e2e-live-{target}-{runId}-{short}), waitsACTIVE_HEALTHY, and exposesprojectRef/anonKey/functionsUrl/dbUrl/storageBucketviainject(); deletes the project on teardown (even on failure). Intentionally dumb — no in-setup retry.src/tests/live/—testLivecontext (direct-wiredrun, HTTPinvokesending the anon JWT, asupabase init-generatedworkspace,seedFunctionsto layer thedeploy-e2e-*fixtures + their[functions.*]config) plus live coverage for: functions deploy (the three bundler modes + deploy-all), functions lifecycle (re-deploy + delete), database (inspect/migration list/db dump), db push→pull,link,projects,gen types,branches,storage,secrets.vitest.live.config.ts+test:e2e:live; the default config excludes*.live.e2e.test.ts.harness.ts—projectHostoption so host-derived commands (storage --linked→<ref>.<host>,db.<ref>.<host>) reach the real endpoint instead oflocalhost..github/workflows/live-e2e.yml—workflow_dispatch+ an hourly@betaschedule;go+ts-legacymatrix (fail-fast: false);docker infopreflight; 3× retry; project cleanup scoped to the job's own prefix.apps/cli/.../functions/deploy/deploy.e2e.test.ts— collocated integration coverage for the negative/arg-validation cases that don't belong in the live suite.docs/adr/0013-…+ README index row;fixtures/live/functions-project/deploy-e2e-*functions.Reviewer notes
db.<ref>.supabase.red) is IPv6-only by design, and the CI runners have no IPv6 egress — so DB-touching commands connect through the project's IPv4 session-mode Supavisor pooler via--db-url(the CLI's own blessed fallback). Session mode (not transaction6543) is required forpg_dump.go(source of truth for the port);ts-legacyruns the same tests to prove the shim matches. Both run as separate CI jobs (independent green/red signals).pull_requesttrigger — run the workflow manually on a branch for pre-merge coverage.workflow_dispatch(Actions branch picker; no free-formrefinput, so the staging token never reaches arbitrary code) andscheduleonly become active once this file is on the default branch (develop) — classic GitHub bootstrap. The hourly run exercises the@betachannel:developis the default branch and the beta release source, so it buildsdevelopfrom source and runs the same matrix. Agatejob skips the run unless the publishedsupabase@betaversion changed since the last green run (anactions/cachemarker keyed on the version, written byfinalizeonly after both legs pass).SUPABASE_E2E_CLI_LIVE_STAGING_ACCESS_TOKEN; neverpull_request_target, so the token is never exposed to fork code.config pushsurfaced a TS↔remote config-schema parity bug onts-legacy, tracked separately in CLI-1810; it is intentionally not covered here.Refs: CLI-1630
🤖 Generated with Claude Code