ci: add /assign self-service issue-claim bot#1026
Conversation
Lets community/interns claim issues via a /assign comment without granting write or triage. Requested by Josh; pairs with the fork-and-PR model.
📝 WalkthroughWalkthroughA new GitHub Actions workflow automates issue self-assignment by listening for ChangesSelf-assign workflow automation
Sequence DiagramsequenceDiagram
participant User
participant GitHub as GitHub (Issue Comment)
participant Workflow as Workflow Runner
participant API as GitHub REST API
User->>GitHub: Comments /assign or /unassign
GitHub->>Workflow: Trigger on_issue_comment
Workflow->>Workflow: Parse command and commenter
Workflow->>API: Add/remove assignee
API->>GitHub: Update issue assignees
Workflow->>API: Add eyes reaction
API->>GitHub: React to comment
Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
No description provided. |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/issue-self-assign.yml:
- Around line 31-36: The current check uses startsWith('/unassign') which can
misfire on longer tokens; instead split the comment text into tokens (e.g.,
const cmd = context.payload.comment.body.trim().split(/\s+/)[0]) and compare cmd
=== '/assign' or cmd === '/unassign' exactly before calling
github.rest.issues.addAssignees or github.rest.issues.removeAssignees; update
the variable (currently named unassign) to be set from this exact-match
comparison so only an exact first token triggers the assignee mutation.
- Around line 32-39: Wrap the assignee mutation calls
(github.rest.issues.removeAssignees and github.rest.issues.addAssignees) in a
try/catch so failures do not abort the workflow; inside the try perform the
add/remove based on unassign, and in the catch log the caught error (include
owner, repo, issue_number and user for context) so failure details are recorded.
After the try/catch always call github.rest.reactions.createForIssueComment with
the same confirmation reaction (and optionally post a brief failure comment) so
the workflow gives deterministic feedback regardless of assignment success.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 5b967a1f-8663-40e3-b2e9-8e942e06cf9f
📒 Files selected for processing (1)
.github/workflows/issue-self-assign.yml
| const unassign = context.payload.comment.body.trim().startsWith('/unassign'); | ||
| if (unassign) { | ||
| await github.rest.issues.removeAssignees({ owner, repo, issue_number, assignees: [user] }); | ||
| } else { | ||
| await github.rest.issues.addAssignees({ owner, repo, issue_number, assignees: [user] }); | ||
| } |
There was a problem hiding this comment.
Use exact command matching instead of prefix matching.
startsWith('/assign') will also match comments like /assignment, which currently falls into the assign path. Parse the first token and require exact /assign or /unassign before mutating assignees.
Suggested fix
- const unassign = context.payload.comment.body.trim().startsWith('/unassign');
- if (unassign) {
+ const command = context.payload.comment.body.trim().split(/\s+/, 1)[0];
+ if (command === '/unassign') {
await github.rest.issues.removeAssignees({ owner, repo, issue_number, assignees: [user] });
- } else {
+ } else if (command === '/assign') {
await github.rest.issues.addAssignees({ owner, repo, issue_number, assignees: [user] });
+ } else {
+ return;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const unassign = context.payload.comment.body.trim().startsWith('/unassign'); | |
| if (unassign) { | |
| await github.rest.issues.removeAssignees({ owner, repo, issue_number, assignees: [user] }); | |
| } else { | |
| await github.rest.issues.addAssignees({ owner, repo, issue_number, assignees: [user] }); | |
| } | |
| const command = context.payload.comment.body.trim().split(/\s+/, 1)[0]; | |
| if (command === '/unassign') { | |
| await github.rest.issues.removeAssignees({ owner, repo, issue_number, assignees: [user] }); | |
| } else if (command === '/assign') { | |
| await github.rest.issues.addAssignees({ owner, repo, issue_number, assignees: [user] }); | |
| } else { | |
| return; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/issue-self-assign.yml around lines 31 - 36, The current
check uses startsWith('/unassign') which can misfire on longer tokens; instead
split the comment text into tokens (e.g., const cmd =
context.payload.comment.body.trim().split(/\s+/)[0]) and compare cmd ===
'/assign' or cmd === '/unassign' exactly before calling
github.rest.issues.addAssignees or github.rest.issues.removeAssignees; update
the variable (currently named unassign) to be set from this exact-match
comparison so only an exact first token triggers the assignee mutation.
| if (unassign) { | ||
| await github.rest.issues.removeAssignees({ owner, repo, issue_number, assignees: [user] }); | ||
| } else { | ||
| await github.rest.issues.addAssignees({ owner, repo, issue_number, assignees: [user] }); | ||
| } | ||
| await github.rest.reactions.createForIssueComment({ | ||
| owner, repo, comment_id: context.payload.comment.id, content: 'eyes', | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
GitHub REST API docs: what error codes can issues.addAssignees and issues.removeAssignees return, especially when a user is not assignable to a repository issue?
💡 Result:
For GitHub REST API “issue assignees” endpoints (issues.addAssignees and issues.removeAssignees), the docs only spell out these HTTP response status codes for the assignee-add/remove operations: 1) issues.addAssignees (POST /repos/{owner}/{repo}/issues/{issue_number}/assignees) - 201 Created when assignees are added successfully. [1] - 204 No Content when the assignee can be assigned to issue_number. [1] - 404 Not Found when the assignee cannot be assigned to issue_number. [1] 2) issues.removeAssignees (DELETE /repos/{owner}/{repo}/issues/{issue_number}/assignees) - 200 OK when the assignees are removed successfully. [2] When a user is not assignable to a repository issue (i.e., cannot be assigned to that specific issue_number), the REST docs describe the response as 404 Not Found. [1] Note: the removeAssignees operation page shown in the search results explicitly lists only 200 OK, and does not mention additional error status codes on that page. [2]
Citations:
- 1: https://docs.github.com/rest/issues/assignees
- 2: https://docs.github.com/en/rest/issues/assignees
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE=".github/workflows/issue-self-assign.yml"
# Show the referenced region with some surrounding context
nl -ba "$FILE" | sed -n '1,120p' | catRepository: rocketride-org/rocketride-server
Length of output: 119
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE=".github/workflows/issue-self-assign.yml"
if [ ! -f "$FILE" ]; then
echo "Missing $FILE"
exit 1
fi
awk 'NR>=1 && NR<=120 { printf "%4d: %s\n", NR, $0 }' "$FILE"Repository: rocketride-org/rocketride-server
Length of output: 1912
Handle GitHub assignee REST failures so the workflow still provides deterministic feedback.
.github/workflows/issue-self-assign.ymlcurrently callsgithub.rest.issues.removeAssignees(...)/github.rest.issues.addAssignees(...)withouttry/catch; if GitHub returns an error (e.g., 404 when the user can’t be assigned to that issue_number), the script exits beforegithub.rest.reactions.createForIssueComment(..., 'eyes'), failing the job and skipping confirmation.- Wrap the mutation in
try/catchand post a consistent reaction/comment for both success and failure (and log the error).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/issue-self-assign.yml around lines 32 - 39, Wrap the
assignee mutation calls (github.rest.issues.removeAssignees and
github.rest.issues.addAssignees) in a try/catch so failures do not abort the
workflow; inside the try perform the add/remove based on unassign, and in the
catch log the caught error (include owner, repo, issue_number and user for
context) so failure details are recorded. After the try/catch always call
github.rest.reactions.createForIssueComment with the same confirmation reaction
(and optionally post a brief failure comment) so the workflow gives
deterministic feedback regardless of assignment success.
What
Adds
.github/workflows/issue-self-assign.yml— a comment-driven bot that lets community contributors and interns claim issues themselves:/assignon an issue → assigns the commenter/unassign→ removes themWhy
Requested by Josh. Self-assignment normally needs Triage/write on the repo; this bot performs the assignment via the workflow's
GITHUB_TOKENinstead, so we don't have to hand out roles to community members. Pairs with the fork-and-PR contribution model (no write access on the engine repo).Notes
permissions: issues: writeonly (least privilege).actions/github-scriptpinned to the same SHA already used elsewhere in the repo.🤖 Generated with Claude Code
Summary by CodeRabbit
/assignor/unassigncommands now automatically update issue assignees.