Сценарий · GitHub

Обработка вебхуков GitHub на serverless для CI/CD и автоматизации

Вебхуки GitHub срабатывают на push, pull_request, release, issues и другие события. Соберите serverless-обработчик, который проверяет HMAC-SHA256, маршрутизирует события по типу и запускает асинхронные пайплайны для CI/CD, индексации репозитория и автоматизации issues.

Обновлено: 2026-06-28

Суть ответа

Обработка вебхуков GitHub на serverless для CI/CD и автоматизации. Одна функция проверяет подпись и направляет события по типу в отдельные задачи пайплайна. События push запускают индексацию, pull_request — автоматизацию ревью, release — пайплайны деплоя.

Когда подходит

  • Триггеры CI/CD на события push и PR
  • Индексация репозитория, синхронизация документации и обновление поискового индекса по push
  • Автоматическая разметка issues, запросы ревью PR и деплой релизов

На что обратить внимание

  • Если индексация репозитория, расчёт эмбеддингов или запуск CI-задач выполняются синхронно до ответа, любая задержка в этих системах заставит GitHub повторить доставку на ваш эндпоинт.
  • Обработка всех типов событий в одном обработчике создаёт монолит, который сложно тестировать, сложно деплоить и легко сломать.

Где ломается обработка вебхуков GitHub

  • HMAC-SHA256 с timing-safe сравнением — стандарт, в котором легко ошибиться
  • Десятки типов событий в одном эндпоинте — логика маршрутизации быстро разрастается
  • Тяжёлая работа (индексация, запуск CI) в окне вебхука приводит к таймаутам доставки GitHub

GitHub повторяет неудачные доставки до 3 дней. Без быстрого ACK и идемпотентной обработки одна и та же CI-задача может запуститься, а один и тот же коммит — проиндексироваться несколько раз.

Почему обработка событий GitHub внутри запроса хрупка

Если индексация репозитория, расчёт эмбеддингов или запуск CI-задач выполняются синхронно до ответа, любая задержка в этих системах заставит GitHub повторить доставку на ваш эндпоинт.

Обработка всех типов событий в одном обработчике создаёт монолит, который сложно тестировать, сложно деплоить и легко сломать.

Проверить, направить, разветвить: паттерн вебхука GitHub

Одна функция проверяет подпись и направляет события по типу в отдельные задачи пайплайна. События push запускают индексацию, pull_request — автоматизацию ревью, release — пайплайны деплоя.

Что нужно для обработки вебхуков GitHub

Timing-safe проверка HMAC-SHA256

Используйте crypto.timingSafeEqual — никогда не сравнение строк, — чтобы исключить атаки по времени на проверку подписи.

Маршрутизация по типу события

Заголовок x-github-event определяет событие. Направляйте разные типы событий в разные функции пайплайна.

Дедупликация по ID доставки

Используйте заголовок x-github-delivery как ключ идемпотентности, чтобы при повторной доставке пропускать уже обработанные события.

Асинхронное разветвление

Одно событие push может запустить параллельные пайплайны: проиндексировать документацию, прогнать линтер, обновить поиск, уведомить Slack.

Поток обработки вебхука GitHub

1

Проверить HMAC-SHA256 timing-safe

Извлеките x-hub-signature-256, посчитайте ожидаемый HMAC, сравните через timingSafeEqual.

2

Направить по типу события, вернуть 200

Сделайте switch по заголовку x-github-event. Запустите нужный пайплайн. Ответьте быстро — не дожидаясь завершения пайплайнов.

3

Обработать в пайплайнах

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

Обработчик вебхуков GitHub для push и PR

Проверить HMAC, направить по типу события, запустить асинхронные пайплайны — всё до ответа 200 для GitHub.

webhooks/github.mjs
import { createHmac, timingSafeEqual } from 'node:crypto';

export async function handler(event) {
  const body = event.body ?? '';
  const sigHeader = (event.headers['x-hub-signature-256'] ?? '').replace('sha256=', '');
  const expected = createHmac('sha256', process.env.GITHUB_WEBHOOK_SECRET).update(body).digest('hex');
  if (sigHeader.length !== expected.length ||
      !timingSafeEqual(Buffer.from(sigHeader, 'hex'), Buffer.from(expected, 'hex'))) {
    return { statusCode: 401, body: 'invalid signature' };
  }
  const eventType = event.headers['x-github-event'];
  const deliveryId = event.headers['x-github-delivery'];
  const payload = JSON.parse(body);
  const isNew = await db.webhookDeliveries.upsert(deliveryId);
  if (!isNew) return { statusCode: 200, body: 'duplicate' };
  if (eventType === 'push') {
    await global.durable.startNew('index-repo', undefined, { repo: payload.repository.full_name, sha: payload.after });
  } else if (eventType === 'pull_request' && payload.action === 'opened') {
    await global.durable.startNew('review-pr', undefined, { repo: payload.repository.full_name, pr: payload.number });
  } else if (eventType === 'release' && payload.action === 'published') {
    await global.durable.startNew('deploy-release', undefined, { repo: payload.repository.full_name, tag: payload.release.tag_name });
  }
  return { statusCode: 200, body: 'accepted' };
}

Используйте для автоматизации GitHub

Когда это уместно

  • Триггеры CI/CD на события push и PR
  • Индексация репозитория, синхронизация документации и обновление поискового индекса по push
  • Автоматическая разметка issues, запросы ревью PR и деплой релизов

Когда лучше выбрать другое

  • Простые воркфлоу GitHub Actions, которым не нужна внешняя serverless-обработка

Частые вопросы

Как тестировать локально?

Используйте gh webhook forward --repo owner/repo --events push --url http://localhost:PORT, чтобы пересылать реальные события в локальную функцию.

А события ревью pull_request?

GitHub присылает много под-действий (submitted, dismissed, edited). Всегда проверяйте payload.action, чтобы направить событие правильно.