'Sequencing GitHub actions job on conditions

I have: Job A, Job B & Job C.

when Job A completes If job B runs I need job C to run (after job B has completed with success)

if Job B skipped I need Job C to run (If job A has completed with success)

See below for code snip:

    check_if_containers_exist_to_pass_to_last_known_tagger_job: (JobA)
    name: check_if_containers_exist
    environment: test
    runs-on: ubuntu-latest
    #needs: [push_web_to_ecr, push_cron_###_to_ecr, push_to_###_shared_ecr, push_to_###_ecr]
    needs: push_###_to_shared_ecr
    #if: ${{ github.ref == 'refs/heads/main' }}
    outputs:
      signal_job: ${{ steps.step_id.outputs.run_container_tagger_job }}

    steps:

    - name: Configure AWS credentials
      id: config-aws-creds
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.SHARED_AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.SHARED_AWS_SECRET_ACCESS_KEY }}
        aws-region: eu-west-2

    - name: Check if container exists (If containers don't exist then don't run last known tagging job for rollback)
      id: step_id
      run: |
        aws ecr describe-images --repository-name anonymizer --image-ids imageTag=testing-latest
        if [ $? == 254 ]; then echo "::set-output name=run_container_tagger_job::false"; else echo "::set-output name=run_container_tagger_job::true"; fi

  tag_latest_testing_containers_as_last_known_testing_containers: (Job B)
    needs: check_if_containers_exist_to_pass_to_last_known_tagger_job
    if: needs.check_if_containers_exist_to_pass_to_last_known_tagger_job.outputs.signal_job == 'true'
    uses: ###/###/.github/workflows/container-tagger.yml@###
    with:
      tag_to_identify_containers: testing-latest
      new_tag_to_apply_to_containers: last-known-testing
      aws-region: eu-west-2
      run_cron_and_cycle_containers: false
    secrets:
      SHARED_AWS_ACCESS_KEY_ID: ${{ secrets.SHARED_AWS_ACCESS_KEY_ID }}
      SHARED_AWS_SECRET_ACCESS_KEY: ${{ secrets.SHARED_AWS_SECRET_ACCESS_KEY }}

  tag_testing_containers_to_testing_latest: (Job C)
    needs: [check_if_containers_exist_to_pass_to_last_known_tagger_job,tag_latest_testing_containers_as_last_known_testing_containers]
    if: ${{ always() }}
    uses: ###/##/.github/workflows/container-tagger.yml@###
    with:
      tag_to_identify_containers: dev-${{ github.sha }}
      new_tag_to_apply_to_containers: james-cron-test
      aws-region: eu-west-2
      run_cron_and_cycle_containers: true
    secrets:
      SHARED_AWS_ACCESS_KEY_ID: ${{ secrets.SHARED_AWS_ACCESS_KEY_ID }}
      SHARED_AWS_SECRET_ACCESS_KEY: ${{ secrets.SHARED_AWS_SECRET_ACCESS_KEY }}
      ENVIRONMENT_AWS_ACCESS_KEY_ID: ${{ secrets.TESTING_AWS_ACCESS_KEY_ID }}
      ENVIRONMENT_AWS_SECRET_ACCESS_KEY: ${{ secrets.TESTING_AWS_SECRET_ACCESS_KEY }}


Solution 1:[1]

It might not be the most elegant solution, but it works.

The workaround would consist of adding 2 extra steps at the end of the Job A, and set the 2 steps always execute (if: always()).

  • The first one is used to create a text file and write the job status into it.
  • The second one is used to upload this text file as an artifact.

Then, in Job B and Job C, you will need to add the steps to download the artifacts and read the status of Job A to then perform or not specific operations.

Here is a demo of how it might look:

jobs:
  JOB_A:
    name: Job A
    ...
    steps:
      - name: Some steps of job A
      ...
      
      - name: Create file status_jobA.txt and write the job status into it
        if: always()
        run: |
          echo ${{ job.status }} > status_jobA.txt

      - name: Upload file status_jobA.txt as an artifact
        if: always()
        uses: actions/upload-artifact@v1
        with:
          name: pass_status_jobA
          path: status_jobA.txt

JOB_B:
    needs: [JOB_A]
    if: always()
    name: Job B
    ...
    steps:
      - name: Download artifact pass_status_jobA
        uses: actions/download-artifact@v1
        with:
          name: pass_status_jobA

      - name: Set the status of Job A as output parameter
        id: set_outputs
        run: echo "::set-output name=status_jobA::$(<pass_status_jobA/status_jobA.txt)"

      - name: Check Job A status
        if: ${{ steps.set_outputs.outputs.status_jobA }} == "success"
        run: |
         ...

JOB_C:
    needs: [JOB_A]
    if: always()
    name: Job C
    ...
    steps:
      - name: Download artifact pass_status_jobA
        uses: actions/download-artifact@v1
        with:
          name: pass_status_jobA

      - name: Set the status of Job A as output parameter
        id: set_outputs
        run: echo "::set-output name=status_jobA::$(<pass_status_jobA/status_jobA.txt)"

      - name: Check Job A status
        if: ${{ steps.set_outputs.outputs.status_jobA }} == "failure"
        run: |
         ...

Note that here, all jobs will always run, but Job B steps after the check will only run for Job A success, and Job C steps after the check will only run for Job A failure.

  • Job A --> Success --> Job B + Job C checks --> Job B steps
  • Job A --> Failure --> Job B + Job C checks --> Job C steps

Reference

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 GuiFalourd