diff --git a/.github/workflows/release-shared.yml b/.github/workflows/release-shared.yml index df0990c4d..726967779 100644 --- a/.github/workflows/release-shared.yml +++ b/.github/workflows/release-shared.yml @@ -134,6 +134,10 @@ jobs: run: chmod +x packages/cli-*/bin/supabase || true - name: Run smoke tests + # Force bash so ${VERSION}/${NPM_TAG} expand identically across the + # ubuntu/macos/windows matrix — windows-latest defaults to pwsh, which + # treats those as empty PowerShell variables (env vars are `$env:VAR`). + shell: bash run: pnpm run test:smoke -- --version "${VERSION}" --tag "${NPM_TAG}" working-directory: apps/cli @@ -164,7 +168,7 @@ jobs: run: pnpm exec bun apps/cli/scripts/sync-versions.ts --version "${VERSION}" - name: Publish to npm - run: pnpm exec bun apps/cli/scripts/publish.ts --tag ""${NPM_TAG}"" + run: pnpm exec bun apps/cli/scripts/publish.ts --tag "${NPM_TAG}" # Push the version tag to origin as soon as npm has the bytes, before any # downstream step that can fail. Without this, a failure in the GH-release diff --git a/apps/cli/src/legacy/commands/branches/branches.command.ts b/apps/cli/src/legacy/commands/branches/branches.command.ts index 223414eda..21d3eda1c 100644 --- a/apps/cli/src/legacy/commands/branches/branches.command.ts +++ b/apps/cli/src/legacy/commands/branches/branches.command.ts @@ -1,4 +1,5 @@ import { Command } from "effect/unstable/cli"; +import { withHiddenSubcommands } from "../../../shared/cli/hidden-flag.ts"; import { legacyBranchesListCommand } from "./list/list.command.ts"; import { legacyBranchesCreateCommand } from "./create/create.command.ts"; import { legacyBranchesGetCommand } from "./get/get.command.ts"; @@ -11,6 +12,7 @@ import { legacyBranchesDisableCommand } from "./disable/disable.command.ts"; export const legacyBranchesCommand = Command.make("branches").pipe( Command.withDescription("Manage Supabase preview branches."), Command.withShortDescription("Manage preview branches"), + withHiddenSubcommands(["disable"]), Command.withSubcommands([ legacyBranchesListCommand, legacyBranchesCreateCommand, diff --git a/apps/cli/src/legacy/commands/db/db.command.ts b/apps/cli/src/legacy/commands/db/db.command.ts index 60c2b170f..21e637f9a 100644 --- a/apps/cli/src/legacy/commands/db/db.command.ts +++ b/apps/cli/src/legacy/commands/db/db.command.ts @@ -1,4 +1,5 @@ import { Command } from "effect/unstable/cli"; +import { withHiddenSubcommands } from "../../../shared/cli/hidden-flag.ts"; import { legacyDbDiffCommand } from "./diff/diff.command.ts"; import { legacyDbDumpCommand } from "./dump/dump.command.ts"; import { legacyDbPushCommand } from "./push/push.command.ts"; @@ -16,6 +17,7 @@ import { legacyDbSchemaCommand } from "./schema/schema.command.ts"; export const legacyDbCommand = Command.make("db").pipe( Command.withDescription("Manage Postgres databases."), Command.withShortDescription("Manage databases"), + withHiddenSubcommands(["branch", "remote", "test"]), Command.withSubcommands([ legacyDbDiffCommand, legacyDbDumpCommand, diff --git a/apps/cli/src/shared/cli/hidden-flag.ts b/apps/cli/src/shared/cli/hidden-flag.ts index f83c877c6..2641242f6 100644 --- a/apps/cli/src/shared/cli/hidden-flag.ts +++ b/apps/cli/src/shared/cli/hidden-flag.ts @@ -13,6 +13,12 @@ export const LegacyHiddenFlags: Context.Reference> = Context defaultValue: () => new Set(), }); +export const LegacyHiddenSubcommands: Context.Reference> = Context.Reference< + ReadonlySet +>("supabase/legacy/LegacyHiddenSubcommands", { + defaultValue: () => new Set(), +}); + const hiddenFlagNames = new WeakMap>(); const collectSingleNames = (param: Param.Param): Array => { @@ -70,14 +76,36 @@ export const withHiddenFromConfig = return Command.annotate(cmd, LegacyHiddenFlags, hidden); }; +export const withHiddenSubcommands = + (names: ReadonlyArray) => + ( + cmd: Command.Command, + ): Command.Command => + Command.annotate(cmd, LegacyHiddenSubcommands, new Set(names)); + export const stripHiddenFlagsFromHelpDoc = (doc: HelpDoc.HelpDoc): HelpDoc.HelpDoc => { - const hidden = Context.get(doc.annotations, LegacyHiddenFlags); - if (hidden.size === 0) return doc; - const filteredFlags = doc.flags.filter((flag) => !hidden.has(flag.name)); - const filteredGlobalFlags = doc.globalFlags?.filter((flag) => !hidden.has(flag.name)); + const hiddenFlags = Context.get(doc.annotations, LegacyHiddenFlags); + const hiddenSubcommands = Context.get(doc.annotations, LegacyHiddenSubcommands); + if (hiddenFlags.size === 0 && hiddenSubcommands.size === 0) return doc; + const filteredFlags = doc.flags.filter((flag) => !hiddenFlags.has(flag.name)); + const filteredGlobalFlags = doc.globalFlags?.filter((flag) => !hiddenFlags.has(flag.name)); + const filteredSubcommands = doc.subcommands?.flatMap((group) => { + const commands = group.commands.filter((command) => !hiddenSubcommands.has(command.name)); + if (commands.length === 0) return []; + return [ + { + ...group, + commands: commands as unknown as readonly [ + HelpDoc.SubcommandDoc, + ...Array, + ], + }, + ]; + }); return { ...doc, flags: filteredFlags, + ...(filteredSubcommands !== undefined && { subcommands: filteredSubcommands }), ...(filteredGlobalFlags !== undefined && { globalFlags: filteredGlobalFlags }), }; }; diff --git a/apps/cli/src/shared/cli/hidden-flag.unit.test.ts b/apps/cli/src/shared/cli/hidden-flag.unit.test.ts index 474293a2c..356cd980f 100644 --- a/apps/cli/src/shared/cli/hidden-flag.unit.test.ts +++ b/apps/cli/src/shared/cli/hidden-flag.unit.test.ts @@ -3,9 +3,11 @@ import { Command, Flag, type HelpDoc } from "effect/unstable/cli"; import { describe, expect, it } from "vitest"; import { LegacyHiddenFlags, + LegacyHiddenSubcommands, stripHiddenFlagsFromHelpDoc, withHidden, withHiddenFromConfig, + withHiddenSubcommands, } from "./hidden-flag.ts"; const flagDoc = (name: string): HelpDoc.FlagDoc => ({ @@ -33,6 +35,15 @@ const helpDocWithHidden = ( annotations: Context.make(LegacyHiddenFlags, new Set(hidden)), }); +const helpDocWithHiddenSubcommands = ( + hidden: ReadonlyArray, + overrides: Partial, +): HelpDoc.HelpDoc => + helpDoc({ + ...overrides, + annotations: Context.make(LegacyHiddenSubcommands, new Set(hidden)), + }); + // Reach into the internal command shape to obtain the help doc the formatter // would render. Effect builds this from `Command.annotations`, which is the // contract `withHiddenFromConfig` relies on. @@ -110,6 +121,15 @@ describe("withHiddenFromConfig", () => { }); }); +describe("withHiddenSubcommands", () => { + it("adds hidden subcommand annotations to the command help doc", () => { + const cmd = Command.make("demo").pipe(withHiddenSubcommands(["legacy"])); + const annotated = Context.get(buildHelpDoc(cmd).annotations, LegacyHiddenSubcommands); + + expect([...annotated]).toEqual(["legacy"]); + }); +}); + describe("stripHiddenFlagsFromHelpDoc", () => { it("returns the doc unchanged when annotations are empty", () => { const doc = helpDoc({ flags: [flagDoc("foo")] }); @@ -134,4 +154,26 @@ describe("stripHiddenFlagsFromHelpDoc", () => { expect(stripped.globalFlags).toBeUndefined(); expect(stripped.flags.map((f) => f.name)).toEqual(["bar"]); }); + + it("filters hidden subcommands by the doc's annotation", () => { + const doc = helpDocWithHiddenSubcommands(["legacy"], { + subcommands: [ + { + group: undefined, + commands: [ + { + name: "visible", + alias: undefined, + shortDescription: "visible", + description: "visible", + }, + { name: "legacy", alias: undefined, shortDescription: "legacy", description: "legacy" }, + ], + }, + ], + }); + + const stripped = stripHiddenFlagsFromHelpDoc(doc); + expect(stripped.subcommands?.[0]?.commands.map((command) => command.name)).toEqual(["visible"]); + }); });