Automatic releases: get the right version

February 20, 2007

There is one topic I’d like to do a series of small articles: automatic releases. This is something we have been improving more and more in CLAM and now I feel we are getting it right. In a nutshell, managing a CLAM release used to be very time consuming (it could take months!) now it’s like pressing a button.

We’ve done a lot of things: keep the code stable in all platforms (automatic tests and testfarm is a great help here), automate the process of generating all release files which includes source tarballs, mac-osx and windows installers, debian package, and more. Of course, the apps must have the version embedded (available in the about box, for instance). And finally, all those files have to be published accessible and well organized in the web.

And there’s more complexity to manage here: we provide “subversion” or “development” releases on one hand and “stable” releases on the other. The first ones look like CLAM-NetworkEditor-0.4.3_setup.exe, the second ones look like CLAM-NetworkEditor-0.4.4~svn09719_setup.exe. This last long version means: this is a svn snapshot of NetworkEditor (revision 9719) in the way of the stable version 0.4.4.
And of course, we are managing versions of four applications and one set of libs. Thus, the complete version of one app includes the CLAM lib version.

On the lowest level of this infrastructure we have the scripts that automatically retrieve the correct version. Today I’ve finished a code clean-up of the python module we are using. The main functions of the module is this:

def versionFromLocalInfo( product='CLAM', changesFile="CHANGES" )

Keep reading to learn how it works.

The function returns two values: the main version (say, 0.4.4) and the svn-version (say, 0.4.4~svn09719). This is used in all CLAM (apps and libs) SConstructs (see an example), so we don’t want to access the remote svn server in any way. Getting the version is easy, it parses the CHANGES (our changelog) file. But getting the subversion revision is a bit trickier. First it try to use the local svn information, if available. If our code does not have svn information because, for example, it comes from the source package, then it falls back to the last revision saved in the CHANGES file.

The first line of the CHANGES file looks like this during development:

2007-??-?? NetworkEditor 0.4.4 SVN $Revision: 9710$

And when we release this app, it’d change to something like this:

2007-02-25 NetworkEditor 0.4.4

The $Revision: 9710$ is a pattern that is updated with the current revision each time we commit the file. Of course this revision can become somehow out-dated, since we modify (and commit) the code more often than we do with the CHANGES file. And that’s why we prefer to use the local svn revision if possible.

Program that kind of stuff in Python is really straight foward, so let’s have a look at the code of the main function.

def versionFromLocalInfo( product='CLAM', changesFile="CHANGES" ):
    "If available take the revision in (local) svn info, else use CHANGES"
    version, revision = _parseChangesFile( changesFile, product ) 
    if not revision : # it's a release. forget about svnVersion
        return version, version 
    try : 
        revision = _svnRevisionOf(".")
    return version, _svnVersion(version, revision)

This uses helper functions with a little bit of file and command-line output parsing. They parse the CHANGES file, retrieve the svn revision from local svn info and, last, format the svn-version. They are quite simple but note that _svnRevisionOf can receive both a local path or a remote URI.

def _svnRevisionOf( whatToCheck ):
	output = os.popen("svn info "+whatToCheck)
	revisionLocator = re.compile(r'^Revision:(?P.*)')
	for line in output :
		match = revisionLocator.match(line)
		if not match: continue
	raise "No svn revision found for "+ whatToCheck

def _parseChangesFile( changesFile, product='CLAM' ) :
	versionExtractor = re.compile(r'[0-9?]+-[0-9?]+-[0-9?]+ %s (?P[0-9]+)\.(?P[0-9]+)\.(?P[0-9]+)(?P.*SVN[^0-9]*(?P[0-9]+))?'%product)
	for line in file(changesFile) :
		match = versionExtractor.match(line)
		if match is None: continue
		major, minor, patch, revision = [ for tag in ('Major', 'Minor', 'Patch', 'Revision') ]
		version = "%s.%s.%s"%(major,minor,patch)
		return version, revision
	assert "not found a valid CHANGES file: "+changesFile

def _svnVersion(version, revision) :
	return "%s~svn%05i"%(version, int(revision))

That’s enought Python for now! I’ll try to keep posting on this topic.
Updated Feb 21th: a general revision and addition of helper functions.


3 Responses to “Automatic releases: get the right version”

  1. albert Says:

    bona feina, pau

  2. ubuntutribe Says:

    You are invited to watch the Ubuntu Tribe movie trailer.
    Thank you and nice to meet you!

  3. sgarealestates is the best website who offers you best deal of property in chandigarh, property in panchkula, property in zirakpur and property in mohali on reasonable rates.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: