From 9ebceaab4090bf5d252bd3a35c7249464fde48dc Mon Sep 17 00:00:00 2001 From: njord Date: Sat, 18 Jul 2026 13:23:50 -0500 Subject: [PATCH] ci(semver-label): fetch labels fresh + self-heal superseded runs so auto-tag can verify the SHA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first cut of this check read labels from the event payload and used cancel-in-progress concurrency. Both interact badly with 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: - Label events don't move the head SHA, so the opened-event run that (correctly) failed or was cancelled before the label landed lingers non-green on the very SHA auto-tag verifies. This exact interaction blocked the v1.6.0 recovery: PR #583's auto-tag run refused to tag because the superseded Semver Label run sat cancelled on its head SHA. - Payload-based runs can never be healed: a rerun replays the original payload, so it stays red even after the label is added. Now the check fetches labels fresh from the API (rerunnable to green), drops concurrency cancellation (a cancelled conclusion is just another way to poison the SHA; runs take ~2s), and — once green — re-runs any superseded non-green runs of itself on the same SHA so they re-fetch labels and flip green, leaving the SHA clean for auto-tag. Co-Authored-By: Claude Fable 5 --- .github/workflows/semver-label.yml | 52 ++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 9 deletions(-) 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