GitHub Actions Tutorial — Workflow Run Chain
Workflow Run Chain
The workflow_run trigger lets one workflow trigger another workflow after it completes. This is useful for separating CI from CD, running post-deployment tests, or building follow-up automations.
How workflow_run Works
Workflow A (CI) Workflow B (Deploy)
┌──────────────┐ ┌──────────────────┐
│ on: push │ │ on: workflow_run │
│ │ completes │ workflows: [CI] │
│ Build & Test │ ─────────► │ │
│ │ │ Deploy to staging │
└──────────────┘ └──────────────────┘
Workflow B triggers after Workflow A completes (success, failure, or any conclusion).
Basic Chain
Workflow A — CI (ci.yml):
name: CI
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm test
Workflow B — Deploy (deploy.yml):
name: Deploy
on:
workflow_run:
workflows: ["CI"]
types: [completed]
branches: [main]
jobs:
deploy:
if: github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "Deploying — CI passed!"
Key Points
The if Check is Critical
workflow_run triggers on any conclusion — success, failure, or cancelled. You almost always want to check the conclusion:
jobs:
deploy:
if: github.event.workflow_run.conclusion == 'success'
Without this check, your deploy workflow would run even when CI fails.
Available Conclusions
| Conclusion | Meaning |
|---|---|
success |
All jobs passed |
failure |
At least one job failed |
cancelled |
Workflow was cancelled |
skipped |
Workflow was skipped |
Branch Filter
The branches: filter matches the branch of the triggering workflow, not the workflow_run workflow itself:
on:
workflow_run:
workflows: ["CI"]
types: [completed]
branches: [main] # only triggers when CI ran on main
Accessing Artifacts from the Triggering Workflow
A common pattern is to download artifacts from Workflow A in Workflow B:
name: Deploy
on:
workflow_run:
workflows: ["Build"]
types: [completed]
jobs:
deploy:
if: github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
steps:
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: build-output
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Deploy
run: ls -la && echo "Deploying downloaded artifact..."
workflow_run Context Variables
| Variable | Value |
|---|---|
${{ github.event.workflow_run.name }} |
Name of the triggering workflow |
${{ github.event.workflow_run.conclusion }} |
success, failure, cancelled
|
${{ github.event.workflow_run.head_branch }} |
Branch of the triggering run |
${{ github.event.workflow_run.id }} |
Run ID (for artifact downloads) |
Chaining Multiple Workflows
You can create a chain of three or more workflows:
CI (push) → Build → Deploy → Smoke Test
Each workflow uses workflow_run to trigger the next. Keep the chain short — deeply nested chains are hard to debug.
Common Mistakes
| Mistake | Fix |
|---|---|
| Deploy runs when CI fails | Add if: github.event.workflow_run.conclusion == 'success'
|
| workflow_run doesn’t trigger | The workflows: value must match the name: of the triggering workflow exactly (case-sensitive) |
| Can’t download artifacts | Use run-id parameter with ${{ github.event.workflow_run.id }}
|
| Wrong branch triggers | Add branches: filter to workflow_run
|
Quick Reference
| Concept | Syntax |
|---|---|
| Trigger after workflow | on: workflow_run: workflows: ["Name"] |
| On success only | if: github.event.workflow_run.conclusion == 'success' |
| Filter branch | branches: [main] |
| Get run ID | ${{ github.event.workflow_run.id }} |
| Conclusion types |
completed (includes success/failure/cancelled) |
← Previous: Release Events
Next: Practice Exercises →