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.
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.
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.
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
ghinstalada 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:
| Campo | Descrição |
|---|---|
secret (por rota) | Segredo HMAC desta rota. Se omitido, cai para o extra.secret global. |
events | Lista de valores do cabeçalho X-GitHub-Event a aceitar. Lista vazia = aceita todos. |
prompt | Template; {field} e {nested.field} são resolvidos a partir do payload do GitHub. |
deliver | github_comment publica via gh pr comment. log só escreve no log do gateway. |
deliver_extra.repo | Resolve para algo como org/repo a partir do payload. |
deliver_extra.pr_number | Resolve para o número do PR a partir do payload. |
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
- Vá ao seu repositório → Settings → Webhooks → Add webhook
- 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
secretna configuração da rota - Which events? → Select individual events → marque Pull requests
- Payload URL:
- 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.
deliver: log ao testar localmenteTroque 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"
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.
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_AUTHem 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 devolve429. - 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 oX-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
| Sintoma | O que conferir |
|---|---|
401 Invalid signature | O segredo no config.yaml não bate com o segredo do webhook no GitHub |
404 Unknown route | O nome da rota na URL não bate com a chave em routes: |
429 Rate limit exceeded | Passou 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 publicado | O 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ário | Veja o log do gateway — se a saída do agente veio vazia ou só com "SKIP", a entrega mesmo assim é tentada |
| Porta já em uso | Troque o extra.port no config.yaml |
| O agente roda mas revisa só a descrição do PR | O 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 ping | Eventos 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