Created
March 27, 2014 22:28
-
-
Save jfeaver/9820478 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Deploy and rollback on Heroku in staging and production | |
class RakeHerokuDeployer | |
def initialize app_env | |
@app = ENV["#{app_env.to_s.upcase}_APP"] | |
end | |
def run_migrations | |
push; turn_app_off; migrate; restart; turn_app_on; tag; | |
end | |
def deploy | |
push; restart; tag; | |
end | |
def rollback | |
turn_app_off; push_previous; restart; turn_app_on; | |
end | |
private | |
def push | |
current_branch = `git rev-parse --abbrev-ref HEAD`.chomp | |
branch_to_branch = (current_branch.length > 0) ? "#{current_branch}:master" : "" | |
puts 'Deploying site to Heroku ...' | |
puts "git push -f [email protected]:#{@app}.git #{branch_to_branch}" | |
puts `git push -f [email protected]:#{@app}.git #{branch_to_branch}` | |
end | |
def restart | |
puts 'Restarting app servers ...' | |
Bundler.with_clean_env { puts `heroku restart --app #{@app}` } | |
end | |
def tag | |
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 [email protected]:#{@app}.git` | |
end | |
def migrate | |
puts 'Running database migrations ...' | |
Bundler.with_clean_env { puts `heroku run 'bundle exec rake db:migrate' --app #{@app}` } | |
end | |
def turn_app_off | |
puts 'Putting the app into maintenance mode ...' | |
Bundler.with_clean_env { puts `heroku maintenance:on --app #{@app}` } | |
end | |
def turn_app_on | |
puts 'Taking the app out of maintenance mode ...' | |
Bundler.with_clean_env { puts `heroku maintenance:off --app #{@app}` } | |
end | |
def push_previous | |
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 [email protected]:#{@app}.git :refs/tags/#{previous_release}` | |
puts "Pushing '#{previous_release}' to Heroku master ..." | |
puts `git push [email protected]:#{@app}.git +#{previous_release}:master --force` | |
puts "Deleting rollbacked release '#{current_release}' ..." | |
puts `git tag -d #{current_release}` | |
puts `git push [email protected]:#{@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 [email protected]:#{@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 | |
namespace :deploy do | |
namespace :staging do | |
task :migrations do | |
deployer = RakeHerokuDeployer.new(:staging) | |
deployer.run_migrations | |
end | |
task :rollback do | |
deployer = RakeHerokuDeployer.new(:staging) | |
deployer.rollback | |
end | |
end | |
task :staging do | |
deployer = RakeHerokuDeployer.new(:staging) | |
deployer.deploy | |
end | |
namespace :production do | |
task :migrations do | |
deployer = RakeHerokuDeployer.new(:production) | |
deployer.run_migrations | |
end | |
task :rollback do | |
deployer = RakeHerokuDeployer.new(:production) | |
deployer.rollback | |
end | |
end | |
task :production do | |
deployer = RakeHerokuDeployer.new(:production) | |
deployer.deploy | |
end | |
end | |
Sorry that I missed your questions @supremebeing7! May and June were crazy months this past year and the email notification must have been lost in the storm.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is really cool. Thanks!
Sorry if this is a noob question, but where is
app_env
coming from on your initialize method? Everything runs fine when I set my env variable explicitlyENV['MY_APP']
but not how you have it withapp_env
.EDIT: I think I see where it's coming from -
deployer = RakeHerokuDeployer.new(:production)
- but how do I specify what gets passed in as:production
? For example, on the below code, what would the command be to pass in the app name as:production
? I triedrake deploy:production MY_APP_NAME
but that didn't do it.EDIT 2:
Nevermind, I got it. Rookie mistake. Realized it was just taking the
:production
param and converting it to PRODUCTION, then using that in the ENV asENV['PRODUCTION_APP']