From ef3c4a7aa7c0a79a00f4daa50e0be1184d9fe536 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Wed, 25 Sep 2024 16:53:56 +0900 Subject: Merge RubyGems-3.5.18 and Bundler-2.5.18 --- lib/bundler/cli/install.rb | 7 +- lib/bundler/definition.rb | 29 +++---- lib/bundler/lockfile_parser.rb | 2 +- lib/bundler/resolver.rb | 40 ++++++--- lib/bundler/resolver/base.rb | 6 ++ lib/bundler/resolver/package.rb | 11 ++- lib/bundler/source/git.rb | 8 +- lib/bundler/source/git/git_proxy.rb | 6 +- lib/bundler/templates/newgem/README.md.tt | 8 +- lib/bundler/version.rb | 2 +- lib/rubygems.rb | 2 +- lib/rubygems/commands/uninstall_command.rb | 11 ++- spec/bundler/bundler/bundler_spec.rb | 1 + spec/bundler/cache/git_spec.rb | 40 +++++++++ spec/bundler/commands/install_spec.rb | 44 +++++++++- spec/bundler/commands/lock_spec.rb | 60 ++++++++++++++ spec/bundler/commands/outdated_spec.rb | 2 +- spec/bundler/commands/remove_spec.rb | 2 +- spec/bundler/commands/update_spec.rb | 2 +- spec/bundler/install/allow_offline_install_spec.rb | 4 +- spec/bundler/install/deploy_spec.rb | 12 +++ spec/bundler/install/gemfile/sources_spec.rb | 94 ++++++++++++++++++---- spec/bundler/install/git_spec.rb | 13 +++ spec/bundler/install/prereleases_spec.rb | 2 +- spec/bundler/other/major_deprecation_spec.rb | 2 +- spec/bundler/support/builders.rb | 15 +++- spec/bundler/support/rubygems_ext.rb | 2 + .../test_gem_commands_uninstall_command.rb | 25 ++++++ .../custom_name/ext/custom_name_lib/Cargo.lock | 8 +- .../custom_name/ext/custom_name_lib/Cargo.toml | 2 +- .../rust_ruby_example/Cargo.lock | 8 +- .../rust_ruby_example/Cargo.toml | 2 +- 32 files changed, 396 insertions(+), 76 deletions(-) diff --git a/lib/bundler/cli/install.rb b/lib/bundler/cli/install.rb index a233d5d2e5..0ed8bc6ea2 100644 --- a/lib/bundler/cli/install.rb +++ b/lib/bundler/cli/install.rb @@ -25,9 +25,10 @@ module Bundler if options[:deployment] || options[:frozen] || Bundler.frozen_bundle? unless Bundler.default_lockfile.exist? - flag = "--deployment flag" if options[:deployment] - flag ||= "--frozen flag" if options[:frozen] - flag ||= "deployment setting" + flag = "--deployment flag" if options[:deployment] + flag ||= "--frozen flag" if options[:frozen] + flag ||= "deployment setting" if Bundler.settings[:deployment] + flag ||= "frozen setting" if Bundler.settings[:frozen] raise ProductionError, "The #{flag} requires a lockfile. Please make " \ "sure you have checked your #{SharedHelpers.relative_lockfile_path} into version control " \ "before deploying." diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 4cae6cd892..5471a72fdd 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -214,6 +214,7 @@ module Bundler @resolve = nil @resolver = nil @resolution_packages = nil + @source_requirements = nil @specs = nil Bundler.ui.debug "The definition is missing dependencies, failed to resolve & materialize locally (#{e})" @@ -476,9 +477,6 @@ module Bundler end end - attr_reader :sources - private :sources - def nothing_changed? return false unless lockfile_exists? @@ -502,8 +500,12 @@ module Bundler @unlocking end + attr_writer :source_requirements + private + attr_reader :sources + def should_add_extra_platforms? !lockfile_exists? && generic_local_platform_is_ruby? && !Bundler.settings[:force_ruby_platform] end @@ -569,7 +571,7 @@ module Bundler @resolution_packages ||= begin last_resolve = converge_locked_specs remove_invalid_platforms! - packages = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: @gems_to_unlock, prerelease: gem_version_promoter.pre?) + packages = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: @gems_to_unlock, prerelease: gem_version_promoter.pre?, prefer_local: @prefer_local) packages = additional_base_requirements_to_prevent_downgrades(packages, last_resolve) packages = additional_base_requirements_to_force_updates(packages) packages @@ -653,19 +655,6 @@ module Bundler sources.non_global_rubygems_sources.all?(&:dependency_api_available?) && !sources.aggregate_global_source? end - def pin_locally_available_names(source_requirements) - source_requirements.each_with_object({}) do |(name, original_source), new_source_requirements| - local_source = original_source.dup - local_source.local_only! - - new_source_requirements[name] = if local_source.specs.search(name).any? - local_source - else - original_source - end - end - end - def current_platform_locked? @platforms.any? do |bundle_platform| MatchPlatform.platforms_match?(bundle_platform, local_platform) @@ -972,12 +961,15 @@ module Bundler end def source_requirements + @source_requirements ||= find_source_requirements + end + + def find_source_requirements # Record the specs available in each gem's source, so that those # specs will be available later when the resolver knows where to # look for that gemspec (or its dependencies) source_requirements = if precompute_source_requirements_for_indirect_dependencies? all_requirements = source_map.all_requirements - all_requirements = pin_locally_available_names(all_requirements) if @prefer_local { default: default_source }.merge(all_requirements) else { default: Source::RubygemsAggregate.new(sources, source_map) }.merge(source_map.direct_requirements) @@ -1053,6 +1045,7 @@ module Bundler def dup_for_full_unlock unlocked_definition = self.class.new(@lockfile, @dependencies, @sources, true, @ruby_version, @optional_groups, @gemfiles) + unlocked_definition.source_requirements = source_requirements unlocked_definition.gem_version_promoter.tap do |gvp| gvp.level = gem_version_promoter.level gvp.strict = gem_version_promoter.strict diff --git a/lib/bundler/lockfile_parser.rb b/lib/bundler/lockfile_parser.rb index 1e11621e55..8a15e356c4 100644 --- a/lib/bundler/lockfile_parser.rb +++ b/lib/bundler/lockfile_parser.rb @@ -272,7 +272,7 @@ module Bundler end def parse_platform(line) - @platforms << Gem::Platform.new($1) if line =~ /^ (.*)$/ + @platforms << Gem::Platform.new($1.strip) if line =~ /^ (.*)$/ end def parse_bundled_with(line) diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index 2be7af94a2..a38b6974f8 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -84,9 +84,9 @@ module Bundler rescue PubGrub::SolveFailure => e incompatibility = e.incompatibility - names_to_unlock, names_to_allow_prereleases_for, extended_explanation = find_names_to_relax(incompatibility) + names_to_unlock, names_to_allow_prereleases_for, names_to_allow_remote_specs_for, extended_explanation = find_names_to_relax(incompatibility) - names_to_relax = names_to_unlock + names_to_allow_prereleases_for + names_to_relax = names_to_unlock + names_to_allow_prereleases_for + names_to_allow_remote_specs_for if names_to_relax.any? if names_to_unlock.any? @@ -96,11 +96,17 @@ module Bundler end if names_to_allow_prereleases_for.any? - Bundler.ui.debug "Found conflicts with dependencies with prereleases. Will retrying considering prereleases for #{names_to_allow_prereleases_for.join(", ")}...", true + Bundler.ui.debug "Found conflicts with dependencies with prereleases. Will retry considering prereleases for #{names_to_allow_prereleases_for.join(", ")}...", true @base.include_prereleases(names_to_allow_prereleases_for) end + if names_to_allow_remote_specs_for.any? + Bundler.ui.debug "Found conflicts with local versions of #{names_to_allow_remote_specs_for.join(", ")}. Will retry considering remote versions...", true + + @base.include_remote_specs(names_to_allow_remote_specs_for) + end + root, logger = setup_solver Bundler.ui.debug "Retrying resolution...", true @@ -120,6 +126,7 @@ module Bundler def find_names_to_relax(incompatibility) names_to_unlock = [] names_to_allow_prereleases_for = [] + names_to_allow_remote_specs_for = [] extended_explanation = nil while incompatibility.conflict? @@ -134,6 +141,8 @@ module Bundler names_to_unlock << name elsif package.ignores_prereleases? && @all_specs[name].any? {|s| s.version.prerelease? } names_to_allow_prereleases_for << name + elsif package.prefer_local? && @all_specs[name].any? {|s| !s.is_a?(StubSpecification) } + names_to_allow_remote_specs_for << name end no_versions_incompat = [cause.incompatibility, cause.satisfier].find {|incompat| incompat.cause.is_a?(PubGrub::Incompatibility::NoVersions) } @@ -143,7 +152,7 @@ module Bundler end end - [names_to_unlock.uniq, names_to_allow_prereleases_for.uniq, extended_explanation] + [names_to_unlock.uniq, names_to_allow_prereleases_for.uniq, names_to_allow_remote_specs_for.uniq, extended_explanation] end def parse_dependency(package, dependency) @@ -244,7 +253,7 @@ module Bundler def all_versions_for(package) name = package.name - results = (@base[name] + filter_prereleases(@all_specs[name], package)).uniq {|spec| [spec.version.hash, spec.platform] } + results = (@base[name] + filter_specs(@all_specs[name], package)).uniq {|spec| [spec.version.hash, spec.platform] } if name == "bundler" && !bundler_pinned_to_current_version? bundler_spec = Gem.loaded_specs["bundler"] @@ -368,12 +377,22 @@ module Bundler end end + def filter_specs(specs, package) + filter_remote_specs(filter_prereleases(specs, package), package) + end + def filter_prereleases(specs, package) return specs unless package.ignores_prereleases? && specs.size > 1 specs.reject {|s| s.version.prerelease? } end + def filter_remote_specs(specs, package) + return specs unless package.prefer_local? + + specs.select {|s| s.is_a?(StubSpecification) } + end + # Ignore versions that depend on themselves incorrectly def filter_invalid_self_dependencies(specs, name) specs.reject do |s| @@ -405,10 +424,13 @@ module Bundler dep_range = dep_constraint.range versions = select_sorted_versions(dep_package, dep_range) - if versions.empty? && dep_package.ignores_prereleases? - @all_versions.delete(dep_package) - @sorted_versions.delete(dep_package) - dep_package.consider_prereleases! + if versions.empty? + if dep_package.ignores_prereleases? || dep_package.prefer_local? + @all_versions.delete(dep_package) + @sorted_versions.delete(dep_package) + end + dep_package.consider_prereleases! if dep_package.ignores_prereleases? + dep_package.consider_remote_versions! if dep_package.prefer_local? versions = select_sorted_versions(dep_package, dep_range) end diff --git a/lib/bundler/resolver/base.rb b/lib/bundler/resolver/base.rb index b0c5c58047..3f2436672a 100644 --- a/lib/bundler/resolver/base.rb +++ b/lib/bundler/resolver/base.rb @@ -72,6 +72,12 @@ module Bundler end end + def include_remote_specs(names) + names.each do |name| + get_package(name).consider_remote_versions! + end + end + private def indirect_pins(names) diff --git a/lib/bundler/resolver/package.rb b/lib/bundler/resolver/package.rb index 16c43d05af..5aecc12d05 100644 --- a/lib/bundler/resolver/package.rb +++ b/lib/bundler/resolver/package.rb @@ -15,7 +15,7 @@ module Bundler class Package attr_reader :name, :platforms, :dependency, :locked_version - def initialize(name, platforms, locked_specs:, unlock:, prerelease: false, dependency: nil) + def initialize(name, platforms, locked_specs:, unlock:, prerelease: false, prefer_local: false, dependency: nil) @name = name @platforms = platforms @locked_version = locked_specs[name].first&.version @@ -23,6 +23,7 @@ module Bundler @dependency = dependency || Dependency.new(name, @locked_version) @top_level = !dependency.nil? @prerelease = @dependency.prerelease? || @locked_version&.prerelease? || prerelease ? :consider_first : :ignore + @prefer_local = prefer_local end def platform_specs(specs) @@ -69,6 +70,14 @@ module Bundler @prerelease = :consider_last end + def prefer_local? + @prefer_local + end + + def consider_remote_versions! + @prefer_local = false + end + def force_ruby_platform? @dependency.force_ruby_platform end diff --git a/lib/bundler/source/git.rb b/lib/bundler/source/git.rb index 3f69ea1e65..9ce74adc2c 100644 --- a/lib/bundler/source/git.rb +++ b/lib/bundler/source/git.rb @@ -188,9 +188,11 @@ module Bundler end def specs(*) - set_cache_path!(app_cache_path) if has_app_cache? && !local? + set_cache_path!(app_cache_path) if use_app_cache? if requires_checkout? && !@copied + FileUtils.rm_rf(app_cache_path) if use_app_cache? && git_proxy.not_a_bare_repository? + fetch checkout end @@ -321,6 +323,10 @@ module Bundler cached_revision && super end + def use_app_cache? + has_app_cache? && !local? + end + def requires_checkout? allow_git_ops? && !local? && !cached_revision_checked_out? end diff --git a/lib/bundler/source/git/git_proxy.rb b/lib/bundler/source/git/git_proxy.rb index 2fc9c6535f..768d40392f 100644 --- a/lib/bundler/source/git/git_proxy.rb +++ b/lib/bundler/source/git/git_proxy.rb @@ -84,6 +84,10 @@ module Bundler end end + def not_a_bare_repository? + git_local("rev-parse", "--is-bare-repository", dir: path).strip == "false" + end + def contains?(commit) allowed_with_path do result, status = git_null("branch", "--contains", commit, dir: path) @@ -332,8 +336,6 @@ module Bundler config_auth = Bundler.settings[remote.to_s] || Bundler.settings[remote.host] remote.userinfo ||= config_auth remote.to_s - elsif File.exist?(uri) - "file://#{uri}" else uri.to_s end diff --git a/lib/bundler/templates/newgem/README.md.tt b/lib/bundler/templates/newgem/README.md.tt index 5bf36378e8..f9c97d5c7e 100644 --- a/lib/bundler/templates/newgem/README.md.tt +++ b/lib/bundler/templates/newgem/README.md.tt @@ -10,11 +10,15 @@ TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_O Install the gem and add to the application's Gemfile by executing: - $ bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG +```bash +bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG +``` If bundler is not being used to manage dependencies, install the gem by executing: - $ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG +```bash +gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG +``` ## Usage diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb index 7920aaec0f..eac1ffb45e 100644 --- a/lib/bundler/version.rb +++ b/lib/bundler/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module Bundler - VERSION = "2.5.17".freeze + VERSION = "2.5.18".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 beaad57618..36f0f7f7c2 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -9,7 +9,7 @@ require "rbconfig" module Gem - VERSION = "3.5.17" + VERSION = "3.5.18" end # Must be first since it unloads the prelude from 1.9.2 diff --git a/lib/rubygems/commands/uninstall_command.rb b/lib/rubygems/commands/uninstall_command.rb index 283bc96ce3..3d6e41e49e 100644 --- a/lib/rubygems/commands/uninstall_command.rb +++ b/lib/rubygems/commands/uninstall_command.rb @@ -157,9 +157,14 @@ that is a dependency of an existing gem. You can use the gem_specs = Gem::Specification.find_all_by_name(name, original_gem_version[name]) - say("Gem '#{name}' is not installed") if gem_specs.empty? - gem_specs.each do |spec| - deplist.add spec + if gem_specs.empty? + say("Gem '#{name}' is not installed") + else + gem_specs.reject!(&:default_gem?) if gem_specs.size > 1 + + gem_specs.each do |spec| + deplist.add spec + end end end diff --git a/spec/bundler/bundler/bundler_spec.rb b/spec/bundler/bundler/bundler_spec.rb index 2c8453da4d..7cfc12a6f6 100644 --- a/spec/bundler/bundler/bundler_spec.rb +++ b/spec/bundler/bundler/bundler_spec.rb @@ -267,6 +267,7 @@ RSpec.describe Bundler do it "should issue a warning and return a temporary user home" do allow(Bundler.rubygems).to receive(:user_home).and_return(path) allow(File).to receive(:directory?).with(path).and_return true + allow(File).to receive(:writable?).and_call_original allow(File).to receive(:writable?).with(path).and_return false allow(File).to receive(:directory?).with(dotbundle).and_return false allow(Bundler).to receive(:tmp).and_return(Pathname.new("/tmp/trulyrandom")) diff --git a/spec/bundler/cache/git_spec.rb b/spec/bundler/cache/git_spec.rb index 4e3038f3ce..cbd755872c 100644 --- a/spec/bundler/cache/git_spec.rb +++ b/spec/bundler/cache/git_spec.rb @@ -258,6 +258,46 @@ RSpec.describe "bundle cache with git" do end end + it "can install after bundle cache generated with an older Bundler that kept checkouts in the cache" do + git = build_git("foo") + locked_revision = git.ref_for("main") + path_revision = git.ref_for("main", 11) + + git_path = lib_path("foo-1.0") + + gemfile <<-G + source "https://gem.repo1" + gem "foo", :git => '#{git_path}' + G + lockfile <<~L + GIT + remote: #{git_path}/ + revision: #{locked_revision} + specs: + foo (1.0) + + GEM + remote: https://gem.repo1/ + specs: + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + foo! + + BUNDLED WITH + #{Bundler::VERSION} + L + + # Simulate an old incorrect situation where vendor/cache would be the install location of git gems + FileUtils.mkdir_p bundled_app("vendor/cache") + FileUtils.cp_r git_path, bundled_app("vendor/cache/foo-1.0-#{path_revision}") + FileUtils.rm_rf bundled_app("vendor/cache/foo-1.0-#{path_revision}/.git") + + bundle :install, env: { "BUNDLE_DEPLOYMENT" => "true", "BUNDLE_CACHE_ALL" => "true" } + end + it "respects the --no-install flag" do git = build_git "foo", &:add_c_extension ref = git.ref_for("main", 11) diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb index 09f920052a..c89ed0c870 100644 --- a/spec/bundler/commands/install_spec.rb +++ b/spec/bundler/commands/install_spec.rb @@ -1362,12 +1362,20 @@ RSpec.describe "bundle install with gem sources" do build_gem "foo", "1.0.1" build_gem "foo", "1.0.0" build_gem "bar", "1.0.0" + + build_gem "a", "1.0.0" do |s| + s.add_dependency "foo", "~> 1.0.0" + end + + build_gem "b", "1.0.0" do |s| + s.add_dependency "foo", "~> 1.0.1" + end end system_gems "foo-1.0.0", path: default_bundle_path, gem_repo: gem_repo4 end - it "fetches remote sources only when not available locally" do + it "fetches remote sources when not available locally" do install_gemfile <<-G, "prefer-local": true, verbose: true source "https://gem.repo4" @@ -1378,6 +1386,40 @@ RSpec.describe "bundle install with gem sources" do expect(out).to include("Using foo 1.0.0").and include("Fetching bar 1.0.0").and include("Installing bar 1.0.0") expect(last_command).to be_success end + + it "fetches remote sources when local version does not match requirements" do + install_gemfile <<-G, "prefer-local": true, verbose: true + source "https://gem.repo4" + + gem "foo", "1.0.1" + gem "bar" + G + + expect(out).to include("Fetching foo 1.0.1").and include("Installing foo 1.0.1").and include("Fetching bar 1.0.0").and include("Installing bar 1.0.0") + expect(last_command).to be_success + end + + it "uses the locally available version for sub-dependencies when possible" do + install_gemfile <<-G, "prefer-local": true, verbose: true + source "https://gem.repo4" + + gem "a" + G + + expect(out).to include("Using foo 1.0.0").and include("Fetching a 1.0.0").and include("Installing a 1.0.0") + expect(last_command).to be_success + end + + it "fetches remote sources for sub-dependencies when the locally available version does not satisfy the requirement" do + install_gemfile <<-G, "prefer-local": true, verbose: true + source "https://gem.repo4" + + gem "b" + G + + expect(out).to include("Fetching foo 1.0.1").and include("Installing foo 1.0.1").and include("Fetching b 1.0.0").and include("Installing b 1.0.0") + expect(last_command).to be_success + end end context "with a symlinked configured as bundle path and a gem with symlinks" do diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb index 2502eae33e..6d005cfc96 100644 --- a/spec/bundler/commands/lock_spec.rb +++ b/spec/bundler/commands/lock_spec.rb @@ -1990,4 +1990,64 @@ RSpec.describe "bundle lock" do L end end + + context "when lockfile has incorrectly indented platforms" do + before do + build_repo4 do + build_gem "ffi", "1.1.0" do |s| + s.platform = "x86_64-linux" + end + + build_gem "ffi", "1.1.0" do |s| + s.platform = "arm64-darwin" + end + end + + gemfile <<~G + source "https://gem.repo4" + + gem "ffi" + G + + lockfile <<~L + GEM + remote: https://gem.repo4/ + specs: + ffi (1.1.0-arm64-darwin) + + PLATFORMS + arm64-darwin + + DEPENDENCIES + ffi + + BUNDLED WITH + #{Bundler::VERSION} + L + end + + it "does not remove any gems" do + simulate_platform "x86_64-linux" do + bundle "lock --update" + end + + expect(lockfile).to eq <<~L + GEM + remote: https://gem.repo4/ + specs: + ffi (1.1.0-arm64-darwin) + ffi (1.1.0-x86_64-linux) + + PLATFORMS + arm64-darwin + x86_64-linux + + DEPENDENCIES + ffi + + BUNDLED WITH + #{Bundler::VERSION} + L + end + end end diff --git a/spec/bundler/commands/outdated_spec.rb b/spec/bundler/commands/outdated_spec.rb index c5663acfb8..55706df0d6 100644 --- a/spec/bundler/commands/outdated_spec.rb +++ b/spec/bundler/commands/outdated_spec.rb @@ -162,7 +162,7 @@ RSpec.describe "bundle outdated" do build_gem "vcr", "6.0.0" end - build_repo gem_repo3 do + build_repo3 do build_gem "pkg-gem-flowbyte-with-dep", "1.0.0" do |s| s.add_dependency "oj" end diff --git a/spec/bundler/commands/remove_spec.rb b/spec/bundler/commands/remove_spec.rb index d2d7d1b6a8..7ac2ea9b26 100644 --- a/spec/bundler/commands/remove_spec.rb +++ b/spec/bundler/commands/remove_spec.rb @@ -409,7 +409,7 @@ RSpec.describe "bundle remove" do context "with sources" do before do - build_repo gem_repo3 do + build_repo3 do build_gem "rspec" end end diff --git a/spec/bundler/commands/update_spec.rb b/spec/bundler/commands/update_spec.rb index feea554d22..3760d49d7f 100644 --- a/spec/bundler/commands/update_spec.rb +++ b/spec/bundler/commands/update_spec.rb @@ -956,7 +956,7 @@ RSpec.describe "bundle update" do build_gem "vcr", "6.0.0" end - build_repo gem_repo3 do + build_repo3 do build_gem "pkg-gem-flowbyte-with-dep", "1.0.0" do |s| s.add_dependency "oj" end diff --git a/spec/bundler/install/allow_offline_install_spec.rb b/spec/bundler/install/allow_offline_install_spec.rb index 2d313531e0..21b0568f7d 100644 --- a/spec/bundler/install/allow_offline_install_spec.rb +++ b/spec/bundler/install/allow_offline_install_spec.rb @@ -53,10 +53,10 @@ RSpec.describe "bundle install with :allow_offline_install" do File.open(tmp("broken_path/git"), "w", 0o755) do |f| f.puts <<~RUBY #!/usr/bin/env ruby - fetch_args = %w(fetch --force --quiet) + fetch_args = %w(fetch --force --quiet --no-tags) clone_args = %w(clone --bare --no-hardlinks --quiet) - if (fetch_args.-(ARGV).empty? || clone_args.-(ARGV).empty?) && ARGV.any? {|arg| arg.start_with?("file://") } + if (fetch_args.-(ARGV).empty? || clone_args.-(ARGV).empty?) && File.exist?(ARGV[ARGV.index("--") + 1]) warn "git remote ops have been disabled" exit 1 end diff --git a/spec/bundler/install/deploy_spec.rb b/spec/bundler/install/deploy_spec.rb index dfb352f170..7b6e775b4c 100644 --- a/spec/bundler/install/deploy_spec.rb +++ b/spec/bundler/install/deploy_spec.rb @@ -74,6 +74,18 @@ RSpec.describe "install in deployment or frozen mode" do end end + it "fails without a lockfile and says that deployment requires a lock" do + bundle "config deployment true" + bundle "install", raise_on_error: false + expect(err).to include("The deployment setting requires a lockfile") + end + + it "fails without a lockfile and says that frozen requires a lock" do + bundle "config frozen true" + bundle "install", raise_on_error: false + expect(err).to include("The frozen setting requires a lockfile") + end + it "still works if you are not in the app directory and specify --gemfile" do bundle "install" simulate_new_machine diff --git a/spec/bundler/install/gemfile/sources_spec.rb b/spec/bundler/install/gemfile/sources_spec.rb index f05e61f0b2..84af5c0d06 100644 --- a/spec/bundler/install/gemfile/sources_spec.rb +++ b/spec/bundler/install/gemfile/sources_spec.rb @@ -8,7 +8,7 @@ RSpec.describe "bundle install with gems on multiple sources" do before do # Oh no! Someone evil is trying to hijack myrack :( # need this to be broken to check for correct source ordering - build_repo gem_repo3 do + build_repo3 do build_gem "myrack", repo3_myrack_version do |s| s.write "lib/myrack.rb", "MYRACK = 'FAIL'" end @@ -156,7 +156,7 @@ RSpec.describe "bundle install with gems on multiple sources" do before do # Oh no! Someone evil is trying to hijack myrack :( # need this to be broken to check for correct source ordering - build_repo gem_repo3 do + build_repo3 do build_gem "myrack", "1.0.0" do |s| s.write "lib/myrack.rb", "MYRACK = 'FAIL'" end @@ -200,7 +200,7 @@ RSpec.describe "bundle install with gems on multiple sources" do before do # Oh no! Someone evil is trying to hijack myrack :( # need this to be broken to check for correct source ordering - build_repo gem_repo3 do + build_repo3 do build_gem "myrack", "1.0.0" do |s| s.write "lib/myrack.rb", "MYRACK = 'FAIL'" end @@ -225,7 +225,7 @@ RSpec.describe "bundle install with gems on multiple sources" do context "when a pinned gem has an indirect dependency in the pinned source" do before do - build_repo gem_repo3 do + build_repo3 do build_gem "depends_on_myrack", "1.0.1" do |s| s.add_dependency "myrack" end @@ -287,7 +287,7 @@ RSpec.describe "bundle install with gems on multiple sources" do before do # In these tests, we need a working myrack gem in repo2 and not repo3 - build_repo gem_repo3 do + build_repo3 do build_gem "depends_on_myrack", "1.0.1" do |s| s.add_dependency "myrack" end @@ -502,7 +502,7 @@ RSpec.describe "bundle install with gems on multiple sources" do before do build_repo2 - build_repo gem_repo3 do + build_repo3 do build_gem "private_gem_1", "1.0.0" build_gem "private_gem_2", "1.0.0" end @@ -528,7 +528,7 @@ RSpec.describe "bundle install with gems on multiple sources" do before do build_repo2 - build_repo gem_repo3 do + build_repo3 do build_gem "depends_on_missing", "1.0.1" do |s| s.add_dependency "missing" end @@ -565,7 +565,7 @@ RSpec.describe "bundle install with gems on multiple sources" do end end - build_repo gem_repo3 do + build_repo3 do build_gem "unrelated_gem", "1.0.0" end @@ -645,7 +645,7 @@ RSpec.describe "bundle install with gems on multiple sources" do context "when a scoped gem has a deeply nested indirect dependency" do before do - build_repo gem_repo3 do + build_repo3 do build_gem "depends_on_depends_on_myrack", "1.0.1" do |s| s.add_dependency "depends_on_myrack" end @@ -764,7 +764,7 @@ RSpec.describe "bundle install with gems on multiple sources" do build_gem "zeitwerk", "2.4.2" end - build_repo gem_repo3 do + build_repo3 do build_gem "sidekiq-pro", "5.2.1" do |s| s.add_dependency "connection_pool", ">= 2.2.3" s.add_dependency "sidekiq", ">= 6.1.0" @@ -1080,7 +1080,7 @@ RSpec.describe "bundle install with gems on multiple sources" do context "when a pinned gem has an indirect dependency with more than one level of indirection in the default source " do before do - build_repo gem_repo3 do + build_repo3 do build_gem "handsoap", "0.2.5.5" do |s| s.add_dependency "nokogiri", ">= 1.2.3" end @@ -1157,7 +1157,7 @@ RSpec.describe "bundle install with gems on multiple sources" do context "with a gem that is only found in the wrong source" do before do - build_repo gem_repo3 do + build_repo3 do build_gem "not_in_repo1", "1.0.0" end @@ -1250,7 +1250,7 @@ RSpec.describe "bundle install with gems on multiple sources" do end before do - build_repo gem_repo3 do + build_repo3 do build_gem "myrack", "0.9.1" end @@ -1393,7 +1393,7 @@ RSpec.describe "bundle install with gems on multiple sources" do context "re-resolving" do context "when there is a mix of sources in the gemfile" do before do - build_repo gem_repo3 do + build_repo3 do build_gem "myrack" end @@ -1921,4 +1921,70 @@ RSpec.describe "bundle install with gems on multiple sources" do expect(err).to include("Could not find gem 'example' in rubygems repository https://gem.repo4/") end end + + context "when a gem has versions in two sources, but only the locked one has updates" do + let(:original_lockfile) do + <<~L + GEM + remote: https://main.source/ + specs: + activesupport (1.0) + bigdecimal + bigdecimal (1.0.0) + + GEM + remote: https://main.source/extra/ + specs: + foo (1.0) + bigdecimal + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + activesupport + foo! + + BUNDLED WITH + #{Bundler::VERSION} + L + end + + before do + build_repo3 do + build_gem "activesupport" do |s| + s.add_dependency "bigdecimal" + end + + build_gem "bigdecimal", "1.0.0" + build_gem "bigdecimal", "3.3.1" + end + + build_repo4 do + build_gem "foo" do |s| + s.add_dependency "bigdecimal" + end + + build_gem "bigdecimal", "1.0.0" + end + + gemfile <<~G + source "https://main.source" + + gem "activesupport" + + source "https://main.source/extra" do + gem "foo" + end + G + + lockfile original_lockfile + end + + it "properly upgrades the lockfile when updating that specific gem" do + bundle "update bigdecimal --conservative", artifice: "compact_index_extra_api", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo3.to_s } + + expect(lockfile).to eq original_lockfile.gsub("bigdecimal (1.0.0)", "bigdecimal (3.3.1)") + end + end end diff --git a/spec/bundler/install/git_spec.rb b/spec/bundler/install/git_spec.rb index 179e6df4b4..caed4571e5 100644 --- a/spec/bundler/install/git_spec.rb +++ b/spec/bundler/install/git_spec.rb @@ -14,6 +14,19 @@ RSpec.describe "bundle install" do expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}" end + it "displays the revision hash of the gem repository when passed a relative local path" do + build_git "foo", "1.0", path: lib_path("foo") + + relative_path = lib_path("foo").relative_path_from(bundled_app) + install_gemfile <<-G, verbose: true + source "https://gem.repo1" + gem "foo", :git => "#{relative_path}" + G + + expect(out).to include("Using foo 1.0 from #{relative_path} (at main@#{revision_for(lib_path("foo"))[0..6]})") + expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}" + end + it "displays the correct default branch", git: ">= 2.28.0" do build_git "foo", "1.0", path: lib_path("foo"), default_branch: "main" diff --git a/spec/bundler/install/prereleases_spec.rb b/spec/bundler/install/prereleases_spec.rb index cde27c14fc..57764ce722 100644 --- a/spec/bundler/install/prereleases_spec.rb +++ b/spec/bundler/install/prereleases_spec.rb @@ -38,7 +38,7 @@ RSpec.describe "bundle install" do describe "when prerelease gems are not available" do it "still works" do - build_repo gem_repo3 do + build_repo3 do build_gem "myrack" end FileUtils.rm_rf Dir[gem_repo3("prerelease*")] diff --git a/spec/bundler/other/major_deprecation_spec.rb b/spec/bundler/other/major_deprecation_spec.rb index 192dc7413e..f05a8c43f8 100644 --- a/spec/bundler/other/major_deprecation_spec.rb +++ b/spec/bundler/other/major_deprecation_spec.rb @@ -454,7 +454,7 @@ RSpec.describe "major deprecations" do context "bundle install in frozen mode with a lockfile with a single rubygems section with multiple remotes" do before do - build_repo gem_repo3 do + build_repo3 do build_gem "myrack", "0.9.1" end diff --git a/spec/bundler/support/builders.rb b/spec/bundler/support/builders.rb index e2a7aed219..a0b94004f2 100644 --- a/spec/bundler/support/builders.rb +++ b/spec/bundler/support/builders.rb @@ -188,9 +188,15 @@ module Spec # A repo that has no pre-installed gems included. (The caller completely # determines the contents with the block.) + def build_repo3(**kwargs, &blk) + build_empty_repo gem_repo3, **kwargs, &blk + end + + # Like build_repo3, this is a repo that has no pre-installed gems included. + # We have two different methods for situations where two different empty + # sources are needed. def build_repo4(**kwargs, &blk) - FileUtils.rm_rf gem_repo4 - build_repo(gem_repo4, **kwargs, &blk) + build_empty_repo gem_repo4, **kwargs, &blk end def update_repo4(&blk) @@ -307,6 +313,11 @@ module Spec private + def build_empty_repo(gem_repo, **kwargs, &blk) + FileUtils.rm_rf gem_repo + build_repo(gem_repo, **kwargs, &blk) + end + def build_with(builder, name, args, &blk) @_build_path ||= nil @_build_repo ||= nil diff --git a/spec/bundler/support/rubygems_ext.rb b/spec/bundler/support/rubygems_ext.rb index fb76e34a74..1fc5aa16c0 100644 --- a/spec/bundler/support/rubygems_ext.rb +++ b/spec/bundler/support/rubygems_ext.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +abort "RubyGems only supports Ruby 3.0 or higher" if RUBY_VERSION < "3.0.0" + require_relative "path" $LOAD_PATH.unshift(Spec::Path.source_lib_dir.to_s) diff --git a/test/rubygems/test_gem_commands_uninstall_command.rb b/test/rubygems/test_gem_commands_uninstall_command.rb index 5bbb5856a6..32553d1730 100644 --- a/test/rubygems/test_gem_commands_uninstall_command.rb +++ b/test/rubygems/test_gem_commands_uninstall_command.rb @@ -111,6 +111,31 @@ class TestGemCommandsUninstallCommand < Gem::InstallerTestCase assert_equal "There was both a regular copy and a default copy of z-1. The regular copy was successfully uninstalled, but the default copy was left around because default gems can't be removed.", output.shift end + def test_execute_does_not_error_on_shadowed_default_gems + z_1_default = new_default_spec "z", "1" + install_default_gems z_1_default + + z_1 = util_spec "z", "1" do |spec| + spec.date = "2024-01-01" + end + install_gem z_1 + + Gem::Specification.reset + + @cmd.options[:args] = %w[z:1] + + use_ui @ui do + @cmd.execute + end + + output = @ui.output.split "\n" + assert_equal "Successfully uninstalled z-1", output.shift + assert_equal "There was both a regular copy and a default copy of z-1. The regular copy was successfully uninstalled, but the default copy was left around because default gems can't be removed.", output.shift + + error = @ui.error.split "\n" + assert_empty error + end + def test_execute_dependency_order initial_install diff --git a/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock b/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock index b5a20091e9..8809cfcf89 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock +++ b/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.lock @@ -152,18 +152,18 @@ dependencies = [ [[package]] name = "rb-sys" -version = "0.9.99" +version = "0.9.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83151cfea2b67db2444f68c53b119ff77cff235ad711c765072e4daf8f3185b" +checksum = "1ba2704ccfa7875c91792c57a9aa7c3caac524d3036c122e36eeddad6f6e7c6f" dependencies = [ "rb-sys-build", ] [[package]] name = "rb-sys-build" -version = "0.9.99" +version = "0.9.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d038214c118ad4a75db555ccb78672e17e1c5c10f344456cd129008dbaa7de" +checksum = "c73585ec80c217b7a81257ca9bb89b191b5e452ec4b9106dc4c2e4e96a822242" dependencies = [ "bindgen", "lazy_static", diff --git a/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml b/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml index 8d58d41044..9b931ba722 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml +++ b/test/rubygems/test_gem_ext_cargo_builder/custom_name/ext/custom_name_lib/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -rb-sys = "0.9.99" +rb-sys = "0.9.101" diff --git a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock index f3969f3621..8290fbd935 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock +++ b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.lock @@ -145,18 +145,18 @@ dependencies = [ [[package]] name = "rb-sys" -version = "0.9.98" +version = "0.9.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8914b2e6af10bd50dd7aaac8c5146872d3924d6012929b4ff504e988f6badd24" +checksum = "1ba2704ccfa7875c91792c57a9aa7c3caac524d3036c122e36eeddad6f6e7c6f" dependencies = [ "rb-sys-build", ] [[package]] name = "rb-sys-build" -version = "0.9.98" +version = "0.9.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12af68c9757d419b82d65a12b5db538990dfe9416049fea3f0ba4b9a8ca108cd" +checksum = "c73585ec80c217b7a81257ca9bb89b191b5e452ec4b9106dc4c2e4e96a822242" dependencies = [ "bindgen", "lazy_static", diff --git a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml index ac8fd45bc6..664c6ccd43 100644 --- a/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml +++ b/test/rubygems/test_gem_ext_cargo_builder/rust_ruby_example/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -rb-sys = "0.9.98" +rb-sys = "0.9.101" -- cgit v1.2.3