H繁中版
文件教學與最佳實踐webhook github pr review
<!-- Source: https://hermesbible.com/docs/guides/webhook-github-pr-review -->

本指南帶你將 Hermes Agent 連接到 GitHub,使其自動取得拉取請求的差異、分析程式碼變更,並發佈評論 — 由 Webhook 事件觸發,無需手動提示。

當 PR 被開啟或更新時,GitHub 會向你的 Hermes 實例發送 Webhook POST。Hermes 使用提示詞執行代理程式,指示其透過 gh CLI 取得差異,回應會發回 PR 線程。

提示 — 想要更簡單的設定,不需要公開端點?

如果你沒有公開 URL 或只想快速開始,請查看建立 GitHub PR 評論代理 — 使用 Cron 工作按排程輪詢 PR,在 NAT 和防火牆後也能運作。

資訊 — 參考文件

完整的 Webhook 平台參考(所有設定選項、傳遞類型、動態訂閱、安全模型)請參見 Webhook

警告 — 提示注入風險

Webhook 負載包含攻擊者控制的資料 — PR 標題、提交訊息和描述可能包含惡意指令。當你的 Webhook 端點暴露在網際網路上時,在沙箱環境(Docker、SSH 後端)中運行閘道。參見下方的安全說明


前置需求

  • Hermes Agent 已安裝並運行(hermes gateway
  • 閘道主機上已安裝並認證的 gh CLIgh auth login
  • 你的 Hermes 實例的公開可達 URL(如果在本機運行,參見使用 ngrok 進行本機測試
  • GitHub 儲存庫的管理員存取權限(管理 Webhook 所需)

步驟 1 — 啟用 Webhook 平台

將以下內容新增到你的 ~/.hermes/config.yaml

platforms:
  webhook:
    enabled: true
    extra:
      port: 8644          # 預設;如果其他服務佔用此連接埠則變更
      rate_limit: 30      # 每個路由每分鐘最大請求數(非全域上限)

      routes:
        github-pr-review:
          secret: "your-webhook-secret-here"   # 必須完全匹配 GitHub Webhook 密鑰
          events:
            - pull_request

          # 代理程式被指示在審查前取得實際差異。
          # {number} 和 {repository.full_name} 從 GitHub 負載解析。
          prompt: |
            A pull request event was received (action: {action}).

            PR #{number}: {pull_request.title}
            Author: {pull_request.user.login}
            Branch: {pull_request.head.ref} → {pull_request.base.ref}
            Description: {pull_request.body}
            URL: {pull_request.html_url}

            If the action is "closed" or "labeled", stop here and do not post a comment.

            Otherwise:
            1. Run: gh pr diff {number} --repo {repository.full_name}
            2. Review the code changes for correctness, security issues, and clarity.
            3. Write a concise, actionable review comment and post it.

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

關鍵欄位:

欄位描述
secret(路由層級)此路由的 HMAC 密鑰。省略時退回 extra.secret 全域密鑰。
events要接受的 X-GitHub-Event 標頭值清單。空清單 = 接受全部。
prompt模板;{field}{nested.field} 從 GitHub 負載解析。
delivergithub_comment 透過 gh pr comment 發佈。log 僅寫入閘道日誌。
deliver_extra.repo從負載解析為例如 org/repo
deliver_extra.pr_number從負載解析為 PR 號碼。

注意 — 負載不包含程式碼

GitHub Webhook 負載包含 PR 中繼資料(標題、描述、分支名稱、URL),但不包含差異。上方的提示詞指示代理程式執行 gh pr diff 以取得實際變更。terminal 工具包含在預設的 hermes-webhook 工具組中,因此不需要額外設定。


步驟 2 — 啟動閘道

hermes gateway

你應該看到:

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

驗證它正在運行:

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

步驟 3 — 在 GitHub 上註冊 Webhook

  1. 前往你的儲存庫 → SettingsWebhooksAdd webhook
  2. 填入:
    • Payload URL: https://your-public-url.example.com/webhooks/github-pr-review
    • Content type: application/json
    • Secret: 與你在路由設定中設定的 secret 相同的值
    • Which events? → 選擇個別事件 → 勾選 Pull requests
  3. 按一下 Add webhook

GitHub 會立即發送 ping 事件以確認連線。它會被安全忽略 — ping 不在你的 events 清單中 — 並傳回 {"status": "ignored", "event": "ping"}。它僅在 DEBUG 級別記錄,因此在預設日誌級別下不會出現在控制台中。


步驟 4 — 開啟測試 PR

建立一個分支,推送一個變更,並開啟一個 PR。在 30-90 秒內(取決於 PR 大小和模型),Hermes 應該會發佈評論。

要即時追蹤代理程式的進度:

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

使用 ngrok 進行本機測試

如果 Hermes 在你的筆記型電腦上運行,使用 ngrok 暴露它:

ngrok http 8644

複製 https://...ngrok-free.app URL 並將其用作你的 GitHub Payload URL。在免費 ngrok 層級上,URL 每次 ngrok 重啟時都會改變 — 每個工作階段更新你的 GitHub Webhook。付費 ngrok 帳戶獲得靜態網域。

你可以直接使用 curl 對靜態路由進行煙霧測試 — 不需要 GitHub 帳戶或真實的 PR。

提示 — 本機測試時使用 deliver: log

在測試時將 deliver: github_comment 改為 deliver: log。否則代理程式會嘗試向測試負載中的假 org/repo#99 儲存庫發佈評論,這會失敗。一旦你對提示詞輸出滿意,切換回 deliver: github_comment

SECRET="your-webhook-secret-here"
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"
# Expected: {"status":"accepted","route":"github-pr-review","event":"pull_request","delivery_id":"..."}

然後觀察代理程式運行:

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

注意

hermes webhook test <name> 僅適用於使用 hermes webhook subscribe 建立的動態訂閱。它不會從 config.yaml 讀取路由。


過濾到特定動作

GitHub 為許多動作發送 pull_request 事件:openedsynchronizereopenedclosedlabeled 等。events 清單僅按 X-GitHub-Event 標頭值過濾 — 它無法在路由層級按動作子類型過濾。

步驟 1 中的提示詞已經透過指示代理程式在 closedlabeled 事件時提前停止來處理此問題。

警告 — 代理程式仍會運行並消耗代幣

「在這裡停止」指令防止了有意義的審查,但代理程式仍會為每個 pull_request 事件完成執行,無論動作如何。GitHub Webhook 只能按事件類型(pull_requestpushissues 等)過濾 — 不能按動作子類型(openedclosedlabeled)過濾。沒有路由層級的子動作過濾器。對於高吞吐量的儲存庫,接受此成本或在上游使用條件性呼叫你的 Webhook URL 的 GitHub Actions 工作流程過濾。

沒有 Jinja2 或條件模板語法。{field}{nested.field} 是唯一支援的替換。其他任何內容都會逐字傳遞給代理程式。


使用技能獲得一致的審查風格

載入一個 Hermes 技能以給代理程式一致的審查人物。在 config.yamlplatforms.webhook.extra.routes 中為你的路由新增 skills

platforms:
  webhook:
    enabled: true
    extra:
      routes:
        github-pr-review:
          secret: "your-webhook-secret-here"
          events: [pull_request]
          prompt: |
            A pull request event was received (action: {action}).
            PR #{number}: {pull_request.title} by {pull_request.user.login}
            URL: {pull_request.html_url}

            If the action is "closed" or "labeled", stop here and do not post a comment.

            Otherwise:
            1. Run: gh pr diff {number} --repo {repository.full_name}
            2. Review the diff using your review guidelines.
            3. Write a concise, actionable review comment and post it.
          skills:
            - review
          deliver: github_comment
          deliver_extra:
            repo: "{repository.full_name}"
            pr_number: "{number}"

注意: 只會載入清單中找到的第一個技能。Hermes 不堆疊多個技能 — 後續條目被忽略。


將回應改為發送到 Slack 或 Discord

替換你路由中的 deliverdeliver_extra 欄位為你的目標平台:

# 在 platforms.webhook.extra.routes.<route-name> 中:

# Slack
deliver: slack
deliver_extra:
  chat_id: "C0123456789"   # Slack 頻道 ID(省略以使用已設定的主頻道)

# Discord
deliver: discord
deliver_extra:
  chat_id: "987654321012345678"  # Discord 頻道 ID(省略以使用主頻道)

目標平台也必須在閘道中啟用並連線。如果省略 chat_id,回應會發送到該平台已設定的主頻道。

有效的 deliver 值:log · github_comment · telegram · discord · slack · signal · sms


GitLab 支援

相同的適配器適用於 GitLab。GitLab 使用 X-Gitlab-Token 進行認證(純字串匹配,非 HMAC)— Hermes 兩者都自動處理。

對於事件過濾,GitLab 將 X-GitLab-Event 設定為 Merge Request HookPush HookPipeline Hook 等值。在 events 中使用確切的標頭值:

events:
  - Merge Request Hook

GitLab 負載欄位與 GitHub 不同 — 例如 {object_attributes.title} 用於 MR 標題,{object_attributes.iid} 用於 MR 號碼。發現完整負載結構最簡單的方法是 GitLab Webhook 設定中的 Test 按鈕,搭配 Recent Deliveries 日誌。或者,從你的路由設定中省略 prompt — Hermes 會將完整負載作為格式化的 JSON 直接傳遞給代理程式,代理程式的回應(在閘道日誌中使用 deliver: log 可見)將描述其結構。


安全說明

  • 永遠不要在生產環境中使用 INSECURE_NO_AUTH — 它完全停用簽章驗證。僅用於本機開發。
  • 定期輪替你的 Webhook 密鑰 並在 GitHub(Webhook 設定)和你的 config.yaml 中更新它。
  • 速率限制 預設為每個路由每分鐘 30 個請求(可透過 extra.rate_limit 設定)。超過時傳回 429
  • 重複傳遞(Webhook 重試)透過 1 小時的冪等性快取去重複。快取鍵是 X-GitHub-Delivery(如果存在),然後是 X-Request-ID,然後是毫秒時間戳。當兩者都未設定時,重試去重複。
  • 提示注入: PR 標題、描述和提交訊息由攻擊者控制。惡意 PR 可能嘗試操縱代理程式的行為。在暴露於公共網際網路時在沙箱環境(Docker、VM)中運行閘道。

疑難排解

症狀檢查
401 Invalid signatureconfig.yaml 中的密鑰與 GitHub Webhook 密鑰不匹配
404 Unknown routeURL 中的路由名稱與 routes: 中的鍵不匹配
429 Rate limit exceeded超過每個路由 30 個請求/分鐘 — 從 GitHub 的 UI 重新傳遞測試事件時常見;等一分鐘或提高 extra.rate_limit
未發佈評論gh 未安裝、不在 PATH 上,或未認證(gh auth login
代理運行但無評論檢查閘道日誌 — 如果代理輸出為空或僅為「SKIP」,仍會嘗試傳遞
連接埠已被使用變更 config.yaml 中的 extra.port
代理運行但僅審查 PR 描述提示詞未包含 gh pr diff 指令 — 差異不在 Webhook 負載中
看不到 ping 事件被忽略的事件僅在 DEBUG 日誌級別傳回 {"status":"ignored","event":"ping"} — 檢查 GitHub 的傳遞日誌(repo → Settings → Webhooks → your webhook → Recent Deliveries)

GitHub 的 Recent Deliveries 標籤(repo → Settings → Webhooks → your webhook)顯示每個傳遞的確切請求標頭、負載、HTTP 狀態和回應內文。這是無需查看伺服器日誌即可診斷失敗的最快方法。


完整設定參考

platforms:
  webhook:
    enabled: true
    extra:
      host: "0.0.0.0"         # 綁定位址(預設:0.0.0.0)
      port: 8644               # 監聽連接埠(預設:8644)
      secret: ""               # 選擇性全域備用密鑰
      rate_limit: 30           # 每個路由每分鐘請求數
      max_body_bytes: 1048576  # 負載大小上限(位元組)(預設:1 MB)

      routes:
        <route-name>:
          secret: "required-per-route"
          events: []            # [] = 接受全部;否則列出 X-GitHub-Event 值
          prompt: ""            # {field} / {nested.field} 從負載解析
          skills: []            # 載入第一個匹配的技能(僅一個)
          deliver: "log"        # log | github_comment | telegram | discord | slack | signal | sms
          deliver_extra: {}     # github_comment 需要 repo + pr_number;其他需要 chat_id

接下來要做什麼?



xAI Grok OAuth(SuperGrok / X Premium+)