mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-12 22:14:22 +00:00
feat(ci): migrate ai-review to claude-code-action with fork PR support (#304)
* fix(ci): add persistent logging and remove turn/diff limits for AI review (#298) - Remove --max-turns limit (unlimited exploration) - Remove head -8000 diff limit (full diff) - Add persistent logging to /opt/actions-runner/logs/ai-review/ - Save prompt, stdout, stderr, timing, metadata as separate files - Add structured metadata.json with run details - Separate stdout/stderr capture for better debugging - Include debug info in failure comments (exit code, duration, log path) - Retry without tool restrictions if first attempt fails * fix(cliproxy): correct faulty version range to v81-85 and set v89 as stable (#302) PR #289 incorrectly marked all v81+ as faulty. v89 confirmed stable. - CLIPROXY_MAX_STABLE_VERSION: 6.6.80-0 → 6.6.89-0 - CLIPROXY_FAULTY_RANGE max: 6.6.999-0 → 6.6.85-0 * ci(github): migrate ai-review to claude-code-action with CLIProxy - Replace manual claude CLI setup with anthropics/claude-code-action@v1 - Add REVIEW_MODEL env var for centralized model configuration - Configure ANTHROPIC_BASE_URL for CLIProxy routing - Enable track_progress for live PR comment updates - Add model attribution footer instruction - Restrict tools to gh pr commands, Read, Glob, Grep only - Simplify workflow from 424 to 55 lines (-369 lines) * chore(release): 7.16.0-dev.1 [skip ci] * fix(ci): skip ai-review when secrets unavailable Add secrets check to job condition to gracefully skip on fork PRs where CCS_REVIEWER_APP_ID secret is not accessible * chore(release): 7.16.0-dev.2 [skip ci] * feat(ci): enable ai-review for fork PRs via pull_request_target Use pull_request_target event to allow secrets access for fork PRs. Safe for code review since we only read diff, not execute fork code. * chore(release): 7.16.0-dev.3 [skip ci] * fix(ci): disable track_progress for workflow_dispatch * chore(release): 7.16.0-dev.4 [skip ci] * fix(ci): clear Claude install locks before each run * perf(ci): use pre-installed Claude executable on runner * chore(release): 7.16.0-dev.5 [skip ci] --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
80e2124f63
commit
5651935797
2 files changed
+54
-364
No files matched your search
+53
-363
@@ -1,17 +1,15 @@
|
|||||||
# AI Code Review Workflow
|
# AI Code Review Workflow
|
||||||
# Uses Claude Code CLI on self-hosted runner with CLIProxyAPI
|
# Uses anthropics/claude-code-action with CLIProxy for model flexibility
|
||||||
# Posts as ccs-agy-reviewer[bot] via GitHub App
|
# Posts as ccs-agy-reviewer[bot] via GitHub App
|
||||||
#
|
#
|
||||||
# Triggers:
|
# Triggers:
|
||||||
# - Automatically on PR open/update
|
# - Automatically on PR open/update
|
||||||
# - Manually via /review comment on PR
|
# - Manually via /review comment on PR
|
||||||
#
|
|
||||||
# Logs stored on runner at: /opt/actions-runner/logs/ai-review/
|
|
||||||
|
|
||||||
name: AI Code Review
|
name: AI Code Review
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request_target:
|
||||||
types: [opened, synchronize, reopened]
|
types: [opened, synchronize, reopened]
|
||||||
issue_comment:
|
issue_comment:
|
||||||
types: [created]
|
types: [created]
|
||||||
@@ -30,20 +28,32 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
review:
|
review:
|
||||||
name: Claude Code Review
|
name: Claude Code Review
|
||||||
# Only run on self-hosted runner with cliproxy access
|
|
||||||
runs-on: [self-hosted, cliproxy]
|
runs-on: [self-hosted, cliproxy]
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
issues: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
# Conditions:
|
# Conditions:
|
||||||
# - PR event: always run
|
# - PR event: always run (pull_request_target has secrets access for forks)
|
||||||
# - Comment event: only if it's a PR and contains /review
|
# - Comment event: only if it's a PR and contains /review
|
||||||
if: >
|
if: >
|
||||||
github.event_name == 'pull_request' ||
|
github.event_name == 'pull_request_target' ||
|
||||||
github.event_name == 'workflow_dispatch' ||
|
github.event_name == 'workflow_dispatch' ||
|
||||||
(github.event_name == 'issue_comment' &&
|
(github.event_name == 'issue_comment' &&
|
||||||
github.event.issue.pull_request &&
|
github.event.issue.pull_request &&
|
||||||
contains(github.event.comment.body, '/review'))
|
contains(github.event.comment.body, '/review'))
|
||||||
|
|
||||||
|
# CLIProxy environment for model routing
|
||||||
|
env:
|
||||||
|
ANTHROPIC_BASE_URL: http://localhost:8317
|
||||||
|
REVIEW_MODEL: gemini-claude-opus-4-5-thinking
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: Clear Claude install locks
|
||||||
|
run: rm -rf ~/.local/state/claude/locks/* 2>/dev/null || true
|
||||||
|
|
||||||
- name: Generate App Token
|
- name: Generate App Token
|
||||||
id: app-token
|
id: app-token
|
||||||
uses: actions/create-github-app-token@v1
|
uses: actions/create-github-app-token@v1
|
||||||
@@ -51,7 +61,7 @@ jobs:
|
|||||||
app-id: ${{ secrets.CCS_REVIEWER_APP_ID }}
|
app-id: ${{ secrets.CCS_REVIEWER_APP_ID }}
|
||||||
private-key: ${{ secrets.CCS_REVIEWER_PRIVATE_KEY }}
|
private-key: ${{ secrets.CCS_REVIEWER_PRIVATE_KEY }}
|
||||||
|
|
||||||
- name: Checkout PR branch
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
@@ -65,17 +75,6 @@ jobs:
|
|||||||
gh pr checkout $PR_NUM || git checkout ${{ github.event.pull_request.head.sha }} 2>/dev/null || true
|
gh pr checkout $PR_NUM || git checkout ${{ github.event.pull_request.head.sha }} 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Determine PR Number
|
|
||||||
id: pr
|
|
||||||
run: |
|
|
||||||
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
|
||||||
echo "number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
|
|
||||||
elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
|
||||||
echo "number=${{ github.event.inputs.pr_number }}" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "number=${{ github.event.issue.number }}" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Add reaction to comment
|
- name: Add reaction to comment
|
||||||
if: github.event_name == 'issue_comment'
|
if: github.event_name == 'issue_comment'
|
||||||
run: |
|
run: |
|
||||||
@@ -84,360 +83,51 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||||
|
|
||||||
- name: Ensure Claude Code installed
|
|
||||||
run: |
|
|
||||||
# Force cleanup of any stale lock/install markers
|
|
||||||
rm -rf ~/.claude/.installing ~/.claude/downloads 2>/dev/null || true
|
|
||||||
pkill -f "claude install" 2>/dev/null || true
|
|
||||||
|
|
||||||
if ! command -v claude &> /dev/null; then
|
|
||||||
echo "[i] Installing Claude Code..."
|
|
||||||
# Download and run install script
|
|
||||||
curl -fsSL https://claude.ai/install.sh > /tmp/claude-install.sh
|
|
||||||
chmod +x /tmp/claude-install.sh
|
|
||||||
|
|
||||||
# Run installer; on failure, try running claude install --force directly
|
|
||||||
if ! bash /tmp/claude-install.sh; then
|
|
||||||
echo "[!] Script install failed, trying direct --force..."
|
|
||||||
BINARY=$(find ~/.claude -name "claude" -type f -executable 2>/dev/null | head -1)
|
|
||||||
if [ -n "$BINARY" ]; then
|
|
||||||
"$BINARY" install --force || true
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
|
||||||
else
|
|
||||||
echo "[i] Claude Code already installed: $(claude --version)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Create review prompt template
|
|
||||||
run: |
|
|
||||||
cat > /tmp/review-instructions.txt << 'INSTRUCTIONS_END'
|
|
||||||
You are a senior staff engineer performing a comprehensive code review.
|
|
||||||
|
|
||||||
## MANDATORY: Tool Usage Requirements
|
|
||||||
Before writing your review, you MUST:
|
|
||||||
|
|
||||||
1. **Read each changed file in full** using the Read tool
|
|
||||||
- Don't just look at the diff - read the ENTIRE file for context
|
|
||||||
- Understand the surrounding code, imports, and dependencies
|
|
||||||
|
|
||||||
2. **Find related test files** using Glob tool
|
|
||||||
- Pattern: `**/*test*` or `**/*.spec.*` or `**/__tests__/**`
|
|
||||||
- Read the test files to assess coverage
|
|
||||||
|
|
||||||
3. **Search for usages** using Grep tool
|
|
||||||
- Find where modified functions/classes are called
|
|
||||||
- Check for breaking changes impact
|
|
||||||
|
|
||||||
4. **Check project structure** using Glob tool
|
|
||||||
- Understand the codebase architecture
|
|
||||||
- Verify the changes follow existing patterns
|
|
||||||
|
|
||||||
## Analysis Dimensions
|
|
||||||
|
|
||||||
### 🔒 Security (CRITICAL)
|
|
||||||
- Auth bypass, privilege escalation, injection (SQL, command, XSS)
|
|
||||||
- Secrets/credentials exposure, insecure defaults
|
|
||||||
- OWASP Top 10 vulnerabilities
|
|
||||||
- Input validation at trust boundaries
|
|
||||||
|
|
||||||
### ✅ Correctness
|
|
||||||
- Logic errors, off-by-one, null/undefined handling
|
|
||||||
- Race conditions, deadlocks, async issues
|
|
||||||
- Edge cases, boundary conditions
|
|
||||||
- Error handling completeness
|
|
||||||
|
|
||||||
### 🏗️ Architecture and Design
|
|
||||||
- Coupling, cohesion, separation of concerns
|
|
||||||
- Design patterns usage (appropriate or over-engineered)
|
|
||||||
- API contract changes, backward compatibility
|
|
||||||
- Single responsibility, interface segregation
|
|
||||||
|
|
||||||
### 🧪 Testing
|
|
||||||
- Test coverage for new/modified code
|
|
||||||
- Edge case coverage, negative tests
|
|
||||||
- Test quality (not just quantity)
|
|
||||||
- Integration vs unit test balance
|
|
||||||
|
|
||||||
### ⚡ Performance
|
|
||||||
- O(n^2) or worse algorithms, unnecessary iterations
|
|
||||||
- Memory leaks, resource cleanup
|
|
||||||
- Database query efficiency (N+1, missing indexes)
|
|
||||||
- Only flag if measurable impact
|
|
||||||
|
|
||||||
### 📖 Maintainability
|
|
||||||
- Naming clarity, self-documenting code
|
|
||||||
- Comments for "why" not "what"
|
|
||||||
- Complexity (cyclomatic, cognitive)
|
|
||||||
- Consistency with codebase patterns
|
|
||||||
|
|
||||||
## Output Format (Markdown)
|
|
||||||
|
|
||||||
## 🔍 Code Review
|
|
||||||
|
|
||||||
**Verdict**: [Choose one: ✅ Approve | ⚠️ Approve with notes | ❌ Request changes]
|
|
||||||
|
|
||||||
> 🤖 Reviewed by `MODEL_NAME`
|
|
||||||
|
|
||||||
### Summary
|
|
||||||
[2-3 sentences: what this PR does, the approach taken, and overall assessment]
|
|
||||||
|
|
||||||
### ✅ Strengths
|
|
||||||
[Specific things done well with file:line-range references]
|
|
||||||
1. **[Category]**: Description
|
|
||||||
- `src/path/file.ts:10-25` - specific code reference
|
|
||||||
2. ...
|
|
||||||
|
|
||||||
### ⚠️ Issues Found
|
|
||||||
| File:Line | Issue | Severity | Impact |
|
|
||||||
|-----------|-------|----------|--------|
|
|
||||||
| `src/file.ts:123-145` | Description | 🔴/🟡/🟢 | What could go wrong |
|
|
||||||
|
|
||||||
### 🔍 Code Quality Observations
|
|
||||||
**Positive:**
|
|
||||||
- Observation with `file.ts:line` reference
|
|
||||||
|
|
||||||
**Concerns:**
|
|
||||||
- Concern with `file.ts:line` reference and code example:
|
|
||||||
```typescript
|
|
||||||
// Current code (problematic):
|
|
||||||
const x = something.slice(0, 60);
|
|
||||||
|
|
||||||
// Suggested improvement:
|
|
||||||
const x = truncateString(something, 60);
|
|
||||||
```
|
|
||||||
|
|
||||||
### 🧪 Testing
|
|
||||||
- **Coverage**: [Assessed by reading test files]
|
|
||||||
- **Test files examined**: `tests/path/file.test.ts`
|
|
||||||
- **Missing tests**: [List specific scenarios not covered]
|
|
||||||
- **Test quality**: [Observations about test implementations]
|
|
||||||
|
|
||||||
### 🔒 Security
|
|
||||||
- [Detailed security analysis or "No concerns identified"]
|
|
||||||
|
|
||||||
### ⚡ Performance
|
|
||||||
- [Performance analysis or "No concerns identified"]
|
|
||||||
|
|
||||||
### 📝 Recommendations
|
|
||||||
1. **[Category]**: Specific recommendation
|
|
||||||
```typescript
|
|
||||||
// Instead of:
|
|
||||||
problematicCode();
|
|
||||||
|
|
||||||
// Consider:
|
|
||||||
betterApproach();
|
|
||||||
```
|
|
||||||
|
|
||||||
2. ...
|
|
||||||
|
|
||||||
### 🎯 Alignment with Project Standards
|
|
||||||
- ✅/❌ Follows codebase patterns (cite specific examples)
|
|
||||||
- ✅/❌ Consistent naming/style
|
|
||||||
- ✅/❌ Proper error handling
|
|
||||||
|
|
||||||
### Verdict: **[Final verdict with emoji]**
|
|
||||||
[One sentence final summary with key takeaway]
|
|
||||||
|
|
||||||
---
|
|
||||||
**Guidelines:**
|
|
||||||
- Skip empty sections
|
|
||||||
- Only ❌ Request changes for 🔴 High severity issues
|
|
||||||
- Include code snippets for ALL non-trivial suggestions
|
|
||||||
- Reference specific line ranges, not just file names
|
|
||||||
INSTRUCTIONS_END
|
|
||||||
|
|
||||||
- name: Run Claude Code Review
|
- name: Run Claude Code Review
|
||||||
env:
|
id: claude-review
|
||||||
ANTHROPIC_BASE_URL: http://localhost:8317
|
uses: anthropics/claude-code-action@v1
|
||||||
ANTHROPIC_API_KEY: ${{ secrets.CLIPROXY_API_KEY }}
|
with:
|
||||||
ANTHROPIC_MODEL: gemini-3-pro-preview
|
anthropic_api_key: ${{ secrets.CLIPROXY_API_KEY }}
|
||||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
github_token: ${{ steps.app-token.outputs.token }}
|
||||||
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
|
path_to_claude_code_executable: /home/github-runner/.local/bin/claude
|
||||||
run: |
|
track_progress: ${{ github.event_name != 'workflow_dispatch' }}
|
||||||
PR_NUM="${{ steps.pr.outputs.number }}"
|
prompt: |
|
||||||
REPO="${{ github.repository }}"
|
ultrathink
|
||||||
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
|
||||||
|
|
||||||
# Setup logging directory on runner
|
REPO: ${{ github.repository }}
|
||||||
LOG_DIR="/opt/actions-runner/logs/ai-review/pr-${PR_NUM}-${TIMESTAMP}"
|
PR NUMBER: ${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }}
|
||||||
mkdir -p "$LOG_DIR"
|
|
||||||
|
|
||||||
echo "[i] Running Claude Code review for PR #${PR_NUM}"
|
Perform a comprehensive code review. Follow the repository's CLAUDE.md for project-specific guidelines.
|
||||||
echo "[i] Model: ${ANTHROPIC_MODEL}"
|
|
||||||
echo "[i] Working directory: $(pwd)"
|
|
||||||
echo "[i] Log directory: ${LOG_DIR}"
|
|
||||||
|
|
||||||
# Get PR metadata
|
## Review Focus Areas
|
||||||
PR_TITLE=$(gh pr view $PR_NUM --repo $REPO --json title --jq .title)
|
|
||||||
PR_BODY=$(gh pr view $PR_NUM --repo $REPO --json body --jq .body)
|
|
||||||
|
|
||||||
# Get list of changed files
|
1. **Security** - OWASP Top 10, injection, auth bypass, secrets exposure
|
||||||
CHANGED_FILES=$(gh pr diff $PR_NUM --repo $REPO --name-only)
|
2. **Correctness** - Logic errors, edge cases, error handling
|
||||||
|
3. **Architecture** - Coupling, cohesion, API contracts, backward compatibility
|
||||||
|
4. **Testing** - Coverage, edge cases, test quality
|
||||||
|
5. **Performance** - Algorithm complexity, memory leaks, query efficiency
|
||||||
|
6. **Maintainability** - Naming, complexity, consistency with codebase
|
||||||
|
|
||||||
# Get full diff (NO LIMIT - unlimited)
|
## Output Structure
|
||||||
DIFF=$(gh pr diff $PR_NUM --repo $REPO)
|
|
||||||
|
|
||||||
# Replace model name in instructions
|
Use visual hierarchy with `---` separators between major sections:
|
||||||
sed -i "s|MODEL_NAME|${ANTHROPIC_MODEL}|g" /tmp/review-instructions.txt
|
- Summary (2-3 sentences)
|
||||||
|
- Strengths (numbered, with file:line references)
|
||||||
|
- Observations & Suggestions (with code examples)
|
||||||
|
- Security Considerations (✅/❌ checklist)
|
||||||
|
- Code Quality Checklist (✅/❌ format)
|
||||||
|
- Recommendations (Priority: High/Medium/Low)
|
||||||
|
- Overall Assessment (APPROVED/APPROVED WITH NOTES/CHANGES REQUESTED)
|
||||||
|
|
||||||
# Build the full prompt
|
Be thorough but concise. Skip empty sections. Include code examples for non-trivial suggestions.
|
||||||
cat > /tmp/full-prompt.txt << PROMPT_END
|
|
||||||
# Pull Request Review Task
|
|
||||||
|
|
||||||
## PR Information
|
End your review with:
|
||||||
- **PR**: #${PR_NUM}
|
> 🤖 Reviewed by `${{ env.REVIEW_MODEL }}`
|
||||||
- **Title**: ${PR_TITLE}
|
|
||||||
- **Repository**: ${REPO}
|
|
||||||
|
|
||||||
## Description
|
claude_args: |
|
||||||
${PR_BODY}
|
--model ${{ env.REVIEW_MODEL }}
|
||||||
|
--allowedTools "Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Read,Glob,Grep"
|
||||||
## Changed Files
|
continue-on-error: true
|
||||||
${CHANGED_FILES}
|
|
||||||
|
|
||||||
## Diff (for initial understanding only)
|
|
||||||
${DIFF}
|
|
||||||
|
|
||||||
## Your Task
|
|
||||||
|
|
||||||
**STEP 1 - MANDATORY EXPLORATION (before writing review):**
|
|
||||||
You MUST use tools to explore the codebase. Do not skip this step.
|
|
||||||
|
|
||||||
a) For EACH file in "Changed Files" above:
|
|
||||||
- Use Read tool to read the FULL file (not just diff)
|
|
||||||
- Examine imports, exports, and dependencies
|
|
||||||
|
|
||||||
b) Find test files:
|
|
||||||
- Use Glob with pattern: tests/**/*.test.* or **/*.spec.*
|
|
||||||
- Read relevant test files to assess coverage
|
|
||||||
|
|
||||||
c) Search for usages:
|
|
||||||
- Use Grep to find where modified functions/classes are used
|
|
||||||
- Assess breaking change impact
|
|
||||||
|
|
||||||
**STEP 2 - WRITE REVIEW:**
|
|
||||||
Only after completing Step 1, write your review following the format below.
|
|
||||||
|
|
||||||
$(cat /tmp/review-instructions.txt)
|
|
||||||
|
|
||||||
---
|
|
||||||
BEGIN YOUR EXPLORATION NOW. Use Read, Glob, and Grep tools before writing your review.
|
|
||||||
PROMPT_END
|
|
||||||
|
|
||||||
# Save prompt for debugging
|
|
||||||
cp /tmp/full-prompt.txt "$LOG_DIR/prompt.txt"
|
|
||||||
|
|
||||||
# Save metadata
|
|
||||||
cat > "$LOG_DIR/metadata.json" << EOF
|
|
||||||
{
|
|
||||||
"pr_number": ${PR_NUM},
|
|
||||||
"repository": "${REPO}",
|
|
||||||
"model": "${ANTHROPIC_MODEL}",
|
|
||||||
"timestamp": "${TIMESTAMP}",
|
|
||||||
"changed_files_count": $(echo "$CHANGED_FILES" | wc -l),
|
|
||||||
"diff_lines": $(echo "$DIFF" | wc -l),
|
|
||||||
"workflow_run_id": "${{ github.run_id }}"
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Run Claude Code with NO LIMITS
|
|
||||||
echo "[i] Starting comprehensive review (unlimited turns)..."
|
|
||||||
START_TIME=$(date +%s)
|
|
||||||
|
|
||||||
# Run with separate stdout/stderr capture
|
|
||||||
set +e
|
|
||||||
cat /tmp/full-prompt.txt | claude -p \
|
|
||||||
--allowed-tools "Read,Glob,Grep" \
|
|
||||||
> "$LOG_DIR/claude-stdout.txt" \
|
|
||||||
2> "$LOG_DIR/claude-stderr.txt"
|
|
||||||
EXIT_CODE=$?
|
|
||||||
set -e
|
|
||||||
|
|
||||||
END_TIME=$(date +%s)
|
|
||||||
DURATION=$((END_TIME - START_TIME))
|
|
||||||
|
|
||||||
# Save timing info
|
|
||||||
cat > "$LOG_DIR/timing.txt" << EOF
|
|
||||||
Start: $(date -d @$START_TIME '+%Y-%m-%d %H:%M:%S')
|
|
||||||
End: $(date -d @$END_TIME '+%Y-%m-%d %H:%M:%S')
|
|
||||||
Duration: ${DURATION} seconds
|
|
||||||
Exit code: ${EXIT_CODE}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo "[i] Review completed in ${DURATION}s with exit code ${EXIT_CODE}"
|
|
||||||
|
|
||||||
# Read the review output
|
|
||||||
REVIEW=$(cat "$LOG_DIR/claude-stdout.txt")
|
|
||||||
REVIEW_LEN=${#REVIEW}
|
|
||||||
|
|
||||||
echo "[i] Review output: ${REVIEW_LEN} chars"
|
|
||||||
echo "[i] Stderr output: $(wc -c < "$LOG_DIR/claude-stderr.txt") chars"
|
|
||||||
|
|
||||||
# Log stderr if non-empty (for debugging)
|
|
||||||
if [ -s "$LOG_DIR/claude-stderr.txt" ]; then
|
|
||||||
echo "[!] Claude stderr output:"
|
|
||||||
cat "$LOG_DIR/claude-stderr.txt"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Validate review output - retry if too short
|
|
||||||
if [ -z "$REVIEW" ] || [ ${REVIEW_LEN} -lt 100 ]; then
|
|
||||||
echo "[!] Review output too short (${REVIEW_LEN} chars), retrying without tool restrictions..."
|
|
||||||
|
|
||||||
cat /tmp/full-prompt.txt | claude -p \
|
|
||||||
> "$LOG_DIR/claude-stdout-retry.txt" \
|
|
||||||
2> "$LOG_DIR/claude-stderr-retry.txt" || true
|
|
||||||
|
|
||||||
REVIEW=$(cat "$LOG_DIR/claude-stdout-retry.txt")
|
|
||||||
REVIEW_LEN=${#REVIEW}
|
|
||||||
echo "[i] Retry output: ${REVIEW_LEN} chars"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Final fallback with error details
|
|
||||||
if [ -z "$REVIEW" ] || [ ${REVIEW_LEN} -lt 50 ]; then
|
|
||||||
STDERR_CONTENT=$(cat "$LOG_DIR/claude-stderr.txt" 2>/dev/null | head -10 || echo "No stderr")
|
|
||||||
REVIEW="## 🔍 Code Review
|
|
||||||
|
|
||||||
**Verdict**: ⚠️ Review generation failed
|
|
||||||
|
|
||||||
> 🤖 Attempted by \`${ANTHROPIC_MODEL}\`
|
|
||||||
|
|
||||||
The automated review encountered an issue. Details logged to runner.
|
|
||||||
|
|
||||||
**Debug info:**
|
|
||||||
- Exit code: ${EXIT_CODE}
|
|
||||||
- Duration: ${DURATION}s
|
|
||||||
- Output length: ${REVIEW_LEN} chars
|
|
||||||
- Log path: \`${LOG_DIR}\`
|
|
||||||
|
|
||||||
Please request a manual review or check runner logs."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Save final review
|
|
||||||
echo "$REVIEW" > "$LOG_DIR/final-review.txt"
|
|
||||||
|
|
||||||
# Update metadata with result
|
|
||||||
cat > "$LOG_DIR/metadata.json" << EOF
|
|
||||||
{
|
|
||||||
"pr_number": ${PR_NUM},
|
|
||||||
"repository": "${REPO}",
|
|
||||||
"model": "${ANTHROPIC_MODEL}",
|
|
||||||
"timestamp": "${TIMESTAMP}",
|
|
||||||
"changed_files_count": $(echo "$CHANGED_FILES" | wc -l),
|
|
||||||
"diff_lines": $(echo "$DIFF" | wc -l),
|
|
||||||
"workflow_run_id": "${{ github.run_id }}",
|
|
||||||
"duration_seconds": ${DURATION},
|
|
||||||
"exit_code": ${EXIT_CODE},
|
|
||||||
"review_length": ${REVIEW_LEN},
|
|
||||||
"success": $([ ${REVIEW_LEN} -ge 100 ] && echo "true" || echo "false")
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Post review as comment
|
|
||||||
echo "[i] Posting review (${REVIEW_LEN} chars)..."
|
|
||||||
echo "$REVIEW" | gh pr comment $PR_NUM --repo $REPO --body-file -
|
|
||||||
echo "[OK] Review posted"
|
|
||||||
echo "[i] Logs saved to: ${LOG_DIR}"
|
|
||||||
|
|
||||||
- name: Add success reaction
|
- name: Add success reaction
|
||||||
if: success() && github.event_name == 'issue_comment'
|
if: success() && github.event_name == 'issue_comment'
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@kaitranntt/ccs",
|
"name": "@kaitranntt/ccs",
|
||||||
"version": "7.16.0",
|
"version": "7.16.0-dev.5",
|
||||||
"description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6",
|
"description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"cli",
|
"cli",
|
||||||
|
|||||||
Reference in new issue
Block a user