Sunday, March 19, 2017

Rails Deployment Without Rubygems.org

30/52 - Step(s)
30/52 - Step(s) (Photo credit: whatmattdoes)
A while back I had to deploy a Rails app to a remote machine without access to the Internet.  That means the gem and bundle commands are unable to download gems from rubygems.org.

Fortunately, you can upload gem files and install them from the current directory.

gem install -f -l *.gem

When you run bundler in your app's root directory, tell it to use gems that are already locally installed:

bundle install --local

But if you put your gems are in the vendor/cache directory, no need to --install.  Bundler looks in vendor/cache first.

Uploading works when both machines have the same OS.  But what if the target machine is a different platform?  Maybe you develop on Mac OS, but deploy on Linux.

Fortunately, the  gem install command lets you specify what platform to target with the "--platform" option

gem install rails --platform x86_64-linux -i vendor/cache
Note that an install directory is specified with the "-i" option.  This puts the new gems in a specific directory rather than overriding the gems you want to keep using in your development environment.

You'll need to determine the platform of the target machine.  Its the second half of the colon-separated string returned with this command.

$ gem env platform
ruby:x86_64-linux

This gem install command needs to be executed for every gem mentioned in your app's Gemfile.  All the gems these are dependent on will get installed in the process.

To execute the install command for the exact version of each dependency, execute this code from the rails console for the application:

where = "~/apps/tardir"
x = Bundler.load.current_dependencies.map{|d| d.name}
y = Bundler.load.specs.map do |s|
  if x.include? s.name 
    system "sudo gem install #{s.name} -v #{s.version}#{s.git_version}\
    --platform x86_64-linux -i #{where} --no-rdoc --no-ri"
  end  
end

How you get your gems to the production machine will depend on how you deploy.  At some point your deployment will need to install the gems on the linux machine with this command:

gem install --force --local *.gem --no-rdoc --no-ri

When deployment is happening on a regular basis, gems can change as new features are implemented.  You'll probably want to run the code above periodically.