diff --git a/misc/tools.func b/misc/tools.func index 76974c828..7ad1c023c 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1,669 +1,670 @@ +#!/bin/bash install_node_and_modules() { - local NODE_VERSION="${NODE_VERSION:-22}" - local NODE_MODULE="${NODE_MODULE:-}" - local CURRENT_NODE_VERSION="" - local NEED_NODE_INSTALL=false + local NODE_VERSION="${NODE_VERSION:-22}" + local NODE_MODULE="${NODE_MODULE:-}" + local CURRENT_NODE_VERSION="" + local NEED_NODE_INSTALL=false - # Check if Node.js is already installed - if command -v node >/dev/null; then - CURRENT_NODE_VERSION="$(node -v | grep -oP '^v\K[0-9]+')" - if [[ "$CURRENT_NODE_VERSION" != "$NODE_VERSION" ]]; then - msg_info "Node.js version $CURRENT_NODE_VERSION found, replacing with $NODE_VERSION" - NEED_NODE_INSTALL=true - else - msg_ok "Node.js $NODE_VERSION already installed" - fi + # Check if Node.js is already installed + if command -v node >/dev/null; then + CURRENT_NODE_VERSION="$(node -v | grep -oP '^v\K[0-9]+')" + if [[ "$CURRENT_NODE_VERSION" != "$NODE_VERSION" ]]; then + msg_info "Node.js version $CURRENT_NODE_VERSION found, replacing with $NODE_VERSION" + NEED_NODE_INSTALL=true else - msg_info "Node.js not found, installing version $NODE_VERSION" - NEED_NODE_INSTALL=true + msg_ok "Node.js $NODE_VERSION already installed" + fi + else + msg_info "Node.js not found, installing version $NODE_VERSION" + NEED_NODE_INSTALL=true + fi + + # Install Node.js if required + if [[ "$NEED_NODE_INSTALL" == true ]]; then + $STD apt-get purge -y nodejs + rm -f /etc/apt/sources.list.d/nodesource.list /etc/apt/keyrings/nodesource.gpg + + mkdir -p /etc/apt/keyrings + + if ! curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | + gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg; then + msg_error "Failed to download or import NodeSource GPG key" + exit 1 fi - # Install Node.js if required - if [[ "$NEED_NODE_INSTALL" == true ]]; then - $STD apt-get purge -y nodejs - rm -f /etc/apt/sources.list.d/nodesource.list /etc/apt/keyrings/nodesource.gpg + echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_${NODE_VERSION}.x nodistro main" \ + >/etc/apt/sources.list.d/nodesource.list - mkdir -p /etc/apt/keyrings - - if ! curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | - gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg; then - msg_error "Failed to download or import NodeSource GPG key" - exit 1 - fi - - echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_${NODE_VERSION}.x nodistro main" \ - >/etc/apt/sources.list.d/nodesource.list - - if ! apt-get update >/dev/null 2>&1; then - msg_error "Failed to update APT repositories after adding NodeSource" - exit 1 - fi - - if ! apt-get install -y nodejs >/dev/null 2>&1; then - msg_error "Failed to install Node.js ${NODE_VERSION} from NodeSource" - exit 1 - fi - - msg_ok "Installed Node.js ${NODE_VERSION}" + if ! apt-get update >/dev/null 2>&1; then + msg_error "Failed to update APT repositories after adding NodeSource" + exit 1 fi - export NODE_OPTIONS="--max_old_space_size=4096" - - # Install global Node modules - if [[ -n "$NODE_MODULE" ]]; then - IFS=',' read -ra MODULES <<<"$NODE_MODULE" - for mod in "${MODULES[@]}"; do - local MODULE_NAME MODULE_REQ_VERSION MODULE_INSTALLED_VERSION - if [[ "$mod" == *"@"* ]]; then - MODULE_NAME="${mod%@*}" - MODULE_REQ_VERSION="${mod#*@}" - else - MODULE_NAME="$mod" - MODULE_REQ_VERSION="latest" - fi - - # Check if the module is already installed - if npm list -g --depth=0 "$MODULE_NAME" >/dev/null 2>&1; then - MODULE_INSTALLED_VERSION="$(npm list -g --depth=0 "$MODULE_NAME" | grep "$MODULE_NAME@" | awk -F@ '{print $2}' | tr -d '[:space:]')" - if [[ "$MODULE_REQ_VERSION" != "latest" && "$MODULE_REQ_VERSION" != "$MODULE_INSTALLED_VERSION" ]]; then - msg_info "Updating $MODULE_NAME from v$MODULE_INSTALLED_VERSION to v$MODULE_REQ_VERSION" - if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}"; then - msg_error "Failed to update $MODULE_NAME to version $MODULE_REQ_VERSION" - exit 1 - fi - elif [[ "$MODULE_REQ_VERSION" == "latest" ]]; then - msg_info "Updating $MODULE_NAME to latest version" - if ! $STD npm install -g "${MODULE_NAME}@latest"; then - msg_error "Failed to update $MODULE_NAME to latest version" - exit 1 - fi - else - msg_ok "$MODULE_NAME@$MODULE_INSTALLED_VERSION already installed" - fi - else - msg_info "Installing $MODULE_NAME@$MODULE_REQ_VERSION" - if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}"; then - msg_error "Failed to install $MODULE_NAME@$MODULE_REQ_VERSION" - exit 1 - fi - fi - done - msg_ok "All requested Node modules have been processed" + if ! apt-get install -y nodejs >/dev/null 2>&1; then + msg_error "Failed to install Node.js ${NODE_VERSION} from NodeSource" + exit 1 fi + + msg_ok "Installed Node.js ${NODE_VERSION}" + fi + + export NODE_OPTIONS="--max-old-space-size=4096" + + # Install global Node modules + if [[ -n "$NODE_MODULE" ]]; then + IFS=',' read -ra MODULES <<<"$NODE_MODULE" + for mod in "${MODULES[@]}"; do + local MODULE_NAME MODULE_REQ_VERSION MODULE_INSTALLED_VERSION + if [[ "$mod" == *"@"* ]]; then + MODULE_NAME="${mod%@*}" + MODULE_REQ_VERSION="${mod#*@}" + else + MODULE_NAME="$mod" + MODULE_REQ_VERSION="latest" + fi + + # Check if the module is already installed + if npm list -g --depth=0 "$MODULE_NAME" >/dev/null 2>&1; then + MODULE_INSTALLED_VERSION="$(npm list -g --depth=0 "$MODULE_NAME" | grep "$MODULE_NAME@" | awk -F@ '{print $2}' | tr -d '[:space:]')" + if [[ "$MODULE_REQ_VERSION" != "latest" && "$MODULE_REQ_VERSION" != "$MODULE_INSTALLED_VERSION" ]]; then + msg_info "Updating $MODULE_NAME from v$MODULE_INSTALLED_VERSION to v$MODULE_REQ_VERSION" + if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}"; then + msg_error "Failed to update $MODULE_NAME to version $MODULE_REQ_VERSION" + exit 1 + fi + elif [[ "$MODULE_REQ_VERSION" == "latest" ]]; then + msg_info "Updating $MODULE_NAME to latest version" + if ! $STD npm install -g "${MODULE_NAME}@latest"; then + msg_error "Failed to update $MODULE_NAME to latest version" + exit 1 + fi + else + msg_ok "$MODULE_NAME@$MODULE_INSTALLED_VERSION already installed" + fi + else + msg_info "Installing $MODULE_NAME@$MODULE_REQ_VERSION" + if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}"; then + msg_error "Failed to install $MODULE_NAME@$MODULE_REQ_VERSION" + exit 1 + fi + fi + done + msg_ok "All requested Node modules have been processed" + fi } install_postgresql() { - local PG_VERSION="${PG_VERSION:-16}" - local CURRENT_PG_VERSION="" - local DISTRO - local NEED_PG_INSTALL=false - DISTRO="$(awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release)" + local PG_VERSION="${PG_VERSION:-16}" + local CURRENT_PG_VERSION="" + local DISTRO + local NEED_PG_INSTALL=false + DISTRO="$(awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release)" - if command -v psql >/dev/null; then - CURRENT_PG_VERSION="$(psql -V | grep -oP '\s\K[0-9]+(?=\.)')" - if [[ "$CURRENT_PG_VERSION" != "$PG_VERSION" ]]; then - msg_info "PostgreSQL Version $CURRENT_PG_VERSION found, replacing with $PG_VERSION" - NEED_PG_INSTALL=true - fi - else - msg_info "PostgreSQL not found, installing version $PG_VERSION" - NEED_PG_INSTALL=true + if command -v psql >/dev/null; then + CURRENT_PG_VERSION="$(psql -V | grep -oP '\s\K[0-9]+(?=\.)')" + if [[ "$CURRENT_PG_VERSION" != "$PG_VERSION" ]]; then + msg_info "PostgreSQL Version $CURRENT_PG_VERSION found, replacing with $PG_VERSION" + NEED_PG_INSTALL=true fi + else + msg_info "PostgreSQL not found, installing version $PG_VERSION" + NEED_PG_INSTALL=true + fi - if [[ "$NEED_PG_INSTALL" == true ]]; then - msg_info "Stopping PostgreSQL if running" - systemctl stop postgresql >/dev/null 2>&1 || true + if [[ "$NEED_PG_INSTALL" == true ]]; then + msg_info "Stopping PostgreSQL if running" + systemctl stop postgresql >/dev/null 2>&1 || true - msg_info "Removing conflicting PostgreSQL packages" - $STD apt-get purge -y "postgresql*" - rm -f /etc/apt/sources.list.d/pgdg.list /etc/apt/trusted.gpg.d/postgresql.gpg + msg_info "Removing conflicting PostgreSQL packages" + $STD apt-get purge -y "postgresql*" + rm -f /etc/apt/sources.list.d/pgdg.list /etc/apt/trusted.gpg.d/postgresql.gpg - msg_info "Setting up PostgreSQL Repository" - curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | - gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg + msg_info "Setting up PostgreSQL Repository" + curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | + gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg - echo "deb https://apt.postgresql.org/pub/repos/apt ${DISTRO}-pgdg main" \ - >/etc/apt/sources.list.d/pgdg.list + echo "deb https://apt.postgresql.org/pub/repos/apt ${DISTRO}-pgdg main" \ + >/etc/apt/sources.list.d/pgdg.list - $STD apt-get update - $STD apt-get install -y "postgresql-${PG_VERSION}" + $STD apt-get update + $STD apt-get install -y "postgresql-${PG_VERSION}" - msg_ok "Installed PostgreSQL ${PG_VERSION}" - fi + msg_ok "Installed PostgreSQL ${PG_VERSION}" + fi } install_mariadb() { - local MARIADB_VERSION="${MARIADB_VERSION:-latest}" - local DISTRO_CODENAME - DISTRO_CODENAME="$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release)" + local MARIADB_VERSION="${MARIADB_VERSION:-latest}" + local DISTRO_CODENAME + DISTRO_CODENAME="$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release)" - # grab dynamic latest LTS version - if [[ "$MARIADB_VERSION" == "latest" ]]; then - msg_info "Resolving latest MariaDB version" - MARIADB_VERSION=$(curl -fsSL https://mariadb.org | grep -oP 'MariaDB \K10\.[0-9]+' | head -n1) - if [[ -z "$MARIADB_VERSION" ]]; then - msg_error "Could not determine latest MariaDB version" - return 1 - fi - msg_ok "Latest MariaDB version is $MARIADB_VERSION" + # grab dynamic latest LTS version + if [[ "$MARIADB_VERSION" == "latest" ]]; then + msg_info "Resolving latest MariaDB version" + MARIADB_VERSION=$(curl -fsSL https://mariadb.org | grep -oP 'MariaDB \K10\.[0-9]+' | head -n1) + if [[ -z "$MARIADB_VERSION" ]]; then + msg_error "Could not determine latest MariaDB version" + return 1 fi + msg_ok "Latest MariaDB version is $MARIADB_VERSION" + fi - local CURRENT_VERSION="" - if command -v mariadb >/dev/null; then - CURRENT_VERSION="$(mariadb --version | grep -oP 'Ver\s+\K[0-9]+\.[0-9]+')" - fi - - if [[ "$CURRENT_VERSION" == "$MARIADB_VERSION" ]]; then - msg_info "MariaDB $MARIADB_VERSION already installed, checking for upgrade" - $STD apt-get update - $STD apt-get install --only-upgrade -y mariadb-server mariadb-client - msg_ok "MariaDB $MARIADB_VERSION upgraded if applicable" - return 0 - fi - - if [[ -n "$CURRENT_VERSION" ]]; then - msg_info "Replacing MariaDB $CURRENT_VERSION with $MARIADB_VERSION (data will be preserved)" - $STD systemctl stop mariadb >/dev/null 2>&1 || true - $STD apt-get purge -y 'mariadb*' || true - rm -f /etc/apt/sources.list.d/mariadb.list /etc/apt/trusted.gpg.d/mariadb.gpg - else - msg_info "Installing MariaDB $MARIADB_VERSION" - fi - - msg_info "Setting up MariaDB Repository" - curl -fsSL "https://mariadb.org/mariadb_release_signing_key.asc" | - gpg --dearmor -o /etc/apt/trusted.gpg.d/mariadb.gpg - - echo "deb [signed-by=/etc/apt/trusted.gpg.d/mariadb.gpg] http://mirror.mariadb.org/repo/${MARIADB_VERSION}/debian ${DISTRO_CODENAME} main" \ - >/etc/apt/sources.list.d/mariadb.list + local CURRENT_VERSION="" + if command -v mariadb >/dev/null; then + CURRENT_VERSION="$(mariadb --version | grep -oP 'Ver\s+\K[0-9]+\.[0-9]+')" + fi + if [[ "$CURRENT_VERSION" == "$MARIADB_VERSION" ]]; then + msg_info "MariaDB $MARIADB_VERSION already installed, checking for upgrade" $STD apt-get update - $STD apt-get install -y mariadb-server mariadb-client + $STD apt-get install --only-upgrade -y mariadb-server mariadb-client + msg_ok "MariaDB $MARIADB_VERSION upgraded if applicable" + return 0 + fi - msg_ok "Installed MariaDB $MARIADB_VERSION" + if [[ -n "$CURRENT_VERSION" ]]; then + msg_info "Replacing MariaDB $CURRENT_VERSION with $MARIADB_VERSION (data will be preserved)" + $STD systemctl stop mariadb >/dev/null 2>&1 || true + $STD apt-get purge -y 'mariadb*' || true + rm -f /etc/apt/sources.list.d/mariadb.list /etc/apt/trusted.gpg.d/mariadb.gpg + else + msg_info "Installing MariaDB $MARIADB_VERSION" + fi + + msg_info "Setting up MariaDB Repository" + curl -fsSL "https://mariadb.org/mariadb_release_signing_key.asc" | + gpg --dearmor -o /etc/apt/trusted.gpg.d/mariadb.gpg + + echo "deb [signed-by=/etc/apt/trusted.gpg.d/mariadb.gpg] http://mirror.mariadb.org/repo/${MARIADB_VERSION}/debian ${DISTRO_CODENAME} main" \ + >/etc/apt/sources.list.d/mariadb.list + + $STD apt-get update + $STD apt-get install -y mariadb-server mariadb-client + + msg_ok "Installed MariaDB $MARIADB_VERSION" } install_mysql() { - local MYSQL_VERSION="${MYSQL_VERSION:-8.0}" - local CURRENT_VERSION="" - local NEED_INSTALL=false + local MYSQL_VERSION="${MYSQL_VERSION:-8.0}" + local CURRENT_VERSION="" + local NEED_INSTALL=false - if command -v mysql >/dev/null; then - CURRENT_VERSION="$(mysql --version | grep -oP 'Distrib\s+\K[0-9]+\.[0-9]+')" - if [[ "$CURRENT_VERSION" != "$MYSQL_VERSION" ]]; then - msg_info "MySQL $CURRENT_VERSION found, replacing with $MYSQL_VERSION" - NEED_INSTALL=true - else - msg_ok "MySQL $MYSQL_VERSION already installed" - fi + if command -v mysql >/dev/null; then + CURRENT_VERSION="$(mysql --version | grep -oP 'Distrib\s+\K[0-9]+\.[0-9]+')" + if [[ "$CURRENT_VERSION" != "$MYSQL_VERSION" ]]; then + msg_info "MySQL $CURRENT_VERSION found, replacing with $MYSQL_VERSION" + NEED_INSTALL=true else - msg_info "MySQL not found, installing version $MYSQL_VERSION" - NEED_INSTALL=true + msg_ok "MySQL $MYSQL_VERSION already installed" fi + else + msg_info "MySQL not found, installing version $MYSQL_VERSION" + NEED_INSTALL=true + fi - if [[ "$NEED_INSTALL" == true ]]; then - msg_info "Removing conflicting MySQL packages" - $STD systemctl stop mysql >/dev/null 2>&1 || true - $STD apt-get purge -y 'mysql*' - rm -f /etc/apt/sources.list.d/mysql.list /etc/apt/trusted.gpg.d/mysql.gpg + if [[ "$NEED_INSTALL" == true ]]; then + msg_info "Removing conflicting MySQL packages" + $STD systemctl stop mysql >/dev/null 2>&1 || true + $STD apt-get purge -y 'mysql*' + rm -f /etc/apt/sources.list.d/mysql.list /etc/apt/trusted.gpg.d/mysql.gpg - msg_info "Setting up MySQL APT Repository" - DISTRO_CODENAME="$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release)" - curl -fsSL https://repo.mysql.com/RPM-GPG-KEY-mysql-2022 | gpg --dearmor -o /etc/apt/trusted.gpg.d/mysql.gpg - echo "deb [signed-by=/etc/apt/trusted.gpg.d/mysql.gpg] https://repo.mysql.com/apt/debian/ ${DISTRO_CODENAME} mysql-${MYSQL_VERSION}" \ - >/etc/apt/sources.list.d/mysql.list + msg_info "Setting up MySQL APT Repository" + DISTRO_CODENAME="$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release)" + curl -fsSL https://repo.mysql.com/RPM-GPG-KEY-mysql-2022 | gpg --dearmor -o /etc/apt/trusted.gpg.d/mysql.gpg + echo "deb [signed-by=/etc/apt/trusted.gpg.d/mysql.gpg] https://repo.mysql.com/apt/debian/ ${DISTRO_CODENAME} mysql-${MYSQL_VERSION}" \ + >/etc/apt/sources.list.d/mysql.list - $STD apt-get update - $STD apt-get install -y mysql-server + $STD apt-get update + $STD apt-get install -y mysql-server - msg_ok "Installed MySQL $MYSQL_VERSION" - fi + msg_ok "Installed MySQL $MYSQL_VERSION" + fi } install_php() { - local PHP_VERSION="${PHP_VERSION:-8.4}" - local PHP_MODULE="${PHP_MODULE:-}" - local PHP_APACHE="${PHP_APACHE:-NO}" - local PHP_FPM="${PHP_FPM:-NO}" - local DEFAULT_MODULES="bcmath,cli,curl,gd,intl,mbstring,opcache,readline,xml,zip" - local COMBINED_MODULES + local PHP_VERSION="${PHP_VERSION:-8.4}" + local PHP_MODULE="${PHP_MODULE:-}" + local PHP_APACHE="${PHP_APACHE:-NO}" + local PHP_FPM="${PHP_FPM:-NO}" + local DEFAULT_MODULES="bcmath,cli,curl,gd,intl,mbstring,opcache,readline,xml,zip" + local COMBINED_MODULES - local PHP_MEMORY_LIMIT="${PHP_MEMORY_LIMIT:-512M}" - local PHP_UPLOAD_MAX_FILESIZE="${PHP_UPLOAD_MAX_FILESIZE:-128M}" - local PHP_POST_MAX_SIZE="${PHP_POST_MAX_SIZE:-128M}" - local PHP_MAX_EXECUTION_TIME="${PHP_MAX_EXECUTION_TIME:-300}" + local PHP_MEMORY_LIMIT="${PHP_MEMORY_LIMIT:-512M}" + local PHP_UPLOAD_MAX_FILESIZE="${PHP_UPLOAD_MAX_FILESIZE:-128M}" + local PHP_POST_MAX_SIZE="${PHP_POST_MAX_SIZE:-128M}" + local PHP_MAX_EXECUTION_TIME="${PHP_MAX_EXECUTION_TIME:-300}" - # Merge default + user-defined modules - if [[ -n "$PHP_MODULE" ]]; then - COMBINED_MODULES="${DEFAULT_MODULES},${PHP_MODULE}" - else - COMBINED_MODULES="${DEFAULT_MODULES}" + # Merge default + user-defined modules + if [[ -n "$PHP_MODULE" ]]; then + COMBINED_MODULES="${DEFAULT_MODULES},${PHP_MODULE}" + else + COMBINED_MODULES="${DEFAULT_MODULES}" + fi + + # Deduplicate modules + COMBINED_MODULES=$(echo "$COMBINED_MODULES" | tr ',' '\n' | awk '!seen[$0]++' | paste -sd, -) + + local CURRENT_PHP + CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) + + if [[ "$CURRENT_PHP" != "$PHP_VERSION" ]]; then + $STD echo "PHP $CURRENT_PHP detected, migrating to PHP $PHP_VERSION" + if [[ ! -f /etc/apt/sources.list.d/php.list ]]; then + $STD curl -fsSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb + $STD dpkg -i /tmp/debsuryorg-archive-keyring.deb + echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" \ + >/etc/apt/sources.list.d/php.list + $STD apt-get update fi - # Deduplicate modules - COMBINED_MODULES=$(echo "$COMBINED_MODULES" | tr ',' '\n' | awk '!seen[$0]++' | paste -sd, -) + $STD apt-get purge -y "php${CURRENT_PHP//./}"* || true + fi - local CURRENT_PHP - CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) + local MODULE_LIST="php${PHP_VERSION}" + IFS=',' read -ra MODULES <<<"$COMBINED_MODULES" + for mod in "${MODULES[@]}"; do + MODULE_LIST+=" php${PHP_VERSION}-${mod}" + done - if [[ "$CURRENT_PHP" != "$PHP_VERSION" ]]; then - $STD echo "PHP $CURRENT_PHP detected, migrating to PHP $PHP_VERSION" - if [[ ! -f /etc/apt/sources.list.d/php.list ]]; then - $STD curl -fsSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb - $STD dpkg -i /tmp/debsuryorg-archive-keyring.deb - echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" \ - >/etc/apt/sources.list.d/php.list - $STD apt-get update - fi - - $STD apt-get purge -y "php${CURRENT_PHP//./}"* || true + if [[ "$PHP_APACHE" == "YES" ]]; then + # Optionally disable old Apache PHP module + if [[ -f /etc/apache2/mods-enabled/php${CURRENT_PHP}.load ]]; then + $STD a2dismod php${CURRENT_PHP} || true fi + fi - local MODULE_LIST="php${PHP_VERSION}" - IFS=',' read -ra MODULES <<<"$COMBINED_MODULES" - for mod in "${MODULES[@]}"; do - MODULE_LIST+=" php${PHP_VERSION}-${mod}" - done + if [[ "$PHP_FPM" == "YES" ]]; then + $STD systemctl stop php${CURRENT_PHP}-fpm || true + $STD systemctl disable php${CURRENT_PHP}-fpm || true + fi - if [[ "$PHP_APACHE" == "YES" ]]; then - # Optionally disable old Apache PHP module - if [[ -f /etc/apache2/mods-enabled/php${CURRENT_PHP}.load ]]; then - $STD a2dismod php${CURRENT_PHP} || true - fi + $STD apt-get install -y $MODULE_LIST + msg_ok "Installed PHP $PHP_VERSION with selected modules" + + if [[ "$PHP_APACHE" == "YES" ]]; then + $STD systemctl restart apache2 || true + fi + + if [[ "$PHP_FPM" == "YES" ]]; then + $STD systemctl enable php${PHP_VERSION}-fpm + $STD systemctl restart php${PHP_VERSION}-fpm + fi + + # Patch all relevant php.ini files + local PHP_INI_PATHS=() + PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/cli/php.ini") + [[ "$PHP_FPM" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/fpm/php.ini") + [[ "$PHP_APACHE" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/apache2/php.ini") + + for ini in "${PHP_INI_PATHS[@]}"; do + if [[ -f "$ini" ]]; then + msg_info "Patching $ini" + sed -i "s|^memory_limit = .*|memory_limit = ${PHP_MEMORY_LIMIT}|" "$ini" + sed -i "s|^upload_max_filesize = .*|upload_max_filesize = ${PHP_UPLOAD_MAX_FILESIZE}|" "$ini" + sed -i "s|^post_max_size = .*|post_max_size = ${PHP_POST_MAX_SIZE}|" "$ini" + sed -i "s|^max_execution_time = .*|max_execution_time = ${PHP_MAX_EXECUTION_TIME}|" "$ini" + msg_ok "Patched $ini" fi - - if [[ "$PHP_FPM" == "YES" ]]; then - $STD systemctl stop php${CURRENT_PHP}-fpm || true - $STD systemctl disable php${CURRENT_PHP}-fpm || true - fi - - $STD apt-get install -y $MODULE_LIST - msg_ok "Installed PHP $PHP_VERSION with selected modules" - - if [[ "$PHP_APACHE" == "YES" ]]; then - $STD systemctl restart apache2 || true - fi - - if [[ "$PHP_FPM" == "YES" ]]; then - $STD systemctl enable php${PHP_VERSION}-fpm - $STD systemctl restart php${PHP_VERSION}-fpm - fi - - # Patch all relevant php.ini files - local PHP_INI_PATHS=() - PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/cli/php.ini") - [[ "$PHP_FPM" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/fpm/php.ini") - [[ "$PHP_APACHE" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/apache2/php.ini") - - for ini in "${PHP_INI_PATHS[@]}"; do - if [[ -f "$ini" ]]; then - msg_info "Patching $ini" - sed -i "s|^memory_limit = .*|memory_limit = ${PHP_MEMORY_LIMIT}|" "$ini" - sed -i "s|^upload_max_filesize = .*|upload_max_filesize = ${PHP_UPLOAD_MAX_FILESIZE}|" "$ini" - sed -i "s|^post_max_size = .*|post_max_size = ${PHP_POST_MAX_SIZE}|" "$ini" - sed -i "s|^max_execution_time = .*|max_execution_time = ${PHP_MAX_EXECUTION_TIME}|" "$ini" - msg_ok "Patched $ini" - fi - done + done } install_composer() { - local COMPOSER_BIN="/usr/local/bin/composer" - export COMPOSER_ALLOW_SUPERUSER=1 + local COMPOSER_BIN="/usr/local/bin/composer" + export COMPOSER_ALLOW_SUPERUSER=1 - # Check if composer is already installed - if [[ -x "$COMPOSER_BIN" ]]; then - local CURRENT_VERSION - CURRENT_VERSION=$("$COMPOSER_BIN" --version | awk '{print $3}') - msg_info "Composer $CURRENT_VERSION found, updating to latest" - else - msg_info "Composer not found, installing latest version" - fi + # Check if composer is already installed + if [[ -x "$COMPOSER_BIN" ]]; then + local CURRENT_VERSION + CURRENT_VERSION=$("$COMPOSER_BIN" --version | awk '{print $3}') + msg_info "Composer $CURRENT_VERSION found, updating to latest" + else + msg_info "Composer not found, installing latest version" + fi - # Download and install latest composer - curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php - php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer >/dev/null 2>&1 + # Download and install latest composer + curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php + php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer >/dev/null 2>&1 - if [[ $? -ne 0 ]]; then - msg_error "Failed to install Composer" - return 1 - fi + if [[ $? -ne 0 ]]; then + msg_error "Failed to install Composer" + return 1 + fi - chmod +x "$COMPOSER_BIN" - msg_ok "Installed Composer $($COMPOSER_BIN --version | awk '{print $3}')" + chmod +x "$COMPOSER_BIN" + msg_ok "Installed Composer $($COMPOSER_BIN --version | awk '{print $3}')" } install_go() { - local ARCH - case "$(uname -m)" in - x86_64) ARCH="amd64" ;; - aarch64) ARCH="arm64" ;; - *) - msg_error "Unsupported architecture: $(uname -m)" - return 1 - ;; - esac + local ARCH + case "$(uname -m)" in + x86_64) ARCH="amd64" ;; + aarch64) ARCH="arm64" ;; + *) + msg_error "Unsupported architecture: $(uname -m)" + return 1 + ;; + esac - # Determine version - if [[ -z "$GO_VERSION" || "$GO_VERSION" == "latest" ]]; then - GO_VERSION=$(curl -fsSL https://go.dev/VERSION?m=text | head -n1 | sed 's/^go//') - if [[ -z "$GO_VERSION" ]]; then - msg_error "Could not determine latest Go version" - return 1 - fi - msg_info "Detected latest Go version: $GO_VERSION" + # Determine version + if [[ -z "${GO_VERSION:-}" || "${GO_VERSION}" == "latest" ]]; then + GO_VERSION=$(curl -fsSL https://go.dev/VERSION?m=text | head -n1 | sed 's/^go//') + if [[ -z "$GO_VERSION" ]]; then + msg_error "Could not determine latest Go version" + return 1 fi + msg_info "Detected latest Go version: $GO_VERSION" + fi - local GO_BIN="/usr/local/bin/go" - local GO_INSTALL_DIR="/usr/local/go" + local GO_BIN="/usr/local/bin/go" + local GO_INSTALL_DIR="/usr/local/go" - if [[ -x "$GO_BIN" ]]; then - local CURRENT_VERSION - CURRENT_VERSION=$("$GO_BIN" version | awk '{print $3}' | sed 's/go//') - if [[ "$CURRENT_VERSION" == "$GO_VERSION" ]]; then - msg_ok "Go $GO_VERSION already installed" - return 0 - else - msg_info "Go $CURRENT_VERSION found, upgrading to $GO_VERSION" - rm -rf "$GO_INSTALL_DIR" - fi + if [[ -x "$GO_BIN" ]]; then + local CURRENT_VERSION + CURRENT_VERSION=$("$GO_BIN" version | awk '{print $3}' | sed 's/go//') + if [[ "$CURRENT_VERSION" == "$GO_VERSION" ]]; then + msg_ok "Go $GO_VERSION already installed" + return 0 else - msg_info "Installing Go $GO_VERSION" + msg_info "Go $CURRENT_VERSION found, upgrading to $GO_VERSION" + rm -rf "$GO_INSTALL_DIR" fi + else + msg_info "Installing Go $GO_VERSION" + fi - local TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz" - local URL="https://go.dev/dl/${TARBALL}" - local TMP_TAR=$(mktemp) + local TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz" + local URL="https://go.dev/dl/${TARBALL}" + local TMP_TAR=$(mktemp) - curl -fsSL "$URL" -o "$TMP_TAR" || { - msg_error "Failed to download $TARBALL" - return 1 - } + curl -fsSL "$URL" -o "$TMP_TAR" || { + msg_error "Failed to download $TARBALL" + return 1 + } - tar -C /usr/local -xzf "$TMP_TAR" - ln -sf /usr/local/go/bin/go /usr/local/bin/go - ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt - rm -f "$TMP_TAR" + tar -C /usr/local -xzf "$TMP_TAR" + ln -sf /usr/local/go/bin/go /usr/local/bin/go + ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt + rm -f "$TMP_TAR" - msg_ok "Installed Go $GO_VERSION" + msg_ok "Installed Go $GO_VERSION" } install_java() { - local JAVA_VERSION="${JAVA_VERSION:-17}" - local DISTRO_CODENAME - DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) - local DESIRED_PACKAGE="temurin-${JAVA_VERSION}-jdk" + local JAVA_VERSION="${JAVA_VERSION:-21}" + local DISTRO_CODENAME + DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) + local DESIRED_PACKAGE="temurin-${JAVA_VERSION}-jdk" - # Add Adoptium repo if missing - if [[ ! -f /etc/apt/sources.list.d/adoptium.list ]]; then - msg_info "Setting up Adoptium Repository" - mkdir -p /etc/apt/keyrings - curl -fsSL "https://packages.adoptium.net/artifactory/api/gpg/key/public" | gpg --dearmor -o /etc/apt/trusted.gpg.d/adoptium.gpg - echo "deb [signed-by=/etc/apt/trusted.gpg.d/adoptium.gpg] https://packages.adoptium.net/artifactory/deb ${DISTRO_CODENAME} main" \ - >/etc/apt/sources.list.d/adoptium.list - $STD apt-get update - msg_ok "Set up Adoptium Repository" + # Add Adoptium repo if missing + if [[ ! -f /etc/apt/sources.list.d/adoptium.list ]]; then + msg_info "Setting up Adoptium Repository" + mkdir -p /etc/apt/keyrings + curl -fsSL "https://packages.adoptium.net/artifactory/api/gpg/key/public" | gpg --dearmor -o /etc/apt/trusted.gpg.d/adoptium.gpg + echo "deb [signed-by=/etc/apt/trusted.gpg.d/adoptium.gpg] https://packages.adoptium.net/artifactory/deb ${DISTRO_CODENAME} main" \ + >/etc/apt/sources.list.d/adoptium.list + $STD apt-get update + msg_ok "Set up Adoptium Repository" + fi + + # Detect currently installed temurin version + local INSTALLED_VERSION="" + if dpkg -l | grep -q "temurin-.*-jdk"; then + INSTALLED_VERSION=$(dpkg -l | awk '/temurin-.*-jdk/{print $2}' | grep -oP 'temurin-\K[0-9]+') + fi + + if [[ "$INSTALLED_VERSION" == "$JAVA_VERSION" ]]; then + msg_info "Temurin JDK $JAVA_VERSION already installed, updating if needed" + $STD apt-get update + $STD apt-get install --only-upgrade -y "$DESIRED_PACKAGE" + msg_ok "Updated Temurin JDK $JAVA_VERSION (if applicable)" + else + if [[ -n "$INSTALLED_VERSION" ]]; then + msg_info "Removing Temurin JDK $INSTALLED_VERSION" + $STD apt-get purge -y "temurin-${INSTALLED_VERSION}-jdk" fi - # Detect currently installed temurin version - local INSTALLED_VERSION="" - if dpkg -l | grep -q "temurin-.*-jdk"; then - INSTALLED_VERSION=$(dpkg -l | awk '/temurin-.*-jdk/{print $2}' | grep -oP 'temurin-\K[0-9]+') - fi - - if [[ "$INSTALLED_VERSION" == "$JAVA_VERSION" ]]; then - msg_info "Temurin JDK $JAVA_VERSION already installed, updating if needed" - $STD apt-get update - $STD apt-get install --only-upgrade -y "$DESIRED_PACKAGE" - msg_ok "Updated Temurin JDK $JAVA_VERSION (if applicable)" - else - if [[ -n "$INSTALLED_VERSION" ]]; then - msg_info "Removing Temurin JDK $INSTALLED_VERSION" - $STD apt-get purge -y "temurin-${INSTALLED_VERSION}-jdk" - fi - - msg_info "Installing Temurin JDK $JAVA_VERSION" - $STD apt-get install -y "$DESIRED_PACKAGE" - msg_ok "Installed Temurin JDK $JAVA_VERSION" - fi + msg_info "Installing Temurin JDK $JAVA_VERSION" + $STD apt-get install -y "$DESIRED_PACKAGE" + msg_ok "Installed Temurin JDK $JAVA_VERSION" + fi } install_mongodb() { - local MONGO_VERSION="${MONGO_VERSION:-8.0}" - local DISTRO_CODENAME - DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) - local REPO_LIST="/etc/apt/sources.list.d/mongodb-org-${MONGO_VERSION}.list" + local MONGO_VERSION="${MONGO_VERSION:-8.0}" + local DISTRO_CODENAME + DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) + local REPO_LIST="/etc/apt/sources.list.d/mongodb-org-${MONGO_VERSION}.list" - # Aktuell installierte Major-Version ermitteln - local INSTALLED_VERSION="" - if command -v mongod >/dev/null; then - INSTALLED_VERSION=$(mongod --version | awk '/db version/{print $3}' | cut -d. -f1,2) - fi - - if [[ "$INSTALLED_VERSION" == "$MONGO_VERSION" ]]; then - msg_info "MongoDB $MONGO_VERSION already installed, checking for upgrade" - $STD apt-get update - $STD apt-get install --only-upgrade -y mongodb-org - msg_ok "MongoDB $MONGO_VERSION upgraded if needed" - return 0 - fi - - # Ältere Version entfernen (nur Packages, nicht Daten!) - if [[ -n "$INSTALLED_VERSION" ]]; then - msg_info "Replacing MongoDB $INSTALLED_VERSION with $MONGO_VERSION (data will be preserved)" - $STD systemctl stop mongod || true - $STD apt-get purge -y mongodb-org || true - rm -f /etc/apt/sources.list.d/mongodb-org-*.list - rm -f /etc/apt/trusted.gpg.d/mongodb-*.gpg - else - msg_info "Installing MongoDB $MONGO_VERSION" - fi - - # MongoDB Repo hinzufügen - curl -fsSL "https://pgp.mongodb.com/server-${MONGO_VERSION}.asc" | gpg --dearmor -o "/etc/apt/trusted.gpg.d/mongodb-${MONGO_VERSION}.gpg" - echo "deb [signed-by=/etc/apt/trusted.gpg.d/mongodb-${MONGO_VERSION}.gpg] https://repo.mongodb.org/apt/debian ${DISTRO_CODENAME}/mongodb-org/${MONGO_VERSION} main" \ - >"$REPO_LIST" + # Aktuell installierte Major-Version ermitteln + local INSTALLED_VERSION="" + if command -v mongod >/dev/null; then + INSTALLED_VERSION=$(mongod --version | awk '/db version/{print $3}' | cut -d. -f1,2) + fi + if [[ "$INSTALLED_VERSION" == "$MONGO_VERSION" ]]; then + msg_info "MongoDB $MONGO_VERSION already installed, checking for upgrade" $STD apt-get update - $STD apt-get install -y mongodb-org + $STD apt-get install --only-upgrade -y mongodb-org + msg_ok "MongoDB $MONGO_VERSION upgraded if needed" + return 0 + fi - # Sicherstellen, dass Datenverzeichnis intakt bleibt - mkdir -p /var/lib/mongodb - chown -R mongodb:mongodb /var/lib/mongodb + # Ältere Version entfernen (nur Packages, nicht Daten!) + if [[ -n "$INSTALLED_VERSION" ]]; then + msg_info "Replacing MongoDB $INSTALLED_VERSION with $MONGO_VERSION (data will be preserved)" + $STD systemctl stop mongod || true + $STD apt-get purge -y mongodb-org || true + rm -f /etc/apt/sources.list.d/mongodb-org-*.list + rm -f /etc/apt/trusted.gpg.d/mongodb-*.gpg + else + msg_info "Installing MongoDB $MONGO_VERSION" + fi - $STD systemctl enable mongod - $STD systemctl start mongod - msg_ok "MongoDB $MONGO_VERSION installed and started" + # MongoDB Repo hinzufügen + curl -fsSL "https://pgp.mongodb.com/server-${MONGO_VERSION}.asc" | gpg --dearmor -o "/etc/apt/trusted.gpg.d/mongodb-${MONGO_VERSION}.gpg" + echo "deb [signed-by=/etc/apt/trusted.gpg.d/mongodb-${MONGO_VERSION}.gpg] https://repo.mongodb.org/apt/debian ${DISTRO_CODENAME}/mongodb-org/${MONGO_VERSION} main" \ + >"$REPO_LIST" + + $STD apt-get update + $STD apt-get install -y mongodb-org + + # Sicherstellen, dass Datenverzeichnis intakt bleibt + mkdir -p /var/lib/mongodb + chown -R mongodb:mongodb /var/lib/mongodb + + $STD systemctl enable mongod + $STD systemctl start mongod + msg_ok "MongoDB $MONGO_VERSION installed and started" } fetch_and_deploy_gh_release() { - local repo="$1" - local app=$(echo ${APPLICATION,,} | tr -d ' ') - local api_url="https://api.github.com/repos/$repo/releases/latest" - local header=() - local attempt=0 - local max_attempts=3 - local api_response tag http_code - local current_version="" - local curl_timeout="--connect-timeout 10 --max-time 30" + local repo="$1" + local app=$(echo ${APPLICATION,,} | tr -d ' ') + local api_url="https://api.github.com/repos/$repo/releases/latest" + local header=() + local attempt=0 + local max_attempts=3 + local api_response tag http_code + local current_version="" + local curl_timeout="--connect-timeout 10 --max-time 30" - # Check if the app directory exists and if there's a version file - if [[ -f "/opt/${app}_version.txt" ]]; then - current_version=$(cat "/opt/${app}_version.txt") - $STD msg_info "Current version: $current_version" + # Check if the app directory exists and if there's a version file + if [[ -f "/opt/${app}_version.txt" ]]; then + current_version=$(cat "/opt/${app}_version.txt") + $STD msg_info "Current version: $current_version" + fi + + # ensure that jq is installed + if ! command -v jq &>/dev/null; then + $STD msg_info "Installing jq..." + $STD apt-get update -qq &>/dev/null + $STD apt-get install -y jq &>/dev/null || { + msg_error "Failed to install jq" + return 1 + } + fi + + [[ -n "${GITHUB_TOKEN:-}" ]] && header=(-H "Authorization: token $GITHUB_TOKEN") + + until [[ $attempt -ge $max_attempts ]]; do + ((attempt++)) || true + $STD msg_info "[$attempt/$max_attempts] Fetching GitHub release for $repo...\n" + + api_response=$(curl $curl_timeout -fsSL -w "%{http_code}" -o /tmp/gh_resp.json "${header[@]}" "$api_url") + http_code="${api_response:(-3)}" + + if [[ "$http_code" == "404" ]]; then + msg_error "Repository $repo has no Release candidate (404)" + return 1 fi - # ensure that jq is installed - if ! command -v jq &>/dev/null; then - $STD msg_info "Installing jq..." - $STD apt-get update -qq &>/dev/null - $STD apt-get install -y jq &>/dev/null || { - msg_error "Failed to install jq" - return 1 - } + if [[ "$http_code" != "200" ]]; then + $STD msg_info "Request failed with HTTP $http_code, retrying...\n" + sleep $((attempt * 2)) + continue fi - [[ -n "${GITHUB_TOKEN:-}" ]] && header=(-H "Authorization: token $GITHUB_TOKEN") + api_response=$(/dev/null; then + msg_error "Repository not found: $repo" + return 1 + fi - if [[ "$http_code" == "404" ]]; then - msg_error "Repository $repo has no Release candidate (404)" - return 1 - fi - - if [[ "$http_code" != "200" ]]; then - $STD msg_info "Request failed with HTTP $http_code, retrying...\n" - sleep $((attempt * 2)) - continue - fi - - api_response=$(/dev/null; then - msg_error "Repository not found: $repo" - return 1 - fi - - tag=$(echo "$api_response" | jq -r '.tag_name // .name // empty') - [[ "$tag" =~ ^v[0-9] ]] && tag="${tag:1}" - - if [[ -z "$tag" ]]; then - $STD msg_info "Empty tag received, retrying...\n" - sleep $((attempt * 2)) - continue - fi - - $STD msg_ok "Found release: $tag for $repo" - break - done + tag=$(echo "$api_response" | jq -r '.tag_name // .name // empty') + [[ "$tag" =~ ^v[0-9] ]] && tag="${tag:1}" if [[ -z "$tag" ]]; then - msg_error "Failed to fetch release for $repo after $max_attempts attempts." - exit 1 + $STD msg_info "Empty tag received, retrying...\n" + sleep $((attempt * 2)) + continue fi - # Version comparison (if we already have this version, skip) - if [[ "$current_version" == "$tag" ]]; then - $STD msg_info "Already running the latest version ($tag). Skipping update." - return 0 + $STD msg_ok "Found release: $tag for $repo" + break + done + + if [[ -z "$tag" ]]; then + msg_error "Failed to fetch release for $repo after $max_attempts attempts." + exit 1 + fi + + # Version comparison (if we already have this version, skip) + if [[ "$current_version" == "$tag" ]]; then + $STD msg_info "Already running the latest version ($tag). Skipping update." + return 0 + fi + + local version="$tag" + local base_url="https://github.com/$repo/releases/download/v$tag" + local tmpdir + tmpdir=$(mktemp -d) || return 1 + + # Extract list of assets from the Release API + local assets urls + assets=$(echo "$api_response" | jq -r '.assets[].browser_download_url') || true + + # Detect current architecture + local arch + if command -v dpkg &>/dev/null; then + arch=$(dpkg --print-architecture) + elif command -v uname &>/dev/null; then + case "$(uname -m)" in + x86_64) arch="amd64" ;; + aarch64) arch="arm64" ;; + armv7l) arch="armv7" ;; + armv6l) arch="armv6" ;; + *) arch="unknown" ;; + esac + else + arch="unknown" + fi + $STD msg_info "Detected system architecture: $arch" + + # Try to find a matching asset for our architecture + local url="" + for u in $assets; do + if [[ "$u" =~ $arch.*\.tar\.gz$ ]]; then + url="$u" + $STD msg_info "Found matching architecture asset: $url" + break fi + done - local version="$tag" - local base_url="https://github.com/$repo/releases/download/v$tag" - local tmpdir - tmpdir=$(mktemp -d) || return 1 - - # Extract list of assets from the Release API - local assets urls - assets=$(echo "$api_response" | jq -r '.assets[].browser_download_url') || true - - # Detect current architecture - local arch - if command -v dpkg &>/dev/null; then - arch=$(dpkg --print-architecture) - elif command -v uname &>/dev/null; then - case "$(uname -m)" in - x86_64) arch="amd64" ;; - aarch64) arch="arm64" ;; - armv7l) arch="armv7" ;; - armv6l) arch="armv6" ;; - *) arch="unknown" ;; - esac - else - arch="unknown" - fi - $STD msg_info "Detected system architecture: $arch" - - # Try to find a matching asset for our architecture - local url="" + # Fallback to other architectures if our specific one isn't found + if [[ -z "$url" ]]; then for u in $assets; do - if [[ "$u" =~ $arch.*\.tar\.gz$ ]]; then - url="$u" - $STD msg_info "Found matching architecture asset: $url" - break - fi + if [[ "$u" =~ (x86_64|amd64|arm64|armv7|armv6).*\.tar\.gz$ ]]; then + url="$u" + $STD msg_info "Architecture-specific asset not found, using: $url" + break + fi done + fi - # Fallback to other architectures if our specific one isn't found - if [[ -z "$url" ]]; then - for u in $assets; do - if [[ "$u" =~ (x86_64|amd64|arm64|armv7|armv6).*\.tar\.gz$ ]]; then - url="$u" - $STD msg_info "Architecture-specific asset not found, using: $url" - break - fi - done - fi + # Fallback to any tar.gz + if [[ -z "$url" ]]; then + for u in $assets; do + if [[ "$u" =~ \.tar\.gz$ ]]; then + url="$u" + $STD msg_info "Using generic tarball: $url" + break + fi + done + fi - # Fallback to any tar.gz - if [[ -z "$url" ]]; then - for u in $assets; do - if [[ "$u" =~ \.tar\.gz$ ]]; then - url="$u" - $STD msg_info "Using generic tarball: $url" - break - fi - done - fi + # Final fallback to GitHub source tarball + if [[ -z "$url" ]]; then + url="https://github.com/$repo/archive/refs/tags/$version.tar.gz" + $STD msg_info "Trying GitHub source tarball fallback: $url" + fi - # Final fallback to GitHub source tarball - if [[ -z "$url" ]]; then - url="https://github.com/$repo/archive/refs/tags/$version.tar.gz" - $STD msg_info "Trying GitHub source tarball fallback: $url" - fi + local filename="${url##*/}" + $STD msg_info "Downloading $url" - local filename="${url##*/}" - $STD msg_info "Downloading $url" - - if ! curl $curl_timeout -fsSL -o "$tmpdir/$filename" "$url"; then - msg_error "Failed to download release asset from $url" - rm -rf "$tmpdir" - return 1 - fi - - mkdir -p "/opt/$app" - - tar -xzf "$tmpdir/$filename" -C "$tmpdir" - local content_root - content_root=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d) - if [[ $(echo "$content_root" | wc -l) -eq 1 ]]; then - cp -r "$content_root"/* "/opt/$app/" - else - cp -r "$tmpdir"/* "/opt/$app/" - fi - - echo "$version" >"/opt/${app}_version.txt" - $STD msg_ok "Deployed $app v$version to /opt/$app" + if ! curl $curl_timeout -fsSL -o "$tmpdir/$filename" "$url"; then + msg_error "Failed to download release asset from $url" rm -rf "$tmpdir" + return 1 + fi + + mkdir -p "/opt/$app" + + tar -xzf "$tmpdir/$filename" -C "$tmpdir" + local content_root + content_root=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d) + if [[ $(echo "$content_root" | wc -l) -eq 1 ]]; then + cp -r "$content_root"/* "/opt/$app/" + else + cp -r "$tmpdir"/* "/opt/$app/" + fi + + echo "$version" >"/opt/${app}_version.txt" + $STD msg_ok "Deployed $app v$version to /opt/$app" + rm -rf "$tmpdir" } setup_local_ip_helper() { - local BASE_DIR="/usr/local/community-scripts/ip-management" - local SCRIPT_PATH="$BASE_DIR/update_local_ip.sh" - local IP_FILE="/run/local-ip.env" - local DISPATCHER_SCRIPT="/etc/networkd-dispatcher/routable.d/10-update-local-ip.sh" + local BASE_DIR="/usr/local/community-scripts/ip-management" + local SCRIPT_PATH="$BASE_DIR/update_local_ip.sh" + local IP_FILE="/run/local-ip.env" + local DISPATCHER_SCRIPT="/etc/networkd-dispatcher/routable.d/10-update-local-ip.sh" - mkdir -p "$BASE_DIR" + mkdir -p "$BASE_DIR" - # Install networkd-dispatcher if not present - if ! dpkg -s networkd-dispatcher >/dev/null 2>&1; then - $STD apt-get update -qq - $STD apt-get install -yq networkd-dispatcher - fi + # Install networkd-dispatcher if not present + if ! dpkg -s networkd-dispatcher >/dev/null 2>&1; then + apt-get update -qq + apt-get install -yq networkd-dispatcher + fi - # Write update_local_ip.sh - cat <<'EOF' >"$SCRIPT_PATH" + # Write update_local_ip.sh + cat <<'EOF' >"$SCRIPT_PATH" #!/bin/bash set -euo pipefail @@ -705,116 +706,198 @@ echo "LOCAL_IP=$current_ip" > "$IP_FILE" echo "[INFO] LOCAL_IP updated to $current_ip" EOF - chmod +x "$SCRIPT_PATH" + chmod +x "$SCRIPT_PATH" - # Install dispatcher hook - mkdir -p "$(dirname "$DISPATCHER_SCRIPT")" - cat <"$DISPATCHER_SCRIPT" + # Install dispatcher hook + mkdir -p "$(dirname "$DISPATCHER_SCRIPT")" + cat <"$DISPATCHER_SCRIPT" #!/bin/bash $SCRIPT_PATH EOF - chmod +x "$DISPATCHER_SCRIPT" - systemctl enable --now networkd-dispatcher.service + chmod +x "$DISPATCHER_SCRIPT" + systemctl enable --now networkd-dispatcher.service - $STD msg_ok "LOCAL_IP helper installed using networkd-dispatcher" + $STD msg_ok "LOCAL_IP helper installed using networkd-dispatcher" } import_local_ip() { - local IP_FILE="/run/local-ip.env" - if [[ -f "$IP_FILE" ]]; then - # shellcheck disable=SC1090 - source "$IP_FILE" - fi + local IP_FILE="/run/local-ip.env" + if [[ -f "$IP_FILE" ]]; then + # shellcheck disable=SC1090 + source "$IP_FILE" + fi - if [[ -z "${LOCAL_IP:-}" ]]; then - get_current_ip() { - local targets=("8.8.8.8" "1.1.1.1" "192.168.1.1" "10.0.0.1" "172.16.0.1" "default") - local ip + if [[ -z "${LOCAL_IP:-}" ]]; then + get_current_ip() { + local targets=("8.8.8.8" "1.1.1.1" "192.168.1.1" "10.0.0.1" "172.16.0.1" "default") + local ip - for target in "${targets[@]}"; do - if [[ "$target" == "default" ]]; then - ip=$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') - else - ip=$(ip route get "$target" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') - fi - if [[ -n "$ip" ]]; then - echo "$ip" - return 0 - fi - done - - return 1 - } - - LOCAL_IP="$(get_current_ip || true)" - if [[ -z "$LOCAL_IP" ]]; then - msg_error "Could not determine LOCAL_IP" - return 1 + for target in "${targets[@]}"; do + if [[ "$target" == "default" ]]; then + ip=$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') + else + ip=$(ip route get "$target" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') fi - fi + if [[ -n "$ip" ]]; then + echo "$ip" + return 0 + fi + done - export LOCAL_IP + return 1 + } + + LOCAL_IP="$(get_current_ip || true)" + if [[ -z "$LOCAL_IP" ]]; then + msg_error "Could not determine LOCAL_IP" + return 1 + fi + fi + + export LOCAL_IP +} + +function download_with_progress() { + local url="$1" + local output="$2" + if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi + + if ! command -v pv &>/dev/null; then + $STD apt-get install -y pv + fi + set -o pipefail + + # Content-Length aus HTTP-Header holen + local content_length + content_length=$(curl -fsSLI "$url" | awk '/Content-Length/ {print $2}' | tr -d '\r' || true) + + if [[ -z "$content_length" ]]; then + #msg_warn "Content-Length not available, falling back to plain download" + if ! curl -fL# -o "$output" "$url"; then + msg_error "Download failed" + return 1 + fi + else + if ! curl -fsSL "$url" | pv -s "$content_length" >"$output"; then + msg_error "Download failed" + return 1 + fi + fi } function setup_uv() { - $STD msg_info "Checking uv installation..." - UV_BIN="/usr/local/bin/uv" - TMP_DIR=$(mktemp -d) - ARCH=$(uname -m) + $STD msg_info "Checking uv installation..." + UV_BIN="/usr/local/bin/uv" + TMP_DIR=$(mktemp -d) + ARCH=$(uname -m) - if [[ "$ARCH" == "x86_64" ]]; then - UV_TAR="uv-x86_64-unknown-linux-gnu.tar.gz" - elif [[ "$ARCH" == "aarch64" ]]; then - UV_TAR="uv-aarch64-unknown-linux-gnu.tar.gz" - else - msg_error "Unsupported architecture: $ARCH" - rm -rf "$TMP_DIR" - return 1 - fi - - # get current github version - LATEST_VERSION=$(curl -s https://api.github.com/repos/astral-sh/uv/releases/latest | grep '"tag_name":' | cut -d '"' -f4 | sed 's/^v//') - if [[ -z "$LATEST_VERSION" ]]; then - msg_error "Could not fetch latest uv version from GitHub." - rm -rf "$TMP_DIR" - return 1 - fi - - # check if uv exists - if [[ -x "$UV_BIN" ]]; then - INSTALLED_VERSION=$($UV_BIN -V | awk '{print $2}') - if [[ "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then - $STD msg_ok "uv is already at the latest version ($INSTALLED_VERSION)" - rm -rf "$TMP_DIR" - # set path - if [[ ":$PATH:" != *":/usr/local/bin:"* ]]; then - export PATH="/usr/local/bin:$PATH" - fi - return 0 - else - $STD msg_info "Updating uv from $INSTALLED_VERSION to $LATEST_VERSION" - fi - else - $STD msg_info "uv not found. Installing version $LATEST_VERSION" - fi - - # install or update uv - curl -fsSL "https://github.com/astral-sh/uv/releases/latest/download/${UV_TAR}" -o "$TMP_DIR/uv.tar.gz" - tar -xzf "$TMP_DIR/uv.tar.gz" -C "$TMP_DIR" - install -m 755 "$TMP_DIR"/*/uv "$UV_BIN" + if [[ "$ARCH" == "x86_64" ]]; then + UV_TAR="uv-x86_64-unknown-linux-gnu.tar.gz" + elif [[ "$ARCH" == "aarch64" ]]; then + UV_TAR="uv-aarch64-unknown-linux-gnu.tar.gz" + else + msg_error "Unsupported architecture: $ARCH" rm -rf "$TMP_DIR" + return 1 + fi - # set path - ensure_usr_local_bin_persist - msg_ok "uv installed/updated to $LATEST_VERSION" + # get current github version + LATEST_VERSION=$(curl -s https://api.github.com/repos/astral-sh/uv/releases/latest | grep '"tag_name":' | cut -d '"' -f4 | sed 's/^v//') + if [[ -z "$LATEST_VERSION" ]]; then + msg_error "Could not fetch latest uv version from GitHub." + rm -rf "$TMP_DIR" + return 1 + fi + + # check if uv exists + if [[ -x "$UV_BIN" ]]; then + INSTALLED_VERSION=$($UV_BIN -V | awk '{print $2}') + if [[ "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then + $STD msg_ok "uv is already at the latest version ($INSTALLED_VERSION)" + rm -rf "$TMP_DIR" + # set path + if [[ ":$PATH:" != *":/usr/local/bin:"* ]]; then + export PATH="/usr/local/bin:$PATH" + fi + return 0 + else + $STD msg_info "Updating uv from $INSTALLED_VERSION to $LATEST_VERSION" + fi + else + $STD msg_info "uv not found. Installing version $LATEST_VERSION" + fi + + # install or update uv + curl -fsSL "https://github.com/astral-sh/uv/releases/latest/download/${UV_TAR}" -o "$TMP_DIR/uv.tar.gz" + tar -xzf "$TMP_DIR/uv.tar.gz" -C "$TMP_DIR" + install -m 755 "$TMP_DIR"/*/uv "$UV_BIN" + rm -rf "$TMP_DIR" + + # set path + ensure_usr_local_bin_persist + msg_ok "uv installed/updated to $LATEST_VERSION" } function ensure_usr_local_bin_persist() { - local PROFILE_FILE="/etc/profile.d/custom_path.sh" + local PROFILE_FILE="/etc/profile.d/custom_path.sh" - if [[ ! -f "$PROFILE_FILE" ]] && ! command -v pveversion &>/dev/null; then - echo 'export PATH="/usr/local/bin:$PATH"' >"$PROFILE_FILE" - chmod +x "$PROFILE_FILE" - fi + if [[ ! -f "$PROFILE_FILE" ]] && ! command -v pveversion &>/dev/null; then + echo 'export PATH="/usr/local/bin:$PATH"' >"$PROFILE_FILE" + chmod +x "$PROFILE_FILE" + fi +} + +function setup_gs() { + msg_info "Setup Ghostscript" + mkdir -p /tmp + TMP_DIR=$(mktemp -d) + CURRENT_VERSION=$(gs --version 2>/dev/null || echo "0") + + RELEASE_JSON=$(curl -fsSL https://api.github.com/repos/ArtifexSoftware/ghostpdl-downloads/releases/latest) + LATEST_VERSION=$(echo "$RELEASE_JSON" | grep '"tag_name":' | head -n1 | cut -d '"' -f4 | sed 's/^gs//') + LATEST_VERSION_DOTTED=$(echo "$RELEASE_JSON" | grep '"name":' | head -n1 | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+') + + if [[ -z "$LATEST_VERSION" ]]; then + msg_error "Could not determine latest Ghostscript version from GitHub." + rm -rf "$TMP_DIR" + return + fi + + if dpkg --compare-versions "$CURRENT_VERSION" ge "$LATEST_VERSION_DOTTED"; then + msg_ok "Ghostscript is already at version $CURRENT_VERSION" + rm -rf "$TMP_DIR" + return + fi + + msg_info "Installing/Updating Ghostscript to $LATEST_VERSION_DOTTED" + curl -fsSL "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs${LATEST_VERSION}/ghostscript-${LATEST_VERSION_DOTTED}.tar.gz" -o "$TMP_DIR/ghostscript.tar.gz" + + if ! tar -xzf "$TMP_DIR/ghostscript.tar.gz" -C "$TMP_DIR"; then + msg_error "Failed to extract Ghostscript archive." + rm -rf "$TMP_DIR" + return + fi + + cd "$TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" || { + msg_error "Failed to enter Ghostscript source directory." + rm -rf "$TMP_DIR" + } + $STD apt-get install -y build-essential libpng-dev zlib1g-dev + ./configure >/dev/null && make && sudo make install >/dev/null + local EXIT_CODE=$? + hash -r + if [[ ! -x "$(command -v gs)" ]]; then + if [[ -x /usr/local/bin/gs ]]; then + ln -sf /usr/local/bin/gs /usr/bin/gs + fi + fi + + rm -rf "$TMP_DIR" + + if [[ $EXIT_CODE -eq 0 ]]; then + msg_ok "Ghostscript installed/updated to version $LATEST_VERSION_DOTTED" + else + msg_error "Ghostscript installation failed" + fi }