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 <jvalleroy@mailbox.org>
Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
This commit is contained in:
James Valleroy 2022-06-29 11:11:08 -04:00
parent c70006d03a
commit 892deefdca
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
2 changed files with 26 additions and 13 deletions

View File

@ -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'])

View File

@ -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