k3_capistrano 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Rakefile +1 -0
- data/Readme.md +86 -0
- data/k3_capistrano.gemspec +30 -0
- data/lib/capistrano_database_yml.rb +142 -0
- data/lib/k3/capistrano.rb +2 -0
- data/lib/k3_capistrano.rb +5 -0
- data/lib/k3_capistrano/asset_pipeline.rb +316 -0
- data/lib/k3_capistrano/capistrano.rb +132 -0
- data/lib/k3_capistrano/chef.rb +41 -0
- data/lib/k3_capistrano/db.rb +72 -0
- data/lib/k3_capistrano/db_backups.rb +207 -0
- data/lib/k3_capistrano/ensure_db_is_set_up.rb +34 -0
- data/lib/k3_capistrano/git.rb +37 -0
- data/lib/k3_capistrano/logs.rb +20 -0
- data/lib/k3_capistrano/ssh.rb +14 -0
- data/lib/k3_capistrano/test_request.rb +16 -0
- data/lib/k3_capistrano/test_results.rb +85 -0
- data/lib/k3_capistrano/unicorn.rb +30 -0
- data/lib/k3_capistrano/version.rb +5 -0
- data/lib/templates/database.yml.erb +8 -0
- data/lib/test_result_logging.rb +79 -0
- metadata +164 -0
@@ -0,0 +1,132 @@
|
|
1
|
+
# This is the main file that you should require. It loads everything except k3_capistrano/test_request.
|
2
|
+
|
3
|
+
require 'k3_capistrano/version'
|
4
|
+
require 'bundler/capistrano'
|
5
|
+
require 'capistrano/ext/multistage' # Note: This currently requires the capistrano-ext gem
|
6
|
+
require 'capistrano_colors'
|
7
|
+
|
8
|
+
#===================================================================================================
|
9
|
+
Capistrano::Configuration::Namespaces::Namespace.class_eval do
|
10
|
+
def capture(*args)
|
11
|
+
parent.capture *args
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
Capistrano::Configuration.instance(:must_exist).load do
|
16
|
+
|
17
|
+
#===================================================================================================
|
18
|
+
|
19
|
+
set :scm, 'git'
|
20
|
+
_cset :scm_verbose, false
|
21
|
+
#set :git_shallow_clone, 1
|
22
|
+
|
23
|
+
current_branch = `git symbolic-ref HEAD | sed s#^refs/heads/##`.chomp
|
24
|
+
#current_branch = `git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/'`.chomp
|
25
|
+
_cset :branch, ENV['branch'] || current_branch
|
26
|
+
puts "Warning: Deploying branch '#{branch}'" unless branch == 'master'
|
27
|
+
|
28
|
+
# Deploying to a '/u/' directory seems silly. Set a more reasonable default here.
|
29
|
+
if deploy_to.start_with? '/u/'
|
30
|
+
#_cset :deploy_to, '~'
|
31
|
+
set :deploy_to, "/apps/#{application}"
|
32
|
+
end
|
33
|
+
|
34
|
+
#set :deploy_via, :remote_cache
|
35
|
+
_cset :deploy_via, :export
|
36
|
+
_cset :rails_env, 'production'
|
37
|
+
|
38
|
+
_cset :user, application
|
39
|
+
_cset :use_sudo, false
|
40
|
+
|
41
|
+
# We can always use this for the application user, even if user is unset.
|
42
|
+
# Question: Where is app_user used?
|
43
|
+
#_cset :app_user, user
|
44
|
+
|
45
|
+
# Skip bundling cucumber and other non-production environments
|
46
|
+
_cset :bundle_without, [:development, :test, :cucumber]
|
47
|
+
|
48
|
+
# Get rid of annoying zlib error message at end of deploy. I can't imagine compression is helping much...
|
49
|
+
ssh_options[:compression] = "none"
|
50
|
+
|
51
|
+
# Enable this if you run into problems with the bundle command hanging
|
52
|
+
default_run_options[:pty] = true
|
53
|
+
|
54
|
+
set :skip, ENV['skip'] ? ENV['skip'].split(',') : []
|
55
|
+
|
56
|
+
#===================================================================================================
|
57
|
+
# Stages
|
58
|
+
|
59
|
+
_cset :stages, %w(staging production)
|
60
|
+
_cset :default_stage, "staging"
|
61
|
+
|
62
|
+
#===================================================================================================
|
63
|
+
require 'rvm/capistrano'
|
64
|
+
|
65
|
+
set :rvm_type, :system
|
66
|
+
|
67
|
+
#===================================================================================================
|
68
|
+
# Triggers
|
69
|
+
|
70
|
+
before 'deploy', 'git:check_if_needs_push'
|
71
|
+
before 'deploy:update_code', 'db:read_remote_my_cnf'
|
72
|
+
before 'deploy:update_code', 'db:database_yml:setup'
|
73
|
+
after 'deploy:update_code', 'deploy:ensure_db_is_set_up'
|
74
|
+
after 'deploy:update_code', 'deploy:migrate' unless skip.include?('migrate') || ARGV.include?('deploy:update_code')
|
75
|
+
|
76
|
+
#===================================================================================================
|
77
|
+
# Problem: release_path is set to a new path each time you run cap, and usually that path doesn't exist.
|
78
|
+
# It only exists after doing a deploy.
|
79
|
+
# TODO: Can we detect whether it exists? Like this: capture("ls -1 #{release_path} #{current_path}").split.first
|
80
|
+
# (Possibly not because release_or_current_path might get called too early and thus release_path would never exist by that point in the deploy.)
|
81
|
+
if ARGV.include? 'deploy'
|
82
|
+
# If deploying, use release_path, which *will* become current_path, but will not be symlinked from current until the deploy is done
|
83
|
+
set :release_or_current_path, -> { release_path }
|
84
|
+
else
|
85
|
+
# If not deploying, use current_path, which is the path of the last/current deploy. release_path wouldn't even exist or make sense in this case.
|
86
|
+
set :release_or_current_path, -> { current_path }
|
87
|
+
end
|
88
|
+
#puts %(release_or_current_path=#{(release_or_current_path).inspect})
|
89
|
+
|
90
|
+
#===================================================================================================
|
91
|
+
# Common tasks
|
92
|
+
|
93
|
+
_cset :http_server, 'unicorn' # Valid options: 'unicorn', 'passenger'
|
94
|
+
require 'k3_capistrano/unicorn'
|
95
|
+
|
96
|
+
require 'k3_capistrano/ensure_db_is_set_up'
|
97
|
+
require 'k3_capistrano/db'
|
98
|
+
require 'k3_capistrano/db_backups'
|
99
|
+
require 'k3_capistrano/git'
|
100
|
+
require 'k3_capistrano/ssh'
|
101
|
+
require 'k3_capistrano/logs'
|
102
|
+
require 'k3_capistrano/asset_pipeline'
|
103
|
+
require 'k3_capistrano/test_results'
|
104
|
+
|
105
|
+
task :uname do
|
106
|
+
run "uname -a"
|
107
|
+
end
|
108
|
+
|
109
|
+
#===================================================================================================
|
110
|
+
|
111
|
+
def random_password
|
112
|
+
alphanumerics = [('0'..'9'),('A'..'Z'),('a'..'z')].map {|range| range.to_a}.flatten
|
113
|
+
(0...25).map { alphanumerics[Kernel.rand(alphanumerics.size)] }.join
|
114
|
+
end
|
115
|
+
|
116
|
+
def exec_verbose(command)
|
117
|
+
puts command
|
118
|
+
exec command
|
119
|
+
end
|
120
|
+
|
121
|
+
def system_verbose(command)
|
122
|
+
puts command
|
123
|
+
system command
|
124
|
+
end
|
125
|
+
|
126
|
+
def beep
|
127
|
+
STDOUT.print "#{7.chr}"
|
128
|
+
end
|
129
|
+
|
130
|
+
#===================================================================================================
|
131
|
+
|
132
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# Chef integration
|
2
|
+
# Reuses as much configuration data about the app from the app data bag as possible, so that the
|
3
|
+
# app's configuration is only defined once, in one canonical location.
|
4
|
+
|
5
|
+
gem 'capistrano-ext'
|
6
|
+
gem 'tylerrick-chef'
|
7
|
+
gem 'capistrano-chef'
|
8
|
+
|
9
|
+
Capistrano::Configuration.instance(:must_exist).load do
|
10
|
+
|
11
|
+
require 'chef'
|
12
|
+
ENV['chef_home'] or abort("Please set the chef_home environment variable to point to your copy of K3's deployment/kitchen/")
|
13
|
+
Chef::Config[:data_bag_path] = ENV['chef_home'] + '/data_bags'
|
14
|
+
Chef::Config[:solo] = true
|
15
|
+
require 'capistrano/chef'
|
16
|
+
set_from_data_bag
|
17
|
+
|
18
|
+
# Note: If you require 'rvm/capistrano', make sure you do that *after* requiring
|
19
|
+
# 'k3_capistrano/chef' because the 'rvm' namespace that 'rvm/capistrano' creates would shadow the
|
20
|
+
# 'rvm' variables that set_from_data_bag would create.
|
21
|
+
|
22
|
+
# Some variables from the data bag have different options for staging/production, so we set the
|
23
|
+
# corresponding cap variables here but since we know whether we're on staging/production, we only
|
24
|
+
# grab the appropriate value for the server we're deploying to instead of the full hash...
|
25
|
+
set :rails_env_from_data_bag, rails_env; unset :rails_env
|
26
|
+
_cset :domain, ->{ server_name[stage.to_s] or server_name or raise("server_name should have been set from app data bag but was not defined") }
|
27
|
+
set :database, ->{ databases[stage.to_s] or raise("databases have been set from app data bag but was not defined") }
|
28
|
+
_cset :rails_env,->{ rails_env_from_data_bag[stage.to_s] or rails_env_from_data_bag or raise("rails_env have been set from app data bag but was not defined") }
|
29
|
+
|
30
|
+
# If we wanted to set the revision/branch from the data_bag as well, we could do something like this...
|
31
|
+
#set :revision_from_data_bag, revision; unset :revision
|
32
|
+
#set :revision, ->{ revision_from_data_bag[stage.to_s] or revision_from_data_bag or raise("revision was not defined") }
|
33
|
+
|
34
|
+
set :rvm_type, :system
|
35
|
+
rvm['ruby_string'] or fail("rvm['ruby_string'] was nil. Please make sure you aren't requiring 'rvm/capistrano' before 'k3/capistrano'. rvm=#{rvm.inspect}")
|
36
|
+
set :rvm_ruby_string, rvm['ruby_string']
|
37
|
+
set :user, owner
|
38
|
+
|
39
|
+
set :repository, "#{customer}@git.k3integrations.com:#{application}.git"
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
Capistrano::Configuration.instance(:must_exist).load do
|
4
|
+
|
5
|
+
_cset :database, {
|
6
|
+
'adapter' => "mysql2",
|
7
|
+
'host' => "localhost",
|
8
|
+
'database' => application,
|
9
|
+
'username' => application,
|
10
|
+
'reconnect' => "true",
|
11
|
+
'encoding' => "utf8",
|
12
|
+
}
|
13
|
+
|
14
|
+
namespace :db do
|
15
|
+
# Adapted from deploy:migrate task in capistrano-2.12.0/lib/capistrano/recipes/deploy.rb
|
16
|
+
desc <<-DESC
|
17
|
+
Run the db:reset rake task. By default, it runs this in most recently \
|
18
|
+
deployed version of the app. However, you can specify a different release \
|
19
|
+
via the migrate_target variable, which must be one of :latest (for the \
|
20
|
+
default behavior), or :current (for the release indicated by the \
|
21
|
+
`current' symlink). Strings will work for those values instead of symbols, \
|
22
|
+
too. You can also specify additional environment variables to pass to rake \
|
23
|
+
via the migrate_env variable. Finally, you can specify the full path to the \
|
24
|
+
rake executable by setting the rake variable. The defaults are:
|
25
|
+
|
26
|
+
(Note: We should actually be using `rake db:setup` instead of `rake db:reset` but `rake db:setup`
|
27
|
+
currently fails with "db/schema.rb doesn't exist yet" error due to
|
28
|
+
https://github.com/rails/rails/issues/4772. The main difference is that `rake db:reset` does a drop
|
29
|
+
first.)
|
30
|
+
|
31
|
+
|
32
|
+
set :rake, "rake"
|
33
|
+
set :rails_env, "production"
|
34
|
+
set :migrate_env, ""
|
35
|
+
set :migrate_target, :latest
|
36
|
+
DESC
|
37
|
+
task :setup, :roles => :db, :only => { :primary => true } do
|
38
|
+
rake = fetch(:rake, "rake")
|
39
|
+
rails_env = fetch(:rails_env, "production")
|
40
|
+
migrate_env = fetch(:migrate_env, "")
|
41
|
+
migrate_target = fetch(:migrate_target, :latest)
|
42
|
+
|
43
|
+
directory = case migrate_target.to_sym
|
44
|
+
when :current then current_path
|
45
|
+
when :latest then latest_release
|
46
|
+
else raise ArgumentError, "unknown migration target #{migrate_target.inspect}"
|
47
|
+
end
|
48
|
+
|
49
|
+
run "cd #{directory} && #{rake} RAILS_ENV=#{rails_env} #{migrate_env} db:reset"
|
50
|
+
end
|
51
|
+
|
52
|
+
task :read_remote_my_cnf, :roles => :app do
|
53
|
+
path = "#{deploy_to}/.my.cnf"
|
54
|
+
raw = capture("cat #{path}")
|
55
|
+
config = nil
|
56
|
+
Tempfile.new('remote_my_cnf').tap do |file|
|
57
|
+
file.write raw
|
58
|
+
file.close
|
59
|
+
config = ParseConfig.new(file.path)
|
60
|
+
end
|
61
|
+
(config.params['client']['password'] rescue nil) or raise "Couldn't find password in #{path}"
|
62
|
+
set :my_cnf, config.params
|
63
|
+
end
|
64
|
+
|
65
|
+
task :list_tables, on_error: :continue do
|
66
|
+
tables = capture(%(echo "show tables" | mysql #{database['database']})).cleanlines
|
67
|
+
#puts %(tables=#{tables.to_a.inspect})
|
68
|
+
tables
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
# Database backup/restore
|
2
|
+
|
3
|
+
Capistrano::Configuration.instance(:must_exist).load do
|
4
|
+
|
5
|
+
_cset :db_backups_dir, ->{ "#{shared_path}/db_backups" }
|
6
|
+
|
7
|
+
# Lets us expand ~'s so sftp won't complain
|
8
|
+
set :home_dir, -> { capture('echo $HOME') }
|
9
|
+
|
10
|
+
namespace :backup do
|
11
|
+
|
12
|
+
desc "Back up the database"
|
13
|
+
task :create, :roles => :db, :only => { :primary => true }, on_error: :continue do
|
14
|
+
run "#{shared_path}/script/back_up_db"
|
15
|
+
backup.locate_last
|
16
|
+
end
|
17
|
+
|
18
|
+
task :list do
|
19
|
+
run("ls -t #{db_backups_dir}")
|
20
|
+
end
|
21
|
+
task :_list do
|
22
|
+
capture("ls -1t #{db_backups_dir}")
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "Locate the latest backup"
|
26
|
+
task :locate_last do
|
27
|
+
last = "#{db_backups_dir}/" + backup._list.cleanlines.grep(/.*\.sql/).first
|
28
|
+
backup_details = capture("ls -alh #{last}")
|
29
|
+
puts ' '*6 + "Last backup is: #{last}"
|
30
|
+
puts ' '*6 + "#{backup_details}"
|
31
|
+
last
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "Download the latest backup"
|
35
|
+
task :download do
|
36
|
+
set :which_backup, backup.locate_last unless exists?(:which_backup)
|
37
|
+
which_backup = capture("echo #{which_backup()}").chomp # expand ~'s so sftp won't complain
|
38
|
+
get which_backup, "#{local_dir}/#{File.basename(which_backup)}"
|
39
|
+
end
|
40
|
+
|
41
|
+
desc <<-End
|
42
|
+
Upload a database backup file to the remote host. Specify filename with file=.
|
43
|
+
End
|
44
|
+
task :upload_backup, :roles => :db do
|
45
|
+
file = ENV['file'] or raise(ArgumentError, "file must be specified")
|
46
|
+
upload file db_backups_dir.gsub('~', home_dir)
|
47
|
+
end
|
48
|
+
|
49
|
+
desc <<-End
|
50
|
+
Restore a database backup file.
|
51
|
+
The backup to restore must be specified by the which_backup variable and must already exist on the server.
|
52
|
+
End
|
53
|
+
task :restore, :roles => :db do
|
54
|
+
fetch(:which_backup) or raise(ArgumentError, "which_backup must be specified")
|
55
|
+
run "mysql #{application} < #{which_backup}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
#_cset :local_dir, ->{ "#{File.dirname(__FILE__)}/../" + "db/backups/production" }
|
60
|
+
#namespace :backup do
|
61
|
+
# # Tasks for downloading a database snapshot from production and loading into localhost DB.
|
62
|
+
# namespace :local do
|
63
|
+
#
|
64
|
+
# desc "Create a new backup/snapshot of production database and populate dev database with it."
|
65
|
+
# task :from_new_backup, :roles => :db do
|
66
|
+
# puts ' '*6 + 'Current path: ' + current_path
|
67
|
+
# backup.create
|
68
|
+
# local.from_latest_backup
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# desc "Populate the database with contents of your latest backup on the server."
|
72
|
+
# task :from_latest_backup, :roles => :db do
|
73
|
+
# set :which_backup, backup.locate_last
|
74
|
+
# from_backup
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# desc "Populate the database with contents of a backup (database dump)."
|
78
|
+
# task :from_backup, :roles => :db do
|
79
|
+
# local_backup_file = Dir["#{local_dir}/#{File.basename(which_backup)}"].to_a.first
|
80
|
+
# puts "Populating local database from '#{local_backup_file}'..."
|
81
|
+
# if local_backup_file
|
82
|
+
# # Note: rails db seems to ignore file input specified with <
|
83
|
+
# #command = "rails db -p < #{local_backup_file}"
|
84
|
+
# # so I just made a ./script/dbconsole file instead which invokes mysql client directly.
|
85
|
+
# command = "./script/dbconsole < #{local_backup_file}"
|
86
|
+
# puts command; system command
|
87
|
+
# else
|
88
|
+
# backup.download
|
89
|
+
# # Try again now (recursive) that we've downloaded the file
|
90
|
+
# local.from_backup
|
91
|
+
# end
|
92
|
+
# end
|
93
|
+
# end
|
94
|
+
#end
|
95
|
+
|
96
|
+
#---------------------------------------------------------------------------------------------------
|
97
|
+
# Taps (simple database import/export app)
|
98
|
+
_cset :local_rails_env, ->{ ENV['RAILS_ENV'] || 'development' }
|
99
|
+
|
100
|
+
=begin
|
101
|
+
Ideas for improvement:
|
102
|
+
|
103
|
+
Consider using https://github.com/hsume2/cap-taffy, which tries to solve the same problem.
|
104
|
+
They really do pretty much the same thing in a pretty similar way, but cap-taffy is a bit more advanced.
|
105
|
+
|
106
|
+
Advantages of cap-taffy:
|
107
|
+
* It starts a server and client with a single command (cap db:pull)
|
108
|
+
|
109
|
+
Advantages of current system:
|
110
|
+
* Zero-configuration. Reads all the connection info it needs from chef data bag, database.yml, etc.
|
111
|
+
cap-taffy appears to read database.yml for you, but does not parse it with eRuby and doesn't know about chef.
|
112
|
+
* uses bundle exec
|
113
|
+
|
114
|
+
Idea: Fork cap-taff. Refactor it to allow us to reuse most of it and simply pass connection string
|
115
|
+
(built from chef vars) and let cap-taffy handle the rest. It looks like we should be able to set
|
116
|
+
@remote_database_url somehow.
|
117
|
+
|
118
|
+
See https://github.com/hsume2/cap-taffy/blob/master/lib/cap-taffy/db.rb for the meat of cap-taffy.
|
119
|
+
=end
|
120
|
+
namespace :db do
|
121
|
+
desc <<-End
|
122
|
+
Start up a taps server on the server to let you pull the data from production database over to another host (such as localhost, for debugging with production data)
|
123
|
+
|
124
|
+
This uses your database config from your app's data bag item and password from .my.cnf to connect to the database.
|
125
|
+
|
126
|
+
You need to add 'taps' gem to your Gemfile and deploy your app first.
|
127
|
+
|
128
|
+
Then run:
|
129
|
+
[window 1] cap production db:taps_server
|
130
|
+
[window 2] cap production db:pull
|
131
|
+
|
132
|
+
You can also pull from one server to another:
|
133
|
+
[window 1] cap production db:taps_server
|
134
|
+
[window 2] cap staging db:pull_from_production
|
135
|
+
|
136
|
+
See https://github.com/ricardochimal/taps for more info.
|
137
|
+
End
|
138
|
+
task :taps_server, roles: :db do
|
139
|
+
# TODO: write pw to tmp/
|
140
|
+
http_user, http_password = application, random_password
|
141
|
+
File.open('tmp/taps_password', 'w') {|f| f.puts http_password }
|
142
|
+
run "cd #{current_path} && bundle exec taps server #{database['adapter']}://#{my_cnf['client']['user']}:#{my_cnf['client']['password']}@localhost/#{application}?encoding=#{database['encoding']} #{http_user} #{http_password}"
|
143
|
+
end
|
144
|
+
|
145
|
+
desc <<-End
|
146
|
+
Pull data from remote database to your local app database on localhost.
|
147
|
+
|
148
|
+
You'll need to start the taps server first with 'cap {stage} db:taps_server' and leave that running in another window.
|
149
|
+
|
150
|
+
This will connect to the host named by the domain variable (via an ssh tunnel since port 5000 is unlikely to be open), which depends on whether you run 'cap staging db:pull' or 'cap production db:pull'. This is where it will 'pull' the data from.
|
151
|
+
|
152
|
+
It will connect to your local app database using the connection info in your local config/database.yml. This is where the data gets pulled *to*.
|
153
|
+
End
|
154
|
+
task :pull do
|
155
|
+
http_user, http_password = application, ENV['password'] || File.read('tmp/taps_password').chomp
|
156
|
+
http_password.to_s.length > 1 or raise("could not read password")
|
157
|
+
ActiveRecord::Base.configurations = YAML::load(Erubis::Eruby.new(File.read('config/database.yml')).result)
|
158
|
+
configs = ActiveRecord::Base.configurations
|
159
|
+
database = configs[local_rails_env]
|
160
|
+
fork do
|
161
|
+
puts "Creating ssh tunnel..."
|
162
|
+
exec_verbose "ssh -N -L5000:127.0.0.1:5000 #{domain}"
|
163
|
+
end
|
164
|
+
system_verbose "bundle exec taps pull #{database['adapter']}://#{database['username']}:#{database['password']}@localhost/#{database['database']}?encoding=#{database['encoding']} " +
|
165
|
+
"http://#{http_user}:#{http_password}@localhost:5000"
|
166
|
+
puts "Done. Ctrl-C to exit ssh."
|
167
|
+
Process.wait
|
168
|
+
end
|
169
|
+
|
170
|
+
desc <<-End
|
171
|
+
Pull data from production database to staging database.
|
172
|
+
|
173
|
+
Usage: cap staging db:pull_to_staging
|
174
|
+
End
|
175
|
+
task :pull_to_staging do
|
176
|
+
production_domain = server_name['production']
|
177
|
+
|
178
|
+
http_user, http_password = application, ENV['password'] || File.read('tmp/taps_password').chomp
|
179
|
+
http_password.to_s.length > 1 or raise("could not read password")
|
180
|
+
remote_database_yml = capture("cat #{current_path}/config/database.yml")
|
181
|
+
|
182
|
+
ActiveRecord::Base.configurations = YAML::load(Erubis::Eruby.new(remote_database_yml).result)
|
183
|
+
configs = ActiveRecord::Base.configurations
|
184
|
+
database = configs[rails_env]
|
185
|
+
|
186
|
+
fork do
|
187
|
+
puts "Creating ssh tunnel..."
|
188
|
+
#run "ssh -N -L5000:127.0.0.1:5000 #{production_domain}"
|
189
|
+
exec_verbose "ssh #{domain} 'ssh -N -L5000:127.0.0.1:5000 #{production_domain}'"
|
190
|
+
# Note: If you get this error "Host key verification failed." then manually ssh to staging and
|
191
|
+
# from there to production (answer yes when prompted) so that the host key gets saved.
|
192
|
+
end
|
193
|
+
sleep 3 # TODO: read ssh output until it's ready
|
194
|
+
run "cd #{current_path} && bundle exec taps pull #{database['adapter']}://#{database['username']}:#{database['password']}@localhost/#{database['database']}?encoding=#{database['encoding']} " +
|
195
|
+
"http://#{http_user}:#{http_password}@localhost:5000"
|
196
|
+
puts "Done. Ctrl-C to exit ssh."
|
197
|
+
Process.wait
|
198
|
+
# For some reason the ssh processes seem to stick around, so just to make sure we clean up,
|
199
|
+
# let's try to kill them.
|
200
|
+
run 'killall ssh'
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
before 'db:taps_server', 'db:read_remote_my_cnf'
|
205
|
+
before 'deploy:migrate', 'backup:create'
|
206
|
+
|
207
|
+
end
|