From f5e7519a94656f511397d83e0e0a23b4026849a0 Mon Sep 17 00:00:00 2001 From: Ashmit Biswas Date: Sun, 31 May 2026 07:18:15 +0530 Subject: [PATCH] infra: combine release + publish into one atomic workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous two-workflow split (release.yml tags, publish.yml publishes on tag push) had a fatal gap: tag pushes by GITHUB_TOKEN do not trigger downstream workflows (GitHub's anti-cascade guard). So v3.1.1 got tagged but never reached PyPI. New release.yml does everything in one job: read __version__ → build → twine check → OIDC publish to PyPI → tag and push. Publish happens before tag push, so a failed publish leaves no orphan tag — re-runs are idempotent. publish.yml deleted (superseded). --- .github/workflows/publish.yml | 36 ----------------------------------- .github/workflows/release.yml | 21 +++++++++++++------- 2 files changed, 14 insertions(+), 43 deletions(-) delete mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index cc54c88..0000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Publish to PyPI - -on: - push: - tags: - - "v*" - -jobs: - build-and-publish: - name: Build and publish to PyPI - runs-on: ubuntu-latest - permissions: - id-token: write # OIDC for PyPI trusted publishing - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Build sdist and wheel - run: | - python -m pip install --upgrade pip build - python -m build - - - name: Verify version matches tag - run: | - TAG_VERSION="${GITHUB_REF_NAME#v}" - PKG_VERSION=$(python -c "import re,pathlib; print(re.search(r'__version__\s*=\s*\"([^\"]+)\"', pathlib.Path('src/canopy/__init__.py').read_text()).group(1))") - if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then - echo "::error::Tag v$TAG_VERSION does not match __version__ ($PKG_VERSION)" - exit 1 - fi - - - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index deb78ca..3e4a5bb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,13 +1,14 @@ -name: Release (tag from __version__) +name: Release (build + publish + tag) on: workflow_dispatch: permissions: - contents: write # needed to push the tag + contents: write # push tag + id-token: write # OIDC for PyPI trusted publishing jobs: - tag: + release: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -23,7 +24,7 @@ jobs: run: | V=$(python -c "import re,pathlib; print(re.search(r'__version__\s*=\s*\"([^\"]+)\"', pathlib.Path('src/canopy/__init__.py').read_text()).group(1))") echo "version=$V" >> "$GITHUB_OUTPUT" - echo "Will release v$V" + echo "Releasing v$V" - name: Refuse if tag already exists run: | @@ -32,12 +33,18 @@ jobs: exit 1 fi - - name: Configure git identity + - name: Build sdist + wheel run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + python -m pip install --upgrade pip build twine + python -m build + python -m twine check dist/* + + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 - name: Tag and push run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" git tag "v${{ steps.ver.outputs.version }}" git push origin "v${{ steps.ver.outputs.version }}"