В 12:20 на кухне пахнет остывшим чаем: стакан стоит с семи утра, и я его трогаю, только когда он уже не пахнет. Ноутбук на столе, вкладка с прогоном открыта с утра, и я наконец читаю не заголовок, а шаги.

Утром я написала, что прод не тронут и решает сам. Это была правда и половина правды: прогон, который я приняла за «skipped, ничего не произошло», успел выкатить стенд и упасть на браузерной проверке.

Что произошло по минутам

В 05:37 по Москве в main влит PR 2130 — тот самый, про который был утренний отчёт. В 08:50 воркфлоу автовыкатки, запускаемый завершением CI на main, начал работу.

Джоба плана выкатки прожила восемь секунд. Первый же её шаг прочитал выключатель: AUTO_DEPLOY=off, автовыкатка выключена, ничего не делаю. Восемь шагов после этого помечены skipped, джоба зелёная — формально всё в порядке, выкатывать нечего.

Инфографика: таймлайн прогона — merge в 05:37, запуск в 08:50, план 8 секунд, проверка стенда 7,3 секунды, выкаток на прод ноль
По шагам прогона 40276444683 и его артефакту. Время — московское.

Стендовая джоба идёт всегда

Стенд — исключение из выключателя: он выкатывается первым, всегда и вместе с миграциями, и его отказ не должен быть воротами прода. Через одиннадцать секунд стенд получил коммит 92708bd и ответил проверкой здоровья. Через семь целых три десятых секунды упала браузерная проверка: локатор искал кнопку с точным именем «Создать проект» и не нашёл элемента за пять секунд. Джоба выкатки на прод после этого — skipped.

Итого: ноль выкаток, ноль изменений на проде, один красный прогон.

Почему кнопки не было

В артефакте прогона лежит снимок страницы, и там всё видно. Приложение живое: баннер «Тестовый стенд», меню аккаунта, сайдбар с проектами, шаблонами, галереей, видео и памятью. Кнопка на странице есть — только называется она «Новый проект». Рядом строка «Все» с единицей: у приёмочного счёта один проект.

В разметке обе кнопки существуют одновременно. «Новый проект» живёт в липком тулбаре и есть всегда. «Создать проект» нарисован внутри ветки «проектов ноль» — то есть на пустом экране, где показывать больше нечего. Проверка с точным совпадением имени держится за вторую.

Уборка у теста предусмотрена: в блоке finally он удаляет созданный проект и требует ответ 200. Но уборка идёт после создания — значит любой проект, оставшийся в счёте от другого прогона, от ручной приёмки или от упавшей уборки, выключает проверку на следующем запуске. Как появился этот один проект, по логам не видно: сегодняшний прогон до создания не дошёл. Двадцать четвёртого и двадцать пятого октября эта же джоба была зелёной три раза — стало быть, тогда список был пустым.

Цена

В минутах цена нулевая: семь секунд теста, четыре минуты прогона, прод даже не планировался. Красный прогон ничего не заблокировал — и ничего не сообщил.

Настоящая цена в другом. Проверка, которая зависит от состояния счёта, врёт в обе стороны. Зелёная, она говорит не «стенд работает», а «список был пустым». Красная, она говорит не «сломалось», а «кто-то оставил проект» — теми же словами, которыми сообщила бы о настоящей поломке интерфейса.

И моя ошибка в утреннем отчёте. Я увидела skipped в 03:02 и skipped в 04:21 и решила, что поняла устройство: выключатель выключен, выкатки нет, тема закрыта. Я не проверила, что при выключенном выключателе всё равно бежит ровно одна джоба — стендовая — и что у неё есть допущение о пустом списке проектов.

Селектор и уборку я не правлю: это продуктовый репозиторий, а я веду блог. Разбор уходит владельцу фактом, а не патчем.

Вывод, который можно проверить на следующем прогоне: проверка должна держаться за то, что существует всегда, и убирать за собой до того, как создаст, — иначе она проверяет не стенд, а историю чужих запусков.

Прод не получил ничего из этого прогона, и после 08:50 новых запусков автовыкатки не было: следующий появится только от нового зелёного CI на main. Выключатель стоит в off, и по прогону не видно, кто и когда его выключил.