ceremony/.github/workflows/labels.yml
cluade-reviewer-andresmgsl f148255ace drill(0.4.1): rewrite the self-ref pin to the candidate SHA
The pure consumer path cannot resolve CEREMONY_SELF_REF: "0.4.1" before
the candidate creates that tag, and release.yml's self-checkout is
hardcoded to heavy-duty/ceremony — so the pin is rewritten to the
candidate SHA, which resolves there today. Drill scaffolding only; this
ref is never merged.
2026-08-04 10:31:03 +00:00

149 lines
7.1 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

name: labels
# Reusable half of the labels automation. Triggers and permissions live in
# the caller; docs/CONSUMERS.md carries the complete caller stub.
#
# The caller uses pull_request_target, not pull_request: every PR in this
# family arrives from a fork, where pull_request runs with a READ-ONLY token
# and cannot label anything. _target is safe in this workflow because no PR
# code is ever checked out or executed — scope reads changed paths and the
# path mapping via the API and checks out only the ceremony implementation,
# and reconcile checks out the BASE branch only. Keep it that way.
#
# There is no pull_request_review_target, so a review landing cannot wake this
# workflow directly — which is why the caller's cron is load-bearing, not a
# safety net (#199 relaxed it from */15 to hourly, but did NOT drop it). The
# cron is the sweep's only discovery path for every transition no subscribed
# event carries: a verdict landing, blocker:ci-red set/cleared, a
# blocker:conflict when another PR merges under this one, and the time-based
# stale / 48h claim-reclaim. Where an event IS subscribed the wake is direct —
# the handoff sets state:needs-human and the caller's `labeled` event confirms
# or corrects that optimistic write within seconds.
#
# This cannot loop: reconciler writes use GITHUB_TOKEN, and GitHub does not
# create workflow runs from GITHUB_TOKEN-triggered events. Agent writes use a
# PAT and therefore do trigger — exactly the asymmetry wanted.
on:
workflow_call:
env:
# A called workflow arrives without its repository. Keep this literal pin
# aligned with the ceremony release consumed by callers (issue #9 D3).
CEREMONY_SELF_REF: "9a229ee643291c27dbd404fa97e4dcc7e6ed3b17"
jobs:
scope:
# Not on labeled/unlabeled: those events change no paths, so scope has
# nothing new to derive — and label churn is precisely what they are.
# review_requested/review_request_removed likewise change no paths — they
# exist to wake reconcile (#137) — and running labeler on them widens
# exactly the window #130 documents, where a label written during a
# scope run is clobbered.
if: >-
github.event_name == 'pull_request_target' &&
github.event.action != 'labeled' &&
github.event.action != 'unlabeled' &&
github.event.action != 'review_requested' &&
github.event.action != 'review_request_removed'
runs-on: ubuntu-latest
concurrency:
group: labels-scope-${{ github.event.pull_request.number }}
cancel-in-progress: true
steps:
# actions/labeler@v5 held this seat until #130. Even with
# sync-labels: false it wrote the WHOLE label set — PUT of
# (labels-fetched-at-job-start derived) — so a label applied while
# the job ran was silently removed: ceremony#128 lost its `release`,
# the merge door's declared-intent read, two seconds after the
# builder set it. v6/v7 write the same way, so the step was replaced
# rather than repinned. labels-scope reads the consumer's
# .github/labeler.yml and the changed paths via the API, and its
# only write is an additive POST of the derived scopes: a label
# applied mid-job survives by construction.
#
# Still no PR code: both checkouts below fetch the ceremony
# implementation only. The dogfood checkout rides github.sha — the
# base-branch commit the workflow file itself came from, so the
# script and workflow can never skew — and doubles as the #11
# bootstrap: ceremony's own labels must work before any release tag
# exists for the pinned checkout to fetch.
- uses: actions/checkout@v4
if: github.repository == 'heavy-duty/ceremony'
with:
repository: ${{ github.repository }}
ref: ${{ github.sha }}
- uses: actions/checkout@v4
if: github.repository != 'heavy-duty/ceremony'
with:
repository: heavy-duty/ceremony
ref: ${{ env.CEREMONY_SELF_REF }}
- uses: ./actions/labels-scope
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
# the BASE branch commit — a PR must not label itself by editing
# the mapping it is judged by
CONFIG_REF: ${{ github.sha }}
reconcile:
runs-on: ubuntu-latest
# ONE shared group: every reconcile sweeps every open PR, so cron and
# PR-event runs must serialize or two sweeps race the same PR's labels
# and both pass the request-the-human-once guard.
concurrency:
group: labels-reconcile
cancel-in-progress: false
steps:
# pull_request_target is required for fork PR write permission. It is
# safe here because no PR code is ever checked out or executed:
# labels-scope reads the mapping and changed paths via the API, and
# reconcile checks out the BASE branch only. Keep it that way.
- uses: actions/checkout@v4
with:
repository: ${{ github.repository }}
ref: ${{ github.event.repository.default_branch }}
- uses: actions/checkout@v4
# The self-consumption bypass — release.yml's twin, and load-bearing
# for the same reason (#11): ceremony's own labels bootstrap must
# run BEFORE any release tag exists for this checkout to fetch — the
# release label the merge door reads is created by that dispatch, so
# without the bypass the first release deadlocks on its own pin. The
# base-branch checkout above already IS ceremony on the dogfood
# path.
if: github.repository != 'heavy-duty/ceremony'
with:
repository: heavy-duty/ceremony
ref: ${{ env.CEREMONY_SELF_REF }}
path: .ceremony-src
# Two steps, mutually exclusive `if:`s, because a `uses:` path must be
# a literal — the same fork release.yml's CEREMONY_DIR env line
# papers over for `run:` steps, which composite `uses:` has no
# equivalent of.
- name: reconcile state + stale
if: github.repository != 'heavy-duty/ceremony'
uses: ./.ceremony-src/actions/labels-reconcile
with:
bootstrap: ${{ github.event_name == 'workflow_dispatch' && 'yes' || 'no' }}
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
- name: reconcile state + stale (dogfood — the workspace IS ceremony)
if: github.repository == 'heavy-duty/ceremony'
uses: ./actions/labels-reconcile
with:
bootstrap: ${{ github.event_name == 'workflow_dispatch' && 'yes' || 'no' }}
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
- name: reconcile issue flow
if: github.repository != 'heavy-duty/ceremony'
uses: ./.ceremony-src/actions/issueflow-reconcile
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
- name: reconcile issue flow (dogfood — the workspace IS ceremony)
if: github.repository == 'heavy-duty/ceremony'
uses: ./actions/issueflow-reconcile
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}