Unassign Inactive Contributors #1213
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 hour | |
| - cron: "0 * * * *" | |
| workflow_dispatch: # Allow manual trigger for testing | |
| permissions: | |
| issues: write | |
| jobs: | |
| unassign-inactive: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check and unassign inactive contributors | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const now = new Date(); | |
| const WARNING_HOURS = 15; | |
| const UNASSIGN_HOURS = 30; | |
| console.log('🔍 Checking for inactive assigned issues...'); | |
| 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 unassigned issues | |
| if (!issue.assignees || issue.assignees.length === 0) continue; | |
| const assignedAt = new Date(issue.updated_at); | |
| const hoursInactive = (now - assignedAt) / (1000 * 60 * 60); | |
| console.log(`Issue #${issue.number}: ${hoursInactive.toFixed(1)} hours inactive`); | |
| // Get comments to check for warning | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issue.number, | |
| }); | |
| const hasWarning = comments.some(c => | |
| c.body && c.body.includes('⏰ Inactivity Warning') | |
| ); | |
| const assigneeNames = issue.assignees.map(a => `@${a.login}`).join(', '); | |
| const assigneeLogins = issue.assignees.map(a => a.login); | |
| // Send warning after 15 hours (if not already warned) | |
| if (hoursInactive >= WARNING_HOURS && hoursInactive < UNASSIGN_HOURS && !hasWarning) { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issue.number, | |
| body: `⏰ **Inactivity Warning** | |
| Hi ${assigneeNames}! 👋 | |
| This issue has been inactive for **${Math.floor(hoursInactive)} hours**. | |
| Please provide an update within the next **15 hours**, or you may be **automatically unassigned** to give others a chance to contribute. | |
| 💡 *If you need more time, just leave a comment to reset the timer!*` | |
| }); | |
| console.log(`⚠️ Warning sent for issue #${issue.number}`); | |
| } | |
| // Unassign after 30 hours (only if warning was sent) | |
| if (hoursInactive >= UNASSIGN_HOURS && hasWarning) { | |
| 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** | |
| This issue has been unassigned after **30+ hours of inactivity**. | |
| The issue is now open for others to work on! 🙌 | |
| *If you'd like to continue working on this, feel free to comment and request reassignment.*` | |
| }); | |
| console.log(`🚫 Unassigned issue #${issue.number}`); | |
| } | |
| } | |
| console.log('✅ Inactivity check complete!'); |