actions/* + lib/* + CHANGELOG — merge upstream 0.6.0 onto the forge tree, and port every gh call site it brought (#198) #204
3 changed files with 85 additions and 1 deletions
|
|
@ -49,6 +49,15 @@ LABELS=""
|
||||||
# retirement heals the board instead of stranding a label nothing recomputes.
|
# retirement heals the board instead of stranding a label nothing recomputes.
|
||||||
RETIRED=(state:needs-rebase)
|
RETIRED=(state:needs-rebase)
|
||||||
STALE_AFTER=$((48 * 3600))
|
STALE_AFTER=$((48 * 3600))
|
||||||
|
# The workflow whose runs checks_state must never grade — its own (#208).
|
||||||
|
# GITHUB_WORKFLOW is ambient in every Actions step and names the CALLER (the
|
||||||
|
# consumer's PR-facing workflow, since consumers name the caller), so this
|
||||||
|
# self-serves with no workflow-file change. The explicit override exists for
|
||||||
|
# two readers: the fixtures, and #209's detached sweep caller, which will
|
||||||
|
# need to point this at the PR-facing caller's name once reconcile no longer
|
||||||
|
# runs inside it. Empty means "filter nothing" — a caller outside Actions
|
||||||
|
# (a local rehearsal, an older pin) must not silently start dropping entries.
|
||||||
|
SELF_WORKFLOW="${SELF_WORKFLOW:-${GITHUB_WORKFLOW:-}}"
|
||||||
|
|
||||||
# The needs-ruling invariants (#52) — one implementation for both surfaces.
|
# The needs-ruling invariants (#52) — one implementation for both surfaces.
|
||||||
# shellcheck source=lib/ruling.sh
|
# shellcheck source=lib/ruling.sh
|
||||||
|
|
@ -221,7 +230,27 @@ checks_state() { # rollup JSON on stdin → SUCCESS | FAILURE | PENDING | NONE |
|
||||||
# a context whose entries are ALL cancelled never reported at all (a killed
|
# a context whose entries are ALL cancelled never reported at all (a killed
|
||||||
# or timed-out required job), so it keeps CANCELLED and still blocks —
|
# or timed-out required job), so it keeps CANCELLED and still blocks —
|
||||||
# discard needs a surviving verdict, never an empty context.
|
# discard needs a surviving verdict, never an empty context.
|
||||||
jq -r '
|
#
|
||||||
|
# And one exclusion that comes before every rule above: the label machine
|
||||||
|
# never grades its own runs (#208). Every reconcile sweep serializes
|
||||||
|
# through one shared concurrency group, and GitHub records a displaced
|
||||||
|
# queued run as CANCELLED — there is no "superseded" conclusion for queue
|
||||||
|
# displacement. When the displaced run was born from a pull_request_target
|
||||||
|
# event, that cancelled entry attaches to the victim PR while its
|
||||||
|
# SUCCESSOR — triggered by a different PR or an issues event — attaches
|
||||||
|
# elsewhere, so the #139 carve-out's premise (a surviving sibling on the
|
||||||
|
# same PR) fails structurally: on the victim the newest self entry stays
|
||||||
|
# CANCELLED, the deny-list scores it FAILURE, and the sweep sets
|
||||||
|
# blocker:ci-red off its own corpse — then re-affirms it every cadence.
|
||||||
|
# Proven on crew#227: every real check green, the only red rollup entry
|
||||||
|
# the sweep's own displaced run. So drop every entry belonging to
|
||||||
|
# $SELF_WORKFLOW before the newest-per-context collapse. Accepted
|
||||||
|
# consequences: a rollup of ONLY self entries scores NONE (honestly: no
|
||||||
|
# checks — never SUCCESS), and a genuine reconcile failure surfaces on the
|
||||||
|
# Actions tab instead of as blocker:ci-red, which is right because no PR
|
||||||
|
# edit can fix the label machinery. An empty $self filters nothing — the
|
||||||
|
# exclusion must never widen into dropping entries on a guess.
|
||||||
|
jq -r --arg self "$SELF_WORKFLOW" '
|
||||||
if (has("statusCheckRollup") | not) then "UNREADABLE" else
|
if (has("statusCheckRollup") | not) then "UNREADABLE" else
|
||||||
|
|
||||||
# NEUTRAL and SKIPPED satisfy branch protection — a skipped required check
|
# NEUTRAL and SKIPPED satisfy branch protection — a skipped required check
|
||||||
|
|
@ -262,7 +291,11 @@ checks_state() { # rollup JSON on stdin → SUCCESS | FAILURE | PENDING | NONE |
|
||||||
# and treating it as newest keeps an undateable in-flight run from being
|
# and treating it as newest keeps an undateable in-flight run from being
|
||||||
# discarded in favour of a stale success. Every ambiguity resolves toward
|
# discarded in favour of a stale success. Every ambiguity resolves toward
|
||||||
# "not settled".
|
# "not settled".
|
||||||
|
# The #208 exclusion (header above): self entries leave the rollup here,
|
||||||
|
# BEFORE the group_by — a self-only context must vanish entirely, never
|
||||||
|
# survive as an all-cancelled context that still classifies FAILURE.
|
||||||
| [ (.statusCheckRollup // [])[]
|
| [ (.statusCheckRollup // [])[]
|
||||||
|
| select($self == "" or (.workflowName // "") != $self)
|
||||||
| { ctx: [.workflowName // "", .name // .context // ""],
|
| { ctx: [.workflowName // "", .name // .context // ""],
|
||||||
at: ([.startedAt, .createdAt, .completedAt]
|
at: ([.startedAt, .createdAt, .completedAt]
|
||||||
| map(select(type == "string" and . != ""
|
| map(select(type == "string" and . != ""
|
||||||
|
|
|
||||||
11
changelog.d/208.md
Normal file
11
changelog.d/208.md
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- `checks_state` drops rollup entries belonging to the workflow it runs
|
||||||
|
inside — `SELF_WORKFLOW`, defaulting to the ambient `GITHUB_WORKFLOW` —
|
||||||
|
before the newest-per-context collapse: the label machine never grades
|
||||||
|
its own runs, and an empty name filters nothing (#208).
|
||||||
|
- A sweep displaced from the shared concurrency queue attaches CANCELLED to
|
||||||
|
its PR while its successor attaches elsewhere, so the sweep set
|
||||||
|
`blocker:ci-red` off its own displaced run and re-affirmed it every
|
||||||
|
cadence (crew#227). A rollup of only self entries now honestly scores
|
||||||
|
NONE (#208).
|
||||||
|
|
@ -351,6 +351,13 @@ run_() { jq -n --arg n "$1" --arg o "$2" --arg t "${3:-2026-07-20T15:00:00Z}" \
|
||||||
ctx_() { jq -n --arg n "$1" --arg s "$2" --arg t "${3:-2026-07-20T15:00:00Z}" \
|
ctx_() { jq -n --arg n "$1" --arg s "$2" --arg t "${3:-2026-07-20T15:00:00Z}" \
|
||||||
'{__typename:"StatusContext", context:$n, state:$s, createdAt:$t}'; }
|
'{__typename:"StatusContext", context:$n, state:$s, createdAt:$t}'; }
|
||||||
|
|
||||||
|
# Pinned empty for every fixture below except the #208 block, which sets its
|
||||||
|
# own. The script defaults SELF_WORKFLOW from the ambient GITHUB_WORKFLOW —
|
||||||
|
# present in any CI run of this suite — and an inherited name that happened
|
||||||
|
# to match a fixture's workflowName ("ci", "labels") would silently drop
|
||||||
|
# entries these fixtures rely on. The verdicts must not flip with the runner.
|
||||||
|
SELF_WORKFLOW=""
|
||||||
|
|
||||||
expect "no checks at all is NONE" NONE "$(rollup '[]' | checks_state)"
|
expect "no checks at all is NONE" NONE "$(rollup '[]' | checks_state)"
|
||||||
# A failed fetch leaves no rollup KEY; a PR with no checks leaves an empty
|
# A failed fetch leaves no rollup KEY; a PR with no checks leaves an empty
|
||||||
# ARRAY. Collapsing the two let an API hiccup read as "nothing is failing" —
|
# ARRAY. Collapsing the two let an API hiccup read as "nothing is failing" —
|
||||||
|
|
@ -429,6 +436,39 @@ expect "a cancelled newest over an earlier FAILURE is still that failure" FAILUR
|
||||||
"$(rollup "[$(rec_ FAILURE 2026-07-24T12:16:17Z 2026-07-24T12:17:06Z),\
|
"$(rollup "[$(rec_ FAILURE 2026-07-24T12:16:17Z 2026-07-24T12:17:06Z),\
|
||||||
$(rec_ CANCELLED 2026-07-24T12:16:41Z 2026-07-24T12:16:41Z)]" | checks_state)"
|
$(rec_ CANCELLED 2026-07-24T12:16:41Z 2026-07-24T12:16:41Z)]" | checks_state)"
|
||||||
|
|
||||||
|
# -- the #208 exclusion: the label machine never grades its own runs. The
|
||||||
|
# shared concurrency group displaces queued sweeps as CANCELLED, and the
|
||||||
|
# displaced run's successor was triggered by a DIFFERENT PR or an issues
|
||||||
|
# event — so on the victim PR the #139 carve-out's premise (a surviving
|
||||||
|
# sibling on the same head) fails structurally: the newest self entry
|
||||||
|
# stays CANCELLED, and the sweep set blocker:ci-red off its own corpse,
|
||||||
|
# re-affirming it every cadence. Proven on crew#227: every real check
|
||||||
|
# green, the only red rollup entry the sweep's own displaced run. rec_
|
||||||
|
# already builds entries under workflowName "labels"; naming that as
|
||||||
|
# self must drop them whole, before the newest-per-context collapse.
|
||||||
|
SELF_WORKFLOW="labels"
|
||||||
|
expect "a displaced self CANCELLED beside green others is no verdict (crew#227)" SUCCESS \
|
||||||
|
"$(rollup "[$(run_ a SUCCESS),$(run_ b SUCCESS),\
|
||||||
|
$(rec_ CANCELLED 2026-08-01T15:17:56Z 2026-08-01T15:17:59Z)]" | checks_state)"
|
||||||
|
expect "a FAILED self run surfaces on the Actions tab, not as the PR's red" SUCCESS \
|
||||||
|
"$(rollup "[$(run_ a SUCCESS),$(run_ b SUCCESS),\
|
||||||
|
$(rec_ FAILURE 2026-08-01T15:00:00Z 2026-08-01T15:01:00Z)]" | checks_state)"
|
||||||
|
expect "a rollup of ONLY self entries is honestly NONE, never SUCCESS" NONE \
|
||||||
|
"$(rollup "[$(rec_ CANCELLED 2026-08-01T15:17:56Z 2026-08-01T15:17:59Z)]" | checks_state)"
|
||||||
|
# must-fail: the filter keys on the self workflow ALONE. Widening it — any
|
||||||
|
# cancelled entry, any labels-shaped name — certifies a genuine foreign
|
||||||
|
# failure green, which is #136's unknown-as-green shape all over again.
|
||||||
|
expect "a genuine foreign FAILURE still blocks beside a cancelled self entry" FAILURE \
|
||||||
|
"$(rollup "[$(run_ a FAILURE),\
|
||||||
|
$(rec_ CANCELLED 2026-08-01T15:17:56Z 2026-08-01T15:17:59Z)]" | checks_state)"
|
||||||
|
# ...and an empty self filters NOTHING: outside Actions no workflow name is
|
||||||
|
# ambient, and the exclusion must never drop entries on a guess — the same
|
||||||
|
# displaced-self rollup keeps blocking there, all-cancelled context intact.
|
||||||
|
SELF_WORKFLOW=""
|
||||||
|
expect "an empty SELF_WORKFLOW filters nothing — the same rollup still blocks" FAILURE \
|
||||||
|
"$(rollup "[$(run_ a SUCCESS),$(run_ b SUCCESS),\
|
||||||
|
$(rec_ CANCELLED 2026-08-01T15:17:56Z 2026-08-01T15:17:59Z)]" | checks_state)"
|
||||||
|
|
||||||
# -- a run still IN FLIGHT. `run_()` cannot express this: it always carries a
|
# -- a run still IN FLIGHT. `run_()` cannot express this: it always carries a
|
||||||
# real completedAt, which is exactly why the supersede rule shipped dating
|
# real completedAt, which is exactly why the supersede rule shipped dating
|
||||||
# runs by completion and nothing caught it. Both spellings of "no
|
# runs by completion and nothing caught it. Both spellings of "no
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue