diff --git a/misc/tools.func b/misc/tools.func index c4a70e81f..a9a385cdd 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -205,6 +205,54 @@ should_upgrade() { return 1 } +# ------------------------------------------------------------------------------ +# Get OS information (cached for performance) +# ------------------------------------------------------------------------------ +get_os_info() { + local field="${1:-all}" # id, codename, version, version_id, all + + # Cache OS info to avoid repeated file reads + if [[ -z "${_OS_ID:-}" ]]; then + export _OS_ID=$(awk -F= '/^ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release) + export _OS_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{gsub(/"/,"",$2); print $2}' /etc/os-release) + export _OS_VERSION=$(awk -F= '/^VERSION_ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release) + export _OS_VERSION_FULL=$(awk -F= '/^VERSION=/{gsub(/"/,"",$2); print $2}' /etc/os-release) + fi + + case "$field" in + id) echo "$_OS_ID" ;; + codename) echo "$_OS_CODENAME" ;; + version) echo "$_OS_VERSION" ;; + version_id) echo "$_OS_VERSION" ;; + version_full) echo "$_OS_VERSION_FULL" ;; + all) echo "ID=$_OS_ID CODENAME=$_OS_CODENAME VERSION=$_OS_VERSION" ;; + *) echo "$_OS_ID" ;; + esac +} + +# ------------------------------------------------------------------------------ +# Check if running on specific OS +# ------------------------------------------------------------------------------ +is_debian() { + [[ "$(get_os_info id)" == "debian" ]] +} + +is_ubuntu() { + [[ "$(get_os_info id)" == "ubuntu" ]] +} + +is_alpine() { + [[ "$(get_os_info id)" == "alpine" ]] +} + +# ------------------------------------------------------------------------------ +# Get Debian/Ubuntu major version +# ------------------------------------------------------------------------------ +get_os_version_major() { + local version=$(get_os_info version) + echo "${version%%.*}" +} + # ------------------------------------------------------------------------------ # Download file with retry logic and progress # ------------------------------------------------------------------------------ @@ -233,7 +281,7 @@ download_file() { } # ------------------------------------------------------------------------------ -# Get fallback suite for repository +# Get fallback suite for repository (comprehensive mapping) # ------------------------------------------------------------------------------ get_fallback_suite() { local distro_id="$1" @@ -246,18 +294,54 @@ get_fallback_suite() { return 0 fi - # Fallback mappings + # Comprehensive fallback mappings case "$distro_id" in debian) case "$distro_codename" in - trixie | forky) echo "bookworm" ;; - *) echo "bookworm" ;; + # Debian 13 (Trixie) → Debian 12 (Bookworm) + trixie | forky | sid) + echo "bookworm" + ;; + # Debian 12 (Bookworm) stays + bookworm) + echo "bookworm" + ;; + # Debian 11 (Bullseye) stays + bullseye) + echo "bullseye" + ;; + # Unknown → latest stable + *) + echo "bookworm" + ;; esac ;; ubuntu) case "$distro_codename" in - oracular | noble | mantic) echo "jammy" ;; - *) echo "jammy" ;; + # Ubuntu 24.10 (Oracular) → 24.04 LTS (Noble) + oracular | plucky) + echo "noble" + ;; + # Ubuntu 24.04 LTS (Noble) stays + noble) + echo "noble" + ;; + # Ubuntu 23.10 (Mantic) → 22.04 LTS (Jammy) + mantic | lunar) + echo "jammy" + ;; + # Ubuntu 22.04 LTS (Jammy) stays + jammy) + echo "jammy" + ;; + # Ubuntu 20.04 LTS (Focal) stays + focal) + echo "focal" + ;; + # Unknown → latest LTS + *) + echo "jammy" + ;; esac ;; *) @@ -279,6 +363,148 @@ verify_package_source() { return 1 } +# ------------------------------------------------------------------------------ +# Check if running on LTS version +# ------------------------------------------------------------------------------ +is_lts_version() { + local os_id=$(get_os_info id) + local codename=$(get_os_info codename) + + if [[ "$os_id" == "ubuntu" ]]; then + case "$codename" in + focal | jammy | noble) return 0 ;; # 20.04, 22.04, 24.04 + *) return 1 ;; + esac + elif [[ "$os_id" == "debian" ]]; then + # Debian releases are all "stable" + case "$codename" in + bullseye | bookworm | trixie) return 0 ;; + *) return 1 ;; + esac + fi + + return 1 +} + +# ------------------------------------------------------------------------------ +# Get optimal number of parallel jobs (cached) +# ------------------------------------------------------------------------------ +get_parallel_jobs() { + if [[ -z "${_PARALLEL_JOBS:-}" ]]; then + local cpu_count=$(nproc 2>/dev/null || echo 1) + local mem_gb=$(free -g | awk '/^Mem:/{print $2}') + + # Limit by available memory (assume 1GB per job for compilation) + local max_by_mem=$((mem_gb > 0 ? mem_gb : 1)) + local max_jobs=$((cpu_count < max_by_mem ? cpu_count : max_by_mem)) + + # At least 1, at most cpu_count + export _PARALLEL_JOBS=$((max_jobs > 0 ? max_jobs : 1)) + fi + echo "$_PARALLEL_JOBS" +} + +# ------------------------------------------------------------------------------ +# Get default PHP version for OS +# ------------------------------------------------------------------------------ +get_default_php_version() { + local os_id=$(get_os_info id) + local os_version=$(get_os_version_major) + + case "$os_id" in + debian) + case "$os_version" in + 13) echo "8.3" ;; # Debian 13 (Trixie) + 12) echo "8.2" ;; # Debian 12 (Bookworm) + 11) echo "7.4" ;; # Debian 11 (Bullseye) + *) echo "8.2" ;; + esac + ;; + ubuntu) + case "$os_version" in + 24) echo "8.3" ;; # Ubuntu 24.04 LTS (Noble) + 22) echo "8.1" ;; # Ubuntu 22.04 LTS (Jammy) + 20) echo "7.4" ;; # Ubuntu 20.04 LTS (Focal) + *) echo "8.1" ;; + esac + ;; + *) + echo "8.2" + ;; + esac +} + +# ------------------------------------------------------------------------------ +# Get default Python version for OS +# ------------------------------------------------------------------------------ +get_default_python_version() { + local os_id=$(get_os_info id) + local os_version=$(get_os_version_major) + + case "$os_id" in + debian) + case "$os_version" in + 13) echo "3.12" ;; # Debian 13 (Trixie) + 12) echo "3.11" ;; # Debian 12 (Bookworm) + 11) echo "3.9" ;; # Debian 11 (Bullseye) + *) echo "3.11" ;; + esac + ;; + ubuntu) + case "$os_version" in + 24) echo "3.12" ;; # Ubuntu 24.04 LTS + 22) echo "3.10" ;; # Ubuntu 22.04 LTS + 20) echo "3.8" ;; # Ubuntu 20.04 LTS + *) echo "3.10" ;; + esac + ;; + *) + echo "3.11" + ;; + esac +} + +# ------------------------------------------------------------------------------ +# Get default Node.js LTS version +# ------------------------------------------------------------------------------ +get_default_nodejs_version() { + # Always return current LTS (as of 2025) + echo "20" +} + +# ------------------------------------------------------------------------------ +# Check if package manager is locked +# ------------------------------------------------------------------------------ +is_apt_locked() { + if fuser /var/lib/dpkg/lock-frontend &>/dev/null || + fuser /var/lib/apt/lists/lock &>/dev/null || + fuser /var/cache/apt/archives/lock &>/dev/null; then + return 0 + fi + return 1 +} + +# ------------------------------------------------------------------------------ +# Wait for apt to be available +# ------------------------------------------------------------------------------ +wait_for_apt() { + local max_wait="${1:-300}" # 5 minutes default + local waited=0 + + while is_apt_locked; do + if [[ $waited -ge $max_wait ]]; then + msg_error "Timeout waiting for apt to be available" + return 1 + fi + + debug_log "Waiting for apt to be available... (${waited}s)" + sleep 5 + waited=$((waited + 5)) + done + + return 0 +} + # ------------------------------------------------------------------------------ # Cleanup old repository files (migration helper) # ------------------------------------------------------------------------------