Введение
GitLab CI — альтернативная, широко используемая платформа CI/CD, особенно распространённая в организациях, использующих самостоятельно развёрнутый (self-hosted) GitLab — в статье разберём её ключевые отличия от GitHub Actions и базовый синтаксис .gitlab-ci.yml.
Концепция
GitLab CI определяется единственным файлом .gitlab-ci.yml в корне репозитория (в отличие от множества отдельных workflow-файлов GitHub Actions), организованным через «стадии» (stages, например build, test, deploy), выполняемые последовательно, и «задачи» (jobs) внутри каждой стадии, способные выполняться параллельно друг с другом в рамках одной стадии. GitLab Runner — агент, выполняющий реальную работу задач CI, может быть как предоставляемым самим GitLab.com (shared runners), так и самостоятельно развёрнутым (self-hosted runners) на собственной инфраструктуре организации, что особенно ценно для задач, требующих специфичного оборудования (например, физическая Raspberry Pi/STM32-плата для реального, а не эмулированного embedded-тестирования) или доступа к внутренней, не публичной сети организации.
Пример кода
# .gitlab-ci.yml — структура со стадиями build/test/deploy
stages:
- build
- test
- deploy
build_job:
stage: build
image: ubuntu:22.04
before_script:
- apt-get update && apt-get install -y cmake qt6-base-dev
script:
- cmake -B build -S .
- cmake --build build
artifacts:
paths:
- build/MyQtApp # передача результата сборки в следующие стадии (test, deploy)
test_job:
stage: test
image: ubuntu:22.04
script:
- ctest --test-dir build --output-on-failure
dependencies:
- build_job # явное указание зависимости от результата предыдущей стадии
deploy_job:
stage: deploy
script:
- echo "Публикация релиза..."
only:
- main # выполняется ТОЛЬКО для основной ветки, не для каждого pull request
# Использование self-hosted runner со специфичными возможностями (например, доступ к реальной
# Raspberry Pi-плате для embedded-тестирования, продолжение Raspberry Pi-цикла)
embedded_test_job:
stage: test
tags:
- raspberry-pi # этот job выполнится ТОЛЬКО на runner'е с этим конкретным тегом,
# зарегистрированном на машине с реально подключённой Raspberry Pi
script:
- ./run_hardware_tests.sh
Пояснения к коду
Структура stages/stage явно показывает последовательную организацию GitLab CI — test_job выполняется только после успешного завершения всех задач стадии build, а deploy_job — только после успешного завершения test, и artifacts обеспечивают передачу конкретных файлов (результата сборки) между разными, изолированными друг от друга задачами разных стадий, выполняющимися на потенциально разных runner’ах. tags: [raspberry-pi] показывает практичный механизм направления конкретной задачи именно на runner с нужными специфичными возможностями — это особенно ценно для проектов с embedded-составляющей (Raspberry Pi/STM32-циклы), где часть тестирования реально требует физического, специфичного оборудования, недоступного на обычных, общих runner’ах облачного CI-сервиса.
Подводные камни
- Отсутствие явного указания
only/rules(более новый, гибкий синтаксис) для задач, которые не должны выполняться для каждого изменения (например,deploy_job, которая должна срабатывать только для основной ветки, а не для каждой ветки функции или pull request) — без такого ограничения задача деплоя могла бы непреднамеренно сработать для незавершённой, экспериментальной ветки разработки, что особенно опасно для задач с реальными, видимыми пользователю побочными эффектами (публикация релиза). - Использование self-hosted runner без должного понимания его операционной нагрузки и обслуживания — в отличие от полностью управляемых, облачных shared runners GitHub Actions/GitLab.com, self-hosted runner требует самостоятельного администрирования (обновления, мониторинг доступности, безопасность машины, на которой он работает), и эта операционная ответственность должна быть осознанно принята командой, разворачивающей такой runner, особенно для критичных задач (например, embedded-тестирование на реальном оборудовании), зависящих от непрерывной доступности именно этого конкретного, специфично настроенного runner’а.
- Забытое указание
artifactsдля передачи результата сборки между стадиями, что приводит к ошибке на следующей стадии (например,test_jobне находит скомпилированный исполняемый файл, поскольку каждая задача GitLab CI по умолчанию выполняется в полностью чистом, изолированном окружении, не сохраняющем автоматически результаты предыдущих задач) — явное указаниеartifacts(и соответствующихdependenciesдля последующих задач, нуждающихся в этих артефактах) обязательно для любых данных, которые должны «пережить» границу между отдельными задачами разных стадий. - Слепое копирование конфигурации
.gitlab-ci.ymlиз примера/документации без адаптации к конкретному проекту, особенно версии используемых образов (image: ubuntu:22.04) и реальных команд установки зависимостей, специфичных для конкретного, возможно отличного от примера набора зависимостей конкретного Qt-проекта — конфигурация CI должна быть протестирована и адаптирована именно для реальных, конкретных потребностей текущего проекта, а не скопирована механически из чужого, возможно не полностью соответствующего примера.