
Ci Cd Pipeline Patterns
- 398 installs
- 61 repo stars
- Updated June 13, 2026
- manutej/luxor-claude-marketplace
ci-cd-pipeline-patterns is a skill that designs CI/CD workflows with staged jobs, test gates, build caches, matrix strategies, artifact promotion, and rollback hooks for developers shipping application releases.
About
ci-cd-pipeline-patterns is a Claude marketplace skill for designing production-grade CI/CD workflows before merges or releases ship. The skill covers staged jobs, automated test gates, build cache strategies, matrix build configurations, artifact promotion between environments, and rollback hooks for failed deployments. Developers reach for ci-cd-pipeline-patterns when GitHub Actions, GitLab CI, or similar platforms need a structured pipeline blueprint instead of ad hoc job copies that skip gates or promotion steps.
- Multi-stage job orchestration
- Cache and artifact strategies
- Parallel matrix build patterns
- Deployment promotion gates
- Rollback and release workflows
Ci Cd Pipeline Patterns by the numbers
- 398 all-time installs (skills.sh)
- +18 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #301 of 1,435 DevOps & CI/CD skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/manutej/luxor-claude-marketplace --skill ci-cd-pipeline-patternsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 398 |
|---|---|
| repo stars | ★ 61 |
| Last updated | June 13, 2026 |
| Repository | manutej/luxor-claude-marketplace ↗ |
How do you design CI/CD pipelines with test gates?
Design CI/CD workflows with staged jobs, test gates, build caches, matrix strategies, artifact promotion, and rollback hooks before merging or shipping application releases.
Who is it for?
Developers architecting release pipelines who need staged validation, matrix builds, and promotion patterns before production deploys.
Skip if: Teams wanting a single lint job only or infrastructure-as-code modules without pipeline orchestration guidance.
When should I use this skill?
A repository needs a multi-stage CI/CD design with test gates, caching, matrix strategies, or rollback hooks.
What you get
CI/CD workflow YAML blueprints, staged job graphs, cache strategies, and rollback hook configurations.
- CI/CD workflow blueprints
- staged job configurations
Files
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
1. Separate concerns: Different workflows for CI, CD, and scheduled tasks 2. Use descriptive names: Clear workflow and job names 3. Organize with directories: Group related workflows 4. Version control: Track workflow changes like code
Efficiency
1. Cache dependencies: Reduce build times significantly 2. Parallel execution: Run independent jobs simultaneously 3. Conditional runs: Skip unnecessary jobs 4. Matrix strategies: Test multiple configurations efficiently 5. Artifact reuse: Share build outputs between jobs
Security
1. Minimize permissions: Use least-privilege principle 2. Use OIDC: Avoid long-lived credentials 3. Secret rotation: Regularly update secrets 4. Pin dependencies: Use specific versions or SHAs 5. Scan for vulnerabilities: Automated security checks
Reliability
1. Timeout settings: Prevent hanging jobs 2. Retry logic: Handle transient failures 3. Failure notifications: Alert on critical failures 4. Rollback mechanisms: Quick recovery from failed deployments 5. Health checks: Verify deployments before marking complete
Observability
1. Detailed logging: Clear, actionable logs 2. Status checks: Prevent merging failing builds 3. Deployment tracking: Know what's deployed where 4. Metrics collection: Track pipeline performance 5. 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/myapp---
Skill 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
CI/CD Pipeline Examples
Production-ready workflow examples for modern software delivery
This document contains comprehensive, battle-tested CI/CD pipeline examples that you can adapt for your projects. Each example includes complete configuration files and explanations.
Table of Contents
1. Complete Node.js CI/CD Pipeline 2. Docker Multi-Stage Build and Push 3. Multi-Environment Deployment with Approvals 4. Matrix Testing Across Platforms 5. Semantic Release Automation 6. Terraform Infrastructure Deployment 7. Kubernetes Blue-Green Deployment 8. Monorepo CI with Turborepo 9. Python Application with Poetry 10. Serverless Lambda Deployment 11. Frontend Deploy to Vercel/Netlify 12. Database Migration Pipeline 13. Mobile App CI (React Native) 14. Canary Deployment with Flagger 15. Security Scanning Pipeline 16. Performance Benchmarking 17. Scheduled Maintenance Jobs 18. Reusable Workflow Templates
---
1. Complete Node.js CI/CD Pipeline
A comprehensive pipeline covering linting, testing, building, and deployment for a Node.js application.
.github/workflows/nodejs-cicd.yml
name: Node.js CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
NODE_VERSION: '20'
jobs:
# Code quality checks
lint:
name: Lint Code
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- 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
# Unit and integration tests
test:
name: Run Tests
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
POSTGRES_DB: testdb
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
redis:
image: redis:7-alpine
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run unit tests
run: npm run test:unit -- --coverage
- name: Run integration tests
env:
DATABASE_URL: postgresql://testuser:testpass@localhost:5432/testdb
REDIS_URL: redis://localhost:6379
run: npm run test:integration
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/coverage-final.json
flags: unittests,integrationtests
fail_ci_if_error: true
# Security audit
security:
name: Security Audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Run npm audit
run: npm audit --audit-level=moderate
- name: Run Snyk security scan
uses: snyk/actions/node@master
continue-on-error: true
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
# Build application
build:
name: Build Application
needs: [lint, test, security]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build application
run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build-artifacts
path: dist/
retention-days: 7
# Deploy to staging
deploy-staging:
name: Deploy to Staging
needs: build
if: github.ref == 'refs/heads/develop'
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.example.com
steps:
- uses: actions/checkout@v4
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: build-artifacts
path: dist/
- 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 to S3
run: aws s3 sync dist/ s3://staging-bucket --delete
- name: Invalidate CloudFront
run: |
aws cloudfront create-invalidation \
--distribution-id ${{ secrets.STAGING_CLOUDFRONT_ID }} \
--paths "/*"
- name: Run smoke tests
run: |
sleep 10
curl -f https://staging.example.com/health || exit 1
# Deploy to production
deploy-production:
name: Deploy to Production
needs: build
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment:
name: production
url: https://example.com
steps:
- uses: actions/checkout@v4
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: build-artifacts
path: dist/
- 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 to S3
run: aws s3 sync dist/ s3://production-bucket --delete
- name: Invalidate CloudFront
run: |
aws cloudfront create-invalidation \
--distribution-id ${{ secrets.PRODUCTION_CLOUDFRONT_ID }} \
--paths "/*"
- name: Run smoke tests
run: |
sleep 10
curl -f https://example.com/health || exit 1
- name: Notify Slack
if: always()
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 'Production deployment ${{ job.status }}'
webhook_url: ${{ secrets.SLACK_WEBHOOK }}---
2. Docker Multi-Stage Build and Push
Build optimized Docker images with multi-stage builds and push to multiple registries.
.github/workflows/docker-build.yml
name: Docker Build and Push
on:
push:
branches: [main, develop]
tags: ['v*']
pull_request:
branches: [main]
env:
REGISTRY_DOCKERHUB: docker.io
REGISTRY_GHCR: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY_DOCKERHUB }}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY_GHCR }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: |
${{ env.REGISTRY_DOCKERHUB }}/${{ env.IMAGE_NAME }}
${{ env.REGISTRY_GHCR }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
BUILD_DATE=${{ github.event.head_commit.timestamp }}
VCS_REF=${{ github.sha }}
VERSION=${{ steps.meta.outputs.version }}
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY_GHCR }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
- name: Upload Trivy results to GitHub Security
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: ${{ env.REGISTRY_GHCR }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
format: spdx-json
output-file: sbom.spdx.json
- name: Upload SBOM
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.spdx.jsonDockerfile (Multi-Stage)
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
# Install dependencies
COPY package*.json ./
RUN npm ci --only=production && \
npm cache clean --force
# Copy source and build
COPY . .
RUN npm run build
# Production stage
FROM node:20-alpine
WORKDIR /app
# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
# Copy dependencies and build from builder
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --chown=nodejs:nodejs package*.json ./
# Security: Don't run as root
USER nodejs
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
EXPOSE 3000
CMD ["node", "dist/index.js"]---
3. Multi-Environment Deployment with Approvals
Deploy to multiple environments with manual approval gates and environment protection rules.
.github/workflows/multi-env-deploy.yml
name: Multi-Environment Deployment
on:
push:
branches: [main, staging, develop]
workflow_dispatch:
inputs:
environment:
description: 'Environment to deploy to'
required: true
type: choice
options:
- development
- staging
- production
jobs:
build:
name: Build Application
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Generate version
id: version
run: |
VERSION=$(date +%Y%m%d)-$(git rev-parse --short HEAD)
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Build application
run: npm run build
env:
VERSION: ${{ steps.version.outputs.version }}
- name: Create artifact
run: tar -czf app-${{ steps.version.outputs.version }}.tar.gz dist/
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: app-artifact
path: app-${{ steps.version.outputs.version }}.tar.gz
deploy-development:
name: Deploy to Development
needs: build
if: github.ref == 'refs/heads/develop' || (github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'development')
runs-on: ubuntu-latest
environment:
name: development
url: https://dev.example.com
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: app-artifact
- name: Extract artifact
run: tar -xzf app-${{ needs.build.outputs.version }}.tar.gz
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_DEV }}
aws-region: us-east-1
- name: Deploy to development
run: |
aws s3 sync dist/ s3://dev-bucket --delete
aws cloudfront create-invalidation \
--distribution-id ${{ secrets.DEV_CLOUDFRONT_ID }} \
--paths "/*"
- name: Health check
run: |
for i in {1..5}; do
if curl -f https://dev.example.com/health; then
echo "Health check passed"
exit 0
fi
echo "Attempt $i failed, retrying..."
sleep 10
done
exit 1
deploy-staging:
name: Deploy to Staging
needs: build
if: github.ref == 'refs/heads/staging' || (github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'staging')
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.example.com
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: app-artifact
- name: Extract artifact
run: tar -xzf app-${{ needs.build.outputs.version }}.tar.gz
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_STAGING }}
aws-region: us-east-1
- name: Deploy to staging
run: |
aws s3 sync dist/ s3://staging-bucket --delete
aws cloudfront create-invalidation \
--distribution-id ${{ secrets.STAGING_CLOUDFRONT_ID }} \
--paths "/*"
- name: Run smoke tests
run: |
npm ci
npm run test:smoke -- --env=staging
deploy-production:
name: Deploy to Production
needs: [build, deploy-staging]
if: github.ref == 'refs/heads/main' || (github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'production')
runs-on: ubuntu-latest
environment:
name: production
url: https://example.com
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: app-artifact
- name: Extract artifact
run: tar -xzf app-${{ needs.build.outputs.version }}.tar.gz
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_PRODUCTION }}
aws-region: us-east-1
- name: Backup current version
run: |
aws s3 sync s3://production-bucket s3://production-bucket-backup/$(date +%Y%m%d-%H%M%S)
- name: Deploy to production
id: deploy
run: |
aws s3 sync dist/ s3://production-bucket --delete
aws cloudfront create-invalidation \
--distribution-id ${{ secrets.PRODUCTION_CLOUDFRONT_ID }} \
--paths "/*"
- name: Health check
id: health
run: |
for i in {1..10}; do
if curl -f https://example.com/health; then
echo "Health check passed"
exit 0
fi
echo "Attempt $i failed, retrying..."
sleep 15
done
exit 1
- name: Rollback on failure
if: failure() && (steps.deploy.conclusion == 'success' || steps.health.conclusion == 'failure')
run: |
echo "Rolling back to previous version"
BACKUP=$(aws s3 ls s3://production-bucket-backup/ | tail -1 | awk '{print $2}')
aws s3 sync s3://production-bucket-backup/$BACKUP s3://production-bucket --delete
aws cloudfront create-invalidation \
--distribution-id ${{ secrets.PRODUCTION_CLOUDFRONT_ID }} \
--paths "/*"
- name: Create deployment record
if: success()
run: |
echo "Deployment successful: ${{ needs.build.outputs.version }}"
# Log to deployment tracking system
curl -X POST https://api.example.com/deployments \
-H "Authorization: Bearer ${{ secrets.API_TOKEN }}" \
-d "{\"version\": \"${{ needs.build.outputs.version }}\", \"environment\": \"production\", \"status\": \"success\"}"
- name: Notify team
if: always()
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: |
Production deployment ${{ job.status }}
Version: ${{ needs.build.outputs.version }}
URL: https://example.com
webhook_url: ${{ secrets.SLACK_WEBHOOK }}---
4. Matrix Testing Across Platforms
Test across multiple operating systems, language versions, and configurations.
.github/workflows/matrix-testing.yml
name: Cross-Platform Testing
on: [push, pull_request]
jobs:
test-matrix:
name: Test on ${{ matrix.os }} with Node ${{ matrix.node }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node: [18, 20, 22]
include:
# Add coverage only for one configuration
- os: ubuntu-latest
node: 20
coverage: true
# Exclude specific combinations
exclude:
- os: macos-latest
node: 18
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run tests
run: npm test
- name: Run tests with coverage
if: matrix.coverage
run: npm test -- --coverage
- name: Upload coverage
if: matrix.coverage
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/coverage-final.json
flags: node-${{ matrix.node }}
- name: Build application
run: npm run build
- name: Test build output
shell: bash
run: |
if [ ! -d "dist" ]; then
echo "Build failed - dist directory not found"
exit 1
fi
test-databases:
name: Test with ${{ matrix.database }}
runs-on: ubuntu-latest
strategy:
matrix:
database:
- postgres:14
- postgres:15
- postgres:16
- mysql:8.0
- mysql:8.2
services:
database:
image: ${{ matrix.database }}
env:
POSTGRES_PASSWORD: postgres
MYSQL_ROOT_PASSWORD: mysql
options: >-
--health-cmd "${{ contains(matrix.database, 'postgres') && 'pg_isready' || 'mysqladmin ping' }}"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
- 3306:3306
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run database migrations
env:
DATABASE_URL: ${{ contains(matrix.database, 'postgres') && 'postgresql://postgres:postgres@localhost:5432/testdb' || 'mysql://root:mysql@localhost:3306/testdb' }}
run: npm run migrate
- name: Run integration tests
env:
DATABASE_URL: ${{ contains(matrix.database, 'postgres') && 'postgresql://postgres:postgres@localhost:5432/testdb' || 'mysql://root:mysql@localhost:3306/testdb' }}
run: npm run test:integration
test-browsers:
name: E2E on ${{ matrix.browser }}
runs-on: ubuntu-latest
strategy:
matrix:
browser: [chromium, firefox, webkit]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Install Playwright
run: npx playwright install --with-deps ${{ matrix.browser }}
- name: Build app
run: npm run build
- name: Run E2E tests
run: npx playwright test --project=${{ matrix.browser }}
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-results-${{ matrix.browser }}
path: playwright-report/
retention-days: 7---
5. Semantic Release Automation
Automatically version, generate changelogs, and publish releases based on commit conventions.
.github/workflows/release.yml
name: Release
on:
push:
branches:
- main
- next
- beta
- alpha
permissions:
contents: write
issues: write
pull-requests: write
packages: write
jobs:
release:
name: Semantic Release
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build application
run: npm run build
- name: Semantic Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npx semantic-release
- name: Get release version
id: version
run: |
VERSION=$(node -p "require('./package.json').version")
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Create GitHub Release
if: steps.version.outputs.version != ''
uses: ncipollo/release-action@v1
with:
tag: v${{ steps.version.outputs.version }}
name: Release v${{ steps.version.outputs.version }}
bodyFile: RELEASE_NOTES.md
artifacts: 'dist/*'
generateReleaseNotes: true
token: ${{ secrets.GITHUB_TOKEN }}
- name: Publish to npm
if: steps.version.outputs.version != ''
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Build and push Docker image
if: steps.version.outputs.version != ''
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
myorg/myapp:latest
myorg/myapp:v${{ steps.version.outputs.version }}
myorg/myapp:${{ github.sha }}
- name: Update documentation
if: steps.version.outputs.version != ''
run: |
npm run docs:generate
# Deploy docs to GitHub Pages or documentation site
- name: Notify release
if: steps.version.outputs.version != ''
uses: 8398a7/action-slack@v3
with:
status: custom
custom_payload: |
{
text: "New release published!",
attachments: [{
color: 'good',
text: `Version v${{ steps.version.outputs.version }} has been released\nhttps://github.com/${{ github.repository }}/releases/tag/v${{ steps.version.outputs.version }}`
}]
}
webhook_url: ${{ secrets.SLACK_WEBHOOK }}.releaserc.json
{
"branches": [
"main",
{
"name": "next",
"prerelease": true
},
{
"name": "beta",
"prerelease": true
},
{
"name": "alpha",
"prerelease": true
}
],
"plugins": [
[
"@semantic-release/commit-analyzer",
{
"preset": "angular",
"releaseRules": [
{ "type": "docs", "scope": "README", "release": "patch" },
{ "type": "refactor", "release": "patch" },
{ "type": "style", "release": "patch" },
{ "type": "perf", "release": "patch" }
]
}
],
[
"@semantic-release/release-notes-generator",
{
"preset": "angular",
"writerOpts": {
"commitsSort": ["subject", "scope"]
}
}
],
"@semantic-release/changelog",
[
"@semantic-release/npm",
{
"npmPublish": true
}
],
[
"@semantic-release/git",
{
"assets": ["CHANGELOG.md", "package.json", "package-lock.json"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
],
[
"@semantic-release/github",
{
"assets": [
{
"path": "dist/**",
"label": "Distribution"
}
]
}
]
]
}---
6. Terraform Infrastructure Deployment
Deploy and manage infrastructure as code with Terraform.
.github/workflows/terraform.yml
name: Terraform Infrastructure
on:
push:
branches: [main]
paths:
- 'terraform/**'
pull_request:
branches: [main]
paths:
- 'terraform/**'
workflow_dispatch:
inputs:
action:
description: 'Terraform action to perform'
required: true
type: choice
options:
- plan
- apply
- destroy
env:
TF_VERSION: '1.7.0'
TF_WORKING_DIR: './terraform'
jobs:
terraform-validation:
name: Terraform Validation
runs-on: ubuntu-latest
defaults:
run:
working-directory: ${{ env.TF_WORKING_DIR }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TF_VERSION }}
- name: Terraform Format Check
run: terraform fmt -check -recursive
- name: Terraform Init
run: terraform init -backend=false
- name: Terraform Validate
run: terraform validate
- name: Run tflint
uses: terraform-linters/setup-tflint@v4
with:
tflint_version: latest
- name: Initialize tflint
run: tflint --init
- name: Run tflint
run: tflint --recursive
terraform-plan:
name: Terraform Plan
needs: terraform-validation
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
pull-requests: write
defaults:
run:
working-directory: ${{ env.TF_WORKING_DIR }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TF_VERSION }}
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_TERRAFORM_ROLE }}
aws-region: us-east-1
- name: Terraform Init
run: terraform init
- name: Terraform Plan
id: plan
run: |
terraform plan -no-color -out=tfplan
terraform show -no-color tfplan > plan.txt
- name: Upload plan
uses: actions/upload-artifact@v4
with:
name: terraform-plan
path: ${{ env.TF_WORKING_DIR }}/tfplan
- name: Comment PR with plan
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const plan = fs.readFileSync('${{ env.TF_WORKING_DIR }}/plan.txt', 'utf8');
const output = `#### Terraform Plan 📋
<details><summary>Show Plan</summary>
\`\`\`terraform
${plan}
\`\`\`
</details>
*Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
});
- name: Run Checkov security scan
uses: bridgecrewio/checkov-action@master
with:
directory: ${{ env.TF_WORKING_DIR }}
framework: terraform
output_format: sarif
output_file_path: checkov-results.sarif
- name: Upload Checkov results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: checkov-results.sarif
terraform-apply:
name: Terraform Apply
needs: terraform-plan
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
environment:
name: production-infrastructure
defaults:
run:
working-directory: ${{ env.TF_WORKING_DIR }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TF_VERSION }}
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_TERRAFORM_ROLE }}
aws-region: us-east-1
- name: Terraform Init
run: terraform init
- name: Download plan
uses: actions/download-artifact@v4
with:
name: terraform-plan
path: ${{ env.TF_WORKING_DIR }}
- name: Terraform Apply
run: terraform apply -auto-approve tfplan
- name: Get outputs
id: outputs
run: |
terraform output -json > outputs.json
echo "outputs=$(cat outputs.json)" >> $GITHUB_OUTPUT
- name: Update documentation
run: |
# Generate infrastructure documentation
terraform-docs markdown table ${{ env.TF_WORKING_DIR }} > INFRASTRUCTURE.md
- name: Notify deployment
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 'Terraform infrastructure deployment completed'
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
terraform-destroy:
name: Terraform Destroy
if: github.event_name == 'workflow_dispatch' && github.event.inputs.action == 'destroy'
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
environment:
name: production-infrastructure-destroy
defaults:
run:
working-directory: ${{ env.TF_WORKING_DIR }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TF_VERSION }}
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_TERRAFORM_ROLE }}
aws-region: us-east-1
- name: Terraform Init
run: terraform init
- name: Terraform Destroy
run: terraform destroy -auto-approve---
7. Kubernetes Blue-Green Deployment
Zero-downtime deployment using blue-green strategy in Kubernetes.
.github/workflows/k8s-blue-green.yml
name: Kubernetes Blue-Green Deployment
on:
push:
branches: [main]
workflow_dispatch:
env:
CLUSTER_NAME: production-cluster
NAMESPACE: production
APP_NAME: myapp
jobs:
build-and-push:
name: Build and Push Docker Image
runs-on: ubuntu-latest
outputs:
image-tag: ${{ steps.meta.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to container registry
uses: docker/login-action@v3
with:
registry: ${{ secrets.REGISTRY_URL }}
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ secrets.REGISTRY_URL }}/${{ env.APP_NAME }}
tags: |
type=sha,prefix={{branch}}-
type=ref,event=branch
type=semver,pattern={{version}}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
deploy-green:
name: Deploy to Green Environment
needs: build-and-push
runs-on: ubuntu-latest
environment:
name: production-green
steps:
- uses: actions/checkout@v4
- name: Configure kubectl
uses: azure/k8s-set-context@v3
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBE_CONFIG }}
- name: Deploy green deployment
run: |
# Create green deployment if it doesn't exist
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${{ env.APP_NAME }}-green
namespace: ${{ env.NAMESPACE }}
labels:
app: ${{ env.APP_NAME }}
version: green
spec:
replicas: 3
selector:
matchLabels:
app: ${{ env.APP_NAME }}
version: green
template:
metadata:
labels:
app: ${{ env.APP_NAME }}
version: green
spec:
containers:
- name: ${{ env.APP_NAME }}
image: ${{ secrets.REGISTRY_URL }}/${{ env.APP_NAME }}:${{ needs.build-and-push.outputs.image-tag }}
ports:
- containerPort: 3000
env:
- name: VERSION
value: "${{ needs.build-and-push.outputs.image-tag }}"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
EOF
- name: Wait for green deployment
run: |
kubectl rollout status deployment/${{ env.APP_NAME }}-green -n ${{ env.NAMESPACE }} --timeout=5m
- name: Run smoke tests against green
run: |
# Get green pod IP
GREEN_POD=$(kubectl get pod -n ${{ env.NAMESPACE }} -l app=${{ env.APP_NAME }},version=green -o jsonpath='{.items[0].metadata.name}')
# Port forward to test
kubectl port-forward -n ${{ env.NAMESPACE }} $GREEN_POD 8080:3000 &
PF_PID=$!
sleep 5
# Run tests
curl -f http://localhost:8080/health || exit 1
# Cleanup
kill $PF_PID
switch-to-green:
name: Switch Traffic to Green
needs: deploy-green
runs-on: ubuntu-latest
environment:
name: production-switch
steps:
- uses: actions/checkout@v4
- name: Configure kubectl
uses: azure/k8s-set-context@v3
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBE_CONFIG }}
- name: Update service to point to green
run: |
kubectl patch service ${{ env.APP_NAME }} -n ${{ env.NAMESPACE }} -p '{"spec":{"selector":{"version":"green"}}}'
- name: Wait and monitor
run: |
echo "Monitoring green deployment for 5 minutes..."
sleep 300
- name: Check error rates
run: |
# Query metrics/logs to check for errors
# If error rate is high, fail the deployment
ERROR_RATE=$(kubectl logs -n ${{ env.NAMESPACE }} -l app=${{ env.APP_NAME }},version=green --tail=1000 | grep -c "ERROR" || echo "0")
if [ "$ERROR_RATE" -gt 10 ]; then
echo "Error rate too high: $ERROR_RATE"
exit 1
fi
cleanup-blue:
name: Cleanup Blue Deployment
needs: switch-to-green
runs-on: ubuntu-latest
steps:
- name: Configure kubectl
uses: azure/k8s-set-context@v3
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBE_CONFIG }}
- name: Scale down blue deployment
run: |
kubectl scale deployment/${{ env.APP_NAME }}-blue -n ${{ env.NAMESPACE }} --replicas=0 || echo "Blue deployment doesn't exist"
- name: Rename deployments
run: |
# Rename green to blue for next deployment
kubectl delete deployment/${{ env.APP_NAME }}-blue -n ${{ env.NAMESPACE }} || true
kubectl get deployment/${{ env.APP_NAME }}-green -n ${{ env.NAMESPACE }} -o yaml | \
sed 's/-green/-blue/g' | \
kubectl apply -f -
kubectl delete deployment/${{ env.APP_NAME }}-green -n ${{ env.NAMESPACE }}
- name: Update service
run: |
kubectl patch service ${{ env.APP_NAME }} -n ${{ env.NAMESPACE }} -p '{"spec":{"selector":{"version":"blue"}}}'
rollback:
name: Rollback to Blue
if: failure()
needs: [deploy-green, switch-to-green]
runs-on: ubuntu-latest
steps:
- name: Configure kubectl
uses: azure/k8s-set-context@v3
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBE_CONFIG }}
- name: Switch back to blue
run: |
kubectl patch service ${{ env.APP_NAME }} -n ${{ env.NAMESPACE }} -p '{"spec":{"selector":{"version":"blue"}}}'
- name: Scale down green
run: |
kubectl scale deployment/${{ env.APP_NAME }}-green -n ${{ env.NAMESPACE }} --replicas=0
- name: Notify rollback
uses: 8398a7/action-slack@v3
with:
status: failure
text: 'Deployment failed - rolled back to blue environment'
webhook_url: ${{ secrets.SLACK_WEBHOOK }}---
8. Monorepo CI with Turborepo
Efficient CI/CD for monorepos using Turborepo for task caching and parallelization.
.github/workflows/turborepo-ci.yml
name: Monorepo CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
jobs:
changes:
name: Detect Changes
runs-on: ubuntu-latest
outputs:
packages: ${{ steps.filter.outputs.changes }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
frontend:
- 'apps/frontend/**'
backend:
- 'apps/backend/**'
mobile:
- 'apps/mobile/**'
shared:
- 'packages/**'
setup:
name: Setup
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Get pnpm store directory
id: pnpm-cache
run: echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
- name: Setup pnpm cache
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Cache Turbo
uses: actions/cache@v4
with:
path: .turbo
key: ${{ runner.os }}-turbo-${{ github.sha }}
restore-keys: |
${{ runner.os }}-turbo-
lint:
name: Lint
needs: setup
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Restore cache
uses: actions/cache@v4
with:
path: |
~/.pnpm-store
.turbo
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run lint
run: pnpm turbo run lint
type-check:
name: Type Check
needs: setup
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Restore cache
uses: actions/cache@v4
with:
path: |
~/.pnpm-store
.turbo
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run type check
run: pnpm turbo run type-check
test:
name: Test
needs: setup
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Restore cache
uses: actions/cache@v4
with:
path: |
~/.pnpm-store
.turbo
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Run tests
run: pnpm turbo run test -- --coverage
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
directory: ./coverage
flags: monorepo
token: ${{ secrets.CODECOV_TOKEN }}
build:
name: Build
needs: [lint, type-check, test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Restore cache
uses: actions/cache@v4
with:
path: |
~/.pnpm-store
.turbo
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build packages
run: pnpm turbo run build
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build-outputs
path: |
apps/*/dist
apps/*/.next
retention-days: 7
deploy-affected:
name: Deploy Affected Apps
needs: [build, changes]
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
strategy:
matrix:
app: ${{ fromJson(needs.changes.outputs.packages) }}
steps:
- uses: actions/checkout@v4
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: build-outputs
- name: Deploy ${{ matrix.app }}
run: |
echo "Deploying ${{ matrix.app }}"
# Add deployment logic specific to each appturbo.json
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"]
},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"]
},
"lint": {
"outputs": []
},
"type-check": {
"dependsOn": ["^build"],
"outputs": []
},
"deploy": {
"dependsOn": ["build", "test", "lint"],
"outputs": []
}
}
}---
9. Python Application with Poetry
CI/CD for Python applications using Poetry for dependency management.
.github/workflows/python-poetry.yml
name: Python CI/CD
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
PYTHON_VERSION: '3.12'
jobs:
quality:
name: Code Quality
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Poetry
uses: snok/install-poetry@v1
with:
version: 1.7.1
virtualenvs-create: true
virtualenvs-in-project: true
- name: Load cached venv
id: cached-poetry-dependencies
uses: actions/cache@v4
with:
path: .venv
key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}
- name: Install dependencies
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
run: poetry install --no-interaction --no-root
- name: Install project
run: poetry install --no-interaction
- name: Run black
run: poetry run black --check .
- name: Run isort
run: poetry run isort --check-only .
- name: Run flake8
run: poetry run flake8 .
- name: Run mypy
run: poetry run mypy .
- name: Run pylint
run: poetry run pylint src/
test:
name: Test Python ${{ matrix.python-version }}
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12']
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: testdb
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- uses: actions/checkout@v4
- name: Setup Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install Poetry
uses: snok/install-poetry@v1
- name: Load cached venv
uses: actions/cache@v4
with:
path: .venv
key: venv-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }}
- name: Install dependencies
run: poetry install --no-interaction
- name: Run tests
env:
DATABASE_URL: postgresql://test:test@localhost:5432/testdb
run: |
poetry run pytest \
--cov=src \
--cov-report=xml \
--cov-report=html \
--junit-xml=junit.xml \
-v
- name: Upload coverage
if: matrix.python-version == '3.12'
uses: codecov/codecov-action@v4
with:
file: ./coverage.xml
flags: python-${{ matrix.python-version }}
token: ${{ secrets.CODECOV_TOKEN }}
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.python-version }}
path: junit.xml
security:
name: Security Scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Poetry
uses: snok/install-poetry@v1
- name: Install dependencies
run: poetry install --no-interaction
- name: Run safety check
run: poetry run safety check
- name: Run bandit
run: poetry run bandit -r src/
build:
name: Build Package
needs: [quality, test, security]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Poetry
uses: snok/install-poetry@v1
- name: Build package
run: poetry build
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
deploy:
name: Deploy to PyPI
needs: build
if: github.ref == 'refs/heads/main' && startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/project/myproject
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Poetry
uses: snok/install-poetry@v1
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Publish to PyPI
env:
POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_TOKEN }}
run: poetry publish---
10. Serverless Lambda Deployment
Deploy AWS Lambda functions with automated testing and deployment.
.github/workflows/serverless-lambda.yml
name: Serverless Lambda Deployment
on:
push:
branches: [main]
paths:
- 'functions/**'
- 'serverless.yml'
pull_request:
branches: [main]
jobs:
test:
name: Test Lambda Functions
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
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
- name: Run integration tests
run: npm run test:integration
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
deploy-dev:
name: Deploy to Development
needs: test
if: github.ref == 'refs/heads/develop'
runs-on: ubuntu-latest
environment:
name: dev
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- 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: Install dependencies
run: npm ci
- name: Install Serverless Framework
run: npm install -g serverless@3
- name: Deploy to dev
run: |
serverless deploy --stage dev --verbose
- name: Run smoke tests
env:
API_ENDPOINT: ${{ steps.deploy.outputs.api-endpoint }}
run: npm run test:smoke -- --env=dev
deploy-prod:
name: Deploy to Production
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment:
name: production
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- 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: Install dependencies
run: npm ci --production
- name: Install Serverless Framework
run: npm install -g serverless@3
- name: Deploy to production
id: deploy
run: |
serverless deploy --stage prod --verbose
API_ENDPOINT=$(serverless info --stage prod | grep "endpoint:" | cut -d' ' -f5)
echo "api-endpoint=$API_ENDPOINT" >> $GITHUB_OUTPUT
- name: Run smoke tests
env:
API_ENDPOINT: ${{ steps.deploy.outputs.api-endpoint }}
run: npm run test:smoke -- --env=prod
- name: Publish metrics
run: |
# Publish deployment metrics to CloudWatch
aws cloudwatch put-metric-data \
--namespace ServerlessApp \
--metric-name Deployment \
--value 1 \
--dimensions Environment=production
- name: Notify deployment
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: |
Production deployment completed
API Endpoint: ${{ steps.deploy.outputs.api-endpoint }}
webhook_url: ${{ secrets.SLACK_WEBHOOK }}---
Additional Examples (11-18)
Due to length constraints, here are condensed versions of the remaining examples. Each would follow similar comprehensive patterns as above.
11. Frontend Deploy to Vercel/Netlify
name: Frontend Deployment
on:
push:
branches: [main]
jobs:
deploy-vercel:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- 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'12. Database Migration Pipeline
name: Database Migrations
on:
push:
branches: [main]
paths:
- 'migrations/**'
jobs:
migrate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run migrations
run: npm run migrate:prod
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}13. Mobile App CI (React Native)
name: React Native CI
on: [push, pull_request]
jobs:
build-ios:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm ci
- run: cd ios && pod install
- run: xcodebuild -workspace ios/App.xcworkspace -scheme App build14. Canary Deployment with Flagger
name: Canary Deployment
jobs:
canary:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy canary
run: kubectl apply -f canary.yaml
- name: Monitor canary
run: flagger-loadtester -gate http://canary-endpoint15. Security Scanning Pipeline
name: Security Scan
on:
schedule:
- cron: '0 0 * * *'
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Trivy
uses: aquasecurity/trivy-action@master
- name: Run Snyk
uses: snyk/actions/node@master16. Performance Benchmarking
name: Performance Tests
on: [pull_request]
jobs:
benchmark:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run benchmarks
run: npm run benchmark
- name: Compare with main
run: npm run benchmark:compare17. Scheduled Maintenance Jobs
name: Scheduled Maintenance
on:
schedule:
- cron: '0 2 * * 0' # Weekly on Sunday at 2 AM
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- name: Cleanup old artifacts
run: |
# Cleanup logic
aws s3 rm s3://bucket/old/ --recursive18. Reusable Workflow Templates
# .github/workflows/reusable-deploy.yml
name: Reusable Deploy
on:
workflow_call:
inputs:
environment:
required: true
type: string
secrets:
api-key:
required: true
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- uses: actions/checkout@v4
- name: Deploy
run: ./deploy.sh ${{ inputs.environment }}---
Document Version: 1.0.0 Last Updated: October 2025 Total Examples: 18 Coverage: Node.js, Docker, Python, Serverless, Kubernetes, Monorepos, Mobile, and more
CI/CD Pipeline Patterns
Comprehensive guide to building production-ready CI/CD pipelines with GitHub Actions
Overview
This skill provides comprehensive patterns and best practices for implementing continuous integration and continuous deployment pipelines using GitHub Actions. Master workflow automation, testing strategies, deployment patterns, and release management for modern software delivery.
Quick Start
Basic CI Pipeline
Create .github/workflows/ci.yml:
name: CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linting
run: npm run lint
- name: Run tests
run: npm test
- name: Build project
run: npm run buildBasic CD Pipeline
Create .github/workflows/deploy.yml:
name: Deploy to Production
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: production
url: https://example.com
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Deploy to production
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
run: npm run deployCore Features
Workflow Triggers
Configure when your pipelines run:
- Push events: On code commits to specific branches
- Pull requests: On PR creation/updates
- Schedules: Cron-based periodic runs
- Manual triggers: workflow_dispatch for on-demand execution
- Release events: On GitHub release creation
- Workflow calls: Reusable workflow invocation
Testing Strategies
Comprehensive testing in CI:
- Unit tests: Fast, isolated component tests
- Integration tests: Multi-component interaction tests
- E2E tests: Full application workflow testing
- Performance tests: Load and benchmark testing
- Security scans: Vulnerability and dependency audits
- Code coverage: Track and enforce coverage thresholds
Deployment Patterns
Production-ready deployment strategies:
- Blue-Green: Zero-downtime deployments with instant rollback
- Canary: Gradual rollout to subset of users
- Rolling: Sequential instance updates
- Multi-environment: Staged deployments (dev → staging → production)
Build Optimization
Speed up your pipelines:
- Dependency caching: Cache npm, pip, maven, etc.
- Docker layer caching: Reuse unchanged Docker layers
- Parallel jobs: Run independent tasks simultaneously
- Matrix builds: Test across multiple configurations
- Conditional execution: Skip unnecessary steps
Common Workflows
Node.js Application
name: Node.js CI
on: [push, pull_request]
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm test
- run: npm run buildDocker Build and Push
name: Docker Build
on:
push:
branches: [main]
tags: ['v*']
jobs:
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_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
myorg/myapp:latest
myorg/myapp:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=maxPython Application
name: Python CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install -r requirements-dev.txt
- name: Run linting
run: |
flake8 .
black --check .
mypy .
- name: Run tests
run: pytest --cov=. --cov-report=xml
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
file: ./coverage.xmlSecurity Best Practices
Secret Management
Never hardcode secrets in workflows:
# ❌ Bad
- run: curl -H "Authorization: Bearer abc123" api.example.com
# ✅ Good
- run: curl -H "Authorization: Bearer $TOKEN" api.example.com
env:
TOKEN: ${{ secrets.API_TOKEN }}OIDC Authentication
Use short-lived tokens instead of long-lived credentials:
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActions
aws-region: us-east-1
- run: aws s3 sync ./dist s3://my-bucketMinimal Permissions
Restrict workflow permissions to minimum required:
permissions:
contents: read # Read code
pull-requests: write # Comment on PRs
id-token: write # Generate OIDC tokensPin Action Versions
Use commit SHAs for immutable references:
# Less secure (tag can be moved)
- uses: actions/checkout@v4
# More secure (immutable)
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1Advanced Patterns
Reusable Workflows
Create shareable workflow templates:
# .github/workflows/reusable-test.yml
name: Reusable Test Workflow
on:
workflow_call:
inputs:
node-version:
required: false
type: string
default: '20'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
- run: npm ci && npm testUsage:
jobs:
test-app:
uses: ./.github/workflows/reusable-test.yml
with:
node-version: '20'Monorepo CI/CD
Detect and build only affected packages:
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
packages: ${{ steps.filter.outputs.changes }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
frontend:
- 'packages/frontend/**'
backend:
- 'packages/backend/**'
build:
needs: detect-changes
if: needs.detect-changes.outputs.packages != '[]'
strategy:
matrix:
package: ${{ fromJson(needs.detect-changes.outputs.packages) }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm run build --workspace=${{ matrix.package }}Release Automation
Automatically version and release based on commits:
name: Release
on:
push:
branches: [main]
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- name: Semantic Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npx semantic-releaseDeployment Targets
AWS
Deploy to various AWS services:
# S3 Static Site
- name: Deploy to S3
run: aws s3 sync ./dist s3://my-bucket --delete
# ECS Service
- name: Update ECS service
run: |
aws ecs update-service \
--cluster my-cluster \
--service my-service \
--force-new-deployment
# Lambda Function
- name: Deploy Lambda
run: |
aws lambda update-function-code \
--function-name my-function \
--zip-file fileb://function.zipVercel
- 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
- name: Deploy to Netlify
uses: nwtgck/actions-netlify@v3
with:
publish-dir: './dist'
production-branch: main
github-token: ${{ secrets.GITHUB_TOKEN }}
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}Kubernetes
- name: Deploy to Kubernetes
run: |
kubectl set image deployment/myapp \
myapp=myregistry/myapp:${{ github.sha }}
kubectl rollout status deployment/myappPerformance Tips
1. Cache Dependencies
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # Automatically caches npm dependencies2. Parallel Jobs
jobs:
# These run in parallel
lint:
runs-on: ubuntu-latest
steps:
- run: npm run lint
test:
runs-on: ubuntu-latest
steps:
- run: npm test
build:
runs-on: ubuntu-latest
steps:
- run: npm run build3. Skip Redundant Runs
on:
push:
paths-ignore:
- 'docs/**'
- '**.md'
- '.github/ISSUE_TEMPLATE/**'4. Use Sparse Checkout
- uses: actions/checkout@v4
with:
sparse-checkout: |
src/
package.json
sparse-checkout-cone-mode: false5. Optimize Docker Builds
- uses: docker/build-push-action@v5
with:
context: .
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64 # Build single platform if multi-arch not neededTroubleshooting
Common Issues
Slow builds
- Enable caching for dependencies
- Use parallel jobs
- Optimize Docker layer caching
- Consider self-hosted runners
Failed deployments
- Add retry logic for transient failures
- Implement health checks before marking complete
- Use deployment protection rules
- Set appropriate timeouts
Secret access issues
- Verify secret names match exactly
- Check environment-scoped secrets
- Ensure workflow has necessary permissions
- Use OIDC instead of long-lived credentials
Workflow not triggering
- Check branch/path filters
- Verify workflow syntax is valid
- Ensure
.github/workflows/location is correct - Check if workflow is disabled
Best Practices Checklist
- [ ] Use dependency caching to speed up builds
- [ ] Run jobs in parallel when possible
- [ ] Pin action versions to SHAs for security
- [ ] Use OIDC for cloud authentication
- [ ] Implement proper secret management
- [ ] Add health checks to deployments
- [ ] Set up deployment environments with protection rules
- [ ] Configure status checks to prevent bad merges
- [ ] Use matrix builds for multi-platform testing
- [ ] Implement automatic rollback on deployment failure
- [ ] Add code coverage reporting
- [ ] Set up security scanning (dependencies, containers)
- [ ] Use reusable workflows for common patterns
- [ ] Configure notifications for failed deployments
- [ ] Document deployment process and runbooks
Resources
Official Documentation
Tools and Actions
- actions/checkout - Check out repository
- actions/setup-node - Setup Node.js
- docker/build-push-action - Build Docker images
- codecov/codecov-action - Upload coverage
Learning Resources
- GitHub Skills - Interactive tutorials
- Awesome Actions - Curated list
- GitHub Actions Toolkit - Build custom actions
Examples
See EXAMPLES.md for detailed, production-ready workflow examples including:
- Complete Node.js CI/CD pipeline
- Docker multi-stage build and deployment
- Multi-environment deployment with approvals
- Monorepo CI/CD with Turborepo
- Kubernetes blue-green deployment
- Terraform infrastructure deployment
- Semantic versioning and release automation
- And many more...
---
Version: 1.0.0 Last Updated: October 2025 Maintained By: Claude Skills Team
Related skills
How it compares
Reach for ci-cd-pipeline-patterns for end-to-end pipeline architecture rather than isolated linter or formatter configuration skills.
FAQ
What pipeline elements does ci-cd-pipeline-patterns cover?
ci-cd-pipeline-patterns addresses staged jobs, test gates, build caches, matrix strategies, artifact promotion between environments, and rollback hooks. The skill helps developers structure workflows before merging or shipping releases.
When should developers use ci-cd-pipeline-patterns?
ci-cd-pipeline-patterns fits teams designing or refactoring CI/CD workflows that need reliable gates and promotion. Use it when ad hoc pipeline YAML lacks staging, caching, matrix coverage, or rollback planning.