Repository navigation
177 lines (168 loc) · 6.67 KB
/
Copy pathpython-release.yml
File metadata and controls
177 lines (168 loc) · 6.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# SPDX-License-Identifier: Apache-2.0
#
# Builds the `gbif-name-parser` Python wheels (the PyO3 binding in crates/nameparser-py) for
# every platform and publishes them + an sdist to PyPI. Because the binding is built `abi3-py39`
# (stable ABI, see crates/nameparser-py/Cargo.toml), ONE wheel per (os, arch) covers Python 3.9+
# — no per-Python-version matrix. `import nameparser` after `pip install gbif-name-parser`.
#
# This workflow is ONLY for the Python binding. CLI -> GitHub Releases (cli-release.yml), Java FFM
# binding -> Jenkins, Rust engine -> crates.io, R -> CRAN. See DISTRIBUTION.md.
#
# Release flow:
# 1. Bump `version` in crates/nameparser-py/pyproject.toml to the release version.
# 2. Tag and push: git tag py-v0.1.0 && git push origin py-v0.1.0
# 3. This builds all wheels + sdist and (only on a py-v* tag) publishes to PyPI.
# A manual run (workflow_dispatch) builds + checks the wheels but does NOT publish.
#
# ONE-TIME PyPI setup (Trusted Publishing — no token/secret stored anywhere):
# * On https://pypi.org, add a Trusted Publisher (or a "pending publisher" before the first
# upload) for project `gbif-name-parser`: owner `gbif`, repository `name-parser-rust`,
# workflow `python-release.yml`, environment `pypi`.
# * In this repo's Settings -> Environments, create an environment named `pypi` (optionally add
# required reviewers to gate each release behind an approval).
name: Publish Python
on:
push:
tags:
- 'py-v*'
workflow_dispatch:
inputs:
testpypi:
description: 'Dry run — also publish the built wheels + sdist to TestPyPI (test.pypi.org)'
type: boolean
default: true
permissions:
contents: read
jobs:
linux:
name: wheel linux-${{ matrix.target }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target: [x86_64, aarch64]
steps:
- uses: actions/checkout@v7
- name: Build manylinux wheel
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
manylinux: auto
args: --release --out dist -m crates/nameparser-py/Cargo.toml
sccache: 'true'
- uses: actions/upload-artifact@v7
with:
name: wheels-linux-${{ matrix.target }}
path: dist
macos:
name: wheel macos-${{ matrix.target }}
strategy:
fail-fast: false
matrix:
include:
# Intel macOS (x86_64) is intentionally NOT built — GitHub is retiring the macos-13 Intel
# runners (matching the CLI). Intel-Mac users fall back to the sdist (needs a Rust
# toolchain at install time). If wanted back, cross-compile on macos-14 (arm64) with
# maturin's `--target x86_64-apple-darwin` rather than depending on a macos-13 runner.
- target: aarch64
runner: macos-14 # native Apple Silicon
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v7
- name: Build macOS wheel
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.target }}
args: --release --out dist -m crates/nameparser-py/Cargo.toml
sccache: 'true'
- uses: actions/upload-artifact@v7
with:
name: wheels-macos-${{ matrix.target }}
path: dist
windows:
name: wheel windows-x64
runs-on: windows-latest
steps:
- uses: actions/checkout@v7
- name: Build Windows wheel
uses: PyO3/maturin-action@v1
with:
target: x64
args: --release --out dist -m crates/nameparser-py/Cargo.toml
sccache: 'true'
- uses: actions/upload-artifact@v7
with:
name: wheels-windows-x64
path: dist
sdist:
name: sdist
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Build sdist
uses: PyO3/maturin-action@v1
with:
command: sdist
args: --out dist -m crates/nameparser-py/Cargo.toml
- uses: actions/upload-artifact@v7
with:
name: wheels-sdist
path: dist
publish:
name: Publish to PyPI
# Only publish on a py-v* tag; a manual (workflow_dispatch) run builds + validates but stops here.
if: startsWith(github.ref, 'refs/tags/py-v')
needs: [linux, macos, windows, sdist]
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write # Trusted Publishing (OIDC) — no PyPI token/secret needed
steps:
- uses: actions/checkout@v7
- name: Guard — tag must match pyproject.toml version (PyPI uploads are irreversible)
run: |
ver=$(grep -m1 -E '^version' crates/nameparser-py/pyproject.toml | sed -E 's/.*"([^"]+)".*/\1/')
if [ "${GITHUB_REF_NAME}" != "py-v${ver}" ]; then
echo "::error::tag ${GITHUB_REF_NAME} != py-v${ver} (crates/nameparser-py/pyproject.toml). Bump the version to match the tag before releasing."
exit 1
fi
echo "tag ${GITHUB_REF_NAME} matches pyproject version ${ver}"
- uses: actions/download-artifact@v8
with:
pattern: wheels-*
merge-multiple: true
path: dist
- name: List artifacts to publish
run: ls -l dist
- name: Publish to PyPI (Trusted Publishing)
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: dist
publish-testpypi:
# Dry run: a manual (workflow_dispatch) run with the `testpypi` box checked publishes the SAME
# wheels + sdist to TestPyPI (test.pypi.org) instead of the real index. Never runs on a tag
# push. Needs its own Trusted Publisher on test.pypi.org (Environment name: `testpypi`).
# `skip-existing: true` (below) makes this re-runnable: TestPyPI rejects re-uploading a file
# that already exists, so on a repeat dry run the already-present files are skipped and only new
# ones (e.g. a fixed sdist) upload, rather than failing the whole job on a collision.
name: Dry-run publish to TestPyPI
if: github.event_name == 'workflow_dispatch' && inputs.testpypi
needs: [linux, macos, windows, sdist]
runs-on: ubuntu-latest
environment: testpypi
permissions:
id-token: write # Trusted Publishing (OIDC) against test.pypi.org
steps:
- uses: actions/download-artifact@v8
with:
pattern: wheels-*
merge-multiple: true
path: dist
- name: List artifacts to publish
run: ls -l dist
- name: Publish to TestPyPI (Trusted Publishing)
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
packages-dir: dist
skip-existing: true # dry runs are re-runnable — skip files already on TestPyPI