pilot: central webhook service (user-level Gitea webhook + AI-REVIEW gate)

- pilot/webhook_server.py: stdlib HTTP receiver. HMAC-verifies X-Gitea-Signature,
  gates on pull_request action + AI-REVIEW label, runs review_pr in a background
  thread (responds 202 immediately so Gitea's delivery timeout never fires).
  Accepts both GitHub-style (labeled/synchronize) and Gitea event-type-style
  (label_updated/synchronized) action names.
- pilot/ai_review.py: extract review_pr() core so both the CI run() and the
  webhook server share one review path. run() is now an env-driven wrapper.
- pilot/README-webhook.md: architecture, onboarding, one-time per-owner
  user-webhook setup, the Gitea 1.26.1 system-webhook bug, the SSRF
  ALLOWED_HOST_LIST change, K8s deploy + script-update recipe.
- README.md + design doc: note the webhook service as the preferred delivery
  path (partially reverses 'central webhook = non-goal', pilot only).

Gitea 1.26.1 system webhooks broken (POST /admin/hooks -> 201 but never
persists); user-level webhooks (one per repo-owner) are the working fallback.
Gitea SSRF allow-list blocks in-cluster webhook targets by default; required a
scoped [webhook] ALLOWED_HOST_LIST addition + gitea restart.

E2E verified 2026-08-17: pragent-bot reviewed gitea_admin/pragent PR #2 and
masi/portfolio PR #3 via the webhook service (glm-5.2:cloud).

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Marcos
2026-08-17 19:37:38 +00:00
parent 9379b52334
commit 789fb38bae
5 changed files with 430 additions and 18 deletions
+23
View File
@@ -318,3 +318,26 @@ The pilot validates the delivery model (CI-step + bot user, not a central
webhook) and the on-network provider path. When the framework build resumes,
`pilot/ai_review.py` is replaced by `pragent review`; the per-repo workflow
stays, just calling the CLI instead of curling the script. See `pilot/README.md`.
### Central webhook service (2026-08-17) — partially reverses the non-goal
User decision 2026-08-17: ship a central webhook service so "add the bot +
label a PR" is the only per-repo action (no workflow file, no secret, no runner).
This walks back the "central webhook service = non-goal" line above, for the
pilot only — the framework's CLI-step delivery model is unchanged.
Implementation notes (see `pilot/README-webhook.md`):
- **Gitea 1.26.1 system webhooks are broken**: `POST /admin/hooks` returns `201`
but the hook never persists (`GET /admin/hooks` lists 0, no delivery). The
one-webhook-per-instance ideal is not achievable on this version. Fallback:
**user-level webhooks** — one webhook per repo-owner, fires for every repo
that user owns. Few owners on this instance, so near-equivalent.
- **Gitea SSRF allow-list** blocks webhook delivery to in-cluster hosts by
default; required a scoped `[webhook] ALLOWED_HOST_LIST` addition to
`app.ini` (via the helm inline-config secret) + a gitea pod restart.
- The webhook receiver (`pilot/webhook_server.py`, stdlib only) HMAC-verifies
the delivery, gates on `AI-REVIEW` label + PR action, and calls the same
`ai_review.review_pr()` core the CI-step uses — one review path, two triggers.
- The bot stays a **normal user** (not site admin); it must be a Write
collaborator on each reviewed repo.