capistrano 2.14.2 → 2.15.0

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 (68) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +1 -0
  3. data/CHANGELOG +49 -8
  4. data/Gemfile +1 -1
  5. data/README.md +4 -4
  6. data/capistrano.gemspec +9 -17
  7. data/lib/capistrano/callback.rb +1 -1
  8. data/lib/capistrano/cli.rb +1 -1
  9. data/lib/capistrano/cli/execute.rb +3 -3
  10. data/lib/capistrano/cli/help.rb +3 -3
  11. data/lib/capistrano/cli/help.txt +23 -23
  12. data/lib/capistrano/cli/options.rb +12 -12
  13. data/lib/capistrano/command.rb +20 -7
  14. data/lib/capistrano/configuration/actions/invocation.rb +23 -11
  15. data/lib/capistrano/configuration/connections.rb +22 -15
  16. data/lib/capistrano/configuration/execution.rb +2 -2
  17. data/lib/capistrano/configuration/loading.rb +2 -2
  18. data/lib/capistrano/configuration/log_formatters.rb +5 -1
  19. data/lib/capistrano/configuration/roles.rb +1 -1
  20. data/lib/capistrano/configuration/servers.rb +6 -6
  21. data/lib/capistrano/errors.rb +4 -4
  22. data/lib/capistrano/ext/multistage.rb +2 -2
  23. data/lib/capistrano/logger.rb +14 -1
  24. data/lib/capistrano/recipes/deploy.rb +94 -27
  25. data/lib/capistrano/recipes/deploy/assets.rb +21 -18
  26. data/lib/capistrano/recipes/deploy/dependencies.rb +2 -2
  27. data/lib/capistrano/recipes/deploy/remote_dependency.rb +11 -11
  28. data/lib/capistrano/recipes/deploy/scm.rb +1 -1
  29. data/lib/capistrano/recipes/deploy/scm/accurev.rb +6 -6
  30. data/lib/capistrano/recipes/deploy/scm/cvs.rb +4 -4
  31. data/lib/capistrano/recipes/deploy/scm/darcs.rb +3 -3
  32. data/lib/capistrano/recipes/deploy/scm/git.rb +3 -0
  33. data/lib/capistrano/recipes/deploy/scm/mercurial.rb +3 -3
  34. data/lib/capistrano/recipes/deploy/scm/none.rb +9 -5
  35. data/lib/capistrano/recipes/deploy/scm/perforce.rb +5 -5
  36. data/lib/capistrano/recipes/deploy/scm/subversion.rb +1 -1
  37. data/lib/capistrano/recipes/deploy/strategy.rb +1 -1
  38. data/lib/capistrano/recipes/deploy/strategy/base.rb +5 -1
  39. data/lib/capistrano/recipes/deploy/strategy/copy.rb +14 -2
  40. data/lib/capistrano/recipes/standard.rb +1 -1
  41. data/lib/capistrano/server_definition.rb +1 -1
  42. data/lib/capistrano/shell.rb +4 -1
  43. data/lib/capistrano/ssh.rb +1 -1
  44. data/lib/capistrano/version.rb +2 -2
  45. data/test/cli/options_test.rb +1 -1
  46. data/test/cli/ui_test.rb +1 -1
  47. data/test/command_test.rb +11 -1
  48. data/test/configuration/actions/invocation_test.rb +5 -1
  49. data/test/configuration/callbacks_test.rb +1 -1
  50. data/test/configuration/connections_test.rb +19 -0
  51. data/test/configuration/execution_test.rb +1 -1
  52. data/test/configuration/namespace_dsl_test.rb +6 -6
  53. data/test/configuration/roles_test.rb +2 -2
  54. data/test/configuration/servers_test.rb +12 -12
  55. data/test/configuration/variables_test.rb +3 -3
  56. data/test/deploy/scm/bzr_test.rb +1 -1
  57. data/test/deploy/scm/darcs_test.rb +2 -2
  58. data/test/deploy/scm/git_test.rb +10 -0
  59. data/test/deploy/scm/mercurial_test.rb +3 -3
  60. data/test/deploy/scm/perforce_test.rb +1 -1
  61. data/test/deploy/strategy/copy_test.rb +25 -1
  62. data/test/extensions_test.rb +1 -1
  63. data/test/logger_formatting_test.rb +56 -1
  64. data/test/recipes_test.rb +1 -1
  65. data/test/server_definition_test.rb +2 -2
  66. data/test/shell_test.rb +12 -6
  67. data/test/transfer_test.rb +1 -1
  68. metadata +63 -31
@@ -34,4 +34,4 @@ DESC
34
34
  task :shell do
35
35
  require 'capistrano/shell'
36
36
  Capistrano::Shell.run(self)
37
- end
37
+ end
@@ -53,4 +53,4 @@ module Capistrano
53
53
  end
54
54
  end
55
55
  end
56
- end
56
+ end
@@ -64,6 +64,9 @@ INTRO
64
64
  return false
65
65
  when /^set -(\w)\s*(\S+)/
66
66
  set_option($1, $2)
67
+ when /^set :(.*)\s+(.*)/
68
+ configuration.set($1.to_sym, $2)
69
+ puts "updated :#{$1} to #{$2}"
67
70
  when /^(?:(with|on)\s*(\S+))?\s*(\S.*)?/i
68
71
  process_command($1, $2, $3)
69
72
  else
@@ -171,7 +174,7 @@ HELP
171
174
 
172
175
  # Execute a command on the given list of servers.
173
176
  def exec_command(command, servers)
174
- command = command.gsub(/\bsudo\b/, "sudo -p '#{configuration.sudo_prompt}'")
177
+ command = command.gsub(/^(\s*)sudo\b|([|;&])\s*sudo\b/, "\\0 -p '#{configuration.sudo_prompt}'")
175
178
  processor = configuration.sudo_behavior_callback(Configuration.default_io_proc)
176
179
  sessions = servers.map { |server| configuration.sessions[server] }
177
180
  options = configuration.add_default_command_options({})
@@ -70,7 +70,7 @@ module Capistrano
70
70
  port = server.port || options[:port] || ssh_options[:port]
71
71
 
72
72
  # the .ssh/config file might have changed the host-name on us
73
- host = ssh_options.fetch(:host_name, server.host)
73
+ host = ssh_options.fetch(:host_name, server.host)
74
74
 
75
75
  ssh_options[:port] = port if port
76
76
 
@@ -1,8 +1,8 @@
1
1
  module Capistrano
2
2
  class Version
3
3
  MAJOR = 2
4
- MINOR = 14
5
- PATCH = 2
4
+ MINOR = 15
5
+ PATCH = 0
6
6
 
7
7
  def self.to_s
8
8
  "#{MAJOR}.#{MINOR}.#{PATCH}"
@@ -23,7 +23,7 @@ class CLIOptionsTest < Test::Unit::TestCase
23
23
  @cli.expects(:exit).raises(ExitException)
24
24
  assert_raises(ExitException) { @cli.parse_options! }
25
25
  end
26
-
26
+
27
27
  def test_parse_options_with_d_should_set_debug_option
28
28
  @cli.args << "-d"
29
29
  @cli.parse_options!
@@ -25,4 +25,4 @@ class CLIUITest < Test::Unit::TestCase
25
25
  MockCLI.expects(:ui).returns(ui)
26
26
  assert_equal "sayuncle", MockCLI.password_prompt("Give the passphrase: ")
27
27
  end
28
- end
28
+ end
@@ -154,6 +154,16 @@ class CommandTest < Test::Unit::TestCase
154
154
  assert_equal 5, channel[:status]
155
155
  end
156
156
 
157
+ def test_on_request_should_log_exit_signal_if_logger_present
158
+ data = mock(:read_string => "TERM")
159
+ logger = stub_everything
160
+
161
+ session = setup_for_extracting_channel_action([:on_request, "exit-signal"], data)
162
+ logger.expects(:important).with("command received signal TERM", server("capistrano"))
163
+
164
+ Capistrano::Command.new("puppet", [session], :logger => logger)
165
+ end
166
+
157
167
  def test_on_close_should_set_channel_closed
158
168
  channel = nil
159
169
  session = setup_for_extracting_channel_action(:on_close) { |ch| channel = ch }
@@ -236,7 +246,7 @@ class CommandTest < Test::Unit::TestCase
236
246
  class MockConfig
237
247
  include Capistrano::Configuration::Roles
238
248
  end
239
-
249
+
240
250
  def test_hostroles_substitution
241
251
  @config = MockConfig.new
242
252
  @config.server "capistrano", :db, :worker
@@ -7,7 +7,7 @@ class ConfigurationActionsInvocationTest < Test::Unit::TestCase
7
7
  attr_reader :options
8
8
  attr_accessor :debug
9
9
  attr_accessor :dry_run
10
- attr_accessor :preserve_roles
10
+ attr_accessor :preserve_roles
11
11
  attr_accessor :servers
12
12
 
13
13
  def initialize
@@ -27,6 +27,10 @@ class ConfigurationActionsInvocationTest < Test::Unit::TestCase
27
27
  @options.fetch(*args)
28
28
  end
29
29
 
30
+ def filter_servers(options = {})
31
+ [nil, @servers]
32
+ end
33
+
30
34
  def execute_on_servers(options = {})
31
35
  yield @servers
32
36
  end
@@ -126,7 +126,7 @@ class ConfigurationCallbacksTest < Test::Unit::TestCase
126
126
  assert_equal 1, @config.callbacks[:before].length
127
127
  assert_equal %w(primary), @config.callbacks[:before].first.except
128
128
  end
129
-
129
+
130
130
  def test_on_without_tasks_or_block_should_raise_error
131
131
  assert_raises(ArgumentError) { @config.on(:before) }
132
132
  end
@@ -5,6 +5,7 @@ class ConfigurationConnectionsTest < Test::Unit::TestCase
5
5
  class MockConfig
6
6
  attr_reader :original_initialize_called
7
7
  attr_reader :values
8
+ attr_reader :dry_run
8
9
  attr_accessor :current_task
9
10
 
10
11
  def initialize
@@ -397,6 +398,24 @@ class ConfigurationConnectionsTest < Test::Unit::TestCase
397
398
  @config.execute_on_servers {}
398
399
  end
399
400
 
401
+ def test_execute_on_servers_should_cope_with_already_disconnected_connections_when_attempting_to_close_them
402
+ cap1 = server("cap1")
403
+ cap2 = server("cap2")
404
+ connection1 = mock()
405
+ connection2 = mock()
406
+ connection3 = mock()
407
+ connection4 = mock()
408
+ connection1.expects(:close).raises(Net::SSH::Disconnect)
409
+ connection2.expects(:close)
410
+ connection3.expects(:close)
411
+ connection4.expects(:close)
412
+ @config.current_task = mock_task(:max_hosts => 1)
413
+ @config.expects(:find_servers_for_task).times(2).with(@config.current_task, {}).returns([cap1, cap2])
414
+ Capistrano::SSH.expects(:connect).times(4).returns(connection1).then.returns(connection2).then.returns(connection3).then.returns(connection4)
415
+ @config.execute_on_servers {}
416
+ @config.execute_on_servers {}
417
+ end
418
+
400
419
  def test_connect_should_honor_once_option
401
420
  assert @config.sessions.empty?
402
421
  @config.current_task = mock_task
@@ -172,4 +172,4 @@ class ConfigurationExecutionTest < Test::Unit::TestCase
172
172
  block ||= stack_inspector
173
173
  namespace.tasks[name] = Capistrano::TaskDefinition.new(name, namespace, &block)
174
174
  end
175
- end
175
+ end
@@ -146,7 +146,7 @@ class ConfigurationNamespacesDSLTest < Test::Unit::TestCase
146
146
 
147
147
  def test_parent_for_namespace_should_be_the_top_level
148
148
  @config.namespace(:outer) {}
149
- assert_equal @config, @config.namespaces[:outer].parent
149
+ assert_equal @config, @config.namespaces[:outer].parent
150
150
  end
151
151
 
152
152
  def test_fqn_for_nested_namespace_should_be_color_delimited
@@ -238,7 +238,7 @@ class ConfigurationNamespacesDSLTest < Test::Unit::TestCase
238
238
  ns = @config.namespaces[:outer]
239
239
  assert ns.respond_to?(:original_initialize_called)
240
240
  end
241
-
241
+
242
242
  def test_namespace_should_accept_respond_to_with_include_priv_parameter
243
243
  @config.namespace(:outer) {}
244
244
  ns = @config.namespaces[:outer]
@@ -308,16 +308,16 @@ class ConfigurationNamespacesDSLTest < Test::Unit::TestCase
308
308
  end
309
309
  assert_nil @config.find_task("outer::inner")
310
310
  end
311
-
311
+
312
312
  def test_kernel_method_clashing_should_not_affect_method_delegation_to_parent
313
313
  @config.class.class_eval do
314
314
  def some_weird_method() 'config' end
315
315
  end
316
-
316
+
317
317
  @config.namespace(:clash) {}
318
318
  namespace = @config.namespaces[:clash]
319
319
  assert_equal 'config', namespace.some_weird_method
320
-
320
+
321
321
  Kernel.module_eval do
322
322
  def some_weird_method() 'kernel' end
323
323
  end
@@ -325,7 +325,7 @@ class ConfigurationNamespacesDSLTest < Test::Unit::TestCase
325
325
  @config.namespace(:clash2) {}
326
326
  namespace = @config.namespaces[:clash2]
327
327
  assert_equal 'config', namespace.some_weird_method
328
-
328
+
329
329
  Kernel.send :remove_method, :some_weird_method
330
330
  @config.class.send :remove_method, :some_weird_method
331
331
  end
@@ -21,14 +21,14 @@ class ConfigurationRolesTest < Test::Unit::TestCase
21
21
  assert @config.original_initialize_called
22
22
  assert @config.roles.empty?
23
23
  end
24
-
24
+
25
25
  def test_roles_for_host_with_one_role
26
26
  @config.role :app, "app1.capistrano.test"
27
27
  @config.role :not_app, "not-app.capistrano.test"
28
28
  app_server = @config.roles[:app].servers.first
29
29
  assert @config.role_names_for_host(app_server)==[ :app ]
30
30
  end
31
-
31
+
32
32
  def test_roles_for_host_with_multiple_roles
33
33
  @config.server "www.capistrano.test", :db, :worker
34
34
  db_server = @config.roles[:db].servers.first
@@ -5,11 +5,11 @@ require 'capistrano/configuration/servers'
5
5
  class ConfigurationServersTest < Test::Unit::TestCase
6
6
  class MockConfig
7
7
  attr_reader :roles
8
- attr_accessor :preserve_roles
8
+ attr_accessor :preserve_roles
9
9
 
10
10
  def initialize
11
11
  @roles = {}
12
- @preserve_roles = false
12
+ @preserve_roles = false
13
13
  end
14
14
 
15
15
  include Capistrano::Configuration::Servers
@@ -66,31 +66,31 @@ class ConfigurationServersTest < Test::Unit::TestCase
66
66
 
67
67
  def test_task_with_roles_as_environment_variable_and_preserve_roles_should_apply_only_to_existant_task_role
68
68
  ENV['ROLES'] = "app,file"
69
- @config.preserve_roles = true
69
+ @config.preserve_roles = true
70
70
  task = new_task(:testing,@config, :roles => :app)
71
71
  assert_equal %w(app1 app2 app3).sort, @config.find_servers_for_task(task).map { |s| s.host }.sort
72
72
  ensure
73
73
  ENV.delete('ROLES')
74
- end
74
+ end
75
75
 
76
76
  def test_task_with_roles_as_environment_variable_and_preserve_roles_should_apply_only_to_existant_task_roles
77
77
  ENV['ROLES'] = "app,file,web"
78
- @config.preserve_roles = true
78
+ @config.preserve_roles = true
79
79
  task = new_task(:testing,@config, :roles => [ :app,:file ])
80
80
  assert_equal %w(app1 app2 app3 file).sort, @config.find_servers_for_task(task).map { |s| s.host }.sort
81
81
  ensure
82
82
  ENV.delete('ROLES')
83
- end
83
+ end
84
84
 
85
85
  def test_task_with_roles_as_environment_variable_and_preserve_roles_should_not_apply_if_not_exists_those_task_roles
86
86
  ENV['ROLES'] = "file,web"
87
- @config.preserve_roles = true
87
+ @config.preserve_roles = true
88
88
  task = new_task(:testing,@config, :roles => [ :app ])
89
89
  assert_equal [], @config.find_servers_for_task(task).map { |s| s.host }.sort
90
90
  ensure
91
91
  ENV.delete('ROLES')
92
- end
93
-
92
+ end
93
+
94
94
  def test_task_with_hosts_as_environment_variable_should_apply_only_to_those_hosts
95
95
  ENV['HOSTS'] = "foo,bar"
96
96
  task = new_task(:testing)
@@ -163,7 +163,7 @@ class ConfigurationServersTest < Test::Unit::TestCase
163
163
  assert_equal %w(app1 app2 app3), @config.find_servers(:roles => lambda { :app }).map { |s| s.host }.sort
164
164
  assert_equal %w(app2 file), @config.find_servers(:roles => lambda { [:report, :file] }).map { |s| s.host }.sort
165
165
  end
166
-
166
+
167
167
  def test_find_servers_with_hosts_nil_or_empty
168
168
  assert_equal [], @config.find_servers(:hosts => nil)
169
169
  assert_equal [], @config.find_servers(:hosts => [])
@@ -172,12 +172,12 @@ class ConfigurationServersTest < Test::Unit::TestCase
172
172
  result = @config.find_servers(:hosts => "app1")
173
173
  assert_equal 1, result.size
174
174
  end
175
-
175
+
176
176
  def test_find_servers_with_rolees_nil_or_empty
177
177
  assert_equal [], @config.find_servers(:roles => nil)
178
178
  assert_equal [], @config.find_servers(:roles => [])
179
179
  result = @config.find_servers(:roles => :report)
180
180
  assert_equal 1, result.size
181
181
  end
182
-
182
+
183
183
  end
@@ -53,13 +53,13 @@ class ConfigurationVariablesTest < Test::Unit::TestCase
53
53
  @config[:sample] = :value
54
54
  assert @config.respond_to?(:sample)
55
55
  end
56
-
56
+
57
57
  def test_respond_to_should_be_true_when_passed_a_string
58
58
  assert !@config.respond_to?('sample')
59
59
  @config[:sample] = :value
60
60
  assert @config.respond_to?('sample')
61
61
  end
62
-
62
+
63
63
  def test_respond_to_with_include_priv_paramter
64
64
  assert !@config.respond_to?(:sample, true)
65
65
  end
@@ -187,4 +187,4 @@ class ConfigurationVariablesTest < Test::Unit::TestCase
187
187
  assert_nil @config[:sample]
188
188
  end
189
189
  end
190
- end
190
+ end
@@ -41,7 +41,7 @@ class DeploySCMBzrTest < Test::Unit::TestCase
41
41
  'before:revid:aaaa@bbbb-1234567890',
42
42
  'last:3',
43
43
  nil, {}, [], true, false, 1.34, ]
44
-
44
+
45
45
  revision_samples.each do |revivsion_spec|
46
46
  assert_equal(revivsion_spec,
47
47
  @source.query_revision(revivsion_spec),
@@ -15,7 +15,7 @@ class DeploySCMDarcsTest < Test::Unit::TestCase
15
15
  # We should be able to pick a specific hash.
16
16
  def test_checkout_hash
17
17
  hsh = "*version_hash*"
18
- assert_match(%r{--to-match=.hash #{Regexp.quote(hsh)}},
18
+ assert_match(%r{--to-match=.hash #{Regexp.quote(hsh)}},
19
19
  @source.checkout(hsh, "*foo_location*"),
20
20
  "Specifying a revision hash got the --to-match option wrong.")
21
21
  end
@@ -31,7 +31,7 @@ class DeploySCMDarcsTest < Test::Unit::TestCase
31
31
  # Leaving the revision as nil shouldn't break anything.
32
32
  def test_checkout_nil
33
33
  assert_no_match(%r{--to-match}, @source.checkout(nil, "*foo_location*"),
34
- "Leaving the revision as nil incorrectly produced a --to-match option.")
34
+ "Leaving the revision as nil incorrectly produced a --to-match option.")
35
35
  end
36
36
  end
37
37
 
@@ -145,11 +145,21 @@ class DeploySCMGitTest < Test::Unit::TestCase
145
145
  def test_shallow_clone
146
146
  @config[:repository] = "git@somehost.com:project.git"
147
147
  @config[:git_shallow_clone] = 1
148
+ @config[:branch] = nil
148
149
  dest = "/var/www"
149
150
  rev = 'c2d9e79'
150
151
  assert_equal "git clone -q --depth 1 git@somehost.com:project.git /var/www && cd /var/www && git checkout -q -b deploy #{rev}", @source.checkout(rev, dest)
151
152
  end
152
153
 
154
+ def test_shallow_clone_with_branch
155
+ @config[:repository] = "git@somehost.com:project.git"
156
+ @config[:git_shallow_clone] = 1
157
+ @config[:branch] = 'foobar'
158
+ dest = "/var/www"
159
+ rev = 'c2d9e79'
160
+ assert_equal "git clone -q -b foobar --depth 1 git@somehost.com:project.git /var/www && cd /var/www && git checkout -q -b deploy #{rev}", @source.checkout(rev, dest)
161
+ end
162
+
153
163
  def test_remote_clone
154
164
  @config[:repository] = "git@somehost.com:project.git"
155
165
  @config[:remote] = "username"
@@ -62,19 +62,19 @@ class DeploySCMMercurialTest < Test::Unit::TestCase
62
62
  @config[:scm_command] = "/opt/local/bin/hg"
63
63
  assert_equal "/opt/local/bin/hg pull --repository /var/www && /opt/local/bin/hg update --repository /var/www --clean 8a8e00b8f11b", @source.sync('8a8e00b8f11b', dest)
64
64
  end
65
-
65
+
66
66
  def test_export
67
67
  dest = "/var/www"
68
68
  assert_raise(NotImplementedError) { @source.export('8a8e00b8f11b', dest) }
69
69
  end
70
-
70
+
71
71
  def test_sends_password_if_set
72
72
  require 'capistrano/logger'
73
73
  text = "password:"
74
74
  @config[:scm_password] = "opensesame"
75
75
  assert_equal "opensesame\n", @source.handle_data(mock_state, :test_stream, text)
76
76
  end
77
-
77
+
78
78
  def test_prompts_for_password_if_preferred
79
79
  require 'capistrano/logger'
80
80
  require 'capistrano/cli'
@@ -20,4 +20,4 @@ class DeploySCMPerforceTest < Test::Unit::TestCase
20
20
  assert_equal "@some_p4_label", @source.send(:rev_no, 'foo')
21
21
  end
22
22
 
23
- end
23
+ end
@@ -197,6 +197,27 @@ class DeployStrategyCopyTest < Test::Unit::TestCase
197
197
  @strategy.deploy!
198
198
  end
199
199
 
200
+ def test_deploy_with_copy_via_should_use_the_given_transfer_method
201
+ @config[:copy_via] = :scp
202
+ Dir.expects(:tmpdir).returns("/temp/dir")
203
+ Dir.expects(:chdir).yields
204
+ @source.expects(:checkout).returns(:local_checkout)
205
+
206
+ @strategy.expects(:system).with(:local_checkout)
207
+ @strategy.expects(:system).with("tar czf 1234567890.tar.gz 1234567890")
208
+ @strategy.expects(:upload).with("/temp/dir/1234567890.tar.gz", "/tmp/1234567890.tar.gz", {:via => :scp})
209
+ @strategy.expects(:run).with("cd /u/apps/test/releases && tar xzf /tmp/1234567890.tar.gz && rm /tmp/1234567890.tar.gz")
210
+
211
+ mock_file = mock("file")
212
+ mock_file.expects(:puts).with("154")
213
+ File.expects(:open).with("/temp/dir/1234567890/REVISION", "w").yields(mock_file)
214
+
215
+ FileUtils.expects(:rm).with("/temp/dir/1234567890.tar.gz")
216
+ FileUtils.expects(:rm_rf).with("/temp/dir/1234567890")
217
+
218
+ @strategy.deploy!
219
+ end
220
+
200
221
  def test_with_copy_cache_should_checkout_to_cache_if_cache_does_not_exist_and_then_copy
201
222
  @config[:copy_cache] = true
202
223
 
@@ -306,13 +327,16 @@ class DeployStrategyCopyTest < Test::Unit::TestCase
306
327
  private
307
328
 
308
329
  def prepare_directory_tree!(cache, exclude=false)
309
- Dir.expects(:glob).with("*", File::FNM_DOTMATCH).returns([".", "..", "app", "foo.txt"])
330
+ Dir.expects(:glob).with("*", File::FNM_DOTMATCH).returns([".", "..", "app", "app{1}", "foo.txt"])
310
331
  File.expects(:ftype).with("app").returns("directory")
332
+ File.expects(:ftype).with("app{1}").returns("directory")
311
333
  FileUtils.expects(:mkdir).with("/temp/dir/1234567890/app")
334
+ FileUtils.expects(:mkdir).with("/temp/dir/1234567890/app{1}")
312
335
  File.expects(:ftype).with("foo.txt").returns("file")
313
336
  FileUtils.expects(:ln).with("foo.txt", "/temp/dir/1234567890/foo.txt")
314
337
 
315
338
  Dir.expects(:glob).with("app/*", File::FNM_DOTMATCH).returns(["app/.", "app/..", "app/bar.txt"])
339
+ Dir.expects(:glob).with("app\\{1\\}/*", File::FNM_DOTMATCH).returns(["app{1}/.", "app{1}/.."])
316
340
 
317
341
  unless exclude
318
342
  File.expects(:ftype).with("app/bar.txt").returns("file")