/
remote_installer.rb
182 lines (167 loc) · 5.9 KB
/
remote_installer.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
module Gem
class DependencyError < Gem::Exception; end
class RemoteSourceException < Gem::Exception; end
class GemNotFoundException < Gem::Exception; end
class RemoteInstaller
##
# <tt>http_proxy</tt>::
# * [String]: explicit specification of proxy; overrides any environment variable
# setting
# * nil: respect environment variables
# * <tt>:no_proxy</tt>: ignore environment variables and _don't_ use a proxy
#
def initialize(http_proxy=nil)
# Ensure http_proxy env vars are used if no proxy explicitly supplied.
@http_proxy =
case http_proxy
when :no_proxy
false
when nil
true
else
http_proxy.to_str
end
end
##
# This method will install package_name onto the local system.
# package_name:: [String] Name of the Gem to install
# version_requirement:: [default = "> 0.0.0"] Gem version requirement to install
#
# Returns: an array of Gem::Specification objects, one for each gem installed.
#
def install(package_name, version_requirement = "> 0.0.0", force=false, install_dir=Gem.dir,
install_stub=true)
unless version_requirement.respond_to?(:version)
version_requirement = Version::Requirement.new(version_requirement)
end
installed_gems = []
sources = get_cache_sources()
caches = get_caches(sources)
spec, source = find_latest_valid_package_in_caches(package_name,version_requirement,caches)
dependencies = find_dependencies_not_installed(spec.dependencies)
installed_gems << install_dependencies(dependencies)
cache_dir = File.join(Gem::dir, "cache")
destination_file = File.join(cache_dir, spec.full_name + ".gem")
download_gem(destination_file, source, spec)
installer = new_installer(destination_file)
installed_gems.unshift installer.install(force, install_dir, install_stub)
installed_gems.flatten
end
##
# Search Gem repository for a gem by specifying all of part of
# the Gem's name
def search(pattern_to_match)
#TODO - move all of this logic into cache.rb
#Gem::Cache.new(caches).search(pattern_to_match)
items = []
pattern_to_match = /#{ pattern_to_match }/i if String === pattern_to_match
sources = get_cache_sources()
caches = get_caches(sources)
caches.sort.each { |key,value|
value.each do |entry|
if entry[0] =~ pattern_to_match
items.push entry
end
end
}
items
end
##
# Return a list of the sources that we can download gems from
def get_cache_sources
require_gem("sources")
Gem.sources
end
##
# Given a list of sources, return a hash of all the caches from those sources, where the key is the source and the value is the cache.
def get_caches(sources)
require 'yaml'
caches = {}
sources.each do |source|
begin
begin
require 'zlib'
yaml_spec = fetch(source + "/yaml.Z")
yaml_spec = Zlib::Inflate.inflate(yaml_spec)
rescue
yaml_spec = nil
end
yaml_spec = fetch(source + "/yaml") unless yaml_spec
spec = YAML.load(yaml_spec)
raise "Didn't get a valid YAML document" if not spec
caches[source] = spec
rescue SocketError => e
raise RemoteSourceException.new("Error fetching remote gem cache: #{e.to_s}")
end
end
return caches
end
def find_latest_valid_package_in_caches(package_name,version_requirement,caches)
max_version = Version.new("0.0.0")
package = []
caches.each do |source, cache|
cache.each do |name, spec|
if (/#{package_name}/i === name &&
spec.version > max_version &&
version_requirement.satisfied_by?(spec.version)) then
package = [spec, source]
max_version = spec.version
end
end
end
raise GemNotFoundException.new("Could not find #{package_name} (#{version_requirement}) in the repository") unless max_version > Version.new("0.0.0")
package
end
def find_dependencies_not_installed(dependencies)
to_install = []
dependencies.each do |dependency|
begin
require_gem(dependency.name, dependency.version_requirement.version)
rescue LoadError => e
to_install.push dependency
end
end
to_install
end
#
# Install all the given dependencies. Returns an array of Gem::Specification objects, one
# for each dependency installed.
#
# TODO: For now, we recursively install, but this is not the right way to do things (e.g.
# if a package fails to download, we shouldn't install anything).
def install_dependencies(dependencies)
installed_gems = []
dependencies.each do |dependency|
print "Install required dependency #{dependency.name}? [Yn] "
answer = STDIN.gets
if(answer =~ /^y/i || answer =~ /^[^a-zA-Z0-9]$/) then
remote_installer = RemoteInstaller.new
installed_gems << remote_installer.install(dependency.name, dependency.version_requirement)
else
raise DependencyError.new("Required dependency #{dependency.name} not installed")
end
end
installed_gems
end
def download_gem(destination_file, source, spec)
# TODO
uri = source + "/gems/#{spec.full_name}.gem"
response = fetch(uri)
write_gem_to_file(response, destination_file)
end
def write_gem_to_file(body, destination_file)
File.open(destination_file, 'w') do |out|
out.write(body)
end
end
def fetch( uri_str )
require 'open-uri'
open(uri_str, :proxy => @http_proxy) do |input|
input.read
end
end
def new_installer(gem)
return Installer.new(gem)
end
end
end