.PHONY: all test-local format lint clean help test-remote test-remote-setup test-remote-run test-remote-verify test-remote-teardown setup-user setup-system backup-now backup-now-system check-now check-now-system status status-system snapshots snapshots-system recover uninstall-user uninstall-system update-gotify update-gotify-user update-gotify-system validate-config logs logs-system config test-update-gotify # Default target all: test-local # Run all tests test-local: @echo "--- Running Bats Tests ---" @if ! command -v bats >/dev/null 2>&1; then \ echo "Error: Bats-core is not installed. Run 'make install-deps' first."; \ exit 1; \ fi @echo "Bats version: $$(bats --version)" @BATS_NO_PRETTY_PRINT_FAILURES=true bats tests/basic.bats @echo "--- All Bats Tests Completed ---" # Format shell scripts using shfmt format: @echo "--- Formatting Shell Scripts with shfmt ---" shfmt -w *.sh # For setup.sh, restic_backup.sh, restic_check.sh, common.sh shfmt -w tests/basic.bats # For the single simplified test file @echo "--- Shell Scripts Formatted ---" # Lint shell scripts using shellcheck lint: @echo "--- Linting Shell Scripts with shellcheck ---" @if command -v shellcheck >/dev/null 2>&1; then \ shellcheck setup.sh restic_backup.sh restic_check.sh common.sh || echo "Shellcheck found issues"; \ else \ echo "Warning: shellcheck not found. Install with: sudo apt install shellcheck"; \ fi @echo "--- Linting Complete ---" # Clean up temporary files (if any are generated by tests or other processes) clean: @echo "--- Cleaning up temporary and generated files ---" @echo "Removing temporary test directories and files..." @rm -rf /tmp/drestic-test-* @rm -rf ~/.config/restic-test @echo "Removing editor backup/swap files..." @find . -name "*~" -delete 2>/dev/null || true @find . -name "*.bak" -delete 2>/dev/null || true @find . -name ".*.swp" -delete 2>/dev/null || true @echo "Removing downloaded temporary files (restic installer)..." @rm -f /tmp/restic.bz2 /tmp/restic 2>/dev/null || true @echo "--- Cleanup Complete ---" # Install dependencies for the project install-deps: @echo "--- Installing Dependencies ---" @echo "Installing basic dependencies..." @if command -v apt >/dev/null 2>&1; then \ echo "Using apt package manager"; \ sudo apt update; \ sudo apt install -y curl git restic shellcheck shfmt unzip jq; \ echo "✓ Basic dependencies installed"; \ elif command -v yum >/dev/null 2>&1; then \ echo "Using yum package manager"; \ sudo yum install -y curl git ShellCheck unzip jq; \ echo "✓ Basic dependencies installed (note: shfmt may need manual installation)"; \ echo "Installing restic separately..."; \ if ! command -v restic >/dev/null 2>&1; then \ wget -q https://github.com/restic/restic/releases/latest/download/restic_*_linux_amd64.bz2 -O /tmp/restic.bz2; \ bunzip2 /tmp/restic.bz2; \ chmod +x /tmp/restic; \ sudo mv /tmp/restic /usr/local/bin/restic; \ echo "✓ Restic installed"; \ fi; \ elif command -v pacman >/dev/null 2>&1; then \ echo "Using pacman package manager"; \ sudo pacman -S --noconfirm curl git restic shellcheck shfmt unzip jq; \ echo "✓ Basic dependencies installed"; \ elif command -v zypper >/dev/null 2>&1; then \ echo "Using zypper package manager"; \ sudo zypper install -y curl git restic ShellCheck shfmt unzip jq; \ echo "✓ Basic dependencies installed"; \ else \ echo "❌ No supported package manager found (apt/yum/pacman/zypper)"; \ echo "Please install manually: curl git restic shellcheck shfmt unzip"; \ exit 1; \ fi @echo "--- Installing Official Rclone (with MEGA support) ---" @if command -v rclone >/dev/null 2>&1; then \ echo "Checking if rclone supports MEGA backend..."; \ if rclone help backends | grep -q mega; then \ echo "✓ Rclone with MEGA support already installed"; \ else \ echo "⚠ Rclone found but lacks MEGA support. Installing official version..."; \ curl -s https://rclone.org/install.sh | sudo bash; \ echo "✓ Official rclone installed"; \ fi; \ else \ echo "Installing official rclone with all backends..."; \ curl -s https://rclone.org/install.sh | sudo bash; \ echo "✓ Official rclone installed"; \ fi @echo "--- Installing Restic (if not already installed) ---" @if ! command -v restic >/dev/null 2>&1; then \ echo "Downloading and installing restic..."; \ wget -q https://github.com/restic/restic/releases/latest/download/restic_*_linux_amd64.bz2 -O /tmp/restic.bz2; \ bunzip2 /tmp/restic.bz2; \ chmod +x /tmp/restic; \ sudo mv /tmp/restic /usr/local/bin/restic; \ echo "✓ Restic installed"; \ else \ echo "✓ Restic already installed"; \ fi @echo "--- Installing Bats (for testing) ---" @if ! command -v bats >/dev/null 2>&1; then \ echo "Installing Bats testing framework..."; \ git clone https://github.com/bats-core/bats-core.git /tmp/bats-core; \ sudo /tmp/bats-core/install.sh /usr/local; \ rm -rf /tmp/bats-core; \ echo "✓ Bats installed"; \ else \ echo "✓ Bats already installed"; \ fi @echo "--- Dependencies Installation Complete ---" @echo "You can now run: make test-local" # check-config target removed as validate_config.sh is deprecated/removed # and its functionality is now inline or simplified. # Manual testing targets test-remote: test-remote-setup test-remote-run test-remote-verify test-remote-recovery test-remote-gotify test-remote-teardown # Setup test environment test-remote-setup: @echo "Setting up test environment with real MEGA backend..." @if [ ! -f ~/.config/restic/env ]; then \ echo "Error: Real restic configuration not found. Run './setup.sh --scope=user' first."; \ exit 1; \ fi @echo "Creating test configuration directory: ~/.config/restic-test" @mkdir -p ~/.config/restic-test @echo "Creating temporary test data directory: /tmp/drestic-test-data" @mkdir -p /tmp/drestic-test-data @echo "Creating test paths file: ~/.config/restic-test/paths" @echo "/tmp/drestic-test-data" > ~/.config/restic-test/paths @echo "Creating test excludes file: ~/.config/restic-test/excludes" @echo "*.tmp" > ~/.config/restic-test/excludes @echo "Copying Restic password file to test config: ~/.config/restic-test/password" @cp ~/.config/restic/password ~/.config/restic-test/ @echo "Copying Restic environment file to test config: ~/.config/restic-test/env" @cp ~/.config/restic/env ~/.config/restic-test/ @echo "Updating CONFIG_DIR in test environment file to point to test config" @sed -i 's|CONFIG_DIR=.*|CONFIG_DIR="$$HOME/.config/restic-test"|' ~/.config/restic-test/env && \ echo "Copying Gotify settings from main config to test config..." && \ grep -E "^GOTIFY_(URL|TOKEN)=" ~/.config/restic/env >> ~/.config/restic-test/env || true @echo "Creating test data files in /tmp/drestic-test-data..." @echo "Test file 1 - $$(date)" > /tmp/drestic-test-data/file1.txt @echo "Test file 2 - $$(date)" > /tmp/drestic-test-data/file2.txt @mkdir -p /tmp/drestic-test-data/subdir @echo "Nested test file" > /tmp/drestic-test-data/subdir/nested.txt @echo "Temp file to exclude" > /tmp/drestic-test-data/exclude-me.tmp @echo "Test environment ready! Using real MEGA repository with test data only." # Run test backup test-remote-run: @echo "Running test backup..." @RESTIC_ENV_FILE=~/.config/restic-test/env ./restic_backup.sh @echo "Test backup completed!" # Verify test results test-remote-verify: @echo "Verifying test backup..." @if [ -f ~/.config/restic-test/password ] && [ -f ~/.config/restic-test/env ]; then \ . ~/.config/restic-test/env && \ echo "Waiting 10 seconds for repository to settle..." && sleep 10 && \ timeout 180 env RESTIC_PASSWORD_FILE="$$HOME/.config/restic-test/password" restic snapshots --repo "$$RESTIC_REPOSITORY" | tail -5 || \ echo "⚠ Verification timed out - this is often due to network issues but backup likely succeeded"; \ else \ echo "Error: Test configuration not found"; \ fi # Teardown test environment test-remote-teardown: @echo "Cleaning up test environment..." @rm -rf ~/.config/restic-test @rm -rf /tmp/drestic-test-data @rm -rf /tmp/drestic-recovery-restore @echo "Test environment cleaned up!" @echo "Note: Test snapshots remain in your MEGA repository. Clean manually if needed:" @echo " # The repository path is unique per host. Find it in ~/.config/restic/env" @echo " # Example for a host named 'my-server':" @echo " restic forget --repo rclone:backup_remote:/drestic_backups_my-server --tag daily --prune" # Test file recovery using existing test data test-remote-recovery: @echo "Testing file recovery..." @if [ ! -d /tmp/drestic-test-data ]; then \ echo "No test data found. Run 'make test-remote-setup' first."; \ exit 1; \ fi @echo "Creating additional recovery test file..." @echo "Recovery test file - $(date)" > /tmp/drestic-test-data/recovery-test.txt @echo "Backing up recovery test file..." @RESTIC_ENV_FILE=~/.config/restic-test/env ./restic_backup.sh @echo "Simulating data loss - deleting recovery test file..." @rm /tmp/drestic-test-data/recovery-test.txt @echo "Restoring file from backup..." @mkdir -p /tmp/drestic-recovery-restore @. ~/.config/restic-test/env && \ echo "Waiting 10 seconds for repository to settle..." && sleep 10 && \ timeout 180 env RESTIC_PASSWORD_FILE="$$HOME/.config/restic-test/password" \ restic restore latest --target /tmp/drestic-recovery-restore \ --include /tmp/drestic-test-data/recovery-test.txt \ --repo "$$RESTIC_REPOSITORY" || \ { echo "⚠ Recovery test timed out - network issues with MEGA"; exit 0; } @if [ -f /tmp/drestic-recovery-restore/tmp/drestic-test-data/recovery-test.txt ]; then \ echo "✓ Recovery test PASSED! File restored successfully."; \ echo "Restored content: $$(cat /tmp/drestic-recovery-restore/tmp/drestic-test-data/recovery-test.txt)"; \ rm -rf /tmp/drestic-recovery-restore; \ else \ echo "✗ Recovery test FAILED! File not restored."; \ exit 1; \ fi # Test Gotify notifications (if configured) test-remote-gotify: @echo "Testing Gotify notifications..." @if [ ! -f ~/.config/restic-test/env ]; then \ echo "No test environment found. Run 'make test-remote-setup' first."; \ exit 1; \ fi @. ~/.config/restic-test/env && \ if [ -z "$$GOTIFY_URL" ] || [ -z "$$GOTIFY_TOKEN" ]; then \ echo "ℹ Gotify not configured - skipping notification test."; \ echo "To test Gotify, set GOTIFY_URL and GOTIFY_TOKEN in ~/.config/restic/env"; \ else \ echo "Sending test notification to $$GOTIFY_URL..."; \ if curl -sS "$$GOTIFY_URL/message?token=$$GOTIFY_TOKEN" \ -F "title=DRestic Test ($$(whoami)@$$(hostname))" \ -F "message=This is a test notification from your DRestic backup system on $$(whoami)@$$(hostname) at $$(date)" \ -F "priority=5" >/dev/null 2>&1; then \ echo "✓ Test notification sent successfully!"; \ echo "Check your Gotify server/app for the test message."; \ else \ echo "✗ Failed to send notification. Check your GOTIFY_URL and $$GOTIFY_TOKEN."; \ fi; \ fi # User convenience operations setup-user: @./setup.sh --scope=user setup-system: @sudo ./setup.sh --scope=system backup-now: @systemctl --user start restic-backup.service @echo "Backup started. Monitor with: journalctl --user -fu restic-backup.service" backup-now-system: @sudo systemctl start restic-backup.service @echo "System backup started. Monitor with: sudo journalctl -fu restic-backup.service" check-now: @systemctl --user start restic-check.service @echo "Repository check started. Monitor with: journalctl --user -fu restic-check.service" check-now-system: @sudo systemctl start restic-check.service @echo "System repository check started. Monitor with: sudo journalctl -fu restic-check.service" status: @echo "=== User Timer Status ===" @systemctl --user status restic-backup.timer restic-check.timer --no-pager || true @echo "" @echo "=== Recent User Backup Logs ===" @journalctl --user -u restic-backup.service --since "24 hours ago" --no-pager -n 5 || echo "No recent backup logs found" status-system: @echo "=== System Timer Status ===" @sudo systemctl status restic-backup.timer restic-check.timer --no-pager || true @echo "" @echo "=== Recent System Backup Logs ===" @sudo journalctl -u restic-backup.service --since "24 hours ago" --no-pager -n 5 || echo "No recent backup logs found" snapshots: @echo "Listing user backup snapshots..." @if [ -f ~/.config/restic/env ]; then \ . ~/.config/restic/env && \ env RESTIC_PASSWORD_FILE="$$RESTIC_PASSWORD_FILE" restic snapshots --repo "$$RESTIC_REPOSITORY" && \ echo "" && \ echo "Repository statistics:" && \ env RESTIC_PASSWORD_FILE="$$RESTIC_PASSWORD_FILE" restic stats --repo "$$RESTIC_REPOSITORY"; \ else \ echo "Error: Restic not configured. Run 'make setup-user' first."; \ exit 1; \ fi snapshots-system: @echo "Listing system backup snapshots..." @if sudo [ -f /root/.restic_env ]; then \ sudo bash -c '. /root/.restic_env && env RESTIC_PASSWORD_FILE="$$RESTIC_PASSWORD_FILE" restic snapshots --repo "$$RESTIC_REPOSITORY" && echo "" && echo "Repository statistics:" && env RESTIC_PASSWORD_FILE="$$RESTIC_PASSWORD_FILE" restic stats --repo "$$RESTIC_REPOSITORY"'; \ else \ echo "Error: System restic not configured. Run 'make setup-system' first."; \ exit 1; \ fi unlock-repo: @echo "=== Unlock Restic Repository ===" @if sudo [ -f /root/.restic_env ]; then \ echo "Unlocking system scope repository..."; \ sudo bash -c 'source /root/.restic_env && restic unlock --repo "$$RESTIC_REPOSITORY" --password-file "$$RESTIC_PASSWORD_FILE"'; \ echo "✓ System repository unlocked"; \ elif [ -f ~/.config/restic/env ]; then \ echo "Unlocking user scope repository..."; \ bash -c 'source ~/.config/restic/env && restic unlock --repo "$$RESTIC_REPOSITORY" --password-file "$$RESTIC_PASSWORD_FILE"'; \ echo "✓ User repository unlocked"; \ else \ echo "Error: No restic configuration found"; \ echo "Run 'make setup-user' or 'make setup-system' first"; \ exit 1; \ fi recover: @echo "=== DRestic Recovery Helper ===" @echo "" @echo "Step 1: List available snapshots" @echo " User scope: make snapshots" @echo " System scope: make snapshots-system" @echo "" @echo "Step 2: Mount backup as filesystem (easiest method)" @echo " # The repository path is unique per host (e.g., drestic_backups_my-server)." @echo " # Find your exact path in the 'env' file (~/.config/restic/env or /root/.restic_env)." @echo " mkdir ~/restore" @echo " User scope: RESTIC_PASSWORD_FILE=~/.config/restic/password restic mount ~/restore --repo rclone:backup_remote:/drestic_backups_my-server" @echo " System scope: sudo RESTIC_PASSWORD_FILE=/root/.restic_password restic mount ~/restore --repo rclone:backup_remote:/drestic_backups_my-server" @echo "" @echo "Step 3: Browse files in ~/restore/ (like a normal folder)" @echo " cd ~/restore/snapshots/latest/home/username/" @echo " cp important-file.txt ~/recovered-file.txt" @echo "" @echo "Step 4: Unmount when done" @echo " umount ~/restore" @echo "" @echo "Alternative: Restore specific files directly" @echo " # Remember to use your unique repository path (e.g., rclone:backup_remote:/drestic_backups_my-server)" @echo " restic restore latest --target /tmp/restore --include /path/to/file --repo rclone:backup_remote:/drestic_backups_my-server" @echo "" @echo "For more details, see README.md Recovery section" uninstall-user: @./uninstall_user.sh uninstall-system: @./uninstall_system.sh update-gotify: @echo "=== Update Gotify Configuration ===" @if [ -f ~/.config/restic/env ]; then \ echo "Updating user scope Gotify settings..."; \ ./update_gotify.sh ~/.config/restic/env; \ elif sudo [ -f /root/.restic_env ]; then \ echo "Updating system scope Gotify settings..."; \ sudo ./update_gotify.sh /root/.restic_env; \ else \ echo "Error: No DRestic installation found. Run 'make setup-user' or 'make setup-system' first."; \ exit 1; \ fi update-gotify-user: @./update_gotify.sh ~/.config/restic/env update-gotify-system: @sudo ./update_gotify.sh /root/.restic_env # New target for updating Gotify settings in the test environment test-update-gotify: @echo "=== Update Gotify Configuration for Test Environment ===" @if [ ! -f ~/.config/restic-test/env ]; then \ echo "Error: Test environment not set up. Run 'make test-remote-setup' first."; \ exit 1; \ fi @./update_gotify.sh ~/.config/restic-test/env validate-config: @echo "=== DRestic Configuration Validation ===" @if [ -f ~/.config/restic/env ]; then \ echo "✓ User scope configuration found"; \ echo "Checking user configuration..."; \ if [ -f ~/.config/restic/password ] && [ -s ~/.config/restic/password ]; then \ echo "✓ Password file exists and is not empty"; \ else \ echo "✗ Password file missing or empty: ~/.config/restic/password"; \ fi; \ if [ -f ~/.config/restic/paths ] && [ -s ~/.config/restic/paths ]; then \ echo "✓ Paths file exists and is not empty"; \ else \ echo "✗ Paths file missing or empty: ~/.config/restic/paths"; \ fi; \ if [ -f ~/.config/restic/excludes ]; then \ echo "✓ Excludes file exists"; \ else \ echo "⚠ Excludes file missing (optional): ~/.config/restic/excludes"; \ fi; \ echo "Testing rclone connection..."; \ if bash -c ". ~/.config/restic/env && timeout 60 rclone ls backup_remote:" >/dev/null 2>&1; then \ echo "✓ Rclone connection successful"; \ else \ echo "✗ Rclone connection failed"; \ fi; \ elif [ -f /root/.restic_env ]; then \ echo "✓ System scope configuration found"; \ echo "Checking system configuration..."; \ if sudo [ -f /root/.restic_password ] && sudo [ -s /root/.restic_password ]; then \ echo "✓ Password file exists and is not empty"; \ else \ echo "✗ Password file missing or empty: /root/.restic_password"; \ fi; \ if sudo [ -f /etc/restic/paths ] && sudo [ -s /etc/restic/paths ]; then \ echo "✓ Paths file exists and is not empty"; \ else \ echo "✗ Paths file missing or empty: /etc/restic/paths"; \ fi; \ if sudo [ -f /etc/restic/excludes ]; then \ echo "✓ Excludes file exists"; \ else \ echo "⚠ Excludes file missing (optional): /etc/restic/excludes"; \ fi; \ echo "Testing rclone connection..."; \ if sudo bash -c ". /root/.restic_env && timeout 60 rclone ls backup_remote:" >/dev/null 2>&1; then \ echo "✓ Rclone connection successful"; \ else \ echo "✗ Rclone connection failed"; \ fi; \ else \ echo "✗ No DRestic configuration found"; \ echo "Run 'make setup-user' or 'make setup-system' first"; \ exit 1; \ fi logs: @echo "=== Recent User Backup Logs ===" @journalctl --user -u restic-backup.service --since "7 days ago" --no-pager || echo "No recent backup logs found" logs-system: @echo "=== Recent System Backup Logs ===" @sudo journalctl -u restic-backup.service --since "7 days ago" --no-pager || echo "No recent backup logs found" config: @echo "=== DRestic Configuration Status ===" @if [ -f ~/.config/restic/env ]; then \ echo "User scope configuration:"; \ echo " Config directory: ~/.config/restic/"; \ echo " Password file: ~/.config/restic/password"; \ echo " Environment file: ~/.config/restic/env"; \ echo " Paths file: ~/.config/restic/paths"; \ echo " Excludes file: ~/.config/restic/excludes"; \ echo " Timer status: $$(systemctl --user is-active restic-backup.timer 2>/dev/null || echo 'inactive')"; \ elif [ -f /root/.restic_env ]; then \ echo "System scope configuration:"; \ echo " Config directory: /etc/restic/"; \ echo " Password file: /root/.restic_password"; \ echo " Environment file: /root/.restic_env"; \ echo " Paths file: /etc/restic/paths"; \ echo " Excludes file: /etc/restic/excludes"; \ echo " Timer status: $$(sudo systemctl is-active restic-backup.timer 2>/dev/null || echo 'inactive')"; \ else \ echo "No DRestic configuration found."; \ echo "Run 'make setup-user' or 'make setup-system' to get started."; \ fi help: @echo "Available targets:" @echo "" @echo "Setup and Operations:" @echo " setup-user : Run initial setup for user scope" @echo " setup-system : Run initial setup for system scope" @echo " backup-now : Start user backup immediately" @echo " backup-now-system: Start system backup immediately" @echo " check-now : Start user repository integrity check" @echo " check-now-system : Start system repository integrity check" @echo " status : Show user backup timer status and recent logs" @echo " status-system : Show system backup timer status and recent logs" @echo " snapshots : List user backup snapshots" @echo " snapshots-system : List system backup snapshots" @echo " unlock-repo : Remove stale repository locks (auto-detects scope)" @echo " uninstall-user : Uninstall user scope DRestic" @echo " uninstall-system : Uninstall system scope DRestic" @echo " recover : Show recovery instructions" @echo " update-gotify : Update Gotify notification settings (auto-detects scope)" @echo " update-gotify-user : Update Gotify settings for user scope" @echo " update-gotify-system : Update Gotify settings for system scope" @echo " validate-config : Validate DRestic configuration and connectivity" @echo " logs : Show recent user backup logs" @echo " logs-system : Show recent system backup logs" @echo " config : Show current configuration status and paths" @echo "" @echo "Testing:" @echo " test-local : Runs all (fully-local) Bats tests" @echo " test-remote : Full manual test cycle, incl. to remote (setup, run, verify, recovery, gotify, teardown)" @echo " test-remote-setup : Setup manual test environment with small test files" @echo " test-remote-run : Run backup with test configuration" @echo " test-remote-verify : Verify test backup completed successfully" @echo " test-remote-recovery : Test file recovery workflow" @echo " test-remote-gotify : Test Gotify notification system (if configured)" @echo " test-update-gotify : Update Gotify settings for the test environment" @echo " test-remote-teardown : Clean up test environment" @echo "" @echo "Development:" @echo " all : Runs all default tasks (currently 'test')" @echo " format : Formats all shell scripts using shfmt" @echo " lint : Lints all shell scripts using shellcheck" @echo " install-deps : Installs all required dependencies" @echo " clean : Cleans up temporary files" @echo " help : Displays this help message"