Skip to content

Build Wheels & Publish #7

Build Wheels & Publish

Build Wheels & Publish #7

Workflow file for this run

# Build pre-compiled wheels for all platforms and publish to PyPI.
# Triggered on release publish or version tag push.
# Tests must pass before wheels are built and published.
name: Build Wheels & Publish
on:
release:
types: [published]
push:
tags:
- 'v*'
# Allow manual trigger for testing the workflow
workflow_dispatch:
permissions:
contents: read
jobs:
# ── Gate: run tests first ──────────────────────────────────────
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ['3.10', '3.12']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install package (builds C backend)
run: |
pip install pytest pandas
pip install -e ".[all]"
- name: Run tests
run: pytest tests/ -x -q
# ── Build wheels for all platforms ─────────────────────────────
# macOS needs two runners: macos-13 (Intel) for x86_64, macos-latest (M-series) for arm64.
# cibuildwheel cannot cross-compile C extensions across architectures.
build_wheels:
needs: test
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
cibw_archs: "x86_64 aarch64"
artifact_name: "wheels-linux"
- os: macos-latest
cibw_archs: "arm64"
artifact_name: "wheels-macos-arm64"
- os: windows-latest
cibw_archs: "AMD64"
artifact_name: "wheels-windows"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
# Linux aarch64 wheels need QEMU emulation
- name: Set up QEMU
if: runner.os == 'Linux'
uses: docker/setup-qemu-action@v3
with:
platforms: arm64
# Pre-download virtualenv.pyz into cibuildwheel's cache dir using
# authenticated GitHub API to avoid HTTP 429 rate limits.
- name: Pre-cache virtualenv for cibuildwheel
shell: bash
env:
GH_TOKEN: ${{ github.token }}
run: |
VENV_VERSION="20.27.1"
VENV_FILE="virtualenv-${VENV_VERSION}.pyz"
# Determine cibuildwheel cache directory per OS
if [ "$RUNNER_OS" = "macOS" ]; then
CACHE_DIR="$HOME/Library/Caches/cibuildwheel"
elif [ "$RUNNER_OS" = "Windows" ]; then
CACHE_DIR="$LOCALAPPDATA/cibuildwheel/Cache"
else
CACHE_DIR="$HOME/.cache/cibuildwheel"
fi
mkdir -p "$CACHE_DIR"
DEST="$CACHE_DIR/$VENV_FILE"
if [ ! -f "$DEST" ]; then
echo "Downloading virtualenv.pyz with authenticated GitHub API..."
curl -sSL --retry 3 --retry-delay 5 \
-H "Authorization: Bearer $GH_TOKEN" \
-H "Accept: application/octet-stream" \
"https://github.com/pypa/get-virtualenv/raw/refs/tags/${VENV_VERSION}/public/virtualenv.pyz" \
-o "$DEST"
echo "Downloaded to $DEST ($(wc -c < "$DEST") bytes)"
else
echo "virtualenv.pyz already cached at $DEST"
fi
- name: Build wheels
uses: pypa/cibuildwheel@v2.22
env:
CIBW_ARCHS: ${{ matrix.cibw_archs }}
- name: Upload wheel artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact_name }}
path: ./wheelhouse/*.whl
# ── Build source distribution ──────────────────────────────────
build_sdist:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Build sdist
run: |
pip install build
python -m build --sdist
- name: Upload sdist artifact
uses: actions/upload-artifact@v4
with:
name: sdist
path: dist/*.tar.gz
# ── Publish to PyPI ────────────────────────────────────────────
publish:
needs: [build_wheels, build_sdist]
runs-on: ubuntu-latest
# Only publish on release or tag push (not manual dispatch)
if: github.event_name == 'release' || startsWith(github.ref, 'refs/tags/v')
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}