Skip to content

Close old issues and pull requests #10511

Close old issues and pull requests

Close old issues and pull requests #10511

Workflow file for this run

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}`);
}
}