Wrapping the Rational Adapter for Git pre-receive hook

Toto, I’ve a feeling we’re not in Kansas anymore.

If you’re wondering why you can’t find the “z” in this post, I’ve taken on a new challenge as the development lead for the Rational Lifecycle Integration Adapters for Git and HP ALM. So, while this blog will still maintain its Jazz-y theme, I’ll no longer be focusing on the Rational Team Concert Enterprise Extensions. Hopefully by now you’ve started following jorgediazblog.wordpress.com and rtcee.wordpress.com for all the latest and greatest EE news.

What are the Rational Lifecycle Integration Adapters? You can find a good introduction to our three Standard Edition (OSLC-based) adapters, as well as an announcement of our latest V1.1 release, on jazz.net. 60-day trial versions of all three adapters are available for download on the jazz.net Downloads page.

Today I’d like to address a question we’ve received on more than one occasion regarding the Git adapter pre-receive hook. The Git adapter’s primary function is to create a link between a Git commit and an RTC work item when a developer pushes his changes to the shared repository. The Git adapter provides a pre-receive hook that parses the commit message for pre-defined (and configurable) keywords such as “bug” or “task” and automatically establishes the link to the RTC work item. If the developer forgets to include the work item reference in his commit message, he can view his commit in Gitweb and manually add a link to his RTC work item using the banner provided by the Git adapter.

Git Adapter banner in Gitweb

Some users would rather see the push fail if there is no work item referenced in the commit message. If you fall into this category, or if you have other custom validation you need to perform prior to establishing the link, you can write your own custom pre-receive hook that in turn calls the Git adapter’s hook after any custom logic is performed.

I tested this out by creating a simple Perl script based on a sample provided in Scott Chacon’s Pro Git book. It tests the commit message for the word “bug” before calling the Git adapter’s pre-receive hook. If “bug” is not found, the hook ends in error. I named this script pre-receive, saved it in my Git repository’s hooks directory, and renamed the Git adapter’s pre-receive symbolic link as lia-pre-receive.

A few things to note about this solution:

  1. Like any other sample on this blog, this code is in no way, shape, or form a supported solution. It is only intended to get you started.
  2. This sample doesn’t account for things like excluded branches and customized work item tags (like “task”, “defect”, etc).
  3. If the regular expression in this sample is giving you nightmares, you can pop open the pre-receive hook shipped with the Git adapter for a nice explanation of all the complexity. Fun!
  4. We have a reprocess script available for re-attempting the links if something goes wrong on the push. This reprocess script refers to the Git adapter’s pre-receive hook by name, and as such would need to be appropriately updated to refer to the original Git adapter pre-receive hook and not your new custom hook.
  5. If you thought adding your custom checks to the update hook would be an easier solution, think again. The update hook runs after pre-receive, so at that point it’s too late.
  6. Lastly, if you have a whole bunch of logic to perform during your pre-receive, a quick google will turn up some much sexier options for chaining your hooks.

So, with no further ado, I give you my sample script. Enjoy! And as always, feel free to comment back with your own, better ideas for handling custom hook logic.

#!/usr/bin/env perl

use strict;
use diagnostics;
use IPC::Run3;

sub main() {
    my @list = <STDIN>;
    my ($name,$path,$suffix) = File::Basename::fileparse($0);
    my $cmd = $path . 'lia-pre-receive';

    foreach my $line (@list) {
        validate($line);
        run3($cmd, \$line);
    }
}

sub validate() {
    #print "validate: @_\n";
    my ($line) = @_;
    my @inputs = split(/\s+/, $line);
    my $oldrev = $inputs[0];
    my $newrev = $inputs[1];
    my $refname = $inputs[2];

    my $tag = "bug";

    my $revs = `git rev-list $oldrev..$newrev`;

    my @missed_revs = split("\n", $revs);
    foreach my $rev (@missed_revs) {
        my $sed = q(sed '1,/^$/d');
        my $message = `git cat-file commit $rev | $sed`;
        if ($message =~ m/(?<!\w)$tag(?:\s*(\(.*?\)))?[:\s]\s*(\d+)/) {
            #Nothing to do here...
        } else {
            print "No work item was specified for commit $rev. Exiting...\n";
            exit 1;
        }
    }
}

main();
exit 0;
Advertisements
This entry was posted in Git, Rational Lifecycle Integration Adapters. Bookmark the permalink.

Leave a Reply

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

WordPress.com Logo

You are commenting using your WordPress.com 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