PlaggerでCommit2Mail on Subversion

ここ数日Plaggerで遊んでいました。


あれだけ担当内でRuby使えと言ってまわったのにPerlかよ!という突っ込みはさておき。


Plaggerが何か知らない人はhttp://tokyo.yapcasia.org/wiki/plugin/attachments/SlidesList/Plagger.pdfを読むと良いです。



うちの担当ではSVNを利用してますが、そのサーバーがファイアウォールで守られたマシン室に設置してあります。
これはこれでセキュリティが守られるのでいいんですが、マシン室から対外アクセスできないので、同時に利用しているTracのmail notification機能(SVNにコミットしたらメールしてくれる機能。なんでSVNじゃなくてTracについてるかは謎。)が使えないことになります。SMTPサーバがマシン室とは違う場所に置いてあるから。


でもこの機能がないとすごーく不便なので、どうしようか考えてました。
で、最近トラックバック先(MASATOの開発日記: http://www.sutosoft.com/room/archives/000252.html)で素晴らしいスクリプトを見つけたので

  • SVNサーバにコミットされたらRSSフィードを出力して、
  • Plaggerでマシン室外のPCからRSSを取得してメールする。
  • Plaggerはcronで定期実行。

というのをやってみることにしました。
こういう環境ってよくあるのではないかと思いますので、作業メモを書いておきます。
ただしすっっっっっごい面倒くさかったので一部うろ覚えです。

自席マシンにPlaggerをインストールする、のまえにインターネットアクセスできるようにする。

Plaggerのインストールは各所で言われているようにすごくめんどくさいです。
自席マシンのOSはFedora Core 3なので
http://www.ark-web.jp/sandbox/wiki/index.php?plagger%2Finstall#kb0c360f
を参考にしようとしましたが、cpanをインストールするときに、「そもそもインターネットアクセスの設定をしてなかった」ということに気づきました。
といっても社内ネットワークには接続されているので、あとはプロキシ認証を通すだけです。

http://bbs.fedora.jp/read.php?FID=9&TID=3295
によると

$ export http_proxy=http://user:pass@XXXX.XXXX.XXXX.XXXX:port/

とするそうです。認証つきプロキシの設定に関する情報が他に見つからなかったので助かりました。
ただし、yumは内部でFTP使ってるかもしれないので

$ export http_proxy http://user:pass@proxy.xxxx.jp:8080/
$ export ftp_proxy http://user:pass@proxy.xxxx.jp:8080/
$ export HTTP_PROXY http://user:pass@proxy.xxxx.jp:8080/
$ export FTP_PROXY http://user:pass@proxy.xxxx.jp:8080/

としておくのが無難です。
環境変数名の大文字小文字はコマンドによって区別するのとしないのがあるらしいので、どっちもやっておくのがよいです。
なお、同掲示板にシェルスクリプトを作っておく方法が用意されていますが、IDとパスワードがモロばれになるのでやめておいたほうがよいです。
exportしたあとは

$ history -c

でちゃんとコマンド履歴を消しておきます。
でもログアウトするとこの環境変数設定が無効になるのでかなり面倒です…

http://www.ark-web.jp/sandbox/wiki/index.php?plagger%2Finstall#kb0c360f
を参考にyumります。

yum install gnupg
yum install ncftp
yum install unzip
yum install gcc
yum install make
yum install libxml2-devel
yum install libxml-devel
yum install expat-devel
yum install openssl-devel

で、

yum install perl-Plagger

したんですが無いといわれる始末。
しょうがないので、CPAN経由でインストールすることにします。(つづく)

補足

http://blog.woremacx.com/2006/03/plagger_yum.html
という記事がありまして、非常に間単にPlaggerがインストールできると評判なのですが、Fedora Core 3では無理みたいです。
というのもリンク先で参照しているファイルがなくなっているので、URL削っていったら

http://pub.woremacx.com/worepo/
にFC6のリンクが…
それでも無理やり

$ wget http://pub.woremacx.com/worepo/worepo-release-6.rpm
$ rpm -ivh worepo-release-6.rpm

とかやったら、yumするときにhttp://pub.woremacx.com/worepo/yumのサイトリストにしてしまい、(しかも「サイトになにもないよー」という感じのエラーメッセージがでて)yumでなにもできなくなりました。
CPANインストールのときにもミラーサイト(http: -- that host a CPAN mirror.の後に番号で示すやつ)が一切リストアップされなくて原因がわからなかったのですが、これっぽいです。
もしくはCPANのurllistのせい。

/etc/yum.repos.d
にwo.repoファイルがあって、おそらくそれを参照していることはわかりました。
直し方はrpmファイルをwgetしてきて、

$ rpm -Uvh worepo-release-6.rpm

とすればよいです。(http://経由でもいいのかな?)

次のcpanインストールとか並行してやっていたので、問題が絡み合って原因がわけわかんなくなるところでした…危なかった。

自席マシンにPlaggerを入れる、の前にcpanを使えるようにする。

CPANPlaggerがインストールできると簡単なので、cpan(cpan専用のyumみたいなもの)をインストールします。

$ perl -MCPAN -e 'CPAN::Shell->install(CPAN::Shell->r)'

でいけるみたいですが、当然のごとくエラーでます。

bat 'urllist'といわれているので、ググる

http://www.drk7.jp/MT/archives/000885.html
によさげな記事がありました。
CPANyumと同じくサイトリストがあって、それを参照するそうです。

cpan> o conf

とすると、CPAN環境変数が一覧ででるので確認すると、プロキシとurlリストがおかしいです。
yumと違ってproxy_user、proxy_passがあるので、ユーザIDとパスワードはこちらに書いておくようです。

cpan
cpan> o conf http_proxy http://プロキシ名:ポート番号
cpan> o conf ftp_proxy http://プロキシ名:ポート番号
cpan> o conf proxy_user ユーザ名
cpan> o conf proxy_pass パスワード
cpan> o conf urllist push http://どっか/
cpan> o conf commit

とすればよいようです。urllistは3つくらい登録しておきました。
最後にコミットしないと反映されないので気をつけます。

$ perl -MCPAN -e 'CPAN::Shell->install(CPAN::Shell->r)'

とやりましたが最初はurllistが悪かった(設定してなかった)ので、
エラーしまくりました。

何回かトライして*1みてインストールできたので、

cpan> install Plagger

しましたがエラー。
依存パッケージがあるみたいです…
何回やっても駄目です。

ということで、

cpan> force install Plagger

フォースインストールしてしまいました。まどーせperl使ってないですし。
地道に解決しようとしないところがシステム運用には向いてないと思います。

そしたら、

$ plagger

してみます。plagger特有のエラーがでたらOK。

Plaggerの設定

Plaggerの三大罠を解決しておきます。
1. のEncodeモジュールはやらなくても大丈夫だったけど
assetと改行はやらないと動かないので気をつけましょう。

http://subtech.g.hatena.ne.jp/otsune/20060828/PlaggerTrap

そしてassetsの罠を回避するためにhttp://search.cpan.org/~miyagawa/Plagger-0.7.17/lib/Plagger.pm*2からファイルを落としてきます。LinuxFTPして(Linuxで直接wgetしてもいいけど)

$ tar xvzf Plagger-0.7.9.tar.gz

で解凍したらassetsフォルダをコピーします。(とりあえずここでは/usr/etc/plagger/assetsにコピーしました)

で、yamlを書くと。yamlのデフォルトの場所は/usr/bin/config.yamlです。

global:
  assets_path: /usr/etc/plagger/assets
  timezone: Asia/Tokyo
  log:
    lovel: info

plugins:
  - module: Subscription::Config
    config:
      feed:
        - url: http://SVNのホスト名/適当なパス/commit.rdf

  - module: Filter::Rule
    rule: 
      module: Deduped

  - module: Filter::EntryFullText
    rule:
      module: Deduped

  - module: Publish::Gmail
    config:
      mailto: 送り先メールアドレス
      mailfrom: mail@example.co.jp
      mailroute:
        via: smtp
        host: SMTPサーバの名前

最後に改行を忘れずに。
なお、Filter::Rule, Filter::EntryFullTextは、同じエントリを取得しないためのフィルタです。
http://emasaka.blog65.fc2.com/blog-entry-156.html

plaggerはデーモンではないので、かならず重複エントリを取ってきてしまいますから、メール通知する場合は、これをはさまないと使いづらいです。

SubversionサーバにXML::RSSをインストールする

SubversionサーバにXML::RSSを入れます。
外にアクセスできないので、手動でCPANからとってきます。割りと面倒でした。

ここを参考にしてインストールしました。
http://www.nurs.or.jp/~sug/homep/rss/rss2.htm
上記サイトだとXML::RSSXML**Parserだけで良いと書いてありますが、実際は10コくらい入れました。

perl -MXML::RSS -e ' print $XML::RSS::Version'

とかやるとエラーメッセージがでるので、足りないモジュールをインストールします。
CPANモジュールのインストールは簡単ですね。

Subversionサーバにcommit-rss.plを設置する。

コミットされたメッセージをRDF(RSS)に変換するためにトラックバック先のスクリプトを設置します。
apacheユーザが入れない場所においちゃうと駄目なので注意です。


/path/to/svn/hooks/post-commitに、以下のように追記します。

REPOS="$1"
REV="$2"
# feed rss
export LANG=ja_JP.utf8
export LC_ALL=ja_JP.utf8
export LANGUAGE=ja_JP.utf8
PERL_BADLANG=0; export PERL_BADLANG

touch /tmp/log.txt
(
pushd .
cd トラックバック先のスクリプトが置いてある場所
perl commit-rss.pl \
 $REPOS \
 $REV \
 "Commit RSS Feed" \
 http://path/to/repository/ \
 20 \
  RSSを出力したいパス(apacheで動かしているなら外部公開できる場所)
popd
) > /tmp/log.txt 2>&1

$REPOSと$REVはpost-commitに与えられる第一引数と第二引数です。
最初エラーがでるので、エラーメッセージをどっかに保存しとくのが良いです。ここでは/tmp/log.txtに保存してます。
あと、export文は試行錯誤の名残です。なくても動くかもしれません。環境によって変わるので色々いじるのがよいかと。
さらに、touchはしなくてよいかと思います。

で、肝心のスクリプトですが、そのままでは動きませんでした。

書き換え箇所は以下の2点です。
1点目:SVNサーバのデフォルトエンコードを変更(元スクリプトWindows前提なので)

< my $host_encode = 'sjis' ;
> my $host_encode = 'euc' ;

2点目:エンコーディング箇所を変更
ちょっと長いので変更後のみ示します。

# log はエンコードされている(LANGに依存)
# my $logmessage = '?\229?\191?\152?\227?\130?\140?\227?\129?\166?\227?\129?\132?\227?\129?\159?\227?\128?\130' ;
$logmessage =~ s/\?\\([0-9]..)/pack('C', $1)/eg ; # "?\000" のエンコードを 戻す
  utf8::decode($logmessage);
  print utf8::is_utf8($logmessage) ? 'utf' : 'not utf';
# unix
# Jcode::convert(\$logmessage, $encode, $host_encode ) if ($encode ne $host_encode); # 目的の文字コードへ変換
 
# windows ? 内部で特殊な変換がかかっているぽい
# Jcode::convert(\$logmessage, $encode,  'utf8' ) if ($encode ne 'utf8'); # 目的の文字コードへ変換

おそらくperlのバージョンが関係していますが、Perl5.8以降はutf8に対して特別な処理が掛かっているようです。

このへんhttp://www.rwds.net/kuroita/program/Perl_unicode.htmlとかこのへんhttp://www.hikoboshi.org/perl/doc/encode.htmlを参考にさせていただきました。

色々試していたところ、use utf8プラグマなどつけるとログメッセージをスクリプト上に固定リテラルで書いたときはうまくいくのですが、引数として与えられたのを処理するにはutf8::decode()して「utf8フラグ」をつけなければならないようです。
(decodeでフラグが付くのは妙な感じですが)

で、Jcodeでのエンコードはしませんでした。
昔はjcode.plを使ってたような気がしますが時代が変わったんですね。
というかperlは言語処理用の言語のくせしてマルチバイト関連でわかりづらいところが多すぎです。これの解明に時間が掛かりまくりました。

なお、調査される際は、コミットメッセージを「あ」だけにしておくとよいと思います。
utf-8では日本語文字は3バイトになるので、どうなっているかわかりやすいです。

さらにOSの環境変数との絡みでもエラーがでまして、失敗すると先ほどの/tmp/log.txtに

svnlook: error: cannot set LC_ALL locale
svnlook: error: environment variable LANG is ja_JP.sjis
svnlook: error: please check that your locale name is correct

などでます。
post-commitのほうでいろいろexportしてみてください。


ということで後は運用するだけです。
1週間くらいはベータテストしてみようかと思います。

*1:M/MI/MIYAGAWA/Plagger-0.7.17.tar.gzが壊れているからremoveするのをお勧めするよとかいうエラー

*2:http://search.cpan.org/~miyagawa/Plagger-0.7.9/lib/Plagger.pmと書いていましたが、誤りです。otsuneさんご指摘ありがとうございます。