From 85fa65932f6b27f36ed4e7ed341d2c97d24bd0d6 Mon Sep 17 00:00:00 2001 From: Haozhe Jiang <162801044+provoke210@users.noreply.github.com> Date: Mon, 5 Oct 2026 04:18:11 +0800 Subject: [PATCH 1/2] fix(ci): avoid Metal wheel artifact collisions Build one py3-none Metal wheel and download its artifact by name so two macOS jobs cannot write different wheels to the same release path. Verify the wheel ZIP before artifact upload and before release. Assisted-by: OpenAI Codex Signed-off-by: Haozhe Jiang <162801044+provoke210@users.noreply.github.com> --- .github/workflows/build-wheels-metal.yaml | 28 +++++++++++----- scripts/verify-wheel-archives.py | 40 +++++++++++++++++++++++ 2 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 scripts/verify-wheel-archives.py diff --git a/.github/workflows/build-wheels-metal.yaml b/.github/workflows/build-wheels-metal.yaml index 892787ee1..797c91970 100644 --- a/.github/workflows/build-wheels-metal.yaml +++ b/.github/workflows/build-wheels-metal.yaml @@ -7,11 +7,10 @@ permissions: jobs: build_wheels: - name: Build wheels on ${{ matrix.os }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [macos-14, macos-15] + # Two runners produced the same py3-none wheel filename, which collided + # when their artifacts were downloaded into one release directory. + name: Build wheels on macos-14 + runs-on: macos-14 steps: - uses: actions/checkout@v6 @@ -38,14 +37,18 @@ jobs: CIBW_REPAIR_WHEEL_COMMAND: "" CIBW_ARCHS: "arm64" CIBW_ENVIRONMENT: CMAKE_ARGS="-DCMAKE_OSX_ARCHITECTURES=arm64 -DCMAKE_APPLE_SILICON_PROCESSOR=arm64 -DGGML_METAL=on -DCMAKE_CROSSCOMPILING=ON" - CIBW_BUILD: "cp39-* cp310-* cp311-* cp312-*" + # wheel.py-api = py3 produces one wheel for all supported Python versions. + CIBW_BUILD: "cp39-*" with: package-dir: . output-dir: wheelhouse2 + - name: Verify wheel archives + run: python3 scripts/verify-wheel-archives.py --expected-count 1 wheelhouse2/*.whl + - uses: actions/upload-artifact@v7 with: - name: wheels-mac_${{ matrix.os }} + name: wheels-mac_macos-14 path: ./wheelhouse2/*.whl release: @@ -54,11 +57,20 @@ jobs: runs-on: ubuntu-latest steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-python@v6 + with: + python-version: "3.12" + - uses: actions/download-artifact@v8 with: - merge-multiple: true + name: wheels-mac_macos-14 path: dist2 + - name: Verify release wheel archives + run: python3 scripts/verify-wheel-archives.py --expected-count 1 dist2/*.whl + - uses: softprops/action-gh-release@v3 with: files: dist2/* diff --git a/scripts/verify-wheel-archives.py b/scripts/verify-wheel-archives.py new file mode 100644 index 000000000..c1e63d073 --- /dev/null +++ b/scripts/verify-wheel-archives.py @@ -0,0 +1,40 @@ +"""Reject damaged wheel archives before uploading or publishing them.""" + +import argparse +import sys +import zipfile +from pathlib import Path + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--expected-count", type=int) + parser.add_argument("wheels", nargs="+") + args = parser.parse_args() + + if args.expected_count is not None and len(args.wheels) != args.expected_count: + print( + f"Expected {args.expected_count} wheel archive(s), found {len(args.wheels)}", + file=sys.stderr, + ) + return 1 + + failed = False + for name in args.wheels: + path = Path(name) + try: + with zipfile.ZipFile(path) as archive: + bad_file = archive.testzip() + if bad_file is not None: + raise zipfile.BadZipFile(f"bad CRC in {bad_file}") + except (OSError, zipfile.BadZipFile) as exc: + print(f"Invalid wheel archive {path}: {exc}", file=sys.stderr) + failed = True + else: + print(f"Valid wheel archive: {path}") + + return 1 if failed else 0 + + +if __name__ == "__main__": + sys.exit(main()) From 2c41ecedac4aab30c18253633a87787c89834022 Mon Sep 17 00:00:00 2001 From: Haozhe Jiang <162801044+provoke210@users.noreply.github.com> Date: Mon, 5 Oct 2026 04:19:06 +0800 Subject: [PATCH 2/2] docs: record Metal wheel release fix Assisted-by: OpenAI Codex Signed-off-by: Haozhe Jiang <162801044+provoke210@users.noreply.github.com> --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c34e2729..8da7f5da1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- fix(ci): prevent corrupted Metal wheel releases by @provoke210 in #2379 + ## [0.3.36] - feat: update llama.cpp to ggml-org/llama.cpp@0c1e57098