feat(cli): port functions deploy#5561
Conversation
There was a problem hiding this comment.
💡 Codex Review
cli/apps/cli/src/legacy/commands/functions/deploy/deploy.command.ts
Lines 35 to 38 in 6713273
When legacy users run supabase functions deploy without --use-api, this flag now parses as false, so deployFunctions takes the server-side API path instead of the local Docker bundler. The Go CLI that this legacy shell is porting sets --use-docker to default true in apps/cli-go/cmd/functions.go:155-157, and apps/cli-go/internal/functions/deploy/deploy.go:56-58 uses Docker whenever it is running; only an explicit --use-api opts out. This changes the default deployment artifact/path for existing stable CLI users and can break projects that rely on Docker/local bundling behavior.
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 94aa2f0097
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 59977ff17d
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a14a8e908d
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
67a5fc2 to
e456290
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e456290738
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 237af1e14d
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ec9a343578
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b6bc946519
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1ce91eb76b
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
## TL;DR fixes nullable function import map paths ## prob - While testing the ported `functions deploy` flow here: supabase#5561 I found that `functions list` could fall back to `request_failed` because the management api can return `import_map_path: null` Go already accepted that null shape, but the generated ts contract only accepted a string basically this updates the generated function response contracts to accept `null` for `import_map_path` and adds a integration coverage around it! ## ref - smol regression of: (supabase#5185)
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a7a4308b0d
ℹ️ 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".
avallete
left a comment
There was a problem hiding this comment.
I think the PR description should call out the now default --use-api as a behavioral change, and include how to revert to the default behavior (opt-out using the --use-docker flag) if encountering an issue.
eb55dc5 to
82a79c2
Compare
82a79c2 to
60bac3d
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 60bac3d513
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8dd302273b
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
cli/apps/cli/src/legacy/commands/functions/deploy/deploy.command.ts
Lines 35 to 37 in 1ac4c42
For the legacy shell, supabase functions deploy with no mode flag now leaves useDocker false because Effect boolean flags default to false, so deployFunctions takes the API upload path and never probes Docker. The Go command's hidden --use-docker flag defaults to true (apps/cli-go/cmd/functions.go:155-157) and is only disabled by --use-api, so existing legacy users who rely on local eszip/Docker bundling will silently switch to server-side bundling.
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
avallete
left a comment
There was a problem hiding this comment.
Testing things locally e2e I encountered some regressions bugs.
Bug: functions deploy (deploy all) fails with An error occurred in Effect.tryPromise
Summary
functions deploy <slug> works, but functions deploy (no slug — deploy all) fails immediately with a generic Effect.tryPromise error. Root cause is in discoverFunctionSlugs in apps/cli/src/shared/functions/deploy.ts.
Root cause
When no function names are passed, the handler calls discoverFunctionSlugs, which readdirs supabase/functions/ and stats index.ts in each subdirectory. For deploy-e2e-custom-entry, that file does not exist — the function uses a custom entrypoint (handler.ts) declared in config.toml.
The code tries to ignore missing entrypoints with a JS try/catch around yield* Effect.tryPromise(() => stat(...)), but that does not catch Effect failures in an Effect.gen. The ENOENT from stat propagates as an uncaught Effect.tryPromise failure.
The Go CLI avoids this: it globs functions/*/index.ts and separately merges slugs from config.toml (GetFunctionSlugs in apps/cli-go/internal/functions/deploy/deploy.go). So config-only / custom-entrypoint functions are included without requiring index.ts on disk.
Why single deploy works
Explicit slugs skip discoverFunctionSlugs and go straight to deploy, so deploy-e2e-basic (and deploy-e2e-custom-entry if named explicitly) succeeds.
How to reproduce
From local-supabase-test (linked project, auth configured):
# Setup (if needed)
./scripts/verify-setup.sh
# PASS — single function, skips discovery
./scripts/deploy-matrix.sh default single linked
# or:
supabase-legacy functions deploy deploy-e2e-basic
# FAIL — deploy all, hits discovery bug
./scripts/deploy-matrix.sh default all linked
# or:
supabase-legacy functions deployExpected failure:
An error occurred in Effect.tryPromise
Try rerunning the command with --debug to troubleshoot the error.
(--debug does not surface the underlying ENOENT; the real error is wrapped in Effect’s UnknownError.)
Triggering fixture
supabase/functions/deploy-e2e-custom-entry/ has handler.ts + deno.json but no index.ts, by design:
[functions."deploy-e2e-custom-entry"]
entrypoint = "./handler.ts"Suggested fix
Align discoverFunctionSlugs with Go:
- Discover filesystem functions via
functions/*/index.ts(glob orfs.exists), notreaddir+ barestat. - Merge in config-declared slugs from
configFunctions(already partially done). - Replace JS
try/catcharoundyield* Effect.tryPromisewithEffect.catch/Effect.ignoreso missing entrypoints are skipped safely.
Worth adding an integration test: project with [functions.foo] entrypoint = "./handler.ts" and no index.ts, then deploy with functionNames: [].
I did a little e2e test suite on this branch: feat/functions-deploy-port-e2e-test-script
aha! should work now, no longer blows up QQ: maybe as a smol followup after this should I refactor deploy.ts a bit so it stays easier to maintain...? |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5375763f6e
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
cli/apps/cli/src/legacy/commands/functions/deploy/deploy.command.ts
Lines 35 to 38 in 70c500c
In the legacy shell, a plain supabase functions deploy now leaves useDocker false, so deployFunctions takes the API/source-upload path without even checking Docker. The Go command this ports declares --use-docker with default true in apps/cli-go/cmd/functions.go:155-158 and only switches to API when --use-api is set, so stable users with Docker running lose local bundling/no-change hashing and can get different deploy artifacts unless they discover the hidden --use-docker flag.
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| mkdtemp(join(tmpdir(), `.supabase-output-${config.slug}-`)), | ||
| ); | ||
| try { | ||
| yield* Effect.tryPromise(() => chmod(outputDir, 0o777)); |
There was a problem hiding this comment.
🟡 Severity: MEDIUM
The temporary directory is set to world-writable/readable permissions (0o777). In multi-user or shared environments (like shared development servers or CI/CD runners), this allows local attackers to read the bundled function source or modify the compilation output before deployment, risking information disclosure and malicious code injection.
Helpful? Add 👍 / 👎
💡 Fix Suggestion
Suggestion: Replace the world-writable permission 0o777 with 0o700 to restrict the temporary output directory to the owner only. Since mkdtemp already creates the directory with 0o700 by default, this chmod call is explicitly widening permissions unnecessarily. Docker (which typically runs as root) can still write to the directory regardless of these permissions. If Docker is configured to run as a non-root user in your environment, consider using 0o750 instead, which grants group read/execute but no world access.
⚠️ Experimental Feature: This code suggestion is automatically generated. Please review carefully.
| yield* Effect.tryPromise(() => chmod(outputDir, 0o777)); | |
| yield* Effect.tryPromise(() => chmod(outputDir, 0o700)); |
There was a problem hiding this comment.
this matches the current go path apps/cli-go/internal/functions/deploy/bundle.go#L36-L43
it also creates the docker bundler output dir with 0777 ....
There was a problem hiding this comment.
💡 Codex Review
cli/apps/cli/src/legacy/commands/functions/deploy/deploy.command.ts
Lines 39 to 42 in ba88d5e
In the legacy command this hidden flag now defaults to false, so a plain supabase functions deploy reaches deployFunctions with useDocker=false and takes the API/server-side path. I checked apps/cli-go/cmd/functions.go: the Go command registers --use-docker with default true and only clears it when --use-api is set. This regresses default deploys on machines with Docker from local bundling to server-side uploads, breaking projects that rely on Docker-only network/package resolution unless users discover and pass a hidden flag.
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
as mentioned in pr desc, its an intentional behavior change |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 67a65c0ee4
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
Looking at all the back and forth, it loo like this might actually be more impacting than we expected (turning the default to Maybe we should revert this in favor of a pure 1-1 port, and change the default on a dedicated follow-up PR ? Also re-running the e2e suite I showed earlier I caught another regression diff: Docker bulk deploy fails for functions without an import map (TS port bug)Manual testing against a linked project shows a regression in the ported Failure: Observed on: Go CLI: same project and fixtures pass — Docker bulk deploy succeeds for package.json-only functions. Cause: the eszip upsert path always passes The asset-upload path is unaffected because it always sends Fix: only include |
Yeah, I was thinking the same 😭 re:
fixed now, also reran that local e2e matrix and it passed all the cases |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 751b36007e
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
QQ: in that default behavior followup, |
|
Let's plan for it but maybe schedule it with a few stable releases in between.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b57a1a99f1
ℹ️ 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".
| projectRef: string, | ||
| ): Promise<ProjectConfig> { | ||
| const matchedRemoteNames = Object.entries(loadedConfig.config.remotes) | ||
| .filter(([, candidate]) => candidate.project_id === projectRef) |
There was a problem hiding this comment.
Reject malformed remote project refs
When a [remotes.*] block has a mistyped project_id (for example the wrong length) and the user deploys to the intended project ref, this filter simply finds no matching remote and proceeds with the base function config, silently dropping the remote-specific overrides. The Go config loader validates every remote project_id before deploy, so the same typo is caught instead of deploying with the wrong settings; validate remote refs before matching them here.
Useful? React with 👍 / 👎.
| if (!isContainedPath(realProjectRoot, realPathname)) { | ||
| throw new Error(`refusing to upload asset outside project root: ${pathname}`); |
There was a problem hiding this comment.
Allow explicitly configured static assets outside the root
When a function config points static_files at a shared directory outside the project root, such as static_files = ["../../shared/*.html"] relative to supabase/config.toml, the pattern is resolved but each matched file is rejected here. The Go upload path expands configured static globs and uploads the resulting files without this project-root containment check, so monorepos that explicitly share static assets outside the Supabase project now fail before deploying.
Useful? React with 👍 / 👎.
| const scopes: Record<string, Record<string, string>> = {}; | ||
| let importMapReference = ""; | ||
|
|
||
| if (typeof input === "object" && input !== null) { |
There was a problem hiding this comment.
Reject non-object import map documents
When deno.json or an import map parses to a non-object document such as [] or a string, this branch falls through and returns an empty import map, so deploy silently ignores the invalid file and any intended remaps. The Go path unmarshals into the import-map struct and fails on those shapes, so this should reject non-object documents instead of treating them as {}.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Do not require unused import-map targets to exist
When an import map contains a local mapping that this function does not import, for example a shared deno.json with a stale "old": "./old.ts" entry, this eagerly realpaths every local target and aborts on ENOENT. The Go upload path only reads targets reached from the entrypoint import graph, so unused mappings do not block deploy; skip or warn on missing eager targets unless they are actually walked as imports.
Useful? React with 👍 / 👎.

TL;DR
ports
functions deployto native tsWhat’s introduced
adds the native ts implementation for
supabase functions deploy, keeping the existing command surface for API deploys, Docker bundling, import maps, static files, pruning, disabled functions, and output& includes coverage around all this!
(will address as a followup)
ref: