From 892deefdcaf597b0c573e4ea4d309c53245016d7 Mon Sep 17 00:00:00 2001 From: James Valleroy Date: Wed, 29 Jun 2022 11:11:08 -0400 Subject: [PATCH] upgrades: Hold packages one at a time `apt-mark hold PACKAGES` accepts a list of packages. But if one of the package is missing from the apt repository, then it will fail to hold any of the listed packages. So it is necessary to try to hold each package by itself. Test: - Run dist-upgrade from bullseye to bookworm. mumble-server package is currently missing from bookworm, but it should not cause an error in dist-upgrade. Signed-off-by: James Valleroy Reviewed-by: Sunil Mohan Adapa --- actions/upgrades | 4 ---- plinth/action_utils.py | 35 ++++++++++++++++++++++++++--------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/actions/upgrades b/actions/upgrades index 4d894782b..e9d4ca96a 100755 --- a/actions/upgrades +++ b/actions/upgrades @@ -522,10 +522,6 @@ def _perform_dist_upgrade(): print( 'Holding packages with conffile prompts: ' + ', '.join(DIST_UPGRADE_PACKAGES_WITH_PROMPTS) + '...', flush=True) - # XXX: If any of these packages have been removed from the - # next stable release, they will causes the hold command to - # fail for all packages. So it would be safer to just hold one - # package at a time. with apt_hold(DIST_UPGRADE_PACKAGES_WITH_PROMPTS): print('Running apt full-upgrade...', flush=True) run_apt_command(['full-upgrade']) diff --git a/plinth/action_utils.py b/plinth/action_utils.py index ccc71a0e6..86042f85a 100644 --- a/plinth/action_utils.py +++ b/plinth/action_utils.py @@ -424,16 +424,33 @@ def run_apt_command(arguments): @contextmanager -def apt_hold(packages, ignore_errors=False): - """Prevent packages from being removed during apt operations.""" - current_hold = subprocess.check_output(['apt-mark', 'showhold'] + packages) - try: - yield current_hold or subprocess.run(['apt-mark', 'hold'] + packages, - check=not ignore_errors) - finally: +def apt_hold(packages): + """Prevent packages from being removed during apt operations. + + `apt-mark hold PACKAGES` accepts a list of packages. But if one of + the package is missing from the apt repository, then it will fail + to hold any of the listed packages. So it is necessary to try to + hold each package by itself. + + Packages held by this context will be unheld when leaving the + context. But if a package was already held beforehand, it will be + ignored (and not unheld). + + """ + held_packages = [] + for package in packages: + current_hold = subprocess.check_output( + ['apt-mark', 'showhold', package]) if not current_hold: - subprocess.run(['apt-mark', 'unhold'] + packages, - check=not ignore_errors) + process = subprocess.run(['apt-mark', 'hold', package], + check=False) + if process.returncode == 0: # success + held_packages.append(package) + + yield held_packages + + for package in held_packages: + subprocess.check_call(['apt-mark', 'unhold', package]) @contextmanager