Don't go undercov! Track your test coverage and check for under-coverage with this Git standalone tool. 🕵️
undercov is a command-line tool built with Go that helps you track your test coverage. It works standalone and stores the coverage data inside a branch in your Git repository. With undercov, you can easily check on your CI pipeline if your changes meet the required coverage thresholds, ensuring that your code is well-tested and maintainable.
- Supports monorepos with multiple coverage files.
- Checks for coverage regressions in pull requests.
It is supposed to be an alternative to codecov and coveralls that is hosted within your own Git repository and does not require any external service.
To make undercov easily usable with Forgejo, Gitea or GitHub Actions, you can use undercov-action.
This repository is mirrored to GitHub: https://github.com/openscript-ch/undercov
When you run undercov with the --push option, it will create or update a branch in your Git repository (default: coverage) and store the coverage files in a structured way. The coverage files are stored in a separate branch in the same Git repository. Then it uses that branch data to check for coverage regressions.
Coverage snapshots are keyed by a logical ref name:
current-ref: where the current run stores coverage data.target-ref: the baseline ref used for regression checks.
If you do not set them explicitly, undercov auto-detects pull request refs on GitHub Actions (GITHUB_HEAD_REF and GITHUB_BASE_REF) so regression checks compare PR coverage against the PR target branch coverage. Outside pull requests, current-ref falls back to the current branch context and target-ref falls back to current-ref.
The structure of the branch with the coverage data is as follows:
- .undercov/ <- This is the root directory for undercov data. The name is hardcoded and should not be changed.
- [...branch-name/]/ <- This is the directory for each branch. If the branch name has slashes, it leads to nested directories. For example, if the branch name is `feature/coverage`, the path would be `.undercov/feature/coverage/`.
- [base64-encoded-file-path].lcov <- Each coverage file is stored with its path encoded in base64, so multiple files can be stored without conflicts. The file extension is `.lcov` to indicate that it is a coverage file.
undercov is a single multi-platform binary that you can download from the release page.
threshold: The minimum coverage percentage required to pass the check.files: The glob pattern to locate the coverage files (e.g.,**/coverage/lcov.info).branch: The git branch where undercov coverage snapshots are stored (default:coverage).current-ref: The logical ref name used to store the current coverage snapshot. Defaults to PR head ref in GitHub PR builds, otherwise falls back to the current git branch context.target-ref: The logical ref name used as the regression baseline. Defaults to PR base ref in GitHub PR builds, otherwise falls back tocurrent-ref.push: Push the updated coverage branch to a remote (default:false).remote: The remote used whenpushis enabled (default:origin).push-force-with-lease: Push with--force-with-leasewhenpushis enabled (default:false).
By default, undercov stores coverage snapshots in the local coverage branch only. Enable push in CI to publish updates to the remote branch. If your remote branch can diverge (for example with parallel jobs), push-force-with-lease allows replacing the remote tip while still protecting against unexpected concurrent updates.
- Clone the repository
- Download Go dependencies
go mod download- Run lint checks with the same golangci-lint version used in CI
make lint- Run tests
make testIf you prefer to run golangci-lint directly without make, use:
go run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2 runReleases are managed with git-cliff and Forgejo workflows.
- Conventional commits are pushed to
main. - The
version.ymlworkflow computes the next semantic version, updatesCHANGELOG.mdwith git-cliff, and creates or updates a release PR. - When the release PR is merged,
tag.ymlcreates and pushes the correspondingv*tag. - The tag triggers
release.yml, which builds binaries for Linux (x86_64,arm64,armv7) and Windows (x86_64,arm64) and uploads artifacts plus SHA256 checksums.
Check out the milestones and issues for upcoming features and improvements here: https://codeberg.org/openscript-ch/undercov/milestones