Skip to content

fix(cli): validate config before the destructive db reset --local recreate#5840

Merged
Coly010 merged 4 commits into
developfrom
columferry/cli-1877-reject-directlocal-db-push-db-reset-without-project-config
Jul 9, 2026
Merged

fix(cli): validate config before the destructive db reset --local recreate#5840
Coly010 merged 4 commits into
developfrom
columferry/cli-1877-reject-directlocal-db-push-db-reset-without-project-config

Conversation

@Coly010

@Coly010 Coly010 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

What changed

supabase db reset --local now runs full Go-parity config validation (legacyCheckDbToml, matching Go's flags.LoadConfig) at the top of its local branch, before AssertSupabaseDbIsRunning/the destructive container recreate — the same pre-destructive-work gate db start and db push already have. A broken supabase/config.toml (unterminated TOML, an undecryptable encrypted: vault secret, an unparseable env(VAR) boolean, an explicit empty project_id) now aborts before the local database is ever recreated, instead of only surfacing later (or never) during bucket seeding.

A genuinely missing config.toml is still tolerated, unchanged: Go's Config.Load defaults project_id to the current directory's basename when no config file exists, so Validate never rejects it — this is exactly the mechanism the cli-e2e parity suite relies on when it runs db push --local / db reset --local from a project with no config.toml. Only a config file that is present but broken is now caught earlier.

db reset --local's post-recreate bucket-seeding step catches a LegacySeedConfigLoadError from its own config reload and warns-and-continues rather than failing the command. An earlier version of this PR removed that fallback (reasoning: Go loads config.toml exactly once into memory, so it can never reach "recreate succeeded, then a later reload of the same file fails"). A Codex review on this PR caught that this doesn't transfer to the TS port: the new pre-recreate gate resolves env(VAR) via the Go-parity nested-env reader (sees supabase/.env.development, the project root, etc.), but the post-recreate reload goes through @supabase/config's narrower loader (supabase/.env/.env.local only) — so a genuinely Go-valid config can pass the gate and the real recreate, then hard-fail only at the later, narrower reload, after the local database has already been dropped and rebuilt. The fallback is restored, with a comment naming the actual env-file-set gap instead of generic "loader strictness."

Why

Linear CLI-1877: a Codex P1 finding on #5715 flagged that the native db reset/db push/db start port tolerates a missing project config more broadly than Go, and that db reset --local's config validation happened too late relative to the destructive recreate.

Investigation (via go-parity-auditor, cross-checked against the compiled Go binary) found the issue's literal premise — "reject direct/local db push + db reset without project config" — does not match current Go behavior: Go tolerates a missing config file by defaulting project_id to the cwd basename, and the TS port already matches that. Implementing a hard reject-on-missing-config would have been a new divergence from Go and would have broken the currently-green cli-e2e parity tests (exactly the regression risk the issue's own "deferred from #5715" note called out). db start's and db push's validation-ordering guarantees were also found to already be correct and already covered by existing tests. The one real, narrow gap was db reset --local's ordering guarantee being implicit — buried inside a config resolver whose test double bypasses it entirely — rather than explicit and independently testable. This PR closes that gap.

Test plan

  • Added regression tests in reset.integration.test.ts for a local reset: malformed config.toml, an unparseable boolean, an undecryptable vault secret, and an explicit empty project_id, all asserting the destructive recreate never runs; a test pinning that a broken config wins over the "not running" error; and a test confirming a genuinely missing config.toml is still tolerated.
  • pnpm check:all and the full apps/cli unit + integration suite (4758 tests) pass.
  • Ran the 4-agent review-changes procedure (architect/engineer/security/DX) against the diff and worked every finding; see "Judgement calls" below for what a review-adjudicator settled and what remains a documented, deliberate trade-off.

Judgement calls / open notes

  • The review-changes engineer pass flagged that rewriting a pre-existing test orphaned the bucket-seed warn-and-skip branch, regressing coverage. A review-adjudicator pass at the time concluded there was no Go-parity reason to keep that fallback and recommended deleting it (done in the second commit) — a subsequent Codex review on the open PR found a concrete Go-valid config shape (an env(VAR) value sourced from supabase/.env.development) that the deletion broke, so the fallback was restored with an accurate comment (see "What changed" above).
  • The new pre-recreate gate is currently unreachable in production (the resolver's own internal read already validates first and would already reject a broken config identically) — it exists for defense-in-depth and so the "validate before destroy" guarantee is enforced directly by the handler and stays covered by a test even if the resolver is ever mocked or refactored to stop validating. Both the architect and DX review passes flagged this as worth calling out explicitly rather than as a blocking issue.
  • Not addressed here (flagged by review but out of scope): LegacyDbConfigResolver.resolve could return the validated config it already reads, so callers stop re-parsing config.toml up to 2-3 times on the local reset path. This is a pre-existing, codebase-wide pattern (db push does the same double-read), not something introduced by this PR, and is better done as its own resolver-contract refactor.

Fixes CLI-1877.

Coly010 added 3 commits July 9, 2026 11:48
…reate

Go's flags.LoadConfig (the local target's per-connType LoadConfig,
internal/utils/flags/db_url.go:77-80) runs full config validation before
reset.Run reaches AssertSupabaseDbIsRunning/resetDatabase
(internal/db/reset/reset.go:57-61). The native local db reset path already
gets this for free from the resolver's own internal read, but that guarantee
was invisible to (and unprotected by) the handler-level test suite, which
mocks LegacyDbConfigResolver — so a regression here would go undetected.

Add an explicit, independent legacyCheckDbToml gate to the local branch of
reset.handler.ts, mirroring the pattern db start and db push already use for
their own pre-destructive-work checks, and add regression tests covering a
malformed config.toml, an unparseable boolean, an undecryptable vault secret,
and an explicit empty project_id, all asserting the local database is never
recreated. Also add a test documenting that a genuinely missing config.toml
is tolerated (Go defaults project_id to the cwd basename), matching the
cli-e2e parity suite's config-less db reset --local runs.

Fixes CLI-1877.
… path

Follow-up to the previous commit: the local reset path already reads
config.toml unconditionally before any destructive work now, not just on
the remote path / during bucket seeding.
…b reset

review-changes findings from CLI-1877:

- Removed the LegacySeedConfigLoadError catchTag around the post-recreate
  bucket-seed call. Go loads config.toml exactly once into memory, so it can
  never reach "recreate succeeded, then a later reload of the same file
  fails" in any form -- there is no Go behavior this warn-and-skip fallback
  was preserving parity with. The identical loader-strictness gap is already
  handled by hard-failing in config push (CLI-1489); this brings db reset in
  line with that precedent and with Go's own buckets.Run contract (any
  failure there fails the whole command). The fallback had also become
  untestable now that the local path is genuinely validated up front,
  regressing branch coverage on a destructive command -- deleting it
  restores the pre-existing coverage baseline instead of leaving dead,
  unverified recovery code on that path.
- Clarified the pre-recreate gate's comment: in production it is currently
  unreachable (the resolver's own local read already validates first and
  would already reject a broken config), so call out plainly that this gate
  exists for defense-in-depth and handler-level test coverage, not because
  it is load-bearing today.
- Added a regression test pinning that a broken config.toml wins over the
  "is not running" error (config validated strictly before
  AssertSupabaseDbIsRunning, matching Go's ordering even when both would
  fail).
- Loosened the undecryptable-secret test's assertion to the stable "failed
  to parse config:" prefix instead of the exact decrypt-failure tail, which
  depends on whether an ambient DOTENV_PRIVATE_KEY* is set.
@Coly010

Coly010 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c0d3fcd775

ℹ️ 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".

Comment thread apps/cli/src/legacy/commands/db/reset/reset.handler.ts
…eset --local (review: codex P1)

The pre-recreate `legacyCheckDbToml` gate resolves env(VAR) via
legacyLoadProjectEnv, which mirrors Go's full nested-env walk
(supabase/.env.development, project-root .env, etc. — pkg/config/config.go:
1220-1257). The post-recreate bucket-seed reload instead goes through
@supabase/config's loadProjectEnvironment, which only ever reads
supabase/.env/.env.local + ambient env. A Go-valid config whose env(VAR) is
backed by one of those wider files now passed the gate and the real
destructive recreate, then hard-failed only at this narrower reload — a
regression from Go, which never re-parses config.toml and would finish such
a config successfully. Restore the warn-and-skip for
LegacySeedConfigLoadError here, with a comment naming the actual env-file-set
gap, and add a regression test sourcing the value from
supabase/.env.development.
@Coly010

Coly010 commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

Reviewed commit: 7cc4df3c3a

ℹ️ 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".

@Coly010 Coly010 self-assigned this Jul 9, 2026
@Coly010 Coly010 marked this pull request as ready for review July 9, 2026 12:18
@Coly010 Coly010 requested a review from a team as a code owner July 9, 2026 12:18
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Supabase CLI preview

npx --yes https://pkg.pr.new/supabase/cli/supabase@7cc4df3c3a533c70ba31659bceda492c6ef2783e

Preview package for commit 7cc4df3.

@Coly010 Coly010 added this pull request to the merge queue Jul 9, 2026
Merged via the queue into develop with commit 7f3a7e1 Jul 9, 2026
36 checks passed
@Coly010 Coly010 deleted the columferry/cli-1877-reject-directlocal-db-push-db-reset-without-project-config branch July 9, 2026 12:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants