Skip to content
Merged
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
12 changes: 8 additions & 4 deletions .github/RELEASING.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
# Building and publishing python-ace

The **Build wheels** GitHub Actions workflow (`.github/workflows/wheels.yml`)
builds a source distribution and Linux x86-64 manylinux_2_28 wheels. Pull
builds a source distribution, Linux x86-64 manylinux_2_28 wheels, and native
macOS wheels for Apple Silicon (arm64) and Intel (x86-64). Pull
requests and normal pushes build Python 3.12; manual runs build Python
3.9, 3.10, 3.11, 3.12, and 3.13. The wheels are installed and smoke-tested,
then all distributions pass metadata, package-content, and version checks.
This workflow currently produces Linux x86-64 wheels; other platforms can
build from the source distribution with a C++ compiler.
Each macOS architecture is built and smoke-tested on its matching native
runner. A manual release requires 15 wheels: five Python versions on each
of the three platforms. Other platforms can build from the source distribution
with a C++ compiler.

## Build without uploading

In GitHub Actions, select **Build wheels → Run workflow** and leave `publish`
as `none`. Download `release-sdist` and `release-wheels-linux-x86_64` from the
as `none`. Download `release-sdist` and `release-wheels-linux-x86_64`,
`release-wheels-macos-arm64`, and `release-wheels-macos-x86_64` from the
completed run. This works before allocating a new release version.

```bash
Expand Down
32 changes: 25 additions & 7 deletions .github/workflows/wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,31 @@ jobs:

wheels:
needs: plan
name: wheels (linux x86_64)
runs-on: ubuntu-latest
name: wheels (${{ matrix.platform }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- platform: linux-x86_64
os: ubuntu-latest
arch: x86_64
- platform: macos-arm64
os: macos-15
arch: arm64
- platform: macos-x86_64
os: macos-15-intel
arch: x86_64
steps:
- uses: actions/checkout@v7

- name: Build wheels
uses: pypa/cibuildwheel@v4.2.0
env:
CIBW_ARCHS_LINUX: x86_64
CIBW_SKIP: "*-musllinux_*"
CIBW_ARCHS_LINUX: ${{ matrix.arch }}
CIBW_ARCHS_MACOS: ${{ matrix.arch }}
MACOSX_DEPLOYMENT_TARGET: "11.0"
# Routine commits build one interpreter; all five are built only when
# asked for, so a normal push costs one build rather than five. Ask
# either with a manual run, or -- since workflow_dispatch is invisible
Expand All @@ -101,8 +117,8 @@ jobs:
CIBW_BUILD: >-
${{ (github.event_name == 'workflow_dispatch'
|| contains(github.event.head_commit.message, '[full-matrix]'))
&& 'cp39-manylinux_x86_64 cp310-manylinux_x86_64 cp311-manylinux_x86_64 cp312-manylinux_x86_64 cp313-manylinux_x86_64'
|| 'cp312-manylinux_x86_64' }}
&& 'cp39-* cp310-* cp311-* cp312-* cp313-*'
|| 'cp312-*' }}

# glibc 2.28 -- RHEL/Alma/Rocky 8+, Ubuntu 20.04+, Debian 10+. This is
# the floor the dependency set allows, not a compromise.
Expand Down Expand Up @@ -134,11 +150,13 @@ jobs:
# fallback -- it silently vanished from wheels before.
# 4. a console entry point resolves, since bin/ scripts became
# [project.scripts].
CIBW_TEST_REQUIRES: pytest
CIBW_TEST_COMMAND: >-
python -c "import pyace; print('version:', pyace.__version__); assert pyace.__version__ != 'unknown', 'distribution-name lookup failed'"
&& python -c "import pyace.sharmonics, pyace.coupling, pyace.basis, pyace.evaluator, pyace.catomicenvironment, pyace.calculator, pyace.grace_fs; print('7 pybind11 modules ok')"
&& python -c "from pyace.maxvol import c_maxvol; import numpy as np; i, c = c_maxvol(np.random.rand(64, 8), 1.05, 100, -1, 0); print('compiled maxvol ok, selected', len(i), 'rows')"
&& pace_info --help > /dev/null && echo 'entry points ok'
&& cd "{project}" && python -m pytest tests/test_PyACECalculator.py -q

# MANIFEST.in names the C++ sources so the sdist can build, and they sit
# inside the package tree -- so a lapse in include-package-data silently
Expand All @@ -158,7 +176,7 @@ jobs:

- uses: actions/upload-artifact@v7
with:
name: release-wheels-linux-x86_64
name: release-wheels-${{ matrix.platform }}
path: wheelhouse/*.whl

validate:
Expand All @@ -177,7 +195,7 @@ jobs:
- run: python -m pip install twine
- name: Validate distribution metadata and wheel coverage
env:
FULL_MATRIX: ${{ github.event_name == 'workflow_dispatch' }}
FULL_MATRIX: ${{ github.event_name == 'workflow_dispatch' || contains(github.event.head_commit.message, '[full-matrix]') }}
run: |
python -m twine check --strict dist/*
if [ "$FULL_MATRIX" = "true" ]; then
Expand Down
24 changes: 19 additions & 5 deletions tools/check_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,25 @@ def check_metadata(raw, filename):
if len(entries) != 1:
raise SystemExit("Expected one top-level PKG-INFO in the source distribution.")
check_metadata(archive.extractfile(entries[0]).read(), sdists[0])
if args.full_matrix:
expected = {"cp39", "cp310", "cp311", "cp312", "cp313"}
actual = [w.name.split("-")[2] for w in wheels]
if set(actual) != expected or len(actual) != len(expected):
raise SystemExit(f"Expected exactly one wheel per interpreter {sorted(expected)}, found {actual}")
interpreters = {"cp39", "cp310", "cp311", "cp312", "cp313"} if args.full_matrix else {"cp312"}
platforms = {"linux-x86_64", "macos-arm64", "macos-x86_64"}
actual = []
for wheel in wheels:
interpreter = wheel.name.split("-")[2]
tag = wheel.name.rsplit("-", 1)[1].removesuffix(".whl")
if tag.startswith("manylinux") and tag.endswith("_x86_64"):
platform = "linux-x86_64"
elif tag.startswith("macosx_") and tag.endswith("_arm64"):
platform = "macos-arm64"
elif tag.startswith("macosx_") and tag.endswith("_x86_64"):
platform = "macos-x86_64"
else:
raise SystemExit(f"Unexpected wheel platform: {wheel.name}")
actual.append((interpreter, platform))
expected = {(interpreter, platform) for interpreter in interpreters for platform in platforms}
if set(actual) != expected or len(actual) != len(expected):
missing = sorted(expected - set(actual))
raise SystemExit(f"Wheel coverage incomplete or duplicated; missing={missing}, found={actual}")
print(f"Validated {len(wheels)} wheels and one source distribution for {name} {version}.")


Expand Down
Loading