Що запускати на PR, на merge і вночі
Головна ідея пайплайна для автотестів: чим дорожчий набір, тим рідше й пізніше його запускають. Один «повний регрес на кожен коміт» — найкоротший шлях до 40-хвилинного очікування та розробників, які перестають дивитися на CI.
- Pull request (мета — фідбек до ~10 хвилин): lint і typecheck, unit, API/контрактні тести, збірка, smoke-набір e2e на критичних шляхах (логін, головна дія продукту). Усе це — required checks: без зеленого статусу merge заборонено;
- Merge у main: ширший набір (повний API, e2e по основних модулях), деплой на staging і smoke після деплою. Падіння тут означає, що PR-набір щось пропустив, і це привід його доповнити;
- Nightly / за розкладом: повний регрес, усі браузери, довгі сценарії, візуальні тести, performance-перевірки, прогін з вимкненим карантином. Результат — звіт і сповіщення команді, а не блок merge;
- Ручний запуск (
workflow_dispatch): повторити довгий набір на вимогу, наприклад на певній гілці чи оточенні.
Порядок етапів у межах запуску — fail-fast: дешеві й швидкі перевірки (lint, unit) ідуть першими, повільні e2e — після них. Незалежні етапи виконуються паралельно, а не по черзі.
Quality gate — це набір умов, без яких код не йде далі: усі required checks зелені, немає нових критичних помилок, покриття нового коду не нижче порогу, flaky-тести не в блокуючому наборі. Чим менше gate «на слово», тим краще: умова має перевірятися автоматично.
Flaky у CI: ретраї, карантин, повторний запуск
Flaky-тест — той, що на тому самому коді то проходить, то падає. У CI він коштує двічі: червоний PR без причини і втрата довіри до набору. Тому потрібна політика, а не «перезапустимо ще раз».
Ретраї. Дозволяють 1–2 повторні спроби в CI (у Playwright retries: process.env.CI ? 2 : 0), локально — 0, щоб розробник бачив нестабільність одразу. Ретрай — не лікування, а сенсор: тест, що пройшов лише з повтору, отримує статус flaky, потрапляє в метрики й веде до тікета. На ретраї зручно вмикати trace (on-first-retry).
Карантин. Тест, який нестабільний і блокує інших, виносять із required-набору (тег, окремий проєкт, skip з посиланням на тікет), але не видаляють: у нього є власник, причина і дедлайн. Карантинний набір продовжує бігати вночі, щоб бачити, чи він «одужав».
Повторний запуск. Якщо впала інфраструктура (раннер, мережа, rate limit), у GitHub Actions можна натиснути Re-run failed jobs: перезапуститься лише впала job (наприклад, одна шарда), а не весь пайплайн. Це економить час, але не відміняє аналізу причини.
Чого не робити: ставити глобально retries: 5 (ховає реальні баги з гонками), автоматично перезапускати червоні запуски без запису в метрики, лишати skip без власника.
Як утримати фідбек PR у межах 10 хвилин
Після ~10 хвилин розробник перемикається на іншу задачу, а повернення до червоного PR коштує контексту. Коли пайплайн повільний, спершу міряють, а не оптимізують навмання: розбивка часу по джобах і крокам (checkout, install, build, тести), p50/p90 тривалості, черга на раннер.
Важелі, від дешевих до дорогих:
- Паралельні джоби. lint, unit, build, API і e2e не мають чекати одне одного, якщо між ними немає залежності;
- Кеш. npm-кеш, браузери Playwright, Docker-шари, результати збірки;
- Шардування e2e.
--shard=i/N у matrix; тривалість ≈ найповільніша шарда, тому важлива рівномірність;
- Селективний запуск. Path filters і аналіз зачеплених пакетів у монорепо: PR, що змінив документацію чи один сервіс, не запускає все. Повний набір лишається на main і вночі як страховка;
- Піраміда. Перенести перевірки з e2e в API/компонентні тести; e2e на PR — лише smoke;
- Стабільність. Ретраї та перезапуски теж «їдять» час: низький flaky rate прискорює пайплайн;
- Сильніші раннери. Остання опція: зазвичай вузьке місце не в CPU.
Ризик селективного запуску — пропустити залежність. Тому потрібен fallback: періодичний повний прогін і правило «зміна спільної бібліотеки запускає все».