capistrano 2.14.2 → 2.15.0

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