build(docker): cross-compile the musl build instead of emulating it #841
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 }}" |