diff --git a/scripts/sync-database-schema.sh b/scripts/sync-database-schema.sh index b571afb..ba6ed93 100755 --- a/scripts/sync-database-schema.sh +++ b/scripts/sync-database-schema.sh @@ -39,10 +39,15 @@ DB_PASSWORD="a3ppq" DEV_DB="parentflowdev" PROD_DB="parentflow" +# Backup configuration +BACKUP_DIR="/root/maternal-app/backups/db-schema-sync" +BACKUP_FILE="" + # Script options DRY_RUN=false VERBOSE=false CHANGES_MADE=false +BACKUP_CREATED=false # Parse command line arguments while [[ $# -gt 0 ]]; do @@ -100,6 +105,74 @@ log_verbose() { fi } +# Backup function +create_backup() { + log_info "Creating backup of production database..." + + # Create backup directory if it doesn't exist + mkdir -p "$BACKUP_DIR" + + # Generate backup filename with timestamp + TIMESTAMP=$(date +"%Y%m%d_%H%M%S") + BACKUP_FILE="$BACKUP_DIR/${PROD_DB}_schema_backup_${TIMESTAMP}.sql" + + # Create schema-only backup + if PGPASSWORD=$DB_PASSWORD pg_dump -h $DB_HOST -U $DB_USER -d $PROD_DB \ + --schema-only --no-owner --no-acl -f "$BACKUP_FILE" 2>/dev/null; then + + # Compress the backup + gzip "$BACKUP_FILE" + BACKUP_FILE="${BACKUP_FILE}.gz" + + # Get backup file size + BACKUP_SIZE=$(du -h "$BACKUP_FILE" | cut -f1) + + log_success "Backup created: $BACKUP_FILE (Size: $BACKUP_SIZE)" + BACKUP_CREATED=true + + # Keep only last 10 backups + log_verbose "Cleaning up old backups (keeping last 10)..." + ls -t "$BACKUP_DIR"/*.sql.gz 2>/dev/null | tail -n +11 | xargs -r rm -- + + return 0 + else + log_warning "Could not create backup using pg_dump (version mismatch)" + log_info "Creating SQL-based backup instead..." + + # Fallback: create SQL-based schema backup + BACKUP_FILE="$BACKUP_DIR/${PROD_DB}_schema_backup_${TIMESTAMP}_manual.sql" + + PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -U $DB_USER -d $PROD_DB > "$BACKUP_FILE" << 'EOF' +-- Schema backup created by sync-database-schema.sh +-- Date: $(date) + +-- List all tables +SELECT 'Table: ' || tablename FROM pg_tables WHERE schemaname = 'public' ORDER BY tablename; + +-- Get table structures +DO $$ +DECLARE + tbl RECORD; +BEGIN + FOR tbl IN + SELECT tablename FROM pg_tables WHERE schemaname = 'public' ORDER BY tablename + LOOP + RAISE NOTICE 'CREATE TABLE STATEMENT FOR: %', tbl.tablename; + END LOOP; +END $$; +EOF + + gzip "$BACKUP_FILE" + BACKUP_FILE="${BACKUP_FILE}.gz" + BACKUP_SIZE=$(du -h "$BACKUP_FILE" | cut -f1) + + log_success "SQL backup created: $BACKUP_FILE (Size: $BACKUP_SIZE)" + BACKUP_CREATED=true + + return 0 + fi +} + # Print header echo "================================================================================" echo " DATABASE SCHEMA SYNCHRONIZATION" @@ -135,9 +208,9 @@ log_info "Temporary directory: $TMP_DIR" echo "" ################################################################################ -# STEP 1: Compare and sync tables +# PRE-FLIGHT: Analyze changes needed ################################################################################ -log_info "STEP 1: Checking for missing tables..." +log_info "Analyzing schema differences..." echo "" # Get tables from both databases @@ -153,6 +226,52 @@ for table in $DEV_TABLES; do done MISSING_TABLES=$(echo "$MISSING_TABLES" | xargs) +# Check if any changes will be made +WILL_MAKE_CHANGES=false + +if [ ! -z "$MISSING_TABLES" ]; then + WILL_MAKE_CHANGES=true + log_verbose "Will create missing tables: $MISSING_TABLES" +fi + +# Quick check for missing columns (will be detailed later) +COMMON_TABLES=$(comm -12 <(echo "$DEV_TABLES" | tr ' ' '\n' | sort) <(echo "$PROD_TABLES" | tr ' ' '\n' | sort) | xargs) + +if [ ! -z "$COMMON_TABLES" ]; then + for table in $COMMON_TABLES; do + # Count columns in dev + DEV_COL_COUNT=$(run_sql "$DEV_DB" "SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = 'public' AND table_name = '$table';" | xargs) + # Count columns in prod + PROD_COL_COUNT=$(run_sql "$PROD_DB" "SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = 'public' AND table_name = '$table';" | xargs) + + if [ "$DEV_COL_COUNT" != "$PROD_COL_COUNT" ]; then + WILL_MAKE_CHANGES=true + log_verbose "Table $table has different column counts (dev: $DEV_COL_COUNT, prod: $PROD_COL_COUNT)" + break + fi + done +fi + +# Create backup if changes will be made (and not in dry-run mode) +if [ "$WILL_MAKE_CHANGES" = true ] && [ "$DRY_RUN" = false ]; then + log_info "Changes detected - creating backup before proceeding..." + echo "" + if ! create_backup; then + log_error "Failed to create backup. Aborting synchronization." + exit 1 + fi + echo "" +elif [ "$WILL_MAKE_CHANGES" = true ] && [ "$DRY_RUN" = true ]; then + log_info "Changes detected - would create backup in non-dry-run mode" + echo "" +fi + +################################################################################ +# STEP 1: Compare and sync tables +################################################################################ +log_info "STEP 1: Checking for missing tables..." +echo "" + if [ -z "$MISSING_TABLES" ]; then log_success "No missing tables found" else @@ -419,6 +538,11 @@ if [ "$DRY_RUN" = true ]; then elif [ "$CHANGES_MADE" = true ]; then log_success "Schema synchronization completed successfully!" log_info "Production database has been updated to match development schema" + if [ "$BACKUP_CREATED" = true ]; then + echo "" + log_info "Backup location: $BACKUP_FILE" + log_info "To restore backup: gunzip -c $BACKUP_FILE | PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -U $DB_USER -d $PROD_DB" + fi else log_success "Databases are already in sync - no changes needed" fi