#!/usr/bin/env bash # Shared utility functions for bootstrap CLI # Avoid double sourcing if [ -n "${_LIB_COMMON_SOURCED:-}" ]; then return 0 fi _LIB_COMMON_SOURCED=1 # Ensure running in Bash require_bash() { if [ -z "${BASH_VERSION:-}" ]; then echo "Error: This script must be run using bash." >&2 exit 1 fi } # Color definitions (only if stdout is a TTY) if [ -t 1 ]; then RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color else RED='' GREEN='' YELLOW='' BLUE='' NC='' fi log_info() { echo -e "${BLUE}[INFO]${NC} $1" } log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } log_warn() { echo -e "${YELLOW}[WARNING]${NC} $1" >&2 } log_error() { echo -e "${RED}[ERROR]${NC} $1" >&2 } # Yes/No Confirmation prompt confirm() { local prompt="$1" local response # Read from /dev/tty to support piped installations read -r -p "$prompt [y/N]: " response /dev/null 2>&1 } # Temporary directory helper with automatic cleanup on exit make_temp_dir() { local tmp_dir tmp_dir="$(mktemp -d)" echo "$tmp_dir" } # Version comparison helper (returns 0 if $1 < $2, 1 otherwise) version_lt() { [ "$1" = "$2" ] && return 1 local IFS=. local i ver1=($1) ver2=($2) for ((i=${#ver1[@]}; i<3; i++)); do ver1[i]=0; done for ((i=${#ver2[@]}; i<3; i++)); do ver2[i]=0; done for ((i=0; i<3; i++)); do if ((10#${ver1[i]} < 10#${ver2[i]})); then return 0 elif ((10#${ver1[i]} > 10#${ver2[i]})); then return 1 fi done return 1 } # Cached and resumable download helper download_file() { local url="$1" local dest="$2" local cache_dir="$HOME/.local/state/bootstrap/cache" mkdir -p "$cache_dir" local safe_name if has_command md5sum; then safe_name=$(echo -n "$url" | md5sum | cut -d' ' -f1) elif has_command shasum; then safe_name=$(echo -n "$url" | shasum | cut -d' ' -f1) else safe_name=$(echo -n "$url" | tr -c '[:alnum:]_.-' '_') fi local base_name base_name=$(basename "$url") local cache_file="$cache_dir/${safe_name}_${base_name}" log_info "Downloading $base_name (resumable)..." if ! curl -fL -C - "$url" -o "$cache_file"; then local exit_code=$? # Exit code 33: HTTP server doesn't support ranges/resuming # Exit code 36: Bad download resume offset if [ $exit_code -eq 33 ] || [ $exit_code -eq 36 ]; then log_warn "Server does not support resuming. Retrying from scratch..." rm -f "$cache_file" curl -fL "$url" -o "$cache_file" || return 1 else return $exit_code fi fi mkdir -p "$(dirname "$dest")" cp "$cache_file" "$dest" } # Export functions and variables for subshells export _LIB_COMMON_SOURCED=1 export RED GREEN YELLOW BLUE NC export -f require_bash log_info log_success log_warn log_error confirm has_command make_temp_dir version_lt download_file