Running GitHub Actions only on Certain Pull Requests
GitHub Actions is powerful and allows you to run various workflows on your pull requests. There is an entire marketplace of actions that you can use, reuse, and abuse. GitHub Actions became a defacto standard for many developer teams that want to run jobs when creating a pull request.
This GitHub feature does come with great flexibility and allows you to define precisely how you want to run any workflow. However, it can be cumbersome in certain situations to bend it to act the way you want.
A common need is to trigger an action for a pull request matching certain criteria. Let's see how you can achieve this.
Running a job on a particular branch
Filtering workflows run by branches is relatively easy. If you want to limit GitHub Actions jobs to run only if a pull request targets a particular branch, there's a built-in mechanism for it.
GitHub Actions workflow definition allows you to filter runs based on a branch name. It is as easy as adding a branches
condition in your YAML file.
name: Run tests
on:
pull_request:
branches:
- main
jobs:
build:
...
By specifying that you only want to trigger this workflow on pull requests targeting the main
branch, GitHub Actions does the heavy-lifting of filtering the concerned events and runs the jobs for you.
Alternatively, you can run a workflow on all branches except some by leveraging the branches-ignore
key:
name: Run tests
on:
push:
branches-ignore:
- notest
jobs:
build:
...
By using these settings, the workflow's jobs will be executed and reported — or not — to the pull request and visible in the checks tab and near the merge button.
Running a job under any condition
This is where things start getting a little bit more complicated. While GitHub Actions has a native way of running — or not — a workflow based on branch names, that's all it provides as a condition to select the workflows.
That means that if you're not using branch names to decide wherever a workflow needs to run, then it will run. You can't decide to run a workflow based on any other criteria.
That does not mean you have to execute the jobs from the workflow, and that's where things get interesting. You can use an if
condition in a job to make it run — or not. Here's an example:
name: Run Tests
on:
pull_request:
branches:
- main
jobs:
build:
if: ${{ !contains(github.event.head_commit.message, '#docs') }}
steps:
- name: Run tests
run: echo running tests
Here, the job will only be executed if the commit message does not contain the text #docs
.
That's the trick: by using a if
condition, you can decide if a job will execute anything. This is, unfortunately, the only way to not execute (the content of) a workflow using any other predicate than the branch name.
You can use different kinds of conditions. The contexts exposed through the workflow are documented and allow to retrieve various information about the workflow, such as the source event. That source event exposes many fields. For example, in the case of a pull_request
event, you can access information such as the pull request author or creation timestamp. The list of available functions to write conditions is also documented.
This allows building more complex filtering on workflow runs.
Running a job based on a pull request label
Using the same principle, you could trigger a job based on the presence of a label on a pull request. Configure your workflow to be triggered when a pull request's label changes, and filter based on the label's presence.
Here's an example:
name: Run Tests
on:
pull_request:
types:
- labeled
jobs:
build:
if: ${{ github.event.label.name == 'queued' }}
steps:
- name: Run tests
run: echo running tests
When a pull request has a new label added or removed, the event will trigger the workflow, which will execute the test suite if the label is named queued
.
About Check Statuses
Ideally, if a workflow does nothing, you might be tempted to use the various check statuses and conclusions offered by GitHub API. It would be great to report a little gray square for your workflow, stating it didn't do anything but that it is normal.
Regrettably, this is not available through GitHub Actions. The way GitHub Actions work boils down to this:
- Workflow queue ➝
Queued
- Workflow start ➝
In progress
- Workflow fails because of GitHub ➝
Error
- Workflow fails because of your script/tests ➝
Failure
- Workflow succeed ➝
Success
There is no way for you, as a user, to have control over the returned status. If you wanted to use the conclusion Skipped
or Neutral
for example, to indicate your workflow didn't do anything, there is no possibility to do this currently. Only GitHub Applications, such as Mergify, can use those statuses.
That being said, it does not prevent the concept of skipping the actual job to work as expected!