Skip to content

Commit

Permalink
Merge pull request #31 from broadinstitute/simplify-version
Browse files Browse the repository at this point in the history
Simplify determination of package version
  • Loading branch information
haydenm committed Aug 6, 2019
2 parents b0cb3e8 + dfff03e commit b96418b
Showing 1 changed file with 66 additions and 52 deletions.
118 changes: 66 additions & 52 deletions catch/utils/version.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,54 @@
#!/usr/bin/python
''' This gets the git version into python-land
'''
"""This determines the catch package version, primarily based on git.
If git is available, we use the version specified by git. This can indicate
commits on top of the last numbered version and can also indicate if the
working directory is dirty (i.e., has local modifications). If git is not
available but some version from git was stored in a file, we use that. Finally,
if none of these are available, we resort to the numbered release version
manually specified in the variable RELEASE_VERSION.
"""

# Manually specify a numbered release version to use as a fallback
RELEASE_VERSION = 'v1.3.1'

__author__ = "dpark@broadinstitute.org"
__version__ = None

import subprocess
import os
import re
import time, datetime

__author__ = ['Danny Park <dpark@broadinstitute.org>',
'Hayden Metsky <hayden@mit.edu>']

# Set __version__ lazily below
__version__ = None


def get_project_path():
'''Return the absolute path of the top-level project, assumed to be the
parent of the directory containing this script.'''
"""Determine absolute path to the top-level of the catch project.
This is assumed to be the parent of the directory containing this script.
Returns:
path (string) to top-level of the catch project
"""
# abspath converts relative to absolute path; expanduser interprets ~
path = __file__ # path to this script
path = os.path.expanduser(path) # interpret ~
path = os.path.abspath(path) # convert to absolute path
path = os.path.dirname(path) # containing directory: util
path = os.path.dirname(path) # containing directory: main project dir
path = os.path.dirname(path) # containing directory: utils
path = os.path.dirname(path) # containing directory: catch project dir
return path


def call_git_describe():
def get_version_from_git_describe():
"""Determine a version according to git.
This calls `git describe`, if git is available.
Returns:
version from `git describe --tags --always --dirty` if git is
available; otherwise, None
"""
cwd = os.getcwd()
try:
os.chdir(get_project_path())
Expand All @@ -39,10 +64,20 @@ def call_git_describe():


def release_file():
"""Obtain path to file storing version, according to git.
Returns:
path to VERSION file
"""
return os.path.join(get_project_path(), 'VERSION')


def read_release_version():
"""Read VERSION file, containing git version.
Returns:
if VERSION file exists, version stored in it; otherwise, None
"""
try:
with open(release_file(), 'rt') as inf:
version = inf.readlines()[0].strip()
Expand All @@ -52,66 +87,45 @@ def read_release_version():


def write_release_version(version):
with open(release_file(), 'wt') as outf:
outf.write(version + '\n')

"""Save version, according to git, into VERSION file.
def approx_version_number():
Args:
version: version to save
"""
In the event that git is unavailable and the VERSION file is not present
this returns a "version number" in the following precedence:
- version number from path
downloads from GitHub tagged releases
might be extracted into directories containing
the version number. If they contain a version number
in the form d.d.d, we can use it
- modification time of this file (unix timestamp)
file modification time for github releases corresponds to
when the release archives were created, a rough way to ballpark
the release date. If we can't get the version number from the path
we can at least use the modification time of this file as a proxy
for the true version number
- the current time (unix timestamp)
the current time is better than not having any version number
"""
version_re = re.compile(r"(?:(\d+)\.)?(?:(\d+)\.)?(?:(\d+))")
# path relative to version.py
relative_path = os.path.basename(get_project_path())

# for tagged releases, the version number might be part of
# the root directory name
matches = version_re.search(relative_path)

if matches and len([n for n in matches.groups() if n]) == 3:
version = ".".join(map(str, matches.groups()))
else:
try:
# Try to use modification time of the current file
version = str(int(os.path.getmtime(__file__)))
except OSError:
# Just use the current time
version = str(int(time.time()))
with open(release_file(), 'wt') as outf:
outf.write(version + '\n')

return version

def get_version():
"""Determine version from git, and save if available.
"""
# Allow modifying the global __version__ variable
global __version__

if __version__ is None:
from_git = call_git_describe()
from_git = get_version_from_git_describe()
from_file = read_release_version()

if from_git:
# A version is available from git; use this
if from_file != from_git:
# Update the version stored in the VERSION file
write_release_version(from_git)
__version__ = from_git
else:
__version__ = from_file
if from_file:
# No version is available from git but one is in the
# VERSION file; use this
__version__ = from_file

if __version__ is None:
__version__ = approx_version_number()
# No version is available from git and there is no VERSION
# file; use the manually set release version
__version__ = RELEASE_VERSION

return __version__


if __name__ == "__main__":
# Determine and print the package version
print(get_version())

0 comments on commit b96418b

Please sign in to comment.