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.
- 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
|
|