From c44a9639d75911fd82d5a8df5c24dcc4918eaa1c Mon Sep 17 00:00:00 2001 From: Alex Burger Date: Wed, 12 Aug 2026 20:32:00 -0400 Subject: [PATCH] simpleclient: Preserve the "#auto" install marker through local dependency resolution. SimpleAPTClient.install_packages() locally resolves dependencies with python-apt to discover which extra packages a request will pull in (e.g. a kernel image pulled in by a meta package), then flattens those names into the same list sent to the worker. Since none of those names carried the "#auto" suffix that aptworker._mark_packages_for_installation() looks for (and that _check_package_names() already validates at the D-Bus boundary), every package in the flattened list ended up marked as manually installed regardless of whether it was actually requested by the caller or merely a resolved dependency - defeating the purpose of the "#auto" convention and leaving "apt autoremove" unable to reclaim packages like superseded kernels (linuxmint/mintupdate#938). Now, package names already tagged "#auto" by the caller are honored (the tag is stripped only for the local apt.Cache() lookups, which would otherwise fail), and newly-discovered dependency packages that weren't explicitly requested are tagged "#auto" themselves before being handed to the worker. Note this affects every SimpleAPTClient.install_packages() consumer: dependency packages are now recorded as automatically installed, matching standard apt semantics, where previously the flattening step left them permanently manual. Callers that want a dependency pinned as manual can still request it explicitly by name. Co-Authored-By: Claude Fable 5 --- aptkit/simpleclient.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/aptkit/simpleclient.py b/aptkit/simpleclient.py index 26a7990..4056a56 100644 --- a/aptkit/simpleclient.py +++ b/aptkit/simpleclient.py @@ -44,8 +44,13 @@ def install_packages(self, packages, use_apt_resolver=True): # Use the resolver from python-apt # it works better in complex scenarios # mark the packages as mark_install + # Package names may carry an explicit "#auto" suffix (see + # aptworker._mark_packages_for_installation()) telling the worker to + # record them as automatically installed rather than manual. Strip it + # for the cache lookups below, since apt itself doesn't know about it. + requested_names = [name.partition("#")[0] for name in packages] cache = apt.Cache() - for name in packages: + for name in requested_names: try: pkg = cache[name] pkg.mark_install() @@ -55,8 +60,12 @@ def install_packages(self, packages, use_apt_resolver=True): # via cache.get_changes() changes = cache.get_changes() for pkg in changes: - if pkg.marked_install and pkg.name not in packages: - packages.append(pkg.name) + if pkg.marked_install and pkg.name not in requested_names: + # Not explicitly requested, just pulled in as a dependency + # (e.g. a kernel image pulled in by a meta package): flag it + # as automatic so it stays eligible for autoremoval, same as + # a normal "apt install"/"apt upgrade" would leave it. + packages.append(pkg.name + "#auto") client = aptkit.client.AptClient() client.install_packages(packages, reply_handler=self._simulate_trans, error_handler=self._on_error)