Webhook сокращает задержку между событием и автоматизацией, но не превращает внешнюю систему в надёжную очередь команд. Получатель может увидеть повторную доставку, получить события не в том порядке или остаться без уведомления. Поэтому production-интеграция должна воспринимать webhook как сигнал: проверить источник, зафиксировать событие, сделать обработку идемпотентной и сверить итог с системой-источником.

В декабре 2025 года Launchpad добавил webhooks для загрузок пакетов в Personal Package Archives (PPA). Они сообщают об успешных и неуспешных загрузках исходных и бинарных пакетов, а также об изменениях статуса сборки. На этом примере удобно разобрать практику, которая подходит любому CI/CD-контуру: от публикации артефакта до уведомления команды и запуска следующего этапа pipeline.

Подписывайтесь на событие, а не на весь поток

Первая защита от лишней работы — узкая подписка. Для PPA текущая документация Launchpad описывает три типа событий:

  • archive:source-package-upload:0.1 — изменение статуса загрузки исходного пакета;
  • archive:binary-package-upload:0.1 — изменение статуса загрузки бинарного пакета;
  • archive:binary-build:0.1 — изменение статуса бинарной сборки.

Не стоит отправлять все три типа в один универсальный обработчик, если бизнес-действие связано только с публикацией исходного пакета. Чем шире подписка, тем больше ветвлений, ложных оповещений и случайных side effects. Сначала сформулируйте действие: например, «после Accepted обновить внутренний каталог» или «после Failed to build создать сигнал для дежурного». Затем выберите минимальный набор событий, который действительно нужен.

Тип включает версию схемы. Launchpad обещает повысить её при несовместимом изменении payload, но может добавлять новые поля в словари без смены версии. Следовательно, consumer должен проверять известную версию и обязательные поля, но спокойно игнорировать незнакомые дополнительные поля. Строгий запрет любого нового ключа превратит совместимое расширение API в аварийную остановку интеграции.

Проверяйте подпись до разбора JSON

Для webhook можно настроить secret. Тогда Launchpad передаёт в X-Hub-Signature HMAC-SHA1 от исходного тела запроса. Проверять нужно именно полученные байты, а не JSON, который приложение уже разобрало и собрало заново: пробелы, порядок ключей и кодирование способны изменить представление без изменения данных.

Безопасный входной контур выглядит так:

  1. принять тело как массив байтов и ограничить допустимый размер запроса;
  2. потребовать заголовки Content-Type, X-Launchpad-Event-Type, X-Launchpad-Delivery и подпись;
  3. вычислить HMAC по исходному телу и сравнить подписи библиотечной функцией постоянного времени;
  4. отклонить запрос при отсутствии или несовпадении подписи;
  5. только после этого разобрать JSON и проверить схему события.

Secret не должен попадать в логи, трассировки и сообщения об ошибках. Allowlist адресов webhooks-proxy.launchpad.net, которую рекомендует Launchpad для firewall, полезна как сетевой фильтр, но не заменяет криптографическую проверку: адрес источника и подлинность сообщения решают разные задачи.

Отделите приём от выполнения

Webhook endpoint не должен в одном HTTP-запросе обновлять каталог, отправлять сообщение, запускать deployment и ждать завершения каждого шага. Чем длиннее синхронная цепочка, тем больше неоднозначных исходов: внешняя система могла не дождаться ответа, хотя половина действий уже произошла.

Надёжнее разделить путь на две части. Приёмник проверяет подпись и схему, сохраняет событие вместе с ключевыми заголовками, после чего передаёт ссылку на запись внутреннему worker. Worker выполняет предметное действие отдельно и пишет результат. Так входной endpoint остаётся простым, а повторные попытки, лимиты и наблюдаемость принадлежат вашей системе, а не времени жизни внешнего HTTP-запроса.

Хранить весь payload бесконечно необязательно. Срок и состав данных зависят от требований аудита и приватности. Но для расследования нужны хотя бы delivery ID, тип и версия события, время приёма, проверенный digest тела, состояние обработки и ссылка на результат.

Delivery ID должен стать ключом идемпотентности

Каждая доставка Launchpad содержит уникальный X-Launchpad-Delivery. Используйте его как внешний idempotency key и создайте уникальное ограничение в хранилище входящих событий. Если тот же идентификатор приходит снова, endpoint возвращает успешный результат для уже принятого события, но не запускает side effect повторно.

Одного флага processed=true недостаточно. Возможен сбой после публикации артефакта, но до сохранения флага. Предметное действие тоже должно иметь стабильный ключ: например, сочетание archive, package name, version и целевого шага. Перед повтором worker проверяет фактическое состояние назначения. Если результат неизвестен, сначала выполняется reconciliation, а не новый вызов публикации.

Так webhook не сможет дважды создать релиз или разослать два взаимоисключающих уведомления даже после рестарта worker.

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

Launchpad прямо предупреждает: события обычно приходят примерно в порядке возникновения, но полагаться на это нельзя. При необходимости их можно упорядочить по числовому X-Launchpad-Delivery. Однако сортировка на входе не решает ситуацию, когда более раннее событие задержалось и появилось после обработки нового.

Проектируйте consumer вокруг состояния, а не последовательности сообщений. Событие status-changed означает «проверь, что произошло», а не «безусловно замени локальный статус». Перед необратимым действием загрузите актуальное состояние объекта через API Launchpad. Старое уведомление тогда не откатит уже завершённую сборку в промежуточный статус.

Для каждого типа события задайте допустимые переходы. У загрузки пакета документация перечисляет Accepted, Rejected и Unapproved; у бинарной сборки — собственный набор терминальных и промежуточных состояний. Не сводите их к одному полю success: явная таблица переходов лучше показывает, где нужна публикация, где ожидание, а где вмешательство инженера.

Сначала ping и теневой режим, затем side effects

Launchpad умеет отправлять тестовое событие ping с телом {"ping": true}. Оно появляется среди недавних доставок и позволяет проверить доступность endpoint и валидацию подписи. Это минимальный smoke test, но не доказательство готовности всей цепочки.

Перед включением действий проведите теневой этап: принимайте реальные события, проверяйте подпись и схему, сохраняйте решение worker, но ничего не меняйте во внешних системах. Сравните ожидаемые решения с фактическими статусами пакетов. Затем разрешите один обратимый side effect, например внутреннее уведомление, и только после наблюдения подключайте публикацию или deployment.

В runbook зафиксируйте, где смотреть недавние доставки. Launchpad показывает их в UI и предоставляет коллекцию webhook.deliveries через API. Эта точка нужна дежурному, чтобы отличить три класса сбоя: событие не сформировано, доставка не дошла или ваш consumer её отклонил.

Webhook не отменяет периодическую сверку

В актуальной документации Launchpad описаны идентификатор, подпись, события и просмотр доставок, но нет контракта, на который можно было бы опереться как на гарантированную очередь с заданным числом повторов. Поэтому критический процесс не должен существовать только в webhook-обработчике.

Добавьте периодический reconciliation: запросите из Launchpad актуальные загрузки и сборки за выбранный интервал, сопоставьте их с журналом consumer и поставьте пропущенные действия в очередь. Частота сверки зависит от допустимой задержки конкретного процесса; универсальное число здесь было бы выдумкой. Важно другое: webhook обеспечивает быстрый путь, а reconciliation — полноту.

Надёжная интеграция строится не вокруг удачного POST-запроса. Её составляют узкая подписка, подпись исходного тела, журнал приёма, idempotency key, обработка вне HTTP-запроса, проверка текущего состояния и периодическая сверка. Тогда событие ускоряет CI/CD, но не становится единственной точкой истины.

Источник и лицензия

  • Автор: Ines Almeida (ines-almeida).
  • Организация и корпоративный блог: Launchpad Blog, Canonical Ltd.
  • Исходная статья: Introducing Webhooks for Package Uploads in PPAs, 1 декабря 2025 года.
  • Лицензия исходного материала: Creative Commons Attribution 2.0 UK: England & Wales.
  • Это самостоятельная русская адаптация: исходный материал переведён, сокращён и редакционно переработан для DevOps/SRE/Platform-аудитории. Поведение событий, заголовков, подписи, тестов и доставок сверено с актуальным руководством Launchpad по webhooks, обновлённым 3 июля 2026 года. Добавлены архитектура приёмника, идемпотентность, reconciliation и эксплуатационные ограничения. Canonical и автор исходной статьи не одобряли эту публикацию и не связаны с ней.