covergate

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Aug 11, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package covergate parses Go coverage profiles and enforces a ratchet: coverage may rise freely but may never fall below a recorded baseline.

It is designed for repositories that want to lock in the coverage they already have before starting a large refactor, without committing to an absolute target such as 100 percent.

Typical use from a magefile:

report, err := covergate.Report("coverage.out", covergate.Options{
	Exclude: []string{"**/gen/**"},
})
if err != nil {
	return err
}
return covergate.Check(report, "coverage-baseline.json", covergate.CheckOptions{})

Index

Constants

View Source
const DefaultBaselineFile = "coverage-baseline.json"

DefaultBaselineFile is the conventional baseline location for a repository.

View Source
const ScopeTotal = "total"

ScopeTotal is the scope name used for repository-wide coverage, as opposed to a package import path.

Variables

View Source
var ErrNoBaseline = errors.New("coverage baseline not found")

ErrNoBaseline is returned when a baseline file does not exist.

Functions

func Check

func Check(r Report, b Baseline, opts CheckOptions) error

Check enforces the ratchet: every scope in the baseline must still meet its recorded floor, and by default any package new since the baseline must meet the baseline total.

It returns a *CheckError when coverage regressed, so callers can inspect the individual regressions with errors.As.

func FormatReport

func FormatReport(r Report) string

FormatReport renders a human-readable summary, worst packages first.

func Gate

func Gate(c Config) error

Gate parses the profile and enforces the ratchet against the recorded baseline. It is the function a magefile's coverage target should call.

If no baseline exists yet, Gate fails with instructions rather than silently passing, so an unrecorded repository cannot appear to be protected.

func Record

func Record(c Config, note string) error

Record measures the current profile and writes it as the new baseline.

It refuses to overwrite a baseline recorded on another platform. Coverage is measured per platform, so re-recording on a developer machine would replace the numbers CI can actually reach with numbers only that machine can reach, and the gate would then fail for everyone else.

func SaveBaseline

func SaveBaseline(name string, b Baseline) error

SaveBaseline writes a baseline file atomically, with a trailing newline so it stays diff-friendly.

Types

type Baseline

type Baseline struct {
	// Total is the minimum acceptable overall percentage.
	Total float64 `json:"total"`
	// Packages maps a package import path to its minimum percentage.
	Packages map[string]float64 `json:"packages"`
	// Exclude records the glob patterns used when the baseline was written,
	// so a later check cannot silently widen them.
	Exclude []string `json:"exclude,omitempty"`
	// Mode records the "go test -covermode" counter mode the baseline was
	// measured with. Percentages are not comparable across modes, so a later
	// check measured in a different mode is rejected.
	Mode string `json:"mode,omitempty"`
	// OS records the GOOS the baseline was measured on. Platform-specific
	// branches are unreachable, and therefore uncovered, on every other
	// platform, so percentages are not comparable across operating systems.
	OS string `json:"os,omitempty"`
	// Note is free-form context for humans reading the file.
	Note string `json:"note,omitempty"`
}

Baseline is the recorded coverage floor for a repository.

func BaselineFrom

func BaselineFrom(r Report, exclude []string, note string) Baseline

BaselineFrom derives a baseline from a report, keeping the report's own exclude patterns for later verification.

func LoadBaseline

func LoadBaseline(name string) (Baseline, error)

LoadBaseline reads a baseline file. It returns ErrNoBaseline if the file is absent, which callers can treat as "record one now".

The path comes from build tooling rather than untrusted input.

type Block

type Block struct {
	// File is the import-path-qualified file name, for example
	// "github.com/jongio/azd-core/env/load.go".
	File string
	// Statements is the number of statements in the block.
	Statements int
	// Count is the number of times the block was executed.
	Count int
}

Block is a single counted region from a Go coverage profile.

func (Block) Covered

func (b Block) Covered() bool

Covered reports whether the block was executed at least once.

func (Block) Package

func (b Block) Package() string

Package returns the directory portion of the block's file path, which for Go coverage profiles is the package import path.

type CheckError

type CheckError struct {
	Regressions []Regression
}

CheckError reports one or more coverage regressions.

func (*CheckError) Error

func (e *CheckError) Error() string

type CheckOptions

type CheckOptions struct {
	// Tolerance is the percentage drop allowed before a regression is
	// reported. It absorbs rounding noise from non-deterministic test
	// selection. Zero means any drop fails.
	Tolerance float64
	// IgnoreNewPackages disables the "new package below total" rule. By
	// default a package absent from the baseline must at least meet the
	// baseline total, so new code cannot dilute overall coverage.
	IgnoreNewPackages bool
	// AllowExcludeDrift permits the report's exclude patterns to differ from
	// those recorded in the baseline. By default a mismatch fails, because
	// widening exclusions is the easiest way to fake a passing gate.
	AllowExcludeDrift bool
	// AllowModeDrift permits the profile's counter mode to differ from the
	// mode the baseline was recorded with. By default a mismatch fails,
	// because percentages are not comparable across modes.
	AllowModeDrift bool
	// AllowOSDrift permits the check to run on a different GOOS than the
	// baseline was recorded on. By default a mismatch fails, because
	// platform-specific code is unreachable, and so uncovered, elsewhere.
	AllowOSDrift bool
}

CheckOptions tunes ratchet enforcement.

type Config

type Config struct {
	// Profile is the coverage profile to read. Defaults to "coverage.out".
	Profile string
	// BaselineFile is where the recorded floor lives. Defaults to
	// DefaultBaselineFile.
	BaselineFile string
	// Exclude holds glob patterns for code that should not count, such as
	// generated sources.
	Exclude []string
	// Check tunes ratchet enforcement.
	Check CheckOptions
	// SkipOnForeignOS downgrades a platform mismatch from a failure to a
	// visible notice, so a developer on a platform other than the one the
	// baseline was recorded on still gets a coverage report instead of a
	// hard stop. Enforcement then belongs to CI, which runs on the recording
	// platform. Leave it false wherever the gate is authoritative.
	SkipOnForeignOS bool
	// Out receives human-readable progress. Defaults to os.Stdout.
	Out io.Writer
}

Config describes a repository's coverage gate.

type Delta

type Delta struct {
	// Scope is "total" or a package import path.
	Scope string
	// Baseline is the recorded floor.
	Baseline float64
	// Current is the measured value.
	Current float64
	// New reports whether the scope is absent from the baseline.
	New bool
}

Delta is a change in coverage for one scope, in either direction.

func Improvements

func Improvements(r Report, b Baseline, minDelta float64) []Delta

Improvements lists scopes that now exceed their baseline by at least minDelta, so a caller can prompt the user to re-record and lock in the gain.

func (Delta) Change

func (d Delta) Change() float64

Change returns Current minus Baseline, rounded to one decimal place.

type Options

type Options struct {
	// Exclude holds slash-separated glob patterns matched against each
	// block's file path. Matching blocks are dropped before any percentage
	// is computed. Use this for generated code, for example "**/gen/**".
	Exclude []string
}

Options controls how a coverage profile is aggregated into a Report.

type ProfileData

type ProfileData struct {
	// Mode is the value of the profile's "mode:" header, such as "atomic".
	Mode string
	// Blocks holds every counted region in the profile.
	Blocks []Block
}

ProfileData is a parsed coverage profile: the counter mode declared in its header plus the blocks it contains.

The mode matters to the ratchet because "go test -covermode" materially changes the reported percentage. Atomic counters survive concurrent updates that "set" and "count" mode lose, so the same tests can report several points higher under atomic. Comparing a baseline recorded in one mode against a profile measured in another produces meaningless deltas.

func ParseProfile

func ParseProfile(r io.Reader) (ProfileData, error)

ParseProfile reads a Go coverage profile produced by "go test -coverprofile".

The leading "mode:" line is required, matching the format that "go tool cover" itself accepts. Blank lines are ignored.

func ParseProfileFile

func ParseProfileFile(name string) (ProfileData, error)

ParseProfileFile reads and parses a coverage profile from disk.

The path comes from build tooling rather than untrusted input, so it is opened directly.

type Regression

type Regression Delta

Regression is a coverage drop below the recorded floor.

func (Regression) String

func (r Regression) String() string

type Report

type Report struct {
	// Total is coverage across every retained block.
	Total Stats `json:"total"`
	// Packages maps a package import path to its coverage.
	Packages map[string]Stats `json:"packages"`
	// Mode is the counter mode of the profile this report came from, such as
	// "atomic". Aggregate leaves it empty; Profile fills it in from the
	// profile header.
	Mode string `json:"mode,omitempty"`
	// OS is the GOOS the profile was measured on. Aggregate leaves it empty;
	// Profile fills it in from the running process, because a profile is only
	// ever produced by tests running on the current platform.
	OS string `json:"os,omitempty"`
	// Excluded is the number of blocks dropped by the Exclude patterns.
	Excluded int `json:"-"`
	// contains filtered or unexported fields
}

Report is the aggregated coverage of one profile.

func Aggregate

func Aggregate(blocks []Block, opts Options) Report

Aggregate folds blocks into a Report, applying opts.Exclude.

func Profile

func Profile(profilePath string, opts Options) (Report, error)

Profile parses a coverage profile file and aggregates it in one step, carrying the profile's counter mode and the measuring platform onto the report.

func (Report) ExcludePatterns

func (r Report) ExcludePatterns() []string

ExcludePatterns returns the glob patterns used to build this report.

func (Report) PackageNames

func (r Report) PackageNames() []string

PackageNames returns the report's package paths in sorted order.

type Stats

type Stats struct {
	Covered    int `json:"covered"`
	Statements int `json:"statements"`
}

Stats is a statement count and its covered subset.

func (Stats) Percent

func (s Stats) Percent() float64

Percent returns covered statements as a percentage, rounded to one decimal place. A unit with no statements is defined as fully covered, which keeps empty or excluded-to-nothing packages from failing the gate.

Directories

Path Synopsis
cmd
covergate command
Command covergate enforces a coverage ratchet against a recorded baseline.
Command covergate enforces a coverage ratchet against a recorded baseline.

Jump to

Keyboard shortcuts

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