Skip to content

Commit 34e3750

Browse files
JustAnOkapitheblazehen
authored andcommitted
ci: Claude Code Review workflow Update 2 (assistant-ui#2660)
Implement dual-path architecture to support both internal and cross-repository pull request reviews while navigating GitHub's OIDC security boundaries. ## Architecture Changes ### Dual-trigger system - Add `pull_request` trigger alongside existing `pull_request_target` - Internal PRs use pull_request + OIDC authentication (claude[bot] identity) - Fork PRs use pull_request_target + github_token (github-actions[bot] identity) - Job-level XOR logic ensures only one path executes per PR **Why:** GitHub prevents OIDC with pull_request_target to protect secrets from untrusted forks. claude-code-action requires OIDC for claude[bot] identity, forcing us to use github_token for forks (creates github-actions[bot] instead). ### Conditional action steps Replace single action step with two conditional steps: **Internal Review:** - Trigger: pull_request event + same repository - Auth: claude_code_oauth_token (OIDC) - Identity: claude[bot] - Features: Full sticky comment support **Fork Review:** - Trigger: pull_request_target event + different repository - Auth: github_token (GITHUB_TOKEN secret) - Identity: github-actions[bot] - Features: allowed_non_write_users: '*' for fork contributors ## Review Quality Improvements ### Comprehensive review prompt Move from inline checklist to sophisticated multi-stage review process: **Context gathering phase:** - Check CI status via mcp__github_ci__get_ci_status - Fetch previous reviews via gh pr view --comments - Track previously identified issues across commits - Analyze workflow failures via mcp__github_ci__get_workflow_run_details **Review approach:** - First review: Comprehensive analysis of all changes - Subsequent reviews: Incremental feedback tracking issue status (Fixed/Unresolved/New) - Incorporate CI/test failure context into feedback - Prohibit meta-commentary about review process itself **Review criteria:** - Code quality and best practices - Potential bugs and security implications - Performance considerations and test coverage - assistant-ui requirements (changesets, documentation) ### Inline comment quality controls - Restrict to critical code issues or relevant suggestions - Check for existing comments to prevent duplicates - Prohibit use for good practices or positive observations - Require actionable, precise feedback ### Tool restrictions - Remove gh pr comment permissions (prevents spam) - Require use of mcp__github_comment__update_claude_comment - Allow read-only gh commands (pr view, diff, list) - Enable inline comments via mcp__github_inline_comment__create_inline_comment ## Fork PR Comment Management Add GraphQL minimization script for fork PRs: - Collapses previous github-actions[bot] comments as OUTDATED - Runs only on cross-repository PRs (pull_request_target) - Supports pagination for handling many comments - No external dependencies (pure github-script@v8) **Why:** Fork PRs can't use sticky comments due to bot identity change (github-actions[bot] instead of claude[bot]). Minimizing old comments keeps discussion clean until sticky comment support is added. **Note:** Temporary workaround until anthropics/claude-code-action#411 merges. PR assistant-ui#411 adds sticky_comment_app_bot_id/name config for github-actions[bot]. ## CI Integration Enable progress tracking and CI tool access: - Set TRACK_PROGRESS: 'true' to load mcp__github_ci__* tools - Add additional_permissions: actions: read - Enable Claude to access test results and workflow run details - Support context-aware reviews based on CI status ## Configuration Improvements ### DRY environment variables Extract shared configuration to env block: - REVIEW_PROMPT: Comprehensive review instructions - CLAUDE_ARGS: Tool allowlist and restrictions - TRACK_PROGRESS: Enable CI MCP tools - USE_STICKY_COMMENT: Enable sticky comment mode - ADDITIONAL_PERMISSIONS: Grant actions: read access ### Checkout optimization - Change from head.repo.full_name + head.ref to refs/pull/{number}/head - Universal reference works for both internal and cross-repository PRs - Ensures correct code is reviewed regardless of fork status ### Resource controls - Add timeout-minutes: 15 to prevent runaway costs - Upgrade to checkout@v5 and github-script@v8 ## claude.yml Refinements Reduce workflow noise and modernize dependencies: - Remove pull_request_review trigger (eliminates duplicate check runs) - Simplify bot filtering (delegate to claude-code-action) - Remove manual github.event.sender.type == 'User' checks - Upgrade actions/github-script@v7 → @v8 - Upgrade actions/checkout@v4 → @v5
1 parent 0ee6562 commit 34e3750

File tree

2 files changed

+181
-47
lines changed

2 files changed

+181
-47
lines changed
Lines changed: 175 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,99 @@
11
name: Claude Code Review
22

33
on:
4-
pull_request_target:
4+
pull_request:
55
types: [opened, synchronize, ready_for_review, reopened]
66
# Optional: Only run on specific file changes
77
# paths:
88
# - "src/**/*.ts"
99
# - "src/**/*.tsx"
1010
# - "src/**/*.js"
1111
# - "src/**/*.jsx"
12+
pull_request_target:
13+
types: [opened, synchronize, ready_for_review, reopened]
14+
15+
env:
16+
# Shared action configuration
17+
TRACK_PROGRESS: 'true'
18+
USE_STICKY_COMMENT: 'true'
19+
ADDITIONAL_PERMISSIONS: |
20+
actions: read
21+
22+
REVIEW_PROMPT: |
23+
REPO: ${{ github.repository }}
24+
PR NUMBER: ${{ github.event.pull_request.number }}
25+
TITLE: ${{ github.event.pull_request.title }}
26+
BODY: ${{ github.event.pull_request.body }}
27+
AUTHOR: ${{ github.event.pull_request.user.login }}
28+
COMMIT: ${{ github.event.pull_request.head.sha }}
29+
Note: The PR branch is already checked out in the current working directory.
30+
31+
Gather Context (complete these steps FIRST, silently - don't report on this process):
32+
1. Check CI status: Use mcp__github_ci__get_ci_status to see if tests passed/failed
33+
2. Get previous reviews: Run gh pr view ${{ github.event.pull_request.number }} --comments
34+
3. Identify previous bot reviews and track which issues were raised before
35+
4. If CI failed: Use mcp__github_ci__get_workflow_run_details for failure details
36+
37+
Review Approach:
38+
- If first review: Comprehensive analysis of all changes
39+
- If previous reviews exist: Focus on changes since last review, track issue status, understand how changes impact pr
40+
- Incorporate CI/test failure context into your feedback if relevant
41+
- Use inline comments (mcp__github_inline_comment__create_inline_comment) for specific code issues
42+
43+
Do not include in your review:
44+
- Task completion statements ("I completed the review", "Review finished")
45+
- Process descriptions ("First I checked X, then I analyzed Y")
46+
- Generic meta-commentary about reviewing
47+
- Progress tracking
48+
49+
Review this pull request by individually analysing each of these thoroughly:
50+
- Code quality and best practices
51+
- Potential bugs or issues
52+
- Security implications
53+
- Performance considerations
54+
- Test coverage
55+
- assistant-ui specific requirements:
56+
- The PR has a changeset if updating source code of published packages (packages/*)
57+
- The PR includes documentation if API surface changes
58+
59+
Provide a comprehensive review build on previous reviews including:
60+
- Summary of changes since last review
61+
- Critical issues found (be thorough)
62+
- Suggested improvements (be thorough)
63+
- Good practices observed (be concise - list only the most notable items without elaboration)
64+
- Leverage collapsible <details> sections where appropriate for lengthy explanations or code snippets to enhance human readability
65+
66+
When reviewing subsequent commits:
67+
- Track status of previously identified issues
68+
- Identify new problems introduced since last review
69+
- Note if fixes introduced new issues
70+
71+
IMPORTANT: Be comprehensive about issues and improvements. For good practices, be brief - just note what was done well without explaining why or praising excessively. NO meta-commentary about the review process itself.
72+
73+
Use `mcp__github_inline_comment__create_inline_comment` only for critical code issues or relevant suggestions. Check existing inline comments first to avoid duplicates. Do NOT use inline comments for good practices or positive observations. Each inline comment must be well thought out, precise, and actionable.
74+
75+
You have been provided with the mcp__github_comment__update_claude_comment tool to update your comment. This tool automatically handles PR comments. Only the body parameter is required - the tool automatically knows which comment to update.
76+
77+
IMPORTANT: Submit your review feedback by updating the Claude comment using mcp__github_comment__update_claude_comment. This will be displayed as your PR review.
78+
79+
# Allowed tools:
80+
# - mcp__github_inline_comment__create_inline_comment: Create inline code comments
81+
# - Bash(gh ...): Read-only GitHub CLI commands for PR/issue info
82+
# Note: gh pr comment is NOT allowed - Claude must use update_claude_comment only
83+
CLAUDE_ARGS: '--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*)"'
1284

1385
jobs:
1486
claude-review:
15-
# Skip draft PRs
16-
if: github.event.pull_request.draft == false
87+
# Only run if at least one review scenario matches
88+
if: |
89+
github.event.pull_request.draft == false &&
90+
github.event.pull_request.head.repo != null &&
91+
(
92+
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) ||
93+
(github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository)
94+
)
1795
runs-on: ubuntu-latest
96+
timeout-minutes: 15
1897
permissions:
1998
contents: read
2099
pull-requests: write
@@ -24,52 +103,110 @@ jobs:
24103

25104
steps:
26105
- name: Checkout repository
27-
uses: actions/checkout@v4
106+
uses: actions/checkout@v5
28107
with:
29-
repository: ${{ github.event.pull_request.head.repo.full_name }}
30-
ref: ${{ github.event.pull_request.head.ref }}
108+
ref: refs/pull/${{ github.event.pull_request.number }}/head
31109
fetch-depth: 1
32110

33-
- name: Run Claude Code Review
34-
id: claude-review
35-
uses: anthropics/claude-code-action@v1
111+
# Minimize previous bot comments on fork PRs to keep discussion clean
112+
# NOTE: Temporary workaround until https://github.com/anthropics/claude-code-action/pull/411 is merged
113+
# PR #411 adds sticky_comment_app_bot_id/name config to make sticky comments work with github-actions[bot]
114+
# Uses GraphQL script instead of int128/hide-comment-action or step-security fork for no external dependencies
115+
- name: Minimize previous fork PR comments
116+
if: |
117+
github.event_name == 'pull_request_target' &&
118+
github.event.pull_request.head.repo.full_name != github.repository
119+
uses: actions/github-script@v8
36120
with:
37-
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
38-
github_token: ${{ secrets.GITHUB_TOKEN }}
39-
allowed_non_write_users: '*'
121+
script: |
122+
try {
123+
const owner = context.repo.owner;
124+
const repo = context.repo.repo;
125+
const prNumber = context.issue.number;
126+
let hasNextPage = true;
127+
let afterCursor = null;
128+
let minimizedCount = 0;
40129
41-
prompt: |
42-
REPO: ${{ github.repository }}
43-
PR NUMBER: ${{ github.event.pull_request.number }}
130+
core.info(`Checking for previous github-actions comments on PR #${prNumber}...`);
44131
45-
Please review this pull request against our checklist:
132+
while (hasNextPage) {
133+
const { repository } = await github.graphql(`
134+
query($owner: String!, $repo: String!, $prNumber: Int!, $after: String) {
135+
repository(owner: $owner, name: $repo) {
136+
pullRequest(number: $prNumber) {
137+
comments(first: 100, after: $after) {
138+
nodes {
139+
id
140+
author { login }
141+
isMinimized
142+
}
143+
pageInfo {
144+
hasNextPage
145+
endCursor
146+
}
147+
}
148+
}
149+
}
150+
}
151+
`, { owner, repo, prNumber, after: afterCursor });
46152
47-
## Code Quality
48-
- [ ] Code follows our style guide
49-
- [ ] No commented-out code
50-
- [ ] Meaningful variable names
51-
- [ ] DRY principle followed
153+
const comments = repository.pullRequest.comments.nodes;
154+
const pageInfo = repository.pullRequest.comments.pageInfo;
52155
53-
## Requirements
54-
- [ ] The PR has a changeset when updating source code of published packages (packages/*)
55-
- [ ] The PR includes documentation if API surface changes
156+
// Filter for non-minimized github-actions comments
157+
const botComments = comments.filter(c =>
158+
c.author?.login === 'github-actions' && !c.isMinimized
159+
);
56160
57-
## Review Areas
58-
- [ ] Code quality and best practices
59-
- [ ] Potential bugs or issues
60-
- [ ] Performance considerations
61-
- [ ] Security concerns
62-
- [ ] Test coverage
161+
core.info(`Found ${botComments.length} non-minimized bot comment(s) in this batch`);
63162
64-
Use the repository's CLAUDE.md for guidance on style and conventions. Be constructive and helpful in your feedback.
163+
// Minimize each bot comment
164+
for (const comment of botComments) {
165+
await github.graphql(`
166+
mutation($commentId: ID!) {
167+
minimizeComment(input: { subjectId: $commentId, classifier: OUTDATED }) {
168+
minimizedComment { isMinimized }
169+
}
170+
}
171+
`, { commentId: comment.id });
172+
minimizedCount++;
173+
}
65174
66-
Use `mcp__github_inline_comment__create_inline_comment` to comment on specific code lines.
67-
Use your Bash tool to run `gh pr comment ${{ github.event.pull_request.number }}` to post your review.
175+
hasNextPage = pageInfo.hasNextPage;
176+
afterCursor = pageInfo.endCursor;
177+
}
68178
69-
# Optional: Use sticky comments to make Claude reuse the same comment on subsequent pushes to the same PR
70-
use_sticky_comment: true
179+
core.info(`✓ Successfully minimized ${minimizedCount} previous comment(s)`);
180+
} catch (error) {
181+
core.setFailed(`Failed to minimize comments: ${error.message}`);
182+
}
71183
72-
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
73-
# or https://docs.claude.com/en/docs/claude-code/cli-reference for available options
74-
claude_args: '--allowed-tools "mcp__github_inline_comment__create_inline_comment,Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh pr comment ${{ github.event.pull_request.number }}:*),Bash(gh pr comment --edit-last:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*)"'
184+
# Same-repo PRs: Use pull_request trigger + OIDC → claude[bot]
185+
- name: Internal Review
186+
if: |
187+
github.event_name == 'pull_request' &&
188+
github.event.pull_request.head.repo.full_name == github.repository
189+
uses: anthropics/claude-code-action@v1
190+
with:
191+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
192+
prompt: ${{ env.REVIEW_PROMPT }}
193+
use_sticky_comment: ${{ fromJSON(env.USE_STICKY_COMMENT) }}
194+
track_progress: ${{ fromJSON(env.TRACK_PROGRESS) }}
195+
claude_args: ${{ env.CLAUDE_ARGS }}
196+
additional_permissions: ${{ env.ADDITIONAL_PERMISSIONS }}
75197

198+
# Fork PRs: Use pull_request_target trigger + github_token → github-actions[bot]
199+
- name: Fork Review
200+
if: |
201+
github.event_name == 'pull_request_target' &&
202+
github.event.pull_request.head.repo.full_name != github.repository
203+
uses: anthropics/claude-code-action@v1
204+
with:
205+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
206+
github_token: ${{ secrets.GITHUB_TOKEN }}
207+
allowed_non_write_users: '*'
208+
prompt: ${{ env.REVIEW_PROMPT }}
209+
use_sticky_comment: ${{ fromJSON(env.USE_STICKY_COMMENT) }}
210+
track_progress: ${{ fromJSON(env.TRACK_PROGRESS) }}
211+
claude_args: ${{ env.CLAUDE_ARGS }}
212+
additional_permissions: ${{ env.ADDITIONAL_PERMISSIONS }}

.github/workflows/claude.yml

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,14 @@ on:
77
types: [created]
88
issues:
99
types: [opened, assigned]
10-
pull_request_review:
11-
types: [submitted]
1210

1311
jobs:
1412
check-permissions:
15-
# Only run if @claude is mentioned and triggered by a User (not bot)
13+
# Only run if @claude is mentioned (bot filtering handled by claude-code-action)
1614
if: |
17-
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude') && github.event.sender.type == 'User') ||
18-
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude') && github.event.sender.type == 'User') ||
19-
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude') && github.event.sender.type == 'User') ||
20-
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')) && github.event.sender.type == 'User')
15+
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
16+
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
17+
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
2118
runs-on: ubuntu-latest
2219
permissions:
2320
contents: read
@@ -26,7 +23,7 @@ jobs:
2623
steps:
2724
- name: Check user has write access
2825
id: check
29-
uses: actions/github-script@v7
26+
uses: actions/github-script@v8
3027
with:
3128
script: |
3229
try {
@@ -57,7 +54,7 @@ jobs:
5754
actions: read
5855
steps:
5956
- name: Checkout repository
60-
uses: actions/checkout@v4
57+
uses: actions/checkout@v5
6158
with:
6259
fetch-depth: 1
6360

0 commit comments

Comments
 (0)