Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/mutation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ name: Mutation Testing
on:
push:
branches: [master]
paths:
- "**/*.go"
- "**/testdata/**"
- go.mod
- go.sum
- .github/workflows/mutation.yml
pull_request:
branches: [master]

Expand All @@ -15,6 +21,9 @@ jobs:
timeout-minutes: 60
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# --git-diff-lines needs the base branch and merge-base history.
fetch-depth: 0
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
Expand All @@ -28,13 +37,27 @@ jobs:
# cmd/ - integration tests re-invoke the binary (would recurse)
# internal/importing - resolution behaviour depends on GOPATH layout; excluded from mutation targets
# internal/parser - excluded from mutation targets (type-checker setup is test-infrastructure, not user logic)
# Pull requests mutate only lines in the PR. Pushes to master retain the full-tree
# gate so a faulty diff filter cannot hide a regression permanently.
# --coverage generates a profile before mutation to compute covered-code MSI
# and marks mutants in untested code as "not covered" rather than running them.
# --min-msi and --min-covered-msi enforce quality gates.
env:
EVENT_NAME: ${{ github.event_name }}
run: |
scope_args=()
if [[ "$EVENT_NAME" == "pull_request" ]]; then
scope_args=(
--git-diff-lines
--git-diff-base origin/master
--ignore-msi-with-no-mutations
)
fi

go-mutesting \
--exec-timeout 30 \
--coverage \
"${scope_args[@]}" \
--min-msi 75 \
--min-covered-msi 80 \
github.com/jonbaldie/go-mutesting/v2/mutator/arithmetic \
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ All notable changes to this project will be documented here.

Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). This project uses [Semantic Versioning](https://semver.org/).

## [Unreleased]

### Changed
- Mutation CI now tests only lines changed by a pull request, while retaining the full-tree gate for code-related pushes to `master`. Pull requests with no mutable changes pass without running mutants.

---

## [v2.7.8] — 2026-07-13

### Added
Expand Down
7 changes: 4 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,10 @@ git config core.hooksPath githooks

`pre-commit` runs the fast, whole-tree, deterministic checks (gofmt, go vet, gocyclo,
ineffassign, messgo, build, unit tests) and hard-fails on any finding or missing tool.
`pre-push` runs mutation testing scoped to the diff against `origin/master` via
`--git-diff-lines`. Vulnerability scanning (`security.yml`) depends on external advisory
feeds and stays CI-only; it is not mirrored locally.
`pre-push` mirrors the pull-request mutation gate, scoped to the diff against
`origin/master` via `--git-diff-lines`. CI retains a full-tree mutation gate for
code-related pushes to `master`. Vulnerability scanning (`security.yml`) depends on external
advisory feeds and stays CI-only; it is not mirrored locally.

## Shipping workflow

Expand Down
5 changes: 5 additions & 0 deletions docs/ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ Once you've written tests to kill the known survivors, remove them from the base
Limit mutation to lines changed in the PR to keep feedback fast and relevant:

```yaml
- uses: actions/checkout@v6
with:
# Required so go-mutesting can resolve the base branch and merge-base.
fetch-depth: 0

- run: |
/tmp/go-mutesting \
--git-diff-lines \
Expand Down
12 changes: 6 additions & 6 deletions githooks/pre-push
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/bin/sh
# Local mirror of .github/workflows/mutation.yml, scoped to the diff against
# origin/master instead of the whole tree (mutation testing is slow; running it
# whole-tree on every push is not viable locally). Hard-fails on escaped
# mutants, on a gate miss, and on any missing tool - no silent skips.
# Local mirror of .github/workflows/mutation.yml's pull-request mode, scoped to
# the diff against origin/master. CI retains a full-tree gate for code pushes to
# master. Hard-fails on escaped mutants, on a gate miss, and on any missing tool -
# no silent skips.
#
# Not opt-out by default. To enable this hook, run once:
# git config core.hooksPath githooks
Expand Down Expand Up @@ -72,8 +72,8 @@ if ! go build -o "$GO_MUTESTING_BIN" ./cmd/go-mutesting; then
fi

echo "pre-push: running mutation testing scoped to the diff against origin/master..."
# Same package list, timeout, and quality gates as .github/workflows/mutation.yml
# and CLAUDE.md, restricted to changed lines via --git-diff-lines/--git-diff-base.
# Same package list, timeout, quality gates, and changed-line scope as the
# pull-request mode in .github/workflows/mutation.yml and CLAUDE.md.
# --ignore-msi-with-no-mutations avoids failing pushes that don't touch these
# packages at all.
STATUS=0
Expand Down
Loading