diff --git a/README.md b/README.md index afa957b..5a1cf7e 100644 --- a/README.md +++ b/README.md @@ -400,6 +400,12 @@ Validates MAC addresses. Examples: - `mac()`: Allows any valid MAC address +### SemVer (Semantic Versioning) - `semver()` +Validates [Semantic Versioning](https://semver.org/) strings. + +Examples: +- `semver()`: Allows any valid SemVer string + ### Any - `any([validators])` Validates against a union of types. Use when a node **must** contain **one and only one** of several types. It is valid if at least one of the listed validators is valid. If no validators are given, accept any value. diff --git a/yamale/validators/tests/test_validate.py b/yamale/validators/tests/test_validate.py index a3370fe..630ba3e 100644 --- a/yamale/validators/tests/test_validate.py +++ b/yamale/validators/tests/test_validate.py @@ -144,3 +144,83 @@ def test_mac(): assert not v.is_valid("ab:cd:ef:12:34:56:78") assert not v.is_valid("abcdefghijkl") assert not v.is_valid("1234567890ax") + + +def test_semver(): + """ + - https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string + - https://regex101.com/r/Ly7O1x/3/ + """ + v = val.SemVer() + + assert v.is_valid("0.0.4") + assert v.is_valid("1.2.3") + assert v.is_valid("10.20.30") + assert v.is_valid("1.1.2-prerelease+meta") + assert v.is_valid("1.1.2+meta") + assert v.is_valid("1.1.2+meta-valid") + assert v.is_valid("1.0.0-alpha") + assert v.is_valid("1.0.0-beta") + assert v.is_valid("1.0.0-alpha.beta") + assert v.is_valid("1.0.0-alpha.beta.1") + assert v.is_valid("1.0.0-alpha.1") + assert v.is_valid("1.0.0-alpha0.valid") + assert v.is_valid("1.0.0-alpha.0valid") + assert v.is_valid("1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay") + assert v.is_valid("1.0.0-rc.1+build.1") + assert v.is_valid("2.0.0-rc.1+build.123") + assert v.is_valid("1.2.3-beta") + assert v.is_valid("10.2.3-DEV-SNAPSHOT") + assert v.is_valid("1.2.3-SNAPSHOT-123") + assert v.is_valid("1.0.0") + assert v.is_valid("2.0.0") + assert v.is_valid("1.1.7") + assert v.is_valid("2.0.0+build.1848") + assert v.is_valid("2.0.1-alpha.1227") + assert v.is_valid("1.0.0-alpha+beta") + assert v.is_valid("1.2.3----RC-SNAPSHOT.12.9.1--.12+788") + assert v.is_valid("1.2.3----R-S.12.9.1--.12+meta") + assert v.is_valid("1.2.3----RC-SNAPSHOT.12.9.1--.12") + assert v.is_valid("1.0.0+0.build.1-rc.10000aaa-kk-0.1") + assert v.is_valid("99999999999999999999999.999999999999999999.99999999999999999") + assert v.is_valid("1.0.0-0A.is.legal") + + assert not v.is_valid("1") + assert not v.is_valid("1.2") + assert not v.is_valid("1.2.3-0123") + assert not v.is_valid("1.2.3-0123.0123") + assert not v.is_valid("1.1.2+.123") + assert not v.is_valid("+invalid") + assert not v.is_valid("-invalid") + assert not v.is_valid("-invalid+invalid") + assert not v.is_valid("-invalid.01") + assert not v.is_valid("alpha") + assert not v.is_valid("alpha.beta") + assert not v.is_valid("alpha.beta.1") + assert not v.is_valid("alpha.1") + assert not v.is_valid("alpha+beta") + assert not v.is_valid("alpha_beta") + assert not v.is_valid("alpha.") + assert not v.is_valid("alpha..") + assert not v.is_valid("beta") + assert not v.is_valid("1.0.0-alpha_beta") + assert not v.is_valid("-alpha.") + assert not v.is_valid("1.0.0-alpha..") + assert not v.is_valid("1.0.0-alpha..1") + assert not v.is_valid("1.0.0-alpha...1") + assert not v.is_valid("1.0.0-alpha....1") + assert not v.is_valid("1.0.0-alpha.....1") + assert not v.is_valid("1.0.0-alpha......1") + assert not v.is_valid("1.0.0-alpha.......1") + assert not v.is_valid("01.1.1") + assert not v.is_valid("1.01.1") + assert not v.is_valid("1.1.01") + assert not v.is_valid("1.2") + assert not v.is_valid("1.2.3.DEV") + assert not v.is_valid("1.2-SNAPSHOT") + assert not v.is_valid("1.2.31.2.3----RC-SNAPSHOT.12.09.1--..12+788") + assert not v.is_valid("1.2-RC-SNAPSHOT") + assert not v.is_valid("-1.0.3-gamma+b7718") + assert not v.is_valid("+justmeta") + assert not v.is_valid("9.8.7+meta+meta") + assert not v.is_valid("9.8.7-whatever+meta+meta") diff --git a/yamale/validators/validators.py b/yamale/validators/validators.py index d72478e..026e540 100644 --- a/yamale/validators/validators.py +++ b/yamale/validators/validators.py @@ -248,6 +248,19 @@ def __init__(self, *args, **kwargs): ] +class SemVer(Regex): + """Semantic Versioning (semver.org) validator""" + + tag = "semver" + + def __init__(self, *args, **kwargs): + super(SemVer, self).__init__(*args, **kwargs) + self.regexes = [ + # https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string + re.compile("^(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"), + ] + + DefaultValidators = {} for v in util.get_subclasses(Validator):