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()