diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ab75347eeab..7d28ecb8896c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## 4.0.12 / 2026-05-20 + +### Enhancements: + +* Remove cygwin from WIN_PATTERNS. Pull request [#9527](https://github.com/ruby/rubygems/pull/9527) by fd00 +* Installs bundler 4.0.12 as a default gem. + +### Bug fixes: + +* Fall back to lockfile version when `BUNDLE_VERSION` is "lockfile". Pull request [#9545](https://github.com/ruby/rubygems/pull/9545) by hsbt +* Read `BUNDLE_VERSION` env var in `BundlerVersionFinder`. Pull request [#9538](https://github.com/ruby/rubygems/pull/9538) by hsbt + ## 4.0.11 / 2026-04-30 ### Enhancements: diff --git a/bundler/CHANGELOG.md b/bundler/CHANGELOG.md index 368b4dc51578..78b630a1254a 100644 --- a/bundler/CHANGELOG.md +++ b/bundler/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 4.0.12 / 2026-05-20 + +### Enhancements: + +* Make `bundle config get` return status 1 when the value is not set. Pull request [#9505](https://github.com/ruby/rubygems/pull/9505) by willnet +* Use Pathname#absolute?. Pull request [#9529](https://github.com/ruby/rubygems/pull/9529) by nobu +* Deprecate parsing non-lockfile content in LockfileParser. Pull request [#9502](https://github.com/ruby/rubygems/pull/9502) by kurotaky +* Print a warning for a potential confusion from the indirect dependencies. Pull request [#5029](https://github.com/ruby/rubygems/pull/5029) by junaruga +* Respect Gemfile bundler setting in `Bundler.setup`. Pull request [#4892](https://github.com/ruby/rubygems/pull/4892) by godfat + +### Bug fixes: + +* Gracefully handle missing checksums in Compact Index. Pull request [#9492](https://github.com/ruby/rubygems/pull/9492) by jneen +* Skip git source exclusion when lockfile cannot backfill. Pull request [#9544](https://github.com/ruby/rubygems/pull/9544) by yahonda +* Fix bundle config gemfile unset behavior. Pull request [#9514](https://github.com/ruby/rubygems/pull/9514) by afurm + ## 4.0.11 / 2026-04-30 ### Enhancements: diff --git a/bundler/lib/bundler.rb b/bundler/lib/bundler.rb index 26e946e05981..8bec10c0db87 100644 --- a/bundler/lib/bundler.rb +++ b/bundler/lib/bundler.rb @@ -156,6 +156,7 @@ def setup(*groups) # Return if all groups are already loaded return @setup if defined?(@setup) && @setup + configure_custom_gemfile definition.validate_runtime! SharedHelpers.print_major_deprecations! @@ -586,6 +587,15 @@ def configure_gem_home_and_path(path = bundle_path) Bundler.rubygems.clear_paths end + def configure_custom_gemfile(custom_gemfile = nil) + custom_gemfile ||= Bundler.settings[:gemfile] + + if custom_gemfile && !custom_gemfile.empty? + Bundler::SharedHelpers.set_env "BUNDLE_GEMFILE", File.expand_path(custom_gemfile) + reset_settings_and_root! + end + end + def self_manager @self_manager ||= begin require_relative "bundler/self_manager" diff --git a/bundler/lib/bundler/cli.rb b/bundler/lib/bundler/cli.rb index 0f234f26b956..7a04f3da2390 100644 --- a/bundler/lib/bundler/cli.rb +++ b/bundler/lib/bundler/cli.rb @@ -61,18 +61,18 @@ def initialize(*args) current_cmd = args.last[:current_command].name - custom_gemfile = options[:gemfile] || Bundler.settings[:gemfile] - if custom_gemfile && !custom_gemfile.empty? - Bundler::SharedHelpers.set_env "BUNDLE_GEMFILE", File.expand_path(custom_gemfile) - reset_settings = true - end - - # lock --lockfile works differently than install --lockfile - unless current_cmd == "lock" - custom_lockfile = options[:lockfile] || ENV["BUNDLE_LOCKFILE"] || Bundler.settings[:lockfile] - if custom_lockfile && !custom_lockfile.empty? - Bundler::SharedHelpers.set_env "BUNDLE_LOCKFILE", File.expand_path(custom_lockfile) - reset_settings = true + # `bundle config` manages stored settings, so avoid promoting settings + # like `gemfile` or `lockfile` to environment variables before it runs. + unless current_cmd == "config" + Bundler.configure_custom_gemfile(options[:gemfile]) + + # lock --lockfile works differently than install --lockfile + unless current_cmd == "lock" + custom_lockfile = options[:lockfile] || ENV["BUNDLE_LOCKFILE"] || Bundler.settings[:lockfile] + if custom_lockfile && !custom_lockfile.empty? + Bundler::SharedHelpers.set_env "BUNDLE_LOCKFILE", File.expand_path(custom_lockfile) + reset_settings = true + end end end diff --git a/bundler/lib/bundler/cli/config.rb b/bundler/lib/bundler/cli/config.rb index 6a77e4a65ea3..976cda748466 100644 --- a/bundler/lib/bundler/cli/config.rb +++ b/bundler/lib/bundler/cli/config.rb @@ -87,16 +87,21 @@ def run if value.nil? warn_unused_scope "Ignoring --#{scope} since no value to set was given" + current_value = Bundler.settings[name] if options[:parseable] if value = Bundler.settings[name] Bundler.ui.info("#{name}=#{value}") end - return + else + confirm(name) end - confirm(name) - return + if current_value.nil? + exit 1 + else + return + end end Bundler.ui.info(message) if message diff --git a/bundler/lib/bundler/compact_index_client/parser.rb b/bundler/lib/bundler/compact_index_client/parser.rb index 43581fd7efe5..ad0d17ed4a4a 100644 --- a/bundler/lib/bundler/compact_index_client/parser.rb +++ b/bundler/lib/bundler/compact_index_client/parser.rb @@ -71,7 +71,10 @@ def gem_parser # This method gets called at least once for every gem when parsing versions. def parse_version_checksum(line, checksums) return unless (name_end = line.index(" ")) # Artifactory bug causes blank lines in artifactor index files - return unless (checksum_start = line.index(" ", name_end + 1) + 1) + checksum_start = line.index(" ", name_end + 1) + return unless checksum_start + checksum_start += 1 + checksum_end = line.size - checksum_start line.freeze # allows slicing into the string to not allocate a copy of the line diff --git a/bundler/lib/bundler/definition.rb b/bundler/lib/bundler/definition.rb index 69842b4813fa..6c12d790722e 100644 --- a/bundler/lib/bundler/definition.rb +++ b/bundler/lib/bundler/definition.rb @@ -783,7 +783,25 @@ def start_resolution end def precompute_source_requirements_for_indirect_dependencies? - sources.non_global_rubygems_sources.all?(&:dependency_api_available?) + if sources.non_global_rubygems_sources.all?(&:dependency_api_available?) + true + else + non_dependency_api_warning + false + end + end + + def non_dependency_api_warning + non_api_sources = sources.non_global_rubygems_sources.reject(&:dependency_api_available?) + non_api_source_names = non_api_sources.map {|d| " * #{d}" }.join("\n") + + msg = String.new + msg << "Your Gemfile contains scoped sources that don't implement a dependency API, namely:\n\n" + msg << non_api_source_names + msg << "\n\nUsing the above gem servers may result in installing unexpected gems. " \ + "To resolve this warning, make sure you use gem servers that implement dependency APIs, " \ + "such as gemstash or geminabox gem servers." + Bundler.ui.warn msg end def current_platform_locked? @@ -1159,16 +1177,20 @@ def excluded_git_sources def find_source_requirements preload_git_sources + # Only safe to exclude when locked_requirements (merged below) backfills the gap. + nothing_changed = nothing_changed? + excluded = nothing_changed ? excluded_git_sources : [] + # 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(excluded_git_sources) + all_requirements = source_map.all_requirements(excluded) { default: default_source }.merge(all_requirements) else - { default: Source::RubygemsAggregate.new(sources, source_map, excluded_git_sources) }.merge(source_map.direct_requirements) + { default: Source::RubygemsAggregate.new(sources, source_map, excluded) }.merge(source_map.direct_requirements) end - source_requirements.merge!(source_map.locked_requirements) if nothing_changed? + source_requirements.merge!(source_map.locked_requirements) if nothing_changed metadata_dependencies.each do |dep| source_requirements[dep.name] = sources.metadata_source end diff --git a/bundler/lib/bundler/lockfile_parser.rb b/bundler/lib/bundler/lockfile_parser.rb index a837f994cd90..9e21f8f1d2a1 100644 --- a/bundler/lib/bundler/lockfile_parser.rb +++ b/bundler/lib/bundler/lockfile_parser.rb @@ -115,6 +115,17 @@ def initialize(lockfile, strict: false) "Run `git checkout HEAD -- #{@lockfile_path}` first to get a clean lock." end + @valid = lockfile.strip.empty? || + lockfile.split(/(?:\r?\n)+/).any? {|l| KNOWN_SECTIONS.include?(l) } + + unless @valid + SharedHelpers.feature_deprecated!( + "Your #{@lockfile_path} does not appear to be a valid lockfile. " \ + "Run `rm #{@lockfile_path}` and then `bundle install` to generate a new lockfile. " \ + "This will raise a LockfileError in a future version of Bundler." + ) + end + lockfile.split(/((?:\r?\n)+)/) do |line| # split alternates between the line and the following whitespace next @pos.advance!(line) if line.match?(/^\s*$/) @@ -164,6 +175,10 @@ def may_include_redundant_platform_specific_gems? bundler_version.nil? || bundler_version < Gem::Version.new("1.16.2") end + def valid? + @valid + end + private TYPES = { diff --git a/bundler/lib/bundler/source/path.rb b/bundler/lib/bundler/source/path.rb index 82e782ba257a..366a23aea725 100644 --- a/bundler/lib/bundler/source/path.rb +++ b/bundler/lib/bundler/source/path.rb @@ -220,10 +220,11 @@ def generate_bin(spec, options = {}) # Some gem authors put absolute paths in their gemspec # and we have to save them from themselves spec.files = spec.files.filter_map do |path| - next path unless /\A#{Pathname::SEPARATOR_PAT}/o.match?(path) + pathname = Pathname.new(path) + next path unless pathname.absolute? next if File.directory?(path) begin - Pathname.new(path).relative_path_from(gem_dir).to_s + pathname.relative_path_from(gem_dir).to_s rescue ArgumentError path end diff --git a/bundler/lib/bundler/version.rb b/bundler/lib/bundler/version.rb index e8b442674a61..405bd339ae48 100644 --- a/bundler/lib/bundler/version.rb +++ b/bundler/lib/bundler/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module Bundler - VERSION = "4.0.11".freeze + VERSION = "4.0.12".freeze def self.bundler_major_version @bundler_major_version ||= gem_version.segments.first diff --git a/bundler/spec/bundler/cli_spec.rb b/bundler/spec/bundler/cli_spec.rb index e2c64b939401..746330e7ef54 100644 --- a/bundler/spec/bundler/cli_spec.rb +++ b/bundler/spec/bundler/cli_spec.rb @@ -251,8 +251,10 @@ def out_with_macos_man_workaround context "running a parseable command" do it "prints no warning" do + bundle "config set foo value", env: { "BUNDLER_VERSION" => bundler_version } bundle "config get --parseable foo", env: { "BUNDLER_VERSION" => bundler_version } - expect(stdboth).to eq "" + expect(out).to eq "foo=value" + expect(err).to eq "" bundle "platform --ruby", env: { "BUNDLER_VERSION" => bundler_version }, raise_on_error: false expect(stdboth).to eq "Could not locate Gemfile" diff --git a/bundler/spec/bundler/compact_index_client/parser_spec.rb b/bundler/spec/bundler/compact_index_client/parser_spec.rb index 1f6b9e593bcd..6015f66f33a7 100644 --- a/bundler/spec/bundler/compact_index_client/parser_spec.rb +++ b/bundler/spec/bundler/compact_index_client/parser_spec.rb @@ -233,5 +233,17 @@ def set_info_data(name, value) VERSIONS expect(parser.info("a")).to eq(a_result) end + + it "handles lines without a checksum" do + compact_index.versions = <<~VERSIONS + created_at: 2024-05-01T00:00:04Z + --- + a 1.0.0,1.0.1,1.1.0 aaa111 + b 2.0.0,2.0.0-java + c 3.0.0,3.0.3,3.3.3 ccc333 + VERSIONS + + expect(parser.info("a")).to eq(a_result) + end end end diff --git a/bundler/spec/bundler/definition_spec.rb b/bundler/spec/bundler/definition_spec.rb index 9524c70193eb..8c7d5667ac66 100644 --- a/bundler/spec/bundler/definition_spec.rb +++ b/bundler/spec/bundler/definition_spec.rb @@ -289,6 +289,57 @@ end end + describe "#precompute_source_requirements_for_indirect_dependencies?" do + before do + allow(Bundler::SharedHelpers).to receive(:find_gemfile) { Pathname.new("Gemfile") } + end + + let(:sources) { Bundler::SourceList.new } + subject { Bundler::Definition.new(nil, [], sources, []) } + + before do + allow(sources).to receive(:non_global_rubygems_sources).and_return(non_global_rubygems_sources) + end + + context "when all the scoped sources implement a dependency API" do + let(:non_global_rubygems_sources) do + [ + double("non-global-source-0", "dependency_api_available?":true, to_s:"a"), + double("non-global-source-1", "dependency_api_available?":true, to_s:"b"), + ] + end + + it "returns true without warning" do + expect(subject).not_to receive(:non_dependency_api_warning) + + expect(subject.send(:precompute_source_requirements_for_indirect_dependencies?)).to be_truthy + end + end + + context "when some scoped sources do not implement a dependency API" do + let(:non_global_rubygems_sources) do + [ + double("non-global-source-0", "dependency_api_available?":true, to_s:"a"), + double("non-global-source-1", "dependency_api_available?":false, to_s:"b"), + double("non-global-source-2", "dependency_api_available?":false, to_s:"c"), + ] + end + + it "returns false and warns about the non-API sources" do + expect(Bundler.ui).to receive(:warn).with(<<-W.strip) +Your Gemfile contains scoped sources that don't implement a dependency API, namely: + + * b + * c + +Using the above gem servers may result in installing unexpected gems. To resolve this warning, make sure you use gem servers that implement dependency APIs, such as gemstash or geminabox gem servers. + W + + expect(subject.send(:precompute_source_requirements_for_indirect_dependencies?)).to be_falsy + end + end + end + def mock_source_list Class.new do def all_sources diff --git a/bundler/spec/bundler/lockfile_parser_spec.rb b/bundler/spec/bundler/lockfile_parser_spec.rb index f38da2c99321..4b493a37576b 100644 --- a/bundler/spec/bundler/lockfile_parser_spec.rb +++ b/bundler/spec/bundler/lockfile_parser_spec.rb @@ -129,6 +129,7 @@ shared_examples_for "parsing" do it "parses correctly" do + expect(subject.valid?).to be(true) expect(subject.sources).to eq sources expect(subject.dependencies).to eq dependencies expect(subject.specs).to eq specs @@ -191,6 +192,66 @@ include_examples "parsing" end + context "when the content does not contain any recognized lockfile sections" do + let(:lockfile_contents) { "hello world\nlorem ipsum\n" } + + it "does not raise, is not valid, and deprecates" do + expect(Bundler::SharedHelpers).to receive(:feature_deprecated!).with( + /does not appear to be a valid lockfile.*future version of Bundler/m + ) + parser = described_class.new(lockfile_contents) + expect(parser.valid?).to be(false) + expect(parser.specs).to eq([]) + expect(parser.dependencies).to eq({}) + end + + it "does not raise when strict: true, and still deprecates" do + expect(Bundler::SharedHelpers).to receive(:feature_deprecated!).with( + /does not appear to be a valid lockfile.*future version of Bundler/m + ) + parser = described_class.new(lockfile_contents, strict: true) + expect(parser.valid?).to be(false) + expect(parser.specs).to eq([]) + expect(parser.dependencies).to eq({}) + end + end + + context "when the content looks like a Gemfile DSL" do + let(:lockfile_contents) { <<~G } + source "https://rubygems.org" + gem "rake" + G + + it "does not raise, is not valid, and deprecates" do + expect(Bundler::SharedHelpers).to receive(:feature_deprecated!).with( + /does not appear to be a valid lockfile.*future version of Bundler/m + ) + parser = described_class.new(lockfile_contents) + expect(parser.valid?).to be(false) + expect(parser.specs).to eq([]) + expect(parser.dependencies).to eq({}) + end + + it "does not raise when strict: true, and still deprecates" do + expect(Bundler::SharedHelpers).to receive(:feature_deprecated!).with( + /does not appear to be a valid lockfile.*future version of Bundler/m + ) + parser = described_class.new(lockfile_contents, strict: true) + expect(parser.valid?).to be(false) + expect(parser.specs).to eq([]) + expect(parser.dependencies).to eq({}) + end + end + + context "when the content is empty" do + let(:lockfile_contents) { "" } + + it "does not raise and is valid" do + expect { subject }.not_to raise_error + expect(subject.valid?).to be(true) + end + end + context "when CHECKSUMS has duplicate checksums in the lockfile that don't match" do let(:bad_checksum) { "sha256=c0ffee11c0ffee11c0ffee11c0ffee11c0ffee11c0ffee11c0ffee11c0ffee11" } let(:lockfile_contents) { super().split(/(?<=CHECKSUMS\n)/m).insert(1, " rake (10.3.2) #{bad_checksum}\n").join } diff --git a/bundler/spec/commands/config_spec.rb b/bundler/spec/commands/config_spec.rb index 954cae09d846..e8ab32ca5da1 100644 --- a/bundler/spec/commands/config_spec.rb +++ b/bundler/spec/commands/config_spec.rb @@ -313,9 +313,10 @@ describe "parseable option" do it "prints an empty string" do - bundle "config get foo --parseable" + bundle "config get foo --parseable", raise_on_error: false expect(out).to eq "" + expect(last_command).to be_failure end it "only prints the value of the config" do @@ -501,8 +502,9 @@ it "get" do ENV["BUNDLE_BAR"] = "bar_val" - bundle "config get foo" + bundle "config get foo", raise_on_error: false expect(out).to eq "Settings for `foo` in order of priority. The top value will be used\nYou have not configured a value for `foo`" + expect(last_command).to be_failure ENV["BUNDLE_FOO"] = "foo_val" @@ -547,7 +549,8 @@ bundle "config unset foo" expect(out).to eq "" - expect(bundle("config get foo")).to eq "Settings for `foo` in order of priority. The top value will be used\nYou have not configured a value for `foo`" + expect(bundle("config get foo", raise_on_error: false)).to eq "Settings for `foo` in order of priority. The top value will be used\nYou have not configured a value for `foo`" + expect(last_command).to be_failure bundle "config set --local foo 1" bundle "config set --global foo 2" @@ -557,7 +560,8 @@ expect(bundle("config get foo")).to eq "Settings for `foo` in order of priority. The top value will be used\nSet for the current user (#{home(".bundle/config")}): \"2\"" bundle "config unset foo --global" expect(out).to eq "" - expect(bundle("config get foo")).to eq "Settings for `foo` in order of priority. The top value will be used\nYou have not configured a value for `foo`" + expect(bundle("config get foo", raise_on_error: false)).to eq "Settings for `foo` in order of priority. The top value will be used\nYou have not configured a value for `foo`" + expect(last_command).to be_failure bundle "config set --local foo 1" bundle "config set --global foo 2" @@ -567,7 +571,8 @@ expect(bundle("config get foo")).to eq "Settings for `foo` in order of priority. The top value will be used\nSet for your local app (#{bundled_app(".bundle/config")}): \"1\"" bundle "config unset foo --local" expect(out).to eq "" - expect(bundle("config get foo")).to eq "Settings for `foo` in order of priority. The top value will be used\nYou have not configured a value for `foo`" + expect(bundle("config get foo", raise_on_error: false)).to eq "Settings for `foo` in order of priority. The top value will be used\nYou have not configured a value for `foo`" + expect(last_command).to be_failure bundle "config unset foo --local --global", raise_on_error: false expect(last_command).to be_failure @@ -577,6 +582,36 @@ end RSpec.describe "setting gemfile via config" do + context "when a default Gemfile exists" do + before do + gemfile <<-G + source "https://gem.repo1" + G + + gemfile bundled_app("foo/bar_gemfile"), <<-G + source "https://gem.repo1" + G + end + + it "reports the local gemfile setting without promoting it to the environment" do + bundle "config set gemfile foo/bar_gemfile" + + bundle "config list" + expect(out).to include("Set for your local app (#{bundled_app(".bundle/config")}): \"foo/bar_gemfile\"") + expect(out).not_to include("Set via BUNDLE_GEMFILE") + end + + it "unsets the local gemfile setting from the app config" do + bundle "config set gemfile foo/bar_gemfile" + + bundle "config unset gemfile" + bundle "config get gemfile", raise_on_error: false + + expect(out).to include("You have not configured a value for `gemfile`") + expect(File.read(bundled_app(".bundle/config"))).not_to include("BUNDLE_GEMFILE") + end + end + context "when only the non-default Gemfile exists" do it "persists the gemfile location to .bundle/config" do gemfile bundled_app("NotGemfile"), <<-G diff --git a/bundler/spec/install/git_spec.rb b/bundler/spec/install/git_spec.rb index b711f6e61408..6c2af5f33e99 100644 --- a/bundler/spec/install/git_spec.rb +++ b/bundler/spec/install/git_spec.rb @@ -317,5 +317,53 @@ expect(the_bundle).to include_gems("production_gem 1.0") expect(the_bundle).not_to include_gems("development_gem 1.0") end + + it "resolves indirect dependencies from a git source not in the requested groups" do + build_lib "activesupport", "1.0", path: lib_path("rails/activesupport") + build_git "activerecord", "1.0", path: lib_path("rails") do |s| + s.add_dependency "activesupport", "= 1.0" + end + + gemfile <<-G + source "https://gem.repo1" + + gem "activerecord", :git => "#{lib_path("rails")}" + + group :ci do + gem "myrack" + end + G + + bundle "config set --local only ci" + bundle :install + + expect(the_bundle).to include_gems("myrack 1.0.0") + expect(the_bundle).not_to include_gems("activerecord 1.0") + end + + it "resolves indirect dependencies from a git source not in the requested groups (without compact_index dependency API)" do + build_lib "activesupport", "1.0", path: lib_path("rails/activesupport") + build_git "activerecord", "1.0", path: lib_path("rails") do |s| + s.add_dependency "activesupport", "= 1.0" + end + + gemfile <<-G + source "https://gem.repo1" + + gem "activerecord", :git => "#{lib_path("rails")}" + + group :ci do + gem "myrack" + end + G + + # Force the RubygemsAggregate code path in find_source_requirements by + # making the dependency API unavailable. + bundle "config set --local only ci" + bundle :install, artifice: "endpoint_api_forbidden" + + expect(the_bundle).to include_gems("myrack 1.0.0") + expect(the_bundle).not_to include_gems("activerecord 1.0") + end end end diff --git a/bundler/spec/other/major_deprecation_spec.rb b/bundler/spec/other/major_deprecation_spec.rb index 24d4153dfabe..b0228cf94378 100644 --- a/bundler/spec/other/major_deprecation_spec.rb +++ b/bundler/spec/other/major_deprecation_spec.rb @@ -290,7 +290,7 @@ describe "old get interface" do before do - bundle "config waka" + bundle "config waka", raise_on_error: false end it "warns", bundler: "4" do diff --git a/bundler/spec/realworld/fixtures/tapioca/Gemfile.lock b/bundler/spec/realworld/fixtures/tapioca/Gemfile.lock index bbaf8328d638..347fd5aaa470 100644 --- a/bundler/spec/realworld/fixtures/tapioca/Gemfile.lock +++ b/bundler/spec/realworld/fixtures/tapioca/Gemfile.lock @@ -46,4 +46,4 @@ DEPENDENCIES tapioca BUNDLED WITH - 4.0.11 + 4.0.12 diff --git a/bundler/spec/realworld/fixtures/warbler/Gemfile.lock b/bundler/spec/realworld/fixtures/warbler/Gemfile.lock index 3c6fd5218080..795dfdac6641 100644 --- a/bundler/spec/realworld/fixtures/warbler/Gemfile.lock +++ b/bundler/spec/realworld/fixtures/warbler/Gemfile.lock @@ -36,4 +36,4 @@ DEPENDENCIES warbler! BUNDLED WITH - 4.0.11 + 4.0.12 diff --git a/bundler/spec/runtime/setup_spec.rb b/bundler/spec/runtime/setup_spec.rb index 2cbb61dbff5d..63448151c2a0 100644 --- a/bundler/spec/runtime/setup_spec.rb +++ b/bundler/spec/runtime/setup_spec.rb @@ -303,6 +303,32 @@ def clean_load_path(lp) expect(out).to eq("WIN") end end + + context "user sets it via `config set --local gemfile`" do + it "uses the value in the config" do + gemfile <<-G + source "https://gem.repo1" + gem "myrack" + G + + gemfile bundled_app("CustomGemfile"), <<-G + source "https://gem.repo1" + gem "activesupport", "2.3.5" + G + + bundle "config set --local gemfile #{bundled_app("CustomGemfile")}" + bundle "install" + + ruby <<-R + require 'bundler' + Bundler.setup + require 'activesupport' + puts ACTIVESUPPORT + R + + expect(out).to eq("2.3.5") + end + end end it "prioritizes gems in BUNDLE_PATH over gems in GEM_HOME" do diff --git a/lib/rubygems.rb b/lib/rubygems.rb index d9da84ed3b1f..1adb74dded26 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -9,7 +9,7 @@ require "rbconfig" module Gem - VERSION = "4.0.11" + VERSION = "4.0.12" end require_relative "rubygems/defaults" diff --git a/lib/rubygems/bundler_version_finder.rb b/lib/rubygems/bundler_version_finder.rb index d792358da701..bbe7bf0ab55e 100644 --- a/lib/rubygems/bundler_version_finder.rb +++ b/lib/rubygems/bundler_version_finder.rb @@ -2,7 +2,8 @@ module Gem::BundlerVersionFinder def self.bundler_version - return if bundle_config_version == "system" + bcv = bundle_config_version + return if bcv == "system" v = ENV["BUNDLER_VERSION"] v = nil if v&.empty? @@ -10,7 +11,7 @@ def self.bundler_version v ||= bundle_update_bundler_version return if v == true - v ||= bundle_config_version + v ||= bcv unless bcv == "lockfile" v ||= lockfile_version return unless v @@ -70,6 +71,9 @@ def self.lockfile_contents private_class_method :lockfile_contents def self.bundle_config_version + env_version = ENV["BUNDLE_VERSION"] + return env_version if env_version && !env_version.empty? + version = nil [bundler_local_config_file, bundler_global_config_file].each do |config_file| diff --git a/lib/rubygems/win_platform.rb b/lib/rubygems/win_platform.rb index 78e968fe4930..10556871b2a6 100644 --- a/lib/rubygems/win_platform.rb +++ b/lib/rubygems/win_platform.rb @@ -8,7 +8,6 @@ module Gem WIN_PATTERNS = [ /bccwin/i, - /cygwin/i, /djgpp/i, /mingw/i, /mswin/i, diff --git a/test/rubygems/test_gem_bundler_version_finder.rb b/test/rubygems/test_gem_bundler_version_finder.rb index 88ee9c6759f2..b5ef6293abfd 100644 --- a/test/rubygems/test_gem_bundler_version_finder.rb +++ b/test/rubygems/test_gem_bundler_version_finder.rb @@ -104,6 +104,73 @@ def test_bundler_version_with_bundle_config_version end end + def test_bundler_version_with_bundle_version_env_system + ENV["BUNDLE_VERSION"] = "system" + + bvf.stub(:lockfile_contents, "\n\nBUNDLED WITH\n 1.1.1.1\n") do + assert_nil bvf.bundler_version + end + end + + def test_bundler_version_with_bundle_version_env_overrides_config + ENV["BUNDLE_VERSION"] = "2.3.4" + + config_content = <<~CONFIG + BUNDLE_VERSION: "1.2.3" + CONFIG + + Tempfile.create("bundle_config") do |f| + f.write(config_content) + f.flush + + bvf.stub(:bundler_global_config_file, f.path) do + assert_equal v("2.3.4"), bvf.bundler_version + end + end + end + + def test_bundler_version_with_empty_bundle_version_env + ENV["BUNDLE_VERSION"] = "" + + config_content = <<~CONFIG + BUNDLE_VERSION: "1.2.3" + CONFIG + + Tempfile.create("bundle_config") do |f| + f.write(config_content) + f.flush + + bvf.stub(:bundler_global_config_file, f.path) do + assert_equal v("1.2.3"), bvf.bundler_version + end + end + end + + def test_bundler_version_with_bundle_version_env_lockfile + ENV["BUNDLE_VERSION"] = "lockfile" + + bvf.stub(:lockfile_contents, "\n\nBUNDLED WITH\n 1.1.1.1\n") do + assert_equal v("1.1.1.1"), bvf.bundler_version + end + end + + def test_bundler_version_with_bundle_config_version_lockfile + config_content = <<~CONFIG + BUNDLE_VERSION: "lockfile" + CONFIG + + Tempfile.create("bundle_config") do |f| + f.write(config_content) + f.flush + + bvf.stub(:bundler_global_config_file, f.path) do + bvf.stub(:lockfile_contents, "\n\nBUNDLED WITH\n 1.1.1.1\n") do + assert_equal v("1.1.1.1"), bvf.bundler_version + end + end + end + end + def test_bundler_version_with_bundle_config_non_existent_file bvf.stub(:bundler_global_config_file, "/non/existent/path") do assert_nil bvf.bundler_version diff --git a/tool/bundler/dev_gems.rb.lock b/tool/bundler/dev_gems.rb.lock index b2d1783acb39..4b7ac8d612b9 100644 --- a/tool/bundler/dev_gems.rb.lock +++ b/tool/bundler/dev_gems.rb.lock @@ -129,4 +129,4 @@ CHECKSUMS turbo_tests (2.2.5) sha256=3fa31497d12976d11ccc298add29107b92bda94a90d8a0a5783f06f05102509f BUNDLED WITH - 4.0.11 + 4.0.12 diff --git a/tool/bundler/lint_gems.rb.lock b/tool/bundler/lint_gems.rb.lock index 80baf85f0370..b959288a2d65 100644 --- a/tool/bundler/lint_gems.rb.lock +++ b/tool/bundler/lint_gems.rb.lock @@ -119,4 +119,4 @@ CHECKSUMS wmi-lite (1.0.7) sha256=116ef5bb470dbe60f58c2db9047af3064c16245d6562c646bc0d90877e27ddda BUNDLED WITH - 4.0.11 + 4.0.12 diff --git a/tool/bundler/release_gems.rb.lock b/tool/bundler/release_gems.rb.lock index 657e6158b8df..a7edf0627313 100644 --- a/tool/bundler/release_gems.rb.lock +++ b/tool/bundler/release_gems.rb.lock @@ -87,4 +87,4 @@ CHECKSUMS uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6 BUNDLED WITH - 4.0.11 + 4.0.12 diff --git a/tool/bundler/rubocop_gems.rb.lock b/tool/bundler/rubocop_gems.rb.lock index 2893c91a2ca0..7c1610f2d94b 100644 --- a/tool/bundler/rubocop_gems.rb.lock +++ b/tool/bundler/rubocop_gems.rb.lock @@ -156,4 +156,4 @@ CHECKSUMS unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f BUNDLED WITH - 4.0.11 + 4.0.12 diff --git a/tool/bundler/standard_gems.rb.lock b/tool/bundler/standard_gems.rb.lock index 285bafab7442..24e33d943a0d 100644 --- a/tool/bundler/standard_gems.rb.lock +++ b/tool/bundler/standard_gems.rb.lock @@ -176,4 +176,4 @@ CHECKSUMS unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f BUNDLED WITH - 4.0.11 + 4.0.12 diff --git a/tool/bundler/test_gems.rb.lock b/tool/bundler/test_gems.rb.lock index 031216b465ab..ccb9db178541 100644 --- a/tool/bundler/test_gems.rb.lock +++ b/tool/bundler/test_gems.rb.lock @@ -103,4 +103,4 @@ CHECKSUMS tilt (2.6.1) sha256=35a99bba2adf7c1e362f5b48f9b581cce4edfba98117e34696dde6d308d84770 BUNDLED WITH - 4.0.11 + 4.0.12 diff --git a/tool/bundler/vendor_gems.rb.lock b/tool/bundler/vendor_gems.rb.lock index 2534e5c595c2..4619364802ee 100644 --- a/tool/bundler/vendor_gems.rb.lock +++ b/tool/bundler/vendor_gems.rb.lock @@ -72,4 +72,4 @@ CHECKSUMS uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6 BUNDLED WITH - 4.0.11 + 4.0.12