Commit 78ddf912 authored by Aurélien Dunand's avatar Aurélien Dunand Committed by Seblu

Handle complex version number

Image version now accept a string of digit separated by dots and extra char as
a qualifier (~ or +).

Example:
1.2.3.4~dev < 1.2.3.4 < 1.2.3.4+dev

Old repositories are still compatible thanks to sqlite type affinity
(https://www.sqlite.org/faq.html#q3).

We use a package version comparison algorithm for image version comparison
inspired by Debian.

Seblu: Fix typos
Signed-off-by: Seblu's avatarSébastien Luttringer <sebastien.luttringer@smartjog.com>
parent b40a9b7f
......@@ -4,8 +4,8 @@ INSTALLSYSTEMS VERSIONNING
__________________________
A valid version is an integer without dot.
A version n, may be followed by a ~, to indicate it's inferior to n
A version n, may be followed by a +, to indicate it's superior to n
A version n may be followed by a ~ to indicate it's inferior to n
A version n may be followed by a + to indicate it's superior to n
Any following chars after ~ or + are ignored
Examples:
......@@ -17,4 +17,10 @@ Examples:
IMAGES VERSIONNING
__________________
A valid version is an integer. Nothing more!
\ No newline at end of file
A valid version is a string of digits separated by dots.
A version n may be followed by a ~ to indicate it's inferior to n
A version n may be followed by a + to indicate it's superior to n
Examples:
1.2.3~dev < 1.2.3 < 1.2.3+dev
7~dev < 7 < 7+dev
......@@ -160,7 +160,7 @@ def c_changelog(args):
'''
repoman = load_repositories(args)
for image, repo in get_images(args.pattern, repoman, min=1):
image.changelog.show(int(image.version), args.all_version)
image.changelog.show(image.version, args.all_version)
def c_check(args):
'''
......
......@@ -69,7 +69,7 @@ class Image(object):
'''
Check if @buf is a valid image version
'''
if re.match("^\d+$", buf) is None:
if re.match("^\d+(\.\d+)*(([~+]).*)?$", buf) is None:
raise ISError(u"Invalid image version %s" % buf)
@staticmethod
......@@ -1340,9 +1340,9 @@ class Changelog(dict):
if line.lstrip().startswith("#"):
continue
# try to match a new version
m = re.match("\[(\d+)\]", line.lstrip())
m = re.match("\[(\d+(?:\.\d+)*)(?:([~+]).*)?\]", line.lstrip())
if m is not None:
version = int(m.group(1))
version = m.group(1)
self[version] = []
continue
# if line are out of a version => invalid format
......
......@@ -196,14 +196,15 @@ class Repository(object):
def last(self, name):
'''
Return last version of name in repo or -1 if not found
Return last version of name in repo or None if not found
'''
r = self.db.ask("SELECT version FROM image WHERE name = ? ORDER BY version DESC LIMIT 1", (name,)).fetchone()
r = self.db.ask("SELECT version FROM image WHERE name = ?", (name,)).fetchall()
# no row => no way
if r is None:
return -1
return None
f = lambda x,y: x[0] if istools.compare_versions(x[0], y[0]) > 0 else y[0]
# return last
return r[0]
return reduce(f, r)
def add(self, image, delete=False):
'''
......@@ -455,7 +456,7 @@ class Repository(object):
# is no version take the last
if version is None:
version = self.last(name)
if version < 0:
if version is None:
raise ISError(u"Unable to find image %s in %s" % (name,
self.config.name))
# get file md5 from db
......
......@@ -82,7 +82,7 @@ arrow(u"hostname: %s" % namespace.hostname)
createdb = u"""
CREATE TABLE image (md5 TEXT NOT NULL PRIMARY KEY,
name TEXT NOT NULL,
version INTEGER NOT NULL,
version TEXT NOT NULL,
date INTEGER NOT NULL,
author TEXT,
description TEXT,
......
......@@ -589,28 +589,82 @@ def compare_versions(v1, v2):
return > 0 if v1 > v2
return < 0 if v2 > v1
return = 0 if v1 == v2
This uses the Debian package version sorting algorithm (see 'man deb-version')
'''
def get_ver(version):
'''Return float version'''
if type(version) is int or type(version) is float:
return float(version)
elif isinstance(version, basestring):
iv = re.match("^(\d+)(?:([-~+]).*)?$", version)
if iv is None:
raise TypeError(u"Invalid version format: %s" % version)
rv = float(iv.group(1))
if iv.group(2) == "~":
rv -= 0.1
# Ensure versions have the right format
for version in v1, v2:
iv = re.match("^(\d+(?:\.\d+)*)(?:([~+]).*)?$", str(version))
if iv is None:
raise TypeError(u"Invalid version format: %s" % version)
digitregex = re.compile(r'^([0-9]*)(.*)$')
nondigitregex = re.compile(r'^([^0-9]*)(.*)$')
digits = True
while v1 or v2:
pattern = digitregex if digits else nondigitregex
sub_v1, v1 = pattern.findall(str(v1))[0]
sub_v2, v2 = pattern.findall(str(v2))[0]
if digits:
sub_v1 = int(sub_v1 if sub_v1 else 0)
sub_v2 = int(sub_v2 if sub_v2 else 0)
if sub_v1 < sub_v2:
rv = -1
elif sub_v1 > sub_v2:
rv = 1
else:
rv += 0.1
return rv
rv = 0
if rv != 0:
return rv
else:
raise TypeError(u"Invalid version format: %s" % version)
rv = strvercmp(sub_v1, sub_v2)
if rv != 0:
return rv
digits = not digits
return 0
def strvercmp(lhs, rhs):
'''
Compare string part of a version number
'''
size = max(len(lhs), len(rhs))
lhs_array = str_version_array(lhs, size)
rhs_array = str_version_array(rhs, size)
if lhs_array > rhs_array:
return 1
elif lhs_array < rhs_array:
return -1
else:
return 0
def str_version_array(str_version, size):
'''
Turns a string into an array of numeric values kind-of corresponding to
the ASCII numeric values of the characters in the string. I say 'kind-of'
because any character which is not an alphabetic character will be
it's ASCII value + 256, and the tilde (~) character will have the value
-1.
fv1 = get_ver(v1)
fv2 = get_ver(v2)
return fv1 - fv2
Additionally, the +size+ parameter specifies how long the array needs to
be; any elements in the array beyond the length of the string will be 0.
This method has massive ASCII assumptions. Use with caution.
'''
a = [0] * size
for i, char in enumerate(str_version):
char = ord(char)
if ((char >= ord('a') and char <= ord('z')) or
(char >= ord('A') and char <= ord('Z'))):
a[i] = char
elif char == ord('~'):
a[i] = -1
else:
a[i] = char + 256
return a
def get_compressor_path(name, compress=True, level=None):
'''
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment