From 1dbd7977b81f7fa0792913f8320cdff982ba4781 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 19 May 2026 15:02:31 -0500 Subject: [PATCH] ci: make the deploy job re-runnable after a partial failure Skip the Make Release step when the GitHub Release for the tag already exists, and pass skip-existing to the PyPI publish step. This lets the deploy job be re-run after a partial release failure (e.g. PyPI 5xx after the GitHub Release was already created) without having to cut a new version. The existence check matches only the literal "release not found" reply for the not-exists branch and re-raises anything else, so auth, rate limit, or transient network failures fail loudly with the real error instead of falling through to Make Release. --- .github/workflows/ci-cd.yml | 26 ++++++++++++++++++++++++++ CHANGES/1353.contrib.rst | 5 +++++ docs/spelling_wordlist.txt | 1 + 3 files changed, 32 insertions(+) create mode 100644 CHANGES/1353.contrib.rst diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 65370b886..0b394487a 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -583,7 +583,29 @@ jobs: - name: Login run: | echo "${{ secrets.GITHUB_TOKEN }}" | gh auth login --with-token + - name: Check whether the GitHub Release already exists + # Allows re-running the deploy job after a partial failure (e.g. PyPI + # upload error) without the Make Release step failing with HTTP 422 + # because the tag/release was created on a prior attempt. Treat + # only the literal `release not found` reply as "does not exist"; + # other failures (auth, rate-limit, network) re-raise so the job + # fails loudly instead of falling through to Make Release. + id: gh-release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ github.ref_name }} + run: | + if gh release view "${TAG}" --repo "${GITHUB_REPOSITORY}" \ + >/dev/null 2>err; then + echo 'exists=true' >> "${GITHUB_OUTPUT}" + elif grep -qx 'release not found' err; then + echo 'exists=false' >> "${GITHUB_OUTPUT}" + else + cat err >&2 + exit 1 + fi - name: Make Release + if: steps.gh-release.outputs.exists != 'true' uses: aio-libs/create-release@v1.6.6 with: changes_file: CHANGES.rst @@ -602,6 +624,10 @@ jobs: - name: >- Publish 🐍📦 to PyPI uses: pypa/gh-action-pypi-publish@release/v1 + with: + # Allow re-running the deploy job after a partial PyPI upload + # without failing on dists that were already published. + skip-existing: true - name: Sign the dists with Sigstore uses: sigstore/gh-action-sigstore-python@v3.3.0 diff --git a/CHANGES/1353.contrib.rst b/CHANGES/1353.contrib.rst new file mode 100644 index 000000000..3a5e34b20 --- /dev/null +++ b/CHANGES/1353.contrib.rst @@ -0,0 +1,5 @@ +Allowed re-running the deploy job after a partial release failure: the +``Make Release`` step now skips when the GitHub Release already exists, +and the PyPI publish step uses ``skip-existing`` so dists that were +already uploaded on a prior attempt do not break the retry +-- by :user:`bdraco`. diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt index 7b20d5413..ef515d3ca 100644 --- a/docs/spelling_wordlist.txt +++ b/docs/spelling_wordlist.txt @@ -32,6 +32,7 @@ deallocation decrementation dev dict +dists docstrings downstreams eof