I haven’t done a techie blog post for a while, and this “solved problem” keeps raising its head at work, so here goes.
Update Ruby to Latest Version on Mac OS X. This took a while to run as it had to install a lot of dependencies and asked for my permission a couple of times.
Here’s the situation:
- A ruby web app
- Develop on Mac OS X
- Deploy on Linux
- Using bundler to control gem versions
- Using therubyracer and libv8
You build your app on your Mac, install your bundle of gems using bundler, and your tests run fine. Now you deploy the app to your production system, and there’s this error:
Or worse, this error:
If you’re careful with your production deploys, then
- Your app does not run as root on the production servers, and
- Your app’s gems are cached in vendor/cache, thus not pulling gems from rubygems.org, or anywhere else.
Here’s the thing with libv8: it’s designed to be optimized for each platform where it runs, so the gem has native extensions with different versions for Mac OS X and Linux. Your gem cache needs to have both native versions of the gem in vendor/cache to satisfy the two platforms. When you bundle the gems on your development machine it doesn’t build the linux native version (obviously).
When you deploy the app on Linux, bundler fails because it doesn’t find the Linux custom gem.
Under the deployment conditions I described, trying to build libv8 fails trying to write to the system gems, hence the permission error I mentioned. (I have no idea what therubyracer is doing trying to write into the libv8 gem!)
So here’s how to solve the problem.
First, on your development system, do the normal bundle install, verify that the darwin (Mac OS X) version of libv8 is found in vendor/cache:
Now commit your changes with the new gem file, and push it to your git repository.
Next, connect to a handy Linux server. Install rvm (or rbenv) and your current Ruby, with a nice clean gemset. Be sure you have bundler installed.
(You may need some bundle options):
Clone your git repo, and do a bundle install. Using rvm (or rbenv) means you’ll have local copies of ruby and your gem set, and that should avoid any permissions problems.
Now check vendor/cache, and you should have a Linux version of the libv8 gem available. To deploy on both platforms, keep both in vendor/cache. Bundler has been good about keeping both versions around and not trying to clean the one you’re not using:
For good measure, I also often include the generic version of the gem, downloaded manually from rubygems. Add your updates to your git repository and push them to the remote origin.
You should find that your app deploys successfully on Linux now.