From ac5f3a3a2a895008782bc3627feb91361f16efc6 Mon Sep 17 00:00:00 2001 From: Tomi Eckert Date: Thu, 25 Sep 2025 15:43:12 +0200 Subject: [PATCH] update installer --- install.sh | 234 ++++++++++++++++++++++++++------------------------ packages.conf | 21 +++-- 2 files changed, 130 insertions(+), 125 deletions(-) diff --git a/install.sh b/install.sh index e74da0c..6eef1d9 100644 --- a/install.sh +++ b/install.sh @@ -2,38 +2,26 @@ # --- Main Script --- -# This script presents a menu of software packages defined in a remote -# configuration file. The user can select one or more packages, and the -# script will download the corresponding files. It includes a feature to show -# a diff and ask for confirmation before overwriting existing config files. -# It can also check for updates to already-installed scripts. +# This script presents a menu of software packages, or installs them +# non-interactively via command-line arguments. It downloads files from a +# remote configuration, shows a diff for config updates, and checks versions. # --- Functions --- -# A simple function to log messages with a consistent format. -log() { - echo "[$1] $2" -} - # Get the version from a local script file. -# It reads the first 5 lines and extracts the version number. get_local_version() { local file_path="$1" if [[ -f "${file_path}" ]]; then - # Grep for the version line, then use awk to get the last field. head -n 5 "${file_path}" | grep -m 1 "^# Version:" | awk '{print $NF}' else echo "0.0.0" # Return a base version if file doesn't exist. fi } -# Compare two version strings (e.g., "1.2.0" vs "1.10.0"). -# Returns 0 if v1 is newer, 1 if they are the same or v2 is newer. +# Compare two version strings. Returns 0 if v1 is newer. is_version_greater() { local v1=$1 local v2=$2 - # Use sort's version sorting capability to find the "highest" version. - # If the highest version is v1, then v1 > v2. if [[ "$(printf '%s\n' "$v1" "$v2" | sort -V | head -n 1)" != "$v1" ]]; then return 0 # v1 is greater else @@ -41,181 +29,199 @@ is_version_greater() { fi } -# New function to process a single selected package. +# Process a single selected package. process_package() { - local choice="$1" - # Check if the choice is a valid package name. - if [[ -z "${SCRIPT_PACKAGES[$choice]}" ]]; then - log "❌" "Invalid package name provided: '${choice}'" + local choice_key="$1" + local force_overwrite="$2" # Expects "true" or "false" + + if [[ -z "${SCRIPT_PACKAGES[$choice_key]}" ]]; then + echo "[❌] Invalid package name provided: '${choice_key}'" return fi echo - log "⬇️" "Processing package: '${choice}'..." + echo "[⬇️] Processing package: '${choice_key}'..." - # Get the config value and split it into version and URLs - config_value="${SCRIPT_PACKAGES[$choice]}" - remote_version=$(echo "${config_value}" | cut -d'|' -f1) - urls_to_download=$(echo "${config_value}" | cut -d'|' -f2-) + # Parse the new config format + config_value="${SCRIPT_PACKAGES[$choice_key]}" + display_name=$(echo "${config_value}" | cut -d'|' -f1) + remote_version=$(echo "${config_value}" | cut -d'|' -f2) + description=$(echo "${config_value}" | cut -d'|' -f3) + urls_to_download=$(echo "${config_value}" | cut -d'|' -f4-) read -r -a urls_to_download_array <<< "$urls_to_download" for url in "${urls_to_download_array[@]}"; do filename=$(basename "${url}") - # If it's a .conf file AND it already exists, ask to overwrite. + # Handle config file overwrites if [[ "${filename}" == *.conf && -f "${filename}" ]]; then - log "->" "Found existing config file: '${filename}'." - tmp_file=$(mktemp) + if [[ "$force_overwrite" == "true" ]]; then + echo "[⚠️] Overwriting '${filename}' due to --overwrite-config flag." + if ! curl -fsSL -o "${filename}" "${url}"; then + echo "[❌] Error: Failed to download '${filename}'." + fi + continue + fi + echo "[->] Found existing config file: '${filename}'." + tmp_file=$(mktemp) if curl -fsSL -o "${tmp_file}" "${url}"; then - log "🔎" "Comparing versions..." + echo "[🔎] Comparing versions..." echo "-------------------- DIFF START --------------------" if command -v colordiff &> /dev/null; then colordiff -u "${filename}" "${tmp_file}" else - # Attempt to use diff's color option, which is common. diff --color=always -u "${filename}" "${tmp_file}" 2>/dev/null || diff -u "${filename}" "${tmp_file}" fi echo "--------------------- DIFF END ---------------------" - read -p "Do you want to overwrite '${filename}'? (y/N) " -n 1 -r REPLY echo - if [[ $REPLY =~ ^[Yy]$ ]]; then mv "${tmp_file}" "${filename}" - log "✅" "Updated '${filename}'." + echo "[✅] Updated '${filename}'." else rm "${tmp_file}" - log "🤷" "Kept existing version of '${filename}'." + echo "[🤷] Kept existing version of '${filename}'." fi else - log "❌" "Error: Failed to download new version of '${filename}' for comparison." + echo "[❌] Error downloading new version of '${filename}' for comparison." rm -f "${tmp_file}" fi else # Original download logic for all other files. - log "->" "Downloading '${filename}'..." + echo "[->] Downloading '${filename}'..." if curl -fsSL -o "${filename}" "${url}"; then - log "✅" "Successfully downloaded '${filename}'." + echo "[✅] Successfully downloaded '${filename}'." if [[ "${filename}" == *.sh || "${filename}" == *.bash ]]; then chmod +x "${filename}" - log "🤖" "Made '${filename}' executable." + echo "[🤖] Made '${filename}' executable." fi else - log "❌" "Error: Failed to download '${filename}'." + echo "[❌] Error: Failed to download '${filename}'." fi fi done - log "📦" "Package processing complete for '${choice}'." + echo "[📦] Package processing complete for '${choice_key}'." } - # --- Main Logic --- -# Generate a unique temporary filename with a timestamp. conf_file="packages.conf.$(date +%Y%m%d%H%M%S)" - -# Set up a trap to delete the temporary file on exit. trap 'rm -f "${conf_file}"' EXIT -# Download the configuration file. -log "🔄" "Downloading configuration file..." +echo "[🔄] Downloading configuration file..." if ! curl -fsSL -o "${conf_file}" "https://git.technopunk.space/tomi/Scripts/raw/branch/main/packages.conf"; then - log "❌" "Error: Failed to download packages.conf. Exiting." + echo "[❌] Error: Failed to download packages.conf. Exiting." exit 1 fi -log "✅" "Configuration file downloaded successfully." +echo "[✅] Configuration file downloaded successfully." -# Source the configuration file to load the SCRIPT_PACKAGES associative array. source "${conf_file}" -# --- Update Check & User Interface --- +# --- Argument Parsing for Non-Interactive Mode --- +if [ "$#" -gt 0 ]; then + declare -a packages_to_install + overwrite_configs=false + for arg in "$@"; do + case $arg in + --overwrite-config) + overwrite_configs=true + ;; + -*) + echo "[❌] Unknown flag: $arg" >&2 + exit 1 + ;; + *) + packages_to_install+=("$arg") + ;; + esac + done -# Create an array of options from the package names. -# We will modify this array to show installation and update status. -declare -a options -package_keys=("${!SCRIPT_PACKAGES[@]}") - -log "🔎" "Checking for updates..." -for key in "${package_keys[@]}"; do - # The config format is now "VERSION|URL1 URL2..." - config_value="${SCRIPT_PACKAGES[$key]}" - remote_version=$(echo "${config_value}" | cut -d'|' -f1) - - # Get just the URLs and assume the first URL is the main script to check. - urls=$(echo "${config_value}" | cut -d'|' -f2-) - read -r -a url_array <<< "$urls" - main_script_filename=$(basename "${url_array[0]}") - - # Get the local version of the main script file. - local_version=$(get_local_version "${main_script_filename}") - - status="" - if [[ -f "${main_script_filename}" ]]; then - status=" (Installed: v${local_version})" - # Compare versions - if is_version_greater "$remote_version" "$local_version"; then - status+=" [Update available: v${remote_version}]" - fi + if [ ${#packages_to_install[@]} -eq 0 ]; then + echo "[❌] Flag provided with no package names. Exiting." + exit 1 fi - options+=("${key}${status}") -done -options+=("Quit") # Add a Quit option to the menu. + echo "[🚀] Running in non-interactive mode." + for pkg_key in "${packages_to_install[@]}"; do + if [[ -n "${SCRIPT_PACKAGES[$pkg_key]}" ]]; then + process_package "$pkg_key" "$overwrite_configs" + else + echo "[⚠️] Unknown package: '$pkg_key'. Skipping." + fi + done + echo "[🏁] Non-interactive run complete." + exit 0 +fi -# --- User Interaction --- +# --- Interactive Mode --- +declare -a ordered_keys +package_keys_sorted=($(for k in "${!SCRIPT_PACKAGES[@]}"; do echo $k; done | sort)) +ordered_keys=("${package_keys_sorted[@]}") -# Manually display the options with numbers. +# --- Display Menu --- echo echo "-------------------------------------" echo " Script Downloader " echo "-------------------------------------" -for i in "${!options[@]}"; do - printf "%d) %s\n" "$((i+1))" "${options[$i]}" -done +echo "[🔎] Checking for updates..." echo -# Prompt the user for one or more choices. +for i in "${!ordered_keys[@]}"; do + key="${ordered_keys[$i]}" + config_value="${SCRIPT_PACKAGES[$key]}" + display_name=$(echo "${config_value}" | cut -d'|' -f1) + remote_version=$(echo "${config_value}" | cut -d'|' -f2) + description=$(echo "${config_value}" | cut -d'|' -f3) + urls=$(echo "${config_value}" | cut -d'|' -f4-) + read -r -a url_array <<< "$urls" + main_script_filename=$(basename "${url_array[0]}") + local_version=$(get_local_version "${main_script_filename}") + + # Print main package line + echo -e "\033[1m$((i+1))) $key - $display_name (v$remote_version)\033[0m" + # Print description + echo " $description" + # Print status + if [[ -f "${main_script_filename}" ]]; then + if is_version_greater "$remote_version" "$local_version"; then + echo -e " \033[33m[Update available: v${local_version} -> v${remote_version}]\033[0m" + else + echo -e " \033[32m[Installed: v${local_version}]\033[0m" + fi + fi + echo +done +quit_num=$((${#ordered_keys[@]} + 1)) +echo -e "\033[1m${quit_num}) Quit\033[0m" +echo + +# --- Handle User Input --- read -p "Please enter your choice(s) (e.g., 1 3 4), or press Enter to quit: " -r -a user_choices -# If no choices are made, exit gracefully. if [ ${#user_choices[@]} -eq 0 ]; then - log "👋" "No selection made. Exiting." + echo "[👋] No selection made. Exiting." exit 0 fi -# Loop through the user's selections and process each one. for choice_num in "${user_choices[@]}"; do - # Validate that the input is a number. if ! [[ "$choice_num" =~ ^[0-9]+$ ]]; then - log "⚠️" "Skipping invalid input: '${choice_num}'. Not a number." + echo "[⚠️] Skipping invalid input: '${choice_num}'. Not a number." continue fi - - # Convert selection number to array index (0-based). - index=$((choice_num - 1)) - - # Validate that the index is within the bounds of the options array. - if [[ -z "${options[$index]}" ]]; then - log "⚠️" "Skipping invalid choice: '${choice_num}'. Out of range." - continue - fi - - # Get the choice text from the array. - choice_with_status="${options[$index]}" - - # Strip the status message to get the package key. - choice=$(echo "${choice_with_status}" | sed 's/ (.*//') - - # Handle the "Quit" option. - if [[ "${choice}" == "Quit" ]]; then - log "👋" "Quit selected. Exiting now." + if [ "$choice_num" -eq "$quit_num" ]; then + echo "[👋] Quit selected. Exiting." exit 0 fi - - # Process the selected package. - process_package "${choice}" + index=$((choice_num - 1)) + if [[ -z "${ordered_keys[$index]}" ]]; then + echo "[⚠️] Skipping invalid choice: '${choice_num}'. Out of range." + continue + fi + choice_key="${ordered_keys[$index]}" + process_package "$choice_key" "false" # Never force overwrite in interactive mode done echo -log "🏁" "All selected packages have been processed." +echo "[🏁] All selected packages have been processed." + diff --git a/packages.conf b/packages.conf index f9e3c33..0fedd35 100644 --- a/packages.conf +++ b/packages.conf @@ -1,17 +1,16 @@ #!/bin/bash # # This file contains the configuration for the script downloader. -# The `SCRIPT_PACKAGES` associative array maps a package name to a -# pipe-separated string: "|". +# The `SCRIPT_PACKAGES` associative array maps a short package name +# to a pipe-separated string with the following format: +# "|||" declare -A SCRIPT_PACKAGES -# The version should match the "# Version: x.x.x" line in the main script file. -SCRIPT_PACKAGES["Aurora Suite"]="2.1.0|https://git.technopunk.space/tomi/Scripts/raw/branch/main/aurora/aurora.sh https://git.technopunk.space/tomi/Scripts/raw/branch/main/aurora/aurora.conf" -SCRIPT_PACKAGES["Backup Suite"]="1.0.5|https://git.technopunk.space/tomi/Scripts/raw/branch/main/backup/backup.sh https://git.technopunk.space/tomi/Scripts/raw/branch/main/backup/backup.conf" -SCRIPT_PACKAGES["Monitor Suite"]="1.0.5|https://git.technopunk.space/tomi/Scripts/raw/branch/main/monitor/monitor.sh https://git.technopunk.space/tomi/Scripts/raw/branch/main/monitor/monitor.conf" -SCRIPT_PACKAGES["Key Manager"]="1.2.1|https://git.technopunk.space/tomi/Scripts/raw/branch/main/hdb_keymanager.sh" -SCRIPT_PACKAGES["File Cleaner"]="1.1.0|https://git.technopunk.space/tomi/Scripts/raw/branch/main/clean.sh" -SCRIPT_PACKAGES["HANA Tool"]="1.5.0|https://git.technopunk.space/tomi/Scripts/raw/branch/main/hanatool.sh" -# Example: Add a new script with its version. -# SCRIPT_PACKAGES["My Other Script"]="1.0.0|https://path/to/my-other-script.sh" +# Format: short_name="Display Name|Version|Description|URL1 URL2..." +SCRIPT_PACKAGES["aurora"]="Aurora Suite|2.1.0|A collection of scripts for managing Aurora database instances.|https://git.technopunk.space/tomi/Scripts/raw/branch/main/aurora/aurora.sh https://git.technopunk.space/tomi/Scripts/raw/branch/main/aurora/aurora.conf" +SCRIPT_PACKAGES["backup"]="Backup Suite|1.0.5|A comprehensive script for backing up system files and databases.|https://git.technopunk.space/tomi/Scripts/raw/branch/main/backup/backup.sh https://git.technopunk.space/tomi/Scripts/raw/branch/main/backup/backup.conf" +SCRIPT_PACKAGES["monitor"]="Monitor Suite|1.0.5|Scripts for monitoring system health and performance metrics.|https://git.technopunk.space/tomi/Scripts/raw/branch/main/monitor/monitor.sh https://git.technopunk.space/tomi/Scripts/raw/branch/main/monitor/monitor.conf" +SCRIPT_PACKAGES["keymanager"]="Key Manager|1.2.1|A utility for managing HDB user keys for SAP HANA.|https://git.technopunk.space/tomi/Scripts/raw/branch/main/hdb_keymanager.sh" +SCRIPT_PACKAGES["cleaner"]="File Cleaner|1.1.0|A simple script to clean up temporary files and logs.|https://git.technopunk.space/tomi/Scripts/raw/branch/main/clean.sh" +SCRIPT_PACKAGES["hanatool"]="HANA Tool|1.5.0|A command-line tool for various SAP HANA administration tasks.|https://git.technopunk.space/tomi/Scripts/raw/branch/main/hanatool.sh"