Skip to content

feat: scaffold migration command#8022

Merged
paulo merged 9 commits intomainfrom
pa/migration-new-command
Mar 16, 2026
Merged

feat: scaffold migration command#8022
paulo merged 9 commits intomainfrom
pa/migration-new-command

Conversation

@paulo
Copy link
Contributor

@paulo paulo commented Mar 12, 2026

  • Creates both the migrations directory if doesn't exist
  • Allows to choose both the numbering schema (sequential or timestamp) and the slug (basically just the description separated by -)

@coderabbitai
Copy link

coderabbitai bot commented Mar 12, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new nested CLI command group db migration with a new subcommand new (registered when ENV EXPERIMENTAL_NETLIFY_DB_ENABLED = '1'). The new command accepts -d/--description, -s/--scheme (sequential|timestamp) and --json, dynamically imports src/commands/database/migration-new.ts, and delegates scaffold creation. migration-new.ts introduces NumberingScheme and MigrationNewOptions types, slug generation, numbering-scheme detection, next-prefix computation, migrations-directory resolution, and a migration scaffold writer. Adds comprehensive unit tests for the new command and a small import consolidation in db-connection.ts. CLI help/examples updated accordingly.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: a new migration scaffolding command is being added to the CLI with full implementation including directory creation, numbering schemes, and slug generation.
Description check ✅ Passed The description is directly related to the changeset and accurately captures the key features: automatic migrations directory creation, numbering schema selection, and slug generation from descriptions.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch pa/migration-new-command
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

github-actions bot commented Mar 12, 2026

📊 Benchmark results

Comparing with 03146de

  • Dependency count: 1,122 (no change)
  • Package size: 357 MB ⬇️ 0.00% decrease vs. 03146de
  • Number of ts-expect-error directives: 362 (no change)

@paulo paulo force-pushed the pa/migration-new-command branch from 05aee9a to 7adc066 Compare March 12, 2026 12:03
@paulo paulo force-pushed the pa/migration-new-command branch from 7adc066 to 182fe9e Compare March 12, 2026 12:29
@paulo paulo requested a review from eduardoboucas March 12, 2026 12:37
@paulo paulo marked this pull request as ready for review March 12, 2026 12:39
@paulo paulo requested a review from a team as a code owner March 12, 2026 12:39
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/commands/database/migration-new.ts`:
- Around line 149-151: The code currently allows descriptions that slugify to an
empty string (e.g., "!!!" or "___"), producing invalid folder names like
`${prefix}_`; after calling generateSlug(description) in the migration-new flow,
validate the returned slug and if it's empty (or only whitespace) reject the
input: surface a clear error message and abort (throw or process.exit with
non-zero) rather than proceeding to build folderName; update any user-facing
error text to mention the original description and suggest a valid
alphanumeric/hyphenated description. Ensure you update the logic around
generateSlug, generateNextPrefix, and folderName so the check runs immediately
after generateSlug(description) and before using prefix/creating the folder.
- Around line 60-65: The code that computes the next prefix (using existingNames
-> prefixes -> maxPrefix) currently uses /^(\d+)/ which also matches
timestamp-style folders; update the matching logic so when computing sequential
prefixes you only consider 4-digit sequential prefixes (e.g. use a regex like
/^(\d{4})/ to extract the four-digit prefix) so timestamp names (e.g.
20260312143000_add-users) are ignored, then continue to parseInt, compute
maxPrefix and padStart as before.
- Around line 154-155: The current code unconditionally creates the folder and
then calls writeFile(join(folderPath, 'migration.sql'), ...) which will silently
overwrite an existing migration; update migration-new.ts so you do not clobber
user edits: after mkdir(folderPath, { recursive: true }) check whether the
target file (join(folderPath, 'migration.sql')) already exists and, if it does,
abort with a clear error message; alternatively replace the writeFile call with
writeFile(..., { flag: 'wx' }) so the write fails if the file exists. Reference
the existing symbols mkdir, writeFile, folderPath and the 'migration.sql'
filename when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ce3eb328-c6ba-44df-b7a7-8550b98629a2

📥 Commits

Reviewing files that changed from the base of the PR and between b5a44db and 182fe9e.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • src/commands/database/database.ts
  • src/commands/database/db-connection.ts
  • src/commands/database/migration-new.ts
  • tests/unit/commands/database/migration-new.test.ts

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
tests/unit/commands/database/migration-new.test.ts (1)

82-98: Consider adding a test for mixed numbering patterns.

The current tests cover pure sequential, pure timestamp, and unrecognized patterns, but not a mix of valid sequential and timestamp migrations in the same directory.

🧪 Suggested test case
test('returns undefined for mixed sequential and timestamp patterns', () => {
  expect(detectNumberingScheme(['0001_create-users', '20260312143000_add-posts'])).toBeUndefined()
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/commands/database/migration-new.test.ts` around lines 82 - 98, Add
a test to cover mixed numbering patterns so detectNumberingScheme returns
undefined when both sequential and timestamp-style names are present; update
tests/unit/commands/database/migration-new.test.ts by adding a new test case
that calls detectNumberingScheme(['0001_create-users',
'20260312143000_add-posts']) and expects undefined, ensuring the suite checks
mixed-pattern detection in the detectNumberingScheme unit tests.
src/commands/database/migration-new.ts (1)

118-147: Consider deduplicating the prompt configuration.

The two inquirer.prompt calls for scheme selection (lines 120-132 and 134-145) share nearly identical configuration, differing only by the presence of default.

♻️ Suggested refactor
   if (!scheme) {
-    if (detectedScheme) {
-      const answers = await inquirer.prompt<{ scheme: NumberingScheme }>([
-        {
-          type: 'list',
-          name: 'scheme',
-          message: 'Numbering scheme:',
-          choices: [
-            { name: 'Sequential (0001, 0002, ...)', value: 'sequential' },
-            { name: 'Timestamp (20260312143000)', value: 'timestamp' },
-          ],
-          default: detectedScheme,
-        },
-      ])
-      scheme = answers.scheme
-    } else {
-      const answers = await inquirer.prompt<{ scheme: NumberingScheme }>([
-        {
-          type: 'list',
-          name: 'scheme',
-          message: 'Numbering scheme:',
-          choices: [
-            { name: 'Sequential (0001, 0002, ...)', value: 'sequential' },
-            { name: 'Timestamp (20260312143000)', value: 'timestamp' },
-          ],
-        },
-      ])
-      scheme = answers.scheme
-    }
+    const answers = await inquirer.prompt<{ scheme: NumberingScheme }>([
+      {
+        type: 'list',
+        name: 'scheme',
+        message: 'Numbering scheme:',
+        choices: [
+          { name: 'Sequential (0001, 0002, ...)', value: 'sequential' },
+          { name: 'Timestamp (20260312143000)', value: 'timestamp' },
+        ],
+        ...(detectedScheme && { default: detectedScheme }),
+      },
+    ])
+    scheme = answers.scheme
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/database/migration-new.ts` around lines 118 - 147, The two
inquirer.prompt calls for selecting the numbering scheme in migration-new.ts are
duplicated; consolidate them by building a single question object (or questions
array) with the shared properties (type, name, message, choices) and only add a
default property when detectedScheme is truthy, then call inquirer.prompt once
to set scheme; reference the variables and symbols scheme, detectedScheme,
NumberingScheme, and the inquirer.prompt invocation when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/commands/database/migration-new.ts`:
- Around line 118-147: The two inquirer.prompt calls for selecting the numbering
scheme in migration-new.ts are duplicated; consolidate them by building a single
question object (or questions array) with the shared properties (type, name,
message, choices) and only add a default property when detectedScheme is truthy,
then call inquirer.prompt once to set scheme; reference the variables and
symbols scheme, detectedScheme, NumberingScheme, and the inquirer.prompt
invocation when making the change.

In `@tests/unit/commands/database/migration-new.test.ts`:
- Around line 82-98: Add a test to cover mixed numbering patterns so
detectNumberingScheme returns undefined when both sequential and timestamp-style
names are present; update tests/unit/commands/database/migration-new.test.ts by
adding a new test case that calls detectNumberingScheme(['0001_create-users',
'20260312143000_add-posts']) and expects undefined, ensuring the suite checks
mixed-pattern detection in the detectNumberingScheme unit tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 79f44c13-c311-4c09-b40e-ff497c599911

📥 Commits

Reviewing files that changed from the base of the PR and between 182fe9e and 6eb5333.

📒 Files selected for processing (2)
  • src/commands/database/migration-new.ts
  • tests/unit/commands/database/migration-new.test.ts

@paulo paulo enabled auto-merge (squash) March 12, 2026 16:14
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
tests/unit/commands/database/migration-new.test.ts (1)

245-253: Add a test to ensure unexpected directory-read errors are propagated.

You already test the graceful ENOENT-like path. Please add a companion test asserting that other read failures are not treated as empty migrations.

Suggested test addition
 describe('migrationNew', () => {
@@
   test('handles empty migrations directory gracefully', async () => {
     mockReaddir.mockRejectedValue(new Error('ENOENT'))
@@
   })
+
+  test('rethrows unexpected errors when reading migrations directory', async () => {
+    mockReaddir.mockRejectedValueOnce(new Error('EACCES'))
+
+    await expect(
+      migrationNew({ description: 'initial migration', scheme: 'sequential' }, createMockCommand()),
+    ).rejects.toThrow('EACCES')
+
+    expect(mockMkdir).not.toHaveBeenCalled()
+    expect(mockWriteFile).not.toHaveBeenCalled()
+  })
 })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/commands/database/migration-new.test.ts` around lines 245 - 253,
Add a new unit test next to the existing "handles empty migrations directory
gracefully" test that ensures non-ENOENT errors from readdir are propagated:
configure mockReaddir to reject with a different error (e.g., new
Error('EACCES') or new Error('boom')), call migrationNew({ description: 'initial
migration', scheme: 'sequential' }, createMockCommand()), and assert the call
rejects (use await expect(...).rejects.toThrow()) and that mockMkdir was not
called; reference migrationNew, mockReaddir, mockMkdir, and createMockCommand to
locate and implement the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/commands/database/migration-new.ts`:
- Around line 147-155: The file fails Prettier formatting; run the project's
formatter (e.g., Prettier) on the code that writes the migration template around
the writeFile call using migrationFilePath and the inline SQL template string so
the generated file and this source file conform to the repo's formatting rules;
re-run CI/lint to confirm the formatting gate passes and commit the reformatted
file.
- Around line 75-77: The catch in src/commands/database/migration-new.ts
currently swallows all readdir errors and returns []; change it to only return
[] when the caught error is an ENOENT (check err.code === 'ENOENT') and rethrow
otherwise so real I/O/permission errors surface. Also update the test in
migration-new.test.ts (the mock at line ~246) to create the error as a
NodeJS.ErrnoException, set readError.code = 'ENOENT', and use
mockReaddir.mockRejectedValue(readError) (mirroring the pattern used around
mockReaddir at ~236-238) so the test accurately simulates a missing-directory
error.

---

Nitpick comments:
In `@tests/unit/commands/database/migration-new.test.ts`:
- Around line 245-253: Add a new unit test next to the existing "handles empty
migrations directory gracefully" test that ensures non-ENOENT errors from
readdir are propagated: configure mockReaddir to reject with a different error
(e.g., new Error('EACCES') or new Error('boom')), call migrationNew({
description: 'initial migration', scheme: 'sequential' }, createMockCommand()),
and assert the call rejects (use await expect(...).rejects.toThrow()) and that
mockMkdir was not called; reference migrationNew, mockReaddir, mockMkdir, and
createMockCommand to locate and implement the test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 36f6b047-40eb-490f-96b7-df5ea29fecc1

📥 Commits

Reviewing files that changed from the base of the PR and between 6eb5333 and 05a97d8.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • src/commands/database/migration-new.ts
  • tests/unit/commands/database/migration-new.test.ts

@paulo paulo force-pushed the pa/migration-new-command branch from 961f4bd to 426af3d Compare March 16, 2026 07:54
@paulo paulo merged commit 0938c79 into main Mar 16, 2026
71 checks passed
@paulo paulo deleted the pa/migration-new-command branch March 16, 2026 08:37
serhalp added a commit that referenced this pull request Mar 16, 2026
🤖 I have created a release *beep* *boop*
---


## [24.3.0](v24.2.0...v24.3.0)
(2026-03-16)


### Features

* scaffold migration command
([#8022](#8022))
([0938c79](0938c79))


### Bug Fixes

* **deps:** pin dependencies
([#8033](#8033))
([a3f9db4](a3f9db4))
* **deps**: update dependency comment-json to v4.6.2
([#8036](#8036))
[c1039b6](c1039b6)
* **deps:** remove problematic `gh-release-fetch` dep
([#8020](#8020))
([520caab](520caab))
* **deps:** update dependency @netlify/ai to v0.4.1
([#8044](#8044))
([e258cc4](e258cc4))
* **deps:** update dependency @netlify/dev to v4.16.1
([#8024](#8024))
([08aff83](08aff83))
* **deps:** update dependency @netlify/dev to v4.16.2
([#8045](#8045))
([720dd2e](720dd2e))
* **deps:** update dependency @netlify/dev-utils to v4.4.1
([#8025](#8025))
([3af7d88](3af7d88))
* **deps:** update dependency @netlify/edge-functions to v3.0.4
([#8027](#8027))
([33160b3](33160b3))
* **deps:** update dependency @netlify/functions to v5.1.3
([#8028](#8028))
([ef18129](ef18129))
* **deps:** update dependency @netlify/images to v1.3.5
([#8029](#8029))
([8dae2c5](8dae2c5))
* **deps:** update dependency @netlify/types to v2.4.0
([#8030](#8030))
([3104635](3104635))
* **deps:** update dependency ansi-escapes to v7.3.0
([#8035](#8035))
([50741f4](50741f4))
* **deps:** update dependency dotenv to v17.3.1
([#8038](#8038))
([bf8e84a](bf8e84a))
* **deps:** update netlify packages
([#8034](#8034))
([ae8e22c](ae8e22c))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: token-generator-app[bot] <82042599+token-generator-app[bot]@users.noreply.github.com>
Co-authored-by: Philippe Serhal <philippe.serhal@netlify.com>
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.

2 participants