Files
mars3142 81207143f3 initial
Signed-off-by: Peter Siegmund <developer@mars3142.org>
2026-04-28 16:07:35 +02:00

154 lines
5.3 KiB
YAML

name: Claude Code Review
description: >
Reviews pull requests using Claude AI. Reads review instructions from a
prompt file in the target branch and posts the result as a PR comment.
author: mars3142
inputs:
api_key:
description: Anthropic API key
required: true
gitea_token:
description: Gitea token for posting PR comments
required: true
prompt_file:
description: Path to the review prompt file inside the target branch
required: false
default: .claude/review-prompt.md
model:
description: Claude model ID
required: false
default: claude-sonnet-4-6
runs:
using: composite
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fetch PR branches
shell: bash
run: |
git fetch origin "${{ github.base_ref }}"
git fetch origin "${{ github.head_ref }}" 2>/dev/null || \
git fetch origin \
"refs/pull/${{ github.event.pull_request.number }}/head:pr/${{ github.event.pull_request.number }}"
- name: Read prompt file from target branch
shell: bash
run: |
PROMPT_FILE="${{ inputs.prompt_file }}"
if ! git show "origin/${{ github.base_ref }}:${PROMPT_FILE}" \
> /tmp/claude-review-prompt.txt 2>/dev/null; then
echo "::error::Prompt file '${PROMPT_FILE}' not found in branch '${{ github.base_ref }}'"
exit 1
fi
echo "Prompt loaded from '${PROMPT_FILE}' ($(wc -c < /tmp/claude-review-prompt.txt) bytes)"
- name: Generate PR diff
shell: bash
run: |
HEAD_REF="${{ github.head_ref }}"
BASE_REF="origin/${{ github.base_ref }}"
if git diff "${BASE_REF}...origin/${HEAD_REF}" \
> /tmp/claude-review-diff.txt 2>/dev/null; then
true
else
git diff "${BASE_REF}" \
"pr/${{ github.event.pull_request.number }}" \
> /tmp/claude-review-diff.txt
fi
DIFF_SIZE=$(wc -c < /tmp/claude-review-diff.txt)
if [ "$DIFF_SIZE" -eq 0 ]; then
echo "::notice::Empty diff — skipping review"
echo "CLAUDE_SKIP=true" >> "$GITHUB_ENV"
exit 0
fi
# Truncate at 80 KB to stay within API limits
if [ "$DIFF_SIZE" -gt 81920 ]; then
head -c 81920 /tmp/claude-review-diff.txt > /tmp/claude-review-diff-trunc.txt
printf '\n\n[... diff truncated at 80 KB ...]\n' >> /tmp/claude-review-diff-trunc.txt
mv /tmp/claude-review-diff-trunc.txt /tmp/claude-review-diff.txt
echo "::warning::Diff truncated to 80 KB"
fi
echo "Diff ready ($(wc -c < /tmp/claude-review-diff.txt) bytes)"
- name: Call Claude API
if: env.CLAUDE_SKIP != 'true'
shell: bash
env:
CLAUDE_API_KEY: ${{ inputs.api_key }}
run: |
jq -n \
--arg model "${{ inputs.model }}" \
--arg prompt "$(cat /tmp/claude-review-prompt.txt)" \
--arg diff "$(cat /tmp/claude-review-diff.txt)" \
'{
model: $model,
max_tokens: 4096,
messages: [{
role: "user",
content: ($prompt + "\n\n---\n\n## Pull Request Diff\n\n```diff\n" + $diff + "\n```")
}]
}' > /tmp/claude-review-request.json
HTTP_STATUS=$(curl -s -w "%{http_code}" -o /tmp/claude-review-response.json \
-X POST "https://api.anthropic.com/v1/messages" \
-H "x-api-key: ${CLAUDE_API_KEY}" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d @/tmp/claude-review-request.json)
if [ "$HTTP_STATUS" -ne 200 ]; then
echo "::error::Claude API returned HTTP ${HTTP_STATUS}"
jq -r '.error.message // .' /tmp/claude-review-response.json
exit 1
fi
REVIEW=$(jq -r '.content[0].text' /tmp/claude-review-response.json)
if [ -z "$REVIEW" ] || [ "$REVIEW" = "null" ]; then
echo "::error::Empty response from Claude API"
cat /tmp/claude-review-response.json
exit 1
fi
printf '%s' "$REVIEW" > /tmp/claude-review-result.txt
echo "Review ready ($(wc -c < /tmp/claude-review-result.txt) bytes)"
- name: Post review comment
if: env.CLAUDE_SKIP != 'true'
shell: bash
env:
GITEA_TOKEN: ${{ inputs.gitea_token }}
run: |
jq -n \
--arg review "$(cat /tmp/claude-review-result.txt)" \
--arg model "${{ inputs.model }}" \
'{body: ("## Claude Code Review\n\n" + $review + "\n\n---\n*Review generated by `" + $model + "`*")}' \
> /tmp/claude-review-comment.json
HTTP_STATUS=$(curl -s -w "%{http_code}" -o /tmp/claude-review-comment-response.json \
-X POST \
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d @/tmp/claude-review-comment.json)
if [ "$HTTP_STATUS" -ne 201 ]; then
echo "::error::Failed to post comment (HTTP ${HTTP_STATUS})"
cat /tmp/claude-review-comment-response.json
exit 1
fi
echo "Review posted to PR #${{ github.event.pull_request.number }}"