summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Rodríguez <deivid.rodriguez@riseup.net>2023-03-17 14:18:30 +0100
committerHiroshi SHIBATA <hsbt@ruby-lang.org>2023-11-13 11:06:10 +0900
commit435eb56f6175b7c9a16121ec8441f7492fa9aec5 (patch)
tree086201ded65f06c9f09d057287ce92778fe9abaf
parent59b361aaca0194bd526e32b7053948a49da4e39d (diff)
[rubygems/rubygems] Automatically lock extra ruby platforms
Since we started locking the specific platform in the lockfile, that has created an annoying situation for users that don't develop on Linux. They will create a lockfile on their machines, locking their local platform, for example, darwin. But then that lockfile won't work automatically when deploying to Heroku for example, because the lockfile is frozen and the Linux platform is not included. There's the chance though that resolving against two platforms (Linux + the local platform) won't succeed while resolving for just the current platform will. So, instead, we check other platform specific variants available for the resolution we initially found, and lock those platforms and specs too if they satisfy the resolution. This is only done when generating new lockfiles from scratch, existing lockfiles should keep working as before, and it's only done for "ruby platforms", i.e., not Java or Windows which have their own complexities, and so are excluded. With this change, we expect that MacOS users can bundle locally and deploy to Heroku without needing to do anything special. https://github.com/rubygems/rubygems/commit/5f24f06bc5
-rw-r--r--lib/bundler/definition.rb15
-rw-r--r--lib/bundler/spec_set.rb38
-rw-r--r--spec/bundler/commands/lock_spec.rb11
-rw-r--r--spec/bundler/commands/update_spec.rb26
-rw-r--r--spec/bundler/install/gemfile/sources_spec.rb30
-rw-r--r--spec/bundler/install/gemfile/specific_platform_spec.rb136
-rw-r--r--spec/bundler/lock/lockfile_spec.rb2
-rw-r--r--spec/bundler/support/platforms.rb2
8 files changed, 216 insertions, 44 deletions
diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb
index 72fbae1984..9ef0abed93 100644
--- a/lib/bundler/definition.rb
+++ b/lib/bundler/definition.rb
@@ -84,7 +84,7 @@ module Bundler
@new_platform = nil
@removed_platform = nil
- if lockfile && File.exist?(lockfile)
+ if lockfile_exists?
@lockfile_contents = Bundler.read_file(lockfile)
@locked_gems = LockfileParser.new(@lockfile_contents)
@locked_platforms = @locked_gems.platforms
@@ -302,6 +302,10 @@ module Bundler
end
end
+ def should_complete_platforms?
+ !lockfile_exists? && generic_local_platform_is_ruby? && !Bundler.settings[:force_ruby_platform]
+ end
+
def spec_git_paths
sources.git_sources.map {|s| File.realpath(s.path) if File.exist?(s.path) }.compact
end
@@ -491,6 +495,10 @@ module Bundler
private
+ def lockfile_exists?
+ lockfile && File.exist?(lockfile)
+ end
+
def resolver
@resolver ||= Resolver.new(resolution_packages, gem_version_promoter)
end
@@ -567,11 +575,12 @@ module Bundler
end
def start_resolution
- result = resolver.start
+ result = SpecSet.new(resolver.start)
@resolved_bundler_version = result.find {|spec| spec.name == "bundler" }&.version
+ @platforms = result.complete_platforms!(platforms) if should_complete_platforms?
- SpecSet.new(SpecSet.new(result).for(dependencies, false, @platforms))
+ SpecSet.new(result.for(dependencies, false, @platforms))
end
def precompute_source_requirements_for_indirect_dependencies?
diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb
index 277824c34f..f4fc005ef2 100644
--- a/lib/bundler/spec_set.rb
+++ b/lib/bundler/spec_set.rb
@@ -52,6 +52,44 @@ module Bundler
specs.uniq
end
+ def complete_platforms!(platforms)
+ return platforms.concat([Gem::Platform::RUBY]).uniq if @specs.empty?
+
+ new_platforms = @specs.flat_map {|spec| spec.source.specs.search([spec.name, spec.version]).map(&:platform) }.uniq.select do |platform|
+ next if platforms.include?(platform)
+ next unless GemHelpers.generic(platform) == Gem::Platform::RUBY
+
+ new_specs = []
+
+ valid_platform = lookup.all? do |_, specs|
+ spec = specs.first
+ matching_specs = spec.source.specs.search([spec.name, spec.version])
+ platform_spec = GemHelpers.select_best_platform_match(matching_specs, platform).first
+
+ if platform_spec
+ new_specs << LazySpecification.from_spec(platform_spec)
+ true
+ else
+ false
+ end
+ end
+ next unless valid_platform
+
+ @specs.concat(new_specs.uniq)
+ end
+ return platforms if new_platforms.empty?
+
+ platforms.concat(new_platforms)
+
+ less_specific_platform = new_platforms.find {|platform| platform != Gem::Platform::RUBY && platform === Bundler.local_platform }
+ platforms.delete(Bundler.local_platform) if less_specific_platform
+
+ @sorted = nil
+ @lookup = nil
+
+ platforms
+ end
+
def [](key)
key = key.name if key.respond_to?(:name)
lookup[key].reverse
diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb
index ac220c9318..5c6a2c0e3d 100644
--- a/spec/bundler/commands/lock_spec.rb
+++ b/spec/bundler/commands/lock_spec.rb
@@ -733,7 +733,7 @@ RSpec.describe "bundle lock" do
gem "libv8"
G
- simulate_platform(Gem::Platform.new("x86_64-darwin")) { bundle "lock" }
+ simulate_platform(Gem::Platform.new("x86_64-darwin-19")) { bundle "lock" }
expect(lockfile).to eq <<~G
GEM
@@ -743,7 +743,8 @@ RSpec.describe "bundle lock" do
libv8 (8.4.255.0-x86_64-darwin-20)
PLATFORMS
- x86_64-darwin
+ x86_64-darwin-19
+ x86_64-darwin-20
DEPENDENCIES
libv8
@@ -1237,7 +1238,7 @@ RSpec.describe "bundle lock" do
activemodel (>= 6.0.4)
PLATFORMS
- #{lockfile_platforms}
+ #{local_platform}
DEPENDENCIES
activeadmin (= 2.13.1)
@@ -1273,7 +1274,7 @@ RSpec.describe "bundle lock" do
version solving has failed.
ERR
- lockfile lockfile.gsub(/PLATFORMS\n #{lockfile_platforms}/m, "PLATFORMS\n #{lockfile_platforms("ruby")}")
+ lockfile lockfile.gsub(/PLATFORMS\n #{local_platform}/m, "PLATFORMS\n #{lockfile_platforms("ruby")}")
bundle "lock", :raise_on_error => false
@@ -1438,7 +1439,7 @@ RSpec.describe "bundle lock" do
nokogiri (1.14.2)
PLATFORMS
- x86_64-linux
+ #{lockfile_platforms}
DEPENDENCIES
foo!
diff --git a/spec/bundler/commands/update_spec.rb b/spec/bundler/commands/update_spec.rb
index 8e48758c04..5c7b569fe2 100644
--- a/spec/bundler/commands/update_spec.rb
+++ b/spec/bundler/commands/update_spec.rb
@@ -559,12 +559,36 @@ RSpec.describe "bundle update" do
before do
build_repo2
- install_gemfile <<-G
+ gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
gem "activesupport"
gem "rack-obama"
gem "platform_specific"
G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo2)}/
+ specs:
+ activesupport (2.3.5)
+ platform_specific (1.0-#{local_platform})
+ rack (1.0.0)
+ rack-obama (1.0)
+ rack
+
+ PLATFORMS
+ #{local_platform}
+
+ DEPENDENCIES
+ activesupport
+ platform_specific
+ rack-obama
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
end
it "doesn't hit repo2" do
diff --git a/spec/bundler/install/gemfile/sources_spec.rb b/spec/bundler/install/gemfile/sources_spec.rb
index 64eed1a2f4..15ff15e46d 100644
--- a/spec/bundler/install/gemfile/sources_spec.rb
+++ b/spec/bundler/install/gemfile/sources_spec.rb
@@ -379,7 +379,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
rack
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
depends_on_rack!
@@ -422,7 +422,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
rack
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
depends_on_rack!
@@ -803,7 +803,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
zeitwerk (2.4.2)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
activesupport
@@ -874,7 +874,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
sidekiq (>= 6.1.0)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
activesupport
@@ -975,7 +975,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
sidekiq (>= 6.1.0)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
activesupport
@@ -1049,7 +1049,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
sidekiq (>= 6.1.0)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
activesupport
@@ -1146,7 +1146,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
nokogiri (>= 1.2.3)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
handsoap!
@@ -1209,7 +1209,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
rack (0.9.1)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
rack!
@@ -1239,7 +1239,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
rack (0.9.1)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
rack!
@@ -1261,7 +1261,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
rack (0.9.1)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
rack!
@@ -1701,7 +1701,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
mime-types (3.3.1)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
capybara (~> 2.5.0)
@@ -1732,7 +1732,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
mime-types (3.0.0)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
capybara (~> 2.5.0)
@@ -1789,7 +1789,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
pdf-writer (= 1.1.8)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
ruport (= 1.7.0.3)!
@@ -1851,7 +1851,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
pdf-writer (= 1.1.8)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
ruport (= 1.7.0.3)!
@@ -1899,7 +1899,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
pdf-writer (1.1.8)
PLATFORMS
- #{local_platform}
+ #{lockfile_platforms}
DEPENDENCIES
pdf-writer (= 1.1.8)
diff --git a/spec/bundler/install/gemfile/specific_platform_spec.rb b/spec/bundler/install/gemfile/specific_platform_spec.rb
index 099ef35bbf..0c9816eaac 100644
--- a/spec/bundler/install/gemfile/specific_platform_spec.rb
+++ b/spec/bundler/install/gemfile/specific_platform_spec.rb
@@ -11,11 +11,11 @@ RSpec.describe "bundle install with specific platforms" do
setup_multiplatform_gem
install_gemfile(google_protobuf)
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- expect(the_bundle.locked_gems.platforms).to eq([pl("x86_64-darwin-15")])
+ expect(the_bundle.locked_gems.platforms).to include(pl("x86_64-darwin-15"))
expect(the_bundle).to include_gem("google-protobuf 3.0.0.alpha.5.0.5.1 universal-darwin")
- expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[
- google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin
- ])
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to include(
+ "google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin"
+ )
end
end
@@ -309,10 +309,10 @@ RSpec.describe "bundle install with specific platforms" do
G
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- expect(the_bundle.locked_gems.platforms).to eq([pl("x86_64-darwin-15")])
+ expect(the_bundle.locked_gems.platforms).to include(pl("x86_64-darwin-15"))
expect(the_bundle).to include_gems("facter 2.4.6 universal-darwin", "CFPropertyList 1.0")
- expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(["CFPropertyList-1.0",
- "facter-2.4.6-universal-darwin"])
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to include("CFPropertyList-1.0",
+ "facter-2.4.6-universal-darwin")
end
end
@@ -327,8 +327,8 @@ RSpec.describe "bundle install with specific platforms" do
install_gemfile(google_protobuf)
bundle "lock --add-platform=#{x64_mingw32}"
- expect(the_bundle.locked_gems.platforms).to eq([x64_mingw32, pl("x86_64-darwin-15")])
- expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[
+ expect(the_bundle.locked_gems.platforms).to include(x64_mingw32, pl("x86_64-darwin-15"))
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to include(*%w[
google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin
google-protobuf-3.0.0.alpha.5.0.5.1-x64-mingw32
])
@@ -341,11 +341,11 @@ RSpec.describe "bundle install with specific platforms" do
install_gemfile(google_protobuf)
bundle "lock --add-platform=#{java}"
- expect(the_bundle.locked_gems.platforms).to eq([java, pl("x86_64-darwin-15")])
- expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[
- google-protobuf-3.0.0.alpha.5.0.5.1
- google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin
- ])
+ expect(the_bundle.locked_gems.platforms).to include(java, pl("x86_64-darwin-15"))
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to include(
+ "google-protobuf-3.0.0.alpha.5.0.5.1",
+ "google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin"
+ )
end
end
end
@@ -517,7 +517,7 @@ RSpec.describe "bundle install with specific platforms" do
sorbet-runtime (= 0.5.10160)
PLATFORMS
- #{lockfile_platforms("ruby")}
+ #{lockfile_platforms}
DEPENDENCIES
sorbet-static-and-runtime
@@ -548,7 +548,7 @@ RSpec.describe "bundle install with specific platforms" do
sorbet-runtime (= 0.5.10160)
PLATFORMS
- #{lockfile_platforms}
+ #{local_platform}
DEPENDENCIES
sorbet-static-and-runtime
@@ -601,7 +601,7 @@ RSpec.describe "bundle install with specific platforms" do
DEPENDENCIES
nokogiri
- sorbet
+ sorbet-static
BUNDLED WITH
#{Bundler::VERSION}
@@ -702,7 +702,7 @@ RSpec.describe "bundle install with specific platforms" do
sorbet-runtime (= 0.5.10160)
PLATFORMS
- #{lockfile_platforms}
+ #{local_platform}
DEPENDENCIES
sorbet-static-and-runtime
@@ -1067,6 +1067,106 @@ RSpec.describe "bundle install with specific platforms" do
end
end
+ it "locks specific platforms automatically" do
+ simulate_platform "x86_64-linux" do
+ build_repo4 do
+ build_gem "nokogiri", "1.14.0"
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "x86_64-linux"
+ end
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "arm-linux"
+ end
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "x64-mingw32"
+ end
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "java"
+ end
+
+ build_gem "sorbet-static", "0.5.10696" do |s|
+ s.platform = "x86_64-linux"
+ end
+ build_gem "sorbet-static", "0.5.10696" do |s|
+ s.platform = "universal-darwin-22"
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "nokogiri"
+ G
+
+ bundle "lock"
+
+ # locks all compatible platforms, excluding Java and Windows
+ expect(lockfile).to eq(<<~L)
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.14.0)
+ nokogiri (1.14.0-arm-linux)
+ nokogiri (1.14.0-x86_64-linux)
+
+ PLATFORMS
+ arm-linux
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+
+ CHECKSUMS
+ #{gem_no_checksum "nokogiri", "1.14.0"}
+ #{gem_no_checksum "nokogiri", "1.14.0", "arm-linux"}
+ #{gem_no_checksum "nokogiri", "1.14.0", "x86_64-linux"}
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "nokogiri"
+ gem "sorbet-static"
+ G
+
+ FileUtils.rm bundled_app_lock
+
+ bundle "lock"
+
+ # locks only platforms compatible with all gems in the bundle
+ expect(lockfile).to eq(<<~L)
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.14.0)
+ nokogiri (1.14.0-x86_64-linux)
+ sorbet-static (0.5.10696-universal-darwin-22)
+ sorbet-static (0.5.10696-x86_64-linux)
+
+ PLATFORMS
+ universal-darwin-22
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+ sorbet-static
+
+ CHECKSUMS
+ #{gem_no_checksum "nokogiri", "1.14.0"}
+ #{gem_no_checksum "nokogiri", "1.14.0", "x86_64-linux"}
+ #{gem_no_checksum "sorbet-static", "0.5.10696", "universal-darwin-22"}
+ #{gem_no_checksum "sorbet-static", "0.5.10696", "x86_64-linux"}
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
private
def setup_multiplatform_gem
diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb
index be1fdc37b5..efa5b85788 100644
--- a/spec/bundler/lock/lockfile_spec.rb
+++ b/spec/bundler/lock/lockfile_spec.rb
@@ -1212,7 +1212,7 @@ RSpec.describe "the lockfile format" do
rack (1.0.0)
PLATFORMS
- #{lockfile_platforms("java")}
+ #{lockfile_platforms("java", local_platform, :defaults => [])}
DEPENDENCIES
rack
diff --git a/spec/bundler/support/platforms.rb b/spec/bundler/support/platforms.rb
index 973d34a43a..3289d5e8e2 100644
--- a/spec/bundler/support/platforms.rb
+++ b/spec/bundler/support/platforms.rb
@@ -105,7 +105,7 @@ module Spec
end
def default_locked_platforms
- [local_platform]
+ [local_platform, generic_local_platform]
end
end
end