mirror of
https://github.com/ansible/mazer.git
synced 2026-02-05 12:45:17 +01:00
Validate collection metadata (#147)
* Validate collection metadata * Fixes tests * Bring galaxy.yml and manifest.json in line with galaxy pull #1173 * Fix codacy complaints * Ignore codacy checks on tests/
This commit is contained in:
committed by
GitHub
parent
2e2b9ebd4e
commit
ef82d16a08
2
.codacy.yml
Normal file
2
.codacy.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
exclude_paths:
|
||||
- tests/**
|
||||
@@ -6,6 +6,7 @@ import yaml
|
||||
|
||||
from ansible_galaxy.models.collection_info import \
|
||||
CollectionInfo
|
||||
from ansible_galaxy import exceptions
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
pf = pprint.pformat
|
||||
@@ -24,9 +25,12 @@ def load(data_or_file_object, klass=None):
|
||||
|
||||
log.debug('data: %s', pf(data_dict))
|
||||
|
||||
klass = CollectionInfo
|
||||
collection_info = CollectionInfo(**data_dict)
|
||||
collection_info = klass(**data_dict)
|
||||
try:
|
||||
collection_info = CollectionInfo(**data_dict)
|
||||
except ValueError:
|
||||
raise
|
||||
except Exception as exc:
|
||||
raise exceptions.GalaxyClientError("Error parsing collection metadata: %s" % str(exc))
|
||||
|
||||
log.debug('artifact_manifest from_kwargs: %s', collection_info)
|
||||
|
||||
|
||||
4730
ansible_galaxy/data/spdx_licenses.json
Normal file
4730
ansible_galaxy/data/spdx_licenses.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -15,5 +15,4 @@ class CollectionArtifactFile(object):
|
||||
src_name = attr.ib(default=None)
|
||||
chksum_type = attr.ib(default="sha256")
|
||||
chksum_sha256 = attr.ib(default=None)
|
||||
# name = attr.ib(default=None)
|
||||
format_version = attr.ib(default=0.0)
|
||||
_format = attr.ib(default=1)
|
||||
|
||||
@@ -31,7 +31,7 @@ def convert_list_to_artifact_file_list(val):
|
||||
@attr.s(frozen=True)
|
||||
class CollectionArtifactManifest(object):
|
||||
collection_info = attr.ib(type=CollectionInfo)
|
||||
format_version = attr.ib(default=0.0)
|
||||
format = attr.ib(default=1)
|
||||
|
||||
files = attr.ib(factory=list, converter=convert_list_to_artifact_file_list)
|
||||
|
||||
|
||||
@@ -1,17 +1,84 @@
|
||||
|
||||
import logging
|
||||
from __future__ import print_function
|
||||
|
||||
import attr
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import semver
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
TAG_REGEXP = re.compile('^[a-z0-9]+$')
|
||||
|
||||
# see https://github.com/ansible/galaxy/issues/957
|
||||
|
||||
|
||||
@attr.s(frozen=True)
|
||||
class CollectionInfo(object):
|
||||
namespace = attr.ib()
|
||||
name = attr.ib()
|
||||
version = attr.ib()
|
||||
format_version = attr.ib(default=0.0)
|
||||
author = attr.ib(default=None)
|
||||
name = attr.ib(default=None)
|
||||
version = attr.ib(default=None)
|
||||
authors = attr.ib(default=[])
|
||||
license = attr.ib(default=None)
|
||||
description = attr.ib(default=None)
|
||||
keywords = attr.ib(default=[])
|
||||
readme = attr.ib(default='README.md')
|
||||
dependencies = attr.ib(default=[])
|
||||
|
||||
@property
|
||||
def namespace(self):
|
||||
return self.name.split('.', 1)[0]
|
||||
|
||||
@staticmethod
|
||||
def value_error(msg):
|
||||
raise ValueError("Invalid collection metadata. %s" % msg)
|
||||
|
||||
@name.validator
|
||||
@version.validator
|
||||
@license.validator
|
||||
@description.validator
|
||||
def _check_required(self, attribute, value):
|
||||
if value is None:
|
||||
self.value_error("'%s' is required" % attribute.name)
|
||||
|
||||
@version.validator
|
||||
def _check_version_format(self, attribute, value):
|
||||
try:
|
||||
semver.parse_version_info(value)
|
||||
except ValueError:
|
||||
self.value_error("Expecting 'version' to be in semantic version format, "
|
||||
"instead found '%s'." % value)
|
||||
|
||||
@license.validator
|
||||
def _check_license(self, attribute, value):
|
||||
cwd = os.path.dirname(os.path.abspath(__file__))
|
||||
license_path = os.path.join(cwd, '..', 'data', 'spdx_licenses.json')
|
||||
license_data = json.load(open(license_path, 'r'))
|
||||
for lic in license_data['licenses']:
|
||||
if lic['licenseId'] == value:
|
||||
if lic['isDeprecatedLicenseId']:
|
||||
print("Warning: collection metadata 'license' value '%s' is "
|
||||
"deprecated." % value)
|
||||
return True
|
||||
self.value_error("Expecting 'license' to be a valid SPDX license ID, instead found '%s'. "
|
||||
"For more info, visit https://spdx.org" % value)
|
||||
|
||||
@authors.validator
|
||||
@keywords.validator
|
||||
@dependencies.validator
|
||||
def _check_list_type(self, attribute, value):
|
||||
if not isinstance(value, list):
|
||||
self.value_error("Expecting '%s' to be a list" % attribute.name)
|
||||
|
||||
@keywords.validator
|
||||
def _check_keywords(self, attribute, value):
|
||||
for k in value:
|
||||
if not re.match(TAG_REGEXP, k):
|
||||
self.value_error("Expecting keywords to contain alphanumeric characters only, "
|
||||
"instead found '%s'." % k)
|
||||
|
||||
@name.validator
|
||||
def _check_name(self, attribute, value):
|
||||
if len(value.split('.', 1)) != 2:
|
||||
self.value_error("Expecting 'name' to be in Galaxy name format, <namespace>.<collection_name>, "
|
||||
"instead found '%s'." % value)
|
||||
|
||||
@@ -2,4 +2,4 @@ namespace: "greetings_namespace"
|
||||
name: "greetings"
|
||||
version: "11.11.11"
|
||||
author: "Cowboy King Buzzo Lightyear"
|
||||
license: "GPLv2"
|
||||
license: "GPL-3.0-or-later"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace: "greetings_namespace"
|
||||
name: "hello"
|
||||
name: "greetings_namespace.hello"
|
||||
version: "11.11.11"
|
||||
author: "Cowboy King Buzzo Lightyear"
|
||||
license: "GPLv2"
|
||||
description: "hello world"
|
||||
authors: ["Cowboy King Buzzo Lightyear"]
|
||||
license: "GPL-3.0-or-later"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace: "greeting_namespace"
|
||||
name: "howdy"
|
||||
name: "howdy.greeting_namespace"
|
||||
version: "12.11.11"
|
||||
author: "Cowboy King Buzzo Lightyear"
|
||||
license: "GPLv2"
|
||||
description: "howdy thing"
|
||||
authors: ["Cowboy King Buzzo Lightyear"]
|
||||
license: "GPL-3.0-or-later"
|
||||
|
||||
@@ -2,10 +2,9 @@ collection_info:
|
||||
namespace: "some_namespace"
|
||||
name: "some_name"
|
||||
version: "1.2.3"
|
||||
format_version: 0.0
|
||||
author: "Javale McGee"
|
||||
license: "GPLv2"
|
||||
format_version: 0.0
|
||||
authors: ["Javale McGee"]
|
||||
license: "GPL-3.0-or-later"
|
||||
format: 1
|
||||
files:
|
||||
- name: "roles/some_role/defaults/main.yml"
|
||||
chksum_type: "sha256"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace: "some_namespace"
|
||||
name: "some_name"
|
||||
name: "some_namespace.some_name"
|
||||
version: "11.11.11"
|
||||
author: "Carlos Boozer"
|
||||
license: "GPLv2"
|
||||
description: "something"
|
||||
authors: ["Carlos Boozer"]
|
||||
license: "GPL-3.0-or-later"
|
||||
|
||||
82
tests/ansible_galaxy/models/test_collection_info_model.py
Normal file
82
tests/ansible_galaxy/models/test_collection_info_model.py
Normal file
@@ -0,0 +1,82 @@
|
||||
import logging
|
||||
import pytest
|
||||
|
||||
from ansible_galaxy.models.collection_info import CollectionInfo
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def test_license_error():
|
||||
test_data = {
|
||||
'name': 'foo.foo',
|
||||
'authors': ['chouseknecht'],
|
||||
'license': 'GPLv2',
|
||||
'version': '0.0.1',
|
||||
'description': 'unit testing thing',
|
||||
}
|
||||
with pytest.raises(ValueError) as exc:
|
||||
CollectionInfo(**test_data)
|
||||
assert 'license' in str(exc)
|
||||
|
||||
|
||||
def test_required_error():
|
||||
test_data = {
|
||||
'authors': ['chouseknecht'],
|
||||
'license': 'GPL-3.0-or-later',
|
||||
'version': '0.0.1',
|
||||
'description': 'unit testing thing'
|
||||
}
|
||||
with pytest.raises(ValueError) as exc:
|
||||
CollectionInfo(**test_data)
|
||||
assert 'name' in str(exc) in str(exc)
|
||||
|
||||
|
||||
def test_name_parse_error():
|
||||
test_data = {
|
||||
'name': 'foo',
|
||||
'authors': ['chouseknecht'],
|
||||
'license': 'GPL-3.0-or-later',
|
||||
'version': '0.0.1',
|
||||
'description': 'unit testing thing'
|
||||
}
|
||||
with pytest.raises(ValueError) as exc:
|
||||
CollectionInfo(**test_data)
|
||||
assert 'name' in str(exc)
|
||||
|
||||
|
||||
def test_type_list_error():
|
||||
test_data = {
|
||||
'name': 'foo.foo',
|
||||
'authors': 'chouseknecht',
|
||||
'license': 'GPL-3.0-or-later',
|
||||
'version': '0.0.1',
|
||||
'description': 'unit testing thing',
|
||||
}
|
||||
with pytest.raises(ValueError) as exc:
|
||||
CollectionInfo(**test_data)
|
||||
assert 'authors' in str(exc)
|
||||
|
||||
|
||||
def test_semantic_version_error():
|
||||
test_data = {
|
||||
'name': 'foo.foo',
|
||||
'authors': ['chouseknecht'],
|
||||
'license': 'GPL-3.0-or-later',
|
||||
'version': 'foo',
|
||||
'description': 'unit testing thing',
|
||||
}
|
||||
with pytest.raises(ValueError) as exc:
|
||||
CollectionInfo(**test_data)
|
||||
assert 'version' in str(exc)
|
||||
|
||||
|
||||
def test_namespace_property():
|
||||
test_data = {
|
||||
'name': 'foo.foo',
|
||||
'authors': ['chouseknecht'],
|
||||
'license': 'GPL-3.0-or-later',
|
||||
'version': '1.0.0',
|
||||
'description': 'unit testing thing',
|
||||
}
|
||||
info = CollectionInfo(**test_data)
|
||||
assert info.namespace == 'foo'
|
||||
@@ -26,13 +26,15 @@ def _build_context(collection_path=None, output_path=None):
|
||||
output_path=output_path)
|
||||
|
||||
|
||||
def _collection_info(namespace=None, name=None, version=None, author=None):
|
||||
namespace = namespace or 'some_namespace'
|
||||
name = name or 'some_name'
|
||||
def _collection_info(namespace=None, name=None, version=None, authors=None):
|
||||
name = name or 'some_namespace.some_name'
|
||||
version = version or '1.2.3'
|
||||
author = author or 'Rex Chapman'
|
||||
authors = authors or ['Rex Chapman']
|
||||
description = "Unit testing thing"
|
||||
test_license = 'GPL-3.0-or-later'
|
||||
|
||||
return CollectionInfo(namespace, name, version, author=author)
|
||||
return CollectionInfo(name=name, version=version, authors=authors, description=description,
|
||||
license=test_license)
|
||||
|
||||
|
||||
def test_build(tmpdir):
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
|
||||
import attr
|
||||
import logging
|
||||
import os
|
||||
|
||||
import attr
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
from ansible_galaxy import collection_info
|
||||
from ansible_galaxy.models.collection_info import CollectionInfo
|
||||
from ansible_galaxy import yaml_persist
|
||||
from ansible_galaxy import exceptions
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -14,12 +15,14 @@ log = logging.getLogger(__name__)
|
||||
def test_load():
|
||||
file_name = "example_collection_info1.yml"
|
||||
test_data_path = os.path.join(os.path.dirname(__file__), '%s' % file_name)
|
||||
expected = {'namespace': 'some_namespace',
|
||||
'name': 'some_name',
|
||||
expected = {'name': 'some_namespace.some_name',
|
||||
'version': '11.11.11',
|
||||
'author': 'Carlos Boozer',
|
||||
'license': 'GPLv2',
|
||||
'format_version': 0.0}
|
||||
'authors': ['Carlos Boozer'],
|
||||
'description': 'something',
|
||||
'license': 'GPL-3.0-or-later',
|
||||
'keywords': [],
|
||||
'readme': 'README.md',
|
||||
'dependencies': []}
|
||||
|
||||
with open(test_data_path, 'r') as data_fd:
|
||||
res = collection_info.load(data_fd)
|
||||
@@ -62,3 +65,17 @@ def test_save(tmpdir):
|
||||
read_fd.seek(0)
|
||||
buf = read_fd.read()
|
||||
log.debug('buf: %s', buf)
|
||||
|
||||
|
||||
def test_parse_error(tmpdir):
|
||||
test_data = {
|
||||
'name': 'foo.foo',
|
||||
'authors': ['chouseknecht'],
|
||||
'license': 'GPL-3.0-or-later',
|
||||
'version': '0.0.1',
|
||||
'description': 'unit testing thing',
|
||||
'foo': 'foo',
|
||||
}
|
||||
collection_yaml = yaml.safe_dump(test_data, stream=None)
|
||||
with pytest.raises(exceptions.GalaxyClientError):
|
||||
collection_info.load(collection_yaml)
|
||||
|
||||
@@ -9,9 +9,10 @@ from ansible_galaxy_cli import exceptions as cli_exceptions
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
COLLECTION_INFO1 = '''
|
||||
namespace: some_namespace
|
||||
name: some_name
|
||||
name: some_namespace.some_name
|
||||
version: 3.1.4
|
||||
license: GPL-3.0-or-later
|
||||
description: 'a thing'
|
||||
'''
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user