Skip to content

feat(cli): port db diff and db pull to native TypeScript (CLI-1313)#5620

Open
Coly010 wants to merge 25 commits into
developfrom
cli/port-db-diff-pull-commands
Open

feat(cli): port db diff and db pull to native TypeScript (CLI-1313)#5620
Coly010 wants to merge 25 commits into
developfrom
cli/port-db-diff-pull-commands

Conversation

@Coly010

@Coly010 Coly010 commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Ports supabase db diff and supabase db pull from the Go CLI to native Effect handlers in the legacy shell, replacing their Phase-0 proxy stubs. Linear: CLI-1313. Builds on the pg-delta infrastructure merged in #5586.

What changed

Shared engine (promotion + new modules)

  • Promotes the pg-delta engine — adapter, seam, cache, deno-templates, declarative write, and its errors — out of db/schema/declarative/ into db/shared/legacy-pgdelta.*, so diff, pull, and declarative import one copy. The generate/sync call sites are updated in the same change; findDropStatements is hoisted to legacy-sql-split.ts.
  • New shared modules: legacy-diff-engine.ts (pure resolveDiffEngine / resolvePullDiffEngine / shouldUsePgDelta), legacy-migra.* (byte-exact Go templates + a legacyDiffMigra free function over the pg-delta context with the OOM→supabase/migra bash fallback), and migration-file helpers.

Live-shadow Go seam

  • Adds a hidden db __shadow command (with PrepareShadowSource / PrepareRawShadow, refactored out of DiffDatabase / pullDeclarativePgDelta). It provisions the shadow Postgres, prints its connection URL + container id, and leaves it running so the native TS handler runs migra/pg-delta against it and tears it down — mirroring Go's differ(shadowConfig, config) for both engines (migra needs a live source, which the catalog-file __catalog seam couldn't provide). start.SetupDatabase stays unported, which is why provisioning remains in Go.

db diff

  • Native pg-delta / migra diff via edge-runtime; explicit --from/--to mode (always pg-delta). --use-pgadmin / --use-pg-schema rebuild the argv and delegate to the bundled Go binary (telemetry disabled on the child so the single cli_command_executed event comes from the TS command).

db pull

  • Native pg-delta / migra migration pull + --declarative pg-delta export; reconciles supabase_migrations.schema_migrations and updates remote history on confirmation. --use-pg-delta is hidden + emits the cobra deprecation line. The --experimental structured dump and initial-pull pg_dump (migra) sub-branches delegate to the Go binary (unported pg_dump).

Cross-cutting

  • Adds --output-format json/stream-json envelopes (text reproduces Go bytes); unit + integration + e2e tests; SIDE_EFFECTS.md for both; flips both commands to ported in go-cli-porting-status.md.

Reviewer notes

  • Architecture decision (vs the stale CLI-1313 issue text): pg-delta/migra run via the edge-runtime Deno container, not a library import — Docker is intentionally retained for byte-exact Go parity and zero new published deps, following feat(cli): port db dump, query, and schema declarative to native TypeScript #5586.
  • Parity gap to flag: an empty db pull exits non-zero (No schema changes found), matching Go's errInSync; db diff exits 0. Documented in pull/SIDE_EFFECTS.md.
  • Deliberately preserved Go behaviors: the migra bash-fallback shell construction and --file/--output/migration-name path handling reproduce pre-existing Go behavior rather than diverging from the strict 1:1 port.
  • The migra OOM bash fallback and the linked-pull IPv4 pooler fallback for declarative export are the thinnest-tested paths.

Replace the Phase-0 Go proxy stubs for `db diff` and `db pull` with native
Effect handlers in the legacy shell.

- Promote the pg-delta engine (adapter, seam, cache, deno-templates, write,
  errors) from db/schema/declarative/ to db/shared/legacy-pgdelta.*; update the
  generate/sync call sites. Hoist findDropStatements to legacy-sql-split.
- Add a live-shadow Go seam: hidden `db __shadow` command (+ PrepareShadowSource
  / PrepareRawShadow, refactored from DiffDatabase / pullDeclarativePgDelta)
  provisions a shadow Postgres, prints its URL + container, and leaves it running
  so the native handler runs migra/pg-delta against it then tears it down. Both
  engines now mirror Go's differ(shadowConfig, config).
- db diff: native pg-delta / migra (edge-runtime) + explicit --from/--to mode;
  --use-pgadmin / --use-pg-schema delegate to the Go binary.
- db pull: native pg-delta / migra migration + --declarative pg-delta export;
  reconciles schema_migrations and updates remote history. --experimental dump
  and initial-pull pg_dump (migra) delegate to the Go binary.
- Shared: legacy-diff-engine (pure resolvers), legacy-migra (free fn over
  context, OOM->bash fallback), byte-exact migra deno-templates, migration-file
  helpers, pull.sync reconciliation.
- Add --output-format json/stream-json envelopes; unit + integration + e2e
  tests; SIDE_EFFECTS.md; flip both commands to `ported` in the porting status.
@Coly010 Coly010 requested a review from a team as a code owner June 18, 2026 13:25
Comment thread apps/cli-go/cmd/db.go Fixed
Comment thread apps/cli-go/cmd/db.go Fixed
@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown

Supabase CLI preview

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

Preview package for commit a016ddc.

Coly010 added 2 commits June 18, 2026 14:38
…CLI driver behaviour (ci: e2e shard 2/3)

db pull --local connects via the native LegacyDbConnection sql-pg layer, so
its connection-failure stderr diverges from Go's pgx output by driver — the same
un-normalizable divergence already documented for db lint --local and test db
--local, which deliberately omit testParity. The newly-added
testParity(["db","pull","--local"]) was inconsistent with that established
pattern; the user-observable contract (non-zero exit + connect error on stderr)
remains covered by the behaviour test.
CodeQL flagged clear-text logging (CWE-312) on the hidden `db __shadow`
seam: it printed the shadow Postgres connection URLs — password included —
to stdout for the native-TS db diff/pull caller to capture.

Emit the URLs without the password via a new ToPostgresURLWithoutPassword
(ToPostgresURL is unchanged; its many env-var callers still need the
password). The shadow always uses the local Postgres password, which the TS
seam already resolves from config.toml, so it re-injects the password into
the captured URLs before handing them to the differ / sql-pg connection. No
credential is ever written to stdout.

Fixes CodeQL alerts #27/#28.
@Coly010 Coly010 self-assigned this Jun 18, 2026
Comment thread apps/cli-go/internal/utils/connect.go
@Coly010

Coly010 commented Jun 19, 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

output: Flag.string("output").pipe(
Flag.withAlias("o"),
Flag.withDescription("Write explicit diff output to a file path."),
Flag.optional,

P2 Badge Avoid reusing the global --output flag for paths

For db diff --from ... --to ... --output out.sql, this local flag has the same name and -o alias as the legacy root LegacyOutputFlag, whose parser only accepts the Go machine formats (env|pretty|json|toml|yaml|table|csv). Since global flags are registered for the whole legacy command tree, an arbitrary file path is consumed/rejected as the global output format before the handler can receive it, so the explicit diff file-output mode added here is not usable through the CLI.

ℹ️ 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/pull/pull.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/pull/pull.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/pull/pull.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/diff/diff.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/shared/legacy-pgdelta.seam.layer.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/diff/diff.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/pull/pull.handler.ts
Comment thread apps/cli/src/legacy/commands/db/diff/diff.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/diff/diff.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/diff/diff.handler.ts
Coly010 added 3 commits June 19, 2026 10:40
… output for db pull

Two Go-parity fixes in the native db pull handler, both flagged in review:

- --experimental: Go gates the structured-dump branch on viper's EXPERIMENTAL,
  which resolves from the --experimental pflag OR SUPABASE_EXPERIMENTAL env
  (cmd/root.go:318-320,327,334). The handler only checked the env var, so
  `supabase --experimental db pull` slipped past and ran the native diff path.
  Now reads LegacyExperimentalFlag and ORs it with the env check.
- Machine-output prompt: json/stream-json layers fail every prompt as
  non-interactive, so the history-update confirm failed the command before the
  structured payload was emitted. Go's PromptYesNo returns the default (yes) on
  --yes / non-interactive / error (console.go:74-82); now machine mode and any
  prompt error fall through to the default, matching Go.

Adds regression tests for the --experimental flag form and machine-mode-in-TTY.
Go's shadow cleanup routes through DockerRemove with RemoveOptions{RemoveVolumes:
true, Force: true} (internal/utils/docker.go:330). The native seam ran
`docker rm -f` without -v, leaving the Postgres anonymous data volume dangling
after every db diff / db pull. Add -v to match. Flagged in review.
Automated-review findings, each grounded in Go behaviour:

- Preserve connect_timeout from --db-url: the connToUrl helpers (diff + pull)
  dropped conn.connectTimeoutSeconds before serializing, so it defaulted to 10s;
  Go's ToPostgresURL serializes the parsed ConnectTimeout (connect.go).
- Don't double-print the --use-pg-schema experimental warning: the delegated Go
  child prints it in its RunE (cmd/db.go), so the TS pre-print was a duplicate.
- Print the migration repair confirmation: Go's repair.UpdateMigrationTable
  emits 'Repaired migration history: [<version>] => applied' to stderr
  (repair.go); the native pull updated the table silently.
- Detect the git branch from the resolved --workdir, not the caller's CWD: Go
  chdirs into --workdir before GetGitBranch (cmd/root.go). detectGitBranch now
  takes an optional startDir (default = runtime CWD, so branches callers are
  unchanged) and db diff passes cliConfig.workdir.

Updates the pg-schema delegate test and adds a detectGitBranch startDir test.

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

ℹ️ 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-go/cmd/db.go Outdated
Comment thread apps/cli/src/legacy/commands/db/pull/pull.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/diff/diff.handler.ts
yield* fs
.makeDirectory(path.dirname(migrationPath), { recursive: true })
.pipe(Effect.mapError((cause) => new LegacyDbPullWriteError({ message: cause.message })));
yield* fs.writeFileString(migrationPath, out).pipe(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Append pulled SQL instead of truncating existing migration

If the generated migration path already exists, this overwrites the file with the new diff SQL. Go opens the same path with O_APPEND (apps/cli-go/internal/db/pull/pull.go:189-196) so it preserves existing content, which matters for timestamp/name collisions and for the initial-pull flow that appends diff output after a dump; the native path can silently discard migration content before repairing the remote history.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Rejecting this one — the native write here can't reach the case Go's O_APPEND is for. Go opens with O_APPEND (internal/db/pull/pull.go:191), but its own comment (:189-190) explains it preserves content only in the dump-then-diff sequence (errMissing && !usePgDeltaDiff): dumpRemoteSchema truncates with O_TRUNC (:150), then diffRemoteSchema appends a second pass. In the TS port that exact branch is delegated to the Go binary (pull.handler.ts, the sync.kind === "missing" && !usePgDeltaDiffproxy.exec path), so the native write never runs after a dump. Every native flow that reaches this write corresponds to a Go path where diffRemoteSchema is the sole writer into a freshly-O_CREATEd file (pg-delta initial pull; in-sync delta path with a brand-new timestamped path), where append and truncate are byte-identical. The only residual gap is a same-second + same---name collision (seconds-resolution timestamps), which isn't what Go's append targets and would concatenate unrelated diffs. Leaving the truncate as-is; worth revisiting if the migra initial-pull branch is ever ported natively (then the append becomes load-bearing).

Comment thread apps/cli/src/legacy/commands/db/diff/diff.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/pull/pull.handler.ts
…xplicit diff output

Two Go-parity fixes from review:

- db __shadow never loaded config.toml: the hidden seam carries no
  db-url/local/linked target flag, so the root ParseDatabaseConfig skips
  LoadConfig (db_url.go:46-90) and the shadow was provisioned with default [db]
  settings. With a custom [db].password the shadow was created with the default
  password while the native-TS caller injects the config password into the seam
  URLs (the CWE-312 fix), so the diff/export connection failed. Load config
  explicitly in the __shadow RunE so the shadow uses the project's password,
  shadow_port, version, and baseline.
- Explicit db diff --output to a nested path failed: the native handler called
  writeFileString without creating parent dirs; Go's writeOutput -> utils.WriteFile
  creates them first (explicit.go, misc.go). Make the parent dir first.
@Coly010

Coly010 commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

🤖 pr-autopilot: the Start check (CI / Go-CLI stack smoke job) is failing on supabase/logflare:1.44.3manifest unknown during stack bring-up (initSchema15 then exits 1). This is infra, not this PR: it reproduces with a byte-identical signature on a recent unrelated develop commit, and this branch touches none of the stack-init / start / logflare paths. It triggers here only because the PR edits apps/cli-go/**, which matches the cli-go-ci.yml path filter. A re-run did not clear it, so it looks like a standing ghcr.io image-mirror gap for logflare 1.44.3 rather than a transient flake — one for the infra/release owners. Flagging so reviewers don't block this PR on it.

Coly010 added 2 commits June 19, 2026 11:29
… in db pull

Two Go-parity fixes from review:

- Project .env for the pg-delta engine flag: Go's LoadConfig loads supabase/.env
  via godotenv before viper reads EXPERIMENTAL_PG_DELTA (config.go:624,1055-1096),
  so a project enabling pg-delta in supabase/.env takes the pg-delta path. The
  handlers read SUPABASE_EXPERIMENTAL_PG_DELTA (and SUPABASE_EXPERIMENTAL) only
  from process.env. Route them through the project-env-aware resolver already
  loaded by legacyReadDbToml (exposed as toml.envLookup), matching Go's
  shell-then-.env precedence.
- Local-target shadow for db pull: Go's pull runs through DiffDatabase, which
  derives targetLocal from utils.IsLocalDatabase and substitutes the declarative
  contrib_regression target override (diff.go:190,196-197). The native pull
  hard-coded targetLocal:false and ignored shadow.targetUrlOverride, so
  db pull --local with declarative schemas diffed the live local DB. Now passes
  targetLocal: resolved.isLocal and uses shadow.targetUrlOverride ?? targetUrl,
  mirroring the native db diff handler.

Adds pull integration tests for the .env-pg-delta and local-target paths.

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

ℹ️ 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/pull/pull.handler.ts
Comment thread apps/cli/src/legacy/commands/db/pull/pull.sync.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/pull/pull.handler.ts Outdated
…ad of an empty repair

Go drives the migration-history repair from the generated timestamp and globs
supabase/migrations/<timestamp>_*.sql, failing with os.ErrNotExist when nothing
matches (repair.GetMigrationFile, internal/migration/repair/repair.go). A name
with a path separator (supabase db pull foo/bar) writes a nested file
<ts>_foo/bar.sql whose basename doesn't match, so Go fails the repair. The native
legacyUpdateMigrationHistory re-parsed the basename and fell back to version="",
silently upserting an empty-version migration-history row. Now it fails with Go's
glob/file-does-not-exist message before any upsert. Adds a pull integration test.

@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: 923d6a437d

ℹ️ 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/diff/diff.handler.ts
Comment thread apps/cli/src/legacy/commands/db/pull/pull.handler.ts
Comment thread apps/cli/src/legacy/commands/db/shared/legacy-pgdelta.seam.layer.ts
Comment thread apps/cli/src/legacy/commands/db/diff/diff.handler.ts Outdated
…reat empty --output as stdout

Four Go-parity fixes from review:

- Forward explicit false target flags (--linked=false/--local=false) to delegated
  db diff/pull children. Go's ParseDatabaseConfig selects the target by
  flag.Changed before the value (internal/utils/flags/db_url.go), so a
  Changed-but-false flag still selects that target. Dropping Some(false) made the
  child fall through to a different default (diff: local; pull: linked) than the
  native path resolved. New pushTarget forwards linked/local whenever Some.
- Disable telemetry for the hidden db __shadow and __catalog seam subprocesses
  (SUPABASE_TELEMETRY_DISABLED=1), matching the explicit LegacyGoProxy delegates,
  so native db diff/pull doesn't record an extra internal cli_command_executed.
- Treat an empty --output value as stdout in explicit diff mode: Go gates the file
  write on len(outputPath) > 0 (explicit.go), so --output="" prints to stdout
  instead of failing to write SQL into the project directory.

Adds diff/pull integration tests for the delegate target flag and empty --output.

@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

output: Flag.string("output").pipe(
Flag.withAlias("o"),
Flag.withDescription("Write explicit diff output to a file path."),
Flag.optional,

P2 Badge Route explicit diff output around the global parser

This command-local --output/-o will not receive normal file paths because the legacy root already registers a global LegacyOutputFlag with the same name and alias, and the repo documents that Effect extracts global flags before the leaf parse and validates them against the fixed output-format enum. As a result, supabase db diff --from local --to linked --output tmp/diff.sql is rejected as an invalid global output value, while a path like --output json is treated as machine-output mode instead of an explicit diff file, so the advertised Go-compatible explicit-output workflow is broken before legacyDbDiff runs.

ℹ️ 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/diff/diff.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/shared/legacy-pgdelta.seam.layer.ts
…shadow seam

Two Go-parity fixes from review:

- Empty --file -> stdout: Go's SaveDiff gates the file write on len(file) > 0
  (internal/db/diff/pgadmin.go), so an empty --file="" (unset shell var) prints
  the diff to stdout instead of writing a nameless <timestamp>_.sql migration.
  The explicit-mode file branch now requires a non-empty value.
- Forward --profile into the hidden seam subprocesses: the Go root loads the
  profile before config and applies profile-specific overrides (cmd/root.go,
  config_path.go), but a flag-only --profile snap was never forwarded to the
  __shadow / __catalog / db start children (only SUPABASE_PROFILE env was
  inherited). Forward the raw --profile flag token when it isn't the default
  'supabase', mirroring the existing --network-id forwarding, so the shadow
  baseline is built under the selected profile.

Adds a diff test for the empty --file case.

@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

output: Flag.string("output").pipe(

P2 Badge Avoid reusing the global --output flag

The legacy root already registers --output/-o globally via LegacyOutputFlag, and the repo notes that Effect CLI hoists global flags into a single tree-wide registry. Adding another --output here does not faithfully shadow Go's command-local diff output flag: real invocations such as supabase db diff --from local --to linked --output tmp/diff.sql can be parsed by the global enum (env|pretty|json|...) before this handler sees the path, while the new tests call the handler directly. That makes explicit diff file output unusable for arbitrary paths from the actual CLI.

ℹ️ 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/pull/pull.sync.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/shared/legacy-migra.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/diff/diff.handler.ts
Coly010 added 5 commits June 19, 2026 13:50
…d db pull

Go loads the project ref before LoadConfig on the linked path
(internal/utils/flags/db_url.go:87-97), so a matching [remotes.<ref>] block
merges before experimental.pgdelta.enabled, format_options, deno_version, and
declarative paths are read. The native handlers read config once without the
ref, ignoring remote overrides. Pass the resolved linked ref into
legacyReadDbToml; for db diff, reorder so the linked ref resolves before the
merged read and engine decision, and thread the merged config through the
explicit --from/--to cascade (Go's resolveExplicitDatabaseRef LoadConfig).
…he IPv4 pooler

Go wraps both the migration-style diff (diffRemoteSchema) and the declarative
export (pullDeclarativePgDelta) with dump.PoolerFallbackConfig and retries
against the IPv4 transaction pooler when the direct host is unreachable over
IPv6 from inside the edge-runtime container (internal/db/pull/pull.go). The
native handler ran each once against the direct targetUrl with no fallback.

Add a withPoolerFallback helper mirroring the ported db dump (step 7b): classify
the differ/export error message via legacyIsIPv6ConnectivityError (the
edge-runtime/migra errors embed the container stderr Go classifies), gate to the
linked path with a direct db.<ref>.<host> connection, resolve the pooler with
orElseSucceed(None) so a resolution failure preserves the ORIGINAL error (Go's
ok=false), emit the byte-exact IPv6 warning, and retry once against the pooler
reusing the same shadow source.
Go's WriteDeclarativeSchemas points [db.migrations] schema_paths at the
declarative dir after a declarative pull, but only when pg-delta is disabled in
config (declarative.go:260-268, gated on IsPgDeltaEnabled). generate/sync
force-enable pg-delta so the branch is unreachable for them, but db pull
--declarative does not (cmd/db.go:180-182), so without this a pull with pg-delta
disabled leaves db reset/db diff reading supabase/migrations and ignoring the
pulled files.

Port updateDeclarativeSchemaPathsConfig as a raw-text replace-or-append
(byte-matching Go's regex and literal block, not a TOML re-serialize) and invoke
it from the pull declarative path when config pg-delta is disabled, keeping the
shared declarative writer a pure file-materializer for generate/sync.
…l paths

The pgAdmin/pg-schema delegate (db diff) and the initial-migra / EXPERIMENTAL
delegate (db pull) forwarded to the Go binary with inherited stdout and returned
without output.success, so in --output-format json|stream-json mode scripted
callers got the child's raw human/SQL output on stdout instead of a structured
envelope (broken JSON.parse, the CLI-1546 'stdout is payload-only in machine
mode' invariant).

Add LegacyGoProxy.execCapture, which pipes the child's stdout (capturing it as a
string) while keeping stdin/stderr inherited and still propagating the exit code.
In machine mode the delegate paths now capture the child stdout and wrap it in
output.success with the native envelope shape; text mode keeps the inheriting
exec so the child's output streams straight through. Go has no --output-format
concept, so the envelope is a TS-layer concern with no Go template; the delegated
child owns any file write, so those fields are reported as null/best-effort.
…GDELTA_DEBUG

When migration-style db pull uses pg-delta and PGDELTA_DEBUG is set, Go saves a
debug bundle on an empty diff and embeds its path in the in-sync error
(internal/db/pull/pull.go:176-185, pgdelta_pull_debug.go). The native path
discarded the capture and returned the plain error.

Port saveEmptyPgDeltaPullDebug: capture the shadow source catalog + pg-delta
stderr during the diff run (Go's DiffDatabase), export the remote catalog at
save time (warn-and-continue), write the bundle (source/target catalog, stderr,
redacted connection.txt, error.txt), print the catalog summary + issue-report
message, and fail with 'No schema changes found (debug bundle: <dir>)'; a
save failure falls through to the plain in-sync error.

Hoist the declarative debug-bundle helper to db/shared/legacy-debug-bundle.ts
(now used by declarative + pull), extend it with inline catalog strings and
connection.txt (Go's full DebugBundle), and hoist the debug-id formatter. Add
pure helpers (URL redaction, catalog summary, byte-size) with unit coverage and
an integration test for the empty-diff bundle.
…t, explicit preflight)

Three Go-parity fixes from review of the db diff/pull port:

- pull.sync.ts: reject migration versions above Number.MAX_SAFE_INTEGER in
  parseVersion, mirroring Go's strconv.Atoi range error (skip). A crafted 16+
  digit version could exceed the exhausted-side sentinel and stall the two-pointer
  scan in legacyReconcileMigrations indefinitely.

- legacy-migra.ts: the migra OOM bash fallback now derives extraHosts (Linux
  host.docker.internal mapping) and the network from --network-id, matching Go's
  DockerStart (internal/utils/docker.go:266-271). Previously it always ran host
  networking with no extra hosts, so the fallback failed on custom-network /
  host.docker.internal setups the primary path handles.

- diff.handler.ts: explicit --from/--to mode now runs the target-flag preflight
  (resolver.resolve) when a target flag is changed, matching Go running
  ParseDatabaseConfig in PersistentPreRunE before RunExplicit (cmd/root.go:118).
  A bad --db-url / unreachable --linked is surfaced instead of silently ignored.

@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 command = ChildProcess.make(resolved.found, args, {

P2 Badge Forward --profile through execInherit

When db schema declarative generate or sync invokes the reset recovery path under a flag-only profile such as --profile snap, this child runs supabase-go db reset --local without the selected profile, unlike the other seam subprocesses that append profileArgs. Go's original path runs reset in-process after loading the root profile, so profile-specific config and auth overrides are applied; here the reset can target the default supabase profile instead of the user's selected profile.

ℹ️ 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-pgdelta.seam.layer.ts
The native db diff/pull handlers merge a linked [remotes.<ref>] override for the
target/engine/format, but the shadow database is provisioned by the hidden Go
'db __shadow' command, which ran flags.LoadConfig with no project ref — so the
shadow baseline (db.major_version, service enables, vault) was built from the
base config, not the remote-merged config the Go monolith uses. That can produce
spurious or missing migration SQL on the linked path.

cli-go: db __shadow gains a --project-ref flag; when set it seeds
flags.ProjectRef before LoadConfig so the matching [remotes.<ref>] block merges
(pkg/config/config.go). Omitted on local/db-url shadows, which the monolith
never remote-merges, so base config is used exactly as before.

TS: provisionShadow gains an optional projectRef forwarded as --project-ref
(flag, not env — channel parity, and avoids over-merging on local shadows via
inherited env). The diff native path and both pull shadow paths pass the resolved
ref only on the linked path.

@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

output: Flag.string("output").pipe(
Flag.withAlias("o"),
Flag.withDescription("Write explicit diff output to a file path."),

P2 Badge Don't let global --output consume diff file paths

For real supabase db diff --from … --to … --output tmp/diff.sql invocations, --output/-o is already registered as the legacy global output-format flag; apps/cli/src/shared/legacy/global-flags.ts documents that Effect hoists global flags before leaf parsing and restricts this flag to env|pretty|json|toml|yaml|table|csv. This local file-path flag therefore never reliably receives arbitrary paths, so explicit diff output either fails parsing (e.g. tmp/diff.sql) or is treated as a global format instead of writing the file that Go's db diff --output supports.

ℹ️ 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/pull/pull.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/diff/diff.handler.ts Outdated
…grations catalog ref

Two Go-parity follow-ups from review:

- pull: the machine-mode delegated pull (initial-migra/EXPERIMENTAL) ran the Go
  child with inherited stdin and reported remoteHistoryUpdated:false. Go's
  PromptYesNo(...,true) updates schema_migrations on the default (non-TTY/accept),
  so the report was wrong, and in a TTY the child's prompt could block a
  --output-format json caller before the envelope. Run execCapture with a non-TTY
  stdin ('ignore') so the prompt takes Go's default without blocking, and report
  remoteHistoryUpdated:true to match the child and the native machine-mode path
  (internal/db/pull/pull.go:73, internal/utils/console.go). Adds an optional
  stdin to LegacyGoProxy.execCapture (default inherit).

- diff: explicit --from linked --to migrations exported the migrations catalog
  without the resolved ref, so the __catalog child reloaded base config. Go
  resolves refs in order and the linked LoadConfig(ref) merges [remotes.<ref>]
  before the migrations catalog is built (explicit.go), so pass the ref to
  exportCatalog — but only when a linked ref resolved earlier in the cascade, so
  --from migrations --to linked still uses base config, matching Go's order.

@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

output: Flag.string("output").pipe(
Flag.withAlias("o"),

P2 Badge Preserve db diff --output as a file flag

In explicit diff mode this needs to be Go's file-path flag, but the legacy root already registers --output/-o as a global choice flag and documents that Effect hoists globals before the leaf parse. With this duplicate name/alias, an invocation such as supabase db diff --from local --to linked --output tmp/diff.sql is consumed or validated as the global output-format flag instead of populating flags.output, so arbitrary file paths are rejected (or the explicit file-write branch is unreachable) even though Go registers this flag locally on db diff.

ℹ️ 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/diff/diff.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/pull/pull.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/pull/pull.sync.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/pull/pull.handler.ts Outdated
…sion, experimental-pull repair flag

Four Go-parity follow-ups from review of the db diff/pull port:

- diff/pull delegated argv: re-encode each parsed --schema value as a CSV field
  before forwarding to the Go child, so pflag's StringSlice doesn't CSV-split a
  comma-containing schema (e.g. "tenant,one") a second time. New
  legacySchemaToCsvField mirrors Go's encoding/csv Writer.

- pull.sync ListRemoteMigrations: suppress only undefined_table (42P01) like Go's
  pgerrcode.UndefinedTable, not every "does not exist" — a malformed history
  table (missing version column, 42703) now propagates instead of being treated as
  an initial pull. Surface the SQLSTATE on LegacyDbExecError (extracted from the
  driver cause chain) and match the code, with a tightened message fallback.

- delegatePull: parameterize remoteHistoryUpdated. Go's experimental structured
  dump returns without touching schema_migrations (pull.go:49-61) so it reports
  false; the initial-migra path repairs history so it reports true.

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

ℹ️ 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/diff/diff.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/diff/diff.handler.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/pull/pull.sync.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/shared/legacy-pgdelta.seam.layer.ts Outdated
…arity, shadow password merge

Four Go-parity follow-ups from review of the db diff/pull port:

- diff explicit mode: seed the merged config from a changed --linked preflight.
  Go's root ParseDatabaseConfig runs LoadProjectRef+LoadConfig before RunExplicit
  (cmd/root.go:118), leaving utils.Config remote-merged, so explicit local/
  migrations refs + format options use the linked [remotes.<ref>] override even
  when neither explicit ref is itself linked. Reuse the preflight's resolved ref
  to seed cfg/mergedLinkedRef.

- diff explicit mode: gate on non-empty --from/--to like Go's len()>0, so
   (shell vars) falls through to the normal diff instead of
  erroring on an unknown empty target;  still errors.

- pull.sync: compare migration versions as BigInt over Go's full int64 range.
  strconv.Atoi accepts up to math.MaxInt64, so 9999999999999999 (> JS safe int,
  < int64 max) is a real conflict, not an initial pull; only values above int64
  max are skipped (preserving the prior infinite-loop guard). Number lost
  precision in that range.

- shadow seam: re-read the injected shadow password with the linked ref so a
  [remotes.<ref>].db.password override matches the password the __shadow child
  built the shadow with (it received --project-ref); otherwise the shadow
  connection fails auth.

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

ℹ️ 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/diff/diff.handler.ts Outdated
Go loads db diff config once in PreRun and, on the linked path, only AFTER
resolving the ref (LoadProjectRef then LoadConfig), so it validates the
remote-merged config: config.go merges [remotes.<ref>] before Validate. The TS
handler read+validated the BASE config unconditionally up front, failing on
fields a remote block overrides (db.major_version, edge_runtime.deno_version,
format_options, bucket/function names, ...) before the ref was known — so
db diff --linked / --use-pgadmin --linked could fail on a project Go accepts.

Defer the read: the pgAdmin/pg-schema delegate paths now read no config (the
forwarded Go child loads it with the ref), the native local/db-url path reads
the base config (Go local/direct LoadConfig, no merge), and the linked/explicit
paths read with the resolved ref (merged). Tests: a delegated --use-pgadmin
--linked with an invalid base major_version succeeds (no base validation), while
a native local diff with the same config still fails.

@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: 2a4d9dfab6

ℹ️ 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-pgdelta.seam.layer.ts Outdated
…ote config

The exportCatalog seam forwarded the resolved linked ref only as the
SUPABASE_PROJECT_ID env var, but the hidden db schema declarative __catalog
command never runs LoadProjectRef: its group PersistentPreRunE calls
flags.LoadConfig directly, and LoadConfig keys the [remotes.<ref>] merge off
flags.ProjectRef (populated from the env only by LoadProjectRef). So the env was
silently ignored and linked catalog exports (db diff --from linked --to
migrations, db schema declarative generate --linked) built from the base config.

Mirror the db __shadow fix: add a --project-ref flag to __catalog, seed
flags.ProjectRef from it before the group LoadConfig, and have the TS seam pass
--project-ref (a flag, not env — Go-proxy channel parity) instead of
SUPABASE_PROJECT_ID.

@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

if (
typeof block["project_id"] === "string" &&
legacyExpandEnv(block["project_id"], lookup) === ref
) {

P2 Badge Match remote blocks using Go's raw project_id

When a remote block uses an env reference like project_id = "env(PROD_REF)", this comparison expands it before deciding whether to merge the block. Go's merge loop calls v.GetString("remotes.<name>.project_id") before the LoadEnvHook used by UnmarshalExact, so the merge comparison sees the raw env(PROD_REF) string and does not apply that remote override even though validation may expand it later. In that configuration the native path can unexpectedly apply remote db/pgdelta settings and build a different shadow/diff than the Go CLI.

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

Go loads db diff/pull config once, after resolving the linked ref, and validates
the [remotes.<ref>]-merged result. The TS port validated the BASE config at three
points before the ref was known, so a project valid only after a remote override
(e.g. base db.major_version=16 overridden to 15) failed in TS where Go succeeds:

- shared resolver (legacy-db-config.layer.ts): the linked branch no longer does
  the unconditional top-of-resolve base read; only --db-url/--local read base
  config (Go local/direct LoadConfig, no merge), and the linked pooler fallback
  reads with the ref.
- diff explicit mode: the cfg base read is deferred until after the linked
  preflight, reading the merged config when --linked resolved a ref.
- edge-runtime layer: the Deno-image config read moved from layer acquisition
  into run (resolved from the callers effective deno_version), so composing the
  diff/pull runtime no longer validates base config — even db diff --use-pgadmin
  --linked no longer fails at layer build.

All shared-infra changes keep --local/--db-url base validation unchanged and only
affect the linked path (matching Go). Full legacy suite (2416 tests) green.
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