feat: buildwheel in ghrunner (#221)

This commit is contained in:
Cuiys 2026-03-16 14:20:01 +08:00 committed by GitHub
parent de1e7050d7
commit 7bc4838aca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 151 additions and 190 deletions

102
.github/workflows/_build_wheel_job.yml vendored Normal file
View File

@ -0,0 +1,102 @@
name: "(Reusable) Build, Publish and Smoke-test a Wheel"
on:
workflow_call:
inputs:
runner:
description: "GitHub Actions runner label"
required: true
type: string
pypi_repository_url:
description: "PyPI repository URL (empty string means official PyPI)"
required: false
type: string
default: ""
secrets:
PYPI_API_TOKEN:
required: true
jobs:
build_publish_test:
name: Build / publish / smoke-test on ${{ inputs.runner }}
runs-on: ${{ inputs.runner }}
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
submodules: recursive
- name: Set up Python (for cibuildwheel controller)
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install cibuildwheel
run: |
pip install --upgrade pip
pip install cibuildwheel==3.4.0
- name: Build wheels using cibuildwheel
run: |
python -m cibuildwheel --output-dir wheelhouse
# Save list of built wheels for publishing
ls wheelhouse/*.whl | tee $GITHUB_STEP_SUMMARY
echo "wheels=$(ls wheelhouse/*.whl | tr '\n' ' ')" >> $GITHUB_ENV
- name: Publish to PyPI
if: success() && github.event_name == 'workflow_dispatch'
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
TWINE_REPOSITORY_URL: ${{ inputs.pypi_repository_url }}
run: |
pip install twine
twine upload --skip-existing --verbose wheelhouse/*.whl
- name: Smoke test from PyPI
if: success() && github.event_name == 'workflow_dispatch'
shell: bash
env:
PYPI_REPOSITORY_URL: ${{ inputs.pypi_repository_url }}
run: |
# Extract version from wheel filename (e.g. zvec-0.2.1.dev24-cp311-...whl -> 0.2.1.dev24)
WHEEL_FILE=$(ls wheelhouse/zvec-*.whl | head -1)
ZVEC_VERSION=$(basename "$WHEEL_FILE" | sed 's/zvec-\([^-]*\)-.*/\1/')
# Build index-url flags: use TestPyPI when repository URL is set, otherwise official PyPI
if [ -n "$PYPI_REPOSITORY_URL" ]; then
INDEX_FLAGS="--index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/"
echo "Waiting for zvec==$ZVEC_VERSION to become available on TestPyPI..."
else
INDEX_FLAGS=""
echo "Waiting for zvec==$ZVEC_VERSION to become available on PyPI..."
fi
# Poll until the version is available (max 5 minutes)
FOUND=0
for i in $(seq 1 30); do
if pip install $INDEX_FLAGS --dry-run "zvec==$ZVEC_VERSION" > /dev/null 2>&1; then
echo "Version $ZVEC_VERSION is available."
FOUND=1
break
fi
echo "Attempt $i/30: not yet available, retrying in 10s..."
sleep 10
done
if [ "$FOUND" -eq 0 ]; then
echo "ERROR: Timed out (5 min) waiting for zvec==$ZVEC_VERSION on PyPI. Aborting smoke test."
exit 1
fi
# Create a clean venv and install
python -m venv test_env
source test_env/bin/activate
pip install --upgrade pip
pip install $INDEX_FLAGS "zvec==$ZVEC_VERSION"
pip install --upgrade pip
pip install $INDEX_FLAGS "zvec==$ZVEC_VERSION"
# Run a simple smoke test
python -c "import zvec; print('Import OK:', zvec.__version__)"

View File

@ -8,97 +8,28 @@ permissions:
jobs:
build_wheels_linux_x64:
name: Build wheels on self-hosted manylinux_2_28_x64 for TestPyPi
runs-on: linux_x64
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
submodules: recursive
- name: Set up Python (for cibuildwheel controller)
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install cibuildwheel
run: |
pip install --upgrade pip
pip install cibuildwheel==2.17.0
- name: Build wheels using cibuildwheel
run: |
python -m cibuildwheel --output-dir wheelhouse
# Save list of built wheels for publishing
ls wheelhouse/*.whl | tee $GITHUB_STEP_SUMMARY
echo "wheels=$(ls wheelhouse/*.whl | tr '\n' ' ')" >> $GITHUB_ENV
- name: Publish to TestPyPI
if: success() && github.event_name == 'workflow_dispatch'
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }}
TWINE_REPOSITORY_URL: https://test.pypi.org/legacy/
run: |
pip install twine
twine upload --skip-existing --verbose wheelhouse/*.whl
- name: (Optional) Install and test from TestPyPI
if: success() && github.event_name == 'workflow_dispatch'
run: |
# Create a clean venv
python -m venv test_env
source test_env/bin/activate
pip install --upgrade pip
# Install from TestPyPI (must allow pre-releases if version has dev/alpha)
pip install numpy
pip install --index-url https://test.pypi.org/simple/ zvec
# Run a simple smoke test
python -c "import zvec; print('Import OK:', zvec.__version__)"
shell: bash
name: Build wheels on ubuntu-24.04 (x64) for TestPyPi
uses: ./.github/workflows/_build_wheel_job.yml
with:
runner: ubuntu-24.04
pypi_repository_url: https://test.pypi.org/legacy/
secrets:
PYPI_API_TOKEN: ${{ secrets.TEST_PYPI_API_TOKEN }}
build_wheels_linux_arm64:
name: Build wheels on self-hosted manylinux_2_28_arm64 for TestPyPi
runs-on: linux_arm64
name: Build wheels on ubuntu-24.04-arm (arm64) for TestPyPi
uses: ./.github/workflows/_build_wheel_job.yml
with:
runner: ubuntu-24.04-arm
pypi_repository_url: https://test.pypi.org/legacy/
secrets:
PYPI_API_TOKEN: ${{ secrets.TEST_PYPI_API_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
submodules: recursive
- name: Set up Python (for cibuildwheel controller)
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install cibuildwheel
run: |
pip install --upgrade pip
pip install cibuildwheel==2.17.0
- name: Build wheels using cibuildwheel
run: |
python -m cibuildwheel --output-dir wheelhouse
# Save list of built wheels for publishing
ls wheelhouse/*.whl | tee $GITHUB_STEP_SUMMARY
echo "wheels=$(ls wheelhouse/*.whl | tr '\n' ' ')" >> $GITHUB_ENV
- name: Publish to TestPyPI
if: success() && github.event_name == 'workflow_dispatch'
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }}
TWINE_REPOSITORY_URL: https://test.pypi.org/legacy/
run: |
pip install twine
twine upload --skip-existing --verbose wheelhouse/*.whl
- name: (Optional) Install and test from TestPyPI
if: success() && github.event_name == 'workflow_dispatch'
run: |
# Create a clean venv
python -m venv test_env
source test_env/bin/activate
pip install --upgrade pip
# Install from TestPyPI (must allow pre-releases if version has dev/alpha)
pip install numpy
pip install --index-url https://test.pypi.org/simple/ zvec
# Run a simple smoke test
python -c "import zvec; print('Import OK:', zvec.__version__)"
shell: bash
build_wheels_macos_arm64:
name: Build wheels on macos-15 (arm64) for TestPyPi
uses: ./.github/workflows/_build_wheel_job.yml
with:
runner: macos-15
pypi_repository_url: https://test.pypi.org/legacy/
secrets:
PYPI_API_TOKEN: ${{ secrets.TEST_PYPI_API_TOKEN }}

View File

@ -8,101 +8,25 @@ permissions:
jobs:
build_wheels_linux_x64:
name: Build wheels on self-hosted manylinux_2_28_x64
runs-on: linux_x64
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
submodules: recursive
- name: Set up Python (for cibuildwheel controller)
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install cibuildwheel
run: |
pip install --upgrade pip
pip install cibuildwheel==2.17.0
- name: Build wheels using cibuildwheel
run: |
python -m cibuildwheel --output-dir wheelhouse
# Save list of built wheels for publishing
ls wheelhouse/*.whl | tee $GITHUB_STEP_SUMMARY
echo "wheels=$(ls wheelhouse/*.whl | tr '\n' ' ')" >> $GITHUB_ENV
- name: Publish to PyPI
if: success() && github.event_name == 'workflow_dispatch'
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
TWINE_REPOSITORY: pypi
run: |
pip install twine
twine upload --skip-existing --verbose wheelhouse/*.whl
- name: (Optional) Install and test from PyPI
if: success() && github.event_name == 'workflow_dispatch'
run: |
# Create a clean venv
python -m venv test_env
source test_env/bin/activate
pip install --upgrade pip
# Install from PyPI
pip install zvec
# Run a simple smoke test
python -c "import zvec; print('Import OK:', zvec.__version__)"
shell: bash
name: Build wheels on ubuntu-24.04 (x64) for PyPi
uses: ./.github/workflows/_build_wheel_job.yml
with:
runner: ubuntu-24.04
secrets:
PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
build_wheels_linux_arm64:
name: Build wheels on self-hosted manylinux_2_28_arm64
runs-on: linux_arm64
name: Build wheels on ubuntu-24.04-arm (arm64) for PyPi
uses: ./.github/workflows/_build_wheel_job.yml
with:
runner: ubuntu-24.04-arm
secrets:
PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
submodules: recursive
- name: Set up Python (for cibuildwheel controller)
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install cibuildwheel
run: |
pip install --upgrade pip
pip install cibuildwheel==2.17.0
- name: Build wheels using cibuildwheel
run: |
python -m cibuildwheel --output-dir wheelhouse
# Save list of built wheels for publishing
ls wheelhouse/*.whl | tee $GITHUB_STEP_SUMMARY
echo "wheels=$(ls wheelhouse/*.whl | tr '\n' ' ')" >> $GITHUB_ENV
- name: Publish to PyPI
if: success() && github.event_name == 'workflow_dispatch'
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
TWINE_REPOSITORY: pypi
run: |
pip install twine
twine upload --skip-existing --verbose wheelhouse/*.whl
- name: (Optional) Install and test from PyPI
if: success() && github.event_name == 'workflow_dispatch'
run: |
# Create a clean venv
python -m venv test_env
source test_env/bin/activate
pip install --upgrade pip
# Install from PyPI
pip install zvec
# Run a simple smoke test
python -c "import zvec; print('Import OK:', zvec.__version__)"
shell: bash
build_wheels_macos_arm64:
name: Build wheels on macos-15 (arm64) for PyPi
uses: ./.github/workflows/_build_wheel_job.yml
with:
runner: macos-15
secrets:
PYPI_API_TOKEN: ${{ secrets.PYPI_API_TOKEN }}

View File

@ -51,7 +51,7 @@ test = [
"pytest >=8.0",
"pytest-cov >=4.1",
"pytest-mock >=3.12",
"cibuildwheel == 2.17.0",
"cibuildwheel == 3.4.0",
]
docs = [
"mkdocs >=1.5",
@ -70,7 +70,7 @@ dev = [
"pytest >=8.0",
"pytest-cov >=4.1",
"pytest-mock >=3.12",
"cibuildwheel == 2.17.0",
"cibuildwheel == 3.4.0",
# Inherit docs deps
"mkdocs >=1.5",
"mkdocs-material >=9.5",
@ -130,6 +130,7 @@ BUILD_PYTHON_BINDINGS = "ON"
[tool.setuptools_scm]
local_scheme = "no-local-version"
version_scheme = "guess-next-dev"
fallback_version = "0.2.1b1"
######################################################################################################
# TESTING & QUALITY
######################################################################################################
@ -169,11 +170,12 @@ build = [
]
build-frontend = "build"
test-requires = ["pytest", "numpy"]
test-command = "cd {project} && pytest python/tests -v --tb=short"
build-verbosity = 1
[tool.cibuildwheel.linux]
archs = ["auto"]
test-command = "cd {project} && pytest python/tests -v --tb=short"
environment = { CMAKE_GENERATOR = "Unix Makefiles", CMAKE_BUILD_PARALLEL_LEVEL = "16" }
manylinux-x86_64-image = "manylinux_2_28"
manylinux-aarch64-image = "manylinux_2_28"
# Skip 32-bit builds and musllinux
@ -181,7 +183,9 @@ skip = ["*-manylinux_i686", "*-musllinux*"]
[tool.cibuildwheel.macos]
archs = ["arm64"]
environment = { MACOSX_DEPLOYMENT_TARGET = "11.0" }
# Inherits CMAKE_GENERATOR and CMAKE_BUILD_PARALLEL_LEVEL from [tool.cibuildwheel] won't work;
# platform-level environment overrides the top-level entirely, so all vars must be listed here
environment = { CMAKE_GENERATOR = "Unix Makefiles", CMAKE_BUILD_PARALLEL_LEVEL = "16", MACOSX_DEPLOYMENT_TARGET = "11.0" }
######################################################################################################
# CODE QUALITY & FORMATTING (Ruff)
######################################################################################################