Skip to content

fix(cli): default functions download --use-docker to true (Go parity)#5793

Merged
Coly010 merged 9 commits into
developfrom
columferry/cli-1862-functions-download-default-use-docker-to-true-go-parity
Jul 7, 2026
Merged

fix(cli): default functions download --use-docker to true (Go parity)#5793
Coly010 merged 9 commits into
developfrom
columferry/cli-1862-functions-download-default-use-docker-to-true-go-parity

Conversation

@Coly010

@Coly010 Coly010 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Current Behavior

Go defaults --use-docker to true on functions download (cmd/functions.go:181); the TS port omitted the default, so it resolved to false. This flipped the default download path (native HTTP vs. Docker-proxied) whenever no flags were passed, and once fixed naively, the mutex check between --use-api/--use-docker/--legacy-bundle (truthiness-based) would start misfiring on the new default-true value, and --use-api alone would keep routing to Docker instead of overriding it the way Go's if useApi { useDocker = false } does.

Expected Behavior

  • useDocker now defaults to true via Flag.withDefault(true), matching Go.
  • The mutex check (validateDownloadFlags) now scans raw argv for explicit flag presence (hasExplicitLongFlag, hoisted out of deploy.ts into the shared functions.shared.ts family root, since it's now used by two commands) instead of checking parsed boolean truthiness — mirroring Go's pflag.Changed-based MarkFlagsMutuallyExclusive.
  • downloadFunctions gates the Docker/legacy-bundle proxy branch on !flags.useApi, mirroring Go's value-based if useApi { useDocker = false } override (cmd/functions.go:51-53) — not presence, so --use-api=false still proxies to Docker like an unpassed --use-api would.
  • The Go-proxy arg builder (makeGoProxyDownloadArgs) now forwards at most one of --use-docker/--legacy-bundle — previously it could forward both once useDocker defaults true, which the Go binary's own mutex check would then reject.
  • Slug validation now runs unconditionally, before the proxy-dispatch branch, so a malformed/traversal-shaped function name is rejected before ever reaching the Go proxy's argv.
  • The delegated Go call now runs with SUPABASE_TELEMETRY_DISABLED=1 (mirrors db pull/db diff's delegated-call pattern), and in --output-format json|stream-json its stdout is captured/discarded via execCapture instead of inherited, with downloadFunctions emitting the Output envelope itself — the Docker/legacy-bundle proxy branch is now safe as the default path for both telemetry accuracy and machine-output correctness (CLI-1546), not just opt-in flags.
  • rawArgs is threaded through DownloadFunctionsDependencies from the Stdio.Stdio service in both the legacy and next shell handlers (the shared downloadFunctions helper is used by both); next's own --use-docker default is intentionally left unchanged — this is a legacy/Go-parity-only fix.

Known follow-ups (not fixed here)

  • Go's own server-side unbundle extractor (saveFile in apps/cli-go/internal/functions/download/download.go) lacks the path-containment checks the native TS downloader already has — pre-existing Go CLI behavior, now reachable by default rather than via opt-in flags. Tracked as CLI-1891.
  • hasExplicitLongFlag detects flag token presence, not resolved value, so --use-docker=false isn't treated as disabling Docker in the mutex check. Pre-existing pattern shared with deploy.ts.

Closes CLI-1862

## Current Behavior

Go defaults `--use-docker` to `true` on `functions download`
(`cmd/functions.go:181`); the TS port omitted the default, so it
resolved to `false`. This flipped the default download path
(native HTTP vs. Docker-proxied) whenever no flags were passed, and
once fixed naively, the mutex check between `--use-api`/`--use-docker`/
`--legacy-bundle` (truthiness-based) would start misfiring on the new
default-true value, and `--use-api` alone would keep routing to Docker
instead of overriding it the way Go's `if useApi { useDocker = false }`
does.

## Expected Behavior

- `useDocker` now defaults to `true` via `Flag.withDefault(true)`,
  matching Go.
- The mutex check (`validateDownloadFlags`) now scans raw argv for
  explicit flag presence (`hasExplicitLongFlag`, hoisted out of
  `deploy.ts` into the shared `functions.shared.ts` family root, since
  it's now used by two commands) instead of checking parsed boolean
  truthiness — mirroring Go's `pflag.Changed`-based
  `MarkFlagsMutuallyExclusive`.
- `downloadFunctions` gates the Docker/legacy-bundle proxy branch on
  `!explicitUseApi`, mirroring Go's `useApi` override.
- The Go-proxy arg builder (`makeGoProxyDownloadArgs`) now forwards at
  most one of `--use-docker`/`--legacy-bundle` — previously it could
  forward both once `useDocker` defaults true, which the Go binary's
  own mutex check would then reject.
- Slug validation now runs unconditionally, before the proxy-dispatch
  branch, so a malformed/traversal-shaped function name is rejected
  before ever reaching the Go proxy's argv.
- `rawArgs` is threaded through `DownloadFunctionsDependencies` from
  the `Stdio.Stdio` service in both the legacy and next shell handlers
  (the shared `downloadFunctions` helper is used by both); `next`'s own
  `--use-docker` default is intentionally left unchanged, this is a
  legacy/Go-parity-only fix.

## Known follow-ups (not fixed here)

- Go's own server-side unbundle extractor (`saveFile` in
  `apps/cli-go/internal/functions/download/download.go`) lacks the
  path-containment checks the native TS downloader already has —
  pre-existing Go CLI behavior, now reachable by default rather than
  via opt-in flags. Tracked as CLI-1891.
- The Go-proxy path doesn't route through `execCapture` for
  `--output-format json|stream-json`, so machine output can be
  corrupted by inherited subprocess stdout (pre-existing, same as
  `deploy`'s already-shipped Docker default).
- `hasExplicitLongFlag` detects flag token presence, not resolved
  value, so `--use-docker=false` isn't treated as disabling Docker.
  Pre-existing pattern shared with `deploy.ts`.

Fixes CLI-1862
@Coly010

Coly010 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 03e7474878

ℹ️ 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".

Comment thread apps/cli/src/shared/functions/download.ts Outdated
Comment thread apps/cli/src/shared/functions/download.ts Outdated
Comment thread apps/cli/src/shared/functions/download.ts Outdated
Comment thread apps/cli/src/legacy/commands/functions/download/download.e2e.test.ts Outdated
Four issues Codex raised against the --use-docker default-to-true change,
each verified against apps/cli-go/ and fixed:

- Gate the Docker/legacy-bundle proxy branch on the parsed `flags.useApi`
  value instead of raw-argv presence, matching Go's value-based
  `if useApi { useDocker = false }` (cmd/functions.go:51-53) — an explicit
  `--use-api=false` was being treated like `--use-api`, wrongly skipping
  the proxy path Go would still take.
- Capture/discard the Go proxy's stdout via `execCapture` in machine-output
  mode instead of inheriting it, and emit the `Output` envelope from
  `downloadFunctions` itself, so `--output-format json|stream-json` no
  longer loses its structured payload now that the proxy branch is the
  default path (CLI-1546).
- Run the delegated Go call with `SUPABASE_TELEMETRY_DISABLED=1` (mirrors
  `db pull`/`db diff`'s delegated-call pattern) so the child's own
  `cli_command_executed` doesn't double-count on top of this command's own
  telemetry now that the proxy branch is the default.
- Drop the `--legacy-bundle`-alone e2e case: it delegates to Go's
  `RunLegacy`, which installs Deno before any network call, so every e2e
  run would trigger a real, uncached download from GitHub. Equivalent
  coverage already exists in download.integration.test.ts via a mocked
  proxy.
@Coly010

Coly010 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2e2496191e

ℹ️ 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".

Comment thread apps/cli/src/legacy/commands/functions/download/download.e2e.test.ts Outdated
Comment thread apps/cli/src/shared/functions/download.ts
Comment thread apps/cli/src/legacy/commands/functions/download/download.handler.ts
Two more issues from Codex's re-review of 2e24961, both fixed; a third
was investigated and found to be a false positive (documented in the reply
rather than "fixed"):

- List remote function slugs before delegating to the Go proxy in
  machine-output mode, not after. The Go child never reports which slugs
  it downloaded, so this list only exists to build the JSON payload; doing
  it first mirrors Go's own `downloadAll` (which also lists before
  looping) and means a transient listing failure is reported before any
  download side effect, instead of masking an already-successful delegated
  download with an unrelated post-hoc listing failure.
- Drop the "--use-api alone" e2e case: now that the mutex fix routes
  --use-api to the native downloader correctly, this case no longer fails
  during argument validation — it proceeds to a real Management API call
  with a fake token/ref. Equivalent (better) coverage already exists in
  download.integration.test.ts via a mocked platform API.
- Investigated the claimed "missing Stdio service" regression in
  hidden-flag.unit.test.ts: confirmed via the actual test run (and a
  temporary debug trace) that it passes and does not crash — `withEnv()`'s
  `BunServices.layer` already satisfies the `Stdio.Stdio` requirement.
  No code change; explained in the review reply instead.
@Coly010

Coly010 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5df8d953f2

ℹ️ 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".

Comment thread apps/cli/src/shared/functions/download.ts
Comment thread apps/cli/src/legacy/commands/functions/download/download.command.ts
Codex re-review of 5df8d95 found a real bug: in the default Docker/proxy
path with --output-format json|stream-json and no function name, an empty
project would still delegate to the Go proxy (an unnecessary Docker/child
round-trip) and then report success as "Downloaded Edge Function source."
with an empty slug list — inconsistent with the native path just below,
which correctly short-circuits empty projects as "No functions found."
without delegating anywhere.

Added the same short-circuit to the proxy branch, right after resolving
the slug list and before calling proxyDownload, in both the legacy and
next handlers.
@Coly010

Coly010 commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 78e37acb06

ℹ️ 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".

Comment thread apps/cli/src/shared/functions/download.ts
Comment thread apps/cli/src/shared/functions/download.ts
Comment thread apps/cli/src/legacy/commands/functions/download/download.handler.ts
@Coly010 Coly010 marked this pull request as ready for review July 6, 2026 16:01
@Coly010 Coly010 requested a review from a team as a code owner July 6, 2026 16:01
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Supabase CLI preview

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

Preview package for commit be603e3.

@Coly010 Coly010 self-assigned this Jul 7, 2026

@jgoux jgoux left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One non-code note: the PR description currently ends with Fixes CLI-1862. Repo guidance says not to use Fixes / Addresses issue-linking lines for Linear issues because the Linear branch already provides that linkage. Please remove that line or rewrite it as plain context.

Comment thread apps/cli/src/legacy/commands/functions/download/download.command.ts
Coly010 added 3 commits July 7, 2026 13:44
…oad extractor (CLI-1891)

The Go CLI's server-side-unbundle extractor joined server-controlled
Supabase-Path/Content-Disposition metadata straight into a filesystem
path and wrote to it with no validation, letting a malicious response
traverse outside supabase/functions or write through a pre-existing
symlink at the destination.

Validate the resolved destination against utils.FunctionsDir before
touching disk, reject paths that resolve through an existing symlink,
and write via a temp file + atomic rename so an existing symlink at the
destination is replaced rather than followed.
…CLI-1891)

downloadAll's per-function loop handed f.Slug -- sourced from
V1ListAllFunctionsWithResponse, the same Management API channel already
treated as untrusted in this threat model -- straight to downloadOne (via
downloadWithDockerUnbundle) and downloadWithServerSideUnbundle, both of
which join it into a filesystem path. downloadOne had no validation at
all: a slug like "../../../../../poc-escaped-outside-project" resolves,
once joined with utils.TempDir and cleaned, to a path outside the project
root, and afero.WriteReader would MkdirAll and write the attacker-controlled
response body there. downloadWithServerSideUnbundle was only incidentally
safe, via saveFile's unrelated validateDownloadPath check on the final
joined path.

Validate the slug once, at the single point where it enters this dispatch
logic, before it reaches any downloader. This closes the downloadOne gap
directly and turns downloadWithServerSideUnbundle's protection from an
implicit side effect into an explicit guard.
…mlink resolution policy)

DX review: the ErrUnsafeDownloadPath error path in saveFile fell through to
the generic --debug suggestion instead of an actionable one, unlike the
other CmdSuggestion sites in this file. Add suggestUnsafeDownloadPath() and
set it at each of saveFile's three ErrUnsafeDownloadPath return points.

DX review: ensureNoSymlinkInPath rejected any symlink under
utils.FunctionsDir unconditionally, which would break legitimate patterns
like a monorepo symlinking a shared directory into the functions tree.
Replace the reject-any-symlink walk with resolve-then-check: resolve both
root and dir to their deepest existing ancestor via filepath.EvalSymlinks
and re-validate the two resolved paths against each other, rejecting only
when the resolved dir actually escapes the resolved root. Resolving root
the same way as dir (rather than against a literal root) is required so an
OS-level symlink above both of them doesn't produce a false mismatch, and
also happens to naturally cover root itself being a symlink.

Engineer review: gate the WARNING log on a failed entrypoint-relative Rel
call behind --debug, since the raw error embeds the server-controlled
partPath verbatim.

Also gate the afero filesystem check on afero.LinkReader rather than
afero.Lstater, since afero.MemMapFs implements the latter by delegating to
Stat, which made the previous implementation's "no-op on in-memory fs"
guarantee accidental rather than structural.
@Coly010

Coly010 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@Coly010 Coly010 added this pull request to the merge queue Jul 7, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: df5ab92a74

ℹ️ 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".

Comment thread apps/cli/src/legacy/commands/functions/download/download.handler.ts
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Jul 7, 2026
Coly010 added 2 commits July 7, 2026 14:25
…2-functions-download-default-use-docker-to-true-go-parity

# Conflicts:
#	apps/cli/src/legacy/commands/functions/download/download.integration.test.ts
#	apps/cli/src/next/commands/functions/download/download.integration.test.ts
#	apps/cli/src/shared/functions/deploy.ts
#	apps/cli/src/shared/functions/download.ts
#	apps/cli/src/shared/functions/functions.shared.ts
@Coly010 Coly010 added this pull request to the merge queue Jul 7, 2026
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

Reviewed commit: be603e3045

ℹ️ 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".

Merged via the queue into develop with commit 30c88fb Jul 7, 2026
22 checks passed
@Coly010 Coly010 deleted the columferry/cli-1862-functions-download-default-use-docker-to-true-go-parity branch July 7, 2026 13:44
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