tl;dr

Set your project’s bundler config up to always cache all platforms’ gems.

bundle config set cache_all_platforms true

Don’t forget to update .gitignore if you want to commit the file.

- /.bundle
+ /.bundle/*
+ !/.bundle/config

Bundling all platforms, all the time

Bundler introduced some fantastic features for multi-platform support in version 2.2.

These changes are especially handy if you want to get all the benefits of using a local cache but you’re, say, developing on a Mac and deploying on Linux.

For this post, we’ll focus on Nokogiri. The same steps would apply to any gem.

It’s actually quite simple, and Nokogiri’s docs even spell out how you can achieve it.

bundle lock --add-platform x86_64-darwin
bundle lock --add-platform x86_64-linux
bundle package --all-platforms

Kevin Murphy wrote about “Caching all native Ruby gem platforms” over at The Gnar Company. He goes into more details and I recommend reading that post.

These steps are great, and they work, but they’re still extra steps.

Let’s configure a project to automatically run the equivalent of bundle package --all-platforms.

Overwriting a user’s bundle config

Thankfully Bundler makes it easy to overwrite a user’s global config with project specific settings

The bundler config docs lay out the order configuration files are loaded in.

  1. Local config (<project_root>/.bundle/config or $BUNDLE_APP_CONFIG/config)
  2. Environmental variables (ENV)
  3. Global config (~/.bundle/config)
  4. Bundler default config

In this case, it’s as simple as creating a .bundle/config file in your project’s root directory.

Heading back to bundler’s docs, we’ll find what config option to add.

This was a bit of a search to find what config option to add. I searched for platform and eventually found the option that described what I was trying to achieve.

cache_all_platforms (BUNDLE_CACHE_ALL_PLATFORMS): Cache gems for all platforms.

Now we have the configuration option, we can set it.

bundle config set --local cache_all_platforms true

To check that it worked, open .bundle/config and look for the BUNDLE_CACHE_ALL_PLATFORMS key.

Updating .gitignore

Rails, in its convention over configuration ways, provides a default .gitignore to prevent common files from being committed. While this is helpful, it also prevents any files in .bundle/ from being tracked by git, as seen here.

I strongly believe in making it clear when something deviates from a well established norm. While it would be possible to force git to track the file without updating the project’s .gitignore, it would make it confusing for anyone who is trying to track down why this file is committed.

The .gitignore file allows all manner of file patterns. We’ll be looking at a negative pattern. That is, we want to continue to ignore most files in the project’s .bundle/ directory, but we want to start tracking .bundle/config.

- /.bundle/
+ /.bundle/*
+ !/.bundle/config

The ! prefix excludes a file or pattern from matching. Remember that this is a list of files to ignore. We’re telling git to ignore all files in the .bundle directory, except for .bundle/config which we do want to track.

Now git status will correctly say that .bundle/config is untracked, so it is ready to be added and committed.