#!/usr/bin/perl
##-----------------------------------------------------------------------------
## pre-commit-check.pl
##-----------------------------------------------------------------------------
## This is a Subversion pre-commit hook script that can do the following:
## * Check the log message length
## * Only allow modification of TAGS by admin user
## * Validate svn:externals use the -r parameter
## As required by Subversion, if all tests pass, this script exits with a
## return code of 0 indicating success. If there is an error, the error message
## is printed on STDERR, and the script exits with a non 0 return code.
##-----------------------------------------------------------------------------
## USAGE:
## pre-commit-check.pl REPO_PATH TX_NUM {-d}
## REPO_PATH = Path to local repository
## TX_NUM = Pending transaction number (or revision number if
## debugging is enabled
## -d = Enable debugging
##
##-----------------------------------------------------------------------------
use strict;
##
## Modify this to match the path to svnlook
##
my $svnlook = "/usr/bin/svnlook";
## Indicates the minimum log message length
## If set to 0, message length will not be checked
my $logMessageLengthMinimum = 10;
## Indicates if the user can modify tags
## Any non 0 value inidcates a "normal" user can modify tags
my $allowTagModify = 0;
## Indicates the username that can modify tags
## Only used if $allowTagModify is set to 0
my $svnSuperUser = "Admin";
## Indicates if svn:externals should be validated to ensure
## a -r parameter is provided
## Any non 0 value indicates this test should be performed
my $svnExternalsCheck = 0;
##!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
##!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
## No more modifications should be needed below this point
##!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
##!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
## Misc. variables
my $repo_path = $ARGV[ 0 ];
my $txn_name = $ARGV[ 1 ];
my $logMessageLength = 0;
my $debugEnabled = 0;
my $argNum = 0;
my $argC = 0;
my $exitCode = 0;
##-----------------------------------------------------------------------------
##
## Parse the command line
##
##-----------------------------------------------------------------------------
{
foreach $argNum (0 .. $#ARGV)
{
if ( ($ARGV[$argNum] eq "-d") || ($ARGV[$argNum] eq "-D"))
{
$debugEnabled++;
}
$argC++;
}
## Validate arguments
if ((($debugEnabled) && ($argC < 3)) || ((!$debugEnabled) && ($argC < 2)))
{
printf STDERR "ERROR: Incorrect number of arguments!\n\n";
exit 1;
}
## Display debug banner
if ($debugEnabled)
{
print "\n------ DEBUG LEVEL $debugEnabled ------\n";
## Use revision numbers since there is not a transaction number
$txn_name = "-r $txn_name";
}
else
{
## Use transaction numbers
$txn_name = "-t $txn_name";
}
}
##-----------------------------------------------------------------------------
## Check to see if svn:externals have the -r parameter
##-----------------------------------------------------------------------------
{
if ($svnExternalsCheck)
{
my @directories;
my $dir;
my $prop_val;
if ($debugEnabled >= 2)
{
print "\"$svnlook\" dirs-changed $txn_name \"$repo_path\"\n"
}
open( INPUT, "\"$svnlook\" dirs-changed $txn_name \"$repo_path\"|" );
while( )
{
chomp;
if ($debugEnabled >= 2)
{
print "CHANGED: \"$_\"\n";
}
push @directories, $_;
}
close INPUT;
my @externalsFound;
foreach $dir ( @directories )
{
if ($debugEnabled >= 2)
{
print "Checking proplist of \"$dir\"\n";
print "\"$svnlook\" proplist $txn_name \"$repo_path\" \"$dir\"\n"
}
open( INPUT, "\"$svnlook\" proplist $txn_name \"$repo_path\" \"$dir\"|" );
while( $prop_val = )
{
chomp $prop_val;
if ( $prop_val =~ /svn:externals/i )
{
push @externalsFound, $dir;
if ($debugEnabled >= 2)
{
print "WILL";
}
}
elsif ($debugEnabled >= 2)
{
print " WILL NOT";
}
if ($debugEnabled >= 2)
{
print " check $dir\n";
}
}
close INPUT;
}
my $externalsPassed = 1;
foreach $dir ( @externalsFound )
{
print "Checking externals of \"$dir\"\n";
open( INPUT, "\"$svnlook\" propget $txn_name \"$repo_path\" svn:externals \"$dir\"|" );
while( $prop_val = )
{
chomp $prop_val;
if ( $prop_val =~ /^(\".*\"|\S*)\s+\-r\s*(\d*)\s+(.*)/ )
{
if ($debugEnabled >= 2)
{
print "\"$prop_val\"\n";
print "OK: $1 is rev $2 of $3\n";
}
}
else
{
$externalsPassed = 0;
printf STDERR "ERROR: svn:externals is missing the -r parameter\n";
printf STDERR " \"$prop_val\$\n";
}
}
close INPUT;
}
if (!$externalsPassed)
{
$exitCode++;
}
}
elsif ($debugEnabled)
{
print "Skipping svn:externals validation\n"
}
}
##-----------------------------------------------------------------------------
## Check to see if the log message is long enough
##-----------------------------------------------------------------------------
{
if ($logMessageLengthMinimum > 0)
{
open( INPUT, "\"$svnlook\" log $txn_name \"$repo_path\"|" );
while( )
{
$logMessageLength += length;
}
close INPUT;
if ($logMessageLength < $logMessageLengthMinimum)
{
printf STDERR "ERROR: Log message length ($logMessageLength) does not ";
printf STDERR "meet minimum length requirements of ";
printf STDERR "$logMessageLengthMinimum.\n";
$exitCode++;
}
}
}
##-----------------------------------------------------------------------------
## Check to see TAGS are being modified
##-----------------------------------------------------------------------------
sub ValidateTagsModification()
{
## Check to see if this is a super user that is allowed to change TAGS
if (!$allowTagModify)
{
open( INPUT, "\"$svnlook\" author $txn_name \"$repo_path\"|" );
while( )
{
if( /^$svnSuperUser/i )
{
if ($debugEnabled)
{
print "Author == \"$svnSuperUser\", will not check for tags
modification\n"
}
$allowTagModify = 1;
}
}
close INPUT;
}
elsif ($debugEnabled)
{
print "Skipping TAGS modification tests\n"
}
## Check to see if someone is modifying a TAGS directory
if (!$allowTagModify)
{
if ($debugEnabled)
{
print "Checking to see if TAGS directory is being modified\n";
}
open( INPUT, "\"$svnlook\" changed $txn_name \"$repo_path\"|" );
while( )
{
if( /^[UD]\s*?.*?\/?tags\/.*/i )
{
printf STDERR "Tags can only be modified by $svnSuperUser.\n";
$exitCode++;
last;
}
}
close INPUT;
}
}
##-----------------------------------------------------------------------------
if ($debugEnabled)
{
print "\n\nExit code = $exitCode\n\n";
}
exit $exitCode;
##-----------------------------------------------------------------------------