ubuntu-release-upgrade: rework DistUpgradeQuirks for different Trisquel flavors.

This commit is contained in:
Luis Guzmán 2025-10-11 01:34:12 -06:00
parent 715877a0b0
commit a12c2b558e
8 changed files with 278 additions and 33 deletions

View file

@ -10,40 +10,56 @@ index f7427ac2..51f85b32 100644
self._protect_essential_gui()
self._maybe_remove_gpg_wks_server()
self._install_t64_replacement_packages()
@@ -205,6 +206,37 @@ class DistUpgradeQuirks(object):
@@ -205,6 +206,53 @@ class DistUpgradeQuirks(object):
self._disable_cloud_init()
# helpers
+ def _t64_transition_helper(self):
+ """
+ Provides a minimal t64 transition focused on GLib and Qt5Core.
+ Marks the t64 variants for installation if the older packages are
+ installed. Idempotent and conservative for initial testing.
+ Help the resolver by preferring t64 variants for commonly-blocking libs.
+ Safe to run multiple times.
+ """
+ cache = self.controller.cache
+ log = logging.getLogger("DistUpgrade")
+
+ # Run only if at least one of the target t64 packages is in the cache.
+ if "libglib2.0-0t64" not in cache and "libqt5core5t64" not in cache:
+ log.debug("t64-helper: no target t64 packages in cache; skipping")
+ cache = getattr(self.controller, "cache", None)
+ if cache is None:
+ return
+ log = logging.getLogger("DistUpgrade")
+
+ pairs = [
+ ("libglib2.0-0", "libglib2.0-0t64"),
+ ("libqt5core5a", "libqt5core5t64"),
+ ("libelf1", "libelf1t64"),
+ ("libssl3", "libssl3t64"),
+ ("libpsl5", "libpsl5t64"),
+ ("libdb5.3", "libdb5.3t64"),
+ ("libgdbm6", "libgdbm6t64"),
+ ("libpng16-16", "libpng16-16t64"),
+ ("libtevent0", "libtevent0t64"),
+ ("libmtdev1", "libmtdev1t64"),
+ ("libts0", "libts0t64"),
+ ("libtirpc3", "libtirpc3t64"),
+ ("libqt6core6", "libqt6core6t64"),
+ ("libqt6dbus6", "libqt6dbus6t64"),
+ ("libparted2", "libparted2t64"),
+ ]
+
+ # Run only if at least one of the target t64 packages is in the cache.
+ if not any(new in cache for _, new in pairs):
+ log.debug("t64-helper: no target t64 packages in cache; skipping")
+ return
+
+ touched = []
+ for old, new in pairs:
+ try:
+ if old in cache and cache[old].is_installed and new in cache:
+ cache[new].mark_install(True, True, False)
+ touched.append(f"{old}->{new}")
+ except Exception as e:
+ log.warning("t64-helper: error marking %s->%s: %s", old, new, e)
+ for old, newpkg in pairs:
+ if old in cache and newpkg in cache:
+ try:
+ if getattr(cache[old], "is_installed", False) or getattr(cache[old], "marked_install", False):
+ cache[newpkg].mark_install(from_user=True, auto_fix=True)
+ touched.append(newpkg)
+ except Exception as e:
+ log.debug("t64-helper: mark_install(%s) failed: %s", newpkg, e)
+
+ if touched:
+ log.info("t64-helper: touched=%s", ", ".join(touched))
+ log.info("t64-helper: forced %s", ", ".join(touched))
+
+
def _is_lxde_system(self):
"""Return True if LXDE (trisquel-mini) is detected as installed."""

View file

@ -2,15 +2,25 @@ diff --git a/DistUpgrade/DistUpgradeQuirks.py b/DistUpgrade/DistUpgradeQuirks.py
index 243efc14..a63db6bb 100644
--- a/DistUpgrade/DistUpgradeQuirks.py
+++ b/DistUpgrade/DistUpgradeQuirks.py
@@ -196,6 +196,8 @@ class DistUpgradeQuirks(object):
self._install_t64_replacement_packages()
self._install_pipewire_audio_on_ubuntu_studio()
self._handle_ufw_breaks()
@@ -189,6 +189,18 @@ class DistUpgradeQuirks(object):
def PreDistUpgradeCache(self):
""" run right before calculating the dist-upgrade """
logging.debug("running Quirks.PreDistUpgradeCache")
+
+ cache = self._get_cache()
+ self.__resolver = None
+ # Ensure recommends are ON for desktops, avoids resolver dead-ends
+ try:
+ self.ensure_recommends_are_installed_on_desktops()
+ except Exception as e:
+ logging.debug("ensure_recommends skipped: %s", e)
+
+ # Prefer Ecne-style candidates before marking transitions
+ self._prefer_ecne_suffix(cache)
# individual quirks handler that run *after* the dist-upgrade was
# calculated in the cache
+
# self._install_python_is_python2()
self._t64_transition_helper()
self._protect_essential_gui()
@@ -206,6 +208,88 @@ class DistUpgradeQuirks(object):
self._disable_cloud_init()

View file

@ -41,7 +41,7 @@ index a63db6bb..c91dff31 100644
# run right before the first packages get installed
def StartUpgrade(self):
@@ -186,13 +191,24 @@ class DistUpgradeQuirks(object):
@@ -193,6 +193,15 @@ class DistUpgradeQuirks(object):
# individual quirks handler that run *right before* the dist-upgrade
# is calculated in the cache
@ -57,6 +57,9 @@ index a63db6bb..c91dff31 100644
def PreDistUpgradeCache(self):
""" run right before calculating the dist-upgrade """
logging.debug("running Quirks.PreDistUpgradeCache")
@@ -209,9 +218,11 @@ class DistUpgradeQuirks(object):
self._prefer_ecne_suffix(cache)
# self._install_python_is_python2()
+ self._wks_purge_from_cache()
+ self._mta_cancel_selection()

View file

@ -0,0 +1,49 @@
diff --git a/DistUpgrade/DistUpgradeQuirks.py b/DistUpgrade/DistUpgradeQuirks.py
index f1337e12..9250dd3a 100644
--- a/DistUpgrade/DistUpgradeQuirks.py
+++ b/DistUpgrade/DistUpgradeQuirks.py
@@ -209,6 +209,7 @@ class DistUpgradeQuirks(object):
self._wks_purge_from_cache()
self._mta_cancel_selection()
self._t64_transition_helper()
+ self._ensure_trisquel_meta_selected()
self._protect_essential_gui()
# self._maybe_remove_gpg_wks_server()
self._install_t64_replacement_packages()
@@ -476,6 +477,36 @@ class DistUpgradeQuirks(object):
else:
logging.debug("prefer-ecne-suffix: no candidates changed")
+ def _ensure_trisquel_meta_selected(self):
+ """
+ Ensure base Trisquel meta-packages are explicitly selected for installation/upgrade.
+ This nudges APT's resolver to plan the transition (e.g., PipeWire replacing PulseAudio)
+ in the same run, instead of deferring meta-packages as "Not Upgrading".
+ """
+ # Get the apt cache from the controller (attribute name varies across releases)
+ cache = getattr(self.controller, "cache", None) or getattr(self.controller, "_cache", None)
+ if cache is None:
+ # Best effort: nothing to do if cache is not yet attached here
+ return
+
+ # Root meta-packages by flavour plus the common desktop pivot
+ roots = [
+ "trisquel", "triskel", "trisquel-mini", "trisquel-gnome", "trisquel-sugar",
+ "trisquel-desktop-common",
+ ]
+ for name in roots:
+ try:
+ pkg = cache[name]
+ except KeyError:
+ continue # package not present in this flavour
+ if getattr(pkg, "is_installed", False):
+ try:
+ # Mark as user-requested so the resolver upgrades it with its peers
+ pkg.mark_install(from_user=True)
+ except Exception:
+ # Non-fatal: keep going
+ pass
+
def _t64_transition_helper(self):
"""
Provides a minimal t64 transition focused on GLib and Qt5Core.

View file

@ -0,0 +1,64 @@
diff --git a/DistUpgrade/DistUpgradeQuirks.py b/DistUpgrade/DistUpgradeQuirks.py
index 35ed55bd..b70a8899 100644
--- a/DistUpgrade/DistUpgradeQuirks.py
+++ b/DistUpgrade/DistUpgradeQuirks.py
@@ -209,6 +209,7 @@ class DistUpgradeQuirks(object):
self._wks_purge_from_cache()
self._mta_cancel_selection()
self._t64_transition_helper()
+ self._ensure_pipewire_stack()
self._ensure_trisquel_meta_selected()
self._protect_essential_gui()
# self._maybe_remove_gpg_wks_server()
@@ -605,6 +606,51 @@ class DistUpgradeQuirks(object):
except Exception:
pass
+ def _ensure_pipewire_stack(self):
+ """
+ Nudge APT to pick the PipeWire stack so desktop metas can upgrade.
+ Also remove PulseAudio family to resolve Conflicts/Breaks with pipewire-audio.
+ """
+ cache = getattr(self.controller, "cache", None)
+ if cache is None:
+ return
+ log = logging.getLogger("DistUpgrade")
+
+ touched_i, touched_d = [], []
+
+ # Full PipeWire stack
+ for n in ("pipewire-audio", "pipewire-alsa", "pipewire-pulse", "gstreamer1.0-pipewire", "wireplumber"):
+ if self._apt_mark_install(n, protect=True): touched_i.append(n)
+
+ # Remove PulseAudio family to avoid conflicts
+ for n in ("pulseaudio", "pulseaudio-module-bluetooth", "pulseaudio-module-gsettings", "pulseaudio-module-jack", "pulseaudio-utils"):
+ if self._apt_mark_delete(n): touched_d.append(n)
+
+ if touched_i or touched_d:
+ log.info("pipewire-helper: install=%s remove=%s",
+ ", ".join(touched_i) if touched_i else "-",
+ ", ".join(touched_d) if touched_d else "-")
+
+
+ def mark(name):
+ if name in cache and getattr(cache[name], "candidate", None):
+ try:
+ cache[name].mark_install(from_user=True)
+ return True
+ except Exception:
+ pass
+ return False
+
+ touched = []
+ for n in ("pipewire-alsa", "pipewire-audio", "gstreamer1.0-pipewire"):
+ if mark(n):
+ touched.append(n)
+
+ if touched:
+ logging.getLogger("DistUpgrade").info(
+ "pipewire-helper: preselected %s", ", ".join(touched)
+ )
+
def _get_pci_ids(self):
""" return a set of pci ids of the system (using lspci -n) """
lspci = set()

View file

@ -0,0 +1,57 @@
diff --git a/DistUpgrade/DistUpgradeQuirks.py b/DistUpgrade/DistUpgradeQuirks.py
index 484ceaae..c390003a 100644
--- a/DistUpgrade/DistUpgradeQuirks.py
+++ b/DistUpgrade/DistUpgradeQuirks.py
@@ -209,6 +209,7 @@ class DistUpgradeQuirks(object):
self._wks_purge_from_cache()
self._mta_cancel_selection()
self._t64_transition_helper()
+ self._parted_transition_helper()
self._ensure_pipewire_stack()
self._ensure_trisquel_meta_selected()
self._protect_essential_gui()
@@ -618,6 +624,44 @@ class DistUpgradeQuirks(object):
except Exception:
pass
+
+ def _parted_transition_helper(self):
+ """
+ Handle libparted/udisks2 transition seen in logs:
+ - Remove libparted-fs-resize0 (obsolete transitional)
+ - Prefer libparted2t64 when available, else libparted2
+ - Protect libblockdev-fs2 and udisks2 from accidental removal
+ """
+ cache = getattr(self.controller, "cache", None)
+ if cache is None:
+ return
+ log = logging.getLogger("DistUpgrade")
+
+ touched_i, touched_d = [], []
+
+ if self._apt_mark_delete("libparted-fs-resize0"):
+ touched_d.append("libparted-fs-resize0")
+
+ # Prefer t64 if present
+ if "libparted2t64" in cache:
+ if self._apt_mark_install("libparted2t64"):
+ touched_i.append("libparted2t64")
+ else:
+ if self._apt_mark_install("libparted2"):
+ touched_i.append("libparted2")
+
+ # Keep storage stack in place
+ for keep in ("libblockdev-fs2", "udisks2"):
+ try:
+ if keep in cache and (getattr(cache[keep], "is_installed", False) or getattr(cache[keep], "marked_install", False)):
+ if self._resolver(): self._resolver().protect(cache[keep])
+ except Exception: pass
+
+ if touched_i or touched_d:
+ log.info("parted-helper: install=%s remove=%s",
+ ", ".join(touched_i) if touched_i else "-",
+ ", ".join(touched_d) if touched_d else "-")
+
def _ensure_pipewire_stack(self):
"""
Nudge APT to pick the PipeWire stack so desktop metas can upgrade.

View file

@ -0,0 +1,46 @@
diff --git a/DistUpgrade/DistUpgradeQuirks.py b/DistUpgrade/DistUpgradeQuirks.py
index 4c599207..2c3fd680 100644
--- a/DistUpgrade/DistUpgradeQuirks.py
+++ b/DistUpgrade/DistUpgradeQuirks.py
@@ -202,6 +202,41 @@ class DistUpgradeQuirks(object):
# --- MTA hard-block config (postfix only, temporary during upgrade) ---
MTA_BLOCK_PREF = "/etc/apt/preferences.d/zz-urug-block-mta-postfix.pref"
+ def _resolver(self):
+ # use only one ProblemResolver per stage
+ if not hasattr(self, "__resolver") or self.__resolver is None:
+ try:
+ import apt
+ self.__resolver = apt.ProblemResolver(self._get_cache())
+ except Exception:
+ self.__resolver = None
+ return self.__resolver
+
+ def _apt_mark_install(self, name, protect=False, auto_fix=True, from_user=True):
+ cache = self._get_cache()
+ if cache and name in cache and getattr(cache[name], "candidate", None):
+ try:
+ cache[name].mark_install(from_user=from_user, auto_fix=auto_fix)
+ if protect and self._resolver():
+ try: self._resolver().protect(cache[name])
+ except Exception: pass
+ return True
+ except Exception:
+ return False
+ return False
+
+ def _apt_mark_delete(self, name, purge=False):
+ cache = self._get_cache()
+ if cache and name in cache:
+ pkg = cache[name]
+ if getattr(pkg, "is_installed", False) or getattr(pkg, "marked_install", False):
+ try:
+ pkg.mark_delete(auto_fix=False)
+ return True
+ except Exception:
+ return False
+ return False
+
def PreDistUpgradeCache(self):
""" run right before calculating the dist-upgrade """
logging.debug("running Quirks.PreDistUpgradeCache")

View file

@ -25,7 +25,7 @@
# Also, don't forget to update the meta-release files at archive and packages.t.i
# The "obsoletes" list from ubuntu has been removed
VERSION=19
VERSION=20
. ./config
# Previous upstream release name, update for each release.
@ -213,12 +213,12 @@ perl $DATA/parsewiki DistUpgrade/DevelReleaseAnnouncement > DistUpgrade/DevelRel
cat <<EOF > data/removal_denylist.cfg
# list of packages that should never be removed
trisquel-base
trisquel-minimal
trisquel-desktop-common
trisquel-mini
triskel
trisquel-sugar
^trisquel-base$
^trisquel-minimal$
^trisquel-desktop-common$
^trisquel-mini$
^triskel$
^trisquel-sugar$
# update-manager should not remove itself
update-manager
update-manager-core