name: PR Slash Commands description: >- Handles /lgtm, /hold, and /stageblog slash commands on PRs. Core maintainers can approve via /lgtm (adds an accepted label, submits a bot APPROVE review, and enables auto-merge). Core maintainers or CODEOWNERS can block via /hold or trigger a blog preview staging workflow via /stageblog. Approval is invalidated when new commits are pushed. Optionally auto-approves narrowly-scoped Dependabot PRs (dependabot-auto-approve). inputs: github-token: description: >- GitHub App installation token used for all API calls: team-membership checks, labels, comments, reactions, reviews, auto-merge, CODEOWNERS fetch, and workflow dispatch. Must have Organization Members:read plus Repository Pull requests:write and Contents:read (and Actions:write if /stageblog is enabled). The default GITHUB_TOKEN will NOT work — it lacks read:org and its approvals may not count toward required reviews. required: true approved-label: description: Label added by /lgtm to mark the PR as accepted required: false default: accepted 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 any PR. Only these teams can /lgtm — CODEOWNERS does not grant /lgtm authorization. Members may also /hold and /stageblog any PR. required: false default: core-maintainers force-allow-teams: description: >- Comma-separated team slugs (within the repo's org) whose members may use /lgtm force. This bypasses the self-approval guard and CODEOWNERS — only membership in one of these teams grants it. required: false default: core-maintainers,lead-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. Skipped if the label was most recently applied manually by a member of always-allow-teams or force-allow-teams (not the bot via /lgtm). Set to 'false' to keep approval across pushes unconditionally. 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' enable-auto-merge: description: >- Enable auto-merge on the PR after a successful /lgtm. The PR will merge automatically once all required checks pass. Requires the repo to have "Allow auto-merge" enabled in Settings. Disabled on /lgtm cancel and on push invalidation. required: false default: 'true' auto-merge-method: description: Merge method for auto-merge — one of squash, merge, rebase required: false default: squash remove-labels-on-accept: description: >- Comma-separated label names to remove from the PR when /lgtm accepts it (e.g. in-review,draft,proposal). Missing labels are ignored. required: false default: '' project-number: description: >- Org-level Project (V2) number to update when /lgtm accepts the PR. Leave empty to disable project updates. The PR is added to the project if not already present. required: false default: '' project-status-field: description: Name of the single-select field to set on accept required: false default: Status project-accepted-option: description: Name of the option to set in the status field on accept required: false default: Accepted project-gate-label: description: >- If set, only update the project status when the PR has this label. Leave empty to update the project for every accepted PR. required: false default: '' 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/**' dependabot-auto-approve: description: >- Opt-in Dependabot auto-approve policy. Empty (default) = disabled. 'dev-patch-minor' approves + labels + enables auto-merge on Dependabot PRs where ALL of these hold: author is the real dependabot[bot] app (login and user id), same-repo dependabot/ head branch, every commit is signature-verified and dependabot-authored, changed files are a subset of dependabot-allowed-files, every updated dependency is direct:development with a semver-patch/minor update (per Dependabot's commit-message metadata; missing metadata never approves), all check runs on the head SHA are green with at least one non-skipped check, and the PR is open, non-draft, conflict-free, and not on hold. Requires the caller workflow to also trigger on pull_request_target [opened, reopened] and check_suite [completed]. Any other value disables the mode (fail closed). required: false default: '' dependabot-allowed-files: description: >- Comma-separated exact file paths a Dependabot PR may touch to qualify for auto-approve. A PR changing any other file is never auto-approved. required: false default: 'package.json,package-lock.json' outputs: result: description: >- One of: lgtm-added, lgtm-forced, lgtm-removed, hold-added, hold-removed, invalidated, invalidate-skipped, unauthorized, force-unauthorized, self-lgtm-blocked, stageblog-dispatched, stageblog-not-blog, stageblog-disabled, dependabot-approved, dependabot-skipped, 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 }} FORCE_ALLOW_TEAMS: ${{ inputs.force-allow-teams }} CODEOWNERS_PATH: ${{ inputs.codeowners-path }} INVALIDATE_ON_PUSH: ${{ inputs.invalidate-on-push }} SUBMIT_REVIEW: ${{ inputs.submit-review }} ENABLE_AUTO_MERGE: ${{ inputs.enable-auto-merge }} AUTO_MERGE_METHOD: ${{ inputs.auto-merge-method }} REMOVE_LABELS_ON_ACCEPT: ${{ inputs.remove-labels-on-accept }} PROJECT_NUMBER: ${{ inputs.project-number }} PROJECT_STATUS_FIELD: ${{ inputs.project-status-field }} PROJECT_ACCEPTED_OPTION: ${{ inputs.project-accepted-option }} PROJECT_GATE_LABEL: ${{ inputs.project-gate-label }} STAGEBLOG_WORKFLOW: ${{ inputs.stageblog-workflow }} STAGEBLOG_PATHS: ${{ inputs.stageblog-paths }} DEPENDABOT_AUTO_APPROVE: ${{ inputs.dependabot-auto-approve }} DEPENDABOT_ALLOWED_FILES: ${{ inputs.dependabot-allowed-files }} with: github-token: ${{ inputs.github-token }} script: | const main = require(process.env.GITHUB_ACTION_PATH + '/main.js'); await main({ github, context, core });