Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
231 changes: 231 additions & 0 deletions .github/workflows/goose-pr-reviewer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
# goose PR Reviewer
#
# Automated PR review using goose AI agent.
#
# Trigger: Comment "/goose [optional instructions]" on a PR (OWNER/MEMBER only)
#
# Examples:
# /goose
# /goose focus on security implications
# /goose check error handling in src/auth/
#
# Required Secrets:
# - ANTHROPIC_API_KEY: API key for Anthropic
#
# Optional Variables:
# - GOOSE_PROVIDER: LLM provider (default: anthropic)
# - GOOSE_MODEL: Model name (default: claude-sonnet-4-5)
#
# Security:
# - PR content could prompt-inject the agent; only trigger on PRs you trust.
# - Do not add workflow_dispatch: API calls fetch mutable data, enabling TOCTOU attacks.

name: goose PR Reviewer

on:
issue_comment:
types: [created]

env:
GOOSE_RECIPE: |
version: "1.0.0"
title: "Review Pull Request"
description: "Review PR #${PR_NUMBER}"

extensions:
- type: builtin
name: developer
- type: platform
name: todo

instructions: |
You are a code reviewer. Your job is to evaluate code, not implement changes.

Principles:
- Be constructive and specific in feedback
- Reference exact files and line numbers (format: path/file.rs:42)
- Categorize issues by severity
- Respect project conventions (AGENTS.md)
- Never modify code - this is a read-only review
- Your context degrades. The TODO is your memory. Update it after each step.

Issue Categories:
- 🔴 BLOCKING: Must fix before merge (bugs, security, breaking changes)
- 🟡 WARNING: Should fix (performance, conventions, missing tests)
- 🟢 SUGGESTION: Nice to have (style, refactoring opportunities)
- ✅ HIGHLIGHT: Good practices to acknowledge

prompt: |
Review PR #${PR_NUMBER}: ${PR_TITLE}

The PR metadata is saved at /tmp/pr.json
The PR diff is saved at /tmp/pr.diff

The PR branch is already checked out. You are in the repository directory.
Use the codebase to understand context - explore with analyze, rg, and file reads.

Reviewer instructions from trigger:
${REVIEW_INSTRUCTIONS}

Write this to your TODO immediately and update as you progress:

## Phase 1: Understand
- [ ] Read /tmp/pr.json for PR description and context
- [ ] Read /tmp/pr.diff for the actual changes
- [ ] Read AGENTS.md if it exists
- [ ] Note the reviewer instructions above - prioritize any specific requests
- [ ] Identify the intent and scope of the PR

## Phase 2: Analyze Code
- [ ] Explore the codebase to understand context (use analyze, rg, read files)
- [ ] Review for correctness and logic errors
- [ ] Check for security vulnerabilities
- [ ] Assess error handling
- [ ] Look for performance issues
- [ ] Verify code follows project patterns

Note: Do NOT run cargo check, cargo test, cargo fmt, or other build commands.
CI pipelines already validate those automatically. Focus on code review only.

## Phase 3: Write Review
- [ ] Write detailed review to /tmp/pr_review.md
- [ ] Include specific file:line references
- [ ] Categorize all issues by severity
- [ ] Provide actionable suggestions

Format your review in /tmp/pr_review.md as:

## Summary
[1-2 sentence overview of the PR and your assessment]

## 🔴 Blocking Issues
[Issues that must be fixed before merge, or "None found"]

## 🟡 Warnings
[Issues that should be addressed, or "None found"]

## 🟢 Suggestions
[Optional improvements, or "None"]

## ✅ Highlights
[Good practices observed in this PR]

---
*Review generated by goose*

Only write /tmp/pr_review.md when the review is complete.
Do NOT make any changes to the codebase.

permissions:
contents: read
pull-requests: write

concurrency:
group: goose-pr-review-${{ github.event.issue.number }}
cancel-in-progress: true

jobs:
review-pr:
if: |
github.event.issue.pull_request &&
startsWith(github.event.comment.body, '/goose') &&
contains(fromJSON('["OWNER", "MEMBER"]'), github.event.comment.author_association)

runs-on: ubuntu-latest
timeout-minutes: 15

container:
image: ghcr.io/block/goose:latest
options: --user root
env:
GOOSE_PROVIDER: ${{ vars.GOOSE_PROVIDER || 'anthropic' }}
GOOSE_MODEL: ${{ vars.GOOSE_MODEL || 'claude-sonnet-4-5' }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
HOME: /tmp/goose-home

steps:
- name: Acknowledge trigger
run: |
curl -sL -X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
"https://api.github.com/repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions" \
-d '{"content":"eyes"}'

- name: Checkout PR
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4
with:
ref: refs/pull/${{ github.event.issue.number }}/head
fetch-depth: 1

- name: Install tools
run: |
apt-get update
apt-get install -y gettext curl
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null
apt-get update
apt-get install -y gh

- name: Get PR diff
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr diff ${{ github.event.issue.number }} --repo ${{ github.repository }} > /tmp/pr.diff

- name: Extract review instructions
id: instructions
env:
COMMENT_BODY: ${{ github.event.comment.body }}
run: |
if [ -n "$COMMENT_BODY" ]; then
INSTRUCTIONS=$(echo "$COMMENT_BODY" | sed 's|^/goose||' | sed 's|^[[:space:]]*||' | sed 's|[[:space:]]*$||')
fi

if [ -z "$INSTRUCTIONS" ]; then
INSTRUCTIONS="No specific instructions - perform a general code review."
fi

echo "instructions<<INSTRUCTIONS_EOF" >> $GITHUB_OUTPUT
echo "$INSTRUCTIONS" >> $GITHUB_OUTPUT
echo "INSTRUCTIONS_EOF" >> $GITHUB_OUTPUT

- name: Run goose review
id: goose
env:
PR_NUMBER: ${{ github.event.issue.number }}
PR_TITLE: ${{ github.event.issue.title }}
PR_BODY: ${{ github.event.issue.body }}
REVIEW_INSTRUCTIONS: ${{ steps.instructions.outputs.instructions }}
run: |
mkdir -p $HOME/.local/share/goose/sessions
mkdir -p $HOME/.config/goose
git config --global --add safe.directory "$GITHUB_WORKSPACE"

cat > /tmp/pr.json << 'PRJSON'
${{ toJson(github.event.issue) }}
PRJSON

echo "$GOOSE_RECIPE" | envsubst '$PR_NUMBER $PR_TITLE $PR_BODY $REVIEW_INSTRUCTIONS' > /tmp/recipe.yaml

goose run --recipe /tmp/recipe.yaml

if [ -f /tmp/pr_review.md ]; then
echo "has_review=true" >> $GITHUB_OUTPUT
else
echo "has_review=false" >> $GITHUB_OUTPUT
fi

- name: Post review comment
if: steps.goose.outputs.has_review == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr comment ${{ github.event.issue.number }} --repo ${{ github.repository }} --body-file /tmp/pr_review.md

- name: Post failure comment
if: failure() || steps.goose.outputs.has_review != 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr comment ${{ github.event.issue.number }} --repo ${{ github.repository }} --body "⚠️ goose PR review could not be completed. Check the [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details."
Loading