Skip to content

Cleanup Stale Bot PRs #20

Cleanup Stale Bot PRs

Cleanup Stale Bot PRs #20

name: Cleanup Stale Bot PRs
on:
schedule:
- cron: '0 6 * * 1' # Weekly on Mondays
workflow_dispatch:
permissions:
pull-requests: write
issues: write
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- name: Close stale automated PRs
uses: actions/github-script@v7
with:
script: |
const STALE_DAYS = 30;
const staleDate = new Date(Date.now() - STALE_DAYS * 24 * 60 * 60 * 1000);
// Use Search API to find PRs with the label
// pulls.list doesn't support labels parameter
const searchQuery = `repo:${context.repo.owner}/${context.repo.repo} is:pr is:open label:automated-bug-fix`;
const { data: searchResults } = await github.rest.search.issuesAndPullRequests({
q: searchQuery,
per_page: 100
});
const prs = searchResults.items;
for (const pr of prs) {
const createdAt = new Date(pr.created_at);
const updatedAt = new Date(pr.updated_at);
// Only close if both created AND last updated over STALE_DAYS ago
if (createdAt < staleDate && updatedAt < staleDate) {
// Check if there are recent comments or review activity
// Use 'since' parameter to let server filter comments, avoiding pagination issues
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
since: staleDate.toISOString(),
per_page: 100
});
// If any comments were returned, there's recent activity
const hasRecentActivity = comments.length > 0;
if (!hasRecentActivity) {
// Add stale label before closing
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
labels: ['stale', 'auto-closed']
});
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body: `Closing this automated PR as it has been open for ${STALE_DAYS}+ days without activity or merge. The bug hunter may re-detect and fix this issue in a future run if still relevant.`
});
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number,
state: 'closed'
});
core.info(`Closed stale PR #${pr.number}`);
} else {
core.info(`Skipping PR #${pr.number} - has recent activity`);
}
}
}