Close old issues and pull requests #10511
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: Close old issues and pull requests | |
| on: | |
| schedule: | |
| - cron: '0 6 * * *' | |
| workflow_dispatch: | |
| concurrency: | |
| group: close-old-threads | |
| cancel-in-progress: false | |
| permissions: | |
| issues: write | |
| pull-requests: write | |
| jobs: | |
| close-old-threads: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/github-script@v9 | |
| with: | |
| script: | | |
| const cutoffDays = 30; | |
| const exemptLabels = ['keep-open']; | |
| const cutoff = new Date(); | |
| cutoff.setDate(cutoff.getDate() - cutoffDays); | |
| const closeIssueMessage = [ | |
| 'This issue has been automatically closed and locked.', | |
| '', | |
| 'Processing 3.x is no longer actively developed. Since January 2020, development moved to the Processing 4 repository.', | |
| '', | |
| "If this issue is still relevant to Processing 4, we'd love to hear from you. Please open a new issue at:", | |
| 'https://github.com/processing/processing4/issues/new/choose', | |
| '', | |
| 'Thank you for helping improve Processing.' | |
| ].join('\n'); | |
| const closePrMessage = [ | |
| 'This pull request has been automatically closed and locked.', | |
| '', | |
| 'Processing 3.x is no longer actively developed. Since January 2020, development moved to the Processing 4 repository.', | |
| '', | |
| "If you'd still like to contribute this work, please open an issue in the Processing 4 repository to discuss it first:", | |
| 'https://github.com/processing/processing4/issues/new/choose', | |
| '', | |
| 'Thank you for your contribution.' | |
| ].join('\n'); | |
| async function closeThread(thread) { | |
| const isPr = Boolean(thread.pull_request); | |
| if (isPr) { | |
| await github.rest.pulls.update({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: thread.number, | |
| state: 'closed' | |
| }); | |
| } else { | |
| await github.rest.issues.update({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: thread.number, | |
| state: 'closed', | |
| state_reason: 'not_planned' | |
| }); | |
| } | |
| } | |
| const threads = await github.paginate(github.rest.issues.listForRepo, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| state: 'open', | |
| per_page: 100, | |
| sort: 'updated', | |
| direction: 'asc' | |
| }); | |
| for (const thread of threads) { | |
| try { | |
| const updatedAt = new Date(thread.updated_at); | |
| if (updatedAt > cutoff) { | |
| break; | |
| } | |
| const labels = thread.labels.map(label => | |
| typeof label === 'string' ? label : label.name | |
| ); | |
| const isExempt = labels.some(label => exemptLabels.includes(label)); | |
| if (isExempt) { | |
| console.log(`Skipping #${thread.number}: exempt label.`); | |
| continue; | |
| } | |
| const isPr = Boolean(thread.pull_request); | |
| const body = isPr ? closePrMessage : closeIssueMessage; | |
| await closeThread(thread); | |
| if (thread.locked) { | |
| console.log(`Closed #${thread.number}: already locked, so no comment was added.`); | |
| continue; | |
| } | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: thread.number, | |
| body | |
| }); | |
| await github.rest.issues.lock({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: thread.number, | |
| lock_reason: 'resolved' | |
| }); | |
| console.log(`Closed and locked #${thread.number}.`); | |
| } catch (error) { | |
| console.log(`Failed on #${thread.number}: ${error.status || 'unknown status'} ${error.message}`); | |
| } | |
| } |