Skip to content

Commit 07de1f6

Browse files
authored
moved to a webhook (#13836)
1 parent b003a47 commit 07de1f6

1 file changed

Lines changed: 62 additions & 30 deletions

File tree

.github/workflows/serge_review.yml

Lines changed: 62 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
name: Claude AI Review with inline comments
22

3+
# Instead of running the ai-reviewer GitHub Action inline, this workflow acts as
4+
# a thin, VPN-side relay to the Serge GitHub App hosted at
5+
# https://serge.huggingface.tech/. The App's /webhook endpoint sits behind a VPN
6+
# that GitHub's own webhook delivery cannot reach, so a runner inside the VPN
7+
# (group: aws-general-8-plus) re-delivers the triggering comment event to the App.
8+
#
9+
# The relay reproduces a genuine GitHub App webhook delivery:
10+
# - body: the original event payload with `installation.id` injected (the App
11+
# needs it to mint an installation token; Actions payloads omit it)
12+
# - X-Hub-Signature-256: HMAC-SHA256 of that exact body using the App's
13+
# webhook secret (verified at webapp.py:_verify_webhook_signature)
14+
# - X-GitHub-Event: the original event name (issue_comment / pull_request_review_comment)
15+
#
16+
# All reviewing, diff fetching and comment posting happens server-side under the
17+
# App identity, so this job needs no checkout and no write permissions.
18+
319
on:
420
issue_comment:
521
types: [created]
@@ -8,11 +24,9 @@ on:
824

925
permissions:
1026
contents: read
11-
pull-requests: write
12-
issues: read
1327

1428
jobs:
15-
claude-ai-review:
29+
forward-to-serge-app:
1630
if: |
1731
(
1832
github.event_name == 'issue_comment' &&
@@ -32,35 +46,53 @@ jobs:
3246
concurrency:
3347
group: claude-ai-review-${{ github.event.issue.number || github.event.pull_request.number }}
3448
cancel-in-progress: false
35-
runs-on: ubuntu-latest
49+
# Must run inside the VPN so https://serge.huggingface.tech/ is reachable.
50+
runs-on:
51+
group: aws-general-8-plus
3652
steps:
37-
- name: Resolve PR number
38-
id: pr
53+
- name: Relay event to the Serge GitHub App
54+
env:
55+
WEBHOOK_URL: https://serge.huggingface.tech/webhook
56+
# App webhook secret — must match the App's GITHUB_WEBHOOK_SECRET.
57+
WEBHOOK_SECRET: ${{ secrets.SERGE_WEBHOOK_SECRET }}
58+
# Installation id of the Serge App on this repo. Not sensitive, but the
59+
# App requires it in the payload to obtain an installation token.
60+
INSTALLATION_ID: ${{ secrets.SERGE_INSTALLATION_ID }}
61+
EVENT_NAME: ${{ github.event_name }}
62+
DELIVERY_ID: ${{ github.run_id }}-${{ github.run_attempt }}
3963
run: |
40-
NUM="${{ github.event.issue.number || github.event.pull_request.number }}"
41-
echo "number=${NUM}" >> "$GITHUB_OUTPUT"
64+
set -euo pipefail
65+
66+
if [ -z "${WEBHOOK_SECRET}" ]; then
67+
echo "::error::SERGE_WEBHOOK_SECRET secret is not set" >&2
68+
exit 1
69+
fi
70+
if [ -z "${INSTALLATION_ID}" ]; then
71+
echo "::error::SERGE_INSTALLATION_ID secret is not set" >&2
72+
exit 1
73+
fi
74+
75+
# Inject installation.id into the original event payload, compact form.
76+
# The signed bytes and the POSTed bytes must be byte-identical, so we
77+
# write the body to a file and reuse it for both the HMAC and the POST.
78+
jq -c --argjson iid "${INSTALLATION_ID}" \
79+
'. + {installation: {id: $iid}}' \
80+
"${GITHUB_EVENT_PATH}" > payload.json
4281
43-
- name: Check out PR head (shallow)
44-
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
45-
with:
46-
ref: refs/pull/${{ steps.pr.outputs.number }}/head
47-
fetch-depth: 1
82+
SIG="sha256=$(openssl dgst -sha256 -hmac "${WEBHOOK_SECRET}" payload.json | awk '{print $NF}')"
4883
49-
- name: Strip fork-supplied reviewer/agent config
50-
# ai-reviewer fetches its config (.ai/review-rules.md, .ai/review-tools.json,
51-
# .ai/context-script) from the base repo's default branch via the GitHub
52-
# Contents API, so wiping the fork's local copies does not affect rule
53-
# loading. The wipe matters because the action also exposes read-only
54-
# browse tools (read_file/list_dir/grep) rooted at the PR-head checkout —
55-
# without this step a fork could ship its own .ai/review-tools.json or
56-
# .ai/context-script and surface them to the LLM. .claude/ + CLAUDE.md
57-
# are wiped for parity with the hardening in claude_review.yml.
58-
run: rm -rf .ai/ .claude/ CLAUDE.md
84+
HTTP_CODE=$(curl --silent --show-error --fail-with-body \
85+
--output response.txt --write-out '%{http_code}' \
86+
--request POST "${WEBHOOK_URL}" \
87+
--header "Content-Type: application/json" \
88+
--header "X-GitHub-Event: ${EVENT_NAME}" \
89+
--header "X-GitHub-Delivery: ${DELIVERY_ID}" \
90+
--header "X-Hub-Signature-256: ${SIG}" \
91+
--data-binary @payload.json) || {
92+
echo "::error::Failed to deliver event to Serge App (HTTP ${HTTP_CODE:-000})" >&2
93+
cat response.txt >&2 || true
94+
exit 1
95+
}
5996
60-
- uses: huggingface/ai-reviewer@main
61-
with:
62-
llm_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
63-
llm_api_base: https://api.anthropic.com
64-
llm_model: claude-opus-4-6
65-
llm_stream: 'true'
66-
mention_trigger: '@askserge'
97+
echo "Serge App responded with HTTP ${HTTP_CODE}"
98+
cat response.txt

0 commit comments

Comments
 (0)