Skip to content

[Wrangler/D1] Add CLI flag to skip migration confirmation#10598

Open
Ehbraheem wants to merge 6 commits intocloudflare:mainfrom
Ehbraheem:wrangler/d1-migration-confirmation-skip
Open

[Wrangler/D1] Add CLI flag to skip migration confirmation#10598
Ehbraheem wants to merge 6 commits intocloudflare:mainfrom
Ehbraheem:wrangler/d1-migration-confirmation-skip

Conversation

@Ehbraheem
Copy link
Copy Markdown

@Ehbraheem Ehbraheem commented Sep 10, 2025

Fixes #5017.

Add a new --force boolean flag to d1 migrations apply that skips the interactive
confirmation and applies any pending migrations non-interactively. This is useful
for CI and automation where a prompt is undesirable.

Usage example:

wrangler d1 migrations apply <db> --remote --force

Behavior:

  • When --force is present the command will not prompt and will proceed to apply
    all pending migrations. It logs a short informational message indicating force
    was used.
  • Default behavior is unchanged when the flag is not provided.

  • Tests
    • Tests included
    • Tests not necessary because:
  • Public documentation
    • Cloudflare docs PR(s):
    • Documentation not necessary because: The CLI usage text has been updated
  • Wrangler V3 Backport
    • Wrangler PR:
    • Not necessary because: A small patch with visible usage information

Open with Devin

@Ehbraheem Ehbraheem requested review from a team as code owners September 10, 2025 00:45
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Sep 10, 2025

🦋 Changeset detected

Latest commit: d98f1dd

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@Ehbraheem Ehbraheem force-pushed the wrangler/d1-migration-confirmation-skip branch from 3c9bb7f to f42c299 Compare September 10, 2025 00:47
@Ehbraheem
Copy link
Copy Markdown
Author

@penalosa, please help take a look at this PR when you're free. It's not a big change that'll take your time 🙏

Comment on lines +52 to +56
"auto-apply": {
type: "boolean",
description: "Automatically apply all pending migrations without prompt",
default: false,
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think we want to enable this only at the D1 level. There were few discussions internally for a "non-interactive" mode for wrangler, and therefore it's better to have a more consistent plan across products instead of doing something just for D1 migrations.

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.

Yeah @lambrospetrou, you’re right, it does make sense to aim for a consistent approach to non-interactive mode across Wrangler.

That said, I think starting with D1 makes sense since there’s already visible demand for this feature and the change here is relatively small.

It could also serve as a lightweight way to gauge how users respond to non-interactive workflows before rolling out a broader plan.

So rather than blocking this on the bigger initiative, this could be a useful first step to validate its impact and guide that wider effort.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@lambrospetrou from a Wrangler perspective, this seems pretty reasonable. We need a wider solution here, but I don't think we should block on that. I have a mild preference towards a potentially dangerous flag like this being called --force, but otherwise this seems conceptually good to me. Once someone from D1 has reviewed the code I can review and get this landed.

lambrospetrou
lambrospetrou previously approved these changes Oct 28, 2025
Copy link
Copy Markdown
Contributor

@lambrospetrou lambrospetrou left a comment

Choose a reason for hiding this comment

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

Before merging please do the suggested changes, to use --force-non-interactive.

@@ -49,9 +49,17 @@ export const d1MigrationsApplyCommand = createCommand({
"Specify directory to use for local persistence (you must use --local with this flag)",
requiresArg: true,
},
"auto-apply": {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
"auto-apply": {
"force-non-interactive": {

},
positionalArgs: ["database"],
async handler({ database, local, remote, persistTo, preview }, { config }) {
async handler(
{ database, local, remote, persistTo, preview, autoApply },
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
{ database, local, remote, persistTo, preview, autoApply },
{ database, local, remote, persistTo, preview, forceNonInteractive },

@@ -123,12 +131,19 @@ export const d1MigrationsApplyCommand = createCommand({
logger.log("Migrations to be applied:");
logger.table(unappliedMigrations.map((m) => ({ name: m.name })));

const ok = await confirm(
`About to apply ${unappliedMigrations.length} migration(s)
if (!autoApply) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (!autoApply) {
if (!forceNonInteractive) {

@Ehbraheem Ehbraheem force-pushed the wrangler/d1-migration-confirmation-skip branch from 5643923 to 63646c3 Compare December 15, 2025 00:07
@Ehbraheem
Copy link
Copy Markdown
Author

Before merging please do the suggested changes, to use --force-non-interactive.

@lambrospetrou Thanks for the review. I've addressed all the suggestions in a single commit to avoid vain changes. You can take a look again.

@serhii-chernenko
Copy link
Copy Markdown

follow up

@dario-piotrowicz
Copy link
Copy Markdown
Member

Hi @Ehbraheem, thank you very much for the PR and sorry for having left this PR go stale

I think the changes look good, but ideally the flag should be called --force (like @penalosa mentioned here: #10598 (comment))

This would also be consistent with similar flags in the repo:

force: {
type: "boolean",
alias: "y",
default: false,
description: "Skip confirmation",
},

Would you be able to make this change? (and solve the merge conflict?)

Please let me know, otherwise I'm happy to take over this PR 🙏

@Ehbraheem
Copy link
Copy Markdown
Author

Hi @Ehbraheem, thank you very much for the PR and sorry for having left this PR go stale

I think the changes look good, but ideally the flag should be called --force (like @penalosa mentioned here: #10598 (comment))

This would also be consistent with similar flags in the repo:

force: {
type: "boolean",
alias: "y",
default: false,
description: "Skip confirmation",
},

Would you be able to make this change? (and solve the merge conflict?)

Please let me know, otherwise I'm happy to take over this PR 🙏

Ok, makes sense. I'll look into that

@Ehbraheem Ehbraheem force-pushed the wrangler/d1-migration-confirmation-skip branch from 1fca9e9 to c47b8f0 Compare February 26, 2026 22:27
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

View 4 additional findings in Devin Review.

Open in Devin Review

Comment on lines +308 to +312
expect(commands.some((c) => c.includes("CREATE TABLE users"))).toBe(true);
expect(commands.some((c) => c.includes("INSERT INTO users"))).toBe(true);
expect(commands.some((c) => c.includes("INSERT INTO migrations"))).toBe(
true
);
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot Feb 26, 2026

Choose a reason for hiding this comment

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

🔴 Test assertions check for SQL content that is never written to the migration file

The test "applies migrations from a migration directory containing .sql files" creates a migration via runWrangler("d1 migrations create db test"), which only writes a comment line (-- Migration number: 0001 \t <timestamp>\n) to the file (packages/wrangler/src/d1/migrations/create.ts:70-72). However, the assertions at lines 307-311 expect CREATE TABLE users, INSERT INTO users, and INSERT INTO migrations to appear in the captured API request bodies. None of these SQL statements exist anywhere in the migration file or in the appended tracking insert (which uses INSERT INTO d1_migrations per packages/wrangler/src/d1/constants.ts:2). All three assertions will always fail, making this test dead-on-arrival.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Author

@Ehbraheem Ehbraheem left a comment

Choose a reason for hiding this comment

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

@dario-piotrowicz You can check now.

@github-project-automation github-project-automation bot moved this to Untriaged in workers-sdk Mar 20, 2026
@github-project-automation github-project-automation bot moved this from Untriaged to Approved in workers-sdk Mar 20, 2026
@workers-devprod workers-devprod dismissed lambrospetrou’s stale review March 20, 2026 19:57

Changes in owned files since last approval

penalosa
penalosa previously approved these changes Mar 20, 2026
@penalosa
Copy link
Copy Markdown
Contributor

/bonk can you update the formatting on this PR so that it passes pnpm check?

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk bot left a comment

Choose a reason for hiding this comment

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

Thanks for the PR! The core implementation is sound and the --force flag is a welcome addition. I have a few issues that should be addressed before merging.

Summary of issues:

  1. Broken test — The "applies migrations from a migration directory containing .sql files" test asserts SQL content (CREATE TABLE users, INSERT INTO users) that never exists in the generated migration file. d1 migrations create only writes a comment line, so these assertions will always fail.
  2. Changeset version level — Adding a new CLI flag is a new feature, which should be minor, not patch.
  3. Minor style issues — Extraneous blank lines, and new tests don't follow the existing pattern of destructuring expect from the test context.
  4. Missing --force alias — Other --force flags in the codebase consistently include alias: "y" for convenience.

Comment on lines +308 to +312
expect(commands.some((c) => c.includes("CREATE TABLE users"))).toBe(true);
expect(commands.some((c) => c.includes("INSERT INTO users"))).toBe(true);
expect(commands.some((c) => c.includes("INSERT INTO migrations"))).toBe(
true
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bug: These assertions will always fail. d1 migrations create only writes a comment line (-- Migration number: 0001 ...), so CREATE TABLE users and INSERT INTO users will never appear in the request body.

You need to either:

  1. Write actual SQL content into the migration file after d1 migrations create (before running apply), or
  2. Remove these incorrect assertions and only assert on what the migration actually contains (the comment line and the INSERT INTO migrations statement injected by apply.ts).

Option 1 is probably what was intended — add something like this after line 304:

Suggested change
expect(commands.some((c) => c.includes("CREATE TABLE users"))).toBe(true);
expect(commands.some((c) => c.includes("INSERT INTO users"))).toBe(true);
expect(commands.some((c) => c.includes("INSERT INTO migrations"))).toBe(
true
);
await runWrangler("d1 migrations create db test");
// Write actual SQL content into the migration file
const fs = await import("node:fs");
const migrationsDir = "migrations";
const files = fs.readdirSync(migrationsDir);
const migrationFile = files.find((f: string) => f.endsWith(".sql"));
if (migrationFile) {
fs.writeFileSync(
`${migrationsDir}/${migrationFile}`,
"CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT);\nINSERT INTO users (name) VALUES ('test');\n"
);
}

@@ -228,6 +229,162 @@ Your database may not be available to serve requests during the migration, conti
await runWrangler("d1 migrations apply db --remote");
expect(std.out).toBe("");
});

it("applies migrations from a migration directory containing .sql files", async () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Style: Existing tests in this file destructure expect from the test context (e.g. async ({ expect }) => {). The two new tests use global expect instead. Per the wrangler testing conventions, tests should not import expect from vitest — they should use the test context parameter.

Suggested change
it("applies migrations from a migration directory containing .sql files", async () => {
it("applies migrations from a migration directory containing .sql files", async ({ expect }) => {

);
});

it("should not prompt when --force is passed", async () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same as above — destructure expect from the test context:

Suggested change
it("should not prompt when --force is passed", async () => {
it("should not prompt when --force is passed", async ({ expect }) => {


await runWrangler("d1 migrations create db test");

await runWrangler("d1 migrations apply db --remote");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This test doesn't use --force and runs in non-interactive mode (setIsTTY(false)). When not interactive, confirm() automatically returns the fallback value (true) without prompting, so the migration will apply — but this means the test isn't really testing anything specific to this PR. Is this test meant to be a standalone test for .sql file migrations, or was it intended to use --force?

Also, since this test doesn't call mockConfirm(), it relies on the confirm() CI fallback behavior. If that's intentional, consider adding a comment explaining that.

@@ -0,0 +1,15 @@
---
"wrangler": patch
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Adding a new CLI flag (--force) is a new feature, which should be versioned as minor per the changeset guidelines ("minor: New features, new CLI commands, new configuration options").

Suggested change
"wrangler": patch
"wrangler": minor

getUnappliedMigrations,
initMigrationsTable,
} from "./helpers";
import { getMigrationsPath, getUnappliedMigrations, initMigrationsTable } from "./helpers";
import type { ParseError } from "@cloudflare/workers-utils";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: Extraneous blank line.

Suggested change

@@ -12,6 +12,7 @@ import { msw } from "../helpers/msw";
import { runInTempDir } from "../helpers/run-in-tmp";
import { runWrangler } from "../helpers/run-wrangler";


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: Extraneous blank line introduced.

Suggested change

Comment on lines +66 to +70
force: {
type: "boolean",
description: "Automatically apply all pending migrations without prompt",
default: false,
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

For consistency with other --force flags in the codebase (e.g. vectorize/delete.ts, kv/index.ts), consider adding alias: "y" so users can use -y as a shorthand.

Suggested change
force: {
type: "boolean",
description: "Automatically apply all pending migrations without prompt",
default: false,
},
force: {
type: "boolean",
alias: "y",
description: "Automatically apply all pending migrations without prompt",
default: false,
},

@github-project-automation github-project-automation bot moved this from Approved to In Review in workers-sdk Mar 20, 2026
@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk bot commented Mar 20, 2026

Review submitted. Here's a summary of the feedback:

Key issues found:

  1. Broken test (blocking) — The "applies migrations from a migration directory containing .sql files" test asserts that CREATE TABLE users and INSERT INTO users appear in request bodies, but d1 migrations create only writes a comment line. These assertions will always fail. The test needs to write actual SQL content into the migration file before running apply.

  2. Changeset version level — A new CLI flag is a new feature, so the changeset should be minor rather than patch.

  3. Missing alias: "y" — Other --force flags in the codebase (e.g., vectorize/delete.ts, kv/index.ts) include alias: "y" for convenience.

  4. Style nits — New tests use global expect instead of destructuring from test context (async ({ expect }) =>), extraneous blank lines, and the helpers import was collapsed to a single long line.

github run

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 20, 2026

create-cloudflare

npm i https://pkg.pr.new/create-cloudflare@10598

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/@cloudflare/kv-asset-handler@10598

miniflare

npm i https://pkg.pr.new/miniflare@10598

@cloudflare/pages-shared

npm i https://pkg.pr.new/@cloudflare/pages-shared@10598

@cloudflare/unenv-preset

npm i https://pkg.pr.new/@cloudflare/unenv-preset@10598

@cloudflare/vite-plugin

npm i https://pkg.pr.new/@cloudflare/vite-plugin@10598

@cloudflare/vitest-pool-workers

npm i https://pkg.pr.new/@cloudflare/vitest-pool-workers@10598

@cloudflare/workers-editor-shared

npm i https://pkg.pr.new/@cloudflare/workers-editor-shared@10598

wrangler

npm i https://pkg.pr.new/wrangler@10598

commit: 73c3340

Copilot AI review requested due to automatic review settings March 26, 2026 23:49
@Ehbraheem Ehbraheem force-pushed the wrangler/d1-migration-confirmation-skip branch from 73c3340 to d8a267a Compare March 26, 2026 23:49
@workers-devprod
Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • @cloudflare/d1
  • @cloudflare/wrangler
Show detailed file reviewers
  • packages/wrangler/src/tests/d1/migrate.test.ts: [@cloudflare/d1 @cloudflare/wrangler]
  • packages/wrangler/src/d1/migrations/apply.ts: [@cloudflare/d1 @cloudflare/wrangler]

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a --force flag to wrangler d1 migrations apply to bypass the interactive confirmation and apply pending migrations non-interactively (useful for automation/CI), with accompanying tests and a changeset entry.

Changes:

  • Add --force boolean flag to d1 migrations apply to skip the confirmation prompt and log that force mode was used.
  • Extend D1 migration tests to cover --force behavior (and additional migration-application behavior).
  • Add a changeset documenting the new flag.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
packages/wrangler/src/d1/migrations/apply.ts Adds --force arg and conditional logic to bypass confirmation + log informational message.
packages/wrangler/src/tests/d1/migrate.test.ts Adds test coverage for --force (and additional migration apply behavior).
.changeset/auto-apply-migrations.md Documents the new --force flag in release notes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +316 to +318
setIsTTY(false);
const std = mockConsoleMethods();

Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

mockConsoleMethods() registers beforeEach/afterEach hooks and uses module-level spy variables. Calling it inside an individual it(...) block can register extra hooks for subsequent tests and potentially re-spy on already-spied console methods, leading to order-dependent/flaky behavior. Prefer using the suite-level mockStd declared at the top of this file (and getAndClearOut() to reset output between assertions) instead of creating a new console mock here.

Copilot uses AI. Check for mistakes.

expect(std.out).toContain(
"--force passed, applying 1 migration(s) without prompt"
);
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

This test is named “should not prompt when --force is passed”, but it only asserts that the force info message is present. If the command accidentally still calls confirm() (which logs the prompt text in non-interactive mode), this assertion would still pass. Consider asserting that the output does not contain the confirm prompt text (e.g. "About to apply" / "Using fallback value"), or use the dialogs mock to fail the test if confirm() is invoked.

Suggested change
);
);
expect(std.out).not.toContain("About to apply");

Copilot uses AI. Check for mistakes.
@Ehbraheem Ehbraheem force-pushed the wrangler/d1-migration-confirmation-skip branch from 600628a to d98f1dd Compare March 27, 2026 00:31
@penalosa
Copy link
Copy Markdown
Contributor

@Ehbraheem can you address the review comments from Bonk? It seems like your tests have some bugs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Review

Development

Successfully merging this pull request may close these issues.

🚀 Feature Request: allow auto approve d1 migrations apply

7 participants