-
Notifications
You must be signed in to change notification settings - Fork 1
feat: add programmatic api #67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughThe create-db package is refactored from a monolithic JavaScript CLI entry point to a modularized TypeScript architecture. The original index.js is removed and replaced with src/index.ts (comprehensive CLI and programmatic API), src/cli.ts (CLI entrypoint), and src/types.ts (type definitions). Build tooling (tsdown, TypeScript), test files migrated to TypeScript, package.json restructured with ESM exports and dist-based entry points, and GitHub workflows updated. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Pre-merge checks❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
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 |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
claim-db-worker | 3dbbffd | Commit Preview URL Branch Preview URL |
Dec 03 2025, 07:05 PM |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 8
🧹 Nitpick comments (7)
create-db/tsconfig.json (1)
1-15: Minor formatting inconsistency: mixed tabs and spaces.Lines 3-12 use tabs for indentation while lines 13-14 use spaces. Consider normalizing to consistent indentation throughout.
{ "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "moduleResolution": "bundler", - "esModuleInterop": true, - "verbatimModuleSyntax": true, - "strict": true, - "skipLibCheck": true, - "outDir": "dist", - "types": ["node"] - }, + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "esModuleInterop": true, + "verbatimModuleSyntax": true, + "strict": true, + "skipLibCheck": true, + "outDir": "dist", + "types": ["node"] + }, "include": ["src/**/*"], "exclude": ["node_modules", "dist", "__tests__"] }create-db/tsdown.config.ts (1)
10-12: Apply shebang banner only to the CLI entry.The
outputOptions.bannercurrently applies the shebang (#!/usr/bin/env node) to all entry outputs, includingdist/index.mjs. While Node.js strips the shebang before parsing, it should be applied only to executable CLI files per best practices. Some toolchain components may also struggle with shebangs in library modules.Use
outputOptionsas a function to conditionally apply the banner only when buildingcli.ts:outputOptions: (output) => { if (output.fileName?.includes('cli')) { output.banner = "#!/usr/bin/env node"; } return output; }create-db/__tests__/regions.test.ts (1)
4-21: Tests rely on external API without mocking.These tests make real network requests to the regions API. While this validates end-to-end behavior, it makes tests flaky if the API is unavailable. Consider adding unit tests with mocked fetch for deterministic testing, keeping these as integration tests.
create-db/src/index.ts (4)
229-238: No timeout on database creation fetch request.Unlike analytics (5s timeout), the
createDatabaseCorefetch has no timeout. A slow/hanging API response could block indefinitely.+ const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 30000); + const resp = await fetch(`${CREATE_DB_WORKER_URL}/create`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ region, name, utm_source: getCommandName(), userAgent, }), + signal: controller.signal, }); + + clearTimeout(timeoutId);
54-83:pendingAnalyticsarray grows unbounded.Promises are pushed to
pendingAnalyticsbut never removed afterflushAnalytics(). In a long-running process using the programmatic API, this could cause a memory leak.async function flushAnalytics(maxWaitMs = 500): Promise<void> { if (pendingAnalytics.length === 0) return; await Promise.race([ Promise.all(pendingAnalytics), new Promise<void>((resolve) => setTimeout(resolve, maxWaitMs)), ]); + pendingAnalytics.length = 0; }
575-579: Programmaticcreate()doesn't validate region.Unlike the CLI handler which calls
validateRegion(), the programmatic API accepts anyRegionIdwithout checking if that region is currently available.Consider validating against available regions or documenting that the region must be valid:
export async function create( options?: ProgrammaticCreateOptions ): Promise<CreateDatabaseResult> { + if (options?.region) { + await validateRegion(options.region); + } return createDatabaseCore(options?.region || "us-east-1", options?.userAgent); }
549-556: Hardcoded version string.Consider reading version from
package.jsonto avoid version drift.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (14)
create-db/__tests__/create.test.js(0 hunks)create-db/__tests__/create.test.ts(1 hunks)create-db/__tests__/regions.test.js(0 hunks)create-db/__tests__/regions.test.ts(1 hunks)create-db/__tests__/utils.test.js(0 hunks)create-db/__tests__/utils.test.ts(1 hunks)create-db/index.js(0 hunks)create-db/package.json(2 hunks)create-db/src/cli.ts(1 hunks)create-db/src/index.ts(1 hunks)create-db/src/types.ts(1 hunks)create-db/tsconfig.json(1 hunks)create-db/tsdown.config.ts(1 hunks)create-db/vitest.config.ts(1 hunks)
💤 Files with no reviewable changes (4)
- create-db/tests/utils.test.js
- create-db/tests/regions.test.js
- create-db/tests/create.test.js
- create-db/index.js
🧰 Additional context used
🧬 Code graph analysis (4)
create-db/src/cli.ts (1)
create-db/src/index.ts (1)
createDbCli(549-556)
create-db/__tests__/utils.test.ts (1)
create-db/src/types.ts (3)
CreateDatabaseResult(59-59)isDatabaseError(61-65)isDatabaseSuccess(67-71)
create-db/__tests__/regions.test.ts (2)
create-db/src/index.ts (2)
regions(592-594)RegionSchema(34-34)create-db/src/types.ts (1)
RegionSchema(3-10)
create-db/src/index.ts (1)
create-db/src/types.ts (10)
RegionId(12-12)RegionCoordinates(28-31)UserLocation(14-21)GeoLocationResponse(111-118)Region(33-37)RegionsResponse(124-124)CreateDatabaseResult(59-59)ApiResponse(99-104)RegionSchema(3-10)ProgrammaticCreateOptions(126-129)
🪛 GitHub Actions: Tests
create-db/__tests__/create.test.ts
[error] 24-24: AssertionError: expected '' to contain 'Database created successfully!'
[error] 29-29: SyntaxError: Unexpected end of JSON input
[error] 37-37: AssertionError: expected '' to contain 'Available Prisma Postgres regions'
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Workers Builds: create-db-worker
- GitHub Check: Workers Builds: claim-db-worker
🔇 Additional comments (9)
create-db/src/cli.ts (1)
1-3: LGTM!Clean CLI entrypoint that correctly delegates to the
createDbCli()API. The.jsextension in the import is appropriate for ESM TypeScript withmoduleResolution: "bundler".create-db/vitest.config.ts (1)
1-7: LGTM!Clean Vitest configuration that correctly targets the TypeScript test files under
__tests__/.create-db/package.json (2)
5-17: Well-structured ESM package exports.The exports map correctly defines both the library entry point (
.) with types and the CLI entry (./cli). The setup properly supports modern ESM consumption patterns.
35-48: Build and publish workflow looks solid.Good use of
prepublishOnlyto ensure the package is built before publishing. The bin entries correctly point to the built CLI artifact.create-db/__tests__/regions.test.ts (1)
23-35: LGTM!Good coverage of
RegionSchemavalidation including valid region IDs, invalid strings, empty strings, and type coercion rejection.create-db/__tests__/utils.test.ts (1)
8-35: LGTM!Type guard tests correctly verify both positive and negative cases with representative payloads matching the
DatabaseResultandDatabaseErrorinterfaces.create-db/src/types.ts (3)
1-12: LGTM!Clean Zod schema definition with proper type inference for
RegionId.
39-71: LGTM!Well-designed discriminated union with
successas the discriminator and correctly implemented type guards.
23-26: Remove unusedPartialUserLocationinterface.This interface is defined but has no usages in the codebase. Consider removing it to keep the types clean.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (3)
create-db/src/index.ts (3)
416-420: UseisCancel()instead of=== nullfor cancellation check.As flagged in past reviews,
@clack/prompts'select()returns a cancel symbol when cancelled, notnull. You must use theisCancel()helper to properly detect cancellation.Apply this diff to fix the cancellation check:
- if (selectedRegion === null) { + if (isCancel(selectedRegion)) { cancel(pc.red("Operation cancelled.")); await flushAnalytics(); process.exit(0); }
446-453: Addreturnafter--envoutput to prevent fall-through.As flagged in past reviews, after outputting the
DATABASE_URLformat (lines 451-452), the code falls through to line 455 which shows the interactive intro. The function must return after the--envoutput.Apply this diff to add the missing return:
console.log(`DATABASE_URL="${result.connectionString}"`); console.error(`\n# Claim your database at: ${result.claimUrl}`); + return; }
469-473: UseisCancel()instead of=== nullfor cancellation check.Same issue as line 416 -
@clack/prompts'select()returns a cancel symbol, notnull. Use theisCancel()helper.Apply this diff to fix the cancellation check:
- if (selectedRegion === null) { + if (isCancel(selectedRegion)) { cancel(pc.red("Operation cancelled.")); await flushAnalytics(); process.exit(0); }
🧹 Nitpick comments (1)
create-db/src/index.ts (1)
573-577: Consider validating region in programmatic API.The
create()function doesn't validate the region before callingcreateDatabaseCore(). While the API will reject invalid regions, validating upfront would provide better error messages and avoid unnecessary network calls.Apply this diff to add region validation:
export async function create( options?: ProgrammaticCreateOptions ): Promise<CreateDatabaseResult> { + if (options?.region) { + await validateRegion(options.region); + } return createDatabaseCore(options?.region || "us-east-1", options?.userAgent); }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
.github/workflows/tests.yml(1 hunks)create-db/__tests__/create.test.ts(1 hunks)create-db/src/index.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- create-db/tests/create.test.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Workers Builds: create-db-worker
- GitHub Check: Workers Builds: claim-db-worker
🔇 Additional comments (2)
.github/workflows/tests.yml (2)
44-46: Build step correctly positioned before test execution.The new build step is properly placed in the workflow sequence and necessary for the TypeScript-to-ESM compilation required by this PR.
53-54: These environment variables have fallback defaults and are already documented.The code in
create-db/src/index.tsdefines default values for bothCREATE_DB_WORKER_URLandCLAIM_DB_WORKER_URL:
CREATE_DB_WORKER_URLdefaults to"https://create-db-temp.prisma.io"CLAIM_DB_WORKER_URLdefaults to"https://create-db.prisma.io"If the GitHub Actions secrets are not configured, these variables will use the hardcoded defaults rather than being empty strings. The README.md already documents these environment variables and their purpose, including example values for local development. The workflow implementation is correct—the secrets allow overriding the defaults for CI if needed, but the tests will run successfully with the default endpoints if secrets are unconfigured.
Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
create-db/src/index.ts (1)
404-453: Addreturnafter--envoutput to avoid falling through into interactive modeWhen
input.envis true (andinput.jsonis false), the handler printsDATABASE_URLand the claim URL but then falls through out of theif (input.json || input.env)block into the interactive flow below. That can cause a second database to be created and an unexpected prompt even though the user requested non‑interactive.envoutput.Add a
returnafter theconsole.errorto terminate the handler in--envmode:if (!result.success) { console.error(result.message); process.exit(1); } console.log(`DATABASE_URL="${result.connectionString}"`); console.error(`\n# Claim your database at: ${result.claimUrl}`); + return; }Note: when both
--jsonand--envare supplied, JSON currently wins and.envoutput is skipped because of the earlierif (input.json) { ... return; }– if that’s not intentional, you may want to enforce mutual exclusivity or emit both in a defined order.
🧹 Nitpick comments (6)
create-db/src/index.ts (6)
54-91: Avoid unbounded growth ofpendingAnalyticsin long‑lived processes
sendAnalyticskeeps appending promises topendingAnalytics, andflushAnalyticsnever clears the array. In a long‑running process (e.g. using the programmatic API repeatedly), this can accumulate resolved promises and re‑await them on every flush.You can snapshot and clear the queue before waiting:
async function flushAnalytics(maxWaitMs = 500): Promise<void> { - if (pendingAnalytics.length === 0) return; - await Promise.race([ - Promise.all(pendingAnalytics), - new Promise<void>((resolve) => setTimeout(resolve, maxWaitMs)), - ]); + if (pendingAnalytics.length === 0) return; + + const toWait = pendingAnalytics.splice(0, pendingAnalytics.length); + + await Promise.race([ + Promise.all(toWait), + new Promise<void>((resolve) => setTimeout(resolve, maxWaitMs)), + ]); }
36-38: Consider movingdotenvside‑effect out of the library entrypointCalling
dotenv.config()at module load time couples importingcreate-db(for the programmatic API) to reading and mutatingprocess.env. That’s convenient for the CLI, but can be surprising when the package is used as a library inside other apps.If you want a cleaner library surface, consider moving
dotenv.config()into the actual CLI entry (e.g.cli.ts/ bin script) and letting host applications decide when/how to load env files.
155-174: Avoid re‑parsing.envnow thatdotenv.config()is already used
readUserEnvFilemanually parses.env, even thoughdotenv.config()has already populatedprocess.env. That duplicates parsing logic and can subtly diverge from dotenv’s behavior.Given you only need a couple of keys, you could simplify to reading from
process.env(which will already include.envcontent) and drop the custom parser, or at least gate this helper behind a clear reason (e.g. “read only from file, ignore real env”).
221-338: Defensive handling for non‑2xx responses withouterrorpayload (optional)
createDatabaseCorespecial‑cases 429 and otherwise relies on the worker always returning either valid JSON with anerrorfield or a non‑JSON body (caught asinvalid_json). If the API ever returns a non‑2xx JSON response withouterror(e.g. a bare{}with status 500), this path would be treated as success.If you want extra robustness, consider also checking
!resp.okbefore returning success, e.g.:- if (result.error) { + if (!resp.ok || result.error) { // existing error handling...This keeps current behavior but guards against unexpected wire formats.
532-544: Optional: reusecheckOnline()for theregionscommand for consistent UXThe
regionssub‑command currently callsgetRegions()directly. If the API is unreachable or returns a non‑OK response, users may see a thrown error stack rather than the friendly “Cannot reach Prisma Postgres API server” messaging you implemented incheckOnline().For consistency with
create, consider:regions: os .meta({ description: "List available Prisma Postgres regions" }) .handler(async (): Promise<void> => { - const regions = await getRegions(); + await checkOnline(); + const regions = await getRegions();
573-592: Programmatic API reuses core logic but never flushes analytics (design choice)
create()andregions()nicely reusecreateDatabaseCoreandgetRegions, giving a clean programmatic surface. However,createDatabaseCoresends analytics and relies onflushAnalytics()being called by the CLI; the programmatic path never flushes, and callers have no way to opt out or flush explicitly.If this API is intended for embedding in other apps/services, consider one of:
- Disabling analytics for programmatic calls (e.g. flag parameter).
- Exposing a documented
flushAnalytics()export for hosts that care.- Injecting an analytics sink so library users can stub/disable it in tests.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
create-db/src/index.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
create-db/src/index.ts (1)
create-db/src/types.ts (10)
RegionId(12-12)RegionCoordinates(28-31)UserLocation(14-21)GeoLocationResponse(111-118)Region(33-37)RegionsResponse(124-124)CreateDatabaseResult(59-59)ApiResponse(99-104)RegionSchema(3-10)ProgrammaticCreateOptions(126-129)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Workers Builds: claim-db-worker
- GitHub Check: Workers Builds: create-db-worker
🔇 Additional comments (2)
create-db/src/index.ts (2)
100-153: Location → nearest‑region helpers look correct and defensiveThe
detectUserLocation+getRegionClosestToLocationcombo handles network failures and malformed/partial coordinates gracefully (null + NaN checks) while doing a proper Haversine distance calculation over the knownREGION_COORDINATES. This is a solid, side‑effect‑free helper pair.
176-219: Online check and region fetching/validation are straightforward and consistent
checkOnline,getRegions, andvalidateRegionform a clear, reusable layer: health‑check first, then fetch, then validate against the live region list with a helpful error message. Error paths exit with a non‑zero code and flush analytics, which is appropriate for a CLI.
There was a problem hiding this 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 (2)
create-db/src/index.ts (2)
100-121: Add timeout to geolocation fetch to prevent CLI hangs.Unlike the analytics calls,
detectUserLocationblocks the main flow. Ifipapi.cois slow or unresponsive, the CLI will hang indefinitely. Consider adding anAbortControllerwith a reasonable timeout (e.g., 3-5 seconds).async function detectUserLocation(): Promise<UserLocation | null> { try { + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), 3000); const response = await fetch("https://ipapi.co/json/", { method: "GET", headers: { "User-Agent": "create-db-cli/1.0" }, + signal: controller.signal, }); + clearTimeout(timer); if (!response.ok) return null;
176-206: Consider adding timeouts to API fetches for resilience.
checkOnline()andgetRegions()lack timeouts. While they have error handling, a hung connection will block indefinitely without feedback to the user. Adding a 10-second timeout would improve resilience.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
create-db/src/index.ts(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Workers Builds: claim-db-worker
- GitHub Check: Workers Builds: create-db-worker
🔇 Additional comments (7)
create-db/src/index.ts (7)
1-34: Imports and type exports look good.The
isCancelimport is now included, addressing the previous review feedback. The type exports provide a clean public API surface.
36-52: Configuration setup is appropriate.The
quiet: trueoption correctly suppresses warnings for missing.envfiles. Region coordinates are appropriately hardcoded for geolocation calculations.
123-153: Haversine distance calculation is correctly implemented.The function properly handles type coercion and edge cases, returning
nullfor invalid coordinates.
155-174: Basic .env parsing is acceptable for this use case.The simple parser handles the expected
PRISMA_ACTOR_NAMEandPRISMA_ACTOR_PROJECTvariables. More complex edge cases (multiline values, escaped quotes) aren't handled, but this is fine given the limited scope.
221-338: Database creation logic is well-structured with comprehensive error handling.The function properly handles rate limiting, JSON parse errors, and API errors. Connection string construction and analytics tracking are correctly implemented.
340-591: Router and CLI handlers are correctly implemented.The cancellation checks now properly use
isCancel(), and the control flow for JSON/env output modes includes proper returns. The interactive region selection logic is sound.
610-628: Programmatic API exports are clean and well-documented.The
create()andregions()functions provide a clean programmatic interface with helpful JSDoc examples.
| const pendingAnalytics: Promise<void>[] = []; | ||
|
|
||
| async function sendAnalytics( | ||
| eventName: string, | ||
| properties: Record<string, unknown>, | ||
| cliRunId: string | ||
| ): Promise<void> { | ||
| const controller = new AbortController(); | ||
| const timer = setTimeout(() => controller.abort(), 5000); | ||
|
|
||
| const promise = (async () => { | ||
| try { | ||
| await fetch(`${CREATE_DB_WORKER_URL}/analytics`, { | ||
| method: "POST", | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: JSON.stringify({ | ||
| eventName, | ||
| properties: { distinct_id: cliRunId, ...properties }, | ||
| }), | ||
| signal: controller.signal, | ||
| }); | ||
| } catch { | ||
| // Analytics failures should not block CLI | ||
| } finally { | ||
| clearTimeout(timer); | ||
| } | ||
| })(); | ||
|
|
||
| pendingAnalytics.push(promise); | ||
| } | ||
|
|
||
| async function flushAnalytics(maxWaitMs = 500): Promise<void> { | ||
| if (pendingAnalytics.length === 0) return; | ||
| await Promise.race([ | ||
| Promise.all(pendingAnalytics), | ||
| new Promise<void>((resolve) => setTimeout(resolve, maxWaitMs)), | ||
| ]); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pendingAnalytics array is never cleared, causing unbounded growth.
After flushAnalytics() completes, the pendingAnalytics array still contains all previous (now-settled) promises. On subsequent calls, you re-await already-settled promises and the array grows indefinitely. Clear the array after flushing.
async function flushAnalytics(maxWaitMs = 500): Promise<void> {
if (pendingAnalytics.length === 0) return;
await Promise.race([
Promise.all(pendingAnalytics),
new Promise<void>((resolve) => setTimeout(resolve, maxWaitMs)),
]);
+ pendingAnalytics.length = 0;
}🤖 Prompt for AI Agents
In create-db/src/index.ts around lines 54 to 91, pendingAnalytics is never
cleared which leads to unbounded growth; update flushAnalytics so it operates on
a snapshot of the current pending promises and clears the shared array
immediately (e.g., copy or splice the array into a local variable and set the
original to empty) before awaiting Promise.race on that snapshot, ensuring
settled promises are not retained and new sendAnalytics calls can push into an
empty array.
| }); | ||
| } | ||
|
|
||
| const caller = createRouterClient(router, { context: {} }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Remove unused caller variable.
caller is created but never referenced anywhere in the file. This is dead code.
-const caller = createRouterClient(router, { context: {} });📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const caller = createRouterClient(router, { context: {} }); |
🤖 Prompt for AI Agents
In create-db/src/index.ts around line 593, the variable "caller" is created with
const caller = createRouterClient(router, { context: {} }); but never used;
remove this dead-code declaration (delete that line) or, if a client is intended
to be used later, replace with a used identifier and use it; otherwise simply
remove the unused const to eliminate the unused variable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (2)
create-db/src/index.ts (2)
86-92:pendingAnalyticsarray is never cleared after flushing.After
flushAnalytics()completes, the array still contains all settled promises, causing unbounded growth on repeated calls.Apply this diff to clear the array after flushing:
async function flushAnalytics(maxWaitMs = 500): Promise<void> { if (pendingAnalytics.length === 0) return; + const toFlush = [...pendingAnalytics]; + pendingAnalytics.length = 0; await Promise.race([ - Promise.all(pendingAnalytics), + Promise.all(toFlush), new Promise<void>((resolve) => setTimeout(resolve, maxWaitMs)), ]); }
594-594: Remove unusedcallervariable.
calleris created but never used anywhere in the file.-const caller = createRouterClient(router, { context: {} });
🧹 Nitpick comments (4)
create-db/src/index.ts (4)
101-122: Consider adding a timeout to the geolocation request.
detectUserLocationlacks a timeout, so a slow or unresponsiveipapi.cocould cause the CLI to hang. Consider usingAbortControllersimilar tosendAnalytics.async function detectUserLocation(): Promise<UserLocation | null> { try { + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), 3000); const response = await fetch("https://ipapi.co/json/", { method: "GET", headers: { "User-Agent": "create-db-cli/1.0" }, + signal: controller.signal, }); + clearTimeout(timer); if (!response.ok) return null;
457-488: Consider handling the case whereenvPathis a directory.The code appends to
envPathwithout checking if it's a valid file path. If a user passes a directory path,appendFileSyncwill fail with a cryptic error.try { const targetEnvPath = envPath!; + const stat = fs.existsSync(targetEnvPath) ? fs.statSync(targetEnvPath) : null; + if (stat?.isDirectory()) { + console.error(pc.red(`Cannot write to directory: ${targetEnvPath}. Please specify a file path.`)); + process.exit(1); + } const lines = [
589-589: Hardcoded version string may drift frompackage.json.Consider importing the version from
package.jsonor using a build-time replacement to keep them in sync.
618-622: Consider validating the region in the programmatic API.The
createfunction doesn't validate theregionoption before callingcreateDatabaseCore. An invalid region will result in an API error from the backend rather than a clear client-side validation error.export async function create( options?: ProgrammaticCreateOptions ): Promise<CreateDatabaseResult> { + const region = options?.region || "us-east-1"; + if (options?.region) { + await validateRegion(region); + } - return createDatabaseCore(options?.region || "us-east-1", options?.userAgent); + return createDatabaseCore(region, options?.userAgent); }Alternatively, if you want to allow the backend to handle validation, document this behavior in the JSDoc.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
create-db/src/index.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
create-db/src/index.ts (1)
create-db/src/types.ts (10)
RegionId(12-12)RegionCoordinates(28-31)UserLocation(14-21)GeoLocationResponse(111-118)Region(33-37)RegionsResponse(124-124)CreateDatabaseResult(59-59)ApiResponse(99-104)RegionSchema(3-10)ProgrammaticCreateOptions(126-129)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Workers Builds: claim-db-worker
- GitHub Check: Workers Builds: create-db-worker
🔇 Additional comments (5)
create-db/src/index.ts (5)
1-35: LGTM!Imports and re-exports are well-organized. The
isCancelimport from@clack/promptsis correctly included as addressed from previous feedback.
37-53: LGTM!Configuration setup with sensible defaults and well-defined region coordinates matching the
RegionSchemaenum.
177-207: LGTM!Online check and region fetching are appropriately implemented. The health check provides clear user feedback on connection issues.
222-339: LGTM!
createDatabaseCorehandles rate limiting, JSON parsing errors, and API errors robustly with appropriate analytics tracking. Connection string construction properly URL-encodes credentials.
570-583: LGTM!The regions handler is clean and straightforward. Error propagation to the CLI framework is acceptable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
🧹 Nitpick comments (2)
.github/workflows/publish.yml (2)
65-76: Extract duplicated build logic into a reusable workflow.The build steps (checking for build script and running
pnpm run build) are duplicated between thebuildjob andexperimental-releasejob. Consider extracting this into a reusable workflow to reduce maintenance burden.Create
.github/workflows/build-packages.ymlas a reusable workflow, then call it from both jobs:# .github/workflows/build-packages.yml name: Build Packages on: workflow_call: jobs: build-packages: runs-on: ubuntu-latest steps: - name: 🔨 Build packages run: | for pkg in create-db create-pg create-postgres; do if [ -f "$pkg/package.json" ]; then cd "$pkg" if grep -q '"build"' package.json; then echo "Building $pkg..." pnpm run build || echo "Build skipped for $pkg (no build script)" fi cd - >/dev/null fi doneThen in
publish.yml, call the reusable workflow after setup steps in bothbuildandexperimental-releasejobs.Also applies to: 219-230
65-76: Extract duplicated build logic into a reusable workflow.The build step (checking for build script and running
pnpm run build) is duplicated betweenbuildandexperimental-releasejobs. Create a reusable workflow to reduce maintenance burden and ensure consistency.Also applies to: 219-230
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
.github/workflows/preview.yml(0 hunks).github/workflows/publish.yml(1 hunks).github/workflows/release.yml(0 hunks)
💤 Files with no reviewable changes (2)
- .github/workflows/preview.yml
- .github/workflows/release.yml
🧰 Additional context used
🪛 actionlint (1.7.9)
.github/workflows/publish.yml
215-215: "github.event.pull_request.head.ref" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/reference/security/secure-use#good-practices-for-mitigating-script-injection-attacks for more details
(expression)
260-260: the runner of "actions/github-script@v6" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Workers Builds: create-db-worker
- GitHub Check: Workers Builds: claim-db-worker
🔇 Additional comments (5)
.github/workflows/publish.yml (5)
21-22: Workflow permissions set restrictively at the top level.The default permissions are
contents: read, which is appropriate. Child jobs explicitly elevate permissions only where needed (release jobs needcontents: write,pull-requests: write,id-token: write). This follows least-privilege principle well.
12-12: Concurrency control prevents simultaneous workflow runs.The concurrency group is scoped to workflow + ref, which prevents duplicate runs for the same branch/PR. This is appropriate.
21-22: Permission model appropriately follows least-privilege principle.Top-level permissions are read-only by default. Jobs that need elevated permissions (
contents: write,pull-requests: write,id-token: write) explicitly declare them. This is a security best practice.Also applies to: 94-97
12-12: Concurrency control prevents simultaneous runs.Concurrency group scoped to workflow + ref appropriately prevents duplicate executions on the same branch/PR.
154-159: The auto-generation code doesn't execute in normal operation.The condition
if [ -z "$(ls -A .changeset 2>/dev/null)" ]checks whether the.changesetdirectory is empty. Since.changeset/config.jsonand.changeset/README.mdare committed to the repository and always present, the directory is never empty. The defensive code block never runs in normal circumstances.The architectural concern about auto-generated changesets is theoretically sound, but it doesn't apply to this codebase in its current state. The condition that would trigger the auto-generation (an empty
.changesetdirectory) should not occur during normal workflow execution.Likely an incorrect or invalid review comment.
.github/workflows/publish.yml
Outdated
| - name: 📦 Update npm to support Trusted Publishers | ||
| run: npm install -g npm@11.5.1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
npm 11.5.1 GitHub Actions Trusted Publishers latest version 2025
💡 Result:
Summary — Trusted Publishing (GitHub Actions & npm CLI)
- npm Trusted Publishing (OIDC) reached general availability on July 31, 2025 and supports GitHub Actions; it requires npm CLI v11.5.1 or later. [1]
- GitHub has been rolling related npm security changes and recommending migration to Trusted Publishing in 2025. [2]
- Note: there have been follow-up npm CLI patch releases (e.g., 11.5.2) with reported Windows issues — test upgrades in CI before wide rollout. [3]
Sources:
[1] GitHub / npm changelog: “npm trusted publishing with OpenID Connect (OIDC) is generally available” (July 31, 2025).
[2] GitHub Changelog: “Strengthening npm security… Trusted publishers” (Sept 29, 2025).
[3] npm/cli GitHub issue reporting problems with npm 11.5.2 (Aug 2025).
🌐 Web query:
npm Trusted Publishers GitHub Actions requirements
💡 Result:
Short checklist — requirements to use npm Trusted Publishers with GitHub Actions
- npm CLI v11.5.1 or later. [1]
- Use GitHub-hosted runners (self-hosted runners not supported). [1]
- Configure a Trusted Publisher for the package on npmjs.com: specify organization/user, repository, and the workflow filename (exact filename, must include .yml/.yaml and live in .github/workflows/). Environment name is optional. Each package can have only one trusted publisher at a time. [1]
- Workflow must request OIDC ID token permission (id-token: write) so GitHub can issue the job-specific token. [1][2]
- The npm CLI will use OIDC for npm publish (provenance is generated automatically when using Trusted Publishing). [1][3]
- Trusted publishing applies only to publish (npm publish); other npm commands (install, view, access) still need normal auth for private packages. For installing private deps in CI you may still need a read token. [1]
Sources: npm Trusted Publishers docs and GitHub/npm announcements. [1][2][3]
🌐 Web query:
npm latest version December 2025
💡 Result:
The latest stable npm CLI version as of December 3, 2025 is v11.6.2 (released Oct 8, 2025). [1][2]
Sources:
[1] Node.js release notes showing bundled npm v11.6.2.
[2] npm/cli release v11.6.2 (release date Oct 8, 2025).
Consider upgrading npm to v11.6.2, the latest stable version for Trusted Publishers support.
Line 50 pins npm to v11.5.1, which meets the minimum requirement for Trusted Publishers (GA July 31, 2025). However, v11.6.2 (released October 8, 2025) is now available and includes bug fixes from v11.5.x patch releases that had reported Windows issues. Upgrading to v11.6.2 provides better stability without breaking changes.
🤖 Prompt for AI Agents
.github/workflows/publish.yml around lines 49 to 50: the workflow currently
installs npm@11.5.1; update the pinned npm version to 11.6.2 to use the latest
stable Trusted Publishers-supporting release. Modify the run step to install
npm@11.6.2, then re-run the workflow to verify the action works on all runner
OSes (especially Windows) and that no other steps depend on the older npm patch
version.
🧩 Analysis chain
🌐 Web query:
npm 11.5.1 GitHub Actions Trusted Publishers OIDC latest version 2025
💡 Result:
Short answer: Trusted publishing (OIDC) for npm was made generally available in July 2025 and requires npm CLI v11.5.1 or later. [1][2][3]
Details:
- GitHub announced npm "trusted publishing with OpenID Connect (OIDC)" GA on July 31, 2025. [1]
- npm's docs state trusted publishing requires npm CLI v11.5.1 or later. [2]
- npm CLI v11.5.1 was released July 24, 2025 (follow-up patch 11.5.2 released July 30, 2025). [3]
Sources:
[1] GitHub Blog changelog: "npm trusted publishing with OIDC is generally available" (July 31, 2025).
[2] npm Docs: "Trusted publishing for npm packages" (requires npm CLI v11.5.1 or later).
[3] npm/cli release notes: v11.5.1 (released July 24, 2025).
🌐 Web query:
GitHub Actions npm Trusted Publishers recommended version
💡 Result:
Use npm CLI version 11.5.1 or later (npm@>=11.5.1) when using npm Trusted Publishing with GitHub Actions — this enables OIDC/trusted-publisher publishing (no long‑lived npm token) and automatic provenance. [1][2]
Sources:
[1] GitHub Changelog: "Trusted publishing requires npm CLI v11.5.1 or later."
[2] npm Docs: "Trusted publishing — configure for GitHub Actions"
Consider using npm@11.5.2 or a version constraint like npm@>=11.5.1 instead of pinning to 11.5.1.
npm@11.5.1 is the minimum required version for Trusted Publishers (GA July 2025), but npm@11.5.2 (released July 30, 2025) is available with additional patches. Pinning to the exact minimum version may miss important bug fixes; consider using a flexible constraint or updating to the latest patch.
🤖 Prompt for AI Agents
.github/workflows/publish.yml around lines 49 to 50: the workflow pins npm to
the exact version 11.5.1 which misses later patches; update the run step to
install either npm@11.5.2 (recommended) or use a flexible constraint such as
npm@">=11.5.1" to ensure bugfixes are included while still meeting the Trusted
Publishers minimum requirement.
| if ! pnpm publish --access public --no-git-checks; then | ||
| echo "Publish failed, trying to bump version and retry..." | ||
| npm version patch --no-git-tag-version | ||
| pnpm publish --access public --no-git-checks || echo "Publish failed again for $pkg" | ||
| fi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
cat -n .github/workflows/publish.yml | sed -n '130,160p'Repository: prisma/create-db
Length of output: 1867
🏁 Script executed:
cat -n .github/workflows/publish.yml | sed -n '145,175p'Repository: prisma/create-db
Length of output: 1603
Fix publish failure handling to prevent silent deployment failures.
The workflow silences the second publish failure with || echo instead of failing explicitly. If both publish attempts fail for any package, the loop continues and the job reports success despite the release being incomplete.
Since this is a monorepo with multiple packages, a single failed publish should block the workflow. Replace line 149 with an explicit failure:
if ! pnpm publish --access public --no-git-checks; then
echo "Publish failed, trying to bump version and retry..."
npm version patch --no-git-tag-version
- pnpm publish --access public --no-git-checks || echo "Publish failed again for $pkg"
+ if ! pnpm publish --access public --no-git-checks; then
+ echo "❌ Publish failed for $pkg after retry"
+ exit 1
+ fi
fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if ! pnpm publish --access public --no-git-checks; then | |
| echo "Publish failed, trying to bump version and retry..." | |
| npm version patch --no-git-tag-version | |
| pnpm publish --access public --no-git-checks || echo "Publish failed again for $pkg" | |
| fi | |
| if ! pnpm publish --access public --no-git-checks; then | |
| echo "Publish failed, trying to bump version and retry..." | |
| npm version patch --no-git-tag-version | |
| if ! pnpm publish --access public --no-git-checks; then | |
| echo "❌ Publish failed for $pkg after retry" | |
| exit 1 | |
| fi | |
| fi |
🤖 Prompt for AI Agents
.github/workflows/publish.yml around lines 146 to 150: the second publish
attempt currently uses "|| echo" which swallows failures and lets the job
continue; change the failure handling to explicitly fail the workflow when the
second publish fails (for example, replace the "|| echo" with logic that prints
the error and exits non‑zero or wrap the second publish in a conditional that on
failure prints a clear message and calls exit 1) so any package publish failure
stops the job.
🧩 Analysis chain
🏁 Script executed:
cat -n .github/workflows/publish.yml | sed -n '135,160p'Repository: prisma/create-db
Length of output: 1527
Hard-fail on publish failures to prevent silent deployment failures.
The publish step silently suppresses the second retry with || echo "...", causing the loop to continue and the workflow to report success even if all packages fail to publish. This is particularly dangerous in a multi-package loop—failed releases are masked entirely.
Remove the error suppression:
# First try to publish
if ! pnpm publish --access public --no-git-checks; then
echo "Publish failed, trying to bump version and retry..."
npm version patch --no-git-tag-version
- pnpm publish --access public --no-git-checks || echo "Publish failed again for $pkg"
+ pnpm publish --access public --no-git-checks
fiAlternatively, track failed packages and exit with a non-zero code if any publish fails.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if ! pnpm publish --access public --no-git-checks; then | |
| echo "Publish failed, trying to bump version and retry..." | |
| npm version patch --no-git-tag-version | |
| pnpm publish --access public --no-git-checks || echo "Publish failed again for $pkg" | |
| fi | |
| if ! pnpm publish --access public --no-git-checks; then | |
| echo "Publish failed, trying to bump version and retry..." | |
| npm version patch --no-git-tag-version | |
| pnpm publish --access public --no-git-checks | |
| fi |
🤖 Prompt for AI Agents
.github/workflows/publish.yml around lines 146 to 150: the publish retry
suppresses errors with `|| echo ...`, allowing the workflow to continue as
success even when publish fails; remove the error-suppression so the command
exits non-zero on failure (or replace it with logic that records failed package
names and exits non-zero after the loop), e.g., make the second publish
invocation fail loudly and/or append failure tracking and an explicit exit 1 if
any package failed.
| - name: 📝 Ensure Changeset Exists | ||
| run: | | ||
| if [ -z "$(ls -A .changeset 2>/dev/null)" ]; then | ||
| echo "No changeset found. Creating a default one..." | ||
| pnpm changeset add --empty --message "chore(release): auto-generated changeset" | ||
| fi |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changeset fallback creates generic entries that may mask missing release notes.
Creating an empty changeset with auto-generated messages could hide real version bumps without proper documentation of what changed. This should either fail explicitly or require manual intervention to ensure releases are properly documented.
Consider failing explicitly when changesets are missing:
- name: 📝 Ensure Changeset Exists
run: |
- if [ -z "$(ls -A .changeset 2>/dev/null)" ]; then
- echo "No changeset found. Creating a default one..."
- pnpm changeset add --empty --message "chore(release): auto-generated changeset"
- fi
+ if [ -z "$(ls -A .changeset 2>/dev/null)" ]; then
+ echo "❌ No changeset found. Please run 'pnpm changeset' and commit your changes."
+ exit 1
+ fi🤖 Prompt for AI Agents
.github/workflows/publish.yml around lines 154-159: the current step
auto-creates an empty changeset which can mask missing release notes; change
this step to fail the workflow if .changeset is empty (exit non-zero with a
clear message instructing the author to add a proper changeset) or gate
auto-generation behind an explicit opt-in flag (e.g., only create an auto
changeset when a repo secret/env var is set), and update the run script to emit
the failure message and exit 1 when the directory is empty unless the opt-in
flag is present.
| done | ||
|
|
||
| - name: 💬 Post preview-testing instructions | ||
| uses: actions/github-script@v6 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
latest version actions/github-script GitHub Actions
💡 Result:
The latest released version is 8.0.0 — use actions/github-script@v8 (or @v8.0.0). [1][2]
Sources:
[1] actions/packages (github-script) — latest version 8.0.0.
[2] actions/github-script repository (releases/tags).
🌐 Web query:
actions/github-script v6 maintained supported 2025
💡 Result:
Short answer: v6 is not the current release and GitHub treats the repository as low‑activity — they’re not taking contributions and only promise security/major-breaking fixes. Upgrade to the latest (v8) if you want current Node/runtime support. Sources: GitHub repo README (actions/github-script) and Marketplace listing. [1][2]
Sources
[1] actions/github-script repository README (shows note about not taking contributions, V6 uses Node 16, and examples using v8).
[2] GitHub Marketplace listing for actions/github-script (same maintenance note).
Upgrade actions/github-script from v6 to v8.
Version 6 is outdated and uses Node 16. The latest supported version is v8.0.0, which provides current Node.js runtime support. Version 6 is no longer actively maintained—GitHub only provides security fixes for it.
Also applies to: 267-267
🧰 Tools
🪛 actionlint (1.7.9)
260-260: the runner of "actions/github-script@v6" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
🤖 Prompt for AI Agents
.github/workflows/publish.yml around lines 260 and 267: the workflow uses
actions/github-script@v6 which is outdated and uses Node 16; update both
occurrences to actions/github-script@v8 (or pin to actions/github-script@v8.0.0)
so the action runs on the current supported Node runtime and receives the latest
fixes/security updates.
🧩 Analysis chain
🌐 Web query:
actions/github-script latest version 2025 current maintenance
💡 Result:
Latest release: actions/github-script v8.0.0 (released Sep 4, 2025). [1]
Maintenance status: the repository is in limited-maintenance — GitHub notes they are not taking contributions right now but will provide security updates and fix major breaking issues; support requests are directed to Community Discussions / GitHub Support. [2]
Sources:
- Release page (v8.0.0). [1]
- Repository README / maintenance note. [2]
Update actions/github-script to v8 or latest version.
v6 is two major versions behind the current v8.0.0 release (Sep 2025). While v6 receives security updates under limited maintenance, upgrading to the latest stable version ensures access to improvements and full compatibility with current Node.js environments.
🧰 Tools
🪛 actionlint (1.7.9)
260-260: the runner of "actions/github-script@v6" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
🤖 Prompt for AI Agents
.github/workflows/publish.yml around line 260: the workflow pins
actions/github-script to v6 which is outdated; update the action reference to
the current major (use actions/github-script@v8 or actions/github-script@v8.0.0
or the latest stable tag), commit the change to the workflow file, and
run/validate the CI workflow to ensure compatibility with your Node.js runner
and no breaking changes in the updated action.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (4)
.github/workflows/publish.yml (4)
85-91:⚠️ Fix silent publish failure handling to prevent incomplete releases.Line 88 uses
|| echoto suppress publish failures, allowing the loop to continue even if all retries fail. In a monorepo, a single failed package should block the entire workflow. This was flagged in prior reviews and remains unresolved.Replace the error suppression with an explicit failure:
- pnpm publish --access public --no-git-checks || echo "Publish failed again for $pkg" + if ! pnpm publish --access public --no-git-checks; then + echo "❌ Publish failed for $pkg after retry" + exit 1 + fi
93-99:⚠️ Replace automatic changeset fallback with explicit failure.Auto-generating an empty changeset can mask missing release notes and hide real version bumps. This was flagged in prior reviews.
Fail explicitly if changesets are missing:
- name: 📝 Ensure Changeset Exists run: | - if [ -z "$(ls -A .changeset 2>/dev/null)" ]; then - echo "No changeset found. Creating a default one..." - pnpm changeset add --empty --message "chore(release): auto-generated changeset" - fi + if [ -z "$(ls -A .changeset 2>/dev/null)" ]; then + echo "❌ No changeset found. Please run 'pnpm changeset' and commit your changes." + exit 1 + fi
152-156: 🔴 Fix script injection vulnerability in preview tag creation.Line 154 directly interpolates
github.event.pull_request.head.refinto a shell script. Althoughtrsanitizes forward slashes, it does not prevent shell metacharacter injection (e.g.,$, backticks,;,|). An attacker can craft a malicious branch name to execute arbitrary commands. GitHub's security documentation requires untrusted context values to be passed via environment variables. This was flagged in prior reviews.Apply this diff:
- name: 🔖 Create unique preview tag + env: + HEAD_REF: ${{ github.event.pull_request.head.ref }} run: | - SAFE_REF=$(echo "${{ github.event.pull_request.head.ref }}" | tr '/' '-') + SAFE_REF=$(echo "${HEAD_REF}" | tr '/' '-') echo "PRE_TAG=pr${{ github.event.number }}-${SAFE_REF}-${{ github.run_id }}" >> $GITHUB_ENV
191-224:⚠️ Upgradeactions/github-scriptfrom v6 to v8.Line 192 uses
actions/github-script@v6, which is outdated and runs on Node 16. The latest stable version is v8.0.0 (released September 2025), which provides current Node.js runtime support and critical updates. This was flagged in prior reviews.Update the action reference:
- uses: actions/github-script@v6 + uses: actions/github-script@v8
🧹 Nitpick comments (1)
.github/workflows/publish.yml (1)
166-171: Clean up redundant environment variable reassignments.Lines 166–171 reassign
CREATE_DB_WORKER_URLandCLAIM_DB_WORKER_URLafter they are already declared in the step'senvblock (lines 160–163). This is redundant; remove the inline reassignments.run: | - # Resolve URLs with fallback - CREATE_DB_WORKER_URL="${{ secrets.CREATE_DB_WORKER_URL }}" - CLAIM_DB_WORKER_URL="${{ secrets.CLAIM_DB_WORKER_URL }}" - - # Persist for next steps - echo "CREATE_DB_WORKER_URL=$CREATE_DB_WORKER_URL" >> $GITHUB_ENV - echo "CLAIM_DB_WORKER_URL=$CLAIM_DB_WORKER_URL" >> $GITHUB_ENV - echo "Using CREATE_DB_WORKER_URL=$CREATE_DB_WORKER_URL"
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
.github/workflows/publish.yml(1 hunks)
🧰 Additional context used
🪛 actionlint (1.7.9)
.github/workflows/publish.yml
153-153: "github.event.pull_request.head.ref" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/reference/security/secure-use#good-practices-for-mitigating-script-injection-attacks for more details
(expression)
192-192: the runner of "actions/github-script@v6" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Workers Builds: claim-db-worker
- GitHub Check: Workers Builds: create-db-worker
🔇 Additional comments (5)
.github/workflows/publish.yml (5)
1-27: ✅ Workflow triggers and environment setup look good.Concurrency, event triggers, and secret/workspace env variables are properly configured.
29-64: ✅ Release job setup is correct.Node/pnpm configuration and permissions are appropriate.
100-108: ✅ Changesets PR preparation is correct.
110-145: ✅ Preview job setup is correct.
146-151: ✅ README copying logic is straightforward.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (3)
.github/workflows/release.yml (3)
93-98: Changeset fallback creates generic entries that may mask missing release notes.Creating an empty changeset with auto-generated messages could hide real version bumps without proper documentation of what changed. This should either fail explicitly or require manual intervention to ensure releases are properly documented.
Consider failing explicitly when changesets are missing:
- name: 📝 Ensure Changeset Exists run: | - if [ -z "$(ls -A .changeset 2>/dev/null)" ]; then - echo "No changeset found. Creating a default one..." - pnpm changeset add --empty --message "chore(release): auto-generated changeset" - fi + if [ -z "$(ls -A .changeset 2>/dev/null)" ]; then + echo "❌ No changeset found. Please run 'pnpm changeset' and commit your changes." + exit 1 + fi
85-89: Hard-fail on publish failures to prevent silent deployment failures.Line 88 silently suppresses the second publish retry with
|| echo "...", allowing the workflow to continue and report success even if all packages fail to publish. This is particularly dangerous in a multi-package loop—failed releases are masked entirely.Remove the error suppression:
# First try to publish if ! pnpm publish --access public --no-git-checks; then echo "Publish failed, trying to bump version and retry..." npm version patch --no-git-tag-version - pnpm publish --access public --no-git-checks || echo "Publish failed again for $pkg" + pnpm publish --access public --no-git-checks fiAlternatively, track failed packages and exit with a non-zero code if any publish fails.
191-192: Upgradeactions/github-scriptfrom v6 to v8.Version 6 is outdated. The latest version is v8.0.0 and should be used instead.
- uses: actions/github-script@v6 + uses: actions/github-script@v8
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
.github/workflows/release.yml(1 hunks)
🧰 Additional context used
🪛 actionlint (1.7.9)
.github/workflows/release.yml
153-153: "github.event.pull_request.head.ref" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/reference/security/secure-use#good-practices-for-mitigating-script-injection-attacks for more details
(expression)
192-192: the runner of "actions/github-script@v6" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Workers Builds: claim-db-worker
- GitHub Check: Workers Builds: create-db-worker
| - name: 🔖 Create unique preview tag | ||
| run: | | ||
| SAFE_REF=$(echo "${{ github.event.pull_request.head.ref }}" | tr '/' '-') | ||
| echo "PRE_TAG=pr${{ github.event.number }}-${SAFE_REF}-${{ github.run_id }}" >> $GITHUB_ENV | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
GitHub Actions security context variables shell injection best practices
💡 Result:
Short best-practices to avoid shell/command injection from GitHub Actions context variables
-
Never interpolate untrusted expressions directly into run: avoid patterns like run: echo "…${{ github.event.issue.title }}…". Treat github/event fields (title, body, ref, name, email, etc.) as untrusted. [1][2]
-
Use an intermediate env (recommended for inline scripts): assign the expression to env then reference the env var inside run (and quote it). Example:
env: TITLE: ${{ github.event.issue.title }}
run: echo "$TITLE"
This prevents expression evaluation from producing executable shell code. [1][2][3] -
Prefer an action (JavaScript/TypeScript action or composite action) and pass the value as an input instead of building shell scripts with the value. Actions receive arguments without generating shell code. [2]
-
Always quote shell variables and avoid eval/command substitution (no eval, no backticks, no $(...) on untrusted data). Use safe argument-passing (array args) where possible. [1][2]
-
Limit workflow permissions & tokens: give the least privilege (restrict GITHUB_TOKEN, avoid write when unnecessary), require environment reviewers for secrets, and avoid running untrusted PRs with high privileges (be careful with pull_request_target). [2][4]
-
Validate or sanitize inputs when you must use them (whitelisting, strict regex) and prefer transforming inputs in code (action) rather than in shell string interpolation. [1][3]
-
Monitor advisories and scan workflows: apply known fixes and use static analysis / CodeQL queries aimed at expression/script-injection patterns. Review recent disclosure examples to learn failure modes. [3][4]
Sources
- GitHub Docs — Script injections / Security hardening for Actions. [1][2]
- GitHub Security Lab — “Keeping your GitHub Actions and workflows secure: Untrusted input” (env pattern + CodeQL). [3]
- GitHub Security Lab advisories (real-world expression-injection incidents, examples). [4]
If you want, I can: (a) show a safe/unsafe example diff for your workflow, or (b) run a quick scan (static heuristics) of a workflow file you paste. Which would you prefer?
🌐 Web query:
github.event.pull_request.head.ref untrusted user input security
💡 Result:
Short answer: treat github.event.pull_request.head.ref as untrusted user input. It can be attacker-controlled (branch names from forks/PRs) and — if interpolated into shell/checkout/API calls in privileged workflows (especially pull_request_target) — can lead to expression/code injection, TOCTOU, or secret exfiltration.
Why it's dangerous (high-level):
- head.ref is attacker-controllable and has been used to inject expressions/commands into workflows. [1][2]
- Using it with pull_request_target (runs in repo context/secrets) + checkout/run of PR code can give an attacker repository-level code execution and secret theft. [2][3][4]
- Branch names are mutable (TOCTOU): resolving by name (ref) instead of immutable SHA can be exploited. [7]
Concrete mitigations (apply these immediately):
- Never directly interpolate into shell/run lines. Don’t do: run: git checkout ${{ github.event.pull_request.head.ref }}. Use SHA instead: ${{ github.event.pull_request.head.sha }} where you need exact commit. [1][5]
- Avoid actions/checkout@... with ref set to head.ref in pull_request_target workflows. Prefer checking out the base/default branch (github.base_ref or default branch) or use pull_request (unprivileged) triggers. [3][6]
- If you must act on PR content in a privileged workflow: do not run code from the PR until a trusted human or bot has approved/merged; restrict permissions (least privilege for GITHUB_TOKEN); avoid exposing secrets to PR runs; use environment branch-protection rules. [4][5][7]
- Sanitize and treat any github.event.* fields (including head.ref) as tainted: pass through action inputs/env vars (not inline shells), validate against an allowlist, and escape before use. Prefer actions that accept parameters rather than embedding into scripts. [5][6]
- Use code scanning/CI checks (CodeQL) and the GitHub Security Lab guidance to find expression-injection patterns. Monitor advisories for workflow vulnerabilities. [1][5]
References
[1] GitHub Security Lab — GHSL advisory and examples of expression injection via github.event.pull_request.head.ref.
[2] GitHub Security Lab — “Keeping your GitHub Actions and workflows secure: Untrusted input.”
[3] Spotipy advisory / real-world PoC showing pull_request_target + checkout of head.ref leads to secret exfiltration.
[4] GitHub Changelog — changes around pull_request_target/default-branch behavior and environment protections.
[5] Orca Security blog — workflow injection risks and guidance (never interpolate user input into shell).
[6] Guidance pages / docs showing safer checkout patterns (use base_ref/default, bind to env or use inputs).
[7] GitHub Security Lab — newer writeups on TOCTOU and mitigation strategies.
If you want, I can (a) scan a specific workflow file and highlight risky lines, or (b) produce a safe rewrite of a given workflow snippet. Which would you like?
🌐 Web query:
GitHub Actions official security documentation context variables
💡 Result:
Key official sources and short summary:
-
Contexts reference — lists available contexts (github, secrets, env, etc.) and warns the github context includes sensitive fields (github.token) and that some contexts are only available in specific workflow keys. [1]
-
Security hardening (Secure use reference) — advises treating certain contexts as untrusted input, avoid injecting context values directly into run/commands (use env variables instead), mask sensitive data, use least-privilege for GITHUB_TOKEN, rotate/delete exposed secrets, and pin third-party actions. [2]
-
Using secrets in GitHub Actions — details how to use secrets (secrets context), notes secrets aren’t passed to forked workflows (except GITHUB_TOKEN), recommends masking and safe handling (avoid printing, prefer env or STDIN), and shows limitations on where secrets are available. [3]
If you want, I can extract the exact warning lines and code examples from those pages.
Sources: [1] Contexts reference — GitHub Docs. [2] Security hardening for GitHub Actions — GitHub Docs. [3] Using secrets in GitHub Actions — GitHub Docs.
Fix critical shell injection vulnerability in preview tag creation.
The github.event.pull_request.head.ref context is untrusted user input that can be controlled by attackers through malicious branch names. Line 154 interpolates this directly into the shell script without proper protection. Using tr alone is insufficient—shell metacharacters like $, backticks, ;, and | can still execute arbitrary commands. GitHub's official security documentation requires passing untrusted context through environment variables to prevent expression evaluation.
Apply this diff:
- name: 🔖 Create unique preview tag
+ env:
+ HEAD_REF: ${{ github.event.pull_request.head.ref }}
run: |
- SAFE_REF=$(echo "${{ github.event.pull_request.head.ref }}" | tr '/' '-')
+ SAFE_REF=$(echo "${HEAD_REF}" | tr '/' '-')
echo "PRE_TAG=pr${{ github.event.number }}-${SAFE_REF}-${{ github.run_id }}" >> $GITHUB_ENV🧰 Tools
🪛 actionlint (1.7.9)
153-153: "github.event.pull_request.head.ref" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/reference/security/secure-use#good-practices-for-mitigating-script-injection-attacks for more details
(expression)
🤖 Prompt for AI Agents
.github/workflows/release.yml lines 152-156: the workflow currently interpolates
the untrusted github.event.pull_request.head.ref directly into a shell run
block, allowing shell injection; instead, pass that value into the runner via an
env mapping (e.g. SAFE_REF: ${{ github.event.pull_request.head.ref }}) so GitHub
sets it without shell evaluation, then in the run step read $SAFE_REF and
sanitize it before using (replace slashes with dashes and strip or collapse any
characters outside a safe whitelist such as A–Za–z0–9._-), then construct
PRE_TAG from trusted components and append it to GITHUB_ENV; ensure you never
inject the raw expression into the shell and only use the sanitized env value
when writing PRE_TAG.
Summary by CodeRabbit
New Features
Refactor
✏️ Tip: You can customize this high-level summary in your review settings.