v0.2.6 #2
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Publish to PyPI | |
| on: | |
| # A merge to main that changes the version in pyproject.toml is the release | |
| # trigger. Merges that leave the version alone do nothing. | |
| push: | |
| branches: [main] | |
| paths: ["pyproject.toml"] | |
| # Kept as an escape hatch: publishing a GitHub Release by hand still works. | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| jobs: | |
| check: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.check.outputs.version }} | |
| publish: ${{ steps.check.outputs.publish }} | |
| steps: | |
| - uses: actions/checkout@v6.0.2 | |
| with: | |
| # HEAD^ is needed to read the previous version. On a merge commit that | |
| # is the first parent, i.e. main as it was before the merge. | |
| fetch-depth: 2 | |
| - name: Decide whether this commit is a release | |
| id: check | |
| run: | | |
| VERSION=$(grep -m1 '^version = ' pyproject.toml | cut -d'"' -f2) | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "pyproject.toml declares version $VERSION" | |
| case "${{ github.event_name }}" in | |
| push) | |
| PREV=$(git show HEAD^:pyproject.toml | grep -m1 '^version = ' | cut -d'"' -f2) | |
| if [ "$VERSION" = "$PREV" ]; then | |
| echo "Version unchanged at $VERSION -- nothing to publish." | |
| echo "publish=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| # A tag already existing means this version was released before and | |
| # this run is a re-run or a revert. Publishing again would fail at | |
| # PyPI, which refuses to overwrite a released version. | |
| if git ls-remote --exit-code --tags origin "refs/tags/v$VERSION" >/dev/null 2>&1; then | |
| echo "::warning::Tag v$VERSION already exists; skipping publish." | |
| echo "publish=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| echo "Version bumped $PREV -> $VERSION; releasing." | |
| echo "publish=true" >> "$GITHUB_OUTPUT" | |
| ;; | |
| release) | |
| TAG=${GITHUB_REF_NAME#v} | |
| if [ "$VERSION" != "$TAG" ]; then | |
| echo "::error::Tag $GITHUB_REF_NAME does not match pyproject.toml version $VERSION" | |
| exit 1 | |
| fi | |
| echo "publish=true" >> "$GITHUB_OUTPUT" | |
| ;; | |
| *) | |
| echo "publish=true" >> "$GITHUB_OUTPUT" | |
| ;; | |
| esac | |
| test: | |
| # A PyPI version cannot be replaced once uploaded, so the tests gate the | |
| # release rather than merely running alongside it. | |
| needs: check | |
| if: needs.check.outputs.publish == 'true' | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| python-version: ["3.11", "3.12", "3.13"] | |
| steps: | |
| - uses: actions/checkout@v6.0.2 | |
| - name: Install uv and set the python version | |
| uses: astral-sh/setup-uv@v8.0.0 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache-dependency-glob: "pyproject.toml" | |
| - name: Install the project with `dev` | |
| run: uv sync --extra dev | |
| - name: Run tests | |
| run: uv run pytest tests | |
| build: | |
| needs: [check, test] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6.0.2 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v8.0.0 | |
| with: | |
| python-version: "3.12" | |
| cache-dependency-glob: "pyproject.toml" | |
| - name: Build sdist and wheel | |
| run: uv build | |
| - uses: actions/upload-artifact@v7.0.1 | |
| with: | |
| name: dist | |
| path: dist/ | |
| publish: | |
| needs: build | |
| runs-on: ubuntu-latest | |
| # Trusted publishing via OIDC: no API token or repository secret needed. | |
| # Isolated from `build` so the token is never exposed to the build itself. | |
| permissions: | |
| id-token: write | |
| steps: | |
| - uses: actions/download-artifact@v8.0.1 | |
| with: | |
| name: dist | |
| path: dist/ | |
| - uses: pypa/gh-action-pypi-publish@v1.14.1 | |
| tag: | |
| # Records what was published. Only for the push path -- on the release path | |
| # the tag and release already exist. | |
| # | |
| # Creating a release with GITHUB_TOKEN does not re-trigger this workflow's | |
| # `release: published` event; GitHub suppresses that to prevent recursion. | |
| needs: [check, publish] | |
| if: github.event_name == 'push' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v6.0.2 | |
| - uses: actions/download-artifact@v8.0.1 | |
| with: | |
| name: dist | |
| path: dist/ | |
| - name: Tag the commit and open a GitHub Release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| gh release create "v${{ needs.check.outputs.version }}" \ | |
| --target "$GITHUB_SHA" \ | |
| --title "v${{ needs.check.outputs.version }}" \ | |
| --generate-notes \ | |
| dist/* |