Gem Versioning and Bundler: Doing it Right
Gem Versioning and Bundler: Doing it Rightvia yehudakatz.comMay 30th, 2011
Executables
When you install a gem to the system, Rubygems creates wrappers for every executable that the gem makes available. When you run an executable from the command line without bundle exec, this wrapper invokes Rubygems, which then uses the normal Rubygems activation mechanism to invoke the gem’s executable. This has changed in the past several months, but Rubygems will invoke the latest version of the gem installed in your system, even if your
Gemfile.lockspecifies a different version. In addition, it will activate the latest (compatible) installed version of dependencies of that gem, even if a different version is specified in yourGemfile.lock.This means that invoking executables as normal system executables bypasses bundler’s locked dependencies. In many cases, this will not pose a problem, because developers of your app tend to have the right version of the system-installed executable. For a long time, the Rake gem was a good example of this phenomenon, as most
Gemfile.locks declaredRake 0.8.7, and virtually all Ruby developers hadRake 0.8.7installed in their system.As a result, users fell into the unfortunate belief that running system executables was compatible with bundler’s locked dependencies. To work around some of the remaining cases, people often advocate the use of rvm gemsets. Combined with manually setting up application-specific gemsets, this can make sure that the “system executables” as provided via the gemset remain compatible with the
Gemfile.lock.Unfortunately, this kludge (and others) sufficiently reduced the pain that most people ignored the advice of the bundler documentation to always use
bundle execwhen running executables tied to gems in the application’sGemfile.lock.It’s worth noting that typing in
Click here to cancel reply.rake foo(oranyexecutable foo) in the presence of aGemfile.lock, and expecting it to execute in the bundler sandbox doesn’t make any sense, since you’re not invoking Bundler. Bundler’s sandbox relies on its ability to be present at the very beginning of the Ruby process, and to therefore have the ability to ensure that the versions of all loaded libraries will reflect the ones listed in theGemfile.lock. By running a system executable, you are executing Ruby code before Bundler can modify the load path and replace the normal Rubygems loading mechanism, allowing arbitrary unmanaged gems to get loaded into memory. Once that happens, all bets are off.
Great tip as I’ve been having the ‘rake-problem’ myself lately while upgrading to 0.9.1.