capistrano 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/CHANGELOG +29 -0
  2. data/README +4 -7
  3. data/bin/cap +0 -0
  4. data/bin/capify +0 -0
  5. data/lib/capistrano/command.rb +32 -38
  6. data/lib/capistrano/configuration/actions/file_transfer.rb +21 -13
  7. data/lib/capistrano/configuration/actions/invocation.rb +1 -1
  8. data/lib/capistrano/configuration/connections.rb +30 -20
  9. data/lib/capistrano/errors.rb +1 -1
  10. data/lib/capistrano/processable.rb +53 -0
  11. data/lib/capistrano/recipes/deploy/remote_dependency.rb +6 -0
  12. data/lib/capistrano/recipes/deploy/scm/git.rb +26 -11
  13. data/lib/capistrano/recipes/deploy/scm/none.rb +44 -0
  14. data/lib/capistrano/recipes/deploy/strategy/base.rb +6 -0
  15. data/lib/capistrano/recipes/deploy/strategy/copy.rb +74 -3
  16. data/lib/capistrano/recipes/deploy.rb +15 -13
  17. data/lib/capistrano/role.rb +0 -14
  18. data/lib/capistrano/server_definition.rb +5 -0
  19. data/lib/capistrano/shell.rb +21 -17
  20. data/lib/capistrano/ssh.rb +24 -58
  21. data/lib/capistrano/transfer.rb +216 -0
  22. data/lib/capistrano/version.rb +1 -1
  23. data/test/cli/execute_test.rb +1 -1
  24. data/test/cli/help_test.rb +1 -1
  25. data/test/cli/options_test.rb +1 -1
  26. data/test/cli/ui_test.rb +1 -1
  27. data/test/cli_test.rb +1 -1
  28. data/test/command_test.rb +31 -51
  29. data/test/configuration/actions/file_transfer_test.rb +21 -19
  30. data/test/configuration/actions/inspect_test.rb +1 -1
  31. data/test/configuration/actions/invocation_test.rb +6 -6
  32. data/test/configuration/callbacks_test.rb +1 -1
  33. data/test/configuration/connections_test.rb +11 -12
  34. data/test/configuration/execution_test.rb +1 -1
  35. data/test/configuration/loading_test.rb +1 -1
  36. data/test/configuration/namespace_dsl_test.rb +1 -1
  37. data/test/configuration/roles_test.rb +1 -1
  38. data/test/configuration/servers_test.rb +1 -1
  39. data/test/configuration/variables_test.rb +1 -1
  40. data/test/configuration_test.rb +1 -1
  41. data/test/deploy/scm/accurev_test.rb +1 -1
  42. data/test/deploy/scm/base_test.rb +1 -1
  43. data/test/deploy/scm/git_test.rb +10 -6
  44. data/test/deploy/scm/mercurial_test.rb +1 -1
  45. data/test/deploy/strategy/copy_test.rb +120 -27
  46. data/test/extensions_test.rb +1 -1
  47. data/test/logger_test.rb +1 -1
  48. data/test/server_definition_test.rb +1 -1
  49. data/test/shell_test.rb +27 -1
  50. data/test/ssh_test.rb +27 -21
  51. data/test/task_definition_test.rb +1 -1
  52. data/test/transfer_test.rb +160 -0
  53. data/test/utils.rb +30 -34
  54. data/test/version_test.rb +1 -1
  55. metadata +26 -14
  56. data/lib/capistrano/gateway.rb +0 -131
  57. data/lib/capistrano/upload.rb +0 -152
  58. data/test/gateway_test.rb +0 -167
  59. data/test/upload_test.rb +0 -131
@@ -89,6 +89,15 @@ ensure
89
89
  ENV[name] = saved
90
90
  end
91
91
 
92
+ # If :run_method is :sudo (or :use_sudo is true), this executes the given command
93
+ # via +sudo+. Otherwise is uses +run+. Further, if sudo is being used and :runner
94
+ # is set, the command will be executed as the user given by :runner.
95
+ def try_sudo(command)
96
+ as = fetch(:runner, "app")
97
+ via = fetch(:run_method, :sudo)
98
+ invoke_command(command, :via => via, :as => as)
99
+ end
100
+
92
101
  # =========================================================================
93
102
  # These are the tasks that are available to help with deploying web apps,
94
103
  # and specifically, Rails applications. You can have cap give you a summary
@@ -122,7 +131,7 @@ namespace :deploy do
122
131
  task :setup, :except => { :no_release => true } do
123
132
  dirs = [deploy_to, releases_path, shared_path]
124
133
  dirs += %w(system log pids).map { |d| File.join(shared_path, d) }
125
- run "umask 02 && mkdir -p #{dirs.join(' ')}"
134
+ try_sudo "umask 02 && mkdir -p #{dirs.join(' ')}"
126
135
  end
127
136
 
128
137
  desc <<-DESC
@@ -246,9 +255,7 @@ namespace :deploy do
246
255
  set :use_sudo, false
247
256
  DESC
248
257
  task :restart, :roles => :app, :except => { :no_release => true } do
249
- as = fetch(:runner, "app")
250
- via = fetch(:run_method, :sudo)
251
- invoke_command "#{current_path}/script/process/reaper", :via => via, :as => as
258
+ try_sudo "#{current_path}/script/process/reaper"
252
259
  end
253
260
 
254
261
  desc <<-DESC
@@ -337,7 +344,7 @@ namespace :deploy do
337
344
  directories = (releases - releases.last(count)).map { |release|
338
345
  File.join(releases_path, release) }.join(" ")
339
346
 
340
- invoke_command "rm -rf #{directories}", :via => run_method
347
+ try_sudo "rm -rf #{directories}"
341
348
  end
342
349
  end
343
350
 
@@ -406,9 +413,7 @@ namespace :deploy do
406
413
  the :use_sudo variable to false.
407
414
  DESC
408
415
  task :start, :roles => :app do
409
- as = fetch(:runner, "app")
410
- via = fetch(:run_method, :sudo)
411
- invoke_command "sh -c 'cd #{current_path} && nohup script/spin'", :via => via, :as => as
416
+ try_sudo "sh -c 'cd #{current_path} && nohup script/spin'"
412
417
  end
413
418
 
414
419
  desc <<-DESC
@@ -423,11 +428,8 @@ namespace :deploy do
423
428
  the :use_sudo variable to false.
424
429
  DESC
425
430
  task :stop, :roles => :app do
426
- as = fetch(:runner, "app")
427
- via = fetch(:run_method, :sudo)
428
-
429
- invoke_command "if [ -f #{current_path}/tmp/pids/dispatch.spawner.pid ]; then #{current_path}/script/process/reaper -a kill -r dispatch.spawner.pid; fi", :via => via, :as => as
430
- invoke_command "#{current_path}/script/process/reaper -a kill", :via => via, :as => as
431
+ try_sudo "if [ -f #{current_path}/tmp/pids/dispatch.spawner.pid ]; then #{current_path}/script/process/reaper -a kill -r dispatch.spawner.pid; fi"
432
+ try_sudo "#{current_path}/script/process/reaper -a kill"
431
433
  end
432
434
 
433
435
  namespace :pending do
@@ -1,4 +1,3 @@
1
-
2
1
  module Capistrano
3
2
  class Role
4
3
  include Enumerable
@@ -34,27 +33,14 @@ module Capistrano
34
33
  servers.empty?
35
34
  end
36
35
 
37
- # Resets the cache, so that proc values may be recalculated.
38
- # There should be a command in Configuration::Roles to do this,
39
- # but I haven't needed it yet, and I'm not sure yet
40
- # what to call that command. Suggestions?
41
- def reset!
42
- @dynamic_servers.each { |item| item.reset! }
43
- end
44
-
45
- # Clears everything. I still thing this should be 'clear!', but that's not
46
- # the way Array does it.
47
36
  def clear
48
37
  @dynamic_servers.clear
49
38
  @static_servers.clear
50
39
  end
51
40
 
52
- # Mostly for documentation purposes. Doesn't seem to do anything.
53
41
  protected
54
42
 
55
43
  # This is the combination of a block, a hash of options, and a cached value.
56
- # It is protected because it is an implementation detail -- the original
57
- # implementation was two lists (blocks and cached results of calling them).
58
44
  class DynamicServerList
59
45
  def initialize (block, options)
60
46
  @block = block
@@ -7,6 +7,11 @@ module Capistrano
7
7
  attr_reader :port
8
8
  attr_reader :options
9
9
 
10
+ # The default user name to use when a user name is not explicitly provided
11
+ def self.default_user
12
+ ENV['USER'] || ENV['USERNAME'] || "not-specified"
13
+ end
14
+
10
15
  def initialize(string, options={})
11
16
  @user, @host, @port = string.match(/^(?:([^;,:=]+)@|)(.*?)(?::(\d+)|)$/)[1,3]
12
17
 
@@ -1,4 +1,5 @@
1
1
  require 'thread'
2
+ require 'capistrano/processable'
2
3
 
3
4
  module Capistrano
4
5
  # The Capistrano::Shell class is the guts of the "shell" task. It implements
@@ -6,6 +7,8 @@ module Capistrano
6
7
  # commands. It makes for a GREAT way to monitor systems, and perform quick
7
8
  # maintenance on one or more machines.
8
9
  class Shell
10
+ include Processable
11
+
9
12
  # A Readline replacement for platforms where readline is either
10
13
  # unavailable, or has not been installed.
11
14
  class ReadlineFallback #:nodoc:
@@ -142,11 +145,13 @@ HELP
142
145
  # be invoked. Otherwise, it is executed as a command on all associated
143
146
  # servers.
144
147
  def exec(command)
145
- if command[0] == ?!
146
- exec_tasks(command[1..-1].split)
147
- else
148
- servers = connect(configuration.current_task)
149
- exec_command(command, servers)
148
+ @mutex.synchronize do
149
+ if command[0] == ?!
150
+ exec_tasks(command[1..-1].split)
151
+ else
152
+ servers = connect(configuration.current_task)
153
+ exec_command(command, servers)
154
+ end
150
155
  end
151
156
  ensure
152
157
  STDOUT.flush
@@ -169,7 +174,8 @@ HELP
169
174
  command = command.gsub(/\bsudo\b/, "sudo -p '#{configuration.sudo_prompt}'")
170
175
  processor = configuration.sudo_behavior_callback(Configuration.default_io_proc)
171
176
  sessions = servers.map { |server| configuration.sessions[server] }
172
- cmd = Command.new(command, sessions, :logger => configuration.logger, &processor)
177
+ options = configuration.add_default_command_options({})
178
+ cmd = Command.new(command, sessions, options.merge(:logger => configuration.logger), &processor)
173
179
  previous = trap("INT") { cmd.stop! }
174
180
  cmd.process!
175
181
  rescue Capistrano::Error => error
@@ -196,17 +202,10 @@ HELP
196
202
 
197
203
  @mutex = Mutex.new
198
204
  @bgthread = Thread.new do
199
- loop do
200
- ready = configuration.sessions.values.select { |sess| sess.connection.reader_ready? }
201
- if ready.empty?
202
- sleep 0.1
203
- else
204
- @mutex.synchronize do
205
- ready.each { |session| session.connection.process(true) }
206
- end
207
- end
208
- end
205
+ loop do
206
+ @mutex.synchronize { process_iteration(0.1) }
209
207
  end
208
+ end
210
209
  end
211
210
 
212
211
  # Set the given option to +value+.
@@ -244,7 +243,7 @@ HELP
244
243
  old_var, ENV[env_var] = ENV[env_var], (scope_value == "all" ? nil : scope_value) if env_var
245
244
  if command
246
245
  begin
247
- @mutex.synchronize { exec(command) }
246
+ exec(command)
248
247
  ensure
249
248
  ENV[env_var] = old_var if env_var
250
249
  end
@@ -253,4 +252,9 @@ HELP
253
252
  end
254
253
  end
255
254
  end
255
+
256
+ # All open sessions, needed to satisfy the Command::Processable include
257
+ def sessions
258
+ configuration.sessions.values
259
+ end
256
260
  end
@@ -1,61 +1,12 @@
1
1
  begin
2
2
  require 'rubygems'
3
- gem 'net-ssh', "< 1.99.0"
3
+ gem 'net-ssh', ">= 1.99.1"
4
4
  rescue LoadError, NameError
5
5
  end
6
6
 
7
7
  require 'net/ssh'
8
8
 
9
9
  module Capistrano
10
- unless ENV['SKIP_VERSION_CHECK']
11
- require 'capistrano/version'
12
- require 'net/ssh/version'
13
- ssh_version = [Net::SSH::Version::MAJOR, Net::SSH::Version::MINOR, Net::SSH::Version::TINY]
14
- if !Version.check(Version::SSH_REQUIRED, ssh_version)
15
- raise "You have Net::SSH #{ssh_version.join(".")}, but you need at least #{Version::SSH_REQUIRED.join(".")}"
16
- end
17
- end
18
-
19
- # Now, Net::SSH is kind of silly, and tries to lazy-load everything. This
20
- # wreaks havoc with the parallel connection trick that Capistrano wants to
21
- # use, so we're going to do something hideously ugly here and force all the
22
- # files that Net::SSH uses to load RIGHT NOW, rather than lazily.
23
-
24
- net_ssh_dependencies = %w(connection/services connection/channel connection/driver
25
- service/agentforward/services service/agentforward/driver
26
- service/process/driver util/prompter
27
- service/forward/services service/forward/driver service/forward/local-network-handler service/forward/remote-network-handler
28
- service/shell/services service/shell/driver
29
- lenient-host-key-verifier
30
- transport/compress/services transport/compress/zlib-compressor transport/compress/none-compressor transport/compress/zlib-decompressor transport/compress/none-decompressor
31
- transport/kex/services transport/kex/dh transport/kex/dh-gex
32
- transport/ossl/services
33
- transport/ossl/hmac/services transport/ossl/hmac/sha1 transport/ossl/hmac/sha1-96 transport/ossl/hmac/md5 transport/ossl/hmac/md5-96 transport/ossl/hmac/none
34
- transport/ossl/cipher-factory transport/ossl/hmac-factory transport/ossl/buffer-factory transport/ossl/key-factory transport/ossl/digest-factory
35
- transport/identity-cipher transport/packet-stream transport/version-negotiator transport/algorithm-negotiator transport/session
36
- userauth/methods/services userauth/methods/password userauth/methods/keyboard-interactive userauth/methods/publickey userauth/methods/hostbased
37
- userauth/services userauth/agent userauth/userkeys userauth/driver
38
- transport/services service/services
39
- )
40
-
41
- net_ssh_dependencies << "userauth/pageant" if File::ALT_SEPARATOR
42
- net_ssh_dependencies.each do |path|
43
- begin
44
- require "net/ssh/#{path}"
45
- rescue LoadError
46
- # Ignore load errors from this, since some files are in the list which
47
- # do not exist in different (supported) versions of Net::SSH. We know
48
- # (by this point) that Net::SSH is installed, though, since we do a
49
- # require 'net/ssh' at the very top of this file, and we know the
50
- # installed version meets the minimum version requirements because of
51
- # the version check, also at the top of this file. So, if we get a
52
- # LoadError, it's simply because the file in question does not exist in
53
- # the version of Net::SSH that is installed.
54
- #
55
- # Whew!
56
- end
57
- end
58
-
59
10
  # A helper class for dealing with SSH connections.
60
11
  class SSH
61
12
  # Patch an accessor onto an SSH connection so that we can record the server
@@ -88,13 +39,30 @@ module Capistrano
88
39
  # constructor. Values in +options+ are then merged into it, and any
89
40
  # connection information in +server+ is added last, so that +server+ info
90
41
  # takes precedence over +options+, which takes precendence over ssh_options.
91
- def self.connect(server, options={}, &block)
42
+ def self.connect(server, options={})
43
+ connection_strategy(server, options) do |host, user, connection_options|
44
+ connection = Net::SSH.start(host, user, connection_options)
45
+ Server.apply_to(connection, server)
46
+ end
47
+ end
48
+
49
+ # Abstracts the logic for establishing an SSH connection (which includes
50
+ # testing for connection failures and retrying with a password, and so forth,
51
+ # mostly made complicated because of the fact that some of these variables
52
+ # might be lazily evaluated and try to do something like prompt the user,
53
+ # which should only happen when absolutely necessary.
54
+ #
55
+ # This will yield the hostname, username, and a hash of connection options
56
+ # to the given block, which should return a new connection.
57
+ def self.connection_strategy(server, options={}, &block)
92
58
  methods = [ %w(publickey hostbased), %w(password keyboard-interactive) ]
93
59
  password_value = nil
94
-
95
- ssh_options = (options[:ssh_options] || {}).dup
96
- ssh_options[:username] = server.user || options[:user] || ssh_options[:username]
97
- ssh_options[:port] = server.port || options[:port] || ssh_options[:port] || DEFAULT_PORT
60
+
61
+ ssh_options = (server.options[:ssh_options] || {}).merge(options[:ssh_options] || {})
62
+ user = server.user || options[:user] || ssh_options[:username] || ServerDefinition.default_user
63
+ ssh_options[:port] = server.port || options[:port] || ssh_options[:port] || DEFAULT_PORT
64
+
65
+ ssh_options.delete(:username)
98
66
 
99
67
  begin
100
68
  connection_options = ssh_options.merge(
@@ -102,9 +70,7 @@ module Capistrano
102
70
  :auth_methods => ssh_options[:auth_methods] || methods.shift
103
71
  )
104
72
 
105
- connection = Net::SSH.start(server.host, connection_options, &block)
106
- Server.apply_to(connection, server)
107
-
73
+ yield server.host, user, connection_options
108
74
  rescue Net::SSH::AuthenticationFailed
109
75
  raise if methods.empty? || ssh_options[:auth_methods]
110
76
  password_value = options[:password]
@@ -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 = callback
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
@@ -11,7 +11,7 @@ module Capistrano
11
11
  end
12
12
 
13
13
  MAJOR = 2
14
- MINOR = 2
14
+ MINOR = 3
15
15
  TINY = 0
16
16
 
17
17
  STRING = [MAJOR, MINOR, TINY].join(".")
@@ -1,4 +1,4 @@
1
- require "#{File.dirname(__FILE__)}/../utils"
1
+ require "utils"
2
2
  require 'capistrano/cli/execute'
3
3
 
4
4
  class CLIExecuteTest < Test::Unit::TestCase
@@ -1,4 +1,4 @@
1
- require "#{File.dirname(__FILE__)}/../utils"
1
+ require "utils"
2
2
  require 'capistrano/cli/help'
3
3
 
4
4
  class CLIHelpTest < Test::Unit::TestCase
@@ -1,4 +1,4 @@
1
- require "#{File.dirname(__FILE__)}/../utils"
1
+ require "utils"
2
2
  require 'capistrano/cli/options'
3
3
 
4
4
  class CLIOptionsTest < Test::Unit::TestCase
data/test/cli/ui_test.rb CHANGED
@@ -1,4 +1,4 @@
1
- require "#{File.dirname(__FILE__)}/../utils"
1
+ require "utils"
2
2
  require 'capistrano/cli/ui'
3
3
 
4
4
  class CLIUITest < Test::Unit::TestCase
data/test/cli_test.rb CHANGED
@@ -1,4 +1,4 @@
1
- require "#{File.dirname(__FILE__)}/utils"
1
+ require "utils"
2
2
  require 'capistrano/cli'
3
3
 
4
4
  class CLI_Test < Test::Unit::TestCase