358 lines
9.0 KiB
Bash
Executable File
358 lines
9.0 KiB
Bash
Executable File
#!/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 --depth 1 "$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<idx; i++)); do
|
|
process_app "${all_apps[$i]}" "${all_git[$i]}" "${all_wget[$i]}" "${all_install[$i]}" \
|
|
"${all_steps[$i]}" "${all_versions[$i]}" "${all_desktops[$i]}" "${all_guis[$i]}" \
|
|
"${all_types[$i]}"
|
|
done
|
|
}
|
|
|
|
# --- Main ---
|
|
|
|
if [ ! -f "$CONF_FILE" ]; then
|
|
echo "Error: $CONF_FILE not found."
|
|
exit 1
|
|
fi
|
|
|
|
rm -rf "$TMP_DIR"
|
|
mkdir -p "$TMP_DIR"
|
|
mkdir -p "$HOME/.local/state/HamPackServer"
|
|
|
|
trap "echo 'Cleaning up...'; rm -rf '$TMP_DIR'" EXIT
|
|
|
|
echo "Starting HamPack source builds..."
|
|
process_conf
|
|
|
|
echo ""
|
|
echo "All done." |