mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-07 06:59:46 +00:00
ci: classify against the last proven-green ancestor
Диапазон классификации брался от `github.event.before` — головы предыдущего пуша. Это допущение «до этого уже проверено», и оно неверно ровно тогда, когда прогон предыдущего пуша не завершился. А не завершается он штатно: concurrency отменяет его следующим пушем. На #86 (r5) это дало ложный зелёный: push04da7eb1тронул dist/** и frontend/**, его прогон отменили через три минуты; следующий pushfa146fb1тронул только docs/images/**, классификация сравнила эти два коммита и выставила frontend=false. Job «Фронтенд», а за ней golden, smoke и backend оказались skipped — прогон при этом success. Маркеры переиспользования эти гейты тоже не подтверждали: `Cache not found` по всем четырём. Теперь база — самый новый предок HEAD, для которого Validate ДЕЙСТВИТЕЛЬНО завершился успешно; если такого нет, диапазон расширяется до merge-base с dev, то есть до всего вклада ветки. Работает по индукции: цепочка узких диффов покрывает всё изменённое с последней настоящей проверки, а одно незавершённое звено теперь расширяет диапазон, а не сужает. Недоступность API не роняет job: пустой ответ опускает базу до merge-base, то есть в сторону большего объёма проверок. Защита от force-push (#347) сохранена: механизм, из-за которого merge-base врал на переписанной истории, до конца не разобран, и снимать защиту, не объяснив её, — способ получить #347 второй раз. Issue: #387 User-Visible: no
This commit is contained in:
@@ -141,3 +141,29 @@ test('упавшая golden называет первопричину, а не
|
||||
// свидетеля и смысл сообщения перевернётся.
|
||||
assert.match(golden, /if: failure\(\) && steps\.fail_note\.outputs\.first == 'true'/);
|
||||
});
|
||||
|
||||
test('классификация опирается на завершённый прогон, а не на предыдущий пуш (#387)', () => {
|
||||
const workflow = read('validate.yml');
|
||||
const changes = workflow.slice(
|
||||
workflow.indexOf('\n changes:\n'), workflow.indexOf('\n reuse:\n'),
|
||||
);
|
||||
// `github.event.before` — источник ложного «уже проверено»: прогон
|
||||
// предыдущего пуша штатно отменяется следующим. Классификация читать его
|
||||
// больше не имеет права. В процессном гейте он законен: там проверяются
|
||||
// трейлеры именно отправленных коммитов, а не объём проверок.
|
||||
// `before` остаётся ровно одной проверкой — жив он или переписан (#347).
|
||||
// Базой диапазона он больше не служит: его прогон штатно отменяется.
|
||||
assert.equal(/base="\$BEFORE_SHA"/.test(changes), false,
|
||||
'диапазон не имеет права опираться на голову предыдущего пуша (#387)');
|
||||
assert.match(changes, /git cat-file -e "\$BEFORE_SHA"/,
|
||||
'защита от force-push остаётся на месте (#347)');
|
||||
assert.match(changes, /node scripts\/classify-base\.mjs --head=/);
|
||||
assert.match(changes, /actions: read/, 'чтение прогонов требует прав');
|
||||
// База считается только для push вне dev: у PR диапазон задан событием.
|
||||
assert.match(changes, /if: github\.event_name != 'pull_request' && github\.ref != 'refs\/heads\/dev'/);
|
||||
// Пустая база означает «доказательства нет» и обязана вести к полному
|
||||
// прогону, а не к пустому диффу, который выглядел бы как «ничего не менялось».
|
||||
const empty = changes.slice(changes.indexOf('if [ -z "$base" ]'));
|
||||
assert.match(empty, /frontend=true\\nbackend=true\\nintegration=true/,
|
||||
'без базы классификация обязана раскрываться в полный прогон');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user