Wednesday, September 24, 2008

Subversion pre-commit hook in Perl

Subversion has a beautiful concept called a "hook" to ensure/apply control mechanisms in your project.

A hook is a program triggered by some repository event like, creating a new revision or changing some property of an artifact. Each hook is given information as to what the event is, which target is it working on, username who triggered the event and depending on the hook’s output or return status, the hook program may continue the action, stop it or suspend it.

The "hooks" subdirectory of the repository path is filled with templates for various hooks like:

$ dir /hooks/
post-commit.tmpl pre-commit.tmpl
post-lock.tmpl pre-lock.tmpl
post-revprop-change.tmpl pre-revprop-change.tmpl

By examining the contents of these template scripts you can see which event triggers each such script to run and what data is passed to that script.

To actually have a working hook, you need to place some .exe or a script into the /hooks directory which can be executed and the name of the hook should be similar to the .tmpl name without its extension, like "pre-commit.exe" or "post-commit.bat" etc depending on the event it get triggered. As the name itself suggests, "pre-commit" hook gets triggered before a commit action is performed, a "post-commit" hook is triggered after a successful commit action is performed.

On unix these scripts can be written as a shell script, python program or a c program etc and the name should be exactly like the name of the hook. On windows the concept is the same but the extension of the script should be .bat or .exe accordingly.

Task to Perform: The pre-commit hook should checkout for:
  1. A “mandatory” message (comment) during svn commit operation. In case you haven’t given any message, it would throw an error and abort the commit.
  2. In case you have given a message but the message doesn’t meet the minimum character requirement, it would throw an error and abort the commit.
You will need these two files to implement it.
  1. pre-commit.bat:

    PATH=C:\perl\bin
    perl.exe c:\newrepo\hooks\pre-commit.pl %1 %2

    A windows batch file that sets the path of the perl executable and also specifies the path of the pre-commit.pl script that needs to be executed by the perl.exe.

  2. pre-commit.pl:

    #! c:\perl\bin;
    # creating scalar variables that holds some values
    $min = 8;
    $svnlook = '"C:\Program Files\CollabNet Subversion Server\\svnlook.exe"';

    #--------------------------------------------
    $repos = $ARGV[0];
    $txn = $ARGV[1];
    $msg = `$svnlook log -t "$txn" "$repos"`;
    chomp($msg);

    if ( length($msg) == 0 )
    {
    print STDERR "A Message is Mandatory";
    exit(1);
    }
    elsif ( length($msg) < $min ) { print STDERR "Message should be atleast $min characters in length"; exit(1); } exit(0);

    The above perl script that does the following:
    • Has the perl executable path set in the shebang line
    • Has a scalar variable which stores the minimum number of characters to expect in a message(comment)
    • Has another scalar variable which is set to the path of svnlook.exe.
      Note the entire path is enclosed within “” (double quotes) to negate the embedded spaces and then the whole entry is enclosed within a ‘’ (single quotes) and double back slashes before the exe name.
    • svnlook command runs to gather the message that was entered in the last transaction in the repository. If the length of the message is ‘0’, then it displays a message saying “A Message is Mandatory” , if the message is less than the minimum characters ie “8” in this example, it would throw an error message saying “ Message should be at least 8 characters in length”.

Set-up for Test: Go to the hooks directory of the repository path and create/copy both the above files there (i.e., pre-commit.bat and pre-commit.pl).

Now when you try to commit something into the repository giving an empty message or even a very short message, it should throw an error.

Now try out the following in your working copy and see the hook working:
  1. Try to open a file and change some contents of the file

    C:\wc1\trunk>notepad foo.txt

  2. Now try to commit the same with empty message (output will be an error)

    C:\wc1\trunk>svn commit -m ""

    Output:
    Sending trunk\foo.txt
    Transmitting file data .svn: Commit failed (details follow):
    svn: 'pre-commit' hook failed with error output:
    A Message is Mandatory

  3. Now try to commit the same with a message which is just 2 characters in length

    C:\wc1\trunk>svn commit -m "so"

    Output:
    Sending trunk\foo.txt
    Transmitting file data .svn: Commit failed (details follow):
    svn: 'pre-commit' hook failed with error output:
    Message should be atleast 8 in length

  4. Now try to commit the same with a message of appropriate length

    C:\wc1\trunk>svn commit -m "some meaningful msg"

    Output:
    Sending trunk\foo.txt
    Transmitting file data.
    Committed revision 10.