GitHub Actions Integration

GitHub Actions can automate GitOps workflows by building container images, updating manifests, running tests, and triggering deployments. This integration combines CI/CD automation with GitOps principles.

GitOps + CI/CD Flow

graph TB A[Developer Push] --> B[GitHub Actions] B --> C[Build Image] C --> D[Push to Registry] D --> E[Update Manifest] E --> F[Git Commit] F --> G[GitOps Controller] G --> H[Deploy to Cluster] style B fill:#e1f5ff style C fill:#e8f5e9 style E fill:#fff4e1 style G fill:#f3e5f5

Basic Workflow

Image Build and Push

# .github/workflows/build.yml
name: Build and Push

on:
  push:
    branches: [main]
    paths:
    - 'apps/web-app/**'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v2
    
    - name: Login to Registry
      uses: docker/login-action@v2
      with:
        registry: ghcr.io
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}
    
    - name: Build and Push
      uses: docker/build-push-action@v4
      with:
        context: ./apps/web-app
        push: true
        tags: |
          ghcr.io/${{ github.repository }}/web-app:${{ github.sha }}
          ghcr.io/${{ github.repository }}/web-app:latest

Update GitOps Manifest

# .github/workflows/update-manifest.yml
name: Update Manifest

on:
  workflow_run:
    workflows: ["Build and Push"]
    types:
      - completed

jobs:
  update:
    runs-on: ubuntu-latest
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    steps:
    - uses: actions/checkout@v3
      with:
        token: ${{ secrets.GITOPS_TOKEN }}
    
    - name: Update Image Tag
      run: |
        IMAGE_TAG="ghcr.io/${{ github.repository }}/web-app:${{ github.sha }}"
        cd apps/web-app/overlays/production
        sed -i "s|newImage:.*|newImage: ${IMAGE_TAG}|" kustomization.yaml
    
    - name: Commit Changes
      run: |
        git config user.name "GitHub Actions"
        git config user.email "[email protected]"
        git add apps/web-app/overlays/production/kustomization.yaml
        git commit -m "Update web-app image to ${{ github.sha }}"
        git push

Image Update Automation

Automated Image Updates

# .github/workflows/image-update.yml
name: Update Image

on:
  push:
    branches: [main]
    paths:
    - 'apps/web-app/**'
  workflow_dispatch:

jobs:
  update-image:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
      with:
        token: ${{ secrets.GITOPS_TOKEN }}
    
    - name: Get Image SHA
      id: image
      run: |
        SHA=$(git rev-parse --short HEAD)
        echo "sha=$SHA" >> $GITHUB_OUTPUT
    
    - name: Update Kustomize Image
      run: |
        cd apps/web-app/overlays/production
        IMAGE="ghcr.io/${{ github.repository }}/web-app:${{ steps.image.outputs.sha }}"
        kustomize edit set image web-app=$IMAGE
    
    - name: Create Pull Request
      uses: peter-evans/create-pull-request@v5
      with:
        token: ${{ secrets.GITOPS_TOKEN }}
        commit-message: "chore: update web-app image to ${{ steps.image.outputs.sha }}"
        title: "Update web-app image"
        body: |
          Automated image update for web-app
          - Image: ghcr.io/${{ github.repository }}/web-app:${{ steps.image.outputs.sha }}
        branch: update-web-app-image

Manifest Validation

Validate Kubernetes Manifests

# .github/workflows/validate.yml
name: Validate Manifests

on:
  pull_request:
    paths:
    - 'apps/**'
    - 'infrastructure/**'

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    
    - name: Install kubeval
      run: |
        wget https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz
        tar xf kubeval-linux-amd64.tar.gz
        sudo mv kubeval /usr/local/bin
    
    - name: Validate Manifests
      run: |
        for file in $(find apps -name "*.yaml" -o -name "*.yml"); do
          kubeval $file
        done

Validate with Kustomize

# .github/workflows/kustomize-validate.yml
name: Kustomize Validate

on:
  pull_request:
    paths:
    - 'apps/**'

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    
    - name: Install Kustomize
      uses: imranismail/setup-kustomize@v1
      with:
        version: "4.5.7"
    
    - name: Build and Validate
      run: |
        cd apps/web-app/overlays/production
        kustomize build . | kubectl apply --dry-run=client -f -

Security Scanning

Container Image Scanning

# .github/workflows/scan.yml
name: Security Scan

on:
  push:
    branches: [main]

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    
    - name: Run Trivy vulnerability scanner
      uses: aquasecurity/trivy-action@master
      with:
        image-ref: 'ghcr.io/${{ github.repository }}/web-app:${{ github.sha }}'
        format: 'sarif'
        output: 'trivy-results.sarif'
    
    - name: Upload Trivy results
      uses: github/codeql-action/upload-sarif@v2
      with:
        sarif_file: 'trivy-results.sarif'

Manifest Security Scanning

# .github/workflows/manifest-scan.yml
name: Scan Manifests

on:
  pull_request:
    paths:
    - 'apps/**'

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    
    - name: Run Checkov
      uses: bridgecrewio/checkov-action@master
      with:
        directory: apps/
        framework: kubernetes
        output_format: sarif
        output_file_path: checkov.sarif
    
    - name: Upload results
      uses: github/codeql-action/upload-sarif@v2
      with:
        sarif_file: checkov.sarif

Promotion Workflows

Environment Promotion

# .github/workflows/promote.yml
name: Promote to Production

on:
  workflow_dispatch:
    inputs:
      version:
        description: 'Version to promote'
        required: true
        type: string

jobs:
  promote:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
      with:
        token: ${{ secrets.GITOPS_TOKEN }}
    
    - name: Update Production Image
      run: |
        cd apps/web-app/overlays/production
        IMAGE="ghcr.io/${{ github.repository }}/web-app:${{ github.event.inputs.version }}"
        kustomize edit set image web-app=$IMAGE
    
    - name: Create Promotion PR
      uses: peter-evans/create-pull-request@v5
      with:
        token: ${{ secrets.GITOPS_TOKEN }}
        title: "Promote web-app ${{ github.event.inputs.version }} to production"
        body: |
          Promoting web-app version ${{ github.event.inputs.version }} to production.
          
          - Version: ${{ github.event.inputs.version }}
          - Image: ghcr.io/${{ github.repository }}/web-app:${{ github.event.inputs.version }}
        branch: promote-prod-${{ github.event.inputs.version }}
        labels: promotion,production

Automated Promotion with Tests

# .github/workflows/promote-with-tests.yml
name: Promote with Tests

on:
  push:
    branches: [staging]

jobs:
  test-staging:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    
    - name: Run Integration Tests
      run: |
        # Run tests against staging environment
        ./scripts/test-staging.sh
    
    - name: Promote to Production
      if: success()
      uses: actions/checkout@v3
      with:
        token: ${{ secrets.GITOPS_TOKEN }}
      run: |
        cd apps/web-app/overlays/production
        STAGING_IMAGE=$(grep newImage overlays/staging/kustomization.yaml | cut -d: -f2)
        kustomize edit set image web-app=$STAGING_IMAGE
        git commit -am "Promote web-app to production"
        git push

ArgoCD Integration

Trigger ArgoCD Sync

# .github/workflows/argocd-sync.yml
name: Sync ArgoCD

on:
  push:
    branches: [main]
    paths:
    - 'apps/**'

jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
    - name: Sync ArgoCD Application
      uses: actions-hub/kubectl@master
      env:
        KUBE_CONFIG: ${{ secrets.KUBE_CONFIG }}
      with:
        args: >
          patch application web-app
          -n argocd
          --type merge
          -p '{"operation":{"initiatedBy":{"username":"github-actions"},"sync":{"revision":"${{ github.sha }}"}}}'

ArgoCD Webhook

# .github/workflows/argocd-webhook.yml
name: ArgoCD Webhook

on:
  repository_dispatch:
    types: [argocd-sync]

jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
    - name: Trigger ArgoCD Sync
      run: |
        curl -X POST ${{ secrets.ARGOCD_WEBHOOK_URL }} \
          -H "Authorization: Bearer ${{ secrets.ARGOCD_TOKEN }}"

Flux Integration

Update Flux Image Policy

# .github/workflows/flux-update.yml
name: Update Flux Image

on:
  push:
    branches: [main]

jobs:
  update:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
      with:
        token: ${{ secrets.GITOPS_TOKEN }}
    
    - name: Update ImagePolicy
      run: |
        IMAGE_TAG="${{ github.sha }}"
        kubectl patch imagepolicy web-app -n flux-system \
          --type json \
          -p="[{\"op\": \"replace\", \"path\": \"/spec/policy/semver/range\", \"value\": \">=$IMAGE_TAG\"}]"

Best Practices

1. Use Secrets for Credentials

# Store in GitHub Secrets
- name: Login to Registry
  uses: docker/login-action@v2
  with:
    username: ${{ secrets.REGISTRY_USERNAME }}
    password: ${{ secrets.REGISTRY_PASSWORD }}

2. Limit Workflow Permissions

permissions:
  contents: write  # Only what's needed
  pull-requests: write

3. Protect Main Branch

Require:

  • Pull request reviews
  • Status checks to pass
  • Branch protection rules

4. Use Matrix Builds

strategy:
  matrix:
    app: [web-app, api, worker]
steps:
- name: Build ${{ matrix.app }}
  run: docker build -t ${{ matrix.app }} ./apps/${{ matrix.app }}

5. Cache Dependencies

- name: Cache dependencies
  uses: actions/cache@v3
  with:
    path: ~/.m2
    key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}

6. Run Tests Before Update

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - name: Run Tests
      run: npm test
  
  update:
    needs: test
    runs-on: ubuntu-latest
    steps:
    - name: Update Manifest
      run: ...

Common Workflows

Complete CI/CD Pipeline

# .github/workflows/ci-cd.yml
name: CI/CD Pipeline

on:
  push:
    branches: [main, develop]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: Build Image
      run: docker build -t app:${{ github.sha }} .
    - name: Push Image
      run: docker push app:${{ github.sha }}
  
  test:
    runs-on: ubuntu-latest
    needs: build
    steps:
    - name: Run Tests
      run: ./test.sh
  
  scan:
    runs-on: ubuntu-latest
    needs: build
    steps:
    - name: Security Scan
      uses: aquasecurity/trivy-action@master
  
  update-manifest:
    runs-on: ubuntu-latest
    needs: [test, scan]
    if: github.ref == 'refs/heads/main'
    steps:
    - uses: actions/checkout@v3
    - name: Update Manifest
      run: |
        sed -i "s|image:.*|image: app:${{ github.sha }}|" manifest.yaml
        git commit -am "Update image"
        git push

Troubleshooting

Workflow Not Triggering

# Check paths filter
on:
  push:
    paths:
    - 'apps/**'  # Must match file paths

Permission Denied

# Add permissions
permissions:
  contents: write
  pull-requests: write

Secrets Not Available

  • Check secret name matches
  • Verify secret is set in repository settings
  • Use ${{ secrets.SECRET_NAME }} syntax

Summary

GitHub Actions + GitOps integration:

  • Build Images - Automate container builds
  • Update Manifests - Automatically update GitOps manifests
  • Validate - Test manifests before deployment
  • Scan - Security scanning for images and manifests
  • Promote - Automated environment promotion
  • Sync - Trigger GitOps controller syncs

Best practices:

  • Use secrets for credentials
  • Limit workflow permissions
  • Protect main branch
  • Run tests before updates
  • Cache dependencies
  • Validate before deploying

This combination provides automated CI/CD while maintaining GitOps principles of declarative, auditable deployments.