Миграция с v1
Для workflow, который шлёт в один чат, jtprogru/notiflow@v1 → jtprogru/notiflow@v2 —
замена без правок. Все входы сохранили имена и смысл, все выходы сохранили имена, exit-коды
10–16 значат то же, что значили.
Ломающее изменение ровно одно, плюс набор починок, которые меняют поведение только там, где оно и так было сломано.
Ломающее: multi-chat fan-out убран
Заголовок раздела «Ломающее: multi-chat fan-out убран»В v1.5.0 chat_id мог содержать список через запятую, а edit_message_id — парный список.
В v2 отправка идёт ровно в один чат.
| v1 | v2 | |
|---|---|---|
chat_id |
одно значение или CSV | одно значение; запятая → exit 11 |
edit_message_id |
целое или CSV, длина обязана совпадать с chat_id |
одно целое |
выход message_id |
CSV в порядке входа, пустой слот на упавший чат (42,,103) |
один id |
выход error |
chat <id>: <причина>, склеенные через ; |
сырая причина |
выход http_status |
200 либо первый не-200 | статус единственного запроса |
| exit 16 | не совпало число элементов CSV | значение не положительное целое |
Агрегированные выходы теряли ровно то, что важно. message_id=42,,103 говорил, что что-то
упало, но не что именно; http_status схлопывал несколько результатов в одно число; шаг
показывал одну галочку или один крестик на то, что на деле было N независимых отправок.
Восстанавливать эту точность внутри одного шага — значит переизобретать матрицу, которая
уже делает это правильно.
Что делать вместо
Заголовок раздела «Что делать вместо»Матрица, если чаты известны заранее:
jobs: notify: needs: [build] if: always() strategy: fail-fast: false matrix: chat: ['-1001111111111', '-1002222222222'] runs-on: ubuntu-latest steps: - uses: jtprogru/notiflow@v2 with: bot_token: ${{ secrets.TELEGRAM_BOT_TOKEN }} chat_id: ${{ matrix.chat }} status: ${{ needs.build.result }}Или несколько шагов подряд, если неизвестны:
- uses: jtprogru/notiflow@v2 id: team with: bot_token: ${{ secrets.TELEGRAM_BOT_TOKEN }} chat_id: '-1001111111111' status: ${{ job.status }}
- uses: jtprogru/notiflow@v2 id: oncall if: always() with: bot_token: ${{ secrets.TELEGRAM_BOT_TOKEN }} chat_id: '-1002222222222' status: ${{ job.status }}Оба варианта дают выходы и статус на каждый чат отдельно.
Если ты сочетал edit_message_id с CSV в chat_id, теперь у каждой ветки матрицы свой
message_id — что для редактирования и требовалось: id всё равно никогда не были
взаимозаменяемы между чатами.
Multi-chat fan-out оформлен как feature request, а не просто вычеркнут. Если ты на него опирался — это место, где об этом стоит сказать: issue заведён ровно затем, чтобы собрать случаи, которые матрица не покрывает.
Починки, меняющие наблюдаемое поведение
Заголовок раздела «Починки, меняющие наблюдаемое поведение»Каждая записана в parity-корпус с выводами v1 и v2 рядом, так что разница задокументирована, а не обнаруживается на проде.
Плейсхолдеры подставляются за один проход
Заголовок раздела «Плейсхолдеры подставляются за один проход»v1 подставлял ключ за ключом в цикле, поэтому значение, подставленное раньше, само
просматривалось на плейсхолдеры каждой следующей итерацией. Workflow с именем {{.Actor}}
действительно разворачивался в имя актора при parse_mode: none и HTML. При MarkdownV2
экранирование случайно это прятало. v2 читает шаблон ровно один раз.
Обрезка идёт по границам разметки
Заголовок раздела «Обрезка идёт по границам разметки»Сообщения длиннее 4096 UTF-16 единиц обрезаются. v1 резал по сырой границе code unit и мог
разорвать escape-пару MarkdownV2 (\ + .), HTML-сущность (&) или тег — Telegram
отвечал 400 на сообщение, вся вина которого была в длине. v2 режет между токенами разметки,
а в HTML-режиме ещё и закрывает теги, оставшиеся открытыми.
У того же кода был второй отказ. iconv выходит с ненулевым кодом на неполном хвостовом
символе даже с -c — BSD и GNU одинаково, — поэтому любое сообщение сверх лимита, у
которого обрез пришёлся внутрь суррогатной пары, роняло отправку целиком на всех
платформах. В v2 никакого iconv нет.
Токен вычищается из всех потоков
Заголовок раздела «Токен вычищается из всех потоков»::add-mask:: закрывает лог workflow, но в CLI такого механизма нет, а ошибка HTTP может
нести URL с bot<TOKEN>. v2 фильтрует всё, что печатает, и переписывает любой оставшийся
сегмент пути /bot<token>/ — даже для токена, о котором ему не сообщали.
--bot-token в аргументах предупреждает
Заголовок раздела «--bot-token в аргументах предупреждает»Аргументы видны любому процессу на машине. Флаг работает, но печатает предупреждение, а
документация указывает на NOTIFLOW_BOT_TOKEN или конфиг с правами 0600.
В backoff появился джиттер, Retry-After учитывается
Заголовок раздела «В backoff появился джиттер, Retry-After учитывается»v1 спал ровно 1, 2 и 4 секунды, поэтому флот job’ов, упёршихся в лимит одновременно,
ретраил синхронно и снова вызывал лимит. v2 добавляет до 50% джиттера. Ещё он читает
заголовок Retry-After, когда в теле нет parameters.retry_after, — именно так отвечают
прокси и self-hosted Bot API.
disable_web_page_preview переехал на проводе
Заголовок раздела «disable_web_page_preview переехал на проводе»Имя входа сохранено; в запросе теперь link_preview_options.is_disabled, потому что
плоское поле Telegram задепрекейтил.
Политика api_base зависит от режима
Заголовок раздела «Политика api_base зависит от режима»Внутри Actions allowlist прежний: api.telegram.org плюс loopback, всё остальное
игнорируется с предупреждением. В CLI --api-base принимает любой http(s) URL, потому что
self-hosted Bot API — легитимный сценарий, и это явный выбор пользователя.
Что не изменилось
Заголовок раздела «Что не изменилось»- Имена и дефолты всех входов, кроме семантики
chat_idиedit_message_idвыше. - Имена всех выходов.
- Приоритет шаблонов:
message>template_<status>>message_template> встроенный. messageпо-прежнему дословный: без подстановки и без экранирования.- Набор плейсхолдеров, шаблон по умолчанию и правила экранирования каждого parse mode.
- Exit-коды 10–16. Коды 20 (
UNSUPPORTED_BASH) и 22 (MISSING_DEPENDENCY) принадлежали bash-рантайму; v2 их не выдаёт, и никто другой эти номера не займёт. - Семантика
notify_on, включая сокращенияanyиall.
Что нового в v2
Заголовок раздела «Что нового в v2»- CLI:
notiflow send | edit | render | whoami | completions, ставится из Homebrew, crates.io или релизного архива. - Поддержка Windows-раннеров.
verify_signature— проверка cosign при скачивании бинаря.- Exit-коды 17 (
CONFIG_ERROR), 18 (INVALID_ARGUMENT) и 30 (IO_ERROR) для сценариев, до которых добирается CLI и почти не добирается Action.
Остаться на v1
Заголовок раздела «Остаться на v1»Тег v1 продолжает указывать на bash-реализацию в ветке v1.x, которая получает только
security-фиксы. Bash-исходники живут и в дереве v2, в tests/parity/v1/, где служат
эталоном, с которым parity-набор сравнивается на каждом коммите.