We want to bundle gems in a way that supports multiple platforms or versions of
ruby. This might translate into different Gemfile
version rules, and will
almost certainly result in different versions recorded in Gemfile.lock
.
This is sometimes referred to as "Dualbooting" (as in this conference talk by @rafaelfranca).
It seems like what we want is a Gemfile
and Gemfile.lock
for each supported
platform/ruby. However, in practice, the actual Gemfile content will be mostly
identical.
This would be useful when used with matrixed tests, like Travis
CI's build matrix keyword
gemfile
:
matrix:
include:
- rvm: 2.5
gemfile: gemfiles/Gemfile.rails-3.2.x
env: ISOLATED=false
- rvm: 2.5
gemfile: gemfiles/Gemfile.rails-3.2.x
env: ISOLATED=true
Approaches to this issue have been discussed and evolved over the years:
- 2018:
- 2017:
- 2013:
- A second Gemfile and lock? I.e. Gemfile.local, Gemfile.lock.local? (ruby-bundler Google group)
- How to use different Gemfiles with Bundler
- 2010:
- Multiple lockfiles? (ruby-bundler Google group)
See bundle_gemfile/
directory for an example
Multiple Gemfiles, Multiple Ruby Versions, One Rails reviews the current
options (including the appraisal gem) and makes a strong case for
just using BUNDLE_GEMFILE
+ eval_gemfile
:
BUNDLE_GEMFILE=gemfiles/Gemfile.2.4.5 bundle install
BUNDLE_GEMFILE=gemfiles/Gemfile.2.4.5 rails server -p 4321
# Contents of Gemfile.2.4.5
ruby "2.4.5"
eval_gemfile "Gemfile"
A classic (2010-2013) solution, mentioned in the ruby=bunder
Google group
discussion, A second Gemfile and lock? I.e. Gemfile.local,
Gemfile.lock.local?.
From the article How to use different Gemfiles with Bundler:
Bundler supports passing an environment variable called
BUNDLE_GEMFILE
to all of its commands. Here is an example how we can tell it to use a file calledGemfile-rails4
:
BUNDLE_GEMFILE=Gemfile-rails4 bundle install --path vendor/bundle-rails4
You can then run tests in the similar way:
BUNDLE_GEMFILE=Gemfile-rails4 bundle exec rake spec
See eval_gemfile/
directory for an example
The eval_gemfile
method has been available since bundler 1.2.0. It is a
popular choice, but is not considered part of bundler's public API by its
developers.
From @segiddins comment in Bundler cop idea: recommend eval_gemfile #3903:
Please note that
eval_gemfile
is still (purposefully) undocumented, and while we strive to support it, we do not fully consider it public API at this point
Before eval_gemfile
, it was common to load another file with the Gemfile
like so
From rubocop-hq/rubocop
issue Bundler cop idea: recommend eval_gemfile #3903 (2017):
eval(File.read('Gemfile.local')) if File.exist?('Gemfile.local')
Mentioned in A second Gemfile and lock? I.e. Gemfile.local, Gemfile.lock.local? (ruby-bundler Google group):
load "#{File.dirname __FILE__}/Gemfile"
From phamkykhoi/redmine.tanphat.com
instance_eval File.read(file), file
shopify/bootboot
is a bundler plugin than natively supports dualbooting.
- The sync feature requires bundler >= 1.17
- β Only supports two versions
`thoughtbot/appraisal is a bundler plugin than natively supports dualbooting.
Adds a new file, Appraisals
:
appraise "rails-3" do
gem "rails", "3.2.14"
end
appraise "rails-4" do
gem "rails", "4.0.0"
end
These combine (and in the event of a collision, override) the dependencies in
your Gemfile
.
# In .travis.yml
gemfile:
- gemfiles/3.0.gemfile
- gemfiles/3.1.gemfile
- gemfiles/3.2.gemfile
- bundler 2 has many features that can help
- Bundler 2.0 dropped support for Ruby < 2.3 (no legacy Puppet 4) and RubyGems < 3.0
Travis simulator: https://github.com/grosser/wwtd
- nicer than travish
wwtd --local # Run all gemfiles on current ruby -> get rid of Appraisal