What's new in 0.6
0.6 makes the gate faster and puts its findings
where you already look. The faster run, the since-last-run summary and
the weakened-test lane are on by default; the rest are one input each. Every Action input has a CLI flag of the same name
(budget ↔ --budget).
| You want to… | Use |
|---|---|
| Nothing — just a faster gate | on by default |
| Keep the gate inside a CI time limit | budget |
| Stop paying a cold build on every run | in-place |
| See what changed since the last push | on with comment |
| Survivors on the line they are about | comment-inline |
| Findings in the Security tab | sarif |
| Catch a PR that deletes or weakens tests | on, advisory |
| Split a large run across a CI matrix | --shard |
Faster by default
Before the first mutant the gate now runs your suite once (it was up to three times), and each mutant runs only the changed crate's tests instead of the whole workspace's. Two knobs for when the defaults don't fit:
- A crate tested mainly from another crate would show survivors those tests
actually catch.
test-workspace: "true"tests each mutant against the whole workspace; the report points this out when it matters. - A flaky suite:
--preflight-runs 2(orpreflight_runs: 2in the config file) runs the pre-flight twice to catch a pass/fail flip first.
A time budget
- uses: lucheeseng827/mergestro-gate@v0.6.1
with:
budget: "10m" # 600, 90s, 10m, 1h
# block-on-budget: "true" # untested mutants block instead of warning
When the budget runs out the mutation run stops. Finished mutants count as usual; the
rest are reported as not tested — never as caught or surviving — in the
log and at the top of the pull-request comment. That is a warning unless you set
block-on-budget. The budget covers the mutation run, including its first
build, not the pre-flight.
No second cold build
- uses: Swatinem/rust-cache@v2
- uses: lucheeseng827/mergestro-gate@v0.6.1
with:
in-place: "true"
By default mutants are built in a scratch copy without target/, so every run
pays a cold build even right after the pre-flight built the same tree.
in-place mutates the checkout instead and reuses that build — on a small
crate the first mutant's build went from 4 s to 0.2 s. Mutants then run one at a time.
It edits files while it runs and puts them back, so use it on CI checkouts, not on a
working tree you are editing.
Since the last run
With comment: "true" (the default) the pull-request comment says what
changed since the previous push:
Since the last run: 1 new · 2 still open · 3 resolved
New survivors are marked, and fixed ones are listed as resolved. A run that did not re-test everything — the suite was red, or the budget ran out — never claims anything was fixed.
Inline review comments
permissions: { contents: read, pull-requests: write }
# …
- uses: lucheeseng827/mergestro-gate@v0.6.1
with:
comment-inline: "true"
Each surviving mutant is also posted as a review comment on its line, once: reruns do not repeat it. Only lines the pull request's diff shows get one; the rest stay in the summary comment. If posting the review fails, that is a warning and it is retried next run. GitHub only.
Code scanning (SARIF)
permissions: { contents: read, pull-requests: write, security-events: write }
# …
- uses: lucheeseng827/mergestro-gate@v0.6.1
with:
sarif: mergestro.sarif
upload-sarif: "true"
Survivors, assertion-free tests and the pattern lanes' findings go to GitHub code
scanning as SARIF 2.1.0: they appear in the pull request's Files changed tab and
the Security tab, with code scanning's own dismiss and reopen. Critical and high
survivors are errors, medium are warnings, low are notes. The upload runs even when the
gate blocks. From the CLI: slop-gate --sarif out.sarif.
Weakened tests
Mutation testing cannot see a pull request that only deletes a test or removes assertions: there is no changed code to mutate, so it used to pass silently. The weakened-tests lane compares each changed file's tests before and after and reports:
test-removed— a test that no longer exists anywhere in the change (one that moved to another file under the same name is not reported);assertions-reduced— a test that checks less than it did, countingassert*!macros and#[should_panic].
It is advisory. To make it block:
block-on-pattern: "weakened-tests" # or one rule, e.g. test-removed
Sharding a large run
For a change with more mutants than one job should run, split the Rust mutants across a CI matrix and merge the results into one verdict:
# in each matrix job, k = 1..4
$ slop-gate --base origin/main --shard k/4 --format json --advisory > shard.json
# in one job after them all
$ slop-gate merge-reports --base origin/main --comment shard-*/shard.json
merge-reports adds the shards up, decides the verdict on the
total (so max-survivors applies to the whole run) and posts
one comment. It refuses a missing or duplicated shard, and shards run on different
commits or settings: a missing shard is untested mutants, not a clean result. A complete
GitHub Actions matrix is in the
operations guide.
Putting it together
on: pull_request
permissions: { contents: read, pull-requests: write, security-events: write }
jobs:
gate:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: lucheeseng827/mergestro-gate@v0.6.1
with:
in-place: "true"
budget: "12m"
comment-inline: "true"
sarif: mergestro.sarif
upload-sarif: "true"
block-on-pattern: "weakened-tests"
0.6.1: a smaller image
The Docker image went from 1.08 GB to 804 MB (359 to 278 MB compressed). The runtime is
plain Alpine plus the Rust toolchain without the parts mutants never use — the
rust-lld linker, the WebAssembly component linker, the nightly-only sanitizer
runtimes and the docs — with gate results unchanged. The full list of changes is in the
changelog.