subversion+redmineで、コミットされたらすぐにRedmineのリポジトリーやらなんやらに反映させる方法

Redmineリポジトリはshow/project/xxxを一旦見ないとリポジトリやらなんやらが更新されません。
これでは都合が悪いというか、私は怠惰なので許せないので、
subversionでコミットされたらredmineの方も更新されるように細工をしてみました。
(前提として、redminesubversionサーバは一緒のサーバです。)

まず、フックファイルをおく

subversionディレクトリにはhooksというディレクトリがあって、
この中にpost-commitというファイルをchmod 755しておいておくと
コミットされたときに呼び出されます。
post-commitの中身は

#!/bin/sh
sudo -u www-data /svn/hooks/update_changeset.sh

これだけ。

フックからsudoされて呼び出されるスクリプトを書く

/svn/hooks/update_changeset.shの中身は

#!/bin/sh
cd /var/www/default-ssl/redmine
export RAILS_ENV=production
ruby script/runner 'Project.find("knj").repository.fetch_changesets'

これだけ。環境変数を与えなければならないので、わざわざ別ファイルにしています。
そして、ユーザーがsudoしたときにパスワードを聞かれないように
/etc/sudoersを修正します。

%subversion ALL = (ALL) NOPASSWD: /svn/hooks/update_changeset.sh

subversionグループの人はノーパスワードで/svn/hooks/update_changeset.shをsudoで
叩くことができるという意味合いです。

さらにコミットメールにリンクを埋め込む

これをコミットがあったときに送信されるメールに埋め込むと便利です。
具体的にはこんなメールです。

Subversion committed to /svn 5016
http://foo.bar/redmine/projects/show/baz
--------------------------------------------------------
author : xibbar
date   : 2008-10-28 15:35:25 +0900 (Tue, 28 Oct 2008)
Log    : メール送信の条件を変更

Changed:               [U:Update, A:Add, D:Delete]
U   trunk/app/models/user_notifier.rb

--------------------------------------------------------

ChangeSet:
http://foo.bar/redmine/repositories/revision/baz?rev=5016

うちの設定を全部晒してみると、
post-commit

!/bin/sh
REPOS="$1"
REV="$2"
${REPOS}/hooks/email-send.rb "$REPOS" "$REV"
sudo -u www-data /svn/hooks/update_changeset.sh

email-send.rb

#!/usr/bin/ruby
require 'net/smtp'
require 'kconv'
require 'base64'

REPOS=ARGV[0]
REV=ARGV[1].to_i

def svnlog2utf8(str="")
  str.gsub!(/\?\\(...)/) {
    $1.to_i.chr
  }
  str
end

svnauthor=%x{svnlook author #{REPOS} -r #{REV}}.chomp!
svndate=%x{svnlook date #{REPOS} -r #{REV}}.chomp!
svnchanged=%x{svnlook changed #{REPOS} -r #{REV}}.chomp!
svnlog=svnlog2utf8(%x{svnlook log #{REPOS} -r #{REV}}.chomp!).strip
svndiff=%x{svnlook diff #{REPOS} -r #{REV}}.chomp!

tmpary=[]
svnchanged.each_line{|line|
        line=~/^.\s*(.*?)\//
                tmpary.push $1
}
tmpary.uniq!
if tmpary.size==1
  projectname="[#{tmpary[0]}] "
else
  projectname=" "
end

toaddr=['developer@foo.bar'] # 送信先をarrayで入れる
mail_subject=''
svnlog.scan(/.{1,15}/u){|str|
puts str
  str_jis=NKF.nkf("-Wj",str)
  mail_subject+=" =?iso-2022-jp?B?"+
  Base64.encode64(str_jis).chomp+"?="+"\n"
}
mail_subject.strip!

body = <<BODY
Subversion committed to #{REPOS} #{REV}
http://foo.bar/redmine/projects/show/baz
--------------------------------------------------------
author : #{svnauthor.ljust(45)}
date   : #{svndate}
Log    : #{svnlog.toutf8}

Changed:               [U:Update, A:Add, D:Delete]
#{svnchanged}

--------------------------------------------------------

ChangeSet:
http://foo.bar/redmine/repositories/revision/baz?rev=#{REV}
BODY

Net::SMTP.start( 'localhost', 25 ) {|smtp|
  smtp.send_mail <<EndOfMail, 'noreply@foo.bar', *toaddr
From: FOO BAR subversion <noreply@foo.bar>
To: FOO BAR Developpers <developer@foo.bar>
Subject: [svn-#{REV}] #{mail_subject} 
MIME-Version: 1.0
Content-Type: text/plain; charset = iso-2022-jp

#{body.tojis}
EndOfMail
}

便利なコミットメール+redmineの世界にようこそ

このemail-send.rbをhooksディレクトリにおき、chmod 755しておき、
メールアドレスやらリポジトリやらを適切に設定すれば、
コミットしたときにメールが飛び、
その中に入っているURLをクリックするとchangesetが簡単に見られるという
超すてきな開発ライフが待っています。

さらにdiffのURLとかも入れられるよ

https://foo.bar/redmine/repositories/diff/baz?rev=#{REV}

とかいうリンクもメールの中に埋め込んでおけば、
diffもメールから一発でいけます。

ぜひお試しあれ。

おしまい