ТестированиеПроцессы качестваИнженер по обеспечению качества

В репозитории два pull request по отдельности проходят CI, но после объединения иногда ломают основную ветк...

В репозитории два pull request по отдельности проходят CI, но после объединения иногда ломают основную ветку. Какой контроль CI предотвращает проверку устаревшего результата слияния?

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  quality:
    script: run-tests
    required: true

merge:
  strategy: direct
Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Нужно проверять не только исходную ветку pull request, а результат её слияния с актуальной основной веткой, и разрешать слияние только после успешной проверки этого результата. Для этого применяют merge queue, merge train или эквивалентный механизм последовательного слияния с обязательным CI на объединённом состоянии.

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

Исторический контекст

В ранних моделях CI было достаточно запускать сборку для каждого коммита или pull request отдельно. Такой подход хорошо обнаруживал ошибки внутри одного изменения, но предполагал, что независимые успешно проверенные изменения безопасно объединяются.

При параллельной разработке это предположение оказалось неверным: два изменения могут конфликтовать логически, даже если текстовых конфликтов Git нет. Поэтому появились merge queue и merge train — механизмы, которые проверяют предполагаемый результат слияния до фактического обновления основной ветки.

Постановка проблемы

Пусть pull request A и pull request B построены от одного состояния основной ветки. Оба проходят тесты по отдельности. После слияния A API меняется так, что тесты B начинают падать, хотя в исходном состоянии B этого изменения ещё нет.

Если политика разрешает прямое слияние зелёных pull request, основная ветка может временно стать неисправной. Это приводит к поломке последующих сборок, задержке релиза и спору о том, какое изменение стало причиной сбоя.

Обычная проверка наличия успешного CI недостаточна, если проверенный commit уже не является актуальным предком будущего состояния основной ветки. Важна свежесть проверки относительно точки слияния.

Подробное решение

Механизм работает так: система строит временный результат, объединяя pull request с актуальной основной веткой. CI запускается на этом объединённом состоянии. Слияние разрешается только при успешных обязательных проверках и отсутствии более новых изменений, которые делают результат проверки устаревшим.

В очереди слияния несколько pull request могут проверяться последовательно или в оптимизированных комбинациях. Если первый элемент очереди меняет базу для следующего, второй либо перепроверяется, либо заранее тестируется на соответствующем виртуальном результате слияния.

Концептуальная политика выглядит так:

merge_policy: require_merge_result_checks: true require_up_to_date_base: true invalidate_stale_checks: true merge_queue: enabled checks: - unit - integration - security

require_merge_result_checks означает, что проверяется объединённое состояние, а не только ветка автора. invalidate_stale_checks не позволяет использовать старый зелёный результат после изменения основной ветки или состава очереди.

Важное ограничение — стоимость и время CI могут вырасти, особенно при большом числе pull request. Компромисс достигается кэшированием, параллельным запуском независимых проверок, выбором быстрого набора обязательных тестов и отдельным полным регрессом после слияния.

Этот механизм не заменяет защиту от текстовых конфликтов, code review, тесты совместимости и проверку качества самого пайплайна. Он решает более узкую проблему: совместимость изменений в том состоянии, которое реально будет опубликовано.

Ситуация из практики

Команда заметила, что основная ветка несколько раз в неделю становилась красной. Каждый pull request имел зелёный статус, поэтому сначала рассматривались два варианта: разрешить разработчикам вручную перепроверять ветку перед слиянием или запускать полный регресс после каждого объединения.

Ручная перепроверка почти не требовала инфраструктурных изменений, но зависела от дисциплины и оставляла окно для гонки: другой pull request мог попасть в основную ветку сразу после проверки. Полный регресс после каждого слияния надёжнее, но увеличивал очередь и обнаруживал проблему уже после изменения основной ветки.

Команда выбрала очередь слияния с обязательными проверками результата слияния. Быстрые проверки запускались для каждого кандидата, а тяжёлые проверки выполнялись параллельно и дополнительно на итоговом состоянии. Это снизило число поломок основной ветки, сохранив приемлемое время обратной связи.

Что кандидаты часто упускают

  1. Достаточно ли перепроверить pull request перед слиянием, если основная ветка быстро меняется?

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

  1. Решает ли merge queue проблему логических конфликтов между изменениями?

Он обнаруживает такие конфликты только в той мере, в какой их выявляют обязательные проверки. Если два изменения совместимы синтаксически, но нарушают бизнес-правило, без соответствующего теста очередь не сможет это определить. Поэтому merge queue обеспечивает актуальность проверяемого состояния, но не заменяет адекватную стратегию тестирования.

  1. Почему нельзя просто разрешить слияние двух зелёных pull request подряд?

Потому что зелёный статус каждого pull request относится к разному состоянию системы — обычно к его собственной ветке и прежней базе. Взаимодействие изменений появляется только после объединения. Проверять нужно не сумму независимых результатов, а фактический совместный результат, который будет записан в основную ветку.