From 92b9a9f88d80e14dce98fcdb0d111fc8ad50cc11 Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Wed, 10 Jun 2026 11:33:05 +0300 Subject: [PATCH] ci: harden manual PR review workflow --- .github/workflows/pr-review.yml | 67 +++++++++++++++++++++++++++++++-- .opencode/agent/pr-review.md | 2 +- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pr-review.yml b/.github/workflows/pr-review.yml index befe1ce0..79fcd228 100644 --- a/.github/workflows/pr-review.yml +++ b/.github/workflows/pr-review.yml @@ -10,9 +10,10 @@ on: 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 + # is exposed as `github.event.issue.number`. Keep comment-triggered runs in a + # separate group so skipped non-command comments do not cancel active reviews. + group: pr-review-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.issue.number }} + cancel-in-progress: ${{ github.event_name == 'pull_request_target' }} jobs: review: @@ -86,6 +87,30 @@ jobs: echo "safe=true" >> "$GITHUB_OUTPUT" + - name: Acknowledge manual review command + if: steps.pr.outputs.draft == 'false' && steps.safety.outputs.safe == 'true' && github.event_name != 'pull_request_target' + id: manual-reaction + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + EVENT_NAME: ${{ github.event_name }} + COMMENT_ID: ${{ github.event.comment.id }} + run: | + if [ "$EVENT_NAME" = "pull_request_review_comment" ]; then + endpoint="repos/${GITHUB_REPOSITORY}/pulls/comments/${COMMENT_ID}/reactions" + else + endpoint="repos/${GITHUB_REPOSITORY}/issues/comments/${COMMENT_ID}/reactions" + fi + + reaction_id="$(gh api \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "$endpoint" \ + -f content='eyes' \ + --jq '.id')" + + echo "endpoint=$endpoint" >> "$GITHUB_OUTPUT" + echo "reaction_id=$reaction_id" >> "$GITHUB_OUTPUT" + - name: Skip unsafe review if: steps.pr.outputs.draft == 'false' && steps.safety.outputs.safe != 'true' env: @@ -124,6 +149,8 @@ jobs: run: | opencode run --agent pr-review "A pull request in the OpenChamber repository needs code review. + This may be a repeated review request. Before writing a new review, inspect prior PR comments, bot comments, reviews, and inline comments via GitHub, then determine which previous findings are already addressed by the current diff. + PR: $PR_URL Number: $PR_NUMBER Author: $PR_AUTHOR @@ -133,3 +160,37 @@ jobs: Title: $PR_TITLE $PR_BODY" + + - name: Verify manual review comment + if: steps.pr.outputs.draft == 'false' && steps.safety.outputs.safe == 'true' && github.event_name != 'pull_request_target' + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + PR_NUMBER: ${{ steps.pr.outputs.number }} + COMMAND_CREATED_AT: ${{ github.event.comment.created_at }} + REACTION_ENDPOINT: ${{ steps.manual-reaction.outputs.endpoint }} + EYES_REACTION_ID: ${{ steps.manual-reaction.outputs.reaction_id }} + run: | + review_comment_count="$(gh api \ + "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" \ + --paginate \ + --slurp \ + --jq "map(.[]) | [.[] | select(.created_at > \"${COMMAND_CREATED_AT}\" and .user.login == \"openchamber-bot[bot]\" and (.body | contains(\"

Code Review Summary

\")))] | length")" + + if [ "$review_comment_count" -lt 1 ]; then + echo "Manual /oc-review completed without creating a new OpenChamber Bot PR comment." >&2 + exit 1 + fi + + if [ -n "$EYES_REACTION_ID" ]; then + gh api \ + --method DELETE \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "${REACTION_ENDPOINT}/${EYES_REACTION_ID}" + fi + + gh api \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "$REACTION_ENDPOINT" \ + -f content='+1' >/dev/null diff --git a/.opencode/agent/pr-review.md b/.opencode/agent/pr-review.md index 3e85ea28..3ad37bfd 100644 --- a/.opencode/agent/pr-review.md +++ b/.opencode/agent/pr-review.md @@ -92,7 +92,7 @@ Pay extra attention to: Match the repository's existing PR-review style: concise summary first, then a confidence/merge signal, then concrete findings. Do not use a header like `## OpenCode PR review`. -Leave exactly one top-level PR comment with `gh pr comment "$PR_NUMBER" --body "..."` or an equivalent `gh api` call. Do not create separate inline review comments unless the workflow explicitly asks for inline comments later. +Leave exactly one top-level PR comment with `gh pr comment "$PR_NUMBER" --body "..."` or an equivalent `gh api` call. Do not create separate inline review comments unless the workflow explicitly asks for inline comments later. Printing the review to stdout is not enough: after posting, verify that the new comment exists on the PR (for example with `gh pr view "$PR_NUMBER" --json comments`) before finishing. Use this structure: