Use a stricter semantic version check for some version fields #225
@ -400,11 +400,8 @@ class ValidateManifestFields(TestCase):
|
|||||||
data['blender_version_min'] = '2.9'
|
data['blender_version_min'] = '2.9'
|
||||||
with self.assertRaises(ValidationError) as e:
|
with self.assertRaises(ValidationError) as e:
|
||||||
ManifestValidator(data)
|
ManifestValidator(data)
|
||||||
|
|
||||||
self.assertEqual(1, len(e.exception.messages))
|
self.assertEqual(1, len(e.exception.messages))
|
||||||
|
|
||||||
self.assertIn('blender_version_min', e.exception.messages[0])
|
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'
|
data['blender_version_min'] = '4.1.0'
|
||||||
with self.assertRaises(ValidationError) as e:
|
with self.assertRaises(ValidationError) as e:
|
||||||
@ -419,12 +416,8 @@ class ValidateManifestFields(TestCase):
|
|||||||
data['blender_version_min'] = '4.2.0-beta'
|
data['blender_version_min'] = '4.2.0-beta'
|
||||||
with self.assertRaises(ValidationError) as e:
|
with self.assertRaises(ValidationError) as e:
|
||||||
ManifestValidator(data)
|
ManifestValidator(data)
|
||||||
self.assertEqual(
|
self.assertIn('blender_version_min', e.exception.messages[0])
|
||||||
e.exception.messages,
|
self.assertIn('should follow', e.exception.messages[0])
|
||||||
[
|
|
||||||
'Manifest value error: <code>blender_version_min</code> should be at least "4.2.0"',
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_blender_version_max(self):
|
def test_blender_version_max(self):
|
||||||
data = {
|
data = {
|
||||||
@ -443,6 +436,52 @@ class ValidateManifestFields(TestCase):
|
|||||||
self.assertEqual(1, len(e.exception.messages))
|
self.assertEqual(1, len(e.exception.messages))
|
||||||
self.assertIn('blender_version_max', e.exception.messages[0])
|
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):
|
def test_licenses(self):
|
||||||
data = {
|
data = {
|
||||||
**self.mandatory_fields,
|
**self.mandatory_fields,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from semantic_version import Version
|
from semantic_version import Version
|
||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.validators import URLValidator, validate_unicode_slug
|
from django.core.validators import URLValidator, validate_unicode_slug
|
||||||
@ -20,6 +21,10 @@ import files.models
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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
|
@deconstructible
|
||||||
class FileMIMETypeValidator:
|
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'
|
example = '1.0.0'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -558,7 +591,7 @@ class SchemaVersionValidator(VersionValidator):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class VersionMinValidator(VersionValidator):
|
class VersionMinValidator(SemVerNoLabelValidator):
|
||||||
example = '4.2.0'
|
example = '4.2.0'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -574,7 +607,7 @@ class VersionMinValidator(VersionValidator):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class VersionMaxValidator(VersionValidator):
|
class VersionMaxValidator(SemVerNoLabelValidator):
|
||||||
example = '4.3.0'
|
example = '4.3.0'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -591,15 +624,7 @@ class VersionMaxValidator(VersionValidator):
|
|||||||
# assuming that VersionMinValidator has caught this and reported properly
|
# assuming that VersionMinValidator has caught this and reported properly
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
|
||||||
max = Version(value)
|
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:
|
if max <= min:
|
||||||
return mark_safe(
|
return mark_safe(
|
||||||
'Manifest value error: <code>blender_version_max</code> should be greater than '
|
'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