Claude Code Plugins

Community-maintained marketplace

Feedback
0
0

Set up GitHub Actions CI/CD pipelines for testing, building, and deploying to Kubernetes. Use when implementing continuous integration and deployment for Phase 5. (project)

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name github-actions-cicd
description Set up GitHub Actions CI/CD pipelines for testing, building, and deploying to Kubernetes. Use when implementing continuous integration and deployment for Phase 5. (project)
allowed-tools Bash, Write, Read, Glob, Edit, Grep

GitHub Actions CI/CD Skill

Quick Start

  1. Read Phase 5 Constitution - constitution-prompt-phase-5.md
  2. Create workflows directory - .github/workflows/
  3. Set up CI pipeline - Test, lint, build on PR
  4. Set up CD pipeline - Deploy to staging/production
  5. Configure secrets - Docker registry, K8s credentials
  6. Add branch protection - Require CI to pass

Workflow Structure

.github/
└── workflows/
    ├── ci.yaml           # Test & build on PR
    ├── cd-staging.yaml   # Deploy to staging
    ├── cd-production.yaml # Deploy to production
    └── security.yaml     # Security scanning

CI Pipeline (Pull Requests)

Create .github/workflows/ci.yaml:

name: CI Pipeline

on:
  pull_request:
    branches: [main, develop]
  push:
    branches: [main, develop]

env:
  REGISTRY: ghcr.io
  IMAGE_PREFIX: ${{ github.repository }}

jobs:
  # Backend Tests
  backend-test:
    name: Backend Tests
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: ./backend

    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_USER: test
          POSTGRES_PASSWORD: test
          POSTGRES_DB: todo_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
        with:
          version: "latest"

      - name: Set up Python
        run: uv python install 3.13

      - name: Install dependencies
        run: uv sync --frozen

      - name: Run linting
        run: uv run ruff check .

      - name: Run type checking
        run: uv run mypy src/

      - name: Run tests
        env:
          DATABASE_URL: postgresql://test:test@localhost:5432/todo_test
          BETTER_AUTH_SECRET: test-secret
          GEMINI_API_KEY: test-key
        run: uv run pytest tests/ -v --cov=src --cov-report=xml

      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          files: ./backend/coverage.xml
          flags: backend

  # Frontend Tests
  frontend-test:
    name: Frontend Tests
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: ./frontend

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
          cache-dependency-path: './frontend/package-lock.json'

      - name: Install dependencies
        run: npm ci

      - name: Run linting
        run: npm run lint

      - name: Run type checking
        run: npm run type-check

      - name: Run tests
        run: npm test -- --coverage

      - name: Build
        run: npm run build
        env:
          NEXT_PUBLIC_API_URL: http://localhost:8000

  # Build Docker Images
  build-images:
    name: Build Images
    runs-on: ubuntu-latest
    needs: [backend-test, frontend-test]
    if: github.event_name == 'push'

    permissions:
      contents: read
      packages: write

    strategy:
      matrix:
        service:
          - name: backend
            context: ./backend
            dockerfile: ./backend/Dockerfile
          - name: frontend
            context: ./frontend
            dockerfile: ./frontend/Dockerfile
          - name: notification-service
            context: ./services/notification
            dockerfile: ./services/notification/Dockerfile
          - name: recurring-service
            context: ./services/recurring
            dockerfile: ./services/recurring/Dockerfile

    steps:
      - uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to GHCR
        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_PREFIX }}/${{ matrix.service.name }}
          tags: |
            type=sha,prefix=
            type=ref,event=branch
            type=semver,pattern={{version}}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: ${{ matrix.service.context }}
          file: ${{ matrix.service.dockerfile }}
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  # Helm Chart Validation
  helm-lint:
    name: Helm Lint
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Set up Helm
        uses: azure/setup-helm@v4
        with:
          version: v3.14.0

      - name: Lint charts
        run: helm lint ./helm/evolution-todo

      - name: Template validation
        run: |
          helm template evolution-todo ./helm/evolution-todo \
            --values ./helm/evolution-todo/values.yaml \
            --debug

CD Pipeline (Staging)

Create .github/workflows/cd-staging.yaml:

name: CD Staging

on:
  push:
    branches: [develop]
  workflow_dispatch:

env:
  REGISTRY: ghcr.io
  IMAGE_PREFIX: ${{ github.repository }}
  CLUSTER_NAME: todo-staging
  NAMESPACE: todo-staging

jobs:
  deploy-staging:
    name: Deploy to Staging
    runs-on: ubuntu-latest
    environment: staging

    steps:
      - uses: actions/checkout@v4

      - name: Install doctl
        uses: digitalocean/action-doctl@v2
        with:
          token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}

      - name: Save DigitalOcean kubeconfig
        run: doctl kubernetes cluster kubeconfig save ${{ env.CLUSTER_NAME }}

      - name: Set up Helm
        uses: azure/setup-helm@v4
        with:
          version: v3.14.0

      - name: Deploy with Helm
        run: |
          helm upgrade --install evolution-todo ./helm/evolution-todo \
            --namespace ${{ env.NAMESPACE }} \
            --create-namespace \
            --values ./helm/evolution-todo/values-staging.yaml \
            --set global.image.tag=${{ github.sha }} \
            --set backend.env.DATABASE_URL=${{ secrets.STAGING_DATABASE_URL }} \
            --set backend.env.GEMINI_API_KEY=${{ secrets.GEMINI_API_KEY }} \
            --set backend.env.BETTER_AUTH_SECRET=${{ secrets.BETTER_AUTH_SECRET }} \
            --wait \
            --timeout 10m

      - name: Verify deployment
        run: |
          kubectl rollout status deployment/backend -n ${{ env.NAMESPACE }}
          kubectl rollout status deployment/frontend -n ${{ env.NAMESPACE }}

      - name: Run smoke tests
        run: |
          FRONTEND_URL=$(kubectl get svc frontend -n ${{ env.NAMESPACE }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
          curl -f http://$FRONTEND_URL/health || exit 1

CD Pipeline (Production)

Create .github/workflows/cd-production.yaml:

name: CD Production

on:
  release:
    types: [published]
  workflow_dispatch:
    inputs:
      version:
        description: 'Version to deploy'
        required: true

env:
  REGISTRY: ghcr.io
  IMAGE_PREFIX: ${{ github.repository }}
  CLUSTER_NAME: todo-production
  NAMESPACE: todo-app

jobs:
  deploy-production:
    name: Deploy to Production
    runs-on: ubuntu-latest
    environment: production

    steps:
      - uses: actions/checkout@v4

      - name: Determine version
        id: version
        run: |
          if [ "${{ github.event_name }}" == "release" ]; then
            echo "version=${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT
          else
            echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
          fi

      - name: Install doctl
        uses: digitalocean/action-doctl@v2
        with:
          token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}

      - name: Save DigitalOcean kubeconfig
        run: doctl kubernetes cluster kubeconfig save ${{ env.CLUSTER_NAME }}

      - name: Set up Helm
        uses: azure/setup-helm@v4
        with:
          version: v3.14.0

      - name: Deploy with Helm
        run: |
          helm upgrade --install evolution-todo ./helm/evolution-todo \
            --namespace ${{ env.NAMESPACE }} \
            --values ./helm/evolution-todo/values-production.yaml \
            --set global.image.tag=${{ steps.version.outputs.version }} \
            --set backend.env.DATABASE_URL=${{ secrets.PROD_DATABASE_URL }} \
            --set backend.env.GEMINI_API_KEY=${{ secrets.GEMINI_API_KEY }} \
            --set backend.env.BETTER_AUTH_SECRET=${{ secrets.BETTER_AUTH_SECRET }} \
            --set kafka.brokers=${{ secrets.REDPANDA_BROKERS }} \
            --wait \
            --timeout 15m

      - name: Verify deployment
        run: |
          kubectl rollout status deployment/backend -n ${{ env.NAMESPACE }} --timeout=5m
          kubectl rollout status deployment/frontend -n ${{ env.NAMESPACE }} --timeout=5m

      - name: Notify success
        if: success()
        uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {
              "text": "Production deployment successful: ${{ steps.version.outputs.version }}"
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

      - name: Rollback on failure
        if: failure()
        run: |
          helm rollback evolution-todo -n ${{ env.NAMESPACE }}

Security Scanning

Create .github/workflows/security.yaml:

name: Security Scanning

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 0 * * 0'  # Weekly on Sunday

jobs:
  trivy-scan:
    name: Trivy Vulnerability Scan
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Run Trivy vulnerability scanner (repo)
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'
          scan-ref: '.'
          format: 'sarif'
          output: 'trivy-results.sarif'

      - name: Upload Trivy scan results
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: 'trivy-results.sarif'

  dependency-review:
    name: Dependency Review
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request'

    steps:
      - uses: actions/checkout@v4

      - name: Dependency Review
        uses: actions/dependency-review-action@v4
        with:
          fail-on-severity: high

Required Secrets

Configure in GitHub Settings → Secrets and variables → Actions:

Secret Description Used In
DIGITALOCEAN_ACCESS_TOKEN DO API token CD pipelines
STAGING_DATABASE_URL Staging Neon DB URL CD Staging
PROD_DATABASE_URL Production Neon DB URL CD Production
GEMINI_API_KEY Google AI API key All deployments
BETTER_AUTH_SECRET Auth secret All deployments
REDPANDA_BROKERS Redpanda Cloud brokers Production
SLACK_WEBHOOK Slack notifications CD Production

Branch Protection Rules

Configure in GitHub Settings → Branches → Branch protection rules:

# For main branch
- Require pull request before merging
- Require status checks to pass:
  - backend-test
  - frontend-test
  - helm-lint
- Require branches to be up to date
- Require signed commits (optional)
- Include administrators

Verification Checklist

  • .github/workflows/ directory created
  • CI pipeline runs on PRs
  • Docker images build and push to GHCR
  • Helm charts lint successfully
  • Staging deployment works
  • Production deployment works
  • All secrets configured
  • Branch protection rules set
  • Security scanning enabled

Troubleshooting

Issue Cause Solution
Tests fail Missing env vars Add to workflow env
Push denied No GHCR permission Check packages: write
Helm deploy fails Bad values Run helm template locally
K8s auth fails Bad kubeconfig Check doctl token

References