Search code examples
bashyamlgithub-actionsexit-code

Why is my Github Actions job being skipped?


Been trying to figure this out for a couple of days now. I have these jobs in a GitHub Actions workflow:

jobs:
    
  check-for-changes:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v2
        
      - name: Install Black
        run: python -m pip install black
        
      - name: Check Black Formatting
        id: check
        continue-on-error: true
        run: |
          black black_test.py --check
          exit_code=$?
          echo "exit_code=$exit_code" >> $GITHUB_OUTPUT

  black-formatting:
    runs-on: ubuntu-latest
    needs: [check-for-changes]
    if: ${{ needs.check-for-changes.outputs.exit_code == 1 }}
    steps:
      - name: Checkout Code
        uses: actions/checkout@v2
        
      - name: Install Black
        run: python -m pip install black
        
      - name: Apply Black Formatting
        run: black black_test.py

The logic that I'm trying to build is for the check-for-changes job to always run first. Then, it checks whether or not a Python file needs to be formatted or not with Black. I'm capturing the exit code and saving it using an output.

The next job, black-formatting should run ONLY if there is an exit code of 1 from the check-for-changes job.

I just ran it with the provided yml file with a file that needs to be reformatted (I checked the output of the check-for-changes job and there was a message stating that a file would need to be formatted and an exit code of 1) and the job was skipped? If I switch the if: ${{ needs.check-for-changes.outputs.exit_code == 1 }} line to == 0, then it continues but that shouldn't be happening. I'm not sure if I am capturing the exit_code properly or if there is a better way to do this.


Solution

  • There are 2 issues in play:

    1. You need to declare the output variable at the step as well as at the job level
    2. Bash will stop processing when a command returns an error.

    Declare the output variable at the job level

    Your 2nd job can't access the step output without you also declaring it as a job output:

    jobs:
      check-for-changes:
        # Map a step output to a job output
        outputs:
          exit_code: ${{ steps.check.outputs.exit_code}}
    
        steps:
          - name: Check Black Formatting
            id: check
            run: |
              echo "exit_code=1" >> $GITHUB_OUTPUT
    

    Then reference that in the 2nd job:

    jobs: 
      black-formatting:
        runs-on: ubuntu-latest
        needs: [check-for-changes]
        if: ${{ needs.check-for-changes.outputs.exit_code == 1 }}
    

    Bash stops processing when a command returns a non-0 exit code.

    Your script black black_test.py --check returns a non-0 exit code, bash will stop processing the rest of the script. The code to set the exit_code variable and the code to set the output variable are skipped, so the value of the output variable will either be 0 or .

    You can capture the exit code and continue processing, you can add || exit_code=$? after the failing command:

          - name: Check Black Formatting
            id: check
            continue-on-error: true
            run: |
              black black_test.py --check || exit_code=$?
              echo "exit_code=$exit_code" >> $GITHUB_OUTPUT
    

    The continue-on-error: true doesn't let bash keep processing, it just doesn't cause the job to fail if anything fails in the script, even if the script is terminated half-way.

    Final script:

    jobs:
        
      check-for-changes:
        outputs:
          exit_code: ${{ steps.check.outputs.exit_code }}
    
        runs-on: ubuntu-latest
        steps:
          - name: Checkout Code
            uses: actions/checkout@v2
            
          - name: Install Black
            run: python -m pip install black
            
          - name: Check Black Formatting
            id: check
            run: |
              black black_test.py --check || exit_code=$?
              echo "exit_code=$exit_code" >> $GITHUB_OUTPUT
    
      black-formatting:
        runs-on: ubuntu-latest
        needs: [check-for-changes]
        if: ${{ needs.check-for-changes.outputs.exit_code == 1 }}
        steps:
          - name: Checkout Code
            uses: actions/checkout@v2
            
          - name: Install Black
            run: python -m pip install black
            
          - name: Apply Black Formatting
            run: black black_test.py