install_node_and_modules() { 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 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 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}" 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 } function install_postgresql() { local PG_VERSION="${PG_VERSION:-15}" 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 fi 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 "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 $STD apt-get update $STD apt-get install -y "postgresql-${PG_VERSION}" msg_ok "Installed PostgreSQL ${PG_VERSION}" fi } function install_mariadb() { local MARIADB_VERSION="${MARIADB_VERSION:-10.11}" local CURRENT_VERSION="" local NEED_INSTALL=false if command -v mariadb >/dev/null; then CURRENT_VERSION="$(mariadb --version | grep -oP 'Ver\s+\K[0-9]+\.[0-9]+')" if [[ "$CURRENT_VERSION" != "$MARIADB_VERSION" ]]; then msg_info "MariaDB $CURRENT_VERSION found, replacing with $MARIADB_VERSION" NEED_INSTALL=true else msg_ok "MariaDB $MARIADB_VERSION already installed" fi else msg_info "MariaDB not found, installing version $MARIADB_VERSION" NEED_INSTALL=true fi if [[ "$NEED_INSTALL" == true ]]; then msg_info "Removing conflicting MariaDB packages" $STD systemctl stop mariadb >/dev/null 2>&1 || true $STD apt-get purge -y 'mariadb*' rm -f /etc/apt/sources.list.d/mariadb.list /etc/apt/trusted.gpg.d/mariadb.gpg 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 DISTRO_CODENAME="$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release)" 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" fi } function install_mysql() { 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 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 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 msg_ok "Installed MySQL $MYSQL_VERSION" fi } 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" # 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..." apt-get update -qq &>/dev/null 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 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 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 # 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 # 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" 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" }