mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-09 14:02:56 +09:00
Three gaps in the guards that decide whether the published candidate is still the tree being tagged. All three passed silently rather than failing, which is the worst shape for a check that runs immediately before a write-once name is created. `normalized_refs` piped `collect_image_refs` straight into its parsing loop, so the pipeline reported `sort -u`'s status and the collector's was discarded. POSIX sh has no `pipefail` and this script has to stay POSIX, so `set -e` never saw it: a failed or truncated collection became a smaller set and the rc-vs-candidate comparison matched it happily. The verifier then reported that no container bytes changed across promotion having proven nothing — on the guard that carries that entire claim. Collect into a file first, so a nonzero collector aborts before anything is compared. An empty collection is the same failure wearing a zero exit status and needs its own check: two empty sets compare equal, so the proof passes having examined nothing, and every packages tree carries image references — none means the collector understood nothing it was handed. The rc-leftovers scan swallowed grep's stderr, turning "could not read this file" into "this file is clean" — the silent-skip shape hack/promote-rewrite-tags.sh refuses by name. The `|| true` has to stay, because `-exec … +` reports the legitimate "nothing matched" as a failure too, so the diagnostics are the only remaining signal that a file went unscanned. Fail on any of them. Defence in depth rather than a live hole: a surviving rc string would also fail the per-file compare, since the release tree has none. The per-file content comparison had no test at all. It is the only leg that catches a file present on BOTH sides with different bytes — exactly the threat the job exists for, a maintainer editing packages/ on the release PR after promotion published the candidate. The file-set compare sees the same names, the installer compare reads one file, and the rc-refs compare never looks at the release tree. Replacing the comparison with a no-op left all nine other tests green. Red phases, all observed before the fixes. The content-compare case changes one non-image line on one side, so no other leg can fire and claim the credit; it fails with the comparison no-op'd and passes with it restored. The two collector cases run the verifier from a copy whose sibling lib/ holds a stub — which is how the workflows already invoke it, `.release-tooling/hack/…` with libraries resolved next to the script, so the failure is reached the way production would reach it. Against the piped version both fail on `[ "$rc" -ne 0 ]`: the verifier exits 0 and prints its success line whether the collector returns 3 or returns nothing. Also pins `e2e`'s dependency on the candidate gate. The contract pinned `e2e-report`'s, so removing `verify-release-candidate` from `e2e`'s `needs` together with the `success` clause on its release arm left all six tests green — and running a full-e2e promotion suite against a candidate that never verified is the worse of the two failures. Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com> Assisted-By: Claude <noreply@anthropic.com>