name: Close Inactive Issues on: schedule: # run every day at midnight - cron: "0 0 * * *" workflow_dispatch: permissions: issues: write pull-requests: write contents: read jobs: close_inactive_issues: runs-on: ubuntu-latest steps: - name: Check inactive issues and close them uses: actions/github-script@v7 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: script: | const inactivePeriod = 7 * 24 * 60 * 60 * 1000; // 7 days const now = Date.now(); // paginate through all open issues (listForRepo also returns PRs) const issues = await github.paginate(github.rest.issues.listForRepo, { owner: context.repo.owner, repo: context.repo.repo, state: 'open', }); for (const issue of issues) { // skip pull requests (they are also returned by listForRepo) if (issue.pull_request) continue; // skip labeled issues if (issue.labels.length > 0) { console.log(`Skipping issue #${issue.number} (Has labels).`); continue; } // last activity time = the issue's own updated_at, which is // refreshed on comments, labels, etc. This avoids relying on // fetching comments and is accurate even when comments are edited. const lastActivityTime = new Date(issue.updated_at).getTime(); // check inactivity if (now - lastActivityTime > inactivePeriod) { console.log(`Closing inactive issue: #${issue.number} (No activity for 7 days)`); await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, body: "This issue has been automatically closed due to inactivity for 7 days." }); await github.rest.issues.update({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, state: 'closed', }); } else { console.log(`Skipping issue #${issue.number} (Active within 7 days).`); } }