GitLab / CIЗаметка для разработчиковFree или платный?

MR · MERGE · RELEASE

Тесты в MR и master:
когда запускать дважды

Повтор нужен, если он проверяет другой код, сборку или окружение. Сам факт merge ещё не повод запускать всё заново.

Здесь master — основная ветка. В CI её имя берётся из CI_DEFAULT_BRANCH.

ПОВТОР НУЖЕН

В master попал непроверенный результат слияния, либо появились условия, которых не было в MR.

ПОВТОР МОЖНО УБРАТЬ

Конечный код уже проверен перед merge, а тесты и все существенные входные данные совпадают.

01

Проверяем разные вещи

ДО MERGE

Можно ли принять код?

Линтер, модульные и интеграционные тесты. Ошибка должна блокировать слияние.

В MASTER, ДО РЕЛИЗА

Можно ли выпускать сборку?

Проверки пакета, образа и доступных только здесь интеграций. Ошибка блокирует выкладку.

ПОСЛЕ ВЫКЛАДКИ

Сервис работает?

Проверка запуска, доступности и ключевого сценария в реальном окружении.

Тесты после merge не защищают master от этого merge. Они находят проблему уже в основной ветке. Чтобы остановить релиз, выкладка должна зависеть от их успеха.
СитуацияТесты после mergeПочему
Строгий Fast-forward, проверен конечный коммитМожно убрать повторВ master тот же код. Условия — ниже.
Ветки разошлись; проверялись только исходные ветки MRНужны для результатаКомбинация изменений ещё не проверена. Лучше проверить её до merge.
Автоматический rebase при merge, результат не проверен очередьюНужны для результатаGitLab не перезапускает CI на результате такого rebase. [1]
Squash или новый merge-коммитЗависит от сборкиНовый SHA сам по себе не означает новый код. Важны содержимое и использование Git-метаданных.
Перед merge проверен актуальный общий результатМожно убрать повторЕсли содержимое, сборка и окружение эквивалентны проверенным.
Другой образ, зависимости, переменные или внешние сервисыНужны целевые проверкиТестируется новое условие. Повторять весь набор автоматически незачем.
02

Fast-forward меняет указатель ветки

Если текущий master — предок проверенного коммита MR, Git может передвинуть master на него. Без squash и rebase при слиянии новый коммит не появляется. [1]

Fast-forward без squashFree
Один проверенный коммит до и после Fast-forwardДо слияния master указывает на A, MR на Б′, содержащий A. После слияния обе ветки указывают на тот же Б′. Тесты прошли на Б′ до слияния.До mergeПослеM0AБ′ · CI прошёлM0Aтот же Б′masterMR Бmaster + MR Б
Изменения A уже включены в Б′ и проверены вместе. Повтор не создаёт новой проверки их совместимости.

Если master ушёл вперёд и ветки разошлись: обновить ветку MR, проверить новый результат, затем сливать. Сам Fast-forward не знает о тестах — обязательность CI задаётся отдельно.

Как два зелёных MR могут сломаться вместе?

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

Версияworkersbatch_sizeПроизведение
Исходная2100200 ✓
Только MR A4100400 ✓
Только MR Б2200400 ✓
A + Б4200800 — ошибка
test_queue.py
from workers import WORKERS
from batches import BATCH_SIZE

def test_queue_fits():
    assert WORKERS * BATCH_SIZE <= 500

Обновление MR выявит проблему, если такой тест есть. Отсутствие конфликтов Git не доказывает совместимость поведения.

Что меняют squash и semi-linear merge?

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

Сравнение файлов двух коммитов
# Подставь SHA проверенного и влитого коммитов.
git diff --exit-code "$TESTED_SHA" "$MERGED_SHA" --

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

03

Когда достаточно GitLab Free

Для схемы «тесты кода только в MR» нужны все условия ниже. Это рабочая дисциплина для обычного процесса слияния, а не гарантия отсутствия ошибок в программе.

  1. В master попадает проверенный коммит

    Settings → Merge requests: Fast-forward merge; squash — Do not allow; Enable automatic rebase prior to merge — выключено. После обновления ветки дождаться нового успешного CI. [1]

  2. Успешные тесты обязательны

    Включить Pipelines must succeed; не считать skipped pipeline успешным. Обязательные тесты входят в MR pipeline, не исключены через rules и не имеют allow_failure: true. [4] [7]

  3. Нет прямого пути в master

    Защитить ветку: Allowed to push and merge — No one; force push выключен. Право сливать MR задать отдельно через Allowed to merge. Проверить пересекающиеся правила доступа. [5]

  4. Повтор действительно одинаковый

    Совпадают тесты, файлы, версии зависимостей, образ среды исполнения, параметры и значимое внешнее состояние. Фиксируй зависимости и образы по digest; одинаковый SHA не делает окружения одинаковыми.

Проверки с защищёнными переменными и отдельной релизной сборкой могут оставаться после merge. Доступ к таким переменным в MR зависит от защиты веток и настроек проекта. [6]

04

Три схемы CI

Примеры для Python. Выбери схему под свой процесс. Проверки релиза здесь обозначены отдельным скриптом проекта.

AСтрогий Fast-forward: тесты кода только в MRFree

При выполнении условий выше в master остаются проверки релиза.

.gitlab-ci.yml · полный пример
workflow:
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
    - when: never

stages: [test, release_check]

default:
  image: python:3.12-slim

tests:
  stage: test
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
  script:
    - python -m pip install --require-hashes -r requirements-ci.txt
    - python -m pytest -q
  allow_failure: false

release_check:
  stage: release_check
  rules:
    - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
  script:
    - ./ci/verify-release.sh
  allow_failure: false

requirements-ci.txt должен содержать pytest, зависимости проекта и транзитивные зависимости с версиями и хешами. Тег python:3.12-slim дан для читаемости: в рабочем CI закрепи одобренный образ по digest.

verify-release.sh — твой скрипт сборки и проверки пакета/образа. Его нужно реализовать. Выкладывай именно проверенный артефакт; при ошибке скрипт должен завершаться ненулевым кодом. Если релиза нет, убери это задание и правило master в workflow.

BРезультат merge заранее не проверен: тесты в обоихFree

Оставь workflow, этапы и release_check из A. Замени задание tests этим:

.gitlab-ci.yml · замена tests
tests:
  stage: test
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
  script:
    - python -m pip install --require-hashes -r requirements-ci.txt
    - python -m pytest -q
  allow_failure: false

Тесты выполняются в MR и при push в master. В master этап release_check начнётся только после успешных тестов.

Это страховка релиза. Master до исправления может оставаться сломанным. Следующий шаг — проверять общий результат до слияния.
CМного параллельных MR: проверка в merge train

Сохрани схему A: её правило merge_request_event допускает и merged results, и merge train pipelines. В Settings → Merge requests включи Enable merged results pipelines и Enable merge trains. YAML сам очередь не включает. [3]

Если дорогая проверка нужна только общему результату, добавь отдельное задание:

.gitlab-ci.yml · дополнительное задание
combined_tests:
  stage: test
  rules:
    - if: '$CI_MERGE_REQUEST_EVENT_TYPE == "merged_result" || $CI_MERGE_REQUEST_EVENT_TYPE == "merge_train"'
  script:
    - python -m pip install --require-hashes -r requirements-ci.txt
    - python -m pytest -q integration/
  allow_failure: false

integration/ — отдельный набор тестов проекта. В задании tests из A замени команду на python -m pytest -q --ignore=integration/, чтобы не запускать этот набор дважды. Значения CI_MERGE_REQUEST_EVENT_TYPE описаны в документации. [8]

Оставь обязательность CI и защиту master. Проводи слияния через очередь, без обхода. Убирать повтор в master можно при эквивалентности проверенного кода, сборки и условий; проверка релиза сохраняет свою задачу.

Во всех примерах workflow разрешает только MR и push в основную ветку. Теги, расписания, API и ручной запуск отдельного branch pipeline не включены. Нужные события добавляй явно. Это также убирает лишний branch pipeline при push в ветку MR. [9]

05

За что здесь нужен платный GitLab

ВозможностьТарифЧто проверяется
Обычный MR pipelineFreeИсходная ветка MR. Актуальный master автоматически не подмешивается. [6]
Fast-forward + обязательный CI + защита ветки по ролямFreeПозволяет требовать проверку обновлённой ветки перед слиянием. [1] [4] [5]
Merged results pipelineВременный коммит: MR + целевая ветка. Другие ожидающие MR не учитываются. [2]
Merge trainsMR + целевая ветка + предыдущие MR в очереди. [3]
Что проверяет очередь
MR A
master+A
MR Б
master+A+Б
MR В
master+A+Б+В
Проверки могут идти параллельно, слияния — по порядку. Если Б выпадает, результат для В пересчитывается без Б. [3]

Очередь нужна для совместимости параллельных MR. Merged results pipeline сам по себе не закрывает гонку двух слияний. При текстовом конфликте GitLab может запустить обычный MR pipeline вместо merged results — проверяй тип. [2] [3]

Free означает доступность функции, а не бесплатные вычисления: runners и лимиты CI считаются отдельно. Для Self-Managed важны и лицензия, и установленная версия. Auto-merge лишь ждёт обязательные проверки; сам по себе он не создаёт merge train. [4]

ЧТО УДАЛЯТЬ ИЗ CI

Для каждого повторного задания спроси: «Какое новое условие оно проверяет?» Если никакое — убирай после проверки настроек. Если ответ «общий результат ещё не тестировали» — перенеси эту проверку до merge, когда процесс это позволяет.

06

Документация

Факты о функциях и тарифах — из GitLab Docs. Разделение проверок и примеры CI — инженерные рекомендации для описанных условий.

  1. Merge methodsFast-forward, squash, automatic rebase
  2. Merged results pipelinesОбщий результат до merge · Premium / Ultimate
  3. Merge trainsОчередь с проверкой · Premium / Ultimate
  4. Auto-merge и Pipelines must succeedОбязательный CI и skipped pipelines
  5. Protected branchesПрава на push и merge
  6. Merge request pipelinesИсходная ветка и защищённые ресурсы
  7. CI/CD YAML: allow_failureКакие ошибки блокируют pipeline
  8. Predefined variablesdetached / merged_result / merge_train
  9. Workflow rulesКакие события создают pipeline