diff --git a/.github/workflows/semver-label.yml b/.github/workflows/semver-label.yml index 84f0fd1..2a8d2ee 100644 --- a/.github/workflows/semver-label.yml +++ b/.github/workflows/semver-label.yml @@ -10,9 +10,24 @@ name: Semver Label # It is wired as a required status check on main separately (branch ruleset, # managed in dataviking-infra) — this file only defines the check. # -# `labeled` / `unlabeled` are in the trigger list so the check re-evaluates -# immediately when labels change; the event payload's label set reflects the -# state after the change. +# Two design constraints, both forced by auto-tag.yml's "Verify CI checks +# passed" step, which refuses to tag while ANY non-green check run sits on +# the merged head SHA: +# +# 1. Labels are fetched fresh from the API, not read from the event +# payload. Label events don't move the head SHA, and reruns replay the +# original payload — a payload-based run that failed before the label +# was added could never be rerun to green, permanently poisoning the +# SHA for auto-tag. +# +# 2. Once the check passes, it re-runs any superseded non-green runs of +# itself on the same SHA (e.g. the `opened` run that correctly failed +# before the label existed, moments later). Thanks to (1), those +# reruns re-fetch the labels and flip green, leaving the SHA clean. +# +# No concurrency/cancel-in-progress: a cancelled run is just another +# non-green conclusion parked on the SHA (this exact failure blocked the +# v1.6.0 recovery), and runs here take ~2 seconds anyway. on: pull_request: @@ -20,10 +35,7 @@ on: permissions: contents: read - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number }} - cancel-in-progress: true + actions: write # rerun superseded runs of this workflow (step 2) jobs: semver-label: @@ -32,10 +44,12 @@ jobs: steps: - name: Require exactly one semver label env: - LABELS_JSON: ${{ toJSON(github.event.pull_request.labels.*.name) }} + GH_TOKEN: ${{ github.token }} + PR_NUMBER: ${{ github.event.pull_request.number }} run: | set -euo pipefail - echo "PR labels: ${LABELS_JSON}" + LABELS_JSON=$(gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/labels?per_page=100" --jq '[.[].name]') + echo "PR labels (fresh fetch): ${LABELS_JSON}" SEMVER_LABELS=$(echo "$LABELS_JSON" | jq -r '.[] | select(startswith("semver:"))') COUNT=$(echo "$SEMVER_LABELS" | grep -c . || true) @@ -59,3 +73,23 @@ jobs: exit 1 ;; esac + + - name: Flip superseded non-green runs of this check green + env: + GH_TOKEN: ${{ github.token }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + set -euo pipefail + RUN_IDS=$(gh api "repos/${GITHUB_REPOSITORY}/actions/workflows/semver-label.yml/runs?head_sha=${HEAD_SHA}&per_page=100" \ + --jq ".workflow_runs[] | select(.status == \"completed\" and .conclusion != \"success\" and .id != ${GITHUB_RUN_ID}) | .id") + + if [ -z "$RUN_IDS" ]; then + echo "No superseded non-green runs on ${HEAD_SHA} — nothing to heal." + exit 0 + fi + + for id in $RUN_IDS; do + echo "Re-running superseded run ${id} so it re-fetches labels and goes green" + gh api -X POST "repos/${GITHUB_REPOSITORY}/actions/runs/${id}/rerun" \ + || echo "::warning::Could not re-run ${id} (fork PR token lacks actions:write?). Re-run it manually before merge, or Auto Semver Tag will refuse to tag this SHA." + done