Use a stricter semantic version check for some version fields #225
@ -400,11 +400,8 @@ class ValidateManifestFields(TestCase):
|
||||
data['blender_version_min'] = '2.9'
|
||||
with self.assertRaises(ValidationError) as e:
|
||||
ManifestValidator(data)
|
||||
|
||||
self.assertEqual(1, len(e.exception.messages))
|
||||
|
||||
self.assertIn('blender_version_min', e.exception.messages[0])
|
||||
self.assertIn('4.2.0', e.exception.messages[0])
|
||||
|
||||
data['blender_version_min'] = '4.1.0'
|
||||
with self.assertRaises(ValidationError) as e:
|
||||
@ -419,12 +416,8 @@ class ValidateManifestFields(TestCase):
|
||||
data['blender_version_min'] = '4.2.0-beta'
|
||||
with self.assertRaises(ValidationError) as e:
|
||||
ManifestValidator(data)
|
||||
self.assertEqual(
|
||||
e.exception.messages,
|
||||
[
|
||||
'Manifest value error: <code>blender_version_min</code> should be at least "4.2.0"',
|
||||
],
|
||||
)
|
||||
self.assertIn('blender_version_min', e.exception.messages[0])
|
||||
self.assertIn('should follow', e.exception.messages[0])
|
||||
|
||||
def test_blender_version_max(self):
|
||||
data = {
|
||||
@ -443,6 +436,52 @@ class ValidateManifestFields(TestCase):
|
||||
self.assertEqual(1, len(e.exception.messages))
|
||||
self.assertIn('blender_version_max', e.exception.messages[0])
|
||||
|
||||
def test_non_semver_versions(self):
|
||||
data = {
|
||||
**self.mandatory_fields,
|
||||
**self.optional_fields,
|
||||
}
|
||||
|
||||
for field, value in (
|
||||
('blender_version_min', '4.2'),
|
||||
('blender_version_min', '4.2.1+alpha'),
|
||||
('blender_version_min', '5'),
|
||||
('blender_version_max', '4.3'),
|
||||
('blender_version_max', '4.3.0+alpha'),
|
||||
('blender_version_max', '5'),
|
||||
('schema_version', '1.0'),
|
||||
('schema_version', '1'),
|
||||
('schema_version', '1.0.0.0'),
|
||||
):
|
||||
value_original = data[field]
|
||||
data[field] = value
|
||||
|
||||
with self.assertRaises(ValidationError) as e:
|
||||
ManifestValidator(data)
|
||||
self.assertEqual(1, len(e.exception.messages))
|
||||
self.assertIn(field, e.exception.messages[0])
|
||||
|
||||
data[field] = value_original
|
||||
|
||||
def test_version(self):
|
||||
data = {
|
||||
**self.mandatory_fields,
|
||||
**self.optional_fields,
|
||||
}
|
||||
|
||||
data['version'] = '1.2.0'
|
||||
ManifestValidator(data)
|
||||
|
||||
data['version'] = '1.2.0+alpha'
|
||||
ManifestValidator(data)
|
||||
|
||||
for bad_version in ['1.2', '1.2.0.0', '1']:
|
||||
data['version'] = bad_version
|
||||
with self.assertRaises(ValidationError) as e:
|
||||
ManifestValidator(data)
|
||||
self.assertEqual(1, len(e.exception.messages))
|
||||
self.assertIn('version', e.exception.messages[0])
|
||||
|
||||
def test_licenses(self):
|
||||
data = {
|
||||
**self.mandatory_fields,
|
||||
|
@ -1,5 +1,6 @@
|
||||
from semantic_version import Version
|
||||
import logging
|
||||
import re
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import URLValidator, validate_unicode_slug
|
||||
@ -20,6 +21,10 @@ import files.models
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Define the regex pattern for major.minor.patch (do not include any additional label).
|
||||
# https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
|
||||
version_regex_pattern = re.compile(r'^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$')
|
||||
|
||||
|
||||
@deconstructible
|
||||
class FileMIMETypeValidator:
|
||||
@ -536,7 +541,35 @@ class VersionValidator:
|
||||
)
|
||||
|
||||
|
||||
class SchemaVersionValidator(VersionValidator):
|
||||
class SemVerNoLabelValidator(StringValidator):
|
||||
"""These version are a sub-set of the semantic version
|
||||
|
||||
They only support major.minor.patch.
|
||||
dfelinto marked this conversation as resolved
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def is_version_string(version):
|
||||
return version_regex_pattern.match(version)
|
||||
|
||||
dfelinto marked this conversation as resolved
Oleg-Komarov
commented
missing I would also declare this as a package "constant" at the top of the file, so that it is compiled once, and not on every validation missing `$` anchor
I would also declare this as a package "constant" at the top of the file, so that it is compiled once, and not on every validation
|
||||
@classmethod
|
||||
def validate(cls, *, name: str, value: str, manifest: dict) -> str:
|
||||
"""Return error message if cannot validate, otherwise returns nothing"""
|
||||
dfelinto marked this conversation as resolved
Oleg-Komarov
commented
this is unusual, why not use the this is unusual, why not use the `$` anchor in the regex and do a regular match?
|
||||
err_message = mark_safe(
|
||||
f'Manifest value error: <code>{name}</code> should follow a '
|
||||
f'<a href="https://semver.org/" target="_blank">semantic version</a> '
|
||||
f'with only major.minor.patch '
|
||||
f'e.g., "{cls.example}"'
|
||||
)
|
||||
|
||||
if super().validate(name=name, value=value, manifest=manifest):
|
||||
return err_message
|
||||
|
||||
# Make sure we only have major.minor.patch.
|
||||
if not cls.is_version_string(version=value):
|
||||
return err_message
|
||||
|
||||
dfelinto marked this conversation as resolved
Oleg-Komarov
commented
this is not needed, we need to fully rely on the check below this is not needed, we need to fully rely on the check below
|
||||
|
||||
class SchemaVersionValidator(SemVerNoLabelValidator):
|
||||
example = '1.0.0'
|
||||
|
||||
@classmethod
|
||||
@ -558,7 +591,7 @@ class SchemaVersionValidator(VersionValidator):
|
||||
)
|
||||
|
||||
|
||||
class VersionMinValidator(VersionValidator):
|
||||
class VersionMinValidator(SemVerNoLabelValidator):
|
||||
example = '4.2.0'
|
||||
|
||||
@classmethod
|
||||
@ -574,7 +607,7 @@ class VersionMinValidator(VersionValidator):
|
||||
)
|
||||
|
||||
|
||||
class VersionMaxValidator(VersionValidator):
|
||||
class VersionMaxValidator(SemVerNoLabelValidator):
|
||||
example = '4.3.0'
|
||||
|
||||
@classmethod
|
||||
@ -591,15 +624,7 @@ class VersionMaxValidator(VersionValidator):
|
||||
# assuming that VersionMinValidator has caught this and reported properly
|
||||
return
|
||||
|
||||
try:
|
||||
max = Version(value)
|
||||
except (ValueError, TypeError):
|
||||
return mark_safe(
|
||||
f'Manifest value error: <code>{name}</code> should follow a '
|
||||
f'<a href="https://semver.org/" target="_blank">semantic version</a>. '
|
||||
f'e.g., "{cls.example}"'
|
||||
)
|
||||
|
||||
if max <= min:
|
||||
return mark_safe(
|
||||
'Manifest value error: <code>blender_version_max</code> should be greater than '
|
||||
|
Loading…
Reference in New Issue
Block a user
this import should be at the top of the file