caploy 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +19 -0
- data/Gemfile +4 -0
- data/README.md +12 -0
- data/Rakefile +2 -0
- data/caploy.gemspec +29 -0
- data/lib/caploy/recipes/airbrake.rb +4 -0
- data/lib/caploy/recipes/assets.rb +26 -0
- data/lib/caploy/recipes/bundler.rb +23 -0
- data/lib/caploy/recipes/database.rb +58 -0
- data/lib/caploy/recipes/defaults.rb +90 -0
- data/lib/caploy/recipes/info.rb +38 -0
- data/lib/caploy/recipes/monitoring.rb +12 -0
- data/lib/caploy/recipes/nginx.rb +108 -0
- data/lib/caploy/recipes/paperclip.rb +11 -0
- data/lib/caploy/recipes/passenger.rb +32 -0
- data/lib/caploy/recipes/puma.rb +25 -0
- data/lib/caploy/recipes/rbenv.rb +7 -0
- data/lib/caploy/recipes/redis.rb +41 -0
- data/lib/caploy/recipes/rvm.rb +8 -0
- data/lib/caploy/recipes/seeding.rb +9 -0
- data/lib/caploy/recipes/setup.rb +75 -0
- data/lib/caploy/recipes/symlink.rb +24 -0
- data/lib/caploy/recipes/unicorn.rb +43 -0
- data/lib/caploy/recipes/unicorn_bluepill.rb +90 -0
- data/lib/caploy/recipes/whenever.rb +8 -0
- data/lib/caploy/render.rb +8 -0
- data/lib/caploy/templates/bluepill/init.erb +9 -0
- data/lib/caploy/templates/bluepill/unicorn_config.rb.erb +43 -0
- data/lib/caploy/templates/nginx/vhost.erb +130 -0
- data/lib/caploy/templates/unicorn/unicorn.rb.erb +34 -0
- data/lib/caploy/version.rb +3 -0
- data/lib/caploy.rb +1 -0
- data/lib/examples/deploy.rb +73 -0
- data/lib/mysql.rb +109 -0
- data/lib/util.rb +10 -0
- metadata +225 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
Capistrano::Configuration.instance.load do
|
2
|
+
|
3
|
+
namespace :deploy do
|
4
|
+
desc <<-DESC
|
5
|
+
Symlink shared directories and files.
|
6
|
+
DESC
|
7
|
+
task :symlink_dependencies, :roles => :web, :except => {:no_release => true} do
|
8
|
+
shared_directories_to_link = fetch(:shared_directories_to_link, [])
|
9
|
+
directories_to_create = fetch(:directories_to_create, [])
|
10
|
+
files_to_delete = fetch(:files_to_delete, [])
|
11
|
+
files_to_link = fetch(:files_to_link, {})
|
12
|
+
chmods_to_set = fetch(:chmods_to_set, [])
|
13
|
+
|
14
|
+
directories_to_create.each { |directory| run "mkdir -p #{directory}" }
|
15
|
+
shared_directories_to_link.each { |source, target| run "ln -s #{source} #{target}" }
|
16
|
+
files_to_delete.each { |file| run "rm #{file}" }
|
17
|
+
files_to_link.each { |source, target| run "ln -s #{source} #{target}"}
|
18
|
+
chmods_to_set.each { |target, chmod| run "chmod #{chmod} #{target}" }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
after 'deploy:finalize_update', 'deploy:symlink_dependencies'
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
Capistrano::Configuration.instance.load do
|
2
|
+
|
3
|
+
_cset :unicorn_bin, "bundle exec unicorn"
|
4
|
+
_cset :unicorn_pid, "#{deploy_to}/current/tmp/pids/unicorn.pid"
|
5
|
+
_cset :unicorn_std_log, "log/unicorn.stderr.log"
|
6
|
+
_cset :unicorn_err_log, "log/unicorn.stderr.log"
|
7
|
+
_cset :unicorn_worker_processes, 2
|
8
|
+
_cset :unicorn_listen_backlog, 2048
|
9
|
+
|
10
|
+
require "capistrano-unicorn"
|
11
|
+
|
12
|
+
namespace :unicorn do
|
13
|
+
desc "Setup unicorn"
|
14
|
+
task :setup, :roles => :app, :except => { :no_release => true } do
|
15
|
+
run "mkdir -p \"#{shared_path}/config/unicorn\""
|
16
|
+
config_path = "#{shared_path}/config/unicorn/#{rails_env}.rb"
|
17
|
+
template_path = File.expand_path('../../templates/unicorn/unicorn.rb.erb', __FILE__)
|
18
|
+
vars = {
|
19
|
+
'application'=> application,
|
20
|
+
'current_path' => current_path,
|
21
|
+
'unicorn_pid' => unicorn_pid,
|
22
|
+
'unicorn_std_log' => unicorn_std_log,
|
23
|
+
'unicorn_err_log' => unicorn_err_log,
|
24
|
+
'stage' => stage,
|
25
|
+
'unicorn_listen_backlog' => unicorn_listen_backlog,
|
26
|
+
'unicorn_worker_processes' => unicorn_worker_processes
|
27
|
+
}
|
28
|
+
put(render_erb_template(template_path, vars), config_path)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
after :"deploy:setup", :"unicorn:setup";
|
33
|
+
|
34
|
+
namespace :deploy do
|
35
|
+
task :start, :roles => :app do
|
36
|
+
unicorn.start
|
37
|
+
end
|
38
|
+
|
39
|
+
task :stop, :roles => :app do
|
40
|
+
unicorn.stop
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# sudoers:
|
4
|
+
# %deploy ALL=(ALL) NOPASSWD: /usr/local/bin/bluepill, /sbin/start my_app_bluepill , /sbin/stop my_app_bluepill
|
5
|
+
# sudo chown root:root deploy_conf
|
6
|
+
# sudo chmod 0440 deploy_conf
|
7
|
+
# => /etc/sudoers.d
|
8
|
+
#
|
9
|
+
Capistrano::Configuration.instance.load do
|
10
|
+
|
11
|
+
namespace :deploy do
|
12
|
+
|
13
|
+
task :start, :roles => :app, :except => { :no_release => true } do
|
14
|
+
sudo 'start bluepill_conf'
|
15
|
+
end
|
16
|
+
|
17
|
+
task :stop, :roles => :app, :except => { :no_release => true } do
|
18
|
+
sudo "bluepill stop"
|
19
|
+
sudo "stop bluepill_conf"
|
20
|
+
end
|
21
|
+
|
22
|
+
task :restart, :roles => :app, :except => { :no_release => true } do
|
23
|
+
sudo "bluepill restart unicorn"
|
24
|
+
end
|
25
|
+
|
26
|
+
#desc "Restart Resque Workers"
|
27
|
+
#task :restart_workers do
|
28
|
+
# sudo "bluepill stop resque"
|
29
|
+
# sudo "bluepill start resque"
|
30
|
+
#end
|
31
|
+
#
|
32
|
+
#desc "Start Resque Workers"
|
33
|
+
#task :start_workers do
|
34
|
+
# sudo "bluepill start resque"
|
35
|
+
#end
|
36
|
+
#
|
37
|
+
#desc "Stop Resque Workers"
|
38
|
+
#task :stop_workers do
|
39
|
+
# sudo "bluepill stop resque"
|
40
|
+
#end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
namespace :bluepill do
|
45
|
+
desc "Prints bluepills monitored processes statuses"
|
46
|
+
task :status, :roles => [:app] do
|
47
|
+
sudo "bluepill status"
|
48
|
+
end
|
49
|
+
|
50
|
+
desc "Setup blupill config"
|
51
|
+
task :setup, :roles => [:app] do
|
52
|
+
setup_init
|
53
|
+
setup_config
|
54
|
+
end
|
55
|
+
|
56
|
+
task :setup_init do
|
57
|
+
template_path = File.expand_path('../../templates/bluepill/init.erb', __FILE__)
|
58
|
+
vars = {
|
59
|
+
'application' => application,
|
60
|
+
'config_path' => fetch(:bluepill_config_path, "#{shared_path}/config/bluepill_config.pill")
|
61
|
+
}
|
62
|
+
|
63
|
+
config_path = "#{shared_path}/config/bluepill_init.conf"
|
64
|
+
|
65
|
+
put(render_erb_template(template_path, vars), config_path)
|
66
|
+
sudo "rm -f /etc/init/bluepill_#{application}_#{stage}.conf"
|
67
|
+
sudo "ln -s #{config_path} /etc/init/bluepill_#{application}_#{stage}.conf"
|
68
|
+
end
|
69
|
+
|
70
|
+
task :setup_config do
|
71
|
+
template_path = File.expand_path('../../templates/bluepill/default_config.rb.erb', __FILE__)
|
72
|
+
log_file = "#{latest_release}/log/bluepill.log"
|
73
|
+
unicorn_config_path = "#{shared_path}/config/unicorn/#{stage}.rb"
|
74
|
+
vars = {
|
75
|
+
'application' => application,
|
76
|
+
'stage' => stage,
|
77
|
+
'log_file' => log_file,
|
78
|
+
'unicorn_config_path' => unicorn_config_path
|
79
|
+
}
|
80
|
+
|
81
|
+
config_path = fetch(:bluepill_config_path, "#{shared_path}/config/bluepill_config.pill")
|
82
|
+
put(render_erb_template(template_path, vars), config_path)
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
#after "deploy:restart", "deploy:restart_workers"
|
88
|
+
after "deploy:setup", "bluepill:setup"
|
89
|
+
|
90
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
ENV["RAILS_ENV"] = "<%= stage %>"
|
2
|
+
|
3
|
+
Bluepill.application("<%= application %>-<%= stage %>", :log_file => '<%= log_file %>') do |app|
|
4
|
+
|
5
|
+
app.process("unicorn") do |process|
|
6
|
+
process.pid_file = '<%= unicorn_config_path %>'
|
7
|
+
process.working_dir = '/var/my_app/current'
|
8
|
+
|
9
|
+
process.start_command = "/usr/local/bin/bundle exec unicorn -c /var/my_app/current/config/unicorn.rb -E staging -D"
|
10
|
+
process.stop_command = "kill -QUIT {{PID}}"
|
11
|
+
process.restart_command = "kill -USR2 {{PID}}"
|
12
|
+
|
13
|
+
process.uid = process.gid = 'deploy'
|
14
|
+
|
15
|
+
process.start_grace_time = 30.seconds
|
16
|
+
process.stop_grace_time = 30.seconds
|
17
|
+
process.restart_grace_time = 30.seconds
|
18
|
+
|
19
|
+
|
20
|
+
process.monitor_children do |child_process|
|
21
|
+
child_process.stop_command = "kill -QUIT {{PID}}"
|
22
|
+
|
23
|
+
child_process.checks :mem_usage, :every => 30.seconds, :below => 200.megabytes, :times => [3, 4], :fires => :stop
|
24
|
+
child_process.checks :cpu_usage, :every => 30.seconds, :below => 40, :times => [3, 4], :fires => :stop
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
2.times do |i|
|
29
|
+
app.process("resque-#{i}") do |process|
|
30
|
+
process.working_dir = '/var/my_app/current'
|
31
|
+
process.group = "resque"
|
32
|
+
process.start_command = "/usr/local/bin/bundle exec rake resque:work"
|
33
|
+
process.pid_file = "/var/my_app/shared/pids/my_app-resque-#{i}.pid"
|
34
|
+
process.stop_command = "kill -QUIT {{PID}}"
|
35
|
+
process.daemonize = true
|
36
|
+
|
37
|
+
process.start_grace_time = 30.seconds
|
38
|
+
process.stop_grace_time = 30.seconds
|
39
|
+
process.uid = process.gid = 'deploy'
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
|
2
|
+
<% if with_upstream_server %>
|
3
|
+
|
4
|
+
upstream <%= application %>_<%= stage %>_<%= protocol %>_server {
|
5
|
+
# fail_timeout=0 means we always retry an upstream even if it failed
|
6
|
+
# to return a good HTTP response (in case the Unicorn master nukes a
|
7
|
+
# single worker for timing out).
|
8
|
+
|
9
|
+
# This is the socket we configured in unicorn.rb
|
10
|
+
server unix:/tmp/socket.<%= application %>_<%= stage %>.sock fail_timeout=0;
|
11
|
+
|
12
|
+
# for TCP setups, point these to your backend servers
|
13
|
+
# server 127.0.0.1:8080 fail_timeout=0;
|
14
|
+
# server 192.168.0.8:8080 fail_timeout=0;
|
15
|
+
# server 192.168.0.9:8080 fail_timeout=0;
|
16
|
+
}
|
17
|
+
<% end %>
|
18
|
+
|
19
|
+
<%= optional_nginx_http_content if protocol == 'http' %>
|
20
|
+
<%= optional_nginx_https_content if protocol == 'https' %>
|
21
|
+
|
22
|
+
server {
|
23
|
+
|
24
|
+
server_name <%= domain %>;
|
25
|
+
|
26
|
+
<% if protocol == 'https' %>
|
27
|
+
|
28
|
+
listen 443 ssl;
|
29
|
+
|
30
|
+
ssl_certificate <%= nginx_cert_dir %>/<%= application %>_cert.<%= cert_type %>;
|
31
|
+
ssl_certificate_key <%= nginx_cert_dir %>/<%= application %>_cert.<%= key_type %>;
|
32
|
+
|
33
|
+
#ssl_ciphers SSLv3+HIGH:RC4+MEDIUM:!aNULL:!eNULL:!3DES:!MD5:@STRENGTH;
|
34
|
+
#ssl_prefer_server_ciphers on;
|
35
|
+
#ssl_protocols SSLv3;
|
36
|
+
#ssl_session_cache shared:SSL:10m;
|
37
|
+
|
38
|
+
keepalive_timeout 70;
|
39
|
+
|
40
|
+
<% else %>
|
41
|
+
|
42
|
+
listen 80;
|
43
|
+
|
44
|
+
# ~2 seconds is often enough for most folks to parse HTML/CSS and
|
45
|
+
# retrieve needed images/icons/frames, connections are cheap in
|
46
|
+
# nginx so increasing this is generally safe...
|
47
|
+
#keepalive_timeout 5;
|
48
|
+
|
49
|
+
<% end %>
|
50
|
+
|
51
|
+
client_max_body_size 4G;
|
52
|
+
|
53
|
+
<% if serve_static_files %>
|
54
|
+
|
55
|
+
# path for static files
|
56
|
+
root <%= project_root %>/public;
|
57
|
+
|
58
|
+
<% if with_file_expire_max %>
|
59
|
+
location ~* \.(ico|css|js|gif|jpe?g|png)(\?[0-9]+)?$ {
|
60
|
+
expires max;
|
61
|
+
break;
|
62
|
+
}
|
63
|
+
<% end %>
|
64
|
+
|
65
|
+
<% end %>
|
66
|
+
|
67
|
+
<%= optional_http_content if protocol == 'http' %>
|
68
|
+
<%= optional_https_content if protocol == 'https' %>
|
69
|
+
|
70
|
+
# Prefer to serve static files directly from nginx to avoid unnecessary
|
71
|
+
# data copies from the application server.
|
72
|
+
#
|
73
|
+
# try_files directive appeared in in nginx 0.7.27 and has stabilized
|
74
|
+
# over time. Older versions of nginx (e.g. 0.6.x) requires
|
75
|
+
# "if (!-f $request_filename)" which was less efficient:
|
76
|
+
# http://bogomips.org/unicorn.git/tree/examples/nginx.conf?id=v3.3.1#n127
|
77
|
+
try_files $uri/index.html $uri.html $uri @app;
|
78
|
+
|
79
|
+
location / {
|
80
|
+
# an HTTP header important enough to have its own Wikipedia entry:
|
81
|
+
# http://en.wikipedia.org/wiki/X-Forwarded-For
|
82
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
83
|
+
|
84
|
+
# enable this if and only if you use HTTPS, this helps Rack
|
85
|
+
# set the proper protocol for doing redirects:
|
86
|
+
# proxy_set_header X-Forwarded-Proto https;
|
87
|
+
|
88
|
+
# pass the Host: header from the client right along so redirects
|
89
|
+
# can be set properly within the Rack application
|
90
|
+
proxy_set_header Host $http_host;
|
91
|
+
|
92
|
+
# we don't want nginx trying to do something clever with
|
93
|
+
# redirects, we set the Host: header above already.
|
94
|
+
proxy_redirect off;
|
95
|
+
|
96
|
+
# set "proxy_buffering off" *only* for Rainbows! when doing
|
97
|
+
# Comet/long-poll/streaming. It's also safe to set if you're using
|
98
|
+
# only serving fast clients with Unicorn + nginx, but not slow
|
99
|
+
# clients. You normally want nginx to buffer responses to slow
|
100
|
+
# clients, even with Rails 3.1 streaming because otherwise a slow
|
101
|
+
# client can become a bottleneck of Unicorn.
|
102
|
+
#
|
103
|
+
# The Rack application may also set "X-Accel-Buffering (yes|no)"
|
104
|
+
# in the response headers do disable/enable buffering on a
|
105
|
+
# per-response basis.
|
106
|
+
# proxy_buffering off;
|
107
|
+
|
108
|
+
<% if protocol == 'https' %>
|
109
|
+
# This makes sure that Rack::SslEnforcer knows it's being accessed over SSL.
|
110
|
+
proxy_set_header X-Forwarded-Proto https;
|
111
|
+
<% end %>
|
112
|
+
|
113
|
+
if (!-f $request_filename) {
|
114
|
+
proxy_pass http://<%= application %>_<%= stage %>_<%= protocol %>_server;
|
115
|
+
break;
|
116
|
+
}
|
117
|
+
|
118
|
+
<% if auth_basic_title %>
|
119
|
+
auth_basic "<%= auth_basic_title %>";
|
120
|
+
auth_basic_user_file <%= auth_basic_password_file %>;
|
121
|
+
<% end %>
|
122
|
+
}
|
123
|
+
|
124
|
+
# Rails error pages
|
125
|
+
error_page 500 502 503 504 /500.html;
|
126
|
+
location = /500.html {
|
127
|
+
root <%= project_root %>/public;
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
working_directory "<%= current_path %>"
|
2
|
+
pid "<%= unicorn_pid %>"
|
3
|
+
stderr_path "<%= unicorn_std_log %>"
|
4
|
+
stdout_path "<%= unicorn_err_log %>"
|
5
|
+
|
6
|
+
listen "/tmp/socket.<%= application %>_<%= stage %>.sock", :backlog => <%= unicorn_listen_backlog %>
|
7
|
+
worker_processes <%= unicorn_worker_processes %>
|
8
|
+
timeout 30
|
9
|
+
|
10
|
+
preload_app true
|
11
|
+
|
12
|
+
before_fork do |server, worker|
|
13
|
+
# Disconnect since the database connection will not carry over
|
14
|
+
if defined? ActiveRecord::Base
|
15
|
+
ActiveRecord::Base.connection.disconnect!
|
16
|
+
end
|
17
|
+
|
18
|
+
# Quit the old unicorn process
|
19
|
+
old_pid = "#{server.config[:pid]}.oldbin"
|
20
|
+
if File.exists?(old_pid) && server.pid != old_pid
|
21
|
+
begin
|
22
|
+
Process.kill("QUIT", File.read(old_pid).to_i)
|
23
|
+
rescue Errno::ENOENT, Errno::ESRCH
|
24
|
+
# someone else did our job for us
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
after_fork do |server, worker|
|
30
|
+
# Start up the database connection again in the worker
|
31
|
+
if defined?(ActiveRecord::Base)
|
32
|
+
ActiveRecord::Base.establish_connection
|
33
|
+
end
|
34
|
+
end
|
data/lib/caploy.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "caploy/version"
|
@@ -0,0 +1,73 @@
|
|
1
|
+
|
2
|
+
set :application, ''
|
3
|
+
set :domain, ''
|
4
|
+
set :deploy_domain, ''
|
5
|
+
set :vhost_domain, ''
|
6
|
+
|
7
|
+
# set application folder
|
8
|
+
set :deploy_to, "/var/projects/#{application}/xxx"
|
9
|
+
|
10
|
+
set :rails_root, File.expand_path('../../', __FILE__)
|
11
|
+
set :rails_env, :production # use 'stage' to differ between stage environments
|
12
|
+
|
13
|
+
set :user, 'deploy'
|
14
|
+
|
15
|
+
server domain, :web, :app, :db, :primary => true
|
16
|
+
|
17
|
+
# Multistage settings
|
18
|
+
#set :stage_dir, File.dirname(__FILE__) + '/deploy/stages'
|
19
|
+
#set :default_stage, "vagrant"
|
20
|
+
#require 'capistrano/ext/multistage'
|
21
|
+
|
22
|
+
# database
|
23
|
+
set :db_name, ''
|
24
|
+
set :db_user_name, ''
|
25
|
+
set :db_user_password, ''
|
26
|
+
|
27
|
+
set :branch, ENV['BRANCH'] || 'develop'
|
28
|
+
set :repository, 'xxx.git'
|
29
|
+
|
30
|
+
set(:shared_directories_to_create) {
|
31
|
+
%W(#{shared_path}/bundle #{shared_path}/cache)
|
32
|
+
}
|
33
|
+
|
34
|
+
set(:shared_directories_to_link) {
|
35
|
+
{
|
36
|
+
"#{shared_path}/bundle/" => "#{release_path}/vendor/bundle"
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
set(:directories_to_create) {
|
41
|
+
%W()
|
42
|
+
}
|
43
|
+
|
44
|
+
set(:files_to_link) {
|
45
|
+
{
|
46
|
+
"#{shared_path}/config/config.#{stage}.yml" => "#{release_path}/config/config.local.yml",
|
47
|
+
"#{shared_path}/config/unicorn.#{stage}.rb" => "#{release_path}/config/unicorn.production.rb"
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
set(:files_to_delete) {
|
52
|
+
%W()
|
53
|
+
}
|
54
|
+
|
55
|
+
set(:chmods_to_set) {
|
56
|
+
{
|
57
|
+
# "#{shared_path}/data/pdf" => 755
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
require 'caploy/recipes/defaults'
|
62
|
+
|
63
|
+
require 'caploy/recipes/rbenv'
|
64
|
+
require 'caploy/recipes/assets'
|
65
|
+
require 'caploy/recipes/nginx'
|
66
|
+
require 'caploy/recipes/bundler'
|
67
|
+
require 'caploy/recipes/symlink'
|
68
|
+
require 'caploy/recipes/unicorn'
|
69
|
+
require 'caploy/recipes/setup'
|
70
|
+
require 'caploy/recipes/monitoring'
|
71
|
+
require 'caploy/recipes/seeding'
|
72
|
+
#require 'caploy/recipes/paperclip'
|
73
|
+
#require 'caploy/recipes/airbrake'
|
data/lib/mysql.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
module Database
|
2
|
+
class Base
|
3
|
+
attr_accessor :config, :output_file
|
4
|
+
def initialize(cap_instance)
|
5
|
+
@cap = cap_instance
|
6
|
+
end
|
7
|
+
|
8
|
+
def mysql?
|
9
|
+
@config['adapter'] == 'mysql' || @config['adapter'] == 'mysql2'
|
10
|
+
end
|
11
|
+
|
12
|
+
def credentials
|
13
|
+
" -u #{@config['username']} " + (@config['password'] ? " -p\"#{@config['password']}\" " : '') + (@config['host'] ? " -h #{@config['host']}" : '')
|
14
|
+
end
|
15
|
+
|
16
|
+
def database
|
17
|
+
@config['database']
|
18
|
+
end
|
19
|
+
|
20
|
+
def output_file
|
21
|
+
@output_file ||= "db/dump_#{database}.sql.bz2"
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def dump_cmd
|
26
|
+
"mysqldump #{credentials} #{database}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def import_cmd(file)
|
30
|
+
"mysql #{credentials} -D #{database} < #{file}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Remote < Base
|
35
|
+
def initialize(cap_instance)
|
36
|
+
super(cap_instance)
|
37
|
+
@cap.run("cat #{@cap.current_path}/config/database.yml") { |c, s, d| @config = YAML.load(d)[(@cap.rails_env || 'production').to_s] }
|
38
|
+
end
|
39
|
+
|
40
|
+
def dump
|
41
|
+
@cap.run "cd #{@cap.current_path}; #{dump_cmd} | bzip2 - - > #{output_file}"
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
def download(local_file = "#{output_file}")
|
46
|
+
remote_file = "#{@cap.current_path}/#{output_file}"
|
47
|
+
@cap.get remote_file, local_file
|
48
|
+
end
|
49
|
+
|
50
|
+
# cleanup = true removes the mysqldump file after loading, false leaves it in db/
|
51
|
+
def load(file, cleanup)
|
52
|
+
unzip_file = File.join(File.dirname(file), File.basename(file, '.bz2'))
|
53
|
+
@cap.run "cd #{@cap.current_path}; bunzip2 -f #{file} && RAILS_ENV=#{@cap.rails_env} rake db:drop db:create && #{import_cmd(unzip_file)}"
|
54
|
+
File.unlink(unzip_file) if cleanup
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class Local < Base
|
59
|
+
def initialize(cap_instance)
|
60
|
+
super(cap_instance)
|
61
|
+
@config = YAML.load_file(File.join('config', 'database.yml'))[@cap.local_rails_env]
|
62
|
+
end
|
63
|
+
|
64
|
+
# cleanup = true removes the mysqldump file after loading, false leaves it in db/
|
65
|
+
def load(file, cleanup)
|
66
|
+
unzip_file = File.join(File.dirname(file), File.basename(file, '.bz2'))
|
67
|
+
system("bunzip2 -f #{file} && rake db:drop db:create && #{import_cmd(unzip_file)} && rake db:migrate")
|
68
|
+
File.unlink(unzip_file) if cleanup
|
69
|
+
end
|
70
|
+
|
71
|
+
def dump
|
72
|
+
system "#{dump_cmd} | bzip2 - - > #{output_file}"
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
def upload
|
77
|
+
remote_file = "#{@cap.current_path}/#{output_file}"
|
78
|
+
@cap.upload output_file, remote_file
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class << self
|
83
|
+
def check(local_db, remote_db)
|
84
|
+
unless local_db.mysql? && remote_db.mysql?
|
85
|
+
raise 'Only mysql on remote and local server is supported'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def remote_to_local(instance)
|
90
|
+
local_db = Database::Local.new(instance)
|
91
|
+
remote_db = Database::Remote.new(instance)
|
92
|
+
|
93
|
+
check(local_db, remote_db)
|
94
|
+
|
95
|
+
remote_db.dump.download
|
96
|
+
local_db.load(remote_db.output_file, instance.fetch(:db_local_clean))
|
97
|
+
end
|
98
|
+
|
99
|
+
def local_to_remote(instance)
|
100
|
+
local_db = Database::Local.new(instance)
|
101
|
+
remote_db = Database::Remote.new(instance)
|
102
|
+
|
103
|
+
check(local_db, remote_db)
|
104
|
+
|
105
|
+
local_db.dump.upload
|
106
|
+
remote_db.load(local_db.output_file, instance.fetch(:db_local_clean))
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
data/lib/util.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
module Util
|
2
|
+
def self.prompt(msg, prompt = "(y)es, (n)o ")
|
3
|
+
answer = Capistrano::CLI.ui.ask("#{msg} #{prompt} ? ") do |q|
|
4
|
+
q.overwrite = false
|
5
|
+
q.validate = /^y$|^yes$|^n$|^no$/i
|
6
|
+
q.responses[:not_valid] = prompt
|
7
|
+
end
|
8
|
+
(answer =~ /^y$|^yes$/i) == 0
|
9
|
+
end
|
10
|
+
end
|