Skip to content

feat: add issue comment command bot for assignment, labels and state management#101

Open
khushiiagrawal wants to merge 2 commits intokrkn-chaos:mainfrom
khushiiagrawal:feat/issue-bot
Open

feat: add issue comment command bot for assignment, labels and state management#101
khushiiagrawal wants to merge 2 commits intokrkn-chaos:mainfrom
khushiiagrawal:feat/issue-bot

Conversation

@khushiiagrawal
Copy link
Contributor

@khushiiagrawal khushiiagrawal commented Jan 23, 2026

User description

Summary

Implements an issue management bot that enables contributors and maintainers to manage issues through comment commands.

Fixes #91

Features

Assignment Commands

Command Description Who Can Use
/assign Self-assign to an issue Issue author or users with write access
/assign @user Assign another user Maintainers only
/unassign Remove yourself Anyone assigned
/unassign @user Remove another user Maintainers only

Label Commands

Command Description Who Can Use
/label <name> Add a label Maintainers only
/unlabel <name> Remove a label Maintainers only
/<label-name> Shortcut to add label (e.g., /bug) Maintainers only

Issue State Commands

Command Description Who Can Use
/close Close the issue Maintainers only
/reopen Reopen the issue Maintainers only
/help Show available commands Anyone

Implementation Details

  • Single workflow file: .github/workflows/issue-bot.yml
  • Efficient API usage: Lazy-loads labels only when needed, caches permission checks
  • Pagination support: Handles repositories with 100+ labels
  • Graceful error handling: Provides informative messages when GitHub API restrictions apply
  • Multiple commands: Supports multiple commands in a single comment (one per line)
  • Aggregated responses: Bot replies with a single comment containing all responses

Permission Handling

GitHub's API only allows assigning users who:

  1. Created the issue (can always self-assign to their own issue)
  2. Have write/admin access to the repository
  3. Are organization members with appropriate permissions

For first-time contributors on issues they didn't create, the bot responds with a helpful message explaining the limitation and indicates a maintainer will assist.

Testing

  1. Create a new issue and comment /assign → Should assign yourself
  2. Comment /help → Should display available commands
  3. Comment /bug (if label exists) → Should add label (maintainers) or show permission error
  4. On someone else's issue, comment /assign → Should explain GitHub's limitation

PR Type

Enhancement


Description

  • Implements comprehensive issue management bot with command support

  • Supports assignment, labeling, and state management via comments

  • Enables label shortcuts and dynamic command recognition

  • Includes permission-based access control and error handling


Diagram Walkthrough

flowchart LR
  A["Issue Comment Created"] --> B["Parse Commands"]
  B --> C["Permission Check"]
  C --> D["Execute Commands"]
  D --> E["Assignment/Labels/State"]
  E --> F["Aggregate Response"]
  F --> G["Post Single Comment"]
Loading

File Walkthrough

Relevant files
Enhancement
issue-bot.yml
Complete issue bot workflow implementation                             

.github/workflows/issue-bot.yml

  • Implements GitHub Actions workflow triggered on issue comments
  • Supports /assign, /unassign, /label, /unlabel, /close, /reopen, and
    /help commands
  • Includes lazy-loaded label caching with pagination for repositories
    with 100+ labels
  • Implements permission-based access control with cached permission
    checks
  • Handles dynamic label shortcuts (e.g., /bug) and aggregates responses
    into single comment
  • Provides graceful error handling for GitHub API restrictions and
    unknown commands
+435/-0 

Signed-off-by: khushiiagrawal <khushisaritaagrawal@gmail.com>
@qodo-code-review
Copy link

qodo-code-review bot commented Jan 23, 2026

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🟡
🎫 #91
🟢 Support `/assign` in issue comments so contributors can self-assign issues.
Support `/label ` in issue comments to add labels via comments.
Whether /label is intended to be usable by all contributors or restricted to
maintainers/collaborators (the workflow restricts labeling to users with write/admin
permission).
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Unhandled API failures: Several GitHub API calls (e.g., unassign, label add/remove, issue update, label listing)
are not wrapped in operation-level error handling so failures fall through to a generic
outer catch and stop processing remaining commands.

Referred Code
  await github.rest.issues.removeAssignees({
    owner,
    repo,
    issue_number: issueNumber,
    assignees: [assignee]
  });

  // Unassignment is visible in UI - no need to announce
  continue;
}

// --------------------
// /label <name>
// Maintainers only - auto-creates label if it doesn't exist
// --------------------
if (isCommand(line, "label")) {
  // Only maintainers can add labels
  const canLabel = await hasWritePermission(commenter);
  if (!canLabel) {
    responses.push("Only maintainers and collaborators can add labels.");
    continue;


 ... (clipped 232 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
Error details exposed: The bot posts raw ${error.message} back to the issue comment, which can reveal internal
details from GitHub/API errors to end users.

Referred Code
      responses.push(`Failed to assign @${assignee}: ${error.message}`);
    }
  }
  continue;
}

// --------------------
// /unassign [@user]
// Users can only unassign themselves.
// Maintainers can unassign anyone.
// --------------------
if (isCommand(line, "unassign")) {
  let assignee = commenter;
  const match = line.match(/^\/unassign\s+@(\S+)/i);
  if (match) {
    assignee = match[1];
  }

  // Only allow self-unassign unless maintainer
  if (assignee.toLowerCase() !== commenter.toLowerCase()) {
    const canUnassignOthers = await hasWritePermission(commenter);


 ... (clipped 258 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Missing audit logs: The workflow performs critical actions (assignments, label changes, state changes) but
does not emit any explicit audit log entries with actor/action/outcome, relying instead on
GitHub UI/events which may or may not meet audit requirements.

Referred Code
try {
  // Check if commenter is the issue author (they can always self-assign)
  const isIssueAuthor = issue.user.login.toLowerCase() === commenter.toLowerCase();

  for (const line of lines) {

    // --------------------
    // /assign [@user]
    // Users can assign themselves to their own issues.
    // Contributors with write access can assign themselves to any issue.
    // Maintainers can assign anyone to any issue.
    // --------------------
    if (isCommand(line, "assign")) {
      let assignee = commenter;
      const match = line.match(/^\/assign\s+@(\S+)/i);
      if (match) {
        assignee = match[1];
      }

      const isSelfAssign = assignee.toLowerCase() === commenter.toLowerCase();



 ... (clipped 297 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status:
Logging approach unclear: The workflow does not produce structured logs for command execution outcomes, and it is
unclear whether GitHub Actions logs or repository audit logs are considered sufficient and
properly scrubbed for sensitive data.

Referred Code
script: |
  const { comment, issue, repository } = context.payload;
  const owner = repository.owner.login;
  const repo = repository.name;
  const issueNumber = issue.number;
  const commenter = comment.user.login;

  // Ignore bot comments
  if (comment.user.type === "Bot") return;

  const rawBody = comment.body.trim();
  const lines = rawBody
    .split('\n')
    .map(line => line.trim())
    .filter(Boolean);

  // Check if any line looks like a command
  const hasCommands = lines.some(line => line.startsWith('/'));
  if (!hasCommands) return;

  const isCommand = (line, cmd) =>


 ... (clipped 397 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Insufficient input validation: User-controlled inputs (e.g., assignee from /assign @user and label names from
/label/shortcuts) are only minimally constrained, and without stricter validation it is
unclear whether malformed values could cause unsafe behavior or unintended API errors
across all edge cases.

Referred Code
if (isCommand(line, "assign")) {
  let assignee = commenter;
  const match = line.match(/^\/assign\s+@(\S+)/i);
  if (match) {
    assignee = match[1];
  }

  const isSelfAssign = assignee.toLowerCase() === commenter.toLowerCase();

  // Only allow self-assign unless maintainer
  if (!isSelfAssign) {
    const canAssignOthers = await hasWritePermission(commenter);
    if (!canAssignOthers) {
      responses.push("You can only assign yourself. Use `/assign` without arguments.");
      continue;
    }
  }

  try {
    await github.rest.issues.addAssignees({
      owner,


 ... (clipped 274 lines)

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link

qodo-code-review bot commented Jan 23, 2026

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Move the complex script into a dedicated action

To improve maintainability, the suggestion recommends extracting the large
inline JavaScript logic from the workflow YAML into a dedicated, reusable GitHub
Action. This separates code from configuration and allows for better development
practices like testing and linting.

Examples:

.github/workflows/issue-bot.yml [18-435]
          script: |
            const { comment, issue, repository } = context.payload;
            const owner = repository.owner.login;
            const repo = repository.name;
            const issueNumber = issue.number;
            const commenter = comment.user.login;

            // Ignore bot comments
            if (comment.user.type === "Bot") return;


 ... (clipped 408 lines)

Solution Walkthrough:

Before:

# .github/workflows/issue-bot.yml
jobs:
  process-commands:
    runs-on: ubuntu-latest
    steps:
      - name: Process issue commands
        uses: actions/github-script@v8
        with:
          script: |
            // 400+ lines of JavaScript logic for the bot
            const { comment, issue, repository } = context.payload;
            // ...
            async function hasWritePermission(username) { /* ... */ }
            // ...
            for (const line of lines) {
              if (isCommand(line, "assign")) { /* ... */ }
              // ... more command handling
            }

After:

# .github/workflows/issue-bot.yml
jobs:
  process-commands:
    runs-on: ubuntu-latest
    steps:
      - name: Process issue commands
        uses: ./.github/actions/issue-bot

# .github/actions/issue-bot/action.yml (new file)
name: 'Issue Command Bot'
runs:
  using: 'node20'
  main: 'index.js'

# .github/actions/issue-bot/index.js (new file)
// 400+ lines of JavaScript logic for the bot
const core = require('@actions/core');
const github = require('@actions/github');
// ... (rest of the script logic)
Suggestion importance[1-10]: 8

__

Why: This suggestion correctly identifies a significant architectural issue where over 400 lines of complex logic are embedded in a YAML file, which severely impacts maintainability, readability, and testability.

Medium
General
Handle unassign errors gracefully

Add a try/catch block around the removeAssignees API call to gracefully handle
potential errors and report them back to the user.

.github/workflows/issue-bot.yml [183-188]

-await github.rest.issues.removeAssignees({
-  owner,
-  repo,
-  issue_number: issueNumber,
-  assignees: [assignee]
-});
+try {
+  await github.rest.issues.removeAssignees({
+    owner,
+    repo,
+    issue_number: issueNumber,
+    assignees: [assignee]
+  });
+} catch (error) {
+  responses.push(`Failed to unassign @${assignee}: ${error.message}`);
+}
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: This suggestion correctly identifies a missing error handling case. Adding a try/catch block prevents the entire workflow from failing on a failed unassignment and provides useful feedback to the user, significantly improving the script's robustness.

Medium
Catch label addition errors

Add a try/catch block around the addLabels API call for dynamic label shortcuts
to handle potential errors and report them to the user.

.github/workflows/issue-bot.yml [403-408]

-await github.rest.issues.addLabels({
-  owner,
-  repo,
-  issue_number: issueNumber,
-  labels: [actualLabelName]
-});
+try {
+  await github.rest.issues.addLabels({
+    owner,
+    repo,
+    issue_number: issueNumber,
+    labels: [actualLabelName]
+  });
+} catch (error) {
+  responses.push(`Failed to add label \`${actualLabelName}\`: ${error.message}`);
+}
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: This suggestion correctly identifies missing error handling for dynamic label commands. Adding a try/catch block prevents the workflow from crashing on API errors and provides clear feedback to the user, which is a critical improvement for robustness.

Medium
Allow issue authors to reopen issues

Update the /reopen command to allow the original issue author to reopen an
issue, in addition to users with write permissions.

.github/workflows/issue-bot.yml [313-330]

 // --------------------
-// /reopen (requires write permission)
+// /reopen (requires write permission OR issue author)
 // --------------------
 if (isCommand(line, "reopen")) {
-  const canReopen = await hasWritePermission(commenter);
-  if (!canReopen) {
-    responses.push("Only maintainers and collaborators can reopen issues.");
+  const isIssueAuthor = issue.user.login.toLowerCase() === commenter.toLowerCase();
+  const hasMaintainerAccess = await hasWritePermission(commenter);
+
+  if (!isIssueAuthor && !hasMaintainerAccess) {
+    responses.push("Only maintainers or the issue author can reopen this issue.");
     continue;
   }
 
   await github.rest.issues.update({
     owner,
     repo,
     issue_number: issueNumber,
     state: "open"
   });
 
   responses.push("Reopened this issue.");
   continue;
 }
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out that the /reopen command logic is inconsistent with GitHub's permissions, which allow the issue author to reopen. Aligning the bot's behavior with the /close command and standard GitHub functionality is a valuable improvement.

Medium
Possible issue
Handle different line ending styles

Modify the line splitting logic to handle both Windows (\r\n) and Unix (\n) line
endings by using a regular expression.

.github/workflows/issue-bot.yml [28-32]

 const rawBody = comment.body.trim();
 const lines = rawBody
-  .split('\n')
+  .split(/\r?\n/)
   .map(line => line.trim())
   .filter(Boolean);
  • Apply / Chat
Suggestion importance[1-10]: 3

__

Why: While using split(/\r?\n/) is more robust for handling cross-platform line endings, the current code works correctly because the subsequent .map(line => line.trim()) removes the carriage return (\r) characters. This is a good practice suggestion but does not fix an actual bug.

Low
  • Update

Signed-off-by: khushiiagrawal <khushisaritaagrawal@gmail.com>
Copy link
Collaborator

@rh-rahulshetty rh-rahulshetty left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO we should probably adopt something that already exists from the Github Marketplace like: https://github.com/marketplace/actions/custom-slash-commands

This Github action uses YAML config, which will make configuration much easier.

We will need something common across all Krkn repositories.

@chaitanyaenr @paigerube14 @tsebastiani looping in to get your feedback if there is any existing tool that we are using for managing slash commands.

@paigerube14
Copy link
Contributor

@rh-rahulshetty we don't currently have any existing tooling that is able to label or assign issues/pr's. Think it is definitely needed. I think a more reusable marketplace action would be better so we don't need to reconfigure each repo each time we want a change

@AR21SM
Copy link
Contributor

AR21SM commented Jan 28, 2026

@paigerube14 @rh-rahulshetty @chaitanyaenr @tsebastiani Hey!
Just wanted to share what I've noticed, most major CNCF projects use actual bots instead of GitHub Actions for this kind of automation.
Quick examples:

  • Kubernetes, Knative, Volcano - all use Prow
  • Envoy - uses RepoKitteh

Why bots work better:
GitHub has restrictions on who can be assigned to issues - generally limited to collaborators, org members, or the issue author. GitHub Actions follow these same API restrictions.

Bots like Prow run as GitHub Apps with elevated permissions, allowing them to handle assignments more reliably for external contributors. This is why projects with lots of community contributions prefer bot infrastructure over GitHub Actions.

@khushiiagrawal
Copy link
Contributor Author

@AR21SM thanks for your suggestion. im aware that bots/apps handle permissions much better (specifically bypassing the assignee restrictions that affect GitHub Actions), but adding a bot requires org level installation and infrastructure that I cannot configure myself.

@rh-rahulshetty @paigerube14 , to address the reusability concern: i agree we shouldn't duplicate this workflow. i can convert this into a Reusable Workflow located in a central repository (e.g - krkn-chaos/.github), or we can adopt a Marketplace action as suggested.

if we want to solve the permission issues AR21SM mentioned without spinning up Prow, we would need to run the Action using an Admin PAT instead of the default GITHUB_TOKEN.
let me know which approach fits better. thanks!

@AR21SM
Copy link
Contributor

AR21SM commented Jan 28, 2026

@AR21SM thanks for your suggestion. im aware that bots/apps handle permissions much better (specifically bypassing the assignee restrictions that affect GitHub Actions), but adding a bot requires org level installation and infrastructure that I cannot configure myself.

@rh-rahulshetty @paigerube14 , to address the reusability concern: i agree we shouldn't duplicate this workflow. i can convert this into a Reusable Workflow located in a central repository (e.g - krkn-chaos/.github), or we can adopt a Marketplace action as suggested.

if we want to solve the permission issues AR21SM mentioned without spinning up Prow, we would need to run the Action using an Admin PAT instead of the default GITHUB_TOKEN. let me know which approach fits better. thanks!

@khushiiagrawal using an Admin PAT wouldn't solve the external contributor assignment issue. The limitation is at the GitHub API level: only users with push access (collaborator)or org members can be assigned to the native assignee field, regardless of what token is used.

@AR21SM
Copy link
Contributor

AR21SM commented Jan 28, 2026

I think this might be better suited for maintainers to handle directly. Just a thought! 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enable /assign and /label slash commands for contributors

4 participants