[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: Partial read-only repository?

From: Tim Moloney <moloney_at_mrsl.com>
Date: 2002-04-16 06:06:17 CEST

cmpilato@collab.net wrote:
> Tim Moloney <moloney@mrsl.com> writes:
>
>
>>Following the example of http://svn.collab.net/repos/svn/, I've
>>created a repository that has the following structure
>>
>> /svn/project/trunk
>> /svn/project/tags
>> /svn/project/branches
>>
>>I know that the only difference between tags and branches is that
>>tags won't (shouldn't) have anything commited back to them.
>>
>>Unfortunately, people make mistakes, is there a way to make the
>>tags directory read-only to prevent accidental commits that would
>>corrupt the tagged revision?
>
>
> You could write a pre-commit hook that uses svnlook to determine which
> paths were being modified. If the tags directory is being modified,
> and if those mods were anything more than just the creation of a new
> tag, the hook returns and error, and the commit doesn't happen. You
> could even have the script send email to the would-be committer
> saying, "I know what you tried to do, buck-o!"

I think that I have a pre-commit script that verifies that only copies
are made to the tags directory. This is implemented in the two files
below.

Please note that I haven't done perl in a while (and I probably wasn't
any good then either). If there are any perl gurus out there, feel
free to clean up the style, "perlize" it (versus my lame C-like style),
etc.

Hopefully, somebody else will find this useful. =)

===== begin svn_hook_utils.pm =====
#!/usr/bin/perl -w

#
# /usr/lib/perl/site_perl/5.6.0/i386-linux/svn_hook_utils.pm
#

package svn_hook_utils;
require Exporter;
@ISA =qw(Exporter);
@EXPORT = qw(
   svn_hook_init svn_fatal_error svn_mail_error svn_svnlook
   svn_check_log svn_check_tags
);
@EXPORT_OK = qw($author $date $log_size $log_msg @changed @dirs_changed);

#
# Configuration
#
$domain = 'mrsl.com';
$sendmail = '/usr/sbin/sendmail';
$svnlook = '/usr/local/bin/svnlook';
$svn_dir = '/var/svn';
$svn_admin = 'moloney';

#
# Constants
#
@months = ('Jan' ,'Feb', 'Mar', 'Apr', 'May', 'Jun',
            'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');

#
# Functions
#

#
# svn_fatal_error()
#
# Write a time-stamped message to the error log.
#
sub svn_fatal_error {

   my($msg) = @_;

   my($sec, $min, $hour, $day, $mon) = localtime;

   if (open(ERROR_LOG, ">>$svn_dir/error.log")) {
     printf(ERROR_LOG "\n");
     printf(ERROR_LOG "%s %2d %02d:%02d:%02d\n",
            $months[$mon], $day, $hour, $min, $sec);
     printf(ERROR_LOG "\n");
     printf(ERROR_LOG "%s", $msg);
   }
   else {
     #
     # Don't bother doing anything here because no one will ever see it.
     #
   }

   exit(1);

}

#
# svn_mail_error()
#
# Mail an error message to the author.
#
sub svn_mail_error {

   my($msg) = @_;

   my($cmd) = "| $sendmail $author\@$domain";
   if (open(SENDMAIL, $cmd)) {
     print(SENDMAIL "To: $author\@$domain\n");
     print(SENDMAIL "From: svn_server\n");
     print(SENDMAIL "Subject: Subversion Commit Error\n");
     print(SENDMAIL "Reply-to: $svn_admin\@$domain\n");
     print(SENDMAIL $msg);
     close(SENDMAIL);
   }
   else {
     svn_fatal_error("Failed to execute '$cmd'\n");
   }

   exit(1);

}

#
# svn_get_args()
#
# Verify and load the command line arguments based on the hook type.
#
# TODO - Add the following hook types:
# post-commit, read-sentinels, write-sentinals
#
sub svn_get_args {

   my($hook_type, @in_args) = @_;

   my(%out_args);

   if ($hook_type eq 'precommit') {
     if ($#in_args != 1) {
       $msg = " Invalid number of parameters for pre-commit script.\n";
       $msg .= " The following arguments were passed:\n";
       $msg .= "\n";
       if ($#in_args == -1) {
         $msg .= " <none>\n";
       }
       else {
         for ($i = 0; $i <= $#in_args; $i++) {
           $msg .= sprintf(" %d - '%s'\n", $i, $in_args[$i]);
         }
       }
       svn_fatal_error($msg);
     }

     $out_args{repos} = $in_args[0];
     $out_args{txn_rev} = "txn $in_args[1]";
   }

   return(%out_args);

}

#
# svn_hook_init()
#
# Get all of the transaction data based on the hook type and command line
# arguments.
#
sub svn_hook_init {

   my($hook_type, @args) = @_;

   %args = svn_get_args($hook_type, @args);

   ($author, $date, $log_size, $log_msg) = svn_svnlook('info');
   chomp($author);
   chomp($date);
   chomp($log_size);

   @changed = svn_svnlook('changed');

   @dirs_changed = svn_svnlook('dirs-changed');

}

#
# svn_svnlook()
#
# Get the specified transaction data.
#
sub svn_svnlook {

   my($subcmd) = @_;

   my($cmd) = "$svnlook $args{repos} $args{txn_rev} $subcmd";

   return(`$cmd`);

}

#
# svn_check_log()
#
# Verifies that some sort of log was entered.
#
sub svn_check_log {

   if ($log_msg !~ m/[a-zA-Z0-9]/) {
     return(1);
   }

   return(0);

}

#
# build_id_table()
#
# Builds the table that contains the directory entries and their node ids.
#
sub build_id_table {
   my(@lines) = split(/\n/, svn_svnlook('ids'));
   my($line);
   my(@parents);
   my($full_dir);
   my($dir);
   my($id);
   my(%table);
   foreach $line (@lines) {
     chomp($line);
     $full_dir = '';
     for ($i = 0; substr($line, $i, 1) eq ' '; $i++) {
       $full_dir .= $parents[$i];
     };
     ($dir, $id) = split(/ /, substr($line, $i));
     $parents[$i] = $dir;
     $full_dir .= $dir;
     $table{$full_dir} = $id;
   }
   return(%table);
}

#
# svn_check_tags()
#
# Verifies that no changes are made to the tags directory. Only copies are
# made.
#
sub svn_check_tags {

   $check = 0;
   foreach $dir (@dirs_changed) {
     if ($dir =~ m/^tags/) {
       $check = 1;
       last;
     }
   }

   if ($check) {
     my(%ids) = build_id_table();
     my($key);
     my($tag_file);
     my($trunk_file);
     foreach $key (sort keys %ids) {
       if ($key =~ m/$dirs_changed[1].+/) {
         $tag_file = $key;
         $trunk_file = $key;
         $trunk_file =~ s/$dirs_changed[1]/trunk\//;
         if ($ids{$tag_file} ne $ids{$trunk_file}) {
           return(1);
         }
       }
     }
   }

   return(0);

}
===== end svn_hook_utils.pm =====

===== begin pre-commit =====
#!/usr/bin/perl -w

#
# <repos-dir>/hooks/pre-commit
#

use svn_hook_utils;

svn_hook_init('precommit', @ARGV);

$changed = join('', @svn_hook_utils::changed);

#
# Make sure that some sort of log message was provided.
#
if (svn_check_log() != 0) {
   $msg = "
The following commit was rejected because there was no log message.

  Changes
=========
$changed
";
   svn_mail_error($msg);
}

#
# Make sure that no changes to the tags directory were made.
#
if (svn_check_tags() != 0) {
   $msg = "
The following commit was rejected because it attempted to change
the tags directory.

  Changes
=========
$changed

  Log
=====
$svn_hook_utils::log_msg
";
   svn_mail_error($msg);
}

#
# Return success.
#
exit(0);
===== end pre-commit =====

-- 
Tim Moloney
ManTech Real-time Systems Laboratory
2015 Cattlemen Road                             \     /
Sarasota, FL  34232                     .________\(O)/________.
(941) 377-6775 x208                        '  '  O(.)O  '  '
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Tue Apr 16 06:06:21 2002

This is an archived mail posted to the Subversion Dev mailing list.