fix(pr-daemon): watch "*" in the webhook allowlist, log drops

The webhook allowlist never matched the "*" repo pattern, so every gitea
delivery was dropped as unwatched. Each drop returns 202, which the forge
records as a successful delivery, and only accepts and signature failures
were logged -- so a delivery that arrived and was discarded looked exactly
like no delivery at all.

Gitea also flags a PR comment with is_pull rather than nesting a
pull_request link the way github does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Miguel Palhas
2026-08-19 21:10:48 +01:00
co-authored by Claude Opus 5
parent 69522ce961
commit 663a5ee235
+19 -6
View File
@@ -532,6 +532,19 @@ const PR_ACTIONS = new Set([
"synchronize", "synchronized", "closed", "merged",
]);
// Every drop is a 202, which the forge records as a successful delivery -- so
// without this line a misconfigured filter looks identical to no traffic.
function drop(forge: string, event: string, why: string): Response {
log(`webhook ${forge} ${event}: dropped, ${why}`);
return new Response("ignored", { status: 202 });
}
function watched(repo: string): boolean {
return config.repos.some(
(r) => r.repo === "*" || r.repo === repo || (r.repo.endsWith("/*") && repo.startsWith(r.repo.slice(0, -1))),
);
}
function verify(forge: string, body: string, headers: Headers): boolean {
const env = config.forges[forge]?.webhookSecretEnv;
const secret = env ? process.env[env] : undefined;
@@ -573,12 +586,12 @@ function serveWebhooks(): void {
const repo = payload.repository?.full_name;
// An issue_comment on a real issue carries a number too, and it is not
// one of ours -- only the ones with a pull_request link are.
const number = payload.pull_request?.number ?? (payload.issue?.pull_request ? payload.issue.number : undefined);
if (!repo || !number) return new Response("ignored", { status: 202 });
// gitea marks a PR comment with is_pull; github nests a pull_request link.
const isPr = Boolean(payload.issue?.pull_request) || payload.is_pull === true;
const number = payload.pull_request?.number ?? (isPr ? payload.issue?.number : undefined);
if (!repo || !number) return drop(forge, event, `no pull request in payload (repo=${repo ?? "?"})`);
// Allowlist, so an unrelated repo pointed at this endpoint does nothing.
if (!config.repos.some((r) => r.repo === repo || (r.repo.endsWith("/*") && repo.startsWith(r.repo.slice(0, -1))))) {
return new Response("not watched", { status: 202 });
}
if (!watched(repo)) return drop(forge, event, `${repo} not in the watched set`);
const actionable =
(event === "pull_request" && PR_ACTIONS.has(payload.action ?? "")) ||
event.startsWith("pull_request_review") ||
@@ -588,7 +601,7 @@ function serveWebhooks(): void {
event === "check_suite" ||
event === "check_run" ||
event === "status";
if (!actionable) return new Response("filtered", { status: 202 });
if (!actionable) return drop(forge, event, `${repo}#${number} action=${payload.action ?? "-"}`);
// The payload only ever selects which PR to look at. Everything acted on
// is re-read from the API in the next tick.