automateit 0.70923

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. data.tar.gz.sig +1 -0
  2. data/CHANGES.txt +100 -0
  3. data/Hoe.rake +35 -0
  4. data/Manifest.txt +111 -0
  5. data/README.txt +44 -0
  6. data/Rakefile +284 -0
  7. data/TESTING.txt +57 -0
  8. data/TODO.txt +26 -0
  9. data/TUTORIAL.txt +390 -0
  10. data/bin/ai +3 -0
  11. data/bin/aifield +82 -0
  12. data/bin/aitag +128 -0
  13. data/bin/automateit +117 -0
  14. data/docs/friendly_errors.txt +50 -0
  15. data/docs/previews.txt +86 -0
  16. data/env.sh +4 -0
  17. data/examples/basic/Rakefile +26 -0
  18. data/examples/basic/config/automateit_env.rb +16 -0
  19. data/examples/basic/config/fields.yml +3 -0
  20. data/examples/basic/config/tags.yml +13 -0
  21. data/examples/basic/dist/README.txt +9 -0
  22. data/examples/basic/dist/myapp_server.erb +30 -0
  23. data/examples/basic/install.log +15 -0
  24. data/examples/basic/lib/README.txt +10 -0
  25. data/examples/basic/recipes/README.txt +4 -0
  26. data/examples/basic/recipes/install.rb +53 -0
  27. data/examples/basic/recipes/uninstall.rb +6 -0
  28. data/gpl.txt +674 -0
  29. data/lib/automateit.rb +66 -0
  30. data/lib/automateit/account_manager.rb +106 -0
  31. data/lib/automateit/account_manager/linux.rb +171 -0
  32. data/lib/automateit/account_manager/passwd.rb +69 -0
  33. data/lib/automateit/account_manager/portable.rb +136 -0
  34. data/lib/automateit/address_manager.rb +165 -0
  35. data/lib/automateit/address_manager/linux.rb +80 -0
  36. data/lib/automateit/address_manager/portable.rb +37 -0
  37. data/lib/automateit/cli.rb +80 -0
  38. data/lib/automateit/common.rb +65 -0
  39. data/lib/automateit/constants.rb +33 -0
  40. data/lib/automateit/edit_manager.rb +292 -0
  41. data/lib/automateit/error.rb +10 -0
  42. data/lib/automateit/field_manager.rb +103 -0
  43. data/lib/automateit/interpreter.rb +641 -0
  44. data/lib/automateit/package_manager.rb +242 -0
  45. data/lib/automateit/package_manager/apt.rb +63 -0
  46. data/lib/automateit/package_manager/egg.rb +64 -0
  47. data/lib/automateit/package_manager/gem.rb +179 -0
  48. data/lib/automateit/package_manager/portage.rb +69 -0
  49. data/lib/automateit/package_manager/yum.rb +65 -0
  50. data/lib/automateit/platform_manager.rb +47 -0
  51. data/lib/automateit/platform_manager/darwin.rb +30 -0
  52. data/lib/automateit/platform_manager/debian.rb +26 -0
  53. data/lib/automateit/platform_manager/freebsd.rb +25 -0
  54. data/lib/automateit/platform_manager/gentoo.rb +26 -0
  55. data/lib/automateit/platform_manager/lsb.rb +40 -0
  56. data/lib/automateit/platform_manager/struct.rb +78 -0
  57. data/lib/automateit/platform_manager/uname.rb +29 -0
  58. data/lib/automateit/platform_manager/windows.rb +33 -0
  59. data/lib/automateit/plugin.rb +7 -0
  60. data/lib/automateit/plugin/base.rb +32 -0
  61. data/lib/automateit/plugin/driver.rb +218 -0
  62. data/lib/automateit/plugin/manager.rb +232 -0
  63. data/lib/automateit/project.rb +460 -0
  64. data/lib/automateit/root.rb +14 -0
  65. data/lib/automateit/service_manager.rb +79 -0
  66. data/lib/automateit/service_manager/chkconfig.rb +39 -0
  67. data/lib/automateit/service_manager/rc_update.rb +37 -0
  68. data/lib/automateit/service_manager/sysv.rb +126 -0
  69. data/lib/automateit/service_manager/update_rcd.rb +35 -0
  70. data/lib/automateit/shell_manager.rb +261 -0
  71. data/lib/automateit/shell_manager/base_link.rb +67 -0
  72. data/lib/automateit/shell_manager/link.rb +24 -0
  73. data/lib/automateit/shell_manager/portable.rb +421 -0
  74. data/lib/automateit/shell_manager/symlink.rb +32 -0
  75. data/lib/automateit/shell_manager/which.rb +25 -0
  76. data/lib/automateit/tag_manager.rb +63 -0
  77. data/lib/automateit/tag_manager/struct.rb +101 -0
  78. data/lib/automateit/tag_manager/tag_parser.rb +91 -0
  79. data/lib/automateit/tag_manager/yaml.rb +29 -0
  80. data/lib/automateit/template_manager.rb +55 -0
  81. data/lib/automateit/template_manager/base.rb +172 -0
  82. data/lib/automateit/template_manager/erb.rb +17 -0
  83. data/lib/ext/metaclass.rb +17 -0
  84. data/lib/ext/object.rb +18 -0
  85. data/lib/hashcache.rb +22 -0
  86. data/lib/helpful_erb.rb +63 -0
  87. data/lib/nested_error.rb +33 -0
  88. data/lib/queued_logger.rb +68 -0
  89. data/lib/tempster.rb +239 -0
  90. data/misc/index_gem_repository.rb +303 -0
  91. data/misc/setup_egg.rb +12 -0
  92. data/misc/setup_gem_dependencies.sh +7 -0
  93. data/misc/setup_rubygems.sh +21 -0
  94. data/misc/which.cmd +6 -0
  95. data/spec/extras/automateit_service_sysv_test +50 -0
  96. data/spec/extras/scratch.rb +15 -0
  97. data/spec/extras/simple_recipe.rb +8 -0
  98. data/spec/integration/account_manager_spec.rb +218 -0
  99. data/spec/integration/address_manager_linux_spec.rb +119 -0
  100. data/spec/integration/address_manager_portable_spec.rb +30 -0
  101. data/spec/integration/cli_spec.rb +215 -0
  102. data/spec/integration/examples_spec.rb +54 -0
  103. data/spec/integration/examples_spec_editor.rb +71 -0
  104. data/spec/integration/package_manager_spec.rb +104 -0
  105. data/spec/integration/platform_manager_spec.rb +69 -0
  106. data/spec/integration/service_manager_sysv_spec.rb +115 -0
  107. data/spec/integration/shell_manager_spec.rb +471 -0
  108. data/spec/integration/template_manager_erb_spec.rb +31 -0
  109. data/spec/spec_helper.rb +23 -0
  110. data/spec/unit/edit_manager_spec.rb +162 -0
  111. data/spec/unit/field_manager_spec.rb +79 -0
  112. data/spec/unit/hashcache_spec.rb +28 -0
  113. data/spec/unit/interpreter_spec.rb +98 -0
  114. data/spec/unit/platform_manager_spec.rb +44 -0
  115. data/spec/unit/plugins_spec.rb +253 -0
  116. data/spec/unit/tag_manager_spec.rb +189 -0
  117. data/spec/unit/template_manager_erb_spec.rb +137 -0
  118. metadata +249 -0
  119. metadata.gz.sig +0 -0
@@ -0,0 +1,67 @@
1
+ # == ShellManager::BaseLink
2
+ #
3
+ # An abstract ShellManager driver used by drivers that provide either hard or
4
+ # symbolic links.
5
+ class AutomateIt::ShellManager::BaseLink < AutomateIt::ShellManager::BaseDriver
6
+ abstract_driver
7
+
8
+ # See ShellManager#ln
9
+ def _ln(sources, target, opts={})
10
+ kind = \
11
+ if opts[:symbolic] and opts[:force]
12
+ :ln_sf
13
+ elsif opts[:symbolic]
14
+ :ln_s
15
+ else
16
+ :ln
17
+ end
18
+
19
+ missing = []
20
+ sources = [sources].flatten
21
+
22
+ if kind == :ln
23
+ raise TypeError.new("source for hard link must be a String") unless sources.size == 1
24
+ end
25
+
26
+ for source in sources
27
+ peer = File.directory?(target) ? File.join(target, File.basename(source)) : target
28
+ begin
29
+ peer_stat = File.stat(peer)
30
+ source_stat = File.stat(source)
31
+
32
+ if peer_stat.ino == source_stat.ino
33
+ next
34
+ elsif kind == :ln
35
+ missing << source
36
+ elsif Pathname.new(peer).realpath != Pathname.new(source).realpath
37
+ # It's either :ln_s or :ln_sf
38
+ missing << source
39
+ end
40
+ rescue Errno::ENOENT
41
+ # File doesn't exist, so obviously missing
42
+ missing << source
43
+ end
44
+ end
45
+ return false if missing.empty?
46
+
47
+ log.debug(PNOTE+"_ln(%s, %s, %s) # => %s" % [kind, sources.inspect, target.inspect, missing.inspect])
48
+ missing = missing.first if missing.size == 1
49
+
50
+ displayed = "ln"
51
+ if opts[:symbolic] and opts[:force]
52
+ displayed << " -sf"
53
+ else
54
+ displayed << " -s" if opts[:symbolic]
55
+ displayed << " -f" if opts[:force]
56
+ end
57
+
58
+ if kind == :ln
59
+ log.info(PEXEC+"#{displayed} #{missing} #{target}")
60
+ FileUtils.ln(missing, target, _fileutils_opts) && missing
61
+ else
62
+ log.info(PEXEC+"#{displayed} #{String === missing ? missing : missing.join(' ')} #{target}")
63
+ FileUtils.send(kind, missing, target, _fileutils_opts) && missing
64
+ end
65
+ return missing
66
+ end
67
+ end
@@ -0,0 +1,24 @@
1
+ # == ShellManager::Link
2
+ #
3
+ # A ShellManager driver providing access to the hard link +ln+ command found on
4
+ # Unix-like systems.
5
+ class AutomateIt::ShellManager::Link < AutomateIt::ShellManager::BaseLink
6
+ depends_on :callbacks => [lambda{
7
+ RUBY_PLATFORM !~ /mswin/i and File.respond_to?(:link)
8
+ }]
9
+
10
+ def suitability(method, *args) # :nodoc:
11
+ # Level must be higher than Portable
12
+ return available? ? 2 : 0
13
+ end
14
+
15
+ # See ShellManager#provides_link?
16
+ def provides_link?
17
+ available? ? true : false
18
+ end
19
+
20
+ # See ShellManager#ln
21
+ def ln(*args)
22
+ _ln(*args)
23
+ end
24
+ end
@@ -0,0 +1,421 @@
1
+ # == ShellManager::Portable
2
+ #
3
+ # Pure-Ruby, portable driver for ShellManager provides Unix-like shell
4
+ # commands for manipulating files and executing commands.
5
+ #
6
+ # It does not provide commands for:
7
+ # * #which
8
+ # * #which!
9
+ class AutomateIt::ShellManager::Portable < AutomateIt::ShellManager::BaseDriver
10
+ depends_on :nothing
11
+
12
+ def suitability(method, *args) # :nodoc:
13
+ return available? ? 1 : 0
14
+ end
15
+
16
+ def broken?
17
+ RUBY_PLATFORM =~ /mswin|java/
18
+ end
19
+ private :broken?
20
+
21
+ def provides_mode?
22
+ ! broken?
23
+ end
24
+
25
+ def provides_ownership?
26
+ ! broken?
27
+ end
28
+
29
+ #...[ Custom commands ].................................................
30
+
31
+ # See ShellManager#sh
32
+ def sh(*commands)
33
+ args, opts = args_and_opts(*commands)
34
+ log.info(PEXEC+"#{args.join(' ')}")
35
+ return writing? ? system(*args) : true
36
+ end
37
+
38
+ def _mktemp_helper(kind, name=nil, opts={}, &block)
39
+ # Tempster takes care of rethrowing exceptions
40
+ opts[:name] = name || "automateit_temp"
41
+ opts[:message_callback] = lambda{|msg| log.info(PEXEC+msg)}
42
+ opts[:noop] = interpreter.preview?
43
+ ::Tempster.send(kind, opts, &block)
44
+ end
45
+ private :_mktemp_helper
46
+
47
+ # See ShellManager#mktemp
48
+ def mktemp(name=nil, &block)
49
+ _mktemp_helper(:mktemp, name, &block)
50
+ end
51
+
52
+ # See ShellManager#mktempdir
53
+ def mktempdir(name=nil, &block)
54
+ _mktemp_helper(:mktempdir, name, &block)
55
+ end
56
+
57
+ # See ShellManager#mktempdircd
58
+ def mktempdircd(name=nil, &block)
59
+ _mktemp_helper(:mktempdircd, name, &block)
60
+ end
61
+
62
+ # See ShellManager#umask
63
+ def umask(mode=nil, &block)
64
+ if mode
65
+ old = File::umask
66
+ File::umask(mode)
67
+ if block
68
+ begin
69
+ block.call
70
+ rescue Exception => e
71
+ raise e
72
+ ensure
73
+ File::umask(old)
74
+ end
75
+ end
76
+ else
77
+ File::umask
78
+ end
79
+ end
80
+
81
+ #...[ FileUtils wrappers ]...............................................
82
+
83
+ # See ShellManager#cd
84
+ def cd(dir, opts={}, &block)
85
+ if block
86
+ log.enqueue(:info, PEXEC+(block ? "pushd" : "cd")+" "+dir)
87
+ begin
88
+ if writing? or File.directory?(dir)
89
+ FileUtils.cd(dir, &block)
90
+ else
91
+ begin
92
+ block.call(true)
93
+ rescue Exception => e
94
+ raise e
95
+ end
96
+ end
97
+ rescue Exception => e
98
+ raise e
99
+ ensure
100
+ log.dequeue(:info, PEXEC+"popd # => #{pwd}")
101
+ end
102
+ else
103
+ FileUtils.cd(dir) if writing?
104
+ end
105
+ return dir
106
+ end
107
+
108
+ # See ShellManager#pwd
109
+ def pwd()
110
+ return FileUtils.pwd()
111
+ end
112
+
113
+ # See ShellManager#mkdir
114
+ def mkdir(dirs, opts={}, &block)
115
+ _replace_owner_with_user(opts)
116
+ kind = opts[:parents] ? :mkdir_p : :mkdir
117
+ missing = [dirs].flatten.select{|dir| ! File.directory?(dir)}
118
+ result = false
119
+ if missing.empty? and not block
120
+ chperm(opts) if opts[:user] or opts[:group] or opts[:mode]
121
+ return result
122
+ end
123
+ unless missing.empty?
124
+ cmd = kind.to_s.gsub(/_/, ' -')
125
+ log.info(PEXEC+"#{cmd} #{missing.join(" ")}")
126
+ result = [FileUtils.send(kind, missing, _fileutils_opts)].flatten
127
+ end
128
+ if block
129
+ if missing.size > 1
130
+ raise ArgumentError.new(
131
+ "can only use a block if you mkdir a single directory")
132
+ end
133
+ dir = [dirs].flatten.first
134
+ cd(dir) do
135
+ block.call(result)
136
+ end
137
+ end
138
+ chperm(opts) if opts[:user] or opts[:group] or opts[:mode]
139
+ return missing
140
+ end
141
+
142
+ # See ShellManager#mkdir_p
143
+ def mkdir_p(dirs, opts={}, &block)
144
+ mkdir(dirs, {:parents => true}.merge(opts), &block)
145
+ end
146
+
147
+ # See ShellManager#rmdir
148
+ def rmdir(dirs)
149
+ present = [dirs].flatten.select{|dir| File.directory?(dir)}
150
+ return false if present.empty?
151
+ log.info(PEXEC+"rmdir #{String === dirs ? dirs : dirs.join(' ')}")
152
+ FileUtils.rmdir(present, _fileutils_opts)
153
+ return present
154
+ end
155
+
156
+ # See ShellManager#install
157
+ def install(source, target, mode=nil)
158
+ cp_rv = nil
159
+ chmod_rv = nil
160
+ log.silence(Logger::WARN) do
161
+ cp_rv = cp(source, target)
162
+ chmod_rv = chmod(mode, target) if mode
163
+ end
164
+
165
+ return false unless cp_rv or chmod_rv
166
+
167
+ log.info(PEXEC+"install%s %s %s" %
168
+ [mode ? ' -m 0%o' % mode : '', source, target])
169
+ return source
170
+ end
171
+
172
+ # See ShellManager#cp
173
+ def cp(sources, target, opts={})
174
+ # TODO ShellManager::Portable#cp -- rather funky, needs a code review
175
+ fu_opts = _fileutils_opts
176
+ for opt in [:noop, :verbose]
177
+ opt = opt.to_sym
178
+ fu_opts[opt] = opts[opt] if opts[opt]
179
+ end
180
+ #fu_opts[:verbose] = true
181
+ fu_opts_with_preserve = {:preserve => opts[:preserve]}.merge(fu_opts)
182
+
183
+ changed = []
184
+ sources_a = [sources].flatten
185
+ sources_a.each do |parent|
186
+ Find.find(parent) do |child|
187
+ source_fn = File.directory?(child) ? child+"/" : child
188
+ target_dir = File.directory?(target)
189
+ target_fn = \
190
+ if target_dir
191
+ #File.join(target, source_fn.match(/#{parent}\/?(.*)$/)[1])
192
+ File.join(target, source_fn)
193
+ else
194
+ target
195
+ end
196
+ log.debug(PNOTE+"comparing %s => %s" % [source_fn, target_fn])
197
+ source_st = File.stat(source_fn)
198
+ is_copy = false
199
+ begin
200
+ begin
201
+ target_st = File.stat(target_fn)
202
+
203
+ unless target_dir
204
+ # Is the file obviously different?
205
+ if source_st.file?
206
+ for kind in %w(size mtime)
207
+ next if kind == "mtime" and ! opts[:preserve]
208
+ unless source_st.send(kind) == target_st.send(kind)
209
+ log.debug(PNOTE+"%s not same %s" % [target_fn, kind])
210
+ raise EOFError.new
211
+ end
212
+ end
213
+
214
+ unless FileUtils.identical?(source_fn, target_fn)
215
+ log.debug(PNOTE+"%s not identical" % target_fn)
216
+ raise EOFError.new
217
+ end
218
+ end
219
+
220
+ # File just needs to be altered
221
+ if opts[:preserve]
222
+ unless source_st.mode == target_st.mode
223
+ changed << child
224
+ log.debug(PNOTE+"%s not same mode" % target_fn)
225
+ chmod(source_st.mode, target_fn, fu_opts)
226
+ end
227
+ unless source_st.uid == target_st.uid and source_st.gid == target_st.gid
228
+ changed << child
229
+ log.debug(PNOTE+"%s not same uid/gid" % target_fn)
230
+ chown(source_st.uid, source_st.gid, target_fn, fu_opts)
231
+ end
232
+ end
233
+ end
234
+ rescue EOFError
235
+ changed << child
236
+ is_copy = true
237
+ end
238
+ rescue Errno::ENOENT
239
+ changed << child
240
+ log.debug(PNOTE+"%s not present" % target_fn)
241
+ is_copy = true
242
+ end
243
+ if is_copy
244
+ log.info(PEXEC+"cp%s %s %s" % [opts[:recursive] ? ' -r' : '', source_fn, target_fn])
245
+ ## puts "fo %s" % fu_opts.inspect
246
+ ## puts "fowp %s" % fu_opts_with_preserve.inspect
247
+ FileUtils.cp_r(source_fn, target_fn, fu_opts_with_preserve)
248
+ end
249
+ end
250
+ end
251
+
252
+ result = \
253
+ if changed.empty?
254
+ false
255
+ else
256
+ if sources_a.size == 1
257
+ changed.first
258
+ else
259
+ changed.uniq
260
+ end
261
+ end
262
+ return result
263
+ end
264
+
265
+ # See ShellManager#cp_r
266
+ def cp_r(sources, target, opts={})
267
+ cp(sources, target, {:recursive => true}.merge(opts))
268
+ end
269
+
270
+ # See ShellManager#cp_R
271
+ def cp_R(*args)
272
+ cp_r(*args)
273
+ end
274
+
275
+ # See ShellManager#mv
276
+ def mv(sources, target)
277
+ present = [sources].flatten.select{|entry| File.exists?(entry)}
278
+ return false if present.empty?
279
+ present = present.first if present.size == 1
280
+ FileUtils.mv(present, target, _fileutils_opts) && present
281
+ return present
282
+ end
283
+
284
+ # See ShellManager#rm
285
+ def rm(targets, opts={})
286
+ kind = \
287
+ if opts[:recursive] and opts[:force]
288
+ :rm_rf
289
+ elsif opts[:recursive]
290
+ :rm_r
291
+ else
292
+ :rm
293
+ end
294
+ present = [targets].flatten.select{|entry| File.exists?(entry)}
295
+ return false if present.empty?
296
+ present = present.first if present.size == 0
297
+ log.info(PEXEC+"#{kind} #{String === present ? present : present.join(' ')}")
298
+ FileUtils.send(kind, present, _fileutils_opts)
299
+ return present
300
+ end
301
+
302
+ # See ShellManager#rm_r
303
+ def rm_r(targets, opts={})
304
+ rm(targets, {:recursive => true}.merge(opts))
305
+ end
306
+
307
+ # See ShellManager#rm_rf
308
+ def rm_rf(targets, opts={})
309
+ rm(targets, {:recursive => true, :force => true}.merge(opts))
310
+ end
311
+
312
+ FILE_MASK = 0100000
313
+ DIRECTORY_MASK = 040000
314
+
315
+ # See ShellManager#chperm
316
+ def chperm(targets, opts={})
317
+ _replace_owner_with_user(opts)
318
+ user = \
319
+ if opts[:user]
320
+ opts[:user] = opts[:user].to_s if opts[:user].is_a?(Symbol)
321
+ if opts[:user].is_a?(String)
322
+ begin
323
+ Etc.getpwnam(opts[:user]).uid
324
+ rescue ArgumentError
325
+ :not_present
326
+ end
327
+ else
328
+ opts[:user]
329
+ end
330
+ end
331
+
332
+ group = \
333
+ if opts[:group]
334
+ opts[:group] = opts[:group].to_s if opts[:group].is_a?(Symbol)
335
+ if opts[:group].is_a?(String)
336
+ begin
337
+ Etc.getgrnam(opts[:group]).gid
338
+ rescue ArgumentError
339
+ :not_present
340
+ end
341
+ else
342
+ opts[:group]
343
+ end
344
+ end
345
+
346
+ modified_entries = []
347
+ Find.find(*targets) do |path|
348
+ modified = false
349
+ stat = writing? || File.exists?(path) ? File.stat(path) : nil
350
+ if opts[:mode]
351
+ # TODO ShellManager::Portable#chperm -- process chmod symbolic strings, e.g., [ugoa...][[+-=][rwxXstugo...]...][,...]
352
+ mode = opts[:mode] | (stat.directory? ? DIRECTORY_MASK : FILE_MASK) if stat
353
+ unless stat and (mode ^ stat.mode).zero?
354
+ modified = true
355
+ File.chmod(mode, path) if writing?
356
+ end
357
+ end
358
+ if user and (not stat or user != stat.uid)
359
+ modified = true
360
+ File.chown(user, nil, path) if writing?
361
+ end
362
+ if group and (not stat or group != stat.gid)
363
+ modified = true
364
+ File.chown(nil, group, path) if writing?
365
+ end
366
+ modified_entries << path if modified
367
+ Find.prune if not opts[:recursive] and File.directory?(path)
368
+ end
369
+
370
+ return false if modified_entries.empty?
371
+
372
+ display_entries = opts[:details] ? modified_entries : targets
373
+ display_entries = [display_entries].flatten
374
+
375
+ if opts[:mode]
376
+ msg = "chmod"
377
+ msg << " -R" if opts[:recursive]
378
+ msg << " 0%o" % opts[:mode] if opts[:mode]
379
+ msg << " " << display_entries.join(' ')
380
+ log.info(PEXEC+msg)
381
+ end
382
+ if opts[:user] or opts[:group]
383
+ msg = "chown"
384
+ msg << " -R" if opts[:recursive]
385
+ msg << " %s" % opts[:user] if opts[:user]
386
+ msg << ":" if opts[:user] and opts[:group]
387
+ msg << " " unless opts[:user] and opts[:group]
388
+ msg << "%s" % opts[:group] if opts[:group]
389
+ msg << " " << display_entries.join(' ')
390
+ log.info(PEXEC+msg)
391
+ end
392
+ return targets.is_a?(String) ? modified_entries.first : modified_entries
393
+ end
394
+
395
+ # See ShellManager#chmod
396
+ def chmod(mode, targets, opts={})
397
+ chperm(targets, {:mode => mode}.merge(opts))
398
+ end
399
+
400
+ # See ShellManager#chmod_R
401
+ def chmod_R(mode, targets, opts={})
402
+ chmod(mode, targets, {:recursive => true}.merge(opts))
403
+ end
404
+
405
+ # See ShellManager#chown
406
+ def chown(user, group, targets, opts={})
407
+ chperm(targets, {:user => user, :group => group}.merge(opts))
408
+ end
409
+
410
+ # See ShellManager#chown_R
411
+ def chown_R(user, group, targets, opts={})
412
+ chown(user, group, targets, {:recursive => true}.merge(opts))
413
+ end
414
+
415
+ # See ShellManager#touch
416
+ def touch(targets)
417
+ log.info(PEXEC+"touch #{String === targets ? targets : targets.join(' ')}")
418
+ FileUtils.touch(targets, _fileutils_opts)
419
+ return targets
420
+ end
421
+ end