← Course Index

Advanced Actions Patterns

~25 min · CI/CD

Ref
Primary Source
GitHub Actions Docs — Reusing Workflows

Official guide to reusable workflows, composite actions, and environment protection rules. Read →

Reusable Workflows

# .github/workflows/reusable-test.yml
name: Reusable Tests
on:
  workflow_call:
    inputs:
      node-version: { required: true, type: string }
    secrets:
      DATABASE_URL: { required: true }

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: INPUT_NODE }
      - run: npm ci && npm test
        env:
          DATABASE_URL: SECRET_DATABASE_URL

# ── Call it from another workflow ─────────────────────────────────
jobs:
  run-tests:
    uses: ./.github/workflows/reusable-test.yml
    with:
      node-version: '20'
    secrets:
      DATABASE_URL: SECRETS_DATABASE_URL

Environment Protection Rules

# GitHub: Settings → Environments → production
# Configure:
#   Required reviewers: team leads (require approval before deploy)
#   Wait timer: 5 minutes (cooling period)
#   Deployment branches: main only

jobs:
  deploy:
    needs: test
    environment: production  # Triggers protection rules
    steps:
      - run: echo "Human approved — deploying"
💡

Always gate production with at least one required reviewer. This prevents accidental pushes to main from immediately deploying to production.

Manual Triggers (workflow_dispatch)

on:
  push:
    branches: [main]
  workflow_dispatch:         # Adds "Run workflow" button in GitHub UI
    inputs:
      environment:
        description: 'Target environment'
        type: choice
        options: [staging, production]
        default: staging
      skip-tests:
        description: 'Emergency deploy (skip tests)'
        type: boolean
        default: false

jobs:
  deploy:
    steps:
      - run: ./deploy.sh INPUT_ENV

Concurrency Control

# Cancel in-progress deploys when a newer commit is pushed
concurrency:
  group: deploy-$GITHUB_REF
  cancel-in-progress: true

# This prevents:
# Deploy 1 (commit A) starts
# Deploy 2 (commit B) starts
# Deploy 1 finishes AFTER Deploy 2 — rolling back to older version!

Check Your Understanding

1. What does environment: production in a job do?
2. Without concurrency control, what race condition can happen with rapid pushes to main?
3. What does workflow_dispatch enable?