GitLab CI запускает два pipeline на один push: разбираем workflow и rules

В merge request появился новый commit, а рядом с ним сразу два pipeline. В обоих выполняются тесты, расходуются минуты Runner, иногда дважды запускается подготовка окружения. Причина часто находится в .gitlab-ci.yml: конфигурация разрешает pipeline для события push и одновременно для обновления merge request.

Это два разных источника запуска вокруг одного изменения. Чтобы исправить поведение, сначала нужно выбрать политику проекта. В примере ниже до открытия merge request работают branch pipelines. После открытия остаётся merge request pipeline. Теги и расписания считаются отдельными сценариями и допускаются явно.

Создание pipeline и добавление job — два решения

workflow:rules определяет, появится ли pipeline вообще. rules внутри job решает, попадёт ли эта конкретная задача в уже разрешённый pipeline. Если вы исправите только тестовую job, второй pipeline может продолжить существовать с другой задачей из include. Поэтому сначала полезно сравнить Source обоих запусков на их страницах, затем посмотреть объединённую конфигурацию проекта.

workflow:
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"'
      when: never
    - if: '$CI_PIPELINE_SOURCE == "push"'
    - if: '$CI_PIPELINE_SOURCE == "schedule"'

test:
  image: alpine:3.21
  script:
    - printf '%s\n' "$CI_PIPELINE_SOURCE" "$CI_COMMIT_SHA"
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - if: '$CI_PIPELINE_SOURCE == "push"'
    - if: '$CI_PIPELINE_SOURCE == "schedule"'

Это полный минимальный файл. Правила проверяются сверху вниз до первого совпадения. Для merge_request_event сработает первая строка. Для push в ветку с открытым MR — запрет во второй строке. Для push без MR и для Git-тега — разрешение третьей строкой. Расписание допускается четвёртой. Запуск из Web UI, API и дочернего pipeline этот пример не разрешает: при необходимости добавьте для них отдельную осмысленную ветку правил.

Условие source == push внутри запрета принципиально. Переменная CI_COMMIT_BRANCH встречается и в других сценариях. Если запрещать любую ветку с открытым MR, можно заодно отключить нужный scheduled pipeline. Мы запрещаем именно лишний push pipeline, оставляя возможность отдельно решить судьбу расписаний.

Пройдём четыре события вручную

Сделайте push в новую ветку без MR. Ожидается один pipeline с источником push. Откройте MR и отправьте следующий commit: ожидается один merge_request_event, а второй push pipeline не создаётся. После закрытия MR новый push снова должен проходить как branch pipeline. При запуске расписания источник schedule сохраняется независимо от открытого MR.

Новый pipeline, возникший при самом открытии MR, не означает дубль на один и тот же push. Сопоставляйте источник, время и commit SHA. Иначе старый branch pipeline, который уже успел выполниться до открытия MR, легко принять за повторный запуск по новым правилам.

Откуда возвращается второй набор jobs

В большой конфигурации проверьте include и правила всех задач. Завершающее when: always в job разрешает работу в источниках, которые не были исключены предыдущими строками. Если workflow допускает одновременно branch и MR pipelines, такая задача может присутствовать в обоих. Правка одного шаблона тестов не меняет политику создания остальных pipelines.

Не смешивайте без необходимости задачи с rules и старые only/except: их значения по умолчанию и модель отбора различаются, что затрудняет чтение общей схемы. Сначала выпишите реальные источники для сборки, тестов и релиза, затем выразите их одинаковым способом. Детали оценки условий доступны в справке job rules.

Не переносите rules релиза в тестовую job механически. В MR pipeline переменная CI_COMMIT_BRANCH не представляет собой обычный контекст branch pipeline. Для тестирования MR используйте источник merge_request_event; для публикации из защищённой основной ветки задавайте отдельное условие. Тогда отсутствие релиза в MR становится ожидаемым поведением, а не поводом добавлять широкое when: always.

Как проверить правила перед объединением изменений

Откройте Pipeline editor и CI Lint для проверки итогового YAML вместе с include. Успешная проверка синтаксиса ещё не подтверждает все варианты событий: после неё воспроизведите выбранные сценарии в тестовом проекте или ветке. На время оставьте диагностическую печать источника и SHA в безопасной job, чтобы журнал объяснял, почему она появилась.

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

Ещё по теме

Продолжить изучение

Хотите собрать проверяемый путь от коммита до релиза? Jobs, runners, артефакты и деплой — темы программы «GitLab CI/CD: автоматизация сборки и деплоя».

Продолжить на практике

  • GitLab CI/CD: автоматизация сборки и деплоя ↗

    От первого .gitlab-ci.yml до pipeline, который тестирует код, собирает образ и выкладывает приложение. Runners, artifacts, cache, registry, окружения и безопасный откат — на одном практическом проекте.