1 | #!/usr/bin/perl |
---|
2 | # ------------------------------------------------------------------------------ |
---|
3 | # NAME |
---|
4 | # pre-revprop-change.pl |
---|
5 | # |
---|
6 | # SYNOPSIS |
---|
7 | # pre-revprop-change.pl REPOS REV USER PROPNAME ACTION <&0 |
---|
8 | # |
---|
9 | # DESCRIPTION |
---|
10 | # This script e-mails authors and watchers when a user attempts to modify the |
---|
11 | # svn:log of a particular revision. The new property value is passed via |
---|
12 | # STDIN. Watchers are set in the "watch.cfg" file, which should be located in |
---|
13 | # the root within the Subversion repository. The watch.cfg file is a standard |
---|
14 | # INI-type configuration file with the basic format: |
---|
15 | # |
---|
16 | # [repos_base] |
---|
17 | # path/in/repos = list,of,watchers |
---|
18 | # |
---|
19 | # E.g.: |
---|
20 | # |
---|
21 | # [FCM_svn] |
---|
22 | # |
---|
23 | # FCM/trunk/src = fcm,frsn |
---|
24 | # FCM/trunk/doc = fcm,frsn,frdm,frbj |
---|
25 | # FCM/branches/dev/*/*/src = fcm,frsn |
---|
26 | # FCM/branches/dev/*/*/doc = fcm,frsn,frdm,frbj |
---|
27 | # |
---|
28 | # COPYRIGHT |
---|
29 | # This program is part of the FCM system. |
---|
30 | # (C) Crown copyright Met Office. All rights reserved. |
---|
31 | # For further details please refer to the file COPYRIGHT.txt |
---|
32 | # which you should have received as part of this distribution. |
---|
33 | # ------------------------------------------------------------------------------ |
---|
34 | |
---|
35 | use strict; |
---|
36 | use warnings; |
---|
37 | |
---|
38 | use File::Basename; |
---|
39 | use File::Spec; |
---|
40 | use File::Temp qw/tempfile/; |
---|
41 | use Mail::Mailer; |
---|
42 | use Config::IniFiles; |
---|
43 | |
---|
44 | # Arguments |
---|
45 | # ------------------------------------------------------------------------------ |
---|
46 | my ($repos, $rev, $user, $propname, $action) = @ARGV; |
---|
47 | |
---|
48 | # Basename of repository |
---|
49 | my $base = basename $repos; |
---|
50 | |
---|
51 | # Top level variables |
---|
52 | # ------------------------------------------------------------------------------ |
---|
53 | # The watch configuration file, at the root of the current repository |
---|
54 | my $watch_config = 'watch.cfg'; |
---|
55 | |
---|
56 | # Determine whether change is permitted |
---|
57 | # ------------------------------------------------------------------------------ |
---|
58 | # Switch off for most revision properties |
---|
59 | my $return = 1; |
---|
60 | |
---|
61 | # Switch on only for "svn:log" |
---|
62 | $return = 0 if $propname eq 'svn:log' and $action eq 'M'; |
---|
63 | |
---|
64 | if ($return == 0) { |
---|
65 | # Diagnostic |
---|
66 | print $repos, ': ', $propname, ' at revision ', $rev, |
---|
67 | ' is being modified by ', $user, '.', "\n"; |
---|
68 | |
---|
69 | my %mail_to = (); |
---|
70 | |
---|
71 | # Mail original author, if he/she is not the current user |
---|
72 | # ---------------------------------------------------------------------------- |
---|
73 | # Find out who is the author of the changeset at $rev |
---|
74 | my @command = (qw/svnlook author -r/, $rev, $repos); |
---|
75 | my $author = qx(@command); |
---|
76 | chomp $author; |
---|
77 | |
---|
78 | # Add author to mail list, if necessary |
---|
79 | $mail_to{$author} = 1 if $author ne $user; |
---|
80 | |
---|
81 | # Mail watchers, if changeset involves files being watched |
---|
82 | # ---------------------------------------------------------------------------- |
---|
83 | # Find out what files were involved in the changeset |
---|
84 | @command = (qw/svnlook changed -r/, $rev, $repos); |
---|
85 | my @changed = qx(@command); |
---|
86 | |
---|
87 | # Get list of watchers for current repository |
---|
88 | my %watch = &get_watchers (); |
---|
89 | |
---|
90 | for my $file (@changed) { |
---|
91 | # Remove trailing line break and leading status |
---|
92 | chomp $file; |
---|
93 | $file = substr ($file, 4); |
---|
94 | |
---|
95 | # Find out who are watching this file |
---|
96 | my @watchers = &who_watch ($file, \%watch); |
---|
97 | |
---|
98 | # If necessary, add watchers to list, unless he/she is the current user |
---|
99 | for my $watcher (@watchers) { |
---|
100 | $mail_to{$watcher} = 1 if $user ne $watcher; |
---|
101 | } |
---|
102 | } |
---|
103 | |
---|
104 | # Send mail if necessary |
---|
105 | # ---------------------------------------------------------------------------- |
---|
106 | if (keys %mail_to) { |
---|
107 | # Old value of revision property |
---|
108 | my @command = (qw/svnlook pg -r/, $rev, '--revprop', $repos, $propname); |
---|
109 | my $oldval = qx(@command); |
---|
110 | |
---|
111 | # Addresses as a comma-separated list |
---|
112 | my $address = join (',', sort keys %mail_to); |
---|
113 | |
---|
114 | # Invoke a new Mail::Mailer object |
---|
115 | my $mailer = Mail::Mailer->new (); |
---|
116 | $mailer->open ({ |
---|
117 | From => 'my.name@somewhere.org', |
---|
118 | To => $address, |
---|
119 | Subject => $base . '@' . $rev . ': ' . $propname . ' modified by ' . $user, |
---|
120 | }) or die 'Cannot e-mail ', $address, ' (', $!, ')'; |
---|
121 | |
---|
122 | # Write the mail |
---|
123 | # Old value |
---|
124 | print $mailer <<EOF; |
---|
125 | Old value: |
---|
126 | ---------- |
---|
127 | $oldval |
---|
128 | |
---|
129 | New value: |
---|
130 | ---------- |
---|
131 | EOF |
---|
132 | |
---|
133 | # New value from STDIN |
---|
134 | print $mailer $_ while (<STDIN>); |
---|
135 | |
---|
136 | # Send the mail |
---|
137 | $mailer->close; |
---|
138 | |
---|
139 | print 'Mail notification has been sent to ', $address, '.', "\n"; |
---|
140 | |
---|
141 | } else { |
---|
142 | print 'No mail notification is required for this change.', "\n"; |
---|
143 | } |
---|
144 | } |
---|
145 | |
---|
146 | exit $return; |
---|
147 | |
---|
148 | # ------------------------------------------------------------------------------ |
---|
149 | # SYNOPSIS |
---|
150 | # %watch = &get_watchers (); |
---|
151 | # |
---|
152 | # DESCRIPTION |
---|
153 | # From the list of watch configuration files, get a list of watched files and |
---|
154 | # their watchers for the current repository. Returns the results in a hash |
---|
155 | # containing the watched paths (keys) and their corresponding list of |
---|
156 | # watchers (values, array references). |
---|
157 | # ------------------------------------------------------------------------------ |
---|
158 | |
---|
159 | sub get_watchers { |
---|
160 | my %watch; |
---|
161 | |
---|
162 | # Get contents in watch file |
---|
163 | my @command = (qw/svnlook cat/, $repos, $watch_config); |
---|
164 | my @output = qx(@command); |
---|
165 | |
---|
166 | if (@output) { |
---|
167 | # Write result to temporary file |
---|
168 | my ($fh, $temp_file) = tempfile (UNLINK => 1); |
---|
169 | print $fh @output; |
---|
170 | close $fh; |
---|
171 | |
---|
172 | # Parse the configuration |
---|
173 | my $cfg = Config::IniFiles->new ('-file' => $temp_file); |
---|
174 | |
---|
175 | # Check if current repository name exists in the configuration file |
---|
176 | if ($cfg and $cfg->SectionExists ($base)) { |
---|
177 | # The name of the parameter is a sub-path in the repository |
---|
178 | # The value of the parameter is a comma-delimited list of the watchers |
---|
179 | my $separator = '/'; |
---|
180 | for my $parameter ($cfg->Parameters ($base)) { |
---|
181 | # Parameter may contain wildcards * and ? |
---|
182 | $parameter =~ s#\*#[^$separator]*#g; |
---|
183 | $parameter =~ s#\?#[^$separator]#g; |
---|
184 | |
---|
185 | $watch{$parameter} = [split (/,/, $cfg->val ($base, $parameter))]; |
---|
186 | } |
---|
187 | } |
---|
188 | } |
---|
189 | |
---|
190 | return %watch; |
---|
191 | } |
---|
192 | |
---|
193 | # ------------------------------------------------------------------------------ |
---|
194 | # SYNOPSIS |
---|
195 | # my @watchers = &who_watch ($file, \%watch); |
---|
196 | # |
---|
197 | # DESCRIPTION |
---|
198 | # Using the %watch hash, determine who are the watchers watching $file. |
---|
199 | # Returns the list of watchers. |
---|
200 | # ------------------------------------------------------------------------------ |
---|
201 | |
---|
202 | sub who_watch { |
---|
203 | my $file = $_[0]; |
---|
204 | my %watch = %{ $_[1] }; |
---|
205 | |
---|
206 | my %watchers; |
---|
207 | my $separator = '/'; |
---|
208 | |
---|
209 | for my $watched (keys %watch) { |
---|
210 | # Test if $file or its parent path is being watched |
---|
211 | next unless $file =~ m#^$watched(?:$separator+|$)#; |
---|
212 | |
---|
213 | # Add watchers to the return list |
---|
214 | $watchers{$_} = 1 for (@{ $watch{$watched} }); |
---|
215 | } |
---|
216 | |
---|
217 | return keys %watchers; |
---|
218 | } |
---|
219 | |
---|
220 | # ------------------------------------------------------------------------------ |
---|
221 | |
---|
222 | __END__ |
---|