Skip to content
Back to skills

Github Actions Cicd

BSecurity

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)

  • 666 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added February 8, 2026
devopspythongobashsqlnodedockerkubernetesazuretestinggit

Works with

  • api

Security analysis

B84/100
  • mediumUses curl or wget to download content
  • criticalSends environment variables or credentials to an external URL

Pro shows the line behind each finding and how to fix it

Scanned February 12, 2026

npx -y skills add majiayu000/claude-skill-registry --skill github-actions-cicd --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Github Actions Cicd?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Github Actions Cicd
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-github-actions-cicd/badge)](https://www.skillsdirectory.com/skills/majiayu000-github-actions-cicd)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
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`:

```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`:

```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`:

```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`:

```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:

```yaml
# 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

- [GitHub Actions Documentation](https://docs.github.com/en/actions)
- [Docker Build Push Action](https://github.com/docker/build-push-action)
- [Helm GitHub Actions](https://github.com/azure/setup-helm)
- [DigitalOcean Action](https://github.com/digitalocean/action-doctl)
- [Phase 5 Constitution](../../../constitution-prompt-phase-5.md)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.