diff --git a/locales/en.json b/locales/en.json index 6852e9c6a..4150f2761 100644 --- a/locales/en.json +++ b/locales/en.json @@ -20,7 +20,9 @@ "app_not_installed" : "{app:s} is not installed", "app_not_correctly_installed" : "{app:s} seems to be not correctly installed", "custom_app_url_required" : "You must provide an URL to upgrade your custom app {app:s}", - "app_recent_version_required" : "{app:s} requires a more recent version of YunoHost", + "app_requirements_checking" : "Checking required packages...", + "app_requirements_unmeet" : "Requirements are not met, the package {pkgname} ({version}) must be {spec}", + "app_requirements_failed" : "Unable to meet requirements: {err}", "app_upgraded" : "{app:s} successfully upgraded", "app_upgrade_failed" : "Unable to upgrade {app:s}", "app_id_invalid" : "Invalid app id", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 6d688009f..c5ea1bdec 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -40,7 +40,7 @@ from moulinette.core import MoulinetteError from moulinette.utils.log import getActionLogger from yunohost.service import service_log -from yunohost.utils.packages import meets_version_specifier +from yunohost.utils import packages logger = getActionLogger('yunohost.app') @@ -358,13 +358,8 @@ def app_upgrade(auth, app=[], url=None, file=None): else: continue - # Check min version - if 'min_version' in manifest \ - and not meets_version_specifier( - 'yunohost', '>> {0}'.format(manifest['min_version'])): - raise MoulinetteError(errno.EPERM, - m18n.n('app_recent_version_required', - app=app_id)) + # Check requirements + _check_manifest_requirements(manifest) app_setting_path = apps_setting_path +'/'+ app_id @@ -451,13 +446,8 @@ def app_install(auth, app, label=None, args=None): app_id = manifest['id'] - # Check min version - if 'min_version' in manifest \ - and not meets_version_specifier( - 'yunohost', '>> {0}'.format(manifest['min_version'])): - raise MoulinetteError(errno.EPERM, - m18n.n('app_recent_version_required', - app=app_id)) + # Check requirements + _check_manifest_requirements(manifest) # Check if app can be forked instance_number = _installed_instance_number(app_id, last=True) + 1 @@ -1371,6 +1361,36 @@ def _encode_string(value): return value +def _check_manifest_requirements(manifest): + """Check if required packages are met from the manifest""" + requirements = manifest.get('requirements', dict()) + # FIXME: Deprecate min_version key + if 'min_version' in manifest: + requirements['yunohost'] = '>> {0}'.format(manifest['min_version']) + logger.debug("the manifest key 'min_version' is deprecated, " + "use 'requirements' instead.") + if not requirements: + return + + logger.info(m18n.n('app_requirements_checking')) + + # Retrieve versions of each required package + try: + versions = packages.get_installed_version( + *requirements.keys(), strict=True, as_dict=True) + except packages.PackageException as e: + raise MoulinetteError(errno.EINVAL, + m18n.n('app_requirements_failed', err=str(e))) + + # Iterate over requirements + for pkgname, spec in requirements.items(): + version = versions[pkgname] + if version not in packages.SpecifierSet(spec): + raise MoulinetteError( + errno.EINVAL, m18n.n('app_requirements_unmeet', + pkgname=pkgname, version=version, + spec=spec)) + def _parse_args_from_manifest(manifest, action, args={}, auth=None): """Parse arguments needed for an action from the manifest diff --git a/src/yunohost/utils/packages.py b/src/yunohost/utils/packages.py index a2c447dc3..edade553f 100644 --- a/src/yunohost/utils/packages.py +++ b/src/yunohost/utils/packages.py @@ -250,14 +250,18 @@ def get_installed_version(*pkgnames, **kwargs): """Get the installed version of package(s) Retrieve one or more packages named `pkgnames` and return their installed - version as a dict or as a string if only one is requested. If `strict` is - `True`, an exception will be raised if a package is unknown or not - installed. + version as a dict or as a string if only one is requested and `as_dict` is + `False`. If `strict` is `True`, an exception will be raised if a package + is unknown or not installed. """ - cache = apt.Cache() - strict = kwargs.get('strict', False) versions = OrderedDict() + cache = apt.Cache() + + # Retrieve options + as_dict = kwargs.get('as_dict', False) + strict = kwargs.get('strict', False) + for pkgname in pkgnames: try: pkg = cache[pkgname] @@ -272,7 +276,8 @@ def get_installed_version(*pkgnames, **kwargs): raise UninstalledPackage(pkgname) version = None versions[pkgname] = version - if len(pkgnames) == 1: + + if len(pkgnames) == 1 and not as_dict: return versions[pkgnames[0]] return versions