Reusable Workflows and Composite Actions
Eliminate duplication by creating reusable workflow components.
Reusable Workflow
Create .github/workflows/reusable-deploy.yml:
name: Reusable Deploy
on:
workflow_call:
inputs:
environment:
required: true
type: string
node-version:
required: false
type: string
default: '18'
secrets:
deploy-token:
required: true
outputs:
deployment-url:
description: "Deployed URL"
value: ${{ jobs.deploy.outputs.url }}
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
outputs:
url: ${{ steps.deploy.outputs.url }}
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
- name: Deploy
id: deploy
run: |
echo "Deploying to ${{ inputs.environment }}"
URL=$(./deploy.sh)
echo "url=$URL" >> $GITHUB_OUTPUT
env:
DEPLOY_TOKEN: ${{ secrets.deploy-token }}
Using Reusable Workflow
name: Deploy All Environments
on:
push:
branches: [main]
jobs:
deploy-staging:
uses: ./.github/workflows/reusable-deploy.yml
with:
environment: staging
node-version: '18'
secrets:
deploy-token: ${{ secrets.STAGING_TOKEN }}
deploy-production:
needs: deploy-staging
uses: ./.github/workflows/reusable-deploy.yml
with:
environment: production
node-version: '20'
secrets:
deploy-token: ${{ secrets.PRODUCTION_TOKEN }}
Composite Actions
Create .github/actions/setup-project/action.yml:
name: 'Setup Project'
description: 'Setup Node.js and install dependencies'
inputs:
node-version:
description: 'Node.js version'
required: false
default: '18'
cache:
description: 'Cache package manager'
required: false
default: 'npm'
outputs:
cache-hit:
description: 'Whether cache was hit'
value: ${{ steps.cache.outputs.cache-hit }}
runs:
using: 'composite'
steps:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: ${{ inputs.cache }}
- name: Cache dependencies
id: cache
uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
- name: Install dependencies
if: steps.cache.outputs.cache-hit != 'true'
shell: bash
run: npm ci
Using Composite Action
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup project
uses: ./.github/actions/setup-project
with:
node-version: '18'
cache: 'npm'
- name: Run tests
run: npm test
Matrix with Reusable Workflows
name: Test Matrix
on: [push]
jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node: [16, 18, 20]
uses: ./.github/workflows/reusable-test.yml
with:
os: ${{ matrix.os }}
node-version: ${{ matrix.node }}
Docker Composite Action
Create .github/actions/docker-build/action.yml:
name: 'Docker Build and Push'
description: 'Build and push Docker image'
inputs:
registry:
required: true
image-name:
required: true
tag:
required: false
default: 'latest'
runs:
using: 'composite'
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to registry
uses: docker/login-action@v3
with:
registry: ${{ inputs.registry }}
username: ${{ github.actor }}
password: ${{ github.token }}
- name: Build and push
uses: docker/build-push-action@v5
with:
push: true
tags: ${{ inputs.registry }}/${{ inputs.image-name }}:${{ inputs.tag }}
cache-from: type=gha
cache-to: type=gha,mode=max
Call External Reusable Workflows
jobs:
deploy:
uses: my-org/workflows/.github/workflows/deploy.yml@main
with:
environment: production
secrets: inherit
Conditional Reusable Workflows
jobs:
deploy-preview:
if: github.event_name == 'pull_request'
uses: ./.github/workflows/deploy-preview.yml
with:
pr-number: ${{ github.event.number }}
deploy-production:
if: github.ref == 'refs/heads/main'
uses: ./.github/workflows/deploy-prod.yml
secrets: inherit
Best Practices
✅ Centralize common logic - Reusable workflows
✅ Parameterize inputs - Make workflows flexible
✅ Document actions - Clear descriptions
✅ Version reusable workflows - Use tags or branches
✅ Test composite actions - Validate locally
✅ Share across repos - Organization-level workflows
Reusable vs Composite
Reusable Workflows:
- Entire workflow files
- Can have multiple jobs
- Called with
uses:in jobs - Good for complete processes
Composite Actions:
- Groups of steps
- Single job context
- Called with
uses:in steps - Good for repeated step sequences
Key Takeaways
Reusable workflows and composite actions eliminate duplication, ensure consistency, and make maintenance easier. Use reusable workflows for complete processes and composite actions for step sequences.