Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow semver parsing to fall back on more generic parser #146

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/wurf/post_resolve_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ class PostResolveRun(object):
"""

def __init__(self, resolver, ctx, run, cwd):

"""Construct a new instance.

:param resolver: A resolver instance.
Expand Down
72 changes: 57 additions & 15 deletions src/wurf/semver_selector.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
#! /usr/bin/env python
# encoding: utf-8

import re


BASEVERSION = re.compile(
r"""[vV]?
(?P<major>0|[1-9]\d*)
(\.
(?P<minor>0|[1-9]\d*)
(\.
(?P<patch>0|[1-9]\d*)
)?
)?
""",
re.VERBOSE,
)


class SemverSelector(object):
"""
Expand All @@ -27,32 +43,58 @@ def select_tag(self, major, tags):
"""
assert isinstance(major, int), "Major version is not an int"

valid_tags = []
valid_tags_ver = dict()

# Get tags with a matching major version
for tag in tags:
for tag_str in tags:
try:
t = self.semver.parse(tag)
if t["major"] != major:
continue
tag_ver = self.semver.VersionInfo.parse(tag_str)
except ValueError:
# Version might be prefixed with `v` or otherwise not be semver
# compatible. Try to find a simpler `major.minor.patch` match
# somewhere in the tag as a last resort.
tag_ver = self._semver_coerce(tag_str)
if tag_ver and tag_ver.major == major:
valid_tags_ver[tag_ver] = tag_str

valid_tags.append(tag)
except ValueError: # ignore tags we cannot parse
pass

if len(valid_tags) == 0:
if not valid_tags_ver:
return None

# Now figure out which version is the newest.
# We only use tags that have the specified major version to ensure
# compatibility, see rules at semver.org
best_match = valid_tags[0]
best_match_ver = max(valid_tags_ver.keys())

# Return original string representation of version tag.
return valid_tags_ver[best_match_ver]

for t in valid_tags:
if self.semver.match(best_match, "<" + t):
best_match = t
def _semver_coerce(self, version):
"""Convert an incomplete version string into a semver-compatible Version
object

* Tries to detect a "basic" version string (``major.minor.patch``).
* If not enough components can be found, missing components are
set to zero to obtain a valid semver version.

Copyright (c) 2013, Konstantine Rybnikov
https://github.com/python-semver/python-semver/

:param str version: the version string to convert
:return: a :class:`Version` instance (or ``None`` if it's not a version)
:rtype: :class:`Version` | None
"""
# Changed: using `match` instead of `search` to avoid situation where
# a tag simply containing a number inside it is parsed as a major
# version.
match = BASEVERSION.match(version)
if not match:
return None

return best_match
ver = {
key: 0 if value is None else int(value)
for key, value in match.groupdict().items()
}
return self.semver.VersionInfo(**ver)

def __repr__(self):
"""
Expand Down
1 change: 0 additions & 1 deletion src/wurf/waf_standalone_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ def options(opt):


class WafStandaloneContext(Dist):

"""creates a standalone archive that contains all dependencies"""

cmd = "standalone"
Expand Down
11 changes: 11 additions & 0 deletions test/python/test_semver_selector.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ def test_semver_selector():
"3.0.0",
"3.0.0-lts.0",
"3.0.0-lts.1",
"backups/3.1.0-lts.0", # Do not match non-toplevel tags.
"4.0.0",
"v4.0.1",
"5.0.0",
"5.1",
]

# Select latest tag for major version 1
Expand All @@ -27,3 +32,9 @@ def test_semver_selector():

# Select latest tag for major version 3 (LTS tags should be ignored)
assert selector.select_tag(major=3, tags=tags) == "3.0.0"

# Select latest tag for major version 4 (v prefix should be ignored)
assert selector.select_tag(major=4, tags=tags) == "v4.0.1"

# Select latest tag for major version 5 (missing minor should be ignored)
assert selector.select_tag(major=5, tags=tags) == "5.1"
Loading