My current team has started using GitHub Actions to automate some tedious tasks for pull requests. In particular, we use labels on GitHub to categorize pull requests or highlight important metadata about them. Most of the time, a machine can figure out which labels are appropriate to add or remove. This is a great use case for GitHub Actions.
Labeling stacked pull requests
One common workflow (or problem, depending on how you view it) in git and GitHub is stacking pull requests. This is necessary when you have multiple pull requests that depend on each other and must merge into your main branch sequentially. For example, pull request A
is opened to merge into main
, pull request B
is opened to merge into A
, pull request C
is opened to merge into B
, and so on. After A
is merged, B
must be updated to merge into main
, and so on.
We like to highlight this and make it obvious. We have a brightly colored stacked PR
label, which communicates to the reviewer “hey! this PR is part of a stack, just so you know.” It is tedious to repeatedly manually add and remove this label for every pull request in the stack, so I wrote this workflow to automate it.
name: Label stacked PRs
on:
pull_request:
types: [synchronize, opened, reopened, edited, closed]
env:
BASE_BRANCH: ${{ github.base_ref }}
jobs:
remove-labels:
runs-on: ubuntu-latest
steps:
- name: remove labels
uses: actions-ecosystem/action-remove-labels@v1
if: env.BASE_BRANCH == 'main'
with:
github_token: ${{ github.token }}
labels: stacked PR
add-labels:
runs-on: ubuntu-latest
steps:
- name: add labels
uses: actions-ecosystem/action-add-labels@v1
if: env.BASE_BRANCH != 'main'
with:
github_token: ${{ github.token }}
labels: stacked PR
This workflow contains two jobs that are nearly identical. The first checks if the pull request is merging into the main
branch, and if so, it will remove the stacked PR
label, if present. The second job does the opposite, it will add the stacked PR
label to any pull request that is not targeting the main
branch. It uses two actions from the Actions Ecosystem community project, add-labels and remove-labels.
Do Not Merge
Another important label we have is do not merge
, which (unsurprisingly) indicates that a pull request should not be merged. Despite the label being a striking bright red, it could still be overlooked if all of our other CI status checks pass and you see that tempting, big green “merge” button on GitHub. It would be much safer if adding this label would fail the pull request to prevent you from merging it. We can write a workflow to do that using Michael Heap’s required-labels action.
name: Do Not Merge
on:
pull_request:
types: [opened, reopened, labeled, unlabeled]
jobs:
do-not-merge:
runs-on: ubuntu-latest
steps:
- name: check labels
uses: mheap/github-action-required-labels@v1
with:
mode: exactly
count: 0
labels: "do not merge"
This will check if the pull request has the do not merge
label. If it does, the workflow will fail. Once you remove the label, the workflow will pass.
To make this workflow actually prevent merging requires a few extra steps. You need to set up branch protection rules for your main branch and require this workflow to pass before a pull request can be merged. Once configured, the “merge” button will only be enabled if this workflow succeeds.
Update 21 March 2022
Thanks to my friend Ben Asher for sharing an improvement to the ‘Do Not Merge’ workflow, which can be rewritten without using third-party actions. You can find it below.
Also, I’ve started maintaining a collection of useful workflows on GitHub. Check them out.
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