Skip to content

build(docker): cross-compile the musl build instead of emulating it #841

build(docker): cross-compile the musl build instead of emulating it

build(docker): cross-compile the musl build instead of emulating it #841

Workflow file for this run

name: Rust tests
on:
push:
branches: [master, main, develop]
pull_request:
branches: ["**"]
env:
CARGO_TERM_COLOR: always
# phase34 / #320 — CI starts cold, so incremental compilation
# only adds artifacts and slows the build (Rust perf-team:
# https://kobzol.github.io/rust/rustc/2025/05/20/disable-debuginfo-to-improve-rust-compile-times.html).
CARGO_INCREMENTAL: "0"
jobs:
rust-tests:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
steps:
- name: Install minimal stable
uses: dtolnay/rust-toolchain@stable
- uses: actions/checkout@v7
- uses: Swatinem/rust-cache@v2
- name: Install mold (optional, speeds up linking)
uses: rui314/setup-mold@v1
continue-on-error: true
- name: Enable mold on Linux
run: |
if [[ "${{ matrix.os }}" == "ubuntu-latest" ]]; then
if [ -f "/usr/local/bin/mold" ]; then
mkdir -p .cargo
echo "[target.x86_64-unknown-linux-gnu]" >> .cargo/config.toml
echo "linker = \"clang\"" >> .cargo/config.toml
echo "rustflags = [\"-C\", \"link-arg=-fuse-ld=/usr/local/bin/mold\"]" >> .cargo/config.toml
else
echo "mold not available, using default linker"
fi
fi
shell: bash
- name: Install protoc (Protocol Buffers compiler)
# GitHub-releases-backed install instead of the Chocolatey mirror, which
# returns flaky 503s on the Windows runner (choco was down for the whole
# retry window in run 29297062082).
uses: arduino/setup-protoc@v3
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Install system OpenSSL (ubuntu)
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y libssl-dev pkg-config
shell: bash
- name: Override leaked Windows OPENSSL_DIR (ubuntu matrix only)
if: matrix.os == 'ubuntu-latest'
# The ubuntu-latest GH-hosted image currently surfaces
# `OPENSSL_DIR=C:/Program Files/OpenSSL-Win64` to Linux jobs,
# which makes openssl-sys panic because that path doesn't
# exist on a Linux runner. Empty string also panics. Point at
# the canonical ubuntu libssl-dev install location instead.
run: |
echo "OPENSSL_DIR=/usr" >> $GITHUB_ENV
echo "OPENSSL_INCLUDE_DIR=/usr/include" >> $GITHUB_ENV
echo "OPENSSL_LIB_DIR=/usr/lib/x86_64-linux-gnu" >> $GITHUB_ENV
echo "PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig" >> $GITHUB_ENV
shell: bash
- name: Install nextest
uses: taiki-e/install-action@nextest
- name: Setup Node.js for dashboard build
uses: actions/setup-node@v7
with:
node-version: '20'
- name: Install pnpm
run: npm install -g pnpm@10 # pinned: pnpm 11 ignores pnpm.overrides (see sdk-publish-typescript.yml)
- name: Build dashboard (required for rust-embed)
run: |
cd dashboard
pnpm install --frozen-lockfile
pnpm build
shell: bash
- name: Build
run: cargo build --tests --workspace
env:
# Ensure s2s-tests feature is NOT enabled (explicitly disable it)
CARGO_BUILD_RUSTFLAGS: ""
- name: Build vectorizer server binary
# Needed by the SDK integration tests below. Built separately from
# `--tests` so both the test binaries and the server bin land in
# `target/debug/` together and the step above stays focused.
run: cargo build --bin vectorizer
- name: Start vectorizer server (SDK integration tests)
# Integration tests hit the default endpoint `http://localhost:15002`.
# We bind to 127.0.0.1 to satisfy the bind-time auth check and flip
# auth off via env var so the client can reach gated endpoints
# without credentials. The server is killed in an always-run
# cleanup step further below.
#
# Windows detail: the test step below calls `cargo nextest run
# --all-targets`, which may try to re-link `target/debug/vectorizer.exe`.
# Windows refuses to delete an executable that is currently mapped by
# a running process (`Access is denied. (os error 5)`), so we run the
# server from a sidecar copy (`vectorizer-running(.exe)`) and leave
# the original artifact free for cargo to replace.
env:
RUNNER_OS_NAME: ${{ matrix.os }}
run: |
if [[ "$RUNNER_OS_NAME" == "windows-latest" ]]; then
SRC="./target/debug/vectorizer.exe"
BIN="./target/debug/vectorizer-running.exe"
else
SRC="./target/debug/vectorizer"
BIN="./target/debug/vectorizer-running"
fi
cp "$SRC" "$BIN"
VECTORIZER_AUTH_ENABLED=false "$BIN" \
--host 127.0.0.1 --port 15002 \
> vectorizer-test.log 2>&1 &
echo $! > vectorizer.pid
for i in $(seq 1 30); do
if curl -fsS http://127.0.0.1:15002/health >/dev/null 2>&1; then
echo "Server ready after ${i}s (pid=$(cat vectorizer.pid))"
exit 0
fi
sleep 1
done
echo "Server failed to start within 30s; last 50 lines of log:"
tail -50 vectorizer-test.log || true
exit 1
shell: bash
- name: Run unit and integration tests
# Scope: --lib --bins --tests (unit + integration tests). We must NOT
# use --all-targets here: that also enumerates every `[[bench]]` and the
# SDK `examples/*` (drift_smoke, test_master_replica, comprehensive_test,
# …), which are custom-`main` binaries that connect to / spin up servers.
# nextest executes them during its run/list phase and one blocks on a
# socket, hanging the whole job until the 45-minute timeout (run
# 29297062082 sat idle for 45m after "Finished" with zero test output).
# Excluding them also cuts build+run time (benches pull candle/criterion).
# Benches and examples stay compile-checked by rust-lint.yml's
# `clippy --workspace --all-targets`.
#
# Note: the heavy end-to-end suite at
# `crates/vectorizer/tests/all_tests.rs` is gated behind the
# `integration-tests` feature (declared in
# `crates/vectorizer/Cargo.toml` via `required-features`) and is
# intentionally NOT run in CI — it needs a fully indexed live
# server and historically blows past the job timeout. Run it
# locally with
# `cargo nextest run -p vectorizer --features integration-tests --test all_tests`.
timeout-minutes: 45
run: cargo nextest run --workspace --lib --bins --tests --test-threads 4
env:
# Set test timeouts for integration tests
NEXTEST_TIMEOUT: 300s
- name: Run doc tests
run: cargo test --workspace --doc
- name: Stop vectorizer server
# Runs regardless of prior step outcome so the runner doesn't
# leave the server hanging on a failed test step.
if: always()
run: |
if [ -f vectorizer.pid ]; then
kill "$(cat vectorizer.pid)" 2>/dev/null || true
rm -f vectorizer.pid
fi
if [ -f vectorizer-test.log ]; then
echo "=== vectorizer server log (last 200 lines) ==="
tail -200 vectorizer-test.log
fi
shell: bash
- name: Upload test report
uses: actions/upload-artifact@v4
if: always()
with:
name: junit-${{ matrix.os }}.xml
path: target/nextest/default/junit.xml
if-no-files-found: ignore
# Monitor test stability over time
test-stability:
runs-on: ubuntu-latest
needs: rust-tests
if: always()
permissions:
contents: write
steps:
- uses: actions/checkout@v7
- name: Download test reports
uses: actions/download-artifact@v5
continue-on-error: true
with:
pattern: junit-*.xml
merge-multiple: true
- name: Track test stability
run: |
mkdir -p .test-history
TIMESTAMP=$(date +%s)
# Count test results
if [ -f junit-ubuntu-latest.xml ]; then
TOTAL=$(grep -o 'tests="[0-9]*"' junit-ubuntu-latest.xml | head -1 | grep -o '[0-9]*' || echo "0")
FAILURES=$(grep -o 'failures="[0-9]*"' junit-ubuntu-latest.xml | head -1 | grep -o '[0-9]*' || echo "0")
ERRORS=$(grep -o 'errors="[0-9]*"' junit-ubuntu-latest.xml | head -1 | grep -o '[0-9]*' || echo "0")
echo "$TIMESTAMP,$TOTAL,$FAILURES,$ERRORS" >> .test-history/stability.csv
# Keep last 100 runs only
tail -n 100 .test-history/stability.csv > .test-history/stability.csv.tmp
mv .test-history/stability.csv.tmp .test-history/stability.csv
# Calculate pass rate
PASSED=$((TOTAL - FAILURES - ERRORS))
PASS_RATE=$((PASSED * 100 / TOTAL))
echo "### Test Stability Report 📊" >> $GITHUB_STEP_SUMMARY
echo "- Total Tests: $TOTAL" >> $GITHUB_STEP_SUMMARY
echo "- Passed: $PASSED" >> $GITHUB_STEP_SUMMARY
echo "- Failed: $FAILURES" >> $GITHUB_STEP_SUMMARY
echo "- Errors: $ERRORS" >> $GITHUB_STEP_SUMMARY
echo "- Pass Rate: ${PASS_RATE}%" >> $GITHUB_STEP_SUMMARY
# Warning if pass rate < 95%
if [ $PASS_RATE -lt 95 ]; then
echo "::warning::Test pass rate below 95%: ${PASS_RATE}%"
fi
else
echo "::warning::No test report found for stability tracking"
fi
- name: Commit test history
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add .test-history/
git diff --staged --quiet || git commit -m "chore: update test stability history [skip ci]"
git push
continue-on-error: true
# Process test results and create issues for flaky tests
process-results:
runs-on: ubuntu-latest
needs: rust-tests
if: always()
permissions:
contents: read
issues: write
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
steps:
- name: Download test report
uses: actions/download-artifact@v5
continue-on-error: true
with:
name: junit-${{ matrix.os }}.xml
- name: Process test report
id: process-test-report
continue-on-error: true
run: |
if [ ! -f junit.xml ]; then
echo "No test report found, skipping"
echo "has_flaky_tests=false" >> $GITHUB_OUTPUT
exit 0
fi
pip install yq
xq '.. | select(type == "object") | select(has("flakyFailure"))' junit.xml > flaky_tests.json || echo "{}" > flaky_tests.json
echo has_flaky_tests=$(jq '. | has("flakyFailure")' flaky_tests.json) >> $GITHUB_OUTPUT
- name: Get flaky test details
id: get-flaky-tests
if: ${{ steps.process-test-report.outputs.has_flaky_tests == 'true' }}
run: |
echo "Flaky tests found"
echo test=$(jq '.["@name"]' flaky_tests.json -r ) >> $GITHUB_OUTPUT
delimiter="###r###"
echo "content<<$delimiter" >> $GITHUB_OUTPUT
echo "$(jq '[.flakyFailure] | flatten | .[0]["system-err"]' flaky_tests.json -r)" >> $GITHUB_OUTPUT
echo $delimiter >> $GITHUB_OUTPUT
- name: Report flaky tests
if: ${{ steps.process-test-report.outputs.has_flaky_tests == 'true' }}
run: |
echo "::warning::Flaky test detected: ${{ steps.get-flaky-tests.outputs.test }}"
echo "OS: ${{ matrix.os }}"
echo "Details: ${{ steps.get-flaky-tests.outputs.content }}"