Standardized coverage ignoring for Go projects via .covignore glob patterns.
Go projects accumulate generated code, mocks, and third-party wrappers that inflate or deflate coverage numbers. The lack of real singular solution in this space leads to - at best a sprawl of fragile grep, sed, or awk solutions, and at worst frustration with perceived false-positives that lead to "temporarily" (see forever) switching test gates off. The latter of those led me to make this project. But the project has grown large enough that I want to make it available generally.
covignore introduces a single .covignore file (think .gitignore for coverage) that:
- Standardizes what gets excluded across all environments
- Composes with
go testtransparently - wrap your command or post-process a profile - Pipes - read from stdin, write to stdout, chain with other tools
- Works in monorepos -
go.workawareness with per-module.covignorefiles - Runs everywhere - zero dependencies, single binary, no runtime overhead
- Gates filtered coverage - build in
--minflag for simple pass fail execution based on percentage of filtered coverage - Supports presets - at this point there's only one preset for generated files, but could be expanded
go install github.com/RayJSeth/covignore/cmd/covignore@latestPre-built binaries for macOS, Linux, and Windows are available on the Releases page.
Requires Go 1.26 or later when building from source.
# Zero-config: use the built-in preset to ignore generated code and mocks
covignore --preset=generated -- ./...
# Or create a .covignore for more granular control
covignore --init
# Run tests with coverage filtering
covignore -- ./...
# Post-process an existing coverage file
covignore coverage.out
# Chain coverage via pipe
go test -coverprofile=/dev/stdout ./... | covignore - --jsonPlain text file with gitignore-style glob patterns. Example common usage:
# Generated protobuf
**/*.pb.go
# Code generators
**/*_generated.go
**/*_gen.go
# Mocks
**/mock/**
**/mocks/**
**/*_mock.goLines starting with # are comments. Prefix a pattern with ! to negate (re-include a previously matched file).
| Pattern | Matches |
|---|---|
** |
Zero or more path segments |
* |
Any characters within a single segment |
? |
A single character |
!{pattern} |
Negates a previous match (re-include) |
covignore -- ./...
covignore --min=80 -- -race ./...
covignore --json -- -run TestIntegration ./...Use -- to separate covignore flags from go test flags. The separator is optional when there's no ambiguity - single-dash flags like -v and -race always pass through to go test because covignore only uses double-dash flags.
covignore coverage.outFilters an existing coverage profile in-place.
# Read from file, write filtered profile to stdout
covignore -o - coverage.out
# Full pipeline
go test -coverprofile=/dev/stdout ./... | covignore - -o - | some-other-toolUse - as the input to read from stdin. Use -o - to write the filtered profile to stdout.
covignore --min=80 -- ./...Exits with code 1 if filtered coverage is below the threshold.
covignore --preset=generated -- ./...Built-in pattern sets that can be used alongside .covignore. Currently available: generated (protobuf, codegen, mocks, ent, sqlc).
covignore --json -- ./... # JSON with coverage stats + file lists
covignore --summary -- ./... # One-line coverage percentage
covignore --html=coverage.html -- ./... # HTML report saved to file
covignore -o filtered.out -- ./... # Custom output pathcovignore --checkLists all .go files in the module matched by the current .covignore patterns, without running tests. Particularly usefule in monorepo mode as those nested .covignore files can get hard to track!
covignore --dry-run -- ./... # Preview filtering without writing
covignore --verbose -- ./... # Show per-file filter details
covignore --version # Print version
covignore --init # Create default .covignore| Flag | Description |
|---|---|
--init |
Create a default .covignore file |
--min=N |
Minimum coverage threshold (exits 1 if below) |
--json |
Output coverage report as JSON |
--summary |
Print coverage summary line |
--html=PATH |
Write HTML coverage report to PATH |
--preset=NAME |
Use a built-in ignore preset |
-o PATH |
Output file path (default: coverage.out; use - for stdout) |
--dry-run |
Preview filtering without writing files |
--verbose |
Show per-file filter details (summary always prints when entries are filtered) |
--check |
List files matched by current patterns |
--version |
Print version |
In a go.work workspace, covignore automatically discovers all modules and applies per-module .covignore files:
myproject/
├── go.work
├── .covignore # fallback patterns
├── svcA/
│ ├── go.mod
│ └── .covignore # svcA-specific patterns
└── svcB/
├── go.mod
└── .covignore # svcB-specific patterns
Each module's coverage entries are filtered using the closest .covignore. The --check flag inspects each module independently.
- name: Test with coverage
run: |
go install github.com/RayJSeth/covignore/cmd/covignore@latest
covignore --min=80 -- ./...- name: Coverage report
run: covignore --json -- ./... > coverage.json- name: Build
run: |
go build -ldflags="-X github.com/RayJSeth/covignore/internal/cli.Version=${{ github.ref_name }}" \
./cmd/covignorecovignore uses flags and .covignore files for all configuration. There is no separate config file by design - your CI script is your config - leaves the control local and traceable.
For shared team defaults, check .covignore into version control (this is the intended use case) and set flags in your CI configuration or Makefile:
.PHONY: test
test:
covignore --min=80 --preset=generated -- -race ./...- Runs
go test -coverprofile=.covignore.raw <your args> - Parses the raw coverage profile
- Loads
.covignorepatterns (+ optional preset) - Strips module prefixes and filters matching entries
- Writes filtered
coverage.out - Reports coverage, checks threshold, generates HTML - as requested
In post-process or pipe mode, step 1 is skipped in lieu of an existing profile.
MIT - see LICENSE.