Skip to content

Commit fb7262d

Browse files
Merge pull request #8630 from rubygems/deivid-rodriguez/refuse-to-add-invalid-platforms
Fix `bundle lock` sometimes allowing invalid platforms into the lockfile (cherry picked from commit 89b3776)
1 parent 6daacd0 commit fb7262d

File tree

4 files changed

+84
-28
lines changed

4 files changed

+84
-28
lines changed

bundler/lib/bundler/definition.rb

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ def missing_specs?
257257
rescue BundlerError => e
258258
@resolve = nil
259259
@resolver = nil
260-
@resolution_packages = nil
260+
@resolution_base = nil
261261
@source_requirements = nil
262262
@specs = nil
263263

@@ -614,7 +614,7 @@ def write_lock(file, preserve_unknown_sections)
614614
end
615615

616616
def resolver
617-
@resolver ||= Resolver.new(resolution_packages, gem_version_promoter, @most_specific_locked_platform)
617+
@resolver ||= Resolver.new(resolution_base, gem_version_promoter, @most_specific_locked_platform)
618618
end
619619

620620
def expanded_dependencies
@@ -628,15 +628,15 @@ def dependencies_with_bundler
628628
[Dependency.new("bundler", @unlocking_bundler)] + dependencies
629629
end
630630

631-
def resolution_packages
632-
@resolution_packages ||= begin
631+
def resolution_base
632+
@resolution_base ||= begin
633633
last_resolve = converge_locked_specs
634634
remove_invalid_platforms!
635635
new_resolution_platforms = @current_platform_missing ? @new_platforms + [local_platform] : @new_platforms
636-
packages = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: @unlocking_all || @gems_to_unlock, prerelease: gem_version_promoter.pre?, prefer_local: @prefer_local, new_platforms: new_resolution_platforms)
637-
packages = additional_base_requirements_to_prevent_downgrades(packages)
638-
packages = additional_base_requirements_to_force_updates(packages)
639-
packages
636+
base = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: @unlocking_all || @gems_to_unlock, prerelease: gem_version_promoter.pre?, prefer_local: @prefer_local, new_platforms: new_resolution_platforms)
637+
base = additional_base_requirements_to_prevent_downgrades(base)
638+
base = additional_base_requirements_to_force_updates(base)
639+
base
640640
end
641641
end
642642

@@ -711,8 +711,7 @@ def materialize(dependencies)
711711
still_incomplete_specs = resolve.incomplete_specs
712712

713713
if still_incomplete_specs == incomplete_specs
714-
package = resolution_packages.get_package(incomplete_specs.first.name)
715-
resolver.raise_not_found! package
714+
resolver.raise_incomplete! incomplete_specs
716715
end
717716

718717
incomplete_specs = still_incomplete_specs
@@ -734,7 +733,7 @@ def materialize(dependencies)
734733
end
735734

736735
def reresolve_without(incomplete_specs)
737-
resolution_packages.delete(incomplete_specs)
736+
resolution_base.delete(incomplete_specs)
738737
@resolve = start_resolution
739738
end
740739

@@ -747,6 +746,14 @@ def start_resolution
747746

748747
@resolved_bundler_version = result.find {|spec| spec.name == "bundler" }&.version
749748

749+
@new_platforms.each do |platform|
750+
incomplete_specs = result.incomplete_specs_for_platform(current_dependencies, platform)
751+
752+
if incomplete_specs.any?
753+
resolver.raise_incomplete! incomplete_specs
754+
end
755+
end
756+
750757
if @most_specific_non_local_locked_platform
751758
if spec_set_incomplete_for_platform?(result, @most_specific_non_local_locked_platform)
752759
@platforms.delete(@most_specific_non_local_locked_platform)
@@ -1124,27 +1131,27 @@ def lockfiles_equal?(current, proposed, preserve_unknown_sections)
11241131
current == proposed
11251132
end
11261133

1127-
def additional_base_requirements_to_prevent_downgrades(resolution_packages)
1128-
return resolution_packages unless @locked_gems && !sources.expired_sources?(@locked_gems.sources)
1134+
def additional_base_requirements_to_prevent_downgrades(resolution_base)
1135+
return resolution_base unless @locked_gems && !sources.expired_sources?(@locked_gems.sources)
11291136
@originally_locked_specs.each do |locked_spec|
11301137
next if locked_spec.source.is_a?(Source::Path)
11311138

11321139
name = locked_spec.name
11331140
next if @changed_dependencies.include?(name)
11341141

1135-
resolution_packages.base_requirements[name] = Gem::Requirement.new(">= #{locked_spec.version}")
1142+
resolution_base.base_requirements[name] = Gem::Requirement.new(">= #{locked_spec.version}")
11361143
end
1137-
resolution_packages
1144+
resolution_base
11381145
end
11391146

1140-
def additional_base_requirements_to_force_updates(resolution_packages)
1141-
return resolution_packages if @explicit_unlocks.empty?
1147+
def additional_base_requirements_to_force_updates(resolution_base)
1148+
return resolution_base if @explicit_unlocks.empty?
11421149
full_update = dup_for_full_unlock.resolve
11431150
@explicit_unlocks.each do |name|
11441151
version = full_update.version_for(name)
1145-
resolution_packages.base_requirements[name] = Gem::Requirement.new("= #{version}") if version
1152+
resolution_base.base_requirements[name] = Gem::Requirement.new("= #{version}") if version
11461153
end
1147-
resolution_packages
1154+
resolution_base
11481155
end
11491156

11501157
def dup_for_full_unlock

bundler/lib/bundler/resolver.rb

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,16 @@ def name_for_explicit_dependency_source
312312
"Gemfile"
313313
end
314314

315+
def raise_incomplete!(incomplete_specs)
316+
raise_not_found!(@base.get_package(incomplete_specs.first.name))
317+
end
318+
319+
def sort_versions_by_preferred(package, versions)
320+
@gem_version_promoter.sort_versions(package, versions)
321+
end
322+
323+
private
324+
315325
def raise_not_found!(package)
316326
name = package.name
317327
source = source_for(name)
@@ -348,12 +358,6 @@ def raise_not_found!(package)
348358
raise GemNotFound, message
349359
end
350360

351-
def sort_versions_by_preferred(package, versions)
352-
@gem_version_promoter.sort_versions(package, versions)
353-
end
354-
355-
private
356-
357361
def filtered_versions_for(package)
358362
@gem_version_promoter.filter_versions(package, @all_versions[package])
359363
end

bundler/lib/bundler/spec_set.rb

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,15 @@ def materialized_for_all_platforms
130130
end
131131

132132
def incomplete_for_platform?(deps, platform)
133-
return false if @specs.empty?
133+
incomplete_specs_for_platform(deps, platform).any?
134+
end
135+
136+
def incomplete_specs_for_platform(deps, platform)
137+
return [] if @specs.empty?
134138

135139
validation_set = self.class.new(@specs)
136140
validation_set.for(deps, [platform])
137-
138-
validation_set.incomplete_specs.any?
141+
validation_set.incomplete_specs
139142
end
140143

141144
def missing_specs_for(deps)

bundler/spec/commands/lock_spec.rb

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,6 +1346,48 @@
13461346
L
13471347
end
13481348

1349+
it "refuses to add platforms incompatible with the lockfile" do
1350+
build_repo4 do
1351+
build_gem "sorbet-static", "0.5.11989" do |s|
1352+
s.platform = "x86_64-linux"
1353+
end
1354+
end
1355+
1356+
gemfile <<~G
1357+
source "https://gem.repo4"
1358+
1359+
gem "sorbet-static"
1360+
G
1361+
1362+
lockfile <<~L
1363+
GEM
1364+
remote: https://gem.repo4/
1365+
specs:
1366+
sorbet-static (0.5.11989-x86_64-linux)
1367+
1368+
PLATFORMS
1369+
x86_64-linux
1370+
1371+
DEPENDENCIES
1372+
sorbet-static
1373+
1374+
BUNDLED WITH
1375+
#{Bundler::VERSION}
1376+
L
1377+
1378+
simulate_platform "x86_64-linux" do
1379+
bundle "lock --add-platform ruby", raise_on_error: false
1380+
end
1381+
1382+
nice_error = <<~E.strip
1383+
Could not find gems matching 'sorbet-static' valid for all resolution platforms (x86_64-linux, ruby) in rubygems repository https://gem.repo4/ or installed locally.
1384+
1385+
The source contains the following gems matching 'sorbet-static':
1386+
* sorbet-static-0.5.11989-x86_64-linux
1387+
E
1388+
expect(err).to include(nice_error)
1389+
end
1390+
13491391
it "does not crash on conflicting ruby requirements between platform versions in two different gems" do
13501392
build_repo4 do
13511393
build_gem "unf_ext", "0.0.8.2"

0 commit comments

Comments
 (0)