Skip to content

Commit 94593d6

Browse files
authored
feat(workflow): add workflow to unassign inactive issue assignees (#341)
1 parent 4c7437f commit 94593d6

2 files changed

Lines changed: 107 additions & 0 deletions

File tree

.github/scripts/unassignIssues.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
module.exports = async ({ github, context }) => {
2+
const owner = context.repo.owner;
3+
const repo = context.repo.repo;
4+
5+
// Fetch all open issues (excluding PRs)
6+
let page = 1;
7+
let issues = [];
8+
9+
while (true) {
10+
const { data } = await github.rest.issues.listForRepo({
11+
owner,
12+
repo,
13+
state: 'open',
14+
per_page: 100,
15+
page,
16+
});
17+
18+
const onlyIssues = data.filter(i => !i.pull_request);
19+
issues = issues.concat(onlyIssues);
20+
21+
if (data.length < 100) break;
22+
page++;
23+
}
24+
25+
console.log(`Found ${issues.length} open issue(s) to check.`);
26+
27+
for (const issue of issues) {
28+
const issueNumber = issue.number;
29+
30+
if (!issue.assignees || issue.assignees.length === 0) {
31+
console.log(
32+
`Issue #${issueNumber} has no assignees — skipping.`
33+
);
34+
continue;
35+
}
36+
37+
let linkedPRFound = false;
38+
39+
try {
40+
const timeline =
41+
await github.rest.issues.listEventsForTimeline({
42+
owner,
43+
repo,
44+
issue_number: issueNumber,
45+
per_page: 100,
46+
});
47+
48+
linkedPRFound = timeline.data.some(
49+
event =>
50+
event.event === 'cross-referenced' &&
51+
event.source?.issue?.pull_request &&
52+
event.source?.issue?.state === 'open'
53+
);
54+
} catch (err) {
55+
console.log(
56+
`Could not fetch timeline for issue #${issueNumber}: ${err.message}`
57+
);
58+
}
59+
60+
if (!linkedPRFound) {
61+
const assigneeLogins =
62+
issue.assignees.map(a => a.login);
63+
64+
await github.rest.issues.removeAssignees({
65+
owner,
66+
repo,
67+
issue_number: issueNumber,
68+
assignees: assigneeLogins,
69+
});
70+
71+
const assigneesMention =
72+
assigneeLogins.map(u => `@${u}`).join(', ');
73+
74+
await github.rest.issues.createComment({
75+
owner,
76+
repo,
77+
issue_number: issueNumber,
78+
body: `Hey @ShantKhatri (Project Admin) and @Harxhit (Maintainer),
79+
80+
This issue (previously assigned to ${assigneesMention}) has been **automatically unassigned** because no linked pull request was found after the scheduled check.
81+
82+
If work is in progress, please open a PR and link it to this issue to keep the assignment.`,
83+
});
84+
}
85+
}
86+
};
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Unassign Issues Without Linked PR
2+
3+
on:
4+
schedule:
5+
- cron: '0 9 */5 * *' # Runs every 5 days at 9:00 AM UTC
6+
workflow_dispatch: # Also allows manual triggering
7+
8+
jobs:
9+
unassign-issues:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
issues: write
13+
14+
steps:
15+
- name: Unassign issues with no linked PR and notify
16+
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 #v9.0.0
17+
with:
18+
github-token: ${{ secrets.GITHUB_TOKEN }}
19+
script: |
20+
const script = require('./.github/scripts/unassignIssues.js');
21+
await script({ github, context });

0 commit comments

Comments
 (0)