zoryn flow¶
zoryn flow выполняет сценарий (flow) — заданную вами последовательность шагов, которая сводит рутинное обновление пакета к одной команде. Сценарий умеет обновить и собрать пакет, прогнать ваши проверки по ssh или внутри devenv, отправить результат в gyle, а если шаг упал — запустить восстановительный сценарий (вплоть до ИИ-агента, читающего лог сборки) и повторить шаг.
Ни один шаг ничего не спрашивает у пользователя: сценарий рассчитан на запуск из cron, из CI или в терминале, за которым никто не следит.
Экспериментально
zoryn flow — экспериментальная команда. Типы шагов, ключи конфигурации и формат состояния могут измениться в будущих версиях.
Использование¶
zoryn flow [NAME] [--continue] [--restart] [--abort] [--dry-run] [-C DIR] [-d] [-v]
zoryn flow run [NAME] [--continue] [--restart] [--abort] [--dry-run] [-C DIR] [-d] [-v]
zoryn flow list [-C DIR]
zoryn flow show NAME [-C DIR]
zoryn flow NAME и zoryn flow run NAME — одно и то же: короткая форма переписывается в run ещё до разбора аргументов. Явная форма zoryn flow run <имя> нужна только для сценария, названного буквально run, list, show или help.
Команду запускают внутри gear-репозитория; zoryn сначала переходит в корень репозитория, поэтому слои конфигурации, .gear/version-up, {pkg} и файл состояния относятся к одному и тому же пакету, из какого бы подкаталога вы ни стартовали.
zoryn flow run¶
Выполняет сценарий. Без NAME сценарий выбирается по ключу [flow] name из .gear/version-up, а если его нет — по default из таблицы [flows] машинной конфигурации; с --continue и без NAME имя берётся из сохранённого состояния.
Опции¶
NAME— какой сценарий выполнить. Без него имя берётся из[flow] nameв.gear/version-up, иначе из[flows] default.--continue— продолжить прерванный сценарий с упавшего шага.--restart— отбросить сохранённое состояние и выполнить сценарий с первого шага.--abort— отбросить сохранённое состояние прерванного сценария.--dry-run— напечатать разобранные шаги и выйти, ничего не выполняя.-C DIR— перейти в каталогDIRперед выполнением.-d,--debug— отладочный вывод (в том числе трассировка порождаемых процессов).-v,--verbose— показывать вывод сборки на экране для шаговbuild, как это делаетzoryn build -v(первая сборка внутри шагаupвсегда пишется только в лог-файл).
--continue, --restart и --abort взаимно исключают друг друга, а --continue несовместим с --dry-run.
zoryn flow # выполнить выбранный сценарий
zoryn flow update # выполнить сценарий с именем 'update'
zoryn flow update --dry-run # только напечатать разобранные шаги
zoryn flow --continue # продолжить после исправления упавшего шага
zoryn flow --abort # отбросить сохранённое состояние
zoryn flow list¶
Печатает сценарии, видимые из текущего пакета: имя на строке, под ним — файл конфигурации, в котором сценарий определён. Если одно имя описано в двух слоях, показывается только победившее определение.
zoryn flow show¶
Печатает шаги сценария NAME в том же нумерованном виде, что и --dry-run, а под каждым шагом — его if, on-failure и max-retries, если тот отличается от значения по умолчанию. Значения, которые появятся только после следующих шагов, ещё неизвестны, поэтому строка с {new_version} или {task} печатается как есть.
$ zoryn flow show update
Flow update
1. up
on-failure: fix-build
max-retries: 3
2. devenv: make smoke
3. ssh stand1: run-checks {pkg} {new_version}
4. submit -B sisyphus --run
Где описываются сценарии¶
Сценарии читаются только из машинной конфигурации, из двух слоёв:
~/.config/zoryn/projects.d/<проект>.toml— для конкретного пакета, приоритетнее;~/.zoryn— общая настройка.
Репозиторий пакета описывать шаги не может. В шагах лежат shell-команды и ssh-хосты, поэтому склонированный репозиторий не должен иметь возможности их подсунуть — максимум, что ему позволено, это выбрать сценарий по имени:
Если выбранного имени нет в вашей собственной конфигурации, запуск завершится ошибкой unknown flow '<имя>', и ничего из репозитория выполнено не будет.
Формат конфигурации¶
Каждый сценарий — это таблица [flows.<имя>] с массивом steps из inline-таблиц. В самой таблице [flows] хранится только ключ default — сценарий, который берётся, когда его никто не выбрал явно.
[flows]
default = "update"
[flows.update]
steps = [
{ run = "up", on-failure = "fix-build", max-retries = 3 },
{ run = "devenv", cmd = "make smoke" },
{ run = "ssh", host = "stand1", cmd = "run-checks {pkg} {new_version}" },
{ run = "submit", branch = "sisyphus", task-run = true },
]
[flows.fix-build]
steps = [ { run = "agent" } ]
[flows.batch-update]
steps = [
{ run = "batch", config = "qt6.toml" },
{ run = "bash", cmd = "notify-send 'batch {pkg} done'" },
]
# отправка в несколько репозиториев: один submit создаёт задание на каждую ветку, а {tasks} адресует их все
[flows.kernel]
steps = [
{ run = "submit", branch = "sisyphus,p11", task-run = false },
{ run = "zoryn", args = ["task", "add", "{tasks}", "rebuild", "--dependent-on", "kernel-image-for-vm"] },
{ run = "zoryn", args = ["task", "approve", "{tasks}", "all", "-m", "sign"] },
{ run = "zoryn", args = ["task", "run", "{tasks}", "--commit"] },
]
Типы шагов¶
run | Ключи | Что делает шаг |
|---|---|---|
up | опции сборки + no-build | полный конвейер обновления zoryn up (стадии detect … up-hooks) и сборка новой версии — либо, при no-build = true, только обновление. Даёт {old_version} и {new_version} |
build | опции сборки | zoryn build |
submit | опции submit | zoryn submit. Редактор сообщения коммита не открывается, а конфликт тэгов отменяет шаг, а не задаёт вопрос. Даёт {task} |
task-add | task (по умолчанию {task}), args, dependent-on | zoryn task add: добавляет подзадание в task — args задаёт действие (пакеты, rebuild, ветка), dependent-on соответствует --dependent-on. Все значения шаблонизируются, так что по умолчанию шаг работает с заданием, созданным шагом submit |
task-approve | task (по умолчанию {task}), subtask (по умолчанию all), message | zoryn task approve: подтверждает subtask (номер, пакет, pkg.git=tag или all) задания task; message уходит в комментарий -m |
task-run | task (по умолчанию {task}), commit, message | zoryn task run: запускает сборку задания task; commit = true добавляет --commit, message — комментарий -m |
batch | config (обязателен) | zoryn task batch <config> --no-edit-commit; если от прошлой попытки осталось состояние пакетной сборки, повтор добавляет --continue и продолжает её, а не получает отказ. Дочерний процесс ничего не спрашивает: его stdin — /dev/null, поэтому на вопрос о повторе берётся ответ по умолчанию (упавшие внутри пакеты не перезапускаются) |
bash | cmd (обязателен) | выполняет cmd системным шеллом (/bin/sh -c) в каталоге пакета |
ssh | host, cmd (оба обязательны) | выполняет cmd на хосте host через настроенную команду {ssh}; оба аргумента экранируются |
devenv | cmd (обязателен), profile | zoryn devenv [--profile <профиль>] -- bash -lc <cmd> — команда выполняется внутри окружения разработки, где bash действительно есть |
zoryn | args (обязателен, массив строк) | запускает zoryn с этими аргументами как есть, например args = ["check", "spec"] |
agent | cli, prompt | поднимает devenv с включённой фичей ИИ-агента и запускает его CLI в неинтерактивном режиме на самом свежем .gear/build.*.log (см. Починка ИИ-агентом). Отключён, пока не задан [agent] flow_steps = true |
Неизвестное значение run, пропущенный обязательный ключ, steps не в виде массива inline-таблиц или отсутствие самого массива steps — ошибка конфигурации; в сообщении указывается имя сценария, а где это применимо — и номер шага.
Общие ключи шага¶
| Ключ | Тип | Смысл |
|---|---|---|
name | строка | подпись, которую show/--dry-run печатают рядом с шагом, и имя шага в квалифицированных флагах (--<имя>.<ключ>) — дайте шагам одного типа разные имена, чтобы адресовать их по отдельности |
if | строка | условие-команда; шаг выполняется, только если она вернула 0. Пропущенный шаг ошибкой не считается |
on-failure | строка | имя восстановительного сценария, который запускается при падении шага перед его повтором |
max-retries | целое, по умолчанию 1 | сколько попыток «восстановление плюс повтор» отведено шагу. Без on-failure не имеет смысла |
Условие if выполняется тем же шеллом, что и шаг bash, поэтому в нём работают и шаблоны команд вида {git}, и переменные сценария: if = "test -f .gear/{pkg}.spec".
Опции шага¶
Шаги up, build, submit, task-* и agent принимают типизированные ключи-опции, которые ложатся на флаги соответствующей команды. Каждая опция необязательна; если её не задать, шаг ведёт себя ровно как голая команда без этого флага — включая чтение вашей конфигурации. Например, шаг up/build без ключа parallel соберёт пакет параллельно, если задано [builders] parallel = "on", как и zoryn up; шаг submit без commit/task-run публикует и запускает задание согласно вашей конфигурации [submit].
up и build (фаза сборки; no-build — только для up):
| Ключ | Тип | Действие |
|---|---|---|
builder | строка | собрать на указанном билдере (иначе настроенный/стандартный) |
arch | строка | фильтр по целевой архитектуре |
branch | строка | целевая ветка сборки |
rebuild | булев | использовать hsh-rebuild |
skip-check | булев | пропустить проверки — как голое zoryn build --skip-check (rpmbuild,all) |
parallel | булев | принудительно параллельно (true) или последовательно (false) для нескольких билдеров; без ключа берётся [builders] parallel |
no-python-auto-deps | булев | отключить авто-обновление Python-зависимостей и повтор сборки |
no-build | булев | только для up: обновить пакет (версия, spec, gear-тэги, up-хуки) и остановиться, не собирая; шаг всё равно даёт {old_version}/{new_version}, а сборку выполняет более поздний шаг build. На шаге build этот ключ — ошибка конфигурации. В командной строке отключается флагом --build, а не --no-no-build |
submit:
| Ключ | Тип | Действие |
|---|---|---|
branch | строка | целевой репозиторий (-B); без ключа его определяет конвейер |
task-run | булев | true запускает задание (--run), false форсирует --no-run; без ключа берётся [submit] run |
commit | булев | true форсирует настоящую отправку; без ключа — по [submit] (тест или публикация) |
test-only | булев | true форсирует тестовое задание |
no-deps | булев | отправить без зависимостей |
skip-check | булев | пропустить предотправочную проверку spec |
allow-overwrite-tag | булев | разрешить перезапись существующего тэга |
message | строка | сообщение коммита/тэга (без редактора) |
with | строка | добавить пакет в задание (--with) |
replace | строка | заменить подзадание в существующем задании (--replace) |
Одновременно заданные commit и test-only конфликтуют и роняют шаг — ровно так же, как zoryn submit отвергает такую пару.
task-add:
| Ключ | Тип | Действие |
|---|---|---|
task | строка | задание, в которое добавлять; по умолчанию {task} — задание, созданное шагом submit |
args | массив строк | действие как есть: пакеты, rebuild, ветка |
dependent-on | строка | --dependent-on PKG |
task-approve:
| Ключ | Тип | Действие |
|---|---|---|
task | строка | задание для подтверждения; по умолчанию {task} |
subtask | строка | номер, пакет, pkg.git=tag или all; по умолчанию all |
message | строка | комментарий -m |
task-run:
| Ключ | Тип | Действие |
|---|---|---|
task | строка | задание для запуска; по умолчанию {task} |
commit | булев | --commit — закоммитить задание в репозиторий при успехе |
message | строка | комментарий -m |
agent:
| Ключ | Тип | Действие |
|---|---|---|
cli | строка | какой ИИ CLI запустить (claude, codex или opencode); по умолчанию [agent] cli |
prompt | строка | заменяет встроенный промпт починки; поддерживает подстановки, и шаг с собственным промптом больше не требует наличия лога сборки |
Опции как флаги (неявно по имени)¶
Каждый ключ-опция шагов выбранного сценария доступен и как флаг командной строки у zoryn flow <ИМЯ> — так можно переопределить конфигурационное значение шага на один запуск, не правя сценарий:
- булев ключ
k→--k(ставитtrue) или--no-k(ставитfalse); - ключ со значением
k→--k ЗНАЧЕНИЕили--k=ЗНАЧЕНИЕ; - у
branchработает и привычная для zoryn короткая форма-B(-B sisyphus,p11).
Значение из CLI переопределяет конфигурационное значение для того шага (шагов), где ключ объявлен; если ключ не задан ни в CLI, ни в конфиге, действует «умолчание команды». --dry-run печатает каждый шаг с его итоговыми опциями, так что видно, что именно выполнится.
Если ключ объявлен только одним шагом, достаточно голого --k. Если его объявляют несколько шагов (например, skip-check есть и у up, и у submit), голая форма неоднозначна и отклоняется; уточните её как --<шаг>.<ключ>, где <шаг> — это name шага, если он задан, иначе его тип (up, submit, …). Уточнённая форма принимается всегда.
Неизвестный флаг выводит список опций, которые сценарий принимает; неоднозначный — перечисляет конкретные квалифицированные формы. Дополнение по TAB в bash предлагает флаги выбранного сценария и подставляет имена веток после -B/--branch.
zoryn flow release --commit # переопределить commit у шага submit
zoryn flow release --no-commit # на этот запуск сделать тестовую отправку
zoryn flow update --submit.skip-check # уточнить ключ, общий для двух шагов
zoryn flow update --builder arm-01 # направить up/build на конкретный билдер
Подстановки¶
Токены {pkg}, {old_version}, {new_version}, {tasks} и {task} подставляются в значения cmd, host, элементы args, prompt и if:
| Токен | Значение | Доступен |
|---|---|---|
{pkg} | имя каталога пакета (корень git-репозитория) | с первого шага |
{old_version} | версия до обновления | после успешного шага up |
{new_version} | версия после обновления | после успешного шага up |
{tasks} | номера всех созданных заданий gyle через запятую, в порядке отправки (например, 123,456 для отправки в несколько репозиториев) | после успешного шага submit |
{task} | номер первого созданного задания (совместимость для одного репозитория) | после успешного шага submit |
Предпочитайте {tasks}: он годится и для одного репозитория (просто 123), и для нескольких, а zoryn task run/add/approve принимают список через запятую. {task} используйте только когда осознанно нужен один первый номер.
Токен, значение которого ещё не выдал ни один предыдущий шаг, роняет шаг с сообщением flow variable '<имя>' is not available yet (produced by a later step) — это ошибка в конфигурации, а не случайность времени выполнения.
Все остальные токены {...} остаются нетронутыми и доходят до обычных шаблонов команд: {git}, {ssh} и прочие продолжают работать внутри cmd.
Подставленное значение попадает в /bin/sh, а часть значений приходит из репозитория пакета ({old_version} — это сырое поле Version:). Чтобы враждебный репозиторий не протащил через них синтаксис шелла, значение подставляется только если состоит исключительно из безопасного набора A-Za-z0-9.+~_,- (его хватает для любого настоящего имени пакета, версии, номера задания и списка {tasks} через запятую); всё прочее роняет шаг сообщением flow variable '<имя>' has an unsafe value …, не доходя до шелла.
Остальные ключи (branch, config, profile, cli, name) берутся буквально — подстановка в них не выполняется.
Ветвление, повторы и восстановление¶
Упавший шаг не обязан обрывать сценарий. Если у шага есть on-failure и остались попытки:
- восстановительный сценарий ищется в тех же слоях конфигурации и выполняется целиком;
- упавший шаг повторяется;
max-retriesзадаёт число таких попыток (по умолчанию1— одно восстановление и один повтор).
Восстановительный сценарий — обычный сценарий, у его шагов тоже может быть on-failure. Глубина вложенности ограничена пятью, поэтому сценарий, восстанавливающий сам себя и всегда падающий, всё равно завершится ошибкой, а не зациклится. Прогресс восстановительного сценария живёт только в памяти и в файл состояния не попадает.
Если восстановительный сценарий упал сам или его имя не определено, исходный шаг падает с объединённым сообщением (build failed; recovery flow 'fix-build' also failed: …).
Шаг up и отложенная сборка¶
Шаг up поднимает и коммитит версию до сборки. Как только коммит сделан, наивный повтор увидел бы, что пакет актуален, и остановил бы сценарий как «делать нечего», так и не собрав закоммиченное дерево и не дойдя до submit. zoryn закрывает это с обеих сторон. Поднятая версия и маркер отложенной сборки записываются в файл состояния до начала сборки, поэтому даже жёсткое прерывание во время сборки (Ctrl-C, kill, OOM, перезагрузка) оставляет состояние, с которого можно продолжить. А если сборка просто упала, тот же маркер сохраняется вместе с вычисленными версиями. В любом случае повтор — в том же процессе после восстановительного сценария или в отдельном zoryn flow --continue, пусть и в новом процессе — пересобирает уже поднятую версию, публикует {old_version} и {new_version} так же, как это сделал бы успешный up, и сценарий идёт дальше по шагам.
Шаг up, не нашедший новой версии, — не ошибка: сценарий на нём успешно завершается (код возврата 0). Для ночного запуска из cron это штатный исход.
Починка ИИ-агентом¶
Шаг agent поднимает devenv с фичей, названной в [agent] cli (по умолчанию claude, также codex или opencode), и запускает этот CLI в неинтерактивном режиме — claude -p, codex exec, opencode run — с промптом, который называет самый свежий .gear/build.*.log и отсылает CLI к сценарию починки сборки из поставляемого Agent Skill zoryn (references/repair.md: прочитать лог, поправить пакет, проверить через gear-rpm внутри devenv, оставить изменения незакоммиченными для следующих шагов сценария). Devenv монтирует каталог поставляемого скилла только на чтение по его хостовому пути, поэтому указатель, установленный zoryn agent skill install, внутри него разрешается. Если лога сборки нет, шаг падает; неподдерживаемое значение cli тоже роняет шаг.
Обе половины настраиваются на самом шаге: ключ cli переопределяет машинное значение [agent] cli, а ключ prompt целиком заменяет встроенный промпт починки — { run = "agent", prompt = "прогони тесты {pkg} и почини упавшие, затем выйди" }. Промпт шага шаблонизируется как cmd и самодостаточен, поэтому наличия лога сборки не требует; позаботиться о том, чтобы CLI завершился после работы, должен сам промпт — как это делает и встроенный.
Агент работает с вашими реальными учётными данными, смонтированными на запись
Devenv, в котором работает агент, монтирует ваши хостовые ~/.claude и ~/.claude.json (и аналоги для codex/opencode) на чтение и запись — без них CLI не пройдёт аутентификацию. Правило «меняй только этот репозиторий» живёт лишь в скилле; песочница его не навязывает, и агент, которому подсунули инъекцию через промпт (текст логов сборки подконтролен потенциальному злоумышленнику), может записать в эти файлы, например поставить хуки, срабатывающие в вашей следующей сессии. Поэтому шаги agent по умолчанию выключены: включите их через [agent] flow_steps = true в ~/.zoryn и только для сценариев и репозиториев, которым доверяете. Без этого шаг agent падает с сообщением-объяснением.
Вместе с on-failure и max-retries получается цикл «сборка сломалась — пусть агент попробует — собрать снова»:
[flows.update]
steps = [ { run = "up", on-failure = "fix-build", max-retries = 3 } ]
[flows.fix-build]
steps = [ { run = "agent" } ]
[agent]
cli = "claude"
flow_steps = true
Состояние и продолжение¶
Запущенный сценарий хранит своё положение в ~/.local/state/zoryn/flow-<проект>-state.json ($XDG_STATE_HOME учитывается). В состоянии лежат имя сценария, номер следующего шага, накопленные переменные и число использованных попыток текущего шага; оно записывается после каждого выполненного или пропущенного шага и удаляется, когда сценарий завершается — успешно или штатной остановкой.
- Упавший шаг оставляет состояние на месте и печатает, как продолжить. Исправьте причину — и
zoryn flow --continueповторит именно этот шаг с уже накопленными переменными. - Запустить сценарий, когда сохранено незавершённое состояние, нельзя: выберите
--continue,--restartили--abort. --continueи--restartбезNAMEработают с тем сценарием, который назван в состоянии, даже если конфигурация сейчас выбрала бы другой. СNAME, не совпадающим с сохранённым сценарием, оба отклоняются.- Файл состояния именуется по имени каталога пакета, поэтому две копии одного пакета (например, клоны
sisyphusиp11, оба в каталогеfoo) делили бы его. В состоянии записан путь его копии, и--continue/--restartиз другой копии отклоняется, а не продолжает не то дерево. - Для
--abortдостаточно самого факта существования файла, поэтому он спасает и состояние, повреждённое настолько, что его не удаётся прочитать; сообщение о повреждённом состоянии в любой команде называет--abortкак выход. --dry-runпечатает план раньше всего этого: незавершённый сценарий ему не мешает, а--restartего в этом режиме не стирает.
Сценарий с шагом up или build бесполезен без рабочего билдера, поэтому билдеры разрешаются, а их точки монтирования проверяются до первого шага — неверно настроенный билдер обрывает сценарий сразу, а не на середине.
Коды возврата¶
| Код | Значение |
|---|---|
0 | сценарий завершён либо шаг up не нашёл новой версии |
1 | шаг упал (состояние сохранено) либо сценарий не удалось запустить: неизвестное имя, сценарий не выбран, конфигурация не разбирается, несовместимые флаги, нет подходящего билдера |
Связанное¶
zoryn up— конвейер обновления, который выполняет шагupzoryn submit— что делает шагsubmitzoryn devenv— окружение для шаговdevenvиagent- Конфигурация —
[flows],[agent]и.gear/version-up