This commit is contained in:
Peter Wood
2025-05-29 07:32:02 -04:00
13 changed files with 1511 additions and 38 deletions

512
backup-env-files.sh Executable file
View File

@@ -0,0 +1,512 @@
#!/bin/bash
# backup-env-files.sh - Backup .env files to private Gitea repository
# Author: Shell Repository
# Description: Securely backup and version control .env files from ~/docker/* directories
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_REPO_NAME="docker-env-backup"
BACKUP_DIR="$HOME/.env-backup"
LOG_FILE="$SCRIPT_DIR/logs/env-backup.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 "Backup .env files from ~/docker/* to private Gitea repository"
echo ""
echo "Options:"
echo " -h, --help Show this help message"
echo " -i, --init Initialize the backup repository"
echo " -f, --force Force overwrite existing files"
echo " -d, --dry-run Show what would be backed up without doing it"
echo " -r, --restore Restore .env files from backup"
echo " -l, --list List all .env files found"
echo " -g, --gitea-url URL Set Gitea instance URL"
echo " -u, --username USER Set Gitea username"
echo ""
echo "Examples:"
echo " $0 --init # First time setup"
echo " $0 # Regular backup"
echo " $0 --dry-run # See what would be backed up"
echo " $0 --restore # Restore files from backup"
}
# Check dependencies
check_dependencies() {
local missing_deps=()
command -v git >/dev/null 2>&1 || missing_deps+=("git")
command -v find >/dev/null 2>&1 || missing_deps+=("find")
if [ ${#missing_deps[@]} -ne 0 ]; then
echo -e "${RED}Error: Missing required dependencies: ${missing_deps[*]}${NC}"
echo "Please install the missing dependencies and try again."
exit 1
fi
}
# Find all .env files in docker directories
find_env_files() {
local base_dir="$1"
if [ ! -d "$base_dir" ]; then
echo -e "${YELLOW}Warning: Docker directory $base_dir does not exist${NC}"
return 0
fi
# Find all .env files, including hidden ones and those with different extensions
find "$base_dir" -type f \( -name "*.env" -o -name ".env*" -o -name "env.*" \) 2>/dev/null | sort
}
# List all .env files
list_env_files() {
echo -e "${BLUE}=== Environment Files Found ===${NC}"
local count=0
# Use a temp file to avoid subshell issues
local temp_file=$(mktemp)
find_env_files "$DOCKER_DIR" > "$temp_file"
while IFS= read -r env_file; do
if [ -n "$env_file" ]; then
local rel_path="${env_file#$DOCKER_DIR/}"
local size=$(du -h "$env_file" 2>/dev/null | cut -f1)
local modified=$(stat -c %y "$env_file" 2>/dev/null | cut -d' ' -f1)
echo -e "${GREEN}📄 $rel_path${NC}"
echo " Size: $size | Modified: $modified"
echo " Full path: $env_file"
echo ""
count=$((count + 1))
fi
done < "$temp_file"
# Clean up temp file
rm -f "$temp_file"
echo -e "${BLUE}Total .env files found: $count${NC}"
if [ $count -eq 0 ]; then
echo -e "${YELLOW}No .env files found in $DOCKER_DIR${NC}"
echo "Make sure you have Docker containers with .env files in subdirectories."
fi
}
# Initialize backup repository
init_backup_repo() {
echo -e "${YELLOW}Initializing .env backup repository...${NC}"
# Prompt for Gitea details if not provided
if [ -z "$GITEA_URL" ]; then
read -p "Enter your Gitea instance URL (e.g., https://git.yourdomain.com): " GITEA_URL
fi
if [ -z "$GITEA_USERNAME" ]; then
read -p "Enter your Gitea username: " GITEA_USERNAME
fi
# Create backup directory
mkdir -p "$BACKUP_DIR"
cd "$BACKUP_DIR"
# Initialize git repository if not already done
if [ ! -d ".git" ]; then
git init
echo -e "${GREEN}Initialized local git repository${NC}"
fi
# Create .gitignore for additional security
cat > .gitignore << 'EOF'
# Temporary files
*.tmp
*.swp
*.bak
*~
# OS generated files
.DS_Store
Thumbs.db
# Logs
*.log
EOF
# Create README with important information
cat > README.md << 'EOF'
# Docker Environment Files Backup
This repository contains backup copies of .env files from Docker containers.
## ⚠️ SECURITY WARNING ⚠️
This repository contains sensitive configuration files including:
- API keys
- Database passwords
- Secret tokens
- Private configurations
**NEVER make this repository public!**
## Structure
```
docker-containers/
├── container1/
│ ├── .env
│ └── docker-compose.yml (reference only)
├── container2/
│ └── .env
└── ...
```
## Usage
- Files are organized by container/service name
- Only .env files are backed up (no other sensitive files)
- Restore using the backup-env-files.sh script
## Last Backup
This information is updated automatically by the backup script.
EOF
# Create directory structure
mkdir -p docker-containers
# Set up remote if URL provided
if [ -n "$GITEA_URL" ] && [ -n "$GITEA_USERNAME" ]; then
local remote_url="${GITEA_URL%/}/${GITEA_USERNAME}/${BACKUP_REPO_NAME}.git"
# Check if remote already exists
if ! git remote get-url origin >/dev/null 2>&1; then
git remote add origin "$remote_url"
echo -e "${GREEN}Added remote origin: $remote_url${NC}"
fi
# Save configuration
cat > .env-backup-config << EOF
GITEA_URL="$GITEA_URL"
GITEA_USERNAME="$GITEA_USERNAME"
BACKUP_REPO_NAME="$BACKUP_REPO_NAME"
EOF
echo -e "${YELLOW}Configuration saved to .env-backup-config${NC}"
echo -e "${BLUE}Next steps:${NC}"
echo "1. Create a private repository '$BACKUP_REPO_NAME' in your Gitea instance"
echo "2. Run the backup script to perform your first backup"
echo "3. The script will attempt to push to the remote repository"
fi
# Initial commit
git add .
git commit -m "Initial setup of .env backup repository" || echo "Nothing to commit"
log "Backup repository initialized at $BACKUP_DIR"
}
# Load configuration
load_config() {
local config_file="$BACKUP_DIR/.env-backup-config"
if [ -f "$config_file" ]; then
source "$config_file"
fi
}
# Backup .env files
backup_env_files() {
local dry_run="$1"
local force="$2"
echo -e "${YELLOW}Starting .env files backup...${NC}"
# Check if backup directory exists
if [ ! -d "$BACKUP_DIR" ]; then
echo -e "${RED}Backup directory not found. Run with --init first.${NC}"
exit 1
fi
cd "$BACKUP_DIR"
load_config
# Create timestamp
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
local backup_count=0
local unchanged_count=0
# Process each .env file using a temp file to avoid subshell issues
local temp_file=$(mktemp)
find_env_files "$DOCKER_DIR" > "$temp_file"
while IFS= read -r env_file; do
if [ -n "$env_file" ]; then
# Determine relative path and backup location
local rel_path="${env_file#$DOCKER_DIR/}"
local backup_path="docker-containers/$rel_path"
local backup_dir=$(dirname "$backup_path")
if [ "$dry_run" = "true" ]; then
echo -e "${BLUE}Would backup: $rel_path${NC}"
continue
fi
# Create backup directory structure
mkdir -p "$backup_dir"
# Check if file has changed
local needs_backup=true
if [ -f "$backup_path" ] && [ "$force" != "true" ]; then
if cmp -s "$env_file" "$backup_path"; then
needs_backup=false
unchanged_count=$((unchanged_count + 1))
fi
fi
if [ "$needs_backup" = "true" ]; then
# Copy the file
cp "$env_file" "$backup_path"
echo -e "${GREEN}✓ Backed up: $rel_path${NC}"
backup_count=$((backup_count + 1))
# Also create a reference docker-compose.yml if it exists
local compose_file=$(dirname "$env_file")/docker-compose.yml
local compose_backup="$backup_dir/docker-compose.yml.ref"
if [ -f "$compose_file" ] && [ ! -f "$compose_backup" ]; then
cp "$compose_file" "$compose_backup"
echo -e "${BLUE} + Reference: docker-compose.yml${NC}"
fi
else
echo -e "${YELLOW}- Unchanged: $rel_path${NC}"
fi
fi
done < "$temp_file"
# Clean up temp file
rm -f "$temp_file"
if [ "$dry_run" = "true" ]; then
echo -e "${BLUE}Dry run completed. No files were actually backed up.${NC}"
return 0
fi
# Update README with backup information
sed -i "/^## Last Backup/,$ d" README.md
cat >> README.md << EOF
## Last Backup
- **Date**: $timestamp
- **Files backed up**: $backup_count
- **Files unchanged**: $unchanged_count
- **Total files**: $((backup_count + unchanged_count))
Generated by backup-env-files.sh
EOF
# Commit changes
git add .
if git diff --staged --quiet; then
echo -e "${YELLOW}No changes to commit${NC}"
log "Backup completed - no changes detected"
else
git commit -m "Backup .env files - $timestamp
- Files backed up: $backup_count
- Files unchanged: $unchanged_count
- Total files: $((backup_count + unchanged_count))"
echo -e "${GREEN}Changes committed to local repository${NC}"
# Push to remote if configured
if git remote get-url origin >/dev/null 2>&1; then
echo -e "${YELLOW}Pushing to remote repository...${NC}"
if git push origin main 2>/dev/null || git push origin master 2>/dev/null; then
echo -e "${GREEN}✓ Successfully pushed to remote repository${NC}"
log "Backup completed and pushed to remote - $backup_count files backed up, $unchanged_count unchanged"
else
echo -e "${YELLOW}Warning: Could not push to remote repository${NC}"
echo "You may need to:"
echo "1. Create the repository in Gitea first"
echo "2. Set up authentication (SSH key or token)"
log "Backup completed locally but failed to push to remote - $backup_count files backed up"
fi
else
echo -e "${YELLOW}No remote repository configured${NC}"
log "Backup completed locally - $backup_count files backed up, $unchanged_count unchanged"
fi
fi
echo -e "${GREEN}Backup completed!${NC}"
echo -e "${BLUE}Summary:${NC}"
echo " - Files backed up: $backup_count"
echo " - Files unchanged: $unchanged_count"
echo " - Backup location: $BACKUP_DIR"
}
# Restore .env files
restore_env_files() {
echo -e "${YELLOW}Starting .env files restore...${NC}"
if [ ! -d "$BACKUP_DIR" ]; then
echo -e "${RED}Backup directory not found at $BACKUP_DIR${NC}"
echo "Either run --init first or clone your backup repository to this location."
exit 1
fi
cd "$BACKUP_DIR"
load_config
# Pull latest changes if remote is configured
if git remote get-url origin >/dev/null 2>&1; then
echo -e "${YELLOW}Pulling latest changes from remote...${NC}"
git pull origin main 2>/dev/null || git pull origin master 2>/dev/null || true
fi
local restore_count=0
local error_count=0
# Use a temp file to avoid subshell issues
local temp_file=$(mktemp)
find docker-containers -name "*.env" -type f 2>/dev/null > "$temp_file"
while IFS= read -r backup_file; do
if [ -n "$backup_file" ]; then
# Determine target path
local rel_path="${backup_file#docker-containers/}"
local target_file="$DOCKER_DIR/$rel_path"
local target_dir=$(dirname "$target_file")
# Create target directory if it doesn't exist
if [ ! -d "$target_dir" ]; then
echo -e "${YELLOW}Creating directory: $target_dir${NC}"
mkdir -p "$target_dir"
fi
# Ask for confirmation if file exists and is different
if [ -f "$target_file" ]; then
if ! cmp -s "$backup_file" "$target_file"; then
echo -e "${YELLOW}File exists and differs: $rel_path${NC}"
read -p "Overwrite? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo -e "${YELLOW}Skipped: $rel_path${NC}"
continue
fi
else
echo -e "${GREEN}Identical: $rel_path${NC}"
continue
fi
fi
# Copy the file
if cp "$backup_file" "$target_file"; then
echo -e "${GREEN}✓ Restored: $rel_path${NC}"
restore_count=$((restore_count + 1))
else
echo -e "${RED}✗ Failed to restore: $rel_path${NC}"
error_count=$((error_count + 1))
fi
fi
done < "$temp_file"
# Clean up temp file
rm -f "$temp_file"
echo -e "${GREEN}Restore completed!${NC}"
echo -e "${BLUE}Summary:${NC}"
echo " - Files restored: $restore_count"
echo " - Errors: $error_count"
log "Restore completed - $restore_count files restored, $error_count errors"
}
# Main function
main() {
local init_repo=false
local dry_run=false
local force=false
local restore=false
local list_files=false
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
usage
exit 0
;;
-i|--init)
init_repo=true
shift
;;
-f|--force)
force=true
shift
;;
-d|--dry-run)
dry_run=true
shift
;;
-r|--restore)
restore=true
shift
;;
-l|--list)
list_files=true
shift
;;
-g|--gitea-url)
GITEA_URL="$2"
shift 2
;;
-u|--username)
GITEA_USERNAME="$2"
shift 2
;;
*)
echo "Unknown option: $1"
usage
exit 1
;;
esac
done
# Check dependencies
check_dependencies
# Execute requested action
if [ "$list_files" = true ]; then
list_env_files
elif [ "$init_repo" = true ]; then
init_backup_repo
elif [ "$restore" = true ]; then
restore_env_files
else
backup_env_files "$dry_run" "$force"
fi
}
# Run main function with all arguments
main "$@"

View File

@@ -0,0 +1,50 @@
#!/bin/bash
# env-backup-completion.bash - Bash completion for environment backup scripts
# Source this file or copy to ~/.local/share/bash-completion/completions/
_backup_env_files() {
local cur prev opts
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
opts="--help --init --force --dry-run --restore --list --gitea-url --username"
case ${prev} in
--gitea-url|-g)
# Suggest common gitea URL patterns
COMPREPLY=( $(compgen -W "https://git. https://gitea. https://code." -- ${cur}) )
return 0
;;
--username|-u)
# No completion for username
return 0
;;
*)
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
}
_validate_env_backups() {
local cur prev opts
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
opts="--help --verbose --summary-only --missing-only --diff"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
}
# Register completion functions
complete -F _backup_env_files backup-env-files.sh
complete -F _validate_env_backups validate-env-backups.sh
# Also register for the full path versions
complete -F _backup_env_files ./backup-env-files.sh
complete -F _validate_env_backups ./validate-env-backups.sh

View File

@@ -1,6 +1,8 @@
0 1 * * * /home/acedanger/shell/move-backups.sh 2>&1 | logger -t backup-move -p user.info 0 1 * * * /home/acedanger/shell/move-backups.sh 2>&1 | logger -t backup-move -p user.info
0 2 * * * { echo "Starting .env files backup"; /home/acedanger/shell/backup-env-files.sh; echo ".env backup completed with exit code: $?"; } 2>&1 | logger -t env-backup -p user.info
15 4 * * * { echo "Starting Plex backup"; /home/acedanger/shell/plex/backup-plex.sh --non-interactive --auto-repair; echo "Plex backup completed with exit code: $?"; } 2>&1 | logger -t plex-backup -p user.info 15 4 * * * { echo "Starting Plex backup"; /home/acedanger/shell/plex/backup-plex.sh --non-interactive --auto-repair; echo "Plex backup completed with exit code: $?"; } 2>&1 | logger -t plex-backup -p user.info
0 7 * * * { echo "Starting Plex backup validation"; /home/acedanger/shell/plex/validate-plex-backups.sh --fix; echo "Validation completed with exit code: $?"; } 2>&1 | logger -t plex-validation -p user.info 0 7 * * * { echo "Starting Plex backup validation"; /home/acedanger/shell/plex/validate-plex-backups.sh --fix; echo "Validation completed with exit code: $?"; } 2>&1 | logger -t plex-validation -p user.info
0 5 * * 1 { echo "Starting Immich database backup move"; if mv /mnt/share/media/immich/uploads/backups/immich-db-backup* /mnt/share/media/backups/immich 2>/dev/null; then echo "Immich backup move completed successfully"; else echo "No Immich backup files found or move failed"; fi; } 2>&1 | logger -t immich-backup -p user.info 0 5 * * 1 { echo "Starting Immich database backup move"; if mv /mnt/share/media/immich/uploads/backups/immich-db-backup* /mnt/share/media/backups/immich 2>/dev/null; then echo "Immich backup move completed successfully"; else echo "No Immich backup files found or move failed"; fi; } 2>&1 | logger -t immich-backup -p user.info
0 4 * * * /home/acedanger/shell/crontab/crontab-backup-system.sh backup auto --auto-cleanup 2>&1 | logger -t crontab-backup -p user.info 0 4 * * * /home/acedanger/shell/crontab/crontab-backup-system.sh backup auto --auto-cleanup 2>&1 | logger -t crontab-backup -p user.info
0 8 * * 0 { echo "Starting weekly Plex backup report generation"; /home/acedanger/shell/plex/validate-plex-backups.sh --report; echo "Weekly report generation completed with exit code: $?"; } 2>&1 | logger -t plex-report -p user.info 0 8 * * 0 { echo "Starting weekly Plex backup report generation"; /home/acedanger/shell/plex/validate-plex-backups.sh --report; echo "Weekly report generation completed with exit code: $?"; } 2>&1 | logger -t plex-report -p user.info
30 8 * * 0 { echo "Starting .env backup validation"; /home/acedanger/shell/validate-env-backups.sh; echo ".env validation completed with exit code: $?"; } 2>&1 | logger -t env-validation -p user.info

View File

@@ -1,7 +1,4 @@
0 2 * * * { echo "Starting Docker backup"; /home/acedanger/shell/backup-docker.sh; echo "Docker backup completed with exit code: $?"; } 2>&1 | logger -t docker-backup -p user.info 0 2 * * * { echo "Starting Docker backup"; /home/acedanger/shell/backup-docker.sh; echo "Docker backup completed with exit code: $?"; } 2>&1 | logger -t docker-backup -p user.info
0 4 * * * /home/acedanger/shell/crontab/crontab-backup-system.sh backup auto --auto-cleanup 2>&1 | logger -t crontab-backup -p user.info 0 4 * * * /home/acedanger/shell/crontab/crontab-backup-system.sh backup auto --auto-cleanup 2>&1 | logger -t crontab-backup -p user.info
# Daily backups at 2 AM 0 3 * * * { echo "Starting .env files backup"; /home/acedanger/shell/backup-env-files.sh; echo ".env backup completed with exit code: $?"; } 2>&1 | logger -t env-backup -p user.info
0 2 * * * /home/acedanger/shell/backup-media.sh >/dev/null 2>&1 30 8 * * 0 { echo "Starting .env backup validation"; /home/acedanger/shell/validate-env-backups.sh; echo ".env validation completed with exit code: $?"; } 2>&1 | logger -t env-validation -p user.info
#
# Weekly verified backups at 3 AM
0 3 * * 0 /home/acedanger/shell/backup-media.sh --verify

View File

@@ -7,6 +7,10 @@
# Logs both stdout and stderr with backup-move tag # Logs both stdout and stderr with backup-move tag
0 1 * * * /home/acedanger/shell/move-backups.sh 2>&1 | logger -t backup-move -p user.info 0 1 * * * /home/acedanger/shell/move-backups.sh 2>&1 | logger -t backup-move -p user.info
# Daily .env files backup at 0200 with logging
# Backs up all Docker .env files to private Gitea repository
0 2 * * * { echo "Starting .env files backup"; /home/acedanger/shell/backup-env-files.sh; echo ".env backup completed with exit code: $?"; } 2>&1 | logger -t env-backup -p user.info
# Daily Plex database integrity check every 30 minutes # Daily Plex database integrity check every 30 minutes
*/30 * * * * { echo "Check Plex database corruption"; /home/acedanger/shell/plex/backup-plex.sh --check-integrity --auto-repair; } 2>&1 | logger -t plex-database-integrity-check -p user.info */30 * * * * { echo "Check Plex database corruption"; /home/acedanger/shell/plex/backup-plex.sh --check-integrity --auto-repair; } 2>&1 | logger -t plex-database-integrity-check -p user.info
@@ -29,6 +33,10 @@
# Comprehensive reporting with system logging # Comprehensive reporting with system logging
0 8 * * 0 { echo "Starting weekly Plex backup report generation"; /home/acedanger/shell/plex/validate-plex-backups.sh --report; echo "Weekly report generation completed with exit code: $?"; } 2>&1 | logger -t plex-report -p user.info 0 8 * * 0 { echo "Starting weekly Plex backup report generation"; /home/acedanger/shell/plex/validate-plex-backups.sh --report; echo "Weekly report generation completed with exit code: $?"; } 2>&1 | logger -t plex-report -p user.info
# Weekly .env backup validation (Sundays at 0830)
# Validates integrity of .env backup repository
30 8 * * 0 { echo "Starting .env backup validation"; /home/acedanger/shell/validate-env-backups.sh; echo ".env validation completed with exit code: $?"; } 2>&1 | logger -t env-validation -p user.info
# Optional: Add a health check entry to monitor cron jobs (every 6 hours) # Optional: Add a health check entry to monitor cron jobs (every 6 hours)
# This can help detect if any of the backup processes are failing # This can help detect if any of the backup processes are failing
# 0 */6 * * * { echo "Cron health check - all backup jobs scheduled"; ps aux | grep -E "(backup-plex|validate-plex|move-backups)" | grep -v grep | wc -l; } 2>&1 | logger -t cron-health -p user.info # 0 */6 * * * { echo "Cron health check - all backup jobs scheduled"; ps aux | grep -E "(backup-plex|validate-plex|move-backups)" | grep -v grep | wc -l; } 2>&1 | logger -t cron-health -p user.info

View File

@@ -10,6 +10,14 @@
# Daily system backup at 0400 with auto-cleanup # Daily system backup at 0400 with auto-cleanup
0 4 * * * /home/acedanger/shell/crontab/crontab-backup-system.sh backup auto --auto-cleanup 2>&1 | logger -t crontab-backup -p user.info 0 4 * * * /home/acedanger/shell/crontab/crontab-backup-system.sh backup auto --auto-cleanup 2>&1 | logger -t crontab-backup -p user.info
# Daily .env files backup at 0300 with enhanced logging
# Backs up Docker container .env files to Git repository
0 3 * * * { echo "Starting .env files backup"; /home/acedanger/shell/backup-env-files.sh; echo ".env backup completed with exit code: $?"; } 2>&1 | logger -t env-backup -p user.info
# Weekly .env backup validation at 0830 on Sundays
# Validates the integrity of .env backup repository
30 8 * * 0 { echo "Starting .env backup validation"; /home/acedanger/shell/validate-env-backups.sh; echo ".env validation completed with exit code: $?"; } 2>&1 | logger -t env-validation -p user.info
# Optional: Monitor Docker container health (every 6 hours) # Optional: Monitor Docker container health (every 6 hours)
# This can help detect if any download services are failing # This can help detect if any download services are failing
# 0 */6 * * * { echo "Docker health check"; docker ps --format "table {{.Names}}\t{{.Status}}" | grep -v "Up"; } 2>&1 | logger -t docker-health -p user.info # 0 */6 * * * { echo "Docker health check"; docker ps --format "table {{.Names}}\t{{.Status}}" | grep -v "Up"; } 2>&1 | logger -t docker-health -p user.info

View File

@@ -9,6 +9,14 @@
# Daily system backup at 0400 with auto-cleanup # Daily system backup at 0400 with auto-cleanup
0 4 * * * /home/acedanger/shell/crontab/crontab-backup-system.sh backup auto --auto-cleanup 2>&1 | logger -t crontab-backup -p user.info 0 4 * * * /home/acedanger/shell/crontab/crontab-backup-system.sh backup auto --auto-cleanup 2>&1 | logger -t crontab-backup -p user.info
# Daily .env files backup at 0300 with enhanced logging
# Backs up Docker container .env files to Git repository
0 3 * * * { echo "Starting .env files backup"; /home/acedanger/shell/backup-env-files.sh; echo ".env backup completed with exit code: $?"; } 2>&1 | logger -t env-backup -p user.info
# Weekly .env backup validation at 0830 on Sundays
# Validates the integrity of .env backup repository
30 8 * * 0 { echo "Starting .env backup validation"; /home/acedanger/shell/validate-env-backups.sh; echo ".env validation completed with exit code: $?"; } 2>&1 | logger -t env-validation -p user.info
# Optional: Add a health check entry to monitor backup jobs (every 6 hours) # Optional: Add a health check entry to monitor backup jobs (every 6 hours)
# This can help detect if the backup process is failing # This can help detect if the backup process is failing
# 0 */6 * * * { echo "Cron health check - Docker backup job scheduled"; ps aux | grep "backup-docker" | grep -v grep | wc -l; } 2>&1 | logger -t cron-health -p user.info # 0 */6 * * * { echo "Cron health check - Docker backup job scheduled"; ps aux | grep "backup-docker" | grep -v grep | wc -l; } 2>&1 | logger -t cron-health -p user.info

View File

@@ -0,0 +1,146 @@
# .env Backup Integration Guide
## Quick Setup Summary
Your .env backup system is now fully operational! Here's what was set up:
### ✅ What's Working
- **31 .env files** discovered across your Docker containers
- **30 files backed up** successfully to `/home/acedanger/.env-backup`
- **Private Gitea repository** configured and pushed successfully
- **Version control** with automatic commit messages and timestamps
- **Reference files** included (docker-compose.yml for context)
### 🔧 Integration Options
#### 1. Manual Backup (Current)
```bash
cd /home/acedanger/shell
./backup-env-files.sh # Regular backup
./backup-env-files.sh --dry-run # Preview changes
./backup-env-files.sh --list # Show all .env files
```
#### 2. Automated Daily Backup (Recommended)
Add to your crontab for daily backups at 2 AM:
```bash
# Daily .env backup at 2 AM
0 2 * * * /home/acedanger/shell/backup-env-files.sh >/dev/null 2>&1
```
#### 3. Integration with Existing Backup Scripts
The backup integrates with your existing backup system through:
- **Logs**: Written to `/home/acedanger/shell/logs/env-backup.log`
- **Completion**: Tab completion available via `env-backup-completion.bash`
- **Validation**: Use `validate-env-backups.sh` for integrity checks
### 🔐 Security Features
1. **Private Repository**: Only you have access
2. **Gitignore**: Excludes temporary files and logs
3. **SSH Authentication**: Uses your existing SSH key
4. **Local + Remote**: Dual backup (local git + remote Gitea)
### 📊 Backup Structure
```
~/.env-backup/
├── docker-containers/
│ ├── authentik/
│ │ └── .env.example
│ ├── caddy/
│ │ ├── .env
│ │ ├── .env.example
│ │ └── docker-compose.yml.ref
│ ├── database/
│ │ ├── .env
│ │ ├── .env.example
│ │ └── docker-compose.yml.ref
│ └── ... (all your containers)
├── README.md
└── .env-backup-config
```
### 🔄 Common Operations
#### Restore Files (if needed)
```bash
./backup-env-files.sh --restore
```
#### Force Backup (ignore unchanged files)
```bash
./backup-env-files.sh --force
```
#### Check What Would Change
```bash
./backup-env-files.sh --dry-run
```
### 🚨 Emergency Recovery
If you lose your filesystem:
1. **Clone the backup**: `git clone https://git.ptrwd.com/peterwood/docker-env-backup.git`
2. **Restore files**: `./backup-env-files.sh --restore`
3. **Recreate containers**: Your docker-compose.yml reference files are included
### 📈 Monitoring
- **Logs**: Check `/home/acedanger/shell/logs/env-backup.log`
- **Git History**: View changes with `git log` in backup directory
- **Validation**: Run `validate-env-backups.sh` for integrity checks
### 🔧 Maintenance
#### Weekly Validation (Recommended)
```bash
# Add to crontab for weekly validation
0 3 * * 0 /home/acedanger/shell/validate-env-backups.sh >/dev/null 2>&1
```
#### Cleanup Old Logs (Monthly)
The system automatically manages logs, but you can clean them manually if needed.
### 🆘 Troubleshooting
#### Push Fails
- Check SSH key: `ssh -T git@git.ptrwd.com`
- Verify repository exists and is private
- Check network connectivity
#### Files Not Found
- Verify Docker directory structure: `ls -la ~/docker/*/`
- Check file permissions
- Run with `--list` to see what's detected
#### Restore Issues
- Ensure target directories exist
- Check file permissions
- Use `--dry-run` first to preview
## Integration Complete! 🎉
Your .env files are now safely backed up and version controlled. The system will:
1. Track all changes to your .env files
2. Maintain a secure backup in your private Gitea
3. Provide easy restore capabilities
4. Integrate with your existing shell toolkit
Run `./backup-env-files.sh` regularly or set up the cron job for automatic backups!

320
docs/env-backup-system.md Normal file
View File

@@ -0,0 +1,320 @@
# Environment Files Backup System
This document describes the secure backup system for `.env` files from Docker containers to a private Gitea repository.
## Overview
The environment files backup system provides:
- **Automated discovery** of all `.env` files in `~/docker/*` directories
- **Secure version control** using private Git repository
- **Change tracking** with timestamps and commit history
- **Easy restoration** of backed up configurations
- **Validation tools** to ensure backup integrity
## Components
### Scripts
1. **backup-env-files.sh** - Main backup script
2. **validate-env-backups.sh** - Validation and integrity checking
### Repository Structure
```
~/.env-backup/
├── .git/ # Git repository
├── .gitignore # Security-focused gitignore
├── README.md # Repository documentation
├── .env-backup-config # Configuration file
└── docker-containers/ # Backed up files
├── container1/
│ ├── .env # Environment file
│ └── docker-compose.yml.ref # Reference compose file
├── container2/
│ └── .env
└── ...
```
## Security Considerations
### 🔒 Critical Security Points
1. **Repository Privacy**: The backup repository MUST be private
2. **Access Control**: Only you should have access to the repository
3. **Network Security**: Use HTTPS or SSH for Git operations
4. **Local Security**: Backup directory should have restricted permissions
### Best Practices
- Use SSH keys for Git authentication (more secure than passwords)
- Regularly rotate any exposed credentials
- Monitor repository access logs
- Consider encrypting the entire backup repository
## Setup Instructions
### 1. Initial Setup
```bash
# First time setup
./backup-env-files.sh --init
# Follow prompts to configure:
# - Gitea instance URL
# - Username
# - Repository name
```
### 2. Create Repository in Gitea
1. Log into your Gitea instance
2. Create a new **private** repository named `docker-env-backup`
3. Do not initialize with README (the script handles this)
### 3. Configure Authentication
#### Option A: SSH Key (Recommended)
```bash
# Generate SSH key if you don't have one
ssh-keygen -t ed25519 -C "your_email@domain.com"
# Add public key to Gitea:
# 1. Go to Settings → SSH/GPG Keys
# 2. Add the content of ~/.ssh/id_ed25519.pub
```
#### Option B: Personal Access Token
```bash
# In Gitea: Settings → Applications → Generate Token
# Configure Git to use token:
git config --global credential.helper store
```
### 4. First Backup
```bash
# List all .env files that will be backed up
./backup-env-files.sh --list
# Perform dry run to see what would happen
./backup-env-files.sh --dry-run
# Execute actual backup
./backup-env-files.sh
```
## Usage
### Regular Backup
```bash
# Standard backup (only backs up changed files)
./backup-env-files.sh
# Force backup all files
./backup-env-files.sh --force
# See what would be backed up
./backup-env-files.sh --dry-run
```
### Validation
```bash
# Basic validation
./validate-env-backups.sh
# Detailed validation with file differences
./validate-env-backups.sh --diff --verbose
# Show only missing files
./validate-env-backups.sh --missing-only
```
### Restoration
```bash
# Restore all .env files from backup
./backup-env-files.sh --restore
# This will:
# 1. Pull latest changes from remote
# 2. Prompt before overwriting existing files
# 3. Create directory structure as needed
```
## Automation
### Cron Job Setup
Add to your crontab for automated backups:
```bash
# Backup .env files daily at 2 AM
0 2 * * * /home/yourusername/shell/backup-env-files.sh >/dev/null 2>&1
# Validate backups weekly on Sundays at 3 AM
0 3 * * 0 /home/yourusername/shell/validate-env-backups.sh --summary-only
```
### Integration with Existing Backup System
Add to your main backup script:
```bash
# In your existing backup script
echo "Backing up environment files..."
/home/yourusername/shell/backup-env-files.sh
# Validate the backup
if ! /home/yourusername/shell/validate-env-backups.sh --summary-only; then
echo "Warning: .env backup validation failed"
fi
```
## File Discovery
The system automatically finds:
- `*.env` files (e.g., `production.env`, `staging.env`)
- `.env*` files (e.g., `.env`, `.env.local`, `.env.production`)
- `env.*` files (e.g., `env.development`, `env.local`)
### Example Structure
```
~/docker/
├── traefik/
│ ├── .env # ✓ Backed up
│ └── docker-compose.yml
├── nextcloud/
│ ├── .env.production # ✓ Backed up
│ ├── .env.local # ✓ Backed up
│ └── docker-compose.yml
├── grafana/
│ ├── env.grafana # ✓ Backed up
│ └── docker-compose.yml
└── plex/
├── config.env # ✓ Backed up
└── docker-compose.yml
```
## Troubleshooting
### Common Issues
1. **Git Push Fails**
```bash
# Check remote URL
cd ~/.env-backup && git remote -v
# Test connectivity
git ls-remote origin
```
2. **Missing Files**
```bash
# List what would be found
./backup-env-files.sh --list
# Check file permissions
ls -la ~/docker/*/
```
3. **Repository Not Found**
- Ensure repository exists in Gitea
- Check repository name matches configuration
- Verify you have access permissions
### Recovery Scenarios
#### Disaster Recovery
If you lose your entire system:
```bash
# 1. Clone your backup repository
git clone https://git.yourdomain.com/username/docker-env-backup.git ~/.env-backup
# 2. Restore all files
cd /path/to/shell
./backup-env-files.sh --restore
```
#### Selective Recovery
```bash
# Restore specific file manually
cp ~/.env-backup/docker-containers/traefik/.env ~/docker/traefik/
```
## Monitoring
### Log Files
- **backup-env-files.sh**: `logs/env-backup.log`
- **validate-env-backups.sh**: `logs/env-backup-validation.log`
### Health Checks
```bash
# Weekly health check script
#!/bin/bash
echo "=== .env Backup Health Check ==="
./validate-env-backups.sh --summary-only
# Check last backup time
cd ~/.env-backup
echo "Last backup: $(git log -1 --format='%ci')"
# Check repository status
git status --porcelain
```
## Security Enhancements
### Additional Security Measures
1. **GPG Encryption** (Optional)
```bash
# Encrypt sensitive files before committing
gpg --symmetric --cipher-algo AES256 file.env
```
2. **Restricted Permissions**
```bash
# Secure backup directory
chmod 700 ~/.env-backup
chmod 600 ~/.env-backup/.env-backup-config
```
3. **Audit Trail**
```bash
# Monitor repository access
git log --oneline --graph --all
```
## Best Practices
1. **Regular Testing**: Test restoration process monthly
2. **Version Control**: Never force push; preserve history
3. **Documentation**: Keep README.md updated with changes
4. **Monitoring**: Set up alerts for failed backups
5. **Security**: Regularly review repository access permissions
## Support
For issues or questions:
1. Check the troubleshooting section
2. Review log files for error details
3. Validate your Gitea configuration
4. Test Git connectivity manually

View File

@@ -12,13 +12,6 @@ alias findzombie="ps -A -ostat,pid,ppid | grep -e '[zZ]'"
alias la-eza="eza -la --color=auto --group-directories-first" alias la-eza="eza -la --color=auto --group-directories-first"
alias ll-eza="eza -laFh --color=auto --group-directories-first" alias ll-eza="eza -laFh --color=auto --group-directories-first"
alias l-eza="eza -1 --color=auto --group-directories-first" alias l-eza="eza -1 --color=auto --group-directories-first"
alias lt="eza --tree --level=2 --color=auto --group-directories-first" # Tree view (2 levels)
alias llt="eza -la --tree --level=2 --color=auto --group-directories-first" # Long tree view
alias lg="eza -la --git --color=auto --group-directories-first" # Show git status
alias lh="eza -la --color=auto --group-directories-first --sort=size" # Sort by size
alias lr="eza -la --color=auto --group-directories-first --sort=modified" # Sort by modified
alias lx="eza -la --color=auto --group-directories-first --sort=extension" # Sort by extension
alias tree="eza --tree --color=auto --group-directories-first" # Tree alias
# 🎬 Plex Media Server Management - Sexy Edition # 🎬 Plex Media Server Management - Sexy Edition
alias plex="/home/acedanger/shell/plex/plex.sh" alias plex="/home/acedanger/shell/plex/plex.sh"
@@ -33,19 +26,33 @@ alias dcdn="docker compose down"
alias dcupd="docker compose up -d" alias dcupd="docker compose up -d"
alias dcpull="docker compose pull" alias dcpull="docker compose pull"
alias lzd="lazydocker" alias lzd="lazydocker"
alias cat="bat"
alias fd="fd"
alias fzf="fzf --preview='bat {}'"
# 🌟 Eza aliases - Modern replacement for ls # 🌟 Eza aliases - Modern replacement for ls
alias ls="eza --color=auto --group-directories-first"
alias la="eza -la --color=auto --group-directories-first" # 🌟 Eza aliases - Modern replacement for ls
alias ll="eza -laFh --color=auto --group-directories-first"
alias l="eza -1 --color=auto --group-directories-first" # 🌟 Eza aliases - Modern replacement for ls
alias lt="eza --tree --level=2 --color=auto --group-directories-first"
alias llt="eza -la --tree --level=2 --color=auto --group-directories-first" # 🌟 Eza aliases - Modern replacement for ls
alias lg="eza -la --git --color=auto --group-directories-first"
alias lh="eza -la --color=auto --group-directories-first --sort=size" # 🌟 Eza aliases - Modern replacement for ls
alias lr="eza -la --color=auto --group-directories-first --sort=modified"
alias lx="eza -la --color=auto --group-directories-first --sort=extension" # 🌟 Eza aliases - Modern replacement for ls
alias tree="eza --tree --color=auto --group-directories-first"
# 🌟 Eza aliases - Modern replacement for ls
alias cat="batcat"
alias fd="fdfind"
alias fzf="fzf --preview='batcat {}'"
# 🌟 Eza aliases - Modern replacement for ls
alias ls="eza --icons=always -a --color=auto --group-directories-first"
alias la="eza --icons=always -la --color=auto --group-directories-first"
alias ll="eza --icons=always -la --classify=always -h --color=auto --group-directories-first"
alias l="eza --icons=always -1 -a --color=auto --group-directories-first"
alias lt="eza --icons=always -a --tree --level=2 --color=auto --group-directories-first"
alias llt="eza --icons=always -la --tree --level=2 --color=auto --group-directories-first"
alias lg="eza --icons=always -la --git --color=auto --group-directories-first"
alias lh="eza --icons=always -la --color=auto --group-directories-first --sort=size"
alias lr="eza --icons=always -la --color=auto --group-directories-first --sort=modified"
alias lx="eza --icons=always -la --color=auto --group-directories-first --sort=extension"
alias tree="eza --icons=always -a --tree --color=auto --group-directories-first"

181
env-backup-integration.sh Executable file
View File

@@ -0,0 +1,181 @@
#!/bin/bash
# env-backup-integration.sh - Integration script for adding .env backup to existing backup system
# Author: Shell Repository
# Description: Add .env backup functionality to existing backup scripts
# Colors for output
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo -e "${BLUE}=== Environment Files Backup Integration ===${NC}"
# Function to add .env backup to a script
integrate_env_backup() {
local target_script="$1"
local integration_point="$2"
if [ ! -f "$target_script" ]; then
echo -e "${YELLOW}Target script not found: $target_script${NC}"
return 1
fi
# Check if already integrated
if grep -q "backup-env-files.sh" "$target_script"; then
echo -e "${GREEN}✓ Already integrated with $target_script${NC}"
return 0
fi
echo -e "${YELLOW}Integrating with $target_script...${NC}"
# Create backup of original script
cp "$target_script" "$target_script.backup"
# Integration code
local integration_code="
# === Environment Files Backup Integration ===
echo -e \"\${YELLOW}Backing up environment files...\${NC}\"
if [ -f \"\$SCRIPT_DIR/backup-env-files.sh\" ]; then
if \"\$SCRIPT_DIR/backup-env-files.sh\"; then
echo -e \"\${GREEN}✓ Environment files backed up successfully\${NC}\"
else
echo -e \"\${YELLOW}Warning: Environment files backup had issues\${NC}\"
fi
else
echo -e \"\${YELLOW}Warning: backup-env-files.sh not found\${NC}\"
fi
# Validate the backup
if [ -f \"\$SCRIPT_DIR/validate-env-backups.sh\" ]; then
if \"\$SCRIPT_DIR/validate-env-backups.sh\" --summary-only; then
echo -e \"\${GREEN}✓ Environment backup validation passed\${NC}\"
else
echo -e \"\${YELLOW}Warning: Environment backup validation failed\${NC}\"
fi
fi
echo \"\"
# === End Environment Files Backup Integration ===
"
# Add integration based on integration point
case "$integration_point" in
"after_docker")
# Add after Docker backup section
if grep -q "docker" "$target_script" || grep -q "backup.*docker" "$target_script"; then
# Find a good insertion point after docker backup
local line_num=$(grep -n -i "docker.*backup\|backup.*docker" "$target_script" | tail -1 | cut -d: -f1)
if [ -n "$line_num" ]; then
sed -i "${line_num}a\\${integration_code}" "$target_script"
echo -e "${GREEN}✓ Integrated after Docker backup section${NC}"
else
echo -e "${YELLOW}Could not find Docker backup section, adding at end${NC}"
echo "$integration_code" >> "$target_script"
fi
else
echo -e "${YELLOW}No Docker backup section found, adding at end${NC}"
echo "$integration_code" >> "$target_script"
fi
;;
"before_end")
# Add before the end of the script
local last_line=$(wc -l < "$target_script")
sed -i "${last_line}i\\${integration_code}" "$target_script"
echo -e "${GREEN}✓ Integrated before end of script${NC}"
;;
"manual")
echo -e "${BLUE}Manual integration code:${NC}"
echo "$integration_code"
echo -e "${YELLOW}Please add this code manually to your script at the appropriate location${NC}"
;;
*)
echo -e "${YELLOW}Unknown integration point, adding at end${NC}"
echo "$integration_code" >> "$target_script"
;;
esac
echo -e "${GREEN}Integration completed. Backup saved as $target_script.backup${NC}"
}
# Find and integrate with existing backup scripts
echo -e "${YELLOW}Scanning for backup scripts to integrate with...${NC}"
# Common backup script patterns
declare -a backup_scripts=(
"$SCRIPT_DIR/backup-docker.sh"
"$SCRIPT_DIR/backup-media.sh"
"$SCRIPT_DIR/update.sh"
"$SCRIPT_DIR/backup.sh"
"$SCRIPT_DIR/daily-backup.sh"
)
found_scripts=()
for script in "${backup_scripts[@]}"; do
if [ -f "$script" ]; then
found_scripts+=("$script")
echo -e "${GREEN}Found: $(basename "$script")${NC}"
fi
done
if [ ${#found_scripts[@]} -eq 0 ]; then
echo -e "${YELLOW}No backup scripts found to integrate with${NC}"
echo -e "${BLUE}You can manually add the .env backup to your backup routine:${NC}"
echo ""
echo "# Add to your backup script:"
echo "$SCRIPT_DIR/backup-env-files.sh"
echo "$SCRIPT_DIR/validate-env-backups.sh --summary-only"
echo ""
else
echo -e "${BLUE}Select scripts to integrate with (or 'all' for all, 'none' to skip):${NC}"
for i in "${!found_scripts[@]}"; do
echo "$((i+1)). $(basename "${found_scripts[$i]}")"
done
echo ""
read -p "Enter your choice: " choice
case "$choice" in
"all")
for script in "${found_scripts[@]}"; do
integrate_env_backup "$script" "after_docker"
done
;;
"none")
echo -e "${YELLOW}Skipping integration${NC}"
;;
[0-9]*)
if [ "$choice" -ge 1 ] && [ "$choice" -le ${#found_scripts[@]} ]; then
script_index=$((choice-1))
integrate_env_backup "${found_scripts[$script_index]}" "after_docker"
else
echo -e "${RED}Invalid choice${NC}"
fi
;;
*)
echo -e "${RED}Invalid choice${NC}"
;;
esac
fi
# Create a simple cron entry suggestion
echo -e "${BLUE}=== Automation Suggestions ===${NC}"
echo "Add to crontab for automated backups:"
echo ""
echo "# Daily .env backup at 2 AM"
echo "0 2 * * * $SCRIPT_DIR/backup-env-files.sh >/dev/null 2>&1"
echo ""
echo "# Weekly validation on Sundays at 3 AM"
echo "0 3 * * 0 $SCRIPT_DIR/validate-env-backups.sh --summary-only"
echo ""
echo -e "${GREEN}Integration setup completed!${NC}"
echo -e "${BLUE}Next steps:${NC}"
echo "1. Run: $SCRIPT_DIR/backup-env-files.sh --init"
echo "2. Create private repository in Gitea"
echo "3. Run first backup: $SCRIPT_DIR/backup-env-files.sh"
echo "4. Test restoration: $SCRIPT_DIR/backup-env-files.sh --restore"

View File

@@ -384,7 +384,14 @@ grep -v "^alias fzf=" | \
grep -v "^alias ls=" | \ grep -v "^alias ls=" | \
grep -v "^alias ll=" | \ grep -v "^alias ll=" | \
grep -v "^alias la=" | \ grep -v "^alias la=" | \
grep -v "^alias l=" > "$ALIASES_FILE" grep -v "^alias l=" | \
grep -v "^alias tree=" | \
grep -v "^alias lt=" | \
grep -v "^alias llt=" | \
grep -v "^alias lg=" | \
grep -v "^alias lh=" | \
grep -v "^alias lr=" | \
grep -v "^alias lx=" > "$ALIASES_FILE"
# Function to check for command existence and add appropriate alias # Function to check for command existence and add appropriate alias
add_conditional_alias() { add_conditional_alias() {
@@ -435,17 +442,17 @@ if command -v eza &> /dev/null; then
cat >> "$ALIASES_FILE" << 'EOF' cat >> "$ALIASES_FILE" << 'EOF'
# 🌟 Eza aliases - Modern replacement for ls # 🌟 Eza aliases - Modern replacement for ls
alias ls="eza --color=auto --group-directories-first" alias ls="eza --icons=always -a --color=auto --group-directories-first"
alias la="eza -la --color=auto --group-directories-first" alias la="eza --icons=always -la --color=auto --group-directories-first"
alias ll="eza -laFh --color=auto --group-directories-first" alias ll="eza --icons=always -la --classify=always -h --color=auto --group-directories-first"
alias l="eza -1 --color=auto --group-directories-first" alias l="eza --icons=always -1 -a --color=auto --group-directories-first"
alias lt="eza --tree --level=2 --color=auto --group-directories-first" alias lt="eza --icons=always -a --tree --level=2 --color=auto --group-directories-first"
alias llt="eza -la --tree --level=2 --color=auto --group-directories-first" alias llt="eza --icons=always -la --tree --level=2 --color=auto --group-directories-first"
alias lg="eza -la --git --color=auto --group-directories-first" alias lg="eza --icons=always -la --git --color=auto --group-directories-first"
alias lh="eza -la --color=auto --group-directories-first --sort=size" alias lh="eza --icons=always -la --color=auto --group-directories-first --sort=size"
alias lr="eza -la --color=auto --group-directories-first --sort=modified" alias lr="eza --icons=always -la --color=auto --group-directories-first --sort=modified"
alias lx="eza -la --color=auto --group-directories-first --sort=extension" alias lx="eza --icons=always -la --color=auto --group-directories-first --sort=extension"
alias tree="eza --tree --color=auto --group-directories-first" alias tree="eza --icons=always -a --tree --color=auto --group-directories-first"
EOF EOF
echo -e "${GREEN}Eza aliases configured successfully!${NC}" echo -e "${GREEN}Eza aliases configured successfully!${NC}"
else else

227
validate-env-backups.sh Executable file
View File

@@ -0,0 +1,227 @@
#!/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 "$@"