このブログはBitbucketにソースがあって、 そこにpushすると werckerに送られて自動でビルドされ、GitHubへ送られる 様になっているのですが、 ちょっと動かなくなっていたのを直したのでそれについて。
Octopressブログのwerckerでのビルド
werckerはTravis CI と同じような CI(継続的インテグレーション)サービスですが、 Bitbucketに対応していることと、 無料でGitHub/Bitbucketのプライベートレポジトリに対応しています。
Bitbucketでは無料でプライベートレポジトリが持てるので、 Bitbucket+werckerを使うと無料でプライベートなCI環境を作ることが出来ます。
起こった問題
rake
コマンドを使おうとした際、
$ rake generate
rake aborted!
Gem::LoadError: You have already activated rake 10.4.2, but your Gemfile requires rake 10.5.0. Prepending `bundle exec` to your command may solve this.
/home/ubuntu/.rvm/gems/ruby-2.3.0@global/gems/bundler-1.9.10/lib/bundler/runtime.rb:34:in `block in setup'
/home/ubuntu/.rvm/gems/ruby-2.3.0@global/gems/bundler-1.9.10/lib/bundler/runtime.rb:19:in `setup'
/home/ubuntu/.rvm/gems/ruby-2.3.0@global/gems/bundler-1.9.10/lib/bundler.rb:122:in `setup'
/home/ubuntu/.rvm/gems/ruby-2.3.0@global/gems/bundler-1.9.10/lib/bundler/setup.rb:8:in `<top (required)>'
/pipeline/build/Rakefile:2:in `<top (required)>'
LoadError: cannot load such file -- bundler/setup
/pipeline/build/Rakefile:2:in `<top (required)>'
(See full trace by running task with --trace)
みたいなエラーが出ていました。
rakeの10.5.0が先日リリースされて 1 それが影響している様です。
まず、wercker.ymlではboxを
box: wercker/rvm
で指定しています。
この状態だと、rake
がデフォルトで
/home/ubuntu/.rvm/rubies/ruby-2.3.0/bin/rake
に既にインストールされている状態でした。
また、環境変数としては
PATH=/home/ubuntu/.rvm/gems/ruby-2.3.0/bin:/home/ubuntu/.rvm/gems/ruby-2.3.0@global/bin:/home/ubuntu/.rvm/rubies/ruby-2.3.0/bin:/home/ubuntu/phantomjs/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/ubuntu/.rvm/bin
GEM_PATH=/home/ubuntu/.rvm/gems/ruby-2.3.0:/home/ubuntu/.rvm/gems/ruby-2.3.0@global
GEM_HOME=/home/ubuntu/.rvm/rubies/ruby-2.3.0
みたいな感じになっています。
Octopressの中で、Gemfileで
gem 'rake', '~> 10.0'
とrake
を指定していて、ビルドなどを行う前にbundle install
するようにしています。
このbundle install
ですが、werckerでは既にステップが用意されているので
それを使っています。
実はこれが問題で、単に
- bundle-install
と呼ぶと、
bundle install --path $WERCKER_CACHE_DIR/bundle-install/
が走ります。つまり、$GEM_HOMEとは別の所にインストールされます。
なので、このままrake
を使おうとすると$WERCKER_CACHE_DIR/bundle-install/(WERCKER_CACHE_DIRは/cacheという値が入っています。)
の方は無視して古い10.4.2を呼び出し、
ディレクトリにあるGemfile.lockと比較してオカシイ、と言う事になっています。
対処法
いくつか方法がありますが、一つはGEM_PATHにキャッシュディレクトリを 加えてあげる方法。
rake
を使う前に
export GEM_PATH=$(cat .bundle/config|grep BUNDLE_PATH|cut -d'"' -f2)/ruby/$(ruby -v|cut -d" " -f2|cut -d"p" -f1)/:$GEM_PATH
みたいな行を加えておけば行ける様になります。
(キャッシュ位置が変わっても良いようにbundle install
時に記憶される.bundle/config内の値を使って。)
もし、新たな実行コマンドもインストールされる場合、 PATHも
export PATH=$(cat .bundle/config|grep BUNDLE_PATH|cut -d'"' -f2)/ruby/$(ruby -v|cut -d" " -f2|cut -d"p" -f1)/bin:$PATH
と変更する必要があります。
ただし、例えばRakefileの中でjekyll
等を使う場合、
jekyll
がcacheの方にだけインストールされていても、
Rakefile内で呼ぶ限りはPATHの設定が無くても呼ぶことが出来ます。
または、キャッシュを使わずGEM_HOMEにそのままインストールするように、bundle-installを使わず、
- script:
name: bundle install
code: bundle install
と、普通に--path
オプション無しのbundle install
を自分で呼んであげれば
PATH等も気にせずに使えて楽です。
代わりにキャッシュを使えなくなるので、毎回全部インストールすることになりますが、 それ程多くのgemをインストールしないのであればこちらの方で良いかと。
取り敢えず今はこの後者の方法でやっています。
bundle-installの問題
ちょっとbundle-installのスクリプト を見ていたらバグが有って、 PATHを指定すると $WERCKER_CACHE_DIR/bundle-install/以外にもインストールできる様になってるはずなんですが、 これが
1 2 3 4 5 6 7 8 9 |
|
みたいな事をしています。
werckerでは、
- bundle-install:
path: verndor/bundle
の様に、ステップに対して変数を与える事が出来て、この場合、 WERCKER + ステップ名 + 変数名みたいな環境変数としてスクリプト内で使われます。
従って、ここではWERCKER_BUNDLE_INSTALL_PATHを指定しているわけですが、
スクリプトの中で、最初-z
でチェックして、その後-n
でチェックしています。
単なるシェルスクリプトですが、-z
は指定文字列が0文字であればTrue、
-n
は指定文字列が1文字以上ならばTrueになります。
従って、ここでは、pathに適当な空白以外の文字列を与えれば この部分全て飛ばされ、 何も与えないか空白を与え場合は最後のキャッシュをパスに指定する部分にたどり着きます。
コード中のコメントは嘘ですし、そもそもその通りのことをしても何も意味が無い。。。。
コメント通りの事をしたいなら、
if [ -z "${WERCKER_BUNDLE_INSTALL_PATH+x}" ] ; # Check $WERCKER_BUNDLE_INSTALL exists
とすれば出来ます2 。ので、どこかから変数チェックの方法をコピペしてきて間違えたのかな、、、と。
なので、上では、
- bundle-install:
path: verndor/bundle
と書きましたが、これはpathを指定せずに、デフォルトのGEM_HOMEにインストールすることになります。
なので、上の解決方の後者を使いたい時に、 適当に
- bundle-install:
path: nocache
とかpathに値を入れておけば単にbundle install
をパス指定無しで走らせて
同じことが出来ます。
が、bundle-installステップがこの辺対処してしまうと困るので、 直接自分で書いています。
bundle-installを使うメリットとしては、短い記述で済むのと、
rbenv
等を自動で呼んだりしてくれるところですが、
今は使ってないので特に必要ないので。
–pathの指定とGEM_HOME
bundlerの仕様でちょっとつまずいた所として、
--path
で指定する場所とGEM_HOMEの値とが同じようなものではない、と言う点。
どういうことかというと、
--path
でvendor/bundleの様に指定すると、
現在使ってるrubyのバージョンを含めた
vendor/bundle/ruby/2.3.0と言ったディレクトリにインストールされます。
一方、GEM_HOMEはこの vendor/bundle/ruby/2.3.0と言った実際にインストールされるディレクトリを指しています。
従って、
bundle install --path $GEM_HOME
みたいにしてしまうと、 インストールされるのは$GEM_HOME/ruby/2.3.0になるので、 vendor/bundle/ruby/2.3.0/ruby/2.3.0 みたいになってしまいます。
この辺、ちょっと勘違いがあるかもしれませんが、 bundle-installのpathへ値を渡そうと色々やってる時にちょっと戸惑ったのでメモ。
インストールされたgemの実行ファイル
/home/ubuntu/.rvm/rubies/ruby-2.3.0/bin/rake 等の実行ファイルですが、この中身は
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
と言った感じの簡単なRubyのスクリプトになっています。 実際のrakeの機能はここからGEM_PATHにあるrakeの本体を辿って実行されます。
ここで一つつまずいたのが、最初、
export PATH=$(cat .bundle/config|grep BUNDLE_PATH|cut -d'"' -f2)/ruby/$(ruby -v|cut -d" " -f2|cut -d"p" -f1)/bin:$PATH
と、PATHの方だけ指定して、rake
も新しい方を使ってるだろう、と思ってたんですが、
これをしても、さらに
/cache/bundle-install/ruby/2.3.0/bin/rakeと直接
新しいrake
を指定しても10.4.2の古いバージョンが呼ばれて困りました。
実際には、新たにインストールしたrake実行ファイルも 元から入ってる実行ファイルと全く一緒なのでPATHだけ変更しても意味が無く、
export GEM_PATH=$(cat .bundle/config|grep BUNDLE_PATH|cut -d'"' -f2)/ruby/$(ruby -v|cut -d" " -f2|cut -d"p" -f1)/:$GEM_PATH
とGEM_PATHの方も加えてあげないと意味がありません。
逆に、GEM_PATHに新たなgemのパスを加えると、 古い /home/ubuntu/.rvm/rubies/ruby-2.3.0/bin/rake の方を使っても新しい10.5.0のrakeが呼ばれる事になります。
-
bashの変数で、
+x
を後ろに付けると、変数が定義されている場合にはx
(これは何でも良い)を返し、 そうでない場合はNULLを返します。ちなみにもし、
${WERCKER_BUNDLE_INSTALL_PATH:+x}
みたいに:+
を使うと、 未定義、もしくはNULL(空文字)の場合にx
を返す様になります。また、
-x
とすれば未定義の場合にx
、それ以外は変数の値を返します。 また、:-x
となら未定義及びNULLの場合にx
、それ以外は変数の値を返します。-
の代わりに=
を使うとx
を返す際に、変数にもx
を代入するようになります。