Created
January 26, 2014 17:42
-
-
Save mbrgm/8636481 to your computer and use it in GitHub Desktop.
Offers a way to get the current version in a git repository by finding the latest version tag and counting the number of commits since then, if necessary. The version tags are required to begin with a leading 'v'. The version format is <major>.<minor>.<patch>[-<prerelease-type>.<prerelease-number>][+<commit-number>].
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import sys | |
import os | |
import re | |
import plistlib | |
from subprocess import check_output | |
class Version: | |
_REGEX = re.compile( | |
'^v?(?P<major>[0-9]+)' | |
'\.(?P<minor>[0-9]+)' | |
'\.(?P<patch>[0-9]+)' | |
'(\-(?P<release_type>(alpha|beta|rc))\.' | |
'(?P<prerelease_number>[0-9]+))?' | |
'(\+(?P<commit_number>[0-9]+))?$') | |
RELEASE_TYPES = ['alpha', 'beta', 'rc', 'public'] | |
def __init__(self, | |
major, | |
minor, | |
patch, | |
release_type='public', | |
prerelease_number=None, | |
commit_number=None): | |
self.major = int(major) | |
self.minor = int(minor) | |
self.patch = int(patch) | |
if release_type: | |
self.release_type = release_type | |
else: | |
self.release_type = 'public' | |
if prerelease_number and self.release_type != 'public': | |
self.prerelease_number = int(prerelease_number) | |
else: | |
self.prerelease_number = None | |
if commit_number: | |
self.commit_number = int(commit_number) | |
else: | |
try: | |
self.commit_number = self.get_commit_number() | |
except: | |
self.commit_number = None | |
def get_commit_number(self): | |
# Get commits since tagged commit | |
tag = self.get_tag() | |
devnull = open(os.devnull, 'w') | |
output = check_output(['git', | |
'rev-list', | |
tag + '..HEAD'], stderr=devnull).strip() | |
output_lines = output.split('\n') | |
commits = filter(lambda x: x, output_lines) | |
return len(commits) | |
def get_tag(self): | |
return 'v' + self.get_version_string_without_commit() | |
@classmethod | |
def from_string(cls, string): | |
# Parse version components using regex | |
match = cls._REGEX.match(string.strip()) | |
v = match.groupdict() | |
version = Version(major=v['major'], | |
minor=v['minor'], | |
patch=v['patch'], | |
release_type=v['release_type'], | |
prerelease_number=v['prerelease_number']) | |
return version | |
def get_version_string_without_commit(self): | |
# Build version string | |
version_string = "%d.%d.%d" % (self.major, | |
self.minor, | |
self.patch) | |
if self.release_type != 'public': | |
version_string += "-%s.%d" % (self.release_type, | |
self.prerelease_number) | |
return version_string | |
def get_version_string(self): | |
version_string = self.get_version_string_without_commit() | |
if self.commit_number: | |
version_string += "+%d" % self.commit_number | |
return version_string | |
def __repr__(self): | |
return "<Version %s>" % self.get_version_string() | |
def __eq__(self, other): | |
self_type_value = \ | |
self.RELEASE_TYPES.index(self.release_type) | |
other_type_value = \ | |
self.RELEASE_TYPES.index(other.release_type) | |
return self.major == other.major \ | |
and self.minor == other.minor \ | |
and self.patch == other.patch \ | |
and self_type_value == other_type_value \ | |
and self.prerelease_number == other.prerelease_number \ | |
and self.commit_number == other.commit_number | |
def __ne__(self, other): | |
return not self == other | |
def __gt__(self, other): | |
# Compare main version part | |
if self.major > other.major: | |
return True | |
if self.minor > other.minor: | |
return True | |
if self.patch > other.patch: | |
return True | |
# Get ordinal value for prerelease type | |
self_type_value = \ | |
self.RELEASE_TYPES.index(self.release_type) | |
other_type_value = \ | |
self.RELEASE_TYPES.index(other.release_type) | |
# Compare prerelease part | |
if self_type_value > other_type_value: | |
return True | |
if self.prerelease_number > other.prerelease_number: | |
return True | |
# Compare commit number | |
if self.commit_number > other.commit_number: | |
return True | |
return False | |
def __lt__(self, other): | |
return not self > other | |
def __ge__(self, other): | |
return not self < other | |
def __le__(self, other): | |
return not self > other | |
def get_latest_version_from_git(): | |
# Get all version tags | |
devnull = open(os.devnull, 'w') | |
output = check_output(['git', | |
'tag', | |
'--list', | |
'v*'], stderr=devnull) | |
output_lines = output.split('\n') | |
version_tags = filter(lambda x: x, output_lines) | |
# Create versions for all version tags | |
versions = map(lambda x: Version.from_string(x), version_tags) | |
# Pick latest version | |
versions.sort() | |
latest_version = versions[-1] | |
return latest_version | |
# Parse arguments | |
command = sys.argv[1] | |
# Execute given command | |
if command == 'show': | |
latest_version = get_latest_version_from_git() | |
print latest_version.get_version_string().strip() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment