capistrano 2.14.2 → 2.15.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 (68) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +1 -0
  3. data/CHANGELOG +49 -8
  4. data/Gemfile +1 -1
  5. data/README.md +4 -4
  6. data/capistrano.gemspec +9 -17
  7. data/lib/capistrano/callback.rb +1 -1
  8. data/lib/capistrano/cli.rb +1 -1
  9. data/lib/capistrano/cli/execute.rb +3 -3
  10. data/lib/capistrano/cli/help.rb +3 -3
  11. data/lib/capistrano/cli/help.txt +23 -23
  12. data/lib/capistrano/cli/options.rb +12 -12
  13. data/lib/capistrano/command.rb +20 -7
  14. data/lib/capistrano/configuration/actions/invocation.rb +23 -11
  15. data/lib/capistrano/configuration/connections.rb +22 -15
  16. data/lib/capistrano/configuration/execution.rb +2 -2
  17. data/lib/capistrano/configuration/loading.rb +2 -2
  18. data/lib/capistrano/configuration/log_formatters.rb +5 -1
  19. data/lib/capistrano/configuration/roles.rb +1 -1
  20. data/lib/capistrano/configuration/servers.rb +6 -6
  21. data/lib/capistrano/errors.rb +4 -4
  22. data/lib/capistrano/ext/multistage.rb +2 -2
  23. data/lib/capistrano/logger.rb +14 -1
  24. data/lib/capistrano/recipes/deploy.rb +94 -27
  25. data/lib/capistrano/recipes/deploy/assets.rb +21 -18
  26. data/lib/capistrano/recipes/deploy/dependencies.rb +2 -2
  27. data/lib/capistrano/recipes/deploy/remote_dependency.rb +11 -11
  28. data/lib/capistrano/recipes/deploy/scm.rb +1 -1
  29. data/lib/capistrano/recipes/deploy/scm/accurev.rb +6 -6
  30. data/lib/capistrano/recipes/deploy/scm/cvs.rb +4 -4
  31. data/lib/capistrano/recipes/deploy/scm/darcs.rb +3 -3
  32. data/lib/capistrano/recipes/deploy/scm/git.rb +3 -0
  33. data/lib/capistrano/recipes/deploy/scm/mercurial.rb +3 -3
  34. data/lib/capistrano/recipes/deploy/scm/none.rb +9 -5
  35. data/lib/capistrano/recipes/deploy/scm/perforce.rb +5 -5
  36. data/lib/capistrano/recipes/deploy/scm/subversion.rb +1 -1
  37. data/lib/capistrano/recipes/deploy/strategy.rb +1 -1
  38. data/lib/capistrano/recipes/deploy/strategy/base.rb +5 -1
  39. data/lib/capistrano/recipes/deploy/strategy/copy.rb +14 -2
  40. data/lib/capistrano/recipes/standard.rb +1 -1
  41. data/lib/capistrano/server_definition.rb +1 -1
  42. data/lib/capistrano/shell.rb +4 -1
  43. data/lib/capistrano/ssh.rb +1 -1
  44. data/lib/capistrano/version.rb +2 -2
  45. data/test/cli/options_test.rb +1 -1
  46. data/test/cli/ui_test.rb +1 -1
  47. data/test/command_test.rb +11 -1
  48. data/test/configuration/actions/invocation_test.rb +5 -1
  49. data/test/configuration/callbacks_test.rb +1 -1
  50. data/test/configuration/connections_test.rb +19 -0
  51. data/test/configuration/execution_test.rb +1 -1
  52. data/test/configuration/namespace_dsl_test.rb +6 -6
  53. data/test/configuration/roles_test.rb +2 -2
  54. data/test/configuration/servers_test.rb +12 -12
  55. data/test/configuration/variables_test.rb +3 -3
  56. data/test/deploy/scm/bzr_test.rb +1 -1
  57. data/test/deploy/scm/darcs_test.rb +2 -2
  58. data/test/deploy/scm/git_test.rb +10 -0
  59. data/test/deploy/scm/mercurial_test.rb +3 -3
  60. data/test/deploy/scm/perforce_test.rb +1 -1
  61. data/test/deploy/strategy/copy_test.rb +25 -1
  62. data/test/extensions_test.rb +1 -1
  63. data/test/logger_formatting_test.rb +56 -1
  64. data/test/recipes_test.rb +1 -1
  65. data/test/server_definition_test.rb +2 -2
  66. data/test/shell_test.rb +12 -6
  67. data/test/transfer_test.rb +1 -1
  68. metadata +63 -31
@@ -54,7 +54,7 @@ module Capistrano
54
54
  end
55
55
  end
56
56
  end
57
-
57
+
58
58
  def connect_to(server)
59
59
  @options[:logger].debug "establishing connection to `#{server}' via gateway" if @options[:logger]
60
60
  local_host = ServerDefinition.new("127.0.0.1", :user => server.user, :port => gateway_for(server).open(server.host, server.port || 22))
@@ -106,7 +106,7 @@ module Capistrano
106
106
  # establish connections to servers defined via ServerDefinition objects.
107
107
  def connection_factory
108
108
  @connection_factory ||= begin
109
- if exists?(:gateway)
109
+ if exists?(:gateway) && !fetch(:gateway).nil? && !fetch(:gateway).empty?
110
110
  logger.debug "establishing connection to gateway `#{fetch(:gateway).inspect}'"
111
111
  GatewayConnectionFactory.new(fetch(:gateway), self)
112
112
  else
@@ -139,44 +139,51 @@ module Capistrano
139
139
  def teardown_connections_to(servers)
140
140
  servers.each do |server|
141
141
  begin
142
- sessions.delete(server).close
143
- rescue IOError
142
+ session = sessions.delete(server)
143
+ session.close if session
144
+ rescue IOError, Net::SSH::Disconnect
144
145
  # the TCP connection is already dead
145
146
  end
146
147
  end
147
148
  end
148
149
 
149
- # Determines the set of servers within the current task's scope and
150
- # establishes connections to them, and then yields that list of
151
- # servers.
152
- def execute_on_servers(options={})
153
- raise ArgumentError, "expected a block" unless block_given?
154
-
150
+ # Determines the set of servers within the current task's scope
151
+ def filter_servers(options={})
155
152
  if task = current_task
156
153
  servers = find_servers_for_task(task, options)
157
154
 
158
155
  if servers.empty?
159
156
  if ENV['HOSTFILTER'] || task.options.merge(options)[:on_no_matching_servers] == :continue
160
157
  logger.info "skipping `#{task.fully_qualified_name}' because no servers matched"
161
- return
162
158
  else
163
- raise Capistrano::NoMatchingServersError, "`#{task.fully_qualified_name}' is only run for servers matching #{task.options.inspect}, but no servers matched"
159
+ unless dry_run
160
+ raise Capistrano::NoMatchingServersError, "`#{task.fully_qualified_name}' is only run for servers matching #{task.options.inspect}, but no servers matched"
161
+ end
164
162
  end
165
163
  end
166
164
 
167
165
  if task.continue_on_error?
168
166
  servers.delete_if { |s| has_failed?(s) }
169
- return if servers.empty?
170
167
  end
171
168
  else
172
169
  servers = find_servers(options)
173
- if servers.empty?
170
+ if servers.empty? && !dry_run
174
171
  raise Capistrano::NoMatchingServersError, "no servers found to match #{options.inspect}" if options[:on_no_matching_servers] != :continue
175
- return
176
172
  end
177
173
  end
178
174
 
179
175
  servers = [servers.first] if options[:once]
176
+ [task, servers.compact]
177
+ end
178
+
179
+ # Determines the set of servers within the current task's scope and
180
+ # establishes connections to them, and then yields that list of
181
+ # servers.
182
+ def execute_on_servers(options={})
183
+ raise ArgumentError, "expected a block" unless block_given?
184
+
185
+ task, servers = filter_servers(options)
186
+ return if servers.empty?
180
187
  logger.trace "servers: #{servers.map { |s| s.host }.inspect}"
181
188
 
182
189
  max_hosts = (options[:max_hosts] || (task && task.max_hosts) || servers.size).to_i
@@ -27,8 +27,8 @@ module Capistrano
27
27
  def task_call_frames
28
28
  Thread.current[:task_call_frames] ||= []
29
29
  end
30
-
31
-
30
+
31
+
32
32
  # The stack of tasks that have registered rollback handlers within the
33
33
  # current transaction. If this is nil, then there is no transaction
34
34
  # that is currently active.
@@ -141,9 +141,9 @@ module Capistrano
141
141
  # look to see if this specific configuration instance has ever seen
142
142
  # these arguments to require before
143
143
  if @loaded_features.include?(args)
144
- return false
144
+ return false
145
145
  end
146
-
146
+
147
147
  @loaded_features << args
148
148
  begin
149
149
  original_instance, self.class.instance = self.class.instance, self
@@ -63,9 +63,13 @@ module Capistrano
63
63
  end
64
64
  end
65
65
 
66
+ def default_log_formatters(formatters)
67
+ default_formatters = [*formatters]
68
+ end
69
+
66
70
  def disable_log_formatters
67
71
  @logger.disable_formatters = true
68
72
  end
69
73
  end
70
74
  end
71
- end
75
+ end
@@ -68,7 +68,7 @@ module Capistrano
68
68
  raise ArgumentError, "you must associate a server with at least one role" if roles.empty?
69
69
  roles.each { |name| role(name, host, options) }
70
70
  end
71
-
71
+
72
72
  def role_names_for_host(host)
73
73
  roles.map {|role_name, role| role_name if role.include?(host) }.compact || []
74
74
  end
@@ -13,8 +13,8 @@ module Capistrano
13
13
  # The options hash may include a :hosts option (which should specify
14
14
  # an array of host names or ServerDefinition instances), a :roles
15
15
  # option (specifying an array of roles), an :only option (specifying
16
- # a hash of key/value pairs that any matching server must match),
17
- # an :exception option (like :only, but the inverse), and a
16
+ # a hash of key/value pairs that any matching server must match),
17
+ # an :exception option (like :only, but the inverse), and a
18
18
  # :skip_hostfilter option to ignore the HOSTFILTER environment variable
19
19
  # described below.
20
20
  #
@@ -27,7 +27,7 @@ module Capistrano
27
27
  # will limit the result to hosts found in that (comma-separated) list.
28
28
  #
29
29
  # If the HOSTROLEFILTER environment variable is set, it will limit the
30
- # result to hosts found in that (comma-separated) list of roles
30
+ # result to hosts found in that (comma-separated) list of roles
31
31
  #
32
32
  # Usage:
33
33
  #
@@ -44,9 +44,9 @@ module Capistrano
44
44
  def find_servers(options={})
45
45
  return [] if options.key?(:hosts) && (options[:hosts].nil? || [] == options[:hosts])
46
46
  return [] if options.key?(:roles) && (options[:roles].nil? || [] == options[:roles])
47
-
47
+
48
48
  hosts = server_list_from(ENV['HOSTS'] || options[:hosts])
49
-
49
+
50
50
  if hosts.any?
51
51
  if options[:skip_hostfilter]
52
52
  hosts.uniq
@@ -59,7 +59,7 @@ module Capistrano
59
59
 
60
60
  only = options[:only] || {}
61
61
  except = options[:except] || {}
62
-
62
+
63
63
  # If we don't have a def for a role it means its bogus, skip it so higher level can handle
64
64
  servers = roles.inject([]) { |list, role| list.concat(self.roles[role] || []) }
65
65
  servers = servers.select { |server| only.all? { |key,value| server.options[key] == value } }
@@ -1,11 +1,11 @@
1
1
  module Capistrano
2
-
2
+
3
3
  Error = Class.new(RuntimeError)
4
4
 
5
5
  CaptureError = Class.new(Capistrano::Error)
6
6
  NoSuchTaskError = Class.new(Capistrano::Error)
7
7
  NoMatchingServersError = Class.new(Capistrano::Error)
8
-
8
+
9
9
  class RemoteError < Error
10
10
  attr_accessor :hosts
11
11
  end
@@ -13,7 +13,7 @@ module Capistrano
13
13
  ConnectionError = Class.new(Capistrano::RemoteError)
14
14
  TransferError = Class.new(Capistrano::RemoteError)
15
15
  CommandError = Class.new(Capistrano::RemoteError)
16
-
16
+
17
17
  LocalArgumentError = Class.new(Capistrano::Error)
18
-
18
+
19
19
  end
@@ -15,7 +15,7 @@ Capistrano::Configuration.instance.load do
15
15
  desc "Set the target stage to `#{name}'."
16
16
  task(name) do
17
17
  set :stage, name.to_sym
18
- load "#{location}/#{stage}"
18
+ load "#{location}/#{stage}" if File.exist?(File.join(location, "#{stage}.rb"))
19
19
  end
20
20
  end
21
21
 
@@ -39,7 +39,7 @@ Capistrano::Configuration.instance.load do
39
39
  else
40
40
  abort "No stage specified. Please specify one of: #{stages.join(', ')} (e.g. `cap #{stages.first} #{ARGV.last}')"
41
41
  end
42
- end
42
+ end
43
43
  end
44
44
 
45
45
  desc "Stub out the staging config files."
@@ -31,7 +31,7 @@ module Capistrano
31
31
  }
32
32
 
33
33
  # Set up default formatters
34
- @formatters = [
34
+ @default_formatters = [
35
35
  # TRACE
36
36
  { :match => /command finished/, :color => :white, :style => :dim, :level => 3, :priority => -10 },
37
37
  { :match => /executing locally/, :color => :yellow, :level => 3, :priority => -20 },
@@ -49,8 +49,21 @@ module Capistrano
49
49
  { :match => /^err ::/, :color => :red, :level => 0, :priority => -10 },
50
50
  { :match => /.*/, :color => :blue, :level => 0, :priority => -20 }
51
51
  ]
52
+ @formatters = @default_formatters
52
53
 
53
54
  class << self
55
+ def default_formatters
56
+ @default_formatters
57
+ end
58
+
59
+ def default_formatters=(defaults=nil)
60
+ @default_formatters = [defaults].flatten
61
+
62
+ # reset the formatters
63
+ @formatters = @default_formatters
64
+ @sorted_formatters = nil
65
+ end
66
+
54
67
  def add_formatter(options) #:nodoc:
55
68
  @formatters.push(options)
56
69
  @sorted_formatters = nil
@@ -57,13 +57,13 @@ _cset(:shared_path) { File.join(deploy_to, shared_dir) }
57
57
  _cset(:current_path) { File.join(deploy_to, current_dir) }
58
58
  _cset(:release_path) { File.join(releases_path, release_name) }
59
59
 
60
- _cset(:releases) { capture("ls -x #{releases_path}", :except => { :no_release => true }).split.sort }
60
+ _cset(:releases) { capture("#{try_sudo} ls -x #{releases_path}", :except => { :no_release => true }).split.sort }
61
61
  _cset(:current_release) { releases.length > 0 ? File.join(releases_path, releases.last) : nil }
62
62
  _cset(:previous_release) { releases.length > 1 ? File.join(releases_path, releases[-2]) : nil }
63
63
 
64
- _cset(:current_revision) { capture("cat #{current_path}/REVISION", :except => { :no_release => true }).chomp }
65
- _cset(:latest_revision) { capture("cat #{current_release}/REVISION", :except => { :no_release => true }).chomp }
66
- _cset(:previous_revision) { capture("cat #{previous_release}/REVISION", :except => { :no_release => true }).chomp if previous_release }
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 }
67
67
 
68
68
  _cset(:run_method) { fetch(:use_sudo, true) ? :sudo : :run }
69
69
 
@@ -74,14 +74,16 @@ _cset(:run_method) { fetch(:use_sudo, true) ? :sudo : :run }
74
74
  # standalone case, or during deployment.
75
75
  _cset(:latest_release) { exists?(:deploy_timestamped) ? release_path : current_release }
76
76
 
77
+ _cset :maintenance_basename, "maintenance"
78
+ _cset(:maintenance_template_path) { File.join(File.dirname(__FILE__), "templates", "maintenance.rhtml") }
77
79
  # =========================================================================
78
80
  # These are helper methods that will be available to your recipes.
79
81
  # =========================================================================
80
82
 
81
- # Checks known version control directories to intelligently set the version
82
- # control in-use. For example, if a .svn directory exists in the project,
83
- # it will set the :scm variable to :subversion, if a .git directory exists
84
- # in the project, it will set the :scm variable to :git and so on. If no
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
85
87
  # directory is found, it will default to :git.
86
88
  def scm_default
87
89
  if File.exist? '.git'
@@ -127,6 +129,9 @@ end
127
129
  # logs the command then executes it locally.
128
130
  # returns the command output as a string
129
131
  def run_locally(cmd)
132
+ if dry_run
133
+ return logger.debug "executing locally: #{cmd.inspect}"
134
+ end
130
135
  logger.trace "executing locally: #{cmd.inspect}" if logger
131
136
  output_on_stdout = nil
132
137
  elapsed = Benchmark.realtime do
@@ -153,7 +158,7 @@ end
153
158
  # THUS, if you want to try to run something via sudo, and what to use the
154
159
  # root user, you'd just to try_sudo('something'). If you wanted to try_sudo as
155
160
  # someone else, you'd just do try_sudo('something', :as => "bob"). If you
156
- # always wanted sudo to run as a particular user, you could do
161
+ # always wanted sudo to run as a particular user, you could do
157
162
  # set(:admin_runner, "bob").
158
163
  def try_sudo(*args)
159
164
  options = args.last.is_a?(Hash) ? args.pop : {}
@@ -262,7 +267,7 @@ namespace :deploy do
262
267
  public/stylesheets, and public/javascripts so that the times are \
263
268
  consistent (so that asset timestamping works). This touch process \
264
269
  is only carried out if the :normalize_asset_timestamps variable is \
265
- set to true, which is the default The asset directories can be overridden \
270
+ set to true, which is the default. The asset directories can be overridden \
266
271
  using the :public_children variable.
267
272
  DESC
268
273
  task :finalize_update, :except => { :no_release => true } do
@@ -279,7 +284,7 @@ namespace :deploy do
279
284
  "mkdir -p -- #{escaped_release}/#{dir.slice(0..(dir.rindex('/'))).shellescape}"]
280
285
  else
281
286
  commands << "rm -rf -- #{escaped_release}/#{d}"
282
- end
287
+ end
283
288
  commands << "ln -s -- #{shared_path}/#{dir.split('/').last.shellescape} #{escaped_release}/#{d}"
284
289
  end
285
290
 
@@ -287,8 +292,10 @@ namespace :deploy do
287
292
 
288
293
  if fetch(:normalize_asset_timestamps, true)
289
294
  stamp = Time.now.utc.strftime("%Y%m%d%H%M.%S")
290
- asset_paths = fetch(:public_children, %w(images stylesheets javascripts)).map { |p| "#{escaped_release}/public/#{p}" }
291
- run("find #{asset_paths.join(" ")} -exec touch -t #{stamp} -- {} ';'; true",
295
+ asset_paths = fetch(:public_children, %w(images stylesheets javascripts)).
296
+ map { |p| "#{latest_release}/public/#{p}" }.
297
+ map { |p| p.shellescape }.join(" ")
298
+ run("find #{asset_paths} -exec touch -t #{stamp} -- {} ';'; true",
292
299
  :env => { "TZ" => "UTC" }) if asset_paths.any?
293
300
  end
294
301
  end
@@ -313,13 +320,13 @@ namespace :deploy do
313
320
  task :create_symlink, :except => { :no_release => true } do
314
321
  on_rollback do
315
322
  if previous_release
316
- run "rm -f #{current_path}; ln -s #{previous_release} #{current_path}; true"
323
+ run "#{try_sudo} rm -f #{current_path}; #{try_sudo} ln -s #{previous_release} #{current_path}; true"
317
324
  else
318
325
  logger.important "no previous release to rollback to, rollback of symlink skipped"
319
326
  end
320
327
  end
321
328
 
322
- run "rm -f #{current_path} && ln -s #{latest_release} #{current_path}"
329
+ run "#{try_sudo} rm -f #{current_path} && #{try_sudo} ln -s #{latest_release} #{current_path}"
323
330
  end
324
331
 
325
332
  desc <<-DESC
@@ -363,7 +370,7 @@ namespace :deploy do
363
370
  DESC
364
371
  task :revision, :except => { :no_release => true } do
365
372
  if previous_release
366
- run "rm #{current_path}; ln -s #{previous_release} #{current_path}"
373
+ run "#{try_sudo} rm #{current_path}; #{try_sudo} ln -s #{previous_release} #{current_path}"
367
374
  else
368
375
  abort "could not rollback the code because there is no prior release"
369
376
  end
@@ -375,7 +382,7 @@ namespace :deploy do
375
382
  (if ever) need to be called directly.
376
383
  DESC
377
384
  task :cleanup, :except => { :no_release => true } do
378
- run "if [ `readlink #{current_path}` != #{current_release} ]; then rm -rf #{current_release}; fi"
385
+ run "if [ `readlink #{current_path}` != #{current_release} ]; then #{try_sudo} rm -rf #{current_release}; fi"
379
386
  end
380
387
 
381
388
  desc <<-DESC
@@ -455,16 +462,7 @@ namespace :deploy do
455
462
  DESC
456
463
  task :cleanup, :except => { :no_release => true } do
457
464
  count = fetch(:keep_releases, 5).to_i
458
- local_releases = capture("ls -xt #{releases_path}").split.reverse
459
- if count >= local_releases.length
460
- logger.important "no old releases to clean up"
461
- else
462
- logger.info "keeping #{count} of #{local_releases.length} deployed releases"
463
- directories = (local_releases - local_releases.last(count)).map { |release|
464
- File.join(releases_path, release) }.join(" ")
465
-
466
- try_sudo "rm -rf #{directories}"
467
- end
465
+ try_sudo "ls -1dt #{releases_path}/* | tail -n +#{count + 1} | xargs rm -rf"
468
466
  end
469
467
 
470
468
  desc <<-DESC
@@ -555,4 +553,73 @@ namespace :deploy do
555
553
  system(source.local.log(from))
556
554
  end
557
555
  end
556
+
557
+ namespace :web do
558
+ desc <<-DESC
559
+ Present a maintenance page to visitors. Disables your application's web \
560
+ interface by writing a "#{maintenance_basename}.html" file to each web server. The \
561
+ servers must be configured to detect the presence of this file, and if \
562
+ it is present, always display it instead of performing the request.
563
+
564
+ By default, the maintenance page will just say the site is down for \
565
+ "maintenance", and will be back "shortly", but you can customize the \
566
+ page by specifying the REASON and UNTIL environment variables:
567
+
568
+ $ cap deploy:web:disable \\
569
+ REASON="hardware upgrade" \\
570
+ UNTIL="12pm Central Time"
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
+
576
+ Further customization will require that you write your own task.
577
+ DESC
578
+ task :disable, :roles => :web, :except => { :no_release => true } do
579
+ require 'erb'
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
605
+
606
+ reason = ENV['REASON']
607
+ deadline = ENV['UNTIL']
608
+
609
+ template = File.read(maintenance_template_path)
610
+ result = ERB.new(template).result(binding)
611
+
612
+ put result, "#{shared_path}/system/#{maintenance_basename}.html", :mode => 0644
613
+ end
614
+
615
+ desc <<-DESC
616
+ Makes the application web-accessible again. Removes the \
617
+ "#{maintenance_basename}.html" page generated by deploy:web:disable, which (if your \
618
+ web servers are configured correctly) will make your application \
619
+ web-accessible again.
620
+ DESC
621
+ task :enable, :roles => :web, :except => { :no_release => true } do
622
+ run "rm -f #{shared_path}/system/#{maintenance_basename}.html"
623
+ end
624
+ end
558
625
  end