diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index 916fc36..0000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Publish Package - -on: - release: - types: - - published - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: extractions/setup-just@v4 - - uses: astral-sh/setup-uv@v8.2.0 - - run: just publish - env: - PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..ef3c93e --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,60 @@ +name: Release + +# Tag-driven: pushing a semver tag publishes to PyPI and creates the matching +# GitHub Release. Replaces the old `on: release: published` publish.yml — that +# trigger is removed so the published Release this workflow creates can't re-fire +# it (double-publish). The tag is the sole entry point; by convention a tag is +# only cut off a green main, so there is no in-workflow CI gate. +on: + push: + tags: + - '[0-9]+.[0-9]+.[0-9]+' # stable: 2.7.2 + - '[0-9]+.[0-9]+.[0-9]+[a-z]+[0-9]+' # pre-release: 2.0.0rc1, 4.0.0a2 + +# Needed for softprops/action-gh-release to create the GitHub Release. +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: extractions/setup-just@v4 + - uses: astral-sh/setup-uv@v7 + + # PyPI is irreversible, so it runs FIRST: if it fails the job stops and no + # GitHub Release is created advertising a version that never reached PyPI. + # `just publish` derives the version from $GITHUB_REF_NAME (the tag name). + - run: just publish + env: + PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} + + # Description source: planning/releases/.md if present (verbatim, no + # auto-changelog appended); otherwise GitHub's generated notes. A tag with + # a letter (2.0.0rc1) is a pre-release -> flagged so GitHub won't mark it + # "Latest". + - name: Resolve release metadata + id: meta + run: | + set -euo pipefail + notes="planning/releases/${GITHUB_REF_NAME}.md" + if [ -f "$notes" ]; then + echo "body_path=$notes" >> "$GITHUB_OUTPUT" + echo "generate_notes=false" >> "$GITHUB_OUTPUT" + else + echo "generate_notes=true" >> "$GITHUB_OUTPUT" + fi + if [[ "$GITHUB_REF_NAME" =~ [a-z] ]]; then + echo "prerelease=true" >> "$GITHUB_OUTPUT" + else + echo "prerelease=false" >> "$GITHUB_OUTPUT" + fi + + - name: Publish GitHub Release + uses: softprops/action-gh-release@v3 + with: + body_path: ${{ steps.meta.outputs.body_path }} + generate_release_notes: ${{ steps.meta.outputs.generate_notes }} + prerelease: ${{ steps.meta.outputs.prerelease }} + draft: false