Переглянути джерело

fix(ci): make the review bot post its findings and acknowledge mentions

Three separate ways the bot went silent after the move to the official
code-review skill:

- The skill skips a PR it has already commented on without comparing the
  reviewed head to the current one, so #6272 got no review of the commits
  pushed after the first pass. A prior review now only justifies a skip
  when its "Reviewed head:" SHA matches the current head, and never when
  the run came from an explicit "@claude review".
- The review agent launched its subagents in the background and ended its
  turn to wait for them. A headless run terminates on end_turn, so the
  findings were discarded and the job still reported success. The prompt
  now requires foreground subagents, and a new step fails the job when a
  run posts nothing for the current head, instead of passing green.
- A custom prompt puts claude-code-action in agent mode, which never adds
  the eyes reaction, so a mention gave no sign it had been picked up.
Sanaei 9 годин тому
батько
коміт
845abc380e
1 змінених файлів з 43 додано та 1 видалено
  1. 43 1
      .github/workflows/claude-bot.yml

+ 43 - 1
.github/workflows/claude-bot.yml

@@ -478,6 +478,19 @@ jobs:
       issues: read
       id-token: write
     steps:
+      - name: Record when this run started
+        id: started
+        run: echo "at=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT"
+      # A custom prompt puts the action in agent mode, which never reacts on its
+      # own, so the requester gets no sign the run started.
+      - name: Acknowledge the request
+        if: github.event_name == 'issue_comment'
+        continue-on-error: true
+        env:
+          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          REPO: ${{ github.repository }}
+          COMMENT_ID: ${{ github.event.comment.id }}
+        run: gh api "repos/${REPO}/issues/comments/${COMMENT_ID}/reactions" -f content=eyes
       - uses: actions/checkout@v7
         with:
           persist-credentials: false
@@ -494,7 +507,7 @@ jobs:
             --effort xhigh
             --max-turns 100
             --allowedTools "mcp__github_inline_comment__create_inline_comment"
-            --append-system-prompt "Before reviewing, read REVIEW.md at the repository root and follow it: it defines what counts as a blocking finding in this repository, what not to report, and the repo-specific checks."
+            --append-system-prompt "Before reviewing, read REVIEW.md at the repository root and follow it: it defines what counts as a blocking finding in this repository, what not to report, and the repo-specific checks. Two overrides apply here. First, the skip gate for already-reviewed PRs: an existing Claude review comment justifies skipping ONLY when its 'Reviewed head:' SHA equals the PR's current head SHA; when the head has moved on, or this run was triggered by an explicit '@claude review' comment, run the full review, focusing on the commits since the previously reviewed head. Second, this is a headless run that terminates the moment you end your turn: launch every subagent with run_in_background set to false and wait for its result inside the same turn - never end your turn while a subagent is still running, and never end it before the review comment is posted. A run that ends without posting the review has failed."
       - name: Upload the run transcript
         if: always()
         env:
@@ -505,6 +518,26 @@ jobs:
           path: ${{ runner.temp }}/claude-execution-output.json
           if-no-files-found: ignore
           retention-days: 7
+      - name: Fail if the review posted nothing
+        if: ${{ !cancelled() }}
+        env:
+          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          REPO: ${{ github.repository }}
+          PR: ${{ github.event.pull_request.number || github.event.issue.number }}
+          STARTED_AT: ${{ steps.started.outputs.at }}
+        run: |
+          set -euo pipefail
+          head=$(gh api "repos/${REPO}/pulls/${PR}" --jq '.head.sha')
+          # updated_at, not created_at: the skill may update its existing sticky comment.
+          # A pre-existing comment naming the current head SHA means a legitimate skip.
+          posted=$(gh api "repos/${REPO}/issues/${PR}/comments" --paginate \
+            --jq "[.[] | select(.user.login == \"github-actions[bot]\") | select((.updated_at >= \"${STARTED_AT}\") or (.body | contains(\"${head}\")))] | length")
+          inline=$(gh api "repos/${REPO}/pulls/${PR}/comments" --paginate \
+            --jq "[.[] | select(.user.login == \"github-actions[bot]\") | select(.updated_at >= \"${STARTED_AT}\")] | length")
+          if [ "$posted" = "0" ] && [ "$inline" = "0" ]; then
+            echo "::error::The review run ended without posting a review of ${head} on #${PR}. Read the uploaded transcript before re-running."
+            exit 1
+          fi
 
   mention:
     if: >-
@@ -525,6 +558,15 @@ jobs:
       pull-requests: write
       id-token: write
     steps:
+      # A custom prompt puts the action in agent mode, which never reacts on its
+      # own, so the requester gets no sign the run started.
+      - name: Acknowledge the mention
+        continue-on-error: true
+        env:
+          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          REPO: ${{ github.repository }}
+          COMMENT_ID: ${{ github.event.comment.id }}
+        run: gh api "repos/${REPO}/issues/comments/${COMMENT_ID}/reactions" -f content=eyes
       - uses: actions/checkout@v7
         with:
           fetch-depth: 0