Skip to content

chore(py): release tket-exts 0.14.1 (#1908) #1294

chore(py): release tket-exts 0.14.1 (#1908)

chore(py): release tket-exts 0.14.1 (#1908) #1294

Workflow file for this run

# This file is loosely based on the one generated by maturin v1.9.2 using
#
# maturin generate-ci github
#
# The script has been heavily modified from there.
# In particular, we run `maturin build` manually instead of using the `maturin-action`,
# so we can setup the workspace dependencies (rust, llvm, conan, tket-c-api) correctly.
#
name: Python wheels 🐍
on:
push:
branches:
- main
tags:
- "tket-py-v*"
workflow_dispatch:
permissions:
contents: read
env:
MODULE_DIR: tket-py
# Pinned version for the uv package manager
UV_VERSION: "0.11.15"
UV_FROZEN: 1
# Python version used to build the wheels
# We use abi3-compatible building, so this still supports newer Python versions
PYTHON_VERSION: "3.10"
# sccache config
SCCACHE_GHA_ENABLED: "true"
RUSTC_WRAPPER: "sccache"
jobs:
# Check if the tag matches the package name,
# or if the workflow is running on a non-tag event.
check-tag:
name: Check tag
runs-on: ubuntu-latest
outputs:
run: ${{ steps.check-tag.outputs.run }}
steps:
- name: Check tag
id: check-tag
run: |
echo "run=$SHOULD_RUN" >> $GITHUB_OUTPUT
env:
SHOULD_RUN: ${{ github.ref_type != 'tag' || startsWith(github.ref, 'refs/tags/tket-py-v') }}
build:
name: ${{ matrix.platform.target }}
needs: check-tag
if: ${{ needs.check-tag.outputs.run == 'true' }}
runs-on: ${{ matrix.platform.runner }}
strategy:
fail-fast: false
matrix:
# List of platforms to build the wheels for.
#
# Each entry defines:
# - runner: The github runner to use for the build.
# - target: The target platform used when building the rust code.
# - manylinux: (optional) If doing manylinux-compatible wheels,
# the manylinux version to use. Setting this option causes the wheels
# to be built with zig.
# - test-wheels: Whether to test installing the wheels.
# This is only done when the target platform matches the runner platform.
# - macos-compatibility: the minimum supported macosx version to build macos
# wheels for. Violations (e.g. library dependencies on newer system libraries)
# will result in CI failure.
#
# Note that platform support is limited by the pytket wheels' availability.
# Since we have it as a dependency, wheels built for other platforms will be unusable.
platform:
# ---------------- Linux
- runner: ubuntu-24.04-arm
target: aarch64-unknown-linux-gnu
manylinux: manylinux_2_28
test-wheels: true
- runner: ubuntu-24.04
target: x86_64-unknown-linux-gnu
manylinux: manylinux_2_28
test-wheels: true
# ---------------- Mac
- runner: macos-14
target: aarch64-apple-darwin
macos-compatibility: "11.0"
test-wheels: true
- runner: macos-15-intel
target: x86_64-apple-darwin
macos-compatibility: "11.0"
test-wheels: true
# ---------------- Windows
- runner: windows-2025
target: x86_64-pc-windows-msvc
test-wheels: true
steps:
- uses: actions/checkout@v7
- uses: mozilla-actions/sccache-action@v0.0.10
- name: Install stable toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.platform.target }}
- name: Set up uv
uses: astral-sh/setup-uv@v7
with:
version: ${{ env.UV_VERSION }}
enable-cache: true
- uses: quantinuum/hugrverse-env/install-hugrenv-action@main
- uses: mlugg/setup-zig@v2
if: ${{ matrix.platform.manylinux }}
- name: Set MACOSX_DEPLOYMENT_TARGET on macos
if: matrix.platform.macos-compatibility
run: echo "MACOSX_DEPLOYMENT_TARGET=${{ matrix.platform.macos-compatibility }}" >> $GITHUB_ENV
- name: Set compatibility flag
id: compatibility-flag
shell: bash
run: |
if [ -n "${{ matrix.platform.manylinux }}" ]; then
WITH_ZIG_FLAG="--with ziglang --with maturin[zig]"
COMPATIBILITY_FLAG="--compatibility ${{ matrix.platform.manylinux }} --zig"
else
WITH_ZIG_FLAG=""
COMPATIBILITY_FLAG=""
fi
echo "COMPATIBILITY_FLAG: $COMPATIBILITY_FLAG"
echo "WITH_ZIG_FLAG: $WITH_ZIG_FLAG"
echo "COMPATIBILITY_FLAG=$COMPATIBILITY_FLAG" >> "$GITHUB_OUTPUT"
echo "WITH_ZIG_FLAG=$WITH_ZIG_FLAG" >> "$GITHUB_OUTPUT"
- name: Copy DLL to package directory (Windows)
if: runner.os == 'Windows'
shell: bash
run: |
# Copy the DLL to the package directory so maturin can include it in the wheel
if [ -f "$TKET_C_API_PATH/bin/tket-c-api.dll" ]; then
cp "$TKET_C_API_PATH/bin/tket-c-api.dll" "$MODULE_DIR/tket/tket-c-api.dll"
echo "Copied DLL to $MODULE_DIR/tket/tket-c-api.dll"
ls -lh "$MODULE_DIR/tket/tket-c-api.dll"
else
echo "ERROR: DLL not found at $TKET_C_API_PATH/bin/tket-c-api.dll"
exit 1
fi
- name: Build wheels
shell: bash
run: |
cd ${{ env.MODULE_DIR }}
uv run --python ${{ env.PYTHON_VERSION }} \
${{ steps.compatibility-flag.outputs.WITH_ZIG_FLAG }} \
maturin build ${{ steps.compatibility-flag.outputs.COMPATIBILITY_FLAG }} \
--release \
--out ../dist \
--target ${{ matrix.platform.target }} \
--strip
- name: Build free-threaded wheels
# TODO: Waiting for hugr 0.16.1 / 0.17.0 to release python 3.14 free-threaded wheels.
# <https://github.com/Quantinuum/hugr/pull/3021>
if: false
shell: bash
run: |
cd ${{ env.MODULE_DIR }}
uv run --python ${{ env.PYTHON_VERSION }}+freethreaded \
${{ steps.compatibility-flag.outputs.WITH_ZIG_FLAG }} \
maturin build \
${{ steps.compatibility-flag.outputs.COMPATIBILITY_FLAG }} \
--release \
--out ../dist \
--target ${{ matrix.platform.target }} \
--strip
- name: Repair macOS wheels with delocate
if: runner.os == 'macOS'
shell: bash
run: |
echo "Repairing macOS wheels with delocate..."
export DYLD_LIBRARY_PATH="$HUGRENV_PATH/lib:$HUGRENV_PATH/lib64:$DYLD_LIBRARY_PATH"
ls -lh dist || true
MAC_WHEELS=$(ls dist/*macosx*.whl 2>/dev/null || true)
if [ -z "$MAC_WHEELS" ]; then
echo "No macOS wheels found to repair; skipping delocate."
exit 0
fi
uvx --from delocate delocate-listdeps $MAC_WHEELS || true
uvx --from delocate delocate-wheel -v -w dist/repaired $MAC_WHEELS
rm -v -f $MAC_WHEELS
mv -v dist/repaired/*.whl dist/
rmdir -v dist/repaired || true
echo "Repaired wheels:"
ls -lh dist/*macosx*.whl
- name: Upload wheels
uses: actions/upload-artifact@v7
with:
name: wheels-${{ matrix.platform.target }}
path: dist
if-no-files-found: error
- name: Test installing the built wheels
shell: bash
run: |
echo "Testing the newly built ${{ matrix.platform.target }} wheels..."
if [ "$TEST_INSTALL" = "true" ]; then
# Remove original libraries from the build environment to ensure that
# the test installation is actually using the built wheels, and not picking up
# the libraries from the hugrverse environment.
rm -rf $HUGRENV_PATH;
unset HUGRENV_PATH;
unset TKET_C_API_PATH;
unset LLVM_SYS_211_PREFIX;
# Import tket from within a fresh environment/directory rather than the source
# directory to avoid source conflicts (the repaired wheel has modified library paths)
tmpdir=$(mktemp -d);
cd $tmpdir;
# To avoid interference from the existing environment, use a python venv.
# Note that this approach is chosen because some unwanted behaviour was seen
# on some platforms when using uv directly.
uv run python -m venv venv
if [ -d "venv/Scripts" ]; then
venv_python="venv/Scripts/python"
venv_pip="venv/Scripts/pip"
else
venv_python="venv/bin/python"
venv_pip="venv/bin/pip"
fi
# First install the pure python packages from this commit; this avoids fetching them
# from pypi and providing false positives or false negatives in the presence of a conflict.
${venv_pip} install ${GITHUB_WORKSPACE}/tket-eccs
${venv_pip} install ${GITHUB_WORKSPACE}/tket-exts
# Install the built wheel
wheel_path=$(ls "$GITHUB_WORKSPACE/dist/"*.whl | head -n 1)
${venv_pip} install ${wheel_path}
# Try importing tket to verify that the installation works.
# Although not a comprehensive test (TODO), this at least verifies
# that the dynamic libraries are correctly linked and can be loaded.
${venv_python} -c "import tket; print(tket.__file__);"
# Now return to the original workspace for further steps.
cd $GITHUB_WORKSPACE;
fi
uvx twine check --strict dist/*
env:
WORKSPACE: ${{ github.workspace }}
TEST_INSTALL: ${{ matrix.platform.test-wheels }}
sdist:
needs: check-tag
if: ${{ needs.check-tag.outputs.run == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: mozilla-actions/sccache-action@v0.0.10
- name: Set up uv
uses: astral-sh/setup-uv@v7
with:
version: ${{ env.UV_VERSION }}
enable-cache: true
- name: Build sdist
run: |
cd ${{ env.MODULE_DIR }}
uv run --no-project \
--python ${{ env.PYTHON_VERSION }} \
--with maturin \
maturin sdist \
--out ../dist \
--manifest-path Cargo.toml
- name: Upload sdist
uses: actions/upload-artifact@v7
with:
name: wheels-sdist
path: dist
if-no-files-found: error
release:
name: Release
runs-on: ubuntu-latest
if: ${{ github.ref_type == 'tag' && startsWith(github.ref, 'refs/tags/tket-py-v') }}
needs: [build, sdist]
environment: release
permissions:
# Use to sign the release artifacts
id-token: write
# Used to upload release artifacts
contents: write
# Used to generate artifact attestation
attestations: write
steps:
- uses: actions/checkout@v7
- name: Check if the tag has an associated release
env:
GH_TOKEN: ${{ github.token }}
run: |
if ! gh release view ${{ github.ref_name }}; then
echo "No release found for tag ${{ github.ref_name }}"
exit 1
fi
- uses: actions/download-artifact@v8
with:
path: dist
merge-multiple: true
- name: Generate artifact attestation
uses: actions/attest@v4.2.0
with:
subject-path: 'dist/*'
- name: Report
run: |
echo "Publishing to PyPI..."
echo "Based on the following workflow variables, this is a new version tag push:"
echo " - event_name: ${{ github.event_name }}"
echo " - ref_type: ${{ github.ref_type }}"
echo " - ref: ${{ github.ref }}"
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
verbose: true
skip-existing: true
- name: Upload wheels to the release and un-draft it
env:
GH_TOKEN: ${{ github.token }}
run: |
# Upload wheels to the release assets
gh release upload ${{ github.ref_name }} dist/*
# Un-draft the release on GitHub now that the package has been published to PyPI
gh release edit ${{ github.ref_name }} --draft=false