# 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](#overview) 2. [Required GitHub Secrets](#required-github-secrets) 3. [Workflow Files Structure](#workflow-files-structure) 4. [Main Deployment Workflow](#main-deployment-workflow) 5. [CDN Integration (Optional)](#cdn-integration-optional) 6. [Documentation Deployment](#documentation-deployment) 7. [Build Artifacts](#build-artifacts) 8. [Environment Configuration](#environment-configuration) 9. [Testing Strategy](#testing-strategy) 10. [Deployment Checklist](#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 ```bash # 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` ```yaml 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` ```yaml 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 ```bash 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` ```bash #!/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` ```bash #!/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` ```ini [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 ```bash # 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` ```yaml 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: ```makefile .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 ```bash # 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 ```bash # 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 ```bash # 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 ```bash # 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: ```bash #!/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 ```bash #!/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 ```bash # 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: ```yaml - 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 ```yaml - 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 ```go // 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 ```bash # 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 - **GitHub Actions Documentation**: https://docs.github.com/en/actions - **Go Build Documentation**: https://pkg.go.dev/cmd/go#hdr-Compile_packages_and_dependencies - **Systemd Service Files**: https://www.freedesktop.org/software/systemd/man/systemd.service.html - **SSH Key Management**: https://docs.github.com/en/authentication/connecting-to-github-with-ssh --- **Last Updated**: January 2025 **Maintained By**: Development Team **Support**: Create an issue in the repository