update installer

This commit is contained in:
2025-09-25 15:43:12 +02:00
parent 9cc772f10f
commit ac5f3a3a2a
2 changed files with 130 additions and 125 deletions

View File

@@ -2,38 +2,26 @@
# --- Main Script --- # --- Main Script ---
# This script presents a menu of software packages defined in a remote # This script presents a menu of software packages, or installs them
# configuration file. The user can select one or more packages, and the # non-interactively via command-line arguments. It downloads files from a
# script will download the corresponding files. It includes a feature to show # remote configuration, shows a diff for config updates, and checks versions.
# a diff and ask for confirmation before overwriting existing config files.
# It can also check for updates to already-installed scripts.
# --- Functions --- # --- Functions ---
# A simple function to log messages with a consistent format.
log() {
echo "[$1] $2"
}
# Get the version from a local script file. # Get the version from a local script file.
# It reads the first 5 lines and extracts the version number.
get_local_version() { get_local_version() {
local file_path="$1" local file_path="$1"
if [[ -f "${file_path}" ]]; then 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}' head -n 5 "${file_path}" | grep -m 1 "^# Version:" | awk '{print $NF}'
else else
echo "0.0.0" # Return a base version if file doesn't exist. echo "0.0.0" # Return a base version if file doesn't exist.
fi fi
} }
# Compare two version strings (e.g., "1.2.0" vs "1.10.0"). # Compare two version strings. Returns 0 if v1 is newer.
# Returns 0 if v1 is newer, 1 if they are the same or v2 is newer.
is_version_greater() { is_version_greater() {
local v1=$1 local v1=$1
local v2=$2 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 if [[ "$(printf '%s\n' "$v1" "$v2" | sort -V | head -n 1)" != "$v1" ]]; then
return 0 # v1 is greater return 0 # v1 is greater
else else
@@ -41,181 +29,199 @@ is_version_greater() {
fi fi
} }
# New function to process a single selected package. # Process a single selected package.
process_package() { process_package() {
local choice="$1" local choice_key="$1"
# Check if the choice is a valid package name. local force_overwrite="$2" # Expects "true" or "false"
if [[ -z "${SCRIPT_PACKAGES[$choice]}" ]]; then
log "❌" "Invalid package name provided: '${choice}'" if [[ -z "${SCRIPT_PACKAGES[$choice_key]}" ]]; then
echo "[❌] Invalid package name provided: '${choice_key}'"
return return
fi fi
echo echo
log "⬇️" "Processing package: '${choice}'..." echo "[⬇️] Processing package: '${choice_key}'..."
# Get the config value and split it into version and URLs # Parse the new config format
config_value="${SCRIPT_PACKAGES[$choice]}" config_value="${SCRIPT_PACKAGES[$choice_key]}"
remote_version=$(echo "${config_value}" | cut -d'|' -f1) display_name=$(echo "${config_value}" | cut -d'|' -f1)
urls_to_download=$(echo "${config_value}" | cut -d'|' -f2-) 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" read -r -a urls_to_download_array <<< "$urls_to_download"
for url in "${urls_to_download_array[@]}"; do for url in "${urls_to_download_array[@]}"; do
filename=$(basename "${url}") 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 if [[ "${filename}" == *.conf && -f "${filename}" ]]; then
log "->" "Found existing config file: '${filename}'." if [[ "$force_overwrite" == "true" ]]; then
tmp_file=$(mktemp) 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 if curl -fsSL -o "${tmp_file}" "${url}"; then
log "🔎" "Comparing versions..." echo "[🔎] Comparing versions..."
echo "-------------------- DIFF START --------------------" echo "-------------------- DIFF START --------------------"
if command -v colordiff &> /dev/null; then if command -v colordiff &> /dev/null; then
colordiff -u "${filename}" "${tmp_file}" colordiff -u "${filename}" "${tmp_file}"
else 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}" diff --color=always -u "${filename}" "${tmp_file}" 2>/dev/null || diff -u "${filename}" "${tmp_file}"
fi fi
echo "--------------------- DIFF END ---------------------" echo "--------------------- DIFF END ---------------------"
read -p "Do you want to overwrite '${filename}'? (y/N) " -n 1 -r REPLY read -p "Do you want to overwrite '${filename}'? (y/N) " -n 1 -r REPLY
echo echo
if [[ $REPLY =~ ^[Yy]$ ]]; then if [[ $REPLY =~ ^[Yy]$ ]]; then
mv "${tmp_file}" "${filename}" mv "${tmp_file}" "${filename}"
log "✅" "Updated '${filename}'." echo "[✅] Updated '${filename}'."
else else
rm "${tmp_file}" rm "${tmp_file}"
log "🤷" "Kept existing version of '${filename}'." echo "[🤷] Kept existing version of '${filename}'."
fi fi
else 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}" rm -f "${tmp_file}"
fi fi
else else
# Original download logic for all other files. # Original download logic for all other files.
log "->" "Downloading '${filename}'..." echo "[->] Downloading '${filename}'..."
if curl -fsSL -o "${filename}" "${url}"; then if curl -fsSL -o "${filename}" "${url}"; then
log "✅" "Successfully downloaded '${filename}'." echo "[✅] Successfully downloaded '${filename}'."
if [[ "${filename}" == *.sh || "${filename}" == *.bash ]]; then if [[ "${filename}" == *.sh || "${filename}" == *.bash ]]; then
chmod +x "${filename}" chmod +x "${filename}"
log "🤖" "Made '${filename}' executable." echo "[🤖] Made '${filename}' executable."
fi fi
else else
log "❌" "Error: Failed to download '${filename}'." echo "[❌] Error: Failed to download '${filename}'."
fi fi
fi fi
done done
log "📦" "Package processing complete for '${choice}'." echo "[📦] Package processing complete for '${choice_key}'."
} }
# --- Main Logic --- # --- Main Logic ---
# Generate a unique temporary filename with a timestamp.
conf_file="packages.conf.$(date +%Y%m%d%H%M%S)" 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 trap 'rm -f "${conf_file}"' EXIT
# Download the configuration file. echo "[🔄] Downloading configuration file..."
log "🔄" "Downloading configuration file..."
if ! curl -fsSL -o "${conf_file}" "https://git.technopunk.space/tomi/Scripts/raw/branch/main/packages.conf"; then 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 exit 1
fi 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}" source "${conf_file}"
# --- Update Check & User Interface --- # --- Argument Parsing for Non-Interactive Mode ---
if [ "$#" -gt 0 ]; then
# Create an array of options from the package names. declare -a packages_to_install
# We will modify this array to show installation and update status. overwrite_configs=false
declare -a options for arg in "$@"; do
package_keys=("${!SCRIPT_PACKAGES[@]}") case $arg in
--overwrite-config)
log "🔎" "Checking for updates..." overwrite_configs=true
for key in "${package_keys[@]}"; do ;;
# The config format is now "VERSION|URL1 URL2..." -*)
config_value="${SCRIPT_PACKAGES[$key]}" echo "[❌] Unknown flag: $arg" >&2
remote_version=$(echo "${config_value}" | cut -d'|' -f1) exit 1
;;
# Get just the URLs and assume the first URL is the main script to check. *)
urls=$(echo "${config_value}" | cut -d'|' -f2-) packages_to_install+=("$arg")
read -r -a url_array <<< "$urls" ;;
main_script_filename=$(basename "${url_array[0]}") esac
# 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
fi
options+=("${key}${status}")
done done
options+=("Quit") # Add a Quit option to the menu. if [ ${#packages_to_install[@]} -eq 0 ]; then
echo "[❌] Flag provided with no package names. Exiting."
exit 1
fi
# --- User Interaction --- 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
# Manually display the options with numbers. # --- Interactive Mode ---
declare -a ordered_keys
package_keys_sorted=($(for k in "${!SCRIPT_PACKAGES[@]}"; do echo $k; done | sort))
ordered_keys=("${package_keys_sorted[@]}")
# --- Display Menu ---
echo echo
echo "-------------------------------------" echo "-------------------------------------"
echo " Script Downloader " echo " Script Downloader "
echo "-------------------------------------" echo "-------------------------------------"
for i in "${!options[@]}"; do echo "[🔎] Checking for updates..."
printf "%d) %s\n" "$((i+1))" "${options[$i]}"
done
echo 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 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 if [ ${#user_choices[@]} -eq 0 ]; then
log "👋" "No selection made. Exiting." echo "[👋] No selection made. Exiting."
exit 0 exit 0
fi fi
# Loop through the user's selections and process each one.
for choice_num in "${user_choices[@]}"; do for choice_num in "${user_choices[@]}"; do
# Validate that the input is a number.
if ! [[ "$choice_num" =~ ^[0-9]+$ ]]; then 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 continue
fi fi
if [ "$choice_num" -eq "$quit_num" ]; then
# Convert selection number to array index (0-based). echo "[👋] Quit selected. Exiting."
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."
exit 0 exit 0
fi fi
index=$((choice_num - 1))
# Process the selected package. if [[ -z "${ordered_keys[$index]}" ]]; then
process_package "${choice}" 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 done
echo echo
log "🏁" "All selected packages have been processed." echo "[🏁] All selected packages have been processed."

View File

@@ -1,17 +1,16 @@
#!/bin/bash #!/bin/bash
# #
# This file contains the configuration for the script downloader. # This file contains the configuration for the script downloader.
# The `SCRIPT_PACKAGES` associative array maps a package name to a # The `SCRIPT_PACKAGES` associative array maps a short package name
# pipe-separated string: "<version>|<space-separated list of URLs>". # to a pipe-separated string with the following format:
# "<Display Name>|<Version>|<Description>|<Space-separated list of URLs>"
declare -A SCRIPT_PACKAGES declare -A SCRIPT_PACKAGES
# The version should match the "# Version: x.x.x" line in the main script file. # Format: short_name="Display Name|Version|Description|URL1 URL2..."
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["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 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["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 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["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["Key Manager"]="1.2.1|https://git.technopunk.space/tomi/Scripts/raw/branch/main/hdb_keymanager.sh" 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["File Cleaner"]="1.1.0|https://git.technopunk.space/tomi/Scripts/raw/branch/main/clean.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["HANA Tool"]="1.5.0|https://git.technopunk.space/tomi/Scripts/raw/branch/main/hanatool.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"
# Example: Add a new script with its version.
# SCRIPT_PACKAGES["My Other Script"]="1.0.0|https://path/to/my-other-script.sh"