mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
ci(validate): мутанты по диффу — только по явному запросу, не на кандидате беты и не в full (#601)
`mutantsRequested` отвечает true лишь на PR и `workflow_dispatch mutants=true` (конвейер ревью, слияние кандидата). Трейлер `Release:` и `full=true` включают тяжёлые гейты — смоки, golden, performance_smoke — но не мутантов: к бете каждая задача прогнана ими на ревью и на слитом после ребейза кандидате, ночь покрыта полным реестром (mutation-gate.yml, #513), а ручной полный прогон ради артефакта эталонов и приёмка эталонов с трейлером на ветке задачи платили шестью job впустую. `schedule` мутантов тоже не запрашивает. Политика release в ci-proof — `mutants: false`: иначе proof кандидата беты без запрошенных mutant-jobs объявлялся бы stale. review и merge по-прежнему требуют шесть исполненных job (#541). Тесты: #510 AC1 переписан под новый список, ci-proof — release без мутантов green, лёгкий stale, review/merge без запроса stale. Мутанты протокола: `mutants-run-on-every-push` перепривязан, новые `mutants-run-on-beta-candidate`, `mutants-run-on-full-dispatch`, `release-proof-demands-mutant-jobs`. PROCESS.md §10.4, AGENTS.md, docs/TESTING.md, комментарии workflow. Issue: #601 User-Visible: no
This commit is contained in:
@@ -8,7 +8,9 @@ name: Мутационный гейт
|
||||
# Прогон дорогой и проверяет не продукт, а тесты, поэтому он не входит ни в
|
||||
# Validate, ни в цикл разработки, ни в релизный гейт (#513, решение владельца
|
||||
# 09.09): каждую ночь по расписанию, отказ — issue с отчётом (#472).
|
||||
# Мутанты, задетые диффом, конвейер ревью гоняет отдельно на кандидате (#510).
|
||||
# Мутанты, задетые диффом, конвейер ревью и слияние гоняют отдельно на своём
|
||||
# кандидате (#510); кандидат беты, `full=true` и ночной Validate их не
|
||||
# запрашивают (#601) — ночью достаточно этого полного реестра.
|
||||
# Дешёвая половина — «якоря патчей живы, guard-файлы существуют» — идёт с
|
||||
# обычными юнитами: test/mutation-gate.test.mjs.
|
||||
#
|
||||
|
||||
@@ -18,13 +18,15 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
full:
|
||||
description: 'Полный набор: смоки, golden, performance_smoke, мутанты по диффу'
|
||||
description: 'Полный набор: смоки, golden, performance_smoke (без мутантов — #601)'
|
||||
type: boolean
|
||||
default: true
|
||||
# Мутанты по диффу бегут по запросу (#510): ревью-конвейер и слияние
|
||||
# кандидата запускают `-f full=false -f mutants=true` на материале.
|
||||
# Мутанты по диффу бегут только по явному запросу (#510, #601):
|
||||
# ревью-конвейер и слияние кандидата запускают `-f full=false
|
||||
# -f mutants=true` на материале. `full=true`, трейлер `Release:` и ночь
|
||||
# их не включают — ночью идёт полный реестр (mutation-gate.yml).
|
||||
mutants:
|
||||
description: 'Мутанты по диффу на этом SHA (кандидат ревью)'
|
||||
description: 'Мутанты по диффу на этом SHA (кандидат ревью или слияния)'
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
@@ -556,9 +558,11 @@ jobs:
|
||||
# детерминированных непересекающихся шарда вместо ослабления проверки.
|
||||
# С #510 job бежит не на каждом пуше, а на кандидате: ревью-конвейер и
|
||||
# слияние кандидата запускают Validate по кнопке с `mutants=true` на
|
||||
# материале, ночной прогон/PR/кандидат беты берут её сами. За 08–09.09 на
|
||||
# промежуточных пушах она стоила 48 из 56 часов job-минут и в основном
|
||||
# отменялась следующим пушем. Когда мутанты запрошены, job бежит даже при
|
||||
# материале; PR берёт её сам. За 08–09.09 на промежуточных пушах она стоила
|
||||
# 48 из 56 часов job-минут и в основном отменялась следующим пушем. С #601
|
||||
# её не запрашивают ни `full=true`, ни кандидат беты (`Release:`): к бете
|
||||
# каждая задача уже прогнана мутантами на ревью и на слитом кандидате, а
|
||||
# ночь покрыта полным реестром. Когда мутанты запрошены, job бежит даже при
|
||||
# диффе без входов (отбор пустой, минута на checkout): гейт ревью читает
|
||||
# её исход по job, и skipped был бы неотличим от «не запрашивали».
|
||||
changed_mutants:
|
||||
|
||||
@@ -425,10 +425,14 @@ until the verdict or the return arrives: a push on top of a running review cance
|
||||
it (10–20 runner minutes) and, after the material is fixed, also the merge (#312).
|
||||
Set `S7` once per round, not after every CI fix: the pipeline now runs Validate
|
||||
with the diff mutants on the material itself and returns a red one to `S6` without
|
||||
spending a review cycle. Mutants by diff no longer run on ordinary pushes — only
|
||||
on the review candidate, the merge candidate, the beta candidate, PRs and the
|
||||
nightly run — so a routine push costs ~3 minutes; 08–09.09 they cost 48 of 56
|
||||
Validate job-hours and were mostly cancelled by the next push.
|
||||
spending a review cycle. Mutants by diff run only where they are explicitly
|
||||
requested — the review candidate, the merge candidate (both dispatch Validate
|
||||
with `mutants=true`) and PRs (#510, #601). Ordinary pushes, the beta candidate
|
||||
(`Release:` trailer) and `full=true` do not request them: a routine push costs
|
||||
~3 minutes (08–09.09 mutants cost 48 of 56 Validate job-hours and were mostly
|
||||
cancelled by the next push), and by the beta every issue has already been
|
||||
mutated twice — on review and on the rebased merge candidate; the night runs
|
||||
the full registry (`mutation-gate.yml`), not a diff subset.
|
||||
|
||||
The full smoke set, `golden` and `performance_smoke` still belong to the
|
||||
pre-beta run — which is then mandatory and complete. WSL runs of the full HA
|
||||
|
||||
+11
-4
@@ -962,10 +962,17 @@ Medium-находки вне скоупа задачи (#202), кладёт до
|
||||
`workflow_dispatch validate.yml -f mutants=true`) и ждёт его до 45 минут. Красный или
|
||||
пропавший прогон возвращает задачу в `S6-in-progress` с комментарием и ссылкой —
|
||||
код никто не читал, цикл ревью не израсходован. Мутанты по диффу вообще бегут
|
||||
только по запросу: на кандидате ревью, кандидате слияния (#492), кандидате беты,
|
||||
в ночном прогоне и на PR; обычный push обходится дешёвыми гейтами (~3 минуты).
|
||||
За 08–09.09 мутанты на каждом промежуточном пуше стоили 48 из 56 часов
|
||||
job-минут Validate и в основном отменялись следующим пушем.
|
||||
только по явному запросу: на кандидате ревью, кандидате слияния (#492) — оба
|
||||
диспатчат Validate с `mutants=true` — и на PR, где Validate единственный сигнал;
|
||||
обычный push обходится дешёвыми гейтами (~3 минуты). За 08–09.09 мутанты на
|
||||
каждом промежуточном пуше стоили 48 из 56 часов job-минут Validate и в основном
|
||||
отменялись следующим пушем. **Кандидат беты (`Release:`), `full=true` и ночь
|
||||
мутантов не запрашивают** (#601, решение владельца 20.09): мутационный гейт
|
||||
проверяет тесты, а не продукт (#513), к бете каждая задача прогнана им дважды —
|
||||
на ревью и на слитом после ребейза кандидате, — а ночью идёт полный реестр
|
||||
(`mutation-gate.yml`, 01:00 UTC). Релизный гейт (#541) требует полного Validate,
|
||||
но не mutant-jobs; для ревью и слияния шесть исполненных mutant-jobs остаются
|
||||
обязательными.
|
||||
|
||||
Ожидание gates, работа модели и публикация/интеграция — три независимых jobs
|
||||
(#551) с отдельными бюджетами 55, 45 и 55 минут. Поэтому долгий Validate не
|
||||
|
||||
+10
-7
@@ -82,13 +82,16 @@ browser/backend guard нельзя. Все недоказанные исходы
|
||||
рантайме, но не статически: `if (eps < 0)`, `String(mode) === 'mutant-never-…'`,
|
||||
`if (walls.length >= 0) return walls.slice();`. В `.mjs` этого ограничения нет.
|
||||
|
||||
В CI `changed_mutants` бежит не на каждом пуше, а по запросу (#510):
|
||||
`workflow_dispatch validate.yml -f mutants=true` (его делают ревью-конвейер на
|
||||
материале ревью и слияние на кандидате), `full=true` (ночь, кнопка), PR и кандидат
|
||||
беты (трейлер `Release:`). Обычный push в ветку задачи обходится дешёвыми гейтами:
|
||||
за 08–09.09 мутанты на промежуточных пушах стоили 48 из 56 часов job-минут и в
|
||||
основном отменялись следующим пушем. Доказательство мутантов для ревью — именно
|
||||
dispatch-прогон на точном SHA; зелёный push-прогон им не является.
|
||||
В CI `changed_mutants` бежит не на каждом пуше, а по явному запросу (#510,
|
||||
сужено в #601): `workflow_dispatch validate.yml -f mutants=true` (его делают
|
||||
ревью-конвейер на материале ревью и слияние на кандидате) и PR. `full=true`
|
||||
(ночь, кнопка), кандидат беты (трейлер `Release:`) и обычный push мутантов не
|
||||
запрашивают: за 08–09.09 мутанты на промежуточных пушах стоили 48 из 56 часов
|
||||
job-минут и в основном отменялись следующим пушем, а к бете каждая задача уже
|
||||
прогнана ими на ревью и на слитом кандидате; ночь покрыта полным реестром
|
||||
(`mutation-gate.yml`). Доказательство мутантов для ревью — именно dispatch-прогон
|
||||
на точном SHA; зелёный push-прогон им не является. Релизный гейт mutant-jobs не
|
||||
требует (`CI_PROOF_POLICIES.release.mutants = false`).
|
||||
|
||||
`changed_mutants` добавляет `--ledger=<файл>` — журнал доказанных
|
||||
свидетелей (#481, #550): после каждого пойманного мутанта в файл пишутся тип
|
||||
|
||||
@@ -18,10 +18,15 @@ export const CI_PROOF_STATES = Object.freeze([
|
||||
'green', 'missing', 'pending', 'cancelled', 'stale', 'failed',
|
||||
]);
|
||||
|
||||
// Мутанты по диффу — доказательство для ревью и слияния (#510): без шести
|
||||
// исполненных mutant-jobs ни то ни другое не разрешается. Релиз их не
|
||||
// требует (#601): к кандидату беты каждая задача прогнана ими на ревью и на
|
||||
// слитом кандидате, а `Release:` мутантов больше не запрашивает — политика
|
||||
// с `mutants: true` объявляла бы каждый кандидат беты `stale`.
|
||||
export const CI_PROOF_POLICIES = Object.freeze({
|
||||
review: Object.freeze({ name: 'review', full: false, mutants: true }),
|
||||
merge: Object.freeze({ name: 'merge', full: false, mutants: true }),
|
||||
release: Object.freeze({ name: 'release', full: true, mutants: true }),
|
||||
release: Object.freeze({ name: 'release', full: true, mutants: false }),
|
||||
});
|
||||
|
||||
const JOB_RULES = Object.freeze({
|
||||
|
||||
@@ -96,17 +96,25 @@ export function heavyGatesRequested({ eventName, headMessage, fullInput } = {})
|
||||
}
|
||||
|
||||
/**
|
||||
* Нужны ли мутанты по диффу (#510). За 08–09.09 они съели 86 % job-минут
|
||||
* Validate, потому что бежали на каждом промежуточном пуше и отменялись
|
||||
* следующим. Место мутантов — кандидат: ревью-конвейер и слияние кандидата
|
||||
* запускают Validate по кнопке с `mutants=true`, ночной прогон и PR берут
|
||||
* полный набор, кандидат беты несёт трейлер `Release:`. Обычный push — нет.
|
||||
* Нужны ли мутанты по диффу (#510, сужено в #601). За 08–09.09 они съели 86 %
|
||||
* job-минут Validate, потому что бежали на каждом промежуточном пуше и
|
||||
* отменялись следующим. Место мутантов — кандидат ревью и кандидат слияния:
|
||||
* оба запускают Validate по кнопке с `mutants=true`; на PR Validate —
|
||||
* единственный сигнал, поэтому там тоже. Больше нигде: мутанты проверяют
|
||||
* тесты, а не продукт (#513), и к моменту беты каждая задача прогнана ими
|
||||
* дважды — на ревью и на слитом после ребейза кандидате. Трейлер `Release:`
|
||||
* и `full=true` включают тяжёлые гейты, но не мутантов — иначе ручной полный
|
||||
* прогон ради артефакта эталонов и приёмка эталонов с трейлером на ветке
|
||||
* задачи платили шестью job впустую (#601). Ночь — полный реестр
|
||||
* (mutation-gate.yml), не диффовое подмножество; `schedule` здесь тоже
|
||||
* не запрашивает, чтобы будущее расписание Validate не вернуло их молча.
|
||||
* CLI по-прежнему передаёт headMessage и fullInput вместе с остальным —
|
||||
* функция их не читает, и тест закрепляет, что они НЕ влияют на ответ.
|
||||
*/
|
||||
export function mutantsRequested({ eventName, headMessage, fullInput, mutantsInput } = {}) {
|
||||
export function mutantsRequested({ eventName, mutantsInput } = {}) {
|
||||
if (eventName === 'pull_request') return true;
|
||||
if (eventName === 'schedule') return true;
|
||||
if (eventName === 'workflow_dispatch') return String(fullInput) === 'true' || String(mutantsInput) === 'true';
|
||||
return hasReleaseTrailer(headMessage);
|
||||
if (eventName === 'workflow_dispatch') return String(mutantsInput) === 'true';
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9618,12 +9618,37 @@ const MUTANT_DEFINITIONS = [
|
||||
{
|
||||
id: 'mutants-run-on-every-push',
|
||||
guard: 'node --test --test-name-pattern="#510" test/classify-changes.test.mjs',
|
||||
because: 'mutants by diff belong to the review candidate, the PR, the nightly run and the beta '
|
||||
+ 'candidate — an ordinary push must not spend 3×8 runner minutes on them (#510 AC1)',
|
||||
because: 'mutants by diff belong to the review candidate, the merge candidate and the PR — an '
|
||||
+ 'ordinary push must not spend 3×8 runner minutes on them (#510 AC1, narrowed in #601)',
|
||||
patches: [{
|
||||
file: 'scripts/classify-changes.mjs',
|
||||
find: " if (eventName === 'workflow_dispatch') return String(fullInput) === 'true' || String(mutantsInput) === 'true';\n return hasReleaseTrailer(headMessage);\n}",
|
||||
replace: " if (eventName === 'workflow_dispatch') return String(fullInput) === 'true' || String(mutantsInput) === 'true';\n return true; // mutant: every push\n}",
|
||||
find: " if (eventName === 'workflow_dispatch') return String(mutantsInput) === 'true';\n return false;\n}",
|
||||
replace: " if (eventName === 'workflow_dispatch') return String(mutantsInput) === 'true';\n return true; // mutant: every push\n}",
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'mutants-run-on-beta-candidate',
|
||||
guard: 'node --test --test-name-pattern="#601" test/classify-changes.test.mjs',
|
||||
because: 'the `Release:` trailer requests the heavy gates, not the diff mutants (#601 AC1): by the '
|
||||
+ 'beta candidate every issue has already been mutated on its review and merge candidates, and '
|
||||
+ 'the trailer also lands on class-D baseline commits inside task branches (f342ccce) — six '
|
||||
+ 'mutant jobs there prove nothing about tests that did not change',
|
||||
patches: [{
|
||||
file: 'scripts/classify-changes.mjs',
|
||||
find: "export function mutantsRequested({ eventName, mutantsInput } = {}) {\n if (eventName === 'pull_request') return true;\n if (eventName === 'workflow_dispatch') return String(mutantsInput) === 'true';\n return false;\n}",
|
||||
replace: "export function mutantsRequested({ eventName, headMessage, mutantsInput } = {}) {\n if (eventName === 'pull_request') return true;\n if (eventName === 'workflow_dispatch') return String(mutantsInput) === 'true';\n return hasReleaseTrailer(headMessage); // mutant: beta candidate\n}",
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'mutants-run-on-full-dispatch',
|
||||
guard: 'node --test --test-name-pattern="#601" test/classify-changes.test.mjs',
|
||||
because: '`full=true` is the heavy set — smokes, golden, performance — and the nightly dispatch '
|
||||
+ '(#601 AC1). Tying the diff mutants to it made a manual full run for a golden artifact pay six '
|
||||
+ 'jobs the review conveyor then cancelled by concurrency, and duplicated the nightly registry',
|
||||
patches: [{
|
||||
file: 'scripts/classify-changes.mjs',
|
||||
find: "export function mutantsRequested({ eventName, mutantsInput } = {}) {\n if (eventName === 'pull_request') return true;\n if (eventName === 'workflow_dispatch') return String(mutantsInput) === 'true';",
|
||||
replace: "export function mutantsRequested({ eventName, fullInput, mutantsInput } = {}) {\n if (eventName === 'pull_request') return true;\n if (eventName === 'workflow_dispatch') return String(fullInput) === 'true' || String(mutantsInput) === 'true'; // mutant: full requests",
|
||||
}],
|
||||
},
|
||||
{
|
||||
@@ -9637,6 +9662,18 @@ const MUTANT_DEFINITIONS = [
|
||||
replace: " if (false && run.conclusion !== 'success')\n return result('failed', 'mutant'); // mutant: completed means green",
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'release-proof-demands-mutant-jobs',
|
||||
guard: 'node --test --test-name-pattern="#601" test/ci-proof.test.mjs',
|
||||
because: 'with #601 the beta candidate no longer requests diff mutants, so a release policy that '
|
||||
+ 'still demands them marks every candidate proof stale and blocks publish-prerelease on a green '
|
||||
+ 'full Validate (#601 AC3); review and merge keep demanding them (#541)',
|
||||
patches: [{
|
||||
file: 'scripts/ci-proof.mjs',
|
||||
find: " release: Object.freeze({ name: 'release', full: true, mutants: false }),",
|
||||
replace: " release: Object.freeze({ name: 'release', full: true, mutants: true }), // mutant: release demands mutants",
|
||||
}],
|
||||
},
|
||||
{
|
||||
id: 'review-anchor-drops-issue-body',
|
||||
guard: 'node --test test/review-doc-guard.test.mjs',
|
||||
|
||||
@@ -135,6 +135,27 @@ test('#541 AC: full red followed by light green still blocks release; a later fu
|
||||
assert.equal(selectCiProofVerdict([newerFull, light, red]).status, 'green');
|
||||
});
|
||||
|
||||
test('#601 AC3: release policy accepts a full proof without requested mutants; light stays stale; review/merge still demand them', () => {
|
||||
// Кандидат беты (`Release:` на dev, event=push) с #601 несёт тяжёлые гейты без
|
||||
// мутантов — proof без запрошенных mutant-jobs для релиза зелёный.
|
||||
const beta = proofFixture({ id: 30, mutants: false });
|
||||
beta.run.event = 'push';
|
||||
beta.proof.run.event = 'push';
|
||||
assert.equal(CI_PROOF_POLICIES.release.mutants, false);
|
||||
assert.equal(evaluateCiProof({ ...beta, policy: CI_PROOF_POLICIES.release }).status, 'green');
|
||||
assert.ok(!beta.proof.requiredChecks.includes('mutants'), 'мутанты не в списке обязательных — их никто не запрашивал');
|
||||
// Лёгкий proof релиз по-прежнему не принимает — тяжёлые гейты обязаны быть запрошены.
|
||||
const light = proofFixture({ id: 31, full: false, mutants: false, backend: false, integration: false });
|
||||
assert.equal(evaluateCiProof({ ...light, policy: CI_PROOF_POLICIES.release }).status, 'stale');
|
||||
// Ревью и слияние без запрошенных мутантов — stale (#541 не ослаблен).
|
||||
for (const policy of [CI_PROOF_POLICIES.review, CI_PROOF_POLICIES.merge]) {
|
||||
assert.equal(policy.mutants, true, policy.name);
|
||||
const verdict = evaluateCiProof({ ...proofFixture({ id: 32, full: false, mutants: false, backend: false, integration: false }), policy });
|
||||
assert.equal(verdict.status, 'stale', policy.name);
|
||||
assert.match(verdict.note, /no requested mutant jobs/);
|
||||
}
|
||||
});
|
||||
|
||||
test('#541 AC: green dispatch without six executed mutant jobs proves neither review nor merge', () => {
|
||||
const fixture = proofFixture({ full: false, backend: false, integration: false });
|
||||
fixture.jobs = fixture.jobs.filter((job) => !job.name.startsWith('Мутанты по диффу'));
|
||||
|
||||
@@ -181,10 +181,13 @@ test('CLI --heavy читает событие и сообщение из окр
|
||||
encoding: 'utf8', env: { ...process.env, ...env },
|
||||
}).trim();
|
||||
assert.equal(run({ EVENT_NAME: 'push', HEAD_MESSAGE: 'fix: x\n\nIssue: #1\nUser-Visible: no' }), 'heavy=false\nmutants_requested=false');
|
||||
assert.equal(run({ EVENT_NAME: 'push', HEAD_MESSAGE: 'x\n\nRelease: v1.2.3' }), 'heavy=true\nmutants_requested=true');
|
||||
assert.equal(run({ EVENT_NAME: 'workflow_dispatch', FULL_INPUT: 'true', HEAD_MESSAGE: '' }), 'heavy=true\nmutants_requested=true');
|
||||
// #601: кандидат беты и полный набор берут тяжёлые job, но не мутантов
|
||||
assert.equal(run({ EVENT_NAME: 'push', HEAD_MESSAGE: 'x\n\nRelease: v1.2.3' }), 'heavy=true\nmutants_requested=false');
|
||||
assert.equal(run({ EVENT_NAME: 'workflow_dispatch', FULL_INPUT: 'true', HEAD_MESSAGE: '' }), 'heavy=true\nmutants_requested=false');
|
||||
// #510: мутанты по кнопке без полного набора — вызов конвейера ревью и слияния
|
||||
assert.equal(run({ EVENT_NAME: 'workflow_dispatch', FULL_INPUT: 'false', MUTANTS_INPUT: 'true', HEAD_MESSAGE: '' }), 'heavy=false\nmutants_requested=true');
|
||||
// обе кнопки разом — полный набор И мутанты; так делает только человек, конвейер шлёт full=false
|
||||
assert.equal(run({ EVENT_NAME: 'workflow_dispatch', FULL_INPUT: 'true', MUTANTS_INPUT: 'true', HEAD_MESSAGE: '' }), 'heavy=true\nmutants_requested=true');
|
||||
});
|
||||
|
||||
test('#586: режим гейта скриншотов приходит одним значением и на кандидате строгий', () => {
|
||||
@@ -215,13 +218,21 @@ test('#586: preflight спрашивает режим одним значени
|
||||
assert.match(workflow, /check-docs\.mjs --external --screenshots=\$mode/);
|
||||
});
|
||||
|
||||
test('#510 AC1: мутанты по диффу запрашиваются кандидатом, PR, ночью и по кнопке — не обычным пушем', () => {
|
||||
test('#510 AC1 / #601 AC1: мутанты по диффу запрашиваются только кнопкой mutants=true и PR — не пушем, не кандидатом беты, не full', () => {
|
||||
const t = (env) => mutantsRequested(env);
|
||||
assert.equal(t({ eventName: 'push', headMessage: 'fix: x\n\nIssue: #1\nUser-Visible: no' }), false, 'обычный push');
|
||||
assert.equal(t({ eventName: 'push', headMessage: 'x\n\nRelease: v1.2.3' }), true, 'кандидат беты');
|
||||
assert.equal(t({ eventName: 'pull_request' }), true);
|
||||
assert.equal(t({ eventName: 'schedule' }), true);
|
||||
assert.equal(t({ eventName: 'workflow_dispatch', fullInput: 'true' }), true);
|
||||
assert.equal(t({ eventName: 'workflow_dispatch', fullInput: 'false', mutantsInput: 'true' }), true);
|
||||
assert.equal(t({ eventName: 'pull_request' }), true, 'на PR Validate — единственный сигнал');
|
||||
assert.equal(t({ eventName: 'workflow_dispatch', fullInput: 'false', mutantsInput: 'true' }), true, 'конвейер ревью и слияния');
|
||||
assert.equal(t({ eventName: 'workflow_dispatch', fullInput: 'false', mutantsInput: 'false' }), false, 'кнопка без запроса');
|
||||
// #601: мутанты проверяют тесты, а не продукт (#513); к бете задача прогнана
|
||||
// ими дважды, а ночь покрыта полным реестром — эти входы их не включают.
|
||||
assert.equal(t({ eventName: 'push', headMessage: 'x\n\nRelease: v1.2.3' }), false, 'кандидат беты — тяжёлые гейты без мутантов');
|
||||
assert.equal(t({ eventName: 'push', headMessage: 'x\n\nRelease: v1.76.0' }), false, 'кандидат стабильного — тоже');
|
||||
assert.equal(t({ eventName: 'workflow_dispatch', fullInput: 'true' }), false, 'полный набор по кнопке — без мутантов');
|
||||
assert.equal(t({ eventName: 'workflow_dispatch', fullInput: 'true', mutantsInput: 'false' }), false);
|
||||
assert.equal(t({ eventName: 'schedule' }), false, 'расписание Validate — не место мутантов, реестр идёт в mutation-gate.yml');
|
||||
assert.equal(t({ eventName: 'workflow_dispatch', fullInput: 'true', mutantsInput: 'true' }), true, 'явный запрос действует и рядом с full');
|
||||
// #601 AC2: тяжёлые гейты на тех же входах не изменились
|
||||
assert.equal(heavyGatesRequested({ eventName: 'push', headMessage: 'x\n\nRelease: v1.2.3' }), true);
|
||||
assert.equal(heavyGatesRequested({ eventName: 'workflow_dispatch', fullInput: 'true' }), true);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user