Skip to content

Commit 471fcb3

Browse files
petercorkeclaude
andcommitted
ci: publish to PyPI from a GitHub release, by trusted publishing
publish.yml follows the release workflow of the other toolboxes: verify that the tag is the version in pyproject.toml and that the tagged commit is on main, build the sdist and wheel, twine check, install the wheel into a clean environment and run a path query, then upload with OIDC from the pypi environment. skip_existing makes a re-run harmless. The default token is read-only. The docs job of ci.yml can be run by hand, from main only. The manual twine-upload Makefile target is gone, as a release does it. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
1 parent 4cd0d65 commit 471fcb3

2 files changed

Lines changed: 89 additions & 20 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ jobs:
8282
verbose: true
8383

8484
docs:
85-
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
85+
if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main')
8686
needs: unittest
8787
runs-on: ubuntu-latest
8888
steps:

‎.github/workflows/publish.yml‎

Lines changed: 88 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,114 @@
1+
# Build and publish to PyPI when a GitHub release is created, or on manual trigger.
2+
# Uses OIDC trusted publishing, so no PyPI token is stored. The trusted publisher
3+
# on PyPI is: owner petercorke, repository pgraph-python, workflow publish.yml,
4+
# environment pypi. Pure Python package, so one universal wheel and an sdist.
5+
#
6+
# To release: make sure main has the version in pyproject.toml, then create a
7+
# GitHub release whose tag is v<version> on main.
8+
19
name: Publish to PyPI
210

311
on:
412
release:
5-
types: [published]
13+
types: [created]
14+
workflow_dispatch:
15+
16+
permissions:
17+
contents: read
618

719
jobs:
20+
verify_version:
21+
name: Verify tag matches package version
22+
runs-on: ubuntu-latest
23+
steps:
24+
- uses: actions/checkout@v7
25+
with:
26+
fetch-depth: 0
27+
- name: Check version
28+
# Skip on manual workflow_dispatch runs (no release tag exists)
29+
if: github.event_name == 'release'
30+
run: |
31+
TAG="${GITHUB_REF_NAME#v}"
32+
PKG=$(python3 -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
33+
echo "Tag version : $TAG"
34+
echo "Package version: $PKG"
35+
[ "$TAG" = "$PKG" ] || { echo "ERROR: tag $GITHUB_REF_NAME does not match pyproject.toml version $PKG"; exit 1; }
36+
- name: Check the tagged commit is on main
37+
if: github.event_name == 'release'
38+
run: |
39+
git fetch origin main
40+
git merge-base --is-ancestor "$GITHUB_SHA" origin/main \
41+
|| { echo "ERROR: $GITHUB_SHA is not on main, so it was not reviewed and tested there"; exit 1; }
42+
843
build:
44+
name: Build sdist and wheel
45+
needs: verify_version
946
runs-on: ubuntu-latest
1047
steps:
11-
- uses: actions/checkout@v4
12-
- name: Set up Python
13-
uses: actions/setup-python@v5
48+
- uses: actions/checkout@v7
49+
50+
- uses: actions/setup-python@v7
1451
with:
1552
python-version: "3.12"
16-
- name: Install build tooling
17-
run: |
18-
python -m pip install --upgrade pip
19-
pip install build twine
53+
2054
- name: Build sdist and wheel
21-
run: python -m build
55+
run: pipx run build
56+
2257
- name: Check metadata
23-
run: twine check dist/*
24-
- name: Upload build artifacts
25-
uses: actions/upload-artifact@v4
58+
run: pipx run twine check dist/*
59+
60+
- uses: actions/upload-artifact@v7
2661
with:
2762
name: dist
2863
path: dist/
2964

30-
publish:
65+
smoke_test:
66+
name: Smoke-test wheel
3167
needs: build
3268
runs-on: ubuntu-latest
69+
steps:
70+
- uses: actions/setup-python@v7
71+
with:
72+
python-version: "3.12"
73+
74+
- uses: actions/download-artifact@v8
75+
with:
76+
name: dist
77+
path: dist
78+
79+
- name: Install the wheel (pulls dependencies from PyPI)
80+
run: pip install dist/*.whl
81+
82+
- name: Smoke test
83+
run: |
84+
python - <<'PY'
85+
from importlib.metadata import version
86+
import pgraph
87+
88+
g = pgraph.UGraph()
89+
g.add_vertex(coord=[0, 0], name="a")
90+
g.add_vertex(coord=[3, 4], name="b")
91+
g.add_edge("a", "b")
92+
path, length, _ = g.path_Astar("a", "b")
93+
assert length == 5.0, length
94+
print("OK", version("pgraph-python"), length)
95+
PY
96+
97+
upload_pypi:
98+
name: Publish to PyPI
99+
needs: [build, smoke_test]
100+
runs-on: ubuntu-latest
33101
environment:
34102
name: pypi
35103
url: https://pypi.org/project/pgraph-python/
36104
permissions:
37-
id-token: write # required for PyPI trusted publishing (OIDC)
105+
id-token: write # required for OIDC trusted publishing
38106
steps:
39-
- name: Download build artifacts
40-
uses: actions/download-artifact@v4
107+
- uses: actions/download-artifact@v8
41108
with:
42109
name: dist
43-
path: dist/
44-
- name: Publish to PyPI
45-
uses: pypa/gh-action-pypi-publish@release/v1
110+
path: dist
111+
112+
- uses: pypa/gh-action-pypi-publish@release/v1
113+
with:
114+
skip_existing: true

0 commit comments

Comments
 (0)