From bac9917e7f6b214e7e7e826b721aa7eb4ef794f5 Mon Sep 17 00:00:00 2001 From: Patrick Tannoury Date: Sat, 8 Aug 2026 17:49:02 +0200 Subject: [PATCH] fix: serialize release pushes and publish before tagging The Python release job failed with a rejected push: the JavaScript job landed its version bump on main first, leaving Python pushing a stale ref. Both jobs are fanned out in parallel by detect-changes, so any commit touching two SDKs hit this. Retry the version-bump push with a rebase and jittered backoff so a lost ref-lock race no longer fails the release. Also move the registry publish ahead of the GitHub Release step. The previous order tagged and released before publishing, which is why a failed npm publish left behind a javascript/v1.1.0 release for a version that was never published. --- .github/workflows/publish-javascript.yml | 24 +++++++++++++++------- .github/workflows/publish-python.yml | 26 ++++++++++++++++-------- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/.github/workflows/publish-javascript.yml b/.github/workflows/publish-javascript.yml index 3c3c533..a9edbcb 100644 --- a/.github/workflows/publish-javascript.yml +++ b/.github/workflows/publish-javascript.yml @@ -59,7 +59,23 @@ jobs: git config user.email "github-actions[bot]@users.noreply.github.com" git add package.json package-lock.json git commit -m "chore(release): javascript ${{ steps.release_version.outputs.next }} [skip ci]" - git push origin HEAD:main + # Sibling language jobs push to main in parallel, so rebase and retry + # instead of failing the release on a lost ref-lock race. + for attempt in 1 2 3 4 5; do + if git push origin HEAD:main; then exit 0; fi + echo "push attempt $attempt failed; rebasing onto origin/main" + sleep $(( (RANDOM % 5) + 2 )) + git pull --rebase origin main + done + exit 1 + + # Publish before tagging so a registry failure does not leave behind a + # GitHub release for a version that was never published. + - name: Publish to npm + if: steps.release_version.outputs.next != '' + run: npm publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - name: Create GitHub Release if: steps.release_version.outputs.next != '' @@ -75,9 +91,3 @@ jobs: generate_release_notes: true }); console.log(`Created release ${release.id} at ${release.html_url}`); - - - name: Publish to npm - if: steps.release_version.outputs.next != '' - run: npm publish --access public - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/publish-python.yml b/.github/workflows/publish-python.yml index c252519..af22a7b 100644 --- a/.github/workflows/publish-python.yml +++ b/.github/workflows/publish-python.yml @@ -58,12 +58,29 @@ jobs: git config user.email "github-actions[bot]@users.noreply.github.com" git add pyproject.toml git commit -m "chore(release): python ${{ steps.release_version.outputs.next }} [skip ci]" - git push origin HEAD:main + # Sibling language jobs push to main in parallel, so rebase and retry + # instead of failing the release on a lost ref-lock race. + for attempt in 1 2 3 4 5; do + if git push origin HEAD:main; then exit 0; fi + echo "push attempt $attempt failed; rebasing onto origin/main" + sleep $(( (RANDOM % 5) + 2 )) + git pull --rebase origin main + done + exit 1 - name: Build package if: steps.release_version.outputs.next != '' run: python -m build + # Publish before tagging so a registry failure does not leave behind a + # GitHub release for a version that was never published. + - name: Publish to PyPI + if: steps.release_version.outputs.next != '' + uses: pypa/gh-action-pypi-publish@release/v1 + with: + password: ${{ secrets.PYPI_TOKEN }} + packages-dir: python/dist/ + - name: Create GitHub Release if: steps.release_version.outputs.next != '' uses: actions/github-script@v9 @@ -78,10 +95,3 @@ jobs: generate_release_notes: true }); console.log(`Created release ${release.id} at ${release.html_url}`); - - - name: Publish to PyPI - if: steps.release_version.outputs.next != '' - uses: pypa/gh-action-pypi-publish@release/v1 - with: - password: ${{ secrets.PYPI_TOKEN }} - packages-dir: python/dist/