CI/CD Templates
Ready-to-use CI/CD configurations. Auto-discovered when deployment/pipeline work detected.
GitHub Actions
Basic CI Pipeline
yaml
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
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: Lint
run: npm run lint
- name: Type check
run: npm run typecheck
- name: Test
run: npm test -- --coverage
- name: Upload coverage
uses: codecov/codecov-action@v3
Full Pipeline (Test → Build → Deploy)
yaml
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
env:
NODE_VERSION: '20'
jobs:
# Stage 1: Test
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run typecheck
- run: npm test
# Stage 2: Build
build:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build
path: dist/
# Stage 3: Deploy to Staging
deploy-staging:
needs: build
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/download-artifact@v4
with:
name: build
path: dist/
- name: Deploy to staging
run: |
# Your deployment command
echo "Deploying to staging..."
# Stage 4: Deploy to Production
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/download-artifact@v4
with:
name: build
path: dist/
- name: Deploy to production
run: |
echo "Deploying to production..."
Python CI
yaml
# .github/workflows/python-ci.yml
name: Python CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12']
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-dev.txt
- name: Lint with ruff
run: ruff check .
- name: Type check with mypy
run: mypy src/
- name: Test with pytest
env:
DATABASE_URL: postgresql://postgres:postgres@localhost/test
run: pytest --cov=src --cov-report=xml
- name: Security scan
run: pip-audit
Docker Build & Push
yaml
# .github/workflows/docker.yml
name: Docker
on:
push:
tags: ['v*']
jobs:
build:
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: myapp/backend
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- 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
GitLab CI
yaml
# .gitlab-ci.yml
stages:
- test
- build
- deploy
variables:
NODE_VERSION: "20"
# Cache node_modules between jobs
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
test:
stage: test
image: node:${NODE_VERSION}
script:
- npm ci
- npm run lint
- npm run typecheck
- npm test -- --coverage
coverage: '/Lines\s*:\s*(\d+\.?\d*)%/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
build:
stage: build
image: node:${NODE_VERSION}
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 week
deploy_staging:
stage: deploy
environment:
name: staging
url: https://staging.example.com
script:
- echo "Deploying to staging..."
only:
- develop
deploy_production:
stage: deploy
environment:
name: production
url: https://example.com
script:
- echo "Deploying to production..."
only:
- main
when: manual
Dockerfile Best Practices
dockerfile
# Multi-stage build for smaller images
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
# Copy package files first (better caching)
COPY package*.json ./
RUN npm ci
# Copy source and build
COPY . .
RUN npm run build
# Stage 2: Production
FROM node:20-alpine AS production
WORKDIR /app
# Don't run as root
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001
# Copy only what's needed
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
USER nextjs
EXPOSE 3000
CMD ["node", "dist/index.js"]
dockerfile
# Python Dockerfile
FROM python:3.12-slim AS builder
WORKDIR /app
# Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
&& rm -rf /var/lib/apt/lists/*
# Create virtual environment
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Production stage
FROM python:3.12-slim
WORKDIR /app
# Copy virtual environment
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# Don't run as root
RUN useradd -m -u 1000 appuser
USER appuser
COPY --chown=appuser:appuser . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Docker Compose
yaml
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://postgres:postgres@db:5432/app
- REDIS_URL=redis://redis:6379
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
db:
image: postgres:15-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: app
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:
Pre-commit Hooks
yaml
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-json
- id: check-added-large-files
- id: detect-private-key
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.9
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.8.0
hooks:
- id: mypy
additional_dependencies: [types-all]
# For JavaScript/TypeScript
- repo: https://github.com/pre-commit/mirrors-eslint
rev: v8.56.0
hooks:
- id: eslint
files: \.[jt]sx?$
additional_dependencies:
- eslint
- eslint-config-prettier
Deployment Checklist
markdown
## Pre-Deployment Checklist ### Code Quality - [ ] All tests passing - [ ] Lint clean - [ ] Type check clean - [ ] Security scan clean ### Environment - [ ] Environment variables set - [ ] Secrets rotated if needed - [ ] Database migrations ready ### Monitoring - [ ] Health check endpoint - [ ] Logging configured - [ ] Alerts set up ### Rollback Plan - [ ] Previous version tagged - [ ] Rollback procedure documented - [ ] Database rollback possible ### Communication - [ ] Team notified - [ ] Changelog updated - [ ] Documentation current