Skip to content

feat(cli): Add --no-apply flag for db schema declarative sync command#5220

Open
mittal-parth wants to merge 4 commits into
supabase:developfrom
mittal-parth:feat/declarative-sync-no-apply
Open

feat(cli): Add --no-apply flag for db schema declarative sync command#5220
mittal-parth wants to merge 4 commits into
supabase:developfrom
mittal-parth:feat/declarative-sync-no-apply

Conversation

@mittal-parth
Copy link
Copy Markdown

@mittal-parth mittal-parth commented May 10, 2026

Summary

This change introduces a new flag called no-apply to the db schema declarative sync command , allowing users to generate migration files without applying them to the local database.

Current behavior

$ supabase db schema declarative sync --name test
...
Created new migration at supabase/migrations/20260428093833_test.sql
Apply this migration to local database? [Y/n]

The only escape hatches today:

  • --apply — force-applies the migration (opposite of what's wanted)
  • Closing stdin via piping, e.g. true | supabase db schema declarative sync ... 2>&1 | cat

Closes #5218

New behavior

  • If --no-apply is set, the command writes the migration file and skips the apply step without any prompt
  • --no-apply overrides global --yes and cannot be combined with --apply.
  • Parity with legacy TS is maintained

The earlier behavior did not have any way to opt out of the prompt without also force applying via `--apply`  in the `db schema declarative sync` command.
This change introduces a new flag called `no-apply` , allowing users to generate migration files without applying them to the local database.
@mittal-parth mittal-parth requested a review from a team as a code owner May 10, 2026 20:04
@coveralls
Copy link
Copy Markdown

coveralls commented May 11, 2026

Coverage Report for CI Build 25658370387

Coverage increased (+0.008%) to 63.755%

Details

  • Coverage increased (+0.008%) from the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • 79 coverage regressions across 2 files.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

79 previously-covered lines in 2 files lost coverage.

File Lines Losing Coverage Coverage
cmd/db_schema_declarative.go 74 23.61%
internal/utils/git.go 5 57.14%

Coverage Stats

Coverage Status
Relevant Lines: 15693
Covered Lines: 10005
Line Coverage: 63.75%
Coverage Strength: 7.05 hits per line

💛 - Coveralls

| `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) — `--apply` and `--no-apply` are mutually exclusive |
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nipick

Worth flagging here that --no-apply is not compatible with --yes flag.

Comment on lines +1 to +80
import { describe, expect, it } from "@effect/vitest";
import { Effect, Layer, Option } from "effect";
import { LegacyGoProxy } from "../../../../../../shared/legacy/go-proxy.service.ts";
import type { LegacyDbSchemaDeclarativeSyncFlags } from "./sync.command.ts";
import { legacyDbSchemaDeclarativeSync } from "./sync.handler.ts";

function mockLegacyGoProxy() {
const calls: string[][] = [];
return {
layer: Layer.succeed(LegacyGoProxy, {
exec: (args: ReadonlyArray<string>) =>
Effect.sync(() => {
calls.push([...args]);
}),
}),
get calls() {
return calls;
},
};
}

const baseFlags: LegacyDbSchemaDeclarativeSyncFlags = {
noCache: false,
schema: [],
file: Option.none(),
name: Option.none(),
apply: false,
noApply: false,
};

describe("legacyDbSchemaDeclarativeSync", () => {
it.live("forwards --no-apply to the Go binary", () => {
const go = mockLegacyGoProxy();
return Effect.gen(function* () {
yield* legacyDbSchemaDeclarativeSync({ ...baseFlags, noApply: true });
expect(go.calls).toEqual([["db", "schema", "declarative", "sync", "--no-apply"]]);
}).pipe(Effect.provide(go.layer));
});

it.live("default invocation omits --apply and --no-apply", () => {
const go = mockLegacyGoProxy();
return Effect.gen(function* () {
yield* legacyDbSchemaDeclarativeSync({ ...baseFlags });
expect(go.calls).toEqual([["db", "schema", "declarative", "sync"]]);
}).pipe(Effect.provide(go.layer));
});

it.live("forwards only --apply when set", () => {
const go = mockLegacyGoProxy();
return Effect.gen(function* () {
yield* legacyDbSchemaDeclarativeSync({ ...baseFlags, apply: true });
expect(go.calls).toEqual([["db", "schema", "declarative", "sync", "--apply"]]);
}).pipe(Effect.provide(go.layer));
});

it.live("combines --no-apply with --name and --schema in argv order", () => {
const go = mockLegacyGoProxy();
return Effect.gen(function* () {
yield* legacyDbSchemaDeclarativeSync({
...baseFlags,
schema: ["public"],
name: Option.some("foo"),
noApply: true,
});
expect(go.calls).toEqual([
[
"db",
"schema",
"declarative",
"sync",
"--schema",
"public",
"--name",
"foo",
"--no-apply",
],
]);
}).pipe(Effect.provide(go.layer));
});
});
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nitpick

The integration test for the TS wrapper duplicates argv-construction coverage that's effectively a pure transformation. Per CLAUDE.md, "If a test is mostly validating a pure transformation … it should usually be a unit test instead."

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Two ways to solve this:

  1. We extract a pure function out in the sync.handler.ts which can be unit tested separately, without the need for building a mock. This would however, follow a different pattern than all other legacy proxy handlers.
    Eg:
export function buildLegacyDbSchemaDeclarativeSyncArgs(
  flags: LegacyDbSchemaDeclarativeSyncFlags,
): readonly string[] {
  const args: string[] = ["db", "schema", "declarative", "sync"];
  if (flags.noCache) args.push("--no-cache");
 ....
}

export const legacyDbSchemaDeclarativeSync = Effect.fn("legacy.db.schema.declarative.sync")(
  function* (flags: LegacyDbSchemaDeclarativeSyncFlags) {
    const proxy = yield* LegacyGoProxy;
    yield* proxy.exec(buildLegacyDbSchemaDeclarativeSyncArgs(flags));
  },
);
  1. The other legacy proxy handlers do not have any tests. So we could get rid of this sync.integration.test.ts entirely, similar to other handlers.

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.

feat(cli): non-interactive flag for db schema declarative sync

3 participants