Self-hosted coverage and test reporting with GitHub Actions. Uses GitHub Artifacts for storage — no external service or Codecov token required.
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests with coverage
run: npm test -- --coverage
- name: Codecov Action
uses: getsentry/codecov-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}| Format | File Patterns | Languages/Tools |
|---|---|---|
| Clover XML | clover.xml |
Istanbul/NYC (JS/TS), PHPUnit, OpenClover |
| Cobertura XML | coverage.xml, cobertura.xml |
coverage.py (Python), Coverlet (.NET), PHPUnit |
| JaCoCo XML | jacoco.xml |
Java, Kotlin, Scala |
| LCOV | lcov.info, *.lcov |
c8, lcov (C/C++), grcov (Rust), gcov |
| Istanbul JSON | coverage-final.json |
Jest, Vitest, NYC (JS/TS) |
| Go Coverage | coverage.out, cover.out |
go test -coverprofile |
| Codecov JSON | codecov.json |
cargo-llvm-cov (Rust), custom tools |
| Input | Description | Required | Default |
|---|---|---|---|
token |
GitHub token for API access and artifacts | Yes | — |
base-branch |
Base branch to compare results against | No | main |
enable-tests |
Enable test results reporting | No | true |
enable-coverage |
Enable coverage reporting | No | true |
post-pr-comment |
Post results as a PR comment | No | false |
| Input | Description | Required | Default |
|---|---|---|---|
files |
Comma-separated list of coverage files | No | — |
directory |
Folder to search for coverage files | No | . |
exclude |
Comma-separated patterns to exclude | No | — |
coverage-format |
Format hint: auto, clover, cobertura, jacoco, lcov, istanbul, go, codecov |
No | auto |
disable-search |
Disable auto-search, use only explicit files |
No | false |
| Input | Description | Required | Default |
|---|---|---|---|
fail-ci-if-error |
Fail if coverage processing errors (e.g., parsing failures, missing files) | No | false |
handle-no-reports-found |
Don't fail if no coverage found | No | false |
verbose |
Enable verbose logging | No | false |
| Input | Description | Required | Default |
|---|---|---|---|
target-project |
Target project coverage % (or auto to use base branch coverage) |
No | — |
threshold-project |
Allowed project coverage drop % (only used when target is auto) |
No | — |
target-patch |
Target patch coverage % for changed lines | No | 80 |
fail-on-error |
Fail CI if coverage thresholds are not met (distinct from fail-ci-if-error) |
No | false |
When thresholds are not configured, status checks report coverage metrics without enforcing pass/fail.
| Input | Description | Required | Default |
|---|---|---|---|
flags |
Comma-separated flags to tag coverage (e.g., unittests,frontend) |
No | — |
name |
Custom name for this coverage upload | No | — |
| Input | Description | Required | Default |
|---|---|---|---|
junit-xml-pattern |
Glob pattern for JUnit XML files | No | ./**/*.junit.xml |
| Output | Description |
|---|---|
total-tests |
Total number of tests run |
passed-tests |
Number of passed tests |
failed-tests |
Number of failed tests |
test-pass-rate |
Percentage of tests that passed |
tests-added |
Tests added compared to base branch |
tests-removed |
Tests removed compared to base branch |
tests-fixed |
Tests changed from failing to passing |
tests-broken |
Tests changed from passing to failing |
| Output | Description |
|---|---|
line-coverage |
Line coverage percentage |
branch-coverage |
Branch coverage percentage |
coverage-change |
Change in line coverage vs base branch |
branch-coverage-change |
Change in branch coverage vs base branch |
coverage-improved |
Whether coverage improved (true/false) |
coverage-format |
The detected/used coverage format |
- name: Codecov Action
uses: getsentry/codecov-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}- name: Codecov Action
uses: getsentry/codecov-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: ./coverage/lcov.info,./backend/coverage.xml
disable-search: true
flags: unittests
name: my-coverage
verbose: true- name: Run tests
run: pytest --cov=src --cov-report=xml
- name: Codecov Action
uses: getsentry/codecov-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
directory: ./
coverage-format: cobertura
fail-ci-if-error: true- name: Build and test
run: ./gradlew test jacocoTestReport
- name: Codecov Action
uses: getsentry/codecov-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: ./build/reports/jacoco/test/jacocoTestReport.xml
coverage-format: jacoco- name: Run tests
run: go test -coverprofile=coverage.out ./...
- name: Codecov Action
uses: getsentry/codecov-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: ./coverage.out
coverage-format: go- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov
- name: Run tests with coverage
run: cargo llvm-cov --codecov --output-path codecov.json
- name: Codecov Action
uses: getsentry/codecov-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: ./codecov.json
coverage-format: codecov- name: Run tests
run: npm test -- --coverage --coverageReporters=lcov
- name: Codecov Action
uses: getsentry/codecov-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
directory: ./coverage
coverage-format: lcov- name: Frontend Coverage
uses: getsentry/codecov-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
directory: ./frontend/coverage
flags: frontend
name: frontend-coverage
- name: Backend Coverage
uses: getsentry/codecov-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
directory: ./backend/coverage
flags: backend
name: backend-coverageUse built-in threshold enforcement with GitHub status checks:
- name: Codecov Action
uses: getsentry/codecov-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
target-project: auto # Use base branch coverage as target
threshold-project: 1 # Allow up to 1% coverage drop
target-patch: 80 # Require 80% coverage on changed lines
fail-on-error: true # Fail CI if thresholds not metThis creates two status checks (codecov/project and codecov/patch) that:
- Appear on commits and PRs
- Can be required via branch protection rules
- Provide clear pass/fail feedback
The action searches for coverage files using:
- Explicit files: If
filesinput is provided - Auto-discovery: Searches
directoryfor known coverage file patterns - Format detection: Auto-detects format from file content
Multiple coverage files are parsed and aggregated:
- Supports mixing formats (e.g., frontend LCOV + backend Cobertura)
- Calculates unified line, branch, and method coverage
Results are stored as GitHub Artifacts:
coverage-results-{branch}— Aggregated coverage datatest-results-{branch}— Aggregated test results
On PRs or feature branches:
- Downloads latest results from base branch
- Compares current vs baseline
- Calculates deltas
- Job Summary: Always generated in Actions UI
- PR Comment: Optional detailed comment on PRs
The action creates GitHub commit status checks that appear on commits and PRs:
| Status Context | Description |
|---|---|
codecov/project |
Overall project coverage status (pass/fail based on target) |
codecov/patch |
Coverage for changed lines in the PR |
These status checks:
- Show as green checkmarks or red X marks on commits and PRs
- Can be used in branch protection rules to require coverage thresholds
- Provide immediate feedback on coverage quality
Display your CI status in your README using GitHub's workflow badge:
| Format | Typical File | Common Test Frameworks |
|---|---|---|
| JUnit XML | *.junit.xml |
Jest, Vitest, Mocha, pytest, JUnit, NUnit, PHPUnit |
Jest / Vitest:
{
"reporters": ["default", ["jest-junit", { "outputFile": "report.junit.xml" }]]
}pytest:
pytest --junitxml=report.junit.xml{
"coverageReporters": ["lcov", "clover", "json"]
}pytest --cov=src --cov-report=xml # Cobertura formatjacocoTestReport {
reports {
xml.required = true
}
}go test -coverprofile=coverage.out ./...# Install cargo-llvm-cov
cargo install cargo-llvm-cov
# Generate Codecov JSON format
cargo llvm-cov --codecov --output-path codecov.json
# Or generate LCOV format
cargo llvm-cov --lcov --output-path lcov.infodotnet test --collect:"XPlat Code Coverage"
# or
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=coberturaYou can configure status check thresholds using a .github/coverage.yml file in your repository. Action inputs take precedence over the config file, allowing you to override settings per-workflow.
# .github/coverage.yml
coverage:
status:
project:
target: 80 # Target coverage percentage (or "auto" to use base branch)
threshold: 1 # Allowed coverage drop when using "auto" (supports "1%" or 1)
informational: false # When true, status check reports but never fails the build
patch:
target: 90 # Target coverage for changed lines
informational: false
ignore:
- "**/*.test.ts" # Patterns to exclude from coverage
- "**/fixtures/**"
# Enable PR comments from config (alternative to post-pr-comment input)
comment: true| Option | Description |
|---|---|
status.project.target |
Target project coverage % (number or "auto") |
status.project.threshold |
Allowed drop from base branch when target is "auto". Supports number (1) or string ("1%") |
status.project.informational |
When true, status check reports but never fails the build (advisory mode) |
status.patch.target |
Target coverage % for changed lines |
status.patch.informational |
When true, patch status check is advisory-only |
ignore |
Glob patterns to exclude from coverage calculations |
comment |
Enable PR comments. Set to true, false, or {} (object form for future options) |
This action also supports the standard Codecov YAML format with nested default keys:
# .github/codecov.yml (Codecov-compatible format)
coverage:
status:
project:
default:
target: auto
threshold: 10% # Percentage strings are supported
informational: true
patch:
default:
target: 80
ignore:
- "tests/**"
comment: trueBoth formats work identically—use whichever style you prefer.
permissions:
contents: read # Read repository contents
actions: read # Read workflow runs and artifacts
pull-requests: write # Post PR comments (if enabled)
statuses: write # Create commit status checksIf you're migrating from the official Codecov action:
| Codecov Input | This Action |
|---|---|
fail_ci_if_error |
fail-ci-if-error |
files |
files |
directory |
directory |
exclude |
exclude |
flags |
flags |
name |
name |
verbose |
verbose |
handle-no-reports-found |
handle-no-reports-found |
Note: This action doesn't require a Codecov token—it uses GitHub's native artifacts for storage.