From 317fdac938f9d3f3ea92524af82b308e8c32ca67 Mon Sep 17 00:00:00 2001 From: John Coffey Date: Sat, 22 Aug 2026 22:42:46 -0700 Subject: [PATCH] Make the prune removal threshold actually reachable The removal-count guard was skipped whenever --yes was set: if [[ "${ASSUME_YES}" != "yes" && "${remv_count}" -gt ... ]] and prune refuses to run *without* --yes. So inside prune -- the only command that removes anything -- the first condition was always false and the threshold could never fire. It fired only in plan, which changes nothing. The guard was live exactly where it did not matter and dead where it did. The check is now unconditional, with --max-removals N as the explicit override, so raising the limit is a separate decision from not wanting to be prompted. The critical-package check was already unconditional and is unchanged. Verified with --yes set and the default limit of 75: an ordinary purge passes, 90 removals aborts, and a critical package aborts. Also applies the same startup fix as the converter -- mkdir and the tee redirection ran at file scope, before argument parsing, so --help and a mistyped flag failed with a raw mkdir error rather than printing usage. --- ubuntu-desktop-prune.sh | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/ubuntu-desktop-prune.sh b/ubuntu-desktop-prune.sh index 9ace7db..3321f62 100644 --- a/ubuntu-desktop-prune.sh +++ b/ubuntu-desktop-prune.sh @@ -26,9 +26,12 @@ SCRIPT_VERSION="1.1" # Globals / Defaults # ========================= LOG_DIR="/var/log/ubuntu-to-mint" -mkdir -p "$LOG_DIR" +# Not created here: this runs before any argument is parsed, so creating +# it made `--help` and a mistyped flag fail with a raw mkdir error +# instead of printing usage. ensure_log_dir does it once a command is +# dispatched. LOG_FILE="${LOG_DIR}/ubuntu-desktop-prune-$(date +%Y%m%d-%H%M%S).log" -exec > >(tee -a "$LOG_FILE") 2>&1 +# Logging starts in ensure_log_dir, for the same reason. CMD="" ASSUME_YES="no" @@ -38,6 +41,7 @@ ROLLBACK_DIR="" # For safety gates MAX_REMOVALS_DEFAULT=75 +MAX_REMOVALS="$MAX_REMOVALS_DEFAULT" # ========================= # Pretty output @@ -94,6 +98,9 @@ Commands: Options: --yes Non-interactive / proceed (required for prune) + --max-removals N Abort if the simulation removes more than N packages + (default: ${MAX_REMOVALS_DEFAULT}). + Critical packages abort regardless of this number. --with-recommends Allow recommends (default: off) --skip-dm-fix Do not attempt to set LightDM as default before pruning EOF @@ -362,8 +369,13 @@ apt_simulate_purge() { remv_count="$(grep -cE '^(Remv|Purg)[[:space:]]' "$sim" || true)" info "Simulation removal count: ${remv_count}" - if [[ "${ASSUME_YES}" != "yes" && "${remv_count}" -gt "${MAX_REMOVALS_DEFAULT}" ]]; then - die "Simulation wants to remove ${remv_count} packages (too many for 'gentle'). Re-run with --yes only if you reviewed $sim." + # Unconditional. This was previously skipped when --yes was set, which + # made it dead code in the only command that removes anything: prune + # refuses to run *without* --yes, so the threshold could never fire + # there. It fired only in plan, where nothing is at stake. Raising the + # limit is now an explicit, separate decision from not being prompted. + if [[ "${remv_count}" -gt "${MAX_REMOVALS}" ]]; then + die "Simulation wants to remove ${remv_count} packages (limit ${MAX_REMOVALS}). Review $sim, then re-run with --max-removals N if that is genuinely expected." fi ok "Simulation looks acceptable. Review: $sim" @@ -482,6 +494,9 @@ parse_args_any_order() { fi fi ;; + --max-removals) + [[ "${2:-}" =~ ^[0-9]+$ ]] || die "--max-removals requires a number, got '${2:-}'" + MAX_REMOVALS="${2}"; shift 2;; --yes) ASSUME_YES="yes"; shift 1;; --with-recommends) WITH_RECOMMENDS="yes"; shift 1;; --skip-dm-fix) SKIP_DM_FIX="yes"; shift 1;; @@ -491,10 +506,18 @@ parse_args_any_order() { done } +ensure_log_dir() { + mkdir -p "$LOG_DIR" 2>/dev/null || \ + die "Cannot create ${LOG_DIR}. Every command here needs root -- re-run with sudo." + exec > >(tee -a "$LOG_FILE") 2>&1 +} + main() { parse_args_any_order "$@" [[ -n "$CMD" ]] || { usage; exit 1; } + ensure_log_dir + case "$CMD" in doctor) doctor ;; plan) plan ;;