Skip to content

Commit f6baa77

Browse files
hartsockclaude
andcommitted
feat(publish): abi3 maturin wheel publish workflow → PyPI
WHAT: Add .github/workflows/publish.yml — fires on vX.Y.Z tags (and workflow_dispatch). Builds abi3 wheels (cp39 ABI floor) across: - linux x86_64 + aarch64 (manylinux, QEMU) - macOS universal2 (x86_64+arm64, combined by maturin-action) - windows x86_64 Plus an sdist. Uploads via `maturin upload --skip-existing` using the repo's `pypi` environment + PYPI_API_TOKEN secret. Also add `abi3-py39` to the pyo3 feature list in Cargo.toml (required for maturin to stamp the wheel tag correctly); verified it still builds clean. Hook/pipeline parity notes updated in ci.yml + pre-push. WHY: gila-cap-git-tend (branch feat/cap-git-tend in gilamonster-capabilities) declares `gitxtend>=0.1.0` as a dep. The caps CI is Python-only and can't build Rust from source, so gitxtend must be on PyPI for that cap to land. v0.1.0 is release-ready (all M1 read methods + parity-tests merged, E2E suite green, version already bumped on this branch). To release: push the tag — `git tag v0.1.0 && git push origin v0.1.0`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018R92DDLCsWorb4fSJQrdvD
1 parent ae5eb12 commit f6baa77

4 files changed

Lines changed: 142 additions & 1 deletion

File tree

.githooks/pre-push

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
# CI steps, update this hook to match (and vice-versa). Do not bypass with
66
# --no-verify; if a check fails, fix the issue.
77
#
8+
# NOTE: .github/workflows/publish.yml (abi3 PyPI wheels) fires on vX.Y.Z tags
9+
# and is NOT mirrored here — publishing to PyPI is a release gate only.
10+
#
811
# Install once per clone: git config core.hooksPath .githooks
912
set -euo pipefail
1013

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
#
33
# HOOK PARITY: this pipeline is mirrored by .githooks/pre-push. When editing the
44
# steps here, update the hook to match (and vice-versa).
5+
# PUBLISH PIPELINE: .github/workflows/publish.yml fires on vX.Y.Z tags and uploads
6+
# abi3 manylinux/macOS/Windows wheels + an sdist to PyPI. It is NOT mirrored by the
7+
# pre-push hook (uploading to PyPI is a release gate only).
58
name: ci
69

710
on:

.github/workflows/publish.yml

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# gitxtend PyPI publish — fires on a vX.Y.Z tag or workflow_dispatch.
2+
#
3+
# HOOK PARITY: this pipeline is additive (publish is not mirrored by the pre-push
4+
# hook — uploading to PyPI is a release gate only). The pre-push hook runs the
5+
# same build + test steps that the `check` job in ci.yml runs.
6+
#
7+
# Publishes abi3 maturin wheels for:
8+
# - linux x86_64 + aarch64 (via QEMU)
9+
# - macOS x86_64 + arm64 (via two native runners, combined into universal2)
10+
# - windows x86_64
11+
#
12+
# The wheel uses cp39 abi3 (--interpreter python3.9 --abi3) so one wheel covers
13+
# Python 3.9+ on each platform. pyproject.toml declares requires-python = ">=3.11"
14+
# which is still compatible (the abi3 tag is the build ABI floor, not the runtime
15+
# floor — pip enforces pyproject.toml's requires-python separately).
16+
17+
name: publish
18+
19+
on:
20+
push:
21+
tags:
22+
- "v[0-9]+.[0-9]+.[0-9]+"
23+
workflow_dispatch:
24+
inputs:
25+
tag:
26+
description: "Tag to publish (e.g. v0.1.0)"
27+
required: true
28+
29+
env:
30+
CARGO_TERM_COLOR: always
31+
# maturin needs this to pick the right Python for abi3 builds.
32+
MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
33+
34+
jobs:
35+
# ── Linux x86_64 + aarch64 (manylinux) ───────────────────────────────────
36+
linux:
37+
name: linux (${{ matrix.target }})
38+
runs-on: ubuntu-latest
39+
strategy:
40+
matrix:
41+
target: [x86_64, aarch64]
42+
steps:
43+
- uses: actions/checkout@v4
44+
- uses: actions/setup-python@v5
45+
with:
46+
python-version: "3.11"
47+
- name: build wheel (manylinux abi3)
48+
uses: PyO3/maturin-action@v1
49+
with:
50+
target: ${{ matrix.target }}
51+
args: --release --out dist --features extension-module --interpreter python3.11 --abi3
52+
manylinux: auto
53+
sccache: "true"
54+
- uses: actions/upload-artifact@v4
55+
with:
56+
name: wheels-linux-${{ matrix.target }}
57+
path: dist
58+
59+
# ── macOS x86_64 + arm64 (combined into universal2) ──────────────────────
60+
macos:
61+
name: macos (universal2)
62+
runs-on: macos-latest
63+
steps:
64+
- uses: actions/checkout@v4
65+
- uses: actions/setup-python@v5
66+
with:
67+
python-version: "3.11"
68+
- name: build wheel (universal2 abi3)
69+
uses: PyO3/maturin-action@v1
70+
with:
71+
target: universal2-apple-darwin
72+
args: --release --out dist --features extension-module --interpreter python3.11 --abi3
73+
sccache: "true"
74+
- uses: actions/upload-artifact@v4
75+
with:
76+
name: wheels-macos-universal2
77+
path: dist
78+
79+
# ── Windows x86_64 ───────────────────────────────────────────────────────
80+
windows:
81+
name: windows (x86_64)
82+
runs-on: windows-latest
83+
steps:
84+
- uses: actions/checkout@v4
85+
- uses: actions/setup-python@v5
86+
with:
87+
python-version: "3.11"
88+
- name: build wheel (abi3)
89+
uses: PyO3/maturin-action@v1
90+
with:
91+
target: x86_64
92+
args: --release --out dist --features extension-module --interpreter python3.11 --abi3
93+
sccache: "true"
94+
- uses: actions/upload-artifact@v4
95+
with:
96+
name: wheels-windows-x86_64
97+
path: dist
98+
99+
# ── sdist ────────────────────────────────────────────────────────────────
100+
sdist:
101+
name: sdist
102+
runs-on: ubuntu-latest
103+
steps:
104+
- uses: actions/checkout@v4
105+
- name: build sdist
106+
uses: PyO3/maturin-action@v1
107+
with:
108+
command: sdist
109+
args: --out dist
110+
- uses: actions/upload-artifact@v4
111+
with:
112+
name: wheels-sdist
113+
path: dist
114+
115+
# ── Upload to PyPI ────────────────────────────────────────────────────────
116+
release:
117+
name: upload to PyPI
118+
runs-on: ubuntu-latest
119+
needs: [linux, macos, windows, sdist]
120+
environment:
121+
name: pypi
122+
url: https://pypi.org/p/gitxtend
123+
permissions:
124+
id-token: write
125+
steps:
126+
- uses: actions/download-artifact@v4
127+
with:
128+
pattern: wheels-*
129+
merge-multiple: true
130+
path: dist
131+
- name: publish
132+
uses: PyO3/maturin-action@v1
133+
with:
134+
command: upload
135+
args: --non-interactive --skip-existing dist/*

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ gix = { version = "0.70", features = [
4646
# PyO3 — only compiled when the `python` feature is on. `extension-module` is
4747
# layered via the crate feature of the same name, so `cargo test` links no
4848
# libpython and the pure-Rust core stays interpreter-free.
49-
pyo3 = { version = "0.28", optional = true, default-features = false, features = ["macros"] }
49+
pyo3 = { version = "0.28", optional = true, default-features = false, features = ["macros", "abi3-py39"] }
5050

5151
[dev-dependencies]
5252
# Temp-dir fixtures for parity tests vs the git CLI.

0 commit comments

Comments
 (0)