-
Notifications
You must be signed in to change notification settings - Fork 518
feat(workers): add exposure control and new --instances flag #6432
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,15 +22,21 @@ import { | |
| resolveWorkerSource, | ||
| } from "../../../../../shared/workers/worker-paths.ts"; | ||
| import { | ||
| DEFAULT_WORKER_EXPOSURE, | ||
| DEFAULT_WORKER_INSTANCES, | ||
| DEFAULT_WORKER_RUNTIME, | ||
| DEFAULT_WORKER_SIZE, | ||
| parseWorkerExposure, | ||
| parseWorkerRuntime, | ||
| parseWorkerSize, | ||
| validateWorkerNameMessage, | ||
| vcpuForSize, | ||
| WORKER_EXPOSURE_DESCRIPTIONS, | ||
| WORKER_EXPOSURES, | ||
| WORKER_RUNTIME_DESCRIPTIONS, | ||
| WORKER_RUNTIMES, | ||
| WORKER_SIZES, | ||
| type WorkerExposure, | ||
| type WorkerRuntime, | ||
| type WorkerSize, | ||
| } from "../../../../../shared/workers/worker-runtimes.ts"; | ||
|
|
@@ -51,8 +57,10 @@ import type { LegacyWorkersNewFlags } from "./new.command.ts"; | |
| * chosen runtime's starter files and record the choice in `config.toml`. | ||
| * Nothing is deployed; this is entirely local-disk work. | ||
| * | ||
| * The name, runtime and size are all resolved *before* anything is written, so a | ||
| * cancelled prompt leaves nothing behind for this worker at all. | ||
| * The name, runtime, size and exposure are all resolved *before* anything is | ||
| * written, so a cancelled prompt leaves nothing behind for this worker at all. | ||
| * `--instances` is recorded rather than resolved: it has no prompt, and it only | ||
| * reaches `config.toml` when it differs from the default. | ||
| */ | ||
|
|
||
| /** `values`, with `defaultValue` first, so a prompt pre-selects what it shows first. */ | ||
|
|
@@ -174,6 +182,55 @@ const resolveSize = Effect.fnUntraced(function* (options: { | |
| return DEFAULT_WORKER_SIZE; | ||
| }); | ||
|
|
||
| /** | ||
| * Recorded on every scaffold, not just when it is asked for: `push` sends a | ||
| * complete spec each time, so a worker whose `exposure` is absent from | ||
| * `config.toml` is deployed public by the next bare `push`. Writing the value | ||
| * down — default included, the way `runtime` and `size` are — is what makes | ||
| * `--exposure private` stick past the deploy that chose it. | ||
| */ | ||
| const resolveExposure = Effect.fnUntraced(function* (options: { | ||
| readonly explicit: Option.Option<WorkerExposure>; | ||
| /** Whether there is a terminal to ask on — see `canPromptFor`. */ | ||
| readonly canPrompt: boolean; | ||
| }) { | ||
| if (Option.isSome(options.explicit)) { | ||
| return options.explicit.value; | ||
| } | ||
|
|
||
| if (options.canPrompt) { | ||
| const output = yield* Output; | ||
| const selected = yield* output.promptSelect( | ||
| "Should this worker be reachable from the internet?", | ||
| defaultFirst([...WORKER_EXPOSURES], DEFAULT_WORKER_EXPOSURE).map((exposure) => ({ | ||
| value: exposure, | ||
| label: exposure, | ||
| hint: WORKER_EXPOSURE_DESCRIPTIONS[exposure], | ||
| })), | ||
| ); | ||
| return parseWorkerExposure(selected) ?? DEFAULT_WORKER_EXPOSURE; | ||
| } | ||
|
|
||
| return DEFAULT_WORKER_EXPOSURE; | ||
| }); | ||
|
|
||
| /** | ||
| * The instance count to record, and whether to record it at all. | ||
| * | ||
| * Not prompted for, unlike the other dials: how many instances a worker needs is | ||
| * an operational answer nobody has while scaffolding it, so the flag records | ||
| * one when it is given and the file stays quiet when it is not. | ||
| * | ||
| * `undefined` — meaning "write no key" — for the default count, because an | ||
| * absent `instances` and `instances = 1` mean the same thing to `push`, and a | ||
| * scaffold should not commit a line that says nothing. A `0` is not that: it | ||
| * scales the worker to nothing, so it is written like any other explicit count. | ||
| */ | ||
| function recordedInstances(explicit: Option.Option<number>): number | undefined { | ||
| const instances = Option.getOrUndefined(explicit); | ||
| return instances === undefined || instances === DEFAULT_WORKER_INSTANCES ? undefined : instances; | ||
| } | ||
|
|
||
| /** | ||
| * Whether the destination is free for a scaffold: nothing there, or an empty | ||
| * directory. A plain file counts as occupied, so it is refused by name rather | ||
|
|
@@ -226,11 +283,13 @@ export const legacyWorkersNew = Effect.fn("legacy.experimental.workers.new")(fun | |
| ); | ||
| } | ||
|
|
||
| // Resolved before anything is written, so cancelling either prompt leaves | ||
| // Resolved before anything is written, so cancelling any prompt leaves | ||
| // nothing behind — the name included. With nowhere to ask, the defaults | ||
| // stand; only the name has nothing to fall back to. | ||
| const runtime = yield* resolveRuntime({ explicit: flags.runtime, canPrompt }); | ||
| const size = yield* resolveSize({ explicit: flags.size, canPrompt }); | ||
| const exposure = yield* resolveExposure({ explicit: flags.exposure, canPrompt }); | ||
| const instances = recordedInstances(flags.instances); | ||
|
|
||
| // Validated before anything is written: this is the directory the starter | ||
| // files land in, so a value naming the project root, `supabase/`, or | ||
|
|
@@ -289,6 +348,8 @@ export const legacyWorkersNew = Effect.fn("legacy.experimental.workers.new")(fun | |
| patch: { | ||
| runtime, | ||
| size, | ||
| exposure, | ||
| ...(instances === undefined ? {} : { instances }), | ||
| ...(source === undefined ? {} : { source }), | ||
| }, | ||
| }); | ||
|
|
@@ -310,6 +371,11 @@ export const legacyWorkersNew = Effect.fn("legacy.experimental.workers.new")(fun | |
| runtime, | ||
| size, | ||
| vcpu: vcpuForSize(size), | ||
| exposure, | ||
| // The count a deploy will use, whether or not it was written down — a | ||
| // payload that omitted it for the default would read as "unknown" rather | ||
| // than "one". | ||
| instances: instances ?? DEFAULT_WORKER_INSTANCES, | ||
| source: sourceDisplay, | ||
| config_path: project.configPath, | ||
| }; | ||
|
|
@@ -335,7 +401,10 @@ export const legacyWorkersNew = Effect.fn("legacy.experimental.workers.new")(fun | |
| legacyRenderWorkerDetails([ | ||
| ["Runtime", runtime], | ||
| ["Size", `${size} (${vcpuForSize(size)} vCPU)`], | ||
| ["Access", "public"], | ||
| ["Access", exposure], | ||
| // `declared`, the way `workers status` labels the same number: nothing | ||
| // is running yet, so a bare count would read as a live tally. | ||
| ["Instances", `${instances ?? DEFAULT_WORKER_INSTANCES} declared`], | ||
|
Comment on lines
374
to
+407
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ⚪ NIT · The new machine-output fields and human-readable exposure and instance rows are not asserted by tests. Evidence: new.handler.ts:374-378 adds Suggested fix: Assert |
||
| ]), | ||
| ); | ||
| // On the success trailer rather than inline, the way `bootstrap` emits its | ||
|
|
||
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.
⚪ NIT ·
documentation· source: claudeThe Files Written table incorrectly implies that
sourceis written only when it differs from a default.Evidence: SIDE_EFFECTS.md:19 groups
instancesandsourceunder “when those differ from the default,” but new.handler.ts:337-354 writessourcewhenever--sourceis present; no source default is compared.Suggested fix: State separately that
instancesis written when non-default andsourceis written when--sourceis supplied.