Unassign Issues with No PR After 7 Days #153
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 Issues with No PR After 7 Days | |
| on: | |
| schedule: | |
| - cron: '0 2 * * *' # Runs daily at 2 AM UTC | |
| workflow_dispatch: # Manual trigger support | |
| jobs: | |
| unassign-stale-issues: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check for stale assigned issues | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.PAT }} | |
| script: | | |
| const daysBeforeUnassign = 7; | |
| const now = new Date(); | |
| // Get all open issues with assignees | |
| const issues = await github.paginate(github.rest.issues.listForRepo, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| state: 'open', | |
| assignee: '*' | |
| }); | |
| for (const issue of issues) { | |
| if (issue.pull_request) continue; // Skip PRs | |
| // Get timeline events to find assignment and PR link events | |
| const timeline = await github.paginate(github.rest.issues.listEventsForTimeline, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issue.number, | |
| mediaType: { previews: ["mockingbird"] } | |
| }); | |
| // Find the first assignment event date | |
| const assignedEvent = timeline.filter(e => e.event === 'assigned').at(-1);// latest | |
| if (!assignedEvent) { | |
| // No assignment event found, skip issue | |
| continue; | |
| } | |
| const assignedAt = new Date(assignedEvent.created_at); | |
| // Find the first PR linked event date | |
| const prLinkedEvent = timeline | |
| .filter( | |
| e => | |
| ['connected', 'cross-referenced'].includes(e.event) && | |
| new Date(e.created_at) >= assignedAt | |
| ) | |
| .at(0); // earliest link *after* assignment | |
| const prLinkedAt = prLinkedEvent ? new Date(prLinkedEvent.created_at) : null; | |
| // Calculate days since assignment | |
| const daysSinceAssignment = (now - assignedAt) / (1000 * 60 * 60 * 24); | |
| // Check if issue is still assigned (in case assignees changed later) | |
| const currentlyAssigned = issue.assignees.length > 0; | |
| if (currentlyAssigned && daysSinceAssignment >= daysBeforeUnassign) { | |
| // If PR not linked or linked after 7 days | |
| if (!prLinkedAt || prLinkedAt > new Date(assignedAt.getTime() + daysBeforeUnassign * 24 * 60 * 60 * 1000)) { | |
| console.log(`Unassigning issue #${issue.number} - no PR linked within ${daysBeforeUnassign} days of assignment.`); | |
| // Remove assignees | |
| await github.rest.issues.removeAssignees({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issue.number, | |
| assignees: issue.assignees.map(user => user.login) | |
| }); | |
| // Post comment | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issue.number, | |
| body: `⏰ This issue was automatically unassigned because no pull request was linked within ${daysBeforeUnassign} days of assignment. Please request reassignment if you are still working on it.` | |
| }); | |
| } | |
| } | |
| } |