Automatically generate Heroku .gems file

One of the greatest parts of Heroku is that it takes care of installing your gems for you. No more /vendor/gems, missing spec files, painful upgrades, etc. In fact, no more /vendor/rails, either. A large number of gems are preinstalled for your dyno, and those are listed at http://installed-gems.heroku.com. If you have any gems not listed there, then you just have to list them in your RAILS_ROOT/.gems file:

ambethia-smtp-tls --version '>= 1.1.2' --source http://gems.github.com
mislav-will_paginate --version '>= 2.3.11' --source http://gems.github.com
thoughtbot-paperclip --version '>= 2.3.1' --source http://gems.github.com
josevalim-inherited_resources --version '>= 0.8.5' --source http://gems.github.com
justinfrench-formtastic --version '>= 0.2.2' --source http://gems.github.com
authlogic --version '>= 2.1.1'

The problem.

This can become a bit of a pain, in terms of keeping your .gems file in sync with the config.gems declarations in your environment.rb file. Luckily, Mark Dodwell posted a little rake task to produce your .gems file automatically.

The improved solution.

I’ve gone ahead and added a small improvement to that task. The version below will ping http://installed-gems.heroku.com and only write out the gems you need that aren’t already installed there. Just put this snippet into lib/tasks/heroku_gems.rake:

namespace :gems do
  desc "Generate .gems file for Heroku"
  task :heroku_spec => :environment do
    require 'open-uri'
    installed_gems = []
    url = "http://installed-gems.heroku.com/"
    open(url).read.scan(/<li>(\w+) [^<]*<\/li>/) do |w| 
      installed_gems << w.first
    end

    gems = Rails.configuration.gems
    
    # output .gems
    dot_gems = File.join(RAILS_ROOT, ".gems")
    File.open(dot_gems, "w") do |f|
      output = []
      gems.each do |gem|
        next if installed_gems.include?(gem.name)
        spec = "#{gem.name} --version '#{gem.version_requirements.to_s}'"
        spec << " --source #{gem.source}" if gem.source
        output << spec
      end
      f.write output.join("\n")
      puts output.join("\n")
    end
  end
end

Fork and edit this post on Github.