Review Queue Manager #49
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: Review Queue Manager | |
| on: | |
| pull_request_review: | |
| types: [submitted] | |
| schedule: | |
| - cron: "0 9 * * *" | |
| permissions: | |
| pull-requests: write | |
| issues: write | |
| jobs: | |
| handle-editor-feedback: | |
| if: | | |
| github.event_name == 'pull_request_review' && | |
| github.event.review.state == 'changes_requested' && | |
| contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.review.author_association) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Label PR and post deadline comment | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const pr = context.payload.pull_request; | |
| const editor = context.payload.review.user.login; | |
| const deadline = new Date(); | |
| deadline.setDate(deadline.getDate() + 14); | |
| const deadlineStr = deadline.toISOString().split('T')[0]; | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number, | |
| labels: ['editor-feedback-pending'], | |
| }); | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr.number, | |
| body: `π **Editor feedback received** (from @${editor}).\n\n` + | |
| `@${pr.user.login}, please address the feedback and push edits by **${deadlineStr}** (2 weeks).\n` + | |
| `If no changes are made by then, this PR will be returned to the review queue.`, | |
| }); | |
| # ββ Daily check: enforce the 2-day and 2-week deadlines ββββββββββββββββββββββ | |
| check-deadlines: | |
| if: github.event_name == 'schedule' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Close PRs that missed their deadline | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const now = Date.now(); | |
| const TWO_DAYS = 2 * 24 * 60 * 60 * 1000; | |
| const TWO_WEEKS = 14 * 24 * 60 * 60 * 1000; | |
| async function getLabeledAt(prNumber, labelName) { | |
| const { data: events } = await github.rest.issues.listEvents({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| per_page: 100, | |
| }); | |
| // Return the timestamp of the most recent time this label was added | |
| const match = events.reverse().find(e => e.event === 'labeled' && e.label.name === labelName); | |
| return match ? new Date(match.created_at).getTime() : null; | |
| } | |
| async function returnToQueue(prNumber, reason) { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| body: `β° **Returned to queue.**\n\n${reason}\n\nFeel free to reopen when you're ready.`, | |
| }); | |
| await github.rest.pulls.update({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: prNumber, | |
| state: 'closed', | |
| }); | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| labels: ['returned-to-queue'], | |
| }).catch(() => {}); | |
| } | |
| // ββ Check: no edits within 2 days ββ | |
| const { data: noEditPRs } = await github.rest.issues.listForRepo({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| labels: 'needs-reviewer-edits', | |
| state: 'open', | |
| }); | |
| for (const pr of noEditPRs) { | |
| const labeledAt = await getLabeledAt(pr.number, 'needs-reviewer-edits'); | |
| if (labeledAt && now - labeledAt >= TWO_DAYS) { | |
| await returnToQueue( | |
| pr.number, | |
| 'No edits were made to the transcript body within 2 days of the warning.' | |
| ); | |
| } | |
| } | |
| // ββ Check: no response to editor feedback within 2 weeks ββ | |
| const { data: feedbackPRs } = await github.rest.issues.listForRepo({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| labels: 'editor-feedback-pending', | |
| state: 'open', | |
| }); | |
| for (const pr of feedbackPRs) { | |
| const labeledAt = await getLabeledAt(pr.number, 'editor-feedback-pending'); | |
| if (labeledAt && now - labeledAt >= TWO_WEEKS) { | |
| await returnToQueue( | |
| pr.number, | |
| 'No edits were made in response to editor feedback within 2 weeks.' | |
| ); | |
| } | |
| } |