Skip to content

feat(cli): port db push, db reset, and db start to native TypeScript - #5715

Merged
avallete merged 56 commits into
developfrom
avallete/exciting-noyce-078f0e
Jul 7, 2026
Merged

feat(cli): port db push, db reset, and db start to native TypeScript#5715
avallete merged 56 commits into
developfrom
avallete/exciting-noyce-078f0e

Conversation

@avallete

@avallete avallete commented Jun 26, 2026

Copy link
Copy Markdown
Member

Ports the db push, db reset, and db start commands of the legacy CLI shell from Go-binary proxies to native TypeScript (CLI-1325), and introduces a hidden Go seam for the container-bootstrap primitives that aren't ported.

What changed

db push — fully native: pending-migration reconciliation, seed-file ops with seed_files hash tracking, [db.vault] upsert, --include-roles/--include-seed/--dry-run, against local / linked / --db-url.

db reset — native on both legs:

  • Remote (--linked / remote --db-url): drop user schemas → vault upsert → migrate + seed, --version/--last, and the Go-parity --sql-paths seed override (merged from develop, mutually exclusive with --no-seed).
  • Local (--local / local --db-url): running check, Resetting local database…, container recreate + migrate + seed via the seam, storage-gated bucket seeding (reuses the ported seed buckets core), and the Finished … on branch <branch>. line.
  • Only the niche --experimental remote schema-files path still delegates to the Go binary.

db start — native: config validation, the AssertSupabaseDbIsRunning check (prints Go's "already running" line), else container bootstrap via the seam. No status table and no cli_stack_started — those belong to the top-level supabase start, not db start.

Hidden Go seam (db __db-bootstrap) — mirrors the existing db __shadow seam. Exposes the un-ported container primitives (StartDatabase + DockerRemoveAll cleanup, the PG14/PG15 reset recreate, the storage health gate) behind --mode {start|recreate|await-storage}. The TS side orchestrates everything else (messages, version resolution, bucket seeding, the git-branch line, telemetry, --output-format shaping); the seam runs with telemetry disabled and stderr inherited, like __shadow.

Config loading (review follow-ups)db push / db reset / db start load config.toml through the legacy Go-parity reader (legacyCheckDbToml, Go's flags.LoadConfigconfig.Load + Validate) rather than @supabase/config, so their config semantics match the Go CLI exactly:

  • Go-style env-reference booleans (e.g. [db.seed] enabled = "env(SEED_ENABLED)") load like Go (env-expand → strconv.ParseBool) instead of failing with a parse error.
  • a matched [remotes.<ref>] block's db.migrations.enabled / db.seed.enabled beats the SUPABASE_DB_*_ENABLED env var (Go's v.Set override tier sits above AutomaticEnv), closing the last remote-vs-env precedence gap.
  • encrypted: [db.vault] secrets are decrypted at config-load time with the shell and project-.env DOTENV_PRIVATE_KEY* keys and fail fast (before any connect / schema drop) on an undecryptable value, matching Go's DecryptSecretHookFunc.
  • seed sql_paths (config and --sql-paths) resolve once to Go's config-load form; db start validates config before the already-running check.

Malformed config now surfaces the reader's Go message (failed to load config), consistent with db diff / dump / pull / migration.

Why

db start / db reset --local need container lifecycle (create/recreate, image pull, health checks, init schema, service restarts) that is impractical to reimplement in TS. The seam keeps that lifecycle in Go while moving orchestration, output parity, telemetry, and --output-format handling to native TS — matching the approach already used for db diff / db pull.

Reviewer notes

  • The seam is the only db reset/db start boundary the in-process integration suites mock; it's covered end-to-end by the cli-e2e live suite (db-reset-start.live.e2e.test.ts, run via pnpm --filter @supabase/cli-e2e test:e2e:live), which exercises the local leg against a real Docker socket and the remote db reset leg against the staging session pooler. Both describes skip the ts-next target (the next shell has no db group).
  • db reset --local recreate forwards --no-seed / --sql-paths to the seam, which applies them via the same applyDbResetSeedFlags helper db reset uses, so seed handling stays identical across local and remote.
  • The bundled supabase-go binary must be rebuilt (pnpm build:go-sidecar copies it into dist/) since the seam adds the db __db-bootstrap command.
  • docs/go-cli-porting-status.md flips db push / db reset / db start to ported; each command's SIDE_EFFECTS.md documents the files, subprocesses, DB mutations, env vars, and exit codes.

🤖 Generated with Claude Code

claude and others added 18 commits June 24, 2026 19:10
Pure 1:1 port of Go's FindPendingMigrations + GetPendingMigrations
suggestion strings (internal/migration/up, pkg/migration/apply), the
foundation for the native legacy `db push` handler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nv8pAJ695qfNs5Btbv5v7h
Port Go's GetPendingSeeds/SeedData with a faithful fs.Glob/path.Match
matcher, sha256 dirty detection against supabase_migrations.seed_files,
and transactional seed application (pkg/migration/seed.go, file.go).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nv8pAJ695qfNs5Btbv5v7h
Port vault.UpsertVaultSecrets (literal/env-skip parity) and the
ApplyMigrations / SeedGlobals stderr-emitting loops, extracting a shared
transactional batch core from legacyApplyMigrationFile.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nv8pAJ695qfNs5Btbv5v7h
Replace the Go-proxy shim with a native Effect handler porting
internal/db/push/push.go: target mutex, config-driven skip messages,
pending-migration/seed/roles collection, dry-run, confirm prompts,
vault upsert + migration/seed/globals apply, and three output modes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nv8pAJ695qfNs5Btbv5v7h
Cover up-to-date, apply+confirm, decline/cancel, dry-run, missing-local
and missing-remote classification, --include-all, config-disabled skips,
seed apply/up-to-date/disabled, and custom roles.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nv8pAJ695qfNs5Btbv5v7h
Add vault upsert (update+create), seed dirty/no-table/glob-warning,
linked-path, no-target-flag default, apply-error, parse-error, and
remotes-override cases. Relocate vault document parsing into the vault
module with unit coverage. The lone uncovered branch is Go's unreachable
afero.Exists error wrap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nv8pAJ695qfNs5Btbv5v7h
Rewrite push/SIDE_EFFECTS.md for the native implementation and flip
db push to `ported` in go-cli-porting-status.md. De-export internal-only
helpers flagged by knip.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nv8pAJ695qfNs5Btbv5v7h
Port internal/db/reset/reset.go's remote path: --version/--last
validation, drop user schemas (embedded drop.sql), vault upsert, and
MigrateAndSeed (partial migrations + seed). The local and --experimental
paths delegate to the Go binary (telemetry-disabled) as the documented
interim until the container-bootstrap seam lands. 19 integration tests,
handler at 98.7% branch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nv8pAJ695qfNs5Btbv5v7h
Rewrite reset/SIDE_EFFECTS.md for the native remote path and the
local/experimental Go delegation; mark db reset `partial` in
go-cli-porting-status.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nv8pAJ695qfNs5Btbv5v7h
Self-contained handoff covering the container-bootstrap work remaining
(hidden __db-bootstrap Go seam + native orchestration), exact Go behavior
to match, reusable helpers, build/test commands, and parity gotchas.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nv8pAJ695qfNs5Btbv5v7h
Port `supabase db start` to a native TypeScript handler. The handler validates
config, runs Go's `AssertSupabaseDbIsRunning` check (printing the "already
running" line), and otherwise delegates the container bootstrap to a new hidden
Go `db __db-bootstrap` seam that mirrors `db __shadow`: it exposes the
un-ported container primitives (StartDatabase + DockerRemoveAll cleanup, the
PG14/PG15 reset recreate, and the storage health gate) behind `--mode`.

No status table and no `cli_stack_started` event — those belong to the
top-level `supabase start`, not `db start`. `--output-format json` emits a
structured `{ status }` result; progress stays on stderr.

The TS seam service/layer (`legacy-db-bootstrap.seam.*`) shells out to the
bundled binary with telemetry disabled and stderr inherited. The recreate /
await-storage modes are landed here for the upcoming native db reset --local.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the Go delegation for local `db reset` with a native handler: assert
the db container is running, print "Resetting local database…", recreate the
container + migrate + seed via the hidden Go `db __db-bootstrap --mode recreate`
seam (forwarding `--version`/`--no-seed`), seed buckets through the storage
health gate, and print "Finished supabase db reset on branch <branch>.".

Extract the seed-buckets local path into `legacySeedBucketsRun` so reset reuses
the exact bucket-seed logic Go invokes via `buckets.Run(ctx, "", false, fsys)`,
with its machine-summary suppressed for the reset caller. Add a `--no-seed` flag
to the recreate seam mode so it disables MigrateAndSeed's seed like `db reset`.

Only the niche `--experimental` remote schema-files path still delegates to the
Go binary. Reset handler integration coverage is 98.7% branch (one unreachable
defensive guard, matching the push/reset precedent).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Real-subprocess golden-path e2e for the native db start / db reset commands,
matching the Docker-free convention of the sibling legacy e2e tests (db diff,
seed buckets): db reset's pre-split flag validations (mutually-exclusive targets,
invalid --version, --version+--last), and db start's config-parse failure that
aborts before the running check. Full container behavior is covered by the
integration suites with the bootstrap seam mocked.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Stage 3 work (native db start, native db reset --local, the hidden Go
db __db-bootstrap seam) is implemented, tested, and documented in the command
SIDE_EFFECTS.md files and go-cli-porting-status.md. The handoff note described
this as remaining/unvalidated work and is now obsolete.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ocal

Covers the one boundary the integration suites mock — the db __db-bootstrap Go
seam driving real Docker. Boots an actual local Postgres container and exercises
db start (fresh), db start (already-running, exit 0), and db reset --local
(recreate + branch line), tearing the stack down in afterAll.

Gated behind SUPABASE_E2E_DOCKER=1 (skipped by default) so it never runs in the
normal feedback loop / default e2e suite. Validated locally against Docker
29.4.0 (3/3 pass, ~60s with images cached).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…noyce-078f0e

# Conflicts:
#	apps/cli-go/cmd/db.go
#	apps/cli/docs/go-cli-porting-status.md
#	apps/cli/src/legacy/commands/db/reset/SIDE_EFFECTS.md
#	apps/cli/src/legacy/commands/db/reset/reset.handler.ts
#	apps/cli/src/legacy/commands/db/reset/reset.integration.test.ts
Adds a live-suite test in apps/cli-e2e that boots a real local Postgres
container via the compiled CLI and drives db start (fresh), db start
(already-running, idempotent), and db reset --local (recreate + branch line),
stopping the stack in finally. Runs through the cli-e2e harness against the
real Docker socket the live setup wires up, exercising the hidden
db __db-bootstrap Go seam end-to-end across the go + ts-legacy targets
(skipped for ts-next, which has no db group).

Inert on replay/PR runs (testLive skips unless CLI_E2E_MODE=live); runs under
pnpm --filter @supabase/cli-e2e test:e2e:live.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the remote (`--db-url` over the staging session pooler) leg of `db reset` to
the live suite: drop user schemas → re-apply a local migration → seed against a
real Postgres, verified via `migration list`. `db start` has no remote leg.

Fold the local-leg coverage (db start / db reset --local, real Docker seam) and
the new remote leg into one `db-reset-start.live.e2e.test.ts`, both gated to
skip the `ts-next` target (no `db` group there).

Remove the `SUPABASE_E2E_DOCKER`-gated apps/cli live test: `*.live` tests belong
in the cli-e2e live suite, which already runs only in Docker-available
environments (gated by `CLI_E2E_MODE=live`); a separate env flag was the wrong
pattern and duplicated this coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@avallete

Copy link
Copy Markdown
Member Author

@codex review

`go generate` (oapi-codegen against the live Management API spec) adds the new
`storage.purge_cache` entitlement and the `Features.PurgeCache` field on the
storage config request/response. Regenerate `pkg/api/types.gen.go` and add the
matching `PurgeCache` field to the hand-built `UpdateStorageConfigBody.Features`
literal in `pkg/config/storage.go` so the package still compiles.

Fixes the Codegen CI check (`go generate` left the committed `pkg` dirty). Pure
spec sync — `PurgeCache` is nil + omitempty, so serialized request bodies are
unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@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: 73252bd3a5

ℹ️ 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/shared/legacy-db-bootstrap.seam.layer.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/shared/legacy-seed-ops.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/reset/reset.handler.ts
Comment thread apps/cli/src/legacy/commands/db/shared/legacy-seed-ops.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/reset/reset.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/reset/reset.command.ts
Comment thread apps/cli/src/legacy/commands/db/reset/reset.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/reset/reset.handler.ts
Comment thread apps/cli/src/legacy/commands/db/reset/reset.handler.ts Outdated
avallete and others added 2 commits June 26, 2026 18:28
- Honor SUPABASE_EXPERIMENTAL (not just --experimental) on db reset via
  legacyResolveExperimental, and forward --experimental into the db __db-bootstrap
  seam so the local recreate's MigrateAndSeed takes Go's experimental schema-file
  path on a versionless reset/start.
- Preserve absolute seed paths in legacyGetPendingSeeds/globOne/legacySeedData
  (Go only prefixes relative patterns with the supabase dir).
- Reject a negative --last (Go declares it UintVar; cobra rejects at parse).
- Create the supabase_migrations schema before seed_files, matching Go's
  CreateSeedTable, so seed-only runs don't fail on a missing schema.
- Keep bucket seeding non-interactive during db reset (Go's
  buckets.Run(ctx, "", false, fsys)) via a new interactive flag threaded through
  legacySeedBucketsRun and legacyPromptYesNo.
- Cache the linked project before the delegated --experimental reset (Go's
  PersistentPostRun runs even though the delegated child has telemetry disabled).
- Record --sql-paths in reset telemetry (flags map + value-consuming set).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@avallete

Copy link
Copy Markdown
Member Author

@codex review again

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

avallete and others added 3 commits June 29, 2026 11:15
`go generate` adds the `api.members.roles` entitlement and the
`DbPoolAcquisitionTimeout` field on the PostgREST config responses. Regenerate
`pkg/api/types.gen.go` to keep the Codegen check clean. Additive named-struct
fields only — no hand-written literals affected, both Go modules build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@avallete
avallete marked this pull request as ready for review June 29, 2026 09:56
@avallete
avallete requested a review from a team as a code owner June 29, 2026 09:56
avallete and others added 2 commits July 6, 2026 16:32
…rom-backup

Addresses a Codex review round on the native db reset/start port:

- Delegated experimental reset now forwards the target from the resolved connType,
  so `db reset --experimental --linked=false` (Cobra Changed → linked) delegates
  with `--linked` instead of letting the Go child fall back to a local reset (P1,
  wrong-database).
- Resolve the experimental gate with the nested project `.env`
  (legacyResolveExperimentalWithProjectEnv), so `SUPABASE_EXPERIMENTAL` in
  supabase/.env selects the experimental path like Go's loadNestedEnv.
- Pass the project-env-resolved `yes` into legacySeedBucketsRun so local reset's
  bucket/vector/analytics pruning honors SUPABASE_YES from supabase/.env.
- Add `db reset` to run.ts selfManagedSignalCommands so Ctrl-C stays blocked on
  the bootstrap child instead of racing the global handler.
- Treat an empty `db start --from-backup ""` as a no-backup start (Go's
  len == 0) rather than joining it to the caller cwd.
- Attach Go's validateDbResetSeedFlags CmdSuggestion to the --no-seed/--sql-paths
  seed-flag conflict errors (renderer already prints Suggestion:).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@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

const passwordRaw = typeof db?.["password"] === "string" ? db["password"] : undefined;

P2 Badge Stop honoring ignored db.password

When config.toml contains [db] password = "secret", Go does not use it for local connections: the db.Password field is tagged json:"-" and config loading uses UnmarshalExact, so this is rejected as an invalid config key rather than changing the local Postgres password. This reader accepts the key and returns it as the local password, so native paths such as db push --local or a passwordless local --db-url can try to authenticate with a password that the Go-started database never used; reject this key like Go or keep the fixed default.


if (legacyIsPipelineIncompatible(statement)) {

P2 Badge Keep migration batching transactional

When a migration contains statements such as CREATE INDEX CONCURRENTLY or VACUUM, this branch flushes the current batch and runs the statement outside the transaction, then records the migration. The Go CLI path used by db push/db reset still queues every statement into one pgconn.Batch and relies on ExecBatch being implicitly transactional (apps/cli-go/pkg/migration/file.go:75-88), so the same migration fails and is not recorded. Until the Go side has the same behavior, native push/reset can apply migrations that the legacy CLI would reject, leaving history diverged across implementations.

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

@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: 7e5c1103bd

ℹ️ 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 Outdated
Comment thread apps/cli/src/legacy/commands/db/reset/reset.handler.ts
Comment thread apps/cli/src/legacy/commands/db/reset/reset.handler.ts Outdated
…ject_id

Addresses two Codex findings on the native db reset port:

- Forward the parent's resolved `--yes` into the experimental-reset delegate argv
  (`--yes=${yes}`). Go's bound `--yes` beats AutomaticEnv, so `--yes=false` now
  overrides an inherited SUPABASE_YES=true and the Go child no longer auto-confirms
  a reset the user protected (P1); `--yes=true` is honored even in machine mode.
- Reject an explicit empty top-level `project_id = ""` in legacyCheckDbToml with
  Go's "Missing required field in config: project_id" (config.Validate parity), so a
  native remote reset aborts before legacyDropUserSchemas instead of dropping on a
  config Go rejects. Absent project_id stays tolerated (deferred broader case).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@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: e590ac2ca1

ℹ️ 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/shared/cli/run.ts

@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

const str = (rec: RawDoc | undefined, key: string): string => {
const value = rec?.[key];
return typeof value === "string" ? legacyExpandEnv(value, lookup) : "";

P2 Badge Honor auth SUPABASE_ env overrides*

When an auth validation depends on fields supplied via Go-supported env overrides—for example [auth.captcha] enabled = true with SUPABASE_AUTH_CAPTCHA_PROVIDER and SUPABASE_AUTH_CAPTCHA_SECRET in the shell or supabase/.env—this helper only reads the TOML value and returns empty for absent keys. Go's config.Load uses Viper AutomaticEnv/ExperimentalBindStruct before Validate, so that config loads successfully; the native db push/db reset preflight now fails with Missing required field... and blocks valid projects. Thread the SUPABASE_AUTH_* override lookup through str and the matching gate helper before treating fields as absent.


yield* flushBatch;
const index = executed;
yield* session
.exec(statement)

P2 Badge Keep migration batches transactional

When a migration contains a statement like CREATE INDEX CONCURRENTLY or VACUUM, this branch flushes the current batch and executes that statement outside the surrounding transaction, then later inserts the migration history row. The current Go CLI still queues every statement and the history insert into one pgconn.Batch in MigrationFile.ExecBatch, so those files fail instead of being partially applied or recorded; the native db push/remote reset path can now mutate the database and mark a migration applied that Go would reject. Remove this standalone path unless the Go engine is updated in the same change.

ℹ️ 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/shared/legacy/global-flags.ts
@avallete

avallete commented Jul 7, 2026

Copy link
Copy Markdown
Member Author

Manual results

Scenario Result Notes
L1–L3 Local cycle Pass initdb start → idempotent db startdb reset --localstop --no-backup
L6 Bucket seeding Pass Full start required. [storage.buckets.qa-bucket] + objects_path = "./seed-buckets/qa-bucket". Missing seed dir fails with NotFound on objects_path (expected); after creating supabase/seed-buckets/qa-bucket/test.txt, reset prints Creating Storage bucket + Uploading: … => qa-bucket/test.txt + Finished … on branch main.
R2 Linked reset Pass link --project-ref … --profile supabase-staging + db reset --linked --yes
P3 Linked push Pass db push --linked --yes → up to date
GAP-remote-prec Pass On --linked, SUPABASE_DB_MIGRATIONS_ENABLED=true overridden by [remotes.production]Loading config override + migrations skipped. Prior harness false positive used --db-url (no remote merge on either binary).
GAP-env Pass (TS improvement) SUPABASE_YES=1 in supabase/.env.local auto-confirms db push --db-url without shell env. Intentional TS behavior; do not regress.
R8 Experimental reset Pass SUPABASE_EXPERIMENTAL=true db reset --linked --yes on legacy binary completes remote truncate/reset. System-installed supabase fails on local_smtp config key (different CLI/schema version) — not a port issue.

@avallete
avallete enabled auto-merge July 7, 2026 10:40
@avallete
avallete added this pull request to the merge queue Jul 7, 2026
Merged via the queue into develop with commit 973ed75 Jul 7, 2026
22 checks passed
@avallete
avallete deleted the avallete/exciting-noyce-078f0e branch July 7, 2026 10:54
Coly010 added a commit that referenced this pull request Jul 7, 2026
…ets hoist merge

develop's db push/reset/start port (#5715) hoisted the seed-buckets
config load out of buckets.handler.ts and into the shared
legacySeedBucketsRun (also used by db reset --local), which
superseded the inline goViperCompat opt-in added on this branch.
Carry the opt-in over to the new call site so seed buckets and
db reset --local keep Go-parity config semantics.
mxcl pushed a commit to automic-vault/supabase-cli that referenced this pull request Jul 9, 2026
…reate (supabase#5840)

## 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](https://linear.app/supabase/issue/CLI-1877/reject-directlocal-db-push-db-reset-without-project-config-go):
a Codex P1 finding on supabase#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 supabase#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.
mxcl pushed a commit to automic-vault/supabase-cli that referenced this pull request Jul 9, 2026
…abled remote precedence (CLI-1878) (supabase#5839)

## What

Closes the remaining gaps in
[CLI-1878](https://linear.app/supabase/issue/CLI-1878/legacy-shell-full-viper-env-override-semantics-project-env-remote):
full viper env-override semantics (project `.env`, remote precedence,
malformed bools) in the TS legacy shell.

A `go-parity-auditor` pass determined most of the issue's original
claims had already been fixed by an earlier PR (supabase#5715) —
project-`.env`-aware `SUPABASE_YES`/`SUPABASE_EXPERIMENTAL` resolvers,
remote-config precedence, malformed-bool-fails-the-load,
explicit-flag-beats-env, and case-agnostic `env(...)` resolution all
already exist for `db push`/`db reset`/`db pull`/`config
push`/`migration down`/`repair`/declarative-schema. This PR closes the
**5 concrete gaps** that pass left open:

1. **`gen signing-key`** — the overwrite-confirmation prompt only
consulted the shell env for `SUPABASE_YES`. Go's `flags.LoadConfig`
loads the project `.env` before the prompt (`signingkeys.go:99,130`).
2. **`storage rm`** — same gap for the delete-confirmation prompt (both
the `--local` and default `--linked` branches of Go's
`ParseDatabaseConfig` load the project `.env` first).
3. **`migration fetch`** — same gap for the migrations-dir overwrite
prompt (defaults to `--linked`, same `ParseDatabaseConfig` path).
4. **standalone `seed buckets`** — its fallback `yes` resolution (used
when `db reset` doesn't pass a pre-resolved value) had the same gap. `db
reset`'s own passthrough was already correct.
5. **`[remotes.*].auth.enabled` remote precedence** — this key was
missing from `LEGACY_ENV_OVERRIDABLE_KEYS`, so a linked remote's
`auth.enabled` TOML value could lose to a `SUPABASE_AUTH_ENABLED` env
var instead of winning, unlike every other allowlisted key (Go's
`mergeRemoteConfig` applies the whole matched block above
`AutomaticEnv`).

Each of the 4 handler fixes follows the existing `legacyLoadProjectEnv`
+ `legacyResolveYesWithProjectEnv` pattern already used by `db
push`/`config push`/`migration down`/`repair`.

### Fixed along the way

- `migration fetch`'s new project-`.env` load initially ran *before* the
`[db-url linked local]` flag-conflict check — an ordering regression
relative to Go and sibling `migration down`/`repair` (caught by
`architect-reviewer`). Reordered so the flag check runs first; added a
regression test that fails without the fix.

## Behavior changes to be aware of

- `gen signing-key`, `storage rm`, `migration fetch`, and `seed buckets`
now honor `SUPABASE_YES` set only in
`supabase/.env`/`.env.local`/`.env.<env>[.local]` (previously they only
saw the shell env). This is a pure Go-parity fix, but on an earlier
build of this TS legacy shell it was inert — a stale `SUPABASE_YES=true`
left in a project's `.env` will now silently auto-confirm these
destructive prompts.
- A linked project's `[remotes.<name>].auth.enabled` TOML value now
correctly beats a `SUPABASE_AUTH_ENABLED` env var (previously the
reverse). If anything relied on the env var overriding a remote block's
explicit `auth.enabled`, that no longer happens.

## Test plan

- `bun run test:core` — all unit + integration tests green (300 tests
across the touched files, full suite unaffected).
- `bun run check:all` — types/lint/fmt/knip clean.
- New regression tests (integration, per this workspace's testing
pyramid):
- `signing-key.integration.test.ts` — project-`.env` `SUPABASE_YES`
auto-confirms the overwrite even with a piped `n` (defensively clears
any leaked shell `SUPABASE_YES` first).
  - `rm.integration.test.ts` — same, for the delete confirmation.
- `buckets.integration.test.ts` — same, for the standalone `seed
buckets` overwrite prompt.
- `fetch.integration.test.ts` — same, for the migrations-dir overwrite
prompt; plus a new test locking in the flag-conflict-before-env-read
ordering fix (verified it fails without the reorder).
- `legacy-db-config.toml-read.unit.test.ts` — 2 new unit tests for
`auth.enabled`: a matched remote block beats the env var, and a control
case where the env var still wins when the block omits the key.
- Relocated one pre-existing test's fixture
(`fetch.integration.test.ts`, "reports a write failure"): the
file-collision now lives at `<workdir>/supabase/migrations` instead of
`<workdir>/supabase` itself, since the latter would break the new
project-`.env` read before ever reaching the `mkdir` under test.
Verified this preserves the original test's coverage.

## Judgement calls deliberately left open

- **`[y/N] y` echo doesn't say *why* it auto-confirmed.**
`supabase-dx-reviewer` flagged that the prompt echo is byte-identical
whether the answer came from `--yes`, the shell env, or a forgotten
project `.env` value — but explicitly recommended *not* annotating it,
since that would diverge from Go's byte-identical `console.PromptYesNo`
output (this is a strict 1:1 port). Not changed here; a source
annotation would need to land in the Go CLI first if ever desired.
- **DB-bootstrap seam explicit `--experimental=false` forwarding.**
`go-parity-auditor` flagged (but could not fully confirm, since it
didn't trace the hidden `db __db-bootstrap` subprocess) that
`legacy-db-bootstrap.seam.layer.ts` forwards `--experimental` to the Go
child only when true, never an explicit false — for `db reset
--experimental=false` with `SUPABASE_EXPERIMENTAL=true` inherited by the
child, the child might re-resolve `true` independently. Flagged as a
caveat, not a confirmed gap; out of scope here.
- **`storage.enabled`/`realtime.enabled` don't read any `SUPABASE_*` env
override at all** in `legacy-db-config.toml-read.ts`, unlike
`auth.enabled`. This is a latent divergence noted by `go-parity-auditor`
but is outside CLI-1878's 5 named items; left as a separate follow-up.

🤖 Generated with the `issue-autopilot` skill.
pull Bot pushed a commit to oogalieboogalie/cli that referenced this pull request Jul 24, 2026
## TL;DR:

Ports the catalog cache to native TS the recent `db push` port
supabase#5715 <br>left this Go feature out entirely this
restores/extends it....

## What's Introduced?

Basically, we're porting Go's pg-delta migrations-catalog cache to
native TS, <br>and for that we've introduced:

`legacyTryCacheMigrationsCatalog` in `legacy-pgdelta.cache.ts` (gated on
pg-delta being enabled, <br>reusing the `legacyExportCatalogPgDelta` so
it can't inherit the `/workspace` mount bug fixed separately on the Go)
<br>the corresponding `edge-runtime/docker/ssl-probe` layer wiring in
`push.layers.ts`, the actual call site in `push.handler.ts` (after a
successful migration apply, warns and never fails the push on error)

also added integration + unit test coverage in
`push.integration.test.ts` and `legacy-pgdelta.cache.unit.test.ts`
<br>for the enabled/disabled/failure paths and the new pure helpers....

## Ref:

* closes supabase#5921
* Followup to: [supabase#5929](<supabase#5929>)
* Extends: supabase#5715
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.

3 participants