一週間のコミットをメールする

毎週、開発の中間報告を書いてたんだけど、しばらく放置状態だったので、それくらいなら自動化してしまおうかなーと思い立つ。svnコマンドラインクライアントをRubyから呼び、cronで週一で実行すればいいので、すぐ書けるはず。
と思ったら微妙なところでコケる。
svn logのログメッセージが、

weekly commit mail?\227?\130?\146?\227?\129?\168?\227?\130?\138?\227?\129?\130?\227?\129?\136?\227?\129?\154?\228?\189?\156?\230?\136?\144.

といった感じで化けている。

setenv LC_ALL ja_JP.eucJP

してみてもシェルのメッセージ(command not foundとか。何ていうのあれ?)は日本語化されるものの、svn logの内容は変わらず。シェルではなくsvnの問題っぽい。
~/.subversion/configを見てみる。

### Set log-encoding to the default encoding for log messages
# log-encoding = latin1

log-encodingをlatain1からeucJPに書き換えてみるものの変化なし。
Web上にもあんまり情報がないし、Unixあんま詳しくないので、これ以上設定変えて解決を図ってもうまくいかなさそう。ごり押しでいく!

  1. ?\を%に置換
  2. /\%[0-9]{3}/にマッチさせて、各3桁の10進数を2桁の16進数に変換。これでURLエンコード
  3. URI.decodeでデコード

したらうまくいった。まあかなり無理矢理なので、設定から解決できるようになったら修正するつもり。
シンプルにRubyからsvnのログを扱うSvnクラスを作る。simplesvn.rb。

class Svn
  def initialize( path)
    # リポジトリへのパスを与えて初期化.
    @path = path
  end
  def head_rev
    # HEADのリビジョンナンバーを返す.
    `svn log --quiet --limit 1 #{@path}|grep r`[/r[0-9]*\ /][1..-2].to_i
  end
  def log_hash( rev)
    # 指定されたリビジョンのログを,
    # body, rev, user, dateのキーを持つハッシュとして返す.
    require "date"
    arr = `svn log --limit 1 #{@path}@#{rev.to_s}`.split("\n")[1..-2]
    h = Hash.new
    arr[2].gsub!( "?\\", "%")
    keys = arr[2].scan( /\%[0-9]{3}/).uniq
    vals = keys.map{|s| "%#{s[1..-1].to_i.to_s(16)}"}
    keys.length.times do |i|
      arr[2].gsub!( keys[i], vals[i])
    end
    h['body'] = arr[2] 
    header = arr[0].split(" | ")
    h['rev']  = header[0][1..-1].to_i
    h['user'] = header[1]
    h['date'] = Date.strptime( header[2], "%Y-%m-%d")
    h
  end
  def rev_since( date)
    # 指定した日付の一番古いリビジョンナンバーを返す.
    require "date"
    stop = date - 1
    rev = head_rev
    h = Hash.new
    h['date'] = Date.today
    while stop <= h['date']
      rev = rev - 1
      h = log_hash( rev)
    end
    rev + 1
  end
end

#require "date"
#svn = Svn.new( "file:///path/to/repositry")
#puts head = svn.head_rev
#h = svn.log_hash( head)
#puts h['body']
#puts h['rev'].to_s
#puts h['user'].to_s
#puts h['date'].to_s
#week = Date.today - 7 
#puts week.to_s
#puts oldest = svn.rev_since( week)

で、こいつを使ってメールを送るweeklycommit.rb。メールの送信にはid:i123さんのSimpleMailクラスを利用。

require "cgi"
require "uri"
require "kconv"
require "date"
require "net/pop"
require "simplesvn"
require "simplemail"

svn = Svn.new("file:///path/to/repositry")
subject = "今週のコミット"
t  = Time.new
pt = t - 604800
message << "今週もお疲れさまです。\n#{pt.month}#{pt.day}日から#{t.month}#{t.day}日の間に、\n社内のsvnによせられたコミットは次のとおりです。\n(時系列順)".toutf8

head   = svn.head_rev
oldest = svn.rev_since( Date.today - 7)

(oldest..head).each do |i|
  h = svn.log_hash( i)
  s = "\n\n"
  s << "==== #{h['user']}: #{h['date'].to_s.gsub("-","/")} ===============\n"
  s << URI.decode( h['body'])
  message << s
end

# さくらなのでPOP Before SMTP
pop = Net::POP3.new( "popserver")
pop.auth_only( "from@mail.addre.ss", "password")

smail = SimpleMail.new( "from@mail.addre.ss", "mailserver")
smail.send( "to@mail.addre.ss", subject.kconv( Kconv::JIS, Kconv::UTF8), message.tojis)

こいつをcronで週一で呼び出す。crontabに以下の設定を追加。環境変数の設定を忘れずに。

*/5 * * * * ruby /path/to/weeklycommit.rb

これで5分ごとにメールがくるはず。
しばらく放置していると、ちょっと放置しすぎて40分の間に8通のメールが。うまくいってる。
今度は毎週月曜日のPM8:00にメールを送るようにする。

0 20 * * 1 ruby /path/to/weeklycommit.rb

テストではうまくいったけど、この設定でうまく動くかは不安。あと5時間のはずだけど...リアルタイムの絡むデバッグってやりにくいよね。