| name | ci-cd-patterns |
| description | CI/CD pipeline patterns with GitHub Actions: build/test/lint workflows, environment gates, caching, security scanning, Docker builds, and deployment automation. Includes templates for TypeScript, Python, Go, and Java. |
CI/CD Patterns Skill
Reliable software delivery requires automated pipelines that catch problems before they reach users. Every merge to main should run through the same quality gates that Claude Code runs locally.
When to Activate
- Setting up CI/CD for a new project (
/setup-ci)
- Adding a new check to an existing pipeline
- Debugging flaky or slow CI workflows
- Setting up environment-based deployment gates (staging โ production)
- Adding security scanning to the pipeline
- Configuring Docker build and push in CI
- Setting up breaking change detection for APIs
Pipeline Architecture
@startuml
start
fork
:Lint\n(fast, < 1min);
fork again
:Type Check\n(fast, < 2min);
fork again
:Unit Tests\n(parallel shards);
end fork
:Integration Tests\n(needs services: DB, Redis);
if (Branch = main?) then (yes)
:Build Docker Image\npush to registry;
:Deploy โ Staging;
:Smoke Tests on Staging;
if (Manual approval?) then (yes)
:Deploy โ Production;
:Notify team;
endif
else (PR / feature branch)
:Report results\nto PR;
stop
endif
stop
@enduml
Core Workflow: PR Checks
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '24'
cache: 'npm'
- run: npm ci
- run: npm run lint
typecheck:
name: Type Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '24', cache: 'npm' }
- run: npm ci
- run: npm run typecheck
test:
name: Test
runs-on: ubuntu-latest
services:
postgres:
image: postgres:18-alpine
env:
POSTGRES_DB: testdb
POSTGRES_USER: test
POSTGRES_PASSWORD: test
ports: ['5432:5432']
options: --health-cmd pg_isready --health-interval 5s --health-timeout 5s --health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '24', cache: 'npm' }
- run: npm ci
- run: npm test -- --coverage
env:
DATABASE_URL: postgresql://test:test@localhost:5432/testdb
- uses: codecov/codecov-action@v4
API Breaking Change Detection
Add this job to every PR that touches api/v1/openapi.yaml:
api-breaking-changes:
name: API Breaking Change Detection
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- name: Install oasdiff
run: |
curl -fsSL https://raw.githubusercontent.com/tufin/oasdiff/main/install.sh | sh
- name: Check for breaking changes
run: |
oasdiff breaking \
https://raw.githubusercontent.com/${{ github.repository }}/main/api/v1/openapi.yaml \
api/v1/openapi.yaml \
--fail-on ERR
Docker Build & Push
docker:
name: Build & Push Docker Image
runs-on: ubuntu-latest
needs: [lint, typecheck, test]
if: github.ref == 'refs/heads/main'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v5
with:
push: true
tags: |
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:${{ github.sha }}
cache-from: type=gha
Environment Gates: Staging โ Production
name: Deploy
on:
workflow_run:
workflows: [CI]
branches: [main]
types: [completed]
jobs:
deploy-staging:
if: ${{ github.event.workflow_run.conclusion == 'success' }}
environment: staging
runs-on: ubuntu-latest
steps:
- run: echo "Deploying to staging..."
smoke-test:
needs: deploy-staging
runs-on: ubuntu-latest
steps:
- run: |
curl --fail https://staging.yourapp.com/health/ready || exit 1
deploy-production:
needs: smoke-test
environment:
name: production
url: https://yourapp.com
runs-on: ubuntu-latest
steps:
- run:
Set up environment protection rules in GitHub โ Settings โ Environments:
staging: no protection (auto-deploy)
production: require 1 reviewer approval
Security Scanning
security:
name: Security Scan
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- uses: actions/checkout@v4
- name: Run npm audit
run: npm audit --audit-level=high
continue-on-error: true
- uses: github/codeql-action/init@v3
with:
languages: javascript
- uses: github/codeql-action/analyze@v3
- uses: trufflesecurity/trufflehog@main
with:
path: ./
base: ${{ github.event.repository.default_branch }}
head: HEAD
Language-Specific Templates
Python (uv + pytest)
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
with: { enable-cache: true }
- run: uv sync --frozen
- run: uv run ruff check .
- run: uv run mypy .
- run: uv run pytest --cov --cov-report=xml
Go
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with: { go-version-file: go.mod, cache: true }
- run: go vet ./...
- run: go test ./... -race -coverprofile=coverage.out
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
Java (Maven)
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with: { java-version: '25', distribution: 'temurin', cache: 'maven' }
- run: mvn verify
Caching Strategy
| Language | Cache Key | What to Cache |
|---|
| Node.js | package-lock.json hash | ~/.npm |
| Python (uv) | uv.lock hash | ~/.cache/uv |
| Go | go.sum hash | ~/.cache/go-build, ~/go/pkg/mod |
| Java (Maven) | pom.xml hash | ~/.m2 |
| Docker | Dockerfile + source hash | GitHub Actions cache (type=gha) |
Always restore cache before install, save after. Use actions/cache@v4 or the setup actions' built-in caching.
Secrets Management in CI
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
API_KEY: ${{ secrets.STAGING_API_KEY }}
API_KEY: ${{ secrets.PRODUCTION_API_KEY }}
Rules:
- Production secrets only accessible from the
production environment (with required reviewers)
- Never print secrets to logs (
echo $SECRET โ shows in logs)
- Rotate secrets on every PR merge to main if a team member leaves
- Use OIDC for cloud provider auth instead of long-lived credentials
CI/CD Checklist
Before going live: