Watch the deploy through, instead of exiting at the interesting moment #22

Merged
geusebio merged 2 commits from feature/sc-deploy-follow-progress into main 2026-08-09 10:41:07 +00:00
Owner

The last piece of #293.
#21 gave sc deploy
feedback for the half it does locally — packing, uploading — and then it stopped dead at the
most interesting moment: the upload answers 201 pending before a worker has picked the
revision up
, so everything a person means by "deploying" happened after sc had already
exited, and all it could offer was a URL and an apology.

It now stays and watches.

What it looks like

Off a terminal — a CI log, a pipe — the spinner degrades to one plain line per step and
this is the whole transcript:

Deployment #12 queued — pending
Watch it:     http://.../applications/example-app/deployments/0198f3a1-…

Waiting for a build worker…
[S⠿C] Queued for 2s
Building…
  api | Retrieving image manifest for golang:1.25-alpine
  api | Built cross stage deps
  api | Unpacking rootfs as cmd COPY . . requires it
  api | RUN go build -trimpath -o /out/api ./cmd/api
  api | Pushing image to registry.someones.computer/acme/api:12
  web | Retrieving image manifest for node:22-alpine
  web | RUN npm ci --omit=dev
  web | Pushing image to registry.someones.computer/acme/web:12
[S⠿C] Built in 10s
Waiting for the rollout…
[S⠿C] Rolled out in 4s

Deployment #12 is running.

On a terminal the bottom line is live — a still frame mid-build, the build output scrolling
above a spinner that is counting:

[S⠿C] Uploaded 4.8 MiB in 2.4s

Deployment #12 queued — pending
Watch it:     http://.../applications/example-app/deployments/0198f3a1-…

[S⠿C] Queued for 2s
  api | Retrieving image manifest for golang:1.25-alpine
  api | Built cross stage deps
  api | Unpacking rootfs as cmd COPY . . requires it
  api | Taking snapshot of full filesystem...
  api | RUN go build -trimpath -o /out/api ./cmd/api
  api | Pushing image to registry.someones.computer/acme/api:12
  web | Retrieving image manifest for node:22-alpine
[S⠋C] Building  0:07

…and a build that breaks, which exits 1 with the reason on stderr:

  api | cmd/api/main.go:14:2: undefined: startServer
  api | error building image: error building stage: failed to execute command
[S⠿C] Build failed after 4s          ← red brackets on a terminal

sc: Deployment #12 failed. kaniko exited 1 building service "api"

Two decisions worth arguing with

A step is coarser than a status. built and deploying are one step — the instant
between the images existing and the rollout starting, and the rollout itself — so the seam
is a relabel in place rather than a step closed and reopened. Closing there would print
Rolled out in 0s the moment the images existed, with the actual rollout still to come. The
label still tracks the status (Waiting for the rolloutRolling out), so a wedged deploy
worker can't spend four minutes claiming to be rolling out.

Exit zero only if it saw the revision reach running. Not seeing an outcome is not the
same as a good one: a dropped connection, a full stream pool, a platform too old to have the
route all leave a sc deploy && notify-the-team about to run against a deploy nobody
checked. Every one of those messages names --detach as the way to opt out on purpose.
Ctrl-C is the one exception — a withdrawn question rather than an unanswered one — so it
exits zero saying the deploy carries on without the terminal.

There is a subtle case in there the tests pin down: the platform's tail gives up after an
hour so a wedged revision cannot hold a slot, which arrives as an end frame with the
revision still moving. That closes the step as unfinished (Building — stopped watching after 2m14s) rather than as done; the first draft cheerfully printed Built in 45s for a
build that was still going.

Flags

Flag Does
--detach, -d Returns as soon as the upload is accepted, printing the revision and its url — exactly the behaviour before this PR
--no-build-logs Follows the step list but drops service/line frames. Purely client-side: the server sends everything, because which half you want is a display choice and not worth a query parameter every server would have to honour

Both work off a terminal.

Verification

go vet ./... clean, gofmt clean, go test -race ./... green — 18 new tests. The step
list is asserted as a whole transcript against a hand-wound clock, so "a build took 1m47s"
costs no wall time and the exact CI output is pinned rather than described.

Beyond the tests, the whole command was run end to end against a throttled stand-in platform
(a small Python server answering the preflight, reading the bundle slowly enough to move the
upload gauge, then streaming a scripted progress tail with real pauses) — every transcript
above is captured from that, including the pty run for the animated frames. Exit codes
checked for each: 0 running, 1 failed, 1 dropped mid-build, 0 --detach,
0 --no-build-logs.

Depends on

someones.computer_scclientFollowDeployment, ProgressFrame and DeploymentStatus
live there so sc-tray gets them too. go.mod and vendor/ are bumped to the merged
commit.

The last piece of [#293](https://git.grey.ooo/Grey.ooo/someones.computer/issues/293). [#21](https://git.grey.ooo/Grey.ooo/someones.computer_agent/pulls/21) gave `sc deploy` feedback for the half it does locally — packing, uploading — and then it stopped dead at the most interesting moment: the upload answers `201 pending` **before a worker has picked the revision up**, so everything a person means by "deploying" happened after `sc` had already exited, and all it could offer was a URL and an apology. It now stays and watches. ## What it looks like Off a terminal — a CI log, a pipe — the spinner degrades to one plain line per step and this is the whole transcript: ```text Deployment #12 queued — pending Watch it: http://.../applications/example-app/deployments/0198f3a1-… Waiting for a build worker… [S⠿C] Queued for 2s Building… api | Retrieving image manifest for golang:1.25-alpine api | Built cross stage deps api | Unpacking rootfs as cmd COPY . . requires it api | RUN go build -trimpath -o /out/api ./cmd/api api | Pushing image to registry.someones.computer/acme/api:12 web | Retrieving image manifest for node:22-alpine web | RUN npm ci --omit=dev web | Pushing image to registry.someones.computer/acme/web:12 [S⠿C] Built in 10s Waiting for the rollout… [S⠿C] Rolled out in 4s Deployment #12 is running. ``` On a terminal the bottom line is live — a still frame mid-build, the build output scrolling above a spinner that is counting: ```text [S⠿C] Uploaded 4.8 MiB in 2.4s Deployment #12 queued — pending Watch it: http://.../applications/example-app/deployments/0198f3a1-… [S⠿C] Queued for 2s api | Retrieving image manifest for golang:1.25-alpine api | Built cross stage deps api | Unpacking rootfs as cmd COPY . . requires it api | Taking snapshot of full filesystem... api | RUN go build -trimpath -o /out/api ./cmd/api api | Pushing image to registry.someones.computer/acme/api:12 web | Retrieving image manifest for node:22-alpine [S⠋C] Building 0:07 ``` …and a build that breaks, which exits **1** with the reason on stderr: ```text api | cmd/api/main.go:14:2: undefined: startServer api | error building image: error building stage: failed to execute command [S⠿C] Build failed after 4s ← red brackets on a terminal sc: Deployment #12 failed. kaniko exited 1 building service "api" ``` ## Two decisions worth arguing with **A step is coarser than a status.** `built` and `deploying` are one step — the instant between the images existing and the rollout starting, and the rollout itself — so the seam is a relabel in place rather than a step closed and reopened. Closing there would print `Rolled out in 0s` the moment the images existed, with the actual rollout still to come. The label still tracks the status (`Waiting for the rollout` → `Rolling out`), so a wedged deploy worker can't spend four minutes claiming to be rolling out. **Exit zero only if it saw the revision reach `running`.** Not seeing an outcome is not the same as a good one: a dropped connection, a full stream pool, a platform too old to have the route all leave a `sc deploy && notify-the-team` about to run against a deploy nobody checked. Every one of those messages names `--detach` as the way to opt out on purpose. Ctrl-C is the one exception — a withdrawn question rather than an unanswered one — so it exits zero saying the deploy carries on without the terminal. There is a subtle case in there the tests pin down: the platform's tail gives up after an hour so a wedged revision cannot hold a slot, which arrives as an `end` frame with the revision **still moving**. That closes the step as unfinished (`Building — stopped watching after 2m14s`) rather than as done; the first draft cheerfully printed `Built in 45s` for a build that was still going. ## Flags | Flag | Does | |---|---| | `--detach`, `-d` | Returns as soon as the upload is accepted, printing the revision and its `url` — exactly the behaviour before this PR | | `--no-build-logs` | Follows the step list but drops `service`/`line` frames. Purely client-side: the server sends everything, because which half you want is a display choice and not worth a query parameter every server would have to honour | Both work off a terminal. ## Verification `go vet ./...` clean, `gofmt` clean, `go test -race ./...` green — 18 new tests. The step list is asserted as a whole transcript against a hand-wound clock, so "a build took 1m47s" costs no wall time and the exact CI output is pinned rather than described. Beyond the tests, the whole command was run end to end against a throttled stand-in platform (a small Python server answering the preflight, reading the bundle slowly enough to move the upload gauge, then streaming a scripted progress tail with real pauses) — every transcript above is captured from that, including the pty run for the animated frames. Exit codes checked for each: `0` running, `1` failed, `1` dropped mid-build, `0` `--detach`, `0` `--no-build-logs`. ## Depends on `someones.computer_scclient` — `FollowDeployment`, `ProgressFrame` and `DeploymentStatus` live there so `sc-tray` gets them too. `go.mod` and `vendor/` are bumped to the merged commit.
The upload answers 201 pending before a worker has picked the revision up,
so everything a person means by "deploying" happened after sc had already
exited and all it could offer was a URL. It now attaches to the platform's
progress stream and draws the rest as a step list: a spinner per step
counting up, build output scrolling above it, each step leaving one line
behind.

A step is deliberately coarser than a status. `built` and `deploying` are
one step — the instant between the images existing and the rollout
starting, and the rollout itself — so the seam is a relabel in place rather
than a step closed and reopened, which would have printed "Rolled out in
0s" with the rollout still to come. The label still tracks the status, so a
wedged deploy worker cannot spend four minutes claiming to be rolling out.

It exits zero only having seen the revision reach running. Not seeing an
outcome is not the same as a good one: a failed build, a dropped
connection, a full stream pool and a platform too old to have the route all
leave a `sc deploy && …` about to run against a deploy nobody checked, and
each of those messages names --detach as the way to opt out on purpose.
Ctrl-C is the exception — a withdrawn question rather than an unanswered
one. So is an end frame arriving with the revision still moving, which is
the platform's tail giving up on its own hour-long deadline: that closes
the step as unfinished rather than calling a running build built.

  --detach, -d    return as soon as the upload is accepted
  --no-build-logs follow the steps but not the build output

The second is client-side only. The server sends everything, because which
half a reader wants is a display choice and not worth a query parameter
every server would then have to honour.
Bump scclient to the deploy-progress client
All checks were successful
CI / build (pull_request) Successful in 8m13s
011faf6d9f
FollowDeployment, ProgressFrame and DeploymentStatus land in the shared
client (someones.computer_scclient#1), where sc-tray gets them too. The
vendor tree is what CI builds from, so it moves with the pin.
geusebio scheduled this pull request to auto merge when all checks succeed 2026-08-09 10:18:04 +00:00
geusebio deleted branch feature/sc-deploy-follow-progress 2026-08-09 10:41:08 +00:00
Sign in to join this conversation.
No reviewers
No labels
in-progress
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Grey.ooo/someones.computer_agent!22
No description provided.