Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions .github/workflows/publish-pypi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
name: Publish PyPI Wheels

on:
workflow_dispatch:
inputs:
tag:
description: "Git tag to publish (empty = build current commit, artifact only)"
required: false
default: ""
type: string

run-name: ${{ inputs.tag != '' && format('pypi/{0}', inputs.tag) || format('artifacts-only/{0}', github.sha) }}

jobs:
resolve-target:
name: Resolve build target
runs-on: ubuntu-latest
outputs:
ref: ${{ steps.target.outputs.ref }}
publish: ${{ steps.target.outputs.publish }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Resolve ref and publish mode
id: target
shell: bash
run: |
raw_tag="${{ inputs.tag }}"
if [[ -z "$raw_tag" ]]; then
echo "ref=${GITHUB_SHA}" >> "$GITHUB_OUTPUT"
echo "publish=false" >> "$GITHUB_OUTPUT"
exit 0
fi

if [[ "$raw_tag" == refs/tags/* ]]; then
tag_ref="$raw_tag"
else
tag_ref="refs/tags/$raw_tag"
fi

git fetch --force --tags
git rev-parse --verify "$tag_ref"
echo "ref=$tag_ref" >> "$GITHUB_OUTPUT"
echo "publish=true" >> "$GITHUB_OUTPUT"

build-and-test:
name: ${{ matrix.label }}
runs-on: ${{ matrix.os }}
needs: resolve-target
strategy:
fail-fast: false
matrix:
include:
- label: Linux (x86_64)
os: ubuntu-latest
archs: x86_64
artifact: wheels-linux-x86_64
- label: Linux (aarch64)
os: ubuntu-24.04-arm
archs: aarch64
artifact: wheels-linux-aarch64
- label: Windows (AMD64)
os: windows-latest
archs: AMD64
artifact: wheels-windows
- label: macOS (arm64)
os: macos-14
archs: arm64
artifact: wheels-macos

steps:
- name: Checkout tag
uses: actions/checkout@v4
with:
ref: ${{ needs.resolve-target.outputs.ref }}
fetch-depth: 0
submodules: true

- name: Build wheels and run wheel tests
uses: pypa/cibuildwheel@v2.22.0
env:
CIBW_BUILD: cp310-* cp311-* cp312-* cp313-*
CIBW_SKIP: "*-musllinux_*"
CIBW_TEST_REQUIRES: pytest scipy==1.15.0 # 1.15.0 have wheels for Python 3.10
CIBW_TEST_COMMAND: pytest -q {project}/tests
CIBW_PROJECT_REQUIRES_PYTHON: ">=3.10,<3.14"
CIBW_ARCHS: ${{ matrix.archs }}

- name: Upload wheel artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact }}
path: wheelhouse/*.whl
if-no-files-found: error

publish-to-pypi:
name: Publish to PyPI
runs-on: ubuntu-latest
needs:
- resolve-target
- build-and-test
if: ${{ needs.resolve-target.outputs.publish == 'true' }}
permissions:
id-token: write

steps:
- name: Download all wheel artifacts
uses: actions/download-artifact@v4
with:
pattern: wheels-*
merge-multiple: true
path: dist

- name: Verify wheel files
run: ls -lh dist/*.whl

- name: Upload to PyPI (Trusted Publisher)
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://upload.pypi.org/legacy/
packages-dir: dist
verbose: true
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ sample.*
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
.mypy_cache

# C extensions
*.so
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ classifiers = [
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"License :: OSI Approved :: MIT License",
"Topic :: Scientific/Engineering",
"Topic :: Software Development",
Expand Down