Thursday, July 26, 2012

Tracking Application Build Versions Through Git Hooks

One thing I notice that development teams often lack is unified conventions. Most people like to do things their own way. That makes it hard to build a strong development process, but when everyone can agree on flexible, robust methods of approach we can do a whole lot to make everyone's lives on the team easier.

Tracking software version is important. It becomes even more important when you need to constantly push files to production environments or coordinate versions between developers.

Here's we're doing more and more in our development process at Canvas to coordinate multiple versions for an application we're building on Kohana PHP Framework. As an aside, Kohana is a great up and rising HMVC framework for PHP. Our team has been building on it for about a year now and we're pleased with the results.

Kohana has configuration files and we're updating application/config/version.php via a git post-commit hook.

So here's the version.php file:

<?php defined('SYSPATH') or die('No direct access allowed.');
return array(
  'version' => '1.2.8',
  'last_modified' => 'Thu, 26 Jul 2012 22:59:45 EDT',
);

As you can see the file is pretty basic. It simply stores a version and a timestamp.

Git hooks are simply scripts which get called automatically when a specific git command is made. We've set up our script as a post-commit so that each time a commit is made our script fires off.

This Git script resides here in .git/hooks/post-commit

Here's the post-commit script

#!/bin/sh
#
# An example hook script to prepare a packed repository for use over
# dumb transports.
#
# To enable this hook, rename this file to "post-commit".
exec php bin/post-commit.php

As you can see its a bash script that calls a PHP script. Here's an example of a simple version of our post-commit.php script

<?php
define('SYSPATH', getcwd());

$config_path = 'application/config/version.php';
$config = include($config_path);

date_default_timezone_set('America/New_York');

function increase_minor_version($config) {
  $new_ver = '1.0.1';
  if(isset($config['version'])) {
    $old_ver = $config['version'];
    $parts = explode('.', $old_ver);
    if(isset($parts[2])) {
      $parts[2] = ((int) $parts[2]) + 1;
    }
    $new_ver = implode('.', $parts);
  }
  
  return $new_ver;
}

function store_config($version, $last_modified, $path) {
  $content  = "   $content .= "return array(\n";
  $content .= "  'version' => '".$version."',\n";
  $content .= "  'last_modified' => '".$last_modified."',\n";
  $content .= ");\n";

  $fh = fopen($path, 'w+');
  fwrite($fh, $content);
  fclose($fh);
}

store_config(increase_minor_version($config), date("D, d M Y H:i:s T"), $config_path);

We're not doing much with this script here but as with our development team we can do all sorts of automation. For example, we can fire off notifications, collate version updates for our team meetings, and/or update QA bug list databases or bootstrap our application and make CLI calls in our application (which is the real reason we're firing another PHP script).

Our teams are organized so that each team member develops in separate branches. And when we coordinate our version files are consolidated and incremented accordingly.