buildr 1.3.5-x86-mswin32

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 (186) hide show
  1. data/CHANGELOG +998 -0
  2. data/LICENSE +176 -0
  3. data/NOTICE +26 -0
  4. data/README.rdoc +134 -0
  5. data/Rakefile +45 -0
  6. data/_buildr +29 -0
  7. data/_jbuildr +29 -0
  8. data/addon/buildr/antlr.rb +65 -0
  9. data/addon/buildr/cobertura.rb +22 -0
  10. data/addon/buildr/drb.rb +281 -0
  11. data/addon/buildr/emma.rb +22 -0
  12. data/addon/buildr/hibernate.rb +142 -0
  13. data/addon/buildr/javacc.rb +85 -0
  14. data/addon/buildr/jdepend.rb +60 -0
  15. data/addon/buildr/jetty.rb +248 -0
  16. data/addon/buildr/jibx.rb +86 -0
  17. data/addon/buildr/nailgun.rb +221 -0
  18. data/addon/buildr/openjpa.rb +90 -0
  19. data/addon/buildr/org/apache/buildr/BuildrNail$Main.class +0 -0
  20. data/addon/buildr/org/apache/buildr/BuildrNail.class +0 -0
  21. data/addon/buildr/org/apache/buildr/BuildrNail.java +41 -0
  22. data/addon/buildr/org/apache/buildr/JettyWrapper$1.class +0 -0
  23. data/addon/buildr/org/apache/buildr/JettyWrapper$BuildrHandler.class +0 -0
  24. data/addon/buildr/org/apache/buildr/JettyWrapper.class +0 -0
  25. data/addon/buildr/org/apache/buildr/JettyWrapper.java +144 -0
  26. data/addon/buildr/xmlbeans.rb +93 -0
  27. data/bin/buildr +19 -0
  28. data/buildr.buildfile +58 -0
  29. data/buildr.gemspec +65 -0
  30. data/doc/_config.yml +1 -0
  31. data/doc/_layouts/default.html +88 -0
  32. data/doc/_layouts/preface.html +22 -0
  33. data/doc/artifacts.textile +211 -0
  34. data/doc/building.textile +244 -0
  35. data/doc/contributing.textile +252 -0
  36. data/doc/css/default.css +236 -0
  37. data/doc/css/print.css +101 -0
  38. data/doc/css/syntax.css +23 -0
  39. data/doc/download.textile +79 -0
  40. data/doc/extending.textile +186 -0
  41. data/doc/images/1442160941-frontcover.jpg +0 -0
  42. data/doc/images/asf-logo.gif +0 -0
  43. data/doc/images/asf-logo.png +0 -0
  44. data/doc/images/buildr-hires.png +0 -0
  45. data/doc/images/buildr.png +0 -0
  46. data/doc/images/favicon.png +0 -0
  47. data/doc/images/growl-icon.tiff +0 -0
  48. data/doc/images/note.png +0 -0
  49. data/doc/images/project-structure.png +0 -0
  50. data/doc/images/tip.png +0 -0
  51. data/doc/images/zbuildr.png +0 -0
  52. data/doc/images/zbuildr.tif +0 -0
  53. data/doc/index.textile +69 -0
  54. data/doc/installing.textile +266 -0
  55. data/doc/languages.textile +459 -0
  56. data/doc/mailing_lists.textile +25 -0
  57. data/doc/more_stuff.textile +457 -0
  58. data/doc/packaging.textile +430 -0
  59. data/doc/preface.textile +54 -0
  60. data/doc/projects.textile +271 -0
  61. data/doc/quick_start.textile +210 -0
  62. data/doc/scripts/buildr-git.rb +512 -0
  63. data/doc/scripts/gitflow.rb +296 -0
  64. data/doc/scripts/install-jruby.sh +44 -0
  65. data/doc/scripts/install-linux.sh +72 -0
  66. data/doc/scripts/install-osx.sh +52 -0
  67. data/doc/settings_profiles.textile +280 -0
  68. data/doc/testing.textile +222 -0
  69. data/etc/KEYS +151 -0
  70. data/lib/buildr.rb +36 -0
  71. data/lib/buildr/core.rb +35 -0
  72. data/lib/buildr/core/application.rb +656 -0
  73. data/lib/buildr/core/build.rb +452 -0
  74. data/lib/buildr/core/checks.rb +254 -0
  75. data/lib/buildr/core/common.rb +150 -0
  76. data/lib/buildr/core/compile.rb +608 -0
  77. data/lib/buildr/core/environment.rb +129 -0
  78. data/lib/buildr/core/filter.rb +362 -0
  79. data/lib/buildr/core/generate.rb +195 -0
  80. data/lib/buildr/core/help.rb +119 -0
  81. data/lib/buildr/core/osx.rb +46 -0
  82. data/lib/buildr/core/progressbar.rb +156 -0
  83. data/lib/buildr/core/project.rb +866 -0
  84. data/lib/buildr/core/shell.rb +198 -0
  85. data/lib/buildr/core/test.rb +723 -0
  86. data/lib/buildr/core/transports.rb +559 -0
  87. data/lib/buildr/core/util.rb +449 -0
  88. data/lib/buildr/groovy.rb +19 -0
  89. data/lib/buildr/groovy/bdd.rb +106 -0
  90. data/lib/buildr/groovy/compiler.rb +138 -0
  91. data/lib/buildr/groovy/shell.rb +48 -0
  92. data/lib/buildr/ide.rb +19 -0
  93. data/lib/buildr/ide/eclipse.rb +334 -0
  94. data/lib/buildr/ide/eclipse/java.rb +53 -0
  95. data/lib/buildr/ide/eclipse/plugin.rb +68 -0
  96. data/lib/buildr/ide/eclipse/scala.rb +66 -0
  97. data/lib/buildr/ide/idea.ipr.template +300 -0
  98. data/lib/buildr/ide/idea.rb +190 -0
  99. data/lib/buildr/ide/idea7x.ipr.template +290 -0
  100. data/lib/buildr/ide/idea7x.rb +212 -0
  101. data/lib/buildr/java.rb +23 -0
  102. data/lib/buildr/java/ant.rb +94 -0
  103. data/lib/buildr/java/bdd.rb +459 -0
  104. data/lib/buildr/java/cobertura.rb +274 -0
  105. data/lib/buildr/java/commands.rb +213 -0
  106. data/lib/buildr/java/compiler.rb +349 -0
  107. data/lib/buildr/java/deprecated.rb +141 -0
  108. data/lib/buildr/java/emma.rb +244 -0
  109. data/lib/buildr/java/jruby.rb +117 -0
  110. data/lib/buildr/java/jtestr_runner.rb.erb +116 -0
  111. data/lib/buildr/java/org/apache/buildr/JavaTestFilter.class +0 -0
  112. data/lib/buildr/java/org/apache/buildr/JavaTestFilter.java +137 -0
  113. data/lib/buildr/java/packaging.rb +716 -0
  114. data/lib/buildr/java/pom.rb +174 -0
  115. data/lib/buildr/java/rjb.rb +155 -0
  116. data/lib/buildr/java/test_result.rb +353 -0
  117. data/lib/buildr/java/tests.rb +333 -0
  118. data/lib/buildr/java/version_requirement.rb +172 -0
  119. data/lib/buildr/packaging.rb +24 -0
  120. data/lib/buildr/packaging/archive.rb +488 -0
  121. data/lib/buildr/packaging/artifact.rb +749 -0
  122. data/lib/buildr/packaging/artifact_namespace.rb +972 -0
  123. data/lib/buildr/packaging/artifact_search.rb +140 -0
  124. data/lib/buildr/packaging/gems.rb +102 -0
  125. data/lib/buildr/packaging/package.rb +238 -0
  126. data/lib/buildr/packaging/tar.rb +186 -0
  127. data/lib/buildr/packaging/version_requirement.rb +172 -0
  128. data/lib/buildr/packaging/zip.rb +73 -0
  129. data/lib/buildr/packaging/ziptask.rb +316 -0
  130. data/lib/buildr/resources/buildr.icns +0 -0
  131. data/lib/buildr/scala.rb +25 -0
  132. data/lib/buildr/scala/bdd.rb +109 -0
  133. data/lib/buildr/scala/compiler.rb +195 -0
  134. data/lib/buildr/scala/org/apache/buildr/SpecsSingletonRunner$.class +0 -0
  135. data/lib/buildr/scala/org/apache/buildr/SpecsSingletonRunner.class +0 -0
  136. data/lib/buildr/scala/org/apache/buildr/SpecsSingletonRunner.scala +35 -0
  137. data/lib/buildr/scala/shell.rb +55 -0
  138. data/lib/buildr/scala/tests.rb +157 -0
  139. data/lib/buildr/shell.rb +180 -0
  140. data/rakelib/checks.rake +57 -0
  141. data/rakelib/doc.rake +92 -0
  142. data/rakelib/jekylltask.rb +120 -0
  143. data/rakelib/package.rake +73 -0
  144. data/rakelib/release.rake +149 -0
  145. data/rakelib/rspec.rake +73 -0
  146. data/rakelib/setup.rake +54 -0
  147. data/rakelib/stage.rake +213 -0
  148. data/rakelib/stage.rake~ +213 -0
  149. data/spec/addon/drb_spec.rb +328 -0
  150. data/spec/core/application_spec.rb +502 -0
  151. data/spec/core/build_spec.rb +677 -0
  152. data/spec/core/checks_spec.rb +519 -0
  153. data/spec/core/common_spec.rb +670 -0
  154. data/spec/core/compile_spec.rb +583 -0
  155. data/spec/core/extension_spec.rb +93 -0
  156. data/spec/core/generate_spec.rb +33 -0
  157. data/spec/core/project_spec.rb +762 -0
  158. data/spec/core/test_spec.rb +1098 -0
  159. data/spec/core/transport_spec.rb +537 -0
  160. data/spec/core/util_spec.rb +67 -0
  161. data/spec/groovy/bdd_spec.rb +80 -0
  162. data/spec/groovy/compiler_spec.rb +240 -0
  163. data/spec/ide/eclipse_spec.rb +501 -0
  164. data/spec/ide/idea7x_spec.rb +84 -0
  165. data/spec/java/ant_spec.rb +33 -0
  166. data/spec/java/bdd_spec.rb +382 -0
  167. data/spec/java/cobertura_spec.rb +85 -0
  168. data/spec/java/compiler_spec.rb +446 -0
  169. data/spec/java/emma_spec.rb +119 -0
  170. data/spec/java/java_spec.rb +124 -0
  171. data/spec/java/packaging_spec.rb +1134 -0
  172. data/spec/java/test_coverage_helper.rb +257 -0
  173. data/spec/java/tests_spec.rb +493 -0
  174. data/spec/packaging/archive_spec.rb +527 -0
  175. data/spec/packaging/artifact_namespace_spec.rb +654 -0
  176. data/spec/packaging/artifact_spec.rb +795 -0
  177. data/spec/packaging/packaging_helper.rb +63 -0
  178. data/spec/packaging/packaging_spec.rb +684 -0
  179. data/spec/sandbox.rb +142 -0
  180. data/spec/scala/bdd_spec.rb +119 -0
  181. data/spec/scala/compiler_spec.rb +284 -0
  182. data/spec/scala/scala.rb +38 -0
  183. data/spec/scala/tests_spec.rb +261 -0
  184. data/spec/spec_helpers.rb +340 -0
  185. data/spec/version_requirement_spec.rb +129 -0
  186. metadata +383 -0
@@ -0,0 +1,559 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one or more
2
+ # contributor license agreements. See the NOTICE file distributed with this
3
+ # work for additional information regarding copyright ownership. The ASF
4
+ # licenses this file to you under the Apache License, Version 2.0 (the
5
+ # "License"); you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations under
14
+ # the License.
15
+
16
+
17
+ require 'uri'
18
+ require 'net/http'
19
+ # PATCH: On Windows, Net::SSH 2.0.2 attempts to load the Pageant DLLs which break on JRuby.
20
+ $LOADED_FEATURES << 'net/ssh/authentication/pageant.rb' if RUBY_PLATFORM =~ /java/
21
+ gem 'net-ssh' ; Net.autoload :SSH, 'net/ssh'
22
+ gem 'net-sftp' ; Net.autoload :SFTP, 'net/sftp'
23
+ autoload :CGI, 'cgi'
24
+ require 'digest/md5'
25
+ require 'digest/sha1'
26
+ require 'stringio'
27
+ autoload :ProgressBar, 'buildr/core/progressbar'
28
+
29
+
30
+ # Not quite open-uri, but similar. Provides read and write methods for the resource represented by the URI.
31
+ # Currently supports reads for URI::HTTP and writes for URI::SFTP. Also provides convenience methods for
32
+ # downloads and uploads.
33
+ module URI
34
+
35
+ # Raised when trying to read/download a resource that doesn't exist.
36
+ class NotFoundError < RuntimeError
37
+ end
38
+
39
+ # How many bytes to read/write at once. Do not change without checking BUILDR-214 first.
40
+ RW_CHUNK_SIZE = 128 * 1024 #:nodoc:
41
+
42
+ class << self
43
+
44
+ # :call-seq:
45
+ # read(uri, options?) => content
46
+ # read(uri, options?) { |chunk| ... }
47
+ #
48
+ # Reads from the resource behind this URI. The first form returns the content of the resource,
49
+ # the second form yields to the block with each chunk of content (usually more than one).
50
+ #
51
+ # For example:
52
+ # File.open 'image.jpg', 'w' do |file|
53
+ # URI.read('http://example.com/image.jpg') { |chunk| file.write chunk }
54
+ # end
55
+ # Shorter version:
56
+ # File.open('image.jpg', 'w') { |file| file.write URI.read('http://example.com/image.jpg') }
57
+ #
58
+ # Supported options:
59
+ # * :modified -- Only download if file modified since this timestamp. Returns nil if not modified.
60
+ # * :progress -- Show the progress bar while reading.
61
+ def read(uri, options = nil, &block)
62
+ uri = URI.parse(uri.to_s) unless URI === uri
63
+ uri.read options, &block
64
+ end
65
+
66
+ # :call-seq:
67
+ # download(uri, target, options?)
68
+ #
69
+ # Downloads the resource to the target.
70
+ #
71
+ # The target may be a file name (string or task), in which case the file is created from the resource.
72
+ # The target may also be any object that responds to +write+, e.g. File, StringIO, Pipe.
73
+ #
74
+ # Use the progress bar when running in verbose mode.
75
+ def download(uri, target, options = nil)
76
+ uri = URI.parse(uri.to_s) unless URI === uri
77
+ uri.download target, options
78
+ end
79
+
80
+ # :call-seq:
81
+ # write(uri, content, options?)
82
+ # write(uri, options?) { |bytes| .. }
83
+ #
84
+ # Writes to the resource behind the URI. The first form writes the content from a string or an object
85
+ # that responds to +read+ and optionally +size+. The second form writes the content by yielding to the
86
+ # block. Each yield should return up to the specified number of bytes, the last yield returns nil.
87
+ #
88
+ # For example:
89
+ # File.open 'killer-app.jar', 'rb' do |file|
90
+ # write('sftp://localhost/jars/killer-app.jar') { |chunk| file.read(chunk) }
91
+ # end
92
+ # Or:
93
+ # write 'sftp://localhost/jars/killer-app.jar', File.read('killer-app.jar')
94
+ #
95
+ # Supported options:
96
+ # * :progress -- Show the progress bar while reading.
97
+ def write(uri, *args, &block)
98
+ uri = URI.parse(uri.to_s) unless URI === uri
99
+ uri.write *args, &block
100
+ end
101
+
102
+ # :call-seq:
103
+ # upload(uri, source, options?)
104
+ #
105
+ # Uploads from source to the resource.
106
+ #
107
+ # The source may be a file name (string or task), in which case the file is uploaded to the resource.
108
+ # The source may also be any object that responds to +read+ (and optionally +size+), e.g. File, StringIO, Pipe.
109
+ #
110
+ # Use the progress bar when running in verbose mode.
111
+ def upload(uri, source, options = nil)
112
+ uri = URI.parse(uri.to_s) unless URI === uri
113
+ uri.upload source, options
114
+ end
115
+
116
+ end
117
+
118
+ class Generic
119
+
120
+ # :call-seq:
121
+ # read(options?) => content
122
+ # read(options?) { |chunk| ... }
123
+ #
124
+ # Reads from the resource behind this URI. The first form returns the content of the resource,
125
+ # the second form yields to the block with each chunk of content (usually more than one).
126
+ #
127
+ # For options, see URI::read.
128
+ def read(options = nil, &block)
129
+ fail 'This protocol doesn\'t support reading (yet, how about helping by implementing it?)'
130
+ end
131
+
132
+ # :call-seq:
133
+ # download(target, options?)
134
+ #
135
+ # Downloads the resource to the target.
136
+ #
137
+ # The target may be a file name (string or task), in which case the file is created from the resource.
138
+ # The target may also be any object that responds to +write+, e.g. File, StringIO, Pipe.
139
+ #
140
+ # Use the progress bar when running in verbose mode.
141
+ def download(target, options = nil)
142
+ case target
143
+ when Rake::Task
144
+ download target.name, options
145
+ when String
146
+ # If download breaks we end up with a partial file which is
147
+ # worse than not having a file at all, so download to temporary
148
+ # file and then move over.
149
+ modified = File.stat(target).mtime if File.exist?(target)
150
+ temp = Tempfile.new(File.basename(target))
151
+ temp.binmode
152
+ read({:progress=>verbose}.merge(options || {}).merge(:modified=>modified)) { |chunk| temp.write chunk }
153
+ temp.close
154
+ mkpath File.dirname(target)
155
+ mv temp.path, target
156
+ when File
157
+ read({:progress=>verbose}.merge(options || {}).merge(:modified=>target.mtime)) { |chunk| target.write chunk }
158
+ target.flush
159
+ else
160
+ raise ArgumentError, 'Expecting a target that is either a file name (string, task) or object that responds to write (file, pipe).' unless target.respond_to?(:write)
161
+ read({:progress=>verbose}.merge(options || {})) { |chunk| target.write chunk }
162
+ target.flush
163
+ end
164
+ end
165
+
166
+ # :call-seq:
167
+ # write(content, options?)
168
+ # write(options?) { |bytes| .. }
169
+ #
170
+ # Writes to the resource behind the URI. The first form writes the content from a string or an object
171
+ # that responds to +read+ and optionally +size+. The second form writes the content by yielding to the
172
+ # block. Each yield should return up to the specified number of bytes, the last yield returns nil.
173
+ #
174
+ # For options, see URI::write.
175
+ def write(*args, &block)
176
+ options = args.pop if Hash === args.last
177
+ options ||= {}
178
+ if String === args.first
179
+ ios = StringIO.new(args.first, 'r')
180
+ write(options.merge(:size=>args.first.size)) { |bytes| ios.read(bytes) }
181
+ elsif args.first.respond_to?(:read)
182
+ size = args.first.size rescue nil
183
+ write({:size=>size}.merge(options)) { |bytes| args.first.read(bytes) }
184
+ elsif args.empty? && block
185
+ write_internal options, &block
186
+ else
187
+ raise ArgumentError, 'Either give me the content, or pass me a block, otherwise what would I upload?'
188
+ end
189
+ end
190
+
191
+ # :call-seq:
192
+ # upload(source, options?)
193
+ #
194
+ # Uploads from source to the resource.
195
+ #
196
+ # The source may be a file name (string or task), in which case the file is uploaded to the resource.
197
+ # If the source is a directory, uploads all files inside the directory (including nested directories).
198
+ # The source may also be any object that responds to +read+ (and optionally +size+), e.g. File, StringIO, Pipe.
199
+ #
200
+ # Use the progress bar when running in verbose mode.
201
+ def upload(source, options = nil)
202
+ source = source.name if Rake::Task === source
203
+ options ||= {}
204
+ if String === source
205
+ raise NotFoundError, 'No source file/directory to upload.' unless File.exist?(source)
206
+ if File.directory?(source)
207
+ Dir.glob("#{source}/**/*").reject { |file| File.directory?(file) }.each do |file|
208
+ uri = self + (File.join(self.path, file.sub(source, '')))
209
+ uri.upload file, {:digests=>[]}.merge(options)
210
+ end
211
+ else
212
+ File.open(source, 'rb') { |input| upload input, options }
213
+ end
214
+ elsif source.respond_to?(:read)
215
+ digests = (options[:digests] || [:md5, :sha1]).
216
+ inject({}) { |hash, name| hash[name] = Digest.const_get(name.to_s.upcase).new ; hash }
217
+ size = source.size rescue nil
218
+ write (options).merge(:progress=>verbose && size, :size=>size) do |bytes|
219
+ source.read(bytes).tap do |chunk|
220
+ digests.values.each { |digest| digest << chunk } if chunk
221
+ end
222
+ end
223
+ digests.each do |key, digest|
224
+ self.merge("#{self.path}.#{key}").write "#{digest.hexdigest} #{File.basename(path)}",
225
+ (options).merge(:progress=>false)
226
+ end
227
+ else
228
+ raise ArgumentError, 'Expecting source to be a file name (string, task) or any object that responds to read (file, pipe).'
229
+ end
230
+ end
231
+
232
+ protected
233
+
234
+ # :call-seq:
235
+ # with_progress_bar(show, file_name, size) { |progress| ... }
236
+ #
237
+ # Displays a progress bar while executing the block. The first argument must be true for the
238
+ # progress bar to show (TTY output also required), as a convenient for selectively using the
239
+ # progress bar from a single block.
240
+ #
241
+ # The second argument provides a filename to display, the third its size in bytes.
242
+ #
243
+ # The block is yielded with a progress object that implements a single method.
244
+ # Call << for each block of bytes down/uploaded.
245
+ def with_progress_bar(show, file_name, size, &block) #:nodoc:
246
+ options = { :total=>size || 0, :title=>file_name }
247
+ options[:hidden] = true unless show
248
+ ProgressBar.start options, &block
249
+ end
250
+
251
+ # :call-seq:
252
+ # proxy_uri => URI?
253
+ #
254
+ # Returns the proxy server to use. Obtains the proxy from the relevant environment variable (e.g. HTTP_PROXY).
255
+ # Supports exclusions based on host name and port number from environment variable NO_PROXY.
256
+ def proxy_uri
257
+ proxy = ENV["#{scheme.upcase}_PROXY"]
258
+ proxy = URI.parse(proxy) if String === proxy
259
+ excludes = ENV['NO_PROXY'].to_s.split(/\s*,\s*/).compact
260
+ excludes = excludes.map { |exclude| exclude =~ /:\d+$/ ? exclude : "#{exclude}:*" }
261
+ return proxy unless excludes.any? { |exclude| File.fnmatch(exclude, "#{host}:#{port}") }
262
+ end
263
+
264
+ def write_internal(options, &block) #:nodoc:
265
+ fail 'This protocol doesn\'t support writing (yet, how about helping by implementing it?)'
266
+ end
267
+
268
+ end
269
+
270
+
271
+ class HTTP #:nodoc:
272
+
273
+ # See URI::Generic#read
274
+ def read(options = nil, &block)
275
+ options ||= {}
276
+ connect do |http|
277
+ trace "Requesting #{self}"
278
+ headers = { 'If-Modified-Since' => CGI.rfc1123_date(options[:modified].utc) } if options[:modified]
279
+ request = Net::HTTP::Get.new(request_uri.empty? ? '/' : request_uri, headers)
280
+ request.basic_auth self.user, self.password if self.user
281
+ http.request request do |response|
282
+ case response
283
+ when Net::HTTPNotModified
284
+ # No modification, nothing to do.
285
+ trace 'Not modified since last download'
286
+ return nil
287
+ when Net::HTTPRedirection
288
+ # Try to download from the new URI, handle relative redirects.
289
+ trace "Redirected to #{response['Location']}"
290
+ rself = self + URI.parse(response['Location'])
291
+ rself.user, rself.password = self.user, self.password
292
+ return rself.read(options, &block)
293
+ when Net::HTTPOK
294
+ info "Downloading #{self}"
295
+ result = nil
296
+ with_progress_bar options[:progress], path.split('/').last, response.content_length do |progress|
297
+ if block
298
+ response.read_body do |chunk|
299
+ block.call chunk
300
+ progress << chunk
301
+ end
302
+ else
303
+ result = ''
304
+ response.read_body do |chunk|
305
+ result << chunk
306
+ progress << chunk
307
+ end
308
+ end
309
+ end
310
+ return result
311
+ when Net::HTTPNotFound
312
+ raise NotFoundError, "Looking for #{self} and all I got was a 404!"
313
+ else
314
+ raise RuntimeError, "Failed to download #{self}: #{response.message}"
315
+ end
316
+ end
317
+ end
318
+ end
319
+
320
+ private
321
+
322
+ def write_internal(options, &block) #:nodoc:
323
+ options ||= {}
324
+ connect do |http|
325
+ trace "Uploading to #{path}"
326
+ content = StringIO.new
327
+ while chunk = yield(RW_CHUNK_SIZE)
328
+ content << chunk
329
+ end
330
+ headers = { 'Content-MD5'=>Digest::MD5.hexdigest(content.string) }
331
+ request = Net::HTTP::Put.new(request_uri.empty? ? '/' : request_uri, headers)
332
+ request.basic_auth self.user, self.password if self.user
333
+ response = nil
334
+ with_progress_bar options[:progress], path.split('/').last, content.size do |progress|
335
+ request.content_length = content.size
336
+ content.rewind
337
+ stream = Object.new
338
+ class << stream ; self ;end.send :define_method, :read do |count|
339
+ bytes = content.read(count)
340
+ progress << bytes if bytes
341
+ bytes
342
+ end
343
+ request.body_stream = stream
344
+ response = http.request(request)
345
+ end
346
+
347
+ case response
348
+ when Net::HTTPRedirection
349
+ # Try to download from the new URI, handle relative redirects.
350
+ trace "Redirected to #{response['Location']}"
351
+ content.rewind
352
+ return (self + URI.parse(response['location'])).write_internal(options) { |bytes| content.read(bytes) }
353
+ when Net::HTTPSuccess
354
+ else
355
+ raise RuntimeError, "Failed to upload #{self}: #{response.message}"
356
+ end
357
+ end
358
+ end
359
+
360
+ def connect
361
+ if proxy = proxy_uri
362
+ proxy = URI.parse(proxy) if String === proxy
363
+ http = Net::HTTP.new(host, port, proxy.host, proxy.port, proxy.user, proxy.password)
364
+ else
365
+ http = Net::HTTP.new(host, port)
366
+ end
367
+ if self.instance_of? URI::HTTPS
368
+ require 'net/https'
369
+ http.use_ssl = true
370
+ end
371
+ yield http
372
+ end
373
+
374
+ end
375
+
376
+
377
+ class SFTP < Generic #:nodoc:
378
+
379
+ DEFAULT_PORT = 22
380
+ COMPONENT = [ :scheme, :userinfo, :host, :port, :path ].freeze
381
+
382
+ class << self
383
+ # Caching of passwords, so we only need to ask once.
384
+ def passwords
385
+ @passwords ||= {}
386
+ end
387
+ end
388
+
389
+ def initialize(*arg)
390
+ super
391
+ end
392
+
393
+ def read(options = {}, &block)
394
+ # SSH options are based on the username/password from the URI.
395
+ ssh_options = { :port=>port, :password=>password }.merge(options[:ssh_options] || {})
396
+ ssh_options[:password] ||= SFTP.passwords[host]
397
+ begin
398
+ trace "Connecting to #{host}"
399
+ result = nil
400
+ Net::SFTP.start(host, user, ssh_options) do |sftp|
401
+ SFTP.passwords[host] = ssh_options[:password]
402
+ trace 'connected'
403
+
404
+ with_progress_bar options[:progress] && options[:size], path.split('/'), options[:size] || 0 do |progress|
405
+ trace "Downloading to #{path}"
406
+ sftp.file.open(path, 'r') do |file|
407
+ if block
408
+ while chunk = file.read(RW_CHUNK_SIZE)
409
+ block.call chunk
410
+ progress << chunk
411
+ end
412
+ else
413
+ result = ''
414
+ while chunk = file.read(RW_CHUNK_SIZE)
415
+ result << chunk
416
+ progress << chunk
417
+ end
418
+ end
419
+ end
420
+ end
421
+ end
422
+ return result
423
+ rescue Net::SSH::AuthenticationFailed=>ex
424
+ # Only if running with console, prompt for password.
425
+ if !ssh_options[:password] && $stdout.isatty
426
+ password = ask("Password for #{host}:") { |q| q.echo = '*' }
427
+ ssh_options[:password] = password
428
+ retry
429
+ end
430
+ raise
431
+ end
432
+ end
433
+
434
+ protected
435
+
436
+ def write_internal(options, &block) #:nodoc:
437
+ # SSH options are based on the username/password from the URI.
438
+ ssh_options = { :port=>port, :password=>password }.merge(options[:ssh_options] || {})
439
+ ssh_options[:password] ||= SFTP.passwords[host]
440
+ begin
441
+ trace "Connecting to #{host}"
442
+ Net::SFTP.start(host, user, ssh_options) do |sftp|
443
+ SFTP.passwords[host] = ssh_options[:password]
444
+ trace 'Connected'
445
+
446
+ # To create a path, we need to create all its parent. We use realpath to determine if
447
+ # the path already exists, otherwise mkdir fails.
448
+ trace "Creating path #{path}"
449
+ File.dirname(path).split('/').reject(&:empty?).inject('/') do |base, part|
450
+ combined = base + part
451
+ sftp.close(sftp.opendir!(combined)) rescue sftp.mkdir! combined, {}
452
+ "#{combined}/"
453
+ end
454
+
455
+ with_progress_bar options[:progress] && options[:size], path.split('/'), options[:size] || 0 do |progress|
456
+ trace "Uploading to #{path}"
457
+ sftp.file.open(path, 'w') do |file|
458
+ while chunk = yield(RW_CHUNK_SIZE)
459
+ file.write chunk
460
+ progress << chunk
461
+ end
462
+ sftp.setstat(path, :permissions => options[:permissions]) if options[:permissions]
463
+ end
464
+ end
465
+ end
466
+ rescue Net::SSH::AuthenticationFailed=>ex
467
+ # Only if running with console, prompt for password.
468
+ if !ssh_options[:password] && $stdout.isatty
469
+ password = ask("Password for #{host}:") { |q| q.echo = '*' }
470
+ ssh_options[:password] = password
471
+ retry
472
+ end
473
+ raise
474
+ end
475
+ end
476
+
477
+ end
478
+
479
+ @@schemes['SFTP'] = SFTP
480
+
481
+
482
+ # File URL. Keep in mind that file URLs take the form of <code>file://host/path</code>, although the host
483
+ # is not used, so typically all you will see are three backslashes. This methods accept common variants,
484
+ # like <code>file:/path</code> but always returns a valid URL.
485
+ class FILE < Generic
486
+
487
+ COMPONENT = [ :host, :path ].freeze
488
+
489
+ def initialize(*args)
490
+ super
491
+ # file:something (opaque) becomes file:///something
492
+ if path.nil?
493
+ set_path "/#{opaque}"
494
+ unless opaque.nil?
495
+ set_opaque nil
496
+ warn "#{caller[2]}: We'll accept this URL, but just so you know, it needs three slashes, as in: #{to_s}"
497
+ end
498
+ end
499
+ # Sadly, file://something really means file://something/ (something being server)
500
+ set_path '/' if path.empty?
501
+
502
+ # On windows, file://c:/something is not a valid URL, but people do it anyway, so if we see a drive-as-host,
503
+ # we'll just be nice enough to fix it. (URI actually strips the colon here)
504
+ if host =~ /^[a-zA-Z]$/
505
+ set_path "/#{host}:#{path}"
506
+ set_host nil
507
+ end
508
+ end
509
+
510
+ # See URI::Generic#read
511
+ def read(options = nil, &block)
512
+ options ||= {}
513
+ raise ArgumentError, 'Either you\'re attempting to read a file from another host (which we don\'t support), or you used two slashes by mistake, where you should have file:///<path>.' if host
514
+
515
+ path = real_path
516
+ # TODO: complain about clunky URLs
517
+ raise NotFoundError, "Looking for #{self} and can't find it." unless File.exists?(path)
518
+ raise NotFoundError, "Looking for the file #{self}, and it happens to be a directory." if File.directory?(path)
519
+ File.open path, 'rb' do |input|
520
+ with_progress_bar options[:progress], path.split('/').last, input.stat.size do |progress|
521
+ block ? block.call(input.read) : input.read
522
+ end
523
+ end
524
+ end
525
+
526
+ def to_s
527
+ "file://#{host}#{path}"
528
+ end
529
+
530
+ # The URL path always starts with a backslash. On most operating systems (Linux, Darwin, BSD) it points
531
+ # to the absolute path on the file system. But on Windows, it comes before the drive letter, creating an
532
+ # unusable path, so real_path fixes that. Ugly but necessary hack.
533
+ def real_path #:nodoc:
534
+ RUBY_PLATFORM =~ /win32/ && path =~ /^\/[a-zA-Z]:\// ? path[1..-1] : path
535
+ end
536
+
537
+ protected
538
+
539
+ def write_internal(options, &block) #:nodoc:
540
+ raise ArgumentError, 'Either you\'re attempting to write a file to another host (which we don\'t support), or you used two slashes by mistake, where you should have file:///<path>.' if host
541
+ temp = Tempfile.new(File.basename(path))
542
+ temp.binmode
543
+ with_progress_bar options[:progress] && options[:size], path.split('/'), options[:size] || 0 do |progress|
544
+ while chunk = yield(RW_CHUNK_SIZE)
545
+ temp.write chunk
546
+ progress << chunk
547
+ end
548
+ end
549
+ temp.close
550
+ mkpath File.dirname(real_path)
551
+ mv temp.path, real_path
552
+ real_path
553
+ end
554
+
555
+ @@schemes['FILE'] = FILE
556
+
557
+ end
558
+
559
+ end