KDEでCI

タグ: KDE   テスト志向   Linux  

記事は消えてしまいましたが、過去にJenkinsの紹介をしました。NetBeansと組み合わせて使用する方法も紹介しました。

その時のワークフローは、ファイル更新のたびにテストされたのでは、邪魔ですので、Gitでcommitした場合に、テストが実行されるようにしていました。プロジェクトの後半では、それが良いと思います。

しかし、テストをバリバリ書いている途中ですと、ファイル保存のたびに行なってくれる方が便利です。NetBeansのプラグインを探しましたが、該当するものがPHP関係には存在していませんでした。(よく探せば、きっとあると思います。)

Eclipsにはあります。しかし、IDEでのテストは派手にやられます。場合によってはテスト結果に興味がない時があり、あまり目に付くように行なってほしくありません。

そこで、自分好みの環境を作れるのがLinuxディスクトップの魅力です。いろいろ試してみました。

目的とするのは、特定のディレクトリー下のPHPファイル変更(再帰的にチェック)を見つけたら、裏でこそっとphpunitを実行し、その結果を控えめに通知してくれる機能を設定します。

動作環境はLinuxのKDEを対象にしています。しかし、KDEに依存する部分は多くありませんので、Gnomeなどでも実現できます。

ファイル監視

なにはともあれ、ファイルが変更されたことを監視しなくてはなりません。当初はKDE関係の設定やツールを探しましたが、めぼしいものがありませんでした。この手のプログラムはrubyとかpythonとかによく存在しているようです。そうした言語を使用している方なら、存在しているツールを使用するのもよいでしょう。(Laravelのテスト本ではrubyのツールを使用していました。)

記憶を頼りに検索し、inotify-toolsを使用することにしました。これはLinux(Unixにはありません)のファイル変更通知機能のinotifyをコマンドラインツールで実行できるようにしたものです。

デフォルトでは、通常インストールされていません。パッケージをインストールする必要がありますが、大抵のディストリビューションには用意されています。以下のどれかで、インストールできるでしょう。

# RedHat系
yum install inotify-tools
# Debian系
apt-get install inotify-tools
# openSUSE
zypper install inotify-tools

2つコマンドが使用できるようになりますが、実際に使用するのはinotifywaitです。

これで、シェルスクリプトを作成します。例えば、以下のようにです。

#!/bin/bash

# watchdog 対象ディレクトリー

if [ $# -ne 1 ]
then
  echo 監視対象ディレクトリーを指定してください。最後のスラッシュは付けないでください。
  exit 1
fi

if [ ! -d $1 ]
then
  echo 監視対象ディレクトリーが存在しません
  exit 1
fi

while :
do
  inotifywait -r -e close_write,move,create,delete \
  --exclude $1/testresult.txt $1 \
 | awk -v DIR=$1 -e '
$3 ~ /\w+.\.php$/{
    print "cd " DIR "; vendor/bin/phpunit >& " DIR "/testresult.txt ;"
}' | sh
done

使用方法は監視するディレクトリーを指定します。このスクリプトをwatchdogという名前にした場合、以下のようになります。

watchdog /home/your-home/target-directory

ディレクトリー名の最後にスラッシュを付けないでください。そこは手を抜いています。:D

スクリプトを軽く解説します。

inotifywaitは、-eオプションでどの様なファイル操作を監視対象にするか決めています。この場合、変更ありの更新、移動、削除です。--excludeでphpunitの結果を保存するファイルを管理対象外にしています。結果保存ファイルを監視対象ディレクトリー外に作成すれば、これは必要ありません。(今回のコードなら、多分無くても動作します) 最後に引数で対象ディレクトリーを指定しています。-rオプションで再帰的に監視することを指定しています。

このコマンドは、監視対象で指定された変化が起きるまで、じっと待っています。そして、変化が起きるとディレクトリー、イベント、ファイル名を空白で区切り、表示してくれます。

今回はawkを使用し、ファイル名の拡張子が"php"の場合のみ、phpunitを実行し、その表示を結果保存ファイルにリダイレクトしています。私がawk使いなので、awkを使っているだけで、判定は別の手段でも可能です。

管理ディレクトリー下でディレクトリーが新しく作成された時も、テストが実行されてしまいます。実害はないのでよしとしましたが、気になる方は2番目の項目をチェックし、CREATE,ISDIRの場合は実行しないようにしましょう。(ディレクトリー削除の場合は、テストを実行する必要はあります。なぜなら、関連するPHPファイルが入っていた可能性があります。つまり、テスト結果に影響が起きる可能性があるからです。)

これで監視対象ディレクトリー下のPHPファイルが変更されると、phpunitが実行され、結果がファイルに保存されるようになりました。

結果通知

当初は、結果の通知をKDEのウィジット経由で表示しようと思っていました。デフォルトで含まれている「ファイルウォッチャー」ウィジットは、ファイルの変化を感知するタイミングが速すぎ、書き出しているテスト結果の途中が表示されます。もちろん確認したいのは、最後の一行が"OK"で始まっているかです。途中が表示されても役に立ちません。

次に、"tail"というウィジットをダウンロードしたのですが、こちらはファイル変化の感知が上手く行われなく、役に立ちませんでした。

そこで、多少目立ちますが、結果が通知エリアに残るため、kdialogを使用することにしました。これは、シェルスクリプトから簡単にダイアログなどを表示することのできるコマンドで、KDE専用となっています。

今度は、テスト出力ファイルを単体で監視すれば良いわけです。次のようなスクリプトにしました。

#!/bin/bash

# testresultnotifer 対象ディレクトリー 表示名

if [ $# -ne 2 ]
then
  echo 監視対象ディレクトリーと表示名を指定してください。最後のスラッシュは付けないでください。
  exit 1
fi

if [ ! -d $1 ]
then
  echo 監視対象ディレクトリーが存在しません
  exit 1
fi


while :
do
  inotifywait -e close_write,create $1/testresult.txt
  if (tail -n 3 $1/testresult.txt | grep 'OK' )
  then
    kdialog --title "単体テスト実行結果" --passivepopup "$2 成功" 1
  else
    kdialog --error "$2 失敗!!" 1
  fi
done

このファイルをnotifierとして保存したら、使い方は以下の通りです。

notifier /home/your-home/target-directory MyProjectName

もし、標示されるメッセージが邪魔なら、出力をリダイレクトしてください。

notifier /home/your-home/target-directory MyProjectName >& /dev/null

更に、バックグラウンドで実行することもできます。

notifier /home/your-home/target-directory MyProjectName >& /dev/null &

最初の引数は監視ディレクトリー名です。watchdogで指定するのと同じディレクトリー名です。最後のスラッシュは付けないでください。2つ目の引数は表示するプロジェクト名です。複数のプロジェクトで利用する場合、見分けるために利用されます。表示名ですね。

こちらも、簡単に解説します。

監視するイベントを絞っています。ファイル一つの監視ですので"-r"オプションは必要ありません。

結果ファイルの最後をgrepで検査します。grepは見つかった場合は0、見つからない場合は1を返します。これで結果がOKであるかをテストしています。

kdialogのpassivepopupは自然と時間が経てば消える通知ですが、その内容はKDEの通知領域へ残ります。ですから、ポップアップを見落としても、後からチェックすることが可能です。

kdialogのエラーはOKを押さないと消えません。いささか煩雑に思えるのでしたら、OK時と同様にpassivepopupを使用しましょう。(私はpassivepopupを使っています。)

逆に失敗時は必ずその内容をチェックしないと気が済まない方は、結果をエディターで開くように一行付け加えましょう。

この部分はKDE依存ですが、Gnomeならばzenityも存在しますので、適当なコマンドで置き換えてください。

2つ一緒に

どうせ一緒に使用するものですので、お手軽に起動できるようにしましょう。例えば、ciというスクリプトを作ります。

#!/bin/bash

watchdog /home/your-home/target-directory >& /dev/null &
notifier /home/your-home/target-directory MyProjectName >& /dev/null &

自動化

openSUSEは13.1になりました。

以前のバージョンでは、KDEのシステム設定で起動させると、所有者がrootになってしまい、個人ユーザーへの通知ができませんでした。今回のバージョンでは上手く動くようです。

KDEのシステム設定から、起動と終了、自動起動を選びます。続いてスクリプトを追加で希望するスクリプトを選んで下さい。実行のタイミングはデフォルトの「起動時」のままです。「起動時」はKDEのAutostartディレクトリーに登録され、スクリプトが終了しないデーモンタイプのものでもKDEに影響しません。起動タイミングを「KDEの起動前」に設定するとenvディレクトリーに登録され、終了しないスクリプトでは、KDEが立ち上がらなくなります。

.bash_profileでも試してみましたが、何度か起動されるらしく、3つ同じ管理プロセスができていました。もちろん、OSやディスクトップ環境により、異なるでしょう。

多重に起動した場合

多重に起動してしまうと、上手く動作しません。

確認方法は例えばwatchdogでしたら、以下のようにします。

ps -aux | grep watchdog

表示される一番右が、起動したコマンドです。そこを見て、起動しているwatchdogを一度全部killしてしまいましょう。

kill -9 pid, pid...

pidは、ps…で表示された、一番左の整数です。

一度に削除するには、以下のようにします。

ps -aux | grep watchdoc | awk -e '$11~/bin\/bash/ {print $2}' | xargs kill -9

次にnotifierに対しても、同じ事をする必要があります。

ps -aux | grep notifier | awk -e '$11~/bin\/bash/ {print $2}' | xargs kill -9

その後、watchdogとnotifierにより起動されているinotifywatiをkillしましょう。

ps -aux | grep inotifywait | awk -e '$11~/inotifywait/ {print $2}' | xargs kill -9

お手軽なCIをどうぞ。