#!/bin/bash # validate-env-backups.sh - Validate .env file backups # Author: Shell Repository # Description: Verify integrity and consistency of .env file backups set -e # Colors for output GREEN='\033[0;32m' YELLOW='\033[0;33m' RED='\033[0;31m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Configuration SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DOCKER_DIR="$HOME/docker" BACKUP_DIR="$HOME/.env-backup" LOG_FILE="$SCRIPT_DIR/logs/env-backup-validation.log" # Ensure logs directory exists mkdir -p "$(dirname "$LOG_FILE")" # Logging function log() { echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE" } # Display usage information usage() { echo "Usage: $0 [OPTIONS]" echo "" echo "Validate .env file backups against source files" echo "" echo "Options:" echo " -h, --help Show this help message" echo " -v, --verbose Verbose output" echo " -s, --summary-only Show only summary" echo " -m, --missing-only Show only missing files" echo " -d, --diff Show differences between files" echo "" echo "Examples:" echo " $0 # Basic validation" echo " $0 --verbose # Detailed validation" echo " $0 --missing-only # Show only missing backups" echo " $0 --diff # Show file differences" } # Validate backups validate_backups() { local verbose="$1" local summary_only="$2" local missing_only="$3" local show_diff="$4" echo -e "${BLUE}=== .env Backup Validation ===${NC}" echo "Source: $DOCKER_DIR" echo "Backup: $BACKUP_DIR" echo "" if [ ! -d "$BACKUP_DIR" ]; then echo -e "${RED}Error: Backup directory not found at $BACKUP_DIR${NC}" echo "Run backup-env-files.sh --init first" exit 1 fi local total_source=0 local total_backup=0 local missing_backup=0 local outdated_backup=0 local identical_files=0 local different_files=0 local backup_only=0 # Arrays to store file lists declare -a missing_files=() declare -a outdated_files=() declare -a different_files_list=() declare -a backup_only_files=() # Count and validate source files echo -e "${YELLOW}Scanning source files...${NC}" while IFS= read -r source_file; do if [ -n "$source_file" ]; then ((total_source++)) # Determine backup path local rel_path="${source_file#$DOCKER_DIR/}" local backup_file="$BACKUP_DIR/docker-containers/$rel_path" if [ ! -f "$backup_file" ]; then ((missing_backup++)) missing_files+=("$rel_path") [ "$summary_only" != true ] && [ "$missing_only" != true ] && echo -e "${RED}✗ Missing backup: $rel_path${NC}" else # Compare files if cmp -s "$source_file" "$backup_file"; then ((identical_files++)) [ "$verbose" = true ] && [ "$summary_only" != true ] && [ "$missing_only" != true ] && echo -e "${GREEN}✓ Identical: $rel_path${NC}" else # Check if backup is older if [ "$source_file" -nt "$backup_file" ]; then ((outdated_backup++)) outdated_files+=("$rel_path") [ "$summary_only" != true ] && [ "$missing_only" != true ] && echo -e "${YELLOW}⚠ Outdated backup: $rel_path${NC}" else ((different_files++)) different_files_list+=("$rel_path") [ "$summary_only" != true ] && [ "$missing_only" != true ] && echo -e "${BLUE}△ Different: $rel_path${NC}" fi # Show diff if requested if [ "$show_diff" = true ] && [ "$summary_only" != true ] && [ "$missing_only" != true ]; then echo -e "${YELLOW} Differences:${NC}" diff -u "$backup_file" "$source_file" | head -20 || true echo "" fi fi fi fi done < <(find "$DOCKER_DIR" -type f \( -name "*.env" -o -name ".env*" -o -name "env.*" \) 2>/dev/null | sort) # Check for backup-only files echo -e "${YELLOW}Scanning backup files...${NC}" if [ -d "$BACKUP_DIR/docker-containers" ]; then while IFS= read -r backup_file; do if [ -n "$backup_file" ]; then ((total_backup++)) # Determine source path local rel_path="${backup_file#$BACKUP_DIR/docker-containers/}" local source_file="$DOCKER_DIR/$rel_path" if [ ! -f "$source_file" ]; then ((backup_only++)) backup_only_files+=("$rel_path") [ "$summary_only" != true ] && [ "$missing_only" != true ] && echo -e "${BLUE}⚡ Backup only: $rel_path${NC}" fi fi done < <(find "$BACKUP_DIR/docker-containers" -type f \( -name "*.env" -o -name ".env*" -o -name "env.*" \) 2>/dev/null | sort) fi # Display missing files if requested if [ "$missing_only" = true ] && [ ${#missing_files[@]} -gt 0 ]; then echo -e "${RED}=== Missing Backup Files ===${NC}" for file in "${missing_files[@]}"; do echo -e "${RED}✗ $file${NC}" done echo "" fi # Summary echo -e "${BLUE}=== Validation Summary ===${NC}" echo -e "Source files: ${BLUE}$total_source${NC}" echo -e "Backup files: ${BLUE}$total_backup${NC}" echo -e "Identical: ${GREEN}$identical_files${NC}" echo -e "Missing backups: ${RED}$missing_backup${NC}" echo -e "Outdated backups: ${YELLOW}$outdated_backup${NC}" echo -e "Different files: ${BLUE}$different_files${NC}" echo -e "Backup only: ${BLUE}$backup_only${NC}" echo "" # Recommendations if [ $missing_backup -gt 0 ] || [ $outdated_backup -gt 0 ]; then echo -e "${YELLOW}=== Recommendations ===${NC}" if [ $missing_backup -gt 0 ]; then echo -e "${YELLOW}• Run backup-env-files.sh to backup missing files${NC}" fi if [ $outdated_backup -gt 0 ]; then echo -e "${YELLOW}• Run backup-env-files.sh to update outdated backups${NC}" fi echo "" fi # Log summary log "Validation completed - Source: $total_source, Backup: $total_backup, Missing: $missing_backup, Outdated: $outdated_backup" # Exit with error code if issues found if [ $missing_backup -gt 0 ] || [ $outdated_backup -gt 0 ]; then exit 1 fi } # Main function main() { local verbose=false local summary_only=false local missing_only=false local show_diff=false # Parse command line arguments while [[ $# -gt 0 ]]; do case $1 in -h|--help) usage exit 0 ;; -v|--verbose) verbose=true shift ;; -s|--summary-only) summary_only=true shift ;; -m|--missing-only) missing_only=true shift ;; -d|--diff) show_diff=true shift ;; *) echo "Unknown option: $1" usage exit 1 ;; esac done validate_backups "$verbose" "$summary_only" "$missing_only" "$show_diff" } # Run main function with all arguments main "$@"