rubygems-update 0.8.3
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.
Potentially problematic release.
This version of rubygems-update might be problematic. Click here for more details.
- data/ChangeLog +2335 -0
- data/README +54 -0
- data/Rakefile +293 -0
- data/Releases +98 -0
- data/TODO +7 -0
- data/bin/gem +11 -0
- data/bin/gem_server +111 -0
- data/bin/generate_yaml_index.rb +58 -0
- data/bin/update_rubygems +18 -0
- data/doc/doc.css +73 -0
- data/doc/makedoc.rb +4 -0
- data/examples/application/an-app.gemspec +26 -0
- data/examples/application/bin/myapp +3 -0
- data/examples/application/lib/somefunctionality.rb +3 -0
- data/gemspecs/README +4 -0
- data/gemspecs/cgikit-1.1.0.gemspec +18 -0
- data/gemspecs/jabber4r.gemspec +26 -0
- data/gemspecs/linguistics.gemspec +22 -0
- data/gemspecs/ook.gemspec +21 -0
- data/gemspecs/progressbar.gemspec +22 -0
- data/gemspecs/redcloth.gemspec +22 -0
- data/gemspecs/rublog.gemspec +23 -0
- data/gemspecs/ruby-doom.gemspec +21 -0
- data/gemspecs/rubyjdwp.gemspec +21 -0
- data/gemspecs/statistics.gemspec +21 -0
- data/lib/rubygems.rb +353 -0
- data/lib/rubygems/builder.rb +54 -0
- data/lib/rubygems/cmd_manager.rb +127 -0
- data/lib/rubygems/command.rb +191 -0
- data/lib/rubygems/config_file.rb +57 -0
- data/lib/rubygems/doc_manager.rb +94 -0
- data/lib/rubygems/format.rb +65 -0
- data/lib/rubygems/gem_commands.rb +925 -0
- data/lib/rubygems/gem_runner.rb +23 -0
- data/lib/rubygems/installer.rb +621 -0
- data/lib/rubygems/loadpath_manager.rb +108 -0
- data/lib/rubygems/old_format.rb +150 -0
- data/lib/rubygems/open-uri.rb +604 -0
- data/lib/rubygems/package.rb +740 -0
- data/lib/rubygems/remote_installer.rb +499 -0
- data/lib/rubygems/rubygems_version.rb +6 -0
- data/lib/rubygems/source_index.rb +130 -0
- data/lib/rubygems/specification.rb +613 -0
- data/lib/rubygems/user_interaction.rb +176 -0
- data/lib/rubygems/validator.rb +148 -0
- data/lib/rubygems/version.rb +279 -0
- data/lib/ubygems.rb +4 -0
- data/pkgs/sources/lib/sources.rb +6 -0
- data/pkgs/sources/sources.gemspec +14 -0
- data/post-install.rb +75 -0
- data/redist/session.gem +433 -0
- data/scripts/buildtests.rb +25 -0
- data/scripts/gemdoc.rb +62 -0
- data/scripts/runtest.rb +17 -0
- data/scripts/specdoc.rb +164 -0
- data/setup.rb +1360 -0
- data/test/bogussources.rb +5 -0
- data/test/data/legacy/keyedlist-0.4.0.ruby +11 -0
- data/test/data/legacy/keyedlist-0.4.0.yaml +16 -0
- data/test/data/lib/code.rb +1 -0
- data/test/data/one/README.one +1 -0
- data/test/data/one/lib/one.rb +3 -0
- data/test/data/one/one.gemspec +17 -0
- data/test/data/one/one.yaml +40 -0
- data/test/functional.rb +145 -0
- data/test/gemenvironment.rb +45 -0
- data/test/gemutilities.rb +18 -0
- data/test/insure_session.rb +46 -0
- data/test/mock/gems/gems/sources-0.0.1/lib/sources.rb +5 -0
- data/test/mock/gems/specifications/sources-0.0.1.gemspec +8 -0
- data/test/mockgemui.rb +45 -0
- data/test/onegem.rb +23 -0
- data/test/simple_gem.rb +66 -0
- data/test/test_builder.rb +13 -0
- data/test/test_cached_fetcher.rb +60 -0
- data/test/test_check_command.rb +28 -0
- data/test/test_command.rb +130 -0
- data/test/test_configfile.rb +36 -0
- data/test/test_format.rb +70 -0
- data/test/test_gemloadpaths.rb +45 -0
- data/test/test_gempaths.rb +115 -0
- data/test/test_loadmanager.rb +40 -0
- data/test/test_local_cache.rb +157 -0
- data/test/test_package.rb +600 -0
- data/test/test_parse_commands.rb +179 -0
- data/test/test_process_commands.rb +21 -0
- data/test/test_remote_fetcher.rb +162 -0
- data/test/test_remote_installer.rb +154 -0
- data/test/test_source_index.rb +58 -0
- data/test/test_specification.rb +286 -0
- data/test/test_validator.rb +53 -0
- data/test/test_version_comparison.rb +204 -0
- data/test/testgem.rc +6 -0
- data/test/user_capture.rb +1 -0
- data/test/yaml_data.rb +59 -0
- metadata +151 -0
@@ -0,0 +1,740 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (C) 2004 Mauricio Julio Fern�ndez Pradier
|
3
|
+
# See LICENSE.txt for additional licensing information.
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'yaml'
|
7
|
+
require 'yaml/syck'
|
8
|
+
require 'fileutils'
|
9
|
+
require 'zlib'
|
10
|
+
require 'digest/md5'
|
11
|
+
require 'fileutils'
|
12
|
+
require 'find'
|
13
|
+
require 'stringio'
|
14
|
+
|
15
|
+
require 'rubygems/specification'
|
16
|
+
|
17
|
+
module Gem
|
18
|
+
|
19
|
+
# Wrapper for FileUtils meant to provide logging and additional operations if
|
20
|
+
# needed.
|
21
|
+
class FileOperations
|
22
|
+
extend FileUtils
|
23
|
+
class << self
|
24
|
+
# additional methods not implemented in FileUtils
|
25
|
+
end
|
26
|
+
def initialize(logger = nil)
|
27
|
+
@logger = logger
|
28
|
+
end
|
29
|
+
|
30
|
+
def method_missing(meth, *args, &block)
|
31
|
+
case
|
32
|
+
when FileUtils.respond_to?(meth)
|
33
|
+
@logger.log "#{meth}: #{args}" if @logger
|
34
|
+
FileUtils.send meth, *args, &block
|
35
|
+
when FileOperations.respond_to?(meth)
|
36
|
+
@logger.log "#{meth}: #{args}" if @logger
|
37
|
+
FileOperations.send meth, *args, &block
|
38
|
+
else
|
39
|
+
super
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
module Package
|
46
|
+
|
47
|
+
class NonSeekableIO < StandardError; end
|
48
|
+
class ArgumentError < ::ArgumentError; end
|
49
|
+
class ClosedIO < StandardError; end
|
50
|
+
class BadCheckSum < StandardError; end
|
51
|
+
class TooLongFileName < StandardError; end
|
52
|
+
|
53
|
+
module FSyncDir
|
54
|
+
private
|
55
|
+
def fsync_dir(dirname)
|
56
|
+
# make sure this hits the disc
|
57
|
+
begin
|
58
|
+
dir = open(dirname, "r")
|
59
|
+
dir.fsync
|
60
|
+
rescue # ignore IOError if it's an unpatched (old) Ruby
|
61
|
+
ensure
|
62
|
+
dir.close if dir rescue nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class TarHeader
|
68
|
+
FIELDS = [:name, :mode, :uid, :gid, :size, :mtime, :checksum, :typeflag,
|
69
|
+
:linkname, :magic, :version, :uname, :gname, :devmajor,
|
70
|
+
:devminor, :prefix]
|
71
|
+
FIELDS.each {|x| attr_reader x}
|
72
|
+
|
73
|
+
def self.new_from_stream(stream)
|
74
|
+
data = stream.read(512)
|
75
|
+
fields = data.unpack( "A100" + # record name
|
76
|
+
"A8A8A8" + # mode, uid, gid
|
77
|
+
"A12A12" + # size, mtime
|
78
|
+
"A8A" + # checksum, typeflag
|
79
|
+
"A100" + # linkname
|
80
|
+
"A6A2" + # magic, version
|
81
|
+
"A32" + # uname
|
82
|
+
"A32" + # gname
|
83
|
+
"A8A8" + # devmajor, devminor
|
84
|
+
"A155" # prefix
|
85
|
+
)
|
86
|
+
name = fields.shift
|
87
|
+
mode = fields.shift.oct
|
88
|
+
uid = fields.shift.oct
|
89
|
+
gid = fields.shift.oct
|
90
|
+
size = fields.shift.oct
|
91
|
+
mtime = fields.shift.oct
|
92
|
+
checksum = fields.shift.oct
|
93
|
+
typeflag = fields.shift
|
94
|
+
linkname = fields.shift
|
95
|
+
magic = fields.shift
|
96
|
+
version = fields.shift.oct
|
97
|
+
uname = fields.shift
|
98
|
+
gname = fields.shift
|
99
|
+
devmajor = fields.shift.oct
|
100
|
+
devminor = fields.shift.oct
|
101
|
+
prefix = fields.shift
|
102
|
+
|
103
|
+
empty = (data == "\0" * 512)
|
104
|
+
|
105
|
+
new(:name=>name, :mode=>mode, :uid=>uid, :gid=>gid, :size=>size,
|
106
|
+
:mtime=>mtime, :checksum=>checksum, :typeflag=>typeflag, :magic=>magic,
|
107
|
+
:version=>version, :uname=>uname, :gname=>gname, :devmajor=>devmajor,
|
108
|
+
:devminor=>devminor, :prefix=>prefix, :empty => empty )
|
109
|
+
end
|
110
|
+
|
111
|
+
def initialize(vals)
|
112
|
+
unless vals[:name] && vals[:size] && vals[:prefix] && vals[:mode]
|
113
|
+
raise Package::ArgumentError
|
114
|
+
end
|
115
|
+
vals[:uid] ||= 0
|
116
|
+
vals[:gid] ||= 0
|
117
|
+
vals[:mtime] ||= 0
|
118
|
+
vals[:checksum] ||= ""
|
119
|
+
vals[:typeflag] ||= "0"
|
120
|
+
vals[:magic] ||= "ustar"
|
121
|
+
vals[:version] ||= "00"
|
122
|
+
vals[:uname] ||= "wheel"
|
123
|
+
vals[:gname] ||= "wheel"
|
124
|
+
vals[:devmajor] ||= 0
|
125
|
+
vals[:devminor] ||= 0
|
126
|
+
FIELDS.each {|x| instance_variable_set "@#{x.to_s}", vals[x]}
|
127
|
+
@empty = vals[:empty]
|
128
|
+
end
|
129
|
+
|
130
|
+
def empty?
|
131
|
+
@empty
|
132
|
+
end
|
133
|
+
|
134
|
+
def to_s
|
135
|
+
update_checksum
|
136
|
+
header(checksum)
|
137
|
+
end
|
138
|
+
|
139
|
+
def update_checksum
|
140
|
+
h = header(" " * 8)
|
141
|
+
@checksum = oct(calculate_checksum(h), 6)
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
def oct(num, len)
|
146
|
+
"%0#{len}o" % num
|
147
|
+
end
|
148
|
+
|
149
|
+
def calculate_checksum(hdr)
|
150
|
+
hdr.unpack("C*").inject{|a,b| a+b}
|
151
|
+
end
|
152
|
+
|
153
|
+
def header(chksum)
|
154
|
+
# struct tarfile_entry_posix {
|
155
|
+
# char name[100]; # ASCII + (Z unless filled)
|
156
|
+
# char mode[8]; # 0 padded, octal, null
|
157
|
+
# char uid[8]; # ditto
|
158
|
+
# char gid[8]; # ditto
|
159
|
+
# char size[12]; # 0 padded, octal, null
|
160
|
+
# char mtime[12]; # 0 padded, octal, null
|
161
|
+
# char checksum[8]; # 0 padded, octal, null, space
|
162
|
+
# char typeflag[1]; # file: "0" dir: "5"
|
163
|
+
# char linkname[100]; # ASCII + (Z unless filled)
|
164
|
+
# char magic[6]; # "ustar\0"
|
165
|
+
# char version[2]; # "00"
|
166
|
+
# char uname[32]; # ASCIIZ
|
167
|
+
# char gname[32]; # ASCIIZ
|
168
|
+
# char devmajor[8]; # 0 padded, octal, null
|
169
|
+
# char devminor[8]; # o padded, octal, null
|
170
|
+
# char prefix[155]; # ASCII + (Z unless filled)
|
171
|
+
# };
|
172
|
+
arr = [name, oct(mode, 7), oct(uid, 7), oct(gid, 7), oct(size, 11),
|
173
|
+
oct(mtime, 11), chksum, " ", typeflag, linkname, magic, version,
|
174
|
+
uname, gname, oct(devmajor, 7), oct(devminor, 7), prefix]
|
175
|
+
str = arr.pack("a100a8a8a8a12a12" + # name, mode, uid, gid, size, mtime
|
176
|
+
"a7aaa100a6a2" + # chksum, typeflag, linkname, magic, version
|
177
|
+
"a32a32a8a8a155") # uname, gname, devmajor, devminor, prefix
|
178
|
+
str + "\0" * ((512 - str.size) % 512)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
class TarWriter
|
183
|
+
class FileOverflow < StandardError; end
|
184
|
+
class BlockNeeded < StandardError; end
|
185
|
+
|
186
|
+
class BoundedStream
|
187
|
+
attr_reader :limit, :written
|
188
|
+
def initialize(io, limit)
|
189
|
+
@io = io
|
190
|
+
@limit = limit
|
191
|
+
@written = 0
|
192
|
+
end
|
193
|
+
|
194
|
+
def write(data)
|
195
|
+
if data.size + @written > @limit
|
196
|
+
raise FileOverflow,
|
197
|
+
"You tried to feed more data than fits in the file."
|
198
|
+
end
|
199
|
+
@io.write data
|
200
|
+
@written += data.size
|
201
|
+
data.size
|
202
|
+
end
|
203
|
+
end
|
204
|
+
class RestrictedStream
|
205
|
+
def initialize(anIO)
|
206
|
+
@io = anIO
|
207
|
+
end
|
208
|
+
|
209
|
+
def write(data)
|
210
|
+
@io.write data
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def self.new(anIO)
|
215
|
+
writer = super(anIO)
|
216
|
+
return writer unless block_given?
|
217
|
+
begin
|
218
|
+
yield writer
|
219
|
+
ensure
|
220
|
+
writer.close
|
221
|
+
end
|
222
|
+
nil
|
223
|
+
end
|
224
|
+
|
225
|
+
def initialize(anIO)
|
226
|
+
@io = anIO
|
227
|
+
@closed = false
|
228
|
+
end
|
229
|
+
|
230
|
+
def add_file_simple(name, mode, size)
|
231
|
+
raise BlockNeeded unless block_given?
|
232
|
+
raise ClosedIO if @closed
|
233
|
+
name, prefix = split_name(name)
|
234
|
+
header = TarHeader.new(:name => name, :mode => mode,
|
235
|
+
:size => size, :prefix => prefix).to_s
|
236
|
+
@io.write header
|
237
|
+
os = BoundedStream.new(@io, size)
|
238
|
+
yield os
|
239
|
+
#FIXME: what if an exception is raised in the block?
|
240
|
+
min_padding = size - os.written
|
241
|
+
@io.write("\0" * min_padding)
|
242
|
+
remainder = (512 - (size % 512)) % 512
|
243
|
+
@io.write("\0" * remainder)
|
244
|
+
end
|
245
|
+
|
246
|
+
def add_file(name, mode)
|
247
|
+
raise BlockNeeded unless block_given?
|
248
|
+
raise ClosedIO if @closed
|
249
|
+
raise NonSeekableIO unless @io.respond_to? :pos=
|
250
|
+
name, prefix = split_name(name)
|
251
|
+
init_pos = @io.pos
|
252
|
+
@io.write "\0" * 512 # placeholder for the header
|
253
|
+
yield RestrictedStream.new(@io)
|
254
|
+
#FIXME: what if an exception is raised in the block?
|
255
|
+
#FIXME: what if an exception is raised in the block?
|
256
|
+
size = @io.pos - init_pos - 512
|
257
|
+
remainder = (512 - (size % 512)) % 512
|
258
|
+
@io.write("\0" * remainder)
|
259
|
+
final_pos = @io.pos
|
260
|
+
@io.pos = init_pos
|
261
|
+
header = TarHeader.new(:name => name, :mode => mode,
|
262
|
+
:size => size, :prefix => prefix).to_s
|
263
|
+
@io.write header
|
264
|
+
@io.pos = final_pos
|
265
|
+
end
|
266
|
+
|
267
|
+
def mkdir(name, mode)
|
268
|
+
raise ClosedIO if @closed
|
269
|
+
name, prefix = split_name(name)
|
270
|
+
header = TarHeader.new(:name => name, :mode => mode, :typeflag => "5",
|
271
|
+
:size => 0, :prefix => prefix).to_s
|
272
|
+
@io.write header
|
273
|
+
nil
|
274
|
+
end
|
275
|
+
|
276
|
+
def flush
|
277
|
+
raise ClosedIO if @closed
|
278
|
+
@io.flush if @io.respond_to? :flush
|
279
|
+
end
|
280
|
+
|
281
|
+
def close
|
282
|
+
#raise ClosedIO if @closed
|
283
|
+
return if @closed
|
284
|
+
@io.write "\0" * 1024
|
285
|
+
@closed = true
|
286
|
+
end
|
287
|
+
|
288
|
+
private
|
289
|
+
def split_name name
|
290
|
+
raise TooLongFileName if name.size > 256
|
291
|
+
if name.size <= 100
|
292
|
+
prefix = ""
|
293
|
+
else
|
294
|
+
parts = name.split(/\//)
|
295
|
+
newname = parts.pop
|
296
|
+
nxt = ""
|
297
|
+
loop do
|
298
|
+
nxt = parts.pop
|
299
|
+
break if newname.size + 1 + nxt.size > 100
|
300
|
+
newname = nxt + "/" + newname
|
301
|
+
end
|
302
|
+
prefix = (parts + [nxt]).join "/"
|
303
|
+
name = newname
|
304
|
+
raise TooLongFileName if name.size > 100 || prefix.size > 155
|
305
|
+
end
|
306
|
+
return name, prefix
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
class TarReader
|
311
|
+
include Gem::Package
|
312
|
+
class UnexpectedEOF < StandardError; end
|
313
|
+
module InvalidEntry
|
314
|
+
def read(len=nil); raise ClosedIO; end
|
315
|
+
def getc; raise ClosedIO; end
|
316
|
+
def rewind; raise ClosedIO; end
|
317
|
+
end
|
318
|
+
class Entry
|
319
|
+
TarHeader::FIELDS.each{|x| attr_reader x}
|
320
|
+
|
321
|
+
def initialize(header, anIO)
|
322
|
+
@io = anIO
|
323
|
+
@name = header.name
|
324
|
+
@mode = header.mode
|
325
|
+
@uid = header.uid
|
326
|
+
@gid = header.gid
|
327
|
+
@size = header.size
|
328
|
+
@mtime = header.mtime
|
329
|
+
@checksum = header.checksum
|
330
|
+
@typeflag = header.typeflag
|
331
|
+
@linkname = header.linkname
|
332
|
+
@magic = header.magic
|
333
|
+
@version = header.version
|
334
|
+
@uname = header.uname
|
335
|
+
@gname = header.gname
|
336
|
+
@devmajor = header.devmajor
|
337
|
+
@devminor = header.devminor
|
338
|
+
@prefix = header.prefix
|
339
|
+
@read = 0
|
340
|
+
@orig_pos = @io.pos
|
341
|
+
end
|
342
|
+
|
343
|
+
def read(len = nil)
|
344
|
+
return nil if @read >= @size
|
345
|
+
len ||= @size - @read
|
346
|
+
max_read = [len, @size - @read].min
|
347
|
+
ret = @io.read(max_read)
|
348
|
+
@read += ret.size
|
349
|
+
ret
|
350
|
+
end
|
351
|
+
|
352
|
+
def getc
|
353
|
+
return nil if @read >= @size
|
354
|
+
ret = @io.getc
|
355
|
+
@read += 1 if ret
|
356
|
+
ret
|
357
|
+
end
|
358
|
+
|
359
|
+
def is_directory?
|
360
|
+
@typeflag == "5"
|
361
|
+
end
|
362
|
+
|
363
|
+
def is_file?
|
364
|
+
@typeflag == "0"
|
365
|
+
end
|
366
|
+
|
367
|
+
def eof?
|
368
|
+
@read >= @size
|
369
|
+
end
|
370
|
+
|
371
|
+
def pos
|
372
|
+
@read
|
373
|
+
end
|
374
|
+
|
375
|
+
def rewind
|
376
|
+
raise NonSeekableIO unless @io.respond_to? :pos=
|
377
|
+
@io.pos = @orig_pos
|
378
|
+
@read = 0
|
379
|
+
end
|
380
|
+
|
381
|
+
alias_method :is_directory, :is_directory?
|
382
|
+
alias_method :is_file, :is_file
|
383
|
+
|
384
|
+
def bytes_read
|
385
|
+
@read
|
386
|
+
end
|
387
|
+
|
388
|
+
def full_name
|
389
|
+
if @prefix != ""
|
390
|
+
File.join(@prefix, @name)
|
391
|
+
else
|
392
|
+
@name
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
def close
|
397
|
+
invalidate
|
398
|
+
end
|
399
|
+
|
400
|
+
private
|
401
|
+
def invalidate
|
402
|
+
extend InvalidEntry
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
def self.new(anIO)
|
407
|
+
reader = super(anIO)
|
408
|
+
return reader unless block_given?
|
409
|
+
begin
|
410
|
+
yield reader
|
411
|
+
ensure
|
412
|
+
reader.close
|
413
|
+
end
|
414
|
+
nil
|
415
|
+
end
|
416
|
+
|
417
|
+
def initialize(anIO)
|
418
|
+
@io = anIO
|
419
|
+
@init_pos = anIO.pos
|
420
|
+
end
|
421
|
+
|
422
|
+
def each(&block)
|
423
|
+
each_entry(&block)
|
424
|
+
end
|
425
|
+
|
426
|
+
# do not call this during a #each or #each_entry iteration
|
427
|
+
def rewind
|
428
|
+
if @init_pos == 0
|
429
|
+
raise NonSeekableIO unless @io.respond_to? :rewind
|
430
|
+
@io.rewind
|
431
|
+
else
|
432
|
+
raise NonSeekableIO unless @io.respond_to? :pos=
|
433
|
+
@io.pos = @init_pos
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
def each_entry
|
438
|
+
loop do
|
439
|
+
return if @io.eof?
|
440
|
+
header = TarHeader.new_from_stream(@io)
|
441
|
+
return if header.empty?
|
442
|
+
entry = Entry.new header, @io
|
443
|
+
size = entry.size
|
444
|
+
yield entry
|
445
|
+
skip = (512 - (size % 512)) % 512
|
446
|
+
if @io.respond_to? :seek
|
447
|
+
# avoid reading...
|
448
|
+
@io.seek(size - entry.bytes_read, IO::SEEK_CUR)
|
449
|
+
else
|
450
|
+
pending = size - entry.bytes_read
|
451
|
+
while pending > 0
|
452
|
+
bread = @io.read([pending, 4096].min).size
|
453
|
+
raise UnexpectedEOF if @io.eof?
|
454
|
+
pending -= bread
|
455
|
+
end
|
456
|
+
end
|
457
|
+
@io.read(skip) # discard trailing zeros
|
458
|
+
# make sure nobody can use #read, #getc or #rewind anymore
|
459
|
+
entry.close
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
def close
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
class TarInput
|
468
|
+
include FSyncDir
|
469
|
+
include Enumerable
|
470
|
+
attr_reader :metadata
|
471
|
+
class << self; private :new end
|
472
|
+
|
473
|
+
def initialize(io)
|
474
|
+
@io = io
|
475
|
+
@tarreader = TarReader.new @io
|
476
|
+
has_meta = false
|
477
|
+
@tarreader.each do |entry|
|
478
|
+
case entry.full_name
|
479
|
+
when "metadata"
|
480
|
+
# (GS) Changed to line below: @metadata = YAML.load(entry.read) rescue nil
|
481
|
+
@metadata = load_gemspec(entry.read)
|
482
|
+
has_meta = true
|
483
|
+
break
|
484
|
+
when "metadata.gz"
|
485
|
+
begin
|
486
|
+
gzis = Zlib::GzipReader.new entry
|
487
|
+
# YAML wants an instance of IO
|
488
|
+
# (GS) Changed to line below: @metadata = YAML.load(gzis) rescue nil
|
489
|
+
@metadata = load_gemspec(gzis)
|
490
|
+
has_meta = true
|
491
|
+
ensure
|
492
|
+
gzis.close
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|
496
|
+
@tarreader.rewind
|
497
|
+
@fileops = FileOperations.new
|
498
|
+
raise RuntimeError, "No metadata found!" unless has_meta
|
499
|
+
end
|
500
|
+
|
501
|
+
# Attempt to YAML-load a gemspec from the given _io_ parameter. Return nil if it fails.
|
502
|
+
def load_gemspec(io)
|
503
|
+
Gem::Specification.from_yaml(io)
|
504
|
+
rescue Gem::Exception
|
505
|
+
nil
|
506
|
+
end
|
507
|
+
|
508
|
+
def self.open(filename, &block)
|
509
|
+
open_from_io(File.open(filename, "rb"), &block)
|
510
|
+
end
|
511
|
+
|
512
|
+
def self.open_from_io(io, &block)
|
513
|
+
raise "Want a block" unless block_given?
|
514
|
+
begin
|
515
|
+
is = new(io)
|
516
|
+
yield is
|
517
|
+
ensure
|
518
|
+
is.close if is
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
def each(&block)
|
523
|
+
@tarreader.each do |entry|
|
524
|
+
next unless entry.full_name == "data.tar.gz"
|
525
|
+
is = zipped_stream(entry)
|
526
|
+
begin
|
527
|
+
TarReader.new(is) do |inner|
|
528
|
+
inner.each(&block)
|
529
|
+
end
|
530
|
+
ensure
|
531
|
+
is.close if is
|
532
|
+
end
|
533
|
+
end
|
534
|
+
@tarreader.rewind
|
535
|
+
end
|
536
|
+
|
537
|
+
# Return an IO stream for the zipped entry.
|
538
|
+
#
|
539
|
+
# If zlib is earlier than 1.2.1, then read the entry into memory
|
540
|
+
# and create a string IO object from it. This avoids a "buffer
|
541
|
+
# error" problem on windows when using an earlier version of zlib.
|
542
|
+
# This problem has not been observed in versions of zlib 1.2.1 or
|
543
|
+
# later.
|
544
|
+
def zipped_stream(entry)
|
545
|
+
if Zlib::ZLIB_VERSION < '1.2.1'
|
546
|
+
zis = Zlib::GzipReader.new entry
|
547
|
+
dis = zis.read
|
548
|
+
is = StringIO.new(dis)
|
549
|
+
else
|
550
|
+
is = Zlib::GzipReader.new entry
|
551
|
+
end
|
552
|
+
ensure
|
553
|
+
zis.finish if zis
|
554
|
+
end
|
555
|
+
|
556
|
+
def extract_entry(destdir, entry, expected_md5sum = nil)
|
557
|
+
if entry.is_directory?
|
558
|
+
dest = File.join(destdir, entry.full_name)
|
559
|
+
if file_class.dir? dest
|
560
|
+
@fileops.chmod entry.mode, dest, :verbose=>false
|
561
|
+
else
|
562
|
+
@fileops.mkdir_p(dest, :mode => entry.mode, :verbose=>false)
|
563
|
+
end
|
564
|
+
fsync_dir dest
|
565
|
+
fsync_dir File.join(dest, "..")
|
566
|
+
return
|
567
|
+
end
|
568
|
+
# it's a file
|
569
|
+
md5 = Digest::MD5.new if expected_md5sum
|
570
|
+
destdir = File.join(destdir, File.dirname(entry.full_name))
|
571
|
+
@fileops.mkdir_p(destdir, :mode => 0755, :verbose=>false)
|
572
|
+
destfile = File.join(destdir, File.basename(entry.full_name))
|
573
|
+
@fileops.chmod(0600, destfile, :verbose=>false) rescue nil # Errno::ENOENT
|
574
|
+
file_class.open(destfile, "wb", entry.mode) do |os|
|
575
|
+
loop do
|
576
|
+
data = entry.read(4096)
|
577
|
+
break unless data
|
578
|
+
md5 << data if expected_md5sum
|
579
|
+
os.write(data)
|
580
|
+
end
|
581
|
+
os.fsync
|
582
|
+
end
|
583
|
+
@fileops.chmod(entry.mode, destfile, :verbose=>false)
|
584
|
+
fsync_dir File.dirname(destfile)
|
585
|
+
fsync_dir File.join(File.dirname(destfile), "..")
|
586
|
+
if expected_md5sum && expected_md5sum != md5.hexdigest
|
587
|
+
raise BadCheckSum
|
588
|
+
end
|
589
|
+
end
|
590
|
+
|
591
|
+
def close
|
592
|
+
@io.close
|
593
|
+
@tarreader.close
|
594
|
+
end
|
595
|
+
|
596
|
+
private
|
597
|
+
|
598
|
+
def file_class
|
599
|
+
File
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
class TarOutput
|
604
|
+
|
605
|
+
class << self; private :new end
|
606
|
+
|
607
|
+
class << self
|
608
|
+
|
609
|
+
end
|
610
|
+
|
611
|
+
def initialize(io)
|
612
|
+
@io = io
|
613
|
+
@external = TarWriter.new @io
|
614
|
+
end
|
615
|
+
|
616
|
+
def external_handle
|
617
|
+
@external
|
618
|
+
end
|
619
|
+
|
620
|
+
def self.open(filename, &block)
|
621
|
+
io = File.open(filename, "wb")
|
622
|
+
open_from_io(io, &block)
|
623
|
+
nil
|
624
|
+
end
|
625
|
+
|
626
|
+
def self.open_from_io(io, &block)
|
627
|
+
outputter = new(io)
|
628
|
+
metadata = nil
|
629
|
+
set_meta = lambda{|x| metadata = x}
|
630
|
+
raise "Want a block" unless block_given?
|
631
|
+
begin
|
632
|
+
outputter.external_handle.add_file("data.tar.gz", 0644) do |inner|
|
633
|
+
begin
|
634
|
+
os = Zlib::GzipWriter.new inner
|
635
|
+
TarWriter.new(os) do |inner_tar_stream|
|
636
|
+
klass = class <<inner_tar_stream; self end
|
637
|
+
klass.send(:define_method, :metadata=, &set_meta)
|
638
|
+
block.call inner_tar_stream
|
639
|
+
end
|
640
|
+
ensure
|
641
|
+
os.flush
|
642
|
+
os.finish
|
643
|
+
#os.close
|
644
|
+
end
|
645
|
+
end
|
646
|
+
outputter.external_handle.add_file("metadata.gz", 0644) do |os|
|
647
|
+
begin
|
648
|
+
gzos = Zlib::GzipWriter.new os
|
649
|
+
gzos.write metadata
|
650
|
+
ensure
|
651
|
+
gzos.flush
|
652
|
+
gzos.finish
|
653
|
+
end
|
654
|
+
end
|
655
|
+
ensure
|
656
|
+
outputter.close
|
657
|
+
end
|
658
|
+
nil
|
659
|
+
end
|
660
|
+
|
661
|
+
def close
|
662
|
+
@external.close
|
663
|
+
@io.close
|
664
|
+
end
|
665
|
+
end
|
666
|
+
end # module Package
|
667
|
+
|
668
|
+
|
669
|
+
module Package
|
670
|
+
#FIXME: refactor the following 2 methods
|
671
|
+
def self.open(dest, mode = "r", &block)
|
672
|
+
raise "Block needed" unless block_given?
|
673
|
+
|
674
|
+
case mode
|
675
|
+
when "r"
|
676
|
+
TarInput.open(dest, &block)
|
677
|
+
when "w"
|
678
|
+
TarOutput.open(dest, &block)
|
679
|
+
else
|
680
|
+
raise "Unknown Package open mode"
|
681
|
+
end
|
682
|
+
end
|
683
|
+
|
684
|
+
def self.open_from_io(io, mode = "r", &block)
|
685
|
+
raise "Block needed" unless block_given?
|
686
|
+
|
687
|
+
case mode
|
688
|
+
when "r"
|
689
|
+
TarInput.open_from_io(io, &block)
|
690
|
+
when "w"
|
691
|
+
TarOutput.open_from_io(io, &block)
|
692
|
+
else
|
693
|
+
raise "Unknown Package open mode"
|
694
|
+
end
|
695
|
+
end
|
696
|
+
|
697
|
+
def self.pack(src, destname)
|
698
|
+
TarOutput.open(destname) do |outp|
|
699
|
+
dir_class.chdir(src) do
|
700
|
+
outp.metadata = (file_class.read("RPA/metadata") rescue nil)
|
701
|
+
find_class.find('.') do |entry|
|
702
|
+
case
|
703
|
+
when file_class.file?(entry)
|
704
|
+
entry.sub!(%r{\./}, "")
|
705
|
+
next if entry =~ /\ARPA\//
|
706
|
+
stat = File.stat(entry)
|
707
|
+
outp.add_file_simple(entry, stat.mode, stat.size) do |os|
|
708
|
+
file_class.open(entry, "rb") do |f|
|
709
|
+
os.write(f.read(4096)) until f.eof?
|
710
|
+
end
|
711
|
+
end
|
712
|
+
when file_class.dir?(entry)
|
713
|
+
entry.sub!(%r{\./}, "")
|
714
|
+
next if entry == "RPA"
|
715
|
+
outp.mkdir(entry, file_class.stat(entry).mode)
|
716
|
+
else
|
717
|
+
raise "Don't know how to pack this yet!"
|
718
|
+
end
|
719
|
+
end
|
720
|
+
end
|
721
|
+
end
|
722
|
+
end
|
723
|
+
|
724
|
+
class << self
|
725
|
+
def file_class
|
726
|
+
File
|
727
|
+
end
|
728
|
+
|
729
|
+
def dir_class
|
730
|
+
Dir
|
731
|
+
end
|
732
|
+
|
733
|
+
def find_class
|
734
|
+
Find
|
735
|
+
end
|
736
|
+
end
|
737
|
+
end
|
738
|
+
|
739
|
+
end
|
740
|
+
|