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
3 changes: 3 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ concurrency:
group: documentation-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
build:
name: Build documentation
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ concurrency:
group: ${{ github.head_ref || github.sha }}-${{ github.workflow }}
cancel-in-progress: true

permissions:
contents: read

jobs:
changes:
uses: ./.github/workflows/changes.yml
Expand Down
106 changes: 69 additions & 37 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ on:
branches: ["main"]

concurrency:
group: release-${{ github.event.workflow_run.head_sha || github.sha }}
cancel-in-progress: false
group: release-${{ github.event.pull_request.number || github.event.workflow_run.head_sha || github.sha }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

permissions:
contents: read
Expand Down Expand Up @@ -55,90 +55,108 @@ jobs:
GH_TOKEN: ${{ github.token }}
run: python .github/scripts/prepare_release.py

linux-wheels:
name: Linux x86-64 wheels
linux-wheel-build:
name: Linux x86-64 ${{ matrix.abi }} wheel
needs: [prepare, changes]
if: >-
!cancelled() && needs.prepare.result == 'success' &&
needs.prepare.outputs.build == 'true' &&
(needs.changes.result != 'success' || needs.changes.outputs.wheels == 'true')
runs-on: ubuntu-latest
timeout-minutes: 90

strategy:
fail-fast: false
matrix:
abi: ${{ fromJSON(github.event_name == 'pull_request' && '["cp311","cp314"]' || '["cp311","cp312","cp313","cp314"]') }}
steps:
- uses: actions/checkout@v7
with:
ref: ${{ needs.prepare.outputs.sha }}

- uses: actions/setup-python@v7
with:
python-version: "3.14"

- name: Build and test PR wheel
if: github.event_name == 'pull_request'
- name: Build and test wheel
uses: pypa/cibuildwheel@v4.2.0
env:
CIBW_BUILD: cp311-manylinux_x86_64
CIBW_BUILD: ${{ matrix.abi }}-manylinux_x86_64
with:
output-dir: wheelhouse

- name: Build and test release wheels
if: github.event_name != 'pull_request'
uses: pypa/cibuildwheel@v4.2.0
with:
output-dir: wheelhouse

- name: Store wheel artifacts
- name: Store wheel artifact
uses: actions/upload-artifact@v7
with:
name: linux-x86_64-wheels
name: linux-${{ matrix.abi }}-wheels
path: wheelhouse/*.whl
if-no-files-found: error

windows-wheels:
name: Windows x86-64 wheels
linux-wheels:
# Preserve the required check name and include every selected ABI.
name: Linux x86-64 wheels
needs: linux-wheel-build
if: ${{ always() && !cancelled() && needs.linux-wheel-build.result != 'skipped' }}
runs-on: ubuntu-latest
steps:
- name: Require all selected wheels
env:
BUILD_RESULT: ${{ needs.linux-wheel-build.result }}
run: test "$BUILD_RESULT" = success

windows-wheel-build:
name: Windows x86-64 ${{ matrix.abi }} wheel
needs: [prepare, changes]
if: >-
!cancelled() && needs.prepare.result == 'success' &&
needs.prepare.outputs.build == 'true' &&
(needs.changes.result != 'success' || needs.changes.outputs.wheels == 'true')
runs-on: windows-2025-vs2026
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
abi: ${{ fromJSON(github.event_name == 'pull_request' && '["cp311","cp314"]' || '["cp311","cp312","cp313","cp314"]') }}
steps:
- uses: actions/checkout@v7
with:
ref: ${{ needs.prepare.outputs.sha }}
- uses: actions/setup-python@v7
with:
python-version: "3.14"
- name: Build and test PR wheel
if: github.event_name == 'pull_request'
- name: Build and test wheel
uses: pypa/cibuildwheel@v4.2.0
env:
CIBW_BUILD: cp311-win_amd64
CIBW_BUILD: ${{ matrix.abi }}-win_amd64
with:
output-dir: wheelhouse
- name: Build and test release wheels
if: github.event_name != 'pull_request'
uses: pypa/cibuildwheel@v4.2.0
with:
output-dir: wheelhouse
- name: Store wheel artifacts
- name: Store wheel artifact
uses: actions/upload-artifact@v7
with:
name: windows-x86_64-wheels
name: windows-${{ matrix.abi }}-wheels
path: wheelhouse/*.whl
if-no-files-found: error

macos-wheels:
name: macOS arm64 wheels (CPython 3.11–3.14)
windows-wheels:
name: Windows x86-64 wheels
needs: windows-wheel-build
if: ${{ always() && !cancelled() && needs.windows-wheel-build.result != 'skipped' }}
runs-on: ubuntu-latest
steps:
- name: Require all selected wheels
env:
BUILD_RESULT: ${{ needs.windows-wheel-build.result }}
run: test "$BUILD_RESULT" = success

macos-wheel-build:
name: macOS arm64 ${{ matrix.abi }} wheel
needs: [prepare, changes]
if: >-
!cancelled() && needs.prepare.result == 'success' &&
needs.prepare.outputs.build == 'true' &&
(needs.changes.result != 'success' || needs.changes.outputs.wheels == 'true')
runs-on: macos-14
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
abi: ${{ fromJSON(github.event_name == 'pull_request' && '["cp311","cp314"]' || '["cp311","cp312","cp313","cp314"]') }}
steps:
- uses: actions/checkout@v7
with:
Expand All @@ -148,17 +166,31 @@ jobs:
python-version: "3.14"
- name: Verify native Apple Silicon runner
run: test "$(uname -m)" = arm64
# All four ABIs are built, repaired, installed and tested on PRs as well.
- name: Build and test wheels with bundled OpenMP
- name: Build and test wheel with bundled OpenMP
uses: pypa/cibuildwheel@v4.2.0
env:
CIBW_BUILD: ${{ matrix.abi }}-macosx_arm64
with:
output-dir: wheelhouse
- uses: actions/upload-artifact@v7
- name: Store wheel artifact
uses: actions/upload-artifact@v7
with:
name: macos-arm64-wheels
name: macos-${{ matrix.abi }}-wheels
path: wheelhouse/*.whl
if-no-files-found: error

macos-wheels:
# Preserve the protected check name; PRs test the oldest and newest ABI.
name: macOS arm64 wheels (CPython 3.11–3.14)
needs: macos-wheel-build
if: ${{ always() && !cancelled() && needs.macos-wheel-build.result != 'skipped' }}
runs-on: ubuntu-latest
steps:
- name: Require all selected wheels
env:
BUILD_RESULT: ${{ needs.macos-wheel-build.result }}
run: test "$BUILD_RESULT" = success

release-tag:
name: Create version tag
needs: [prepare, linux-wheels, windows-wheels, macos-wheels]
Expand Down
32 changes: 28 additions & 4 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ on:
concurrency:
group: ${{ github.head_ref || github.sha }}-${{ github.workflow }}
cancel-in-progress: true
permissions:
contents: read
jobs:
changes:
uses: ./.github/workflows/changes.yml
Expand All @@ -26,12 +28,19 @@ jobs:
- name: Check formatting
run: ./scripts/check-format.sh

clang-tidy:
clang-tidy-shard:
needs: changes
if: ${{ !cancelled() && (needs.changes.result != 'success' || needs.changes.outputs.cpp_tidy == 'true') }}
name: C++ static analysis
name: C++ static analysis shard ${{ matrix.shard }}
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
shard: [0, 1]
env:
TIDY_SHARD_INDEX: ${{ matrix.shard }}
TIDY_SHARD_COUNT: "2"
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
Expand All @@ -55,6 +64,17 @@ jobs:
- name: Run clang-tidy
run: ./scripts/check-tidy.sh build-tidy

clang-tidy:
name: C++ static analysis
needs: clang-tidy-shard
if: ${{ always() && !cancelled() && needs.clang-tidy-shard.result != 'skipped' }}
runs-on: ubuntu-latest
steps:
- name: Require both shards
env:
BUILD_RESULT: ${{ needs.clang-tidy-shard.result }}
run: test "$BUILD_RESULT" = success

shellcheck:
needs: changes
if: ${{ !cancelled() && (needs.changes.result != 'success' || needs.changes.outputs.shell == 'true') }}
Expand Down Expand Up @@ -141,15 +161,19 @@ jobs:
- uses: actions/checkout@v7
- name: Install dependencies
run: sudo apt-get update -qq && sudo apt-get install -y cmake ninja-build
- name: Build and install RaBitQ-Library
- name: Build RaBitQ-Library with portable tests
run: |
cmake -S . -B build-install -G Ninja \
-DRABITQ_BUILD_SAMPLES=OFF \
-DRABITQ_BUILD_TESTS=ON \
-DRABITQ_ENABLE_NATIVE_OPTIMIZATION=OFF \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="$PWD/rabitqlib-install"
cmake --build build-install
cmake --install build-install
- name: Test portable Release build
run: ctest --test-dir build-install --output-on-failure
- name: Install RaBitQ-Library
run: cmake --install build-install
- name: Build downstream consumer
run: |
cmake -S tests/consumer -B build-consumer -G Ninja \
Expand Down
39 changes: 37 additions & 2 deletions scripts/check-tidy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,37 @@ for header in "${first_party_headers[@]}"; do
done
header_filter+=")$"

source_filter="^$repo_root/(src|python_bindings|sample|tests)/"
if [[ -n "${TIDY_SHARD_INDEX:-}" || -n "${TIDY_SHARD_COUNT:-}" ]]; then
source_filter="$(python - "$build_dir/compile_commands.json" "$repo_root" \
"${TIDY_SHARD_INDEX:-}" "${TIDY_SHARD_COUNT:-}" <<'PYCODE'
import json
import re
import sys
from pathlib import Path

database, root, index, count = sys.argv[1:]
root = Path(root).resolve()
index, count = int(index), int(count)
if count < 1 or not 0 <= index < count:
raise SystemExit("error: invalid clang-tidy shard index or count")

entries = json.loads(Path(database).read_text())
sources = set()
for entry in entries:
path = (Path(entry["directory"]) / entry["file"]).resolve()
if path.is_relative_to(root) and path.relative_to(root).parts[0] in {
"src", "python_bindings", "sample", "tests"
}:
sources.add(path)
shard = sorted(sources)[index::count]
if not shard:
raise SystemExit("error: clang-tidy shard has no translation units")
print("^(" + "|".join(re.escape(str(path)) for path in shard) + ")$")
PYCODE
)"
fi

tidy_output="$(mktemp)"
if [[ -z "$tidy_output" || ! -f "$tidy_output" ]]; then
echo "error: could not create a temporary clang-tidy output file" >&2
Expand All @@ -87,10 +118,14 @@ if ! "$tidy_runner" \
-p "$build_dir" \
-header-filter="^$header_filter" \
"${extra_args[@]}" \
"^$repo_root/(src|python_bindings|sample|tests)/" \
"$source_filter" \
>"$tidy_output" 2>&1; then
cat "$tidy_output" >&2
exit 1
fi

echo "clang-tidy passed for all configured first-party translation units"
if [[ -n "${TIDY_SHARD_INDEX:-}" ]]; then
echo "clang-tidy passed for shard $TIDY_SHARD_INDEX of $TIDY_SHARD_COUNT"
else
echo "clang-tidy passed for all configured first-party translation units"
fi
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ FetchContent_Declare(
GIT_TAG v1.14.0
)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
set(INSTALL_GTEST OFF CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)

# Automatically register all tests under unit and <other folders> folders
Expand Down
Loading