17

I have a (simplified) GitHub workflow that looks like this:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: echo "build"

  test:
    runs-on: ubuntu-latest
    needs: build

    steps:
      - run: echo "build"

  deploy:
    runs-on: ubuntu-latest
    needs: [build, test]

    steps:
      - run: echo "deploy"

Now, I'd like to make it so that the deploy step only needs the tests step if the branch is main.

Is that possible?

Essentially I'd love to do something like this:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: echo "build"

  test:
    runs-on: ubuntu-latest
    needs: build

    steps:
      - run: echo "build"

  deploy:
    runs-on: ubuntu-latest
    needs: contains(branch, "master") ? [build, test] : [build] #notice this line

    steps:
      - run: echo "deploy"
Mathias Lykkegaard Lorenzen
  • 15,031
  • 23
  • 100
  • 187
  • Would e.g. `${{ (contains(branch, "master") && [build, test]) || [build] }}` work? There are some options for conditional expressions at https://github.community/t/do-expressions-support-ternary-operators-to-change-their-returned-value/18114 – jonrsharpe Jan 22 '21 at 11:18

3 Answers3

15

GitHub action isn't allowing conditional needs (unfortunately, imho).

Yet, there is a workaround: Make your jobs run sequentially by defining the prerequisites in needs. All jobs required to run no matter what happened to the prerequisites need get the if condition set to always().

If a failure of a previous job is supposed to kill you need to add a step for verifying the job result(s).

This is an general solution for adaption:

jobs:
  init_job:
    runs-on: ubuntu-latest
    steps:
      - run: echo "initial run"

  conditional_job:
    runs-on: ubuntu-latest
    needs: init_job
    if: ${{ your condition here }}
    steps:
      - run: echo "conditonal run"

  final_job:
    needs: conditional_job
    if: always()
    steps:
      - name: fail if conditional job failed
        if: ${{ needs.conditional_job.result == 'failure' }}
        run: exit 1
      - run: echo "final run"

Janluak
  • 195
  • 2
  • 10
  • 2
    To aggregate multiple conditional jobs in `needs`, the step condition could be `if: ${{ contains(needs.*.result, 'failure') }}` – Ivan Klass Oct 13 '21 at 17:17
  • 1
    This solution works, but be mindful about it. `always()` jobs cannot be cancelled, so if they get stuck, that's it. Use it together with a timeout (bug discussion here: https://github.com/orgs/community/discussions/26303). –  Feb 03 '23 at 17:51
3

Not ideal but you can use this:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: echo "build"

  test:
    runs-on: ubuntu-latest
    needs: build

    steps:
      - run: echo "build"

  deploy-main:
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    needs: [build, test]

    steps:
      - run: echo "deploy"
        
  deploy:
    runs-on: ubuntu-latest
    if: github.ref != 'refs/heads/main'
    needs: build

    steps:
      - run: echo "deploy"

Krzysztof Madej
  • 32,704
  • 10
  • 78
  • 107
  • The problem is that the deploy steps are really long and complex, so that's unfortunately not useful for me at the moment. – Mathias Lykkegaard Lorenzen Jan 22 '21 at 12:00
  • You can consider [composite actions](https://docs.github.com/en/actions/creating-actions/creating-a-composite-run-steps-action) to avoid repeating yourself – Krzysztof Madej Jan 22 '21 at 12:08
  • And please also check this [topic](https://stackoverflow.com/questions/59809764/how-to-reference-context-values-in-github-actions-expression-syntax) I'm afraid that you won't be able to use `github` or `env` context in `needs` scope. When I use that I got `Unrecognized named-value: 'env'` – Krzysztof Madej Jan 22 '21 at 12:11
  • Hmmm, with composite actions, would I be able to have them locally? Or do they need to be hosted in a separate repo? – Mathias Lykkegaard Lorenzen Jan 22 '21 at 13:41
  • You can have them in the same repo. Please check it [here](https://github.com/kmadof/github-actions-manual/blob/main/.github/workflows/so-010-composite.yaml) – Krzysztof Madej Jan 22 '21 at 15:11
  • But they (composite actions) seem to be still limited - please check this [topic](https://github.com/actions/runner/issues/646) – Krzysztof Madej Jan 22 '21 at 15:18
1

As replied above there is no way to use conditions in needs but you can split your workflow into two:

deploy-master.yml:

on:
  push:
    branches:
      - master

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: echo "build"

  test:
    runs-on: ubuntu-latest
    needs: build

    steps:
      - run: echo "build"

  deploy:
    runs-on: ubuntu-latest
    needs: [build, test]

    steps:
      - run: echo "deploy"

deploy-pr.yml:

on:
  push:
    branches-ignore:
      - master
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: echo "build"

  deploy:
    runs-on: ubuntu-latest
    needs: [build]

    steps:
      - run: echo "deploy"

Also because you mentioned that your deploy job is "really long and complex" You can define it in a separate file and refer to it as to a reusable workflow

CAMOBAP
  • 5,523
  • 8
  • 58
  • 93