diff --git a/.github/workflows/assign-command.yml b/.github/workflows/assign-command.yml new file mode 100644 index 00000000..205e7a53 --- /dev/null +++ b/.github/workflows/assign-command.yml @@ -0,0 +1,65 @@ +name: Assign command + +on: + issue_comment: + types: [created] + +permissions: + issues: write + +jobs: + assign: + if: ${{ !github.event.issue.pull_request && (github.event.comment.body == '/assign' || github.event.comment.body == '/unassign') }} + runs-on: ubuntu-latest + steps: + - name: Handle /assign + if: github.event.comment.body == '/assign' + uses: actions/github-script@v7 + with: + script: | + const { owner, repo } = context.repo; + const issue_number = context.issue.number; + const commenter = context.payload.comment.user.login; + + const { data: issue } = await github.rest.issues.get({ owner, repo, issue_number }); + + if (issue.assignees.length > 0) { + const names = issue.assignees.map(a => `@${a.login}`).join(', '); + await github.rest.issues.createComment({ + owner, repo, issue_number, + body: `⚠️ This issue is already assigned to ${names}. Ask them to comment \`/unassign\` first if they're no longer working on it.`, + }); + return; + } + + await github.rest.issues.addAssignees({ owner, repo, issue_number, assignees: [commenter] }); + await github.rest.issues.createComment({ + owner, repo, issue_number, + body: `✅ Assigned to @${commenter}. Comment \`/unassign\` if you can no longer work on this. Open a PR that includes \`Closes #${issue_number}\` in its description when you're ready for review.`, + }); + + - name: Handle /unassign + if: github.event.comment.body == '/unassign' + uses: actions/github-script@v7 + with: + script: | + const { owner, repo } = context.repo; + const issue_number = context.issue.number; + const commenter = context.payload.comment.user.login; + + const { data: issue } = await github.rest.issues.get({ owner, repo, issue_number }); + const isAssigned = issue.assignees.some(a => a.login === commenter); + + if (!isAssigned) { + await github.rest.issues.createComment({ + owner, repo, issue_number, + body: `⚠️ @${commenter}, you're not currently assigned to this issue.`, + }); + return; + } + + await github.rest.issues.removeAssignees({ owner, repo, issue_number, assignees: [commenter] }); + await github.rest.issues.createComment({ + owner, repo, issue_number, + body: `Unassigned @${commenter}. This issue is open again — comment \`/assign\` to pick it up.`, + }); diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..20f6137f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,48 @@ +# Contributing to Modly + +Thanks for wanting to help out! You don't need write access to the repository to +pick up a ticket, work on it, and ship a fix — here's how the flow works. + +## Finding something to work on + +- Browse [open issues](https://github.com/lightningpixel/modly/issues) or the + [project board](https://github.com/users/lightningpixel/projects/1). +- Issues labeled `good first issue` are a good place to start if you're new to + the codebase. See [`CLAUDE.md`](./CLAUDE.md) for an architecture overview. + +## Claiming a ticket + +Comment **`/assign`** on the issue you want to work on. A bot will assign it to +you automatically — no repo permissions required. + +- Only one person can be assigned to an issue at a time. If it's already + assigned, ask the assignee first or wait for them to release it. +- No longer working on it? Comment **`/unassign`** to free it up for someone + else. + +This keeps the [project board](https://github.com/users/lightningpixel/projects/1) +honest: an assigned issue moves to **In progress** automatically, so anyone +looking at the board can see what's actively being worked on. + +## Submitting your work + +1. **Fork** the repository and create a branch for your change. +2. Make your change. Keep it focused — one issue, one PR. +3. Run the checks locally before opening a PR: + ```bash + npm run lint + npm run test + ``` +4. Open a **pull request** against `dev`. Include `Closes #` in + the PR description so it's linked to the ticket and closes it automatically + on merge. + +Opening a PR from your fork moves the linked issue to **Ready to review** on +the board. Once a maintainer approves the review, it moves to **Ready to +test**; once merged, it moves to **Done**. + +## Getting help + +If something in an issue is unclear, ask in a comment on the issue itself +before starting — it's cheaper to clarify scope up front than to redo work +later.