diff --git a/docs/linux/installer.sh b/docs/linux/installer.sh index 87f8b54e..8c442ddc 100755 --- a/docs/linux/installer.sh +++ b/docs/linux/installer.sh @@ -1,25 +1,40 @@ #!/bin/bash -set -euo pipefail # Exit immediately if a command exits with a non-zero status. -# Define common variables -DESKTOP_DIR=$HOME/.local/share/applications # Directory for desktop entries -GITHUB_REPO="phcode-dev/phoenix-desktop" -API_URL="https://api.github.com/repos/$GITHUB_REPO/releases/latest" +set -euo pipefail + +# Phoenix Code Linux installer. +# +# Downloads the latest Phoenix Code AppImage from the update JSON, then probes +# the downloaded AppImage with `--version` (early-exit handler at +# src-electron/main.js:23-26). If the probe succeeds, no apt/dnf/pacman install +# is attempted — the system already has every shared library Phoenix Code +# needs at startup, regardless of which package name provides it. +# +# Beyond the --version probe, the installer also checks for the gnome-keyring +# daemon binary — libsecret needs the daemon for keytar credential storage, +# and --version exits before keytar is require()d. Sudo is only requested if +# libs or the keyring daemon are actually missing. +# +# On Ubuntu 24.04+ / Debian 13+, Chromium's unprivileged userns sandbox can +# be blocked by the kernel sysctl. The app still launches because the wrapper +# script falls back to --no-sandbox; we no longer install an AppArmor profile +# to keep the kernel sandbox enabled (the renderer-RCE-to-syscall threat the +# kernel sandbox guards against is narrow, and Phoenix Code's actual attack +# surface flows through the IPC bridge which the kernel sandbox doesn't gate). +# +# Also wires up a desktop entry + `phcode` wrapper script that handles +# AppImage launch fallbacks. + +DESKTOP_DIR=$HOME/.local/share/applications +UPDATE_JSON_URL="https://updates.phcode.io/tauri/update-latest-experimental-build.json" ICON_URL="https://updates.phcode.io/icons/phoenix_icon.png" -GTK_URL="https://github.com/phcode-dev/dependencies/releases/download/v1.0.0/gtk.tar.xz" -WEBKIT2GTK_URL="https://github.com/phcode-dev/dependencies/releases/download/v1.0.0/webkit2gtk-4.0.tar.xz" INSTALL_DIR="$HOME/.phoenix-code" LINK_DIR="$HOME/.local/bin" DESKTOP_ENTRY_NAME="PhoenixCode.desktop" DESKTOP_APP_NAME="Phoenix Code" DESKTOP_ENTRY="$DESKTOP_DIR/$DESKTOP_ENTRY_NAME" -SCRIPT_NAME="phcode" # Name of the script to invoke the binary +SCRIPT_NAME="phcode" BINARY_NAME="phoenix-code" -# STARTUP_WM_CLASS is set to the binary name and used as the application's class name in the desktop file. -# This class name helps the desktop environment manage application windows. If native libraries are missing -# and the application needs to be launched via a script instead of directly, this variable is updated -# accordingly before the desktop file is created. -STARTUP_WM_CLASS=$BINARY_NAME - +APPIMAGE_NAME="phoenix-code.AppImage" declare -a MIME_TYPES=( "text/html" @@ -48,460 +63,310 @@ declare -a MIME_TYPES=( "text/cjs" ) - -# Define color variables for easy reference GREEN="\e[32m" YELLOW="\e[33m" RED="\e[31m" RESET="\e[0m" -# Cleanup Function -# -# Purpose: -# The cleanup function is designed to ensure that the script cleans up any temporary -# resources it used during its execution. Specifically, it removes the temporary directory -# created at the beginning of the script, along with all its contents. This is crucial for -# maintaining a clean file system and avoiding the accumulation of unused files. -# -# Behavior: -# - The function targets a temporary directory specified by the `$TMP_DIR` variable. -# - It uses `rm -rf` to recursively and forcefully remove this directory and all its contents. -# This includes any temporary files, downloaded assets, or other data generated during the script's execution. -# - The cleanup is registered with a `trap` command on the `EXIT` signal, ensuring it executes -# automatically when the script exits, regardless of the exit point. This includes normal completion, -# errors, or manual termination via signals like SIGINT (Ctrl+C). -# -# Considerations: -# - The function assumes that `$TMP_DIR` is correctly set to the path of the temporary directory. -# If `$TMP_DIR` is unset or incorrectly set, the function may not perform as intended. -# - Using `rm -rf` carries the risk of deleting significant data if misused. It's crucial that `$TMP_DIR` -# is always a temporary directory intended for deletion and does not overlap with any critical system or user directories. -# - The function does not provide a confirmation prompt before deletion, so it's assumed that any data within -# `$TMP_DIR` is expendable and safe to delete. -# -# Usage: -# This function is not intended to be called directly by the user. It's automatically triggered by the `trap` -# command set up at the beginning of the script. However, if manual cleanup is necessary, ensure that `$TMP_DIR` -# is correctly set to the temporary directory path before invoking the function. -# cleanup() { - rm -rf "$TMP_DIR" # Command to remove the temporary directory and its contents. + rm -rf "$TMP_DIR" } - -trap cleanup EXIT # Register the cleanup function to be called on script exit. - +# Surface user interrupts (Ctrl+C / kill) with a clear message and exit 130, +# while still running cleanup. Without this, dpkg unpacks and sudo prompts can +# make the script look unresponsive to SIGINT for a beat. +on_interrupt() { + echo + echo -e "${RED}Interrupted by user. Cleaning up...${RESET}" + cleanup + exit 130 +} +trap cleanup EXIT +trap on_interrupt INT TERM TMP_DIR=$(mktemp -d) -# Check OS Version Function -# -# Purpose: -# This function checks if the current operating system is Ubuntu 24.04, Linux Mint 22, or Kali Linux 2024.2 or newer. -# It reads the /etc/os-release file, which contains identification data for the operating system. -# -# Behavior: -# 1. The function first checks if the /etc/os-release file exists. -# 2. If the file exists, it sources the file to load the OS identification variables. -# 3. It then checks if: -# - The ID variable is set to "ubuntu" and the VERSION_ID variable is "24.04", -# - The ID variable is "linuxmint" and the VERSION_ID is "22", -# - The ID variable is "kali" and the VERSION_ID is "2024.2" or newer. -# 4. If any of these conditions are met, the function returns 0 (indicating success). -# 5. If none of these conditions are met, the function returns 1 (indicating failure). -# -# Usage: -# This function can be called to determine if special handling is needed for these specific versions of -# Ubuntu, Linux Mint, or Kali Linux, such as installing additional libraries or making compatibility adjustments. -# -# Example: -# if check_os_version; then -# echo "Supported OS version detected." -# else -# echo "Unsupported OS version." -# fi -# -# Notes: -# - This function assumes that the /etc/os-release file follows the standard format for Linux OS -# identification and that the distribution's identifiers are consistent with those specified in the script. +configure_wget_options() { + local wget_version + wget_version=$(wget --version | head -n1 | awk '{print $3}') + local major_version + major_version=$(echo "$wget_version" | cut -d. -f1) + + if [[ "$major_version" -ge 2 ]]; then + echo "-c --tries=10 --timeout=30 --waitretry=5 --progress=bar --retry-connrefused -O" + else + echo "-c -N --tries=10 --timeout=30 --waitretry=5 --retry-connrefused --show-progress -qO" + fi +} -check_os_version() { - if [ -f /etc/os-release ]; then - . /etc/os-release - # Set VERSION_ID to empty string if not defined (for rolling releases) - VERSION_ID="${VERSION_ID:-}" - # Define supported OS and version combinations - case "$ID:${VERSION_ID%%.*}" in # Extract major version number - ubuntu:24) # Ubuntu 24.x - return 0 - ;; - ubuntu:25) # Ubuntu 25.x - return 0 - ;; - linuxmint:22) # Linux Mint 22.x - return 0 - ;; - neon:24) # KDE Neon 24.x - return 0 - ;; - debian:13) # Debian 13.x (Trixie) - return 0 - ;; - kali:2024|kali:20[2-9][0-9]) # Kali 2024.x or newer - return 0 - ;; - esac +check_architecture() { + local arch + arch=$(uname -m) + if [ "$arch" != "x86_64" ]; then + echo -e "${RED}This installer only supports 64-bit x86 (x86_64). Detected: $arch.${RESET}" + exit 1 fi - return 1 } -# Create Invocation Script Function -# -# Purpose: -# Generates a Bash script that serves as a wrapper for invoking the installed binary of Phoenix Code. -# This script is intended to simplify the execution of Phoenix Code by providing a more accessible -# command interface. The generated script is placed in a user-accessible binary directory, making it -# possible to run Phoenix Code from anywhere in the system without needing to specify the full path -# to the binary. -# -# Parameters: -# - binary_path: The absolute path to the directory where the Phoenix Code binary is located. -# - script_name: The name to be given to the invocation script. This name is used both for the -# script file itself and for the symlink created in the user's binary directory. -# - link_dir: The directory where a symlink to the invocation script will be placed, typically -# a location in the user's PATH, such as ~/.local/bin, for easy access. -# -# Behavior: -# 1. Creates a new Bash script in the specified 'binary_path' with the 'script_name'. -# 2. Writes a shebang line, a warning comment indicating the script is auto-generated, and a -# command to execute the Phoenix Code binary with any passed arguments ("$@"). -# 3. Sets executable permissions on the newly created script. -# 4. Copies the script to the specified 'link_dir', creating a symlink in a user-accessible location. -# -# Considerations: -# - It's crucial that 'binary_path' and 'link_dir' are valid directories and that 'script_name' -# does not conflict with existing files in these directories to prevent unintended overwrites. -# - The function does not check for the existence of 'binary_path' or 'link_dir'; it assumes -# these directories are already created and accessible. -# - The generated script includes a warning comment advising against deletion, as it is -# essential for the proper functioning of Phoenix Code when invoked through this script. -# -# Usage: -# This function is intended to be called during the installation or upgrade process of Phoenix Code, -# after the binary has been placed in its final location. It should not be called arbitrarily, as it -# assumes a specific setup and context established by the installer script. -# +# Runtime-library probe: ask the AppImage to print its version and exit. This +# is handled at src-electron/main.js:23-26, but `require('electron')` runs +# before that handler — which means Chromium initializes its sandbox bits even +# for --version. On Ubuntu 24.04+ / Debian 13+ where unprivileged user +# namespaces are restricted, that init can SIGTRAP. Pass --no-sandbox so the +# probe isolates to "are the system shared libraries present and loadable?" +# regardless of the kernel's userns policy. +# +# The `{ … } 2>/dev/null` wrapper suppresses bash's own "Trace/breakpoint trap +# (core dumped)" report when the subprocess dies on signal — those messages +# look alarming but are harmless: we already detect failure via the exit code. +verify_appimage_launches() { + { "$1" --no-sandbox --version >/dev/null 2>&1; } 2>/dev/null +} + +# Returns 0 if the gnome-keyring secret-service daemon binary is on PATH. +# `--version` can't test this because keytar is only require()d at first use, +# so we need an independent presence check. +gnome_keyring_present() { + command -v gnome-keyring-daemon >/dev/null 2>&1 +} + +downloadLatestReleaseInfo() { + local release_info_file="$TMP_DIR/latest_release.json" + if [ ! -f "$release_info_file" ]; then + >&2 echo -e "${GREEN}Fetching the latest release information...${RESET}" + wget -qO "$release_info_file" "$UPDATE_JSON_URL" || { + >&2 echo -e "${RED}Failed to fetch release info from $UPDATE_JSON_URL. Check your internet connection.${RESET}" + exit 1 + } + fi + grep -Po '"version"\s*:\s*"\K[^"]+' "$release_info_file" | head -n1 +} + +# Parses the linux-x86_64 download URL out of the update JSON without needing jq. +getLinuxAppImageUrl() { + awk ' + /"linux-x86_64"[[:space:]]*:/ { in_linux=1 } + in_linux && /"url"[[:space:]]*:/ { + if (match($0, /"url"[[:space:]]*:[[:space:]]*"[^"]+"/)) { + s = substr($0, RSTART, RLENGTH) + sub(/^"url"[[:space:]]*:[[:space:]]*"/, "", s) + sub(/"$/, "", s) + print s + exit + } + } + ' "$TMP_DIR/latest_release.json" +} + +# Generates the `phcode` wrapper script. The wrapper runs the AppImage directly, +# falling back to --appimage-extract-and-run on FUSE failure or to --no-sandbox +# on userns/AppArmor failure (Ubuntu 24.04+ / Debian 13+ blocks Chromium's +# unprivileged user namespace sandbox unless our AppArmor profile is loaded). create_invocation_script() { - local binary_path="$1" + local install_dir="$1" local script_name="$2" local link_dir="$3" - echo "Creating an invocation script for the binary..." - # Start of the generated script - echo "#!/bin/bash" > "$binary_path/$script_name.sh" - # Add a warning comment about the script being auto-generated - echo "# DO NOT DELETE: This script is generated by the Phoenix Code installer." >> "$binary_path/$script_name" - echo "$binary_path/$BINARY_NAME \"\$@\"" >> "$binary_path/$script_name" - chmod +x "$binary_path/$script_name" - - echo "Copying the invocation script to $link_dir..." - mkdir -p "$link_dir" # Ensure the directory exists - cp "$binary_path/$script_name" "$link_dir/$script_name" + echo "Creating an invocation script for the AppImage..." + cat > "$install_dir/$script_name" <"\$ERR"; then + if grep -qiE 'fuse|libfuse|fusermount|/dev/fuse' "\$ERR"; then + rm -f "\$ERR" + exec "\$APPIMAGE" --appimage-extract-and-run "\$@" + fi + if grep -qiE 'userns|apparmor|sandbox|setuid|namespace|Check failed' "\$ERR"; then + rm -f "\$ERR" + exec "\$APPIMAGE" --no-sandbox "\$@" + fi + cat "\$ERR" >&2 + rm -f "\$ERR" + exit 1 +fi +rm -f "\$ERR" +EOF + chmod +x "$install_dir/$script_name" + mkdir -p "$link_dir" + cp "$install_dir/$script_name" "$link_dir/$script_name" echo -e "Invocation script created at: $link_dir/$script_name" } -# Install Dependencies Function -# -# Purpose: -# Installs the necessary dependencies for Phoenix Code to run properly on the user's system. -# This function identifies the user's Linux distribution and installs the appropriate packages -# using the distribution's package manager. It supports Ubuntu/Debian, Fedora/RHEL/CentOS, and -# Arch Linux distributions. -# -# Behavior: -# 1. Checks for administrative privileges by attempting a non-interactive `sudo` command. -# If the command fails, it prompts the user to enter their password for `sudo` access. -# 2. Determines the Linux distribution by examining the contents of `/etc/os-release`. -# 3. Based on the identified distribution, executes the appropriate package manager command -# with the required packages. Supported package managers are `apt` for Debian-based systems, -# `dnf` for Fedora/RHEL/CentOS, and `pacman` for Arch Linux. -# 4. Updates the package index (if applicable) and installs the packages. -# -# Considerations: -# - The function requires internet access to fetch package information and install packages. -# - It assumes that the user has `sudo` privileges to install packages system-wide. -# - The list of dependencies is hardcoded within the function. If Phoenix Code's dependencies change, -# this function must be updated accordingly. -# - Unsupported distributions will result in an error message and termination of the script, -# as the required dependencies cannot be installed automatically. -# - This function does not handle potential errors from package installation commands. It's -# recommended to monitor the output for any issues that may require manual intervention. -# -# Usage: -# This function is intended to be called during the Phoenix Code installation process, typically -# before attempting to run or configure the Phoenix Code binary. It ensures that all prerequisites -# are met for a successful Phoenix Code execution. -# -install_dependencies() { - echo "Attempting to install required dependencies..." - # Inform the user that the next steps require administrative access - echo "The installation of dependencies requires administrative access. You may be prompted to enter your password." - - # Check if the script can execute sudo commands without interaction - if ! sudo -n true 2>/dev/null; then - echo "Please enter your password to proceed with the installation of dependencies." +# Returns "gnome-keyring" unless the current session is KDE. +# On KDE, kwallet implements the Secret Service interface (KF 5.97+ / ksecretd +# in 2026), and installing gnome-keyring causes a D-Bus activation race for +# org.freedesktop.secrets — see VSCode issue #189672. +gnome_keyring_pkg_if_needed() { + if [[ "${XDG_CURRENT_DESKTOP:-}" =~ KDE ]]; then + echo "" + else + echo "gnome-keyring" fi +} - # Attempt to identify the Linux distribution - if [ -f /etc/os-release ]; then - . /etc/os-release - DISTRO=$ID +# Picks the libfuse2 package name for whatever Ubuntu/Debian release we're on. +# Ubuntu 24.04 Noble+ / Debian 13 Trixie+ / Kali rolling / Mint 22+ / Neon 24+ +# ship libfuse2t64 (the post-y2038 rename); older releases (Ubuntu 20.04 Focal +# / 22.04 Jammy, Debian 11/12, Mint 20/21, Neon 22) ship the pre-rename +# libfuse2. Both provide the same libfuse.so.2 SONAME, so picking by repo +# availability is correct. +choose_fuse_package_apt() { + if apt-cache show libfuse2t64 >/dev/null 2>&1; then + echo libfuse2t64 else - echo "Unable to identify the operating system." - exit 1 + echo libfuse2 fi +} - # Install dependencies based on the distribution - case "$DISTRO" in - ubuntu|debian|linuxmint|kali) - echo "Detected an Ubuntu/Debian based distribution." - sudo apt update - yes | sudo apt install libgtk-3-0 libwebkit2gtk-4.0-37 \ - gstreamer1.0-plugins-base gstreamer1.0-plugins-good \ - gstreamer1.0-tools +# Per-distro package install. Only called when verify_appimage_launches failed +# OR the keyring daemon is missing — i.e., we've already decided we need sudo. +install_packages_for_distro() { + local distro="$1" keyring="$2" + # RHEL 10 does not ship libXScrnSaver in any default channel — left off the + # dnf list intentionally; Electron's XScreenSaver calls no-op on Wayland. + # libxss + libnotify are listed for Arch because gtk3 does NOT pull them in + # transitively but Electron dlopen's them at runtime. + local -a pkgs=() + case "$distro" in + ubuntu|debian|linuxmint|kali|neon) pkgs=("$(choose_fuse_package_apt)" libsecret-1-0) ;; + fedora|rhel|centos) pkgs=(fuse-libs fuse3-libs libsecret) ;; + arch|manjaro|cachyos) pkgs=(fuse2 libsecret libxss libnotify) ;; + esac + [ -n "$keyring" ] && pkgs+=("$keyring") + + case "$distro" in + ubuntu|debian|linuxmint|kali|neon) + # No `yes |` pipeline: under `set -o pipefail`, `yes` dies of SIGPIPE + # after apt exits and the pipeline returns 141, silently aborting the + # installer. Use apt-get -y, and keep user /etc config files via the + # force-conf* options so an unrelated pending upgrade can't prompt. + echo "Detected an Ubuntu/Debian-based distribution." + sudo apt-get update + # shellcheck disable=SC2068 + sudo DEBIAN_FRONTEND=noninteractive apt-get install -y \ + -o Dpkg::Options::="--force-confdef" \ + -o Dpkg::Options::="--force-confold" \ + ${pkgs[@]} ;; fedora|rhel|centos) - echo "Detected a Fedora/Red Hat based distribution." - yes | sudo dnf install webkit2gtk3 gtk3 \ - gstreamer1-plugins-base gstreamer1-plugins-good - yes | sudo dnf upgrade webkit2gtk3 gtk3 \ - gstreamer1 gstreamer1-plugins-base gstreamer1-plugins-good + echo "Detected a Fedora/Red Hat-based distribution." + # shellcheck disable=SC2068 + sudo dnf install -y ${pkgs[@]} ;; arch|manjaro|cachyos) - echo "Detected an Arch Linux based distribution." - echo "Updating package database..." + echo "Detected an Arch-based distribution." sudo pacman -Sy - yes | sudo pacman -S webkit2gtk gtk3 libxml2 - ;; - *) - echo "Unsupported distribution. Please manually install the required dependencies." - exit 1 + # shellcheck disable=SC2068 + sudo pacman -S --noconfirm --needed ${pkgs[@]} ;; esac } -download_and_install_gtk() { - # Function: create_launch_script_with_gtk - # - # Purpose: - # This function creates a launch script for the Phoenix Code application that sets the required - # GTK library paths in the LD_LIBRARY_PATH environment variable. The launch script ensures that - # the Phoenix Code application uses the correct GTK libraries, which are included in the - # application's installation directory. - # - # Parameters: - # - binary_path: The path to the directory containing the Phoenix Code binary. - # - binary_name: The name of the Phoenix Code binary file. - # - # Behavior: - # 1. The function renames the original Phoenix Code binary to append ".real" to its name. - # 2. It then creates a new launch script with the original binary name in the same directory. - # 3. The launch script sets the LD_LIBRARY_PATH environment variable to include the path to the - # GTK libraries located in the same directory as the binary. - # 4. The launch script executes the original Phoenix Code binary (now renamed) with any - # arguments passed to the script. - # 5. The function sets the appropriate executable permissions on the launch script. - # - # Usage: - # This function should be called after the Phoenix Code binary and its required GTK libraries - # have been installed. It ensures that the application uses the correct libraries when launched. - # - # Example: - # create_launch_script_with_gtk "/path/to/phoenix-code" "phoenix-code" - # - # Notes: - # - This function assumes that the GTK libraries are located in a subdirectory named "gtk" within - # the same directory as the Phoenix Code binary. - # - The function requires the mv, chmod, and cat commands to be available in the environment. - # - This piece of code should only be executed if the package manager does not distribute - # libgtk or if the version provided by the package manager is not compatible with Phoenix Code. - - local URL_PREFIX="https://github.com/phcode-dev/dependencies/releases/download/v1.0.5/" - local GTK_FILE="gtk.tar.xz" - local WEBKIT2GTK_FILE="webkit2gtk-4.0.tar.xz" - - local GTK_URL="${URL_PREFIX}${GTK_FILE}" - local WEBKIT2GTK_URL="${URL_PREFIX}${WEBKIT2GTK_FILE}" - - echo -e "${YELLOW}Downloading GTK from $GTK_URL...${RESET}" - local destination="$TMP_DIR/$BINARY_NAME" - WGET_OPTS=$(configure_wget_options) - wget $WGET_OPTS "$TMP_DIR/gtk.tar.xz" "$GTK_URL" || { - echo -e "${RED}Failed to download GTK. Please check your internet connection and try again.${RESET}" - exit 1 - } - echo "Extracting GTK..." - tar -xJf "$TMP_DIR/$GTK_FILE" -C "$destination" || { - echo -e "${RED}Failed to extract GTK. The downloaded file might be corrupt.${RESET}" +# Decides what (if anything) the user's system is missing for Phoenix Code to +# run, then installs only the missing piece(s). Three independent probes: +# +# 1. System shared libraries — probed by running the downloaded AppImage +# with `--version`. main.js exits immediately on this flag, AFTER libfuse +# mounts the AppImage and AFTER the Electron binary loads its NEEDED libs. +# 2. gnome-keyring daemon — binary-presence check; `--version` doesn't +# exercise keytar so it can't verify libsecret/Secret Service end-to-end. +# +# Sudo is requested ONLY if at least one of the probes fails. +ensure_runtime_dependencies() { + local appimage="$1" + if [ ! -f /etc/os-release ]; then + echo -e "${RED}Unable to identify the operating system (no /etc/os-release).${RESET}" exit 1 - } - - if [ ! -d "/usr/lib/x86_64-linux-gnu/webkit2gtk-4.0" ]; then - echo -e "${YELLOW}Downloading WebKit2GTK from $WEBKIT2GTK_URL...${RESET}" - wget $WGET_OPTS "$TMP_DIR/$WEBKIT2GTK_FILE" "$WEBKIT2GTK_URL" || { - echo -e "${RED}Failed to download WebKit2GTK. Please check your internet connection and try again.${RESET}" - exit 1 - } - echo "Extracting WebKit2GTK..." - tar -xJf "$TMP_DIR/$WEBKIT2GTK_FILE" -C "$TMP_DIR" || { - echo -e "${RED}Failed to extract WebKit2GTK. The downloaded file might be corrupt.${RESET}" + fi + . /etc/os-release + local distro="${ID:-}" + case "$distro" in + ubuntu|debian|linuxmint|kali|neon|fedora|rhel|centos|arch|manjaro|cachyos) ;; + *) + echo -e "${RED}Unsupported distribution: $distro. Please install libfuse2/fuse2, libsecret, and (on non-KDE) gnome-keyring manually.${RESET}" exit 1 - } + ;; + esac + local keyring; keyring=$(gnome_keyring_pkg_if_needed) - # Inform the user that the next steps require administrative access - echo "The installation of dependencies requires administrative access. You may be prompted to enter your password." + local libs_ok=1 keyring_ok=1 + verify_appimage_launches "$appimage" || libs_ok=0 + if [ -n "$keyring" ] && ! gnome_keyring_present; then keyring_ok=0; fi - # Check if the script can execute sudo commands without interaction - if ! sudo -n true 2>/dev/null; then - echo "Please enter your password to proceed with the installation of dependencies." - fi + if [ "$libs_ok" = 1 ] && [ "$keyring_ok" = 1 ]; then + echo -e "${GREEN}All runtime dependencies already present.${RESET}" + return 0 + fi - echo "Installing WebKit2GTK..." - sudo cp -r "$TMP_DIR/webkit2gtk-4.0" /usr/lib/x86_64-linux-gnu/ || { - echo -e "${RED}Failed to install WebKit2GTK. Please check the permissions and try again.${RESET}" - exit 1 - } - echo -e "${GREEN}WebKit2GTK installed successfully.${RESET}" + if [ "$libs_ok" = 0 ]; then + echo "Installing missing runtime libraries..." else - echo -e "${GREEN}WebKit2GTK already installed.${RESET}" + echo "Installing system keychain daemon..." + fi + echo "This step requires administrative access." + if ! sudo -n true 2>/dev/null; then + echo "Please enter your password to proceed." fi -} - - -create_launch_script_with_gtk() { - # Function: create_launch_script_with_gtk - # - # Purpose: - # This function creates a launch script for the Phoenix Code application that sets the required - # GTK library paths in the LD_LIBRARY_PATH environment variable. The launch script ensures that - # the Phoenix Code application uses the correct GTK libraries, which are included in the - # application's installation directory. - # - # Parameters: - # - binary_path: The path to the directory containing the Phoenix Code binary. - # - binary_name: The name of the Phoenix Code binary file. - # - # Behavior: - # 1. The function renames the original Phoenix Code binary to append ".real" to its name. - # 2. It then creates a new launch script with the original binary name in the same directory. - # 3. The launch script sets the LD_LIBRARY_PATH environment variable to include the path to the - # GTK libraries located in the same directory as the binary. - # 4. The launch script executes the original Phoenix Code binary (now renamed) with any - # arguments passed to the script. - # 5. The function sets the appropriate executable permissions on the launch script. - # - # Usage: - # This function should be called after the Phoenix Code binary and its required GTK libraries - # have been installed. It ensures that the application uses the correct libraries when launched. - # - # Example: - # create_launch_script_with_gtk "/path/to/phoenix-code" "phoenix-code" - # - # Notes: - # - This function assumes that the GTK libraries are located in a subdirectory named "gtk" within - # the same directory as the Phoenix Code binary. - # - The function requires the mv, chmod, and cat commands to be available in the environment. - # - This piece of code should only be executed if the package manager does not distribute - # libgtk or if the version provided by the package manager is not compatible with Phoenix Code. - local binary_path="$1" - local binary_name="$BINARY_NAME" - local original_bin=$binary_name.app - local realBin="$binary_path/$original_bin" - mv "$binary_path/$binary_name" "$realBin" - echo "Creating a launch script for Phoenix Code with GTK libraries..." - cat > "$binary_path/$binary_name" </dev/null 2>&1 || return 0 + local locked + locked=$(busctl --user --no-pager call org.freedesktop.secrets \ + /org/freedesktop/secrets/collection/login \ + org.freedesktop.DBus.Properties Get \ + ss org.freedesktop.Secret.Collection Locked 2>/dev/null \ + | awk '/^b /{print $NF}') + [ "$locked" = "true" ] || return 0 - if "$TMP_DIR/phoenix-code/phoenix-code" --runVerify; then - echo "Application launch verification successful after installing dependencies." - return 0 - else - echo "Verification failed even after installing dependencies. Please check the application requirements or contact support." - return 1 + echo + echo -e "${YELLOW}Note: your system keychain is currently locked.${RESET}" + echo -e "${YELLOW}The first time Phoenix Code stores or reads a credential, gnome-keyring${RESET}" + echo -e "${YELLOW}will ask once for your login password to unlock it. This is a one-time${RESET}" + echo -e "${YELLOW}per-session Linux behavior — not a Phoenix Code prompt.${RESET}" + if autologin_detected; then + echo -e "${YELLOW}(Autologin is enabled on this machine, which is why PAM did not${RESET}" + echo -e "${YELLOW} auto-unlock the keyring at session start.)${RESET}" fi } -# Set Phoenix Code as the default application for specified MIME types + set_default_application() { - local desktop_file="$DESKTOP_ENTRY_NAME" # Name of the Phoenix Code desktop entry file + local desktop_file="$DESKTOP_ENTRY_NAME" - # Dynamically detect qtpaths if KDE is running and qtpaths is not in PATH if [ "${KDE_SESSION_VERSION:-0}" -gt 0 ] && ! command -v qtpaths &> /dev/null; then - # Common locations where qtpaths might be installed local qtpaths_locations=( "/usr/lib/qt6/bin" "/usr/lib/qt5/bin" @@ -510,7 +375,6 @@ set_default_application() { "/opt/qt6/bin" "/opt/qt5/bin" ) - for qtpath_dir in "${qtpaths_locations[@]}"; do if [ -x "$qtpath_dir/qtpaths" ] || [ -x "$qtpath_dir/qtpaths6" ]; then export PATH="$qtpath_dir:$PATH" @@ -520,82 +384,36 @@ set_default_application() { fi for mime_type in "${MIME_TYPES[@]}"; do - # Skip setting default application for text/html - if [ "$mime_type" = "text/html" ]; then - continue # Skip to the next iteration - fi - - xdg-mime default "$desktop_file" "$mime_type" + if [ "$mime_type" = "text/html" ]; then + continue + fi + xdg-mime default "$desktop_file" "$mime_type" done echo -e "${GREEN}Success! You can now right-click on files in your file manager and choose Phoenix Code to edit them.${RESET}" } -# Copy Files to Destination Function -# -# Purpose: -# This function is responsible for setting up the final installation directory for Phoenix Code, -# moving the necessary files into place, creating the invocation script, and setting up the desktop -# entry for the application. It ensures that all components of Phoenix Code are correctly installed -# and configured in the user's environment. -# -# Behavior: -# 1. Creates the installation directory at `$INSTALL_DIR`, where Phoenix Code and its related files -# will reside. -# 2. Similarly, ensures the desktop entry directory `$DESKTOP_DIR` exists for application launchers. -# 3. Moves the downloaded and possibly compiled Phoenix Code binary and other necessary files from -# the temporary directory (`$TMP_DIR`) to the installation directory. -# 4. Downloads and moves the application icon to the installation directory for use in the desktop entry. -# 5. Sets the correct executable permissions for the Phoenix Code binary to ensure it can be run by the user. -# 6. Calls `create_invocation_script` to generate a wrapper script for easier execution of Phoenix Code. -# 7. Constructs a `.desktop` file for Phoenix Code, defining how it should appear in application menus and -# launchers, and places this file in the desktop entry directory. -# 8. Updates the system's desktop database to ensure the new application entry is recognized and available -# in application menus and launchers. This step might involve commands like `update-desktop-database` -# or `kbuildsycoca5`, depending on the desktop environment. -# -# Considerations: -# - The function assumes that the necessary files for Phoenix Code are already present in `$TMP_DIR` and -# that this directory is correctly set. -# - It relies on specific directory paths (`$INSTALL_DIR`, `$DESKTOP_DIR`, `$LINK_DIR`) being set and -# accessible. If these variables are unset or incorrect, the function may not perform as intended. -# - Administrative privileges may be required for certain operations, such as updating the desktop database -# or setting permissions, depending on the system's configuration. -# - Care is taken to avoid overwriting existing files or directories without confirmation, but this function -# does perform significant file system operations that could potentially conflict with existing user data -# or system settings. -# -# Usage: -# This function is intended to be called as part of the Phoenix Code installation process, after the -# application's binary and other necessary files have been prepared in a temporary location. It finalizes -# the installation by placing all components in their appropriate locations within the user's system. -# -copyFilesToDestination(){ +copyFilesToDestination() { echo "Setting up the installation directory at $INSTALL_DIR..." mkdir -p "$INSTALL_DIR" mkdir -p "$DESKTOP_DIR" - echo -e "${YELLOW}Installation directory set up at: $INSTALL_DIR${RESET}" - echo "Moving the necessary files to the installation directory..." - echo -e "Phoenix Code files moved to: $INSTALL_DIR" - mv "$TMP_DIR"/phoenix-code/* "$INSTALL_DIR/" || { - echo -e "${RED}Failed to move the files to the installation directory. Please check the permissions and try again.${RESET}" + mv "$TMP_DIR/$APPIMAGE_NAME" "$INSTALL_DIR/$APPIMAGE_NAME" || { + echo -e "${RED}Failed to move the AppImage to the installation directory. Please check the permissions and try again.${RESET}" exit 1 } - # Move the icon to the installation directory mv "$TMP_DIR/icon.png" "$INSTALL_DIR/" - echo "Setting the correct permissions for the executable..." - chmod +x "$INSTALL_DIR/phoenix-code" || { - echo -e "${RED}Failed to set executable permissions. Please check the file path and permissions.${RESET}" + chmod +x "$INSTALL_DIR/$APPIMAGE_NAME" || { + echo -e "${RED}Failed to set executable permissions on the AppImage.${RESET}" exit 1 } - echo -e "Executable permissions set for: $INSTALL_DIR/$BINARY_NAME" + echo -e "AppImage installed at: $INSTALL_DIR/$APPIMAGE_NAME" - mkdir -p "$LINK_DIR" # Ensure the directory exists - # Call the function to create and copy the invocation script + mkdir -p "$LINK_DIR" create_invocation_script "$INSTALL_DIR" "$SCRIPT_NAME" "$LINK_DIR" - # Convert MIME types array to semicolon-separated string for the desktop entry - MIME_TYPES_STRING=$(IFS=";"; echo "${MIME_TYPES[*]}") + + local mime_types_string + mime_types_string=$(IFS=";"; echo "${MIME_TYPES[*]}") echo "Creating desktop entry..." cat > "$DESKTOP_ENTRY" < /dev/null; then - update-desktop-database "$DESKTOP_DIR" - echo -e "Desktop database updated in: $DESKTOP_DIR" + update-desktop-database "$DESKTOP_DIR" fi - - # Update the KDE desktop database if KDE is in use - if [ "$XDG_CURRENT_DESKTOP" = "KDE" ]; then + if [ "${XDG_CURRENT_DESKTOP:-}" = "KDE" ]; then if command -v kbuildsycoca5 &> /dev/null; then - kbuildsycoca5 + kbuildsycoca5 fi fi - - if [[ "$XDG_CURRENT_DESKTOP" =~ LXQt ]]; then + if [[ "${XDG_CURRENT_DESKTOP:-}" =~ LXQt ]]; then if command -v xdg-desktop-menu &> /dev/null; then - xdg-desktop-menu forceupdate - echo -e "${YELLOW}Please log out and log back in to see Phoenix Code in the panel.${RESET}" + xdg-desktop-menu forceupdate + echo -e "${YELLOW}Please log out and log back in to see Phoenix Code in the panel.${RESET}" else - echo -e "${RED}Failed to update LXQt menu. Please log out and log back in to see Phoenix Code in the panel.${RESET}" + echo -e "${RED}Failed to update LXQt menu. Please log out and log back in to see Phoenix Code in the panel.${RESET}" fi fi - # Set Phoenix Code as the default application for the MIME types + set_default_application echo -e "${GREEN}Installation completed successfully. Phoenix Code is now installed.${RESET}" - -} - -downloadLatestReleaseInfo() { - local release_info_file="$TMP_DIR/latest_release.json" - - if [ -f "$release_info_file" ]; then - # Only extract and echo the version number, without any additional messages - grep -Po '"tag_name":\s*"prod-app-v\K[\d.]+(?=")' "$release_info_file" - return - fi - - # Direct informational messages to stderr to avoid them being captured or displayed unexpectedly - >&2 echo -e "${GREEN}Fetching the latest release information from $GITHUB_REPO...${RESET}" - wget -qO "$release_info_file" "$API_URL" || { - >&2 echo -e "${RED}Failed to fetch the latest release information. Please check your internet connection and try again.${RESET}" - exit 1 - } - - # Only extract and echo the version number after successful download - grep -Po '"tag_name":\s*"prod-app-v\K[\d.]+(?=")' "$release_info_file" + print_keyring_hint_if_locked } -# Function to check if the architecture is 64-bit x86 -check_architecture() { - ARCHITECTURE=$(uname -m) - if [ "$ARCHITECTURE" != "x86_64" ]; then - local latestFileUrl - latestFileUrl=$(grep -oP 'https://[^"]*latest\.json' "$TMP_DIR/latest_release.json") - WGET_OPTS=$(configure_wget_options) - wget $WGET_OPTS "$TMP_DIR/latest.json" "$latestFileUrl" || { - echo -e "${RED}Failed to download the latestFile. Please check your internet connection and try again.${RESET}" - } - echo -e "${RED}This script can only be run on 64-bit x86 architecture.${RESET}" - exit 1 - fi -} -# Download Latest Release Information Function -# -# Purpose: -# Retrieves the latest release information for Phoenix Code from its GitHub repository. This function -# is designed to fetch the JSON data of the latest release using GitHub's API, allowing the script to -# determine the most recent version of Phoenix Code and any associated assets like binaries or icons. -# -# Behavior: -# 1. Checks if the release information file (`latest_release.json`) already exists in the temporary -# directory (`$TMP_DIR`). If it does, the function assumes the latest release information has already -# been fetched and proceeds to extract the version number from this file without re-downloading. -# 2. If the release information file does not exist, the function uses `wget` to download the JSON data -# from the GitHub API URL specified by `$API_URL`, which should point to the latest release endpoint -# of the Phoenix Code GitHub repository. -# 3. The JSON data is saved to `latest_release.json` in the temporary directory. -# 4. Extracts the version number from the downloaded JSON data using `grep` with a Perl-compatible regular -# expression (PCRE) that looks for the `tag_name` field. This field is expected to contain the version -# number prefixed by `prod-app-v` (e.g., `prod-app-v1.2.3`), and the function extracts the version number -# portion. -# 5. Echoes the extracted version number for use by the calling context. -# -# Considerations: -# - This function relies on external utilities `wget` and `grep` with PCRE support, which must be available -# in the execution environment. -# - The GitHub API URL (`$API_URL`) must be correctly set to the latest release endpoint of the Phoenix Code -# repository for this function to work as intended. -# - The function assumes a specific version naming convention (`prod-app-vX.Y.Z`). If this convention changes, -# the `grep` pattern used to extract the version number may need to be updated. -# - Network connectivity is required to fetch the release information from GitHub. If the script is run in an -# environment without internet access, or if GitHub is unreachable, this function will fail to retrieve the -# latest release information. -# - The function does not perform error handling for the `wget` command. If the download fails, the script may -# exit or behave unexpectedly depending on the `set -e` setting. -# -# Usage: -# This function is intended to be called during the installation or upgrade process to determine the latest -# version of Phoenix Code. The extracted version number can be used to compare with the currently installed -# version and decide whether an upgrade is necessary. -# -downloadAndInstall(){ +downloadAndInstall() { echo "Using temporary directory $TMP_DIR for processing" downloadLatestReleaseInfo > /dev/null check_architecture - CURRENT_GLIBC_VERSION=$(ldd --version | grep "ldd" | awk '{print $NF}') - echo "Current GLIBC version: $CURRENT_GLIBC_VERSION" - - BEST_MATCH_URL="" - BEST_MATCH_VERSION=0 - echo "Searching for a compatible binary..." - - while read -r BINARY_URL; do - BINARY_GLIBC_VERSION=$(echo "$BINARY_URL" | grep -oP 'GLIBC-\K[\d\.]+(?=\.tar\.gz)') - if awk -v bin_ver="$BINARY_GLIBC_VERSION" -v cur_ver="$CURRENT_GLIBC_VERSION" -v best_ver="$BEST_MATCH_VERSION" 'BEGIN { bin_ver += 0; cur_ver += 0; best_ver += 0; exit !(bin_ver <= cur_ver && bin_ver > best_ver) }'; then - BEST_MATCH_URL="$BINARY_URL" - BEST_MATCH_VERSION="$BINARY_GLIBC_VERSION" - echo "Found a new best match: $BEST_MATCH_URL with GLIBC version $BEST_MATCH_VERSION" - fi - done < <(grep -oP '"browser_download_url"\s*:\s*"\K[^"]*_linux_bin-GLIBC-[\d\.]+\.tar\.gz(?=")' "$TMP_DIR/latest_release.json") - if [ -z "$BEST_MATCH_URL" ]; then - echo -e "${RED}No compatible binary found for the current GLIBC version ($CURRENT_GLIBC_VERSION). Exiting installation.${RESET}" + local appimage_url + appimage_url=$(getLinuxAppImageUrl) + if [ -z "$appimage_url" ]; then + echo -e "${RED}Could not find a linux-x86_64 AppImage URL in the release info.${RESET}" exit 1 fi - echo -e "${YELLOW}Downloading the compatible binary from $BEST_MATCH_URL...${RESET}" - # Set options based on wget version - WGET_OPTS=$(configure_wget_options) - - wget $WGET_OPTS "$TMP_DIR/phoenix-code.tar.gz" "$BEST_MATCH_URL" || { - echo -e "${RED}Failed to download the binary. Please check your internet connection and try again.${RESET}" + echo -e "${YELLOW}Downloading the AppImage from $appimage_url...${RESET}" + local wget_opts + wget_opts=$(configure_wget_options) + # shellcheck disable=SC2086 + wget $wget_opts "$TMP_DIR/$APPIMAGE_NAME" "$appimage_url" || { + echo -e "${RED}Failed to download the AppImage. Please check your internet connection and try again.${RESET}" exit 1 } - # Download the icon - echo -e "Downloading the icon..." - wget $WGET_OPTS "$TMP_DIR/icon.png" "$ICON_URL" || { - echo -e "${RED}Failed to download the icon${RESET}" - exit 1 - } - echo "Extracting the binary to $TMP_DIR..." - tar -xzf "$TMP_DIR/phoenix-code.tar.gz" -C "$TMP_DIR" || { - echo -e "${RED}Failed to extract the binary. The downloaded file might be corrupt.${RESET}" + echo "Downloading the icon..." + # shellcheck disable=SC2086 + wget $wget_opts "$TMP_DIR/icon.png" "$ICON_URL" || { + echo -e "${RED}Failed to download the icon.${RESET}" exit 1 } - # Verify binary execution and install dependencies if necessary - if ! verify_and_install_dependencies; then - echo -e "${RED}Unable to successfully verify application launch. Exiting installation.${RESET}" - exit 1 - fi + chmod +x "$TMP_DIR/$APPIMAGE_NAME" + + ensure_runtime_dependencies "$TMP_DIR/$APPIMAGE_NAME" } -# Function to check wget version and configure options -configure_wget_options() { - local wget_version=$(wget --version | head -n1 | awk '{print $3}') - local major_version=$(echo "$wget_version" | cut -d. -f1) - if [[ "$major_version" -ge 2 ]]; then - echo "-c --tries=10 --timeout=30 --waitretry=5 --progress=bar --retry-connrefused -O" - else - echo "-c -N --tries=10 --timeout=30 --waitretry=5 --retry-connrefused --show-progress -qO" - fi +# Temporary cleanup for files from older installer versions. +uninstallBetaAppImage() { + rm -f "$LINK_DIR"/phoenix_icon.png } -# Install Function -# -# Purpose: -# Facilitates the entire installation process of Phoenix Code on the user's system. This includes -# checking for previous installations, downloading and installing the latest version, setting up -# necessary directories, and ensuring that all dependencies are met. It also handles user prompts -# for repair or reinstallation options if Phoenix Code is already installed. -# -# Behavior: -# 1. Checks if Phoenix Code is already installed by looking for specific files or directories -# associated with the installation (e.g., the invocation script in `$LINK_DIR` or the main -# directory in `$INSTALL_DIR`). If found, prompts the user with an option to repair (reinstall). -# 2. If the user opts for repair, the function proceeds to uninstall the existing installation and -# then reinstalls the application, ensuring the user ends up with the latest version. -# 3. For a fresh installation or repair, the function calls `downloadAndInstall` to fetch the latest -# release and install it. This step involves downloading the binary, setting permissions, and -# moving files to their correct locations. -# 4. After installing the binary, `copyFilesToDestination` is invoked to set up the desktop entry, -# create an invocation script, and perform other necessary post-installation configurations. -# -# Considerations: -# - The function assumes that the user has sufficient permissions to write to the target directories -# and install Phoenix Code. Administrative privileges may be required for certain operations. -# - If the repair option is chosen, all existing Phoenix Code files and configurations in the installation -# directory will be replaced. Users should ensure that any custom configurations or data are backed up -# before proceeding. -# - Network connectivity is required to download the latest version of Phoenix Code from the repository. -# - The function does not explicitly handle all potential error scenarios, such as download failures or -# permission issues. It relies on the `set -e` option to halt execution on any unhandled errors. -# -# Usage: -# This function is intended to be invoked when the script is run without specific options (e.g., not an -# uninstall or upgrade). It can be directly called from the command line when the user wishes to install -# Phoenix Code, or it may be triggered by default when the script is executed without arguments. -# + install() { - # Check if the application is already installed if [ -f "$LINK_DIR/$SCRIPT_NAME" ] || [ -d "$INSTALL_DIR" ]; then echo -e "${YELLOW}Phoenix Code appears to be already installed.${RESET}" - # Checking if the shell has a controlling terminal if its non interactive reinstall phoenix with latest version if [ ! -t 0 ]; then echo -e "${GREEN}Reinstalling Phoenix Code...${RESET}" uninstall downloadAndInstall copyFilesToDestination else - # Simplified prompt for reinstall without detailed explanation read -r -p "Would you like to reinstall it? (y/N): " response case "$response" in [Yy]* ) - echo -e "${GREEN}Reinstalling Phoenix Code...${RESET}" - uninstall - downloadAndInstall - copyFilesToDestination - ;; + echo -e "${GREEN}Reinstalling Phoenix Code...${RESET}" + uninstall + downloadAndInstall + copyFilesToDestination + ;; * ) - echo -e "${RED}Reinstall aborted by the user.${RESET}" - exit 0 - ;; + echo -e "${RED}Reinstall aborted by the user.${RESET}" + exit 0 + ;; esac fi else @@ -844,65 +522,23 @@ install() { fi } -# Temporary code to clean up earlier beta installations -function uninstallBetaAppImage() { - rm -f "$LINK_DIR"/phoenix_icon.png -} -# Upgrade Function -# -# Purpose: -# Manages the upgrade process for Phoenix Code, ensuring that the user's installation is updated -# to the latest version available. This function checks the currently installed version against -# the latest version available in the GitHub repository. If a newer version is found, it proceeds -# to download and install the update, effectively replacing the old version with the new one. -# -# Behavior: -# 1. Verifies that Phoenix Code is already installed by checking for the existence of the installation -# directory and the invocation script. If not found, it exits with an error message indicating that -# Phoenix Code must be installed before it can be upgraded. -# 2. Determines the currently installed version of Phoenix Code by invoking the binary with a version -# check command (e.g., `phoenix-code --version`) and captures the output. -# 3. Fetches the latest release information from the GitHub repository using the `downloadLatestReleaseInfo` -# function, which retrieves the version number of the latest release. -# 4. Compares the currently installed version with the latest version. If the installed version is older, -# it initiates the upgrade process by calling `downloadAndInstall`, which handles the download and -# installation of the new version. -# 5. If the currently installed version is already up to date, it informs the user that no upgrade is necessary. -# -# Considerations: -# - The function relies on the correct implementation of version checking in the Phoenix Code binary. -# The binary must support a command-line option to output its version, and the output format must be -# consistent for correct parsing and comparison. -# - Network connectivity is required to fetch the latest release information from the GitHub repository. -# The upgrade process will fail if the script cannot connect to GitHub. -# - The user must have sufficient permissions to overwrite the existing installation files and to -# execute network requests (e.g., downloading the latest release). -# - The upgrade process replaces the existing installation files. Users should ensure that any -# necessary backups or custom configurations are saved before proceeding with the upgrade. -# -# Usage: -# This function is intended to be called when the user explicitly wants to upgrade their installation -# of Phoenix Code to the latest version. It can be triggered by passing an `--upgrade` option to the -# script, as defined in the script's main case statement handling command-line arguments. -# upgrade() { echo -e "${YELLOW}Checking for upgrades to Phoenix Code...${RESET}" - # Ensure Phoenix Code is installed if [ ! -f "$LINK_DIR/$SCRIPT_NAME" ] && [ ! -d "$INSTALL_DIR" ]; then echo -e "${RED}Phoenix Code is not installed. Please install it first.${RESET}" exit 1 fi - # Get the current installed version - CURRENT_VERSION=$("$INSTALL_DIR/phoenix-code" --version) - echo "Current installed version: $CURRENT_VERSION" - LATEST_VERSION=$(downloadLatestReleaseInfo) - # Now LATEST_VERSION should only contain the version number without extra messages - echo "Latest available version: $LATEST_VERSION" + local current_version + current_version=$("$INSTALL_DIR/$APPIMAGE_NAME" --version 2>/dev/null || true) + echo "Current installed version: ${current_version:-unknown}" - # Compare versions and upgrade if the latest version is greater - if [ "$(printf '%s\n' "$LATEST_VERSION" "$CURRENT_VERSION" | sort -V | tail -n1)" = "$LATEST_VERSION" ] && [ "$LATEST_VERSION" != "$CURRENT_VERSION" ]; then + local latest_version + latest_version=$(downloadLatestReleaseInfo) + echo "Latest available version: $latest_version" + + if [ -n "$latest_version" ] && [ "$(printf '%s\n' "$latest_version" "$current_version" | sort -V | tail -n1)" = "$latest_version" ] && [ "$latest_version" != "$current_version" ]; then echo -e "${YELLOW}A newer version of Phoenix Code is available. Proceeding with the upgrade...${RESET}" downloadAndInstall uninstall @@ -911,48 +547,12 @@ upgrade() { else echo "Your Phoenix Code installation is up-to-date." fi - } -# Uninstall Function -# -# Purpose: -# Handles the removal of Phoenix Code from the user's system. This function is responsible for -# cleaning up all files, directories, and links created during the installation process, effectively -# reverting any changes made by the installer script. It ensures that Phoenix Code and its components -# are uninstalled cleanly, leaving no residual files or configurations. -# -# Behavior: -# 1. Checks for and removes the invocation script located in the user's binary directory (`$LINK_DIR`). -# This step prevents the Phoenix Code command from being accessible after uninstallation. -# 2. Deletes the desktop entry file (`$DESKTOP_ENTRY`) to remove Phoenix Code from application menus -# and launchers. This step is crucial for desktop environments to recognize that the application has been uninstalled. -# 3. Updates the desktop database, if necessary, to reflect the removal of the Phoenix Code desktop entry. -# This may involve commands like `update-desktop-database` or `kbuildsycoca5`, depending on the desktop environment. -# 4. Removes the Phoenix Code installation directory (`$INSTALL_DIR`) and all its contents, including -# the main binary, configuration files, and any other related files placed during installation. -# 5. Prints a confirmation message indicating that the uninstallation process has been completed successfully. -# -# Considerations: -# - The function assumes that the paths stored in `$LINK_DIR`, `$DESKTOP_ENTRY`, and `$INSTALL_DIR` accurately -# reflect the locations used during the installation. If these variables are incorrect, the uninstallation -# process may not remove all components. -# - Administrative privileges may be required for some operations, especially if Phoenix Code was installed -# in system-wide directories. -# - Users should be advised to back up any important data or configurations related to Phoenix Code before -# initiating the uninstallation process, as this function will remove all associated files without recovery options. -# - The function does not provide a rollback mechanism. Once the uninstallation is initiated, the process cannot -# be reversed through this script. -# -# Usage: -# This function is intended to be invoked when the user wishes to completely remove Phoenix Code from their system. -# It can be triggered by passing an `--uninstall` option to the script, as defined in the script's main case statement -# handling command-line arguments. Users should be prompted to confirm their intention to uninstall before this -# function is executed to prevent accidental data loss. -# + uninstall() { echo -e "${YELLOW}Starting uninstallation of Phoenix Code...${RESET}" uninstallBetaAppImage - # Remove the invocation script from ~/.local/bin + if [ -f "$LINK_DIR/$SCRIPT_NAME" ]; then echo -e "${YELLOW}Removing invocation script from $LINK_DIR...${RESET}" rm "$LINK_DIR/$SCRIPT_NAME" @@ -960,40 +560,33 @@ uninstall() { echo -e "${RED}Invocation script not found in $LINK_DIR. Skipping...${RESET}" fi - # Delete the desktop entry if [ -f "$DESKTOP_ENTRY" ]; then echo -e "${YELLOW}Removing desktop entry...${RESET}" rm "$DESKTOP_ENTRY" - - # Update the desktop database for GNOME, Unity, XFCE, etc. - echo -e "${YELLOW}Updating desktop database...${RESET}" if command -v update-desktop-database &> /dev/null; then - update-desktop-database "$DESKTOP_DIR" + update-desktop-database "$DESKTOP_DIR" fi - - # Update the KDE desktop database if KDE is in use - if [ "$XDG_CURRENT_DESKTOP" = "KDE" ]; then - if command -v kbuildsycoca5 &> /dev/null; then - kbuildsycoca5 - fi + if [ "${XDG_CURRENT_DESKTOP:-}" = "KDE" ]; then + if command -v kbuildsycoca5 &> /dev/null; then + kbuildsycoca5 + fi fi - - if [[ "$XDG_CURRENT_DESKTOP" =~ LXQt ]]; then + if [[ "${XDG_CURRENT_DESKTOP:-}" =~ LXQt ]]; then if command -v xdg-desktop-menu &> /dev/null; then - xdg-desktop-menu forceupdate + xdg-desktop-menu forceupdate fi fi else - echo -e "${RED}Desktop entry not found. Skipping...${RESET}" + echo -e "${RED}Desktop entry not found. Skipping...${RESET}" fi - # Remove the installation directory and its contents if [ -d "$INSTALL_DIR" ]; then echo -e "${YELLOW}Removing installation directory and its contents...${RESET}" rm -rf "$INSTALL_DIR" else echo -e "${RED}Installation directory not found. Skipping...${RESET}" fi + echo -e "${GREEN}Uninstallation of Phoenix Code completed.${RESET}" } @@ -1009,7 +602,6 @@ show_help() { echo "Without any options, the script will install Phoenix Code." } -# Check for GUI session by looking for DISPLAY or WAYLAND_DISPLAY variables if [ -z "${DISPLAY:-}" ] && [ -z "${WAYLAND_DISPLAY:-}" ]; then echo "This script should only be run from terminals in GUI sessions." exit 1 @@ -1017,22 +609,20 @@ fi case "${1-}" in -h|--help) - show_help # Function to show help + show_help ;; --uninstall) - uninstall # Function to uninstall + uninstall ;; --upgrade) - upgrade # Function to upgrade + upgrade ;; "") - # This case handles when $1 is unset (acts as a default action) - install # Function to install + install ;; *) - # This case handles unexpected arguments echo "Invalid option: $1" >&2 - show_help # Assuming you have a function to show usage information + show_help exit 1 ;; esac