capistrano-exts 1.11.3 → 1.12.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.
Files changed (40) hide show
  1. data/.todo +9 -0
  2. data/README.md +3 -3
  3. data/TODO +3 -0
  4. data/examples/php_fpm/deploy/development.rb +16 -0
  5. data/examples/php_fpm/deploy/production.rb +16 -0
  6. data/examples/php_fpm/deploy/staging.rb +16 -0
  7. data/examples/rails_passenger/deploy/development.rb +16 -0
  8. data/examples/rails_passenger/deploy/production.rb +16 -0
  9. data/examples/rails_passenger/deploy/staging.rb +16 -0
  10. data/examples/rails_reverse_proxy/deploy/development.rb +16 -0
  11. data/examples/rails_reverse_proxy/deploy/production.rb +16 -0
  12. data/examples/rails_reverse_proxy/deploy/staging.rb +16 -0
  13. data/lib/capistrano-exts.rb +1 -1
  14. data/lib/capistrano-exts/receipts.rb +10 -1
  15. data/lib/capistrano-exts/receipts/contao.rb +1 -5
  16. data/lib/capistrano-exts/receipts/contents.rb +2 -6
  17. data/lib/capistrano-exts/receipts/deploy.rb +5 -9
  18. data/lib/capistrano-exts/receipts/files.rb +0 -4
  19. data/lib/capistrano-exts/receipts/functions.rb +1 -4
  20. data/lib/capistrano-exts/receipts/git.rb +0 -2
  21. data/lib/capistrano-exts/receipts/god.rb +0 -2
  22. data/lib/capistrano-exts/receipts/multistage.rb +0 -2
  23. data/lib/capistrano-exts/receipts/mysql.rb +5 -10
  24. data/lib/capistrano-exts/receipts/rails.rb +0 -4
  25. data/lib/capistrano-exts/receipts/servers.rb +1 -3
  26. data/lib/capistrano-exts/receipts/servers/web_server.rb +1 -1
  27. data/lib/capistrano-exts/receipts/unicorn.rb +28 -21
  28. data/lib/capistrano-exts/receipts/web.rb +136 -0
  29. data/lib/capistrano-exts/servers/web_server.rb +5 -5
  30. data/lib/capistrano-exts/templates/maintenance/index.rhtml +26 -0
  31. data/lib/capistrano-exts/templates/maintenance/stylesheets/styles.css +37 -0
  32. data/lib/capistrano-exts/templates/multistage.rb +16 -0
  33. data/lib/capistrano-exts/templates/web_servers/nginx.conf.erb +25 -11
  34. data/lib/capistrano-exts/version.rb +2 -2
  35. data/spec/{requests → acceptance}/nginx_spec.rb +28 -8
  36. data/spec/rendered_templates/nginx_passenger.conf +24 -10
  37. data/spec/rendered_templates/nginx_php_fpm.conf +25 -6
  38. data/spec/rendered_templates/nginx_reverse_proxy_address.conf +25 -6
  39. data/spec/rendered_templates/nginx_reverse_proxy_socket.conf +25 -6
  40. metadata +23 -20
@@ -1,7 +1,3 @@
1
- require 'capistrano'
2
- require 'capistrano-exts/receipts/deploy'
3
- require 'capistrano-exts/receipts/files'
4
-
5
1
  # Verify that Capistrano is version 2
6
2
  unless Capistrano::Configuration.respond_to?(:instance)
7
3
  abort "This extension requires Capistrano 2"
@@ -1,8 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  # Requirements
4
- require 'capistrano'
5
- require 'capistrano-exts/receipts/deploy'
6
4
  require 'capistrano-exts/receipts/mysql'
7
5
  require 'capistrano-exts/receipts/servers/web_server'
8
6
  require 'capistrano-exts/receipts/servers/db_server'
@@ -25,7 +23,7 @@ Capistrano::Configuration.instance(:must_exist).load do
25
23
  idrsa_filename_argv = ARGV.try(:[], argv_file_index)
26
24
 
27
25
  # Generate the file name
28
- if idrsa_filename_argv && !idrsa_filename_argv =~ /.+:.+/ && !File.exists?(idrsa_filename_argv)
26
+ if idrsa_filename_argv and not idrsa_filename_argv =~ /.+:.+/ and not File.exists?(idrsa_filename_argv)
29
27
  idrsa_filename = idrsa_filename_argv
30
28
  else
31
29
  idrsa_filename = "#{ENV['HOME']}/.ssh/id_rsa.pub"
@@ -71,7 +71,7 @@ Capistrano::Configuration.instance(:must_exist).load do
71
71
 
72
72
  desc "[internal] Write authentification file"
73
73
  task :write_web_server_auth_file do
74
- if exists?(:web_server_auth_file) && !remote_file_exists?(fetch :web_server_auth_file)
74
+ if exists?(:web_server_auth_file) and not remote_file_exists?(fetch :web_server_auth_file)
75
75
  web_server_auth_file = fetch :web_server_auth_file
76
76
  web_server_auth_file_contents = fetch :web_server_auth_file_contents
77
77
  web_server_auth_file_unencrypted_contents = fetch :web_server_auth_file_unencrypted_contents
@@ -1,27 +1,34 @@
1
- namespace :unicorn do
2
- desc "start unicorn"
3
- task :start, :roles => :app, :except => {:no_release => true} do
4
- run "cd #{current_path} && #{unicorn_binary} -c #{unicorn_config} -E #{rails_env} -D"
5
- end
1
+ # Verify that Capistrano is version 2
2
+ unless Capistrano::Configuration.respond_to?(:instance)
3
+ abort "This extension requires Capistrano 2"
4
+ end
6
5
 
7
- desc "stop unicorn"
8
- task :stop, :roles => :app, :except => {:no_release => true} do
9
- run "#{try_sudo} kill `cat #{unicorn_pid}`"
10
- end
6
+ Capistrano::Configuration.instance(:must_exist).load do
7
+ namespace :unicorn do
8
+ desc "start unicorn"
9
+ task :start, :roles => :app, :except => {:no_release => true} do
10
+ run "cd #{current_path} && #{unicorn_binary} -c #{unicorn_config} -E #{rails_env} -D"
11
+ end
11
12
 
12
- desc "unicorn reload"
13
- task :reload, :roles => :app, :except => {:no_release => true} do
14
- run "#{try_sudo} kill -s USR2 `cat #{unicorn_pid}`"
15
- end
13
+ desc "stop unicorn"
14
+ task :stop, :roles => :app, :except => {:no_release => true} do
15
+ run "#{try_sudo} kill `cat #{unicorn_pid}`"
16
+ end
16
17
 
17
- desc "graceful stop unicorn"
18
- task :graceful_stop, :roles => :app, :except => {:no_release => true} do
19
- run "#{try_sudo} kill -s QUIT `cat #{unicorn_pid}`"
20
- end
18
+ desc "unicorn reload"
19
+ task :reload, :roles => :app, :except => {:no_release => true} do
20
+ run "#{try_sudo} kill -s USR2 `cat #{unicorn_pid}`"
21
+ end
22
+
23
+ desc "graceful stop unicorn"
24
+ task :graceful_stop, :roles => :app, :except => {:no_release => true} do
25
+ run "#{try_sudo} kill -s QUIT `cat #{unicorn_pid}`"
26
+ end
21
27
 
22
- desc "restart unicorn"
23
- task :restart, :roles => :app, :except => {:no_release => true} do
24
- stop
25
- start
28
+ desc "restart unicorn"
29
+ task :restart, :roles => :app, :except => {:no_release => true} do
30
+ stop
31
+ start
32
+ end
26
33
  end
27
34
  end
@@ -0,0 +1,136 @@
1
+ # Verify that Capistrano is version 2
2
+ unless Capistrano::Configuration.respond_to?(:instance)
3
+ abort "This extension requires Capistrano 2"
4
+ end
5
+
6
+ require 'fileutils'
7
+
8
+ Capistrano::Configuration.instance(:must_exist).load do
9
+ namespace :web do
10
+
11
+ desc "Symlink system folder"
12
+ task :symlink_system_folder, :roles => :web, :except => { :no_release => true } do
13
+ public_path = fetch(:public_path).gsub(%r{#{current_path}}, latest_release)
14
+ link_file "#{fetch :shared_path}/__system__", "#{public_path}/__system__"
15
+ end
16
+
17
+ desc <<-DESC
18
+ Present a maintenance page to visitors. Disables your application's web \
19
+ interface by writing a maintenance page to each web server. The \
20
+ servers must be configured to detect the presence of this file, and if \
21
+ it is present, always display it instead of performing the request.
22
+
23
+ By default, the maintenance page will just say the site is down for \
24
+ "maintenance", and will be back "shortly", but you can customize the \
25
+ page by specifying the TITLE, REASON and UNTIL environment variables:
26
+
27
+ $ cap deploy:web:disable \\
28
+ TITLE="Upgrading" \\
29
+ REASON="hardware upgrade" \\
30
+ UNTIL="12pm Central Time"
31
+
32
+ Further customization will require that you write your own task.
33
+ DESC
34
+ task :disable, :roles => :web, :except => { :no_release => true } do
35
+ on_rollback { run "rm -rf #{fetch :shared_path}/__system__/maintenance" }
36
+
37
+ # Define the maintenance_url
38
+ maintenance_url = '/__system__/maintenance'
39
+
40
+ # Get the maintenance_path or use the default one
41
+ maintenance_path = fetch :maintenance_path,
42
+ File.expand_path(File.join(File.dirname(__FILE__), '..', "templates", "maintenance"))
43
+
44
+ # Fetch command line options
45
+ title = ENV['TITLE']
46
+ reason = ENV['REASON']
47
+ deadline = ENV['UNTIL']
48
+
49
+ # Create the maintenance folder
50
+ run <<-CMD
51
+ #{try_sudo} mkdir -p #{fetch :shared_path}/__system__/maintenance
52
+ CMD
53
+
54
+ if File.directory?(maintenance_path)
55
+ # Generate random names
56
+ random_folder = random_tmp_file
57
+ random_file = "#{random_tmp_file}.tar.gz"
58
+ # Add a rollback hook
59
+ on_rollback do
60
+ run "rm -f #{random_file}"
61
+ system "rm -rf #{random_file} #{random_folder}"
62
+ end
63
+ # Copy the folder to /tmp
64
+ FileUtils.cp_r maintenance_path, random_folder
65
+
66
+ # If we have an rhtml file
67
+ if File.exists?("#{random_folder}/index.rhtml")
68
+ # Read it
69
+ erb_contents = File.read("#{random_folder}/index.rhtml")
70
+ # Delete it
71
+ FileUtils.rm "#{random_folder}/index.rhtml"
72
+ # Write the rendered ERB to the html file
73
+ File.open("#{random_folder}/index.html", 'w') do |f|
74
+ f.write ERB.new(erb_contents).result(binding)
75
+ end
76
+ end
77
+
78
+ # Create a local tar file
79
+ system <<-CMD
80
+ cd #{random_folder} &&
81
+ tar chzf #{random_file} --exclude='*~' --exclude='*.tmp' --exclude='*.bak' * &&
82
+ rm -rf #{random_folder}
83
+ CMD
84
+
85
+ # Upload the file
86
+ put File.read(random_file), random_file
87
+
88
+ # Remove the file locally
89
+ system <<-CMD
90
+ rm -f #{random_file}
91
+ CMD
92
+
93
+ # Extract the folder remotely
94
+ run <<-CMD
95
+ cd #{fetch :shared_path}/__system__/maintenance &&
96
+ #{try_sudo} tar xzf #{random_file} &&
97
+ rm -f #{random_file}
98
+ CMD
99
+ else
100
+ erb_contents = File.read(maintenance_path)
101
+ result = ERB.new(erb_contents).result(binding)
102
+
103
+ put result, "#{fetch :shared_path}/__system__/maintenance/index.html", :mode => 0644
104
+ end
105
+
106
+ if fetch(:web_server_app) == :apache
107
+ warn <<-EOHTACCESS
108
+
109
+ # Please add something like this to your site's htaccess to redirect users to the maintenance page.
110
+ # More Info: http://www.shiftcommathree.com/articles/make-your-rails-maintenance-page-respond-with-a-503
111
+
112
+ ErrorDocument 503 /__system__/maintenance/index.html
113
+ RewriteEngine On
114
+ RewriteCond %{REQUEST_URI} !\.(css|js|gif|jpg|png)$
115
+ RewriteCond %{DOCUMENT_ROOT}/__system__/maintenance/index.html -f
116
+ RewriteCond %{SCRIPT_FILENAME} !/__system__/maintenance/index.html
117
+ RewriteRule ^.*$ - [redirect=503,last]
118
+ EOHTACCESS
119
+ end
120
+ end
121
+
122
+ desc <<-DESC
123
+ Makes the application web-accessible again. Removes the \
124
+ "#{maintenance_basename}.html" page generated by deploy:web:disable, which (if your \
125
+ web servers are configured correctly) will make your application \
126
+ web-accessible again.
127
+ DESC
128
+ task :enable, :roles => :web, :except => { :no_release => true } do
129
+ run "rm -rf #{fetch :shared_path}/__system__/maintenance"
130
+ end
131
+ end
132
+
133
+ # Dependencies
134
+ after "deploy:finalize_update", "web:symlink_system_folder"
135
+ after "web:disable", "deploy:fix_permissions"
136
+ end
@@ -40,7 +40,7 @@ module Capistrano
40
40
 
41
41
  def php_build_with_force_cgi_redirect?
42
42
  # required if PHP was built with --enable-force-cgi-redirect
43
- @php_build_with_force_cgi_redirect.present? && @php_build_with_force_cgi_redirect == true
43
+ @php_build_with_force_cgi_redirect.present? and @php_build_with_force_cgi_redirect == true
44
44
  end
45
45
 
46
46
  def sanity_check
@@ -59,19 +59,19 @@ module Capistrano
59
59
  end
60
60
 
61
61
  if reverse_proxy?
62
- if @reverse_proxy_server_address.blank? && @reverse_proxy_server_port.blank? && @reverse_proxy_socket.blank?
62
+ if @reverse_proxy_server_address.blank? and @reverse_proxy_server_port.blank? and @reverse_proxy_socket.blank?
63
63
  raise ArgumentError, "None of the address, port or socket has been defined."
64
64
  end
65
65
 
66
- if @reverse_proxy_server_address.present? && @reverse_proxy_server_port.blank?
66
+ if @reverse_proxy_server_address.present? and @reverse_proxy_server_port.blank?
67
67
  raise ArgumentError, "reverse_proxy_server_address is defined but reverse_proxy_server_port is not please define it."
68
68
  end
69
69
 
70
- if @reverse_proxy_server_port.present? && @reverse_proxy_server_address.blank?
70
+ if @reverse_proxy_server_port.present? and @reverse_proxy_server_address.blank?
71
71
  raise ArgumentError, "reverse_proxy_server_port is defined but reverse_proxy_server_address is not please define it."
72
72
  end
73
73
 
74
- if @reverse_proxy_server_address.present? && @reverse_proxy_server_port.present? && @reverse_proxy_socket.present?
74
+ if @reverse_proxy_server_address.present? and @reverse_proxy_server_port.present? and @reverse_proxy_socket.present?
75
75
  raise ArgumentError, "you should not define reverse_proxy_server_address, reverse_proxy_server_port and reverse_proxy_socket."
76
76
  end
77
77
  end
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
3
+ <head>
4
+ <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
5
+ <title><%= title || 'System down for maintenance' %></title>
6
+ <link type="text/css" rel="stylesheet" href="<%= maintenance_url %>/stylesheets/styles.css">
7
+ <!--[if lt IE 9]>
8
+ <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
9
+ <![endif]-->
10
+ </head>
11
+ <body>
12
+ <section>
13
+ <article>
14
+ <div class="inside">
15
+ <p class="reason">
16
+ The system is down for <%= reason ? reason : "maintenance" %>
17
+ as of <%= Time.now.strftime("%H:%M %Z") %>.
18
+ </p>
19
+ <p class="duration">
20
+ It'll be back <%= deadline ? deadline : "shortly" %>.
21
+ </p>
22
+ </div>
23
+ </article>
24
+ </section>
25
+ </body>
26
+ </html>
@@ -0,0 +1,37 @@
1
+ section {
2
+ position: absolute;
3
+ left: 50%;
4
+ top: 50%;
5
+ width: 500px;
6
+ height: 300px;
7
+ margin-left: -260px;
8
+ margin-top: -150px;
9
+ }
10
+
11
+ article {
12
+ margin: 0;
13
+ padding: 10px;
14
+ text-align: center;
15
+ border: 1px solid #ccc;
16
+ border-right: 1px solid #999;
17
+ border-bottom: 1px solid #999;
18
+ background-color: #fff;
19
+ }
20
+
21
+ article .inside {
22
+ text-align: center;
23
+ width: 200px;
24
+ margin: 0 auto;
25
+ }
26
+
27
+ article .inside .reason {
28
+ color: red;
29
+ font-size: 16px;
30
+ line-height: 20px;
31
+ }
32
+
33
+ article .inside .duration {
34
+ color: #666;
35
+ }
36
+
37
+ body { background-color: #fff; }
@@ -88,6 +88,22 @@ set :rvm_ruby_string, "1.9.3"
88
88
  #
89
89
  #############
90
90
 
91
+ #############
92
+ # Maintenance
93
+ #
94
+
95
+ # Set the maintenance path to wherever you have stored the maintenance page,
96
+ # it could be a single file or an entire folder. The template will be parsed
97
+ # with ERB.
98
+ # if it's a folder, capistrano expects an index.html file. You could provide an
99
+ # index.rhtml file and it would be parsed with ERB before uploading to the server
100
+ # set :maintenance_path,
101
+ # File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'maintenance'))
102
+
103
+ #
104
+ #
105
+ #############
106
+
91
107
  #############
92
108
  # Mysql
93
109
  #
@@ -21,26 +21,40 @@ server {
21
21
  index <%= @indexes.join(' ') %>;
22
22
  <% end %>
23
23
 
24
- # This allows people to use images and css in their maintenance.html file
25
- if ($request_filename ~* \.(css|jpg|gif|png)$) {
24
+ # Enable recursive error pages to catch 405 in a 503 error.
25
+ recursive_error_pages on;
26
+
27
+ # This allows people to use stylesheet, javascript and images in their
28
+ # maintenance html file
29
+ if ($request_filename ~* \.(css|js|jpg|gif|png)$) {
30
+ expires 30d;
26
31
  break;
27
32
  }
28
- # Rewrite all the requests to the maintenance.html page if it exists.
29
- if (-f $document_root/system/maintenance.html) {
33
+
34
+ # If the maintenance page exists, throw a 503 error which we'd catch later.
35
+ if (-f $document_root/__system__/maintenance/index.html) {
30
36
  return 503;
31
37
  }
32
- # with 503 error code
38
+
39
+ # Catch the 503 error and send it to the @503 location
40
+ error_page 503 @503;
41
+
42
+ # The @503 location
33
43
  location @503 {
34
- rewrite ^(.*)$ /system/maintenance.html break;
35
- }
44
+ # Nginx will refuse POST requests to a static file and will throw a 405 error
45
+ # So we need to catch that and display again our maintenance page
46
+ error_page 405 = /__system__/maintenance/index.html;
36
47
 
37
- <% if passenger? %>
38
- passenger_enabled on;
48
+ # Rewrite all the requests to the maintenance page if it exists.
49
+ rewrite ^(.*)$ /__system__/maintenance/index.html break;
50
+ }
39
51
 
40
- # Rails errors
52
+ # Error Pages
41
53
  error_page 404 /404.html;
42
54
  error_page 500 502 504 /500.html;
43
- error_page 503 @503;
55
+
56
+ <% if passenger? %>
57
+ passenger_enabled on;
44
58
 
45
59
  <% if mod_rewrite? %>
46
60
  # this serves static files that exist without running other rewrite tests
@@ -2,8 +2,8 @@ module Capistrano
2
2
  module Extensions
3
3
  module Version #:nodoc:
4
4
  MAJOR = 1
5
- MINOR = 11
6
- TINY = 3
5
+ MINOR = 12
6
+ TINY = 0
7
7
 
8
8
  ARRAY = [MAJOR, MINOR, TINY]
9
9
  STRING = ARRAY.join(".")
@@ -22,8 +22,13 @@ describe Nginx do
22
22
  end
23
23
 
24
24
  it "should render the correct file" do
25
- expected_result = File.read File.join(RENDERED_TEMPLATES_PATH, 'nginx_php_fpm.conf')
26
- subject.render.should == expected_result
25
+ rendered_template = File.join(RENDERED_TEMPLATES_PATH, 'nginx_php_fpm.conf')
26
+ if ENV['write_templates'] == 'yes'
27
+ File.open(rendered_template, 'w') { |f| f.write(subject.render) }
28
+ else
29
+ expected_result = File.read rendered_template
30
+ subject.render.should == expected_result
31
+ end
27
32
  end
28
33
  end
29
34
 
@@ -41,8 +46,13 @@ describe Nginx do
41
46
  end
42
47
 
43
48
  it "should render the correct file" do
44
- expected_result = File.read File.join(RENDERED_TEMPLATES_PATH, 'nginx_passenger.conf')
45
- subject.render.should == expected_result
49
+ rendered_template = File.join(RENDERED_TEMPLATES_PATH, 'nginx_passenger.conf')
50
+ if ENV['write_templates'] == 'yes'
51
+ File.open(rendered_template, 'w') { |f| f.write(subject.render) }
52
+ else
53
+ expected_result = File.read rendered_template
54
+ subject.render.should == expected_result
55
+ end
46
56
  end
47
57
  end
48
58
 
@@ -60,8 +70,13 @@ describe Nginx do
60
70
  end
61
71
 
62
72
  it "should render the correct file" do
63
- expected_result = File.read File.join(RENDERED_TEMPLATES_PATH, 'nginx_reverse_proxy_socket.conf')
64
- subject.render.should == expected_result
73
+ rendered_template = File.join(RENDERED_TEMPLATES_PATH, 'nginx_reverse_proxy_socket.conf')
74
+ if ENV['write_templates'] == 'yes'
75
+ File.open(rendered_template, 'w') { |f| f.write(subject.render) }
76
+ else
77
+ expected_result = File.read rendered_template
78
+ subject.render.should == expected_result
79
+ end
65
80
  end
66
81
  end
67
82
 
@@ -81,8 +96,13 @@ describe Nginx do
81
96
  end
82
97
 
83
98
  it "should render the correct file" do
84
- expected_result = File.read File.join(RENDERED_TEMPLATES_PATH, 'nginx_reverse_proxy_address.conf')
85
- subject.render.should == expected_result
99
+ rendered_template = File.join(RENDERED_TEMPLATES_PATH, 'nginx_reverse_proxy_address.conf')
100
+ if ENV['write_templates'] == 'yes'
101
+ File.open(rendered_template, 'w') { |f| f.write(subject.render) }
102
+ else
103
+ expected_result = File.read rendered_template
104
+ subject.render.should == expected_result
105
+ end
86
106
  end
87
107
  end
88
108