ci(mutants): skip the nightly registry on a proven tree; install only the shard's environment

#620. Mutants were ~70 % of CI machine time.

1. mutation-gate.yml: a green full run (aggregator verified all six shards)
   leaves a cache marker keyed by material tree + workflow SHA. A scheduled
   night with the same tree and workflow, marker not older than 7 days, skips
   the shards and writes "reused from run N" to the run summary. Red runs
   leave no marker, so the next night runs again and still files the issue
   (#472). Manual dispatch always runs the full registry. Decision is a pure
   function in scripts/mutation-nightly-reuse.mjs.
2. changed_mutants: the shard plan (already computed before setup, #518) now
   names the environment of its guards (plan-browser=/plan-python=, from
   scripts/mutation-environment.mjs). Python + backend deps only for shards
   with pytest guards, Chromium only for shards whose guards reach
   Playwright; pip wheels cached. Every shard still runs and reports, so the
   six mutant jobs of the review proof are unchanged.

Item 3 of the issue (smoke guards -> node --test) is out of scope here.

Issue: #620
User-Visible: no
This commit is contained in:
Claude
2026-09-24 05:48:42 +00:00
committed by claude[bot]
parent 7bb55c2a41
commit 44ee23ee33
10 changed files with 739 additions and 11 deletions
+25 -7
View File
@@ -652,29 +652,47 @@ jobs:
fi
echo "диапазон: $base..$HEAD_SHA"
echo "base=$base" >> "$GITHUB_OUTPUT"
count=$(node scripts/mutation-gate.mjs --changed="$base..$HEAD_SHA" --shard="$SHARD/6" \
--ledger="artifacts/mutation-ledger/shard-$SHARD.json" --plan-only \
| tee /dev/stderr | sed -n 's/^plan=//p' | tail -1)
plan="$RUNNER_TEMP/mutation-plan-$SHARD.txt"
node scripts/mutation-gate.mjs --changed="$base..$HEAD_SHA" --shard="$SHARD/6" \
--ledger="artifacts/mutation-ledger/shard-$SHARD.json" --plan-only > "$plan"
cat "$plan"
count=$(sed -n 's/^plan=//p' "$plan" | tail -1)
echo "к прогону в этом шарде: ${count:-0}"
echo "count=${count:-0}" >> "$GITHUB_OUTPUT"
# #620: план называет и окружение своих гардов. Непрочитанная строка
# при непустом плане значит «ставить»: лишняя установка стоит минуты,
# недоустановка — красный чистый прогон и лишний круг задачи.
if [ "${count:-0}" != "0" ]; then
browser=$(sed -n 's/^plan-browser=//p' "$plan" | tail -1)
python=$(sed -n 's/^plan-python=//p' "$plan" | tail -1)
echo "окружение шарда: браузер ${browser:-true}, Python ${python:-true}"
echo "browser=${browser:-true}" >> "$GITHUB_OUTPUT"
echo "python=${python:-true}" >> "$GITHUB_OUTPUT"
fi
- if: steps.plan.outputs.count != '0'
run: npm ci
- if: steps.plan.outputs.count != '0'
# Python и зависимости бэкенда — только шарду с pytest-гардами (#620);
# колёса pip кэшируются по requirements.txt.
- if: steps.plan.outputs.count != '0' && steps.plan.outputs.python == 'true'
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
with:
python-version: '3.14'
cache: pip
cache-dependency-path: tests_backend/requirements.txt
- name: Backend test dependencies
if: steps.plan.outputs.count != '0'
if: steps.plan.outputs.count != '0' && steps.plan.outputs.python == 'true'
run: pip install -r tests_backend/requirements.txt
# Chromium — только шарду с браузерными гардами (#620): смоки, golden и
# юниты, чей граф исполнения импортирует Playwright.
- name: Кэш браузеров Playwright
id: pw
if: steps.plan.outputs.count != '0'
if: steps.plan.outputs.count != '0' && steps.plan.outputs.browser == 'true'
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
- name: Установить Chromium
if: steps.plan.outputs.count != '0' && steps.pw.outputs.cache-hit != 'true'
if: steps.plan.outputs.count != '0' && steps.plan.outputs.browser == 'true' && steps.pw.outputs.cache-hit != 'true'
run: npx playwright install --with-deps chromium
- name: Затронутые мутанты ловятся
if: steps.plan.outputs.count != '0'