capistrano 2.0.0 → 2.15.5
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.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.travis.yml +7 -0
- data/CHANGELOG +737 -18
- data/Gemfile +13 -0
- data/README.md +94 -0
- data/Rakefile +11 -0
- data/bin/cap +0 -0
- data/bin/capify +37 -22
- data/capistrano.gemspec +40 -0
- data/lib/capistrano/callback.rb +5 -1
- data/lib/capistrano/cli/execute.rb +10 -7
- data/lib/capistrano/cli/help.rb +39 -16
- data/lib/capistrano/cli/help.txt +44 -16
- data/lib/capistrano/cli/options.rb +71 -11
- data/lib/capistrano/cli/ui.rb +13 -1
- data/lib/capistrano/cli.rb +5 -5
- data/lib/capistrano/command.rb +215 -58
- data/lib/capistrano/configuration/actions/file_transfer.rb +29 -14
- data/lib/capistrano/configuration/actions/inspect.rb +3 -3
- data/lib/capistrano/configuration/actions/invocation.rb +224 -22
- data/lib/capistrano/configuration/alias_task.rb +26 -0
- data/lib/capistrano/configuration/callbacks.rb +26 -27
- data/lib/capistrano/configuration/connections.rb +130 -52
- data/lib/capistrano/configuration/execution.rb +34 -18
- data/lib/capistrano/configuration/loading.rb +99 -6
- data/lib/capistrano/configuration/log_formatters.rb +75 -0
- data/lib/capistrano/configuration/namespaces.rb +45 -12
- data/lib/capistrano/configuration/roles.rb +28 -2
- data/lib/capistrano/configuration/servers.rb +51 -10
- data/lib/capistrano/configuration/variables.rb +3 -3
- data/lib/capistrano/configuration.rb +20 -4
- data/lib/capistrano/errors.rb +12 -8
- data/lib/capistrano/ext/multistage.rb +67 -0
- data/lib/capistrano/ext/string.rb +5 -0
- data/lib/capistrano/extensions.rb +1 -1
- data/lib/capistrano/fix_rake_deprecated_dsl.rb +8 -0
- data/lib/capistrano/logger.rb +112 -5
- data/lib/capistrano/processable.rb +55 -0
- data/lib/capistrano/recipes/compat.rb +2 -2
- data/lib/capistrano/recipes/deploy/assets.rb +201 -0
- data/lib/capistrano/recipes/deploy/dependencies.rb +2 -2
- data/lib/capistrano/recipes/deploy/local_dependency.rb +10 -2
- data/lib/capistrano/recipes/deploy/remote_dependency.rb +54 -2
- data/lib/capistrano/recipes/deploy/scm/accurev.rb +169 -0
- data/lib/capistrano/recipes/deploy/scm/base.rb +31 -11
- data/lib/capistrano/recipes/deploy/scm/bzr.rb +14 -14
- data/lib/capistrano/recipes/deploy/scm/cvs.rb +11 -9
- data/lib/capistrano/recipes/deploy/scm/darcs.rb +12 -1
- data/lib/capistrano/recipes/deploy/scm/git.rb +293 -0
- data/lib/capistrano/recipes/deploy/scm/mercurial.rb +23 -15
- data/lib/capistrano/recipes/deploy/scm/none.rb +55 -0
- data/lib/capistrano/recipes/deploy/scm/perforce.rb +54 -28
- data/lib/capistrano/recipes/deploy/scm/subversion.rb +36 -18
- data/lib/capistrano/recipes/deploy/scm.rb +1 -1
- data/lib/capistrano/recipes/deploy/strategy/base.rb +32 -4
- data/lib/capistrano/recipes/deploy/strategy/copy.rb +238 -43
- data/lib/capistrano/recipes/deploy/strategy/remote.rb +1 -1
- data/lib/capistrano/recipes/deploy/strategy/remote_cache.rb +11 -1
- data/lib/capistrano/recipes/deploy/strategy/unshared_remote_cache.rb +21 -0
- data/lib/capistrano/recipes/deploy/strategy.rb +1 -1
- data/lib/capistrano/recipes/deploy.rb +265 -123
- data/lib/capistrano/recipes/standard.rb +1 -1
- data/lib/capistrano/role.rb +102 -0
- data/lib/capistrano/server_definition.rb +6 -1
- data/lib/capistrano/shell.rb +30 -33
- data/lib/capistrano/ssh.rb +46 -60
- data/lib/capistrano/task_definition.rb +16 -8
- data/lib/capistrano/transfer.rb +218 -0
- data/lib/capistrano/version.rb +6 -17
- data/lib/capistrano.rb +4 -1
- data/test/cli/execute_test.rb +3 -3
- data/test/cli/help_test.rb +33 -7
- data/test/cli/options_test.rb +109 -6
- data/test/cli/ui_test.rb +2 -2
- data/test/cli_test.rb +3 -3
- data/test/command_test.rb +144 -124
- data/test/configuration/actions/file_transfer_test.rb +41 -20
- data/test/configuration/actions/inspect_test.rb +21 -7
- data/test/configuration/actions/invocation_test.rb +123 -30
- data/test/configuration/alias_task_test.rb +118 -0
- data/test/configuration/callbacks_test.rb +41 -46
- data/test/configuration/connections_test.rb +187 -36
- data/test/configuration/execution_test.rb +18 -2
- data/test/configuration/loading_test.rb +33 -4
- data/test/configuration/namespace_dsl_test.rb +54 -5
- data/test/configuration/roles_test.rb +114 -4
- data/test/configuration/servers_test.rb +97 -4
- data/test/configuration/variables_test.rb +12 -2
- data/test/configuration_test.rb +9 -13
- data/test/deploy/local_dependency_test.rb +76 -0
- data/test/deploy/remote_dependency_test.rb +146 -0
- data/test/deploy/scm/accurev_test.rb +23 -0
- data/test/deploy/scm/base_test.rb +1 -1
- data/test/deploy/scm/bzr_test.rb +51 -0
- data/test/deploy/scm/darcs_test.rb +37 -0
- data/test/deploy/scm/git_test.rb +274 -0
- data/test/deploy/scm/mercurial_test.rb +134 -0
- data/test/deploy/scm/none_test.rb +35 -0
- data/test/deploy/scm/perforce_test.rb +23 -0
- data/test/deploy/scm/subversion_test.rb +68 -0
- data/test/deploy/strategy/copy_test.rb +240 -26
- data/test/extensions_test.rb +2 -2
- data/test/logger_formatting_test.rb +149 -0
- data/test/logger_test.rb +13 -2
- data/test/recipes_test.rb +25 -0
- data/test/role_test.rb +11 -0
- data/test/server_definition_test.rb +15 -2
- data/test/shell_test.rb +33 -1
- data/test/ssh_test.rb +40 -24
- data/test/task_definition_test.rb +18 -2
- data/test/transfer_test.rb +168 -0
- data/test/utils.rb +28 -33
- metadata +215 -101
- data/MIT-LICENSE +0 -20
- data/README +0 -43
- data/examples/sample.rb +0 -14
- data/lib/capistrano/gateway.rb +0 -131
- data/lib/capistrano/recipes/deploy/templates/maintenance.rhtml +0 -53
- data/lib/capistrano/recipes/upgrade.rb +0 -33
- data/lib/capistrano/upload.rb +0 -146
- data/test/gateway_test.rb +0 -167
- data/test/upload_test.rb +0 -131
- data/test/version_test.rb +0 -24
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
3
|
-
require
|
|
1
|
+
require "benchmark"
|
|
2
|
+
require "set"
|
|
3
|
+
require "shellwords"
|
|
4
|
+
require "yaml"
|
|
5
|
+
require "capistrano/recipes/deploy/scm"
|
|
6
|
+
require "capistrano/recipes/deploy/strategy"
|
|
4
7
|
|
|
5
8
|
def _cset(name, *args, &block)
|
|
6
9
|
unless exists?(name)
|
|
@@ -21,12 +24,15 @@ _cset(:repository) { abort "Please specify the repository that houses your appl
|
|
|
21
24
|
# are not sufficient.
|
|
22
25
|
# =========================================================================
|
|
23
26
|
|
|
24
|
-
_cset
|
|
27
|
+
_cset(:scm) { scm_default }
|
|
25
28
|
_cset :deploy_via, :checkout
|
|
26
29
|
|
|
27
30
|
_cset(:deploy_to) { "/u/apps/#{application}" }
|
|
28
31
|
_cset(:revision) { source.head }
|
|
29
32
|
|
|
33
|
+
_cset :rails_env, "production"
|
|
34
|
+
_cset :rake, "rake"
|
|
35
|
+
|
|
30
36
|
# =========================================================================
|
|
31
37
|
# These variables should NOT be changed unless you are very confident in
|
|
32
38
|
# what you are doing. Make sure you understand all the implications of your
|
|
@@ -34,23 +40,30 @@ _cset(:revision) { source.head }
|
|
|
34
40
|
# =========================================================================
|
|
35
41
|
|
|
36
42
|
_cset(:source) { Capistrano::Deploy::SCM.new(scm, self) }
|
|
37
|
-
_cset(:real_revision) { source.local.query_revision(revision) { |cmd| with_env("LC_ALL", "C") {
|
|
43
|
+
_cset(:real_revision) { source.local.query_revision(revision) { |cmd| with_env("LC_ALL", "C") { run_locally(cmd) } } }
|
|
38
44
|
|
|
39
45
|
_cset(:strategy) { Capistrano::Deploy::Strategy.new(deploy_via, self) }
|
|
40
46
|
|
|
47
|
+
# If overriding release name, please also select an appropriate setting for :releases below.
|
|
41
48
|
_cset(:release_name) { set :deploy_timestamped, true; Time.now.utc.strftime("%Y%m%d%H%M%S") }
|
|
42
|
-
|
|
43
|
-
_cset
|
|
44
|
-
_cset
|
|
49
|
+
|
|
50
|
+
_cset :version_dir, "releases"
|
|
51
|
+
_cset :shared_dir, "shared"
|
|
52
|
+
_cset :shared_children, %w(public/system log tmp/pids)
|
|
53
|
+
_cset :current_dir, "current"
|
|
54
|
+
|
|
55
|
+
_cset(:releases_path) { File.join(deploy_to, version_dir) }
|
|
56
|
+
_cset(:shared_path) { File.join(deploy_to, shared_dir) }
|
|
57
|
+
_cset(:current_path) { File.join(deploy_to, current_dir) }
|
|
45
58
|
_cset(:release_path) { File.join(releases_path, release_name) }
|
|
46
59
|
|
|
47
|
-
_cset(:releases) { capture("ls -x #{releases_path}").split.sort }
|
|
48
|
-
_cset(:current_release) { File.join(releases_path, releases.last) }
|
|
49
|
-
_cset(:previous_release) { File.join(releases_path, releases[-2]) }
|
|
60
|
+
_cset(:releases) { capture("#{try_sudo} ls -x #{releases_path}", :except => { :no_release => true }).split.sort }
|
|
61
|
+
_cset(:current_release) { releases.length > 0 ? File.join(releases_path, releases.last) : nil }
|
|
62
|
+
_cset(:previous_release) { releases.length > 1 ? File.join(releases_path, releases[-2]) : nil }
|
|
50
63
|
|
|
51
|
-
_cset(:current_revision) { capture("cat #{current_path}/REVISION").chomp }
|
|
52
|
-
_cset(:latest_revision) { capture("cat #{current_release}/REVISION").chomp }
|
|
53
|
-
_cset(:previous_revision) { capture("cat #{previous_release}/REVISION").chomp }
|
|
64
|
+
_cset(:current_revision) { capture("#{try_sudo} cat #{current_path}/REVISION", :except => { :no_release => true }).chomp }
|
|
65
|
+
_cset(:latest_revision) { capture("#{try_sudo} cat #{current_release}/REVISION", :except => { :no_release => true }).chomp }
|
|
66
|
+
_cset(:previous_revision) { capture("#{try_sudo} cat #{previous_release}/REVISION", :except => { :no_release => true }).chomp if previous_release }
|
|
54
67
|
|
|
55
68
|
_cset(:run_method) { fetch(:use_sudo, true) ? :sudo : :run }
|
|
56
69
|
|
|
@@ -61,10 +74,39 @@ _cset(:run_method) { fetch(:use_sudo, true) ? :sudo : :run }
|
|
|
61
74
|
# standalone case, or during deployment.
|
|
62
75
|
_cset(:latest_release) { exists?(:deploy_timestamped) ? release_path : current_release }
|
|
63
76
|
|
|
77
|
+
_cset :maintenance_basename, "maintenance"
|
|
78
|
+
_cset(:maintenance_template_path) { File.join(File.dirname(__FILE__), "templates", "maintenance.rhtml") }
|
|
64
79
|
# =========================================================================
|
|
65
80
|
# These are helper methods that will be available to your recipes.
|
|
66
81
|
# =========================================================================
|
|
67
82
|
|
|
83
|
+
# Checks known version control directories to intelligently set the version
|
|
84
|
+
# control in-use. For example, if a .svn directory exists in the project,
|
|
85
|
+
# it will set the :scm variable to :subversion, if a .git directory exists
|
|
86
|
+
# in the project, it will set the :scm variable to :git and so on. If no
|
|
87
|
+
# directory is found, it will default to :git.
|
|
88
|
+
def scm_default
|
|
89
|
+
if File.exist? '.git'
|
|
90
|
+
:git
|
|
91
|
+
elsif File.exist? '.accurev'
|
|
92
|
+
:accurev
|
|
93
|
+
elsif File.exist? '.bzr'
|
|
94
|
+
:bzr
|
|
95
|
+
elsif File.exist? '.cvs'
|
|
96
|
+
:cvs
|
|
97
|
+
elsif File.exist? '_darcs'
|
|
98
|
+
:darcs
|
|
99
|
+
elsif File.exist? '.hg'
|
|
100
|
+
:mercurial
|
|
101
|
+
elsif File.exist? '.perforce'
|
|
102
|
+
:perforce
|
|
103
|
+
elsif File.exist? '.svn'
|
|
104
|
+
:subversion
|
|
105
|
+
else
|
|
106
|
+
:none
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
68
110
|
# Auxiliary helper method for the `deploy:check' task. Lets you set up your
|
|
69
111
|
# own dependencies.
|
|
70
112
|
def depend(location, type, *args)
|
|
@@ -84,6 +126,64 @@ ensure
|
|
|
84
126
|
ENV[name] = saved
|
|
85
127
|
end
|
|
86
128
|
|
|
129
|
+
# logs the command then executes it locally.
|
|
130
|
+
# returns the command output as a string
|
|
131
|
+
def run_locally(cmd)
|
|
132
|
+
if dry_run
|
|
133
|
+
return logger.debug "executing locally: #{cmd.inspect}"
|
|
134
|
+
end
|
|
135
|
+
logger.trace "executing locally: #{cmd.inspect}" if logger
|
|
136
|
+
output_on_stdout = nil
|
|
137
|
+
elapsed = Benchmark.realtime do
|
|
138
|
+
output_on_stdout = `#{cmd}`
|
|
139
|
+
end
|
|
140
|
+
if $?.to_i > 0 # $? is command exit code (posix style)
|
|
141
|
+
raise Capistrano::LocalArgumentError, "Command #{cmd} returned status code #{$?}"
|
|
142
|
+
end
|
|
143
|
+
logger.trace "command finished in #{(elapsed * 1000).round}ms" if logger
|
|
144
|
+
output_on_stdout
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# If a command is given, this will try to execute the given command, as
|
|
149
|
+
# described below. Otherwise, it will return a string for use in embedding in
|
|
150
|
+
# another command, for executing that command as described below.
|
|
151
|
+
#
|
|
152
|
+
# If :run_method is :sudo (or :use_sudo is true), this executes the given command
|
|
153
|
+
# via +sudo+. Otherwise is uses +run+. If :as is given as a key, it will be
|
|
154
|
+
# passed as the user to sudo as, if using sudo. If the :as key is not given,
|
|
155
|
+
# it will default to whatever the value of the :admin_runner variable is,
|
|
156
|
+
# which (by default) is unset.
|
|
157
|
+
#
|
|
158
|
+
# THUS, if you want to try to run something via sudo, and what to use the
|
|
159
|
+
# root user, you'd just to try_sudo('something'). If you wanted to try_sudo as
|
|
160
|
+
# someone else, you'd just do try_sudo('something', :as => "bob"). If you
|
|
161
|
+
# always wanted sudo to run as a particular user, you could do
|
|
162
|
+
# set(:admin_runner, "bob").
|
|
163
|
+
def try_sudo(*args)
|
|
164
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
|
165
|
+
command = args.shift
|
|
166
|
+
raise ArgumentError, "too many arguments" if args.any?
|
|
167
|
+
|
|
168
|
+
as = options.fetch(:as, fetch(:admin_runner, nil))
|
|
169
|
+
via = fetch(:run_method, :sudo)
|
|
170
|
+
if command
|
|
171
|
+
invoke_command(command, :via => via, :as => as)
|
|
172
|
+
elsif via == :sudo
|
|
173
|
+
sudo(:as => as)
|
|
174
|
+
else
|
|
175
|
+
""
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Same as sudo, but tries sudo with :as set to the value of the :runner
|
|
180
|
+
# variable (which defaults to "app").
|
|
181
|
+
def try_runner(*args)
|
|
182
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
|
183
|
+
args << options.merge(:as => fetch(:runner, "app"))
|
|
184
|
+
try_sudo(*args)
|
|
185
|
+
end
|
|
186
|
+
|
|
87
187
|
# =========================================================================
|
|
88
188
|
# These are the tasks that are available to help with deploying web apps,
|
|
89
189
|
# and specifically, Rails applications. You can have cap give you a summary
|
|
@@ -105,19 +205,20 @@ namespace :deploy do
|
|
|
105
205
|
desc <<-DESC
|
|
106
206
|
Prepares one or more servers for deployment. Before you can use any \
|
|
107
207
|
of the Capistrano deployment tasks with your project, you will need to \
|
|
108
|
-
make sure all of your servers have been prepared with `cap setup'. When \
|
|
208
|
+
make sure all of your servers have been prepared with `cap deploy:setup'. When \
|
|
109
209
|
you add a new server to your cluster, you can easily run the setup task \
|
|
110
210
|
on just that server by specifying the HOSTS environment variable:
|
|
111
211
|
|
|
112
|
-
$ cap HOSTS=new.server.com setup
|
|
212
|
+
$ cap HOSTS=new.server.com deploy:setup
|
|
113
213
|
|
|
114
214
|
It is safe to run this task on servers that have already been set up; it \
|
|
115
215
|
will not destroy any deployed revisions or data.
|
|
116
216
|
DESC
|
|
117
217
|
task :setup, :except => { :no_release => true } do
|
|
118
218
|
dirs = [deploy_to, releases_path, shared_path]
|
|
119
|
-
dirs +=
|
|
120
|
-
run "
|
|
219
|
+
dirs += shared_children.map { |d| File.join(shared_path, d.split('/').last) }
|
|
220
|
+
run "#{try_sudo} mkdir -p #{dirs.join(' ')}"
|
|
221
|
+
run "#{try_sudo} chmod g+w #{dirs.join(' ')}" if fetch(:group_writable, true)
|
|
121
222
|
end
|
|
122
223
|
|
|
123
224
|
desc <<-DESC
|
|
@@ -131,7 +232,7 @@ namespace :deploy do
|
|
|
131
232
|
task :update do
|
|
132
233
|
transaction do
|
|
133
234
|
update_code
|
|
134
|
-
|
|
235
|
+
create_symlink
|
|
135
236
|
end
|
|
136
237
|
end
|
|
137
238
|
|
|
@@ -164,25 +265,47 @@ namespace :deploy do
|
|
|
164
265
|
symlinks to the shared directory for the log, system, and tmp/pids \
|
|
165
266
|
directories, and will lastly touch all assets in public/images, \
|
|
166
267
|
public/stylesheets, and public/javascripts so that the times are \
|
|
167
|
-
consistent (so that asset timestamping works).
|
|
268
|
+
consistent (so that asset timestamping works). This touch process \
|
|
269
|
+
is only carried out if the :normalize_asset_timestamps variable is \
|
|
270
|
+
set to true, which is the default. The asset directories can be overridden \
|
|
271
|
+
using the :public_children variable.
|
|
168
272
|
DESC
|
|
169
273
|
task :finalize_update, :except => { :no_release => true } do
|
|
170
|
-
|
|
274
|
+
escaped_release = latest_release.to_s.shellescape
|
|
275
|
+
commands = []
|
|
276
|
+
commands << "chmod -R -- g+w #{escaped_release}" if fetch(:group_writable, true)
|
|
171
277
|
|
|
172
278
|
# mkdir -p is making sure that the directories are there for some SCM's that don't
|
|
173
279
|
# save empty folders
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
run
|
|
280
|
+
shared_children.map do |dir|
|
|
281
|
+
d = dir.shellescape
|
|
282
|
+
if (dir.rindex('/')) then
|
|
283
|
+
commands += ["rm -rf -- #{escaped_release}/#{d}",
|
|
284
|
+
"mkdir -p -- #{escaped_release}/#{dir.slice(0..(dir.rindex('/'))).shellescape}"]
|
|
285
|
+
else
|
|
286
|
+
commands << "rm -rf -- #{escaped_release}/#{d}"
|
|
287
|
+
end
|
|
288
|
+
commands << "ln -s -- #{shared_path}/#{dir.split('/').last.shellescape} #{escaped_release}/#{d}"
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
run commands.join(' && ') if commands.any?
|
|
292
|
+
|
|
293
|
+
if fetch(:normalize_asset_timestamps, true)
|
|
294
|
+
stamp = Time.now.utc.strftime("%Y%m%d%H%M.%S")
|
|
295
|
+
asset_paths = fetch(:public_children, %w(images stylesheets javascripts)).
|
|
296
|
+
map { |p| "#{latest_release}/public/#{p}" }.
|
|
297
|
+
map { |p| p.shellescape }
|
|
298
|
+
run("find #{asset_paths.join(" ")} -exec touch -t #{stamp} -- {} ';'; true",
|
|
299
|
+
:env => { "TZ" => "UTC" }) if asset_paths.any?
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
desc <<-DESC
|
|
304
|
+
Deprecated API. This has become deploy:create_symlink, please update your recipes
|
|
305
|
+
DESC
|
|
306
|
+
task :symlink, :except => { :no_release => true } do
|
|
307
|
+
Kernel.warn "[Deprecation Warning] This API has changed, please hook `deploy:create_symlink` instead of `deploy:symlink`."
|
|
308
|
+
create_symlink
|
|
186
309
|
end
|
|
187
310
|
|
|
188
311
|
desc <<-DESC
|
|
@@ -194,9 +317,16 @@ namespace :deploy do
|
|
|
194
317
|
deploy, including `restart') or the 'update' task (which does everything \
|
|
195
318
|
except `restart').
|
|
196
319
|
DESC
|
|
197
|
-
task :
|
|
198
|
-
on_rollback
|
|
199
|
-
|
|
320
|
+
task :create_symlink, :except => { :no_release => true } do
|
|
321
|
+
on_rollback do
|
|
322
|
+
if previous_release
|
|
323
|
+
run "#{try_sudo} rm -f #{current_path}; #{try_sudo} ln -s #{previous_release} #{current_path}; true"
|
|
324
|
+
else
|
|
325
|
+
logger.important "no previous release to rollback to, rollback of symlink skipped"
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
run "#{try_sudo} rm -f #{current_path} && #{try_sudo} ln -s #{latest_release} #{current_path}"
|
|
200
330
|
end
|
|
201
331
|
|
|
202
332
|
desc <<-DESC
|
|
@@ -209,60 +339,73 @@ namespace :deploy do
|
|
|
209
339
|
To use this task, specify the files and directories you want to copy as a \
|
|
210
340
|
comma-delimited list in the FILES environment variable. All directories \
|
|
211
341
|
will be processed recursively, with all files being pushed to the \
|
|
212
|
-
deployment servers.
|
|
213
|
-
will be ignored.
|
|
342
|
+
deployment servers.
|
|
214
343
|
|
|
215
344
|
$ cap deploy:upload FILES=templates,controller.rb
|
|
345
|
+
|
|
346
|
+
Dir globs are also supported:
|
|
347
|
+
|
|
348
|
+
$ cap deploy:upload FILES='config/apache/*.conf'
|
|
216
349
|
DESC
|
|
217
350
|
task :upload, :except => { :no_release => true } do
|
|
218
|
-
files = (ENV["FILES"] || "").
|
|
219
|
-
|
|
220
|
-
map { |f| f.strip!; File.directory?(f) ? Dir["#{f}/**/*"] : f }.
|
|
221
|
-
flatten.
|
|
222
|
-
reject { |f| File.directory?(f) || File.basename(f)[0] == ?. }
|
|
223
|
-
|
|
224
|
-
abort "Please specify at least one file to update (via the FILES environment variable)" if files.empty?
|
|
351
|
+
files = (ENV["FILES"] || "").split(",").map { |f| Dir[f.strip] }.flatten
|
|
352
|
+
abort "Please specify at least one file or directory to update (via the FILES environment variable)" if files.empty?
|
|
225
353
|
|
|
226
|
-
files.each
|
|
227
|
-
put File.read(file), File.join(current_path, file)
|
|
228
|
-
end
|
|
354
|
+
files.each { |file| top.upload(file, File.join(current_path, file)) }
|
|
229
355
|
end
|
|
230
356
|
|
|
231
357
|
desc <<-DESC
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
but if you are in an environment where sudo is not an option, or is not \
|
|
235
|
-
allowed, you can indicate that restarts should use `run' instead by \
|
|
236
|
-
setting the `use_sudo' variable to false:
|
|
237
|
-
|
|
238
|
-
set :use_sudo, false
|
|
358
|
+
Blank task exists as a hook into which to install your own environment \
|
|
359
|
+
specific behaviour.
|
|
239
360
|
DESC
|
|
240
361
|
task :restart, :roles => :app, :except => { :no_release => true } do
|
|
241
|
-
|
|
362
|
+
# Empty Task to overload with your platform specifics
|
|
242
363
|
end
|
|
243
364
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
365
|
+
namespace :rollback do
|
|
366
|
+
desc <<-DESC
|
|
367
|
+
[internal] Points the current symlink at the previous revision.
|
|
368
|
+
This is called by the rollback sequence, and should rarely (if
|
|
369
|
+
ever) need to be called directly.
|
|
370
|
+
DESC
|
|
371
|
+
task :revision, :except => { :no_release => true } do
|
|
372
|
+
if previous_release
|
|
373
|
+
run "#{try_sudo} rm #{current_path}; #{try_sudo} ln -s #{previous_release} #{current_path}"
|
|
374
|
+
else
|
|
375
|
+
abort "could not rollback the code because there is no prior release"
|
|
376
|
+
end
|
|
255
377
|
end
|
|
256
|
-
end
|
|
257
378
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
379
|
+
desc <<-DESC
|
|
380
|
+
[internal] Removes the most recently deployed release.
|
|
381
|
+
This is called by the rollback sequence, and should rarely
|
|
382
|
+
(if ever) need to be called directly.
|
|
383
|
+
DESC
|
|
384
|
+
task :cleanup, :except => { :no_release => true } do
|
|
385
|
+
run "if [ `readlink #{current_path}` != #{current_release} ]; then #{try_sudo} rm -rf #{current_release}; fi"
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
desc <<-DESC
|
|
389
|
+
Rolls back to the previously deployed version. The `current' symlink will \
|
|
390
|
+
be updated to point at the previously deployed version, and then the \
|
|
391
|
+
current release will be removed from the servers. You'll generally want \
|
|
392
|
+
to call `rollback' instead, as it performs a `restart' as well.
|
|
393
|
+
DESC
|
|
394
|
+
task :code, :except => { :no_release => true } do
|
|
395
|
+
revision
|
|
396
|
+
cleanup
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
desc <<-DESC
|
|
400
|
+
Rolls back to a previous version and restarts. This is handy if you ever \
|
|
401
|
+
discover that you've deployed a lemon; `cap rollback' and you're right \
|
|
402
|
+
back where you were, on the previously deployed version.
|
|
403
|
+
DESC
|
|
404
|
+
task :default do
|
|
405
|
+
revision
|
|
406
|
+
restart
|
|
407
|
+
cleanup
|
|
408
|
+
end
|
|
266
409
|
end
|
|
267
410
|
|
|
268
411
|
desc <<-DESC
|
|
@@ -288,11 +431,11 @@ namespace :deploy do
|
|
|
288
431
|
|
|
289
432
|
directory = case migrate_target.to_sym
|
|
290
433
|
when :current then current_path
|
|
291
|
-
when :latest then
|
|
434
|
+
when :latest then latest_release
|
|
292
435
|
else raise ArgumentError, "unknown migration target #{migrate_target.inspect}"
|
|
293
436
|
end
|
|
294
437
|
|
|
295
|
-
run "cd #{directory}
|
|
438
|
+
run "cd #{directory} && #{rake} RAILS_ENV=#{rails_env} #{migrate_env} db:migrate"
|
|
296
439
|
end
|
|
297
440
|
|
|
298
441
|
desc <<-DESC
|
|
@@ -306,7 +449,7 @@ namespace :deploy do
|
|
|
306
449
|
set :migrate_target, :latest
|
|
307
450
|
update_code
|
|
308
451
|
migrate
|
|
309
|
-
|
|
452
|
+
create_symlink
|
|
310
453
|
restart
|
|
311
454
|
end
|
|
312
455
|
|
|
@@ -319,16 +462,7 @@ namespace :deploy do
|
|
|
319
462
|
DESC
|
|
320
463
|
task :cleanup, :except => { :no_release => true } do
|
|
321
464
|
count = fetch(:keep_releases, 5).to_i
|
|
322
|
-
|
|
323
|
-
logger.important "no old releases to clean up"
|
|
324
|
-
else
|
|
325
|
-
logger.info "keeping #{count} of #{releases.length} deployed releases"
|
|
326
|
-
|
|
327
|
-
directories = (releases - releases.last(count)).map { |release|
|
|
328
|
-
File.join(releases_path, release) }.join(" ")
|
|
329
|
-
|
|
330
|
-
invoke_command "rm -rf #{directories}", :via => run_method
|
|
331
|
-
end
|
|
465
|
+
try_sudo "ls -1dt #{releases_path}/* | tail -n +#{count + 1} | #{try_sudo} xargs rm -rf"
|
|
332
466
|
end
|
|
333
467
|
|
|
334
468
|
desc <<-DESC
|
|
@@ -384,40 +518,19 @@ namespace :deploy do
|
|
|
384
518
|
end
|
|
385
519
|
|
|
386
520
|
desc <<-DESC
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
your application listeners. For Rails applications, you might just have \
|
|
390
|
-
that script invoke `script/process/spawner' with the appropriate \
|
|
391
|
-
arguments.
|
|
392
|
-
|
|
393
|
-
By default, the script will be executed via sudo as the `app' user. If \
|
|
394
|
-
you wish to run it as a different user, set the :runner variable to \
|
|
395
|
-
that user. If you are in an environment where you can't use sudo, set \
|
|
396
|
-
the :use_sudo variable to false.
|
|
521
|
+
Blank task exists as a hook into which to install your own environment \
|
|
522
|
+
specific behaviour.
|
|
397
523
|
DESC
|
|
398
524
|
task :start, :roles => :app do
|
|
399
|
-
|
|
400
|
-
via = fetch(:run_method, :sudo)
|
|
401
|
-
invoke_command "sh -c 'cd #{current_path} && nohup script/spin'", :via => via, :as => as
|
|
525
|
+
# Empty Task to overload with your platform specifics
|
|
402
526
|
end
|
|
403
527
|
|
|
404
528
|
desc <<-DESC
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
spawned. As such, it is fairly Rails specific and may need to be \
|
|
408
|
-
overridden for other systems.
|
|
409
|
-
|
|
410
|
-
By default, the script will be executed via sudo as the `app' user. If \
|
|
411
|
-
you wish to run it as a different user, set the :runner variable to \
|
|
412
|
-
that user. If you are in an environment where you can't use sudo, set \
|
|
413
|
-
the :use_sudo variable to false.
|
|
529
|
+
Blank task exists as a hook into which to install your own environment \
|
|
530
|
+
specific behaviour.
|
|
414
531
|
DESC
|
|
415
532
|
task :stop, :roles => :app do
|
|
416
|
-
|
|
417
|
-
via = fetch(:run_method, :sudo)
|
|
418
|
-
|
|
419
|
-
invoke_command "#{current_path}/script/process/reaper -a kill -r dispatch.spawner.pid", :via => via, :as => as
|
|
420
|
-
invoke_command "#{current_path}/script/process/reaper -a kill", :via => via, :as => as
|
|
533
|
+
# Empty Task to overload with your platform specifics
|
|
421
534
|
end
|
|
422
535
|
|
|
423
536
|
namespace :pending do
|
|
@@ -436,14 +549,15 @@ namespace :deploy do
|
|
|
436
549
|
might not be supported on all SCM's.
|
|
437
550
|
DESC
|
|
438
551
|
task :default, :except => { :no_release => true } do
|
|
439
|
-
|
|
552
|
+
from = source.next_revision(current_revision)
|
|
553
|
+
system(source.local.log(from))
|
|
440
554
|
end
|
|
441
555
|
end
|
|
442
556
|
|
|
443
557
|
namespace :web do
|
|
444
558
|
desc <<-DESC
|
|
445
559
|
Present a maintenance page to visitors. Disables your application's web \
|
|
446
|
-
interface by writing a "
|
|
560
|
+
interface by writing a "#{maintenance_basename}.html" file to each web server. The \
|
|
447
561
|
servers must be configured to detect the presence of this file, and if \
|
|
448
562
|
it is present, always display it instead of performing the request.
|
|
449
563
|
|
|
@@ -455,29 +569,57 @@ namespace :deploy do
|
|
|
455
569
|
REASON="hardware upgrade" \\
|
|
456
570
|
UNTIL="12pm Central Time"
|
|
457
571
|
|
|
572
|
+
You can use a different template for the maintenance page by setting the \
|
|
573
|
+
:maintenance_template_path variable in your deploy.rb file. The template file \
|
|
574
|
+
should either be a plaintext or an erb file.
|
|
575
|
+
|
|
458
576
|
Further customization will require that you write your own task.
|
|
459
577
|
DESC
|
|
460
578
|
task :disable, :roles => :web, :except => { :no_release => true } do
|
|
461
579
|
require 'erb'
|
|
462
|
-
on_rollback { run "rm #{shared_path}/system
|
|
580
|
+
on_rollback { run "rm -f #{shared_path}/system/#{maintenance_basename}.html" }
|
|
581
|
+
|
|
582
|
+
warn <<-EOHTACCESS
|
|
583
|
+
|
|
584
|
+
# Please add something like this to your site's Apache htaccess to redirect users to the maintenance page.
|
|
585
|
+
# More Info: http://www.shiftcommathree.com/articles/make-your-rails-maintenance-page-respond-with-a-503
|
|
586
|
+
|
|
587
|
+
ErrorDocument 503 /system/#{maintenance_basename}.html
|
|
588
|
+
RewriteEngine On
|
|
589
|
+
RewriteCond %{REQUEST_URI} !\.(css|gif|jpg|png)$
|
|
590
|
+
RewriteCond %{DOCUMENT_ROOT}/system/#{maintenance_basename}.html -f
|
|
591
|
+
RewriteCond %{SCRIPT_FILENAME} !#{maintenance_basename}.html
|
|
592
|
+
RewriteRule ^.*$ - [redirect=503,last]
|
|
593
|
+
|
|
594
|
+
# Or if you are using Nginx add this to your server config:
|
|
595
|
+
|
|
596
|
+
if (-f $document_root/system/maintenance.html) {
|
|
597
|
+
return 503;
|
|
598
|
+
}
|
|
599
|
+
error_page 503 @maintenance;
|
|
600
|
+
location @maintenance {
|
|
601
|
+
rewrite ^(.*)$ /system/maintenance.html break;
|
|
602
|
+
break;
|
|
603
|
+
}
|
|
604
|
+
EOHTACCESS
|
|
463
605
|
|
|
464
606
|
reason = ENV['REASON']
|
|
465
607
|
deadline = ENV['UNTIL']
|
|
466
608
|
|
|
467
|
-
template = File.read(
|
|
609
|
+
template = File.read(maintenance_template_path)
|
|
468
610
|
result = ERB.new(template).result(binding)
|
|
469
611
|
|
|
470
|
-
put result, "#{shared_path}/system
|
|
612
|
+
put result, "#{shared_path}/system/#{maintenance_basename}.html", :mode => 0644
|
|
471
613
|
end
|
|
472
614
|
|
|
473
615
|
desc <<-DESC
|
|
474
616
|
Makes the application web-accessible again. Removes the \
|
|
475
|
-
"
|
|
617
|
+
"#{maintenance_basename}.html" page generated by deploy:web:disable, which (if your \
|
|
476
618
|
web servers are configured correctly) will make your application \
|
|
477
619
|
web-accessible again.
|
|
478
620
|
DESC
|
|
479
621
|
task :enable, :roles => :web, :except => { :no_release => true } do
|
|
480
|
-
run "rm #{shared_path}/system
|
|
622
|
+
run "rm -f #{shared_path}/system/#{maintenance_basename}.html"
|
|
481
623
|
end
|
|
482
624
|
end
|
|
483
625
|
end
|