Files
biblical-guide.com/temp/git-deployment-strategy.md
Claude Assistant ee99e93ec2 Implement dynamic daily verse system with rotating Biblical content
- Add daily-verse API endpoint with 7 rotating verses in Romanian and English
- Replace static homepage verse with dynamic fetch from API
- Ensure consistent daily rotation using day-of-year calculation
- Support both ro and en locales for verse content

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-22 19:22:34 +00:00

10 KiB

Git-Based Production Deployment Strategy

Overview

This guide covers how to deploy the latest code from your git repository to production, ensuring your local development changes are properly deployed.

Prerequisites

  • Git repository (GitHub, GitLab, Bitbucket, or self-hosted)
  • Production server with Docker and Git installed
  • SSH access to production server

Deployment Strategies

Option 1: Manual Git Pull Deployment (Simple)

Setup on Production Server

# Clone the repository on production server
cd /opt
git clone https://github.com/yourusername/ghidul-biblic.git
cd ghidul-biblic

# Create production environment file
cp .env.example .env.production
# Edit .env.production with production values

Create Deployment Script

Create /opt/ghidul-biblic/deploy.sh:

#!/bin/bash
set -e

echo "🚀 Starting deployment..."

# Navigate to project directory
cd /opt/ghidul-biblic

# Stop current services
echo "⏹️  Stopping current services..."
docker-compose -f docker-compose.prod.simple.yml down

# Pull latest code
echo "📥 Pulling latest code..."
git pull origin main

# Load environment variables
export $(cat .env.production | xargs)

# Build and start services
echo "🔨 Building and starting services..."
docker-compose -f docker-compose.prod.simple.yml up -d --build

# Wait for health check
echo "🏥 Waiting for health check..."
sleep 30
curl -f http://localhost:3010/api/health || {
    echo "❌ Health check failed!"
    docker-compose -f docker-compose.prod.simple.yml logs app
    exit 1
}

echo "✅ Deployment successful!"
echo "🌐 Application is running at http://localhost:3010"

Make it executable:

chmod +x /opt/ghidul-biblic/deploy.sh

Deploy from Local Machine

# Push your changes to git
git add .
git commit -m "Your changes"
git push origin main

# SSH into production and deploy
ssh user@your-server "cd /opt/ghidul-biblic && ./deploy.sh"

Setup Webhook Server on Production

Create /opt/ghidul-biblic/webhook-server.js:

const express = require('express');
const { execSync } = require('child_process');
const crypto = require('crypto');

const app = express();
const PORT = 9000;
const SECRET = process.env.WEBHOOK_SECRET || 'your-webhook-secret';

app.use(express.json());

function verifySignature(payload, signature) {
    const hmac = crypto.createHmac('sha256', SECRET);
    const digest = 'sha256=' + hmac.update(payload).digest('hex');
    return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
}

app.post('/webhook', (req, res) => {
    const signature = req.headers['x-hub-signature-256'];
    const payload = JSON.stringify(req.body);
    
    if (!verifySignature(payload, signature)) {
        return res.status(401).send('Unauthorized');
    }
    
    // Only deploy on push to main branch
    if (req.body.ref === 'refs/heads/main') {
        console.log('🚀 Webhook received, starting deployment...');
        
        try {
            execSync('/opt/ghidul-biblic/deploy.sh', { 
                stdio: 'inherit',
                cwd: '/opt/ghidul-biblic'
            });
            res.status(200).send('Deployment successful');
        } catch (error) {
            console.error('Deployment failed:', error);
            res.status(500).send('Deployment failed');
        }
    } else {
        res.status(200).send('Not main branch, skipping deployment');
    }
});

app.listen(PORT, () => {
    console.log(`Webhook server running on port ${PORT}`);
});

Setup Webhook Service

Create /etc/systemd/system/ghidul-webhook.service:

[Unit]
Description=Ghidul Biblic Webhook Server
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/opt/ghidul-biblic
ExecStart=/usr/bin/node webhook-server.js
Restart=always
Environment=WEBHOOK_SECRET=your-webhook-secret-here

[Install]
WantedBy=multi-user.target

Enable and start the service:

systemctl enable ghidul-webhook.service
systemctl start ghidul-webhook.service
systemctl status ghidul-webhook.service

Configure Git Repository Webhook

For GitHub:

  1. Go to your repository → Settings → Webhooks
  2. Add webhook:
    • URL: http://your-server:9000/webhook
    • Content type: application/json
    • Secret: your-webhook-secret-here
    • Events: Just the push event

For GitLab:

  1. Go to your project → Settings → Webhooks
  2. Add webhook:
    • URL: http://your-server:9000/webhook
    • Secret token: your-webhook-secret-here
    • Trigger: Push events

Option 3: GitHub Actions CI/CD (Advanced)

Create GitHub Actions Workflow

Create .github/workflows/deploy.yml:

name: Deploy to Production

on:
  push:
    branches: [ main ]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
    - name: Deploy to server
      uses: appleboy/ssh-action@v0.1.5
      with:
        host: ${{ secrets.HOST }}
        username: ${{ secrets.USERNAME }}
        key: ${{ secrets.SSH_KEY }}
        script: |
          cd /opt/ghidul-biblic
          ./deploy.sh

Setup GitHub Secrets

In your GitHub repository, go to Settings → Secrets and variables → Actions:

  • HOST: Your production server IP
  • USERNAME: SSH username
  • SSH_KEY: Private SSH key content

Option 4: Docker Hub Auto-Build (Professional)

Setup Dockerfile for Production

Ensure your docker/Dockerfile.prod is optimized:

FROM node:20-alpine AS builder

WORKDIR /app

# Copy package files
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force

# Copy source code
COPY . .

# Generate Prisma client
RUN npx prisma generate

# Build application
RUN npm run build

FROM node:20-alpine AS runner

WORKDIR /app

ENV NODE_ENV production

# Create non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001

# Copy built application
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma
COPY --from=builder /app/package.json ./package.json

USER nextjs

EXPOSE 3000

ENV PORT 3000
ENV HOSTNAME "0.0.0.0"

CMD ["node", "server.js"]

Create Docker Compose for Auto-Pull

Create docker-compose.prod.autopull.yml:

version: '3.8'

services:
  postgres:
    image: pgvector/pgvector:pg16
    restart: always
    environment:
      POSTGRES_DB: bible_chat
      POSTGRES_USER: bible_admin
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./scripts/init.sql:/docker-entrypoint-initdb.d/init.sql
    networks:
      - bible_network
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U bible_admin -d bible_chat"]
      interval: 30s
      timeout: 10s
      retries: 3

  app:
    image: yourdockerhub/ghidul-biblic:latest
    restart: always
    ports:
      - "3010:3000"
    environment:
      DATABASE_URL: postgresql://bible_admin:${DB_PASSWORD}@postgres:5432/bible_chat
      AZURE_OPENAI_KEY: ${AZURE_OPENAI_KEY}
      AZURE_OPENAI_ENDPOINT: ${AZURE_OPENAI_ENDPOINT}
      AZURE_OPENAI_DEPLOYMENT: ${AZURE_OPENAI_DEPLOYMENT}
      OLLAMA_API_URL: ${OLLAMA_API_URL}
      JWT_SECRET: ${JWT_SECRET}
      NEXTAUTH_URL: ${NEXTAUTH_URL}
      NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
      NODE_ENV: production
    depends_on:
      postgres:
        condition: service_healthy
    networks:
      - bible_network
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:3000/api/health || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3

  watchtower:
    image: containrrr/watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command: --interval 300 --cleanup app
    restart: always

networks:
  bible_network:
    driver: bridge

volumes:
  postgres_data:

For Development

  1. Local Development:

    # Work on your local machine
    npm run dev
    # Make changes, test locally
    
  2. Commit and Push:

    git add .
    git commit -m "Feature: Add new functionality"
    git push origin main
    
  3. Automatic Deployment:

    • Webhook triggers deployment
    • Or manual trigger: ssh user@server "/opt/ghidul-biblic/deploy.sh"

For Production Server Setup

  1. Initial Setup:

    # Clone repository
    git clone https://github.com/yourusername/ghidul-biblic.git /opt/ghidul-biblic
    cd /opt/ghidul-biblic
    
    # Setup environment
    cp .env.example .env.production
    # Edit .env.production
    
    # Make deployment script executable
    chmod +x deploy.sh
    
    # Setup webhook (optional)
    npm install express
    # Setup systemd service
    
  2. Deploy:

    ./deploy.sh
    

Security Considerations

Git Repository

  • Use private repositories for production code
  • Use deploy keys or personal access tokens
  • Never commit sensitive environment variables

Production Server

  • Use non-root user for deployment when possible
  • Secure webhook endpoints with proper secrets
  • Use SSH key authentication
  • Keep server updated

Environment Variables

# .env.production should never be in git
echo ".env.production" >> .gitignore
echo ".env.local" >> .gitignore

Monitoring Deployment

Create Health Check Script

Create /opt/ghidul-biblic/health-check.sh:

#!/bin/bash

HEALTH_URL="http://localhost:3010/api/health"
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" $HEALTH_URL)

if [ $RESPONSE -eq 200 ]; then
    echo "✅ Application is healthy"
    exit 0
else
    echo "❌ Application is unhealthy (HTTP $RESPONSE)"
    exit 1
fi

Setup Cron for Health Monitoring

# Add to crontab
*/5 * * * * /opt/ghidul-biblic/health-check.sh >> /var/log/ghidul-health.log 2>&1

Troubleshooting

Common Issues

  1. Git pull fails: Check SSH keys and repository access
  2. Docker build fails: Check Dockerfile and dependencies
  3. Health check fails: Check application logs
  4. Webhook not triggered: Verify webhook URL and secret

Useful Commands

# Check deployment logs
tail -f /var/log/ghidul-deployment.log

# Check application logs
docker-compose -f docker-compose.prod.simple.yml logs -f app

# Manual health check
curl http://localhost:3010/api/health

# Restart services
docker-compose -f docker-compose.prod.simple.yml restart app

This setup ensures your production environment always has the latest code from your git repository while maintaining proper deployment practices.