GitHub Actions automates software workflows including CI/CD. Workflows are defined using YAML files in .github/workflows/.
Basic Workflow Structure (.github/workflows/main.yml)
name: CI/CD Pipeline
# When does the workflow run?
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
workflow_dispatch: # Allows manual triggering from the GitHub UI
# What does the workflow do?
jobs:
build:
runs-on: ubuntu-latest # The runner environment
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm testCommon Triggers (on)
on:
push:
branches: [ "main", "develop" ]
tags:
- 'v*.*.*' # Trigger on version tags (e.g., v1.0.0)
paths:
- 'src/**' # Only trigger when files in src/ change
pull_request:
types: [ opened, synchronize ]
schedule:
- cron: '0 0 * * *' # Run daily at midnight UTC
workflow_dispatch: # Manual trigger with optional inputs
inputs:
environment:
description: 'Deploy environment'
required: true
default: 'staging'Environment Variables and Secrets
jobs:
deploy:
runs-on: ubuntu-latest
env:
NODE_ENV: production # Available to all steps in this job
steps:
- name: Deploy to Server
run: ./deploy.sh
env:
API_KEY: ${{ secrets.PROD_API_KEY }} # GitHub Secret (encrypted)
DEPLOY_URL: ${{ vars.DEPLOY_URL }} # GitHub Variable (not encrypted)Matrix Builds
Run a job multiple times with different variable combinations.
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20, 22]
fail-fast: false # Don't cancel other jobs if one fails
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm testCaching Dependencies
Speed up workflows by caching package manager files.
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # Built-in npm caching
- run: npm ciArtifacts (Upload / Download)
Share files between jobs or download build outputs.
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm run build
- name: Upload build output
uses: actions/upload-artifact@v4
with:
name: build-output
path: dist/
deploy:
needs: build # Run after 'build' job completes
runs-on: ubuntu-latest
steps:
- name: Download build output
uses: actions/download-artifact@v4
with:
name: build-output
path: dist/
- run: ./deploy.shConditional Execution
steps:
- name: Deploy to production
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: ./deploy-prod.sh
- name: Run on PR only
if: github.event_name == 'pull_request'
run: echo "This is a PR"
- name: Run even if previous step failed
if: always()
run: echo "Cleanup"Job Dependencies
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: npm test
deploy:
needs: test # Only runs if 'test' succeeds
runs-on: ubuntu-latest
steps:
- run: ./deploy.sh
notify:
needs: [test, deploy] # Runs after both jobs complete
if: always() # Run even if a dependency failed
runs-on: ubuntu-latest
steps:
- run: echo "Pipeline complete"Reusable Workflows
Call a workflow from another workflow.
# In .github/workflows/deploy.yml (the reusable workflow)
on:
workflow_call:
inputs:
environment:
required: true
type: string
secrets:
deploy_key:
required: true
# In .github/workflows/main.yml (the caller)
jobs:
deploy:
uses: ./.github/workflows/deploy.yml
with:
environment: production
secrets:
deploy_key: ${{ secrets.DEPLOY_KEY }}