-
-
Save sseletskyy/9248357 to your computer and use it in GitHub Desktop.
improved heroku deploy rake task
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, production, dev and test environments | |
require 'tempfile' | |
# for capturing error in 'git push' | |
def capture_stderr | |
stderr = $stderr.dup | |
Tempfile.open 'stderr-redirect' do |temp| | |
$stderr.reopen temp.path, 'w+' | |
yield if block_given? | |
$stderr.reopen stderr | |
temp.read | |
end | |
end | |
%w[dev test production staging].each do |app| | |
desc "Deploy to #{app}" | |
task "deploy:#{app}" => %W[deploy:set_#{app}_app deploy:push deploy:restart deploy:tag] | |
desc "Deploy #{app} with migrations" | |
task "deploy:#{app}:migrations" => %W[deploy:set_#{app}_app deploy:push deploy:off deploy:migrate deploy:restart deploy:on deploy:tag] | |
desc "Rollback #{app}" | |
task "#{app}:rollback" => ["set_#{app}_app".to_sym, :off, :push_previous, :restart, :on] | |
%w[logs config ps releases console scheduler init_db].each do |app_task| | |
desc "#{app} #{app_task}" | |
task "app:#{app}:#{app_task}" => ["deploy:set_#{app}_app", "app:#{app_task}"] | |
end | |
end | |
namespace :deploy do | |
PRODUCTION_APP = 'YOUR_PRODUCTION_APP_NAME_ON_HEROKU' | |
STAGING_APP = 'YOUR_STAGING_APP_NAME_ON_HEROKU' | |
TEST_APP = 'YOUR_TEST_APP_NAME_ON_HEROKU' | |
DEV_APP = 'YOUR_DEVELOPMENT_APP_NAME_ON_HEROKU' | |
# metaprogramming | |
%w[dev test production staging].each do |app| | |
task "set_#{app}_app" do | |
# a trick to read a constant | |
const_value = Object.const_get(app.upcase+'_APP') | |
if !const_value || const_value.empty? || const_value == "YOUR_#{app.upcase}_APP_NAME_ON_HEROKU" | |
raise "APP #{app.upcase} IS NOT SET" | |
end | |
APP = const_value | |
end | |
end | |
task :push do | |
git_status = `git status -v` | |
abort("local branch is out of sync with github origin. please push there first") if git_status.match(/\*[^\]]+?\[ahead|behind/s) != nil | |
current_branch = `git rev-parse --abbrev-ref HEAD`.chomp | |
current_branch += ":master" if current_branch != "master" | |
puts "Deploying #{current_branch} to #{APP} ..." | |
#captured_content = capture_stderr do | |
puts `git push -f [email protected]:#{APP}.git #{current_branch}` | |
#end | |
# display captured content | |
#puts captured_content.to_s | |
# stop furthe task chain in case of error (e.g. app name is invalid) | |
#raise "EXIT" if captured_content.to_s.match(/fatal/) | |
end | |
task :restart do | |
puts 'Restarting app servers ...' | |
Bundler.with_clean_env do | |
puts `heroku restart --app #{APP}` | |
end | |
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 [email protected]:#{APP}.git` | |
puts `git push --tags origin` | |
end | |
task :migrate do | |
puts 'Running database migrations ...' | |
Bundler.with_clean_env do | |
puts `heroku run rake db:migrate --app #{APP}` | |
end | |
end | |
task :off do | |
puts 'Putting the app into maintenance mode ...' | |
Bundler.with_clean_env do | |
puts `heroku maintenance:on --app #{APP}` | |
end | |
end | |
task :on do | |
puts 'Taking the app out of maintenance mode ...' | |
Bundler.with_clean_env do | |
puts `heroku maintenance:off --app #{APP}` | |
end | |
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 [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 :app do | |
task :logs do | |
puts "tailing logs..." | |
Bundler.clean_exec "heroku logs --tail --app #{APP}" | |
end | |
task :config do | |
puts "config..." | |
Bundler.with_clean_env do | |
puts `heroku config --app #{APP}` | |
end | |
end | |
task :console do | |
puts "console..." | |
Bundler.with_clean_env do | |
sh "heroku run rails c --app #{APP}" | |
end | |
end | |
task :scheduler do | |
puts "scheduler..." | |
Bundler.with_clean_env do | |
sh "heroku addons:open scheduler --app #{APP}" | |
end | |
end | |
task :ps do | |
puts "running processes..." | |
Bundler.with_clean_env do | |
puts `heroku ps --app #{APP}` | |
end | |
end | |
task :releases do | |
puts "releases..." | |
Bundler.with_clean_env do | |
puts `heroku releases --app #{APP}` | |
end | |
end | |
task :init_db do | |
puts "init database" | |
Bundler.with_clean_env do | |
puts `heroku maintenance:on --app #{APP}` | |
puts `heroku run rake db:schema:load --app #{APP}` | |
puts `heroku run rake db:migrate --app #{APP}` | |
puts `heroku run rake db:seed --app #{APP}` | |
puts `heroku maintenance:off --app #{APP}` | |
end | |
end | |
end |
use pty to monitor git push and stop further tasks in case of error (it helps to avoid tagging in case of error during deployment)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
added environments QA and DEV