Statistics
| Branch: | Tag: | Revision:

root / devflow / versioning.py @ 866bb9c1

History | View | Annotate | Download (14.7 kB)

1 55775645 Vangelis Koukis
#!/usr/bin/env python
2 55775645 Vangelis Koukis
#
3 55775645 Vangelis Koukis
# Copyright (C) 2010, 2011, 2012 GRNET S.A. All rights reserved.
4 55775645 Vangelis Koukis
#
5 55775645 Vangelis Koukis
# Redistribution and use in source and binary forms, with or
6 55775645 Vangelis Koukis
# without modification, are permitted provided that the following
7 55775645 Vangelis Koukis
# conditions are met:
8 55775645 Vangelis Koukis
#
9 55775645 Vangelis Koukis
#   1. Redistributions of source code must retain the above
10 55775645 Vangelis Koukis
#      copyright notice, this list of conditions and the following
11 55775645 Vangelis Koukis
#      disclaimer.
12 55775645 Vangelis Koukis
#
13 55775645 Vangelis Koukis
#   2. Redistributions in binary form must reproduce the above
14 55775645 Vangelis Koukis
#      copyright notice, this list of conditions and the following
15 55775645 Vangelis Koukis
#      disclaimer in the documentation and/or other materials
16 55775645 Vangelis Koukis
#      provided with the distribution.
17 55775645 Vangelis Koukis
#
18 55775645 Vangelis Koukis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
19 55775645 Vangelis Koukis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 55775645 Vangelis Koukis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 55775645 Vangelis Koukis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
22 55775645 Vangelis Koukis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 55775645 Vangelis Koukis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 55775645 Vangelis Koukis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
25 55775645 Vangelis Koukis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 55775645 Vangelis Koukis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 55775645 Vangelis Koukis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 55775645 Vangelis Koukis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 55775645 Vangelis Koukis
# POSSIBILITY OF SUCH DAMAGE.
30 55775645 Vangelis Koukis
#
31 55775645 Vangelis Koukis
# The views and conclusions contained in the software and
32 55775645 Vangelis Koukis
# documentation are those of the authors and should not be
33 55775645 Vangelis Koukis
# interpreted as representing official policies, either expressed
34 55775645 Vangelis Koukis
# or implied, of GRNET S.A.
35 55775645 Vangelis Koukis
36 55775645 Vangelis Koukis
37 55775645 Vangelis Koukis
import os
38 55775645 Vangelis Koukis
import re
39 55775645 Vangelis Koukis
import sys
40 55775645 Vangelis Koukis
import pprint
41 55775645 Vangelis Koukis
import subprocess
42 55775645 Vangelis Koukis
import git
43 55775645 Vangelis Koukis
44 55775645 Vangelis Koukis
from distutils import log
45 55775645 Vangelis Koukis
from collections import namedtuple
46 55775645 Vangelis Koukis
47 55775645 Vangelis Koukis
48 55775645 Vangelis Koukis
# Branch types:
49 55775645 Vangelis Koukis
# builds_snapshot: Whether the branch can produce snapshot builds
50 55775645 Vangelis Koukis
# builds_release: Whether the branch can produce release builds
51 55775645 Vangelis Koukis
# versioned: Whether the name of the branch defines a specific version
52 55775645 Vangelis Koukis
# allowed_version_re: A regular expression describing allowed values for
53 55775645 Vangelis Koukis
#                     base_version in this branch
54 55775645 Vangelis Koukis
branch_type = namedtuple("branch_type", ["builds_snapshot", "builds_release",
55 55775645 Vangelis Koukis
                                         "versioned", "allowed_version_re"])
56 55775645 Vangelis Koukis
VERSION_RE = "[0-9]+\.[0-9]+"
57 55775645 Vangelis Koukis
BRANCH_TYPES = {
58 55775645 Vangelis Koukis
    "feature": branch_type(True, False, False, "^%snext$" % VERSION_RE),
59 55775645 Vangelis Koukis
    "develop": branch_type(True, False, False, "^%snext$" % VERSION_RE),
60 55775645 Vangelis Koukis
    "release": branch_type(True, True, True,
61 55775645 Vangelis Koukis
                           "^(?P<bverstr>%s)rc[1-9][0-9]*$" % VERSION_RE),
62 55775645 Vangelis Koukis
    "master": branch_type(False, True, False,
63 55775645 Vangelis Koukis
                          "^%s$" % VERSION_RE),
64 55775645 Vangelis Koukis
    "hotfix": branch_type(True, True, True,
65 55775645 Vangelis Koukis
                          "^(?P<bverstr>^%s\.[1-9][0-9]*)$" % VERSION_RE)}
66 55775645 Vangelis Koukis
BASE_VERSION_FILE = "version"
67 55775645 Vangelis Koukis
68 55775645 Vangelis Koukis
69 55775645 Vangelis Koukis
def get_commit_id(commit, current_branch):
70 55775645 Vangelis Koukis
    """Return the commit ID
71 55775645 Vangelis Koukis

72 55775645 Vangelis Koukis
    If the commit is a 'merge' commit, and one of the parents is a
73 55775645 Vangelis Koukis
    debian branch we return a compination of the parents commits.
74 55775645 Vangelis Koukis

75 55775645 Vangelis Koukis
    """
76 55775645 Vangelis Koukis
    def short_id(commit):
77 55775645 Vangelis Koukis
        return commit.hexsha[0:7]
78 55775645 Vangelis Koukis
79 55775645 Vangelis Koukis
    parents = commit.parents
80 55775645 Vangelis Koukis
    cur_br_name = current_branch.name
81 55775645 Vangelis Koukis
    if len(parents) == 1:
82 55775645 Vangelis Koukis
        return short_id(commit)
83 55775645 Vangelis Koukis
    elif len(parents) == 2:
84 55775645 Vangelis Koukis
        if cur_br_name.startswith("debian-") or cur_br_name == "debian":
85 55775645 Vangelis Koukis
            pr1, pr2 = parents
86 866bb9c1 Christos Stavrakakis
            return short_id(pr1) + "-" + short_id(pr2)
87 55775645 Vangelis Koukis
        else:
88 55775645 Vangelis Koukis
            return short_id(commit)
89 55775645 Vangelis Koukis
    else:
90 55775645 Vangelis Koukis
        raise RuntimeError("Commit %s has more than 2 parents!" % commit)
91 55775645 Vangelis Koukis
92 55775645 Vangelis Koukis
93 55775645 Vangelis Koukis
def vcs_info():
94 55775645 Vangelis Koukis
    """
95 55775645 Vangelis Koukis
    Return current git HEAD commit information.
96 55775645 Vangelis Koukis

97 55775645 Vangelis Koukis
    Returns a tuple containing
98 55775645 Vangelis Koukis
        - branch name
99 55775645 Vangelis Koukis
        - commit id
100 55775645 Vangelis Koukis
        - commit count
101 55775645 Vangelis Koukis
        - git describe output
102 55775645 Vangelis Koukis
        - path of git toplevel directory
103 55775645 Vangelis Koukis

104 55775645 Vangelis Koukis
    """
105 55775645 Vangelis Koukis
    try:
106 55775645 Vangelis Koukis
        repo = git.Repo(".")
107 55775645 Vangelis Koukis
        branch = repo.head.reference
108 55775645 Vangelis Koukis
        revid = get_commit_id(branch.commit, branch)
109 55775645 Vangelis Koukis
        revno = len(list(repo.iter_commits()))
110 55775645 Vangelis Koukis
        toplevel = repo.working_dir
111 55775645 Vangelis Koukis
    except git.InvalidGitRepositoryError:
112 55775645 Vangelis Koukis
        log.error("Could not retrieve git information. " +
113 55775645 Vangelis Koukis
                  "Current directory not a git repository?")
114 55775645 Vangelis Koukis
        return None
115 55775645 Vangelis Koukis
116 55775645 Vangelis Koukis
    info = namedtuple("vcs_info", ["branch", "revid", "revno",
117 866bb9c1 Christos Stavrakakis
                                   "toplevel"])
118 55775645 Vangelis Koukis
119 866bb9c1 Christos Stavrakakis
    return info(branch=branch.name, revid=revid, revno=revno,
120 55775645 Vangelis Koukis
                toplevel=toplevel)
121 55775645 Vangelis Koukis
122 55775645 Vangelis Koukis
123 55775645 Vangelis Koukis
def base_version(vcs_info):
124 55775645 Vangelis Koukis
    """Determine the base version from a file in the repository"""
125 55775645 Vangelis Koukis
126 55775645 Vangelis Koukis
    f = open(os.path.join(vcs_info.toplevel, BASE_VERSION_FILE))
127 55775645 Vangelis Koukis
    lines = [l.strip() for l in f.readlines()]
128 55775645 Vangelis Koukis
    l = [l for l in lines if not l.startswith("#")]
129 55775645 Vangelis Koukis
    if len(l) != 1:
130 55775645 Vangelis Koukis
        raise ValueError("File '%s' should contain a single non-comment line.")
131 55775645 Vangelis Koukis
    return l[0]
132 55775645 Vangelis Koukis
133 55775645 Vangelis Koukis
134 55775645 Vangelis Koukis
def build_mode():
135 55775645 Vangelis Koukis
    """Determine the build mode from the value of $GITFLOW_BUILD_MODE"""
136 55775645 Vangelis Koukis
    try:
137 55775645 Vangelis Koukis
        mode = os.environ["GITFLOW_BUILD_MODE"]
138 55775645 Vangelis Koukis
        assert mode == "release" or mode == "snapshot"
139 866bb9c1 Christos Stavrakakis
    except KeyError:
140 866bb9c1 Christos Stavrakakis
        raise ValueError("GITFLOW_BUILD_MODE environment variable is not set."
141 866bb9c1 Christos Stavrakakis
                         " Set this variable to 'release' or 'snapshot'")
142 866bb9c1 Christos Stavrakakis
    except AssertionError:
143 866bb9c1 Christos Stavrakakis
        raise ValueError("GITFLOW_BUILD_MODE environment variable must be"
144 866bb9c1 Christos Stavrakakis
                         " 'release' or 'snapshot'")
145 55775645 Vangelis Koukis
    return mode
146 55775645 Vangelis Koukis
147 55775645 Vangelis Koukis
148 55775645 Vangelis Koukis
def python_version(base_version, vcs_info, mode):
149 55775645 Vangelis Koukis
    """Generate a Python distribution version following devtools conventions.
150 55775645 Vangelis Koukis

151 55775645 Vangelis Koukis
    This helper generates a Python distribution version from a repository
152 55775645 Vangelis Koukis
    commit, following devtools conventions. The input data are:
153 55775645 Vangelis Koukis
        * base_version: a base version number, presumably stored in text file
154 55775645 Vangelis Koukis
          inside the repository, e.g., /version
155 55775645 Vangelis Koukis
        * vcs_info: vcs information: current branch name and revision no
156 55775645 Vangelis Koukis
        * mode: "snapshot", or "release"
157 55775645 Vangelis Koukis

158 55775645 Vangelis Koukis
    This helper assumes a git branching model following:
159 55775645 Vangelis Koukis
    http://nvie.com/posts/a-successful-git-branching-model/
160 55775645 Vangelis Koukis

161 55775645 Vangelis Koukis
    with 'master', 'develop', 'release-X', 'hotfix-X' and 'feature-X' branches.
162 55775645 Vangelis Koukis

163 55775645 Vangelis Koukis
    General rules:
164 55775645 Vangelis Koukis
    a) any repository commit can get as a Python version
165 55775645 Vangelis Koukis
    b) a version is generated either in 'release' or in 'snapshot' mode
166 55775645 Vangelis Koukis
    c) the choice of mode depends on the branch, see following table.
167 55775645 Vangelis Koukis

168 55775645 Vangelis Koukis
    A python version is of the form A_NNN,
169 55775645 Vangelis Koukis
    where A: X.Y.Z{,next,rcW} and NNN: a revision number for the commit,
170 55775645 Vangelis Koukis
    as returned by vcs_info().
171 55775645 Vangelis Koukis

172 55775645 Vangelis Koukis
    For every combination of branch and mode, releases are numbered as follows:
173 55775645 Vangelis Koukis

174 55775645 Vangelis Koukis
    BRANCH:  /  MODE: snapshot        release
175 55775645 Vangelis Koukis
    --------          ------------------------------
176 55775645 Vangelis Koukis
    feature           0.14next_150    N/A
177 55775645 Vangelis Koukis
    develop           0.14next_151    N/A
178 55775645 Vangelis Koukis
    release           0.14rc2_249     0.14rc2
179 55775645 Vangelis Koukis
    master            N/A             0.14
180 55775645 Vangelis Koukis
    hotfix            0.14.1rc6_121   0.14.1rc6
181 55775645 Vangelis Koukis
                      N/A             0.14.1
182 55775645 Vangelis Koukis

183 55775645 Vangelis Koukis
    The suffix 'next' in a version name is used to denote the upcoming version,
184 55775645 Vangelis Koukis
    the one being under development in the develop and release branches.
185 55775645 Vangelis Koukis
    Version '0.14next' is the version following 0.14, and only lives on the
186 55775645 Vangelis Koukis
    develop and feature branches.
187 55775645 Vangelis Koukis

188 55775645 Vangelis Koukis
    The suffix 'rc' is used to denote release candidates. 'rc' versions live
189 55775645 Vangelis Koukis
    only in release and hotfix branches.
190 55775645 Vangelis Koukis

191 55775645 Vangelis Koukis
    Suffixes 'next' and 'rc' have been chosen to ensure proper ordering
192 55775645 Vangelis Koukis
    according to setuptools rules:
193 55775645 Vangelis Koukis

194 55775645 Vangelis Koukis
        http://www.python.org/dev/peps/pep-0386/#setuptools
195 55775645 Vangelis Koukis

196 55775645 Vangelis Koukis
    Every branch uses a value for A so that all releases are ordered based
197 55775645 Vangelis Koukis
    on the branch they came from, so:
198 55775645 Vangelis Koukis

199 55775645 Vangelis Koukis
    So
200 55775645 Vangelis Koukis
        0.13next < 0.14rcW < 0.14 < 0.14next < 0.14.1
201 55775645 Vangelis Koukis

202 55775645 Vangelis Koukis
    and
203 55775645 Vangelis Koukis

204 55775645 Vangelis Koukis
    >>> V("0.14next") > V("0.14")
205 55775645 Vangelis Koukis
    True
206 55775645 Vangelis Koukis
    >>> V("0.14next") > V("0.14rc7")
207 55775645 Vangelis Koukis
    True
208 55775645 Vangelis Koukis
    >>> V("0.14next") > V("0.14.1")
209 55775645 Vangelis Koukis
    False
210 55775645 Vangelis Koukis
    >>> V("0.14rc6") > V("0.14")
211 55775645 Vangelis Koukis
    False
212 55775645 Vangelis Koukis
    >>> V("0.14.2rc6") > V("0.14.1")
213 55775645 Vangelis Koukis
    True
214 55775645 Vangelis Koukis

215 55775645 Vangelis Koukis
    The value for _NNN is chosen based of the revision number of the specific
216 55775645 Vangelis Koukis
    commit. It is used to ensure ascending ordering of consecutive releases
217 55775645 Vangelis Koukis
    from the same branch. Every version of the form A_NNN comes *before*
218 55775645 Vangelis Koukis
    than A: All snapshots are ordered so they come before the corresponding
219 55775645 Vangelis Koukis
    release.
220 55775645 Vangelis Koukis

221 55775645 Vangelis Koukis
    So
222 55775645 Vangelis Koukis
        0.14next_* < 0.14
223 55775645 Vangelis Koukis
        0.14.1_* < 0.14.1
224 55775645 Vangelis Koukis
        etc
225 55775645 Vangelis Koukis

226 55775645 Vangelis Koukis
    and
227 55775645 Vangelis Koukis

228 55775645 Vangelis Koukis
    >>> V("0.14next_150") < V("0.14next")
229 55775645 Vangelis Koukis
    True
230 55775645 Vangelis Koukis
    >>> V("0.14.1next_150") < V("0.14.1next")
231 55775645 Vangelis Koukis
    True
232 55775645 Vangelis Koukis
    >>> V("0.14.1_149") < V("0.14.1")
233 55775645 Vangelis Koukis
    True
234 55775645 Vangelis Koukis
    >>> V("0.14.1_149") < V("0.14.1_150")
235 55775645 Vangelis Koukis
    True
236 55775645 Vangelis Koukis

237 55775645 Vangelis Koukis
    Combining both of the above, we get
238 55775645 Vangelis Koukis
       0.13next_* < 0.13next < 0.14rcW_* < 0.14rcW < 0.14_* < 0.14
239 55775645 Vangelis Koukis
       < 0.14next_* < 0.14next < 0.14.1_* < 0.14.1
240 55775645 Vangelis Koukis

241 55775645 Vangelis Koukis
    and
242 55775645 Vangelis Koukis

243 55775645 Vangelis Koukis
    >>> V("0.13next_102") < V("0.13next")
244 55775645 Vangelis Koukis
    True
245 55775645 Vangelis Koukis
    >>> V("0.13next") < V("0.14rc5_120")
246 55775645 Vangelis Koukis
    True
247 55775645 Vangelis Koukis
    >>> V("0.14rc3_120") < V("0.14rc3")
248 55775645 Vangelis Koukis
    True
249 55775645 Vangelis Koukis
    >>> V("0.14rc3") < V("0.14_1")
250 55775645 Vangelis Koukis
    True
251 55775645 Vangelis Koukis
    >>> V("0.14_120") < V("0.14")
252 55775645 Vangelis Koukis
    True
253 55775645 Vangelis Koukis
    >>> V("0.14") < V("0.14next_20")
254 55775645 Vangelis Koukis
    True
255 55775645 Vangelis Koukis
    >>> V("0.14next_20") < V("0.14next")
256 55775645 Vangelis Koukis
    True
257 55775645 Vangelis Koukis

258 55775645 Vangelis Koukis
    Note: one of the tests above fails because of constraints in the way
259 55775645 Vangelis Koukis
    setuptools parses version numbers. It does not affect us because the
260 55775645 Vangelis Koukis
    specific version format that triggers the problem is not contained in the
261 55775645 Vangelis Koukis
    table showing allowed branch / mode combinations, above.
262 55775645 Vangelis Koukis

263 55775645 Vangelis Koukis

264 55775645 Vangelis Koukis
    """
265 55775645 Vangelis Koukis
266 55775645 Vangelis Koukis
    branch = vcs_info.branch
267 55775645 Vangelis Koukis
268 55775645 Vangelis Koukis
    # If it's a debian branch, ignore starting "debian-"
269 55775645 Vangelis Koukis
    brnorm = branch
270 55775645 Vangelis Koukis
    if brnorm == "debian":
271 55775645 Vangelis Koukis
        brnorm = "debian-master"
272 55775645 Vangelis Koukis
    if brnorm.startswith("debian-"):
273 55775645 Vangelis Koukis
        brnorm = brnorm.replace("debian-", "", 1)
274 55775645 Vangelis Koukis
275 55775645 Vangelis Koukis
    # Sanity checks
276 55775645 Vangelis Koukis
    if "-" in brnorm:
277 55775645 Vangelis Koukis
        btypestr = brnorm.split("-")[0]
278 55775645 Vangelis Koukis
    else:
279 55775645 Vangelis Koukis
        btypestr = brnorm
280 55775645 Vangelis Koukis
281 55775645 Vangelis Koukis
    try:
282 55775645 Vangelis Koukis
        btype = BRANCH_TYPES[btypestr]
283 55775645 Vangelis Koukis
    except KeyError:
284 55775645 Vangelis Koukis
        allowed_branches = ", ".join(x for x in BRANCH_TYPES.keys())
285 55775645 Vangelis Koukis
        raise ValueError("Malformed branch name '%s', cannot classify as one "
286 55775645 Vangelis Koukis
                         "of %s" % (btypestr, allowed_branches))
287 55775645 Vangelis Koukis
288 55775645 Vangelis Koukis
    if btype.versioned:
289 55775645 Vangelis Koukis
        try:
290 55775645 Vangelis Koukis
            bverstr = brnorm.split("-")[1]
291 55775645 Vangelis Koukis
        except IndexError:
292 55775645 Vangelis Koukis
            # No version
293 55775645 Vangelis Koukis
            raise ValueError("Branch name '%s' should contain version" %
294 55775645 Vangelis Koukis
                             branch)
295 55775645 Vangelis Koukis
296 55775645 Vangelis Koukis
        # Check that version is well-formed
297 55775645 Vangelis Koukis
        if not re.match(VERSION_RE, bverstr):
298 55775645 Vangelis Koukis
            raise ValueError("Malformed version '%s' in branch name '%s'" %
299 55775645 Vangelis Koukis
                             (bverstr, branch))
300 55775645 Vangelis Koukis
301 55775645 Vangelis Koukis
    m = re.match(btype.allowed_version_re, base_version)
302 55775645 Vangelis Koukis
    if not m or (btype.versioned and m.groupdict()["bverstr"] != bverstr):
303 55775645 Vangelis Koukis
        raise ValueError("Base version '%s' unsuitable for branch name '%s'" %
304 55775645 Vangelis Koukis
                         (base_version, branch))
305 55775645 Vangelis Koukis
306 55775645 Vangelis Koukis
    if mode not in ["snapshot", "release"]:
307 55775645 Vangelis Koukis
        raise ValueError("Specified mode '%s' should be one of 'snapshot' or "
308 55775645 Vangelis Koukis
                         "'release'" % mode)
309 55775645 Vangelis Koukis
    snap = (mode == "snapshot")
310 55775645 Vangelis Koukis
311 55775645 Vangelis Koukis
    if ((snap and not btype.builds_snapshot) or
312 55775645 Vangelis Koukis
        (not snap and not btype.builds_release)):
313 55775645 Vangelis Koukis
        raise ValueError("Invalid mode '%s' in branch type '%s'" %
314 55775645 Vangelis Koukis
                         (mode, btypestr))
315 55775645 Vangelis Koukis
316 55775645 Vangelis Koukis
    if snap:
317 55775645 Vangelis Koukis
        v = "%s_%d_%s" % (base_version, vcs_info.revno, vcs_info.revid)
318 55775645 Vangelis Koukis
    else:
319 55775645 Vangelis Koukis
        v = base_version
320 55775645 Vangelis Koukis
    return v
321 55775645 Vangelis Koukis
322 55775645 Vangelis Koukis
323 55775645 Vangelis Koukis
def debian_version_from_python_version(pyver):
324 55775645 Vangelis Koukis
    """Generate a debian package version from a Python version.
325 55775645 Vangelis Koukis

326 55775645 Vangelis Koukis
    This helper generates a Debian package version from a Python version,
327 55775645 Vangelis Koukis
    following devtools conventions.
328 55775645 Vangelis Koukis

329 55775645 Vangelis Koukis
    Debian sorts version strings differently compared to setuptools:
330 55775645 Vangelis Koukis
    http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-f-Version
331 55775645 Vangelis Koukis

332 55775645 Vangelis Koukis
    Initial tests:
333 55775645 Vangelis Koukis

334 55775645 Vangelis Koukis
    >>> debian_version("3") < debian_version("6")
335 55775645 Vangelis Koukis
    True
336 55775645 Vangelis Koukis
    >>> debian_version("3") < debian_version("2")
337 55775645 Vangelis Koukis
    False
338 55775645 Vangelis Koukis
    >>> debian_version("1") == debian_version("1")
339 55775645 Vangelis Koukis
    True
340 55775645 Vangelis Koukis
    >>> debian_version("1") != debian_version("1")
341 55775645 Vangelis Koukis
    False
342 55775645 Vangelis Koukis
    >>> debian_version("1") >= debian_version("1")
343 55775645 Vangelis Koukis
    True
344 55775645 Vangelis Koukis
    >>> debian_version("1") <= debian_version("1")
345 55775645 Vangelis Koukis
    True
346 55775645 Vangelis Koukis

347 55775645 Vangelis Koukis
    This helper defines a 1-1 mapping between Python and Debian versions,
348 55775645 Vangelis Koukis
    with the same ordering.
349 55775645 Vangelis Koukis

350 55775645 Vangelis Koukis
    Debian versions are ordered in the same way as Python versions:
351 55775645 Vangelis Koukis

352 55775645 Vangelis Koukis
    >>> D("0.14next") > D("0.14")
353 55775645 Vangelis Koukis
    True
354 55775645 Vangelis Koukis
    >>> D("0.14next") > D("0.14rc7")
355 55775645 Vangelis Koukis
    True
356 55775645 Vangelis Koukis
    >>> D("0.14next") > D("0.14.1")
357 55775645 Vangelis Koukis
    False
358 55775645 Vangelis Koukis
    >>> D("0.14rc6") > D("0.14")
359 55775645 Vangelis Koukis
    False
360 55775645 Vangelis Koukis
    >>> D("0.14.2rc6") > D("0.14.1")
361 55775645 Vangelis Koukis
    True
362 55775645 Vangelis Koukis

363 55775645 Vangelis Koukis
    and
364 55775645 Vangelis Koukis

365 55775645 Vangelis Koukis
    >>> D("0.14next_150") < D("0.14next")
366 55775645 Vangelis Koukis
    True
367 55775645 Vangelis Koukis
    >>> D("0.14.1next_150") < D("0.14.1next")
368 55775645 Vangelis Koukis
    True
369 55775645 Vangelis Koukis
    >>> D("0.14.1_149") < D("0.14.1")
370 55775645 Vangelis Koukis
    True
371 55775645 Vangelis Koukis
    >>> D("0.14.1_149") < D("0.14.1_150")
372 55775645 Vangelis Koukis
    True
373 55775645 Vangelis Koukis

374 55775645 Vangelis Koukis
    and
375 55775645 Vangelis Koukis

376 55775645 Vangelis Koukis
    >>> D("0.13next_102") < D("0.13next")
377 55775645 Vangelis Koukis
    True
378 55775645 Vangelis Koukis
    >>> D("0.13next") < D("0.14rc5_120")
379 55775645 Vangelis Koukis
    True
380 55775645 Vangelis Koukis
    >>> D("0.14rc3_120") < D("0.14rc3")
381 55775645 Vangelis Koukis
    True
382 55775645 Vangelis Koukis
    >>> D("0.14rc3") < D("0.14_1")
383 55775645 Vangelis Koukis
    True
384 55775645 Vangelis Koukis
    >>> D("0.14_120") < D("0.14")
385 55775645 Vangelis Koukis
    True
386 55775645 Vangelis Koukis
    >>> D("0.14") < D("0.14next_20")
387 55775645 Vangelis Koukis
    True
388 55775645 Vangelis Koukis
    >>> D("0.14next_20") < D("0.14next")
389 55775645 Vangelis Koukis
    True
390 55775645 Vangelis Koukis

391 55775645 Vangelis Koukis
    """
392 55775645 Vangelis Koukis
    return pyver.replace("_", "~").replace("rc", "~rc") + "-1"
393 55775645 Vangelis Koukis
394 55775645 Vangelis Koukis
395 866bb9c1 Christos Stavrakakis
def get_python_version():
396 866bb9c1 Christos Stavrakakis
    v = vcs_info()
397 866bb9c1 Christos Stavrakakis
    b = base_version(v)
398 866bb9c1 Christos Stavrakakis
    mode = build_mode()
399 866bb9c1 Christos Stavrakakis
    return python_version(b, v, mode)
400 866bb9c1 Christos Stavrakakis
401 866bb9c1 Christos Stavrakakis
402 55775645 Vangelis Koukis
def debian_version(base_version, vcs_info, mode):
403 55775645 Vangelis Koukis
    p = python_version(base_version, vcs_info, mode)
404 55775645 Vangelis Koukis
    return debian_version_from_python_version(p)
405 55775645 Vangelis Koukis
406 55775645 Vangelis Koukis
407 866bb9c1 Christos Stavrakakis
def get_debian_version():
408 866bb9c1 Christos Stavrakakis
    v = vcs_info()
409 866bb9c1 Christos Stavrakakis
    b = base_version(v)
410 866bb9c1 Christos Stavrakakis
    mode = build_mode()
411 866bb9c1 Christos Stavrakakis
    return debian_version(b, v, mode)
412 866bb9c1 Christos Stavrakakis
413 866bb9c1 Christos Stavrakakis
414 55775645 Vangelis Koukis
def user_info():
415 55775645 Vangelis Koukis
    import getpass
416 55775645 Vangelis Koukis
    import socket
417 55775645 Vangelis Koukis
    return "%s@%s" % (getpass.getuser(), socket.getfqdn())
418 55775645 Vangelis Koukis
419 55775645 Vangelis Koukis
420 55775645 Vangelis Koukis
def update_version(module, name="version", root="."):
421 55775645 Vangelis Koukis
    """
422 55775645 Vangelis Koukis
    Generate or replace version.py as a submodule of `module`.
423 55775645 Vangelis Koukis

424 55775645 Vangelis Koukis
    This is a helper to generate/replace a version.py file containing version
425 55775645 Vangelis Koukis
    information as a submodule of passed `module`.
426 55775645 Vangelis Koukis

427 55775645 Vangelis Koukis
    """
428 55775645 Vangelis Koukis
429 866bb9c1 Christos Stavrakakis
    paths = [root] + module.split(".") + ["%s.py" % name]
430 866bb9c1 Christos Stavrakakis
    module_filename = os.path.join(*paths)
431 866bb9c1 Christos Stavrakakis
432 55775645 Vangelis Koukis
    v = vcs_info()
433 55775645 Vangelis Koukis
    if not v:
434 55775645 Vangelis Koukis
        # Return early if not in development environment
435 866bb9c1 Christos Stavrakakis
        log.error("Can not compute version outside of a git repository."
436 866bb9c1 Christos Stavrakakis
                  " Will not update %s version file" % module_filename)
437 55775645 Vangelis Koukis
        return
438 55775645 Vangelis Koukis
    b = base_version(v)
439 55775645 Vangelis Koukis
    mode = build_mode()
440 55775645 Vangelis Koukis
    version = python_version(b, v, mode)
441 55775645 Vangelis Koukis
    content = """
442 55775645 Vangelis Koukis
__version__ = "%(version)s"
443 55775645 Vangelis Koukis
__version_info__ = %(version_info)s
444 55775645 Vangelis Koukis
__version_vcs_info__ = %(vcs_info)s
445 55775645 Vangelis Koukis
__version_user_info__ = "%(user_info)s"
446 866bb9c1 Christos Stavrakakis
""" % dict(version=version, version_info=version.split("."),
447 55775645 Vangelis Koukis
               vcs_info=pprint.PrettyPrinter().pformat(dict(v._asdict())),
448 55775645 Vangelis Koukis
               user_info=user_info())
449 55775645 Vangelis Koukis
450 55775645 Vangelis Koukis
    module_file = file(module_filename, "w+")
451 55775645 Vangelis Koukis
    module_file.write(content)
452 55775645 Vangelis Koukis
    module_file.close()
453 866bb9c1 Christos Stavrakakis
    return module_filename
454 55775645 Vangelis Koukis
455 55775645 Vangelis Koukis
456 866bb9c1 Christos Stavrakakis
def main():
457 55775645 Vangelis Koukis
    v = vcs_info()
458 55775645 Vangelis Koukis
    b = base_version(v)
459 55775645 Vangelis Koukis
    mode = build_mode()
460 55775645 Vangelis Koukis
461 55775645 Vangelis Koukis
    try:
462 55775645 Vangelis Koukis
        arg = sys.argv[1]
463 55775645 Vangelis Koukis
        assert arg == "python" or arg == "debian"
464 55775645 Vangelis Koukis
    except IndexError:
465 55775645 Vangelis Koukis
        raise ValueError("A single argument, 'python' or 'debian is required")
466 55775645 Vangelis Koukis
467 55775645 Vangelis Koukis
    if arg == "python":
468 55775645 Vangelis Koukis
        print python_version(b, v, mode)
469 55775645 Vangelis Koukis
    elif arg == "debian":
470 55775645 Vangelis Koukis
        print debian_version(b, v, mode)
471 866bb9c1 Christos Stavrakakis
472 866bb9c1 Christos Stavrakakis
if __name__ == "__main__":
473 866bb9c1 Christos Stavrakakis
    sys.exit(main())