#!/bin/bash # Production Deployment Verification Script # Run this before and after production deployment set -e echo "╔════════════════════════════════════════════════════════════════╗" echo "║ DigiServer v2 Production Deployment Verification ║" echo "╚════════════════════════════════════════════════════════════════╝" TIMESTAMP=$(date +%Y-%m-%d\ %H:%M:%S) cd "$(dirname "$0")" # Color codes RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Counters PASSED=0 FAILED=0 WARNINGS=0 # Helper functions pass() { echo -e "${GREEN}✓${NC} $1" ((PASSED++)) } fail() { echo -e "${RED}✗${NC} $1" ((FAILED++)) } warn() { echo -e "${YELLOW}⚠${NC} $1" ((WARNINGS++)) } info() { echo -e "${BLUE}ℹ${NC} $1" } section() { echo -e "\n${BLUE}═══════════════════════════════════════${NC}" echo -e "${BLUE} $1${NC}" echo -e "${BLUE}═══════════════════════════════════════${NC}" } # ============================================================================ section "1. Git Status" # ============================================================================ if git rev-parse --git-dir > /dev/null 2>&1; then pass "Git repository initialized" BRANCH=$(git rev-parse --abbrev-ref HEAD) COMMIT=$(git rev-parse --short HEAD) info "Current branch: $BRANCH, Commit: $COMMIT" if [ -n "$(git status --porcelain)" ]; then warn "Uncommitted changes detected" git status --short else pass "All changes committed" fi else fail "Not a git repository" fi # ============================================================================ section "2. Environment Configuration" # ============================================================================ if [ -f .env ]; then pass ".env file exists" else warn ".env file not found (using defaults or docker-compose environment)" fi if [ -f .env.example ]; then pass ".env.example template exists" else warn ".env.example template missing" fi # ============================================================================ section "3. Docker Configuration" # ============================================================================ if command -v docker &> /dev/null; then pass "Docker installed" DOCKER_VERSION=$(docker --version | cut -d' ' -f3 | tr -d ',') info "Docker version: $DOCKER_VERSION" else fail "Docker not installed" fi if command -v docker-compose &> /dev/null; then pass "Docker Compose installed" DC_VERSION=$(docker-compose --version | cut -d' ' -f3 | tr -d ',') info "Docker Compose version: $DC_VERSION" else fail "Docker Compose not installed" fi if [ -f docker-compose.yml ]; then pass "docker-compose.yml exists" # Validate syntax if docker-compose config > /dev/null 2>&1; then pass "docker-compose.yml syntax valid" else fail "docker-compose.yml syntax error" fi else fail "docker-compose.yml not found" fi # ============================================================================ section "4. Dockerfile & Images" # ============================================================================ if [ -f Dockerfile ]; then pass "Dockerfile exists" # Check for security best practices if grep -q "HEALTHCHECK" Dockerfile; then pass "Health check configured" else warn "No health check in Dockerfile" fi if grep -q "USER appuser" Dockerfile || grep -q "USER.*:1000" Dockerfile; then pass "Non-root user configured" else warn "Root user may be used in container" fi if grep -q "FROM.*alpine\|FROM.*slim\|FROM.*distroless" Dockerfile; then pass "Minimal base image used" else warn "Large base image detected" fi else fail "Dockerfile not found" fi # ============================================================================ section "5. Python Dependencies" # ============================================================================ if [ -f requirements.txt ]; then pass "requirements.txt exists" PACKAGE_COUNT=$(wc -l < requirements.txt) info "Total packages: $PACKAGE_COUNT" # Check for critical packages for pkg in Flask SQLAlchemy gunicorn flask-cors cryptography; do if grep -q "$pkg" requirements.txt; then pass "$pkg installed" else warn "$pkg not found in requirements.txt" fi done # Check for specific versions FLASK_VERSION=$(grep "^Flask==" requirements.txt | cut -d'=' -f3) SQLALCHEMY_VERSION=$(grep "^SQLAlchemy==" requirements.txt | cut -d'=' -f3) if [ -n "$FLASK_VERSION" ]; then info "Flask version: $FLASK_VERSION" fi if [ -n "$SQLALCHEMY_VERSION" ]; then info "SQLAlchemy version: $SQLALCHEMY_VERSION" fi else fail "requirements.txt not found" fi # ============================================================================ section "6. Database Configuration" # ============================================================================ if [ -d migrations ]; then pass "migrations directory exists" MIGRATION_COUNT=$(find migrations -name "*.py" | wc -l) info "Migration files: $MIGRATION_COUNT" if [ "$MIGRATION_COUNT" -gt 0 ]; then pass "Database migrations configured" else warn "No migration files found" fi else warn "migrations directory not found" fi # ============================================================================ section "7. SSL/TLS Certificate" # ============================================================================ if [ -f data/nginx-ssl/cert.pem ]; then pass "SSL certificate found" CERT_EXPIRY=$(openssl x509 -enddate -noout -in data/nginx-ssl/cert.pem 2>/dev/null | cut -d= -f2) EXPIRY_EPOCH=$(date -d "$CERT_EXPIRY" +%s 2>/dev/null || echo 0) NOW_EPOCH=$(date +%s) DAYS_LEFT=$(( ($EXPIRY_EPOCH - $NOW_EPOCH) / 86400 )) info "Certificate expires: $CERT_EXPIRY" info "Days remaining: $DAYS_LEFT days" if [ "$DAYS_LEFT" -lt 0 ]; then fail "Certificate has expired!" elif [ "$DAYS_LEFT" -lt 30 ]; then warn "Certificate expires in less than 30 days" else pass "Certificate is valid" fi if [ -f data/nginx-ssl/key.pem ]; then pass "SSL private key found" else warn "SSL private key not found" fi else warn "SSL certificate not found (self-signed required)" fi # ============================================================================ section "8. Configuration Files" # ============================================================================ if [ -f app/config.py ]; then pass "Flask config.py exists" if grep -q "class ProductionConfig" app/config.py; then pass "ProductionConfig class defined" else warn "ProductionConfig class missing" fi if grep -q "SESSION_COOKIE_SECURE" app/config.py; then pass "SESSION_COOKIE_SECURE configured" else warn "SESSION_COOKIE_SECURE not configured" fi else fail "app/config.py not found" fi if [ -f nginx.conf ]; then pass "nginx.conf exists" if grep -q "ssl_protocols" nginx.conf; then pass "SSL protocols configured" else warn "SSL protocols not configured" fi if grep -q "access-control-allow" nginx.conf; then pass "CORS headers in nginx" else info "CORS headers may be handled by Flask only" fi else warn "nginx.conf not found" fi # ============================================================================ section "9. Runtime Verification" # ============================================================================ if docker-compose ps 2>/dev/null | grep -q "Up"; then pass "Docker containers are running" # Check if app is healthy if docker-compose ps 2>/dev/null | grep -q "digiserver-app.*healthy"; then pass "DigiServer app container is healthy" else warn "DigiServer app container health status unknown" fi if docker-compose ps 2>/dev/null | grep -q "digiserver-nginx.*healthy"; then pass "Nginx container is healthy" else warn "Nginx container health status unknown" fi else info "Docker containers not running (will start on deployment)" fi # ============================================================================ section "10. Security Best Practices" # ============================================================================ # Check for hardcoded secrets if grep -r "SECRET_KEY\|PASSWORD\|API_KEY" app/ 2>/dev/null | grep -v "os.getenv\|config.py\|#" | wc -l | grep -q "^0$"; then pass "No hardcoded secrets found" else warn "Possible hardcoded secrets detected (verify they use os.getenv)" fi # Check for debug mode if grep -q "DEBUG.*=.*True" app/config.py 2>/dev/null; then fail "DEBUG mode is enabled" else pass "DEBUG mode is disabled" fi # ============================================================================ section "Summary" # ============================================================================ echo "" echo -e "Test Results:" echo -e " ${GREEN}Passed: $PASSED${NC}" echo -e " ${YELLOW}Warnings: $WARNINGS${NC}" echo -e " ${RED}Failed: $FAILED${NC}" TOTAL=$((PASSED + FAILED + WARNINGS)) PERCENTAGE=$((PASSED * 100 / (PASSED + FAILED))) if [ "$FAILED" -eq 0 ]; then echo -e "\n${GREEN}✓ Production Deployment Ready!${NC}" echo "Recommendation: Safe to deploy to production" exit 0 elif [ "$FAILED" -le 2 ] && [ "$WARNINGS" -gt 0 ]; then echo -e "\n${YELLOW}⚠ Deployment Possible with Caution${NC}" echo "Recommendation: Address warnings before deployment" exit 0 else echo -e "\n${RED}✗ Deployment Not Recommended${NC}" echo "Recommendation: Fix critical failures before deployment" exit 1 fi