From 8e1189d862c5779f435fe832c8d9c79162ccd7ff Mon Sep 17 00:00:00 2001 From: Colum Ferry Date: Mon, 6 Jul 2026 20:20:45 +0100 Subject: [PATCH 1/2] fix(cli): remove "nano" from projects/branches create --size enum Go's --size EnumFlag (apps/cli-go/cmd/projects.go:34-55, reused by branches create at cmd/branches.go:212) is an 18-value list that omits "nano" (and "pico") and strictly rejects any other value at flag-parse time. TS listed "nano" as a valid choice on both projects create and branches create, silently succeeding where Go errors. Fixes CLI-1869. --- .../branches/create/create.command.ts | 1 - .../create/create.integration.test.ts | 46 ++++++++++++++- .../projects/create/create.command.ts | 1 - .../create/create.integration.test.ts | 56 ++++++++++++++++++- 4 files changed, 98 insertions(+), 6 deletions(-) diff --git a/apps/cli/src/legacy/commands/branches/create/create.command.ts b/apps/cli/src/legacy/commands/branches/create/create.command.ts index 69367b3f4d..468642e34c 100644 --- a/apps/cli/src/legacy/commands/branches/create/create.command.ts +++ b/apps/cli/src/legacy/commands/branches/create/create.command.ts @@ -44,7 +44,6 @@ const BRANCH_SIZES = [ "48xlarge_optimized_memory", "4xlarge", "8xlarge", - "nano", "small", "xlarge", ] as const; diff --git a/apps/cli/src/legacy/commands/branches/create/create.integration.test.ts b/apps/cli/src/legacy/commands/branches/create/create.integration.test.ts index fcbb44aa0f..8fb2a9e5d5 100644 --- a/apps/cli/src/legacy/commands/branches/create/create.integration.test.ts +++ b/apps/cli/src/legacy/commands/branches/create/create.integration.test.ts @@ -1,8 +1,10 @@ import type { V1CreateABranchOutput } from "@supabase/api/effect"; import { describe, expect, it } from "@effect/vitest"; -import { Effect, Exit, Option } from "effect"; +import { Cause, Effect, Exit, Option } from "effect"; +import { Command } from "effect/unstable/cli"; import { mockAnalytics, mockOutput } from "../../../../../tests/helpers/mocks.ts"; +import { LEGACY_GLOBAL_FLAGS } from "../../../../shared/legacy/global-flags.ts"; import { LEGACY_VALID_REF, buildLegacyTestRuntime, @@ -13,7 +15,7 @@ import { mockLegacyTelemetryStateTracked, useLegacyTempWorkdir, } from "../../../../../tests/helpers/legacy-mocks.ts"; -import type { LegacyBranchesCreateFlags } from "./create.command.ts"; +import { legacyBranchesCreateCommand, type LegacyBranchesCreateFlags } from "./create.command.ts"; import { legacyBranchesCreate } from "./create.handler.ts"; type CreatedBranch = typeof V1CreateABranchOutput.Type; @@ -282,4 +284,44 @@ describe("legacy branches create integration", () => { expect(cache.cached).toBe(true); }).pipe(Effect.provide(layer)); }); + + // Go parity (`apps/cli-go/cmd/projects.go:34-55`, reused by `branches create` + // at `apps/cli-go/cmd/branches.go:212`): Go's --size EnumFlag is an 18-value + // list that does not include "nano" (or "pico") and rejects any other value + // at flag-parse time. TS previously listed "nano" as a valid choice, silently + // succeeding where Go errors. + it.live("rejects --size nano at flag-parse time, matching Go's 18-value enum", () => { + const root = Command.make("supabase").pipe( + Command.withGlobalFlags(LEGACY_GLOBAL_FLAGS), + Command.withSubcommands([legacyBranchesCreateCommand]), + ); + + return Effect.gen(function* () { + const exit = yield* Effect.exit( + Command.runWith(root, { version: "0.0.0-test" })(["create", "--size", "nano"]), + ); + expect(Exit.isFailure(exit)).toBe(true); + if (Exit.isFailure(exit)) { + expect(rejectsInvalidSizeChoice(Cause.squash(exit.cause))).toBe(true); + } + }) as Effect.Effect; + }); }); + +// Distinguishes "the --size flag itself was rejected at parse time" from any +// other failure (e.g. a missing runtime service in this minimal test setup), +// so the regression test above can't pass for the wrong reason. +function rejectsInvalidSizeChoice(error: unknown): boolean { + if (typeof error !== "object" || error === null || !("errors" in error)) return false; + const { errors } = error; + if (!Array.isArray(errors)) return false; + return errors.some( + (candidate: unknown) => + typeof candidate === "object" && + candidate !== null && + "_tag" in candidate && + candidate._tag === "InvalidValue" && + "option" in candidate && + candidate.option === "size", + ); +} diff --git a/apps/cli/src/legacy/commands/projects/create/create.command.ts b/apps/cli/src/legacy/commands/projects/create/create.command.ts index 2486931973..8db6aa9995 100644 --- a/apps/cli/src/legacy/commands/projects/create/create.command.ts +++ b/apps/cli/src/legacy/commands/projects/create/create.command.ts @@ -27,7 +27,6 @@ const AWS_REGIONS = [ ] as const; const INSTANCE_SIZES = [ - "nano", "micro", "small", "medium", diff --git a/apps/cli/src/legacy/commands/projects/create/create.integration.test.ts b/apps/cli/src/legacy/commands/projects/create/create.integration.test.ts index f21138df6c..d38c35c021 100644 --- a/apps/cli/src/legacy/commands/projects/create/create.integration.test.ts +++ b/apps/cli/src/legacy/commands/projects/create/create.integration.test.ts @@ -1,8 +1,10 @@ import type { OrganizationResponseV1, V1CreateAProjectOutput } from "@supabase/api/effect"; import { describe, expect, it } from "@effect/vitest"; -import { Effect, Exit, Option } from "effect"; +import { Cause, Effect, Exit, Option } from "effect"; +import { Command } from "effect/unstable/cli"; import { mockOutput, mockTty } from "../../../../../tests/helpers/mocks.ts"; +import { LEGACY_GLOBAL_FLAGS } from "../../../../shared/legacy/global-flags.ts"; import { type LegacyApiResponse, type LegacyHttpMethod, @@ -13,7 +15,7 @@ import { mockLegacyTelemetryStateTracked, useLegacyTempWorkdir, } from "../../../../../tests/helpers/legacy-mocks.ts"; -import type { LegacyProjectsCreateFlags } from "./create.command.ts"; +import { legacyProjectsCreateCommand, type LegacyProjectsCreateFlags } from "./create.command.ts"; import { legacyProjectsCreate } from "./create.handler.ts"; const CREATED: typeof V1CreateAProjectOutput.Type = { @@ -451,4 +453,54 @@ describe("legacy projects create integration", () => { expect(cache.cached).toBe(false); }).pipe(Effect.provide(layer)); }); + + // Go parity (`apps/cli-go/cmd/projects.go:34-55`): Go's --size EnumFlag is an + // 18-value list that does not include "nano" (or "pico") and rejects any other + // value at flag-parse time. TS previously listed "nano" as a valid choice, + // silently succeeding where Go errors. + it.live("rejects --size nano at flag-parse time, matching Go's 18-value enum", () => { + const root = Command.make("supabase").pipe( + Command.withGlobalFlags(LEGACY_GLOBAL_FLAGS), + Command.withSubcommands([legacyProjectsCreateCommand]), + ); + + return Effect.gen(function* () { + const exit = yield* Effect.exit( + Command.runWith(root, { version: "0.0.0-test" })([ + "create", + "alpha", + "--org-id", + "acme", + "--db-password", + "s3cret-pass", + "--region", + "us-east-1", + "--size", + "nano", + ]), + ); + expect(Exit.isFailure(exit)).toBe(true); + if (Exit.isFailure(exit)) { + expect(rejectsInvalidSizeChoice(Cause.squash(exit.cause))).toBe(true); + } + }) as Effect.Effect; + }); }); + +// Distinguishes "the --size flag itself was rejected at parse time" from any +// other failure (e.g. a missing runtime service in this minimal test setup), +// so the regression test above can't pass for the wrong reason. +function rejectsInvalidSizeChoice(error: unknown): boolean { + if (typeof error !== "object" || error === null || !("errors" in error)) return false; + const { errors } = error; + if (!Array.isArray(errors)) return false; + return errors.some( + (candidate: unknown) => + typeof candidate === "object" && + candidate !== null && + "_tag" in candidate && + candidate._tag === "InvalidValue" && + "option" in candidate && + candidate.option === "size", + ); +} From 99ff98bea3eb456a16f8080a62e88bfa27d1b4ff Mon Sep 17 00:00:00 2001 From: Colum Ferry Date: Tue, 7 Jul 2026 16:44:20 +0100 Subject: [PATCH 2/2] fix(cli): match Go's --size enum declaration order for projects create (review: #5799) Reorder INSTANCE_SIZES to Go's size.Allowed declaration order (apps/cli-go/cmd/projects.go:35-52, shared with branches create), matching the convention already followed by BRANCH_SIZES, NAME_ID_FORMATS, and BRANCH_STATUSES elsewhere in the legacy shell. --- .../commands/projects/create/create.command.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/cli/src/legacy/commands/projects/create/create.command.ts b/apps/cli/src/legacy/commands/projects/create/create.command.ts index 8db6aa9995..75a93dfbb1 100644 --- a/apps/cli/src/legacy/commands/projects/create/create.command.ts +++ b/apps/cli/src/legacy/commands/projects/create/create.command.ts @@ -27,24 +27,24 @@ const AWS_REGIONS = [ ] as const; const INSTANCE_SIZES = [ - "micro", - "small", - "medium", "large", - "xlarge", - "2xlarge", - "4xlarge", - "8xlarge", + "medium", + "micro", "12xlarge", "16xlarge", "24xlarge", "24xlarge_high_memory", "24xlarge_optimized_cpu", "24xlarge_optimized_memory", + "2xlarge", "48xlarge", "48xlarge_high_memory", "48xlarge_optimized_cpu", "48xlarge_optimized_memory", + "4xlarge", + "8xlarge", + "small", + "xlarge", ] as const; const config = {