diff --git a/.cargo-crap.toml b/.cargo-crap.toml new file mode 100644 index 000000000..c20cfbcd2 --- /dev/null +++ b/.cargo-crap.toml @@ -0,0 +1,16 @@ +threshold = 30 +fail-above = true +min = 30 +missing = "pessimistic" +# Suppress non-shipped code so the gate only measures real library logic. +# NOTE: must use `allow` (matches a function's defining file) rather than +# `exclude` (filesystem-walk only — does not filter functions read from the +# lcov file). Globs match absolute paths, hence the `**/` prefixes. +allow = [ + "**/cot-codegen/**", # generated AST/codegen helpers + "**/cot-macros/**", # proc-macro implementations + "**/tests/**", # integration & e2e test code + "**/src/test.rs", # in-crate test utilities (TestDatabase, etc.) + "**/src/test_utils.rs", # in-crate test helpers + "**/examples/**", # example applications +] diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 184d58186..5caea5c2e 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -245,6 +245,69 @@ jobs: env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + - name: Generate lcov report for cargo-crap + run: cargo llvm-cov report --lcov --output-path lcov.info + + - name: Upload lcov artifact + uses: actions/upload-artifact@v4 + with: + # Unique per workflow run so concurrent PR runs never collide. + name: lcov-${{ github.run_id }} + path: lcov.info + retention-days: 1 + + cargo-crap: + if: github.event_name == 'push' || github.event_name == 'schedule' || github.event.pull_request.head.repo.full_name != github.repository + + name: CRAP metric gate + runs-on: ubuntu-latest + needs: ["coverage"] + env: + RUSTC_WRAPPER: "" + SCCACHE_GHA_ENABLED: "" + steps: + - name: Checkout source + uses: actions/checkout@v6 + + - name: Download lcov artifact + uses: actions/download-artifact@v4 + with: + name: lcov-${{ github.run_id }} + path: lcov + + - name: Install cargo-crap + uses: taiki-e/install-action@v2 + with: + tool: cargo-crap + + - name: Run cargo-crap + run: cargo crap --lcov lcov/lcov.info --workspace --fail-above --format pr-comment --output crap-comment.md + + - name: Post or update PR comment + if: github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const body = fs.readFileSync('crap-comment.md', 'utf8'); + const marker = ''; + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + const existing = comments.find(c => c.body.startsWith(marker)); + const args = { + owner: context.repo.owner, + repo: context.repo.repo, + body, + }; + if (existing) { + await github.rest.issues.updateComment({ ...args, comment_id: existing.id }); + } else { + await github.rest.issues.createComment({ ...args, issue_number: context.issue.number }); + } + rustfmt: if: github.event_name == 'push' || github.event_name == 'schedule' || github.event.pull_request.head.repo.full_name != github.repository diff --git a/.gitignore b/.gitignore index a611abcd7..c875357b1 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,7 @@ target/ *.db *.sqlite3 *.sqlite3-journal + +# Coverage / cargo-crap artifacts +/lcov.info +/codecov.json diff --git a/justfile b/justfile index 6bc5eb7a3..f638333a2 100644 --- a/justfile +++ b/justfile @@ -43,6 +43,20 @@ coverage: # requires cargo-llvm-cov installed and nightly toolchain cargo llvm-cov --all-features --workspace --branch --doctests --html --open +alias cr := crap + +crap: + #!/usr/bin/env bash + # compute the CRAP metric the same way CI does (absolute-threshold gate) + # requires cargo-llvm-cov + cargo-crap installed and the nightly toolchain + set -euxo pipefail + docker compose up -d --wait + cargo llvm-cov --all-features --workspace --no-report -- --include-ignored + cargo llvm-cov --all-features --workspace --doc --no-report + cargo llvm-cov report --lcov --output-path lcov.info + docker compose down + cargo crap --lcov lcov.info --workspace + alias d := docs docs: