forked from heavy-duty/ceremony
docs(runner-probes): enumerate the real carriers, spare the consumer checkouts, and make the snippet run (#202)
@codex-reviewer-andresmgsl's three, all verified against current main before
fixing.
1. THERE ARE THREE SELF-REF CARRIERS, NOT TWO — labels-sweep.yml:52,
labels.yml:51, release.yml:132. My `[ "$n" -eq 2 ]` came from the
pre-upstream tree, so it would have REJECTED a correctly armed candidate and
told the operator to rewrite two of three, leaving one workflow pinned to
the tag. The gate enumerates from the tree now, with the derivation commands
beside the table so the list is re-checked rather than trusted.
2. NOT EVERY `repository:` BELONGS TO THE FORK. Three are
`${{ github.repository }}` — labels-sweep.yml:69, labels.yml:92,
release-exercise.yml:72 — and they fetch the CALLER's repository. My loop
required every one to equal the fork, which would have rewritten the
consumer checkouts and quietly changed what the probe exercises. Internal
self-checkouts (four) are asserted to be the fork; consumer checkouts are
asserted to stay dynamic.
3. EACH CHECK IS BOUND TO THE TREE IT IS ABOUT — `git -C "$armed"` for the
carriers, `git -C "$probe"` for the callers, instead of depending on the
operator's current directory. And `mapfile` rather than `git grep | while …
fail`: the loop ran in a pipeline subshell, so `fail` exited the subshell
and the gate carried on. Collect first, validate after, under a declared
`set -euo pipefail`.
And the snippet is now executable rather than illustrative: placeholders became
positional parameters, so it parses, is shellcheck-clean, and runs. Driven
against the unarmed tree it refuses with `CEREMONY_SELF_REF=0.6.0` — a tag
rather than the candidate SHA, which is exactly the case it exists to catch.
Publishing a gate that could not run would have been the same defect one level
up.
Branch updated from merged main (e236318). test/run.sh 28/28; shellcheck 0.10.0
and changelog-armed clean.
Refs #202
This commit is contained in:
parent
8c3c37d412
commit
dc87051c69
2 changed files with 72 additions and 35 deletions
|
|
@ -33,8 +33,12 @@
|
|||
equal the candidate code SHA, and callers match the layer they belong to
|
||||
(#202).
|
||||
|
||||
- It counts the `CEREMONY_SELF_REF` carriers as well as comparing them, which
|
||||
is what catches one that vanished rather than being rewritten (#202).
|
||||
- It enumerates the carriers from the tree rather than encoding a count, and
|
||||
distinguishes ceremony's internal self-checkouts from the consumer checkouts
|
||||
that must stay `${{ github.repository }}` (#202).
|
||||
|
||||
- The published snippet parses, lints clean and runs: driven against an unarmed
|
||||
tree it refuses, naming the carrier (#202).
|
||||
|
||||
- Probe results are written to an issue in the probe repo and carried to the
|
||||
ceremony issue by a human, so the probe holds no path that can write to the
|
||||
|
|
|
|||
|
|
@ -124,43 +124,76 @@ So:
|
|||
- reusable-workflow callers →
|
||||
`<identity>/ceremony/.github/workflows/<file>@<armed-workflow-sha>`, since
|
||||
that is the only revision whose inner checkout is rewritten.
|
||||
4. **Gate the arming POSITIVELY, against the armed tree.** Absence of the old
|
||||
literal is not proof of correct arming: a negative grep stays green if
|
||||
`CEREMONY_SELF_REF` names a tag, the *armed* SHA or some other commit; if a
|
||||
carrier was rewritten to the wrong fork; if an executable carrier lives
|
||||
outside `.github`; or if a carrier simply disappeared
|
||||
(@codex-reviewer-andresmgsl, #202 review). So assert what each carrier
|
||||
**is**, not merely what it is not:
|
||||
4. **Gate the arming POSITIVELY, and against the tree each check is about.**
|
||||
Absence of the old literal proves nothing: a negative grep stays green if
|
||||
`CEREMONY_SELF_REF` names a tag or the *armed* SHA, if a carrier went to the
|
||||
wrong fork, or if a carrier vanished instead of being rewritten. So assert
|
||||
what each carrier **is** — and enumerate them, because the set is a property
|
||||
of the current tree rather than a number to remember
|
||||
(@codex-reviewer-andresmgsl, #202 review).
|
||||
|
||||
**The carriers on `main` today.** Re-derive this list each time rather than
|
||||
trusting it; workflows come and go:
|
||||
|
||||
```sh
|
||||
fork=<identity>/ceremony ; code_sha=<candidate-code-sha> ; armed_sha=<armed-workflow-sha>
|
||||
fail() { echo "arming incomplete: $*" >&2; exit 1; }
|
||||
|
||||
# every self-checkout points at the fork
|
||||
for v in $(git grep -hoP '(?<=repository:\s)\S+' -- .github); do
|
||||
[ "$v" = "$fork" ] || fail "repository: $v"
|
||||
done
|
||||
# both CEREMONY_SELF_REF carriers name the CANDIDATE CODE sha
|
||||
n=0
|
||||
for v in $(git grep -hoP '(?<=CEREMONY_SELF_REF:\s)"?\K[^"]+' -- .github); do
|
||||
[ "$v" = "$code_sha" ] || fail "CEREMONY_SELF_REF: $v"; n=$((n+1))
|
||||
done
|
||||
[ "$n" -eq 2 ] || fail "expected 2 CEREMONY_SELF_REF carriers, found $n"
|
||||
# and in the PROBE repo: workflows pin the armed sha, actions the code sha
|
||||
git grep -hoP '(?<=uses:\s)\S+' -- .github | while read -r u; do
|
||||
case "$u" in
|
||||
*/.github/workflows/*) [ "${u##*@}" = "$armed_sha" ] || fail "workflow caller $u" ;;
|
||||
*/actions/*) [ "${u##*@}" = "$code_sha" ] || fail "action caller $u" ;;
|
||||
esac
|
||||
done
|
||||
# the negative check stays, as a cheap extra rather than as the proof
|
||||
! git grep -qE '(uses:|repository:)[[:space:]]*heavy-duty/ceremony' -- .github \
|
||||
|| fail "canonical coordinate still present"
|
||||
git grep -n 'CEREMONY_SELF_REF:' -- .github/workflows
|
||||
git grep -n 'repository:' -- .github/workflows
|
||||
```
|
||||
|
||||
**A count, not just a comparison** — `n -eq 2` is what catches a carrier
|
||||
that vanished rather than being rewritten, which a per-value loop alone
|
||||
cannot see.
|
||||
| carrier | on current `main` | armed value |
|
||||
|---|---|---|
|
||||
| `CEREMONY_SELF_REF` | `labels-sweep.yml`, `labels.yml`, `release.yml` | the **candidate code** SHA |
|
||||
| internal self-checkout | `labels-sweep.yml`, `labels.yml`, `release.yml` ×2 | `<identity>/ceremony` |
|
||||
| **consumer** checkout | `labels-sweep.yml`, `labels.yml`, `release-exercise.yml` | **unchanged** — `${{ github.repository }}` |
|
||||
|
||||
That third row is the one an over-broad rewrite destroys. Those checkouts
|
||||
fetch the **caller's** repository; pointing them at the fork changes what
|
||||
the probe exercises into something else entirely.
|
||||
|
||||
```sh
|
||||
#!/usr/bin/env bash
|
||||
# check-arming <armed-checkout> <probe-checkout> <fork> <code-sha> <armed-sha>
|
||||
set -euo pipefail
|
||||
armed="$1"; probe="$2"; fork="$3"; code_sha="$4"; armed_sha="$5"
|
||||
fail() { echo "arming incomplete: $*" >&2; exit 1; }
|
||||
|
||||
# (a) every self-ref carrier names the CANDIDATE CODE sha — enumerated, so a
|
||||
# carrier that vanished is a failure rather than one fewer loop pass.
|
||||
mapfile -t refs < <(git -C "$armed" grep -hoP '(?<=CEREMONY_SELF_REF: ")[^"]+' \
|
||||
-- .github/workflows)
|
||||
[ "${#refs[@]}" -gt 0 ] || fail "no CEREMONY_SELF_REF carriers found"
|
||||
for v in "${refs[@]}"; do [ "$v" = "$code_sha" ] || fail "CEREMONY_SELF_REF=$v"; done
|
||||
|
||||
# (b) internal checkouts point at the fork; consumer checkouts stay dynamic.
|
||||
mapfile -t repos < <(git -C "$armed" grep -hoP '(?<=repository: ).*' \
|
||||
-- .github/workflows)
|
||||
for v in "${repos[@]}"; do
|
||||
case "$v" in
|
||||
'${{ github.repository }}') ;; # the caller's repo — must NOT change
|
||||
"$fork") ;; # an internal self-checkout, armed
|
||||
*) fail "repository: $v" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# (c) in the PROBE repo: workflow callers pin the armed sha, action callers
|
||||
# the code sha.
|
||||
mapfile -t uses < <(git -C "$probe" grep -hoP '(?<=uses: ).*' -- .github)
|
||||
for u in "${uses[@]}"; do
|
||||
case "$u" in
|
||||
*/.github/workflows/*@*) [ "${u##*@}" = "$armed_sha" ] || fail "workflow caller $u" ;;
|
||||
*/actions/*@*) [ "${u##*@}" = "$code_sha" ] || fail "action caller $u" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# (d) the negative check, as a cheap extra rather than as the proof.
|
||||
! git -C "$armed" grep -qE '(uses:|repository:)[[:space:]]*heavy-duty/ceremony' \
|
||||
-- .github || fail "canonical coordinate still present"
|
||||
```
|
||||
|
||||
**`mapfile`, not a pipeline.** `git grep | while … fail` runs the loop in a
|
||||
subshell, so `fail` exits *that* subshell and the gate continues. Collect
|
||||
first, validate after — and run under `set -euo pipefail`, which the snippet
|
||||
declares rather than assumes.
|
||||
|
||||
5. **Invoke the probe by the event it is about**, and record which: a
|
||||
`workflow_dispatch`, or the real board event under test. A probe that fires
|
||||
|
|
|
|||
Loading…
Reference in a new issue