Update: Jamie Macey wrote a gem for this, and we improved the logic a bit. Use it.
Quite a few people have asked about using Isolate on Heroku. There’s no built-in support for Isolate in Heroku’s current deployment tools, but it’s pretty easy to hack together using Heroku’s support for a .gems
manifest file.
Assumptions
-
You’re using some sort of
rake release
task for deployment. See “Alternatives” at the end of this post for, uh, an alternative. -
You’re using Isolate with its
:system
option set totrue
. If you’re using Isolate 2, this is the default.:system => true
allows isolated projects to use gems that are already installed on the system, which is necessary for Heroku support. -
I’m testing this with a simple isolated Rails 2.3.8 app, but the process is basically the same for any Rails version (or other framework). See the examples in Isolate’s
README
for details.
Dot Gems
Heroku’s .gems
manifest is a simple text file containing one gem dependency per line, in a format that’s very similar to a gem
command line. Heroku will read this file every time you release/push reinstall all your gem dependencies if it’s changed.
We’re going to generate .gems
from our Isolate file. Here’s my lib/tasks/isolate.rake
file:
desc "Generate a .gems manifest."
task :dotgems => %w(Isolate lib/tasks/isolate.rake) do
File.open ".gems", "wb" do |f|
entries = Isolate.sandbox.entries.sort_by { |e| e.name }
f.puts "isolate --version '= #{Isolate::VERSION}'"
entries.each do |entry|
next unless entry.matches? "production"
gems = [entry.name]
gems << "--version '#{entry.requirement}'"
if entry.options[:source]
gems << "--source #{entry.options[:source]}"
end
f.puts gems.join(" ")
end
end
abort "Commit .gems, it changed." if /\.gems/ =~ `git status`
end
This task looks at all your isolated gem entries, picks the ones that are valid for production, and writes each of them to the .gems
file. It also adds Isolate itself as a dependency. If .gems
has changed, the task aborts and reminds you to commit the changes.
Releasing
My rake release
task does a fair amount of stuff before it pushes to Heroku, like creating a release email and running some sanity checks. To make sure my gem dependencies are always up-to-date, I just make sure the release
task depends on dotgems
:
task :release => :dotgems
Done. Any time I run rake release
, .gems
will either be up-to-date or prompt me to commit the changes before continuing.
Alternatives
This works Well Enough for me at the moment, but it could certainly be smoother. If you deploy using git push
instead of wrapping it in a Rake tasks, you could use a Git pre-commit hook to regenerate .gems
if necessary.
Isolate 2 has added some lifecycle event hooks, so it’d also be pretty easy to autogenerate .gems
every time Isolate runs.
Of course, the best possible solution would be for Heroku to support Isolate natively, but I wouldn’t hold your breath for that. :-)