From cf3f10d0e3d46afd3957aa626d534169c0aec198 Mon Sep 17 00:00:00 2001 From: Martin Emde Date: Wed, 15 Nov 2023 10:36:01 -0800 Subject: [PATCH] Draft: lock_platform directive in Gemfile This allows the Gemfile to include a deploy platform by default on first deploy, and since Gemfiles are often cargo culted or generated, we can make the deploy platform intention more more clear from the beginning. --- bundler/lib/bundler/definition.rb | 8 +++-- bundler/lib/bundler/dsl.rb | 16 ++++++++- bundler/lib/bundler/spec_set.rb | 2 +- bundler/spec/runtime/platform_spec.rb | 52 +++++++++++++++++++++++++++ bundler/spec/support/builders.rb | 5 +++ 5 files changed, 78 insertions(+), 5 deletions(-) diff --git a/bundler/lib/bundler/definition.rb b/bundler/lib/bundler/definition.rb index 761185ff21d8..20800d67e446 100644 --- a/bundler/lib/bundler/definition.rb +++ b/bundler/lib/bundler/definition.rb @@ -55,7 +55,7 @@ def self.build(gemfile, lockfile, unlock) # to be updated or true if all gems should be updated # @param ruby_version [Bundler::RubyVersion, nil] Requested Ruby Version # @param optional_groups [Array(String)] A list of optional groups - def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, optional_groups = [], gemfiles = []) + def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, optional_groups = [], gemfiles = [], platforms = []) if [true, false].include?(unlock) @unlocking_bundler = false @unlocking = unlock @@ -89,6 +89,7 @@ def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, opti @locked_gems = LockfileParser.new(@lockfile_contents) @locked_platforms = @locked_gems.platforms @platforms = @locked_platforms.dup + platforms.each {|p| add_platform(p) } @locked_bundler_version = @locked_gems.bundler_version @locked_ruby_version = @locked_gems.ruby_version @originally_locked_specs = SpecSet.new(@locked_gems.specs) @@ -105,13 +106,14 @@ def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, opti end else @unlock = {} - @platforms = [] @locked_gems = nil @locked_deps = {} @locked_specs = SpecSet.new([]) @originally_locked_specs = @locked_specs @locked_sources = [] @locked_platforms = [] + @platforms = platforms + @new_platform = true unless @platforms.empty? end locked_gem_sources = @locked_sources.select {|s| s.is_a?(Source::Rubygems) } @@ -143,7 +145,7 @@ def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, opti @unlock[:gems] ||= @dependencies.map(&:name) else eager_unlock = (@unlock[:gems] || []).map {|name| Dependency.new(name, ">= 0") } - @unlock[:gems] = @locked_specs.for(eager_unlock, false, platforms).map(&:name).uniq + @unlock[:gems] = @locked_specs.for(eager_unlock, false, @platforms).map(&:name).uniq end @dependency_changes = converge_dependencies diff --git a/bundler/lib/bundler/dsl.rb b/bundler/lib/bundler/dsl.rb index 03c80a408cc4..9d7fd8f17925 100644 --- a/bundler/lib/bundler/dsl.rb +++ b/bundler/lib/bundler/dsl.rb @@ -32,6 +32,7 @@ def initialize @install_conditionals = [] @optional_groups = [] @platforms = [] + @lock_platforms = [] @env = nil @ruby_version = nil @gemspecs = [] @@ -215,7 +216,7 @@ def github(repo, options = {}) def to_definition(lockfile, unlock) check_primary_source_safety - Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version, @optional_groups, @gemfiles) + Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version, @optional_groups, @gemfiles, @lock_platforms) end def group(*args, &blk) @@ -249,6 +250,19 @@ def platforms(*platforms) end alias_method :platform, :platforms + # Name collision, working around it for a proof + def lock_platform(*platforms) + platforms.each do |p| + gem_platform = Gem::Platform.new(p) + # TODO: this is copied from lock + if gem_platform.to_s == "unknown" + Bundler.ui.warn "The platform `#{platform_string}` is unknown to RubyGems " \ + "and adding it will likely lead to resolution errors" + end + @lock_platforms << gem_platform + end + end + def env(name) old = @env @env = name diff --git a/bundler/lib/bundler/spec_set.rb b/bundler/lib/bundler/spec_set.rb index cb8f0fd1b261..ad7e12a8ddf1 100644 --- a/bundler/lib/bundler/spec_set.rb +++ b/bundler/lib/bundler/spec_set.rb @@ -15,7 +15,7 @@ def initialize(specs, incomplete_specs = []) end def for(dependencies, check = false, platforms = [nil]) - handled = ["bundler"].product(platforms).map {|k| [k, true] }.to_h + handled = ["bundler"].product(platforms).to_h {|k| [k, true] } deps = dependencies.product(platforms) specs = [] diff --git a/bundler/spec/runtime/platform_spec.rb b/bundler/spec/runtime/platform_spec.rb index 31d93a559faf..fcf92727dee7 100644 --- a/bundler/spec/runtime/platform_spec.rb +++ b/bundler/spec/runtime/platform_spec.rb @@ -467,4 +467,56 @@ end end end + + it "will add platform directly specified by a gemfile" do + simulate_platform "x86-darwin-100" + + build_repo4 do + build_gem "nokogiri", "1.13.8" + build_gem "nokogiri", "1.13.8" do |s| + s.platform = "aarch64-linux" + end + end + + install_gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + + lock_platform "aarch64-linux" + + gem "nokogiri" + G + + checksums = checksum_section do |c| + c.repo_gem gem_repo4, "nokogiri", "1.13.8" + # TODO: This is wrong, the point is to get the checksum + # Figure out why it's not fetching them for other platforms + c.no_checksum "nokogiri", "1.13.8", "aarch64-linux" + # c.repo_gem gem_repo4, "nokogiri", "1.13.8", "aarch64-linux" + end + + expected_lockfile = <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + nokogiri (1.13.8) + nokogiri (1.13.8-aarch64-linux) + + PLATFORMS + aarch64-linux + x86-darwin-100 + + DEPENDENCIES + nokogiri + + CHECKSUMS + #{checksums} + + BUNDLED WITH + #{Bundler::VERSION} + L + + # TODO: "nokogiri was expected to be of platform ruby but was ruby" + # expect(the_bundle).to include_gems "nokogiri 1.13.8 ruby" + expect(lockfile).to eq(expected_lockfile) + end end diff --git a/bundler/spec/support/builders.rb b/bundler/spec/support/builders.rb index 7b91aaef57de..e2f6d5fa093e 100644 --- a/bundler/spec/support/builders.rb +++ b/bundler/spec/support/builders.rb @@ -126,6 +126,11 @@ def build_repo1 s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 x86-darwin-100'" end + build_gem "platform_specific" do |s| + s.platform = "x86_64-linux" + s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 x86_64-linux'" + end + build_gem "only_java", "1.0" do |s| s.platform = "java" s.write "lib/only_java.rb", "ONLY_JAVA = '1.0.0 JAVA'"