bundler 1.9.10 → 1.10.0.pre
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bundler might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/CHANGELOG.md +21 -13
- data/Rakefile +2 -2
- data/bin/bundle_ruby +2 -0
- data/bin/bundler +1 -1
- data/lib/bundler.rb +6 -10
- data/lib/bundler/cli.rb +23 -1
- data/lib/bundler/cli/gem.rb +5 -2
- data/lib/bundler/cli/install.rb +37 -5
- data/lib/bundler/cli/lock.rb +36 -0
- data/lib/bundler/cli/outdated.rb +9 -2
- data/lib/bundler/definition.rb +22 -7
- data/lib/bundler/dependency.rb +7 -6
- data/lib/bundler/deployment.rb +3 -0
- data/lib/bundler/dsl.rb +172 -39
- data/lib/bundler/endpoint_specification.rb +1 -1
- data/lib/bundler/fetcher.rb +90 -252
- data/lib/bundler/fetcher/base.rb +27 -0
- data/lib/bundler/fetcher/dependency.rb +88 -0
- data/lib/bundler/fetcher/downloader.rb +61 -0
- data/lib/bundler/fetcher/index.rb +31 -0
- data/lib/bundler/friendly_errors.rb +3 -0
- data/lib/bundler/inline.rb +50 -0
- data/lib/bundler/installer.rb +15 -60
- data/lib/bundler/installer/parallel_installer.rb +117 -0
- data/lib/bundler/lazy_specification.rb +1 -1
- data/lib/bundler/lockfile_parser.rb +26 -10
- data/lib/bundler/remote_specification.rb +21 -1
- data/lib/bundler/resolver.rb +2 -1
- data/lib/bundler/retry.rb +11 -10
- data/lib/bundler/rubygems_ext.rb +1 -1
- data/lib/bundler/rubygems_integration.rb +33 -6
- data/lib/bundler/settings.rb +58 -14
- data/lib/bundler/shared_helpers.rb +6 -3
- data/lib/bundler/source.rb +0 -10
- data/lib/bundler/source/git.rb +2 -2
- data/lib/bundler/source/path.rb +1 -1
- data/lib/bundler/source/path/installer.rb +8 -11
- data/lib/bundler/source/rubygems.rb +46 -16
- data/lib/bundler/source/rubygems/remote.rb +39 -0
- data/lib/bundler/templates/newgem/.travis.yml.tt +1 -0
- data/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt +2 -2
- data/lib/bundler/templates/newgem/Rakefile.tt +2 -0
- data/lib/bundler/templates/newgem/test/{test_newgem.rb.tt → newgem_test.rb.tt} +2 -2
- data/lib/bundler/templates/newgem/test/{minitest_helper.rb.tt → test_helper.rb.tt} +0 -0
- data/lib/bundler/version.rb +1 -1
- data/man/bundle-config.ronn +7 -0
- data/man/bundle-install.ronn +9 -0
- data/man/bundle.ronn +3 -3
- data/man/gemfile.5.ronn +9 -5
- metadata +13 -8
- data/UPGRADING.md +0 -103
- data/lib/bundler/anonymizable_uri.rb +0 -32
@@ -0,0 +1,27 @@
|
|
1
|
+
module Bundler
|
2
|
+
class Fetcher
|
3
|
+
class Base
|
4
|
+
attr_reader :downloader
|
5
|
+
attr_reader :remote_uri
|
6
|
+
attr_reader :fetch_uri
|
7
|
+
attr_reader :display_uri
|
8
|
+
|
9
|
+
def initialize(downloader, remote_uri, fetch_uri, display_uri)
|
10
|
+
raise 'Abstract class' if self.class == Base
|
11
|
+
@downloader = downloader
|
12
|
+
@remote_uri = remote_uri
|
13
|
+
@fetch_uri = fetch_uri
|
14
|
+
@display_uri = display_uri
|
15
|
+
end
|
16
|
+
|
17
|
+
def api_available?
|
18
|
+
api_fetcher?
|
19
|
+
end
|
20
|
+
|
21
|
+
def api_fetcher?
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'bundler/fetcher/base'
|
2
|
+
|
3
|
+
module Bundler
|
4
|
+
class Fetcher
|
5
|
+
class Dependency < Base
|
6
|
+
def api_available?
|
7
|
+
downloader.fetch(dependency_api_uri)
|
8
|
+
rescue NetworkDownError => e
|
9
|
+
raise HTTPError, e.message
|
10
|
+
rescue AuthenticationRequiredError
|
11
|
+
# We got a 401 from the server. Just fail.
|
12
|
+
raise
|
13
|
+
rescue HTTPError
|
14
|
+
end
|
15
|
+
|
16
|
+
def api_fetcher?
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
def specs(gem_names, full_dependency_list = [], last_spec_list = [])
|
21
|
+
query_list = gem_names - full_dependency_list
|
22
|
+
|
23
|
+
# only display the message on the first run
|
24
|
+
if Bundler.ui.debug?
|
25
|
+
Bundler.ui.debug "Query List: #{query_list.inspect}"
|
26
|
+
else
|
27
|
+
Bundler.ui.info ".", false
|
28
|
+
end
|
29
|
+
|
30
|
+
return {remote_uri => last_spec_list} if query_list.empty?
|
31
|
+
|
32
|
+
remote_specs = Bundler::Retry.new("dependency api", AUTH_ERRORS).attempts do
|
33
|
+
dependency_specs(query_list)
|
34
|
+
end
|
35
|
+
|
36
|
+
spec_list, deps_list = remote_specs
|
37
|
+
returned_gems = spec_list.map(&:first).uniq
|
38
|
+
specs(deps_list, full_dependency_list + returned_gems, spec_list + last_spec_list)
|
39
|
+
rescue HTTPError, MarshalError, GemspecError
|
40
|
+
Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over
|
41
|
+
Bundler.ui.debug "could not fetch from the dependency API, trying the full index"
|
42
|
+
return nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def dependency_specs(gem_names)
|
46
|
+
Bundler.ui.debug "Query Gemcutter Dependency Endpoint API: #{gem_names.join(',')}"
|
47
|
+
gem_list = []
|
48
|
+
deps_list = []
|
49
|
+
|
50
|
+
gem_names.each_slice(Source::Rubygems::API_REQUEST_SIZE) do |names|
|
51
|
+
marshalled_deps = downloader.fetch dependency_api_uri(names)
|
52
|
+
gem_list += Bundler.load_marshal(marshalled_deps)
|
53
|
+
end
|
54
|
+
|
55
|
+
spec_list = gem_list.map do |s|
|
56
|
+
dependencies = s[:dependencies].map do |name, requirement|
|
57
|
+
dep = well_formed_dependency(name, requirement.split(", "))
|
58
|
+
deps_list << dep.name
|
59
|
+
dep
|
60
|
+
end
|
61
|
+
|
62
|
+
[s[:name], Gem::Version.new(s[:number]), s[:platform], dependencies]
|
63
|
+
end
|
64
|
+
|
65
|
+
[spec_list, deps_list.uniq]
|
66
|
+
end
|
67
|
+
|
68
|
+
def dependency_api_uri(gem_names = [])
|
69
|
+
uri = fetch_uri + "api/v1/dependencies"
|
70
|
+
uri.query = "gems=#{URI.encode(gem_names.join(","))}" if gem_names.any?
|
71
|
+
uri
|
72
|
+
end
|
73
|
+
|
74
|
+
def well_formed_dependency(name, *requirements)
|
75
|
+
Gem::Dependency.new(name, *requirements)
|
76
|
+
rescue ArgumentError => e
|
77
|
+
illformed = 'Ill-formed requirement ["#<YAML::Syck::DefaultKey'
|
78
|
+
raise e unless e.message.include?(illformed)
|
79
|
+
puts # we shouldn't print the error message on the "fetching info" status line
|
80
|
+
raise GemspecError,
|
81
|
+
"Unfortunately, the gem #{s[:name]} (#{s[:number]}) has an invalid " \
|
82
|
+
"gemspec. \nPlease ask the gem author to yank the bad version to fix " \
|
83
|
+
"this issue. For more information, see http://bit.ly/syck-defaultkey."
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Bundler
|
2
|
+
class Fetcher
|
3
|
+
class Downloader
|
4
|
+
attr_reader :connection
|
5
|
+
attr_reader :redirect_limit
|
6
|
+
|
7
|
+
def initialize(connection, redirect_limit)
|
8
|
+
@connection = connection
|
9
|
+
@redirect_limit = redirect_limit
|
10
|
+
end
|
11
|
+
|
12
|
+
def fetch(uri, counter = 0)
|
13
|
+
raise HTTPError, "Too many redirects" if counter >= redirect_limit
|
14
|
+
|
15
|
+
response = request(uri)
|
16
|
+
Bundler.ui.debug("HTTP #{response.code} #{response.message}")
|
17
|
+
|
18
|
+
case response
|
19
|
+
when Net::HTTPRedirection
|
20
|
+
new_uri = URI.parse(response["location"])
|
21
|
+
if new_uri.host == uri.host
|
22
|
+
new_uri.user = uri.user
|
23
|
+
new_uri.password = uri.password
|
24
|
+
end
|
25
|
+
fetch(new_uri, counter + 1)
|
26
|
+
when Net::HTTPSuccess
|
27
|
+
response.body
|
28
|
+
when Net::HTTPRequestEntityTooLarge
|
29
|
+
raise FallbackError, response.body
|
30
|
+
when Net::HTTPUnauthorized
|
31
|
+
raise AuthenticationRequiredError, uri.host
|
32
|
+
else
|
33
|
+
raise HTTPError, "#{response.class}: #{response.body}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def request(uri)
|
38
|
+
Bundler.ui.debug "HTTP GET #{uri}"
|
39
|
+
req = Net::HTTP::Get.new uri.request_uri
|
40
|
+
if uri.user
|
41
|
+
user = CGI.unescape(uri.user)
|
42
|
+
password = uri.password ? CGI.unescape(uri.password) : nil
|
43
|
+
req.basic_auth(user, password)
|
44
|
+
end
|
45
|
+
connection.request(uri, req)
|
46
|
+
rescue OpenSSL::SSL::SSLError
|
47
|
+
raise CertificateFailureError.new(uri)
|
48
|
+
rescue *HTTP_ERRORS => e
|
49
|
+
Bundler.ui.trace e
|
50
|
+
case e.message
|
51
|
+
when /host down:/, /getaddrinfo: nodename nor servname provided/
|
52
|
+
raise NetworkDownError, "Could not reach host #{uri.host}. Check your network " \
|
53
|
+
"connection and try again."
|
54
|
+
else
|
55
|
+
raise HTTPError, "Network error while fetching #{uri}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'bundler/fetcher/base'
|
2
|
+
|
3
|
+
module Bundler
|
4
|
+
class Fetcher
|
5
|
+
class Index < Base
|
6
|
+
def specs(_gem_names)
|
7
|
+
old_sources = Bundler.rubygems.sources
|
8
|
+
Bundler.rubygems.sources = [remote_uri.to_s]
|
9
|
+
Bundler.rubygems.fetch_all_remote_specs
|
10
|
+
rescue Gem::RemoteFetcher::FetchError, OpenSSL::SSL::SSLError => e
|
11
|
+
case e.message
|
12
|
+
when /certificate verify failed/
|
13
|
+
raise CertificateFailureError.new(display_uri)
|
14
|
+
when /401/
|
15
|
+
raise AuthenticationRequiredError, remote_uri
|
16
|
+
when /403/
|
17
|
+
if remote_uri.userinfo
|
18
|
+
raise BadAuthenticationError, remote_uri
|
19
|
+
else
|
20
|
+
raise AuthenticationRequiredError, remote_uri
|
21
|
+
end
|
22
|
+
else
|
23
|
+
Bundler.ui.trace e
|
24
|
+
raise HTTPError, "Could not fetch specs from #{display_uri}"
|
25
|
+
end
|
26
|
+
ensure
|
27
|
+
Bundler.rubygems.sources = old_sources
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -5,6 +5,9 @@ require "bundler/vendored_thor"
|
|
5
5
|
module Bundler
|
6
6
|
def self.with_friendly_errors
|
7
7
|
yield
|
8
|
+
rescue Bundler::Dsl::DSLError => e
|
9
|
+
Bundler.ui.error e.message
|
10
|
+
exit e.status_code
|
8
11
|
rescue Bundler::BundlerError => e
|
9
12
|
Bundler.ui.error e.message, :wrap => true
|
10
13
|
Bundler.ui.trace e
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# Allows for declaring a Gemfile inline in a ruby script, optionally installing
|
2
|
+
# any gems that aren't already installed on the user's system.
|
3
|
+
#
|
4
|
+
# @note Every gem that is specified in this 'Gemfile' will be `require`d, as if
|
5
|
+
# the user had manually called `Bundler.require`. To avoid a requested gem
|
6
|
+
# being automatically required, add the `:require => false` option to the
|
7
|
+
# `gem` dependency declaration.
|
8
|
+
#
|
9
|
+
# @param install [Boolean] whether gems that aren't already installed on the
|
10
|
+
# user's system should be installed.
|
11
|
+
# Defaults to `false`.
|
12
|
+
#
|
13
|
+
# @param gemfile [Proc] a block that is evaluated as a `Gemfile`.
|
14
|
+
#
|
15
|
+
# @example Using an inline Gemfile
|
16
|
+
#
|
17
|
+
# #!/usr/bin/env ruby
|
18
|
+
#
|
19
|
+
# require 'bundler/inline'
|
20
|
+
#
|
21
|
+
# gemfile do
|
22
|
+
# source 'https://rubygems.org'
|
23
|
+
# gem 'json', require: false
|
24
|
+
# gem 'nap', require: 'rest'
|
25
|
+
# gem 'cocoapods', '~> 0.34.1'
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# puts Pod::VERSION => "0.34.4"
|
29
|
+
#
|
30
|
+
def gemfile(install = false, &gemfile)
|
31
|
+
require 'bundler'
|
32
|
+
old_root = Bundler.method(:root)
|
33
|
+
def Bundler.root
|
34
|
+
Pathname.pwd.expand_path
|
35
|
+
end
|
36
|
+
ENV['BUNDLE_GEMFILE'] ||= 'Gemfile'
|
37
|
+
|
38
|
+
builder = Bundler::Dsl.new
|
39
|
+
builder.instance_eval(&gemfile)
|
40
|
+
definition = builder.to_definition(nil, true)
|
41
|
+
def definition.lock(file); end
|
42
|
+
definition.validate_ruby!
|
43
|
+
Bundler::Installer.install(Bundler.root, definition, :system => true) if install
|
44
|
+
runtime = Bundler::Runtime.new(nil, definition)
|
45
|
+
runtime.setup_environment
|
46
|
+
runtime.setup.require
|
47
|
+
|
48
|
+
bundler_module = class << Bundler; self; end
|
49
|
+
bundler_module.send(:define_method, :root, old_root)
|
50
|
+
end
|
data/lib/bundler/installer.rb
CHANGED
@@ -79,32 +79,38 @@ module Bundler
|
|
79
79
|
options["local"] ? @definition.resolve_with_cache! : @definition.resolve_remotely!
|
80
80
|
end
|
81
81
|
|
82
|
+
force = options["force"]
|
83
|
+
|
82
84
|
# the order that the resolver provides is significant, since
|
83
85
|
# dependencies might actually affect the installation of a gem.
|
84
86
|
# that said, it's a rare situation (other than rake), and parallel
|
85
87
|
# installation is just SO MUCH FASTER. so we let people opt in.
|
86
88
|
jobs = [Bundler.settings[:jobs].to_i-1, 1].max
|
87
89
|
if jobs > 1 && can_install_in_parallel?
|
88
|
-
|
90
|
+
require 'bundler/installer/parallel_installer'
|
91
|
+
install_in_parallel jobs, options[:standalone], force
|
89
92
|
else
|
90
|
-
install_sequentially options[:standalone]
|
93
|
+
install_sequentially options[:standalone], force
|
91
94
|
end
|
92
95
|
|
93
96
|
lock unless Bundler.settings[:frozen]
|
94
97
|
generate_standalone(options[:standalone]) if options[:standalone]
|
95
98
|
end
|
96
99
|
|
97
|
-
def install_gem_from_spec(spec, standalone = false, worker = 0)
|
100
|
+
def install_gem_from_spec(spec, standalone = false, worker = 0, force = false)
|
98
101
|
# Fetch the build settings, if there are any
|
99
102
|
settings = Bundler.settings["build.#{spec.name}"]
|
100
103
|
messages = nil
|
101
104
|
|
105
|
+
install_options = { :force => force, :ensure_builtin_gems_cached => standalone }
|
106
|
+
|
102
107
|
if settings
|
108
|
+
# Build arguments are global, so this is mutexed
|
103
109
|
Bundler.rubygems.with_build_args [settings] do
|
104
|
-
messages = spec.source.install(spec)
|
110
|
+
messages = spec.source.install(spec, install_options)
|
105
111
|
end
|
106
112
|
else
|
107
|
-
messages = spec.source.install(spec)
|
113
|
+
messages = spec.source.install(spec, install_options)
|
108
114
|
end
|
109
115
|
|
110
116
|
install_message, post_install_message, debug_message = *messages
|
@@ -264,68 +270,17 @@ module Bundler
|
|
264
270
|
end
|
265
271
|
end
|
266
272
|
|
267
|
-
def install_sequentially(standalone)
|
273
|
+
def install_sequentially(standalone, force = false)
|
268
274
|
specs.each do |spec|
|
269
|
-
message = install_gem_from_spec spec, standalone, 0
|
275
|
+
message = install_gem_from_spec spec, standalone, 0, force
|
270
276
|
if message
|
271
277
|
Installer.post_install_messages[spec.name] = message
|
272
278
|
end
|
273
279
|
end
|
274
280
|
end
|
275
281
|
|
276
|
-
def install_in_parallel(size, standalone)
|
277
|
-
|
278
|
-
remains = {}
|
279
|
-
enqueued = {}
|
280
|
-
specs.each do |spec|
|
281
|
-
name2spec[spec.name] = spec
|
282
|
-
remains[spec.name] = true
|
283
|
-
end
|
284
|
-
|
285
|
-
worker_pool = Worker.new size, lambda { |name, worker_num|
|
286
|
-
spec = name2spec[name]
|
287
|
-
message = install_gem_from_spec spec, standalone, worker_num
|
288
|
-
{ :name => spec.name, :post_install => message }
|
289
|
-
}
|
290
|
-
|
291
|
-
# Keys in the remains hash represent uninstalled gems specs.
|
292
|
-
# We enqueue all gem specs that do not have any dependencies.
|
293
|
-
# Later we call this lambda again to install specs that depended on
|
294
|
-
# previously installed specifications. We continue until all specs
|
295
|
-
# are installed.
|
296
|
-
enqueue_remaining_specs = lambda do
|
297
|
-
remains.keys.each do |name|
|
298
|
-
next if enqueued[name]
|
299
|
-
spec = name2spec[name]
|
300
|
-
if ready_to_install?(spec, remains)
|
301
|
-
worker_pool.enq name
|
302
|
-
enqueued[name] = true
|
303
|
-
end
|
304
|
-
end
|
305
|
-
end
|
306
|
-
enqueue_remaining_specs.call
|
307
|
-
|
308
|
-
until remains.empty?
|
309
|
-
message = worker_pool.deq
|
310
|
-
remains.delete message[:name]
|
311
|
-
if message[:post_install]
|
312
|
-
Installer.post_install_messages[message[:name]] = message[:post_install]
|
313
|
-
end
|
314
|
-
enqueue_remaining_specs.call
|
315
|
-
end
|
316
|
-
message
|
317
|
-
ensure
|
318
|
-
worker_pool && worker_pool.stop
|
319
|
-
end
|
320
|
-
|
321
|
-
# We only want to install a gem spec if all its dependencies are met.
|
322
|
-
# If the dependency is no longer in the `remains` hash then it has been met.
|
323
|
-
# If a dependency is only development or is self referential it can be ignored.
|
324
|
-
def ready_to_install?(spec, remains)
|
325
|
-
spec.dependencies.none? do |dep|
|
326
|
-
next if dep.type == :development || dep.name == spec.name
|
327
|
-
remains[dep.name]
|
328
|
-
end
|
282
|
+
def install_in_parallel(size, standalone, force = false)
|
283
|
+
ParallelInstaller.call(self, specs, size, standalone, force)
|
329
284
|
end
|
330
285
|
|
331
286
|
def create_bundle_path
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'bundler/worker'
|
2
|
+
|
3
|
+
|
4
|
+
class ParallelInstaller
|
5
|
+
|
6
|
+
class SpecInstallation
|
7
|
+
|
8
|
+
attr_accessor :spec, :name, :post_install_message, :state
|
9
|
+
def initialize(spec)
|
10
|
+
@spec, @name = spec, spec.name
|
11
|
+
@state = :none
|
12
|
+
@post_install_message = ""
|
13
|
+
end
|
14
|
+
|
15
|
+
def installed?
|
16
|
+
state == :installed
|
17
|
+
end
|
18
|
+
|
19
|
+
def enqueued?
|
20
|
+
state == :enqueued
|
21
|
+
end
|
22
|
+
|
23
|
+
# Only true when spec in neither installed nor already enqueued
|
24
|
+
def ready_to_enqueue?
|
25
|
+
!installed? && !enqueued?
|
26
|
+
end
|
27
|
+
|
28
|
+
def has_post_install_message?
|
29
|
+
!post_install_message.empty?
|
30
|
+
end
|
31
|
+
|
32
|
+
def ignorable_dependency?(dep)
|
33
|
+
dep.type == :development || dep.name == @name
|
34
|
+
end
|
35
|
+
|
36
|
+
# Checks installed dependencies against spec's dependencies to make
|
37
|
+
# sure needed dependencies have been installed.
|
38
|
+
def dependencies_installed?(remaining_specs)
|
39
|
+
installed_specs = remaining_specs.reject(&:installed?).map(&:name)
|
40
|
+
already_installed = lambda {|dep| installed_specs.include? dep.name }
|
41
|
+
dependencies.all? {|d| already_installed[d] }
|
42
|
+
end
|
43
|
+
|
44
|
+
# Represents only the non-development dependencies and the ones that
|
45
|
+
# are itself.
|
46
|
+
def dependencies
|
47
|
+
@dependencies ||= all_dependencies.reject {|dep| ignorable_dependency? dep }
|
48
|
+
end
|
49
|
+
|
50
|
+
# Represents all dependencies
|
51
|
+
def all_dependencies
|
52
|
+
@spec.dependencies
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.call(*args)
|
57
|
+
new(*args).call
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns max number of threads machine can handle with a min of 1
|
61
|
+
def self.max_threads
|
62
|
+
[Bundler.settings[:jobs].to_i-1, 1].max
|
63
|
+
end
|
64
|
+
|
65
|
+
def initialize(installer, all_specs, size, standalone, force)
|
66
|
+
@installer = installer
|
67
|
+
@size = size
|
68
|
+
@standalone = standalone
|
69
|
+
@force = force
|
70
|
+
@specs = all_specs.map { |s| SpecInstallation.new(s) }
|
71
|
+
end
|
72
|
+
|
73
|
+
def call
|
74
|
+
enqueue_specs
|
75
|
+
process_specs until @specs.all?(&:installed?)
|
76
|
+
ensure
|
77
|
+
worker_pool && worker_pool.stop
|
78
|
+
end
|
79
|
+
|
80
|
+
def worker_pool
|
81
|
+
@worker_pool ||= Bundler::Worker.new @size, lambda { |spec_install, worker_num|
|
82
|
+
message = @installer.install_gem_from_spec spec_install.spec, @standalone, worker_num, @force
|
83
|
+
spec_install.post_install_message = message unless message.nil?
|
84
|
+
spec_install
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
# Dequeue a spec and save its post-install message and then enqueue the
|
89
|
+
# remaining specs.
|
90
|
+
# Some specs might've had to wait til this spec was installed to be
|
91
|
+
# processed so the call to `enqueue_specs` is important after every
|
92
|
+
# dequeue.
|
93
|
+
def process_specs
|
94
|
+
spec = worker_pool.deq
|
95
|
+
spec.state = :installed
|
96
|
+
collect_post_install_message spec if spec.has_post_install_message?
|
97
|
+
enqueue_specs
|
98
|
+
end
|
99
|
+
|
100
|
+
def collect_post_install_message(spec)
|
101
|
+
Bundler::Installer.post_install_messages[spec.name] = spec.post_install_message
|
102
|
+
end
|
103
|
+
|
104
|
+
# Keys in the remains hash represent uninstalled gems specs.
|
105
|
+
# We enqueue all gem specs that do not have any dependencies.
|
106
|
+
# Later we call this lambda again to install specs that depended on
|
107
|
+
# previously installed specifications. We continue until all specs
|
108
|
+
# are installed.
|
109
|
+
def enqueue_specs
|
110
|
+
@specs.select(&:ready_to_enqueue?).each do |spec|
|
111
|
+
if spec.dependencies_installed? @specs
|
112
|
+
worker_pool.enq spec
|
113
|
+
spec.state = :enqueued
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|