|
3 | 3 | # "good first task" or "help wanted" are exempt. Pull requests are never |
4 | 4 | # touched. The workflow runs daily and can also be triggered manually. |
5 | 5 | # |
6 | | -# Additionally, whenever the author of an issue or pull request comments, or |
7 | | -# new commits are pushed to their pull request, the "awaiting response" |
8 | | -# label is removed automatically so the item no longer counts as awaiting a |
9 | | -# reply. |
| 6 | +# Additionally, whenever the author of an issue or pull request comments, |
| 7 | +# new commits are pushed to their pull request, or the author re-requests a |
| 8 | +# review, the "awaiting response" label is removed automatically so the item |
| 9 | +# no longer counts as awaiting a reply. The daily run also sweeps open pull |
| 10 | +# requests whose author re-requested a review while the label was present, |
| 11 | +# covering re-requests the event triggers missed. |
10 | 12 | name: Stale issues |
11 | 13 |
|
12 | 14 | on: |
|
16 | 18 | issue_comment: |
17 | 19 | types: [created] |
18 | 20 | pull_request_target: |
19 | | - types: [synchronize] |
| 21 | + types: [synchronize, review_requested] |
20 | 22 |
|
21 | 23 | permissions: |
22 | 24 | contents: read |
23 | 25 | issues: write |
| 26 | + pull-requests: read |
24 | 27 |
|
25 | 28 | concurrency: |
26 | 29 | group: stale |
|
51 | 54 | days-before-pr-close: -1 |
52 | 55 | operations-per-run: 100 |
53 | 56 |
|
| 57 | + sweep-awaiting-response: |
| 58 | + # Reconcile open PRs whose author re-requested a review while the label |
| 59 | + # was present, covering review-request events the triggers missed. |
| 60 | + if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' |
| 61 | + runs-on: ubuntu-latest |
| 62 | + timeout-minutes: 10 |
| 63 | + steps: |
| 64 | + - uses: actions/github-script@v7 |
| 65 | + with: |
| 66 | + script: | |
| 67 | + const LABEL = 'awaiting response' |
| 68 | + const prs = await github.paginate(github.rest.pulls.list, { |
| 69 | + owner: context.repo.owner, |
| 70 | + repo: context.repo.repo, |
| 71 | + state: 'open', |
| 72 | + per_page: 100 |
| 73 | + }) |
| 74 | + for (const pr of prs) { |
| 75 | + if (!pr.labels.some((l) => l.name === LABEL)) continue |
| 76 | + const events = await github.paginate( |
| 77 | + github.rest.issues.listEventsForTimeline, |
| 78 | + { |
| 79 | + owner: context.repo.owner, |
| 80 | + repo: context.repo.repo, |
| 81 | + issue_number: pr.number, |
| 82 | + per_page: 100 |
| 83 | + } |
| 84 | + ) |
| 85 | + const lastLabeled = events.findLast( |
| 86 | + (e) => e.event === 'labeled' && e.label?.name === LABEL |
| 87 | + ) |
| 88 | + const reRequested = |
| 89 | + lastLabeled && |
| 90 | + events.some( |
| 91 | + (e) => |
| 92 | + e.event === 'review_requested' && |
| 93 | + e.actor?.login === pr.user.login && |
| 94 | + new Date(e.created_at) > new Date(lastLabeled.created_at) |
| 95 | + ) |
| 96 | + if (!reRequested) continue |
| 97 | + core.info(`Removing "${LABEL}" from #${pr.number}`) |
| 98 | + try { |
| 99 | + await github.rest.issues.removeLabel({ |
| 100 | + owner: context.repo.owner, |
| 101 | + repo: context.repo.repo, |
| 102 | + issue_number: pr.number, |
| 103 | + name: LABEL |
| 104 | + }) |
| 105 | + } catch (error) { |
| 106 | + // An event-driven job may have removed the label after the |
| 107 | + // sweep listed this PR; don't let that abort the loop. |
| 108 | + if (error.status !== 403 && error.status !== 404) { |
| 109 | + throw error |
| 110 | + } |
| 111 | + } |
| 112 | + } |
| 113 | +
|
54 | 114 | remove-awaiting-response: |
55 | 115 | # When the author of the issue/PR comments, clear the awaiting response label. |
56 | 116 | if: > |
@@ -80,9 +140,14 @@ jobs: |
80 | 140 | } |
81 | 141 |
|
82 | 142 | remove-awaiting-response-pr: |
83 | | - # Any push to the PR clears the awaiting response label. |
| 143 | + # Any push to the PR, or the PR author re-requesting a review, clears |
| 144 | + # the awaiting response label. review_requested also fires when a |
| 145 | + # maintainer adds a reviewer, so it only counts when triggered by the |
| 146 | + # author. |
84 | 147 | if: > |
85 | 148 | github.event_name == 'pull_request_target' && |
| 149 | + (github.event.action == 'synchronize' || |
| 150 | + github.event.sender.login == github.event.pull_request.user.login) && |
86 | 151 | contains(github.event.pull_request.labels.*.name, 'awaiting response') |
87 | 152 | runs-on: ubuntu-latest |
88 | 153 | timeout-minutes: 10 |
|
0 commit comments