Skip to content

Commit b678148

Browse files
Zachary-wWclaude
andcommitted
[ci] refactor: replace claude-code-action with direct API call
The claude-code-action has workflow validation and tool compatibility issues with third-party API proxies. Replace with a simple Python script that calls the API directly and posts review via gh CLI. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent e5335bd commit b678148

2 files changed

Lines changed: 101 additions & 38 deletions

File tree

.github/scripts/claude-review.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#!/usr/bin/env python3
2+
# Copyright 2026 The LoongForge Authors.
3+
# SPDX-License-Identifier: Apache-2.0
4+
"""Call Claude API to review a PR diff and output the review as markdown."""
5+
import json
6+
import os
7+
import sys
8+
import urllib.request
9+
10+
11+
def main():
12+
api_key = os.environ.get("ANTHROPIC_API_KEY")
13+
base_url = os.environ.get("ANTHROPIC_BASE_URL", "https://api.anthropic.com")
14+
model = os.environ.get("CLAUDE_MODEL", "Claude Sonnet 4.6")
15+
skill_path = os.environ.get("SKILL_PATH", "skills/loongforge-review/SKILL.md")
16+
diff_path = os.environ.get("DIFF_PATH", "/tmp/pr-diff.txt")
17+
pr_title = os.environ.get("PR_TITLE", "")
18+
pr_author = os.environ.get("PR_AUTHOR", "")
19+
20+
if not api_key:
21+
print("ERROR: ANTHROPIC_API_KEY not set", file=sys.stderr)
22+
sys.exit(1)
23+
24+
with open(skill_path, "r") as f:
25+
skill = f.read()
26+
27+
with open(diff_path, "r") as f:
28+
diff = f.read()[:80000]
29+
30+
prompt = f"""{skill}
31+
32+
## PR Information
33+
34+
- Title: {pr_title}
35+
- Author: {pr_author}
36+
37+
## Diff
38+
39+
```diff
40+
{diff}
41+
```
42+
43+
Now review this pull request following the instructions above."""
44+
45+
payload = {
46+
"model": model,
47+
"max_tokens": 4096,
48+
"messages": [{"role": "user", "content": prompt}],
49+
}
50+
51+
req = urllib.request.Request(
52+
f"{base_url}/v1/messages",
53+
data=json.dumps(payload).encode(),
54+
headers={
55+
"x-api-key": api_key,
56+
"anthropic-version": "2023-06-01",
57+
"content-type": "application/json",
58+
},
59+
)
60+
61+
try:
62+
with urllib.request.urlopen(req, timeout=180) as resp:
63+
result = json.loads(resp.read())
64+
text = result["content"][0]["text"]
65+
print(text)
66+
except urllib.error.HTTPError as e:
67+
body = e.read().decode()
68+
print(f"API Error ({e.code}): {body}", file=sys.stderr)
69+
sys.exit(1)
70+
except Exception as e:
71+
print(f"Error: {e}", file=sys.stderr)
72+
sys.exit(1)
73+
74+
75+
if __name__ == "__main__":
76+
main()

.github/workflows/claude-review.yml

Lines changed: 25 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,61 +3,48 @@ name: Claude PR Review
33
permissions:
44
contents: read
55
pull-requests: write
6-
issues: write
7-
id-token: write
86

97
on:
108
pull_request:
119
types: [opened, synchronize]
12-
issue_comment:
13-
types: [created]
14-
pull_request_review_comment:
15-
types: [created]
1610

1711
jobs:
1812
auto-review:
19-
if: github.event_name == 'pull_request'
2013
runs-on: self-hosted
2114
timeout-minutes: 15
2215
steps:
2316
- uses: actions/checkout@v4
2417
with:
2518
fetch-depth: 0
2619

27-
- name: Read review skill
28-
id: skill
20+
- name: Get PR diff
21+
env:
22+
GH_TOKEN: ${{ github.token }}
2923
run: |
30-
{
31-
echo 'REVIEW_PROMPT<<EOF'
32-
cat skills/loongforge-review/SKILL.md
33-
echo 'EOF'
34-
} >> "$GITHUB_OUTPUT"
24+
gh pr diff ${{ github.event.pull_request.number }} > /tmp/pr-diff.txt
3525
36-
- uses: anthropics/claude-code-action@v1
26+
- name: Run Claude review
3727
env:
28+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
3829
ANTHROPIC_BASE_URL: ${{ secrets.ANTHROPIC_BASE_URL }}
39-
with:
40-
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
41-
prompt: |
42-
${{ steps.skill.outputs.REVIEW_PROMPT }}
43-
44-
Now review the current pull request following the instructions above.
45-
claude_args: "--model 'Claude Sonnet 4.6' --max-turns 10"
46-
47-
interactive:
48-
if: |
49-
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
50-
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude'))
51-
runs-on: self-hosted
52-
timeout-minutes: 15
53-
steps:
54-
- uses: actions/checkout@v4
55-
with:
56-
fetch-depth: 0
30+
CLAUDE_MODEL: "Claude Sonnet 4.6"
31+
SKILL_PATH: "skills/loongforge-review/SKILL.md"
32+
DIFF_PATH: "/tmp/pr-diff.txt"
33+
PR_TITLE: ${{ github.event.pull_request.title }}
34+
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
35+
run: |
36+
python3 .github/scripts/claude-review.py > /tmp/review-result.md
5737
58-
- uses: anthropics/claude-code-action@v1
38+
- name: Post review comment
5939
env:
60-
ANTHROPIC_BASE_URL: ${{ secrets.ANTHROPIC_BASE_URL }}
61-
with:
62-
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
63-
claude_args: "--model 'Claude Sonnet 4.6' --max-turns 10"
40+
GH_TOKEN: ${{ github.token }}
41+
run: |
42+
{
43+
echo '## 🤖 Claude Code Review'
44+
echo ''
45+
cat /tmp/review-result.md
46+
echo ''
47+
echo '---'
48+
echo '*Automated review by Claude Sonnet 4.6 · [Review Skill](skills/loongforge-review/SKILL.md)*'
49+
} > /tmp/review-comment.md
50+
gh pr comment ${{ github.event.pull_request.number }} --body-file /tmp/review-comment.md

0 commit comments

Comments
 (0)