bundler 2.2.26 → 2.3.26
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 +501 -1
- data/README.md +1 -1
- data/bundler.gemspec +6 -8
- data/exe/bundle +7 -8
- data/exe/bundler +1 -1
- data/lib/bundler/.document +1 -0
- data/lib/bundler/build_metadata.rb +3 -3
- data/lib/bundler/cli/check.rb +1 -1
- data/lib/bundler/cli/common.rb +3 -2
- data/lib/bundler/cli/config.rb +10 -1
- data/lib/bundler/cli/doctor.rb +12 -3
- data/lib/bundler/cli/gem.rb +98 -9
- data/lib/bundler/cli/info.rb +27 -6
- data/lib/bundler/cli/init.rb +5 -1
- data/lib/bundler/cli/install.rb +13 -30
- data/lib/bundler/cli/issue.rb +4 -3
- data/lib/bundler/cli/outdated.rb +12 -3
- data/lib/bundler/cli/platform.rb +2 -2
- data/lib/bundler/cli/remove.rb +1 -2
- data/lib/bundler/cli/show.rb +1 -1
- data/lib/bundler/cli/update.rb +8 -4
- data/lib/bundler/cli.rb +23 -19
- data/lib/bundler/compact_index_client/cache.rb +0 -9
- data/lib/bundler/compact_index_client/updater.rb +16 -8
- data/lib/bundler/compact_index_client.rb +2 -8
- data/lib/bundler/current_ruby.rb +16 -6
- data/lib/bundler/definition.rb +204 -217
- data/lib/bundler/dependency.rb +23 -71
- data/lib/bundler/digest.rb +71 -0
- data/lib/bundler/dsl.rb +28 -45
- data/lib/bundler/endpoint_specification.rb +19 -13
- data/lib/bundler/env.rb +1 -1
- data/lib/bundler/environment_preserver.rb +4 -1
- data/lib/bundler/errors.rb +28 -2
- data/lib/bundler/feature_flag.rb +0 -1
- data/lib/bundler/fetcher/base.rb +6 -8
- data/lib/bundler/fetcher/compact_index.rb +9 -14
- data/lib/bundler/fetcher/index.rb +0 -26
- data/lib/bundler/fetcher.rb +20 -22
- data/lib/bundler/friendly_errors.rb +26 -34
- data/lib/bundler/gem_helper.rb +7 -18
- data/lib/bundler/gem_helpers.rb +9 -2
- data/lib/bundler/gem_version_promoter.rb +14 -25
- data/lib/bundler/index.rb +10 -40
- data/lib/bundler/injector.rb +16 -2
- data/lib/bundler/inline.rb +2 -12
- data/lib/bundler/installer/gem_installer.rb +13 -5
- data/lib/bundler/installer/standalone.rb +30 -3
- data/lib/bundler/installer.rb +18 -29
- data/lib/bundler/lazy_specification.rb +52 -35
- data/lib/bundler/lockfile_generator.rb +2 -2
- data/lib/bundler/lockfile_parser.rb +12 -10
- data/lib/bundler/man/bundle-add.1 +21 -5
- data/lib/bundler/man/bundle-add.1.ronn +16 -4
- data/lib/bundler/man/bundle-binstubs.1 +1 -1
- data/lib/bundler/man/bundle-cache.1 +7 -1
- data/lib/bundler/man/bundle-cache.1.ronn +7 -0
- data/lib/bundler/man/bundle-check.1 +1 -1
- data/lib/bundler/man/bundle-clean.1 +2 -2
- data/lib/bundler/man/bundle-clean.1.ronn +1 -1
- data/lib/bundler/man/bundle-config.1 +33 -14
- data/lib/bundler/man/bundle-config.1.ronn +30 -18
- data/lib/bundler/man/bundle-console.1 +53 -0
- data/lib/bundler/man/bundle-console.1.ronn +44 -0
- data/lib/bundler/man/bundle-doctor.1 +1 -1
- data/lib/bundler/man/bundle-exec.1 +2 -2
- data/lib/bundler/man/bundle-exec.1.ronn +1 -1
- data/lib/bundler/man/bundle-gem.1 +14 -1
- data/lib/bundler/man/bundle-gem.1.ronn +16 -0
- data/lib/bundler/man/bundle-help.1 +13 -0
- data/lib/bundler/man/bundle-help.1.ronn +12 -0
- 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 +5 -2
- data/lib/bundler/man/bundle-inject.1.ronn +3 -1
- data/lib/bundler/man/bundle-install.1 +6 -2
- data/lib/bundler/man/bundle-install.1.ronn +8 -2
- 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 +3 -10
- data/lib/bundler/man/bundle-outdated.1.ronn +1 -10
- data/lib/bundler/man/bundle-platform.1 +16 -6
- data/lib/bundler/man/bundle-platform.1.ronn +14 -7
- data/lib/bundler/man/bundle-plugin.1 +81 -0
- data/lib/bundler/man/bundle-plugin.1.ronn +59 -0
- 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 +2 -2
- data/lib/bundler/man/bundle-update.1.ronn +2 -1
- data/lib/bundler/man/bundle-version.1 +35 -0
- data/lib/bundler/man/bundle-version.1.ronn +24 -0
- data/lib/bundler/man/bundle-viz.1 +4 -1
- data/lib/bundler/man/bundle-viz.1.ronn +2 -0
- data/lib/bundler/man/bundle.1 +15 -10
- data/lib/bundler/man/bundle.1.ronn +12 -7
- data/lib/bundler/man/gemfile.5 +117 -80
- data/lib/bundler/man/gemfile.5.ronn +105 -84
- data/lib/bundler/man/index.txt +4 -0
- data/lib/bundler/match_metadata.rb +13 -0
- data/lib/bundler/match_platform.rb +0 -1
- data/lib/bundler/match_remote_metadata.rb +29 -0
- data/lib/bundler/plugin/api/source.rb +4 -9
- data/lib/bundler/plugin/installer/git.rb +0 -4
- data/lib/bundler/plugin/installer/rubygems.rb +0 -4
- data/lib/bundler/plugin/installer.rb +3 -1
- data/lib/bundler/plugin.rb +25 -6
- data/lib/bundler/process_lock.rb +1 -1
- data/lib/bundler/remote_specification.rb +10 -4
- data/lib/bundler/resolver/base.rb +50 -0
- data/lib/bundler/resolver/spec_group.rb +31 -49
- data/lib/bundler/resolver.rb +183 -192
- data/lib/bundler/ruby_dsl.rb +1 -1
- data/lib/bundler/ruby_version.rb +5 -18
- data/lib/bundler/rubygems_ext.rb +138 -20
- data/lib/bundler/rubygems_gem_installer.rb +42 -16
- data/lib/bundler/rubygems_integration.rb +42 -90
- data/lib/bundler/runtime.rb +2 -3
- data/lib/bundler/self_manager.rb +168 -0
- data/lib/bundler/settings.rb +13 -4
- data/lib/bundler/shared_helpers.rb +15 -24
- data/lib/bundler/source/git/git_proxy.rb +7 -4
- data/lib/bundler/source/git.rb +29 -13
- data/lib/bundler/source/metadata.rb +3 -3
- data/lib/bundler/source/path.rb +1 -1
- data/lib/bundler/source/rubygems.rb +148 -161
- data/lib/bundler/source/rubygems_aggregate.rb +1 -1
- data/lib/bundler/source.rb +6 -5
- data/lib/bundler/source_list.rb +15 -29
- data/lib/bundler/source_map.rb +15 -2
- data/lib/bundler/spec_set.rb +52 -32
- data/lib/bundler/stub_specification.rb +5 -3
- data/lib/bundler/templates/Executable +2 -4
- data/lib/bundler/templates/Executable.bundler +2 -2
- data/lib/bundler/templates/Executable.standalone +2 -4
- data/lib/bundler/templates/Gemfile +0 -2
- data/lib/bundler/templates/gems.rb +0 -3
- data/lib/bundler/templates/newgem/Gemfile.tt +5 -2
- data/lib/bundler/templates/newgem/README.md.tt +3 -9
- data/lib/bundler/templates/newgem/Rakefile.tt +15 -2
- data/lib/bundler/templates/newgem/github/workflows/main.yml.tt +5 -4
- data/lib/bundler/templates/newgem/gitlab-ci.yml.tt +5 -4
- data/lib/bundler/templates/newgem/newgem.gemspec.tt +16 -16
- data/lib/bundler/templates/newgem/sig/newgem.rbs.tt +8 -0
- data/lib/bundler/templates/newgem/standard.yml.tt +3 -0
- data/lib/bundler/templates/newgem/test/minitest/{newgem_test.rb.tt → test_newgem.rb.tt} +1 -1
- data/lib/bundler/ui/shell.rb +1 -1
- data/lib/bundler/vendor/.document +1 -0
- data/lib/bundler/vendor/connection_pool/LICENSE +20 -0
- data/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb +19 -21
- data/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb +1 -1
- data/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb +57 -0
- data/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +39 -74
- data/lib/bundler/vendor/fileutils/LICENSE.txt +22 -0
- data/lib/bundler/vendor/molinillo/LICENSE +9 -0
- data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb +3 -3
- data/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb +32 -26
- data/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb +1 -1
- data/lib/bundler/vendor/net-http-persistent/README.rdoc +82 -0
- data/lib/bundler/vendor/thor/LICENSE.md +20 -0
- data/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb +6 -6
- data/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb +1 -2
- data/lib/bundler/vendor/thor/lib/thor/actions.rb +6 -2
- data/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +6 -0
- data/lib/bundler/vendor/thor/lib/thor/error.rb +9 -4
- data/lib/bundler/vendor/thor/lib/thor/parser/options.rb +19 -1
- data/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +22 -4
- data/lib/bundler/vendor/thor/lib/thor/shell.rb +1 -1
- data/lib/bundler/vendor/thor/lib/thor/util.rb +1 -1
- data/lib/bundler/vendor/thor/lib/thor/version.rb +1 -1
- data/lib/bundler/vendor/tsort/LICENSE.txt +22 -0
- data/lib/bundler/vendor/tsort/lib/tsort.rb +452 -0
- data/lib/bundler/vendor/uri/LICENSE.txt +22 -0
- data/lib/bundler/vendor/uri/lib/uri/common.rb +17 -80
- data/lib/bundler/vendor/uri/lib/uri/ftp.rb +0 -1
- data/lib/bundler/vendor/uri/lib/uri/generic.rb +5 -6
- data/lib/bundler/vendor/uri/lib/uri/http.rb +0 -1
- data/lib/bundler/vendor/uri/lib/uri/https.rb +0 -1
- data/lib/bundler/vendor/uri/lib/uri/ldap.rb +1 -1
- data/lib/bundler/vendor/uri/lib/uri/mailto.rb +0 -1
- data/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb +1 -14
- data/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb +1 -12
- data/lib/bundler/vendor/uri/lib/uri/version.rb +1 -1
- data/lib/bundler/vendor/uri/lib/uri/ws.rb +84 -0
- data/lib/bundler/vendor/uri/lib/uri/wss.rb +22 -0
- data/lib/bundler/vendor/uri/lib/uri.rb +0 -1
- data/lib/bundler/vendored_tsort.rb +4 -0
- data/lib/bundler/version.rb +1 -1
- data/lib/bundler/worker.rb +2 -2
- data/lib/bundler.rb +40 -29
- metadata +37 -12
- data/lib/bundler/dep_proxy.rb +0 -55
- data/lib/bundler/gemdeps.rb +0 -29
- data/lib/bundler/psyched_yaml.rb +0 -22
- data/lib/bundler/vendor/connection_pool/lib/connection_pool/monotonic_time.rb +0 -66
data/lib/bundler/definition.rb
CHANGED
|
@@ -6,6 +6,11 @@ module Bundler
|
|
|
6
6
|
class Definition
|
|
7
7
|
include GemHelpers
|
|
8
8
|
|
|
9
|
+
class << self
|
|
10
|
+
# Do not create or modify a lockfile (Makes #lock a noop)
|
|
11
|
+
attr_accessor :no_lock
|
|
12
|
+
end
|
|
13
|
+
|
|
9
14
|
attr_reader(
|
|
10
15
|
:dependencies,
|
|
11
16
|
:locked_deps,
|
|
@@ -65,6 +70,7 @@ module Bundler
|
|
|
65
70
|
@unlock = unlock
|
|
66
71
|
@optional_groups = optional_groups
|
|
67
72
|
@remote = false
|
|
73
|
+
@prefer_local = false
|
|
68
74
|
@specs = nil
|
|
69
75
|
@ruby_version = ruby_version
|
|
70
76
|
@gemfiles = gemfiles
|
|
@@ -73,7 +79,6 @@ module Bundler
|
|
|
73
79
|
@lockfile_contents = String.new
|
|
74
80
|
@locked_bundler_version = nil
|
|
75
81
|
@locked_ruby_version = nil
|
|
76
|
-
@locked_specs_incomplete_for_platform = false
|
|
77
82
|
@new_platform = nil
|
|
78
83
|
|
|
79
84
|
if lockfile && File.exist?(lockfile)
|
|
@@ -83,10 +88,11 @@ module Bundler
|
|
|
83
88
|
@platforms = @locked_platforms.dup
|
|
84
89
|
@locked_bundler_version = @locked_gems.bundler_version
|
|
85
90
|
@locked_ruby_version = @locked_gems.ruby_version
|
|
91
|
+
@originally_locked_specs = SpecSet.new(@locked_gems.specs)
|
|
86
92
|
|
|
87
93
|
if unlock != true
|
|
88
94
|
@locked_deps = @locked_gems.dependencies
|
|
89
|
-
@locked_specs =
|
|
95
|
+
@locked_specs = @originally_locked_specs
|
|
90
96
|
@locked_sources = @locked_gems.sources
|
|
91
97
|
else
|
|
92
98
|
@unlock = {}
|
|
@@ -100,6 +106,7 @@ module Bundler
|
|
|
100
106
|
@locked_gems = nil
|
|
101
107
|
@locked_deps = {}
|
|
102
108
|
@locked_specs = SpecSet.new([])
|
|
109
|
+
@originally_locked_specs = @locked_specs
|
|
103
110
|
@locked_sources = []
|
|
104
111
|
@locked_platforms = []
|
|
105
112
|
end
|
|
@@ -132,8 +139,8 @@ module Bundler
|
|
|
132
139
|
if @unlock[:conservative]
|
|
133
140
|
@unlock[:gems] ||= @dependencies.map(&:name)
|
|
134
141
|
else
|
|
135
|
-
eager_unlock =
|
|
136
|
-
@unlock[:gems] = @locked_specs.for(eager_unlock, false,
|
|
142
|
+
eager_unlock = (@unlock[:gems] || []).map {|name| Dependency.new(name, ">= 0") }
|
|
143
|
+
@unlock[:gems] = @locked_specs.for(eager_unlock, false, platforms).map(&:name).uniq
|
|
137
144
|
end
|
|
138
145
|
|
|
139
146
|
@dependency_changes = converge_dependencies
|
|
@@ -143,22 +150,20 @@ module Bundler
|
|
|
143
150
|
end
|
|
144
151
|
|
|
145
152
|
def gem_version_promoter
|
|
146
|
-
@gem_version_promoter ||=
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
else
|
|
154
|
-
@locked_specs
|
|
155
|
-
end
|
|
156
|
-
GemVersionPromoter.new(locked_specs, @unlock[:gems])
|
|
157
|
-
end
|
|
153
|
+
@gem_version_promoter ||= GemVersionPromoter.new(@originally_locked_specs, @unlock[:gems])
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def resolve_only_locally!
|
|
157
|
+
@remote = false
|
|
158
|
+
sources.local_only!
|
|
159
|
+
resolve
|
|
158
160
|
end
|
|
159
161
|
|
|
160
|
-
def
|
|
161
|
-
@
|
|
162
|
+
def resolve_prefering_local!
|
|
163
|
+
@prefer_local = true
|
|
164
|
+
@remote = true
|
|
165
|
+
sources.remote!
|
|
166
|
+
resolve
|
|
162
167
|
end
|
|
163
168
|
|
|
164
169
|
def resolve_with_cache!
|
|
@@ -201,6 +206,7 @@ module Bundler
|
|
|
201
206
|
true
|
|
202
207
|
rescue BundlerError => e
|
|
203
208
|
@resolve = nil
|
|
209
|
+
@resolver = nil
|
|
204
210
|
@specs = nil
|
|
205
211
|
@gem_version_promoter = nil
|
|
206
212
|
|
|
@@ -218,14 +224,26 @@ module Bundler
|
|
|
218
224
|
|
|
219
225
|
def current_dependencies
|
|
220
226
|
dependencies.select do |d|
|
|
221
|
-
d.should_include? && !d.gem_platforms(
|
|
227
|
+
d.should_include? && !d.gem_platforms([generic_local_platform]).empty?
|
|
222
228
|
end
|
|
223
229
|
end
|
|
224
230
|
|
|
231
|
+
def locked_dependencies
|
|
232
|
+
@locked_deps.values
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def new_deps
|
|
236
|
+
@new_deps ||= @dependencies - locked_dependencies
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def deleted_deps
|
|
240
|
+
@deleted_deps ||= locked_dependencies - @dependencies
|
|
241
|
+
end
|
|
242
|
+
|
|
225
243
|
def specs_for(groups)
|
|
226
|
-
|
|
244
|
+
return specs if groups.empty?
|
|
227
245
|
deps = dependencies_for(groups)
|
|
228
|
-
materialize(
|
|
246
|
+
materialize(deps)
|
|
229
247
|
end
|
|
230
248
|
|
|
231
249
|
def dependencies_for(groups)
|
|
@@ -241,20 +259,24 @@ module Bundler
|
|
|
241
259
|
#
|
|
242
260
|
# @return [SpecSet] resolved dependencies
|
|
243
261
|
def resolve
|
|
244
|
-
@resolve ||=
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
last_resolve
|
|
262
|
+
@resolve ||= if Bundler.frozen_bundle?
|
|
263
|
+
Bundler.ui.debug "Frozen, using resolution from the lockfile"
|
|
264
|
+
@locked_specs
|
|
265
|
+
elsif !unlocking? && nothing_changed?
|
|
266
|
+
if deleted_deps.any?
|
|
267
|
+
Bundler.ui.debug("Some dependencies were deleted, using a subset of the resolution from the lockfile")
|
|
268
|
+
SpecSet.new(filter_specs(@locked_specs, @dependencies - deleted_deps))
|
|
252
269
|
else
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
270
|
+
Bundler.ui.debug("Found no changes, using resolution from the lockfile")
|
|
271
|
+
if @locked_gems.may_include_redundant_platform_specific_gems?
|
|
272
|
+
SpecSet.new(filter_specs(@locked_specs, @dependencies))
|
|
273
|
+
else
|
|
274
|
+
@locked_specs
|
|
275
|
+
end
|
|
257
276
|
end
|
|
277
|
+
else
|
|
278
|
+
Bundler.ui.debug("Found changes from the lockfile, re-resolving dependencies because #{change_reason}")
|
|
279
|
+
resolver.start(expanded_dependencies)
|
|
258
280
|
end
|
|
259
281
|
end
|
|
260
282
|
|
|
@@ -267,6 +289,8 @@ module Bundler
|
|
|
267
289
|
end
|
|
268
290
|
|
|
269
291
|
def lock(file, preserve_unknown_sections = false)
|
|
292
|
+
return if Definition.no_lock
|
|
293
|
+
|
|
270
294
|
contents = to_lock
|
|
271
295
|
|
|
272
296
|
# Convert to \r\n if the existing lock has them
|
|
@@ -277,10 +301,7 @@ module Bundler
|
|
|
277
301
|
locked_major = @locked_bundler_version.segments.first
|
|
278
302
|
current_major = Gem::Version.create(Bundler::VERSION).segments.first
|
|
279
303
|
|
|
280
|
-
|
|
281
|
-
Bundler.ui.warn "Warning: the lockfile is being updated to Bundler #{current_major}, " \
|
|
282
|
-
"after which you will be unable to return to Bundler #{@locked_bundler_version.segments.first}."
|
|
283
|
-
end
|
|
304
|
+
updating_major = locked_major < current_major
|
|
284
305
|
end
|
|
285
306
|
|
|
286
307
|
preserve_unknown_sections ||= !updating_major && (Bundler.frozen_bundle? || !(unlocking? || @unlocking_bundler))
|
|
@@ -297,14 +318,6 @@ module Bundler
|
|
|
297
318
|
end
|
|
298
319
|
end
|
|
299
320
|
|
|
300
|
-
def locked_bundler_version
|
|
301
|
-
if @locked_bundler_version && @locked_bundler_version < Gem::Version.new(Bundler::VERSION)
|
|
302
|
-
new_version = Bundler::VERSION
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
new_version || @locked_bundler_version || Bundler::VERSION
|
|
306
|
-
end
|
|
307
|
-
|
|
308
321
|
def locked_ruby_version
|
|
309
322
|
return unless ruby_version
|
|
310
323
|
if @unlock[:ruby] || !@locked_ruby_version
|
|
@@ -344,7 +357,7 @@ module Bundler
|
|
|
344
357
|
"bundle config unset deployment"
|
|
345
358
|
end
|
|
346
359
|
msg << "\n\nIf this is a development machine, remove the #{Bundler.default_gemfile} " \
|
|
347
|
-
"freeze \nby running `#{suggested_command}`."
|
|
360
|
+
"freeze \nby running `#{suggested_command}`." if suggested_command
|
|
348
361
|
end
|
|
349
362
|
|
|
350
363
|
added = []
|
|
@@ -356,44 +369,28 @@ module Bundler
|
|
|
356
369
|
added.concat new_platforms.map {|p| "* platform: #{p}" }
|
|
357
370
|
deleted.concat deleted_platforms.map {|p| "* platform: #{p}" }
|
|
358
371
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
new_sources = gemfile_sources - @locked_sources
|
|
362
|
-
deleted_sources = @locked_sources - gemfile_sources
|
|
363
|
-
|
|
364
|
-
new_deps = @dependencies - @locked_deps.values
|
|
365
|
-
deleted_deps = @locked_deps.values - @dependencies
|
|
372
|
+
added.concat new_deps.map {|d| "* #{pretty_dep(d)}" } if new_deps.any?
|
|
373
|
+
deleted.concat deleted_deps.map {|d| "* #{pretty_dep(d)}" } if deleted_deps.any?
|
|
366
374
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
new_sources.reject! {|source| (source.path? && source.path.exist?) || equivalent_rubygems_remotes?(source) }
|
|
370
|
-
deleted_sources.reject! {|source| (source.path? && source.path.exist?) || equivalent_rubygems_remotes?(source) }
|
|
371
|
-
end
|
|
375
|
+
both_sources = Hash.new {|h, k| h[k] = [] }
|
|
376
|
+
@dependencies.each {|d| both_sources[d.name][0] = d }
|
|
372
377
|
|
|
373
|
-
|
|
374
|
-
if
|
|
375
|
-
added.concat new_sources.map {|source| "* source: #{source}" }
|
|
376
|
-
end
|
|
378
|
+
locked_dependencies.each do |d|
|
|
379
|
+
next if !Bundler.feature_flag.bundler_3_mode? && @locked_specs[d.name].empty?
|
|
377
380
|
|
|
378
|
-
|
|
379
|
-
deleted.concat deleted_sources.map {|source| "* source: #{source}" }
|
|
380
|
-
end
|
|
381
|
+
both_sources[d.name][1] = d
|
|
381
382
|
end
|
|
382
383
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
deleted.concat deleted_deps.map {|d| "* #{pretty_dep(d)}" }
|
|
386
|
-
end
|
|
384
|
+
both_sources.each do |name, (dep, lock_dep)|
|
|
385
|
+
next if dep.nil? || lock_dep.nil?
|
|
387
386
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
387
|
+
gemfile_source = dep.source || sources.default_source
|
|
388
|
+
lock_source = lock_dep.source || sources.default_source
|
|
389
|
+
next if lock_source.include?(gemfile_source)
|
|
391
390
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
lockfile_source_name = lock_source
|
|
396
|
-
changed << "* #{name} from `#{gemfile_source_name}` to `#{lockfile_source_name}`"
|
|
391
|
+
gemfile_source_name = dep.source ? gemfile_source.identifier : "no specified source"
|
|
392
|
+
lockfile_source_name = lock_dep.source ? lock_source.identifier : "no specified source"
|
|
393
|
+
changed << "* #{name} from `#{lockfile_source_name}` to `#{gemfile_source_name}`"
|
|
397
394
|
end
|
|
398
395
|
|
|
399
396
|
reason = change_reason
|
|
@@ -441,7 +438,7 @@ module Bundler
|
|
|
441
438
|
|
|
442
439
|
raise ProductionError, "Your bundle only supports platforms #{@platforms.map(&:to_s)} " \
|
|
443
440
|
"but your local platform is #{Bundler.local_platform}. " \
|
|
444
|
-
"Add the current platform to the lockfile with
|
|
441
|
+
"Add the current platform to the lockfile with\n`bundle lock --add-platform #{Bundler.local_platform}` and try again."
|
|
445
442
|
end
|
|
446
443
|
|
|
447
444
|
def add_platform(platform)
|
|
@@ -464,7 +461,7 @@ module Bundler
|
|
|
464
461
|
private :sources
|
|
465
462
|
|
|
466
463
|
def nothing_changed?
|
|
467
|
-
!@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes
|
|
464
|
+
!@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes
|
|
468
465
|
end
|
|
469
466
|
|
|
470
467
|
def unlocking?
|
|
@@ -473,6 +470,22 @@ module Bundler
|
|
|
473
470
|
|
|
474
471
|
private
|
|
475
472
|
|
|
473
|
+
def resolver
|
|
474
|
+
@resolver ||= begin
|
|
475
|
+
last_resolve = converge_locked_specs
|
|
476
|
+
remove_ruby_from_platforms_if_necessary!(current_dependencies)
|
|
477
|
+
Resolver.new(source_requirements, last_resolve, gem_version_promoter, additional_base_requirements_for_resolve(last_resolve), platforms)
|
|
478
|
+
end
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
def expanded_dependencies
|
|
482
|
+
@expanded_dependencies ||= dependencies + metadata_dependencies
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
def filter_specs(specs, deps)
|
|
486
|
+
SpecSet.new(specs).for(deps, false, platforms)
|
|
487
|
+
end
|
|
488
|
+
|
|
476
489
|
def materialize(dependencies)
|
|
477
490
|
specs = resolve.materialize(dependencies)
|
|
478
491
|
missing_specs = specs.missing_specs
|
|
@@ -487,14 +500,25 @@ module Bundler
|
|
|
487
500
|
"removed in order to install."
|
|
488
501
|
end
|
|
489
502
|
|
|
490
|
-
|
|
503
|
+
missing_specs_list = missing_specs.group_by(&:source).map do |source, missing_specs_for_source|
|
|
504
|
+
"#{missing_specs_for_source.map(&:full_name).join(", ")} in #{source}"
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
raise GemNotFound, "Could not find #{missing_specs_list.join(" nor ")}"
|
|
491
508
|
end
|
|
492
509
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
510
|
+
loop do
|
|
511
|
+
incomplete_specs = specs.incomplete_specs
|
|
512
|
+
break if incomplete_specs.empty?
|
|
513
|
+
|
|
514
|
+
Bundler.ui.debug("The lockfile does not have all gems needed for the current platform though, Bundler will still re-resolve dependencies")
|
|
515
|
+
@resolve = resolver.start(expanded_dependencies, :exclude_specs => incomplete_specs)
|
|
516
|
+
specs = resolve.materialize(dependencies)
|
|
496
517
|
end
|
|
497
518
|
|
|
519
|
+
bundler = sources.metadata_source.specs.search(Gem::Dependency.new("bundler", VERSION)).last
|
|
520
|
+
specs["bundler"] = bundler
|
|
521
|
+
|
|
498
522
|
specs
|
|
499
523
|
end
|
|
500
524
|
|
|
@@ -502,8 +526,22 @@ module Bundler
|
|
|
502
526
|
@remote && sources.non_global_rubygems_sources.all?(&:dependency_api_available?) && !sources.aggregate_global_source?
|
|
503
527
|
end
|
|
504
528
|
|
|
529
|
+
def pin_locally_available_names(source_requirements)
|
|
530
|
+
source_requirements.each_with_object({}) do |(name, original_source), new_source_requirements|
|
|
531
|
+
local_source = original_source.dup
|
|
532
|
+
local_source.local_only!
|
|
533
|
+
|
|
534
|
+
new_source_requirements[name] = if local_source.specs.search(name).any?
|
|
535
|
+
local_source
|
|
536
|
+
else
|
|
537
|
+
original_source
|
|
538
|
+
end
|
|
539
|
+
end
|
|
540
|
+
end
|
|
541
|
+
|
|
505
542
|
def current_ruby_platform_locked?
|
|
506
543
|
return false unless generic_local_platform == Gem::Platform::RUBY
|
|
544
|
+
return false if Bundler.settings[:force_ruby_platform] && !@platforms.include?(Gem::Platform::RUBY)
|
|
507
545
|
|
|
508
546
|
current_platform_locked?
|
|
509
547
|
end
|
|
@@ -536,12 +574,11 @@ module Bundler
|
|
|
536
574
|
[@new_platform, "you added a new platform to your gemfile"],
|
|
537
575
|
[@path_changes, "the gemspecs for path gems changed"],
|
|
538
576
|
[@local_changes, "the gemspecs for git local gems changed"],
|
|
539
|
-
[@locked_specs_incomplete_for_platform, "the lockfile does not have all gems needed for the current platform"],
|
|
540
577
|
].select(&:first).map(&:last).join(", ")
|
|
541
578
|
end
|
|
542
579
|
|
|
543
|
-
def pretty_dep(dep
|
|
544
|
-
SharedHelpers.pretty_dependency(dep
|
|
580
|
+
def pretty_dep(dep)
|
|
581
|
+
SharedHelpers.pretty_dependency(dep)
|
|
545
582
|
end
|
|
546
583
|
|
|
547
584
|
# Check if the specs of the given source changed
|
|
@@ -554,7 +591,7 @@ module Bundler
|
|
|
554
591
|
|
|
555
592
|
def dependencies_for_source_changed?(source, locked_source = source)
|
|
556
593
|
deps_for_source = @dependencies.select {|s| s.source == source }
|
|
557
|
-
locked_deps_for_source =
|
|
594
|
+
locked_deps_for_source = locked_dependencies.select {|dep| dep.source == locked_source }
|
|
558
595
|
|
|
559
596
|
deps_for_source.uniq.sort != locked_deps_for_source.sort
|
|
560
597
|
end
|
|
@@ -637,25 +674,14 @@ module Bundler
|
|
|
637
674
|
end
|
|
638
675
|
|
|
639
676
|
def converge_dependencies
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
# after locked_source and sources don't match, we still use locked_source.
|
|
645
|
-
if frozen && !locked_source.nil? &&
|
|
646
|
-
locked_source.respond_to?(:source) && locked_source.source.instance_of?(Source::Path) && locked_source.source.path.exist?
|
|
647
|
-
dep.source = locked_source.source
|
|
648
|
-
elsif dep.source
|
|
677
|
+
changes = false
|
|
678
|
+
|
|
679
|
+
@dependencies.each do |dep|
|
|
680
|
+
if dep.source
|
|
649
681
|
dep.source = sources.get(dep.source)
|
|
650
682
|
end
|
|
651
|
-
end
|
|
652
683
|
|
|
653
|
-
|
|
654
|
-
# We want to know if all match, but don't want to check all entries
|
|
655
|
-
# This means we need to return false if any dependency doesn't match
|
|
656
|
-
# the lock or doesn't exist in the lock.
|
|
657
|
-
@dependencies.each do |dependency|
|
|
658
|
-
unless locked_dep = @locked_deps[dependency.name]
|
|
684
|
+
unless locked_dep = @locked_deps[dep.name]
|
|
659
685
|
changes = true
|
|
660
686
|
next
|
|
661
687
|
end
|
|
@@ -666,11 +692,11 @@ module Bundler
|
|
|
666
692
|
# directive, the lockfile dependencies and resolved dependencies end up
|
|
667
693
|
# with a mismatch on #type. Work around that by setting the type on the
|
|
668
694
|
# dep from the lockfile.
|
|
669
|
-
locked_dep.instance_variable_set(:@type,
|
|
695
|
+
locked_dep.instance_variable_set(:@type, dep.type)
|
|
670
696
|
|
|
671
697
|
# We already know the name matches from the hash lookup
|
|
672
698
|
# so we only need to check the requirement now
|
|
673
|
-
changes ||=
|
|
699
|
+
changes ||= dep.requirement != locked_dep.requirement
|
|
674
700
|
end
|
|
675
701
|
|
|
676
702
|
changes
|
|
@@ -680,43 +706,50 @@ module Bundler
|
|
|
680
706
|
# commonly happen if the Gemfile has changed since the lockfile was last
|
|
681
707
|
# generated
|
|
682
708
|
def converge_locked_specs
|
|
683
|
-
|
|
709
|
+
converged = converge_specs(@locked_specs)
|
|
684
710
|
|
|
685
|
-
|
|
686
|
-
# and Gemfile.lock. If the Gemfile modified a dependency, but
|
|
687
|
-
# the gem in the Gemfile.lock still satisfies it, this is fine
|
|
688
|
-
# too.
|
|
689
|
-
@dependencies.each do |dep|
|
|
690
|
-
locked_dep = @locked_deps[dep.name]
|
|
711
|
+
resolve = SpecSet.new(converged.reject {|s| @unlock[:gems].include?(s.name) })
|
|
691
712
|
|
|
692
|
-
|
|
693
|
-
locked_dep = nil unless locked_dep == dep
|
|
713
|
+
diff = nil
|
|
694
714
|
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
@locked_specs.each do |s|
|
|
699
|
-
@unlock[:gems] << s.name if s.source == dep.source
|
|
700
|
-
end
|
|
715
|
+
# Now, we unlock any sources that do not have anymore gems pinned to it
|
|
716
|
+
sources.all_sources.each do |source|
|
|
717
|
+
next unless source.respond_to?(:unlock!)
|
|
701
718
|
|
|
702
|
-
|
|
703
|
-
|
|
719
|
+
unless resolve.any? {|s| s.source == source }
|
|
720
|
+
diff ||= @locked_specs.to_a - resolve.to_a
|
|
721
|
+
source.unlock! if diff.any? {|s| s.source == source }
|
|
704
722
|
end
|
|
705
723
|
end
|
|
706
724
|
|
|
725
|
+
resolve
|
|
726
|
+
end
|
|
727
|
+
|
|
728
|
+
def converge_specs(specs)
|
|
707
729
|
converged = []
|
|
708
|
-
|
|
709
|
-
|
|
730
|
+
|
|
731
|
+
deps = @dependencies.select do |dep|
|
|
732
|
+
specs[dep].any? {|s| s.satisfies?(dep) && (!dep.source || s.source.include?(dep.source)) }
|
|
733
|
+
end
|
|
734
|
+
|
|
735
|
+
@specs_that_changed_sources = []
|
|
736
|
+
|
|
737
|
+
specs.each do |s|
|
|
710
738
|
dep = @dependencies.find {|d| s.satisfies?(d) }
|
|
711
|
-
s.source = (dep && dep.source) || sources.get(s.source) unless multisource_allowed?
|
|
712
739
|
|
|
713
|
-
#
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
740
|
+
# Replace the locked dependency's source with the equivalent source from the Gemfile
|
|
741
|
+
s.source = if dep && dep.source
|
|
742
|
+
gemfile_source = dep.source
|
|
743
|
+
lockfile_source = s.source
|
|
717
744
|
|
|
718
|
-
|
|
719
|
-
|
|
745
|
+
@specs_that_changed_sources << s if gemfile_source != lockfile_source
|
|
746
|
+
|
|
747
|
+
gemfile_source
|
|
748
|
+
else
|
|
749
|
+
sources.get_with_fallback(s.source)
|
|
750
|
+
end
|
|
751
|
+
|
|
752
|
+
next if @unlock[:sources].include?(s.source.name)
|
|
720
753
|
|
|
721
754
|
# Path sources have special logic
|
|
722
755
|
if s.source.instance_of?(Source::Path) || s.source.instance_of?(Source::Gemspec)
|
|
@@ -725,8 +758,8 @@ module Bundler
|
|
|
725
758
|
rescue PathError, GitError
|
|
726
759
|
# if we won't need the source (according to the lockfile),
|
|
727
760
|
# don't error if the path/git source isn't available
|
|
728
|
-
next if
|
|
729
|
-
for(requested_dependencies, false
|
|
761
|
+
next if specs.
|
|
762
|
+
for(requested_dependencies, false).
|
|
730
763
|
none? {|locked_spec| locked_spec.source == s.source }
|
|
731
764
|
|
|
732
765
|
raise
|
|
@@ -741,79 +774,21 @@ module Bundler
|
|
|
741
774
|
s.dependencies.replace(new_spec.dependencies)
|
|
742
775
|
end
|
|
743
776
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
@locked_specs_incomplete_for_platform = !resolve.for(expand_dependencies(requested_dependencies & deps), true, true)
|
|
749
|
-
resolve = SpecSet.new(resolve.for(expand_dependencies(deps, true), false, false).reject{|s| @unlock[:gems].include?(s.name) })
|
|
750
|
-
diff = nil
|
|
751
|
-
|
|
752
|
-
# Now, we unlock any sources that do not have anymore gems pinned to it
|
|
753
|
-
sources.all_sources.each do |source|
|
|
754
|
-
next unless source.respond_to?(:unlock!)
|
|
755
|
-
|
|
756
|
-
unless resolve.any? {|s| s.source == source }
|
|
757
|
-
diff ||= @locked_specs.to_a - resolve.to_a
|
|
758
|
-
source.unlock! if diff.any? {|s| s.source == source }
|
|
777
|
+
if dep.nil? && requested_dependencies.find {|d| s.name == d.name }
|
|
778
|
+
@unlock[:gems] << s.name
|
|
779
|
+
else
|
|
780
|
+
converged << s
|
|
759
781
|
end
|
|
760
782
|
end
|
|
761
783
|
|
|
762
|
-
|
|
763
|
-
end
|
|
764
|
-
|
|
765
|
-
def in_locked_deps?(dep, locked_dep)
|
|
766
|
-
# Because the lockfile can't link a dep to a specific remote, we need to
|
|
767
|
-
# treat sources as equivalent anytime the locked dep has all the remotes
|
|
768
|
-
# that the Gemfile dep does.
|
|
769
|
-
locked_dep && locked_dep.source && dep.source && locked_dep.source.include?(dep.source)
|
|
770
|
-
end
|
|
771
|
-
|
|
772
|
-
def satisfies_locked_spec?(dep)
|
|
773
|
-
@locked_specs[dep].any? {|s| s.satisfies?(dep) && (!dep.source || s.source.include?(dep.source)) }
|
|
784
|
+
filter_specs(converged, deps)
|
|
774
785
|
end
|
|
775
786
|
|
|
776
787
|
def metadata_dependencies
|
|
777
|
-
@metadata_dependencies ||=
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
Dependency.new("RubyGems\0", Gem::VERSION),
|
|
782
|
-
]
|
|
783
|
-
end
|
|
784
|
-
end
|
|
785
|
-
|
|
786
|
-
def ruby_version_requirements(ruby_version)
|
|
787
|
-
return [] unless ruby_version
|
|
788
|
-
if ruby_version.patchlevel
|
|
789
|
-
[ruby_version.to_gem_version_with_patchlevel]
|
|
790
|
-
else
|
|
791
|
-
ruby_version.versions.map do |version|
|
|
792
|
-
requirement = Gem::Requirement.new(version)
|
|
793
|
-
if requirement.exact?
|
|
794
|
-
"~> #{version}.0"
|
|
795
|
-
else
|
|
796
|
-
requirement
|
|
797
|
-
end
|
|
798
|
-
end
|
|
799
|
-
end
|
|
800
|
-
end
|
|
801
|
-
|
|
802
|
-
def expand_dependencies(dependencies, remote = false)
|
|
803
|
-
deps = []
|
|
804
|
-
dependencies.each do |dep|
|
|
805
|
-
dep = Dependency.new(dep, ">= 0") unless dep.respond_to?(:name)
|
|
806
|
-
next unless remote || dep.current_platform?
|
|
807
|
-
target_platforms = dep.gem_platforms(remote ? @platforms : [generic_local_platform])
|
|
808
|
-
deps += expand_dependency_with_platforms(dep, target_platforms)
|
|
809
|
-
end
|
|
810
|
-
deps
|
|
811
|
-
end
|
|
812
|
-
|
|
813
|
-
def expand_dependency_with_platforms(dep, platforms)
|
|
814
|
-
platforms.map do |p|
|
|
815
|
-
DepProxy.get_proxy(dep, p)
|
|
816
|
-
end
|
|
788
|
+
@metadata_dependencies ||= [
|
|
789
|
+
Dependency.new("Ruby\0", Gem.ruby_version),
|
|
790
|
+
Dependency.new("RubyGems\0", Gem::VERSION),
|
|
791
|
+
]
|
|
817
792
|
end
|
|
818
793
|
|
|
819
794
|
def source_requirements
|
|
@@ -821,20 +796,34 @@ module Bundler
|
|
|
821
796
|
# specs will be available later when the resolver knows where to
|
|
822
797
|
# look for that gemspec (or its dependencies)
|
|
823
798
|
source_requirements = if precompute_source_requirements_for_indirect_dependencies?
|
|
824
|
-
|
|
799
|
+
all_requirements = source_map.all_requirements
|
|
800
|
+
all_requirements = pin_locally_available_names(all_requirements) if @prefer_local
|
|
801
|
+
{ :default => sources.default_source }.merge(all_requirements)
|
|
825
802
|
else
|
|
826
803
|
{ :default => Source::RubygemsAggregate.new(sources, source_map) }.merge(source_map.direct_requirements)
|
|
827
804
|
end
|
|
805
|
+
source_requirements.merge!(source_map.locked_requirements) unless @remote
|
|
828
806
|
metadata_dependencies.each do |dep|
|
|
829
807
|
source_requirements[dep.name] = sources.metadata_source
|
|
830
808
|
end
|
|
831
809
|
source_requirements[:default_bundler] = source_requirements["bundler"] || sources.default_source
|
|
832
810
|
source_requirements["bundler"] = sources.metadata_source # needs to come last to override
|
|
811
|
+
verify_changed_sources!
|
|
833
812
|
source_requirements
|
|
834
813
|
end
|
|
835
814
|
|
|
815
|
+
def verify_changed_sources!
|
|
816
|
+
@specs_that_changed_sources.each do |s|
|
|
817
|
+
if s.source.specs.search(s.name).empty?
|
|
818
|
+
raise GemNotFound, "Could not find gem '#{s.name}' in #{s.source}"
|
|
819
|
+
end
|
|
820
|
+
end
|
|
821
|
+
end
|
|
822
|
+
|
|
836
823
|
def requested_groups
|
|
837
|
-
groups - Bundler.settings[:without] - @optional_groups + Bundler.settings[:with]
|
|
824
|
+
values = groups - Bundler.settings[:without] - @optional_groups + Bundler.settings[:with]
|
|
825
|
+
values &= Bundler.settings[:only] unless Bundler.settings[:only].empty?
|
|
826
|
+
values
|
|
838
827
|
end
|
|
839
828
|
|
|
840
829
|
def lockfiles_equal?(current, proposed, preserve_unknown_sections)
|
|
@@ -861,28 +850,26 @@ module Bundler
|
|
|
861
850
|
end
|
|
862
851
|
end
|
|
863
852
|
|
|
864
|
-
def additional_base_requirements_for_resolve
|
|
853
|
+
def additional_base_requirements_for_resolve(last_resolve)
|
|
865
854
|
return [] unless @locked_gems && unlocking? && !sources.expired_sources?(@locked_gems.sources)
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
dependency = dependencies_by_name[name]
|
|
870
|
-
next requirements if @locked_gems.dependencies[name] != dependency
|
|
871
|
-
next requirements if dependency && dependency.source.is_a?(Source::Path)
|
|
872
|
-
dep = Gem::Dependency.new(name, ">= #{locked_spec.version}")
|
|
873
|
-
requirements[name] = DepProxy.get_proxy(dep, locked_spec.platform)
|
|
874
|
-
requirements
|
|
875
|
-
end.values
|
|
855
|
+
converge_specs(@originally_locked_specs - last_resolve).map do |locked_spec|
|
|
856
|
+
Dependency.new(locked_spec.name, ">= #{locked_spec.version}")
|
|
857
|
+
end.uniq
|
|
876
858
|
end
|
|
877
859
|
|
|
878
|
-
def
|
|
879
|
-
return
|
|
860
|
+
def remove_ruby_from_platforms_if_necessary!(dependencies)
|
|
861
|
+
return if Bundler.frozen_bundle? ||
|
|
862
|
+
Bundler.local_platform == Gem::Platform::RUBY ||
|
|
863
|
+
!platforms.include?(Gem::Platform::RUBY) ||
|
|
864
|
+
(@new_platform && platforms.last == Gem::Platform::RUBY) ||
|
|
865
|
+
!@originally_locked_specs.incomplete_ruby_specs?(dependencies)
|
|
880
866
|
|
|
881
|
-
|
|
867
|
+
remove_platform(Gem::Platform::RUBY)
|
|
868
|
+
add_current_platform
|
|
882
869
|
end
|
|
883
870
|
|
|
884
871
|
def source_map
|
|
885
|
-
@source_map ||= SourceMap.new(sources, dependencies)
|
|
872
|
+
@source_map ||= SourceMap.new(sources, dependencies, @locked_specs)
|
|
886
873
|
end
|
|
887
874
|
end
|
|
888
875
|
end
|