Unverified 提交 9587e79f authored 作者: Will Chen's avatar Will Chen 提交者: GitHub

Add retry loop support to PR review responder workflow (#2493)

## Summary Enhanced the PR review responder workflow to support multiple retry iterations (up to 3) for Claude Code review requests. The workflow now tracks retry attempts using numbered labels (`cc:request:N`) and automatically re-requests reviews when new commits are pushed, while escalating to human review after max retries are exhausted. ## Key Changes - **Retry tracking**: Replaced simple `cc:request` label detection with support for `cc:request` (initial) and `cc:request:N` (re-request iterations) labels - **Max retry guard**: Added logic to prevent infinite loops by capping retries at 3 attempts; PRs reaching this limit are labeled with `cc:needs-human-review` for manual intervention - **Dynamic label management**: Updated label removal/addition to use the current label dynamically instead of hardcoding `cc:request` - **Conditional re-triggering**: Split the final label update into two paths: - If commits were pushed: increment the request count and apply `cc:request:N` to auto-re-request review - If no commits were pushed: apply `cc:done` label to mark completion - **Output tracking**: Added `commits_pushed` output to the retrigger step to enable conditional downstream steps - **Label creation**: Ensured next iteration labels are created before being applied to PRs ## Implementation Details - Request count is parsed from label names using regex matching (`/^cc:request:(\d+)$/`) - The workflow maintains state through GitHub labels, allowing it to resume correctly even if interrupted - Label creation attempts are wrapped in try-catch to gracefully handle existing labels - The retry loop is bounded at 3 iterations to prevent runaway automation https://claude.ai/code/session_01KNj2M6wfeQKjTPmkgGP5dt <!-- devin-review-badge-begin --> --- <a href="https://app.devin.ai/review/dyad-sh/dyad/pull/2493"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1"> <img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open with Devin"> </picture> </a> <!-- devin-review-badge-end --> <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes a privileged GitHub Actions workflow’s control flow and label automation, which could cause unexpected re-trigger loops or incorrect PR state if misconfigured. Risk is bounded by the new max-retry guard and explicit `commits_pushed` gating. > > **Overview** > Adds **bounded auto-retry support** to `pr-review-responder.yml` by recognizing `cc:request` *and* numbered `cc:request:N` labels, tracking the current iteration via step outputs. > > When Claude pushes new commits, the workflow now emits a `commits_pushed` output, creates the next `cc:request:N` label if needed, and swaps `cc:pending` to the incremented request label to auto re-request review; if no commits were pushed it continues to mark the PR `cc:done`. > > Introduces a **max retry guard (>=3)** that removes the current request label and escalates the PR to `cc:needs-human-review` (creating the label if missing) to prevent infinite automation loops. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit d68ddd159d6160b221c03e862dedf5f2585c6573. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Adds an automatic re-request loop to the PR review responder so Claude reviews are re-requested on new commits. Iterations are tracked with cc:request:N labels (max 3), escalating to cc:needs-human-review at the limit or cc:done if no new commits. - **New Features** - Detect and parse cc:request / cc:request:N; pick highest count; expose request_count and current_label outputs. - Use dynamic label management: remove current_label, add cc:pending while processing. - On new commits: create next cc:request:N, apply it, retrigger via workflow_dispatch; set commits_pushed output. - On no commits: remove cc:pending and add cc:done. - After 3 retries: remove current_label and cc:pending, add cc:needs-human-review. <sup>Written for commit 1beb4f648e64628aaa8f59c5b0e1d6b315604212. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. --> --------- Co-authored-by: 's avatarClaude <noreply@anthropic.com>
上级 88c03faf
......@@ -74,17 +74,83 @@ jobs:
return;
}
const hasRequestLabel = pr.labels.some(label => label.name === 'cc:request');
// Check for cc:request (initial) or cc:request:N (re-request) labels
// If multiple labels exist, use the highest count to avoid resetting the retry counter
let requestCount = -1;
let currentLabel = '';
for (const label of pr.labels) {
if (label.name === 'cc:request') {
if (requestCount < 0) {
requestCount = 0;
currentLabel = 'cc:request';
}
}
const match = label.name.match(/^cc:request:(\d+)$/);
if (match) {
const count = parseInt(match[1], 10);
if (count > requestCount) {
requestCount = count;
currentLabel = label.name;
}
}
}
if (requestCount === -1) {
console.log(`PR #${prNumber} does not have a cc:request or cc:request:N label`);
core.setOutput('should_continue', 'false');
return;
}
// Guard: do not loop more than 3 retries (4 total runs: initial + 3 retries)
// cc:request (0) -> cc:request:1 (1) -> cc:request:2 (2) -> cc:request:3 (3) -> blocked
if (requestCount >= 4) {
console.log(`PR #${prNumber} has reached max retry count (${requestCount}), adding cc:needs-human-review`);
// Ensure the label exists
try {
await github.rest.issues.createLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: 'cc:needs-human-review',
color: 'd93f0b',
description: 'Claude Code has exhausted auto-retries; needs human review'
});
} catch (e) {
// Label already exists, ignore
}
// Remove current label, cc:pending, and add needs-human-review
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
name: currentLabel
}).catch(() => {});
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
name: 'cc:pending'
}).catch(() => {});
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
labels: ['cc:needs-human-review']
});
if (!hasRequestLabel) {
console.log(`PR #${prNumber} does not have the cc:request label`);
core.setOutput('should_continue', 'false');
return;
}
console.log(`PR #${prNumber} has cc:request label, proceeding with pr-fix`);
console.log(`PR #${prNumber} has ${currentLabel} label (count=${requestCount}), proceeding with pr-fix`);
core.setOutput('pr_number', prNumber);
core.setOutput('should_continue', 'true');
core.setOutput('request_count', requestCount);
core.setOutput('current_label', currentLabel);
- name: Checkout repository
if: steps.pr-info.outputs.should_continue == 'true'
......@@ -106,7 +172,7 @@ jobs:
- name: Update labels to pending
if: steps.pr-info.outputs.should_continue == 'true'
run: |
gh pr edit ${{ steps.pr-info.outputs.pr_number }} --repo ${{ github.repository }} --remove-label "cc:request" --add-label "cc:pending"
gh pr edit ${{ steps.pr-info.outputs.pr_number }} --repo ${{ github.repository }} --remove-label "${{ steps.pr-info.outputs.current_label }}" --add-label "cc:pending"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
......@@ -122,6 +188,7 @@ jobs:
- name: Re-trigger workflows if commits were pushed
# Use always() to ensure commits get tested even if Claude Code fails partway through
if: steps.pr-info.outputs.should_continue == 'true' && always()
id: retrigger
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
......@@ -137,6 +204,7 @@ jobs:
if [ "${{ steps.before-claude.outputs.sha }}" != "$PR_HEAD_SHA" ]; then
echo "Claude pushed new commits (before: ${{ steps.before-claude.outputs.sha }}, after: $PR_HEAD_SHA)"
echo "Re-triggering workflows via workflow_dispatch"
echo "commits_pushed=true" >> $GITHUB_OUTPUT
# Use workflow_dispatch to trigger workflows - this works with GITHUB_TOKEN unlike PR events
# which are blocked to prevent infinite loops
#
......@@ -163,10 +231,42 @@ jobs:
|| echo "::warning::Failed to trigger Claude PR Review workflow"
else
echo "No new commits pushed, skipping workflow re-triggers"
echo "commits_pushed=false" >> $GITHUB_OUTPUT
fi
- name: Update labels to done
if: steps.pr-info.outputs.should_continue == 'true' && success()
- name: Ensure next request label exists
# Use always() so retry loop continues even if Claude Code fails after pushing commits
if: steps.pr-info.outputs.should_continue == 'true' && always() && steps.retrigger.outputs.commits_pushed == 'true'
uses: actions/github-script@v7
with:
script: |
const nextCount = parseInt('${{ steps.pr-info.outputs.request_count }}', 10) + 1;
const labelName = `cc:request:${nextCount}`;
try {
await github.rest.issues.createLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: labelName,
color: '1d76db',
description: `Claude Code auto-re-request iteration ${nextCount}`
});
} catch (e) {
// Label already exists, ignore
}
- name: Update labels - re-request review (commits pushed)
# Use always() so retry loop continues even if Claude Code fails after pushing commits
if: steps.pr-info.outputs.should_continue == 'true' && always() && steps.retrigger.outputs.commits_pushed == 'true'
run: |
REQUEST_COUNT="${{ steps.pr-info.outputs.request_count }}"
NEXT_COUNT=$(( ${REQUEST_COUNT:-0} + 1 ))
echo "Setting cc:request:${NEXT_COUNT} to auto-re-request review (retry ${NEXT_COUNT} of 3)"
gh pr edit ${{ steps.pr-info.outputs.pr_number }} --repo ${{ github.repository }} --remove-label "cc:pending" --add-label "cc:request:${NEXT_COUNT}"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Update labels to done (no new commits)
if: steps.pr-info.outputs.should_continue == 'true' && success() && steps.retrigger.outputs.commits_pushed == 'false'
run: |
gh pr edit ${{ steps.pr-info.outputs.pr_number }} --repo ${{ github.repository }} --remove-label "cc:pending" --add-label "cc:done"
env:
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论