[enh] Introduce new 'requirements' manifest key (close #113)

The 'requirements' key allows to specify the Debian packages which must
be installed and their required version. It must be an array of the
package name as the key and its version specifier - for its format, see
yunohost.utils.packages.Specifier - as value. For example:

"requirements": {
  "yunohost": ">= 2.4, << 2.5"
}
This commit is contained in:
Jérôme Lebleu 2016-03-08 22:58:47 +01:00
parent cc4dc54ed3
commit fba14e55df
3 changed files with 49 additions and 22 deletions

View file

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

View file

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

View file

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