2005-02-06

A better Subversion post-commit hook than commit-email.pl

I finally got round to automating sending out commit emails for our Subversion repositories. I tried the supplied post-commit.tmpl and commit-email.pl, but they suck. Six hundred lines of Perl and no effort made to color the patch or format the check-in comment?

I knocked up something that isn't going to win any beauty contents unless it's only up against commit-email.pl, in which case it should be a walk-over.

Here's the script; as usual, the latest version will be in salma-hayek:

#!/usr/bin/ruby -w

# A Subversion post-commit hook. Edit the configurable stuff below, and
# copy into your repository's hooks/ directory as "post-commit". Don't
# forget to "chmod a+x post-commit".

# ------------------------------------------------------------------------

# You *will* need to change these.

address="software@jessies.org"
sendmail="/usr/sbin/sendmail"
svnlook="/opt/subversion-1.1.0-rc2/bin/svnlook"

# ------------------------------------------------------------------------

require 'cgi'

# Subversion's commit-email.pl suggests that svnlook might create files.
Dir.chdir("/tmp")

# What revision in what repository?
repo = ARGV.shift()
rev = ARGV.shift()

# Get the overview information.
info=`#{svnlook} info #{repo} -r #{rev}`
info_lines=info.split("\n")
author=info_lines.shift
date=info_lines.shift
info_lines.shift
comment=info_lines

body = ""

# Output the overview.
body << "<p><b>#{author}</b> #{date}</p>"
body << "<p>"
comment.each() {
|line|
body << CGI.escapeHTML(line)
body << "<br>\n"
}
body << "</p>"
body << "<hr noshade>"

# Get and output the patch.
changes=`#{svnlook} diff #{repo} -r #{rev}`
body << "<pre>"
changes.split('\n').each() {
|line|
color = "black"
if line =~ /^Modified: / || line =~ /^=+$/ || line =~ /^@@ /
color = "gray"
elsif line =~ /^-/
color = "red"
elsif line =~ /^\+/
color = "blue"
end

body << "<font color=\"#{color}\">#{CGI.escapeHTML(line)}</font>\n"
}
body << "</pre>"

# Write the header.
header = ""
header << "To: #{address}\n"
header << "From: #{address}\n"
header << "Subject: #{repo} revision #{rev}\n"
header << "Reply-to: #{address}\n"
header << "MIME-Version: 1.0\n"
header << "Content-Type: text/html; charset=UTF-8\n"
header << "Content-Transfer-Encoding: 8bit\n"
header << "\n"

# Send the mail.
begin
fd = open("|#{sendmail} #{address}", "w")
fd.print(header)
fd.print(body)
rescue
exit(1)
end
fd.close

# We're done.
exit(0)

[Update: without the MIME-Version field in the header, the Content-Type field isn't valid, and this matters to some programs.]