covercheck

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 18, 2026 License: LGPL-2.1 Imports: 9 Imported by: 0

README

covercheck — Go diff-coverage gate

check codecov Go Report Card Go version Latest tag Top language License

A tiny, dependency-free Go tool that fails when the source lines a change adds or modifies are covered below a threshold — patch (diff) coverage. It judges only changed lines, so a repository's pre-existing untouched-code coverage backlog never blocks a PR.

It reads the coverage profiles go test -coverprofile already produces and the git diff against a base ref; it needs no external service (works the same locally and in CI) and pulls in nothing beyond the Go standard library (the test suite uses testify).

Install

go install github.com/dr-dobermann/covercheck/cmd/covercheck@latest

Use

# produce a coverage profile
go test -coverprofile=coverage.txt ./...

# fail if the changed lines (vs origin/master) are < 80% covered
covercheck -min 80 -base origin/master -profiles coverage.txt

# ignore changed observability-only lines, such as logging calls
covercheck -min 80 -profiles coverage.txt -exclude-lines 'log\.|logger\.|slog\.'

# ignore repository-specific generated files by repo-relative path
covercheck -min 80 -profiles coverage.txt -exclude-paths '^internal/generated/|\.pb\.go$'

Flags:

Flag Default Meaning
-min 70 minimum patch-coverage percent; exit 1 below it
-base origin/master ref to diff against (uses base...HEAD, i.e. the merge-base)
-profiles coverage.txt comma-separated Go coverage profiles
-exclude-lines "" comma-separated regexes for changed line text to exclude
-exclude-paths "" comma-separated regexes for repo-relative changed-file paths to exclude

It diffs the committed state (base...HEAD) so a local run and CI agree. In CI, check out with full history (fetch-depth: 0) so the merge-base resolves.

Excluded from measurement by default (entry points / non-product code): *_test.go, cmd/**, generated/** (mocks), examples/**. Use -exclude-lines sparingly for intentionally untested changed lines such as log-only or other observability-only statements.

Configured -exclude-paths patterns add to those defaults. They use Go regular expressions and match anywhere in the slash-separated repo-relative path; use ^ or $ when a pattern must match at the start or end of the path.

Exit codes

0 pass · 1 below the threshold · 2 error (no profile, bad base ref, …).

License

LGPL-3.0 — see LICENSE.

Documentation

Overview

Package covercheck computes diff (patch) coverage: of the source lines a change adds or modifies, the fraction that the test coverage profiles mark as covered. It is the testable core behind the covercheck command — it judges only changed lines, so the untouched-code coverage backlog never affects the result.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CompileLineExcluder added in v0.1.2

func CompileLineExcluder(patternsCSV string) (func(string, ChangedLine) bool, error)

CompileLineExcluder compiles comma-separated regular expressions into a changed-line exclusion function. Empty input means no line exclusions.

func CompilePathExcluder added in v0.2.0

func CompilePathExcluder(patternsCSV string) (func(string) bool, error)

CompilePathExcluder compiles comma-separated regular expressions into a repo-relative path exclusion function. Empty input means no configured path exclusions. Expressions use regexp.MatchString semantics, so callers can use anchors when they need to match a complete path.

func DefaultExcluded

func DefaultExcluded(path string) bool

DefaultExcluded reports whether a repo-relative path is outside the gate's scope: tests, generated mocks, examples, and CLI entry points.

func Gate

func Gate(
	w io.Writer,
	minPct float64,
	diff string,
	profiles map[string][]Block,
) (int, error)

Gate computes patch coverage from an already-fetched unified=0 diff and coverage profiles, writes the report to w, and returns the exit code (1 if below minPct). Separated from the git/file I/O so it is unit-testable.

func GateWithExclusions added in v0.2.0

func GateWithExclusions(
	w io.Writer,
	minPct float64,
	diff string,
	profiles map[string][]Block,
	excludeLinesCSV, excludePathsCSV string,
) (int, error)

GateWithExclusions computes patch coverage from an already-fetched diff, applying optional changed-line and repo-relative path exclusion regexes before reporting coverage. Configured path exclusions supplement the built-in DefaultExcluded paths.

func GateWithLineExclusions added in v0.1.2

func GateWithLineExclusions(
	w io.Writer,
	minPct float64,
	diff string,
	profiles map[string][]Block,
	excludeLinesCSV string,
) (int, error)

GateWithLineExclusions computes patch coverage from an already-fetched diff, applying optional changed-line exclusion regexes before reporting coverage.

func GitDiff

func GitDiff(base string) (string, error)

GitDiff returns the unified=0 Go-file diff from merge-base(base, HEAD) to HEAD — the committed changes the branch introduces. Committed state (not the working tree) keeps local `make cover-check` and CI in lockstep. The `base...HEAD` form resolves the merge-base itself.

func ParseDiff

func ParseDiff(r io.Reader) (map[string][]int, error)

ParseDiff parses `git diff --unified=0` output and returns the added/changed line numbers on the new (HEAD/working-tree) side, per repo-relative file path. Only the `+` side matters — those are the lines a change introduces.

func ParseDiffLines added in v0.1.2

func ParseDiffLines(r io.Reader) (map[string][]ChangedLine, error)

ParseDiffLines parses `git diff --unified=0` output and returns the added/changed line numbers and text on the new side, per repo-relative file path.

func ParseProfiles

func ParseProfiles(r io.Reader) (map[string][]Block, error)

ParseProfiles reads one or more concatenated Go coverage profiles (the `go test -coverprofile` format) and returns the blocks per profile file path. Profile paths keep their module-import-path prefix (e.g. github.com/x/y/internal/z/f.go); matching to repo-relative paths is by suffix (see Evaluate). The leading `mode:` line(s) are ignored.

func ReadProfiles

func ReadProfiles(paths []string) (map[string][]Block, error)

ReadProfiles parses and merges the coverage profiles at the given paths. It errors if none exist — running the gate with no profile must fail loudly (run `make test-all` first), not pass vacuously.

func Report

func Report(w io.Writer, res Result, minPct float64)

Report writes the per-file and total patch coverage to w.

func RunGate

func RunGate(w io.Writer, minPct float64, base, profilesCSV string) (int, error)

RunGate fetches the committed diff (base...HEAD) and the coverage profiles, then delegates to Gate. It writes the report to w and returns the process exit code: 1 if below minPct, 0 otherwise.

func RunGateWithExclusions added in v0.2.0

func RunGateWithExclusions(
	w io.Writer,
	minPct float64,
	base, profilesCSV, excludeLinesCSV, excludePathsCSV string,
) (int, error)

RunGateWithExclusions is RunGate with optional changed-line and repo-relative path exclusion regexes, each supplied as a comma-separated list.

func RunGateWithLineExclusions added in v0.1.2

func RunGateWithLineExclusions(
	w io.Writer,
	minPct float64,
	base, profilesCSV, excludeLinesCSV string,
) (int, error)

RunGateWithLineExclusions is RunGate with optional changed-line exclusion regexes, supplied as a comma-separated list.

Types

type Block

type Block struct {
	StartLine int
	EndLine   int
	NumStmts  int
	Count     int
}

Block is one coverage region of a Go coverage profile: the half-open source range [StartLine, EndLine] of a basic block, the number of statements in it, and how many times it executed (0 == not covered).

type ChangedLine added in v0.1.2

type ChangedLine struct {
	Text   string
	Number int
}

ChangedLine is one added or modified line from the new side of a diff.

type FileResult

type FileResult struct {
	Covered   int
	Coverable int
}

FileResult is the patch-coverage tally for one changed file.

type Result

type Result struct {
	PerFile   map[string]FileResult
	Covered   int
	Coverable int
}

Result is the overall patch-coverage outcome.

func Evaluate

func Evaluate(
	profiles map[string][]Block,
	changed map[string][]int,
	exclude func(string) bool,
) Result

Evaluate intersects the changed lines with the profile blocks and computes patch coverage. A changed line counts as *coverable* only if it falls inside some profile block (statements); blank/comment/declaration lines are ignored. A coverable line is *covered* if any block covering it has Count > 0. Files for which exclude(path) is true are skipped. Profile paths are matched to the repo-relative changed paths by suffix.

func EvaluateLines added in v0.1.2

func EvaluateLines(
	profiles map[string][]Block,
	changed map[string][]ChangedLine,
	excludeFile func(string) bool,
	excludeLine func(path string, line ChangedLine) bool,
) Result

EvaluateLines intersects changed lines with profile blocks, then skips files and lines matched by the provided exclusion functions.

func (Result) Ratio

func (r Result) Ratio() float64

Ratio returns the covered fraction of coverable changed lines in [0,1]. With no coverable changed lines it returns 1 (nothing to cover ⇒ pass).

Directories

Path Synopsis
cmd
covercheck command
Command covercheck is the diff-coverage gate: it fails when the source lines a change adds or modifies are covered below a threshold, judging only changed lines so the untouched-code coverage backlog never blocks it.
Command covercheck is the diff-coverage gate: it fails when the source lines a change adds or modifies are covered below a threshold, judging only changed lines so the untouched-code coverage backlog never blocks it.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL