From 416deb8fd0ddcaad8566132b86074e654d0f5637 Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 26 Aug 2026 21:24:33 +0200 Subject: [PATCH] tools.func: fix mongodb version comparison, guard apt purge against removing dependents (#16795) --- misc/tools.func | 74 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 67 insertions(+), 7 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index bfe6cba96..bf1228e4f 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -632,7 +632,7 @@ is_tool_installed() { ;; mongodb | mongod) if command -v mongod >/dev/null 2>&1; then - installed_version=$(mongod --version 2>/dev/null | awk '/db version/{print $3}' | cut -d. -f1,2) + installed_version=$(mongod --version 2>/dev/null | awk '/db version/{print $3}' | sed 's/^v//' | cut -d. -f1,2) fi ;; node | nodejs) @@ -685,6 +685,39 @@ is_tool_installed() { return 0 # Installed and version matches (if specified) } +# ------------------------------------------------------------------------------ +# Checks whether purging the given package glob(s) would also remove packages +# outside those globs (i.e. reverse dependents, such as an app depending on +# mongodb-org-server). Uses `apt-get -s` (simulate) with the stable apt-get +# output format, not `apt`, whose output is explicitly unstable for scripting. +# Returns 0 (safe to purge) or 1 (unsafe - prints the unexpected packages). +# Usage: _purge_is_safe 'mongodb*' ['other-glob*' ...] +# ------------------------------------------------------------------------------ +_purge_is_safe() { + local -a globs=("$@") + local -a collateral=() + local pkg glob matched + local sim_out + sim_out=$(apt-get -s purge -y "${globs[@]}" 2>/dev/null) || return 0 + while IFS= read -r pkg; do + [[ -z "$pkg" ]] && continue + matched=0 + for glob in "${globs[@]}"; do + # shellcheck disable=SC2053 + [[ "$pkg" == $glob ]] && { + matched=1 + break + } + done + ((matched)) || collateral+=("$pkg") + done < <(awk '/^(Remv|Purg) /{print $2}' <<<"$sim_out") + if ((${#collateral[@]} > 0)); then + msg_warn "Refusing purge of '${globs[*]}': would also remove ${collateral[*]}" + return 1 + fi + return 0 +} + # ------------------------------------------------------------------------------ # Remove old tool version completely (purge + cleanup repos) # Usage: remove_old_tool_version "mariadb" "repository-name" @@ -696,11 +729,13 @@ remove_old_tool_version() { case "$tool_name" in mariadb) stop_all_services "mariadb" + _purge_is_safe 'mariadb*' || return 1 $STD apt purge -y 'mariadb*' >/dev/null 2>&1 || true cleanup_tool_keyrings "mariadb" ;; mysql) stop_all_services "mysql" + _purge_is_safe 'mysql*' || return 1 $STD apt purge -y 'mysql*' >/dev/null 2>&1 || true # Keep data directory for safety (remove manually if needed) # rm -rf /var/lib/mysql 2>/dev/null || true @@ -708,12 +743,14 @@ remove_old_tool_version() { ;; mongodb) stop_all_services "mongod" + _purge_is_safe 'mongodb*' || return 1 $STD apt purge -y 'mongodb*' >/dev/null 2>&1 || true # Keep data directory for safety (remove manually if needed) # rm -rf /var/lib/mongodb 2>/dev/null || true cleanup_tool_keyrings "mongodb" ;; node | nodejs) + _purge_is_safe nodejs npm || return 1 $STD apt purge -y nodejs npm >/dev/null 2>&1 || true # Clean up npm global modules if command -v npm >/dev/null 2>&1; then @@ -726,23 +763,27 @@ remove_old_tool_version() { ;; php) stop_all_services "php.*-fpm" + _purge_is_safe 'php*' || return 1 $STD apt purge -y 'php*' >/dev/null 2>&1 || true rm -rf /etc/php 2>/dev/null || true cleanup_tool_keyrings "deb.sury.org-php" "php" ;; postgresql) stop_all_services "postgresql" + _purge_is_safe 'postgresql*' || return 1 $STD apt purge -y 'postgresql*' >/dev/null 2>&1 || true # Keep data directory for safety (can be removed manually if needed) # rm -rf /var/lib/postgresql 2>/dev/null || true cleanup_tool_keyrings "postgresql" "pgdg" ;; java) + _purge_is_safe 'temurin*' 'adoptium*' 'openjdk*' || return 1 $STD apt purge -y 'temurin*' 'adoptium*' 'openjdk*' >/dev/null 2>&1 || true cleanup_tool_keyrings "adoptium" ;; ruby) cleanup_legacy_install "ruby" + _purge_is_safe 'ruby*' || return 1 $STD apt purge -y 'ruby*' >/dev/null 2>&1 || true ;; rust) @@ -754,6 +795,7 @@ remove_old_tool_version() { ;; clickhouse) stop_all_services "clickhouse-server" + _purge_is_safe 'clickhouse*' || return 1 $STD apt purge -y 'clickhouse*' >/dev/null 2>&1 || true # Keep data directory for safety (remove manually if needed) # rm -rf /var/lib/clickhouse 2>/dev/null || true @@ -4473,7 +4515,10 @@ setup_clickhouse() { if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$CLICKHOUSE_VERSION" ]]; then msg_info "Upgrade ClickHouse from $CURRENT_VERSION to $CLICKHOUSE_VERSION" stop_all_services "clickhouse-server" - remove_old_tool_version "clickhouse" + remove_old_tool_version "clickhouse" || { + msg_error "Aborting ClickHouse upgrade: another package depends on it" + return 1 + } else msg_info "Setup ClickHouse $CLICKHOUSE_VERSION" fi @@ -5150,7 +5195,10 @@ setup_go() { # Scenario 2: Different version or not installed if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$GO_VERSION" ]]; then msg_info "Upgrade Go from $CURRENT_VERSION to $GO_VERSION" - remove_old_tool_version "go" + remove_old_tool_version "go" || { + msg_error "Aborting Go upgrade: another package depends on it" + return 1 + } else msg_info "Setup Go $GO_VERSION" fi @@ -6753,7 +6801,10 @@ EOF # Scenario 2b: Different version installed - clean upgrade if [[ -n "$CURRENT_VERSION" ]] && ! version_matches_spec "$CURRENT_VERSION" "$MARIADB_VERSION"; then msg_info "Upgrade MariaDB from $CURRENT_VERSION to $MARIADB_VERSION" - remove_old_tool_version "mariadb" + remove_old_tool_version "mariadb" || { + msg_error "Aborting MariaDB upgrade: another package depends on it" + return 1 + } fi # Scenario 3: Fresh install or version change with specific version @@ -7283,7 +7334,10 @@ setup_mongodb() { # Scenario 2: Different version installed - clean upgrade if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" != "$MONGO_VERSION" ]]; then msg_info "Upgrade MongoDB from $INSTALLED_VERSION to $MONGO_VERSION" - remove_old_tool_version "mongodb" + remove_old_tool_version "mongodb" || { + msg_error "Aborting MongoDB upgrade: another package depends on it (e.g. an app using it)" + return 1 + } else msg_info "Setup MongoDB $MONGO_VERSION" fi @@ -7473,7 +7527,10 @@ setup_mysql() { # Scenario 2: Different version installed - clean upgrade if [[ -n "$CURRENT_VERSION" ]] && ! version_matches_spec "$CURRENT_VERSION" "$MYSQL_VERSION"; then msg_info "Upgrade MySQL from $CURRENT_VERSION to $MYSQL_VERSION" - remove_old_tool_version "mysql" + remove_old_tool_version "mysql" || { + msg_error "Aborting MySQL upgrade: another package depends on it" + return 1 + } else msg_info "Setup MySQL $MYSQL_VERSION" fi @@ -7637,7 +7694,10 @@ setup_nodejs() { if [[ -n "$CURRENT_NODE_VERSION" && "$CURRENT_NODE_VERSION" != "$NODE_VERSION" ]]; then msg_info "Upgrade Node.js from $CURRENT_NODE_VERSION to $NODE_VERSION" node_setup_ok_msg="Upgrade Node.js to $NODE_VERSION" - remove_old_tool_version "nodejs" + remove_old_tool_version "nodejs" || { + msg_error "Aborting Node.js upgrade: another package depends on it" + return 1 + } else msg_info "Setup Node.js $NODE_VERSION" node_setup_ok_msg="Setup Node.js $NODE_VERSION"