Skip to content

Commit 507991b

Browse files
authored
Merge pull request #102 from acgetchell/ci/101-llvm-cov
ci: migrate coverage to cargo-llvm-cov Closes #101
2 parents c6e04fd + f75a01c commit 507991b

4 files changed

Lines changed: 108 additions & 43 deletions

File tree

.github/workflows/codecov.yml

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
name: Code Coverage
2222
runs-on: ubuntu-latest
2323
env:
24-
TARPAULIN_VERSION: "0.32.8"
24+
CARGO_LLVM_COV_VERSION: "0.8.5"
2525
steps:
2626
- name: Checkout repository
2727
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -33,18 +33,25 @@ jobs:
3333
with:
3434
cache: true # toolchain/components are specified in rust-toolchain.toml
3535

36-
- name: Cache tarpaulin
36+
- name: Install LLVM coverage tools
37+
run: rustup component add llvm-tools-preview
38+
39+
- name: Cache cargo-llvm-cov
3740
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
3841
with:
39-
path: ~/.cargo/bin/cargo-tarpaulin
40-
key: tarpaulin-${{ runner.os }}-${{ env.TARPAULIN_VERSION }}
41-
restore-keys: |
42-
tarpaulin-${{ runner.os }}-
42+
path: ~/.cargo/bin/cargo-llvm-cov
43+
key: cargo-llvm-cov-${{ runner.os }}-${{ env.CARGO_LLVM_COV_VERSION }}
4344

44-
- name: Install tarpaulin
45+
- name: Install cargo-llvm-cov
4546
run: |
46-
if ! command -v cargo-tarpaulin &> /dev/null; then
47-
cargo install cargo-tarpaulin --locked --version "${TARPAULIN_VERSION}"
47+
installed_version=""
48+
if command -v cargo-llvm-cov &> /dev/null; then
49+
installed_version="$(cargo llvm-cov --version)"
50+
installed_version="${installed_version#cargo-llvm-cov }"
51+
fi
52+
53+
if [ "$installed_version" != "$CARGO_LLVM_COV_VERSION" ]; then
54+
cargo install cargo-llvm-cov --locked --version "${CARGO_LLVM_COV_VERSION}"
4855
fi
4956
5057
- name: Install just
@@ -66,7 +73,7 @@ jobs:
6673
ls -la coverage/ || true
6774
6875
if [ ! -f coverage/cobertura.xml ]; then
69-
echo "::error::coverage/cobertura.xml not found. Tarpaulin failed to generate XML output."
76+
echo "::error::coverage/cobertura.xml not found. cargo-llvm-cov failed to generate XML output."
7077
exit 2
7178
fi
7279
echo "::notice::Coverage report generated successfully: $(wc -l < coverage/cobertura.xml) lines"

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ just fix # apply auto-fixes (mutating)
304304
just ci # lint + tests + examples + bench compile
305305
```
306306

307+
For coverage commands and report locations, see [`docs/COVERAGE.md`](docs/COVERAGE.md).
307308
For the full set of developer commands, see `just --list` and `AGENTS.md`.
308309

309310
## 📝 Citation

docs/COVERAGE.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Coverage
2+
3+
la-stack uses `cargo-llvm-cov` for local and CI coverage. Coverage runs use
4+
Rust's LLVM source-based instrumentation with the same core test selection in
5+
both environments:
6+
7+
```bash
8+
cargo llvm-cov --features exact --workspace --lib --tests
9+
```
10+
11+
## Local HTML
12+
13+
Generate the local developer report with:
14+
15+
```bash
16+
just coverage
17+
```
18+
19+
The HTML report is written to:
20+
21+
```text
22+
target/llvm-cov/html/index.html
23+
```
24+
25+
The report opens automatically after generation.
26+
27+
## CI XML
28+
29+
Generate the CI-compatible Cobertura report with:
30+
31+
```bash
32+
just coverage-ci
33+
```
34+
35+
The XML report is written to:
36+
37+
```text
38+
coverage/cobertura.xml
39+
```
40+
41+
The Codecov workflow installs Rust's `llvm-tools-preview` component, installs
42+
`cargo-llvm-cov`, caches the installed cargo binary by version, runs
43+
`just coverage-ci`, verifies `coverage/cobertura.xml`, uploads that file to
44+
Codecov, and archives the full `coverage/` directory. Local setup via
45+
`just setup-tools` installs the same Rust component and cargo subcommand.
46+
47+
## Migration Notes
48+
49+
- Keep `just coverage-ci` as the single source of truth for CI coverage
50+
arguments; workflows should install tools and upload artifacts, not duplicate
51+
the coverage command.
52+
- Use `--cobertura --output-path coverage/cobertura.xml` for services that
53+
consume Cobertura XML.
54+
- Use `--open --output-dir target/llvm-cov` for local reports.
55+
- Preserve the crate's full coverage surface with `--features exact
56+
--workspace --lib --tests`.
57+
- `cargo-llvm-cov` excludes workspace `tests/`, `examples/`, and `benches/`
58+
source files from reports by default, while still allowing integration tests
59+
to exercise library code. This matches the intended reporting surface here:
60+
library implementation coverage, not test harness coverage.
61+
- Doc-test coverage remains intentionally disabled because `cargo-llvm-cov`
62+
marks that path as unstable.

justfile

Lines changed: 28 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,23 @@
66
# Use bash with strict error handling for all recipes
77
set shell := ["bash", "-euo", "pipefail", "-c"]
88

9+
cargo_llvm_cov_version := "0.8.5"
10+
911
# Internal helpers: ensure external tooling is installed
1012
_ensure-actionlint:
1113
#!/usr/bin/env bash
1214
set -euo pipefail
1315
command -v actionlint >/dev/null || { echo "❌ 'actionlint' not found. See 'just setup' or https://github.com/rhysd/actionlint"; exit 1; }
1416

17+
_ensure-cargo-llvm-cov:
18+
#!/usr/bin/env bash
19+
set -euo pipefail
20+
if ! command -v cargo-llvm-cov >/dev/null; then
21+
echo "❌ 'cargo-llvm-cov' not found. See 'just setup-tools' or install:"
22+
echo " cargo install --locked cargo-llvm-cov --version {{cargo_llvm_cov_version}}"
23+
exit 1
24+
fi
25+
1526
_ensure-git-cliff:
1627
#!/usr/bin/env bash
1728
set -euo pipefail
@@ -172,7 +183,7 @@ ci: check bench-compile test-all examples
172183
# Clean build artifacts
173184
clean:
174185
cargo clean
175-
rm -rf target/tarpaulin
186+
rm -rf target/llvm-cov
176187
rm -rf coverage
177188

178189
# Code quality and formatting
@@ -183,41 +194,29 @@ clippy:
183194
clippy-exact:
184195
cargo clippy --features exact --all-targets -- -D warnings -W clippy::pedantic
185196

186-
# Coverage (cargo-tarpaulin)
197+
# Coverage (cargo-llvm-cov)
187198
#
188-
# Common tarpaulin arguments for all coverage runs
189-
# Note: -t 300 sets per-test timeout to 5 minutes (needed for slow CI environments)
190-
_coverage_base_args := '''--exclude-files 'benches/*' --exclude-files 'examples/*' \
191-
--features exact \
199+
# Common cargo-llvm-cov arguments for all coverage runs.
200+
_coverage_base_args := '''--features exact \
192201
--workspace --lib --tests \
193-
-t 300 --verbose --implicit-test-threads'''
202+
--verbose'''
194203

195204
# Coverage analysis for local development (HTML output)
196-
coverage:
205+
coverage: _ensure-cargo-llvm-cov
197206
#!/usr/bin/env bash
198207
set -euo pipefail
199208

200-
if ! command -v cargo-tarpaulin >/dev/null 2>&1; then
201-
echo "cargo-tarpaulin not found. Install with: cargo install cargo-tarpaulin"
202-
exit 1
203-
fi
204-
205-
mkdir -p target/tarpaulin
206-
cargo tarpaulin {{_coverage_base_args}} --out Html --output-dir target/tarpaulin
207-
echo "Coverage report generated: target/tarpaulin/tarpaulin-report.html"
209+
mkdir -p target/llvm-cov
210+
cargo llvm-cov {{_coverage_base_args}} --open --output-dir target/llvm-cov
211+
echo "Coverage report generated: target/llvm-cov/html/index.html"
208212

209213
# Coverage analysis for CI (XML output for codecov/codacy)
210-
coverage-ci:
214+
coverage-ci: _ensure-cargo-llvm-cov
211215
#!/usr/bin/env bash
212216
set -euo pipefail
213217

214-
if ! command -v cargo-tarpaulin >/dev/null 2>&1; then
215-
echo "cargo-tarpaulin not found. Install with: cargo install cargo-tarpaulin"
216-
exit 1
217-
fi
218-
219218
mkdir -p coverage
220-
cargo tarpaulin {{_coverage_base_args}} --out Xml --output-dir coverage
219+
cargo llvm-cov {{_coverage_base_args}} --cobertura --output-path coverage/cobertura.xml
221220

222221
# Default recipe shows available commands
223222
default:
@@ -429,7 +428,7 @@ setup-tools:
429428
echo "❌ 'rustup' not found. Install Rust via https://rustup.rs and re-run: just setup-tools"
430429
exit 1
431430
fi
432-
rustup component add clippy rustfmt rust-docs rust-src
431+
rustup component add clippy rustfmt rust-docs rust-src llvm-tools-preview
433432
echo ""
434433

435434
echo "Ensuring cargo tools..."
@@ -454,21 +453,17 @@ setup-tools:
454453
echo " ✓ typos"
455454
fi
456455

457-
if ! have cargo-tarpaulin; then
458-
if [[ "$os" == "Linux" ]]; then
459-
echo " ⏳ Installing cargo-tarpaulin (cargo)..."
460-
cargo install --locked cargo-tarpaulin
461-
else
462-
echo " ⚠️ Skipping cargo-tarpaulin install on $os (coverage is typically Linux-only)"
463-
fi
456+
if ! have cargo-llvm-cov; then
457+
echo " ⏳ Installing cargo-llvm-cov {{cargo_llvm_cov_version}} (cargo)..."
458+
cargo install --locked cargo-llvm-cov --version {{cargo_llvm_cov_version}}
464459
else
465-
echo " ✓ cargo-tarpaulin"
460+
echo " ✓ cargo-llvm-cov"
466461
fi
467462

468463
echo ""
469464
echo "Verifying required commands are available..."
470465
missing=0
471-
for cmd in uv jq taplo yamllint shfmt shellcheck actionlint node npx typos git-cliff; do
466+
for cmd in uv jq taplo yamllint shfmt shellcheck actionlint node npx typos git-cliff cargo-llvm-cov; do
472467
if have "$cmd"; then
473468
echo " ✓ $cmd"
474469
else

0 commit comments

Comments
 (0)