diff --git a/.github/workflows/label-policy-sanity.yml b/.github/workflows/label-policy-sanity.yml new file mode 100644 index 0000000..67d4590 --- /dev/null +++ b/.github/workflows/label-policy-sanity.yml @@ -0,0 +1,63 @@ +name: Label Policy Sanity + +on: + pull_request: + paths: + - ".github/workflows/labeler.yml" + - ".github/workflows/auto-response.yml" + push: + paths: + - ".github/workflows/labeler.yml" + - ".github/workflows/auto-response.yml" + +concurrency: + group: label-policy-sanity-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + contributor-tier-consistency: + runs-on: blacksmith-2vcpu-ubuntu-2404 + timeout-minutes: 10 + steps: + - name: Checkout + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + + - name: Verify contributor-tier parity across workflows + shell: bash + run: | + set -euo pipefail + python3 - <<'PY' + import re + from pathlib import Path + + files = [ + Path('.github/workflows/labeler.yml'), + Path('.github/workflows/auto-response.yml'), + ] + + parsed = {} + for path in files: + text = path.read_text(encoding='utf-8') + rules = re.findall(r'\{ label: "([^"]+ contributor)", minMergedPRs: (\d+) \}', text) + color_match = re.search(r'const contributorTierColor = "([0-9A-Fa-f]{6})"', text) + if not color_match: + raise SystemExit(f'failed to parse contributorTierColor in {path}') + parsed[str(path)] = { + 'rules': rules, + 'color': color_match.group(1).upper(), + } + + baseline = parsed[str(files[0])] + for path in files[1:]: + entry = parsed[str(path)] + if entry != baseline: + raise SystemExit( + 'contributor-tier mismatch between workflows: ' + f'{files[0]}={baseline} vs {path}={entry}' + ) + + print('contributor tier rules/color are consistent across label workflows') + PY diff --git a/.github/workflows/workflow-sanity.yml b/.github/workflows/workflow-sanity.yml index 45b9cac..f353144 100644 --- a/.github/workflows/workflow-sanity.yml +++ b/.github/workflows/workflow-sanity.yml @@ -62,47 +62,3 @@ jobs: - name: Lint GitHub workflows uses: rhysd/actionlint@393031adb9afb225ee52ae2ccd7a5af5525e03e8 # v1.7.11 - - contributor-tier-consistency: - runs-on: blacksmith-2vcpu-ubuntu-2404 - timeout-minutes: 10 - steps: - - name: Checkout - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - - name: Verify contributor-tier parity across workflows - shell: bash - run: | - set -euo pipefail - python3 - <<'PY' - import re - from pathlib import Path - - files = [ - Path('.github/workflows/labeler.yml'), - Path('.github/workflows/auto-response.yml'), - ] - - parsed = {} - for path in files: - text = path.read_text(encoding='utf-8') - rules = re.findall(r'\{ label: "([^"]+ contributor)", minMergedPRs: (\d+) \}', text) - color_match = re.search(r'const contributorTierColor = "([0-9A-Fa-f]{6})"', text) - if not color_match: - raise SystemExit(f'failed to parse contributorTierColor in {path}') - parsed[str(path)] = { - 'rules': rules, - 'color': color_match.group(1).upper(), - } - - baseline = parsed[str(files[0])] - for path in files[1:]: - entry = parsed[str(path)] - if entry != baseline: - raise SystemExit( - 'contributor-tier mismatch between workflows: ' - f'{files[0]}={baseline} vs {path}={entry}' - ) - - print('contributor tier rules/color are consistent across label workflows') - PY diff --git a/docs/ci-map.md b/docs/ci-map.md index e642d36..af37881 100644 --- a/docs/ci-map.md +++ b/docs/ci-map.md @@ -25,6 +25,8 @@ Merge-blocking checks should stay small and deterministic. Optional checks are u - Purpose: dependency advisories (`cargo audit`) and policy/license checks (`cargo deny`) - `.github/workflows/release.yml` (`Release`) - Purpose: build tagged release artifacts and publish GitHub releases +- `.github/workflows/label-policy-sanity.yml` (`Label Policy Sanity`) + - Purpose: enforce contributor-tier rule/color parity between `labeler.yml` and `auto-response.yml` ### Optional Repository Automation @@ -60,6 +62,7 @@ Merge-blocking checks should stay small and deterministic. Optional checks are u - `Release`: tag push (`v*`) - `Security Audit`: push to `main`, PRs to `main`, weekly schedule - `Workflow Sanity`: PR/push when `.github/workflows/**`, `.github/*.yml`, or `.github/*.yaml` change +- `Label Policy Sanity`: PR/push when `.github/workflows/labeler.yml` or `.github/workflows/auto-response.yml` changes - `PR Labeler`: `pull_request_target` lifecycle events - `PR Auto Responder`: issue opened/labeled, `pull_request_target` opened/labeled - `Stale`: daily schedule, manual dispatch @@ -73,8 +76,9 @@ Merge-blocking checks should stay small and deterministic. Optional checks are u 3. Release failures on tags: inspect `.github/workflows/release.yml`. 4. Security failures: inspect `.github/workflows/security.yml` and `deny.toml`. 5. Workflow syntax/lint failures: inspect `.github/workflows/workflow-sanity.yml`. -6. Docs failures in CI: inspect `docs-quality` job logs in `.github/workflows/ci.yml`. -7. Strict delta lint failures in CI: inspect `lint-strict-delta` job logs and compare with `BASE_SHA` diff scope. +6. Label policy parity failures: inspect `.github/workflows/label-policy-sanity.yml`. +7. Docs failures in CI: inspect `docs-quality` job logs in `.github/workflows/ci.yml`. +8. Strict delta lint failures in CI: inspect `lint-strict-delta` job logs and compare with `BASE_SHA` diff scope. ## Maintenance Rules