caploy 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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