perf(bench): exclude resolution fixtures from incremental-benchmark sweep #3846
Workflow file for this run
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: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| lint: | |
| runs-on: ubuntu-latest | |
| name: Lint | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 22 | |
| - name: Install dependencies | |
| timeout-minutes: 20 | |
| shell: bash | |
| run: | | |
| for attempt in 1 2 3; do | |
| npm install && break | |
| if [ "$attempt" -lt 3 ]; then | |
| echo "::warning::npm install attempt $attempt failed, retrying in 15s..." | |
| sleep 15 | |
| else | |
| echo "::error::npm install failed after 3 attempts" | |
| exit 1 | |
| fi | |
| done | |
| - name: Run Biome | |
| run: npx @biomejs/biome check src/ tests/ | |
| native-host-build: | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| runs-on: ${{ matrix.os }} | |
| name: Native host build (${{ matrix.os }}) | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 22 | |
| - name: Setup Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Rust cache | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: crates/codegraph-core | |
| - name: Install napi-rs CLI | |
| timeout-minutes: 5 | |
| run: npm install -g @napi-rs/cli@3 | |
| - name: Build native addon | |
| working-directory: crates/codegraph-core | |
| run: napi build --release | |
| # Runs `cargo test`, which exercises the grammar-ABI regression test | |
| # added in #1054. Without this step a future tree-sitter / grammar | |
| # version drift would only surface as a runtime "files dropped" | |
| # warning during benchmarks, not as a test failure on the PR. | |
| - name: Run Rust tests | |
| working-directory: crates/codegraph-core | |
| run: cargo test --release | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: native-host-${{ matrix.os }} | |
| path: crates/codegraph-core/*.node | |
| if-no-files-found: error | |
| test: | |
| needs: native-host-build | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| node-version: [22] | |
| runs-on: ${{ matrix.os }} | |
| name: Test Node ${{ matrix.node-version }} (${{ matrix.os }}) | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: ${{ matrix.node-version }} | |
| - name: Install dependencies | |
| timeout-minutes: 20 | |
| shell: bash | |
| run: | | |
| for attempt in 1 2 3; do | |
| npm install && break | |
| if [ "$attempt" -lt 3 ]; then | |
| echo "::warning::npm install attempt $attempt failed, retrying in 15s..." | |
| sleep 15 | |
| else | |
| echo "::error::npm install failed after 3 attempts" | |
| exit 1 | |
| fi | |
| done | |
| - name: Download PR-built native addon | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: native-host-${{ matrix.os }} | |
| path: crates/codegraph-core | |
| - name: Install native addon over published binary | |
| shell: bash | |
| run: node scripts/ci-install-native.mjs | |
| - name: Run tests | |
| run: npm test | |
| typecheck: | |
| runs-on: ubuntu-latest | |
| name: TypeScript type check | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 22 | |
| - name: Install dependencies | |
| timeout-minutes: 20 | |
| shell: bash | |
| run: | | |
| for attempt in 1 2 3; do | |
| npm install && break | |
| if [ "$attempt" -lt 3 ]; then | |
| echo "::warning::npm install attempt $attempt failed, retrying in 15s..." | |
| sleep 15 | |
| else | |
| echo "::error::npm install failed after 3 attempts" | |
| exit 1 | |
| fi | |
| done | |
| - name: Type check | |
| run: npm run typecheck | |
| audit: | |
| runs-on: ubuntu-latest | |
| name: Security audit | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 22 | |
| - name: Install dependencies | |
| timeout-minutes: 20 | |
| run: npm ci | |
| - name: Audit production dependencies | |
| run: npm audit --omit=dev --audit-level=high | |
| verify-imports: | |
| runs-on: ubuntu-latest | |
| name: Verify dynamic imports | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 22 | |
| - name: Verify all dynamic imports resolve | |
| run: | | |
| STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") | |
| node $STRIP_FLAG scripts/verify-imports.ts | |
| parity: | |
| needs: native-host-build | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| runs-on: ${{ matrix.os }} | |
| name: Engine parity (${{ matrix.os }}) | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: 22 | |
| - name: Install dependencies | |
| timeout-minutes: 20 | |
| shell: bash | |
| run: | | |
| for attempt in 1 2 3; do | |
| npm install && break | |
| if [ "$attempt" -lt 3 ]; then | |
| echo "::warning::npm install attempt $attempt failed, retrying in 15s..." | |
| sleep 15 | |
| else | |
| echo "::error::npm install failed after 3 attempts" | |
| exit 1 | |
| fi | |
| done | |
| - name: Download PR-built native addon | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: native-host-${{ matrix.os }} | |
| path: crates/codegraph-core | |
| - name: Install native addon over published binary | |
| shell: bash | |
| run: node scripts/ci-install-native.mjs | |
| - name: Verify native addon is available | |
| shell: bash | |
| run: | | |
| node -e " | |
| const { createRequire } = require('node:module'); | |
| const r = createRequire(require.resolve('./package.json')); | |
| const os = require('os'); | |
| const fs = require('fs'); | |
| const plat = os.platform(); | |
| const arch = os.arch(); | |
| let libc = ''; | |
| if (plat === 'linux') { | |
| try { | |
| const files = fs.readdirSync('/lib'); | |
| libc = files.some(f => f.startsWith('ld-musl-') && f.endsWith('.so.1')) ? 'musl' : 'gnu'; | |
| } catch { libc = 'gnu'; } | |
| } | |
| const pkgs = { | |
| 'linux-x64-gnu': '@optave/codegraph-linux-x64-gnu', | |
| 'linux-x64-musl': '@optave/codegraph-linux-x64-musl', | |
| 'linux-arm64-gnu': '@optave/codegraph-linux-arm64-gnu', | |
| 'linux-arm64-musl': '@optave/codegraph-linux-arm64-musl', | |
| 'darwin-arm64': '@optave/codegraph-darwin-arm64', | |
| 'darwin-x64': '@optave/codegraph-darwin-x64', | |
| 'win32-x64': '@optave/codegraph-win32-x64-msvc', | |
| }; | |
| const key = libc ? plat + '-' + arch + '-' + libc : plat + '-' + arch; | |
| const pkg = pkgs[key]; | |
| if (!pkg) { console.error('No native package for ' + key); process.exit(1); } | |
| try { r(pkg); console.log('Native addon loaded: ' + pkg); } | |
| catch (e) { console.error('Failed to load ' + pkg + ': ' + e.message); process.exit(1); } | |
| " | |
| - name: Run parity tests | |
| shell: bash | |
| env: | |
| CODEGRAPH_PARITY: '1' | |
| run: npx vitest run tests/engines/ tests/integration/build-parity.test.ts --reporter=verbose | |
| rust-check: | |
| runs-on: ubuntu-latest | |
| name: Rust compile check | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Rust cache | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: crates/codegraph-core | |
| - name: Check compilation | |
| run: cargo check --workspace | |
| # ── Pre-publish benchmark gate ── | |
| # | |
| # Mirrors the gate in publish.yml so every PR catches regressions before | |
| # merge instead of at release time. Measures the PR-built native artifact | |
| # against the local source as version "dev", appends to the benchmark | |
| # history files (in-job only — never committed from CI), and runs the | |
| # regression guard against the most recent release baseline. | |
| # | |
| # Long-running but parallel with the rest of CI, so it does not extend | |
| # the critical path for fast-failing checks (lint/typecheck/test). | |
| pre-publish-benchmark: | |
| name: Pre-publish benchmark gate | |
| # Only run on PRs — push-to-main re-runs the same benchmarks the merged | |
| # PR already gated on, doubling CI minutes per landed change with no new | |
| # signal. Mirrors the `if: github.event_name != 'push'` skip in | |
| # publish.yml's equivalent gate. | |
| if: github.event_name == 'pull_request' | |
| needs: native-host-build | |
| runs-on: ubuntu-latest | |
| env: | |
| # Surface why detectNoChanges returns false on each fast-skip pre-flight | |
| # so we can pinpoint the cause of the ~2s incremental-rebuild regression | |
| # observed in CI but not locally (#1066). Remove once root cause is fixed. | |
| CODEGRAPH_FAST_SKIP_DIAG: "1" | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: "22" | |
| cache: "npm" | |
| - name: Setup Python (for resolution benchmark + tracer validation) | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.12" | |
| - name: Setup Go (for resolution benchmark + tracer validation) | |
| uses: actions/setup-go@v6 | |
| with: | |
| go-version: "stable" | |
| cache: false | |
| - name: Download PR-built native addon | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: native-host-ubuntu-latest | |
| path: crates/codegraph-core/ | |
| - name: Install dependencies | |
| timeout-minutes: 20 | |
| shell: bash | |
| run: | | |
| for attempt in 1 2 3; do | |
| npm install && break | |
| if [ "$attempt" -lt 3 ]; then | |
| echo "::warning::npm install attempt $attempt failed, retrying in 15s..." | |
| sleep 15 | |
| else | |
| echo "::error::npm install failed after 3 attempts" | |
| exit 1 | |
| fi | |
| done | |
| - name: Install native addon over published binary | |
| run: node scripts/ci-install-native.mjs | |
| # Build dist/ so benchmarks load the same compiled JS that ships to npm. | |
| # Historical baselines (v3.9.6 and earlier) were measured against dist/ | |
| # via the post-publish --npm path; running against src/ with --strip-types | |
| # changes the load path and introduces version-to-version noise unrelated | |
| # to the code under test (#1055). | |
| - name: Build TypeScript | |
| run: npm run build | |
| - name: Run build benchmark | |
| timeout-minutes: 20 | |
| run: | | |
| STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") | |
| node $STRIP_FLAG --import ./scripts/ts-resolve-loader.js scripts/benchmark.ts --version dev --dist > benchmark-result.json | |
| - name: Run resolution benchmark | |
| timeout-minutes: 20 | |
| run: | | |
| STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") | |
| node $STRIP_FLAG --import ./scripts/ts-resolve-loader.js scripts/resolution-benchmark.ts --version dev --dist > resolution-result.json | |
| - name: Gate on resolution thresholds | |
| timeout-minutes: 30 | |
| run: npx vitest run tests/benchmarks/resolution/resolution-benchmark.test.ts --reporter=verbose | |
| - name: Run tracer validation (same-file edge recall) | |
| timeout-minutes: 10 | |
| run: npx vitest run tests/benchmarks/resolution/tracer/tracer-validation.test.ts --reporter=verbose | |
| - name: Merge resolution into build result | |
| run: | | |
| node -e " | |
| const fs = require('fs'); | |
| const build = JSON.parse(fs.readFileSync('benchmark-result.json', 'utf8')); | |
| const resolution = JSON.parse(fs.readFileSync('resolution-result.json', 'utf8')); | |
| build.resolution = resolution; | |
| fs.writeFileSync('benchmark-result.json', JSON.stringify(build, null, 2)); | |
| " | |
| - name: Run query benchmark | |
| timeout-minutes: 20 | |
| run: | | |
| STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") | |
| node $STRIP_FLAG --import ./scripts/ts-resolve-loader.js scripts/query-benchmark.ts --version dev --dist > query-benchmark-result.json | |
| - name: Run incremental benchmark | |
| timeout-minutes: 20 | |
| run: | | |
| STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") | |
| node $STRIP_FLAG --import ./scripts/ts-resolve-loader.js scripts/incremental-benchmark.ts --version dev --dist > incremental-benchmark-result.json | |
| - name: Update build report | |
| run: | | |
| STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") | |
| node $STRIP_FLAG scripts/update-benchmark-report.ts benchmark-result.json | |
| - name: Update query report | |
| run: | | |
| STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") | |
| node $STRIP_FLAG scripts/update-query-report.ts query-benchmark-result.json | |
| - name: Update incremental report | |
| run: | | |
| STRIP_FLAG=$(node -e "const [M]=process.versions.node.split('.').map(Number); console.log(M>=23?'--strip-types':'--experimental-strip-types')") | |
| node $STRIP_FLAG scripts/update-incremental-report.ts incremental-benchmark-result.json | |
| - name: Regression guard | |
| env: | |
| RUN_REGRESSION_GUARD: "1" | |
| run: npm run test:regression-guard | |
| # Always upload raw JSON so a failed regression guard is debuggable | |
| # without re-running the full benchmark suite locally. | |
| - name: Upload benchmark JSON results | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: benchmark-results-json | |
| path: | | |
| benchmark-result.json | |
| query-benchmark-result.json | |
| incremental-benchmark-result.json | |
| if-no-files-found: warn | |
| ci-pipeline: | |
| if: always() | |
| needs: [lint, native-host-build, test, typecheck, audit, verify-imports, rust-check, parity, pre-publish-benchmark] | |
| runs-on: ubuntu-latest | |
| name: CI Testing Pipeline | |
| steps: | |
| - name: Check results | |
| run: | | |
| if [[ "${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}" == "true" ]]; then | |
| echo "One or more CI jobs failed or were cancelled." | |
| exit 1 | |
| fi |