From 41f913779f818203e0d48ca1e6c735cd24cefb36 Mon Sep 17 00:00:00 2001 From: Elliott de Launay Date: Mon, 15 Jun 2026 02:49:21 +0000 Subject: [PATCH 1/2] feat(stale): adding workflow and updating CONTRIBUTING.md to close stale PRs --- .github/workflows/stale.yml | 61 +++++++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 7 +++++ 2 files changed, 68 insertions(+) create mode 100644 .github/workflows/stale.yml diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000000..9344dc8833 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,61 @@ +name: Close stale PRs + +on: + schedule: + - cron: '0 0 * * *' + workflow_dispatch: + +permissions: + pull-requests: write + issues: write + +jobs: + stale-inactivity: + name: 60-day inactivity + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v9 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + + days-before-issue-stale: -1 + days-before-issue-close: -1 + days-before-pr-stale: 60 + days-before-pr-close: 7 + + stale-pr-label: stale + stale-pr-message: > + This PR has had no activity for 60 days and will be automatically + closed in 7 days unless there is new activity. If you'd like to + continue, please rebase and leave a comment. + close-pr-message: > + Closing due to 60+ days of inactivity. Feel free to reopen if + you'd like to resume this work. + + exempt-pr-labels: 'do-not-close,pinned,work-in-progress' + + stale-review-inactivity: + name: 14-day no review response + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v9 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + + # Only target PRs with the awaiting-changes label (set by reviewers) + only-labels: awaiting-changes + days-before-issue-stale: -1 + days-before-issue-close: -1 + days-before-pr-stale: 14 + days-before-pr-close: 7 + + stale-pr-label: stale + stale-pr-message: > + This PR has had no response to review feedback for 14 days and will + be automatically closed in 7 days. Please address the review + comments or leave a comment if you need more time. + close-pr-message: > + Closing due to 14+ days with no response to review feedback. Feel + free to reopen once the requested changes have been addressed. + + exempt-pr-labels: 'do-not-close,pinned,work-in-progress' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d65a7a3535..b381dea788 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -150,6 +150,13 @@ Pull requests should be reviewable, tested, and maintainable. Before opening a P Maintainers may close PRs that are incomplete, too broad, inactive, not aligned with the project direction, or that create disproportionate review or maintenance burden. Closing a PR is not a judgment on the contributor; it is a maintainer decision that the change cannot be accepted in its present form. +PRs are also closed automatically by bot: + +- **60-day inactivity:** A PR with no activity for 60 days is marked stale and closed after a further 7 days if there is still no activity. Any new comment, commit, or review resets the timer. +- **14-day no review response:** A PR labelled `awaiting-changes` with no response for 14 days is marked stale and closed after a further 7 days. + +To opt a PR out of automatic closure, apply the `do-not-close`, `pinned`, or `work-in-progress` label. + ### AI-Assisted Contributions Use of AI tools is allowed, but contributors remain fully responsible for their submissions. From f7d828cce208dd0f1bde27e45bd38d6a4ed22962 Mon Sep 17 00:00:00 2001 From: Elliott de Launay Date: Mon, 15 Jun 2026 02:49:21 +0000 Subject: [PATCH 2/2] feat(stale): adding workflow and updating CONTRIBUTING.md to close stale PRs --- .github/workflows/label-pr-review-state.yml | 81 +++++++++++++++++++++ .github/workflows/stale.yml | 19 +++-- CONTRIBUTING.md | 2 +- 3 files changed, 91 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/label-pr-review-state.yml diff --git a/.github/workflows/label-pr-review-state.yml b/.github/workflows/label-pr-review-state.yml new file mode 100644 index 0000000000..2c83ecc6a9 --- /dev/null +++ b/.github/workflows/label-pr-review-state.yml @@ -0,0 +1,81 @@ +name: Label PR review state + +on: + schedule: + - cron: '0 * * * *' # hourly + workflow_dispatch: + +permissions: + pull-requests: read + issues: write + +jobs: + reconcile: + runs-on: ubuntu-latest + steps: + - name: Reconcile PR review state labels + uses: actions/github-script@v7 + with: + script: | + const { owner, repo } = context.repo; + const stateLabels = ['awaiting-author', 'awaiting-review']; + + const prs = await github.paginate(github.rest.pulls.list, { + owner, repo, state: 'open', per_page: 100, + }); + + for (const pr of prs) { + const reviews = await github.paginate(github.rest.pulls.listReviews, { + owner, repo, pull_number: pr.number, per_page: 100, + }); + + // Reviews are returned chronologically, so later entries replace + // each reviewer's earlier decision. + const latest = new Map(); + for (const r of reviews) { + if (r.state !== 'COMMENTED') { + latest.set(r.user.login, r); + } + } + + const changeRequestReviewers = [...latest.entries()] + .filter(([, review]) => review.state === 'CHANGES_REQUESTED') + .map(([login]) => login); + const requestedReviewers = new Set( + pr.requested_reviewers.map(reviewer => reviewer.login), + ); + + let desiredLabel = null; + if (changeRequestReviewers.length > 0) { + desiredLabel = changeRequestReviewers.every( + reviewer => requestedReviewers.has(reviewer), + ) + ? 'awaiting-review' + : 'awaiting-author'; + } + + const currentLabels = new Set(pr.labels.map(label => label.name)); + for (const label of stateLabels) { + if (label !== desiredLabel && currentLabels.has(label)) { + await github.rest.issues.removeLabel({ + owner, repo, issue_number: pr.number, name: label, + }); + } + } + + if (desiredLabel && !currentLabels.has(desiredLabel)) { + await github.rest.issues.addLabels({ + owner, repo, issue_number: pr.number, labels: [desiredLabel], + }); + } + + if ( + desiredLabel !== 'awaiting-author' && + currentLabels.has('stale-awaiting-author') + ) { + await github.rest.issues.removeLabel({ + owner, repo, issue_number: pr.number, + name: 'stale-awaiting-author', + }); + } + } diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 9344dc8833..3e51b44850 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -23,7 +23,7 @@ jobs: days-before-pr-stale: 60 days-before-pr-close: 7 - stale-pr-label: stale + stale-pr-label: stale-inactive stale-pr-message: > This PR has had no activity for 60 days and will be automatically closed in 7 days unless there is new activity. If you'd like to @@ -35,27 +35,26 @@ jobs: exempt-pr-labels: 'do-not-close,pinned,work-in-progress' stale-review-inactivity: - name: 14-day no review response + name: 14-day author inactivity after requested changes runs-on: ubuntu-latest steps: - uses: actions/stale@v9 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - # Only target PRs with the awaiting-changes label (set by reviewers) - only-labels: awaiting-changes + only-labels: awaiting-author days-before-issue-stale: -1 days-before-issue-close: -1 days-before-pr-stale: 14 days-before-pr-close: 7 - stale-pr-label: stale + stale-pr-label: stale-awaiting-author stale-pr-message: > - This PR has had no response to review feedback for 14 days and will - be automatically closed in 7 days. Please address the review - comments or leave a comment if you need more time. + This PR has been awaiting author changes for 14 days and will be + automatically closed in 7 days. Please address the review comments + or leave a comment if you need more time. close-pr-message: > - Closing due to 14+ days with no response to review feedback. Feel - free to reopen once the requested changes have been addressed. + Closing due to author inactivity after requested changes. Feel free + to reopen once the requested changes have been addressed. exempt-pr-labels: 'do-not-close,pinned,work-in-progress' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b381dea788..2ce81bd722 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -153,7 +153,7 @@ Maintainers may close PRs that are incomplete, too broad, inactive, not aligned PRs are also closed automatically by bot: - **60-day inactivity:** A PR with no activity for 60 days is marked stale and closed after a further 7 days if there is still no activity. Any new comment, commit, or review resets the timer. -- **14-day no review response:** A PR labelled `awaiting-changes` with no response for 14 days is marked stale and closed after a further 7 days. +- **14-day author inactivity:** After a reviewer requests changes, the PR is labelled `awaiting-author`. Author activity resets the inactivity timer. Once the changes are ready, re-request review from the reviewer; the PR will move to `awaiting-review` and is no longer eligible for automatic closure under this policy. To opt a PR out of automatic closure, apply the `do-not-close`, `pinned`, or `work-in-progress` label.