Changelog¶
All notable changes to go-crap will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
v0.5.1 - UNRELEASED¶
Added¶
--progressflag — force-enable progress indicators (default: auto-detect terminal)--no-progressflag — force-disable progress indicators- Progress bars on stderr showing scan phases: "Discovering modules", "Running coverage tests", "Analyzing complexity", "Processing results"
- Docker images published to
docker.io/padiazg/go-crapandghcr.io/padiazg/go-crap(multi-arch linux/amd64 + linux/arm64) — run withdocker run --rm -v "$PWD:/code" ghcr.io/padiazg/go-crap scan scannow accepts multiple Go package patterns (same syntax asgo list/go build):./...(recursive),./pkg(single),github.com/x/y(import path). Patterns resolved viago list -json -e, with fallback to path-based discovery for nested modules
Changed¶
- No-arg
scandefault changed from current directory (.) to whole module (./...); accepts multiple positional patterns instead of a singlepathargument
v0.5.0 - 2026-07-28¶
Added¶
- Partial coverage preserved on test failure — when
go testfails in a module, go-crap still parses the coverage profile from any passing tests instead of discarding all coverage data extractFailedTestshelper parses--- FAIL: TestNamelines from stderr and logs failed test names at Warn level (use--verboseto see them)--coverage-profileflag — supply an existing coverage profile instead of runninggo test(collaboration with @mem)--timeoutflag — configurable timeout for the scan command (collaboration with @matiasinsaurralde)Profilefield onModuleCoverage— stores the path to the coverage profile file- Baseline comparison engine — load previous JSON reports for delta analysis
- New
--baseline <path>flag — compare current CRAP against a previous report - New
--fail-regressionflag — exit code 1 when functions regressed vs baseline - New
--fail-regression-ignore-coveredflag — exclude fully covered functions from regression failures (still shows them with~) - New
--fail-regression-threshold <float>flag (default0.01) — minimum delta to trigger - New
--show-unchangedflag — in baseline mode, also show functions whose CRAP score did not change (requires--baseline) Baselinestruct withLoadBaseline()— parses JSON reports into comparison-ready dataAnnotateWithBaseline()— setsBaselineCRAPon each CRAP entry for delta trackingComputeSummaryWithBaseline()— aggregate stats with baseline/compare deltasFindRegressions()— exported regression detection for CLI useErrRegressionsentinel error — distinct from threshold exceedanceBaselineCRAPfield onCRAPEntry— stores previous report CRAP value (-1 = new function)Deltafield onCRAPEntry— CRAP delta from baseline
Changed¶
runTestsno longer deletes the temp coverage profile on error; the profile is kept forparseCoverProfileand cleaned up after parsingscanModuleno longer returns early onrunTestsfailure; coverage is always parsed and partial data is returned alongside the errorScanpreserves the fullModuleCoveragestruct fromscanModule, retainingFunctionsalongside the wrapped error- JSON schema bumped to 1.1.0 — per-entry
baseline_crap/deltafields,summaryobject with baseline/compare - PR comment formatter: header shows Combined/Average CRAP with deltas vs baseline; ASCII badges (
[OK],[!!],[ERROR],[NEW]); "New Functions" and "Regressions" sections; summary table - Table formatter:
Δcolumn when baseline available; per-function delta with+X ↑/-X ↓; delta footer lines - GitHub formatter: summary
::notice::annotation when baseline provided - CLI:
--fail-regressionwithout--baselinereturns error - JSON output: schema field updated to 1.1.0, new
summaryobject with baseline/compare stats
Fixed¶
- Coverage profile deduplication — when
go test -coverprofileproduces a merged profile from multiple test binaries, the same source blocks appeared multiple times. Entries are now deduplicated by(startLine, endLine)position using OR semantics (a block is covered if any test binary covered it), fixing inaccurate coverage percentages (reported by @corani) - Coverage profile temp files now cleaned up after successful scan (collaboration with @matiasinsaurralde)
- Field alignment for better memory layout
- Tests added for
Spashfunction
v0.4.1 - 2026-06-18¶
Added¶
- Coverage unavailable warning — propagates
go testfailure messages through merge → score → formatters CoverageWarningfield onMergedEntryandCRAPEntry— set when coverage data is unavailable due to module-level test errorstableformatter: coverage column showsN/A ‼with deduplicated footer warning when coverage unavailablejsonformatter:coverageisnullandcoverage_warningfield populated when coverage unavailablegithubformatter: emits::warningannotation when coverage is unavailablesarifformatter: new result withRuleID: "go-crap/coverage-unavailable"when coverage unavailablepr-commentformatter: "Coverage Unavailable" section listing affected functions- Issue templates (bug report + feature request)
- Dependabot configuration
.golangci.ymlwith full linter configurationinternal/scan/entries.gowithEntryListtest helpers- Extensive test coverage across all packages
- Mutation testing robustness improvements across all internal packages
permissions: read-allto GitHub Actions workflow
Changed¶
- Refactored
cmd/scan.gooutput pipeline to use config struct - Refactored
internal/scan/scan.gowithOptionsstruct and helper extraction - DRY refactors across internal packages
- Dead code removal
- Doc comment cleanup across internal packages
- Bumped golangci-lint config version to v1.65.0
- Bumped golangci-lint-action from v6 to v9
- Removed dead code in
pkg/sloggerandpkg/dummylogger
Fixed¶
crappy[:25]slice bounds panic when filtered entries < 25 items- Linting issues across codebase
- Typo
ThresholdExeeded→ThresholdExceeded - Typo in version command description ("TestGen" → "go-crap")
- Logic bugs in merge, score, mutation, report, and scan packages
- Typos and embarrassment fixes across codebase
v0.4.0 - 2026-06-08¶
Added¶
- New
--mutation-reportflag — path to a gremlins JSON mutation report to validate coverage reliability - New
internal/mutationpackage — parses gremlins mutation reports and annotates CRAP entries - New
CoverageUntrustedfield onCRAPEntry— set totruewhen lived mutants are found in a function - New
MutationScorefield onCRAPEntry— killed/(killed+lived) ratio for the function - New
EffectiveCRAPfield onCRAPEntry— CRAP score recalculated with 0% coverage whenCoverageUntrustedistrue - Mutation score included in JSON output (
mutation_score,coverage_untrusted,effective_crapfields) - Coverage warning flag (⚠) in
tableandpr-commentformatters when coverage is unreliable - Coverage-untrusted warnings in
githubandsarifformatters (SARIF adds a secondcoverage-untrustedresult) - New "Unreliable Coverage" section in
pr-commentoutput listing affected functions with mutation scores - New
--detailedflag — includes mutation failure details (original/replacement code, line, type) in report output - New
MutationDetailstruct onCRAPEntry— stores survived mutant details when mutation report is provided - JSON output includes
mutation_detailsarray per entry when--detailedis set, withtype,mutator_name,file,line,status,original_text, andreplacement_textfields - SARIF appends survived mutation details (type, line, code diff) to warning messages when
--detailed - PR Comment adds
Survived Mutantscolumn with code snippets when--detailed - New
OriginalCodeandReplacementCodefields on mutationMutantstruct, parsed from Gremlins JSON report
Changed¶
ThresholdExceeded()now usesEffectiveCRAPinstead ofCRAPfor threshold comparison--fail-abovenow checksEffectiveCRAPagainst the threshold
Fixed¶
- Functions with lived mutants now show their true risk level via
EffectiveCRAP(CRAP at 0% coverage) --topno longer drops functions flagged with lived mutation mutants —CoverageUntrustedentries always survive truncation alongside the top trusted entries--minno longer filters outCoverageUntrustedentries, even when theirCRAPscore is below the thresholdgithubformat now emits::warningforCoverageUntrustedentries regardless of thresholdpr-commentformat now always renders the "Unreliable Coverage" section when mutation report is provided, even when no function exceeds the CRAP threshold
v0.3.0 - 2026-06-05¶
Added¶
- New
sarifoutput format — SARIF 2.1.0 compliant JSON for static analysis tools - New
pr-commentoutput format — markdown table formatted for pull request comments - New
--verboseflag — enables debug-level logging viapkg/loggerandpkg/slogger - New
--output/-oflag — write output to a file instead of stdout
v0.2.0 - 2026-06-04¶
Added¶
- New
internal/scanpackage — unified scan pipeline orchestrating coverage, complexity, merge, and score in a single call - New
pkg/utilspackage withBuildExcludeRegexandMatchExcludehelpers - Method receiver tracking — CRAP entries now include receiver info for struct methods
EntryListwrapper type withThresholdExceeded()method for CI integrationSkippedfield onCRAPEntryfor functions excluded by--missing skippolicy- JSON output now uses
EntryListstructure with optional receiver field
Changed¶
--excludeflag now uses regex matching instead of glob matching- Refactored
cmd/scan.goto use newinternal/scanpackage - Coverage and complexity packages now accept
*regexp.Regexpinstead of[]stringfor exclude patterns - Merge now constructs full function names from receiver + method name
- Report formatters now accept
*score.EntryListinstead of[]score.CRAPEntry - GitHub formatter now includes function name (and receiver for methods) in warning messages
Fixed¶
- Properly match function names with method receivers (e.g.
(*MyType).Method) - Handle zero-coverage entries correctly in merge step
runCoverToolnow runs in correct module directory (modDirset ascmd.Dir)- Correct coverage path matching in CI environments —
buildSuffixnow uses 3 path components instead of 2, bridging Go module paths (github.com/.../file.go) and absolute filesystem paths (/home/runner/.../go-crap/file.go) - Fix
normalizeFuncNamefailing to strip value-receiver prefixes — methods likeLevel.StringorLogger.Debugwere not matched against coverage data, causing false 0% coverage reports for all value-receiver methods - Fix merge discarding 0% coverage as "missing" — functions with known 0% coverage (coverage data present but no statements executed) are now correctly distinguished from functions with no coverage data at all
v0.1.0 - 2026-06-02¶
Added¶
- Initial release of go-crap
scancommand — analyze Go modules and calculate CRAP scores- AST-based cyclomatic complexity via
internal/complexity(adapted from gocyclo, BSD-3-Clause) - Coverage profiling via
internal/coverage(adapted from test-finder, MIT) - Double-index merge — joins coverage and complexity by
(filepath, funcname)without CWD path resolution issues - CRAP formula —
CC² × (1 - coverage/100)³ + CC - Missing coverage policy —
pessimistic(default),optimistic,skip - Output formatters:
table— human-readable with status symbols (✓, ▲, ✗) and coverage barsjson— structured output with schema URLgithub— GitHub Actions workflow annotation format- Filtering —
--top N,--min score,--exclude glob - CI integration —
--fail-aboveexits with code 1,--format githubproduces workflow annotations - GitHub Actions CI/CD workflow (tests + CRAP check)
- GitHub Pages documentation deployment