Running GitHub Actions only on Certain Pull Requests

Running GitHub Actions only on Certain Pull Requests

Julien Danjou

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.

GitHub Actions logo

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.

GitHub Actions workflow report

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!