Subversion のコミットログを cron で Slack に流す


今や、Git 大流行ですね。
「Git でなければ、人じゃなし」とかいわれる時代ですが(嘘)、弊社のソースコード管理は、相変わらず Subversion です。

でも、trunk にしかコミットされていませんし、直線なコミットで、かつコミット単位もなんとなく作業が終わったらコミットするぐらいのなんちゃってソースコード管理です(単に変更ログが取れて、人が更新されていたのを間違えて上書きしない検知ができるファイル置き場ぐらいにしかなっていません)
レポジトリも1個しかなく、そのサブフォルダーをプロジェクト毎に割りあてています。

しかし、「プロジェクトでこれを使う」と言われれば使わざるを得ません。しかし、人が Subversion 上で更新されたのが分からないと「おおっとコンフリクト!(Wizadry 1をやったことがある人は分かる)」ということになりかねません。

そこで、流行の Slack へ Update を通知することにしてみました。

しかし、問題山積です。

  1. Subersion サーバーは、VisualSVN という Windows OS 上で動いている。
  2. 私には、その Windows OS の操作権限がない。(hooks/post-commit とか置けない)
  3. レポジトリが1つしかない。(hooks/post-commit を置いても自分と無関係のプロジェクトでも動いちゃう)

という制約があります。

しかし、私は、Ubuntu OS マシンとその操作権限を持っています。その権限内でどうにかしようと考えて以下の方法を考えました。

  1. Ubuntu OS 上にプロジェクトのレポジトリ以下をチェックアウトする。
  2. cron で bash を1分間に1回動かす。
  3. svn update;svn log を そのbashで動かす。
  4. svn log の内容を curl で Slack の WebHook に送る。

どうということもないのですが、格好つけて図にしてみました。

バッチリですね(^^ゞ。

Qiita で似たようなことをやっている人がいないか探してみました。

svnコミット時にSlackに投稿する - Qiita
http://qiita.com/holly_nano/items/cc3289f74e8b766798dd
 ----> hooks/post-commit を使う方式でした。残念使えません。

rubyスクリプトでsvnコミット通知をslackに送る - Qiita
http://qiita.com/hollydad/items/a2b0a4af01e7b18a534d
 ---> こちらも hooks/post-commit でした。

Subversionのコミット通知をbashとcurlを使ってSlackのチャンネルに流す - Qiita
http://qiita.com/TakezawaHirokazu/items/2b5c596929e36a971127
 ---> こちらも hooks/post-commit でしたが、curl と bash というところを
   参考にさせていただきました。

Subversionのログをcronでポーリングしゆる~くコミット検知 - Qiita
http://qiita.com/khsk/items/f559c798a8511cf118f4
 ---> なかなかぽいですね。

SSHログインをSlackに通知する。 - Qiita
http://qiita.com/FoxBoxsnet/items/a496e4bf158177e263fc
 ---> シェルスクリプトを参照させていただきました。

それでは順にやってみましょう。

1. svn チェックアウトする

まず、該当のディレクトリをチェックアウトします。

bash
$ mkdir -p /opt/svn/
$ cd /opt/svn/
$ svn checkout http://example.jp/hogehoge hogehoge

/opt/src/hogehoge
に SVN の hogehoge がチェックアウトされました。

2. Webhook を設定する

Webhook は、
http://qiita.com/TakezawaHirokazu/items/2b5c596929e36a971127
が詳しいのでそちらを参照してください。今回は、svn_update というチャンネルを作りました。

Webhook で出力されるURLを保存しておいてください。次のbashスクリプトで使います。
https://hooks.slack.com/services/XXXXXXXXX

3. bash スクリプトを設置する

bashスクリプトを設置する場所を用意して、そこにbashスクリプトを置きます。

bash
$ mkdir ~/bin

以下のようにbashスクリプトを作成します。
webhookurl="https://hooks.slack.com/services/XXXXXXXXX"
svnuser=user
svnpassword=password
の部分は環境に合わせて直してください。

/home/user/bin/svn_update_post_slack.sh
#!/bin/bash

webhookurl="https://hooks.slack.com/services/XXXXXXXXX"
svnuser=user
svnpassword=password

pushd /opt/svn/hogehoge
SVN_UPDATE_LOG=$(svn update . --username $svnuser --password $svnpassword --non-interactive 2>&1 )
popd

echo $SVN_UPDATE_LOG | grep 'に更新しました。'
RET=$?

#ログに'に更新しました。'があった場合は以下を実行
if [ "$RET" = "0" ]
then
  # 前回保存したチェックアウトのリビジョンを取り出して1を足す
  prev_revision=$(( $(cat /tmp/svn_old-revision.txt) + 1))
  # 今回取得したリビジョンを抜き出す
  old_revision=$(echo $SVN_UPDATE_LOG | sed -n -e 's/.*リビジョン \(.*\) に更新しました。/\1/p')

  pushd /opt/svn/hogehoge
  # 前回取得したチェックアウトの一つ後以降のログを取得して格納
  SVN_LOG=$(svn log --verbose --revision ${prev_revision}:HEAD --username $svnuser --password $svnpassword --non-interactive)
  popd

  payload="payload={
  \"channel\": \"#svn_update\",
  \"username\": \"SVN update log\",
  \"text\": \"${SVN_LOG}\",
  \"icon_emoji\": \":ghost:\"
}"

  curl -m 5 --data-urlencode "${payload}" "${webhookurl}" > /dev/null 2>&1

  echo $old_revision > /tmp/svn_old-revision.txt
fi

svn log--verbose を付けるのが今回のミソですね。

4. cron を設置する

/etc/cron.d/svn_update_post_slack
* * * * *  root  /home/user/bin/svn_update_post_slack.sh  2> /dev/null 1> /dev/null

ログを /dev/null に入れると「藤原さんに椅子を投げられる」のでご利用は計画的に。

5. 結果確認

Slackの #svn_update チャンネルには以下のようなメッセージが送られてきます。

上の図で、rで始まる文字列がリビジョン番号で、コミットした人、コミット日付と続きます。
次に変更、追加、削除のあったファイルのパス
その下の黄色の線を引いてあるところが、ログメッセージ
になります。

これで、Subversionのログをcronでポーリングしゆる~くコミット検知 の人が言っているとおり、他人がどういうコミットをしたかが分かります。これは結構重要なことなのですが、社内ではあまり理解されていないような気がします。
別に、アトミックにコミットしろとまではいわないので、ログメッセージぐらいちゃんと書いてくれるといいのですが。

また、自分がコミットした時のログが流れてくるので、「ファイルを保存だけしたけどコミットし忘れた」ということも防げます。