Gemfile 内で切り替える
こういう ENV
で切り替えるやつとか、eval_gemfile
を使うやつとか。
https://github.com/discourse/discourse/blob/v2.6.2/Gemfile#L9-L16
def rails_master? ENV["RAILS_MASTER"] == '1' end if rails_master? gem 'arel', git: 'https://github.com/rails/arel.git' gem 'rails', git: 'https://github.com/rails/rails.git' else # NOTE: Until rubygems gives us optional dependencies we are stuck with this needing to be explicit # this allows us to include the bits of rails we use without pieces we do not. # # To issue a rails update bump the version number here gem 'actionmailer', '6.0.3.3' gem 'actionpack', '6.0.3.3' gem 'actionview', '6.0.3.3' gem 'activemodel', '6.0.3.3' gem 'activerecord', '6.0.3.3' gem 'activesupport', '6.0.3.3' gem 'railties', '6.0.3.3' gem 'sprockets-rails' end
Gemfile.lock に差分が出るので、アプリケーション (Gemfile.lock をリポジトリに含める) の場合はイマイチだと思う。
ライブラリの場合は Gemfile.lock は含めないことの方が多いので、大いにやると良さそう。マトリックステストも ENV を置くだけなので簡単。
- https://github.com/zdennis/activerecord-import/blob/v1.0.8/Gemfile#L5-L9
- https://github.com/zdennis/activerecord-import/blob/v1.0.8/.travis.yml#L10-L14
eval_gemfile
と言えば dry-rb や thredded 等で、開発用の gem を別ファイルに追い出している文化も面白いよね。(余談です)
- https://github.com/dry-rb/dry-validation/blob/v1.6.0/Gemfile#L5
- https://github.com/thredded/thredded/blob/v0.16.16/Gemfile#L21-L23
これも余談だけど、一時期、Gemfile.local を読み込ませるために
eval_gemfile(__FILE__ + ".local") if File.exist?(__FILE__ + ".local")
と書くのが流行っていた時期もあって、これも Gemfile.lock に差分が出るので僕は苦手だったなって記憶が蘇ってきた。
各環境の Gemfile を用意する
各環境用の Gemfile を用意して、 BUNDLE_GEMFILE
環境変数を設定する。これも gem でよく見る。
# gemfiles/rails_5_2.gemfile source "https://rubygems.org" gem "rails", "~> 5.2.0" gemspec path: '../'
# .github/workflows/main.yml strategy: matrix: gemfile: - rails_5_2 - rails_6_0 - rails_6_1 env: BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile
gem は基本的に gemspec に依存が書かれていて、一部の指定が厳しい部分だけ *.gemfile で上書きすれば良いので、この形が自然に取れる。
アプリケーションの場合は eval_gemfile
を利用して、共通部分は Gemfile.common とか Gemfile.global とかに抽出して同じことをやる。
- https://github.com/brainspec/enumerize/blob/v2.3.1/Gemfile#L1
- https://github.com/brainspec/enumerize/blob/v2.3.1/Gemfile.rails52#L1
3 ファイル (current 用, next 用, common) 必要なことと、common に何を追い出すかを考えるのがめんどくさいことがイマイチ。
Gemfile.next の symlink を置いて切り替える
RailsConf 2018 で発表されていた方法。
僕は Getting Ready for Rails 6.0: How to Dual Boot - FastRuby.io | Rails Upgrade Service に書いてあるので知った。
Gemfile に next?
を定義して、symlink 経由で呼ばれたかどうかで切り替える。
def next? File.basename(__FILE__) == "Gemfile.next" end if next? gem 'rails', git: 'https://github.com/rails/rails.git', branch: 'main' else gem 'rails', '~> 6.1.0' end
- next? 用の lock ファイルは Gemfile.next.lock になる
- 差分が別ファイルになるのでコミットできる
- Gemfile だけで一元管理できる
という、上であげた 2 つの方法のデメリットをそれぞれ潰すことができている。
Shopify/bootboot
id:takkan_m が教えてくれた。
これ知ってます?https://t.co/ecxlsPWkKh
— えむ。 (@takkanm) February 28, 2021
https://github.com/Shopify/bootboot
symlink を使わず、Bundler の plugin として実装したもの。
if ENV['DEPENDENCIES_NEXT'] ... end
の内容が Gemfile_next.lock に書き出される。