Repository navigation
Add a GitHub Actions workflow that reviews pull requests #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same-repo check does not cover PRs opened by Dependabot. For those, |
||
| 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 }} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
| 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 }}". | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The workflow works around claude-code-action#1003 by asking the model to call the Skill tool. If the model skips the call or mangles the arguments, the run still exits successfully, so the review can silently post nothing. No step checks that a comment was actually created. |
||
| 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" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| # 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") | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This check only confirms that a |
||
| | .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" | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The concurrency group is evaluated before the job's
if:. A second event for the same PR (reopened,ready_for_review) can cancel a review in progress, and if that second run's job is then skipped, nothing replaces the cancelled review.