Skip to content

DevOps Specialist Agent

Specialization: GitHub Actions CI/CD pipelines, container builds, automated testing, and deployment automation.

Foundation: This agent extends ../context/LLM-BaselineBehaviors.md and ../context/copilot-instructions.md. All baseline behaviors apply.


Core Expertise

GitHub Actions

  • Workflow syntax and structure
  • Event triggers (push, pull_request, schedule, workflow_dispatch)
  • Job dependencies and parallelization
  • Matrix builds for multi-platform testing
  • Reusable workflows and composite actions
  • GitHub-hosted and self-hosted runners
  • Secrets and environment management
  • Artifacts and caching strategies

CI/CD Pipelines

  • Continuous Integration (build, test, lint)
  • Continuous Deployment (staging, production)
  • Branch-based deployment strategies
  • Environment-specific configurations
  • Rollback procedures
  • Blue-green deployments
  • Canary releases

Container Builds

  • Docker multi-stage builds
  • Image optimization and layer caching
  • Container registry management (GHCR, GCR, Docker Hub)
  • Image scanning for vulnerabilities
  • Tag strategies (latest, semver, commit SHA)

Testing Automation

  • Unit test execution
  • Integration test pipelines
  • End-to-end test automation
  • Code coverage reporting
  • Test result publishing
  • Parallel test execution

Deployment Automation

  • Cloud Run deployments
  • Database migration automation
  • Environment configuration
  • Health checks and smoke tests
  • Deployment notifications
  • Automated rollback on failure

Security & Quality

  • Dependency scanning (Dependabot)
  • Container vulnerability scanning
  • SAST (Static Application Security Testing)
  • Secret scanning
  • Code quality checks (linting, formatting)
  • License compliance

GitHub Actions Workflows for This Project

Main CI/CD Pipeline

.github/workflows/ci-cd.yml:

name: CI/CD Pipeline

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

env:
  GCP_PROJECT_ID: ${{ secrets.GCP_PROJECT_ID }}
  GCP_REGION: us-central1
  REGISTRY: gcr.io

jobs:
  # Backend API Build and Test
  api-build-test:
    name: API - Build & Test
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: src/api

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.0.x'

      - name: Cache NuGet packages
        uses: actions/cache@v4
        with:
          path: ~/.nuget/packages
          key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
          restore-keys: |
            ${{ runner.os }}-nuget-

      - name: Restore dependencies
        run: dotnet restore

      - name: Build
        run: dotnet build --no-restore --configuration Release

      - name: Run tests
        run: dotnet test --no-build --configuration Release --verbosity normal --collect:"XPlat Code Coverage"

      - name: Upload coverage reports
        uses: codecov/codecov-action@v4
        with:
          directory: ./src/api/TestResults
          flags: api

      - name: Publish artifacts
        if: github.event_name != 'pull_request'
        run: dotnet publish --no-build --configuration Release --output ./publish

      - name: Upload publish artifacts
        if: github.event_name != 'pull_request'
        uses: actions/upload-artifact@v4
        with:
          name: api-publish
          path: src/api/publish
          retention-days: 1

  # Frontend Build and Test
  webapp-build-test:
    name: Frontend - Build & Test
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: src/webapp

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

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

      - name: Install dependencies
        run: npm ci

      - name: Lint
        run: npm run lint

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

      - name: Upload coverage reports
        uses: codecov/codecov-action@v4
        with:
          directory: ./src/webapp/coverage
          flags: frontend

      - name: Build
        if: github.event_name != 'pull_request'
        run: npm run build
        env:
          VITE_API_URL: ${{ secrets.VITE_API_URL }}
          VITE_FIREBASE_API_KEY: ${{ secrets.VITE_FIREBASE_API_KEY }}
          VITE_FIREBASE_AUTH_DOMAIN: ${{ secrets.VITE_FIREBASE_AUTH_DOMAIN }}
          VITE_FIREBASE_PROJECT_ID: ${{ secrets.VITE_FIREBASE_PROJECT_ID }}

      - name: Upload build artifacts
        if: github.event_name != 'pull_request'
        uses: actions/upload-artifact@v4
        with:
          name: webapp-build
          path: src/webapp/dist
          retention-days: 1

  # Build and Push API Container
  api-container:
    name: API - Build Container
    runs-on: ubuntu-latest
    needs: api-build-test
    if: github.event_name != 'pull_request'

    permissions:
      contents: read
      id-token: write

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

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

      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          credentials_json: ${{ secrets.GCP_SA_KEY }}

      - name: Configure Docker for GCR
        run: gcloud auth configure-docker ${{ env.REGISTRY }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.GCP_PROJECT_ID }}/hcp-api
          tags: |
            type=ref,event=branch
            type=sha,prefix={{branch}}-
            type=raw,value=latest,enable={{is_default_branch}}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          file: ./src/api/Dockerfile
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
          build-args: |
            BUILD_CONFIGURATION=Release

  # Build and Push Frontend Container
  webapp-container:
    name: Frontend - Build Container
    runs-on: ubuntu-latest
    needs: webapp-build-test
    if: github.event_name != 'pull_request'

    permissions:
      contents: read
      id-token: write

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

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

      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          credentials_json: ${{ secrets.GCP_SA_KEY }}

      - name: Configure Docker for GCR
        run: gcloud auth configure-docker ${{ env.REGISTRY }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.GCP_PROJECT_ID }}/hcp-webapp
          tags: |
            type=ref,event=branch
            type=sha,prefix={{branch}}-
            type=raw,value=latest,enable={{is_default_branch}}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: ./src/webapp
          file: ./src/webapp/Dockerfile
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
          build-args: |
            VITE_API_URL=${{ secrets.VITE_API_URL }}
            VITE_FIREBASE_API_KEY=${{ secrets.VITE_FIREBASE_API_KEY }}

  # Deploy to Staging
  deploy-staging:
    name: Deploy to Staging
    runs-on: ubuntu-latest
    needs: [api-container, webapp-container]
    if: github.ref == 'refs/heads/staging'
    environment:
      name: staging
      url: https://staging.happycamperplanner.com

    permissions:
      contents: read
      id-token: write

    steps:
      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          credentials_json: ${{ secrets.GCP_SA_KEY }}

      - name: Set up Cloud SDK
        uses: google-github-actions/setup-gcloud@v2

      - name: Deploy API to Cloud Run
        run: |
          gcloud run deploy hcp-api-staging \
            --image=${{ env.REGISTRY }}/${{ env.GCP_PROJECT_ID }}/hcp-api:staging-${{ github.sha }} \
            --region=${{ env.GCP_REGION }} \
            --platform=managed \
            --allow-unauthenticated \
            --set-env-vars=ASPNETCORE_ENVIRONMENT=Staging \
            --set-secrets=DATABASE_CONNECTION_STRING=database-connection-staging:latest \
            --min-instances=0 \
            --max-instances=5 \
            --cpu=1 \
            --memory=512Mi \
            --timeout=60s \
            --vpc-connector=hcp-vpc-connector-staging

      - name: Deploy Frontend to Cloud Run
        run: |
          gcloud run deploy hcp-webapp-staging \
            --image=${{ env.REGISTRY }}/${{ env.GCP_PROJECT_ID }}/hcp-webapp:staging-${{ github.sha }} \
            --region=${{ env.GCP_REGION }} \
            --platform=managed \
            --allow-unauthenticated \
            --min-instances=0 \
            --max-instances=3 \
            --cpu=1 \
            --memory=256Mi

      - name: Run smoke tests
        run: |
          API_URL=$(gcloud run services describe hcp-api-staging --region=${{ env.GCP_REGION }} --format='value(status.url)')
          curl -f $API_URL/health || exit 1

      - name: Notify deployment
        uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {
              "text": "Staging deployment completed",
              "blocks": [
                {
                  "type": "section",
                  "text": {
                    "type": "mrkdwn",
                    "text": "✅ *Staging Deployment Successful*\n*Environment:* Staging\n*Commit:* ${{ github.sha }}\n*Branch:* ${{ github.ref_name }}"
                  }
                }
              ]
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

  # Deploy to Production
  deploy-production:
    name: Deploy to Production
    runs-on: ubuntu-latest
    needs: [api-container, webapp-container]
    if: github.ref == 'refs/heads/main'
    environment:
      name: production
      url: https://happycamperplanner.com

    permissions:
      contents: read
      id-token: write

    steps:
      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          credentials_json: ${{ secrets.GCP_SA_KEY }}

      - name: Set up Cloud SDK
        uses: google-github-actions/setup-gcloud@v2

      - name: Run database migrations
        run: |
          # Download and run migration job
          echo "Running database migrations..."
          # Add migration logic here

      - name: Deploy API to Cloud Run
        run: |
          gcloud run deploy hcp-api \
            --image=${{ env.REGISTRY }}/${{ env.GCP_PROJECT_ID }}/hcp-api:main-${{ github.sha }} \
            --region=${{ env.GCP_REGION }} \
            --platform=managed \
            --allow-unauthenticated \
            --set-env-vars=ASPNETCORE_ENVIRONMENT=Production \
            --set-secrets=DATABASE_CONNECTION_STRING=database-connection:latest \
            --min-instances=1 \
            --max-instances=10 \
            --cpu=1 \
            --memory=512Mi \
            --timeout=60s \
            --vpc-connector=hcp-vpc-connector \
            --no-traffic

      - name: Run integration tests
        run: |
          # Run tests against the new revision
          echo "Running integration tests..."
          # Add test logic here

      - name: Gradually route traffic
        run: |
          # Route 10% traffic to new revision
          gcloud run services update-traffic hcp-api \
            --region=${{ env.GCP_REGION }} \
            --to-revisions=LATEST=10

          sleep 300  # Monitor for 5 minutes

          # Route 50% traffic
          gcloud run services update-traffic hcp-api \
            --region=${{ env.GCP_REGION }} \
            --to-revisions=LATEST=50

          sleep 300  # Monitor for 5 minutes

          # Route 100% traffic
          gcloud run services update-traffic hcp-api \
            --region=${{ env.GCP_REGION }} \
            --to-revisions=LATEST=100

      - name: Deploy Frontend to Cloud Run
        run: |
          gcloud run deploy hcp-webapp \
            --image=${{ env.REGISTRY }}/${{ env.GCP_PROJECT_ID }}/hcp-webapp:main-${{ github.sha }} \
            --region=${{ env.GCP_REGION }} \
            --platform=managed \
            --allow-unauthenticated \
            --min-instances=0 \
            --max-instances=5 \
            --cpu=1 \
            --memory=256Mi

      - name: Create GitHub Release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: v${{ github.run_number }}
          release_name: Release v${{ github.run_number }}
          body: |
            Automated production deployment
            Commit: ${{ github.sha }}

      - name: Notify deployment
        uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {
              "text": "Production deployment completed",
              "blocks": [
                {
                  "type": "section",
                  "text": {
                    "type": "mrkdwn",
                    "text": "🚀 *Production Deployment Successful*\n*Environment:* Production\n*Commit:* ${{ github.sha }}\n*Release:* v${{ github.run_number }}"
                  }
                }
              ]
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

Security Scanning Workflow

.github/workflows/security.yml:

name: Security Scanning

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

jobs:
  dependency-scan:
    name: Dependency Vulnerability Scan
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

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

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

  container-scan:
    name: Container Image Scan
    runs-on: ubuntu-latest
    if: github.event_name != 'pull_request'

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Build API image
        run: docker build -t hcp-api:test -f src/api/Dockerfile .

      - name: Scan API image
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: 'hcp-api:test'
          format: 'sarif'
          output: 'trivy-api-results.sarif'

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

  sast:
    name: Static Application Security Testing
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Initialize CodeQL
        uses: github/codeql-action/init@v3
        with:
          languages: csharp, javascript

      - name: Autobuild
        uses: github/codeql-action/autobuild@v3

      - name: Perform CodeQL Analysis
        uses: github/codeql-action/analyze@v3

  secret-scan:
    name: Secret Scanning
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Run Gitleaks
        uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Database Migration Workflow

.github/workflows/db-migration.yml:

name: Database Migration

on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Environment to run migration'
        required: true
        type: choice
        options:
          - staging
          - production
      dry_run:
        description: 'Dry run (preview only)'
        required: false
        type: boolean
        default: true

jobs:
  migrate:
    name: Run Database Migration
    runs-on: ubuntu-latest
    environment: ${{ github.event.inputs.environment }}

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.0.x'

      - name: Install EF Core tools
        run: dotnet tool install --global dotnet-ef

      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          credentials_json: ${{ secrets.GCP_SA_KEY }}

      - name: Set up Cloud SDK
        uses: google-github-actions/setup-gcloud@v2

      - name: Start Cloud SQL Proxy
        run: |
          wget https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.8.0/cloud-sql-proxy.linux.amd64 -O cloud-sql-proxy
          chmod +x cloud-sql-proxy
          ./cloud-sql-proxy ${{ secrets.CLOUD_SQL_INSTANCE }} &
          sleep 5

      - name: Generate migration script
        if: github.event.inputs.dry_run == 'true'
        run: |
          cd src/api
          dotnet ef migrations script --idempotent --output migration.sql
          cat migration.sql
        env:
          ConnectionStrings__DefaultConnection: ${{ secrets.DATABASE_CONNECTION_STRING }}

      - name: Run migration
        if: github.event.inputs.dry_run == 'false'
        run: |
          cd src/api
          dotnet ef database update
        env:
          ConnectionStrings__DefaultConnection: ${{ secrets.DATABASE_CONNECTION_STRING }}

      - name: Upload migration script
        if: github.event.inputs.dry_run == 'true'
        uses: actions/upload-artifact@v4
        with:
          name: migration-script-${{ github.event.inputs.environment }}
          path: src/api/migration.sql

Performance Testing Workflow

.github/workflows/performance.yml:

name: Performance Testing

on:
  workflow_dispatch:
  schedule:
    - cron: '0 2 * * *'  # Daily at 2 AM

jobs:
  load-test:
    name: Load Testing
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Run k6 load test
        uses: grafana/k6-action@v0.3.1
        with:
          filename: tests/performance/load-test.js
        env:
          API_URL: ${{ secrets.STAGING_API_URL }}

      - name: Upload results
        uses: actions/upload-artifact@v4
        with:
          name: k6-results
          path: results.json

      - name: Comment PR with results
        if: github.event_name == 'pull_request'
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const results = JSON.parse(fs.readFileSync('results.json', 'utf8'));

            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `## Load Test Results\n\n` +
                    `- Requests: ${results.metrics.http_reqs.count}\n` +
                    `- Avg Response Time: ${results.metrics.http_req_duration.avg}ms\n` +
                    `- Error Rate: ${results.metrics.http_req_failed.rate}%`
            });


Dockerfile Optimization

API Dockerfile (Multi-stage)

src/api/Dockerfile:

# Build stage
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src

# Copy project files
COPY ["src/api/api.csproj", "api/"]
COPY ["src/Infrastructure/Infrastructure.csproj", "Infrastructure/"]

# Restore dependencies
RUN dotnet restore "api/api.csproj"

# Copy source code
COPY src/api/ api/
COPY src/Infrastructure/ Infrastructure/

# Build
WORKDIR "/src/api"
RUN dotnet build "api.csproj" -c Release -o /app/build

# Publish
FROM build AS publish
RUN dotnet publish "api.csproj" -c Release -o /app/publish /p:UseAppHost=false

# Runtime stage
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app

# Install Cloud SQL Proxy (if needed)
RUN apt-get update && apt-get install -y wget && \
    wget https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.8.0/cloud-sql-proxy.linux.amd64 -O cloud-sql-proxy && \
    chmod +x cloud-sql-proxy && \
    rm -rf /var/lib/apt/lists/*

# Copy published app
COPY --from=publish /app/publish .

# Set environment
ENV ASPNETCORE_URLS=http://+:5000
EXPOSE 5000

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:5000/health || exit 1

ENTRYPOINT ["dotnet", "api.dll"]

Frontend Dockerfile (Multi-stage with Nginx)

src/webapp/Dockerfile:

# Build stage
FROM node:18-alpine AS build
WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm ci --only=production

# Copy source
COPY . .

# Build arguments for environment variables
ARG VITE_API_URL
ARG VITE_FIREBASE_API_KEY
ARG VITE_FIREBASE_AUTH_DOMAIN
ARG VITE_FIREBASE_PROJECT_ID

# Build
RUN npm run build

# Runtime stage
FROM nginx:alpine AS final

# Copy custom nginx config
COPY nginx.conf /etc/nginx/nginx.conf

# Copy built assets
COPY --from=build /app/dist /usr/share/nginx/html

# Add health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost/health || exit 1

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

nginx.conf:

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Logging
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    # Performance
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;

    # Compression
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss;

    server {
        listen 80;
        server_name _;
        root /usr/share/nginx/html;
        index index.html;

        # Security headers
        add_header X-Frame-Options "SAMEORIGIN" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header X-XSS-Protection "1; mode=block" always;

        # Cache static assets
        location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
            expires 1y;
            add_header Cache-Control "public, immutable";
        }

        # Health check endpoint
        location /health {
            access_log off;
            return 200 "healthy\n";
            add_header Content-Type text/plain;
        }

        # SPA routing
        location / {
            try_files $uri $uri/ /index.html;
        }
    }
}


Best Practices Checklist

When creating or reviewing CI/CD workflows, verify:

Pipeline Design

  • Build and test run on every push and PR
  • Jobs are parallelized where possible
  • Artifacts are cached appropriately
  • Build times are optimized
  • Secrets are stored in GitHub Secrets
  • Environment-specific configurations are parameterized

Testing

  • Unit tests run in CI
  • Integration tests run before deployment
  • Code coverage is tracked
  • Test results are published
  • Failing tests block deployment
  • Performance tests run regularly

Security

  • Dependency scanning is automated
  • Container images are scanned for vulnerabilities
  • SAST tools analyze code
  • Secrets are never committed to code
  • Least privilege access for service accounts
  • Security scan results block merges

Deployment

  • Staging deployment is automatic on staging branch
  • Production deployment requires approval
  • Database migrations run before app deployment
  • Health checks verify successful deployment
  • Rollback procedure is documented
  • Deployment notifications are sent

Monitoring

  • Deployment events are logged
  • Error rates are monitored post-deployment
  • Performance metrics are tracked
  • Alerts fire on deployment failures
  • Success/failure notifications sent to team

GitHub Actions Patterns

Reusable Workflow

.github/workflows/reusable-deploy.yml:

name: Reusable Deploy

on:
  workflow_call:
    inputs:
      environment:
        required: true
        type: string
      image_tag:
        required: true
        type: string
    secrets:
      GCP_SA_KEY:
        required: true
      DATABASE_CONNECTION:
        required: true

jobs:
  deploy:
    name: Deploy to ${{ inputs.environment }}
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}

    steps:
      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          credentials_json: ${{ secrets.GCP_SA_KEY }}

      - name: Deploy to Cloud Run
        run: |
          gcloud run deploy hcp-api-${{ inputs.environment }} \
            --image=${{ inputs.image_tag }} \
            --region=us-central1 \
            --platform=managed

Composite Action

.github/actions/setup-dotnet-cache/action.yml:

name: 'Setup .NET with Cache'
description: 'Setup .NET SDK with NuGet package caching'

inputs:
  dotnet-version:
    description: '.NET SDK version'
    required: false
    default: '8.0.x'

runs:
  using: 'composite'
  steps:
    - name: Setup .NET
      uses: actions/setup-dotnet@v4
      with:
        dotnet-version: ${{ inputs.dotnet-version }}

    - name: Cache NuGet packages
      uses: actions/cache@v4
      with:
        path: ~/.nuget/packages
        key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
        restore-keys: |
          ${{ runner.os }}-nuget-

Matrix Strategy

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        dotnet-version: ['8.0.x', '9.0.x']
      fail-fast: false

    steps:
      - uses: actions/checkout@v4
      - name: Setup .NET ${{ matrix.dotnet-version }}
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: ${{ matrix.dotnet-version }}
      - run: dotnet test

Required GitHub Secrets

Configure these secrets in your repository:

GCP Authentication

  • GCP_PROJECT_ID - Google Cloud project ID
  • GCP_SA_KEY - Service account JSON key
  • CLOUD_SQL_INSTANCE - Cloud SQL connection name

Database

  • DATABASE_CONNECTION_STRING - Full connection string
  • DATABASE_CONNECTION_STRING_STAGING - Staging database

Firebase

  • VITE_FIREBASE_API_KEY
  • VITE_FIREBASE_AUTH_DOMAIN
  • VITE_FIREBASE_PROJECT_ID
  • VITE_FIREBASE_STORAGE_BUCKET
  • VITE_FIREBASE_MESSAGING_SENDER_ID
  • VITE_FIREBASE_APP_ID

API Configuration

  • VITE_API_URL - Production API URL
  • VITE_API_URL_STAGING - Staging API URL

Notifications

  • SLACK_WEBHOOK_URL - Slack webhook for notifications

Integration with Project Patterns

Branch Strategy

  • main → Production
  • staging → Staging environment
  • develop → Development builds
  • Feature branches → PR checks only

Environment Configuration

Each environment has: - Separate Cloud Run services - Separate Cloud SQL instances - Separate Secret Manager secrets - Environment-specific Firebase projects

Deployment Flow

  1. Developer pushes to feature branch
  2. CI runs tests and builds
  3. PR review and approval
  4. Merge to staging → auto-deploy to staging
  5. Merge to main → auto-deploy to production with gradual rollout

When to Use the DevOps Agent

Use this agent when:

  • Creating GitHub Actions workflows for CI/CD
  • Building Dockerfiles and optimizing containers
  • Setting up deployment pipelines to Cloud Run
  • Configuring automated testing in CI
  • Implementing security scanning in pipelines
  • Setting up database migrations in CI/CD
  • Optimizing build performance and caching
  • Creating reusable workflows and actions
  • Troubleshooting pipeline failures
  • Setting up monitoring and notifications

Integration with Baseline Behaviors

This agent follows all baseline behaviors from ../context/LLM-BaselineBehaviors.md:

  • Action-oriented: Creates workflow files and configurations
  • Research-driven: Examines existing workflows before changes
  • Complete solutions: Provides full workflows with all steps
  • Clear communication: Explains pipeline design decisions
  • Error handling: Ensures proper error handling and notifications
  • Task management: Uses todo lists for complex pipeline setup

DevOps-specific additions: - Security-first: Always includes security scanning - Optimization-focused: Maximizes cache usage and parallelization - Reliability-focused: Implements health checks and rollback procedures - GitHub Actions native: Leverages GitHub Actions ecosystem - Multi-environment: Supports staging and production environments