Skip to content

Commit 35b5ec2

Browse files
committed
Let contributors claim an issue by commenting /take
GitHub only offers collaborators in the assignee dropdown, so assigning an outside contributor looks impossible from the web UI. The REST API allows it once the person has commented, which this uses — so nobody waits for a maintainer to be awake.
1 parent f349092 commit 35b5ec2

1 file changed

Lines changed: 78 additions & 0 deletions

File tree

‎.github/workflows/self-assign.yml‎

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Let a contributor claim an issue themselves by commenting `/take`.
2+
#
3+
# GitHub's UI only offers collaborators in the assignee dropdown, and the "can this user be
4+
# assigned" API returns 404 for everyone else — so assigning an outside contributor looks impossible
5+
# from the web interface. The REST API allows it once the person has commented on the issue, which
6+
# this workflow uses. That removes a round trip: nobody has to wait for a maintainer to be awake.
7+
name: Self-assign
8+
9+
on:
10+
issue_comment:
11+
types: [created]
12+
13+
permissions:
14+
issues: write
15+
16+
# One claim at a time per issue. The job reads the assignee list and then adds to it, so two
17+
# comments arriving together could both see it empty and both assign — leaving two people who each
18+
# think the issue is theirs, which is the exact outcome this workflow exists to prevent.
19+
concurrency:
20+
group: self-assign-${{ github.repository }}-${{ github.event.issue.number }}
21+
cancel-in-progress: false
22+
23+
jobs:
24+
take:
25+
# Issues only. This is a cheap pre-filter; the script below checks the command exactly, because
26+
# `startsWith` alone would also fire on `/takeover` and quietly assign someone who was arguing
27+
# against the idea.
28+
if: >
29+
!github.event.issue.pull_request &&
30+
(startsWith(github.event.comment.body, '/take') || startsWith(github.event.comment.body, '.take'))
31+
runs-on: ubuntu-latest
32+
steps:
33+
- uses: actions/github-script@v9
34+
with:
35+
script: |
36+
const { owner, repo } = context.repo;
37+
const issue_number = context.issue.number;
38+
const who = context.payload.comment.user.login;
39+
40+
// The first token must be exactly the command. Without this, `/takeover this is a bad
41+
// idea` reads as a claim and assigns the person objecting to it.
42+
const firstToken = (context.payload.comment.body || '').trim().split(/\s+/)[0];
43+
if (firstToken !== '/take' && firstToken !== '.take') {
44+
core.info(`Not a claim command (${firstToken}); ignoring.`);
45+
return;
46+
}
47+
48+
const { data: issue } = await github.rest.issues.get({ owner, repo, issue_number });
49+
const current = issue.assignees.map(a => a.login);
50+
51+
if (current.includes(who)) {
52+
core.info(`${who} already has it; nothing to do.`);
53+
return;
54+
}
55+
56+
// Someone else is already on it. Say so rather than silently piling on — two people
57+
// discovering they duplicated a weekend's work is the outcome this is meant to prevent.
58+
if (current.length > 0) {
59+
await github.rest.issues.createComment({
60+
owner, repo, issue_number,
61+
body: `@${who} this one is already with @${current.join(', @')}. ` +
62+
`Worth checking in with them before starting — they may be glad of the help, ` +
63+
`or it may be further along than it looks. There are other issues going spare ` +
64+
`under [help wanted](https://github.com/${owner}/${repo}/issues?q=is%3Aopen+label%3A%22help+wanted%22).`,
65+
});
66+
return;
67+
}
68+
69+
await github.rest.issues.addAssignees({ owner, repo, issue_number, assignees: [who] });
70+
await github.rest.issues.createComment({
71+
owner, repo, issue_number,
72+
body: `Assigned to @${who} — it's yours. 🎉\n\n` +
73+
`No deadline, and no hard feelings if you change your mind; just say so and ` +
74+
`someone else can pick it up. A draft PR early is very welcome if you'd like ` +
75+
`eyes on the direction before polishing it.\n\n` +
76+
`CLA Assistant will ask you to sign on your first pull request. It takes about ` +
77+
`thirty seconds and you keep copyright of your work.`,
78+
});

0 commit comments

Comments
 (0)