Skip to content

Coverage tracking

Pullminder can track code coverage changes across your pull requests. When coverage reports are uploaded from your CI pipeline, the dashboard shows which new lines are covered by tests, which are not, and how the overall coverage picture is changing.

Coverage tracking is available on the Team plan and above (Trial and Enterprise plans also include it). Starter plan users will see an upgrade prompt in the dashboard.

If your organization is on the Starter plan, see the Billing page to upgrade.

Pullminder accepts two coverage report formats. The format is detected automatically from the filename.

FormatTypical filenamesNotes
LCOVlcov.info, coverage.infoStandard LCOV tracefile format. Generated by most JavaScript/TypeScript coverage tools (Istanbul, c8, Vitest) and many other languages.
Go coverage profilecover.out, coverage.outGo’s built-in coverage profile format (go test -coverprofile).

Uploading reports in other formats (Cobertura XML, JaCoCo, Clover) is not currently supported. Convert them to LCOV before uploading — most coverage tools provide an LCOV reporter or a conversion utility.

LCOV reports use SF: (source file), DA: (line data), and end_of_record directives. Tools that emit LCOV-compatible output — even if the file extension is not .info — are supported as long as the filename contains “lcov” or ends with .info.

Go coverage profiles begin with a mode: header line (set, count, or atomic) followed by block coverage lines. Files ending in .out are parsed as Go profiles.

When multiple coverage files are present in an artifact, Pullminder parses each one and merges the results. Different artifacts can use different formats — the union of covered lines is computed across all of them.

The general pattern is:

  1. Run your test suite with coverage enabled.
  2. Save the coverage report as an artifact that Pullminder can download.
  3. Configure the artifact name in the Pullminder dashboard.

Pullminder receives a webhook when your CI workflow completes and automatically fetches the coverage artifact.

Generate a coverage report and upload it as a workflow artifact. Pullminder will pick it up after the workflow completes.

.github/workflows/test.yml
name: Test
on:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests with coverage
run: npx vitest --coverage
# This produces coverage/lcov.info with Vitest's default config.
- name: Upload coverage artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage/lcov.info

The artifact name (coverage-report) is the default that Pullminder looks for. If you use a different name, update it in the dashboard settings.

For a full Pullminder workflow that runs analysis alongside coverage, combine it with the ci command:

.github/workflows/pullminder.yml
name: Pullminder
on:
pull_request:
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run tests with coverage
run: npx vitest --coverage
- name: Upload coverage artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage/lcov.info
analyze:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Pullminder
run: curl -fsSL https://get.pullminder.com | sh
- name: Run analysis
run: pullminder ci --fail-on high
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

The if: always() on the artifact upload ensures the coverage report is uploaded even if some tests fail — you still want to see partial coverage data.

.circleci/config.yml
version: 2.1
jobs:
test:
docker:
- image: cimg/node:20.11
steps:
- checkout
- run:
name: Run tests with coverage
command: npx vitest --coverage
- store_artifacts:
path: coverage/lcov.info
destination: coverage-report
workflows:
pr-check:
jobs:
- test

CircleCI stores artifacts differently from GitHub Actions. Pullminder retrieves them via the CircleCI API after the pipeline completes. Set the artifact destination to match the name configured in your dashboard settings.

.gitlab-ci.yml
test:
stage: test
image: node:20
script:
- npx vitest --coverage
artifacts:
when: always
paths:
- coverage/lcov.info
expire_in: 7 days
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"

The when: always ensures the coverage artifact is saved regardless of test outcome. Pullminder fetches artifacts from GitLab after the pipeline finishes.

Coverage artifact names are configured per repository in the dashboard settings.

  1. Open the Settings page.
  2. Find the repository in the Repository management section.
  3. Set the Coverage artifacts field to the name of your uploaded artifact.

The default artifact name is coverage-report. You can configure multiple artifacts by separating names with commas (e.g. unit-coverage,integration-coverage). When multiple artifacts are present, Pullminder downloads all of them and merges the covered-line data — a line is considered covered if any artifact reports it as covered.

Artifacts are capped at 50 MB. If your coverage report exceeds this, consider excluding generated or vendor directories from your coverage instrumentation.

The coverage section on the PR detail page shows two metrics:

Delta coverage — the percentage of newly added lines in the PR that are covered by tests. This is the primary metric: it tells reviewers whether the new code has tests. The delta is color-coded:

  • Green (≥80%): most new lines are covered.
  • Amber (50–79%): some new lines are covered, but coverage gaps exist.
  • Red (<50%): few or none of the new lines are covered.

Overall coverage — the total coverage percentage across the entire repository. This provides context for the delta.

Below the summary, a per-file breakdown shows each changed file’s coverage percentage, how many lines are covered versus uncovered, and the delta for that file.

Pullminder compares the coverage report against the PR diff:

  1. For each changed file in the diff, only added lines are considered.
  2. Each added line is checked against the coverage report: if the line was executed during tests, it is counted as covered.
  3. Files that appear in the diff but are absent from the coverage report are treated as 0% covered.
  4. Test files (*.test.*, *.spec.*, *_test.go), generated files (*.pb.go, *.gen.go, *_generated.*), and non-source files (markdown, YAML, JSON, etc.) are excluded from the delta.

If your project is a monorepo where coverage file paths don’t match diff paths exactly (e.g. coverage reports internal/foo.go but the diff shows apps/api/internal/foo.go), Pullminder applies a suffix-based fallback to match them.

When no coverage data is available for a PR, the dashboard shows a prompt to set up coverage tracking. This happens when no workflow with the configured artifact name has completed for the PR’s head commit.