Skip to content

GitHub Actions Reference

Overview

The DineTogether infrastructure uses GitHub Actions for continuous deployment. This reference covers all workflows and their configuration.

Workflow Files

Reusable Deploy Workflow

Location: .github/workflows/deploy.yml

Purpose: Shared workflow that any repository can use for deployment.

name: Deploy to Kubernetes

on:
  workflow_call:
    inputs:
      repository:
        required: true
        type: string
        description: 'Repository name (owner/repo)'
      environment:
        required: false
        type: string
        default: 'staging'
        description: 'Deployment environment'
    secrets:
      DEPLOY_TOKEN:
        required: true
        description: 'GitHub token with package read permissions'

Using the Deploy Workflow

Basic Usage

In your application repository, create .github/workflows/deploy.yml:

name: Build and Deploy

on:
  push:
    branches: [main]
  pull_request:
    types: [opened, synchronize]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

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

      - name: Log in to GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            ghcr.io/${{ github.repository }}:latest
            ghcr.io/${{ github.repository }}:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  deploy:
    needs: build
    if: github.ref == 'refs/heads/main'
    uses: dine-together/k8s-infrastructure/.github/workflows/deploy.yml@main
    with:
      repository: ${{ github.repository }}
      environment: staging
    secrets:
      DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

Advanced Configuration

Multiple Environments

jobs:
  deploy-staging:
    uses: dine-together/k8s-infrastructure/.github/workflows/deploy.yml@main
    with:
      repository: ${{ github.repository }}
      environment: staging
    secrets:
      DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

  deploy-production:
    if: github.ref == 'refs/heads/production'
    uses: dine-together/k8s-infrastructure/.github/workflows/deploy.yml@main
    with:
      repository: ${{ github.repository }}
      environment: production
    secrets:
      DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN_PROD }}

Manual Deployment

on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Environment to deploy to'
        required: true
        type: choice
        options:
          - staging
          - production

Workflow Steps Explained

1. Checkout Infrastructure Code

- uses: actions/checkout@v4
  with:
    repository: dine-together/k8s-infrastructure
    token: ${{ secrets.DEPLOY_TOKEN }}
Clones the infrastructure repository to access scripts.

2. Setup Tools

- name: Set up kubectl
  uses: azure/setup-kubectl@v3
  with:
    version: 'latest'
Installs kubectl for Kubernetes operations.

3. Checkout App Repository

- uses: actions/checkout@v4
  with:
    repository: ${{ inputs.repository }}
    path: app
    token: ${{ secrets.DEPLOY_TOKEN }}
Clones the application repository containing docker-compose.yml.

4. Convert Docker Compose

- name: Convert docker-compose to k8s
  run: |
    python scripts/compose-to-k8s.py \
      app/docker-compose.yml \
      k8s-manifests.yaml
Transforms Docker Compose to Kubernetes manifests.

5. Configure kubectl

- name: Configure kubectl
  env:
    KUBECONFIG_FILE: ${{ secrets.KUBECONFIG }}
  run: |
    mkdir -p ~/.kube
    echo "$KUBECONFIG_FILE" | base64 -d > ~/.kube/config
Sets up Kubernetes cluster access.

6. Apply Manifests

- name: Deploy to Kubernetes
  run: |
    kubectl apply -f k8s-manifests.yaml -n $NAMESPACE
Deploys the application to Kubernetes.

Secrets Reference

Required Secrets

Secret Purpose Scope
DEPLOY_TOKEN GitHub PAT with package:read Repository
KUBECONFIG Base64-encoded kubeconfig Infrastructure repo
GITHUB_TOKEN Automatic token for builds Automatic

Creating DEPLOY_TOKEN

  1. Go to GitHub Settings → Developer settings → Personal access tokens
  2. Create token with permissions:
  3. read:packages (required)
  4. repo (if accessing private repos)
  5. Add to repository:
    gh secret set DEPLOY_TOKEN --repo dine-together/myapp
    

Setting KUBECONFIG

# Get kubeconfig from server
ssh user@server 'sudo cat /etc/rancher/k3s/k3s.yaml' > kubeconfig.yaml

# Update server URL
sed -i 's/127.0.0.1/YOUR_SERVER_IP/g' kubeconfig.yaml

# Encode and set
cat kubeconfig.yaml | base64 -w 0 > kubeconfig.b64
gh secret set KUBECONFIG < kubeconfig.b64 --repo dine-together/k8s-infrastructure

Environment Variables

Build Job

Variable Description Example
GIT_SHA Commit SHA for tagging abc123...
REGISTRY Container registry ghcr.io
IMAGE_NAME Full image path ghcr.io/dine-together/app

Deploy Job

Variable Description Default
NAMESPACE K8s namespace test-staging or dine-together-production
DOMAIN Base domain test.dinetogether.co.uk
ENVIRONMENT Environment name staging

Caching Strategies

Docker Build Cache

cache-from: type=gha
cache-to: type=gha,mode=max

Uses GitHub Actions cache for faster builds.

Alternative: Registry Cache

cache-from: type=registry,ref=ghcr.io/${{ github.repository }}:buildcache
cache-to: type=registry,ref=ghcr.io/${{ github.repository }}:buildcache,mode=max

Conditional Deployment

Branch-based

if: github.ref == 'refs/heads/main'

Tag-based

if: startsWith(github.ref, 'refs/tags/v')

PR Preview

if: github.event_name == 'pull_request'
with:
  environment: pr-${{ github.event.pull_request.number }}

Error Handling

Retry Logic

- name: Deploy with retry
  uses: nick-invision/retry@v2
  with:
    timeout_minutes: 10
    max_attempts: 3
    command: kubectl apply -f k8s-manifests.yaml

Rollback on Failure

- name: Rollback on failure
  if: failure()
  run: |
    kubectl rollout undo deployment/myapp -n $NAMESPACE

Monitoring Deployment

Wait for Rollout

- name: Wait for deployment
  run: |
    kubectl rollout status deployment/myapp -n $NAMESPACE

Health Check

- name: Verify deployment
  run: |
    kubectl wait --for=condition=ready pod \
      -l app=myapp -n $NAMESPACE \
      --timeout=300s

Advanced Patterns

Matrix Deployments

strategy:
  matrix:
    service: [frontend, backend, worker]
jobs:
  deploy:
    uses: ./.github/workflows/deploy.yml
    with:
      repository: dine-together/${{ matrix.service }}

Parallel Deployments

jobs:
  deploy-frontend:
    uses: ./.github/workflows/deploy.yml
    # ...

  deploy-backend:
    uses: ./.github/workflows/deploy.yml
    # ...

  verify:
    needs: [deploy-frontend, deploy-backend]
    runs-on: ubuntu-latest
    steps:
      - run: echo "All deployments complete"

Blue-Green Deployment

- name: Deploy to green
  run: |
    kubectl apply -f k8s-manifests.yaml -n $NAMESPACE-green
    kubectl wait --for=condition=ready pod -l app=myapp -n $NAMESPACE-green

- name: Switch traffic
  run: |
    kubectl patch ingress myapp -n $NAMESPACE \
      -p '{"spec":{"rules":[{"host":"myapp.com","http":{"paths":[{"backend":{"service":{"name":"myapp-green"}}}]}}]}}'

Debugging Workflows

Enable Debug Logging

env:
  ACTIONS_RUNNER_DEBUG: true
  ACTIONS_STEP_DEBUG: true

SSH into Runner

- name: Setup tmate session
  if: ${{ failure() }}
  uses: mxschmitt/action-tmate@v3

Artifact Upload

- name: Upload manifests
  if: always()
  uses: actions/upload-artifact@v3
  with:
    name: k8s-manifests
    path: k8s-manifests.yaml

Security Best Practices

  1. Limit Token Permissions
  2. Use minimum required permissions
  3. Separate tokens for different environments

  4. Protect Secrets

  5. Never log secret values
  6. Use GitHub Environments for protection

  7. Verify Images

    - name: Scan image
      uses: aquasecurity/trivy-action@master
      with:
        image-ref: ghcr.io/${{ github.repository }}:${{ github.sha }}
    

Troubleshooting

Common Issues

  1. 401 Unauthorized
  2. Check DEPLOY_TOKEN permissions
  3. Verify token hasn't expired

  4. kubectl connection refused

  5. Verify KUBECONFIG is correct
  6. Check server IP and port

  7. Deployment timeout

  8. Check pod logs
  9. Verify image can be pulled
  10. Check resource limits

Debug Commands

- name: Debug info
  if: failure()
  run: |
    echo "Repository: ${{ inputs.repository }}"
    echo "Environment: ${{ inputs.environment }}"
    kubectl get all -n $NAMESPACE
    kubectl describe pod -l app=myapp -n $NAMESPACE

Next Steps