diff --git a/actions/labels-reconcile/action.yml b/actions/labels-reconcile/action.yml index 36257fe..90a5f53 100644 --- a/actions/labels-reconcile/action.yml +++ b/actions/labels-reconcile/action.yml @@ -23,7 +23,8 @@ runs: BOOTSTRAP: ${{ inputs.bootstrap }} LABELS_CONF: ${{ github.workspace }}/.github/labels.conf run: | - if [ "$BOOTSTRAP" = yes ]; then - export GITHUB_EVENT_NAME=workflow_dispatch - fi + # BOOTSTRAP passes through as-is: the script gates on the input. The + # export-the-event-name hack that lived here died with ceremony#215 — + # the script keyed on GITHUB_EVENT_NAME, which #209 made true for + # every machine wake, so "no" could never mean no. bash "$GITHUB_ACTION_PATH/labels-reconcile.sh" diff --git a/actions/labels-reconcile/labels-reconcile.sh b/actions/labels-reconcile/labels-reconcile.sh index 449ab12..72e8375 100755 --- a/actions/labels-reconcile/labels-reconcile.sh +++ b/actions/labels-reconcile/labels-reconcile.sh @@ -28,8 +28,9 @@ fi # stale approval must never promote unreviewed code to the human. # # DRY_RUN=1 narrates every mutation instead of performing it (how this script -# is rehearsed against the live repo). A workflow_dispatch run also bootstraps -# the taxonomy (label create --force) — that heal is dispatch-only; the cron +# is rehearsed against the live repo). A run with BOOTSTRAP=yes also +# bootstraps the taxonomy (label create --force) — the operator's manual +# dispatch defaults the input to yes; every machine wake passes no. The cron # sweep tolerates a missing label rather than recreating it. # # The state machine below is pure (globals in, state out) and covered by @@ -1021,8 +1022,15 @@ main() { load_config "$LABELS_CONF" NOW="$(date +%s)" - if [ "${GITHUB_EVENT_NAME:-}" = workflow_dispatch ]; then - log "workflow_dispatch: bootstrapping the taxonomy" + # The bootstrap keys on the INPUT, never the event name. It used to test + # GITHUB_EVENT_NAME = workflow_dispatch — correct while an operator's manual + # dispatch was the only dispatch there was, and wrong from #209 on, when the + # trigger job made EVERY event-woken sweep a workflow_dispatch run: the + # bootstrap=no input became inert by construction, and every board event + # re-upserted the taxonomy (ceremony#215 — runs 459/523, then venue drill + # runs 16/17, which bootstrapped on a delivered "no" and caught this). + if [ "${BOOTSTRAP:-no}" = yes ]; then + log "bootstrap=yes: bootstrapping the taxonomy" bootstrap_labels fi diff --git a/changelog.d/215.md b/changelog.d/215.md index 9411adf..160ee13 100644 --- a/changelog.d/215.md +++ b/changelog.d/215.md @@ -31,3 +31,17 @@ - The same test drives the four value paths — schedule-empty, `no`, `yes`, invalid — through the shipped expressions into the action's real validator (#215). + +- The taxonomy bootstrap keys on the `BOOTSTRAP` input, never the event name. + It tested `GITHUB_EVENT_NAME = workflow_dispatch` — correct while an + operator's manual dispatch was the only dispatch there was, inert-by- + construction from #209 on, when every machine wake became a dispatch + event (#215). + +- The venue drill caught that: with the bridge delivering `no` perfectly, + drill runs 16/17 still bootstrapped, because the script never read the + input the whole chain existed to deliver (#215). + +- `test/labels-reconcile.test.sh` pins the regression pair exactly: a + `workflow_dispatch` event with `BOOTSTRAP=no` (or unset) creates and + deletes nothing; only `BOOTSTRAP=yes` bootstraps (#215). diff --git a/test/labels-reconcile.test.sh b/test/labels-reconcile.test.sh index d9e9a9a..696fb9f 100755 --- a/test/labels-reconcile.test.sh +++ b/test/labels-reconcile.test.sh @@ -1148,17 +1148,18 @@ EOF chmod +x "$EXEC/stub/gh" printf 'panel=bot-a bot-b bot-c\n' >"$EXEC/labels.conf" -exec_env() { # $1 = event name → the real script, executed under the PATH stub +exec_env() { # $1 = event name, $2 = BOOTSTRAP value ("" = unset) : >"$EXEC/record" env PATH="$EXEC/stub:$PATH" GH_RECORD="$EXEC/record" \ CEREMONY_FORGE=github \ REPO=owner/repo LABELS_CONF="$EXEC/labels.conf" GITHUB_EVENT_NAME="$1" \ + ${2:+BOOTSTRAP="$2"} \ bash actions/labels-reconcile/labels-reconcile.sh } exec_rc=0 -exec_out="$(exec_env workflow_dispatch 2>&1)" || exec_rc=$? -expect "an executed dispatch with all six absent completes green" 0 "$exec_rc" +exec_out="$(exec_env workflow_dispatch yes 2>&1)" || exec_rc=$? +expect "an executed bootstrap=yes with all six absent completes green" 0 "$exec_rc" expect "...reaching the end of the sweep" \ yes "$(grep -q 'reconciled.' <<<"$exec_out" && echo yes || echo no)" expect "...having attempted all six deletions" \ @@ -1167,12 +1168,18 @@ expect "...and created the full taxonomy" \ "$(core_label_rows | cut -d'|' -f1)" \ "$(sed -n 's/^create //p' "$EXEC/record")" -# -- bootstrap is dispatch-only, deletes included: the cron and -# pull_request_target paths touch no label -for ev in schedule pull_request_target; do +# -- the bootstrap keys on the INPUT, never the event (ceremony#215): from +# #209 on, EVERY machine wake is a workflow_dispatch event, so an event +# gate made bootstrap=no inert — runs 459/523 and venue drill runs 16/17 +# bootstrapped on a delivered "no". The regression case is exactly that +# pair: dispatch event, no. +for pair in "workflow_dispatch:no" "workflow_dispatch:" "schedule:no" "schedule:" "pull_request_target:"; do + ev="${pair%%:*}"; bs="${pair#*:}" ev_rc=0 - exec_env "$ev" >/dev/null 2>&1 || ev_rc=$? - expect "the $ev path completes green" 0 "$ev_rc" + exec_env "$ev" "$bs" >/dev/null 2>&1 || ev_rc=$? + expect "the $ev event with BOOTSTRAP='${bs:-unset}' completes green" 0 "$ev_rc" + expect "...and creates nothing" \ + no "$(grep -q '^create ' "$EXEC/record" && echo yes || echo no)" expect "...and deletes nothing" \ no "$(grep -q '^delete ' "$EXEC/record" && echo yes || echo no)" done