diff options
| author | Sam Scholten | 2025-09-11 16:06:10 +1000 |
|---|---|---|
| committer | Sam Scholten | 2025-09-11 16:06:10 +1000 |
| commit | a7115770e9e377689d9996abe32a28e8db87429d (patch) | |
| tree | 27d08eb347a8b38aeb47b7eb51050a85ff4c7876 /Makefile | |
| download | drestic-a7115770e9e377689d9996abe32a28e8db87429d.tar.gz drestic-a7115770e9e377689d9996abe32a28e8db87429d.zip | |
init
Diffstat (limited to 'Makefile')
| -rw-r--r-- | Makefile | 504 |
1 files changed, 504 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..017841a --- /dev/null +++ b/Makefile @@ -0,0 +1,504 @@ +.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 " restic forget --repo rclone:backup_remote:/restic_backups --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 " mkdir ~/restore" + @echo " User scope: RESTIC_PASSWORD_FILE=~/.config/restic/password restic mount ~/restore --repo rclone:backup_remote:/restic_backups" + @echo " System scope: sudo RESTIC_PASSWORD_FILE=/root/.restic_password restic mount ~/restore --repo rclone:backup_remote:/restic_backups" + @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 " restic restore latest --target /tmp/restore --include /path/to/file --repo rclone:backup_remote:/restic_backups" + @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 timeout 30 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 timeout 30 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" |
