GitHub Actions Tutorial — Workflow Run Chain

2 minute read

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 →

Updated: