reap 6.0.2 → 9.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (174) hide show
  1. data/CHANGES +6 -0
  2. data/COPYING +666 -395
  3. data/MANIFEST +139 -0
  4. data/NOTES +12 -0
  5. data/README +19 -163
  6. data/bin/reap-announce +7 -0
  7. data/bin/reap-check-load +8 -0
  8. data/bin/reap-check-syntax +7 -0
  9. data/bin/reap-clean +7 -0
  10. data/bin/reap-clobber +7 -0
  11. data/bin/reap-doc +7 -0
  12. data/bin/reap-doc-rdoc +7 -0
  13. data/bin/reap-doc-ri +7 -0
  14. data/bin/reap-init +69 -0
  15. data/bin/reap-inspect +7 -0
  16. data/bin/reap-install +8 -0
  17. data/bin/reap-install-gem +8 -0
  18. data/bin/reap-log +7 -0
  19. data/bin/reap-log-changes +8 -0
  20. data/bin/reap-log-notes +8 -0
  21. data/bin/reap-make +7 -0
  22. data/bin/reap-make-clean +7 -0
  23. data/bin/reap-make-distclean +7 -0
  24. data/bin/reap-make-extconf +7 -0
  25. data/bin/reap-make-static +8 -0
  26. data/bin/reap-package +8 -0
  27. data/bin/reap-package-gem +7 -0
  28. data/bin/reap-package-tgz +7 -0
  29. data/bin/reap-package-zip +7 -0
  30. data/bin/reap-prepare +7 -0
  31. data/bin/reap-publish +7 -0
  32. data/bin/reap-release +7 -0
  33. data/bin/reap-scaffold +8 -0
  34. data/bin/reap-scm-branch +7 -0
  35. data/bin/reap-scm-tag +7 -0
  36. data/bin/reap-spec +7 -0
  37. data/bin/reap-spec-doc +8 -0
  38. data/bin/reap-stamp +7 -0
  39. data/bin/reap-stats +7 -0
  40. data/bin/reap-test +7 -0
  41. data/bin/reap-test-cross +7 -0
  42. data/bin/reap-test-load +9 -0
  43. data/bin/reap-test-solo +7 -0
  44. data/bin/reap-uninstall +7 -0
  45. data/bin/reap-uninstall-gem +7 -0
  46. data/data/reap/base/CHANGES +19 -0
  47. data/data/reap/base/COPYING +674 -0
  48. data/data/reap/base/NOTES +12 -0
  49. data/data/reap/base/README +8 -0
  50. data/data/reap/build/rake/Rakefile +130 -0
  51. data/{forge/reference/installers → data/reap/build/rake}/setup.rb +155 -97
  52. data/data/reap/build/rake-lite/install.rb +79 -0
  53. data/data/reap/build/tasks/task/rdoc +211 -0
  54. data/data/reap/{setup.rb → build/tasks/task/setup} +248 -200
  55. data/data/reap/build/tasks/task/test +38 -0
  56. data/data/reap/init/meta/project.yaml +21 -0
  57. data/demo/README +15 -0
  58. data/demo/lib/foo/foo.rb +7 -0
  59. data/demo/meta/VERSION +1 -0
  60. data/demo/meta/project.yaml +21 -0
  61. data/lib/reap/application.rb +80 -0
  62. data/lib/reap/default.yaml +77 -0
  63. data/lib/reap/extensions/array.rb +50 -0
  64. data/lib/reap/extensions/hash.rb +63 -0
  65. data/lib/reap/extensions/string.rb +31 -0
  66. data/lib/reap/extensions.rb +6 -0
  67. data/lib/reap/iobject.rb +264 -0
  68. data/lib/reap/manager/announce.rb +193 -0
  69. data/lib/reap/manager/check.rb +109 -0
  70. data/lib/reap/manager/clean.rb +58 -0
  71. data/lib/reap/manager/gem.rb +200 -0
  72. data/lib/reap/manager/html.rb +69 -0
  73. data/lib/reap/manager/log.rb +214 -0
  74. data/lib/reap/manager/make.rb +109 -0
  75. data/lib/reap/manager/pack.rb +232 -0
  76. data/lib/reap/manager/publish.rb +72 -0
  77. data/lib/reap/manager/rdoc.rb +194 -0
  78. data/lib/reap/manager/release.rb +78 -0
  79. data/lib/reap/manager/rubyforge.rb +44 -0
  80. data/lib/reap/manager/scaffold.rb +138 -0
  81. data/lib/reap/manager/scm.rb +66 -0
  82. data/lib/reap/manager/site.rb +27 -0
  83. data/lib/reap/manager/spec.rb +94 -0
  84. data/lib/reap/manager/stats.rb +145 -0
  85. data/lib/reap/manager/svn.rb +76 -0
  86. data/lib/reap/manager/test.rb +389 -0
  87. data/lib/reap/manager.rb +74 -0
  88. data/lib/reap/metadata.rb +603 -0
  89. data/lib/reap/project.rb +165 -0
  90. data/lib/reap/settings.rb +67 -0
  91. data/lib/reap/systems/rubyforge.rb +737 -0
  92. data/lib/reap/systems/subversion.rb +333 -0
  93. data/lib/reap/utilities/fileutils.rb +304 -0
  94. data/lib/reap/utilities/netutils.rb +221 -0
  95. data/lib/reap/utilities/setuputils.rb +124 -0
  96. data/lib/reap/utilities/shellutils.rb +175 -0
  97. data/log/Changelog.txt +107 -0
  98. data/log/FIXME.txt +25 -0
  99. data/log/TODO.txt +72 -0
  100. data/meta/VERSION +1 -0
  101. data/meta/description +4 -0
  102. data/meta/project.yaml +20 -0
  103. data/task/man +14 -0
  104. data/task/rdoc +20 -0
  105. data/{data/reap/scaffold/standard/setup.rb → task/setup} +248 -200
  106. metadata +225 -153
  107. data/ProjectInfo +0 -105
  108. data/bin/reap +0 -5
  109. data/bin/rubytest +0 -5
  110. data/data/reap/install.rb +0 -62
  111. data/data/reap/scaffold/standard/COPYING +0 -403
  112. data/data/reap/scaffold/standard/ChangeLog +0 -0
  113. data/data/reap/scaffold/standard/INSTALL +0 -14
  114. data/data/reap/scaffold/standard/ProjectInfo +0 -77
  115. data/data/reap/scaffold/standard/README +0 -3
  116. data/data/reap/scaffold/standard/Rakefile +0 -10
  117. data/data/reap/scaffold/standard/TODO +0 -0
  118. data/data/reap/scaffold/subversion/trunk/COPYING +0 -403
  119. data/data/reap/scaffold/subversion/trunk/ChangeLog +0 -0
  120. data/data/reap/scaffold/subversion/trunk/INSTALL +0 -14
  121. data/data/reap/scaffold/subversion/trunk/ProjectInfo +0 -76
  122. data/data/reap/scaffold/subversion/trunk/README +0 -3
  123. data/data/reap/scaffold/subversion/trunk/Rakefile +0 -10
  124. data/data/reap/scaffold/subversion/trunk/TODO +0 -0
  125. data/data/reap/scaffold/subversion/trunk/setup.rb +0 -1568
  126. data/forge/ProjectInfo +0 -38
  127. data/forge/ProjectInfo.rb +0 -76
  128. data/forge/TODO +0 -10
  129. data/forge/installer.rb +0 -250
  130. data/forge/reference/Rakefile +0 -124
  131. data/forge/reference/Rakefile.htm +0 -69
  132. data/forge/reference/aRakefile +0 -60
  133. data/forge/reference/compositepublisher.rb +0 -24
  134. data/forge/reference/ftptools.rb +0 -139
  135. data/forge/reference/installers/package.rb +0 -629
  136. data/forge/reference/license-each.rb +0 -85
  137. data/forge/reference/publisher.rb +0 -75
  138. data/forge/reference/rubyforge.rb +0 -247
  139. data/forge/reference/rubyforgepublisher.rb +0 -18
  140. data/forge/reference/sshpublisher.rb +0 -47
  141. data/forge/reference/suby-cvs.rb +0 -46
  142. data/forge/scaffold.rb +0 -126
  143. data/forge/unit_runner/README +0 -6
  144. data/forge/unit_runner/commentrunner.rb +0 -62
  145. data/forge/unit_runner/cunit.rb +0 -17
  146. data/forge/unit_runner/forkedrunner.rb +0 -91
  147. data/forge/unit_runner/sample.rb +0 -16
  148. data/lib/reap/bin/reap.rb +0 -230
  149. data/lib/reap/bin/rubytest.rb +0 -53
  150. data/lib/reap/class/announce.rb +0 -220
  151. data/lib/reap/class/doap.rb +0 -80
  152. data/lib/reap/class/extest.rb +0 -146
  153. data/lib/reap/class/filer.rb +0 -62
  154. data/lib/reap/class/manifest.rb +0 -68
  155. data/lib/reap/class/package.rb +0 -576
  156. data/lib/reap/class/publish.rb +0 -152
  157. data/lib/reap/class/rdoc.rb +0 -123
  158. data/lib/reap/class/test.rb +0 -264
  159. data/lib/reap/projectinfo.rb +0 -208
  160. data/lib/reap/rake.rb +0 -42
  161. data/lib/reap/reap.rb +0 -89
  162. data/lib/reap/tasks.rb +0 -756
  163. data/lib/reap/taskutils.rb +0 -122
  164. data/note/LATEST +0 -44
  165. data/note/doap.xml +0 -28
  166. data/note/history/Rakefile-0.1 +0 -308
  167. data/sample/ProjectInfo +0 -96
  168. data/sample/Rakefile +0 -9
  169. data/sample/Reapfile +0 -11
  170. data/sample/task/demo.rb +0 -15
  171. data/setup.rb +0 -1568
  172. data/web/ProjectInfo.html +0 -75
  173. data/web/images/grape.jpg +0 -0
  174. data/web/index.html +0 -312
@@ -0,0 +1,333 @@
1
+ require 'reap/utilities/shellutils'
2
+ require 'reap/utilities/fileutils'
3
+
4
+ module Reap
5
+
6
+ class Subversion
7
+ include Utilities::ShellUtils
8
+ include Utilities::FileUtils
9
+
10
+ # Project name (for repository).
11
+
12
+ attr_accessor :project
13
+
14
+ # Current version of project.
15
+
16
+ attr_accessor :version
17
+
18
+ # Developers URL to repository. Defaults to Rubyforge address.
19
+
20
+ attr_accessor :repository
21
+
22
+ # Username. Defaults to ENV['RUBYFORGE_USERNAME'].
23
+
24
+ attr_accessor :username
25
+
26
+ # The URL protocol to use. Defaults to "svn+ssh".
27
+
28
+ attr_accessor :protocol
29
+
30
+ # Prefix to use on tag folder. Default is no prefix.
31
+
32
+ attr_accessor :prefix
33
+
34
+ # Optional commit message. This is intended for commandline
35
+ # usage. (Use -m for shorthand).
36
+
37
+ attr_accessor :message
38
+
39
+ # Directory to store tags. Defaults to tags/.
40
+
41
+ attr_accessor :tagpath
42
+
43
+ # Directory to store branches. Defaults to branches/.
44
+
45
+ attr_accessor :branchpath
46
+
47
+ # Operate in dryrun mode.
48
+
49
+ attr_accessor :dryrun
50
+
51
+ # New Subversion object.
52
+ #
53
+ # TODO: Perhaps format prefix, like:
54
+ # prefix = prefix + '_' if prefix && prefix !~ /[_-]$/
55
+
56
+ def initialize(options={})
57
+ options.each do |k,v|
58
+ send("#{k}=", v) if respond_to?("#{k}=")
59
+ end
60
+
61
+ # defaults are for rubyforge
62
+ @repository ||= "rubyforge.org/var/svn/#{project}"
63
+ @username ||= ENV['RUBYFORGE_USERNAME']
64
+ @protocol ||= "svn+ssh"
65
+ @tagpath ||= "tags"
66
+ @branchpath ||= "branches"
67
+
68
+ if i = @repository.index('//')
69
+ @repository = @repository[i+2..-1]
70
+ end
71
+ end
72
+
73
+ # Developer domain is "username@repository".
74
+
75
+ def developer_domain
76
+ "#{username}@#{repository}"
77
+ end
78
+
79
+ # Branch current version.
80
+ #
81
+ # message Optional commit message. This is intended for commandline
82
+ # usage. (Use -m for shorthand).
83
+ #
84
+ def branch(options={})
85
+ msg = options['message'] || options['m']
86
+ name = "#{prefix}#{version}"
87
+ path = branchpath.to_s
88
+
89
+ if path == '.' or path.empty?
90
+ url = "#{protocol}://" + File.join(developer_domain, name)
91
+ else
92
+ url = "#{protocol}://" + File.join(developer_domain, path, name)
93
+ end
94
+
95
+ case ask("Branch: #{url} ? [yN]").strip.downcase
96
+ when 'y', 'yes'
97
+ #sh "svn copy #{protocol}://#{username}@#{repository}/trunk #{url}"
98
+ sh "svn copy . #{url}"
99
+ end
100
+ end
101
+
102
+ # Tag current version.
103
+ #
104
+ # message Optional commit message. This is intended for commandline
105
+ # usage. (Use -m for shorthand).
106
+ #
107
+ def tag(options={})
108
+ msg = options['message'] || options['m']
109
+ name = "#{prefix}#{version}"
110
+ path = tagpath.to_s
111
+
112
+ if path == '.' or path.empty?
113
+ url = "#{protocol}://" + File.join(developer_domain, name)
114
+ else
115
+ url = "#{protocol}://" + File.join(developer_domain, path, name)
116
+ end
117
+
118
+ case ask("Tag: #{url} ? [yN]").strip.downcase
119
+ when 'y', 'yes'
120
+ #sh "svn copy #{protocol}://#{username}@#{repository}/trunk #{url}"
121
+ sh "svn copy . #{url}"
122
+ end
123
+ end
124
+
125
+ # Create a change log.
126
+ #
127
+ # change File name to store rdoc formated changelog. Default is 'changelog.txt'.
128
+ #
129
+ # output Path to store rdoc formated changelog. Default is 'log'.
130
+ #
131
+ # xmlchange File name to store XML formated changelog. Default is 'changelog.xml'.
132
+ # This also creates a file, if needed, by the same name but with .xsl extension.
133
+ #
134
+ # xmloutput Path to store XML-formated changelog. Default is 'doc/log'.
135
+ #
136
+ # If change or xmlchange contain a path separator ('/', not '\'), then it is assumed they
137
+ # provide their own path and the output fields will not be used. This allows you to set
138
+ # +change+ to "./CHANGES" for instance, without effecting the location of other logs.
139
+ # You can also set change or xmlchange to 'false' to supress creation altogether.
140
+ #
141
+ # TODO: Allow for a way to dump the text-based Changelog to standard out. "$stdout" as the filename?
142
+ # TODO: How to apply naming policy from here?
143
+
144
+ def log(file=nil)
145
+ #txtlog = file #options['change'] || 'changelog.txt'
146
+ #txtdir = options['output'] || 'log'
147
+ #xmllog = options['xmlchange'] || 'changelog.xml'
148
+ #xmldir = options['xmloutput'] || 'doc/log'
149
+ #txtlog = File.join(txtdir, txtlog) unless xmllog.include?('/')
150
+ #xmllog = File.join(xmldir, xmllog) unless xmllog.include?('/')
151
+ text = changelog_text
152
+ if dryrun?
153
+ puts "svn log > #{file}"
154
+ elsif file
155
+ mkdir_p(File.dirname(file))
156
+ File.open(file, 'w'){|f| f << text }
157
+ puts "Updated #{file}"
158
+ else
159
+ puts text
160
+ end
161
+ end
162
+
163
+ #
164
+
165
+ def log_xml(file)
166
+ #xmldir = options['xmloutput'] || 'doc/log'
167
+
168
+ xslfile = file.chomp(File.extname(file)) + '.xsl'
169
+ log_xsl(xslfile)
170
+
171
+ text = changelog_xml
172
+ i = text.index("?>\n")
173
+ text.insert(i+2, "\n" + %[<?xml-stylesheet href="#{xslfile}" type="text/xsl" ?>])
174
+
175
+ if dryrun?
176
+ puts "svn log --xml > #{file}"
177
+ elsif file
178
+ mkdir_p(File.dirname(file))
179
+ File.open(file, 'w'){ |f| f << text }
180
+ puts "Updated #{file}"
181
+ else
182
+ puts text
183
+ end
184
+ end
185
+
186
+ private
187
+
188
+ def changelog_xml
189
+ @changelog_xml ||= `svn log --xml`
190
+ end
191
+
192
+ def changelog_text
193
+ @changelog_text ||= log_format(log_organize(log_parse(changelog_xml)))
194
+ end
195
+
196
+ # Parse xml log into rdoc markup.
197
+
198
+ def log_parse(xml)
199
+ require "rexml/document"
200
+ changes = []
201
+ doc = REXML::Document.new(xml)
202
+ doc.root.elements.each do |element|
203
+ revision = element.attributes["revision"]
204
+ author = element.elements["author"].text
205
+ date = element.elements["date"].text
206
+ message = element.elements["msg"].text
207
+
208
+ revision = revision.strip
209
+ author = author.strip
210
+ date = Date.parse(date)
211
+ message = message.strip
212
+
213
+ changes << [ date, author, revision, message ]
214
+ end
215
+ changes
216
+ end
217
+
218
+ #
219
+
220
+ def log_organize(changes)
221
+ mapped = {}
222
+ changes.each do |date, who, rev, text|
223
+ mapped[[date, who]] ||= []
224
+ mapped[[date, who]] << [text, rev]
225
+ end
226
+ sorted = []
227
+ mapped.each do |(date, who), entries|
228
+ sorted << [ date, who, entries ]
229
+ end
230
+ sorted.sort{ |a,b| b[0] <=> a[0] }
231
+ end
232
+
233
+ #
234
+
235
+ def log_format(sorted)
236
+ string = "= Subversion Changelog\n\n"
237
+ sorted.each do |date, who, entries|
238
+ string << "== #{date} #{who}\n\n" # no email :(
239
+ entries.each do |entry|
240
+ string << "* #{entry.first} (#{entry.last})\n"
241
+ end
242
+ string << "\n"
243
+ end
244
+ string
245
+ end
246
+
247
+ #
248
+
249
+ def log_xsl(xslfile)
250
+ if force? or not File.exist?(xslfile)
251
+ mkdir_p(File.dirname(xslfile))
252
+ if dryrun?
253
+ puts "touch #{xslfile}"
254
+ else
255
+ File.open(xslfile, 'w') do |f|
256
+ f << DEFAULT_LOG_XSL
257
+ end
258
+ end
259
+ end
260
+ end
261
+
262
+ DEFAULT_LOG_XSL = <<-END
263
+ <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
264
+
265
+ <xsl:output cdata-section-elements="script"/>
266
+
267
+ <xsl:template match="/">
268
+ <html>
269
+ <head>
270
+ <title>Changelog</title>
271
+ <link REL='SHORTCUT ICON' HREF="../img/ruby-sm.png" />
272
+ <style>
273
+ td { font-family: sans-serif; padding: 0px 10px; }
274
+ </style>
275
+ </head>
276
+ <body>
277
+ <div class="container">
278
+ <h1>Changelog</h1>
279
+ <table style="width: 100%;">
280
+ <xsl:apply-templates />
281
+ </table>
282
+ </div>
283
+ </body>
284
+ </html>
285
+ </xsl:template>
286
+
287
+ <xsl:template match="logentry">
288
+ <tr>
289
+ <td><b><pre><xsl:value-of select="msg"/></pre></b></td>
290
+ <td><xsl:value-of select="author"/></td>
291
+ <td><xsl:value-of select="date"/></td>
292
+ </tr>
293
+ </xsl:template>
294
+
295
+ </xsl:stylesheet>
296
+ END
297
+
298
+ end
299
+
300
+ end
301
+
302
+
303
+
304
+ =begin
305
+ #
306
+
307
+ def svn_repository_configuration(options, *entries)
308
+ entries << 'svn'
309
+ options = configure_options(options, *entries)
310
+
311
+ options['repository'] ||= metadata.repository
312
+ options['protocol'] ||= 'svn+ssh'
313
+ options['message'] ||= options.delete('m')
314
+ options['tagpath'] ||= 'tags'
315
+
316
+ unless repository
317
+ rubyforge = configuration['rubyforge'] || {}
318
+ projectname = rubyforge['project'] || metadata.name
319
+ options['repository'] = "rubyforge.org/var/svn/#{projectname}"
320
+ end
321
+
322
+ if i = options['repository'].index('//')
323
+ options['repository'] = options['repository'][i+2..-1]
324
+ end
325
+
326
+ if /rubyforge.org/i =~ options['repository']
327
+ options['username'] ||= ENV['RUBYFORGE_USERNAME']
328
+ end
329
+
330
+ return options
331
+ end
332
+ =end
333
+
@@ -0,0 +1,304 @@
1
+ # = TITLE:
2
+ #
3
+ # File Utitlies
4
+ #
5
+ # = COPYING:
6
+ #
7
+ # Copyright (c) 2007,2008 Tiger Ops
8
+ #
9
+ # This file is part of the Reap program.
10
+ #
11
+ # Reap is free software: you can redistribute it and/or modify
12
+ # it under the terms of the GNU General Public License as published by
13
+ # the Free Software Foundation, either version 3 of the License, or
14
+ # (at your option) any later version.
15
+ #
16
+ # Reap is distributed in the hope that it will be useful,
17
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ # GNU General Public License for more details.
20
+ #
21
+ # You should have received a copy of the GNU General Public License
22
+ # along with Reap. If not, see <http://www.gnu.org/licenses/>.
23
+ #
24
+ # TODO:
25
+ #
26
+ # - Move out_of_data? and safe? to Facets FileTest extensions.
27
+
28
+ require 'fileutils'
29
+ require 'facets/ziputils'
30
+
31
+ require 'reap/extensions'
32
+
33
+ #
34
+ module Reap
35
+ module Utilities
36
+
37
+ module FileUtils
38
+
39
+ # Delegate access to FileUtils.
40
+
41
+ def fileutils
42
+ dryrun? ? ::FileUtils::DryRun : ::FileUtils
43
+ end
44
+
45
+ # Add FileUtils Features
46
+
47
+ ::FileUtils.private_instance_methods(false).each do |meth|
48
+ next if meth =~ /^fu_/
49
+ module_eval %{
50
+ def #{meth}(*a,&b)
51
+ fileutils.#{meth}(*a,&b)
52
+ end
53
+ }
54
+ end
55
+
56
+ # Add FileTest Features
57
+
58
+ ::FileTest.private_instance_methods(false).each do |meth|
59
+ next if meth =~ /^fu_/
60
+ module_eval %{
61
+ def #{meth}(*a,&b)
62
+ FileTest.#{meth}(*a,&b)
63
+ end
64
+ }
65
+ end
66
+
67
+ # Specific.
68
+
69
+ def rm_r(*a)
70
+ if dryrun?
71
+ puts "rm_r #{a.join(' ')}"
72
+ else
73
+ fileutils.rm_r(*a)
74
+ end
75
+ end
76
+
77
+ # Bonus FileUtils features.
78
+
79
+ def cd(*a,&b)
80
+ puts "cd #{a}" if dryrun? or trace?
81
+ fileutils.chdir(*a,&b)
82
+ end
83
+
84
+ # Read file.
85
+
86
+ def file_read(path)
87
+ File.read(path)
88
+ end
89
+
90
+ # Write file.
91
+
92
+ def file_write(path, text)
93
+ if dryrun?
94
+ puts "write #{path}"
95
+ else
96
+ File.open(path, 'w'){ |f| f << text }
97
+ end
98
+ end
99
+
100
+ # Assert that a path exists.
101
+
102
+ def exists?(path)
103
+ paths = Dir.glob(path)
104
+ paths.not_empty?
105
+ end
106
+ alias_method :exist?, :exists? ; module_function :exist?
107
+ alias_method :path?, :exists? ; module_function :path?
108
+
109
+ # Assert that a path exists.
110
+
111
+ def exists!(*paths)
112
+ abort "path not found #{path}" unless paths.any?{|path| exists?(path)}
113
+ end
114
+ alias_method :exist!, :exists! ; module_function :exist!
115
+ alias_method :path!, :exists! ; module_function :path!
116
+
117
+ # Is a given path a regular file? If +path+ is a glob
118
+ # then checks to see if all matches are refular files.
119
+
120
+ def file?(path)
121
+ paths = Dir.glob(path)
122
+ paths.not_empty? && paths.all?{ |f| FileTest.file?(f) }
123
+ end
124
+
125
+ # Assert that a given path is a file.
126
+
127
+ def file!(*paths)
128
+ abort "file not found #{path}" unless paths.any?{|path| file?(path)}
129
+ end
130
+
131
+ # Is a given path a directory? If +path+ is a glob
132
+ # checks to see if all matches are directories.
133
+
134
+ def dir?(path)
135
+ paths = Dir.glob(path)
136
+ paths.not_empty? && paths.all?{ |f| FileTest.directory?(f) }
137
+ end
138
+ alias_method :directory?, :dir? ; module_function :directory?
139
+
140
+ # Assert that a given path is a directory.
141
+
142
+ def dir!(*paths)
143
+ paths.each do |path|
144
+ abort "Directory not found: '#{path}'." unless dir?(path)
145
+ end
146
+ end
147
+ alias_method :directory!, :dir! ; module_function :directory!
148
+
149
+ # # Okay, I'm being a dork, but 'fold' seems like a better word
150
+ # # then 'dir', 'folder', or 'directory'.
151
+ #
152
+ # def fold?(path)
153
+ # paths = Dir.glob(path)
154
+ # paths.not_empty? && paths.all?{ |f| FileTest.directory?(f) }
155
+ # end
156
+ #
157
+ # # Assert that a given path is a fold (ie. a folder).
158
+ #
159
+ # def fold!(*paths)
160
+ # abort "fold not found #{path}" unless paths.any?{|path| fold?(path)}
161
+ # end
162
+
163
+ # Return a cached list of the PATH environment variable.
164
+ # This is a support method used by #bin?
165
+
166
+ def command_paths
167
+ @command_paths ||= ENV['PATH'].split(/[:;]/)
168
+ end
169
+
170
+ # Is a file a command executable?
171
+ #
172
+ # TODO: Make more robust. Probably needs to be fixed for Windows.
173
+
174
+ def bin?(fname)
175
+ #@command_paths ||= ENV['PATH'].split(/[:;]/)
176
+ is_bin = command_paths.any? do |f|
177
+ FileTest.exist?(File.join(f, fname))
178
+ end
179
+ #is_bin ? File.basename(fname) : false
180
+ is_bin ? fname : false
181
+ end
182
+
183
+ # Is a path considered reasonably "safe"?
184
+ #
185
+ # TODO: Make more robust.
186
+
187
+ def safe?(path)
188
+ case path
189
+ when *[ '/', '/*', '/**/*' ]
190
+ return false
191
+ end
192
+ true
193
+ end
194
+
195
+ # Does a path need updating, based on given +sources+?
196
+ # This compares mtimes of give paths. Returns false
197
+ # if the path needs to be updated.
198
+
199
+ def out_of_date?(path, *sources)
200
+ return true unless File.exist?(path)
201
+
202
+ sources = sources.collect{ |source| Dir.glob(source) }.flatten
203
+ mtimes = sources.collect{ |file| File.mtime(file) }
204
+
205
+ return true if mtimes.empty? # TODO: This the way to go here?
206
+
207
+ File.mtime(path) < mtimes.max
208
+ end
209
+
210
+ # Glob files.
211
+
212
+ def glob(*args, &blk)
213
+ Dir.glob(*args, &blk)
214
+ end
215
+
216
+ def multiglob(*args, &blk)
217
+ Dir.multiglob(*args, &blk)
218
+ end
219
+
220
+ def multiglob_r(*args, &blk)
221
+ Dir.multiglob_r(*args, &blk)
222
+ end
223
+
224
+ # Stage package by hard linking included files to a stage directory.
225
+ # Stage files in a directory.
226
+ #
227
+ # stage_directory Stage directory.
228
+ # files Files to link to stage.
229
+
230
+ def stage(stage_directory, files)
231
+ return stage_directory if dryrun? # Don't link to stage if dryrun.
232
+
233
+ if File.directory?(stage_directory) # Ensure existance of staging area.
234
+ #raise(???Error, stage_directory) unless force?
235
+ rm_r(stage_directory)
236
+ end
237
+
238
+ mkdir_p(stage_directory) #dir = File.expand_path(stage)
239
+
240
+ #files = package.filelist #+ [package.manifest_file]
241
+
242
+ # TODO Dryrun test here or before folder creation?
243
+ files.each do |f| # Link files into staging area.
244
+ file = File.join(stage_directory, f)
245
+ if File.directory?(f)
246
+ mkdir_p(file)
247
+ else
248
+ unless File.exist?(file) and File.mtime(file) >= File.mtime(f)
249
+ ln(f, file) #safe_ln ?
250
+ end
251
+ end
252
+ end
253
+
254
+ # stage meanifest ?
255
+
256
+ return stage_directory
257
+ end
258
+
259
+ # Create manifest for a directory.
260
+ #
261
+ # TODO: Do this programatically rather then via shell.
262
+
263
+ def stage_manifest(directory)
264
+ cd(directory) do
265
+ sh 'manifest up'
266
+ end
267
+ end
268
+
269
+ # Delegate access to ZipUtils.
270
+
271
+ def ziputils
272
+ dryrun? ? ::ZipUtils::DryRun : ::ZipUtils
273
+ end
274
+
275
+ # Zip folder into file.
276
+
277
+ def zip(folder, file=nil, options={})
278
+ ziputils.zip(folder, file, options)
279
+ end
280
+
281
+ # BZip and tarball folder into file.
282
+
283
+ def tar_bzip(folder, file=nil, options={})
284
+ ziputils.tar_bzip(folder, file, options)
285
+ end
286
+
287
+ # GZip and tarball folder into file. Shortcut for ziputils.tgz.
288
+
289
+ def tgz(folder, file=nil, options={})
290
+ ziputils.tgz(folder, file, options)
291
+ end
292
+
293
+ # # Is a file a task?
294
+ #
295
+ # def task?(path)
296
+ # task = File.dirname($0) + "/#{path}"
297
+ # task.chomp!('!')
298
+ # task if FileTest.file?(task) && FileTest.executable?(task)
299
+ # end
300
+
301
+ end
302
+
303
+ end
304
+ end