Pular para o conteúdo principal

Documentação oficial desta página · v0.20.6

Comentários Automáticos em PR via Webhook

Este guia mostra como conectar o Hermes Agent ao GitHub para que ele busque sozinho o diff de um pull request, analise as mudanças de código e publique um comentário — disparado por um evento de webhook, sem você pedir nada na hora.

Quando um PR é aberto ou atualizado, o GitHub manda um POST de webhook para a sua instância do Hermes. O Hermes roda o agente com um prompt que manda buscar o diff pela CLI gh, e a resposta é publicada de volta na conversa do PR.

Quer uma configuração mais simples, sem endpoint público?

Se você não tem uma URL pública ou só quer começar rápido, veja Monte um agente de revisão de PR no GitHub — usa tarefas de cron para consultar os PRs de tempos em tempos, e funciona atrás de NAT e firewall.

Documentação de referência

Para a referência completa da plataforma de webhook (todas as opções de configuração, tipos de entrega, assinaturas dinâmicas, modelo de segurança), veja Webhooks.

Risco de injeção de prompt

Payloads de webhook trazem dados controlados por terceiros — títulos de PR, mensagens de commit e descrições podem conter instruções maliciosas. Quando o seu endpoint de webhook está exposto à internet, rode o gateway em um ambiente isolado (Docker, backend SSH). Veja a seção de segurança abaixo.


Pré-requisitos​

  • Hermes Agent instalado e rodando (hermes gateway)
  • CLI gh instalada e autenticada na máquina do gateway (gh auth login)
  • Uma URL publicamente acessível para a sua instância do Hermes (veja Teste local com ngrok se estiver rodando localmente)
  • Acesso de administrador ao repositório no GitHub (necessário para gerenciar webhooks)

Passo 1 — Habilite a plataforma de webhook​

Adicione o seguinte ao seu ~/.hermes/config.yaml:

platforms:
webhook:
enabled: true
extra:
port: 8644 # padrão; troque se outro serviço já ocupa esta porta
rate_limit: 30 # máximo de requisições por minuto por rota (não é um teto global)

routes:
github-pr-review:
secret: "seu-segredo-de-webhook-aqui" # tem que bater exatamente com o segredo do webhook no GitHub
events:
- pull_request

# O agente é instruído a buscar o diff de verdade antes de revisar.
# {number} e {repository.full_name} são resolvidos a partir do payload do GitHub.
prompt: |
Chegou um evento de pull request (ação: {action}).

PR #{number}: {pull_request.title}
Autor: {pull_request.user.login}
Branch: {pull_request.head.ref} → {pull_request.base.ref}
Descrição: {pull_request.body}
URL: {pull_request.html_url}

Se a ação for "closed" ou "labeled", pare aqui e não publique comentário.

Caso contrário:
1. Rode: gh pr diff {number} --repo {repository.full_name}
2. Revise as mudanças de código quanto a correção, problemas de segurança e clareza.
3. Escreva um comentário de revisão curto e acionável e publique.

deliver: github_comment
deliver_extra:
repo: "{repository.full_name}"
pr_number: "{number}"

Campos principais:

CampoDescrição
secret (por rota)Segredo HMAC desta rota. Se omitido, cai para o extra.secret global.
eventsLista de valores do cabeçalho X-GitHub-Event a aceitar. Lista vazia = aceita todos.
promptTemplate; {field} e {nested.field} são resolvidos a partir do payload do GitHub.
delivergithub_comment publica via gh pr comment. log só escreve no log do gateway.
deliver_extra.repoResolve para algo como org/repo a partir do payload.
deliver_extra.pr_numberResolve para o número do PR a partir do payload.
O payload não traz código

O payload do webhook do GitHub inclui os metadados do PR (título, descrição, nomes de branch, URLs) mas não o diff. O prompt acima manda o agente rodar gh pr diff para buscar as mudanças de verdade. O conjunto de ferramentas padrão hermes-webhook é deliberadamente restrito (busca/extração na web, visão, esclarecimento — sem terminal), porque payloads de webhook podem carregar conteúdo não confiável. Para deixar esta rota rodar o gh, adicione uma concessão de conjunto de ferramentas por rota: toolsets: ["terminal", "web"] na configuração da rota — veja Webhooks.


Passo 2 — Suba o gateway​

hermes gateway

Você deve ver:

[webhook] Listening on 0.0.0.0:8644 — routes: github-pr-review

Confirme que está no ar:

curl http://localhost:8644/health
# {"status": "ok", "platform": "webhook"}

Passo 3 — Cadastre o webhook no GitHub​

  1. Vá ao seu repositório → Settings → Webhooks → Add webhook
  2. Preencha:
    • Payload URL: https://sua-url-publica.exemplo.com/webhooks/github-pr-review
    • Content type: application/json
    • Secret: o mesmo valor que você colocou em secret na configuração da rota
    • Which events? → Select individual events → marque Pull requests
  3. Clique em Add webhook

O GitHub manda na hora um evento ping para confirmar a conexão. Ele é ignorado com segurança — ping não está na sua lista de events — e devolve {"status": "ignored", "event": "ping"}. Ele só é registrado em nível DEBUG, então não aparece no console no nível de log padrão.


Passo 4 — Abra um PR de teste​

Crie uma branch, publique uma mudança e abra um PR. Em 30–90 segundos (dependendo do tamanho do PR e do modelo), o Hermes deve publicar um comentário de revisão.

Para acompanhar o progresso do agente em tempo real:

tail -f "${HERMES_HOME:-$HOME/.hermes}/logs/gateway.log"

Teste local com ngrok​

Se o Hermes está rodando no seu notebook, use o ngrok para expor ele:

ngrok http 8644

Copie a URL https://...ngrok-free.app e use como Payload URL no GitHub. No plano gratuito do ngrok a URL muda a cada reinício do ngrok — atualize o webhook do GitHub a cada sessão. Contas pagas do ngrok ganham um domínio fixo.

Você pode fazer um teste de fumaça direto em uma rota estática com curl — sem conta no GitHub e sem PR de verdade.

Use deliver: log ao testar localmente

Troque deliver: github_comment por deliver: log na sua configuração enquanto testa. Senão o agente vai tentar publicar um comentário no repositório fictício org/repo#99 do payload de teste, e vai falhar. Volte para deliver: github_comment quando estiver satisfeito com a saída do prompt.

SECRET="seu-segredo-de-webhook-aqui"
BODY='{"action":"opened","number":99,"pull_request":{"title":"Test PR","body":"Adds a feature.","user":{"login":"testuser"},"head":{"ref":"feat/x"},"base":{"ref":"main"},"html_url":"https://github.com/org/repo/pull/99"},"repository":{"full_name":"org/repo"}}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print "sha256="$2}')

curl -s -X POST http://localhost:8644/webhooks/github-pr-review \
-H "Content-Type: application/json" \
-H "X-GitHub-Event: pull_request" \
-H "X-Hub-Signature-256: $SIG" \
-d "$BODY"
# Esperado: {"status":"accepted","route":"github-pr-review","event":"pull_request","delivery_id":"..."}

Depois acompanhe o agente rodando:

tail -f "${HERMES_HOME:-$HOME/.hermes}/logs/gateway.log"
observação

O hermes webhook test <name> só funciona para assinaturas dinâmicas criadas com hermes webhook subscribe. Ele não lê rotas do config.yaml.


Filtrar por ações específicas​

O GitHub manda eventos pull_request para várias ações: opened, synchronize, reopened, closed, labeled, etc. A lista events filtra pelo valor do cabeçalho X-GitHub-Event, e os filters por rota podem restringir por campos do payload, como o action.

O prompt do Passo 1 já cuida disso mandando o agente parar cedo nos eventos closed e labeled.

O agente ainda roda e consome tokens

A instrução de "pare aqui" evita uma revisão de verdade, mas o agente ainda roda até o fim em todo evento pull_request, seja qual for a ação. Prefira filtrar antes de acordar o agente:

filters:
- field: "action"
in: ["opened", "synchronize", "reopened"]

Em repositórios de volume alto, dá para filtrar mais acima ainda, com um workflow do GitHub Actions que chama a URL do seu webhook condicionalmente.

Não existe Jinja2 nem sintaxe condicional de template. {field} e {nested.field} são as únicas substituições suportadas. Qualquer outra coisa é repassada literalmente ao agente.


Usar uma skill para manter o estilo de revisão​

Carregue uma skill do Hermes para dar ao agente uma persona de revisão consistente. Acrescente skills à sua rota, dentro de platforms.webhook.extra.routes no config.yaml:

platforms:
webhook:
enabled: true
extra:
routes:
github-pr-review:
secret: "seu-segredo-de-webhook-aqui"
events: [pull_request]
prompt: |
Chegou um evento de pull request (ação: {action}).
PR #{number}: {pull_request.title} por {pull_request.user.login}
URL: {pull_request.html_url}

Se a ação for "closed" ou "labeled", pare aqui e não publique comentário.

Caso contrário:
1. Rode: gh pr diff {number} --repo {repository.full_name}
2. Revise o diff seguindo as suas diretrizes de revisão.
3. Escreva um comentário de revisão curto e acionável e publique.
skills:
- review
deliver: github_comment
deliver_extra:
repo: "{repository.full_name}"
pr_number: "{number}"

Nota: só a primeira skill da lista que for encontrada é carregada. O Hermes não empilha várias skills — as entradas seguintes são ignoradas.


Mandar as respostas para o Slack ou o Discord​

Troque os campos deliver e deliver_extra dentro da sua rota pela plataforma de destino:

# Dentro de platforms.webhook.extra.routes.<nome-da-rota>:

# Slack
deliver: slack
deliver_extra:
chat_id: "C0123456789" # ID do canal no Slack (omita para usar o canal padrão configurado)

# Discord
deliver: discord
deliver_extra:
chat_id: "987654321012345678" # ID do canal no Discord (omita para usar o canal padrão)

A plataforma de destino também precisa estar habilitada e conectada no gateway. Se o chat_id for omitido, a resposta vai para o canal padrão configurado daquela plataforma.

Valores válidos de deliver: log · github_comment · telegram · discord · slack · signal · sms


Suporte a GitLab​

O mesmo adaptador funciona com o GitLab. O GitLab usa X-Gitlab-Token para autenticação (comparação literal de string, não HMAC) — o Hermes lida com os dois automaticamente.

Para filtrar eventos, o GitLab define o X-GitLab-Event com valores como Merge Request Hook, Push Hook, Pipeline Hook. Use o valor exato do cabeçalho em events:

events:
- Merge Request Hook

Os campos do payload do GitLab são diferentes dos do GitHub — por exemplo, {object_attributes.title} para o título do MR e {object_attributes.iid} para o número do MR. O jeito mais fácil de descobrir a estrutura completa do payload é o botão Test nas configurações do webhook no GitLab, combinado com o log de Recent Deliveries. Como alternativa, omita o prompt da configuração da rota — aí o Hermes passa o payload inteiro como JSON formatado direto para o agente, e a resposta dele (visível no log do gateway com deliver: log) descreve a estrutura.


Notas de segurança​

  • Nunca use INSECURE_NO_AUTH em produção — ele desliga a validação de assinatura por completo. É só para desenvolvimento local.
  • Rotacione o segredo do webhook de tempos em tempos e atualize nos dois lados: no GitHub (configurações do webhook) e no seu config.yaml.
  • O limite de taxa é de 30 req/min por rota por padrão (configurável em extra.rate_limit). Passar disso devolve 429.
  • Entregas duplicadas (retentativas do webhook) são deduplicadas por um cache de idempotência de 1 hora. A chave do cache é o X-GitHub-Delivery, se houver, depois o X-Request-ID, depois um timestamp em milissegundos. Quando nenhum dos cabeçalhos de ID de entrega vem, as retentativas não são deduplicadas.
  • Injeção de prompt: títulos, descrições e mensagens de commit de PR são controlados por terceiros. PRs maliciosos podem tentar manipular as ações do agente. Rode o gateway em um ambiente isolado (Docker, VM) quando ele estiver exposto à internet pública.

Solução de problemas​

SintomaO que conferir
401 Invalid signatureO segredo no config.yaml não bate com o segredo do webhook no GitHub
404 Unknown routeO nome da rota na URL não bate com a chave em routes:
429 Rate limit exceededPassou de 30 req/min na rota — comum ao reenviar eventos de teste pela interface do GitHub; espere um minuto ou aumente o extra.rate_limit
Nenhum comentário publicadoO gh não está instalado, não está no PATH, ou não está autenticado (gh auth login)
O agente roda mas não sai comentárioVeja o log do gateway — se a saída do agente veio vazia ou só com "SKIP", a entrega mesmo assim é tentada
Porta já em usoTroque o extra.port no config.yaml
O agente roda mas revisa só a descrição do PRO prompt não está incluindo a instrução do gh pr diff — o diff não vem no payload do webhook
Não consigo ver o evento de pingEventos ignorados devolvem {"status":"ignored","event":"ping"} só em nível de log DEBUG — confira o log de entregas do GitHub (repositório → Settings → Webhooks → seu webhook → Recent Deliveries)

A aba Recent Deliveries do GitHub (repositório → Settings → Webhooks → seu webhook) mostra os cabeçalhos exatos da requisição, o payload, o status HTTP e o corpo da resposta de cada entrega. É o jeito mais rápido de diagnosticar falhas sem mexer nos logs do seu servidor.


Referência completa de configuração​

platforms:
webhook:
enabled: true
extra:
port: 8644 # porta de escuta (padrão: 8644)
secret: "" # segredo global opcional, usado como reserva
rate_limit: 30 # requisições por minuto por rota
max_body_bytes: 1048576 # limite de tamanho do payload em bytes (padrão: 1 MB)

routes:
<nome-da-rota>:
secret: "obrigatorio-por-rota"
events: [] # [] = aceita todos; senão, liste valores de X-GitHub-Event
prompt: "" # {field} / {nested.field} resolvidos do payload
skills: [] # a primeira skill encontrada é carregada (só uma)
deliver: "log" # log | github_comment | telegram | discord | slack | signal | sms
deliver_extra: {} # repo + pr_number para github_comment; chat_id para os outros

E agora?​

  • Revisões de PR por cron — consulta os PRs de tempos em tempos, sem precisar de endpoint público
  • Referência de webhooks — referência completa de configuração da plataforma de webhook
  • Monte um plugin — empacote a lógica de revisão em um plugin compartilhável
  • Perfis — rode um perfil dedicado de revisor, com memória e configuração próprias