GitHub Actions Tutorial — Advanced Patterns
Advanced Patterns
These patterns go beyond standard CI/CD — they handle real-world complexity like monorepos, dynamic pipelines, and cross-repository coordination.
Monorepo — Path-Based Triggers
Only build what changed:
name: Monorepo CI
on:
push:
branches: [main]
pull_request:
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
api: ${{ steps.filter.outputs.api }}
web: ${{ steps.filter.outputs.web }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
api:
- 'packages/api/**'
web:
- 'packages/web/**'
build-api:
needs: detect-changes
if: needs.detect-changes.outputs.api == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: cd packages/api && npm ci && npm test
build-web:
needs: detect-changes
if: needs.detect-changes.outputs.web == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: cd packages/web && npm ci && npm test
Only build-api runs if only packages/api/ changed. Only build-web runs if only packages/web/ changed.
Dynamic Matrix from JSON
Generate matrix values at runtime:
jobs:
setup:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set.outputs.matrix }}
steps:
- uses: actions/checkout@v4
- name: Generate matrix
id: set
run: |
# Read from a config file or compute dynamically
MATRIX=$(cat matrix.json)
echo "matrix=$MATRIX" >> "$GITHUB_OUTPUT"
build:
needs: setup
strategy:
matrix: ${{ fromJSON(needs.setup.outputs.matrix) }}
runs-on: ubuntu-latest
steps:
- run: echo "Building ${{ matrix.project }} with ${{ matrix.node }}"
matrix.json:
{
"include": [
{ "project": "api", "node": "20" },
{ "project": "web", "node": "18" },
{ "project": "cli", "node": "20" }
]
}
Cross-Repository Dispatch
Trigger a workflow in another repository:
jobs:
notify:
runs-on: ubuntu-latest
steps:
- name: Trigger deploy repo
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.PAT_TOKEN }}
repository: my-org/deploy-service
event-type: app-updated
client-payload: '{"version": "${{ github.sha }}"}'
The target repository listens for the event:
on:
repository_dispatch:
types: [app-updated]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- run: echo "Deploying version ${{ github.event.client_payload.version }}"
Concurrency Control
Prevent duplicate runs:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
- Pushes to the same branch cancel previous runs
- Only the latest commit gets a full run
- Saves runner minutes on frequent pushes
Conditional Deployment
Deploy only when specific conditions are met:
jobs:
deploy:
if: |
github.ref == 'refs/heads/main' &&
github.event_name == 'push' &&
!contains(github.event.head_commit.message, '[skip deploy]')
runs-on: ubuntu-latest
steps:
- run: echo "Deploying..."
Quick Reference
| Pattern | Use Case |
|---|---|
| Path filters | Monorepo — build only changed packages |
| Dynamic matrix | Runtime-determined build configurations |
| Repository dispatch | Cross-repo coordination |
| Concurrency | Cancel duplicate runs |
| Conditional deploy | Skip deploy on certain commits |
← Back to: Chapter 16: Next Steps
Next: GitHub Apps →