update readme and add gitignore
[client-tools.git] / tools / p4_external_review.pl
blobc5a81bfc2578d7e93deed021ce1357e0fbf221b9
1 #!/usr/bin/perl
2 # ======================================================================
3 # ======================================================================
5 use Net::SMTP;
7 # Perfreview
9 #### What Does perfreview Do?
11 # perfreview - A change review 'daemon' for Perforce changes.
12 # Sends email to user when files they've subscribed to
13 # change in the depot.
15 #### How Does it Work?
17 # Uses 'p4 review' to dish up changes for review,
18 # 'p4 reviews' to find out who should review the changes,
19 # 'p4 describe' to fill out mail to send to users, and
20 # Net::SMTP to deliver the mail.
22 #### Instructions: Please read before running!
24 # 1) There is no step 1.
26 # 2) This script can be installed in any directory, although
27 # we recommend installing it in the same directory as
28 # the p4d server.
30 # 3) If necessary, change the top line of this script to point to the
31 # location of your local Perl executable. The Perl version should
32 # be 4 or higher.
34 # 4) Change the values of the following variables as desired:
36 # $once says process the queue once instead of every minute;
37 # This is good for being called by cron(8).
39 # $sleepTime is the number of seconds to wait before doing the
40 # next change review (the default is 60 seconds);
42 # $doOutput, a Boolean value, specifies whether or not any output
43 # is written to STDOUT (the default is false).
45 # $p4server is the name of your server
47 # $p4 is the executable and global arguments.
49 # $mailhost is the name of the SMTP email host.
51 # $lockfile is the name of a lockfile to be used in case this is
52 # invoked from a cron job, and the script hasn't finished yet.
54 # $counter is the name of the counter to be used. (default = 'review')
56 # $p4db is the partial URL for an instance of P4DB. Undefine it if you
57 # don't have P4DB.
59 # 5) If $doOutput is set to 0, run perfreview as
61 # perfreview.perl &
63 # Otherwise, run it as
65 # perfreview.perl >> somefile &
67 ####
69 # ======================================================================
70 # Globals
71 # ======================================================================
73 my $once = 1;
74 my $sleepTime = 60;
75 my $doOutput = 0;
76 my $p4server = "aus-perforce1.station.sony.com";
77 my $p4 = "/usr/local/bin/p4 -p $p4server:1666";
78 my $mailhost = "127.0.0.1:25";
79 my $lockfile = "/tmp/perfreview.lock";
80 my $counter = "review";
81 my $maillist = "//depot/admin/data/maillist.txt";
82 my $p4db = "http://perforce/p4db/chv.cgi?CH=";
84 # Temp variable
85 my %depot_email = ();
87 # ======================================================================
88 # Subroutines
89 # ======================================================================
91 sub p4_filespec_to_regexp
93 local $_ = $_[0];
94 my $out = "";
96 # process the leading part of the input
97 while (length($_))
99 if (s/^([^\.\*\(\)\{\}\|\\\[\]\?\+]+)//)
101 # copy non-wildcard leading characters
102 $out .= $1;
104 elsif (s/^\.\.\.//)
106 # convert ... to .*
107 $out .= ".*";
109 elsif (s/^\*//)
111 # convert * to [^/]*
112 $out .= "[^/]*";
114 elsif (s/^([\.\(\)\{\}\|\\\[\]\?\+])//)
116 # convert . to \.
117 $out .= "\\$1";
119 else
121 die "should never get here: $_\n";
125 return $out;
128 # ======================================================================
129 # Main
130 # ======================================================================
132 if (-e $lockfile)
134 die "Lockfile $lockfile already exists.";
136 open(LOCKFILE, ">$lockfile") || die "Cannot create lockfile $lockfile\n";
137 print LOCKFILE "locked\n";
138 close(LOCKFILE) || die "Cannot close lockfile $lockfile\n";
140 print("START: ". localtime() . "\n") if ($doOutput);
142 # make unbuffered
143 select(STDOUT);
144 $| = 1;
146 # if -1 is given, we'll run once
147 $flag = shift( @ARGV );
148 $once = 1 if( $flag eq '-1' );
152 # Remember highest numbered change we see, so that we can
153 # reset the 'review' counter once all mail is delivered.
154 # If we crash, the worst that happens is we'll send the mail
155 # again.
157 local( $topChange ) = 0;
159 open(P4PRINT, "$p4 print -q $maillist|");
160 while ( <P4PRINT> ) {
161 # Format: //depot/<path> email
162 if ( m!(//depot/\S*)\s+(.+)! )
164 push( @{ $depot_email{p4_filespec_to_regexp($1)} }, split(/\s+/, $2));
167 close(P4PRINT);
170 # REVIEW - list of changes to review.
173 open( REVIEW, "$p4 review -t $counter|" ) || next;
174 while( <REVIEW> )
178 # Format: "Change x user <email> (Full Name)"
180 local( $change, $user, $email, $fullName ) = /Change (\d*) (\S*) <(\S*)> (\(.*\))$/;
181 print (localtime() . "review '$change', user='$user', email='$email', fullname='$fullName'\n") if ($doOutput);
184 # Get list of people who will review this change
186 print(localtime() . "...mail start: From: $email\n") if ($doOutput);
188 open( REVIEWERS, "$p4 reviews -c $change|" ) || next;
189 local (@reviewers) = ();
190 while( <REVIEWERS> )
192 # user <email> (Full Name)
193 local( $user2, $email2, $fullName2 ) = /(\S*) <(\S*)> (\(.*\))$/;
194 print (localtime() . "......reviewer user2='$user2', email2='$email2', fullname2='$fullName2'\n") if ($doOutput);
195 push(@reviewers, $email2);
197 close( REVIEWERS );
199 open( DESCRIBE, "$p4 describe -s $change|" );
200 local (@describe) = ();
201 my %branches;
202 my %email_seen = ();
203 while( <DESCRIBE> )
205 # don't allow single .'s through
206 $_ = "..\n" if( $_ eq ".\n" );
208 push(@describe, $_);
210 # email list
211 foreach my $tok (keys %depot_email)
213 # This line checks the file versus what's stored in the maillist.txt
214 if (m!^\.\.\. ($tok)!)
216 foreach my $e ( @{ $depot_email{$1} } )
218 $email_seen{$e} = 1;
223 # branch name -- add other if() cases here first for odd project/branches
224 # example:
225 # if (m!\.\.\. //depot/projecta/([^/]+/[^/]+)/[^\#]+\#\d+ \w+!)
227 # $branches{$1} = 1;
228 # } else
229 if (m!\.\.\. //depot/([^/]+/[^/]+)/[^\#]+\#\d+ \w+!)
231 $branches{$1} = 1;
234 close( DESCRIBE );
236 my @branches = keys(%branches);
238 my $smtp = Net::SMTP->new($mailhost) || die "cannot connect to $mailhost via SMTP";
240 $smtp->mail("$email $fullName");
241 $smtp->to("$email");
243 foreach (keys %email_seen)
245 $smtp->to($_);
247 foreach (@reviewers)
249 $smtp->bcc($_);
252 $smtp->data();
253 $smtp->datasend("To: $email\n");
254 foreach (keys %email_seen)
256 $smtp->datasend("To: $_\n");
258 $smtp->datasend("Reply-To: $email\n");
260 $smtp->datasend("Subject: PERFORCE change $change for review on $p4server");
262 if (scalar(@branches))
264 $smtp->datasend(", branch(es): " . join(" ", @branches));
266 $smtp->datasend("\n\n");
268 # if you have a P4DB, here is where you can put the
269 $smtp->datasend("<" . $p4db . $change . ">\n") if defined $p4db;
270 $smtp->datasend(@describe);
271 $smtp->dataend();
272 $smtp->quit();
274 print(localtime() . "......mail end: From: $email\n") if ($doOutput);
276 $topChange = $change;
280 # Update counter to reflect changes reviewed.
283 system( "$p4 review -c $topChange -t $counter" ) if( $topChange );
285 print("Update counters...\n") if ($doOutput)
287 while( !$once && sleep($sleepTime) );
289 print("END: " . localtime() . "\n") if ($doOutput);
290 unlink "$lockfile";