Uh oh!
There was an error while loading. Please reload this page.
feat(cli): accept branch names and refs in supabase link - #6168
Conversation
Uh oh!
There was an error while loading. Please reload this page.
Supabase CLI previewnpx --yes https://pkg.pr.new/supabase/cli/supabase@5b745989f3254465289c33cc44f77e0cbb549213Preview package for commit |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit:fa633c1091
ℹ️ 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".
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Legacy `supabase link` now accepts a branch name (or branch UUID) via a new optional positional [ref-or-branch] and via --project-ref. Non-ref- shaped values are resolved to the branch's project ref against the linked parent project's branches (parent: SUPABASE_PROJECT_ID -> supabase/.temp/linked-project.json -> supabase/.temp/project-ref, first valid wins), then linking proceeds exactly as before. Ref-shaped values (20 lowercase letters) always short-circuit as refs; the env var and TTY prompt stay refs-only. No new persistent state. TS-only QoL divergence from the Go CLI (CLI-2167).
Branch-name/UUID links previously emitted no custom event (the 404 path skips the project telemetry block). Now a successful branch resolution fires cli_project_linked with TS-only properties linked_via: "branch" and parent_project_ref, plus a project group association on the branch's own ref. The branch name is never sent (user-created content); the plain 404-ref path (no name resolution) intentionally stays silent since a 404 is only assumed to be a branch (CLI-2167).
…a branch After supabase link <branch>, supabase/.temp/project-ref holds the branch's own ref, and the platform returns 403 for branch refs on the branches-management endpoints, breaking every branches subcommand. Hoist link's parent-resolution chain (SUPABASE_PROJECT_ID -> supabase/.temp/linked-project.json -> supabase/.temp/project-ref, first valid wins) to legacy/shared/legacy-parent-project-ref.ts and add a legacyResolveParentScopedProjectRef wrapper: an explicit --project-ref still wins and the invalid/absent paths fall back to the existing resolver, so prompt and error behavior are unchanged. All 8 branches handlers now resolve their parent ref through it; when linked to a real project the cache and ref file agree, so behavior is identical to before (CLI-2167 follow-up).
Three TS-only visibility improvements for the branch-linking workflow (CLI-2167 follow-up), so users and AI agents can discover which org, project, and branch the workdir is linked to: - supabase status reports the linked state in every output mode. Human mode prints a block on stdout before any daemon work (so it shows even when status then fails to connect): Linked Project: Org: <org_slug> (<org_id>) Project: <name> (<parent_ref>) Branch: <branch_name> (<branch_ref>) 'Not linked.' when nothing is linked; the Branch line degrades to the bare ref when the name lookup is unavailable, and is only shown at all when a parent is actually known or the API confirms the branch. Machine modes get additive fields: LINKED_PROJECT_REF / LINKED_BRANCH / LINKED_PARENT_PROJECT_REF / LINKED_PROJECT_NAME / LINKED_ORG_SLUG / LINKED_ORG_ID in -o env (snake_case twins in -o json/yaml/toml, absent when not linked), and a nested linked_project object (or null) in --output-format json/stream-json. The lookup runs against the real Management API via a lazy token-optional LegacyPlatformApiFactory wired into status's runtime; resolution never fails - no token, no network, or no match degrade gracefully and status's own failure modes are untouched. - branches list marks the currently linked branch's NAME cell with '(active)' in the human table (mirrors the next shell's convention); machine payloads unchanged. - projects list falls back to marking the parent project (via the shared parent chain) when the linked ref matches no project row, i.e. when linked to a branch; an exact row match always wins. This also makes the linked field in -o json truthful for that state. The state resolution lives in legacy/shared/legacy-linked-state.ts.
- linked-project.json alone is never proof of a link: the parent-chain cache candidate now only participates when the project-ref file exists, so a failed link's telemetry cache can no longer make no-flag branches commands operate on the never-linked project (P1). - Replace the production 'as Record<string, unknown>' cast with an isRecord type guard (P1). - Default-branch links (branch project_ref == parent ref) now tag the cli_project_linked capture with linked_via/parent_project_ref while keeping the groupIdentify-rich path (P2). - Remove the self-referential no-cache branch inference from the status linked-state resolution: it could only ever match the default-branch row (rendering a project as a branch of itself) or 403 (P2). - Sanitize all API-derived names rendered to the terminal (did-you-mean suggestion, resolved-branch line, not-ready message, status block labels) against ANSI/control injection (LOW). - Strip the CLI-2167 linked-state stdout block symmetrically in the status parity e2e via the established stripPatterns normalization.
fa633c1 to
e4d3b8bCompareUh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
💡 Codex Review
cli/apps/cli/src/legacy/commands/link/link.handler.ts
Lines 343 to 344 in e4d3b8b
When branch-name linking starts from a valid project-ref but linked-project.json is missing or malformed, this write replaces the only parent reference with the branch ref. The post-run cache fill cannot repair it: a missing cache triggers GET /v1/projects/{branchRef}, which returns 404, while an existing malformed cache is never overwritten. The next link <branch> or no-flag branches command therefore treats the branch ref as the parent and receives the platform's parent-scope 403; persist branchResolution.parentRef as durable linked-state metadata before overwriting this file.
ℹ️ 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".
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
sanitizeLegacyErrorBody deliberately preserves newline/tab for JSON response bodies, so a newline-bearing branch name could still forge CLI output lines. Inline names (did-you-mean, resolved-branch line, not-ready message, available-branches list, status block labels) now go through legacySanitizeInlineName, which additionally collapses newline/tab runs to a single space.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit:e42dc68704
ℹ️ 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".
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Persist the resolved parent as a ref-only linked-project.json record on name-resolved branch links (the parent chain now survives a missing/malformed cache), and best-effort invalidate a cached parent that verifiably does not own a raw-ref-linked branch. - Bound the status linked-state lookup with a 5s timeout so a blackholed Management API cannot stall a local status check. - An env-sourced SUPABASE_PROJECT_ID override only yields a parent claim on positive lookup confirmation, so an unrelated workdir cache can no longer render the env project as a branch of it. - --override-name collisions: existing status payload keys always win over the additive linked_* fields; machine error-envelope context extras can no longer clobber envelope keys. - Branch-resolution failures now flush telemetry state (ensuring block moved to wrap resolution). - Sanitize refs (not just names) in the status block display; refs come from unvalidated worktree files. - Doc drift: status SIDE_EFFECTS no longer describes the removed no-cache lookup.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit:4f0bf889a1
ℹ️ 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".
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- legacyResolveSoftLinkedRef only accepts PROJECT_REF_PATTERN-shaped env/file candidates: a symlinked or garbage project-ref file (e.g. pointed at an access token) is treated as not linked and its content never reaches any output channel. - SUPABASE_PROJECT_ID that merely restates the linked branch ref is deduped from the parent chain so the cached parent wins. - The raw-ref link cache-correlation lookup gets the same 5s timeout as the status lookup.
Coly010
commented
Aug 12, 2026
Also addressed from the latest review body (no inline thread): the raw-ref link cache-correlation lookup is now bounded by the same 5s timeout as the status lookup (993054c), so an unreachable Management API can no longer stall an otherwise-successful |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit:993054c872
ℹ️ 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".
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
…f file - The post-run cache fill now skips its write when the project-ref file holds a different ref than the one being cached: the cache describes the linked workdir's state, not whatever ref the last command resolved, so a failed link can no longer plant a never-linked project as the parent-chain's cache candidate. Deliberate TS divergence from Go's ensureProjectGroupsCached, documented in the layer. - The raw-ref link cache-correlation call shows a text-mode spinner. - The cacheless raw-branch-ref link remains a documented gap: no read-only Management API endpoint can resolve a branch ref's parent (only listAllBranches carries parent_project_ref, and it requires already knowing the parent).
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit:f5947e23b0
ℹ️ 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".
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- An unverifiable divergent parent cache (correlation timeout or failure) is now deleted rather than kept: a wrong parent claim silently misdirects parent-scoped mutations, while deletion only downgrades later branches commands to a recoverable not-linked error. - Branch UUID matching in link is case-insensitive (canonical ids are lowercase hex; uppercase input is a valid UUID spelling).
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit:d0eae3a62d
ℹ️ 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".
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
…ctions - If the ref-only parent-cache replacement write fails on a name-resolved branch link, the stale divergent cache is deleted rather than left trusted by the parent chain (fail-safe fallback). - link SIDE_EFFECTS documents the fail-safe deletion on correlation failure; status SIDE_EFFECTS distinguishes the pattern-VALIDATED linked ref from the merely-sanitized cache display fields.
There was a problem hiding this comment.
💡 Codex Review
When a workdir linked to project A successfully links a normal project B but replacing A's linked-project.json fails, this ignored error leaves A's cache beside the newly written project-ref for B. The post-run cache fill also returns immediately because the stale file already exists, so subsequent flagless parent-scoped branch mutations prioritize A and can modify the wrong project. Fresh evidence after the accepted 404-path replacement fix is that this 200/plain-project arm still ignores replacement failure without deleting the stale cache; apply the same fail-safe invalidation here.
ℹ️ 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".
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- The parent chain's first PRESENT candidate now decides: a typo'd SUPABASE_PROJECT_ID hard-classifies as invalid instead of silently falling through to a lower-priority project (restores the pre-2167 resolver's env validation; the env/file dedup is now restricted to pattern-valid restatements so garbage can't sneak past it). - Status's linked-state lookup moved inside the telemetry-ensured scope so an interruption mid-lookup still flushes telemetry state. - Parity tracker documents fail-safe cache deletion on every unverified correlation result.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit:a87872a03e
ℹ️ 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".
Uh oh!
There was an error while loading. Please reload this page.
Same fail-safe fallback as the branch path: a successful link to an ordinary project whose best-effort linked-project.json rewrite fails now deletes a surviving stale cache instead of leaving the parent chain trusting the previously linked project.
Uh oh!
There was an error while loading. Please reload this page.
What kind of change does this PR introduce?
Feature (legacy shell, TS-only QoL — no Go counterpart).
What is the current behavior?
supabase linkonly accepts a 20-character project ref via--project-ref. Linking to a preview branch means digging its project ref out of the dashboard. Once linked to a branch, everybranchessubcommand fails (the platform returns 403 for branch refs on the branches-management endpoints), and nothing in the CLI tells you which project or branch a workdir is linked to.Closes CLI-2167
What is the new behavior?
supabase linkaccepts branch names and refs — new optional positional[ref-or-branch], and--project-refaccepts a branch name/UUID too. Ref-shaped values (20 lowercase letters) always short-circuit as refs, so every currently-working invocation is unchanged; anything else resolves against the linked parent project's branches (parent chain:SUPABASE_PROJECT_ID→supabase/.temp/linked-project.json→supabase/.temp/project-ref, first valid wins — the cache file only ever holds a real non-404 project, which is what makes re-linking between branches work with no new persistent state). Env var and TTY prompt stay refs-only. A branch whoseproject_refhasn't provisioned yet fails loudly instead of silently linking the parent.branchessubcommands survive branch links — all 8 handlers resolve their parent-scoped ref through the same chain (explicit--project-refstill wins; prompt/error fallbacks unchanged).Linked-state visibility (agent-discovery oriented):
supabase statusprints aLinked Project:block (Org/Project/Branch, name + ref) on stdout before any daemon work, so it shows even when status then fails; machine modes get additiveLINKED_*env keys /linked_*keys / a nestedlinked_projectobject — including on the--output-format json/stream-jsonfailure envelope via a new opt-inMachineErrorContextcell (inert for every other command; Go-oformats deliberately unchanged on failure). Resolution is lazy and token-optional — offline runs degrade to the known refs and never change status's own failure modes.branches listmarks the linked branch(active)in the human table (machine payloads unchanged);projects listmarks the parent project when linked to a branch.Telemetry: branch links fire
cli_project_linkedwith TS-onlylinked_via: "branch"+parent_project_ref(branch names never sent;--project-refis only telemetry-safe when ref-shaped).Docs:
SIDE_EFFECTS.mdfiles anddocs/go-cli-porting-status.mdupdated for every divergence. Related follow-up filed separately: CLI-2171 (reference docs are generated from the Go cobra tree, so TS-only surfaces like this positional don't reach the docs site).