labels-sweep — pass bootstrap through the workflow_call boundary it was lost at (#215) #218

Merged
andres merged 6 commits from build/215-bootstrap-bridge into main 2026-08-06 16:53:04 +00:00
4 changed files with 27 additions and 23 deletions
Showing only changes of commit ceaf66bd13 - Show all commits

View file

@ -37,12 +37,13 @@ on:
bootstrap: bootstrap:
description: >- description: >-
Bootstrap the label taxonomy before sweeping. The CALLER passes Bootstrap the label taxonomy before sweeping. The CALLER passes
this through from its own workflow_dispatch input; a called this through from its own workflow_dispatch input. The measured
workflow cannot read the caller's dispatch inputs on every forge invariant (ceremony#215): the value must be DECLARED here and
(Forgejo drops them at the workflow_call boundary — ceremony#215, EXPLICITLY passed — on this instance the called workflow did not
probe runs 6/7 vs merged runs 459/523), so the value must arrive see the caller's event inputs as an implicit substitute (runs
through this declared boundary, never via github.event.inputs. 459/523 bootstrapped on a bootstrap=no dispatch) while the
Absent means "no": an event- or cron-woken sweep must never top-level caller received the value in both contexts (probe runs
6/7). Absent means "no": an event- or cron-woken sweep must never
re-upsert ~20 labels. re-upsert ~20 labels.
type: string type: string
required: false required: false
@ -101,14 +102,14 @@ jobs:
# #
# bootstrap: read from the DECLARED workflow_call input and nothing # bootstrap: read from the DECLARED workflow_call input and nothing
# else. The old gate read `github.event.inputs.bootstrap` from inside # else. The old gate read `github.event.inputs.bootstrap` from inside
# this called workflow — which Forgejo leaves empty at the # this called workflow, and on this instance that context arrived
# workflow_call boundary even though the top-level caller receives the # empty (runs 459/523: every dispatch-woken sweep bootstrapped on a
# value in both contexts (ceremony#215; probe runs 6/7 measured the # bootstrap=no body) while the top-level caller received the value in
# boundary, merged runs 459/523 paid for it: every dispatch-woken # both contexts (probe runs 6/7) — ceremony#215. The reliable channel
# sweep bootstrapped). The caller passes the value through # is declare-and-pass, so that is the only one used. The caller passes
# `with.bootstrap`; anything not exactly "yes" is fed through for # the value through `with.bootstrap`; anything not exactly yes|no is
# labels-reconcile's own yes|no validation to judge, so a typo refuses # fed through for labels-reconcile's own validation to judge, so a
# loudly instead of silently bootstrapping. # typo refuses loudly instead of silently bootstrapping.
- name: reconcile state + stale - name: reconcile state + stale
if: github.repository != 'heavy-duty/ceremony' if: github.repository != 'heavy-duty/ceremony'
uses: ./.ceremony-src/actions/labels-reconcile uses: ./.ceremony-src/actions/labels-reconcile

View file

@ -45,9 +45,10 @@ jobs:
# named `labels` (self-labels.yml). # named `labels` (self-labels.yml).
uses: ./.github/workflows/labels-sweep.yml uses: ./.github/workflows/labels-sweep.yml
with: with:
# The dispatch input crosses the workflow_call boundary HERE, or not at # The dispatch input crosses the workflow_call boundary HERE, or not
# all: the called workflow cannot read this caller's dispatch inputs on # at all: on this instance the called workflow did not see this
# this forge (ceremony#215). On `schedule` the top-level context is # caller's event inputs implicitly (ceremony#215), so declare-and-pass
# is the only channel used. On `schedule` the top-level context is
# empty, and empty maps to "no" EXPLICITLY — a cron that bootstraps is # empty, and empty maps to "no" EXPLICITLY — a cron that bootstraps is
# the failure kimi named before it could exist. # the failure kimi named before it could exist.
bootstrap: ${{ inputs.bootstrap || 'no' }} bootstrap: ${{ inputs.bootstrap || 'no' }}

View file

@ -1,8 +1,9 @@
### Fixed ### Fixed
- The sweep's `bootstrap` value crosses the `workflow_call` boundary as a - The sweep's `bootstrap` value crosses the `workflow_call` boundary as a
declared input passed by the caller — a called workflow cannot read the declared input, explicitly passed by the caller — the one channel measured
caller's dispatch inputs on this forge (#215). to work; the called workflow did not see the caller's event inputs as an
implicit substitute on this instance (#215).
- Before the bridge, `github.event.inputs` was empty inside the called - Before the bridge, `github.event.inputs` was empty inside the called
workflow, so every dispatch-woken sweep bootstrapped: ~20 label upserts on workflow, so every dispatch-woken sweep bootstrapped: ~20 label upserts on

View file

@ -459,10 +459,11 @@ jobs:
sweep: sweep:
uses: heavy-duty/ceremony/.github/workflows/labels-sweep.yml@<pinned-tag> uses: heavy-duty/ceremony/.github/workflows/labels-sweep.yml@<pinned-tag>
with: with:
# Pass the dispatch input through the workflow_call boundary — the # Pass the dispatch input through the workflow_call boundary
# called workflow cannot read this caller's dispatch inputs on every # explicitly — a called workflow must not rely on reading the caller's
# forge (ceremony#215). Empty (schedule) maps to "no" explicitly, so a # event inputs (ceremony#215 measured that failing). Empty (schedule)
# cron-woken sweep never re-upserts the taxonomy. # maps to "no" explicitly, so a cron-woken sweep never re-upserts the
# taxonomy.
bootstrap: ${{ inputs.bootstrap || 'no' }} bootstrap: ${{ inputs.bootstrap || 'no' }}
# If this repo's PR-facing labels caller is named anything but `labels`, # If this repo's PR-facing labels caller is named anything but `labels`,
# pass that name alongside: `pr_workflow_name: <name>`. The sweep exports # pass that name alongside: `pr_workflow_name: <name>`. The sweep exports