diff --git a/misc/tools.func b/misc/tools.func index b9394b6f..02ab747c 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -675,22 +675,6 @@ install_mongodb() { msg_ok "MongoDB $MONGO_VERSION installed and started" } -install_from_gh_release() { - local app="$1" - local repo="$2" - local mode="${3:-source}" # Default: source - local version="${4:-}" # Default: latest - local deb_name="${5:-}" # Default: empty - - APP="$app" - INSTALL_DIR="/opt/$app" - VERSION="$version" - SOURCE_PACKAGE="$deb_name" - BUILD_SOURCE=$([[ "$mode" == "binary" ]] && echo 0 || echo 1) - - fetch_and_deploy_gh_release "$repo" -} - # ------------------------------------------------------------------------------ # Downloads and deploys latest GitHub release tarball. # @@ -705,22 +689,24 @@ install_from_gh_release() { # ------------------------------------------------------------------------------ fetch_and_deploy_gh_release() { - local repo="$1" - local raw_app="${APP:-$APPLICATION}" - local app=$(echo "${raw_app,,}" | 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 app="$1" + local repo="$2" + local mode="${3:-tarball}" # tarball | source | binary + local version="${4:-latest}" # optional, default "latest" + local target="${5:-/opt/$app}" # optional target dir + + local app_lc=$(echo "${app,,}" | tr -d ' ') + local version_file="$HOME/.${app_lc}/${app_lc}_version.txt" 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") + + mkdir -p "$(dirname "$version_file")" + + local current_version="" + if [[ -f "$version_file" ]]; then + current_version=$(<"$version_file") $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 @@ -729,136 +715,97 @@ fetch_and_deploy_gh_release() { return 1 } fi + + local api_url="https://api.github.com/repos/$repo/releases" + [[ "$version" != "latest" ]] && api_url="$api_url/tags/$version" || api_url="$api_url/latest" + local header=() [[ -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}" - version="${tag#v}" - 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." + + $STD msg_info "Fetching release metadata for $repo ($version)..." + local resp http_code + resp=$(curl $curl_timeout -fsSL -w "%{http_code}" -o /tmp/gh_rel.json "${header[@]}" "$api_url") + http_code="${resp:(-3)}" + [[ "$http_code" != "200" ]] && { + msg_error "Failed to fetch release: HTTP $http_code" + return 1 + } + local json + json=$(/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 filename="" 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 + + if [[ "$mode" == "tarball" || "$mode" == "source" ]]; then + url=$(echo "$json" | jq -r '.tarball_url // empty') + [[ -z "$url" ]] && url="https://github.com/$repo/archive/refs/tags/v$version.tar.gz" + filename="${app_lc}-${version}.tar.gz" + + $STD msg_info "Downloading source tarball: $url" + curl $curl_timeout -fsSL -o "$tmpdir/$filename" "$url" || { + msg_error "Download failed: $url" + rm -rf "$tmpdir" + return 1 + } + + mkdir -p "$target" + tar -xzf "$tmpdir/$filename" -C "$tmpdir" + local unpack_dir + unpack_dir=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d | head -n1) + + shopt -s dotglob nullglob + cp -r "$unpack_dir"/* "$target/" + shopt -u dotglob nullglob + + elif [[ "$mode" == "binary" ]]; then + local arch + arch=$(dpkg --print-architecture 2>/dev/null || uname -m) + [[ "$arch" == "x86_64" ]] && arch="amd64" + [[ "$arch" == "aarch64" ]] && arch="arm64" + + local assets url_match + assets=$(echo "$json" | jq -r '.assets[].browser_download_url') 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" + if [[ "$u" =~ $arch.*\.deb$ ]]; then + url_match="$u" 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 - # Use tarball_url directly from API response instead of constructing our own URL - url=$(echo "$api_response" | jq -r '.tarball_url // empty') - - # If tarball_url is empty for some reason, fall back to a constructed URL as before - if [[ -z "$url" ]]; then - url="https://github.com/$repo/archive/refs/tags/v$version.tar.gz" + if [[ -z "$url_match" ]]; then + msg_error "No matching .deb asset for arch '$arch'" + rm -rf "$tmpdir" + return 1 fi - - $STD msg_info "Using GitHub source tarball: $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" + filename="${url_match##*/}" + $STD msg_info "Downloading binary .deb: $url_match" + curl $curl_timeout -fsSL -o "$tmpdir/$filename" "$url_match" || { + msg_error "Download failed: $url_match" + rm -rf "$tmpdir" + return 1 + } + $STD msg_info "Installing $filename via apt" + apt-get install -y "$tmpdir/$filename" || { + msg_error "Failed to install $filename" + rm -rf "$tmpdir" + return 1 + } + else + msg_error "Unknown mode: $mode" 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 - shopt -s dotglob nullglob - cp -r "$content_root"/* "/opt/$app/" - shopt -u dotglob nullglob - else - shopt -s dotglob nullglob - cp -r "$tmpdir"/* "/opt/$app/" - shopt -u dotglob nullglob - fi - echo "$version" >"/opt/${app}_version.txt" - $STD msg_ok "Deployed $app v$version to /opt/$app" + + echo "$version" >"$version_file" + $STD msg_ok "$app deployed (version: $version)" rm -rf "$tmpdir" }