-
-
Notifications
You must be signed in to change notification settings - Fork 442
111 lines (96 loc) · 5.08 KB
/
Copy pathissue-assignment.yml
File metadata and controls
111 lines (96 loc) · 5.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
name: Issue Assignment Bot
on:
issue_comment:
types: [created]
permissions:
issues: write
jobs:
assign:
name: Auto-assign contributor
runs-on: ubuntu-latest
# Only issues — not PR comments
if: github.event.issue.pull_request == null
steps:
- name: Handle assignment request
uses: actions/github-script@v9
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const comment = context.payload.comment.body.toLowerCase().trim();
// Broad intent matching. The old exact-string list missed common
// phrasings — e.g. "I'd love to work on this feature", "please
// assign it to me", "/assign" — leaving contributors unassigned
// even though the bot ran. These regexes tolerate those variants.
const PATTERNS = [
/(^|\s)\/assign\b/, // slash command: /assign, /assign me
/\bassign(ed|ing)?\b[\s\S]{0,40}\b(me|this|it|myself|to me)\b/, // "assign me", "assign this to me", "assign it to me"
/\b(me|this|it|myself)\b[\s\S]{0,25}\bassign(ed)?\b/, // "can this be assigned to me"
/\bwork(ing)?\s+on\s+(this|it)\b/, // "work on this", "working on it"
/\b(want|like|love|willing|happy|keen|glad|wish|hoping)\s+to\s+(work|take|tackle|contribute|help|solve|fix|do)\b/,
/\b(let me|i can|i'?ll|i will|i'?d|i would|i want|can i|may i|could i)\b[\s\S]{0,30}\b(work|take|tackle|handle|pick|solve|fix|contribute|do this)\b/,
/\binterested\s+in\s+(work|contribut|solv|this|tak|fix)/, // "interested in working/contributing"
/\b(take|tackle|handle|pick)\s+(this|it|up)\b/, // "take this", "pick it up"
];
if (!PATTERNS.some(re => re.test(comment))) return;
// Skip bots
if (context.payload.comment.user.type === 'Bot') return;
const { owner, repo } = context.repo;
const issueNumber = context.payload.issue.number;
const commenter = context.payload.comment.user.login;
// Fetch live issue state — do NOT use context.payload.issue which
// is a stale webhook snapshot and causes a race condition where
// concurrent comments both see 0 assignees and both get assigned.
const { data: issue } = await github.rest.issues.get({
owner, repo, issue_number: issueNumber,
});
// Re-check: skip if issue is now closed (may have changed since comment)
if (issue.state !== 'open') return;
// Check if issue is already assigned
if (issue.assignees && issue.assignees.length > 0) {
const assignees = issue.assignees.map(a => a.login);
if (assignees.includes(commenter)) {
await github.rest.issues.createComment({
owner, repo, issue_number: issueNumber,
body: `@${commenter} You are already assigned to this issue. Open a PR when ready.`,
});
} else {
await github.rest.issues.createComment({
owner, repo, issue_number: issueNumber,
body: `@${commenter} This issue is already taken by @${assignees.join(', @')}. Check other open issues!`,
});
}
return;
}
// NOTE: per-contributor issue limit is temporarily disabled
// TODO: re-enable when needed by restoring the block below
/*
// Limit: max 2 open issues assigned per contributor
const allAssigned = await github.paginate(github.rest.issues.listForRepo, {
owner, repo, assignee: commenter, state: 'open', per_page: 50,
});
const openIssueCount = allAssigned.filter(i => !i.pull_request).length;
if (openIssueCount >= 2) {
await github.rest.issues.createComment({
owner, repo, issue_number: issueNumber,
body: `@${commenter} You already have ${openIssueCount} open issue(s) assigned. Complete those first before picking up more.`,
});
return;
}
*/
// Assign
await github.rest.issues.addAssignees({
owner, repo, issue_number: issueNumber,
assignees: [commenter],
});
// Add gssoc:assigned label if not already present
const currentLabels = issue.labels.map(l => l.name);
if (!currentLabels.includes('gssoc:assigned')) {
await github.rest.issues.addLabels({
owner, repo, issue_number: issueNumber,
labels: ['gssoc:assigned'],
});
}
await github.rest.issues.createComment({
owner, repo, issue_number: issueNumber,
body: `Assigned to @${commenter}! You have **7 days** to open a PR. No PR after 7 days = auto-unassigned so others can pick it up. Good luck!`,
});