From ae63786cfb2d67616ce6b13732fb6ccfc23b7ed7 Mon Sep 17 00:00:00 2001 From: Yuneng Jiang Date: Tue, 25 Aug 2026 23:16:40 -0700 Subject: [PATCH] fix(ci): let the mutation workflow find covered lines so it generates mutants mutmut's gather_coverage() looks each source file's covered lines up by absolute path, but [tool.coverage.run] sets relative_files = true, so every lookup misses. With mutate_only_covered_lines = true that leaves no line eligible for mutation, and the run ends on "Stopping early, because we could not find any test case for any mutant" after spending 26 minutes collecting coverage. The last four dispatches all died that way. Point COVERAGE_RCFILE at a small rc file for mutation runs only, so the coverage instance mutmut builds stores absolute paths. Scoped to one module locally this takes the run from 0 mutants to 8 generated and 8 killed. Also give the mutmut step a deadline inside the job's own. mutmut records each mutant's verdict to mutants/mutmut-stats.json as it finishes, so a run that outlasts its budget still scores what it got through, but a cancelled job skips the report and upload steps and publishes nothing. That is how the two runs before these four ended. Ignore mutants/ and .venv-mutmut, which a local run leaves behind untracked. --- .github/mutmut-coverage.rc | 5 +++++ .github/workflows/mutation-test.yml | 9 +++++++++ .gitignore | 2 ++ 3 files changed, 16 insertions(+) create mode 100644 .github/mutmut-coverage.rc diff --git a/.github/mutmut-coverage.rc b/.github/mutmut-coverage.rc new file mode 100644 index 00000000000..c607df68853 --- /dev/null +++ b/.github/mutmut-coverage.rc @@ -0,0 +1,5 @@ +# mutmut's gather_coverage() looks covered lines up by absolute path, so the +# repo's `relative_files = true` makes every lookup miss and mutmut generates +# zero mutants. Point COVERAGE_RCFILE here for mutation runs only. +[run] +relative_files = false diff --git a/.github/workflows/mutation-test.yml b/.github/workflows/mutation-test.yml index 602c26a3e98..6c278b152a1 100644 --- a/.github/workflows/mutation-test.yml +++ b/.github/workflows/mutation-test.yml @@ -87,11 +87,20 @@ jobs: run: | uv pip uninstall pytest-retry || true + # Ends before the job's own deadline so a run that outlasts the budget is + # still followed by the report and upload steps. mutmut writes each + # mutant's result to mutants/mutmut-stats.json as it goes, so an + # interrupted run still scores the mutants it finished; a cancelled job + # skips those steps and publishes nothing at all. - name: Run mutmut + timeout-minutes: 300 env: # Make the mutants/ sandbox win over site-packages on sys.path so the # trampolined files are imported instead of the installed copy. PYTHONPATH: ${{ github.workspace }}/mutants + # Without this mutmut finds no covered lines and generates 0 mutants. + # See the file itself for why. + COVERAGE_RCFILE: ${{ github.workspace }}/.github/mutmut-coverage.rc run: | set -o pipefail mkdir -p mutants diff --git a/.gitignore b/.gitignore index 201e02f2189..deb0acae56e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ tests/e2e/.fixtures/ .venv-typecheck .venv_policy_test +.venv-mutmut +mutants/ .env .claude CLAUDE.local.md