A fast, flexible CLI tool for enforcing test coverage thresholds in Go projects.
Fail builds when coverage drops below acceptable thresholds β by file, statement, or block level.
- Enforce minimum coverage thresholds for files, packages, and the entire project.
- π Check coverage only on changed files in git diff.
- Supports statement and block coverage separately.
- Native
table|json|yaml|md|html|csv|tsvoutput. - Configurable via a
.go-covercheck.ymlor CLI flags. - Sorting and colored table output.
- Colored
jsonandyamloutput. - Built-in file or package regex filtering with
--skip. - Save and compare against historical results from a commit, branch, tag, or user defined label.
- Works seamlessly in CI/CD environments.
The following items are noteworthy and not (currently) supported.
- Does not support configurable profile block count (how many times a section of code was hit) thresholds. The assumption
is any value
>=1is enough. - Table style is not configurable.
- Color codes (see Color Legend) are not configurable.
- Severity weights (see Color Legend) are not configurable.
I had access to a similar tool in a previous job. I took it for granted. After leaving this job and continuing to work in Go, I realized that I needed that tool in my life again. The closest that I was able to find online is gocovergate. I forked it to make it configurable.
This held me over for a little while. However, I still wanted the functionality that I had access to before, and I did not want to put a lot of effort into creating it.
So, I used generative AI as a starting point and a few dozen prompts later, go-covercheck was born. Many hours later,
the first release was ready.
There are several ways to install go-covercheck. Choose the one that best fits your needs.
You can download official releases from the releases page.
All releases are built with the latest Go version and include build information, such as the version number and commit sha.
After downloading, you can place the binary in your PATH to use it globally. If you are on Linux, Freebsd or Mac OS,
don't forget to make it executable:
chmod +x go-covercheckYou can install go-covercheck using Homebrew:
brew install mach6/tap/go-covercheckYou can install go-covercheck using the go install command, if you don't care about official releases. Versions installed
this way are not stamped with build information, such as the version number or commit sha.
go install github.com/mach6/go-covercheck/cmd/go-covercheck@latestDocker images are available on the github container registry. You can pull the latest image with:
docker pull ghcr.io/mach6/go-covercheck:latestOr you can use a tag which maps to a specific version:
docker pull ghcr.io/mach6/go-covercheck:0.6.1Create a .go-coverheck.yml which defines the threshold requirements. You can create this file manually or use the
--init flag to generate a sample config file in the current directory.
- This step is optional but recommended.
- See full sample.
go-covercheck --initHere is a sample .go-covercheck.yml configuration file:
# Optional, global thresholds overriding the default (70 statements, 50 blocks)
statementThreshold: 65.0
blockThreshold: 60.0
# Optional, by total thresholds overriding the global values above
total:
statements: 75.0
blocks: 70.0Run the tests for a go project and produce a coverage.out.
go test ./... --coverprofile coverage.outUse the go-covercheck CLI to check the coverage against the thresholds defined in the config file or CLI flags.
$ go-covercheck coverage.out
βββββββββββββββββ¬βββββββββββββ¬ββββββββββββ¬ββββββββββββββ¬ββββββββββββ
β β STATEMENTS β BLOCKS β STATEMENT % β BLOCK % β
βββββββββββββββββΌβββββββββββββΌββββββββββββΌββββββββββββββΌββββββββββββ€
β BY FILE β β β β β
βββββββββββββββββΌβββββββββββββΌββββββββββββΌββββββββββββββΌββββββββββββ€
β cmd/foo.go β 0/1 β 0/1 β 0.0 β 0.0 β
β cmd/bar.go β 20/110 β 7/80 β 18.2 β 8.8 β
βββββββββββββββββΌβββββββββββββΌββββββββββββΌββββββββββββββΌββββββββββββ€
β BY PACKAGE β β β β β
βββββββββββββββββΌβββββββββββββΌββββββββββββΌββββββββββββββΌββββββββββββ€
β cmd β 20/111 β 7/81 β 18.0 β 8.6 β
βββββββββββββββββΌβββββββββββββΌββββββββββββΌββββββββββββββΌββββββββββββ€
β BY TOTAL β β β β β
βββββββββββββββββΌβββββββββββββΌββββββββββββΌββββββββββββββΌββββββββββββ€
β β 20/111 β 7/81 β 18.0 β 8.6 β
βββββββββββββββββ΄βββββββββββββ΄ββββββββββββ΄ββββββββββββββ΄ββββββββββββ
β Coverage check failed
β By File
[S] cmd/foo.go [+70.0% required for 70.0% threshold]
[B] cmd/foo.go [+50.0% required for 50.0% threshold]
[S] cmd/bar.go [+51.8% required for 70.0% threshold]
[B] cmd/bar.go [+41.2% required for 50.0% threshold]
β By Package
[S] cmd [+52.0% required for 70.0% threshold]
[B] cmd [+41.4% required for 50.0% threshold]
β By Total
[S] total [+52.0% required for 70.0% threshold]
[B] total [+41.4% required for 50.0% threshold]
Note: if the file coverage.out is not specified, go-covercheck will look for a file named coverage.out in the current directory.
You can also specify a different file name and path.
You can also use CLI flags to configure go-covercheck without a config file.
$ go-covercheck -h
go-covercheck: Coverage gatekeeper for enforcing test thresholds in Go
Usage:
go-covercheck [coverage.out] [flags]
Flags:
-b, --block-threshold float global block threshold to enforce [0=disabled] (default 50)
-C, --compare-history string compare current coverage against historical ref [commit|branch|tag|label]
-c, --config string path to YAML config file (default ".go-covercheck.yml")
-D, --delete-history string delete historical entry by ref [commit|branch|tag|label]
--diff-from string git reference (commit/branch/tag) to diff from; enables diff-only mode
-f, --format string output format [table|json|yaml|md|html|csv|tsv] (default "table")
-h, --help help for go-covercheck
--history-file string path to go-covercheck history file (default ".go-covercheck.history.json")
--init create a sample .go-covercheck.yml config file in the current directory
-l, --label string optional label name for history entry
-L, --limit-history int limit number of historical entries to save or display [0=no limit]
-m, --module-name string explicitly set module name for path normalization (overrides module inference)
-w, --no-color disable color output
-u, --no-summary suppress failure summary and only show tabular output [disabled for json|yaml]
-t, --no-table suppress tabular output and only show failure summary [disabled for json|yaml]
-H, --save-history add coverage result to history
-I, --show-history show historical entries in tabular format
-k, --skip stringArray regex string of file(s) and/or package(s) to skip
--sort-by string sort-by [file|blocks|statements|statement-percent|block-percent] (default "file")
--sort-order string sort order [asc|desc] (default "asc")
-s, --statement-threshold float global statement threshold to enforce [0=disabled] (default 70)
--term-width int force output to specified column width [0=autodetect]
-B, --total-block-threshold float total block threshold to enforce [0=disabled]
-S, --total-statement-threshold float total statement threshold to enforce [0=disabled]
-v, --version version for go-covercheck
For integration tests or when collecting coverage from running binaries, you can use go tool covdata to work with coverage data directories and convert them to the standard coverage profile format that go-covercheck understands.
This workflow is particularly useful for integration tests where you want to assert coverage from a running binary:
# 1. Build your binary with coverage support
go build -cover -o myapp ./cmd/myapp
# 2. Set environment variable for coverage data directory
export GOCOVERDIR=./coverdata
mkdir -p $GOCOVERDIR
# 3. Run your binary (coverage data will be written to GOCOVERDIR)
./myapp &
APP_PID=$!
# 4. Run your integration tests against the running binary
curl http://localhost:8080/health
curl http://localhost:8080/api/users
# ... more integration test calls
# 5. Stop the binary (this flushes coverage data)
kill $APP_PID
# 6. Convert coverage data to standard format
go tool covdata textfmt -i=./coverdata -o=integration-coverage.out
# 7. Check coverage with go-covercheck
go-covercheck integration-coverage.outEnforce coverage thresholds only on files that have changed in your git diff. This is perfect for gradually improving coverage in large codebases without being penalized by legacy code.
- Pull Request Validation: Ensure new code meets coverage standards without failing on existing legacy code
- Incremental Coverage Improvement: Gradually increase coverage standards for new development
- CI/CD Integration: Gate deployments based on coverage of changes, not entire codebase
# Check coverage only on files changed since a specific commit
go-covercheck --diff-from HEAD~1
# Compare against a specific commit
go-covercheck --diff-from HEAD~3
# Compare against a branch
go-covercheck --diff-from main
# Compare against a tag
go-covercheck --diff-from v1.0.0
# CI/CD: Check coverage for PR changes
go-covercheck --diff-from origin/main
# Local development: Check changes since last push
go-covercheck --diff-from @{upstream}
# Release validation: Check changes since last tag
go-covercheck --diff-from $(git describe --tags --abbrev=0)If git operations fail (e.g., not in a git repository, invalid reference), go-covercheck will automatically fall back to checking all files with a warning message.
History is a feature that allows you to save and compare coverage results against previous runs.
Save the current coverage result to history with the --save-history flag. This will create or update a history file
(.go-covercheck.history.json by default) with the current coverage results. Check this file into your version control
system (or other) to keep a shared record of coverage over time.
go-covercheck --save-historyOptionally, specify a label for the history entry with the --label flag. This is useful when the project is not in git.
go-covercheck --save-history --label "my-label"Compare the current coverage against saved history with the --compare-history flag.
History integrates with git to compare against a refβa commit, a branch, or a tag.
$ go-covercheck --compare-history main
...
β‘ Comparing against ref: main [commit e402629]
β By Total
[S] total [+4.5 %]
[B] total [+7.3 %]A label can also be used to compare against a specific history entry.
go-covercheck --compare-history my-labelDisplay saved history entries in a tabular format with the --show-history flag. This will show all saved history entries sorted by timestamp..
$ go-covercheck --show-history
ββββββββββββββ¬ββββββββββ¬ββββββββββββββββββββββββββββ¬ββββββββββββββββββ¬ββββββββββββββββββ¬ββββββββββββββ
β TIMESTAMP β COMMIT β BRANCH β TAGS β LABEL β COVERAGE β
ββββββββββββββΌββββββββββΌββββββββββββββββββββββββββββΌββββββββββββββββββΌββββββββββββββββββΌββββββββββββββ€
β 2025-07-25 β e7c7d91 β mach6_bugfix_feat_history β β β 536/656 [S] β
β β β β β β 309/409 [B] β
ββββββββββββββΌββββββββββΌββββββββββββββββββββββββββββΌββββββββββββββββββΌββββββββββββββββββΌββββββββββββββ€
β 2025-07-25 β 4f7469a β mach6_bugfix_feat_history β β β 484/653 [S] β
β β β β β β 278/413 [B] β
ββββββββββββββΌββββββββββΌββββββββββββββββββββββββββββΌββββββββββββββββββΌββββββββββββββββββΌββββββββββββββ€
β 2025-07-18 β e402629 β main β β β 180/648 [S] β
β β β β β β 95/409 [B] β
ββββββββββββββΌββββββββββΌββββββββββββββββββββββββββββΌββββββββββββββββββΌββββββββββββββββββΌββββββββββββββ€
β 2025-07-18 β 7fccdbd β detached β v0.4.1 β β 220/285 [S] β
β β β β β β 112/164 [B] β
ββββββββββββββ΄ββββββββββ΄ββββββββββββββββββββββββββββ΄ββββββββββββββββββ΄ββββββββββββββββββ΄ββββββββββββββ
β‘ Showing last 4 history entries
You can limit the number of history entries displayed or saved with the --limit-history flag. This is useful to avoid
overloading the output with too many entries. Set it to 0 (the default) to disable any limit.
go-covercheck --save-history --limit-history 5 -l my-label$ go-covercheck --show-history --limit-history 2
ββββββββββββββ¬ββββββββββ¬ββββββββββββββββββββββββββββ¬ββββββββββββββββββ¬ββββββββββββββββββ¬ββββββββββββββ
β TIMESTAMP β COMMIT β BRANCH β TAGS β LABEL β COVERAGE β
ββββββββββββββΌββββββββββΌββββββββββββββββββββββββββββΌββββββββββββββββββΌββββββββββββββββββΌββββββββββββββ€
β 2025-07-25 β e7c7d91 β mach6_bugfix_feat_history β β my-label β 536/656 [S] β
β β β β β β 309/409 [B] β
ββββββββββββββΌββββββββββΌββββββββββββββββββββββββββββΌββββββββββββββββββΌββββββββββββββββββΌββββββββββββββ€
β 2025-07-25 β 4f7469a β mach6_bugfix_feat_history β β β 484/653 [S] β
β β β β β β 278/413 [B] β
ββββββββββββββ΄ββββββββββ΄ββββββββββββββββββββββββββββ΄ββββββββββββββββββ΄ββββββββββββββββββ΄ββββββββββββββ
β‘ Showing last 2 history entries
You can delete specific history entries using the --delete-history flag. This allows you to remove outdated or unwanted entries from your history file. The deletion uses the same reference matching as compare and show operations.
# Delete by commit hash (full or short)
go-covercheck --delete-history e402629
# Delete by branch name
go-covercheck --delete-history main
# Delete by tag
go-covercheck --delete-history v1.0.0
# Delete by label
go-covercheck --delete-history my-labelWhen a history entry is successfully deleted, you'll see a confirmation message:
$ go-covercheck --delete-history main
β‘ Deleted history entry for ref: main
If the reference is not found, an error message will be displayed:
$ go-covercheck --delete-history nonexistent
Error: no history entry found for ref: nonexistent
go-covercheck supports multiple output formats. The default is table, but you can specify other formats using the
--format flag (short form -f) or through the format: field of the config file.
The available formats are:
json: Outputs the coverage details in JSON format.yaml: Outputs the coverage details in YAML format.md: Outputs the coverage details in Markdown format.html: Outputs the coverage details in HTML format.csv: Outputs the coverage details in CSV format.tsv: Outputs the coverage details in TSV (Tab-Separated Values) format.table: Outputs the coverage details in a human-readable table format (default).
The table format provides a human-readable output with color coding to indicate coverage status. It shows coverage
details by file, package, and total. The table is sorted by file name by default, but you can change the sort order and
field using the --sort-by and --sort-order flags.
$ go-covercheck -f table coverage.out -u
βββββββββββββββββ¬βββββββββββββ¬ββββββββββββ¬ββββββββββββββ¬ββββββββββββ
β β STATEMENTS β BLOCKS β STATEMENT % β BLOCK % β
βββββββββββββββββΌβββββββββββββΌββββββββββββΌββββββββββββββΌββββββββββββ€
β BY FILE β β β β β
βββββββββββββββββΌβββββββββββββΌββββββββββββΌββββββββββββββΌββββββββββββ€
β cmd/foo.go β 0/1 β 0/1 β 0.0 β 0.0 β
β cmd/bar.go β 20/110 β 7/80 β 18.2 β 8.8 β
βββββββββββββββββΌβββββββββββββΌββββββββββββΌββββββββββββββΌββββββββββββ€
β BY PACKAGE β β β β β
βββββββββββββββββΌβββββββββββββΌββββββββββββΌββββββββββββββΌββββββββββββ€
β cmd β 20/111 β 7/81 β 18.0 β 8.6 β
βββββββββββββββββΌβββββββββββββΌββββββββββββΌββββββββββββββΌββββββββββββ€
β BY TOTAL β β β β β
βββββββββββββββββΌβββββββββββββΌββββββββββββΌββββββββββββββΌββββββββββββ€
β β 20/111 β 7/81 β 18.0 β 8.6 β
βββββββββββββββββ΄βββββββββββββ΄ββββββββββββ΄ββββββββββββββ΄ββββββββββββ
Note: Here the -u flag was used to suppress the summary lines.
go-covercheck supports other tabular formats such as csv, tsv, and md. These formats are useful for
exporting coverage data to other tools or visualizations. The flag -u (or --no-summary) can be used to suppress the
summary lines in these formats, as well, which is necessary for generating clean output for further processing.
The json format provides a structured output that is easy to read and parse. It includes coverage details by file,
package, and total. It also includes the thresholds and the actual coverage percentages.
JSON output is color-coded by default, but you can disable color with the --no-color flag.
The --no-summary flag is applied when the json format is used.
$ go-covercheck -f json coverage.out
{
"byFile": [
{
"statementCoverage": "150/150",
"blockCoverage": "1/1",
"statementPercentage": 100,
"blockPercentage": 100,
"statementThreshold": 0,
"blockThreshold": 0,
"failed": false,
"file": "foo"
}
],
"byPackage": [
{
"statementCoverage": "150/150",
"blockCoverage": "1/1",
"statementPercentage": 100,
"blockPercentage": 100,
"statementThreshold": 0,
"blockThreshold": 0,
"failed": false,
"package": "."
}
],
"byTotal": {
"statements": {
"coverage": "150/150",
"threshold": 0,
"percentage": 100,
"failed": false
},
"blocks": {
"coverage": "1/1",
"threshold": 0,
"percentage": 100,
"failed": false
}
}
}The yaml format provides a structured output that is easy to read and parse. It includes coverage details by file,
package, and total. It also includes the thresholds and the actual coverage percentages.
YAML output is color-coded by default, but you can disable color with the --no-color flag.
The --no-summary flag is applied when the yaml format is used.
$ go-covercheck -f yaml coverage.out
byFile:
- statementCoverage: 150/150
blockCoverage: 1/1
statementPercentage: 100
blockPercentage: 100
statementThreshold: 0
blockThreshold: 0
failed: false
file: foo
byPackage:
- statementCoverage: 150/150
blockCoverage: 1/1
statementPercentage: 100
blockPercentage: 100
statementThreshold: 0
blockThreshold: 0
failed: false
package: .
byTotal:
statements:
coverage: 150/150
threshold: 0
percentage: 100
failed: false
blocks:
coverage: 1/1
threshold: 0
percentage: 100
failed: falseBy default, go-covercheck uses color in tabular format(s). The color is used to indicate severity as follows:
- % in
${\color{red}red}$ indicates the actual is<=50%of the threshold goal - % in
${\color{yellow}yellow}$ indicates the actual is>50%and<=99%of the threshold goal - % in
${\color{green}green}$ indicates the actual is>99%of the threshold goal or the goal was met - % in no color indicates the goal and actual are
0or the goal is0
Contributions are welcome. Please see CONTRIBUTING.