-
Notifications
You must be signed in to change notification settings - Fork 81
/
remote_file.rb
182 lines (158 loc) · 4.99 KB
/
remote_file.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
require 'fileutils'
require 'uri'
require 'zlib'
module Pod
module Downloader
class RemoteFile < Base
def self.options
[:type, :flatten, :sha1, :sha256, :headers]
end
class UnsupportedFileTypeError < StandardError; end
private
executable :unzip
executable :tar
executable :hdiutil
attr_accessor :filename, :download_path
def download!
@filename = filename_with_type(type)
@download_path = target_path + @filename
download_file(@download_path)
verify_checksum(@download_path)
extract_with_type(@download_path, type)
end
def type
if options[:type]
options[:type].to_sym
else
type_with_url(url)
end
end
def headers
options[:headers]
end
# @note The archive is flattened if it contains only one folder and its
# extension is either `tgz`, `tar`, `tbz` or the options specify
# it.
#
# @return [Bool] Whether the archive should be flattened if it contains
# only one folder.
#
def should_flatten?
if options.key?(:flatten)
options[:flatten]
elsif [:tgz, :tar, :tbz, :txz].include?(type)
true # those archives flatten by default
else
false # all others (actually only .zip) default not to flatten
end
end
def type_with_url(url)
case URI.parse(url).path
when /\.zip$/
:zip
when /\.(tgz|tar\.gz)$/
:tgz
when /\.tar$/
:tar
when /\.(tbz|tar\.bz2)$/
:tbz
when /\.(txz|tar\.xz)$/
:txz
when /\.dmg$/
:dmg
end
end
def filename_with_type(type = :zip)
case type
when :zip, :tgz, :tar, :tbz, :txz, :dmg
"file.#{type}"
else
raise UnsupportedFileTypeError, "Unsupported file type: #{type}"
end
end
def download_file(_full_filename)
raise NotImplementedError
end
def extract_with_type(full_filename, type = :zip)
unpack_from = full_filename
unpack_to = @target_path
case type
when :zip
unzip! unpack_from, '-d', unpack_to
when :tgz
tar! 'xfz', unpack_from, '-C', unpack_to
when :tar
tar! 'xf', unpack_from, '-C', unpack_to
when :tbz
tar! 'xfj', unpack_from, '-C', unpack_to
when :txz
tar! 'xf', unpack_from, '-C', unpack_to
when :dmg
extract_dmg(unpack_from, unpack_to)
else
raise UnsupportedFileTypeError, "Unsupported file type: #{type}"
end
# If the archive is a tarball and it only contained a folder, move its
# contents to the target (#727)
#
if should_flatten?
contents = target_path.children
contents.delete(target_path + @filename)
entry = contents.first
if contents.count == 1 && entry.directory?
tmp_entry = entry.sub_ext("#{entry.extname}.tmp")
begin
FileUtils.move(entry, tmp_entry)
FileUtils.move(tmp_entry.children, target_path)
ensure
FileUtils.remove_entry(tmp_entry)
end
end
end
FileUtils.rm(unpack_from) if File.exist?(unpack_from)
end
def extract_dmg(unpack_from, unpack_to)
require 'rexml/document'
plist_s = hdiutil! 'attach', '-plist', '-nobrowse', unpack_from, '-mountrandom', unpack_to
plist = REXML::Document.new plist_s
xpath = '//key[.="mount-point"]/following-sibling::string'
mount_point = REXML::XPath.first(plist, xpath).text
FileUtils.cp_r(Dir.glob(mount_point + '/*'), unpack_to)
hdiutil! 'detach', mount_point
end
def compare_hash(filename, hasher, hash)
incremental_hash = hasher.new
File.open(filename, 'rb') do |file|
buf = ''
incremental_hash << buf while file.read(1024, buf)
end
computed_hash = incremental_hash.hexdigest
if computed_hash != hash
raise DownloaderError, 'Verification checksum was incorrect, ' \
"expected #{hash}, got #{computed_hash}"
end
end
# Verify that the downloaded file matches a sha1 hash
#
def verify_sha1_hash(filename, hash)
require 'digest/sha1'
compare_hash(filename, Digest::SHA1, hash)
end
# Verify that the downloaded file matches a sha256 hash
#
def verify_sha256_hash(filename, hash)
require 'digest/sha2'
compare_hash(filename, Digest::SHA2, hash)
end
# Verify that the downloaded file matches the hash if set
#
def verify_checksum(filename)
if options[:sha256]
verify_sha256_hash(filename, options[:sha256])
elsif options[:sha1]
verify_sha1_hash(filename, options[:sha1])
end
end
end
end
end