capistrano 2.12.0 → 2.13.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +29 -1
- data/README.mdown +3 -2
- data/bin/capify +1 -3
- data/capistrano.gemspec +3 -3
- data/lib/capistrano/command.rb +1 -0
- data/lib/capistrano/configuration.rb +6 -1
- data/lib/capistrano/configuration/actions/inspect.rb +2 -2
- data/lib/capistrano/configuration/actions/invocation.rb +7 -0
- data/lib/capistrano/configuration/callbacks.rb +22 -4
- data/lib/capistrano/configuration/execution.rb +2 -3
- data/lib/capistrano/configuration/log_formatters.rb +71 -0
- data/lib/capistrano/configuration/namespaces.rb +20 -13
- data/lib/capistrano/logger.rb +98 -4
- data/lib/capistrano/recipes/deploy.rb +45 -83
- data/lib/capistrano/recipes/deploy/assets.rb +1 -1
- data/lib/capistrano/recipes/deploy/scm/git.rb +0 -1
- data/lib/capistrano/recipes/deploy/scm/none.rb +7 -0
- data/lib/capistrano/recipes/deploy/strategy/base.rb +1 -1
- data/lib/capistrano/shell.rb +4 -4
- data/lib/capistrano/version.rb +2 -7
- data/test/command_test.rb +8 -0
- data/test/configuration/actions/inspect_test.rb +13 -2
- data/test/configuration/actions/invocation_test.rb +6 -1
- data/test/configuration/callbacks_test.rb +24 -0
- data/test/configuration/namespace_dsl_test.rb +2 -2
- data/test/configuration_test.rb +1 -1
- data/test/deploy/scm/git_test.rb +1 -1
- data/test/deploy/strategy/copy_test.rb +2 -1
- data/test/logger_formatting_test.rb +94 -0
- data/test/logger_test.rb +12 -1
- metadata +51 -19
- data/lib/capistrano/recipes/deploy/templates/maintenance.rhtml +0 -53
- data/lib/capistrano/recipes/templates/maintenance.rhtml +0 -53
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'benchmark'
|
2
2
|
require 'yaml'
|
3
|
+
require 'shellwords'
|
3
4
|
require 'capistrano/recipes/deploy/scm'
|
4
5
|
require 'capistrano/recipes/deploy/strategy'
|
5
6
|
|
@@ -22,7 +23,7 @@ _cset(:repository) { abort "Please specify the repository that houses your appl
|
|
22
23
|
# are not sufficient.
|
23
24
|
# =========================================================================
|
24
25
|
|
25
|
-
_cset
|
26
|
+
_cset(:scm) { scm_default }
|
26
27
|
_cset :deploy_via, :checkout
|
27
28
|
|
28
29
|
_cset(:deploy_to) { "/u/apps/#{application}" }
|
@@ -31,9 +32,6 @@ _cset(:revision) { source.head }
|
|
31
32
|
_cset :rails_env, "production"
|
32
33
|
_cset :rake, "rake"
|
33
34
|
|
34
|
-
_cset :maintenance_basename, "maintenance"
|
35
|
-
_cset(:maintenance_template_path) { File.join(File.dirname(__FILE__), "templates", "maintenance.rhtml") }
|
36
|
-
|
37
35
|
# =========================================================================
|
38
36
|
# These variables should NOT be changed unless you are very confident in
|
39
37
|
# what you are doing. Make sure you understand all the implications of your
|
@@ -79,6 +77,33 @@ _cset(:latest_release) { exists?(:deploy_timestamped) ? release_path : current_r
|
|
79
77
|
# These are helper methods that will be available to your recipes.
|
80
78
|
# =========================================================================
|
81
79
|
|
80
|
+
# Checks known version control directories to intelligently set the version
|
81
|
+
# control in-use. For example, if a .svn directory exists in the project,
|
82
|
+
# it will set the :scm variable to :subversion, if a .git directory exists
|
83
|
+
# in the project, it will set the :scm variable to :git and so on. If no
|
84
|
+
# directory is found, it will default to :git.
|
85
|
+
def scm_default
|
86
|
+
if File.exist? '.git'
|
87
|
+
:git
|
88
|
+
elsif File.exist? '.accurev'
|
89
|
+
:accurev
|
90
|
+
elsif File.exist? '.bzr'
|
91
|
+
:bzr
|
92
|
+
elsif File.exist? '.cvs'
|
93
|
+
:cvs
|
94
|
+
elsif File.exist? '_darcs'
|
95
|
+
:darcs
|
96
|
+
elsif File.exist? '.hg'
|
97
|
+
:mercurial
|
98
|
+
elsif File.exist? '.perforce'
|
99
|
+
:perforce
|
100
|
+
elsif File.exist? '.svn'
|
101
|
+
:subversion
|
102
|
+
else
|
103
|
+
:none
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
82
107
|
# Auxiliary helper method for the `deploy:check' task. Lets you set up your
|
83
108
|
# own dependencies.
|
84
109
|
def depend(location, type, *args)
|
@@ -240,23 +265,29 @@ namespace :deploy do
|
|
240
265
|
using the :public_children variable.
|
241
266
|
DESC
|
242
267
|
task :finalize_update, :except => { :no_release => true } do
|
243
|
-
|
268
|
+
escaped_release = latest_release.to_s.shellescape
|
269
|
+
commands = []
|
270
|
+
commands << "chmod -R -- g+w #{escaped_release}" if fetch(:group_writable, true)
|
244
271
|
|
245
272
|
# mkdir -p is making sure that the directories are there for some SCM's that don't
|
246
273
|
# save empty folders
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
274
|
+
shared_children.map do |dir|
|
275
|
+
d = dir.shellescape
|
276
|
+
if (dir.rindex('/')) then
|
277
|
+
commands += ["rm -rf -- #{escaped_release}/#{d}",
|
278
|
+
"mkdir -p -- #{escaped_release}/#{dir.slice(0..(dir.rindex('/'))).shellescape}"]
|
279
|
+
else
|
280
|
+
commands << "rm -rf -- #{escaped_release}/#{d}"
|
281
|
+
end
|
282
|
+
commands << "ln -s -- #{shared_path}/#{dir.split('/').last.shellescape} #{escaped_release}/#{d}"
|
254
283
|
end
|
255
284
|
|
285
|
+
run commands.join(' && ') if commands.any?
|
286
|
+
|
256
287
|
if fetch(:normalize_asset_timestamps, true)
|
257
288
|
stamp = Time.now.utc.strftime("%Y%m%d%H%M.%S")
|
258
|
-
asset_paths = fetch(:public_children, %w(images stylesheets javascripts)).map { |p| "#{
|
259
|
-
run
|
289
|
+
asset_paths = fetch(:public_children, %w(images stylesheets javascripts)).map { |p| "#{escaped_release}/public/#{p}" }
|
290
|
+
run("find #{asset_paths.join(" ").shellescape} -exec touch -t -- #{stamp} {} ';'; true", :env => { "TZ" => "UTC" }) if asset_paths.any?
|
260
291
|
end
|
261
292
|
end
|
262
293
|
|
@@ -522,73 +553,4 @@ namespace :deploy do
|
|
522
553
|
system(source.local.log(from))
|
523
554
|
end
|
524
555
|
end
|
525
|
-
|
526
|
-
namespace :web do
|
527
|
-
desc <<-DESC
|
528
|
-
Present a maintenance page to visitors. Disables your application's web \
|
529
|
-
interface by writing a "#{maintenance_basename}.html" file to each web server. The \
|
530
|
-
servers must be configured to detect the presence of this file, and if \
|
531
|
-
it is present, always display it instead of performing the request.
|
532
|
-
|
533
|
-
By default, the maintenance page will just say the site is down for \
|
534
|
-
"maintenance", and will be back "shortly", but you can customize the \
|
535
|
-
page by specifying the REASON and UNTIL environment variables:
|
536
|
-
|
537
|
-
$ cap deploy:web:disable \\
|
538
|
-
REASON="hardware upgrade" \\
|
539
|
-
UNTIL="12pm Central Time"
|
540
|
-
|
541
|
-
You can use a different template for the maintenance page by setting the \
|
542
|
-
:maintenance_template_path variable in your deploy.rb file. The template file \
|
543
|
-
should either be a plaintext or an erb file.
|
544
|
-
|
545
|
-
Further customization will require that you write your own task.
|
546
|
-
DESC
|
547
|
-
task :disable, :roles => :web, :except => { :no_release => true } do
|
548
|
-
require 'erb'
|
549
|
-
on_rollback { run "rm -f #{shared_path}/system/#{maintenance_basename}.html" }
|
550
|
-
|
551
|
-
warn <<-EOHTACCESS
|
552
|
-
|
553
|
-
# Please add something like this to your site's Apache htaccess to redirect users to the maintenance page.
|
554
|
-
# More Info: http://www.shiftcommathree.com/articles/make-your-rails-maintenance-page-respond-with-a-503
|
555
|
-
|
556
|
-
ErrorDocument 503 /system/#{maintenance_basename}.html
|
557
|
-
RewriteEngine On
|
558
|
-
RewriteCond %{REQUEST_URI} !\.(css|gif|jpg|png)$
|
559
|
-
RewriteCond %{DOCUMENT_ROOT}/system/#{maintenance_basename}.html -f
|
560
|
-
RewriteCond %{SCRIPT_FILENAME} !#{maintenance_basename}.html
|
561
|
-
RewriteRule ^.*$ - [redirect=503,last]
|
562
|
-
|
563
|
-
# Or if you are using Nginx add this to your server config:
|
564
|
-
|
565
|
-
if (-f $document_root/system/maintenance.html) {
|
566
|
-
return 503;
|
567
|
-
}
|
568
|
-
error_page 503 @maintenance;
|
569
|
-
location @maintenance {
|
570
|
-
rewrite ^(.*)$ /system/maintenance.html last;
|
571
|
-
break;
|
572
|
-
}
|
573
|
-
EOHTACCESS
|
574
|
-
|
575
|
-
reason = ENV['REASON']
|
576
|
-
deadline = ENV['UNTIL']
|
577
|
-
|
578
|
-
template = File.read(maintenance_template_path)
|
579
|
-
result = ERB.new(template).result(binding)
|
580
|
-
|
581
|
-
put result, "#{shared_path}/system/#{maintenance_basename}.html", :mode => 0644
|
582
|
-
end
|
583
|
-
|
584
|
-
desc <<-DESC
|
585
|
-
Makes the application web-accessible again. Removes the \
|
586
|
-
"#{maintenance_basename}.html" page generated by deploy:web:disable, which (if your \
|
587
|
-
web servers are configured correctly) will make your application \
|
588
|
-
web-accessible again.
|
589
|
-
DESC
|
590
|
-
task :enable, :roles => :web, :except => { :no_release => true } do
|
591
|
-
run "rm -f #{shared_path}/system/#{maintenance_basename}.html"
|
592
|
-
end
|
593
|
-
end
|
594
556
|
end
|
@@ -16,7 +16,7 @@ namespace :deploy do
|
|
16
16
|
for the assets directory. Assets are shared across deploys to avoid \
|
17
17
|
mid-deploy mismatches between old application html asking for assets \
|
18
18
|
and getting a 404 file not found error. The assets cache is shared \
|
19
|
-
for efficiency. If you
|
19
|
+
for efficiency. If you customize the assets path prefix, override the \
|
20
20
|
:assets_prefix variable to match.
|
21
21
|
DESC
|
22
22
|
task :symlink, :roles => assets_role, :except => { :no_release => true } do
|
@@ -190,7 +190,6 @@ module Capistrano
|
|
190
190
|
|
191
191
|
if variable(:git_enable_submodules)
|
192
192
|
execute << "#{git} submodule #{verbose} init"
|
193
|
-
execute << "for mod in `#{git} submodule status | awk '{ print $2 }'`; do #{git} config -f .git/config submodule.${mod}.url `#{git} config -f .gitmodules --get submodule.${mod}.url` && echo Synced $mod; done"
|
194
193
|
execute << "#{git} submodule #{verbose} sync"
|
195
194
|
if false == variable(:git_submodules_recursive)
|
196
195
|
execute << "#{git} submodule #{verbose} update --init"
|
@@ -73,7 +73,7 @@ module Capistrano
|
|
73
73
|
private
|
74
74
|
|
75
75
|
def logger
|
76
|
-
@logger ||= configuration
|
76
|
+
@logger ||= configuration.logger || Capistrano::Logger.new(:output => STDOUT)
|
77
77
|
end
|
78
78
|
|
79
79
|
# The revision to deploy. Must return a real revision identifier,
|
data/lib/capistrano/shell.rb
CHANGED
@@ -251,10 +251,10 @@ HELP
|
|
251
251
|
puts "scoping #{scope_type} #{scope_value}"
|
252
252
|
end
|
253
253
|
end
|
254
|
-
end
|
255
254
|
|
256
|
-
|
257
|
-
|
258
|
-
|
255
|
+
# All open sessions, needed to satisfy the Command::Processable include
|
256
|
+
def sessions
|
257
|
+
configuration.sessions.values
|
258
|
+
end
|
259
259
|
end
|
260
260
|
end
|
data/lib/capistrano/version.rb
CHANGED
data/test/command_test.rb
CHANGED
@@ -254,6 +254,14 @@ class CommandTest < Test::Unit::TestCase
|
|
254
254
|
Capistrano::Command.new("echo $CAPISTRANO:OTHER$", [session])
|
255
255
|
end
|
256
256
|
|
257
|
+
def test_input_stream_closed_when_eof_option_is_true
|
258
|
+
channel = nil
|
259
|
+
session = setup_for_extracting_channel_action { |ch| channel = ch }
|
260
|
+
channel.expects(:eof!)
|
261
|
+
Capistrano::Command.new("cat", [session], :data => "here we go", :eof => true)
|
262
|
+
assert_equal({ :data => 'here we go', :eof => true }, channel[:options])
|
263
|
+
end
|
264
|
+
|
257
265
|
private
|
258
266
|
|
259
267
|
def mock_session(channel=nil)
|
@@ -9,13 +9,19 @@ class ConfigurationActionsInspectTest < Test::Unit::TestCase
|
|
9
9
|
def setup
|
10
10
|
@config = MockConfig.new
|
11
11
|
@config.stubs(:logger).returns(stub_everything)
|
12
|
+
@config.stubs(:sudo).returns('sudo')
|
12
13
|
end
|
13
14
|
|
14
15
|
def test_stream_should_pass_options_through_to_run
|
15
|
-
@config.expects(:invoke_command).with("tail -f foo.log", :once => true)
|
16
|
+
@config.expects(:invoke_command).with("tail -f foo.log", :once => true, :eof => true)
|
16
17
|
@config.stream("tail -f foo.log", :once => true)
|
17
18
|
end
|
18
19
|
|
20
|
+
def test_stream_with_sudo_should_avoid_closing_stdin
|
21
|
+
@config.expects(:invoke_command).with("sudo tail -f foo.log", :once => true, :eof => false)
|
22
|
+
@config.stream("sudo tail -f foo.log", :once => true)
|
23
|
+
end
|
24
|
+
|
19
25
|
def test_stream_should_emit_stdout_via_puts
|
20
26
|
@config.expects(:invoke_command).yields(mock("channel"), :out, "something streamed")
|
21
27
|
@config.expects(:puts).with("something streamed")
|
@@ -33,10 +39,15 @@ class ConfigurationActionsInspectTest < Test::Unit::TestCase
|
|
33
39
|
end
|
34
40
|
|
35
41
|
def test_capture_should_pass_options_merged_with_once_to_run
|
36
|
-
@config.expects(:invoke_command).with("hostname", :foo => "bar", :once => true)
|
42
|
+
@config.expects(:invoke_command).with("hostname", :foo => "bar", :once => true, :eof => true)
|
37
43
|
@config.capture("hostname", :foo => "bar")
|
38
44
|
end
|
39
45
|
|
46
|
+
def test_capture_with_sudo_should_avoid_closing_stdin
|
47
|
+
@config.expects(:invoke_command).with("sudo hostname", :foo => "bar", :once => true, :eof => false)
|
48
|
+
@config.capture("sudo hostname", :foo => "bar")
|
49
|
+
end
|
50
|
+
|
40
51
|
def test_capture_with_stderr_should_emit_stderr_via_warn
|
41
52
|
ch = mock("channel")
|
42
53
|
ch.expects(:[]).with(:server).returns(server("capistrano"))
|
@@ -45,7 +45,7 @@ class ConfigurationActionsInvocationTest < Test::Unit::TestCase
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def test_run_options_should_be_passed_to_execute_on_servers
|
48
|
-
@config.expects(:execute_on_servers).with(:foo => "bar")
|
48
|
+
@config.expects(:execute_on_servers).with(:foo => "bar", :eof => true)
|
49
49
|
@config.run "ls", :foo => "bar"
|
50
50
|
end
|
51
51
|
|
@@ -115,6 +115,11 @@ class ConfigurationActionsInvocationTest < Test::Unit::TestCase
|
|
115
115
|
@config.sudo "ls"
|
116
116
|
end
|
117
117
|
|
118
|
+
def test_sudo_should_keep_input_stream_open
|
119
|
+
@config.expects(:execute_on_servers).with(:foo => "bar")
|
120
|
+
@config.sudo "ls", :foo => "bar"
|
121
|
+
end
|
122
|
+
|
118
123
|
def test_sudo_should_use_sudo_variable_definition
|
119
124
|
@config.expects(:run).with("/opt/local/bin/sudo -p 'sudo password: ' ls", {})
|
120
125
|
@config.options[:sudo] = "/opt/local/bin/sudo"
|
@@ -39,11 +39,35 @@ class ConfigurationCallbacksTest < Test::Unit::TestCase
|
|
39
39
|
@config.before :bar, :foo, "bing:blang", :zip => :zing
|
40
40
|
end
|
41
41
|
|
42
|
+
def test_before_should_map_before_deploy_symlink
|
43
|
+
@config.before "deploy:symlink", "bing:blang", "deploy:symlink"
|
44
|
+
assert_equal "bing:blang", @config.callbacks[:before][0].source
|
45
|
+
assert_equal "deploy:create_symlink", @config.callbacks[:before][1].source
|
46
|
+
assert_equal ["deploy:create_symlink"], @config.callbacks[:before][1].only
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_before_should_map_before_deploy_symlink_array
|
50
|
+
@config.before ["deploy:symlink", "bingo:blast"], "bing:blang"
|
51
|
+
assert_equal ["deploy:create_symlink", "bingo:blast"], @config.callbacks[:before].last.only
|
52
|
+
end
|
53
|
+
|
42
54
|
def test_after_should_delegate_to_on
|
43
55
|
@config.expects(:on).with(:after, :foo, "bing:blang", {:only => :bar, :zip => :zing})
|
44
56
|
@config.after :bar, :foo, "bing:blang", :zip => :zing
|
45
57
|
end
|
46
58
|
|
59
|
+
def test_after_should_map_before_deploy_symlink
|
60
|
+
@config.after "deploy:symlink", "bing:blang", "deploy:symlink"
|
61
|
+
assert_equal "bing:blang", @config.callbacks[:after][0].source
|
62
|
+
assert_equal "deploy:create_symlink", @config.callbacks[:after][1].source
|
63
|
+
assert_equal ["deploy:create_symlink"], @config.callbacks[:after][1].only
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_after_should_map_before_deploy_symlink_array
|
67
|
+
@config.after ["deploy:symlink", "bingo:blast"], "bing:blang"
|
68
|
+
assert_equal ["deploy:create_symlink", "bingo:blast"], @config.callbacks[:after].last.only
|
69
|
+
end
|
70
|
+
|
47
71
|
def test_on_with_single_reference_should_add_task_callback
|
48
72
|
@config.on :before, :a_test
|
49
73
|
assert_equal 1, @config.callbacks[:before].length
|
@@ -321,7 +321,7 @@ class ConfigurationNamespacesDSLTest < Test::Unit::TestCase
|
|
321
321
|
Kernel.module_eval do
|
322
322
|
def some_weird_method() 'kernel' end
|
323
323
|
end
|
324
|
-
|
324
|
+
|
325
325
|
@config.namespace(:clash2) {}
|
326
326
|
namespace = @config.namespaces[:clash2]
|
327
327
|
assert_equal 'config', namespace.some_weird_method
|
@@ -329,4 +329,4 @@ class ConfigurationNamespacesDSLTest < Test::Unit::TestCase
|
|
329
329
|
Kernel.send :remove_method, :some_weird_method
|
330
330
|
@config.class.send :remove_method, :some_weird_method
|
331
331
|
end
|
332
|
-
end
|
332
|
+
end
|
data/test/configuration_test.rb
CHANGED
@@ -16,7 +16,7 @@ class ConfigurationTest < Test::Unit::TestCase
|
|
16
16
|
process_args = Proc.new do |tree, session, opts|
|
17
17
|
tree.fallback.command == "echo 'hello world'" &&
|
18
18
|
session == [:session] &&
|
19
|
-
opts == { :logger => @config.logger }
|
19
|
+
opts == { :logger => @config.logger, :eof => true }
|
20
20
|
end
|
21
21
|
|
22
22
|
Capistrano::Command.expects(:process).with(&process_args)
|
data/test/deploy/scm/git_test.rb
CHANGED
@@ -127,7 +127,7 @@ class DeploySCMGitTest < Test::Unit::TestCase
|
|
127
127
|
|
128
128
|
# with submodules
|
129
129
|
@config[:git_enable_submodules] = true
|
130
|
-
assert_equal "cd #{dest} && #{git} fetch -q origin && #{git} fetch --tags -q origin && #{git} reset -q --hard #{rev} && #{git} submodule -q init &&
|
130
|
+
assert_equal "cd #{dest} && #{git} fetch -q origin && #{git} fetch --tags -q origin && #{git} reset -q --hard #{rev} && #{git} submodule -q init && #{git} submodule -q sync && export GIT_RECURSIVE=$([ ! \"`#{git} --version`\" \\< \"git version 1.6.5\" ] && echo --recursive) && #{git} submodule -q update --init $GIT_RECURSIVE && #{git} clean -q -d -x -f", @source.sync(rev, dest)
|
131
131
|
end
|
132
132
|
|
133
133
|
def test_sync_with_remote
|
@@ -6,10 +6,11 @@ require 'stringio'
|
|
6
6
|
class DeployStrategyCopyTest < Test::Unit::TestCase
|
7
7
|
def setup
|
8
8
|
@config = { :application => "captest",
|
9
|
-
:logger => Capistrano::Logger.new(:output => StringIO.new),
|
10
9
|
:releases_path => "/u/apps/test/releases",
|
11
10
|
:release_path => "/u/apps/test/releases/1234567890",
|
12
11
|
:real_revision => "154" }
|
12
|
+
@config.stubs(:logger).returns(stub_everything)
|
13
|
+
|
13
14
|
@source = mock("source")
|
14
15
|
@config.stubs(:source).returns(@source)
|
15
16
|
@strategy = Capistrano::Deploy::Strategy::Copy.new(@config)
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require File.expand_path("../utils", __FILE__)
|
2
|
+
require 'capistrano/logger'
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
Capistrano::Logger.class_eval do
|
6
|
+
# Allows formatters to be changed during tests
|
7
|
+
def self.formatters=(formatters)
|
8
|
+
@formatters = formatters
|
9
|
+
@sorted_formatters = nil
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class LoggerFormattingTest < Test::Unit::TestCase
|
14
|
+
def setup
|
15
|
+
@io = StringIO.new
|
16
|
+
@io.stubs(:tty?).returns(true)
|
17
|
+
@logger = Capistrano::Logger.new(:output => @io, :level => 3)
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_matching_with_style_and_color
|
21
|
+
Capistrano::Logger.formatters = [{ :match => /^err ::/, :color => :red, :style => :underscore, :level => 0 }]
|
22
|
+
@logger.log(0, "err :: Error Occurred")
|
23
|
+
assert @io.string.include? "\e[4;31merr :: Error Occurred\e[0m"
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_style_without_color
|
27
|
+
Capistrano::Logger.formatters = [{ :match => /.*/, :style => :underscore, :level => 0 }]
|
28
|
+
@logger.log(0, "test message")
|
29
|
+
# Default color should be blank (0m)
|
30
|
+
assert @io.string.include? "\e[4;0mtest message\e[0m"
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_prepending_text
|
34
|
+
Capistrano::Logger.formatters = [{ :match => /^executing/, :level => 0, :prepend => '== Currently ' }]
|
35
|
+
@logger.log(0, "executing task")
|
36
|
+
assert @io.string.include? '== Currently executing task'
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_replacing_matched_text
|
40
|
+
Capistrano::Logger.formatters = [{ :match => /^executing/, :level => 0, :replace => 'running' }]
|
41
|
+
@logger.log(0, "executing task")
|
42
|
+
assert @io.string.include? 'running task'
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_prepending_timestamps
|
46
|
+
Capistrano::Logger.formatters = [{ :match => /.*/, :level => 0, :timestamp => true }]
|
47
|
+
@logger.log(0, "test message")
|
48
|
+
assert @io.string.match /\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} test message/
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_formatter_priorities
|
52
|
+
Capistrano::Logger.formatters = [
|
53
|
+
{ :match => /.*/, :color => :red, :level => 0, :priority => -10 },
|
54
|
+
{ :match => /.*/, :color => :blue, :level => 0, :priority => -20, :prepend => '###' }
|
55
|
+
]
|
56
|
+
|
57
|
+
@logger.log(0, "test message")
|
58
|
+
# Only the red formatter (color 31) should be applied.
|
59
|
+
assert @io.string.include? "\e[31mtest message"
|
60
|
+
# The blue formatter should not have prepended $$$
|
61
|
+
assert !@io.string.include?('###')
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_no_formatting_if_no_color_or_style
|
65
|
+
Capistrano::Logger.formatters = []
|
66
|
+
@logger.log(0, "test message")
|
67
|
+
assert @io.string.include? "*** test message"
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_formatter_log_levels
|
71
|
+
Capistrano::Logger.formatters = [{ :match => /.*/, :color => :blue, :level => 3 }]
|
72
|
+
@logger.log(0, "test message")
|
73
|
+
# Should not match log level
|
74
|
+
assert @io.string.include? "*** test message"
|
75
|
+
|
76
|
+
clear_logger
|
77
|
+
@logger.log(3, "test message")
|
78
|
+
# Should match log level and apply blue color
|
79
|
+
assert @io.string.include? "\e[34mtest message"
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def colorize(message, color, style = nil)
|
85
|
+
style = "#{style};" if style
|
86
|
+
"\e[#{style}#{color}m" + message + "\e[0m"
|
87
|
+
end
|
88
|
+
|
89
|
+
def clear_logger
|
90
|
+
@io = StringIO.new
|
91
|
+
@io.stubs(:tty?).returns(true)
|
92
|
+
@logger.device = @io
|
93
|
+
end
|
94
|
+
end
|