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.
- data/CHANGELOG +998 -0
- data/LICENSE +176 -0
- data/NOTICE +26 -0
- data/README.rdoc +134 -0
- data/Rakefile +45 -0
- data/_buildr +29 -0
- data/_jbuildr +29 -0
- data/addon/buildr/antlr.rb +65 -0
- data/addon/buildr/cobertura.rb +22 -0
- data/addon/buildr/drb.rb +281 -0
- data/addon/buildr/emma.rb +22 -0
- data/addon/buildr/hibernate.rb +142 -0
- data/addon/buildr/javacc.rb +85 -0
- data/addon/buildr/jdepend.rb +60 -0
- data/addon/buildr/jetty.rb +248 -0
- data/addon/buildr/jibx.rb +86 -0
- data/addon/buildr/nailgun.rb +221 -0
- data/addon/buildr/openjpa.rb +90 -0
- data/addon/buildr/org/apache/buildr/BuildrNail$Main.class +0 -0
- data/addon/buildr/org/apache/buildr/BuildrNail.class +0 -0
- data/addon/buildr/org/apache/buildr/BuildrNail.java +41 -0
- data/addon/buildr/org/apache/buildr/JettyWrapper$1.class +0 -0
- data/addon/buildr/org/apache/buildr/JettyWrapper$BuildrHandler.class +0 -0
- data/addon/buildr/org/apache/buildr/JettyWrapper.class +0 -0
- data/addon/buildr/org/apache/buildr/JettyWrapper.java +144 -0
- data/addon/buildr/xmlbeans.rb +93 -0
- data/bin/buildr +19 -0
- data/buildr.buildfile +58 -0
- data/buildr.gemspec +65 -0
- data/doc/_config.yml +1 -0
- data/doc/_layouts/default.html +88 -0
- data/doc/_layouts/preface.html +22 -0
- data/doc/artifacts.textile +211 -0
- data/doc/building.textile +244 -0
- data/doc/contributing.textile +252 -0
- data/doc/css/default.css +236 -0
- data/doc/css/print.css +101 -0
- data/doc/css/syntax.css +23 -0
- data/doc/download.textile +79 -0
- data/doc/extending.textile +186 -0
- data/doc/images/1442160941-frontcover.jpg +0 -0
- data/doc/images/asf-logo.gif +0 -0
- data/doc/images/asf-logo.png +0 -0
- data/doc/images/buildr-hires.png +0 -0
- data/doc/images/buildr.png +0 -0
- data/doc/images/favicon.png +0 -0
- data/doc/images/growl-icon.tiff +0 -0
- data/doc/images/note.png +0 -0
- data/doc/images/project-structure.png +0 -0
- data/doc/images/tip.png +0 -0
- data/doc/images/zbuildr.png +0 -0
- data/doc/images/zbuildr.tif +0 -0
- data/doc/index.textile +69 -0
- data/doc/installing.textile +266 -0
- data/doc/languages.textile +459 -0
- data/doc/mailing_lists.textile +25 -0
- data/doc/more_stuff.textile +457 -0
- data/doc/packaging.textile +430 -0
- data/doc/preface.textile +54 -0
- data/doc/projects.textile +271 -0
- data/doc/quick_start.textile +210 -0
- data/doc/scripts/buildr-git.rb +512 -0
- data/doc/scripts/gitflow.rb +296 -0
- data/doc/scripts/install-jruby.sh +44 -0
- data/doc/scripts/install-linux.sh +72 -0
- data/doc/scripts/install-osx.sh +52 -0
- data/doc/settings_profiles.textile +280 -0
- data/doc/testing.textile +222 -0
- data/etc/KEYS +151 -0
- data/lib/buildr.rb +36 -0
- data/lib/buildr/core.rb +35 -0
- data/lib/buildr/core/application.rb +656 -0
- data/lib/buildr/core/build.rb +452 -0
- data/lib/buildr/core/checks.rb +254 -0
- data/lib/buildr/core/common.rb +150 -0
- data/lib/buildr/core/compile.rb +608 -0
- data/lib/buildr/core/environment.rb +129 -0
- data/lib/buildr/core/filter.rb +362 -0
- data/lib/buildr/core/generate.rb +195 -0
- data/lib/buildr/core/help.rb +119 -0
- data/lib/buildr/core/osx.rb +46 -0
- data/lib/buildr/core/progressbar.rb +156 -0
- data/lib/buildr/core/project.rb +866 -0
- data/lib/buildr/core/shell.rb +198 -0
- data/lib/buildr/core/test.rb +723 -0
- data/lib/buildr/core/transports.rb +559 -0
- data/lib/buildr/core/util.rb +449 -0
- data/lib/buildr/groovy.rb +19 -0
- data/lib/buildr/groovy/bdd.rb +106 -0
- data/lib/buildr/groovy/compiler.rb +138 -0
- data/lib/buildr/groovy/shell.rb +48 -0
- data/lib/buildr/ide.rb +19 -0
- data/lib/buildr/ide/eclipse.rb +334 -0
- data/lib/buildr/ide/eclipse/java.rb +53 -0
- data/lib/buildr/ide/eclipse/plugin.rb +68 -0
- data/lib/buildr/ide/eclipse/scala.rb +66 -0
- data/lib/buildr/ide/idea.ipr.template +300 -0
- data/lib/buildr/ide/idea.rb +190 -0
- data/lib/buildr/ide/idea7x.ipr.template +290 -0
- data/lib/buildr/ide/idea7x.rb +212 -0
- data/lib/buildr/java.rb +23 -0
- data/lib/buildr/java/ant.rb +94 -0
- data/lib/buildr/java/bdd.rb +459 -0
- data/lib/buildr/java/cobertura.rb +274 -0
- data/lib/buildr/java/commands.rb +213 -0
- data/lib/buildr/java/compiler.rb +349 -0
- data/lib/buildr/java/deprecated.rb +141 -0
- data/lib/buildr/java/emma.rb +244 -0
- data/lib/buildr/java/jruby.rb +117 -0
- data/lib/buildr/java/jtestr_runner.rb.erb +116 -0
- data/lib/buildr/java/org/apache/buildr/JavaTestFilter.class +0 -0
- data/lib/buildr/java/org/apache/buildr/JavaTestFilter.java +137 -0
- data/lib/buildr/java/packaging.rb +716 -0
- data/lib/buildr/java/pom.rb +174 -0
- data/lib/buildr/java/rjb.rb +155 -0
- data/lib/buildr/java/test_result.rb +353 -0
- data/lib/buildr/java/tests.rb +333 -0
- data/lib/buildr/java/version_requirement.rb +172 -0
- data/lib/buildr/packaging.rb +24 -0
- data/lib/buildr/packaging/archive.rb +488 -0
- data/lib/buildr/packaging/artifact.rb +749 -0
- data/lib/buildr/packaging/artifact_namespace.rb +972 -0
- data/lib/buildr/packaging/artifact_search.rb +140 -0
- data/lib/buildr/packaging/gems.rb +102 -0
- data/lib/buildr/packaging/package.rb +238 -0
- data/lib/buildr/packaging/tar.rb +186 -0
- data/lib/buildr/packaging/version_requirement.rb +172 -0
- data/lib/buildr/packaging/zip.rb +73 -0
- data/lib/buildr/packaging/ziptask.rb +316 -0
- data/lib/buildr/resources/buildr.icns +0 -0
- data/lib/buildr/scala.rb +25 -0
- data/lib/buildr/scala/bdd.rb +109 -0
- data/lib/buildr/scala/compiler.rb +195 -0
- data/lib/buildr/scala/org/apache/buildr/SpecsSingletonRunner$.class +0 -0
- data/lib/buildr/scala/org/apache/buildr/SpecsSingletonRunner.class +0 -0
- data/lib/buildr/scala/org/apache/buildr/SpecsSingletonRunner.scala +35 -0
- data/lib/buildr/scala/shell.rb +55 -0
- data/lib/buildr/scala/tests.rb +157 -0
- data/lib/buildr/shell.rb +180 -0
- data/rakelib/checks.rake +57 -0
- data/rakelib/doc.rake +92 -0
- data/rakelib/jekylltask.rb +120 -0
- data/rakelib/package.rake +73 -0
- data/rakelib/release.rake +149 -0
- data/rakelib/rspec.rake +73 -0
- data/rakelib/setup.rake +54 -0
- data/rakelib/stage.rake +213 -0
- data/rakelib/stage.rake~ +213 -0
- data/spec/addon/drb_spec.rb +328 -0
- data/spec/core/application_spec.rb +502 -0
- data/spec/core/build_spec.rb +677 -0
- data/spec/core/checks_spec.rb +519 -0
- data/spec/core/common_spec.rb +670 -0
- data/spec/core/compile_spec.rb +583 -0
- data/spec/core/extension_spec.rb +93 -0
- data/spec/core/generate_spec.rb +33 -0
- data/spec/core/project_spec.rb +762 -0
- data/spec/core/test_spec.rb +1098 -0
- data/spec/core/transport_spec.rb +537 -0
- data/spec/core/util_spec.rb +67 -0
- data/spec/groovy/bdd_spec.rb +80 -0
- data/spec/groovy/compiler_spec.rb +240 -0
- data/spec/ide/eclipse_spec.rb +501 -0
- data/spec/ide/idea7x_spec.rb +84 -0
- data/spec/java/ant_spec.rb +33 -0
- data/spec/java/bdd_spec.rb +382 -0
- data/spec/java/cobertura_spec.rb +85 -0
- data/spec/java/compiler_spec.rb +446 -0
- data/spec/java/emma_spec.rb +119 -0
- data/spec/java/java_spec.rb +124 -0
- data/spec/java/packaging_spec.rb +1134 -0
- data/spec/java/test_coverage_helper.rb +257 -0
- data/spec/java/tests_spec.rb +493 -0
- data/spec/packaging/archive_spec.rb +527 -0
- data/spec/packaging/artifact_namespace_spec.rb +654 -0
- data/spec/packaging/artifact_spec.rb +795 -0
- data/spec/packaging/packaging_helper.rb +63 -0
- data/spec/packaging/packaging_spec.rb +684 -0
- data/spec/sandbox.rb +142 -0
- data/spec/scala/bdd_spec.rb +119 -0
- data/spec/scala/compiler_spec.rb +284 -0
- data/spec/scala/scala.rb +38 -0
- data/spec/scala/tests_spec.rb +261 -0
- data/spec/spec_helpers.rb +340 -0
- data/spec/version_requirement_spec.rb +129 -0
- 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
|