diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml new file mode 100644 index 0000000..a32d4fb --- /dev/null +++ b/.github/workflows/code-review.yml @@ -0,0 +1,72 @@ +name: code-review + +on: + pull_request: + # Review a pull request when it opens or becomes ready, and again + # after each push to it. + types: [opened, reopened, ready_for_review, synchronize] + +permissions: + contents: read + pull-requests: write + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + review: + # GitHub withholds repository secrets from pull requests opened + # from a fork, so the review runs only on branches pushed to + # this repository. + if: >- + github.event.pull_request.draft == false && + github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + # Full history, so the review can walk each commit in the PR. + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # The action does not run a slash command given as its prompt + # (anthropics/claude-code-action#1003), so the prompt asks Claude + # to invoke the skill through the Skill tool instead. + - uses: anthropics/claude-code-action@d535f5172dec02b204866b393d6b20b9b2aaf98d # v1.0.266 + id: review + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + github_token: ${{ secrets.GITHUB_TOKEN }} + prompt: >- + Use the Skill tool to run the code-review skill with the + arguments "high --comment ${{ github.event.pull_request.number }}". + claude_args: >- + --model claude-sonnet-5-5 + --allowedTools + "Skill,Read,Grep,Glob,Agent,Bash(git log:*),Bash(git show:*),Bash(git diff:*),Bash(git blame:*),Bash(gh pr view:*),Bash(gh pr diff:*),Bash(gh pr comment:*),Bash(gh api:*),mcp__github_inline_comment__create_inline_comment" + + # A run in which Claude never invokes the skill still exits + # successfully, with no review posted. Fail the job when the + # execution log holds no Skill tool call. + - name: Check that the review ran + env: + EXECUTION_FILE: ${{ steps.review.outputs.execution_file }} + run: | + if ! jq -e '[.[] | select(.type == "assistant") + | .message.content[]? + | select(.type == "tool_use" and .name == "Skill")] + | length > 0' "$EXECUTION_FILE" > /dev/null; then + echo "::error::Claude did not invoke the code-review skill" + exit 1 + fi + + # The action hides Claude's output, so a failed run otherwise + # leaves no error text. Print only the final result message. + - name: Show why the review failed + if: failure() && steps.review.outputs.execution_file != '' + env: + EXECUTION_FILE: ${{ steps.review.outputs.execution_file }} + run: | + jq -r '.[] | select(.type == "result") | .result // empty' \ + "$EXECUTION_FILE"