Skip to content

ci: fix python job by installing wheel instead of maturin develop #336

ci: fix python job by installing wheel instead of maturin develop

ci: fix python job by installing wheel instead of maturin develop #336

Workflow file for this run

name: CI
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
# Cancel in-flight runs of the same workflow when a new commit is pushed.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: "0" # CI builds don't benefit from incremental; smaller cache.
CARGO_PROFILE_DEV_DEBUG: "0" # Reduces target/ size, improves cache hit rate.
RUSTFLAGS: -Dwarnings
# Testing strategy grounded in real-world ANN library issues:
# - hnswlib #635: M vs Mcurmax bug in neighbor selection
# - hnswlib #626: Use-after-free in deletion
# - hnswlib #592: Vector not normalized for cosine distance
# - hnswlib #608: Issues after deleting vectors
# - faiss #4295: Integer overflow on large datasets
# - usearch #405: Quantization issues with i8 + inner product
# See docs/TESTING.md for full details
jobs:
# Cheap fan-in gate: fmt + clippy + basic check on ubuntu.
# Failure here aborts every downstream matrix job (saves ~5-10 minutes per
# PR on a fmt typo). Pattern from tokio-rs/tokio.
basics:
name: Basics (fmt + clippy + check)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
- name: Check formatting
run: cargo fmt --check
- name: Clippy (hnsw)
run: cargo clippy --no-default-features --features hnsw --all-targets -- -D warnings
- name: Clippy (all features)
run: cargo clippy --all-features --all-targets -- -D warnings
# Primary test suite (x86) -- nextest for parallel test-binary execution.
test:
name: Test (ubuntu)
runs-on: ubuntu-latest
needs: basics
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@v2
with:
tool: cargo-nextest
- name: Build
run: cargo build --no-default-features --features hnsw
- name: Test (unit + integration)
run: cargo nextest run --no-default-features --features hnsw
- name: Test (sq4)
run: cargo nextest run --no-default-features --features hnsw,sq4
- name: Test (sq8)
run: cargo nextest run --no-default-features --features hnsw,sq8
- name: Test (lemur)
run: cargo nextest run --no-default-features --features lemur
- name: Test (all features, lib only)
run: cargo nextest run --lib --all-features
env:
RUSTFLAGS: ""
# nextest does not support doctests yet (nextest-rs/nextest#16).
- name: Doc tests
run: cargo test --doc --no-default-features --features hnsw
- name: Property tests
run: cargo nextest run --no-default-features --features hnsw -E 'test(property_)'
# Semver-checks only on PRs.
semver:
name: Semver check
runs-on: ubuntu-latest
needs: basics
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@v2
with:
tool: cargo-semver-checks
- run: cargo semver-checks --default-features
# ARM native testing (M1/M2 - tests NEON code paths)
test-arm:
name: Test (macos / ARM)
runs-on: macos-latest
needs: basics
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@v2
with:
tool: cargo-nextest
- name: Build (ARM)
run: cargo build --no-default-features --features hnsw
- name: Test (ARM)
run: cargo nextest run --no-default-features --features hnsw
# MSRV check
msrv:
name: MSRV (1.89)
runs-on: ubuntu-latest
needs: basics
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@1.89.0
- uses: Swatinem/rust-cache@v2
- name: Check MSRV
run: |
rm -f Cargo.lock # v4 lock requires Cargo 1.83+
cargo check --no-default-features --features hnsw
# Feature-matrix compilation (catches cfg + optional-dep drift)
feature-matrix:
name: Feature matrix
runs-on: ubuntu-latest
needs: basics
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@v2
with:
tool: cargo-hack
- name: Compile each feature (no dev-deps)
run: cargo hack check --each-feature --no-dev-deps --exclude-features persistence
# Cross-compilation check for different targets
cross-compile:
name: Cross-compile (${{ matrix.target }})
runs-on: ubuntu-latest
needs: basics
strategy:
matrix:
target:
- x86_64-unknown-linux-gnu
- aarch64-unknown-linux-gnu
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
- name: Check ${{ matrix.target }}
run: cargo check --target ${{ matrix.target }} --no-default-features --features hnsw
# Recall regression detection -- catches hnswlib #635-class issues.
recall-regression:
name: Recall regression
runs-on: ubuntu-latest
needs: basics
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Build release
run: cargo build --release --no-default-features --features hnsw
- name: Run recall benchmark
run: |
cargo run --release --example 02_measure_recall --no-default-features --features hnsw 2>&1 | tee recall_output.txt
# Fail if recall@10 drops below 80% at ef=100
# This catches issues like hnswlib #635 (wrong M parameter)
awk '/^[[:space:]]*100[[:space:]]/ {
gsub(/%/, "", $2); found=1
if ($2 + 0 < 80.0) { print "FAIL: ef=100 recall " $2 "% < 80%"; exit 1 }
}
END { if (!found) { print "FAIL: ef=100 row missing from output"; exit 1 } }' recall_output.txt
# Regression tests for known bugs
regression:
name: Regression (known bugs)
runs-on: ubuntu-latest
needs: basics
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@v2
with:
tool: cargo-nextest
- name: Run regression tests
run: cargo nextest run --test regression_known_bugs --no-default-features --features hnsw
# Python bindings: build wheel, lint, type-check, stubtest, pytest.
# Stubtest catches drift between the hand-written `_core.pyi` and the
# compiled module the moment a Rust signature changes (voyager pattern).
python:
name: Python (pyvicinity)
runs-on: ubuntu-latest
needs: basics
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install build + test deps
run: |
python -m pip install --upgrade pip
python -m pip install maturin pytest mypy ruff numpy
# `maturin develop` requires an active virtualenv that GH Actions
# runners don't have. Build a wheel and install it instead -- same
# extension module, no venv plumbing.
- name: Build wheel and install
run: |
maturin build --release --features hnsw,python --out target/wheels
python -m pip install --force-reinstall target/wheels/pyvicinity-*.whl
- name: Ruff
run: python -m ruff check pyvicinity tests/test_python.py
- name: mypy --strict
run: python -m mypy --strict pyvicinity
- name: Stubtest (.pyi vs compiled module)
run: python -m mypy.stubtest pyvicinity._core
- name: Pytest
run: python -m pytest tests/test_python.py -v
# Documentation build
docs:
name: Docs
runs-on: ubuntu-latest
needs: basics
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Build docs
run: cargo doc --no-default-features --features hnsw --no-deps
env:
RUSTDOCFLAGS: -Dwarnings