Files
cv-site/GITHUB_ACTIONS_DEPLOYMENT_GUIDE.md
T
root 5e38292d2e Add GitHub Actions deployment workflow
- Add deployment workflow with test, build, and deploy jobs
- Add testing workflow for PRs
- Add deployment scripts (deploy, healthcheck, rollback)
- Add systemd service configuration
- Update Makefile with CI/CD targets
- Add comprehensive deployment documentation
2025-10-30 12:19:57 +00:00

24 KiB

GitHub Actions Deployment Guide for CV Site (Go Project)

Target Project: cv-site (Go application) Repository: github.com/juanatsap/cv-site Deployment: VM-based deployment (similar to La Porra architecture) Reference: Based on La Porra's GitHub Actions workflows


Table of Contents

  1. Overview
  2. Required GitHub Secrets
  3. Workflow Files Structure
  4. Main Deployment Workflow
  5. CDN Integration (Optional)
  6. Documentation Deployment
  7. Build Artifacts
  8. Environment Configuration
  9. Testing Strategy
  10. Deployment Checklist

Overview

This guide provides a complete GitHub Actions setup for deploying a Go-based application to a VM server. The workflow is based on La Porra's proven deployment patterns but adapted for Go applications.

Key Components

  • CI/CD Pipeline: Automated build, test, and deployment
  • Artifact Management: Binary compilation and distribution
  • CDN Integration: CloudFlare for static assets (optional)
  • Health Checks: Post-deployment validation
  • Notifications: Deployment status alerts

Required GitHub Secrets

Configure these secrets in your repository settings (Settings → Secrets and variables → Actions):

Essential Secrets

Secret Name Description Example/Notes
SSH_PRIVATE_KEY Private SSH key for VM access Generate with ssh-keygen -t ed25519
SSH_HOST VM server hostname or IP your-vm.example.com or 192.168.1.100
SSH_USER SSH username for deployment deployer or ubuntu
SSH_PORT SSH port (if non-standard) 22 (default) or custom port
DEPLOY_PATH Application directory on VM /var/www/cv-site or /opt/cv-site

Optional Secrets (if using CDN)

Secret Name Description Required For
CLOUDFLARE_API_TOKEN CloudFlare API token CDN deployment
CLOUDFLARE_ZONE_ID CloudFlare zone identifier CDN deployment
SLACK_WEBHOOK Slack webhook URL for notifications Team notifications

How to Generate SSH Keys

# On your local machine, generate a key pair
ssh-keygen -t ed25519 -C "github-actions-cv-site" -f ~/.ssh/cv-site-deploy

# Copy the public key to your VM
ssh-copy-id -i ~/.ssh/cv-site-deploy.pub user@your-vm.example.com

# Add the PRIVATE key content to GitHub Secrets
cat ~/.ssh/cv-site-deploy
# Copy the entire output (including -----BEGIN and -----END lines)

Workflow Files Structure

Create these files in your repository:

cv-site/
├── .github/
│   └── workflows/
│       ├── deploy.yml           # Main deployment workflow
│       ├── cdn-deploy.yml       # CDN optimization (optional)
│       └── test.yml             # CI testing workflow
├── scripts/
│   ├── deploy.sh                # Deployment script for VM
│   ├── healthcheck.sh           # Post-deployment validation
│   └── rollback.sh              # Rollback to previous version
├── config/
│   └── systemd/
│       └── cv-site.service      # Systemd service file
└── Makefile                     # Build targets

Main Deployment Workflow

File: .github/workflows/deploy.yml

name: Deploy CV Site to VM

on:
  push:
    branches:
      - main
      - production
  workflow_dispatch:
    inputs:
      environment:
        description: 'Deployment environment'
        required: true
        default: 'production'
        type: choice
        options:
          - production
          - staging

env:
  GO_VERSION: '1.22'
  APP_NAME: 'cv-site'
  BUILD_DIR: './build'

jobs:
  test:
    name: Run Tests
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Go
        uses: actions/setup-go@v5
        with:
          go-version: ${{ env.GO_VERSION }}
          cache: true

      - name: Download dependencies
        run: go mod download

      - name: Run tests
        run: |
          go test -v -race -coverprofile=coverage.txt -covermode=atomic ./...

      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          file: ./coverage.txt
          fail_ci_if_error: false

  build:
    name: Build Application
    runs-on: ubuntu-latest
    needs: test
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Go
        uses: actions/setup-go@v5
        with:
          go-version: ${{ env.GO_VERSION }}
          cache: true

      - name: Build binary
        run: |
          mkdir -p ${{ env.BUILD_DIR }}

          # Build for Linux AMD64 (typical VM architecture)
          CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
            -ldflags="-w -s -X main.Version=${{ github.sha }} -X main.BuildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
            -o ${{ env.BUILD_DIR }}/${{ env.APP_NAME }} \
            ./cmd/${{ env.APP_NAME }}

      - name: Compress binary
        run: |
          cd ${{ env.BUILD_DIR }}
          tar -czf ${{ env.APP_NAME }}-${{ github.sha }}.tar.gz ${{ env.APP_NAME }}

      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: cv-site-binary
          path: ${{ env.BUILD_DIR }}/${{ env.APP_NAME }}-${{ github.sha }}.tar.gz
          retention-days: 30

  deploy:
    name: Deploy to VM
    runs-on: ubuntu-latest
    needs: build
    environment:
      name: ${{ github.event.inputs.environment || 'production' }}
      url: https://cv.example.com
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Download artifact
        uses: actions/download-artifact@v4
        with:
          name: cv-site-binary
          path: ./build

      - name: Setup SSH
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/deploy_key
          chmod 600 ~/.ssh/deploy_key
          ssh-keyscan -p ${{ secrets.SSH_PORT || 22 }} ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts

      - name: Deploy to VM
        env:
          SSH_KEY: ~/.ssh/deploy_key
          SSH_USER: ${{ secrets.SSH_USER }}
          SSH_HOST: ${{ secrets.SSH_HOST }}
          SSH_PORT: ${{ secrets.SSH_PORT || 22 }}
          DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }}
        run: |
          # Extract artifact
          cd build
          tar -xzf ${{ env.APP_NAME }}-${{ github.sha }}.tar.gz

          # Upload binary to VM
          scp -i $SSH_KEY -P $SSH_PORT ${{ env.APP_NAME }} \
            $SSH_USER@$SSH_HOST:$DEPLOY_PATH/${{ env.APP_NAME }}.new

          # Upload deployment script
          scp -i $SSH_KEY -P $SSH_PORT ../scripts/deploy.sh \
            $SSH_USER@$SSH_HOST:$DEPLOY_PATH/

          # Execute deployment on VM
          ssh -i $SSH_KEY -p $SSH_PORT $SSH_USER@$SSH_HOST << 'ENDSSH'
            cd ${{ secrets.DEPLOY_PATH }}

            # Make scripts executable
            chmod +x deploy.sh
            chmod +x ${{ env.APP_NAME }}.new

            # Run deployment script
            ./deploy.sh ${{ env.APP_NAME }}
          ENDSSH

      - name: Health check
        run: |
          echo "Waiting for application to start..."
          sleep 10

          # Check if service is running
          ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT || 22 }} \
            ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} \
            "systemctl is-active ${{ env.APP_NAME }}"

          # HTTP health check
          RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" https://cv.example.com/health)

          if [ "$RESPONSE" = "200" ]; then
            echo "✅ Health check passed (HTTP $RESPONSE)"
          else
            echo "❌ Health check failed (HTTP $RESPONSE)"
            exit 1
          fi

      - name: Send notification
        if: always()
        env:
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
        run: |
          STATUS="${{ job.status }}"
          COLOR="good"
          if [ "$STATUS" != "success" ]; then
            COLOR="danger"
          fi

          curl -X POST "$SLACK_WEBHOOK" \
            -H "Content-Type: application/json" \
            -d "{
              \"attachments\": [{
                \"color\": \"$COLOR\",
                \"title\": \"CV Site Deployment $STATUS\",
                \"text\": \"Deployment to VM completed\",
                \"fields\": [
                  {\"title\": \"Branch\", \"value\": \"${{ github.ref_name }}\", \"short\": true},
                  {\"title\": \"Commit\", \"value\": \"${{ github.sha }}\", \"short\": true},
                  {\"title\": \"Environment\", \"value\": \"${{ github.event.inputs.environment || 'production' }}\", \"short\": true}
                ]
              }]
            }" || true

      - name: Cleanup SSH keys
        if: always()
        run: |
          rm -f ~/.ssh/deploy_key

CDN Integration (Optional)

File: .github/workflows/cdn-deploy.yml

name: CloudFlare CDN Deployment

on:
  push:
    branches:
      - main
    paths:
      - 'static/**'
      - 'assets/**'
  workflow_dispatch:
    inputs:
      purge_cache:
        description: 'Purge CDN cache'
        required: false
        default: 'true'
        type: choice
        options:
          - 'true'
          - 'false'

jobs:
  deploy-cdn:
    name: Deploy Static Assets to CDN
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup SSH
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/deploy_key
          chmod 600 ~/.ssh/deploy_key
          ssh-keyscan -p ${{ secrets.SSH_PORT || 22 }} ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts

      - name: Sync static assets to VM
        run: |
          rsync -avz -e "ssh -i ~/.ssh/deploy_key -p ${{ secrets.SSH_PORT || 22 }}" \
            --delete \
            ./static/ \
            ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:${{ secrets.DEPLOY_PATH }}/static/

      - name: Configure CloudFlare
        if: secrets.CLOUDFLARE_API_TOKEN != ''
        env:
          CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }}
        run: |
          # Enable WebP
          curl -X PATCH "https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE_ID}/settings/webp" \
            -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
            -H "Content-Type: application/json" \
            --data '{"value":"on"}'

          # Enable Brotli compression
          curl -X PATCH "https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE_ID}/settings/brotli" \
            -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
            -H "Content-Type: application/json" \
            --data '{"value":"on"}'

      - name: Purge CDN cache
        if: github.event.inputs.purge_cache == 'true' || github.event_name == 'push'
        env:
          CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }}
        run: |
          curl -X POST "https://api.cloudflare.com/client/v4/zones/${CLOUDFLARE_ZONE_ID}/purge_cache" \
            -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
            -H "Content-Type: application/json" \
            --data '{"purge_everything":true}'

      - name: Cleanup
        if: always()
        run: rm -f ~/.ssh/deploy_key

Build Artifacts

What Gets Built

For a Go application, the main artifact is the compiled binary:

  1. Binary: cv-site (or cv-site.exe for Windows)
  2. Archive: cv-site-{commit-sha}.tar.gz
  3. Metadata: Version info embedded during build

Build Flags Explained

CGO_ENABLED=0      # Static binary (no C dependencies)
GOOS=linux         # Target OS
GOARCH=amd64       # Target architecture

-ldflags="-w -s    # Strip debug info (smaller binary)
  -X main.Version=$SHA        # Embed git commit
  -X main.BuildTime=$DATE"    # Embed build timestamp

Artifact Storage

  • GitHub Actions Artifacts: 30 days retention
  • VM Server: /opt/cv-site/releases/{version}/
  • Backup: Previous 5 versions kept for rollback

Deployment Scripts

File: scripts/deploy.sh

#!/bin/bash
set -e

APP_NAME="${1:-cv-site}"
DEPLOY_PATH="${DEPLOY_PATH:-/opt/cv-site}"
SERVICE_NAME="cv-site"

echo "🚀 Starting deployment of $APP_NAME"

# Create backup of current version
if [ -f "$DEPLOY_PATH/$APP_NAME" ]; then
    echo "📦 Backing up current version..."
    cp "$DEPLOY_PATH/$APP_NAME" "$DEPLOY_PATH/$APP_NAME.backup"
fi

# Move new binary into place
echo "📥 Installing new version..."
mv "$DEPLOY_PATH/$APP_NAME.new" "$DEPLOY_PATH/$APP_NAME"
chmod +x "$DEPLOY_PATH/$APP_NAME"

# Restart service
echo "🔄 Restarting service..."
sudo systemctl restart "$SERVICE_NAME"

# Wait for service to start
sleep 3

# Check service status
if sudo systemctl is-active --quiet "$SERVICE_NAME"; then
    echo "✅ Service started successfully"

    # Remove backup after successful deployment
    rm -f "$DEPLOY_PATH/$APP_NAME.backup"
else
    echo "❌ Service failed to start - rolling back"

    # Rollback to backup
    if [ -f "$DEPLOY_PATH/$APP_NAME.backup" ]; then
        mv "$DEPLOY_PATH/$APP_NAME.backup" "$DEPLOY_PATH/$APP_NAME"
        sudo systemctl restart "$SERVICE_NAME"
        echo "⚠️ Rolled back to previous version"
    fi

    exit 1
fi

echo "🎉 Deployment completed successfully"

File: scripts/healthcheck.sh

#!/bin/bash
set -e

HEALTH_URL="${HEALTH_URL:-http://localhost:8080/health}"
MAX_RETRIES=30
RETRY_DELAY=2

echo "🏥 Running health check..."

for i in $(seq 1 $MAX_RETRIES); do
    if curl -sf "$HEALTH_URL" > /dev/null; then
        echo "✅ Health check passed (attempt $i)"
        exit 0
    fi

    echo "⏳ Waiting for service... (attempt $i/$MAX_RETRIES)"
    sleep $RETRY_DELAY
done

echo "❌ Health check failed after $MAX_RETRIES attempts"
exit 1

Environment Configuration

Systemd Service File

Create: config/systemd/cv-site.service

[Unit]
Description=CV Site Go Application
After=network.target

[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/cv-site
ExecStart=/opt/cv-site/cv-site
Restart=always
RestartSec=5s

# Environment variables
Environment="PORT=8080"
Environment="GIN_MODE=release"
EnvironmentFile=/opt/cv-site/.env

# Security hardening
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/cv-site/data

# Resource limits
LimitNOFILE=65536
MemoryMax=512M

[Install]
WantedBy=multi-user.target

Installation on VM

# Copy service file
sudo cp config/systemd/cv-site.service /etc/systemd/system/

# Reload systemd
sudo systemctl daemon-reload

# Enable service
sudo systemctl enable cv-site

# Start service
sudo systemctl start cv-site

# Check status
sudo systemctl status cv-site

Testing Strategy

File: .github/workflows/test.yml

name: Test CV Site

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

jobs:
  test:
    name: Test on Go ${{ matrix.go-version }}
    runs-on: ubuntu-latest
    strategy:
      matrix:
        go-version: ['1.21', '1.22']

    steps:
      - uses: actions/checkout@v4

      - name: Setup Go
        uses: actions/setup-go@v5
        with:
          go-version: ${{ matrix.go-version }}
          cache: true

      - name: Install dependencies
        run: go mod download

      - name: Run linter
        uses: golangci/golangci-lint-action@v3
        with:
          version: latest

      - name: Run tests
        run: |
          go test -v -race -coverprofile=coverage.txt ./...

      - name: Build
        run: |
          go build -v ./...

Makefile

Create a Makefile for local development and CI consistency:

.PHONY: build test deploy clean help

APP_NAME := cv-site
BUILD_DIR := ./build
GO_VERSION := 1.22
VERSION := $(shell git describe --tags --always --dirty)
BUILD_TIME := $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
LDFLAGS := -ldflags="-w -s -X main.Version=$(VERSION) -X main.BuildTime=$(BUILD_TIME)"

## help: Display this help message
help:
	@echo "Available targets:"
	@sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /'

## build: Build the application binary
build:
	@echo "🔨 Building $(APP_NAME)..."
	@mkdir -p $(BUILD_DIR)
	CGO_ENABLED=0 go build $(LDFLAGS) -o $(BUILD_DIR)/$(APP_NAME) ./cmd/$(APP_NAME)
	@echo "✅ Build complete: $(BUILD_DIR)/$(APP_NAME)"

## test: Run all tests
test:
	@echo "🧪 Running tests..."
	go test -v -race -coverprofile=coverage.txt ./...

## lint: Run linter
lint:
	@echo "🔍 Running linter..."
	golangci-lint run

## clean: Clean build artifacts
clean:
	@echo "🧹 Cleaning..."
	rm -rf $(BUILD_DIR)
	go clean

## run: Run the application locally
run: build
	@echo "🚀 Starting $(APP_NAME)..."
	$(BUILD_DIR)/$(APP_NAME)

## deploy: Build and deploy (for CI use)
deploy: build
	@echo "📦 Deploying..."
	# Deployment handled by GitHub Actions

Deployment Checklist

Pre-Deployment

  • GitHub Secrets configured

    • SSH_PRIVATE_KEY added
    • SSH_HOST set
    • SSH_USER set
    • DEPLOY_PATH configured
  • VM Server prepared

    • SSH access configured
    • Deploy user created with sudo privileges
    • Application directory created (/opt/cv-site)
    • Systemd service installed
    • Firewall rules configured (port 8080)
  • Code repository ready

    • Workflow files in .github/workflows/
    • Deployment scripts in scripts/
    • Systemd service file in config/systemd/
    • Tests passing locally

First Deployment

  1. Push to GitHub: git push origin main
  2. Monitor Actions: Go to Actions tab in GitHub
  3. Check logs: Verify each step completes
  4. Test deployment: Visit your site URL
  5. Verify service: SSH to VM and run systemctl status cv-site

Post-Deployment

  • Health check passes
  • Service running: systemctl is-active cv-site
  • Logs clean: journalctl -u cv-site -n 50
  • Backup created: Previous version backed up
  • Rollback tested: Verify rollback script works

Troubleshooting

Common Issues

1. SSH Connection Failed

# Test SSH connection manually
ssh -i ~/.ssh/cv-site-deploy user@your-vm.example.com

# Check SSH key format
head -n 1 ~/.ssh/cv-site-deploy
# Should show: -----BEGIN OPENSSH PRIVATE KEY-----

2. Binary Won't Execute

# Check binary architecture
file /opt/cv-site/cv-site
# Should show: ELF 64-bit LSB executable, x86-64

# Check permissions
ls -la /opt/cv-site/cv-site
chmod +x /opt/cv-site/cv-site

3. Service Fails to Start

# Check service status
sudo systemctl status cv-site

# View logs
sudo journalctl -u cv-site -n 100 --no-pager

# Test binary manually
sudo -u www-data /opt/cv-site/cv-site

4. Health Check Timeout

# Test health endpoint locally on VM
curl http://localhost:8080/health

# Check if port is listening
sudo netstat -tlnp | grep 8080

# Check firewall
sudo ufw status

Advanced Features

Blue-Green Deployment

Modify deploy.sh to support zero-downtime deployments:

#!/bin/bash
# Blue-Green deployment strategy

BLUE_PORT=8080
GREEN_PORT=8081
CURRENT_PORT=$(cat /opt/cv-site/current_port)
NEW_PORT=$([[ $CURRENT_PORT == $BLUE_PORT ]] && echo $GREEN_PORT || echo $BLUE_PORT)

# Start new version on alternate port
PORT=$NEW_PORT /opt/cv-site/cv-site.new &

# Wait and health check
sleep 5
curl -f http://localhost:$NEW_PORT/health || exit 1

# Switch nginx upstream
sudo sed -i "s/localhost:$CURRENT_PORT/localhost:$NEW_PORT/" /etc/nginx/sites-enabled/cv-site
sudo systemctl reload nginx

# Stop old version
pkill -f "cv-site.*$CURRENT_PORT"

# Update current port
echo $NEW_PORT > /opt/cv-site/current_port

Rollback Strategy

#!/bin/bash
# scripts/rollback.sh

DEPLOY_PATH="/opt/cv-site"
BACKUP_PATH="$DEPLOY_PATH/releases"

# List available versions
echo "Available versions:"
ls -1t $BACKUP_PATH | head -5

# Rollback to previous version
PREVIOUS=$(ls -1t $BACKUP_PATH | head -1)
cp "$BACKUP_PATH/$PREVIOUS/cv-site" "$DEPLOY_PATH/cv-site"

sudo systemctl restart cv-site
echo "Rolled back to: $PREVIOUS"

Performance Optimization

Binary Size Reduction

# Use UPX compression (optional)
upx --best --lzma build/cv-site

# Result: ~70% size reduction
# Before: 15MB → After: 4.5MB

Caching Strategy

Add to workflow for faster builds:

- name: Cache Go modules
  uses: actions/cache@v3
  with:
    path: |
      ~/go/pkg/mod
      ~/.cache/go-build
    key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
    restore-keys: |
      ${{ runner.os }}-go-

Security Best Practices

  1. SSH Key Security

    • Use Ed25519 keys (stronger than RSA)
    • Never commit private keys
    • Rotate keys every 90 days
  2. Service Hardening

    • Run as non-root user (www-data)
    • Use systemd security features
    • Limit file system access
  3. Secret Management

    • Use GitHub Encrypted Secrets
    • Never log sensitive data
    • Use environment files on VM
  4. Network Security

    • Restrict SSH to GitHub Actions IPs (if possible)
    • Use firewall rules
    • Enable fail2ban

Monitoring & Logging

Add Logging to Deployment

- name: Log deployment
  run: |
    echo "Deployment started at $(date)" >> /var/log/cv-site/deploy.log
    echo "Commit: ${{ github.sha }}" >> /var/log/cv-site/deploy.log
    echo "Branch: ${{ github.ref_name }}" >> /var/log/cv-site/deploy.log

Application Monitoring

// Add to your Go app for health checks
func healthHandler(c *gin.Context) {
    c.JSON(200, gin.H{
        "status":  "healthy",
        "version": Version,
        "uptime":  time.Since(startTime).String(),
    })
}

Summary

Key Differences from La Porra

Aspect La Porra (Node/Bun) CV Site (Go)
Runtime Node.js/Bun Native binary
Build bun run build go build
Artifact /dist directory Single binary
Dependencies node_modules Statically linked
Deployment File sync Binary upload
Service PM2/systemd systemd

Deployment Flow

┌─────────────┐
│ Push to Git │
└──────┬──────┘
       │
       v
┌─────────────────┐
│ GitHub Actions  │
│ - Test          │
│ - Build Binary  │
│ - Create Artifact│
└──────┬──────────┘
       │
       v
┌─────────────────┐
│ Upload to VM    │
│ via SSH/SCP     │
└──────┬──────────┘
       │
       v
┌─────────────────┐
│ Execute deploy.sh│
│ - Backup old    │
│ - Install new   │
│ - Restart service│
└──────┬──────────┘
       │
       v
┌─────────────────┐
│ Health Check    │
│ - HTTP test     │
│ - Service status│
└──────┬──────────┘
       │
       v
┌─────────────────┐
│ Send Notification│
└─────────────────┘

Quick Start Commands

# 1. Clone repository
git clone https://github.com/juanatsap/cv-site.git
cd cv-site

# 2. Create workflow files
mkdir -p .github/workflows
# Copy workflow YAML files from this guide

# 3. Create deployment scripts
mkdir -p scripts config/systemd
# Copy scripts from this guide

# 4. Add secrets to GitHub
# Go to Settings → Secrets and variables → Actions
# Add all required secrets

# 5. Test locally
make test
make build

# 6. Deploy
git add .
git commit -m "Add GitHub Actions deployment"
git push origin main

Additional Resources


Last Updated: January 2025 Maintained By: Development Team Support: Create an issue in the repository