This article explores how to use expressions in GitHub Actions to control the execution flow of steps and jobs. We cover:
Conditional execution with if
Ignoring failures using continue-on-error
Built-in status check functions
Sample Workflow
The following workflow runs tests on both Ubuntu and Windows, uploads a report to AWS S3 (failing intentionally), and then deploys if the report step completes.
on : push
jobs :
testing :
strategy :
matrix :
os : [ 'windows-latest' , 'ubuntu-latest' ]
runs-on : ${{ matrix.os }}
steps :
- name : Testing on Ubuntu
run : |
export apikey=3$CuR3-t0k3N
echo "Running Tests on Ubuntu..."
- name : Testing on Windows
run : |
Set-Variable -Name "apikey" -Value "3$CuR3-t0k3N"
echo "Running Tests on Windows..."
reports :
needs : testing
runs-on : ubuntu-latest
steps :
- name : Upload Report to AWS S3
run : |
echo "Uploading reports..." && exit 1
deploy :
needs : reports
runs-on : ubuntu-latest
steps :
- name : Deploy Application
run : echo "Deploying application..."
In this example:
Ubuntu tests pass.
Windows commands fail on Linux, causing the testing job to abort.
As a result, reports and deploy are skipped.
Conditional Execution with if
Use if to run steps or jobs only when a condition is met. You can reference contexts like runner.os, literals, and functions:
jobs :
testing :
strategy :
matrix :
os : [ 'windows-latest' , 'ubuntu-latest' ]
runs-on : ${{ matrix.os }}
steps :
- name : Testing on Ubuntu
if : runner.os == 'Linux'
run : |
export apikey=3$CuR3-t0k3N
echo "Running Tests on Ubuntu..."
- name : Testing on Windows
if : runner.os == 'Windows'
run : |
Set-Variable -Name "apikey" -Value "3$CuR3-t0k3N"
echo "Running Tests on Windows..."
Each step only executes on its intended OS, preventing unsupported commands from running.
Ignoring Failures with continue-on-error
By default, a failed step aborts its job. To let a job succeed even if a step fails, set continue-on-error: true. Downstream jobs defined with needs will still run if the parent job completes.
The continue-on-error attribute applies at the step level, not at the job level.
on : push
jobs :
testing :
strategy :
matrix :
os : [ 'windows-latest' , 'ubuntu-latest' ]
runs-on : ${{ matrix.os }}
steps :
- name : Testing on Linux
if : runner.os == 'Linux'
run : |
export apikey=3$CuR3-t0k3N
echo "Running Tests on Ubuntu..."
- name : Testing on Windows
if : runner.os == 'Windows'
run : |
Set-Variable -Name "apikey" -Value "3$CuR3-t0k3N"
echo "Running Tests on Windows..."
reports :
needs : testing
runs-on : ubuntu-latest
steps :
- name : Upload Report to AWS S3
run : |
echo "Uploading reports..." && exit 1
continue-on-error : true
deploy :
needs : reports
runs-on : ubuntu-latest
steps :
- name : Deploy Application
run : echo "Deploying application..."
Here, even though the upload step fails, reports completes successfully and triggers the deploy job.
Status Check Functions
GitHub Actions provides built-in functions to inspect previous outcomes:
Function Returns true when… success()all prior steps and jobs have succeeded failure()any prior step or job has failed cancelled()the workflow run was cancelled always()always (useful for cleanup steps)
Example:
jobs :
build-and-test :
runs-on : ubuntu-latest
steps :
- name : Build
run : npm run build
- name : Test
run : npm test
if : success()
- name : Notify on Failure
run : echo "Tests failed!"
if : failure()
- name : Cleanup
run : echo "Cleaning up environment..."
if : always()
These functions help you create resilient, conditional workflows that adapt to your CI/CD pipeline’s status.
Links and References