sgykfjsm.github.com

jenkinsでのテスト自動化いろいろ

jenkins上でテストを実行したい、自動化したいというリクエストが来たので、その設定方法をまとめた。

今回はplay2.2.1のscalaをサンプルプロジェクトととした。

環境の基本情報

今回はこんな設定で試した。setup.shは結構長いけど、簡単に言うとjdkを入れて、jenkins、apache2とscala2.10のインストール、apache2がjenkinsのフロントエンドとなるように設定しているだけ。

Vagrant / vagrantでubuntuを入れて、jenkins,apapcheをセットアップ。link
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# -*- mode: ruby -*-
# vi: set ft=ruby :

# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "precise"
  config.vm.box_url = "/Users/sgyk/local/vagrant/box/precise-server-cloudimg-amd64-vagrant-disk1.box"
  config.cache.auto_detect = true
  config.vm.define :presice do |presice|
    presice.vm.hostname = "precise"
    presice.vm.network :private_network, ip: "192.168.56.100"
    presice.vm.provider "virtualbox" do |v|
      v.customize ["modifyvm", :id, "--memory", 2048]
      v.name = "jenkins_presice"
    end
    presice.vm.synced_folder "data1", "/vagrant_data"
    presice.vm.provision :shell, :path => "./setup.sh"
  end
end
setup.sh / vagrantでubuntuを入れて、jenkins,apapcheをセットアップ。link
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#!/bin/bash

# ssh setting
su - vagrant -c '
  mkdir -p ${HOME}/.ssh
  ## for more secure
  # ssh-keygen -t rsa -q -P "" -f ~/.ssh/id_dsa

  ## sandbox setting
  wget -q -O ${HOME}/.ssh/id_dsa https://raw.github.com/mitchellh/vagrant/master/keys/vagrant
  wget -q -O ${HOME}/.ssh/id_dsa.pub https://raw.github.com/mitchellh/vagrant/master/keys/vagrant.pub

  cat $HOME/.ssh/id_dsa.pub >> $HOME/.ssh/authorized_keys

  chmod 700 ${HOME}/.ssh
  chmod 600 ${HOME}/.ssh/id_dsa*
'
# ssh speed up
echo UseDNS no >> /etc/ssh/sshd_config

cd /tmp
wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -
sudo sh -c 'echo deb http://pkg.jenkins-ci.org/debian binary/ > /etc/apt/sources.list.d/jenkins.list'
sudo apt-get -fy update
sudo apt-get -y install git openjdk-7-jdk apache2 jenkins zip unzip

# Setting up an Apache Proxy for port 80 -> 8080
sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2dissite default
sudo cat <<EOF > /etc/apache2/sites-available/jenkins
<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    ServerName ci.presice.com
    ServerAlias ci
    ProxyRequests Off
    <Proxy *>
        Order deny,allow
        Allow from all
    </Proxy>
    ProxyPreserveHost on
    ProxyPass / http://localhost:8080/
</VirtualHost>
EOF

sudo a2ensite jenkins
sudo apache2ctl restart

# scala
DOWNLOAD_URL=http://www.scala-lang.org/files/archive/scala-2.10.3.tgz
SCALA_TGZ=`basename ${DOWNLOAD_URL}`
SCALA_DIR=`basename ${SCALA_TGZ} .tgz`
wget -q -O /tmp/${SCALA_TGZ} ${DOWNLOAD_URL}
sudo tar zxf /tmp/${SCALA_TGZ} -C /usr/local/
sudo ln -s /usr/local/${SCALA_DIR} /usr/local/scala

# setting environment
SETTING_FILE=/etc/profile.d/append_environment_values.sh
echo '#!/bin/bash' | sudo tee -a ${SETTING_FILE}
echo '# added by vagrant setup.sh' | sudo tee -a ${SETTING_FILE}
echo | sudo tee -a ${SETTING_FILE}
echo 'export JAVA_HOME=/usr/lib/jvm/java-1.7.0-openjdk-amd64' | sudo tee -a ${SETTING_FILE}
echo 'export SCALA_HOME=/usr/local/scala' | sudo tee -a ${SETTING_FILE}
echo 'export PATH=${SCALA_HOME}/bin:${JAVA_HOME}/bin:${PATH}' | sudo tee -a ${SETTING_FILE}

exit 0

サンプルプロジェクトのgit設定

サンプルプロジェクトはPlayframeworkなので、jenkinsがplayコマンドを使えるようにする。

vagrant@precise:~$ wget http://downloads.typesafe.com/play/2.2.1/play-2.2.1.zip
vagrant@precise:~$ unzip play-2.2.1.zip > /dev/null
vagrant@precise:~$ cd play-2.2.1/
vagrant@precise:~/play-2.2.1$ sudo chmod +x play
vagrant@precise:~/play-2.2.1$ sudo chmod -R a+w framework/
vagrant@precise:~/play-2.2.1$ sudo chmod -R a+w repository/
vagrant@precise:~/play-2.2.1$ cd framework/
vagrant@precise:~/play-2.2.1/framework$ find . -maxdepth 1 -type f | grep -v '.bat' | xargs sudo chmod a+x

サンプルプロジェクト用のgitディレクトリを用意する。

vagrant@precise:~$ mkdir git
vagrant@precise:~$ cd git
vagrant@precise:~/git$ mkdir play_sample.git
vagrant@precise:~/git$ cd play_sample.git/
vagrant@precise:~/git/play_sample.git$ git --bare init

このへんぐらいはまではsetup.shに盛り込んでよかったかも。

以下はサンプルプロジェクトの開発場所にて。

[sgyk@fujishima] 14-01-13 4:01:54 ~/local/script/scala/play_sample
$ git init
[sgyk@fujishima] 14-01-13 4:01:59 ~/local/script/scala/play_sample (git)-[master] {?}
$ git add .
[sgyk@fujishima] 14-01-13 4:02:04 ~/local/script/scala/play_sample (git)-[master] {?}
$ git commit -m "initial commit"
[master (root-commit) d3458d3] initial commit
[sgyk@fujishima] 14-01-13 4:11:23 ~/local/script/scala/play_sample (git)-[master] {?}
$ git remote add origin ssh://vagrant@presice/home/vagrant/git/play_sample.git
[sgyk@fujishima] 14-01-13 4:11:24 ~/local/script/scala/play_sample (git)-[master] {?}
$ git push origin master
Counting objects: 32, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (23/23), done.
Writing objects: 100% (32/32), 38.22 KiB, done.
Total 32 (delta 0), reused 0 (delta 0)
To ssh://vagrant@presice/home/vagrant/git/play_sample.git
 * [new branch]      master -> master

これでgitの準備は完了

ちなみに、通常のsshコマンドでvagrantのVMにアクセスする場合は以下のようにしてsshのconfigに追加しておくと楽。

vagrant ssh-config --host presice >> ~/.ssh/config

今回はsshでgitにpushするようにしたので、このように設定しておくとハマりにくくなる。と思う。

jenkinsの設定

最初にjenkinsからGitを扱えるようにプラグインをインストールする。左メニュー「Jenkinsの管理」から「プラグインの管理」にアクセスし、「利用可能」タブから「Jenkins GIT plugin」を選択し、「ダウンロードして再起動後にインストール」を押下する。その後、jenkinsを再起動する。

左メニュー「新規ジョブの作成」をクリック。
ジョブ名を入力、「フリースタイル・プロジェクトのビルド」を選択して、OKを押下する。「ソースコード管理」でGitを選択し、Repository URLを入力する。「適用」、「保存」の順番に押下する。

これでjenkinsへのリポジトリ登録が完了。

ビルドの設定を行う。リストボックスの「ビルド手順の追加」から、「シェルの実行」を選択し、以下の様なシェルを入力する。

#!/bin/bash
play=/home/vagrant/play-2.2.1/play
cd ${WORKSPACE}

${play} clean compile test

exit 0

jenkinsにはPlayframeworkのプラグインがあるのだけど、うまく実行できなかったので上記のようにシェル実行するようにした。

テスト結果は${PLAY_HOME}/target/test-reports/{ApplicationSpec,IntegrationSpec}.xmlに出力されているので、「ビルド後の処理」に「JUnitテスト結果の集計」を追加して、**/target/test-reports/*.xmlと入力すればOK。

この設定により、ビルド後、左メニューの「テスト結果」からテスト結果のレポートを見ることができる。

jenkins上で任意のタイミングでテストを実行する。

Jenkinsで外部パラメータで与えたブランチを対象にビルドできるようにしておくと凄惨性あがって墓ドルを参考に設定する。

設定画面にて、「ビルドのパラメータ化」にチェックを入れる。「パラメータの追加」というリストボックスで「文字列」を選択し、「名前」、「デフォルト値」、「説明」を適宜入力する。入力を終えたら、とりあえず「適用」をクリックする。

「ソースコード管理」の項目にある「Branches to build」で、「Branch Specifier」の欄を$branchに変更する。

「適用」、「保存」の順にクリックする。

GUIからビルドを実行したい場合は、左メニュー「パラメータ付きビルド」をクリックして、ビルドしたいブランチを設定して、ビルドをクリックする。
CLIから実行したい場合は、以下のようにcurlを投げると良い。

vagrant@precise:~/local/script$ cat jenkins_build.sh
#!/bin/bash

JENKINS_JOB_URL=http://192.168.56.100/job/play_sample/buildWithParameters?branch=origin/feature/add_plugin_scct

curl ${JENKINS_JOB_URL}

exit 0

jenkins上で定期的にテストを実行する。

これは簡単で、プロジェクトごとの設定画面から、ビルド・トリガに「定期的に実行」を選択して、cronっぽく設定すればOK。
ただし、これは固定的な設定なので任意のブランチを選択することはできない。

リポジトリにpushする度にjenkins上でテストを実行する。

リポジトリ側のhook/post-receiveに以下のようなスクリプトを用意すればOK。微妙にハマったのが、post-receiveに渡される引数は$1, $2のようなコマンドライン引数ではなくて標準入力だということ。で、その標準入力をどう捉えればいいのかが、よくわからなかった。

#!/bin/bash
while read oldrev newrev refname
do
    branch=$(git rev-parse --symbolic --abbrev-ref $refname)
    echo branch is ${branch}
    JENKINS_JOB_URL=http://192.168.56.100/job/play_sample/buildWithParameters?branch=origin/${branch}
    curl ${JENKINS_JOB_URL}
done

exit 0

参考:Writing a git post-receive hook to deal with a specific branchhttp://stackoverflow.com/a/13057643より。

おまけ

jenkinsからテストを実行しようとしたら、以下のようなエラーが発生した。

java.io.IOException: No such file or directory
    at java.io.UnixFileSystem.createFileExclusively(Native Method)
    at java.io.File.createNewFile(File.java:947)
    at xsbt.boot.Locks$.apply0(Locks.scala:35)
    at xsbt.boot.Locks$.apply(Locks.scala:28)
    at xsbt.boot.Launch.locked(Launch.scala:178)
    at xsbt.boot.Launch.app(Launch.scala:93)
    at xsbt.boot.Launch.app(Launch.scala:91)
    at xsbt.boot.Launch$.run(Launch.scala:51)
    at xsbt.boot.Launch$$anonfun$explicit$1.apply(Launch.scala:45)
    at xsbt.boot.Launch$.launch(Launch.scala:65)
    at xsbt.boot.Launch$.apply(Launch.scala:16)
    at xsbt.boot.Boot$.runImpl(Boot.scala:32)
    at xsbt.boot.Boot$.main(Boot.scala:21)
    at xsbt.boot.Boot.main(Boot.scala)
Error during sbt execution: java.io.IOException: No such file or directory

エラー発生箇所: https://github.com/harrah/xsbt/blob/0.13/launch/src/main/scala/xsbt/boot/Locks.scala#L35
解説はhttp://stackoverflow.com/a/8105883を参照。
要は、playコマンドがあるディレクトリにファイルとかをつくろうとしたけど、ダメでした、というエラー。なので、以下のように対応すればOK。

vagrant@precise:~/play-2.2.1$ sudo chmod a+x play
vagrant@precise:~/play-2.2.1$ sudo chmod -R a+w framework/
vagrant@precise:~/play-2.2.1$ sudo chmod -R a+w repository/
vagrant@precise:~/play-2.2.1$ cd framework/
vagrant@precise:~/play-2.2.1/framework$ find . -maxdepth 1 -type f | grep -v '.bat' | xargs sudo chmod a+x

また、以下のようなエラーが出る場合がある。

java.io.FileNotFoundException: /var/lib/jenkins/jobs/play_sample/workspace/target/resolution-cache/play_sample/play_sample_2.10/1.0-SNAPSHOT/resolved.xml.xml

workspaceを一旦クリアして再ビルドすればOK


以上で、任意 / 定期 / push後 のタイミングで自動テストをjenkins上で行なうことが可能となった。また、任意およびpush後のテストは任意のブランチに対して行なうことができるので、サーバ上でバシバシテストを回していくことができるんじゃないかなーと思う。