Only run job on specific branch with GitHub Actions

I'm relatively new to GitHub Actions and I have 2 jobs–one that runs my tests, and one that deploys my project onto a server.

Obviously I want the tests to run on every branch, but deploying should only happen when something gets pushed to master.

I'm struggling to find a way to run a job on a specific branch. I know it's possible to only run entire workflows on a specific branch, however that would mean I would have a "test" workflow and a "deploy" workflow.

This sounds like a solution, however they would run parallel. In an ideal world, the tests would run first, and only if they succeed, then the deploy job would start. This isn't the case when using 2 separate workflows.

How would I be able to achieve this? Is it possible to run jobs on a specific branch?


Solution 1:

In a recent update you can now put if conditionals at job level. See the documentation here. https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idif

I tested this workflow which runs the job test on every push, but only runs deploy on the master branch.

name: my workflow
on: push
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Execute tests
        run: exit 0
  deploy:
    runs-on: ubuntu-latest
    needs: test
    if: github.ref == 'refs/heads/master'
    steps:
      - name: Deploy app
        run: exit 0

What follows is my original answer, and an alternative solution if you prefer to have separate workflows.

The first workflow runs for every branch except master. In this workflow you run tests only.

on:
  push:
    branches:
      - '*'
      - '!master'

The second workflow runs for just master and runs both your tests and deploys if the tests were successfully passed.

on:
  push:
    branches:
      - master

Solution 2:

Here is what I've done for steps that should only run on a specific branch.

- name: Publish Image
  # Develop branch only
  if: github.ref == 'refs/heads/develop'
  run: |
    ... publish commands ...

Solution 3:

Most answers provide a solution for one single branch. To restrict the job to run on any specific set of branches, you can do it using the if conditional with multiple disjunction (||) operators; but this is too verbose and doesn't respect the DRY principle.

The same can be archived with less repetition using the contains function.

Using contains:

contains('
  refs/heads/dev
  refs/heads/staging
  refs/heads/production
', github.ref)

compared to using multiple ||:

github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/staging' || github.ref == 'refs/heads/production' || …

Full example:

---
on: push
jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Run tests
      run: …

  deployment:
    name: Deployment
    runs-on: ubuntu-latest
    needs: [test]
    if:
      contains('
        refs/heads/dev
        refs/heads/staging
        refs/heads/production
      ', github.ref)
    steps:
    - uses: actions/checkout@v2
    - name: Deploy
      run: …