GitHub Actions Tutorial — Status Functions
Status Functions
Status functions let you react to the outcome of previous steps or jobs. They’re used in if: conditions to control cleanup, notifications, and error handling.
The Four Status Functions
| Function | Returns true When |
|---|---|
success() |
All previous steps succeeded (default) |
failure() |
Any previous step failed |
always() |
Always (regardless of success/failure/cancellation) |
cancelled() |
The workflow was cancelled |
Default Behavior
Without an explicit if:, every step and job has an implicit if: success():
steps:
- run: echo "Step 1" # if: success() (implicit)
- run: exit 1 # this fails
- run: echo "Step 3 — never runs" # skipped because step 2 failed
always() — Always Execute
steps:
- run: npm test
- run: exit 1 # fails
- name: Cleanup (always runs)
if: always()
run: |
echo "Cleaning up resources..."
docker-compose down
Common uses:
- Cleanup resources (databases, containers)
- Upload test results or logs
- Send notifications
failure() — Run on Failure
steps:
- name: Run tests
run: npm test
- name: Upload failure logs
if: failure()
uses: actions/upload-artifact@v4
with:
name: test-logs
path: ./logs/
- name: Notify on failure
if: failure()
run: |
curl -X POST "${{ secrets.SLACK_WEBHOOK }}" \
-d '{"text": "❌ Tests failed in ${{ github.repository }}"}'
cancelled() — Run on Cancellation
steps:
- name: Long running test
run: sleep 3600
- name: Handle cancellation
if: cancelled()
run: echo "Workflow was cancelled — cleaning up"
Combining Status Functions
steps:
- name: Run tests
id: tests
run: npm test
- name: Upload results (success or failure)
if: success() || failure()
uses: actions/upload-artifact@v4
with:
name: test-results
path: ./test-results/
- name: Notify only on failure
if: failure() && github.ref == 'refs/heads/main'
run: echo "Main branch is broken!"
success() || failure() vs always()
| Expression | Runs on success | Runs on failure | Runs on cancellation |
|---|---|---|---|
success() |
✅ | ❌ | ❌ |
failure() |
❌ | ✅ | ❌ |
always() |
✅ | ✅ | ✅ |
success() \|\| failure() |
✅ | ✅ | ❌ |
cancelled() |
❌ | ❌ | ✅ |
Use success() || failure() when you want to run on both outcomes but NOT on cancellation.
Job-Level Status Functions
Status functions also work at the job level with needs::
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: npm test
deploy:
needs: test
runs-on: ubuntu-latest
# Default: only runs if 'test' succeeded
steps:
- run: echo "Deploying..."
notify:
needs: test
if: failure() # runs only if 'test' failed
runs-on: ubuntu-latest
steps:
- run: echo "Tests failed!"
cleanup:
needs: [test, deploy]
if: always() # runs regardless
runs-on: ubuntu-latest
steps:
- run: echo "Cleaning up..."
Real-World Pattern — CI with Notifications
name: CI Pipeline
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test
- name: Upload test report
if: always()
uses: actions/upload-artifact@v4
with:
name: test-report
path: ./reports/
- name: Slack notification on failure
if: failure()
run: |
curl -X POST "${{ secrets.SLACK_WEBHOOK }}" \
-H "Content-Type: application/json" \
-d '{"text": "❌ CI failed on main: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}'
Common Mistakes
| Mistake | Fix |
|---|---|
| Cleanup step skipped after failure | Add if: always() to cleanup steps |
| Notification runs on cancellation | Use if: failure() instead of if: always() |
failure() doesn’t trigger at job level |
Add if: failure() AND the required needs: dependency |
Quick Reference
| Function | When It’s True | Common Use |
|---|---|---|
success() |
All previous OK | Default behavior |
failure() |
Something failed | Notifications, log upload |
always() |
Always | Cleanup, resource teardown |
cancelled() |
Run was cancelled | Cancel-specific cleanup |
← Previous: Conditionals
Next: String Functions →