diff --git a/.github/workflows/goose-pr-reviewer.yml b/.github/workflows/goose-pr-reviewer.yml new file mode 100644 index 000000000000..3657a59b2cd2 --- /dev/null +++ b/.github/workflows/goose-pr-reviewer.yml @@ -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<> $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."