Publish to PyPI #10
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: | |
| workflow_run: | |
| workflows: ["CI Stable"] | |
| types: [completed] | |
| branches: [main] | |
| permissions: | |
| contents: write | |
| jobs: | |
| publish: | |
| # Run only when the upstream CI on the trusted main branch finished | |
| # successfully. workflow_run carries head_branch + head_sha so we can | |
| # gate strictly on the main branch and check out the exact commit | |
| # that passed CI, avoiding any race with a later push to main and | |
| # ensuring no fork-originated ref is ever materialised here. | |
| # Restrict to `push` events so scheduled CI runs do not publish. | |
| if: >- | |
| ${{ github.event.workflow_run.conclusion == 'success' | |
| && github.event.workflow_run.head_branch == 'main' | |
| && github.event.workflow_run.event == 'push' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| # The job's `if` already gates on workflow_run.head_branch == 'main' | |
| # and workflow_run.event != 'pull_request', so a fork PR head can | |
| # never reach this checkout. We pin to workflow_run.head_sha to | |
| # publish exactly the commit that passed CI on main. | |
| # nosemgrep: yaml.github-actions.security.workflow-run-target-code-checkout.workflow-run-target-code-checkout | |
| - name: Checkout the exact commit that passed CI | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.workflow_run.head_sha }} | |
| fetch-depth: 0 | |
| persist-credentials: true | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - name: Install build tools | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install build twine tomlkit | |
| - name: Bump patch version in pyproject.toml | |
| id: bump | |
| run: | | |
| python <<'PYEOF' | |
| import os | |
| import tomlkit | |
| path = "pyproject.toml" | |
| with open(path, encoding="utf-8") as f: | |
| doc = tomlkit.parse(f.read()) | |
| old = str(doc["project"]["version"]) | |
| parts = old.split(".") | |
| parts[-1] = str(int(parts[-1]) + 1) | |
| new = ".".join(parts) | |
| doc["project"]["version"] = new | |
| with open(path, "w", encoding="utf-8") as f: | |
| f.write(tomlkit.dumps(doc)) | |
| with open(os.environ["GITHUB_OUTPUT"], "a") as f: | |
| f.write(f"old_version={old}\n") | |
| f.write(f"new_version={new}\n") | |
| print(f"Bumped version: {old} -> {new}") | |
| PYEOF | |
| - name: Build distributions | |
| run: python -m build | |
| - name: Publish to PyPI | |
| env: | |
| TWINE_USERNAME: __token__ | |
| TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} | |
| run: twine upload dist/* | |
| - name: Commit and tag bumped version | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git add pyproject.toml | |
| git commit -m "chore: bump version to ${{ steps.bump.outputs.new_version }} [skip ci]" | |
| git tag "v${{ steps.bump.outputs.new_version }}" | |
| # Push the bump commit onto main directly. We checked out a | |
| # detached HEAD at workflow_run.head_sha, so push HEAD into | |
| # refs/heads/main. If main has moved since the CI run, this | |
| # rejects as non-fast-forward rather than overwriting history. | |
| git push origin "HEAD:refs/heads/main" | |
| git push origin "v${{ steps.bump.outputs.new_version }}" | |
| - name: Create GitHub Release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| gh release create "v${{ steps.bump.outputs.new_version }}" \ | |
| dist/* \ | |
| --title "v${{ steps.bump.outputs.new_version }}" \ | |
| --notes "Automated release for version ${{ steps.bump.outputs.new_version }}. Published to PyPI: https://pypi.org/project/je-load-density/${{ steps.bump.outputs.new_version }}/" \ | |
| --target main |