Java7/Java8のデュアルビルド&自動テスト - Jenkinsで簡単自動化②
こんにちは、2014年新卒エンジニアの玉川です。
今回のエンジニアブログは怠惰のすゝめ。今更聞けない!? Jenkins・Gradle・Githubで簡単自動化①の続きとして、Java7/Java8のデュアルビルド環境構築およびGitHub・ChatWorkによる連携により、実用的なCI環境を実現します。
Jenkinsのジョブ構成
今回はJava8とJava7のデュアルビルド環境をJenkinsで構築します。将来的にJava8化を行いますが、現状Java8に未対応のライブラリが多く存在しているため、Java7でビルドすることで未対応部分を補うための処置です。こういった場合、本来であればプロジェクトを分けるべきかもしれませんが、大人の事情によりプロジェクトを1つで管理しなければならないケースもあり、このようなケースを想定し、Jenkinsでの環境構築を行います。
同一プロジェクト内に、Java7でテスト&ビルドする必要があるbatchファイルのようなものとJava8でビルド可能なTomcatプロジェクトが含まれているという少し極端な例です。
Job-1ではGitHubのPull Requestを検知し、Pull Requestがあった際に、Job-2及びJob-3に分岐します。
Job-2ではJDK7/Tomcat7にてビルド&テストを行い、Jarファイルを出力します。
Job-3ではJDK8/Tomcat8にてビルド&テストを行い、Warファイルを出力します。
Job-4ではJob-2及びJob-3が成功したら、結果を出力します。
GitHubリポジトリへのフック
GitHubリポジトリのpushをJenkinsでキャッチするには大きく分けて2つの方法があります。
前者はGitHubにJenkinsのURLを登録し、Pushがあった際にそのURLにPushされた情報をPostすることで、Jenkinsジョブが動きます。利点はリアルタイムでジョブが実行される点ですが、Jenkinsサーバーが社外からのアクセスを認めていない環境にある場合に利用できない点が欠点です。
後者はJenkinsに定期的にGitHubリポジトリを監視してもらうことで、Pushを検知します。前者の欠点としてあげた環境でも動作しますが、定期的に監視し、その際にPushされていたらジョブを実行するという処理になるため、リアルタイムではありません。
今回は社内サーバー環境を想定していますので、後者の方法を利用します。
Jenkinsのシステム設定
JDKのインストール
事前にJenkinsサーバーにはJDK1.7とJDK1.8の両方をインストールし、Jenkinsのシステム設定に両方のJDKを追加する必要があります。
プラグインのインストール
事前にJenkinsサーバーにはJDK1.7とJDK1.8の両方をインストールし、Jenkinsのシステム設定に両方のJDKを追加する必要があります。さらに、以下のプラグインが必要です。
Git Plugin
GitリポジトリをSCMとして利用できるようにします。
GitHub Plugin
GitHubプロジェクトをJenkinsで利用可能にします。
GitHub pull request builder plugin
プルリクエストがあったかどうかを判断します。
Parameterized Trigger plugin
Jenkinsのジョブをトリガとして、別のジョブを起動させるという処理が可能になります。パラメータを引き継ぐことも可能です。
GitHubアカウントの作成とSSHキーの登録
GitHubへアクセスし、Pull Requestを監視するため、事前にJenkins用GitHubアカウントの作成とSSHキーを登録する必要があります。GitHubアカウント作成後、以下の手順でSSHキーを登録します。
cd /var/lib/jenkins/.ssh
sudo -u jenkins -H ssh-keygen -t rsa -C jenkins@dev-test.com
rootで作成した場合は、所有権をjenkinsに変更する必要があります。
Jenkins用GitHubアカウントでログインし、SSH Keysにキーを登録します。id_rsa.pubの中身を貼付けるだけです。
GitHub Pull Request Builderのシステム設定
Jenkinsシステム設定からGitHub Pull Request Builderの設定を行います。最低限変更が必要な設定は以下の通りです。
Access Token
GitHubの設定→Applications→Personal Access tokensからtokenを作成し、入力します。repo及びrepo:statusにチェックを入れておく必要があります。
SSHを登録しておけば、下部にあるUsername、Passwordに入力し、Create access tokenボタンを押すと自動でtokenを作成・追加してくれます。
Admin list
Pull Requestを送った際にジョブを実行させる人を登録
Published Jenkins URL
ジョブの実行結果をGitHubのコメントとして残す場合は入力
Close failed pull request automatically?
ビルド&テストに失敗した場合に自動でPull Requestを閉じる場合はチェック
Crontab line
ポーリングを行う間隔を設定します。『*/5 * * * *』で5分おきに監視します。
ジョブの追加
Jenkinsに新規ジョブを追加します。
メインジョブ (Job-1)
GitHub project・・・GitHubプロジェクトのURLを入力(https://~~~~)
ソースコード管理・・・Gitを選択
Repository URL・・・git@github.com:~~~を入力
高度な設定 Name・・・Job
高度な設定 Refspec・・・+refs/pull/*:refs/remotes/origin/pr/*
Branches to build・・・${sha1}
ビルド・トリガ・・・Build when a change is pushed to GitHubにチェック、GitHub pull requests builder Admin listにチェック
Admin list・・・ビルドを行うユーザーを登録。システム設定で設定している場合は追加されているはずです。
Pull Requestを受け取ったらJava7・Java8の2つのジョブを実行し、GitHubのハッシュ値であるsha1を渡します。
Trigger/Call builds on other projectsを選択
Projects to build・・・Job-2
Block until the triggered projects finish their builds・・・チェックを入れる。
Predefined parameters・・・sha1=${sha1}を追加
Projects to build・・・Job-3
Block until the triggered projects finish their builds・・・チェックを入れる。
Predefined parameters・・・sha1=${sha1}を追加
終了後のジョブを登録します。
Trigger parameterized build on other projects・・・選択する。
Projects to build・・・Job-4
Trigger when build is・・・Stable or unstable but not failed
Jobから送っておきたいパラメータがある場合は以下を設定します。
Predefined parameters・・・『受け取り先パラメータ=受け渡したいパラメータ』で追加します。
Current build parameters・・・選択します。
Java7でのテスト&ビルドジョブ (Job-2)
JDK・・・JDK1.7を選択
ビルド設定
./gradlew build
./gradlew jar
Java7でのテスト&ビルドジョブ (Job-3)
JDK・・・JDK1.8を選択
ビルド設定
./gradlew -Pjdk8 build
./gradlew -Pjdk8 war
結果ジョブ (Job-4)
結果として出力したい情報を追加します。以下の例ではシェルの実行からChatWork APIを利用して、指定した部屋にプルリクがあったことを伝えています。各ジョブから受け取ったパラメータを指定することもできます。
シェルの実行に以下を追加
[info]
[title]
GitHub Pull Request Information
[/title]
以下のPRがありました。
Info : ${ghprbPullTitle} from ${ghprbPullAuthorEmail}
Description : ${ghprbPullDescription}
PRLink : ${ghprbPullLink}
Jenkins : ${jenkins_url}
[/info]
_EOT_`
curl -X POST -H "X-ChatWorkToken: chatwork APIのToken" -d "body=${_body}" "https://api.chatwork.com/v1/rooms/XXXXXX/messages"
Gradleファイルの設定
今回利用したgradleファイルは以下となっています。分岐にproject.hasProperty('jdk8')と記述することで、gradle(gradlew)コマンドを実行した際に-Pjdk8というプロパティがあるかどうかを確認することができます。
apply plugin: 'java'
apply plugin: 'war'
configurations {
provided
provided.extendsFrom(compile)
}
repositories {
mavenCentral()
}
dependencies {
compile fileTree(dir: "lib", include: '*.jar')
exclude group: 'org.hamcrest'
}
compile('org.hamcrest:hamcrest-all:1.3')
compile('org.mockito:mockito-all:1.9.5') {
exclude group: 'org.hamcrest'
}
if (project.hasProperty('jdk8')) {
compile 'org.apache.tomcat:tomcat-servlet-api:8.0.8'
compile 'org.apache.tomcat:tomcat-jsp-api:8.0.8'
} else {
compile 'org.apache.tomcat:tomcat-servlet-api:7.0.53'
compile 'org.apache.tomcat:tomcat-jsp-api:7.0.53'
}
compile 'org.apache.httpcomponents:httpclient:4.3.4'
}
if (project.hasProperty('jdk8')) {
[compileJava, compileTestJava].each{
it.options.encoding = 'UTF-8'
it.options.compilerArgs += ['-source', '1.8', '-target', '1.8']
}
sourceSets {
main {
java {
srcDir 'WEB-INF/src'
}
}
test {
java {
srcDir 'WEB-INF/test/'
}
}
}
} else {
[compileJava, compileTestJava].each{
it.options.encoding = 'UTF-8'
it.options.compilerArgs += ['-source', '1.7', '-target', '1.7']
}
sourceSets {
main {
java {
srcDir 'WEB-INF/src'
}
}
test {
java {
srcDir 'WEB-INF/test/'
}
}
}
}
[compileJava, compileTestJava]*.options.collect { options -> options.encoding = 'UTF-8' }
war {
destinationDir = file('./export/')
archiveName = 'test.war'
webXml = file('WEB-INF/web.xml')
}
jar {
copy {
from configurations.compile
into "build/distributions/lib"
}
def manifestClasspath = configurations.compile.collect{ 'lib/' + it.getName() }.join(' ')
manifest {
attributes 'Class-Path': manifestClasspath
}
from (configurations.compile.resolve().collect { it.isDirectory() ? it : fileTree(it) }) {
}
destinationDir = file('./export/')
archiveName = 'test.jar'
}
}
実行
全ての設定が終わった後、指定したリポジトリにPull Requestを送るとJenkinsのジョブが実行されます。ビルド&テストが成功するとGitHubのコメントにて結果が確認できます。
ChatWorkにも結果が出力されました。
ハマりポイント
①GradleによるJava7/Java8の切り替え
→プロパティにより分岐させる方法を取りました。その際にプロパティは『-P文字列』で指定するので注意が必要。
②ジョブ分岐時のブロック
→Block until the triggered projects finish their buildsにチェックを入れないと、ジョブが進行してしまう。ジョブが完了するまで待つ場合は必ずチェックする。
③ジョブ失敗時の中止処理
→Stable or unstable but not failedを選択しないと、途中でジョブが失敗しても継続してしまう。失敗時に次のジョブに移行しない場合は必ず選択する。
④ChatWork APIキーの設定
→ChatWork APIを使用する際に、APIが利用申請をしてから利用ができるようになるまで数日かかる。
おわりに
今回はJenkinsとGitHubの連携により、Java7/Java8のデュアルビルドを行い、結果をGitHubのコメント・ChatWorkのコメントとして出力するという流れを説明しました。
Jenkinsのジョブで分岐させ、Gradleに分岐の設定をすることで、Javaバージョンごとに処理を分けることが可能です。皆さんもこれを機に、各開発環境にマッチしたCI環境を構築し、どんどん怠惰していきましょう!