name: Code Review on: # pull_request_target: # types: [opened, synchronize] workflow_dispatch: inputs: pr_number: description: 'Pull request number to review' required: true type: number instructions: description: 'Instructions for the code review' required: false type: string default: 'Review the changes for correctness' workflow_call: inputs: pr_number: description: 'Pull request number to review' required: true type: number instructions: description: 'Instructions for the code review' required: false type: string default: 'Review the changes for correctness' secrets: ANTHROPIC_API_KEY: required: true permissions: contents: read pull-requests: write concurrency: group: code-review-${{ github.event.pull_request.number || inputs.pr_number }} cancel-in-progress: true jobs: review-pr: runs-on: ubuntu-latest timeout-minutes: 15 container: image: ghcr.io/block/goose:latest options: --user root env: HOME: /tmp/goose-home steps: - name: Checkout base branch uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Install tools run: | apt-get update apt-get install -y gettext curl ripgrep jq 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 git config --global --add safe.directory "$GITHUB_WORKSPACE" - name: Get PR metadata and diff env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }} run: | mkdir -p /tmp/code-review TITLE=$(gh pr view "$PR_NUMBER" --json title --jq '.title') BODY=$(gh pr view "$PR_NUMBER" --json body --jq '.body // "(no description)"') COMMIT_ID=$(gh pr view "$PR_NUMBER" --json headRefOid --jq '.headRefOid') echo "COMMIT_ID=$COMMIT_ID" >> "$GITHUB_ENV" printf '# %s\n\n## Description\n\n%s\n' "$TITLE" "$BODY" > /tmp/code-review/pr.md gh pr diff "$PR_NUMBER" > /tmp/code-review/pr.diff - name: Run goose review id: goose env: PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }} REVIEW_INSTRUCTIONS: ${{ inputs.instructions }} GOOSE_PROVIDER: ${{ vars.GOOSE_PROVIDER || 'anthropic' }} GOOSE_MODEL: ${{ vars.GOOSE_MODEL || 'claude-sonnet-4-20250514' }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GOOSE_SHELL_ENV_SCRUB: ANTHROPIC_API_KEY,GITHUB_TOKEN GOOSE_SKIP_CONTEXT_FILES: AGENTS.md GOOSE_SHELL_ALLOWED_COMMANDS: cat,grep,rg,head,tail,wc,ls,git run: | mkdir -p "$HOME/.local/share/goose/sessions" mkdir -p "$HOME/.config/goose" goose run \ --recipe .github/recipes/code-review.yaml \ --params pr_directory=/tmp/code-review \ --params instructions="$REVIEW_INSTRUCTIONS" - name: Upload review artifacts if: always() uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 with: name: code-review-output path: | /tmp/conclusion.json /tmp/comments.json if-no-files-found: ignore retention-days: 30 - name: Submit review env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }} run: | CONCLUSION="/tmp/conclusion.json" COMMENTS="/tmp/comments.json" if [ ! -f "$COMMENTS" ]; then echo "No comments produced, skipping review submission." exit 0 fi # Default conclusion if finish_review was never called if [ ! -f "$CONCLUSION" ]; then echo '{"body":"","event":"COMMENT"}' > "$CONCLUSION" fi # Build payload using jq file inputs to avoid shell interpolation issues # Omit body when empty for a cleaner review jq -n \ --arg commit_id "$COMMIT_ID" \ --slurpfile conclusion "$CONCLUSION" \ --slurpfile comments "$COMMENTS" \ '{ commit_id: $commit_id, event: $conclusion[0].event, comments: $comments[0] } | if $conclusion[0].body != "" then .body = $conclusion[0].body else . end ' > /tmp/review-payload.json # Try submitting with inline comments first if gh api \ --method POST \ -H "Accept: application/vnd.github+json" \ "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/reviews" \ --input /tmp/review-payload.json; then echo "Review submitted with inline comments." exit 0 fi echo "Inline comments failed, folding into review body." # Fold inline comments into the review body as a fallback jq -n \ --arg commit_id "$COMMIT_ID" \ --slurpfile conclusion "$CONCLUSION" \ --slurpfile comments "$COMMENTS" \ '{ commit_id: $commit_id, body: ( $conclusion[0].body + "\n\n---\n\n## Inline Comments\n\n" + ([$comments[0][] | "### `" + .path + "` (line " + (.line | tostring) + ")\n\n" + .body ] | join("\n\n---\n\n")) ), event: $conclusion[0].event }' \ | gh api \ --method POST \ -H "Accept: application/vnd.github+json" \ "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/reviews" \ --input -