#!/usr/bin/perl # -*-Perl-*- # # Perl filter to handle the log messages from the checkin of files in # a directory. This script will group the lists of files by log # message, and mail a single consolidated log message at the end of # the commit. # # This file assumes a pre-commit checking program that leaves the # names of the first and last commit directories in a temporary file. # # Contributed by David Hampton # # hacked greatly by Greg A. Woods # Usage: log_accum.pl [-d] [-s] [-M module] [[-m mailto] ...] [-f logfile] # -d - turn on debugging # -G product - interface to Bugzilla # -m mailto - send mail to "mailto" (multiple) # -M modulename - set module name to "modulename" # -f logfile - write commit messages to logfile too # -s - *don't* run "cvs status -v" for each file # -T text - use TEXT in temp file names. # -C name - Generate cvsweb URLS; must be run using %{sVv} # format string. Set cvsroot= to "name". Use # -C NONE to pass cvsroot="". # -U URL - Base URL for cvsweb if -C option (above) is used. # -D DOMAIN - Domain from which mail should appear # -l - Update list of modified files in CVSROOT/commits. # Must be run using %{sVv}. # -i - Generate SQL INSERT statements for CVSQL. use POSIX; use DBI; # # Configurable options # # This MUST match TMPDIR defined in commit_prep $TMPDIR = "/var/tmp/cvs"; # Set this to something that takes "-s" $MAILER = "/usr/bin/Mail"; $BMAILER = "/usr/sbin/sendmail"; # Parameter -D dom.ain will be appended to these e-mail addresses $cvslist = "prj-vcs"; $bugzilla_mail = "bugzilla"; # BZ DB database/username/password # If you do not want to reveal these you may disable the SQL connection # as it is only a sanity check. So it is security vs sanity. YMMV. $database = "dbi:mysql:bugs"; $username = "bugs"; $password = "bugs"; # Base name of cvsweb specification $CVSWEB_URL = "http://www.dom.ain/cgi-bin/cvsweb.cgi/"; # Constants (don't change these!) # $STATE_NONE = 0; $STATE_CHANGED = 1; $STATE_ADDED = 2; $STATE_REMOVED = 3; $STATE_LOG = 4; # # Subroutines # sub see_if_bugzilla_bug_exists { local ($dbh, $product, $id) = @_; my $sth1 = $dbh->prepare ("SELECT id from products where name = \'$product\'") or return 0; $sth1->execute() or return 0; my $product_id = $sth1->fetchrow_array (); if (!$product_id) { return 0; } my $sth2 = $dbh->prepare ("SELECT COUNT(*) from bugs where bug_id = $id and product_id = $product_id") or return 0; $sth2->execute() or return 0; my $count = $sth2->fetchrow_array (); return $count > 0; } sub set_temp_vars { local ($name) = @_; $LAST_FILE = sprintf ("$TMPDIR/#%s.lastdir", $name); $CHANGED_FILE = sprintf ("$TMPDIR/#%s.files.changed", $name); $ADDED_FILE = sprintf ("$TMPDIR/#%s.files.added", $name); $REMOVED_FILE = sprintf ("$TMPDIR/#%s.files.removed", $name); $LOG_FILE = sprintf ("$TMPDIR/#%s.files.log", $name); $URL_FILE = sprintf ("$TMPDIR/#%s.files.urls", $name); $SQL_FILE = sprintf ("$TMPDIR/#%s.files.sql", $name); $COMMITS_FILE = sprintf ("$TMPDIR/#%s.files.commits", $name); # Quote for use in a regexp. ($FILE_PREFIX = sprintf ("#%s.files", $name)) =~ s/(\W)/\\$1/g; } sub cleanup_tmpfiles { local($wd, @files); $wd = `pwd`; chdir("$TMPDIR") || die("Can't chdir(\"$TMPDIR\")\n"); opendir(DIR, "."); push(@files, grep(/^$FILE_PREFIX\..*\.$id$/, readdir(DIR))); closedir(DIR); foreach (@files) { unlink $_; } unlink $LAST_FILE . "." . $id; chdir($wd); } sub write_logfile { local($filename, @lines) = @_; open(FILE, ">$filename") || die("Cannot open log file $filename.\n"); print FILE join("\n", @lines), "\n"; close(FILE); } sub format_names { local($dir, @files) = @_; local(@lines); if ($dir =~ /^\.\//) { $dir = $'; } if ($dir =~ /\/$/) { $dir = $`; } if ($dir eq "") { $dir = "."; } $format = "\t%-" . sprintf("%d", length($dir) > 15 ? length($dir) : 15) . "s%s "; $lines[0] = sprintf($format, $dir, ":"); if ($debug) { print STDERR "format_names(): dir = ", $dir, "; files = ", join(":", @files), ".\n"; } foreach $file (@files) { if (length($lines[$#lines]) + length($file) > 65) { $lines[++$#lines] = sprintf($format, " ", " "); } $lines[$#lines] .= $file . " "; } @lines; } sub format_lists { local(@lines) = @_; local(@text, @files, $lastdir); if ($debug) { print STDERR "format_lists(): ", join(":", @lines), "\n"; } @text = (); @files = (); $lastdir = shift @lines; # first thing is always a directory if ($lastdir !~ /.*\/$/) { die("Damn, $lastdir doesn't look like a directory!\n"); } foreach $line (@lines) { if ($line =~ /.*\/$/) { push(@text, &format_names($lastdir, @files)); $lastdir = $line; @files = (); } else { push(@files, $line); } } push(@text, &format_names($lastdir, @files)); @text; } sub accum_subject { local(@lines) = @_; local(@files, $lastdir); $lastdir = shift @lines; # first thing is always a directory @files = ($lastdir); if ($lastdir !~ /.*\/$/) { die("Damn, $lastdir doesn't look like a directory!\n"); } foreach $line (@lines) { if ($line =~ /.*\/$/) { $lastdir = $line; push(@files, $line); } else { push(@files, $lastdir . $line); } } @files; } sub compile_subject { local(@files) = @_; local($text, @a, @b, @c, $dir, $topdir, $topdir_length); # find the highest common directory $dir = '-'; do { $topdir = $dir; foreach $file (@files) { if ($file =~ /.*\/$/) { if ($dir eq '-') { $dir = $file; } else { if (index($dir,$file) == 0) { $dir = $file; } elsif (index($file,$dir) != 0) { @a = split /\//,$file; @b = split /\//,$dir; @c = (); CMP: while ($#a > 0 && $#b > 0) { if ($a[0] eq $b[0]) { push(@c, $a[0]); shift @a; shift @b; } else { last CMP; } } $dir = join('/',@c) . '/'; } } } } } until $dir eq $topdir; # strip out directories and the common prefix topdir. chop $topdir; if ($topdir eq "") { @c = $modulename; $topdir_length = 0; } else { if ($topdir eq ".") { @c = $modulename; } else { @c = ($modulename . '/' . $topdir); } $topdir_length = length ($topdir) + 1; } foreach $file (@files) { if ($file !~ /.*\/$/) { push(@c, substr($file, $topdir_length)); } } # put it together and limit the length. $text = join(' ',@c); if (length($text) > 50) { $text = substr($text, 0, 46) . ' ...'; } $text; } sub append_names_to_file { local($filename, $dir, @files) = @_; if (@files) { open(FILE, ">>$filename") || die("Cannot open file $filename.\n"); if (defined ($dir)) { print FILE $dir, "\n"; } print FILE join("\n", @files), "\n"; close(FILE); } } sub read_line { local($line); local($filename) = @_; open(FILE, "<$filename") || die("Cannot open file $filename.\n"); $line = ; close(FILE); chop($line); $line; } sub read_logfile { local(@text); local($filename, $leader) = @_; open(FILE, "<$filename"); while () { chop; push(@text, $leader.$_); } close(FILE); @text; } sub build_header { local($header); local($sec,$min,$hour,$mday,$mon,$year) = localtime(time); $header = sprintf("CVSROOT:\t%s\nModule name:\t%s\n", $cvsroot, $modulename); if (defined($branch)) { $header .= sprintf("Branch: \t%s\n", $branch); } $header .= sprintf("Changes by:\t%s@%s\t%04d-%02d-%02d %02d:%02d:%02d", $login, $hostdomain, $year + 1900, $mon+1, $mday, $hour, $min, $sec); } sub mail_notification { local($name, $subject, @text) = @_; open(MAIL, "| $MAILER -s \"$subject\" $name"); print MAIL join("\n", @text), "\n"; close(MAIL); } sub mail_bug_notification { local($name, $subject, @text) = @_; open(MAIL, "| $BMAILER -f\"$cvslist\@$hostdomain\" $name"); print MAIL "From: $cvslist\@$hostdomain\n"; print MAIL "Subject: $subject\n"; print MAIL "To: $name\n"; print MAIL "\n"; print MAIL join("\n", @text), "\n"; close(MAIL); } sub write_commitlog { local($logfile, @text) = @_; open(FILE, ">>$logfile"); print FILE join("\n", @text), "\n\n"; close(FILE); } # Return a sequence of SQL inserts. sub generate_cvsql_inserts { local ($dir, $branch, @files) = @_; local (@sp, @result); if ($branch eq '') { $branch = 'null'; } else { $branch = '\'' . $branch . '\''; } foreach (@files) { # List is (FILE OLD-REV NEW-REV). @sp = split (','); if ($sp[1] eq 'NONE') { $sp[1] = 'null'; } else { $sp[1] = '\'' . $sp[1] . '\''; } if ($sp[2] eq 'NONE') { $sp[2] = 'null'; } else { $sp[2] = '\'' . $sp[2] . '\''; } push (@result, 'INSERT INTO cvs_change (filename,prerev,postrev,branch,changeset) values (\'' . $dir . '/' . $sp[0] . '\',' . $sp[1] . ',' . $sp[2] . ',' . $branch . ',:id)'); } return @result; } # Return a list of all cvsweb URLs for this commit. sub generate_cvsweb_urls { local ($dir, $branch, @files) = @_; local (@sp, @result); local ($start) = $CVSWEB_URL . $dir . '/'; $cvsweb_name = undef if ($cvsweb_name eq 'NONE'); local ($args) = '.diff?cvsroot=' . $cvsweb_name; if ($branch ne '') { $args .= '&only_with_tag=' . $branch; } local ($r1); foreach (@files) { # List is (FILE OLD-REV NEW-REV). @sp = split (','); $r1 = $sp[1]; if ($r1 eq 'NONE') { # This lets us make a diff corresponding to the first # revision. $r1 = '0'; } push (@result, ($start . $sp[0] . $args . '&f=h' . '&r1=' . $sp[1] . '&r2=' . $sp[2])); } return @result; } ###### ## cvs commits logging for processing by other programs ## ## Everything takes place in $CVSROOT/commits - as long as that dir is ## writable and exists, the functions should handle the rest. ###### sub generate_modlist { my $path = shift; my $changed_files = shift; # incl operation, but not what rev # my $added_files = shift; my $removed_files = shift; my @filerevs = @_; # incl revision #'s but not what op changed them my @filelist = (); if ($#changed_files >= 0) { push @filelist, modlist_add_files ("chg", $path, $changed_files, \@filerevs); } if ($#added_files >= 0) { push @filelist, modlist_add_files ("add", $path, $added_files, \@filerevs); } if ($#removed_files >= 0) { push @filelist, modlist_add_files ("rm", $path, $removed_files, \@filerevs); } return @filelist; } sub modlist_add_files { my ($type, $topdir, $filelist, $filerevs) = @_; my $type_print = $type; my @lines_to_add = (); $topdir =~ s#^\./##; $topdir =~ s#/$##; $topdir =~ s#^\.$##; if ($topdir eq "./" || $topdir eq ".") { $topdir = ""; } else { $topdir = $topdir . "/"; } foreach $entry (@$filelist) { my $revs = modlist_get_file_revs ($entry, $filerevs); my $revstr = "NA NA"; if (defined ($revs)) { $revstr = "@$revs[0] @$revs[1]"; } if (defined ($branch)) { $type_print = "${type},branch=$branch"; } if ($type eq "rm") { push @lines_to_add, "$type_print $revstr ${topdir}Attic/" . $entry; } push @lines_to_add, "$type_print $revstr $topdir" . $entry; } return @lines_to_add; } sub modlist_get_file_revs { my $desired_filename = shift; my $filerevs = shift; my $filename = undef; my $oldrev = "NONE"; my $newrev = "NONE"; my @revpair; foreach (@$filerevs) { if (m#^\Q${desired_filename}\E#) { if (m/(.*),([0-9.NONE]*),([0-9.NONE]*)$/) { $filename = $1; $oldrev = $2; $newrev = $3; } elsif ($desired_filename eq $_) { $filename = $_; } } } if (!defined ($filename)) { return undef; } @revpair = ($oldrev, $newrev); return \@revpair; } sub modlist_newdir { my $dirname = shift; my @filelist = (); $dirname =~ s#^\./##; $dirname =~ s#^/##; $dirname =~ s#/$##; $dirname = $dirname . "/"; push @filelist, "newdir NA NA " . $dirname; modlist_write_to_log (@filelist); } sub modlist_import { my $parent_dir = shift; my $text = shift; my @filelist = (); $parent_dir =~ s#/$##; foreach (@$text) { chomp; if (m/^ ([UNC]) (.*)/) { my $type = $1; my $file = $2; $file =~ s#^\./##; $file =~ s#^/##; push @filelist, "import,stat=$type NA NA " . $file; } } modlist_write_to_log (@filelist); } # Add lines to the commit list logfile of the format # sub modlist_write_to_log { my @files = @_; my @lines = (); my $now = time (); my $month = sprintf ("%02d", (localtime)[4] + 1); my $year = (localtime)[5] + 1900; my $root_dir = "$cvsroot/CVSROOT/commits"; my $logdir = "$root_dir/commit-logs"; my $logfile = "$logdir/${year}-${month}"; my $plugins = "$root_dir/plugins"; foreach my $dir ("$root_dir", "$logdir") { if (! -d "$dir") { mkdir ("$dir", 0777) or warn "Unable to create commit-list log dir $dir"; chmod 0777, "$dir"; } } # Remove "./" at the beg of filenames; add ",v" at the end of filenames, # add username/timestamp to each line for printing. foreach my $f (@files) { next if (!defined ($f) or $f eq ""); $f =~ s#^\./##; $f =~ s# \./# #; if ($f !~ m#/$# && $f !~ m#,v$#) { $f = $f . ",v"; } if ($f =~ m#(.*)/$#) { $f = $1; } push @lines, "$now $login $f\n"; } ## Save the commit lines to the main log file if (open LOG, ">> $logfile") { foreach (@lines) { print LOG; } close (LOG); if (-o "$logfile") { chmod 0666, "$logfile"; } } else { warn "Warning: Unable to write to $logfile !\n"; } ## Run any plugin scripts and pass them the lines. if (-f "$plugins" && open PLUGINS, "< $plugins") { my $name; while () { chomp; next if m/^\s*#/; next if (! -x $_); do { $name = tmpnam(); } until sysopen (TMP, $name, O_RDWR | O_CREAT | O_EXCL, 0600); foreach (@lines) { print TMP; } close (TMP); open (OUTPUT, "$_ $name 1>&2|") or warn "Warning: Unable to execute plugin '$_' at commit-time"; while () { print; } close (OUTPUT); unlink "$name"; } close (PLUGINS); } } sub write_sql_batch { my $msg; $extension = time(); do { $extension++; $spoolfile = $TMPDIR . "/sql-tmp." . $extension; if ($debug) { print STDERR "Spooling SQL -> " . $spoolfile . "\n"; } } while (!sysopen SQL, $spoolfile, O_WRONLY | O_CREAT | O_EXCL, 0644); $msg = join ("\n", @unindented_log_txt); while ($msg =~ m,[\(<]([a-zA-Z0-9_\.+-]+@[a-zA-Z0-9\.-]+)[\)>](.*)$,s) { push (@authors, $1); $msg = $2; } if (scalar (@authors) == 0) { push (@authors, $login . "@" . $hostdomain); } foreach my $author (@authors) { my $insert = "INSERT INTO cvs_author values (:id,'" . $author . ")\n"; } $msg = join ("\n", @unindented_log_txt); # Escape certain characters. $msg =~ s,(\'|\"|\\),\\$1,g; # Replace newlines with `\n'. $msg =~ s,\n,\\n,g; my $now = strftime ("%Y-%m-%d %H:%M:%S %Z", localtime); my $insert = 'INSERT INTO cvs_changeset (id,committer,date,msg,cvsroot) values (:id,\'' . $login . "','" . $now . "','" . $msg . "','" . $hostdomain . ":" . $cvsroot . "')\n"; if ($debug) { print STDERR $insert; } print SQL $insert; foreach my $author (@authors) { my $insert = "INSERT INTO cvs_author values (:id,'" . $author . "')\n"; if ($debug) { print STDERR $insert; } print SQL $insert; } if ($debug) { print STDERR join("\n", @sql_stmts), "\n"; } print SQL join("\n", @sql_stmts), "\n"; close (SQL); $newfile = $TMPDIR . "/sql." . $extension; rename $spoolfile, $newfile } # # Main Body # # Initialize basic variables # $debug = 0; $id = getpgrp(); # note, you *must* use a shell which does setpgrp() $state = $STATE_NONE; $login = $ENV{'USER'} || (getpwuid($<))[0] || "nobody"; chop($hostname = `hostname`); $cvsroot = $ENV{'CVSROOT'}; $do_status = 1; $modulename = ""; $temp_name = "temp"; $do_cvsweb = 0; $cvsweb_name = ''; $do_modlist = 0; $do_cvsql = 0; # parse command line arguments (file list is seen as one arg) # while (@ARGV) { $arg = shift @ARGV; if ($arg eq '-d') { $debug = 1; print STDERR "Debug turned on...\n"; } elsif ($arg eq '-m') { $mailto = "$mailto " . shift @ARGV; } elsif ($arg eq '-M') { $modulename = shift @ARGV; } elsif ($arg eq '-s') { $do_status = 0; } elsif ($arg eq '-f') { ($commitlog) && die("Too many '-f' args\n"); $commitlog = shift @ARGV; } elsif ($arg eq '-G') { ($bugzillaproduct) && die("Too many '-G' args\n"); $bugzillaproduct = shift @ARGV; } elsif ($arg eq '-T') { $temp_name = shift @ARGV; } elsif ($arg eq '-C') { $do_cvsweb = 1; $cvsweb_name = shift @ARGV; } elsif ($arg eq '-U') { $CVSWEB_URL = shift @ARGV; } elsif ($arg eq '-D') { $hostdomain = shift @ARGV; } elsif ($arg eq '-l') { $do_modlist = 1; } elsif ($arg eq '-i') { $do_cvsql = 1; } else { ($donefiles) && die("Too many arguments! Check usage.\n"); $donefiles = 1; @files = split(/ /, $arg); } } if (defined ($hostdomain)) { # nothing } elsif ($hostname !~ /\./) { chop($domainname = `domainname`); $hostdomain = $hostname . "." . $domainname; } else { $hostdomain = $hostname; } # Used with sprintf to form name of notification mailing list. # %s argument comes from -G option. $MAIL_FORMAT = "$bugzilla_mail\@$hostdomain"; ($mailto) || die("No -m mail recipient specified\n"); &set_temp_vars ($temp_name); # for now, the first "file" is the repository directory being committed, # relative to the $CVSROOT location # @path = split('/', $files[0]); # XXX there are some ugly assumptions in here about module names and # XXX directories relative to the $CVSROOT location -- really should # XXX read $CVSROOT/CVSROOT/modules, but that's not so easy to do, since # XXX we have to parse it backwards. # if ($modulename eq "") { $modulename = $path[0]; # I.e. the module name == top-level dir } if ($commitlog ne "") { $commitlog = $cvsroot . "/" . $modulename . "/" . $commitlog unless ($commitlog =~ /^\//); } if ($#path == 0) { $dir = "."; } else { $dir = join('/', @path[1..$#path]); } $dir = $dir . "/"; if ($debug) { print STDERR "module - ", $modulename, "\n"; print STDERR "dir - ", $dir, "\n"; print STDERR "path - ", join(":", @path), "\n"; print STDERR "files - ", join(":", @files), "\n"; print STDERR "id - ", $id, "\n"; } # Check for a new directory first. This appears with files set as follows: # # files[0] - "path/name/newdir" # files[1] - "-" # files[2] - "New" # files[3] - "directory" # if ($files[2] =~ /New/ && $files[3] =~ /directory/) { local(@text); @text = (); push(@text, &build_header()); push(@text, ""); push(@text, $files[0]); push(@text, ""); while () { chop; # Drop the newline push(@text, $_); } &mail_notification($mailto, $files[0], @text); if ($commitlog) { &write_commitlog($commitlog, @text); } if ($do_modlist) { modlist_newdir ($files[0]); } exit 0; } # Iterate over the body of the message collecting information. # while () { chop; # Drop the newline if ($debug) { print STDERR "Line is $_\n"; print STDERR "State is currently $state\n"; } if (/^Modified Files/) { $state = $STATE_CHANGED; next; } if (/^Added Files/) { $state = $STATE_ADDED; next; } if (/^Removed Files/) { $state = $STATE_REMOVED; next; } if (/^Log Message/) { $state = $STATE_LOG; next; } if (/^\s*Tag:|Revision\/Branch/) { /^[^:]+:\s*(.*)/; $branch = $+; next; } s/^[ \t\n]+//; # delete leading whitespace s/[ \t\n]+$//; # delete trailing whitespace if ($state == $STATE_CHANGED) { push(@changed_files, split); } if ($state == $STATE_ADDED) { push(@added_files, split); } if ($state == $STATE_REMOVED) { push(@removed_files, split); } if ($state == $STATE_LOG) { push(@log_lines, $_); } } # Strip leading and trailing blank lines from the log message. Also # compress multiple blank lines in the body of the message down to a # single blank line. # while ($#log_lines > -1) { last if ($log_lines[0] ne ""); shift(@log_lines); } while ($#log_lines > -1) { last if ($log_lines[$#log_lines] ne ""); pop(@log_lines); } for ($i = $#log_lines; $i > 0; $i--) { if (($log_lines[$i - 1] eq "") && ($log_lines[$i] eq "")) { splice(@log_lines, $i, 1); } } # Check for an import command. This appears with files set as follows: # # files[0] - "path/name" # files[1] - "-" # files[2] - "Imported" # files[3] - "sources" # if ($files[2] =~ /Imported/ && $files[3] =~ /sources/) { local(@text); @text = (); push(@text, &build_header()); push(@text, ""); push(@text, "Log message:"); while ($#log_lines > -1) { push (@text, " " . $log_lines[0]); shift(@log_lines); } &mail_notification($mailto, "Import $file[0]", @text); if ($commitlog) { &write_commitlog($commitlog, @text); } if ($do_modlist) { modlist_import ($files[0], \@text); } exit 0; } # Compute the list of cvsweb URLs if necessary. if ($do_cvsweb) { @urls = &generate_cvsweb_urls (join ('/', @path), $branch, @files[1 .. $#files]); } if ($do_cvsql) { @sql = &generate_cvsql_inserts (join ('/', @path), $branch, @files[1 .. $#files]); } if ($do_modlist) { @modlist = generate_modlist (join ('/', @path), \@changed_files, \@added_files, \@removed_files, @files[1 .. $#files]); } if ($debug) { print STDERR "Searching for log file index..."; } # Find an index to a log file that matches this log message # for ($i = 0; ; $i++) { local(@text); last if (! -e "$LOG_FILE.$i.$id"); # the next available one @text = &read_logfile("$LOG_FILE.$i.$id", ""); last if ($#text == -1); # nothing in this file, use it last if (join(" ", @log_lines) eq join(" ", @text)); # it's the same log message as another } if ($debug) { print STDERR " found log file at $i.$id, now writing tmp files.\n"; } # Spit out the information gathered in this pass. # &append_names_to_file("$CHANGED_FILE.$i.$id", $dir, @changed_files); &append_names_to_file("$ADDED_FILE.$i.$id", $dir, @added_files); &append_names_to_file("$REMOVED_FILE.$i.$id", $dir, @removed_files); &append_names_to_file("$URL_FILE.$i.$id", $dir, @urls); &append_names_to_file("$SQL_FILE.$i.$id", $dir, @sql); &append_names_to_file("$COMMITS_FILE.$i.$id", undef, @modlist); &write_logfile("$LOG_FILE.$i.$id", @log_lines); # Check whether this is the last directory. If not, quit. # if ($debug) { print STDERR "Checking current dir against last dir.\n"; } $_ = &read_line("$LAST_FILE.$id"); if ($_ ne $cvsroot . "/" . $files[0]) { if ($debug) { print STDERR sprintf("Current directory %s is not last directory %s.\n", $cvsroot . "/" .$files[0], $_); } exit 0; } if ($debug) { print STDERR sprintf("Current directory %s is last directory %s -- all commits done.\n", $files[0], $_); } # # End Of Commits! # # This is it. The commits are all finished. Lump everything together # into a single message, fire a copy off to the mailing list, and drop # it on the end of the Changes file. # # # Produce the final compilation of the log messages # @text = (); @status_txt = (); @subject_files = (); @unindented_log_txt = (); @log_txt = (); @modlist_txt = (); push(@text, &build_header()); push(@text, ""); for ($i = 0; ; $i++) { last if (! -e "$LOG_FILE.$i.$id"); # we're done them all! @lines = &read_logfile("$CHANGED_FILE.$i.$id", ""); if ($#lines >= 0) { push(@text, "Modified files:"); push(@text, &format_lists(@lines)); push(@subject_files, &accum_subject(@lines)); } @lines = &read_logfile("$ADDED_FILE.$i.$id", ""); if ($#lines >= 0) { push(@text, "Added files:"); push(@text, &format_lists(@lines)); push(@subject_files, &accum_subject(@lines)); } @lines = &read_logfile("$REMOVED_FILE.$i.$id", ""); if ($#lines >= 0) { push(@text, "Removed files:"); push(@text, &format_lists(@lines)); push(@subject_files, &accum_subject(@lines)); } if ($#text >= 0) { push(@text, ""); } @lines = &read_logfile("$LOG_FILE.$i.$id", ""); if ($#lines >= 0) { push (@unindented_log_txt, @lines); } @log_txt = &read_logfile("$LOG_FILE.$i.$id", "\t"); if ($#log_txt >= 0) { push(@text, "Log message:"); push(@text, @log_txt); push(@text, ""); } @url_txt = &read_logfile("$URL_FILE.$i.$id", ""); if ($#url_txt >= 0) { push (@text, "Patches:"); # Exclude directories listed in the file. push (@text, grep (! /\/$/, @url_txt)); push (@text, ""); } @sql_txt = &read_logfile("$SQL_FILE.$i.$id", ""); if ($#sql_txt >= 0) { # Exclude directories listed in the file. push (@sql_stmts, grep (! /\/$/, @sql_txt)); } if ($do_modlist) { push (@modlist_txt, read_logfile("$COMMITS_FILE.$i.$id", "")); } if ($do_status) { local(@changed_files); @changed_files = (); push(@changed_files, &read_logfile("$CHANGED_FILE.$i.$id", "")); push(@changed_files, &read_logfile("$ADDED_FILE.$i.$id", "")); push(@changed_files, &read_logfile("$REMOVED_FILE.$i.$id", "")); if ($debug) { print STDERR "main: pre-sort changed_files = ", join(":", @changed_files), ".\n"; } @changed_files = sort(@changed_files); if ($debug) { print STDERR "main: post-sort changed_files = ", join(":", @changed_files), ".\n"; } foreach $dofile (@changed_files) { if ($dofile =~ /\/$/) { next; # ignore the silly "dir" entries } if ($debug) { print STDERR "main(): doing status on $dofile\n"; } open(STATUS, "-|") || exec 'cvs', '-nQq', 'status', '-v', $dofile; while () { chop; push(@status_txt, $_); } } } } $subject_txt = &compile_subject(@subject_files); # Write to the commitlog file # if ($commitlog) { &write_commitlog($commitlog, @text); } if ($#modlist_txt >= 0) { modlist_write_to_log (@modlist_txt); } if ($#status_txt >= 0) { push(@text, @status_txt); } # Mailout the notification. # &mail_notification($mailto, $subject_txt, @text); $log_txt = join ("\n", @log_txt); %done_ids = {}; while ($log_txt =~ m/[^Aa](?:bug|BugID:|PR|BZ)\s+\#?\s*(?:[a-z+-]+\/)?(?:\/)?(\d+)(.*)$/si) { $bug_id = $1; $log_txt = $2; if (!defined $done_ids{$bug_id}) { $done_ids{$bug_id} = 1; # Send mail to Bugzilla, if required. if ($bugzillaproduct ne '') { my $dbh = undef; $dbh = DBI->connect ($database, $username, $password); if ($debug) { print STDERR "Attempting to see if bug $bug_id exists\n"; } if (defined $dbh && &see_if_bugzilla_bug_exists ($dbh, $bugzillaproduct, $bug_id)) { if ($debug) { print STDERR "It does\n"; } &mail_bug_notification( sprintf ($MAIL_FORMAT, $bugzillaproduct), "[Bug $bug_id] Bug $bug_id", @text); } if (defined $dbh) { $dbh->disconnect; } } if ($do_cvsql) { push(@sql_stmts, 'INSERT INTO cvs_bugreport (changeset,id) values (:id, \'' . $bug_id . '\')'); } } } # Write out an SQL batch file. # if ($do_cvsql) { &write_sql_batch(); } # cleanup # if (! $debug) { &cleanup_tmpfiles(); } exit 0;