Unassign inactive contributors #839
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: Unassign inactive contributors | |
| on: | |
| schedule: | |
| # Runs every 1 hour | |
| - cron: "0 * * * *" | |
| permissions: | |
| issues: write | |
| jobs: | |
| unassign-inactive: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check and warn inactive contributors | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const now = new Date(); | |
| const WARNING_AFTER_HOURS = 15; // Send warning | |
| const UNASSIGN_AFTER_HOURS = 30; // Unassign after warning window | |
| const issues = await github.paginate( | |
| github.rest.issues.listForRepo, | |
| { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| state: "open", | |
| per_page: 100, | |
| } | |
| ); | |
| for (const issue of issues) { | |
| // Skip PRs | |
| if (issue.pull_request) continue; | |
| // Skip issues with no assignees | |
| if (!issue.assignees || issue.assignees.length === 0) continue; | |
| const lastUpdated = new Date(issue.updated_at); | |
| const diffHours = (now - lastUpdated) / (1000 * 60 * 60); | |
| // Fetch comments to check for existing warning | |
| const comments = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issue.number, | |
| }); | |
| const warningComment = comments.data.find(c => | |
| c.body.includes("⚠️ Inactivity Warning") | |
| ); | |
| const assigneesMention = issue.assignees | |
| .map(a => `@${a.login}`) | |
| .join(", "); | |
| // 🔔 Send warning after 15 hours | |
| if (diffHours >= WARNING_AFTER_HOURS && !warningComment) { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issue.number, | |
| body: `⚠️ **Inactivity Warning** | |
| ${assigneesMention}, this issue has been inactive for **15 hours**. | |
| Please provide an update within the next **15 hours**, or you may be **automatically unassigned**.` | |
| }); | |
| console.log(`Warning sent for issue #${issue.number}`); | |
| continue; | |
| } | |
| // 🚫 Unassign after 30 hours (only if warning was sent) | |
| if (diffHours >= UNASSIGN_AFTER_HOURS && warningComment) { | |
| const assigneeLogins = issue.assignees.map(a => a.login); | |
| await github.rest.issues.removeAssignees({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issue.number, | |
| assignees: assigneeLogins, | |
| }); | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issue.number, | |
| body: `ℹ️ **Unassigned due to inactivity** | |
| You have been automatically unassigned after **30 hours of inactivity**. | |
| Feel free to reassign yourself if you wish to continue working on this issue.` | |
| }); | |
| console.log(`Unassigned contributors from issue #${issue.number}`); | |
| } | |
| } |