Жизненный цикл пайплайна и триггеры

Разбираем, какие события запускают пайплайн и как он живёт от старта до итогового статуса.

Триггер — событие в репозитории (или расписание/ручной запуск), которое заставляет GitLab создать новый пайплайн.

Какие бывают триггеры

Пайплайн не запускается сам по себе — его что-то порождает. Основные источники:

ТриггерКогда срабатывает
pushпри отправке коммитов в любую ветку
merge requestпри открытии/обновлении MR (MR-пайплайн)
scheduleпо расписанию (cron), напр. ночная сборка
tagпри создании git-тега (часто релиз)
manual / APIвручную из UI или через API/триггер-токен

Тип триггера доступен внутри джоб через предопределённые переменные, например CI_PIPELINE_SOURCE принимает значения push, merge_request_event, schedule, web и т.д. На них удобно строить правила запуска (раздел про rules).

Почему так важно знать источник пайплайна? Потому что один и тот же .gitlab-ci.yml должен вести себя по-разному в зависимости от того, что его запустило. Ночная сборка по расписанию может прогонять долгие интеграционные тесты, которые незачем гонять на каждый промежуточный коммит. Пайплайн merge request — показывать ревьюеру результат проверки, но не деплоить. Пайплайн по тегу — собирать релизный артефакт и публиковать его. Переменная CI_PIPELINE_SOURCE и есть тот «датчик контекста», по которому джоба понимает, в каком из этих сценариев она оказалась, и решает — включаться ей или промолчать.

Эта развилка событий объясняет и неочевидное поведение: иногда на один git push в ветку с открытым merge request GitLab создаёт два пайплайна — один с источником push, другой с merge_request_event. Формально это разные события на один коммит, и без настройки workflow вы получаете дублирующиеся прогоны, зря жгущие минуты раннеров. Это не баг, а прямое следствие того, что триггеров несколько и каждый самостоятелен; укрощают дубли как раз правилами workflow, о которых отдельный урок.

Жизнь пайплайна

После триггера GitLab создаёт пайплайн и проходит фазы:

событие → создан pipeline → стадия 1 (джобы параллельно)
                                  │ все зелёные?
                                  ▼
                            стадия 2 → ... → стадия N
                                  │
                                  ▼
              статус: passed / failed / canceled

Каждая стадия открывается, только когда предыдущая успешно завершена. Итоговый статус пайплайна — passed, если все обязательные джобы прошли, failed, если хоть одна упала, canceled, если его отменили.

Стоит уточнить слово «обязательные». Не всякая упавшая джоба роняет пайплайн: джоба с allow_failure: true может покраснеть, но не помешает итоговому passed — её провал лишь предупреждение, а не приговор. Так оформляют необязательные проверки вроде линтеров качества или экспериментальных тестов: видеть их результат полезно, но блокировать релиз из-за них не хочется. Поэтому зелёный пайплайн вовсе не означает «все джобы зелёные» — он означает «все обязательные джобы прошли».

Помимо трёх основных статусов есть и промежуточные, которые видно в интерфейсе: created (пайплайн только что заведён), pending (джобы ждут свободного раннера), running (исполняются), а также skipped и manual для джоб, которые правила пропустили или оставили на ручной запуск. Понимание этих состояний экономит нервы: пайплайн в pending не «сломан» — он просто ждёт раннер; а наполовину серый пайплайн с кнопками «play» не завис, а честно ждёт, когда человек нажмёт ручную джобу. Ключевая идея в том, что пайплайн — это граф джоб с зависимостями по стадиям, а его статус — агрегат статусов этого графа, вычисляемый в реальном времени по мере того, как джобы меняют состояние.

Расписание (schedule)

Расписания настраиваются в CI/CD → Schedules: задаёте cron-выражение и ветку. По нему GitLab сам запустит пайплайн — удобно для ночных интеграционных тестов, регулярных бэкапов или обновления зависимостей. Внутри джоб такой пайплайн отличить можно по CI_PIPELINE_SOURCE == "schedule".

Ручной запуск и manual-джобы

Пайплайн можно запустить вручную из UI или по API. Кроме того, отдельные джобы можно сделать ручными (when: manual) — они появятся в пайплайне как кнопка «play». Так обычно оформляют деплой на production: пайплайн зелёный, но релиз происходит по нажатию человека.

Тег как релизный триггер

Отдельного внимания заслуживает триггер по тегу. В git-командах тег — это метка на конкретном коммите, обычно версия вроде v1.4.0. Создание тега порождает пайплайн с источником, который можно отличить через предопределённую переменную CI_COMMIT_TAG: если она непустая — значит, пайплайн запущен именно тегом. На этом строят релизные конвейеры: «обычные» коммиты только тестируются, а появление тега дополнительно собирает дистрибутив, публикует образ в registry и создаёт запись о релизе. Получается аккуратное разделение: повседневная разработка не трогает прод, а осознанное проставление тега запускает выпуск.

Линейные стадии и их предел

Модель «стадия за стадией» проста и предсказуема, но у неё есть цена: стадия не начнётся, пока не завершилась вся предыдущая, даже если конкретной джобе нужен результат лишь одной из её предшественниц. Из-за этого медленная джоба в ранней стадии задерживает всех. GitLab умеет обходить ограничение через явные зависимости (needs), превращая линейную цепочку стадий в направленный граф (DAG), где джоба стартует сразу, как только готовы именно её входные данные. Но базовый жизненный цикл, который вы видите по умолчанию, — это строго последовательные стадии, и понимать его нужно в первую очередь.

Наконец, у пайплайна есть и обратная сторона жизни — его можно прервать. Если в ту же ветку прилетает новый коммит, прежний прогон нередко уже не нужен: автоотмена устаревших пайплайнов экономит минуты раннеров и место в очереди, переводя «обогнанные» прогоны в canceled. Так конвейер не тратит ресурсы на проверку кода, который через минуту перезаписали, — ещё одно напоминание, что статус живёт и меняется вплоть до самого финала.

Как работает под капотом

Любой триггер в итоге создаёт запись пайплайна, привязанную к конкретному коммиту (CI_COMMIT_SHA) и источнику. GitLab вычисляет набор джоб, применяя правила (rules/workflow), и формирует граф. Если после применения правил не осталось ни одной джобы, пайплайн вообще не создаётся. Поэтому иногда push не приводит к пайплайну — сработали правила исключения.

Частые ошибки

  • Ожидать пайплайн на каждый push, не замечая, что workflow или rules отфильтровали все джобы.
  • Путать push-пайплайн и merge request-пайплайн: у них разные предопределённые переменные и иногда дублирование (об этом — урок про workflow).
  • Настроить schedule, но забыть, что он берёт .gitlab-ci.yml из указанной ветки на момент запуска.

Итоги

  • Пайплайн порождают push, MR, расписание, тег, ручной запуск/API.
  • Источник доступен через CI_PIPELINE_SOURCE и используется в правилах.
  • Стадии открываются последовательно; итоговый статус — агрегат джоб; пустой набор джоб = нет пайплайна.
Проверьте себя
1. Через какую переменную джоба узнаёт, чем был запущен пайплайн?
ACI_COMMIT_SHA
BCI_PIPELINE_SOURCE
CCI_RUNNER_TAGS
DCI_JOB_NAME
2. Почему иногда push не создаёт пайплайн вовсе?
AGitLab случайно пропускает события
BПравила (workflow/rules) отфильтровали все джобы, и создавать нечего
CРаннер выключен
DПайплайны создаются только по тегам