From 6fd83b8d5578526470b1739e0c80b94e02105927 Mon Sep 17 00:00:00 2001 From: Rene Turcios <95189389+CloudCorpRecords@users.noreply.github.com> Date: Sat, 17 Oct 2026 07:13:06 -0700 Subject: [PATCH] cli: don't create tables or indexes on the deployment during codegen `npx convex codegen` calls start_push to get the backend's evaluated schema for the generated bindings, but the request was not marked as a dry run, so the backend provisioned the schema and created tables and indexes on the deployment - contradicting the --help text that says codegen doesn't modify the code running on the deployment. Mark the start_push request as a dry run for codegen runs. `dryRun` is the protocol's established don't-mutate signal (the same one `npx convex deploy --dry-run` relies on). The spinner text is also codegen-aware now instead of claiming to upload functions. Fixes https://github.com/convex/convex-js/issues/199 Signed-off-by: Rene Turcios <95189389+CloudCorpRecords@users.noreply.github.com> --- src/cli/lib/components.codegen.test.ts | 16 ++++++++++++++ src/cli/lib/components.ts | 30 ++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 src/cli/lib/components.codegen.test.ts diff --git a/src/cli/lib/components.codegen.test.ts b/src/cli/lib/components.codegen.test.ts new file mode 100644 index 00000000..545cd030 --- /dev/null +++ b/src/cli/lib/components.codegen.test.ts @@ -0,0 +1,16 @@ +import { describe, expect, it } from "vitest"; +import { isDryRunStartPush } from "./components.js"; + +describe("isDryRunStartPush", () => { + it("marks codegen pushes as dry runs so start_push does not create tables or indexes", () => { + // A normal deploy mutates the deployment. + expect(isDryRunStartPush({ dryRun: false, codegen: false })).toBe(false); + // `npx convex deploy --dry-run` previews without mutating. + expect(isDryRunStartPush({ dryRun: true, codegen: false })).toBe(true); + // `npx convex codegen` is read-only: it needs the backend's evaluated + // schema for the generated bindings, but it must not create tables or + // indexes on the deployment (convex/convex-js#199). + expect(isDryRunStartPush({ dryRun: false, codegen: true })).toBe(true); + expect(isDryRunStartPush({ dryRun: true, codegen: true })).toBe(true); + }); +}); diff --git a/src/cli/lib/components.ts b/src/cli/lib/components.ts index c10105db..372de3aa 100644 --- a/src/cli/lib/components.ts +++ b/src/cli/lib/components.ts @@ -235,6 +235,22 @@ async function getUnchangedModuleHashesFromServer( ); } +/** + * Whether the start_push request should be marked as a dry run. + * + * Codegen needs the backend's evaluated schema to generate the TypeScript + * bindings, but it must not modify the deployment (`npx convex codegen + * --help` says it doesn't). Marking the push as a dry run keeps + * `start_push` from creating tables or indexes on the deployment. + * See https://github.com/convex/convex-js/issues/199. + */ +export function isDryRunStartPush(options: { + dryRun: boolean; + codegen: boolean; +}): boolean { + return options.dryRun || options.codegen; +} + async function startComponentsPushAndCodegen( ctx: Context, parentSpan: Span, @@ -459,7 +475,11 @@ async function startComponentsPushAndCodegen( } const startPushRequest = { adminKey: options.adminKey, - dryRun: options.dryRun, + // Codegen is a read-only operation: it needs the backend's evaluated + // schema for the generated bindings, but it must not create tables or + // indexes on the deployment. `dryRun` is the protocol's established + // "don't mutate" signal (`npx convex deploy --dry-run` relies on it). + dryRun: isDryRunStartPush(options), functions: projectConfig.functions, appDefinition, componentDefinitions, @@ -547,7 +567,13 @@ async function startComponentsPushAndCodegen( ); } - changeSpinner("Uploading functions to Convex..."); + changeSpinner( + // Codegen never uploads anything (see isDryRunStartPush above), so the + // deploy-oriented spinner text would be misleading here. + options.codegen + ? "Analyzing functions for codegen..." + : "Uploading functions to Convex...", + ); const startPushResponse = await parentSpan.enterAsync("startPush", (span) => startPush(ctx, span, startPushRequest, options), );