name: pr-review on: pull_request_target: types: [opened, synchronize, reopened, ready_for_review] issue_comment: types: [created] pull_request_review_comment: types: [created] concurrency: # PR conversation comments arrive as `issue_comment` events, so their PR number # is exposed as `github.event.issue.number`. group: pr-review-${{ github.event.pull_request.number || github.event.issue.number }} cancel-in-progress: true jobs: review: if: | (github.event_name == 'pull_request_target' && github.event.pull_request.draft == false) || (github.event_name == 'issue_comment' && github.event.issue.pull_request && (startsWith(github.event.comment.body, '/oc-review') || contains(github.event.comment.body, ' /oc-review'))) || (github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/oc-review') || contains(github.event.comment.body, ' /oc-review'))) runs-on: ubuntu-latest permissions: contents: read pull-requests: write issues: write steps: - name: Checkout repository uses: actions/checkout@v6 with: fetch-depth: 1 - name: Resolve pull request context id: pr env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} EVENT_PR_NUMBER: ${{ github.event.pull_request.number || github.event.issue.number }} run: | pr_json="$(gh pr view "$EVENT_PR_NUMBER" --json number,url,title,body,author,baseRefName,headRefName,headRepositoryOwner,isDraft)" if [ "$(printf '%s' "$pr_json" | jq -r '.isDraft')" = "true" ]; then echo "draft=true" >> "$GITHUB_OUTPUT" exit 0 fi { echo "draft=false" echo "number=$(printf '%s' "$pr_json" | jq -r '.number')" echo "url=$(printf '%s' "$pr_json" | jq -r '.url')" echo "title=$(printf '%s' "$pr_json" | jq -r '.title')" echo "author=$(printf '%s' "$pr_json" | jq -r '.author.login')" echo "base_ref=$(printf '%s' "$pr_json" | jq -r '.baseRefName')" echo "head_ref=$(printf '%s' "$pr_json" | jq -r '.headRefName')" echo "head_repo_owner=$(printf '%s' "$pr_json" | jq -r '.headRepositoryOwner.login')" echo "body<> "$GITHUB_OUTPUT" - name: Check review safety if: steps.pr.outputs.draft == 'false' id: safety env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_NUMBER: ${{ steps.pr.outputs.number }} run: | changed_sensitive_files="$(gh pr diff "$PR_NUMBER" --name-only | grep -E '^(\.github/workflows/pr-review\.yml|\.opencode/agent/pr-review\.md)$' || true)" if [ -n "$changed_sensitive_files" ]; then { echo "safe=false" echo "changed_sensitive_files<> "$GITHUB_OUTPUT" exit 0 fi echo "safe=true" >> "$GITHUB_OUTPUT" - name: Skip unsafe review if: steps.pr.outputs.draft == 'false' && steps.safety.outputs.safe != 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_NUMBER: ${{ steps.pr.outputs.number }} CHANGED_SENSITIVE_FILES: ${{ steps.safety.outputs.changed_sensitive_files }} run: | gh pr comment "$PR_NUMBER" --body "

Code Review Skipped

Automated review was skipped because this PR changes review automation files: \`\`\` $CHANGED_SENSITIVE_FILES \`\`\` A maintainer should review those changes manually before running automated review." - name: Install opencode if: steps.pr.outputs.draft == 'false' && steps.safety.outputs.safe == 'true' run: curl -fsSL https://opencode.ai/install | bash - name: Review pull request if: steps.pr.outputs.draft == 'false' && steps.safety.outputs.safe == 'true' env: OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PR_URL: ${{ steps.pr.outputs.url }} PR_NUMBER: ${{ steps.pr.outputs.number }} PR_TITLE: ${{ steps.pr.outputs.title }} PR_BODY: ${{ steps.pr.outputs.body }} PR_AUTHOR: ${{ steps.pr.outputs.author }} PR_BASE_REF: ${{ steps.pr.outputs.base_ref }} PR_HEAD_REF: ${{ steps.pr.outputs.head_ref }} PR_HEAD_REPO_OWNER: ${{ steps.pr.outputs.head_repo_owner }} run: | opencode run --agent pr-review "A pull request in the OpenChamber repository needs code review. PR: $PR_URL Number: $PR_NUMBER Author: $PR_AUTHOR Base: $PR_BASE_REF Head: $PR_HEAD_REPO_OWNER:$PR_HEAD_REF Title: $PR_TITLE $PR_BODY"