Paver Patterns 1: Attach git branch to version number

by gldnspud on April 14, 2009

Update: I no longer use this recipe, as it turned out to be an elaborate workaround for Paver 1.0 ignoring setup.cfg and distutils.cfg when using a Paver-based setup.py file. I'm now using a patch that allows Paver to honor such configuration files.

I've been using Paver, a tool to help manage various bits of administrivia surrounding Python projects, with Schevo for several months now. Now that Paver has a 1.0 release, I'm also using it to help streamline such tasks for the commercial software I'm helping develop.

One of the patterns I'm used to is attaching dev to the end of versions, to help differentiate between development versions and release versions.

I wanted to go further than this, also attaching the name of the current git branch name to the version number. That practice is useful when you use branching to manage feature development and bug fixes.

Here's the pattern I settled on. The relevant parts of pavement.py look like this:

from paver.easy import *
from paver.setuputils import setup
 
VERSION = '1.2.3'
DEVELOPMENT = True
 
# Use branch name if git information is available; otherwise, use
# version number from setup_meta.
if DEVELOPMENT:
    try:
        git_head_path = path('.git/HEAD')
        contents = git_head_path.open('rU').readline().strip()
        name, value = contents.split()
        BRANCH = value.split('/')[-1]
        if BRANCH != 'master':
            VERSION += '-' + BRANCH
    except:
        pass
    VERSION += '-dev'
 
 
setup(
    name='MyProject',
    version=VERSION,
    ...
)

When generating releases, set DEVELOPMENT to False, commit that change, tag and release, then change the value back to True and commit again.

{ 2 comments }

Kevin Dangoor April 15, 2009 at 5:08 am

Hey, thanks for the article! Great to see the way people are using Paver.

You could potentially automate this in a “release” task. Create a DEV.txt or similar that says something like “This is unreleased development code. Good luck!”. Commit that to the repository. Here is pseudocode for how you’d do the rest:

    def compute_version():
      if options.dev_file.exists():
          try:
              git_head_path = path('.git/HEAD')
              contents = git_head_path.open('rU').readline().strip()
              name, value = contents.split()
              BRANCH = value.split('/')[-1]
              if BRANCH != 'master':
                  VERSION += '-' + BRANCH
          except:
              pass
          VERSION += '-dev'
 
 
    options(
        version_base="1.2.3",
        dev_file=path('DEV.txt')
    )
 
    setup(
        name='MyProject',
        version=compute_version
    )
 
    @task
    def release(options):
        dev_file.copy("DEV-backup.txt")
        # not totally familiar with git... this command may be a little different
        sh("git rm %s" % dev_file)
        # build the distribution
        call_task('sdist')
        sh("git commit -a -m 'tagging release'")
        sh("git tag %s" % options.setup.version)
        # do tags need to be committed? beats me ;)
        path("DEV-backup.txt").move(dev_file)
        sh("git add %s" % dev_file)
        sh("git commit -a -m 'marking new development'")

This is not necessarily the most elegant way to do it, but you get the idea. At least you don’t need to manually run through these steps each time.

Kevin Dangoor April 15, 2009 at 5:10 am

Oops… I just realized that I never went back and updated compute_version to actually return the proper version number (either options.version_base or the extended version number you provide). I’ll leave that as an exercise for the reader ;)

Comments on this entry are closed.

Previous post:

Next post: