* feat: add macOS 15 support for M4 Mac compatibility - Add macos-15 CI builds for Python 3.9-3.13 - Update MACOSX_DEPLOYMENT_TARGET from 11.0/13.3 to 14.0 for broader compatibility - Addresses issue #34 with Mac M4 wheel compatibility 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: ensure wheels are compatible with older macOS versions - Set MACOSX_DEPLOYMENT_TARGET=11.0 for HNSW backend (broad compatibility) - Set MACOSX_DEPLOYMENT_TARGET=13.0 for DiskANN backend (required for LAPACK) - Add --require-target-macos-version to delocate-wheel commands - This fixes CI failures on macos-13 runners while maintaining M4 Mac support Fixes the issue where wheels built on macos-14 runners were incorrectly tagged as macosx_14_0, preventing installation on macos-13 runners. * fix: use macOS 13.3 for DiskANN backend as required by LAPACK DiskANN requires macOS 13.3+ for sgesdd_ LAPACK function, so we must use 13.3 as the deployment target, not 13.0. * fix: match deployment target with runner OS for library compatibility The issue is that Homebrew libraries on macOS 14 runners are built for macOS 14 and cannot be downgraded. We must use different deployment targets based on the runner OS: - macOS 13 runners: Can build for macOS 11.0 (HNSW) and 13.3 (DiskANN) - macOS 14 runners: Must build for macOS 14.0 (due to system libraries) This ensures delocate-wheel succeeds by matching the deployment target with the actual minimum version required by bundled libraries. * fix: add macOS 15 support to deployment target configuration The issue extends to macOS 15 runners where Homebrew libraries are built for macOS 15. We must handle all runner versions explicitly: - macOS 13 runners: Can build for macOS 11.0 (HNSW) and 13.3 (DiskANN) - macOS 14 runners: Must build for macOS 14.0 (system libraries) - macOS 15 runners: Must build for macOS 15.0 (system libraries) This ensures wheels are properly tagged for their actual minimum supported macOS version, matching the bundled libraries. * fix: correct macOS deployment targets based on Homebrew library requirements The key insight is that Homebrew libraries on each macOS version are compiled for that specific version: - macOS 13: Libraries require macOS 13.0 minimum - macOS 14: Libraries require macOS 14.0 minimum - macOS 15: Libraries require macOS 15.0 minimum We cannot build wheels for older macOS versions than what the bundled Homebrew libraries require. This means: - macOS 13 runners: Build for macOS 13.0+ (HNSW) and 13.3+ (DiskANN) - macOS 14 runners: Build for macOS 14.0+ - macOS 15 runners: Build for macOS 15.0+ This ensures delocate-wheel succeeds by matching deployment targets with the actual minimum versions required by system libraries. * fix: restore macOS 15 build matrix and correct test path - Add back macOS 15 configurations for Python 3.9-3.13 - Fix pytest path from test/ to tests/ (correct directory name) The macOS 15 support was accidentally missing from the matrix, and pytest was looking for the wrong directory name. --------- Co-authored-by: Claude <noreply@anthropic.com>
310 lines
11 KiB
YAML
310 lines
11 KiB
YAML
name: Reusable Build
|
|
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
ref:
|
|
description: 'Git ref to build'
|
|
required: false
|
|
type: string
|
|
default: ''
|
|
|
|
jobs:
|
|
lint:
|
|
name: Lint and Format Check
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ inputs.ref }}
|
|
|
|
- name: Setup Python
|
|
uses: actions/setup-python@v5
|
|
with:
|
|
python-version: '3.11'
|
|
|
|
- name: Install uv
|
|
uses: astral-sh/setup-uv@v4
|
|
|
|
- name: Install ruff
|
|
run: |
|
|
uv tool install ruff
|
|
|
|
- name: Run ruff check
|
|
run: |
|
|
ruff check .
|
|
|
|
- name: Run ruff format check
|
|
run: |
|
|
ruff format --check .
|
|
|
|
build:
|
|
needs: lint
|
|
name: Build ${{ matrix.os }} Python ${{ matrix.python }}
|
|
strategy:
|
|
matrix:
|
|
include:
|
|
- os: ubuntu-22.04
|
|
python: '3.9'
|
|
- os: ubuntu-22.04
|
|
python: '3.10'
|
|
- os: ubuntu-22.04
|
|
python: '3.11'
|
|
- os: ubuntu-22.04
|
|
python: '3.12'
|
|
- os: ubuntu-22.04
|
|
python: '3.13'
|
|
- os: macos-14
|
|
python: '3.9'
|
|
- os: macos-14
|
|
python: '3.10'
|
|
- os: macos-14
|
|
python: '3.11'
|
|
- os: macos-14
|
|
python: '3.12'
|
|
- os: macos-14
|
|
python: '3.13'
|
|
- os: macos-15
|
|
python: '3.9'
|
|
- os: macos-15
|
|
python: '3.10'
|
|
- os: macos-15
|
|
python: '3.11'
|
|
- os: macos-15
|
|
python: '3.12'
|
|
- os: macos-15
|
|
python: '3.13'
|
|
- os: macos-13
|
|
python: '3.9'
|
|
- os: macos-13
|
|
python: '3.10'
|
|
- os: macos-13
|
|
python: '3.11'
|
|
- os: macos-13
|
|
python: '3.12'
|
|
# Note: macos-13 + Python 3.13 excluded due to PyTorch compatibility
|
|
# (PyTorch 2.5+ supports Python 3.13 but not Intel Mac x86_64)
|
|
runs-on: ${{ matrix.os }}
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
ref: ${{ inputs.ref }}
|
|
submodules: recursive
|
|
|
|
- name: Setup Python
|
|
uses: actions/setup-python@v5
|
|
with:
|
|
python-version: ${{ matrix.python }}
|
|
|
|
- name: Install uv
|
|
uses: astral-sh/setup-uv@v4
|
|
|
|
- name: Install system dependencies (Ubuntu)
|
|
if: runner.os == 'Linux'
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y libomp-dev libboost-all-dev protobuf-compiler libzmq3-dev \
|
|
pkg-config libopenblas-dev patchelf libabsl-dev libaio-dev libprotobuf-dev
|
|
|
|
# Install Intel MKL for DiskANN
|
|
wget -q https://registrationcenter-download.intel.com/akdlm/IRC_NAS/79153e0f-74d7-45af-b8c2-258941adf58a/intel-onemkl-2025.0.0.940.sh
|
|
sudo sh intel-onemkl-2025.0.0.940.sh -a --components intel.oneapi.lin.mkl.devel --action install --eula accept -s
|
|
source /opt/intel/oneapi/setvars.sh
|
|
echo "MKLROOT=/opt/intel/oneapi/mkl/latest" >> $GITHUB_ENV
|
|
echo "LD_LIBRARY_PATH=/opt/intel/oneapi/mkl/latest/lib/intel64:$LD_LIBRARY_PATH" >> $GITHUB_ENV
|
|
|
|
- name: Install system dependencies (macOS)
|
|
if: runner.os == 'macOS'
|
|
run: |
|
|
# Don't install LLVM, use system clang for better compatibility
|
|
brew install libomp boost protobuf zeromq
|
|
|
|
- name: Install build dependencies
|
|
run: |
|
|
uv pip install --system scikit-build-core numpy swig Cython pybind11
|
|
if [[ "$RUNNER_OS" == "Linux" ]]; then
|
|
uv pip install --system auditwheel
|
|
else
|
|
uv pip install --system delocate
|
|
fi
|
|
|
|
- name: Set macOS environment variables
|
|
if: runner.os == 'macOS'
|
|
run: |
|
|
# Use brew --prefix to automatically detect Homebrew installation path
|
|
HOMEBREW_PREFIX=$(brew --prefix)
|
|
echo "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" >> $GITHUB_ENV
|
|
echo "OpenMP_ROOT=${HOMEBREW_PREFIX}/opt/libomp" >> $GITHUB_ENV
|
|
|
|
# Set CMAKE_PREFIX_PATH to let CMake find all packages automatically
|
|
echo "CMAKE_PREFIX_PATH=${HOMEBREW_PREFIX}" >> $GITHUB_ENV
|
|
|
|
# Set compiler flags for OpenMP (required for both backends)
|
|
echo "LDFLAGS=-L${HOMEBREW_PREFIX}/opt/libomp/lib" >> $GITHUB_ENV
|
|
echo "CPPFLAGS=-I${HOMEBREW_PREFIX}/opt/libomp/include" >> $GITHUB_ENV
|
|
|
|
- name: Build packages
|
|
run: |
|
|
# Build core (platform independent)
|
|
cd packages/leann-core
|
|
uv build
|
|
cd ../..
|
|
|
|
# Build HNSW backend
|
|
cd packages/leann-backend-hnsw
|
|
if [[ "${{ matrix.os }}" == macos-* ]]; then
|
|
# Use system clang for better compatibility
|
|
export CC=clang
|
|
export CXX=clang++
|
|
# Homebrew libraries on each macOS version require matching minimum version
|
|
if [[ "${{ matrix.os }}" == "macos-13" ]]; then
|
|
export MACOSX_DEPLOYMENT_TARGET=13.0
|
|
elif [[ "${{ matrix.os }}" == "macos-14" ]]; then
|
|
export MACOSX_DEPLOYMENT_TARGET=14.0
|
|
elif [[ "${{ matrix.os }}" == "macos-15" ]]; then
|
|
export MACOSX_DEPLOYMENT_TARGET=15.0
|
|
fi
|
|
uv build --wheel --python ${{ matrix.python }} --find-links ${GITHUB_WORKSPACE}/packages/leann-core/dist
|
|
else
|
|
uv build --wheel --python ${{ matrix.python }} --find-links ${GITHUB_WORKSPACE}/packages/leann-core/dist
|
|
fi
|
|
cd ../..
|
|
|
|
# Build DiskANN backend
|
|
cd packages/leann-backend-diskann
|
|
if [[ "${{ matrix.os }}" == macos-* ]]; then
|
|
# Use system clang for better compatibility
|
|
export CC=clang
|
|
export CXX=clang++
|
|
# DiskANN requires macOS 13.3+ for sgesdd_ LAPACK function
|
|
# But Homebrew libraries on each macOS version require matching minimum version
|
|
if [[ "${{ matrix.os }}" == "macos-13" ]]; then
|
|
export MACOSX_DEPLOYMENT_TARGET=13.3
|
|
elif [[ "${{ matrix.os }}" == "macos-14" ]]; then
|
|
export MACOSX_DEPLOYMENT_TARGET=14.0
|
|
elif [[ "${{ matrix.os }}" == "macos-15" ]]; then
|
|
export MACOSX_DEPLOYMENT_TARGET=15.0
|
|
fi
|
|
uv build --wheel --python ${{ matrix.python }} --find-links ${GITHUB_WORKSPACE}/packages/leann-core/dist
|
|
else
|
|
uv build --wheel --python ${{ matrix.python }} --find-links ${GITHUB_WORKSPACE}/packages/leann-core/dist
|
|
fi
|
|
cd ../..
|
|
|
|
# Build meta package (platform independent)
|
|
cd packages/leann
|
|
uv build
|
|
cd ../..
|
|
|
|
- name: Repair wheels (Linux)
|
|
if: runner.os == 'Linux'
|
|
run: |
|
|
# Repair HNSW wheel
|
|
cd packages/leann-backend-hnsw
|
|
if [ -d dist ]; then
|
|
auditwheel repair dist/*.whl -w dist_repaired
|
|
rm -rf dist
|
|
mv dist_repaired dist
|
|
fi
|
|
cd ../..
|
|
|
|
# Repair DiskANN wheel
|
|
cd packages/leann-backend-diskann
|
|
if [ -d dist ]; then
|
|
auditwheel repair dist/*.whl -w dist_repaired
|
|
rm -rf dist
|
|
mv dist_repaired dist
|
|
fi
|
|
cd ../..
|
|
|
|
- name: Repair wheels (macOS)
|
|
if: runner.os == 'macOS'
|
|
run: |
|
|
# Determine deployment target based on runner OS
|
|
# Must match the Homebrew libraries for each macOS version
|
|
if [[ "${{ matrix.os }}" == "macos-13" ]]; then
|
|
HNSW_TARGET="13.0"
|
|
DISKANN_TARGET="13.3"
|
|
elif [[ "${{ matrix.os }}" == "macos-14" ]]; then
|
|
HNSW_TARGET="14.0"
|
|
DISKANN_TARGET="14.0"
|
|
elif [[ "${{ matrix.os }}" == "macos-15" ]]; then
|
|
HNSW_TARGET="15.0"
|
|
DISKANN_TARGET="15.0"
|
|
fi
|
|
|
|
# Repair HNSW wheel
|
|
cd packages/leann-backend-hnsw
|
|
if [ -d dist ]; then
|
|
export MACOSX_DEPLOYMENT_TARGET=$HNSW_TARGET
|
|
delocate-wheel -w dist_repaired -v --require-target-macos-version $HNSW_TARGET dist/*.whl
|
|
rm -rf dist
|
|
mv dist_repaired dist
|
|
fi
|
|
cd ../..
|
|
|
|
# Repair DiskANN wheel
|
|
cd packages/leann-backend-diskann
|
|
if [ -d dist ]; then
|
|
export MACOSX_DEPLOYMENT_TARGET=$DISKANN_TARGET
|
|
delocate-wheel -w dist_repaired -v --require-target-macos-version $DISKANN_TARGET dist/*.whl
|
|
rm -rf dist
|
|
mv dist_repaired dist
|
|
fi
|
|
cd ../..
|
|
|
|
- name: List built packages
|
|
run: |
|
|
echo "📦 Built packages:"
|
|
find packages/*/dist -name "*.whl" -o -name "*.tar.gz" | sort
|
|
|
|
|
|
- name: Install built packages for testing
|
|
run: |
|
|
# Create a virtual environment with the correct Python version
|
|
uv venv --python ${{ matrix.python }}
|
|
source .venv/bin/activate || source .venv/Scripts/activate
|
|
|
|
# Install packages using --find-links to prioritize local builds
|
|
uv pip install --find-links packages/leann-core/dist --find-links packages/leann-backend-hnsw/dist --find-links packages/leann-backend-diskann/dist packages/leann-core/dist/*.whl || uv pip install --find-links packages/leann-core/dist packages/leann-core/dist/*.tar.gz
|
|
uv pip install --find-links packages/leann-core/dist packages/leann-backend-hnsw/dist/*.whl
|
|
uv pip install --find-links packages/leann-core/dist packages/leann-backend-diskann/dist/*.whl
|
|
uv pip install packages/leann/dist/*.whl || uv pip install packages/leann/dist/*.tar.gz
|
|
|
|
# Install test dependencies using extras
|
|
uv pip install -e ".[test]"
|
|
|
|
- name: Run tests with pytest
|
|
env:
|
|
CI: true # Mark as CI environment to skip memory-intensive tests
|
|
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
HF_HUB_DISABLE_SYMLINKS: 1
|
|
TOKENIZERS_PARALLELISM: false
|
|
PYTORCH_ENABLE_MPS_FALLBACK: 0 # Disable MPS on macOS CI to avoid memory issues
|
|
OMP_NUM_THREADS: 1 # Disable OpenMP parallelism to avoid libomp crashes
|
|
MKL_NUM_THREADS: 1 # Single thread for MKL operations
|
|
run: |
|
|
# Activate virtual environment
|
|
source .venv/bin/activate || source .venv/Scripts/activate
|
|
|
|
# Run tests
|
|
pytest -v tests/
|
|
|
|
- name: Run sanity checks (optional)
|
|
run: |
|
|
# Activate virtual environment
|
|
source .venv/bin/activate || source .venv/Scripts/activate
|
|
|
|
# Run distance function tests if available
|
|
if [ -f test/sanity_checks/test_distance_functions.py ]; then
|
|
echo "Running distance function sanity checks..."
|
|
python test/sanity_checks/test_distance_functions.py || echo "⚠️ Distance function test failed, continuing..."
|
|
fi
|
|
|
|
- name: Upload artifacts
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: packages-${{ matrix.os }}-py${{ matrix.python }}
|
|
path: packages/*/dist/
|