1
0
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:
Chris Houseknecht
2018-09-27 07:47:25 -04:00
committed by GitHub
parent 2e2b9ebd4e
commit ef82d16a08
15 changed files with 4948 additions and 45 deletions

2
.codacy.yml Normal file
View File

@@ -0,0 +1,2 @@
exclude_paths:
- tests/**

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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'

View File

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

View File

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

View File

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