fix: gate Forgejo review/comment triggers on @claude mention

Previously, all pull_request_comment events on Forgejo triggered the
full Claude pipeline unconditionally — both in the workflow if-condition
and in trigger.ts. This wasted CI resources and API credits on reviews
without @claude mentions (e.g. plain "LGTM" reviews).

Workflow: add @claude check via contains() on comment.body and review.body.
Trigger validation: replace blind auto-trigger with payload-aware check
that inspects available body fields, falling back only when no body exists.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Vlad MocanuandClaude Opus 4.6 committed 2026-02-26 10:45:09 +02:00
1 parent d9c22ffe50
commit 71fb686885
2 files changed
+24 -18

No files matched your search

+2 -1
View File
@@ -24,7 +24,8 @@ jobs:
# despite action being 'labeled'). Let all through; trigger.ts validates.
if: >-
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' || github.event_name == 'pull_request_comment') ||
(github.event_name == 'pull_request_review_comment' || github.event_name == 'pull_request_comment') &&
(contains(github.event.comment.body, '@claude') || contains(github.event.review.body, '@claude')) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
(github.event_name == 'issues')
runs-on: grunt
+22 -17
View File
@@ -123,32 +123,37 @@ export function checkContainsTrigger(context: ParsedGitHubContext): boolean {
isIssueCommentEvent(context) ||
isPullRequestReviewCommentEvent(context)
) {
// Forgejo's pull_request_review_comment payload does NOT include a
// top-level `comment` object (it's undefined). On Forgejo, the
// workflow `if:` already gates on event name alone, so always trigger
// here — the actual comment data is fetched via REST in the data
// fetcher, and non-matching comments won't produce useful output.
if (
isPullRequestReviewCommentEvent(context) &&
detectPlatform() === "forgejo"
) {
console.log(
"Forgejo pull_request_review_comment: triggering (payload lacks comment body)",
);
return true;
}
// Forgejo sends both review comments and full reviews as
// `pull_request_comment`, which normalizes to `pull_request_review_comment`.
// The payload may have `comment.body`, `review.body`, both, or neither.
// Try all available body fields before giving up.
const payload = context.payload as Record<string, any>;
const commentBody = isIssueCommentEvent(context)
? context.payload.comment.body
: context.payload.comment.body;
: (payload.comment?.body ?? payload.review?.body ?? "");
// Check for exact match with word boundaries or punctuation
const regex = new RegExp(
`(^|\\s)${escapeRegExp(triggerPhrase)}([\\s.,!?;:]|$)`,
);
if (regex.test(commentBody)) {
if (commentBody && regex.test(commentBody)) {
console.log(`Comment contains exact trigger phrase '${triggerPhrase}'`);
return true;
}
// On Forgejo, if no body was found in the payload at all, still trigger —
// the actual comment data will be fetched via REST in the data fetcher.
// This handles edge cases where Forgejo strips body from the webhook payload.
if (
isPullRequestReviewCommentEvent(context) &&
detectPlatform() === "forgejo" &&
!commentBody
) {
console.log(
"Forgejo pull_request_review_comment: no body in payload, triggering (data fetched via REST)",
);
return true;
}
}
console.log(`No trigger was met for ${triggerPhrase}`);