Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions .github/workflows/assign.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
name: Issue / PR Self-Assign

on:
issue_comment:
types: [created]

permissions:
issues: write
pull-requests: write

jobs:
assign:
if: |
!github.event.sender.type == 'Bot' &&
contains(github.event.comment.body, '/assign')
runs-on: ubuntu-latest

steps:
- name: Parse /assign command
id: parse
uses: actions/github-script@v7
with:
script: |
const body = context.payload.comment.body.trim();
const commenter = context.payload.comment.user.login;
const senderType = context.payload.comment.user.type;

if (senderType === 'Bot') {
core.setOutput('skip', 'true');
return;
}

// Supports /assign or /assign @username
const match = body.match(/^\/assign(?:\s+@?([\w-]+))?/im);

if (!match) {
core.setOutput('skip', 'true');
return;
}

const targetUser = match[1] ? match[1] : commenter;

core.setOutput('skip', 'false');
core.setOutput('target_user', targetUser);
core.setOutput('commenter', commenter);

- name: Assign user
if: steps.parse.outputs.skip == 'false'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const targetUser = '${{ steps.parse.outputs.target_user }}';
const commenter = '${{ steps.parse.outputs.commenter }}';
const issueNumber = context.payload.issue.number;
const owner = context.repo.owner;
const repo = context.repo.repo;

async function postComment(body) {
await github.rest.issues.createComment({
owner, repo, issue_number: issueNumber, body
});
}

let userMeta;
try {
const { data } = await github.rest.users.getByUsername({ username: targetUser });
userMeta = data;
} catch (err) {
if (err.status === 404) {
await postComment(
`> ❌ @${commenter} – \`${targetUser}\` is not a valid GitHub username. ` +
`Please check the spelling and try again.`
);
core.setFailed(`User '${targetUser}' not found on GitHub.`);
return;
}
throw err;
}

if (userMeta.type === 'Bot') {
await postComment(
`> ⚠️ @${commenter} – bot accounts cannot be assigned to issues or PRs.`
);
return;
}

const { data: issue } = await github.rest.issues.get({
owner, repo, issue_number: issueNumber
});

const currentAssignees = issue.assignees.map(a => a.login.toLowerCase());

if (currentAssignees.includes(targetUser.toLowerCase())) {
const isSelf = targetUser.toLowerCase() === commenter.toLowerCase();
await postComment(
isSelf
? `> ℹ️ @${commenter} – you are already assigned to this ${issue.pull_request ? 'PR' : 'issue'}.`
: `> ℹ️ @${commenter} – \`${targetUser}\` is already assigned to this ${issue.pull_request ? 'PR' : 'issue'}.`
);
return;
}

try {
await github.rest.issues.addAssignees({
owner,
repo,
issue_number: issueNumber,
assignees: [targetUser]
});
} catch (err) {
// GitHub returns 422 when a user lacks repo access
if (err.status === 422) {
await postComment(
`> ❌ @${commenter} – \`${targetUser}\` could not be assigned. ` +
`They may not have the necessary repository access. ` +
`Please ask a maintainer for help.`
);
core.setFailed(`Cannot assign '${targetUser}' – insufficient permissions.`);
return;
}
throw err;
}

const isSelf = targetUser.toLowerCase() === commenter.toLowerCase();
const itemType = issue.pull_request ? 'PR' : 'issue';
await postComment(
isSelf
? `> ✅ @${commenter} has self-assigned this ${itemType}. Good luck! 🚀`
: `> ✅ @${commenter} has assigned @${targetUser} to this ${itemType}.`
);
36 changes: 31 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,20 +82,46 @@ devcard/
- **Conventional Commits** for commit messages (`feat:`, `fix:`, `docs:`, `chore:`)
- Write tests for new features and bug fixes

## Self-Assigning Issues & PRs

You can assign yourself (or another contributor) to any open issue or pull request by posting a comment with the `/assign` command — no maintainer action required.

| Command | Effect |
|---|---|
| `/assign` | Assigns **you** (the commenter) to the issue/PR |
| `/assign @username` | Assigns the specified GitHub **@username** |

**Examples:**

```
/assign
```
```
/assign @octocat
```

The bot will reply with a confirmation comment (or an error message if the username is invalid, the user is already assigned, or the account lacks repo access).

> **Note:** Bot accounts cannot be assigned. Duplicate assignments are silently prevented.

---

## Pull Request Process

1. Create a feature branch from `main`: `git checkout -b feat/your-feature`
2. Make your changes with clear, descriptive commits
3. Ensure all tests pass: `pnpm test`
4. Ensure linting passes: `pnpm lint`
5. Open a PR against `main` with a clear description of the change
6. Wait for review — maintainers will respond within 48 hours
2. Self-assign the related issue with `/assign` so others know you're working on it
3. Make your changes with clear, descriptive commits
4. Ensure all tests pass: `pnpm test`
5. Ensure linting passes: `pnpm lint`
6. Open a PR against `main` with a clear description of the change
7. Wait for review — maintainers will respond within 48 hours

## Reporting Issues

- Use GitHub Issues for bug reports and feature requests
- Include reproduction steps for bugs
- Search existing issues before creating a new one
- Use `/assign` on an issue you plan to fix so work isn't duplicated

## Code of Conduct

Expand Down
62 changes: 62 additions & 0 deletions VERIFY_IMPLEMENTATION.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Implementation Verification & Setup Guide

This guide covers how to set up the project and verify the newly implemented `/assign` GitHub Action.

## 1. Local Project Setup
To ensure your local environment matches the project structure and all dependencies are present:

1. **Install pnpm** (if not already installed):
```bash
npm install -g pnpm
```

2. **Install Dependencies**:
Run this from the root of the repository to install all workspace dependencies:
```bash
pnpm install
```

3. **Verify Configuration Files**:
- Check that `.github/workflows/assign.yml` exists.
- Check that `CONTRIBUTING.md` includes the "Self-Assigning Issues & PRs" section.

---

## 2. Verifying the GitHub Action
GitHub Actions run on GitHub's infrastructure. To verify the `/assign` command:

1. **Push Changes**:
Ensure the new `.github/workflows/assign.yml` file is pushed to your GitHub repository.
```bash
git add .github/workflows/assign.yml CONTRIBUTING.md
git commit -m "feat: implement /assign GitHub Action"
git push origin <your-branch-name>
```

2. **Trigger the Action**:
- Open any Issue or Pull Request on your GitHub repository.
- Post a comment: `/assign`
- **Expected Result**: The "Issue / PR Self-Assign" workflow should trigger. You can see this under the "Actions" tab.
- **Confirmation**: A bot (github-actions) should reply to your comment and you should be added as an assignee.

3. **Test Edge Cases**:
- Post: `/assign @nonexistent_user_12345` (Should reply with an error).
- Post: `/assign @another_valid_user` (Should assign that user).
- Post: `/assign` again (Should notify you that you are already assigned).

---

## 3. Optional: Local Workflow Testing
If you want to test the workflow logic without pushing to GitHub, you can use **act**:

1. **Install act**:
```bash
brew install nektos/tap/act
```

2. **Run the workflow**:
Note: This requires Docker to be running.
```bash
act issue_comment
```
*(Note: You may need to provide a mock event JSON to simulate a specific comment body like "/assign")*