Cvsloginfo2mail.pl
From Stack Overflow
Sends diff emails whenever someone checks in a change. It can be installed by adding the following line to your CVSROOT loginfo file:
DEFAULT /usr/local/bin/cvsloginfo2mail.pl -d /var/cvs -w cvsweb -L en %{sVv} ADDRESS@HOST.COM
#!/usr/bin/perl -w
#
# CVS loginfo 2 mail
# Copyright (c) 2001 SATOH Fumiyasu. All rights reserved.
#
# Date: 2001-11-26, since 2001-10-29
# License: GNU General Public License ver.2
#
# Required external commands:
# sendmail
# rcsdiff
#
# $Id: cvsloginfo2mail.pl.txt,v 1.1 2004/04/16 22:12:43 enigma Exp $
# FIXME: Support to customize URL for CVS web interface
# FIXME: Support to customize sender address
# FIXME: Support diff(1) options for rcsdiff(1)
# FIXME: Support chroot(2)-ed environment
use strict;
use English;
use Getopt::Std;
use IO::File;
$ENV{'PATH'} = "/usr/bin:/usr/sbin:/usr/lib:$ENV{'PATH'}";
my $NAME ="cvsloginfo2mail.pl";
my $VERSION = "0.1.5";
# Options
# ======================================================================
# CVS root
my $cvs_root = $ENV{'CVSROOT'};
# CVS Web interface by cvsweb or ViewCVS
my $cvsweb_url = 'http://oscar/cvsweb/index';
# CVS Web interface Type: 'cvsweb' or 'viewcvs'
my $cvsweb_type = 'cvsweb';
# Content language
my $lang = 'NONE';
my $usage = <<EOF_USAGE;
Usage: in \$CVSROOT/CVSROOT/loginfo
^module \$CVSROOT/CVSROOT/$NAME [options] %{sVv} address ...
Options:
-d cvsroot CVS root directory [\$CVSROOT]
-w webtype Type of web interface [$cvsweb_type]
-f address Envelope sender address (not implemented yet)
-D Suppress rcsdiff(1) output
-L language Content language [$lang]
%{sVv} CVS loginfo
address Mail CVS loginfo to this address(es)
Type of web interface:
viewcvs ViewCVS - http://viewcvs.sourceforge.net/
cvsweb cvsweb - http://stud.fh-heilbronn.de/~zeller/cgi/cvsweb.cgi/
Content Language:
ja Japanese (ISO-2022-JP)
EOF_USAGE
# Command-line options
# ----------------------------------------------------------------------
use vars qw($opt_d $opt_w $opt_f $opt_D $opt_L);
if (!getopts('d:w:f:DL:') || @ARGV < 2) {
print($usage);
exit(1);
}
$cvs_root = $opt_d if (defined($opt_d));
$cvsweb_type = $opt_w if (defined($opt_w));
$lang = $opt_L if (defined($opt_L));
my $cvs_info = shift(@ARGV);
my @recip = @ARGV;
# Language specific?
# ======================================================================
my $ja_convert = undef;
if ($lang eq 'ja') {
eval('use Jcode');
if ($@) {
eval('require "jcode.pl"');
if ($@) {
die "Jcode.pm or jcode.pl required to process Japanese.\n";
}
$ja_convert = \&jcode::convert;
} else {
$ja_convert = \&Jcode::convert;
}
}
# Parse %{sVv}
# ======================================================================
$cvs_info =~ s/ - (New directory|Imported sources)//g;
my ($cvs_module, @cvs_fileinfo) = split(/ /, $cvs_info);
my @cvs_file = my %cvs_rev_old = my %cvs_rev_new = ();
foreach my $cvs_fileinfo (@cvs_fileinfo) {
my ($file, $rev_old, $rev_new) = split(/,/, $cvs_fileinfo);
push(@cvs_file, $file);
$cvs_rev_old{$file} = $rev_old;
$cvs_rev_new{$file} = $rev_new;
}
# Get CVS username
# ======================================================================
my $cvs_user = $ENV{'CVS_USER'};
$cvs_user = getpwuid($EUID) if (!defined($cvs_user));
$cvs_user = $EUID if (!defined($cvs_user));
# Make mail header
# ======================================================================
my $header = <<EOF_HEADER;
Subject: $cvs_module committed by $cvs_user
X-CVS-LogInfo: $NAME/$VERSION
X-CVS-Root: $cvs_root
X-CVS-User: $cvs_user
EOF_HEADER
$header .= "To: ";
$header .= join(",\n ", @recip) . "\n";
$header .= 'Content-Type: text/plain';
$header .= '; charset=ISO-2022-JP' if ($opt_L eq 'ja');
$header .= "\n";
foreach my $cvs_file (@cvs_file) {
my $file = "$cvs_module/$cvs_file";
my $rev_old = $cvs_rev_old{$cvs_file} || '';
my $rev_new = $cvs_rev_new{$cvs_file} || '';
if ($rev_old eq 'NONE') {
$header .= "X-CVS-Added";
} elsif ($rev_new eq 'NONE') {
$header .= "X-CVS-Removed";
} else {
$header .= "X-CVS-Modified";
}
$header .= ": $file $rev_old->$rev_new\n";
}
# Send mail
# ======================================================================
my $fh_sendmail = new IO::File;
my $pid_sendmail = $fh_sendmail->open('|-');
if (!defined($pid_sendmail)) { # fork failed
print "Cannot fork child or open pipe: $!\n";
exit(1);
} elsif ($pid_sendmail == 0) { # child process
if (!exec('sendmail', '-t', '-oi')) {
print "Cannot exec sendmail: $!\n";
exit(1);
}
}
# Print header fields and log from cvs command
# ----------------------------------------------------------------------
$fh_sendmail->print($header);
$fh_sendmail->print("\n");
while (defined(my $line = STDIN->getline())) {
if ($opt_L eq 'ja') {
&$ja_convert(\$line, 'jis');
}
$fh_sendmail->print($line);
}
$fh_sendmail->print("\n");
# Print commitment type and URL for web interface
# ----------------------------------------------------------------------
foreach my $cvs_file (@cvs_file) {
my $file = "$cvs_module/$cvs_file";
my $rev_old = $cvs_rev_old{$cvs_file} || '';
my $rev_new = $cvs_rev_new{$cvs_file} || '';
$fh_sendmail->print("$file $rev_old -> $rev_new (");
if ($rev_old eq 'NONE') {
$fh_sendmail->print('added');
} elsif ($rev_new eq 'NONE') {
$fh_sendmail->print('removed');
} else {
$fh_sendmail->print('modified');
}
$fh_sendmail->print(")\n");
$fh_sendmail->print("$cvsweb_url/$file");
if ($rev_old eq 'NONE' || $rev_new eq 'NONE') {
$fh_sendmail->print("?rev=");
$fh_sendmail->print($rev_old eq 'NONE' ? $rev_new : $rev_old);
$fh_sendmail->print('&content-type=text/');
if ($cvsweb_type eq 'cvsweb') {
$fh_sendmail->print('x-cvsweb-markup');
} else {
$fh_sendmail->print('vnd.viewcvs-markup');
}
$fh_sendmail->print("\n");
} else {
$fh_sendmail->print(".diff?r1=$rev_old&r2=$rev_new\n");
}
}
# Print diff by rcsdiff
# ----------------------------------------------------------------------
if ($opt_D) {
exit(0);
}
chdir($cvs_root) || die "Cannot change working directory: $!\n";
foreach my $cvs_file (@cvs_file) {
my $file = "$cvs_module/$cvs_file,v";
my $rev_old = $cvs_rev_old{$cvs_file} || '';
my $rev_new = $cvs_rev_new{$cvs_file} || '';
next if ($rev_new eq 'NONE' || $rev_old eq 'NONE');
$fh_sendmail->print("\n");
my $fh_rcsdiff = new IO::File;
my $pid_rcsdiff = $fh_rcsdiff->open('-|');
if (!defined($pid_rcsdiff)) { # fork failed
print "Cannot fork child or open pipe: $!\n";
exit(1);
} elsif ($pid_rcsdiff == 0) { # child process
open(STDERR, '>&STDOUT') || warn "Cannot duplicate stdout: $!\n";
#if (!exec('rcsdiff', '-u', '-kk', "-r$rev_old", "-r$rev_new", $file)) {
if (!exec('rcsdiff', '-b', '-u', '-kk', "-r$rev_old", "-r$rev_new", $file)) {
print "Cannot exec rcsdiff: $!\n";
exit(1);
}
}
while (defined(my $line = $fh_rcsdiff->getline())) {
if ($opt_L eq 'ja') {
&$ja_convert(\$line, 'jis');
}
$fh_sendmail->print($line);
}
# FIXME: Need close and wait child?
}
# FIXME: Check sendmail exit code.
# Done
# ======================================================================
exit(0);