bundler 4.1.0.beta1 → 4.1.0.beta3
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/lib/bundler/build_metadata.rb +2 -2
- data/lib/bundler/cli/common.rb +4 -0
- data/lib/bundler/cli/doctor/ssl.rb +4 -8
- data/lib/bundler/cli/info.rb +1 -1
- data/lib/bundler/cli/lock.rb +19 -4
- data/lib/bundler/cli/open.rb +7 -1
- data/lib/bundler/cli/outdated.rb +8 -10
- data/lib/bundler/cli/platform.rb +15 -1
- data/lib/bundler/cli/show.rb +5 -1
- data/lib/bundler/cli/update.rb +1 -1
- data/lib/bundler/cli.rb +22 -7
- data/lib/bundler/constants.rb +1 -1
- data/lib/bundler/definition.rb +102 -16
- data/lib/bundler/dsl.rb +76 -5
- data/lib/bundler/fetcher/downloader.rb +14 -4
- data/lib/bundler/fetcher.rb +4 -1
- data/lib/bundler/gem_helper.rb +1 -1
- data/lib/bundler/installer/parallel_installer.rb +2 -1
- data/lib/bundler/installer.rb +5 -1
- data/lib/bundler/lazy_specification.rb +1 -1
- data/lib/bundler/lockfile_generator.rb +6 -0
- data/lib/bundler/lockfile_parser.rb +18 -2
- data/lib/bundler/man/bundle-info.1 +4 -1
- data/lib/bundler/man/bundle-info.1.ronn +4 -0
- data/lib/bundler/man/bundle-lock.1 +2 -2
- data/lib/bundler/man/bundle-lock.1.ronn +4 -3
- data/lib/bundler/man/bundle-open.1 +4 -1
- data/lib/bundler/man/bundle-open.1.ronn +4 -1
- data/lib/bundler/man/bundle-outdated.1 +1 -1
- data/lib/bundler/man/bundle-outdated.1.ronn +1 -1
- data/lib/bundler/man/bundle-show.1 +4 -1
- data/lib/bundler/man/bundle-show.1.ronn +4 -0
- data/lib/bundler/man/bundle-update.1 +3 -3
- data/lib/bundler/man/bundle-update.1.ronn +5 -3
- data/lib/bundler/man/gemfile.5 +43 -2
- data/lib/bundler/man/gemfile.5.ronn +69 -4
- data/lib/bundler/match_platform.rb +12 -3
- data/lib/bundler/materialization.rb +3 -1
- data/lib/bundler/override.rb +83 -2
- data/lib/bundler/resolver.rb +28 -4
- data/lib/bundler/rubygems_ext.rb +15 -4
- data/lib/bundler/self_manager.rb +15 -9
- data/lib/bundler/settings.rb +10 -26
- data/lib/bundler/source/git/git_proxy.rb +39 -2
- data/lib/bundler/source/git.rb +34 -7
- data/lib/bundler/source/rubygems.rb +2 -2
- data/lib/bundler/source/rubygems_aggregate.rb +1 -1
- data/lib/bundler/source_map.rb +7 -3
- data/lib/bundler/spec_set.rb +8 -4
- data/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt +1 -1
- data/lib/bundler/vendor/fileutils/lib/fileutils.rb +54 -29
- data/lib/bundler/version.rb +1 -1
- data/lib/bundler.rb +1 -3
- data/lib/rubygems/compact_index_client/http_fetcher.rb +5 -3
- data/lib/rubygems/compact_index_client/parser.rb +1 -0
- data/lib/rubygems/vendor/uri/lib/uri/common.rb +1 -1
- data/lib/rubygems/vendor/uri/lib/uri/generic.rb +15 -3
- data/lib/rubygems/vendor/uri/lib/uri/version.rb +1 -1
- data/lib/rubygems/yaml_serializer.rb +131 -103
- metadata +391 -390
|
@@ -359,6 +359,20 @@ Git repositories support a number of additional options.
|
|
|
359
359
|
Specify `submodules: true` to cause bundler to expand any
|
|
360
360
|
submodules included in the git repository
|
|
361
361
|
|
|
362
|
+
* `sparse_checkout`:
|
|
363
|
+
Specify a directory, or an array of directories, to include when
|
|
364
|
+
checking out a git repository. Uses git's sparse-checkout feature
|
|
365
|
+
(requires git 2.25+) to only check out the specified directories,
|
|
366
|
+
reducing disk usage for large repos. Unless `glob` is given, the
|
|
367
|
+
`.gemspec` is looked up within those directories.
|
|
368
|
+
|
|
369
|
+
gem "foo", git: "https://github.com/org/monorepo.git",
|
|
370
|
+
sparse_checkout: "packages/foo"
|
|
371
|
+
|
|
372
|
+
Files at the top level of the repository are always checked out.
|
|
373
|
+
On git versions below 2.25, the entire repository will be checked
|
|
374
|
+
out with a warning.
|
|
375
|
+
|
|
362
376
|
If a git repository contains multiple `.gemspecs`, each `.gemspec`
|
|
363
377
|
represents a gem located at the same place in the file system as
|
|
364
378
|
the `.gemspec`.
|
|
@@ -587,8 +601,7 @@ one of:
|
|
|
587
601
|
A per-gem override on the same field takes precedence over an `:all` override.
|
|
588
602
|
`:all + version:` is rejected: version requirements only make sense per gem.
|
|
589
603
|
|
|
590
|
-
|
|
591
|
-
same `target` and `field` twice is an error.
|
|
604
|
+
Declaring the same `target` and `field` twice is an error.
|
|
592
605
|
|
|
593
606
|
source "https://rubygems.org"
|
|
594
607
|
|
|
@@ -610,11 +623,63 @@ same `target` and `field` twice is an error.
|
|
|
610
623
|
gem "rails", "~> 7.0"
|
|
611
624
|
|
|
612
625
|
The override only affects resolution and the install-time Ruby/RubyGems
|
|
613
|
-
compatibility checks; `Gemfile.lock`
|
|
614
|
-
versions, not the rewritten requirements.
|
|
626
|
+
compatibility checks; the gems in `Gemfile.lock` continue to reflect the
|
|
627
|
+
resolved versions, not the rewritten requirements. The overrides themselves are
|
|
628
|
+
recorded in the lockfile's `OVERRIDES` section, one line per field, sorted:
|
|
629
|
+
|
|
630
|
+
OVERRIDES
|
|
631
|
+
"rails", version: ">= 8.0"
|
|
632
|
+
:all, required_ruby_version: :ignore_upper
|
|
633
|
+
|
|
634
|
+
Requirements are recorded in normalized form, so `">=8.0"` and `">= 8.0"` are
|
|
635
|
+
the same override. A frozen install fails when the Gemfile's overrides differ
|
|
636
|
+
from the recorded ones. Adding or removing a `:all` or metadata override
|
|
637
|
+
rewrites the section and keeps the locked versions. The exception is a locked
|
|
638
|
+
gem that is not installed and whose metadata rejects the current Ruby. Bundler
|
|
639
|
+
re-resolves it, as it does without the override. Run `bundle update` to
|
|
640
|
+
re-resolve under the override. When resolution still fails,
|
|
615
641
|
Bundler appends the active overrides (with their Gemfile location) to the
|
|
616
642
|
error message so it is clear which override shaped the constraint set.
|
|
617
643
|
|
|
644
|
+
### DROPPING OR REPLACING A DEPENDENCY
|
|
645
|
+
|
|
646
|
+
`with:` and `in:` rewrite a single dependency of a single gem:
|
|
647
|
+
|
|
648
|
+
override <target>, with: nil, in: <gem>
|
|
649
|
+
override <target>, with: <replacement>, in: <gem>, version: <requirement>
|
|
650
|
+
|
|
651
|
+
`with: nil` removes the dependency of `<gem>` on `<target>`, so `<target>` is
|
|
652
|
+
not installed unless something else still requires it. `with: <replacement>`
|
|
653
|
+
makes `<gem>` depend on `<replacement>` instead of `<target>`. The requirement
|
|
654
|
+
`<gem>` put on `<target>` is discarded, and the optional `version:` gives the
|
|
655
|
+
replacement its own. Both forms apply whether `<gem>` is a direct or a
|
|
656
|
+
transitive dependency.
|
|
657
|
+
|
|
658
|
+
`in:` is required, and so is `with:` once `in:` is given. To rewrite the
|
|
659
|
+
same dependency of several gems, write one `override` per gem. It is an error
|
|
660
|
+
to declare the same `<target>` and `in:` twice, to drop a gem that the
|
|
661
|
+
Gemfile requires directly, or to replace a gem that another gem still
|
|
662
|
+
requires, since both would then be in the bundle.
|
|
663
|
+
|
|
664
|
+
# jekyll only loads em-websocket for `jekyll serve --livereload`.
|
|
665
|
+
override "em-websocket", with: nil, in: "jekyll"
|
|
666
|
+
|
|
667
|
+
# Use an API-compatible gem that provides the same files.
|
|
668
|
+
override "foo", with: "foo-lite", in: "bar"
|
|
669
|
+
|
|
670
|
+
Bundler does not check what a gem loads at runtime. If `<gem>` requires
|
|
671
|
+
`<target>` unconditionally, dropping it makes that require raise `LoadError`,
|
|
672
|
+
as with jekyll's dependency on jekyll-sass-converter or the adapters that
|
|
673
|
+
faraday 1.x requires on load. A replacement only works when it provides the
|
|
674
|
+
files `<gem>` requires, and it does not take over the name of `<target>`:
|
|
675
|
+
`Gem.loaded_specs` has no entry for `<target>`, and `gem "<target>"` fails.
|
|
676
|
+
|
|
677
|
+
`Gemfile.lock` keeps listing `<target>` under `<gem>` as the gem publishes it,
|
|
678
|
+
while the locked gems leave `<target>` out or include `<replacement>`, and the
|
|
679
|
+
`OVERRIDES` section records the rewrite so tools that read the lockfile can
|
|
680
|
+
apply it. A frozen install accepts that lockfile as long as the Gemfile still
|
|
681
|
+
has the override. Removing the override makes Bundler resolve `<target>` again.
|
|
682
|
+
|
|
618
683
|
## SOURCE PRIORITY
|
|
619
684
|
|
|
620
685
|
When attempting to locate a gem to satisfy a gem requirement,
|
|
@@ -27,9 +27,18 @@ module Bundler
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def self.prefer_content_addressable(matching)
|
|
30
|
-
|
|
31
|
-
return matching
|
|
32
|
-
|
|
30
|
+
first = matching.index {|s| Gem::ContentAddress.content_addressed?(s, validate_ruby_abi: false) }
|
|
31
|
+
return matching unless first
|
|
32
|
+
|
|
33
|
+
addressable = [matching[first]]
|
|
34
|
+
non_addressable = matching.take(first)
|
|
35
|
+
matching.drop(first + 1).each do |s|
|
|
36
|
+
if Gem::ContentAddress.content_addressed?(s, validate_ruby_abi: false)
|
|
37
|
+
addressable << s
|
|
38
|
+
else
|
|
39
|
+
non_addressable << s
|
|
40
|
+
end
|
|
41
|
+
end
|
|
33
42
|
compatible = addressable.select(&:matches_current_metadata?)
|
|
34
43
|
return compatible if compatible.any?
|
|
35
44
|
|
|
@@ -30,7 +30,9 @@ module Bundler
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def dependencies
|
|
33
|
-
|
|
33
|
+
spec = materialized_spec || specs.first
|
|
34
|
+
overrides = specs.first.overrides if specs.first.is_a?(LazySpecification)
|
|
35
|
+
Override.rewrite_dependencies(overrides, spec.name, spec.runtime_dependencies).map {|d| [d, platform] }
|
|
34
36
|
end
|
|
35
37
|
|
|
36
38
|
def materialized_spec
|
data/lib/bundler/override.rb
CHANGED
|
@@ -9,6 +9,20 @@ module Bundler
|
|
|
9
9
|
overrides.find {|o| o.target == :all && o.field == field }
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
+
# Drops or replaces the dependencies of the `in:` gem, passed as `from`,
|
|
13
|
+
# that a `with:`/`in:` override targets. The requirement `from` put on the
|
|
14
|
+
# replaced gem is discarded.
|
|
15
|
+
def self.rewrite_dependencies(overrides, from, dependencies)
|
|
16
|
+
return dependencies if overrides.nil? || overrides.none? {|o| o.from == from }
|
|
17
|
+
|
|
18
|
+
dependencies.filter_map do |dep|
|
|
19
|
+
override = overrides.find {|o| o.from == from && o.target == dep.name }
|
|
20
|
+
next dep unless override
|
|
21
|
+
next unless override.operation
|
|
22
|
+
Gem::Dependency.new(override.operation, *Array(override.requirement))
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
12
26
|
# Attach the given overrides onto every LazySpecification in `specs` so
|
|
13
27
|
# downstream consumers (LazySpecification#choose_compatible, the install-
|
|
14
28
|
# time compatibility check, etc.) can read the override list off the spec
|
|
@@ -19,12 +33,14 @@ module Bundler
|
|
|
19
33
|
specs.each {|s| s.overrides = overrides if s.is_a?(LazySpecification) }
|
|
20
34
|
end
|
|
21
35
|
|
|
22
|
-
attr_reader :target, :field, :operation, :source_location
|
|
36
|
+
attr_reader :target, :field, :operation, :from, :requirement, :source_location
|
|
23
37
|
|
|
24
|
-
def initialize(target, field, operation, source_location: nil)
|
|
38
|
+
def initialize(target, field, operation, from: nil, requirement: nil, source_location: nil)
|
|
25
39
|
@target = target
|
|
26
40
|
@field = field
|
|
27
41
|
@operation = operation
|
|
42
|
+
@from = from
|
|
43
|
+
@requirement = requirement
|
|
28
44
|
@source_location = source_location
|
|
29
45
|
end
|
|
30
46
|
|
|
@@ -33,7 +49,72 @@ module Bundler
|
|
|
33
49
|
"#{File.basename(@source_location.path)}:#{@source_location.lineno}"
|
|
34
50
|
end
|
|
35
51
|
|
|
52
|
+
def to_s
|
|
53
|
+
location = source_location_label
|
|
54
|
+
"override #{to_lock}#{" (declared at #{location})" if location}"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# The override as recorded in the OVERRIDES section of Gemfile.lock: the
|
|
58
|
+
# `override` arguments with requirements normalized, so equivalent
|
|
59
|
+
# spellings ("<=3", "<= 3") record the same line.
|
|
60
|
+
def to_lock
|
|
61
|
+
target = @target == :all ? ":all" : @target.inspect
|
|
62
|
+
fields = if @from
|
|
63
|
+
["with: #{@operation.inspect}", "in: #{@from.inspect}", *("version: #{self.class.normalize(@requirement).inspect}" if @requirement)]
|
|
64
|
+
else
|
|
65
|
+
["#{@field}: #{self.class.normalize(@operation).inspect}"]
|
|
66
|
+
end
|
|
67
|
+
[target, *fields].join(", ")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
LOCK_VALUE = /"[^"\\]*"|:[a-z_]+|nil/
|
|
71
|
+
LOCK_FIELD = /, ([a-z_]+): (#{LOCK_VALUE})/
|
|
72
|
+
LOCK_LINE = /\A(:all|"[a-zA-Z0-9._-]+")((?:#{LOCK_FIELD})+)\z/
|
|
73
|
+
private_constant :LOCK_VALUE, :LOCK_FIELD, :LOCK_LINE
|
|
74
|
+
|
|
75
|
+
# Parses a line written by #to_lock. The DSL only accepts valid gem names,
|
|
76
|
+
# and requirements are normalized, so no recorded value contains a quote
|
|
77
|
+
# or a backslash and the line can be read without evaluating it.
|
|
78
|
+
def self.from_lock(line)
|
|
79
|
+
match = LOCK_LINE.match(line)
|
|
80
|
+
raise ArgumentError, "invalid override in lockfile: #{line.inspect}" unless match
|
|
81
|
+
|
|
82
|
+
target = match[1] == ":all" ? :all : match[1][1..-2]
|
|
83
|
+
fields = match[2].scan(LOCK_FIELD).map {|key, value| [key.to_sym, parse_lock_value(value)] }
|
|
84
|
+
keys = fields.map(&:first)
|
|
85
|
+
raise ArgumentError, "invalid override in lockfile: #{line.inspect}" unless keys.uniq.size == keys.size
|
|
86
|
+
fields = fields.to_h
|
|
87
|
+
|
|
88
|
+
if fields.key?(:in)
|
|
89
|
+
unless (keys - [:in, :with, :version]).empty? && fields.key?(:with) && fields[:in].is_a?(String)
|
|
90
|
+
raise ArgumentError, "invalid override in lockfile: #{line.inspect}"
|
|
91
|
+
end
|
|
92
|
+
new(target, :with, fields[:with], from: fields[:in], requirement: normalize(fields[:version]))
|
|
93
|
+
else
|
|
94
|
+
field, operation = fields.first
|
|
95
|
+
unless fields.size == 1 && Dsl::SUPPORTED_OVERRIDE_FIELDS.include?(field)
|
|
96
|
+
raise ArgumentError, "invalid override in lockfile: #{line.inspect}"
|
|
97
|
+
end
|
|
98
|
+
new(target, field, normalize(operation))
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def self.parse_lock_value(value)
|
|
103
|
+
case value
|
|
104
|
+
when "nil" then nil
|
|
105
|
+
when /\A:/ then value[1..].to_sym
|
|
106
|
+
else value[1..-2]
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
private_class_method :parse_lock_value
|
|
110
|
+
|
|
111
|
+
def self.normalize(operation)
|
|
112
|
+
operation.is_a?(String) ? Gem::Requirement.new(operation).to_s : operation
|
|
113
|
+
end
|
|
114
|
+
|
|
36
115
|
def apply_to(requirement)
|
|
116
|
+
raise ArgumentError, "#{self} rewrites a dependency and has no requirement to apply" if @from
|
|
117
|
+
|
|
37
118
|
case operation
|
|
38
119
|
when nil
|
|
39
120
|
Gem::Requirement.default
|
data/lib/bundler/resolver.rb
CHANGED
|
@@ -71,6 +71,7 @@ module Bundler
|
|
|
71
71
|
@cached_dependencies = Hash.new do |dependencies, package|
|
|
72
72
|
dependencies[package] = Hash.new do |versions, version|
|
|
73
73
|
deps = version.dependencies.reject {|d| d.name == package.name }
|
|
74
|
+
deps = Override.rewrite_dependencies(@base.overrides, package.name, deps)
|
|
74
75
|
deps = apply_metadata_overrides(deps, package.name)
|
|
75
76
|
versions[version] = to_dependency_hash(deps, @packages)
|
|
76
77
|
end
|
|
@@ -88,6 +89,7 @@ module Bundler
|
|
|
88
89
|
solver = Gem::PubGrub::VersionSolver.new(source: self, root: root, strategy: Strategy.new(self), logger: logger)
|
|
89
90
|
result = solver.solve
|
|
90
91
|
resolved_specs = result.flat_map {|package, version| version.to_specs(package, @most_specific_locked_platform) }
|
|
92
|
+
check_replaced_gems(resolved_specs)
|
|
91
93
|
Override.attach(resolved_specs, @base.overrides)
|
|
92
94
|
spec_set = SpecSet.new(resolved_specs).specs_with_additional_variants_from(@base.locked_specs)
|
|
93
95
|
@cooldown_skipped = cooldown_skipped_summary(spec_set)
|
|
@@ -137,15 +139,37 @@ module Bundler
|
|
|
137
139
|
raise SolveFailure.new(explanation)
|
|
138
140
|
end
|
|
139
141
|
|
|
142
|
+
# A replacement is only useful when it provides the files of the gem it
|
|
143
|
+
# replaces, so the two cannot be loaded side by side.
|
|
144
|
+
def check_replaced_gems(specs)
|
|
145
|
+
names = specs.map(&:name)
|
|
146
|
+
|
|
147
|
+
@base.overrides.each do |override|
|
|
148
|
+
next unless override.from && override.operation
|
|
149
|
+
next unless names.include?(override.target) && names.include?(override.operation)
|
|
150
|
+
next unless specs.any? {|s| s.name == override.from && s.dependencies.any? {|d| d.name == override.target } }
|
|
151
|
+
|
|
152
|
+
dependents = specs.select do |spec|
|
|
153
|
+
Override.rewrite_dependencies(@base.overrides, spec.name, spec.dependencies).any? {|d| d.name == override.target }
|
|
154
|
+
end.map(&:name).uniq
|
|
155
|
+
|
|
156
|
+
lines = ["#{override} would leave both #{override.target} and #{override.operation} in the bundle because #{override.target} is still required by:"]
|
|
157
|
+
lines << " the Gemfile" if @base.requirements.any? {|d| d.name == override.target }
|
|
158
|
+
lines.concat(dependents.map {|name| " #{name}" })
|
|
159
|
+
if dependents.any?
|
|
160
|
+
lines << "Replace it for each of them, for example:"
|
|
161
|
+
lines << " #{Override.new(override.target, :with, override.operation, from: dependents.first)}"
|
|
162
|
+
end
|
|
163
|
+
raise GemfileError, lines.join("\n")
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
140
167
|
def override_diagnostic_summary
|
|
141
168
|
return nil if @base.overrides.empty?
|
|
142
169
|
|
|
143
170
|
lines = ["Bundler applied the following overrides while resolving:"]
|
|
144
171
|
@base.overrides.each do |override|
|
|
145
|
-
|
|
146
|
-
location = override.source_location_label
|
|
147
|
-
lines << " override #{target}, #{override.field}: #{override.operation.inspect}" \
|
|
148
|
-
"#{location ? " (declared at #{location})" : ""}"
|
|
172
|
+
lines << " #{override}"
|
|
149
173
|
end
|
|
150
174
|
"\n\n#{lines.join("\n")}"
|
|
151
175
|
end
|
data/lib/bundler/rubygems_ext.rb
CHANGED
|
@@ -86,11 +86,22 @@ module Gem
|
|
|
86
86
|
end
|
|
87
87
|
end
|
|
88
88
|
|
|
89
|
-
def open_file_with_lock(path
|
|
89
|
+
def open_file_with_lock(path)
|
|
90
90
|
file_lock = "#{path}.lock"
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
|
|
92
|
+
loop do
|
|
93
|
+
open_file_with_flock(file_lock) do |io|
|
|
94
|
+
# The previous holder removed the file while we were waiting on it,
|
|
95
|
+
# so leave this block and try again on the file now at the lock path
|
|
96
|
+
next unless File.identical?(io, file_lock)
|
|
97
|
+
|
|
98
|
+
begin
|
|
99
|
+
return yield io
|
|
100
|
+
ensure
|
|
101
|
+
FileUtils.rm_f file_lock
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
94
105
|
end
|
|
95
106
|
end
|
|
96
107
|
end
|
data/lib/bundler/self_manager.rb
CHANGED
|
@@ -30,8 +30,8 @@ module Bundler
|
|
|
30
30
|
install_and_restart_with(restart_version)
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
def update_bundler_and_restart_with_it_if_needed(target)
|
|
34
|
-
spec = resolve_update_version_from(target)
|
|
33
|
+
def update_bundler_and_restart_with_it_if_needed(target, pre: false)
|
|
34
|
+
spec = resolve_update_version_from(target, pre: pre)
|
|
35
35
|
return unless spec
|
|
36
36
|
|
|
37
37
|
version = spec.version
|
|
@@ -108,9 +108,9 @@ module Bundler
|
|
|
108
108
|
lockfile_version
|
|
109
109
|
end
|
|
110
110
|
|
|
111
|
-
def resolve_update_version_from(target)
|
|
111
|
+
def resolve_update_version_from(target, pre: false)
|
|
112
112
|
requirement = Gem::Requirement.new(target)
|
|
113
|
-
update_candidate = find_latest_matching_spec(requirement)
|
|
113
|
+
update_candidate = find_latest_matching_spec(requirement, pre: pre)
|
|
114
114
|
|
|
115
115
|
if update_candidate.nil?
|
|
116
116
|
raise InvalidOption, "The `bundle update --bundler` target version (#{target}) does not exist"
|
|
@@ -137,18 +137,24 @@ module Bundler
|
|
|
137
137
|
end
|
|
138
138
|
end
|
|
139
139
|
|
|
140
|
-
def find_latest_matching_spec(requirement)
|
|
140
|
+
def find_latest_matching_spec(requirement, pre: false)
|
|
141
141
|
Bundler.configure
|
|
142
|
-
|
|
142
|
+
# A bare `bundle update --bundler` must stay on releases, like `gem update
|
|
143
|
+
# --system`, so only `--pre` or a prerelease requirement opts into one.
|
|
144
|
+
allow_prerelease = pre || requirement.prerelease?
|
|
145
|
+
|
|
146
|
+
local_result = find_latest_matching_spec_from_collection(local_specs, requirement, allow_prerelease)
|
|
143
147
|
return local_result if local_result && requirement.specific?
|
|
144
148
|
|
|
145
|
-
remote_result = find_latest_matching_spec_from_collection(remote_specs, requirement)
|
|
149
|
+
remote_result = find_latest_matching_spec_from_collection(remote_specs, requirement, allow_prerelease)
|
|
146
150
|
return remote_result if local_result.nil?
|
|
147
151
|
|
|
148
|
-
[local_result, remote_result].max
|
|
152
|
+
[local_result, remote_result].compact.max
|
|
149
153
|
end
|
|
150
154
|
|
|
151
|
-
def find_latest_matching_spec_from_collection(specs, requirement)
|
|
155
|
+
def find_latest_matching_spec_from_collection(specs, requirement, allow_prerelease)
|
|
156
|
+
specs = specs.reject {|spec| spec.version.prerelease? } unless allow_prerelease
|
|
157
|
+
|
|
152
158
|
specs.sort.reverse_each.find {|spec| requirement.satisfied_by?(spec.version) }
|
|
153
159
|
end
|
|
154
160
|
|
data/lib/bundler/settings.rb
CHANGED
|
@@ -896,32 +896,16 @@ module Bundler
|
|
|
896
896
|
"#{prefix}#{uri}#{suffix}"
|
|
897
897
|
end
|
|
898
898
|
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
else
|
|
910
|
-
raise ArgumentError, "Invalid key: #{key.inspect}"
|
|
911
|
-
end
|
|
912
|
-
end
|
|
913
|
-
else
|
|
914
|
-
def self.key_to_s(key)
|
|
915
|
-
case key
|
|
916
|
-
when String
|
|
917
|
-
key
|
|
918
|
-
when Symbol
|
|
919
|
-
key.to_s
|
|
920
|
-
when Gem::URI::HTTP
|
|
921
|
-
key.to_s
|
|
922
|
-
else
|
|
923
|
-
raise ArgumentError, "Invalid key: #{key.inspect}"
|
|
924
|
-
end
|
|
899
|
+
def self.key_to_s(key)
|
|
900
|
+
case key
|
|
901
|
+
when String
|
|
902
|
+
key
|
|
903
|
+
when Symbol
|
|
904
|
+
key.name
|
|
905
|
+
when Gem::URI::HTTP
|
|
906
|
+
key.to_s
|
|
907
|
+
else
|
|
908
|
+
raise ArgumentError, "Invalid key: #{key.inspect}"
|
|
925
909
|
end
|
|
926
910
|
end
|
|
927
911
|
end
|
|
@@ -54,7 +54,7 @@ module Bundler
|
|
|
54
54
|
# All actions required by the Git source is encapsulated in this
|
|
55
55
|
# object.
|
|
56
56
|
class GitProxy
|
|
57
|
-
attr_accessor :path, :uri, :branch, :tag, :ref, :explicit_ref
|
|
57
|
+
attr_accessor :path, :uri, :branch, :tag, :ref, :explicit_ref, :sparse_checkout
|
|
58
58
|
attr_writer :revision
|
|
59
59
|
|
|
60
60
|
def self.version
|
|
@@ -95,6 +95,7 @@ module Bundler
|
|
|
95
95
|
@revision = revision
|
|
96
96
|
@git = git
|
|
97
97
|
@commit_ref = nil
|
|
98
|
+
@sparse_checkout = options["sparse_checkout"]
|
|
98
99
|
end
|
|
99
100
|
|
|
100
101
|
def revision
|
|
@@ -159,6 +160,8 @@ module Bundler
|
|
|
159
160
|
end
|
|
160
161
|
end
|
|
161
162
|
|
|
163
|
+
setup_sparse_checkout(destination)
|
|
164
|
+
|
|
162
165
|
ref = @commit_ref || (locked_to_full_sha? && @revision)
|
|
163
166
|
if ref
|
|
164
167
|
git "config", "uploadpack.allowAnySHA1InWant", "true", dir: path.to_s if @commit_ref.nil? && needs_allow_any_sha1_in_want?
|
|
@@ -507,7 +510,15 @@ module Bundler
|
|
|
507
510
|
|
|
508
511
|
return ["git", *opts, *cmd] unless dir
|
|
509
512
|
|
|
510
|
-
|
|
513
|
+
# With safe.bareRepository=explicit, git refuses to discover a bare
|
|
514
|
+
# repository from -C, so the cache clone is named with --git-dir.
|
|
515
|
+
# Working trees, like a local override, still go through -C.
|
|
516
|
+
location = bare_repo?(dir) ? "--git-dir" : "-C"
|
|
517
|
+
["git", location, dir.to_s, *opts, *cmd]
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
def bare_repo?(dir)
|
|
521
|
+
File.exist?(File.join(dir, "objects")) && File.exist?(File.join(dir, "HEAD"))
|
|
511
522
|
end
|
|
512
523
|
|
|
513
524
|
def extra_clone_args
|
|
@@ -565,6 +576,32 @@ module Bundler
|
|
|
565
576
|
def supports_cloning_with_no_tags?
|
|
566
577
|
@supports_cloning_with_no_tags ||= Gem::Version.new(version) >= Gem::Version.new("2.14.0-rc0")
|
|
567
578
|
end
|
|
579
|
+
|
|
580
|
+
def supports_sparse_checkout?
|
|
581
|
+
@supports_sparse_checkout ||= Gem::Version.new(version) >= Gem::Version.new("2.25.0")
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
def supports_sparse_checkout_set_with_cone?
|
|
585
|
+
@supports_sparse_checkout_set_with_cone ||= Gem::Version.new(version) >= Gem::Version.new("2.35.0")
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
def setup_sparse_checkout(destination)
|
|
589
|
+
return unless @sparse_checkout
|
|
590
|
+
|
|
591
|
+
unless supports_sparse_checkout?
|
|
592
|
+
Bundler.ui.warn "Git #{version} doesn't support sparse-checkout (requires 2.25+). Checking out the full repository."
|
|
593
|
+
return
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
Bundler.ui.debug "Setting sparse checkout to only include #{@sparse_checkout.join(", ")}"
|
|
597
|
+
if supports_sparse_checkout_set_with_cone?
|
|
598
|
+
git "sparse-checkout", "set", "--cone", *@sparse_checkout, dir: destination
|
|
599
|
+
else
|
|
600
|
+
# Before git 2.35, `set` keeps an unknown --cone as a pattern instead of rejecting it.
|
|
601
|
+
git "sparse-checkout", "init", "--cone", dir: destination
|
|
602
|
+
git "sparse-checkout", "set", *@sparse_checkout, dir: destination
|
|
603
|
+
end
|
|
604
|
+
end
|
|
568
605
|
end
|
|
569
606
|
end
|
|
570
607
|
end
|
data/lib/bundler/source/git.rb
CHANGED
|
@@ -7,12 +7,13 @@ module Bundler
|
|
|
7
7
|
class Git < Path
|
|
8
8
|
autoload :GitProxy, File.expand_path("git/git_proxy", __dir__)
|
|
9
9
|
|
|
10
|
-
attr_reader :uri, :ref, :branch, :options, :glob, :submodules
|
|
10
|
+
attr_reader :uri, :ref, :branch, :options, :glob, :submodules, :sparse_checkout
|
|
11
11
|
|
|
12
12
|
def initialize(options)
|
|
13
13
|
@options = options
|
|
14
14
|
@checksum_store = Checksum::Store.new
|
|
15
|
-
@
|
|
15
|
+
@sparse_checkout = normalize_sparse_checkout(options)
|
|
16
|
+
@glob = options["glob"] || sparse_checkout_glob || DEFAULT_GLOB
|
|
16
17
|
|
|
17
18
|
@allow_cached = false
|
|
18
19
|
@allow_remote = false
|
|
@@ -58,12 +59,13 @@ module Bundler
|
|
|
58
59
|
%w[ref branch tag submodules].each do |opt|
|
|
59
60
|
out << " #{opt}: #{options[opt]}\n" if options[opt]
|
|
60
61
|
end
|
|
62
|
+
@sparse_checkout&.each {|dir| out << " sparse_checkout: #{dir}\n" }
|
|
61
63
|
out << " glob: #{@glob}\n" unless default_glob?
|
|
62
64
|
out << " specs:\n"
|
|
63
65
|
end
|
|
64
66
|
|
|
65
67
|
def to_gemfile
|
|
66
|
-
specifiers = %w[ref branch tag submodules glob].map do |opt|
|
|
68
|
+
specifiers = %w[ref branch tag submodules glob sparse_checkout].map do |opt|
|
|
67
69
|
"#{opt}: #{options[opt]}" if options[opt]
|
|
68
70
|
end
|
|
69
71
|
|
|
@@ -71,14 +73,15 @@ module Bundler
|
|
|
71
73
|
end
|
|
72
74
|
|
|
73
75
|
def hash
|
|
74
|
-
[self.class, uri, ref, branch, name, glob, submodules].hash
|
|
76
|
+
[self.class, uri, ref, branch, name, glob, submodules, sparse_checkout].hash
|
|
75
77
|
end
|
|
76
78
|
|
|
77
79
|
def eql?(other)
|
|
78
80
|
other.is_a?(Git) && uri == other.uri && ref == other.ref &&
|
|
79
81
|
branch == other.branch && name == other.name &&
|
|
80
82
|
glob == other.glob &&
|
|
81
|
-
submodules == other.submodules
|
|
83
|
+
submodules == other.submodules &&
|
|
84
|
+
sparse_checkout == other.sparse_checkout
|
|
82
85
|
end
|
|
83
86
|
|
|
84
87
|
alias_method :==, :eql?
|
|
@@ -87,7 +90,8 @@ module Bundler
|
|
|
87
90
|
other.is_a?(Git) && uri == other.uri &&
|
|
88
91
|
name == other.name &&
|
|
89
92
|
glob == other.glob &&
|
|
90
|
-
submodules == other.submodules
|
|
93
|
+
submodules == other.submodules &&
|
|
94
|
+
sparse_checkout == other.sparse_checkout
|
|
91
95
|
end
|
|
92
96
|
|
|
93
97
|
def to_s
|
|
@@ -389,7 +393,30 @@ module Bundler
|
|
|
389
393
|
end
|
|
390
394
|
|
|
391
395
|
def shortref_for_path(ref)
|
|
392
|
-
ref[0..11]
|
|
396
|
+
shortref = ref[0..11]
|
|
397
|
+
return shortref unless @sparse_checkout
|
|
398
|
+
|
|
399
|
+
# Checkouts of the same revision with different cones hold different files
|
|
400
|
+
"#{shortref}-#{Bundler::Digest.sha1(@sparse_checkout.join("\n"))[0..7]}"
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
def normalize_sparse_checkout(options)
|
|
404
|
+
return unless options["sparse_checkout"]
|
|
405
|
+
|
|
406
|
+
dirs = Array(options["sparse_checkout"])
|
|
407
|
+
unless dirs.any? && dirs.all? {|dir| dir.is_a?(String) && !dir.empty? }
|
|
408
|
+
raise GemfileError, "The :sparse_checkout option must be a directory or an array of directories, " \
|
|
409
|
+
"but #{options["sparse_checkout"].inspect} was given"
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
options["sparse_checkout"] = dirs
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
def sparse_checkout_glob
|
|
416
|
+
return unless @sparse_checkout
|
|
417
|
+
|
|
418
|
+
dirs = @sparse_checkout.size == 1 ? @sparse_checkout.first : "{#{@sparse_checkout.join(",")}}"
|
|
419
|
+
"#{dirs}/#{DEFAULT_GLOB}"
|
|
393
420
|
end
|
|
394
421
|
|
|
395
422
|
def glob_for_display
|
|
@@ -77,11 +77,11 @@ module Bundler
|
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def hash
|
|
80
|
-
|
|
80
|
+
credless_remotes.hash
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
def eql?(other)
|
|
84
|
-
other.is_a?(Rubygems) && other.credless_remotes == credless_remotes
|
|
84
|
+
equal?(other) || (other.is_a?(Rubygems) && other.credless_remotes == credless_remotes)
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
alias_method :==, :eql?
|
|
@@ -29,7 +29,7 @@ module Bundler
|
|
|
29
29
|
|
|
30
30
|
def build_index
|
|
31
31
|
Index.build do |idx|
|
|
32
|
-
dependency_names = source_map.pinned_spec_names
|
|
32
|
+
dependency_names = source_map.pinned_spec_names | source_map.replacement_names
|
|
33
33
|
|
|
34
34
|
sources.all_sources.each do |source|
|
|
35
35
|
next if @excluded_sources.include?(source)
|
data/lib/bundler/source_map.rb
CHANGED
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
module Bundler
|
|
4
4
|
class SourceMap
|
|
5
|
-
attr_reader :sources, :dependencies, :locked_specs
|
|
5
|
+
attr_reader :sources, :dependencies, :locked_specs, :replacement_names
|
|
6
6
|
|
|
7
|
-
def initialize(sources, dependencies, locked_specs)
|
|
7
|
+
def initialize(sources, dependencies, locked_specs, replacement_names = [])
|
|
8
8
|
@sources = sources
|
|
9
9
|
@dependencies = dependencies
|
|
10
10
|
@locked_specs = locked_specs
|
|
11
|
+
# No gemspec names a gem that a `with:`/`in:` override substitutes, so
|
|
12
|
+
# the dependency API never fetches it unless asked.
|
|
13
|
+
@replacement_names = replacement_names
|
|
11
14
|
end
|
|
12
15
|
|
|
13
16
|
def pinned_spec_names(skip = nil)
|
|
@@ -22,6 +25,7 @@ module Bundler
|
|
|
22
25
|
end
|
|
23
26
|
|
|
24
27
|
unmet_deps = explicit_sources.map do |source|
|
|
28
|
+
source.add_dependency_names(replacement_names)
|
|
25
29
|
(source.spec_names - pinned_spec_names).each do |indirect_dependency_name|
|
|
26
30
|
previous_source = requirements[indirect_dependency_name]
|
|
27
31
|
if previous_source.nil?
|
|
@@ -39,7 +43,7 @@ module Bundler
|
|
|
39
43
|
source.unmet_deps
|
|
40
44
|
end
|
|
41
45
|
|
|
42
|
-
sources.default_source.add_dependency_names(unmet_deps.flatten - requirements.keys)
|
|
46
|
+
sources.default_source.add_dependency_names(unmet_deps.flatten + replacement_names - requirements.keys)
|
|
43
47
|
|
|
44
48
|
requirements
|
|
45
49
|
end
|