Source code for univention.appcenter.actions.update_check
#!/usr/bin/python3
#
# Univention App Center
# univention-app module checking UCS update regarding installed apps
#
# SPDX-FileCopyrightText: 2021-2025 Univention GmbH
# SPDX-License-Identifier: AGPL-3.0-only
import os
from argparse import Action
from tempfile import TemporaryDirectory
from univention.appcenter.actions import UniventionAppAction, get_action
from univention.appcenter.app_cache import AppCenterCache, Apps, default_server
from univention.appcenter.log import get_logfile_logger
from univention.appcenter.ucr import ucr_get
from univention.lib.ucs import UCS_Version
[docs]
class CheckUcsVersion(Action):
def __call__(self, parser, namespace, value, option_string=None):
try:
UCS_Version(value + '-0')
except ValueError as exc:
parser.error('--ucs-version ' + str(exc))
setattr(namespace, self.dest, value)
[docs]
class UpdateCheck(UniventionAppAction):
"""
Check if update to next ucs minor version is possible with the
locally installed apps
For docker apps check if is available in next UCS version.
For package based apps check if there is an app version with the same
component in the next UCS version
"""
help = 'Check for all locally installed Apps if they are available in the next UCS version'
[docs]
def setup_parser(self, parser):
parser.add_argument(
'--ucs-version',
action=CheckUcsVersion,
required=True,
help="the next ucs version (MAJOR.MINOR), check if update with locally installed apps is possible",
)
[docs]
@classmethod
def app_can_update(cls, app, next_version, next_apps):
"""
checks if update is possible for this app
docker apps have to support the next version
components must be available in the next version
component id of package based app must be available in the next version
"""
if app.docker:
# current docker must support next version
if next_version in app.supported_ucs_versions:
return True
else:
if app.without_repository:
# current component must be available in next version
for a in next_apps:
if a.id == app.id:
return True
else:
# current component id must be available in next version
for a in next_apps:
if a.component_id == app.component_id:
return True
return False
[docs]
@classmethod
def get_blocking_apps(cls, ucs_version):
"""checks if update is possible for this app"""
ucs_version = UCS_Version(ucs_version + '-0')
next_minor = '%(major)d.%(minor)d' % ucs_version
next_version = '%(major)d.%(minor)d-%(patchlevel)d' % ucs_version
current_version = UCS_Version(ucr_get('version/version') + '-0')
current_minor = '%(major)d.%(minor)d' % current_version
# if this is just a patchlevel update, everything is fine
if current_minor >= next_minor:
return {}
# first, update the local cache and get current apps
update = get_action('update')
update.logger = get_logfile_logger('update-check')
update.call()
current_cache = Apps(ucs_version=current_minor, locale='en')
# get apps in next version
with TemporaryDirectory() as tempdir:
update.call(ucs_version=next_minor, cache_dir=os.path.join(tempdir, next_minor), just_get_cache=True)
next_cache = AppCenterCache.build(ucs_versions=[next_minor], server=default_server(), locale='en', cache_dir=tempdir)
next_apps = next_cache.get_every_single_app()
# check apps
blocking_apps = {}
for app in current_cache.get_all_locally_installed_apps():
if not cls.app_can_update(app, next_version, next_apps):
cls.debug('app %s is not available for %s' % (app.id, next_version))
blocking_apps[app.component_id] = app.name
else:
cls.debug('app %s is available for %s' % (app.id, next_version))
return blocking_apps
[docs]
def main(self, args):
blocking_apps = self.get_blocking_apps(args.ucs_version)
if blocking_apps:
self.log('The update to %s is currently not possible,' % args.ucs_version)
self.log('because the following Apps are not available for UCS %s:' % args.ucs_version)
for app in blocking_apps.values():
self.log(' * %s' % app)
return 1