automateit 0.70923

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 (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