package: Add ability to reinstall a package

- Also add ability restore missing configuration files during reinstall.

- Reinstall is useful for restoring the original configuration files of the
package.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
Sunil Mohan Adapa 2020-09-09 19:27:29 -07:00 committed by James Valleroy
parent b9e47dcf0b
commit f59fc5e33b
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
3 changed files with 36 additions and 7 deletions

View File

@ -40,6 +40,12 @@ def parse_arguments():
subparser.add_argument(
'--force-configuration', choices=['new', 'old'],
help='force old/new configuration files during install')
subparser.add_argument(
'--reinstall', action='store_true',
help='force re-installation of package even if it is current')
subparser.add_argument(
'--force-missing-configuration', action='store_true',
help='force installation of missing configuration files')
subparser.add_argument(
'module', help='name of module for which package is being installed')
subparser.add_argument('packages', nargs='+',
@ -94,6 +100,12 @@ def subcommand_install(arguments):
elif arguments.force_configuration == 'new':
extra_arguments += ['-o', 'Dpkg::Options::=--force-confnew']
if arguments.reinstall:
extra_arguments.append('--reinstall')
if arguments.force_missing_configuration:
extra_arguments += ['-o', 'Dpkg::Options::=--force-confmiss']
subprocess.run(['dpkg', '--configure', '-a'])
with _apt_hold():
run_apt_command(['--fix-broken', 'install'])

View File

@ -17,6 +17,7 @@ logger = logging.getLogger(__name__)
class PackageException(Exception):
"""A package operation has failed."""
def __init__(self, error_string=None, error_details=None, *args, **kwargs):
"""Store apt-get error string and details."""
super(PackageException, self).__init__(*args, **kwargs)
@ -32,6 +33,7 @@ class PackageException(Exception):
class Transaction(object):
"""Information about an ongoing transaction."""
def __init__(self, module_name, package_names):
"""Initialize transaction object.
@ -52,13 +54,10 @@ class Transaction(object):
self.percentage = 0
self.stderr = None
def install(self, skip_recommends=False, force_configuration=None):
def install(self, skip_recommends=False, force_configuration=None,
reinstall=False, force_missing_configuration=False):
"""Run an apt-get transaction to install given packages.
FreedomBox Service (Plinth) needs to be running as root when calling
this. Currently, this is meant to be only during first time setup when
--setup is argument is passed.
If force_configuration is set to 'new', dpkg options will be enabled to
make it force overwrite (without prompts) new configuration in place of
old configuration (with a backup). This is useful when writing
@ -75,6 +74,13 @@ class Transaction(object):
If force_configuration is None, no special options are passed to
apt/dpkg for configuration file behavior.
If reinstall is True, packages will be reinstalled, even if they are
the latest version.
If force_missing_configuration is True, any configuration files that
have been removed after the first package has been installed will be
restored.
"""
try:
self._run_apt_command(['update'])
@ -86,6 +92,12 @@ class Transaction(object):
extra_arguments.append(
'--force-configuration={}'.format(force_configuration))
if reinstall:
extra_arguments.append('--reinstall')
if force_missing_configuration:
extra_arguments.append('--force-missing-configuration')
self._run_apt_command(['install'] + extra_arguments +
[self.module_name] + self.package_names)
except subprocess.CalledProcessError as exception:

View File

@ -29,6 +29,7 @@ _force_upgrader = None
class Helper(object):
"""Helper routines for modules to show progress."""
def __init__(self, module_name, module):
"""Initialize the object."""
self.module_name = module_name
@ -90,7 +91,8 @@ class Helper(object):
self.current_operation = None
def install(self, package_names, skip_recommends=False,
force_configuration=None):
force_configuration=None, reinstall=False,
force_missing_configuration=False):
"""Install a set of packages marking progress."""
if self.allow_install is False:
# Raise error if packages are not already installed.
@ -109,7 +111,8 @@ class Helper(object):
'step': 'install',
'transaction': transaction,
}
transaction.install(skip_recommends, force_configuration)
transaction.install(skip_recommends, force_configuration, reinstall,
force_missing_configuration)
def call(self, step, method, *args, **kwargs):
"""Call an arbitrary method during setup and note down its stage."""
@ -408,9 +411,11 @@ class ForceUpgrader():
class TemporaryFailure(Exception):
"""Raised when upgrade fails but can be tried again immediately."""
class PermanentFailure(Exception):
"""Raised when upgrade fails and there is nothing more we wish to do.
"""
def __init__(self):
"""Initialize the force upgrader."""
if plinth.cfg.develop: