-
Notifications
You must be signed in to change notification settings - Fork 516
feat(workers push): make build wait opt-in with --wait flag
#6371
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
base: FUNC-851/general-output-polish
Are you sure you want to change the base?
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 |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| import { Effect, FileSystem, Option, Predicate, type Schedule } from "effect"; | ||
| import type { PlatformError } from "effect/PlatformError"; | ||
| import { Output } from "../../../../shared/output/output.service.ts"; | ||
| import { emitSuccessTrailer } from "../../../../shared/cli/success-trailer.ts"; | ||
| import { legacyRenderWorkerDetails } from "../workers.format.ts"; | ||
| import { | ||
| legacyEmitWorkersMachineOutput, | ||
|
|
@@ -67,6 +68,11 @@ import type { LegacyWorkersPushFlags } from "./push.command.ts"; | |
| * code takes the same path, with the base image and a copy synthesized in place | ||
| * of your Dockerfile. Every runtime this CLI offers has code to package, so | ||
| * there is no path here that skips the upload. | ||
| * | ||
| * The command returns once the platform accepts the deploy. The container build | ||
| * that follows runs for minutes, and blocking on it made every successful | ||
| * deploy as slow as the slowest one — so `--wait` opts into the build's | ||
| * verdict, for CI and for anyone who needs the image version before continuing. | ||
| */ | ||
|
|
||
| const resolveRuntime = Effect.fnUntraced(function* (options: { | ||
|
|
@@ -183,6 +189,8 @@ const deployOneWorker = Effect.fnUntraced(function* (input: { | |
| */ | ||
| readonly refSuffix: string; | ||
| readonly instances: Option.Option<number>; | ||
| /** `--wait`: block on the server-side build instead of returning once it starts. */ | ||
| readonly wait: boolean; | ||
| readonly pollSchedule?: Schedule.Schedule<unknown>; | ||
| readonly pollRetrySchedule?: Schedule.Schedule<unknown>; | ||
| /** Suppresses this step's human output when `-o` owns stdout. */ | ||
|
|
@@ -322,18 +330,27 @@ const deployOneWorker = Effect.fnUntraced(function* (input: { | |
| }; | ||
|
|
||
| const deploying = yield* output.task("Deploying worker..."); | ||
| yield* deployWorker(api, projectRef, name, { spec, contextUploadId }).pipe( | ||
| // The response to the deploy itself is the last thing this command can learn | ||
| // without waiting: the platform answers it only after accepting the spec and | ||
| // the uploaded context, and it carries the accepted spec back. Everything | ||
| // after this point is the server-side container build. | ||
| const accepted = yield* deployWorker(api, projectRef, name, { spec, contextUploadId }).pipe( | ||
| Effect.tapError(() => deploying.fail()), | ||
| ); | ||
|
|
||
| const settled = yield* awaitWorkerBuild(api, projectRef, name, { | ||
| schedule: input.pollSchedule, | ||
| retrySchedule: input.pollRetrySchedule, | ||
| refSuffix: input.refSuffix, | ||
| onPoll: (polled) => | ||
| polled.buildState === "building" ? deploying.message("Building worker...") : Effect.void, | ||
| }).pipe(Effect.tapError(() => deploying.fail())); | ||
|
|
||
| const settled = input.wait | ||
| ? yield* awaitWorkerBuild(api, projectRef, name, { | ||
| schedule: input.pollSchedule, | ||
| retrySchedule: input.pollRetrySchedule, | ||
| refSuffix: input.refSuffix, | ||
| onPoll: (polled) => | ||
| polled.buildState === "building" ? deploying.message("Building worker...") : Effect.void, | ||
| }).pipe(Effect.tapError(() => deploying.fail())) | ||
| : accepted; | ||
|
|
||
| // Checked whether or not the build was waited on. A deploy answered with a | ||
| // spec already in `failed` is a refusal the command should report as one, | ||
| // rather than exiting zero on a worker that will never come up. | ||
| if (settled.buildState === "failed") { | ||
| yield* deploying.clear(); | ||
| return yield* Effect.fail( | ||
|
|
@@ -364,13 +381,38 @@ const deployOneWorker = Effect.fnUntraced(function* (input: { | |
| ); | ||
| yield* output.raw( | ||
| legacyRenderWorkerDetails([ | ||
| // Labelled `State`, and placed first, the way `workers status` renders | ||
| // the same field: without `--wait` it is the one row that says the | ||
| // worker is not serving yet, so it should not be hunted for at the | ||
| // bottom of the block. | ||
| ["State", settled.buildState], | ||
| ["Runtime", runtime], | ||
| ["Size", formatApiSize(settled.spec.size)], | ||
| // Empty without `--wait`: no image exists until the build produces one, | ||
| // and `legacyRenderWorkerDetails` drops an empty-valued row. | ||
| ["Image", settled.imageVersion ?? ""], | ||
| ["Access", settled.spec.exposure], | ||
| ["URL", url ?? ""], | ||
| ]), | ||
| ); | ||
| if (settled.buildState === "building") { | ||
| // A success trailer rather than an inline stderr line: this is a "what to | ||
| // run next" hint, which `stop`, `bootstrap`, `migration repair` and | ||
| // `gen signing-key` all route through `emitSuccessTrailer` so it prints | ||
| // once at the end of the run instead of scrolling away. It matters here | ||
| // more than for those: pushing several workers would otherwise bury each | ||
| // worker's hint under the next worker's packaging and deploy output. | ||
| // | ||
| // One short sentence per line, with the command and the flag aqua'd the | ||
| // way every other follow-up hint in this shell writes them. The single | ||
| // wrapped paragraph this replaced re-flowed differently at every terminal | ||
| // width and buried both commands mid-sentence. | ||
| yield* emitSuccessTrailer( | ||
| `\nYour build was submitted successfully.\n` + | ||
| `Run ${legacyAqua(`supabase workers status ${name}${input.refSuffix}`)} to check on it.\n` + | ||
| `Add ${legacyAqua("--wait")} to block on the build next time.\n`, | ||
|
Comment on lines
+410
to
+413
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.
In a text-mode multi-worker push without Useful? React with 👍 / 👎. |
||
| ); | ||
| } | ||
| } | ||
|
|
||
| return { | ||
|
|
@@ -419,6 +461,10 @@ const reportUnattempted = Effect.fnUntraced(function* (skipped: ReadonlyArray<st | |
| * per-project capacity and shred the progress output. The first failure stops | ||
| * the run, because a build that failed is usually the thing to fix before | ||
| * spending minutes on the rest. | ||
| * | ||
| * Without `--wait` that serialization only covers the package/upload/deploy | ||
| * legs; the builds themselves then run concurrently on the platform, which is | ||
| * what the caller asked for by not waiting. | ||
| */ | ||
| export const legacyWorkersPush = Effect.fn("legacy.workers.push")(function* ( | ||
| flags: LegacyWorkersPushFlags, | ||
|
|
@@ -490,6 +536,7 @@ export const legacyWorkersPush = Effect.fn("legacy.workers.push")(function* ( | |
| projectRef, | ||
| refSuffix, | ||
| instances: flags.instances, | ||
| wait: flags.wait, | ||
| machineOutput, | ||
| ...(options.pollSchedule === undefined ? {} : { pollSchedule: options.pollSchedule }), | ||
| ...(options.pollRetrySchedule === undefined | ||
|
|
||
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.
When
--waitis set and the deploy POST already returnsbuild_state: activeorfailed—both are permitted byV2DeployAWorkerOutput—this branch discards that terminal verdict and performs a GET anyway. If that read is temporarily unavailable, still returns the documented post-deploy 404 race, or observes a concurrent deployment, the command can time out, fail, or even succeed contrary to its own deploy response. Only poll whenaccepted.buildState === "building"; otherwise useaccepteddirectly.Useful? React with 👍 / 👎.