le1t0-capistrano 2.5.18.001

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. data/.gitignore +9 -0
  2. data/CHANGELOG +843 -0
  3. data/README +102 -0
  4. data/Rakefile +36 -0
  5. data/VERSION +1 -0
  6. data/bin/cap +4 -0
  7. data/bin/capify +86 -0
  8. data/lib/capistrano.rb +2 -0
  9. data/lib/capistrano/callback.rb +45 -0
  10. data/lib/capistrano/cli.rb +47 -0
  11. data/lib/capistrano/cli/execute.rb +85 -0
  12. data/lib/capistrano/cli/help.rb +125 -0
  13. data/lib/capistrano/cli/help.txt +78 -0
  14. data/lib/capistrano/cli/options.rb +243 -0
  15. data/lib/capistrano/cli/ui.rb +40 -0
  16. data/lib/capistrano/command.rb +283 -0
  17. data/lib/capistrano/configuration.rb +44 -0
  18. data/lib/capistrano/configuration/actions/file_transfer.rb +52 -0
  19. data/lib/capistrano/configuration/actions/inspect.rb +46 -0
  20. data/lib/capistrano/configuration/actions/invocation.rb +295 -0
  21. data/lib/capistrano/configuration/callbacks.rb +148 -0
  22. data/lib/capistrano/configuration/connections.rb +204 -0
  23. data/lib/capistrano/configuration/execution.rb +143 -0
  24. data/lib/capistrano/configuration/loading.rb +197 -0
  25. data/lib/capistrano/configuration/namespaces.rb +197 -0
  26. data/lib/capistrano/configuration/roles.rb +73 -0
  27. data/lib/capistrano/configuration/servers.rb +98 -0
  28. data/lib/capistrano/configuration/variables.rb +127 -0
  29. data/lib/capistrano/errors.rb +19 -0
  30. data/lib/capistrano/extensions.rb +57 -0
  31. data/lib/capistrano/logger.rb +59 -0
  32. data/lib/capistrano/processable.rb +53 -0
  33. data/lib/capistrano/recipes/compat.rb +32 -0
  34. data/lib/capistrano/recipes/deploy.rb +589 -0
  35. data/lib/capistrano/recipes/deploy/dependencies.rb +44 -0
  36. data/lib/capistrano/recipes/deploy/local_dependency.rb +54 -0
  37. data/lib/capistrano/recipes/deploy/remote_dependency.rb +105 -0
  38. data/lib/capistrano/recipes/deploy/scm.rb +19 -0
  39. data/lib/capistrano/recipes/deploy/scm/accurev.rb +169 -0
  40. data/lib/capistrano/recipes/deploy/scm/base.rb +196 -0
  41. data/lib/capistrano/recipes/deploy/scm/bzr.rb +86 -0
  42. data/lib/capistrano/recipes/deploy/scm/cvs.rb +152 -0
  43. data/lib/capistrano/recipes/deploy/scm/darcs.rb +96 -0
  44. data/lib/capistrano/recipes/deploy/scm/git.rb +278 -0
  45. data/lib/capistrano/recipes/deploy/scm/mercurial.rb +137 -0
  46. data/lib/capistrano/recipes/deploy/scm/none.rb +44 -0
  47. data/lib/capistrano/recipes/deploy/scm/perforce.rb +138 -0
  48. data/lib/capistrano/recipes/deploy/scm/subversion.rb +121 -0
  49. data/lib/capistrano/recipes/deploy/strategy.rb +19 -0
  50. data/lib/capistrano/recipes/deploy/strategy/base.rb +79 -0
  51. data/lib/capistrano/recipes/deploy/strategy/checkout.rb +20 -0
  52. data/lib/capistrano/recipes/deploy/strategy/copy.rb +218 -0
  53. data/lib/capistrano/recipes/deploy/strategy/export.rb +20 -0
  54. data/lib/capistrano/recipes/deploy/strategy/remote.rb +52 -0
  55. data/lib/capistrano/recipes/deploy/strategy/remote_cache.rb +56 -0
  56. data/lib/capistrano/recipes/deploy/templates/maintenance.rhtml +53 -0
  57. data/lib/capistrano/recipes/standard.rb +37 -0
  58. data/lib/capistrano/recipes/templates/maintenance.rhtml +53 -0
  59. data/lib/capistrano/role.rb +102 -0
  60. data/lib/capistrano/server_definition.rb +56 -0
  61. data/lib/capistrano/shell.rb +260 -0
  62. data/lib/capistrano/ssh.rb +99 -0
  63. data/lib/capistrano/task_definition.rb +75 -0
  64. data/lib/capistrano/transfer.rb +216 -0
  65. data/lib/capistrano/version.rb +18 -0
  66. data/test/cli/execute_test.rb +132 -0
  67. data/test/cli/help_test.rb +165 -0
  68. data/test/cli/options_test.rb +329 -0
  69. data/test/cli/ui_test.rb +28 -0
  70. data/test/cli_test.rb +17 -0
  71. data/test/command_test.rb +286 -0
  72. data/test/configuration/actions/file_transfer_test.rb +61 -0
  73. data/test/configuration/actions/inspect_test.rb +65 -0
  74. data/test/configuration/actions/invocation_test.rb +225 -0
  75. data/test/configuration/callbacks_test.rb +220 -0
  76. data/test/configuration/connections_test.rb +349 -0
  77. data/test/configuration/execution_test.rb +175 -0
  78. data/test/configuration/loading_test.rb +132 -0
  79. data/test/configuration/namespace_dsl_test.rb +311 -0
  80. data/test/configuration/roles_test.rb +144 -0
  81. data/test/configuration/servers_test.rb +158 -0
  82. data/test/configuration/variables_test.rb +184 -0
  83. data/test/configuration_test.rb +88 -0
  84. data/test/deploy/local_dependency_test.rb +76 -0
  85. data/test/deploy/remote_dependency_test.rb +114 -0
  86. data/test/deploy/scm/accurev_test.rb +23 -0
  87. data/test/deploy/scm/base_test.rb +55 -0
  88. data/test/deploy/scm/bzr_test.rb +51 -0
  89. data/test/deploy/scm/darcs_test.rb +37 -0
  90. data/test/deploy/scm/git_test.rb +184 -0
  91. data/test/deploy/scm/mercurial_test.rb +134 -0
  92. data/test/deploy/scm/none_test.rb +35 -0
  93. data/test/deploy/scm/subversion_test.rb +32 -0
  94. data/test/deploy/strategy/copy_test.rb +302 -0
  95. data/test/extensions_test.rb +69 -0
  96. data/test/fixtures/cli_integration.rb +5 -0
  97. data/test/fixtures/config.rb +5 -0
  98. data/test/fixtures/custom.rb +3 -0
  99. data/test/logger_test.rb +123 -0
  100. data/test/role_test.rb +11 -0
  101. data/test/server_definition_test.rb +121 -0
  102. data/test/shell_test.rb +90 -0
  103. data/test/ssh_test.rb +104 -0
  104. data/test/task_definition_test.rb +116 -0
  105. data/test/transfer_test.rb +160 -0
  106. data/test/utils.rb +39 -0
  107. metadata +289 -0
@@ -0,0 +1,99 @@
1
+ begin
2
+ require 'rubygems'
3
+ gem 'net-ssh', ">= 2.0.10"
4
+ rescue LoadError, NameError
5
+ end
6
+
7
+ require 'net/ssh'
8
+
9
+ module Capistrano
10
+ # A helper class for dealing with SSH connections.
11
+ class SSH
12
+ # Patch an accessor onto an SSH connection so that we can record the server
13
+ # definition object that defines the connection. This is useful because
14
+ # the gateway returns connections whose "host" is 127.0.0.1, instead of
15
+ # the host on the other side of the tunnel.
16
+ module Server #:nodoc:
17
+ def self.apply_to(connection, server)
18
+ connection.extend(Server)
19
+ connection.xserver = server
20
+ connection
21
+ end
22
+
23
+ attr_accessor :xserver
24
+ end
25
+
26
+ # An abstraction to make it possible to connect to the server via public key
27
+ # without prompting for the password. If the public key authentication fails
28
+ # this will fall back to password authentication.
29
+ #
30
+ # +server+ must be an instance of ServerDefinition.
31
+ #
32
+ # If a block is given, the new session is yielded to it, otherwise the new
33
+ # session is returned.
34
+ #
35
+ # If an :ssh_options key exists in +options+, it is passed to the Net::SSH
36
+ # constructor. Values in +options+ are then merged into it, and any
37
+ # connection information in +server+ is added last, so that +server+ info
38
+ # takes precedence over +options+, which takes precendence over ssh_options.
39
+ def self.connect(server, options={})
40
+ connection_strategy(server, options) do |host, user, connection_options|
41
+ connection = Net::SSH.start(host, user, connection_options)
42
+ Server.apply_to(connection, server)
43
+ end
44
+ end
45
+
46
+ # Abstracts the logic for establishing an SSH connection (which includes
47
+ # testing for connection failures and retrying with a password, and so forth,
48
+ # mostly made complicated because of the fact that some of these variables
49
+ # might be lazily evaluated and try to do something like prompt the user,
50
+ # which should only happen when absolutely necessary.
51
+ #
52
+ # This will yield the hostname, username, and a hash of connection options
53
+ # to the given block, which should return a new connection.
54
+ def self.connection_strategy(server, options={}, &block)
55
+ methods = [ %w(publickey hostbased), %w(password keyboard-interactive) ]
56
+ password_value = nil
57
+
58
+ # construct the hash of ssh options that should be passed more-or-less
59
+ # directly to Net::SSH. This will be the general ssh options, merged with
60
+ # the server-specific ssh-options.
61
+ ssh_options = (options[:ssh_options] || {}).merge(server.options[:ssh_options] || {})
62
+
63
+ # load any SSH configuration files that were specified in the SSH options. This
64
+ # will load from ~/.ssh/config and /etc/ssh_config by default (see Net::SSH
65
+ # for details). Merge the explicitly given ssh_options over the top of the info
66
+ # from the config file.
67
+ ssh_options = Net::SSH.configuration_for(server.host, ssh_options.fetch(:config, true)).merge(ssh_options)
68
+
69
+ # Once we've loaded the config, we don't need Net::SSH to do it again.
70
+ ssh_options[:config] = false
71
+
72
+ user = server.user || options[:user] || ssh_options[:username] ||
73
+ ssh_options[:user] || ServerDefinition.default_user
74
+ port = server.port || options[:port] || ssh_options[:port]
75
+
76
+ # the .ssh/config file might have changed the host-name on us
77
+ host = ssh_options.fetch(:host_name, server.host)
78
+
79
+ ssh_options[:port] = port if port
80
+
81
+ # delete these, since we've determined which username to use by this point
82
+ ssh_options.delete(:username)
83
+ ssh_options.delete(:user)
84
+
85
+ begin
86
+ connection_options = ssh_options.merge(
87
+ :password => password_value,
88
+ :auth_methods => ssh_options[:auth_methods] || methods.shift
89
+ )
90
+
91
+ yield host, user, connection_options
92
+ rescue Net::SSH::AuthenticationFailed
93
+ raise if methods.empty? || ssh_options[:auth_methods]
94
+ password_value = options[:password]
95
+ retry
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,75 @@
1
+ require 'capistrano/server_definition'
2
+
3
+ module Capistrano
4
+ # Represents the definition of a single task.
5
+ class TaskDefinition
6
+ attr_reader :name, :namespace, :options, :body, :desc, :on_error, :max_hosts
7
+
8
+ def initialize(name, namespace, options={}, &block)
9
+
10
+ if name.to_s =~ /^(?:before_|after_)/
11
+ Kernel.warn("[Deprecation Warning] Naming tasks with before_ and after_ is deprecated, please see the new before() and after() methods. (Offending task name was #{name})")
12
+ end
13
+
14
+ @name, @namespace, @options = name, namespace, options
15
+ @desc = @options.delete(:desc)
16
+ @on_error = options.delete(:on_error)
17
+ @max_hosts = options[:max_hosts] && options[:max_hosts].to_i
18
+ @body = block or raise ArgumentError, "a task requires a block"
19
+ @servers = nil
20
+ end
21
+
22
+ # Returns the task's fully-qualified name, including the namespace
23
+ def fully_qualified_name
24
+ @fully_qualified_name ||= begin
25
+ if namespace.default_task == self
26
+ namespace.fully_qualified_name
27
+ else
28
+ [namespace.fully_qualified_name, name].compact.join(":")
29
+ end
30
+ end
31
+ end
32
+
33
+ # Returns the description for this task, with newlines collapsed and
34
+ # whitespace stripped. Returns the empty string if there is no
35
+ # description for this task.
36
+ def description(rebuild=false)
37
+ @description = nil if rebuild
38
+ @description ||= begin
39
+ description = @desc || ""
40
+
41
+ indentation = description[/\A\s+/]
42
+ if indentation
43
+ reformatted_description = ""
44
+ description.strip.each_line do |line|
45
+ line = line.chomp.sub(/^#{indentation}/, "")
46
+ line = line.gsub(/#{indentation}\s*/, " ") if line[/^\S/]
47
+ reformatted_description << line << "\n"
48
+ end
49
+ description = reformatted_description
50
+ end
51
+
52
+ description.strip.gsub(/\r\n/, "\n")
53
+ end
54
+ end
55
+
56
+ # Returns the first sentence of the full description. If +max_length+ is
57
+ # given, the result will be truncated if it is longer than +max_length+,
58
+ # and an ellipsis appended.
59
+ def brief_description(max_length=nil)
60
+ brief = description[/^.*?\.(?=\s|$)/] || description
61
+
62
+ if max_length && brief.length > max_length
63
+ brief = brief[0,max_length-3] + "..."
64
+ end
65
+
66
+ brief
67
+ end
68
+
69
+ # Indicates whether the task wants to continue, even if a server has failed
70
+ # previously
71
+ def continue_on_error?
72
+ @on_error == :continue
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,216 @@
1
+ require 'net/scp'
2
+ require 'net/sftp'
3
+
4
+ require 'capistrano/processable'
5
+
6
+ module Capistrano
7
+ class Transfer
8
+ include Processable
9
+
10
+ def self.process(direction, from, to, sessions, options={}, &block)
11
+ new(direction, from, to, sessions, options, &block).process!
12
+ end
13
+
14
+ attr_reader :sessions
15
+ attr_reader :options
16
+ attr_reader :callback
17
+
18
+ attr_reader :transport
19
+ attr_reader :direction
20
+ attr_reader :from
21
+ attr_reader :to
22
+
23
+ attr_reader :logger
24
+ attr_reader :transfers
25
+
26
+ def initialize(direction, from, to, sessions, options={}, &block)
27
+ @direction = direction
28
+ @from = from
29
+ @to = to
30
+ @sessions = sessions
31
+ @options = options
32
+ @callback = block
33
+
34
+ @transport = options.fetch(:via, :sftp)
35
+ @logger = options.delete(:logger)
36
+
37
+ @session_map = {}
38
+
39
+ prepare_transfers
40
+ end
41
+
42
+ def process!
43
+ loop do
44
+ begin
45
+ break unless process_iteration { active? }
46
+ rescue Exception => error
47
+ if error.respond_to?(:session)
48
+ handle_error(error)
49
+ else
50
+ raise
51
+ end
52
+ end
53
+ end
54
+
55
+ failed = transfers.select { |txfr| txfr[:failed] }
56
+ if failed.any?
57
+ hosts = failed.map { |txfr| txfr[:server] }
58
+ errors = failed.map { |txfr| "#{txfr[:error]} (#{txfr[:error].message})" }.uniq.join(", ")
59
+ error = TransferError.new("#{operation} via #{transport} failed on #{hosts.join(',')}: #{errors}")
60
+ error.hosts = hosts
61
+
62
+ logger.important(error.message) if logger
63
+ raise error
64
+ end
65
+
66
+ logger.debug "#{transport} #{operation} complete" if logger
67
+ self
68
+ end
69
+
70
+ def active?
71
+ transfers.any? { |transfer| transfer.active? }
72
+ end
73
+
74
+ def operation
75
+ "#{direction}load"
76
+ end
77
+
78
+ def sanitized_from
79
+ if from.responds_to?(:read)
80
+ "#<#{from.class}>"
81
+ else
82
+ from
83
+ end
84
+ end
85
+
86
+ def sanitized_to
87
+ if to.responds_to?(:read)
88
+ "#<#{to.class}>"
89
+ else
90
+ to
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ def session_map
97
+ @session_map
98
+ end
99
+
100
+ def prepare_transfers
101
+ logger.info "#{transport} #{operation} #{from} -> #{to}" if logger
102
+
103
+ @transfers = sessions.map do |session|
104
+ session_from = normalize(from, session)
105
+ session_to = normalize(to, session)
106
+
107
+ session_map[session] = case transport
108
+ when :sftp
109
+ prepare_sftp_transfer(session_from, session_to, session)
110
+ when :scp
111
+ prepare_scp_transfer(session_from, session_to, session)
112
+ else
113
+ raise ArgumentError, "unsupported transport type: #{transport.inspect}"
114
+ end
115
+ end
116
+ end
117
+
118
+ def prepare_scp_transfer(from, to, session)
119
+ real_callback = callback || Proc.new do |channel, name, sent, total|
120
+ logger.trace "[#{channel[:host]}] #{name}" if logger && sent == 0
121
+ end
122
+
123
+ channel = case direction
124
+ when :up
125
+ session.scp.upload(from, to, options, &real_callback)
126
+ when :down
127
+ session.scp.download(from, to, options, &real_callback)
128
+ else
129
+ raise ArgumentError, "unsupported transfer direction: #{direction.inspect}"
130
+ end
131
+
132
+ channel[:server] = session.xserver
133
+ channel[:host] = session.xserver.host
134
+
135
+ return channel
136
+ end
137
+
138
+ class SFTPTransferWrapper
139
+ attr_reader :operation
140
+
141
+ def initialize(session, &callback)
142
+ session.sftp(false).connect do |sftp|
143
+ @operation = callback.call(sftp)
144
+ end
145
+ end
146
+
147
+ def active?
148
+ @operation.nil? || @operation.active?
149
+ end
150
+
151
+ def [](key)
152
+ @operation[key]
153
+ end
154
+
155
+ def []=(key, value)
156
+ @operation[key] = value
157
+ end
158
+
159
+ def abort!
160
+ @operation.abort!
161
+ end
162
+ end
163
+
164
+ def prepare_sftp_transfer(from, to, session)
165
+ SFTPTransferWrapper.new(session) do |sftp|
166
+ real_callback = Proc.new do |event, op, *args|
167
+ if callback
168
+ callback.call(event, op, *args)
169
+ elsif event == :open
170
+ logger.trace "[#{op[:host]}] #{args[0].remote}"
171
+ elsif event == :finish
172
+ logger.trace "[#{op[:host]}] done"
173
+ end
174
+ end
175
+
176
+ opts = options.dup
177
+ opts[:properties] = (opts[:properties] || {}).merge(
178
+ :server => session.xserver,
179
+ :host => session.xserver.host)
180
+
181
+ case direction
182
+ when :up
183
+ sftp.upload(from, to, opts, &real_callback)
184
+ when :down
185
+ sftp.download(from, to, opts, &real_callback)
186
+ else
187
+ raise ArgumentError, "unsupported transfer direction: #{direction.inspect}"
188
+ end
189
+ end
190
+ end
191
+
192
+ def normalize(argument, session)
193
+ if argument.is_a?(String)
194
+ argument.gsub(/\$CAPISTRANO:HOST\$/, session.xserver.host)
195
+ elsif argument.respond_to?(:read)
196
+ pos = argument.pos
197
+ clone = StringIO.new(argument.read)
198
+ clone.pos = argument.pos = pos
199
+ clone
200
+ else
201
+ argument
202
+ end
203
+ end
204
+
205
+ def handle_error(error)
206
+ transfer = session_map[error.session]
207
+ transfer[:error] = error
208
+ transfer[:failed] = true
209
+
210
+ case transport
211
+ when :sftp then transfer.abort!
212
+ when :scp then transfer.close
213
+ end
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,18 @@
1
+ require 'scanf'
2
+ module Capistrano
3
+
4
+ class Version
5
+
6
+ CURRENT = File.read(File.dirname(__FILE__) + '/../../VERSION')
7
+
8
+ MAJOR, MINOR, TINY = CURRENT.scanf('%d.%d.%d')
9
+
10
+ STRING = CURRENT.to_s
11
+
12
+ def self.to_s
13
+ CURRENT
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,132 @@
1
+ require "utils"
2
+ require 'capistrano/cli/execute'
3
+
4
+ class CLIExecuteTest < Test::Unit::TestCase
5
+ class MockCLI
6
+ attr_reader :options
7
+
8
+ def initialize
9
+ @options = {}
10
+ end
11
+
12
+ include Capistrano::CLI::Execute
13
+ end
14
+
15
+ def setup
16
+ @cli = MockCLI.new
17
+ @logger = stub_everything
18
+ @config = stub(:logger => @logger, :debug= => nil, :dry_run= => nil, :preserve_roles= => nil)
19
+ @config.stubs(:set)
20
+ @config.stubs(:load)
21
+ @config.stubs(:trigger)
22
+ @cli.stubs(:instantiate_configuration).returns(@config)
23
+ end
24
+
25
+ def test_execute_should_set_logger_verbosity
26
+ @cli.options[:verbose] = 7
27
+ @logger.expects(:level=).with(7)
28
+ @cli.execute!
29
+ end
30
+
31
+ def test_execute_should_set_password
32
+ @cli.options[:password] = "nosoup4u"
33
+ @config.expects(:set).with(:password, "nosoup4u")
34
+ @cli.execute!
35
+ end
36
+
37
+ def test_execute_should_set_prevars_before_loading
38
+ @config.expects(:load).never
39
+ @config.expects(:set).with(:stage, "foobar")
40
+ @config.expects(:load).with("standard")
41
+ @cli.options[:pre_vars] = { :stage => "foobar" }
42
+ @cli.execute!
43
+ end
44
+
45
+ def test_execute_should_load_sysconf_if_sysconf_set_and_exists
46
+ @cli.options[:sysconf] = "/etc/capistrano.conf"
47
+ @config.expects(:load).with("/etc/capistrano.conf")
48
+ File.expects(:file?).with("/etc/capistrano.conf").returns(true)
49
+ @cli.execute!
50
+ end
51
+
52
+ def test_execute_should_not_load_sysconf_when_sysconf_set_and_not_exists
53
+ @cli.options[:sysconf] = "/etc/capistrano.conf"
54
+ File.expects(:file?).with("/etc/capistrano.conf").returns(false)
55
+ @cli.execute!
56
+ end
57
+
58
+ def test_execute_should_load_dotfile_if_dotfile_set_and_exists
59
+ @cli.options[:dotfile] = "/home/jamis/.caprc"
60
+ @config.expects(:load).with("/home/jamis/.caprc")
61
+ File.expects(:file?).with("/home/jamis/.caprc").returns(true)
62
+ @cli.execute!
63
+ end
64
+
65
+ def test_execute_should_not_load_dotfile_when_dotfile_set_and_not_exists
66
+ @cli.options[:dotfile] = "/home/jamis/.caprc"
67
+ File.expects(:file?).with("/home/jamis/.caprc").returns(false)
68
+ @cli.execute!
69
+ end
70
+
71
+ def test_execute_should_load_recipes_when_recipes_are_given
72
+ @cli.options[:recipes] = %w(config/deploy path/to/extra)
73
+ @config.expects(:load).with("config/deploy")
74
+ @config.expects(:load).with("path/to/extra")
75
+ @cli.execute!
76
+ end
77
+
78
+ def test_execute_should_set_vars_and_execute_tasks
79
+ @cli.options[:vars] = { :foo => "bar", :baz => "bang" }
80
+ @cli.options[:actions] = %w(first second)
81
+ @config.expects(:set).with(:foo, "bar")
82
+ @config.expects(:set).with(:baz, "bang")
83
+ @config.expects(:find_and_execute_task).with("first", :before => :start, :after => :finish)
84
+ @config.expects(:find_and_execute_task).with("second", :before => :start, :after => :finish)
85
+ @cli.execute!
86
+ end
87
+
88
+ def test_execute_should_call_load_and_exit_triggers
89
+ @cli.options[:actions] = %w(first second)
90
+ @config.expects(:find_and_execute_task).with("first", :before => :start, :after => :finish)
91
+ @config.expects(:find_and_execute_task).with("second", :before => :start, :after => :finish)
92
+ @config.expects(:trigger).never
93
+ @config.expects(:trigger).with(:load)
94
+ @config.expects(:trigger).with(:exit)
95
+ @cli.execute!
96
+ end
97
+
98
+ def test_execute_should_call_handle_error_when_exceptions_occur
99
+ @config.expects(:load).raises(Exception, "boom")
100
+ @cli.expects(:handle_error).with { |e,| Exception === e }
101
+ @cli.execute!
102
+ end
103
+
104
+ def test_execute_should_return_config_instance
105
+ assert_equal @config, @cli.execute!
106
+ end
107
+
108
+ def test_instantiate_configuration_should_return_new_configuration_instance
109
+ assert_instance_of Capistrano::Configuration, MockCLI.new.instantiate_configuration
110
+ end
111
+
112
+ def test_handle_error_with_auth_error_should_abort_with_message_including_user_name
113
+ @cli.expects(:abort).with { |s| s.include?("jamis") }
114
+ @cli.handle_error(Net::SSH::AuthenticationFailed.new("jamis"))
115
+ end
116
+
117
+ def test_handle_error_with_cap_error_should_abort_with_message
118
+ @cli.expects(:abort).with("Wish you were here")
119
+ @cli.handle_error(Capistrano::Error.new("Wish you were here"))
120
+ end
121
+
122
+ def test_handle_error_with_other_errors_should_reraise_error
123
+ other_error = Class.new(RuntimeError)
124
+ assert_raises(other_error) { @cli.handle_error(other_error.new("boom")) }
125
+ end
126
+
127
+ def test_class_execute_method_should_call_parse_and_execute_with_ARGV
128
+ cli = mock(:execute! => nil)
129
+ MockCLI.expects(:parse).with(ARGV).returns(cli)
130
+ MockCLI.execute
131
+ end
132
+ end