#!/bin/bash set -e # # Purpose : Compile and install applications from compile.conf CONF_FILE="$HOME/.local/share/HamPackServer/compile.conf" TMP_DIR="/tmp/hampack-build" VERSION_FILE="$HOME/.local/state/HamPackServer/.installed_versions" # Get the installed version of an app get_installed_version() { local app="$1" local gui="$2" local type="$3" local install_path="$4" # For npm apps, read version from package.json in the install directory if [ "$type" = "npm" ] && [ -f "$install_path/package.json" ]; then local ver ver=$(grep -m1 '"version"' "$install_path/package.json" | grep -oP '\d+\.\d+[\.\d]*') if [ -n "$ver" ]; then echo "$ver" return fi fi # Skip --version check for GUI apps as it would launch them if [ "$gui" != "true" ]; then if command -v "$app" &> /dev/null; then local ver ver=$("$app" --version 2>&1 | grep -oP '\d+\.\d+[\.\d]*' | head -n1) if [ -n "$ver" ]; then echo "$ver" return fi fi fi # Fall back to version file if [ -f "$VERSION_FILE" ]; then local stored stored=$(grep "^$app=" "$VERSION_FILE" | cut -d'=' -f2) if [ -n "$stored" ]; then echo "$stored" return fi fi echo "unknown" } # Save installed version to version file save_installed_version() { local app="$1" local version="$2" touch "$VERSION_FILE" if grep -q "^$app=" "$VERSION_FILE"; then sed -i "s/^$app=.*/$app=$version/" "$VERSION_FILE" else echo "$app=$version" >> "$VERSION_FILE" fi } # Ask the user whether to proceed when version can't be determined ask_user_proceed() { local app="$1" local latest="$2" echo "Warning: could not determine installed version of $app." echo "compile.conf specifies version $latest." read -rp "Compile and install $app anyway? [y/N] " response [[ "$response" =~ ^[yY] ]] } # Check if an update is needed needs_update() { local app="$1" local install_path="$2" local latest="$3" local gui="$4" local type="$5" # Not installed at all if ! command -v "$app" &> /dev/null && [ ! -f "$install_path" ] && [ ! -d "$install_path" ]; then echo " $app is not installed." return 0 fi local installed installed=$(get_installed_version "$app" "$gui" "$type" "$install_path") if [ "$installed" = "unknown" ]; then if ask_user_proceed "$app" "$latest"; then return 0 else return 1 fi fi echo " Installed version : $installed" echo " Latest version : $latest" if [ "$installed" = "$latest" ]; then echo " $app is up to date, skipping." return 1 fi echo " Update available for $app." return 0 } # Generate desktop file with absolute paths generate_desktop() { local desktop="$1" local tmp_desktop="/tmp/$(basename "$desktop")" sed "s|\$HOME|$HOME|g" "$desktop" > "$tmp_desktop" echo "$tmp_desktop" } # Install desktop file if specified install_desktop() { local desktop="$1" if [ -z "$desktop" ]; then return fi if [ ! -f "$desktop" ]; then echo " Warning: desktop file $desktop not found, skipping." return fi echo " Installing desktop file..." mkdir -p "$HOME/.local/share/applications" local resolved resolved=$(generate_desktop "$desktop") cp "$resolved" "$HOME/.local/share/applications/$(basename "$desktop")" chmod +x "$HOME/.local/share/applications/$(basename "$desktop")" rm -f "$resolved" echo " Desktop file installed." } # Fetch source via git or wget fetch_source() { local app="$1" local git_url="$2" local wget_url="$3" local src_dir="$TMP_DIR/$app" rm -rf "$src_dir" mkdir -p "$src_dir" if [ -n "$git_url" ]; then echo " Cloning $git_url..." git clone "$git_url" "$src_dir" elif [ -n "$wget_url" ]; then echo " Downloading $wget_url..." local filename="${wget_url##*/}" local filepath="$TMP_DIR/$filename" wget -q "$wget_url" -O "$filepath" echo " Extracting $filename..." case "$filename" in *.zip) unzip -q "$filepath" -d "$src_dir" # If the zip extracted into a single subdirectory, flatten it local contents contents=$(ls -1 "$src_dir") local count count=$(echo "$contents" | wc -l) if [ "$count" -eq 1 ] && [ -d "$src_dir/$contents" ]; then echo " Descending into $contents..." local inner="$src_dir/$contents" shopt -s dotglob mv "$inner"/* "$src_dir/" shopt -u dotglob rm -rf "$inner" fi ;; *.tar.gz|*.tgz) tar -xzf "$filepath" -C "$src_dir" --strip-components=1 ;; *.tar.bz2) tar -xjf "$filepath" -C "$src_dir" --strip-components=1 ;; *.tar.xz) tar -xJf "$filepath" -C "$src_dir" --strip-components=1 ;; *) echo " Error: unknown file type for $filename" exit 1 ;; esac rm -f "$filepath" else echo " Error: no git or wget URL specified for $app" exit 1 fi } # cd into source directory and run steps run_steps() { local app="$1" local steps="$2" local src_dir="$TMP_DIR/$app" if [ ! -d "$src_dir" ]; then echo " Error: source directory $src_dir not found." exit 1 fi echo " Changing into $src_dir..." cd "$src_dir" echo " Building $app..." IFS='|' read -ra step_list <<< "$steps" for step in "${step_list[@]}"; do step=$(echo "$step" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') echo " Running: $step" eval "$step" done } # Process a single app process_app() { local app="$1" local git_url="$2" local wget_url="$3" local install_path="$4" local steps="$5" local version="$6" local desktop="$7" local gui="$8" local type="$9" echo "" echo "=== $app ===" # Check if already installed if command -v "$app" &> /dev/null || [ -f "$install_path" ] || [ -d "$install_path" ]; then if [ -z "$version" ]; then echo " $app is already installed, skipping." return fi fi if [ -z "$version" ]; then echo " Warning: no version specified in compile.conf for $app." read -rp " Compile and install $app anyway? [y/N] " response if [[ ! "$response" =~ ^[yY] ]]; then echo " Skipping $app." return fi elif ! needs_update "$app" "$install_path" "$version" "$gui" "$type"; then return fi fetch_source "$app" "$git_url" "$wget_url" run_steps "$app" "$steps" install_desktop "$desktop" save_installed_version "$app" "$version" # Return to a safe directory after build cd "$HOME" echo " $app $version installed successfully." } # Parse compile.conf and process each app process_conf() { local app="" git_url="" wget_url="" install_path="" steps="" version="" desktop="" gui="" # Collect all app configs into parallel arrays first, so stdin is # free for interactive prompts when process_app runs afterwards local -a all_apps all_git all_wget all_install all_steps all_versions all_desktops all_guis all_types local idx=0 local type="" while IFS= read -r line || [ -n "$line" ]; do [[ -z "$line" || "$line" == \#* ]] && continue if [[ "$line" =~ ^\[(.+)\]$ ]]; then if [ -n "$app" ]; then all_apps[$idx]="$app"; all_git[$idx]="$git_url"; all_wget[$idx]="$wget_url" all_install[$idx]="$install_path"; all_steps[$idx]="$steps" all_versions[$idx]="$version"; all_desktops[$idx]="$desktop"; all_guis[$idx]="$gui" all_types[$idx]="$type" idx=$((idx + 1)) fi app="${BASH_REMATCH[1]}" git_url="" wget_url="" install_path="" steps="" version="" desktop="" gui="" type="" continue fi local key="${line%%=*}" local raw_value="${line#*=}" if [ "$key" = "steps" ]; then steps="$raw_value" continue fi local value value=$(eval echo "$raw_value") case "$key" in git) git_url="$value" ;; wget) wget_url="$value" ;; install) install_path="$value" ;; version) version="$value" ;; desktop) desktop="$value" ;; gui) gui="$value" ;; type) type="$value" ;; esac done < "$CONF_FILE" # Store the last app if [ -n "$app" ]; then all_apps[$idx]="$app"; all_git[$idx]="$git_url"; all_wget[$idx]="$wget_url" all_install[$idx]="$install_path"; all_steps[$idx]="$steps" all_versions[$idx]="$version"; all_desktops[$idx]="$desktop"; all_guis[$idx]="$gui" all_types[$idx]="$type" idx=$((idx + 1)) fi # Now process each app — stdin is the terminal, so read -rp works for ((i=0; i