Skip to content

Publish to PyPI

Publish to PyPI #13

Workflow file for this run

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