manifest: support minimum required west version
As we evolve the manifest data, we want to be able to enforce a minimum version of west required to parse it. Add an optional 'version' key to the data to make this possible. We have to validate this before checking the manifest against the schema, as later versions of west will likely extend the schema in ways that would raise errors if we used our schema to check the data. We allow the version to be parsed as a YAML float (like 1.0) and convert it to a string as a convenience for the user. Signed-off-by: Marti Bolivar <marti.bolivar@nordicsemi.no>
This commit is contained in:
parent
428ef11b38
commit
18857356bd
1
setup.py
1
setup.py
|
@ -42,6 +42,7 @@ setuptools.setup(
|
|||
'pykwalify',
|
||||
'configobj',
|
||||
'setuptools>=v40.1.0', # for find_namespace_packages
|
||||
'packaging',
|
||||
],
|
||||
python_requires='>=3.4',
|
||||
entry_points={'console_scripts': ('west = west.main:main',)},
|
||||
|
|
|
@ -17,6 +17,20 @@
|
|||
|
||||
type: map
|
||||
mapping:
|
||||
# The "version" key is optional, and specifies a minimum version of
|
||||
# west (in semantic versioning, so X.Y <= X.Y.0 < X.Y.1 < (X+1).Y,
|
||||
# etc.) that is required to correctly parse the manifest data.
|
||||
#
|
||||
# This is supported starting with west v0.7 (i.e. it was added after
|
||||
# 0.6 was released). Earlier versions of west will treat manifest
|
||||
# data with a version key as malformed, so it can reliably be used
|
||||
# to prevent parsing on west v0.5 and v0.6 as well, though the error
|
||||
# messages users will receive will be a bit more confusing in that
|
||||
# case.
|
||||
version:
|
||||
required: false
|
||||
type: text
|
||||
|
||||
# The "defaults" key specifies some default values used in the
|
||||
# rest of the manifest.
|
||||
defaults:
|
||||
|
|
|
@ -26,12 +26,14 @@ import shutil
|
|||
import shlex
|
||||
import subprocess
|
||||
|
||||
from packaging.version import parse as parse_version
|
||||
import pykwalify.core
|
||||
import yaml
|
||||
|
||||
from west import util, log
|
||||
from west.backports import CompletedProcess
|
||||
import west.configuration as cfg
|
||||
from west.version import __version__ as west_version
|
||||
|
||||
|
||||
#: Index in projects where the project with contains project manifest file is
|
||||
|
@ -231,6 +233,24 @@ class Manifest:
|
|||
self._malformed('manifest contains no manifest element')
|
||||
data = self._data['manifest']
|
||||
|
||||
# Make sure this version of west can load this manifest data.
|
||||
# This has to happen before the schema check -- later schemas
|
||||
# are likely to incompatibly extend our own.
|
||||
if 'version' in data:
|
||||
min_version = data['version']
|
||||
# As a convenience for the user, convert floats to strings.
|
||||
# This avoids forcing them to write:
|
||||
#
|
||||
# version: "1.0"
|
||||
#
|
||||
# by explicitly allowing:
|
||||
#
|
||||
# version: 1.0
|
||||
if isinstance(min_version, float):
|
||||
min_version = str(min_version)
|
||||
if parse_version(min_version) > _WEST_VERSION:
|
||||
raise ManifestVersionError(min_version, file=source_file)
|
||||
|
||||
try:
|
||||
pykwalify.core.Core(source_data=data,
|
||||
schema_files=[_SCHEMA_PATH]).validate()
|
||||
|
@ -511,6 +531,17 @@ class MalformedConfig(Exception):
|
|||
'''Exception indicating that west config is malformed and thus causing west
|
||||
manifest parsing to fail.'''
|
||||
|
||||
class ManifestVersionError(Exception):
|
||||
'''The manifest required a version of west more recent than the
|
||||
current running version.
|
||||
'''
|
||||
|
||||
def __init__(self, version, file=None):
|
||||
self.version = version
|
||||
'''The minimum version of west that was required.'''
|
||||
|
||||
self.file = file
|
||||
'''The file that required this version of west.'''
|
||||
|
||||
# Definitions for Manifest attribute types.
|
||||
|
||||
|
@ -949,7 +980,7 @@ class ManifestProject(Project):
|
|||
|
||||
_SCHEMA_PATH = os.path.join(os.path.dirname(__file__), "manifest-schema.yml")
|
||||
_DEFAULTS = Defaults()
|
||||
|
||||
_WEST_VERSION = parse_version(west_version)
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def _warn_once_if_no_git():
|
||||
|
|
|
@ -11,7 +11,8 @@ import pytest
|
|||
import yaml
|
||||
|
||||
from west.manifest import Manifest, Defaults, Remote, Project, \
|
||||
ManifestProject, MalformedManifest, manifest_path
|
||||
ManifestProject, MalformedManifest, ManifestVersionError, \
|
||||
manifest_path
|
||||
|
||||
THIS_DIRECTORY = os.path.dirname(__file__)
|
||||
|
||||
|
@ -768,6 +769,64 @@ def test_load_str():
|
|||
''')
|
||||
assert manifest.projects[-1].name == 'foo'
|
||||
|
||||
def test_version_check_failure():
|
||||
# Check that the manifest.version key causes manifest parsing to
|
||||
# fail when it should.
|
||||
|
||||
valid_fmt = '''\
|
||||
manifest:
|
||||
version: {}
|
||||
projects:
|
||||
- name: foo
|
||||
url: https://foo.com
|
||||
'''
|
||||
invalid_fmt = '''\
|
||||
manifest:
|
||||
version: {}
|
||||
projects:
|
||||
- name: foo
|
||||
url: https://foo.com
|
||||
pytest-invalid-key: a-value
|
||||
'''
|
||||
|
||||
# Parsing a well-formed manifest for a version of west greater
|
||||
# than our own should raise ManifestVersionError.
|
||||
#
|
||||
# This should be the case whether the version is a string (as is
|
||||
# usual) or, as a special case to work around YAML syntax rules, a
|
||||
# float.
|
||||
with pytest.raises(ManifestVersionError):
|
||||
Manifest.from_data(valid_fmt.format('"99.0"'))
|
||||
with pytest.raises(ManifestVersionError):
|
||||
Manifest.from_data(valid_fmt.format('99.0'))
|
||||
|
||||
# Parsing Manifests with unsatisfiable version requirements should
|
||||
# *not* raise MalformedManifest, even if they have unrecognized keys.
|
||||
with pytest.raises(ManifestVersionError):
|
||||
Manifest.from_data(invalid_fmt.format('"99.0"'))
|
||||
with pytest.raises(ManifestVersionError):
|
||||
Manifest.from_data(invalid_fmt.format('99.0'))
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'ver', ['0.6', '0.6.2', '0.6.0.dev1', '0.6.0rc1', '0.6.99'])
|
||||
def test_version_check_success(ver):
|
||||
# Test that version checking succeeds when it should.
|
||||
#
|
||||
# Parsing a well-formed manifest for a version of west no greater
|
||||
# than this one, including RC and dev versions used for
|
||||
# pre-releases, should not raise this error.
|
||||
|
||||
fmt = '''\
|
||||
manifest:
|
||||
version: {}
|
||||
projects:
|
||||
- name: foo
|
||||
url: https://foo.com
|
||||
'''
|
||||
manifest = Manifest.from_data(fmt.format(ver))
|
||||
assert manifest.projects[-1].name == 'foo'
|
||||
manifest = Manifest.from_data(fmt.format('"' + ver + '"'))
|
||||
assert manifest.projects[-1].name == 'foo'
|
||||
|
||||
# Invalid manifests should raise MalformedManifest.
|
||||
@pytest.mark.parametrize('invalid',
|
||||
|
|
Loading…
Reference in New Issue