Skip to content

feat(cli): add supabase workers push - #6262

Draft
johnstonmatt wants to merge 2 commits into
FUNC-753/workers-newfrom
FUNC-753/workers-push
Draft

feat(cli): add supabase workers push#6262
johnstonmatt wants to merge 2 commits into
FUNC-753/workers-newfrom
FUNC-753/workers-push

Conversation

@johnstonmatt

@johnstonmatt johnstonmatt commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds supabase workers push (aliased deploy) and the machinery it needs:

  • workers-api.ts — the typed Workers Management API client.
  • tar.ts / worker-package.ts — packaging a worker directory into the build
    context that gets uploaded.
  • worker-classify.ts — best-effort runtime detection from marker files, so a
    directory with no [workers.<name>] runtime can still deploy. The guess is
    always reported with a nudge to pin it down, never applied silently.

Stack 3 of 4, on top of workers new (#6261).

Linked issue

FUNC-753 (Linear). Supabase maintainer, exempt from the open-for-contribution flow.

Checklist

Builds and deploys workers into the linked project, and brings the
Management API seam with it. Registered under `deploy` as an alias, for
anyone reaching for the `supabase functions` verb out of habit.

Given no names it deploys every worker in the project, matching
`supabase functions deploy`, whose conventions this command set otherwise
mirrors. "Every worker" is the union of the directories under the workers
root and the `[workers.<name>]` entries, so one with a `source` pointing
outside that root is not missed, and the order is sorted rather than
whatever the filesystem returned. Deploys run one at a time: each is a
server-side container build, so interleaving them would both compete for the
alpha's per-project capacity and shred the progress output; the first
failure stops the run.

The flow is mint an upload slot, PUT the `.tar.gz` build context straight at
the presigned URL, deploy, then poll until `build_state` leaves `building`.
The upload carries no Supabase credentials: the signature in the URL is the
authorization, and the bytes never pass through the management API. Polling
is a `Schedule`, and tolerates a few consecutive read failures so one blip
does not throw away a deploy that is progressing.

Which spec is sent depends on the runtime: a `dockerfile` worker sends a
context and no `spec.runtime`, a catalog runtime sends both, and a bare
`sandbox` sends the runtime alone and skips packaging, so it has no URL. A
directory with no `[workers.<name>] runtime` has one guessed from marker
files, reported on stderr with a nudge to pin it down. An empty source
directory is refused rather than deployed as an image with nothing in it.

The build context is packaged in-process rather than by shelling out to
`tar`, whose BSD, GNU and absent-on-Windows variants each produce a
different archive from the same tree. `tar.ts` writes USTAR directly: files,
directories and symlinks, refusing a value too large for an octal header
field instead of letting it spill into the next one and read back as a
plausible but wrong size. Symlinks are stored as links rather than followed
— anything pnpm installs is symlink-dense, so following them would inline
every dependency and walk into a link pointing at an ancestor.

This is the first command in this shell to call a v2 Management API route;
every other one here is a Go-parity port and uses v1 only.
The Workers routes answer 404 both for a project outside the alpha's
allow-list and for a project ref that names nothing this account can see, and
the CLI read every one of them as the former. A mistyped `--project-ref` was
answered with "Workers are in private alpha. Ask in the Supabase dashboard to
have this project enrolled." — sending someone to request enrolment for a
project that does not exist, and never mentioning the ref.

The two are distinguishable in the body:

  not enrolled   {"error":{"code":"generic_not_found","message":"Workers are not available for this project"}}
  no such project{"error":{"code":"not_found","message":"Not Found"}}

so classify on `error.code` and raise the new WorkerProjectNotFoundError for
`not_found`, pointing at the ref, `supabase link` and `supabase login`. Only
that exact code is treated as a missing project; an unrecognized body keeps
the enrolment answer, since that is what the allow-list has historically
returned and guessing the other way would send someone to check a ref that is
fine.

The existing coverage asserted against a `{message}` body the API does not
send, so it is retargeted at the real shapes.
@johnstonmatt
johnstonmatt force-pushed the FUNC-753/workers-push branch from 91220cf to aa0ea27 Compare August 19, 2026 03:11
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.

1 participant