From e240fd1a31c5c2f35a762fb0cc8d1dd11ea896df Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 18 May 2026 19:22:21 +0000 Subject: [PATCH] B6: release automation workflow Adds .github/workflows/release.yml that fires on pushed v*.*.* tags (or manually-published Releases). It builds the sdist + wheel from pyproject.toml and attaches them to the Release for that tag via softprops/action-gh-release. Key safety: a pre-build step verifies the tag (stripped of leading "v") matches the version in pyproject.toml. If they diverge, the workflow fails before building, so the project can never ship a wheel whose filename version disagrees with the git tag. PyPI trusted publishing is intentionally left out for now; can be added once the project has a PyPI account. --- .github/workflows/release.yml | 63 +++++++++++++++++++++++++++++++++++ CHANGELOG.md | 4 +++ 2 files changed, 67 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..707f53c --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,63 @@ +name: Release + +# Triggers on either: +# - pushing a tag matching v*.*.* (e.g. v1.1.0) +# - publishing a GitHub Release manually from the UI +# +# Builds an sdist + wheel from pyproject.toml and attaches them to the +# Release for that tag. The Release itself can be created manually or +# automatically by `softprops/action-gh-release` if it doesn't exist yet. + +on: + push: + tags: + - "v*.*.*" + release: + types: [published] + +permissions: + contents: write # needed to upload assets to the Release + +jobs: + build-and-attach: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Verify tag matches pyproject version + run: | + TAG="${GITHUB_REF_NAME#v}" + PROJECT_VERSION=$(python -c "import tomllib; print(tomllib.loads(open('pyproject.toml','rb').read().decode())['project']['version'])") + echo "Tag version: $TAG" + echo "Project version: $PROJECT_VERSION" + if [ "$TAG" != "$PROJECT_VERSION" ]; then + echo "::error::Tag $GITHUB_REF_NAME does not match pyproject version $PROJECT_VERSION. Bump version in pyproject.toml before tagging." + exit 1 + fi + + - name: Build sdist and wheel + run: | + python -m pip install --upgrade pip build + python -m build + + - name: List build artifacts + run: ls -la dist/ + + - name: Upload artifacts to workflow run + uses: actions/upload-artifact@v4 + with: + name: dist-${{ github.ref_name }} + path: dist/ + + - name: Attach to GitHub Release + uses: softprops/action-gh-release@v2 + with: + files: dist/* + fail_on_unmatched_files: true + # If the Release for this tag doesn't already exist, this action + # will create one. Body / title can be edited in the UI after. + generate_release_notes: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 01f8e4b..39ecc42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Issue and pull request templates under `.github/`. - Dependabot configuration for weekly dependency and Actions updates. - `.env.example` documenting the supported environment variables. +- Release automation workflow (`.github/workflows/release.yml`): on + pushing a `v*.*.*` tag, builds the sdist and wheel from + `pyproject.toml` and attaches them to the GitHub Release. Verifies the + tag matches the project version to prevent mismatched artifacts. ### Security - `SUBSTACK_COOKIE` environment variable is now supported as a safer