#はじめに
JenkinsでRailsのテストやってますか?
肥大化するテストを高速実行するためにテストを複数のJobに分割するというのは有効な手段です。しかし、Railsのテストを行うためにはbundle installによるGemのセットアップが必須です。分割したJobが一斉にbundle installするとトータルでのテスト実行時間は極端に低下してしまいます。
ここでは、時間のかかるbundle installを高速に行うための解説を行います。
#なぜbundle installは遅いのか?
bundle installコマンドを叩くことで、bundlerはrubygems.orgから関連ライブラリをダウンロードしローカルにインストールします。しかし、GemによってはC言語で書かれたコードをコンパイルする事がありインストールに時間がかかります。また、複数Jobが一気にrubygems.orgへ問い合わせることでテスト環境のネットワーク帯域も圧迫されます。
#Gemfile.lockに変更がなければbundle installは高速じゃん?
gitのbranchが1つしかなければ、あまり問題は無いような気がします。しかし、私のプロジェクトでは開発者が好きなタイミングでbranchを指定してJenkinsを実行できるようになっています。もちろんbranchが違えばGemfile.lockが異なる可能性も考えられます。
#キャッシュすることは可能か?
Gemのインストール先はbundleコマンドの--pathオプションで指定することができます。
$ bundle install --path=vendor/bundle
ここで指定したpathはGemfileがあるディレクトリと同じ場所の.bundle/config
に書かれます。
---
BUNDLE_PATH: "vendor/bundle"
BUNDLE_DISABLE_SHARED_GEMS: '1'
と、いうことは.bundle/config
のBUNDLE_PATHを直接書き換えることができれば、各JobでbundlerがインストールしたGemを共有することができそうです。
シェルスクリプトを作ってみた
RUBY_VERSION=`cat .ruby-version`
GEM_SHA1=`openssl sha1 Gemfile.lock | sed -e s/SHA1\(Gemfile.lock\)=.//`
BUNDLE_PATH=$HOME/bundle_box/$RUBY_VERSION/$GEM_SHA1
if [ -e $BUNDLE_PATH ]; then # すでにbundleが存在すればPATHを書き換え
mkdir -p .bundle
cat << EOT > .bundle/config
---
BUNDLE_PATH: "$BUNDLE_PATH"
BUNDLE_DISABLE_SHARED_GEMS: '1'
EOT
else
bundle install --jobs=2 --path=vendor/bundle
GEM_SHA1=`openssl sha1 Gemfile.lock | sed -e s/SHA1\(Gemfile.lock\)=.//`
BUNDLE_PATH=$HOME/bundle_box/$RUBY_VERSION/$GEM_SHA1
if [ ! -e $BUNDLE_PATH ]; then #bundleが存在しなければmvしてPATHを書き換え
mkdir -p $HOME/bundle_box/$RUBY_VERSION
echo $BUNDLE_PATH
mv vendor/bundle $BUNDLE_PATH
cat << EOT > .bundle/config
---
BUNDLE_PATH: "$BUNDLE_PATH"
BUNDLE_DISABLE_SHARED_GEMS: '1'
EOT
fi
fi
ポイントはRubyのバージョンとGemfile.lockのSHA1ハッシュ値でGemの保存場所を切り分けている点です。
このスクリプトをJenkinsのJobでbundle install
している箇所に記述すれば、2回目以降はGemがインストールされることはありません。しかも、各Job共通でGemを使いまわすことができます。
なお、今回はRUBY_VERSION
はローカルの.ruby-version
ファイルを利用していますがJenkins側で値をセットして自由なRubyバージョンでテストさせてもいいかもしれません。
#さいごに
Jenkinsによるテストを高速化することでプログラムのリリースサイクルが向上し開発者のテンションも上がります。時間がかかっている箇所を探して積極的に高速化に取り組みましょう。