From 69e047d3acbd5e968c501f5a9d278d876172c6e5 Mon Sep 17 00:00:00 2001 From: Colum Ferry Date: Tue, 13 Oct 2026 12:14:17 +0100 Subject: [PATCH 1/2] feat(cli): warn at runtime on deprecated db pull and domains flags (CLI-2599) `domains create|get|reverify|activate|delete` now print a stderr warning when `--include-raw-output` is passed, naming `-o json` as the replacement and v3 as the removing major. The two existing `db pull` deprecation lines (`--use-pg-delta` and `--experimental` structured dump) now name v3 as well. --- apps/cli/src/command-internal/db-pull-run.ts | 4 +-- apps/cli/src/commands/db/pull/SIDE_EFFECTS.md | 8 +++--- .../commands/db/pull/pull.integration.test.ts | 14 ++++++---- apps/cli/src/commands/domains/SIDE_EFFECTS.md | 3 ++- .../domains/activate/activate.handler.ts | 3 +++ .../activate/activate.integration.test.ts | 27 ++++++++++++++++++- .../commands/domains/create/create.handler.ts | 3 +++ .../domains/create/create.integration.test.ts | 27 ++++++++++++++++++- .../commands/domains/delete/delete.handler.ts | 8 +++--- .../domains/delete/delete.integration.test.ts | 22 ++++++++++++--- .../commands/domains/domains.deprecation.ts | 16 +++++++++++ .../src/commands/domains/get/get.handler.ts | 3 +++ .../domains/get/get.integration.test.ts | 25 +++++++++++++++++ .../domains/reverify/reverify.handler.ts | 3 +++ .../reverify/reverify.integration.test.ts | 27 ++++++++++++++++++- 15 files changed, 171 insertions(+), 22 deletions(-) create mode 100644 apps/cli/src/commands/domains/domains.deprecation.ts diff --git a/apps/cli/src/command-internal/db-pull-run.ts b/apps/cli/src/command-internal/db-pull-run.ts index d01b9d1c54..1f92838e7c 100644 --- a/apps/cli/src/command-internal/db-pull-run.ts +++ b/apps/cli/src/command-internal/db-pull-run.ts @@ -90,7 +90,7 @@ export type { DbPullFlags }; // Established output contract; ends with a `.`. const DEPRECATION_LINE = - "Flag --use-pg-delta has been deprecated, use --declarative with [experimental.pgdelta] enabled = true in your config.toml instead."; + "Flag --use-pg-delta is deprecated and will be removed in v3, use --declarative with [experimental.pgdelta] enabled = true in your config.toml instead."; /** * Explains the in-sync non-zero exit instead of the generic "Try rerunning the command with @@ -107,7 +107,7 @@ const MIGRATION_FILE_MODE = 0o644; // export covers the same per-object-files outcome. Printed only when the experimental gate // selected this branch, not when `--declarative` already did. const EXPERIMENTAL_STRUCTURED_DUMP_DEPRECATION_LINE = - "The --experimental structured-dump mode for `db pull` is deprecated and will be removed in a future release. Use --declarative instead to pull the remote schema as per-object files."; + "The --experimental structured-dump mode for `db pull` is deprecated and will be removed in v3. Use --declarative instead to pull the remote schema as per-object files."; export type DbPullInvoke = { /** Skip printing `Finished supabase db pull.` for callers that must not emit it. */ diff --git a/apps/cli/src/commands/db/pull/SIDE_EFFECTS.md b/apps/cli/src/commands/db/pull/SIDE_EFFECTS.md index dd5da5ad77..81db74a60e 100644 --- a/apps/cli/src/commands/db/pull/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/db/pull/SIDE_EFFECTS.md @@ -163,7 +163,7 @@ Progress to stderr. Migration path: `Creating shadow database...`, `Diffing schemas[: ]`, `Schema written to `. Declarative path: `Preparing declarative schema export using pg-delta...`, `Declarative schema written to `. Plus the `--use-pg-delta` deprecation line, the -`--experimental` structured-dump deprecation line, and the history-update +`--experimental` structured-dump deprecation line (both name v3), and the history-update prompt. On success the PostRun line `Finished supabase db pull.` is printed to stdout. @@ -190,7 +190,7 @@ Progress strings still go to stderr; stdout carries a single structured envelope env var simply leaves unused on a non-linked target). The deprecated `--experimental` export honors `--project-ref` the same way `--declarative` does. -- `--use-pg-delta` is hidden and emits the cobra deprecation line to stderr. +- `--use-pg-delta` is hidden and emits `Flag --use-pg-delta is deprecated and will be removed in v3, use --declarative with [experimental.pgdelta] enabled = true in your config.toml instead.` to stderr. - Migration-style pulls always compare migrations with the live target; declarative files and `schema_paths` do not replace that baseline. - Bundled nontransactional files begin with @@ -201,8 +201,8 @@ Progress strings still go to stderr; stdout carries a single structured envelope empty diff is "No schema changes found". Stack-backed pulls never take this path. - The `--experimental` structured-dump branch (or the `SUPABASE_EXPERIMENTAL` project-`.env` equivalent) now runs the same in-process declarative export - as `--declarative`. It is deprecated: a warning pointing at `--declarative` - prints to stderr before the export. `--experimental --declarative` does not + as `--declarative`. It is deprecated and will be removed in v3: a warning pointing + at `--declarative` prints to stderr before the export. `--experimental --declarative` does not print that line. Output is the pg-delta declarative tree under `supabase/schemas` (plus `.pgdelta-export.json`), not Go's former `schemas/` + `cluster/` AST split. diff --git a/apps/cli/src/commands/db/pull/pull.integration.test.ts b/apps/cli/src/commands/db/pull/pull.integration.test.ts index a626a31c54..b0be6b3e14 100644 --- a/apps/cli/src/commands/db/pull/pull.integration.test.ts +++ b/apps/cli/src/commands/db/pull/pull.integration.test.ts @@ -1040,7 +1040,9 @@ describe("db pull", () => { const s = setup(tmp.current, { edgeStdout: EXPORT_JSON }); return Effect.gen(function* () { yield* dbPull(flags({ usePgDelta: Option.some(true) })); - expect(streamText(s.out, "stderr")).toContain("Flag --use-pg-delta has been deprecated"); + expect(streamText(s.out, "stderr")).toContain( + "Flag --use-pg-delta is deprecated and will be removed in v3, use --declarative with [experimental.pgdelta] enabled = true in your config.toml instead.\n", + ); const path = yield* Path.Path; expect(streamText(s.out, "stderr")).toContain( `Declarative schema written to ${path.join("supabase", "schemas")}\n`, @@ -1763,7 +1765,7 @@ describe("db pull", () => { expect(s.engineCalls[0]?.operation).toBe("export"); expect(streamText(s.out, "stderr")).toContain("Connecting to remote database..."); expect(streamText(s.out, "stderr")).toContain( - "The --experimental structured-dump mode for `db pull` is deprecated", + "The --experimental structured-dump mode for `db pull` is deprecated and will be removed in v3.", ); expect(streamText(s.out, "stderr")).toContain("Preparing declarative schema export"); }).pipe(Effect.provide(s.layer)); @@ -1818,7 +1820,7 @@ describe("db pull", () => { expect(s.engineCalls[0]?.operation).toBe("export"); expect(s.proxyCalls).toHaveLength(0); expect(streamText(s.out, "stderr")).toContain( - "The --experimental structured-dump mode for `db pull` is deprecated", + "The --experimental structured-dump mode for `db pull` is deprecated and will be removed in v3.", ); }).pipe(Effect.provide(s.layer)); }); @@ -1842,7 +1844,7 @@ describe("db pull", () => { expect(s.engineCalls[0]?.operation).toBe("export"); expect(streamText(s.out, "stderr")).toContain("Connecting to remote database..."); expect(streamText(s.out, "stderr")).toContain( - "The --experimental structured-dump mode for `db pull` is deprecated", + "The --experimental structured-dump mode for `db pull` is deprecated and will be removed in v3.", ); }).pipe(Effect.provide(s.layer)); }, @@ -2412,7 +2414,9 @@ describe("db remote commit", () => { expect(err).toContain( `Command "commit" is deprecated and will be removed in v3, use "db pull" instead.\n`, ); - expect(err).toContain("The --experimental structured-dump mode for `db pull` is deprecated"); + expect(err).toContain( + "The --experimental structured-dump mode for `db pull` is deprecated and will be removed in v3.", + ); const fs = yield* FileSystem.FileSystem; const path = yield* Path.Path; expect( diff --git a/apps/cli/src/commands/domains/SIDE_EFFECTS.md b/apps/cli/src/commands/domains/SIDE_EFFECTS.md index ed8baa4a33..5b46f04772 100644 --- a/apps/cli/src/commands/domains/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/domains/SIDE_EFFECTS.md @@ -93,8 +93,9 @@ suppressed on stderr. `delete` ignores `-o`. - `create` validates the CNAME via Cloudflare DNS-over-HTTPS (`https://1.1.1.1`, 10s timeout) before initializing; on failure it short-circuits before any POST. - All subcommands resolve the ref via `--project-ref` → `SUPABASE_PROJECT_ID` env → linked-project file. - The project-ref fallback env var is `SUPABASE_PROJECT_ID`. +- `--include-raw-output` is deprecated: passing it prints `Flag --include-raw-output is deprecated and will be removed in v3, use -o json instead.` to stderr before the API call, in every output mode, on all five subcommands (including `delete`). stdout is unaffected. - **Documented divergences from the old Go CLI (intentional):** - - `--include-raw-output` is declared as a normal boolean **on each subcommand** (the old Go CLI declared it as a persistent flag on the `domains` group). Two consequences: (a) it must appear after the subcommand name (`domains get --include-raw-output`) rather than before it (`domains --include-raw-output get`), matching how `--project-ref` is already handled shell-wide; (b) it cannot reproduce the old help-hiding or the `Flag --include-raw-output has been deprecated` stderr warning, which Effect CLI has no hook for. It still reproduces the behavioral effect (forces `-o json` when `-o` is unset/pretty); on `delete` it is inert. + - `--include-raw-output` is declared as a normal boolean **on each subcommand** (the old Go CLI declared it as a persistent flag on the `domains` group). Two consequences: (a) it must appear after the subcommand name (`domains get --include-raw-output`) rather than before it (`domains --include-raw-output get`), matching how `--project-ref` is already handled shell-wide; (b) it cannot reproduce the old help-hiding. It still reproduces the behavioral effect (forces `-o json` when `-o` is unset/pretty); on `delete` it is inert. - The degenerate `validation_records != 1` status message approximates a Go-style `%+v` struct dump (which embeds a non-deterministic pointer address). - Text-mode status output is newline-terminated even where the old CLI's output wasn't. Without the final newline, interactive shell prompts can redraw over the last status line, hiding the ACME TXT record. - In a structured `-o` mode the human status is suppressed on stderr, keeping stdout clean and matching the parity contract. diff --git a/apps/cli/src/commands/domains/activate/activate.handler.ts b/apps/cli/src/commands/domains/activate/activate.handler.ts index cc267ad126..37c3aab106 100644 --- a/apps/cli/src/commands/domains/activate/activate.handler.ts +++ b/apps/cli/src/commands/domains/activate/activate.handler.ts @@ -5,6 +5,7 @@ import { ProjectRefResolver } from "../../../config/project-ref.service.ts"; import { Output } from "../../../shared/output/output.service.ts"; import { LinkedProjectCache } from "../../../telemetry/linked-project-cache.service.ts"; import { TelemetryState } from "../../../telemetry/telemetry-state.service.ts"; +import { warnIncludeRawOutputDeprecated } from "../domains.deprecation.ts"; import { emitHostnameResult } from "../domains.emit.ts"; import { mapDomainsHttpError } from "../domains.errors.ts"; import { gateMapError } from "../../../command-internal/upgrade-suggest.ts"; @@ -21,6 +22,8 @@ export const domainsActivate = Effect.fn("domains.activate")(function* ( const linkedProjectCache = yield* LinkedProjectCache; const telemetryState = yield* TelemetryState; + yield* warnIncludeRawOutputDeprecated(flags.includeRawOutput); + const ref = yield* resolver.resolve(flags.projectRef); yield* Effect.gen(function* () { diff --git a/apps/cli/src/commands/domains/activate/activate.integration.test.ts b/apps/cli/src/commands/domains/activate/activate.integration.test.ts index eabbff260a..d97b8699f4 100644 --- a/apps/cli/src/commands/domains/activate/activate.integration.test.ts +++ b/apps/cli/src/commands/domains/activate/activate.integration.test.ts @@ -1,6 +1,6 @@ import { type V1GetHostnameConfigOutput } from "@supabase/api/effect"; import { describe, expect, it } from "@effect/vitest"; -import { Cause, Effect, Exit, Option } from "effect"; +import { Cause, Effect, Exit, Option, Schema } from "effect"; import { mockAnalytics, mockOutput } from "../../../../tests/helpers/mocks.ts"; import { @@ -67,6 +67,8 @@ function setup(opts: SetupOpts = {}) { const baseFlags = { projectRef: Option.none(), includeRawOutput: false }; +const DeprecationJsonObject = Schema.fromJsonString(Schema.Record(Schema.String, Schema.Unknown)); + describe("domains activate integration", () => { it.effect("suggests upgrade from entitlement_required envelope on 400", () => { const { layer, out, analytics, api } = setup({ @@ -127,6 +129,29 @@ describe("domains activate integration", () => { }).pipe(Effect.provide(layer)); }); + it.effect( + "warns on stderr and keeps stdout JSON parseable when --include-raw-output is set", + () => { + const { layer, out } = setup(); + return Effect.gen(function* () { + yield* domainsActivate({ projectRef: Option.none(), includeRawOutput: true }); + expect(out.stderrText).toBe( + "Flag --include-raw-output is deprecated and will be removed in v3, use -o json instead.\n", + ); + const parsed = yield* Schema.decodeEffect(DeprecationJsonObject)(out.stdoutText); + expect(parsed.custom_hostname).toBe("shop.acme.dev"); + }).pipe(Effect.provide(layer)); + }, + ); + + it.effect("does not warn when --include-raw-output is not set", () => { + const { layer, out } = setup(); + return Effect.gen(function* () { + yield* domainsActivate(baseFlags); + expect(out.stderrText).not.toContain("deprecated"); + }).pipe(Effect.provide(layer)); + }); + it.effect("forces Go JSON output when --include-raw-output is set", () => { const { layer, out } = setup(); return Effect.gen(function* () { diff --git a/apps/cli/src/commands/domains/create/create.handler.ts b/apps/cli/src/commands/domains/create/create.handler.ts index 96d91eca98..9d3b0cb4dd 100644 --- a/apps/cli/src/commands/domains/create/create.handler.ts +++ b/apps/cli/src/commands/domains/create/create.handler.ts @@ -8,6 +8,7 @@ import { Output } from "../../../shared/output/output.service.ts"; import { LinkedProjectCache } from "../../../telemetry/linked-project-cache.service.ts"; import { TelemetryState } from "../../../telemetry/telemetry-state.service.ts"; import { verifyCname } from "../domains.cname.ts"; +import { warnIncludeRawOutputDeprecated } from "../domains.deprecation.ts"; import { emitHostnameResult } from "../domains.emit.ts"; import { mapDomainsHttpError } from "../domains.errors.ts"; import { gateMapError } from "../../../command-internal/upgrade-suggest.ts"; @@ -24,6 +25,8 @@ export const domainsCreate = Effect.fn("domains.create")(function* (flags: Domai const linkedProjectCache = yield* LinkedProjectCache; const telemetryState = yield* TelemetryState; + yield* warnIncludeRawOutputDeprecated(flags.includeRawOutput); + const ref = yield* resolver.resolve(flags.projectRef); // Write the linked-project cache and persist the telemetry state file on diff --git a/apps/cli/src/commands/domains/create/create.integration.test.ts b/apps/cli/src/commands/domains/create/create.integration.test.ts index 07571e7642..ca7cfb3697 100644 --- a/apps/cli/src/commands/domains/create/create.integration.test.ts +++ b/apps/cli/src/commands/domains/create/create.integration.test.ts @@ -1,6 +1,6 @@ import { type V1GetHostnameConfigOutput } from "@supabase/api/effect"; import { describe, expect, it } from "@effect/vitest"; -import { Cause, Deferred, Effect, Exit, Fiber, Option } from "effect"; +import { Cause, Deferred, Effect, Exit, Fiber, Option, Schema } from "effect"; import { TestClock } from "effect/testing"; import { mockAnalytics, mockOutput } from "../../../../tests/helpers/mocks.ts"; @@ -110,6 +110,8 @@ function postedToInitialize(api: { requests: ReadonlyArray<{ url: string }> }): return api.requests.some((r) => r.url.includes("/custom-hostname/initialize")); } +const DeprecationJsonObject = Schema.fromJsonString(Schema.Record(Schema.String, Schema.Unknown)); + describe("domains create integration", () => { it.effect("verifies the CNAME, creates the hostname, and prints status to stderr", () => { const { layer, out, api, telemetry, linkedProjectCache } = setup(); @@ -262,6 +264,29 @@ describe("domains create integration", () => { }).pipe(Effect.provide(layer)); }); + it.effect( + "warns on stderr and keeps stdout JSON parseable when --include-raw-output is set", + () => { + const { layer, out } = setup(); + return Effect.gen(function* () { + yield* domainsCreate(flags({ includeRawOutput: true })); + expect(out.stderrText).toBe( + "Flag --include-raw-output is deprecated and will be removed in v3, use -o json instead.\n", + ); + const parsed = yield* Schema.decodeEffect(DeprecationJsonObject)(out.stdoutText); + expect(parsed.custom_hostname).toBe(CUSTOM_HOSTNAME); + }).pipe(Effect.provide(layer)); + }, + ); + + it.effect("does not warn when --include-raw-output is not set", () => { + const { layer, out } = setup(); + return Effect.gen(function* () { + yield* domainsCreate(flags()); + expect(out.stderrText).not.toContain("deprecated"); + }).pipe(Effect.provide(layer)); + }); + it.effect("forces Go JSON output when --include-raw-output is set", () => { const { layer, out } = setup(); return Effect.gen(function* () { diff --git a/apps/cli/src/commands/domains/delete/delete.handler.ts b/apps/cli/src/commands/domains/delete/delete.handler.ts index 0cab96bb18..c8286c3017 100644 --- a/apps/cli/src/commands/domains/delete/delete.handler.ts +++ b/apps/cli/src/commands/domains/delete/delete.handler.ts @@ -5,6 +5,7 @@ import { ProjectRefResolver } from "../../../config/project-ref.service.ts"; import { Output } from "../../../shared/output/output.service.ts"; import { LinkedProjectCache } from "../../../telemetry/linked-project-cache.service.ts"; import { TelemetryState } from "../../../telemetry/telemetry-state.service.ts"; +import { warnIncludeRawOutputDeprecated } from "../domains.deprecation.ts"; import { mapDomainsHttpError } from "../domains.errors.ts"; import type { DomainsDeleteFlags } from "./delete.command.ts"; @@ -12,9 +13,8 @@ const mapDeleteError = mapDomainsHttpError("delete"); const DELETE_SUCCESS_MESSAGE = "Deleted custom hostname config successfully."; -// `flags.includeRawOutput` is unread: `--include-raw-output` is a persistent flag on the -// `domains` group, so it's accepted on `delete` too, but ignored — delete has no response -// body to encode. Asserted by the "ignores --include-raw-output" integration test. +// `--include-raw-output` is accepted on `delete` but inert (only the deprecation warning fires): +// delete has no response body to encode. export const domainsDelete = Effect.fn("domains.delete")(function* (flags: DomainsDeleteFlags) { const output = yield* Output; const api = yield* CommandPlatformApi; @@ -22,6 +22,8 @@ export const domainsDelete = Effect.fn("domains.delete")(function* (flags: Domai const linkedProjectCache = yield* LinkedProjectCache; const telemetryState = yield* TelemetryState; + yield* warnIncludeRawOutputDeprecated(flags.includeRawOutput); + const ref = yield* resolver.resolve(flags.projectRef); yield* Effect.gen(function* () { diff --git a/apps/cli/src/commands/domains/delete/delete.integration.test.ts b/apps/cli/src/commands/domains/delete/delete.integration.test.ts index e874de9286..261f2c8854 100644 --- a/apps/cli/src/commands/domains/delete/delete.integration.test.ts +++ b/apps/cli/src/commands/domains/delete/delete.integration.test.ts @@ -84,12 +84,26 @@ describe("domains delete integration", () => { }).pipe(Effect.provide(layer)); }); - it.effect("ignores --include-raw-output (inert on delete, Go parity)", () => { + it.effect( + "is inert apart from the stderr deprecation warning when --include-raw-output is set", + () => { + const { layer, out } = setup({ goOutput: "json" }); + return Effect.gen(function* () { + yield* domainsDelete({ projectRef: Option.none(), includeRawOutput: true }); + expect(out.stdoutText).toBe(""); + expect(out.stderrText).toBe( + "Flag --include-raw-output is deprecated and will be removed in v3, use -o json instead.\n" + + "Deleted custom hostname config successfully.\n", + ); + }).pipe(Effect.provide(layer)); + }, + ); + + it.effect("does not warn when --include-raw-output is not set", () => { const { layer, out } = setup(); return Effect.gen(function* () { - yield* domainsDelete({ projectRef: Option.none(), includeRawOutput: true }); - expect(out.stdoutText).toBe(""); - expect(out.stderrText).toBe("Deleted custom hostname config successfully.\n"); + yield* domainsDelete(baseFlags); + expect(out.stderrText).not.toContain("deprecated"); }).pipe(Effect.provide(layer)); }); diff --git a/apps/cli/src/commands/domains/domains.deprecation.ts b/apps/cli/src/commands/domains/domains.deprecation.ts new file mode 100644 index 0000000000..4f609b4034 --- /dev/null +++ b/apps/cli/src/commands/domains/domains.deprecation.ts @@ -0,0 +1,16 @@ +import { Effect } from "effect"; + +import { Output } from "../../shared/output/output.service.ts"; + +const INCLUDE_RAW_OUTPUT_DEPRECATION = + "Flag --include-raw-output is deprecated and will be removed in v3, use -o json instead.\n"; + +export const warnIncludeRawOutputDeprecated = Effect.fnUntraced(function* ( + includeRawOutput: boolean, +) { + if (!includeRawOutput) { + return; + } + const output = yield* Output; + yield* output.raw(INCLUDE_RAW_OUTPUT_DEPRECATION, "stderr"); +}); diff --git a/apps/cli/src/commands/domains/get/get.handler.ts b/apps/cli/src/commands/domains/get/get.handler.ts index e1c1faa711..3c6c10caf9 100644 --- a/apps/cli/src/commands/domains/get/get.handler.ts +++ b/apps/cli/src/commands/domains/get/get.handler.ts @@ -5,6 +5,7 @@ import { ProjectRefResolver } from "../../../config/project-ref.service.ts"; import { Output } from "../../../shared/output/output.service.ts"; import { LinkedProjectCache } from "../../../telemetry/linked-project-cache.service.ts"; import { TelemetryState } from "../../../telemetry/telemetry-state.service.ts"; +import { warnIncludeRawOutputDeprecated } from "../domains.deprecation.ts"; import { emitHostnameResult } from "../domains.emit.ts"; import { mapDomainsHttpError } from "../domains.errors.ts"; import { gateMapError } from "../../../command-internal/upgrade-suggest.ts"; @@ -19,6 +20,8 @@ export const domainsGet = Effect.fn("domains.get")(function* (flags: DomainsGetF const linkedProjectCache = yield* LinkedProjectCache; const telemetryState = yield* TelemetryState; + yield* warnIncludeRawOutputDeprecated(flags.includeRawOutput); + const ref = yield* resolver.resolve(flags.projectRef); // Write the linked-project cache and persist the telemetry state file on diff --git a/apps/cli/src/commands/domains/get/get.integration.test.ts b/apps/cli/src/commands/domains/get/get.integration.test.ts index df325190ba..0ca49954dd 100644 --- a/apps/cli/src/commands/domains/get/get.integration.test.ts +++ b/apps/cli/src/commands/domains/get/get.integration.test.ts @@ -80,6 +80,8 @@ function setup(opts: SetupOpts = {}) { const baseFlags = { projectRef: Option.none(), includeRawOutput: false }; +const DeprecationJsonObject = Schema.fromJsonString(Schema.Record(Schema.String, Schema.Unknown)); + describe("domains get integration", () => { it.effect("prints the hostname status to stderr in text mode", () => { const { layer, out, telemetry, linkedProjectCache } = setup(); @@ -336,6 +338,29 @@ describe("domains get integration", () => { }).pipe(Effect.provide(layer)); }); + it.effect( + "warns on stderr and keeps stdout JSON parseable when --include-raw-output is set", + () => { + const { layer, out } = setup(); + return Effect.gen(function* () { + yield* domainsGet({ projectRef: Option.none(), includeRawOutput: true }); + expect(out.stderrText).toBe( + "Flag --include-raw-output is deprecated and will be removed in v3, use -o json instead.\n", + ); + const parsed = yield* Schema.decodeEffect(DeprecationJsonObject)(out.stdoutText); + expect(parsed.custom_hostname).toBe("shop.acme.dev"); + }).pipe(Effect.provide(layer)); + }, + ); + + it.effect("does not warn when --include-raw-output is not set", () => { + const { layer, out } = setup(); + return Effect.gen(function* () { + yield* domainsGet(baseFlags); + expect(out.stderrText).not.toContain("deprecated"); + }).pipe(Effect.provide(layer)); + }); + it.effect("forces Go JSON output when --include-raw-output is set", () => { const { layer, out } = setup(); return Effect.gen(function* () { diff --git a/apps/cli/src/commands/domains/reverify/reverify.handler.ts b/apps/cli/src/commands/domains/reverify/reverify.handler.ts index 6b2e47c58d..7b9c4dbb56 100644 --- a/apps/cli/src/commands/domains/reverify/reverify.handler.ts +++ b/apps/cli/src/commands/domains/reverify/reverify.handler.ts @@ -5,6 +5,7 @@ import { ProjectRefResolver } from "../../../config/project-ref.service.ts"; import { Output } from "../../../shared/output/output.service.ts"; import { LinkedProjectCache } from "../../../telemetry/linked-project-cache.service.ts"; import { TelemetryState } from "../../../telemetry/telemetry-state.service.ts"; +import { warnIncludeRawOutputDeprecated } from "../domains.deprecation.ts"; import { emitHostnameResult } from "../domains.emit.ts"; import { mapDomainsHttpError } from "../domains.errors.ts"; import { gateMapError } from "../../../command-internal/upgrade-suggest.ts"; @@ -21,6 +22,8 @@ export const domainsReverify = Effect.fn("domains.reverify")(function* ( const linkedProjectCache = yield* LinkedProjectCache; const telemetryState = yield* TelemetryState; + yield* warnIncludeRawOutputDeprecated(flags.includeRawOutput); + const ref = yield* resolver.resolve(flags.projectRef); yield* Effect.gen(function* () { diff --git a/apps/cli/src/commands/domains/reverify/reverify.integration.test.ts b/apps/cli/src/commands/domains/reverify/reverify.integration.test.ts index ad3965fb74..1dff196839 100644 --- a/apps/cli/src/commands/domains/reverify/reverify.integration.test.ts +++ b/apps/cli/src/commands/domains/reverify/reverify.integration.test.ts @@ -1,6 +1,6 @@ import { type V1GetHostnameConfigOutput } from "@supabase/api/effect"; import { describe, expect, it } from "@effect/vitest"; -import { Cause, Effect, Exit, Option } from "effect"; +import { Cause, Effect, Exit, Option, Schema } from "effect"; import { mockAnalytics, mockOutput } from "../../../../tests/helpers/mocks.ts"; import { @@ -67,6 +67,8 @@ function setup(opts: SetupOpts = {}) { const baseFlags = { projectRef: Option.none(), includeRawOutput: false }; +const DeprecationJsonObject = Schema.fromJsonString(Schema.Record(Schema.String, Schema.Unknown)); + describe("domains reverify integration", () => { it.effect("suggests upgrade from entitlement_required envelope on 400", () => { const { layer, out, analytics, api } = setup({ @@ -126,6 +128,29 @@ describe("domains reverify integration", () => { }).pipe(Effect.provide(layer)); }); + it.effect( + "warns on stderr and keeps stdout JSON parseable when --include-raw-output is set", + () => { + const { layer, out } = setup(); + return Effect.gen(function* () { + yield* domainsReverify({ projectRef: Option.none(), includeRawOutput: true }); + expect(out.stderrText).toBe( + "Flag --include-raw-output is deprecated and will be removed in v3, use -o json instead.\n", + ); + const parsed = yield* Schema.decodeEffect(DeprecationJsonObject)(out.stdoutText); + expect(parsed.custom_hostname).toBe("shop.acme.dev"); + }).pipe(Effect.provide(layer)); + }, + ); + + it.effect("does not warn when --include-raw-output is not set", () => { + const { layer, out } = setup(); + return Effect.gen(function* () { + yield* domainsReverify(baseFlags); + expect(out.stderrText).not.toContain("deprecated"); + }).pipe(Effect.provide(layer)); + }); + it.effect("forces Go JSON output when --include-raw-output is set", () => { const { layer, out } = setup(); return Effect.gen(function* () { From c2850bed3c4bfffa0dcdc4c5590284815b142635 Mon Sep 17 00:00:00 2001 From: Colum Ferry Date: Tue, 13 Oct 2026 15:21:58 +0100 Subject: [PATCH 2/2] fix(cli): warn on any explicit --include-raw-output value and dedupe tests (CLI-2599) --- apps/cli/src/commands/domains/SIDE_EFFECTS.md | 4 +-- .../domains/activate/activate.command.ts | 2 +- .../domains/activate/activate.handler.ts | 7 ++++-- .../activate/activate.integration.test.ts | 21 ++++++++++------ .../commands/domains/create/create.command.ts | 2 +- .../commands/domains/create/create.handler.ts | 7 ++++-- .../domains/create/create.integration.test.ts | 23 ++++++++++------- .../commands/domains/delete/delete.command.ts | 2 +- .../domains/delete/delete.integration.test.ts | 14 +++++++---- .../commands/domains/domains.deprecation.ts | 6 ++--- .../src/commands/domains/get/get.command.ts | 2 +- .../src/commands/domains/get/get.handler.ts | 7 ++++-- .../domains/get/get.integration.test.ts | 25 +++++++++++-------- .../domains/reverify/reverify.command.ts | 2 +- .../domains/reverify/reverify.handler.ts | 7 ++++-- .../reverify/reverify.integration.test.ts | 21 ++++++++++------ 16 files changed, 94 insertions(+), 58 deletions(-) diff --git a/apps/cli/src/commands/domains/SIDE_EFFECTS.md b/apps/cli/src/commands/domains/SIDE_EFFECTS.md index 5b46f04772..9c2e7404f9 100644 --- a/apps/cli/src/commands/domains/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/domains/SIDE_EFFECTS.md @@ -93,9 +93,9 @@ suppressed on stderr. `delete` ignores `-o`. - `create` validates the CNAME via Cloudflare DNS-over-HTTPS (`https://1.1.1.1`, 10s timeout) before initializing; on failure it short-circuits before any POST. - All subcommands resolve the ref via `--project-ref` → `SUPABASE_PROJECT_ID` env → linked-project file. - The project-ref fallback env var is `SUPABASE_PROJECT_ID`. -- `--include-raw-output` is deprecated: passing it prints `Flag --include-raw-output is deprecated and will be removed in v3, use -o json instead.` to stderr before the API call, in every output mode, on all five subcommands (including `delete`). stdout is unaffected. +- `--include-raw-output` is deprecated: passing it with any value (including `--include-raw-output=false`) prints `Flag --include-raw-output is deprecated and will be removed in v3, use -o json instead.` to stderr before the API call, in every output mode, on all five subcommands (including `delete`). stdout is unaffected. - **Documented divergences from the old Go CLI (intentional):** - - `--include-raw-output` is declared as a normal boolean **on each subcommand** (the old Go CLI declared it as a persistent flag on the `domains` group). Two consequences: (a) it must appear after the subcommand name (`domains get --include-raw-output`) rather than before it (`domains --include-raw-output get`), matching how `--project-ref` is already handled shell-wide; (b) it cannot reproduce the old help-hiding. It still reproduces the behavioral effect (forces `-o json` when `-o` is unset/pretty); on `delete` it is inert. + - `--include-raw-output` is declared as an optional boolean **on each subcommand** (the old Go CLI declared it as a persistent flag on the `domains` group). Two consequences: (a) it must appear after the subcommand name (`domains get --include-raw-output`) rather than before it (`domains --include-raw-output get`), matching how `--project-ref` is already handled shell-wide; (b) it cannot reproduce the old help-hiding. It still reproduces the behavioral effect (forces `-o json` when the value is `true` and `-o` is unset/pretty); on `delete` it is inert. - The degenerate `validation_records != 1` status message approximates a Go-style `%+v` struct dump (which embeds a non-deterministic pointer address). - Text-mode status output is newline-terminated even where the old CLI's output wasn't. Without the final newline, interactive shell prompts can redraw over the last status line, hiding the ACME TXT record. - In a structured `-o` mode the human status is suppressed on stderr, keeping stdout clean and matching the parity contract. diff --git a/apps/cli/src/commands/domains/activate/activate.command.ts b/apps/cli/src/commands/domains/activate/activate.command.ts index e4cb5d7b8a..9a83478686 100644 --- a/apps/cli/src/commands/domains/activate/activate.command.ts +++ b/apps/cli/src/commands/domains/activate/activate.command.ts @@ -13,7 +13,7 @@ const config = { ), includeRawOutput: Flag.Boolean("include-raw-output").pipe( Flag.withDescription("(Deprecated) use -o json instead."), - Flag.withDefault(false), + Flag.optional, ), } as const; diff --git a/apps/cli/src/commands/domains/activate/activate.handler.ts b/apps/cli/src/commands/domains/activate/activate.handler.ts index 37c3aab106..e3844d3436 100644 --- a/apps/cli/src/commands/domains/activate/activate.handler.ts +++ b/apps/cli/src/commands/domains/activate/activate.handler.ts @@ -1,4 +1,4 @@ -import { Effect } from "effect"; +import { Effect, Option } from "effect"; import { CommandPlatformApi } from "../../../auth/command-platform-api.service.ts"; import { ProjectRefResolver } from "../../../config/project-ref.service.ts"; @@ -35,6 +35,9 @@ export const domainsActivate = Effect.fn("domains.activate")(function* ( ); yield* activating?.clear ?? Effect.void; - yield* emitHostnameResult(response, flags.includeRawOutput); + yield* emitHostnameResult( + response, + Option.getOrElse(flags.includeRawOutput, () => false), + ); }).pipe(Effect.ensuring(linkedProjectCache.cache(ref)), Effect.ensuring(telemetryState.flush)); }); diff --git a/apps/cli/src/commands/domains/activate/activate.integration.test.ts b/apps/cli/src/commands/domains/activate/activate.integration.test.ts index d97b8699f4..f352793246 100644 --- a/apps/cli/src/commands/domains/activate/activate.integration.test.ts +++ b/apps/cli/src/commands/domains/activate/activate.integration.test.ts @@ -65,7 +65,7 @@ function setup(opts: SetupOpts = {}) { return { layer, out, api, analytics, telemetry, linkedProjectCache }; } -const baseFlags = { projectRef: Option.none(), includeRawOutput: false }; +const baseFlags = { projectRef: Option.none(), includeRawOutput: Option.none() }; const DeprecationJsonObject = Schema.fromJsonString(Schema.Record(Schema.String, Schema.Unknown)); @@ -134,7 +134,7 @@ describe("domains activate integration", () => { () => { const { layer, out } = setup(); return Effect.gen(function* () { - yield* domainsActivate({ projectRef: Option.none(), includeRawOutput: true }); + yield* domainsActivate({ projectRef: Option.none(), includeRawOutput: Option.some(true) }); expect(out.stderrText).toBe( "Flag --include-raw-output is deprecated and will be removed in v3, use -o json instead.\n", ); @@ -144,19 +144,24 @@ describe("domains activate integration", () => { }, ); - it.effect("does not warn when --include-raw-output is not set", () => { + it.effect("warns but does not force JSON when --include-raw-output=false", () => { const { layer, out } = setup(); return Effect.gen(function* () { - yield* domainsActivate(baseFlags); - expect(out.stderrText).not.toContain("deprecated"); + yield* domainsActivate({ projectRef: Option.none(), includeRawOutput: Option.some(false) }); + expect( + out.stderrText.startsWith( + "Flag --include-raw-output is deprecated and will be removed in v3, use -o json instead.\n", + ), + ).toBe(true); + expect(out.stdoutText).toBe(""); }).pipe(Effect.provide(layer)); }); - it.effect("forces Go JSON output when --include-raw-output is set", () => { + it.effect("does not warn when --include-raw-output is not set", () => { const { layer, out } = setup(); return Effect.gen(function* () { - yield* domainsActivate({ projectRef: Option.none(), includeRawOutput: true }); - expect(out.stdoutText.startsWith("{")).toBe(true); + yield* domainsActivate(baseFlags); + expect(out.stderrText).not.toContain("deprecated"); }).pipe(Effect.provide(layer)); }); diff --git a/apps/cli/src/commands/domains/create/create.command.ts b/apps/cli/src/commands/domains/create/create.command.ts index 008fde822d..f8f7737ed6 100644 --- a/apps/cli/src/commands/domains/create/create.command.ts +++ b/apps/cli/src/commands/domains/create/create.command.ts @@ -16,7 +16,7 @@ const config = { ), includeRawOutput: Flag.Boolean("include-raw-output").pipe( Flag.withDescription("(Deprecated) use -o json instead."), - Flag.withDefault(false), + Flag.optional, ), } as const; diff --git a/apps/cli/src/commands/domains/create/create.handler.ts b/apps/cli/src/commands/domains/create/create.handler.ts index 9d3b0cb4dd..76336a82aa 100644 --- a/apps/cli/src/commands/domains/create/create.handler.ts +++ b/apps/cli/src/commands/domains/create/create.handler.ts @@ -1,4 +1,4 @@ -import { Effect } from "effect"; +import { Effect, Option } from "effect"; import * as HttpClient from "effect/http/HttpClient"; import { CommandPlatformApi } from "../../../auth/command-platform-api.service.ts"; @@ -51,6 +51,9 @@ export const domainsCreate = Effect.fn("domains.create")(function* (flags: Domai ); yield* creating?.clear ?? Effect.void; - yield* emitHostnameResult(response, flags.includeRawOutput); + yield* emitHostnameResult( + response, + Option.getOrElse(flags.includeRawOutput, () => false), + ); }).pipe(Effect.ensuring(linkedProjectCache.cache(ref)), Effect.ensuring(telemetryState.flush)); }); diff --git a/apps/cli/src/commands/domains/create/create.integration.test.ts b/apps/cli/src/commands/domains/create/create.integration.test.ts index ca7cfb3697..6ccb0b2d23 100644 --- a/apps/cli/src/commands/domains/create/create.integration.test.ts +++ b/apps/cli/src/commands/domains/create/create.integration.test.ts @@ -98,11 +98,11 @@ function setup(opts: SetupOpts = {}) { return { layer, out, api, analytics, telemetry, linkedProjectCache }; } -function flags(over: Partial<{ includeRawOutput: boolean }> = {}) { +function flags(over: Partial<{ includeRawOutput: Option.Option }> = {}) { return { projectRef: Option.none(), customHostname: CUSTOM_HOSTNAME, - includeRawOutput: over.includeRawOutput ?? false, + includeRawOutput: over.includeRawOutput ?? Option.none(), }; } @@ -269,7 +269,7 @@ describe("domains create integration", () => { () => { const { layer, out } = setup(); return Effect.gen(function* () { - yield* domainsCreate(flags({ includeRawOutput: true })); + yield* domainsCreate(flags({ includeRawOutput: Option.some(true) })); expect(out.stderrText).toBe( "Flag --include-raw-output is deprecated and will be removed in v3, use -o json instead.\n", ); @@ -279,19 +279,24 @@ describe("domains create integration", () => { }, ); - it.effect("does not warn when --include-raw-output is not set", () => { + it.effect("warns but does not force JSON when --include-raw-output=false", () => { const { layer, out } = setup(); return Effect.gen(function* () { - yield* domainsCreate(flags()); - expect(out.stderrText).not.toContain("deprecated"); + yield* domainsCreate(flags({ includeRawOutput: Option.some(false) })); + expect( + out.stderrText.startsWith( + "Flag --include-raw-output is deprecated and will be removed in v3, use -o json instead.\n", + ), + ).toBe(true); + expect(out.stdoutText).toBe(""); }).pipe(Effect.provide(layer)); }); - it.effect("forces Go JSON output when --include-raw-output is set", () => { + it.effect("does not warn when --include-raw-output is not set", () => { const { layer, out } = setup(); return Effect.gen(function* () { - yield* domainsCreate(flags({ includeRawOutput: true })); - expect(out.stdoutText.startsWith("{")).toBe(true); + yield* domainsCreate(flags()); + expect(out.stderrText).not.toContain("deprecated"); }).pipe(Effect.provide(layer)); }); diff --git a/apps/cli/src/commands/domains/delete/delete.command.ts b/apps/cli/src/commands/domains/delete/delete.command.ts index 0206785052..c45cecd071 100644 --- a/apps/cli/src/commands/domains/delete/delete.command.ts +++ b/apps/cli/src/commands/domains/delete/delete.command.ts @@ -13,7 +13,7 @@ const config = { ), includeRawOutput: Flag.Boolean("include-raw-output").pipe( Flag.withDescription("(Deprecated) use -o json instead."), - Flag.withDefault(false), + Flag.optional, ), } as const; diff --git a/apps/cli/src/commands/domains/delete/delete.integration.test.ts b/apps/cli/src/commands/domains/delete/delete.integration.test.ts index 261f2c8854..f7e62c7c80 100644 --- a/apps/cli/src/commands/domains/delete/delete.integration.test.ts +++ b/apps/cli/src/commands/domains/delete/delete.integration.test.ts @@ -43,7 +43,7 @@ function setup(opts: SetupOpts = {}) { return { layer, out, api, telemetry, linkedProjectCache }; } -const baseFlags = { projectRef: Option.none(), includeRawOutput: false }; +const baseFlags = { projectRef: Option.none(), includeRawOutput: Option.none() }; describe("domains delete integration", () => { it.effect("prints the success line to stderr in text mode", () => { @@ -89,7 +89,7 @@ describe("domains delete integration", () => { () => { const { layer, out } = setup({ goOutput: "json" }); return Effect.gen(function* () { - yield* domainsDelete({ projectRef: Option.none(), includeRawOutput: true }); + yield* domainsDelete({ projectRef: Option.none(), includeRawOutput: Option.some(true) }); expect(out.stdoutText).toBe(""); expect(out.stderrText).toBe( "Flag --include-raw-output is deprecated and will be removed in v3, use -o json instead.\n" + @@ -99,11 +99,15 @@ describe("domains delete integration", () => { }, ); - it.effect("does not warn when --include-raw-output is not set", () => { + it.effect("warns when --include-raw-output=false", () => { const { layer, out } = setup(); return Effect.gen(function* () { - yield* domainsDelete(baseFlags); - expect(out.stderrText).not.toContain("deprecated"); + yield* domainsDelete({ projectRef: Option.none(), includeRawOutput: Option.some(false) }); + expect(out.stdoutText).toBe(""); + expect(out.stderrText).toBe( + "Flag --include-raw-output is deprecated and will be removed in v3, use -o json instead.\n" + + "Deleted custom hostname config successfully.\n", + ); }).pipe(Effect.provide(layer)); }); diff --git a/apps/cli/src/commands/domains/domains.deprecation.ts b/apps/cli/src/commands/domains/domains.deprecation.ts index 4f609b4034..324e208706 100644 --- a/apps/cli/src/commands/domains/domains.deprecation.ts +++ b/apps/cli/src/commands/domains/domains.deprecation.ts @@ -1,4 +1,4 @@ -import { Effect } from "effect"; +import { Effect, Option } from "effect"; import { Output } from "../../shared/output/output.service.ts"; @@ -6,9 +6,9 @@ const INCLUDE_RAW_OUTPUT_DEPRECATION = "Flag --include-raw-output is deprecated and will be removed in v3, use -o json instead.\n"; export const warnIncludeRawOutputDeprecated = Effect.fnUntraced(function* ( - includeRawOutput: boolean, + includeRawOutput: Option.Option, ) { - if (!includeRawOutput) { + if (Option.isNone(includeRawOutput)) { return; } const output = yield* Output; diff --git a/apps/cli/src/commands/domains/get/get.command.ts b/apps/cli/src/commands/domains/get/get.command.ts index 03d0170ef3..c16551ad4f 100644 --- a/apps/cli/src/commands/domains/get/get.command.ts +++ b/apps/cli/src/commands/domains/get/get.command.ts @@ -13,7 +13,7 @@ const config = { ), includeRawOutput: Flag.Boolean("include-raw-output").pipe( Flag.withDescription("(Deprecated) use -o json instead."), - Flag.withDefault(false), + Flag.optional, ), } as const; diff --git a/apps/cli/src/commands/domains/get/get.handler.ts b/apps/cli/src/commands/domains/get/get.handler.ts index 3c6c10caf9..2d752e8098 100644 --- a/apps/cli/src/commands/domains/get/get.handler.ts +++ b/apps/cli/src/commands/domains/get/get.handler.ts @@ -1,4 +1,4 @@ -import { Effect } from "effect"; +import { Effect, Option } from "effect"; import { CommandPlatformApi } from "../../../auth/command-platform-api.service.ts"; import { ProjectRefResolver } from "../../../config/project-ref.service.ts"; @@ -37,6 +37,9 @@ export const domainsGet = Effect.fn("domains.get")(function* (flags: DomainsGetF ); yield* fetching?.clear ?? Effect.void; - yield* emitHostnameResult(response, flags.includeRawOutput); + yield* emitHostnameResult( + response, + Option.getOrElse(flags.includeRawOutput, () => false), + ); }).pipe(Effect.ensuring(linkedProjectCache.cache(ref)), Effect.ensuring(telemetryState.flush)); }); diff --git a/apps/cli/src/commands/domains/get/get.integration.test.ts b/apps/cli/src/commands/domains/get/get.integration.test.ts index 0ca49954dd..86d38e6acd 100644 --- a/apps/cli/src/commands/domains/get/get.integration.test.ts +++ b/apps/cli/src/commands/domains/get/get.integration.test.ts @@ -78,7 +78,7 @@ function setup(opts: SetupOpts = {}) { return { layer, out, api, analytics, telemetry, linkedProjectCache }; } -const baseFlags = { projectRef: Option.none(), includeRawOutput: false }; +const baseFlags = { projectRef: Option.none(), includeRawOutput: Option.none() }; const DeprecationJsonObject = Schema.fromJsonString(Schema.Record(Schema.String, Schema.Unknown)); @@ -343,30 +343,35 @@ describe("domains get integration", () => { () => { const { layer, out } = setup(); return Effect.gen(function* () { - yield* domainsGet({ projectRef: Option.none(), includeRawOutput: true }); + yield* domainsGet({ projectRef: Option.none(), includeRawOutput: Option.some(true) }); expect(out.stderrText).toBe( "Flag --include-raw-output is deprecated and will be removed in v3, use -o json instead.\n", ); const parsed = yield* Schema.decodeEffect(DeprecationJsonObject)(out.stdoutText); expect(parsed.custom_hostname).toBe("shop.acme.dev"); + expect(out.stdoutText).toContain('"custom_hostname": "shop.acme.dev"'); }).pipe(Effect.provide(layer)); }, ); - it.effect("does not warn when --include-raw-output is not set", () => { + it.effect("warns but does not force JSON when --include-raw-output=false", () => { const { layer, out } = setup(); return Effect.gen(function* () { - yield* domainsGet(baseFlags); - expect(out.stderrText).not.toContain("deprecated"); + yield* domainsGet({ projectRef: Option.none(), includeRawOutput: Option.some(false) }); + expect( + out.stderrText.startsWith( + "Flag --include-raw-output is deprecated and will be removed in v3, use -o json instead.\n", + ), + ).toBe(true); + expect(out.stdoutText).toBe(""); }).pipe(Effect.provide(layer)); }); - it.effect("forces Go JSON output when --include-raw-output is set", () => { + it.effect("does not warn when --include-raw-output is not set", () => { const { layer, out } = setup(); return Effect.gen(function* () { - yield* domainsGet({ projectRef: Option.none(), includeRawOutput: true }); - expect(out.stdoutText.startsWith("{")).toBe(true); - expect(out.stdoutText).toContain('"custom_hostname": "shop.acme.dev"'); + yield* domainsGet(baseFlags); + expect(out.stderrText).not.toContain("deprecated"); }).pipe(Effect.provide(layer)); }); @@ -375,7 +380,7 @@ describe("domains get integration", () => { () => { const { layer, out } = setup({ goOutput: "pretty" }); return Effect.gen(function* () { - yield* domainsGet({ projectRef: Option.none(), includeRawOutput: true }); + yield* domainsGet({ projectRef: Option.none(), includeRawOutput: Option.some(true) }); expect(out.stdoutText.startsWith("{")).toBe(true); }).pipe(Effect.provide(layer)); }, diff --git a/apps/cli/src/commands/domains/reverify/reverify.command.ts b/apps/cli/src/commands/domains/reverify/reverify.command.ts index 0565b0126f..8f686eaa02 100644 --- a/apps/cli/src/commands/domains/reverify/reverify.command.ts +++ b/apps/cli/src/commands/domains/reverify/reverify.command.ts @@ -13,7 +13,7 @@ const config = { ), includeRawOutput: Flag.Boolean("include-raw-output").pipe( Flag.withDescription("(Deprecated) use -o json instead."), - Flag.withDefault(false), + Flag.optional, ), } as const; diff --git a/apps/cli/src/commands/domains/reverify/reverify.handler.ts b/apps/cli/src/commands/domains/reverify/reverify.handler.ts index 7b9c4dbb56..a7b10b9dee 100644 --- a/apps/cli/src/commands/domains/reverify/reverify.handler.ts +++ b/apps/cli/src/commands/domains/reverify/reverify.handler.ts @@ -1,4 +1,4 @@ -import { Effect } from "effect"; +import { Effect, Option } from "effect"; import { CommandPlatformApi } from "../../../auth/command-platform-api.service.ts"; import { ProjectRefResolver } from "../../../config/project-ref.service.ts"; @@ -35,6 +35,9 @@ export const domainsReverify = Effect.fn("domains.reverify")(function* ( ); yield* reverifying?.clear ?? Effect.void; - yield* emitHostnameResult(response, flags.includeRawOutput); + yield* emitHostnameResult( + response, + Option.getOrElse(flags.includeRawOutput, () => false), + ); }).pipe(Effect.ensuring(linkedProjectCache.cache(ref)), Effect.ensuring(telemetryState.flush)); }); diff --git a/apps/cli/src/commands/domains/reverify/reverify.integration.test.ts b/apps/cli/src/commands/domains/reverify/reverify.integration.test.ts index 1dff196839..d5e24c50b0 100644 --- a/apps/cli/src/commands/domains/reverify/reverify.integration.test.ts +++ b/apps/cli/src/commands/domains/reverify/reverify.integration.test.ts @@ -65,7 +65,7 @@ function setup(opts: SetupOpts = {}) { return { layer, out, api, analytics, telemetry, linkedProjectCache }; } -const baseFlags = { projectRef: Option.none(), includeRawOutput: false }; +const baseFlags = { projectRef: Option.none(), includeRawOutput: Option.none() }; const DeprecationJsonObject = Schema.fromJsonString(Schema.Record(Schema.String, Schema.Unknown)); @@ -133,7 +133,7 @@ describe("domains reverify integration", () => { () => { const { layer, out } = setup(); return Effect.gen(function* () { - yield* domainsReverify({ projectRef: Option.none(), includeRawOutput: true }); + yield* domainsReverify({ projectRef: Option.none(), includeRawOutput: Option.some(true) }); expect(out.stderrText).toBe( "Flag --include-raw-output is deprecated and will be removed in v3, use -o json instead.\n", ); @@ -143,19 +143,24 @@ describe("domains reverify integration", () => { }, ); - it.effect("does not warn when --include-raw-output is not set", () => { + it.effect("warns but does not force JSON when --include-raw-output=false", () => { const { layer, out } = setup(); return Effect.gen(function* () { - yield* domainsReverify(baseFlags); - expect(out.stderrText).not.toContain("deprecated"); + yield* domainsReverify({ projectRef: Option.none(), includeRawOutput: Option.some(false) }); + expect( + out.stderrText.startsWith( + "Flag --include-raw-output is deprecated and will be removed in v3, use -o json instead.\n", + ), + ).toBe(true); + expect(out.stdoutText).toBe(""); }).pipe(Effect.provide(layer)); }); - it.effect("forces Go JSON output when --include-raw-output is set", () => { + it.effect("does not warn when --include-raw-output is not set", () => { const { layer, out } = setup(); return Effect.gen(function* () { - yield* domainsReverify({ projectRef: Option.none(), includeRawOutput: true }); - expect(out.stdoutText.startsWith("{")).toBe(true); + yield* domainsReverify(baseFlags); + expect(out.stderrText).not.toContain("deprecated"); }).pipe(Effect.provide(layer)); });