From 95803a183a6b6a5bd597872c6c3cbe4461e26afe Mon Sep 17 00:00:00 2001 From: Alex Burger Date: Wed, 12 Aug 2026 20:30:41 -0400 Subject: [PATCH] Mark installed kernel packages as automatically installed. mintupdate installs kernel packages (linux-image-*, linux-headers-*, linux-modules-*) directly by name whenever no meta package tracks the exact version, and always does so from the kernel selection window. Since these are requested by name rather than pulled in as a dependency, apt records them as manually installed, so "apt autoremove" (and mintupdate's own "remove obsolete kernels" automation, which just runs apt-get autoremove) can never reclaim them once superseded, and old kernels accumulate indefinitely. Tag these package names with aptkit's "#auto" suffix convention before handing them to aptkit, so they get flagged the same way a normal kernel upgrade via a meta package would. The new is_real_kernel_package() helper distinguishes version-specific kernel packages from meta packages (which must stay manual) by looking for an embedded VERSION-ABI kernel version rather than enumerating package flavors, so versioned flavors such as linux-modules-iwlwifi-* and linux-hwe-X.Y-headers-* are covered too. A companion fix in aptkit's SimpleAPTClient.install_packages() is needed for the update-list path, where local dependency resolution previously dropped the "#auto" marker; without it this change still works, but dependency packages discovered client-side get recorded as manual. Fixes #938. Co-Authored-By: Claude Fable 5 --- usr/lib/linuxmint/mintUpdate/Classes.py | 17 +++++++++++++++++ usr/lib/linuxmint/mintUpdate/kernelwindow.py | 6 +++++- usr/lib/linuxmint/mintUpdate/mintUpdate.py | 12 ++++++++++-- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/usr/lib/linuxmint/mintUpdate/Classes.py b/usr/lib/linuxmint/mintUpdate/Classes.py index c09029e9..8475b6d1 100644 --- a/usr/lib/linuxmint/mintUpdate/Classes.py +++ b/usr/lib/linuxmint/mintUpdate/Classes.py @@ -7,6 +7,7 @@ import gettext import json import os +import re import subprocess import sys import time @@ -26,6 +27,22 @@ 'linux-modules-VERSION-KERNELTYPE', 'linux-modules-extra-VERSION-KERNELTYPE'] KERNEL_PKG_NAMES.append('linux-image-extra-VERSION-KERNELTYPE') # Naming convention in 16.04, until 4.15 series +# Matches the actual, version-specific kernel packages (linux-image-5.15.0-91-generic, +# linux-headers-5.15.0-91, linux-modules-iwlwifi-6.8.0-41-generic, ...) as opposed to +# meta packages (linux-generic, linux-generic-hwe-24.04, linux-virtual, ...), by +# looking for an embedded VERSION-ABI kernel version rather than enumerating package +# flavors. Meta package names never contain one (an HWE series suffix like +# "-hwe-24.04" has no third version component or ABI number). Apt normally marks +# versioned kernel packages as automatically installed when they're pulled in as a +# dependency of a meta package, which is what lets "apt autoremove" clean up +# superseded kernels. When mintupdate installs them directly by name (because no +# meta package tracks that exact version, or via the kernel selection window), they +# must be explicitly flagged the same way or they stick around forever. +REAL_KERNEL_PKG_RE = re.compile(r'^linux-.*-\d+\.\d+\.\d+-\d+') + +def is_real_kernel_package(name): + return bool(REAL_KERNEL_PKG_RE.match(name)) + CONFIGURED_KERNEL_TYPE = settings.get_string("selected-kernel-type") if CONFIGURED_KERNEL_TYPE not in SUPPORTED_KERNEL_TYPES: CONFIGURED_KERNEL_TYPE = "-generic" diff --git a/usr/lib/linuxmint/mintUpdate/kernelwindow.py b/usr/lib/linuxmint/mintUpdate/kernelwindow.py index 6fa64932..245c6d8e 100755 --- a/usr/lib/linuxmint/mintUpdate/kernelwindow.py +++ b/usr/lib/linuxmint/mintUpdate/kernelwindow.py @@ -452,7 +452,11 @@ def install_kernels(self, kernels): continue to_purge.append(name) else: - to_install.append(name) + # Flag it for aptkit as an automatic install so that + # "apt autoremove" (and mintupdate's own obsolete-kernel + # cleanup) can reclaim it once it's superseded, matching + # how a normal kernel upgrade marks these packages. + to_install.append(name + "#auto") # Clean out left-over meta package if kernel.installed: diff --git a/usr/lib/linuxmint/mintUpdate/mintUpdate.py b/usr/lib/linuxmint/mintUpdate/mintUpdate.py index 5fb993e7..473e4943 100755 --- a/usr/lib/linuxmint/mintUpdate/mintUpdate.py +++ b/usr/lib/linuxmint/mintUpdate/mintUpdate.py @@ -33,7 +33,7 @@ from kernelwindow import KernelWindow from preferences import PreferencesWindow -from Classes import MainloopTimer, PRIORITY_UPDATES, UpdateTracker +from Classes import MainloopTimer, PRIORITY_UPDATES, UpdateTracker, is_real_kernel_package from util import on_battery, _idle, _async, Inhibitor @@ -2197,7 +2197,15 @@ def install(self, widget): [True for pkg in update.package_names if "nvidia" in pkg]: self.reboot_required = True for package in update.package_names: - self.packages.append(package) + if update.type == "kernel" and is_real_kernel_package(package): + # Flag it for aptkit as an automatic install (see aptkit's + # _mark_packages_for_installation()), matching what a normal + # "apt upgrade" would do when a meta package pulls in a new + # kernel version. Otherwise apt records it as manually + # installed and "apt autoremove" never cleans it up. + self.packages.append(package + "#auto") + else: + self.packages.append(package) self.logger.write("Will install " + str(package)) iter = model.iter_next(iter)