name: Maintainer Command Reactions on: issue_comment: types: [created, edited] permissions: {} concurrency: group: maintainer-command-reactions-${{ github.event.comment.id }} cancel-in-progress: true jobs: react: if: ${{ !endsWith(github.actor, '[bot]') }} runs-on: ubuntu-24.04 permissions: issues: write pull-requests: write env: MAINTAINER_COMMAND_REACTIONS: ${{ vars.MAINTAINER_COMMAND_REACTIONS || '/autoclose,/clawsweeper autoclose,/clawsweeper automerge,/merge,/land,/landpr' }} steps: - name: React to maintainer slash command uses: actions/github-script@v9 with: script: | const comment = context.payload.comment; const issue = context.payload.issue; const commands = (process.env.MAINTAINER_COMMAND_REACTIONS || "") .split(",") .map((command) => command.trim()) .filter(Boolean); const commandLine = String(comment.body || "") .split(/\r?\n/) .map((line) => line.trim()) .find((line) => commands.some((command) => line === command || line.startsWith(`${command} `))); if (!commandLine) { core.info(`Skipping comment ${comment.id}; no tracked maintainer command found.`); return; } const isAutocloseCommand = commandLine === "/autoclose" || commandLine.startsWith("/autoclose ") || commandLine === "/clawsweeper autoclose" || commandLine.startsWith("/clawsweeper autoclose "); if (!issue.pull_request && !isAutocloseCommand) { core.info("Skipping non-autoclose command reaction because the comment is not on a pull request."); return; } const maintainerPermissions = new Set(["admin", "maintain", "write"]); let permission = "none"; try { const result = await github.rest.repos.getCollaboratorPermissionLevel({ owner: context.repo.owner, repo: context.repo.repo, username: comment.user.login, }); permission = String(result.data.permission || "none").toLowerCase(); } catch (error) { if (error.status !== 404) { core.info(`Could not resolve repository permission for ${comment.user.login}: ${error.message}`); } } if (!maintainerPermissions.has(permission)) { core.info( `Skipping non-maintainer command reaction for ${comment.user.login}; repository permission is ${permission}.`, ); return; } async function react(content) { try { await github.rest.reactions.createForIssueComment({ owner: context.repo.owner, repo: context.repo.repo, comment_id: comment.id, content, }); core.info(`Added ${content} reaction to comment ${comment.id}.`); } catch (error) { if (error.status === 422 && /already exists/i.test(String(error.message))) { core.info(`${content} reaction already exists on comment ${comment.id}.`); return; } if (error.status === 403 && /resource not accessible by integration/i.test(String(error.message))) { core.warning(`${content} reaction could not be added with this token: ${error.message}`); return; } throw error; } } await react("eyes"); core.info(`Maintainer command observed on ${issue.pull_request ? "PR" : "issue"} #${issue.number}: ${commandLine}`);