109 lines
4.9 KiB
YAML
109 lines
4.9 KiB
YAML
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 — labeler reads changed paths via the
|
|
# API, 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 — and the */15 cron is advisory: GitHub deprioritises
|
|
# short intervals hard enough that a quiet repo goes hours between ticks. The
|
|
# handoff wakes the sweep itself: the author sets state:needs-human, and the
|
|
# caller's `labeled` event confirms or corrects that optimistic write within
|
|
# seconds. The cron stays as the last resort for a forgotten handoff.
|
|
#
|
|
# 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: "0.1.0"
|
|
|
|
jobs:
|
|
scope:
|
|
# Not on labeled/unlabeled: those events change no paths, so labeler has
|
|
# nothing new to derive — and label churn is precisely what they are.
|
|
if: >-
|
|
github.event_name == 'pull_request_target' &&
|
|
github.event.action != 'labeled' &&
|
|
github.event.action != 'unlabeled'
|
|
runs-on: ubuntu-latest
|
|
concurrency:
|
|
group: labels-scope-${{ github.event.pull_request.number }}
|
|
cancel-in-progress: true
|
|
steps:
|
|
- uses: actions/labeler@v5
|
|
with:
|
|
# labeler reads the consumer's .github/labeler.yml via the API
|
|
# additive only — a hand-applied scope must survive the machine
|
|
sync-labels: false
|
|
|
|
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: labeler
|
|
# reads 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 }}
|