# 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 ```bash # 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`: ```bash #!/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: ```bash chmod +x /opt/ghidul-biblic/deploy.sh ``` #### Deploy from Local Machine ```bash # 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" ``` ### Option 2: Webhook-Based Auto Deployment (Recommended) #### Setup Webhook Server on Production Create `/opt/ghidul-biblic/webhook-server.js`: ```javascript 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`: ```ini [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: ```bash 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`: ```yaml 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: ```dockerfile 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`: ```yaml 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: ``` ## Recommended Workflow ### For Development 1. **Local Development**: ```bash # Work on your local machine npm run dev # Make changes, test locally ``` 2. **Commit and Push**: ```bash 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**: ```bash # 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**: ```bash ./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 ```bash # .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`: ```bash #!/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 ```bash # 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 ```bash # 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.