Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions .github/workflows/codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
name: Code Coverage
runs-on: ubuntu-latest
env:
TARPAULIN_VERSION: "0.32.8"
CARGO_LLVM_COV_VERSION: "0.8.5"
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
Expand All @@ -33,18 +33,25 @@ jobs:
with:
cache: true # toolchain/components are specified in rust-toolchain.toml

- name: Cache tarpaulin
- name: Install LLVM coverage tools
run: rustup component add llvm-tools-preview

- name: Cache cargo-llvm-cov
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.cargo/bin/cargo-tarpaulin
key: tarpaulin-${{ runner.os }}-${{ env.TARPAULIN_VERSION }}
restore-keys: |
tarpaulin-${{ runner.os }}-
path: ~/.cargo/bin/cargo-llvm-cov
key: cargo-llvm-cov-${{ runner.os }}-${{ env.CARGO_LLVM_COV_VERSION }}

- name: Install tarpaulin
- name: Install cargo-llvm-cov
run: |
if ! command -v cargo-tarpaulin &> /dev/null; then
cargo install cargo-tarpaulin --locked --version "${TARPAULIN_VERSION}"
installed_version=""
if command -v cargo-llvm-cov &> /dev/null; then
installed_version="$(cargo llvm-cov --version)"
installed_version="${installed_version#cargo-llvm-cov }"
fi

if [ "$installed_version" != "$CARGO_LLVM_COV_VERSION" ]; then
cargo install cargo-llvm-cov --locked --version "${CARGO_LLVM_COV_VERSION}"
fi

- name: Install just
Expand All @@ -66,7 +73,7 @@ jobs:
ls -la coverage/ || true

if [ ! -f coverage/cobertura.xml ]; then
echo "::error::coverage/cobertura.xml not found. Tarpaulin failed to generate XML output."
echo "::error::coverage/cobertura.xml not found. cargo-llvm-cov failed to generate XML output."
exit 2
fi
echo "::notice::Coverage report generated successfully: $(wc -l < coverage/cobertura.xml) lines"
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ just fix # apply auto-fixes (mutating)
just ci # lint + tests + examples + bench compile
```

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

## 📝 Citation
Expand Down
62 changes: 62 additions & 0 deletions docs/COVERAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Coverage

la-stack uses `cargo-llvm-cov` for local and CI coverage. Coverage runs use
Rust's LLVM source-based instrumentation with the same core test selection in
both environments:

```bash
cargo llvm-cov --features exact --workspace --lib --tests
```

## Local HTML

Generate the local developer report with:

```bash
just coverage
```

The HTML report is written to:

```text
target/llvm-cov/html/index.html
```

The report opens automatically after generation.

## CI XML

Generate the CI-compatible Cobertura report with:

```bash
just coverage-ci
```

The XML report is written to:

```text
coverage/cobertura.xml
```

The Codecov workflow installs Rust's `llvm-tools-preview` component, installs
`cargo-llvm-cov`, caches the installed cargo binary by version, runs
`just coverage-ci`, verifies `coverage/cobertura.xml`, uploads that file to
Codecov, and archives the full `coverage/` directory. Local setup via
`just setup-tools` installs the same Rust component and cargo subcommand.

## Migration Notes

- Keep `just coverage-ci` as the single source of truth for CI coverage
arguments; workflows should install tools and upload artifacts, not duplicate
the coverage command.
- Use `--cobertura --output-path coverage/cobertura.xml` for services that
consume Cobertura XML.
- Use `--open --output-dir target/llvm-cov` for local reports.
- Preserve the crate's full coverage surface with `--features exact
--workspace --lib --tests`.
- `cargo-llvm-cov` excludes workspace `tests/`, `examples/`, and `benches/`
source files from reports by default, while still allowing integration tests
to exercise library code. This matches the intended reporting surface here:
library implementation coverage, not test harness coverage.
- Doc-test coverage remains intentionally disabled because `cargo-llvm-cov`
marks that path as unstable.
61 changes: 28 additions & 33 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,23 @@
# Use bash with strict error handling for all recipes
set shell := ["bash", "-euo", "pipefail", "-c"]

cargo_llvm_cov_version := "0.8.5"

# Internal helpers: ensure external tooling is installed
_ensure-actionlint:
#!/usr/bin/env bash
set -euo pipefail
command -v actionlint >/dev/null || { echo "❌ 'actionlint' not found. See 'just setup' or https://github.com/rhysd/actionlint"; exit 1; }

_ensure-cargo-llvm-cov:
#!/usr/bin/env bash
set -euo pipefail
if ! command -v cargo-llvm-cov >/dev/null; then
echo "❌ 'cargo-llvm-cov' not found. See 'just setup-tools' or install:"
echo " cargo install --locked cargo-llvm-cov --version {{cargo_llvm_cov_version}}"
exit 1
fi

_ensure-git-cliff:
#!/usr/bin/env bash
set -euo pipefail
Expand Down Expand Up @@ -172,7 +183,7 @@ ci: check bench-compile test-all examples
# Clean build artifacts
clean:
cargo clean
rm -rf target/tarpaulin
rm -rf target/llvm-cov
rm -rf coverage

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

# Coverage (cargo-tarpaulin)
# Coverage (cargo-llvm-cov)
#
# Common tarpaulin arguments for all coverage runs
# Note: -t 300 sets per-test timeout to 5 minutes (needed for slow CI environments)
_coverage_base_args := '''--exclude-files 'benches/*' --exclude-files 'examples/*' \
--features exact \
# Common cargo-llvm-cov arguments for all coverage runs.
_coverage_base_args := '''--features exact \
--workspace --lib --tests \
-t 300 --verbose --implicit-test-threads'''
--verbose'''

# Coverage analysis for local development (HTML output)
coverage:
coverage: _ensure-cargo-llvm-cov
#!/usr/bin/env bash
set -euo pipefail

if ! command -v cargo-tarpaulin >/dev/null 2>&1; then
echo "cargo-tarpaulin not found. Install with: cargo install cargo-tarpaulin"
exit 1
fi

mkdir -p target/tarpaulin
cargo tarpaulin {{_coverage_base_args}} --out Html --output-dir target/tarpaulin
echo "Coverage report generated: target/tarpaulin/tarpaulin-report.html"
mkdir -p target/llvm-cov
cargo llvm-cov {{_coverage_base_args}} --open --output-dir target/llvm-cov
echo "Coverage report generated: target/llvm-cov/html/index.html"

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

if ! command -v cargo-tarpaulin >/dev/null 2>&1; then
echo "cargo-tarpaulin not found. Install with: cargo install cargo-tarpaulin"
exit 1
fi

mkdir -p coverage
cargo tarpaulin {{_coverage_base_args}} --out Xml --output-dir coverage
cargo llvm-cov {{_coverage_base_args}} --cobertura --output-path coverage/cobertura.xml

# Default recipe shows available commands
default:
Expand Down Expand Up @@ -429,7 +428,7 @@ setup-tools:
echo "❌ 'rustup' not found. Install Rust via https://rustup.rs and re-run: just setup-tools"
exit 1
fi
rustup component add clippy rustfmt rust-docs rust-src
rustup component add clippy rustfmt rust-docs rust-src llvm-tools-preview
echo ""

echo "Ensuring cargo tools..."
Expand All @@ -454,21 +453,17 @@ setup-tools:
echo " ✓ typos"
fi

if ! have cargo-tarpaulin; then
if [[ "$os" == "Linux" ]]; then
echo " ⏳ Installing cargo-tarpaulin (cargo)..."
cargo install --locked cargo-tarpaulin
else
echo " ⚠️ Skipping cargo-tarpaulin install on $os (coverage is typically Linux-only)"
fi
if ! have cargo-llvm-cov; then
echo " ⏳ Installing cargo-llvm-cov {{cargo_llvm_cov_version}} (cargo)..."
cargo install --locked cargo-llvm-cov --version {{cargo_llvm_cov_version}}
else
echo " ✓ cargo-tarpaulin"
echo " ✓ cargo-llvm-cov"
fi

echo ""
echo "Verifying required commands are available..."
missing=0
for cmd in uv jq taplo yamllint shfmt shellcheck actionlint node npx typos git-cliff; do
for cmd in uv jq taplo yamllint shfmt shellcheck actionlint node npx typos git-cliff cargo-llvm-cov; do
if have "$cmd"; then
echo " ✓ $cmd"
else
Expand Down
Loading