diff --git a/.github/scripts/issue-claim.mjs b/.github/scripts/issue-claim.mjs new file mode 100644 index 000000000..7e8b9ad01 --- /dev/null +++ b/.github/scripts/issue-claim.mjs @@ -0,0 +1,59 @@ +#!/usr/bin/env node +import { execFile } from "node:child_process"; +import { promisify } from "node:util"; + +const execFileAsync = promisify(execFile); +const issueNumber = process.env.ISSUE_NUMBER; +const commentBody = process.env.COMMENT_BODY || ""; +const commentUser = process.env.COMMENT_USER || ""; +const commentUserType = process.env.COMMENT_USER_TYPE || ""; + +// Only handle /claim as a standalone word +if (!/(?:^|\s)\/claim(?:\s|$)/.test(commentBody)) { + process.exit(0); +} + +// Don't let bots claim +if (commentUserType === "Bot") { + console.log("Commenter is a bot, skipping"); + process.exit(0); +} + +console.log(`/claim from @${commentUser} on #${issueNumber}`); + +async function gh(args) { + const { stdout } = await execFileAsync("gh", args, { maxBuffer: 1024 * 1024 }); + return stdout.trim(); +} + +async function ghJson(args) { + const out = await gh(args); + return JSON.parse(out); +} + +// Check current assignees +const assignees = await ghJson([ + "issue", "view", issueNumber, + "--json", "assignees", + "-q", ".assignees", +]); + +if (assignees.length > 0) { + const names = assignees.map((a) => `@${a.login}`).join(", "); + await gh([ + "issue", "comment", issueNumber, + "--body", `❌ @${commentUser} 这个 issue 已经有人认领了:${names}`, + ]); + process.exit(0); +} + +// Assign +await gh(["issue", "edit", issueNumber, "--add-assignee", commentUser]); + +// Confirm +await gh([ + "issue", "comment", issueNumber, + "--body", `✅ @${commentUser} 已认领 #${issueNumber},开始处理吧!`, +]); + +console.log(`Assigned @${commentUser} to #${issueNumber}`); diff --git a/.github/workflows/issue-claim.yml b/.github/workflows/issue-claim.yml new file mode 100644 index 000000000..11c0f0295 --- /dev/null +++ b/.github/workflows/issue-claim.yml @@ -0,0 +1,26 @@ +name: Issue Claim + +on: + issue_comment: + types: [created] + +permissions: + contents: read + issues: write + +jobs: + claim: + runs-on: ubuntu-latest + if: ${{ !github.event.issue.pull_request }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v4 + + - name: Handle /claim command + run: node .github/scripts/issue-claim.mjs + env: + ISSUE_NUMBER: ${{ github.event.issue.number }} + COMMENT_BODY: ${{ github.event.comment.body }} + COMMENT_USER: ${{ github.event.comment.user.login }} + COMMENT_USER_TYPE: ${{ github.event.comment.user.type }}