nfm
nfm

Reputation: 20667

How should I manage binary dependencies for my gems in production?

Bundler does an awesome job of making sure all the right gems are installed when I deploy.

However, some gems depend on binaries (eg Paperclip depends on ImageMagick, PDFKit depends on wkhtmltopdf) that may or may not be installed on the system being deployed to.

I've been bitten by this twice recently and need to figure out the best way to prevent it from happening again.

Is there a good way to ensure these external binaries and libraries are installed? Does bundler have any support for it? Should I just modify my capistrano deploy script? Should I include the binaries in my repository and make sure the gems look for them in the right place?

I can think of a few ways to approach this problem but want to know what you think works best, and why.

Upvotes: 12

Views: 1013

Answers (3)

With Puppet, you could say something like this in your manifest:

package{'foo': ensure => installed, } # native (like RPM) package providing one of your binaries, installed from a repo

#then explicitly declare the dependency like this. This will be installed using 'gem install' from the default place used by gem
package{'my_gem':
  ensure  => installed,
  require => Package['foo'],
  provider => 'gem',
}

Upvotes: 2

nathanvda
nathanvda

Reputation: 50057

Not sure if this is applicable for you, but we use puppet for this purpose. Another (similar) alternative is chef.

So what we do is 'script' the setup of our machines, but independently of our capistrano recipes. It is not completely ideal, but it also allows a cleaner seperation: our systems guys/devops use puppet, and the rails developers use capistrano.

Upvotes: 6

Jirapong
Jirapong

Reputation: 24256

From my experience same issue with you where my servers are difference platforms (OpenSuSe, CentOS, and OSX).

I put script to install binary (yum, zypper, apt-get, etc) on capistrano script, use ruby's system() method to verify the command work. in your example of ImageMagick. It something like this

  desc "ImageMagick from source"
  task :ImageMagick => :prepare do
    d_url = "ftp://ftp.imagemagick.org/pub/ImageMagick/ImageMagick.tar.gz"
    file = "ImageMagick.tar.gz"
    sh("#{os_installer} install -y ghostscript-devel ghostscript-library libpng-devel libjpg-devel")
    Dir.chdir('downloads') do
      sh("wget -O #{file} #{d_url}")

      raise "ERROR: Unable to download ImageMagick" unless File.exist?(file)

      sh("tar -xzvf #{file}")
      unzip_dir = Dir.glob("ImageMagick-*").first

      Dir.chdir(unzip_dir) do
        sh("./configure --prefix=/usr --with-x=no --disable-static --with-modules --without-perl --without-magick-plus-plus --with-quantum-depth=8")
        sh("make")
        sh("make install")
        sh("ldconfig")
      end 

      raise "Unable to find ImageMagick" unless system("convert --version")
    end 
  end 

Upvotes: 12

Related Questions