Skip to content

feat(cli): port network-restrictions commands to native TypeScript#5367

Merged
Coly010 merged 7 commits into
developfrom
cli/port-network-restrictions
May 27, 2026
Merged

feat(cli): port network-restrictions commands to native TypeScript#5367
Coly010 merged 7 commits into
developfrom
cli/port-network-restrictions

Conversation

@Coly010
Copy link
Copy Markdown
Contributor

@Coly010 Coly010 commented May 27, 2026

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.

What changed

  • network-restrictions get/update are 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's restrictions package 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/apply with { dbAllowedCidrs, dbAllowedCidrsV6 }; --append=true PATCHes /v1/projects/{ref}/network-restrictions with { 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-33 parses every --db-allow-cidr via net.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 writing linked-project.json (telemetry still flushes via the outermost Effect.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 a Layer.mergeAll that re-built the easy-to-mis-wire legacyProjectRefLayer.pipe(Layer.provide(...)) subgraph). The new file exports LEGACY_VALID_REF/LEGACY_VALID_TOKEN, the two no-op layers, mockLegacy{Telemetry,LinkedProjectCache}Tracked(), mockLegacyCliConfig, mockLegacyPlatformApi (hybrid response / byMethod / handler surface), useLegacyTempWorkdir(prefix), and a one-shot buildLegacyTestRuntime composer. 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, and validateAndPartitionCidrs tests now live in network-restrictions.cidr.unit.test.ts next to their source, not bundled into update.unit.test.ts.

Reviewer-relevant context

  • IPv4-mapped IPv6 detection — Go's net.IP.IsPrivate() first calls To4(), which unwraps ::ffff:a.b.c.d to its v4 form before the private-range check. Without that, ::ffff:10.0.0.0/104 would slip past the TS check because the IPv6 first byte is 0x00, not 0xfc. parseCidr now detects the IPv4-mapped form (short ::ffff:a.b.c.d and long 0:0:0:0:0:ffff:a.b.c.d) and reclassifies as kind: "v4" with a v4MappedAddress field; isPrivateCidr uses the unwrapped octets, and validateAndPartitionCidrs routes the original input string into the v4 request bucket (matching Go's if ip.To4() != nil { append to v4 } branch).

  • Error body sanitisation in legacy-http-errors.tsmapLegacyHttpError now 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/set and secrets/unset need extra servicessecrets/set reads process.env for env(VAR) resolution and needs RuntimeInfo for CWD; secrets/unset consumes LegacyYesFlag. Those tests compose the extras on top of buildLegacyTestRuntime via Layer.mergeAll(...) rather than expanding the helper's options surface.

  • Smoke-tested the bundled dist/supabase-legacy binary (per the Layer.provide sibling-sharing footgun) — --help for network-restrictions {get,update}, ssl-enforcement {get,update}, and backups list all resolve cleanly, so the centralised runtime wiring doesn't regress production layer composition.

  • docs/go-cli-porting-status.md flips both network-restrictions rows from wrapped to ported.

Fixes CLI-1296

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
@Coly010 Coly010 requested a review from a team as a code owner May 27, 2026 11:04
@Coly010 Coly010 self-assigned this May 27, 2026
Coly010 added 4 commits May 27, 2026 12:11
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.
@Coly010 Coly010 merged commit c87d52b into develop May 27, 2026
8 checks passed
@Coly010 Coly010 deleted the cli/port-network-restrictions branch May 27, 2026 13:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants