-
-
Save njvitto/362873 to your computer and use it in GitHub Desktop.
#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 |
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:in
invoke_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:in
each'
/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:in
run_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:in
block 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:in
run'
/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:in
load'
/app/vendor/bundle/ruby/1.9.1/bin/rake:19:in `
Would you know what should I do to fix this ?
Made a few abstractions, additions, protections https://gist.github.com/jphenow/5694169
Extracting the tasks into a class for easier extension:
https://gist.github.com/jfeaver/9820478
Thanks for the great foundation work @njvitto!
I added Rspec testing, improved Heroku toolbelt support, and post-deploy rake task running on my fork.
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.
here are my mods if you're curious: https://gist.github.com/4410411