bundler 2.6.3 → 2.6.9
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +119 -6
- data/README.md +1 -1
- data/lib/bundler/build_metadata.rb +2 -2
- data/lib/bundler/checksum.rb +22 -12
- data/lib/bundler/cli/console.rb +8 -6
- data/lib/bundler/cli/doctor/diagnose.rb +167 -0
- data/lib/bundler/cli/doctor/ssl.rb +249 -0
- data/lib/bundler/cli/doctor.rb +27 -151
- data/lib/bundler/cli/info.rb +4 -4
- data/lib/bundler/cli/inject.rb +2 -2
- data/lib/bundler/cli/issue.rb +3 -3
- data/lib/bundler/cli/lock.rb +2 -1
- data/lib/bundler/cli/show.rb +1 -1
- data/lib/bundler/cli.rb +2 -11
- data/lib/bundler/compact_index_client/cache.rb +1 -1
- data/lib/bundler/compact_index_client/parser.rb +1 -1
- data/lib/bundler/compact_index_client/updater.rb +2 -1
- data/lib/bundler/current_ruby.rb +23 -33
- data/lib/bundler/definition.rb +220 -184
- data/lib/bundler/dependency.rb +92 -47
- data/lib/bundler/dsl.rb +84 -80
- data/lib/bundler/endpoint_specification.rb +10 -3
- data/lib/bundler/errors.rb +22 -0
- data/lib/bundler/friendly_errors.rb +1 -1
- data/lib/bundler/gem_helpers.rb +4 -10
- data/lib/bundler/gem_version_promoter.rb +0 -2
- data/lib/bundler/injector.rb +9 -9
- data/lib/bundler/installer.rb +2 -2
- data/lib/bundler/lazy_specification.rb +67 -45
- data/lib/bundler/lockfile_parser.rb +8 -5
- data/lib/bundler/man/bundle-add.1 +1 -1
- data/lib/bundler/man/bundle-binstubs.1 +1 -1
- data/lib/bundler/man/bundle-cache.1 +1 -1
- data/lib/bundler/man/bundle-check.1 +1 -1
- data/lib/bundler/man/bundle-clean.1 +1 -1
- data/lib/bundler/man/bundle-config.1 +6 -6
- data/lib/bundler/man/bundle-config.1.ronn +9 -4
- data/lib/bundler/man/bundle-console.1 +1 -1
- data/lib/bundler/man/bundle-doctor.1 +1 -1
- data/lib/bundler/man/bundle-env.1 +1 -1
- data/lib/bundler/man/bundle-exec.1 +3 -3
- data/lib/bundler/man/bundle-exec.1.ronn +2 -2
- data/lib/bundler/man/bundle-fund.1 +1 -1
- data/lib/bundler/man/bundle-gem.1 +1 -1
- data/lib/bundler/man/bundle-help.1 +1 -1
- data/lib/bundler/man/bundle-info.1 +1 -1
- data/lib/bundler/man/bundle-init.1 +1 -1
- data/lib/bundler/man/bundle-inject.1 +1 -1
- data/lib/bundler/man/bundle-install.1 +1 -1
- data/lib/bundler/man/bundle-issue.1 +1 -1
- data/lib/bundler/man/bundle-licenses.1 +1 -1
- data/lib/bundler/man/bundle-list.1 +1 -1
- data/lib/bundler/man/bundle-lock.1 +1 -1
- data/lib/bundler/man/bundle-open.1 +1 -1
- data/lib/bundler/man/bundle-outdated.1 +1 -1
- data/lib/bundler/man/bundle-platform.1 +1 -1
- data/lib/bundler/man/bundle-plugin.1 +1 -1
- data/lib/bundler/man/bundle-pristine.1 +1 -1
- data/lib/bundler/man/bundle-remove.1 +1 -1
- data/lib/bundler/man/bundle-show.1 +1 -1
- data/lib/bundler/man/bundle-update.1 +1 -1
- data/lib/bundler/man/bundle-version.1 +1 -1
- data/lib/bundler/man/bundle-viz.1 +1 -1
- data/lib/bundler/man/bundle.1 +1 -1
- data/lib/bundler/man/gemfile.5 +1 -1
- data/lib/bundler/match_metadata.rb +13 -0
- data/lib/bundler/plugin/api/source.rb +1 -1
- data/lib/bundler/plugin/index.rb +1 -1
- data/lib/bundler/plugin/installer/path.rb +8 -0
- data/lib/bundler/plugin.rb +1 -1
- data/lib/bundler/resolver/candidate.rb +12 -9
- data/lib/bundler/resolver/package.rb +7 -3
- data/lib/bundler/resolver/spec_group.rb +1 -25
- data/lib/bundler/resolver/strategy.rb +40 -0
- data/lib/bundler/resolver.rb +29 -27
- data/lib/bundler/rubygems_ext.rb +97 -81
- data/lib/bundler/rubygems_integration.rb +2 -3
- data/lib/bundler/runtime.rb +27 -29
- data/lib/bundler/shared_helpers.rb +4 -0
- data/lib/bundler/source/gemspec.rb +1 -4
- data/lib/bundler/source/git/git_proxy.rb +14 -3
- data/lib/bundler/source/git.rb +5 -1
- data/lib/bundler/source/path.rb +2 -2
- data/lib/bundler/source/rubygems/remote.rb +11 -3
- data/lib/bundler/source/rubygems.rb +19 -4
- data/lib/bundler/source.rb +2 -0
- data/lib/bundler/source_list.rb +33 -11
- data/lib/bundler/spec_set.rb +98 -40
- data/lib/bundler/templates/newgem/Gemfile.tt +1 -0
- data/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb +53 -3
- data/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb +1 -1
- data/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +11 -0
- data/lib/bundler/vendor/pub_grub/lib/pub_grub/basic_package_source.rb +4 -24
- data/lib/bundler/vendor/pub_grub/lib/pub_grub/strategy.rb +42 -0
- data/lib/bundler/vendor/pub_grub/lib/pub_grub/version_range.rb +20 -8
- data/lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb +17 -29
- data/lib/bundler/vendor/uri/lib/uri/common.rb +7 -3
- data/lib/bundler/vendor/uri/lib/uri/generic.rb +12 -11
- data/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb +6 -6
- data/lib/bundler/vendor/uri/lib/uri/version.rb +1 -1
- data/lib/bundler/version.rb +1 -1
- metadata +7 -4
- data/lib/bundler/compact_index_client/gem_parser.rb +0 -32
@@ -147,6 +147,12 @@ module Bundler
|
|
147
147
|
end
|
148
148
|
end
|
149
149
|
|
150
|
+
def installed_to?(destination)
|
151
|
+
# if copy_to is interrupted, it may leave a partially installed directory that
|
152
|
+
# contains .git but no other files -- consider this not to be installed
|
153
|
+
Dir.exist?(destination) && (Dir.children(destination) - [".git"]).any?
|
154
|
+
end
|
155
|
+
|
150
156
|
private
|
151
157
|
|
152
158
|
def git_remote_fetch(args)
|
@@ -179,7 +185,8 @@ module Bundler
|
|
179
185
|
_, err, status = capture(command, nil)
|
180
186
|
return extra_ref if status.success?
|
181
187
|
|
182
|
-
if err.include?("Could not find remote branch")
|
188
|
+
if err.include?("Could not find remote branch") || # git up to 2.49
|
189
|
+
err.include?("Remote branch #{branch_option} not found") # git 2.49 or higher
|
183
190
|
raise MissingGitRevisionError.new(command_with_no_credentials, nil, explicit_ref, credential_filtered_uri)
|
184
191
|
else
|
185
192
|
idx = command.index("--depth")
|
@@ -256,7 +263,7 @@ module Bundler
|
|
256
263
|
end
|
257
264
|
|
258
265
|
def not_pinned?
|
259
|
-
|
266
|
+
branch_option || ref.nil?
|
260
267
|
end
|
261
268
|
|
262
269
|
def pinned_to_full_sha?
|
@@ -420,7 +427,7 @@ module Bundler
|
|
420
427
|
# anyways.
|
421
428
|
return args if @revision
|
422
429
|
|
423
|
-
args += ["--branch",
|
430
|
+
args += ["--branch", branch_option] if branch_option
|
424
431
|
args
|
425
432
|
end
|
426
433
|
|
@@ -436,6 +443,10 @@ module Bundler
|
|
436
443
|
extra_args
|
437
444
|
end
|
438
445
|
|
446
|
+
def branch_option
|
447
|
+
branch || tag
|
448
|
+
end
|
449
|
+
|
439
450
|
def full_clone?
|
440
451
|
depth.nil?
|
441
452
|
end
|
data/lib/bundler/source/git.rb
CHANGED
@@ -360,7 +360,11 @@ module Bundler
|
|
360
360
|
end
|
361
361
|
|
362
362
|
def locked_revision_checked_out?
|
363
|
-
locked_revision && locked_revision == revision &&
|
363
|
+
locked_revision && locked_revision == revision && installed?
|
364
|
+
end
|
365
|
+
|
366
|
+
def installed?
|
367
|
+
git_proxy.installed_to?(install_path)
|
364
368
|
end
|
365
369
|
|
366
370
|
def base_name
|
data/lib/bundler/source/path.rb
CHANGED
@@ -60,8 +60,8 @@ module Bundler
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def eql?(other)
|
63
|
-
|
64
|
-
|
63
|
+
[Gemspec, Path].include?(other.class) &&
|
64
|
+
expanded_original_path == other.expanded_original_path &&
|
65
65
|
version == other.version
|
66
66
|
end
|
67
67
|
|
@@ -16,6 +16,9 @@ module Bundler
|
|
16
16
|
@anonymized_uri = remove_auth(@uri).freeze
|
17
17
|
end
|
18
18
|
|
19
|
+
MAX_CACHE_SLUG_HOST_SIZE = 255 - 1 - 32 # 255 minus dot minus MD5 length
|
20
|
+
private_constant :MAX_CACHE_SLUG_HOST_SIZE
|
21
|
+
|
19
22
|
# @return [String] A slug suitable for use as a cache key for this
|
20
23
|
# remote.
|
21
24
|
#
|
@@ -28,10 +31,15 @@ module Bundler
|
|
28
31
|
host = cache_uri.to_s.start_with?("file://") ? nil : cache_uri.host
|
29
32
|
|
30
33
|
uri_parts = [host, cache_uri.user, cache_uri.port, cache_uri.path]
|
31
|
-
|
34
|
+
uri_parts.compact!
|
35
|
+
uri_digest = SharedHelpers.digest(:MD5).hexdigest(uri_parts.join("."))
|
36
|
+
|
37
|
+
uri_parts.pop
|
38
|
+
host_parts = uri_parts.join(".")
|
39
|
+
return uri_digest if host_parts.empty?
|
32
40
|
|
33
|
-
|
34
|
-
|
41
|
+
shortened_host_parts = host_parts[0...MAX_CACHE_SLUG_HOST_SIZE]
|
42
|
+
[shortened_host_parts, uri_digest].join(".")
|
35
43
|
end
|
36
44
|
end
|
37
45
|
|
@@ -19,6 +19,7 @@ module Bundler
|
|
19
19
|
@allow_remote = false
|
20
20
|
@allow_cached = false
|
21
21
|
@allow_local = options["allow_local"] || false
|
22
|
+
@prefer_local = false
|
22
23
|
@checksum_store = Checksum::Store.new
|
23
24
|
|
24
25
|
Array(options["remotes"]).reverse_each {|r| add_remote(r) }
|
@@ -30,6 +31,10 @@ module Bundler
|
|
30
31
|
@caches ||= [cache_path, *Bundler.rubygems.gem_cache]
|
31
32
|
end
|
32
33
|
|
34
|
+
def prefer_local!
|
35
|
+
@prefer_local = true
|
36
|
+
end
|
37
|
+
|
33
38
|
def local_only!
|
34
39
|
@specs = nil
|
35
40
|
@allow_local = true
|
@@ -37,6 +42,10 @@ module Bundler
|
|
37
42
|
@allow_remote = false
|
38
43
|
end
|
39
44
|
|
45
|
+
def local_only?
|
46
|
+
@allow_local && !@allow_remote
|
47
|
+
end
|
48
|
+
|
40
49
|
def local!
|
41
50
|
return if @allow_local
|
42
51
|
|
@@ -139,9 +148,15 @@ module Bundler
|
|
139
148
|
index.merge!(cached_specs) if @allow_cached
|
140
149
|
index.merge!(installed_specs) if @allow_local
|
141
150
|
|
142
|
-
|
143
|
-
|
144
|
-
|
151
|
+
if @allow_local
|
152
|
+
if @prefer_local
|
153
|
+
index.merge!(default_specs)
|
154
|
+
else
|
155
|
+
# complete with default specs, only if not already available in the
|
156
|
+
# index through remote, cached, or installed specs
|
157
|
+
index.use(default_specs)
|
158
|
+
end
|
159
|
+
end
|
145
160
|
|
146
161
|
index
|
147
162
|
end
|
@@ -439,7 +454,7 @@ module Bundler
|
|
439
454
|
end
|
440
455
|
|
441
456
|
def installed?(spec)
|
442
|
-
installed_specs[spec].any? && !spec.
|
457
|
+
installed_specs[spec].any? && !spec.installation_missing?
|
443
458
|
end
|
444
459
|
|
445
460
|
def rubygems_dir
|
data/lib/bundler/source.rb
CHANGED
data/lib/bundler/source_list.rb
CHANGED
@@ -141,6 +141,10 @@ module Bundler
|
|
141
141
|
different_sources?(lock_sources, replacement_sources)
|
142
142
|
end
|
143
143
|
|
144
|
+
def prefer_local!
|
145
|
+
all_sources.each(&:prefer_local!)
|
146
|
+
end
|
147
|
+
|
144
148
|
def local_only!
|
145
149
|
all_sources.each(&:local_only!)
|
146
150
|
end
|
@@ -169,39 +173,57 @@ module Bundler
|
|
169
173
|
|
170
174
|
def map_sources(replacement_sources)
|
171
175
|
rubygems = @rubygems_sources.map do |source|
|
172
|
-
replace_rubygems_source(replacement_sources, source)
|
176
|
+
replace_rubygems_source(replacement_sources, source)
|
173
177
|
end
|
174
178
|
|
175
179
|
git, plugin = [@git_sources, @plugin_sources].map do |sources|
|
176
180
|
sources.map do |source|
|
177
|
-
replacement_sources
|
181
|
+
replace_source(replacement_sources, source)
|
178
182
|
end
|
179
183
|
end
|
180
184
|
|
181
185
|
path = @path_sources.map do |source|
|
182
|
-
replacement_sources
|
186
|
+
replace_path_source(replacement_sources, source)
|
183
187
|
end
|
184
188
|
|
185
189
|
[rubygems, path, git, plugin]
|
186
190
|
end
|
187
191
|
|
188
192
|
def global_replacement_source(replacement_sources)
|
189
|
-
|
190
|
-
return global_rubygems_source unless replacement_source
|
191
|
-
|
192
|
-
replacement_source.local!
|
193
|
-
replacement_source
|
193
|
+
replace_rubygems_source(replacement_sources, global_rubygems_source, &:local!)
|
194
194
|
end
|
195
195
|
|
196
196
|
def replace_rubygems_source(replacement_sources, gemfile_source)
|
197
|
+
replace_source(replacement_sources, gemfile_source) do |replacement_source|
|
198
|
+
# locked sources never include credentials so always prefer remotes from the gemfile
|
199
|
+
replacement_source.remotes = gemfile_source.remotes
|
200
|
+
|
201
|
+
yield replacement_source if block_given?
|
202
|
+
|
203
|
+
replacement_source
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def replace_source(replacement_sources, gemfile_source)
|
197
208
|
replacement_source = replacement_sources.find {|s| s == gemfile_source }
|
198
|
-
return unless replacement_source
|
209
|
+
return gemfile_source unless replacement_source
|
210
|
+
|
211
|
+
replacement_source = yield(replacement_source) if block_given?
|
199
212
|
|
200
|
-
# locked sources never include credentials so always prefer remotes from the gemfile
|
201
|
-
replacement_source.remotes = gemfile_source.remotes
|
202
213
|
replacement_source
|
203
214
|
end
|
204
215
|
|
216
|
+
def replace_path_source(replacement_sources, gemfile_source)
|
217
|
+
replace_source(replacement_sources, gemfile_source) do |replacement_source|
|
218
|
+
if gemfile_source.is_a?(Source::Gemspec)
|
219
|
+
gemfile_source.checksum_store = replacement_source.checksum_store
|
220
|
+
gemfile_source
|
221
|
+
else
|
222
|
+
replacement_source
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
205
227
|
def different_sources?(lock_sources, replacement_sources)
|
206
228
|
!equivalent_sources?(lock_sources, replacement_sources)
|
207
229
|
end
|
data/lib/bundler/spec_set.rb
CHANGED
@@ -29,9 +29,10 @@ module Bundler
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def normalize_platforms!(deps, platforms)
|
32
|
-
|
32
|
+
remove_invalid_platforms!(deps, platforms)
|
33
|
+
add_extra_platforms!(platforms)
|
33
34
|
|
34
|
-
|
35
|
+
platforms.map! do |platform|
|
35
36
|
next platform if platform == Gem::Platform::RUBY
|
36
37
|
|
37
38
|
begin
|
@@ -44,11 +45,34 @@ module Bundler
|
|
44
45
|
next platform if incomplete_for_platform?(deps, less_specific_platform)
|
45
46
|
|
46
47
|
less_specific_platform
|
47
|
-
end.uniq
|
48
|
+
end.uniq!
|
49
|
+
end
|
50
|
+
|
51
|
+
def add_originally_invalid_platforms!(platforms, originally_invalid_platforms)
|
52
|
+
originally_invalid_platforms.each do |originally_invalid_platform|
|
53
|
+
platforms << originally_invalid_platform if complete_platform(originally_invalid_platform)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def remove_invalid_platforms!(deps, platforms, skips: [])
|
58
|
+
invalid_platforms = []
|
59
|
+
|
60
|
+
platforms.reject! do |platform|
|
61
|
+
next false if skips.include?(platform)
|
62
|
+
|
63
|
+
invalid = incomplete_for_platform?(deps, platform)
|
64
|
+
invalid_platforms << platform if invalid
|
65
|
+
invalid
|
66
|
+
end
|
67
|
+
|
68
|
+
invalid_platforms
|
48
69
|
end
|
49
70
|
|
50
71
|
def add_extra_platforms!(platforms)
|
51
|
-
|
72
|
+
if @specs.empty?
|
73
|
+
platforms.concat([Gem::Platform::RUBY]).uniq
|
74
|
+
return
|
75
|
+
end
|
52
76
|
|
53
77
|
new_platforms = all_platforms.select do |platform|
|
54
78
|
next if platforms.include?(platform)
|
@@ -56,14 +80,13 @@ module Bundler
|
|
56
80
|
|
57
81
|
complete_platform(platform)
|
58
82
|
end
|
59
|
-
return
|
83
|
+
return if new_platforms.empty?
|
60
84
|
|
61
85
|
platforms.concat(new_platforms)
|
86
|
+
return if new_platforms.include?(Bundler.local_platform)
|
62
87
|
|
63
88
|
less_specific_platform = new_platforms.find {|platform| platform != Gem::Platform::RUBY && Bundler.local_platform === platform && platform === Bundler.local_platform }
|
64
89
|
platforms.delete(Bundler.local_platform) if less_specific_platform
|
65
|
-
|
66
|
-
platforms
|
67
90
|
end
|
68
91
|
|
69
92
|
def validate_deps(s)
|
@@ -83,15 +106,13 @@ module Bundler
|
|
83
106
|
end
|
84
107
|
|
85
108
|
def []=(key, value)
|
86
|
-
|
109
|
+
delete_by_name(key)
|
87
110
|
|
88
|
-
|
111
|
+
add_spec(value)
|
89
112
|
end
|
90
113
|
|
91
114
|
def delete(specs)
|
92
|
-
Array(specs).each {|spec|
|
93
|
-
|
94
|
-
reset!
|
115
|
+
Array(specs).each {|spec| remove_spec(spec) }
|
95
116
|
end
|
96
117
|
|
97
118
|
def sort!
|
@@ -117,20 +138,22 @@ module Bundler
|
|
117
138
|
def materialized_for_all_platforms
|
118
139
|
@specs.map do |s|
|
119
140
|
next s unless s.is_a?(LazySpecification)
|
120
|
-
s.
|
121
|
-
spec = s.materialize_strictly
|
141
|
+
spec = s.materialize_for_cache
|
122
142
|
raise GemNotFound, "Could not find #{s.full_name} in any of the sources" unless spec
|
123
143
|
spec
|
124
144
|
end
|
125
145
|
end
|
126
146
|
|
127
147
|
def incomplete_for_platform?(deps, platform)
|
128
|
-
|
148
|
+
incomplete_specs_for_platform(deps, platform).any?
|
149
|
+
end
|
150
|
+
|
151
|
+
def incomplete_specs_for_platform(deps, platform)
|
152
|
+
return [] if @specs.empty?
|
129
153
|
|
130
154
|
validation_set = self.class.new(@specs)
|
131
155
|
validation_set.for(deps, [platform])
|
132
|
-
|
133
|
-
validation_set.incomplete_specs.any?
|
156
|
+
validation_set.incomplete_specs
|
134
157
|
end
|
135
158
|
|
136
159
|
def missing_specs_for(deps)
|
@@ -169,12 +192,14 @@ module Bundler
|
|
169
192
|
|
170
193
|
def delete_by_name(name)
|
171
194
|
@specs.reject! {|spec| spec.name == name }
|
195
|
+
@sorted&.reject! {|spec| spec.name == name }
|
196
|
+
return if @lookup.nil?
|
172
197
|
|
173
|
-
|
198
|
+
@lookup[name] = nil
|
174
199
|
end
|
175
200
|
|
176
201
|
def version_for(name)
|
177
|
-
|
202
|
+
exemplary_spec(name)&.version
|
178
203
|
end
|
179
204
|
|
180
205
|
def what_required(spec)
|
@@ -212,6 +237,10 @@ module Bundler
|
|
212
237
|
s.matches_current_metadata? && valid_dependencies?(s)
|
213
238
|
end
|
214
239
|
|
240
|
+
def to_s
|
241
|
+
map(&:full_name).to_s
|
242
|
+
end
|
243
|
+
|
215
244
|
private
|
216
245
|
|
217
246
|
def materialize_dependencies(dependencies, platforms = [nil], skips: [])
|
@@ -245,11 +274,6 @@ module Bundler
|
|
245
274
|
@materializations.filter_map(&:materialized_spec)
|
246
275
|
end
|
247
276
|
|
248
|
-
def reset!
|
249
|
-
@sorted = nil
|
250
|
-
@lookup = nil
|
251
|
-
end
|
252
|
-
|
253
277
|
def complete_platform(platform)
|
254
278
|
new_specs = []
|
255
279
|
|
@@ -269,9 +293,7 @@ module Bundler
|
|
269
293
|
end
|
270
294
|
|
271
295
|
if valid_platform && new_specs.any?
|
272
|
-
|
273
|
-
|
274
|
-
reset!
|
296
|
+
new_specs.each {|spec| add_spec(spec) }
|
275
297
|
end
|
276
298
|
|
277
299
|
valid_platform
|
@@ -282,8 +304,13 @@ module Bundler
|
|
282
304
|
end
|
283
305
|
|
284
306
|
def additional_variants_from(other)
|
285
|
-
other.select do |
|
286
|
-
|
307
|
+
other.select do |other_spec|
|
308
|
+
spec = exemplary_spec(other_spec.name)
|
309
|
+
next unless spec
|
310
|
+
|
311
|
+
selected = spec.version == other_spec.version && valid_dependencies?(other_spec)
|
312
|
+
other_spec.source = spec.source if selected
|
313
|
+
selected
|
287
314
|
end
|
288
315
|
end
|
289
316
|
|
@@ -292,15 +319,12 @@ module Bundler
|
|
292
319
|
end
|
293
320
|
|
294
321
|
def sorted
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
" on each other, creating an infinite loop. Please remove either" \
|
302
|
-
" gem '#{cgems[0]}' or gem '#{cgems[1]}' and try again."
|
303
|
-
end
|
322
|
+
@sorted ||= ([@specs.find {|s| s.name == "rake" }] + tsort).compact.uniq
|
323
|
+
rescue TSort::Cyclic => error
|
324
|
+
cgems = extract_circular_gems(error)
|
325
|
+
raise CyclicDependencyError, "Your bundle requires gems that depend" \
|
326
|
+
" on each other, creating an infinite loop. Please remove either" \
|
327
|
+
" gem '#{cgems[0]}' or gem '#{cgems[1]}' and try again."
|
304
328
|
end
|
305
329
|
|
306
330
|
def extract_circular_gems(error)
|
@@ -311,8 +335,7 @@ module Bundler
|
|
311
335
|
@lookup ||= begin
|
312
336
|
lookup = {}
|
313
337
|
@specs.each do |s|
|
314
|
-
lookup
|
315
|
-
lookup[s.name] << s
|
338
|
+
index_spec(lookup, s.name, s)
|
316
339
|
end
|
317
340
|
lookup
|
318
341
|
end
|
@@ -333,5 +356,40 @@ module Bundler
|
|
333
356
|
specs_for_name.each {|s2| yield s2 }
|
334
357
|
end
|
335
358
|
end
|
359
|
+
|
360
|
+
def add_spec(spec)
|
361
|
+
@specs << spec
|
362
|
+
|
363
|
+
name = spec.name
|
364
|
+
|
365
|
+
@sorted&.insert(@sorted.bsearch_index {|s| s.name >= name } || @sorted.size, spec)
|
366
|
+
return if @lookup.nil?
|
367
|
+
|
368
|
+
index_spec(@lookup, name, spec)
|
369
|
+
end
|
370
|
+
|
371
|
+
def remove_spec(spec)
|
372
|
+
@specs.delete(spec)
|
373
|
+
@sorted&.delete(spec)
|
374
|
+
return if @lookup.nil?
|
375
|
+
|
376
|
+
indexed_specs = @lookup[spec.name]
|
377
|
+
return unless indexed_specs
|
378
|
+
|
379
|
+
if indexed_specs.size > 1
|
380
|
+
@lookup[spec.name].delete(spec)
|
381
|
+
else
|
382
|
+
@lookup[spec.name] = nil
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
def index_spec(hash, key, value)
|
387
|
+
hash[key] ||= []
|
388
|
+
hash[key] << value
|
389
|
+
end
|
390
|
+
|
391
|
+
def exemplary_spec(name)
|
392
|
+
self[name].first
|
393
|
+
end
|
336
394
|
end
|
337
395
|
end
|
@@ -41,6 +41,7 @@ class Bundler::ConnectionPool::TimedStack
|
|
41
41
|
def push(obj, options = {})
|
42
42
|
@mutex.synchronize do
|
43
43
|
if @shutdown_block
|
44
|
+
@created -= 1 unless @created == 0
|
44
45
|
@shutdown_block.call(obj)
|
45
46
|
else
|
46
47
|
store_connection obj, options
|
@@ -98,6 +99,26 @@ class Bundler::ConnectionPool::TimedStack
|
|
98
99
|
end
|
99
100
|
end
|
100
101
|
|
102
|
+
##
|
103
|
+
# Reaps connections that were checked in more than +idle_seconds+ ago.
|
104
|
+
def reap(idle_seconds, &block)
|
105
|
+
raise ArgumentError, "reap must receive a block" unless block
|
106
|
+
raise ArgumentError, "idle_seconds must be a number" unless idle_seconds.is_a?(Numeric)
|
107
|
+
raise Bundler::ConnectionPool::PoolShuttingDownError if @shutdown_block
|
108
|
+
|
109
|
+
idle.times do
|
110
|
+
conn =
|
111
|
+
@mutex.synchronize do
|
112
|
+
raise Bundler::ConnectionPool::PoolShuttingDownError if @shutdown_block
|
113
|
+
|
114
|
+
reserve_idle_connection(idle_seconds)
|
115
|
+
end
|
116
|
+
break unless conn
|
117
|
+
|
118
|
+
block.call(conn)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
101
122
|
##
|
102
123
|
# Returns +true+ if there are no available connections.
|
103
124
|
|
@@ -112,6 +133,12 @@ class Bundler::ConnectionPool::TimedStack
|
|
112
133
|
@max - @created + @que.length
|
113
134
|
end
|
114
135
|
|
136
|
+
##
|
137
|
+
# The number of connections created and available on the stack.
|
138
|
+
def idle
|
139
|
+
@que.length
|
140
|
+
end
|
141
|
+
|
115
142
|
private
|
116
143
|
|
117
144
|
def current_time
|
@@ -133,7 +160,7 @@ class Bundler::ConnectionPool::TimedStack
|
|
133
160
|
# This method must return a connection from the stack.
|
134
161
|
|
135
162
|
def fetch_connection(options = nil)
|
136
|
-
@que.pop
|
163
|
+
@que.pop&.first
|
137
164
|
end
|
138
165
|
|
139
166
|
##
|
@@ -144,9 +171,32 @@ class Bundler::ConnectionPool::TimedStack
|
|
144
171
|
def shutdown_connections(options = nil)
|
145
172
|
while connection_stored?(options)
|
146
173
|
conn = fetch_connection(options)
|
174
|
+
@created -= 1 unless @created == 0
|
147
175
|
@shutdown_block.call(conn)
|
148
176
|
end
|
149
|
-
|
177
|
+
end
|
178
|
+
|
179
|
+
##
|
180
|
+
# This is an extension point for TimedStack and is called with a mutex.
|
181
|
+
#
|
182
|
+
# This method returns the oldest idle connection if it has been idle for more than idle_seconds.
|
183
|
+
# This requires that the stack is kept in order of checked in time (oldest first).
|
184
|
+
|
185
|
+
def reserve_idle_connection(idle_seconds)
|
186
|
+
return unless idle_connections?(idle_seconds)
|
187
|
+
|
188
|
+
@created -= 1 unless @created == 0
|
189
|
+
|
190
|
+
@que.shift.first
|
191
|
+
end
|
192
|
+
|
193
|
+
##
|
194
|
+
# This is an extension point for TimedStack and is called with a mutex.
|
195
|
+
#
|
196
|
+
# Returns true if the first connection in the stack has been idle for more than idle_seconds
|
197
|
+
|
198
|
+
def idle_connections?(idle_seconds)
|
199
|
+
connection_stored? && (current_time - @que.first.last > idle_seconds)
|
150
200
|
end
|
151
201
|
|
152
202
|
##
|
@@ -155,7 +205,7 @@ class Bundler::ConnectionPool::TimedStack
|
|
155
205
|
# This method must return +obj+ to the stack.
|
156
206
|
|
157
207
|
def store_connection(obj, options = nil)
|
158
|
-
@que.push obj
|
208
|
+
@que.push [obj, current_time]
|
159
209
|
end
|
160
210
|
|
161
211
|
##
|
@@ -160,6 +160,12 @@ class Bundler::ConnectionPool
|
|
160
160
|
@available.shutdown(reload: true, &block)
|
161
161
|
end
|
162
162
|
|
163
|
+
## Reaps idle connections that have been idle for over +idle_seconds+.
|
164
|
+
# +idle_seconds+ defaults to 60.
|
165
|
+
def reap(idle_seconds = 60, &block)
|
166
|
+
@available.reap(idle_seconds, &block)
|
167
|
+
end
|
168
|
+
|
163
169
|
# Size of this connection pool
|
164
170
|
attr_reader :size
|
165
171
|
# Automatically drop all connections after fork
|
@@ -169,6 +175,11 @@ class Bundler::ConnectionPool
|
|
169
175
|
def available
|
170
176
|
@available.length
|
171
177
|
end
|
178
|
+
|
179
|
+
# Number of pool entries created and idle in the pool.
|
180
|
+
def idle
|
181
|
+
@available.idle
|
182
|
+
end
|
172
183
|
end
|
173
184
|
|
174
185
|
require_relative "connection_pool/timed_stack"
|