From 6ae4e47c809afab5130529c286abc5e0ce371762 Mon Sep 17 00:00:00 2001 From: Peter Feerick <5500713+pfeerick@users.noreply.github.com> Date: Fri, 23 Oct 2026 19:46:46 +1000 Subject: [PATCH] ci: per-branch publish concurrency with gh-pages push retry A single workflow-wide concurrency group dropped publishes: GitHub keeps only one pending run per group, so when the 2.4-2.11 conversions merged together, six of the eight runs were cancelled before they ran. Split the workflow into two jobs: - publish: one concurrency group per branch (a superseded pending run is harmless, since the newest push contains it). Branches build in parallel; if another branch updates gh-pages first, mike's push is rejected and the job rebuilds on the new tip and retries. - deploy: one group across all branches for the Cloudflare upload. It exports gh-pages when it starts, so whichever queued upload survives already includes every earlier publish. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/publish-versioned-docs.yml | 52 +++++++++++++++----- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/.github/workflows/publish-versioned-docs.yml b/.github/workflows/publish-versioned-docs.yml index c5f946502..56a9f9102 100644 --- a/.github/workflows/publish-versioned-docs.yml +++ b/.github/workflows/publish-versioned-docs.yml @@ -24,16 +24,18 @@ on: permissions: contents: write -# Every run rewrites gh-pages and then uploads the whole tree, so run them -# one at a time to avoid racing mike or publishing a stale snapshot. -concurrency: - group: publish-versioned-docs - cancel-in-progress: false - jobs: publish: runs-on: ubuntu-latest + # One group per branch. GitHub keeps at most one pending run per group + # and cancels older pending ones, which is harmless here: the newest + # push to a branch already contains the older ones. Different branches + # publish in parallel and retry if another one updates gh-pages first. + concurrency: + group: publish-versioned-docs-${{ github.ref_name }} + cancel-in-progress: false + steps: - name: Check out source uses: actions/checkout@v7 @@ -98,17 +100,45 @@ jobs: env: # "Edit this page" should open the branch this version is built from. DOCS_EDIT_URI: edit/${{ github.ref_name }}/website/md-docs/ - run: uv run mike deploy --push --update-aliases --title "${{ steps.version.outputs.title }}" ${{ steps.version.outputs.aliases }} + IS_LATEST: ${{ steps.version.outputs.is_latest }} + run: | + # Other branches may push to gh-pages while this one builds. mike's + # push is then rejected as non-fast-forward, so rebuild on top of + # the new tip and try again. + for attempt in 1 2 3 4 5; do + git fetch origin gh-pages + git branch -f gh-pages origin/gh-pages + if uv run mike deploy --push --update-aliases --title "${{ steps.version.outputs.title }}" ${{ steps.version.outputs.aliases }} \ + && { [[ "${IS_LATEST}" != "true" ]] || uv run mike set-default --push latest; }; then + exit 0 + fi + echo "gh-pages push failed (attempt ${attempt}), retrying" >&2 + sleep $((attempt * 10)) + done + exit 1 + + deploy: + needs: publish + runs-on: ubuntu-latest - - name: Set default docs version - if: steps.version.outputs.is_latest == 'true' - run: uv run mike set-default --push latest + # One group across all branches, so uploads never overlap. If several + # are queued, GitHub keeps only the newest; that's fine because it + # exports gh-pages when it starts, which already includes every + # earlier publish. The last upload is therefore always the newest tree. + concurrency: + group: publish-versioned-docs-cloudflare + cancel-in-progress: false + + steps: + - name: Check out source + uses: actions/checkout@v7 - name: Export gh-pages tree for Cloudflare run: | + git fetch --depth 1 origin gh-pages rm -rf .site-publish mkdir .site-publish - git archive gh-pages | tar -x -C .site-publish + git archive FETCH_HEAD | tar -x -C .site-publish - name: Deploy to Cloudflare Workers uses: cloudflare/wrangler-action@v4