GitHub-actions OR operator for needs clause

Viewed 3926

Someone knows if it is possible to use OR operator in needs statement? let's say we expect a successful job-a OR job-b to run job-c, i.e.

name: My workflow
on:
  workflow_dispatch:

jobs:
  job-a:
    runs-on: ubuntu-latest
    steps:
      - name: do something
        run: echo "something"
  job-b:
    runs-on: ubuntu-latest
    steps:
      - name: do something
        run: echo "something"
  job-c:
    runs-on: ubuntu-latest
    needs:
      - job-a OR job-b
    steps:
      - name: do something
        run: echo "something"

4 Answers

Yes it's possible. Unfortunately I have some cases where it doesn't always works as expected. But the idea is to do something as follow:

jobs:
  job-a:
    name: Job A
    runs-on: ubuntu-latest
    steps:
      - run: true

  job-b:
    name: Job B
    runs-on: ubuntu-latest
    if: false
    steps:
      - run: true

  job-ab:
    name: Job AB
    runs-on: ubuntu-latest
    needs: [ job-a, job-b ]
    if: ${{ always() && contains(needs.*.result, 'success') && !(contains(needs.*.result, 'failure')) }}
    steps:
      - run: true

By default when using needs, any job in the dependency list that is skipped will trigger a skip of the current job. If you want to force the job to run you have to use the always() function which will tell your job to always run. Unfortunately this means that it will run event if a job fails or if all the previous jobs are skipped. contains(needs.*.result, 'success') will be true if at least one of the jobs succeeded. So if all your jobs are skipped it will be false. !(contains(needs.*.result, 'failure')) is false if any job failed

I am a more detailed demo here.

You can combine jobs.<job_id>.outputs, jobs.<job_id>.steps[*].if, and jobs.<job_id>.needs:

jobs:
  job1:
    runs-on: ubuntu-latest
    outputs:
      output1: ${{ steps.step1.outputs.test }}
    steps:
      - id: step0
        run: echo "something"

      - id: step1
        if: success()
        run: echo "::set-output name=test::success"

  job2:
    runs-on: ubuntu-latest
    outputs:
      output2: ${{ steps.step1.outputs.test }}
    steps:
      - id: step0
        run: echo "something"

      - id: step1
        if: success()
        run: echo "::set-output name=test::success"

  job3:
    runs-on: ubuntu-latest
    needs: [job1, job2]
    if: needs.job1.outputs.output1 == 'success' && needs.job2.outputs.output2 == 'success'
    steps:
      - run: echo ${{needs.job1.outputs.output1}} ${{needs.job2.outputs.output2}}

Basically, on each job, your last step will set an output only if the other steps succeeded.

And then you can access those outputs through any job and do anything you want.

It's not possible to have a true OR condition in needs, but there are plenty of ways to work around this limitation. My workaround was to make each job run, but to check whether each step should run.

on:
  pull_request:
    branches:
      - "main"
  workflow_dispatch:

jobs:
  job-a:
    runs-on: ubuntu-latest
    steps:
      - if: ${{ github.event_name == 'pull_request' }}
        run: echo "Triggered by a pull request"

  job-b:
    runs-on: ubuntu-latest
    steps:
      - if: ${{ github.event_name == 'workflow_dispatch' }}
        run: echo "Triggered by workflow dispatch"

  job-c:
    needs: [job-a, job-b]
    runs-on: ubuntu-latest
    steps:
      - run: echo "Do some stuff"

  job-d:
    needs: [job-c]
    runs-on: ubuntu-latest
    steps:
      - run: echo "This runs too"

your job will always run if you use "if: ${{ always()" regardless on "needs"

Related