feat(cli): port network-restrictions commands to native TypeScript#5367
Merged
Conversation
Replaces the Phase-0 Go-binary proxies for `supabase network-restrictions get` and `network-restrictions update` with native Effect handlers. Output, error messages, exit codes, request shapes, and filesystem side effects match the Go CLI. Also extracts the duplicated mock-setup code that 9 ported integration test files were independently re-implementing into a shared `tests/helpers/legacy-mocks.ts` factory module, and moves the misplaced cidr unit tests onto their proper sibling file. Fixes CLI-1296
Ensures `apps/cli-e2e` is invalidated and re-runs against the newly-ported TypeScript commands when changes land in `apps/cli`, not only when the Go binary in `apps/cli-go` changes.
…fixtures Three GET response fixtures (`default.response.json`, `2.response.json`, `4.response.json`) and two inline scenario bodies were missing the `status` field that the OpenAPI spec marks as required. Go tolerated the omission (decoded as empty string); the strict generated TS schema validator rejects it with `SchemaError(Missing key "status")`, surfacing as a parity failure after the native TS port. The five sibling fixtures that already include `status` all use `"applied"`, which matches the `0.0.0.0/0` + `::/0` allow-all default state these three fixtures describe.
jgoux
approved these changes
May 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replaces the Phase-0 Go-binary proxies for
supabase network-restrictions getandnetwork-restrictions updatewith native Effect handlers. Output, error messages, exit codes, request shapes, and filesystem side effects match the Go CLI.What changed
network-restrictions get/updateare now native TS — typed Management API client,withLegacyCommandInstrumentation+withJsonErrorHandling+mapLegacyHttpError, honoring both Go's--output {pretty,json,yaml,toml,env}and the TS--output-format {text,json,stream-json}(note: Go'srestrictionspackage itself does NOT honor--output; the TS port honors both per the legacy CLAUDE.md dual-flag rule).Two API paths in
update— default POSTs/v1/projects/{ref}/network-restrictions/applywith{ dbAllowedCidrs, dbAllowedCidrsV6 };--append=truePATCHes/v1/projects/{ref}/network-restrictionswith{ add: { ... } }. The PATCH response uses the V2 shape (config.dbAllowedCidrs: Array<{address, type: "v4"|"v6"}>) and gets partitioned back into the two flat arrays for the text formatter.CIDR validation runs locally before any I/O — Go's
update.go:20-33parses every--db-allow-cidrvianet.ParseCIDR, rejects RFC-1918 / RFC-4193 private inputs unless--bypass-cidr-checks=true, and partitions into v4/v6. The TS port matches this order so a malformed CIDR short-circuits without resolving the project ref or writinglinked-project.json(telemetry still flushes via the outermostEffect.ensuring).Text output is byte-exact with Go's
fmt.Printf("%+v", ...)slice rendering —<nil>when the API omits the field,&[]when the field is present but empty,&[a b c](single-space separated, no quotes) when populated. GET/POST handlers print the response field directly (so the tri-state surfaces); PATCH wraps via&localSlice(always renders&[]or&[...]).New shared test infrastructure at
apps/cli/tests/helpers/legacy-mocks.ts— every Phase-1+ legacy integration test was independently re-implementing ~100 LoC of mock setup (no-op cache/telemetry layers, state-tracking variants,mockCliConfig,httpClientLayer/jsonResponse,mockPlatformApi,VALID_REF/VALID_TOKEN,let tempRoot+beforeEach/afterEach, and aLayer.mergeAllthat re-built the easy-to-mis-wirelegacyProjectRefLayer.pipe(Layer.provide(...))subgraph). The new file exportsLEGACY_VALID_REF/LEGACY_VALID_TOKEN, the two no-op layers,mockLegacy{Telemetry,LinkedProjectCache}Tracked(),mockLegacyCliConfig,mockLegacyPlatformApi(hybridresponse/byMethod/handlersurface),useLegacyTempWorkdir(prefix), and a one-shotbuildLegacyTestRuntimecomposer. All 9 existing native-port integration test files (backups/{list,restore},network-restrictions/{get,update},secrets/{list,set,unset},ssl-enforcement/{get,update}) migrate to use the helpers in this PR.Cidr unit tests moved to their proper sibling file — the
parseCidr,isPrivateCidr, andvalidateAndPartitionCidrstests now live innetwork-restrictions.cidr.unit.test.tsnext to their source, not bundled intoupdate.unit.test.ts.Reviewer-relevant context
IPv4-mapped IPv6 detection — Go's
net.IP.IsPrivate()first callsTo4(), which unwraps::ffff:a.b.c.dto its v4 form before the private-range check. Without that,::ffff:10.0.0.0/104would slip past the TS check because the IPv6 first byte is0x00, not0xfc.parseCidrnow detects the IPv4-mapped form (short::ffff:a.b.c.dand long0:0:0:0:0:ffff:a.b.c.d) and reclassifies askind: "v4"with av4MappedAddressfield;isPrivateCidruses the unwrapped octets, andvalidateAndPartitionCidrsroutes the original input string into the v4 request bucket (matching Go'sif ip.To4() != nil { append to v4 }branch).Error body sanitisation in
legacy-http-errors.ts—mapLegacyHttpErrornow strips ASCII control characters (except\t/\n) from response bodies before embedding them in error messages. Defense-in-depth against log-injection if the Management API ever echoes user-controlled content in error responses; the existing 1024-byte cap is unchanged.secrets/setandsecrets/unsetneed extra services —secrets/setreadsprocess.envforenv(VAR)resolution and needsRuntimeInfofor CWD;secrets/unsetconsumesLegacyYesFlag. Those tests compose the extras on top ofbuildLegacyTestRuntimeviaLayer.mergeAll(...)rather than expanding the helper's options surface.Smoke-tested the bundled
dist/supabase-legacybinary (per theLayer.providesibling-sharing footgun) —--helpfornetwork-restrictions {get,update},ssl-enforcement {get,update}, andbackups listall resolve cleanly, so the centralised runtime wiring doesn't regress production layer composition.docs/go-cli-porting-status.mdflips bothnetwork-restrictionsrows fromwrappedtoported.Fixes CLI-1296