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.
- data/.todo +9 -0
- data/README.md +3 -3
- data/TODO +3 -0
- data/examples/php_fpm/deploy/development.rb +16 -0
- data/examples/php_fpm/deploy/production.rb +16 -0
- data/examples/php_fpm/deploy/staging.rb +16 -0
- data/examples/rails_passenger/deploy/development.rb +16 -0
- data/examples/rails_passenger/deploy/production.rb +16 -0
- data/examples/rails_passenger/deploy/staging.rb +16 -0
- data/examples/rails_reverse_proxy/deploy/development.rb +16 -0
- data/examples/rails_reverse_proxy/deploy/production.rb +16 -0
- data/examples/rails_reverse_proxy/deploy/staging.rb +16 -0
- data/lib/capistrano-exts.rb +1 -1
- data/lib/capistrano-exts/receipts.rb +10 -1
- data/lib/capistrano-exts/receipts/contao.rb +1 -5
- data/lib/capistrano-exts/receipts/contents.rb +2 -6
- data/lib/capistrano-exts/receipts/deploy.rb +5 -9
- data/lib/capistrano-exts/receipts/files.rb +0 -4
- data/lib/capistrano-exts/receipts/functions.rb +1 -4
- data/lib/capistrano-exts/receipts/git.rb +0 -2
- data/lib/capistrano-exts/receipts/god.rb +0 -2
- data/lib/capistrano-exts/receipts/multistage.rb +0 -2
- data/lib/capistrano-exts/receipts/mysql.rb +5 -10
- data/lib/capistrano-exts/receipts/rails.rb +0 -4
- data/lib/capistrano-exts/receipts/servers.rb +1 -3
- data/lib/capistrano-exts/receipts/servers/web_server.rb +1 -1
- data/lib/capistrano-exts/receipts/unicorn.rb +28 -21
- data/lib/capistrano-exts/receipts/web.rb +136 -0
- data/lib/capistrano-exts/servers/web_server.rb +5 -5
- data/lib/capistrano-exts/templates/maintenance/index.rhtml +26 -0
- data/lib/capistrano-exts/templates/maintenance/stylesheets/styles.css +37 -0
- data/lib/capistrano-exts/templates/multistage.rb +16 -0
- data/lib/capistrano-exts/templates/web_servers/nginx.conf.erb +25 -11
- data/lib/capistrano-exts/version.rb +2 -2
- data/spec/{requests → acceptance}/nginx_spec.rb +28 -8
- data/spec/rendered_templates/nginx_passenger.conf +24 -10
- data/spec/rendered_templates/nginx_php_fpm.conf +25 -6
- data/spec/rendered_templates/nginx_reverse_proxy_address.conf +25 -6
- data/spec/rendered_templates/nginx_reverse_proxy_socket.conf +25 -6
- metadata +23 -20
@@ -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
|
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)
|
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
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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?
|
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?
|
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?
|
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?
|
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?
|
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
|
-
#
|
25
|
-
|
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
|
-
|
29
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
38
|
-
|
48
|
+
# Rewrite all the requests to the maintenance page if it exists.
|
49
|
+
rewrite ^(.*)$ /__system__/maintenance/index.html break;
|
50
|
+
}
|
39
51
|
|
40
|
-
#
|
52
|
+
# Error Pages
|
41
53
|
error_page 404 /404.html;
|
42
54
|
error_page 500 502 504 /500.html;
|
43
|
-
|
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
|
@@ -22,8 +22,13 @@ describe Nginx do
|
|
22
22
|
end
|
23
23
|
|
24
24
|
it "should render the correct file" do
|
25
|
-
|
26
|
-
|
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
|
-
|
45
|
-
|
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
|
-
|
64
|
-
|
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
|
-
|
85
|
-
|
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
|
|