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.
@@ -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,8 @@
1
+ Capistrano::Configuration.instance.load do
2
+
3
+ #set :whenever_environment, defer { stage }
4
+ #set :whenever_identifier, defer { "#{application}_#{stage}" }
5
+ #set :whenever_command, "bundle exec whenever"
6
+ #require "whenever/capistrano"
7
+
8
+ end
@@ -0,0 +1,8 @@
1
+ require 'erubis'
2
+
3
+ def render_erb_template(template_path, vars={})
4
+ raise "file '#{template_path}' not exists" unless File.exist? template_path
5
+
6
+ template = File.open(template_path, 'r').read
7
+ Erubis::Eruby.new(template).result(vars)
8
+ end
@@ -0,0 +1,9 @@
1
+ description "<%= application %>"
2
+
3
+ start on runlevel [2]
4
+ stop on runlevel [016]
5
+
6
+ expect daemon
7
+ exec bluepill load <%= config_path %>
8
+
9
+ respawn
@@ -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
@@ -0,0 +1,3 @@
1
+ module Caploy
2
+ VERSION = "0.1.0"
3
+ 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