name: Close unlabeled issues on: schedule: - cron: "23 6 * * *" # daily workflow_dispatch: inputs: dry_run: description: Log matching issues without commenting or closing them required: false type: boolean default: false permissions: issues: write jobs: close_unlabeled: runs-on: ubuntu-latest steps: - name: Close open issues with no labels uses: actions/github-script@v7 with: script: | const owner = context.repo.owner; const repo = context.repo.repo; const inputs = context.payload.inputs || {}; const dryRun = String(inputs.dry_run || "false").toLowerCase() === "true"; const closeMarker = ""; const items = await github.paginate(github.rest.search.issuesAndPullRequests, { q: `repo:${owner}/${repo} is:issue is:open no:label`, per_page: 100, }); core.info(`Found ${items.length} open unlabeled issues.`); for (const item of items) { const issueNumber = item.number; if (dryRun) { core.info(`#${issueNumber}: would comment and close.`); continue; } const comments = await github.paginate(github.rest.issues.listComments, { owner, repo, issue_number: issueNumber, per_page: 100, }); const alreadyCommented = comments.some(comment => (comment.body || "").includes(closeMarker) ); if (!alreadyCommented) { const body = `${closeMarker}\n` + `Sorry about the churn here, and thanks for taking the time to report this.\n\n` + `Closing this issue because it does not have any labels.\n\n` + `Issue labels and triage rules were introduced later, so older unlabeled issues are no longer tracked reliably.\n\n` + `If you are still facing this problem, please open a new issue using the appropriate issue template and label so it can be triaged correctly.`; await github.rest.issues.createComment({ owner, repo, issue_number: issueNumber, body, }); } await github.rest.issues.update({ owner, repo, issue_number: issueNumber, state: "closed", state_reason: "not_planned", }); core.info(`#${issueNumber}: closed.`); }