automate-it 0.9.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 (137) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.hgignore +10 -0
  4. data/.loadpath +5 -0
  5. data/.project +17 -0
  6. data/CHANGES.txt +314 -0
  7. data/Hoe.rake +40 -0
  8. data/Manifest.txt +164 -0
  9. data/README.txt +40 -0
  10. data/Rakefile +256 -0
  11. data/TESTING.txt +57 -0
  12. data/TODO.txt +50 -0
  13. data/TUTORIAL.txt +391 -0
  14. data/automate-it.gemspec +25 -0
  15. data/bin/ai +3 -0
  16. data/bin/aifield +75 -0
  17. data/bin/aissh +93 -0
  18. data/bin/aitag +134 -0
  19. data/bin/automateit +133 -0
  20. data/docs/friendly_errors.txt +50 -0
  21. data/docs/previews.txt +86 -0
  22. data/examples/basic/Rakefile +26 -0
  23. data/examples/basic/config/automateit_env.rb +16 -0
  24. data/examples/basic/config/fields.yml +3 -0
  25. data/examples/basic/config/tags.yml +7 -0
  26. data/examples/basic/dist/README.txt +9 -0
  27. data/examples/basic/dist/myapp_server.erb +30 -0
  28. data/examples/basic/install.log +15 -0
  29. data/examples/basic/lib/README.txt +10 -0
  30. data/examples/basic/recipes/README.txt +4 -0
  31. data/examples/basic/recipes/install.rb +61 -0
  32. data/examples/basic/recipes/uninstall.rb +6 -0
  33. data/gpl.txt +674 -0
  34. data/helpers/cpan_wrapper.pl +220 -0
  35. data/helpers/which.cmd +7 -0
  36. data/lib/automateit.rb +55 -0
  37. data/lib/automateit/account_manager.rb +114 -0
  38. data/lib/automateit/account_manager/base.rb +138 -0
  39. data/lib/automateit/account_manager/etc.rb +128 -0
  40. data/lib/automateit/account_manager/nscd.rb +33 -0
  41. data/lib/automateit/account_manager/passwd_expect.rb +40 -0
  42. data/lib/automateit/account_manager/passwd_pty.rb +69 -0
  43. data/lib/automateit/account_manager/posix.rb +138 -0
  44. data/lib/automateit/address_manager.rb +88 -0
  45. data/lib/automateit/address_manager/base.rb +171 -0
  46. data/lib/automateit/address_manager/bsd.rb +28 -0
  47. data/lib/automateit/address_manager/freebsd.rb +59 -0
  48. data/lib/automateit/address_manager/linux.rb +42 -0
  49. data/lib/automateit/address_manager/openbsd.rb +66 -0
  50. data/lib/automateit/address_manager/portable.rb +37 -0
  51. data/lib/automateit/address_manager/sunos.rb +34 -0
  52. data/lib/automateit/cli.rb +85 -0
  53. data/lib/automateit/common.rb +65 -0
  54. data/lib/automateit/constants.rb +35 -0
  55. data/lib/automateit/download_manager.rb +48 -0
  56. data/lib/automateit/edit_manager.rb +321 -0
  57. data/lib/automateit/error.rb +10 -0
  58. data/lib/automateit/field_manager.rb +103 -0
  59. data/lib/automateit/interpreter.rb +631 -0
  60. data/lib/automateit/package_manager.rb +257 -0
  61. data/lib/automateit/package_manager/apt.rb +27 -0
  62. data/lib/automateit/package_manager/cpan.rb +101 -0
  63. data/lib/automateit/package_manager/dpkg.rb +54 -0
  64. data/lib/automateit/package_manager/egg.rb +64 -0
  65. data/lib/automateit/package_manager/gem.rb +201 -0
  66. data/lib/automateit/package_manager/pear.rb +95 -0
  67. data/lib/automateit/package_manager/pecl.rb +80 -0
  68. data/lib/automateit/package_manager/portage.rb +69 -0
  69. data/lib/automateit/package_manager/yum.rb +65 -0
  70. data/lib/automateit/platform_manager.rb +49 -0
  71. data/lib/automateit/platform_manager/darwin.rb +30 -0
  72. data/lib/automateit/platform_manager/debian.rb +26 -0
  73. data/lib/automateit/platform_manager/freebsd.rb +29 -0
  74. data/lib/automateit/platform_manager/gentoo.rb +26 -0
  75. data/lib/automateit/platform_manager/lsb.rb +44 -0
  76. data/lib/automateit/platform_manager/openbsd.rb +28 -0
  77. data/lib/automateit/platform_manager/struct.rb +80 -0
  78. data/lib/automateit/platform_manager/sunos.rb +39 -0
  79. data/lib/automateit/platform_manager/uname.rb +29 -0
  80. data/lib/automateit/platform_manager/windows.rb +40 -0
  81. data/lib/automateit/plugin.rb +7 -0
  82. data/lib/automateit/plugin/base.rb +32 -0
  83. data/lib/automateit/plugin/driver.rb +256 -0
  84. data/lib/automateit/plugin/manager.rb +224 -0
  85. data/lib/automateit/project.rb +493 -0
  86. data/lib/automateit/root.rb +17 -0
  87. data/lib/automateit/service_manager.rb +93 -0
  88. data/lib/automateit/service_manager/chkconfig.rb +39 -0
  89. data/lib/automateit/service_manager/rc_update.rb +37 -0
  90. data/lib/automateit/service_manager/sysv.rb +139 -0
  91. data/lib/automateit/service_manager/update_rcd.rb +35 -0
  92. data/lib/automateit/shell_manager.rb +316 -0
  93. data/lib/automateit/shell_manager/base_link.rb +67 -0
  94. data/lib/automateit/shell_manager/link.rb +24 -0
  95. data/lib/automateit/shell_manager/portable.rb +523 -0
  96. data/lib/automateit/shell_manager/symlink.rb +32 -0
  97. data/lib/automateit/shell_manager/which_base.rb +30 -0
  98. data/lib/automateit/shell_manager/which_unix.rb +16 -0
  99. data/lib/automateit/shell_manager/which_windows.rb +20 -0
  100. data/lib/automateit/tag_manager.rb +127 -0
  101. data/lib/automateit/tag_manager/struct.rb +121 -0
  102. data/lib/automateit/tag_manager/tag_parser.rb +93 -0
  103. data/lib/automateit/tag_manager/yaml.rb +29 -0
  104. data/lib/automateit/template_manager.rb +56 -0
  105. data/lib/automateit/template_manager/base.rb +181 -0
  106. data/lib/automateit/template_manager/erb.rb +17 -0
  107. data/lib/ext/metaclass.rb +17 -0
  108. data/lib/ext/object.rb +18 -0
  109. data/lib/ext/shell_escape.rb +7 -0
  110. data/lib/hashcache.rb +22 -0
  111. data/lib/helpful_erb.rb +63 -0
  112. data/lib/inactive_support.rb +53 -0
  113. data/lib/inactive_support/basic_object.rb +6 -0
  114. data/lib/inactive_support/clean_logger.rb +127 -0
  115. data/lib/inactive_support/core_ext/array/extract_options.rb +19 -0
  116. data/lib/inactive_support/core_ext/blank.rb +50 -0
  117. data/lib/inactive_support/core_ext/class/attribute_accessors.rb +48 -0
  118. data/lib/inactive_support/core_ext/class/inheritable_attributes.rb +140 -0
  119. data/lib/inactive_support/core_ext/enumerable.rb +63 -0
  120. data/lib/inactive_support/core_ext/hash/keys.rb +54 -0
  121. data/lib/inactive_support/core_ext/module/aliasing.rb +70 -0
  122. data/lib/inactive_support/core_ext/numeric/time.rb +91 -0
  123. data/lib/inactive_support/core_ext/string/inflections.rb +153 -0
  124. data/lib/inactive_support/core_ext/symbol.rb +14 -0
  125. data/lib/inactive_support/core_ext/time/conversions.rb +96 -0
  126. data/lib/inactive_support/duration.rb +96 -0
  127. data/lib/inactive_support/inflections.rb +53 -0
  128. data/lib/inactive_support/inflector.rb +282 -0
  129. data/lib/nested_error.rb +33 -0
  130. data/lib/nitpick.rb +33 -0
  131. data/lib/queued_logger.rb +68 -0
  132. data/lib/tempster.rb +250 -0
  133. data/misc/index_gem_repository.rb +304 -0
  134. data/misc/setup_egg.rb +12 -0
  135. data/misc/setup_gem_dependencies.sh +6 -0
  136. data/misc/setup_rubygems.sh +21 -0
  137. metadata +279 -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 = peer_for(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,523 @@
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#backup
32
+ def backup(*sources)
33
+ sources, opts = args_and_opts(*sources)
34
+
35
+ targets = []
36
+ for source in sources
37
+ is_dir = File.directory?(source)
38
+
39
+ tempster_opts = {
40
+ :verbose => false,
41
+ :noop => noop?,
42
+ :delete => false,
43
+ :dir => File.dirname(source),
44
+ :prefix => "%s.%s" % [File.basename(source), Time.now.to_i],
45
+ :suffix => ".bak",
46
+ :kind => is_dir ? :directory : :file,
47
+ }
48
+
49
+ target = ::Tempster.tempster(tempster_opts)
50
+
51
+ log.silence(opts[:quiet] ? Logger::WARN : log.level) do
52
+ if is_dir
53
+ cp_opts = {}
54
+ cp_opts[:recursive] = true if is_dir
55
+ cp_opts[:preserve] = :try
56
+
57
+ source_children = _directory_contents(source)
58
+ #puts "sc: %s" % source_children.inspect
59
+
60
+ interpreter.cp_r(source_children, target, cp_opts)
61
+ else
62
+ interpreter.cp(source, target)
63
+ end
64
+ end
65
+
66
+ targets << target
67
+ end
68
+ return sources.size == 1 ? targets.first : targets
69
+ end
70
+
71
+ # See ShellManager#sh
72
+ def sh(*commands)
73
+ args, opts = args_and_opts(*commands)
74
+ log.info(PEXEC+"#{args.join(' ')}")
75
+ return writing? ? system(*args) : true
76
+ end
77
+
78
+ def _mktemp_helper(kind, name=nil, opts={}, &block)
79
+ # Tempster takes care of rethrowing exceptions
80
+ opts[:name] = name || "automateit_temp"
81
+ opts[:message_callback] = lambda{|msg| log.info(PEXEC+msg)}
82
+ opts[:noop] = interpreter.preview?
83
+ ::Tempster.send(kind, opts, &block)
84
+ end
85
+ private :_mktemp_helper
86
+
87
+ # See ShellManager#mktemp
88
+ def mktemp(name=nil, &block)
89
+ _mktemp_helper(:mktemp, name, &block)
90
+ end
91
+
92
+ # See ShellManager#mktempdir
93
+ def mktempdir(name=nil, &block)
94
+ _mktemp_helper(:mktempdir, name, &block)
95
+ end
96
+
97
+ # See ShellManager#mktempdircd
98
+ def mktempdircd(name=nil, &block)
99
+ _mktemp_helper(:mktempdircd, name, &block)
100
+ end
101
+
102
+ # See ShellManager#umask
103
+ def umask(mode=nil, &block)
104
+ if mode
105
+ old = File::umask
106
+ File::umask(mode)
107
+ if block
108
+ begin
109
+ block.call
110
+ rescue Exception => e
111
+ raise e
112
+ ensure
113
+ File::umask(old)
114
+ end
115
+ end
116
+ else
117
+ File::umask
118
+ end
119
+ end
120
+
121
+ #...[ FileUtils wrappers ]...............................................
122
+
123
+ # See ShellManager#cd
124
+ def cd(dir, opts={}, &block)
125
+ if block
126
+ log.enqueue(:info, PEXEC+(block ? "pushd" : "cd")+" "+dir)
127
+ begin
128
+ if writing? or File.directory?(dir)
129
+ FileUtils.cd(dir, &block)
130
+ else
131
+ block.call(true)
132
+ end
133
+ rescue Exception => e
134
+ raise e
135
+ ensure
136
+ log.dequeue(:info, PEXEC+"popd # => #{pwd}")
137
+ end
138
+ else
139
+ FileUtils.cd(dir) if writing?
140
+ end
141
+ return dir
142
+ end
143
+
144
+ # See ShellManager#pwd
145
+ def pwd()
146
+ return FileUtils.pwd()
147
+ end
148
+
149
+ # See ShellManager#mkdir
150
+ def mkdir(dirs, opts={}, &block)
151
+ _replace_owner_with_user(opts)
152
+ kind = opts[:parents] ? :mkdir_p : :mkdir
153
+ missing = [dirs].flatten.select{|dir| ! File.directory?(dir)}
154
+ result = false
155
+ if missing.empty? and not block
156
+ chperm(opts) if opts[:user] or opts[:group] or opts[:mode]
157
+ return result
158
+ end
159
+ unless missing.empty?
160
+ cmd = kind.to_s.gsub(/_/, ' -')
161
+ log.info(PEXEC+"#{cmd} #{missing.join(" ")}")
162
+ result = [FileUtils.send(kind, missing, _fileutils_opts)].flatten
163
+ result = result.first if [dirs].flatten.size == 1
164
+ end
165
+ if block
166
+ if missing.size > 1
167
+ raise ArgumentError.new(
168
+ "can only use a block if you mkdir a single directory")
169
+ end
170
+ dir = [dirs].flatten.first
171
+ cd(dir) do
172
+ block.call(result)
173
+ end
174
+ end
175
+ chperm(opts) if opts[:user] or opts[:group] or opts[:mode]
176
+ return missing
177
+ end
178
+
179
+ # See ShellManager#mkdir_p
180
+ def mkdir_p(dirs, opts={}, &block)
181
+ mkdir(dirs, {:parents => true}.merge(opts), &block)
182
+ end
183
+
184
+ # See ShellManager#rmdir
185
+ def rmdir(dirs)
186
+ present = [dirs].flatten.select{|dir| File.directory?(dir)}
187
+ return false if present.empty?
188
+ log.info(PEXEC+"rmdir #{String === dirs ? dirs : dirs.join(' ')}")
189
+ FileUtils.rmdir(present, _fileutils_opts)
190
+ return present
191
+ end
192
+
193
+ # See ShellManager#install
194
+ def install(source, target, mode=nil)
195
+ cp_rv = nil
196
+ chmod_rv = nil
197
+ log.silence(Logger::WARN) do
198
+ cp_rv = cp(source, target)
199
+ chmod_rv = chmod(mode, peer_for(source, target)) if mode
200
+ end
201
+
202
+ return false unless cp_rv or chmod_rv
203
+
204
+ log.info(PEXEC+"install%s %s %s" %
205
+ [mode ? ' -m 0%o' % mode : '', source, target])
206
+ return source
207
+ end
208
+
209
+ # See ShellManager#cp
210
+ def cp(sources, target, opts={})
211
+ # TODO ShellManager::Portable#cp -- rather funky, needs a code review
212
+ fu_opts = _fileutils_opts
213
+ for opt in [:noop, :verbose]
214
+ opt = opt.to_sym
215
+ fu_opts[opt] = opts[opt] if opts[opt]
216
+ end
217
+
218
+ fu_opts_with_preserve = fu_opts.clone
219
+ fu_opts_with_preserve[:preserve] = \
220
+ if opts[:preserve] == :try
221
+ fsim = File::Stat.instance_methods
222
+ (fsim.include?("uid") and fsim.include?("gid") and
223
+ fsim.include?("mode") and fsim.include?("atime"))
224
+ else
225
+ opts[:preserve]
226
+ end
227
+
228
+ changed = []
229
+ sources_a = [sources].flatten
230
+ sources_a.each do |parent|
231
+ Find.find(parent) do |child|
232
+ source_fn = File.directory?(child) ? child+"/" : child
233
+ target_dir = File.directory?(target)
234
+ target_fn = peer_for(source_fn, target)
235
+
236
+ log.debug(PNOTE+"comparing %s => %s" % [source_fn, target_fn])
237
+ source_st = File.stat(source_fn)
238
+ is_copy = false
239
+ begin
240
+ begin
241
+ target_st = File.stat(target_fn)
242
+
243
+ unless target_dir
244
+ # Is the file obviously different?
245
+ if source_st.file?
246
+ for kind in %w(size mtime)
247
+ next if kind == "mtime" and ! opts[:preserve]
248
+ unless source_st.send(kind) == target_st.send(kind)
249
+ log.debug(PNOTE+"%s not same %s" % [target_fn, kind])
250
+ raise EOFError.new
251
+ end
252
+ end
253
+
254
+ unless FileUtils.identical?(source_fn, target_fn)
255
+ log.debug(PNOTE+"%s not identical" % target_fn)
256
+ raise EOFError.new
257
+ end
258
+ end
259
+
260
+ # File just needs to be altered
261
+ if opts[:preserve]
262
+ unless source_st.mode == target_st.mode
263
+ changed << child
264
+ log.debug(PNOTE+"%s not same mode" % target_fn)
265
+ chmod(source_st.mode, target_fn, fu_opts)
266
+ end
267
+ unless source_st.uid == target_st.uid and source_st.gid == target_st.gid
268
+ changed << child
269
+ log.debug(PNOTE+"%s not same uid/gid" % target_fn)
270
+ chown(source_st.uid, source_st.gid, target_fn, fu_opts)
271
+ end
272
+ end
273
+ end
274
+ rescue EOFError
275
+ changed << child
276
+ is_copy = true
277
+ end
278
+ rescue Errno::ENOENT
279
+ changed << child
280
+ log.debug(PNOTE+"%s not present" % target_fn)
281
+ is_copy = true
282
+ end
283
+ if is_copy
284
+ log.info(PEXEC+"cp%s %s %s" % [opts[:recursive] ? ' -r' : '', source_fn, target_fn])
285
+ ## puts "fo %s" % fu_opts.inspect
286
+ ## puts "fowp %s" % fu_opts_with_preserve.inspect
287
+ FileUtils.cp_r(source_fn, target_fn, fu_opts_with_preserve)
288
+ end
289
+ end
290
+ end
291
+
292
+ result = \
293
+ if changed.empty?
294
+ false
295
+ else
296
+ if sources_a.size == 1
297
+ changed.first
298
+ else
299
+ changed.uniq
300
+ end
301
+ end
302
+ return result
303
+ end
304
+
305
+ # See ShellManager#cp_r
306
+ def cp_r(sources, target, opts={})
307
+ cp(sources, target, {:recursive => true}.merge(opts))
308
+ end
309
+
310
+ # See ShellManager#cp_R
311
+ def cp_R(*args)
312
+ cp_r(*args)
313
+ end
314
+
315
+ # See ShellManager#mv
316
+ def mv(sources, target)
317
+ present = [sources].flatten.select{|entry| self._present?(entry)}
318
+ return false if present.empty?
319
+ present = present.first if present.size == 1
320
+ FileUtils.mv(present, target, _fileutils_opts) && present
321
+ return present
322
+ end
323
+
324
+ # See ShellManager#rm
325
+ def rm(targets, opts={})
326
+ kind = \
327
+ if opts[:recursive] and opts[:force]
328
+ :rm_rf
329
+ elsif opts[:recursive]
330
+ :rm_r
331
+ else
332
+ :rm
333
+ end
334
+
335
+ present = [targets].flatten.select{|entry| self._present?(entry)}
336
+ return false if present.empty?
337
+
338
+ msg = "rm"
339
+ if opts[:recursive] and opts[:force]
340
+ msg << " -rf"
341
+ elsif opts[:recursive]
342
+ msg << " -r"
343
+ elsif opts[:force]
344
+ msg << " -f"
345
+ end
346
+ msg << " " << present.join(' ')
347
+ log.info(PEXEC+msg)
348
+
349
+ present = present.first if present.size == 0
350
+
351
+ FileUtils.send(kind, present, _fileutils_opts)
352
+ return present
353
+ end
354
+
355
+ # See ShellManager#rm_r
356
+ def rm_r(targets, opts={})
357
+ rm(targets, {:recursive => true}.merge(opts))
358
+ end
359
+
360
+ # See ShellManager#rm_rf
361
+ def rm_rf(targets, opts={})
362
+ rm(targets, {:recursive => true, :force => true}.merge(opts))
363
+ end
364
+
365
+ FILE_MASK = 0100000
366
+ DIRECTORY_MASK = 040000
367
+
368
+ # See ShellManager#chperm
369
+ def chperm(targets, opts={})
370
+ _replace_owner_with_user(opts)
371
+ user = \
372
+ if opts[:user]
373
+ opts[:user] = opts[:user].to_s if opts[:user].is_a?(Symbol)
374
+ if opts[:user].is_a?(String)
375
+ begin
376
+ Etc.getpwnam(opts[:user]).uid
377
+ rescue ArgumentError
378
+ :not_present
379
+ end
380
+ else
381
+ opts[:user]
382
+ end
383
+ end
384
+
385
+ group = \
386
+ if opts[:group]
387
+ opts[:group] = opts[:group].to_s if opts[:group].is_a?(Symbol)
388
+ if opts[:group].is_a?(String)
389
+ begin
390
+ Etc.getgrnam(opts[:group]).gid
391
+ rescue ArgumentError
392
+ :not_present
393
+ end
394
+ else
395
+ opts[:group]
396
+ end
397
+ end
398
+
399
+ modified_entries = []
400
+ modified_ownership = false
401
+ modified_permission = false
402
+ Find.find(*targets) do |path|
403
+ modified = false
404
+ stat = writing? || File.exists?(path) ? File.stat(path) : nil
405
+ if opts[:mode]
406
+ # TODO ShellManager::Portable#chperm -- process chmod symbolic strings, e.g., [ugoa...][[+-=][rwxXstugo...]...][,...]
407
+ mode = opts[:mode] | (stat.directory? ? DIRECTORY_MASK : FILE_MASK) if stat
408
+ unless stat and (mode ^ stat.mode).zero?
409
+ modified = true
410
+ modified_permission = true
411
+ File.chmod(mode, path) if writing?
412
+ end
413
+ end
414
+ if user and (not stat or user != stat.uid)
415
+ modified = true
416
+ modified_ownership = true
417
+ File.chown(user, nil, path) if writing?
418
+ end
419
+ if group and (not stat or group != stat.gid)
420
+ modified = true
421
+ modified_ownership = true
422
+ File.chown(nil, group, path) if writing?
423
+ end
424
+ modified_entries << path if modified
425
+ Find.prune if not opts[:recursive] and File.directory?(path)
426
+ end
427
+
428
+ return false if modified_entries.empty?
429
+
430
+ display_entries = opts[:details] ? modified_entries : targets
431
+ display_entries = [display_entries].flatten
432
+
433
+ if modified_permission
434
+ msg = "chmod"
435
+ msg << " -R" if opts[:recursive]
436
+ msg << " 0%o" % opts[:mode] if opts[:mode]
437
+ msg << " " << display_entries.join(' ')
438
+ log.info(PEXEC+msg)
439
+ end
440
+ if modified_ownership
441
+ msg = "chown"
442
+ msg << " -R" if opts[:recursive]
443
+ msg << " %s" % opts[:user] if opts[:user]
444
+ msg << ":" if opts[:user] and opts[:group]
445
+ msg << "%s" % opts[:group] if opts[:group]
446
+ msg << " " << display_entries.join(' ')
447
+ log.info(PEXEC+msg)
448
+ end
449
+ return targets.is_a?(String) ? modified_entries.first : modified_entries
450
+ end
451
+
452
+ # See ShellManager#chmod
453
+ def chmod(mode, targets, opts={})
454
+ chperm(targets, {:mode => mode}.merge(opts))
455
+ end
456
+
457
+ # See ShellManager#chmod_R
458
+ def chmod_R(mode, targets, opts={})
459
+ chmod(mode, targets, {:recursive => true}.merge(opts))
460
+ end
461
+
462
+ # See ShellManager#chown
463
+ def chown(user, group, targets, opts={})
464
+ chperm(targets, {:user => user, :group => group}.merge(opts))
465
+ end
466
+
467
+ # See ShellManager#chown_R
468
+ def chown_R(user, group, targets, opts={})
469
+ chown(user, group, targets, {:recursive => true}.merge(opts))
470
+ end
471
+
472
+ # See ShellManager#touch
473
+ def touch(targets, opts={})
474
+ like = opts.delete(:like)
475
+ stamp = opts.delete(:stamp)
476
+ quiet = opts.delete(:quiet) == true ? true : false
477
+ time = \
478
+ if stamp
479
+ stamp
480
+ elsif like
481
+ begin
482
+ File.stat(like).mtime
483
+ rescue Errno::ENOENT => e
484
+ if preview?
485
+ Time.now
486
+ else
487
+ raise e
488
+ end
489
+ end
490
+ else
491
+ Time.now
492
+ end
493
+
494
+ unless quiet
495
+ msg = "touch"
496
+ msg << " --reference %s" % like if like
497
+ msg << " --stamp %s" % stamp if stamp
498
+ msg << " " << [targets].flatten.join(" ")
499
+ log.info(PEXEC+msg)
500
+ end
501
+
502
+ results = []
503
+ for target in [targets].flatten
504
+ begin
505
+ stat = File.stat(target)
506
+ next if stat.mtime.to_i == time.to_i
507
+ rescue Errno::ENOENT
508
+ File.open(target, "a"){} unless preview?
509
+ end
510
+ File.utime(time, time, target) unless preview?
511
+ results << target
512
+ end
513
+
514
+ return false if results.empty?
515
+ return targets.is_a?(String) ? results.first : results
516
+ end
517
+
518
+ # Is the file or symlink at this +path+ present?
519
+ def _present?(path)
520
+ File.symlink?(path) ? true : File.exists?(path)
521
+ end
522
+ protected :_present?
523
+ end