Перейти к содержимому

Детерминированный quality gate на go с механикой Таро для github actions

Судьба в пайплайне: как создать детерминированный Quality Gate на Go с механикой Таро

Инструменты контроля давно стали обязательной частью CI/CD. Линтеры анализируют исходный код, сканеры ищут уязвимости, отдельные проверки оценивают зависимости, конфигурации и контейнерные образы. Всё это помогает снизить риск неудачного релиза и не допустить очевидные ошибки до этапа развертывания.

Но собственный инструмент для GitHub Actions не обязательно должен быть ещё одним стандартным анализатором. В этом проекте Quality Gate получил необычную механику: перед выпуском он вытягивает карту Таро и по её состоянию принимает решение, пропускать релиз дальше или остановить пайплайн. Проект называется Arcana Gate и написан на Go.

На первый взгляд случайность плохо сочетается с автоматизацией. CI/CD должен быть повторяемым, предсказуемым и идемпотентным. Если один и тот же коммит при каждом запуске получает новую карту, результат становится нестабильным: сегодня пайплайн заблокирован, а после перезапуска внезапно проходит. Поэтому в основе инструмента используется не настоящий случайный выбор, а детерминированная генерация псевдослучайных чисел.

Почему случайность привязана к коммиту

Простой вариант - инициализировать генератор текущим временем через `time.Now().UnixNano()`. Для обычной игры это подходит, но для CI/CD создаёт проблему. Повторный запуск одного и того же workflow произойдет в другой момент и получит новое начальное значение. Следовательно, карта изменится.

Arcana Gate использует хэш коммита в качестве источника для seed. В GitHub Actions нужное значение доступно в переменной `GITHUB_SHA`. Дополнительно поддерживаются `CI_COMMIT_SHA` и локальная переменная `ARCANA_SEED`, чтобы утилиту можно было запускать не только внутри GitHub.

Схема преобразования выглядит так:

1. берётся строковое значение SHA коммита;
2. оно дополнительно обрабатывается алгоритмом SHA-256;
3. из результата извлекаются первые восемь байт;
4. байты преобразуются в `int64`;
5. полученное число передаётся в `rand.NewSource()`.

Такой подход обеспечивает одинаковую последовательность псевдослучайных значений для одного и того же коммита. Перезапуск workflow не меняет расклад, а значит, решение Quality Gate остаётся стабильным.

Архитектура проекта

Проект разделён на несколько независимых частей:

- `internal/config/` - загрузка переменных окружения и параметров запуска;
- `internal/domain/` - описание карт, колоды и их свойств;
- `internal/engine/` - выбор карты и вычисление результата;
- `internal/presenter/` - вывод в терминал и формирование Step Summary;
- `cmd/arcana-gate/` - CLI и коды завершения;
- `.goreleaser.yml` - сборка бинарников под разные платформы;
- `Dockerfile` - контейнерный вариант запуска;
- `.github/workflows/` - проверки самого инструмента;
- `action.yml` - манифест GitHub Action.

Такое разделение позволяет не смешивать бизнес-логику с выводом и интеграцией. Движок не должен знать, запускается ли программа локально, в контейнере или внутри GitHub Actions. Он получает колоду и seed, а затем возвращает результат.

Конфигурация запуска

При старте программа читает несколько параметров. В первую очередь определяется источник seed. Для локального запуска можно передать собственное значение, а в CI приоритет отдаётся идентификатору коммита.

Отдельно используется флаг `STRICT`. Он определяет поведение при негативном раскладе:

- в обычном режиме карта может вывести предупреждение, но не остановить job;
- в строгом режиме блокирующая карта завершает процесс с ненулевым кодом.

Коды возврата особенно важны для пайплайна. GitHub Actions воспринимает ненулевой exit code как ошибку шага, поэтому именно таким способом движок передаёт своё решение оркестратору.

Также предусмотрена возможность передать собственную колоду. Это удобно для экспериментов, тестирования и дальнейшего расширения проекта без переписывания движка.

Модель карты и правила блокировки

Каждая карта содержит как минимум:

- название;
- описание;
- признак прямого или перевёрнутого положения;
- правило, определяющее блокировку пайплайна.

В текущей версии реализованы старшие арканы. Инструмент вытягивает одну карту, хотя классические расклады обычно используют несколько. Механика намеренно сделана простой: итог зависит от выбранной карты и её положения.

Ключевая проверка сосредоточена в методе `IsBlocking`. Правило можно задать непосредственно при описании карты. Это делает набор данных расширяемым: чтобы добавить новый аркан или изменить его трактовку, не требуется переписывать алгоритм выбора.

Описание карты выполняет не только декоративную функцию. Оно становится частью результата проверки и объясняет пользователю, почему релиз был разрешён или заблокирован. Для Quality Gate это важно: сухого сообщения "job failed" недостаточно, разработчик должен видеть понятную причину.

Вывод в терминал и Step Summary

Результат отображается в консоли в виде карты, включая её название, положение и интерпретацию. Формат можно адаптировать под локальный запуск, где пользователю нужен подробный текст.

В GitHub Actions дополнительно формируется Step Summary. Это специальный блок, который отображается непосредственно на странице workflow и не требует поиска информации среди длинного лога. В summary можно вывести итог, описание карты, режим работы и рекомендацию по дальнейшим действиям.

Такой вывод делает даже шуточную механику удобной для практического использования. Разработчик сразу понимает, какой аркан был выбран и почему шаг завершился именно так.

Сборка и публикация

Go выбран из-за быстрого запуска, простого кросс-компилирования и возможности распространять инструмент одним бинарным файлом. Для автоматической сборки используется GoReleaser, конфигурация которого хранится в YAML-файле репозитория.

Обычно собираются версии для нескольких операционных систем и архитектур. Контейнерный вариант предоставляет альтернативный способ запуска, особенно если команда предпочитает фиксировать окружение через Docker.

Чтобы подключить инструмент как GitHub Action, нужен корректный `action.yml`, релиз с тегом и доступный собранный артефакт. После этого действие можно использовать в workflow одной декларацией. Важный момент - версия Action должна фиксироваться тегом или конкретным коммитом, иначе обновление может неожиданно изменить поведение пайплайна.

Безопасность самого пайплайна

Инструмент проверяет не только чужие релизы, но и собственную сборку. Внутри репозитория можно настроить workflow, который запускает линтеры, тесты, проверки зависимостей и сборку контейнера. Получается своеобразная рекурсия: Quality Gate является частью процесса, который контролирует качество самого Quality Gate.

При работе с переменными окружения нельзя выводить в логи секреты и чувствительные данные. Значение SHA коммита не является секретом, но общее правило всё равно сохраняется: конфигурацию следует валидировать, а входные параметры - обрабатывать безопасно.

Полезно также предусмотреть защиту от пустого или некорректного seed. Если идентификатор коммита отсутствует, программа должна явно сообщить, какой источник используется вместо него, а не молча переходить к непредсказуемому времени запуска.

Тестирование детерминированности

Главная проверка проекта - многократный запуск с одинаковым seed. Результат должен совпадать полностью: выбранная карта, положение, текст интерпретации и код завершения.

Отдельно стоит тестировать разные источники конфигурации. Например, при наличии `GITHUB_SHA` он должен иметь приоритет над локальным значением, если именно такое поведение заложено в правилах приложения. Также необходимо проверить строгий и мягкий режимы, пустую колоду и неизвестные значения переменных.

Детерминированность не означает, что все коммиты получат разные карты. Коллизии возможны, поскольку генератор выбирает элемент из конечной колоды. Это нормально: задача заключается не в гарантии уникального результата, а в воспроизводимости для конкретного входа.

Практическая роль такого Quality Gate

Arcana Gate не заменяет статический анализ, тесты или сканирование безопасности. Его ценность находится в другом: он демонстрирует, как построить компактный CLI-инструмент, интегрировать его с GitHub Actions, настроить кросс-сборку и обеспечить стабильное поведение при повторных запусках.

Игровая оболочка помогает обратить внимание на важный инженерный принцип: даже необычная бизнес-логика должна подчиняться требованиям автоматизации. Если инструмент участвует в принятии решения о релизе, он обязан быть прозрачным, тестируемым и предсказуемым.

В результате получается полноценный Quality Gate на Go с подключаемой колодой, режимом строгой блокировки, понятным выводом и воспроизводимым выбором карты. Таро здесь выступает не заменой инженерных практик, а способом показать, что случайность в пайплайне может быть управляемой - если её правильно привязать к входным данным и не использовать текущее время в качестве единственного источника решения.

Прокрутить вверх