-
Notifications
You must be signed in to change notification settings - Fork 511
feat(cli): add supabase workers push #6262
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
da81823
feat(cli): add supabase workers push
johnstonmatt e7acc02
fix(cli): stop reporting a missing project as an alpha enrolment gap
johnstonmatt 2e74f82
Merge FUNC-753/workers-new into FUNC-753/workers-push
johnstonmatt 38fd3f4
fix(cli): stop workers push failing late, deploying empty and leaking…
johnstonmatt f06b39a
Merge FUNC-753/workers-new into FUNC-753/workers-push
johnstonmatt eec654d
fix(cli): finish the packaging fixes and stop the retry test waiting …
johnstonmatt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| import { describe, expect, test } from "vitest"; | ||
| import { legacyRedactHttpUrl } from "./legacy-http-debug.layer.ts"; | ||
|
|
||
| /** | ||
| * `--debug` logs every request URL to stderr. For a presigned object-store URL | ||
| * the query string *is* the credential — for the Workers build-context upload, | ||
| * one that authorizes overwriting the archive a deploy is about to build from — | ||
| * so it must not survive into scrollback or a CI log. | ||
| */ | ||
| describe("legacyRedactHttpUrl", () => { | ||
| test.each([ | ||
| [ | ||
| "an AWS presigned upload", | ||
| "https://store.example/bucket/ctx.tar.gz?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Signature=deadbeef", | ||
| "https://store.example/bucket/ctx.tar.gz?<redacted>", | ||
| ], | ||
| [ | ||
| "a GCS presigned upload", | ||
| "https://store.example/bucket/ctx.tar.gz?X-Goog-Signature=deadbeef", | ||
| "https://store.example/bucket/ctx.tar.gz?<redacted>", | ||
| ], | ||
| [ | ||
| "a lowercase signature parameter", | ||
| "https://store.example/o/ctx?signature=deadbeef&expires=123", | ||
| "https://store.example/o/ctx?<redacted>", | ||
| ], | ||
| [ | ||
| "a bare token parameter", | ||
| "https://store.example/o/ctx?token=deadbeef", | ||
| "https://store.example/o/ctx?<redacted>", | ||
| ], | ||
| ])("redacts the query string of %s", (_label, url, expected) => { | ||
| expect(legacyRedactHttpUrl(url)).toBe(expected); | ||
| expect(legacyRedactHttpUrl(url)).not.toContain("deadbeef"); | ||
| }); | ||
|
|
||
| // The debug log is only useful if ordinary requests still read normally, so | ||
| // redaction has to be the exception rather than the rule. | ||
| test.each([ | ||
| ["a Management API route", "https://api.supabase.com/v2/projects/abc/workers/api"], | ||
| ["an ordinary query string", "https://api.supabase.com/v1/projects?limit=10"], | ||
| ["a URL with no query at all", "https://api.supabase.com/v1/projects"], | ||
| ])("leaves %s untouched", (_label, url) => { | ||
| expect(legacyRedactHttpUrl(url)).toBe(url); | ||
| }); | ||
|
|
||
| test("passes through something that is not a parseable URL", () => { | ||
| expect(legacyRedactHttpUrl("not a url at all")).toBe("not a url at all"); | ||
| }); | ||
|
|
||
| test("keeps the path, which is what makes the log line worth having", () => { | ||
| expect(legacyRedactHttpUrl("https://store.example/bucket/deep/ctx.tar.gz?sig=x")).toContain( | ||
| "/bucket/deep/ctx.tar.gz", | ||
| ); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| # `supabase workers push [name...] (alias: deploy)` | ||
|
|
||
| > **No live test yet.** `workers` runs against the v2 Management API, which the | ||
| > supabase/cli-e2e-ci supabox stack is not expected to serve, so a `*.live.test.ts` | ||
| > here would be permanently skipped or permanently red. Revisit when the v2 | ||
| > Workers routes are available on that stack. | ||
|
|
||
| ## Files Read | ||
|
|
||
| | Path | Format | When | | ||
| | ---------------------------------------- | ---------- | ----------------------------------------------------------------------------------------------------------- | | ||
| | `<workdir>/supabase/config.toml` | TOML | always, for each worker's runtime, size, source | | ||
| | `<worker source>/**` | any | always — packaged into the build context | | ||
| | `<SUPABASE_HOME or ~/.supabase>/profile` | plain text | when neither `--profile` nor `SUPABASE_PROFILE` is set — names the profile, defaulting to `supabase` | | ||
| | `<SUPABASE_PROFILE>` (YAML) | YAML | when `SUPABASE_PROFILE` is a filesystem path rather than a built-in name; a read failure aborts the command | | ||
|
|
||
| ## Files Written | ||
|
|
||
| | Path | Format | When | | ||
| | ----------------------------------------------- | ------ | --------------------------------------------------------------- | | ||
| | `<SUPABASE_HOME or ~/.supabase>/telemetry.json` | JSON | always — flushed on success and on failure | | ||
| | `<workdir>/supabase/.temp/linked-project.json` | JSON | after the project ref resolves, when the cache does not hold it | | ||
|
|
||
| ## API Routes | ||
|
|
||
| | Method | Path | Auth | Request body | Response (used fields) | | ||
| | ------ | -------------------------------------------- | ------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------ | | ||
| | `POST` | `/v2/projects/{ref}/workers/{name}/uploads` | Bearer token | none | `data.id`, `data.attributes.url/method` | | ||
| | `PUT` | presigned upload URL (control-plane storage) | URL signature — **no** Supabase credentials | `.tar.gz` build context | status only | | ||
| | `POST` | `/v2/projects/{ref}/workers/{name}/deploy` | Bearer token | `{data:{type,attributes:{spec,context_upload_id}}}` | `data.attributes.build_state` | | ||
| | `GET` | `/v2/projects/{ref}/workers/{name}` | Bearer token | none | `build_state`, `state_reason`, `image_version`, `spec` | | ||
| | `GET` | `/v1/projects/{ref}` | Bearer token | none | linked-project cache miss only — name, org, region | | ||
|
|
||
| `GET` is polled until `build_state` leaves `building`. | ||
|
|
||
| ## Exit Codes | ||
|
|
||
| | Code | Condition | | ||
| | ---- | ---------------------------------------------------- | | ||
| | `0` | success | | ||
| | `1` | no workers named and none found in the project | | ||
| | `1` | a worker's source directory is missing or empty | | ||
| | `1` | build context upload failed | | ||
| | `1` | the build reached `failed`, or never left `building` | | ||
| | `1` | API error, or project not enrolled in the alpha | | ||
|
|
||
| ## Environment Variables | ||
|
|
||
| | Variable | Purpose | Required? | | ||
| | ----------------------- | ---------------------------------------------------- | ------------------------------------------------------- | | ||
| | `SUPABASE_ACCESS_TOKEN` | auth token (bypasses credential file/keyring lookup) | no (falls back to keyring → `~/.supabase/access-token`) | | ||
| | `SUPABASE_PROFILE` | built-in profile name or YAML file path | no (falls back to `~/.supabase/profile` -> `supabase`) | | ||
| | `SUPABASE_WORKDIR` | project directory the command acts on | no (falls back to `--workdir`, then the ancestor walk) | | ||
| | `SUPABASE_HOME` | directory holding `telemetry.json` | no (falls back to `~/.supabase`) | | ||
|
|
||
| ## Telemetry Events Fired | ||
|
|
||
| | Event | When | Notable properties / groups | | ||
| | ---------------------- | ------------------------------------------ | ----------------------------------- | | ||
| | `cli_command_executed` | post-run, success or failure (via wrapper) | `exit_code`, `duration_ms`, `flags` | | ||
|
|
||
| No custom events — only the `cli_command_executed` that the instrumentation | ||
| wrapper emits for every command. | ||
|
|
||
| ## Output Formats | ||
|
|
||
| `-o env` is refused **before** the first deploy rather than at emit time: the | ||
| payload always carries a `workers` array, which a flat `KEY=value` list cannot | ||
| express, and discovering that at the end would fail the command with the remote | ||
| project already changed. | ||
|
|
||
| The presigned `PUT` above is the one request whose URL is itself a credential. | ||
| `--debug` logs every request URL, so `legacyHttpClientLayer` redacts query | ||
| strings that carry a signature. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| import { Argument, Command, Flag } from "effect/unstable/cli"; | ||
| import type * as CliCommand from "effect/unstable/cli/Command"; | ||
| import { withJsonErrorHandling } from "../../../../shared/output/json-error-handling.ts"; | ||
| import { legacyManagementApiRuntimeLayer } from "../../../shared/legacy-management-api-runtime.layer.ts"; | ||
| import { withLegacyCommandInstrumentation } from "../../../telemetry/legacy-command-instrumentation.ts"; | ||
| import { legacyWorkersPush } from "./push.handler.ts"; | ||
|
|
||
| const config = { | ||
| names: Argument.string("name").pipe( | ||
| Argument.withDescription("Workers to deploy. Deploys every worker in the project if omitted."), | ||
| Argument.variadic(), | ||
| ), | ||
| instances: Flag.integer("instances").pipe( | ||
| // Bounded at the parser, the same way `[workers.<name>] instances` is bounded | ||
| // in the config schema. Left unchecked it reached the deploy endpoint — after | ||
| // the build context had been packaged and uploaded — as a scaling request the | ||
| // platform cannot honour. | ||
| Flag.filter( | ||
| (instances) => instances >= 0, | ||
| (instances) => `--instances ${instances} is negative; pass zero or more.`, | ||
| ), | ||
| Flag.withDescription( | ||
| "Number of instances to run, overriding `instances` in supabase/config.toml for this deploy. Falls back to the recorded value, then 1.", | ||
| ), | ||
| Flag.optional, | ||
|
johnstonmatt marked this conversation as resolved.
|
||
| ), | ||
| projectRef: Flag.string("project-ref").pipe( | ||
| Flag.withDescription("Project ref of the Supabase project."), | ||
| Flag.optional, | ||
| ), | ||
| } as const; | ||
|
|
||
| export type LegacyWorkersPushFlags = CliCommand.Command.Config.Infer<typeof config>; | ||
|
|
||
| export const legacyWorkersPushCommand = Command.make("push", config).pipe( | ||
|
johnstonmatt marked this conversation as resolved.
|
||
| Command.withAlias("deploy"), | ||
| Command.withDescription( | ||
| "Build and deploy workers into the linked Supabase project. Reads each worker's runtime, size and source directory from supabase/config.toml.", | ||
| ), | ||
| Command.withShortDescription("Build and deploy workers"), | ||
| Command.withExamples([ | ||
| { | ||
| command: "supabase workers push", | ||
| description: "Deploy every worker in the project", | ||
| }, | ||
| { | ||
| command: "supabase workers push api", | ||
| description: "Deploy a single worker", | ||
| }, | ||
| { | ||
| command: "supabase workers push api web", | ||
| description: "Deploy several workers by name", | ||
| }, | ||
| ]), | ||
| Command.withHandler((flags) => | ||
| legacyWorkersPush(flags).pipe( | ||
| withLegacyCommandInstrumentation({ flags }), | ||
| withJsonErrorHandling, | ||
| ), | ||
| ), | ||
| Command.provide(legacyManagementApiRuntimeLayer(["workers", "push"])), | ||
| ); | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After the prior cache and telemetry additions, the compatibility document still omits side effects performed by the resolver and auth layers:
legacy-project-ref.layer.ts:87-95consumesSUPABASE_PROJECT_ID, reads<workdir>/supabase/.temp/project-ref, and may callGET /v1/projectsfor interactive selection, whilelegacy-credentials.layer.ts:403-443reads the profile and legacy keyring entries or<SUPABASE_HOME>/access-token. These happen before the Workers requests and leave the Files Read, API Routes, and Environment Variables sections incomplete.AGENTS.md reference: apps/cli/AGENTS.md:L359-L366
Useful? React with 👍 / 👎.