Skip to content

Instantly share code, notes, and snippets.

@gregschmit
Last active July 13, 2019 03:13
Show Gist options
  • Save gregschmit/2d508019423c655837e569be7a3f9f72 to your computer and use it in GitHub Desktop.
Save gregschmit/2d508019423c655837e569be7a3f9f72 to your computer and use it in GitHub Desktop.
A `version.py` to include in Django apps that provides an interface for git versioning. In `setup.py` (in the parent directory) you can `from purge import version` and then use `version.get_version()` to get the version programmatically.
"""
This module provides versioning hooks for a Python package in a Git repository.
Verbiage: MAJOR.MINOR.PATCH
To start versioning at 0.1.x, just do ``git tag -a v0.1 -m 'version 0.1'``. Each
commit will increment the PATCH level by 1 according to this module's
``get_version()``. When you want to increment the MINOR, just do ``git tag -a
v0.2 -m 'version 0.2'``.
In your ``setup.py`` you should stamp the directory so when you generate your
package sdist/bdist, the ``VERSION_STAMP`` exists (since your sdist/bdist won't
have the ``.git`` directory telling the user which version they have):
from my_module_name import version
version.stamp_directory('./my_module_name')
You should also get the version from ``get_version`` and ensure your
``VERSION_STAMP`` gets included in your sdists/bdists, like:
...
setup(
name='my-module-name',
version=version.get_version(),
packages=find_packages(),
include_package_data=True,
package_data={'my_module_name': ['VERSION_STAMP']},
...
At the end of your ``setup.py`` you should un-stamp the directory:
...
version.unstamp_directory('./my_module_name')
Finally, source distributions don't listen to ``package_data`` instructions, so
you need to include the following in a ``MANIFEST.in`` file in the same
directory as ``setup.py``:
include */VERSION_STAMP
"""
import os
from subprocess import Popen, PIPE, STDOUT
def cmd_out(cmd):
"""
Helper for running shell commands.
"""
wd = os.path.dirname(os.path.realpath(__file__))
p = Popen(cmd, shell=True, cwd=wd, stdout=PIPE, stderr=STDOUT)
r, _ = p.communicate()
res = r.strip().decode()
return res, p.returncode
def get_version():
"""
Get the version, first by trying to read the ``VERSION_STAMP``, and if that
fails, by attempting to use ``git`` annotated tags.
"""
git_options = ['git', '/usr/local/bin/git', '/usr/bin/git']
d = os.path.dirname(os.path.realpath(__file__))
try:
x = open(os.path.join(d, 'VERSION_STAMP'), 'rb').read().strip().decode()
if x: return x
except OSError:
pass
for g in git_options:
gitver, _ = cmd_out('{0} describe --tags --always'.format(g))
gitchanged, _ = cmd_out('{0} diff-index HEAD --'.format(g))
gitver = gitver.replace('v', '').split('-g')[0].replace('-', '.')
if ' ' in gitver: gitver = '0'
else: break
if not gitver or 'fatal' in gitver or '\n' in gitver:
return '0'
if gitchanged.strip():
gitver += '+changed'
return gitver
def stamp_directory(d):
"""
Write the ``VERSION_STAMP`` file.
"""
v = get_version()
with open(os.path.join(d, 'VERSION_STAMP'), 'wb') as f:
f.write(v.encode() + b'\n')
def unstamp_directory(d):
"""
Remove the ``VERSION_STAMP`` file.
"""
os.remove(os.path.join(d, 'VERSION_STAMP'))
if __name__ == '__main__':
print(get_version())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment