From df2ce636c802499fa60f263f7bfd4bc95cf6e615 Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Thu, 26 Mar 2026 14:57:04 +0100 Subject: [PATCH] fix(ci): make mark-stable main-only and cancel stale runs --- .github/workflows/mark-stable.yml | 112 +++--------------- scripts/github/check-tagged-commit-on-main.sh | 12 ++ .../github/mark-stable-if-highest-version.sh | 43 +++++++ scripts/github/wait-for-main-ci-success.sh | 43 +++++++ 4 files changed, 114 insertions(+), 96 deletions(-) create mode 100755 scripts/github/check-tagged-commit-on-main.sh create mode 100755 scripts/github/mark-stable-if-highest-version.sh create mode 100755 scripts/github/wait-for-main-ci-success.sh diff --git a/.github/workflows/mark-stable.yml b/.github/workflows/mark-stable.yml index b4428a6..5f4130e 100644 --- a/.github/workflows/mark-stable.yml +++ b/.github/workflows/mark-stable.yml @@ -1,8 +1,8 @@ name: Mark stable commit concurrency: - group: mark-${{ github.repository }}-${{ github.ref_name }} - cancel-in-progress: false + group: mark-stable-${{ github.repository }}-main + cancel-in-progress: true on: push: @@ -11,109 +11,29 @@ on: jobs: mark-stable: runs-on: ubuntu-latest + timeout-minutes: 330 permissions: actions: read contents: write steps: - - name: Wait for CI success on main for this commit - env: - GH_TOKEN: ${{ github.token }} - run: | - set -euo pipefail - - SHA="${GITHUB_SHA}" - API_URL="https://api.github.com/repos/${GITHUB_REPOSITORY}/actions/workflows/ci.yml/runs?head_sha=${SHA}&event=push&per_page=20" - WAIT_INTERVAL_SECONDS=20 - MAX_ATTEMPTS=360 # 2 hours max wait - - STATUS="" - CONCLUSION="" - - echo "Waiting for CI on main for ${SHA} (up to 2 hours)..." - for attempt in $(seq 1 "${MAX_ATTEMPTS}"); do - RESPONSE="$(curl -fsSL \ - -H "Authorization: Bearer ${GH_TOKEN}" \ - -H "Accept: application/vnd.github+json" \ - "${API_URL}")" - - STATUS="$(printf '%s' "${RESPONSE}" | jq -r '.workflow_runs[] | select(.head_branch=="main") | .status' | head -n1)" - CONCLUSION="$(printf '%s' "${RESPONSE}" | jq -r '.workflow_runs[] | select(.head_branch=="main") | .conclusion' | head -n1)" - - if [[ -n "${STATUS}" ]]; then - echo "CI status=${STATUS} conclusion=${CONCLUSION:-none} (attempt ${attempt}/${MAX_ATTEMPTS})" - else - echo "No CI run for main found yet (attempt ${attempt}/${MAX_ATTEMPTS})" - fi - - if [[ "${STATUS}" == "completed" ]]; then - if [[ "${CONCLUSION}" == "success" ]]; then - echo "CI succeeded for ${SHA}." - break - fi - echo "CI failed for ${SHA} (conclusion=${CONCLUSION})." - exit 1 - fi - - sleep "${WAIT_INTERVAL_SECONDS}" - done - - if [[ "${STATUS}" != "completed" || "${CONCLUSION}" != "success" ]]; then - echo "Timed out waiting for successful CI on main for ${SHA}." - exit 1 - fi - - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 - fetch-tags: true # We need all tags for version comparison + fetch-tags: true # We need tags and main history for version comparison + + - name: Check whether tagged commit is on main + id: branch-check + run: bash scripts/github/check-tagged-commit-on-main.sh + + - name: Wait for CI success on main for this commit + if: steps.branch-check.outputs.is_on_main == 'true' + env: + GH_TOKEN: ${{ github.token }} + run: bash scripts/github/wait-for-main-ci-success.sh - name: Move 'stable' tag only if this version is the highest - run: | - set -euo pipefail - - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - - echo "Ref: $GITHUB_REF" - echo "SHA: $GITHUB_SHA" - - VERSION="${GITHUB_REF#refs/tags/}" - echo "Current version tag: ${VERSION}" - - echo "Collecting all version tags..." - ALL_V_TAGS="$(git tag --list 'v*' || true)" - - if [[ -z "${ALL_V_TAGS}" ]]; then - echo "No version tags found. Skipping stable update." - exit 0 - fi - - echo "All version tags:" - echo "${ALL_V_TAGS}" - - # Determine highest version using natural version sorting - LATEST_TAG="$(printf '%s\n' ${ALL_V_TAGS} | sort -V | tail -n1)" - - echo "Highest version tag: ${LATEST_TAG}" - - if [[ "${VERSION}" != "${LATEST_TAG}" ]]; then - echo "Current version ${VERSION} is NOT the highest version." - echo "Stable tag will NOT be updated." - exit 0 - fi - - echo "Current version ${VERSION} IS the highest version." - echo "Updating 'stable' tag..." - - # Delete existing stable tag (local + remote) - git tag -d stable 2>/dev/null || true - git push origin :refs/tags/stable || true - - # Create new stable tag - git tag stable "$GITHUB_SHA" - git push origin stable - - echo "✅ Stable tag updated to ${VERSION}." + if: steps.branch-check.outputs.is_on_main == 'true' + run: bash scripts/github/mark-stable-if-highest-version.sh diff --git a/scripts/github/check-tagged-commit-on-main.sh b/scripts/github/check-tagged-commit-on-main.sh new file mode 100755 index 0000000..cd6fce0 --- /dev/null +++ b/scripts/github/check-tagged-commit-on-main.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -euo pipefail + +git fetch --no-tags origin main + +if git merge-base --is-ancestor "${GITHUB_SHA}" "origin/main"; then + echo "is_on_main=true" >> "$GITHUB_OUTPUT" + echo "Tagged commit ${GITHUB_SHA} is contained in origin/main." +else + echo "is_on_main=false" >> "$GITHUB_OUTPUT" + echo "Tagged commit ${GITHUB_SHA} is not contained in origin/main. Skipping stable update." +fi diff --git a/scripts/github/mark-stable-if-highest-version.sh b/scripts/github/mark-stable-if-highest-version.sh new file mode 100755 index 0000000..f1f436e --- /dev/null +++ b/scripts/github/mark-stable-if-highest-version.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +set -euo pipefail + +git config user.name "github-actions[bot]" +git config user.email "github-actions[bot]@users.noreply.github.com" + +echo "Ref: $GITHUB_REF" +echo "SHA: $GITHUB_SHA" + +VERSION="${GITHUB_REF#refs/tags/}" +echo "Current version tag: ${VERSION}" + +echo "Collecting all version tags..." +ALL_V_TAGS="$(git tag --list 'v*' || true)" + +if [[ -z "${ALL_V_TAGS}" ]]; then + echo "No version tags found. Skipping stable update." + exit 0 +fi + +echo "All version tags:" +echo "${ALL_V_TAGS}" + +LATEST_TAG="$(printf '%s\n' "${ALL_V_TAGS}" | sort -V | tail -n1)" + +echo "Highest version tag: ${LATEST_TAG}" + +if [[ "${VERSION}" != "${LATEST_TAG}" ]]; then + echo "Current version ${VERSION} is NOT the highest version." + echo "Stable tag will NOT be updated." + exit 0 +fi + +echo "Current version ${VERSION} IS the highest version." +echo "Updating 'stable' tag..." + +git tag -d stable 2>/dev/null || true +git push origin :refs/tags/stable || true + +git tag stable "$GITHUB_SHA" +git push origin stable + +echo "Stable tag updated to ${VERSION}." diff --git a/scripts/github/wait-for-main-ci-success.sh b/scripts/github/wait-for-main-ci-success.sh new file mode 100755 index 0000000..20e11d3 --- /dev/null +++ b/scripts/github/wait-for-main-ci-success.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +set -euo pipefail + +SHA="${GITHUB_SHA}" +API_URL="https://api.github.com/repos/${GITHUB_REPOSITORY}/actions/workflows/ci.yml/runs?head_sha=${SHA}&event=push&per_page=20" +WAIT_INTERVAL_SECONDS=20 +MAX_ATTEMPTS=990 # 5 hours 30 minutes max wait + +STATUS="" +CONCLUSION="" + +echo "Waiting for CI on main for ${SHA} (up to 5 hours 30 minutes)..." +for attempt in $(seq 1 "${MAX_ATTEMPTS}"); do + RESPONSE="$(curl -fsSL \ + -H "Authorization: Bearer ${GH_TOKEN}" \ + -H "Accept: application/vnd.github+json" \ + "${API_URL}")" + + STATUS="$(printf '%s' "${RESPONSE}" | jq -r '.workflow_runs[] | select(.head_branch=="main") | .status' | head -n1)" + CONCLUSION="$(printf '%s' "${RESPONSE}" | jq -r '.workflow_runs[] | select(.head_branch=="main") | .conclusion' | head -n1)" + + if [[ -n "${STATUS}" ]]; then + echo "CI status=${STATUS} conclusion=${CONCLUSION:-none} (attempt ${attempt}/${MAX_ATTEMPTS})" + else + echo "No CI run for main found yet (attempt ${attempt}/${MAX_ATTEMPTS})" + fi + + if [[ "${STATUS}" == "completed" ]]; then + if [[ "${CONCLUSION}" == "success" ]]; then + echo "CI succeeded for ${SHA}." + break + fi + echo "CI failed for ${SHA} (conclusion=${CONCLUSION})." + exit 1 + fi + + sleep "${WAIT_INTERVAL_SECONDS}" +done + +if [[ "${STATUS}" != "completed" || "${CONCLUSION}" != "success" ]]; then + echo "Timed out waiting for successful CI on main for ${SHA}." + exit 1 +fi