name: Publish Packages
on:
release:
types: [published]
workflow_dispatch:
inputs:
environment:
description: "Environment to publish to"
required: true
default: "testpypi"
type: choice
options:
- testpypi
- pypi
dry_run:
description: "Dry run - build only, skip publishing jobs"
required: false
default: false
type: boolean
jobs:
build-wheels:
name: Build wheels (${{ matrix.name }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- name: manylinux x86_64
os: ubuntu-latest
cibw_archs: x86_64
- name: manylinux aarch64
os: ubuntu-24.04-arm
cibw_archs: aarch64
- name: macOS Intel (15+ SDK)
os: macos-15-intel
cibw_archs: x86_64
- name: macOS Apple Silicon (15+)
os: macos-15
cibw_archs: arm64
steps:
- uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Build wheels
uses: pypa/cibuildwheel@v3.1.4
env:
CIBW_ARCHS: ${{ matrix.cibw_archs }}
CIBW_BEFORE_ALL: |
echo "=== Installing Linux build dependencies (if needed) ==="
if command -v dnf >/dev/null 2>&1; then
dnf install -y openssl-devel pkgconf-pkg-config || dnf install -y openssl-devel pkgconfig
elif command -v yum >/dev/null 2>&1; then
yum install -y openssl-devel pkgconfig
elif command -v apt-get >/dev/null 2>&1; then
apt-get update
apt-get install -y libssl-dev pkg-config
fi
if command -v pkg-config >/dev/null 2>&1 && pkg-config --exists openssl; then
export OPENSSL_DIR="$(pkg-config --variable=prefix openssl)"
export OPENSSL_LIB_DIR="$(pkg-config --variable=libdir openssl)"
export OPENSSL_INCLUDE_DIR="$(pkg-config --variable=includedir openssl)"
echo "Using OpenSSL from: ${OPENSSL_DIR}"
fi
echo "=== Installing Rust toolchain ==="
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable
source "$HOME/.cargo/env"
cargo --version
rustc --version
with:
package-dir: .
output-dir: wheelhouse
config-file: "{package}/pyproject.toml"
- uses: actions/upload-artifact@v4
with:
name: wheels-${{ matrix.cibw_archs }}
path: ./wheelhouse/*.whl
build-sdist:
name: Build source distribution
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Build sdist
run: |
python -m pip install --upgrade pip
python -m pip install maturin
maturin sdist --out dist
- uses: actions/upload-artifact@v4
with:
name: sdist
path: ./dist/*.tar.gz
publish-python:
name: Publish Python Package
runs-on: ubuntu-latest
needs: [build-wheels, build-sdist]
if: >
github.event_name == 'release' ||
(github.event_name == 'workflow_dispatch' && (!github.event.inputs.dry_run || github.event.inputs.dry_run == 'false'))
environment: ${{ github.event.inputs.environment || 'pypi' }}
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
path: artifacts
- name: Collect packages
run: |
mkdir -p dist
find artifacts -name "*.whl" -exec cp {} dist/ \;
find artifacts -name "*.tar.gz" -exec cp {} dist/ \;
ls -la dist/
- name: Publish to TestPyPI
if: github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'testpypi'
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
- name: Publish to PyPI
if: github.event_name == 'release' && github.event.action == 'published'
uses: pypa/gh-action-pypi-publish@release/v1
publish-rust:
name: Publish Rust Crate
runs-on: ubuntu-latest
if: >
github.event_name == 'release' ||
(github.event_name == 'workflow_dispatch' && (!github.event.inputs.dry_run || github.event.inputs.dry_run == 'false'))
steps:
- uses: actions/checkout@v5
- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
- name: Publish to crates.io
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: cargo publish