BUNDLED WITH で Gemfile.lock が更新されてしまう件

  • 310
    Like
  • 3
    Comment
More than 1 year has passed since last update.

BUNDLED WITH って何?

bundler 1.10.0 以降で作成された Gemfile.lock では、BUNDLED WITH というセクションがあって、使用された bundler のバージョンがトラックされるようになっている [3485]

例えば bundler 1.9.0 で作成された Gemfile.lock のあるプロジェクト上で、 最新の bundler 1.10.3 を使用して bundle install --path vendor/bundle すると、 Gemfile.lock には以下の差分ができる。

+
+BUNDLED WITH
+   1.10.3

これをもう一度 1.9.0bundle install すると、上の文字はきれいさっぱり消える。

BUNDLED WITH の何が困るか

多人数で開発するときには、この変更は微妙に邪魔で、意図しない Gemfile.lock 変更が出てしまうことで Gemfile.lock にコンフリクトが出たり、本番環境の bundler とのバージョン違いが顕在化してデプロイが失敗したり、あるいはローカルで bundle install したせいで git pull ができなかったりしそうに見える。まあ git checkout -- Gemfile.lock すればいいけど、逐一やらねばならなくなってめんどくさい。

で、この変更が原因で、本家の issue がプチ炎上することになってしまったのだけど、その本家の issue を見ると今回の変更の意味がよくわかった。

本家が伝えたかったこと

https://github.com/bundler/bundler/issues/3697#issuecomment-108299463

  • まず、bundler開発者としては、ユーザがどのバージョンで bundle install したのかという情報は、大昔からトラックしたかったものだった。今回、多大な努力のうえでようやく実装することができた
  • 本家としては、以下のようなユースケースのフローを想定している
    • bundle install すると、Gemfile.lock 内の BUNDLED WITH を更新する(ただし、より新しい場合のみ)
    • 更新したら、他の gem をアップデートした場合とまったく同じように、 Gemfile.lock の更新をコミットする
    • BUNDLED WITH よりも古いバージョンの bundler を使っている人は、BUNDLED WITHの記述を更新しない。が、bundler を更新するように注意書きが出る
  • …というフローを想定していたが、現在は移行期のため以下のような問題が出ている
    • 1.9.9 以前の bundler では Gemfile.lock 内の BUNDLED WITH を削除してしまう
    • だから 1.9.9 以前の人と 1.10.0 以後の人とで git のやり取りをしていると、しょっちゅうファイルの変更が衝突してしまう
    • この問題は、全員が 1.10.0 以降になれば起こらなくなる
  • 今回の変更はワークフローにこそ影響はあるかもしれないものの、 bundler それ自体としては互換性を失う変更をしていないし、SemVerにも反していない
  • bundler開発者はめっちゃ頑張って開発者のためになるよう作り込んでるわけで、今回それで迷惑かけちゃってごめんね?
  • 1.9 系のアップデートは出さない。出したところで 1.10.X 以降にアップグレードしたほうが根本的解決になるから、1.9系を利用し続ける意味はない
  • これでもなお問題となるものがあるようであれば、きちんとした形で教えてほしい

おこでした。

あ、ちなみに、『BUNDLED WITH で指定されたバージョンの bundler を起動する』機能は実装される可能性があるらしい。できたらめっちゃ素敵。

@suu_g の推奨対応

bundle install--frozen オプションをつけよう。役に立つわりに長年ほとんど注目されることもなかった不遇のオプション1だが、ここに来てようやく報われるときが来たようだ。レリゴーだ。

bundle install--frozen オプションをつけると、その手元では Gemfile.lock を変更することはなくなる。このオプションは .bundle/config に保存されるので最初の一回だけ指定すればいい。
凍結されたディレクトリでは、Gemfile.lock に変更は行われないし、行われるような変更を許容しなくなるので、確実に Gemfile.lock を適用したいというとき(デプロイ時)には非常に便利。
この状態で Gemfile を変更しようとすると、「フリーズさせているので Gemfile.lock が更新できません」と言ってくるようになる。開発環境では Gemfile を変更したときには凍結を解除しなければならない、のだけど、その手段はちゃんと準備されていないのでちょっと不便。

You are trying to install in deployment mode after changing
your Gemfile. Run `bundle install` elsewhere and add the
updated Gemfile.lock to version control.

If this is a development machine, remove the Gemfile freeze
by running `bundle install --no-deployment`.

上は Gemfile に変更を加えて bundle install を試したときの公式のメッセージ。 bundle install --no-deployment でいいと書いているが、これは大嘘。素直に従ってこれを実行すると、以前に設定してあった BUNDLE_PATH などのオプションをガン無視して system ruby 上に gem を install してしまう。危険。

だからこの場合は、.bundle/config に書かれた設定を手動で削除するのがベストな方法だ。

---
BUNDLE_BIN: ".bundle/bin"
BUNDLE_FROZEN: '1'    ## この行を消す
BUNDLE_PATH: ".bundle/gems"
BUNDLE_DISABLE_SHARED_GEMS: '1'

コマンドで設定を削除することもできる。こっちの方が簡単か。

$ bundle config --delete frozen

それと、 bundler のバージョンを可能な限り 1.10 以降に上げよう。現在問題が起きているのは 1.9 以前と 1.10 とが混在しているからなので、それをすべて上げてしまえば問題は起こらなくなる。
1.10 で追加された BUNDLED WITH だけど、これは 1.9 以前を通すと消えてしまう。それで編集コンフリクトが多発し、問題が大きく見えていたってだけのことだった。…というのは先述の通り。

さて、それでは開発者や本番環境に対して適用するべき具体的なものは何か、以下で説明していく。

各開発者のするべき対応

  • bundle install 時にはとにかく gem update をする
  • 他の人のアップデートを見たら自分もアップデートする
  • Gemfile.lockBUNDLED WITH 行でコンフリクトしたら git checkout -- Gemfile.lock; gem update bundler する
  • Gemfile.lock のコミットには気を使う。git add . とかせず、コミット単位を分けておく
  • Gemfile.lock は、内容をアップデートする意思のあるとき以外は極力変更しない。変更する気のないときは、手元環境でも bundle install--frozen オプションをつけておく
  • frozen させた Gemfile.lock を変更するときは手動。 .bundle/config 内にある BUNDLE_FROZEN の行を削除する

みんながこれやれば平和な開発チームになる。

本番環境でとるべき対応

  • bundle install--frozen オプションを追加する

これに限る。無駄な更新をデプロイ先で行わせないこと、大事。
bundlerオプション一覧を見ると --deployment も気になるかもしれない。これは --frozen--path とを組み合わせたオプションで、つまり以下のような挙動をしている:

  • vendor/bundle 以下に gem をインストールする(--path=vendor/bundle
  • Gemfile.lock を変更しないようになる(--frozen

初回ならこれを使っていくので問題ないが、 --path 指定を上書きされてしまう点には注意が必要。2

個人的な感想

BUNDLED WITHに書かれたバージョンは増えこそすれ巻き戻ることはない仕様だそうなので、世の中の bundler がすべて 1.10.0 以降になれば状況はまた変わるでしょう。いまは狭間のタイミングだから混乱が起きているわけで。
それに、デプロイ先に --frozen を使うようにすれば、現状これはほとんど問題にならなくなる。みんな手元の bundler をアップデートしたうえでデプロイ先では --frozen しよう。Gemfile.lock should never bother you anymore.

あとソフトウェア製作者への応援大事だし、自分の期待通りに動かなくなったからといってOSS作者に不満や修正要求をぶつけちゃダメ。自分で直してプルリク作ろう。まじ本家issue読んでると作者たちかわいそうになる。

そんな感じ。誰か私も応援してください。わぁいストック。


著者紹介

@suu_g。バンドラーの記事をたまに書く人。まれに無線とかする。


  1. 今回、本家で言及すらされていない…不遇すぎる… 

  2. 2015/06/21 記述修正。 --no-deployment の挙動がちょっと衝撃的だったので併せて勘違いしてしまいました。vendor/bundle はドキュメント通りの挙動ですね。すみません。(編集内容ご覧になる場合は履歴からどうぞ)