Commit cb8041a
authored
feat(cli): rebuild config push as diff-first partial updates (CLI-2313) (#6454)
## Summary
`supabase config push` is rebuilt as a **diff-first** command. It reads
the project's effective
configuration once (`GET /v2/projects/{ref}/config`), runs the same
`diffProjectConfig` that
`config diff` and `config pull` use, and writes only the properties your
`supabase/config.toml`
declares *and* that differ from the project — as sparse request bodies
to the six existing v1 update
endpoints.
The previous pipeline (six per-service v1 GETs → rebuild each service's
full local subset with
hand-coded presence/enabled gates → TOML unified diff → PUT/PATCH the
whole subset) is deleted:
`push/config-sync/**`, the TOML encoder and diff renderer, and
`push.raw-presence.ts`.
Linear: CLI-2313. The optional follow-up to swap the six v1 writes for
the single v2 PATCH once that
endpoint ships is CLI-2317; the `@supabase/config` cleanup this unblocks
is CLI-2314 (no
`packages/config` changes here).
## What changed
- `push.plan.ts` — routes every comparable `ProjectConfig` path to one
of six write resources
(`api`, `db.settings`, `db.network_restrictions`, `db.ssl_enforcement`,
`auth`, `storage`) or to an
explicit *unsupported* set (`db.major_version`, `db.pooler.*`,
`auth.oauth_server.*`); owns the
resource gates, the MFA add-on gates and the resource→response-block
mapping. A unit test asserts
every registry path is classified, so a new `@supabase/config` row
cannot fall through silently.
- `push.encoders.ts` — six pure encoders turning routed changes into
sparse bodies. Groups the API
requires together still ship whole (CIDR lists, storage feature blocks,
SMTP, captcha, each auth
hook, each OAuth provider, the active SMS provider); **undeclared
members of such a group are sent
with the project's current value read in the same run**, so they do not
change. Only when the API
did not return a member's value is the config default sent, and that is
disclosed in the
confirmation block as `[group-write]`. The storage encoder can emit `{
enabled: false, … }` for
`icebergCatalog`/`vectorBuckets`, which the old builder structurally
could not.
- `push.secrets.ts` — declared secrets are compared as HMAC digests
against what the API reports;
plaintext is sent only when the digest differs or the remote has no
value. Empty values and
unresolved `env(...)` references are never sent and are now disclosed
before the prompt.
- `push.format.ts` — per-service change blocks reuse the family's
per-property renderer
(`config/config.format.ts`), plus the new notes, the machine-readable
summary message and the JSON
payload.
- `push.handler.ts` — one read, then per-resource encode → confirm →
write in the established order. A
resource whose block the API did not return is reported `unavailable`
and never written; a response
with no configuration blocks at all is an error (nothing is written).
- Family-root hoists shared by `config diff`, `config pull` and `config
push`:
`config/config.read-status.ts` (401/403/404 read-status messages) and
`config/config.project-config.ts` (the `ProjectConfigParseError` guard).
Both handlers now use them
instead of private copies.
- Test infrastructure: `mockLegacyPlatformApiService` gains `executeRaw`
support; the v2 response
fixture is shared between the `config diff` and `config push` suites;
the cli-e2e replay scenarios
and recorded fixtures reflect the new request sequence.
- `SIDE_EFFECTS.md` rewritten for the new routes, output, statuses and
exit causes.
## Behavior changes (user-visible)
1. **One read instead of six.** `GET /v2/projects/{ref}/config` replaces
the six per-service
`GET /v1/…` calls. `GET /v1/projects/{ref}/billing/addons` is unchanged.
2. **Undeclared properties are left alone.** Previously a property
absent from `config.toml` was
written back with its schema default whenever its service was pushed.
Now only declared, differing
properties are written; members of a required-together group keep their
current project value.
`config diff` shows the remaining drift, and the push output reports how
many remote properties
were left untouched.
3. **Requests carry only what changed** (plus the required-together
groups above).
4. **Credentials are compared before being sent**; unchanged secrets are
not re-sent, and a credential
that cannot be sent (empty / unresolved `env(...)`) is shown in the
confirmation block.
5. **Per-service change display** is now the same per-property format
`config diff` prints
(`path [update]`, `local:` / `remote:`) instead of a TOML-style unified
diff. Secret values are
never printed.
6. **New reporting**: a `Comparison scope:` line on every run; notes for
properties with no Management
API field, properties that could not be encoded as written (with the
reason), declared properties
the command does not manage (count), remote-only drift (count), and
group members written at a
default.
7. **Partial API responses are never written blind.** A resource whose
configuration block was not
returned is reported `unavailable` and skipped; if no blocks are
returned the command exits 1.
8. **JSON / stream-json payload** gains `schema_version: 1`,
`services[].changes`, `unsupported`,
`unencodable`, `forced`, `unmanaged`, `secrets`
(`sent`/`unchanged`/`not_set`/`gated`/`skipped`,
reflecting the write that actually happened), `declined_addons`,
`remote_only`, `scope`, and a
non-empty summary `message`. Existing fields (`project_ref`,
`services[].service`,
`services[].status`) keep their values; `status` gains `unavailable` and
`not_pushable`.
9. **Declining an MFA cost prompt** behaves as before (both
`verify_enabled` and `enroll_enabled` are
sent as `false` when the project has either enabled) and is now recorded
in `declined_addons`.
10. **Command description** now states the hands-off rule and points at
`config diff` as the preview.
11. **Unchanged**: command path and flags, prompt wording, `Pushing
config to project:` /
`Remote <service> config is up to date.` / `Updating <service> service
with config:` /
`Enabling webhooks for project:` lines, exit codes (declining a prompt
still exits 0), files read
and written, telemetry.
## Reviewer notes
- Storage feature *disable* is encoder-ready but not yet reachable:
`@supabase/config` still prunes a
disabled `storage.analytics`/`storage.vector` from the local projection
(`applyPushUnmanagedOmissions`), so such a change surfaces in the
`unmanaged` count today and starts
being pushed when CLI-2314 lands.
- The one `Effect.die` arm in `legacyConfigProjectConfigTry` is
unreachable from a JSON-parsed body
(every `@supabase/config` failure is a `ProjectConfigParseError`) and is
covered by its unit test
rather than the integration suites.1 parent 0489a3d commit cb8041a
63 files changed
Lines changed: 11439 additions & 9549 deletions
File tree
- apps
- cli-e2e/fixtures
- recorded
- GET_v2_projects___PROJECT_REF___config
- PATCH_v1_projects___PROJECT_REF___config_auth
- scenarios
- config-push-emits-http-trace-with-debug
- config-push-reconciles-every-section-against-the-remote
- cli
- src
- legacy
- commands
- config
- diff
- pull
- push
- config-sync
- start/services
- tests/helpers
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
Lines changed: 12 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
Lines changed: 347 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 1 addition & 41 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
| 8 | + | |
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
14 | | - | |
15 | | - | |
16 | | - | |
17 | | - | |
18 | | - | |
19 | | - | |
20 | | - | |
21 | | - | |
22 | | - | |
23 | | - | |
24 | | - | |
25 | | - | |
26 | | - | |
27 | | - | |
28 | | - | |
29 | | - | |
30 | | - | |
31 | | - | |
32 | | - | |
33 | | - | |
34 | | - | |
35 | | - | |
36 | | - | |
37 | | - | |
38 | | - | |
39 | | - | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
46 | | - | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | 14 | | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | 15 | | |
56 | 16 | | |
57 | 17 | | |
0 commit comments