Subversionのログをcronでポーリングしゆる～くコミット検知

どこもかしこもGitからの通知を受け取っていて悔しいのでSubversionでもやってやろうじゃないかと思いましたが、フックスクリプトを仕込めなくて断念しました。
しかしJenkinsでポーリングという方法を知り、ログで実装しました。
[]:ここまでが要約です
耳慣れないポーリングということばはちょっとむずかしいですが、ここではWikipediaから
>ポーリング（polling）とは、通信やソフトウェアにおいて、競合を回避したり、送受信の準備状況を判断したり、処理を同期したりするために、複数の機器やプログラムに対して順番に定期的に問い合わせを行い、一定の条件を満たした場合に送受信や処理を行う通信及び処理方式のことである。
**クライアントが単一であっても、能動的な送信能力を持たない場合**
    この場合クライアントは、自分からホストへ処理の経過や完了を知らせることができないので、ホストの側が定期的にクライアントに処理が完了したかどうかの状況を問い合わせる必要がある。この完了待ちの状態をポーリングと呼ぶ。

という説明から「定期的に確認しにいくこと」と捉えています。

Jenkinsのポーリングの手法がわからなかったので、SVNのログの差分を通知条件＆メッセージにしました。

# <i class="fa fa-file-text-o"></i> 要件

* Subversionにフックスクリプトが仕込めない
* チェックアウトはしない
* コミットメッセージが確認できる(コミットファイル・ディレクトリは詳しく分からない)
* 新規に追加されたリポジトリ・ブランチには(より上位のリポジトリを指定しないと)対応しない
* コミットを取り逃しても泣かない

# <i class="fa fa-cubes"></i> 概要
引数にリポジトリURL と投稿チャンネルを持つShellScriptを作成する。
ShellScriptはリポジトリURLからログを取得し、古いログと比較する。
新しい記述があった場合のみ、Slackへ通知する。

このスクリプトをcronで定期的に実行することでコミットを擬似的に検出する。


# <i class="fa fa-desktop"></i> 作成環境

bashとsvnがあれば大丈夫と思いますが、作ったときの環境です。

Windows7 → Vagrant ubuntu/trusty64 → Docker ubuntu/trusty

VagrantとDockerの導入テストをしていたので変な感じです。

VagrantのubuntuにはDockerのみapt-get
Dockerのubuntuにはsvnとlanguage-pack-ja、curlをapt-getし、cronで日本語を使えるように設定しています。

# <i class="fa fa-wrench"></i> 事前準備

今回はスクリプトに認証設定を持たせていないので、事前に手動で
`svn log [URL] --limit 1`
を実行して認証を保存します。
最初はroot(ログインユーザー)で認証しようとするので、失敗した後は正しいユーザーとパスワードを入力し、保存するか訊かれるので保存します。

# <i class="fa fa-code"></i> スクリプト

```bash:poll-svn-for-notifying-to-slack.sh
#!/bin/bash
# 変数の置換が#!/bin/sh ではできないのでbashで実行

# 命名的代入 リポジトリはURLを チャンネル名には#をつけない
repository=$1
# 引数の場合はエスケープが必要になるのでここでつける
channel="#"$2
# logの件数はデフォルト10の第3引数で変動
limit=${3:-10}

# logファイルが重複しないようにリポジトリ名のハッシュ値をsuffixに
suffix=`echo -n "$repository" | md5sum`
# そのままのハッシュでは余計なものがついてエラーになるので、英数字のみにするため末尾を取り除く
suffix=${suffix%'  -'}
echo $suffix

svn log "$repository" --limit $limit > /tmp/shared/new-"$suffix".log

if [ ! -f /tmp/shared/old-"$suffix".log ]; then
	cat /tmp/shared/new-"$suffix".log > /tmp/shared/old-"$suffix".log
	echo '比較ファイル無し'
	exit
fi

#diff=`diff -u /tmp/shared/old-${suffix}.log /tmp/shared/new-${suffix}.log`
diff=`diff --new-line-format='%L' --old-line-format='' --unchanged-line-format='' /tmp/shared/old-${suffix}.log /tmp/shared/new-${suffix}.log`

if [ -z "$diff" ]; then
	echo '変更無し'
	# 変更無しならログも更新しない
	exit
fi


# 改行とタブがあるとJSONエラー ただし改行は\nを入れるとslack側で行ってくれる
# 変数の置換はshでは動作しない　http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1227556757
# bashで改行などの特殊記号を使う場合は$''でくくらなければならない　http://oshiete.goo.ne.jp/qa/6733547.html
# 文字列 \n は''で囲まないとnにされる
diff="${diff//$'\n'/'\n'}"
diff="${diff//$'\t'/    }"
# どこのリポジトリのログかを表示する
diff="$repository"'\n'"$diff"

# slackに通知
curl -X POST --data-urlencode 'payload={"channel": "'$channel'", "username": "subversion", "text": "'"$diff"'", "icon_emoji": ":octocat:"}' https://hooks.slack.com/services/「slackトークン」

# 旧ログを更新
cat /tmp/shared/new-"$suffix".log > /tmp/shared/old-"$suffix".log

```

解説を入れようと思いましたが、未来のおバカさんに向けた無駄に多いコメントメッセージを読んでいただければ大体わかるとおもいます。
コメント箇所≒躓いた箇所です。 :joy: 

ちょっとした補足だと、ubuntuの`sh`は`dash`だとか、
[JSONLint](http://jsonlint.com/)にpayloadの出力をコピペでチェックしても、タブによるエラーが検出できないだとか。

```shell-session
root@a01f3dcd16fe:/# which sh
/bin/sh
root@a01f3dcd16fe:/# ll /bin/sh
lrwxrwxrwx 1 root root 4 Feb 19  2014 /bin/sh -> dash*
```

改善するならハッシュをもっと長くしたり、アイコンも引数化したりとか…
スクリプトとは無関係ですが、svnの認証が平文で保管されていたりとかですね。

追記
`curl`が古くて`--data-urlencode`が無い場合は`-d`でも代用できそうです。

## 出力サンプル

![svn_sample.JPG](https://qiita-image-store.s3.amazonaws.com/0/62354/a09838da-6bfb-2443-6535-2c2798928b5d.jpeg)
怒りのオクトキャットくん

Slackにアイコンを登録すれば任意のアイコンで表示できます。
今は`:svn:`にアイコンを登録してSアイコンで通知しています。

# <i class="fa fa-frown-o"></i> 悪い所

* 修正ファイルが見えない  
恐らく出せるが、ブランチの作成とかで死にそうなのでやってない。
* 新規ディレクトリに対応できない  
例えば、新しいブランチを作ってもそのブランチをcronに登録しないとコミットが見えません。  
プロジェクトやbranches全体を対象にすればコミットを拾えますが、修正ファイルが見えないため、何所へのコミットかがわからなくなります。
* Limit以上のコミットを取り逃す  
今の所は10分間隔Limit10で問題はないですが、人が増えたり炎上したら…  
あと、作業PC上のVMで動かしているので、同僚が働いているのを尻目に帰ると翌日には10件以上コミットされて取りこぼすことが多々あります。  
ここは差分にヘッダがLimit数あればLimit増やしてログを再取得…とかできそうだと思っています(やるとはいってない)

スクリプトと無関係なもの

* cronとCLIでの挙動が違う  
日本語設定がうまく行ってないのか、cronと手動のログの差分は変更がなくても全ヘッダ行になります…
* ubuntuのcronが起動できない  
service cron などのスクリプトがすっからかんです。

```shell-session
root@a01f3dcd16fe:/# service cron start
Rather than invoking init scripts through /etc/init.d, use the service(8)
utility, e.g. service cron start

Since the script you are attempting to invoke has been converted to an
Upstart job, you may also use the start(8) utility, e.g. start cron
root@a01f3dcd16fe:/# which start
/sbin/start
root@a01f3dcd16fe:/# ll /sbin/start
lrwxrwxrwx 1 root root 7 Jul 18  2014 /sbin/start -> initctl*
root@a01f3dcd16fe:/# cat /sbin/initctl
#!/bin/sh
exit 0

```

調べていませんが、[こちら](https://registry.hub.docker.com/_/ubuntu-upstart/)を使う必要があったのかも。
デーモンは無理ですが、`cron`をそのまま打つと定期実行されるようになったので、一旦保留にしています。

***

同じ環境の方は少ないとは思いますが、リアルタイムではないにしろ同僚がどんなを作業しているのかが自動でながれてくると、ちょっと孤独感が減ったり相手の進捗がわかったりしてよかよかです。