Merge lp:~rbalint/update-manager/remove-autoremovable-kernels into lp:update-manager
- remove-autoremovable-kernels
- Merge into main
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Julian Andres Klode | Approve | ||
Brian Murray | Pending | ||
Ćukasz Zemczak | Pending | ||
Review via email: mp+341599@code.launchpad.net |
Commit message
Description of the change
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::VersionedK
Do you think this code would break on non-buggy APT::VersionedK
I can add parentheses like (^...)|(^...) but that does not seem to be needed.
I'd like to avoid multiple matching to keep matching fast.
Balint Reczey (rbalint) wrote : | # |
Moreover apt regexps like "foo|.*-bar" would break detecting the current running kernel's packages, too.
Julian Andres Klode (juliank) wrote : | # |
Yes, you're right, | without () would not work correctly anyway.
Preview Diff
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 |
joining the regular expressions with | might fail in case they themselves contain | (or one of them is invalid).