Skip to content

Commit 47de25b

Browse files
Merge pull request #6495 from rubygems/dont-remove-ruby-platform-when-healing
Fix incorrect removal of ruby platform when auto-healing corrupted lockfiles (cherry picked from commit 9eeb6a6)
1 parent edc5b3f commit 47de25b

File tree

7 files changed

+101
-54
lines changed

7 files changed

+101
-54
lines changed

Manifest.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ bundler/lib/bundler/gem_helpers.rb
8080
bundler/lib/bundler/gem_tasks.rb
8181
bundler/lib/bundler/gem_version_promoter.rb
8282
bundler/lib/bundler/graph.rb
83+
bundler/lib/bundler/incomplete_specification.rb
8384
bundler/lib/bundler/index.rb
8485
bundler/lib/bundler/injector.rb
8586
bundler/lib/bundler/inline.rb

bundler/lib/bundler.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ module Bundler
6262
autoload :GemHelpers, File.expand_path("bundler/gem_helpers", __dir__)
6363
autoload :GemVersionPromoter, File.expand_path("bundler/gem_version_promoter", __dir__)
6464
autoload :Graph, File.expand_path("bundler/graph", __dir__)
65+
autoload :IncompleteSpecification, File.expand_path("bundler/incomplete_specification", __dir__)
6566
autoload :Index, File.expand_path("bundler/index", __dir__)
6667
autoload :Injector, File.expand_path("bundler/injector", __dir__)
6768
autoload :Installer, File.expand_path("bundler/installer", __dir__)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# frozen_string_literal: true
2+
3+
module Bundler
4+
#
5+
# Represents a package name that was found to be incomplete when trying to
6+
# materialize a fresh resolution or the lockfile.
7+
#
8+
# Holds the actual partially complete set of specifications for the name.
9+
# These are used so that they can be unlocked in a future resolution, and fix
10+
# the situation.
11+
#
12+
class IncompleteSpecification
13+
attr_reader :name, :partially_complete_specs
14+
15+
def initialize(name, partially_complete_specs = [])
16+
@name = name
17+
@partially_complete_specs = partially_complete_specs
18+
end
19+
20+
def ==(other)
21+
partially_complete_specs == other.partially_complete_specs
22+
end
23+
end
24+
end

bundler/lib/bundler/resolver/base.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,11 @@ def [](name)
3434
@base[name]
3535
end
3636

37-
def delete(specs)
38-
specs.each do |spec|
39-
@base.delete(spec)
37+
def delete(incomplete_specs)
38+
incomplete_specs.each do |incomplete_spec|
39+
incomplete_spec.partially_complete_specs.each do |spec|
40+
@base.delete(spec)
41+
end
4042
end
4143
end
4244

bundler/lib/bundler/spec_set.rb

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,8 @@ class SpecSet
77
include Enumerable
88
include TSort
99

10-
attr_reader :incomplete_specs
11-
12-
def initialize(specs, incomplete_specs = [])
10+
def initialize(specs)
1311
@specs = specs
14-
@incomplete_specs = incomplete_specs
1512
end
1613

1714
def for(dependencies, check = false, platforms = [nil])
@@ -45,7 +42,7 @@ def for(dependencies, check = false, platforms = [nil])
4542
end
4643

4744
if incomplete && check
48-
@incomplete_specs += lookup[name].any? ? lookup[name] : [LazySpecification.new(name, nil, nil)]
45+
specs << IncompleteSpecification.new(name, lookup[name])
4946
end
5047
end
5148

@@ -81,10 +78,10 @@ def to_hash
8178
lookup.dup
8279
end
8380

84-
def materialize(deps)
85-
materialized = self.for(deps, true)
81+
def materialize(deps, platforms = [nil])
82+
materialized = self.for(deps, true, platforms)
8683

87-
SpecSet.new(materialized, incomplete_specs)
84+
SpecSet.new(materialized)
8885
end
8986

9087
# Materialize for all the specs in the spec set, regardless of what platform they're for
@@ -101,15 +98,19 @@ def materialized_for_all_platforms
10198
end
10299

103100
def incomplete_ruby_specs?(deps)
104-
self.for(deps, true, [Gem::Platform::RUBY])
101+
return false if @specs.empty?
105102

106-
@incomplete_specs.any?
103+
materialize(deps, [Gem::Platform::RUBY]).incomplete_specs.any?
107104
end
108105

109106
def missing_specs
110107
@specs.select {|s| s.is_a?(LazySpecification) }
111108
end
112109

110+
def incomplete_specs
111+
@specs.select {|s| s.is_a?(IncompleteSpecification) }
112+
end
113+
113114
def merge(set)
114115
arr = sorted.dup
115116
set.each do |set_spec|

bundler/spec/lock/lockfile_spec.rb

Lines changed: 54 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,7 +1262,7 @@
12621262
indirect_dependency (1.2.3)
12631263
12641264
PLATFORMS
1265-
#{lockfile_platforms}
1265+
#{formatted_lockfile_platforms(*["ruby", generic_local_platform].uniq)}
12661266
12671267
DEPENDENCIES
12681268
direct_dependency
@@ -1272,57 +1272,71 @@
12721272
G
12731273
end
12741274

1275-
it "auto-heals when the lockfile is missing dependent specs" do
1276-
build_repo4 do
1277-
build_gem "minitest-bisect", "1.6.0" do |s|
1278-
s.add_dependency "path_expander", "~> 1.1"
1275+
shared_examples_for "a lockfile missing dependent specs" do
1276+
it "auto-heals" do
1277+
build_repo4 do
1278+
build_gem "minitest-bisect", "1.6.0" do |s|
1279+
s.add_dependency "path_expander", "~> 1.1"
1280+
end
1281+
1282+
build_gem "path_expander", "1.1.1"
12791283
end
12801284

1281-
build_gem "path_expander", "1.1.1"
1282-
end
1285+
gemfile <<~G
1286+
source "#{file_uri_for(gem_repo4)}"
1287+
gem "minitest-bisect"
1288+
G
12831289

1284-
gemfile <<~G
1285-
source "#{file_uri_for(gem_repo4)}"
1286-
gem "minitest-bisect"
1287-
G
1290+
# Corrupt lockfile (completely missing path_expander)
1291+
lockfile <<~L
1292+
GEM
1293+
remote: #{file_uri_for(gem_repo4)}/
1294+
specs:
1295+
minitest-bisect (1.6.0)
12881296
1289-
# Corrupt lockfile (completely missing path_expander)
1290-
lockfile <<~L
1291-
GEM
1292-
remote: #{file_uri_for(gem_repo4)}/
1293-
specs:
1294-
minitest-bisect (1.6.0)
1297+
PLATFORMS
1298+
#{platforms}
12951299
1296-
PLATFORMS
1297-
#{lockfile_platforms}
1300+
DEPENDENCIES
1301+
minitest-bisect
12981302
1299-
DEPENDENCIES
1300-
minitest-bisect
1303+
BUNDLED WITH
1304+
#{Bundler::VERSION}
1305+
L
13011306

1302-
BUNDLED WITH
1303-
#{Bundler::VERSION}
1304-
L
1307+
cache_gems "minitest-bisect-1.6.0", "path_expander-1.1.1", :gem_repo => gem_repo4
1308+
bundle :install
13051309

1306-
cache_gems "minitest-bisect-1.6.0", "path_expander-1.1.1", :gem_repo => gem_repo4
1307-
bundle :install
1310+
expect(lockfile).to eq <<~L
1311+
GEM
1312+
remote: #{file_uri_for(gem_repo4)}/
1313+
specs:
1314+
minitest-bisect (1.6.0)
1315+
path_expander (~> 1.1)
1316+
path_expander (1.1.1)
13081317
1309-
expect(lockfile).to eq <<~L
1310-
GEM
1311-
remote: #{file_uri_for(gem_repo4)}/
1312-
specs:
1313-
minitest-bisect (1.6.0)
1314-
path_expander (~> 1.1)
1315-
path_expander (1.1.1)
1318+
PLATFORMS
1319+
#{platforms}
13161320
1317-
PLATFORMS
1318-
#{lockfile_platforms}
1321+
DEPENDENCIES
1322+
minitest-bisect
13191323
1320-
DEPENDENCIES
1321-
minitest-bisect
1324+
BUNDLED WITH
1325+
#{Bundler::VERSION}
1326+
L
1327+
end
1328+
end
13221329

1323-
BUNDLED WITH
1324-
#{Bundler::VERSION}
1325-
L
1330+
context "with just specific platform" do
1331+
let(:platforms) { lockfile_platforms }
1332+
1333+
it_behaves_like "a lockfile missing dependent specs"
1334+
end
1335+
1336+
context "with both ruby and specific platform" do
1337+
let(:platforms) { lockfile_platforms("ruby") }
1338+
1339+
it_behaves_like "a lockfile missing dependent specs"
13261340
end
13271341

13281342
it "auto-heals when the lockfile is missing specs" do

bundler/spec/support/platforms.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,11 @@ def not_local_patchlevel
9696
end
9797

9898
def lockfile_platforms(*extra)
99-
[local_platform, *extra].map(&:to_s).sort.join("\n ")
99+
formatted_lockfile_platforms(local_platform, *extra)
100+
end
101+
102+
def formatted_lockfile_platforms(*platforms)
103+
platforms.map(&:to_s).sort.join("\n ")
100104
end
101105
end
102106
end

0 commit comments

Comments
 (0)