mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
ci: added npm script does not select every mutant
Run 2795: changed_mutants shard 1/3 hit the 30-minute job limit with zero failures. package.json sits in the guard-input closure of 195 of 590 mutants (`npm run …`, `npx …`), so adding one script — toolchain:check — selected nearly the whole registry. A guard depends only on what it calls: a changed or removed existing script, dependencies, engines. An added script is not an input of any earlier guard. packageJsonRelevance() decides from the base and head package.json; unparsable or anything outside scripts still counts as relevant. For the same range the selection drops from 209 to 38. Issue: #496 User-Visible: no
This commit is contained in:
@@ -8092,6 +8092,28 @@ export function selectChangedMutants(mutants, changedFiles, exists, options = {}
|
||||
|| inputsOf(m.guard).some((file) => changed.has(file)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Задевает ли правка package.json хоть один гвард (#496, run 2795).
|
||||
*
|
||||
* package.json — вход почти каждого гварда (`npm run …`, `npx …`), поэтому любое
|
||||
* его касание отбирало ~195 мутантов из 590 и шард упирался в 30-минутный
|
||||
* лимит job. Но гвард зависит только от того, что он вызывает: изменённый или
|
||||
* удалённый существующий script, зависимости, engines/overrides. ДОБАВЛЕННЫЙ
|
||||
* script никакой прежний гвард не вызывает — он не вход. Любое другое поле или
|
||||
* непрочитанная база — считается задевающим: сторона ошибки — лишний прогон.
|
||||
*/
|
||||
export function packageJsonRelevance(baseText, headText) {
|
||||
let base; let head;
|
||||
try { base = JSON.parse(baseText); head = JSON.parse(headText); } catch { return { relevant: true, reason: 'package.json не разобран' }; }
|
||||
const baseScripts = base.scripts || {}; const headScripts = head.scripts || {};
|
||||
const changedScripts = Object.keys(baseScripts).filter((k) => headScripts[k] !== baseScripts[k]);
|
||||
if (changedScripts.length) return { relevant: true, reason: `изменены/удалены scripts: ${changedScripts.join(', ')}` };
|
||||
const rest = (pkg) => JSON.stringify({ ...pkg, scripts: undefined, description: undefined });
|
||||
if (rest(base) !== rest(head)) return { relevant: true, reason: 'изменены поля вне scripts (зависимости, engines, …)' };
|
||||
const added = Object.keys(headScripts).filter((k) => !(k in baseScripts));
|
||||
return { relevant: false, reason: `только добавлены scripts: ${added.join(', ') || '—'}` };
|
||||
}
|
||||
|
||||
/**
|
||||
* Определения реестра, добавленные или изменённые относительно базы (#492
|
||||
* §6.4): реестр базы читается через `git show` во временный модуль рядом с
|
||||
@@ -8173,8 +8195,19 @@ async function main(argv) {
|
||||
console.error(`git diff ${range} не удался:\n${diff.stderr}`);
|
||||
return 2;
|
||||
}
|
||||
const files = diff.stdout.split('\n').filter(Boolean);
|
||||
let files = diff.stdout.split('\n').filter(Boolean);
|
||||
const before = selected.length;
|
||||
// #496: package.json отбирает ~все гварды; добавленный script — не вход.
|
||||
if (files.includes('package.json') && range.includes('..')) {
|
||||
const [baseRef, headRef] = range.split('..');
|
||||
const show = (ref) => spawnSync('git', ['-C', repoRoot, 'show', `${ref}:package.json`], { encoding: 'utf8' });
|
||||
const baseShown = show(baseRef); const headShown = show(headRef || 'HEAD');
|
||||
const relevance = (baseShown.status === 0 && headShown.status === 0)
|
||||
? packageJsonRelevance(baseShown.stdout, headShown.stdout)
|
||||
: { relevant: true, reason: 'package.json базы или головы не прочитан' };
|
||||
console.log(`package.json в диффе: ${relevance.relevant ? 'задевает гварды' : 'гварды не задевает'} — ${relevance.reason}`);
|
||||
if (!relevance.relevant) files = files.filter((f) => f !== 'package.json');
|
||||
}
|
||||
// #492 §6.4: правка реестра отбирает добавленные и изменённые определения
|
||||
// явно — новый свидетель не обязан трогать чужие patch/guard-файлы.
|
||||
let base = null;
|
||||
|
||||
@@ -7,7 +7,7 @@ import { join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
import {
|
||||
MUTANTS, applyPatches, guardNeedsBundle, guardNeedsTestBuild, selectChangedMutants, shardMutants, guardFiles,
|
||||
MUTANTS, applyPatches, guardNeedsBundle, guardNeedsTestBuild, selectChangedMutants, shardMutants, guardFiles, packageJsonRelevance,
|
||||
witnessFingerprint, readLedger, recordCaught, splitByLedger, LEDGER_SCHEMA,
|
||||
} from '../scripts/mutation-gate.mjs';
|
||||
|
||||
@@ -560,3 +560,19 @@ test('#496: гварды check-docs не требуют свежих скрин
|
||||
assert.deepEqual(strict.map((m) => m.id), []);
|
||||
assert.ok(MUTANTS.some((m) => /check-docs\.mjs --screenshots=warn/.test(m.guard)), 'хотя бы один гвард check-docs есть');
|
||||
});
|
||||
|
||||
// #496 (run 2795): package.json — вход почти каждого гварда, и добавленный
|
||||
// npm-script отбирал 195 мутантов из 590 — шард упирался в 30-минутный лимит.
|
||||
test('#496: добавленный script в package.json гварды не задевает, изменённый или зависимости — задевают', () => {
|
||||
const base = JSON.stringify({ name: 'x', scripts: { test: 'node --test', build: 'rollup -c' }, devDependencies: { playwright: '^1.62.0' } });
|
||||
const added = JSON.stringify({ name: 'x', scripts: { test: 'node --test', build: 'rollup -c', 'toolchain:check': 'node scripts/toolchain-pins.mjs --check' }, devDependencies: { playwright: '^1.62.0' } });
|
||||
assert.deepEqual(packageJsonRelevance(base, added), { relevant: false, reason: 'только добавлены scripts: toolchain:check' });
|
||||
const changed = JSON.stringify({ name: 'x', scripts: { test: 'echo skip', build: 'rollup -c' }, devDependencies: { playwright: '^1.62.0' } });
|
||||
assert.equal(packageJsonRelevance(base, changed).relevant, true);
|
||||
assert.match(packageJsonRelevance(base, changed).reason, /test/);
|
||||
const removed = JSON.stringify({ name: 'x', scripts: { build: 'rollup -c' }, devDependencies: { playwright: '^1.62.0' } });
|
||||
assert.equal(packageJsonRelevance(base, removed).relevant, true);
|
||||
const deps = JSON.stringify({ name: 'x', scripts: { test: 'node --test', build: 'rollup -c' }, devDependencies: { playwright: '^1.63.0' } });
|
||||
assert.equal(packageJsonRelevance(base, deps).relevant, true);
|
||||
assert.equal(packageJsonRelevance('{not json', added).relevant, true, 'неразобранное — задевает: сторона ошибки — лишний прогон');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user