capistrano-exts 1.11.3 → 1.12.0

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