capistrano 2.14.2 → 2.15.0

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