Skip to content

Commit 3591d31

Browse files
kgpaimeta-codesync[bot]
authored andcommitted
fix(ci): Enable posting of claude reviews (#16534)
Summary: The execution log parsing fails with `Cannot read properties of undefined (reading 'filter')` because the code expected an object with a messages property, but claude-code-base-action writes an array of SDKMessage objects directly to the execution file. Pull Request resolved: #16534 Reviewed By: kevinwilfong Differential Revision: D94410061 Pulled By: kgpai fbshipit-source-id: 80204503a51c750a118183bdbb4313387881c44a
1 parent cfc3353 commit 3591d31

File tree

1 file changed

+111
-34
lines changed

1 file changed

+111
-34
lines changed

.github/workflows/claude-review.yml

Lines changed: 111 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,37 @@ jobs:
164164
DIFF_STATS: ${{ steps.generate_diff.outputs.diff_stats }}
165165
IS_TRUNCATED: ${{ steps.generate_diff.outputs.is_truncated }}
166166
run: |
167+
# Create system context first (no variable expansion needed)
168+
cat > /tmp/review_prompt.txt << 'PROMPT_EOF'
169+
You are an expert C++ code reviewer for the Velox project.
170+
171+
**IMPORTANT**: First, read the CLAUDE.md file in the repository root. It contains:
172+
- Project overview and architecture
173+
- Build commands and environment setup
174+
- Code style and naming conventions
175+
- PR title format requirements
176+
- Testing guidelines
177+
- Function addition requirements
178+
179+
Use CLAUDE.md as your primary reference for project-specific standards.
180+
181+
Key things to know about Velox:
182+
- Uses C++20 standard
183+
- Heavy use of templates and SFINAE
184+
- Custom memory management with MemoryPool
185+
- Vectorized execution with custom Vector types
186+
- Follows Google C++ style with some modifications
187+
188+
Provide actionable, specific feedback. Reference exact file paths and line numbers.
189+
Be constructive and educational in your feedback.
190+
If the diff looks good, say so - don't invent problems.
191+
192+
---
193+
194+
PROMPT_EOF
195+
167196
# Note: Using unquoted PROMPT_EOF to enable variable expansion
168-
cat > /tmp/review_prompt.txt << PROMPT_EOF
197+
cat >> /tmp/review_prompt.txt << PROMPT_EOF
169198
Please review the following pull request for the Velox project.
170199
171200
## Pull Request Information
@@ -233,24 +262,11 @@ jobs:
233262
uses: anthropics/claude-code-base-action@e8132bc5e637a42c27763fc757faa37e1ee43b34 # beta
234263
with:
235264
prompt_file: /tmp/review_prompt.txt
236-
system_prompt: |
237-
You are an expert C++ code reviewer for the Velox project.
238-
Velox is a composable C++ execution engine library for analytical data processing.
239-
240-
Key things to know about Velox:
241-
- Uses C++20 standard
242-
- Heavy use of templates and SFINAE
243-
- Custom memory management with MemoryPool
244-
- Vectorized execution with custom Vector types
245-
- Follows Google C++ style with some modifications
246-
247-
Provide actionable, specific feedback. Reference exact file paths and line numbers.
248-
Be constructive and educational in your feedback.
249-
If the diff looks good, say so - don't invent problems.
250-
251-
# Read-only tools only - no Bash, no Edit, no Write
252-
allowed_tools: View,GlobTool,GrepTool
253-
max_turns: "15"
265+
# Use Sonnet 4.6 for code review, with read-only tools and limited turns
266+
claude_args: >-
267+
--model claude-sonnet-4-6-20250514
268+
--max-turns 15
269+
--allowedTools View,GlobTool,GrepTool
254270
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
255271

256272
# Step 7: Post review as PR comment (skip if dry_run)
@@ -270,21 +286,66 @@ jobs:
270286
271287
try {
272288
// Read the execution log
273-
const executionLog = JSON.parse(fs.readFileSync(process.env.EXECUTION_FILE, 'utf8'));
274-
275-
// Extract Claude's final response
276-
const assistantMessages = executionLog.messages.filter(m => m.role === 'assistant');
277-
if (assistantMessages.length > 0) {
278-
const lastMessage = assistantMessages[assistantMessages.length - 1];
279-
// Handle both string content and array content
280-
if (typeof lastMessage.content === 'string') {
281-
reviewBody = lastMessage.content;
282-
} else if (Array.isArray(lastMessage.content)) {
283-
reviewBody = lastMessage.content
284-
.filter(block => block.type === 'text')
285-
.map(block => block.text)
286-
.join('\n\n');
289+
const fileContent = fs.readFileSync(process.env.EXECUTION_FILE, 'utf8');
290+
const executionLog = JSON.parse(fileContent);
291+
292+
// Log structure for debugging
293+
console.log('Execution log type:', Array.isArray(executionLog) ? 'array' : typeof executionLog);
294+
if (!Array.isArray(executionLog)) {
295+
console.log('Execution log keys:', Object.keys(executionLog));
296+
}
297+
298+
// Helper function to extract text from assistant messages
299+
const extractFromMessages = (messages) => {
300+
const assistantMessages = messages.filter(m => m.role === 'assistant');
301+
if (assistantMessages.length > 0) {
302+
const lastMessage = assistantMessages[assistantMessages.length - 1];
303+
if (typeof lastMessage.content === 'string') {
304+
return lastMessage.content;
305+
} else if (Array.isArray(lastMessage.content)) {
306+
return lastMessage.content
307+
.filter(block => block.type === 'text')
308+
.map(block => block.text)
309+
.join('\n\n');
310+
}
287311
}
312+
return null;
313+
};
314+
315+
// Try different possible structures from claude-code-base-action
316+
if (Array.isArray(executionLog)) {
317+
// claude-code-base-action writes messages array directly to file
318+
const extracted = extractFromMessages(executionLog);
319+
if (extracted) {
320+
reviewBody = extracted;
321+
} else {
322+
console.log('No assistant messages found in array');
323+
reviewBody = '⚠️ No assistant response found in execution log.';
324+
}
325+
} else if (executionLog.result) {
326+
// Direct result field
327+
reviewBody = executionLog.result;
328+
} else if (executionLog.output) {
329+
// Output field
330+
reviewBody = executionLog.output;
331+
} else if (executionLog.response) {
332+
// Response field
333+
reviewBody = executionLog.response;
334+
} else if (executionLog.messages && Array.isArray(executionLog.messages)) {
335+
// Messages array inside object
336+
const extracted = extractFromMessages(executionLog.messages);
337+
if (extracted) {
338+
reviewBody = extracted;
339+
} else {
340+
reviewBody = '⚠️ No assistant response found in messages.';
341+
}
342+
} else if (typeof executionLog === 'string') {
343+
// Plain string
344+
reviewBody = executionLog;
345+
} else {
346+
// Fallback: stringify the whole thing
347+
console.log('Unknown format, raw content:', JSON.stringify(executionLog).substring(0, 500));
348+
reviewBody = '⚠️ Could not parse Claude response format. Check workflow logs for details.';
288349
}
289350
} catch (error) {
290351
console.error('Error parsing execution log:', error);
@@ -332,7 +393,23 @@ jobs:
332393
run: |
333394
echo "=== DRY RUN - Review would be posted as comment ==="
334395
echo ""
335-
cat "$EXECUTION_FILE" | jq -r '.messages | map(select(.role == "assistant")) | last | .content' 2>/dev/null || cat "$EXECUTION_FILE"
396+
echo "=== Execution file structure ==="
397+
# Check if it's an array (claude-code-base-action format) or object
398+
if jq -e 'type == "array"' "$EXECUTION_FILE" >/dev/null 2>&1; then
399+
echo "Format: array of messages (length: $(jq 'length' "$EXECUTION_FILE"))"
400+
else
401+
jq 'keys' "$EXECUTION_FILE" 2>/dev/null || echo "Not valid JSON or file not found"
402+
fi
403+
echo ""
404+
echo "=== Attempting to extract review ==="
405+
# Try array format first (claude-code-base-action), then object formats
406+
if jq -e 'type == "array"' "$EXECUTION_FILE" >/dev/null 2>&1; then
407+
# Array format: extract last assistant message content
408+
jq -r '[.[] | select(.role == "assistant")] | last | if .content | type == "string" then .content elif .content | type == "array" then [.content[] | select(.type == "text") | .text] | join("\n\n") else "Could not extract" end' "$EXECUTION_FILE" 2>/dev/null || cat "$EXECUTION_FILE"
409+
else
410+
# Object format: try different possible paths
411+
jq -r '.result // .output // .response // .messages[-1].content // "Could not extract"' "$EXECUTION_FILE" 2>/dev/null || cat "$EXECUTION_FILE"
412+
fi
336413
echo ""
337414
echo "=== End of review ==="
338415

0 commit comments

Comments
 (0)