At my workplace, we sometimes find ourselves in situations where a PR passes all tests, and has been reviewed and approved, but still shouldn’t be merged. Sometimes this is because that PR needs some other work in another repository to be merged and deployed first. Sometimes it’s because merging the PR will kickstart some process like sending out emails, and we are waiting to start that process at a certain time.

Whatever the reason, our convention is that we add a “Do Not Merge” label to the PR. But we recently had a case where someone didn’t see the label and clicked the merge button anyway. I can tell you that it’s not fun scrambling to hit the “cancel action” button on Github before the code gets deployed! So we started looking into a way to prevent such issues.

Now, you might ask why we don’t just leave these PRs as drafts. While that would stop them from being merged on an accidental click, there is still some risk that someone might just mark it ready for review and merge it without checking the label. We also have some automation set up, like automatically changing card state when a PR is marked as ready, which would not work if we leave PRs in draft. Luckily, I found a better solution.

After coming across this post from Jesse Squires, I decided to try the improved version of a “Do Not Merge” check he suggests.

name: Do Not Merge

on:
  pull_request:
    types: [synchronize, opened, reopened, labeled, unlabeled]

jobs:
  do-not-merge:
    if: ${{ contains(github.event.*.labels.*.name, 'do not merge') }}
    name: Prevent Merging
    runs-on: ubuntu-latest
    steps:
      - name: Check for label
        run: |
          echo "Pull request is labeled as 'do not merge'"
          echo "This workflow fails so that the pull request cannot be merged"
          exit 1          

Our first attempt was dropping this into the repository, which worked, but we have a lot of repositories and we sometimes create new ones too. Having to copy this check to all repositories seems like a lot of work! But thanks to a coworker discovering that you can set repository-wide workflows, we were able to set up all of this organization-wide.

To do that, you first add this workflow file in some repository. It doesn’t need to be (and probably shouldn’t be) in your .github/workflows folder. You might even want to create a new repository to contain just this workflow file.

A github repository, with a single file named do-not-merge.yml at the root of the repository. The file contains the code listed earlier in this page.

Next, go to your organization settings and select “Actions > General” on the side menu.

Github side bar menu, with heading Action expanded, and General selected inside that section.

Scroll to the bottom, where you’ll find “Required workflows”. Click to add a workflow.

The required workflows section in Github organization settings. An “Add workflow” button is present.

Then select the repository where you added your action, and write the path to the workflow file within that repository.

Add required workflow page. The previously mentioned repository is selected, and the path do-not-merge.yml is written next to that. A selection below has picked ‘All repositories’.

You’re now done! All PRs in all repositories will run the do not merge label check, and will prevent you from merging any PR with the label.

The checks section on a PR page. A check named “Do Not Merge” has failed, and the merge button is disabled. Github warns that all checks must pass before merging.

One caveat is that there seems to be a bug on Github’s end of things where for any PR that was open at the time you added this check, the check gets stuck with the message “Expected - Waiting for status to be reported”. If that happens, add the “do not merge” label then remove it. This will remind Github to actually run the check.

To make the experience a bit smoother for new repositories, you can also add “do not merge” as a default PR label. To do so, go to the “Repository > Repository defaults” section on the side bar.

Github side bar menu, with heading Repository expanded, and Repository defaults selected inside that section.

Click “New label”, and create a label named “do not merge”.

The repository labels section in Github organization settings. A new label is being added, with the name do not merge.

This will only apply to new repositories, so you may need to add the label to your existing repositories. But even if you don’t add the label to the repository, the check should not block you so you don’t have to worry about going through all your repositories to add this label.