История о том, как простая проблема в команде привела к интересному техническому решению
Проблема, которая достала
Работаю DevOps’ом aka восьмийух-fullstack в стартапе, где делаем платформу для психологов. У нас классический workflow: ветка master для продакшена, основнаяdev для разработки и отдельная layout-branch для верстальщика.
Вроде бы всё логично — верстка работает в своей песочнице, пушит изменения, они автоматически собираются и деплоятся в dev-окружение для моментального просмотра. А потом он должен создать merge request в dev, чтобы изменения попали в общую ветку разработки.
Но вот незадача — парень постоянно забывал создавать эти MR’ы. И я вынужден регулярно отслеживать прогресс вручную, чтобы вёрстка не оказывалась в изоляции. Уже третий-пятый раз, просил, напоминал — толку ноль. Человеческий фактор, что сказать.
Тогда я решил: хватит полагаться на память! Нужно автоматизировать этот процесс на уровне CI/CD.
Техническая реализация
Сборка npm билда происходит минимум 2 минуты, это достаточное время, чтобы не забыть нажать на кнопку — создать MR, а затем идея простая — не позволять запускаться deploy для layout-branch до тех пор, пока не создан открытый merge request в dev. GitLab API в помощь!
Вот что получилось в .gitlab-ci.yml:
deploy-layout-dev:
stage: deploy
environment: development
before_script:
- apk add --no-cache curl jq
script:
- set -e
- |
HTTP_STATUS=$(curl --globoff -s -o response.json -w "%{http_code}" \
--header "PRIVATE-TOKEN: ${GITLAB_API_TOKEN}" \
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/merge_requests?state=opened&source_branch=$CI_COMMIT_BRANCH&target_branch=dev")
MERGE_REQUEST_BODY=$(cat response.json)
if [ "$HTTP_STATUS" -ne 200 ]; then
echo "Ошибка: Не удалось получить данные от GitLab API. HTTP статус: $HTTP_STATUS"
echo "Ответ API: $MERGE_REQUEST_BODY"
exit 1
fi
if [ "$(echo "$MERGE_REQUEST_BODY" | jq 'length')" -eq 0 ]; then
echo "Ошибка: Merge Request из ветки '$CI_COMMIT_BRANCH' в 'dev' не найден."
echo "Пожалуйста, создайте Merge Request перед запуском deploy."
exit 1
fi
- apk add --no-cache bash rsync git
- bash ./.ci-cd/deploy-layout-dev.sh
only:
- layout-branch
interruptible: true
Логика такая:
- Делаем запрос к GitLab API для получения списка открытых MR из текущей ветки в
dev - Если API вернул ошибку — останавливаем пайплайн с диагностикой
- Если список пустой (нет MR) — тоже останавливаем с понятным сообщением
- Только если всё ОК — продолжаем с обычным деплоем
Подводные камни при реализации
Конечно же, с первого раза ничего не заработало. Столкнулся с несколькими проблемами:
Проблема #1: Ошибка 404 при корректном URL
Это было самое загадочное. URL формировался правильно, переменные CI подставлялись корректно, но GitLab упорно отвечал «404 Project Not Found».
Добавил отладочный вывод:
echo "CI_PROJECT_ID: $CI_PROJECT_ID"
echo "CI_API_V4_URL: $CI_API_V4_URL"
echo "Request URL: $REQUEST_URL"
Всё выглядело корректно, но 404 не исчезал. В итоге оказалось банально — переменная GITLAB_API_TOKEN была пустая! GitLab из соображений безопасности маскирует 403 Forbidden под 404 Not Found для приватных проектов.
Проблема #2: Обработка ошибок API Изначально проверял только количество найденных MR через jq 'length'. Но если API возвращает ошибку (например, JSON с полем «message»), то jq может вернуть неожиданный результат. Пришлось сначала проверять HTTP-статус ответа.
Дополнительные улучшения
В процессе отладки добавил несколько полезных деталей:
- Директива environment: Добавил
environment: developmentдля корректного отображения в разделе Deployments GitLab’а - Флаг —globoff для curl: На всякий случай отключил glob-интерпретацию в URL’ах
- Закомментированная отладка: Оставил в коде, но закомментировал — на случай будущих проблем
Результат
Теперь когда верстка пушит в layout-branch, но забывает создать MR, он получает чёткое сообщение:
Ошибка: Merge Request из ветки 'layout-branch' в 'dev' не найден.
Пожалуйста, создайте Merge Request перед запуском deploy.
Сборка останавливается, deploy не происходит. Через пару таких «напоминаний» привычка создавать MR выработалась сама собой.
Что можно улучшить
Если развивать идею дальше, можно добавить:
- Автоматическое создание draft MR через API (но тогда теряется осознанность процесса)
- Уведомления в Slack при блокировке deploy’я
- Проверку не только наличия MR, но и его статуса (например, что он не в draft)
Простая проблема с человеческим фактором решилась довольно элегантно через автоматизацию. GitLab API оказался достаточно мощным для таких сценариев, а CI/CD пайплайн — гибким.
Главное — не переусердствовать с автоматизацией. Цель была не наказать разработчика, а программно создать полезное напоминание, которое улучшит workflow всей команды. Да и нет смысла гавкать в 5-й и 10-й раз, повторяя одно и то же, будь у меня материальные способы воздействия на память, то после 2-го напоминания, всё идеально бы усвоилось наверняка. Но поскольку таковых нет, используем программные.
P.S. Переменную GITLAB_API_TOKEN не забудьте пометить как Protected и Masked в настройках проекта. Безопасность прежде всего!