Skip to content

Instantly share code, notes, and snippets.

@rafaelp
Created December 24, 2011 01:39
Show Gist options
  • Save rafaelp/1515889 to your computer and use it in GitHub Desktop.
Save rafaelp/1515889 to your computer and use it in GitHub Desktop.
Rake task to migrate an application from bamboo stack do cedar stack on Heroku
# -*- encoding : utf-8 -*-
# Rafael Lima (http://rafael.adm.br)
# License: http://creativecommons.org/licenses/by/2.5/
#
# ATTENTION: This is a initial work, use it at your own risk!
#
# Usage:
# $ APP_NAME=myappname rake heroku:migrate_from_bamboo_to_cedar
namespace :heroku do
def run(*cmd)
system(*cmd)
raise "Command #{cmd.inspect} failed!" unless $?.success?
end
def confirm(message, abort=true)
print "\n#{message}\nAre you sure? [yN] "
confirmed = STDIN.gets.chomp.downcase == 'y'
raise 'Aborted' if abort and !confirmed
confirmed
end
def step(message)
return unless confirm(message, false)
print "-----> #{message}…"
yield
puts " OK"
end
task :migrate_from_bamboo_to_cedar => :environment do
current_app = ENV['APP_NAME']
raise "You must set environment variable APP_NAME. Try running: APP_NAME=myappname rake heroku:migrate_from_bamboo_to_cedar" if ENV['APP_NAME'].nil?
new_app = "#{current_app}-new"
old_app = "#{current_app}-old"
step "Checking current stack" do
ret = `heroku stack --app #{current_app} |grep '*'`
stack = ret.strip.gsub("* ", "")
raise "Current application should be running on bamboo-mri-1.9.2 to be migrated but it ir running on #{stack}. More info at: http://devcenter.heroku.com/articles/cedar#migrating_from_bamboo_to_cedar" if stack != "bamboo-mri-1.9.2"
end
step "Checking log configuration" do
ret = `heroku config -s --app #{current_app} |grep RACK_ENV`
env = ret.strip.split("=")[1]
file_name = Rails.root.join('config','environments',"#{env}.rb")
raise "Environment file (#{file_name}) not found" unless File.exists?(file_name)
ret = `cat #{file_name} |grep config.logger`
lines = ret.split("\n")
ocurrences = lines.map do |line|
line.strip[0] != "#"
end
raise "Environment file (#{file_name}) should not have config.logger directive. More info at: https://github.com/ddollar/rails_log_stdout/issues/4#issuecomment-2512778" if ocurrences.include?(true)
end
step "Checking mailer configuration" do
ret = `heroku addons --app #{current_app} |grep sendgrid`
if !ret.strip.blank?
ret = `heroku config -s --app #{current_app} |grep RACK_ENV`
env = ret.strip.split("=")[1]
file_name = Rails.root.join('config','environments',"#{env}.rb")
raise "Environment file (#{file_name}) not found" unless File.exists?(file_name)
ret = `cat #{file_name} |grep config.action_mailer.smtp_settings`
lines = ret.split("\n")
ocurrences = lines.map do |line|
line.strip[0] != "#"
end
raise "You have sendgrid addon and the environment file (#{file_name}) don't have the configuration of action_mailer. More info at: http://devcenter.heroku.com/articles/sendgrid#usage" if ocurrences.empty? or !ocurrences.include?(true)
end
end
step "Checking SSL" do
ret = `heroku ssl --app #{current_app} |grep 'SSL certificate registered'`
if ret.strip != ""
domain = /(.*)\shas a SSL certificate registered/.match(ret.strip)[1]
confirm("As you have SSL, you will have to change the CNAME value for the domain #{domain} and your application will be offline for a while until DNS propagate.")
pem_file_name = Rails.root.join("config/certs/#{domain}.pem")
key_file_name = Rails.root.join("config/certs/#{domain}.key")
raise "Current application has SSL certificate, you will need the PEM file at #{pem_file_name} to finish the upgrade." unless File.exists?(pem_file_name)
raise "Current application has SSL certificate, you will need the KEY file at #{key_file_name} to finish the upgrade." unless File.exists?(key_file_name)
end
end
step "Looking for Procfile" do
raise "This project doesn't have Procfile. Read more at: http://devcenter.heroku.com/articles/rails3#webserver" unless File.exists?(Rails.root.join("Procfile"))
ret = `git blame Procfile`
raise "Procfile is not commited" unless ret.strip != "fatal: cannot stat path 'Procfile': No such file or directory"
end
step "Creating #{new_app}" do
ret = `heroku apps |grep #{new_app}`
if ret.strip != "#{new_app}"
run("heroku create #{new_app} --stack cedar")
end
end
step "Installing addons" do
ret = `heroku addons --app #{current_app}`
current_addons = ret.split("\n").map do |e| e.split(" => ")[0] end
ret = `heroku addons --app #{new_app}`
new_addons = ret.split("\n").map do |e| e.split(" => ")[0] end
addons_to_install = current_addons - new_addons
print " #{addons_to_install.inspect}"
addons_to_install.each do |addon|
puts "ATTENTION: Addon #{addon} is deprecated and need to be replaced!" if ["cron:hourly", "ssl:hostname"].include?(addon)
if ["logging:expanded"].include?(addon)
`heroku addons:upgrade #{addon} --app #{new_app}`
else
`heroku addons:add #{addon} --app #{new_app}`
end
end
end
step "Promoting database" do
print "Wait for database to be created. What is the code of database (Ex: HEROKU_POSTGRESQL_RED)?"
database_code = STDIN.gets.chomp.strip.upcase
run "heroku pg:promote #{database_code} --app #{new_app}"
end
# Only copy variables wich does not exists in new app
step "Copying environment variables" do
ret = `heroku config -s --app #{current_app}`
current_vars = ret.split("\n")
current_pairs = {}
current_keys = []
current_vars.each do |var|
key = var.split("=")[0]
value = var.split("=")[1]
current_pairs[key] = value
current_keys << key
end
ret = `heroku config -s --app #{new_app}`
new_vars = ret.split("\n")
new_pairs = {}
new_keys = []
new_vars.each do |var|
key = var.split("=")[0]
value = var.split("=")[1]
new_pairs[key] = value
new_keys << key
end
configs_to_add = current_keys - new_keys
print " #{configs_to_add.inspect}"
configs = []
configs_to_add.each do |config|
configs << %{#{config}="#{current_pairs[config]}"}
end
if !configs.empty?
`heroku config:add #{configs.join(' ')} --app #{new_app}`
end
end
step "Set RACK_ENV and RAILS_ENV config vars" do
ret = `heroku config -s --app #{current_app} |grep RACK_ENV`
env = ret.strip.split("=")[1]
run("heroku config:add RACK_ENV=#{env} RAILS_ENV=#{env} --app #{new_app}")
end
step "Pushing application to the new stack" do
run "git push [email protected]:#{new_app}.git HEAD:master -f"
end
step "Setting Dynos" do
ret = `heroku dynos --app #{current_app}`
dynos = /.* is running (\d*) dyno/.match(ret.strip)[1].to_i
if dynos > 1
run("heroku scale web=#{dynos} --app #{new_app}")
end
end
step "Setting Workers" do
ret = `heroku workers --app #{current_app}`
workers = /.* is running (\d*) worker/.match(ret.strip)[1].to_i
if workers > 0
run("heroku scale worker=#{workers} --app #{new_app}")
end
end
step "Putting current application in maintenance mode" do
run "heroku maintenance:on --app #{current_app}"
end
step "Copying database between applications" do
run("heroku pgbackups:capture --app #{current_app} --expire")
run("heroku pgbackups:restore DATABASE `heroku pgbackups:url --app #{current_app}` --app #{new_app} --confirm #{new_app}")
run("heroku run rake db:migrate --app #{new_app}")
run("heroku restart --app #{new_app}")
end
step "Copying domains between applications" do
ret = `heroku ssl --app #{current_app} |grep 'SSL certificate registered'`
if ret.strip != ""
ssl_domain = /(.*)\shas a SSL certificate registered/.match(ret.strip)[1]
else
ssl_domain = nil
end
ret = `heroku domains --app #{current_app}`
domains = ret.split("\n")
domains.shift
run("heroku config:add DOMAINS=#{domains.to_json} --app #{new_app}") # just to backup
domains.each do |domain|
run("heroku domains:remove #{domain} --app #{current_app}")
run("heroku domains:add #{domain} --app #{new_app}")
end
if ssl_domain
pem_file_name = Rails.root.join("config/certs/#{ssl_domain}.pem")
key_file_name = Rails.root.join("config/certs/#{ssl_domain}.key")
run("heroku ssl:add #{pem_file_name} #{key_file_name} --app #{new_app}")
end
end
step "Rename applications" do
run("heroku rename #{old_app} --app #{current_app}")
run("heroku rename #{current_app} --app #{new_app}")
end
step "Restarting new application" do
run("heroku restart --app #{current_app}")
end
puts "Adjust repositories in your .git/config"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment