Hello,
I'm administrating a Subversion repository that contains a number of
different (though related) projects. I use the commit-email.pl hook
script for commit e-mails, but is has become a burden that the users
request configuration changes for it through me, especially now they
have come to the point where they create new projects by themselves.
It seemed like a good idea to configure the commit e-mails for an item
through a Subversion property on that item, since it's definitely more
feasible to edit for the users than some script or config file on the
server, and since the properties already have appropriate authentication
from Subversion itself.
As a proof of concept, I quickly patched the commit-email.pl script (see
attach) to pick up properties. It will gather addresses to notify by
looking for the hook:commit-email property on all changed items and
their parents. (So if you subscribe in the hook:commit-email property of
a dir, you receive commit e-mails for revisions that influence any of
its files or subdirs.) It does caching to save on the number of svnlook
processes that get started, and duplicate e-mail addresses are filtered
out (although these two features could probably be better combined,
since e-mail addresses that come from the cache are duplicates, I
realize now).
Anyhow, this patch seems to work quite well in my tests, and the idea of
using properties is quite flexible, but maybe I overlooked something.
Has anybody tried something similar? Any comments or improvements?
Best regards,
Bruno De Fraine
--
Bruno De Fraine
Vrije Universiteit Brussel
Faculty of Applied Sciences, INFO - SSEL
Room 4K209, Pleinlaan 2, B-1050 Brussels
tel: +32 (0)2 629 29 75
fax: +32 (0)2 629 28 70
e-mail: Bruno.De.Fraine@vub.ac.be
--- commit-email.pl 2005-04-19 14:57:50.235811200 +0200
+++ commit-email-prop.pl 2005-04-19 15:32:42.414216000 +0200
@@ -52,6 +52,9 @@
# $no_diff_deleted to 1.
my $no_diff_deleted = 0;
+# The property that contains the e-mail addresses
+my $email_prop = 'hook:commit-email';
+
# Since the path to svnlook depends upon the local installation
# preferences, check that the required programs exist to insure that
# the administrator has set up the script properly.
@@ -81,62 +84,18 @@
######################################################################
# Initial setup/command-line handling.
-# Each value in this array holds a hash reference which contains the
-# associated email information for one project. Start with an
-# implicit rule that matches all paths.
-my @project_settings_list = (&new_project);
-
# Process the command line arguments till there are none left. The
# first two arguments that are not used by a command line option are
# the repository path and the revision number.
my $repos;
my $rev;
-# Use the reference to the first project to populate.
-my $current_project = $project_settings_list[0];
-
-# This hash matches the command line option to the hash key in the
-# project. If a key exists but has a false value (''), then the
-# command line option is allowed but requires special handling.
-my %opt_to_hash_key = ('--from' => 'from_address',
- '-h' => 'hostname',
- '-l' => 'log_file',
- '-m' => '',
- '-r' => 'reply_to',
- '-s' => 'subject_prefix');
-
while (@ARGV)
{
my $arg = shift @ARGV;
if ($arg =~ /^-/)
{
- my $hash_key = $opt_to_hash_key{$arg};
- unless (defined $hash_key)
- {
die "$0: command line option `$arg' is not recognized.\n";
- }
-
- unless (@ARGV)
- {
- die "$0: command line option `$arg' is missing a value.\n";
- }
- my $value = shift @ARGV;
-
- if ($hash_key)
- {
- $current_project->{$hash_key} = $value;
- }
- else
- {
- # Here handle -m.
- unless ($arg eq '-m')
- {
- die "$0: internal error: should only handle -m here.\n";
- }
- $current_project = &new_project;
- $current_project->{match_regex} = $value;
- push(@project_settings_list, $current_project);
- }
}
elsif ($arg =~ /^-/)
{
@@ -154,7 +113,7 @@
}
else
{
- push(@{$current_project->{email_addresses}}, $arg);
+ die "$0: Too many arguments.\n";
}
}
}
@@ -179,33 +138,6 @@
&usage("$0: repos directory `$repos' is not a directory.");
}
-# Check that all of the regular expressions can be compiled and
-# compile them.
-{
- my $ok = 1;
- for (my $i=0; $i<@project_settings_list; ++$i)
- {
- my $match_regex = $project_settings_list[$i]->{match_regex};
-
- # To help users that automatically write regular expressions
- # that match the root directory using ^/, remove the / character
- # because subversion paths, while they start at the root level,
- # do not begin with a /.
- $match_regex =~ s#^\^/#^#;
-
- my $match_re;
- eval { $match_re = qr/$match_regex/ };
- if ($@)
- {
- warn "$0: -m regex #$i `$match_regex' does not compile:\n$@\n";
- $ok = 0;
- next;
- }
- $project_settings_list[$i]->{match_re} = $match_re;
- }
- exit 1 unless $ok;
-}
-
######################################################################
# Harvest data using svnlook.
@@ -361,24 +293,62 @@
push(@body, "\n");
push(@body, map { /[\r\n]+$/ ? $_ : "$_\n" } @difflines);
-# Go through each project and see if there are any matches for this
-# project. If so, send the log out.
-foreach my $project (@project_settings_list)
- {
- my $match_re = $project->{match_re};
- my $match = 0;
- foreach my $path (@dirschanged, @adds, @dels, @mods)
- {
- if ($path =~ $match_re)
- {
- $match = 1;
- last;
- }
- }
+sub strip_trailing {
+ my $path = shift;
+ my $cand = $path;
+ return $cand if (chop($cand) eq '/');
+ return $path;
+}
+
+sub next_up {
+ my $path = shift;
+ return undef if ($path eq '/');
+ my $pos = rindex $path,'/';
+ return '/' if ($pos == -1);
+ return substr($path,0,$pos);
+}
- next unless $match;
+sub calc_emails {
+ my $path = shift;
+ my $lines = &sensitive_read_from_process($svnlook, 'propget', $repos, '-r', $rev,
+ $email_prop, $path);
+ if(defined($lines)) {
+ return @{$lines};
+ } else {
+ return ();
+ }
+}
+
+my %recip_cache = ();
+sub get_recip {
+ my $path = strip_trailing(shift);
+ my @emails = ();
+ do {
+ if(my $cached = $recip_cache{$path}) {
+ push @emails,@{$cached};
+ } else {
+ my @calcemails = calc_emails($path);
+ $recip_cache{$path} = \@calcemails;
+ push @emails,@calcemails;
+ }
+ } while ($path = next_up($path));
+ return @emails;
+}
+
+# Build up recipient list
+my @email_addresses = ();
+foreach my $path (@adds, @dels, @mods) {
+ EMAIL:
+ foreach my $new_email (get_recip($path)) {
+ foreach my $existing (@email_addresses) {
+ next EMAIL if ($new_email eq $existing);
+ }
+ push @email_addresses,$new_email;
+ }
+}
+
+my $project = &new_project;
- my @email_addresses = @{$project->{email_addresses}};
my $userlist = join(' ', @email_addresses);
my $to = join(', ', @email_addresses);
my $from_address = $project->{from_address};
@@ -477,40 +447,13 @@
warn "$0: cannot open `$log_file' for appending: $!\n";
}
}
- }
exit 0;
sub usage
{
warn "@_\n" if @_;
- die "usage: $0 REPOS REVNUM [[-m regex] [options] [email_addr ...]] ...\n",
- "options are\n",
- " --from email_address Email address for 'From:' (overrides -h)\n",
- " -h hostname Hostname to append to author for 'From:'\n",
- " -l logfile Append mail contents to this log file\n",
- " -m regex Regular expression to match committed path\n",
- " -r email_address Email address for 'Reply-To:'\n",
- " -s subject_prefix Subject line prefix\n",
- "\n",
- "This script supports a single repository with multiple projects,\n",
- "where each project receives email only for commits that modify that\n",
- "project. A project is identified by using the -m command line\n",
- "with a regular expression argument. If a commit has a path that\n",
- "matches the regular expression, then the entire commit matches.\n",
- "Any of the following -h, -l, -r and -s command line options and\n",
- "following email addresses are associated with this project. The\n",
- "next -m resets the -h, -l, -r and -s command line options and the\n",
- "list of email addresses.\n",
- "\n",
- "To support a single project conveniently, the script initializes\n",
- "itself with an implicit -m . rule that matches any modifications\n",
- "to the repository. Therefore, to use the script for a single\n",
- "project repository, just use the other comand line options and\n",
- "a list of email addresses on the command line. If you do not want\n",
- "a project that matches the entire repository, then use a -m with a\n",
- "regular expression before any other command line options or email\n",
- "addresses.\n";
+ die "usage: $0 REPOS REVNUM\n",
}
# Return a new hash data structure for a new empty project that
@@ -590,3 +533,20 @@
return @output;
}
}
+
+sub sensitive_read_from_process
+{
+ unless (@_)
+ {
+ croak "$0: read_from_process passed no arguments.\n";
+ }
+ my ($status, @output) = &safe_read_from_pipe(@_);
+ if ($status)
+ {
+ return undef;
+ }
+ else
+ {
+ return \@output;
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@subversion.tigris.org
For additional commands, e-mail: users-help@subversion.tigris.org
Received on Tue Apr 19 16:46:41 2005