Files
systemd/.github/workflows/claude-review.yml
T
Daan De Meyer a67cb44287 ci: React to issue_comment in claude review workflow
issue_comment is the trigger that fires on regular pull request comments,
so we have to trigger the review workflow on that as well.
2026-03-06 15:27:30 +01:00

159 lines
7.9 KiB
YAML

# Integrates Claude Code as an AI assistant for reviewing pull requests.
# Mention @claude in any PR review to request a review. Claude authenticates
# via AWS Bedrock using OIDC — no long-lived API keys required.
name: Claude Review
on:
# Strangely enough you have to use issue_comment to react to regular comments on PRs.
# See https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#pull_request_comment-use-issue_comment.
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
pull_request_review:
types: [submitted]
concurrency:
group: claude-review-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
claude-review:
runs-on: ubuntu-latest
if: |
github.repository_owner == 'systemd' &&
((github.event_name == 'issue_comment' &&
contains(github.event.comment.body, '@claude') &&
contains(fromJSON('["MEMBER","OWNER","COLLABORATOR"]'), github.event.comment.author_association)) ||
(github.event_name == 'pull_request_review_comment' &&
contains(github.event.comment.body, '@claude') &&
contains(fromJSON('["MEMBER","OWNER","COLLABORATOR"]'), github.event.comment.author_association)) ||
(github.event_name == 'pull_request_review' &&
contains(github.event.review.body, '@claude') &&
contains(fromJSON('["MEMBER","OWNER","COLLABORATOR"]'), github.event.review.author_association))
permissions:
contents: read # Read repository contents
pull-requests: write # Post comments and reviews on PRs
id-token: write # Authenticate with AWS via OIDC
actions: read # Access workflow run metadata
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7
with:
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }}
role-session-name: GitHubActions-Claude-${{ github.run_id }}
aws-region: us-east-1
- name: Run Claude Code
uses: anthropics/claude-code-action@1fc90f3ed982521116d8ff6d85b948c9b12cae3e
with:
use_bedrock: "true"
github_token: ${{ secrets.GITHUB_TOKEN }}
claude_args: |
--model us.anthropic.claude-opus-4-6-v1
--max-turns 100
--allowedTools "
Read,Write,Edit,MultiEdit,LS,Grep,Glob,Task,
Bash(cat:*),Bash(test:*),Bash(printf:*),Bash(jq:*),Bash(head:*),Bash(git:*),Bash(gh:*),
mcp__github_inline_comment__create_inline_comment,
"
prompt: |
REPO: ${{ github.repository }}
PR NUMBER: ${{ github.event.pull_request.number }}
HEAD SHA: ${{ github.event.pull_request.head.sha }}
Review this pull request.
You are in the upstream repo without the patch applied. Do not apply it.
## Phase 1: Gather context
Fetch the patch, PR title/body, and list of existing comments (top-level, inline, and reviews):
- `gh pr diff ${{ github.event.pull_request.number }} --patch`
- `gh pr view ${{ github.event.pull_request.number }} --json title,body`
- `gh api --paginate repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments`
- `gh api --paginate repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/comments`
- `gh api --paginate repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews`
## Phase 2: Parallel review subagents
Review:
- Code quality, style, and best practices
- Potential bugs, issues, incorrect logic
- Security implications
- CLAUDE.md - compliance
For every category, launch subagents to review them in parallel. Group related sections
as needed — use 2-4 subagents based on PR size and scope.
Give each subagent the PR title, description, full patch, and the list of changed files.
Each subagent must return a JSON array of issues:
`[{"file": "path", "line": <number or null>, "severity": "must-fix|suggestion|nit", "title": "...", "body": "..."}]`
Subagents must ONLY return the JSON array — they must NOT post comments,
call `gh`, or use `mcp__github_inline_comment__create_inline_comment`.
All posting happens in Phase 3.
Each subagent MUST verify its findings before returning them:
- For style/convention claims, check at least 3 existing examples in the codebase to confirm
the pattern actually exists before flagging a violation.
- For "use X instead of Y" suggestions, confirm X actually exists and works for this case.
- If unsure, don't include the issue.
## Phase 3: Collect and post
After ALL subagents complete:
1. Collect all issues. Merge duplicates (same file, lines within 3 of each other, same problem).
2. Drop low-confidence findings.
3. For CLAUDE.md violations that appear in 3+ existing places in the codebase, do NOT post inline comments.
Instead, add them to the 'CLAUDE.md improvements' section of the tracking comment
4. Check existing inline review comments (fetched in Phase 1). Do NOT post an inline comment if
one already exists on the same file+line about the same problem.
5. Check for author replies that dismiss or reject a previous comment. Do NOT re-raise an issue
the PR author has already responded to disagreeing with.
6. Post new inline comments with `mcp__github_inline_comment__create_inline_comment`.
Prefix ALL comments with "Claude: ".
Link format: https://github.com/${{ github.repository }}/blob/${{ github.event.pull_request.head.sha }}/README.md#L10-L15
Then maintain a single top-level "tracking comment" listing ALL issues as checkboxes.
Use a hidden HTML marker to find it: `<!-- claude-pr-review -->`.
Look through the top-level comments fetched in Phase 1 for one containing that marker.
**If no tracking comment exists (first run):**
Create one with `gh pr comment ${{ github.event.pull_request.number }} --body "..."` using this format:
```
Claude: review of <REPO> #<PR NUMBER> (<HEAD SHA>)
<!-- claude-pr-review -->
### Must fix
- [ ] **title** — `file:line` — short explanation
### Suggestions
- [ ] **title** — `file:line` — short explanation
### Nits
- [ ] **title** — `file:line` — short explanation
### CLAUDE.md improvements
- improvement suggestion
```
Omit empty sections.
**If a tracking comment already exists (subsequent run):**
1. Parse the existing checkboxes. For each old issue, check if the current patch still has
that problem (re-check the relevant lines in the new diff). If fixed, mark it `- [x]`.
If the author dismissed it, mark it `- [x] ~~title~~ (dismissed)`.
2. Append any NEW issues found in this run that aren't already listed.
3. Update the HEAD SHA in the header line.
4. Edit the comment in-place.
```
printf '%s' "$BODY" > pr-review-body.txt
gh api --method PATCH repos/${{ github.repository }}/issues/comments/<comment-id> -F body=@pr-review-body.txt
```