add pyalpm.vercmp as an alternative to parse_version

`pkg_resources.parse_version` has different results from
`pyalpm.vercmp`. This allows to use `pyalpm.vercmp` as the sorting key.

This closes #40.
This commit is contained in:
Jiachen Yang 2016-04-02 23:13:41 +09:00 committed by lilydjwg
parent 8850babe71
commit b9f5a1e476
7 changed files with 79 additions and 18 deletions

View file

@ -114,6 +114,12 @@ oldver
newver newver
Specify a version record file to store the new version info. 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 in a Webpage
------------------- -------------------
Search through a specific webpage for the version string. This type of version finding has these fields: Search through a specific webpage for the version string. This type of version finding has these fields:
@ -137,6 +143,11 @@ proxy
user_agent 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). 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 Find with a Command
------------------- -------------------
Use a shell command line to get the version. The output is striped first, so trailing newlines do not bother. 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 useful to avoid some known badly versioned tags, so the newer tags won't
be "overridden" by the old broken ones. 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. An environment variable ``NVCHECKER_GITHUB_TOKEN`` can be set to a GitHub OAuth token in order to request more frequently than anonymously.
Check BitBucket Check BitBucket
@ -202,6 +218,11 @@ ignored_tags
be useful to avoid some known badly versioned tags, so the newer tags won't be useful to avoid some known badly versioned tags, so the newer tags won't
be "overridden" by the old broken ones. 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
------------- -------------
Check `GitCafe <https://gitcafe.com/>`_ for updates. The version returned is in date format ``%Y%m%d``, e.g. ``20130701``. Check `GitCafe <https://gitcafe.com/>`_ 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 useful to avoid some known badly versioned tags, so the newer tags won't
be "overridden" by the old broken ones. 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 host
Hostname for self-hosted GitLab instance. Hostname for self-hosted GitLab instance.

View file

@ -5,11 +5,11 @@ import sys
import logging import logging
import configparser import configparser
from pkg_resources import parse_version
from tornado.stack_context import ExceptionStackContext from tornado.stack_context import ExceptionStackContext
from .lib import nicelogger from .lib import nicelogger
from .get_version import get_version from .get_version import get_version
from .sortversion import sort_version_keys
from . import __version__ from . import __version__
@ -62,6 +62,7 @@ class Source:
started = False started = False
tasks = 0 tasks = 0
oldver = newver = None oldver = newver = None
sort_version_key = None
def __init__(self, file): def __init__(self, file):
self.config = config = configparser.ConfigParser( self.config = config = configparser.ConfigParser(
dict_type=dict, allow_no_value=True dict_type=dict, allow_no_value=True
@ -73,6 +74,7 @@ class Source:
d = os.path.dirname(file.name) d = os.path.dirname(file.name)
self.oldver = os.path.expandvars(os.path.expanduser(os.path.join(d, c.get('oldver')))) 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.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): def check(self):
self.started = True self.started = True
@ -113,7 +115,7 @@ class Source:
return return
oldver = self.oldvers.get(name, None) 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) logger.info('%s updated version %s', name, version)
self.curvers[name] = version self.curvers[name] = version
self.on_update(name, version, oldver) self.on_update(name, version, oldver)

26
nvchecker/sortversion.py Normal file
View file

@ -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"))

View file

@ -2,9 +2,10 @@ import os
import json import json
from functools import partial from functools import partial
from pkg_resources import parse_version
from tornado.httpclient import AsyncHTTPClient, HTTPRequest from tornado.httpclient import AsyncHTTPClient, HTTPRequest
from ..sortversion import sort_version_keys
# doc: https://confluence.atlassian.com/display/BITBUCKET/commits+or+commit+Resource # doc: https://confluence.atlassian.com/display/BITBUCKET/commits+or+commit+Resource
BITBUCKET_URL = 'https://bitbucket.org/api/2.0/repositories/%s/commits/%s' BITBUCKET_URL = 'https://bitbucket.org/api/2.0/repositories/%s/commits/%s'
BITBUCKET_MAX_TAG = 'https://bitbucket.org/api/1.0/repositories/%s/tags' 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', '') br = conf.get('branch', '')
use_max_tag = conf.getboolean('use_max_tag', False) use_max_tag = conf.getboolean('use_max_tag', False)
ignored_tags = conf.get("ignored_tags", "").split() ignored_tags = conf.get("ignored_tags", "").split()
sort_version_key = sort_version_keys[conf.get("sort_version_key", "parse_version")]
if use_max_tag: if use_max_tag:
url = BITBUCKET_MAX_TAG % repo url = BITBUCKET_MAX_TAG % repo
else: else:
url = BITBUCKET_URL % (repo, br) url = BITBUCKET_URL % (repo, br)
request = HTTPRequest(url, user_agent='lilydjwg/nvchecker') request = HTTPRequest(url, user_agent='lilydjwg/nvchecker')
AsyncHTTPClient().fetch(request, 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')) data = json.loads(res.body.decode('utf-8'))
if use_max_tag: if use_max_tag:
data = [tag for tag in data if tag not in ignored_tags] 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] version = data[-1]
else: else:
version = data['values'][0]['date'].split('T', 1)[0].replace('-', '') version = data['values'][0]['date'].split('T', 1)[0].replace('-', '')

View file

@ -2,9 +2,10 @@ import os
import json import json
from functools import partial from functools import partial
from pkg_resources import parse_version
from tornado.httpclient import AsyncHTTPClient, HTTPRequest from tornado.httpclient import AsyncHTTPClient, HTTPRequest
from ..sortversion import sort_version_keys
GITHUB_URL = 'https://api.github.com/repos/%s/commits?sha=%s' GITHUB_URL = 'https://api.github.com/repos/%s/commits?sha=%s'
GITHUB_LATEST_RELEASE = 'https://api.github.com/repos/%s/releases/latest' GITHUB_LATEST_RELEASE = 'https://api.github.com/repos/%s/releases/latest'
GITHUB_MAX_TAG = 'https://api.github.com/repos/%s/tags' 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_latest_release = conf.getboolean('use_latest_release', False)
use_max_tag = conf.getboolean('use_max_tag', False) use_max_tag = conf.getboolean('use_max_tag', False)
ignored_tags = conf.get("ignored_tags", "").split() ignored_tags = conf.get("ignored_tags", "").split()
sort_version_key = sort_version_keys[conf.get("sort_version_key", "parse_version")]
if use_latest_release: if use_latest_release:
url = GITHUB_LATEST_RELEASE % repo url = GITHUB_LATEST_RELEASE % repo
elif use_max_tag: elif use_max_tag:
@ -26,15 +28,15 @@ def get_version(name, conf, callback):
headers['Authorization'] = 'token %s' % os.environ['NVCHECKER_GITHUB_TOKEN'] headers['Authorization'] = 'token %s' % os.environ['NVCHECKER_GITHUB_TOKEN']
request = HTTPRequest(url, headers=headers, user_agent='lilydjwg/nvchecker') request = HTTPRequest(url, headers=headers, user_agent='lilydjwg/nvchecker')
AsyncHTTPClient().fetch(request, 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')) data = json.loads(res.body.decode('utf-8'))
if use_latest_release: if use_latest_release:
version = data['tag_name'] version = data['tag_name']
elif use_max_tag: elif use_max_tag:
data = [tag["name"] for tag in data if tag["name"] not in ignored_tags] 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] version = data[-1]
else: else:
# YYYYMMDD.HHMMSS # YYYYMMDD.HHMMSS

View file

@ -4,9 +4,10 @@ from functools import partial
import logging import logging
import urllib.parse import urllib.parse
from pkg_resources import parse_version
from tornado.httpclient import AsyncHTTPClient, HTTPRequest 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_URL = 'https://%s/api/v3/projects/%s/repository/commits?ref_name=%s'
GITLAB_MAX_TAG = 'https://%s/api/v3/projects/%s/repository/tags' 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") host = conf.get('host', "gitlab.com")
use_max_tag = conf.getboolean('use_max_tag', False) use_max_tag = conf.getboolean('use_max_tag', False)
ignored_tags = conf.get("ignored_tags", "").split() 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("/", "_") env_name = "NVCHECKER_GITLAB_TOKEN_" + host.upper().replace(".", "_").replace("/", "_")
token = conf.get('token', os.environ.get(env_name, None)) token = conf.get('token', os.environ.get(env_name, None))
@ -34,13 +36,13 @@ def get_version(name, conf, callback):
headers = {"PRIVATE-TOKEN": token} headers = {"PRIVATE-TOKEN": token}
request = HTTPRequest(url, headers=headers, user_agent='lilydjwg/nvchecker') request = HTTPRequest(url, headers=headers, user_agent='lilydjwg/nvchecker')
AsyncHTTPClient().fetch(request, 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')) data = json.loads(res.body.decode('utf-8'))
if use_max_tag: if use_max_tag:
data = [tag["name"] for tag in data if tag["name"] not in ignored_tags] 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] version = data[-1]
else: else:
version = data[0]['created_at'].split('T', 1)[0].replace('-', '') version = data[0]['created_at'].split('T', 1)[0].replace('-', '')

View file

@ -4,10 +4,10 @@ import logging
import urllib.parse import urllib.parse
from functools import partial from functools import partial
from pkg_resources import parse_version
from tornado.httpclient import AsyncHTTPClient from tornado.httpclient import AsyncHTTPClient
from .base import pycurl from .base import pycurl
from ..sortversion import sort_version_keys
logger = logging.getLogger(__name__) 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) logger.warn('%s: proxy set but not used because pycurl is unavailable.', name)
if conf.get('user_agent'): if conf.get('user_agent'):
kwargs['user_agent'] = conf['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( httpclient.fetch(conf['url'], partial(
_got_version, name, r, encoding, callback _got_version, name, r, encoding, sort_version_key, callback
), **kwargs) ), **kwargs)
def _got_version(name, regex, encoding, callback, res): def _got_version(name, regex, encoding, sort_version_key, callback, res):
version = None version = None
try: try:
body = res.body.decode(encoding) body = res.body.decode(encoding)
try: try:
version = max(regex.findall(body), key=parse_version) version = max(regex.findall(body), key=sort_version_key)
except ValueError: except ValueError:
logger.error('%s: version string not found.', name) logger.error('%s: version string not found.', name)
finally: finally: