rcmdnk's blog

Computer Troubleshooting: Troubleshooting (English Edition)

このブログはBitbucketにソースがあって、 そこにpushすると werckerに送られて自動でビルドされ、GitHubへ送られる 様になっているのですが、 ちょっと動かなくなっていたのを直したのでそれについて。

Octopressブログのwerckerでのビルド

werckerTravis 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

と呼ぶと、

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/以外にもインストールできる様になってるはずなんですが、 これが

run.sh
1
2
3
4
5
6
7
8
9
if [ -z "$WERCKER_BUNDLE_INSTALL_PATH" ] ; # Check $WERCKER_BUNDLE_INSTALL exists
then
    if [ -n "$WERCKER_BUNDLE_INSTALL_PATH" ]; # Check $WERCKER_BUNDLE_INSTALL exists and is not empty
    then
        bundle_command="$bundle_command --path $WERCKER_BUNDLE_INSTALL_PATH"
    else
        bundle_command="$bundle_command --path $WERCKER_CACHE_DIR/bundle-install/"
    fi
fi

みたいな事をしています。

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の値とが同じようなものではない、と言う点。

どういうことかというと、 --pathvendor/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 等の実行ファイルですが、この中身は

rake
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/ruby
#
# This file was generated by RubyGems.
#
# The application 'rake' is installed as part of a gem, and
# this file is here to facilitate running it.
#

require 'rubygems'

version = ">= 0"

if ARGV.first
  str = ARGV.first
  str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding
  if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then
    version = $1
    ARGV.shift
  end
end

gem 'rake', version
load Gem.bin_path('rake', 'rake', version)

と言った感じの簡単な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が呼ばれる事になります。

Sponsored Links
  1. rake 10.5.0 on Rubygems - Libraries

  2. bashの変数で、+xを後ろに付けると、変数が定義されている場合にはx(これは何でも良い)を返し、 そうでない場合はNULLを返します。

    ちなみにもし、${WERCKER_BUNDLE_INSTALL_PATH:+x}みたいに:+を使うと、 未定義、もしくはNULL(空文字)の場合にxを返す様になります。

    また、-xとすれば未定義の場合にx、それ以外は変数の値を返します。 また、:-xとなら未定義及びNULLの場合にx、それ以外は変数の値を返します。

    -の代わりに=を使うとxを返す際に、変数にもxを代入するようになります。

Sponsored Links

« ブログのはてブ人気リストをRSSから取得して表示する google-api-ruby-clientが非互換なアップデートされた »

}