.gitlab-ci.yml: команды, image, before_script и YAML
GitLab CI управляется файлом .gitlab-ci.yml в корне репозитория. Когда ты делаешь git push, GitLab читает этот файл, строит pipeline и отдает jobs runner-у.
Это обычный YAML-файл, поэтому отступы важны. Табуляции лучше не использовать. Если GitLab пишет yaml invalid, сначала проверь структуру.
В лабораторных файл удобно редактировать в VSCode внутри VM: нажми кнопку VSCode, открой папку gitlab-ci-lab и файл .gitlab-ci.yml. Если работаешь прямо в терминале, используй Vim.
В GitLab UI файл тоже можно открыть через Build -> Pipeline editor. Там GitLab может подсказать, валидна ли конфигурация. Но полный процесс как в реальном проекте такой: изменить файл в репозитории, сделать commit, push и посмотреть pipeline.
Если GitLab показывает Pipeline syntax is correct, это означает только одно: YAML можно разобрать и GitLab понимает структуру pipeline. Это еще не доказывает, что команды внутри job реально пройдут. Например, docker build может упасть уже во время выполнения, если не найден Dockerfile.
Job
Job - минимальная единица работы. Пример:
check-shell:
image: alpine:3.20
stage: verify
before_script:
- apk add --no-cache bash
script:
- bash --version
- ./scripts/check.sh
after_script:
- echo "job finished"
Что здесь происходит:
check-shell- имя job, оно будет видно в pipeline;image- Docker image, внутри которого runner выполнит команды;stage- этап pipeline;before_script- подготовка перед основными командами;script- главная работа job;after_script- запускается и после успешного job, и после падения.
image
image отвечает на вопрос: “в каком окружении выполнить команды”. Если указать alpine:3.20, внутри job будет маленький Linux с Alpine. Если указать docker:27-cli, внутри будет Docker CLI.
show-os:
image: alpine:3.20
script:
- cat /etc/os-release
Новички часто путают image в job и Docker image приложения. В GitLab CI image означает: runner запустит Docker container из этого образа и уже внутри него выполнит команды из script.
Например, если в job указано image: alpine:3.20, GitLab не собирает твоё приложение в Alpine. Он просто запускает временный container с Alpine и выполняет там команды job. А Docker image приложения - это отдельный результат сборки: его ты создаёшь командой docker build и потом отправляешь в registry.
before_script и after_script
У job есть основная часть - script. Именно там должна быть главная работа: запустить тесты, собрать приложение, собрать Docker image, выполнить deploy-команду.
Но почти всегда вокруг основной работы есть служебные шаги. Перед тестами нужно установить зависимости. Перед deploy нужно войти в registry (docker login) или подготовить SSH-ключ. После падения полезно вывести список файлов, статус сервиса или последние строки лога. Для таких случаев есть before_script и after_script.
before_script выполняется перед script. Его удобно использовать для подготовки окружения:
before_script:
- apk add --no-cache curl bash
- chmod +x ./scripts/check.sh
check-shell:
image: alpine:3.20
script:
- ./scripts/check.sh
В этом примере runner сначала поставит curl и bash, затем сделает скрипт исполняемым, и только потом перейдет к основной команде ./scripts/check.sh.
Частые случаи для before_script:
- поставить маленькую утилиту, которой нет в image;
- выполнить
docker loginперед сборкой или публикацией image; - подготовить SSH-доступ перед deploy;
- создать временную директорию;
- проверить, что нужная переменная окружения задана;
- выполнить одинаковую подготовку для нескольких jobs.
При этом before_script не должен превращаться в “установи половину системы каждый раз”. Если job постоянно начинает с большой установки пакетов, pipeline будет медленным. Обычно лучше выбрать более подходящий image или собрать свой базовый image для CI. Переиспользование общих блоков через шаблоны, extends и include мы разберём дальше.
after_script выполняется после script. Его часто используют не для основной работы, а для диагностики и аккуратного завершения:
after_script:
- echo "job status: $CI_JOB_STATUS"
- ls -la
- tail -50 app.log || true
Такой блок полезен, когда job упал, а тебе нужно понять почему. Например, тесты завершились ошибкой, но after_script всё равно выведет статус, список файлов и последние строки лога. Это помогает быстрее разобраться, что произошло внутри контейнера runner-а.
Частые случаи для after_script:
- вывести
$CI_JOB_STATUS; - показать логи после падения;
- проверить, какие файлы реально были созданы;
- удалить временные файлы или остановить локальный тестовый процесс;
- оставить понятную диагностику для человека, который открыл log job.
Важно: не стоит переносить в after_script то, от чего зависит успешность job. Основная проверка должна быть в script, потому что именно она показывает, прошла работа или нет. after_script лучше воспринимать как “после основной работы покажи полезную информацию и аккуратно заверши окружение”.
Multiline script
Для нескольких связанных shell-команд удобно использовать YAML block scalar:
test:
script:
- |
set -e
echo "start"
./run-tests.sh
echo "done"
set -e останавливает shell при ошибке. Без него команда внутри многострочного блока может упасть, а job все равно продолжит выполнение, если следующая команда завершится успешно. Для CI это может быть опасно: pipeline может стать зеленым, хотя внутри была ошибка.
В следующей главе разберём variables. Это уже следующий слой поверх script и image: сначала посмотрим переменные, которые прямо записаны в .gitlab-ci.yml, потом переменные из интерфейса GitLab, а затем встроенные переменные, которые GitLab создаёт сам.
