From 85ebac761d4dbfbb277e5f26991918f9d4f5738c Mon Sep 17 00:00:00 2001 From: amitrana01 Date: Thu, 11 Sep 2025 18:52:42 +0530 Subject: [PATCH] Add comprehensive deployment automation and prevention system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🎯 Future-proof deployment system: 1. DEPLOYMENT_CHECKLIST.md - Zero-fail step-by-step guide 2. scripts/pre-deploy-check.sh - Validates everything before deployment 3. scripts/post-deploy-verify.sh - Tests deployment after completion 4. .github/workflows/deploy.yml - Automated GitHub Actions validation ✅ Key features: - Catches static files, migrations, Docker issues before deployment - Provides clear error messages and next steps - Tests all endpoints after deployment - 5-minute deployment target with this system - Copy-paste templates for new projects Run: ./scripts/pre-deploy-check.sh before every deployment Run: ./scripts/post-deploy-verify.sh after deployment Never face deployment issues again! 🚀 --- .github/workflows/deploy.yml | 89 +++++++++++++++++++ DEPLOYMENT_CHECKLIST.md | 162 ++++++++++++++++++++++++++++++++++ scripts/post-deploy-verify.sh | 158 +++++++++++++++++++++++++++++++++ scripts/pre-deploy-check.sh | 104 ++++++++++++++++++++++ 4 files changed, 513 insertions(+) create mode 100644 .github/workflows/deploy.yml create mode 100644 DEPLOYMENT_CHECKLIST.md create mode 100755 scripts/post-deploy-verify.sh create mode 100755 scripts/pre-deploy-check.sh diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..eb8b877 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,89 @@ +name: 🚀 Deploy to Dokploy + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + workflow_dispatch: + +jobs: + pre-deploy-checks: + runs-on: ubuntu-latest + name: 🔍 Pre-deployment validation + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python 3.11 + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + + - name: Cache Python dependencies + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('requirements/production.txt') }} + + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements/production.txt + + - name: Install and build Tailwind CSS + run: | + cd theme/static_src + npm install + npm run build + + - name: Run Django system checks + run: | + export DJANGO_SETTINGS_MODULE=django_project.settings.production + export SECRET_KEY=github-actions-secret-key + export ALLOWED_HOSTS=localhost + export DEBUG=False + python manage.py check --deploy + + - name: Test static file collection + run: | + export DJANGO_SETTINGS_MODULE=django_project.settings.build + python manage.py collectstatic --noinput --dry-run + + - name: Check for pending migrations + run: | + export DJANGO_SETTINGS_MODULE=django_project.settings.production + export SECRET_KEY=github-actions-secret-key + export DATABASE_URL=sqlite:///temp.db + python manage.py makemigrations --check --dry-run + + - name: Test Docker build + run: | + docker build --target production -t django-template-test . + docker rmi django-template-test + + deploy: + runs-on: ubuntu-latest + name: 📤 Deploy notification + needs: pre-deploy-checks + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + + steps: + - name: ✅ Pre-deployment checks passed + run: | + echo "🎉 All pre-deployment checks passed!" + echo "🚀 Ready for deployment to Dokploy" + echo "📋 Manual steps required:" + echo " 1. Trigger deployment in Dokploy dashboard" + echo " 2. Wait for build completion" + echo " 3. Run post-deployment verification" + + - name: 📝 Deployment reminder + if: success() + run: | + echo "::notice::Deployment ready! Trigger deployment in Dokploy manually" diff --git a/DEPLOYMENT_CHECKLIST.md b/DEPLOYMENT_CHECKLIST.md new file mode 100644 index 0000000..891311b --- /dev/null +++ b/DEPLOYMENT_CHECKLIST.md @@ -0,0 +1,162 @@ +# 🚀 Django Deployment Checklist - Zero-Fail Guide + +Use this checklist to deploy Django projects smoothly every time. + +## ✅ **Pre-Deployment Checklist** + +### **1. Static Files & Assets** +- [ ] **Build Tailwind CSS locally**: `npm run build` in theme/static_src/ +- [ ] **Verify CSS files exist**: Check `theme/static/css/dist/styles.css` exists +- [ ] **Test collectstatic locally**: `python manage.py collectstatic --noinput` +- [ ] **Commit static files**: Ensure built assets are in Git + +### **2. Database & Migrations** +- [ ] **Create migrations**: `python manage.py makemigrations` +- [ ] **Test migrations locally**: `python manage.py migrate` +- [ ] **Verify Sites framework**: Check `django.contrib.sites` in INSTALLED_APPS +- [ ] **Test site creation**: Run `python manage.py configure_site` locally + +### **3. Environment Configuration** +- [ ] **Update .env.example**: Include all required variables +- [ ] **Document required vars**: List in README what's needed for production +- [ ] **Test with production settings**: `DJANGO_SETTINGS_MODULE=project.settings.production` + +### **4. Docker Configuration** +- [ ] **Test Docker build**: `docker build --target production .` +- [ ] **Verify startup script**: Ensure `startup.sh` is executable +- [ ] **Test compose file**: `docker-compose -f docker-compose.dokploy-simple.yml up` + +## 🔧 **Deployment Steps (Dokploy)** + +### **Step 1: Repository Setup** +- [ ] Push all changes to main branch +- [ ] Verify compose file path is correct in Dokploy +- [ ] Check Dockerfile builds successfully + +### **Step 2: Environment Variables** +Set these in Dokploy environment tab: +```env +SECRET_KEY=your-long-secret-key-here +DEBUG=False +ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com +CSRF_TRUSTED_ORIGINS=https://yourdomain.com +SECURE_SSL_REDIRECT=True +DATABASE_URL=postgresql://... (if using external DB) +``` + +### **Step 3: Domain Configuration** +- [ ] DNS: Point domain to Dokploy server IP +- [ ] Dokploy Domain: Set service=web, port=8000 +- [ ] HTTPS: Enable SSL/Auto SSL +- [ ] Wait 2-3 minutes for SSL certificate + +### **Step 4: Deploy & Verify** +- [ ] Deploy application +- [ ] Check deployment logs for errors +- [ ] Test endpoints: + - [ ] Homepage: `https://yourdomain.com/` + - [ ] Health: `https://yourdomain.com/health/` + - [ ] Login: `https://yourdomain.com/accounts/login/` + - [ ] Admin: `https://yourdomain.com/admin/` + +## 🛡️ **Common Issues Prevention** + +### **Static Files** +✅ **DO**: Build static files during Docker build +❌ **DON'T**: Rely on runtime collectstatic with volume mounts + +### **Database Migrations** +✅ **DO**: Run explicit site migration: `migrate sites` +❌ **DON'T**: Assume all migrations run automatically + +### **Environment Variables** +✅ **DO**: Use environment variables for all settings +❌ **DON'T**: Hardcode production values in settings files + +### **Docker Configuration** +✅ **DO**: Use simple port mapping (ports: "8000:8000") +❌ **DON'T**: Use complex Traefik labels unless necessary + +## 🚀 **Quick Deploy Templates** + +### **For New Projects** (Copy-Paste Ready) + +**docker-compose.dokploy.yml:** +```yaml +services: + web: + build: . + ports: + - "8000:8000" + environment: + - DEBUG=${DEBUG:-False} + - ALLOWED_HOSTS=${ALLOWED_HOSTS} + - SECRET_KEY=${SECRET_KEY} + - DATABASE_URL=${DATABASE_URL:-postgresql://django_user:django_password@db:5432/django_db} + command: sh -c "chmod +x /app/startup.sh && /app/startup.sh" + depends_on: + - db + + db: + image: postgres:15-alpine + environment: + - POSTGRES_DB=django_db + - POSTGRES_USER=django_user + - POSTGRES_PASSWORD=django_password + volumes: + - postgres_data:/var/lib/postgresql/data/ + +volumes: + postgres_data: +``` + +**startup.sh:** +```bash +#!/bin/bash +set -e +python manage.py migrate --noinput +python manage.py migrate sites --noinput +python manage.py collectstatic --noinput +exec gunicorn --bind 0.0.0.0:8000 django_project.wsgi:application +``` + +## 📋 **Environment Variables Template** +```env +SECRET_KEY=generate-long-random-key-here +DEBUG=False +ALLOWED_HOSTS=yourdomain.com +CSRF_TRUSTED_ORIGINS=https://yourdomain.com +SECURE_SSL_REDIRECT=True +DATABASE_URL=postgresql://user:pass@host:5432/db +``` + +## 🎯 **Time to Deploy: 5 Minutes** + +With this checklist, deployment should take: +- ⏱️ **2 min**: Environment setup +- ⏱️ **2 min**: Docker build & deploy +- ⏱️ **1 min**: SSL certificate generation + +**Total: 5 minutes from code to live site!** 🚀 + +## 🆘 **Emergency Debugging** + +If deployment fails: +1. **Check logs**: Look for specific error messages +2. **Test health endpoint**: `/health/` shows Django status +3. **Verify environment**: Check env vars are set correctly +4. **Database connection**: Ensure DATABASE_URL is correct +5. **Static files**: Verify CSS files exist in image + +## 🔄 **Automation Ideas** + +Future improvements: +- [ ] GitHub Actions for automated deployments +- [ ] Pre-commit hooks for static file builds +- [ ] Docker health checks +- [ ] Automated backup scripts +- [ ] Monitoring and alerting setup + +--- + +**Follow this checklist and you'll never have deployment issues again!** ✅ diff --git a/scripts/post-deploy-verify.sh b/scripts/post-deploy-verify.sh new file mode 100755 index 0000000..e0e5aa4 --- /dev/null +++ b/scripts/post-deploy-verify.sh @@ -0,0 +1,158 @@ +#!/bin/bash + +# Post-deployment verification script +# Run this after deployment to verify everything is working + +set -e + +# Default domain (can be overridden) +DOMAIN=${1:-"dt.netcoptech.com"} +PROTOCOL=${2:-"https"} + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +echo "🔍 Verifying deployment for $PROTOCOL://$DOMAIN" + +# Function to test URL +test_url() { + local url=$1 + local expected_status=${2:-200} + local description=$3 + + echo "Testing: $description" + + # Use curl to test the URL + if command -v curl > /dev/null 2>&1; then + response=$(curl -s -o /dev/null -w "%{http_code}" -L "$url" 2>/dev/null || echo "000") + else + echo -e "${YELLOW}⚠️ curl not found, skipping URL test${NC}" + return 0 + fi + + if [ "$response" -eq "$expected_status" ]; then + echo -e "${GREEN}✅ $description - HTTP $response${NC}" + elif [ "$response" -eq "000" ]; then + echo -e "${RED}❌ $description - Connection failed${NC}" + return 1 + else + echo -e "${RED}❌ $description - HTTP $response (expected $expected_status)${NC}" + return 1 + fi +} + +# Function to test JSON endpoint +test_json_endpoint() { + local url=$1 + local description=$2 + + echo "Testing: $description" + + if command -v curl > /dev/null 2>&1; then + response=$(curl -s -L "$url" 2>/dev/null || echo "") + if echo "$response" | python3 -m json.tool > /dev/null 2>&1; then + echo -e "${GREEN}✅ $description - Valid JSON response${NC}" + # Pretty print the response + echo "$response" | python3 -m json.tool | head -10 + else + echo -e "${RED}❌ $description - Invalid JSON response${NC}" + return 1 + fi + else + echo -e "${YELLOW}⚠️ curl not found, skipping JSON test${NC}" + return 0 + fi +} + +echo "" +echo "🌐 Testing core endpoints..." + +# Test main endpoints +test_url "$PROTOCOL://$DOMAIN/" 200 "Homepage" +test_json_endpoint "$PROTOCOL://$DOMAIN/health/" "Health check endpoint" +test_url "$PROTOCOL://$DOMAIN/accounts/login/" 200 "Login page" +test_url "$PROTOCOL://$DOMAIN/admin/" 302 "Admin panel (should redirect to login)" + +echo "" +echo "🔒 Testing SSL/HTTPS..." + +if [ "$PROTOCOL" = "https" ]; then + # Test SSL certificate + if command -v openssl > /dev/null 2>&1; then + echo "Checking SSL certificate..." + cert_info=$(echo | openssl s_client -servername "$DOMAIN" -connect "$DOMAIN:443" 2>/dev/null | openssl x509 -noout -dates 2>/dev/null || echo "") + + if [ -n "$cert_info" ]; then + echo -e "${GREEN}✅ SSL certificate is valid${NC}" + echo "$cert_info" + else + echo -e "${RED}❌ SSL certificate check failed${NC}" + fi + else + echo -e "${YELLOW}⚠️ openssl not found, skipping SSL check${NC}" + fi + + # Test HTTP to HTTPS redirect + if command -v curl > /dev/null 2>&1; then + redirect_response=$(curl -s -o /dev/null -w "%{redirect_url}" "http://$DOMAIN/" || echo "") + if [[ "$redirect_response" == "https://"* ]]; then + echo -e "${GREEN}✅ HTTP to HTTPS redirect working${NC}" + else + echo -e "${YELLOW}⚠️ HTTP to HTTPS redirect not detected${NC}" + fi + fi +else + echo "Testing HTTP deployment..." +fi + +echo "" +echo "🗄️ Testing database connectivity..." + +# Test if site is configured properly by checking login page +login_response=$(curl -s -L "$PROTOCOL://$DOMAIN/accounts/login/" 2>/dev/null || echo "") +if echo "$login_response" | grep -q "django_site" 2>/dev/null; then + echo -e "${RED}❌ Database error: django_site relation not found${NC}" +elif echo "$login_response" | grep -q "Login" 2>/dev/null; then + echo -e "${GREEN}✅ Database connectivity working${NC}" +else + echo -e "${YELLOW}⚠️ Unable to verify database connectivity${NC}" +fi + +echo "" +echo "📱 Testing responsive design..." + +# Test if CSS is loading +css_test=$(curl -s -L "$PROTOCOL://$DOMAIN/" 2>/dev/null | grep -o "styles\.css" || echo "") +if [ -n "$css_test" ]; then + echo -e "${GREEN}✅ CSS files are being loaded${NC}" +else + echo -e "${YELLOW}⚠️ CSS files may not be loading properly${NC}" +fi + +echo "" +echo "🎯 Deployment verification summary:" + +# Final health check +health_url="$PROTOCOL://$DOMAIN/health/" +if curl -s "$health_url" > /dev/null 2>&1; then + echo -e "${GREEN}🎉 Deployment successful!${NC}" + echo -e "${GREEN}✅ Your Django app is live and healthy${NC}" + echo "" + echo "🔗 Important URLs:" + echo " Homepage: $PROTOCOL://$DOMAIN/" + echo " Login: $PROTOCOL://$DOMAIN/accounts/login/" + echo " Admin: $PROTOCOL://$DOMAIN/admin/" + echo " Health: $PROTOCOL://$DOMAIN/health/" + echo "" + echo "Next steps:" + echo "1. Create your admin user: python manage.py createsuperuser" + echo "2. Configure social authentication in admin panel" + echo "3. Test all functionality thoroughly" +else + echo -e "${RED}❌ Deployment verification failed${NC}" + echo "Check deployment logs and retry" + exit 1 +fi diff --git a/scripts/pre-deploy-check.sh b/scripts/pre-deploy-check.sh new file mode 100755 index 0000000..d4729f8 --- /dev/null +++ b/scripts/pre-deploy-check.sh @@ -0,0 +1,104 @@ +#!/bin/bash + +# Pre-deployment validation script +# Run this before every deployment to catch issues early + +set -e + +echo "🔍 Running pre-deployment checks..." + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Function to print status +print_status() { + if [ $1 -eq 0 ]; then + echo -e "${GREEN}✅ $2${NC}" + else + echo -e "${RED}❌ $2${NC}" + exit 1 + fi +} + +print_warning() { + echo -e "${YELLOW}⚠️ $1${NC}" +} + +# Check 1: Verify Tailwind CSS is built +echo "📦 Checking Tailwind CSS files..." +if [ -f "theme/static/css/dist/styles.css" ]; then + print_status 0 "Tailwind CSS files exist" +else + print_status 1 "Tailwind CSS not built. Run: cd theme/static_src && npm run build" +fi + +# Check 2: Test collectstatic +echo "📁 Testing static file collection..." +if python manage.py collectstatic --noinput --dry-run > /dev/null 2>&1; then + print_status 0 "Static files collection test passed" +else + print_status 1 "Static files collection failed. Check STATIC_ROOT and STATICFILES_DIRS" +fi + +# Check 3: Check for pending migrations +echo "🗄️ Checking for pending migrations..." +if python manage.py showmigrations --plan | grep -q "\[ \]"; then + print_status 1 "Pending migrations found. Run: python manage.py makemigrations" +else + print_status 0 "No pending migrations" +fi + +# Check 4: Validate Django configuration +echo "⚙️ Validating Django settings..." +if python manage.py check > /dev/null 2>&1; then + print_status 0 "Django configuration valid" +else + print_status 1 "Django configuration issues found. Run: python manage.py check" +fi + +# Check 5: Test Docker build +echo "🐳 Testing Docker build..." +if docker build --target production -t django-deploy-test . > /dev/null 2>&1; then + print_status 0 "Docker build successful" + docker rmi django-deploy-test > /dev/null 2>&1 || true +else + print_status 1 "Docker build failed. Check Dockerfile" +fi + +# Check 6: Verify startup script +echo "🚀 Checking startup script..." +if [ -x "startup.sh" ]; then + print_status 0 "Startup script is executable" +else + print_status 1 "Startup script not executable. Run: chmod +x startup.sh" +fi + +# Check 7: Validate environment variables +echo "🔐 Checking environment configuration..." +if [ -f ".env.example" ]; then + print_status 0 "Environment example file exists" +else + print_warning "No .env.example file found. Consider creating one" +fi + +# Check 8: Verify required files +required_files=("manage.py" "requirements/production.txt" "docker-compose.dokploy-simple.yml") +for file in "${required_files[@]}"; do + if [ -f "$file" ]; then + print_status 0 "Required file exists: $file" + else + print_status 1 "Missing required file: $file" + fi +done + +echo "" +echo -e "${GREEN}🎉 All pre-deployment checks passed!${NC}" +echo -e "${GREEN}✅ Ready for deployment!${NC}" +echo "" +echo "Next steps:" +echo "1. git add . && git commit -m 'Ready for deployment' && git push" +echo "2. Deploy in Dokploy" +echo "3. Test endpoints after deployment"