name: PR Slash Commands description: >- Handles /lgtm, /hold, and /stageblog slash commands on PRs. Authorized users (core maintainers or CODEOWNERS of the PR's changed files) can approve via /lgtm (adds a label + bot APPROVE review), block via /hold, or trigger a blog preview staging workflow via /stageblog. Approval is invalidated when new commits are pushed. inputs: github-token: description: >- Token used for org team-membership checks only. MUST have read:org scope — the default GITHUB_TOKEN cannot do this. Use a fine-grained PAT with Organization Members:read, or a GitHub App installation token with the same. required: true bot-token: description: >- Token used for all repo API calls: comments, reactions, labels, reviews, CODEOWNERS fetch, listFiles, and workflow dispatch. Defaults to the automatic GITHUB_TOKEN so user-visible side effects show as authored by github-actions[bot]. The caller workflow must grant this token the necessary permissions (pull-requests: write, issues: write, contents: read; plus actions: write for /stageblog). required: false default: ${{ github.token }} approved-label: description: Label added by /lgtm and used by the status job as the merge gate required: false default: approved hold-label: description: Label added by /hold to block merging required: false default: do-not-merge/hold always-allow-teams: description: >- Comma-separated team slugs (within the repo's org) whose members may /lgtm or /hold any PR regardless of CODEOWNERS required: false default: core-maintainers codeowners-path: description: Path to the CODEOWNERS file (fetched from the PR base branch) required: false default: .github/CODEOWNERS invalidate-on-push: description: >- Remove the approved label and dismiss the bot review when new commits are pushed to the PR. Set to 'false' to keep approval across pushes. required: false default: 'true' submit-review: description: >- Whether to submit a bot APPROVE review alongside the label on /lgtm. Set to 'false' to rely on the label alone. required: false default: 'true' stageblog-workflow: description: >- Workflow file name (e.g. stage-blog.yml) to dispatch when /stageblog is invoked. Leave empty to disable the command. The workflow must accept inputs 'pr_number' and 'head_sha'. required: false default: '' stageblog-paths: description: >- Comma-separated glob patterns (CODEOWNERS-style: **, *, ?, leading /, trailing /) matched against the PR's changed files. /stageblog is refused if no file matches. required: false default: 'blog/**' outputs: result: description: >- One of: lgtm-added, lgtm-removed, hold-added, hold-removed, invalidated, unauthorized, self-lgtm-blocked, stageblog-dispatched, stageblog-not-blog, stageblog-disabled, noop value: ${{ steps.handle.outputs.result }} actor: description: Login of the user who triggered the action (commenter) value: ${{ steps.handle.outputs.actor }} runs: using: composite steps: - name: Handle slash command id: handle uses: actions/github-script@v8 env: APPROVED_LABEL: ${{ inputs.approved-label }} HOLD_LABEL: ${{ inputs.hold-label }} ALWAYS_ALLOW_TEAMS: ${{ inputs.always-allow-teams }} CODEOWNERS_PATH: ${{ inputs.codeowners-path }} INVALIDATE_ON_PUSH: ${{ inputs.invalidate-on-push }} SUBMIT_REVIEW: ${{ inputs.submit-review }} STAGEBLOG_WORKFLOW: ${{ inputs.stageblog-workflow }} STAGEBLOG_PATHS: ${{ inputs.stageblog-paths }} ORG_TOKEN: ${{ inputs.github-token }} with: github-token: ${{ inputs.bot-token }} script: | const main = require(process.env.GITHUB_ACTION_PATH + '/main.js'); await main({ github, context, core });