Skip to content

fix(cli): merge matching [remotes.*] block on config push#5618

Merged
Coly010 merged 2 commits into
developfrom
columferry/cli-1808-cli-config-push-with-remotes-blocks-error
Jun 18, 2026
Merged

fix(cli): merge matching [remotes.*] block on config push#5618
Coly010 merged 2 commits into
developfrom
columferry/cli-1808-cli-config-push-with-remotes-blocks-error

Conversation

@Coly010

@Coly010 Coly010 commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

What changed

config push regressed in v2.106.0 (the native-TS port): when a [remotes.<name>] block in config.toml targeted the project ref, the command aborted with

cannot push config: a [remotes.*] block targets project ***, which config push does not yet support.

The Go CLI (v2.105.0) instead merges that remote's subtree over the base config and pushes it. The port had punted on Go's mergeRemoteConfig.

This ports the merge faithfully and removes the abort.

Why this location

The merge is owned by @supabase/config, mirroring Go doing it in pkg/config. loadProjectConfig / loadProjectConfigFile now accept an optional { projectRef }. When set, after env() interpolation and before schema decode, the matching [remotes.<name>] raw subtree is deep-merged over the base document (objects recurse; arrays and scalars replace wholesale — viper's v.Set semantics), db.seed.enabled is forced false when the remote omits it, the remotes key is stripped, and the merged document is decoded. Doing it on the raw document (not the decoded config) is essential: the decoded remote section carries full schema defaults that would otherwise clobber every field the block doesn't override.

The merge is gated on projectRef, so every other loadProjectConfig caller is unaffected.

Notable details for reviewers

  • New DuplicateRemoteProjectIdError (exported from @supabase/config) raised when two remotes share the target project_id, carrying Go's verbatim message duplicate project_id for [remotes.<b>] and [remotes.<a>].
  • LoadedProjectConfig gains optional document (merged, post-interpolation raw doc) and appliedRemote fields.
  • The push handler prints Loading config override: [remotes.<name>] to stderr (Go parity) when a remote applies, and now derives optional pointer-section presence (db.ssl_enforcement, storage.image_transformation, storage.s3_protocol, auth subsections) from the merged document instead of re-reading the file — so sections introduced by the remote are detected. Dead code removed (matchesRemoteProjectRef, resolveRemoteByProjectRef, LegacyConfigPushUnsupportedRemoteError).
  • functions deploy is consolidated onto the same shared merge, deleting its divergent partial copy (configForProjectRef / mergeFunctionConfigByPresence, which only handled functions.* and edge_runtime.deno_version). Verified behavior-preserving since deploy reads only those fields. This also corrects deploy's duplicate-project_id message to match Go (both remote names bracketed).

Closes CLI-1808

The native-TS port of `config push` aborted with "a [remotes.*] block
targets project ..., which config push does not yet support" whenever a
remote block matched the target ref — a regression from the Go CLI
(v2.105.0), which merges the matching remote's subtree over the base
config and pushes it.

Port Go's `mergeRemoteConfig` faithfully into `@supabase/config`:
`loadProjectConfig` now accepts an optional `{ projectRef }` and, when
set, deep-merges the matching `[remotes.<name>]` raw subtree over the
base document before schema decode (objects recurse, arrays/scalars
replace), forces `db.seed.enabled=false` when the remote omits it,
strips `remotes`, and returns the merged `document` + `appliedRemote`.
Duplicate `project_id` across remotes raises `DuplicateRemoteProjectIdError`
with Go's verbatim message. The merge is gated on `projectRef`, so other
callers are unaffected.

The push handler prints `Loading config override: [remotes.<name>]` when
a remote applies and derives pointer-section presence from the merged
document. Also consolidates `functions deploy` onto the shared merge,
deleting its divergent partial copy.

Closes CLI-1808
@Coly010 Coly010 requested a review from a team as a code owner June 18, 2026 09:59
@Coly010 Coly010 self-assigned this Jun 18, 2026
@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown

Supabase CLI preview

npx --yes https://pkg.pr.new/supabase/cli/supabase@65ab5daaf795bd669bfd441a1ec5678e58e610b1

Preview package for commit 65ab5da.

jgoux commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

I found one parity issue worth fixing before merge:

packages/config/src/io.ts currently checks duplicate remote project_ids only among remotes whose project_id matches the requested projectRef:

const matches = Object.entries(remotes)
  .filter(([, remote]) => isObject(remote) && remote["project_id"] === projectRef)
  .map(([name]) => name);

The Go loader builds its duplicate map across all [remotes.*] blocks before applying the matching override (apps/cli-go/pkg/config/config.go:503-514). That means a config with two non-target remotes sharing the same project_id, or two remotes omitting project_id, is accepted here but rejected by Go.

Since this PR is explicitly restoring Go parity for remote config loading, applyRemoteOverride should scan all remotes for duplicate IDs first, then apply the matching remote.

Go's loadFromFile builds the duplicate-detection map across every
[remotes.*] block before applying the matching override, so clashes
between non-target remotes (or two remotes omitting project_id, which
viper reads as "") are rejected. The TS loader only compared remotes
already matching projectRef, accepting configs Go rejects.
@Coly010

Coly010 commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

Good catch — fixed in 65ab5da.

applyRemoteOverride now builds the project_id -> [remotes.<name>] map across all [remotes.*] blocks and fails on the first duplicate before applying the matching override, mirroring Go's loadFromFile (apps/cli-go/pkg/config/config.go:503-518). A missing project_id reads as "" (matching viper.GetString), so two remotes that both omit it now collide on the empty key just like Go.

Added two regression tests: duplicate IDs among non-target remotes, and two remotes omitting project_id.

@Coly010 Coly010 added this pull request to the merge queue Jun 18, 2026
Merged via the queue into develop with commit 4f94f6d Jun 18, 2026
19 checks passed
@Coly010 Coly010 deleted the columferry/cli-1808-cli-config-push-with-remotes-blocks-error branch June 18, 2026 12:09
@7ttp 7ttp mentioned this pull request Jun 24, 2026
2 tasks
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