Comprehensive Claude Code guidance system with: - 5 agents: tdd-guardian, code-reviewer, security-scanner, refactor-scan, dependency-audit - 18 skills covering languages (Python, TypeScript, Rust, Go, Java, C#), infrastructure (AWS, Azure, GCP, Terraform, Ansible, Docker/K8s, Database, CI/CD), testing (TDD, UI, Browser), and patterns (Monorepo, API Design, Observability) - 3 hooks: secret detection, auto-formatting, TDD git pre-commit - Strict TDD enforcement with 80%+ coverage requirements - Multi-model strategy: Opus for planning, Sonnet for execution (opusplan) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
600 lines
15 KiB
Markdown
600 lines
15 KiB
Markdown
---
|
|
name: cicd-pipelines
|
|
description: CI/CD pipeline patterns for Jenkins, GitHub Actions, and GitLab CI. Use when setting up continuous integration or deployment pipelines.
|
|
---
|
|
|
|
# CI/CD Pipelines Skill
|
|
|
|
## Jenkins
|
|
|
|
### Declarative Pipeline
|
|
```groovy
|
|
// Jenkinsfile
|
|
pipeline {
|
|
agent any
|
|
|
|
environment {
|
|
REGISTRY = 'myregistry.azurecr.io'
|
|
IMAGE_NAME = 'myapp'
|
|
COVERAGE_THRESHOLD = '80'
|
|
}
|
|
|
|
options {
|
|
timeout(time: 30, unit: 'MINUTES')
|
|
disableConcurrentBuilds()
|
|
buildDiscarder(logRotator(numToKeepStr: '10'))
|
|
}
|
|
|
|
stages {
|
|
stage('Checkout') {
|
|
steps {
|
|
checkout scm
|
|
}
|
|
}
|
|
|
|
stage('Install Dependencies') {
|
|
parallel {
|
|
stage('Python') {
|
|
when {
|
|
changeset "apps/backend/**"
|
|
}
|
|
steps {
|
|
sh 'uv sync'
|
|
}
|
|
}
|
|
stage('Node') {
|
|
when {
|
|
changeset "apps/frontend/**"
|
|
}
|
|
steps {
|
|
sh 'npm ci'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Lint & Type Check') {
|
|
parallel {
|
|
stage('Python Lint') {
|
|
when {
|
|
changeset "apps/backend/**"
|
|
}
|
|
steps {
|
|
sh 'uv run ruff check apps/backend/'
|
|
sh 'uv run mypy apps/backend/'
|
|
}
|
|
}
|
|
stage('TypeScript Lint') {
|
|
when {
|
|
changeset "apps/frontend/**"
|
|
}
|
|
steps {
|
|
sh 'npm run lint --workspace=frontend'
|
|
sh 'npm run typecheck --workspace=frontend'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Test') {
|
|
parallel {
|
|
stage('Backend Tests') {
|
|
when {
|
|
changeset "apps/backend/**"
|
|
}
|
|
steps {
|
|
sh """
|
|
uv run pytest apps/backend/ \
|
|
--cov=apps/backend/src \
|
|
--cov-report=xml \
|
|
--cov-fail-under=${COVERAGE_THRESHOLD} \
|
|
--junitxml=test-results/backend.xml
|
|
"""
|
|
}
|
|
post {
|
|
always {
|
|
junit 'test-results/backend.xml'
|
|
publishCoverage adapters: [coberturaAdapter('coverage.xml')]
|
|
}
|
|
}
|
|
}
|
|
stage('Frontend Tests') {
|
|
when {
|
|
changeset "apps/frontend/**"
|
|
}
|
|
steps {
|
|
sh """
|
|
npm run test --workspace=frontend -- \
|
|
--coverage \
|
|
--coverageThreshold='{"global":{"branches":${COVERAGE_THRESHOLD},"functions":${COVERAGE_THRESHOLD},"lines":${COVERAGE_THRESHOLD}}}' \
|
|
--reporter=junit \
|
|
--outputFile=test-results/frontend.xml
|
|
"""
|
|
}
|
|
post {
|
|
always {
|
|
junit 'test-results/frontend.xml'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Security Scan') {
|
|
steps {
|
|
sh 'trivy fs --severity HIGH,CRITICAL --exit-code 1 .'
|
|
}
|
|
}
|
|
|
|
stage('Build') {
|
|
when {
|
|
anyOf {
|
|
branch 'main'
|
|
branch 'release/*'
|
|
}
|
|
}
|
|
steps {
|
|
script {
|
|
def version = sh(script: 'git describe --tags --always', returnStdout: true).trim()
|
|
sh """
|
|
docker build -t ${REGISTRY}/${IMAGE_NAME}:${version} .
|
|
docker tag ${REGISTRY}/${IMAGE_NAME}:${version} ${REGISTRY}/${IMAGE_NAME}:latest
|
|
"""
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Push') {
|
|
when {
|
|
branch 'main'
|
|
}
|
|
steps {
|
|
withCredentials([usernamePassword(
|
|
credentialsId: 'registry-credentials',
|
|
usernameVariable: 'REGISTRY_USER',
|
|
passwordVariable: 'REGISTRY_PASS'
|
|
)]) {
|
|
sh """
|
|
echo \$REGISTRY_PASS | docker login ${REGISTRY} -u \$REGISTRY_USER --password-stdin
|
|
docker push ${REGISTRY}/${IMAGE_NAME}:${version}
|
|
docker push ${REGISTRY}/${IMAGE_NAME}:latest
|
|
"""
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Deploy to Staging') {
|
|
when {
|
|
branch 'main'
|
|
}
|
|
steps {
|
|
sh 'kubectl apply -f k8s/staging/'
|
|
sh 'kubectl rollout status deployment/myapp -n staging'
|
|
}
|
|
}
|
|
|
|
stage('Deploy to Production') {
|
|
when {
|
|
branch 'release/*'
|
|
}
|
|
input {
|
|
message "Deploy to production?"
|
|
ok "Deploy"
|
|
}
|
|
steps {
|
|
sh 'kubectl apply -f k8s/production/'
|
|
sh 'kubectl rollout status deployment/myapp -n production'
|
|
}
|
|
}
|
|
}
|
|
|
|
post {
|
|
always {
|
|
cleanWs()
|
|
}
|
|
success {
|
|
slackSend(
|
|
channel: '#deployments',
|
|
color: 'good',
|
|
message: "Build ${env.BUILD_NUMBER} succeeded: ${env.BUILD_URL}"
|
|
)
|
|
}
|
|
failure {
|
|
slackSend(
|
|
channel: '#deployments',
|
|
color: 'danger',
|
|
message: "Build ${env.BUILD_NUMBER} failed: ${env.BUILD_URL}"
|
|
)
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Shared Library
|
|
```groovy
|
|
// vars/pythonPipeline.groovy
|
|
def call(Map config = [:]) {
|
|
pipeline {
|
|
agent any
|
|
|
|
stages {
|
|
stage('Test') {
|
|
steps {
|
|
sh "uv run pytest ${config.testPath ?: 'tests/'} --cov --cov-fail-under=${config.coverage ?: 80}"
|
|
}
|
|
}
|
|
stage('Lint') {
|
|
steps {
|
|
sh "uv run ruff check ${config.srcPath ?: 'src/'}"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Usage in Jenkinsfile
|
|
@Library('my-shared-library') _
|
|
|
|
pythonPipeline(
|
|
testPath: 'apps/backend/tests/',
|
|
srcPath: 'apps/backend/src/',
|
|
coverage: 85
|
|
)
|
|
```
|
|
|
|
## GitHub Actions
|
|
|
|
### Complete Workflow
|
|
```yaml
|
|
# .github/workflows/ci.yml
|
|
name: CI/CD
|
|
|
|
on:
|
|
push:
|
|
branches: [main, 'release/*']
|
|
pull_request:
|
|
branches: [main]
|
|
|
|
env:
|
|
REGISTRY: ghcr.io
|
|
IMAGE_NAME: ${{ github.repository }}
|
|
|
|
jobs:
|
|
detect-changes:
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
backend: ${{ steps.changes.outputs.backend }}
|
|
frontend: ${{ steps.changes.outputs.frontend }}
|
|
infrastructure: ${{ steps.changes.outputs.infrastructure }}
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: dorny/paths-filter@v3
|
|
id: changes
|
|
with:
|
|
filters: |
|
|
backend:
|
|
- 'apps/backend/**'
|
|
- 'packages/shared/**'
|
|
frontend:
|
|
- 'apps/frontend/**'
|
|
- 'packages/shared/**'
|
|
infrastructure:
|
|
- 'infrastructure/**'
|
|
|
|
backend-test:
|
|
needs: detect-changes
|
|
if: needs.detect-changes.outputs.backend == 'true'
|
|
runs-on: ubuntu-latest
|
|
services:
|
|
postgres:
|
|
image: postgres:16
|
|
env:
|
|
POSTGRES_USER: test
|
|
POSTGRES_PASSWORD: test
|
|
POSTGRES_DB: test
|
|
ports:
|
|
- 5432:5432
|
|
options: >-
|
|
--health-cmd pg_isready
|
|
--health-interval 10s
|
|
--health-timeout 5s
|
|
--health-retries 5
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Install uv
|
|
uses: astral-sh/setup-uv@v4
|
|
|
|
- name: Install dependencies
|
|
run: uv sync
|
|
|
|
- name: Lint
|
|
run: |
|
|
uv run ruff check apps/backend/
|
|
uv run mypy apps/backend/
|
|
|
|
- name: Test
|
|
env:
|
|
DATABASE_URL: postgresql://test:test@localhost:5432/test
|
|
run: |
|
|
uv run pytest apps/backend/ \
|
|
--cov=apps/backend/src \
|
|
--cov-report=xml \
|
|
--cov-fail-under=80
|
|
|
|
- name: Upload coverage
|
|
uses: codecov/codecov-action@v4
|
|
with:
|
|
files: coverage.xml
|
|
flags: backend
|
|
|
|
frontend-test:
|
|
needs: detect-changes
|
|
if: needs.detect-changes.outputs.frontend == 'true'
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: '22'
|
|
cache: 'npm'
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- name: Lint & Type Check
|
|
run: |
|
|
npm run lint --workspace=frontend
|
|
npm run typecheck --workspace=frontend
|
|
|
|
- name: Test
|
|
run: npm run test --workspace=frontend -- --coverage
|
|
|
|
- name: Upload coverage
|
|
uses: codecov/codecov-action@v4
|
|
with:
|
|
files: apps/frontend/coverage/lcov.info
|
|
flags: frontend
|
|
|
|
security-scan:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Run Trivy vulnerability scanner
|
|
uses: aquasecurity/trivy-action@master
|
|
with:
|
|
scan-type: 'fs'
|
|
severity: 'CRITICAL,HIGH'
|
|
exit-code: '1'
|
|
|
|
- name: Run Gitleaks
|
|
uses: gitleaks/gitleaks-action@v2
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
build-and-push:
|
|
needs: [backend-test, frontend-test, security-scan]
|
|
if: github.ref == 'refs/heads/main'
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Log in to Container registry
|
|
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=sha
|
|
type=ref,event=branch
|
|
|
|
- name: Build and push
|
|
uses: docker/build-push-action@v5
|
|
with:
|
|
context: .
|
|
push: true
|
|
tags: ${{ steps.meta.outputs.tags }}
|
|
labels: ${{ steps.meta.outputs.labels }}
|
|
cache-from: type=gha
|
|
cache-to: type=gha,mode=max
|
|
|
|
deploy-staging:
|
|
needs: build-and-push
|
|
runs-on: ubuntu-latest
|
|
environment: staging
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Deploy to staging
|
|
run: |
|
|
kubectl apply -f k8s/staging/
|
|
kubectl rollout status deployment/myapp -n staging
|
|
|
|
deploy-production:
|
|
needs: deploy-staging
|
|
runs-on: ubuntu-latest
|
|
environment: production
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Deploy to production
|
|
run: |
|
|
kubectl apply -f k8s/production/
|
|
kubectl rollout status deployment/myapp -n production
|
|
```
|
|
|
|
### Reusable Workflow
|
|
```yaml
|
|
# .github/workflows/python-ci.yml
|
|
name: Python CI
|
|
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
python-version:
|
|
required: false
|
|
type: string
|
|
default: '3.12'
|
|
working-directory:
|
|
required: true
|
|
type: string
|
|
coverage-threshold:
|
|
required: false
|
|
type: number
|
|
default: 80
|
|
|
|
jobs:
|
|
test:
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
working-directory: ${{ inputs.working-directory }}
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: astral-sh/setup-uv@v4
|
|
|
|
- run: uv sync
|
|
|
|
- run: uv run ruff check .
|
|
|
|
- run: uv run pytest --cov --cov-fail-under=${{ inputs.coverage-threshold }}
|
|
```
|
|
|
|
## GitLab CI
|
|
|
|
```yaml
|
|
# .gitlab-ci.yml
|
|
stages:
|
|
- test
|
|
- build
|
|
- deploy
|
|
|
|
variables:
|
|
REGISTRY: registry.gitlab.com
|
|
IMAGE_NAME: $CI_PROJECT_PATH
|
|
|
|
.python-base:
|
|
image: python:3.12
|
|
before_script:
|
|
- pip install uv
|
|
- uv sync
|
|
|
|
.node-base:
|
|
image: node:22
|
|
before_script:
|
|
- npm ci
|
|
|
|
test:backend:
|
|
extends: .python-base
|
|
stage: test
|
|
script:
|
|
- uv run ruff check apps/backend/
|
|
- uv run pytest apps/backend/ --cov --cov-fail-under=80
|
|
rules:
|
|
- changes:
|
|
- apps/backend/**
|
|
|
|
test:frontend:
|
|
extends: .node-base
|
|
stage: test
|
|
script:
|
|
- npm run lint --workspace=frontend
|
|
- npm run test --workspace=frontend -- --coverage
|
|
rules:
|
|
- changes:
|
|
- apps/frontend/**
|
|
|
|
build:
|
|
stage: build
|
|
image: docker:24
|
|
services:
|
|
- docker:24-dind
|
|
script:
|
|
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
|
- docker build -t $REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHA .
|
|
- docker push $REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHA
|
|
rules:
|
|
- if: $CI_COMMIT_BRANCH == "main"
|
|
|
|
deploy:staging:
|
|
stage: deploy
|
|
script:
|
|
- kubectl apply -f k8s/staging/
|
|
environment:
|
|
name: staging
|
|
rules:
|
|
- if: $CI_COMMIT_BRANCH == "main"
|
|
|
|
deploy:production:
|
|
stage: deploy
|
|
script:
|
|
- kubectl apply -f k8s/production/
|
|
environment:
|
|
name: production
|
|
rules:
|
|
- if: $CI_COMMIT_BRANCH == "main"
|
|
when: manual
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### Pipeline Design Principles
|
|
|
|
1. **Fail Fast** - Run quick checks (lint, type check) before slow ones (tests)
|
|
2. **Parallelize** - Run independent jobs concurrently
|
|
3. **Cache** - Cache dependencies between runs
|
|
4. **Change Detection** - Only run what's affected
|
|
5. **Immutable Artifacts** - Tag images with commit SHA
|
|
6. **Environment Parity** - Same process for all environments
|
|
7. **Secrets Management** - Never hardcode, use CI/CD secrets
|
|
|
|
### Quality Gates
|
|
|
|
```yaml
|
|
# Minimum checks before merge
|
|
- Lint passes
|
|
- Type check passes
|
|
- Unit tests pass
|
|
- Coverage threshold met (80%+)
|
|
- Security scan passes
|
|
- No secrets detected
|
|
```
|
|
|
|
### Deployment Strategies
|
|
|
|
```yaml
|
|
# Rolling update (default)
|
|
strategy:
|
|
type: RollingUpdate
|
|
rollingUpdate:
|
|
maxSurge: 1
|
|
maxUnavailable: 0
|
|
|
|
# Blue-green (via service switch)
|
|
# Deploy new version alongside old
|
|
# Switch service selector when ready
|
|
|
|
# Canary (gradual rollout)
|
|
# Route percentage of traffic to new version
|
|
# Monitor metrics before full rollout
|
|
```
|