deploy: the reap step can no longer fail a run that deployed (nix-config#927) #1961
No reviewers
Labels
No labels
backlog
bug
enhancement
in-progress
review
todo
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
ymrtech/nix-config!1961
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "kb/NCFG-927-hostdeploy-reap"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes card nix-config#927.
host-deploy.yml's last step (Reap this run's deploy scratch) failed EVERY run, so a host deploy whose switch, health gate and verify were all green was publishedFAILED.Root cause (reproduced, not inferred)
Forgejo invokes each step as
bash -e {0}. The step wrote onlyset -uo pipefail, so the inherited-estayed live. Its count was:lsexits 2 when the glob matches NOTHING — exactly the state the step's ownrm -rf "$IMPL"leaves behind when this run's checkout was the last one under/tmp. SoAFTER="$(count)"failed and-eaborted the shell before the script's ownexit 0.Running the extracted step body under
bash -e -o pipefailwith an empty match on vpn: exit 2, output stopping right after thereaped …line, never reaching theBEFORE -> AFTERecho.Measured 2026-10-13 on rev
c5d795bd, all four lanes:reaped this run's implementation checkout: /tmp/host-deploy-impl-26096/[runner]: RUN exit status 2A green deploy reported red four times in a row is a status its consumers learn to ignore.
Change
count()usesfind, which reports 0 whether or not anything matched (and counts directories, the class this step creates and deletes).-eexplicitly (set +eafterset -uo pipefail), because a best-effort cleanup must not depend on every future line staying individually immune.set -ustays.Pin
New
.forgejo/tests/test-host-deploy-reap.shextracts the step'srun:block out of the workflow it ships in (so the pin follows the real step, never a copy that drifts), re-points the single/tmpliteral at a sandbox — refusing to execute a body that still names the host's real/tmp— and executes it under the runner's ownbash -e -o pipefail:0 before -> 0 after)nix-build-check.yml'srepo-gatesnext to the host-fanout pinLocally:
7/7 OK.test-pipefail-guard.sh12/12,test-host-fanout.sh96/0,test-dispatch-input-types.sh,test-workflow-paths-filter.sh13/0 all green; both changed workflows parse under yq.NOT IN SCOPE: moving the scratch off
/tmp(nix-config#921) and the tmpfs/tmpchange (nix-config#901).The step is the last of every host deploy and its own comment says it is best-effort with exit 0, but Forgejo invokes each step as `bash -e {0}` and the step only wrote `set -uo pipefail` — the inherited `-e` was never cleared. Its count was `ls -d /tmp/host-deploy-impl-* 2>/dev/null | wc -l`, and `ls` exits 2 when the glob matches nothing, which is exactly the state its own `rm -rf "$IMPL"` leaves behind when this run's checkout was the last one under /tmp. So `AFTER="$(count)"` failed and `-e` aborted the shell before the script's own `exit 0`. Measured 2026-10-13 on revc5d795bd: runs 14769 (mail), 14770 (vpn), 14771 (public) and 14772 (vpn-mx) each published FAILED. In 14772 steps 22-26 were all green (switch 1m3s, health gate + auto-rollback, Verify, Publish the deploy status) and step[27] was the only failure, ending on `reaped this run's implementation checkout: /tmp/host-deploy-impl-26096` then `RUN exit status 2` — a green deploy reported red, four times, which is a status its consumers would learn to ignore. Reproduced locally by running the extracted step body under `bash -e -o pipefail` with an empty match: exit 2, stopping before the count echo. Two changes, and the second is the durable one: the count uses `find`, which reports 0 whether or not anything matched (and counts directories, the class this step creates), and the step disarms the runner's `-e` explicitly because a best-effort cleanup must not depend on every future line staying immune. Pinned by .forgejo/tests/test-host-deploy-reap.sh, which EXTRACTS the step's `run:` block out of the workflow it ships in and EXECUTES it under the runner's own `bash -e -o pipefail` in a sandbox (the `/tmp` literal re-pointed, and it refuses to run a body that still names the host's real /tmp): empty scratch, this run's checkout as the only entry, and a stale sibling must each exit 0. A MUTATION arm runs the pre-fix count shape in the same sandbox and requires it to go RED (it exits 2), so the assertion is discriminating. Wired into nix-build-check.yml's repo-gates next to the host-fanout pin. SURFACES: .forgejo/workflows/host-deploy.ymlcacec85c6803888e4e95