mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-04 10:16:34 +00:00
feat(ci): Claude Code CLI for AI reviews (#290)
* feat(ci): add AI code review workflow with Claude via CLIProxyAPI - Self-hosted runner calls CLIProxyAPI at localhost:8317 - Triggers on PR open/update and /review comment - Uses gemini-claude-opus-4-5-thinking for deep reviews - Posts summary + inline comments via gh CLI - Handles self-PR fallback to COMMENT mode * chore(release): 7.15.0-dev.1 [skip ci] * refactor(ci): use GitHub App for reviewer identity + new review format - Posts as ccs-agy-reviewer[bot] via GitHub App token - New review format: structured markdown with verdict, summary, issues table - Single PR comment instead of inline comments - Concise, focused on PR changes only * chore(release): 7.15.0-dev.2 [skip ci] * refactor(ci): switch to Claude Code CLI for reviews - Use claude -p instead of custom TypeScript script - Auto-install if not present on runner - CLIProxyAPI via env vars (ANTHROPIC_BASE_URL, ANTHROPIC_MODEL) - Allowed tools: Read, Glob, Grep - Max 3 turns for file exploration * chore(release): 7.15.0-dev.3 [skip ci] --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
github-actions[bot] <github-actions[bot]@users.noreply.github.com>
parent
a0ac773ead
commit
49c4d299c0
@@ -0,0 +1,160 @@
|
||||
# AI Code Review Workflow
|
||||
# Uses Claude Code CLI on self-hosted runner with CLIProxyAPI
|
||||
# Posts as ccs-agy-reviewer[bot] via GitHub App
|
||||
#
|
||||
# Triggers:
|
||||
# - Automatically on PR open/update
|
||||
# - Manually via /review comment on PR
|
||||
|
||||
name: AI Code Review
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
# Cancel in-progress runs for same PR
|
||||
concurrency:
|
||||
group: ai-review-${{ github.event.pull_request.number || github.event.issue.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
review:
|
||||
name: Claude Code Review
|
||||
# Only run on self-hosted runner with cliproxy access
|
||||
runs-on: [self-hosted, cliproxy]
|
||||
|
||||
# Conditions:
|
||||
# - PR event: always run
|
||||
# - Comment event: only if it's a PR and contains /review
|
||||
if: >
|
||||
github.event_name == 'pull_request' ||
|
||||
(github.event_name == 'issue_comment' &&
|
||||
github.event.issue.pull_request &&
|
||||
contains(github.event.comment.body, '/review'))
|
||||
|
||||
steps:
|
||||
- name: Generate App Token
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v1
|
||||
with:
|
||||
app-id: ${{ secrets.CCS_REVIEWER_APP_ID }}
|
||||
private-key: ${{ secrets.CCS_REVIEWER_PRIVATE_KEY }}
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Determine PR Number
|
||||
id: pr
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
||||
echo "number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "number=${{ github.event.issue.number }}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Add reaction to comment
|
||||
if: github.event_name == 'issue_comment'
|
||||
run: |
|
||||
gh api repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions \
|
||||
--method POST -f content=eyes
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
|
||||
- name: Ensure Claude Code installed
|
||||
run: |
|
||||
if ! command -v claude &> /dev/null; then
|
||||
echo "[i] Installing Claude Code..."
|
||||
curl -fsSL https://claude.ai/install.sh | bash
|
||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||
else
|
||||
echo "[i] Claude Code already installed: $(claude --version)"
|
||||
fi
|
||||
|
||||
- name: Run Claude Code Review
|
||||
env:
|
||||
# CLIProxyAPI configuration
|
||||
ANTHROPIC_BASE_URL: http://localhost:8317
|
||||
ANTHROPIC_API_KEY: ${{ secrets.CLIPROXY_API_KEY }}
|
||||
ANTHROPIC_MODEL: gemini-3-pro-preview
|
||||
# GitHub token for gh CLI
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
run: |
|
||||
PR_NUM="${{ steps.pr.outputs.number }}"
|
||||
REPO="${{ github.repository }}"
|
||||
|
||||
echo "[i] Running Claude Code review for PR #${PR_NUM}"
|
||||
echo "[i] Model: ${ANTHROPIC_MODEL}"
|
||||
|
||||
# Get PR diff for context
|
||||
DIFF=$(gh pr diff $PR_NUM --repo $REPO | head -5000)
|
||||
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)
|
||||
|
||||
# Run Claude Code in print mode with review prompt
|
||||
REVIEW=$(claude -p "You are a senior engineer reviewing PR #${PR_NUM}: '${PR_TITLE}'.
|
||||
|
||||
## Context
|
||||
Repository: ${REPO}
|
||||
Description: ${PR_BODY}
|
||||
|
||||
## Diff
|
||||
\`\`\`diff
|
||||
${DIFF}
|
||||
\`\`\`
|
||||
|
||||
## Your Task
|
||||
Review this PR with analytical depth. Focus on:
|
||||
- 🔐 Security: auth bypass, injection, secrets exposure
|
||||
- 🐛 Correctness: edge cases, null handling, race conditions
|
||||
- 🧹 Maintainability: coupling, naming, hidden complexity
|
||||
- ⚡ Performance: only if measurable impact
|
||||
|
||||
## Output Format
|
||||
|
||||
## 🔍 Code Review (${ANTHROPIC_MODEL})
|
||||
|
||||
**Verdict**: ✅ Approve | ✅ Approve with notes | ⚠️ Request changes
|
||||
|
||||
### 📋 What This PR Does
|
||||
[1-2 sentences]
|
||||
|
||||
### ✨ The Good
|
||||
[Only if worth mentioning]
|
||||
|
||||
### 🚨 Concerns
|
||||
Use severity: 🔴 Critical | 🟡 Warning | 🟢 Suggestion
|
||||
Format: \`file:line\` - what's wrong → what could happen
|
||||
|
||||
### ❓ Questions for Author
|
||||
[Only if clarification needed]
|
||||
|
||||
---
|
||||
Be direct. Skip sections if nothing significant. Approve unless 🔴 Critical issue exists." \
|
||||
--allowed-tools "Read,Glob,Grep" \
|
||||
--max-turns 3)
|
||||
|
||||
# Post review as comment
|
||||
echo "[i] Posting review..."
|
||||
echo "$REVIEW" | gh pr comment $PR_NUM --repo $REPO --body-file -
|
||||
echo "[OK] Review posted"
|
||||
|
||||
- name: Add success reaction
|
||||
if: success() && github.event_name == 'issue_comment'
|
||||
run: |
|
||||
gh api repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions \
|
||||
--method POST -f content=rocket
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
|
||||
- name: Add failure reaction
|
||||
if: failure() && github.event_name == 'issue_comment'
|
||||
run: |
|
||||
gh api repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions \
|
||||
--method POST -f content=confused
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
Reference in New Issue
Block a user