-
Notifications
You must be signed in to change notification settings - Fork 3
221 lines (204 loc) · 8.65 KB
/
Copy pathpython-publish.yml
File metadata and controls
221 lines (204 loc) · 8.65 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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
# Build + publish the `gitsheets` Python package (rust/gitsheets-py) to PyPI.
#
# This ships the pyo3 binding over gitsheets-core as abi3-py39 wheels — one
# wheel per platform covers CPython >= 3.9 — plus an sdist, per
# specs/behaviors/distribution.md. The wheel matrix mirrors core-napi.yml's
# platform spread:
# x86_64 manylinux_2_28 → ubuntu-latest (container build; native)
# aarch64 manylinux_2_28 → ubuntu-24.04-arm (Arm runner; native aarch64
# manylinux container — NOT the default
# rust-cross image, which is x86_64-hosted)
# x86_64 musllinux_1_2 → ubuntu-latest (container build; the musl wheel
# can't install on the glibc host, so no smoke)
# x86_64 macOS → macos-14 (cross-compiled on the Apple
# Silicon runner, like core-napi.yml — Intel
# macos-13 runners are retired/unschedulable, so
# the x86_64 wheel builds cross and skips smoke)
# aarch64 macOS → macos-14 (Apple Silicon runner)
# x86_64 Windows → windows-latest
#
# VERSION RULE (specs/behaviors/distribution.md): the released version is
# COMMITTED in rust/gitsheets-py/Cargo.toml (maturin reads it via pyproject's
# dynamic version) — deliberately unlike the napi track, which tag-stamps at
# publish. A `py-v*` tag must match the committed version exactly; the guard
# job fails the whole run loudly on mismatch.
#
# Triggers:
# - push of a `py-v*` tag → build wheels + sdist, then publish to PyPI
# - pull_request touching the binding or this workflow → build + smoke
# everything (no publish), like core-napi.yml's PR builds
# - workflow_dispatch → dry-run: build + smoke everything, SKIP publish.
# The optional `guard-tag` input simulates a tag against the version guard
# (e.g. `py-v9.9.9` proves a mismatch fails the run). Note GitHub only
# accepts dispatches once the workflow exists on the default branch.
#
# Publish auth: PyPI TRUSTED PUBLISHING (OIDC) — no token, matching the repo's
# npm workflows. One-time bootstrap: PyPI supports PENDING publishers, so the
# trusted publisher for project `gitsheets` (this repo + this workflow file +
# environment `pypi`) is configured on pypi.org BEFORE the project exists; the
# first tagged publish creates and claims the name. No manual first upload.
#
# Ordering rule: only tag commits where rust-core.yml is green — the
# cross-binding byte-parity suite is the product guarantee, and a Python
# release that could disagree with npm's bytes is a defect.
name: python-publish
on:
push:
tags:
- 'py-v*'
pull_request:
paths:
- 'rust/gitsheets-py/**'
- '.github/workflows/python-publish.yml'
workflow_dispatch:
inputs:
guard-tag:
description: >-
Simulate a py-v* tag for the version guard (e.g. py-v0.1.0 to prove
a match passes, py-v9.9.9 to prove a mismatch fails the run). Leave
empty for a plain build-everything dry-run.
required: false
default: ''
permissions:
contents: read
jobs:
guard:
name: version guard
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Fail loudly if the tag version != the committed crate version
env:
TAG: ${{ github.event_name == 'push' && github.ref_name || inputs.guard-tag }}
run: |
CRATE_VERSION="$(python3 -c 'import tomllib; print(tomllib.load(open("rust/gitsheets-py/Cargo.toml", "rb"))["package"]["version"])')"
if [ -z "$TAG" ]; then
echo "Dry-run with no tag to check — committed version is ${CRATE_VERSION}."
exit 0
fi
TAG_VERSION="${TAG#py-v}"
if [ "$TAG_VERSION" != "$CRATE_VERSION" ]; then
echo "::error title=py-v tag/version mismatch::Tag ${TAG} expects version ${TAG_VERSION}, but rust/gitsheets-py/Cargo.toml commits ${CRATE_VERSION}. The Python track releases the COMMITTED version (specs/behaviors/distribution.md) — commit the bump to develop first, then tag py-v${CRATE_VERSION}."
exit 1
fi
echo "Tag ${TAG} matches committed version ${CRATE_VERSION}."
build:
name: build ${{ matrix.platform }}
needs: guard
strategy:
fail-fast: false
matrix:
include:
- platform: manylinux-x86_64
host: ubuntu-latest
target: x86_64-unknown-linux-gnu
manylinux: '2_28'
test: true
- platform: manylinux-aarch64
host: ubuntu-24.04-arm
target: aarch64-unknown-linux-gnu
manylinux: '2_28'
# Native aarch64 manylinux container: maturin-action's default
# image for this target is the x86_64-hosted rust-cross image,
# which would need QEMU on an Arm runner.
container: quay.io/pypa/manylinux_2_28_aarch64
test: true
- platform: musllinux-x86_64
host: ubuntu-latest
target: x86_64-unknown-linux-musl
manylinux: musllinux_1_2
test: false # musl wheel can't install on the glibc host
- platform: macos-x86_64
# Cross-compiled on the Apple Silicon runner (core-napi.yml's
# pattern): GitHub's Intel macos-13 runners are retired — the job
# queues indefinitely. The arm64 host can't run the x86_64 wheel,
# so no smoke; the abi3 build logic is covered by the smoked
# platforms.
host: macos-14
target: x86_64-apple-darwin
test: false
- platform: macos-arm64
host: macos-14
target: aarch64-apple-darwin
test: true
- platform: windows-x86_64
host: windows-latest
target: x86_64-pc-windows-msvc
test: true
runs-on: ${{ matrix.host }}
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
with:
python-version: '3.12'
- name: Build wheel (abi3-py39)
uses: PyO3/maturin-action@v1
with:
working-directory: rust/gitsheets-py
command: build
target: ${{ matrix.target }}
manylinux: ${{ matrix.manylinux }}
container: ${{ matrix.container }}
args: --release --locked --out dist
sccache: true
- name: Smoke test (install the wheel, run the binding suite)
if: ${{ matrix.test }}
shell: bash
run: |
python -m pip install pytest pydantic
python -m pip install --no-index --find-links rust/gitsheets-py/dist gitsheets
python -m pytest rust/gitsheets-py/tests/test_smoke.py -v
- uses: actions/upload-artifact@v7
with:
name: dist-${{ matrix.platform }}
path: rust/gitsheets-py/dist/*.whl
if-no-files-found: error
sdist:
name: build sdist
needs: guard
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
with:
python-version: '3.12'
- name: Build sdist
uses: PyO3/maturin-action@v1
with:
working-directory: rust/gitsheets-py
command: sdist
args: --out dist
- name: Smoke test (compile-from-source install, then import)
# Proves the sdist is complete — maturin must have packed the local
# path dependency (gitsheets-core) and the workspace Cargo.lock.
run: |
python -m pip install rust/gitsheets-py/dist/*.tar.gz
python -c "import gitsheets; print(gitsheets.serialize_records([{'ok': True}])[0])"
- uses: actions/upload-artifact@v7
with:
name: dist-sdist
path: rust/gitsheets-py/dist/*.tar.gz
if-no-files-found: error
publish:
name: publish gitsheets to PyPI
if: ${{ startsWith(github.ref, 'refs/tags/py-v') }}
needs: [build, sdist]
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/gitsheets
permissions:
id-token: write # OIDC trusted publishing — job-scoped on purpose
steps:
- name: Collect wheels + sdist
uses: actions/download-artifact@v8
with:
path: dist
pattern: dist-*
merge-multiple: true
- name: List distributions for the run log
run: ls -l dist/
# Tokenless: OIDC trusted publishing (uploads from dist/ by default).
# Requires the pending/trusted publisher configured on pypi.org for
# project `gitsheets` → this repo, this workflow, environment `pypi`.
- uses: pypa/gh-action-pypi-publish@release/v1