Copilot Review Poller #3259
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Copilot Review Poller | |
| on: | |
| schedule: | |
| - cron: '*/5 * * * *' | |
| workflow_dispatch: {} | |
| permissions: | |
| actions: write | |
| pull-requests: read | |
| issues: read | |
| concurrency: | |
| group: copilot-poller | |
| cancel-in-progress: false | |
| jobs: | |
| poll: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Find unprocessed Copilot reviews | |
| uses: actions/github-script@v8 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const owner = context.repo.owner; | |
| const repo = context.repo.repo; | |
| const { data: repoInfo } = await github.rest.repos.get({ owner, repo }); | |
| const defaultBranch = repoInfo.default_branch; | |
| // Fetch open PRs with ai-fix label | |
| const issues = await github.paginate(github.rest.issues.listForRepo, { | |
| owner, repo, state: 'open', labels: 'ai-fix', per_page: 100, | |
| }); | |
| for (const issue of issues) { | |
| if (!issue.pull_request) continue; | |
| try { | |
| const { data: pr } = await github.rest.pulls.get({ | |
| owner, repo, pull_number: issue.number, | |
| }); | |
| // Apply the same guards as the fix workflow | |
| if (pr.draft) continue; | |
| if (pr.user.login !== 'gsarig') continue; | |
| if (pr.base.ref === defaultBranch) continue; | |
| if (pr.head.repo.fork) continue; | |
| // Get all reviews on this PR | |
| const reviews = await github.paginate(github.rest.pulls.listReviews, { | |
| owner, repo, pull_number: issue.number, per_page: 100, | |
| }); | |
| const copilotReviews = reviews.filter( | |
| r => r.user.login === 'copilot-pull-request-reviewer[bot]' && r.user.type === 'Bot' | |
| ); | |
| if (copilotReviews.length === 0) continue; | |
| // Get existing PR comments to detect already-processed reviews | |
| const comments = await github.paginate(github.rest.issues.listComments, { | |
| owner, repo, issue_number: issue.number, per_page: 100, | |
| }); | |
| // Fetch all inline comments once per PR (reused across reviews) | |
| const allInlineComments = await github.paginate( | |
| github.rest.pulls.listReviewComments, | |
| { owner, repo, pull_number: issue.number, per_page: 100 }, | |
| ); | |
| // Process most recent Copilot review first | |
| for (const review of [...copilotReviews].reverse()) { | |
| const marker = `<!-- copilot-cr-fix:${review.id} -->`; | |
| if (comments.some(c => c.body && c.body.includes(marker))) { | |
| core.info(`PR #${issue.number} review ${review.id} already processed — skipping.`); | |
| continue; | |
| } | |
| // Check the review has inline comments | |
| const reviewComments = allInlineComments.filter( | |
| c => c.pull_request_review_id === review.id | |
| ); | |
| if (reviewComments.length === 0) { | |
| core.info(`PR #${issue.number} review ${review.id} has no inline comments — skipping.`); | |
| continue; | |
| } | |
| core.info(`Triggering fix for PR #${issue.number}, review ${review.id}`); | |
| await github.rest.actions.createWorkflowDispatch({ | |
| owner, repo, | |
| workflow_id: 'copilot-cr-fix.yml', | |
| ref: defaultBranch, | |
| inputs: { | |
| pr_number: String(issue.number), | |
| review_id: String(review.id), | |
| head_ref: pr.head.ref, | |
| }, | |
| }); | |
| break; // one review at a time per PR | |
| } | |
| } catch (err) { | |
| core.warning(`Error processing PR #${issue.number}: ${err.message || err}`); | |
| } | |
| } |