diff --git a/.gitignore b/.gitignore index 859b4e4..cc7d3ec 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,9 @@ runners.conf /tmp/ *.log +# Install manifests (machine-specific state from setup scripts) +.manifests/ + # Backup archives *.zip diff --git a/CLAUDE.md b/CLAUDE.md index 60cdf27..9ffef55 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -35,12 +35,24 @@ backup/ # Backup and restore scripts ## Key Commands - `setup/configure_env.sh` — Interactive .env setup wizard +- `setup/cleanup.sh` — Reverse everything setup scripts installed (reads .manifests/) - `preflight.sh` — Validate everything before running phases - `run_all.sh` — Execute all phases sequentially -- `teardown_all.sh` — Reverse teardown +- `teardown_all.sh` — Reverse teardown (add `--cleanup` to also uninstall prerequisites) - `manage_runner.sh add|remove|list` — Dynamic runner management +## Version Checking +Setup scripts and preflight validate minimum versions for all tools: +- Local: jq>=1.6, curl>=7.70, git>=2.30, shellcheck>=0.8, gh>=2.0 +- Remote: docker>=20.0, docker-compose>=2.0, jq>=1.6 + +## Install Manifests +Setup scripts record every install action to `.manifests/.manifest`. +`setup/cleanup.sh` reads these manifests to fully reverse setup actions. +Useful for cleaning machines after testing or migrating to new servers. + ## Sensitive Files (never commit) - `.env` — contains passwords, tokens, IPs - `runners.conf` — contains server IPs and paths +- `.manifests/` — machine-specific install state - `*.pem`, `*.key`, `*.crt` — SSL certificates diff --git a/lib/common.sh b/lib/common.sh index 25a70a4..3f9f0bd 100644 --- a/lib/common.sh +++ b/lib/common.sh @@ -352,3 +352,178 @@ wait_for_ssh() { log_error "Timeout waiting for SSH to ${host_key} after ${max_secs}s" return 1 } + +# --------------------------------------------------------------------------- +# Version checking +# --------------------------------------------------------------------------- + +# Compare two semver-like version strings (major.minor or major.minor.patch). +# Returns 0 if $1 >= $2, 1 otherwise. +# Works by comparing each numeric component left to right. +_version_gte() { + local ver="$1" min="$2" + + # Split on dots into arrays + local IFS='.' + # shellcheck disable=SC2206 + local -a v=($ver) + # shellcheck disable=SC2206 + local -a m=($min) + + local max_parts=${#m[@]} + local i + for ((i = 0; i < max_parts; i++)); do + local vp="${v[$i]:-0}" + local mp="${m[$i]:-0}" + if (( vp > mp )); then return 0; fi + if (( vp < mp )); then return 1; fi + done + return 0 +} + +# Extract the first semver-like string (X.Y or X.Y.Z) from arbitrary output. +# Handles common patterns like "jq-1.7.1", "Docker version 24.0.7", "v2.29.1", etc. +_extract_version() { + local raw="$1" + # Match the first occurrence of digits.digits (optionally .digits more) + if [[ "$raw" =~ ([0-9]+\.[0-9]+(\.[0-9]+)*) ]]; then + printf '%s' "${BASH_REMATCH[1]}" + else + printf '' + fi +} + +# Check that a local command meets a minimum version. +# Usage: check_min_version "docker" "docker --version" "20.0" +# tool_name: display name for log messages +# version_cmd: command to run (must output version somewhere in its output) +# min_version: minimum required version (e.g. "1.6", "20.0.0") +# Returns 0 if version >= min, 1 otherwise. +check_min_version() { + local tool_name="$1" version_cmd="$2" min_version="$3" + local raw_output + raw_output=$(eval "$version_cmd" 2>&1) || { + log_error "$tool_name: failed to run '$version_cmd'" + return 1 + } + local actual + actual=$(_extract_version "$raw_output") + if [[ -z "$actual" ]]; then + log_error "$tool_name: could not parse version from: $raw_output" + return 1 + fi + if _version_gte "$actual" "$min_version"; then + log_success "$tool_name $actual (>= $min_version)" + return 0 + else + log_error "$tool_name $actual is below minimum $min_version" + return 1 + fi +} + +# Same as check_min_version but runs the command on a remote host via SSH. +# Usage: check_remote_min_version "UNRAID" "docker" "docker --version" "20.0" +check_remote_min_version() { + local host_key="$1" tool_name="$2" version_cmd="$3" min_version="$4" + local raw_output + raw_output=$(ssh_exec "$host_key" "$version_cmd" 2>&1) || { + log_error "$tool_name on ${host_key}: failed to run '$version_cmd'" + return 1 + } + local actual + actual=$(_extract_version "$raw_output") + if [[ -z "$actual" ]]; then + log_error "$tool_name on ${host_key}: could not parse version from: $raw_output" + return 1 + fi + if _version_gte "$actual" "$min_version"; then + log_success "$tool_name $actual on ${host_key} (>= $min_version)" + return 0 + else + log_error "$tool_name $actual on ${host_key} is below minimum $min_version" + return 1 + fi +} + +# --------------------------------------------------------------------------- +# Install manifest — tracks what each setup script installs for rollback +# --------------------------------------------------------------------------- +# Manifest files live in $PROJECT_ROOT/.manifests/.manifest +# Each line: TYPE|TARGET|DETAILS +# Types: +# brew_pkg — Homebrew package on macOS (TARGET=package_name) +# dnf_pkg — DNF package on Fedora (TARGET=package_name) +# static_bin — Static binary installed to a path (TARGET=path) +# docker_group — User added to docker group (TARGET=username) +# systemd_svc — Systemd service enabled (TARGET=service_name) +# xcode_cli — Xcode CLI Tools installed (TARGET=xcode-select) +# directory — Directory created (TARGET=path) + +_manifest_dir() { + printf '%s/.manifests' "$(_project_root)" +} + +# Initialize manifest directory and file for a host. +# Usage: manifest_init "macbook" +manifest_init() { + local host="$1" + local dir + dir="$(_manifest_dir)" + mkdir -p "$dir" + local file="${dir}/${host}.manifest" + if [[ ! -f "$file" ]]; then + printf '# Install manifest for %s — created %s\n' "$host" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$file" + fi +} + +# Record an action in the manifest. Skips duplicates. +# Usage: manifest_record "macbook" "brew_pkg" "jq" +# manifest_record "unraid" "static_bin" "/usr/local/bin/jq" +manifest_record() { + local host="$1" action_type="$2" target="$3" details="${4:-}" + local file + file="$(_manifest_dir)/${host}.manifest" + + # Ensure manifest file exists + manifest_init "$host" + + local entry="${action_type}|${target}|${details}" + + # Skip if already recorded + if grep -qF "$entry" "$file" 2>/dev/null; then + return 0 + fi + + printf '%s\n' "$entry" >> "$file" +} + +# Check if a manifest file exists and has entries (beyond the header). +# Usage: manifest_exists "macbook" +manifest_exists() { + local host="$1" + local file + file="$(_manifest_dir)/${host}.manifest" + [[ -f "$file" ]] && grep -qv '^#' "$file" 2>/dev/null +} + +# Read all entries from a manifest (skipping comments). +# Usage: manifest_entries "macbook" +# Outputs lines to stdout: TYPE|TARGET|DETAILS +manifest_entries() { + local host="$1" + local file + file="$(_manifest_dir)/${host}.manifest" + if [[ ! -f "$file" ]]; then + return 0 + fi + grep -v '^#' "$file" | grep -v '^$' || true +} + +# Remove the manifest file for a host (after successful cleanup). +# Usage: manifest_clear "macbook" +manifest_clear() { + local host="$1" + local file + file="$(_manifest_dir)/${host}.manifest" + rm -f "$file" +} diff --git a/preflight.sh b/preflight.sh index 090ee38..f90d344 100755 --- a/preflight.sh +++ b/preflight.sh @@ -16,7 +16,7 @@ FAIL_COUNT=0 # --------------------------------------------------------------------------- # Check helper — runs a check function, tracks pass/fail count. -# Intentionally does NOT exit on failure — we want to run ALL 19 checks +# Intentionally does NOT exit on failure — we want to run ALL checks # so the user sees every issue at once, not one at a time. # --------------------------------------------------------------------------- check() { @@ -293,11 +293,49 @@ if ! check_nginx_conf 2>/dev/null; then log_error " → Nginx config path ${NGINX_CONF_PATH:-} not writable on Unraid." fi +# --------------------------------------------------------------------------- +# Check 20: Local tool minimum versions +# Validates that tools on the MacBook meet minimum requirements. +# --------------------------------------------------------------------------- +check_local_versions() { + local fail=0 + check_min_version "jq" "jq --version" "1.6" || fail=1 + check_min_version "curl" "curl --version" "7.70" || fail=1 + check_min_version "git" "git --version" "2.30" || fail=1 + return $fail +} +check 20 "Local tool minimum versions (jq>=1.6, curl>=7.70, git>=2.30)" check_local_versions + +# --------------------------------------------------------------------------- +# Check 21: Unraid tool minimum versions +# --------------------------------------------------------------------------- +check_unraid_versions() { + local fail=0 + check_remote_min_version "UNRAID" "docker" "docker --version" "20.0" || fail=1 + check_remote_min_version "UNRAID" "docker-compose" "docker compose version 2>/dev/null || docker-compose --version" "2.0" || fail=1 + check_remote_min_version "UNRAID" "jq" "jq --version" "1.6" || fail=1 + return $fail +} +check 21 "Unraid tool minimum versions (docker>=20, compose>=2, jq>=1.6)" check_unraid_versions + +# --------------------------------------------------------------------------- +# Check 22: Fedora tool minimum versions +# --------------------------------------------------------------------------- +check_fedora_versions() { + local fail=0 + check_remote_min_version "FEDORA" "docker" "docker --version" "20.0" || fail=1 + check_remote_min_version "FEDORA" "docker-compose" "docker compose version" "2.0" || fail=1 + check_remote_min_version "FEDORA" "jq" "jq --version" "1.6" || fail=1 + return $fail +} +check 22 "Fedora tool minimum versions (docker>=20, compose>=2, jq>=1.6)" check_fedora_versions + # --------------------------------------------------------------------------- # Summary # --------------------------------------------------------------------------- +TOTAL_CHECKS=$((PASS_COUNT + FAIL_COUNT)) printf '\n' -log_info "Results: ${PASS_COUNT} passed, ${FAIL_COUNT} failed (out of 19 checks)" +log_info "Results: ${PASS_COUNT} passed, ${FAIL_COUNT} failed (out of ${TOTAL_CHECKS} checks)" if [[ $FAIL_COUNT -gt 0 ]]; then log_error "Preflight FAILED — fix the issues above before proceeding." diff --git a/setup/cleanup.sh b/setup/cleanup.sh new file mode 100755 index 0000000..1319601 --- /dev/null +++ b/setup/cleanup.sh @@ -0,0 +1,299 @@ +#!/usr/bin/env bash +set -euo pipefail + +# ============================================================================= +# setup/cleanup.sh — Reverse everything installed by setup scripts +# Reads .manifests/.manifest files and undoes each recorded action. +# Use this to clean up machines after testing or after migrating to new servers. +# +# Usage: +# ./setup/cleanup.sh # Clean up all hosts with manifests +# ./setup/cleanup.sh --host=macbook # Clean up macOS only +# ./setup/cleanup.sh --host=unraid # Clean up Unraid only +# ./setup/cleanup.sh --host=fedora # Clean up Fedora only +# ./setup/cleanup.sh --dry-run # Show what would be removed +# ./setup/cleanup.sh --yes # Skip confirmation prompts +# ============================================================================= + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +source "${SCRIPT_DIR}/../lib/common.sh" + +load_env || true # Best effort — .env may already be gone during full cleanup + +# --------------------------------------------------------------------------- +# Parse arguments +# --------------------------------------------------------------------------- +TARGET_HOST="" +DRY_RUN=false +AUTO_YES=false + +for arg in "$@"; do + case "$arg" in + --host=*) TARGET_HOST="${arg#*=}" ;; + --dry-run) DRY_RUN=true ;; + --yes|-y) AUTO_YES=true ;; + --help|-h) + cat </dev/null; then + log_info "Uninstalling brew package: $pkg" + brew uninstall "$pkg" || log_warn "Failed to uninstall $pkg (may have dependents)" + else + log_info "Brew package $pkg not installed — skipping" + fi +} + +cleanup_dnf_pkg() { + local host_key="$1" pkg="$2" + if [[ "$DRY_RUN" == "true" ]]; then + log_info "[dry-run] Would remove dnf package on ${host_key}: $pkg" + return 0 + fi + log_info "Removing dnf package on ${host_key}: $pkg" + ssh_exec "$host_key" "sudo dnf -y remove $pkg" 2>/dev/null || log_warn "Failed to remove $pkg on ${host_key}" +} + +cleanup_static_bin() { + local host_key="$1" path="$2" + if [[ "$DRY_RUN" == "true" ]]; then + log_info "[dry-run] Would remove static binary on ${host_key}: $path" + return 0 + fi + log_info "Removing static binary on ${host_key}: $path" + ssh_exec "$host_key" "rm -f '$path'" 2>/dev/null || log_warn "Failed to remove $path on ${host_key}" +} + +cleanup_docker_group() { + local host_key="$1" username="$2" + if [[ "$DRY_RUN" == "true" ]]; then + log_info "[dry-run] Would remove ${username} from docker group on ${host_key}" + return 0 + fi + log_info "Removing ${username} from docker group on ${host_key}" + ssh_exec "$host_key" "sudo gpasswd -d $username docker" 2>/dev/null || log_warn "Failed to remove $username from docker group on ${host_key}" +} + +cleanup_systemd_svc() { + local host_key="$1" service="$2" + if [[ "$DRY_RUN" == "true" ]]; then + log_info "[dry-run] Would disable systemd service on ${host_key}: $service" + return 0 + fi + log_info "Disabling systemd service on ${host_key}: $service" + ssh_exec "$host_key" "sudo systemctl disable --now $service" 2>/dev/null || log_warn "Failed to disable $service on ${host_key}" +} + +cleanup_xcode_cli() { + if [[ "$DRY_RUN" == "true" ]]; then + log_info "[dry-run] Would remove Xcode CLI Tools" + return 0 + fi + log_info "Removing Xcode Command Line Tools..." + sudo rm -rf /Library/Developer/CommandLineTools 2>/dev/null || log_warn "Failed to remove Xcode CLI Tools (may need sudo)" +} + +# --------------------------------------------------------------------------- +# Map host names to SSH host keys for remote operations +# --------------------------------------------------------------------------- +host_to_ssh_key() { + local host="$1" + case "$host" in + unraid) echo "UNRAID" ;; + fedora) echo "FEDORA" ;; + *) echo "" ;; + esac +} + +# --------------------------------------------------------------------------- +# Process each host's manifest in reverse order +# Reverse order ensures dependent packages are removed before their providers +# (e.g., docker-compose-plugin before docker-ce). +# --------------------------------------------------------------------------- +TOTAL_ACTIONS=0 +CLEANED=0 +FAILED=0 + +for host in "${HOSTS[@]}"; do + log_info "=== Cleaning up: ${host} ===" + + ssh_key=$(host_to_ssh_key "$host") + + # Read entries into array, then reverse + mapfile -t entries < <(manifest_entries "$host") + + if [[ ${#entries[@]} -eq 0 ]]; then + log_info "No entries in ${host} manifest — skipping" + continue + fi + + # Process in reverse order + for ((i = ${#entries[@]} - 1; i >= 0; i--)); do + entry="${entries[$i]}" + IFS='|' read -r action_type target _details <<< "$entry" + TOTAL_ACTIONS=$((TOTAL_ACTIONS + 1)) + + case "$action_type" in + brew_pkg) + if cleanup_brew_pkg "$target"; then + CLEANED=$((CLEANED + 1)) + else + FAILED=$((FAILED + 1)) + fi + ;; + dnf_pkg) + if [[ -z "$ssh_key" ]]; then + log_warn "Cannot clean up dnf_pkg '$target' — no SSH key for host '$host'" + FAILED=$((FAILED + 1)) + continue + fi + if cleanup_dnf_pkg "$ssh_key" "$target"; then + CLEANED=$((CLEANED + 1)) + else + FAILED=$((FAILED + 1)) + fi + ;; + static_bin) + if [[ -z "$ssh_key" ]]; then + log_warn "Cannot clean up static_bin '$target' — no SSH key for host '$host'" + FAILED=$((FAILED + 1)) + continue + fi + if cleanup_static_bin "$ssh_key" "$target"; then + CLEANED=$((CLEANED + 1)) + else + FAILED=$((FAILED + 1)) + fi + ;; + docker_group) + if [[ -z "$ssh_key" ]]; then + log_warn "Cannot clean up docker_group '$target' — no SSH key for host '$host'" + FAILED=$((FAILED + 1)) + continue + fi + if cleanup_docker_group "$ssh_key" "$target"; then + CLEANED=$((CLEANED + 1)) + else + FAILED=$((FAILED + 1)) + fi + ;; + systemd_svc) + if [[ -z "$ssh_key" ]]; then + log_warn "Cannot clean up systemd_svc '$target' — no SSH key for host '$host'" + FAILED=$((FAILED + 1)) + continue + fi + if cleanup_systemd_svc "$ssh_key" "$target"; then + CLEANED=$((CLEANED + 1)) + else + FAILED=$((FAILED + 1)) + fi + ;; + xcode_cli) + if cleanup_xcode_cli; then + CLEANED=$((CLEANED + 1)) + else + FAILED=$((FAILED + 1)) + fi + ;; + *) + log_warn "Unknown action type '${action_type}' for target '${target}' — skipping" + FAILED=$((FAILED + 1)) + ;; + esac + done + + # Clear the manifest after successful cleanup (unless dry run) + if [[ "$DRY_RUN" == "false" ]]; then + manifest_clear "$host" + log_success "Manifest cleared for ${host}" + fi +done + +# --------------------------------------------------------------------------- +# Summary +# --------------------------------------------------------------------------- +printf '\n' +if [[ "$DRY_RUN" == "true" ]]; then + log_info "[dry-run] Would process ${TOTAL_ACTIONS} actions across ${#HOSTS[@]} host(s)" +else + log_info "Cleanup summary: ${CLEANED} cleaned, ${FAILED} failed (out of ${TOTAL_ACTIONS} actions)" + if [[ $FAILED -gt 0 ]]; then + log_warn "Some cleanup actions failed — check logs above" + else + log_success "All cleanup actions completed successfully" + fi +fi diff --git a/setup/fedora.sh b/setup/fedora.sh index 70426b3..38e4e51 100755 --- a/setup/fedora.sh +++ b/setup/fedora.sh @@ -22,6 +22,11 @@ log_info "Verifying Fedora OS..." require_remote_os "FEDORA" "Linux" "Fedora target must be a Linux machine — check FEDORA_IP in .env" require_remote_pkg_manager "FEDORA" "dnf" "Fedora target must have dnf (RPM-based distro) — this script won't work on Debian/Ubuntu" +# -------------------------------------------------------------------------- +# Manifest — track what we install for rollback/cleanup +# -------------------------------------------------------------------------- +manifest_init "fedora" + # -------------------------------------------------------------------------- # SSH connectivity # -------------------------------------------------------------------------- @@ -40,12 +45,19 @@ if ssh_exec FEDORA "docker --version" &>/dev/null; then else log_info "Installing Docker CE on Fedora..." ssh_exec FEDORA "sudo dnf -y install dnf-plugins-core" + manifest_record "fedora" "dnf_pkg" "dnf-plugins-core" ssh_exec FEDORA "sudo dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo" ssh_exec FEDORA "sudo dnf -y install docker-ce docker-ce-cli containerd.io docker-compose-plugin" + manifest_record "fedora" "dnf_pkg" "docker-ce" + manifest_record "fedora" "dnf_pkg" "docker-ce-cli" + manifest_record "fedora" "dnf_pkg" "containerd.io" + manifest_record "fedora" "dnf_pkg" "docker-compose-plugin" ssh_exec FEDORA "sudo systemctl enable --now docker" + manifest_record "fedora" "systemd_svc" "docker" # Add user to docker group ssh_exec FEDORA "sudo usermod -aG docker $FEDORA_SSH_USER" + manifest_record "fedora" "docker_group" "$FEDORA_SSH_USER" log_warn "User $FEDORA_SSH_USER added to docker group. You may need to re-login for this to take effect." if ssh_exec FEDORA "docker --version" &>/dev/null; then @@ -74,6 +86,7 @@ if ssh_exec FEDORA "jq --version" &>/dev/null; then else log_info "Installing jq on Fedora..." ssh_exec FEDORA "sudo dnf -y install jq" + manifest_record "fedora" "dnf_pkg" "jq" if ssh_exec FEDORA "jq --version" &>/dev/null; then log_success "jq installed on Fedora" else @@ -82,6 +95,14 @@ else fi fi +# -------------------------------------------------------------------------- +# Minimum version checks for remote tools +# -------------------------------------------------------------------------- +log_info "Checking minimum versions on Fedora..." +check_remote_min_version "FEDORA" "docker" "docker --version" "20.0" +check_remote_min_version "FEDORA" "docker-compose" "docker compose version" "2.0" +check_remote_min_version "FEDORA" "jq" "jq --version" "1.6" + # -------------------------------------------------------------------------- # Verify Docker works without sudo # -------------------------------------------------------------------------- diff --git a/setup/macbook.sh b/setup/macbook.sh index 47a7ea1..12ce7d0 100755 --- a/setup/macbook.sh +++ b/setup/macbook.sh @@ -15,6 +15,11 @@ log_info "=== MacBook Setup ===" # -------------------------------------------------------------------------- require_local_os "Darwin" "macbook.sh must run on macOS — detected a non-macOS system" +# -------------------------------------------------------------------------- +# Manifest — track what we install for rollback/cleanup +# -------------------------------------------------------------------------- +manifest_init "macbook" + # -------------------------------------------------------------------------- # Homebrew # -------------------------------------------------------------------------- @@ -37,10 +42,21 @@ for pkg in "${BREW_PACKAGES[@]}"; do else log_info "Installing $pkg..." brew install "$pkg" + manifest_record "macbook" "brew_pkg" "$pkg" log_success "$pkg installed" fi done +# -------------------------------------------------------------------------- +# Minimum version checks for local tools +# -------------------------------------------------------------------------- +log_info "Checking minimum versions..." +check_min_version "jq" "jq --version" "1.6" +check_min_version "curl" "curl --version" "7.70" +check_min_version "git" "git --version" "2.30" +check_min_version "shellcheck" "shellcheck --version" "0.8" +check_min_version "gh" "gh --version" "2.0" + # -------------------------------------------------------------------------- # Verify built-in tools # -------------------------------------------------------------------------- @@ -70,6 +86,7 @@ if xcode-select -p &>/dev/null; then else log_info "Installing Xcode Command Line Tools..." xcode-select --install + manifest_record "macbook" "xcode_cli" "xcode-select" log_warn "Xcode CLI Tools installation started. Wait for it to finish, then re-run this script." exit 0 fi diff --git a/setup/unraid.sh b/setup/unraid.sh index 9406b56..9bad469 100755 --- a/setup/unraid.sh +++ b/setup/unraid.sh @@ -19,6 +19,11 @@ log_info "=== Unraid Setup ===" log_info "Verifying Unraid OS..." require_remote_os "UNRAID" "Linux" "Unraid must be a Linux machine — check UNRAID_IP in .env" +# -------------------------------------------------------------------------- +# Manifest — track what we install for rollback/cleanup +# -------------------------------------------------------------------------- +manifest_init "unraid" + # -------------------------------------------------------------------------- # SSH connectivity # -------------------------------------------------------------------------- @@ -52,6 +57,7 @@ else log_info "Installing docker-compose standalone binary on Unraid..." COMPOSE_VERSION="v2.29.1" ssh_exec UNRAID "curl -SL https://github.com/docker/compose/releases/download/${COMPOSE_VERSION}/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose && chmod +x /usr/local/bin/docker-compose" + manifest_record "unraid" "static_bin" "/usr/local/bin/docker-compose" if ssh_exec UNRAID "docker-compose --version" &>/dev/null; then log_success "docker-compose installed on Unraid" else @@ -69,6 +75,7 @@ else log_info "Installing jq static binary on Unraid..." JQ_VERSION="1.7.1" ssh_exec UNRAID "curl -SL https://github.com/jqlang/jq/releases/download/jq-${JQ_VERSION}/jq-linux-amd64 -o /usr/local/bin/jq && chmod +x /usr/local/bin/jq" + manifest_record "unraid" "static_bin" "/usr/local/bin/jq" if ssh_exec UNRAID "jq --version" &>/dev/null; then log_success "jq installed on Unraid" else @@ -77,6 +84,14 @@ else fi fi +# -------------------------------------------------------------------------- +# Minimum version checks for remote tools +# -------------------------------------------------------------------------- +log_info "Checking minimum versions on Unraid..." +check_remote_min_version "UNRAID" "docker" "docker --version" "20.0" +check_remote_min_version "UNRAID" "docker-compose" "docker compose version 2>/dev/null || docker-compose --version" "2.0" +check_remote_min_version "UNRAID" "jq" "jq --version" "1.6" + # -------------------------------------------------------------------------- # Data path # -------------------------------------------------------------------------- diff --git a/teardown_all.sh b/teardown_all.sh index c2fdb7f..59c0c2e 100755 --- a/teardown_all.sh +++ b/teardown_all.sh @@ -19,6 +19,7 @@ source "${SCRIPT_DIR}/lib/common.sh" # --------------------------------------------------------------------------- THROUGH=1 AUTO_YES=false +RUN_CLEANUP=false for arg in "$@"; do case "$arg" in @@ -29,6 +30,7 @@ for arg in "$@"; do exit 1 fi ;; + --cleanup) RUN_CLEANUP=true ;; --yes|-y) AUTO_YES=true ;; --help|-h) cat <>> Running setup cleanup (uninstalling prerequisites)..." + cleanup_args=() + if [[ "$AUTO_YES" == "true" ]]; then + cleanup_args+=(--yes) + fi + if "${SCRIPT_DIR}/setup/cleanup.sh" "${cleanup_args[@]}"; then + PASS=$((PASS + 1)) + else + log_warn "Setup cleanup had issues (continuing)" + FAIL=$((FAIL + 1)) + fi + printf '\n' +fi + # --------------------------------------------------------------------------- # Summary # ---------------------------------------------------------------------------