Merge lp:~rbalint/update-manager/remove-autoremovable-kernels into lp:update-manager

Proposed by Balint Reczey
Status: Merged
Merged at revision: 2810
Proposed branch: lp:~rbalint/update-manager/remove-autoremovable-kernels
Merge into: lp:update-manager
Diff against target: 277 lines (+76/-23)
4 files modified
UpdateManager/Core/MyCache.py (+16/-0)
UpdateManager/Core/UpdateList.py (+38/-20)
UpdateManager/UpdatesAvailable.py (+15/-3)
debian/changelog (+7/-0)
To merge this branch: bzr merge lp:~rbalint/update-manager/remove-autoremovable-kernels
Reviewer Review Type Date Requested Status
Julian Andres Klode Approve
Brian Murray Pending
Ɓukasz Zemczak Pending
Review via email: mp+341599@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Julian Andres Klode (juliank) wrote :

joining the regular expressions with | might fail in case they themselves contain | (or one of them is invalid).

review: Needs Fixing
Revision history for this message
Balint Reczey (rbalint) wrote :

> joining the regular expressions with | might fail in case they themselves
> contain | (or one of them is invalid).

Any of them being invalid would be either an apt bug or an error in system configuration which we can't protect against here.
Having | without parentheses in the APT::VersionedKernelPackages regexps also sounds broken in itself that would make it an apt bug again.
Do you think this code would break on non-buggy APT::VersionedKernelPackages regexps?
I can add parentheses like (^...)|(^...) but that does not seem to be needed.

I'd like to avoid multiple matching to keep matching fast.

Revision history for this message
Balint Reczey (rbalint) wrote :

Moreover apt regexps like "foo|.*-bar" would break detecting the current running kernel's packages, too.

Revision history for this message
Julian Andres Klode (juliank) wrote :

Yes, you're right, | without () would not work correctly anyway.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'UpdateManager/Core/MyCache.py'
2--- UpdateManager/Core/MyCache.py 2018-03-15 10:22:01 +0000
3+++ UpdateManager/Core/MyCache.py 2018-03-18 22:28:45 +0000
4@@ -40,6 +40,7 @@
5 except ImportError:
6 from httplib import BadStatusLine
7 import socket
8+import subprocess
9 import re
10 import DistUpgrade.DistUpgradeCache
11 from gettext import gettext as _
12@@ -84,6 +85,21 @@
13 assert (self._depcache.broken_count == 0 and
14 self._depcache.del_count == 0)
15 self.launchpad = None
16+ # generate versioned_kernel_pkgs_regexp for later use
17+ apt_versioned_kernel_pkgs = apt_pkg.config.value_list(
18+ "APT::VersionedKernelPackages")
19+ if apt_versioned_kernel_pkgs:
20+ self.versioned_kernel_pkgs_regexp = re.compile("(" + "|".join(
21+ ["^" + p for p in apt_versioned_kernel_pkgs]) + ")")
22+ running_kernel_version = subprocess.check_output(
23+ ["uname", "-r"], universal_newlines=True).rstrip()
24+ self.running_kernel_pkgs_regexp = re.compile("(" + "|".join(
25+ [("^" + p + ".*" + running_kernel_version)
26+ if not p.startswith(".*") else (running_kernel_version + p)
27+ for p in apt_versioned_kernel_pkgs]) + ")")
28+ else:
29+ self.versioned_kernel_pkgs_regexp = None
30+ self.running_kernel_pkgs_regexp = None
31
32 def _dpkgJournalDirty(self):
33 """
34
35=== modified file 'UpdateManager/Core/UpdateList.py'
36--- UpdateManager/Core/UpdateList.py 2017-08-07 23:19:51 +0000
37+++ UpdateManager/Core/UpdateList.py 2018-03-18 22:28:45 +0000
38@@ -43,25 +43,29 @@
39
40
41 class UpdateItem():
42- def __init__(self, pkg, name, icon):
43+ def __init__(self, pkg, name, icon, to_remove):
44 self.icon = icon
45 self.name = name
46 self.pkg = pkg
47+ self.to_remove = to_remove
48
49 def is_selected(self):
50- return self.pkg.marked_install or self.pkg.marked_upgrade
51+ if not self.to_remove:
52+ return self.pkg.marked_install or self.pkg.marked_upgrade
53+ else:
54+ return self.pkg.marked_delete
55
56
57 class UpdateGroup(UpdateItem):
58 _depcache = {}
59
60- def __init__(self, pkg, name, icon):
61- UpdateItem.__init__(self, pkg, name, icon)
62+ def __init__(self, pkg, name, icon, to_remove):
63+ UpdateItem.__init__(self, pkg, name, icon, to_remove)
64 self._items = set()
65 self._deps = set()
66 self.core_item = None
67 if pkg is not None:
68- self.core_item = UpdateItem(pkg, name, icon)
69+ self.core_item = UpdateItem(pkg, name, icon, to_remove)
70 self._items.add(self.core_item)
71
72 @property
73@@ -70,10 +74,10 @@
74 all_items.extend(self._items)
75 return sorted(all_items, key=lambda a: a.name.lower())
76
77- def add(self, pkg, cache=None, eventloop_callback=None):
78+ def add(self, pkg, cache=None, eventloop_callback=None, to_remove=False):
79 name = utils.get_package_label(pkg)
80 icon = Gio.ThemedIcon.new("package")
81- self._items.add(UpdateItem(pkg, name, icon))
82+ self._items.add(UpdateItem(pkg, name, icon, to_remove))
83 # If the pkg is in self._deps, stop here. We have already calculated
84 # the recursive dependencies for this package, no need to do it again.
85 if cache and pkg.name in cache and pkg.name not in self._deps:
86@@ -140,6 +144,8 @@
87 len(pkgs_installing) < len(self.items))
88
89 def get_total_size(self):
90+ if self.to_remove:
91+ return 0
92 size = 0
93 for item in self.items:
94 size += getattr(item.pkg.candidate, "size", 0)
95@@ -147,26 +153,26 @@
96
97
98 class UpdateApplicationGroup(UpdateGroup):
99- def __init__(self, pkg, application):
100+ def __init__(self, pkg, application, to_remove):
101 name = application.get_display_name()
102 icon = application.get_icon()
103- super(UpdateApplicationGroup, self).__init__(pkg, name, icon)
104+ super(UpdateApplicationGroup, self).__init__(pkg, name, icon, to_remove)
105
106
107 class UpdatePackageGroup(UpdateGroup):
108- def __init__(self, pkg):
109+ def __init__(self, pkg, to_remove):
110 name = utils.get_package_label(pkg)
111 icon = Gio.ThemedIcon.new("package")
112- super(UpdatePackageGroup, self).__init__(pkg, name, icon)
113+ super(UpdatePackageGroup, self).__init__(pkg, name, icon, to_remove)
114
115
116 class UpdateSystemGroup(UpdateGroup):
117- def __init__(self, cache):
118+ def __init__(self, cache, to_remove):
119 # Translators: the %s is a distro name, like 'Ubuntu' and 'base' as in
120 # the core components and packages.
121 name = _("%s base") % utils.get_ubuntu_flavor_name(cache=cache)
122 icon = Gio.ThemedIcon.new("distributor-logo")
123- super(UpdateSystemGroup, self).__init__(None, name, icon)
124+ super(UpdateSystemGroup, self).__init__(None, name, icon, to_remove)
125
126
127 class UpdateOrigin():
128@@ -205,6 +211,7 @@
129 self.distUpgradeWouldDelete = 0
130 self.update_groups = []
131 self.security_groups = []
132+ self.kernel_autoremove_groups = []
133 self.num_updates = 0
134 self.random = random.Random()
135 self.ignored_phased_updates = []
136@@ -411,7 +418,7 @@
137 'linux-tools-virtual',
138 'linux-virtual']
139
140- def _make_groups(self, cache, pkgs, eventloop_callback):
141+ def _make_groups(self, cache, pkgs, eventloop_callback, to_remove = False):
142 if not pkgs:
143 return []
144 ungrouped_pkgs = []
145@@ -421,7 +428,7 @@
146 for pkg in pkgs:
147 app = self._get_application_for_package(pkg)
148 if app is not None:
149- app_group = UpdateApplicationGroup(pkg, app)
150+ app_group = UpdateApplicationGroup(pkg, app, to_remove)
151 app_groups.append(app_group)
152 else:
153 ungrouped_pkgs.append(pkg)
154@@ -435,14 +442,14 @@
155 if len(dep_groups) > 1:
156 break
157 if len(dep_groups) == 1:
158- dep_groups[0].add(pkg, cache, eventloop_callback)
159+ dep_groups[0].add(pkg, cache, eventloop_callback, to_remove)
160 ungrouped_pkgs.remove(pkg)
161
162 system_group = None
163 if ungrouped_pkgs:
164 # Separate out system base packages. If we have already found an
165 # application for all updates, don't bother.
166- meta_group = UpdateGroup(None, None, None)
167+ meta_group = UpdateGroup(None, None, None, to_remove)
168 flavor_package = utils.get_ubuntu_flavor_package(cache=cache)
169 meta_pkgs = [flavor_package, "ubuntu-standard", "ubuntu-minimal"]
170 meta_pkgs.extend(self._get_linux_packages())
171@@ -452,10 +459,10 @@
172 for pkg in ungrouped_pkgs:
173 if meta_group.is_dependency(pkg, cache, eventloop_callback):
174 if system_group is None:
175- system_group = UpdateSystemGroup(cache)
176+ system_group = UpdateSystemGroup(cache, to_remove)
177 system_group.add(pkg)
178 else:
179- pkg_groups.append(UpdatePackageGroup(pkg))
180+ pkg_groups.append(UpdatePackageGroup(pkg, to_remove))
181
182 app_groups.sort(key=lambda a: a.name.lower())
183 pkg_groups.sort(key=lambda a: a.name.lower())
184@@ -472,6 +479,7 @@
185
186 security_pkgs = []
187 upgrade_pkgs = []
188+ kernel_autoremove_pkgs = []
189
190 # Find all upgradable packages
191 for pkg in cache:
192@@ -502,12 +510,22 @@
193 if pkg.is_upgradable and not (pkg.marked_upgrade or
194 pkg.marked_install):
195 self.held_back.append(pkg.name)
196+ continue
197+ if (pkg.is_auto_removable and
198+ (cache.versioned_kernel_pkgs_regexp and
199+ cache.versioned_kernel_pkgs_regexp.match(pkg.name) and
200+ not cache.running_kernel_pkgs_regexp.match(pkg.name))):
201+ kernel_autoremove_pkgs.append(pkg)
202+ pkg.mark_delete()
203
204 if security_pkgs or upgrade_pkgs:
205 # There's updates available. Initiate the desktop file cache.
206- pkg_names = [p.name for p in security_pkgs + upgrade_pkgs]
207+ pkg_names = [p.name for p in
208+ security_pkgs + upgrade_pkgs + kernel_autoremove_pkgs]
209 self._populate_desktop_cache(pkg_names)
210 self.update_groups = self._make_groups(cache, upgrade_pkgs,
211 eventloop_callback)
212 self.security_groups = self._make_groups(cache, security_pkgs,
213 eventloop_callback)
214+ self.kernel_autoremove_groups = self._make_groups(
215+ cache, kernel_autoremove_pkgs, eventloop_callback, True)
216
217=== modified file 'UpdateManager/UpdatesAvailable.py'
218--- UpdateManager/UpdatesAvailable.py 2017-10-05 23:03:24 +0000
219+++ UpdateManager/UpdatesAvailable.py 2018-03-18 22:28:45 +0000
220@@ -282,7 +282,7 @@
221 self.pkg_cell_area = CellAreaPackage(False)
222 pkg_column = Gtk.TreeViewColumn.new_with_area(self.pkg_cell_area)
223 self.pkg_cell_area.column = pkg_column
224- pkg_column.set_title(_("Install"))
225+ pkg_column.set_title(_("Install or remove"))
226 pkg_column.set_property("spacing", 4)
227 pkg_column.set_expand(True)
228 self.treeview_update.append_column(pkg_column)
229@@ -955,7 +955,10 @@
230 if keep_packages:
231 item.pkg.mark_keep()
232 elif item.pkg.name not in self.list.held_back:
233- item.pkg.mark_install()
234+ if not item.to_remove:
235+ item.pkg.mark_install()
236+ else:
237+ item.pkg.mark_delete()
238 except SystemError:
239 pass
240
241@@ -1051,11 +1054,20 @@
242 self._add_groups(self.list.security_groups)
243 if self.list.security_groups and self.list.update_groups:
244 self._add_header(_("Other updates"), self.list.update_groups)
245+ elif self.list.update_groups and self.list.kernel_autoremove_groups:
246+ self._add_header(_("Updates"), self.list.update_groups)
247 if self.list.update_groups:
248 self._add_groups(self.list.update_groups)
249+ if self.list.kernel_autoremove_groups:
250+ self._add_header(
251+ _("Unused kernel updates to be removed"),
252+ self.list.kernel_autoremove_groups)
253+ self._add_groups(self.list.kernel_autoremove_groups)
254
255 self.treeview_update.set_model(self.store)
256- self.pkg_cell_area.indent_toplevel = bool(self.list.security_groups)
257+ self.pkg_cell_area.indent_toplevel = (
258+ bool(self.list.security_groups) or
259+ bool(self.list.kernel_autoremove_groups))
260 self.update_close_button()
261 self.update_count()
262 self.setBusy(False)
263
264=== modified file 'debian/changelog'
265--- debian/changelog 2018-03-18 21:37:38 +0000
266+++ debian/changelog 2018-03-18 22:28:45 +0000
267@@ -1,3 +1,10 @@
268+update-manager (1:18.04.8) UNRELEASED; urgency=medium
269+
270+ * Offer removal of unused autoremovable kernel packages
271+ (LP: #1624644, #1675079)
272+
273+ -- Balint Reczey <rbalint@ubuntu.com> Sun, 18 Mar 2018 21:44:25 +0000
274+
275 update-manager (1:18.04.7) bionic; urgency=medium
276
277 * Comment out the KDE frontend in debian/control so we can remove pykde4

Subscribers

People subscribed via source and target branches

to status/vote changes: