Skip to content

Начало работы ​

Раздел показывает путь от установки до первого сгенерированного пайплайна.

Установка ​

sh
opm install pipeliner

Библиотека зависит от autumn, autumn-cli и oscript-yaml — они установятся автоматически.

Как устроена библиотека ​

Pipeliner построен на DI-фреймворке autumn. Вы описываете джобы в .os-файлах, библиотека собирает их в YAML.

Три главных желудя:

ЖелудьНазначение
ФабрикаЗадачРеестр задач: Новая() создаёт описание задачи и накапливает список
ГенераторПайплайнаГитлабПревращает накопленные задачи в YAML и пишет файл
ОписаниеПайплайнаТоп-левел настройки: глобальные переменные, coverage, before/after script

Первый пайплайн ​

Создайте скрипт build.os:

bsl
#Использовать pipeliner
#Использовать autumn
#Использовать oscript-yaml

Поделка = Новый Поделка();
Поделка.ЗапуститьПриложение();

Фабрика = Поделка.НайтиЖелудь("ФабрикаЗадач");
Генератор = Поделка.НайтиЖелудь("ГенераторПайплайнаГитлаб");

Сборка = Фабрика.Новая();
Сборка.Наименование = "build";
Сборка.Стадия = "build";
Сборка.Образ.Имя = "alpine:3.19";
Сборка.Скрипты.Добавить("make build");

Тесты = Фабрика.Новая();
Тесты.Наименование = "test";
Тесты.Стадия = "test";
Тесты.Нуждается.Добавить("build");
Тесты.Скрипты.Добавить("make test");

Генератор.СформироватьПайплайн(".gitlab-ci.yml");

Запустите:

sh
oscript build.os

Получите .gitlab-ci.yml — закоммитьте его в репозиторий.

Важные правила ​

  1. Задачи создавайте только через Фабрика.Новая() — прямой Новый ОписаниеЗадачи() уходит мимо DI и реестра задач.
  2. Пустые поля не выводятся — если поле/список не заполнены, ключ не попадёт в YAML. Чистый вывод без мусора.
  3. Валидация на генерации — ошибка в needs, недопустимое условие retry, отсутствие тега у релиза и т.п. роняют генерацию с понятным текстом до записи файла.

Повторное использование конфигурации ​

Общая настройка джоб выносится в обычную процедуру OneScript:

bsl
Процедура НастроитьDeployДжобу(Задача)
	Задача.Стадия = "deploy";
	Задача.Образ.Имя = "alpine:3.19";
	Задача.Окружение.Имя = "production";
	Задача.Кэш.ДобавитьПуть("vendor/");
КонецПроцедуры

ДеплойПрод = Фабрика.Новая();
ДеплойПрод.Наименование = "deploy-prod";
НастроитьDeployДжобу(ДеплойПрод);
ДеплойПрод.Скрипты.Добавить("make deploy-PROD");

ДеплойСтейдж = Фабрика.Новая();
ДеплойСтейдж.Наименование = "deploy-stage";
НастроитьDeployДжобу(ДеплойСтейдж);
ДеплойСтейдж.Окружение.Имя = "staging";
ДеплойСтейдж.Скрипты.Добавить("make deploy-STAGE");

Это ключевая идея pipeliner: вместо YAML-шаблонов (extends) — обычные BSL-процедуры, с параметрами, условиями и автоподсказками.

Локальный запуск вне GitLab ​

Ссылки на переменные GitLab ($CI_COMMIT_TAG и т.п.) при локальной генерации работают: недостающие переменные возвращают своё имя ("$CI_COMMIT_TAG"), поэтому пайплайн генерируется без окружения CI.

Дальше ​


Описание задачи ​

ОписаниеЗадачи — центральная модель библиотеки. Создаётся только через фабрику:

bsl
Задача = Фабрика.Новая();

Под-объекты (образ, кэш, артефакты, сервисы и т.д.) создаются лениво при первом обращении — отдельной инициализации не требуется.

Базовые поля ​

ПолеТипКлюч YAMLОписание
НаименованиеСтрокаимя джобыИмя джобы в пайплайне
СтадияСтрокаstageСтадия выполнения
СкриптыМассивscriptКоманды — добавляйте через Скрипты.Добавить("...")
КогдаСтрокаwhenУсловие запуска. Сахар: СобытияПайплайна.ПриУспехе(), .РучнойЗапуск(), .ПриНеудаче(), .Всегда()
ТегиРаннераМассивtagsТеги раннера
ТаймаутСтрокаtimeoutНапример "2h", "30m"
ПрерываемыйБулевоinterruptibleОтмена при новом пуше
РазрешитьПадениеБулевоallow_failureНе валить пайплайн при падении
СтартоватьСразуБулевоneeds: []Немедленный старт, игнорируя стадии. Взаимоисключающее с Нуждается
ГруппаРесурсовСтрокаresource_groupСериализация запусков (deploy-джобы не должны бежать параллельно)
НаследуетДефолтыБулевоinherit: defaultПо умолчанию Истина. Ложь отключает применение глобальных before_script/after_script (default-секции) к джобе
НаследуетПеременныеБулевоinherit: variablesПо умолчанию Истина. Ложь отключает применение глобальных variables к джобе

Пример ​

bsl
Задача.Наименование = "deploy-prod";
Задача.Стадия = "deploy";
Задача.Таймаут = "2h";
Задача.Прерываемый = Истина;
Задача.ГруппаРесурсов = "production";
Задача.Скрипты.Добавить("make deploy");

Наследование (inherit) ​

По умолчанию GitLab применяет ко всем джобам default-секцию (глобальные before_script/after_script) и корневые variables. Чтобы изолировать джобу, отключите нужные виды наследования — генератор выведет в джобу ключ inherit:

bsl
Задача.НаследуетДефолты = Ложь;    // inherit: { default: false } — без глобальных before/after script
Задача.НаследуетПеременные = Ложь; // inherit: { variables: false } — без глобальных variables

Если оба флага включены (по умолчанию), ключ inherit не выводится вовсе.

Нуждается (needs) ​

Массив имён джоб, от которых зависит запуск (DAG). Валидируется при генерации: ссылка на несуществующую джобу роняет генерацию с перечнем доступных.

bsl
Задача.Нуждается.Добавить("build");
Задача.Нуждается.Добавить("test");

Зависимости (dependencies) ​

Какие артефакты тащить из предыдущих стадий. Валидации нет (это отдельный механизм GitLab).

bsl
Задача.Зависимости.Добавить("build");

Образ (image) ​

bsl
Задача.Образ.Имя = "docker:latest";
Задача.Образ.УказатьПолитику("always");   // с валидацией
// или сахар:
Задача.Образ.Всегда();
Задача.Образ.Никогда();
Задача.Образ.ЕслиОтсутствует();
Задача.Образ.Принудительно();             // pull, GitLab/раннер >= 15.1
Задача.Образ.ДобавитьКомандуЭнтрипоинт("/bin/sh");

Сервисы (services) ​

Массив сервис-контейнеров, фабрика НовыйСервис():

bsl
База = Задача.НовыйСервис();
База.Имя = "postgres:14";
База.Алиас = "db";
База.ДобавитьПеременную("POSTGRES_PASSWORD", "secret");

Кэш (cache) ​

bsl
Задача.Кэш.ДобавитьПуть("vendor/");
Задача.Кэш.КлючПоФайлам.Добавить("packagedef");

Артефакты (artifacts) ​

bsl
Задача.Артефакты.ДобавитьПуть("dist/");
Задача.Артефакты.ДобавитьИсключение("dist/*.tmp");
Задача.Артефакты.ВремяХранения = "1 week";

Отчёты (reports) ​

Виды отчётов — через модуль ВидыОтчетов с валидацией:

bsl
Задача.Артефакты.ДобавитьОтчет(ВидыОтчетов.Юнит(), "junit.xml");
Задача.Артефакты.ДобавитьОтчет(ВидыОтчетов.САСТ(), "gl-sast-report.json");
Задача.Артефакты.ДобавитьОтчетПокрытия(ВидыОтчетов.Кобертура(), "coverage.xml");

Допустимые виды: Юнит, САСТ, ДАСТ, Дотенв, СканированиеЗависимостей, ПоискСекретов, КачествоКода, СканированиеКонтейнеров, САРИФ, ЦиклонДХ, Терраформ и другие — полный список в модуле ВидыОтчетов.os.

Повтор (retry) ​

bsl
Задача.Повтор.Повторы = 2;
Задача.Повтор.ДобавитьУсловие(УсловияПовтора.СбойСкрипта());
Задача.Повтор.ДобавитьУсловие(УсловияПовтора.СбойРаннера());
Задача.Повтор.ДобавитьКодВыхода(42);

Условия валидируются против набора GitLab (22 значения, см. УсловияПовтора.os). При нескольких условиях выводится массив, при одном — строка. Дубли сворачиваются.

Параллельность (parallel) ​

bsl
// Число копий
Задача.Параллельность.Количество = 5;

// Матрица: декартово произведение в одном ряду
Ряд = Новый Соответствие();
Ряд.Вставить("OS", ОС);
Ряд.Вставить("VERSION", Версии);
Задача.Параллельность.ДобавитьРяд(Ряд);

// Или независимые ряды
Задача.Параллельность.ДобавитьРядМатрицы("OS", ОС);

Число и матрица взаимоисключающие — одновременное задание роняет генерацию.

Окружение (environment) ​

bsl
Деплой.Окружение.Имя = "production";
Деплой.Окружение.УРЛ = "https://prod.example.com";
Деплой.Окружение.ПриОстановке = "stop-prod";
Деплой.Окружение.АвтоОстановка = "1 week";

// Стоп-джоба:
СтопДжоба.Окружение.Действие = ДействияОкружения.Остановка();

Валидации: недопустимое действие, пустое имя при заполненных остальных, ПриОстановке на несуществующую джобу.

Релиз (release) ​

bsl
Задача.Релиз.Тег = "$CI_COMMIT_TAG";
Задача.Релиз.Описание = "Что нового";
Задача.Релиз.Имя = "Версия 1.2.3";
Задача.Релиз.ДобавитьВеху("Milestone 1");
Задача.Релиз.ДобавитьСсылку("Установщик", "https://example.com/setup.exe");

Тег и описание обязательны — без них генерация падает с именем задачи.

GitLab Pages ​

bsl
// Джоба с именем "pages", стадия deploy
Страницы.Наименование = "pages";
Страницы.Стадия = "deploy";
Страницы.Страницы.Публиковать("dist");

publish автоматически добавляется в artifacts:paths (GitLab 17.10+). Валидации: pages без артефактов и не в стадии deploy роняют генерацию.

Правила (rules) ​

Правила управляют условиями запуска джобы. Каждое правило — структура из НовыйПравило(), добавляется в Задача.Правила. Внутри правила ключи выводятся в порядке: if → when → changes (+compare_to) → exists → variables → allow_failure.

Базовое правило ​

bsl
Изменения = Новый Массив();
Изменения.Добавить("src/**.os");
Задача.ДобавитьПравило("$CI_PIPELINE_SOURCE == \"merge_request_event\"", "always", Изменения);

exists — запуск по наличию файлов ​

Сахар ДобавитьПравилоЕслиСуществует(Путь, Условие = ""):

bsl
Задача.ДобавитьПравилоЕслиСуществует("Dockerfile");
// или с условием:
Задача.ДобавитьПравилоЕслиСуществует("Dockerfile", "$CI_COMMIT_BRANCH == \"main\"");

Расширенный вариант (project/ref) — через НовыйПравило(); тогда exists выводится объектом с paths:

bsl
Правило = Задача.НовыйПравило();
Правило.Существует.Добавить("**/*.os");
Правило.СуществуетВПроекте = "group/project";   // опционально
Правило.СуществуетПоРефу = "main";              // опционально
Задача.Правила.Добавить(Правило);

variables правила ​

Переменные, применяемые к джобе при срабатывании правила:

bsl
ПеременныеПравила = Новый Соответствие();
ПеременныеПравила.Вставить("BUILD_MODE", "fast");
Задача.ДобавитьПравилоСПеременными("$CI_COMMIT_BRANCH == \"main\"", ПеременныеПравила);

allow_failure в правиле ​

Сахар ДобавитьПравилоПаденияДопустимо(КодыВыхода = Неопределено):

bsl
Задача.ДобавитьПравилоПаденияДопустимо();          // allow_failure: true
Коды = Новый Массив();
Коды.Добавить(1);
Задача.ДобавитьПравилоПаденияДопустимо(Коды);       // allow_failure: {exit_codes: [1]}

Явный запрет — Правило.РазрешитьПадение = Ложь даёт allow_failure: false.

compare_to для changes ​

bsl
Правило = Задача.НовыйПравило();
Правило.Изменения.Добавить("**/*.os");
Правило.СравнитьС = "main";

Комбинированное правило ​

bsl
Правило = Задача.НовыйПравило();
Правило.Условие = "$CI_COMMIT_BRANCH == \"main\"";
Правило.Существует.Добавить("deploy.sh");
Правило.Переменные.Вставить("DEPLOY_MODE", "auto");
Правило.РазрешитьПадение = Истина;
Задача.Правила.Добавить(Правило);

Валидации ​

Генерация падает с исключением, если:

  • exists задан через project/ref без путей;
  • КодыВыходаПадения заполнены, но РазрешитьПадение <> Истина.

Переменные джобы ​

bsl
Задача.ДобавитьПеременную("BUILD_TYPE", "release");

Описание пайплайна ​

ОписаниеПайплайна — топ-левел настройки всего пайплайна. Доступ через желудь:

bsl
ОписаниеПайплайна = Поделка.НайтиЖелудь("ОписаниеПайплайна");

Глобальные переменные ​

bsl
ОписаниеПайплайна.ДобавитьПеременную("REGISTRY", "registry.example.com");
ОписаниеПайплайна.ДобавитьПеременную("DEPLOY_ENV", "staging");

Выводится в корень YAML:

yaml
variables:
  REGISTRY: registry.example.com
  DEPLOY_ENV: staging

Coverage ​

Регекс извлечения процента покрытия из лога джобы:

bsl
ОписаниеПайплайна.ШаблонПокрытия = "/^Total.*?(\d+\s*)$/";

Экранирование бэкслешей выполняется автоматически при сериализации.

Before/After script по умолчанию ​

bsl
ОписаниеПайплайна.ДобавитьПередСкрипт("echo start");
ОписаниеПайплайна.ДобавитьПослеСкрипт("echo done");

Выводится в блок default: и применяется ко всем джобам без собственного before/after.

Порядок топ-левел ключей ​

Генератор выводит секции в фиксированном порядке:

default -> stages -> variables -> coverage -> (джобы)

Стадии ​

Массив stages генератор собирает автоматически из стадий задач (порядок появления). Зарезервированные стадии .pre/.post в список не попадают — подробнее в разделе Стадии.


Стадии ​

Массив stages генератор собирает автоматически: берёт все стадии из задач в порядке появления, без дублей. Явно объявлять стадии не нужно.

bsl
Сборка = Фабрика.Новая();
Сборка.Стадия = "build";

Тесты = Фабрика.Новая();
Тесты.Стадия = "test";
// stages будет [build, test]

Зарезервированные стадии .pre и .post ​

GitLab имеет две особые стадии, которые выполняются до всех пользовательских (.pre) и после всех (.post). Их не объявляют в stages — GitLab знает их сам.

Pipeliner это учитывает: стадии .pre/.post не попадают в сгенерированный stages, но джобы с ними выводятся как обычные.

Сахар ​

Вместо строковых литералов используйте методы задачи:

bsl
Линт = Фабрика.Новая();
Линт.Наименование = "lint";
Линт.ДоВсех();                 // stage: .pre
Линт.Скрипты.Добавить("lint.sh");

Уведомление = Фабрика.Новая();
Уведомление.Наименование = "notify";
Уведомление.ПослеВсех();       // stage: .post
Уведомление.Скрипты.Добавить("notify.sh");

Прямое присваивание тоже работает и фильтруется так же:

bsl
Задача.Стадия = ".pre";

Реестр зарезервированных стадий ​

Источник имён — Гитлаб.ЗарезервированныеСтадии(), соответствие ключ → значение:

bsl
Реестр = Гитлаб.ЗарезервированныеСтадии();
// ДоВсех -> ".pre"
// ПослеВсех -> ".post"

Если GitLab добавит новую зарезервированную стадию — она дописывается в реестр, фильтр генератора подхватит её автоматически.

Ограничение GitLab ​

Пайплайн, состоящий только из .pre/.post джоб, не запускается — нужна минимум одна джоба в обычной стадии.

Связка с needs ​

Джобы в .pre и джобы с needs: [] стартуют немедленно при создании пайплайна — используйте это для быстрых проверок (линт, smoke-тесты), которые не должны ждать сборки.


CLI ​

Pipeliner устанавливается как консольная утилита pipeliner (см. packagedef → ИсполняемыйФайл).

Команда gitlab ​

Генерация пайплайна для GitLab:

sh
pipeliner gitlab <путь_к_файлу>

Пример:

sh
pipeliner gitlab .gitlab-ci.yml

Команда загружает стадии из каталога ./pipeline (все *.os файлы, включая вложенные), выполняет их и пишет накопленный пайплайн в указанный файл.

Структура каталога pipeline ​

Каждый *.os-файл в каталоге pipeline — это стадия пайплайна. Файлы загружаются автоматически:

проект/
├── pipeline/
│   ├── build.os
│   ├── test.os
│   └── deploy.os

Пример содержимого pipeline/build.os:

bsl
#Использовать pipeliner
#Использовать autumn
#Использовать oscript-yaml

Поделка = Новый Поделка();
Поделка.ЗапуститьПриложение();

Фабрика = Поделка.НайтиЖелудь("ФабрикаЗадач");

Сборка = Фабрика.Новая();
Сборка.Наименование = "build";
Сборка.Стадия = "build";
Сборка.Скрипты.Добавить("make build");

Фабрика накапливает задачи из всех загруженных файлов, генератор собирает их в один YAML.

Docker ​

Библиотеку можно запускать в контейнере — Dockerfile.local собирает образ из исходников:

sh
docker build -f Dockerfile.local -t pipeliner:local .
docker run --rm -v $(pwd):/app pipeliner:local gitlab /app/.gitlab-ci.yml

Образ базируется на sleemp/oscript:2.1.0, пакет собирается и устанавливается при сборке образа.

Каталог .dockerignore ​

В репозитории есть .dockerignore, исключающий .git и *.ospx из контекста сборки — без него собранный пакет из рабочего каталога ломает повторную сборку образа.