fix(cli): gate vanity-subdomains and network-restrictions behind --experimental (CLI-1972) - #5945
Conversation
…perimental (CLI-1972)
…ordering divergence (CLI-1972)
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6ddc87a121
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
… match Go CLI (review: codex) Go marks --desired-subdomain required (cmd/vanitySubdomains.go:67,69) but cobra validates required flags only after PersistentPreRunE (cobra@v1.10.2/command.go:985,1005), so the gate, login check, and project-ref resolution all win over the missing flag. Make the flag optional at parse time and enforce it in the handler with cobra's exact wording, inside the telemetry/linked-project ensuring wrappers so PersistentPostRun side effects still fire on that failure, as in Go.
|
@codex review |
|
Codex Review: Didn't find any major issues. Swish! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Supabase CLI previewnpx --yes https://pkg.pr.new/supabase/cli/supabase@9c6d9c7629efd5d2265fbc04d2b1401f2bf4b0aaPreview package for commit |
Paid-feature denials now read the management API's
`entitlement_required` error envelope straight off the failed response
and print the upgrade hint with zero extra API calls. Previously the
suggest path made 2 extra round-trips (project lookup + entitlements)
per gated failure, and only 9 commands were wired at all.
**Changed:**
- **Wired commands stop making extra API calls on denial**: the shared
suggest functions (Go `plan_gate.go`, TS `legacy-upgrade-suggest.ts`)
parse `{ error: { code: "entitlement_required", feature, upgrade_url }
}` first; the entitlements round-trip stays as the fallback for gates
that do not emit the envelope yet (v1 SSO carries no server-side gate at
all).
- **`domains create/get/activate/reverify` and `vanity-subdomains get`
newly show the hint**: envelope-only, no fallback, because the
`custom_domain` entitlement key is plan-level while the gate is a
per-project add-on, so the old round-trip would tell Free orgs to
upgrade on unrelated 404s.
- **`cli_upgrade_suggested` reports the server-authoritative feature**:
`feature_key` comes from the envelope (fixes a `branching_persistent`
denial being logged as `branching_limit`); `org_slug` is parsed from
`upgrade_url`.
- **Go mirror kept 1:1** per the legacy-shell parity doctrine;
`AGENTS.md` telemetry table and the affected `SIDE_EFFECTS.md` files
updated to match.
**Note:** the envelope's `upgrade_url` is server-built and printed
without a host check. This trusts the management API host the same way
every authenticated call already does; a host allowlist would silently
suppress valid hints on profiles whose dashboard URL differs from the
CLI default.
## To test
Requires a Free-org project ref (to hit the gates).
Tested locally against prod (`bun src/legacy/main.ts`, Free org):
- [x] `domains get --project-ref <free-ref> --agent no` prints "Upgrade
your plan: .../org/<slug>/billing" on stderr; `--debug` shows exactly
one HTTP request, no project/entitlements lookups
- [x] `vanity-subdomains get` shows the same hint via the decorated
server gate (since the develop merge bringing in supabase#5945, this command
additionally needs `--experimental` or `SUPABASE_EXPERIMENTAL=1` to get
past the experimental gate)
- [x] Plain 4xx without the envelope on the newly wired commands
produces no hint (integration tests pin the false-positive guard)
- [x] Envelope-less denials on previously wired commands still run the
entitlements fallback unchanged
### Before / after
`domains get --project-ref <free-ref> --agent no` on the same Free-org
project, stderr verbatim (ref and org slug redacted). Before is the
merge-base with develop (`3ad3b875`), after is this branch.
Before, the envelope is already in the raw error JSON but nothing reads
it, so no hint:
```
unexpected get hostname status 400: {"message":"Custom domains require the Custom Domain add-on, available on the Pro plan and above.","error":{"code":"entitlement_required","feature":"custom_domain","upgrade_url":"https://supabase.com/dashboard/org/<slug>/billing"}}
Try rerunning the command with --debug to troubleshoot the error.
```
After, the hint prints first, still exactly one HTTP request (`--debug`
shows only the `/custom-hostname` GET):
```
Your organization does not have access to this feature. Upgrade your plan: https://supabase.com/dashboard/org/<slug>/billing
unexpected get hostname status 400: {"message":"Custom domains require the Custom Domain add-on, available on the Pro plan and above.","error":{"code":"entitlement_required","feature":"custom_domain","upgrade_url":"https://supabase.com/dashboard/org/<slug>/billing"}}
Try rerunning the command with --debug to troubleshoot the error.
```
## Linear
- fixes GROWTH-958
Current Behavior
Go registers
vanityCmdandrestrictionsCmd(alongsidebansCmd) as experimental (apps/cli-go/cmd/root.go:56-65) and refuses them inPersistentPreRunEwith exit 1 andmust set the --experimental flag to run this command— before login, ref resolution, telemetry, or any API call (root.go:91-96).The TS legacy shell only reproduced this gate for
network-bans.vanity-subdomains get|check-availability|activate|deleteandnetwork-restrictions get|updatehad zero gate calls: they ran to completion without--experimental— API call made, exit 0,cli_command_executedfired,linked-project.jsonwritten — where Go does none of that.Expected Behavior
All six leaves now fail with Go's exact gate message (exit 1) unless
--experimentalorSUPABASE_EXPERIMENTALis set, with no API call, no telemetry event, and nolinked-project.json/telemetry.jsonwrite behind a closed gate.The wiring mirrors the merged CLI-1854 precedent (#5766) and the in-repo
network-bansreference exactly:legacyValidateOutputFormat(LEGACY_RESOURCE_OUTPUT_FORMATS)runs first, so an invalid-ovalue still wins over a missing--experimental(cobra enum-validates root's persistent-oat parse time, beforePersistentPreRunE).legacyRequireExperimentalruns next, before any instrumentation or layer work.legacyManagementApiRuntimeLayermoves fromCommand.provideto an inlineEffect.provideafter the gate —Command.providebuilds the layer (and eagerly resolves an access token) before the handler's first yield, which would let a missing-token error mask the gate error, exactly the failure mode CLI-1854 fixed for the other three families.New gate integration suites for both families mirror
network-bans.experimental-gate.integration.test.ts: a closed gate fails withLegacyExperimentalRequiredErrorand zero API requests; an open gate proceeds past the gate to the management-API auth step. The sixSIDE_EFFECTS.mdfiles document the gated file writes, theSUPABASE_EXPERIMENTALenv var, the new exit-code row, and the closed-gate telemetry behavior.Deliberately-open judgement calls
vanity-subdomains activate|check-availabilitymark--desired-subdomainrequired. When both it and--experimentalare omitted, the TS parser reports the missing-required-flag error where Go's gate error wins (cobra runsValidateRequiredFlagsonly afterPersistentPreRunE). This is a pre-existing framework-level parse-ordering divergence, not introduced here — exit code is 1 either way and each error names the exact flag to add. Documented in both commands'SIDE_EFFECTS.md; these are the first gated leaves with a required flag, so none of the merged gate PRs hit this.--experimental), andSUPABASE_EXPERIMENTALremains the env escape hatch.Related Issue(s)
Fixes CLI-1972
https://linear.app/supabase/issue/CLI-1972/missing-experimental-gate-on-vanity-subdomains-and-network