diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b902d38..f5017e0 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -8,15 +8,34 @@ on: jobs: publish: runs-on: ubuntu-latest + permissions: + contents: read + # PyPI Trusted Publishing (OIDC). No PYPI_TOKEN secret needed. + id-token: write steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" - - run: pip install -e ".[dev]" build twine + - run: pip install -e ".[dev]" build - run: pytest + - name: Verify tag matches pyproject version + if: github.ref_type == 'tag' + run: | + TAG_VERSION="${GITHUB_REF_NAME#v}" + PKG_VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])") + if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then + echo "Tag version ($TAG_VERSION) does not match pyproject version ($PKG_VERSION)" + exit 1 + fi - run: python -m build - - run: twine upload dist/* - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} + - name: Publish to PyPI + # Authenticates via OIDC Trusted Publishing — configured as a pending + # publisher for project `sonilo` (repo sonilo-python, workflow publish.yml). + # Tag refs only: a workflow_dispatch on a branch runs tests/build but + # never publishes. To retry a failed publish, dispatch on the tag: + # gh workflow run publish.yml --ref vX.Y.Z + if: github.ref_type == 'tag' + uses: pypa/gh-action-pypi-publish@release/v1 + with: + skip-existing: true