Make bundler use different gems for different platforms

Viewed 30584

I'm working on upgrading one of our Rails 2.3.8 apps to Rails 3, and have run into an annoying problem with bundler and deployment. I develop the application on a Windows machine, but the production environment is running Ubuntu Linux. Now, my problem is that bundler is ignoring the mysql gem in the production environment, and Passenger spits out: "!!! Missing the mysql gem. Add it to your Gemfile: gem 'mysql', '2.8.1'"

Here is my Gemfile:

# Edit this Gemfile to bundle your application's dependencies.
# This preamble is the current preamble for Rails 3 apps; edit as needed.
source 'http://rubygems.org'

gem 'rails', '3.0.0'
gem 'net-ldap', :require => 'net/ldap'
gem 'highline', :require => 'highline/import'
gem 'mysql', '2.8.1'
gem 'net-ssh', :require => 'net/ssh'

# Bundle gems for the local environment. Make sure to
# put test-only gems in this group so their generators
# and rake tasks are available in development mode:
group :development, :test do
  gem 'fakeweb', :require => 'fakeweb'
  gem 'flexmock', :require => 'flexmock/test_unit'
end

As you can see, the mysql gem is specified. However, when deploying, bundler ignores it. Why? The reason is that Bundler generates the following Gemfile.lock (only relevant parts included):

....
mime-types (1.16)
mysql (2.8.1-x86-mingw32)
net-ldap (0.1.1)
....

Notice that it includes the platform specific gem. This is obviously NOT what I want it to do, as that gem is not suitable (and appearently ignored) when running under Linux.

So, does Bundler come with any way to deal with these issues? Or do I have to remember to manually change the mysql gem version in the generated Gemfile.lock every time I run bundle install on my development machine?

Thank you in advance!

Update

It seems like the bundler team is aware of this issue.

11 Answers

I deploy to Linux for our developers staging version, and to Windows for production and our users staging version. Thus I need a .lock file for both of them (production and staging only use the .lock file, and I want to be sure that I am using the gem versions used by the developers which are kept in the .lock file).

So I have three versions of my Gemfile.lock committed to my repository. There is the normal Gemfile.lock. I also create lock files for each different platforms (_win and _linux), which are a copy of the Gemfile.lock created on that platform.

It takes an extra round of commits to get both of the .lock files into the repo. Thus if I have to do a bundle install on my Mac after a windows pull request, I copy the pre-existing .lock file as the windows version, and copy my new .lock file as the linux version. I then do a pull request to update the lock files.

When Deploying, I overwrite the Gemfile.lock with the correct version.

The best solution I've found for this, where you have different platforms for your local development environment and for production is to always use the ruby platform gems on the Gemfile.lock.

You can accomplish this by setting the Bundler config value with

bundle config set --global force_ruby_platform 'true'

That way the bundle will always default to install the ruby platform gems. But this implies that all gems that need native extensions on a specific platform will have to be compiled, so you need to get sure you have the required tools and libraries installed on both your local and production machines.

Related