Cvsloginfo2mail.pl

From Stack Overflow
Jump to: navigation, search

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);
Personal tools