Skip to content

Publish Python SDK

Publish Python SDK #7

name: Publish Python SDK
# This workflow builds native Python wheels, uploads them to GitHub Releases,
# and publishes the wheels to PyPI.
on:
workflow_call:
inputs:
target:
required: false
type: string
description: "Specific target to build (e.g., x86_64-unknown-linux-gnu). If empty, builds all targets."
workflow_dispatch:
inputs:
target:
required: false
type: string
description: "Specific target to build (e.g., x86_64-unknown-linux-gnu). If empty, builds all targets."
permissions:
contents: write
jobs:
# ─────────────────────────────────────────────────────────────────
# Build native wheels for each platform
# These are uploaded to GitHub Releases, NOT to PyPI
# ─────────────────────────────────────────────────────────────────
build:
strategy:
fail-fast: false
matrix:
settings:
- host: macos-latest
target: aarch64-apple-darwin
- host: ubuntu-latest
target: x86_64-unknown-linux-gnu
manylinux: 2_28
- host: windows-latest
target: x86_64-pc-windows-msvc
name: Build ${{ matrix.settings.target }}
runs-on: ${{ matrix.settings.host }}
steps:
- uses: actions/checkout@v4
- name: Setup workspace context
shell: bash
run: bash .github/setup-workspace.sh
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: |
3.10
3.11
3.12
3.13
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.settings.target }}
- name: Install protobuf compiler
shell: bash
run: |
if [ "${{ matrix.settings.host }}" = "ubuntu-latest" ]; then
sudo apt-get update && sudo apt-get install -y protobuf-compiler
elif [ "${{ matrix.settings.host }}" = "windows-latest" ]; then
choco install protoc -y
else
brew install protobuf
fi
# Build native wheels - these go to GitHub Releases ONLY
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.settings.target }}
args: --release --out dist --manifest-path sdk/python/Cargo.toml -i python3.10 -i python3.11 -i python3.12 -i python3.13
manylinux: ${{ matrix.settings.manylinux || 'auto' }}
- name: Upload wheels to GitHub Releases
uses: actions/upload-artifact@v4
with:
name: python-wheels-${{ matrix.settings.target }}
path: dist/*.whl
if-no-files-found: error
# ─────────────────────────────────────────────────────────────────
# Publish to GitHub Releases and PyPI
# ─────────────────────────────────────────────────────────────────
publish:
name: Publish Python SDK
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- name: Download all wheel artifacts
uses: actions/download-artifact@v4
with:
pattern: python-wheels-*
path: dist
merge-multiple: true
- name: List wheels
run: ls -la dist/
# Upload native wheels to GitHub Releases (NOT to PyPI).
# Guarded by a tag check so workflow_dispatch from a branch can still
# exercise the PyPI publish path for debugging without the GitHub
# Release steps tripping on a missing release name.
- name: Ensure release exists and upload native assets
if: startsWith(github.ref, 'refs/tags/')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
if gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1; then
echo "Release $GITHUB_REF_NAME already exists"
else
gh release create "$GITHUB_REF_NAME" \
--title "$GITHUB_REF_NAME" \
--notes "Native Python wheel assets"
fi
gh release upload "$GITHUB_REF_NAME" dist/*.whl --clobber
# Generate manifest with wheel metadata for bootstrap downloader
- name: Generate native wheel manifest
if: startsWith(github.ref, 'refs/tags/')
run: |
python .github/scripts/generate_python_release_manifest.py \
dist \
dist/python-native-manifest.json \
--version "${GITHUB_REF_NAME#v}"
# Upload manifest to GitHub Releases
- name: Upload manifest to GitHub Releases
if: startsWith(github.ref, 'refs/tags/')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release upload "$GITHUB_REF_NAME" dist/python-native-manifest.json --clobber
# Publish native wheels to PyPI
- name: Publish wheels to PyPI
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_NATIVE_TOKEN }}
run: |
python -m pip install --upgrade pip twine
python -m twine upload --verbose --skip-existing dist/*.whl