summaryrefslogtreecommitdiffstats
path: root/packaging/release/versionhelper/version_helper.py
blob: 163494b6aebcc674e2a0a10d30aaf672b080296d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
from __future__ import absolute_import, division, print_function
__metaclass__ = type

import argparse
import os
import re
import sys

from packaging.version import Version, VERSION_PATTERN


class AnsibleVersionMunger(object):
    tag_offsets = dict(
        dev=0,
        a=100,
        b=200,
        rc=1000
    )

    # TODO: allow overrides here for packaging bump etc
    def __init__(self, raw_version, revision=None, codename=None):
        self._raw_version = raw_version
        self._revision = revision
        self._parsed_version = Version(raw_version)
        self._codename = codename
        self._parsed_regex_match = re.match(VERSION_PATTERN, raw_version, re.VERBOSE | re.IGNORECASE)

    @property
    def deb_version(self):
        v = self._parsed_version

        match = self._parsed_regex_match

        # treat dev/post as prerelease for now; treat dev/post as equivalent and disallow together
        if v.is_prerelease or match.group('dev') or match.group('post'):
            if match.group('dev') and match.group('post'):
                raise Exception("dev and post may not currently be used together")
            if match.group('pre'):
                tag_value = match.group('pre')
                tag_type = match.group('pre_l')
                if match.group('dev'):
                    tag_value += ('~%s' % match.group('dev').strip('.'))
                if match.group('post'):
                    tag_value += ('~%s' % match.group('post').strip('.'))
            elif match.group('dev'):
                tag_type = "dev"
                tag_value = match.group('dev').strip('.')
            elif match.group('post'):
                tag_type = "dev"
                tag_value = match.group('post').strip('.')
            else:
                raise Exception("unknown prerelease type for version {0}".format(self._raw_version))
        else:
            tag_type = None
            tag_value = ''

        # not a pre/post/dev release, just return base version
        if not tag_type:
            return '{base_version}'.format(base_version=self.base_version)

        # it is a pre/dev release, include the tag value with a ~
        return '{base_version}~{tag_value}'.format(base_version=self.base_version, tag_value=tag_value)

    @property
    def deb_release(self):
        return '1' if self._revision is None else str(self._revision)

    @property
    def rpm_release(self):
        v = self._parsed_version
        match = self._parsed_regex_match

        # treat presence of dev/post as prerelease for now; treat dev/post the same and disallow together
        if v.is_prerelease or match.group('dev') or match.group('post'):
            if match.group('dev') and match.group('post'):
                raise Exception("dev and post may not currently be used together")
            if match.group('pre'):
                tag_value = match.group('pre')
                tag_type = match.group('pre_l')
                tag_ver = match.group('pre_n')
                if match.group('dev'):
                    tag_value += match.group('dev')
                if match.group('post'):
                    tag_value += match.group('post')
            elif match.group('dev'):
                tag_type = "dev"
                tag_value = match.group('dev')
                tag_ver = match.group('dev_n')
            elif match.group('post'):
                tag_type = "dev"
                tag_value = match.group('post')
                tag_ver = match.group('post_n')
            else:
                raise Exception("unknown prerelease type for version {0}".format(self._raw_version))
        else:
            tag_type = None
            tag_value = ''
            tag_ver = 0

        # not a pre/post/dev release, just append revision (default 1)
        if not tag_type:
            if self._revision is None:
                self._revision = 1
            return '{revision}'.format(revision=self._revision)

        # cleanse tag value in case it starts with .
        tag_value = tag_value.strip('.')

        # coerce to int and None == 0
        tag_ver = int(tag_ver if tag_ver else 0)

        if self._revision is None:
            tag_offset = self.tag_offsets.get(tag_type)
            if tag_offset is None:
                raise Exception('no tag offset defined for tag {0}'.format(tag_type))
            pkgrel = '0.{0}'.format(tag_offset + tag_ver)
        else:
            pkgrel = self._revision

        return '{pkgrel}.{tag_value}'.format(pkgrel=pkgrel, tag_value=tag_value)

    @property
    def raw(self):
        return self._raw_version

    # return the x.y.z version without any other modifiers present
    @property
    def base_version(self):
        return self._parsed_version.base_version

    # return the x.y version without any other modifiers present
    @property
    def major_version(self):
        return re.match(r'^(\d+.\d+)', self._raw_version).group(1)

    @property
    def codename(self):
        return self._codename if self._codename else "UNKNOWN"


def main():
    parser = argparse.ArgumentParser(description='Extract/transform Ansible versions to various packaging formats')

    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('--raw', action='store_true')
    group.add_argument('--majorversion', action='store_true')
    group.add_argument('--baseversion', action='store_true')
    group.add_argument('--debversion', action='store_true')
    group.add_argument('--debrelease', action='store_true')
    group.add_argument('--rpmrelease', action='store_true')
    group.add_argument('--codename', action='store_true')
    group.add_argument('--all', action='store_true')

    parser.add_argument('--revision', action='store', default='auto')

    args = parser.parse_args()

    mydir = os.path.dirname(__file__)
    release_loc = os.path.normpath(mydir + '/../../../lib')

    sys.path.insert(0, release_loc)

    from ansible import release

    rev = None
    if args.revision != 'auto':
        rev = args.revision

    v_raw = release.__version__
    codename = release.__codename__
    v = AnsibleVersionMunger(v_raw, revision=rev, codename=codename)

    if args.raw:
        print(v.raw)
    elif args.baseversion:
        print(v.base_version)
    elif args.majorversion:
        print(v.major_version)
    elif args.debversion:
        print(v.deb_version)
    elif args.debrelease:
        print(v.deb_release)
    elif args.rpmrelease:
        print(v.rpm_release)
    elif args.codename:
        print(v.codename)
    elif args.all:
        props = [name for (name, impl) in vars(AnsibleVersionMunger).items() if isinstance(impl, property)]

        for propname in props:
            print('{0}: {1}'.format(propname, getattr(v, propname)))


if __name__ == '__main__':
    main()