Share a Rust coverage report
Rust's coverage story got good and stayed awkward to share. Both of the
tools people use produce a real HTML report, and both of them produce it
inside target/ on whichever machine ran the tests.
Two tools, two output shapes
# LLVM source-based — more accurate, writes a directory
cargo llvm-cov --html
# → target/llvm-cov/html/index.html + per-file pages
# tarpaulin — writes ONE self-contained file
cargo tarpaulin --out Html
# → tarpaulin-report.html
Use cargo llvm-cov when you care about the numbers being right: it
uses LLVM instrumentation and handles generics and macro expansion far
better than tarpaulin's instruction-counting approach.
Use cargo tarpaulin --out Html when you care about the report being
easy to move, because a single file publishes in one call with nothing to
upload alongside it.
Publishing the tarpaulin single file
cargo tarpaulin --out Html || true
curl -fsS -X PATCH "https://commareports.com/api/v1/reports/$REPORT_ID" \
-H "Authorization: Bearer $COMMA_API_TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -n --rawfile html tarpaulin-report.html \
--arg title "Coverage — $(git rev-parse --short HEAD)" \
'{title: $title, html: $html}')"
One call, no assets. Use a scoped token
(reports:write) from CI secrets and PATCH a saved report id, so one
URL accumulates a revision per run instead of a new orphan link per build.
The || true matters — tarpaulin runs your suite, so a failing test fails
the step. Add if: always() in GitHub Actions. See
publishing from CI.
Publishing the llvm-cov directory
target/llvm-cov/html/
├── index.html
├── coverage/…/src/lib.rs.html # one page per source file
└── style.css
Drag that folder (or a zip of it) into
Comma: index.html becomes the report body,
the per-file pages upload alongside it, and relative references are
rewritten so the drill-down from summary to annotated source resolves.
From CI, the entry page goes in the PATCH body and the per-file pages go
up through POST /api/v1/reports/$REPORT_ID/assets as base64 — one call
per file, worth scripting once. See the API reference.
What the URL changes
- The PR links the coverage. Reviewers click instead of running
cargo llvm-covlocally, which on a large workspace is not a quick thing to ask. - Review lands on the code. Select the uncovered arm of a
match, leave a thread — "this is the error path, it needs a test" — and it stays anchored there across runs. See commenting on HTML. - Revision diffs beat the percentage. Two runs, one diff, which lines changed coverage. A Rust workspace's coverage number moves for uninteresting reasons constantly; the diff doesn't.
Who can see it
The coverage report contains your source code. Access is per report — private, your team, any signed-in user with the link, or public — and team or private is the right default for a private repo. Domain-gating is Enterprise. See sharing & access control.
Limits
- Entry HTML: 5 MB. Tarpaulin inlines every covered source file, so a large workspace can exceed it — generate per-crate reports and publish the crates people review. Assets: 25 MB per file, 250 MB and 500 files total.
- Scripts run, sandboxed (
allow-scripts, noallow-same-origin). - 60 requests/minute per token.
Try it
Comma is free — unlimited reports, unlimited commenters, unlimited revision history.