Команда с двоеточием сломала GitLab CI: различаем YAML и shell-кавычки

В .gitlab-ci.yml добавили обычную команду echo "Status: ready", и pipeline перестал создаваться. GitLab сообщает, что script должен быть строкой или массивом строк. До runner дело не дошло: ошибка возникла при чтении YAML. Двоеточие с последующим пробелом было воспринято как разделитель ключа и значения.

Кавычки вокруг аргумента echo принадлежат shell. Для YAML вся запись после дефиса по-прежнему может оставаться plain scalar, то есть строкой без внешних YAML-кавычек. Внутренние символы кавычек не обязаны переключать парсер в другой режим. Поэтому нужно защитить всю команду на уровне YAML, а затем отдельно сохранить нужный shell-синтаксис.

Исправим одной внешней парой кавычек

Следующий pipeline содержит два корректных варианта одной операции. В первом вся команда — YAML-строка в одинарных кавычках. Во втором используется literal block scalar.

show-status:
  image: alpine:3.22
  script:
    - 'printf "Status: %s\n" "ready"'
    - |
      printf 'Status: %s\n' 'ready'

Обе команды должны напечатать Status: ready. Внешние одинарные кавычки первой строки снимет YAML-парсер. Внутренние двойные кавычки затем увидит shell, который передаст printf один формат и одно значение. Во втором варианте YAML сохраняет содержимое блока как строку, а shell разбирает его обычным способом.

Нельзя просто добавить обратную косую черту перед двоеточием внутри произвольной YAML-строки. Это может создать неверную escape-последовательность либо передать лишний символ в shell. Сначала определите, какой парсер сообщает ошибку, и исправляйте именно его уровень.

Как читать диагностическое сообщение

Если GitLab отклоняет конфигурацию до создания jobs, проверяйте структуру YAML и итоговую CI-конфигурацию. Если job стартовала и shell пишет syntax error, YAML уже прошёл свою часть обработки. Одно и то же визуальное выражение может быть правильным на одном уровне и ошибочным на другом.

Откройте CI Lint или редактор pipeline и проверьте полный файл. При includes полезно посмотреть объединённую конфигурацию: ошибка может находиться в подключённой части. Обычный локальный YAML-парсер способен показать, что элемент script стал mapping, но не проверяет все предметные правила GitLab. Поэтому успешный parse — промежуточная проверка, а не окончательный допуск конфигурации.

Если используете YAML одинарные кавычки снаружи, одинарную кавычку внутри такого скаляра нужно удвоить. Для сложной shell-команды literal block обычно читается проще, чем несколько уровней экранирования. Не стоит сохранять однострочную форму любой ценой: будущий автор должен видеть настоящую команду.

Много строк не должны скрывать отказ

Block scalar удобен для нескольких связанных команд, но итоговый статус shell-блока требует внимания. Если ранняя команда упала, а затем отработал успешный printf, диагностический вывод способен закрыть предметную ошибку. Для критичных действий используйте явную проверку статуса и отдельные логические шаги.

Для короткой команды со строковым заголовком внешний YAML-скаляр обычно достаточен. Для большой логики лучше хранить исполняемый скрипт в репозитории и вызывать его из script. Так YAML описывает шаг pipeline, а shell-код проверяется собственными инструментами и не обрастает лишними слоями кавычек.

После исправления проверьте не только создание pipeline, но и реальный вывод. Добавленные кавычки не должны появляться в тексте заголовка, а двоеточие должно сохраниться. Тогда вы подтвердили оба уровня: YAML передал строку, shell выполнил именно ожидаемую команду.

Откройте программу GitLab CI/CD. Работа с .gitlab-ci.yml, логами и переиспользуемыми конфигурациями помогает собрать pipeline, где ошибки формата быстро отделяются от отказа самой сборки и не мешают выпуску приложения.

Ещё по теме

Продолжить на практике

  • GitLab CI/CD: автоматизация сборки и деплоя ↗

    От первого .gitlab-ci.yml до pipeline, который тестирует код, собирает образ и выкладывает приложение. Runners, artifacts, cache, registry, окружения и безопасный откат — на одном практическом проекте.