summaryrefslogtreecommitdiff
path: root/Makefile
diff options
context:
space:
mode:
authorSam Scholten2025-09-11 16:06:10 +1000
committerSam Scholten2025-09-11 16:06:10 +1000
commita7115770e9e377689d9996abe32a28e8db87429d (patch)
tree27d08eb347a8b38aeb47b7eb51050a85ff4c7876 /Makefile
downloaddrestic-a7115770e9e377689d9996abe32a28e8db87429d.tar.gz
drestic-a7115770e9e377689d9996abe32a28e8db87429d.zip
init
Diffstat (limited to 'Makefile')
-rw-r--r--Makefile504
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"