#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 |
This comment has been minimized.
This comment has been minimized.
The problem is that you can have a lag beetwen the time of code pushed/server restarted and the migration executed (so for some seconds there can be some users navigating parts of your app with the new code that requires new data structure giving 500 errors). |
This comment has been minimized.
This comment has been minimized.
Got, and used. Great stuff. |
This comment has been minimized.
This comment has been minimized.
Oh...thank you! It's a pleasure! :)
Bye,
N.
…On Wed, Jun 8, 2011 at 11:53 AM, toastkid < ***@***.***>wrote:
Got, and used. Great stuff.
##
Reply to this email directly or view it on GitHub:
https://gist.github.com/362873
|
This comment has been minimized.
This comment has been minimized.
Shouldn't you turn off the app before the push, since the push will have code that requires the new schema? |
This comment has been minimized.
This comment has been minimized.
Yes. Sometimes I used that way...but right now I prefer to leave the app up
and running. The new code with the old schema is better than the app down :)
…On Tue, Jun 21, 2011 at 1:01 AM, jksilliman < ***@***.***>wrote:
Shouldn't you turn off the app _before_ the push, since the push will have
code that requires the new schema?
##
Reply to this email directly or view it on GitHub:
https://gist.github.com/362873
|
This comment has been minimized.
This comment has been minimized.
Thanks for sharing, I looked at the heroku_san gem and release management add-on but this seems to be the most elegant solution. |
This comment has been minimized.
This comment has been minimized.
Do you use the task :deploy_staging or deploy:staging_migrations ? |
This comment has been minimized.
This comment has been minimized.
It takes longer if you try to run migrations, so I typically use
:deploy_staging unless I know that there are migrations which need to be
run.
…On Tue, Oct 4, 2011 at 12:40 PM, Leo Romanovsky < ***@***.***>wrote:
Do you use the task :deploy_staging or deploy:staging_migrations ?
##
Reply to this email directly or view it on GitHub:
https://gist.github.com/362873
|
This comment has been minimized.
This comment has been minimized.
Yes it's safer and faster to generally use the :deploy_staging task if you haven't new migrations or if the code is backward compatible. |
This comment has been minimized.
This comment has been minimized.
Of course, I really wish the script could detect whether or not there are
new migrations, because forgetting to run the migrations is a problem.
…On Wed, Oct 5, 2011 at 6:07 AM, Nicola Junior Vitto < ***@***.***>wrote:
Yes it's safer and faster to generally use the :deploy_staging task if you
haven't new migrations or if the code is backward compatible.
So use the deploy:staging_migrations only if you have new migrations to
run.
##
Reply to this email directly or view it on GitHub:
https://gist.github.com/362873
|
This comment has been minimized.
This comment has been minimized.
Anyone is really welcome to upgrade the script :) |
This comment has been minimized.
This comment has been minimized.
Is there a reason you put the app into maintenance mode when pushing? |
This comment has been minimized.
This comment has been minimized.
@jksilliman - i think you could test if the migrations have changed by doing
and then decide whether you wanted to put it into maintenance mode during the push. |
This comment has been minimized.
This comment has been minimized.
Hi cj, no it isn't and you haven't (if you see the deploy_staging and deploy_production tasks, they are without migrations).
"rake deploy_staging"
"rake deploy_production"
Please note that this process is safe in every moment. For instance:
Hope this helps.. Bye, |
This comment has been minimized.
This comment has been minimized.
For the Cedar stack, you need to modify the deploy:migrate task as follows: task :migrate do Note the word run inserted between "heroku" and "rake". |
This comment has been minimized.
This comment has been minimized.
Hey, thanks for the nice gist. I stumbled upon a problem with new bundler 1.2.0.pre. (I'm using rbenv for ruby 1.9.3 installing). When I try the run heroku commands from within the rake task, I got an bundler error: our Ruby version is 1.9.2, but your Gemfile specified 1.9.3. I know, that is not problem specific to this gist, but maybe you have an idea to solve this? Best, |
This comment has been minimized.
This comment has been minimized.
Very convenient, thanks for sharing! |
This comment has been minimized.
This comment has been minimized.
thank you :) |
This comment has been minimized.
This comment has been minimized.
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 |
This comment has been minimized.
This comment has been minimized.
here are my mods if you're curious: https://gist.github.com/4410411 |
This comment has been minimized.
This comment has been minimized.
I try running heroku run rake deploy but i get an error on my app: Would you know what should I do to fix this ? |
This comment has been minimized.
This comment has been minimized.
Made a few abstractions, additions, protections https://gist.github.com/jphenow/5694169 |
This comment has been minimized.
This comment has been minimized.
Extracting the tasks into a class for easier extension: https://gist.github.com/jfeaver/9820478 Thanks for the great foundation work @njvitto! |
This comment has been minimized.
This comment has been minimized.
I added Rspec testing, improved Heroku toolbelt support, and post-deploy rake task running on my fork. |
This comment has been minimized.
This comment has been minimized.
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. |
This comment has been minimized.
You don't need to restart manually after a push, nor do you need to turn on/off maintenance mode. Heroku will auto-restart your app servers upon deploy.