diff --git a/apps/cli-go/cmd/db.go b/apps/cli-go/cmd/db.go index adb526c4f3..da22dee063 100644 --- a/apps/cli-go/cmd/db.go +++ b/apps/cli-go/cmd/db.go @@ -84,6 +84,7 @@ var ( usePgAdmin bool usePgSchema bool usePgDelta bool + useDeclarative bool pullDiffEngine = utils.EnumFlag{ Allowed: []string{"migra", "pg-delta"}, Value: "migra", @@ -106,7 +107,7 @@ var ( return diff.RunExplicit(cmd.Context(), diffFrom, diffTo, schema, outputPath, afero.NewOsFs()) } } - useDelta := shouldUsePgDelta() + useDelta := resolveDiffEngine(cmd.Flags().Changed("use-migra"), usePgAdmin, usePgSchema, shouldUsePgDelta()) if usePgAdmin { return diff.RunPgAdmin(cmd.Context(), schema, file, flags.DbConfig, afero.NewOsFs()) } @@ -176,12 +177,19 @@ var ( if len(args) > 0 { name = args[0] } + // Declarative export is opt-in via --declarative. Enabling pg-delta in config + // does not switch db pull to declarative output; it keeps the migration-file + // workflow and only defaults the shadow diff engine below. + useDeclarativePgDelta := useDeclarative + usePgDeltaDiff := resolvePullDiffEngine( + cmd.Flags().Changed("diff-engine"), + pullDiffEngine.Value, + shouldUsePgDelta(), + ) pullDiffer := diff.DiffSchemaMigra - usePgDeltaDiff := pullDiffEngine.Value == "pg-delta" if usePgDeltaDiff { pullDiffer = diff.DiffPgDelta } - useDeclarativePgDelta := shouldUseDeclarativePgDeltaPull(usePgDeltaDiff) return pull.Run(cmd.Context(), schema, flags.DbConfig, name, useDeclarativePgDelta, usePgDeltaDiff, pullDiffer, afero.NewOsFs()) }, PostRun: func(cmd *cobra.Command, args []string) { @@ -210,8 +218,15 @@ var ( Use: "commit", Short: "Commit remote changes as a new migration", RunE: func(cmd *cobra.Command, args []string) error { - useDelta := shouldUsePgDelta() - return pull.Run(cmd.Context(), schema, flags.DbConfig, "remote_commit", useDelta, false, diff.DiffSchemaMigra, afero.NewOsFs()) + // remote commit always writes a timestamped migration file. When pg-delta is + // enabled it only swaps the shadow diff engine; it never switches to the + // declarative export path. + usePgDeltaDiff := shouldUsePgDelta() + pullDiffer := diff.DiffSchemaMigra + if usePgDeltaDiff { + pullDiffer = diff.DiffPgDelta + } + return pull.Run(cmd.Context(), schema, flags.DbConfig, "remote_commit", false, usePgDeltaDiff, pullDiffer, afero.NewOsFs()) }, } @@ -361,11 +376,28 @@ func shouldUsePgDelta() bool { return utils.IsPgDeltaEnabled() || usePgDelta || viper.GetBool("EXPERIMENTAL_PG_DELTA") } -func shouldUseDeclarativePgDeltaPull(usePgDeltaDiff bool) bool { - if usePgDeltaDiff { +// resolveDiffEngine reports whether `db diff` should run in pg-delta mode. The config / +// env default (pgDeltaDefault) applies unless an explicit non-pg-delta engine is selected: +// --use-migra, --use-pgadmin, or --use-pg-schema is an authoritative rollback that clears +// pg-delta mode so diff.Run skips pg-delta-specific declarative shadow setup and the +// PGDELTA_DEBUG capture path. --use-migra defaults to true, so only an explicit pass +// (useMigraChanged) counts as opting out. +func resolveDiffEngine(useMigraChanged, usePgAdmin, usePgSchema, pgDeltaDefault bool) bool { + if useMigraChanged || usePgAdmin || usePgSchema { return false } - return shouldUsePgDelta() + return pgDeltaDefault +} + +// resolvePullDiffEngine selects whether migration-style db pull uses pg-delta for the +// shadow diff step. An explicit --diff-engine flag always wins, so --diff-engine migra is +// an authoritative rollback even when pg-delta is enabled in config; otherwise the default +// follows whether pg-delta is the active engine (config / env). +func resolvePullDiffEngine(engineFlagChanged bool, engine string, pgDeltaDefault bool) bool { + if engineFlagChanged { + return engine == "pg-delta" + } + return pgDeltaDefault } func init() { @@ -427,15 +459,18 @@ func init() { dbCmd.AddCommand(dbPushCmd) // Build pull command pullFlags := dbPullCmd.Flags() - // This flag activates declarative pull output through pg-delta instead of the - // legacy migration SQL pull path. - pullFlags.BoolVar(&usePgDelta, "use-pg-delta", false, "Use pg-delta to pull declarative schema.") + // --declarative switches pull output from a timestamped migration to declarative + // schema files exported through pg-delta. --use-pg-delta is the deprecated alias. + pullFlags.BoolVar(&useDeclarative, "declarative", false, "Pull schema as declarative files using pg-delta instead of creating a migration.") + pullFlags.BoolVar(&useDeclarative, "use-pg-delta", false, "Use pg-delta to pull declarative schema.") + cobra.CheckErr(pullFlags.MarkDeprecated("use-pg-delta", "use --declarative with [experimental.pgdelta] enabled = true in your config.toml instead.")) pullFlags.Var(&pullDiffEngine, "diff-engine", "Diff engine to use for migration-style db pull.") pullFlags.StringSliceVarP(&schema, "schema", "s", []string{}, "Comma separated list of schema to include.") pullFlags.String("db-url", "", "Pulls from the database specified by the connection string (must be percent-encoded).") pullFlags.Bool("linked", true, "Pulls from the linked project.") pullFlags.Bool("local", false, "Pulls from the local database.") dbPullCmd.MarkFlagsMutuallyExclusive("db-url", "linked", "local") + dbPullCmd.MarkFlagsMutuallyExclusive("declarative", "diff-engine") dbPullCmd.MarkFlagsMutuallyExclusive("use-pg-delta", "diff-engine") pullFlags.StringVarP(&dbPassword, "password", "p", "", "Password to your remote Postgres database.") cobra.CheckErr(viper.BindPFlag("DB_PASSWORD", pullFlags.Lookup("password"))) diff --git a/apps/cli-go/cmd/db_pull_routing_test.go b/apps/cli-go/cmd/db_pull_routing_test.go deleted file mode 100644 index c85c92200f..0000000000 --- a/apps/cli-go/cmd/db_pull_routing_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package cmd - -import ( - "testing" - - "github.com/spf13/viper" - "github.com/stretchr/testify/assert" -) - -func TestShouldUseDeclarativePgDeltaPull(t *testing.T) { - t.Run("diff-engine pg-delta keeps the migration-file workflow", func(t *testing.T) { - usePgDelta = false - t.Cleanup(func() { usePgDelta = false }) - assert.False(t, shouldUseDeclarativePgDeltaPull(true)) - }) - - t.Run("no flag and no config means not declarative", func(t *testing.T) { - usePgDelta = false - t.Cleanup(func() { usePgDelta = false }) - assert.False(t, shouldUseDeclarativePgDeltaPull(false)) - }) - - t.Run("experimental config enables declarative", func(t *testing.T) { - usePgDelta = false - viper.Set("EXPERIMENTAL_PG_DELTA", true) - t.Cleanup(func() { - usePgDelta = false - viper.Set("EXPERIMENTAL_PG_DELTA", false) - }) - assert.True(t, shouldUseDeclarativePgDeltaPull(false)) - }) - - t.Run("use-pg-delta flag forces declarative", func(t *testing.T) { - usePgDelta = true - t.Cleanup(func() { usePgDelta = false }) - assert.True(t, shouldUseDeclarativePgDeltaPull(false)) - }) -} diff --git a/apps/cli-go/cmd/db_test.go b/apps/cli-go/cmd/db_test.go new file mode 100644 index 0000000000..654278d059 --- /dev/null +++ b/apps/cli-go/cmd/db_test.go @@ -0,0 +1,47 @@ +package cmd + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestResolvePullDiffEngine(t *testing.T) { + t.Run("defaults to pg-delta when enabled in config", func(t *testing.T) { + assert.True(t, resolvePullDiffEngine(false, "migra", true)) + }) + + t.Run("defaults to migra when pg-delta is not active", func(t *testing.T) { + assert.False(t, resolvePullDiffEngine(false, "migra", false)) + }) + + t.Run("explicit --diff-engine migra overrides config default", func(t *testing.T) { + assert.False(t, resolvePullDiffEngine(true, "migra", true)) + }) + + t.Run("explicit --diff-engine pg-delta wins when config disabled", func(t *testing.T) { + assert.True(t, resolvePullDiffEngine(true, "pg-delta", false)) + }) +} + +func TestResolveDiffEngine(t *testing.T) { + t.Run("uses pg-delta when enabled in config and no engine flag set", func(t *testing.T) { + assert.True(t, resolveDiffEngine(false, false, false, true)) + }) + + t.Run("uses migra when pg-delta is not active", func(t *testing.T) { + assert.False(t, resolveDiffEngine(false, false, false, false)) + }) + + t.Run("explicit --use-migra clears config-driven pg-delta", func(t *testing.T) { + assert.False(t, resolveDiffEngine(true, false, false, true)) + }) + + t.Run("explicit --use-pg-schema clears config-driven pg-delta", func(t *testing.T) { + assert.False(t, resolveDiffEngine(false, false, true, true)) + }) + + t.Run("explicit --use-pgadmin clears config-driven pg-delta", func(t *testing.T) { + assert.False(t, resolveDiffEngine(false, true, false, true)) + }) +} diff --git a/apps/cli-go/cmd/init.go b/apps/cli-go/cmd/init.go index 170d4dc867..0708a3b846 100644 --- a/apps/cli-go/cmd/init.go +++ b/apps/cli-go/cmd/init.go @@ -16,7 +16,7 @@ var ( initInteractive bool createVscodeSettings bool createIntellijSettings bool - initParams = utils.InitParams{} + initParams = utils.InitParams{UsePgDelta: true} initCmd = &cobra.Command{ GroupID: groupLocalDev, diff --git a/apps/cli-go/docs/supabase/db/diff.md b/apps/cli-go/docs/supabase/db/diff.md index a046707ec2..822c752485 100644 --- a/apps/cli-go/docs/supabase/db/diff.md +++ b/apps/cli-go/docs/supabase/db/diff.md @@ -8,6 +8,8 @@ Runs [djrobstep/migra](https://github.com/djrobstep/migra) in a container to com By default, all schemas in the target database are diffed. Use the `--schema public,extensions` flag to restrict diffing to a subset of schemas. +Projects created by a recent `supabase init` default to the pg-delta diff engine (`[experimental.pgdelta] enabled = true` in `config.toml`). Existing projects are unaffected and keep using migra unless they opt in. To fall back to the legacy migra engine, set `enabled = false` under `[experimental.pgdelta]`, or pass `--use-migra` for a single run. + While the diff command is able to capture most schema changes, there are cases where it is known to fail. Currently, this could happen if you schema contains: - Changes to publication diff --git a/apps/cli-go/docs/supabase/db/pull.md b/apps/cli-go/docs/supabase/db/pull.md index 93f964b9a9..ff35019715 100644 --- a/apps/cli-go/docs/supabase/db/pull.md +++ b/apps/cli-go/docs/supabase/db/pull.md @@ -10,9 +10,9 @@ Optionally, a new row can be inserted into the migration history table to reflec If no entries exist in the migration history table, the default diff engine uses `pg_dump` to capture all contents of the remote schemas you have created. Otherwise, this command will only diff schema changes against the remote database, similar to running `db diff --linked`. -Pass `--diff-engine pg-delta` to keep the migration-file `db pull` workflow while using pg-delta for the shadow diff step. On initial pull, pg-delta replaces `pg_dump` and produces the full migration from the shadow diff alone. Pass `--use-pg-delta` to switch to the declarative pg-delta export workflow instead. +Pass `--diff-engine pg-delta` to keep the migration-file `db pull` workflow while using pg-delta for the shadow diff step. On initial pull, pg-delta replaces `pg_dump` and produces the full migration from the shadow diff alone. Pass `--declarative` to switch to the declarative pg-delta export workflow instead. -When `[experimental.pgdelta] enabled = true` is set in `config.toml`, `db pull` defaults to the declarative export path. Explicit `--diff-engine pg-delta` still selects the migration-file workflow. +When `[experimental.pgdelta] enabled = true` (the default for projects created by a recent `supabase init`), the migration-file `db pull` workflow uses pg-delta for the shadow diff step by default; it does not switch to declarative output. Existing projects without the section are unaffected and keep using migra. To fall back to the legacy migra engine, set `enabled = false` under `[experimental.pgdelta]`, or pass `--diff-engine migra` for a single run. When pulling from a remote database with `--db-url`, prefer a direct connection (`db..supabase.co:5432`) over the connection pooler so pg-delta can introspect the full catalog reliably. diff --git a/apps/cli-go/internal/bootstrap/bootstrap.go b/apps/cli-go/internal/bootstrap/bootstrap.go index 8a07e25301..81fe310869 100644 --- a/apps/cli-go/internal/bootstrap/bootstrap.go +++ b/apps/cli-go/internal/bootstrap/bootstrap.go @@ -60,7 +60,7 @@ func Run(ctx context.Context, starter StarterTemplate, fsys afero.Fs, options .. if err := downloadSample(ctx, client, starter.Url, fsys); err != nil { return err } - } else if err := initBlank.Run(ctx, fsys, false, utils.InitParams{Overwrite: true}); err != nil { + } else if err := initBlank.Run(ctx, fsys, false, utils.InitParams{Overwrite: true, UsePgDelta: true}); err != nil { return err } // 1. Login diff --git a/apps/cli-go/internal/utils/config.go b/apps/cli-go/internal/utils/config.go index 108c3391a1..d171d06753 100644 --- a/apps/cli-go/internal/utils/config.go +++ b/apps/cli-go/internal/utils/config.go @@ -216,6 +216,7 @@ func ToRealtimeEnv(addr config.AddressFamily) string { type InitParams struct { ProjectId string UseOrioleDB bool + UsePgDelta bool Overwrite bool } @@ -225,6 +226,10 @@ func InitConfig(params InitParams, fsys afero.Fs) error { if params.UseOrioleDB { c.Experimental.OrioleDBVersion = "15.1.0.150" } + // The supabase init command opts new projects into pg-delta. Existing configs are + // unaffected because mergeDefaultValues ejects with this flag false (default stays + // migra), and other InitConfig callers leave it disabled. + c.Experimental.PgDeltaInitEnabled = params.UsePgDelta // Create config file if err := MkdirIfNotExistFS(fsys, SupabaseDirPath); err != nil { return err diff --git a/apps/cli-go/internal/utils/config_test.go b/apps/cli-go/internal/utils/config_test.go index 9ac835170f..6d829304f1 100644 --- a/apps/cli-go/internal/utils/config_test.go +++ b/apps/cli-go/internal/utils/config_test.go @@ -72,6 +72,35 @@ func TestInitConfig(t *testing.T) { assert.True(t, exists) }) + t.Run("generated config enables pgdelta when requested", func(t *testing.T) { + fsys := afero.NewMemMapFs() + params := InitParams{ + ProjectId: "test-project", + UsePgDelta: true, + } + + err := InitConfig(params, fsys) + + require.NoError(t, err) + content, err := afero.ReadFile(fsys, ConfigPath) + require.NoError(t, err) + assert.Contains(t, string(content), "[experimental.pgdelta]\nenabled = true") + }) + + t.Run("generated config leaves pgdelta disabled by default", func(t *testing.T) { + fsys := afero.NewMemMapFs() + params := InitParams{ + ProjectId: "test-project", + } + + err := InitConfig(params, fsys) + + require.NoError(t, err) + content, err := afero.ReadFile(fsys, ConfigPath) + require.NoError(t, err) + assert.Contains(t, string(content), "[experimental.pgdelta]\nenabled = false") + }) + t.Run("creates config with orioledb", func(t *testing.T) { fsys := afero.NewMemMapFs() params := InitParams{ diff --git a/apps/cli-go/pkg/config/config.go b/apps/cli-go/pkg/config/config.go index 61597d6806..dfd63999cd 100644 --- a/apps/cli-go/pkg/config/config.go +++ b/apps/cli-go/pkg/config/config.go @@ -253,6 +253,11 @@ type ( Webhooks *webhooks `toml:"webhooks" json:"webhooks"` PgDelta *PgDeltaConfig `toml:"pgdelta" json:"pgdelta"` Inspect inspect `toml:"inspect" json:"inspect"` + // PgDeltaInitEnabled drives the [experimental.pgdelta] enabled value rendered + // by Eject. It is true only for the supabase init scaffold so freshly generated + // projects opt into pg-delta, and false when Eject feeds mergeDefaultValues so + // existing configs without the section keep resolving to migra (non-breaking). + PgDeltaInitEnabled bool `toml:"-" json:"-"` } ) diff --git a/apps/cli-go/pkg/config/config_test.go b/apps/cli-go/pkg/config/config_test.go index 2a1a189076..c884a26229 100644 --- a/apps/cli-go/pkg/config/config_test.go +++ b/apps/cli-go/pkg/config/config_test.go @@ -242,6 +242,49 @@ format_options = "not-json" err := config.Load("", fsys) assert.ErrorContains(t, err, "experimental.pgdelta.format_options") }) + + t.Run("init scaffold opts into pgdelta", func(t *testing.T) { + config := NewConfig() + // supabase init renders the scaffold with the pg-delta opt-in flag set + config.Experimental.PgDeltaInitEnabled = true + var buf bytes.Buffer + require.NoError(t, config.Eject(&buf)) + fsys := fs.MapFS{"supabase/config.toml": &fs.MapFile{Data: buf.Bytes()}} + // Reloading the generated config resolves to pg-delta + require.NoError(t, config.Load("", fsys)) + require.NotNil(t, config.Experimental.PgDelta) + assert.True(t, config.Experimental.PgDelta.Enabled) + }) + + t.Run("absent pgdelta section falls back to migra", func(t *testing.T) { + config := NewConfig() + fsys := fs.MapFS{ + "supabase/config.toml": &fs.MapFile{Data: []byte(` +[experimental] +orioledb_version = "" +`)}, + } + + // The default ejected by mergeDefaultValues keeps pg-delta disabled, so a config + // without the section resolves to migra (PgDelta is non-nil only for version pinning). + require.NoError(t, config.Load("", fsys)) + require.NotNil(t, config.Experimental.PgDelta) + assert.False(t, config.Experimental.PgDelta.Enabled) + }) + + t.Run("explicit enabled false restores migra", func(t *testing.T) { + config := NewConfig() + fsys := fs.MapFS{ + "supabase/config.toml": &fs.MapFile{Data: []byte(` +[experimental.pgdelta] +enabled = false +`)}, + } + + require.NoError(t, config.Load("", fsys)) + require.NotNil(t, config.Experimental.PgDelta) + assert.False(t, config.Experimental.PgDelta.Enabled) + }) } func TestPgDeltaNpmVersionPinning(t *testing.T) { diff --git a/apps/cli-go/pkg/config/templates/config.toml b/apps/cli-go/pkg/config/templates/config.toml index ba3663e109..505811216a 100644 --- a/apps/cli-go/pkg/config/templates/config.toml +++ b/apps/cli-go/pkg/config/templates/config.toml @@ -404,9 +404,10 @@ s3_access_key = "env(S3_ACCESS_KEY)" # Configures AWS_SECRET_ACCESS_KEY for S3 bucket s3_secret_key = "env(S3_SECRET_KEY)" -# [experimental.pgdelta] -# When enabled, pg-delta becomes the active engine for supported schema flows. -# enabled = false +# pg-delta is the schema diff engine for db diff / db pull / db remote commit. +# Set enabled = false to fall back to the legacy migra engine. +[experimental.pgdelta] +enabled = {{ .Experimental.PgDeltaInitEnabled }} # Directory under `supabase/` where declarative files are written. # declarative_schema_path = "./database" # JSON string passed through to pg-delta SQL formatting. diff --git a/apps/cli/docs/go-cli-porting-status.md b/apps/cli/docs/go-cli-porting-status.md index 8bee396119..a77c0de262 100644 --- a/apps/cli/docs/go-cli-porting-status.md +++ b/apps/cli/docs/go-cli-porting-status.md @@ -211,108 +211,108 @@ Legend: - `wrapped`: Phase 0 proxy wrapper exists in the legacy shell - `missing`: no legacy shell command yet -| Command | Legacy status | Legacy command path | -| -------------------------------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `orgs list` | `ported` | [`../src/legacy/commands/orgs/list/list.command.ts`](../src/legacy/commands/orgs/list/list.command.ts) | -| `orgs create` | `ported` | [`../src/legacy/commands/orgs/create/create.command.ts`](../src/legacy/commands/orgs/create/create.command.ts) | -| `projects list` | `ported` | [`../src/legacy/commands/projects/list/list.command.ts`](../src/legacy/commands/projects/list/list.command.ts) | -| `projects create` | `ported` | [`../src/legacy/commands/projects/create/create.command.ts`](../src/legacy/commands/projects/create/create.command.ts) | -| `projects delete` | `ported` | [`../src/legacy/commands/projects/delete/delete.command.ts`](../src/legacy/commands/projects/delete/delete.command.ts) | -| `projects api-keys` | `ported` | [`../src/legacy/commands/projects/api-keys/api-keys.command.ts`](../src/legacy/commands/projects/api-keys/api-keys.command.ts) | -| `branches list` | `ported` | [`../src/legacy/commands/branches/list/list.command.ts`](../src/legacy/commands/branches/list/list.command.ts) | -| `branches create` | `ported` | [`../src/legacy/commands/branches/create/create.command.ts`](../src/legacy/commands/branches/create/create.command.ts) | -| `branches get` | `ported` | [`../src/legacy/commands/branches/get/get.command.ts`](../src/legacy/commands/branches/get/get.command.ts) | -| `branches update` | `ported` | [`../src/legacy/commands/branches/update/update.command.ts`](../src/legacy/commands/branches/update/update.command.ts) | -| `branches pause` | `ported` | [`../src/legacy/commands/branches/pause/pause.command.ts`](../src/legacy/commands/branches/pause/pause.command.ts) | -| `branches unpause` | `ported` | [`../src/legacy/commands/branches/unpause/unpause.command.ts`](../src/legacy/commands/branches/unpause/unpause.command.ts) | -| `branches delete` | `ported` | [`../src/legacy/commands/branches/delete/delete.command.ts`](../src/legacy/commands/branches/delete/delete.command.ts) | -| `branches disable` | `ported` | [`../src/legacy/commands/branches/disable/disable.command.ts`](../src/legacy/commands/branches/disable/disable.command.ts) | -| `secrets list` | `ported` | [`../src/legacy/commands/secrets/list/list.command.ts`](../src/legacy/commands/secrets/list/list.command.ts) | -| `secrets set` | `ported` | [`../src/legacy/commands/secrets/set/set.command.ts`](../src/legacy/commands/secrets/set/set.command.ts) | -| `secrets unset` | `ported` | [`../src/legacy/commands/secrets/unset/unset.command.ts`](../src/legacy/commands/secrets/unset/unset.command.ts) | -| `config push` | `ported` | [`../src/legacy/commands/config/push/push.command.ts`](../src/legacy/commands/config/push/push.command.ts) | -| `backups list` | `ported` | [`../src/legacy/commands/backups/list/list.command.ts`](../src/legacy/commands/backups/list/list.command.ts) | -| `backups restore` | `ported` | [`../src/legacy/commands/backups/restore/restore.command.ts`](../src/legacy/commands/backups/restore/restore.command.ts) | -| `snippets list` | `ported` | [`../src/legacy/commands/snippets/list/list.command.ts`](../src/legacy/commands/snippets/list/list.command.ts) | -| `snippets download` | `ported` | [`../src/legacy/commands/snippets/download/download.command.ts`](../src/legacy/commands/snippets/download/download.command.ts) | -| `sso list` | `ported` | [`../src/legacy/commands/sso/list/list.command.ts`](../src/legacy/commands/sso/list/list.command.ts) | -| `sso add` | `ported` | [`../src/legacy/commands/sso/add/add.command.ts`](../src/legacy/commands/sso/add/add.command.ts) | -| `sso remove` | `ported` | [`../src/legacy/commands/sso/remove/remove.command.ts`](../src/legacy/commands/sso/remove/remove.command.ts) | -| `sso update` | `ported` | [`../src/legacy/commands/sso/update/update.command.ts`](../src/legacy/commands/sso/update/update.command.ts) | -| `sso show` | `ported` | [`../src/legacy/commands/sso/show/show.command.ts`](../src/legacy/commands/sso/show/show.command.ts) | -| `sso info` | `ported` | [`../src/legacy/commands/sso/info/info.command.ts`](../src/legacy/commands/sso/info/info.command.ts) | -| `domains create` | `ported` | [`../src/legacy/commands/domains/create/create.command.ts`](../src/legacy/commands/domains/create/create.command.ts) | -| `domains get` | `ported` | [`../src/legacy/commands/domains/get/get.command.ts`](../src/legacy/commands/domains/get/get.command.ts) | -| `domains reverify` | `ported` | [`../src/legacy/commands/domains/reverify/reverify.command.ts`](../src/legacy/commands/domains/reverify/reverify.command.ts) | -| `domains activate` | `ported` | [`../src/legacy/commands/domains/activate/activate.command.ts`](../src/legacy/commands/domains/activate/activate.command.ts) | -| `domains delete` | `ported` | [`../src/legacy/commands/domains/delete/delete.command.ts`](../src/legacy/commands/domains/delete/delete.command.ts) | -| `vanity-subdomains get` | `ported` | [`../src/legacy/commands/vanity-subdomains/get/get.command.ts`](../src/legacy/commands/vanity-subdomains/get/get.command.ts) | -| `vanity-subdomains check-availability` | `ported` | [`../src/legacy/commands/vanity-subdomains/check-availability/check-availability.command.ts`](../src/legacy/commands/vanity-subdomains/check-availability/check-availability.command.ts) | -| `vanity-subdomains activate` | `ported` | [`../src/legacy/commands/vanity-subdomains/activate/activate.command.ts`](../src/legacy/commands/vanity-subdomains/activate/activate.command.ts) | -| `vanity-subdomains delete` | `ported` | [`../src/legacy/commands/vanity-subdomains/delete/delete.command.ts`](../src/legacy/commands/vanity-subdomains/delete/delete.command.ts) | -| `network-bans get` | `ported` | [`../src/legacy/commands/network-bans/get/get.command.ts`](../src/legacy/commands/network-bans/get/get.command.ts) | -| `network-bans remove` | `ported` | [`../src/legacy/commands/network-bans/remove/remove.command.ts`](../src/legacy/commands/network-bans/remove/remove.command.ts) | -| `network-restrictions get` | `ported` | [`../src/legacy/commands/network-restrictions/get/get.command.ts`](../src/legacy/commands/network-restrictions/get/get.command.ts) | -| `network-restrictions update` | `ported` | [`../src/legacy/commands/network-restrictions/update/update.command.ts`](../src/legacy/commands/network-restrictions/update/update.command.ts) | -| `encryption get-root-key` | `ported` | [`../src/legacy/commands/encryption/get-root-key/get-root-key.command.ts`](../src/legacy/commands/encryption/get-root-key/get-root-key.command.ts) | -| `encryption update-root-key` | `ported` | [`../src/legacy/commands/encryption/update-root-key/update-root-key.command.ts`](../src/legacy/commands/encryption/update-root-key/update-root-key.command.ts) | -| `ssl-enforcement get` | `ported` | [`../src/legacy/commands/ssl-enforcement/get/get.command.ts`](../src/legacy/commands/ssl-enforcement/get/get.command.ts) | -| `ssl-enforcement update` | `ported` | [`../src/legacy/commands/ssl-enforcement/update/update.command.ts`](../src/legacy/commands/ssl-enforcement/update/update.command.ts) | -| `postgres-config get` | `ported` | [`../src/legacy/commands/postgres-config/get/get.command.ts`](../src/legacy/commands/postgres-config/get/get.command.ts) | -| `postgres-config update` | `ported` | [`../src/legacy/commands/postgres-config/update/update.command.ts`](../src/legacy/commands/postgres-config/update/update.command.ts) | -| `postgres-config delete` | `ported` | [`../src/legacy/commands/postgres-config/delete/delete.command.ts`](../src/legacy/commands/postgres-config/delete/delete.command.ts) | -| `login` | `ported` | [`../src/legacy/commands/login/login.command.ts`](../src/legacy/commands/login/login.command.ts) | -| `logout` | `ported` | [`../src/legacy/commands/logout/logout.command.ts`](../src/legacy/commands/logout/logout.command.ts) | -| `link` | `ported` | [`../src/legacy/commands/link/link.command.ts`](../src/legacy/commands/link/link.command.ts) | -| `unlink` | `ported` | [`../src/legacy/commands/unlink/unlink.command.ts`](../src/legacy/commands/unlink/unlink.command.ts) | -| `bootstrap` | `ported` | [`../src/legacy/commands/bootstrap/bootstrap.command.ts`](../src/legacy/commands/bootstrap/bootstrap.command.ts) (native; `db push` step delegated to the Go binary — interim) | -| `init` | `ported` | [`../src/legacy/commands/init/init.command.ts`](../src/legacy/commands/init/init.command.ts) | -| `services` | `ported` | [`../src/legacy/commands/services/services.command.ts`](../src/legacy/commands/services/services.command.ts) | -| `start` | `wrapped` | [`../src/legacy/commands/start/start.command.ts`](../src/legacy/commands/start/start.command.ts) | -| `stop` | `wrapped` | [`../src/legacy/commands/stop/stop.command.ts`](../src/legacy/commands/stop/stop.command.ts) | -| `status` | `wrapped` | [`../src/legacy/commands/status/status.command.ts`](../src/legacy/commands/status/status.command.ts) | -| `telemetry enable` | `ported` | [`../src/legacy/commands/telemetry/enable/enable.command.ts`](../src/legacy/commands/telemetry/enable/enable.command.ts) | -| `telemetry disable` | `ported` | [`../src/legacy/commands/telemetry/disable/disable.command.ts`](../src/legacy/commands/telemetry/disable/disable.command.ts) | -| `telemetry status` | `ported` | [`../src/legacy/commands/telemetry/status/status.command.ts`](../src/legacy/commands/telemetry/status/status.command.ts) | -| `migration list` | `wrapped` | [`../src/legacy/commands/migration/list/list.command.ts`](../src/legacy/commands/migration/list/list.command.ts) | -| `migration new` | `wrapped` | [`../src/legacy/commands/migration/new/new.command.ts`](../src/legacy/commands/migration/new/new.command.ts) | -| `migration repair` | `wrapped` | [`../src/legacy/commands/migration/repair/repair.command.ts`](../src/legacy/commands/migration/repair/repair.command.ts) | -| `migration squash` | `wrapped` | [`../src/legacy/commands/migration/squash/squash.command.ts`](../src/legacy/commands/migration/squash/squash.command.ts) | -| `migration up` | `wrapped` | [`../src/legacy/commands/migration/up/up.command.ts`](../src/legacy/commands/migration/up/up.command.ts) | -| `migration down` | `wrapped` | [`../src/legacy/commands/migration/down/down.command.ts`](../src/legacy/commands/migration/down/down.command.ts) | -| `migration fetch` | `wrapped` | [`../src/legacy/commands/migration/fetch/fetch.command.ts`](../src/legacy/commands/migration/fetch/fetch.command.ts) | -| `gen types` | `ported` | [`../src/legacy/commands/gen/types/types.command.ts`](../src/legacy/commands/gen/types/types.command.ts) | -| `gen signing-key` | `ported` | [`../src/legacy/commands/gen/signing-key/signing-key.command.ts`](../src/legacy/commands/gen/signing-key/signing-key.command.ts) | -| `gen bearer-jwt` | `wrapped` | [`../src/legacy/commands/gen/bearer-jwt/bearer-jwt.command.ts`](../src/legacy/commands/gen/bearer-jwt/bearer-jwt.command.ts) | -| `gen keys` | `wrapped` | [`../src/legacy/commands/gen/keys/keys.command.ts`](../src/legacy/commands/gen/keys/keys.command.ts) | -| `functions list` | `wrapped` | [`../src/legacy/commands/functions/list/list.command.ts`](../src/legacy/commands/functions/list/list.command.ts) | -| `functions delete` | `ported` | [`../src/legacy/commands/functions/delete/delete.command.ts`](../src/legacy/commands/functions/delete/delete.command.ts) | -| `functions download` | `ported` | [`../src/legacy/commands/functions/download/download.command.ts`](../src/legacy/commands/functions/download/download.command.ts) | -| `functions deploy` | `wrapped` | [`../src/legacy/commands/functions/deploy/deploy.command.ts`](../src/legacy/commands/functions/deploy/deploy.command.ts) | -| `functions new` | `wrapped` | [`../src/legacy/commands/functions/new/new.command.ts`](../src/legacy/commands/functions/new/new.command.ts) | -| `functions serve` | `wrapped` | [`../src/legacy/commands/functions/serve/serve.command.ts`](../src/legacy/commands/functions/serve/serve.command.ts) | -| `storage ls` | `wrapped` | [`../src/legacy/commands/storage/ls/ls.command.ts`](../src/legacy/commands/storage/ls/ls.command.ts) | -| `storage cp` | `wrapped` | [`../src/legacy/commands/storage/cp/cp.command.ts`](../src/legacy/commands/storage/cp/cp.command.ts) | -| `storage mv` | `wrapped` | [`../src/legacy/commands/storage/mv/mv.command.ts`](../src/legacy/commands/storage/mv/mv.command.ts) | -| `storage rm` | `wrapped` | [`../src/legacy/commands/storage/rm/rm.command.ts`](../src/legacy/commands/storage/rm/rm.command.ts) | -| `test db` | `ported` | [`../src/legacy/commands/test/db/db.command.ts`](../src/legacy/commands/test/db/db.command.ts) | -| `test new` | `ported` | [`../src/legacy/commands/test/new/new.command.ts`](../src/legacy/commands/test/new/new.command.ts) | -| `seed buckets` | `wrapped` | [`../src/legacy/commands/seed/buckets/buckets.command.ts`](../src/legacy/commands/seed/buckets/buckets.command.ts) | -| `db diff` | `wrapped` | [`../src/legacy/commands/db/diff/diff.command.ts`](../src/legacy/commands/db/diff/diff.command.ts) | -| `db dump` | `wrapped` | [`../src/legacy/commands/db/dump/dump.command.ts`](../src/legacy/commands/db/dump/dump.command.ts) | -| `db push` | `wrapped` | [`../src/legacy/commands/db/push/push.command.ts`](../src/legacy/commands/db/push/push.command.ts) | -| `db pull` | `wrapped` | [`../src/legacy/commands/db/pull/pull.command.ts`](../src/legacy/commands/db/pull/pull.command.ts) — includes `--diff-engine` (migra\|pg-delta, mutually exclusive with `--use-pg-delta`) | -| `db reset` | `wrapped` | [`../src/legacy/commands/db/reset/reset.command.ts`](../src/legacy/commands/db/reset/reset.command.ts) | -| `db lint` | `wrapped` | [`../src/legacy/commands/db/lint/lint.command.ts`](../src/legacy/commands/db/lint/lint.command.ts) | -| `db start` | `wrapped` | [`../src/legacy/commands/db/start/start.command.ts`](../src/legacy/commands/db/start/start.command.ts) | -| `db query` | `wrapped` | [`../src/legacy/commands/db/query/query.command.ts`](../src/legacy/commands/db/query/query.command.ts) | -| `db advisors` | `wrapped` | [`../src/legacy/commands/db/advisors/advisors.command.ts`](../src/legacy/commands/db/advisors/advisors.command.ts) | -| `db test` | `wrapped` | [`../src/legacy/commands/db/test/test.command.ts`](../src/legacy/commands/db/test/test.command.ts) | -| `db branch create` | `wrapped` | [`../src/legacy/commands/db/branch/create/create.command.ts`](../src/legacy/commands/db/branch/create/create.command.ts) | -| `db branch delete` | `wrapped` | [`../src/legacy/commands/db/branch/delete/delete.command.ts`](../src/legacy/commands/db/branch/delete/delete.command.ts) | -| `db branch list` | `wrapped` | [`../src/legacy/commands/db/branch/list/list.command.ts`](../src/legacy/commands/db/branch/list/list.command.ts) | -| `db branch switch` | `wrapped` | [`../src/legacy/commands/db/branch/switch/switch.command.ts`](../src/legacy/commands/db/branch/switch/switch.command.ts) | -| `db remote changes` | `wrapped` | [`../src/legacy/commands/db/remote/changes/changes.command.ts`](../src/legacy/commands/db/remote/changes/changes.command.ts) | -| `db remote commit` | `wrapped` | [`../src/legacy/commands/db/remote/commit/commit.command.ts`](../src/legacy/commands/db/remote/commit/commit.command.ts) | -| `db schema declarative sync` | `wrapped` | [`../src/legacy/commands/db/schema/declarative/sync/sync.command.ts`](../src/legacy/commands/db/schema/declarative/sync/sync.command.ts) | -| `db schema declarative generate` | `wrapped` | [`../src/legacy/commands/db/schema/declarative/generate/generate.command.ts`](../src/legacy/commands/db/schema/declarative/generate/generate.command.ts) | +| Command | Legacy status | Legacy command path | +| -------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `orgs list` | `ported` | [`../src/legacy/commands/orgs/list/list.command.ts`](../src/legacy/commands/orgs/list/list.command.ts) | +| `orgs create` | `ported` | [`../src/legacy/commands/orgs/create/create.command.ts`](../src/legacy/commands/orgs/create/create.command.ts) | +| `projects list` | `ported` | [`../src/legacy/commands/projects/list/list.command.ts`](../src/legacy/commands/projects/list/list.command.ts) | +| `projects create` | `ported` | [`../src/legacy/commands/projects/create/create.command.ts`](../src/legacy/commands/projects/create/create.command.ts) | +| `projects delete` | `ported` | [`../src/legacy/commands/projects/delete/delete.command.ts`](../src/legacy/commands/projects/delete/delete.command.ts) | +| `projects api-keys` | `ported` | [`../src/legacy/commands/projects/api-keys/api-keys.command.ts`](../src/legacy/commands/projects/api-keys/api-keys.command.ts) | +| `branches list` | `ported` | [`../src/legacy/commands/branches/list/list.command.ts`](../src/legacy/commands/branches/list/list.command.ts) | +| `branches create` | `ported` | [`../src/legacy/commands/branches/create/create.command.ts`](../src/legacy/commands/branches/create/create.command.ts) | +| `branches get` | `ported` | [`../src/legacy/commands/branches/get/get.command.ts`](../src/legacy/commands/branches/get/get.command.ts) | +| `branches update` | `ported` | [`../src/legacy/commands/branches/update/update.command.ts`](../src/legacy/commands/branches/update/update.command.ts) | +| `branches pause` | `ported` | [`../src/legacy/commands/branches/pause/pause.command.ts`](../src/legacy/commands/branches/pause/pause.command.ts) | +| `branches unpause` | `ported` | [`../src/legacy/commands/branches/unpause/unpause.command.ts`](../src/legacy/commands/branches/unpause/unpause.command.ts) | +| `branches delete` | `ported` | [`../src/legacy/commands/branches/delete/delete.command.ts`](../src/legacy/commands/branches/delete/delete.command.ts) | +| `branches disable` | `ported` | [`../src/legacy/commands/branches/disable/disable.command.ts`](../src/legacy/commands/branches/disable/disable.command.ts) | +| `secrets list` | `ported` | [`../src/legacy/commands/secrets/list/list.command.ts`](../src/legacy/commands/secrets/list/list.command.ts) | +| `secrets set` | `ported` | [`../src/legacy/commands/secrets/set/set.command.ts`](../src/legacy/commands/secrets/set/set.command.ts) | +| `secrets unset` | `ported` | [`../src/legacy/commands/secrets/unset/unset.command.ts`](../src/legacy/commands/secrets/unset/unset.command.ts) | +| `config push` | `ported` | [`../src/legacy/commands/config/push/push.command.ts`](../src/legacy/commands/config/push/push.command.ts) | +| `backups list` | `ported` | [`../src/legacy/commands/backups/list/list.command.ts`](../src/legacy/commands/backups/list/list.command.ts) | +| `backups restore` | `ported` | [`../src/legacy/commands/backups/restore/restore.command.ts`](../src/legacy/commands/backups/restore/restore.command.ts) | +| `snippets list` | `ported` | [`../src/legacy/commands/snippets/list/list.command.ts`](../src/legacy/commands/snippets/list/list.command.ts) | +| `snippets download` | `ported` | [`../src/legacy/commands/snippets/download/download.command.ts`](../src/legacy/commands/snippets/download/download.command.ts) | +| `sso list` | `ported` | [`../src/legacy/commands/sso/list/list.command.ts`](../src/legacy/commands/sso/list/list.command.ts) | +| `sso add` | `ported` | [`../src/legacy/commands/sso/add/add.command.ts`](../src/legacy/commands/sso/add/add.command.ts) | +| `sso remove` | `ported` | [`../src/legacy/commands/sso/remove/remove.command.ts`](../src/legacy/commands/sso/remove/remove.command.ts) | +| `sso update` | `ported` | [`../src/legacy/commands/sso/update/update.command.ts`](../src/legacy/commands/sso/update/update.command.ts) | +| `sso show` | `ported` | [`../src/legacy/commands/sso/show/show.command.ts`](../src/legacy/commands/sso/show/show.command.ts) | +| `sso info` | `ported` | [`../src/legacy/commands/sso/info/info.command.ts`](../src/legacy/commands/sso/info/info.command.ts) | +| `domains create` | `ported` | [`../src/legacy/commands/domains/create/create.command.ts`](../src/legacy/commands/domains/create/create.command.ts) | +| `domains get` | `ported` | [`../src/legacy/commands/domains/get/get.command.ts`](../src/legacy/commands/domains/get/get.command.ts) | +| `domains reverify` | `ported` | [`../src/legacy/commands/domains/reverify/reverify.command.ts`](../src/legacy/commands/domains/reverify/reverify.command.ts) | +| `domains activate` | `ported` | [`../src/legacy/commands/domains/activate/activate.command.ts`](../src/legacy/commands/domains/activate/activate.command.ts) | +| `domains delete` | `ported` | [`../src/legacy/commands/domains/delete/delete.command.ts`](../src/legacy/commands/domains/delete/delete.command.ts) | +| `vanity-subdomains get` | `ported` | [`../src/legacy/commands/vanity-subdomains/get/get.command.ts`](../src/legacy/commands/vanity-subdomains/get/get.command.ts) | +| `vanity-subdomains check-availability` | `ported` | [`../src/legacy/commands/vanity-subdomains/check-availability/check-availability.command.ts`](../src/legacy/commands/vanity-subdomains/check-availability/check-availability.command.ts) | +| `vanity-subdomains activate` | `ported` | [`../src/legacy/commands/vanity-subdomains/activate/activate.command.ts`](../src/legacy/commands/vanity-subdomains/activate/activate.command.ts) | +| `vanity-subdomains delete` | `ported` | [`../src/legacy/commands/vanity-subdomains/delete/delete.command.ts`](../src/legacy/commands/vanity-subdomains/delete/delete.command.ts) | +| `network-bans get` | `ported` | [`../src/legacy/commands/network-bans/get/get.command.ts`](../src/legacy/commands/network-bans/get/get.command.ts) | +| `network-bans remove` | `ported` | [`../src/legacy/commands/network-bans/remove/remove.command.ts`](../src/legacy/commands/network-bans/remove/remove.command.ts) | +| `network-restrictions get` | `ported` | [`../src/legacy/commands/network-restrictions/get/get.command.ts`](../src/legacy/commands/network-restrictions/get/get.command.ts) | +| `network-restrictions update` | `ported` | [`../src/legacy/commands/network-restrictions/update/update.command.ts`](../src/legacy/commands/network-restrictions/update/update.command.ts) | +| `encryption get-root-key` | `ported` | [`../src/legacy/commands/encryption/get-root-key/get-root-key.command.ts`](../src/legacy/commands/encryption/get-root-key/get-root-key.command.ts) | +| `encryption update-root-key` | `ported` | [`../src/legacy/commands/encryption/update-root-key/update-root-key.command.ts`](../src/legacy/commands/encryption/update-root-key/update-root-key.command.ts) | +| `ssl-enforcement get` | `ported` | [`../src/legacy/commands/ssl-enforcement/get/get.command.ts`](../src/legacy/commands/ssl-enforcement/get/get.command.ts) | +| `ssl-enforcement update` | `ported` | [`../src/legacy/commands/ssl-enforcement/update/update.command.ts`](../src/legacy/commands/ssl-enforcement/update/update.command.ts) | +| `postgres-config get` | `ported` | [`../src/legacy/commands/postgres-config/get/get.command.ts`](../src/legacy/commands/postgres-config/get/get.command.ts) | +| `postgres-config update` | `ported` | [`../src/legacy/commands/postgres-config/update/update.command.ts`](../src/legacy/commands/postgres-config/update/update.command.ts) | +| `postgres-config delete` | `ported` | [`../src/legacy/commands/postgres-config/delete/delete.command.ts`](../src/legacy/commands/postgres-config/delete/delete.command.ts) | +| `login` | `ported` | [`../src/legacy/commands/login/login.command.ts`](../src/legacy/commands/login/login.command.ts) | +| `logout` | `ported` | [`../src/legacy/commands/logout/logout.command.ts`](../src/legacy/commands/logout/logout.command.ts) | +| `link` | `ported` | [`../src/legacy/commands/link/link.command.ts`](../src/legacy/commands/link/link.command.ts) | +| `unlink` | `ported` | [`../src/legacy/commands/unlink/unlink.command.ts`](../src/legacy/commands/unlink/unlink.command.ts) | +| `bootstrap` | `ported` | [`../src/legacy/commands/bootstrap/bootstrap.command.ts`](../src/legacy/commands/bootstrap/bootstrap.command.ts) (native; `db push` step delegated to the Go binary — interim) | +| `init` | `ported` | [`../src/legacy/commands/init/init.command.ts`](../src/legacy/commands/init/init.command.ts) | +| `services` | `ported` | [`../src/legacy/commands/services/services.command.ts`](../src/legacy/commands/services/services.command.ts) | +| `start` | `wrapped` | [`../src/legacy/commands/start/start.command.ts`](../src/legacy/commands/start/start.command.ts) | +| `stop` | `wrapped` | [`../src/legacy/commands/stop/stop.command.ts`](../src/legacy/commands/stop/stop.command.ts) | +| `status` | `wrapped` | [`../src/legacy/commands/status/status.command.ts`](../src/legacy/commands/status/status.command.ts) | +| `telemetry enable` | `ported` | [`../src/legacy/commands/telemetry/enable/enable.command.ts`](../src/legacy/commands/telemetry/enable/enable.command.ts) | +| `telemetry disable` | `ported` | [`../src/legacy/commands/telemetry/disable/disable.command.ts`](../src/legacy/commands/telemetry/disable/disable.command.ts) | +| `telemetry status` | `ported` | [`../src/legacy/commands/telemetry/status/status.command.ts`](../src/legacy/commands/telemetry/status/status.command.ts) | +| `migration list` | `wrapped` | [`../src/legacy/commands/migration/list/list.command.ts`](../src/legacy/commands/migration/list/list.command.ts) | +| `migration new` | `wrapped` | [`../src/legacy/commands/migration/new/new.command.ts`](../src/legacy/commands/migration/new/new.command.ts) | +| `migration repair` | `wrapped` | [`../src/legacy/commands/migration/repair/repair.command.ts`](../src/legacy/commands/migration/repair/repair.command.ts) | +| `migration squash` | `wrapped` | [`../src/legacy/commands/migration/squash/squash.command.ts`](../src/legacy/commands/migration/squash/squash.command.ts) | +| `migration up` | `wrapped` | [`../src/legacy/commands/migration/up/up.command.ts`](../src/legacy/commands/migration/up/up.command.ts) | +| `migration down` | `wrapped` | [`../src/legacy/commands/migration/down/down.command.ts`](../src/legacy/commands/migration/down/down.command.ts) | +| `migration fetch` | `wrapped` | [`../src/legacy/commands/migration/fetch/fetch.command.ts`](../src/legacy/commands/migration/fetch/fetch.command.ts) | +| `gen types` | `ported` | [`../src/legacy/commands/gen/types/types.command.ts`](../src/legacy/commands/gen/types/types.command.ts) | +| `gen signing-key` | `ported` | [`../src/legacy/commands/gen/signing-key/signing-key.command.ts`](../src/legacy/commands/gen/signing-key/signing-key.command.ts) | +| `gen bearer-jwt` | `wrapped` | [`../src/legacy/commands/gen/bearer-jwt/bearer-jwt.command.ts`](../src/legacy/commands/gen/bearer-jwt/bearer-jwt.command.ts) | +| `gen keys` | `wrapped` | [`../src/legacy/commands/gen/keys/keys.command.ts`](../src/legacy/commands/gen/keys/keys.command.ts) | +| `functions list` | `wrapped` | [`../src/legacy/commands/functions/list/list.command.ts`](../src/legacy/commands/functions/list/list.command.ts) | +| `functions delete` | `ported` | [`../src/legacy/commands/functions/delete/delete.command.ts`](../src/legacy/commands/functions/delete/delete.command.ts) | +| `functions download` | `ported` | [`../src/legacy/commands/functions/download/download.command.ts`](../src/legacy/commands/functions/download/download.command.ts) | +| `functions deploy` | `wrapped` | [`../src/legacy/commands/functions/deploy/deploy.command.ts`](../src/legacy/commands/functions/deploy/deploy.command.ts) | +| `functions new` | `wrapped` | [`../src/legacy/commands/functions/new/new.command.ts`](../src/legacy/commands/functions/new/new.command.ts) | +| `functions serve` | `wrapped` | [`../src/legacy/commands/functions/serve/serve.command.ts`](../src/legacy/commands/functions/serve/serve.command.ts) | +| `storage ls` | `wrapped` | [`../src/legacy/commands/storage/ls/ls.command.ts`](../src/legacy/commands/storage/ls/ls.command.ts) | +| `storage cp` | `wrapped` | [`../src/legacy/commands/storage/cp/cp.command.ts`](../src/legacy/commands/storage/cp/cp.command.ts) | +| `storage mv` | `wrapped` | [`../src/legacy/commands/storage/mv/mv.command.ts`](../src/legacy/commands/storage/mv/mv.command.ts) | +| `storage rm` | `wrapped` | [`../src/legacy/commands/storage/rm/rm.command.ts`](../src/legacy/commands/storage/rm/rm.command.ts) | +| `test db` | `ported` | [`../src/legacy/commands/test/db/db.command.ts`](../src/legacy/commands/test/db/db.command.ts) | +| `test new` | `ported` | [`../src/legacy/commands/test/new/new.command.ts`](../src/legacy/commands/test/new/new.command.ts) | +| `seed buckets` | `wrapped` | [`../src/legacy/commands/seed/buckets/buckets.command.ts`](../src/legacy/commands/seed/buckets/buckets.command.ts) | +| `db diff` | `wrapped` | [`../src/legacy/commands/db/diff/diff.command.ts`](../src/legacy/commands/db/diff/diff.command.ts) | +| `db dump` | `wrapped` | [`../src/legacy/commands/db/dump/dump.command.ts`](../src/legacy/commands/db/dump/dump.command.ts) | +| `db push` | `wrapped` | [`../src/legacy/commands/db/push/push.command.ts`](../src/legacy/commands/db/push/push.command.ts) | +| `db pull` | `wrapped` | [`../src/legacy/commands/db/pull/pull.command.ts`](../src/legacy/commands/db/pull/pull.command.ts) — includes `--declarative` (deprecated alias `--use-pg-delta`) and `--diff-engine` (migra\|pg-delta, mutually exclusive with `--declarative`) | +| `db reset` | `wrapped` | [`../src/legacy/commands/db/reset/reset.command.ts`](../src/legacy/commands/db/reset/reset.command.ts) | +| `db lint` | `wrapped` | [`../src/legacy/commands/db/lint/lint.command.ts`](../src/legacy/commands/db/lint/lint.command.ts) | +| `db start` | `wrapped` | [`../src/legacy/commands/db/start/start.command.ts`](../src/legacy/commands/db/start/start.command.ts) | +| `db query` | `wrapped` | [`../src/legacy/commands/db/query/query.command.ts`](../src/legacy/commands/db/query/query.command.ts) | +| `db advisors` | `wrapped` | [`../src/legacy/commands/db/advisors/advisors.command.ts`](../src/legacy/commands/db/advisors/advisors.command.ts) | +| `db test` | `wrapped` | [`../src/legacy/commands/db/test/test.command.ts`](../src/legacy/commands/db/test/test.command.ts) | +| `db branch create` | `wrapped` | [`../src/legacy/commands/db/branch/create/create.command.ts`](../src/legacy/commands/db/branch/create/create.command.ts) | +| `db branch delete` | `wrapped` | [`../src/legacy/commands/db/branch/delete/delete.command.ts`](../src/legacy/commands/db/branch/delete/delete.command.ts) | +| `db branch list` | `wrapped` | [`../src/legacy/commands/db/branch/list/list.command.ts`](../src/legacy/commands/db/branch/list/list.command.ts) | +| `db branch switch` | `wrapped` | [`../src/legacy/commands/db/branch/switch/switch.command.ts`](../src/legacy/commands/db/branch/switch/switch.command.ts) | +| `db remote changes` | `wrapped` | [`../src/legacy/commands/db/remote/changes/changes.command.ts`](../src/legacy/commands/db/remote/changes/changes.command.ts) | +| `db remote commit` | `wrapped` | [`../src/legacy/commands/db/remote/commit/commit.command.ts`](../src/legacy/commands/db/remote/commit/commit.command.ts) | +| `db schema declarative sync` | `wrapped` | [`../src/legacy/commands/db/schema/declarative/sync/sync.command.ts`](../src/legacy/commands/db/schema/declarative/sync/sync.command.ts) | +| `db schema declarative generate` | `wrapped` | [`../src/legacy/commands/db/schema/declarative/generate/generate.command.ts`](../src/legacy/commands/db/schema/declarative/generate/generate.command.ts) | diff --git a/apps/cli/src/legacy/commands/db/pull/SIDE_EFFECTS.md b/apps/cli/src/legacy/commands/db/pull/SIDE_EFFECTS.md index 323ecedd05..f027cb07d8 100644 --- a/apps/cli/src/legacy/commands/db/pull/SIDE_EFFECTS.md +++ b/apps/cli/src/legacy/commands/db/pull/SIDE_EFFECTS.md @@ -52,5 +52,5 @@ Not applicable. - Optional positional argument sets the migration name (defaults to `remote_schema`). - `--schema` / `-s` restricts pull to specific schemas. - `--db-url`, `--linked` (default true), and `--local` are mutually exclusive. -- `--use-pg-delta` activates declarative pull output through pg-delta. -- `--diff-engine migra|pg-delta` selects the diff engine for migration-style pull; mutually exclusive with `--use-pg-delta`. +- `--declarative` activates declarative pull output through pg-delta (writes `./database` files instead of a migration). `--use-pg-delta` is a deprecated alias. +- `--diff-engine migra|pg-delta` selects the diff engine for migration-style pull; mutually exclusive with `--declarative` / `--use-pg-delta`. When the flag is omitted, the engine defaults to pg-delta if `[experimental.pgdelta] enabled = true` in `config.toml` (or `EXPERIMENTAL_PG_DELTA`), otherwise migra. An explicit `--diff-engine migra` always forces migra. Enabling pg-delta in config does not switch `db pull` to declarative output. diff --git a/apps/cli/src/legacy/commands/db/pull/pull.command.ts b/apps/cli/src/legacy/commands/db/pull/pull.command.ts index 16e2ad511e..ba773ecbe8 100644 --- a/apps/cli/src/legacy/commands/db/pull/pull.command.ts +++ b/apps/cli/src/legacy/commands/db/pull/pull.command.ts @@ -7,8 +7,15 @@ const config = { Argument.withDescription("Optional name for the migration file."), Argument.optional, ), + declarative: Flag.boolean("declarative").pipe( + Flag.withDescription( + "Pull schema as declarative files using pg-delta instead of creating a migration.", + ), + ), usePgDelta: Flag.boolean("use-pg-delta").pipe( - Flag.withDescription("Use pg-delta to pull declarative schema."), + Flag.withDescription( + "Deprecated alias for --declarative. Use --declarative with [experimental.pgdelta] enabled = true in your config.toml instead.", + ), ), diffEngine: Flag.choice("diff-engine", ["migra", "pg-delta"] as const).pipe( Flag.withDescription("Diff engine to use for migration-style db pull."), diff --git a/apps/cli/src/legacy/commands/db/pull/pull.handler.ts b/apps/cli/src/legacy/commands/db/pull/pull.handler.ts index c1ab9564c6..01a925035d 100644 --- a/apps/cli/src/legacy/commands/db/pull/pull.handler.ts +++ b/apps/cli/src/legacy/commands/db/pull/pull.handler.ts @@ -6,6 +6,7 @@ export const legacyDbPull = Effect.fn("legacy.db.pull")(function* (flags: Legacy const proxy = yield* LegacyGoProxy; const args: string[] = ["db", "pull"]; if (Option.isSome(flags.name)) args.push(flags.name.value); + if (flags.declarative) args.push("--declarative"); if (flags.usePgDelta) args.push("--use-pg-delta"); if (Option.isSome(flags.diffEngine)) args.push("--diff-engine", flags.diffEngine.value); for (const s of flags.schema) { diff --git a/apps/cli/src/shared/init/project-init.templates.ts b/apps/cli/src/shared/init/project-init.templates.ts index eb2a01a10f..6697a34bf5 100644 --- a/apps/cli/src/shared/init/project-init.templates.ts +++ b/apps/cli/src/shared/init/project-init.templates.ts @@ -404,9 +404,10 @@ s3_access_key = "env(S3_ACCESS_KEY)" # Configures AWS_SECRET_ACCESS_KEY for S3 bucket s3_secret_key = "env(S3_SECRET_KEY)" -# [experimental.pgdelta] -# When enabled, pg-delta becomes the active engine for supported schema flows. -# enabled = false +# pg-delta is the schema diff engine for db diff / db pull / db remote commit. +# Set enabled = false to fall back to the legacy migra engine. +[experimental.pgdelta] +enabled = true # Directory under \`supabase/\` where declarative files are written. # declarative_schema_path = "./database" # JSON string passed through to pg-delta SQL formatting. diff --git a/apps/cli/src/shared/init/project-init.templates.unit.test.ts b/apps/cli/src/shared/init/project-init.templates.unit.test.ts index 163acf3b5b..7b58bf1aea 100644 --- a/apps/cli/src/shared/init/project-init.templates.unit.test.ts +++ b/apps/cli/src/shared/init/project-init.templates.unit.test.ts @@ -25,11 +25,19 @@ describe("project init templates", () => { it("renders config.toml with the same content as the Go scaffold", () => { const expected = readGoTemplate("pkg", "config", "templates", "config.toml") .replace("{{ .ProjectId }}", "demo-project") - .replace("{{ .Experimental.OrioleDBVersion }}", "15.1.0.150"); + .replace("{{ .Experimental.OrioleDBVersion }}", "15.1.0.150") + // supabase init always opts new projects into pg-delta; the Go template renders + // this from a flag set only on the init path (false when deriving defaults). + .replace("{{ .Experimental.PgDeltaInitEnabled }}", "true"); expect(normalizeNewlines(renderProjectConfigTemplate("demo-project", true))).toBe(expected); }); + it("enables pg-delta by default in the generated config", () => { + const rendered = renderProjectConfigTemplate("demo-project", false); + expect(rendered).toContain("[experimental.pgdelta]\nenabled = true"); + }); + it("matches the Go .gitignore scaffold", () => { expect(INIT_GITIGNORE_TEMPLATE).toBe( readGoTemplate("internal", "init", "templates", ".gitignore"), diff --git a/apps/docs/public/cli/config.schema.json b/apps/docs/public/cli/config.schema.json index e3f8bf1ae5..3977893a6e 100644 --- a/apps/docs/public/cli/config.schema.json +++ b/apps/docs/public/cli/config.schema.json @@ -3475,6 +3475,31 @@ }, "additionalProperties": false }, + "pgdelta": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Use pg-delta as the schema diff engine for db diff / db pull / db remote commit. Set false to fall back to the legacy migra engine.", + "default": false + }, + "declarative_schema_path": { + "type": "string", + "description": "Directory under supabase/ where declarative schema files are written.", + "examples": [ + "./database" + ] + }, + "format_options": { + "type": "string", + "description": "JSON string passed through to pg-delta SQL formatting.", + "examples": [ + "{\"keywordCase\":\"upper\",\"indent\":2,\"maxWidth\":80,\"commaStyle\":\"trailing\"}" + ] + } + }, + "additionalProperties": false + }, "inspect": { "type": "object", "properties": { @@ -6995,6 +7020,31 @@ }, "additionalProperties": false }, + "pgdelta": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Use pg-delta as the schema diff engine for db diff / db pull / db remote commit. Set false to fall back to the legacy migra engine.", + "default": false + }, + "declarative_schema_path": { + "type": "string", + "description": "Directory under supabase/ where declarative schema files are written.", + "examples": [ + "./database" + ] + }, + "format_options": { + "type": "string", + "description": "JSON string passed through to pg-delta SQL formatting.", + "examples": [ + "{\"keywordCase\":\"upper\",\"indent\":2,\"maxWidth\":80,\"commaStyle\":\"trailing\"}" + ] + } + }, + "additionalProperties": false + }, "inspect": { "type": "object", "properties": { diff --git a/packages/config/src/experimental.ts b/packages/config/src/experimental.ts index 7738009557..5db11d4ee4 100644 --- a/packages/config/src/experimental.ts +++ b/packages/config/src/experimental.ts @@ -75,6 +75,30 @@ export const experimental = Schema.Struct({ }).pipe(Schema.withDecodingDefaultKey(Effect.succeed(false))), }).pipe(Schema.withDecodingDefaultKey(Effect.succeed({}))), ), + pgdelta: Schema.optionalKey( + Schema.Struct({ + enabled: Schema.Boolean.annotate({ + default: false, + description: + "Use pg-delta as the schema diff engine for db diff / db pull / db remote commit. Set false to fall back to the legacy migra engine.", + tags, + }).pipe(Schema.withDecodingDefaultKey(Effect.succeed(false))), + declarative_schema_path: Schema.optionalKey( + Schema.String.annotate({ + description: "Directory under supabase/ where declarative schema files are written.", + examples: ["./database"], + tags, + }), + ), + format_options: Schema.optionalKey( + Schema.String.annotate({ + description: "JSON string passed through to pg-delta SQL formatting.", + examples: ['{"keywordCase":"upper","indent":2,"maxWidth":80,"commaStyle":"trailing"}'], + tags, + }), + ), + }).pipe(Schema.withDecodingDefaultKey(Effect.succeed({}))), + ), inspect: Schema.optionalKey( Schema.Struct({ rules: Schema.Array(inspectRule)