feat!: add support for new Netlify Database primitive#8212
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughReplaces the legacy Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
c815f63 to
32092b7
Compare
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (5)
src/commands/database/util/spawn-async.ts (1)
3-3: PreferSpawnOptionsoverParameters<typeof spawn>[2].
spawnhas overloads, soParameters<typeof spawn>[2]resolves to whichever overload TS picks last and can change with type updates. ImportingSpawnOptions(orSpawnOptionsWithoutStdio) fromchild_processis more explicit and stable.♻️ Proposed refactor
-import { spawn } from 'child_process' +import { spawn, type SpawnOptions } from 'child_process' -export const spawnAsync = (command: string, args: string[], options: Parameters<typeof spawn>[2]): Promise<number> => { +export const spawnAsync = (command: string, args: string[], options?: SpawnOptions): Promise<number> => {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/database/util/spawn-async.ts` at line 3, The spawnAsync signature currently types its options parameter as Parameters<typeof spawn>[2], which is unstable across overloads; change it to use the explicit child_process type (import SpawnOptions or SpawnOptionsWithoutStdio from 'child_process') and update the function signature (spawnAsync) to accept options: SpawnOptions (or SpawnOptionsWithoutStdio) so the type is stable and explicit; update the import list at the top to include the chosen SpawnOptions type and adjust any call sites if needed.src/commands/database/db-init.ts (2)
55-73: Minor: preferfs/promisesoverexistsSyncfor consistency.The rest of the file uses async fs APIs (
mkdir,readdir,writeFile). UsingexistsSynchere is a small inconsistency; an asyncstat/accesskeeps the helper fully async without mixing sync I/O.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/database/db-init.ts` around lines 55 - 73, The helper carefullyWriteFile currently uses the sync existsSync; replace that with an async fs/promises check (e.g., using access or stat from 'fs/promises') so the function remains fully asynchronous. Modify carefullyWriteFile to await fs.promises.access(filePath) (or await stat(filePath)) in a try/catch to determine existence, then proceed to prompt and writeFile as before; keep the same prompt/overwrite logic and return behavior unchanged. Ensure imports at the top reference the async fs APIs rather than existsSync.
64-64: Usepath.relativeinstead of stringreplace.
filePath.replace(projectRoot, '')is fragile: it only replaces the first occurrence, silently no-ops ifprojectRootisn't a literal prefix (e.g., differing OS casing or a normalized vs. resolved path), and the leading.concatenation produces oddities like.foo/barif the prefix isn't found.path.relativehandles all of this correctly and yields the same./...form when joined.♻️ Proposed refactor
-import { join } from 'path' +import { join, relative } from 'path' @@ - message: `Overwrite existing file .${filePath.replace(projectRoot, '')}?`, + message: `Overwrite existing file ${relative(projectRoot, filePath)}?`,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/database/db-init.ts` at line 64, Replace the fragile string replace when building the confirmation message: instead of using filePath.replace(projectRoot, '') to show the path, compute the relative path with path.relative(projectRoot, filePath) (or join './' with that result when needed) and use that in the message template inside the db-init command so the prompt reliably shows a correct ./... path across platforms; update references to filePath/projectRoot in the message construction accordingly.e2e/install.e2e.ts (1)
158-181: Guard against the samefile:*.tgzdep appearing in bothdependenciesanddevDependencies.The current loop publishes each tarball it encounters; if a package is listed in both buckets (uncommon but legal during ad-hoc testing setups),
npm publishwill fail on the second attempt with409 Conflictand abort the entirebeforeAll. A smallSet<string>of already-publishedname@versionpairs (or skipping when the innernamewas seen earlier) keeps this resilient.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@e2e/install.e2e.ts` around lines 158 - 181, The loop publishing tarballs should skip duplicates to avoid a second `npm publish` causing a 409; add a Set (e.g. published = new Set<string>()) keyed by `${innerManifest.name}@${innerManifest.version}` and, after extracting innerManifest (in the block handling match/tgzPath and before calling execa('npm','publish'...)), check if the key is already present and if so set `bucket[name] = innerManifest.version` and continue; only call `npm publish` and add the key to `published` when it hasn’t been seen, and keep existing checks around publishResult and the existing `bucket[name] = innerManifest.version` assignment.src/commands/database/database.ts (1)
13-13: Nit: hyphenate "production-ready".This description string is surfaced by
--helpand is auto-copied intodocs/commands/database.md(line 12) anddocs/index.md(line 76), where LanguageTool already flags it. Fixing it here corrects all three locations.Proposed copy edit
- .description(`Provision a production ready Postgres database with a single command`) + .description(`Provision a production-ready Postgres database with a single command`)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/database/database.ts` at line 13, The description string passed to the .description(...) call currently reads "Provision a production ready Postgres database with a single command" and should be hyphenated; update that string to "Provision a production-ready Postgres database with a single command" (the change will propagate to generated docs). Locate the .description call for the database command and replace the unhyphenated phrase with the hyphenated "production-ready" (no other code changes required).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/commands/database.md`:
- Around line 50-54: The usage strings are built without the parent command,
causing "netlify <sub>" instead of "netlify database <sub>"; update the docs
generator so formatUsage in site/scripts/docs.js receives or derives the parent
command context and constructs the full command path (e.g., join parent
"database" + subcommand name) before returning the usage string; modify
formatUsage (or its caller) to accept a parent parameter or use the command
object’s full name (e.g., command.fullName/parent.name) when present so all
Usage blocks render as "netlify database <subcommand>".
In `@e2e/install.e2e.ts`:
- Around line 162-169: The current setup unconditionally calls execa('tar',
['-xzOf', tgzPath, 'package/package.json']) in the beforeAll hook and also
copies the .tgz into publishWorkspace because shouldCopyCLIFile only filters
node_modules/**; replace the external tar dependency by reading the package.json
from the tarball with a pure-Node implementation (e.g., use the "tar" or
"node:stream"/zlib APIs) inside the beforeAll logic so you don't require the
system tar binary, and also update shouldCopyCLIFile to explicitly exclude *.tgz
files so the tarball isn't copied into publishWorkspace in the first place
(adjust references to tgzPath, publishWorkspace, the execa('tar'...) call,
beforeAll, and shouldCopyCLIFile accordingly).
In `@src/commands/database/util/spawn-async.ts`:
- Around line 7-13: The exit handler currently calls resolve(code) then
continues to construct and call reject, causing erroneous error creation; update
the child.on('exit', ...) handler (the resolve and reject calls) so that after a
successful exit (code === 0) the function returns immediately (or use an else
branch) and does not execute the reject path; ensure the errorMessage only gets
constructed and reject(new Error(errorMessage)) is invoked when code !== 0 to
prevent constructing an error on success.
In `@tests/integration/commands/dev/dev.programmatic-netlify-dev.test.ts`:
- Line 41: Remove the leftover debug print by deleting the console.log(body)
call in the test (inside the test case where variable body is logged); ensure no
other debug console.* statements remain in the same test file
(tests/integration/commands/dev/dev.programmatic-netlify-dev.test.ts) so test
output is not polluted and assertions remain unchanged.
---
Nitpick comments:
In `@e2e/install.e2e.ts`:
- Around line 158-181: The loop publishing tarballs should skip duplicates to
avoid a second `npm publish` causing a 409; add a Set (e.g. published = new
Set<string>()) keyed by `${innerManifest.name}@${innerManifest.version}` and,
after extracting innerManifest (in the block handling match/tgzPath and before
calling execa('npm','publish'...)), check if the key is already present and if
so set `bucket[name] = innerManifest.version` and continue; only call `npm
publish` and add the key to `published` when it hasn’t been seen, and keep
existing checks around publishResult and the existing `bucket[name] =
innerManifest.version` assignment.
In `@src/commands/database/database.ts`:
- Line 13: The description string passed to the .description(...) call currently
reads "Provision a production ready Postgres database with a single command" and
should be hyphenated; update that string to "Provision a production-ready
Postgres database with a single command" (the change will propagate to generated
docs). Locate the .description call for the database command and replace the
unhyphenated phrase with the hyphenated "production-ready" (no other code
changes required).
In `@src/commands/database/db-init.ts`:
- Around line 55-73: The helper carefullyWriteFile currently uses the sync
existsSync; replace that with an async fs/promises check (e.g., using access or
stat from 'fs/promises') so the function remains fully asynchronous. Modify
carefullyWriteFile to await fs.promises.access(filePath) (or await
stat(filePath)) in a try/catch to determine existence, then proceed to prompt
and writeFile as before; keep the same prompt/overwrite logic and return
behavior unchanged. Ensure imports at the top reference the async fs APIs rather
than existsSync.
- Line 64: Replace the fragile string replace when building the confirmation
message: instead of using filePath.replace(projectRoot, '') to show the path,
compute the relative path with path.relative(projectRoot, filePath) (or join
'./' with that result when needed) and use that in the message template inside
the db-init command so the prompt reliably shows a correct ./... path across
platforms; update references to filePath/projectRoot in the message construction
accordingly.
In `@src/commands/database/util/spawn-async.ts`:
- Line 3: The spawnAsync signature currently types its options parameter as
Parameters<typeof spawn>[2], which is unstable across overloads; change it to
use the explicit child_process type (import SpawnOptions or
SpawnOptionsWithoutStdio from 'child_process') and update the function signature
(spawnAsync) to accept options: SpawnOptions (or SpawnOptionsWithoutStdio) so
the type is stable and explicit; update the import list at the top to include
the chosen SpawnOptions type and adjust any call sites if needed.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 0c3a75c8-cea4-47e2-91e2-08da043d185c
⛔ Files ignored due to path filters (2)
package-lock.jsonis excluded by!**/package-lock.jsontests/integration/commands/help/__snapshots__/help.test.ts.snapis excluded by!**/*.snap
📒 Files selected for processing (22)
docs/commands/database.mddocs/commands/db.mddocs/index.mde2e/install.e2e.tsnetlify-dev-4.17.4-unflag-database.1.tgzpackage.jsonsite/netlify.tomlsrc/commands/database/database.tssrc/commands/database/db-init.tssrc/commands/database/legacy/constants.tssrc/commands/database/legacy/db-init.tssrc/commands/database/legacy/db-status.tssrc/commands/database/legacy/drizzle.tssrc/commands/database/legacy/types.tssrc/commands/database/legacy/utils.tssrc/commands/database/util/db-connection.tssrc/commands/database/util/packages.tssrc/commands/database/util/spawn-async.tssrc/commands/dev/dev.tssrc/commands/dev/programmatic-netlify-dev.tstests/integration/commands/dev/dev.programmatic-netlify-dev.test.tstests/unit/commands/database/db-init.test.ts
💤 Files with no reviewable changes (7)
- docs/commands/db.md
- src/commands/database/legacy/types.ts
- src/commands/database/legacy/constants.ts
- src/commands/database/legacy/db-status.ts
- src/commands/database/legacy/drizzle.ts
- src/commands/database/legacy/db-init.ts
- src/commands/database/legacy/utils.ts
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/commands/database/util/spawn-async.ts (1)
7-14: Optional: surface termination signal in the rejection message.When the child is killed by a signal,
codeisnulland the second'exit'argument (signal) carries the actual cause (e.g.SIGTERM). Including it would make failures easier to diagnose without changing the happy path.♻️ Proposed refinement
- child.on('exit', (code) => { + child.on('exit', (code, signal) => { if (code === 0) { resolve(code) return } - const errorMessage = code ? `Process exited with code ${code.toString()}` : 'Process exited with no code' + const errorMessage = + code != null + ? `Process exited with code ${code.toString()}` + : signal != null + ? `Process terminated by signal ${signal}` + : 'Process exited with no code' reject(new Error(errorMessage)) })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/commands/database/util/spawn-async.ts` around lines 7 - 14, The exit handler for the spawned child only uses the exit code and omits the termination signal; update the child.on('exit') callback in spawnAsync to accept both (code, signal) and when code !== 0 include the signal in the rejection message (e.g. if code is null or non-zero append `signal` when present) so the reject(new Error(...)) contains either the exit code or the terminating signal (reference the child.on('exit', (code, signal) => ...) callback inside spawnAsync).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/commands/database.md`:
- Around line 64-93: The docs generator is incorrectly including top-level
colon-prefixed siblings (like status:hooks) when parsing nested commands because
parseCommand currently spreads commands.filter(...) unconditionally; update
generate-command-data.js so that when building the commands array inside
parseCommand you only include the colon-prefixed siblings if the command being
parsed is a top-level command (check command.parent?.name() === 'netlify' or
equivalent), i.e., change the second spread to run only when
command.parent?.name() === 'netlify' so parseCommand and the commands array no
longer pull in unrelated top-level subcommands like status:hooks.
---
Nitpick comments:
In `@src/commands/database/util/spawn-async.ts`:
- Around line 7-14: The exit handler for the spawned child only uses the exit
code and omits the termination signal; update the child.on('exit') callback in
spawnAsync to accept both (code, signal) and when code !== 0 include the signal
in the rejection message (e.g. if code is null or non-zero append `signal` when
present) so the reject(new Error(...)) contains either the exit code or the
terminating signal (reference the child.on('exit', (code, signal) => ...)
callback inside spawnAsync).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 66d6ac8d-2fed-4379-b23b-cada1781f0bb
📒 Files selected for processing (5)
docs/commands/database.mddocs/index.mdsite/scripts/docs.jssrc/commands/database/util/spawn-async.tstests/integration/commands/dev/dev.programmatic-netlify-dev.test.ts
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/commands/database.md`:
- Line 3: Update the frontmatter and any generated command description text to
hyphenate the compound adjective: change the value for the frontmatter key
"description" from "production ready" to "production-ready" and replace any
other occurrences of the exact phrase "production ready" in the generated
command description output (search for "production ready" in the file and
related generation/template code) so the command description consistently reads
"production-ready".
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 43d92ef8-5d10-4cba-b9fc-72f9a09c5f0d
📒 Files selected for processing (2)
docs/commands/database.mdsite/scripts/util/generate-command-data.js
| @@ -0,0 +1,272 @@ | |||
| --- | |||
| title: Netlify CLI database command | |||
| description: Provision a production ready Postgres database with a single command | |||
There was a problem hiding this comment.
Hyphenate “production-ready” in command description text.
Use production-ready (compound adjective) instead of production ready in both the frontmatter description and generated command description text.
✏️ Proposed wording tweak
-description: Provision a production ready Postgres database with a single command
+description: Provision a production-ready Postgres database with a single command-Provision a production ready Postgres database with a single command
+Provision a production-ready Postgres database with a single commandAlso applies to: 12-12
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/commands/database.md` at line 3, Update the frontmatter and any
generated command description text to hyphenate the compound adjective: change
the value for the frontmatter key "description" from "production ready" to
"production-ready" and replace any other occurrences of the exact phrase
"production ready" in the generated command description output (search for
"production ready" in the file and related generation/template code) so the
command description consistently reads "production-ready".
| | [`database init`](/commands/database#database-init) | Interactive setup: install the package, scaffold a starter migration, and verify the database | | ||
| | [`database connect`](/commands/database#database-connect) | Connect to the database | | ||
| | [`database reset`](/commands/database#database-reset) | Reset the local development database, removing all data and tables | | ||
| | [`database migrations`](/commands/database#database-migrations) | Manage database migrations | |
There was a problem hiding this comment.
Not the best place to mention this because this is index - but we generally had docs generating problem that subcommands where not prefixing commands with db/database before, so in particular usage examples where not generated correctly (for example instead of netlify database status it was printing netlify status on database page which was wrong.
Similarly database migrations subcommands (reset, apply, etc) were just not generated at all
So all the docs generating changes are for those cases.
The main "proof" of docs generating fixes working (at least with our current commands) is the shape of generated docs and fact that commands other than database were NOT touched.
| export const spawnAsync = (command: string, args: string[], options: Parameters<typeof spawn>[2]): Promise<number> => { | ||
| return new Promise((resolve, reject) => { | ||
| const child = spawn(command, args, options) | ||
| child.on('error', reject) | ||
| child.on('exit', (code) => { | ||
| if (code === 0) { | ||
| resolve(code) | ||
| return | ||
| } | ||
| const errorMessage = code ? `Process exited with code ${code.toString()}` : 'Process exited with no code' | ||
| reject(new Error(errorMessage)) | ||
| }) | ||
| }) | ||
| } |
There was a problem hiding this comment.
Extracted from now removed src/commands/database/legacy/utils.ts
| const carefullyWriteFile = async (filePath: string, data: string, projectRoot: string) => { | ||
| if (existsSync(filePath)) { | ||
| type Answers = { | ||
| overwrite: boolean | ||
| } | ||
| const answers = await inquirer.prompt<Answers>([ | ||
| { | ||
| type: 'confirm', | ||
| name: 'overwrite', | ||
| message: `Overwrite existing file .${filePath.replace(projectRoot, '')}?`, | ||
| }, | ||
| ]) | ||
| if (answers.overwrite) { | ||
| await writeFile(filePath, data) | ||
| } | ||
| } else { | ||
| await writeFile(filePath, data) | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
Extracted from now removed Extracted from now removed src/commands/database/legacy/utils.ts
|
@pieh how about "feat!: add support for new Netlify Database primitive"? |
🤖 I have created a release *beep* *boop* --- ## [26.0.0](v25.6.2...v26.0.0) (2026-04-28) ### ⚠ BREAKING CHANGES * add support for new Netlify Database primitive ([#8212](#8212)) ### Features * add support for new Netlify Database primitive ([#8212](#8212)) ([a153489](a153489)) ### Bug Fixes * set NETLIFY_DB_DRIVER when running netlify dev ([#8219](#8219)) ([3ececcd](3ececcd)) --- 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>
🎉 Thanks for submitting a pull request! 🎉
Summary
Fixes #<replace_with_issue_number>
For us to review and ship your PR efficiently, please perform the following steps:
passes our tests.
A picture of a cute animal (not mandatory, but encouraged)