chore: update packaging and CI workflow for PyPI releases

Enhance the documentation in PACKAGING.md and PLATFORMS.md to clarify the supported Python versions and platforms for wheel and source distribution releases. Update the CI workflow in CI.yml to include separate jobs for building wheels on Linux, macOS, and Windows, as well as a job for building the source distribution. Ensure that the publish job verifies the presence of expected distributions before publishing to PyPI.
This commit is contained in:
Pratik Bhadane
2026-03-24 01:00:17 +05:30
parent ba8019ad27
commit 9011250f99
4 changed files with 175 additions and 22 deletions
+139 -13
View File
@@ -130,32 +130,108 @@ jobs:
# Keep release jobs here because trusted-publisher configuration points to
# CI.yml specifically.
# -------------------------------------------------------------------------
build-wheels:
name: Build wheels (${{ matrix.os }})
runs-on: ${{ matrix.os }}
build-wheels-linux:
name: Build wheels (linux / py${{ matrix.python-version }})
runs-on: ubuntu-latest
if: github.event_name == 'release' && github.event.action == 'published'
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v6
- name: Build wheels
- name: Build wheel
uses: PyO3/maturin-action@v1
with:
command: build
args: --release --out dist
manylinux: auto
# Build for both Intel and Apple Silicon on macOS
target: ${{ matrix.os == 'macos-latest' && 'universal2-apple-darwin' || '' }}
args: --release --out dist --compatibility pypi -i python${{ matrix.python-version }}
manylinux: "2_17"
- name: Upload wheels as artifact
uses: actions/upload-artifact@v7
with:
name: wheels-${{ matrix.os }}
path: dist
name: wheels-linux-py${{ matrix.python-version }}
path: dist/*.whl
build-wheels-macos:
name: Build wheels (macos / py${{ matrix.python-version }})
runs-on: macos-latest
if: github.event_name == 'release' && github.event.action == 'published'
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v6
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Build universal2 wheel
uses: PyO3/maturin-action@v1
with:
command: build
args: --release --out dist --compatibility pypi -i python
target: universal2-apple-darwin
- name: Upload wheels as artifact
uses: actions/upload-artifact@v7
with:
name: wheels-macos-py${{ matrix.python-version }}
path: dist/*.whl
build-wheels-windows:
name: Build wheels (windows / py${{ matrix.python-version }})
runs-on: windows-latest
if: github.event_name == 'release' && github.event.action == 'published'
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v6
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Build wheel
uses: PyO3/maturin-action@v1
with:
command: build
args: --release --out dist --compatibility pypi -i python
- name: Upload wheels as artifact
uses: actions/upload-artifact@v7
with:
name: wheels-windows-py${{ matrix.python-version }}
path: dist/*.whl
build-sdist:
name: Build source distribution
runs-on: ubuntu-latest
if: github.event_name == 'release' && github.event.action == 'published'
steps:
- uses: actions/checkout@v6
- name: Build sdist
uses: PyO3/maturin-action@v1
with:
command: sdist
args: --out dist
- name: Upload sdist as artifact
uses: actions/upload-artifact@v7
with:
name: sdist
path: dist/*.tar.gz
# -------------------------------------------------------------------------
# Publish to PyPI
@@ -163,7 +239,11 @@ jobs:
publish:
name: Publish to PyPI
runs-on: ubuntu-latest
needs: build-wheels
needs:
- build-wheels-linux
- build-wheels-macos
- build-wheels-windows
- build-sdist
if: github.event_name == 'release' && github.event.action == 'published'
environment:
name: pypi
@@ -178,6 +258,52 @@ jobs:
merge-multiple: true
path: dist
- name: Download source distribution
uses: actions/download-artifact@v8
with:
name: sdist
path: dist
- name: Verify distribution coverage
run: |
python3 - <<'PY'
import fnmatch
import sys
from pathlib import Path
dist = Path("dist")
files = sorted(p.name for p in dist.iterdir() if p.is_file())
print("Distributions:")
for name in files:
print(f" - {name}")
expected = [
"ferro_ta-*-cp310-cp310-manylinux*_x86_64.whl",
"ferro_ta-*-cp311-cp311-manylinux*_x86_64.whl",
"ferro_ta-*-cp312-cp312-manylinux*_x86_64.whl",
"ferro_ta-*-cp313-cp313-manylinux*_x86_64.whl",
"ferro_ta-*-cp310-cp310-win_amd64.whl",
"ferro_ta-*-cp311-cp311-win_amd64.whl",
"ferro_ta-*-cp312-cp312-win_amd64.whl",
"ferro_ta-*-cp313-cp313-win_amd64.whl",
"ferro_ta-*-cp310-cp310-macosx*_universal2.whl",
"ferro_ta-*-cp311-cp311-macosx*_universal2.whl",
"ferro_ta-*-cp312-cp312-macosx*_universal2.whl",
"ferro_ta-*-cp313-cp313-macosx*_universal2.whl",
"ferro_ta-*.tar.gz",
]
missing = [
pattern for pattern in expected
if not any(fnmatch.fnmatch(name, pattern) for name in files)
]
if missing:
print("Missing expected distributions:")
for pattern in missing:
print(f" - {pattern}")
sys.exit(1)
PY
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
@@ -207,7 +333,7 @@ jobs:
sbom:
name: Generate SBOM (Python + Rust)
runs-on: ubuntu-latest
needs: build-wheels
needs: publish
if: github.event_name == 'release' && github.event.action == 'published'
permissions:
contents: write
+9
View File
@@ -5,6 +5,15 @@ This document describes how ferro-ta is packaged and published.
## PyPI (pip)
Wheels are built by CI on release (see [RELEASE.md](RELEASE.md)).
Release publishing currently targets CPython 3.10, 3.11, 3.12, and 3.13 on:
- Linux x86_64 (`manylinux_2_17` / `manylinux2014`)
- macOS universal2 (covers Intel and Apple Silicon)
- Windows x86_64
Each release also publishes a source distribution (`sdist`) so compatible
environments outside the wheel matrix can still build from source.
Publishing uses PyPI Trusted Publishing via GitHub OIDC; no long-lived PyPI API
token is required.
+7 -7
View File
@@ -20,13 +20,14 @@ Pre-compiled wheels are published to PyPI for the following targets:
| OS | Architecture | Notes |
|---------|-----------------|-------|
| Linux | x86_64 (manylinux2014 / `manylinux_2_17`) | Default CI runner |
| Linux | aarch64 | Built via maturin cross-compilation |
| macOS | x86_64 | Intel |
| macOS | arm64 | Apple Silicon |
| macOS | universal2 | Intel + Apple Silicon fat binary |
| Linux | x86_64 (manylinux2014 / `manylinux_2_17`) | Pre-compiled wheel |
| macOS | universal2 | One wheel covers Intel + Apple Silicon |
| Windows | x86_64 | |
Wheel releases target CPython 3.10, 3.11, 3.12, and 3.13. A source
distribution is also published so other compatible environments can build from
source.
> **Note:** Python 3.14+ is not yet tested. Set
> `PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1` to attempt a build on a newer
> interpreter and report any issues.
@@ -39,8 +40,7 @@ Pre-compiled wheels are published to PyPI for the following targets:
pip install ferro-ta
```
No C-compiler required — pre-compiled wheels are available for all platforms
listed above.
No C-compiler required on the wheel targets listed above.
### conda / conda-forge
+20 -2
View File
@@ -15,6 +15,14 @@ For the packaging and release overview, see [PACKAGING.md](PACKAGING.md).
| **npm (WASM)** | Workflow `wasm-publish`|
| **crates.io** | CI job `publish-cratesio` |
PyPI releases are expected to include:
- Wheels for CPython 3.10, 3.11, 3.12, and 3.13
- Linux x86_64 (`manylinux_2_17`)
- macOS universal2
- Windows x86_64
- One source distribution (`sdist`)
---
## Pre-release checklist
@@ -128,7 +136,7 @@ git push origin v0.2.0
4. Paste the changelog section for `v0.2.0` into the release notes.
5. Click **Publish release**.
Publishing the release triggers the CI `build-wheels` and `publish` jobs
Publishing the release triggers the CI wheel build jobs, `build-sdist`, and `publish`
automatically (the workflow responds to `release: published`). The PyPI upload
uses Trusted Publishing via GitHub OIDC, so no `PYPI_API_TOKEN` secret is used.
@@ -136,7 +144,7 @@ uses Trusted Publishing via GitHub OIDC, so no `PYPI_API_TOKEN` secret is used.
## Step 7 — Monitor CI and verify PyPI
1. Watch the **Actions** tab: `build-wheels` → `publish` (PyPI), `publish-cratesio` (crates.io), and the **wasm-publish** workflow (npm).
1. Watch the **Actions** tab: the release wheel jobs, `build-sdist`, `publish` (PyPI), `publish-cratesio` (crates.io), and the **wasm-publish** workflow (npm).
2. After the `publish` job succeeds, verify the package is live:
```bash
@@ -144,6 +152,16 @@ pip install ferro-ta==0.2.0
python -c "import ferro_ta; print(ferro_ta.__version__ if hasattr(ferro_ta,'__version__') else 'ok')"
```
For version-specific verification, also check at least one install on each
supported Python line, for example:
```bash
uv venv --python 3.13 .venv-313
. .venv-313/bin/activate
uv pip install ferro-ta==0.2.0
python -c "import ferro_ta; print(ferro_ta.SMA([1.0, 2.0, 3.0], 2))"
```
3. If anything fails: fix the issue, bump to a patch version (`0.2.1`), and repeat.
---