capistrano 2.12.0 → 2.13.5

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