GitHub Actions CI/CD Templates
When to Use This Skill
- Setting up CI/CD pipelines with GitHub Actions
- Automating testing and deployment workflows
- Implementing continuous integration best practices
- Creating reusable workflow templates
- Optimizing build and deployment processes
Target Agents
devops-engineer- Primary user for CI/CD setupbackend-developer- Backend testing and deploymentfrontend-developer- Frontend build and deploymentchief-operations-orchestrator- Overall workflow coordination
Core Workflow Templates
1. Node.js CI/CD Pipeline
name: Node.js CI/CD
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x, 18.x, 20.x]
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run tests
run: npm test -- --coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/lcov.info
fail_ci_if_error: true
build:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
env:
CI: true
NODE_ENV: production
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: build
path: dist/
deploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Download build artifacts
uses: actions/download-artifact@v3
with:
name: build
path: dist/
- name: Deploy to Production
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-1
- name: Sync to S3
run: |
aws s3 sync dist/ s3://${{ secrets.S3_BUCKET }} --delete
aws cloudfront create-invalidation --distribution-id ${{ secrets.CLOUDFRONT_ID }} --paths "/*"
2. Python Django CI/CD Pipeline
name: Django CI/CD
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
env:
PYTHON_VERSION: '3.11'
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: test_db
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_USER: ${{ env.POSTGRES_USER }}
POSTGRES_PASSWORD: ${{ env.POSTGRES_PASSWORD }}
POSTGRES_DB: ${{ env.POSTGRES_DB }}
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements/dev.txt
- name: Run migrations
run: python manage.py migrate
env:
DATABASE_URL: postgresql://${{ env.POSTGRES_USER }}:${{ env.POSTGRES_PASSWORD }}@localhost:5432/${{ env.POSTGRES_DB }}
REDIS_URL: redis://localhost:6379/0
- name: Run linter
run: |
flake8 .
black --check .
isort --check-only .
- name: Run tests
run: |
pytest --cov=. --cov-report=xml --cov-report=html
env:
DATABASE_URL: postgresql://${{ env.POSTGRES_USER }}:${{ env.POSTGRES_PASSWORD }}@localhost:5432/${{ env.POSTGRES_DB }}
REDIS_URL: redis://localhost:6379/0
SECRET_KEY: test-secret-key-for-ci
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage.xml
fail_ci_if_error: true
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Deploy to Heroku
uses: akhileshns/heroku-deploy@v3.12.14
with:
heroku_api_key: ${{ secrets.HEROKU_API_KEY }}
heroku_app_name: ${{ secrets.HEROKU_APP_NAME }}
heroku_email: ${{ secrets.HEROKU_EMAIL }}
- name: Run database migrations on Heroku
run: |
heroku run python manage.py migrate -a ${{ secrets.HEROKU_APP_NAME }}
env:
HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
3. Docker Build and Push
name: Docker Build and Push
on:
push:
branches: [ main ]
tags: [ 'v*' ]
pull_request:
branches: [ main ]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
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
platforms: linux/amd64,linux/arm64
- name: Scan image for vulnerabilities
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }}
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy results to GitHub Security
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'
4. Kubernetes Deployment
name: Deploy to Kubernetes
on:
push:
branches: [ main ]
env:
CLUSTER_NAME: production-cluster
REGION: us-east-1
NAMESPACE: production
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- 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: ${{ env.REGION }}
- name: Update kubeconfig
run: |
aws eks update-kubeconfig \
--name ${{ env.CLUSTER_NAME }} \
--region ${{ env.REGION }}
- name: Deploy to Kubernetes
run: |
# Update deployment image
kubectl set image deployment/app \
app=${{ secrets.DOCKER_REGISTRY }}/app:${{ github.sha }} \
-n ${{ env.NAMESPACE }}
# Apply configurations
kubectl apply -f k8s/ -n ${{ env.NAMESPACE }}
# Wait for rollout
kubectl rollout status deployment/app -n ${{ env.NAMESPACE }}
- name: Run smoke tests
run: |
kubectl run smoke-test \
--image=curlimages/curl \
--restart=Never \
--rm \
-i \
-n ${{ env.NAMESPACE }} \
-- curl -f http://app-service/health
- name: Notify deployment
if: always()
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 'Deployment to production: ${{ job.status }}'
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
5. Security Scanning Workflow
name: Security Scanning
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 0 * * 0' # Weekly on Sunday
jobs:
dependency-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Snyk to check for vulnerabilities
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
code-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: javascript, python
- name: Autobuild
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
secrets-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for secret scanning
- name: TruffleHog OSS
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: ${{ github.event.repository.default_branch }}
head: HEAD
Best Practices
1. Caching Dependencies
- name: Cache node modules
uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Cache pip packages
uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
2. Matrix Testing
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.9', '3.10', '3.11']
node-version: [16.x, 18.x, 20.x]
fail-fast: false # Continue other jobs if one fails
3. Conditional Execution
- name: Deploy to staging
if: github.ref == 'refs/heads/develop'
run: ./deploy-staging.sh
- name: Deploy to production
if: startsWith(github.ref, 'refs/tags/v')
run: ./deploy-production.sh
- name: Notify on failure
if: failure()
run: ./notify-failure.sh
4. Reusable Workflows
# .github/workflows/reusable-test.yml
name: Reusable Test Workflow
on:
workflow_call:
inputs:
node-version:
required: true
type: string
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
- run: npm test
# .github/workflows/main.yml
jobs:
test:
uses: ./.github/workflows/reusable-test.yml
with:
node-version: '20.x'
Common Patterns
Environment Variables and Secrets
env:
# Repository-level environment variables
NODE_ENV: production
API_URL: https://api.example.com
jobs:
deploy:
steps:
- name: Deploy
env:
# Job-level secrets
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: ./deploy.sh
Artifact Management
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: build-output
path: dist/
retention-days: 7
- name: Download artifact
uses: actions/download-artifact@v3
with:
name: build-output
path: dist/
Notification Integration
- name: Slack Notification
uses: 8398a7/action-slack@v3
if: always()
with:
status: ${{ job.status }}
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
- name: Send Email
uses: dawidd6/action-send-mail@v3
if: failure()
with:
server_address: smtp.gmail.com
server_port: 465
username: ${{ secrets.EMAIL_USERNAME }}
password: ${{ secrets.EMAIL_PASSWORD }}
subject: Build Failed - ${{ github.repository }}
body: Build failed for ${{ github.ref }}
to: team@example.com
Additional Resources
For more advanced workflows, see:
- kubernetes-deployment.md - Advanced Kubernetes deployment strategies
- terraform-automation.md - Infrastructure as Code automation
- monitoring-integration.md - Monitoring and alerting setup