開発中の Gem に同梱するコマンドが CircleCI の Bundler に認識されない現象と解決方法
Circle CI でテストしながら Gem を開発している際に、Gem に追加したコマンドが認識されない現象と原因、解決方法について。
シチュエーション
以下のようなcircle.yml
の下で Gem を開発しているとする。
dependencies: pre: - gem install bundler
Gem に同梱するコマンドとしてexe/foo
を追加(.gemspec
のbindir
にはexe
を指定済み)し、コマンドが正常動作するかを CircleCI 上で確認するためにcircle.yml
に下記を追加する。
test: post: - bundle exec foo
ここまでの変更を push して CircleCI 上でジョブを起動すると、testステップで以下のようなメッセージと共に失敗する。
$ bundle exec foo bundler: command not found: foo Install missing gem executables with `bundle install` bundle exec foo returned exit code 127
実行コマンドとして追加した筈のfoo
が認識すらされていない。
原因
恐らくだが、CircleCI のキャッシュが原因。
bundle exec
でコマンドを実行する場合、開発中の Gem のコード(lib/**/*.rb
)はレポジトリ内のパスを参照するが、実行ファイル(exe/foo
)はbundle install
でインストールしたパス(vendor/bundle
)を参照する。
CircleCI はビルド開始時に依存ライブラリをvendor/bundle
にダウンロードし、ビルド終了後にその内容をキャッシュする。次回ビルド時にはbundle check
で依存ライブラリに変更があるかを確認し、変更がある時だけbundle install
を再実行する。これは CircleCI の dependency ステップのコマンドを確認するとわかる。
$ bundle check --path=vendor/bundle || bundle install --path=vendor/bundle --jobs=4 --retry=3
開発中Gemへの実行ファイル追加はGemfile.lock
への変更とはならない、つまり依存ライブラリの変更とは見なされない。したがってbundle install
が実行されず追加した実行ファイルもvendor/bundle
以下に入らない。
確認してないが、たぶん追加だけではなく、実行ファイルのコードを修正した場合でも修正が反映されない事象が起きる気がする。
解決方法
もし開発しているGemの依存ライブラリに修正が入れば、そのタイミングでbundle install
が実行されるので解決する。が、もちろんそう都合良くはいかないので、CircleCI の UI からキャッシュを削除して再実行すればいい。各ビルド結果のページの右上から「Rebuild without cache」を選択すれば、その場でキャッシュ(≒ vendor/bundle
)を削除して再ビルドされる(そしてfoo
がコマンドとして認識される筈)。