Антипаттерны: пять способов сделать пайплайн бесполезным
Зелёный job, который врёт, пароль в логе, latest вместо версии, монолитная задача и мёртвый only. Каждый разобран на живом прогоне.
Зачем разбирать плохое
Работающий пайплайн собрать несложно. Сложнее заметить, что он работает вхолостую: гоняется, зеленеет, а от проблем не защищает. Ниже пять таких случаев, каждый встречается в чужих конфигах регулярно.
Плохой пример целиком лежит в lesson-10-antipatterns/bad-pipeline.yml,
исправленный рядом в pipeline.yml.
1. Зелёный job, который врёт
green-lie:
image: node:22-alpine
script:
- node -e "throw new Error('тест упал')" || true
- echo "job завершился успешно"
Прогон этого job в логе:
$ node -e "throw new Error('тест упал')" || true
Error: тест упал
at [eval]:1:7
$ echo "job завершился успешно"
job завершился успешно
Статус — Passed, время — 9 секунд. Тест упал, ошибка в логе, пайплайн
зелёный.
|| true дописывают, чтобы «пока не мешало»: тесты флакают, линтер ругается
на легаси. Дальше про это забывают, и CI превращается в декорацию, которая
всегда показывает зелёный.
Если проверку временно нельзя делать блокирующей, для этого есть честный способ:
lint:
script:
- npm run lint
allow_failure: true
Разница принципиальная. || true прячет падение, allow_failure его
показывает: job помечается предупреждением, видно, что именно не прошло, и
это можно вернуть в блокирующие, когда почините.
2. Секрет в конфиге
- 'echo "Пароль от прода: hunter2"'
В логе:
Пароль от прода: hunter2
Тут два отдельных греха. Первый: пароль лежит в .gitlab-ci.yml, то есть в
git, то есть в истории навсегда, у всех, кто клонировал репозиторий. Второй:
он напечатан в лог, а логи job видны шире, чем кажется, и живут месяцами.
Как правильно, разбирали в уроке про переменные: значение в Settings -> CI/CD -> Variables с типом Masked, в команду через stdin, в лог никогда.
Отдельно про удаление. Убрать пароль коммитом недостаточно: он остаётся в истории. Утёкший секрет надо считать скомпрометированным и менять, а не прятать.
3. latest вместо версии
image: node:latest
Тег latest подвижный. Сегодня за ним Node 22, через месяц Node 24 с
другим поведением. Сборка, которая вчера проходила, сегодня падает, коммитов
между ними нет, и человек час ищет причину в своём коде.
То же самое с образами, которые собираете вы: myapp:latest в registry
означает «неизвестно что», по нему невозможно ни понять, что в проде, ни
откатиться.
Ставьте конкретный тег: node:22-alpine для окружения сборки,
myapp:$CI_COMMIT_SHORT_SHA для своих образов. Тег, привязанный к коммиту,
превращает откат в запуск предыдущего образа.
4. Один job на всё
deploy-everything:
script:
- npm ci
- npm test
- npm run build
- echo "деплой на прод"
Формально работает. Проблем три.
Обратной связи нет: в списке видно «job упал», а что именно упало, тесты или деплой, ясно только из лога.
Перезапуск дорогой: чтобы повторить деплой, придётся заново ставить зависимости и гонять тесты.
Параллельность недоступна: линтер и юнит-тесты могли бы идти одновременно, но они внутри одной последовательной команды.
Разнесите по стадиям, как в уроке про стадии: test, build, deploy.
Каждый со своим статусом, своим временем и возможностью перезапустить
только нужное.
5. only, который живёт рядом с rules
mixed:
script:
- echo hi
only:
- main
rules:
- if: $CI_COMMIT_BRANCH
Пайплайн даже не стартует:
Unable to run pipeline
jobs:mixed config key may not be used with `rules`: only
Это типичный результат правки чужого конфига: человек добавил rules
поверх старого only, не убрав его. Работавший пайплайн ломается целиком, а
не в одном job: ошибка конфигурации отменяет весь прогон.
only/except и rules несовместимы внутри одного job. Переписываете
job — убирайте only полностью.
Как исправленная версия выглядит целиком
stages:
- test
- build
- deploy
test:
stage: test
image: node:22-alpine
script:
- npm ci
- npm test
build:
stage: build
image: node:22-alpine
script:
- npm ci
- npm run build
artifacts:
paths:
- dist
expire_in: 1 day
deploy:
stage: deploy
script:
- echo "Выкат версии $CI_COMMIT_SHORT_SHA"
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual
Тесты блокируют сборку, сборка отдаёт артефакт с ограниченным сроком жизни, выкат идёт только с основной ветки и по кнопке, версия привязана к коммиту, секретов в файле нет.
Что важно запомнить
|| true делает проверку декоративной; когда падение допустимо осознанно,
это allow_failure: true.
Секрет в .gitlab-ci.yml остаётся в истории git навсегда и требует смены,
а не удаления коммитом.
latest — подвижная метка: фиксируйте версию образа, свои образы тегайте
по CI_COMMIT_SHORT_SHA.
Один job на всё лишает вас понятного статуса, дешёвого перезапуска и параллельности.
only и rules в одном job валят весь пайплайн на этапе разбора конфига.
На этом практический трек закончен. У вас есть пайплайн со стадиями, сборкой в контейнере, кешем, секретами и ручным выкатом — то есть скелет того, что делают в работе.
Прошли материал до конца? Отметьте урок пройденным.
Прогресс сохранён в этом браузере
Почистишь историю или откроешь с телефона, и отметки пропадут. Оставь почту, они переедут в аккаунт. Пароль придумывать не нужно.
Готово. теперь в аккаунте: прогресс виден с любого устройства. Мой прогресс