How to Implement /hold and /unhold Merge Gating in GitHub Actions
/hold sets a blocking status so a PR cannot merge, and /unhold clears it, giving reviewers a chat-driven merge brake.
On /hold, add a do-not-merge/hold label and set a failing commit status that is a required check. On /unhold, remove the label and set the status to success. Gate both on an authorized commenter.
Steps
- Match
/holdor/unholdand verify permission. - Set a required commit status: failure for hold, success for unhold.
- Add or remove the
do-not-merge/holdlabel to mirror state.
Hold step
.github/workflows/ci.yml
- uses: actions/github-script@v7
with:
script: |
const held = context.payload.comment.body.trim() === '/hold'
const pr = await github.rest.pulls.get({
owner: context.repo.owner, repo: context.repo.repo,
pull_number: context.issue.number
})
await github.rest.repos.createCommitStatus({
owner: context.repo.owner, repo: context.repo.repo,
sha: pr.data.head.sha,
context: 'hold',
state: held ? 'failure' : 'success',
description: held ? 'PR is held' : 'Hold cleared'
})Gotchas
- Make the
holdstatus a required check in branch protection or it will not block merges. - Only authorized reviewers should be able to hold or release a PR.
- The status is keyed to the head SHA; a new push resets it, so re-apply if needed.
Related guides
How to Implement Prow-Style Commands Like /lgtm and /approveImplement Prow-style commands such as /lgtm and /approve in GitHub Actions, mapping each to a label and a req…
How to Restrict Who Can Run Slash Commands in GitHub ActionsRestrict who can run chat-triggered CI commands in GitHub Actions by checking the commenter repository permis…