Files
James Bland befb8fbaeb feat: initial Claude Code configuration scaffold
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>
2026-01-20 15:47:34 -05:00

15 KiB

name, description
name description
cicd-pipelines 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

// 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

// 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

# .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

# .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

# .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

# Minimum checks before merge
- Lint passes
- Type check passes
- Unit tests pass
- Coverage threshold met (80%+)
- Security scan passes
- No secrets detected

Deployment Strategies

# 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