From d620b0f41f13ddddf4e0293d096cdafcbafd2cbf Mon Sep 17 00:00:00 2001 From: Codex Date: Sat, 26 Sep 2026 03:28:42 +0000 Subject: [PATCH] Report "Installing ployz" as the first Build Step, failed when the install fails --- README.md | 4 ++-- build.sh | 10 ++++++++-- prepare.sh | 13 ++++++++++++- test.sh | 5 +++-- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 524c1a7..5c5e0b1 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,10 @@ Inputs are visible in GitHub, so none of them is secret. {"grant": "ployzgrant1:…", "commit": "<40 hex>", "fingerprint": "<64 hex>", "ployzVersion": "", "deployment": { "snapshots": [{ "resolvedEnv": {} }] }} ``` The token, the grant, and every `resolvedEnv` value are masked (`::add-mask::`, per line) before anything else runs. Lines under 4 characters are not: masking `0` would star out every 0 in the log. A check-in that can't reach Cloud is retried; an HTTP error is not, and the job fails with Cloud's status and message. -4. Installs exactly `ployzVersion` from `https://ployz.sh`: the version of the Cloud process that computed `fingerprint`, so they match even while Cloud rolls out a new version. The installer verifies the release checksum. If the install fails, the Action sends Cloud a final report naming the version (`installFailed`), so Cloud moves the build on to the next Builder. +4. Installs exactly `ployzVersion` from `https://ployz.sh`: the version of the Cloud process that computed `fingerprint`, so they match even while Cloud rolls out a new version. The installer verifies the release checksum. The install is timed as the first Build Step, "Installing ployz". If it fails, the Action sends Cloud a final report with that step failed and naming the version (`installFailed`), so Cloud moves the build on to the next Builder. 5. Checks out `commit` without persisting credentials. 6. Runs `PLOYZ_BUILD_GRANT=… ployz build --deployment --commit --fingerprint --events `. The deployment file lives in `$RUNNER_TEMP` and is deleted when the job ends, pass or fail. -7. Reports the Build Steps while it builds, every few seconds, each time with a fresh OIDC token: `POST {cloud}/api/builds/{build}/steps` with `{"from": , "events": []}`, where `from` is the 0-based line the batch starts at. Cloud files only lines it hasn't taken, so a retried batch is harmless. As soon as `ployz build` prints its result (the image is in the Machine), or when it fails, the last batch adds `"platforms": [...]`; empty means the build failed. That report settles the build, and Cloud takes no more. `ployz build` then uploads the build cache to the GitHub Actions cache; a failed upload is only a warning. +7. Reports the Build Steps while it builds, every few seconds, each time with a fresh OIDC token: `POST {cloud}/api/builds/{build}/steps` with `{"from": , "events": []}`, where `from` is the 0-based line the batch starts at. The lines are the "Installing ployz" step, then `ployz build --events`, which includes the push and its per-layer progress. Cloud files only lines it hasn't taken, so a retried batch is harmless. As soon as `ployz build` prints its result (the image is in the Machine), or when it fails, the last batch adds `"platforms": [...]`; empty means the build failed. That report settles the build, and Cloud takes no more. `ployz build` then uploads the build cache to the GitHub Actions cache; a failed upload is only a warning. Output `digest` is the manifest digest the Machine received. Cloud does not trust it: it reads the pushed digest from the Machine when it ends the grant. diff --git a/build.sh b/build.sh index b3b576a..7f94db9 100755 --- a/build.sh +++ b/build.sh @@ -16,13 +16,19 @@ export PLOYZ_BUILD_GRANT : >"$events" sent=0 +# Every event line so far: prepare.sh's "Installing ployz", then the build's own. +steps() { + [[ ! -f "$work/install.jsonl" ]] || cat "$work/install.jsonl" + cat "$events" +} + # Posts the complete event lines not sent yet, from line $sent; with $1, the platforms built, the # build's end. A fresh OIDC token proves this run each time. Best effort: a refused batch is resent. report() { local lines - lines=$(wc -l <"$events") + lines=$(steps | wc -l) [[ -n "${1:-}" || $lines -gt $sent ]] || return 0 - tail -n "+$((sent + 1))" "$events" | head -n "$((lines - sent))" | + steps | tail -n "+$((sent + 1))" | head -n "$((lines - sent))" | jq -cs --argjson from "$sent" --argjson platforms "${1:-null}" \ '{from: $from, events: .} + (if $platforms == null then {} else {platforms: $platforms} end)' >"$work/steps.json" oidc_token "$cloud" diff --git a/prepare.sh b/prepare.sh index f01a915..cfc4d9a 100755 --- a/prepare.sh +++ b/prepare.sh @@ -60,10 +60,20 @@ version=$(jq -r '.ployzVersion' "$work/check-in.json") # The exact version that computed the fingerprint: during a Cloud rollout, the Cloud that # dispatched this build may run another version than the one that answered the check-in. +# The install is the first Build Step, timed here where it runs, as a `ployz build --events` line: +# build.sh reports it ahead of the build's own. $1: why it failed. +install_started=$(date -u +%FT%T.%3NZ) +install_step() { + jq -cn --argjson at "$(date +%s%3N)" --arg started "$install_started" --arg completed "$(date -u +%FT%T.%3NZ)" --arg error "${1:-}" \ + '{at: $at, event: {Build: {Step: {id: "install", name: "Installing ployz", started: $started, completed: $completed, + cached: false, error: (if $error == "" then null else $error end)}}}}' >"$work/install.jsonl" +} # A failed install never reaches build.sh, so its final report goes from here, naming the version: # Cloud then moves the build on to the next Builder instead of waiting for the run to end. install_failed() { - jq -n --arg version "$version" '{from: 0, events: [], platforms: [], installFailed: $version}' >"$work/steps.json" + install_step "Could not install ployz $version." + jq -n --arg version "$version" --slurpfile events "$work/install.jsonl" \ + '{from: 0, events: $events, platforms: [], installFailed: $version}' >"$work/steps.json" oidc_token "$cloud" cloud_post "$cloud/api/builds/$PLOYZ_BUILD_ID/steps" "$work/steps-response.json" \ -H "Content-Type: application/json" --data-binary "@$work/steps.json" || @@ -72,6 +82,7 @@ install_failed() { } curl -fsSL https://ployz.sh -o "$work/install.sh" || install_failed PLOYZ_VERSION="$version" INSTALL_BIN_DIR="$work/bin" sh "$work/install.sh" || install_failed +install_step echo "$work/bin" >>"$GITHUB_PATH" echo "commit=$commit" >>"$GITHUB_OUTPUT" diff --git a/test.sh b/test.sh index cee77a2..a370fbb 100755 --- a/test.sh +++ b/test.sh @@ -86,7 +86,7 @@ grep -qxF "digest=sha256:abc" "$GITHUB_OUTPUT" || { echo "FAIL: digest output" > # Steps go to Cloud while the build runs, each batch from where the last one ended; the last carries the platforms. # shellcheck disable=SC2016 # jq variables, not shell ones reported='length >= 2 and .[0].from == 0 and (.[0] | has("platforms") | not) - and ([.[].events[].at] == [1, 2, 3]) and (. as $b | all(range(1; $b | length); $b[.].from == $b[. - 1].from + ($b[. - 1].events | length)))' + and ([.[].events[].at][1:] == [1, 2, 3]) and .[0].events[0].event.Build.Step.name == "Installing ployz" and (. as $b | all(range(1; $b | length); $b[.].from == $b[. - 1].from + ($b[. - 1].events | length)))' jq -s -e "$reported"' and .[-1].platforms == ["linux/amd64"]' "$tmp/posted.jsonl" >/dev/null || { echo "FAIL: Build Steps not posted as they happened" >&2; cat "$tmp/posted.jsonl" >&2; exit 1; } [ -e "$tmp/reported-during-export" ] || { echo "FAIL: the final report waited for the cache export" >&2; exit 1; } @@ -115,7 +115,8 @@ grep -qF "::error::Ployz Cloud refused the check-in for build b-1 (HTTP 409: Thi # A failed install tells Cloud, naming the version, so Cloud moves the build on; the job fails. rm -f "$tmp/posted.jsonl" if log=$(INSTALL_FAIL=1 "$here/prepare.sh" 2>&1); then echo "FAIL: a failed install passed" >&2; exit 1; fi -jq -s -e '. == [{from: 0, events: [], platforms: [], installFailed: "0.1.0-beta.28"}]' "$tmp/posted.jsonl" >/dev/null || +jq -s -e 'length == 1 and (.[0] | .from == 0 and .platforms == [] and .installFailed == "0.1.0-beta.28" + and (.events | length == 1 and .[0].event.Build.Step.error == "Could not install ployz 0.1.0-beta.28."))' "$tmp/posted.jsonl" >/dev/null || { echo "FAIL: a failed install did not report" >&2; cat "$tmp/posted.jsonl" >&2; exit 1; } grep -qxF "::error::Could not install ployz 0.1.0-beta.28." <<<"$log" || { echo "FAIL: a failed install did not say why" >&2; echo "$log" >&2; exit 1; }