Skip to content

Commit 47bbde2

Browse files
committed
ci: Add PyPI publishing job. Improve release job structure and security.
1 parent 8cd12bd commit 47bbde2

1 file changed

Lines changed: 39 additions & 19 deletions

File tree

.github/workflows/release.yml

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,20 @@
1-
name: Release (TestPyPI)
1+
name: Release
22

33
on:
44
workflow_dispatch:
55

66
permissions:
77
contents: read
88

9-
# Matches ci.yml: bash everywhere keeps the make recipes portable across the runner images.
109
defaults:
1110
run:
1211
shell: bash
1312

1413
jobs:
15-
release:
16-
name: Publish to TestPyPI
14+
build:
15+
name: Build distribution
1716
runs-on: ubuntu-latest
1817
timeout-minutes: 15
19-
permissions:
20-
contents: read
21-
# OIDC token for PyPI trusted publishing, scoped to this job so nothing else in the
22-
# workflow can mint one. Job-level permissions replace the workflow-level block outright,
23-
# so contents: read has to be repeated here for the checkout.
24-
id-token: write
2518
steps:
2619
- uses: actions/checkout@v5
2720

@@ -30,18 +23,45 @@ jobs:
3023
with:
3124
enable-cache: true
3225

33-
# The same target CI and the pre-push hook run, so the release path cannot drift from the
34-
# checks: it builds into dist/, validates the metadata with twine, and imports the wheel in
35-
# a throwaway environment. It leaves dist/ in place, which is what gets published below.
3626
- run: make dist-check
3727

38-
# No username, password or token: the TestPyPI trusted publisher is matched on this
39-
# repository and on this workflow's filename, so renaming this file breaks publishing.
40-
#
41-
# Deliberately no skip-existing. A version already present on the index should fail the run
42-
# loudly rather than pass as a no-op, because neither index allows a version to be
43-
# re-uploaded once taken -- the fix is always a new version, never a retry.
28+
- uses: actions/upload-artifact@v4
29+
with:
30+
name: dist
31+
path: dist/
32+
33+
testpypi:
34+
name: Publish to TestPyPI
35+
needs: build
36+
runs-on: ubuntu-latest
37+
timeout-minutes: 15
38+
permissions:
39+
contents: read
40+
id-token: write
41+
steps:
42+
- uses: actions/download-artifact@v4
43+
with:
44+
name: dist
45+
path: dist/
46+
4447
- name: Publish to TestPyPI
4548
uses: pypa/gh-action-pypi-publish@release/v1
4649
with:
4750
repository-url: https://test.pypi.org/legacy/
51+
52+
pypi:
53+
name: Publish to PyPI
54+
needs: testpypi
55+
runs-on: ubuntu-latest
56+
timeout-minutes: 15
57+
permissions:
58+
contents: read
59+
id-token: write
60+
steps:
61+
- uses: actions/download-artifact@v4
62+
with:
63+
name: dist
64+
path: dist/
65+
66+
- name: Publish to PyPI
67+
uses: pypa/gh-action-pypi-publish@release/v1

0 commit comments

Comments
 (0)