Files
Scripts/install.sh
Tomi Eckert a16b8aa42b feat(installer): Rework install.sh for non-interactive mode and improved UX
Performs a major refactoring of 'install.sh' to introduce non-interactive installation via command-line arguments (e.g., '--overwrite-config'). Enhances the interactive menu with detailed package information (display name, description, version, update status) and improves config file handling with diff previews. Updates 'packages.conf' format to support new package metadata and uses short, lowercase keys.
2025-09-25 18:33:24 +02:00

228 lines
7.6 KiB
Bash

#!/bin/bash
# --- Main Script ---
# 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 ---
# Get the version from a local script file.
get_local_version() {
local file_path="$1"
if [[ -f "${file_path}" ]]; then
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. Returns 0 if v1 is newer.
is_version_greater() {
local v1=$1
local v2=$2
if [[ "$(printf '%s\n' "$v1" "$v2" | sort -V | head -n 1)" != "$v1" ]]; then
return 0 # v1 is greater
else
return 1 # v1 is not greater (equal or less)
fi
}
# Process a single selected package.
process_package() {
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
echo "[⬇️] Processing package: '${choice_key}'..."
# 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}")
# Handle config file overwrites
if [[ "${filename}" == *.conf && -f "${filename}" ]]; then
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
echo "[🔎] Comparing versions..."
echo "-------------------- DIFF START --------------------"
if command -v colordiff &> /dev/null; then
colordiff -u "${filename}" "${tmp_file}"
else
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}"
echo "[✅] Updated '${filename}'."
else
rm "${tmp_file}"
echo "[🤷] Kept existing version of '${filename}'."
fi
else
echo "[❌] Error downloading new version of '${filename}' for comparison."
rm -f "${tmp_file}"
fi
else
# Original download logic for all other files.
echo "[->] Downloading '${filename}'..."
if curl -fsSL -o "${filename}" "${url}"; then
echo "[✅] Successfully downloaded '${filename}'."
if [[ "${filename}" == *.sh || "${filename}" == *.bash ]]; then
chmod +x "${filename}"
echo "[🤖] Made '${filename}' executable."
fi
else
echo "[❌] Error: Failed to download '${filename}'."
fi
fi
done
echo "[📦] Package processing complete for '${choice_key}'."
}
# --- Main Logic ---
conf_file="packages.conf.$(date +%Y%m%d%H%M%S)"
trap 'rm -f "${conf_file}"' EXIT
echo "[🔄] Downloading configuration file..."
if ! curl -fsSL -o "${conf_file}" "https://git.technopunk.space/tomi/Scripts/raw/branch/main/packages.conf"; then
echo "[❌] Error: Failed to download packages.conf. Exiting."
exit 1
fi
echo "[✅] Configuration file downloaded successfully."
source "${conf_file}"
# --- 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
if [ ${#packages_to_install[@]} -eq 0 ]; then
echo "[❌] Flag provided with no package names. Exiting."
exit 1
fi
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
# --- 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 " Script Downloader "
echo "-------------------------------------"
echo "[🔎] Checking for updates..."
echo
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 [ ${#user_choices[@]} -eq 0 ]; then
echo "[👋] No selection made. Exiting."
exit 0
fi
for choice_num in "${user_choices[@]}"; do
if ! [[ "$choice_num" =~ ^[0-9]+$ ]]; then
echo "[⚠️] Skipping invalid input: '${choice_num}'. Not a number."
continue
fi
if [ "$choice_num" -eq "$quit_num" ]; then
echo "[👋] Quit selected. Exiting."
exit 0
fi
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
echo "[🏁] All selected packages have been processed."