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.
228 lines
7.6 KiB
Bash
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."
|
|
|