Comprehensive CI/CD pipeline patterns skill covering GitHub Actions, workflows, automation, testing, deployment strategies, and release management for modern software delivery
Resources
2Install
npx skillscat add manutej/luxor-claude-marketplace/ci-cd-pipeline-patterns Install via the SkillsCat registry.
CI/CD Pipeline Patterns
A comprehensive skill for designing, implementing, and optimizing CI/CD pipelines using GitHub Actions and modern DevOps practices. Master workflow automation, testing strategies, deployment patterns, and release management for continuous software delivery.
When to Use This Skill
Use this skill when:
- Setting up continuous integration and deployment pipelines for projects
- Automating build, test, and deployment workflows
- Implementing multi-environment deployment strategies (staging, production)
- Managing release automation and versioning
- Configuring matrix builds for multi-platform testing
- Securing CI/CD pipelines with secrets and OIDC
- Optimizing pipeline performance with caching and parallelization
- Building containerized applications with Docker in CI
- Deploying to cloud platforms (AWS, Azure, GCP, Vercel, Netlify)
- Implementing infrastructure as code with Terraform/CloudFormation
- Setting up monorepo CI/CD patterns
- Creating reusable workflow templates and custom actions
- Implementing deployment strategies (blue-green, canary, rolling)
- Automating changelog generation and semantic versioning
- Integrating quality gates and code coverage checks
Core Concepts
CI/CD Fundamentals
Continuous Integration (CI): Automatically building and testing code changes as developers commit to the repository.
Continuous Deployment (CD): Automatically deploying code changes to production after passing tests.
Continuous Delivery: Keeping code in a deployable state, with manual approval for production deployment.
GitHub Actions Architecture
GitHub Actions provides event-driven automation directly integrated with your repository.
Workflows
YAML files in .github/workflows/ that define automated processes:
name: CI Pipeline
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build project
run: npm run buildKey Components:
- name: Human-readable workflow name
- on: Events that trigger the workflow (push, pull_request, schedule, workflow_dispatch)
- jobs: Collection of steps that run in sequence or parallel
- runs-on: The runner environment (ubuntu-latest, windows-latest, macos-latest)
Jobs
Groups of steps executed on the same runner:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm test
deploy:
needs: test # Runs after 'test' job completes
runs-on: ubuntu-latest
steps:
- run: npm run deployJob Features:
- needs: Define job dependencies (sequential execution)
- if: Conditional execution based on expressions
- strategy: Matrix builds for multiple configurations
- outputs: Share data between jobs
- environment: Deployment environments with protection rules
Steps
Individual tasks within a job:
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm testStep Types:
- uses: Run a pre-built action from marketplace or repository
- run: Execute shell commands
- with: Provide inputs to actions
- env: Set environment variables for the step
Actions
Reusable units of code that perform specific tasks:
Official Actions:
actions/checkout@v4: Check out repository codeactions/setup-node@v4: Setup Node.js environmentactions/cache@v4: Cache dependenciesactions/upload-artifact@v4: Upload build artifactsactions/download-artifact@v4: Download artifacts from previous jobs
Marketplace Actions:
docker/build-push-action@v5: Build and push Docker imagesaws-actions/configure-aws-credentials@v4: Configure AWS credentialscodecov/codecov-action@v4: Upload code coveragegoogle-github-actions/auth@v2: Authenticate with Google Cloud
Secrets and Variables
Secrets: Encrypted sensitive data (API keys, credentials, tokens)
steps:
- name: Deploy to production
env:
API_KEY: ${{ secrets.API_KEY }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: npm run deployVariables: Non-sensitive configuration data
env:
NODE_ENV: ${{ vars.NODE_ENV }}
API_ENDPOINT: ${{ vars.API_ENDPOINT }}Secret Types:
- Repository secrets: Available to all workflows in a repository
- Environment secrets: Scoped to specific environments (production, staging)
- Organization secrets: Shared across repositories in an organization
Artifacts
Files produced by workflows that can be downloaded or used by other jobs:
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: dist-files
path: dist/
retention-days: 7
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: dist-files
path: ./distWorkflow Triggers
Event Triggers
Push Events:
on:
push:
branches:
- main
- develop
- 'release/**'
paths:
- 'src/**'
- 'package.json'
tags:
- 'v*'Pull Request Events:
on:
pull_request:
types: [opened, synchronize, reopened]
branches:
- main
paths-ignore:
- 'docs/**'
- '**.md'Schedule (Cron):
on:
schedule:
- cron: '0 0 * * *' # Daily at midnight UTC
- cron: '0 */6 * * *' # Every 6 hoursManual Triggers (workflow_dispatch):
on:
workflow_dispatch:
inputs:
environment:
description: 'Environment to deploy to'
required: true
type: choice
options:
- staging
- production
version:
description: 'Version to deploy'
required: true
type: stringRelease Events:
on:
release:
types: [published, created, released]Workflow Call (Reusable Workflows):
on:
workflow_call:
inputs:
environment:
required: true
type: string
secrets:
api-key:
required: trueMatrix Builds
Run jobs across multiple configurations in parallel:
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [18, 20, 22]
include:
- os: ubuntu-latest
node-version: 20
coverage: true
exclude:
- os: macos-latest
node-version: 18
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm test
- if: matrix.coverage
run: npm run coverageMatrix Features:
- Parallel execution: All combinations run simultaneously
- include: Add specific configurations
- exclude: Remove specific combinations
- fail-fast: Stop all jobs if one fails (default: true)
- max-parallel: Limit concurrent jobs
Caching Strategies
Speed up workflows by caching dependencies:
Node.js Caching:
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # Automatically caches npm dependenciesCustom Caching:
- uses: actions/cache@v4
with:
path: |
~/.npm
~/.cache
node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-Docker Layer Caching:
- uses: docker/build-push-action@v5
with:
context: .
cache-from: type=gha
cache-to: type=gha,mode=maxTesting Strategies in CI
Unit Testing
Fast, isolated tests for individual components:
jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run unit tests
run: npm run test:unit -- --coverage
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
files: ./coverage/coverage-final.json
flags: unit-tests
token: ${{ secrets.CODECOV_TOKEN }}Integration Testing
Test interactions between components and services:
jobs:
integration-tests:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
redis:
image: redis:7
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run database migrations
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/testdb
run: npm run migrate
- name: Run integration tests
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/testdb
REDIS_URL: redis://localhost:6379
run: npm run test:integrationEnd-to-End Testing
Test complete user workflows:
jobs:
e2e-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Build application
run: npm run build
- name: Install Playwright browsers
run: npx playwright install --with-deps
- name: Run E2E tests
run: npm run test:e2e
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 30Performance Testing
Benchmark and performance regression testing:
jobs:
performance-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Build for production
run: npm run build
- name: Run Lighthouse CI
uses: treosh/lighthouse-ci-action@v11
with:
urls: |
http://localhost:3000
http://localhost:3000/dashboard
uploadArtifacts: true
temporaryPublicStorage: true
- name: Run load tests
run: npm run test:loadCode Quality and Linting
Enforce code standards and quality gates:
jobs:
code-quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run ESLint
run: npm run lint
- name: Run Prettier check
run: npm run format:check
- name: Run TypeScript check
run: npm run type-check
- name: Run security audit
run: npm audit --audit-level=moderate
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}Deployment Patterns
Blue-Green Deployment
Zero-downtime deployment by maintaining two identical environments:
jobs:
deploy-blue-green:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to Green environment
run: |
# Deploy new version to green environment
./deploy.sh green
- name: Run smoke tests on Green
run: |
# Verify green environment is healthy
curl -f https://green.example.com/health
- name: Switch traffic to Green
run: |
# Update load balancer to point to green
aws elbv2 modify-rule --rule-arn $RULE_ARN \
--actions Type=forward,TargetGroupArn=$GREEN_TG
- name: Monitor Green environment
run: |
# Monitor for 5 minutes
./monitor.sh green 300
- name: Rollback if needed
if: failure()
run: |
# Switch back to blue
aws elbv2 modify-rule --rule-arn $RULE_ARN \
--actions Type=forward,TargetGroupArn=$BLUE_TGCanary Deployment
Gradual rollout to a subset of users:
jobs:
canary-deployment:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy canary (10% traffic)
run: |
kubectl set image deployment/app app=myapp:${{ github.sha }}
kubectl scale deployment/app-canary --replicas=1
kubectl annotate service app-service \
traffic-split='{"canary": 10, "stable": 90}'
- name: Monitor canary metrics
run: |
# Monitor error rates, latency for 15 minutes
./monitor-canary.sh 900
- name: Increase canary traffic (50%)
run: |
kubectl annotate service app-service \
traffic-split='{"canary": 50, "stable": 50}' --overwrite
- name: Monitor again
run: ./monitor-canary.sh 600
- name: Full rollout (100%)
run: |
kubectl set image deployment/app-stable app=myapp:${{ github.sha }}
kubectl scale deployment/app-canary --replicas=0
- name: Rollback canary
if: failure()
run: |
kubectl scale deployment/app-canary --replicas=0Rolling Deployment
Sequential update of instances:
jobs:
rolling-deployment:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy with rolling update
run: |
kubectl set image deployment/app \
app=myapp:${{ github.sha }} \
--record
- name: Wait for rollout to complete
run: |
kubectl rollout status deployment/app --timeout=10m
- name: Verify deployment
run: |
kubectl get pods -l app=myapp
curl -f https://api.example.com/health
- name: Rollback on failure
if: failure()
run: |
kubectl rollout undo deployment/appMulti-Environment Deployment
Deploy to staging, then production with approvals:
jobs:
deploy-staging:
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.example.com
steps:
- uses: actions/checkout@v4
- name: Deploy to staging
run: ./deploy.sh staging
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment:
name: production
url: https://example.com
steps:
- uses: actions/checkout@v4
- name: Deploy to production
run: ./deploy.sh productionSecurity Best Practices
Secret Management
Using GitHub Secrets:
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1Environment-Scoped Secrets:
jobs:
deploy:
environment: production # Uses production-scoped secrets
steps:
- name: Deploy
env:
API_KEY: ${{ secrets.PRODUCTION_API_KEY }}
run: ./deploy.shOIDC (OpenID Connect)
Authenticate without long-lived credentials:
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials (OIDC)
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
aws-region: us-east-1
- name: Deploy to AWS
run: aws s3 sync ./dist s3://my-bucketGoogle Cloud OIDC:
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
workload_identity_provider: 'projects/123/locations/global/workloadIdentityPools/pool/providers/provider'
service_account: 'github-actions@project.iam.gserviceaccount.com'Secure Workflows
Restrict permissions:
permissions:
contents: read # Read repository contents
pull-requests: write # Comment on PRs
id-token: write # OIDC token generation
actions: read # Read workflow runsPin action versions to SHA:
# Less secure (tag can be moved)
- uses: actions/checkout@v4
# More secure (immutable SHA)
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1Prevent script injection:
# Vulnerable to injection
- run: echo "Hello ${{ github.event.issue.title }}"
# Safe approach
- run: echo "Hello $TITLE"
env:
TITLE: ${{ github.event.issue.title }}Docker in CI/CD
Building Docker Images
jobs:
build-docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: myorg/myapp
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix={{branch}}-
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=maxMulti-Stage Docker Builds
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
EXPOSE 3000
CMD ["npm", "start"]Container Scanning
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myorg/myapp:${{ github.sha }}'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy results to GitHub Security
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'Release Automation
Semantic Versioning
Automatically version releases based on commit messages:
jobs:
release:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Semantic Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npx semantic-releaseConfiguration (.releaserc.json):
{
"branches": ["main"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
"@semantic-release/npm",
"@semantic-release/github",
["@semantic-release/git", {
"assets": ["CHANGELOG.md", "package.json"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}]
]
}Changelog Generation
- name: Generate changelog
uses: mikepenz/release-changelog-builder-action@v4
with:
configuration: '.github/changelog-config.json'
outputFile: 'CHANGELOG.md'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create GitHub Release
uses: ncipollo/release-action@v1
with:
tag: ${{ steps.version.outputs.tag }}
name: Release ${{ steps.version.outputs.tag }}
bodyFile: 'CHANGELOG.md'
artifacts: 'dist/*'Release Notes Automation
- name: Build Release Notes
id: release_notes
uses: mikepenz/release-changelog-builder-action@v4
with:
configurationJson: |
{
"categories": [
{
"title": "## 🚀 Features",
"labels": ["feature", "enhancement"]
},
{
"title": "## 🐛 Fixes",
"labels": ["bug", "fix"]
},
{
"title": "## 📝 Documentation",
"labels": ["documentation"]
}
]
}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}Monorepo CI/CD Patterns
Path-Based Triggers
Run workflows only when specific packages change:
name: Frontend CI
on:
push:
paths:
- 'packages/frontend/**'
- 'package.json'
- 'pnpm-lock.yaml'
jobs:
test-frontend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Test frontend
run: pnpm --filter frontend testAffected Package Detection
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
affected: ${{ steps.affected.outputs.packages }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Detect affected packages
id: affected
run: |
# Use tools like Nx or Turborepo to detect changes
AFFECTED=$(npx nx affected:apps --base=origin/main --plain)
echo "packages=$AFFECTED" >> $GITHUB_OUTPUT
test-affected:
needs: detect-changes
runs-on: ubuntu-latest
strategy:
matrix:
package: ${{ fromJson(needs.detect-changes.outputs.affected) }}
steps:
- uses: actions/checkout@v4
- name: Test ${{ matrix.package }}
run: npm run test --workspace=${{ matrix.package }}Turborepo CI
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Build with Turborepo
run: npx turbo build --cache-dir=.turbo
- name: Cache Turbo
uses: actions/cache@v4
with:
path: .turbo
key: ${{ runner.os }}-turbo-${{ github.sha }}
restore-keys: |
${{ runner.os }}-turbo-Performance Optimization
Parallel Job Execution
jobs:
# These jobs run in parallel
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm run lint
unit-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm run test:unit
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm run build
# This job waits for all above to complete
deploy:
needs: [lint, unit-test, build]
runs-on: ubuntu-latest
steps:
- run: npm run deployConditional Job Execution
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm run build
deploy-staging:
needs: build
if: github.ref == 'refs/heads/develop'
runs-on: ubuntu-latest
steps:
- run: ./deploy.sh staging
deploy-production:
needs: build
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- run: ./deploy.sh productionDependency Caching
steps:
# Node.js with npm
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
# Python with pip
- uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
# Ruby with bundler
- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true
# Go modules
- uses: actions/setup-go@v5
with:
go-version: '1.21'
cache: trueReusable Workflows
Creating Reusable Workflows
# .github/workflows/reusable-deploy.yml
name: Reusable Deploy Workflow
on:
workflow_call:
inputs:
environment:
required: true
type: string
version:
required: false
type: string
default: 'latest'
secrets:
deploy-key:
required: true
outputs:
deployment-url:
description: "URL of the deployment"
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: Deploy
id: deploy
env:
DEPLOY_KEY: ${{ secrets.deploy-key }}
run: |
./deploy.sh ${{ inputs.environment }} ${{ inputs.version }}
echo "url=https://${{ inputs.environment }}.example.com" >> $GITHUB_OUTPUTCalling Reusable Workflows
# .github/workflows/main.yml
name: Main Pipeline
on: [push]
jobs:
deploy-staging:
uses: ./.github/workflows/reusable-deploy.yml
with:
environment: staging
version: ${{ github.sha }}
secrets:
deploy-key: ${{ secrets.STAGING_DEPLOY_KEY }}
deploy-production:
needs: deploy-staging
uses: ./.github/workflows/reusable-deploy.yml
with:
environment: production
version: ${{ github.sha }}
secrets:
deploy-key: ${{ secrets.PRODUCTION_DEPLOY_KEY }}Infrastructure as Code
Terraform Deployment
jobs:
terraform:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.7.0
- name: Terraform Format
run: terraform fmt -check
- name: Terraform Init
run: terraform init
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Terraform Validate
run: terraform validate
- name: Terraform Plan
run: terraform plan -out=tfplan
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Terraform Apply
if: github.ref == 'refs/heads/main'
run: terraform apply -auto-approve tfplan
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}AWS CloudFormation
jobs:
deploy-cloudformation:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- name: Deploy CloudFormation stack
run: |
aws cloudformation deploy \
--template-file infrastructure/template.yml \
--stack-name my-app-stack \
--parameter-overrides \
Environment=production \
Version=${{ github.sha }} \
--capabilities CAPABILITY_IAMBest Practices
Workflow Organization
- Separate concerns: Different workflows for CI, CD, and scheduled tasks
- Use descriptive names: Clear workflow and job names
- Organize with directories: Group related workflows
- Version control: Track workflow changes like code
Efficiency
- Cache dependencies: Reduce build times significantly
- Parallel execution: Run independent jobs simultaneously
- Conditional runs: Skip unnecessary jobs
- Matrix strategies: Test multiple configurations efficiently
- Artifact reuse: Share build outputs between jobs
Security
- Minimize permissions: Use least-privilege principle
- Use OIDC: Avoid long-lived credentials
- Secret rotation: Regularly update secrets
- Pin dependencies: Use specific versions or SHAs
- Scan for vulnerabilities: Automated security checks
Reliability
- Timeout settings: Prevent hanging jobs
- Retry logic: Handle transient failures
- Failure notifications: Alert on critical failures
- Rollback mechanisms: Quick recovery from failed deployments
- Health checks: Verify deployments before marking complete
Observability
- Detailed logging: Clear, actionable logs
- Status checks: Prevent merging failing builds
- Deployment tracking: Know what's deployed where
- Metrics collection: Track pipeline performance
- Audit trails: Track who deployed what and when
Failure Handling
Retry Failed Steps
steps:
- name: Deploy with retry
uses: nick-fields/retry-action@v2
with:
timeout_minutes: 10
max_attempts: 3
retry_wait_seconds: 30
command: npm run deployContinue on Error
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Run optional check
continue-on-error: true
run: npm run optional-check
- name: Run required tests
run: npm testConditional Cleanup
steps:
- name: Deploy
id: deploy
run: ./deploy.sh
- name: Rollback on failure
if: failure() && steps.deploy.conclusion == 'failure'
run: ./rollback.sh
- name: Cleanup
if: always()
run: ./cleanup.shAdvanced Patterns
Dynamic Matrix Generation
jobs:
generate-matrix:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- uses: actions/checkout@v4
- id: set-matrix
run: |
# Generate matrix based on project structure
MATRIX=$(find packages -maxdepth 1 -type d -not -name packages | \
jq -R -s -c 'split("\n")[:-1]')
echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
test:
needs: generate-matrix
runs-on: ubuntu-latest
strategy:
matrix:
package: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
steps:
- uses: actions/checkout@v4
- run: npm test --workspace=${{ matrix.package }}Composite Actions
Create reusable action combinations:
# .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: '20'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: 'npm'
- run: npm ci
shell: bash
- run: npm run build
shell: bashUsage:
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-project
with:
node-version: '20'Self-Hosted Runners
jobs:
deploy:
runs-on: [self-hosted, linux, production]
steps:
- uses: actions/checkout@v4
- name: Deploy to production
run: ./deploy.shBenefits:
- Custom hardware/software requirements
- Faster builds (pre-cached dependencies)
- Access to internal networks
- Cost savings for high-volume CI/CD
Platform-Specific Deployments
Vercel Deployment
jobs:
deploy-vercel:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'Netlify Deployment
jobs:
deploy-netlify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: npm run build
- name: Deploy to Netlify
uses: nwtgck/actions-netlify@v3
with:
publish-dir: './dist'
production-branch: main
github-token: ${{ secrets.GITHUB_TOKEN }}
deploy-message: 'Deploy from GitHub Actions'
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}AWS ECS Deployment
jobs:
deploy-ecs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build and push Docker image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: my-app
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
- name: Update ECS service
run: |
aws ecs update-service \
--cluster my-cluster \
--service my-service \
--force-new-deploymentKubernetes Deployment
jobs:
deploy-k8s:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup kubectl
uses: azure/setup-kubectl@v3
with:
version: 'v1.28.0'
- name: Configure kubeconfig
run: |
echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > kubeconfig.yml
echo "KUBECONFIG=$(pwd)/kubeconfig.yml" >> $GITHUB_ENV
- name: Deploy to Kubernetes
run: |
kubectl set image deployment/myapp \
myapp=myregistry/myapp:${{ github.sha }}
kubectl rollout status deployment/myappSkill Version: 1.0.0
Last Updated: October 2025
Skill Category: DevOps, CI/CD, Automation, Deployment
Compatible With: GitHub Actions, Docker, Kubernetes, AWS, Azure, GCP, Vercel, Netlify