docs: record lessons 91 and 92 and two rules #772
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: | |
| # Manual re-run, matching every other workflow in this directory. The | |
| # `coverage` job is the reason: Codecov compares a PR against the most | |
| # recent *ancestor that has a report*, so a push to main that produces | |
| # no run leaves every later PR silently baselined against an older | |
| # commit — the diff table then attributes those intervening commits' | |
| # coverage to whichever PR is open. That happened at d818e306, which | |
| # carries zero check-runs, and left PR #1223 compared against a base | |
| # eight commits behind. Without a dispatch trigger the only remedy is | |
| # to land another commit on main. | |
| workflow_dispatch: | |
| # Default to read-only; jobs that need more escalate explicitly. | |
| permissions: | |
| contents: read | |
| # Cancel in-progress runs for the same ref (e.g. PR pushes) but not on | |
| # main or tag builds — those should always complete. | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: ${{ github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/') }} | |
| # All `uses:` below are pinned to commit SHAs to eliminate supply-chain | |
| # risk from action-release spoofing. SHAs sourced from the host-identity | |
| # reference workflow for parity; bump via Dependabot. | |
| env: | |
| CARGO_TERM_COLOR: always | |
| CARGO_INCREMENTAL: 0 | |
| CARGO_NET_RETRY: 10 | |
| RUSTUP_MAX_RETRIES: 10 | |
| RUSTFLAGS: "-D warnings" | |
| RUSTDOCFLAGS: "-D warnings" | |
| jobs: | |
| # Path-filter detection. Outputs `py: true` when the change set | |
| # touches the bindings crate, the workflow file itself, mise.toml | |
| # (toolchain pins), or the root Cargo.toml / Cargo.lock (PyO3 / | |
| # workspace dep bumps must re-run the python-test matrix even on a | |
| # Rust-only PR title). On Rust-only PRs the python jobs below are | |
| # `skipped`, which satisfies the `ci` aggregator's `needs:` without | |
| # being `failure` or `cancelled`. | |
| changes: | |
| name: detect path changes | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| outputs: | |
| py: ${{ steps.filter.outputs.py }} | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 1 | |
| submodules: false | |
| - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4 | |
| id: filter | |
| with: | |
| filters: | | |
| py: | |
| - 'big-code-analysis-py/**' | |
| - '.github/workflows/ci.yml' | |
| - '.pre-commit-config.yaml' | |
| - 'Makefile' | |
| - 'mise.toml' | |
| - 'Cargo.toml' | |
| - 'Cargo.lock' | |
| fmt: | |
| name: rustfmt | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 1 | |
| submodules: false | |
| - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # stable tip | |
| with: | |
| toolchain: stable | |
| components: rustfmt | |
| - run: cargo fmt --all -- --check | |
| clippy: | |
| name: clippy | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| # security-events:write lets the upload-sarif step publish the clippy | |
| # findings to GitHub code scanning (Security tab + PR annotations). | |
| # actions:read is required by upload-sarif on private/internal repos | |
| # (harmless on public ones) and matches codeql.yml's permission set. | |
| # The workflow-level default is contents:read. | |
| permissions: | |
| contents: read | |
| security-events: write | |
| actions: read | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 1 | |
| submodules: false | |
| - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # stable tip | |
| with: | |
| toolchain: stable | |
| components: clippy | |
| - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 | |
| # clippy-sarif converts cargo's JSON diagnostics into SARIF; sarif-fmt | |
| # renders the same findings human-readably into the job log. Pinned to | |
| # 0.8.0 for a reproducible install. taiki-e/install-action has no | |
| # manifest entry for these crates, so they are compiled from source. | |
| # Swatinem/rust-cache does not persist ~/.cargo/bin, so cache the built | |
| # binaries explicitly (keyed on the version pin) to avoid recompiling | |
| # them — sarif-fmt pulls in syntect — on every run. | |
| - name: Cache clippy-sarif and sarif-fmt | |
| id: cache-sarif-tools | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: | | |
| ~/.cargo/bin/clippy-sarif | |
| ~/.cargo/bin/sarif-fmt | |
| key: sarif-tools-${{ runner.os }}-0.8.0 | |
| - name: Install clippy-sarif and sarif-fmt | |
| if: steps.cache-sarif-tools.outputs.cache-hit != 'true' | |
| run: | | |
| cargo install clippy-sarif --version 0.8.0 --locked | |
| cargo install sarif-fmt --version 0.8.0 --locked | |
| # Capture clippy's JSON diagnostics to a file (not a pipe) so the SARIF | |
| # is generated from a complete document — a hard compile-error that | |
| # aborts clippy mid-stream cannot truncate a piped report. | |
| # `|| clippy_status=$?` records clippy's real exit code without `set -e` | |
| # aborting first; the final `exit` re-raises it so this step still | |
| # fails the build on any warning, exactly as the old `-- -D warnings` | |
| # invocation did. The SARIF file is written before that exit, so the | |
| # upload step can still publish it via `if: always()`. | |
| - name: Run clippy (SARIF) | |
| run: | | |
| set -euo pipefail | |
| clippy_status=0 | |
| cargo clippy --workspace --all-targets --all-features \ | |
| --message-format=json -- -D warnings > clippy.json || clippy_status=$? | |
| clippy-sarif < clippy.json > rust-clippy-results.sarif | |
| sarif-fmt < rust-clippy-results.sarif | |
| exit "$clippy_status" | |
| # Fork PRs get a read-only GITHUB_TOKEN without security-events scope, | |
| # so the upload would error; skip it there (and when no SARIF was | |
| # produced, e.g. clippy never ran). The clippy gate above still fails | |
| # the build on warnings regardless of this step. | |
| - name: Upload SARIF to code scanning | |
| if: always() && hashFiles('rust-clippy-results.sarif') != '' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) | |
| uses: github/codeql-action/upload-sarif@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 | |
| with: | |
| sarif_file: rust-clippy-results.sarif | |
| category: clippy | |
| docs: | |
| name: rustdoc | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 1 | |
| submodules: false | |
| - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # stable tip | |
| with: | |
| toolchain: stable | |
| - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 | |
| # Delegate to the Makefile so the strict-doc gate lives in one | |
| # place. `make doc-check` appends `-D warnings` to the workflow- | |
| # level RUSTDOCFLAGS rather than replacing it. | |
| - run: make doc-check | |
| # Gate-effectiveness test: inject a known broken intra-doc link | |
| # on a `pub` item and assert `make doc-check` FAILS. Mirrors the | |
| # `enums-check` sabotage step further down — catches regressions | |
| # where `-D warnings` is silently dropped from the recipe, the | |
| # `_pc-doc-check` / `_ci-doc-check` wiring breaks, or rustdoc's | |
| # broken-intra-doc-links lint gets demoted. The trap restores | |
| # the file even if the assertion fails. See lesson 21 in | |
| # docs/development/lessons_learned.md on dedicated-gate parity. | |
| - name: doc-check gate-effectiveness test | |
| run: | | |
| set -eu | |
| test_file=src/lib.rs | |
| cp "$test_file" "$test_file.bak" | |
| trap 'mv "$test_file.bak" "$test_file"' EXIT | |
| # shellcheck disable=SC2016 # backticks inside the literal rustdoc payload are intentional | |
| printf '\n/// Sabotage broken link: [`NonExistentDocCheckGateTarget`]\npub fn _doc_check_gate_sabotage() {}\n' >> "$test_file" | |
| if make doc-check > /tmp/doc-sabotage.log 2>&1; then | |
| echo "FAIL: doc-check did not reject the broken intra-doc link" | |
| tail -40 /tmp/doc-sabotage.log | |
| exit 1 | |
| fi | |
| # Confirm the failure was caused by *this* sabotage (not some | |
| # unrelated pre-existing rustdoc warning, a network glitch, or | |
| # a cargo-side error). Without this check the gate could | |
| # appear effective while silently skipping the injected | |
| # payload. | |
| if ! grep -q 'NonExistentDocCheckGateTarget' /tmp/doc-sabotage.log; then | |
| echo "FAIL: doc-check exited non-zero but not because of the injected payload" | |
| tail -40 /tmp/doc-sabotage.log | |
| exit 1 | |
| fi | |
| echo "PASS: doc-check correctly rejected the broken intra-doc link" | |
| test: | |
| name: test (${{ matrix.os }}) | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 45 | |
| # checks:write lets action-junit-report create the test-report check run; | |
| # pull-requests:write lets it annotate failing tests on the PR. The | |
| # workflow-level default is contents:read. | |
| permissions: | |
| contents: read | |
| checks: write | |
| pull-requests: write | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| steps: | |
| # Integration snapshots live in the big-code-analysis-output | |
| # submodule under tests/repositories/; tests require it. | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 1 | |
| submodules: recursive | |
| - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # stable tip | |
| with: | |
| toolchain: stable | |
| - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 | |
| - uses: taiki-e/install-action@37f7c5781271959fb65b6b35224e28652ff2b63d # v2.87.0 | |
| with: | |
| tool: cargo-nextest | |
| # nextest runs the lib / bins / integration tests and writes the JUnit | |
| # report to target/nextest/ci/junit.xml (see .config/nextest.toml). It | |
| # does not run doctests — those are covered by the next step. | |
| - name: cargo nextest run | |
| run: cargo nextest run --profile ci --all-features --workspace --locked | |
| # Doctests, matching the Makefile's `test` / `test-doc` split. nextest | |
| # cannot run these, so dropping this step would silently stop | |
| # exercising the workspace's documentation examples. | |
| - name: cargo test --doc | |
| run: cargo test --doc --all-features --workspace --locked | |
| # Publish the JUnit XML as an artifact regardless of outcome — this | |
| # works even on fork PRs, where the check-run API used below is not | |
| # available. | |
| - name: Upload JUnit report | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: junit-${{ matrix.os }} | |
| path: target/nextest/ci/junit.xml | |
| if-no-files-found: warn | |
| # Render the JUnit report as a check run + PR annotations + job summary. | |
| # nextest already gates the job, so fail_on_failure stays off to avoid | |
| # masking the real exit code under `if: always()`. Skipped on fork PRs, | |
| # whose read-only token cannot create check runs. | |
| - name: Publish test report | |
| if: always() && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) | |
| uses: mikepenz/action-junit-report@a9170d5795813c01ab4901ffb045b52bab4ab09d # v6.5.0 | |
| with: | |
| report_paths: target/nextest/ci/junit.xml | |
| check_name: test report (${{ matrix.os }}) | |
| detailed_summary: true | |
| include_passed: false | |
| fail_on_failure: false | |
| # The exact ancestor-chain assertion (#1122). `Ancestors::checked` keeps | |
| # only an O(1) approximation on by default, because the exact | |
| # `chain.last() == node.parent()` form costs Node::parent's O(depth) per | |
| # node and makes every debug-build walk quadratic. This lane restores it | |
| # so a walker whose truncate/push bookkeeping desynchronises still fails | |
| # a build rather than silently feeding predicates a wrong ancestor. | |
| # | |
| # ubuntu-only and lib-scoped: the assertion lives in the library's walks, | |
| # so the per-OS matrix and the CLI / web / integration tiers would re-pay | |
| # the quadratic cost without covering anything the lib tests do not. | |
| chain-audit: | |
| name: chain audit | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 1 | |
| submodules: false | |
| - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # stable tip | |
| with: | |
| toolchain: stable | |
| - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 | |
| - name: cargo test --lib with --cfg chain_audit | |
| env: | |
| # Replaces the workflow-level RUSTFLAGS rather than extending | |
| # it — a step `env` key shadows the workflow one — so `-D | |
| # warnings` is repeated here deliberately. Keep the two in | |
| # step if the workflow-level value ever gains a flag. | |
| RUSTFLAGS: "-D warnings --cfg chain_audit" | |
| run: cargo test -p big-code-analysis --lib --all-features --locked | |
| coverage: | |
| name: coverage | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| steps: | |
| # Coverage instruments and runs the full test suite, so it needs the | |
| # big-code-analysis-output submodule exactly like the test job above. | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 1 | |
| submodules: recursive | |
| # llvm-tools-preview supplies llvm-profdata/llvm-cov for source-based | |
| # coverage instrumentation. | |
| - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # stable tip | |
| with: | |
| toolchain: stable | |
| components: llvm-tools-preview | |
| # Coverage builds carry -C instrument-coverage, so their artifacts are | |
| # incompatible with the regular build cache; a distinct key keeps the | |
| # two from thrashing each other. | |
| - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 | |
| with: | |
| key: coverage | |
| - uses: taiki-e/install-action@37f7c5781271959fb65b6b35224e28652ff2b63d # v2.87.0 | |
| with: | |
| tool: cargo-nextest,cargo-llvm-cov | |
| # nextest is the same runner the `test` job uses, so instrumenting it | |
| # keeps the coverage numbers aligned with what CI actually exercises. | |
| # --no-report defers reporting so the same profile data can be emitted | |
| # in two shapes below (Codecov upload + human-readable job summary). | |
| # | |
| # Doctests are intentionally excluded: `cargo llvm-cov --doc` requires | |
| # nightly, and pairing nightly with the workflow-wide `-D warnings` | |
| # would red-X CI on unrelated new nightly lints. The `test` job still | |
| # runs doctests for correctness — only their coverage is omitted. | |
| - name: Run tests with coverage instrumentation | |
| run: cargo llvm-cov --no-report nextest --all-features --workspace --locked | |
| # Codecov's native format preserves region coverage (lcov would drop to | |
| # line-only). | |
| - name: Generate Codecov report | |
| run: cargo llvm-cov report --codecov --output-path codecov.json | |
| # Per-file coverage table in the run's Summary tab, so reviewers can | |
| # read coverage without leaving GitHub or waiting on the Codecov PR | |
| # comment. | |
| - name: Write coverage to job summary | |
| run: | | |
| { | |
| echo '### Coverage' | |
| echo '' | |
| echo '```' | |
| cargo llvm-cov report | |
| echo '```' | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| # token authenticates same-repo runs; fork PRs fall back to Codecov's | |
| # tokenless upload for public repos (the secret resolves empty there). | |
| # fail_ci_if_error stays off so a Codecov outage or a credential-less | |
| # fork PR cannot red-X the merge gate — coverage is informational. | |
| - name: Upload coverage to Codecov | |
| uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 | |
| with: | |
| files: codecov.json | |
| flags: rust | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| fail_ci_if_error: false | |
| msrv: | |
| name: msrv (1.94) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 1 | |
| submodules: false | |
| - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # 1.94 | |
| with: | |
| toolchain: "1.94" | |
| - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 | |
| # Build only — MSRV is a compile-time promise; dev-deps may | |
| # require a newer toolchain. | |
| - run: cargo build --all-features --workspace --locked | |
| feature-matrix: | |
| name: features (${{ matrix.name }}) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - name: default (lib) | |
| flags: -p big-code-analysis | |
| - name: no-default-features (lib) | |
| flags: --no-default-features -p big-code-analysis | |
| # Per-language feature subset (issue #252). Locks in the | |
| # "minimal grammar set" guarantee: dropping default features | |
| # and re-enabling only the languages the caller cares about | |
| # must still produce a working library. `rust` + `typescript` | |
| # is the canonical subset because it exercises both a | |
| # single-feature grammar (`tree-sitter-rust`) and a shared- | |
| # crate grammar (`tree-sitter-typescript`, also used by the | |
| # `Tsx` variant). | |
| - name: minimal-langs (lib) | |
| flags: --no-default-features --features rust,typescript -p big-code-analysis | |
| - name: default (cli) | |
| flags: -p big-code-analysis-cli | |
| - name: no-default-features (cli) | |
| flags: --no-default-features -p big-code-analysis-cli | |
| - name: default (web) | |
| flags: -p big-code-analysis-web | |
| - name: no-default-features (web) | |
| flags: --no-default-features -p big-code-analysis-web | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 1 | |
| submodules: false | |
| - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # stable tip | |
| with: | |
| toolchain: stable | |
| - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 | |
| # `cargo check --all-targets` exercises bins, tests, and examples, | |
| # so a cfg-gated test file that only compiles under default features | |
| # does not slip through the no-default-features matrix leg. | |
| - run: cargo check --all-targets ${{ matrix.flags }} --locked | |
| deny: | |
| name: cargo-deny | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 1 | |
| submodules: false | |
| - uses: EmbarkStudios/cargo-deny-action@3c6349835b2b7b196a839186cb8b78e02f7b5f25 # v2.1.1 | |
| with: | |
| command: check advisories bans licenses sources | |
| license-audit: | |
| name: license-audit | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 1 | |
| submodules: false | |
| - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # stable tip | |
| with: | |
| toolchain: stable | |
| - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 | |
| - uses: taiki-e/install-action@37f7c5781271959fb65b6b35224e28652ff2b63d # v2.87.0 | |
| with: | |
| tool: cargo-about@0.8.4 | |
| # Fails if any dep in either binary crate's transitive closure | |
| # resolves to a license not in `about.toml` `accepted`. Dependabot | |
| # PRs that introduce an unapproved license now fail here instead of | |
| # silently slipping into the release. | |
| # Render to a file (rather than /dev/null) and sanity-check the | |
| # output. A regression in about.hbs that yielded an empty render | |
| # would otherwise pass silently — exit 0 from cargo-about does not | |
| # imply non-empty output. | |
| - name: Render attribution bundle for big-code-analysis-cli | |
| run: | | |
| cargo about generate --locked \ | |
| --manifest-path big-code-analysis-cli/Cargo.toml \ | |
| about.hbs > cli-thirdparty.md | |
| grep -q '^## Crates' cli-thirdparty.md | |
| test "$(wc -l < cli-thirdparty.md)" -gt 50 | |
| - name: Render attribution bundle for big-code-analysis-web | |
| run: | | |
| cargo about generate --locked \ | |
| --manifest-path big-code-analysis-web/Cargo.toml \ | |
| about.hbs > web-thirdparty.md | |
| grep -q '^## Crates' web-thirdparty.md | |
| test "$(wc -l < web-thirdparty.md)" -gt 50 | |
| lint: | |
| name: lint (make lint) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 1 | |
| submodules: false | |
| # `rustfmt` is here for the rustfmt-bail gate, which shells out to | |
| # it directly. The toolchain action installs the minimal profile, | |
| # and the gate skips cleanly when rustfmt is missing — so leaving | |
| # it out would disable the check in CI without failing anything. | |
| - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # stable tip | |
| with: | |
| toolchain: stable | |
| components: clippy, rustfmt | |
| - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: "3.x" | |
| # rumdl is the Markdown linter (replaced markdownlint-cli2). The | |
| # prebuilt binary is not in taiki-e/install-action's manifest, so | |
| # we fetch the upstream release tarball directly with a pinned | |
| # SHA256, mirroring the actionlint and checkmake patterns below. | |
| # Bump RUMDL_SHA256 in lockstep with `RUMDL_VERSION` (and with the | |
| # `rumdl = …` pin in `mise.toml`). To regenerate the SHA256 after | |
| # a version bump: | |
| # curl -fsSL https://github.com/rvben/rumdl/releases/download/v${RUMDL_VERSION}/rumdl-v${RUMDL_VERSION}-x86_64-unknown-linux-gnu.tar.gz.sha256 | |
| - name: Install rumdl | |
| env: | |
| RUMDL_VERSION: "0.2.2" | |
| RUMDL_SHA256: "d38ad81c51221990d5e0204b4746f8c980a77de235aa875c2f30f02f9a19bb1a" | |
| run: | | |
| set -euo pipefail | |
| url="https://github.com/rvben/rumdl/releases/download/v${RUMDL_VERSION}/rumdl-v${RUMDL_VERSION}-x86_64-unknown-linux-gnu.tar.gz" | |
| curl -fsSL -o /tmp/rumdl.tgz "$url" | |
| echo "${RUMDL_SHA256} /tmp/rumdl.tgz" | sha256sum -c - | |
| tar -xzf /tmp/rumdl.tgz -C /tmp rumdl | |
| install -m 0755 /tmp/rumdl /usr/local/bin/rumdl | |
| rumdl --version | |
| - uses: taiki-e/install-action@37f7c5781271959fb65b6b35224e28652ff2b63d # v2.87.0 | |
| with: | |
| tool: taplo-cli@0.10.0,shellcheck@0.10.0,shfmt@3.12.0 | |
| # actionlint 1.7.12's prebuilt binary is not yet in | |
| # taiki-e/install-action's manifest, and cargo-binstall has no | |
| # fallback for it. Fetch the upstream release tarball directly, | |
| # mirroring the checkmake step below. Bump SHA256 in lockstep | |
| # with `ACTIONLINT_VERSION`. | |
| - name: Install actionlint | |
| env: | |
| ACTIONLINT_VERSION: "1.7.12" | |
| ACTIONLINT_SHA256: "8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8" | |
| run: | | |
| set -euo pipefail | |
| url="https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" | |
| curl -fsSL -o /tmp/actionlint.tgz "$url" | |
| echo "${ACTIONLINT_SHA256} /tmp/actionlint.tgz" | sha256sum -c - | |
| tar -xzf /tmp/actionlint.tgz -C /tmp actionlint | |
| install -m 0755 /tmp/actionlint /usr/local/bin/actionlint | |
| actionlint -version | |
| # checkmake is not in taiki-e/install-action's manifest; fetch the | |
| # pinned upstream release binary directly. The SHA256 below comes | |
| # from the upstream `checksums.txt` published alongside the | |
| # 0.2.2 release; bump it in lockstep with the version pin. | |
| - name: Install checkmake | |
| env: | |
| CHECKMAKE_VERSION: "0.2.2" | |
| CHECKMAKE_SHA256: "bedd033b06f2563809855ec2a9950c7a81acea6cd82937fd2f124e2c1c5fc3d5" | |
| run: | | |
| set -euo pipefail | |
| url="https://github.com/mrtazz/checkmake/releases/download/${CHECKMAKE_VERSION}/checkmake-${CHECKMAKE_VERSION}.linux.amd64" | |
| curl -fsSL -o /tmp/checkmake "$url" | |
| echo "${CHECKMAKE_SHA256} /tmp/checkmake" | sha256sum -c - | |
| install -m 0755 /tmp/checkmake /usr/local/bin/checkmake | |
| checkmake --version | |
| - name: make lint | |
| run: make lint | |
| # Defensive: `make lint` invokes the snapshot-anchors recipe, but | |
| # `make lint`'s file-finding helpers depend on a glob fix tracked | |
| # under #160; until that lands, run the snapshot-anchor gate | |
| # explicitly so a regression in `make lint` cannot silently disable | |
| # this check. | |
| - name: snapshot anchors (explicit) | |
| run: python3 utils/check-snapshot-anchors.py | |
| # Run the gate's own unittests as their own explicit step, for the | |
| # same reason as the rustfmt-bail twin below: a refactor that | |
| # breaks the script must not be able to disable the gate silently. | |
| - name: snapshot-anchors self-tests (explicit) | |
| run: python3 -m unittest -q utils/check-snapshot-anchors-test.py | |
| # Defensive twin for the rustfmt-bail gate (#1136): a comment | |
| # inside a match pattern makes rustfmt emit the whole match | |
| # verbatim while `cargo fmt --check` exits 0, so the fmt job | |
| # cannot catch it. `make lint` already runs this gate; pinning it | |
| # here keeps it enforced even if a future refactor drops it from | |
| # the aggregate recipe. | |
| - name: rustfmt-bail (explicit) | |
| run: python3 utils/check-rustfmt-bail.py | |
| # Run the gate's own unittests as their own explicit step | |
| # so a refactor that breaks the script can't disable the | |
| # gate without surfacing the regression in CI. | |
| - name: rustfmt-bail self-tests (explicit) | |
| run: python3 -m unittest -q utils/check-rustfmt-bail-test.py | |
| # Defensive twin to the snapshot-anchors gate above: `make lint` | |
| # already invokes check-versions, but pinning it here guarantees | |
| # the lockstep version invariant is enforced even if a future | |
| # refactor drops it from the aggregate recipe. | |
| - name: check-versions (explicit) | |
| run: python3 utils/check-versions.py | |
| # Defensive twin for the man-page packaging gate (#446): a bca | |
| # subcommand man page that drops out of the hand-maintained | |
| # deb/rpm asset lists ships an artifact without its page (the | |
| # #444 bug class). `make lint` already runs this gate; pinning | |
| # it here guarantees enforcement even if a future refactor drops | |
| # it from the aggregate recipe. | |
| - name: check-manpage-assets (explicit) | |
| run: python3 utils/check-manpage-assets.py | |
| # Defensive twin for the man-page drift gate's self-tests | |
| # (#1249). The gate itself runs in the `manpage` job, which | |
| # invokes the script directly and so cannot be dropped by a | |
| # refactor of the `make lint` aggregate — but the tests that | |
| # prove the gate still catches an untracked page can be, and a | |
| # gate that has silently stopped gating is indistinguishable | |
| # from a clean tree. That is the failure this pair exists for. | |
| - name: check-manpage-drift-test (explicit) | |
| run: python3 -m unittest -q utils/check-manpage-drift-test.py | |
| # Defensive twin for the diagnostic-prefix gate (#1199): a | |
| # capitalised `Warning:` / `Error:` / `Note:` baked into a string | |
| # literal bypasses the one-helper-per-crate severity ladder and | |
| # reads as correct in review. Its self-tests run as their own | |
| # step because a source-scanning gate that stops matching | |
| # reports a clean tree. | |
| - name: check-diagnostic-prefix (explicit) | |
| run: python3 utils/check-diagnostic-prefix.py | |
| - name: check-diagnostic-prefix self-tests (explicit) | |
| run: python3 -m unittest -q utils/check-diagnostic-prefix-test.py | |
| # Defensive twin for the safety-doc pin gate (#1057): the | |
| # module doc of big-code-analysis-py/src/node.rs is the | |
| # canonical soundness argument for this workspace's only | |
| # sanctioned `unsafe` block, and it reasons about a named | |
| # tree-sitter release. A bump that leaves the literal behind | |
| # leaves an argument that reads as verified against a crate | |
| # nobody compiles. Its self-tests run as their own step for the | |
| # usual reason: a source-scanning gate that stops matching | |
| # reports a clean tree. | |
| - name: check-safety-doc-pin (explicit) | |
| run: python3 utils/check-safety-doc-pin.py | |
| - name: check-safety-doc-pin self-tests (explicit) | |
| run: python3 -m unittest -q utils/check-safety-doc-pin-test.py | |
| # Defensive twin for the grammar-marker-sync gate (#400): bumping | |
| # the notification-only marker in tree-sitter-{mozjs,mozcpp}/ | |
| # Cargo.toml without re-running the matching generate-*.sh | |
| # ships a marker that lies about the bundled grammar version. | |
| # Invokes the script directly (matching the snapshot-anchors | |
| # and check-versions defensive twins above) so a regression | |
| # in the `make lint` aggregate cannot silently disable this | |
| # check. | |
| - name: grammar-marker-sync (explicit) | |
| run: python3 utils/check-grammar-marker-sync.py | |
| # Run the gate's own unittests as their own explicit step | |
| # so a refactor that breaks the script can't disable the | |
| # gate without surfacing the regression in CI. | |
| - name: grammar-marker-sync self-tests (explicit) | |
| run: python3 -m unittest -q utils/check-grammar-marker-sync-test.py | |
| # Defensive twin for the workspace-exclusion gate (#1145, #1151): | |
| # an excluded crate that roots no workspace of its own breaks | |
| # `cargo fmt --all` inside every git worktree, and an unpinned | |
| # tree-sitter dependency lets `cargo update` move a grammar | |
| # silently. `make lint` already runs this gate; invoking it here | |
| # (matching the twins above) keeps it enforced even if a future | |
| # refactor drops it from the aggregate recipe. | |
| - name: check-excluded-manifests (explicit) | |
| run: python3 utils/check-excluded-manifests.py | |
| # Run the gate's own unittests as their own explicit step | |
| # so a refactor that breaks the script can't disable the | |
| # gate without surfacing the regression in CI. | |
| - name: check-excluded-manifests self-tests (explicit) | |
| run: python3 -m unittest -q utils/check-excluded-manifests-test.py | |
| # Defensive twin for the ruff-lockstep gate (#1230): the | |
| # ruff-pre-commit `rev:`, `uv.lock`, and the hash-pinned | |
| # `requirements/dev.txt` export CI installs from must name one | |
| # version. They had already drifted once, silently, because a | |
| # comment was the whole mechanism — so this gate in particular | |
| # must not be able to disappear from the aggregate unnoticed. | |
| - name: check-ruff-lockstep (explicit) | |
| run: python3 utils/check-ruff-lockstep.py | |
| # Run the gate's own unittests as their own explicit step | |
| # so a refactor that breaks the script can't disable the | |
| # gate without surfacing the regression in CI. | |
| - name: check-ruff-lockstep self-tests (explicit) | |
| run: python3 -m unittest -q utils/check-ruff-lockstep-test.py | |
| # Defensive twin for the enums-codegen-drift gate (#405): | |
| # running any grammar regen previously regenerated | |
| # `src/c_langs_macros/*.rs` to a pre-optimization form | |
| # silently. The gate diffs codegen output against | |
| # checked-in files; running it explicitly here guards | |
| # against `make lint` ever dropping it from the aggregate. | |
| - name: enums-codegen-drift (explicit) | |
| run: bash utils/check-enums-codegen-drift.sh | |
| # Run the gate's own unittests as their own explicit step | |
| # so a refactor that breaks the script can't disable the | |
| # gate without surfacing the regression in CI. | |
| - name: enums-codegen-drift self-tests (explicit) | |
| run: python3 -m unittest -q utils/check-enums-codegen-drift-test.py | |
| # Defensive: the prior `make lint` step already runs enums-check | |
| # as part of its aggregate. Repeating it here protects against a | |
| # regression to that aggregate (e.g., enums-check accidentally | |
| # dropped from the `lint` recipe), which is precisely the failure | |
| # mode #164 was filed against — `[workspace].exclude` had hidden | |
| # the crate from every workspace-scoped lint until the gate | |
| # existed. The redundant invocation is cheap (cargo cache is warm) | |
| # and pins the gate at the workflow level. | |
| - name: enums-crate check (explicit) | |
| run: make enums-check | |
| # Gate-effectiveness test: inject a known unused-variable warning | |
| # into enums/src/main.rs and assert `make enums-check` FAILS. | |
| # Catches regressions where RUSTFLAGS is silently dropped, the | |
| # recipe stops failing on warnings, or `--all-targets` is | |
| # removed. The trap restores the file even if the assertion | |
| # fails. This step runs only in CI — locally, a developer who | |
| # changes the gate should re-verify with the same maneuver. | |
| - name: enums-check gate-effectiveness test | |
| run: | | |
| set -eu | |
| test_file=enums/src/main.rs | |
| cp "$test_file" "$test_file.bak" | |
| trap 'mv "$test_file.bak" "$test_file"' EXIT | |
| printf '\nfn _enums_check_gate_sabotage() { let unused_x = 1u32; }\n' >> "$test_file" | |
| if make enums-check > /tmp/sabotage.log 2>&1; then | |
| echo "FAIL: enums-check did not reject the injected warning" | |
| tail -40 /tmp/sabotage.log | |
| exit 1 | |
| fi | |
| echo "PASS: enums-check correctly rejected the injected warning" | |
| # Python bindings — lint + type-check. Single Linux job; the heavier | |
| # cross-platform matrix lives in `python-test` below. Both jobs are | |
| # gated by `needs.changes.outputs.py == 'true'` so Rust-only PRs skip | |
| # them entirely (verified by the `ci` aggregator job's tolerance for | |
| # `skipped` results). | |
| python-lint: | |
| name: python-lint | |
| needs: changes | |
| if: needs.changes.outputs.py == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| # The bindings tests use the local fixtures under | |
| # big-code-analysis-py/tests/fixtures/, not the | |
| # big-code-analysis-output submodule (that's the Rust integration | |
| # snapshot store). Skip recursive submodules to avoid pulling | |
| # ~hundreds of MB of unused fixtures and to avoid blocking on a | |
| # missing/force-pushed submodule SHA. | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 1 | |
| submodules: false | |
| - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # stable | |
| with: | |
| toolchain: stable | |
| - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 | |
| with: | |
| # Share the cache key with the ubuntu-latest/py3.12 leg of | |
| # the python-test matrix below — both jobs build the same | |
| # bindings crate against the same toolchain on the same OS, | |
| # so a populated target/ from one job warms the other. | |
| shared-key: "python-bindings-ubuntu-latest-3.12" | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: "3.12" | |
| cache: "pip" | |
| # Create the venv inside big-code-analysis-py/ to match | |
| # pyrightconfig.json's venvPath="." + venv=".venv" pair (relative | |
| # to pyrightconfig.json's location). A repo-root venv would leave | |
| # pyright unable to resolve the maturin-built _native module. | |
| # requirements/dev.txt is the hash-pinned export of uv.lock's | |
| # `dev` extra (regenerated by `make py-relock`), so this job | |
| # installs the exact resolved set `make py-bootstrap` uses | |
| # locally — no floor-vs-lockfile drift — and `--require-hashes` | |
| # pins the supply chain (OpenSSF Scorecard Pinned-Dependencies). | |
| # The ruff-pre-commit `rev:` in .pre-commit-config.yaml is held in | |
| # lockstep with the ruff version resolved there by the | |
| # `check-ruff-lockstep` gate in the `lint` job (#1230), so this is | |
| # no longer something a reviewer has to remember. | |
| - name: Create venv + install Python toolchain | |
| working-directory: big-code-analysis-py | |
| run: | | |
| python -m venv .venv | |
| source .venv/bin/activate | |
| pip install --require-hashes -r requirements/dev.txt | |
| # maturin develop must precede mypy / pyright so the _native | |
| # extension is resolvable from the venv. Both type checkers | |
| # treat the .pyi stub as authoritative, but they still need to | |
| # import the package without ModuleNotFoundError. Use the | |
| # default (debug) profile to match the local `make py-typecheck` | |
| # / `make py-test` invocations — local-CI parity matters more | |
| # than build speed at the lint job (debug is faster anyway). | |
| - name: maturin develop | |
| working-directory: big-code-analysis-py | |
| run: | | |
| source .venv/bin/activate | |
| maturin develop | |
| - name: ruff format --check | |
| working-directory: big-code-analysis-py | |
| run: | | |
| source .venv/bin/activate | |
| ruff format --check . | |
| - name: ruff check | |
| working-directory: big-code-analysis-py | |
| run: | | |
| source .venv/bin/activate | |
| ruff check . | |
| - name: mypy --strict | |
| working-directory: big-code-analysis-py | |
| run: | | |
| source .venv/bin/activate | |
| mypy --strict python tests examples | |
| - name: pyright | |
| working-directory: big-code-analysis-py | |
| run: | | |
| source .venv/bin/activate | |
| pyright | |
| # Python bindings — wheel build + pytest, Linux/macOS/Windows | |
| # × Python 3.12/3.13. The cargo `test` job already builds the | |
| # bindings crate's Rust side; this job is *additive*, verifying that | |
| # the maturin-produced wheel actually loads under CPython on each | |
| # platform (which cargo cannot do). | |
| python-test: | |
| name: python-test (${{ matrix.os }}, py${{ matrix.python }}) | |
| needs: changes | |
| if: needs.changes.outputs.py == 'true' | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 45 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| python: ["3.12", "3.13"] | |
| steps: | |
| # Submodules unnecessary — bindings tests use the local fixtures | |
| # at big-code-analysis-py/tests/fixtures/. The | |
| # big-code-analysis-output submodule is for Rust integration | |
| # snapshots and would otherwise gate this matrix on its | |
| # availability. | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 1 | |
| submodules: false | |
| - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # stable | |
| with: | |
| toolchain: stable | |
| - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 | |
| with: | |
| # Per-matrix-leg cache key so Linux / macOS / Windows do not | |
| # trample each other's target/ directories. The | |
| # ubuntu-latest+3.12 leg shares its key with python-lint so | |
| # they reuse each other's build artifacts. | |
| shared-key: "python-bindings-${{ matrix.os }}-${{ matrix.python }}" | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: ${{ matrix.python }} | |
| cache: "pip" | |
| # Cross-platform venv inside big-code-analysis-py/ (matches | |
| # pyrightconfig.json + the python-lint job + the local Makefile | |
| # convention). bash on Linux/macOS, PowerShell on Windows. | |
| # requirements/dev.txt is the hash-pinned export of uv.lock's | |
| # `dev` extra (regenerated by `make py-relock`): exact resolved | |
| # versions with `--require-hashes` supply-chain pinning (OpenSSF | |
| # Scorecard Pinned-Dependencies). It carries a few lint-only | |
| # tools this job does not run (ruff / mypy / pyright) — the cost | |
| # of installing them is preferred over maintaining a second, | |
| # driftable pin set. | |
| - name: Create venv + install deps (Unix) | |
| if: runner.os != 'Windows' | |
| working-directory: big-code-analysis-py | |
| run: | | |
| python -m venv .venv | |
| .venv/bin/pip install --require-hashes -r requirements/dev.txt | |
| - name: Create venv + install deps (Windows) | |
| if: runner.os == 'Windows' | |
| working-directory: big-code-analysis-py | |
| shell: pwsh | |
| run: | | |
| python -m venv .venv | |
| .venv\Scripts\python.exe -m pip install --require-hashes -r requirements/dev.txt | |
| # maturin develop uses the default (debug) profile to match the | |
| # local `make py-test` recipe — same .so layout end-to-end, so | |
| # local-CI parity issues surface in CI rather than only on a | |
| # contributor's machine. | |
| - name: maturin develop (Unix) | |
| if: runner.os != 'Windows' | |
| working-directory: big-code-analysis-py | |
| run: | | |
| source .venv/bin/activate | |
| maturin develop | |
| - name: maturin develop (Windows) | |
| if: runner.os == 'Windows' | |
| working-directory: big-code-analysis-py | |
| shell: pwsh | |
| run: | | |
| .venv\Scripts\Activate.ps1 | |
| maturin develop | |
| - name: pytest (Unix) | |
| if: runner.os != 'Windows' | |
| working-directory: big-code-analysis-py | |
| run: | | |
| source .venv/bin/activate | |
| python -m pytest --cov=big_code_analysis --cov-report=xml --cov-report=term-missing | |
| - name: pytest (Windows) | |
| if: runner.os == 'Windows' | |
| working-directory: big-code-analysis-py | |
| shell: pwsh | |
| run: | | |
| .venv\Scripts\Activate.ps1 | |
| python -m pytest | |
| # Python-side coverage measures the pure-Python surface | |
| # (python/big_code_analysis/_flatten.py + __init__.py); the Rust | |
| # extension is measured by the `coverage` job. Collected on a single | |
| # matrix leg because pure-Python coverage is identical across OS / | |
| # interpreter — uploading from all six legs would only race parallel | |
| # uploads to Codecov. flags:python keeps it distinct from the Rust | |
| # upload; fail_ci_if_error stays off for the same reason as the Rust | |
| # job (informational, fork-PR-safe). | |
| - name: Upload coverage to Codecov | |
| if: runner.os == 'Linux' && matrix.python == '3.12' | |
| uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 | |
| with: | |
| files: big-code-analysis-py/coverage.xml | |
| flags: python | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| fail_ci_if_error: false | |
| # Execute examples/jupyter_quickstart.ipynb end-to-end so a notebook | |
| # cell that drifts away from the bindings API (renamed kwarg, removed | |
| # function) breaks CI before it can rot in the documentation. The | |
| # job stays single-OS / single-Python because the notebook exercises | |
| # the public Python surface and pandas / matplotlib — both of which | |
| # the `python-test` matrix already covers across OS / Python combos. | |
| # A second matrix axis would only re-prove pandas works on Windows. | |
| # | |
| # Gated by `needs.changes.outputs.py == 'true'` same as the other | |
| # python-* jobs; Rust-only PRs see `skipped` (not `failure`) so the | |
| # aggregator's tolerance for skipped dependents continues to hold. | |
| python-examples-nbconvert: | |
| name: python-examples-nbconvert | |
| needs: changes | |
| if: needs.changes.outputs.py == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 1 | |
| submodules: false | |
| - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # stable | |
| with: | |
| toolchain: stable | |
| - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 | |
| with: | |
| # Share with the python-lint / ubuntu-latest+3.12 python-test | |
| # legs so the maturin develop step warms from a populated | |
| # target/ rather than rebuilding from scratch. | |
| shared-key: "python-bindings-ubuntu-latest-3.12" | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: "3.12" | |
| cache: "pip" | |
| - name: Create venv + install notebook + maturin deps | |
| working-directory: big-code-analysis-py | |
| run: | | |
| python -m venv .venv | |
| source .venv/bin/activate | |
| # requirements/examples.txt is the hash-pinned export of | |
| # uv.lock's [examples] extra (regenerated by `make | |
| # py-relock`) — installing it directly (no `pip install -e | |
| # .`) avoids a maturin PEP 517 build on top of the explicit | |
| # `maturin develop` below, and `--require-hashes` pins the | |
| # supply chain (OpenSSF Scorecard Pinned-Dependencies). | |
| pip install --require-hashes -r requirements/examples.txt | |
| - name: maturin develop | |
| working-directory: big-code-analysis-py | |
| run: | | |
| source .venv/bin/activate | |
| maturin develop | |
| - name: Execute jupyter_quickstart.ipynb | |
| working-directory: big-code-analysis-py | |
| run: | | |
| source .venv/bin/activate | |
| # `--inplace` writes the executed outputs back to the file, | |
| # but we deliberately do not commit them — CI just needs the | |
| # exit code. Using `--to notebook` keeps the format stable; | |
| # `--ExecutePreprocessor.timeout=120` caps a hung kernel. | |
| # `MPLBACKEND=Agg` keeps matplotlib headless on the runner. | |
| MPLBACKEND=Agg jupyter nbconvert \ | |
| --to notebook \ | |
| --execute \ | |
| --inplace \ | |
| --ExecutePreprocessor.timeout=120 \ | |
| examples/jupyter_quickstart.ipynb | |
| manpage: | |
| name: man pages up to date | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 1 | |
| submodules: false | |
| - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # stable tip | |
| with: | |
| toolchain: stable | |
| - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 | |
| - name: Regenerate man pages | |
| run: cargo xtask | |
| # Fails on every direction the regenerated tree can differ from | |
| # the committed one — modified, deleted, *and* added pages. The | |
| # added direction needs `git ls-files --others`: the page xtask | |
| # writes for a brand-new subcommand is untracked, and `git diff` | |
| # alone passed green over it until #1249. The check lives in the | |
| # script rather than inline here so this step and | |
| # `make manpages-check` cannot drift apart; the `::error::` | |
| # annotation is emitted by the script when GITHUB_ACTIONS is set. | |
| - name: Verify no drift | |
| run: python3 utils/check-manpage-drift.py | |
| # Single required status check for branch protection — depends on | |
| # every gating job. Passing this one check guarantees the full matrix | |
| # passed. | |
| ci: | |
| name: ci | |
| if: always() | |
| needs: | |
| - changes | |
| - fmt | |
| - clippy | |
| - docs | |
| - test | |
| - chain-audit | |
| - coverage | |
| - msrv | |
| - feature-matrix | |
| - deny | |
| - license-audit | |
| - lint | |
| - manpage | |
| - python-lint | |
| - python-test | |
| - python-examples-nbconvert | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Fail if any dependency failed or was cancelled | |
| if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') | |
| run: exit 1 |