diff --git a/README.rst b/README.rst index bcfa125..48cf81f 100644 --- a/README.rst +++ b/README.rst @@ -114,6 +114,12 @@ oldver newver Specify a version record file to store the new version info. +sort_version_key + Sort the version string using this key function. Choose between ``parse_version`` and + ``vercmp``. Default value is ``parse_version``. ``parse_version`` use + ``pkg_resources.parse_version``. ``vercmp`` use ``pyalpm.vercmp``. + + Search in a Webpage ------------------- Search through a specific webpage for the version string. This type of version finding has these fields: @@ -137,6 +143,11 @@ proxy user_agent The ``User-Agent`` header value to use. Use something more like a tool (e.g. ``curl/7.40.0``) in Europe or the real web page won't get through because cookie policies (SourceForge has this issue). +sort_version_key + Sort the version string using this key function. Choose between ``parse_version`` and + ``vercmp``. Default value is ``parse_version``. ``parse_version`` use + ``pkg_resources.parse_version``. ``vercmp`` use ``pyalpm.vercmp``. + Find with a Command ------------------- Use a shell command line to get the version. The output is striped first, so trailing newlines do not bother. @@ -180,6 +191,11 @@ ignored_tags be useful to avoid some known badly versioned tags, so the newer tags won't be "overridden" by the old broken ones. +sort_version_key + Sort the version string using this key function. Choose between ``parse_version`` and + ``vercmp``. Default value is ``parse_version``. ``parse_version`` use + ``pkg_resources.parse_version``. ``vercmp`` use ``pyalpm.vercmp``. + An environment variable ``NVCHECKER_GITHUB_TOKEN`` can be set to a GitHub OAuth token in order to request more frequently than anonymously. Check BitBucket @@ -202,6 +218,11 @@ ignored_tags be useful to avoid some known badly versioned tags, so the newer tags won't be "overridden" by the old broken ones. +sort_version_key + Sort the version string using this key function. Choose between ``parse_version`` and + ``vercmp``. Default value is ``parse_version``. ``parse_version`` use + ``pkg_resources.parse_version``. ``vercmp`` use ``pyalpm.vercmp``. + Check GitCafe ------------- Check `GitCafe `_ for updates. The version returned is in date format ``%Y%m%d``, e.g. ``20130701``. @@ -234,6 +255,11 @@ ignored_tags be useful to avoid some known badly versioned tags, so the newer tags won't be "overridden" by the old broken ones. +sort_version_key + Sort the version string using this key function. Choose between ``parse_version`` and + ``vercmp``. Default value is ``parse_version``. ``parse_version`` use + ``pkg_resources.parse_version``. ``vercmp`` use ``pyalpm.vercmp``. + host Hostname for self-hosted GitLab instance. diff --git a/nvchecker/core.py b/nvchecker/core.py index 80d6569..67bf2c2 100644 --- a/nvchecker/core.py +++ b/nvchecker/core.py @@ -5,11 +5,11 @@ import sys import logging import configparser -from pkg_resources import parse_version from tornado.stack_context import ExceptionStackContext from .lib import nicelogger from .get_version import get_version +from .sortversion import sort_version_keys from . import __version__ @@ -62,6 +62,7 @@ class Source: started = False tasks = 0 oldver = newver = None + sort_version_key = None def __init__(self, file): self.config = config = configparser.ConfigParser( dict_type=dict, allow_no_value=True @@ -73,6 +74,7 @@ class Source: d = os.path.dirname(file.name) self.oldver = os.path.expandvars(os.path.expanduser(os.path.join(d, c.get('oldver')))) self.newver = os.path.expandvars(os.path.expanduser(os.path.join(d, c.get('newver')))) + self.sort_version_key = sort_version_keys[c.get("sort_version_key", "parse_version")] def check(self): self.started = True @@ -113,7 +115,7 @@ class Source: return oldver = self.oldvers.get(name, None) - if not oldver or parse_version(oldver) < parse_version(version): + if not oldver or self.sort_version_key(oldver) < self.sort_version_key(version): logger.info('%s updated version %s', name, version) self.curvers[name] = version self.on_update(name, version, oldver) diff --git a/nvchecker/sortversion.py b/nvchecker/sortversion.py new file mode 100644 index 0000000..9bd9dfd --- /dev/null +++ b/nvchecker/sortversion.py @@ -0,0 +1,26 @@ +''' +Sort versions using pkg_resource.parse_version or pyalpm.vercmp +''' + +__all__ = ["sort_version_keys"] + +import logging + +from functools import cmp_to_key + +logger = logging.getLogger(__name__) + +from pkg_resources import parse_version +try: + import pyalpm + vercmp = cmp_to_key(pyalpm.vercmp) +except ImportError: + def vercmp(k): + raise NotImplementedError("Using vercmp but pyalpm can not be imported!") + +sort_version_keys = {"parse_version": parse_version, "vercmp": vercmp} + +if __name__ == '__main__': + assert(parse_version("v6.0") < parse_version("6.1")) + assert(parse_version("v6.0") > parse_version("v6.1-stable")) + assert(vercmp("v6.0") < vercmp("v6.1-stable")) diff --git a/nvchecker/source/bitbucket.py b/nvchecker/source/bitbucket.py index f1fc189..8f089c5 100644 --- a/nvchecker/source/bitbucket.py +++ b/nvchecker/source/bitbucket.py @@ -2,9 +2,10 @@ import os import json from functools import partial -from pkg_resources import parse_version from tornado.httpclient import AsyncHTTPClient, HTTPRequest +from ..sortversion import sort_version_keys + # doc: https://confluence.atlassian.com/display/BITBUCKET/commits+or+commit+Resource BITBUCKET_URL = 'https://bitbucket.org/api/2.0/repositories/%s/commits/%s' BITBUCKET_MAX_TAG = 'https://bitbucket.org/api/1.0/repositories/%s/tags' @@ -14,19 +15,20 @@ def get_version(name, conf, callback): br = conf.get('branch', '') use_max_tag = conf.getboolean('use_max_tag', False) ignored_tags = conf.get("ignored_tags", "").split() + sort_version_key = sort_version_keys[conf.get("sort_version_key", "parse_version")] if use_max_tag: url = BITBUCKET_MAX_TAG % repo else: url = BITBUCKET_URL % (repo, br) request = HTTPRequest(url, user_agent='lilydjwg/nvchecker') AsyncHTTPClient().fetch(request, - callback=partial(_bitbucket_done, name, use_max_tag, ignored_tags, callback)) + callback=partial(_bitbucket_done, name, use_max_tag, ignored_tags, sort_version_key, callback)) -def _bitbucket_done(name, use_max_tag, ignored_tags, callback, res): +def _bitbucket_done(name, use_max_tag, ignored_tags, sort_version_key, callback, res): data = json.loads(res.body.decode('utf-8')) if use_max_tag: data = [tag for tag in data if tag not in ignored_tags] - data.sort(key=parse_version) + data.sort(key=sort_version_key) version = data[-1] else: version = data['values'][0]['date'].split('T', 1)[0].replace('-', '') diff --git a/nvchecker/source/github.py b/nvchecker/source/github.py index 191bddc..e1f79d9 100644 --- a/nvchecker/source/github.py +++ b/nvchecker/source/github.py @@ -2,9 +2,10 @@ import os import json from functools import partial -from pkg_resources import parse_version from tornado.httpclient import AsyncHTTPClient, HTTPRequest +from ..sortversion import sort_version_keys + GITHUB_URL = 'https://api.github.com/repos/%s/commits?sha=%s' GITHUB_LATEST_RELEASE = 'https://api.github.com/repos/%s/releases/latest' GITHUB_MAX_TAG = 'https://api.github.com/repos/%s/tags' @@ -15,6 +16,7 @@ def get_version(name, conf, callback): use_latest_release = conf.getboolean('use_latest_release', False) use_max_tag = conf.getboolean('use_max_tag', False) ignored_tags = conf.get("ignored_tags", "").split() + sort_version_key = sort_version_keys[conf.get("sort_version_key", "parse_version")] if use_latest_release: url = GITHUB_LATEST_RELEASE % repo elif use_max_tag: @@ -26,15 +28,15 @@ def get_version(name, conf, callback): headers['Authorization'] = 'token %s' % os.environ['NVCHECKER_GITHUB_TOKEN'] request = HTTPRequest(url, headers=headers, user_agent='lilydjwg/nvchecker') AsyncHTTPClient().fetch(request, - callback=partial(_github_done, name, use_latest_release, use_max_tag, ignored_tags, callback)) + callback=partial(_github_done, name, use_latest_release, use_max_tag, ignored_tags, sort_version_key, callback)) -def _github_done(name, use_latest_release, use_max_tag, ignored_tags, callback, res): +def _github_done(name, use_latest_release, use_max_tag, ignored_tags, sort_version_key, callback, res): data = json.loads(res.body.decode('utf-8')) if use_latest_release: version = data['tag_name'] elif use_max_tag: data = [tag["name"] for tag in data if tag["name"] not in ignored_tags] - data.sort(key=parse_version) + data.sort(key=sort_version_key) version = data[-1] else: # YYYYMMDD.HHMMSS diff --git a/nvchecker/source/gitlab.py b/nvchecker/source/gitlab.py index 055bf8d..a8e3a9e 100644 --- a/nvchecker/source/gitlab.py +++ b/nvchecker/source/gitlab.py @@ -4,9 +4,10 @@ from functools import partial import logging import urllib.parse -from pkg_resources import parse_version from tornado.httpclient import AsyncHTTPClient, HTTPRequest +from ..sortversion import sort_version_keys + GITLAB_URL = 'https://%s/api/v3/projects/%s/repository/commits?ref_name=%s' GITLAB_MAX_TAG = 'https://%s/api/v3/projects/%s/repository/tags' @@ -18,6 +19,7 @@ def get_version(name, conf, callback): host = conf.get('host', "gitlab.com") use_max_tag = conf.getboolean('use_max_tag', False) ignored_tags = conf.get("ignored_tags", "").split() + sort_version_key = sort_version_keys[conf.get("sort_version_key", "parse_version")] env_name = "NVCHECKER_GITLAB_TOKEN_" + host.upper().replace(".", "_").replace("/", "_") token = conf.get('token', os.environ.get(env_name, None)) @@ -34,13 +36,13 @@ def get_version(name, conf, callback): headers = {"PRIVATE-TOKEN": token} request = HTTPRequest(url, headers=headers, user_agent='lilydjwg/nvchecker') AsyncHTTPClient().fetch(request, - callback=partial(_gitlab_done, name, use_max_tag, ignored_tags, callback)) + callback=partial(_gitlab_done, name, use_max_tag, ignored_tags, sort_version_key, callback)) -def _gitlab_done(name, use_max_tag, ignored_tags, callback, res): +def _gitlab_done(name, use_max_tag, ignored_tags, sort_version_key, callback, res): data = json.loads(res.body.decode('utf-8')) if use_max_tag: data = [tag["name"] for tag in data if tag["name"] not in ignored_tags] - data.sort(key=parse_version) + data.sort(key=sort_version_key) version = data[-1] else: version = data[0]['created_at'].split('T', 1)[0].replace('-', '') diff --git a/nvchecker/source/regex.py b/nvchecker/source/regex.py index 9279784..05c5999 100644 --- a/nvchecker/source/regex.py +++ b/nvchecker/source/regex.py @@ -4,10 +4,10 @@ import logging import urllib.parse from functools import partial -from pkg_resources import parse_version from tornado.httpclient import AsyncHTTPClient from .base import pycurl +from ..sortversion import sort_version_keys logger = logging.getLogger(__name__) @@ -32,17 +32,18 @@ def get_version(name, conf, callback): logger.warn('%s: proxy set but not used because pycurl is unavailable.', name) if conf.get('user_agent'): kwargs['user_agent'] = conf['user_agent'] + sort_version_key = sort_version_keys[conf.get("sort_version_key", "parse_version")] httpclient.fetch(conf['url'], partial( - _got_version, name, r, encoding, callback + _got_version, name, r, encoding, sort_version_key, callback ), **kwargs) -def _got_version(name, regex, encoding, callback, res): +def _got_version(name, regex, encoding, sort_version_key, callback, res): version = None try: body = res.body.decode(encoding) try: - version = max(regex.findall(body), key=parse_version) + version = max(regex.findall(body), key=sort_version_key) except ValueError: logger.error('%s: version string not found.', name) finally: