summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHiroshi SHIBATA <hsbt@ruby-lang.org>2023-12-23 09:08:54 +0900
committerHiroshi SHIBATA <hsbt@ruby-lang.org>2023-12-23 09:34:26 +0900
commit7d7a84e99fca816ec352c95965687db240fb56d3 (patch)
tree3c734dbd75631559194ffda83695f17657320682
parentfa5de8f68d51e4fbc91190061e3ce3817bd68964 (diff)
Merge RubyGems-3.5.3 and Bundler-2.5.3
-rw-r--r--lib/bundler/dependency.rb4
-rw-r--r--lib/bundler/dsl.rb19
-rw-r--r--lib/bundler/version.rb2
-rw-r--r--lib/rubygems.rb2
-rw-r--r--lib/rubygems/safe_marshal/elements.rb8
-rw-r--r--lib/rubygems/safe_marshal/reader.rb4
-rw-r--r--lib/rubygems/safe_marshal/visitors/to_ruby.rb30
-rw-r--r--spec/bundler/commands/install_spec.rb29
-rw-r--r--test/rubygems/test_gem_safe_marshal.rb36
9 files changed, 122 insertions, 12 deletions
diff --git a/lib/bundler/dependency.rb b/lib/bundler/dependency.rb
index efc39a12f3..77d7a00362 100644
--- a/lib/bundler/dependency.rb
+++ b/lib/bundler/dependency.rb
@@ -68,6 +68,10 @@ module Bundler
@should_include && current_env? && current_platform?
end
+ def gemspec_dev_dep?
+ type == :development
+ end
+
def current_env?
return true unless @env
if @env.is_a?(Hash)
diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb
index a67f7ed8a1..1eca749617 100644
--- a/lib/bundler/dsl.rb
+++ b/lib/bundler/dsl.rb
@@ -103,16 +103,21 @@ module Bundler
# if there's already a dependency with this name we try to prefer one
if current = @dependencies.find {|d| d.name == dep.name }
# Always prefer the dependency from the Gemfile
- deleted_dep = @dependencies.delete(current) if current.type == :development
+ @dependencies.delete(current) if current.gemspec_dev_dep?
if current.requirement != dep.requirement
current_requirement_open = current.requirements_list.include?(">= 0")
- if current.type == :development
- unless current_requirement_open || dep.type == :development
- Bundler.ui.warn "A gemspec development dependency (#{dep.name}, #{current.requirement}) is being overridden by a Gemfile dependency (#{dep.name}, #{dep.requirement}).\n" \
- "This behaviour may change in the future. Please remove either of them, or make sure they both have the same requirement\n" \
+ gemspec_dep = [dep, current].find(&:gemspec_dev_dep?)
+ if gemspec_dep
+ gemfile_dep = [dep, current].find(&:runtime?)
+
+ unless current_requirement_open
+ Bundler.ui.warn "A gemspec development dependency (#{gemspec_dep.name}, #{gemspec_dep.requirement}) is being overridden by a Gemfile dependency (#{gemfile_dep.name}, #{gemfile_dep.requirement}).\n" \
+ "This behaviour may change in the future. Please remove either of them, or make sure they both have the same requirement\n"
end
+
+ return if dep.gemspec_dev_dep?
else
update_prompt = ""
@@ -130,8 +135,8 @@ module Bundler
"You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})" \
"#{update_prompt}"
end
- elsif current.type == :development || dep.type == :development
- return if deleted_dep.nil?
+ elsif current.gemspec_dev_dep? || dep.gemspec_dev_dep?
+ return if dep.gemspec_dev_dep?
elsif current.source != dep.source
raise GemfileError, "You cannot specify the same gem twice coming from different sources.\n" \
"You specified that #{dep.name} (#{dep.requirement}) should come from " \
diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb
index a98d7fab30..a0ef931d07 100644
--- a/lib/bundler/version.rb
+++ b/lib/bundler/version.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false
module Bundler
- VERSION = "2.5.2".freeze
+ VERSION = "2.5.3".freeze
def self.bundler_major_version
@bundler_major_version ||= VERSION.split(".").first.to_i
diff --git a/lib/rubygems.rb b/lib/rubygems.rb
index fd5683f1be..fa3da5cc1a 100644
--- a/lib/rubygems.rb
+++ b/lib/rubygems.rb
@@ -9,7 +9,7 @@
require "rbconfig"
module Gem
- VERSION = "3.5.2"
+ VERSION = "3.5.3"
end
# Must be first since it unloads the prelude from 1.9.2
diff --git a/lib/rubygems/safe_marshal/elements.rb b/lib/rubygems/safe_marshal/elements.rb
index 067ab59d19..f8874b1b2f 100644
--- a/lib/rubygems/safe_marshal/elements.rb
+++ b/lib/rubygems/safe_marshal/elements.rb
@@ -133,6 +133,14 @@ module Gem
end
attr_reader :sign, :data
end
+
+ class UserClass < Element
+ def initialize(name, wrapped_object)
+ @name = name
+ @wrapped_object = wrapped_object
+ end
+ attr_reader :name, :wrapped_object
+ end
end
end
end
diff --git a/lib/rubygems/safe_marshal/reader.rb b/lib/rubygems/safe_marshal/reader.rb
index 7c3a703475..740be113e5 100644
--- a/lib/rubygems/safe_marshal/reader.rb
+++ b/lib/rubygems/safe_marshal/reader.rb
@@ -299,7 +299,9 @@ module Gem
end
def read_user_class
- raise NotImplementedError, "Reading Marshal objects of type user_class is not implemented"
+ name = read_element
+ wrapped_object = read_element
+ Elements::UserClass.new(name, wrapped_object)
end
end
end
diff --git a/lib/rubygems/safe_marshal/visitors/to_ruby.rb b/lib/rubygems/safe_marshal/visitors/to_ruby.rb
index 58c44fa8bf..a9f1d048d4 100644
--- a/lib/rubygems/safe_marshal/visitors/to_ruby.rb
+++ b/lib/rubygems/safe_marshal/visitors/to_ruby.rb
@@ -247,6 +247,30 @@ module Gem::SafeMarshal
end
end
+ def visit_Gem_SafeMarshal_Elements_UserClass(r)
+ if resolve_class(r.name) == ::Hash && r.wrapped_object.is_a?(Elements::Hash)
+
+ hash = register_object({}.compare_by_identity)
+
+ o = r.wrapped_object
+ o.pairs.each_with_index do |(k, v), i|
+ push_stack i
+ k = visit(k)
+ push_stack k
+ hash[k] = visit(v)
+ end
+
+ if o.is_a?(Elements::HashWithDefaultValue)
+ push_stack :default
+ hash.default = visit(o.default)
+ end
+
+ hash
+ else
+ raise UnsupportedError.new("Unsupported user class #{resolve_class(r.name)} in marshal stream", stack: formatted_stack)
+ end
+ end
+
def resolve_class(n)
@class_cache[n] ||= begin
to_s = resolve_symbol_name(n)
@@ -375,6 +399,12 @@ module Gem::SafeMarshal
end
end
+ class UnsupportedError < Error
+ def initialize(message, stack:)
+ super "#{message} @ #{stack.join "."}"
+ end
+ end
+
class FormatError < Error
end
diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb
index aa7c54ce4b..7c016ff3d8 100644
--- a/spec/bundler/commands/install_spec.rb
+++ b/spec/bundler/commands/install_spec.rb
@@ -460,6 +460,35 @@ RSpec.describe "bundle install with gem sources" do
expect(the_bundle).to include_gems("rubocop 1.37.1")
end
+ it "warns when a Gemfile dependency is overriding a gemspec development dependency, with different requirements" do
+ build_lib "my-gem", path: bundled_app do |s|
+ s.add_development_dependency "rails", ">= 5"
+ end
+
+ build_repo4 do
+ build_gem "rails", "7.0.8"
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "rails", "~> 7.0.8"
+
+ gemspec
+ G
+
+ bundle :install
+
+ expect(err).to include("A gemspec development dependency (rails, >= 5) is being overridden by a Gemfile dependency (rails, ~> 7.0.8).")
+ expect(err).to include("This behaviour may change in the future. Please remove either of them, or make sure they both have the same requirement")
+
+ # This is not the best behavior I believe, it would be better if both
+ # requirements are considered if they are compatible, and a version
+ # satisfying both is chosen. But not sure about changing it right now, so
+ # I went with a warning for the time being.
+ expect(the_bundle).to include_gems("rails 7.0.8")
+ end
+
it "does not warn if a gem is added once in Gemfile and also inside a gemspec as a development dependency, with same requirements, and different sources" do
build_lib "my-gem", path: bundled_app do |s|
s.add_development_dependency "activesupport"
diff --git a/test/rubygems/test_gem_safe_marshal.rb b/test/rubygems/test_gem_safe_marshal.rb
index 29685f458c..7f56d8d290 100644
--- a/test/rubygems/test_gem_safe_marshal.rb
+++ b/test/rubygems/test_gem_safe_marshal.rb
@@ -247,9 +247,41 @@ class TestGemSafeMarshal < Gem::TestCase
end
def test_hash_with_compare_by_identity
- pend "`read_user_class` not yet implemented"
+ with_const(Gem::SafeMarshal, :PERMITTED_CLASSES, %w[Hash]) do
+ assert_safe_load_as Hash.new.compare_by_identity.tap {|h|
+ h[+"a"] = 1
+ h[+"a"] = 2 }, additional_methods: [:compare_by_identity?], equality: false
+ assert_safe_load_as Hash.new.compare_by_identity, additional_methods: [:compare_by_identity?]
+ assert_safe_load_as Hash.new(0).compare_by_identity.tap {|h|
+ h[+"a"] = 1
+ h[+"a"] = 2 }, additional_methods: [:compare_by_identity?, :default], equality: false
+ end
+ end
+
+ class StringSubclass < ::String
+ end
- assert_safe_load_as Hash.new.compare_by_identity
+ def test_string_subclass
+ with_const(Gem::SafeMarshal, :PERMITTED_CLASSES, [StringSubclass.name]) do
+ with_const(Gem::SafeMarshal, :PERMITTED_IVARS, { StringSubclass.name => %w[E] }) do
+ e = assert_raise(Gem::SafeMarshal::Visitors::ToRuby::UnsupportedError) do
+ Gem::SafeMarshal.safe_load Marshal.dump StringSubclass.new("abc")
+ end
+ assert_equal "Unsupported user class #{StringSubclass.name} in marshal stream @ root.object", e.message
+ end
+ end
+ end
+
+ class ArraySubclass < ::Array
+ end
+
+ def test_array_subclass
+ with_const(Gem::SafeMarshal, :PERMITTED_CLASSES, [ArraySubclass.name]) do
+ e = assert_raise(Gem::SafeMarshal::Visitors::ToRuby::UnsupportedError) do
+ Gem::SafeMarshal.safe_load(Marshal.dump(ArraySubclass.new << "abc"))
+ end
+ assert_equal "Unsupported user class #{ArraySubclass.name} in marshal stream @ root", e.message
+ end
end
def test_frozen_object