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.
There are 2 issues in play:
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 }}
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.
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