feat(ci): mutation PR-time lane + mutants-pr wrapper (#182 PR 3)#233
Conversation
…PR 3)
Adds a label-gated PR-time mutation lane and the xtask wrapper it
invokes, keeping mutation off every PR's hot path while making it cheap
to opt in when warranted.
cargo xtask mutants-pr --changed [--base origin/main] [--dry-run]
is a thin wrapper around `cargo-mutants` that:
1. Refuses to run without `--changed`. Whole-workspace mutation is
intentionally not part of the PR-time lane — that lives in the
weekly schedule (see docs/ci/test-evidence-lanes.md).
2. Computes `git diff <base>...HEAD --name-only -- '*.rs'`, filters
out `tests/` and `benches/` paths (cargo-mutants only mutates
production source).
3. Runs `cargo mutants --no-shuffle --file <each-path>` (or `--list`
when `--dry-run` is passed).
4. If `cargo-mutants` is missing locally, prints install instructions
and exits advisory-success rather than erroring. CI installs the
tool before invoking.
5. Surfaces `cargo mutants` non-zero exit codes — surviving mutants
are load-bearing failures, unlike ripr's purely advisory findings.
`.github/workflows/mutation.yml` adds the `mutants-pr` job alongside
the existing `mutants-weekly` job. The PR job's gate:
if: github.event_name == 'pull_request' && (
contains(github.event.pull_request.labels.*.name, 'mutation') ||
contains(github.event.pull_request.labels.*.name, 'full-ci')
)
The weekly job's crate list stays unchanged (shipper-duration,
shipper-types, shipper-config). Expanding it to the full trust-critical
surface (shipper-core, shipper-encrypt, etc.) is too expensive for a
60-minute job and is its own future rollout step; the policy stays
about routing, not coverage scope.
xtask/src/mutants.rs new — 3 unit tests
xtask/src/main.rs `mutants-pr` subcommand wiring
.github/workflows/mutation.yml two-job split (mutants-weekly +
mutants-pr)
docs/ci/test-evidence-lanes.md advisory-routed table + targeted
mutation section updated
CHANGELOG.md Added entry
Tests:
mutants::tests::install_hint_mentions_cargo_install (new)
mutants::tests::args_defaults_are_explicit (new)
mutants::tests::changed_requires_the_flag (new)
24/24 tests passing.
Refs #182 (this is the third PR in the #182 sequence: advisory lane in
#230, --base + projection in #231, badges in #232, mutation scoping
here).
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (5)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Droid finished @EffortlessSteven's task —— View job Droid Review — PR #233 ✅ ApprovedValidation: 0 candidate comments validated; 0 approved; 0 rejected. Summary: LGTM — the PR cleanly adds a label-gated PR-time mutation lane and the corresponding xtask wrapper. The Inspected surfaces:
Checks performed:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a7a809bbd3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let mut cmd = Command::new("cargo"); | ||
| cmd.arg("mutants").arg("--no-shuffle"); |
There was a problem hiding this comment.
Select workspace packages before applying file filters
When the label-gated lane runs from the repository root, this command does not pass --workspace or package selectors, so cargo-mutants uses the workspace defaults before applying these --file filters. Observed in Cargo.toml, this workspace has default-members = ["crates/shipper", "crates/shipper-cli"], while the documented targeted lane is meant to cover changes in crates like shipper-core, shipper-encrypt, and other trust-critical non-default members. The cargo-mutants workspace docs state that package selection in a virtual workspace uses default-members when present and that --file is combined with that selection, so a labeled PR that changes only crates/shipper-core/src/... can list the changed file but generate no mutants for it. Add --workspace or infer/pass the changed packages so the PR-time lane actually exercises non-default crates. Validation: cargo xtask mutants-pr --changed --dry-run --base <base> on a branch touching only crates/shipper-core/src/..., then confirm mutants are listed after the package-selection fix. Reference: https://mutants.rs/workspaces.html
Useful? React with 👍 / 👎.
`<REF>` inside a doc comment is parsed by rustdoc as an invalid HTML tag under `-D rustdoc::invalid-html-tags` (implied by RUSTDOCFLAGS=-D warnings on the CI Documentation job). Wrap the whole Behaviour block in a ```text fence so the angle brackets are literal.
|
Droid finished @EffortlessSteven's task —— View job Droid Review — PR #233 ✅ Approved Validation: 0 candidate comments validated; 0 approved; 0 rejected. Summary: LGTM — the PR cleanly adds a label-gated PR-time mutation lane and the corresponding xtask wrapper. The Inspected surfaces:
|
…) (#237) Stabilises `concurrent_version_exists_checks` (and any other test using `with_multi_server`) on slow macOS CI runners. Hit three times in a single rollout session (#233, #234, #236) as `version_exists: registry request failed -> operation timed out` against the local tiny_http mock. Root cause: the helper's accept loop blocked on `handler(req)` until each response was fully written before returning to `recv_timeout`. With 5 concurrent reqwest clients hitting the same loopback socket, the remaining clients sat in the kernel's TCP backlog long enough to exceed reqwest's default OS-level connect timeout. Windows and Linux runners process the queue fast enough to mask the bug; macOS does not. Fix: spawn one worker thread per accepted request and let the accept loop return to `recv_timeout` immediately. The accept loop still serialises on `recv_timeout` (tiny_http requires that), but handlers run in parallel, so the kernel's listen queue drains as fast as connections arrive. Other changes: - `recv_timeout` bumped from 30s to 60s for additional headroom. - Trait bound on the handler closure goes from `Fn + Send + 'static` to `Fn + Send + Sync + 'static` (required to wrap the handler in `Arc` for clone-into-workers). All existing call sites use closures that already satisfy `Sync`. - The accept thread joins worker threads before returning so any panic in a handler surfaces in CI. cargo test -p shipper-registry --lib passes 258/258 locally (Windows). The fix targets a macOS-specific timing bug, so CI is the real verification.
Summary
Third and final PR in the #182 sequence. Adds the label-gated PR-time mutation lane and the
cargo xtask mutants-prwrapper. Mutation testing stays explicitly off the default PR hot path — only PRs labeledmutationorfull-ciexercise it.What changed
cargo xtask mutants-pr --changed [--base origin/main] [--dry-run](new) — thin wrapper aroundcargo-mutants. Computesgit diff <base>...HEAD --name-only -- '*.rs', filterstests//benches/, then runscargo mutants --no-shuffle --file <each>.--dry-runmaps tocargo mutants --list. Refuses bare invocation:--changedis currently required so whole-workspace runs cannot accidentally enter the PR-time lane..github/workflows/mutation.yml— split into two jobs:mutants-weekly(unchanged behaviour) — Sunday 04:00 UTC,workflow_dispatch, coversshipper-duration/shipper-types/shipper-config.mutants-pr(new) —pull_requesttrigger with the label gate. Only runs whenmutationorfull-ciis present on the PR.docs/ci/test-evidence-lanes.md— advisory-routed table gets amutants-prrow; the targeted-mutation section documents local invocation patterns and when a maintainer should apply the label.Why the weekly job's crate list stays unchanged
The trust-critical surface (shipper-core, shipper-encrypt, shipper-output-sanitizer, shipper-cargo-failure, shipper-sparse-index, shipper-registry, shipper-cli, shipper) is too expensive for a 60-minute job. Expanding coverage is its own rollout step — the policy this PR fixes is routing (label-gated PR + scheduled weekly), not coverage scope. The commit message and docs say this explicitly so the gap is visible.
Why
cargo mutantsnon-zero exits are surfaced (not advisory)Unlike ripr, which is purely a signal, mutation runs as a runtime backstop. When a maintainer applies the
mutationlabel they've explicitly asked for execution-backed confirmation; if a mutant survives, that should fail the job. The label gate already prevents this from being a PR tax — once you opt in, the result is load-bearing.Test plan
cargo build -p xtask— cleancargo test -p xtask— 24/24 passing (three new:install_hint_mentions_cargo_install,args_defaults_are_explicit,changed_requires_the_flag)cargo clippy --workspace --all-targets -- -D warnings— cleancargo fmt --all -- --check— cleancargo xtask mutants-pr --changed --dry-runlocally — exits success when no diff vsorigin/maincargo xtask check-workflow-surfaces --mode blocking-allowlist— clean (mutation.ymlalready receipted; no new workflows)cargo xtask check-process-policy --mode blocking-allowlist— cleancargo xtask check-network-policy --mode blocking-allowlist— cleanmutationlabel — it doesn't)Scope explicitly excludes (deferred)
mutationlabel based on riprwarning-level findings.--allmode on the wrapper (whole-workspace mutation has its own job already).Refs #182. This is the third PR in the #182 sequence: advisory lane in #230,
--base+ projection in #231, badges in #232, mutation scoping here. #182 can close after this lands.