diff -Nru update-manager-18.04.11.13/.bzrignore update-manager-18.04.11.23/.bzrignore --- update-manager-18.04.11.13/.bzrignore 1970-01-01 00:00:00.000000000 +0000 +++ update-manager-18.04.11.23/.bzrignore 2024-01-22 20:36:03.000000000 +0000 @@ -0,0 +1,11 @@ +__pycache__ +debian/*.debhelper* +debian/*.substvars +debian/files +debian/python-update-manager +debian/python3-update-manager +debian/tmp +debian/update-manager +debian/update-manager-core +debian/update-manager-kde +debian/update-manager-text diff -Nru update-manager-18.04.11.13/.gitignore update-manager-18.04.11.23/.gitignore --- update-manager-18.04.11.13/.gitignore 1970-01-01 00:00:00.000000000 +0000 +++ update-manager-18.04.11.23/.gitignore 2024-01-22 20:36:03.000000000 +0000 @@ -0,0 +1,2 @@ +tags +__pycache__ diff -Nru update-manager-18.04.11.13/UpdateManager/Core/MyCache.py update-manager-18.04.11.23/UpdateManager/Core/MyCache.py --- update-manager-18.04.11.13/UpdateManager/Core/MyCache.py 2018-10-31 11:29:09.000000000 +0000 +++ update-manager-18.04.11.23/UpdateManager/Core/MyCache.py 2024-01-22 20:36:03.000000000 +0000 @@ -81,6 +81,7 @@ self._initDepCache() self.all_changes = {} self.all_news = {} + self.pro_versions = {} # on broken packages, try to fix via saveDistUpgrade() if self._depcache.broken_count > 0: self.saveDistUpgrade() @@ -421,14 +422,22 @@ "Please check your Internet connection.") self.all_changes[name] += error_message + # If the machine is not attached to Ubuntu Pro, Update Manager advertises + # the upgrades that would be available if it were attached. + # As that is unbeknownst to Apt, we need this map to show the correct + # version of each upgradable-if-pro-subscribed package. + def create_pro_cache(self, pro_pkgs): + for (name, version, _a, _a) in pro_pkgs: + self.pro_versions[name] = version + def get_changelog(self, name): " get the changelog file from the changelog location " origins = self[name].candidate.origins + version = self.pro_versions.get(name, self[name].candidate.version) self.all_changes[name] = _("Changes for %s versions:\n" "Installed version: %s\n" "Available version: %s\n\n") % \ - (name, getattr(self[name].installed, "version", None), - self[name].candidate.version) + (name, getattr(self[name].installed, "version", None), version) if self.CHANGELOG_ORIGIN not in [o.origin for o in origins]: self._fetch_changelog_for_third_party_package(name, origins) return diff -Nru update-manager-18.04.11.13/UpdateManager/Core/UpdateList.py update-manager-18.04.11.23/UpdateManager/Core/UpdateList.py --- update-manager-18.04.11.13/UpdateManager/Core/UpdateList.py 2018-10-31 11:29:09.000000000 +0000 +++ update-manager-18.04.11.23/UpdateManager/Core/UpdateList.py 2024-01-22 20:36:03.000000000 +0000 @@ -43,11 +43,12 @@ class UpdateItem(): - def __init__(self, pkg, name, icon, to_remove): + def __init__(self, pkg, name, icon, to_remove, sensitive=True): self.icon = icon self.name = name self.pkg = pkg self.to_remove = to_remove + self.sensitive = sensitive def is_selected(self): if not self.to_remove: @@ -59,13 +60,13 @@ class UpdateGroup(UpdateItem): _depcache = {} - def __init__(self, pkg, name, icon, to_remove): - UpdateItem.__init__(self, pkg, name, icon, to_remove) + def __init__(self, pkg, name, icon, to_remove, sensitive=True): + UpdateItem.__init__(self, pkg, name, icon, to_remove, sensitive) self._items = set() self._deps = set() self.core_item = None if pkg is not None: - self.core_item = UpdateItem(pkg, name, icon, to_remove) + self.core_item = UpdateItem(pkg, name, icon, to_remove, sensitive) self._items.add(self.core_item) @property @@ -74,10 +75,11 @@ all_items.extend(self._items) return sorted(all_items, key=lambda a: a.name.lower()) - def add(self, pkg, cache=None, eventloop_callback=None, to_remove=False): + def add(self, pkg, cache=None, eventloop_callback=None, to_remove=False, + sensitive=True): name = utils.get_package_label(pkg) icon = Gio.ThemedIcon.new("package") - self._items.add(UpdateItem(pkg, name, icon, to_remove)) + self._items.add(UpdateItem(pkg, name, icon, to_remove, sensitive)) # If the pkg is in self._deps, stop here. We have already calculated # the recursive dependencies for this package, no need to do it again. if cache and pkg.name in cache and pkg.name not in self._deps: @@ -153,27 +155,29 @@ class UpdateApplicationGroup(UpdateGroup): - def __init__(self, pkg, application, to_remove): + def __init__(self, pkg, application, to_remove, sensitive=True): name = application.get_display_name() icon = application.get_icon() super(UpdateApplicationGroup, self).__init__(pkg, name, icon, - to_remove) + to_remove, sensitive) class UpdatePackageGroup(UpdateGroup): - def __init__(self, pkg, to_remove): + def __init__(self, pkg, to_remove, sensitive=True): name = utils.get_package_label(pkg) icon = Gio.ThemedIcon.new("package") - super(UpdatePackageGroup, self).__init__(pkg, name, icon, to_remove) + super(UpdatePackageGroup, self).__init__(pkg, name, icon, to_remove, + sensitive) class UpdateSystemGroup(UpdateGroup): - def __init__(self, cache, to_remove): + def __init__(self, cache, to_remove, sensitive=True): # Translators: the %s is a distro name, like 'Ubuntu' and 'base' as in # the core components and packages. name = _("%s base") % utils.get_ubuntu_flavor_name(cache=cache) icon = Gio.ThemedIcon.new("distributor-logo") - super(UpdateSystemGroup, self).__init__(None, name, icon, to_remove) + super(UpdateSystemGroup, self).__init__(None, name, icon, to_remove, + sensitive) class UpdateOrigin(): @@ -213,6 +217,8 @@ self.update_groups = [] self.security_groups = [] self.kernel_autoremove_groups = [] + self.ubuntu_pro_groups = [] + self.ubuntu_pro_fake_groups = [] self.num_updates = 0 self.random = random.Random() self.ignored_phased_updates = [] @@ -419,7 +425,8 @@ 'linux-tools-virtual', 'linux-virtual'] - def _make_groups(self, cache, pkgs, eventloop_callback, to_remove=False): + def _make_groups(self, cache, pkgs, eventloop_callback, to_remove=False, + sensitive=True): if not pkgs: return [] ungrouped_pkgs = [] @@ -429,7 +436,8 @@ for pkg in pkgs: app = self._get_application_for_package(pkg) if app is not None: - app_group = UpdateApplicationGroup(pkg, app, to_remove) + app_group = UpdateApplicationGroup(pkg, app, to_remove, + sensitive) app_groups.append(app_group) else: ungrouped_pkgs.append(pkg) @@ -450,7 +458,7 @@ if ungrouped_pkgs: # Separate out system base packages. If we have already found an # application for all updates, don't bother. - meta_group = UpdateGroup(None, None, None, to_remove) + meta_group = UpdateGroup(None, None, None, to_remove, sensitive) flavor_package = utils.get_ubuntu_flavor_package(cache=cache) meta_pkgs = [flavor_package, "ubuntu-standard", "ubuntu-minimal"] meta_pkgs.extend(self._get_linux_packages()) @@ -460,10 +468,13 @@ for pkg in ungrouped_pkgs: if meta_group.is_dependency(pkg, cache, eventloop_callback): if system_group is None: - system_group = UpdateSystemGroup(cache, to_remove) + system_group = UpdateSystemGroup(cache, to_remove, + sensitive) system_group.add(pkg) else: - pkg_groups.append(UpdatePackageGroup(pkg, to_remove)) + pkg_groups.append(UpdatePackageGroup( + pkg, to_remove, sensitive) + ) app_groups.sort(key=lambda a: a.name.lower()) pkg_groups.sort(key=lambda a: a.name.lower()) @@ -472,16 +483,56 @@ return app_groups + pkg_groups - def update(self, cache, eventloop_callback=None): + def update(self, cache, eventloop_callback=None, ua_security_packages=[]): self.held_back = [] # do the upgrade self.distUpgradeWouldDelete = cache.saveDistUpgrade() + pro_pkgs = [] security_pkgs = [] upgrade_pkgs = [] kernel_autoremove_pkgs = [] + class FakeUbuntuProPackageCandidate: + def __init__(self, source_name, version, size): + self.source_name = source_name + self.summary = source_name + self.description = source_name + self.version = version + self.size = size + self.downloadable = False + self.record = {} + + class FakeUbuntuProPackage: + def __init__(self, package_name, version, size): + self.name = package_name + self.candidate = FakeUbuntuProPackageCandidate(package_name, + version, size) + self.marked_install = False + self.marked_upgrade = False + self.marked_delete = False + self.installed_files = [] + + def mark_install(self): + pass + + def mark_delete(self): + pass + fake_ua_packages = [] + ua_packages_names = [] + for ( + package_name, + version, + size, + downloadable + ) in ua_security_packages: + if downloadable: + ua_packages_names.append(package_name) + else: + fake_ua_packages.append(FakeUbuntuProPackage(package_name, + version, size)) + # Find all upgradable packages for pkg in cache: if pkg.is_upgradable or pkg.marked_install: @@ -501,7 +552,10 @@ self.ignored_phased_updates.append(pkg) continue - if is_security_update: + comes_from_pro = pkg.name in ua_packages_names + if comes_from_pro: + pro_pkgs.append(pkg) + elif is_security_update: security_pkgs.append(pkg) else: upgrade_pkgs.append(pkg) @@ -524,10 +578,13 @@ for pkg in self.ignored_phased_updates: pkg.mark_keep() - if security_pkgs or upgrade_pkgs: + if security_pkgs or upgrade_pkgs or pro_pkgs: # There's updates available. Initiate the desktop file cache. pkg_names = [p.name for p in - security_pkgs + upgrade_pkgs + kernel_autoremove_pkgs] + pro_pkgs + + security_pkgs + + upgrade_pkgs + + kernel_autoremove_pkgs] self._populate_desktop_cache(pkg_names) self.update_groups = self._make_groups(cache, upgrade_pkgs, eventloop_callback) @@ -535,3 +592,7 @@ eventloop_callback) self.kernel_autoremove_groups = self._make_groups( cache, kernel_autoremove_pkgs, eventloop_callback, True) + self.ubuntu_pro_fake_groups = self._make_groups( + cache, fake_ua_packages, eventloop_callback, sensitive=False) + self.ubuntu_pro_groups = self._make_groups( + cache, pro_pkgs, eventloop_callback) diff -Nru update-manager-18.04.11.13/UpdateManager/Core/utils.py update-manager-18.04.11.23/UpdateManager/Core/utils.py --- update-manager-18.04.11.13/UpdateManager/Core/utils.py 2019-04-09 23:03:43.000000000 +0000 +++ update-manager-18.04.11.23/UpdateManager/Core/utils.py 2024-01-22 20:36:03.000000000 +0000 @@ -78,6 +78,16 @@ print("%s: %s" % (self.info, time.time() - self.now)) +class SoftwarePropertiesPage(): + ubuntu_software = 0 + other_software = 1 + updates = 2 + authentication = 3 + additional_drivers = 4 + developer_options = 5 + ubuntu_pro = 6 + + def get_string_with_no_auth_from_source_entry(entry): tmp = copy(entry) url_parts = urlsplit(tmp.uri) diff -Nru update-manager-18.04.11.13/UpdateManager/Dialogs.py update-manager-18.04.11.23/UpdateManager/Dialogs.py --- update-manager-18.04.11.13/UpdateManager/Dialogs.py 2020-04-21 14:27:18.000000000 +0000 +++ update-manager-18.04.11.23/UpdateManager/Dialogs.py 2024-01-22 20:36:03.000000000 +0000 @@ -161,10 +161,10 @@ if self._is_livepatch_supported() and \ self.settings_button and \ self.settings.get_int('launch-count') >= 4: - self.set_desc(_("Tip: You can use Livepatch to " - "keep your computer more secure between " - "restarts.")) - self.settings_button.set_label(_("Settings & Livepatch…")) + self.set_desc(_("Tip: You can use Livepatch with " + "Ubuntu Pro to keep your computer more " + "secure between restarts.")) + self.settings_button.set_label(_("Settings & Pro…")) return needs_reschedule = False diff -Nru update-manager-18.04.11.13/UpdateManager/UpdateManager.py update-manager-18.04.11.23/UpdateManager/UpdateManager.py --- update-manager-18.04.11.13/UpdateManager/UpdateManager.py 2020-06-03 18:41:48.000000000 +0000 +++ update-manager-18.04.11.23/UpdateManager/UpdateManager.py 2024-01-22 20:36:03.000000000 +0000 @@ -36,6 +36,7 @@ import apt_pkg import distro_info +import json import os import subprocess import sys @@ -64,7 +65,7 @@ from .Core.MyCache import MyCache from .Core.roam import NetworkManagerHelper from .Core.UpdateList import UpdateList -from .Core.utils import get_arch, get_dist +from .Core.utils import get_arch, get_dist, SoftwarePropertiesPage from .backend import (InstallBackend, get_backend) @@ -85,6 +86,7 @@ self.unity = UnitySupport() self.controller = None self.cache = None + self.ua_security_packages = [] self.update_list = None self.meta_release = None self.hwe_replacement_packages = None @@ -193,13 +195,13 @@ self._start_pane(None) sys.exit(0) - def show_settings(self): + def show_settings(self, page_number=SoftwarePropertiesPage.updates): try: apt_pkg.pkgsystem_unlock() except SystemError: pass cmd = ["/usr/bin/software-properties-gtk", - "--open-tab", "2"] + "--open-tab", str(page_number)] if "WAYLAND_DISPLAY" not in os.environ: cmd += ["--toplevel", "%s" % self.get_window().get_xid()] @@ -247,23 +249,41 @@ cancelled_update, error_occurred) self._start_pane(pane) + def _get_ua_security_status(self): + self.ua_security_packages = [] + try: + p = subprocess.Popen(['pro', 'security-status', '--format=json'], + stdout=subprocess.PIPE) + except OSError: + pass + else: + while p.poll() is None: + while Gtk.events_pending(): + Gtk.main_iteration() + time.sleep(0.05) + s = json.load(p.stdout) + for package in s.get('packages', []): + if package.get('service_name', '') == 'standard-security': + continue + status = package.get('status', '') + if ( + status == 'pending_attach' + or status == 'pending_enable' + or status == 'upgrade_available' + ): + name = package.get('package', '') + version = package.get('version', '') + size = package.get('download_size', 0) + downloadable = status == 'upgrade_available' + self.ua_security_packages.append( + (name, version, size, downloadable) + ) + self.cache.create_pro_cache(self.ua_security_packages) + def _make_available_pane(self, install_count, need_reboot=False, cancelled_update=False, error_occurred=False): self._check_hwe_support_status() - if install_count == 0: - # Need Restart > New Release > No Updates - if need_reboot: - return NeedRestartDialog(self) - dist_upgrade = self._check_meta_release() - if dist_upgrade: - return dist_upgrade - elif cancelled_update: - return StoppedUpdatesDialog(self) - elif self.hwe_replacement_packages: - return HWEUpgradeDialog(self) - else: - return NoUpdatesDialog(self, error_occurred=error_occurred) - else: + if install_count != 0: header = None desc = None if error_occurred: @@ -276,6 +296,19 @@ elif self.hwe_replacement_packages: return HWEUpgradeDialog(self) return UpdatesAvailable(self, header, desc, need_reboot) + else: + # Need Restart > New Release > No Updates + if need_reboot: + return NeedRestartDialog(self) + dist_upgrade = self._check_meta_release() + if dist_upgrade: + return dist_upgrade + elif cancelled_update: + return StoppedUpdatesDialog(self) + elif self.hwe_replacement_packages: + return HWEUpgradeDialog(self) + else: + return NoUpdatesDialog(self, error_occurred=error_occurred) def start_error(self, update_and_retry, header, desc): if update_and_retry: @@ -406,9 +439,13 @@ Gtk.main_iteration() iterate() + self._get_ua_security_status() + self.update_list = UpdateList(self) try: - self.update_list.update(self.cache, eventloop_callback=iterate) + self.update_list.update(self.cache, eventloop_callback=iterate, + ua_security_packages=self. + ua_security_packages) except SystemError as e: header = _("Could not calculate the upgrade") desc = _("An unresolvable problem occurred while " diff -Nru update-manager-18.04.11.13/UpdateManager/UpdatesAvailable.py update-manager-18.04.11.23/UpdateManager/UpdatesAvailable.py --- update-manager-18.04.11.13/UpdateManager/UpdatesAvailable.py 2018-10-31 11:29:09.000000000 +0000 +++ update-manager-18.04.11.23/UpdateManager/UpdatesAvailable.py 2024-01-22 20:36:03.000000000 +0000 @@ -55,7 +55,7 @@ from gettext import gettext as _ from gettext import ngettext -from .Core.utils import humanize_size +from .Core.utils import humanize_size, SoftwarePropertiesPage from .Core.AlertWatcher import AlertWatcher from .Core.UpdateList import UpdateSystemGroup from .Dialogs import InternalDialog @@ -74,7 +74,8 @@ # - screen reader does not say "Downloaded" for downloaded updates # list constants -(LIST_NAME, LIST_UPDATE_DATA, LIST_SIZE, LIST_TOGGLE_ACTIVE) = range(4) +(LIST_NAME, LIST_UPDATE_DATA, LIST_SIZE, LIST_TOGGLE_ACTIVE, + LIST_SENSITIVE) = range(5) # NetworkManager enums from .Core.roam import NetworkManagerHelper @@ -249,19 +250,24 @@ self.add_settings_button() self.button_close = self.add_button(Gtk.STOCK_CANCEL, self.window_main.close) + self.button_pro = self.add_button(_("Enable Ubuntu Pro..."), + self.on_button_pro_clicked) self.button_install = self.add_button(_("Install Now"), self.on_button_install_clicked) self.focus_button = self.button_install # create text view self.textview_changes = ChangelogViewer() + self.textview_changes.set_wrap_mode(Gtk.WrapMode.WORD) self.textview_changes.show() self.scrolledwindow_changes.add(self.textview_changes) changes_buffer = self.textview_changes.get_buffer() changes_buffer.create_tag("versiontag", weight=Pango.Weight.BOLD) + changes_buffer.create_tag("changestag", weight=Pango.Weight.BOLD) + changes_buffer.create_tag("descriptiontag", weight=Pango.Weight.BOLD) # the treeview (move into it's own code!) - self.store = Gtk.TreeStore(str, GObject.TYPE_PYOBJECT, str, bool) + self.store = Gtk.TreeStore(str, GObject.TYPE_PYOBJECT, str, bool, bool) self.treeview_update.set_model(None) self.image_restart.set_from_gicon(self.get_restart_icon(), @@ -293,6 +299,8 @@ pkg_column.pack_start(pkg_toggle_renderer, False) pkg_column.add_attribute(pkg_toggle_renderer, 'active', LIST_TOGGLE_ACTIVE) + pkg_column.add_attribute(pkg_toggle_renderer, + 'sensitive', LIST_SENSITIVE) pkg_column.set_cell_data_func(pkg_toggle_renderer, self.pkg_toggle_renderer_data_func) @@ -319,6 +327,8 @@ size_column = Gtk.TreeViewColumn(_("Download"), size_renderer, text=LIST_SIZE) size_column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE) + size_column.add_attribute(size_renderer, + 'sensitive', LIST_SENSITIVE) self.treeview_update.append_column(size_column) self.treeview_update.set_headers_visible(True) @@ -342,7 +352,6 @@ self.expander_details.connect("activate", self.pre_activate_details) self.expander_details.connect("notify::expanded", self.activate_details) - self.expander_desc.connect("notify::expanded", self.activate_desc) # If auto-updates are on, change cancel label self.notifier_settings = Gio.Settings.new("com.ubuntu.update-notifier") @@ -358,6 +367,17 @@ self.alert_watcher.connect("network-3g-alert", self._on_network_3g_alert) + try: + from uaclient.api.u.apt_news.current_news.v1 import current_news + apt_news = current_news().current_news + except ImportError: + apt_news = self._get_apt_news( + "/var/lib/ubuntu-advantage/messages/apt-news" + ) + if apt_news: + self.news.get_buffer().set_text(apt_news) + self.expander_news.set_visible(True) + def stop(self): InternalDialog.stop(self) self._save_state() @@ -404,8 +424,11 @@ def restart_icon_renderer_data_func(self, cell_layout, renderer, model, iter, user_data): data = model.get_value(iter, LIST_UPDATE_DATA) + sensitive = model.get_value(iter, LIST_SENSITIVE) path = model.get_path(iter) + renderer.set_sensitive(sensitive) + requires_restart = False if data.item and data.item.pkg: requires_restart = self.pkg_requires_restart(data.item.pkg) @@ -433,6 +456,7 @@ if data.item: activatable = data.item.pkg.name not in self.list.held_back inconsistent = False + renderer.set_sensitive(data.item.sensitive) elif data.group: activatable = True inconsistent = data.group.selection_is_inconsistent() @@ -485,8 +509,11 @@ def pkg_label_renderer_data_func(self, cell_layout, renderer, model, iter, user_data): data = model.get_value(iter, LIST_UPDATE_DATA) + sensitive = model.get_value(iter, LIST_SENSITIVE) name = GLib.markup_escape_text(model.get_value(iter, LIST_NAME)) + renderer.set_sensitive(sensitive) + if data.group: markup = name elif data.item: @@ -496,8 +523,18 @@ renderer.set_property("markup", markup) - def set_changes_buffer(self, changes_buffer, text, name, srcpkg): - changes_buffer.set_text("") + def set_changes_buffer(self, changes_buffer, long_desc, text, name, + srcpkg): + changes_buffer.set_text("") # Clear "downloading list of changes..." + + # Write the technical description section + changes_buffer.insert_with_tags_by_name(changes_buffer.get_end_iter(), + "Technical description\n", + "descriptiontag") + changes_buffer.insert(changes_buffer.get_end_iter(), + long_desc + "\n\n") + + # Write the changes section lines = text.split("\n") if len(lines) == 1: changes_buffer.set_text(text) @@ -509,12 +546,16 @@ r'^%s \((.*)\)(.*)\;.*$' % re.escape(srcpkg), line) #bullet_match = re.match("^.*[\*-]", line) author_match = re.match("^.*--.*<.*@.*>.*$", line) + changes_match = re.match(r'^Changes for [^ ]+ versions:$', line) if version_match: version = version_match.group(1) #upload_archive = version_match.group(2).strip() version_text = _("Version %s: \n") % version changes_buffer.insert_with_tags_by_name(end_iter, version_text, "versiontag") + elif changes_match: + changes_buffer.insert_with_tags_by_name(end_iter, line + "\n", + "changestag") elif (author_match): pass else: @@ -539,12 +580,8 @@ item.pkg.candidate.description is None): changes_buffer = self.textview_changes.get_buffer() changes_buffer.set_text("") - desc_buffer = self.textview_descr.get_buffer() - desc_buffer.set_text("") - self.notebook_details.set_sensitive(False) return long_desc = item.pkg.candidate.description - self.notebook_details.set_sensitive(True) # do some regular expression magic on the description # Add a newline before each bullet p = re.compile(r'^(\s|\t)*(\*|0|-)', re.MULTILINE) @@ -556,9 +593,6 @@ p = re.compile(r'\s\s+', re.MULTILINE) long_desc = p.sub("\n", long_desc) - desc_buffer = self.textview_descr.get_buffer() - desc_buffer.set_text(long_desc) - # now do the changelog name = item.pkg.name if name is None: @@ -571,7 +605,8 @@ if name in self.cache.all_changes: changes = self.cache.all_changes[name] srcpkg = self.cache[name].candidate.source_name - self.set_changes_buffer(changes_buffer, changes, name, srcpkg) + self.set_changes_buffer(changes_buffer, long_desc, changes, name, + srcpkg) # if not connected, do not even attempt to get the changes elif not self.connected: changes_buffer.set_text( @@ -616,8 +651,10 @@ changes += self.cache.all_news[name] if name in self.cache.all_changes: changes += self.cache.all_changes[name] - if changes: - self.set_changes_buffer(changes_buffer, changes, name, srcpkg) + + if changes or long_desc: + self.set_changes_buffer(changes_buffer, long_desc, changes, name, + srcpkg) def on_treeview_button_press(self, widget, event): """ @@ -644,6 +681,11 @@ menu.show() return True + def _get_apt_news(self, apt_news_file): + if os.access(apt_news_file, os.R_OK): + with open(apt_news_file) as f: + return f.read() + # we need this for select all/unselect all def _toggle_group_headers(self, new_selection_value): """ small helper that will set/unset the group headers @@ -741,6 +783,7 @@ # self.button_install.set_sensitive(True) self.button_install.set_sensitive(True) self.unity.set_install_menuitem_visible(True) + self.button_pro.destroy() else: if inst_count > 0: download_str = ngettext( @@ -749,10 +792,17 @@ inst_count) self.button_install.set_sensitive(True) self.unity.set_install_menuitem_visible(True) + self.button_pro.destroy() + elif (self.list.ubuntu_pro_fake_groups + and not self.list.ubuntu_pro_groups): + download_str = _("You need to enable Ubuntu Pro to " + "install these updates.") + self.button_install.destroy() else: download_str = _("There are no updates to install.") self.button_install.set_sensitive(False) self.unity.set_install_menuitem_visible(False) + self.button_pro.destroy() self.image_downsize.set_sensitive(False) self.label_downsize.set_text(download_str) self.hbox_downsize.show() @@ -797,7 +847,6 @@ text_desc = _("The computer also needs to restart " "to finish installing previous updates.") - self.notebook_details.set_sensitive(True) self.treeview_update.set_sensitive(True) self.set_header(text_header) self.set_desc(text_desc) @@ -818,8 +867,10 @@ self._restore_state() def activate_desc(self, expander, data): - expanded = self.expander_desc.get_expanded() - self.expander_desc.set_vexpand(expanded) + return + + def on_button_pro_clicked(self): + self.window_main.show_settings(SoftwarePropertiesPage.ubuntu_pro) def on_button_install_clicked(self): self.unity.set_install_menuitem_visible(False) @@ -989,7 +1040,7 @@ self.window_main.end_user_resizable() return False - def _add_header(self, name, groups): + def _add_header(self, name, groups, sensitive=True): total_size = 0 for group in groups: total_size = total_size + group.get_total_size() @@ -997,7 +1048,8 @@ name, UpdateData(groups, None, None), humanize_size(total_size), - True + True, + sensitive ] return self.store.append(None, header_row) @@ -1016,11 +1068,14 @@ len(group.items) == 1: group_is_item = group.items[0] + if group.name == "Ubuntu base": + group.name = "System components" group_row = [ group.name, UpdateData(None, group, group_is_item), humanize_size(group.get_total_size()), - True + True, + group.sensitive ] group_iter = self.store.append(None, group_row) @@ -1031,7 +1086,8 @@ item.name, UpdateData(None, None, item), humanize_size(getattr(item.pkg.candidate, "size", 0)), - True + True, + group.sensitive ] self.store.append(group_iter, item_row) @@ -1052,22 +1108,36 @@ if self.list.security_groups: self._add_header(_("Security updates"), self.list.security_groups) self._add_groups(self.list.security_groups) - if self.list.security_groups and self.list.update_groups: - self._add_header(_("Other updates"), self.list.update_groups) - elif self.list.update_groups and self.list.kernel_autoremove_groups: - self._add_header(_("Updates"), self.list.update_groups) if self.list.update_groups: + self._add_header(_("Other updates"), self.list.update_groups) self._add_groups(self.list.update_groups) if self.list.kernel_autoremove_groups: self._add_header( _("Unused kernel updates to be removed"), self.list.kernel_autoremove_groups) self._add_groups(self.list.kernel_autoremove_groups) + if self.list.ubuntu_pro_groups: + self._add_header( + _("Ubuntu Pro security updates"), + self.list.ubuntu_pro_groups, + sensitive=True + ) + self._add_groups(self.list.ubuntu_pro_groups) + if self.list.ubuntu_pro_fake_groups: + self._add_header( + _("Ubuntu Pro security updates (enable in Settings…)"), + self.list.ubuntu_pro_fake_groups, + sensitive=False + ) + self._add_groups(self.list.ubuntu_pro_fake_groups) self.treeview_update.set_model(self.store) self.pkg_cell_area.indent_toplevel = ( - bool(self.list.security_groups) or - bool(self.list.kernel_autoremove_groups)) + bool(self.list.ubuntu_pro_fake_groups) + or bool(self.list.ubuntu_pro_groups) + or bool(self.list.update_groups) + or bool(self.list.security_groups) + or bool(self.list.kernel_autoremove_groups)) self.update_close_button() self.update_count() self.setBusy(False) diff -Nru update-manager-18.04.11.13/data/gtkbuilder/UpdateManager.ui update-manager-18.04.11.23/data/gtkbuilder/UpdateManager.ui --- update-manager-18.04.11.13/data/gtkbuilder/UpdateManager.ui 2018-10-31 11:29:09.000000000 +0000 +++ update-manager-18.04.11.23/data/gtkbuilder/UpdateManager.ui 2024-01-22 20:36:03.000000000 +0000 @@ -5,6 +5,63 @@ True False 12 + + + + False + + + + + False + + + + + False + + + + + + + False + True + News + 6 + True + + + True + True + in + 80 + + + True + False + 6 + 6 + 6 + word + False + False + + + + + + + False + 0 + + True @@ -12,11 +69,9 @@ 6 True - + True - False - True - 6 + vertical True @@ -44,113 +99,21 @@ - True - True - 0 + False + True - + True True + in - - True - True - False - - - True - False - 6 - 6 - - - True - True - in - - - - - - True - True - 0 - - - - - - - True - False - Changes - - - False - - - - - True - True - 6 - in - 80 - - - True - True - 6 - False - word - 6 - 6 - False - False - - - Description - - - - - - - 1 - - - - - True - False - Description - - - Description - - - - - 1 - False - - - - - - - True - False - Technical description - + - False - True - 1 + True @@ -167,7 +130,7 @@ True True - 2 + 1 @@ -177,12 +140,13 @@ False - 12 + 8 True False aptdaemon-download + 16 False @@ -400,7 +364,7 @@ False True - 3 + 2 diff -Nru update-manager-18.04.11.13/debian/changelog update-manager-18.04.11.23/debian/changelog --- update-manager-18.04.11.13/debian/changelog 2020-06-03 18:41:50.000000000 +0000 +++ update-manager-18.04.11.23/debian/changelog 2024-01-22 20:36:03.000000000 +0000 @@ -1,3 +1,82 @@ +update-manager (1:18.04.11.23) bionic; urgency=medium + + * d/control: Depend on ubuntu-advantage-tools >= 30~. + Fixes edge case whereby the program crashes upon an incomplete + response from ua security-status (LP: #2049785). + * The New Release dialog should take precedence over a list of updates dialog + if the latter would only show Ubuntu Pro updates in an unattached system. + (LP: #2051115). + + -- Nathan Pratta Teodosio Mon, 22 Jan 2024 21:36:03 +0100 + +update-manager (1:18.04.11.22) bionic; urgency=medium + + * Add back removed widgets to UI file to fix crash when updating + Upgrade Manager from inside itself (LP: #2045918). + + -- Nathan Pratta Teodosio Fri, 08 Dec 2023 15:39:44 +0100 + +update-manager (1:18.04.11.21) bionic; urgency=medium + + * Fix incorrect available version for Ubuntu Pro updates in unattached case + (LP: #2043425). + + -- Nathan Pratta Teodosio Thu, 16 Nov 2023 10:03:37 +0100 + +update-manager (1:18.04.11.20) bionic; urgency=medium + + * Ubuntu Pro (LP: #1990450): + - fix another linter error which was created while fixing a missing + space in the previous upload... + + -- Sebastien Bacher Fri, 06 Oct 2023 09:31:28 +0200 + +update-manager (1:18.04.11.19) bionic; urgency=medium + + * Ubuntu Pro (LP: #1990450): + - Address linter errors that cause autopkgtest to fail. + + -- Nathan Pratta Teodosio Thu, 05 Oct 2023 10:38:09 +0200 + +update-manager (1:18.04.11.18) bionic; urgency=medium + + * Ubuntu Pro (LP: #1990450): + - Show Ubuntu Pro packages, whether the system is attached to Ubuntu Pro + or not. + - Replace Install now button by Enable Ubuntu Pro button when only Ubuntu + Pro packages are available and the machine is not attached. + - Fix checkbox and expander widget from overlapping. + - Add News pane in a expander. + - Replace notebook with Description and Changes tabs by a pane. + + -- Nathan Pratta Teodosio Tue, 29 Aug 2023 07:47:20 +0200 + +update-manager (1:18.04.11.17) bionic; urgency=medium + + * Fix Ubuntu Pro updates checkbox and expander widget from overlapping + (LP: #1990450) + + -- Robert Ancell Fri, 03 Feb 2023 14:55:56 +1300 + +update-manager (1:18.04.11.16) bionic; urgency=medium + + * Update of the parsing for pro client changes (lp: #1990450) + + -- Sebastien Bacher Thu, 26 Jan 2023 12:05:05 +0100 + +update-manager (1:18.04.11.15) bionic; urgency=medium + + * Show pending Ubuntu pro packages (LP: #1990450) + + -- Robert Ancell Wed, 18 Jan 2023 15:09:17 +1300 + +update-manager (1:18.04.11.14) bionic; urgency=medium + + * tests/test_meta_release_core.py: switch a test from using lucid to bionic + as precise was removed from the archive. (LP: #1929865) + + -- Brian Murray Thu, 27 May 2021 13:54:14 -0700 + update-manager (1:18.04.11.13) bionic; urgency=medium * UpdateManager/UpdateManager.py: when refreshing the cache and encountering diff -Nru update-manager-18.04.11.13/debian/control update-manager-18.04.11.23/debian/control --- update-manager-18.04.11.13/debian/control 2018-10-31 11:29:38.000000000 +0000 +++ update-manager-18.04.11.23/debian/control 2024-01-22 20:36:03.000000000 +0000 @@ -27,8 +27,10 @@ ${misc:Depends}, python3-update-manager (= ${source:Version}), python3-distro-info, + python3-yaml, distro-info-data, lsb-release, + ubuntu-advantage-tools (>= 30~), ubuntu-release-upgrader-core (>= 1:18.04.9) Recommends: libpam-modules (>= 1.0.1-9ubuntu3) Replaces: update-manager (<< 1:0.146.2) diff -Nru update-manager-18.04.11.13/tests/test_meta_release_core.py update-manager-18.04.11.23/tests/test_meta_release_core.py --- update-manager-18.04.11.13/tests/test_meta_release_core.py 2019-04-09 23:03:43.000000000 +0000 +++ update-manager-18.04.11.23/tests/test_meta_release_core.py 2024-01-22 20:36:03.000000000 +0000 @@ -167,7 +167,7 @@ def test_html_uri_real(self): # test parsing of a meta-releaes file from the server with EnvironmentVarGuard() as environ: - environ["META_RELEASE_FAKE_CODENAME"] = "lucid" + environ["META_RELEASE_FAKE_CODENAME"] = "bionic" meta = MetaReleaseCore(forceDownload=True) while meta.downloading: time.sleep(0.1)