#!/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."