From 8833698fb4e6c411e8a57202ba536b82b57965e9 Mon Sep 17 00:00:00 2001 From: Alaric Dailey Date: Fri, 1 Aug 2025 13:15:42 -0500 Subject: [PATCH 1/2] Purge broken deborphan --- README.md | 15 +++++++++- autoupdate-and-reboot.sh | 19 ++++++++----- autoupdate-and-shutdown.sh | 11 ++++--- autoupdate.sh | 5 ++-- check-if-already-updating.sh | 2 +- check-requirements.sh | 38 +++++++++++++++---------- checkserver.sh | 12 ++++++-- reboot-if-required.sh | 2 +- remove-all-old-packages.sh | 10 ++++++- tests/test_autoupdate.sh | 23 +++++++++++++++ tests/test_autoupdate_and_reboot.sh | 35 +++++++++++++++++++++++ tests/test_autoupdate_and_shutdown.sh | 35 +++++++++++++++++++++++ tests/test_check_if_already_updating.sh | 17 +++++++++++ tests/test_check_requirements.sh | 29 +++++++++++++++++++ tests/test_checkserver.sh | 17 +++++++++++ tests/test_reboot_if_required.sh | 17 +++++++++++ tests/test_remove_all_old_packages.sh | 29 +++++++++++++++++++ 17 files changed, 282 insertions(+), 34 deletions(-) create mode 100755 tests/test_autoupdate.sh create mode 100755 tests/test_autoupdate_and_reboot.sh create mode 100755 tests/test_autoupdate_and_shutdown.sh create mode 100755 tests/test_check_if_already_updating.sh create mode 100755 tests/test_check_requirements.sh create mode 100755 tests/test_checkserver.sh create mode 100755 tests/test_reboot_if_required.sh create mode 100755 tests/test_remove_all_old_packages.sh diff --git a/README.md b/README.md index 45b34fa..c439bd8 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,15 @@ # debian-scripts -Scripts to keep debian updated with ALL patches, remove old kernels and more. +Scripts to keep Debian-based systems updated with all patches while removing +stale packages and kernels. + +## Notes on deborphan + +Debian dropped the `deborphan` package because it was unmaintained and largely +superseded by modern APT features. Kali follows Debian and no longer ships this +utility. These scripts therefore remove the package if it is installed and do +not attempt to use it. + +With `deborphan` removed, the recommended approach to clean unused packages is +to rely on `apt autoremove` in combination with `apt-mark minimize-manual`. +`remove-all-old-packages.sh` automates this process and purges `deborphan` +itself if it is still installed. diff --git a/autoupdate-and-reboot.sh b/autoupdate-and-reboot.sh index 2580c91..e7f2835 100755 --- a/autoupdate-and-reboot.sh +++ b/autoupdate-and-reboot.sh @@ -1,11 +1,16 @@ #!/usr/bin/env bash PATH=/bin:/usr/sbin:/sbin:/usr/local/sbin + +# Determine the directory where this script lives so we can invoke +# companion scripts reliably when called from any location. +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + if [ -n "$1" ]; then - touch /var/run/reboot-required + touch /var/run/reboot-required fi -/bin/check-if-already-updating.sh && \ -/bin/remove-old-kernels.sh && \ -/bin/remove-all-old-packages.sh && \ -/bin/remove-old-snaps.sh && \ -/bin/autoupdate.sh -/bin/reboot-if-required.sh \ No newline at end of file + +"${SCRIPT_DIR}/check-if-already-updating.sh" && \ +"${SCRIPT_DIR}/remove-old-kernels.sh" && \ +"${SCRIPT_DIR}/autoupdate.sh" && \ +"${SCRIPT_DIR}/remove-old-snaps.sh" && \ +"${SCRIPT_DIR}/reboot-if-required.sh" diff --git a/autoupdate-and-shutdown.sh b/autoupdate-and-shutdown.sh index a5f72b8..1f9ba2d 100755 --- a/autoupdate-and-shutdown.sh +++ b/autoupdate-and-shutdown.sh @@ -1,7 +1,10 @@ #!/usr/bin/env bash PATH=/bin:/usr/sbin:/sbin:/usr/local/sbin -/bin/remove-old-kernels.sh -/bin/remove-all-old-packages.sh -/bin/remove-old-snaps.sh -/bin/autoupdate.sh + +# Use SCRIPT_DIR for consistent invocation of companion scripts +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + +"${SCRIPT_DIR}/remove-old-kernels.sh" +"${SCRIPT_DIR}/autoupdate.sh" +"${SCRIPT_DIR}/remove-old-snaps.sh" shutdown -h now diff --git a/autoupdate.sh b/autoupdate.sh index 851605a..5e41de2 100755 --- a/autoupdate.sh +++ b/autoupdate.sh @@ -5,5 +5,6 @@ export DEBIAN_FRONTEND=noninteractive dpkg --configure -a --force-confdef --force-confold && \ apt-get update && \ apt-get dist-upgrade -y && \ -apt-get purge $(deborphan --guess-all | grep -v "$(apt-mark showmanual)" | tr '\n' ' ') -y && \ -/bin/check-requirements.sh \ No newline at end of file +"$(dirname "$0")/remove-all-old-packages.sh" && \ +"/bin/check-requirements.sh" + diff --git a/check-if-already-updating.sh b/check-if-already-updating.sh index 8a5e879..8423cf4 100755 --- a/check-if-already-updating.sh +++ b/check-if-already-updating.sh @@ -3,4 +3,4 @@ if pidof apt-get > /dev/null || pidof dpkg > /dev/null; then echo "An update process is already running. Exiting..." exit 1 -fi \ No newline at end of file +fi diff --git a/check-requirements.sh b/check-requirements.sh index a332841..b1ccbc0 100755 --- a/check-requirements.sh +++ b/check-requirements.sh @@ -1,26 +1,28 @@ #!/usr/bin/env bash export DEBIAN_FRONTEND=noninteractive -# List of packages to check and install if necessary +# List of packages to check and install if necessary. `deborphan` has been +# removed from modern Debian-based systems and will be purged if present. # Detect the distribution if [ -f /etc/os-release ]; then . /etc/os-release fi -# Define package lists specific to each distribution -if [ "$ID" == "kali" ]; then - # Kali-specific package list (netcat is already included by default) - packages=("nc" "sed" "deborphan" "needrestart") -elif [ "$ID" == "ubuntu" ]; then - # Ubuntu-specific package list - packages=("netcat-openbsd" "sed" "deborphan" "needrestart") -elif [ "$ID" == "debian" ]; then - # Debian-specific package list - packages=("netcat-openbsd" "sed" "deborphan" "needrestart") -else - echo "Unsupported distribution: $ID" - exit 1 -fi +# Define package lists specific to each distribution using a case statement +case "$ID" in + kali) + # Kali-specific package list (netcat is already included by default) + packages=("nc" "sed" "needrestart") + ;; + ubuntu|debian) + # Ubuntu and Debian package list + packages=("netcat-openbsd" "sed" "needrestart") + ;; + *) + echo "Unsupported distribution: $ID" + exit 1 + ;; +esac # Loop through the list of packages and install them if they are not already installed for pkg in "${packages[@]}"; do @@ -42,3 +44,9 @@ for pkg in "${packages[@]}"; do done echo "All packages checked and necessary ones installed." + +# Purge deborphan if it still exists +if dpkg -s deborphan >/dev/null 2>&1; then + apt-get purge -y deborphan +fi + diff --git a/checkserver.sh b/checkserver.sh index 4d5dc6a..33fb509 100755 --- a/checkserver.sh +++ b/checkserver.sh @@ -1,5 +1,13 @@ #!/usr/bin/env bash -nc -z $1 $2 + +if [ "$#" -lt 2 ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +nc -z "$1" "$2" if [ $? -eq 1 ]; then - /bin/autoupdate-and-reboot.sh + /bin/autoupdate-and-reboot.sh fi + + diff --git a/reboot-if-required.sh b/reboot-if-required.sh index 6648e8b..51df66b 100755 --- a/reboot-if-required.sh +++ b/reboot-if-required.sh @@ -10,4 +10,4 @@ else # Restart affected services echo "Restarting affected services..." /usr/sbin/needrestart -r a -fi \ No newline at end of file +fi diff --git a/remove-all-old-packages.sh b/remove-all-old-packages.sh index d41634e..ebd06db 100755 --- a/remove-all-old-packages.sh +++ b/remove-all-old-packages.sh @@ -3,7 +3,15 @@ PATH=/bin:/usr/sbin:/sbin:/usr/local/sbin -# Loop autoremove until no more packages are removed +# Mark packages that are no longer explicitly required as automatic +apt-mark minimize-manual + +# Remove deborphan if it is present +if dpkg -s deborphan >/dev/null 2>&1; then + apt-get purge -y deborphan +fi + +# Loop autoremove until no packages remain while ! sudo apt-get autoremove -y | grep -q '0 upgraded, 0 newly installed, 0 to remove'; do echo "Running autoremove again to ensure all unnecessary packages are removed." done diff --git a/tests/test_autoupdate.sh b/tests/test_autoupdate.sh new file mode 100755 index 0000000..dbc7786 --- /dev/null +++ b/tests/test_autoupdate.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +set -e +script="$(dirname "$0")/../autoupdate.sh" + +# positive: script calls remove-all-old-packages.sh +if ! grep -q "remove-all-old-packages.sh" "$script"; then + echo "Expected call to remove-all-old-packages.sh" >&2 + exit 1 +fi + +# positive: script performs dist-upgrade +if ! grep -q "dist-upgrade" "$script"; then + echo "Expected dist-upgrade command" >&2 + exit 1 +fi + +# negative: script should not reference deborphan +if grep -q deborphan "$script"; then + echo "Script must not reference deborphan" >&2 + exit 1 +fi + +echo "All tests passed." diff --git a/tests/test_autoupdate_and_reboot.sh b/tests/test_autoupdate_and_reboot.sh new file mode 100755 index 0000000..529c79d --- /dev/null +++ b/tests/test_autoupdate_and_reboot.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +set -e +script="$(dirname "$0")/../autoupdate-and-reboot.sh" + +# positive: script defines SCRIPT_DIR variable +if ! grep -q 'SCRIPT_DIR=' "$script"; then + echo "Expected SCRIPT_DIR variable" >&2 + exit 1 +fi + +# positive: uses SCRIPT_DIR when calling check-if-already-updating.sh +if ! grep -q "\${SCRIPT_DIR}/check-if-already-updating.sh" "$script"; then + echo "Expected SCRIPT_DIR usage for check-if-already-updating.sh" >&2 + exit 1 +fi + +# positive: script calls autoupdate.sh via SCRIPT_DIR +if ! grep -q "\${SCRIPT_DIR}/autoupdate.sh" "$script"; then + echo "Expected call to autoupdate.sh" >&2 + exit 1 +fi + +# negative: script should not use hard-coded /bin path +if grep -q '/bin/check-if-already-updating.sh' "$script"; then + echo "Script should not use absolute /bin path" >&2 + exit 1 +fi + +# negative: script should not call remove-all-old-packages.sh +if grep -q 'remove-all-old-packages.sh' "$script"; then + echo "Script should not call remove-all-old-packages.sh" >&2 + exit 1 +fi + +echo "All tests passed." diff --git a/tests/test_autoupdate_and_shutdown.sh b/tests/test_autoupdate_and_shutdown.sh new file mode 100755 index 0000000..68aa277 --- /dev/null +++ b/tests/test_autoupdate_and_shutdown.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +set -e +script="$(dirname "$0")/../autoupdate-and-shutdown.sh" + +# positive: script defines SCRIPT_DIR variable +if ! grep -q 'SCRIPT_DIR=' "$script"; then + echo "Expected SCRIPT_DIR variable" >&2 + exit 1 +fi + +# positive: uses SCRIPT_DIR when calling remove-old-snaps.sh +if ! grep -q "\${SCRIPT_DIR}/remove-old-snaps.sh" "$script"; then + echo "Expected SCRIPT_DIR usage for remove-old-snaps.sh" >&2 + exit 1 +fi + +# positive: script calls autoupdate.sh via SCRIPT_DIR +if ! grep -q "\${SCRIPT_DIR}/autoupdate.sh" "$script"; then + echo "Expected call to autoupdate.sh" >&2 + exit 1 +fi + +# negative: script should not use hard-coded /bin path +if grep -q '/bin/remove-old-snaps.sh' "$script"; then + echo "Script should not use absolute /bin path" >&2 + exit 1 +fi + +# negative: script should not call remove-all-old-packages.sh +if grep -q 'remove-all-old-packages.sh' "$script"; then + echo "Script should not call remove-all-old-packages.sh" >&2 + exit 1 +fi + +echo "All tests passed." diff --git a/tests/test_check_if_already_updating.sh b/tests/test_check_if_already_updating.sh new file mode 100755 index 0000000..ebf20ff --- /dev/null +++ b/tests/test_check_if_already_updating.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -e +script="$(dirname "$0")/../check-if-already-updating.sh" + +# positive: script exits with code 1 when apt-get is running +if ! grep -q "exit 1" "$script"; then + echo "Expected script to exit with status 1 when update is running" >&2 + exit 1 +fi + +# negative: ensure script closes with fi +if ! tail -n 1 "$script" | grep -q '^fi$'; then + echo "Script should end with fi" >&2 + exit 1 +fi + +echo "All tests passed." diff --git a/tests/test_check_requirements.sh b/tests/test_check_requirements.sh new file mode 100755 index 0000000..b470fec --- /dev/null +++ b/tests/test_check_requirements.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +set -e +script="$(dirname "$0")/../check-requirements.sh" + +# positive: uses a case statement for distro detection +if ! grep -q "case \"\$ID\"" "$script"; then + echo "Expected case statement for distro detection" >&2 + exit 1 +fi + +# positive: script purges deborphan if present +if ! grep -q "purge -y deborphan" "$script"; then + echo "Expected deborphan purge step" >&2 + exit 1 +fi + +# negative: script should not attempt to install deborphan +if grep -q "apt-cache show deborphan" "$script"; then + echo "Script should not install deborphan" >&2 + exit 1 +fi + +# negative: script must handle unsupported distributions +if ! grep -q "Unsupported distribution" "$script"; then + echo "Expected unsupported distribution handler" >&2 + exit 1 +fi + +echo "All tests passed." diff --git a/tests/test_checkserver.sh b/tests/test_checkserver.sh new file mode 100755 index 0000000..4d0356b --- /dev/null +++ b/tests/test_checkserver.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -e +script="$(dirname "$0")/../checkserver.sh" + +# positive: script validates argument count +if ! grep -q "Usage" "$script"; then + echo "Expected usage message" >&2 + exit 1 +fi + +# negative: script should not use unquoted positional parameters +if grep -q "nc -z \$1 \$2" "$script"; then + echo "Script should quote positional parameters" >&2 + exit 1 +fi + +echo "All tests passed." diff --git a/tests/test_reboot_if_required.sh b/tests/test_reboot_if_required.sh new file mode 100755 index 0000000..eaebfc7 --- /dev/null +++ b/tests/test_reboot_if_required.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -e +script="$(dirname "$0")/../reboot-if-required.sh" + +# positive: script checks for reboot-required file +if ! grep -q '/var/run/reboot-required' "$script"; then + echo "Expected check for /var/run/reboot-required" >&2 + exit 1 +fi + +# negative: ensure script ends with fi +if ! tail -n 1 "$script" | grep -q '^fi$'; then + echo "Script should end with fi" >&2 + exit 1 +fi + +echo "All tests passed." diff --git a/tests/test_remove_all_old_packages.sh b/tests/test_remove_all_old_packages.sh new file mode 100755 index 0000000..2abd4bd --- /dev/null +++ b/tests/test_remove_all_old_packages.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +set -e +script="$(dirname "$0")/../remove-all-old-packages.sh" + +# positive: script runs apt-mark minimize-manual +if ! grep -q "apt-mark minimize-manual" "$script"; then + echo "Expected apt-mark minimize-manual step" >&2 + exit 1 +fi + +# positive: script loops over apt-get autoremove +if ! grep -q "apt-get autoremove" "$script"; then + echo "Expected apt-get autoremove command" >&2 + exit 1 +fi + +# positive: script purges deborphan if installed +if ! grep -q "purge -y deborphan" "$script"; then + echo "Expected deborphan purge command" >&2 + exit 1 +fi + +# negative: script should not call apt-get dist-upgrade +if grep -q "dist-upgrade" "$script"; then + echo "Script should not run dist-upgrade" >&2 + exit 1 +fi + +echo "All tests passed." From 959b2948405c8dc1a584411aec785f231ceb8935 Mon Sep 17 00:00:00 2001 From: Alaric Dailey Date: Fri, 1 Aug 2025 13:25:50 -0500 Subject: [PATCH 2/2] Refine package cleanup and server check --- README.md | 4 ++-- checkserver.sh | 5 ++++- remove-all-old-packages.sh | 7 +++++-- tests/test_checkserver.sh | 16 ++++++++++++++++ tests/test_remove_all_old_packages.sh | 14 +++++++++++++- 5 files changed, 40 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c439bd8..780539c 100644 --- a/README.md +++ b/README.md @@ -11,5 +11,5 @@ not attempt to use it. With `deborphan` removed, the recommended approach to clean unused packages is to rely on `apt autoremove` in combination with `apt-mark minimize-manual`. -`remove-all-old-packages.sh` automates this process and purges `deborphan` -itself if it is still installed. +`remove-all-old-packages.sh` automates this process, looping `apt autoremove` +up to ten times and purging `deborphan` itself if it is still installed. diff --git a/checkserver.sh b/checkserver.sh index 33fb509..9e3faa9 100755 --- a/checkserver.sh +++ b/checkserver.sh @@ -5,9 +5,12 @@ if [ "$#" -lt 2 ]; then exit 1 fi +# Determine the directory of this script to call helpers reliably +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + nc -z "$1" "$2" if [ $? -eq 1 ]; then - /bin/autoupdate-and-reboot.sh + "${SCRIPT_DIR}/autoupdate-and-reboot.sh" fi diff --git a/remove-all-old-packages.sh b/remove-all-old-packages.sh index ebd06db..b16da58 100755 --- a/remove-all-old-packages.sh +++ b/remove-all-old-packages.sh @@ -11,8 +11,11 @@ if dpkg -s deborphan >/dev/null 2>&1; then apt-get purge -y deborphan fi -# Loop autoremove until no packages remain -while ! sudo apt-get autoremove -y | grep -q '0 upgraded, 0 newly installed, 0 to remove'; do +# Run autoremove repeatedly with an upper attempt limit to avoid infinite loops +for attempt in $(seq 1 10); do + if sudo apt-get autoremove -y | grep -q '0 upgraded, 0 newly installed, 0 to remove'; then + break + fi echo "Running autoremove again to ensure all unnecessary packages are removed." done diff --git a/tests/test_checkserver.sh b/tests/test_checkserver.sh index 4d0356b..82f715c 100755 --- a/tests/test_checkserver.sh +++ b/tests/test_checkserver.sh @@ -8,10 +8,26 @@ if ! grep -q "Usage" "$script"; then exit 1 fi +# positive: script defines SCRIPT_DIR and uses it when calling autoupdate +if ! grep -q 'SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"' "$script"; then + echo "Expected SCRIPT_DIR variable" >&2 + exit 1 +fi +if ! grep -q '\${SCRIPT_DIR}/autoupdate-and-reboot.sh' "$script"; then + echo "Expected SCRIPT_DIR usage for autoupdate-and-reboot.sh" >&2 + exit 1 +fi + # negative: script should not use unquoted positional parameters if grep -q "nc -z \$1 \$2" "$script"; then echo "Script should quote positional parameters" >&2 exit 1 fi +# negative: script should not use hard-coded /bin path +if grep -q '/bin/autoupdate-and-reboot.sh' "$script"; then + echo "Script should not use absolute /bin path" >&2 + exit 1 +fi + echo "All tests passed." diff --git a/tests/test_remove_all_old_packages.sh b/tests/test_remove_all_old_packages.sh index 2abd4bd..1af4926 100755 --- a/tests/test_remove_all_old_packages.sh +++ b/tests/test_remove_all_old_packages.sh @@ -8,12 +8,24 @@ if ! grep -q "apt-mark minimize-manual" "$script"; then exit 1 fi -# positive: script loops over apt-get autoremove +# positive: script loops up to 10 times using a for loop with seq +if ! grep -q "for attempt in \$(seq 1 10)" "$script"; then + echo "Expected for loop with seq limit" >&2 + exit 1 +fi + +# positive: script invokes apt-get autoremove if ! grep -q "apt-get autoremove" "$script"; then echo "Expected apt-get autoremove command" >&2 exit 1 fi +# negative: script should not use a while loop for autoremove +if grep -q "while .*apt-get autoremove" "$script"; then + echo "Should not use while loop for autoremove" >&2 + exit 1 +fi + # positive: script purges deborphan if installed if ! grep -q "purge -y deborphan" "$script"; then echo "Expected deborphan purge command" >&2