capistrano 2.12.0 → 2.13.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.
Files changed (33) hide show
  1. data/CHANGELOG +29 -1
  2. data/README.mdown +3 -2
  3. data/bin/capify +1 -3
  4. data/capistrano.gemspec +3 -3
  5. data/lib/capistrano/command.rb +1 -0
  6. data/lib/capistrano/configuration.rb +6 -1
  7. data/lib/capistrano/configuration/actions/inspect.rb +2 -2
  8. data/lib/capistrano/configuration/actions/invocation.rb +7 -0
  9. data/lib/capistrano/configuration/callbacks.rb +22 -4
  10. data/lib/capistrano/configuration/execution.rb +2 -3
  11. data/lib/capistrano/configuration/log_formatters.rb +71 -0
  12. data/lib/capistrano/configuration/namespaces.rb +20 -13
  13. data/lib/capistrano/logger.rb +98 -4
  14. data/lib/capistrano/recipes/deploy.rb +45 -83
  15. data/lib/capistrano/recipes/deploy/assets.rb +1 -1
  16. data/lib/capistrano/recipes/deploy/scm/git.rb +0 -1
  17. data/lib/capistrano/recipes/deploy/scm/none.rb +7 -0
  18. data/lib/capistrano/recipes/deploy/strategy/base.rb +1 -1
  19. data/lib/capistrano/shell.rb +4 -4
  20. data/lib/capistrano/version.rb +2 -7
  21. data/test/command_test.rb +8 -0
  22. data/test/configuration/actions/inspect_test.rb +13 -2
  23. data/test/configuration/actions/invocation_test.rb +6 -1
  24. data/test/configuration/callbacks_test.rb +24 -0
  25. data/test/configuration/namespace_dsl_test.rb +2 -2
  26. data/test/configuration_test.rb +1 -1
  27. data/test/deploy/scm/git_test.rb +1 -1
  28. data/test/deploy/strategy/copy_test.rb +2 -1
  29. data/test/logger_formatting_test.rb +94 -0
  30. data/test/logger_test.rb +12 -1
  31. metadata +51 -19
  32. data/lib/capistrano/recipes/deploy/templates/maintenance.rhtml +0 -53
  33. data/lib/capistrano/recipes/templates/maintenance.rhtml +0 -53
data/CHANGELOG CHANGED
@@ -1,6 +1,34 @@
1
+ ## 2.13.5 (tentative) / October 26 2012
2
+
3
+ * Merged in `capistrano_colors` gem, and renamed to 'log_formatters', since it does much more than just colors
4
+ (@ndbroadbent)
5
+ * Use more intelligence in setting the :scm variable based on known version control directory names (@czarneckid)
6
+ * Remove the deploy:web:{disable, enable} tasks (@carsomyr)
7
+ * Group finalize_update shell commands into one command, harden shell calls with #shellescape, and separate arguments
8
+ with -- (@ndbroadbent)
9
+
10
+ ## 2.13.0 / August 21 2012
11
+
12
+ This release contains multiple bugfixes and handling of exotic situations. The
13
+ `Configuration#capture` method should now work in spite of `ActiveSupport`
14
+ shenanigans. Thank you, the community, for all of your contributions:
15
+
16
+ * Close input streams when sending commands that don't read input. Dylan Smith
17
+ (dylanahsmith)
18
+ * Listen for method definition on `Kernel` and undefine on `Namespace`. Chris
19
+ Griego (cgriego)
20
+ * Fixed shell `Thread.abort_on_exception` bug. George Malamidis (nutrun)
21
+ * Adding a log method to `Capistrano::Deploy::SCM::None` to maintain
22
+ consistency with other SCM classes. Kevin Lawver (kplawver)
23
+ * Add deprecation warning if someone uses old `deploy:symlink` syntax on
24
+ callbacks. Ken Mazaika (kenmazaika)
25
+ * Simplify the `finalize_update` code by respecting the `:shared_children`
26
+ variable during removal and recreation of the parent. John Knight
27
+ (knightlabs)
28
+
1
29
  ## 2.12.0 / April 13 2012
2
30
 
3
- This release revserts the very verbose logging introduced in the previous version, it also
31
+ This release reverts the very verbose logging introduced in the previous version, it also
4
32
  enables a handful of power-user features which are largely un-documented, but shouldn't be
5
33
  important unless you are looking for them. Undocumented code shouldn't scare you, simply
6
34
  read through deploy.rb in the Gem if you want to know how a new feature works!
@@ -17,7 +17,7 @@ of tasks designed for deploying Rails applications.
17
17
 
18
18
  ## Documentation
19
19
 
20
- * [http://github.com/capistrano/capistrano/wiki/Documentation-v2.x](http://github.com/capistrano/capistrano/wiki/Documentation-v2.x)
20
+ * [https://github.com/capistrano/capistrano/wiki](https://github.com/capistrano/capistrano/wiki)
21
21
 
22
22
  ## DEPENDENCIES
23
23
 
@@ -26,6 +26,7 @@ of tasks designed for deploying Rails applications.
26
26
  * [Net::SCP](http://net-ssh.rubyforge.org)
27
27
  * [Net::SSH::Gateway](http://net-ssh.rubyforge.org)
28
28
  * [HighLine](http://highline.rubyforge.org)
29
+ * [Ruby](http://www.ruby-lang.org/en/) ≥ 1.8.7
29
30
 
30
31
  If you want to run the tests, you'll also need to install the dependencies with
31
32
  Bundler, see the `Gemfile` within .
@@ -51,7 +52,7 @@ In general, you'll use Capistrano as follows:
51
52
 
52
53
  Use the `cap` script as follows:
53
54
 
54
- cap sometask
55
+ cap sometask
55
56
 
56
57
  By default, the script will look for a file called one of `capfile` or
57
58
  `Capfile`. The `someaction` text indicates which task to execute. You can do
data/bin/capify CHANGED
@@ -43,15 +43,13 @@ files = {
43
43
  # Uncomment if you are using Rails' asset pipeline
44
44
  # load 'deploy/assets'
45
45
 
46
- Dir['vendor/gems/*/recipes/*.rb','vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }
47
-
48
46
  load 'config/deploy' # remove this line to skip loading any of the default tasks
49
47
  FILE
50
48
 
51
49
  "config/deploy.rb" => 'set :application, "set your application name here"
52
50
  set :repository, "set your repository location here"
53
51
 
54
- set :scm, :subversion
52
+ # set :scm, :git # You can set :scm explicitly or Capistrano will make an intelligent guess based on known version control directory names
55
53
  # Or: `accurev`, `bzr`, `cvs`, `darcs`, `git`, `mercurial`, `perforce`, `subversion` or `none`
56
54
 
57
55
  role :web, "your web-server here" # Your HTTP server, Apache/etc
@@ -28,14 +28,14 @@ Gem::Specification.new do |s|
28
28
  s.add_runtime_dependency(%q<net-sftp>, [">= 2.0.0"])
29
29
  s.add_runtime_dependency(%q<net-scp>, [">= 1.0.0"])
30
30
  s.add_runtime_dependency(%q<net-ssh-gateway>, [">= 1.1.0"])
31
- s.add_development_dependency(%q<mocha>, [">= 0"])
31
+ s.add_development_dependency(%q<mocha>, ["0.9.12"])
32
32
  else
33
33
  s.add_dependency(%q<net-ssh>, [">= 2.0.14"])
34
34
  s.add_dependency(%q<net-sftp>, [">= 2.0.0"])
35
35
  s.add_dependency(%q<net-scp>, [">= 1.0.0"])
36
36
  s.add_dependency(%q<net-ssh-gateway>, [">= 1.1.0"])
37
37
  s.add_dependency(%q<highline>, [">= 0"])
38
- s.add_dependency(%q<mocha>, [">= 0"])
38
+ s.add_dependency(%q<mocha>, ["0.9.12"])
39
39
  end
40
40
  else
41
41
  s.add_dependency(%q<net-ssh>, [">= 2.0.14"])
@@ -43,6 +43,6 @@ Gem::Specification.new do |s|
43
43
  s.add_dependency(%q<net-scp>, [">= 1.0.0"])
44
44
  s.add_dependency(%q<net-ssh-gateway>, [">= 1.1.0"])
45
45
  s.add_dependency(%q<highline>, [">= 0"])
46
- s.add_dependency(%q<mocha>, [">= 0"])
46
+ s.add_dependency(%q<mocha>, ["0.9.12"])
47
47
  end
48
48
  end
@@ -221,6 +221,7 @@ module Capistrano
221
221
 
222
222
  ch.exec(command_line)
223
223
  ch.send_data(options[:data]) if options[:data]
224
+ ch.eof! if options[:eof]
224
225
  else
225
226
  # just log it, don't actually raise an exception, since the
226
227
  # process method will see that the status is not zero and will
@@ -5,6 +5,7 @@ require 'capistrano/configuration/callbacks'
5
5
  require 'capistrano/configuration/connections'
6
6
  require 'capistrano/configuration/execution'
7
7
  require 'capistrano/configuration/loading'
8
+ require 'capistrano/configuration/log_formatters'
8
9
  require 'capistrano/configuration/namespaces'
9
10
  require 'capistrano/configuration/roles'
10
11
  require 'capistrano/configuration/servers'
@@ -34,12 +35,16 @@ module Capistrano
34
35
 
35
36
  # The includes must come at the bottom, since they may redefine methods
36
37
  # defined in the base class.
37
- include AliasTask, Connections, Execution, Loading, Namespaces, Roles, Servers, Variables
38
+ include AliasTask, Connections, Execution, Loading, LogFormatters, Namespaces, Roles, Servers, Variables
38
39
 
39
40
  # Mix in the actions
40
41
  include Actions::FileTransfer, Actions::Inspect, Actions::Invocation
41
42
 
42
43
  # Must mix last, because it hooks into previously defined methods
43
44
  include Callbacks
45
+
46
+ ((self.instance_methods & Kernel.methods) - Namespaces::Namespace.instance_methods).each do |name|
47
+ Namespaces::Namespace.send(:undef_method, name)
48
+ end
44
49
  end
45
50
  end
@@ -20,7 +20,7 @@ module Capistrano
20
20
  # stream "tail -f #{shared_path}/log/fastcgi.crash.log"
21
21
  # end
22
22
  def stream(command, options={})
23
- invoke_command(command, options) do |ch, stream, out|
23
+ invoke_command(command, options.merge(:eof => !command.include?(sudo))) do |ch, stream, out|
24
24
  puts out if stream == :out
25
25
  warn "[err :: #{ch[:server]}] #{out}" if stream == :err
26
26
  end
@@ -31,7 +31,7 @@ module Capistrano
31
31
  # string. The command is invoked via #invoke_command.
32
32
  def capture(command, options={})
33
33
  output = ""
34
- invoke_command(command, options.merge(:once => true)) do |ch, stream, data|
34
+ invoke_command(command, options.merge(:once => true, :eof => !command.include?(sudo))) do |ch, stream, data|
35
35
  case stream
36
36
  when :out then output << data
37
37
  when :err then warn "[err :: #{ch[:server]}] #{data}"
@@ -138,11 +138,18 @@ module Capistrano
138
138
  # and the values should be their corresponding values. The default is
139
139
  # empty, but may be modified by changing the +default_environment+
140
140
  # Capistrano variable.
141
+ # * :eof - if true, the standard input stream will be closed after sending
142
+ # any data specified in the :data option. If false, the input stream is
143
+ # left open. The default is to close the input stream only if no block is
144
+ # passed.
141
145
  #
142
146
  # Note that if you set these keys in the +default_run_options+ Capistrano
143
147
  # variable, they will apply for all invocations of #run, #invoke_command,
144
148
  # and #parallel.
145
149
  def run(cmd, options={}, &block)
150
+ if options[:eof].nil? && !cmd.include?(sudo)
151
+ options = options.merge(:eof => !block_given?)
152
+ end
146
153
  block ||= self.class.default_io_proc
147
154
  tree = Command::Tree.new(self) { |t| t.else(cmd, &block) }
148
155
  run_tree(tree, options)
@@ -19,7 +19,7 @@ module Capistrano
19
19
  end
20
20
 
21
21
  def invoke_task_directly_with_callbacks(task) #:nodoc:
22
-
22
+
23
23
  trigger :before, task
24
24
 
25
25
  result = invoke_task_directly_without_callbacks(task)
@@ -106,12 +106,30 @@ module Capistrano
106
106
  elsif block
107
107
  callbacks[event] << ProcCallback.new(block, options)
108
108
  else
109
- args.each do |name|
110
- callbacks[event] << TaskCallback.new(self, name, options)
111
- end
109
+ args = filter_deprecated_tasks(args)
110
+ options[:only] = filter_deprecated_tasks(options[:only])
111
+ options[:except] = filter_deprecated_tasks(options[:except])
112
+
113
+ callbacks[event].concat(args.map { |name| TaskCallback.new(self, name, options) })
112
114
  end
113
115
  end
114
116
 
117
+ # Filters the given task name or names and attempts to replace deprecated tasks with their equivalents.
118
+ def filter_deprecated_tasks(names)
119
+ deprecation_msg = "[Deprecation Warning] This API has changed, please hook `deploy:create_symlink` instead of" \
120
+ " `deploy:symlink`."
121
+
122
+ if names == "deploy:symlink"
123
+ warn deprecation_msg
124
+ names = "deploy:create_symlink"
125
+ elsif names.is_a?(Array) && names.include?("deploy:symlink")
126
+ warn deprecation_msg
127
+ names = names.map { |name| name == "deploy:symlink" ? "deploy:create_symlink" : name }
128
+ end
129
+
130
+ names
131
+ end
132
+
115
133
  # Trigger the named event for the named task. All associated callbacks
116
134
  # will be fired, in the order they were defined.
117
135
  def trigger(event, task=nil)
@@ -59,7 +59,7 @@ module Capistrano
59
59
  rollback!
60
60
  raise
61
61
  ensure
62
- self.rollback_requests = nil if Thread.main == Thread.current
62
+ self.rollback_requests = nil
63
63
  end
64
64
  end
65
65
 
@@ -108,8 +108,7 @@ module Capistrano
108
108
 
109
109
  def rollback!
110
110
  return if Thread.current[:rollback_requests].nil?
111
- Thread.current[:rolled_back] = true
112
-
111
+
113
112
  # throw the task back on the stack so that roles are properly
114
113
  # interpreted in the scope of the task in question.
115
114
  rollback_requests.reverse.each do |frame|
@@ -0,0 +1,71 @@
1
+ # Add custom log formatters
2
+ #
3
+ # Passing a hash or a array of hashes with custom log formatters.
4
+ #
5
+ # Add the following to your deploy.rb or in your ~/.caprc
6
+ #
7
+ # == Example:
8
+ #
9
+ # capistrano_log_formatters = [
10
+ # { :match => /command finished/, :color => :hide, :priority => 10, :prepend => "$$$" },
11
+ # { :match => /executing command/, :color => :blue, :priority => 10, :style => :underscore, :timestamp => true },
12
+ # { :match => /^transaction: commit$/, :color => :magenta, :priority => 10, :style => :blink },
13
+ # { :match => /git/, :color => :white, :priority => 20, :style => :reverse }
14
+ # ]
15
+ #
16
+ # format_logs capistrano_log_formatters
17
+ #
18
+ # You can call format_logs multiple times, with either a hash or an array of hashes.
19
+ #
20
+ # == Colors:
21
+ #
22
+ # :color can have the following values:
23
+ #
24
+ # * :hide (hides the row completely)
25
+ # * :none
26
+ # * :black
27
+ # * :red
28
+ # * :green
29
+ # * :yellow
30
+ # * :blue
31
+ # * :magenta
32
+ # * :cyan
33
+ # * :white
34
+ #
35
+ # == Styles:
36
+ #
37
+ # :style can have the following values:
38
+ #
39
+ # * :bright
40
+ # * :dim
41
+ # * :underscore
42
+ # * :blink
43
+ # * :reverse
44
+ # * :hidden
45
+ #
46
+ #
47
+ # == Text alterations
48
+ #
49
+ # :prepend gives static text to be prepended to the output
50
+ # :replace replaces the matched text in the output
51
+ # :timestamp adds the current time before the output
52
+
53
+ module Capistrano
54
+ class Configuration
55
+ module LogFormatters
56
+ def log_formatter(options)
57
+ if options.class == Array
58
+ options.each do |option|
59
+ Capistrano::Logger.add_formatter(option)
60
+ end
61
+ else
62
+ Capistrano::Logger.add_formatter(options)
63
+ end
64
+ end
65
+
66
+ def disable_log_formatters
67
+ @logger.disable_formatters = true
68
+ end
69
+ end
70
+ end
71
+ end
@@ -176,8 +176,6 @@ module Capistrano
176
176
  def initialize(name, parent)
177
177
  @parent = parent
178
178
  @name = name
179
-
180
- explicitly_define_clashing_kernel_methods
181
179
  end
182
180
 
183
181
  def role(*args)
@@ -199,18 +197,27 @@ module Capistrano
199
197
  include Capistrano::Configuration::AliasTask
200
198
  include Capistrano::Configuration::Namespaces
201
199
  undef :desc, :next_description
202
-
203
- protected
204
- def explicitly_define_clashing_kernel_methods
205
- (parent.public_methods & Kernel.methods).each do |m|
206
- next if self.method(m).owner == self.class
207
- if parent.method(m).owner == parent.class
208
- metaclass = class << self; self; end
209
- metaclass.send(:define_method, m) {|*args, &block| parent.send(m, *args, &block)}
210
- end
211
- end
212
- end
213
200
  end
214
201
  end
215
202
  end
216
203
  end
204
+
205
+ module Kernel
206
+ class << self
207
+ alias_method :method_added_without_capistrano, :method_added
208
+
209
+ # Detect method additions to Kernel and remove them in the Namespace class
210
+ def method_added(name)
211
+ result = method_added_without_capistrano(name)
212
+ return result if self != Kernel
213
+
214
+ namespace = Capistrano::Configuration::Namespaces::Namespace
215
+
216
+ if namespace.method_defined?(name) && namespace.method(name).owner == Kernel
217
+ namespace.send :undef_method, name
218
+ end
219
+
220
+ result
221
+ end
222
+ end
223
+ end
@@ -1,15 +1,67 @@
1
1
  module Capistrano
2
2
  class Logger #:nodoc:
3
- attr_accessor :level
4
- attr_reader :device
3
+ attr_accessor :level, :device, :disable_formatters
5
4
 
6
5
  IMPORTANT = 0
7
6
  INFO = 1
8
7
  DEBUG = 2
9
8
  TRACE = 3
10
-
9
+
11
10
  MAX_LEVEL = 3
12
11
 
12
+ COLORS = {
13
+ :none => "0",
14
+ :black => "30",
15
+ :red => "31",
16
+ :green => "32",
17
+ :yellow => "33",
18
+ :blue => "34",
19
+ :magenta => "35",
20
+ :cyan => "36",
21
+ :white => "37"
22
+ }
23
+
24
+ STYLES = {
25
+ :bright => 1,
26
+ :dim => 2,
27
+ :underscore => 4,
28
+ :blink => 5,
29
+ :reverse => 7,
30
+ :hidden => 8
31
+ }
32
+
33
+ # Set up default formatters
34
+ @formatters = [
35
+ # TRACE
36
+ { :match => /command finished/, :color => :white, :style => :dim, :level => 3, :priority => -10 },
37
+ { :match => /executing locally/, :color => :yellow, :level => 3, :priority => -20 },
38
+
39
+ # DEBUG
40
+ { :match => /executing `.*/, :color => :green, :level => 2, :priority => -10, :timestamp => true },
41
+ { :match => /.*/, :color => :yellow, :level => 2, :priority => -30 },
42
+
43
+ # INFO
44
+ { :match => /.*out\] (fatal:|ERROR:).*/, :color => :red, :level => 1, :priority => -10 },
45
+ { :match => /Permission denied/, :color => :red, :level => 1, :priority => -20 },
46
+ { :match => /sh: .+: command not found/, :color => :magenta, :level => 1, :priority => -30 },
47
+
48
+ # IMPORTANT
49
+ { :match => /^err ::/, :color => :red, :level => 0, :priority => -10 },
50
+ { :match => /.*/, :color => :blue, :level => 0, :priority => -20 }
51
+ ]
52
+
53
+ class << self
54
+ def add_formatter(options) #:nodoc:
55
+ @formatters.push(options)
56
+ @sorted_formatters = nil
57
+ end
58
+
59
+ def sorted_formatters
60
+ # Sort matchers in reverse order so we can break if we found a match.
61
+ @sorted_formatters ||= @formatters.sort_by { |i| -(i[:priority] || i[:prio] || 0) }
62
+ end
63
+ end
64
+
13
65
  def initialize(options={})
14
66
  output = options[:output] || $stderr
15
67
  if output.respond_to?(:puts)
@@ -20,7 +72,8 @@ module Capistrano
20
72
  end
21
73
 
22
74
  @options = options
23
- @level = 0
75
+ @level = options[:level] || 0
76
+ @disable_formatters = options[:disable_formatters]
24
77
  end
25
78
 
26
79
  def close
@@ -29,6 +82,42 @@ module Capistrano
29
82
 
30
83
  def log(level, message, line_prefix=nil)
31
84
  if level <= self.level
85
+ # Only format output if device is a TTY or formatters are not disabled
86
+ if device.tty? && !@disable_formatters
87
+ color = :none
88
+ style = nil
89
+
90
+ Logger.sorted_formatters.each do |formatter|
91
+ if (formatter[:level] == level || formatter[:level].nil?)
92
+ if message =~ formatter[:match] || line_prefix =~ formatter[:match]
93
+ color = formatter[:color] if formatter[:color]
94
+ style = formatter[:style] || formatter[:attribute] # (support original cap colors)
95
+ message.gsub!(formatter[:match], formatter[:replace]) if formatter[:replace]
96
+ message = formatter[:prepend] + message unless formatter[:prepend].nil?
97
+ message = message + formatter[:append] unless formatter[:append].nil?
98
+ message = Time.now.strftime('%Y-%m-%d %T') + ' ' + message if formatter[:timestamp]
99
+ break unless formatter[:replace]
100
+ end
101
+ end
102
+ end
103
+
104
+ if color == :hide
105
+ # Don't do anything if color is set to :hide
106
+ return false
107
+ end
108
+
109
+ term_color = COLORS[color]
110
+ term_style = STYLES[style]
111
+
112
+ # Don't format message if no color or style
113
+ unless color == :none and style.nil?
114
+ unless line_prefix.nil?
115
+ line_prefix = format(line_prefix, term_color, term_style, nil)
116
+ end
117
+ message = format(message, term_color, term_style)
118
+ end
119
+ end
120
+
32
121
  indent = "%*s" % [MAX_LEVEL, "*" * (MAX_LEVEL - level)]
33
122
  (RUBY_VERSION >= "1.9" ? message.lines : message).each do |line|
34
123
  if line_prefix
@@ -55,5 +144,10 @@ module Capistrano
55
144
  def trace(message, line_prefix=nil)
56
145
  log(TRACE, message, line_prefix)
57
146
  end
147
+
148
+ def format(message, color, style, nl = "\n")
149
+ style = "#{style};" if style
150
+ "\e[#{style}#{color}m" + message.to_s.strip + "\e[0m#{nl}"
151
+ end
58
152
  end
59
153
  end