Skip to content

Instantly share code, notes, and snippets.

@njvitto
Forked from RSpace/deploy.rake
Created April 11, 2010 16:56
Show Gist options
  • Save njvitto/362873 to your computer and use it in GitHub Desktop.
Save njvitto/362873 to your computer and use it in GitHub Desktop.
Rakefile to deploy and rollback to Heroku in two different environments (staging and production) for the same app
#Deploy and rollback on Heroku in staging and production
task :deploy_staging => ['deploy:set_staging_app', 'deploy:push', 'deploy:restart', 'deploy:tag']
task :deploy_production => ['deploy:set_production_app', 'deploy:push', 'deploy:restart', 'deploy:tag']
namespace :deploy do
PRODUCTION_APP = 'YOUR_PRODUCTION_APP_NAME_ON_HEROKU'
STAGING_APP = 'YOUR_STAGING_APP_NAME_ON_HEROKU'
task :staging_migrations => [:set_staging_app, :push, :off, :migrate, :restart, :on, :tag]
task :staging_rollback => [:set_staging_app, :off, :push_previous, :restart, :on]
task :production_migrations => [:set_production_app, :push, :off, :migrate, :restart, :on, :tag]
task :production_rollback => [:set_production_app, :off, :push_previous, :restart, :on]
task :set_staging_app do
APP = STAGING_APP
end
task :set_production_app do
APP = PRODUCTION_APP
end
task :push do
puts 'Deploying site to Heroku ...'
puts `git push -f git@heroku.com:#{APP}.git`
end
task :restart do
puts 'Restarting app servers ...'
puts `heroku restart --app #{APP}`
end
task :tag do
release_name = "#{APP}_release-#{Time.now.utc.strftime("%Y%m%d%H%M%S")}"
puts "Tagging release as '#{release_name}'"
puts `git tag -a #{release_name} -m 'Tagged release'`
puts `git push --tags git@heroku.com:#{APP}.git`
end
task :migrate do
puts 'Running database migrations ...'
puts `heroku rake db:migrate --app #{APP}`
end
task :off do
puts 'Putting the app into maintenance mode ...'
puts `heroku maintenance:on --app #{APP}`
end
task :on do
puts 'Taking the app out of maintenance mode ...'
puts `heroku maintenance:off --app #{APP}`
end
task :push_previous do
prefix = "#{APP}_release-"
releases = `git tag`.split("\n").select { |t| t[0..prefix.length-1] == prefix }.sort
current_release = releases.last
previous_release = releases[-2] if releases.length >= 2
if previous_release
puts "Rolling back to '#{previous_release}' ..."
puts "Checking out '#{previous_release}' in a new branch on local git repo ..."
puts `git checkout #{previous_release}`
puts `git checkout -b #{previous_release}`
puts "Removing tagged version '#{previous_release}' (now transformed in branch) ..."
puts `git tag -d #{previous_release}`
puts `git push git@heroku.com:#{APP}.git :refs/tags/#{previous_release}`
puts "Pushing '#{previous_release}' to Heroku master ..."
puts `git push git@heroku.com:#{APP}.git +#{previous_release}:master --force`
puts "Deleting rollbacked release '#{current_release}' ..."
puts `git tag -d #{current_release}`
puts `git push git@heroku.com:#{APP}.git :refs/tags/#{current_release}`
puts "Retagging release '#{previous_release}' in case to repeat this process (other rollbacks)..."
puts `git tag -a #{previous_release} -m 'Tagged release'`
puts `git push --tags git@heroku.com:#{APP}.git`
puts "Turning local repo checked out on master ..."
puts `git checkout master`
puts 'All done!'
else
puts "No release tags found - can't roll back!"
puts releases
end
end
end
@djburdick
Copy link

nice gist! there's a rollback feature in heroku (maybe this is new). heroku rollback --app app_name, so you don't actually need to tag the releases yourself now. heroku releases --app app_name

@djburdick
Copy link

here are my mods if you're curious: https://gist.github.com/4410411

@misner
Copy link

misner commented May 9, 2013

I try running heroku run rake deploy but i get an error on my app:
/app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.4/lib/rake/task_manager.rb:49:in []' /app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.4/lib/rake/application.rb:142:ininvoke_task'
/app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.4/lib/rake/application.rb:101:in block (2 levels) in top_level' /app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.4/lib/rake/application.rb:101:ineach'
/app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.4/lib/rake/application.rb:101:in block in top_level' /app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.4/lib/rake/application.rb:110:inrun_with_threads'
/app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.4/lib/rake/application.rb:95:in top_level' /app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.4/lib/rake/application.rb:73:inblock in run'
/app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.4/lib/rake/application.rb:160:in standard_exception_handling' /app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.4/lib/rake/application.rb:70:inrun'
/app/vendor/bundle/ruby/1.9.1/gems/rake-10.0.4/bin/rake:33:in <top (required)>' /app/vendor/bundle/ruby/1.9.1/bin/rake:19:inload'
/app/vendor/bundle/ruby/1.9.1/bin/rake:19:in `

'
Would you know what should I do to fix this ?

@jphenow
Copy link

jphenow commented Jun 2, 2013

Made a few abstractions, additions, protections https://gist.github.com/jphenow/5694169

@jfeaver
Copy link

jfeaver commented Mar 27, 2014

Extracting the tasks into a class for easier extension:

https://gist.github.com/jfeaver/9820478

Thanks for the great foundation work @njvitto!

@J3RN
Copy link

J3RN commented Jul 29, 2014

I added Rspec testing, improved Heroku toolbelt support, and post-deploy rake task running on my fork.

@toncid
Copy link

toncid commented Jan 15, 2015

Great task! :)

A interesting thing happened the other day while I was using it to deploy code and run migrations.

Heroku Toolbelt detected that there is an update available and started updating itself in the middle of a running task. This resulted in maintenance mode being turned on, code being deployed and all other tasks cancelled because of the "update in progress" status. This left the environment down and its DB "unmigrated".

I recommend adding another task that is run first:

  task :update_toolbelt do
    puts 'Checking for Heroku Toolbelt updates ...'
    puts `heroku update`
    puts `heroku plugins:update`
  end

And you probably want to run it first when deploying to production. For example:

task :deploy_production => ['deploy:update_toolbelt', 'deploy:set_production_app', 'deploy:push', 'deploy:restart', 'deploy:tag']

namespace :deploy do
  ...
  task :production_migrations => [:update_toolbelt, :set_production_app, :push, :off, :migrate, :restart, :on, :tag]

Hope this helps!

EDIT: Added the plugin update check.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment