summaryrefslogtreecommitdiff
path: root/spec/bundler
diff options
context:
space:
mode:
Diffstat (limited to 'spec/bundler')
-rw-r--r--spec/bundler/bundler/build_metadata_spec.rb23
-rw-r--r--spec/bundler/bundler/bundler_spec.rb6
-rw-r--r--spec/bundler/bundler/cli_common_spec.rb22
-rw-r--r--spec/bundler/bundler/cli_spec.rb74
-rw-r--r--spec/bundler/bundler/compact_index_client/parser_spec.rb16
-rw-r--r--spec/bundler/bundler/compact_index_client/updater_spec.rb17
-rw-r--r--spec/bundler/bundler/current_ruby_spec.rb157
-rw-r--r--spec/bundler/bundler/definition_spec.rb83
-rw-r--r--spec/bundler/bundler/dependency_spec.rb141
-rw-r--r--spec/bundler/bundler/dsl_spec.rb223
-rw-r--r--spec/bundler/bundler/endpoint_specification_spec.rb40
-rw-r--r--spec/bundler/bundler/env_spec.rb19
-rw-r--r--spec/bundler/bundler/errors_spec.rb91
-rw-r--r--spec/bundler/bundler/fetcher/dependency_spec.rb14
-rw-r--r--spec/bundler/bundler/fetcher/downloader_spec.rb101
-rw-r--r--spec/bundler/bundler/fetcher/gem_remote_fetcher_spec.rb60
-rw-r--r--spec/bundler/bundler/friendly_errors_spec.rb27
-rw-r--r--spec/bundler/bundler/gem_helper_spec.rb11
-rw-r--r--spec/bundler/bundler/gem_version_promoter_spec.rb10
-rw-r--r--spec/bundler/bundler/installer/gem_installer_spec.rb8
-rw-r--r--spec/bundler/bundler/installer/parallel_installer_spec.rb79
-rw-r--r--spec/bundler/bundler/installer/spec_installation_spec.rb79
-rw-r--r--spec/bundler/bundler/lockfile_parser_spec.rb90
-rw-r--r--spec/bundler/bundler/override_spec.rb175
-rw-r--r--spec/bundler/bundler/plugin/events_spec.rb12
-rw-r--r--spec/bundler/bundler/plugin/index_spec.rb79
-rw-r--r--spec/bundler/bundler/plugin/installer_spec.rb7
-rw-r--r--spec/bundler/bundler/plugin_spec.rb24
-rw-r--r--spec/bundler/bundler/resolver/cooldown_spec.rb148
-rw-r--r--spec/bundler/bundler/retry_spec.rb111
-rw-r--r--spec/bundler/bundler/ruby_dsl_spec.rb37
-rw-r--r--spec/bundler/bundler/ruby_version_spec.rb22
-rw-r--r--spec/bundler/bundler/rubygems_ext_spec.rb39
-rw-r--r--spec/bundler/bundler/settings_spec.rb48
-rw-r--r--spec/bundler/bundler/shared_helpers_spec.rb38
-rw-r--r--spec/bundler/bundler/source/git/git_proxy_spec.rb180
-rw-r--r--spec/bundler/bundler/source/git_spec.rb50
-rw-r--r--spec/bundler/bundler/source/rubygems/remote_spec.rb43
-rw-r--r--spec/bundler/bundler/source/rubygems_spec.rb57
-rw-r--r--spec/bundler/bundler/source_list_spec.rb42
-rw-r--r--spec/bundler/bundler/source_spec.rb6
-rw-r--r--spec/bundler/bundler/spec_set_spec.rb89
-rw-r--r--spec/bundler/bundler/specifications/foo.gemspec2
-rw-r--r--spec/bundler/bundler/stub_specification_spec.rb8
-rw-r--r--spec/bundler/bundler/ui/shell_spec.rb28
-rw-r--r--spec/bundler/bundler/uri_normalizer_spec.rb25
-rw-r--r--spec/bundler/bundler/worker_spec.rb20
-rw-r--r--spec/bundler/bundler/yaml_serializer_spec.rb4
-rw-r--r--spec/bundler/cache/cache_path_spec.rb2
-rw-r--r--spec/bundler/cache/gems_spec.rb30
-rw-r--r--spec/bundler/cache/git_spec.rb97
-rw-r--r--spec/bundler/cache/path_spec.rb54
-rw-r--r--spec/bundler/commands/add_spec.rb149
-rw-r--r--spec/bundler/commands/binstubs_spec.rb248
-rw-r--r--spec/bundler/commands/cache_spec.rb241
-rw-r--r--spec/bundler/commands/check_spec.rb86
-rw-r--r--spec/bundler/commands/clean_spec.rb202
-rw-r--r--spec/bundler/commands/config_spec.rb62
-rw-r--r--spec/bundler/commands/console_spec.rb215
-rw-r--r--spec/bundler/commands/doctor_spec.rb72
-rw-r--r--spec/bundler/commands/exec_spec.rb208
-rw-r--r--spec/bundler/commands/fund_spec.rb2
-rw-r--r--spec/bundler/commands/info_spec.rb24
-rw-r--r--spec/bundler/commands/init_spec.rb2
-rw-r--r--spec/bundler/commands/inject_spec.rb117
-rw-r--r--spec/bundler/commands/install_spec.rb652
-rw-r--r--spec/bundler/commands/licenses_spec.rb2
-rw-r--r--spec/bundler/commands/list_spec.rb120
-rw-r--r--spec/bundler/commands/lock_spec.rb808
-rw-r--r--spec/bundler/commands/newgem_spec.rb1888
-rw-r--r--spec/bundler/commands/open_spec.rb8
-rw-r--r--spec/bundler/commands/outdated_spec.rb144
-rw-r--r--spec/bundler/commands/platform_spec.rb181
-rw-r--r--spec/bundler/commands/post_bundle_message_spec.rb214
-rw-r--r--spec/bundler/commands/pristine_spec.rb74
-rw-r--r--spec/bundler/commands/remove_spec.rb15
-rw-r--r--spec/bundler/commands/show_spec.rb61
-rw-r--r--spec/bundler/commands/ssl_spec.rb373
-rw-r--r--spec/bundler/commands/update_spec.rb366
-rw-r--r--spec/bundler/commands/version_spec.rb50
-rw-r--r--spec/bundler/commands/viz_spec.rb144
-rw-r--r--spec/bundler/install/allow_offline_install_spec.rb10
-rw-r--r--spec/bundler/install/binstubs_spec.rb2
-rw-r--r--spec/bundler/install/bundler_spec.rb10
-rw-r--r--spec/bundler/install/cooldown_spec.rb433
-rw-r--r--spec/bundler/install/deploy_spec.rb141
-rw-r--r--spec/bundler/install/failure_spec.rb34
-rw-r--r--spec/bundler/install/force_spec.rb (renamed from spec/bundler/install/redownload_spec.rb)28
-rw-r--r--spec/bundler/install/gemfile/eval_gemfile_spec.rb2
-rw-r--r--spec/bundler/install/gemfile/force_ruby_platform_spec.rb2
-rw-r--r--spec/bundler/install/gemfile/gemspec_spec.rb123
-rw-r--r--spec/bundler/install/gemfile/git_spec.rb131
-rw-r--r--spec/bundler/install/gemfile/groups_spec.rb112
-rw-r--r--spec/bundler/install/gemfile/install_if_spec.rb2
-rw-r--r--spec/bundler/install/gemfile/lockfile_spec.rb29
-rw-r--r--spec/bundler/install/gemfile/override_spec.rb401
-rw-r--r--spec/bundler/install/gemfile/path_spec.rb113
-rw-r--r--spec/bundler/install/gemfile/platform_spec.rb36
-rw-r--r--spec/bundler/install/gemfile/ruby_spec.rb4
-rw-r--r--spec/bundler/install/gemfile/sources_spec.rb1159
-rw-r--r--spec/bundler/install/gemfile/specific_platform_spec.rb319
-rw-r--r--spec/bundler/install/gemfile_spec.rb69
-rw-r--r--spec/bundler/install/gems/compact_index_spec.rb177
-rw-r--r--spec/bundler/install/gems/dependency_api_fallback_spec.rb36
-rw-r--r--spec/bundler/install/gems/dependency_api_spec.rb155
-rw-r--r--spec/bundler/install/gems/flex_spec.rb32
-rw-r--r--spec/bundler/install/gems/fund_spec.rb2
-rw-r--r--spec/bundler/install/gems/gemfile_source_header_spec.rb24
-rw-r--r--spec/bundler/install/gems/mirror_probe_spec.rb68
-rw-r--r--spec/bundler/install/gems/mirror_spec.rb4
-rw-r--r--spec/bundler/install/gems/native_extensions_spec.rb18
-rw-r--r--spec/bundler/install/gems/no_build_extension_spec.rb54
-rw-r--r--spec/bundler/install/gems/no_install_plugin_spec.rb53
-rw-r--r--spec/bundler/install/gems/post_install_spec.rb4
-rw-r--r--spec/bundler/install/gems/resolving_spec.rb44
-rw-r--r--spec/bundler/install/gems/standalone_spec.rb145
-rw-r--r--spec/bundler/install/gemspecs_spec.rb6
-rw-r--r--spec/bundler/install/git_spec.rb165
-rw-r--r--spec/bundler/install/global_cache_spec.rb73
-rw-r--r--spec/bundler/install/path_spec.rb82
-rw-r--r--spec/bundler/install/prereleases_spec.rb2
-rw-r--r--spec/bundler/install/process_lock_spec.rb81
-rw-r--r--spec/bundler/install/yanked_spec.rb16
-rw-r--r--spec/bundler/lock/git_spec.rb45
-rw-r--r--spec/bundler/lock/lockfile_spec.rb467
-rw-r--r--spec/bundler/other/cli_dispatch_spec.rb6
-rw-r--r--spec/bundler/other/cli_man_pages_spec.rb95
-rw-r--r--spec/bundler/other/ext_spec.rb43
-rw-r--r--spec/bundler/other/major_deprecation_spec.rb518
-rw-r--r--spec/bundler/plugins/command_spec.rb34
-rw-r--r--spec/bundler/plugins/hook_spec.rb123
-rw-r--r--spec/bundler/plugins/install_spec.rb48
-rw-r--r--spec/bundler/plugins/source/example_spec.rb28
-rw-r--r--spec/bundler/quality_es_spec.rb4
-rw-r--r--spec/bundler/quality_spec.rb20
-rw-r--r--spec/bundler/realworld/edgecases_spec.rb137
-rw-r--r--spec/bundler/realworld/ffi_spec.rb4
-rw-r--r--spec/bundler/realworld/fixtures/tapioca/Gemfile.lock6
-rw-r--r--spec/bundler/realworld/fixtures/warbler/Gemfile4
-rw-r--r--spec/bundler/realworld/fixtures/warbler/Gemfile.lock31
-rw-r--r--spec/bundler/realworld/gemfile_source_header_spec.rb54
-rw-r--r--spec/bundler/realworld/mirror_probe_spec.rb132
-rw-r--r--spec/bundler/realworld/slow_perf_spec.rb111
-rw-r--r--spec/bundler/resolver/basic_spec.rb62
-rw-r--r--spec/bundler/resolver/platform_spec.rb44
-rw-r--r--spec/bundler/runtime/env_helpers_spec.rb103
-rw-r--r--spec/bundler/runtime/executable_spec.rb46
-rw-r--r--spec/bundler/runtime/gem_tasks_spec.rb15
-rw-r--r--spec/bundler/runtime/inline_spec.rb87
-rw-r--r--spec/bundler/runtime/load_spec.rb2
-rw-r--r--spec/bundler/runtime/platform_spec.rb42
-rw-r--r--spec/bundler/runtime/require_spec.rb53
-rw-r--r--spec/bundler/runtime/requiring_spec.rb8
-rw-r--r--spec/bundler/runtime/self_management_spec.rb110
-rw-r--r--spec/bundler/runtime/setup_spec.rb157
-rw-r--r--spec/bundler/spec_helper.rb88
-rw-r--r--spec/bundler/support/artifice/compact_index_cooldown.rb6
-rw-r--r--spec/bundler/support/artifice/compact_index_etag_match.rb2
-rw-r--r--spec/bundler/support/artifice/compact_index_mirror_down.rb21
-rw-r--r--spec/bundler/support/artifice/compact_index_no_checksums.rb16
-rw-r--r--spec/bundler/support/artifice/helpers/compact_index.rb10
-rw-r--r--spec/bundler/support/artifice/helpers/compact_index_cooldown.rb13
-rw-r--r--spec/bundler/support/artifice/helpers/endpoint.rb2
-rw-r--r--spec/bundler/support/artifice/vcr.rb2
-rw-r--r--spec/bundler/support/build_metadata.rb12
-rw-r--r--spec/bundler/support/builders.rb225
-rwxr-xr-xspec/bundler/support/bundle6
-rw-r--r--spec/bundler/support/bundle.rb6
-rw-r--r--spec/bundler/support/checksums.rb21
-rw-r--r--spec/bundler/support/command_execution.rb5
-rw-r--r--spec/bundler/support/filters.rb19
-rw-r--r--spec/bundler/support/hax.rb51
-rw-r--r--spec/bundler/support/helpers.rb293
-rw-r--r--spec/bundler/support/indexes.rb3
-rw-r--r--spec/bundler/support/matchers.rb2
-rw-r--r--spec/bundler/support/path.rb139
-rw-r--r--spec/bundler/support/platforms.rb70
-rw-r--r--spec/bundler/support/rubygems_ext.rb155
-rw-r--r--spec/bundler/support/setup.rb9
-rw-r--r--spec/bundler/support/shards.rb200
-rw-r--r--spec/bundler/support/silent_logger.rb10
-rw-r--r--spec/bundler/support/subprocess.rb11
-rw-r--r--spec/bundler/support/switch_rubygems.rb1
-rw-r--r--spec/bundler/support/the_bundle.rb16
-rw-r--r--spec/bundler/update/force_spec.rb30
-rw-r--r--spec/bundler/update/gemfile_spec.rb2
-rw-r--r--spec/bundler/update/git_spec.rb6
-rw-r--r--spec/bundler/update/redownload_spec.rb44
188 files changed, 11425 insertions, 6707 deletions
diff --git a/spec/bundler/bundler/build_metadata_spec.rb b/spec/bundler/bundler/build_metadata_spec.rb
index afa2d1716f..2e69821f68 100644
--- a/spec/bundler/bundler/build_metadata_spec.rb
+++ b/spec/bundler/bundler/build_metadata_spec.rb
@@ -6,18 +6,20 @@ require "bundler/build_metadata"
RSpec.describe Bundler::BuildMetadata do
before do
allow(Time).to receive(:now).and_return(Time.at(0))
- Bundler::BuildMetadata.instance_variable_set(:@built_at, nil)
+ Bundler::BuildMetadata.instance_variable_set(:@timestamp, nil)
end
- describe "#built_at" do
- it "returns %Y-%m-%d formatted time" do
- expect(Bundler::BuildMetadata.built_at).to eq "1970-01-01"
+ describe "#timestamp" do
+ it "returns %Y-%m-%d formatted current time if built_at not set" do
+ Bundler::BuildMetadata.instance_variable_set(:@built_at, nil)
+ expect(Bundler::BuildMetadata.timestamp).to eq "1970-01-01"
end
- end
- describe "#release?" do
- it "returns false as default" do
- expect(Bundler::BuildMetadata.release?).to be_falsey
+ it "returns %Y-%m-%d formatted current time if built_at not set" do
+ Bundler::BuildMetadata.instance_variable_set(:@built_at, "2025-01-01")
+ expect(Bundler::BuildMetadata.timestamp).to eq "2025-01-01"
+ ensure
+ Bundler::BuildMetadata.instance_variable_set(:@built_at, nil)
end
end
@@ -40,10 +42,9 @@ RSpec.describe Bundler::BuildMetadata do
describe "#to_h" do
subject { Bundler::BuildMetadata.to_h }
- it "returns a hash includes Built At, Git SHA and Released Version" do
- expect(subject["Built At"]).to eq "1970-01-01"
+ it "returns a hash includes Timestamp, and Git SHA" do
+ expect(subject["Timestamp"]).to eq "1970-01-01"
expect(subject["Git SHA"]).to be_instance_of(String)
- expect(subject["Released Version"]).to be_falsey
end
end
end
diff --git a/spec/bundler/bundler/bundler_spec.rb b/spec/bundler/bundler/bundler_spec.rb
index 4db8c00e52..bddcbdaef3 100644
--- a/spec/bundler/bundler/bundler_spec.rb
+++ b/spec/bundler/bundler/bundler_spec.rb
@@ -52,10 +52,10 @@ RSpec.describe Bundler do
s.description = "Bundler manages an application's dependencies through its entire life, across many machines, systematically and repeatably"
s.email = ["team@bundler.io"]
s.homepage = "https://bundler.io"
- s.metadata = { "bug_tracker_uri" => "https://github.com/rubygems/rubygems/issues?q=is%3Aopen+is%3Aissue+label%3ABundler",
- "changelog_uri" => "https://github.com/rubygems/rubygems/blob/master/bundler/CHANGELOG.md",
+ s.metadata = { "bug_tracker_uri" => "https://github.com/ruby/rubygems/issues?q=is%3Aopen+is%3Aissue+label%3ABundler",
+ "changelog_uri" => "https://github.com/ruby/rubygems/blob/master/bundler/CHANGELOG.md",
"homepage_uri" => "https://bundler.io/",
- "source_code_uri" => "https://github.com/rubygems/rubygems/tree/master/bundler" }
+ "source_code_uri" => "https://github.com/ruby/rubygems/tree/master/bundler" }
s.require_paths = ["lib"]
s.required_ruby_version = Gem::Requirement.new([">= 2.6.0"])
s.required_rubygems_version = Gem::Requirement.new([">= 3.0.1"])
diff --git a/spec/bundler/bundler/cli_common_spec.rb b/spec/bundler/bundler/cli_common_spec.rb
new file mode 100644
index 0000000000..015894b3a1
--- /dev/null
+++ b/spec/bundler/bundler/cli_common_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require "bundler/cli"
+
+RSpec.describe Bundler::CLI::Common do
+ describe "gem_not_found_message" do
+ it "should suggest alternate gem names" do
+ message = subject.gem_not_found_message("ralis", ["BOGUS"])
+ expect(message).to match("Could not find gem 'ralis'.$")
+ message = subject.gem_not_found_message("ralis", ["rails"])
+ expect(message).to match("Did you mean 'rails'?")
+ message = subject.gem_not_found_message("Rails", ["rails"])
+ expect(message).to match("Did you mean 'rails'?")
+ message = subject.gem_not_found_message("meail", %w[email fail eval])
+ expect(message).to match("Did you mean 'email'?")
+ message = subject.gem_not_found_message("nokogri", %w[nokogiri rails sidekiq dog])
+ expect(message).to match("Did you mean 'nokogiri'?")
+ message = subject.gem_not_found_message("methosd", %w[method methods bogus])
+ expect(message).to match(/Did you mean 'method(|s)' or 'method(|s)'?/)
+ end
+ end
+end
diff --git a/spec/bundler/bundler/cli_spec.rb b/spec/bundler/bundler/cli_spec.rb
index b2cc1ccfef..56caf9937e 100644
--- a/spec/bundler/bundler/cli_spec.rb
+++ b/spec/bundler/bundler/cli_spec.rb
@@ -87,15 +87,25 @@ RSpec.describe "bundle executable" do
end
context "with no arguments" do
- it "prints a concise help message", bundler: "3" do
- bundle ""
- expect(err).to be_empty
+ it "tries to installs by default but print help on missing Gemfile" do
+ bundle "", raise_on_error: false
+ expect(err).to include("Could not locate Gemfile")
+ expect(out).to include("In a future version of Bundler")
+
expect(out).to include("Bundler version #{Bundler::VERSION}").
and include("\n\nBundler commands:\n\n").
and include("\n\n Primary commands:\n").
and include("\n\n Utilities:\n").
and include("\n\nOptions:\n")
end
+
+ it "runs bundle install when default_cli_command set to install" do
+ bundle_config "default_cli_command install"
+ bundle "", raise_on_error: false
+ expect(out).to_not include("In a future version of Bundler")
+ expect(err).to include("Could not locate Gemfile")
+ expect(exitstatus).to_not be_zero
+ end
end
context "when ENV['BUNDLE_GEMFILE'] is set to an empty string" do
@@ -112,20 +122,34 @@ RSpec.describe "bundle executable" do
end
context "with --verbose" do
- it "prints the running command" do
+ before do
gemfile "source 'https://gem.repo1'"
+ end
+
+ it "prints the running command" do
bundle "info bundler", verbose: true
expect(out).to start_with("Running `bundle info bundler --verbose` with bundler #{Bundler::VERSION}")
- end
- it "doesn't print defaults" do
- install_gemfile "source 'https://gem.repo1'", verbose: true
+ bundle "install", verbose: true
expect(out).to start_with("Running `bundle install --verbose` with bundler #{Bundler::VERSION}")
end
- it "doesn't print defaults" do
- install_gemfile "source 'https://gem.repo1'", verbose: true
- expect(out).to start_with("Running `bundle install --verbose` with bundler #{Bundler::VERSION}")
+ it "prints the simulated version too when setting is enabled" do
+ bundle "config set simulate_version 4", verbose: true
+ bundle "info bundler", verbose: true
+ expect(out).to start_with("Running `bundle info bundler --verbose` with bundler #{Bundler::VERSION} (simulating Bundler 4)")
+ end
+ end
+
+ context "with verbose configuration" do
+ before do
+ bundle_config "verbose true"
+ end
+
+ it "prints the running command" do
+ gemfile "source 'https://gem.repo1'"
+ bundle "info bundler"
+ expect(out).to start_with("Running `bundle info bundler` with bundler #{Bundler::VERSION}")
end
end
@@ -179,14 +203,14 @@ RSpec.describe "bundle executable" do
shared_examples_for "no warning" do
it "prints no warning" do
bundle "fail", env: { "BUNDLER_VERSION" => bundler_version }, raise_on_error: false
- expect(last_command.stdboth).to eq("Could not find command \"fail\".")
+ expect(stdboth).to eq("Could not find command \"fail\".")
end
end
let(:bundler_version) { "2.0" }
let(:latest_version) { nil }
before do
- bundle "config set --global disable_version_check false"
+ bundle_config_global "disable_version_check false"
pristine_system_gems "bundler-#{bundler_version}"
if latest_version
@@ -227,11 +251,13 @@ To update to the most recent version, run `bundle update --bundler`
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(last_command.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(last_command.stdboth).to eq "Could not locate Gemfile"
+ expect(stdboth).to eq "Could not locate Gemfile"
end
end
@@ -250,13 +276,23 @@ To update to the most recent version, run `bundle update --bundler`
end
RSpec.describe "bundler executable" do
- it "shows the bundler version just as the `bundle` executable does", bundler: "< 3" do
+ it "shows the bundler version just as the `bundle` executable does" do
bundler "--version"
- expect(out).to eq("Bundler version #{Bundler::VERSION}")
- end
+ expect(out).to eq(Bundler::VERSION.to_s)
- it "shows the bundler version just as the `bundle` executable does", bundler: "3" do
+ bundle_config "simulate_version 5"
bundler "--version"
- expect(out).to eq(Bundler::VERSION)
+ expect(out).to eq("#{Bundler::VERSION} (simulating Bundler 5)")
+ end
+
+ it "shows cli_help when bundler install and no Gemfile is found" do
+ bundler "install", raise_on_error: false
+ expect(err).to include("Could not locate Gemfile")
+
+ expect(out).to include("Bundler version #{Bundler::VERSION}").
+ and include("\n\nBundler commands:\n\n").
+ and include("\n\n Primary commands:\n").
+ and include("\n\n Utilities:\n").
+ and include("\n\nOptions:\n")
end
end
diff --git a/spec/bundler/bundler/compact_index_client/parser_spec.rb b/spec/bundler/bundler/compact_index_client/parser_spec.rb
index 1f6b9e593b..6aa867f058 100644
--- a/spec/bundler/bundler/compact_index_client/parser_spec.rb
+++ b/spec/bundler/bundler/compact_index_client/parser_spec.rb
@@ -47,7 +47,7 @@ RSpec.describe Bundler::CompactIndexClient::Parser do
INFO
let(:c_info) { <<~INFO }
3.0.0 a:= 1.0.0,b:~> 2.0|checksum:ccc1,ruby:>= 2.7.0,rubygems:>= 3.0.0
- 3.3.3 a:>= 1.1.0,b:~> 2.0|checksum:ccc3,ruby:>= 3.0.0,rubygems:>= 3.2.3
+ 3.3.3 a:>= 1.1.0,b:~> 2.0|checksum:ccc3,ruby:>= 3.0.0,rubygems:>= 3.2.3,created_at:2026-05-12T10:00:00Z
INFO
describe "#available?" do
@@ -195,7 +195,7 @@ RSpec.describe Bundler::CompactIndexClient::Parser do
"3.3.3",
nil,
[["a", [">= 1.1.0"]], ["b", ["~> 2.0"]]],
- [["checksum", ["ccc3"]], ["ruby", [">= 3.0.0"]], ["rubygems", [">= 3.2.3"]]],
+ [["checksum", ["ccc3"]], ["ruby", [">= 3.0.0"]], ["rubygems", [">= 3.2.3"]], ["created_at", ["2026-05-12T10:00:00Z"]]],
],
]
end
@@ -233,5 +233,17 @@ RSpec.describe Bundler::CompactIndexClient::Parser do
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/spec/bundler/bundler/compact_index_client/updater_spec.rb b/spec/bundler/bundler/compact_index_client/updater_spec.rb
index 87a73d993f..fd63a652a4 100644
--- a/spec/bundler/bundler/compact_index_client/updater_spec.rb
+++ b/spec/bundler/bundler/compact_index_client/updater_spec.rb
@@ -115,6 +115,23 @@ RSpec.describe Bundler::CompactIndexClient::Updater do
expect(local_path.read).to eq(full_body)
expect(etag_path.read).to eq("NewEtag")
end
+
+ it "tries the request again if the partial response is blank" do
+ allow(response).to receive(:[]).with("Repr-Digest") { "sha-256=:baddigest:" }
+ allow(response).to receive(:body) { "" }
+ allow(response).to receive(:is_a?).with(Gem::Net::HTTPPartialContent) { true }
+ expect(fetcher).to receive(:call).once.with(remote_path, headers).and_return(response)
+
+ full_response = double(:full_response, body: full_body, is_a?: false)
+ allow(full_response).to receive(:[]).with("Repr-Digest") { "sha-256=:#{digest}:" }
+ allow(full_response).to receive(:[]).with("ETag") { '"NewEtag"' }
+ expect(fetcher).to receive(:call).once.with(remote_path, { "If-None-Match" => '"LocalEtag"' }).and_return(full_response)
+
+ updater.update(remote_path, local_path, etag_path)
+
+ expect(local_path.read).to eq(full_body)
+ expect(etag_path.read).to eq("NewEtag")
+ end
end
context "without an etag file" do
diff --git a/spec/bundler/bundler/current_ruby_spec.rb b/spec/bundler/bundler/current_ruby_spec.rb
new file mode 100644
index 0000000000..79eb802aa5
--- /dev/null
+++ b/spec/bundler/bundler/current_ruby_spec.rb
@@ -0,0 +1,157 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::CurrentRuby do
+ describe "PLATFORM_MAP" do
+ subject { described_class::PLATFORM_MAP }
+
+ # rubocop:disable Naming/VariableNumber
+ let(:platforms) do
+ { ruby: Gem::Platform::RUBY,
+ ruby_18: Gem::Platform::RUBY,
+ ruby_19: Gem::Platform::RUBY,
+ ruby_20: Gem::Platform::RUBY,
+ ruby_21: Gem::Platform::RUBY,
+ ruby_22: Gem::Platform::RUBY,
+ ruby_23: Gem::Platform::RUBY,
+ ruby_24: Gem::Platform::RUBY,
+ ruby_25: Gem::Platform::RUBY,
+ ruby_26: Gem::Platform::RUBY,
+ ruby_27: Gem::Platform::RUBY,
+ ruby_30: Gem::Platform::RUBY,
+ ruby_31: Gem::Platform::RUBY,
+ ruby_32: Gem::Platform::RUBY,
+ ruby_33: Gem::Platform::RUBY,
+ ruby_34: Gem::Platform::RUBY,
+ ruby_40: Gem::Platform::RUBY,
+ ruby_41: Gem::Platform::RUBY,
+ mri: Gem::Platform::RUBY,
+ mri_18: Gem::Platform::RUBY,
+ mri_19: Gem::Platform::RUBY,
+ mri_20: Gem::Platform::RUBY,
+ mri_21: Gem::Platform::RUBY,
+ mri_22: Gem::Platform::RUBY,
+ mri_23: Gem::Platform::RUBY,
+ mri_24: Gem::Platform::RUBY,
+ mri_25: Gem::Platform::RUBY,
+ mri_26: Gem::Platform::RUBY,
+ mri_27: Gem::Platform::RUBY,
+ mri_30: Gem::Platform::RUBY,
+ mri_31: Gem::Platform::RUBY,
+ mri_32: Gem::Platform::RUBY,
+ mri_33: Gem::Platform::RUBY,
+ mri_34: Gem::Platform::RUBY,
+ mri_40: Gem::Platform::RUBY,
+ mri_41: Gem::Platform::RUBY,
+ rbx: Gem::Platform::RUBY,
+ truffleruby: Gem::Platform::RUBY,
+ jruby: Gem::Platform::JAVA,
+ jruby_18: Gem::Platform::JAVA,
+ jruby_19: Gem::Platform::JAVA,
+ windows: Gem::Platform::WINDOWS,
+ windows_18: Gem::Platform::WINDOWS,
+ windows_19: Gem::Platform::WINDOWS,
+ windows_20: Gem::Platform::WINDOWS,
+ windows_21: Gem::Platform::WINDOWS,
+ windows_22: Gem::Platform::WINDOWS,
+ windows_23: Gem::Platform::WINDOWS,
+ windows_24: Gem::Platform::WINDOWS,
+ windows_25: Gem::Platform::WINDOWS,
+ windows_26: Gem::Platform::WINDOWS,
+ windows_27: Gem::Platform::WINDOWS,
+ windows_30: Gem::Platform::WINDOWS,
+ windows_31: Gem::Platform::WINDOWS,
+ windows_32: Gem::Platform::WINDOWS,
+ windows_33: Gem::Platform::WINDOWS,
+ windows_34: Gem::Platform::WINDOWS,
+ windows_40: Gem::Platform::WINDOWS,
+ windows_41: Gem::Platform::WINDOWS }
+ end
+
+ let(:deprecated) do
+ { mswin: Gem::Platform::MSWIN,
+ mswin_18: Gem::Platform::MSWIN,
+ mswin_19: Gem::Platform::MSWIN,
+ mswin_20: Gem::Platform::MSWIN,
+ mswin_21: Gem::Platform::MSWIN,
+ mswin_22: Gem::Platform::MSWIN,
+ mswin_23: Gem::Platform::MSWIN,
+ mswin_24: Gem::Platform::MSWIN,
+ mswin_25: Gem::Platform::MSWIN,
+ mswin_26: Gem::Platform::MSWIN,
+ mswin_27: Gem::Platform::MSWIN,
+ mswin_30: Gem::Platform::MSWIN,
+ mswin_31: Gem::Platform::MSWIN,
+ mswin_32: Gem::Platform::MSWIN,
+ mswin_33: Gem::Platform::MSWIN,
+ mswin_34: Gem::Platform::MSWIN,
+ mswin_40: Gem::Platform::MSWIN,
+ mswin_41: Gem::Platform::MSWIN,
+ mswin64: Gem::Platform::MSWIN64,
+ mswin64_19: Gem::Platform::MSWIN64,
+ mswin64_20: Gem::Platform::MSWIN64,
+ mswin64_21: Gem::Platform::MSWIN64,
+ mswin64_22: Gem::Platform::MSWIN64,
+ mswin64_23: Gem::Platform::MSWIN64,
+ mswin64_24: Gem::Platform::MSWIN64,
+ mswin64_25: Gem::Platform::MSWIN64,
+ mswin64_26: Gem::Platform::MSWIN64,
+ mswin64_27: Gem::Platform::MSWIN64,
+ mswin64_30: Gem::Platform::MSWIN64,
+ mswin64_31: Gem::Platform::MSWIN64,
+ mswin64_32: Gem::Platform::MSWIN64,
+ mswin64_33: Gem::Platform::MSWIN64,
+ mswin64_34: Gem::Platform::MSWIN64,
+ mswin64_40: Gem::Platform::MSWIN64,
+ mswin64_41: Gem::Platform::MSWIN64,
+ mingw: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_18: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_19: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_20: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_21: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_22: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_23: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_24: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_25: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_26: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_27: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_30: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_31: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_32: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_33: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_34: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_40: Gem::Platform::UNIVERSAL_MINGW,
+ mingw_41: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_20: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_21: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_22: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_23: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_24: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_25: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_26: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_27: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_30: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_31: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_32: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_33: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_34: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_40: Gem::Platform::UNIVERSAL_MINGW,
+ x64_mingw_41: Gem::Platform::UNIVERSAL_MINGW }
+ end
+ # rubocop:enable Naming/VariableNumber
+
+ it "includes all platforms" do
+ expect(subject).to eq(platforms.merge(deprecated))
+ end
+ end
+
+ describe "Deprecated platform" do
+ it "outputs an error and aborts when calling maglev?" do
+ expect { Bundler.current_ruby.maglev? }.to raise_error(Bundler::RemovedError, /`CurrentRuby#maglev\?` was removed with no replacement./)
+ end
+
+ it "outputs an error and aborts when calling maglev_31?" do
+ expect { Bundler.current_ruby.maglev_31? }.to raise_error(Bundler::RemovedError, /`CurrentRuby#maglev_31\?` was removed with no replacement./)
+ end
+ end
+end
diff --git a/spec/bundler/bundler/definition_spec.rb b/spec/bundler/bundler/definition_spec.rb
index 68b170513c..8c4a5a0331 100644
--- a/spec/bundler/bundler/definition_spec.rb
+++ b/spec/bundler/bundler/definition_spec.rb
@@ -3,6 +3,24 @@
require "bundler/definition"
RSpec.describe Bundler::Definition do
+ describe "#overrides" do
+ before do
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile) { bundled_app_gemfile }
+ end
+
+ subject { Bundler::Definition.new(bundled_app_lock, [], Bundler::SourceList.new, {}) }
+
+ it "defaults to an empty array" do
+ expect(subject.overrides).to eq([])
+ end
+
+ it "is writable" do
+ override = Bundler::Override.new("rails", :version, ">= 8.0")
+ subject.overrides = [override]
+ expect(subject.overrides).to eq([override])
+ end
+ end
+
describe "#lock" do
before do
allow(Bundler::SharedHelpers).to receive(:find_gemfile) { bundled_app_gemfile }
@@ -33,7 +51,7 @@ RSpec.describe Bundler::Definition do
before { Bundler::Definition.no_lock = true }
after { Bundler::Definition.no_lock = false }
- it "does not create a lock file" do
+ it "does not create a lockfile" do
subject.lock
expect(bundled_app_lock).not_to be_file
end
@@ -80,7 +98,7 @@ RSpec.describe Bundler::Definition do
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -137,7 +155,7 @@ RSpec.describe Bundler::Definition do
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
expect(lockfile).to eq(expected_lockfile)
@@ -175,7 +193,7 @@ RSpec.describe Bundler::Definition do
only_java
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -205,7 +223,7 @@ RSpec.describe Bundler::Definition do
foo
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
end
@@ -289,6 +307,57 @@ RSpec.describe Bundler::Definition do
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
@@ -299,10 +368,6 @@ RSpec.describe Bundler::Definition do
[]
end
- def rubygems_remotes
- []
- end
-
def replace_sources!(arg)
nil
end
diff --git a/spec/bundler/bundler/dependency_spec.rb b/spec/bundler/bundler/dependency_spec.rb
index 1185c4e5dd..f930459571 100644
--- a/spec/bundler/bundler/dependency_spec.rb
+++ b/spec/bundler/bundler/dependency_spec.rb
@@ -35,140 +35,15 @@ RSpec.describe Bundler::Dependency do
end
end
- describe "PLATFORM_MAP" do
- subject { described_class::PLATFORM_MAP }
+ it "is on the current platform" do
+ engine = Gem.win_platform? ? "windows" : RUBY_ENGINE
- # rubocop:disable Naming/VariableNumber
- let(:platforms) do
- { ruby: Gem::Platform::RUBY,
- ruby_18: Gem::Platform::RUBY,
- ruby_19: Gem::Platform::RUBY,
- ruby_20: Gem::Platform::RUBY,
- ruby_21: Gem::Platform::RUBY,
- ruby_22: Gem::Platform::RUBY,
- ruby_23: Gem::Platform::RUBY,
- ruby_24: Gem::Platform::RUBY,
- ruby_25: Gem::Platform::RUBY,
- ruby_26: Gem::Platform::RUBY,
- ruby_27: Gem::Platform::RUBY,
- ruby_30: Gem::Platform::RUBY,
- ruby_31: Gem::Platform::RUBY,
- ruby_32: Gem::Platform::RUBY,
- ruby_33: Gem::Platform::RUBY,
- ruby_34: Gem::Platform::RUBY,
- ruby_35: Gem::Platform::RUBY,
- mri: Gem::Platform::RUBY,
- mri_18: Gem::Platform::RUBY,
- mri_19: Gem::Platform::RUBY,
- mri_20: Gem::Platform::RUBY,
- mri_21: Gem::Platform::RUBY,
- mri_22: Gem::Platform::RUBY,
- mri_23: Gem::Platform::RUBY,
- mri_24: Gem::Platform::RUBY,
- mri_25: Gem::Platform::RUBY,
- mri_26: Gem::Platform::RUBY,
- mri_27: Gem::Platform::RUBY,
- mri_30: Gem::Platform::RUBY,
- mri_31: Gem::Platform::RUBY,
- mri_32: Gem::Platform::RUBY,
- mri_33: Gem::Platform::RUBY,
- mri_34: Gem::Platform::RUBY,
- mri_35: Gem::Platform::RUBY,
- rbx: Gem::Platform::RUBY,
- truffleruby: Gem::Platform::RUBY,
- jruby: Gem::Platform::JAVA,
- jruby_18: Gem::Platform::JAVA,
- jruby_19: Gem::Platform::JAVA,
- windows: Gem::Platform::WINDOWS,
- windows_18: Gem::Platform::WINDOWS,
- windows_19: Gem::Platform::WINDOWS,
- windows_20: Gem::Platform::WINDOWS,
- windows_21: Gem::Platform::WINDOWS,
- windows_22: Gem::Platform::WINDOWS,
- windows_23: Gem::Platform::WINDOWS,
- windows_24: Gem::Platform::WINDOWS,
- windows_25: Gem::Platform::WINDOWS,
- windows_26: Gem::Platform::WINDOWS,
- windows_27: Gem::Platform::WINDOWS,
- windows_30: Gem::Platform::WINDOWS,
- windows_31: Gem::Platform::WINDOWS,
- windows_32: Gem::Platform::WINDOWS,
- windows_33: Gem::Platform::WINDOWS,
- windows_34: Gem::Platform::WINDOWS,
- windows_35: Gem::Platform::WINDOWS }
- end
-
- let(:deprecated) do
- { mswin: Gem::Platform::MSWIN,
- mswin_18: Gem::Platform::MSWIN,
- mswin_19: Gem::Platform::MSWIN,
- mswin_20: Gem::Platform::MSWIN,
- mswin_21: Gem::Platform::MSWIN,
- mswin_22: Gem::Platform::MSWIN,
- mswin_23: Gem::Platform::MSWIN,
- mswin_24: Gem::Platform::MSWIN,
- mswin_25: Gem::Platform::MSWIN,
- mswin_26: Gem::Platform::MSWIN,
- mswin_27: Gem::Platform::MSWIN,
- mswin_30: Gem::Platform::MSWIN,
- mswin_31: Gem::Platform::MSWIN,
- mswin_32: Gem::Platform::MSWIN,
- mswin_33: Gem::Platform::MSWIN,
- mswin_34: Gem::Platform::MSWIN,
- mswin_35: Gem::Platform::MSWIN,
- mswin64: Gem::Platform::MSWIN64,
- mswin64_19: Gem::Platform::MSWIN64,
- mswin64_20: Gem::Platform::MSWIN64,
- mswin64_21: Gem::Platform::MSWIN64,
- mswin64_22: Gem::Platform::MSWIN64,
- mswin64_23: Gem::Platform::MSWIN64,
- mswin64_24: Gem::Platform::MSWIN64,
- mswin64_25: Gem::Platform::MSWIN64,
- mswin64_26: Gem::Platform::MSWIN64,
- mswin64_27: Gem::Platform::MSWIN64,
- mswin64_30: Gem::Platform::MSWIN64,
- mswin64_31: Gem::Platform::MSWIN64,
- mswin64_32: Gem::Platform::MSWIN64,
- mswin64_33: Gem::Platform::MSWIN64,
- mswin64_34: Gem::Platform::MSWIN64,
- mswin64_35: Gem::Platform::MSWIN64,
- mingw: Gem::Platform::MINGW,
- mingw_18: Gem::Platform::MINGW,
- mingw_19: Gem::Platform::MINGW,
- mingw_20: Gem::Platform::MINGW,
- mingw_21: Gem::Platform::MINGW,
- mingw_22: Gem::Platform::MINGW,
- mingw_23: Gem::Platform::MINGW,
- mingw_24: Gem::Platform::MINGW,
- mingw_25: Gem::Platform::MINGW,
- mingw_26: Gem::Platform::MINGW,
- mingw_27: Gem::Platform::MINGW,
- mingw_30: Gem::Platform::MINGW,
- mingw_31: Gem::Platform::MINGW,
- mingw_32: Gem::Platform::MINGW,
- mingw_33: Gem::Platform::MINGW,
- mingw_34: Gem::Platform::MINGW,
- mingw_35: Gem::Platform::MINGW,
- x64_mingw: Gem::Platform::X64_MINGW,
- x64_mingw_20: Gem::Platform::X64_MINGW,
- x64_mingw_21: Gem::Platform::X64_MINGW,
- x64_mingw_22: Gem::Platform::X64_MINGW,
- x64_mingw_23: Gem::Platform::X64_MINGW,
- x64_mingw_24: Gem::Platform::X64_MINGW,
- x64_mingw_25: Gem::Platform::X64_MINGW,
- x64_mingw_26: Gem::Platform::X64_MINGW,
- x64_mingw_27: Gem::Platform::X64_MINGW,
- x64_mingw_30: Gem::Platform::X64_MINGW,
- x64_mingw_31: Gem::Platform::X64_MINGW,
- x64_mingw_32: Gem::Platform::X64_MINGW,
- x64_mingw_33: Gem::Platform::X64_MINGW,
- x64_mingw_34: Gem::Platform::X64_MINGW,
- x64_mingw_35: Gem::Platform::X64_MINGW }
- end
- # rubocop:enable Naming/VariableNumber
+ dep = described_class.new(
+ "test_gem",
+ "1.0.0",
+ { "platforms" => "#{engine}_#{RbConfig::CONFIG["MAJOR"]}#{RbConfig::CONFIG["MINOR"]}" },
+ )
- it "includes all platforms" do
- expect(subject).to eq(platforms.merge(deprecated))
- end
+ expect(dep.current_platform?).to be_truthy
end
end
diff --git a/spec/bundler/bundler/dsl_spec.rb b/spec/bundler/bundler/dsl_spec.rb
index c4f9d0dbb5..b6e67a312c 100644
--- a/spec/bundler/bundler/dsl_spec.rb
+++ b/spec/bundler/bundler/dsl_spec.rb
@@ -103,7 +103,7 @@ RSpec.describe Bundler::Dsl do
)
end
- context "default hosts", bundler: "< 3" do
+ context "default hosts" do
it "converts :github to URI using https" do
subject.gem("sparks", github: "indirect/sparks")
github_uri = "https://github.com/indirect/sparks.git"
@@ -162,7 +162,7 @@ RSpec.describe Bundler::Dsl do
describe "#method_missing" do
it "raises an error for unknown DSL methods" do
- expect(Bundler).to receive(:read_file).with(source_root.join("Gemfile").to_s).
+ expect(Bundler).to receive(:read_file).with(git_root.join("Gemfile").to_s).
and_return("unknown")
error_msg = "There was an error parsing `Gemfile`: Undefined local variable or method `unknown' for Gemfile. Bundler cannot continue."
@@ -173,13 +173,13 @@ RSpec.describe Bundler::Dsl do
describe "#eval_gemfile" do
it "handles syntax errors with a useful message" do
- expect(Bundler).to receive(:read_file).with(source_root.join("Gemfile").to_s).and_return("}")
+ expect(Bundler).to receive(:read_file).with(git_root.join("Gemfile").to_s).and_return("}")
expect { subject.eval_gemfile("Gemfile") }.
to raise_error(Bundler::GemfileError, /There was an error parsing `Gemfile`: (syntax error, unexpected tSTRING_DEND|(compile error - )?syntax error, unexpected '\}'|.+?unexpected '}', ignoring it\n). Bundler cannot continue./m)
end
it "distinguishes syntax errors from evaluation errors" do
- expect(Bundler).to receive(:read_file).with(source_root.join("Gemfile").to_s).and_return(
+ expect(Bundler).to receive(:read_file).with(git_root.join("Gemfile").to_s).and_return(
"ruby '2.1.5', :engine => 'ruby', :engine_version => '1.2.4'"
)
expect { subject.eval_gemfile("Gemfile") }.
@@ -187,13 +187,13 @@ RSpec.describe Bundler::Dsl do
end
it "populates __dir__ and __FILE__ correctly" do
- abs_path = source_root.join("../fragment.rb").to_s
+ abs_path = git_root.join("../fragment.rb").to_s
expect(Bundler).to receive(:read_file).with(abs_path).and_return(<<~RUBY)
@fragment_dir = __dir__
@fragment_file = __FILE__
RUBY
subject.eval_gemfile("../fragment.rb")
- expect(subject.instance_variable_get(:@fragment_dir)).to eq(source_root.dirname.to_s)
+ expect(subject.instance_variable_get(:@fragment_dir)).to eq(git_root.dirname.to_s)
expect(subject.instance_variable_get(:@fragment_file)).to eq(abs_path)
end
end
@@ -201,8 +201,8 @@ RSpec.describe Bundler::Dsl do
describe "#gem" do
# rubocop:disable Naming/VariableNumber
[:ruby, :ruby_18, :ruby_19, :ruby_20, :ruby_21, :ruby_22, :ruby_23, :ruby_24, :ruby_25, :ruby_26, :ruby_27,
- :ruby_30, :ruby_31, :ruby_32, :ruby_33, :mri, :mri_18, :mri_19, :mri_20, :mri_21, :mri_22, :mri_23, :mri_24,
- :mri_25, :mri_26, :mri_27, :mri_30, :mri_31, :mri_32, :mri_33, :jruby, :rbx, :truffleruby].each do |platform|
+ :ruby_30, :ruby_31, :ruby_32, :ruby_33, :ruby_34, :ruby_40, :mri, :mri_18, :mri_19, :mri_20, :mri_21, :mri_22, :mri_23, :mri_24,
+ :mri_25, :mri_26, :mri_27, :mri_30, :mri_31, :mri_32, :mri_33, :mri_34, :mri_40, :jruby, :rbx, :truffleruby].each do |platform|
it "allows #{platform} as a valid platform" do
subject.gem("foo", platform: platform)
end
@@ -211,7 +211,9 @@ RSpec.describe Bundler::Dsl do
it "allows platforms matching the running Ruby version" do
platform = "ruby_#{RbConfig::CONFIG["MAJOR"]}#{RbConfig::CONFIG["MINOR"]}"
- subject.gem("foo", platform: platform)
+
+ expect { subject.gem("foo", platform: platform) }.not_to raise_error
+ expect(Bundler.current_ruby.respond_to?("#{platform}?")).to be_truthy
end
it "rejects invalid platforms" do
@@ -219,6 +221,11 @@ RSpec.describe Bundler::Dsl do
to raise_error(Bundler::GemfileError, /is not a valid platform/)
end
+ it "warn for legacy windows platforms" do
+ expect(Bundler::SharedHelpers).to receive(:feature_deprecated!).with(/\APlatform :mswin, :x64_mingw will be removed in the future./)
+ subject.gem("foo", platforms: [:mswin, :jruby, :x64_mingw])
+ end
+
it "rejects empty gem name" do
expect { subject.gem("") }.
to raise_error(Bundler::GemfileError, /an empty gem name is not valid/)
@@ -283,6 +290,15 @@ RSpec.describe Bundler::Dsl do
end
end
+ describe "#platforms" do
+ it "warn for legacy windows platforms" do
+ expect(Bundler::SharedHelpers).to receive(:feature_deprecated!).with(/\APlatform :mswin64, :mingw will be removed in the future./)
+ subject.platforms(:mswin64, :jruby, :mingw) do
+ subject.gem("foo")
+ end
+ end
+ end
+
context "can bundle groups of gems with" do
# git "https://github.com/rails/rails.git" do
# gem "railties"
@@ -350,4 +366,193 @@ RSpec.describe Bundler::Dsl do
end
end
end
+
+ describe "#source with cooldown" do
+ before do
+ allow(@rubygems).to receive(:add_remote)
+ end
+
+ it "accepts a non-negative integer" do
+ expect do
+ subject.source("https://rubygems.org", cooldown: 7)
+ end.not_to raise_error
+ end
+
+ it "accepts 0 as an explicit disable" do
+ expect do
+ subject.source("https://rubygems.org", cooldown: 0)
+ end.not_to raise_error
+ end
+
+ it "rejects a string" do
+ expect do
+ subject.source("https://rubygems.org", cooldown: "7")
+ end.to raise_error(Bundler::InvalidOption, /non-negative integer/)
+ end
+
+ it "rejects a float" do
+ expect do
+ subject.source("https://rubygems.org", cooldown: 7.5)
+ end.to raise_error(Bundler::InvalidOption, /non-negative integer/)
+ end
+
+ it "rejects a negative integer" do
+ expect do
+ subject.source("https://rubygems.org", cooldown: -7)
+ end.to raise_error(Bundler::InvalidOption, /non-negative integer/)
+ end
+
+ it "rejects an array" do
+ expect do
+ subject.source("https://rubygems.org", cooldown: [7])
+ end.to raise_error(Bundler::InvalidOption, /non-negative integer/)
+ end
+ end
+
+ describe "#override" do
+ it "stores an Override for a gem with a version: operation" do
+ subject.override("rails", version: ">= 8.0")
+
+ expect(subject.overrides.size).to eq(1)
+ override = subject.overrides.first
+ expect(override.target).to eq("rails")
+ expect(override.field).to eq(:version)
+ expect(override.operation).to eq(">= 8.0")
+ end
+
+ it "accepts :ignore_upper as the operation" do
+ subject.override("nokogiri", version: :ignore_upper)
+ expect(subject.overrides.first.operation).to eq(:ignore_upper)
+ end
+
+ it "accepts nil as the operation" do
+ subject.override("legacy", version: nil)
+ expect(subject.overrides.first.operation).to be_nil
+ end
+
+ it "appends to overrides across multiple statements" do
+ subject.override("rails", version: ">= 8.0")
+ subject.override("nokogiri", version: :ignore_upper)
+ expect(subject.overrides.map(&:target)).to eq(["rails", "nokogiri"])
+ end
+
+ it "is empty by default" do
+ expect(subject.overrides).to eq([])
+ end
+
+ it "raises ArgumentError when target is :all and version: is given" do
+ expect do
+ subject.override(:all, version: ">= 8.0")
+ end.to raise_error(ArgumentError, /`override :all, version:` is not allowed/)
+ end
+
+ it "rejects :all + version: even when other fields are also given" do
+ expect do
+ subject.override(:all, required_ruby_version: :ignore_upper, version: ">= 8.0")
+ end.to raise_error(ArgumentError, /`override :all, version:` is not allowed/)
+ end
+
+ it "does not record any override when :all + version: is rejected" do
+ expect do
+ subject.override(:all, version: ">= 8.0")
+ end.to raise_error(ArgumentError)
+ expect(subject.overrides).to eq([])
+ end
+
+ it "raises ArgumentError when target is neither :all nor a string" do
+ expect do
+ subject.override(:rails, version: ">= 8.0")
+ end.to raise_error(ArgumentError, /target must be :all or a gem name string/)
+ end
+
+ it "raises ArgumentError for an unsupported field" do
+ expect do
+ subject.override("rails", as: "y")
+ end.to raise_error(ArgumentError, /unsupported override field `as:`/)
+ end
+
+ it "stores an Override for a gem with a required_ruby_version: operation" do
+ subject.override("rails", required_ruby_version: :ignore_upper)
+ override = subject.overrides.first
+ expect(override.target).to eq("rails")
+ expect(override.field).to eq(:required_ruby_version)
+ expect(override.operation).to eq(:ignore_upper)
+ end
+
+ it "stores an Override for a gem with a required_rubygems_version: operation" do
+ subject.override("rails", required_rubygems_version: nil)
+ override = subject.overrides.first
+ expect(override.field).to eq(:required_rubygems_version)
+ expect(override.operation).to be_nil
+ end
+
+ it "stores an Override targeting :all with a metadata field" do
+ subject.override(:all, required_ruby_version: :ignore_upper)
+ override = subject.overrides.first
+ expect(override.target).to eq(:all)
+ expect(override.field).to eq(:required_ruby_version)
+ expect(override.operation).to eq(:ignore_upper)
+ end
+
+ it "stores an Override targeting :all with required_rubygems_version" do
+ subject.override(:all, required_rubygems_version: nil)
+ override = subject.overrides.first
+ expect(override.target).to eq(:all)
+ expect(override.field).to eq(:required_rubygems_version)
+ end
+
+ it "raises ArgumentError for a non-string, non-symbol, non-nil operation" do
+ expect do
+ subject.override("rails", version: 42)
+ end.to raise_error(ArgumentError, /override operation must be a String, Symbol, or nil/)
+ end
+
+ it "raises ArgumentError for an unsupported symbol operation" do
+ expect do
+ subject.override("rails", version: :explode)
+ end.to raise_error(ArgumentError, /unsupported override operation/)
+ end
+
+ it "raises ArgumentError for an unparsable version string" do
+ expect do
+ subject.override("rails", version: "not a version")
+ end.to raise_error(ArgumentError, /invalid override version requirement/)
+ end
+
+ it "does not record an override when the version string is invalid" do
+ expect do
+ subject.override("rails", version: "not a version")
+ end.to raise_error(ArgumentError)
+ expect(subject.overrides).to eq([])
+ end
+
+ it "rejects atomically when one field in a multi-field call is invalid" do
+ expect do
+ subject.override("rails", version: ">= 8.0", as: "y")
+ end.to raise_error(ArgumentError, /unsupported override field/)
+ expect(subject.overrides).to eq([])
+ end
+
+ it "raises ArgumentError when the same target and field are overridden twice" do
+ subject.override("rails", version: ">= 8.0")
+ expect do
+ subject.override("rails", version: :ignore_upper)
+ end.to raise_error(ArgumentError, /duplicate override for "rails" `version:`/)
+ end
+
+ it "keeps the original override when a duplicate is rejected" do
+ subject.override("rails", version: ">= 8.0")
+ expect do
+ subject.override("rails", version: :ignore_upper)
+ end.to raise_error(ArgumentError)
+ expect(subject.overrides.size).to eq(1)
+ expect(subject.overrides.first.operation).to eq(">= 8.0")
+ end
+
+ it "allows different targets with the same field" do
+ subject.override("rails", version: ">= 8.0")
+ subject.override("nokogiri", version: :ignore_upper)
+ expect(subject.overrides.size).to eq(2)
+ end
+ end
end
diff --git a/spec/bundler/bundler/endpoint_specification_spec.rb b/spec/bundler/bundler/endpoint_specification_spec.rb
index 6518f125ba..4fbd59d48f 100644
--- a/spec/bundler/bundler/endpoint_specification_spec.rb
+++ b/spec/bundler/bundler/endpoint_specification_spec.rb
@@ -46,6 +46,46 @@ RSpec.describe Bundler::EndpointSpecification do
)
end
end
+
+ context "when the metadata has created_at" do
+ let(:metadata) { { "created_at" => ["2026-05-12T10:00:00Z"] } }
+
+ it "parses created_at as a Time" do
+ expect(subject.created_at).to eq(Time.utc(2026, 5, 12, 10, 0, 0))
+ end
+ end
+
+ context "when the metadata has a string created_at (older rubygems shape)" do
+ let(:metadata) { { "created_at" => "2026-05-12T10:00:00Z" } }
+
+ it "still parses created_at" do
+ expect(subject.created_at).to eq(Time.utc(2026, 5, 12, 10, 0, 0))
+ end
+ end
+
+ context "when created_at is truncated (older rubygems splits on colons)" do
+ let(:metadata) { { "created_at" => "2026-05-12T10" } }
+
+ it "leaves created_at as nil instead of raising" do
+ expect(subject.created_at).to be_nil
+ end
+ end
+
+ context "when the metadata has no created_at" do
+ let(:metadata) { { "checksum" => ["abc"] } }
+ let(:spec_fetcher) { double(:spec_fetcher, uri: "https://rubygems.org") }
+
+ it "leaves created_at as nil" do
+ allow(Bundler::Checksum).to receive(:from_api).and_return(nil)
+ expect(subject.created_at).to be_nil
+ end
+ end
+
+ context "when the metadata is nil" do
+ it "leaves created_at as nil" do
+ expect(subject.created_at).to be_nil
+ end
+ end
end
describe "#required_ruby_version" do
diff --git a/spec/bundler/bundler/env_spec.rb b/spec/bundler/bundler/env_spec.rb
index e0ab0a45e3..2b7dbde217 100644
--- a/spec/bundler/bundler/env_spec.rb
+++ b/spec/bundler/bundler/env_spec.rb
@@ -217,20 +217,21 @@ RSpec.describe Bundler::Env do
context "when the git version is OS specific" do
it "includes OS specific information with the version number" do
- expect(git_proxy_stub).to receive(:git_local).with("--version").
- and_return("git version 1.2.3 (Apple Git-BS)")
+ status = double("success?" => true)
+ expect(Open3).to receive(:capture3).with("git", "--version").
+ and_return(["git version 1.2.3 (Apple Git-BS)", "", status])
expect(Bundler::Source::Git::GitProxy).to receive(:new).and_return(git_proxy_stub)
- expect(described_class.report).to include("Git 1.2.3 (Apple Git-BS)")
+ expect(described_class.report).to include("Git 1.2.3 (Apple Git-BS)")
end
end
- end
-
- describe ".version_of" do
- let(:parsed_version) { described_class.send(:version_of, "ruby") }
- it "strips version of new line characters" do
- expect(parsed_version).to_not end_with("\n")
+ it "no longer reports the Tools section or external tool versions" do
+ report = described_class.report
+ expect(report).not_to include("Tools")
+ ["rbenv", "RVM", "chruby"].each do |tool|
+ expect(report).not_to include(tool)
+ end
end
end
end
diff --git a/spec/bundler/bundler/errors_spec.rb b/spec/bundler/bundler/errors_spec.rb
new file mode 100644
index 0000000000..b62d85d32b
--- /dev/null
+++ b/spec/bundler/bundler/errors_spec.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::IncorrectLockfileDependencies do
+ describe "#message" do
+ let(:spec) do
+ double("LazySpecification", full_name: "rubocop-1.82.0")
+ end
+
+ context "without dependency details" do
+ subject { described_class.new(spec) }
+
+ it "provides a basic error message" do
+ expect(subject.message).to include("Bundler found incorrect dependencies in the lockfile for rubocop-1.82.0")
+ expect(subject.message).to include("Please run `bundle install` to regenerate the lockfile.")
+ end
+ end
+
+ context "with dependency details" do
+ let(:actual_dependencies) do
+ [
+ Gem::Dependency.new("json", [">= 2.3", "< 4.0"]),
+ Gem::Dependency.new("parallel", ["~> 1.10"]),
+ Gem::Dependency.new("parser", [">= 3.3.0.2"]),
+ ]
+ end
+
+ let(:lockfile_dependencies) do
+ [
+ Gem::Dependency.new("json", [">= 2.3", "< 3.0"]),
+ Gem::Dependency.new("parallel", ["~> 1.10"]),
+ Gem::Dependency.new("parser", [">= 3.2.0.0"]),
+ ]
+ end
+
+ subject { described_class.new(spec, actual_dependencies, lockfile_dependencies) }
+
+ it "shows only mismatched dependencies" do
+ message = subject.message
+
+ expect(message).to include("json: gemspec specifies")
+ expect(message).to include("parser: gemspec specifies")
+ expect(message).not_to include("parallel")
+ end
+ end
+
+ context "when gemspec has dependencies but lockfile has none" do
+ let(:actual_dependencies) do
+ [
+ Gem::Dependency.new("myrack-test", ["~> 1.0"]),
+ ]
+ end
+
+ let(:lockfile_dependencies) { [] }
+
+ subject { described_class.new(spec, actual_dependencies, lockfile_dependencies) }
+
+ it "shows the dependency as not in lockfile" do
+ message = subject.message
+
+ expect(message).to include("myrack-test: gemspec specifies ~> 1.0, not in lockfile")
+ end
+ end
+
+ context "when gemspec has no dependencies but lockfile has some" do
+ let(:actual_dependencies) { [] }
+
+ let(:lockfile_dependencies) do
+ [
+ Gem::Dependency.new("unexpected", ["~> 1.0"]),
+ ]
+ end
+
+ subject { described_class.new(spec, actual_dependencies, lockfile_dependencies) }
+
+ it "shows the dependency as not in gemspec" do
+ message = subject.message
+
+ expect(message).to include("unexpected: not in gemspec, lockfile has ~> 1.0")
+ end
+ end
+ end
+
+ describe "#status_code" do
+ let(:spec) { double("LazySpecification", full_name: "test-1.0.0") }
+ subject { described_class.new(spec) }
+
+ it "returns 41" do
+ expect(subject.status_code).to eq(41)
+ end
+ end
+end
diff --git a/spec/bundler/bundler/fetcher/dependency_spec.rb b/spec/bundler/bundler/fetcher/dependency_spec.rb
index c420b7c07f..501bc269a5 100644
--- a/spec/bundler/bundler/fetcher/dependency_spec.rb
+++ b/spec/bundler/bundler/fetcher/dependency_spec.rb
@@ -212,7 +212,7 @@ RSpec.describe Bundler::Fetcher::Dependency do
let(:dep_api_uri) { double(:dep_api_uri) }
let(:unmarshalled_gems) { double(:unmarshalled_gems) }
let(:fetch_response) { double(:fetch_response, body: double(:body)) }
- let(:rubygems_limit) { 50 }
+ let(:rubygems_limit) { 100 }
before { allow(subject).to receive(:dependency_api_uri).with(gem_names).and_return(dep_api_uri) }
@@ -222,6 +222,18 @@ RSpec.describe Bundler::Fetcher::Dependency do
expect(Bundler).to receive(:safe_load_marshal).with(fetch_response.body).and_return([unmarshalled_gems])
expect(subject.unmarshalled_dep_gems(gem_names)).to eq([unmarshalled_gems])
end
+
+ it "should fetch as many dependencies as specified" do
+ allow(subject).to receive(:dependency_api_uri).with([%w[foo bar]]).and_return(dep_api_uri)
+ allow(subject).to receive(:dependency_api_uri).with([%w[bundler rubocop]]).and_return(dep_api_uri)
+
+ expect(downloader).to receive(:fetch).twice.with(dep_api_uri).and_return(fetch_response)
+ expect(Bundler).to receive(:safe_load_marshal).twice.with(fetch_response.body).and_return([unmarshalled_gems])
+
+ Bundler.settings.temporary(api_request_size: 1) do
+ expect(subject.unmarshalled_dep_gems(gem_names)).to eq([unmarshalled_gems, unmarshalled_gems])
+ end
+ end
end
describe "#get_formatted_specs_and_deps" do
diff --git a/spec/bundler/bundler/fetcher/downloader_spec.rb b/spec/bundler/bundler/fetcher/downloader_spec.rb
index 13f8cc74aa..edf426328a 100644
--- a/spec/bundler/bundler/fetcher/downloader_spec.rb
+++ b/spec/bundler/bundler/fetcher/downloader_spec.rb
@@ -83,12 +83,12 @@ RSpec.describe Bundler::Fetcher::Downloader do
/Authentication is required for www.uri-to-fetch.com/)
end
- it "should raise a Bundler::Fetcher::AuthenticationRequiredError with advices" do
+ it "should raise a Bundler::Fetcher::AuthenticationRequiredError with advice" do
expect { subject.fetch(uri, options, counter) }.to raise_error(Bundler::Fetcher::AuthenticationRequiredError,
/`bundle config set --global www\.uri-to-fetch\.com username:password`.*`BUNDLE_WWW__URI___TO___FETCH__COM`/m)
end
- context "when the there are credentials provided in the request" do
+ context "when there are credentials provided in the request" do
let(:uri) { Gem::URI("http://user:password@www.uri-to-fetch.com") }
it "should raise a Bundler::Fetcher::BadAuthenticationError that doesn't contain the password" do
@@ -116,7 +116,7 @@ RSpec.describe Bundler::Fetcher::Downloader do
to raise_error(Bundler::Fetcher::FallbackError, "Gem::Net::HTTPNotFound: http://www.uri-to-fetch.com/api/v2/endpoint")
end
- context "when the there are credentials provided in the request" do
+ context "when there are credentials provided in the request" do
let(:uri) { Gem::URI("http://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
it "should raise a Bundler::Fetcher::FallbackError that doesn't contain the password" do
@@ -126,6 +126,38 @@ RSpec.describe Bundler::Fetcher::Downloader do
end
end
+ context "when the request response is a Gem::Net::HTTPRequestedRangeNotSatisfiable" do
+ let(:http_response) { Gem::Net::HTTPRequestedRangeNotSatisfiable.new("1.1", 416, "Range Not Satisfiable") }
+ let(:success_response) { Gem::Net::HTTPSuccess.new("1.1", 200, "Success") }
+ let(:options) { { "Range" => "bytes=1000-", "If-None-Match" => "some-etag" } }
+
+ before do
+ # First request returns 416, retry request returns success
+ allow(subject).to receive(:request).with(uri, options).and_return(http_response)
+ allow(subject).to receive(:request).with(uri, { "If-None-Match" => "some-etag" }).and_return(success_response)
+ end
+
+ # The 416 handler removes the Range header and retries without incrementing the counter.
+ # Importantly, it does NOT add Accept-Encoding header, which would break Ruby's
+ # automatic gzip decompression (see issue #9271 for details on that bug).
+ it "should retry the request without the Range header" do
+ expect(subject).to receive(:request).with(uri, options).ordered
+ expect(subject).to receive(:request).with(uri, hash_excluding("Range", "Accept-Encoding")).ordered
+ subject.fetch(uri, options, counter)
+ end
+
+ it "should preserve other headers on retry" do
+ expect(subject).to receive(:request).with(uri, options).ordered
+ expect(subject).to receive(:request).with(uri, hash_including("If-None-Match" => "some-etag")).ordered
+ subject.fetch(uri, options, counter)
+ end
+
+ it "should return the successful response" do
+ result = subject.fetch(uri, options, counter)
+ expect(result).to eq(success_response)
+ end
+ end
+
context "when the request response is some other type" do
let(:http_response) { Gem::Net::HTTPBadGateway.new("1.1", 500, "Fatal Error") }
@@ -201,39 +233,29 @@ RSpec.describe Bundler::Fetcher::Downloader do
end
end
- context "when the request response causes an error included in HTTP_ERRORS" do
- let(:message) { nil }
- let(:error) { RuntimeError.new(message) }
+ context "when the request response causes an HTTP error" do
+ let(:message) { "error about network" }
+ let(:error) { error_class.new(message) }
before do
- stub_const("Bundler::Fetcher::HTTP_ERRORS", [RuntimeError])
allow(connection).to receive(:request).with(uri, net_http_get) { raise error }
end
- it "should trace log the error" do
- allow(Bundler).to receive_message_chain(:ui, :debug)
- expect(Bundler).to receive_message_chain(:ui, :trace).with(error)
- expect { subject.request(uri, options) }.to raise_error(Bundler::HTTPError)
- end
-
- context "when error message is about the host being down" do
- let(:message) { "host down: http://www.uri-to-fetch.com" }
+ context "that it's retryable" do
+ let(:error_class) { Gem::Timeout::Error }
- it "should raise a Bundler::Fetcher::NetworkDownError" do
- expect { subject.request(uri, options) }.to raise_error(Bundler::Fetcher::NetworkDownError,
- /Could not reach host www.uri-to-fetch.com/)
+ it "should trace log the error" do
+ allow(Bundler).to receive_message_chain(:ui, :debug)
+ expect(Bundler).to receive_message_chain(:ui, :trace).with(error)
+ expect { subject.request(uri, options) }.to raise_error(Bundler::HTTPError)
end
- end
-
- context "when error message is not about host down" do
- let(:message) { "other error about network" }
it "should raise a Bundler::HTTPError" do
expect { subject.request(uri, options) }.to raise_error(Bundler::HTTPError,
- "Network error while fetching http://www.uri-to-fetch.com/api/v2/endpoint (other error about network)")
+ "Network error while fetching http://www.uri-to-fetch.com/api/v2/endpoint (error about network)")
end
- context "when the there are credentials provided in the request" do
+ context "when there are credentials provided in the request" do
let(:uri) { Gem::URI("http://username:password@www.uri-to-fetch.com/api/v2/endpoint") }
before do
allow(net_http_get).to receive(:basic_auth).with("username", "password")
@@ -241,17 +263,38 @@ RSpec.describe Bundler::Fetcher::Downloader do
it "should raise a Bundler::HTTPError that doesn't contain the password" do
expect { subject.request(uri, options) }.to raise_error(Bundler::HTTPError,
- "Network error while fetching http://username@www.uri-to-fetch.com/api/v2/endpoint (other error about network)")
+ "Network error while fetching http://username@www.uri-to-fetch.com/api/v2/endpoint (error about network)")
end
end
end
- context "when error message is about no route to host" do
+ context "when error is about the host being down" do
+ let(:error_class) { Gem::Net::HTTP::Persistent::Error }
+ let(:message) { "host down: http://www.uri-to-fetch.com" }
+
+ it "should raise a Bundler::Fetcher::NetworkDownError" do
+ expect { subject.request(uri, options) }.to raise_error(Bundler::Fetcher::NetworkDownError,
+ /Could not reach host www.uri-to-fetch.com/)
+ end
+ end
+
+ context "when error is about connection refused" do
+ let(:error_class) { Gem::Net::HTTP::Persistent::Error }
+ let(:message) { "connection refused down: http://www.uri-to-fetch.com" }
+
+ it "should raise a Bundler::Fetcher::NetworkDownError" do
+ expect { subject.request(uri, options) }.to raise_error(Bundler::Fetcher::NetworkDownError,
+ /Could not reach host www.uri-to-fetch.com/)
+ end
+ end
+
+ context "when error is about no route to host" do
+ let(:error_class) { SocketError }
let(:message) { "Failed to open TCP connection to www.uri-to-fetch.com:443 " }
- it "should raise a Bundler::Fetcher::HTTPError" do
- expect { subject.request(uri, options) }.to raise_error(Bundler::HTTPError,
- "Network error while fetching http://www.uri-to-fetch.com/api/v2/endpoint (#{message})")
+ it "should raise a Bundler::Fetcher::NetworkDownError" do
+ expect { subject.request(uri, options) }.to raise_error(Bundler::Fetcher::NetworkDownError,
+ /Could not reach host www.uri-to-fetch.com/)
end
end
end
diff --git a/spec/bundler/bundler/fetcher/gem_remote_fetcher_spec.rb b/spec/bundler/bundler/fetcher/gem_remote_fetcher_spec.rb
new file mode 100644
index 0000000000..df1a58d843
--- /dev/null
+++ b/spec/bundler/bundler/fetcher/gem_remote_fetcher_spec.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+require "rubygems/remote_fetcher"
+require "bundler/fetcher/gem_remote_fetcher"
+require_relative "../../support/artifice/helpers/artifice"
+require "bundler/vendored_persistent.rb"
+
+RSpec.describe Bundler::Fetcher::GemRemoteFetcher do
+ describe "Parallel download" do
+ it "download using multiple connections from the pool" do
+ unless Bundler.rubygems.provides?(">= 4.0.0.dev")
+ skip "This example can only run when RubyGems supports multiple http connection pool"
+ end
+
+ require_relative "../../support/artifice/helpers/endpoint"
+ concurrent_ruby_path = Dir[scoped_base_system_gem_path.join("gems/concurrent-ruby-*/lib/concurrent-ruby")].first
+ $LOAD_PATH.unshift(concurrent_ruby_path)
+ require "concurrent-ruby"
+
+ require_rack_test
+ responses = []
+
+ latch1 = Concurrent::CountDownLatch.new
+ latch2 = Concurrent::CountDownLatch.new
+ previous_client = Gem::Request::ConnectionPools.client
+ dummy_endpoint = Class.new(Endpoint) do
+ get "/foo" do
+ latch2.count_down
+ latch1.wait
+
+ responses << "foo"
+ end
+
+ get "/bar" do
+ responses << "bar"
+
+ latch1.count_down
+ end
+ end
+
+ Artifice.activate_with(dummy_endpoint)
+ Gem::Request::ConnectionPools.client = Gem::Net::HTTP
+
+ first_request = Thread.new do
+ subject.fetch_path("https://example.org/foo")
+ end
+ second_request = Thread.new do
+ latch2.wait
+ subject.fetch_path("https://example.org/bar")
+ end
+
+ [first_request, second_request].each(&:join)
+
+ expect(responses).to eq(["bar", "foo"])
+ ensure
+ Artifice.deactivate
+ Gem::Request::ConnectionPools.client = previous_client
+ end
+ end
+end
diff --git a/spec/bundler/bundler/friendly_errors_spec.rb b/spec/bundler/bundler/friendly_errors_spec.rb
index 255019f40a..426e3c856d 100644
--- a/spec/bundler/bundler/friendly_errors_spec.rb
+++ b/spec/bundler/bundler/friendly_errors_spec.rb
@@ -2,7 +2,8 @@
require "bundler"
require "bundler/friendly_errors"
-require "cgi"
+require "cgi/escape"
+require "cgi/util" unless defined?(CGI::EscapeExt)
RSpec.describe Bundler, "friendly errors" do
context "with invalid YAML in .gemrc" do
@@ -130,17 +131,13 @@ RSpec.describe Bundler, "friendly errors" do
# Does nothing
end
- context "Java::JavaLang::OutOfMemoryError" do
- module Java
- module JavaLang
- class OutOfMemoryError < StandardError; end
- end
- end
-
+ context "Java::JavaLang::OutOfMemoryError", :jruby_only do
it "Bundler.ui receive error" do
- error = Java::JavaLang::OutOfMemoryError.new
- expect(Bundler.ui).to receive(:error).with(/JVM has run out of memory/)
- Bundler::FriendlyErrors.log_error(error)
+ install_gemfile <<-G, raise_on_error: false, env: { "JRUBY_OPTS" => "-J-Xmx32M" }, artifice: nil
+ source "https://gem.repo1"
+ G
+
+ expect(err).to include("JVM has run out of memory")
end
end
@@ -200,7 +197,7 @@ RSpec.describe Bundler, "friendly errors" do
it "generates a search URL for the exception message" do
exception = Exception.new("Exception message")
- expect(Bundler::FriendlyErrors.issues_url(exception)).to eq("https://github.com/rubygems/rubygems/search?q=Exception+message&type=Issues")
+ expect(Bundler::FriendlyErrors.issues_url(exception)).to eq("https://github.com/ruby/rubygems/search?q=Exception+message&type=Issues")
end
it "generates a search URL for only the first line of a multi-line exception message" do
@@ -209,7 +206,7 @@ First line of the exception message
Second line of the exception message
END
- expect(Bundler::FriendlyErrors.issues_url(exception)).to eq("https://github.com/rubygems/rubygems/search?q=First+line+of+the+exception+message&type=Issues")
+ expect(Bundler::FriendlyErrors.issues_url(exception)).to eq("https://github.com/ruby/rubygems/search?q=First+line+of+the+exception+message&type=Issues")
end
it "generates the url without colons" do
@@ -218,7 +215,7 @@ Exception ::: with ::: colons :::
END
issues_url = Bundler::FriendlyErrors.issues_url(exception)
expect(issues_url).not_to include("%3A")
- expect(issues_url).to eq("https://github.com/rubygems/rubygems/search?q=#{CGI.escape("Exception with colons ")}&type=Issues")
+ expect(issues_url).to eq("https://github.com/ruby/rubygems/search?q=#{CGI.escape("Exception with colons ")}&type=Issues")
end
it "removes information after - for Errono::EACCES" do
@@ -228,7 +225,7 @@ END
allow(exception).to receive(:is_a?).with(Errno).and_return(true)
issues_url = Bundler::FriendlyErrors.issues_url(exception)
expect(issues_url).not_to include("/Users/foo/bar")
- expect(issues_url).to eq("https://github.com/rubygems/rubygems/search?q=#{CGI.escape("Errno EACCES Permission denied @ dir_s_mkdir ")}&type=Issues")
+ expect(issues_url).to eq("https://github.com/ruby/rubygems/search?q=#{CGI.escape("Errno EACCES Permission denied @ dir_s_mkdir ")}&type=Issues")
end
end
end
diff --git a/spec/bundler/bundler/gem_helper_spec.rb b/spec/bundler/bundler/gem_helper_spec.rb
index 94f66537d3..b4ae2abdc5 100644
--- a/spec/bundler/bundler/gem_helper_spec.rb
+++ b/spec/bundler/bundler/gem_helper_spec.rb
@@ -9,8 +9,12 @@ RSpec.describe Bundler::GemHelper do
let(:app_gemspec_path) { app_path.join("#{app_name}.gemspec") }
before(:each) do
- global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "false", "BUNDLE_GEM__LINTER" => "false",
- "BUNDLE_GEM__CI" => "false", "BUNDLE_GEM__CHANGELOG" => "false"
+ bundle_config_global "gem.mit false"
+ bundle_config_global "gem.test false"
+ bundle_config_global "gem.coc false"
+ bundle_config_global "gem.linter false"
+ bundle_config_global "gem.ci false"
+ bundle_config_global "gem.changelog false"
git("config --global init.defaultBranch main")
bundle "gem #{app_name}"
prepare_gemspec(app_gemspec_path)
@@ -222,7 +226,7 @@ RSpec.describe Bundler::GemHelper do
mock_confirm_message "#{app_name} (#{app_version}) installed."
subject.install_gem(nil, :local)
expect(app_gem_path).to exist
- gem_command :list
+ installed_gems_list
expect(out).to include("#{app_name} (#{app_version})")
end
end
@@ -386,6 +390,7 @@ RSpec.describe Bundler::GemHelper do
credentials = double("credentials", "file?" => true)
allow(Bundler.user_home).to receive(:join).
with(".gem/credentials").and_return(credentials)
+ allow(Bundler.user_home).to receive(:join).and_call_original
end
describe "success messaging" do
diff --git a/spec/bundler/bundler/gem_version_promoter_spec.rb b/spec/bundler/bundler/gem_version_promoter_spec.rb
index 917daba95d..0e1b7c9cc8 100644
--- a/spec/bundler/bundler/gem_version_promoter_spec.rb
+++ b/spec/bundler/bundler/gem_version_promoter_spec.rb
@@ -20,13 +20,13 @@ RSpec.describe Bundler::GemVersionPromoter do
end
end
- def build_package(name, version, locked = [])
- Bundler::Resolver::Package.new(name, [], locked_specs: Bundler::SpecSet.new(build_spec(name, version)), unlock: locked)
+ def build_package(name, version, unlock)
+ Bundler::Resolver::Package.new(name, [], locked_specs: Bundler::SpecSet.new(build_spec(name, version)), unlock: unlock)
end
- def sorted_versions(candidates:, current:, name: "foo", locked: [])
+ def sorted_versions(candidates:, current:, unlock: true)
gvp.sort_versions(
- build_package(name, current, locked),
+ build_package("foo", current, unlock),
build_candidates(candidates)
).flatten.map(&:version).map(&:to_s)
end
@@ -127,7 +127,7 @@ RSpec.describe Bundler::GemVersionPromoter do
before { gvp.level = :minor }
it "keeps the current version first" do
- versions = sorted_versions(candidates: %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.1.0 2.0.1], current: "0.3.0", locked: ["bar"])
+ versions = sorted_versions(candidates: %w[0.2.0 0.3.0 0.3.1 0.9.0 1.0.0 2.1.0 2.0.1], current: "0.3.0", unlock: [])
expect(versions.first).to eq("0.3.0")
end
end
diff --git a/spec/bundler/bundler/installer/gem_installer_spec.rb b/spec/bundler/bundler/installer/gem_installer_spec.rb
index 6583bd8181..dbd4e1d2c8 100644
--- a/spec/bundler/bundler/installer/gem_installer_spec.rb
+++ b/spec/bundler/bundler/installer/gem_installer_spec.rb
@@ -23,9 +23,7 @@ RSpec.describe Bundler::GemInstaller do
context "spec_settings is build option" do
it "invokes install method with build_args" do
- allow(Bundler.settings).to receive(:[]).with(:bin)
- allow(Bundler.settings).to receive(:[]).with(:inline)
- allow(Bundler.settings).to receive(:[]).with(:forget_cli_options)
+ allow(Bundler.settings).to receive(:[])
allow(Bundler.settings).to receive(:[]).with("build.dummy").and_return("--with-dummy-config=dummy")
expect(spec_source).to receive(:install).with(
spec,
@@ -37,9 +35,7 @@ RSpec.describe Bundler::GemInstaller do
context "spec_settings is build option with spaces" do
it "invokes install method with build_args" do
- allow(Bundler.settings).to receive(:[]).with(:bin)
- allow(Bundler.settings).to receive(:[]).with(:inline)
- allow(Bundler.settings).to receive(:[]).with(:forget_cli_options)
+ allow(Bundler.settings).to receive(:[])
allow(Bundler.settings).to receive(:[]).with("build.dummy").and_return("--with-dummy-config=dummy --with-another-dummy-config")
expect(spec_source).to receive(:install).with(
spec,
diff --git a/spec/bundler/bundler/installer/parallel_installer_spec.rb b/spec/bundler/bundler/installer/parallel_installer_spec.rb
new file mode 100644
index 0000000000..49bcb5310b
--- /dev/null
+++ b/spec/bundler/bundler/installer/parallel_installer_spec.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+require "bundler/installer/parallel_installer"
+require "bundler/rubygems_gem_installer"
+require "rubygems/remote_fetcher"
+require "bundler"
+
+RSpec.describe Bundler::ParallelInstaller do
+ describe "priority queue" do
+ before do
+ require "support/artifice/compact_index"
+
+ @previous_client = Gem::Request::ConnectionPools.client
+ Gem::Request::ConnectionPools.client = Gem::Net::HTTP
+ Gem::RemoteFetcher.fetcher.close_all
+
+ build_repo2 do
+ build_gem "gem_with_extension", &:add_c_extension
+ build_gem "gem_without_extension"
+ end
+
+ gemfile <<~G
+ source "https://gem.repo2"
+
+ gem "gem_with_extension"
+ gem "gem_without_extension"
+ G
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ gem_with_extension (1.0)
+ gem_without_extension (1.0)
+
+ DEPENDENCIES
+ gem_with_extension
+ gem_without_extension
+ L
+
+ @old_ui = Bundler.ui
+ Bundler.ui = Bundler::UI::Silent.new
+ end
+
+ after do
+ Bundler.ui = @old_ui
+ Gem::Request::ConnectionPools.client = @previous_client
+ Artifice.deactivate
+ end
+
+ let(:definition) do
+ allow(Bundler).to receive(:root) { bundled_app }
+
+ definition = Bundler::Definition.build(bundled_app.join("Gemfile"), bundled_app.join("Gemfile.lock"), false)
+ definition.tap(&:setup_domain!)
+ end
+ let(:installer) { Bundler::Installer.new(bundled_app, definition) }
+
+ it "queues native extensions in priority" do
+ parallel_installer = Bundler::ParallelInstaller.new(installer, definition.specs, 2, false, true)
+ worker_pool = parallel_installer.send(:worker_pool)
+ expected = 6 # Enqueue to download bundler and the 2 gems. Enqueue to install Bundler and the 2 gems.
+
+ expect(worker_pool).to receive(:enq).exactly(expected).times.and_wrap_original do |original_enq, spec, opts|
+ unless opts.nil? # Enqueued for download, no priority
+ if spec.name == "gem_with_extension"
+ expect(opts).to eq({ priority: true })
+ else
+ expect(opts).to eq({ priority: false })
+ end
+ end
+
+ opts ||= {}
+ original_enq.call(spec, **opts)
+ end
+
+ parallel_installer.call
+ end
+ end
+end
diff --git a/spec/bundler/bundler/installer/spec_installation_spec.rb b/spec/bundler/bundler/installer/spec_installation_spec.rb
index cbe2589b99..57868766d9 100644
--- a/spec/bundler/bundler/installer/spec_installation_spec.rb
+++ b/spec/bundler/bundler/installer/spec_installation_spec.rb
@@ -3,18 +3,17 @@
require "bundler/installer/parallel_installer"
RSpec.describe Bundler::ParallelInstaller::SpecInstallation do
- let!(:dep) do
- a_spec = Object.new
- def a_spec.name
- "I like tests"
- end
-
- def a_spec.full_name
- "I really like tests"
- end
- a_spec
+ def build_spec(name, extensions: [])
+ spec = Object.new
+ spec.define_singleton_method(:name) { name }
+ spec.define_singleton_method(:full_name) { "#{name}-1.0" }
+ spec.define_singleton_method(:extensions) { extensions }
+ spec.define_singleton_method(:dependencies) { [] }
+ spec
end
+ let!(:dep) { build_spec("I like tests") }
+
describe "#ready_to_enqueue?" do
context "when in enqueued state" do
it "is falsey" do
@@ -39,29 +38,51 @@ RSpec.describe Bundler::ParallelInstaller::SpecInstallation do
end
describe "#dependencies_installed?" do
- context "when all dependencies are installed" do
- it "returns true" do
- dependencies = []
- dependencies << instance_double("SpecInstallation", spec: "alpha", name: "alpha", installed?: true, all_dependencies: [], type: :production)
- dependencies << instance_double("SpecInstallation", spec: "beta", name: "beta", installed?: true, all_dependencies: [], type: :production)
- all_specs = dependencies + [instance_double("SpecInstallation", spec: "gamma", name: "gamma", installed?: false, all_dependencies: [], type: :production)]
+ it "returns true when all dependencies are installed" do
+ alpha = described_class.new(build_spec("alpha"))
+ alpha.dependencies = []
+
+ beta = described_class.new(build_spec("beta"))
+ beta.dependencies = [alpha]
+
+ gamma = described_class.new(build_spec("gamma"))
+ gamma.dependencies = [beta]
+
+ expect(gamma.dependencies_installed?({})).to be_falsey
+ expect(gamma.dependencies_installed?({ "beta" => true })).to be_falsey
+ expect(gamma.dependencies_installed?({ "alpha" => true, "beta" => true })).to be_truthy
+ end
+ end
+
+ describe "#ready_to_install?" do
+ context "when spec has no extensions" do
+ it "returns true regardless of dependencies" do
+ beta = described_class.new(build_spec("beta"))
+ beta.dependencies = []
+
spec = described_class.new(dep)
- allow(spec).to receive(:all_dependencies).and_return(dependencies)
- installed_specs = all_specs.select(&:installed?).map {|s| [s.name, true] }.to_h
- expect(spec.dependencies_installed?(installed_specs)).to be_truthy
+ spec.state = :downloaded
+ spec.dependencies = [beta]
+
+ expect(spec.ready_to_install?({})).to be_truthy
end
end
- context "when all dependencies are not installed" do
- it "returns false" do
- dependencies = []
- dependencies << instance_double("SpecInstallation", spec: "alpha", name: "alpha", installed?: false, all_dependencies: [], type: :production)
- dependencies << instance_double("SpecInstallation", spec: "beta", name: "beta", installed?: true, all_dependencies: [], type: :production)
- all_specs = dependencies + [instance_double("SpecInstallation", spec: "gamma", name: "gamma", installed?: false, all_dependencies: [], type: :production)]
- spec = described_class.new(dep)
- allow(spec).to receive(:all_dependencies).and_return(dependencies)
- installed_specs = all_specs.select(&:installed?).map {|s| [s.name, true] }.to_h
- expect(spec.dependencies_installed?(installed_specs)).to be_falsey
+ context "when spec has extensions" do
+ it "returns true when all dependencies are installed" do
+ alpha = described_class.new(build_spec("alpha"))
+ alpha.dependencies = []
+
+ beta = described_class.new(build_spec("beta"))
+ beta.dependencies = [alpha]
+
+ gamma = described_class.new(build_spec("gamma", extensions: ["ext/Rakefile"]))
+ gamma.state = :downloaded
+ gamma.dependencies = [beta]
+
+ expect(gamma.ready_to_install?({})).to be_falsey
+ expect(gamma.ready_to_install?({ "beta" => true })).to be_falsey
+ expect(gamma.ready_to_install?({ "alpha" => true, "beta" => true })).to be_truthy
end
end
end
diff --git a/spec/bundler/bundler/lockfile_parser_spec.rb b/spec/bundler/bundler/lockfile_parser_spec.rb
index 88932bf009..7364ab98e5 100644
--- a/spec/bundler/bundler/lockfile_parser_spec.rb
+++ b/spec/bundler/bundler/lockfile_parser_spec.rb
@@ -111,11 +111,11 @@ RSpec.describe Bundler::LockfileParser do
end
let(:specs) do
[
- Bundler::LazySpecification.new("peiji-san", v("1.2.0"), rb),
- Bundler::LazySpecification.new("rake", v("10.3.2"), rb),
+ Bundler::LazySpecification.new("peiji-san", v("1.2.0"), Gem::Platform::RUBY),
+ Bundler::LazySpecification.new("rake", v("10.3.2"), Gem::Platform::RUBY),
]
end
- let(:platforms) { [rb] }
+ let(:platforms) { [Gem::Platform::RUBY] }
let(:bundler_version) { Gem::Version.new("1.12.0.rc.2") }
let(:ruby_version) { "ruby 2.1.3p242" }
let(:lockfile_path) { Bundler.default_lockfile.relative_path_from(Dir.pwd) }
@@ -129,6 +129,7 @@ RSpec.describe Bundler::LockfileParser do
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
@@ -138,7 +139,7 @@ RSpec.describe Bundler::LockfileParser do
expect(subject.ruby_version).to eq ruby_version
rake_spec = specs.last
checksums = subject.sources.last.checksum_store.to_lock(specs.last)
- expect(checksums).to eq("#{rake_spec.name_tuple.lock_name} #{rake_checksums.map(&:to_lock).sort.join(",")}")
+ expect(checksums).to eq("#{rake_spec.lock_name} #{rake_checksums.map(&:to_lock).sort.join(",")}")
end
end
@@ -191,6 +192,87 @@ RSpec.describe Bundler::LockfileParser do
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 lockfile_path is given" do
+ it "uses the provided path in error messages instead of looking up Bundler.default_lockfile" do
+ expect(Bundler::SharedHelpers).not_to receive(:relative_lockfile_path)
+ parser = described_class.new(lockfile_contents, lockfile_path: "custom/path.lock")
+ expect(parser.valid?).to be(true)
+ rake_spec = parser.specs.last
+ checksums = parser.sources.last.checksum_store.to_lock(rake_spec)
+ expected_checksum = Bundler::Checksum.from_lock(
+ "sha256=814828c34f1315d7e7b7e8295184577cc4e969bad6156ac069d02d63f58d82e8",
+ "custom/path.lock:20:17"
+ )
+ expect(checksums).to eq("#{rake_spec.lock_name} #{expected_checksum.to_lock}")
+ end
+
+ it "raises with the provided path when the lockfile contains merge conflicts" do
+ expect do
+ described_class.new("<<<<<<<\n", lockfile_path: "custom/path.lock")
+ end.to raise_error(Bundler::LockfileError, %r{custom/path\.lock contains merge conflicts})
+ 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/spec/bundler/bundler/override_spec.rb b/spec/bundler/bundler/override_spec.rb
new file mode 100644
index 0000000000..ad8be75520
--- /dev/null
+++ b/spec/bundler/bundler/override_spec.rb
@@ -0,0 +1,175 @@
+# frozen_string_literal: true
+
+RSpec.describe "MatchMetadata override-aware checks" do
+ let(:spec_class) do
+ Class.new do
+ include Bundler::MatchMetadata
+ attr_accessor :name
+ def initialize(name, ruby_req, rubygems_req)
+ @name = name
+ @required_ruby_version = ruby_req
+ @required_rubygems_version = rubygems_req
+ end
+ end
+ end
+
+ it "matches_current_metadata? ignores overrides (strict path)" do
+ spec = spec_class.new("rails", Gem::Requirement.new("< #{Gem.ruby_version}"), Gem::Requirement.default)
+ overrides = [Bundler::Override.new("rails", :required_ruby_version, :ignore_upper)]
+ # Strict method MUST NOT apply overrides; guards SelfManager and other generic callers.
+ expect(spec.matches_current_metadata?).to be(false)
+ expect(spec.matches_current_metadata_with_overrides?(overrides)).to be(true)
+ end
+
+ it "matches_current_ruby_with_overrides? returns the strict result for an empty override list" do
+ spec = spec_class.new("rails", Gem::Requirement.new(">= #{Gem.ruby_version}"), Gem::Requirement.default)
+ expect(spec.matches_current_ruby_with_overrides?([])).to be(true)
+ expect(spec.matches_current_ruby_with_overrides?(nil)).to be(true)
+ end
+
+ it "matches_current_rubygems_with_overrides? honors :all override" do
+ spec = spec_class.new("rails", Gem::Requirement.default, Gem::Requirement.new("< #{Gem.rubygems_version}"))
+ overrides = [Bundler::Override.new(:all, :required_rubygems_version, :ignore_upper)]
+ expect(spec.matches_current_rubygems_with_overrides?(overrides)).to be(true)
+ end
+end
+
+RSpec.describe "LazySpecification override propagation" do
+ let(:overrides) { [Bundler::Override.new("rails", :required_ruby_version, :ignore_upper)] }
+
+ it "carries overrides forward from a source LazySpec via from_spec" do
+ src = Bundler::LazySpecification.new("rails", "8.0", Gem::Platform::RUBY)
+ src.overrides = overrides
+ derived = Bundler::LazySpecification.from_spec(src)
+ expect(derived.overrides).to eq(overrides)
+ end
+
+ it "does not call respond_to? on the source spec, avoiding gemspec lazy load" do
+ # If from_spec used respond_to?(:overrides), a RemoteSpec source would
+ # force-load the backing gemspec. Use a stand-in object whose
+ # respond_to? raises to prove it is never asked.
+ src = Object.new
+ src.define_singleton_method(:name) { "rails" }
+ src.define_singleton_method(:version) { Gem::Version.new("8.0") }
+ src.define_singleton_method(:platform) { Gem::Platform::RUBY }
+ src.define_singleton_method(:source) { nil }
+ src.define_singleton_method(:runtime_dependencies) { [] }
+ src.define_singleton_method(:required_ruby_version) { Gem::Requirement.default }
+ src.define_singleton_method(:required_rubygems_version) { Gem::Requirement.default }
+ src.define_singleton_method(:respond_to?) {|*| raise "from_spec must not call respond_to?" }
+ expect { Bundler::LazySpecification.from_spec(src) }.not_to raise_error
+ end
+end
+
+RSpec.describe Bundler::Override do
+ describe ".find_for" do
+ it "returns the matching override by target and field" do
+ a = described_class.new("rails", :version, ">= 8.0")
+ b = described_class.new("nokogiri", :version, :ignore_upper)
+ expect(described_class.find_for([a, b], "rails", :version)).to be(a)
+ end
+
+ it "returns nil when no override matches the target" do
+ a = described_class.new("rails", :version, ">= 8.0")
+ expect(described_class.find_for([a], "sinatra", :version)).to be_nil
+ end
+
+ it "returns nil when no override matches the field" do
+ a = described_class.new("rails", :version, ">= 8.0")
+ expect(described_class.find_for([a], "rails", :required_ruby_version)).to be_nil
+ end
+
+ it "returns nil for an empty overrides list" do
+ expect(described_class.find_for([], "rails", :version)).to be_nil
+ end
+
+ it "falls back to an :all override on the same field" do
+ a = described_class.new(:all, :required_ruby_version, :ignore_upper)
+ expect(described_class.find_for([a], "rails", :required_ruby_version)).to be(a)
+ end
+
+ it "prefers a per-gem override over a matching :all override" do
+ per_gem = described_class.new("rails", :required_ruby_version, ">= 3.4")
+ all_target = described_class.new(:all, :required_ruby_version, :ignore_upper)
+ expect(described_class.find_for([all_target, per_gem], "rails", :required_ruby_version)).to be(per_gem)
+ end
+
+ it "does not fall back to :all when the field differs" do
+ a = described_class.new(:all, :required_ruby_version, :ignore_upper)
+ expect(described_class.find_for([a], "rails", :required_rubygems_version)).to be_nil
+ end
+ end
+
+ describe "#apply_to" do
+ context "when operation is a version spec string" do
+ it "replaces the existing requirement entirely" do
+ override = described_class.new("rails", :version, ">= 8.0")
+ result = override.apply_to(Gem::Requirement.new(">= 1.0", "< 2.0"))
+ expect(result).to eq(Gem::Requirement.new(">= 8.0"))
+ end
+
+ it "ignores the existing requirement regardless of its content" do
+ override = described_class.new("rails", :version, "= 1.0")
+ result = override.apply_to(Gem::Requirement.new(">= 99.0"))
+ expect(result).to eq(Gem::Requirement.new("= 1.0"))
+ end
+ end
+
+ context "when operation is :ignore_upper" do
+ it "removes < and <= operators" do
+ override = described_class.new("rails", :version, :ignore_upper)
+ result = override.apply_to(Gem::Requirement.new(">= 1.0", "< 2.0"))
+ expect(result).to eq(Gem::Requirement.new(">= 1.0"))
+ end
+
+ it "keeps >, >=, = operators" do
+ override = described_class.new("rails", :version, :ignore_upper)
+ result = override.apply_to(Gem::Requirement.new("> 1.0", "<= 2.0"))
+ expect(result).to eq(Gem::Requirement.new("> 1.0"))
+ end
+
+ it "converts ~> to >= preserving the lower bound" do
+ override = described_class.new("rails", :version, :ignore_upper)
+ result = override.apply_to(Gem::Requirement.new("~> 1.5"))
+ expect(result).to eq(Gem::Requirement.new(">= 1.5"))
+ end
+
+ it "preserves != exclusion constraints" do
+ override = described_class.new("rails", :version, :ignore_upper)
+ result = override.apply_to(Gem::Requirement.new(">= 1.0", "!= 1.5.0", "< 2.0"))
+ expect(result).to eq(Gem::Requirement.new(">= 1.0", "!= 1.5.0"))
+ end
+
+ it "returns the default requirement when only upper bounds remain" do
+ override = described_class.new("rails", :version, :ignore_upper)
+ result = override.apply_to(Gem::Requirement.new("< 2.0"))
+ expect(result).to eq(Gem::Requirement.default)
+ end
+
+ it "returns the default requirement when the input is nil" do
+ override = described_class.new("rails", :version, :ignore_upper)
+ expect(override.apply_to(nil)).to eq(Gem::Requirement.default)
+ end
+
+ it "returns the default requirement when the input is already the default" do
+ override = described_class.new("rails", :version, :ignore_upper)
+ expect(override.apply_to(Gem::Requirement.default)).to eq(Gem::Requirement.default)
+ end
+ end
+
+ context "when operation is nil" do
+ it "returns the default requirement" do
+ override = described_class.new("rails", :version, nil)
+ result = override.apply_to(Gem::Requirement.new(">= 1.0", "< 2.0"))
+ expect(result).to eq(Gem::Requirement.default)
+ end
+ end
+
+ context "when operation is unsupported" do
+ it "raises ArgumentError" do
+ override = described_class.new("rails", :version, 42)
+ expect { override.apply_to(Gem::Requirement.default) }.to raise_error(ArgumentError, /unsupported override operation/)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/plugin/events_spec.rb b/spec/bundler/bundler/plugin/events_spec.rb
index 28d70c6fdd..77e5fdb74c 100644
--- a/spec/bundler/bundler/plugin/events_spec.rb
+++ b/spec/bundler/bundler/plugin/events_spec.rb
@@ -2,7 +2,17 @@
RSpec.describe Bundler::Plugin::Events do
context "plugin events" do
- before { Bundler::Plugin::Events.send :reset }
+ before do
+ @old_constants = Bundler::Plugin::Events.constants.map {|name| [name, Bundler::Plugin::Events.const_get(name)] }
+ Bundler::Plugin::Events.send :reset
+ end
+
+ after do
+ Bundler::Plugin::Events.send(:reset)
+ Hash[@old_constants].each do |name, value|
+ Bundler::Plugin::Events.send(:define, name, value)
+ end
+ end
describe "#define" do
it "raises when redefining a constant" do
diff --git a/spec/bundler/bundler/plugin/index_spec.rb b/spec/bundler/bundler/plugin/index_spec.rb
index 565fc9b088..a28934269b 100644
--- a/spec/bundler/bundler/plugin/index_spec.rb
+++ b/spec/bundler/bundler/plugin/index_spec.rb
@@ -193,4 +193,83 @@ RSpec.describe Bundler::Plugin::Index do
include_examples "it cleans up"
end
end
+
+ describe "relative plugin paths" do
+ let(:plugin_name) { "relative-plugin" }
+
+ before do
+ Bundler::Plugin.reset!
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+
+ plugin_root = Bundler::Plugin.root
+ FileUtils.mkdir_p(plugin_root)
+
+ path = plugin_root.join(plugin_name)
+ FileUtils.mkdir_p(path.join("lib"))
+
+ index.register_plugin(plugin_name, path.to_s, [path.join("lib").to_s], [], [], [])
+ end
+
+ it "stores plugin paths relative to the plugin root" do
+ require "yaml"
+ data = YAML.load_file(index.index_file)
+
+ expect(data["plugin_paths"][plugin_name]).to eq(plugin_name)
+ expect(data["load_paths"][plugin_name]).to eq([File.join(plugin_name, "lib")])
+ end
+
+ it "expands relative paths to absolute on load" do
+ require "bundler/yaml_serializer"
+
+ plugin_root = Bundler::Plugin.root
+
+ relative_index = {
+ "commands" => {},
+ "hooks" => {},
+ "load_paths" => { plugin_name => [File.join(plugin_name, "lib")] },
+ "plugin_paths" => { plugin_name => plugin_name },
+ "sources" => {},
+ }
+
+ File.open(index.index_file, "w") {|f| f.puts Bundler::YAMLSerializer.dump(relative_index) }
+
+ new_index = Index.new
+ expect(new_index.plugin_path(plugin_name)).to eq(plugin_root.join(plugin_name))
+ expect(new_index.load_paths(plugin_name)).to eq([plugin_root.join(plugin_name, "lib").to_s])
+ end
+
+ it "keeps paths outside the plugin root as absolute" do
+ outside_path = tmp.join("outside", "external-plugin")
+ FileUtils.mkdir_p(outside_path.join("lib"))
+
+ index.register_plugin("external-plugin", outside_path.to_s, [outside_path.join("lib").to_s], [], [], [])
+
+ require "yaml"
+ data = YAML.load_file(index.index_file)
+
+ expect(data["plugin_paths"]["external-plugin"]).to eq(outside_path.to_s)
+ expect(data["load_paths"]["external-plugin"]).to eq([outside_path.join("lib").to_s])
+ end
+
+ it "reads legacy index files with absolute paths" do
+ require "bundler/yaml_serializer"
+
+ plugin_root = Bundler::Plugin.root
+ absolute_path = plugin_root.join(plugin_name).to_s
+
+ legacy_index = {
+ "commands" => {},
+ "hooks" => {},
+ "load_paths" => { plugin_name => [File.join(absolute_path, "lib")] },
+ "plugin_paths" => { plugin_name => absolute_path },
+ "sources" => {},
+ }
+
+ File.open(index.index_file, "w") {|f| f.puts Bundler::YAMLSerializer.dump(legacy_index) }
+
+ new_index = Index.new
+ expect(new_index.plugin_path(plugin_name)).to eq(Pathname.new(absolute_path))
+ expect(new_index.load_paths(plugin_name)).to eq([File.join(absolute_path, "lib")])
+ end
+ end
end
diff --git a/spec/bundler/bundler/plugin/installer_spec.rb b/spec/bundler/bundler/plugin/installer_spec.rb
index 8e1879395a..c200a98afa 100644
--- a/spec/bundler/bundler/plugin/installer_spec.rb
+++ b/spec/bundler/bundler/plugin/installer_spec.rb
@@ -47,6 +47,13 @@ RSpec.describe Bundler::Plugin::Installer do
build_plugin "re-plugin"
build_plugin "ma-plugin"
end
+
+ @previous_ui = Bundler.ui
+ Bundler.ui = Bundler::UI::Silent.new
+ end
+
+ after do
+ Bundler.ui = @previous_ui
end
context "git plugins" do
diff --git a/spec/bundler/bundler/plugin_spec.rb b/spec/bundler/bundler/plugin_spec.rb
index fea3925000..b379594c6f 100644
--- a/spec/bundler/bundler/plugin_spec.rb
+++ b/spec/bundler/bundler/plugin_spec.rb
@@ -65,8 +65,8 @@ RSpec.describe Bundler::Plugin do
end
it "passes the name and options to installer" do
- allow(index).to receive(:installed?).
- with("new-plugin")
+ allow(index).to receive(:up_to_date?).
+ with(spec)
allow(installer).to receive(:install).with(["new-plugin"], opts) do
{ "new-plugin" => spec }
end.once
@@ -75,8 +75,8 @@ RSpec.describe Bundler::Plugin do
end
it "validates the installed plugin" do
- allow(index).to receive(:installed?).
- with("new-plugin")
+ allow(index).to receive(:up_to_date?).
+ with(spec)
allow(subject).
to receive(:validate_plugin!).with(lib_path("new-plugin")).once
@@ -84,8 +84,8 @@ RSpec.describe Bundler::Plugin do
end
it "registers the plugin with index" do
- allow(index).to receive(:installed?).
- with("new-plugin")
+ allow(index).to receive(:up_to_date?).
+ with(spec)
allow(index).to receive(:register_plugin).
with("new-plugin", lib_path("new-plugin").to_s, [lib_path("new-plugin").join("lib").to_s], []).once
subject.install ["new-plugin"], opts
@@ -102,7 +102,7 @@ RSpec.describe Bundler::Plugin do
end.once
allow(subject).to receive(:validate_plugin!).twice
- allow(index).to receive(:installed?).twice
+ allow(index).to receive(:up_to_date?).twice
allow(index).to receive(:register_plugin).twice
subject.install ["new-plugin", "another-plugin"], opts
end
@@ -138,7 +138,7 @@ RSpec.describe Bundler::Plugin do
end
before do
- allow(index).to receive(:installed?) { nil }
+ allow(index).to receive(:up_to_date?) { nil }
allow(definition).to receive(:dependencies) { [Bundler::Dependency.new("new-plugin", ">=0"), Bundler::Dependency.new("another-plugin", ">=0")] }
allow(installer).to receive(:install_definition) { plugin_specs }
end
@@ -279,6 +279,7 @@ RSpec.describe Bundler::Plugin do
s.write "plugins.rb", code
end
+ @old_constants = Bundler::Plugin::Events.constants.map {|name| [name, Bundler::Plugin::Events.const_get(name)] }
Bundler::Plugin::Events.send(:reset)
Bundler::Plugin::Events.send(:define, :EVENT1, "event-1")
Bundler::Plugin::Events.send(:define, :EVENT2, "event-2")
@@ -291,6 +292,13 @@ RSpec.describe Bundler::Plugin do
allow(index).to receive(:load_paths).with("foo-plugin").and_return([])
end
+ after do
+ Bundler::Plugin::Events.send(:reset)
+ Hash[@old_constants].each do |name, value|
+ Bundler::Plugin::Events.send(:define, name, value)
+ end
+ end
+
let(:code) { <<-RUBY }
Bundler::Plugin::API.hook("event-1") { puts "hook for event 1" }
RUBY
diff --git a/spec/bundler/bundler/resolver/cooldown_spec.rb b/spec/bundler/bundler/resolver/cooldown_spec.rb
new file mode 100644
index 0000000000..37ec158cba
--- /dev/null
+++ b/spec/bundler/bundler/resolver/cooldown_spec.rb
@@ -0,0 +1,148 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::Resolver do
+ let(:resolver) { described_class.allocate }
+
+ def remote(cooldown:)
+ instance_double(Bundler::Source::Rubygems::Remote, effective_cooldown: cooldown)
+ end
+
+ def spec(created_at:, remote:, name: "myrack", version: "1.0.0")
+ Struct.new(:name, :version, :created_at, :remote).new(name, Gem::Version.new(version), created_at, remote)
+ end
+
+ describe "#filter_cooldown" do
+ let(:now) { Time.now }
+
+ context "with a 7-day cooldown" do
+ let(:r) { remote(cooldown: 7) }
+
+ it "rejects versions published within the window" do
+ recent = spec(version: "1.1.0", created_at: now - (2 * 86_400), remote: r)
+ old = spec(version: "1.0.0", created_at: now - (30 * 86_400), remote: r)
+
+ expect(resolver.send(:filter_cooldown, [recent, old])).to eq([old])
+ end
+
+ it "keeps versions published exactly at the threshold" do
+ boundary = spec(created_at: now - (7 * 86_400), remote: r)
+
+ expect(resolver.send(:filter_cooldown, [boundary])).to eq([boundary])
+ end
+
+ it "leaves rolling-delay history intact" do
+ # 7-day cooldown with frequent releases must still expose an older candidate.
+ in_cooldown = spec(version: "1.2.0", created_at: now - 86_400, remote: r)
+ also_in_cooldown = spec(version: "1.1.0", created_at: now - (3 * 86_400), remote: r)
+ eligible = spec(version: "1.0.0", created_at: now - (10 * 86_400), remote: r)
+
+ result = resolver.send(:filter_cooldown, [in_cooldown, also_in_cooldown, eligible])
+
+ expect(result).to eq([eligible])
+ end
+
+ it "drops every spec sharing an excluded [name, version] tuple" do
+ # The cooldown check is by version, not per-spec: a StubSpecification for an
+ # in-cooldown release would otherwise slip through on local install paths.
+ endpoint = spec(version: "2.0.0", created_at: now - 86_400, remote: r)
+ local_stub = Struct.new(:name, :version).new("myrack", Gem::Version.new("2.0.0"))
+ eligible = spec(version: "1.0.0", created_at: now - (30 * 86_400), remote: r)
+
+ result = resolver.send(:filter_cooldown, [endpoint, local_stub, eligible])
+
+ expect(result).to eq([eligible])
+ end
+
+ it "keeps stub-only versions that no endpoint marks as in cooldown" do
+ # If no remote spec carries created_at for a version, cooldown cannot judge it;
+ # the stub stays in.
+ local_only = Struct.new(:name, :version).new("myrack", Gem::Version.new("2.0.0"))
+ eligible = spec(version: "1.0.0", created_at: now - (30 * 86_400), remote: r)
+
+ result = resolver.send(:filter_cooldown, [local_only, eligible])
+
+ expect(result).to eq([local_only, eligible])
+ end
+ end
+
+ context "when created_at is missing (blank metadata)" do
+ it "keeps the spec regardless of cooldown" do
+ s = spec(created_at: nil, remote: remote(cooldown: 7))
+
+ expect(resolver.send(:filter_cooldown, [s])).to eq([s])
+ end
+ end
+
+ context "when the remote has no cooldown" do
+ it "keeps every spec" do
+ s = spec(created_at: now - 3600, remote: remote(cooldown: nil))
+
+ expect(resolver.send(:filter_cooldown, [s])).to eq([s])
+ end
+ end
+
+ context "when cooldown is 0" do
+ it "keeps every spec (escape hatch)" do
+ s = spec(created_at: now - 3600, remote: remote(cooldown: 0))
+
+ expect(resolver.send(:filter_cooldown, [s])).to eq([s])
+ end
+ end
+
+ context "when the spec does not respond to created_at" do
+ it "keeps the spec" do
+ bare = Struct.new(:version).new("1.0.0")
+
+ expect(resolver.send(:filter_cooldown, [bare])).to eq([bare])
+ end
+ end
+
+ context "when the spec has no remote" do
+ it "keeps the spec" do
+ s = spec(created_at: now - 86_400, remote: nil)
+
+ expect(resolver.send(:filter_cooldown, [s])).to eq([s])
+ end
+ end
+
+ it "returns the same array when input is empty" do
+ expect(resolver.send(:filter_cooldown, [])).to eq([])
+ end
+ end
+
+ describe "#cooldown_hint" do
+ let(:now) { Time.now }
+ let(:r) { remote(cooldown: 7) }
+
+ it "returns nil when no spec is excluded" do
+ expect(resolver.send(:cooldown_hint, [])).to be_nil
+ end
+
+ it "returns nil when every spec is outside the cooldown window" do
+ eligible = [spec(created_at: now - (30 * 86_400), remote: r)]
+
+ expect(resolver.send(:cooldown_hint, eligible)).to be_nil
+ end
+
+ it "mentions the count and the bypass flag for one excluded version" do
+ excluded = [spec(created_at: now - 86_400, remote: r)]
+
+ hint = resolver.send(:cooldown_hint, excluded)
+
+ expect(hint).to match(/1 version excluded by the cooldown setting/)
+ expect(hint).to match(/--cooldown 0/)
+ end
+
+ it "uses plural wording when multiple versions are excluded" do
+ excluded = %w[1.0.0 1.1.0 1.2.0].map {|v| spec(version: v, created_at: now - 86_400, remote: r) }
+
+ expect(resolver.send(:cooldown_hint, excluded)).to match(/3 versions excluded/)
+ end
+
+ it "counts each unique version once even when multiple spec instances share it" do
+ duplicates = Array.new(3) { spec(created_at: now - 86_400, remote: r) }
+
+ expect(resolver.send(:cooldown_hint, duplicates)).to match(/1 version excluded/)
+ end
+ end
+end
diff --git a/spec/bundler/bundler/retry_spec.rb b/spec/bundler/bundler/retry_spec.rb
index ffbc078074..5c84d0bea5 100644
--- a/spec/bundler/bundler/retry_spec.rb
+++ b/spec/bundler/bundler/retry_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe Bundler::Retry do
end
it "returns the first valid result" do
- jobs = [proc { raise "foo" }, proc { :bar }, proc { raise "foo" }]
+ jobs = [proc { raise "job 1 failed" }, proc { :bar }, proc { raise "job 2 failed" }]
attempts = 0
result = Bundler::Retry.new(nil, nil, 3).attempt do
attempts += 1
@@ -78,4 +78,113 @@ RSpec.describe Bundler::Retry do
end
end
end
+
+ context "exponential backoff" do
+ it "can be disabled by setting base_delay to 0" do
+ attempts = 0
+ expect do
+ Bundler::Retry.new("test", [], 2, base_delay: 0).attempt do
+ attempts += 1
+ raise "error"
+ end
+ end.to raise_error(StandardError)
+
+ # Verify no sleep was called (implicitly - if sleep was called, timing would be different)
+ expect(attempts).to eq(3)
+ end
+
+ it "is enabled by default with 1 second base delay" do
+ original_base_delay = Bundler::Retry.default_base_delay
+ Bundler::Retry.default_base_delay = 1.0
+
+ attempts = 0
+ sleep_times = []
+
+ allow_any_instance_of(Bundler::Retry).to receive(:sleep) do |_instance, delay|
+ sleep_times << delay
+ end
+
+ expect do
+ Bundler::Retry.new("test", [], 2, jitter: 0).attempt do
+ attempts += 1
+ raise "error"
+ end
+ end.to raise_error(StandardError)
+
+ expect(attempts).to eq(3)
+ expect(sleep_times.length).to eq(2)
+ # First retry: 1.0 * 2^0 = 1.0
+ expect(sleep_times[0]).to eq(1.0)
+ # Second retry: 1.0 * 2^1 = 2.0
+ expect(sleep_times[1]).to eq(2.0)
+ ensure
+ Bundler::Retry.default_base_delay = original_base_delay
+ end
+
+ it "sleeps with exponential backoff when base_delay is set" do
+ attempts = 0
+ sleep_times = []
+
+ allow_any_instance_of(Bundler::Retry).to receive(:sleep) do |_instance, delay|
+ sleep_times << delay
+ end
+
+ expect do
+ Bundler::Retry.new("test", [], 2, base_delay: 1.0, jitter: 0).attempt do
+ attempts += 1
+ raise "error"
+ end
+ end.to raise_error(StandardError)
+
+ expect(attempts).to eq(3)
+ expect(sleep_times.length).to eq(2)
+ # First retry: 1.0 * 2^0 = 1.0
+ expect(sleep_times[0]).to eq(1.0)
+ # Second retry: 1.0 * 2^1 = 2.0
+ expect(sleep_times[1]).to eq(2.0)
+ end
+
+ it "respects max_delay" do
+ sleep_times = []
+
+ allow_any_instance_of(Bundler::Retry).to receive(:sleep) do |_instance, delay|
+ sleep_times << delay
+ end
+
+ expect do
+ Bundler::Retry.new("test", [], 3, base_delay: 10.0, max_delay: 15.0, jitter: 0).attempt do
+ raise "error"
+ end
+ end.to raise_error(StandardError)
+
+ # First retry: 10.0 * 2^0 = 10.0
+ expect(sleep_times[0]).to eq(10.0)
+ # Second retry: 10.0 * 2^1 = 20.0, capped at 15.0
+ expect(sleep_times[1]).to eq(15.0)
+ # Third retry: 10.0 * 2^2 = 40.0, capped at 15.0
+ expect(sleep_times[2]).to eq(15.0)
+ end
+
+ it "adds jitter to delay" do
+ sleep_times = []
+
+ allow_any_instance_of(Bundler::Retry).to receive(:sleep) do |_instance, delay|
+ sleep_times << delay
+ end
+
+ expect do
+ Bundler::Retry.new("test", [], 2, base_delay: 1.0, jitter: 0.5).attempt do
+ raise "error"
+ end
+ end.to raise_error(StandardError)
+
+ expect(sleep_times.length).to eq(2)
+ # First retry should be between 1.0 and 1.5 (base + jitter)
+ expect(sleep_times[0]).to be >= 1.0
+ expect(sleep_times[0]).to be <= 1.5
+ # Second retry should be between 2.0 and 2.5
+ expect(sleep_times[1]).to be >= 2.0
+ expect(sleep_times[1]).to be <= 2.5
+ end
+ end
end
diff --git a/spec/bundler/bundler/ruby_dsl_spec.rb b/spec/bundler/bundler/ruby_dsl_spec.rb
index 2607f746e7..45a37c5795 100644
--- a/spec/bundler/bundler/ruby_dsl_spec.rb
+++ b/spec/bundler/bundler/ruby_dsl_spec.rb
@@ -178,11 +178,34 @@ RSpec.describe Bundler::RubyDsl do
let(:file_content) do
<<~TOML
[tools]
- ruby = "#{version}"
+ ruby = #{quote}#{version}#{quote}
TOML
end
- it_behaves_like "it stores the ruby version"
+ context "with double quotes" do
+ let(:quote) { '"' }
+
+ it_behaves_like "it stores the ruby version"
+ end
+
+ context "with single quotes" do
+ let(:quote) { "'" }
+
+ it_behaves_like "it stores the ruby version"
+ end
+
+ context "with mismatched quotes" do
+ let(:file_content) do
+ <<~TOML
+ [tools]
+ ruby = "#{version}'
+ TOML
+ end
+
+ it "raises an error" do
+ expect { subject }.to raise_error(Bundler::InvalidArgumentError, "= is not a valid requirement on the Ruby version")
+ end
+ end
end
context "with a .tool-versions file format" do
@@ -210,6 +233,16 @@ RSpec.describe Bundler::RubyDsl do
it_behaves_like "it stores the ruby version"
end
end
+
+ context "when the file does not exist" do
+ let(:ruby_version_file_path) { nil }
+ let(:ruby_version_arg) { nil }
+ let(:file) { "nonexistent.txt" }
+
+ it "raises an error" do
+ expect { subject }.to raise_error(Bundler::GemfileError, /Could not find version file nonexistent.txt/)
+ end
+ end
end
end
end
diff --git a/spec/bundler/bundler/ruby_version_spec.rb b/spec/bundler/bundler/ruby_version_spec.rb
index 39d0571361..0d41ec9901 100644
--- a/spec/bundler/bundler/ruby_version_spec.rb
+++ b/spec/bundler/bundler/ruby_version_spec.rb
@@ -100,7 +100,7 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
describe "#to_s" do
it "should return info string with the ruby version, patchlevel, engine, and engine version" do
- expect(subject.to_s).to eq("ruby 2.0.0p645 (jruby 2.0.1)")
+ expect(subject.to_s).to eq("ruby 2.0.0 (jruby 2.0.1)")
end
context "no patchlevel" do
@@ -115,7 +115,7 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
let(:engine) { "ruby" }
it "should return info string with the ruby version and patchlevel" do
- expect(subject.to_s).to eq("ruby 2.0.0p645")
+ expect(subject.to_s).to eq("ruby 2.0.0")
end
end
@@ -137,7 +137,7 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
end
end
- context "the versions, pathlevels, engines, and engine_versions match" do
+ shared_examples_for "the versions, engines, and engine_versions match" do
it "should return true" do
expect(subject).to eq(other_ruby_version)
end
@@ -152,7 +152,7 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
context "the patchlevels do not match" do
let(:other_patchlevel) { "21" }
- it_behaves_like "two ruby versions are not equal"
+ it_behaves_like "the versions, engines, and engine_versions match"
end
context "the engines do not match" do
@@ -228,9 +228,9 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
end
end
- shared_examples_for "there is a difference in the patchlevels" do
- it "should return a tuple with :patchlevel and the two different patchlevels" do
- expect(ruby_version.diff(other_ruby_version)).to eq([:patchlevel, patchlevel, other_patchlevel])
+ shared_examples_for "even there is a difference in the patchlevels" do
+ it "should return nil" do
+ expect(ruby_version.diff(other_ruby_version)).to be_nil
end
end
@@ -287,10 +287,10 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
it_behaves_like "there is a difference in the engine versions"
end
- context "detects patchlevel discrepancies last" do
+ context "ignores patchlevel discrepancies last" do
let(:other_patchlevel) { "643" }
- it_behaves_like "there is a difference in the patchlevels"
+ it_behaves_like "even there is a difference in the patchlevels"
end
context "successfully matches gem requirements" do
@@ -355,7 +355,7 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
let(:other_engine) { "ruby" }
let(:other_engine_version) { "2.0.5" }
- it_behaves_like "there is a difference in the patchlevels"
+ it_behaves_like "even there is a difference in the patchlevels"
end
context "successfully detects bad gem requirements with engine versions" do
@@ -389,7 +389,7 @@ RSpec.describe "Bundler::RubyVersion and its subclasses" do
context "and comparing with a patchlevel that is not -1" do
let(:other_patchlevel) { "642" }
- it_behaves_like "there is a difference in the patchlevels"
+ it_behaves_like "even there is a difference in the patchlevels"
end
end
end
diff --git a/spec/bundler/bundler/rubygems_ext_spec.rb b/spec/bundler/bundler/rubygems_ext_spec.rb
new file mode 100644
index 0000000000..0fc528f78c
--- /dev/null
+++ b/spec/bundler/bundler/rubygems_ext_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require "bundler/rubygems_ext"
+
+RSpec.describe Gem::SplitCompactIndexEntryOnFirstColon do
+ # Reproduces the RubyGems < 4.0.13 `Gem::Resolver::APISet::GemParser` that
+ # split each compact index entry on every colon, corrupting metadata values
+ # that themselves contain colons.
+ let(:legacy_parser_class) do
+ Class.new do
+ def parse_dependency(string)
+ dependency = string.split(":")
+ dependency[-1] = dependency[-1].split("&") if dependency.size > 1
+ dependency[0] = -dependency[0]
+ dependency
+ end
+ end
+ end
+
+ before { legacy_parser_class.prepend(described_class) }
+
+ it "preserves colon-bearing metadata values such as created_at timestamps" do
+ parser = legacy_parser_class.new
+
+ expect(parser.send(:parse_dependency, "created_at:2026-05-12T10:00:00Z")).to eq(["created_at", ["2026-05-12T10:00:00Z"]])
+ end
+
+ it "still parses ordinary name:requirement entries" do
+ parser = legacy_parser_class.new
+
+ expect(parser.send(:parse_dependency, "myrack:>= 1.0")).to eq(["myrack", [">= 1.0"]])
+ end
+
+ it "keeps parse_dependency private" do
+ parser = legacy_parser_class.new
+
+ expect { parser.parse_dependency("created_at:x") }.to raise_error(NoMethodError, /private method/)
+ end
+end
diff --git a/spec/bundler/bundler/settings_spec.rb b/spec/bundler/bundler/settings_spec.rb
index 7c64c1c907..5e1aaaa555 100644
--- a/spec/bundler/bundler/settings_spec.rb
+++ b/spec/bundler/bundler/settings_spec.rb
@@ -119,6 +119,11 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
settings.set_local :ssl_verify_mode, "1"
expect(settings[:ssl_verify_mode]).to be 1
end
+
+ it "coerces cooldown to integer" do
+ settings.set_local :cooldown, "7"
+ expect(settings[:cooldown]).to be 7
+ end
end
context "when it's not possible to create the settings directory" do
@@ -200,7 +205,7 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
end
context "with a configured mirror" do
- let(:mirror_uri) { Gem::URI("https://rubygems-mirror.org/") }
+ let(:mirror_uri) { Gem::URI("https://example-mirror.rubygems.org/") }
before { settings.set_local "mirror.https://rubygems.org/", mirror_uri.to_s }
@@ -277,12 +282,12 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
end
it "normalizes HTTP URIs in mirror configuration" do
- settings.set_local "mirror.http://rubygems.org", "http://rubygems-mirror.org"
+ settings.set_local "mirror.http://rubygems.org", "http://example-mirror.rubygems.org"
expect(settings.all).to include("mirror.http://rubygems.org/")
end
it "normalizes HTTPS URIs in mirror configuration" do
- settings.set_local "mirror.https://rubygems.org", "http://rubygems-mirror.org"
+ settings.set_local "mirror.https://rubygems.org", "http://example-mirror.rubygems.org"
expect(settings.all).to include("mirror.https://rubygems.org/")
end
@@ -297,9 +302,9 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
end
it "reads older keys without trailing slashes" do
- settings.set_local "mirror.https://rubygems.org", "http://rubygems-mirror.org"
+ settings.set_local "mirror.https://rubygems.org", "http://example-mirror.rubygems.org"
expect(settings.mirror_for("https://rubygems.org/")).to eq(
- Gem::URI("http://rubygems-mirror.org/")
+ Gem::URI("http://example-mirror.rubygems.org/")
)
end
@@ -318,13 +323,13 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
let(:settings) { described_class.new(bundled_app(".bundle")) }
it "converts older keys without double underscore" do
- config("BUNDLE_MY__PERSONAL.MYRACK" => "~/Work/git/myrack")
+ bundle_config("BUNDLE_MY__PERSONAL.MYRACK" => "~/Work/git/myrack")
expect(settings["my.personal.myrack"]).to eq("~/Work/git/myrack")
end
it "converts older keys without trailing slashes and double underscore" do
- config("BUNDLE_MIRROR__HTTPS://RUBYGEMS.ORG" => "http://rubygems-mirror.org")
- expect(settings["mirror.https://rubygems.org/"]).to eq("http://rubygems-mirror.org")
+ bundle_config("BUNDLE_MIRROR__HTTPS://RUBYGEMS.ORG" => "http://example-mirror.rubygems.org")
+ expect(settings["mirror.https://rubygems.org/"]).to eq("http://example-mirror.rubygems.org")
end
it "ignores commented out keys" do
@@ -337,7 +342,7 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
end
it "converts older keys with dashes" do
- config("BUNDLE_MY-PERSONAL-SERVER__ORG" => "my-personal-server.org")
+ bundle_config("BUNDLE_MY-PERSONAL-SERVER__ORG" => "my-personal-server.org")
expect(Bundler.ui).to receive(:warn).with(
"Your #{bundled_app(".bundle/config")} config includes `BUNDLE_MY-PERSONAL-SERVER__ORG`, which contains the dash character (`-`).\n" \
"This is deprecated, because configuration through `ENV` should be possible, but `ENV` keys cannot include dashes.\n" \
@@ -347,8 +352,29 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
end
it "reads newer keys format properly" do
- config("BUNDLE_MIRROR__HTTPS://RUBYGEMS__ORG/" => "http://rubygems-mirror.org")
- expect(settings["mirror.https://rubygems.org/"]).to eq("http://rubygems-mirror.org")
+ bundle_config("BUNDLE_MIRROR__HTTPS://RUBYGEMS__ORG/" => "http://example-mirror.rubygems.org")
+ expect(settings["mirror.https://rubygems.org/"]).to eq("http://example-mirror.rubygems.org")
+ end
+ end
+
+ describe "default_cli_command validation" do
+ it "accepts 'install' as a valid value" do
+ expect { settings.set_local("default_cli_command", "install") }.not_to raise_error
+ end
+
+ it "accepts 'cli_help' as a valid value" do
+ expect { settings.set_local("default_cli_command", "cli_help") }.not_to raise_error
+ end
+
+ it "rejects invalid values" do
+ expect { settings.set_local("default_cli_command", "invalid") }.to raise_error(
+ Bundler::InvalidOption,
+ /Setting `default_cli_command` to "invalid" failed:\n - default_cli_command must be either 'install' or 'cli_help'\n - must be one of: install, cli_help/
+ )
+ end
+
+ it "accepts nil values" do
+ expect { settings.set_local("default_cli_command", nil) }.not_to raise_error
end
end
end
diff --git a/spec/bundler/bundler/shared_helpers_spec.rb b/spec/bundler/bundler/shared_helpers_spec.rb
index 6db8cd6783..41115aa667 100644
--- a/spec/bundler/bundler/shared_helpers_spec.rb
+++ b/spec/bundler/bundler/shared_helpers_spec.rb
@@ -59,7 +59,7 @@ RSpec.describe Bundler::SharedHelpers do
before { allow(subject).to receive(:default_gemfile).and_return(gemfile_path) }
- it "returns the lock file path" do
+ it "returns the lockfile path" do
expect(subject.default_lockfile).to eq(expected_lockfile_path)
end
end
@@ -159,7 +159,7 @@ RSpec.describe Bundler::SharedHelpers do
let(:pwd_stub) { nil }
it "returns the current absolute path" do
- expect(subject.pwd).to eq(source_root)
+ expect(subject.pwd).to eq(git_root.to_s)
end
end
@@ -354,7 +354,7 @@ RSpec.describe Bundler::SharedHelpers do
it "ENV['PATH'] should only contain one instance of bundle bin path" do
subject.set_bundle_environment
- paths = (ENV["PATH"]).split(File::PATH_SEPARATOR)
+ paths = ENV["PATH"].split(File::PATH_SEPARATOR)
expect(paths.count(bundle_path)).to eq(1)
end
end
@@ -423,7 +423,7 @@ RSpec.describe Bundler::SharedHelpers do
it "sets BUNDLE_BIN_PATH to the bundle executable file" do
subject.set_bundle_environment
bin_path = ENV["BUNDLE_BIN_PATH"]
- expect(bin_path).to eq(bindir.join("bundle").to_s)
+ expect(bin_path).to eq(exedir.join("bundle").to_s)
expect(File.exist?(bin_path)).to be true
end
end
@@ -515,34 +515,4 @@ RSpec.describe Bundler::SharedHelpers do
end
end
end
-
- describe "#major_deprecation" do
- before { allow(Bundler).to receive(:bundler_major_version).and_return(37) }
- before { allow(Bundler.ui).to receive(:warn) }
-
- it "prints and raises nothing below the deprecated major version" do
- subject.major_deprecation(38, "Message")
- subject.major_deprecation(39, "Message", removed_message: "Removal", print_caller_location: true)
- expect(Bundler.ui).not_to have_received(:warn)
- end
-
- it "prints but does not raise _at_ the deprecated major version" do
- subject.major_deprecation(37, "Message")
- subject.major_deprecation(37, "Message", removed_message: "Removal")
- expect(Bundler.ui).to have_received(:warn).with("[DEPRECATED] Message").twice
-
- subject.major_deprecation(37, "Message", print_caller_location: true)
- expect(Bundler.ui).to have_received(:warn).
- with(a_string_matching(/^\[DEPRECATED\] Message \(called at .*:\d+\)$/))
- end
-
- it "raises the appropriate errors when _past_ the deprecated major version" do
- expect { subject.major_deprecation(36, "Message") }.
- to raise_error(Bundler::DeprecatedError, "[REMOVED] Message")
- expect { subject.major_deprecation(36, "Message", removed_message: "Removal") }.
- to raise_error(Bundler::DeprecatedError, "[REMOVED] Removal")
- expect { subject.major_deprecation(35, "Message", removed_message: "Removal", print_caller_location: true) }.
- to raise_error(Bundler::DeprecatedError, /^\[REMOVED\] Removal \(called at .*:\d+\)$/)
- end
- end
end
diff --git a/spec/bundler/bundler/source/git/git_proxy_spec.rb b/spec/bundler/bundler/source/git/git_proxy_spec.rb
index f7c883eed4..1f10ca4b07 100644
--- a/spec/bundler/bundler/source/git/git_proxy_spec.rb
+++ b/spec/bundler/bundler/source/git/git_proxy_spec.rb
@@ -2,7 +2,7 @@
RSpec.describe Bundler::Source::Git::GitProxy do
let(:path) { Pathname("path") }
- let(:uri) { "https://github.com/rubygems/rubygems.git" }
+ let(:uri) { "https://github.com/ruby/rubygems.git" }
let(:ref) { nil }
let(:branch) { nil }
let(:tag) { nil }
@@ -10,7 +10,9 @@ RSpec.describe Bundler::Source::Git::GitProxy do
let(:revision) { nil }
let(:git_source) { nil }
let(:clone_result) { double(Process::Status, success?: true) }
+ let(:fail_result) { double(Process::Status, success?: false) }
let(:base_clone_args) { ["clone", "--bare", "--no-hardlinks", "--quiet", "--no-tags", "--depth", "1", "--single-branch"] }
+ let(:base_fetch_args) { ["fetch", "--force", "--quiet", "--no-tags", "--depth", "1"] }
subject(:git_proxy) { described_class.new(path, uri, options, revision, git_source) }
context "with explicit ref" do
@@ -64,7 +66,7 @@ RSpec.describe Bundler::Source::Git::GitProxy do
it "adds username and password to URI" do
Bundler.settings.temporary(uri => "u:p") do
allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
- expect(git_proxy).to receive(:capture).with([*base_clone_args, "--", "https://u:p@github.com/rubygems/rubygems.git", path.to_s], nil).and_return(["", "", clone_result])
+ expect(git_proxy).to receive(:capture).with([*base_clone_args, "--", "https://u:p@github.com/ruby/rubygems.git", path.to_s], nil).and_return(["", "", clone_result])
subject.checkout
end
end
@@ -72,13 +74,13 @@ RSpec.describe Bundler::Source::Git::GitProxy do
it "adds username and password to URI for host" do
Bundler.settings.temporary("github.com" => "u:p") do
allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
- expect(git_proxy).to receive(:capture).with([*base_clone_args, "--", "https://u:p@github.com/rubygems/rubygems.git", path.to_s], nil).and_return(["", "", clone_result])
+ expect(git_proxy).to receive(:capture).with([*base_clone_args, "--", "https://u:p@github.com/ruby/rubygems.git", path.to_s], nil).and_return(["", "", clone_result])
subject.checkout
end
end
it "does not add username and password to mismatched URI" do
- Bundler.settings.temporary("https://u:p@github.com/rubygems/rubygems-mismatch.git" => "u:p") do
+ Bundler.settings.temporary("https://u:p@github.com/ruby/rubygems-mismatch.git" => "u:p") do
allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
expect(git_proxy).to receive(:capture).with([*base_clone_args, "--", uri, path.to_s], nil).and_return(["", "", clone_result])
subject.checkout
@@ -87,7 +89,7 @@ RSpec.describe Bundler::Source::Git::GitProxy do
it "keeps original userinfo" do
Bundler.settings.temporary("github.com" => "u:p") do
- original = "https://orig:info@github.com/rubygems/rubygems.git"
+ original = "https://orig:info@github.com/ruby/rubygems.git"
git_proxy = described_class.new(Pathname("path"), original, options)
allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
expect(git_proxy).to receive(:capture).with([*base_clone_args, "--", original, path.to_s], nil).and_return(["", "", clone_result])
@@ -99,7 +101,7 @@ RSpec.describe Bundler::Source::Git::GitProxy do
describe "#version" do
context "with a normal version number" do
before do
- expect(git_proxy).to receive(:git_local).with("--version").
+ expect(described_class).to receive(:full_version).
and_return("git version 1.2.3")
end
@@ -114,7 +116,7 @@ RSpec.describe Bundler::Source::Git::GitProxy do
context "with a OSX version number" do
before do
- expect(git_proxy).to receive(:git_local).with("--version").
+ expect(described_class).to receive(:full_version).
and_return("git version 1.2.3 (Apple Git-BS)")
end
@@ -129,7 +131,7 @@ RSpec.describe Bundler::Source::Git::GitProxy do
context "with a msysgit version number" do
before do
- expect(git_proxy).to receive(:git_local).with("--version").
+ expect(described_class).to receive(:full_version).
and_return("git version 1.2.3.msysgit.0")
end
@@ -146,8 +148,9 @@ RSpec.describe Bundler::Source::Git::GitProxy do
describe "#full_version" do
context "with a normal version number" do
before do
- expect(git_proxy).to receive(:git_local).with("--version").
- and_return("git version 1.2.3")
+ status = double("success?" => true)
+ expect(Open3).to receive(:capture3).with("git", "--version").
+ and_return(["git version 1.2.3", "", status])
end
it "returns the git version number" do
@@ -157,8 +160,9 @@ RSpec.describe Bundler::Source::Git::GitProxy do
context "with a OSX version number" do
before do
- expect(git_proxy).to receive(:git_local).with("--version").
- and_return("git version 1.2.3 (Apple Git-BS)")
+ status = double("success?" => true)
+ expect(Open3).to receive(:capture3).with("git", "--version").
+ and_return(["git version 1.2.3 (Apple Git-BS)", "", status])
end
it "does not strip out OSX specific additions in the version string" do
@@ -168,8 +172,9 @@ RSpec.describe Bundler::Source::Git::GitProxy do
context "with a msysgit version number" do
before do
- expect(git_proxy).to receive(:git_local).with("--version").
- and_return("git version 1.2.3.msysgit.0")
+ status = double("success?" => true)
+ expect(Open3).to receive(:capture3).with("git", "--version").
+ and_return(["git version 1.2.3.msysgit.0", "", status])
end
it "does not strip out msysgit specific additions in the version string" do
@@ -199,16 +204,151 @@ RSpec.describe Bundler::Source::Git::GitProxy do
end
context "URI is HTTP" do
- let(:uri) { "http://github.com/rubygems/rubygems.git" }
- let(:without_depth_arguments) { ["clone", "--bare", "--no-hardlinks", "--quiet", "--no-tags", "--single-branch"] }
- let(:fail_clone_result) { double(Process::Status, success?: false) }
+ let(:uri) { "http://github.com/ruby/rubygems.git" }
+ let(:clone_args_without_depth) { ["clone", "--bare", "--no-hardlinks", "--quiet", "--no-tags", "--single-branch"] }
- it "retries without --depth when git url is http and fails" do
+ it "retries clone without --depth when dumb http transport fails" do
allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
- allow(git_proxy).to receive(:capture).with([*base_clone_args, "--", uri, path.to_s], nil).and_return(["", "dumb http transport does not support shallow capabilities", fail_clone_result])
- expect(git_proxy).to receive(:capture).with([*without_depth_arguments, "--", uri, path.to_s], nil).and_return(["", "", clone_result])
+ expect(git_proxy).to receive(:capture).with([*base_clone_args, "--", uri, path.to_s], nil).and_return(["", "dumb http transport does not support shallow capabilities", fail_result])
+ expect(git_proxy).to receive(:capture).with([*clone_args_without_depth, "--", uri, path.to_s], nil).and_return(["", "", clone_result])
subject.checkout
end
end
+
+ describe "#installed_to?" do
+ let(:destination) { "install/dir" }
+ let(:destination_dir_exists) { true }
+ let(:children) { ["gem.gemspec", "README.me", ".git", "Rakefile"] }
+
+ before do
+ allow(Dir).to receive(:exist?).with(destination).and_return(destination_dir_exists)
+ allow(Dir).to receive(:children).with(destination).and_return(children)
+ end
+
+ context "when destination dir exists with children other than just .git" do
+ it "returns true" do
+ expect(git_proxy.installed_to?(destination)).to be true
+ end
+ end
+
+ context "when destination dir does not exist" do
+ let(:destination_dir_exists) { false }
+
+ it "returns false" do
+ expect(git_proxy.installed_to?(destination)).to be false
+ end
+ end
+
+ context "when destination dir is empty" do
+ let(:children) { [] }
+
+ it "returns false" do
+ expect(git_proxy.installed_to?(destination)).to be false
+ end
+ end
+
+ context "when destination dir has only .git directory" do
+ let(:children) { [".git"] }
+
+ it "returns false" do
+ expect(git_proxy.installed_to?(destination)).to be false
+ end
+ end
+ end
+
+ describe "#checkout" do
+ context "when the repository isn't cloned" do
+ before do
+ allow(path).to receive(:exist?).and_return(false)
+ end
+
+ it "clones the repository" do
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:capture).with([*base_clone_args, "--", uri, path.to_s], nil).and_return(["", "", clone_result])
+ subject.checkout
+ end
+ end
+
+ context "when the repository is cloned" do
+ before do
+ allow(path).to receive(:exist?).and_return(true)
+ end
+
+ context "with a locked revision" do
+ let(:revision) { Digest::SHA1.hexdigest("ruby") }
+
+ context "when the revision exists locally" do
+ it "uses the cached revision" do
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:git).with("cat-file", "-e", revision, dir: path).and_return(true)
+ subject.checkout
+ end
+ end
+
+ context "when the revision doesn't exist locally" do
+ it "fetches the specific revision" do
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:git).with("cat-file", "-e", revision, dir: path).and_raise(Bundler::GitError)
+ expect(git_proxy).to receive(:capture).with(["fetch", "--force", "--quiet", "--no-tags", "--depth", "1", "--", uri, "#{revision}:refs/#{revision}-sha"], path).and_return(["", "", clone_result])
+ subject.checkout
+ end
+ end
+ end
+
+ context "with no explicit ref" do
+ it "fetches the HEAD revision" do
+ parsed_revision = Digest::SHA1.hexdigest("ruby")
+ allow(git_proxy).to receive(:git_local).with("rev-parse", "--abbrev-ref", "HEAD", dir: path).and_return(parsed_revision)
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:capture).with(["fetch", "--force", "--quiet", "--no-tags", "--depth", "1", "--", uri, "refs/heads/#{parsed_revision}:refs/heads/#{parsed_revision}"], path).and_return(["", "", clone_result])
+ subject.checkout
+ end
+ end
+
+ context "with a commit ref" do
+ let(:ref) { Digest::SHA1.hexdigest("ruby") }
+
+ context "when the revision exists locally" do
+ it "uses the cached revision" do
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:git).with("cat-file", "-e", ref, dir: path).and_return(true)
+ subject.checkout
+ end
+ end
+
+ context "when the revision doesn't exist locally" do
+ it "fetches the specific revision" do
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:git).with("cat-file", "-e", ref, dir: path).and_raise(Bundler::GitError)
+ expect(git_proxy).to receive(:capture).with(["fetch", "--force", "--quiet", "--no-tags", "--depth", "1", "--", uri, "#{ref}:refs/#{ref}-sha"], path).and_return(["", "", clone_result])
+ subject.checkout
+ end
+ end
+ end
+
+ context "with a non-commit ref" do
+ let(:ref) { "HEAD" }
+
+ it "fetches all revisions" do
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:capture).with(["fetch", "--force", "--quiet", "--no-tags", "--", uri, "refs/*:refs/*"], path).and_return(["", "", clone_result])
+ subject.checkout
+ end
+ end
+
+ context "URI is HTTP" do
+ let(:uri) { "http://github.com/ruby/rubygems.git" }
+
+ it "retries fetch without --depth when dumb http transport fails" do
+ parsed_revision = Digest::SHA1.hexdigest("ruby")
+ allow(git_proxy).to receive(:git_local).with("rev-parse", "--abbrev-ref", "HEAD", dir: path).and_return(parsed_revision)
+ allow(git_proxy).to receive(:git_local).with("--version").and_return("git version 2.14.0")
+ expect(git_proxy).to receive(:capture).with([*base_fetch_args, "--", uri, "refs/heads/#{parsed_revision}:refs/heads/#{parsed_revision}"], path).and_return(["", "dumb http transport does not support shallow capabilities", fail_result])
+ expect(git_proxy).to receive(:capture).with(["fetch", "--force", "--quiet", "--no-tags", "--", uri, "refs/heads/#{parsed_revision}:refs/heads/#{parsed_revision}"], path).and_return(["", "", clone_result])
+ subject.checkout
+ end
+ end
+ end
+ end
end
diff --git a/spec/bundler/bundler/source/git_spec.rb b/spec/bundler/bundler/source/git_spec.rb
index feef54bbf4..14e91c6bdc 100644
--- a/spec/bundler/bundler/source/git_spec.rb
+++ b/spec/bundler/bundler/source/git_spec.rb
@@ -70,4 +70,54 @@ RSpec.describe Bundler::Source::Git do
end
end
end
+
+ describe "#locked_revision_checked_out?" do
+ let(:revision) { "abc" }
+ let(:git_proxy_revision) { revision }
+ let(:git_proxy_installed) { true }
+ let(:git_proxy) { subject.send(:git_proxy) }
+ let(:options) do
+ {
+ "uri" => uri,
+ "revision" => revision,
+ }
+ end
+
+ before do
+ allow(git_proxy).to receive(:revision).and_return(git_proxy_revision)
+ allow(git_proxy).to receive(:installed_to?).with(subject.install_path).and_return(git_proxy_installed)
+ end
+
+ context "when the locked revision is checked out" do
+ it "returns true" do
+ expect(subject.send(:locked_revision_checked_out?)).to be true
+ end
+ end
+
+ context "when no revision is provided" do
+ let(:options) do
+ { "uri" => uri }
+ end
+
+ it "returns falsey value" do
+ expect(subject.send(:locked_revision_checked_out?)).to be_falsey
+ end
+ end
+
+ context "when the git proxy revision is different than the git revision" do
+ let(:git_proxy_revision) { revision.next }
+
+ it "returns falsey value" do
+ expect(subject.send(:locked_revision_checked_out?)).to be_falsey
+ end
+ end
+
+ context "when the gem hasn't been installed" do
+ let(:git_proxy_installed) { false }
+
+ it "returns falsey value" do
+ expect(subject.send(:locked_revision_checked_out?)).to be_falsey
+ end
+ end
+ end
end
diff --git a/spec/bundler/bundler/source/rubygems/remote_spec.rb b/spec/bundler/bundler/source/rubygems/remote_spec.rb
index 56f3bee459..27430d4a3b 100644
--- a/spec/bundler/bundler/source/rubygems/remote_spec.rb
+++ b/spec/bundler/bundler/source/rubygems/remote_spec.rb
@@ -106,8 +106,8 @@ RSpec.describe Bundler::Source::Rubygems::Remote do
context "when a mirror with inline credentials is configured for the URI" do
let(:uri) { Gem::URI("https://rubygems.org/") }
- let(:mirror_uri_with_auth) { Gem::URI("https://username:password@rubygems-mirror.org/") }
- let(:mirror_uri_no_auth) { Gem::URI("https://rubygems-mirror.org/") }
+ let(:mirror_uri_with_auth) { Gem::URI("https://username:password@example-mirror.rubygems.org/") }
+ let(:mirror_uri_no_auth) { Gem::URI("https://example-mirror.rubygems.org/") }
before { Bundler.settings.temporary("mirror.https://rubygems.org/" => mirror_uri_with_auth.to_s) }
@@ -132,8 +132,8 @@ RSpec.describe Bundler::Source::Rubygems::Remote do
context "when a mirror with configured credentials is configured for the URI" do
let(:uri) { Gem::URI("https://rubygems.org/") }
- let(:mirror_uri_with_auth) { Gem::URI("https://#{credentials}@rubygems-mirror.org/") }
- let(:mirror_uri_no_auth) { Gem::URI("https://rubygems-mirror.org/") }
+ let(:mirror_uri_with_auth) { Gem::URI("https://#{credentials}@example-mirror.rubygems.org/") }
+ let(:mirror_uri_no_auth) { Gem::URI("https://example-mirror.rubygems.org/") }
before do
Bundler.settings.temporary("mirror.https://rubygems.org/" => mirror_uri_no_auth.to_s)
@@ -169,4 +169,39 @@ RSpec.describe Bundler::Source::Rubygems::Remote do
end
end
end
+
+ describe "#cooldown" do
+ it "is nil by default" do
+ expect(remote(uri_no_auth).cooldown).to be_nil
+ end
+
+ it "returns the value passed to the constructor" do
+ r = Bundler::Source::Rubygems::Remote.new(uri_no_auth, cooldown: 7)
+ expect(r.cooldown).to eq(7)
+ end
+ end
+
+ describe "#effective_cooldown" do
+ it "returns the per-remote value when no override is set" do
+ r = Bundler::Source::Rubygems::Remote.new(uri_no_auth, cooldown: 7)
+ expect(r.effective_cooldown).to eq(7)
+ end
+
+ it "returns nil when neither override nor per-remote value is set" do
+ expect(remote(uri_no_auth).effective_cooldown).to be_nil
+ end
+
+ it "settings override per-remote value" do
+ r = Bundler::Source::Rubygems::Remote.new(uri_no_auth, cooldown: 7)
+ Bundler.settings.temporary(cooldown: 14) do
+ expect(r.effective_cooldown).to eq(14)
+ end
+ end
+
+ it "settings override even when per-remote value is absent" do
+ Bundler.settings.temporary(cooldown: 14) do
+ expect(remote(uri_no_auth).effective_cooldown).to eq(14)
+ end
+ end
+ end
end
diff --git a/spec/bundler/bundler/source/rubygems_spec.rb b/spec/bundler/bundler/source/rubygems_spec.rb
index 884fa81046..feb787498e 100644
--- a/spec/bundler/bundler/source/rubygems_spec.rb
+++ b/spec/bundler/bundler/source/rubygems_spec.rb
@@ -44,4 +44,61 @@ RSpec.describe Bundler::Source::Rubygems do
end
end
end
+
+ describe "#clear_cache" do
+ it "invalidates memoized indexes so subsequent reads rebuild them" do
+ source = described_class.new
+
+ first_specs = source.specs
+ first_installed = source.send(:installed_specs)
+ first_default = source.send(:default_specs)
+ first_cached = source.send(:cached_specs)
+
+ expect(source.specs).to equal(first_specs)
+ expect(source.send(:installed_specs)).to equal(first_installed)
+ expect(source.send(:default_specs)).to equal(first_default)
+ expect(source.send(:cached_specs)).to equal(first_cached)
+
+ source.clear_cache
+
+ expect(source.specs).not_to equal(first_specs)
+ expect(source.send(:installed_specs)).not_to equal(first_installed)
+ expect(source.send(:default_specs)).not_to equal(first_default)
+ expect(source.send(:cached_specs)).not_to equal(first_cached)
+ end
+
+ it "reflects newly-discovered installed gems after clear_cache" do
+ source = described_class.new
+ foo = Gem::Specification.new("foo", "1.0.0")
+ bar = Gem::Specification.new("bar", "1.0.0")
+
+ allow(Bundler.rubygems).to receive(:installed_specs).and_return([foo])
+ expect(source.send(:installed_specs).search("bar")).to be_empty
+
+ allow(Bundler.rubygems).to receive(:installed_specs).and_return([foo, bar])
+ expect(source.send(:installed_specs).search("bar")).to be_empty
+
+ source.clear_cache
+
+ expect(source.send(:installed_specs).search("bar")).not_to be_empty
+ end
+ end
+
+ describe "log debug information" do
+ it "log the time spent downloading and installing a gem" do
+ build_repo2 do
+ build_gem "warning"
+ end
+
+ gemfile_content = <<~G
+ source "https://gem.repo2"
+ gem "warning"
+ G
+
+ stdout = install_gemfile(gemfile_content, env: { "DEBUG" => "1" })
+
+ expect(stdout).to match(/Downloaded warning in: \d+\.\d+s/)
+ expect(stdout).to match(/Installed warning in: \d+\.\d+s/)
+ end
+ end
end
diff --git a/spec/bundler/bundler/source_list_spec.rb b/spec/bundler/bundler/source_list_spec.rb
index 13453cb2a3..61bd99b063 100644
--- a/spec/bundler/bundler/source_list_spec.rb
+++ b/spec/bundler/bundler/source_list_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe Bundler::SourceList do
subject(:source_list) { Bundler::SourceList.new }
- let(:rubygems_aggregate) { Bundler::Source::Rubygems.new }
+ let(:global_rubygems_source) { Bundler::Source::Rubygems.new }
let(:metadata_source) { Bundler::Source::Metadata.new }
describe "adding sources" do
@@ -118,17 +118,23 @@ RSpec.describe Bundler::SourceList do
describe "#add_global_rubygems_remote" do
let!(:returned_source) { source_list.add_global_rubygems_remote("https://rubygems.org/") }
- it "returns the aggregate rubygems source" do
+ it "returns the global rubygems source" do
expect(returned_source).to be_instance_of(Bundler::Source::Rubygems)
end
- it "adds the provided remote to the beginning of the aggregate source" do
+ it "adds the provided remote to the beginning of the global source" do
source_list.add_global_rubygems_remote("https://othersource.org")
expect(returned_source.remotes).to eq [
Gem::URI("https://othersource.org/"),
Gem::URI("https://rubygems.org/"),
]
end
+
+ it "records the per-remote cooldown when supplied" do
+ source_list.add_global_rubygems_remote("https://othersource.org", cooldown: 7)
+ expect(returned_source.cooldown_for(Gem::URI("https://othersource.org/"))).to eq(7)
+ expect(returned_source.cooldown_for(Gem::URI("https://rubygems.org/"))).to be_nil
+ end
end
describe "#add_plugin_source" do
@@ -156,21 +162,21 @@ RSpec.describe Bundler::SourceList do
end
describe "#all_sources" do
- it "includes the aggregate rubygems source when rubygems sources have been added" do
+ it "includes the global rubygems source when rubygems sources have been added" do
source_list.add_git_source("uri" => "git://host/path.git")
source_list.add_rubygems_source("remotes" => ["https://rubygems.org"])
source_list.add_path_source("path" => "/path/to/gem")
source_list.add_plugin_source("new_source", "uri" => "https://some.url/a")
- expect(source_list.all_sources).to include rubygems_aggregate
+ expect(source_list.all_sources).to include global_rubygems_source
end
- it "includes the aggregate rubygems source when no rubygems sources have been added" do
+ it "includes the global rubygems source when no rubygems sources have been added" do
source_list.add_git_source("uri" => "git://host/path.git")
source_list.add_path_source("path" => "/path/to/gem")
source_list.add_plugin_source("new_source", "uri" => "https://some.url/a")
- expect(source_list.all_sources).to include rubygems_aggregate
+ expect(source_list.all_sources).to include global_rubygems_source
end
it "returns sources of the same type in the reverse order that they were added" do
@@ -204,7 +210,7 @@ RSpec.describe Bundler::SourceList do
Bundler::Source::Rubygems.new("remotes" => ["https://third-rubygems.org"]),
Bundler::Source::Rubygems.new("remotes" => ["https://fourth-rubygems.org"]),
Bundler::Source::Rubygems.new("remotes" => ["https://fifth-rubygems.org"]),
- rubygems_aggregate,
+ global_rubygems_source,
metadata_source,
]
end
@@ -297,19 +303,19 @@ RSpec.describe Bundler::SourceList do
end
describe "#rubygems_sources" do
- it "includes the aggregate rubygems source when rubygems sources have been added" do
+ it "includes the global rubygems source when rubygems sources have been added" do
source_list.add_git_source("uri" => "git://host/path.git")
source_list.add_rubygems_source("remotes" => ["https://rubygems.org"])
source_list.add_path_source("path" => "/path/to/gem")
- expect(source_list.rubygems_sources).to include rubygems_aggregate
+ expect(source_list.rubygems_sources).to include global_rubygems_source
end
- it "returns only the aggregate rubygems source when no rubygems sources have been added" do
+ it "returns only the global rubygems source when no rubygems sources have been added" do
source_list.add_git_source("uri" => "git://host/path.git")
source_list.add_path_source("path" => "/path/to/gem")
- expect(source_list.rubygems_sources).to eq [rubygems_aggregate]
+ expect(source_list.rubygems_sources).to eq [global_rubygems_source]
end
it "returns rubygems sources in the reverse order that they were added" do
@@ -331,7 +337,7 @@ RSpec.describe Bundler::SourceList do
Bundler::Source::Rubygems.new("remotes" => ["https://third-rubygems.org"]),
Bundler::Source::Rubygems.new("remotes" => ["https://fourth-rubygems.org"]),
Bundler::Source::Rubygems.new("remotes" => ["https://fifth-rubygems.org"]),
- rubygems_aggregate,
+ global_rubygems_source,
]
end
end
@@ -442,6 +448,16 @@ RSpec.describe Bundler::SourceList do
end
end
+ describe "#clear_cache" do
+ let(:rubygems_source) { source_list.add_rubygems_source("remotes" => ["https://rubygems.org"]) }
+
+ it "calls #clear_cache on all rubygems sources" do
+ expect(rubygems_source).to receive(:clear_cache)
+ expect(source_list.global_rubygems_source).to receive(:clear_cache)
+ source_list.clear_cache
+ end
+ end
+
describe "implicit_global_source?" do
context "when a global rubygem source provided" do
it "returns a falsy value" do
diff --git a/spec/bundler/bundler/source_spec.rb b/spec/bundler/bundler/source_spec.rb
index 3b49c37431..01b57ce9e8 100644
--- a/spec/bundler/bundler/source_spec.rb
+++ b/spec/bundler/bundler/source_spec.rb
@@ -21,7 +21,7 @@ RSpec.describe Bundler::Source do
end
describe "#version_message" do
- let(:spec) { double(:spec, name: "nokogiri", version: ">= 1.6", platform: rb) }
+ let(:spec) { double(:spec, name: "nokogiri", version: ">= 1.6", platform: Gem::Platform::RUBY) }
shared_examples_for "the lockfile specs are not relevant" do
it "should return a string with the spec name and version" do
@@ -70,7 +70,7 @@ RSpec.describe Bundler::Source do
end
context "with a more recent version" do
- let(:spec) { double(:spec, name: "nokogiri", version: "1.6.1", platform: rb) }
+ let(:spec) { double(:spec, name: "nokogiri", version: "1.6.1", platform: Gem::Platform::RUBY) }
let(:locked_gem) { double(:locked_gem, name: "nokogiri", version: "1.7.0") }
context "with color", :no_color_tty do
@@ -97,7 +97,7 @@ RSpec.describe Bundler::Source do
end
context "with an older version" do
- let(:spec) { double(:spec, name: "nokogiri", version: "1.7.1", platform: rb) }
+ let(:spec) { double(:spec, name: "nokogiri", version: "1.7.1", platform: Gem::Platform::RUBY) }
let(:locked_gem) { double(:locked_gem, name: "nokogiri", version: "1.7.0") }
context "with color", :no_color_tty do
diff --git a/spec/bundler/bundler/spec_set_spec.rb b/spec/bundler/bundler/spec_set_spec.rb
index c4b6676223..1e1ceadf26 100644
--- a/spec/bundler/bundler/spec_set_spec.rb
+++ b/spec/bundler/bundler/spec_set_spec.rb
@@ -43,6 +43,30 @@ RSpec.describe Bundler::SpecSet do
spec = described_class.new(specs).find_by_name_and_platform("b", platform)
expect(spec).to eq platform_spec
end
+
+ it "returns nil when the name is not present" do
+ spec = described_class.new(specs).find_by_name_and_platform("missing", platform)
+ expect(spec).to be_nil
+ end
+
+ it "returns nil when the name exists but no spec is installable on the requested platform" do
+ incompatible_platform = Gem::Platform.new("java")
+ incompatible_spec = build_spec("a", "1.0", incompatible_platform).first
+
+ spec = described_class.new([incompatible_spec]).find_by_name_and_platform("a", platform)
+ expect(spec).to be_nil
+ end
+
+ it "returns the first installable spec for the given name in insertion order" do
+ later_platform_spec = build_spec("b", "3.0", platform).first
+ specs = [
+ platform_spec,
+ later_platform_spec,
+ ]
+
+ spec = described_class.new(specs).find_by_name_and_platform("b", platform)
+ expect(spec).to eq platform_spec
+ end
end
describe "#to_a" do
@@ -55,5 +79,70 @@ RSpec.describe Bundler::SpecSet do
d-2.0
]
end
+
+ it "puts rake first when present" do
+ specs = [
+ build_spec("a", "1.0") {|s| s.dep "rake", ">= 0" },
+ build_spec("rake", "13.0"),
+ ].flatten
+
+ expect(described_class.new(specs).to_a.map(&:full_name)).to eq %w[
+ rake-13.0
+ a-1.0
+ ]
+ end
+ end
+
+ describe "#complete_platform" do
+ let(:platform) { Gem::Platform.new("x86_64-linux") }
+
+ let(:platform_variant) do
+ build_spec("needs_old_ruby", "1.0", platform).first.tap do |s|
+ s.required_ruby_version = Gem::Requirement.new("< #{Gem.ruby_version}")
+ end
+ end
+
+ let(:lazy_spec) do
+ lazy = Bundler::LazySpecification.new("needs_old_ruby", Gem::Version.new("1.0"), Gem::Platform::RUBY)
+ lazy.required_ruby_version = Gem::Requirement.new("< #{Gem.ruby_version}")
+ source = double("source")
+ source_specs = double("source_specs")
+ allow(source).to receive(:specs).and_return(source_specs)
+ allow(source_specs).to receive(:search).
+ with(["needs_old_ruby", Gem::Version.new("1.0")]).and_return([platform_variant])
+ lazy.source = source
+ lazy
+ end
+
+ it "rejects a platform variant whose strict metadata is incompatible when no override is attached" do
+ set = described_class.new([lazy_spec])
+ expect(set.send(:complete_platform, platform)).to be(false)
+ end
+
+ it "accepts a platform variant when the LazySpec carries an override that allows it" do
+ lazy_spec.overrides = [Bundler::Override.new("needs_old_ruby", :required_ruby_version, :ignore_upper)]
+ set = described_class.new([lazy_spec])
+ expect(set.send(:complete_platform, platform)).to be(true)
+ end
+
+ it "carries overrides onto a synthesized LazySpec so a follow-up complete_platform still honors them" do
+ override = Bundler::Override.new("needs_old_ruby", :required_ruby_version, :ignore_upper)
+ lazy_spec.overrides = [override]
+ second_platform = Gem::Platform.new("aarch64-linux")
+ second_variant = build_spec("needs_old_ruby", "1.0", second_platform).first.tap do |s|
+ s.required_ruby_version = Gem::Requirement.new("< #{Gem.ruby_version}")
+ end
+ allow(lazy_spec.source.specs).to receive(:search).
+ with(["needs_old_ruby", Gem::Version.new("1.0")]).and_return([platform_variant, second_variant])
+
+ set = described_class.new([lazy_spec])
+ expect(set.send(:complete_platform, platform)).to be(true)
+ # The synthesized x86_64-linux variant is now in the set. If lookup
+ # picks it as exemplar for the next platform check, the override list
+ # must still be reachable via its overrides accessor.
+ synthesized = set.to_a.find {|s| s.platform == platform }
+ expect(synthesized.overrides).to eq([override])
+ expect(set.send(:complete_platform, second_platform)).to be(true)
+ end
end
end
diff --git a/spec/bundler/bundler/specifications/foo.gemspec b/spec/bundler/bundler/specifications/foo.gemspec
index 81b77d0c86..19b7724e81 100644
--- a/spec/bundler/bundler/specifications/foo.gemspec
+++ b/spec/bundler/bundler/specifications/foo.gemspec
@@ -8,6 +8,6 @@ Gem::Specification.new do |s|
s.version = "1.0.0"
s.loaded_from = __FILE__
s.extensions = "ext/foo"
- s.required_ruby_version = ">= 3.1.0"
+ s.required_ruby_version = ">= 3.2.0"
end
# rubocop:enable Style/FrozenStringLiteralComment
diff --git a/spec/bundler/bundler/stub_specification_spec.rb b/spec/bundler/bundler/stub_specification_spec.rb
index beb966b3ce..f2faa2ea64 100644
--- a/spec/bundler/bundler/stub_specification_spec.rb
+++ b/spec/bundler/bundler/stub_specification_spec.rb
@@ -49,10 +49,14 @@ RSpec.describe Bundler::StubSpecification do
expect(stub.missing_extensions?).to be false
end
- it "returns true if not manually_installed?" do
+ it "returns #{RUBY_ENGINE == "jruby" ? "false" : "true"} if not manually_installed?" do
stub = described_class.from_stub(with_bundler_stub_spec)
stub.installed_by_version = Gem::Version.new(1)
- expect(stub.missing_extensions?).to be true
+ if RUBY_ENGINE == "jruby"
+ expect(stub.missing_extensions?).to be false
+ else
+ expect(stub.missing_extensions?).to be true
+ end
end
end
diff --git a/spec/bundler/bundler/ui/shell_spec.rb b/spec/bundler/bundler/ui/shell_spec.rb
index 422c850a65..83f147191e 100644
--- a/spec/bundler/bundler/ui/shell_spec.rb
+++ b/spec/bundler/bundler/ui/shell_spec.rb
@@ -81,4 +81,32 @@ RSpec.describe Bundler::UI::Shell do
end
end
end
+
+ describe "threads" do
+ it "is thread safe when using with_level" do
+ stop_thr1 = false
+ stop_thr2 = false
+
+ expect(subject.level).to eq("debug")
+
+ thr1 = Thread.new do
+ subject.silence do
+ sleep(0.1) until stop_thr1
+ end
+
+ stop_thr2 = true
+ end
+
+ thr2 = Thread.new do
+ subject.silence do
+ stop_thr1 = true
+ sleep(0.1) until stop_thr2
+ end
+ end
+
+ [thr1, thr2].each(&:join)
+
+ expect(subject.level).to eq("debug")
+ end
+ end
end
diff --git a/spec/bundler/bundler/uri_normalizer_spec.rb b/spec/bundler/bundler/uri_normalizer_spec.rb
new file mode 100644
index 0000000000..1308e86014
--- /dev/null
+++ b/spec/bundler/bundler/uri_normalizer_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+RSpec.describe Bundler::URINormalizer do
+ describe ".normalize_suffix" do
+ context "when trailing_slash is true" do
+ it "adds a trailing slash when missing" do
+ expect(described_class.normalize_suffix("https://example.com", trailing_slash: true)).to eq("https://example.com/")
+ end
+
+ it "keeps the trailing slash when present" do
+ expect(described_class.normalize_suffix("https://example.com/", trailing_slash: true)).to eq("https://example.com/")
+ end
+ end
+
+ context "when trailing_slash is false" do
+ it "removes a trailing slash when present" do
+ expect(described_class.normalize_suffix("https://example.com/", trailing_slash: false)).to eq("https://example.com")
+ end
+
+ it "keeps the value unchanged when no trailing slash exists" do
+ expect(described_class.normalize_suffix("https://example.com", trailing_slash: false)).to eq("https://example.com")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/bundler/worker_spec.rb b/spec/bundler/bundler/worker_spec.rb
index e4ebbd2932..2ad2845e37 100644
--- a/spec/bundler/bundler/worker_spec.rb
+++ b/spec/bundler/bundler/worker_spec.rb
@@ -20,6 +20,26 @@ RSpec.describe Bundler::Worker do
end
end
+ describe "priority queue" do
+ it "process elements from the priority queue first" do
+ processed_elements = []
+
+ function = proc do |element, _|
+ processed_elements << element
+ end
+
+ worker = described_class.new(1, "Spec Worker", function)
+ worker.instance_variable_set(:@threads, []) # Prevent the enqueueing from starting work.
+ worker.enq("Normal element")
+ worker.enq("Priority element", priority: true)
+ worker.send(:create_threads)
+
+ worker.stop
+
+ expect(processed_elements).to eq(["Priority element", "Normal element"])
+ end
+ end
+
describe "handling interrupts" do
let(:status) do
pid = Process.fork do
diff --git a/spec/bundler/bundler/yaml_serializer_spec.rb b/spec/bundler/bundler/yaml_serializer_spec.rb
index de437f764a..9ff1579b76 100644
--- a/spec/bundler/bundler/yaml_serializer_spec.rb
+++ b/spec/bundler/bundler/yaml_serializer_spec.rb
@@ -112,10 +112,10 @@ RSpec.describe Bundler::YAMLSerializer do
it "handles colon in key/value" do
yaml = <<~YAML
- BUNDLE_MIRROR__HTTPS://RUBYGEMS__ORG/: http://rubygems-mirror.org
+ BUNDLE_MIRROR__HTTPS://RUBYGEMS__ORG/: http://example-mirror.rubygems.org
YAML
- expect(serializer.load(yaml)).to eq("BUNDLE_MIRROR__HTTPS://RUBYGEMS__ORG/" => "http://rubygems-mirror.org")
+ expect(serializer.load(yaml)).to eq("BUNDLE_MIRROR__HTTPS://RUBYGEMS__ORG/" => "http://example-mirror.rubygems.org")
end
it "handles arrays inside hashes" do
diff --git a/spec/bundler/cache/cache_path_spec.rb b/spec/bundler/cache/cache_path_spec.rb
index d5bd14965b..2a280ea858 100644
--- a/spec/bundler/cache/cache_path_spec.rb
+++ b/spec/bundler/cache/cache_path_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe "bundle package" do
context "with config cache_path" do
it "caches gems at given path" do
- bundle "config set cache_path vendor/cache-foo"
+ bundle_config "cache_path vendor/cache-foo"
bundle :cache
expect(bundled_app("vendor/cache-foo/myrack-1.0.0.gem")).to exist
end
diff --git a/spec/bundler/cache/gems_spec.rb b/spec/bundler/cache/gems_spec.rb
index c68b20225a..198279d84c 100644
--- a/spec/bundler/cache/gems_spec.rb
+++ b/spec/bundler/cache/gems_spec.rb
@@ -78,13 +78,13 @@ RSpec.describe "bundle cache" do
end
context "using system gems" do
- before { bundle "config set path.system true" }
+ before { bundle_config "path.system true" }
let(:path) { system_gem_path }
it_behaves_like "when there are only gemsources"
end
context "installing into a local path" do
- before { bundle "config set path ./.bundle" }
+ before { bundle_config "path ./.bundle" }
let(:path) { local_gem_path }
it_behaves_like "when there are only gemsources"
end
@@ -136,7 +136,7 @@ RSpec.describe "bundle cache" do
gem "json"
G
- bundle "config set cache_all_platforms true"
+ bundle_config "cache_all_platforms true"
bundle :cache
expect(bundled_app("vendor/cache/json-#{default_json_version}.gem")).to exist
@@ -157,14 +157,14 @@ RSpec.describe "bundle cache" do
context "when a remote gem is not available for caching" do
it "warns, but uses builtin gems when installing to system gems" do
- bundle "config set path.system true"
+ bundle_config "path.system true"
install_gemfile %(source "https://gem.repo1"; gem 'json', '#{default_json_version}'), verbose: true
expect(err).to include("json-#{default_json_version} is built in to Ruby, and can't be cached")
expect(out).to include("Using json #{default_json_version}")
end
it "errors when explicitly caching" do
- bundle "config set path.system true"
+ bundle_config "path.system true"
install_gemfile <<-G
source "https://gem.repo1"
@@ -226,7 +226,7 @@ RSpec.describe "bundle cache" do
it "re-caches during install" do
setup_main_repo
- cached_gem("myrack-1.0.0").rmtree
+ FileUtils.rm_rf cached_gem("myrack-1.0.0")
bundle :install
expect(out).to include("Updating files in vendor/cache")
expect(cached_gem("myrack-1.0.0")).to exist
@@ -291,7 +291,7 @@ RSpec.describe "bundle cache" do
expect(cached_gem("platform_specific-1.0-java")).to exist
end
- simulate_new_machine
+ pristine_system_gems
simulate_platform "x86-darwin-100" do
install_gemfile <<-G
@@ -307,12 +307,13 @@ RSpec.describe "bundle cache" do
it "doesn't remove gems cached gems that don't match their remote counterparts, but also refuses to install and prints an error" do
setup_main_repo
cached_myrack = cached_gem("myrack-1.0.0")
- cached_myrack.rmtree
+ FileUtils.rm_rf cached_myrack
build_gem "myrack", "1.0.0",
path: cached_myrack.parent,
rubygems_version: "1.3.2"
- simulate_new_machine
+ FileUtils.rm_r default_bundle_path
+ default_system_gems
FileUtils.rm bundled_app_lock
bundle :install, raise_on_error: false
@@ -337,14 +338,15 @@ RSpec.describe "bundle cache" do
it "raises an error when a cached gem is altered and produces a different checksum than the remote gem" do
setup_main_repo
- cached_gem("myrack-1.0.0").rmtree
+ FileUtils.rm_rf cached_gem("myrack-1.0.0")
build_gem "myrack", "1.0.0", path: bundled_app("vendor/cache")
checksums = checksums_section do |c|
c.checksum gem_repo1, "myrack", "1.0.0"
end
- simulate_new_machine
+ FileUtils.rm_r default_bundle_path
+ default_system_gems
lockfile <<-L
GEM
@@ -360,16 +362,16 @@ RSpec.describe "bundle cache" do
expect(err).to include("1. remove the gem at #{cached_gem("myrack-1.0.0")}")
expect(cached_gem("myrack-1.0.0")).to exist
- cached_gem("myrack-1.0.0").rmtree
+ FileUtils.rm_rf cached_gem("myrack-1.0.0")
bundle :install
expect(cached_gem("myrack-1.0.0")).to exist
end
it "installs a modified gem with a non-matching checksum when the API implementation does not provide checksums" do
setup_main_repo
- cached_gem("myrack-1.0.0").rmtree
+ FileUtils.rm_rf cached_gem("myrack-1.0.0")
build_gem "myrack", "1.0.0", path: bundled_app("vendor/cache")
- simulate_new_machine
+ pristine_system_gems
lockfile <<-L
GEM
diff --git a/spec/bundler/cache/git_spec.rb b/spec/bundler/cache/git_spec.rb
index 38333c0219..f0976ecac7 100644
--- a/spec/bundler/cache/git_spec.rb
+++ b/spec/bundler/cache/git_spec.rb
@@ -13,6 +13,22 @@ RSpec.describe "git base name" do
end
RSpec.describe "bundle cache with git" do
+ it "does not copy repository to vendor cache when cache_all set to false" do
+ git = build_git "foo"
+ ref = git.ref_for("main", 11)
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "foo", :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle_config "cache_all false"
+ bundle :cache
+ expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).not_to exist
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
it "copies repository to vendor cache and uses it" do
git = build_git "foo"
ref = git.ref_for("main", 11)
@@ -22,13 +38,12 @@ RSpec.describe "bundle cache with git" do
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
expect(bundled_app("vendor/cache/foo-1.0-#{ref}/.git")).not_to exist
expect(bundled_app("vendor/cache/foo-1.0-#{ref}/.bundlecache")).to be_file
- FileUtils.rm_rf lib_path("foo-1.0")
+ FileUtils.rm_r lib_path("foo-1.0")
expect(the_bundle).to include_gems "foo 1.0"
end
@@ -41,15 +56,14 @@ RSpec.describe "bundle cache with git" do
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
- bundle "config set --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
expect(bundled_app("vendor/cache/foo-1.0-#{ref}/.git")).not_to exist
- FileUtils.rm_rf lib_path("foo-1.0")
+ FileUtils.rm_r lib_path("foo-1.0")
expect(the_bundle).to include_gems "foo 1.0"
end
@@ -61,12 +75,11 @@ RSpec.describe "bundle cache with git" do
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
- bundle "config set cache_all true"
bundle :cache
bundle :cache
expect(out).to include "Updating files in vendor/cache"
- FileUtils.rm_rf lib_path("foo-1.0")
+ FileUtils.rm_r lib_path("foo-1.0")
expect(the_bundle).to include_gems "foo 1.0"
end
@@ -79,7 +92,6 @@ RSpec.describe "bundle cache with git" do
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
- bundle "config set cache_all true"
bundle :cache
update_git "foo" do |s|
@@ -95,7 +107,7 @@ RSpec.describe "bundle cache with git" do
expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
expect(bundled_app("vendor/cache/foo-1.0-#{old_ref}")).not_to exist
- FileUtils.rm_rf lib_path("foo-1.0")
+ FileUtils.rm_r lib_path("foo-1.0")
run "require 'foo'"
expect(out).to eq("CACHE")
end
@@ -109,7 +121,6 @@ RSpec.describe "bundle cache with git" do
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
- bundle "config set cache_all true"
bundle :cache
update_git "foo" do |s|
@@ -124,7 +135,7 @@ RSpec.describe "bundle cache with git" do
expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
expect(bundled_app("vendor/cache/foo-1.0-#{old_ref}")).not_to exist
- FileUtils.rm_rf lib_path("foo-1.0")
+ FileUtils.rm_r lib_path("foo-1.0")
run "require 'foo'"
expect(out).to eq("CACHE")
end
@@ -140,7 +151,6 @@ RSpec.describe "bundle cache with git" do
bundle %(config set local.foo #{lib_path("foo-1.0")})
bundle "install"
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/foo-invalid-#{ref}")).to exist
@@ -161,12 +171,12 @@ RSpec.describe "bundle cache with git" do
source "https://gem.repo1"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle :install
- simulate_new_machine
+ pristine_system_gems
with_path_as "" do
- bundle "config set deployment true"
+ bundle_config "deployment true"
bundle "install --local"
expect(the_bundle).to include_gem "foo 1.0"
end
@@ -179,13 +189,10 @@ RSpec.describe "bundle cache with git" do
source "https://gem.repo1"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
- bundle "config set cache_all true"
bundle :cache, "all-platforms" => true
- FileUtils.rm_rf Dir.glob(default_bundle_path("bundler/gems/extensions/**/foo-1.0-*")).first.to_s
- FileUtils.rm_rf Dir.glob(default_bundle_path("bundler/gems/foo-1.0-*")).first.to_s
- simulate_new_machine
- bundle "config set frozen true"
+ pristine_system_gems
+ bundle_config "frozen true"
bundle "install --local --verbose"
expect(out).to_not include("Fetching")
expect(the_bundle).to include_gem "foo 1.0"
@@ -198,14 +205,10 @@ RSpec.describe "bundle cache with git" do
source "https://gem.repo1"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
- bundle "config set cache_all true"
bundle :cache, "all-platforms" => true
- FileUtils.rm_rf Dir.glob(default_bundle_path("bundler/gems/extensions/**/foo-1.0-*")).first.to_s
- FileUtils.rm_rf Dir.glob(default_bundle_path("bundler/gems/foo-1.0-*")).first.to_s
- simulate_new_machine
- bundle "config set frozen true"
- FileUtils.rm_rf "#{default_bundle_path}/cache/bundler/git/foo-1.0-*"
+ pristine_system_gems
+ bundle_config "frozen true"
bundle "install --local --verbose"
expect(out).to_not include("Fetching")
expect(the_bundle).to include_gem "foo 1.0"
@@ -218,14 +221,10 @@ RSpec.describe "bundle cache with git" do
source "https://gem.repo1"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
- bundle "config set cache_all true"
bundle :cache, "all-platforms" => true
- FileUtils.rm_rf Dir.glob(default_bundle_path("bundler/gems/extensions/**/foo-1.0-*")).first.to_s
- FileUtils.rm_rf Dir.glob(default_bundle_path("bundler/gems/foo-1.0-*")).first.to_s
- simulate_new_machine
- bundle "config set frozen true"
- FileUtils.rm_rf "#{default_bundle_path}/cache/bundler/git/foo-1.0-*"
+ pristine_system_gems
+ bundle_config "frozen true"
# Remove untracked files (including the empty refs dir in the cache)
Dir.chdir(bundled_app) do
@@ -249,15 +248,14 @@ RSpec.describe "bundle cache with git" do
source "https://gem.repo1"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
- bundle "config set global_gem_cache false"
- bundle "config set cache_all true"
- bundle "config path vendor/bundle"
+ bundle_config "global_gem_cache false"
+ bundle_config "path vendor/bundle"
bundle :install
# Simulate old cache by copying the real cache folder to vendor/cache
FileUtils.mkdir_p bundled_app("vendor/cache")
FileUtils.cp_r "#{Dir.glob(vendored_gems("cache/bundler/git/foo-1.0-*")).first}/.", cache_dir
- FileUtils.rm_rf bundled_app("vendor/bundle")
+ FileUtils.rm_r bundled_app("vendor/bundle")
bundle "install --local --verbose"
expect(err).to include("Installing from cache in old \"bare repository\" format for compatibility")
@@ -281,15 +279,14 @@ RSpec.describe "bundle cache with git" do
source "https://gem.repo1"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
- bundle "config set global_gem_cache false"
- bundle "config set cache_all true"
- bundle "config path vendor/bundle"
+ bundle_config "global_gem_cache false"
+ bundle_config "path vendor/bundle"
bundle :install
# Simulate old cache by copying the real cache folder to vendor/cache
FileUtils.mkdir_p bundled_app("vendor/cache")
FileUtils.cp_r "#{Dir.glob(vendored_gems("cache/bundler/git/foo-1.0-*")).first}/.", cache_dir
- FileUtils.rm_rf bundled_app("vendor/bundle")
+ FileUtils.rm_r bundled_app("vendor/bundle")
bundle "install --verbose"
expect(out).to include("Fetching")
@@ -311,9 +308,8 @@ RSpec.describe "bundle cache with git" do
source "https://gem.repo1"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
- bundle "config set global_gem_cache false"
- bundle "config set cache_all true"
- bundle "config path vendor/bundle"
+ bundle_config "global_gem_cache false"
+ bundle_config "path vendor/bundle"
bundle :install
# Simulate old cache by copying the real cache folder to vendor/cache
@@ -350,7 +346,6 @@ RSpec.describe "bundle cache with git" do
G
ref = git.ref_for("main", 11)
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/has_submodule-1.0-#{ref}")).to exist
@@ -370,7 +365,6 @@ RSpec.describe "bundle cache with git" do
source "https://gem.repo1"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
- bundle "config set cache_all true"
bundle :cache
ref = git.ref_for("main", 11)
@@ -385,12 +379,11 @@ RSpec.describe "bundle cache with git" do
source "https://gem.repo1"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
- bundle "config set cache_all true"
bundle :cache, "all-platforms" => true, :install => false
- simulate_new_machine
+ pristine_system_gems
with_path_as "" do
- bundle "config set deployment true"
+ bundle_config "deployment true"
bundle :install, local: true
expect(the_bundle).to include_gem "foo 1.0"
end
@@ -425,13 +418,13 @@ RSpec.describe "bundle cache with git" do
foo!
BUNDLED WITH
- #{Bundler::VERSION}
+ #{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")
+ FileUtils.rm_r bundled_app("vendor/cache/foo-1.0-#{path_revision}/.git")
bundle :install, env: { "BUNDLE_DEPLOYMENT" => "true", "BUNDLE_CACHE_ALL" => "true" }
end
@@ -444,7 +437,6 @@ RSpec.describe "bundle cache with git" do
source "https://gem.repo1"
gem "foo", :git => '#{lib_path("foo-1.0")}'
G
- bundle "config set cache_all true"
# The algorithm for the cache location for a git checkout is
# in Bundle::Source::Git#cache_path
@@ -460,7 +452,7 @@ RSpec.describe "bundle cache with git" do
bundle :cache, "all-platforms" => true, :install => false
# it did _NOT_ actually install the gem - neither in $GEM_HOME (bundler 2 mode),
- # nor in .bundle (bundler 3 mode)
+ # nor in .bundle (bundler 4 mode)
expect(Pathname.new(File.join(default_bundle_path, "gems/foo-1.0-#{ref}"))).to_not exist
# it _did_ cache the gem in vendor/
expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
@@ -506,7 +498,6 @@ RSpec.describe "bundle cache with git" do
end
FileUtils.mkdir_p(bundled_app("vendor/cache"))
- bundle "config set cache_all all"
install_gemfile <<-G
source "https://gem.repo1"
diff --git a/spec/bundler/cache/path_spec.rb b/spec/bundler/cache/path_spec.rb
index 966cb6f531..42648aea1f 100644
--- a/spec/bundler/cache/path_spec.rb
+++ b/spec/bundler/cache/path_spec.rb
@@ -9,7 +9,6 @@ RSpec.describe "bundle cache with path" do
gem "foo", :path => '#{bundled_app("lib/foo")}'
G
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/foo-1.0")).not_to exist
expect(the_bundle).to include_gems "foo 1.0"
@@ -23,7 +22,6 @@ RSpec.describe "bundle cache with path" do
gem "foo", :path => '#{lib_path("foo-1.0")}'
G
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/foo-1.0")).to exist
expect(bundled_app("vendor/cache/foo-1.0/.bundlecache")).to be_file
@@ -42,7 +40,6 @@ RSpec.describe "bundle cache with path" do
gem "#{libname}", :path => '#{libpath}'
G
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/#{libname}")).to exist
expect(bundled_app("vendor/cache/#{libname}/.bundlecache")).to be_file
@@ -58,7 +55,6 @@ RSpec.describe "bundle cache with path" do
gem "foo", :path => '#{lib_path("foo-1.0")}'
G
- bundle "config set cache_all true"
bundle :cache
build_lib "foo" do |s|
@@ -81,7 +77,6 @@ RSpec.describe "bundle cache with path" do
gem "foo", :path => '#{lib_path("foo-1.0")}'
G
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/foo-1.0")).to exist
@@ -97,20 +92,21 @@ RSpec.describe "bundle cache with path" do
expect(bundled_app("vendor/cache/foo-1.0")).not_to exist
end
- it "does not cache path gems by default", bundler: "< 3" do
+ it "does not cache path gems if cache_all is set to false" do
build_lib "foo"
install_gemfile <<-G
source "https://gem.repo1"
gem "foo", :path => '#{lib_path("foo-1.0")}'
G
+ bundle_config "cache_all false"
bundle :cache
expect(err).to be_empty
expect(bundled_app("vendor/cache/foo-1.0")).not_to exist
end
- it "caches path gems by default", bundler: "3" do
+ it "caches path gems by default" do
build_lib "foo"
install_gemfile <<-G
@@ -122,48 +118,4 @@ RSpec.describe "bundle cache with path" do
expect(err).to be_empty
expect(bundled_app("vendor/cache/foo-1.0")).to exist
end
-
- it "stores the given flag" do
- build_lib "foo"
-
- install_gemfile <<-G
- source "https://gem.repo1"
- gem "foo", :path => '#{lib_path("foo-1.0")}'
- G
-
- bundle "config set cache_all true"
- bundle :cache
- build_lib "bar"
-
- install_gemfile <<-G
- source "https://gem.repo1"
- gem "foo", :path => '#{lib_path("foo-1.0")}'
- gem "bar", :path => '#{lib_path("bar-1.0")}'
- G
-
- bundle :cache
- expect(bundled_app("vendor/cache/bar-1.0")).to exist
- end
-
- it "can rewind chosen configuration" do
- build_lib "foo"
-
- install_gemfile <<-G
- source "https://gem.repo1"
- gem "foo", :path => '#{lib_path("foo-1.0")}'
- G
-
- bundle "config set cache_all true"
- bundle :cache
- build_lib "baz"
-
- gemfile <<-G
- source "https://gem.repo1"
- gem "foo", :path => '#{lib_path("foo-1.0")}'
- gem "baz", :path => '#{lib_path("baz-1.0")}'
- G
-
- bundle "cache --no-all", raise_on_error: false
- expect(bundled_app("vendor/cache/baz-1.0")).not_to exist
- end
end
diff --git a/spec/bundler/commands/add_spec.rb b/spec/bundler/commands/add_spec.rb
index e63b0b9658..162650f2e5 100644
--- a/spec/bundler/commands/add_spec.rb
+++ b/spec/bundler/commands/add_spec.rb
@@ -38,34 +38,34 @@ RSpec.describe "bundle add" do
end
describe "without version specified" do
- it "version requirement becomes ~> major.minor.patch when resolved version is < 1.0" do
+ it "version requirement becomes >= major.minor.patch when resolved version is < 1.0" do
bundle "add 'bar'"
- expect(bundled_app_gemfile.read).to match(/gem "bar", "~> 0.12.3"/)
+ expect(bundled_app_gemfile.read).to match(/gem "bar", ">= 0.12.3"/)
expect(the_bundle).to include_gems "bar 0.12.3"
end
- it "version requirement becomes ~> major.minor when resolved version is > 1.0" do
+ it "version requirement becomes >= major.minor when resolved version is > 1.0" do
bundle "add 'baz'"
- expect(bundled_app_gemfile.read).to match(/gem "baz", "~> 1.2"/)
+ expect(bundled_app_gemfile.read).to match(/gem "baz", ">= 1.2"/)
expect(the_bundle).to include_gems "baz 1.2.3"
end
- it "version requirement becomes ~> major.minor.patch.pre when resolved version is < 1.0" do
+ it "version requirement becomes >= major.minor.patch.pre when resolved version is < 1.0" do
bundle "add 'cat'"
- expect(bundled_app_gemfile.read).to match(/gem "cat", "~> 0.12.3.pre"/)
+ expect(bundled_app_gemfile.read).to match(/gem "cat", ">= 0.12.3.pre"/)
expect(the_bundle).to include_gems "cat 0.12.3.pre"
end
- it "version requirement becomes ~> major.minor.pre when resolved version is > 1.0.pre" do
+ it "version requirement becomes >= major.minor.pre when resolved version is >= 1.0.pre" do
bundle "add 'dog'"
- expect(bundled_app_gemfile.read).to match(/gem "dog", "~> 1.1.pre"/)
+ expect(bundled_app_gemfile.read).to match(/gem "dog", ">= 1.1.pre"/)
expect(the_bundle).to include_gems "dog 1.1.3.pre"
end
- it "version requirement becomes ~> major.minor.pre.tail when resolved version has a very long tail pre version" do
+ it "version requirement becomes >= major.minor.pre.tail when resolved version has a very long tail pre version" do
bundle "add 'lemur'"
# the trailing pre purposely matches the release version to ensure that subbing the release doesn't change the pre.version"
- expect(bundled_app_gemfile.read).to match(/gem "lemur", "~> 3.1.pre.2023.1.1"/)
+ expect(bundled_app_gemfile.read).to match(/gem "lemur", ">= 3.1.pre.2023.1.1"/)
expect(the_bundle).to include_gems "lemur 3.1.1.pre.2023.1.1"
end
end
@@ -88,25 +88,25 @@ RSpec.describe "bundle add" do
describe "with --require" do
it "adds the require param for the gem" do
bundle "add 'foo' --require=foo/engine"
- expect(bundled_app_gemfile.read).to match(%r{gem "foo",(?: .*,) :require => "foo\/engine"})
+ expect(bundled_app_gemfile.read).to match(%r{gem "foo",(?: .*,) require: "foo\/engine"})
end
it "converts false to a boolean" do
bundle "add 'foo' --require=false"
- expect(bundled_app_gemfile.read).to match(/gem "foo",(?: .*,) :require => false/)
+ expect(bundled_app_gemfile.read).to match(/gem "foo",(?: .*,) require: false/)
end
end
describe "with --group" do
it "adds dependency for the specified group" do
bundle "add 'foo' --group='development'"
- expect(bundled_app_gemfile.read).to match(/gem "foo", "~> 2.0", :group => :development/)
+ expect(bundled_app_gemfile.read).to match(/gem "foo", ">= 2.0", group: :development/)
expect(the_bundle).to include_gems "foo 2.0"
end
it "adds dependency to more than one group" do
bundle "add 'foo' --group='development, test'"
- expect(bundled_app_gemfile.read).to match(/gem "foo", "~> 2.0", :groups => \[:development, :test\]/)
+ expect(bundled_app_gemfile.read).to match(/gem "foo", ">= 2.0", groups: \[:development, :test\]/)
expect(the_bundle).to include_gems "foo 2.0"
end
end
@@ -115,7 +115,7 @@ RSpec.describe "bundle add" do
it "adds dependency with specified source" do
bundle "add 'foo' --source='https://gem.repo2'"
- expect(bundled_app_gemfile.read).to match(%r{gem "foo", "~> 2.0", :source => "https://gem.repo2"})
+ expect(bundled_app_gemfile.read).to match(%r{gem "foo", ">= 2.0", source: "https://gem.repo2"})
expect(the_bundle).to include_gems "foo 2.0"
end
end
@@ -124,7 +124,7 @@ RSpec.describe "bundle add" do
it "adds dependency with specified path" do
bundle "add 'foo' --path='#{lib_path("foo-2.0")}'"
- expect(bundled_app_gemfile.read).to match(/gem "foo", "~> 2.0", :path => "#{lib_path("foo-2.0")}"/)
+ expect(bundled_app_gemfile.read).to match(/gem "foo", ">= 2.0", path: "#{lib_path("foo-2.0")}"/)
expect(the_bundle).to include_gems "foo 2.0"
end
end
@@ -133,7 +133,7 @@ RSpec.describe "bundle add" do
it "adds dependency with specified git source" do
bundle "add foo --git=#{lib_path("foo-2.0")}"
- expect(bundled_app_gemfile.read).to match(/gem "foo", "~> 2.0", :git => "#{lib_path("foo-2.0")}"/)
+ expect(bundled_app_gemfile.read).to match(/gem "foo", ">= 2.0", git: "#{lib_path("foo-2.0")}"/)
expect(the_bundle).to include_gems "foo 2.0"
end
end
@@ -146,7 +146,7 @@ RSpec.describe "bundle add" do
it "adds dependency with specified git source and branch" do
bundle "add foo --git=#{lib_path("foo-2.0")} --branch=test"
- expect(bundled_app_gemfile.read).to match(/gem "foo", "~> 2.0", :git => "#{lib_path("foo-2.0")}", :branch => "test"/)
+ expect(bundled_app_gemfile.read).to match(/gem "foo", ">= 2.0", git: "#{lib_path("foo-2.0")}", branch: "test"/)
expect(the_bundle).to include_gems "foo 2.0"
end
end
@@ -155,32 +155,53 @@ RSpec.describe "bundle add" do
it "adds dependency with specified git source and branch" do
bundle "add foo --git=#{lib_path("foo-2.0")} --ref=#{revision_for(lib_path("foo-2.0"))}"
- expect(bundled_app_gemfile.read).to match(/gem "foo", "~> 2\.0", :git => "#{lib_path("foo-2.0")}", :ref => "#{revision_for(lib_path("foo-2.0"))}"/)
+ expect(bundled_app_gemfile.read).to match(/gem "foo", ">= 2\.0", git: "#{lib_path("foo-2.0")}", ref: "#{revision_for(lib_path("foo-2.0"))}"/)
expect(the_bundle).to include_gems "foo 2.0"
end
end
describe "with --github" do
+ before do
+ build_git "rake", "13.0"
+ git("config --global url.file://#{lib_path("rake-13.0")}.insteadOf https://github.com/ruby/rake.git")
+ end
+
it "adds dependency with specified github source" do
bundle "add rake --github=ruby/rake"
- expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.\d+", :github => "ruby\/rake"})
+ expect(bundled_app_gemfile.read).to match(%r{gem "rake", ">= 13\.\d+", github: "ruby\/rake"})
end
- end
- describe "with --github and --branch" do
it "adds dependency with specified github source and branch" do
- bundle "add rake --github=ruby/rake --branch=master"
+ bundle "add rake --github=ruby/rake --branch=main"
- expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.\d+", :github => "ruby\/rake", :branch => "master"})
+ expect(bundled_app_gemfile.read).to match(%r{gem "rake", ">= 13\.\d+", github: "ruby\/rake", branch: "main"})
end
- end
- describe "with --github and --ref" do
it "adds dependency with specified github source and ref" do
- bundle "add rake --github=ruby/rake --ref=5c60da8"
+ ref = revision_for(lib_path("rake-13.0"))
+ bundle "add rake --github=ruby/rake --ref=#{ref}"
+
+ expect(bundled_app_gemfile.read).to match(%r{gem "rake", ">= 13\.\d+", github: "ruby\/rake", ref: "#{ref}"})
+ end
+
+ it "adds dependency with specified github source and glob" do
+ bundle "add rake --github=ruby/rake --glob='./*.gemspec'"
- expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.\d+", :github => "ruby\/rake", :ref => "5c60da8"})
+ expect(bundled_app_gemfile.read).to match(%r{gem "rake", ">= 13\.\d+", github: "ruby\/rake", glob: "\.\/\*\.gemspec"})
+ end
+
+ it "adds dependency with specified github source, branch and glob" do
+ bundle "add rake --github=ruby/rake --branch=main --glob='./*.gemspec'"
+
+ expect(bundled_app_gemfile.read).to match(%r{gem "rake", ">= 13\.\d+", github: "ruby\/rake", branch: "main", glob: "\.\/\*\.gemspec"})
+ end
+
+ it "adds dependency with specified github source, ref and glob" do
+ ref = revision_for(lib_path("rake-13.0"))
+ bundle "add rake --github=ruby/rake --ref=#{ref} --glob='./*.gemspec'"
+
+ expect(bundled_app_gemfile.read).to match(%r{gem "rake", ">= 13\.\d+", github: "ruby\/rake", ref: "#{ref}", glob: "\.\/\*\.gemspec"})
end
end
@@ -188,7 +209,7 @@ RSpec.describe "bundle add" do
it "adds dependency with specified git source" do
bundle "add foo --git=#{lib_path("foo-2.0")} --glob='./*.gemspec'"
- expect(bundled_app_gemfile.read).to match(%r{gem "foo", "~> 2.0", :git => "#{lib_path("foo-2.0")}", :glob => "\./\*\.gemspec"})
+ expect(bundled_app_gemfile.read).to match(%r{gem "foo", ">= 2.0", git: "#{lib_path("foo-2.0")}", glob: "\./\*\.gemspec"})
expect(the_bundle).to include_gems "foo 2.0"
end
end
@@ -201,7 +222,7 @@ RSpec.describe "bundle add" do
it "adds dependency with specified git source and branch" do
bundle "add foo --git=#{lib_path("foo-2.0")} --branch=test --glob='./*.gemspec'"
- expect(bundled_app_gemfile.read).to match(%r{gem "foo", "~> 2.0", :git => "#{lib_path("foo-2.0")}", :branch => "test", :glob => "\./\*\.gemspec"})
+ expect(bundled_app_gemfile.read).to match(%r{gem "foo", ">= 2.0", git: "#{lib_path("foo-2.0")}", branch: "test", glob: "\./\*\.gemspec"})
expect(the_bundle).to include_gems "foo 2.0"
end
end
@@ -210,32 +231,42 @@ RSpec.describe "bundle add" do
it "adds dependency with specified git source and branch" do
bundle "add foo --git=#{lib_path("foo-2.0")} --ref=#{revision_for(lib_path("foo-2.0"))} --glob='./*.gemspec'"
- expect(bundled_app_gemfile.read).to match(%r{gem "foo", "~> 2\.0", :git => "#{lib_path("foo-2.0")}", :ref => "#{revision_for(lib_path("foo-2.0"))}", :glob => "\./\*\.gemspec"})
+ expect(bundled_app_gemfile.read).to match(%r{gem "foo", ">= 2\.0", git: "#{lib_path("foo-2.0")}", ref: "#{revision_for(lib_path("foo-2.0"))}", glob: "\./\*\.gemspec"})
expect(the_bundle).to include_gems "foo 2.0"
end
end
- describe "with --github and --glob" do
- it "adds dependency with specified github source" do
- bundle "add rake --github=ruby/rake --glob='./*.gemspec'"
+ describe "with mismatched pair in --git/--github, --branch/--ref" do
+ describe "with --git and --github" do
+ it "throws error" do
+ bundle "add 'foo' --git x --github y", raise_on_error: false
- expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.\d+", :github => "ruby\/rake", :glob => "\.\/\*\.gemspec"})
+ expect(err).to include("You cannot specify `--git` and `--github` at the same time.")
+ end
end
- end
- describe "with --github and --branch --and glob" do
- it "adds dependency with specified github source and branch" do
- bundle "add rake --github=ruby/rake --branch=master --glob='./*.gemspec'"
+ describe "with --branch and --ref with --git" do
+ it "throws error" do
+ bundle "add 'foo' --branch x --ref y --git file://git", raise_on_error: false
- expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.\d+", :github => "ruby\/rake", :branch => "master", :glob => "\.\/\*\.gemspec"})
+ expect(err).to include("You cannot specify `--branch` and `--ref` at the same time.")
+ end
end
- end
- describe "with --github and --ref and --glob" do
- it "adds dependency with specified github source and ref" do
- bundle "add rake --github=ruby/rake --ref=5c60da8 --glob='./*.gemspec'"
+ describe "with --branch but without --git or --github" do
+ it "throws error" do
+ bundle "add 'foo' --branch x", raise_on_error: false
- expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.\d+", :github => "ruby\/rake", :ref => "5c60da8", :glob => "\.\/\*\.gemspec"})
+ expect(err).to include("You cannot specify `--branch` unless `--git` or `--github` is specified.")
+ end
+ end
+
+ describe "with --ref but without --git or --github" do
+ it "throws error" do
+ bundle "add 'foo' --ref y", raise_on_error: false
+
+ expect(err).to include("You cannot specify `--ref` unless `--git` or `--github` is specified.")
+ end
end
end
@@ -250,7 +281,7 @@ RSpec.describe "bundle add" do
it "using combination of short form options works like long form" do
bundle "add 'foo' -s='https://gem.repo2' -g='development' -v='~>1.0'"
- expect(bundled_app_gemfile.read).to include %(gem "foo", "~> 1.0", :group => :development, :source => "https://gem.repo2")
+ expect(bundled_app_gemfile.read).to include %(gem "foo", "~> 1.0", group: :development, source: "https://gem.repo2")
expect(the_bundle).to include_gems "foo 1.1"
end
@@ -260,7 +291,7 @@ RSpec.describe "bundle add" do
end
it "shows error message when gem cannot be found" do
- bundle "config set force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
bundle "add 'werk_it'", raise_on_error: false
expect(err).to match("Could not find gem 'werk_it' in")
@@ -277,13 +308,21 @@ RSpec.describe "bundle add" do
end
describe "with --optimistic" do
- it "adds optimistic version" do
+ it "ignores option" do
bundle "add 'foo' --optimistic"
expect(bundled_app_gemfile.read).to include %(gem "foo", ">= 2.0")
expect(the_bundle).to include_gems "foo 2.0"
end
end
+ describe "with --pessimistic" do
+ it "adds pessimistic version" do
+ bundle "add 'foo' --pessimistic"
+ expect(bundled_app_gemfile.read).to include %(gem "foo", "~> 2.0")
+ expect(the_bundle).to include_gems "foo 2.0"
+ end
+ end
+
describe "with --quiet option" do
it "is quiet when there are no warnings" do
bundle "add 'foo' --quiet"
@@ -325,18 +364,18 @@ RSpec.describe "bundle add" do
end
describe "with no option" do
- it "adds pessimistic version" do
+ it "adds optimistic version" do
bundle "add 'foo'"
- expect(bundled_app_gemfile.read).to include %(gem "foo", "~> 2.0")
+ expect(bundled_app_gemfile.read).to include %(gem "foo", ">= 2.0")
expect(the_bundle).to include_gems "foo 2.0"
end
end
- describe "with --optimistic and --strict" do
+ describe "with --pessimistic and --strict" do
it "throws error" do
- bundle "add 'foo' --strict --optimistic", raise_on_error: false
+ bundle "add 'foo' --strict --pessimistic", raise_on_error: false
- expect(err).to include("You cannot specify `--strict` and `--optimistic` at the same time")
+ expect(err).to include("You cannot specify `--strict` and `--pessimistic` at the same time")
end
end
@@ -344,8 +383,8 @@ RSpec.describe "bundle add" do
it "adds multiple gems to gemfile" do
bundle "add bar baz"
- expect(bundled_app_gemfile.read).to match(/gem "bar", "~> 0.12.3"/)
- expect(bundled_app_gemfile.read).to match(/gem "baz", "~> 1.2"/)
+ expect(bundled_app_gemfile.read).to match(/gem "bar", ">= 0.12.3"/)
+ expect(bundled_app_gemfile.read).to match(/gem "baz", ">= 1.2"/)
end
it "throws error if any of the specified gems are present in the gemfile with different version" do
diff --git a/spec/bundler/commands/binstubs_spec.rb b/spec/bundler/commands/binstubs_spec.rb
index 4da6dea16e..af4d24a9e8 100644
--- a/spec/bundler/commands/binstubs_spec.rb
+++ b/spec/bundler/commands/binstubs_spec.rb
@@ -85,220 +85,6 @@ RSpec.describe "bundle binstubs <gem>" do
expect(err).to include("Cannot specify --all with specific gems")
end
- context "when generating bundle binstub outside bundler" do
- it "should abort" do
- install_gemfile <<-G
- source "https://gem.repo1"
- gem "myrack"
- G
-
- bundle "binstubs myrack"
-
- File.open(bundled_app("bin/bundle"), "wb") do |file|
- file.print "OMG"
- end
-
- sys_exec "bin/myrackup", raise_on_error: false
-
- expect(err).to include("was not generated by Bundler")
- end
- end
-
- context "the bundle binstub" do
- before do
- pristine_system_gems "bundler-#{system_bundler_version}"
- build_repo2 do
- build_gem "myrack", "1.2" do |s|
- s.executables = "myrackup"
- end
-
- build_gem "prints_loaded_gems", "1.0" do |s|
- s.executables = "print_loaded_gems"
- s.bindir = "exe"
- s.write "exe/print_loaded_gems", <<~R
- #!/usr/bin/env ruby
- specs = Gem.loaded_specs.values.reject {|s| s.default_gem? }
- puts specs.map(&:full_name).sort.inspect
- R
- end
-
- build_bundler locked_bundler_version
- end
- install_gemfile <<-G
- source "https://gem.repo2"
- gem "myrack"
- gem "prints_loaded_gems"
- G
- bundle "binstubs bundler myrack prints_loaded_gems"
- end
-
- let(:system_bundler_version) { Bundler::VERSION }
- let(:locked_bundler_version) { nil }
- let(:lockfile_content) { lockfile.gsub(system_bundler_version, locked_bundler_version) }
-
- it "runs bundler" do
- bundle "install --verbose", bundle_bin: "bin/bundle"
- expect(out).to include %(Using bundler #{system_bundler_version}\n)
- end
-
- context "when BUNDLER_VERSION is set" do
- it "runs the correct version of bundler" do
- bundle "install", env: { "BUNDLER_VERSION" => "999.999.999" }, raise_on_error: false, bundle_bin: "bin/bundle"
- expect(exitstatus).to eq(42)
- expect(err).to include("Activating bundler (999.999.999) failed:").
- and include("To install the version of bundler this project requires, run `gem install bundler -v '999.999.999'`")
- end
-
- it "runs the correct version of bundler even if a higher version is installed" do
- system_gems "bundler-999.999.998", "bundler-999.999.999"
-
- bundle "install --verbose", env: { "BUNDLER_VERSION" => "999.999.998" }, raise_on_error: false, bundle_bin: "bin/bundle"
- expect(out).to include %(Using bundler 999.999.998\n)
- end
- end
-
- context "when a lockfile exists with a locked bundler version" do
- let(:locked_bundler_version) { "999.999" }
-
- context "and the version is newer" do
- before do
- lockfile lockfile_content
- end
-
- it "runs the correct version of bundler" do
- bundle "install", raise_on_error: false, bundle_bin: "bin/bundle"
- expect(exitstatus).to eq(42)
- expect(err).to include("Activating bundler (~> 999.999) failed:").
- and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 999.999'`")
- end
- end
-
- context "and the version is newer when given `gems.rb` and `gems.locked`" do
- before do
- gemfile bundled_app("gems.rb"), gemfile
- lockfile bundled_app("gems.locked"), lockfile_content
- end
-
- it "runs the correct version of bundler" do
- bundle "install", env: { "BUNDLE_GEMFILE" => "gems.rb" }, raise_on_error: false, bundle_bin: "bin/bundle"
-
- expect(exitstatus).to eq(42)
- expect(err).to include("Activating bundler (~> 999.999) failed:").
- and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 999.999'`")
- end
- end
-
- context "and the version is older and a different major" do
- let(:system_bundler_version) { "55" }
- let(:locked_bundler_version) { "44" }
-
- before do
- lockfile lockfile_content
- end
-
- it "runs the correct version of bundler" do
- bundle "install", raise_on_error: false, bundle_bin: "bin/bundle"
- expect(exitstatus).to eq(42)
- expect(err).to include("Activating bundler (~> 44.0) failed:").
- and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 44.0'`")
- end
- end
-
- context "and the version is older and a different major when given `gems.rb` and `gems.locked`" do
- let(:system_bundler_version) { "55" }
- let(:locked_bundler_version) { "44" }
-
- before do
- gemfile bundled_app("gems.rb"), gemfile
- lockfile bundled_app("gems.locked"), lockfile_content
- end
-
- it "runs the correct version of bundler" do
- bundle "install", env: { "BUNDLE_GEMFILE" => "gems.rb" }, raise_on_error: false, bundle_bin: "bin/bundle"
- expect(exitstatus).to eq(42)
- expect(err).to include("Activating bundler (~> 44.0) failed:").
- and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 44.0'`")
- end
- end
-
- context "and the version is older and the same major" do
- let(:system_bundler_version) { "2.999.999" }
- let(:locked_bundler_version) { "2.3.0" }
-
- before do
- lockfile lockfile_content
- end
-
- it "installs and runs the exact version of bundler" do
- bundle "install --verbose", bundle_bin: "bin/bundle"
- expect(exitstatus).not_to eq(42)
- expect(out).to include("Bundler 2.999.999 is running, but your lockfile was generated with 2.3.0. Installing Bundler 2.3.0 and restarting using that version.")
- expect(out).to include("Using bundler 2.3.0")
- expect(err).not_to include("Activating bundler (~> 2.3.0) failed:")
- end
- end
-
- context "and the version is a pre-releaser" do
- let(:system_bundler_version) { "55" }
- let(:locked_bundler_version) { "2.12.0.a" }
-
- before do
- lockfile lockfile_content
- end
-
- it "runs the correct version of bundler when the version is a pre-release" do
- bundle "install", raise_on_error: false, bundle_bin: "bin/bundle"
- expect(exitstatus).to eq(42)
- expect(err).to include("Activating bundler (~> 2.12.a) failed:").
- and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 2.12.a'`")
- end
- end
- end
-
- context "when update --bundler is called" do
- it "calls through to the latest bundler version" do
- bundle "update --bundler --verbose", bundle_bin: "bin/bundle"
- using_bundler_line = /Using bundler ([\w\.]+)\n/.match(out)
- expect(using_bundler_line).to_not be_nil
- latest_version = using_bundler_line[1]
- expect(Gem::Version.new(latest_version)).to be >= Gem::Version.new(system_bundler_version)
- end
-
- it "calls through to the explicit bundler version" do
- bundle "update --bundler=999.999.999", raise_on_error: false, bundle_bin: "bin/bundle"
- expect(exitstatus).to eq(42)
- expect(err).to include("Activating bundler (999.999.999) failed:").
- and include("To install the version of bundler this project requires, run `gem install bundler -v '999.999.999'`")
- end
- end
-
- context "without a lockfile" do
- it "falls back to the latest installed bundler" do
- FileUtils.rm bundled_app_lock
- bundle "install --verbose", bundle_bin: "bin/bundle"
- expect(out).to include "Using bundler #{system_bundler_version}\n"
- end
- end
-
- context "using another binstub" do
- it "loads all gems" do
- sys_exec bundled_app("bin/print_loaded_gems").to_s
- expect(out).to eq %(["bundler-#{Bundler::VERSION}", "myrack-1.2", "prints_loaded_gems-1.0"])
- end
-
- context "when requesting a different bundler version" do
- before { lockfile lockfile.gsub(Bundler::VERSION, "999.999.999") }
-
- it "attempts to load that version" do
- sys_exec bundled_app("bin/myrackup").to_s, raise_on_error: false
- expect(exitstatus).to eq(42)
- expect(err).to include("Activating bundler (~> 999.999) failed:").
- and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 999.999'`")
- end
- end
- end
- end
-
it "installs binstubs from git gems" do
FileUtils.mkdir_p(lib_path("foo/bin"))
FileUtils.touch(lib_path("foo/bin/foo"))
@@ -370,29 +156,20 @@ RSpec.describe "bundle binstubs <gem>" do
end
end
- context "--path" do
- it "sets the binstubs dir" do
- install_gemfile <<-G
- source "https://gem.repo1"
- gem "myrack"
- G
-
- bundle "binstubs myrack --path exec"
-
- expect(bundled_app("exec/myrackup")).to exist
+ context "with the binstubs dir configured" do
+ before do
+ bundle_config "bin exec"
end
- it "setting is saved for bundle install", bundler: "< 3" do
+ it "creates the binstubs in the configured dir" do
install_gemfile <<-G
source "https://gem.repo1"
gem "myrack"
- gem "rails"
G
- bundle "binstubs myrack", path: "exec"
- bundle :install
+ bundle "binstubs myrack"
- expect(bundled_app("exec/rails")).to exist
+ expect(bundled_app("exec/myrackup")).to exist
end
end
@@ -415,11 +192,10 @@ RSpec.describe "bundle binstubs <gem>" do
expect(File.read(bundled_app("bin/myrackup"))).to_not include("Gem.bin_path")
end
- context "when specified --path option" do
- it "generates a standalone binstub at the given path" do
- bundle "binstubs myrack --standalone --path foo"
- expect(bundled_app("foo/myrackup")).to exist
- end
+ it "generates a standalone binstub at the given path when configured" do
+ bundle_config "bin foo"
+ bundle "binstubs myrack --standalone"
+ expect(bundled_app("foo/myrackup")).to exist
end
context "when specified --all-platforms option" do
@@ -537,7 +313,7 @@ RSpec.describe "bundle binstubs <gem>" do
gem "myrack"
G
- bundle "config set auto_install 1"
+ bundle_config "auto_install 1"
bundle "binstubs myrack"
expect(out).to include("Installing myrack 1.0.0")
expect(the_bundle).to include_gems "myrack 1.0.0"
@@ -549,7 +325,7 @@ RSpec.describe "bundle binstubs <gem>" do
gem "myrack"
G
- bundle "config set auto_install 1"
+ bundle_config "auto_install 1"
bundle "binstubs myrack", env: { "BUNDLE_INSTALL" => "1" }
expect(out).not_to include("Installing myrack 1.0.0")
end
diff --git a/spec/bundler/commands/cache_spec.rb b/spec/bundler/commands/cache_spec.rb
index ab8eb06838..e223d07f7f 100644
--- a/spec/bundler/commands/cache_spec.rb
+++ b/spec/bundler/commands/cache_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe "bundle cache" do
end
end
- context "with --all" do
+ context "with cache_all configured" do
context "without a gemspec" do
it "caches all dependencies except bundler itself" do
gemfile <<-D
@@ -37,7 +37,7 @@ RSpec.describe "bundle cache" do
gem 'bundler'
D
- bundle "config set cache_all true"
+ bundle_config "cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
@@ -68,7 +68,7 @@ RSpec.describe "bundle cache" do
gemspec
D
- bundle "config set cache_all true"
+ bundle_config "cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
@@ -100,7 +100,7 @@ RSpec.describe "bundle cache" do
gemspec
D
- bundle "config set cache_all true"
+ bundle_config "cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
@@ -145,7 +145,7 @@ RSpec.describe "bundle cache" do
gemspec :name => 'mygem_test'
D
- bundle "config set cache_all true"
+ bundle_config "cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
@@ -158,20 +158,6 @@ RSpec.describe "bundle cache" do
end
end
- context "with --path", bundler: "< 3" do
- it "sets root directory for gems" do
- gemfile <<-D
- source "https://gem.repo1"
- gem 'myrack'
- D
-
- bundle "cache --path #{bundled_app("test")}"
-
- expect(the_bundle).to include_gems "myrack 1.0.0"
- expect(bundled_app("test/vendor/cache/")).to exist
- end
- end
-
context "with --no-install" do
it "puts the gems in vendor/cache but does not install them" do
gemfile <<-D
@@ -211,7 +197,7 @@ RSpec.describe "bundle cache" do
end
context "with --all-platforms" do
- it "puts the gems in vendor/cache even for other rubies", bundler: ">= 2.4.0" do
+ it "puts the gems in vendor/cache even for other rubies" do
gemfile <<-D
source "https://gem.repo1"
gem 'myrack', :platforms => [:ruby_20, :windows_20]
@@ -221,13 +207,14 @@ RSpec.describe "bundle cache" do
expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
end
- it "puts the gems in vendor/cache even for legacy windows rubies", bundler: ">= 2.4.0" do
+ it "prints a warn when using legacy windows rubies" do
gemfile <<-D
source "https://gem.repo1"
gem 'myrack', :platforms => [:ruby_20, :x64_mingw_20]
D
- bundle "cache --all-platforms"
+ bundle "cache --all-platforms", raise_on_error: false
+ expect(err).to include("will be removed in the future")
expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
end
@@ -240,7 +227,7 @@ RSpec.describe "bundle cache" do
end
end
- bundle "config set --local without wo"
+ bundle_config "without wo"
install_gemfile <<-G, artifice: "compact_index_extra_api"
source "https://main.repo"
gem "myrack"
@@ -256,14 +243,14 @@ RSpec.describe "bundle cache" do
expect(the_bundle).to include_gem "myrack 1.0"
expect(the_bundle).not_to include_gems "weakling", "uninstallable"
- bundle "config set --local without wo"
+ bundle_config "without wo"
bundle :install, artifice: "compact_index_extra_api"
expect(the_bundle).to include_gem "myrack 1.0"
expect(the_bundle).not_to include_gems "weakling"
end
it "does not fail to cache gems in excluded groups when there's a lockfile but gems not previously installed" do
- bundle "config set --local without wo"
+ bundle_config "without wo"
gemfile <<-G
source "https://gem.repo1"
gem "myrack"
@@ -279,33 +266,107 @@ RSpec.describe "bundle cache" do
end
context "with frozen configured" do
+ let(:app_cache) { bundled_app("vendor/cache") }
+
before do
+ bundle_config "frozen true"
+ end
+
+ it "tries to install but fails when the lockfile is out of sync" do
gemfile <<-G
source "https://gem.repo1"
gem "myrack"
G
- bundle "install"
- end
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo1/
+ specs:
+ myrack (1.0.0)
+ myrack-obama (1.0)
+ myrack
+
+ PLATFORMS
+ #{lockfile_platforms}
- subject do
- bundle "config set --local frozen true"
+ DEPENDENCIES
+ myrack
+ myrack-obama
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
bundle :cache, raise_on_error: false
+ expect(exitstatus).to eq(16)
+ expect(err).to include("frozen mode")
+ expect(err).to include("You have deleted from the Gemfile")
+ expect(err).to include("* myrack-obama")
+ bundle "env"
+ expect(out).to include("frozen")
end
- it "tries to install with frozen" do
- bundle "config set deployment true"
+ it "caches gems without installing when lockfile is in sync, and --no-install is passed, even if vendor/cache directory is initially empty" do
gemfile <<-G
source "https://gem.repo1"
gem "myrack"
- gem "myrack-obama"
G
- subject
- expect(exitstatus).to eq(16)
- expect(err).to include("frozen mode")
- expect(err).to include("You have added to the Gemfile")
- expect(err).to include("* myrack-obama")
- bundle "env"
- expect(out).to include("frozen").or include("deployment")
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo1/
+ specs:
+ myrack (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ FileUtils.mkdir_p app_cache
+
+ bundle "cache --no-install"
+ expect(out).not_to include("Installing myrack 1.0.0")
+ expect(out).to include("Fetching myrack 1.0.0")
+ expect(app_cache.join("myrack-1.0.0.gem")).to exist
+ end
+
+ it "completes a partial cache when lockfile is in sync, even if the already cached gem is no longer available remotely" do
+ build_repo4 do
+ build_gem "foo", "1.0.0"
+ end
+
+ build_gem "bar", "1.0.0", path: bundled_app("vendor/cache")
+
+ gemfile <<-G
+ source "https://gem.repo4"
+ gem "foo"
+ gem "bar"
+ G
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ foo (1.0.0)
+ bar (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo
+ bar
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "cache --no-install"
+ expect(out).to include("Fetching foo 1.0.0")
+ expect(out).not_to include("Fetching bar 1.0.0")
+ expect(app_cache.join("foo-1.0.0.gem")).to exist
+ expect(app_cache.join("bar-1.0.0.gem")).to exist
end
end
@@ -328,7 +389,7 @@ RSpec.describe "bundle cache" do
it "installs them properly from cache to a different path" do
bundle "cache"
- bundle "config set --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install --local"
end
end
@@ -344,8 +405,8 @@ RSpec.describe "bundle install with gem sources" do
G
bundle :cache
- simulate_new_machine
- FileUtils.rm_rf gem_repo2
+ pristine_system_gems
+ FileUtils.rm_r gem_repo2
bundle "install --local"
expect(the_bundle).to include_gems "myrack 1.0.0"
@@ -359,11 +420,27 @@ RSpec.describe "bundle install with gem sources" do
G
bundle :cache
- simulate_new_machine
- FileUtils.rm_rf gem_repo2
+ pristine_system_gems
+ FileUtils.rm_r gem_repo2
- bundle "config set --local deployment true"
- bundle "config set --local path vendor/bundle"
+ bundle_config "deployment true"
+ bundle_config "path vendor/bundle"
+ bundle :install
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+
+ it "does not hit the remote at all in non frozen mode either" do
+ build_repo2
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "myrack"
+ G
+
+ bundle :cache
+ pristine_system_gems
+ FileUtils.rm_r gem_repo2
+
+ bundle_config "path vendor/bundle"
bundle :install
expect(the_bundle).to include_gems "myrack 1.0.0"
end
@@ -376,11 +453,11 @@ RSpec.describe "bundle install with gem sources" do
G
bundle :cache
- simulate_new_machine
- FileUtils.rm_rf gem_repo2
+ pristine_system_gems
+ FileUtils.rm_r gem_repo2
- bundle "config set --local cache_all_platforms true"
- bundle "config set --local path vendor/bundle"
+ bundle_config "cache_all_platforms true"
+ bundle_config "path vendor/bundle"
bundle "install --local"
expect(out).not_to include("Fetching gem metadata")
expect(the_bundle).to include_gems "myrack 1.0.0"
@@ -425,20 +502,19 @@ RSpec.describe "bundle install with gem sources" do
foo
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
simulate_platform "x86_64-linux" do
- bundle "config set cache_all_platforms true"
- bundle "config set path vendor/bundle"
+ bundle_config "cache_all_platforms true"
+ bundle_config "path vendor/bundle"
bundle :cache, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
- build_repo4 do
- # simulate removal of all remote gems
- end
+ # simulate removal of all remote gems
+ empty_repo4
# delete compact index cache
- FileUtils.rm_rf home(".bundle/cache/compact_index")
+ FileUtils.rm_r home(".bundle/cache/compact_index")
bundle "install", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
@@ -471,9 +547,9 @@ RSpec.describe "bundle install with gem sources" do
bundle :cache
end
- simulate_new_machine
+ pristine_system_gems
- bundle "config set --local force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
install_gemfile <<-G
source "https://gem.repo1"
@@ -482,6 +558,53 @@ RSpec.describe "bundle install with gem sources" do
expect(the_bundle).to include_gems("platform_specific 1.0 ruby")
end
+ it "keeps gems that are locked and cached for the current platform, even if incompatible with the current ruby" do
+ build_repo4 do
+ build_gem "bcrypt_pbkdf", "1.1.1"
+ build_gem "bcrypt_pbkdf", "1.1.1" do |s|
+ s.platform = "arm64-darwin"
+ s.required_ruby_version = "< #{current_ruby_minor}"
+ end
+ end
+
+ app_cache = bundled_app("vendor/cache")
+ FileUtils.mkdir_p app_cache
+ FileUtils.cp gem_repo4("gems/bcrypt_pbkdf-1.1.1-arm64-darwin.gem"), app_cache
+ FileUtils.cp gem_repo4("gems/bcrypt_pbkdf-1.1.1.gem"), app_cache
+
+ bundle_config "cache_all_platforms true"
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ bcrypt_pbkdf (1.1.1)
+ bcrypt_pbkdf (1.1.1-arm64-darwin)
+
+ PLATFORMS
+ arm64-darwin
+ ruby
+
+ DEPENDENCIES
+ bcrypt_pbkdf
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "arm64-darwin-23" do
+ install_gemfile <<~G, verbose: true
+ source "https://gem.repo4"
+ gem "bcrypt_pbkdf"
+ G
+
+ expect(out).to include("Updating files in vendor/cache")
+ expect(err).to be_empty
+ expect(app_cache.join("bcrypt_pbkdf-1.1.1-arm64-darwin.gem")).to exist
+ expect(app_cache.join("bcrypt_pbkdf-1.1.1.gem")).to exist
+ end
+ end
+
it "does not update the cache if --no-cache is passed" do
gemfile <<-G
source "https://gem.repo1"
diff --git a/spec/bundler/commands/check_spec.rb b/spec/bundler/commands/check_spec.rb
index 78c2aef58b..7fe6897ae3 100644
--- a/spec/bundler/commands/check_spec.rb
+++ b/spec/bundler/commands/check_spec.rb
@@ -57,7 +57,7 @@ RSpec.describe "bundle check" do
bundle :check, raise_on_error: false
expect(err).to include("The following gems are missing")
- expect(err).to include(" * rake (13.2.1)")
+ expect(err).to include(" * rake (#{rake_version})")
expect(err).to include(" * actionpack (2.3.2)")
expect(err).to include(" * activerecord (2.3.2)")
expect(err).to include(" * actionmailer (2.3.2)")
@@ -76,7 +76,7 @@ RSpec.describe "bundle check" do
expect(exitstatus).to be > 0
expect(err).to include("The following gems are missing")
expect(err).to include(" * rails (2.3.2)")
- expect(err).to include(" * rake (13.2.1)")
+ expect(err).to include(" * rake (#{rake_version})")
expect(err).to include(" * actionpack (2.3.2)")
expect(err).to include(" * activerecord (2.3.2)")
expect(err).to include(" * actionmailer (2.3.2)")
@@ -88,14 +88,14 @@ RSpec.describe "bundle check" do
it "prints a generic error if gem git source is not checked out" do
build_git "foo", path: lib_path("foo")
- bundle "config path vendor/bundle"
+ bundle_config "path vendor/bundle"
install_gemfile <<-G
source "https://gem.repo1"
gem "foo", git: "#{lib_path("foo")}"
G
- FileUtils.rm_rf bundled_app("vendor/bundle")
+ FileUtils.rm_r bundled_app("vendor/bundle")
bundle :check, raise_on_error: false
expect(exitstatus).to eq 1
expect(err).to include("Bundler can't satisfy your Gemfile's dependencies.")
@@ -123,21 +123,8 @@ RSpec.describe "bundle check" do
expect(err).to include("Bundler can't satisfy your Gemfile's dependencies.")
end
- it "remembers --without option from install", bundler: "< 3" do
- gemfile <<-G
- source "https://gem.repo1"
- group :foo do
- gem "myrack"
- end
- G
-
- bundle "install --without foo"
- bundle "check"
- expect(out).to include("The Gemfile's dependencies are satisfied")
- end
-
it "uses the without setting" do
- bundle "config set without foo"
+ bundle_config "without foo"
install_gemfile <<-G
source "https://gem.repo1"
group :foo do
@@ -155,7 +142,7 @@ RSpec.describe "bundle check" do
gem "myrack", :group => :foo
G
- bundle "config set --local without foo"
+ bundle_config "without foo"
bundle :install
gemfile <<-G
@@ -174,10 +161,10 @@ RSpec.describe "bundle check" do
gem "myrack"
G
- bundle "config set --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle :cache
- gem_command "uninstall myrack", env: { "GEM_HOME" => vendored_gems.to_s }
+ uninstall_gem("myrack", env: { "GEM_HOME" => vendored_gems.to_s })
bundle "check", raise_on_error: false
expect(err).to include("* myrack (1.0.0)")
@@ -258,13 +245,13 @@ RSpec.describe "bundle check" do
expect(err).not_to include("Unfortunately, a fatal error has occurred. ")
end
- it "fails when there's no lock file and frozen is set" do
+ it "fails when there's no lockfile and frozen is set" do
install_gemfile <<-G
source "https://gem.repo1"
gem "foo"
G
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
bundle "install"
FileUtils.rm(bundled_app_lock)
@@ -272,46 +259,6 @@ RSpec.describe "bundle check" do
expect(last_command).to be_failure
end
- context "--path", bundler: "< 3" do
- context "after installing gems in the proper directory" do
- before do
- gemfile <<-G
- source "https://gem.repo1"
- gem "rails"
- G
- bundle "install --path vendor/bundle"
-
- FileUtils.rm_rf(bundled_app(".bundle"))
- end
-
- it "returns success" do
- bundle "check --path vendor/bundle"
- expect(out).to include("The Gemfile's dependencies are satisfied")
- end
-
- it "should write to .bundle/config" do
- bundle "check --path vendor/bundle"
- bundle "check"
- end
- end
-
- context "after installing gems on a different directory" do
- before do
- install_gemfile <<-G
- source "https://gem.repo1"
- gem "rails"
- G
-
- bundle "check --path vendor/bundle", raise_on_error: false
- end
-
- it "returns false" do
- expect(exitstatus).to eq(1)
- expect(err).to match(/The following gems are missing/)
- end
- end
- end
-
describe "when locked" do
before :each do
system_gems "myrack-1.0.0"
@@ -328,7 +275,8 @@ RSpec.describe "bundle check" do
end
it "shows what is missing with the current Gemfile if it is not satisfied" do
- simulate_new_machine
+ FileUtils.rm_r default_bundle_path
+ default_system_gems
bundle :check, raise_on_error: false
expect(err).to match(/The following gems are missing/)
expect(err).to include("* myrack (1.0")
@@ -388,7 +336,7 @@ RSpec.describe "bundle check" do
myrack
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -463,7 +411,7 @@ RSpec.describe "bundle check" do
depends_on_myrack!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -534,7 +482,7 @@ RSpec.describe "bundle check" do
dex-dispatch-engine!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -551,7 +499,7 @@ RSpec.describe "bundle check" do
build_gem "bar"
end
- bundle "config set path.system true"
+ bundle_config "path.system true"
# Add all gems to ensure all gems are installed so that a bundle check
# would be successful
@@ -616,7 +564,7 @@ RSpec.describe "bundle check" do
end
before do
- bundle "config set --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
install_gemfile <<-G
source "https://gem.repo1"
diff --git a/spec/bundler/commands/clean_spec.rb b/spec/bundler/commands/clean_spec.rb
index c27766835e..c77859d378 100644
--- a/spec/bundler/commands/clean_spec.rb
+++ b/spec/bundler/commands/clean_spec.rb
@@ -25,8 +25,8 @@ RSpec.describe "bundle clean" do
gem "foo"
G
- bundle "config set path vendor/bundle"
- bundle "config set clean false"
+ bundle_config "path vendor/bundle"
+ bundle_config "clean false"
bundle "install"
gemfile <<-G
@@ -54,8 +54,8 @@ RSpec.describe "bundle clean" do
gem "foo"
G
- bundle "config set path vendor/bundle"
- bundle "config set clean false"
+ bundle_config "path vendor/bundle"
+ bundle_config "clean false"
bundle "install"
gemfile <<-G
@@ -84,8 +84,8 @@ RSpec.describe "bundle clean" do
gem "foo"
G
- bundle "config set path vendor/bundle"
- bundle "config set clean false"
+ bundle_config "path vendor/bundle"
+ bundle_config "clean false"
bundle "install"
gemfile <<-G
@@ -117,9 +117,9 @@ RSpec.describe "bundle clean" do
end
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
- bundle "config set without test_group"
+ bundle_config "without test_group"
bundle "install"
bundle :clean
@@ -145,13 +145,13 @@ RSpec.describe "bundle clean" do
end
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
bundle :clean
digest = Digest(:SHA1).hexdigest(git_path.to_s)
- cache_path = Bundler::VERSION.start_with?("2.") ? vendored_gems("cache/bundler/git/foo-1.0-#{digest}") : home(".bundle/cache/git/foo-1.0-#{digest}")
+ cache_path = vendored_gems("cache/bundler/git/foo-1.0-#{digest}")
expect(cache_path).to exist
end
@@ -169,7 +169,7 @@ RSpec.describe "bundle clean" do
end
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
gemfile <<-G
@@ -210,7 +210,7 @@ RSpec.describe "bundle clean" do
FileUtils.mkdir_p(bundled_app("real-path"))
File.symlink(bundled_app("real-path"), bundled_app("symlink-path"))
- bundle "config set path #{bundled_app("symlink-path")}"
+ bundle_config "path #{bundled_app("symlink-path")}"
bundle "install"
bundle :clean
@@ -220,7 +220,7 @@ RSpec.describe "bundle clean" do
expect(bundled_app("symlink-path/#{Bundler.ruby_scope}/bundler/gems/foo-#{revision[0..11]}")).to exist
end
- it "removes old git gems" do
+ it "removes old git gems on bundle update" do
build_git "foo-bar", path: lib_path("foo-bar")
revision = revision_for(lib_path("foo-bar"))
@@ -233,7 +233,7 @@ RSpec.describe "bundle clean" do
end
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
update_git "foo-bar", path: lib_path("foo-bar")
@@ -265,7 +265,7 @@ RSpec.describe "bundle clean" do
gem "activesupport", :git => "#{lib_path("rails")}", :ref => '#{revision}'
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
bundle :clean
expect(out).to include("")
@@ -288,8 +288,8 @@ RSpec.describe "bundle clean" do
end
end
G
- bundle "config set path vendor/bundle"
- bundle "config set without test"
+ bundle_config "path vendor/bundle"
+ bundle_config "without test"
bundle "install"
bundle :clean
@@ -311,15 +311,15 @@ RSpec.describe "bundle clean" do
end
G
- bundle "config set path vendor/bundle"
- bundle "config set without development"
+ bundle_config "path vendor/bundle"
+ bundle_config "without development"
bundle "install"
bundle :clean
end
it "displays an error when used without --path" do
- bundle "config set path.system true"
+ bundle_config "path.system true"
install_gemfile <<-G
source "https://gem.repo1"
@@ -341,7 +341,7 @@ RSpec.describe "bundle clean" do
gem "foo"
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
gemfile <<-G
@@ -352,8 +352,8 @@ RSpec.describe "bundle clean" do
bundle "install"
FileUtils.rm(vendored_gems("bin/myrackup"))
- FileUtils.rm_rf(vendored_gems("gems/thin-1.0"))
- FileUtils.rm_rf(vendored_gems("gems/myrack-1.0.0"))
+ FileUtils.rm_r(vendored_gems("gems/thin-1.0"))
+ FileUtils.rm_r(vendored_gems("gems/myrack-1.0.0"))
bundle :clean
@@ -364,7 +364,7 @@ RSpec.describe "bundle clean" do
end
it "does not call clean automatically when using system gems" do
- bundle "config set path.system true"
+ bundle_config "path.system true"
install_gemfile <<-G
source "https://gem.repo1"
@@ -379,43 +379,18 @@ RSpec.describe "bundle clean" do
gem "myrack"
G
- gem_command :list
+ installed_gems_list
expect(out).to include("myrack (1.0.0)").and include("thin (1.0)")
end
- it "--clean should override the bundle setting on install", bundler: "< 3" do
- gemfile <<-G
- source "https://gem.repo1"
-
- gem "thin"
- gem "myrack"
- G
- bundle "config set path vendor/bundle"
- bundle "config set clean false"
- bundle "install --clean true"
-
- gemfile <<-G
- source "https://gem.repo1"
-
- gem "myrack"
- G
- bundle "install"
-
- should_have_gems "myrack-1.0.0"
- should_not_have_gems "thin-1.0"
- end
-
- it "--clean should override the bundle setting on update", bundler: "< 3" do
+ it "does not clean on bundle update when path has not been set" do
build_repo2
- gemfile <<-G
+ install_gemfile <<-G
source "https://gem.repo2"
gem "foo"
G
- bundle "config set path vendor/bundle"
- bundle "config set clean false"
- bundle "install --clean true"
update_repo2 do
build_gem "foo", "1.0.1"
@@ -423,11 +398,27 @@ RSpec.describe "bundle clean" do
bundle "update", all: true
- should_have_gems "foo-1.0.1"
- should_not_have_gems "foo-1.0"
+ files = Pathname.glob(default_bundle_path("*", "*"))
+ files.map! {|f| f.to_s.sub(default_bundle_path.to_s, "") }
+ expected_files = %W[
+ /bin/bundle
+ /bin/bundler
+ /cache/bundler-#{Bundler::VERSION}.gem
+ /cache/foo-1.0.1.gem
+ /cache/foo-1.0.gem
+ /gems/bundler-#{Bundler::VERSION}
+ /gems/foo-1.0
+ /gems/foo-1.0.1
+ /specifications/bundler-#{Bundler::VERSION}.gemspec
+ /specifications/foo-1.0.1.gemspec
+ /specifications/foo-1.0.gemspec
+ ]
+ expected_files += ["/bin/bundle.bat", "/bin/bundler.bat"] if Gem.win_platform?
+
+ expect(files.sort).to eq(expected_files.sort)
end
- it "automatically cleans when path has not been set", bundler: "3" do
+ it "will automatically clean on bundle update when path has not been set", bundler: "5" do
build_repo2
install_gemfile <<-G
@@ -442,8 +433,8 @@ RSpec.describe "bundle clean" do
bundle "update", all: true
- files = Pathname.glob(bundled_app(".bundle", Bundler.ruby_scope, "*", "*"))
- files.map! {|f| f.to_s.sub(bundled_app(".bundle", Bundler.ruby_scope).to_s, "") }
+ files = Pathname.glob(local_gem_path("*", "*"))
+ files.map! {|f| f.to_s.sub(local_gem_path.to_s, "") }
expect(files.sort).to eq %w[
/cache/foo-1.0.1.gem
/gems/foo-1.0.1
@@ -451,14 +442,14 @@ RSpec.describe "bundle clean" do
]
end
- it "does not clean automatically on --path" do
+ it "does not clean automatically when path configured" do
gemfile <<-G
source "https://gem.repo1"
gem "thin"
gem "myrack"
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
gemfile <<-G
@@ -471,7 +462,7 @@ RSpec.describe "bundle clean" do
should_have_gems "myrack-1.0.0", "thin-1.0"
end
- it "does not clean on bundle update with --path" do
+ it "does not clean on bundle update when path configured" do
build_repo2
gemfile <<-G
@@ -479,7 +470,7 @@ RSpec.describe "bundle clean" do
gem "foo"
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
update_repo2 do
@@ -490,8 +481,8 @@ RSpec.describe "bundle clean" do
should_have_gems "foo-1.0", "foo-1.0.1"
end
- it "does not clean on bundle update when using --system" do
- bundle "config set path.system true"
+ it "does not clean on bundle update when installing to system gems" do
+ bundle_config "path.system true"
build_repo2
@@ -507,12 +498,12 @@ RSpec.describe "bundle clean" do
end
bundle :update, all: true
- gem_command :list
+ installed_gems_list
expect(out).to include("foo (1.0.1, 1.0)")
end
it "cleans system gems when --force is used" do
- bundle "config set path.system true"
+ bundle_config "path.system true"
gemfile <<-G
source "https://gem.repo1"
@@ -531,7 +522,7 @@ RSpec.describe "bundle clean" do
bundle "clean --force"
expect(out).to include("Removing foo (1.0)")
- gem_command :list
+ installed_gems_list
expect(out).not_to include("foo (1.0)")
expect(out).to include("myrack (1.0.0)")
end
@@ -565,7 +556,7 @@ RSpec.describe "bundle clean" do
expect(err).to include(system_gem_path.to_s)
expect(err).to include("grant write permissions")
- gem_command :list
+ installed_gems_list
expect(out).to include("foo (1.0)")
expect(out).to include("myrack (1.0.0)")
end
@@ -581,7 +572,7 @@ RSpec.describe "bundle clean" do
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
# mimic 7 length git revisions in Gemfile.lock
@@ -591,7 +582,7 @@ RSpec.describe "bundle clean" do
end
lockfile(bundled_app_lock, gemfile_lock.join("\n"))
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
bundle :clean
@@ -602,7 +593,7 @@ RSpec.describe "bundle clean" do
end
it "when using --force on system gems, it doesn't remove binaries" do
- bundle "config set path.system true"
+ bundle_config "path.system true"
build_repo2 do
build_gem "bindir" do |s|
@@ -625,7 +616,7 @@ RSpec.describe "bundle clean" do
expect(out).to eq("1.0")
end
- it "when using --force, it doesn't remove default gem binaries", :realworld do
+ it "when using --force, it doesn't remove default gem binaries" do
default_irb_version = ruby "gem 'irb', '< 999999'; require 'irb'; puts IRB::VERSION", raise_on_error: false
skip "irb isn't a default gem" if default_irb_version.empty?
@@ -634,8 +625,6 @@ RSpec.describe "bundle clean" do
s.executables = "irb"
end
- realworld_system_gems "tsort --version 0.1.0", "pathname --version 0.1.0", "set --version 1.0.1"
-
install_gemfile <<-G
source "https://gem.repo2"
G
@@ -660,7 +649,7 @@ RSpec.describe "bundle clean" do
gem "bar", "1.0", :path => "#{relative_path}"
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
bundle :clean
end
@@ -673,8 +662,8 @@ RSpec.describe "bundle clean" do
gem "foo"
G
- bundle "config set path vendor/bundle"
- bundle "config set clean false"
+ bundle_config "path vendor/bundle"
+ bundle_config "clean false"
bundle "install"
gemfile <<-G
@@ -703,8 +692,8 @@ RSpec.describe "bundle clean" do
gem "foo"
G
- bundle "config set path vendor/bundle"
- bundle "config set clean false"
+ bundle_config "path vendor/bundle"
+ bundle_config "clean false"
bundle "install"
gemfile <<-G
@@ -733,10 +722,10 @@ RSpec.describe "bundle clean" do
gem "foo"
G
- bundle "config set path vendor/bundle"
- bundle "config set clean false"
+ bundle_config "path vendor/bundle"
+ bundle_config "clean false"
bundle "install"
- bundle "config set dry_run false"
+ bundle_config "dry_run false"
gemfile <<-G
source "https://gem.repo1"
@@ -765,8 +754,8 @@ RSpec.describe "bundle clean" do
gem "foo"
G
- bundle "config set path vendor/bundle"
- bundle "config set clean false"
+ bundle_config "path vendor/bundle"
+ bundle_config "clean false"
bundle "install"
gemfile <<-G
@@ -776,7 +765,7 @@ RSpec.describe "bundle clean" do
gem "weakling"
G
- bundle "config set auto_install 1"
+ bundle_config "auto_install 1"
bundle :clean
expect(out).to include("Installing weakling 0.0.3")
should_have_gems "thin-1.0", "myrack-1.0.0", "weakling-0.0.3"
@@ -794,7 +783,7 @@ RSpec.describe "bundle clean" do
gem "very_simple_git_binary", :git => "#{lib_path("very_simple_git_binary-1.0")}", :ref => "#{revision}"
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
expect(vendored_gems("bundler/gems/extensions")).to exist
expect(vendored_gems("bundler/gems/very_simple_git_binary-1.0-#{revision[0..11]}")).to exist
@@ -815,7 +804,7 @@ RSpec.describe "bundle clean" do
gem "simple_binary"
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
very_simple_binary_extensions_dir =
@@ -855,7 +844,7 @@ RSpec.describe "bundle clean" do
gem "very_simple_git_binary", :git => "#{lib_path("very_simple_git_binary-1.0")}", :ref => "#{revision}"
G
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
very_simple_binary_extensions_dir =
@@ -899,8 +888,8 @@ RSpec.describe "bundle clean" do
G
bundle :lock
- bundle "config set without development"
- bundle "config set path vendor/bundle"
+ bundle_config "without development"
+ bundle_config "path vendor/bundle"
bundle "install", verbose: true
bundle :clean
@@ -909,4 +898,39 @@ RSpec.describe "bundle clean" do
expect(very_simple_binary_extensions_dir).to be_nil
end
+
+ it "does not remove the bundler version currently running" do
+ gemfile <<-G
+ source "https://gem.repo1"
+
+ gem "myrack"
+ G
+
+ bundle_config "path vendor/bundle"
+ bundle "install"
+
+ version = Bundler.gem_version.to_s
+ # Simulate that the locked bundler version is installed in the bundle path
+ # by creating the gem directory and gemspec (as would happen after bundle install with that version)
+ Pathname(vendored_gems("cache/bundler-#{version}.gem")).tap do |path|
+ FileUtils.touch(path)
+ end
+ FileUtils.touch(vendored_gems("gems/bundler-#{version}"))
+ Pathname(vendored_gems("specifications/bundler-#{version}.gemspec")).tap do |path|
+ path.write(<<~GEMSPEC)
+ Gem::Specification.new do |s|
+ s.name = "bundler"
+ s.version = "#{version}"
+ s.authors = ["bundler team"]
+ s.summary = "The best way to manage your application's dependencies"
+ end
+ GEMSPEC
+ end
+
+ should_have_gems "bundler-#{version}"
+
+ bundle :clean
+
+ should_have_gems "bundler-#{version}"
+ end
end
diff --git a/spec/bundler/commands/config_spec.rb b/spec/bundler/commands/config_spec.rb
index 1392b17315..e8ab32ca5d 100644
--- a/spec/bundler/commands/config_spec.rb
+++ b/spec/bundler/commands/config_spec.rb
@@ -313,9 +313,10 @@ RSpec.describe ".bundle/config" do
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 @@ E
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 @@ E
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 @@ E
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 @@ E
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 @@ E
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
@@ -592,3 +627,20 @@ RSpec.describe "setting gemfile via config" do
end
end
end
+
+RSpec.describe "setting lockfile via config" do
+ it "persists the lockfile location to .bundle/config" do
+ gemfile bundled_app("NotGemfile"), <<-G
+ source "https://gem.repo1"
+ gem 'myrack'
+ G
+
+ bundle "config set --local gemfile #{bundled_app("NotGemfile")}"
+ bundle "config set --local lockfile #{bundled_app("ReallyNotGemfile.lock")}"
+ expect(File.exist?(bundled_app(".bundle/config"))).to eq(true)
+
+ bundle "config list"
+ expect(out).to include("NotGemfile")
+ expect(out).to include("ReallyNotGemfile.lock")
+ end
+end
diff --git a/spec/bundler/commands/console_spec.rb b/spec/bundler/commands/console_spec.rb
index ba5f5f514b..a44f607546 100644
--- a/spec/bundler/commands/console_spec.rb
+++ b/spec/bundler/commands/console_spec.rb
@@ -35,107 +35,180 @@ RSpec.describe "bundle console", readline: true do
end
RUBY
end
- end
- install_gemfile <<-G
- source "https://gem.repo2"
- gem "myrack"
- gem "activesupport", :group => :test
- gem "myrack_middleware", :group => :development
- G
+ build_dummy_irb
+ end
end
- it "starts IRB with the default group loaded" do
- bundle "console" do |input, _, _|
- input.puts("puts MYRACK")
- input.puts("exit")
+ context "when the library requires a non-existent file" do
+ before do
+ build_lib "loadfuuu", "1.0.0" do |s|
+ s.write "lib/loadfuuu.rb", "require_relative 'loadfuuu/bar'"
+ s.write "lib/loadfuuu/bar.rb", "require 'not-in-bundle'"
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "irb"
+ path "#{lib_path}" do
+ gem "loadfuuu", require: true
+ end
+ G
end
- expect(out).to include("0.9.1")
- end
- it "uses IRB as default console" do
- bundle "console" do |input, _, _|
- input.puts("__FILE__")
- input.puts("exit")
+ it "does not show the bug report template" do
+ bundle("console", raise_on_error: false) do |input, _, _|
+ input.puts("exit")
+ end
+
+ expect(err).not_to include("ERROR REPORT TEMPLATE")
end
- expect(out).to include("(irb)")
end
- it "starts another REPL if configured as such" do
- install_gemfile <<-G
- source "https://gem.repo2"
- gem "pry"
- G
- bundle "config set console pry"
+ context "when the library references a non-existent constant" do
+ before do
+ build_lib "loadfuuu", "1.0.0" do |s|
+ s.write "lib/loadfuuu.rb", "Some::NonExistent::Constant"
+ end
- bundle "console" do |input, _, _|
- input.puts("__method__")
- input.puts("exit")
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "irb"
+ path "#{lib_path}" do
+ gem "loadfuuu", require: true
+ end
+ G
end
- expect(out).to include(":__pry__")
- end
- it "falls back to IRB if the other REPL isn't available" do
- bundle "config set console pry"
- # make sure pry isn't there
+ it "does not show the bug report template" do
+ bundle("console", raise_on_error: false) do |input, _, _|
+ input.puts("exit")
+ end
- bundle "console" do |input, _, _|
- input.puts("__FILE__")
- input.puts("exit")
+ expect(err).not_to include("ERROR REPORT TEMPLATE")
end
- expect(out).to include("(irb)")
end
- it "doesn't load any other groups" do
- bundle "console" do |input, _, _|
- input.puts("puts ACTIVESUPPORT")
- input.puts("exit")
+ context "when the library does not have any errors" do
+ before do
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "irb"
+ gem "myrack"
+ gem "activesupport", :group => :test
+ gem "myrack_middleware", :group => :development
+ G
end
- expect(out).to include("NameError")
- end
- describe "when given a group" do
- it "loads the given group" do
- bundle "console test" do |input, _, _|
- input.puts("puts ACTIVESUPPORT")
+ it "starts IRB with the default group loaded" do
+ bundle "console" do |input, _, _|
+ input.puts("puts MYRACK")
input.puts("exit")
end
- expect(out).to include("2.3.5")
+ expect(out).to include("0.9.1")
end
- it "loads the default group" do
- bundle "console test" do |input, _, _|
- input.puts("puts MYRACK")
+ it "uses IRB as default console" do
+ skip "Does not work in a ruby-core context if irb is in the default $LOAD_PATH because it enables the real IRB, not our dummy one" if ruby_core? && Gem.ruby_version < Gem::Version.new("3.5.0.a")
+
+ bundle "console" do |input, _, _|
+ input.puts("__method__")
input.puts("exit")
end
- expect(out).to include("0.9.1")
+ expect(out).to include("__irb__")
+ end
+
+ it "starts another REPL if configured as such" do
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "irb"
+ gem "pry"
+ G
+ bundle_config "console pry"
+
+ bundle "console" do |input, _, _|
+ input.puts("__method__")
+ input.puts("exit")
+ end
+ expect(out).to include(":__pry__")
end
- it "doesn't load other groups" do
- bundle "console test" do |input, _, _|
- input.puts("puts MYRACK_MIDDLEWARE")
+ it "falls back to IRB if the other REPL isn't available" do
+ skip "Does not work in a ruby-core context if irb is in the default $LOAD_PATH because it enables the real IRB, not our dummy one" if ruby_core? && Gem.ruby_version < Gem::Version.new("3.5.0.a")
+
+ bundle_config "console pry"
+ # make sure pry isn't there
+
+ bundle "console" do |input, _, _|
+ input.puts("__method__")
+ input.puts("exit")
+ end
+ expect(out).to include("__irb__")
+ end
+
+ it "does not try IRB twice if no console is configured and IRB is not available" do
+ create_file("irb.rb", "raise LoadError, 'irb is not available'")
+
+ bundle("console", env: { "RUBYOPT" => "-I#{bundled_app} #{ENV["RUBYOPT"]}" }, raise_on_error: false) do |input, _, _|
+ input.puts("puts ACTIVESUPPORT")
+ input.puts("exit")
+ end
+ expect(err).not_to include("falling back to irb")
+ expect(err).to include("irb is not available")
+ end
+
+ it "doesn't load any other groups" do
+ bundle "console" do |input, _, _|
+ input.puts("puts ACTIVESUPPORT")
input.puts("exit")
end
expect(out).to include("NameError")
end
- end
- it "performs an automatic bundle install" do
- gemfile <<-G
- source "https://gem.repo2"
- gem "myrack"
- gem "activesupport", :group => :test
- gem "myrack_middleware", :group => :development
- gem "foo"
- G
-
- bundle "config set auto_install 1"
- bundle :console do |input, _, _|
- input.puts("puts 'hello'")
- input.puts("exit")
+ describe "when given a group" do
+ it "loads the given group" do
+ bundle "console test" do |input, _, _|
+ input.puts("puts ACTIVESUPPORT")
+ input.puts("exit")
+ end
+ expect(out).to include("2.3.5")
+ end
+
+ it "loads the default group" do
+ bundle "console test" do |input, _, _|
+ input.puts("puts MYRACK")
+ input.puts("exit")
+ end
+ expect(out).to include("0.9.1")
+ end
+
+ it "doesn't load other groups" do
+ bundle "console test" do |input, _, _|
+ input.puts("puts MYRACK_MIDDLEWARE")
+ input.puts("exit")
+ end
+ expect(out).to include("NameError")
+ end
+ end
+
+ it "performs an automatic bundle install" do
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "irb"
+ gem "myrack"
+ gem "activesupport", :group => :test
+ gem "myrack_middleware", :group => :development
+ gem "foo"
+ G
+
+ bundle_config "auto_install 1"
+ bundle :console do |input, _, _|
+ input.puts("puts 'hello'")
+ input.puts("exit")
+ end
+ expect(out).to include("Installing foo 1.0")
+ expect(out).to include("hello")
+ expect(the_bundle).to include_gems "foo 1.0"
end
- expect(out).to include("Installing foo 1.0")
- expect(out).to include("hello")
- expect(the_bundle).to include_gems "foo 1.0"
end
end
diff --git a/spec/bundler/commands/doctor_spec.rb b/spec/bundler/commands/doctor_spec.rb
index 744510cd40..d350b4b3d1 100644
--- a/spec/bundler/commands/doctor_spec.rb
+++ b/spec/bundler/commands/doctor_spec.rb
@@ -4,6 +4,7 @@ require "find"
require "stringio"
require "bundler/cli"
require "bundler/cli/doctor"
+require "bundler/cli/doctor/diagnose"
RSpec.describe "bundle doctor" do
before(:each) do
@@ -14,7 +15,7 @@ RSpec.describe "bundle doctor" do
@stdout = StringIO.new
- [:error, :warn].each do |method|
+ [:error, :warn, :info].each do |method|
allow(Bundler.ui).to receive(method).and_wrap_original do |m, message|
m.call message
@stdout.puts message
@@ -33,6 +34,8 @@ RSpec.describe "bundle doctor" do
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
allow(Find).to receive(:find).with(Bundler.bundle_path.to_s) { [unwritable_file] }
allow(File).to receive(:exist?).and_call_original
+ allow(File).to receive(:writable?).and_call_original
+ allow(File).to receive(:readable?).and_call_original
allow(File).to receive(:exist?).with(unwritable_file).and_return(true)
allow(File).to receive(:stat).with(unwritable_file) { stat }
allow(stat).to receive(:uid) { Process.uid }
@@ -45,25 +48,33 @@ RSpec.describe "bundle doctor" do
allow(File).to receive(:writable?).with(File.expand_path("..", Gem.default_dir))
end
- it "exits with no message if the installed gem has no C extensions" do
- expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
- expect(@stdout.string).to be_empty
+ it "exits with a success message if the installed gem has no C extensions" do
+ doctor = Bundler::CLI::Doctor::Diagnose.new({})
+ allow(doctor).to receive(:lookup_with_fiddle).and_return(false)
+ expect { doctor.run }.not_to raise_error
+ expect(@stdout.string).to include("No issues")
end
- it "exits with no message if the installed gem's C extension dylib breakage is fine" do
- doctor = Bundler::CLI::Doctor.new({})
+ it "exits with a success message if the installed gem's C extension dylib breakage is fine" do
+ doctor = Bundler::CLI::Doctor::Diagnose.new({})
expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/myrack/myrack.bundle"]
expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/lib/libSystem.dylib"]
- allow(Fiddle).to receive(:dlopen).with("/usr/lib/libSystem.dylib").and_return(true)
+ allow(doctor).to receive(:lookup_with_fiddle).with("/usr/lib/libSystem.dylib").and_return(false)
expect { doctor.run }.not_to raise_error
- expect(@stdout.string).to be_empty
+ expect(@stdout.string).to include("No issues")
+ end
+
+ it "parses otool output correctly" do
+ doctor = Bundler::CLI::Doctor::Diagnose.new({})
+ expect(doctor).to receive(:`).with("/usr/bin/otool -L fake").and_return("/home/gem/ruby/3.4.3/gems/blake3-rb-1.5.4.4/lib/digest/blake3/blake3_ext.bundle:\n\t (compatibility version 0.0.0, current version 0.0.0)\n\t/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1351.0.0)")
+ expect(doctor.dylibs_darwin("fake")).to eq(["/usr/lib/libSystem.B.dylib"])
end
it "exits with a message if one of the linked libraries is missing" do
- doctor = Bundler::CLI::Doctor.new({})
+ doctor = Bundler::CLI::Doctor::Diagnose.new({})
expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/myrack/myrack.bundle"]
expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib"]
- allow(Fiddle).to receive(:dlopen).with("/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib").and_raise(Fiddle::DLError)
+ allow(doctor).to receive(:lookup_with_fiddle).with("/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib").and_return(true)
expect { doctor.run }.to raise_error(Bundler::ProductionError, <<~E.strip), @stdout.string
The following gems are missing OS dependencies:
* bundler: /usr/local/opt/icu4c/lib/libicui18n.57.1.dylib
@@ -82,7 +93,9 @@ RSpec.describe "bundle doctor" do
end
it "exits with an error if home contains files that are not readable/writable" do
- expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
+ doctor = Bundler::CLI::Doctor::Diagnose.new({})
+ allow(doctor).to receive(:lookup_with_fiddle).and_return(false)
+ expect { doctor.run }.not_to raise_error
expect(@stdout.string).to include(
"Broken links exist in the Bundler home. Please report them to the offending gem's upstream repo. These files are:\n - #{@broken_symlink}"
)
@@ -97,42 +110,63 @@ RSpec.describe "bundle doctor" do
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
allow(Find).to receive(:find).with(Bundler.bundle_path.to_s) { [@unwritable_file] }
allow(File).to receive(:exist?).and_call_original
+ allow(File).to receive(:writable?).and_call_original
+ allow(File).to receive(:readable?).and_call_original
allow(File).to receive(:exist?).with(@unwritable_file) { true }
allow(File).to receive(:stat).with(@unwritable_file) { @stat }
end
- it "exits with an error if home contains files that are not readable/writable" do
+ it "exits with an error if home contains files that are not readable" do
+ doctor = Bundler::CLI::Doctor::Diagnose.new({})
+ allow(doctor).to receive(:lookup_with_fiddle).and_return(false)
allow(@stat).to receive(:uid) { Process.uid }
allow(File).to receive(:writable?).with(@unwritable_file) { false }
allow(File).to receive(:readable?).with(@unwritable_file) { false }
- expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
+ expect { doctor.run }.not_to raise_error
expect(@stdout.string).to include(
- "Files exist in the Bundler home that are not readable/writable by the current user. These files are:\n - #{@unwritable_file}"
+ "Files exist in the Bundler home that are not readable by the current user. These files are:\n - #{@unwritable_file}"
)
expect(@stdout.string).not_to include("No issues")
end
+ it "exits without an error if home contains files that are not writable" do
+ doctor = Bundler::CLI::Doctor::Diagnose.new({})
+ allow(doctor).to receive(:lookup_with_fiddle).and_return(false)
+ allow(@stat).to receive(:uid) { Process.uid }
+ allow(File).to receive(:writable?).with(@unwritable_file) { false }
+ allow(File).to receive(:readable?).with(@unwritable_file) { true }
+ expect { doctor.run }.not_to raise_error
+ expect(@stdout.string).not_to include(
+ "Files exist in the Bundler home that are not readable by the current user. These files are:\n - #{@unwritable_file}"
+ )
+ expect(@stdout.string).to include("No issues")
+ end
+
context "when home contains files that are not owned by the current process", :permissions do
before(:each) do
allow(@stat).to receive(:uid) { 0o0000 }
end
it "exits with an error if home contains files that are not readable/writable and are not owned by the current user" do
+ doctor = Bundler::CLI::Doctor::Diagnose.new({})
+ allow(doctor).to receive(:lookup_with_fiddle).and_return(false)
allow(File).to receive(:writable?).with(@unwritable_file) { false }
allow(File).to receive(:readable?).with(@unwritable_file) { false }
- expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
+ expect { doctor.run }.not_to raise_error
expect(@stdout.string).to include(
- "Files exist in the Bundler home that are owned by another user, and are not readable/writable. These files are:\n - #{@unwritable_file}"
+ "Files exist in the Bundler home that are owned by another user, and are not readable. These files are:\n - #{@unwritable_file}"
)
expect(@stdout.string).not_to include("No issues")
end
it "exits with a warning if home contains files that are read/write but not owned by current user" do
+ doctor = Bundler::CLI::Doctor::Diagnose.new({})
+ allow(doctor).to receive(:lookup_with_fiddle).and_return(false)
allow(File).to receive(:writable?).with(@unwritable_file) { true }
allow(File).to receive(:readable?).with(@unwritable_file) { true }
- expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
+ expect { doctor.run }.not_to raise_error
expect(@stdout.string).to include(
- "Files exist in the Bundler home that are owned by another user, but are still readable/writable. These files are:\n - #{@unwritable_file}"
+ "Files exist in the Bundler home that are owned by another user, but are still readable. These files are:\n - #{@unwritable_file}"
)
expect(@stdout.string).not_to include("No issues")
end
@@ -141,7 +175,7 @@ RSpec.describe "bundle doctor" do
context "when home contains filenames with special characters" do
it "escape filename before command execute" do
- doctor = Bundler::CLI::Doctor.new({})
+ doctor = Bundler::CLI::Doctor::Diagnose.new({})
expect(doctor).to receive(:`).with("/usr/bin/otool -L \\$\\(date\\)\\ \\\"\\'\\\\.bundle").and_return("dummy string")
doctor.dylibs_darwin('$(date) "\'\.bundle')
expect(doctor).to receive(:`).with("/usr/bin/ldd \\$\\(date\\)\\ \\\"\\'\\\\.bundle").and_return("dummy string")
diff --git a/spec/bundler/commands/exec_spec.rb b/spec/bundler/commands/exec_spec.rb
index e2b965c34c..aa35685be8 100644
--- a/spec/bundler/commands/exec_spec.rb
+++ b/spec/bundler/commands/exec_spec.rb
@@ -95,7 +95,7 @@ RSpec.describe "bundle exec" do
end
it "respects custom process title when loading through ruby" do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+ skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform?
script_that_changes_its_own_title_and_checks_if_picked_up_by_ps_unix_utility = <<~'RUBY'
Process.setproctitle("1-2-3-4-5-6-7")
@@ -120,7 +120,7 @@ RSpec.describe "bundle exec" do
end
it "handles --keep-file-descriptors" do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+ skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform?
require "tempfile"
@@ -139,7 +139,7 @@ RSpec.describe "bundle exec" do
G
install_gemfile "source \"https://gem.repo1\""
- sys_exec "#{Gem.ruby} #{command.path}"
+ in_bundled_app "#{Gem.ruby} #{command.path}"
expect(out).to be_empty
expect(err).to be_empty
@@ -153,7 +153,7 @@ RSpec.describe "bundle exec" do
end
it "can run a command named --verbose" do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+ skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform?
install_gemfile "source \"https://gem.repo1\"; gem \"myrack\""
File.open(bundled_app("--verbose"), "w") do |f|
@@ -193,75 +193,70 @@ RSpec.describe "bundle exec" do
end
context "with default gems" do
- let(:default_irb_version) { ruby "gem 'irb', '< 999999'; require 'irb'; puts IRB::VERSION", raise_on_error: false }
+ # TODO: Switch to ERB::VERSION once Ruby 3.4 support is dropped, so all
+ # supported rubies include an `erb` gem version where `ERB::VERSION` is
+ # public
+ let(:default_erb_version) { ruby "require 'erb/version'; puts ERB.const_get(:VERSION)" }
context "when not specified in Gemfile" do
before do
- skip "irb isn't a default gem" if default_irb_version.empty?
-
install_gemfile "source \"https://gem.repo1\""
end
it "uses version provided by ruby" do
- bundle "exec irb --version"
+ bundle "exec erb --version"
- expect(out).to include(default_irb_version)
+ expect(stdboth).to eq(default_erb_version)
end
end
context "when specified in Gemfile directly" do
- let(:specified_irb_version) { "0.9.6" }
+ let(:specified_erb_version) { "2.0.0" }
before do
- skip "irb isn't a default gem" if default_irb_version.empty?
-
build_repo2 do
- build_gem "irb", specified_irb_version do |s|
- s.executables = "irb"
+ build_gem "erb", specified_erb_version do |s|
+ s.executables = "erb"
end
end
install_gemfile <<-G
source "https://gem.repo2"
- gem "irb", "#{specified_irb_version}"
+ gem "erb", "#{specified_erb_version}"
G
end
it "uses version specified" do
- bundle "exec irb --version"
+ bundle "exec erb --version"
- expect(out).to eq(specified_irb_version)
- expect(err).to be_empty
+ expect(stdboth).to eq(specified_erb_version)
end
end
context "when specified in Gemfile indirectly" do
- let(:indirect_irb_version) { "0.9.6" }
+ let(:indirect_erb_version) { "2.0.0" }
before do
- skip "irb isn't a default gem" if default_irb_version.empty?
-
build_repo2 do
- build_gem "irb", indirect_irb_version do |s|
- s.executables = "irb"
+ build_gem "erb", indirect_erb_version do |s|
+ s.executables = "erb"
end
- build_gem "gem_depending_on_old_irb" do |s|
- s.add_dependency "irb", indirect_irb_version
+ build_gem "gem_depending_on_old_erb" do |s|
+ s.add_dependency "erb", indirect_erb_version
end
end
install_gemfile <<-G
source "https://gem.repo2"
- gem "gem_depending_on_old_irb"
+ gem "gem_depending_on_old_erb"
G
-
- bundle "exec irb --version"
end
it "uses resolved version" do
- expect(out).to eq(indirect_irb_version)
- expect(err).to be_empty
+ bundle "exec erb --version"
+
+ expect(stdboth).to eq(indirect_erb_version)
end
end
end
@@ -273,7 +268,7 @@ RSpec.describe "bundle exec" do
end
end
- bundle "config set --global path.system true"
+ bundle_config_global "path.system true"
install_gemfile <<-G
source "https://gem.repo1"
@@ -294,7 +289,7 @@ RSpec.describe "bundle exec" do
end
it "handles gems installed with --without" do
- bundle "config set --local without middleware"
+ bundle_config "without middleware"
install_gemfile <<-G
source "https://gem.repo1"
gem "myrack" # myrack 0.9.1 and 1.0 exist
@@ -384,13 +379,13 @@ RSpec.describe "bundle exec" do
it "raises a helpful error when exec'ing to something outside of the bundle" do
system_gems(%w[myrack-1.0.0 myrack-0.9.1], path: default_bundle_path)
- bundle "config set clean false" # want to keep the myrackup binstub
+ bundle_config "clean false" # want to keep the myrackup binstub
install_gemfile <<-G
source "https://gem.repo1"
gem "foo"
G
[true, false].each do |l|
- bundle "config set disable_exec_load #{l}"
+ bundle_config "disable_exec_load #{l}"
bundle "exec myrackup", raise_on_error: false
expect(err).to include "can't find executable myrackup for gem myrack. myrack is not currently included in the bundle, perhaps you meant to add it to your Gemfile?"
end
@@ -587,8 +582,8 @@ RSpec.describe "bundle exec" do
gem "foo"
G
- bundle "config set auto_install 1"
- bundle "exec myrackup"
+ bundle_config "auto_install 1"
+ bundle "exec myrackup", artifice: "compact_index"
expect(out).to include("Installing foo 1.0")
end
@@ -602,8 +597,8 @@ RSpec.describe "bundle exec" do
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
- bundle "config set auto_install 1"
- bundle "exec foo"
+ bundle_config "auto_install 1"
+ bundle "exec foo", artifice: "compact_index"
expect(out).to include("Fetching myrack 0.9.1")
expect(out).to include("Fetching #{lib_path("foo-1.0")}")
expect(out.lines).to end_with("1.0")
@@ -622,15 +617,15 @@ RSpec.describe "bundle exec" do
system_gems "optparse-999.999.998", gem_repo: gem_repo4
- bundle "config set auto_install 1"
- bundle "config set --local path vendor/bundle"
+ bundle_config "auto_install 1"
+ bundle_config "path vendor/bundle"
gemfile <<~G
source "https://gem.repo4"
gem "fastlane"
G
- bundle "exec fastlane"
+ bundle "exec fastlane", artifice: "compact_index"
expect(out).to include("Installing optparse 999.999.999")
expect(out).to include("2.192.0")
end
@@ -657,7 +652,7 @@ RSpec.describe "bundle exec" do
gem "foo", :path => "#{lib_path("foo-1.0")}"
G
- bundle "exec irb", raise_on_error: false
+ bundle "exec erb", raise_on_error: false
expect(err).to match("The gemspec at #{lib_path("foo-1.0").join("foo.gemspec")} is not valid")
expect(err).to match(/missing value for attribute rubygems_version|rubygems_version must not be nil/)
@@ -666,8 +661,6 @@ RSpec.describe "bundle exec" do
describe "with gems bundled for deployment" do
it "works when calling bundler from another script" do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
-
gemfile <<-G
source "https://gem.repo1"
@@ -678,7 +671,7 @@ RSpec.describe "bundle exec" do
end
Bundler.rubygems.extend(Monkey)
G
- bundle "config set path.system true"
+ bundle_config "path.system true"
bundle "install"
bundle "exec ruby -e '`bundle -v`; puts $?.success?'", env: { "BUNDLER_VERSION" => Bundler::VERSION }
expect(out).to match("true")
@@ -704,6 +697,27 @@ RSpec.describe "bundle exec" do
end
end
+ describe "running gem commands in presence of rubygems plugins" do
+ before do
+ build_repo4 do
+ build_gem "foo" do |s|
+ s.write "lib/rubygems_plugin.rb", "puts 'FAIL'"
+ end
+ end
+
+ system_gems "foo-1.0", path: default_bundle_path, gem_repo: gem_repo4
+
+ install_gemfile <<-G
+ source "https://gem.repo4"
+ G
+ end
+
+ it "does not load plugins outside of the bundle" do
+ bundle "exec #{gem_cmd} -v"
+ expect(out).not_to include("FAIL")
+ end
+ end
+
context "`load`ing a ruby file instead of `exec`ing" do
let(:path) { bundled_app("ruby_executable") }
let(:shebang) { "#!/usr/bin/env ruby" }
@@ -714,13 +728,16 @@ RSpec.describe "bundle exec" do
puts "EXEC: \#{caller.grep(/load/).empty? ? 'exec' : 'load'}"
puts "ARGS: \#{$0} \#{ARGV.join(' ')}"
puts "MYRACK: \#{MYRACK}"
- process_title = `ps -o args -p \#{Process.pid}`.split("\n", 2).last.strip
+ if Gem.win_platform?
+ process_title = "ruby"
+ else
+ process_title = `ps -o args -p \#{Process.pid}`.split("\n", 2).last.strip
+ end
puts "PROCESS: \#{process_title}"
RUBY
before do
- bundled_app(path).open("w") {|f| f << executable }
- bundled_app(path).chmod(0o755)
+ create_file(bundled_app(path), executable)
install_gemfile <<-G
source "https://gem.repo1"
@@ -732,9 +749,11 @@ RSpec.describe "bundle exec" do
let(:args) { "ARGS: #{path} arg1 arg2" }
let(:myrack) { "MYRACK: 1.0.0" }
let(:process) do
- title = "PROCESS: #{path}"
- title += " arg1 arg2"
- title
+ if Gem.win_platform?
+ "PROCESS: ruby"
+ else
+ "PROCESS: #{path} arg1 arg2"
+ end
end
let(:exit_code) { 0 }
let(:expected) { [exec, args, myrack, process].join("\n") }
@@ -743,8 +762,6 @@ RSpec.describe "bundle exec" do
subject { bundle "exec #{path} arg1 arg2", raise_on_error: false }
it "runs" do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
-
subject
expect(exitstatus).to eq(exit_code)
expect(err).to eq(expected_err)
@@ -756,8 +773,6 @@ RSpec.describe "bundle exec" do
context "with exit 0" do
it "runs" do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
-
subject
expect(exitstatus).to eq(exit_code)
expect(err).to eq(expected_err)
@@ -769,8 +784,6 @@ RSpec.describe "bundle exec" do
let(:exit_code) { 99 }
it "runs" do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
-
subject
expect(exitstatus).to eq(exit_code)
expect(err).to eq(expected_err)
@@ -792,7 +805,7 @@ RSpec.describe "bundle exec" do
end
it "runs" do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+ skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform?
subject
expect(exitstatus).to eq(exit_code)
@@ -809,7 +822,12 @@ RSpec.describe "bundle exec" do
let(:expected) { "" }
it "runs" do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+ # it's empty, so `create_file` won't add executable permission and bat scripts on Windows
+ bundled_app(path).chmod(0o755)
+ path.sub_ext(".bat").write <<~SCRIPT if Gem.win_platform?
+ @ECHO OFF
+ @"ruby.exe" "%~dpn0" %*
+ SCRIPT
subject
expect(exitstatus).to eq(exit_code)
@@ -822,12 +840,10 @@ RSpec.describe "bundle exec" do
let(:executable) { super() << "\nraise 'ERROR'" }
let(:exit_code) { 1 }
let(:expected_err) do
- /\Abundler: failed to load command: #{Regexp.quote(path.to_s)} \(#{Regexp.quote(path.to_s)}\)\n#{Regexp.quote(path.to_s)}:10:in [`']<top \(required\)>': ERROR \(RuntimeError\)/
+ /\Abundler: failed to load command: #{Regexp.quote(path.to_s)} \(#{Regexp.quote(path.to_s)}\)\n#{Regexp.quote(path.to_s)}:[0-9]+:in [`']<top \(required\)>': ERROR \(RuntimeError\)/
end
it "runs like a normally executed executable" do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
-
subject
expect(exitstatus).to eq(exit_code)
expect(err).to match(expected_err)
@@ -842,8 +858,6 @@ RSpec.describe "bundle exec" do
let(:expected) { super() }
it "runs" do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
-
subject
expect(exitstatus).to eq(exit_code)
expect(err).to eq(expected_err)
@@ -855,8 +869,6 @@ RSpec.describe "bundle exec" do
let(:shebang) { "#!#{Gem.ruby}" }
it "runs" do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
-
subject
expect(exitstatus).to eq(exit_code)
expect(err).to eq(expected_err)
@@ -887,8 +899,6 @@ RSpec.describe "bundle exec" do
EOS
it "runs" do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
-
subject
expect(exitstatus).to eq(exit_code)
expect(err).to eq(expected_err)
@@ -911,8 +921,6 @@ RSpec.describe "bundle exec" do
let(:expected) { "" }
it "prints proper suggestion" do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
-
subject
expect(exitstatus).to eq(exit_code)
expect(err).to include("Run `bundle install --gemfile CustomGemfile` to install missing gems.")
@@ -925,8 +933,6 @@ RSpec.describe "bundle exec" do
let(:exit_code) { 1 }
it "runs" do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
-
subject
expect(exitstatus).to eq(exit_code)
expect(err).to eq(expected_err)
@@ -936,15 +942,19 @@ RSpec.describe "bundle exec" do
context "when disable_exec_load is set" do
let(:exec) { "EXEC: exec" }
- let(:process) { "PROCESS: ruby #{path} arg1 arg2" }
+ let(:process) do
+ if Gem.win_platform?
+ "PROCESS: ruby"
+ else
+ "PROCESS: ruby #{path} arg1 arg2"
+ end
+ end
before do
- bundle "config set disable_exec_load true"
+ bundle_config "disable_exec_load true"
end
it "runs" do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
-
subject
expect(exitstatus).to eq(exit_code)
expect(err).to eq(expected_err)
@@ -967,8 +977,6 @@ RSpec.describe "bundle exec" do
EOS
it "runs" do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
-
subject
expect(exitstatus).to eq(exit_code)
expect(err).to eq(expected_err)
@@ -985,8 +993,6 @@ RSpec.describe "bundle exec" do
EOS
it "runs" do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
-
subject
expect(exitstatus).to eq(exit_code)
expect(err).to eq(expected_err)
@@ -1003,8 +1009,6 @@ RSpec.describe "bundle exec" do
EOS
it "runs" do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
-
subject
expect(exitstatus).to eq(exit_code)
expect(err).to eq(expected_err)
@@ -1016,7 +1020,7 @@ RSpec.describe "bundle exec" do
context "signal handling" do
let(:test_signals) do
open3_reserved_signals = %w[CHLD CLD PIPE]
- reserved_signals = %w[SEGV BUS ILL FPE VTALRM KILL STOP EXIT]
+ reserved_signals = %w[SEGV BUS ILL FPE ABRT IOT VTALRM KILL STOP EXIT]
bundler_signals = %w[INT]
Signal.list.keys - (bundler_signals + reserved_signals + open3_reserved_signals)
@@ -1030,7 +1034,7 @@ RSpec.describe "bundle exec" do
puts 'Started' # For process sync
STDOUT.flush
sleep 1 # ignore quality_spec
- raise "Didn't receive INT at all"
+ raise RuntimeError, "Didn't receive expected INT"
end.join
rescue Interrupt
puts "foo"
@@ -1038,7 +1042,7 @@ RSpec.describe "bundle exec" do
RUBY
it "receives the signal" do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+ skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform?
bundle("exec #{path}") do |_, o, thr|
o.gets # Consumes 'Started' and ensures that thread has started
@@ -1061,7 +1065,7 @@ RSpec.describe "bundle exec" do
RUBY
it "makes sure no unexpected signals are restored to DEFAULT" do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+ skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform?
test_signals.each do |n|
Signal.trap(n, "IGNORE")
@@ -1078,13 +1082,13 @@ RSpec.describe "bundle exec" do
context "nested bundle exec" do
context "when bundle in a local path" do
before do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+ skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform?
gemfile <<-G
source "https://gem.repo1"
gem "myrack"
G
- bundle "config set path vendor/bundler"
+ bundle_config "path vendor/bundler"
bundle :install
end
@@ -1102,7 +1106,7 @@ RSpec.describe "bundle exec" do
context "when Kernel.require uses extra monkeypatches" do
before do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
+ skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform?
install_gemfile "source \"https://gem.repo1\""
end
@@ -1145,16 +1149,14 @@ RSpec.describe "bundle exec" do
context "when gemfile and path are configured", :ruby_repo do
before do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
-
build_repo2 do
build_gem "rails", "6.1.0" do |s|
s.executables = "rails"
end
end
- bundle "config set path vendor/bundle"
- bundle "config set gemfile gemfiles/myrack_6_1.gemfile"
+ bundle_config "path vendor/bundle"
+ bundle_config "gemfile gemfiles/myrack_6_1.gemfile"
gemfile(bundled_app("gemfiles/myrack_6_1.gemfile"), <<~RUBY)
source "https://gem.repo2"
@@ -1205,18 +1207,18 @@ RSpec.describe "bundle exec" do
context "with a system gem that shadows a default gem" do
let(:openssl_version) { "99.9.9" }
- let(:expected) { ruby "gem 'openssl', '< 999999'; require 'openssl'; puts OpenSSL::VERSION", artifice: nil, raise_on_error: false }
it "only leaves the default gem in the stdlib available" do
- skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
- skip "openssl isn't a default gem" if expected.empty?
+ default_openssl_version = ruby "require 'openssl'; puts OpenSSL::VERSION"
+
+ skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform?
install_gemfile "source \"https://gem.repo1\"" # must happen before installing the broken system gem
build_repo4 do
build_gem "openssl", openssl_version do |s|
s.write("lib/openssl.rb", <<-RUBY)
- raise "custom openssl should not be loaded, it's not in the gemfile!"
+ raise ArgumentError, "custom openssl should not be loaded"
RUBY
end
end
@@ -1234,10 +1236,10 @@ RSpec.describe "bundle exec" do
env = { "PATH" => path }
aggregate_failures do
- expect(bundle("exec #{file}", artifice: nil, env: env)).to eq(expected)
- expect(bundle("exec bundle exec #{file}", artifice: nil, env: env)).to eq(expected)
- expect(bundle("exec ruby #{file}", artifice: nil, env: env)).to eq(expected)
- expect(run(file.read, artifice: nil, env: env)).to eq(expected)
+ expect(bundle("exec #{file}", env: env)).to eq(default_openssl_version)
+ expect(bundle("exec bundle exec #{file}", env: env)).to eq(default_openssl_version)
+ expect(bundle("exec ruby #{file}", env: env)).to eq(default_openssl_version)
+ expect(run(file.read, artifice: nil, env: env)).to eq(default_openssl_version)
end
skip "ruby_core has openssl and rubygems in the same folder, and this test needs rubygems require but default openssl not in a directly added entry in $LOAD_PATH" if ruby_core?
@@ -1250,7 +1252,7 @@ RSpec.describe "bundle exec" do
context "with a git gem that includes extensions", :ruby_repo do
before do
build_git "simple_git_binary", &:add_c_extension
- bundle "config set --local path .bundle"
+ bundle_config "path .bundle"
install_gemfile <<-G
source "https://gem.repo1"
gem "simple_git_binary", :git => '#{lib_path("simple_git_binary-1.0")}'
@@ -1262,7 +1264,7 @@ RSpec.describe "bundle exec" do
end
it "allows calling bundle install after removing gem.build_complete" do
- FileUtils.rm_rf Dir[bundled_app(".bundle/**/gem.build_complete")]
+ FileUtils.rm_r Dir[bundled_app(".bundle/**/gem.build_complete")]
bundle "exec #{Gem.ruby} -S bundle install"
end
end
diff --git a/spec/bundler/commands/fund_spec.rb b/spec/bundler/commands/fund_spec.rb
index 6f4e61da30..5883b8a63a 100644
--- a/spec/bundler/commands/fund_spec.rb
+++ b/spec/bundler/commands/fund_spec.rb
@@ -73,7 +73,7 @@ RSpec.describe "bundle fund" do
end
it "considers fund information for installed optional dependencies" do
- bundle "config set with whatever"
+ bundle_config "with whatever"
install_gemfile <<-G
source "https://gem.repo2"
diff --git a/spec/bundler/commands/info_spec.rb b/spec/bundler/commands/info_spec.rb
index 42f288a1d8..a26b1696fb 100644
--- a/spec/bundler/commands/info_spec.rb
+++ b/spec/bundler/commands/info_spec.rb
@@ -56,12 +56,12 @@ RSpec.describe "bundle info" do
expect(out).to eq("2.3.2")
end
- it "doesn't claim that bundler has been deleted, even if using a custom path without bundler there" do
- bundle "config set --local path vendor/bundle"
+ it "doesn't claim that bundler is missing, even if using a custom path without bundler there" do
+ bundle_config "path vendor/bundle"
bundle "install"
bundle "info bundler"
expect(out).to include("\tPath: #{root}")
- expect(err).not_to match(/The gem bundler has been deleted/i)
+ expect(err).not_to match(/The gem bundler is missing/i)
end
it "complains if gem not in bundle" do
@@ -69,27 +69,27 @@ RSpec.describe "bundle info" do
expect(err).to eq("Could not find gem 'missing'.")
end
- it "warns if path no longer exists on disk" do
- FileUtils.rm_rf(default_bundle_path("gems", "rails-2.3.2"))
+ it "warns if path does not exist on disk, but specification is there" do
+ FileUtils.rm_r(default_bundle_path("gems", "rails-2.3.2"))
bundle "info rails --path"
- expect(err).to include("The gem rails has been deleted.")
+ expect(err).to include("The gem rails is missing.")
expect(err).to include(default_bundle_path("gems", "rails-2.3.2").to_s)
bundle "info rail --path"
- expect(err).to include("The gem rails has been deleted.")
+ expect(err).to include("The gem rails is missing.")
expect(err).to include(default_bundle_path("gems", "rails-2.3.2").to_s)
bundle "info rails"
- expect(err).to include("The gem rails has been deleted.")
+ expect(err).to include("The gem rails is missing.")
expect(err).to include(default_bundle_path("gems", "rails-2.3.2").to_s)
end
- context "given a default gem shippped in ruby", :ruby_repo do
+ context "given a default gem shipped in ruby", :ruby_repo do
it "prints information about the default gem" do
- bundle "info rdoc"
- expect(out).to include("* rdoc")
+ bundle "info json"
+ expect(out).to include("* json")
expect(out).to include("Default Gem: yes")
end
end
@@ -235,7 +235,7 @@ RSpec.describe "bundle info" do
context "with without configured" do
it "does not find the gem, but gives a helpful error" do
- bundle "config without test"
+ bundle_config "without test"
install_gemfile <<-G
source "https://gem.repo1"
diff --git a/spec/bundler/commands/init_spec.rb b/spec/bundler/commands/init_spec.rb
index 538e61fd47..989d6fa812 100644
--- a/spec/bundler/commands/init_spec.rb
+++ b/spec/bundler/commands/init_spec.rb
@@ -119,7 +119,7 @@ RSpec.describe "bundle init" do
end
context "when init_gems_rb setting is enabled" do
- before { bundle "config set init_gems_rb true" }
+ before { bundle_config "init_gems_rb true" }
it "generates a gems.rb" do
bundle :init
diff --git a/spec/bundler/commands/inject_spec.rb b/spec/bundler/commands/inject_spec.rb
deleted file mode 100644
index 193806a02a..0000000000
--- a/spec/bundler/commands/inject_spec.rb
+++ /dev/null
@@ -1,117 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe "bundle inject", bundler: "< 3" do
- before :each do
- gemfile <<-G
- source "https://gem.repo1"
- gem "myrack"
- G
- end
-
- context "without a lockfile" do
- it "locks with the injected gems" do
- expect(bundled_app_lock).not_to exist
- bundle "inject 'myrack-obama' '> 0'"
- expect(bundled_app_lock.read).to match(/myrack-obama/)
- end
- end
-
- context "with a lockfile" do
- before do
- bundle "install"
- end
-
- it "adds the injected gems to the Gemfile" do
- expect(bundled_app_gemfile.read).not_to match(/myrack-obama/)
- bundle "inject 'myrack-obama' '> 0'"
- expect(bundled_app_gemfile.read).to match(/myrack-obama/)
- end
-
- it "locks with the injected gems" do
- expect(bundled_app_lock.read).not_to match(/myrack-obama/)
- bundle "inject 'myrack-obama' '> 0'"
- expect(bundled_app_lock.read).to match(/myrack-obama/)
- end
- end
-
- context "with injected gems already in the Gemfile" do
- it "doesn't add existing gems" do
- bundle "inject 'myrack' '> 0'", raise_on_error: false
- expect(err).to match(/cannot specify the same gem twice/i)
- end
- end
-
- context "incorrect arguments" do
- it "fails when more than 2 arguments are passed" do
- bundle "inject gem_name 1 v", raise_on_error: false
- expect(err).to eq(<<-E.strip)
-ERROR: "bundle inject" was called with arguments ["gem_name", "1", "v"]
-Usage: "bundle inject GEM VERSION"
- E
- end
- end
-
- context "with source option" do
- it "add gem with source option in gemfile" do
- bundle "inject 'foo' '>0' --source https://gem.repo1"
- gemfile = bundled_app_gemfile.read
- str = "gem \"foo\", \"> 0\", :source => \"https://gem.repo1\""
- expect(gemfile).to include str
- end
- end
-
- context "with group option" do
- it "add gem with group option in gemfile" do
- bundle "inject 'myrack-obama' '>0' --group=development"
- gemfile = bundled_app_gemfile.read
- str = "gem \"myrack-obama\", \"> 0\", :group => :development"
- expect(gemfile).to include str
- end
-
- it "add gem with multiple groups in gemfile" do
- bundle "inject 'myrack-obama' '>0' --group=development,test"
- gemfile = bundled_app_gemfile.read
- str = "gem \"myrack-obama\", \"> 0\", :groups => [:development, :test]"
- expect(gemfile).to include str
- end
- end
-
- context "when frozen" do
- before do
- bundle "install"
- if Bundler.feature_flag.bundler_3_mode?
- bundle "config set --local deployment true"
- else
- bundle "config set --local frozen true"
- end
- end
-
- it "injects anyway" do
- bundle "inject 'myrack-obama' '> 0'"
- expect(bundled_app_gemfile.read).to match(/myrack-obama/)
- end
-
- it "locks with the injected gems" do
- expect(bundled_app_lock.read).not_to match(/myrack-obama/)
- bundle "inject 'myrack-obama' '> 0'"
- expect(bundled_app_lock.read).to match(/myrack-obama/)
- end
-
- it "restores frozen afterwards" do
- bundle "inject 'myrack-obama' '> 0'"
- config = Psych.load(bundled_app(".bundle/config").read)
- expect(config["BUNDLE_DEPLOYMENT"] || config["BUNDLE_FROZEN"]).to eq("true")
- end
-
- it "doesn't allow Gemfile changes" do
- gemfile <<-G
- source "https://gem.repo1"
- gem "myrack-obama"
- G
- bundle "inject 'myrack' '> 0'", raise_on_error: false
- expect(err).to match(/the lockfile can't be updated because frozen mode is set/)
-
- expect(bundled_app_lock.read).not_to match(/myrack-obama/)
- end
- end
-end
diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb
index 992777722c..3b24434dc7 100644
--- a/spec/bundler/commands/install_spec.rb
+++ b/spec/bundler/commands/install_spec.rb
@@ -29,27 +29,81 @@ RSpec.describe "bundle install with gem sources" do
expect(bundled_app_lock).to exist
end
- it "does not create ./.bundle by default", bundler: "< 3" do
- gemfile <<-G
+ it "creates lockfile based on the lockfile method in Gemfile" do
+ install_gemfile <<-G
+ lockfile "OmgFile.lock"
+ source "https://gem.repo1"
+ gem "myrack", "1.0"
+ G
+
+ bundle "install"
+
+ expect(bundled_app("OmgFile.lock")).to exist
+ end
+
+ it "creates lockfile using BUNDLE_LOCKFILE instead of lockfile method" do
+ ENV["BUNDLE_LOCKFILE"] = "ReallyOmgFile.lock"
+ install_gemfile <<-G
+ lockfile "OmgFile.lock"
+ source "https://gem.repo1"
+ gem "myrack", "1.0"
+ G
+
+ expect(bundled_app("ReallyOmgFile.lock")).to exist
+ expect(bundled_app("OmgFile.lock")).not_to exist
+ ensure
+ ENV.delete("BUNDLE_LOCKFILE")
+ end
+
+ it "creates lockfile based on --lockfile option is given" do
+ gemfile bundled_app("OmgFile"), <<-G
+ source "https://gem.repo1"
+ gem "myrack", "1.0"
+ G
+
+ bundle "install --gemfile OmgFile --lockfile ReallyOmgFile.lock"
+
+ expect(bundled_app("ReallyOmgFile.lock")).to exist
+ end
+
+ it "does not make a lockfile if lockfile false is used in Gemfile" do
+ install_gemfile <<-G
+ lockfile false
+ source "https://gem.repo1"
+ gem 'myrack'
+ G
+
+ expect(bundled_app_lock).not_to exist
+ end
+
+ it "does not create ./.bundle by default" do
+ install_gemfile <<-G
source "https://gem.repo1"
gem "myrack"
G
- bundle :install # can't use install_gemfile since it sets retry
expect(bundled_app(".bundle")).not_to exist
end
+ it "will create a ./.bundle by default", bundler: "5" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ expect(bundled_app(".bundle")).to exist
+ end
+
it "does not create ./.bundle by default when installing to system gems" do
- gemfile <<-G
+ install_gemfile <<-G, env: { "BUNDLE_PATH__SYSTEM" => "true" }
source "https://gem.repo1"
gem "myrack"
G
- bundle :install, env: { "BUNDLE_PATH__SYSTEM" => "true" } # can't use install_gemfile since it sets retry
expect(bundled_app(".bundle")).not_to exist
end
- it "creates lock files based on the Gemfile name" do
+ it "creates lockfiles based on the Gemfile name" do
gemfile bundled_app("OmgFile"), <<-G
source "https://gem.repo1"
gem "myrack", "1.0"
@@ -60,6 +114,29 @@ RSpec.describe "bundle install with gem sources" do
expect(bundled_app("OmgFile.lock")).to exist
end
+ it "doesn't create a lockfile if --no-lock option is given" do
+ gemfile bundled_app("OmgFile"), <<-G
+ source "https://gem.repo1"
+ gem "myrack", "1.0"
+ G
+
+ bundle "install --gemfile OmgFile --no-lock"
+
+ expect(bundled_app("OmgFile.lock")).not_to exist
+ end
+
+ it "doesn't create a lockfile if --no-lock and --lockfile options are given" do
+ gemfile bundled_app("OmgFile"), <<-G
+ source "https://gem.repo1"
+ gem "myrack", "1.0"
+ G
+
+ bundle "install --gemfile OmgFile --no-lock --lockfile ReallyOmgFile.lock"
+
+ expect(bundled_app("OmgFile.lock")).not_to exist
+ expect(bundled_app("ReallyOmgFile.lock")).not_to exist
+ end
+
it "doesn't delete the lockfile if one already exists" do
install_gemfile <<-G
source "https://gem.repo1"
@@ -100,7 +177,7 @@ RSpec.describe "bundle install with gem sources" do
gem 'myrack'
G
- FileUtils.rm_rf(default_bundle_path("gems/myrack-1.0.0"))
+ FileUtils.rm_r(default_bundle_path("gems/myrack-1.0.0"))
bundle "install --verbose"
@@ -109,6 +186,23 @@ RSpec.describe "bundle install with gem sources" do
expect(the_bundle).to include_gems("myrack 1.0.0")
end
+ it "does not state that it's constantly reinstalling empty gems" do
+ build_repo4 do
+ build_gem "empty", "1.0.0", no_default: true
+ end
+
+ install_gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "empty"
+ G
+ gem_dir = default_bundle_path("gems/empty-1.0.0")
+ expect(gem_dir).to be_empty
+
+ bundle "install --verbose"
+ expect(out).not_to include("Installing empty")
+ end
+
it "fetches gems when multiple versions are specified" do
install_gemfile <<-G
source "https://gem.repo1"
@@ -243,7 +337,7 @@ RSpec.describe "bundle install with gem sources" do
gem "myrack"
G
- expect(last_command.stdboth).to include(plugin_msg)
+ expect(stdboth).to include(plugin_msg)
end
describe "with a gem that installs multiple platforms" do
@@ -281,7 +375,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "installs gems for windows" do
- simulate_platform x86_mswin32 do
+ simulate_platform "x86-mswin32" do
install_gemfile <<-G
source "https://gem.repo1"
gem "platform_specific"
@@ -290,53 +384,17 @@ RSpec.describe "bundle install with gem sources" do
expect(the_bundle).to include_gems("platform_specific 1.0 x86-mswin32")
end
end
- end
- describe "doing bundle install foo" do
- before do
- gemfile <<-G
- source "https://gem.repo1"
- gem "myrack"
- G
- end
-
- it "works" do
- bundle "config set --local path vendor"
- bundle "install"
- expect(the_bundle).to include_gems "myrack 1.0"
- end
-
- it "allows running bundle install --system without deleting foo", bundler: "< 3" do
- bundle "install --path vendor"
- bundle "install --system"
- FileUtils.rm_rf(bundled_app("vendor"))
- expect(the_bundle).to include_gems "myrack 1.0"
- end
-
- it "allows running bundle install --system after deleting foo", bundler: "< 3" do
- bundle "install --path vendor"
- FileUtils.rm_rf(bundled_app("vendor"))
- bundle "install --system"
- expect(the_bundle).to include_gems "myrack 1.0"
- end
- end
-
- it "finds gems in multiple sources", bundler: "< 3" do
- build_repo2 do
- build_gem "myrack", "1.2" do |s|
- s.executables = "myrackup"
+ it "installs gems for aarch64-mingw-ucrt" do
+ simulate_platform "aarch64-mingw-ucrt" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "platform_specific"
+ G
end
- end
-
- install_gemfile <<-G, artifice: "compact_index_extra"
- source "https://gemserver.test"
- source "https://gemserver.test/extra"
-
- gem "activesupport", "1.2.3"
- gem "myrack", "1.2"
- G
- expect(the_bundle).to include_gems "myrack 1.2", "activesupport 1.2.3"
+ expect(out).to include("Installing platform_specific 1.0 (aarch64-mingw-ucrt)")
+ end
end
it "gives useful errors if no global sources are set, and gems not installed locally, with and without a lockfile" do
@@ -358,7 +416,7 @@ RSpec.describe "bundle install with gem sources" do
myrack
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "install", raise_on_error: false
@@ -446,7 +504,7 @@ RSpec.describe "bundle install with gem sources" do
expect(the_bundle).to include_gems("my-private-gem 1.0")
end
- it "throws a warning if a gem is added once in Gemfile and also inside a gemspec as a development dependency, with different requirements" do
+ it "does not warn if a gem is added once in Gemfile and also inside a gemspec as a development dependency, with compatible requirements" do
build_lib "my-gem", path: bundled_app do |s|
s.add_development_dependency "rubocop", "~> 1.36.0"
end
@@ -466,14 +524,32 @@ RSpec.describe "bundle install with gem sources" do
bundle :install
- expect(err).to include("A gemspec development dependency (rubocop, ~> 1.36.0) is being overridden by a Gemfile dependency (rubocop, >= 0).")
- 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")
+ expect(err).to be_empty
+
+ expect(the_bundle).to include_gems("rubocop 1.36.0")
+ end
+
+ it "raises an error if a gem is added once in Gemfile and also inside a gemspec as a development dependency, with incompatible requirements" do
+ build_lib "my-gem", path: bundled_app do |s|
+ s.add_development_dependency "rubocop", "~> 1.36.0"
+ end
+
+ build_repo4 do
+ build_gem "rubocop", "1.36.0"
+ build_gem "rubocop", "1.37.1"
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gemspec
+
+ gem "rubocop", "~> 1.37.0", group: :development
+ G
+
+ bundle :install, raise_on_error: false
- # 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("rubocop 1.37.1")
+ expect(err).to include("The rubocop dependency has conflicting requirements in Gemfile (~> 1.37.0) and gemspec (~> 1.36.0)")
end
it "includes the gem without warning if two gemspecs add it with the same requirement" do
@@ -559,36 +635,31 @@ RSpec.describe "bundle install with gem sources" do
bundle :install, raise_on_error: false
- expect(err).to include("Two gemspecs have conflicting requirements on the same gem: rubocop (~> 1.36.0, development) and rubocop (~> 2.0, development). Bundler cannot continue.")
+ expect(err).to include("Two gemspec development dependencies have conflicting requirements on the same gem: rubocop (~> 1.36.0) and rubocop (~> 2.0). Bundler cannot continue.")
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"
+ it "errors out if a gem is specified in a gemspec and in the Gemfile" do
+ gem = tmp("my-gem-1")
+
+ build_lib "rubocop", path: gem do |s|
+ s.add_development_dependency "rubocop", "~> 1.0"
end
build_repo4 do
- build_gem "rails", "7.0.8"
+ build_gem "rubocop"
end
gemfile <<~G
source "https://gem.repo4"
- gem "rails", "~> 7.0.8"
-
- gemspec
+ gem "rubocop", :path => "#{gem}"
+ gemspec path: "#{gem}"
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")
+ bundle :install, raise_on_error: false
- # 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")
+ expect(err).to include("There was an error parsing `Gemfile`: You cannot specify the same gem twice coming from different sources.")
+ expect(err).to include("You specified that rubocop (>= 0) should come from source at `#{gem}` and gemspec at `#{gem}`")
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
@@ -673,8 +744,6 @@ RSpec.describe "bundle install with gem sources" do
end
it "gracefully handles error when rubygems server is unavailable" do
- skip "networking issue" if Gem.win_platform?
-
install_gemfile <<-G, artifice: nil, raise_on_error: false
source "https://gem.repo1"
source "http://0.0.0.0:9384" do
@@ -682,20 +751,19 @@ RSpec.describe "bundle install with gem sources" do
end
G
- expect(err).to include("Could not fetch specs from http://0.0.0.0:9384/")
+ expect(err).to eq("Could not reach host 0.0.0.0:9384. Check your network connection and try again.")
expect(err).not_to include("file://")
end
it "fails gracefully when downloading an invalid specification from the full index" do
build_repo2(build_compact_index: false) do
build_gem "ajp-rails", "0.0.0", gemspec: false, skip_validation: true do |s|
- bad_deps = [["ruby-ajp", ">= 0.2.0"], ["rails", ">= 0.14"]]
+ invalid_deps = [["ruby-ajp", ">= 0.2.0"], ["rails", ">= 0.14"]]
s.
instance_variable_get(:@spec).
- instance_variable_set(:@dependencies, bad_deps)
-
- raise "failed to set bad deps" unless s.dependencies == bad_deps
+ instance_variable_set(:@dependencies, invalid_deps)
end
+
build_gem "ruby-ajp", "1.0.0"
end
@@ -705,7 +773,7 @@ RSpec.describe "bundle install with gem sources" do
gem "ajp-rails", "0.0.0"
G
- expect(last_command.stdboth).not_to match(/Error Report/i)
+ expect(stdboth).not_to match(/Error Report/i)
expect(err).to include("An error occurred while installing ajp-rails (0.0.0), and Bundler cannot continue.").
and include("Bundler::APIResponseInvalidDependenciesError")
end
@@ -765,10 +833,10 @@ RSpec.describe "bundle install with gem sources" do
DEPENDENCIES
#{checksums}
RUBY VERSION
- #{Bundler::RubyVersion.system}
+ #{Bundler::RubyVersion.system}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -791,10 +859,10 @@ RSpec.describe "bundle install with gem sources" do
DEPENDENCIES
#{checksums}
RUBY VERSION
- #{Bundler::RubyVersion.system}
+ #{Bundler::RubyVersion.system}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -849,7 +917,7 @@ RSpec.describe "bundle install with gem sources" do
describe "when requesting a quiet install via --quiet" do
it "should be quiet if there are no warnings" do
- bundle "config set force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
gemfile <<-G
source "https://gem.repo1"
@@ -862,7 +930,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "should still display warnings and errors" do
- bundle "config set force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
create_file("install_with_warning.rb", <<~RUBY)
require "#{lib_dir}/bundler"
@@ -907,7 +975,7 @@ RSpec.describe "bundle install with gem sources" do
it "should display a proper message to explain the problem" do
FileUtils.chmod(0o500, bundle_path)
- bundle "config set --local path vendor"
+ bundle_config "path vendor"
bundle :install, raise_on_error: false
expect(err).to include(bundle_path.to_s)
expect(err).to include("grant executable permissions")
@@ -927,7 +995,7 @@ RSpec.describe "bundle install with gem sources" do
it "should display a proper message to explain the problem" do
FileUtils.chmod("-x", gems_path)
- bundle "config set --local path vendor"
+ bundle_config "path vendor"
begin
bundle :install, raise_on_error: false
@@ -957,7 +1025,7 @@ RSpec.describe "bundle install with gem sources" do
it "should display a proper message to explain the problem" do
FileUtils.chmod("-x", full_gem_path)
- bundle "config set --local path vendor"
+ bundle_config "path vendor"
begin
bundle :install, raise_on_error: false
@@ -987,7 +1055,7 @@ RSpec.describe "bundle install with gem sources" do
it "should display a proper message to explain the problem" do
FileUtils.chmod("-x", bin_dir)
- bundle "config set --local path vendor"
+ bundle_config "path vendor"
begin
bundle :install, raise_on_error: false
@@ -1017,7 +1085,7 @@ RSpec.describe "bundle install with gem sources" do
it "should display a proper message to explain the problem" do
FileUtils.chmod("-w", bin_dir)
- bundle "config set --local path vendor"
+ bundle_config "path vendor"
begin
bundle :install, raise_on_error: false
@@ -1047,7 +1115,7 @@ RSpec.describe "bundle install with gem sources" do
it "should display a proper message to explain the problem" do
FileUtils.chmod("-x", extensions_path)
- bundle "config set --local path vendor"
+ bundle_config "path vendor"
begin
bundle :install, raise_on_error: false
@@ -1082,7 +1150,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "should display a proper message to explain the problem" do
- bundle "config set --local path vendor"
+ bundle_config "path vendor"
bundle :install
expect(out).to include("Bundle complete!")
expect(err).to be_empty
@@ -1090,7 +1158,7 @@ RSpec.describe "bundle install with gem sources" do
FileUtils.chmod("-x", foo_path)
begin
- bundle "install --redownload", raise_on_error: false
+ bundle "install --force", raise_on_error: false
ensure
FileUtils.chmod("+x", foo_path)
end
@@ -1118,7 +1186,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "should still work" do
- bundle "config set --local path vendor"
+ bundle_config "path vendor"
bundle :install
expect(out).to include("Bundle complete!")
expect(err).to be_empty
@@ -1126,7 +1194,7 @@ RSpec.describe "bundle install with gem sources" do
FileUtils.chmod("-w", gem_home)
begin
- bundle "install --redownload"
+ bundle "install --force"
ensure
FileUtils.chmod("+w", gem_home)
end
@@ -1153,14 +1221,14 @@ RSpec.describe "bundle install with gem sources" do
end
it "should display a proper message to explain the problem" do
- bundle "config set --local path vendor"
+ bundle_config "path vendor"
bundle :install
expect(out).to include("Bundle complete!")
expect(err).to be_empty
FileUtils.chmod(0o777, gems_path)
- bundle "install --redownload", raise_on_error: false
+ bundle "install --force", raise_on_error: false
expect(err).to include("Bundler cannot reinstall foo-1.0.0 because there's a previous installation of it at #{gems_path}/foo-1.0.0 that is unsafe to remove")
end
@@ -1184,7 +1252,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "does not try to remove the directory and thus don't abort with an error about unsafe directory removal" do
- bundle "config set --local path vendor"
+ bundle_config "path vendor"
FileUtils.mkdir_p(gems_path)
FileUtils.chmod(0o777, gems_path)
@@ -1208,7 +1276,7 @@ RSpec.describe "bundle install with gem sources" do
it "should display a proper message to explain the problem" do
FileUtils.chmod(0o500, cache_path)
- bundle "config set --local path vendor"
+ bundle_config "path vendor"
bundle :install, raise_on_error: false
expect(err).to include(cache_path.to_s)
expect(err).to include("grant write permissions")
@@ -1223,7 +1291,7 @@ RSpec.describe "bundle install with gem sources" do
source "https://gem.repo1"
gem 'myrack'
G
- bundle "config path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle :install
expect(out).to include("Bundle complete!")
expect(err).to be_empty
@@ -1238,6 +1306,158 @@ RSpec.describe "bundle install with gem sources" do
end
end
+ describe "when using umask 002 and setgid bit", :permissions do
+ let(:gems_path) { bundled_app("vendor/#{Bundler.ruby_scope}/gems") }
+ let(:foo_path) { gems_path.join("foo-1.0.0") }
+
+ before do
+ build_repo4 do
+ build_gem "foo", "1.0.0" do |s|
+ s.write "CHANGELOG.md", "foo"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+ gem 'foo'
+ G
+
+ FileUtils.mkdir_p(gems_path)
+ FileUtils.chmod("g+s", gems_path)
+ end
+
+ it "should create the gem directory with proper permissions" do
+ with_umask(0o002) do
+ bundle_config "path vendor"
+ bundle :install
+ expect(out).to include("Bundle complete!")
+ expect(err).to be_empty
+ # Linux's SysV-derived mkdir(2) propagates the set-group-ID bit
+ # from the parent directory to newly created subdirectories. BSD
+ # (including macOS) inherits the parent's group via mkdir(2) but
+ # does not copy the set-group-ID bit itself, so the expected
+ # mode differs by platform.
+ expected = RUBY_PLATFORM.include?("darwin") ? 0o0775 : 0o2775
+ expect(File.stat(foo_path).mode & 0o7777).to eq(expected)
+ end
+ end
+ end
+
+ describe "parallel make" do
+ before do
+ unless Gem::Installer.private_method_defined?(:build_jobs)
+ skip "This example is runnable when RubyGems::Installer implements `build_jobs`"
+ end
+
+ @old_makeflags = ENV["MAKEFLAGS"]
+ @gemspec = nil
+
+ extconf_code = <<~CODE
+ require "mkmf"
+ create_makefile("foo")
+ CODE
+
+ build_repo4 do
+ build_gem "mypsych", "4.0.6" do |s|
+ @gemspec = s
+ extension = "ext/mypsych/extconf.rb"
+ s.extensions = extension
+
+ s.write(extension, extconf_code)
+ end
+ end
+ end
+
+ after do
+ if @old_makeflags
+ ENV["MAKEFLAGS"] = @old_makeflags
+ else
+ ENV.delete("MAKEFLAGS")
+ end
+ end
+
+ it "doesn't pass down -j to make when MAKEFLAGS is set" do
+ ENV["MAKEFLAGS"] = "-j1"
+
+ install_gemfile(<<~G, env: { "BUNDLE_JOBS" => "8" })
+ source "https://gem.repo4"
+ gem "mypsych"
+ G
+
+ gem_make_out = File.read(File.join(@gemspec.extension_dir, "gem_make.out"))
+
+ expect(gem_make_out).not_to include("make -j8")
+ end
+
+ it "pass down the BUNDLE_JOBS to RubyGems when running the compilation of an extension" do
+ ENV.delete("MAKEFLAGS")
+
+ install_gemfile(<<~G, env: { "BUNDLE_JOBS" => "8" })
+ source "https://gem.repo4"
+ gem "mypsych"
+ G
+
+ gem_make_out = File.read(File.join(@gemspec.extension_dir, "gem_make.out"))
+
+ expect(gem_make_out).to include("make -j8")
+ end
+
+ it "uses nprocessors by default" do
+ ENV.delete("MAKEFLAGS")
+
+ install_gemfile(<<~G)
+ source "https://gem.repo4"
+ gem "mypsych"
+ G
+
+ gem_make_out = File.read(File.join(@gemspec.extension_dir, "gem_make.out"))
+
+ expect(gem_make_out).to include("make -j#{Etc.nprocessors + 1}")
+ end
+ end
+
+ describe "when a native extension requires a transitive dependency at build time" do
+ before do
+ build_repo4 do
+ build_gem "alpha", "1.0.0" do |s|
+ extension = "ext/alpha/extconf.rb"
+ s.extensions = extension
+ s.write(extension, <<~CODE)
+ require "mkmf"
+ sleep 1
+ create_makefile("alpha")
+ CODE
+ s.write "lib/alpha.rb", "ALPHA = '1.0.0'"
+ end
+
+ build_gem "beta", "1.0.0" do |s|
+ s.add_dependency "alpha"
+ s.write "lib/beta.rb", "require 'alpha'\nBETA = '1.0.0'"
+ end
+
+ build_gem "gamma", "1.0.0" do |s|
+ s.add_dependency "beta"
+ extension = "ext/gamma/extconf.rb"
+ s.extensions = extension
+ s.write(extension, <<~EXTCONF)
+ require "beta"
+ require "mkmf"
+ create_makefile("gamma")
+ EXTCONF
+ end
+ end
+ end
+
+ it "installs successfully" do
+ install_gemfile <<~G
+ source "https://gem.repo4"
+ gem "gamma"
+ G
+
+ expect(the_bundle).to include_gems "alpha 1.0.0", "beta 1.0.0", "gamma 1.0.0"
+ end
+ end
+
describe "when configured path is UTF-8 and a file inside a gem package too" do
let(:app_path) do
path = tmp("♥")
@@ -1258,7 +1478,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "works" do
- bundle "config path #{app_path}/vendor/bundle", dir: app_path
+ bundle "config set path #{app_path}/vendor/bundle", dir: app_path
install_gemfile app_path.join("Gemfile"),<<~G, dir: app_path
source "https://gem.repo4"
@@ -1273,7 +1493,7 @@ RSpec.describe "bundle install with gem sources" do
source "https://gem.repo1"
gem "myrack"
G
- bundle "config set --local path bundle"
+ bundle_config "path bundle"
bundle "install", standalone: true
end
@@ -1331,7 +1551,7 @@ RSpec.describe "bundle install with gem sources" do
libv8
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
simulate_platform("x86_64-linux", &example)
@@ -1340,7 +1560,8 @@ RSpec.describe "bundle install with gem sources" do
it "adds the current platform to the lockfile" do
bundle "install --verbose"
- expect(out).to include("re-resolving dependencies because your lockfile does not include the current platform")
+ expect(out).to include("re-resolving dependencies because your lockfile is missing the current platform")
+ expect(out).not_to include("you are adding a new platform to your lockfile")
expect(lockfile).to eq <<~L
GEM
@@ -1357,12 +1578,12 @@ RSpec.describe "bundle install with gem sources" do
libv8
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
it "fails loudly if frozen mode set" do
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
bundle "install", raise_on_error: false
expect(err).to eq(
@@ -1433,12 +1654,12 @@ RSpec.describe "bundle install with gem sources" do
#{Bundler::RubyVersion.system}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
it "automatically fixes the lockfile" do
- bundle "config set --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
simulate_platform "x86_64-linux" do
bundle "install", artifice: "compact_index"
@@ -1474,14 +1695,64 @@ RSpec.describe "bundle install with gem sources" do
loofah (~> 2.12.0)
#{checksums}
RUBY VERSION
- #{Bundler::RubyVersion.system}
+ #{Bundler::RubyVersion.system}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
+ context "when lockfile has incorrect dependencies" do
+ before do
+ build_repo2
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "myrack_middleware"
+ G
+
+ system_gems "myrack_middleware-1.0", path: default_bundle_path
+
+ # we want to raise when the 1.0 line should be followed by " myrack (= 0.9.1)" but isn't
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ myrack_middleware (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack_middleware
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "raises a clear error message when frozen" do
+ bundle_config "frozen true"
+ bundle "install", raise_on_error: false
+
+ expect(exitstatus).to eq(41)
+ expect(err).to include("Bundler found incorrect dependencies in the lockfile for myrack_middleware-1.0")
+ expect(err).to include("myrack: gemspec specifies = 0.9.1, not in lockfile")
+ end
+
+ it "updates the lockfile when not frozen" do
+ missing_dep = "myrack (0.9.1)"
+ expect(lockfile).not_to include(missing_dep)
+
+ bundle_config "frozen false"
+ bundle :install
+
+ expect(lockfile).to include(missing_dep)
+ expect(out).to include("now installed")
+ end
+ end
+
context "with --local flag" do
before do
system_gems "myrack-1.0.0", path: default_bundle_path
@@ -1502,7 +1773,7 @@ RSpec.describe "bundle install with gem sources" do
context "with only option" do
before do
- bundle "config set only a:b"
+ bundle_config "only a:b"
end
it "installs only gems of the specified groups" do
@@ -1521,68 +1792,94 @@ RSpec.describe "bundle install with gem sources" do
end
context "with --prefer-local flag" do
- before do
- build_repo4 do
- build_gem "foo", "1.0.1"
- build_gem "foo", "1.0.0"
- build_gem "bar", "1.0.0"
+ context "and gems available locally" do
+ before do
+ build_repo4 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 "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"
+ 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
- system_gems "foo-1.0.0", path: default_bundle_path, gem_repo: gem_repo4
- end
+ it "fetches remote sources when not available locally" do
+ install_gemfile <<-G, "prefer-local": true, verbose: true
+ source "https://gem.repo4"
- it "fetches remote sources when not available locally" do
- install_gemfile <<-G, "prefer-local": true, verbose: true
- source "https://gem.repo4"
+ gem "foo"
+ gem "bar"
+ G
- gem "foo"
- gem "bar"
- G
+ 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
- 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"
- 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
- 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
- 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"
- 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
- 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
- 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
+ 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
- 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"
+ context "and no gems available locally" do
+ before do
+ build_repo4 do
+ build_gem "myreline", "0.3.8"
+ build_gem "debug", "0.2.1"
- gem "b"
- G
+ build_gem "debug", "1.10.0" do |s|
+ s.add_dependency "myreline"
+ end
+ end
+ end
- 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
+ it "resolves to the latest version if no gems are available locally" do
+ install_gemfile <<~G, "prefer-local": true, verbose: true
+ source "https://gem.repo4"
+
+ gem "debug"
+ G
+
+ expect(out).to include("Fetching debug 1.10.0").and include("Installing debug 1.10.0").and include("Fetching myreline 0.3.8").and include("Installing myreline 0.3.8")
+ expect(last_command).to be_success
+ end
end
end
@@ -1590,7 +1887,7 @@ RSpec.describe "bundle install with gem sources" do
before do
symlinked_bundled_app = tmp("bundled_app-symlink")
File.symlink(bundled_app, symlinked_bundled_app)
- bundle "config path #{File.join(symlinked_bundled_app, ".vendor")}"
+ bundle_config "path #{File.join(symlinked_bundled_app, ".vendor")}"
binman_path = tmp("binman")
FileUtils.mkdir_p binman_path
@@ -1679,7 +1976,7 @@ RSpec.describe "bundle install with gem sources" do
zzz!
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -1729,7 +2026,7 @@ RSpec.describe "bundle install with gem sources" do
end
it "only installs executable files in bin" do
- bundle "config set --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
install_gemfile <<~G
source "https://gem.repo1"
@@ -1741,6 +2038,25 @@ RSpec.describe "bundle install with gem sources" do
expect(Dir.glob(vendored_gems("bin/*"))).to eq(expected_executables)
end
+ it "prevents removing binstubs when BUNDLE_CLEAN is set" do
+ build_repo4 do
+ build_gem "kamal", "4.0.6" do |s|
+ s.executables = ["kamal"]
+ end
+ end
+
+ gemfile = <<~G
+ source "https://gem.repo4"
+ gem "kamal"
+ G
+
+ install_gemfile(gemfile, env: { "BUNDLE_CLEAN" => "true", "BUNDLE_PATH" => "vendor/bundle" })
+
+ expected_executables = [vendored_gems("bin/kamal").to_s]
+ expected_executables << vendored_gems("bin/kamal.bat").to_s if Gem.win_platform?
+ expect(Dir.glob(vendored_gems("bin/*"))).to eq(expected_executables)
+ end
+
it "preserves lockfile versions conservatively" do
build_repo4 do
build_gem "mypsych", "4.0.6" do |s|
@@ -1770,7 +2086,7 @@ RSpec.describe "bundle install with gem sources" do
mypsych (~> 4.0)
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
install_gemfile <<~G
@@ -1793,7 +2109,7 @@ RSpec.describe "bundle install with gem sources" do
mypsych (~> 5.0)
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
diff --git a/spec/bundler/commands/licenses_spec.rb b/spec/bundler/commands/licenses_spec.rb
index bfec938efd..ebfad5ed4a 100644
--- a/spec/bundler/commands/licenses_spec.rb
+++ b/spec/bundler/commands/licenses_spec.rb
@@ -30,7 +30,7 @@ RSpec.describe "bundle licenses" do
gem "foo"
G
- bundle "config set auto_install 1"
+ bundle_config "auto_install 1"
bundle :licenses
expect(out).to include("Installing foo 1.0")
end
diff --git a/spec/bundler/commands/list_spec.rb b/spec/bundler/commands/list_spec.rb
index cc0db9169d..c890646a81 100644
--- a/spec/bundler/commands/list_spec.rb
+++ b/spec/bundler/commands/list_spec.rb
@@ -1,6 +1,28 @@
# frozen_string_literal: true
+require "json"
+
RSpec.describe "bundle list" do
+ def find_gem_name(json:, name:)
+ parse_json(json)["gems"].detect {|h| h["name"] == name }
+ end
+
+ def parse_json(json)
+ JSON.parse(json)
+ end
+
+ context "in verbose mode" do
+ it "logs the actual flags passed to the command" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ G
+
+ bundle "list --verbose"
+
+ expect(out).to include("Running `bundle list --verbose`")
+ end
+ end
+
context "with name-only and paths option" do
it "raises an error" do
bundle "list --name-only --paths", raise_on_error: false
@@ -17,6 +39,20 @@ RSpec.describe "bundle list" do
end
end
+ context "with invalid format option" do
+ before do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ G
+ end
+
+ it "raises an error" do
+ bundle "list --format=nope", raise_on_error: false
+
+ expect(err).to eq "Unknown option`--format=nope`. Supported formats: `json`"
+ end
+ end
+
describe "with without-group option" do
before do
install_gemfile <<-G
@@ -36,6 +72,17 @@ RSpec.describe "bundle list" do
expect(out).to include(" * rails (2.3.2)")
expect(out).not_to include(" * rspec (1.2.7)")
end
+
+ it "prints the gems not in the specified group with json" do
+ bundle "list --without-group test --format=json"
+
+ gem = find_gem_name(json: out, name: "myrack")
+ expect(gem["version"]).to eq("1.0.0")
+ gem = find_gem_name(json: out, name: "rails")
+ expect(gem["version"]).to eq("2.3.2")
+ gem = find_gem_name(json: out, name: "rspec")
+ expect(gem).to be_nil
+ end
end
context "when group is not found" do
@@ -54,6 +101,17 @@ RSpec.describe "bundle list" do
expect(out).not_to include(" * rails (2.3.2)")
expect(out).not_to include(" * rspec (1.2.7)")
end
+
+ it "prints the gems not in the specified groups with json" do
+ bundle "list --without-group test production --format=json"
+
+ gem = find_gem_name(json: out, name: "myrack")
+ expect(gem["version"]).to eq("1.0.0")
+ gem = find_gem_name(json: out, name: "rails")
+ expect(gem).to be_nil
+ gem = find_gem_name(json: out, name: "rspec")
+ expect(gem).to be_nil
+ end
end
end
@@ -75,6 +133,15 @@ RSpec.describe "bundle list" do
expect(out).to include(" * myrack (1.0.0)")
expect(out).not_to include(" * rspec (1.2.7)")
end
+
+ it "prints the gems in the specified group with json" do
+ bundle "list --only-group default --format=json"
+
+ gem = find_gem_name(json: out, name: "myrack")
+ expect(gem["version"]).to eq("1.0.0")
+ gem = find_gem_name(json: out, name: "rspec")
+ expect(gem).to be_nil
+ end
end
context "when group is not found" do
@@ -93,6 +160,17 @@ RSpec.describe "bundle list" do
expect(out).to include(" * rails (2.3.2)")
expect(out).not_to include(" * rspec (1.2.7)")
end
+
+ it "prints the gems in the specified groups with json" do
+ bundle "list --only-group default production --format=json"
+
+ gem = find_gem_name(json: out, name: "myrack")
+ expect(gem["version"]).to eq("1.0.0")
+ gem = find_gem_name(json: out, name: "rails")
+ expect(gem["version"]).to eq("2.3.2")
+ gem = find_gem_name(json: out, name: "rspec")
+ expect(gem).to be_nil
+ end
end
end
@@ -112,6 +190,15 @@ RSpec.describe "bundle list" do
expect(out).to include("myrack")
expect(out).to include("rspec")
end
+
+ it "prints only the name of the gems in the bundle with json" do
+ bundle "list --name-only --format=json"
+
+ gem = find_gem_name(json: out, name: "myrack")
+ expect(gem.keys).to eq(["name"])
+ gem = find_gem_name(json: out, name: "rspec")
+ expect(gem.keys).to eq(["name"])
+ end
end
context "with paths option" do
@@ -146,6 +233,27 @@ RSpec.describe "bundle list" do
expect(out).to match(%r{.*\/git_test\-\w})
expect(out).to match(%r{.*\/gemspec_test})
end
+
+ it "prints the path of each gem in the bundle with json" do
+ bundle "list --paths --format=json"
+
+ gem = find_gem_name(json: out, name: "rails")
+ expect(gem["path"]).to match(%r{.*\/rails\-2\.3\.2})
+ expect(gem["git_version"]).to be_nil
+
+ gem = find_gem_name(json: out, name: "myrack")
+ expect(gem["path"]).to match(%r{.*\/myrack\-1\.2})
+ expect(gem["git_version"]).to be_nil
+
+ gem = find_gem_name(json: out, name: "git_test")
+ expect(gem["path"]).to match(%r{.*\/git_test\-\w})
+ expect(gem["git_version"]).to be_truthy
+ expect(gem["git_version"].strip).to eq(gem["git_version"])
+
+ gem = find_gem_name(json: out, name: "gemspec_test")
+ expect(gem["path"]).to match(%r{.*\/gemspec_test})
+ expect(gem["git_version"]).to be_nil
+ end
end
context "when no gems are in the gemfile" do
@@ -159,6 +267,11 @@ RSpec.describe "bundle list" do
bundle "list"
expect(out).to include("No gems in the Gemfile")
end
+
+ it "prints empty json" do
+ bundle "list --format=json"
+ expect(parse_json(out)["gems"]).to eq([])
+ end
end
context "without options" do
@@ -175,6 +288,13 @@ RSpec.describe "bundle list" do
bundle "list"
expect(out).to include(" * myrack (1.0.0)")
end
+
+ it "lists gems installed in the bundle with json" do
+ bundle "list --format=json"
+
+ gem = find_gem_name(json: out, name: "myrack")
+ expect(gem["version"]).to eq("1.0.0")
+ end
end
context "when using the ls alias" do
diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb
index db600e356f..8ab3cc7e8d 100644
--- a/spec/bundler/commands/lock_spec.rb
+++ b/spec/bundler/commands/lock_spec.rb
@@ -46,7 +46,7 @@ RSpec.describe "bundle lock" do
weakling
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -95,15 +95,14 @@ RSpec.describe "bundle lock" do
weakling
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
- before :each do
+ let(:gemfile_with_rails_weakling_and_foo_from_repo4) do
build_repo4 do
- FileUtils.cp rake_path, "#{gem_repo4}/gems/"
-
build_gem "rake", "10.0.1"
+ build_gem "rake", rake_version
%w[2.3.1 2.3.2].each do |version|
build_gem "rails", version do |s|
@@ -143,12 +142,16 @@ RSpec.describe "bundle lock" do
end
it "prints a lockfile when there is no existing lockfile with --print" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
bundle "lock --print"
expect(out).to eq(expected_lockfile.chomp)
end
it "prints a lockfile when there is an existing lockfile with --print" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
lockfile expected_lockfile
bundle "lock --print"
@@ -157,6 +160,8 @@ RSpec.describe "bundle lock" do
end
it "prints a lockfile when there is an existing checksums lockfile with --print" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
lockfile expected_lockfile
bundle "lock --print"
@@ -165,12 +170,16 @@ RSpec.describe "bundle lock" do
end
it "writes a lockfile when there is no existing lockfile" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
bundle "lock"
expect(read_lockfile).to eq(expected_lockfile)
end
it "prints a lockfile without fetching new checksums if the existing lockfile had no checksums" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
lockfile expected_lockfile
bundle "lock --print"
@@ -179,6 +188,8 @@ RSpec.describe "bundle lock" do
end
it "touches the lockfile when there is an existing lockfile that does not need changes" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
lockfile expected_lockfile
expect do
@@ -187,6 +198,8 @@ RSpec.describe "bundle lock" do
end
it "does not touch lockfile with --print" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
lockfile expected_lockfile
expect do
@@ -195,6 +208,8 @@ RSpec.describe "bundle lock" do
end
it "writes a lockfile when there is an outdated lockfile using --update" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
lockfile outdated_lockfile
bundle "lock --update"
@@ -203,6 +218,8 @@ RSpec.describe "bundle lock" do
end
it "prints an updated lockfile when there is an outdated lockfile using --print --update" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
lockfile outdated_lockfile
bundle "lock --print --update"
@@ -211,6 +228,8 @@ RSpec.describe "bundle lock" do
end
it "emits info messages to stderr when updating an outdated lockfile using --print --update" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
lockfile outdated_lockfile
bundle "lock --print --update"
@@ -222,6 +241,8 @@ RSpec.describe "bundle lock" do
end
it "writes a lockfile when there is an outdated lockfile and bundle is frozen" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
lockfile outdated_lockfile
bundle "lock --update", env: { "BUNDLE_FROZEN" => "true" }
@@ -230,12 +251,16 @@ RSpec.describe "bundle lock" do
end
it "does not fetch remote specs when using the --local option" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
bundle "lock --update --local", raise_on_error: false
expect(err).to match(/locally installed gems/)
end
it "does not fetch remote checksums with --local" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
lockfile expected_lockfile
bundle "lock --print --local"
@@ -244,6 +269,8 @@ RSpec.describe "bundle lock" do
end
it "works with --gemfile flag" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
gemfile "CustomGemfile", <<-G
source "https://gem.repo4"
gem "foo"
@@ -267,7 +294,7 @@ RSpec.describe "bundle lock" do
foo
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
expect(out).to match(/Writing lockfile to.+CustomGemfile\.lock/)
expect(read_lockfile("CustomGemfile.lock")).to eq(lockfile)
@@ -275,6 +302,8 @@ RSpec.describe "bundle lock" do
end
it "writes to a custom location using --lockfile" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
bundle "lock --lockfile=lock"
expect(out).to match(/Writing lockfile to.+lock/)
@@ -282,7 +311,47 @@ RSpec.describe "bundle lock" do
expect { read_lockfile }.to raise_error(Errno::ENOENT)
end
+ it "updates a specific gem and write to a custom location" do
+ build_repo4 do
+ build_gem "foo", %w[1.0.2 1.0.3]
+ build_gem "warning", %w[1.4.0 1.5.0]
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "foo"
+ gem "warning"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4
+ specs:
+ foo (1.0.2)
+ warning (1.4.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ uri
+ warning
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock --update foo --lockfile=lock"
+
+ lockfile_content = read_lockfile("lock")
+ expect(lockfile_content).to include("foo (1.0.3)")
+ expect(lockfile_content).to include("warning (1.4.0)")
+ end
+
it "writes to custom location using --lockfile when a default lockfile is present" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
bundle "install"
bundle "lock --lockfile=lock"
@@ -330,7 +399,7 @@ RSpec.describe "bundle lock" do
weakling
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
expect(out).to match(/Writing lockfile to.+lock/)
@@ -338,6 +407,8 @@ RSpec.describe "bundle lock" do
end
it "update specific gems using --update" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
checksums = checksums_section_when_enabled do |c|
c.checksum gem_repo4, "actionmailer", "2.3.1"
c.checksum gem_repo4, "actionpack", "2.3.1"
@@ -382,7 +453,7 @@ RSpec.describe "bundle lock" do
weakling
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
lockfile lockfile_with_outdated_rails_and_rake
@@ -439,7 +510,7 @@ RSpec.describe "bundle lock" do
tapioca
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "lock --update tapioca --verbose"
@@ -505,7 +576,7 @@ RSpec.describe "bundle lock" do
tapioca
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "lock --update tapioca"
@@ -515,6 +586,8 @@ RSpec.describe "bundle lock" do
end
it "preserves unknown checksum algorithms" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
lockfile expected_lockfile.gsub(/(sha256=[a-f0-9]+)$/, "constant=true,\\1,xyz=123")
previous_lockfile = read_lockfile
@@ -525,6 +598,8 @@ RSpec.describe "bundle lock" do
end
it "does not unlock git sources when only uri shape changes" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
build_git("foo")
install_gemfile <<-G
@@ -543,6 +618,8 @@ RSpec.describe "bundle lock" do
end
it "updates specific gems using --update using the locked revision of unrelated git gems for resolving" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
ref = build_git("foo").ref_for("HEAD")
gemfile <<-G
@@ -572,7 +649,7 @@ RSpec.describe "bundle lock" do
rake
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "lock --update rake --verbose"
@@ -581,6 +658,8 @@ RSpec.describe "bundle lock" do
end
it "errors when updating a missing specific gems using --update" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
lockfile expected_lockfile
bundle "lock --update blahblah", raise_on_error: false
@@ -590,14 +669,16 @@ RSpec.describe "bundle lock" do
end
it "can lock without downloading gems" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
gemfile <<-G
source "https://gem.repo1"
gem "thin"
gem "myrack_middleware", :group => "test"
G
- bundle "config set without test"
- bundle "config set path vendor/bundle"
+ bundle_config "without test"
+ bundle_config "path vendor/bundle"
bundle "lock", verbose: true
expect(bundled_app("vendor/bundle")).not_to exist
end
@@ -645,13 +726,13 @@ RSpec.describe "bundle lock" do
it "single gem updates dependent gem to minor" do
bundle "lock --update foo --patch"
- expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.4.5 bar-2.1.1 qux-1.0.0].sort)
+ expect(the_bundle.locked_specs).to eq(%w[foo-1.4.5 bar-2.1.1 qux-1.0.0].sort)
end
it "minor preferred with strict" do
bundle "lock --update --minor --strict"
- expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.5.0 bar-2.1.1 qux-1.1.0].sort)
+ expect(the_bundle.locked_specs).to eq(%w[foo-1.5.0 bar-2.1.1 qux-1.1.0].sort)
end
it "shows proper error when Gemfile changes forbid patch upgrades, and --patch --strict is given" do
@@ -674,25 +755,25 @@ RSpec.describe "bundle lock" do
it "defaults to major" do
bundle "lock --update --pre"
- expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-2.0.0.pre bar-4.0.0.pre qux-2.0.0].sort)
+ expect(the_bundle.locked_specs).to eq(%w[foo-2.0.0.pre bar-4.0.0.pre qux-2.0.0].sort)
end
it "patch preferred" do
bundle "lock --update --patch --pre"
- expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.4.5 bar-2.1.2.pre qux-1.0.1].sort)
+ expect(the_bundle.locked_specs).to eq(%w[foo-1.4.5 bar-2.1.2.pre qux-1.0.1].sort)
end
it "minor preferred" do
bundle "lock --update --minor --pre"
- expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.5.1 bar-3.1.0.pre qux-1.1.0].sort)
+ expect(the_bundle.locked_specs).to eq(%w[foo-1.5.1 bar-3.1.0.pre qux-1.1.0].sort)
end
it "major preferred" do
bundle "lock --update --major --pre"
- expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-2.0.0.pre bar-4.0.0.pre qux-2.0.0].sort)
+ expect(the_bundle.locked_specs).to eq(%w[foo-2.0.0.pre bar-4.0.0.pre qux-2.0.0].sort)
end
end
end
@@ -725,7 +806,7 @@ RSpec.describe "bundle lock" do
sequel
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
@@ -734,7 +815,7 @@ RSpec.describe "bundle lock" do
it "adds the latest version of the new dependency" do
bundle "lock --minor --update sequel"
- expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[sequel-5.72.0 bigdecimal-99.1.4].sort)
+ expect(the_bundle.locked_specs).to eq(%w[sequel-5.72.0 bigdecimal-99.1.4].sort)
end
end
@@ -751,22 +832,35 @@ RSpec.describe "bundle lock" do
lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, '\11.0.0\2')
bundle "lock --update --bundler --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
- expect(lockfile).to end_with("BUNDLED WITH\n 55\n")
+ expect(lockfile).to end_with("BUNDLED WITH\n 55\n")
- update_repo4 do
+ build_repo4 do
build_gem "bundler", "99"
end
bundle "lock --update --bundler --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
- expect(lockfile).to end_with("BUNDLED WITH\n 99\n")
+ expect(lockfile).to end_with("BUNDLED WITH\n 99\n")
end
- it "supports adding new platforms" do
- bundle "lock --add-platform java x86-mingw32"
+ it "supports adding new platforms when there's no previous lockfile" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ bundle "lock --add-platform java x86-mingw32 --verbose"
+ expect(out).to include("Resolving dependencies because there's no lockfile")
+
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+ expect(the_bundle.locked_platforms).to match_array(default_platform_list("java", "x86-mingw32"))
+ end
+
+ it "supports adding new platforms when a previous lockfile exists" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ bundle "lock"
+ bundle "lock --add-platform java x86-mingw32 --verbose"
+ expect(out).to include("Found changes from the lockfile, re-resolving dependencies because you are adding a new platform to your lockfile")
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- lockfile = Bundler::LockfileParser.new(read_lockfile)
- expect(lockfile.platforms).to match_array(default_platform_list(java, x86_mingw32))
+ expect(the_bundle.locked_platforms).to match_array(default_platform_list("java", "x86-mingw32"))
end
it "supports adding new platforms, when most specific locked platform is not the current platform, and current resolve is not compatible with the target platform" do
@@ -800,7 +894,7 @@ RSpec.describe "bundle lock" do
foo
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "lock --add-platform java"
@@ -820,12 +914,14 @@ RSpec.describe "bundle lock" do
foo
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
it "supports adding new platforms with force_ruby_platform = true" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
lockfile <<-L
GEM
remote: https://gem.repo1/
@@ -841,39 +937,41 @@ RSpec.describe "bundle lock" do
platform_specific
L
- bundle "config set force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
bundle "lock --add-platform java x86-mingw32"
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- lockfile = Bundler::LockfileParser.new(read_lockfile)
- expect(lockfile.platforms).to contain_exactly(rb, linux, java, x86_mingw32)
+ expect(the_bundle.locked_platforms).to contain_exactly(Gem::Platform::RUBY, "x86_64-linux", "java", "x86-mingw32")
end
it "supports adding the `ruby` platform" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
bundle "lock --add-platform ruby"
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- lockfile = Bundler::LockfileParser.new(read_lockfile)
- expect(lockfile.platforms).to match_array(default_platform_list("ruby"))
+ expect(the_bundle.locked_platforms).to match_array(default_platform_list("ruby"))
end
it "fails when adding an unknown platform" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
bundle "lock --add-platform foobarbaz", raise_on_error: false
expect(err).to include("The platform `foobarbaz` is unknown to RubyGems and can't be added to the lockfile")
expect(last_command).to be_failure
end
it "allows removing platforms" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
bundle "lock --add-platform java x86-mingw32"
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- lockfile = Bundler::LockfileParser.new(read_lockfile)
- expect(lockfile.platforms).to match_array(default_platform_list(java, x86_mingw32))
+ expect(the_bundle.locked_platforms).to match_array(default_platform_list("java", "x86-mingw32"))
bundle "lock --remove-platform java"
- lockfile = Bundler::LockfileParser.new(read_lockfile)
- expect(lockfile.platforms).to match_array(default_platform_list(x86_mingw32))
+ expect(the_bundle.locked_platforms).to match_array(default_platform_list("x86-mingw32"))
end
it "also cleans up redundant platform gems when removing platforms" do
@@ -912,7 +1010,7 @@ RSpec.describe "bundle lock" do
nokogiri
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
checksums.delete("nokogiri", Gem::Platform::RUBY)
@@ -934,11 +1032,13 @@ RSpec.describe "bundle lock" do
nokogiri
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
it "errors when removing all platforms" do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
bundle "lock --remove-platform #{local_platform}", raise_on_error: false
expect(err).to include("Removing all platforms from the bundle is not allowed")
end
@@ -948,7 +1048,7 @@ RSpec.describe "bundle lock" do
build_repo4 do
build_gem "ffi", "1.9.14"
build_gem "ffi", "1.9.14" do |s|
- s.platform = x86_mingw32
+ s.platform = "x86-mingw32"
end
build_gem "gssapi", "0.1"
@@ -980,7 +1080,7 @@ RSpec.describe "bundle lock" do
gem "gssapi"
G
- simulate_platform(x86_mingw32) { bundle :lock }
+ simulate_platform("x86-mingw32") { bundle :lock }
checksums = checksums_section_when_enabled do |c|
c.checksum gem_repo4, "ffi", "1.9.14", "x86-mingw32"
@@ -1009,10 +1109,10 @@ RSpec.describe "bundle lock" do
mixlib-shellout
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
- bundle "config set --local force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
bundle :lock
checksums.checksum gem_repo4, "ffi", "1.9.14"
@@ -1041,7 +1141,7 @@ RSpec.describe "bundle lock" do
mixlib-shellout
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -1078,7 +1178,7 @@ RSpec.describe "bundle lock" do
libv8
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
simulate_platform("x86_64-darwin-19") { bundle "lock --update" }
@@ -1125,7 +1225,7 @@ RSpec.describe "bundle lock" do
libv8
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -1165,7 +1265,7 @@ RSpec.describe "bundle lock" do
libv8
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
previous_lockfile = lockfile
@@ -1194,11 +1294,6 @@ RSpec.describe "bundle lock" do
end
build_gem "raygun-apm", "1.0.78" do |s|
- s.platform = "x64-mingw32"
- s.required_ruby_version = "< #{next_ruby_minor}.dev"
- end
-
- build_gem "raygun-apm", "1.0.78" do |s|
s.platform = "x64-mingw-ucrt"
s.required_ruby_version = "< #{next_ruby_minor}.dev"
end
@@ -1223,7 +1318,7 @@ RSpec.describe "bundle lock" do
raygun-apm
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "lock --add-platform x86_64-linux"
@@ -1257,7 +1352,7 @@ RSpec.describe "bundle lock" do
nokogiri
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
simulate_platform "x86_64-linux" do
@@ -1279,64 +1374,50 @@ RSpec.describe "bundle lock" do
nokogiri
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
- it "does not crash on conflicting ruby requirements between platform versions in two different gems" do
+ it "refuses to add platforms incompatible with the lockfile" do
build_repo4 do
- build_gem "unf_ext", "0.0.8.2"
-
- build_gem "unf_ext", "0.0.8.2" do |s|
- s.required_ruby_version = [">= 2.4", "< #{previous_ruby_minor}"]
- s.platform = "x64-mingw32"
- end
-
- build_gem "unf_ext", "0.0.8.2" do |s|
- s.required_ruby_version = [">= #{previous_ruby_minor}", "< #{current_ruby_minor}"]
- s.platform = "x64-mingw-ucrt"
- end
-
- build_gem "google-protobuf", "3.21.12"
-
- build_gem "google-protobuf", "3.21.12" do |s|
- s.required_ruby_version = [">= 2.5", "< #{previous_ruby_minor}"]
- s.platform = "x64-mingw32"
- end
-
- build_gem "google-protobuf", "3.21.12" do |s|
- s.required_ruby_version = [">= #{previous_ruby_minor}", "< #{current_ruby_minor}"]
- s.platform = "x64-mingw-ucrt"
+ build_gem "sorbet-static", "0.5.11989" do |s|
+ s.platform = "x86_64-linux"
end
end
gemfile <<~G
source "https://gem.repo4"
- gem "google-protobuf"
- gem "unf_ext"
+ gem "sorbet-static"
G
lockfile <<~L
GEM
remote: https://gem.repo4/
specs:
- google-protobuf (3.21.12)
- unf_ext (0.0.8.2)
+ sorbet-static (0.5.11989-x86_64-linux)
PLATFORMS
- x64-mingw-ucrt
- x64-mingw32
+ x86_64-linux
DEPENDENCIES
- google-protobuf
- unf_ext
+ sorbet-static
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
- bundle "install --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s, "DEBUG_RESOLVER" => "1" }
+ simulate_platform "x86_64-linux" do
+ bundle "lock --add-platform ruby", raise_on_error: false
+ end
+
+ nice_error = <<~E.strip
+ Could not find gems matching 'sorbet-static' valid for all resolution platforms (x86_64-linux, ruby) in rubygems repository https://gem.repo4/ or installed locally.
+
+ The source contains the following gems matching 'sorbet-static':
+ * sorbet-static-0.5.11989-x86_64-linux
+ E
+ expect(err).to include(nice_error)
end
it "respects lower bound ruby requirements" do
@@ -1365,7 +1446,7 @@ RSpec.describe "bundle lock" do
our_private_gem
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "install", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
@@ -1373,7 +1454,9 @@ RSpec.describe "bundle lock" do
context "when an update is available" do
before do
- update_repo4 do
+ gemfile_with_rails_weakling_and_foo_from_repo4
+
+ build_repo4 do
build_gem "foo", "2.0"
end
@@ -1427,7 +1510,7 @@ RSpec.describe "bundle lock" do
weakling
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
expect(read_lockfile).to eq(expected_lockfile)
@@ -1481,7 +1564,7 @@ RSpec.describe "bundle lock" do
weakling
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
expect(read_lockfile).to eq(expected_lockfile)
@@ -1535,7 +1618,7 @@ RSpec.describe "bundle lock" do
debug
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
simulate_platform "arm64-darwin-22" do
@@ -1558,7 +1641,65 @@ RSpec.describe "bundle lock" do
debug
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "when a system gem has incorrect dependencies, different from remote gems" do
+ before do
+ build_repo4 do
+ build_gem "foo", "1.0.0" do |s|
+ s.add_dependency "bar"
+ end
+
+ build_gem "bar", "1.0.0"
+ end
+
+ system_gems "foo-1.0.0", gem_repo: gem_repo4, path: default_bundle_path
+
+ # simulate gemspec with wrong empty dependencies
+ foo_gemspec_path = default_bundle_path("specifications/foo-1.0.0.gemspec")
+ foo_gemspec = Gem::Specification.load(foo_gemspec_path.to_s)
+ foo_gemspec.dependencies.clear
+ File.write(foo_gemspec_path, foo_gemspec.to_ruby)
+ end
+
+ it "generates a lockfile using remote dependencies, and prints a warning" do
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "foo"
+ G
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "foo", "1.0.0"
+ c.checksum gem_repo4, "bar", "1.0.0"
+ end
+
+ simulate_platform "x86_64-linux" do
+ bundle "lock --verbose"
+ end
+
+ expect(err).to eq("Local specification for foo-1.0.0 has different dependencies than the remote gem, ignoring it")
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ bar (1.0.0)
+ foo (1.0.0)
+ bar
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ foo
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
L
end
end
@@ -1661,7 +1802,7 @@ RSpec.describe "bundle lock" do
ransack (= 3.1.0)
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
expected_error = <<~ERR.strip
@@ -1803,7 +1944,7 @@ RSpec.describe "bundle lock" do
nogokiri
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
simulate_platform "x86_64-linux" do
@@ -1830,7 +1971,7 @@ RSpec.describe "bundle lock" do
nokogiri
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -1863,7 +2004,7 @@ RSpec.describe "bundle lock" do
nokogiri
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
simulate_platform "x86_64-linux" do
@@ -1890,7 +2031,57 @@ RSpec.describe "bundle lock" do
nokogiri
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "adds checksums when source is not specified" do
+ system_gems(%w[myrack-1.0.0], path: default_bundle_path)
+
+ gemfile <<-G
+ gem "myrack"
+ G
+
+ lockfile <<~L
+ GEM
+ specs:
+ myrack (1.0.0)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ myrack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "x86_64-linux" do
+ bundle "lock --add-checksums"
+ end
+
+ # myrack is coming from gem_repo1
+ # but it's simulated to install in the system gems path
+ checksums = checksums_section do |c|
+ c.checksum gem_repo1, "myrack", "1.0.0"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ specs:
+ myrack (1.0.0)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ myrack
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
L
end
@@ -1923,7 +2114,7 @@ RSpec.describe "bundle lock" do
nokogiri
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
simulate_platform "x86_64-linux" do
@@ -1952,11 +2143,11 @@ RSpec.describe "bundle lock" do
nokogiri
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
- it "generates checksums by default if configured to do so" do
+ it "generates checksums by default" do
build_repo4 do
build_gem "nokogiri", "1.14.2"
build_gem "nokogiri", "1.14.2" do |s|
@@ -1964,8 +2155,6 @@ RSpec.describe "bundle lock" do
end
end
- bundle "config lockfile_checksums true"
-
simulate_platform "x86_64-linux" do
install_gemfile <<-G
source "https://gem.repo4"
@@ -1994,7 +2183,136 @@ RSpec.describe "bundle lock" do
nokogiri
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "disables checksums if configured to do so" do
+ build_repo4 do
+ build_gem "nokogiri", "1.14.2"
+ build_gem "nokogiri", "1.14.2" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ bundle_config "lockfile_checksums false"
+
+ simulate_platform "x86_64-linux" do
+ install_gemfile <<-G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+ G
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.14.2)
+ nokogiri (1.14.2-x86_64-linux)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "add checksums for gems installed on disk" do
+ build_repo4 do
+ build_gem "warning", "18.0.0"
+ end
+
+ bundle_config "lockfile_checksums false"
+
+ simulate_platform "x86_64-linux" do
+ install_gemfile(<<-G, artifice: "endpoint")
+ source "https://gem.repo4"
+
+ gem "warning"
+ G
+
+ bundle "config --delete lockfile_checksums"
+ bundle("lock --add-checksums", artifice: "endpoint")
+ end
+
+ checksums = checksums_section do |c|
+ c.checksum gem_repo4, "warning", "18.0.0"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ warning (18.0.0)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ warning
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "doesn't add checksum for gems not installed on disk" do
+ lockfile(<<~L)
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ warning (18.0.0)
+
+ PLATFORMS
+ #{local_platform}
+
+ DEPENDENCIES
+ warning
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ gemfile(<<~G)
+ source "https://gem.repo4"
+
+ gem "warning"
+ G
+
+ build_repo4 do
+ build_gem "warning", "18.0.0"
+ end
+
+ FileUtils.rm_rf("#{gem_repo4}/gems")
+
+ bundle("lock --add-checksums", artifice: "endpoint")
+
+ checksums = checksums_section_when_enabled do |c|
+ c.no_checksum "warning", "18.0.0"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ warning (18.0.0)
+
+ PLATFORMS
+ #{local_platform}
+
+ DEPENDENCIES
+ warning
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
L
end
@@ -2075,7 +2393,7 @@ RSpec.describe "bundle lock" do
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -2105,7 +2423,7 @@ RSpec.describe "bundle lock" do
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -2134,14 +2452,13 @@ RSpec.describe "bundle lock" do
nokogiri (1.14.2-x86_64-linux)
PLATFORMS
- ruby
x86_64-linux
DEPENDENCIES
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -2196,7 +2513,7 @@ RSpec.describe "bundle lock" do
govuk_app_config
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -2232,7 +2549,7 @@ RSpec.describe "bundle lock" do
govuk_app_config
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -2268,7 +2585,7 @@ RSpec.describe "bundle lock" do
ffi
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -2292,8 +2609,269 @@ RSpec.describe "bundle lock" do
ffi
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ describe "--normalize-platforms on linux" do
+ let(:normalized_lockfile) do
+ <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ irb (1.0.0)
+ irb (1.0.0-x86_64-linux)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ irb
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ before do
+ build_repo4 do
+ build_gem "irb", "1.0.0"
+
+ build_gem "irb", "1.0.0" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "irb"
+ G
+ end
+
+ context "when already normalized" do
+ before do
+ lockfile normalized_lockfile
+ end
+
+ it "is a noop" do
+ simulate_platform "x86_64-linux" do
+ bundle "lock --normalize-platforms"
+ end
+
+ expect(lockfile).to eq(normalized_lockfile)
+ end
+ end
+
+ context "when not already normalized" do
+ before do
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ irb (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ irb
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "normalizes the list of platforms and native gems in the lockfile" do
+ simulate_platform "x86_64-linux" do
+ bundle "lock --normalize-platforms"
+ end
+
+ expect(lockfile).to eq(normalized_lockfile)
+ end
+ end
+ end
+
+ describe "--normalize-platforms on darwin" do
+ let(:normalized_lockfile) do
+ <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ irb (1.0.0)
+ irb (1.0.0-arm64-darwin)
+
+ PLATFORMS
+ arm64-darwin
+ ruby
+
+ DEPENDENCIES
+ irb
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ before do
+ build_repo4 do
+ build_gem "irb", "1.0.0"
+
+ build_gem "irb", "1.0.0" do |s|
+ s.platform = "arm64-darwin"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "irb"
+ G
+ end
+
+ context "when already normalized" do
+ before do
+ lockfile normalized_lockfile
+ end
+
+ it "is a noop" do
+ simulate_platform "arm64-darwin-23" do
+ bundle "lock --normalize-platforms"
+ end
+
+ expect(lockfile).to eq(normalized_lockfile)
+ end
+ end
+
+ context "when having only ruby" do
+ before do
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ irb (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ irb
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "normalizes the list of platforms and native gems in the lockfile" do
+ simulate_platform "arm64-darwin-23" do
+ bundle "lock --normalize-platforms"
+ end
+
+ expect(lockfile).to eq(normalized_lockfile)
+ end
+ end
+
+ context "when having only the current platform with version" do
+ before do
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ irb (1.0.0-arm64-darwin)
+
+ PLATFORMS
+ arm64-darwin-23
+
+ DEPENDENCIES
+ irb
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "normalizes the list of platforms by removing version" do
+ simulate_platform "arm64-darwin-23" do
+ bundle "lock --normalize-platforms"
+ end
+
+ expect(lockfile).to eq(normalized_lockfile)
+ end
+ end
+
+ context "when having other platforms with version" do
+ before do
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ irb (1.0.0-arm64-darwin)
+
+ PLATFORMS
+ arm64-darwin-22
+
+ DEPENDENCIES
+ irb
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "normalizes the list of platforms by removing version" do
+ simulate_platform "arm64-darwin-23" do
+ bundle "lock --normalize-platforms"
+ end
+
+ expect(lockfile).to eq(normalized_lockfile)
+ end
+ end
+ end
+
+ describe "--normalize-platforms with gems without generic variant" do
+ let(:original_lockfile) do
+ <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ sorbet-static (1.0-x86_64-linux)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ sorbet-static
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
L
end
+
+ before do
+ build_repo4 do
+ build_gem "sorbet-static" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "sorbet-static"
+ G
+
+ lockfile original_lockfile
+ end
+
+ it "removes invalid platforms" do
+ simulate_platform "x86_64-linux" do
+ bundle "lock --normalize-platforms"
+ end
+
+ expect(lockfile).to eq(original_lockfile.gsub(/^ ruby\n/m, ""))
+ end
end
end
diff --git a/spec/bundler/commands/newgem_spec.rb b/spec/bundler/commands/newgem_spec.rb
index 2389eda521..65fbad05aa 100644
--- a/spec/bundler/commands/newgem_spec.rb
+++ b/spec/bundler/commands/newgem_spec.rb
@@ -6,39 +6,54 @@ RSpec.describe "bundle gem" do
expect(bundled_app("#{gem_name}/README.md")).to exist
expect(bundled_app("#{gem_name}/Gemfile")).to exist
expect(bundled_app("#{gem_name}/Rakefile")).to exist
- expect(bundled_app("#{gem_name}/lib/#{require_path}.rb")).to exist
- expect(bundled_app("#{gem_name}/lib/#{require_path}/version.rb")).to exist
+ expect(bundled_app("#{gem_name}/lib/#{gem_name}.rb")).to exist
+ expect(bundled_app("#{gem_name}/lib/#{gem_name}/version.rb")).to exist
+
+ expect(ignore_paths).to include("bin/")
+ expect(ignore_paths).to include("Gemfile")
end
def bundle_exec_rubocop
prepare_gemspec(bundled_app(gem_name, "#{gem_name}.gemspec"))
- bundle "config set path #{rubocop_gems}", dir: bundled_app(gem_name)
+ bundle "config set path #{rubocop_gem_path}", dir: bundled_app(gem_name)
bundle "exec rubocop --debug --config .rubocop.yml", dir: bundled_app(gem_name)
end
def bundle_exec_standardrb
prepare_gemspec(bundled_app(gem_name, "#{gem_name}.gemspec"))
- bundle "config set path #{standard_gems}", dir: bundled_app(gem_name)
+ bundle "config set path #{standard_gem_path}", dir: bundled_app(gem_name)
bundle "exec standardrb --debug", dir: bundled_app(gem_name)
end
- let(:generated_gemspec) { Bundler.load_gemspec_uncached(bundled_app(gem_name).join("#{gem_name}.gemspec")) }
-
- let(:gem_name) { "mygem" }
+ def ignore_paths
+ generated = bundled_app("#{gem_name}/#{gem_name}.gemspec").read
+ matched = generated.match(/^\s+f\.start_with\?\(\*%w\[(?<ignored>.*)\]\)$/)
+ matched[:ignored]&.split(" ")
+ end
- let(:require_path) { "mygem" }
+ def installed_go?
+ sys_exec("go version", raise_on_error: true)
+ true
+ rescue StandardError
+ false
+ end
- let(:minitest_test_file_path) { "test/test_mygem.rb" }
+ let(:generated_gemspec) { Bundler.load_gemspec_uncached(bundled_app(gem_name).join("#{gem_name}.gemspec")) }
- let(:minitest_test_class_name) { "class TestMygem < Minitest::Test" }
+ let(:gem_name) { "mygem" }
before do
git("config --global user.name 'Bundler User'")
git("config --global user.email user@example.com")
git("config --global github.user bundleuser")
- global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "false", "BUNDLE_GEM__LINTER" => "false",
- "BUNDLE_GEM__CI" => "false", "BUNDLE_GEM__CHANGELOG" => "false"
+ bundle_config_global "gem.mit false"
+ bundle_config_global "gem.test false"
+ bundle_config_global "gem.coc false"
+ bundle_config_global "gem.linter false"
+ bundle_config_global "gem.ci false"
+ bundle_config_global "gem.changelog false"
+ bundle_config_global "gem.bundle false"
end
describe "git repo initialization" do
@@ -152,64 +167,23 @@ RSpec.describe "bundle gem" do
end
end
- shared_examples_for "--rubocop flag" do
- context "is deprecated", bundler: "< 3" do
- before do
- global_config "BUNDLE_GEM__LINTER" => nil
- bundle "gem #{gem_name} --rubocop"
- end
-
- it "generates a gem skeleton with rubocop" do
- gem_skeleton_assertions
- expect(bundled_app("test-gem/Rakefile")).to read_as(
- include("# frozen_string_literal: true").
- and(include('require "rubocop/rake_task"').
- and(include("RuboCop::RakeTask.new").
- and(match(/default:.+:rubocop/))))
- )
- end
-
- it "includes rubocop in generated Gemfile" do
- allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- builder = Bundler::Dsl.new
- builder.eval_gemfile(bundled_app("#{gem_name}/Gemfile"))
- builder.dependencies
- rubocop_dep = builder.dependencies.find {|d| d.name == "rubocop" }
- expect(rubocop_dep).not_to be_nil
- end
-
- it "generates a default .rubocop.yml" do
- expect(bundled_app("#{gem_name}/.rubocop.yml")).to exist
- end
+ shared_examples_for "--bundle flag" do
+ before do
+ bundle "gem #{gem_name} --bundle"
+ end
+ it "generates a gem skeleton with bundle install" do
+ gem_skeleton_assertions
+ expect(out).to include("Running bundle install in the new gem directory.")
end
end
- shared_examples_for "--no-rubocop flag" do
- context "is deprecated", bundler: "< 3" do
- define_negated_matcher :exclude, :include
-
- before do
- bundle "gem #{gem_name} --no-rubocop"
- end
-
- it "generates a gem skeleton without rubocop" do
- gem_skeleton_assertions
- expect(bundled_app("test-gem/Rakefile")).to read_as(exclude("rubocop"))
- expect(bundled_app("test-gem/#{gem_name}.gemspec")).to read_as(exclude("rubocop"))
- end
-
- it "does not include rubocop in generated Gemfile" do
- allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- builder = Bundler::Dsl.new
- builder.eval_gemfile(bundled_app("#{gem_name}/Gemfile"))
- builder.dependencies
- rubocop_dep = builder.dependencies.find {|d| d.name == "rubocop" }
- expect(rubocop_dep).to be_nil
- end
-
- it "doesn't generate a default .rubocop.yml" do
- expect(bundled_app("#{gem_name}/.rubocop.yml")).to_not exist
- end
+ shared_examples_for "--no-bundle flag" do
+ before do
+ bundle "gem #{gem_name} --no-bundle"
+ end
+ it "generates a gem skeleton without bundle install" do
+ gem_skeleton_assertions
+ expect(out).to_not include("Running bundle install in the new gem directory.")
end
end
@@ -220,7 +194,7 @@ RSpec.describe "bundle gem" do
it "generates a gem skeleton with rubocop" do
gem_skeleton_assertions
- expect(bundled_app("test-gem/Rakefile")).to read_as(
+ expect(bundled_app("#{gem_name}/Rakefile")).to read_as(
include("# frozen_string_literal: true").
and(include('require "rubocop/rake_task"').
and(include("RuboCop::RakeTask.new").
@@ -234,12 +208,17 @@ RSpec.describe "bundle gem" do
builder.eval_gemfile(bundled_app("#{gem_name}/Gemfile"))
builder.dependencies
rubocop_dep = builder.dependencies.find {|d| d.name == "rubocop" }
- expect(rubocop_dep).not_to be_nil
+ expect(rubocop_dep).not_to be_specific
+ expect(rubocop_dep.requirement).to eq(Gem::Requirement.new([">= 0"]))
end
it "generates a default .rubocop.yml" do
expect(bundled_app("#{gem_name}/.rubocop.yml")).to exist
end
+
+ it "includes .rubocop.yml into ignore list" do
+ expect(ignore_paths).to include(".rubocop.yml")
+ end
end
shared_examples_for "--linter=standard flag" do
@@ -249,7 +228,7 @@ RSpec.describe "bundle gem" do
it "generates a gem skeleton with standard" do
gem_skeleton_assertions
- expect(bundled_app("test-gem/Rakefile")).to read_as(
+ expect(bundled_app("#{gem_name}/Rakefile")).to read_as(
include('require "standard/rake"').
and(match(/default:.+:standard/))
)
@@ -261,12 +240,17 @@ RSpec.describe "bundle gem" do
builder.eval_gemfile(bundled_app("#{gem_name}/Gemfile"))
builder.dependencies
standard_dep = builder.dependencies.find {|d| d.name == "standard" }
- expect(standard_dep).not_to be_nil
+ expect(standard_dep).not_to be_specific
+ expect(standard_dep.requirement).to eq(Gem::Requirement.new([">= 0"]))
end
it "generates a default .standard.yml" do
expect(bundled_app("#{gem_name}/.standard.yml")).to exist
end
+
+ it "includes .standard.yml into ignore list" do
+ expect(ignore_paths).to include(".standard.yml")
+ end
end
shared_examples_for "--no-linter flag" do
@@ -278,8 +262,8 @@ RSpec.describe "bundle gem" do
it "generates a gem skeleton without rubocop" do
gem_skeleton_assertions
- expect(bundled_app("test-gem/Rakefile")).to read_as(exclude("rubocop"))
- expect(bundled_app("test-gem/#{gem_name}.gemspec")).to read_as(exclude("rubocop"))
+ expect(bundled_app("#{gem_name}/Rakefile")).to read_as(exclude("rubocop"))
+ expect(bundled_app("#{gem_name}/#{gem_name}.gemspec")).to read_as(exclude("rubocop"))
end
it "does not include rubocop in generated Gemfile" do
@@ -304,9 +288,17 @@ RSpec.describe "bundle gem" do
expect(bundled_app("#{gem_name}/.rubocop.yml")).to_not exist
end
+ it "does not add .rubocop.yml into ignore list" do
+ expect(ignore_paths).not_to include(".rubocop.yml")
+ end
+
it "doesn't generate a default .standard.yml" do
expect(bundled_app("#{gem_name}/.standard.yml")).to_not exist
end
+
+ it "does not add .standard.yml into ignore list" do
+ expect(ignore_paths).not_to include(".standard.yml")
+ end
end
it "has no rubocop offenses when using --linter=rubocop flag" do
@@ -353,7 +345,6 @@ RSpec.describe "bundle gem" do
it "has no rubocop offenses when using --ext=rust and --linter=rubocop flag" do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
- skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version
bundle "gem #{gem_name} --ext=rust --linter=rubocop"
bundle_exec_rubocop
@@ -362,7 +353,6 @@ RSpec.describe "bundle gem" do
it "has no rubocop offenses when using --ext=rust, --test=minitest, and --linter=rubocop flag" do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
- skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version
bundle "gem #{gem_name} --ext=rust --test=minitest --linter=rubocop"
bundle_exec_rubocop
@@ -371,7 +361,6 @@ RSpec.describe "bundle gem" do
it "has no rubocop offenses when using --ext=rust, --test=rspec, and --linter=rubocop flag" do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
- skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version
bundle "gem #{gem_name} --ext=rust --test=rspec --linter=rubocop"
bundle_exec_rubocop
@@ -380,7 +369,6 @@ RSpec.describe "bundle gem" do
it "has no rubocop offenses when using --ext=rust, --test=test-unit, and --linter=rubocop flag" do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
- skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version
bundle "gem #{gem_name} --ext=rust --test=test-unit --linter=rubocop"
bundle_exec_rubocop
@@ -398,11 +386,17 @@ RSpec.describe "bundle gem" do
shared_examples_for "test framework is absent" do
it "does not create any test framework files" do
expect(bundled_app("#{gem_name}/.rspec")).to_not exist
- expect(bundled_app("#{gem_name}/spec/#{require_path}_spec.rb")).to_not exist
+ expect(bundled_app("#{gem_name}/spec/#{gem_name}_spec.rb")).to_not exist
expect(bundled_app("#{gem_name}/spec/spec_helper.rb")).to_not exist
- expect(bundled_app("#{gem_name}/test/#{require_path}.rb")).to_not exist
+ expect(bundled_app("#{gem_name}/test/#{gem_name}.rb")).to_not exist
expect(bundled_app("#{gem_name}/test/test_helper.rb")).to_not exist
end
+
+ it "does not add any test framework files into ignore list" do
+ expect(ignore_paths).not_to include("test/")
+ expect(ignore_paths).not_to include(".rspec")
+ expect(ignore_paths).not_to include("spec/")
+ end
end
context "README.md" do
@@ -454,10 +448,7 @@ RSpec.describe "bundle gem" do
context "when git is not available" do
# This spec cannot have `git` available in the test env
before do
- load_paths = [lib_dir, spec_dir]
- load_path_str = "-I#{load_paths.join(File::PATH_SEPARATOR)}"
-
- sys_exec "#{Gem.ruby} #{load_path_str} #{bindir.join("bundle")} gem #{gem_name}", env: { "PATH" => "" }
+ bundle "gem #{gem_name}", env: { "PATH" => "" }
end
it "creates the gem without the need for git" do
@@ -471,19 +462,25 @@ RSpec.describe "bundle gem" do
it "doesn't create a .gitignore file" do
expect(bundled_app("#{gem_name}/.gitignore")).to_not exist
end
+
+ it "does not add .gitignore into ignore list" do
+ expect(ignore_paths).not_to include(".gitignore")
+ end
end
- it "generates a valid gemspec", :ruby_repo do
+ it "generates a valid gemspec" do
bundle "gem newgem --bin"
prepare_gemspec(bundled_app("newgem", "newgem.gemspec"))
- gems = ["rake-#{rake_version}"]
- path = Bundler.feature_flag.default_install_uses_path? ? local_gem_path(base: bundled_app("newgem")) : system_gem_path
- system_gems gems, path: path
+ build_repo2 do
+ build_dummy_irb "9.9.9"
+ end
+ gems = ["rake-#{rake_version}", "irb-9.9.9"]
+ system_gems gems, path: system_gem_path, gem_repo: gem_repo2
bundle "exec rake build", dir: bundled_app("newgem")
- expect(last_command.stdboth).not_to include("ERROR")
+ expect(stdboth).not_to include("ERROR")
end
context "gem naming with relative paths" do
@@ -534,7 +531,7 @@ RSpec.describe "bundle gem" do
shared_examples_for "github_username configuration" do
context "with github_username setting set to some value" do
before do
- global_config "BUNDLE_GEM__GITHUB_USERNAME" => "different_username"
+ bundle_config_global "gem.github_username different_username"
bundle "gem #{gem_name}"
end
@@ -550,7 +547,7 @@ RSpec.describe "bundle gem" do
context "with github_username setting set to false" do
before do
- global_config "BUNDLE_GEM__GITHUB_USERNAME" => "false"
+ bundle_config_global "gem.github_username false"
bundle "gem #{gem_name}"
end
@@ -565,914 +562,1108 @@ RSpec.describe "bundle gem" do
end
end
- shared_examples_for "generating a gem" do
- it "generates a gem skeleton" do
- bundle "gem #{gem_name}"
+ it "generates a gem skeleton" do
+ bundle "gem #{gem_name}"
- expect(bundled_app("#{gem_name}/#{gem_name}.gemspec")).to exist
- expect(bundled_app("#{gem_name}/Gemfile")).to exist
- expect(bundled_app("#{gem_name}/Rakefile")).to exist
- expect(bundled_app("#{gem_name}/lib/#{require_path}.rb")).to exist
- expect(bundled_app("#{gem_name}/lib/#{require_path}/version.rb")).to exist
- expect(bundled_app("#{gem_name}/sig/#{require_path}.rbs")).to exist
- expect(bundled_app("#{gem_name}/.gitignore")).to exist
+ expect(bundled_app("#{gem_name}/#{gem_name}.gemspec")).to exist
+ expect(bundled_app("#{gem_name}/Gemfile")).to exist
+ expect(bundled_app("#{gem_name}/Rakefile")).to exist
+ expect(bundled_app("#{gem_name}/lib/#{gem_name}.rb")).to exist
+ expect(bundled_app("#{gem_name}/lib/#{gem_name}/version.rb")).to exist
+ expect(bundled_app("#{gem_name}/sig/#{gem_name}.rbs")).to exist
+ expect(bundled_app("#{gem_name}/.gitignore")).to exist
- expect(bundled_app("#{gem_name}/bin/setup")).to exist
- expect(bundled_app("#{gem_name}/bin/console")).to exist
+ expect(bundled_app("#{gem_name}/bin/setup")).to exist
+ expect(bundled_app("#{gem_name}/bin/console")).to exist
- unless Gem.win_platform?
- expect(bundled_app("#{gem_name}/bin/setup")).to be_executable
- expect(bundled_app("#{gem_name}/bin/console")).to be_executable
- end
-
- expect(bundled_app("#{gem_name}/bin/setup").read).to start_with("#!")
- expect(bundled_app("#{gem_name}/bin/console").read).to start_with("#!")
+ unless Gem.win_platform?
+ expect(bundled_app("#{gem_name}/bin/setup")).to be_executable
+ expect(bundled_app("#{gem_name}/bin/console")).to be_executable
end
- it "starts with version 0.1.0" do
+ expect(bundled_app("#{gem_name}/bin/setup").read).to start_with("#!")
+ expect(bundled_app("#{gem_name}/bin/console").read).to start_with("#!")
+ end
+
+ it "includes bin/ into ignore list" do
+ bundle "gem #{gem_name}"
+
+ expect(ignore_paths).to include("bin/")
+ end
+
+ it "includes Gemfile into ignore list" do
+ bundle "gem #{gem_name}"
+
+ expect(ignore_paths).to include("Gemfile")
+ end
+
+ it "includes .gitignore into ignore list" do
+ bundle "gem #{gem_name}"
+
+ expect(ignore_paths).to include(".gitignore")
+ end
+
+ it "starts with version 0.1.0" do
+ bundle "gem #{gem_name}"
+
+ expect(bundled_app("#{gem_name}/lib/#{gem_name}/version.rb").read).to match(/VERSION = "0.1.0"/)
+ end
+
+ it "declare String type for VERSION constant" do
+ bundle "gem #{gem_name}"
+
+ expect(bundled_app("#{gem_name}/sig/#{gem_name}.rbs").read).to match(/VERSION: String/)
+ end
+
+ context "git config user.{name,email} is set" do
+ before do
bundle "gem #{gem_name}"
+ end
+
+ it "sets gemspec author to git user.name if available" do
+ expect(generated_gemspec.authors.first).to eq("Bundler User")
+ end
- expect(bundled_app("#{gem_name}/lib/#{require_path}/version.rb").read).to match(/VERSION = "0.1.0"/)
+ it "sets gemspec email to git user.email if available" do
+ expect(generated_gemspec.email.first).to eq("user@example.com")
end
+ end
- it "declare String type for VERSION constant" do
+ context "git config user.{name,email} is not set" do
+ before do
+ git("config --global --unset user.name")
+ git("config --global --unset user.email")
bundle "gem #{gem_name}"
+ end
- expect(bundled_app("#{gem_name}/sig/#{require_path}.rbs").read).to match(/VERSION: String/)
+ it "sets gemspec author to default message if git user.name is not set or empty" do
+ expect(generated_gemspec.authors.first).to eq("TODO: Write your name")
end
- context "git config user.{name,email} is set" do
- before do
- bundle "gem #{gem_name}"
- end
+ it "sets gemspec email to default message if git user.email is not set or empty" do
+ expect(generated_gemspec.email.first).to eq("TODO: Write your email address")
+ end
+ end
- it "sets gemspec author to git user.name if available" do
- expect(generated_gemspec.authors.first).to eq("Bundler User")
- end
+ it "sets gemspec metadata['allowed_push_host']" do
+ bundle "gem #{gem_name}"
+
+ expect(generated_gemspec.metadata["allowed_push_host"]).
+ to match(/example\.com/)
+ end
+
+ it "includes a commented-out rubygems_mfa_required metadata hint" do
+ bundle "gem #{gem_name}"
+
+ gemspec_contents = bundled_app("#{gem_name}/#{gem_name}.gemspec").read
+
+ expect(gemspec_contents).to include('# spec.metadata["rubygems_mfa_required"] = "true"')
+ expect(gemspec_contents).to include("https://guides.rubygems.org/mfa-requirement-opt-in/")
+ end
+
+ it "sets a minimum ruby version" do
+ bundle "gem #{gem_name}"
+
+ expect(generated_gemspec.required_ruby_version.to_s).to start_with(">=")
+ end
+
+ it "does not include the gemspec file in files" do
+ bundle "gem #{gem_name}"
+
+ bundler_gemspec = Bundler::GemHelper.new(bundled_app(gem_name), gem_name).gemspec
+
+ expect(bundler_gemspec.files).not_to include("#{gem_name}.gemspec")
+ end
+
+ it "does not include the Gemfile file in files" do
+ bundle "gem #{gem_name}"
+
+ bundler_gemspec = Bundler::GemHelper.new(bundled_app(gem_name), gem_name).gemspec
+
+ expect(bundler_gemspec.files).not_to include("Gemfile")
+ end
- it "sets gemspec email to git user.email if available" do
- expect(generated_gemspec.email.first).to eq("user@example.com")
+ it "runs rake without problems" do
+ bundle "gem #{gem_name}"
+
+ system_gems ["rake-#{rake_version}"]
+
+ rakefile = <<~RAKEFILE
+ task :default do
+ puts 'SUCCESS'
end
+ RAKEFILE
+ File.open(bundled_app("#{gem_name}/Rakefile"), "w") do |file|
+ file.puts rakefile
end
- context "git config user.{name,email} is not set" do
- before do
- git("config --global --unset user.name")
- git("config --global --unset user.email")
- bundle "gem #{gem_name}"
- end
+ sys_exec("rake", dir: bundled_app(gem_name))
+ expect(out).to include("SUCCESS")
+ end
- it "sets gemspec author to default message if git user.name is not set or empty" do
- expect(generated_gemspec.authors.first).to eq("TODO: Write your name")
- end
+ context "--exe parameter set" do
+ before do
+ bundle "gem #{gem_name} --exe"
+ end
- it "sets gemspec email to default message if git user.email is not set or empty" do
- expect(generated_gemspec.email.first).to eq("TODO: Write your email address")
+ it "builds exe skeleton" do
+ expect(bundled_app("#{gem_name}/exe/#{gem_name}")).to exist
+ unless Gem.win_platform?
+ expect(bundled_app("#{gem_name}/exe/#{gem_name}")).to be_executable
end
end
+ end
- it "sets gemspec metadata['allowed_push_host']" do
- bundle "gem #{gem_name}"
+ context "--bin parameter set" do
+ before do
+ bundle "gem #{gem_name} --bin"
+ end
- expect(generated_gemspec.metadata["allowed_push_host"]).
- to match(/example\.com/)
+ it "builds exe skeleton" do
+ expect(bundled_app("#{gem_name}/exe/#{gem_name}")).to exist
end
+ end
- it "sets a minimum ruby version" do
+ context "no --test parameter" do
+ before do
bundle "gem #{gem_name}"
-
- expect(generated_gemspec.required_ruby_version.to_s).to start_with(">=")
end
- it "requires the version file" do
- bundle "gem #{gem_name}"
+ it_behaves_like "test framework is absent"
+ end
- expect(bundled_app("#{gem_name}/lib/#{require_path}.rb").read).to match(%r{require_relative "#{require_relative_path}/version"})
+ context "--test parameter set to rspec" do
+ before do
+ bundle "gem #{gem_name} --test=rspec"
end
- it "creates a base error class" do
- bundle "gem #{gem_name}"
+ it "builds spec skeleton" do
+ expect(bundled_app("#{gem_name}/.rspec")).to exist
+ expect(bundled_app("#{gem_name}/spec/#{gem_name}_spec.rb")).to exist
+ expect(bundled_app("#{gem_name}/spec/spec_helper.rb")).to exist
+ end
- expect(bundled_app("#{gem_name}/lib/#{require_path}.rb").read).to match(/class Error < StandardError; end$/)
+ it "includes .rspec and spec/ into ignore list" do
+ expect(ignore_paths).to include(".rspec")
+ expect(ignore_paths).to include("spec/")
+ end
+
+ it "depends on a non-specific version of rspec in generated Gemfile" do
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+ builder = Bundler::Dsl.new
+ builder.eval_gemfile(bundled_app("#{gem_name}/Gemfile"))
+ builder.dependencies
+ rspec_dep = builder.dependencies.find {|d| d.name == "rspec" }
+ expect(rspec_dep).not_to be_specific
+ expect(rspec_dep.requirement).to eq(Gem::Requirement.new([">= 0"]))
end
+ end
- it "does not include the gemspec file in files" do
+ context "init_gems_rb setting to true" do
+ before do
+ bundle_config "init_gems_rb true"
bundle "gem #{gem_name}"
+ end
- bundler_gemspec = Bundler::GemHelper.new(bundled_app(gem_name), gem_name).gemspec
+ it "generates gems.rb instead of Gemfile" do
+ expect(bundled_app("#{gem_name}/gems.rb")).to exist
+ expect(bundled_app("#{gem_name}/Gemfile")).to_not exist
+ end
- expect(bundler_gemspec.files).not_to include("#{gem_name}.gemspec")
+ it "includes gems.rb and gems.locked into ignore list" do
+ expect(ignore_paths).to include("gems.rb")
+ expect(ignore_paths).to include("gems.locked")
+ expect(ignore_paths).not_to include("Gemfile")
end
+ end
- it "does not include the Gemfile file in files" do
+ context "init_gems_rb setting to false" do
+ before do
+ bundle_config "init_gems_rb false"
bundle "gem #{gem_name}"
+ end
- bundler_gemspec = Bundler::GemHelper.new(bundled_app(gem_name), gem_name).gemspec
+ it "generates Gemfile instead of gems.rb" do
+ expect(bundled_app("#{gem_name}/gems.rb")).to_not exist
+ expect(bundled_app("#{gem_name}/Gemfile")).to exist
+ end
- expect(bundler_gemspec.files).not_to include("Gemfile")
+ it "includes Gemfile into ignore list" do
+ expect(ignore_paths).to include("Gemfile")
+ expect(ignore_paths).not_to include("gems.rb")
+ expect(ignore_paths).not_to include("gems.locked")
end
+ end
- it "runs rake without problems" do
+ context "gem.test setting set to rspec" do
+ before do
+ bundle_config "gem.test rspec"
bundle "gem #{gem_name}"
+ end
- system_gems ["rake-#{rake_version}"]
-
- rakefile = <<~RAKEFILE
- task :default do
- puts 'SUCCESS'
- end
- RAKEFILE
- File.open(bundled_app("#{gem_name}/Rakefile"), "w") do |file|
- file.puts rakefile
- end
-
- sys_exec(rake, dir: bundled_app(gem_name))
- expect(out).to include("SUCCESS")
+ it "builds spec skeleton" do
+ expect(bundled_app("#{gem_name}/.rspec")).to exist
+ expect(bundled_app("#{gem_name}/spec/#{gem_name}_spec.rb")).to exist
+ expect(bundled_app("#{gem_name}/spec/spec_helper.rb")).to exist
end
- context "--exe parameter set" do
- before do
- bundle "gem #{gem_name} --exe"
- end
+ it "includes .rspec and spec/ into ignore list" do
+ expect(ignore_paths).to include(".rspec")
+ expect(ignore_paths).to include("spec/")
+ end
+ end
- it "builds exe skeleton" do
- expect(bundled_app("#{gem_name}/exe/#{gem_name}")).to exist
- unless Gem.win_platform?
- expect(bundled_app("#{gem_name}/exe/#{gem_name}")).to be_executable
- end
- end
+ context "gem.test setting set to rspec and --test is set to minitest" do
+ before do
+ bundle_config "gem.test rspec"
+ bundle "gem #{gem_name} --test=minitest"
+ end
- it "requires the main file" do
- expect(bundled_app("#{gem_name}/exe/#{gem_name}").read).to match(/require "#{require_path}"/)
- end
+ it "builds spec skeleton" do
+ expect(bundled_app("#{gem_name}/test/test_#{gem_name}.rb")).to exist
+ expect(bundled_app("#{gem_name}/test/test_helper.rb")).to exist
end
- context "--bin parameter set" do
- before do
- bundle "gem #{gem_name} --bin"
- end
+ it "includes test/ into ignore list" do
+ expect(ignore_paths).to include("test/")
+ end
+ end
- it "builds exe skeleton" do
- expect(bundled_app("#{gem_name}/exe/#{gem_name}")).to exist
- end
+ context "--test parameter set to minitest" do
+ before do
+ bundle "gem #{gem_name} --test=minitest"
+ end
- it "requires the main file" do
- expect(bundled_app("#{gem_name}/exe/#{gem_name}").read).to match(/require "#{require_path}"/)
- end
+ it "depends on a non-specific version of minitest" do
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+ builder = Bundler::Dsl.new
+ builder.eval_gemfile(bundled_app("#{gem_name}/Gemfile"))
+ builder.dependencies
+ minitest_dep = builder.dependencies.find {|d| d.name == "minitest" }
+ expect(minitest_dep).not_to be_specific
+ expect(minitest_dep.requirement).to eq(Gem::Requirement.new([">= 0"]))
end
- context "no --test parameter" do
- before do
- bundle "gem #{gem_name}"
- end
+ it "builds spec skeleton" do
+ expect(bundled_app("#{gem_name}/test/test_#{gem_name}.rb")).to exist
+ expect(bundled_app("#{gem_name}/test/test_helper.rb")).to exist
+ end
- it_behaves_like "test framework is absent"
+ it "includes test/ into ignore list" do
+ expect(ignore_paths).to include("test/")
end
- context "--test parameter set to rspec" do
- before do
- bundle "gem #{gem_name} --test=rspec"
- end
+ it "creates a default rake task to run the test suite" do
+ rakefile = <<~RAKEFILE
+ # frozen_string_literal: true
- it "builds spec skeleton" do
- expect(bundled_app("#{gem_name}/.rspec")).to exist
- expect(bundled_app("#{gem_name}/spec/#{require_path}_spec.rb")).to exist
- expect(bundled_app("#{gem_name}/spec/spec_helper.rb")).to exist
- end
+ require "bundler/gem_tasks"
+ require "minitest/test_task"
- it "depends on a specific version of rspec in generated Gemfile" do
- allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- builder = Bundler::Dsl.new
- builder.eval_gemfile(bundled_app("#{gem_name}/Gemfile"))
- builder.dependencies
- rspec_dep = builder.dependencies.find {|d| d.name == "rspec" }
- expect(rspec_dep).to be_specific
- end
+ Minitest::TestTask.create
- it "requires the main file" do
- expect(bundled_app("#{gem_name}/spec/spec_helper.rb").read).to include(%(require "#{require_path}"))
- end
+ task default: :test
+ RAKEFILE
- it "creates a default test which fails" do
- expect(bundled_app("#{gem_name}/spec/#{require_path}_spec.rb").read).to include("expect(false).to eq(true)")
- end
+ expect(bundled_app("#{gem_name}/Rakefile").read).to eq(rakefile)
end
+ end
- context "init_gems_rb setting to true" do
- before do
- bundle "config set init_gems_rb true"
- bundle "gem #{gem_name}"
- end
-
- it "generates gems.rb instead of Gemfile" do
- expect(bundled_app("#{gem_name}/gems.rb")).to exist
- expect(bundled_app("#{gem_name}/Gemfile")).to_not exist
- end
+ context "gem.test setting set to minitest" do
+ before do
+ bundle_config "gem.test minitest"
+ bundle "gem #{gem_name}"
end
- context "init_gems_rb setting to false" do
- before do
- bundle "config set init_gems_rb false"
- bundle "gem #{gem_name}"
- end
+ it "creates a default rake task to run the test suite" do
+ rakefile = <<~RAKEFILE
+ # frozen_string_literal: true
- it "generates Gemfile instead of gems.rb" do
- expect(bundled_app("#{gem_name}/gems.rb")).to_not exist
- expect(bundled_app("#{gem_name}/Gemfile")).to exist
- end
- end
+ require "bundler/gem_tasks"
+ require "minitest/test_task"
- context "gem.test setting set to rspec" do
- before do
- bundle "config set gem.test rspec"
- bundle "gem #{gem_name}"
- end
+ Minitest::TestTask.create
- it "builds spec skeleton" do
- expect(bundled_app("#{gem_name}/.rspec")).to exist
- expect(bundled_app("#{gem_name}/spec/#{require_path}_spec.rb")).to exist
- expect(bundled_app("#{gem_name}/spec/spec_helper.rb")).to exist
- end
+ task default: :test
+ RAKEFILE
+
+ expect(bundled_app("#{gem_name}/Rakefile").read).to eq(rakefile)
end
+ end
- context "gem.test setting set to rspec and --test is set to minitest" do
- before do
- bundle "config set gem.test rspec"
- bundle "gem #{gem_name} --test=minitest"
- end
+ context "--test parameter set to test-unit" do
+ before do
+ bundle "gem #{gem_name} --test=test-unit"
+ end
- it "builds spec skeleton" do
- expect(bundled_app("#{gem_name}/#{minitest_test_file_path}")).to exist
- expect(bundled_app("#{gem_name}/test/test_helper.rb")).to exist
- end
+ it "depends on a non-specific version of test-unit" do
+ allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
+ builder = Bundler::Dsl.new
+ builder.eval_gemfile(bundled_app("#{gem_name}/Gemfile"))
+ builder.dependencies
+ test_unit_dep = builder.dependencies.find {|d| d.name == "test-unit" }
+ expect(test_unit_dep).not_to be_specific
+ expect(test_unit_dep.requirement).to eq(Gem::Requirement.new([">= 0"]))
end
- context "--test parameter set to minitest" do
- before do
- bundle "gem #{gem_name} --test=minitest"
- end
+ it "builds spec skeleton" do
+ expect(bundled_app("#{gem_name}/test/#{gem_name}_test.rb")).to exist
+ expect(bundled_app("#{gem_name}/test/test_helper.rb")).to exist
+ end
- it "depends on a specific version of minitest" do
- allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- builder = Bundler::Dsl.new
- builder.eval_gemfile(bundled_app("#{gem_name}/Gemfile"))
- builder.dependencies
- minitest_dep = builder.dependencies.find {|d| d.name == "minitest" }
- expect(minitest_dep).to be_specific
- end
+ it "includes test/ into ignore list" do
+ expect(ignore_paths).to include("test/")
+ end
- it "builds spec skeleton" do
- expect(bundled_app("#{gem_name}/#{minitest_test_file_path}")).to exist
- expect(bundled_app("#{gem_name}/test/test_helper.rb")).to exist
- end
+ it "creates a default rake task to run the test suite" do
+ rakefile = <<~RAKEFILE
+ # frozen_string_literal: true
- it "requires the main file" do
- expect(bundled_app("#{gem_name}/test/test_helper.rb").read).to include(%(require "#{require_path}"))
- end
+ require "bundler/gem_tasks"
+ require "rake/testtask"
- it "requires 'test_helper'" do
- expect(bundled_app("#{gem_name}/#{minitest_test_file_path}").read).to include(%(require "test_helper"))
- end
+ Rake::TestTask.new(:test) do |t|
+ t.libs << "test"
+ t.libs << "lib"
+ t.test_files = FileList["test/**/*_test.rb"]
+ end
- it "defines valid test class name" do
- expect(bundled_app("#{gem_name}/#{minitest_test_file_path}").read).to include(minitest_test_class_name)
- end
+ task default: :test
+ RAKEFILE
- it "creates a default test which fails" do
- expect(bundled_app("#{gem_name}/#{minitest_test_file_path}").read).to include("assert false")
- end
+ expect(bundled_app("#{gem_name}/Rakefile").read).to eq(rakefile)
end
+ end
- context "gem.test setting set to minitest" do
- before do
- bundle "config set gem.test minitest"
- bundle "gem #{gem_name}"
- end
-
- it "creates a default rake task to run the test suite" do
- rakefile = <<~RAKEFILE
- # frozen_string_literal: true
+ context "--test parameter set to an invalid value" do
+ before do
+ bundle "gem #{gem_name} --test=foo", raise_on_error: false
+ end
- require "bundler/gem_tasks"
- require "minitest/test_task"
+ it "fails loudly" do
+ expect(last_command).to be_failure
+ expect(err).to match(/Expected '--test' to be one of .*; got foo/)
+ end
+ end
- Minitest::TestTask.create
+ context "gem.test set to rspec and --test with no arguments" do
+ before do
+ bundle_config "gem.test rspec"
+ bundle "gem #{gem_name} --test"
+ end
- task default: :test
- RAKEFILE
+ it "builds spec skeleton" do
+ expect(bundled_app("#{gem_name}/.rspec")).to exist
+ expect(bundled_app("#{gem_name}/spec/#{gem_name}_spec.rb")).to exist
+ expect(bundled_app("#{gem_name}/spec/spec_helper.rb")).to exist
+ end
- expect(bundled_app("#{gem_name}/Rakefile").read).to eq(rakefile)
- end
+ it "includes .rspec and spec/ into ignore list" do
+ expect(ignore_paths).to include(".rspec")
+ expect(ignore_paths).to include("spec/")
end
- context "--test parameter set to test-unit" do
- before do
- bundle "gem #{gem_name} --test=test-unit"
- end
+ it "hints that --test is already configured" do
+ expect(out).to match("rspec is already configured, ignoring --test flag.")
+ end
+ end
- it "depends on a specific version of test-unit" do
- allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
- builder = Bundler::Dsl.new
- builder.eval_gemfile(bundled_app("#{gem_name}/Gemfile"))
- builder.dependencies
- test_unit_dep = builder.dependencies.find {|d| d.name == "test-unit" }
- expect(test_unit_dep).to be_specific
+ context "gem.test setting set to false and --test with no arguments", :readline do
+ before do
+ bundle_config "gem.test false"
+ bundle "gem #{gem_name} --test" do |input, _, _|
+ input.puts
end
+ end
- it "builds spec skeleton" do
- expect(bundled_app("#{gem_name}/test/#{require_path}_test.rb")).to exist
- expect(bundled_app("#{gem_name}/test/test_helper.rb")).to exist
- end
+ it "asks to generate test files" do
+ expect(out).to match("Do you want to generate tests with your gem?")
+ end
- it "requires the main file" do
- expect(bundled_app("#{gem_name}/test/test_helper.rb").read).to include(%(require "#{require_path}"))
- end
+ it "hints that the choice will only be applied to the current gem" do
+ expect(out).to match("Your choice will only be applied to this gem.")
+ end
- it "requires 'test_helper'" do
- expect(bundled_app("#{gem_name}/test/#{require_path}_test.rb").read).to include(%(require "test_helper"))
- end
+ it_behaves_like "test framework is absent"
+ end
- it "creates a default test which fails" do
- expect(bundled_app("#{gem_name}/test/#{require_path}_test.rb").read).to include("assert_equal(\"expected\", \"actual\")")
+ context "gem.test setting not set and --test with no arguments", :readline do
+ before do
+ bundle_config_global "BUNDLE_GEM__TEST" => nil
+ bundle "gem #{gem_name} --test" do |input, _, _|
+ input.puts
end
end
- context "--test parameter set to an invalid value" do
- before do
- bundle "gem #{gem_name} --test=foo", raise_on_error: false
- end
+ it "asks to generate test files" do
+ expect(out).to match("Do you want to generate tests with your gem?")
+ end
- it "fails loudly" do
- expect(last_command).to be_failure
- expect(err).to match(/Expected '--test' to be one of .*; got foo/)
- end
+ it "hints that the choice will be applied to future bundle gem calls" do
+ hint = "Future `bundle gem` calls will use your choice. " \
+ "This setting can be changed anytime with `bundle config gem.test`."
+ expect(out).to match(hint)
end
- context "gem.test setting set to test-unit" do
- before do
- bundle "config set gem.test test-unit"
- bundle "gem #{gem_name}"
- end
+ it_behaves_like "test framework is absent"
+ end
- it "creates a default rake task to run the test suite" do
- rakefile = <<~RAKEFILE
- # frozen_string_literal: true
+ context "gem.test setting set to a test framework and --no-test" do
+ before do
+ bundle_config "gem.test rspec"
+ bundle "gem #{gem_name} --no-test"
+ end
- require "bundler/gem_tasks"
- require "rake/testtask"
+ it_behaves_like "test framework is absent"
+ end
- Rake::TestTask.new(:test) do |t|
- t.libs << "test"
- t.libs << "lib"
- t.test_files = FileList["test/**/*_test.rb"]
- end
+ context "--ci with no argument" do
+ before do
+ bundle "gem #{gem_name}"
+ end
- task default: :test
- RAKEFILE
+ it "does not generate any CI config" do
+ expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to_not exist
+ expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to_not exist
+ expect(bundled_app("#{gem_name}/.circleci/config.yml")).to_not exist
+ end
- expect(bundled_app("#{gem_name}/Rakefile").read).to eq(rakefile)
- end
+ it "does not add any CI config files into ignore list" do
+ expect(ignore_paths).not_to include(".github/")
+ expect(ignore_paths).not_to include(".gitlab-ci.yml")
+ expect(ignore_paths).not_to include(".circleci/")
end
+ end
- context "gem.test set to rspec and --test with no arguments" do
- before do
- bundle "config set gem.test rspec"
- bundle "gem #{gem_name} --test"
- end
+ context "--ci set to github" do
+ before do
+ bundle "gem #{gem_name} --ci=github"
+ end
- it "builds spec skeleton" do
- expect(bundled_app("#{gem_name}/.rspec")).to exist
- expect(bundled_app("#{gem_name}/spec/#{require_path}_spec.rb")).to exist
- expect(bundled_app("#{gem_name}/spec/spec_helper.rb")).to exist
- end
+ it "generates a GitHub Actions config file" do
+ expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to exist
+ end
- it "hints that --test is already configured" do
- expect(out).to match("rspec is already configured, ignoring --test flag.")
- end
+ it "includes .github/ into ignore list" do
+ expect(ignore_paths).to include(".github/")
end
+ end
- context "gem.test setting set to false and --test with no arguments", :readline do
- before do
- bundle "config set gem.test false"
- bundle "gem #{gem_name} --test" do |input, _, _|
- input.puts
- end
- end
+ context "--ci set to gitlab" do
+ before do
+ bundle "gem #{gem_name} --ci=gitlab"
+ end
- it "asks to generate test files" do
- expect(out).to match("Do you want to generate tests with your gem?")
- end
+ it "generates a GitLab CI config file" do
+ expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to exist
+ end
- it "hints that the choice will only be applied to the current gem" do
- expect(out).to match("Your choice will only be applied to this gem.")
- end
+ it "includes .gitlab-ci.yml into ignore list" do
+ expect(ignore_paths).to include(".gitlab-ci.yml")
+ end
+ end
- it_behaves_like "test framework is absent"
+ context "--ci set to circle" do
+ before do
+ bundle "gem #{gem_name} --ci=circle"
end
- context "gem.test setting not set and --test with no arguments", :readline do
- before do
- global_config "BUNDLE_GEM__TEST" => nil
- bundle "gem #{gem_name} --test" do |input, _, _|
- input.puts
- end
- end
+ it "generates a CircleCI config file" do
+ expect(bundled_app("#{gem_name}/.circleci/config.yml")).to exist
+ end
- it "asks to generate test files" do
- expect(out).to match("Do you want to generate tests with your gem?")
- end
+ it "includes .circleci/ into ignore list" do
+ expect(ignore_paths).to include(".circleci/")
+ end
+ end
- it "hints that the choice will be applied to future bundle gem calls" do
- hint = "Future `bundle gem` calls will use your choice. " \
- "This setting can be changed anytime with `bundle config gem.test`."
- expect(out).to match(hint)
- end
+ context "--ci set to an invalid value" do
+ before do
+ bundle "gem #{gem_name} --ci=foo", raise_on_error: false
+ end
- it_behaves_like "test framework is absent"
+ it "fails loudly" do
+ expect(last_command).to be_failure
+ expect(err).to match(/Expected '--ci' to be one of .*; got foo/)
end
+ end
- context "gem.test setting set to a test framework and --no-test" do
- before do
- bundle "config set gem.test rspec"
- bundle "gem #{gem_name} --no-test"
- end
+ context "gem.ci setting set to none" do
+ it "doesn't generate any CI config" do
+ expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to_not exist
+ expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to_not exist
+ expect(bundled_app("#{gem_name}/.circleci/config.yml")).to_not exist
+ end
+ end
- it_behaves_like "test framework is absent"
+ context "gem.ci setting set to github" do
+ it "generates a GitHub Actions config file" do
+ bundle_config "gem.ci github"
+ bundle "gem #{gem_name}"
+
+ expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to exist
end
+ end
- context "--ci with no argument" do
- it "does not generate any CI config" do
- bundle "gem #{gem_name}"
+ context "gem.ci setting set to gitlab" do
+ it "generates a GitLab CI config file" do
+ bundle_config "gem.ci gitlab"
+ bundle "gem #{gem_name}"
- expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to_not exist
- expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to_not exist
- expect(bundled_app("#{gem_name}/.circleci/config.yml")).to_not exist
- end
+ expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to exist
end
+ end
- context "--ci set to github" do
- it "generates a GitHub Actions config file" do
- bundle "gem #{gem_name} --ci=github"
+ context "gem.ci setting set to circle" do
+ it "generates a CircleCI config file" do
+ bundle_config "gem.ci circle"
+ bundle "gem #{gem_name}"
- expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to exist
- end
+ expect(bundled_app("#{gem_name}/.circleci/config.yml")).to exist
+ end
+ end
- it "contained .gitlab-ci.yml into ignore list" do
- bundle "gem #{gem_name} --ci=github"
+ context "gem.ci set to github and --ci with no arguments" do
+ before do
+ bundle_config "gem.ci github"
+ bundle "gem #{gem_name} --ci"
+ end
- expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to include(".git .github appveyor")
- end
+ it "generates a GitHub Actions config file" do
+ expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to exist
end
- context "--ci set to gitlab" do
- it "generates a GitLab CI config file" do
- bundle "gem #{gem_name} --ci=gitlab"
+ it "hints that --ci is already configured" do
+ expect(out).to match("github is already configured, ignoring --ci flag.")
+ end
+ end
- expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to exist
+ context "gem.ci setting set to false and --ci with no arguments", :readline do
+ before do
+ bundle_config "gem.ci false"
+ bundle "gem #{gem_name} --ci" do |input, _, _|
+ input.puts "github"
end
+ end
+
+ it "asks to setup CI" do
+ expect(out).to match("Do you want to set up continuous integration for your gem?")
+ end
- it "contained .gitlab-ci.yml into ignore list" do
- bundle "gem #{gem_name} --ci=gitlab"
+ it "hints that the choice will only be applied to the current gem" do
+ expect(out).to match("Your choice will only be applied to this gem.")
+ end
+ end
- expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to include(".git .gitlab-ci.yml appveyor")
+ context "gem.ci setting not set and --ci with no arguments", :readline do
+ before do
+ bundle_config_global "BUNDLE_GEM__CI" => nil
+ bundle "gem #{gem_name} --ci" do |input, _, _|
+ input.puts "github"
end
end
- context "--ci set to circle" do
- it "generates a CircleCI config file" do
- bundle "gem #{gem_name} --ci=circle"
+ it "asks to setup CI" do
+ expect(out).to match("Do you want to set up continuous integration for your gem?")
+ end
- expect(bundled_app("#{gem_name}/.circleci/config.yml")).to exist
- end
+ it "hints that the choice will be applied to future bundle gem calls" do
+ hint = "Future `bundle gem` calls will use your choice. " \
+ "This setting can be changed anytime with `bundle config gem.ci`."
+ expect(out).to match(hint)
+ end
+ end
- it "contained .circleci into ignore list" do
- bundle "gem #{gem_name} --ci=circle"
+ context "gem.ci setting set to a CI service and --no-ci" do
+ before do
+ bundle_config "gem.ci github"
+ bundle "gem #{gem_name} --no-ci"
+ end
- expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to include(".git .circleci appveyor")
- end
+ it "does not generate any CI config" do
+ expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to_not exist
+ expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to_not exist
+ expect(bundled_app("#{gem_name}/.circleci/config.yml")).to_not exist
end
+ end
- context "--ci set to an invalid value" do
- before do
- bundle "gem #{gem_name} --ci=foo", raise_on_error: false
- end
+ context "--linter with no argument" do
+ before do
+ bundle "gem #{gem_name}"
+ end
- it "fails loudly" do
- expect(last_command).to be_failure
- expect(err).to match(/Expected '--ci' to be one of .*; got foo/)
- end
+ it "does not generate any linter config" do
+ expect(bundled_app("#{gem_name}/.rubocop.yml")).to_not exist
+ expect(bundled_app("#{gem_name}/.standard.yml")).to_not exist
end
- context "gem.ci setting set to none" do
- it "doesn't generate any CI config" do
- expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to_not exist
- expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to_not exist
- expect(bundled_app("#{gem_name}/.circleci/config.yml")).to_not exist
- end
+ it "does not add any linter config files into ignore list" do
+ expect(ignore_paths).not_to include(".rubocop.yml")
+ expect(ignore_paths).not_to include(".standard.yml")
end
+ end
- context "gem.ci setting set to github" do
- it "generates a GitHub Actions config file" do
- bundle "config set gem.ci github"
- bundle "gem #{gem_name}"
+ context "--linter set to rubocop" do
+ before do
+ bundle "gem #{gem_name} --linter=rubocop"
+ end
- expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to exist
- end
+ it "generates a RuboCop config" do
+ expect(bundled_app("#{gem_name}/.rubocop.yml")).to exist
+ expect(bundled_app("#{gem_name}/.standard.yml")).to_not exist
end
- context "gem.ci setting set to gitlab" do
- it "generates a GitLab CI config file" do
- bundle "config set gem.ci gitlab"
- bundle "gem #{gem_name}"
+ it "includes .rubocop.yml into ignore list" do
+ expect(ignore_paths).to include(".rubocop.yml")
+ expect(ignore_paths).not_to include(".standard.yml")
+ end
+ end
- expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to exist
- end
+ context "--linter set to standard" do
+ before do
+ bundle "gem #{gem_name} --linter=standard"
end
- context "gem.ci setting set to circle" do
- it "generates a CircleCI config file" do
- bundle "config set gem.ci circle"
- bundle "gem #{gem_name}"
+ it "generates a Standard config" do
+ expect(bundled_app("#{gem_name}/.standard.yml")).to exist
+ expect(bundled_app("#{gem_name}/.rubocop.yml")).to_not exist
+ end
- expect(bundled_app("#{gem_name}/.circleci/config.yml")).to exist
- end
+ it "includes .standard.yml into ignore list" do
+ expect(ignore_paths).to include(".standard.yml")
+ expect(ignore_paths).not_to include(".rubocop.yml")
end
+ end
- context "gem.ci set to github and --ci with no arguments" do
- before do
- bundle "config set gem.ci github"
- bundle "gem #{gem_name} --ci"
- end
+ context "--linter set to an invalid value" do
+ before do
+ bundle "gem #{gem_name} --linter=foo", raise_on_error: false
+ end
- it "generates a GitHub Actions config file" do
- expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to exist
- end
+ it "fails loudly" do
+ expect(last_command).to be_failure
+ expect(err).to match(/Expected '--linter' to be one of .*; got foo/)
+ end
+ end
- it "hints that --ci is already configured" do
- expect(out).to match("github is already configured, ignoring --ci flag.")
- end
+ context "gem.linter setting set to none" do
+ before do
+ bundle "gem #{gem_name}"
end
- context "gem.ci setting set to false and --ci with no arguments", :readline do
- before do
- bundle "config set gem.ci false"
- bundle "gem #{gem_name} --ci" do |input, _, _|
- input.puts "github"
- end
- end
+ it "doesn't generate any linter config" do
+ expect(bundled_app("#{gem_name}/.rubocop.yml")).to_not exist
+ expect(bundled_app("#{gem_name}/.standard.yml")).to_not exist
+ end
- it "asks to setup CI" do
- expect(out).to match("Do you want to set up continuous integration for your gem?")
- end
+ it "does not add any linter config files into ignore list" do
+ expect(ignore_paths).not_to include(".rubocop.yml")
+ expect(ignore_paths).not_to include(".standard.yml")
+ end
+ end
- it "hints that the choice will only be applied to the current gem" do
- expect(out).to match("Your choice will only be applied to this gem.")
- end
+ context "gem.linter setting set to rubocop" do
+ before do
+ bundle_config "gem.linter rubocop"
+ bundle "gem #{gem_name}"
end
- context "gem.ci setting not set and --ci with no arguments", :readline do
- before do
- global_config "BUNDLE_GEM__CI" => nil
- bundle "gem #{gem_name} --ci" do |input, _, _|
- input.puts "github"
- end
- end
+ it "generates a RuboCop config file" do
+ expect(bundled_app("#{gem_name}/.rubocop.yml")).to exist
+ end
- it "asks to setup CI" do
- expect(out).to match("Do you want to set up continuous integration for your gem?")
- end
+ it "includes .rubocop.yml into ignore list" do
+ expect(ignore_paths).to include(".rubocop.yml")
+ end
+ end
- it "hints that the choice will be applied to future bundle gem calls" do
- hint = "Future `bundle gem` calls will use your choice. " \
- "This setting can be changed anytime with `bundle config gem.ci`."
- expect(out).to match(hint)
- end
+ context "gem.linter setting set to standard" do
+ before do
+ bundle_config "gem.linter standard"
+ bundle "gem #{gem_name}"
end
- context "gem.ci setting set to a CI service and --no-ci" do
- before do
- bundle "config set gem.ci github"
- bundle "gem #{gem_name} --no-ci"
- end
+ it "generates a Standard config file" do
+ expect(bundled_app("#{gem_name}/.standard.yml")).to exist
+ end
- it "does not generate any CI config" do
- expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to_not exist
- expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to_not exist
- expect(bundled_app("#{gem_name}/.circleci/config.yml")).to_not exist
- end
+ it "includes .standard.yml into ignore list" do
+ expect(ignore_paths).to include(".standard.yml")
end
+ end
- context "--linter with no argument" do
- it "does not generate any linter config" do
- bundle "gem #{gem_name}"
+ context "gem.linter set to rubocop and --linter with no arguments" do
+ before do
+ bundle_config "gem.linter rubocop"
+ bundle "gem #{gem_name} --linter"
+ end
- expect(bundled_app("#{gem_name}/.rubocop.yml")).to_not exist
- expect(bundled_app("#{gem_name}/.standard.yml")).to_not exist
- end
+ it "generates a RuboCop config file" do
+ expect(bundled_app("#{gem_name}/.rubocop.yml")).to exist
end
- context "--linter set to rubocop" do
- it "generates a RuboCop config" do
- bundle "gem #{gem_name} --linter=rubocop"
+ it "includes .rubocop.yml into ignore list" do
+ expect(ignore_paths).to include(".rubocop.yml")
+ end
- expect(bundled_app("#{gem_name}/.rubocop.yml")).to exist
- expect(bundled_app("#{gem_name}/.standard.yml")).to_not exist
+ it "hints that --linter is already configured" do
+ expect(out).to match("rubocop is already configured, ignoring --linter flag.")
+ end
+ end
+
+ context "gem.linter setting set to false and --linter with no arguments", :readline do
+ before do
+ bundle_config "gem.linter false"
+ bundle "gem #{gem_name} --linter" do |input, _, _|
+ input.puts "rubocop"
end
end
- context "--linter set to standard" do
- it "generates a Standard config" do
- bundle "gem #{gem_name} --linter=standard"
+ it "asks to setup a linter" do
+ expect(out).to match("Do you want to add a code linter and formatter to your gem?")
+ end
- expect(bundled_app("#{gem_name}/.standard.yml")).to exist
- expect(bundled_app("#{gem_name}/.rubocop.yml")).to_not exist
- end
+ it "hints that the choice will only be applied to the current gem" do
+ expect(out).to match("Your choice will only be applied to this gem.")
end
+ end
- context "--linter set to an invalid value" do
- before do
- bundle "gem #{gem_name} --linter=foo", raise_on_error: false
+ context "gem.linter setting not set and --linter with no arguments", :readline do
+ before do
+ bundle_config_global "BUNDLE_GEM__LINTER" => nil
+ bundle "gem #{gem_name} --linter" do |input, _, _|
+ input.puts "rubocop"
end
+ end
- it "fails loudly" do
- expect(last_command).to be_failure
- expect(err).to match(/Expected '--linter' to be one of .*; got foo/)
- end
+ it "asks to setup a linter" do
+ expect(out).to match("Do you want to add a code linter and formatter to your gem?")
end
- context "gem.linter setting set to none" do
- it "doesn't generate any linter config" do
- bundle "gem #{gem_name}"
+ it "hints that the choice will be applied to future bundle gem calls" do
+ hint = "Future `bundle gem` calls will use your choice. " \
+ "This setting can be changed anytime with `bundle config gem.linter`."
+ expect(out).to match(hint)
+ end
+ end
- expect(bundled_app("#{gem_name}/.rubocop.yml")).to_not exist
- expect(bundled_app("#{gem_name}/.standard.yml")).to_not exist
- end
+ context "gem.linter setting set to a linter and --no-linter" do
+ before do
+ bundle_config "gem.linter rubocop"
+ bundle "gem #{gem_name} --no-linter"
end
- context "gem.linter setting set to rubocop" do
- it "generates a RuboCop config file" do
- bundle "config set gem.linter rubocop"
- bundle "gem #{gem_name}"
+ it "does not generate any linter config" do
+ expect(bundled_app("#{gem_name}/.rubocop.yml")).to_not exist
+ expect(bundled_app("#{gem_name}/.standard.yml")).to_not exist
+ end
- expect(bundled_app("#{gem_name}/.rubocop.yml")).to exist
- end
+ it "does not add any linter config files into ignore list" do
+ expect(ignore_paths).not_to include(".rubocop.yml")
+ expect(ignore_paths).not_to include(".standard.yml")
end
+ end
- context "gem.linter setting set to standard" do
- it "generates a Standard config file" do
- bundle "config set gem.linter standard"
- bundle "gem #{gem_name}"
+ context "--edit option" do
+ it "opens the generated gemspec in the user's text editor" do
+ output = bundle "gem #{gem_name} --edit=echo"
+ gemspec_path = File.join(bundled_app, gem_name, "#{gem_name}.gemspec")
+ expect(output).to include("echo \"#{gemspec_path}\"")
+ end
+ end
- expect(bundled_app("#{gem_name}/.standard.yml")).to exist
- end
+ shared_examples_for "paths that depend on gem name" do
+ it "generates entrypoint, version file and signatures file at the proper path, with the proper content" do
+ bundle "gem #{gem_name}"
+
+ expect(bundled_app("#{gem_name}/lib/#{require_path}.rb")).to exist
+ expect(bundled_app("#{gem_name}/lib/#{require_path}.rb").read).to match(%r{require_relative "#{require_relative_path}/version"})
+ expect(bundled_app("#{gem_name}/lib/#{require_path}.rb").read).to match(/class Error < StandardError; end$/)
+
+ expect(bundled_app("#{gem_name}/lib/#{require_path}/version.rb")).to exist
+ expect(bundled_app("#{gem_name}/sig/#{require_path}.rbs")).to exist
end
- context "gem.rubocop setting set to true", bundler: "< 3" do
+ context "--exe parameter set" do
before do
- global_config "BUNDLE_GEM__LINTER" => nil
- bundle "config set gem.rubocop true"
- bundle "gem #{gem_name}"
+ bundle "gem #{gem_name} --exe"
end
- it "generates rubocop config" do
- expect(bundled_app("#{gem_name}/.rubocop.yml")).to exist
+ it "builds an exe file that requires the proper entrypoint" do
+ expect(bundled_app("#{gem_name}/exe/#{gem_name}")).to exist
+ expect(bundled_app("#{gem_name}/exe/#{gem_name}").read).to match(/require "#{require_path}"/)
end
+ end
- it "unsets gem.rubocop" do
- bundle "config gem.rubocop"
- expect(out).to include("You have not configured a value for `gem.rubocop`")
+ context "--bin parameter set" do
+ before do
+ bundle "gem #{gem_name} --bin"
end
- it "sets gem.linter=rubocop instead" do
- bundle "config gem.linter"
- expect(out).to match(/Set for the current user .*: "rubocop"/)
+ it "builds an exe file that requires the proper entrypoint" do
+ expect(bundled_app("#{gem_name}/exe/#{gem_name}")).to exist
+ expect(bundled_app("#{gem_name}/exe/#{gem_name}").read).to match(/require "#{require_path}"/)
end
end
- context "gem.linter set to rubocop and --linter with no arguments" do
+ context "--test parameter set to rspec" do
before do
- bundle "config set gem.linter rubocop"
- bundle "gem #{gem_name} --linter"
- end
-
- it "generates a RuboCop config file" do
- expect(bundled_app("#{gem_name}/.rubocop.yml")).to exist
+ bundle "gem #{gem_name} --test=rspec"
end
- it "hints that --linter is already configured" do
- expect(out).to match("rubocop is already configured, ignoring --linter flag.")
+ it "builds a spec helper that requires the proper entrypoint, and a default test in the proper path which fails" do
+ expect(bundled_app("#{gem_name}/spec/spec_helper.rb")).to exist
+ expect(bundled_app("#{gem_name}/spec/spec_helper.rb").read).to include(%(require "#{require_path}"))
+ expect(bundled_app("#{gem_name}/spec/#{require_path}_spec.rb")).to exist
+ expect(bundled_app("#{gem_name}/spec/#{require_path}_spec.rb").read).to include("expect(false).to eq(true)")
end
end
- context "gem.linter setting set to false and --linter with no arguments", :readline do
+ context "--test parameter set to minitest" do
before do
- bundle "config set gem.linter false"
- bundle "gem #{gem_name} --linter" do |input, _, _|
- input.puts "rubocop"
- end
+ bundle "gem #{gem_name} --test=minitest"
end
- it "asks to setup a linter" do
- expect(out).to match("Do you want to add a code linter and formatter to your gem?")
- end
+ it "builds a test helper that requires the proper entrypoint, and default test file in the proper path that defines the proper test class name, requires helper, and fails" do
+ expect(bundled_app("#{gem_name}/test/test_helper.rb")).to exist
+ expect(bundled_app("#{gem_name}/test/test_helper.rb").read).to include(%(require "#{require_path}"))
- it "hints that the choice will only be applied to the current gem" do
- expect(out).to match("Your choice will only be applied to this gem.")
+ expect(bundled_app("#{gem_name}/#{minitest_test_file_path}")).to exist
+ expect(bundled_app("#{gem_name}/#{minitest_test_file_path}").read).to include(minitest_test_class_name)
+ expect(bundled_app("#{gem_name}/#{minitest_test_file_path}").read).to include(%(require "test_helper"))
+ expect(bundled_app("#{gem_name}/#{minitest_test_file_path}").read).to include("assert false")
end
end
- context "gem.linter setting not set and --linter with no arguments", :readline do
+ context "--test parameter set to test-unit" do
before do
- global_config "BUNDLE_GEM__LINTER" => nil
- bundle "gem #{gem_name} --linter" do |input, _, _|
- input.puts "rubocop"
- end
+ bundle "gem #{gem_name} --test=test-unit"
end
- it "asks to setup a linter" do
- expect(out).to match("Do you want to add a code linter and formatter to your gem?")
+ it "builds a test helper that requires the proper entrypoint, and default test file in the proper path which requires helper and fails" do
+ expect(bundled_app("#{gem_name}/test/test_helper.rb")).to exist
+ expect(bundled_app("#{gem_name}/test/test_helper.rb").read).to include(%(require "#{require_path}"))
+ expect(bundled_app("#{gem_name}/test/#{require_path}_test.rb")).to exist
+ expect(bundled_app("#{gem_name}/test/#{require_path}_test.rb").read).to include(%(require "test_helper"))
+ expect(bundled_app("#{gem_name}/test/#{require_path}_test.rb").read).to include("assert_equal(\"expected\", \"actual\")")
end
+ end
+ end
- it "hints that the choice will be applied to future bundle gem calls" do
- hint = "Future `bundle gem` calls will use your choice. " \
- "This setting can be changed anytime with `bundle config gem.linter`."
- expect(out).to match(hint)
- end
+ context "with mit option in bundle config settings set to true" do
+ before do
+ bundle_config_global "gem.mit true"
end
+ it_behaves_like "--mit flag"
+ it_behaves_like "--no-mit flag"
+ end
- context "gem.linter setting set to a linter and --no-linter" do
- before do
- bundle "config set gem.linter rubocop"
- bundle "gem #{gem_name} --no-linter"
- end
+ context "with mit option in bundle config settings set to false" do
+ before do
+ bundle_config_global "gem.mit false"
+ end
+ it_behaves_like "--mit flag"
+ it_behaves_like "--no-mit flag"
+ end
- it "does not generate any linter config" do
- expect(bundled_app("#{gem_name}/.rubocop.yml")).to_not exist
- expect(bundled_app("#{gem_name}/.standard.yml")).to_not exist
- end
+ context "with coc option in bundle config settings set to true" do
+ before do
+ bundle_config_global "gem.coc true"
end
+ it_behaves_like "--coc flag"
+ it_behaves_like "--no-coc flag"
+ end
- context "--edit option" do
- it "opens the generated gemspec in the user's text editor" do
- output = bundle "gem #{gem_name} --edit=echo"
- gemspec_path = File.join(bundled_app, gem_name, "#{gem_name}.gemspec")
- expect(output).to include("echo \"#{gemspec_path}\"")
- end
+ context "with coc option in bundle config settings set to false" do
+ before do
+ bundle_config_global "gem.coc false"
end
+ it_behaves_like "--coc flag"
+ it_behaves_like "--no-coc flag"
end
- context "testing --mit and --coc options against bundle config settings" do
- let(:gem_name) { "test-gem" }
+ context "with rubocop option in bundle config settings set to true" do
+ before do
+ bundle_config_global "gem.rubocop true"
+ end
+ it_behaves_like "--linter=rubocop flag"
+ it_behaves_like "--linter=standard flag"
+ it_behaves_like "--no-linter flag"
+ end
- let(:require_path) { "test/gem" }
+ context "with rubocop option in bundle config settings set to false" do
+ before do
+ bundle_config_global "gem.rubocop false"
+ end
+ it_behaves_like "--linter=rubocop flag"
+ it_behaves_like "--linter=standard flag"
+ it_behaves_like "--no-linter flag"
+ end
- context "with mit option in bundle config settings set to true" do
- before do
- global_config "BUNDLE_GEM__MIT" => "true"
- end
- it_behaves_like "--mit flag"
- it_behaves_like "--no-mit flag"
+ context "with linter option in bundle config settings set to rubocop" do
+ before do
+ bundle_config_global "gem.linter rubocop"
end
+ it_behaves_like "--linter=rubocop flag"
+ it_behaves_like "--linter=standard flag"
+ it_behaves_like "--no-linter flag"
+ end
- context "with mit option in bundle config settings set to false" do
- before do
- global_config "BUNDLE_GEM__MIT" => "false"
- end
- it_behaves_like "--mit flag"
- it_behaves_like "--no-mit flag"
+ context "with linter option in bundle config settings set to standard" do
+ before do
+ bundle_config_global "gem.linter standard"
end
+ it_behaves_like "--linter=rubocop flag"
+ it_behaves_like "--linter=standard flag"
+ it_behaves_like "--no-linter flag"
+ end
- context "with coc option in bundle config settings set to true" do
- before do
- global_config "BUNDLE_GEM__COC" => "true"
- end
- it_behaves_like "--coc flag"
- it_behaves_like "--no-coc flag"
+ context "with linter option in bundle config settings set to false" do
+ before do
+ bundle_config_global "gem.linter false"
end
+ it_behaves_like "--linter=rubocop flag"
+ it_behaves_like "--linter=standard flag"
+ it_behaves_like "--no-linter flag"
+ end
- context "with coc option in bundle config settings set to false" do
- before do
- global_config "BUNDLE_GEM__COC" => "false"
- end
- it_behaves_like "--coc flag"
- it_behaves_like "--no-coc flag"
+ context "with changelog option in bundle config settings set to true" do
+ before do
+ bundle_config_global "gem.changelog true"
end
+ it_behaves_like "--changelog flag"
+ it_behaves_like "--no-changelog flag"
+ end
- context "with rubocop option in bundle config settings set to true" do
- before do
- global_config "BUNDLE_GEM__RUBOCOP" => "true"
- end
- it_behaves_like "--linter=rubocop flag"
- it_behaves_like "--linter=standard flag"
- it_behaves_like "--no-linter flag"
- it_behaves_like "--rubocop flag"
- it_behaves_like "--no-rubocop flag"
+ context "with changelog option in bundle config settings set to false" do
+ before do
+ bundle_config_global "gem.changelog false"
end
+ it_behaves_like "--changelog flag"
+ it_behaves_like "--no-changelog flag"
+ end
- context "with rubocop option in bundle config settings set to false" do
- before do
- global_config "BUNDLE_GEM__RUBOCOP" => "false"
- end
- it_behaves_like "--linter=rubocop flag"
- it_behaves_like "--linter=standard flag"
- it_behaves_like "--no-linter flag"
- it_behaves_like "--rubocop flag"
- it_behaves_like "--no-rubocop flag"
+ context "with bundle option in bundle config settings set to true" do
+ before do
+ bundle_config_global "gem.bundle true"
end
+ it_behaves_like "--bundle flag"
+ it_behaves_like "--no-bundle flag"
- context "with linter option in bundle config settings set to rubocop" do
- before do
- global_config "BUNDLE_GEM__LINTER" => "rubocop"
- end
- it_behaves_like "--linter=rubocop flag"
- it_behaves_like "--linter=standard flag"
- it_behaves_like "--no-linter flag"
+ it "runs bundle install" do
+ bundle "gem #{gem_name}"
+ expect(out).to include("Running bundle install in the new gem directory.")
+ end
+ end
+
+ context "with bundle option in bundle config settings set to false" do
+ before do
+ bundle_config_global "gem.bundle false"
+ end
+ it_behaves_like "--bundle flag"
+ it_behaves_like "--no-bundle flag"
+
+ it "does not run bundle install" do
+ bundle "gem #{gem_name}"
+ expect(out).to_not include("Running bundle install in the new gem directory.")
end
+ end
- context "with linter option in bundle config settings set to standard" do
+ context "without git config github.user set" do
+ before do
+ git("config --global --unset github.user")
+ end
+ context "with github-username option in bundle config settings set to some value" do
before do
- global_config "BUNDLE_GEM__LINTER" => "standard"
+ bundle_config_global "gem.github_username different_username"
end
- it_behaves_like "--linter=rubocop flag"
- it_behaves_like "--linter=standard flag"
- it_behaves_like "--no-linter flag"
+ it_behaves_like "--github-username option", "gh_user"
end
- context "with linter option in bundle config settings set to false" do
+ it_behaves_like "github_username configuration"
+
+ context "with github-username option in bundle config settings set to false" do
before do
- global_config "BUNDLE_GEM__LINTER" => "false"
+ bundle_config_global "gem.github_username false"
end
- it_behaves_like "--linter=rubocop flag"
- it_behaves_like "--linter=standard flag"
- it_behaves_like "--no-linter flag"
+ it_behaves_like "--github-username option", "gh_user"
end
- context "with changelog option in bundle config settings set to true" do
- before do
- global_config "BUNDLE_GEM__CHANGELOG" => "true"
+ context "when changelog is enabled" do
+ it "sets gemspec changelog_uri, homepage, homepage_uri, source_code_uri to TODOs" do
+ bundle "gem #{gem_name} --changelog"
+
+ expect(generated_gemspec.metadata["changelog_uri"]).
+ to eq("TODO: Put your gem's CHANGELOG.md URL here.")
+ expect(generated_gemspec.homepage).to eq("TODO: Put your gem's website or public repo URL here.")
+ expect(generated_gemspec.metadata["homepage_uri"]).to eq("TODO: Put your gem's website or public repo URL here.")
+ expect(generated_gemspec.metadata["source_code_uri"]).to eq("TODO: Put your gem's public repo URL here.")
end
- it_behaves_like "--changelog flag"
- it_behaves_like "--no-changelog flag"
end
- context "with changelog option in bundle config settings set to false" do
- before do
- global_config "BUNDLE_GEM__CHANGELOG" => "false"
+ context "when changelog is not enabled" do
+ it "sets gemspec homepage, homepage_uri, source_code_uri to TODOs and changelog_uri to nil" do
+ bundle "gem #{gem_name}"
+
+ expect(generated_gemspec.metadata["changelog_uri"]).to be_nil
+ expect(generated_gemspec.homepage).to eq("TODO: Put your gem's website or public repo URL here.")
+ expect(generated_gemspec.metadata["homepage_uri"]).to eq("TODO: Put your gem's website or public repo URL here.")
+ expect(generated_gemspec.metadata["source_code_uri"]).to eq("TODO: Put your gem's public repo URL here.")
end
- it_behaves_like "--changelog flag"
- it_behaves_like "--no-changelog flag"
end
end
- context "testing --github-username option against git and bundle config settings" do
- context "without git config set" do
+ context "with git config github.user set" do
+ context "with github-username option in bundle config settings set to some value" do
before do
- git("config --global --unset github.user")
- end
- context "with github-username option in bundle config settings set to some value" do
- before do
- global_config "BUNDLE_GEM__GITHUB_USERNAME" => "different_username"
- end
- it_behaves_like "--github-username option", "gh_user"
+ bundle_config_global "gem.github_username different_username"
end
+ it_behaves_like "--github-username option", "gh_user"
+ end
- context "with github-username option in bundle config settings set to false" do
- before do
- global_config "BUNDLE_GEM__GITHUB_USERNAME" => "false"
- end
- it_behaves_like "--github-username option", "gh_user"
+ it_behaves_like "github_username configuration"
+
+ context "with github-username option in bundle config settings set to false" do
+ before do
+ bundle_config_global "gem.github_username false"
end
+ it_behaves_like "--github-username option", "gh_user"
end
- context "with git config set" do
- context "with github-username option in bundle config settings set to some value" do
- before do
- global_config "BUNDLE_GEM__GITHUB_USERNAME" => "different_username"
- end
- it_behaves_like "--github-username option", "gh_user"
+ context "when changelog is enabled" do
+ it "sets gemspec changelog_uri, homepage, homepage_uri, source_code_uri based on git username" do
+ bundle "gem #{gem_name} --changelog"
+
+ expect(generated_gemspec.metadata["changelog_uri"]).
+ to eq("https://github.com/bundleuser/#{gem_name}/blob/main/CHANGELOG.md")
+ expect(generated_gemspec.homepage).to eq("https://github.com/bundleuser/#{gem_name}")
+ expect(generated_gemspec.metadata["homepage_uri"]).to eq("https://github.com/bundleuser/#{gem_name}")
+ expect(generated_gemspec.metadata["source_code_uri"]).to eq("https://github.com/bundleuser/#{gem_name}")
end
+ end
- context "with github-username option in bundle config settings set to false" do
- before do
- global_config "BUNDLE_GEM__GITHUB_USERNAME" => "false"
- end
- it_behaves_like "--github-username option", "gh_user"
+ context "when changelog is not enabled" do
+ it "sets gemspec source_code_uri, homepage, homepage_uri but not changelog_uri" do
+ bundle "gem #{gem_name}"
+
+ expect(generated_gemspec.metadata["changelog_uri"]).to be_nil
+ expect(generated_gemspec.homepage).to eq("https://github.com/bundleuser/#{gem_name}")
+ expect(generated_gemspec.metadata["homepage_uri"]).to eq("https://github.com/bundleuser/#{gem_name}")
+ expect(generated_gemspec.metadata["source_code_uri"]).to eq("https://github.com/bundleuser/#{gem_name}")
end
end
end
- context "testing github_username bundle config against git config settings" do
- context "without git config set" do
- before do
- git("config --global --unset github.user")
- end
+ context "standard gem naming" do
+ let(:require_path) { gem_name }
- it_behaves_like "github_username configuration"
- end
+ let(:require_relative_path) { gem_name }
- context "with git config set" do
- it_behaves_like "github_username configuration"
- end
+ let(:minitest_test_file_path) { "test/test_#{gem_name}.rb" }
+
+ let(:minitest_test_class_name) { "class TestMygem < Minitest::Test" }
+
+ include_examples "paths that depend on gem name"
end
context "gem naming with underscore" do
@@ -1494,25 +1685,7 @@ RSpec.describe "bundle gem" do
expect(bundled_app("#{gem_name}/lib/#{require_path}.rb").read).to match(/module TestGem/)
end
- include_examples "generating a gem"
-
- context "--ext parameter with no value" do
- context "is deprecated", bundler: "< 3" do
- it "prints deprecation when used after gem name" do
- bundle ["gem", "--ext", gem_name].compact.join(" ")
- expect(err).to include "[DEPRECATED]"
- expect(err).to include "`--ext` with no arguments has been deprecated"
- expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.c")).to exist
- end
-
- it "prints deprecation when used before gem name" do
- bundle ["gem", gem_name, "--ext"].compact.join(" ")
- expect(err).to include "[DEPRECATED]"
- expect(err).to include "`--ext` with no arguments has been deprecated"
- expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.c")).to exist
- end
- end
- end
+ include_examples "paths that depend on gem name"
context "--ext parameter set with C" do
let(:flags) { "--ext=c" }
@@ -1521,16 +1694,19 @@ RSpec.describe "bundle gem" do
bundle ["gem", gem_name, flags].compact.join(" ")
end
- it "is not deprecated" do
- expect(err).not_to include "[DEPRECATED] Option `--ext` without explicit value is deprecated."
- end
-
it "builds ext skeleton" do
expect(bundled_app("#{gem_name}/ext/#{gem_name}/extconf.rb")).to exist
expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.h")).to exist
expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.c")).to exist
end
+ it "generates native extension loading code" do
+ expect(bundled_app("#{gem_name}/lib/#{gem_name}.rb").read).to include(<<~RUBY)
+ require_relative "test_gem/version"
+ require "#{gem_name}/#{gem_name}"
+ RUBY
+ end
+
it "includes rake-compiler, but no Rust related changes" do
expect(bundled_app("#{gem_name}/Gemfile").read).to include('gem "rake-compiler"')
@@ -1560,24 +1736,10 @@ RSpec.describe "bundle gem" do
end
end
- context "--ext parameter set with rust and old RubyGems" do
- it "fails in friendly way" do
- if ::Gem::Version.new("3.3.11") <= ::Gem.rubygems_version
- skip "RubyGems compatible with Rust builder"
- end
-
- expect do
- bundle ["gem", gem_name, "--ext=rust"].compact.join(" ")
- end.to raise_error(RuntimeError, /too old to build Rust extension/)
- end
- end
-
context "--ext parameter set with rust" do
let(:flags) { "--ext=rust" }
before do
- skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version
-
bundle ["gem", gem_name, flags].compact.join(" ")
end
@@ -1590,12 +1752,12 @@ RSpec.describe "bundle gem" do
expect(bundled_app("#{gem_name}/ext/#{gem_name}/Cargo.toml")).to exist
expect(bundled_app("#{gem_name}/ext/#{gem_name}/extconf.rb")).to exist
expect(bundled_app("#{gem_name}/ext/#{gem_name}/src/lib.rs")).to exist
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/build.rs")).to exist
end
- it "includes rake-compiler, rb_sys gems and required_rubygems_version constraint" do
+ it "includes rake-compiler and rb_sys gems constraint" do
expect(bundled_app("#{gem_name}/Gemfile").read).to include('gem "rake-compiler"')
expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to include('spec.add_dependency "rb_sys"')
- expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to include('spec.required_rubygems_version = ">= ')
end
it "depends on compile task for build" do
@@ -1618,6 +1780,182 @@ RSpec.describe "bundle gem" do
expect(bundled_app("#{gem_name}/Rakefile").read).to eq(rakefile)
end
+
+ it "configures the crate such that `cargo test` works", :ruby_repo, :mri_only do
+ env = setup_rust_env
+ gem_path = bundled_app(gem_name)
+ result = sys_exec("cargo test", env: env, dir: gem_path, timeout: 300)
+
+ expect(result).to include("1 passed")
+ end
+
+ def setup_rust_env
+ skip "rust toolchain of mingw is broken" if RUBY_PLATFORM.match?("mingw")
+
+ env = {
+ "CARGO_HOME" => ENV.fetch("CARGO_HOME", File.join(ENV["HOME"], ".cargo")),
+ "RUSTUP_HOME" => ENV.fetch("RUSTUP_HOME", File.join(ENV["HOME"], ".rustup")),
+ "RUSTUP_TOOLCHAIN" => ENV.fetch("RUSTUP_TOOLCHAIN", "stable"),
+ }
+
+ system(env, "cargo", "-V", out: IO::NULL, err: [:child, :out])
+ skip "cargo not present" unless $?.success?
+ # Hermetic Cargo setup
+ RbConfig::CONFIG.each {|k, v| env["RBCONFIG_#{k}"] = v }
+ env
+ end
+ end
+
+ context "--ext parameter set with go" do
+ let(:flags) { "--ext=go" }
+
+ before do
+ bundle ["gem", gem_name, flags].compact.join(" ")
+ end
+
+ after do
+ sys_exec("go clean -modcache", raise_on_error: true) if installed_go?
+ end
+
+ it "is not deprecated" do
+ expect(err).not_to include "[DEPRECATED] Option `--ext` without explicit value is deprecated."
+ end
+
+ it "builds ext skeleton" do
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.c")).to exist
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.go")).to exist
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.h")).to exist
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/extconf.rb")).to exist
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/go.mod")).to exist
+ end
+
+ it "includes extconf.rb in gem_name.gemspec" do
+ expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to include(%(spec.extensions = ["ext/#{gem_name}/extconf.rb"]))
+ end
+
+ it "includes go_gem in gem_name.gemspec" do
+ expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to include('spec.add_dependency "go_gem", ">= 0.2"')
+ end
+
+ it "includes go_gem extension in extconf.rb" do
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/extconf.rb").read).to include(<<~RUBY)
+ require "mkmf"
+ require "go_gem/mkmf"
+ RUBY
+
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/extconf.rb").read).to include(%(create_go_makefile("#{gem_name}/#{gem_name}")))
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/extconf.rb").read).not_to include("create_makefile")
+ end
+
+ it "includes go_gem extension in gem_name.c" do
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.c").read).to eq(<<~C)
+ #include "#{gem_name}.h"
+ #include "_cgo_export.h"
+ C
+ end
+
+ it "includes skeleton code in gem_name.go" do
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.go").read).to include(<<~GO)
+ /*
+ #include "#{gem_name}.h"
+
+ VALUE rb_#{gem_name}_sum(VALUE self, VALUE a, VALUE b);
+ */
+ import "C"
+ GO
+
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.go").read).to include(<<~GO)
+ //export rb_#{gem_name}_sum
+ func rb_#{gem_name}_sum(_ C.VALUE, a C.VALUE, b C.VALUE) C.VALUE {
+ GO
+
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.go").read).to include(<<~GO)
+ //export Init_#{gem_name}
+ func Init_#{gem_name}() {
+ GO
+ end
+
+ it "includes valid module name in go.mod" do
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/go.mod").read).to include("module github.com/bundleuser/#{gem_name}")
+ end
+
+ it "includes go_gem extension in Rakefile" do
+ expect(bundled_app("#{gem_name}/Rakefile").read).to include(<<~RUBY)
+ require "go_gem/rake_task"
+
+ GoGem::RakeTask.new("#{gem_name}")
+ RUBY
+ end
+
+ context "with --no-ci" do
+ let(:flags) { "--ext=go --no-ci" }
+
+ it_behaves_like "CI config is absent"
+ end
+
+ context "--ci set to github" do
+ let(:flags) { "--ext=go --ci=github" }
+
+ it "generates .github/workflows/main.yml" do
+ expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to exist
+ expect(bundled_app("#{gem_name}/.github/workflows/main.yml").read).to include("go-version-file: ext/#{gem_name}/go.mod")
+ end
+ end
+
+ context "--ci set to circle" do
+ let(:flags) { "--ext=go --ci=circle" }
+
+ it "generates a .circleci/config.yml" do
+ expect(bundled_app("#{gem_name}/.circleci/config.yml")).to exist
+
+ expect(bundled_app("#{gem_name}/.circleci/config.yml").read).to include(<<-YAML.strip)
+ environment:
+ GO_VERSION:
+ YAML
+
+ expect(bundled_app("#{gem_name}/.circleci/config.yml").read).to include(<<-YAML)
+ - run:
+ name: Install Go
+ command: |
+ wget https://go.dev/dl/go$GO_VERSION.linux-amd64.tar.gz -O /tmp/go.tar.gz
+ tar -C /usr/local -xzf /tmp/go.tar.gz
+ echo 'export PATH=/usr/local/go/bin:"$PATH"' >> "$BASH_ENV"
+ YAML
+ end
+ end
+
+ context "--ci set to gitlab" do
+ let(:flags) { "--ext=go --ci=gitlab" }
+
+ it "generates a .gitlab-ci.yml" do
+ expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to exist
+
+ expect(bundled_app("#{gem_name}/.gitlab-ci.yml").read).to include(<<-YAML)
+ - wget https://go.dev/dl/go$GO_VERSION.linux-amd64.tar.gz -O /tmp/go.tar.gz
+ - tar -C /usr/local -xzf /tmp/go.tar.gz
+ - export PATH=/usr/local/go/bin:$PATH
+ YAML
+
+ expect(bundled_app("#{gem_name}/.gitlab-ci.yml").read).to include(<<-YAML.strip)
+ variables:
+ GO_VERSION:
+ YAML
+ end
+ end
+
+ context "without github.user" do
+ before do
+ # FIXME: GitHub Actions Windows Runner hang up here for some reason...
+ skip "Workaround for hung up" if Gem.win_platform?
+
+ git("config --global --unset github.user")
+ bundle ["gem", gem_name, flags].compact.join(" ")
+ end
+
+ it "includes valid module name in go.mod" do
+ expect(bundled_app("#{gem_name}/ext/#{gem_name}/go.mod").read).to include("module github.com/username/#{gem_name}")
+ end
+ end
end
end
@@ -1638,7 +1976,7 @@ RSpec.describe "bundle gem" do
expect(bundled_app("#{gem_name}/lib/#{require_path}.rb").read).to match(/module Test\n module Gem/)
end
- include_examples "generating a gem"
+ include_examples "paths that depend on gem name"
end
describe "uncommon gem names" do
@@ -1687,6 +2025,19 @@ Usage: "bundle gem NAME [OPTIONS]"
it { expect(err).to include("Invalid gem name #{subject}") }
end
+ context "starting with a number" do
+ subject { "1gem" }
+ it { expect(err).to include("Invalid gem name #{subject}") }
+ end
+
+ context "including capital letter" do
+ subject { "CAPITAL" }
+ it "should warn but not error" do
+ expect(err).to include("Gem names with capital letters are not recommended")
+ expect(bundled_app("#{subject}/#{subject}.gemspec")).to exist
+ end
+ end
+
context "starting with an existing const name" do
subject { "gem-somenewconstantname" }
it { expect(err).not_to include("Invalid gem name #{subject}") }
@@ -1700,7 +2051,7 @@ Usage: "bundle gem NAME [OPTIONS]"
context "on first run", :readline do
it "asks about test framework" do
- global_config "BUNDLE_GEM__TEST" => nil
+ bundle_config_global "BUNDLE_GEM__TEST" => nil
bundle "gem foobar" do |input, _, _|
input.puts "rspec"
@@ -1723,7 +2074,7 @@ Usage: "bundle gem NAME [OPTIONS]"
end
it "asks about CI service" do
- global_config "BUNDLE_GEM__CI" => nil
+ bundle_config_global "BUNDLE_GEM__CI" => nil
bundle "gem foobar" do |input, _, _|
input.puts "github"
@@ -1732,8 +2083,8 @@ Usage: "bundle gem NAME [OPTIONS]"
expect(bundled_app("foobar/.github/workflows/main.yml")).to exist
end
- it "asks about MIT license" do
- global_config "BUNDLE_GEM__MIT" => nil
+ it "asks about MIT license just once" do
+ bundle_config_global "BUNDLE_GEM__MIT" => nil
bundle "config list"
@@ -1742,26 +2093,29 @@ Usage: "bundle gem NAME [OPTIONS]"
end
expect(bundled_app("foobar/LICENSE.txt")).to exist
+ expect(out).to include("Using a MIT license means").once
end
- it "asks about CoC" do
- global_config "BUNDLE_GEM__COC" => nil
+ it "asks about CoC just once" do
+ bundle_config_global "BUNDLE_GEM__COC" => nil
bundle "gem foobar" do |input, _, _|
input.puts "yes"
end
expect(bundled_app("foobar/CODE_OF_CONDUCT.md")).to exist
+ expect(out).to include("Codes of conduct can increase contributions to your project").once
end
- it "asks about CHANGELOG" do
- global_config "BUNDLE_GEM__CHANGELOG" => nil
+ it "asks about CHANGELOG just once" do
+ bundle_config_global "BUNDLE_GEM__CHANGELOG" => nil
bundle "gem foobar" do |input, _, _|
input.puts "yes"
end
expect(bundled_app("foobar/CHANGELOG.md")).to exist
+ expect(out).to include("A changelog is a file which contains").once
end
end
diff --git a/spec/bundler/commands/open_spec.rb b/spec/bundler/commands/open_spec.rb
index e0c79aa407..664dc58919 100644
--- a/spec/bundler/commands/open_spec.rb
+++ b/spec/bundler/commands/open_spec.rb
@@ -49,7 +49,7 @@ RSpec.describe "bundle open" do
it "suggests alternatives for similar-sounding gems" do
bundle "open Rails", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, raise_on_error: false
- expect(err).to match(/did you mean rails\?/i)
+ expect(err).to match(/did you mean 'rails'\?/i)
end
it "opens the gem with short words" do
@@ -80,12 +80,12 @@ RSpec.describe "bundle open" do
it "suggests alternatives for similar-sounding gems when using subpath" do
bundle "open Rails --path README.md", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, raise_on_error: false
- expect(err).to match(/did you mean rails\?/i)
+ expect(err).to match(/did you mean 'rails'\?/i)
end
it "suggests alternatives for similar-sounding gems when using deep subpath" do
bundle "open Rails --path some/path/here", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, raise_on_error: false
- expect(err).to match(/did you mean rails\?/i)
+ expect(err).to match(/did you mean 'rails'\?/i)
end
it "opens subpath of the short worded gem" do
@@ -139,7 +139,7 @@ RSpec.describe "bundle open" do
gem "foo"
G
- bundle "config set auto_install 1"
+ bundle_config "auto_install 1"
bundle "open rails", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }
expect(out).to include("Installing foo 1.0")
end
diff --git a/spec/bundler/commands/outdated_spec.rb b/spec/bundler/commands/outdated_spec.rb
index 38121ce50e..28ed51d61e 100644
--- a/spec/bundler/commands/outdated_spec.rb
+++ b/spec/bundler/commands/outdated_spec.rb
@@ -30,7 +30,7 @@ RSpec.describe "bundle outdated" do
bundle "outdated", raise_on_error: false
expected_output = <<~TABLE.gsub("x", "\\\h").tr(".", "\.").strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
activesupport 2.3.5 3.0 = 2.3.5 default
foo 1.0 xxxxxxx 1.0 xxxxxxx >= 0 default
weakling 0.0.3 0.2 ~> 0.0.1 default
@@ -53,7 +53,7 @@ RSpec.describe "bundle outdated" do
bundle "outdated", raise_on_error: false
expected_output = <<~TABLE
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
AAA 1.0.0 2.0.0 = 1.0.0 default
TABLE
@@ -92,7 +92,7 @@ RSpec.describe "bundle outdated" do
bundle "outdated", raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
activesupport 2.3.5 3.0 = 2.3.5 development, test
terranova 8 9 = 8 default
TABLE
@@ -120,7 +120,7 @@ RSpec.describe "bundle outdated" do
end
it "shows the location of the latest version's gemspec if installed" do
- bundle "config set clean false"
+ bundle_config "clean false"
update_repo2 { build_gem "activesupport", "3.0" }
update_repo2 { build_gem "terranova", "9" }
@@ -142,81 +142,15 @@ RSpec.describe "bundle outdated" do
bundle "outdated --verbose", raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups Path
+ Gem Current Latest Requested Groups Release Date Path
activesupport 2.3.5 3.0 = 2.3.5 default
- terranova 8 9 = 8 default #{default_bundle_path("specifications/terranova-9.gemspec")}
+ terranova 8 9 = 8 default #{default_bundle_path("specifications/terranova-9.gemspec")}
TABLE
expect(out).to end_with(expected_output)
end
end
- describe "with multiple, duplicated sources, with lockfile in old format", bundler: "< 3" do
- before do
- build_repo2 do
- build_gem "dotenv", "2.7.6"
-
- build_gem "oj", "3.11.3"
- build_gem "oj", "3.11.5"
-
- build_gem "vcr", "6.0.0"
- end
-
- build_repo3 do
- build_gem "pkg-gem-flowbyte-with-dep", "1.0.0" do |s|
- s.add_dependency "oj"
- end
- end
-
- gemfile <<~G
- source "https://gem.repo2"
-
- gem "dotenv"
-
- source "https://gem.repo3" do
- gem 'pkg-gem-flowbyte-with-dep'
- end
-
- gem "vcr",source: "https://gem.repo2"
- G
-
- lockfile <<~L
- GEM
- remote: https://gem.repo2/
- remote: https://gem.repo3/
- specs:
- dotenv (2.7.6)
- oj (3.11.3)
- pkg-gem-flowbyte-with-dep (1.0.0)
- oj
- vcr (6.0.0)
-
- PLATFORMS
- #{local_platform}
-
- DEPENDENCIES
- dotenv
- pkg-gem-flowbyte-with-dep!
- vcr!
-
- BUNDLED WITH
- #{Bundler::VERSION}
- L
- end
-
- it "works" do
- bundle :install, artifice: "compact_index"
- bundle :outdated, artifice: "compact_index", raise_on_error: false
-
- expected_output = <<~TABLE
- Gem Current Latest Requested Groups
- oj 3.11.3 3.11.5
- TABLE
-
- expect(out).to include(expected_output.strip)
- end
- end
-
describe "with --group option" do
before do
build_repo2 do
@@ -263,7 +197,7 @@ RSpec.describe "bundle outdated" do
test_group_option("default")
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
terranova 8 9 = 8 default
TABLE
@@ -274,7 +208,7 @@ RSpec.describe "bundle outdated" do
test_group_option("development")
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
activesupport 2.3.5 3.0 = 2.3.5 development, test
duradura 7.0 8.0 = 7.0 development, test
TABLE
@@ -286,7 +220,7 @@ RSpec.describe "bundle outdated" do
test_group_option("test")
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
activesupport 2.3.5 3.0 = 2.3.5 development, test
duradura 7.0 8.0 = 7.0 development, test
TABLE
@@ -323,7 +257,7 @@ RSpec.describe "bundle outdated" do
bundle "outdated --groups", raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
bar 2.0.0 3.0.0
TABLE
@@ -365,7 +299,7 @@ RSpec.describe "bundle outdated" do
bundle "outdated --groups", raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
activesupport 2.3.5 3.0 = 2.3.5 development, test
duradura 7.0 8.0 = 7.0 development, test
terranova 8 9 = 8 default
@@ -399,7 +333,7 @@ RSpec.describe "bundle outdated" do
build_gem "activesupport", "2.3.4"
end
- bundle "config set clean false"
+ bundle_config "clean false"
install_gemfile <<-G
source "https://gem.repo2"
@@ -409,7 +343,7 @@ RSpec.describe "bundle outdated" do
bundle "outdated --local", raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
activesupport 2.3.4 2.3.5 = 2.3.4 default
TABLE
@@ -417,7 +351,7 @@ RSpec.describe "bundle outdated" do
end
it "doesn't hit repo2" do
- FileUtils.rm_rf(gem_repo2)
+ FileUtils.rm_r(gem_repo2)
bundle "outdated --local"
expect(out).not_to match(/Fetching (gem|version|dependency) metadata from/)
@@ -520,7 +454,7 @@ RSpec.describe "bundle outdated" do
bundle "outdated foo", raise_on_error: false
expected_output = <<~TABLE.gsub("x", "\\\h").tr(".", "\.").strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
foo 1.0 xxxxxxx 1.0 xxxxxxx >= 0 default
TABLE
@@ -551,13 +485,13 @@ RSpec.describe "bundle outdated" do
zeitwerk
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "outdated zeitwerk", raise_on_error: false
expected_output = <<~TABLE.tr(".", "\.").strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
zeitwerk 1.0.0 2.0.0 >= 0 default
TABLE
@@ -605,7 +539,7 @@ RSpec.describe "bundle outdated" do
bundle "outdated --pre", raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
activesupport 2.3.5 3.0.0.beta = 2.3.5 default
TABLE
@@ -628,7 +562,7 @@ RSpec.describe "bundle outdated" do
bundle "outdated", raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
activesupport 3.0.0.beta.1 3.0.0.beta.2 = 3.0.0.beta.1 default
TABLE
@@ -664,7 +598,7 @@ RSpec.describe "bundle outdated" do
bundle :outdated, "filter-strict": true, raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
weakling 0.0.3 0.0.5 ~> 0.0.1 default
TABLE
@@ -680,7 +614,7 @@ RSpec.describe "bundle outdated" do
bundle :outdated, strict: true, raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
weakling 0.0.3 0.0.5 ~> 0.0.1 default
TABLE
@@ -725,7 +659,7 @@ RSpec.describe "bundle outdated" do
bundle :outdated, :"filter-strict" => true, "filter-patch" => true, :raise_on_error => false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
weakling 0.0.3 0.0.5 >= 0.0.1 default
TABLE
@@ -747,7 +681,7 @@ RSpec.describe "bundle outdated" do
bundle :outdated, :"filter-strict" => true, "filter-minor" => true, :raise_on_error => false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
weakling 0.0.3 0.1.5 >= 0.0.1 default
TABLE
@@ -769,7 +703,7 @@ RSpec.describe "bundle outdated" do
bundle :outdated, :"filter-strict" => true, "filter-major" => true, :raise_on_error => false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
weakling 0.0.3 1.1.5 >= 0.0.1 default
TABLE
@@ -814,12 +748,12 @@ RSpec.describe "bundle outdated" do
gem "foo"
G
- bundle "config set auto_install 1"
+ bundle_config "auto_install 1"
bundle :outdated, raise_on_error: false
expect(out).to include("Installing foo 1.0")
end
- context "after bundle install --deployment", bundler: "< 3" do
+ context "in deployment mode" do
before do
build_repo2
@@ -830,7 +764,7 @@ RSpec.describe "bundle outdated" do
gem "foo"
G
bundle :lock
- bundle :install, deployment: true
+ bundle_config "deployment true"
end
it "outputs a helpful message about being in deployment mode" do
@@ -858,7 +792,7 @@ RSpec.describe "bundle outdated" do
gem "myrack"
gem "foo"
G
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
end
it "outputs a helpful message about being in deployment mode" do
@@ -913,7 +847,7 @@ RSpec.describe "bundle outdated" do
bundle "outdated", raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
laduradura 5.15.2 5.15.3 = 5.15.2 default
TABLE
@@ -974,7 +908,7 @@ RSpec.describe "bundle outdated" do
gem "terranova", '8'
G
- simulate_new_machine
+ pristine_system_gems
update_git "foo", path: lib_path("foo")
update_repo2 do
@@ -1215,7 +1149,7 @@ RSpec.describe "bundle outdated" do
bundle "outdated --patch --filter-patch", raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
major 1.0.0 1.0.1 >= 0 default
minor 1.0.0 1.0.1 >= 0 default
patch 1.0.0 1.0.1 >= 0 default
@@ -1228,7 +1162,7 @@ RSpec.describe "bundle outdated" do
bundle "outdated --minor --filter-minor", raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
major 1.0.0 1.1.0 >= 0 default
minor 1.0.0 1.1.0 >= 0 default
TABLE
@@ -1282,7 +1216,7 @@ RSpec.describe "bundle outdated" do
bundle "outdated --patch --filter-patch", raise_on_error: false, env: { "DEBUG_RESOLVER" => "1" }
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
bar 2.0.3 2.0.5
foo 1.4.3 1.4.4 >= 0 default
TABLE
@@ -1294,7 +1228,7 @@ RSpec.describe "bundle outdated" do
bundle "outdated --patch --filter-patch", raise_on_error: false, env: { "DEBUG" => "1" }
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups Path
+ Gem Current Latest Requested Groups Release Date Path
bar 2.0.3 2.0.5
foo 1.4.3 1.4.4 >= 0 default
TABLE
@@ -1326,7 +1260,7 @@ RSpec.describe "bundle outdated" do
bundle "outdated --only-explicit", raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
weakling 0.2 0.3 >= 0 default
TABLE
@@ -1363,7 +1297,7 @@ RSpec.describe "bundle outdated" do
nokogiri
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
gemfile <<-G
@@ -1376,7 +1310,7 @@ RSpec.describe "bundle outdated" do
bundle "outdated", raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
nokogiri 1.11.1 1.11.2 >= 0 default
TABLE
@@ -1417,7 +1351,7 @@ RSpec.describe "bundle outdated" do
mini_portile2
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -1425,7 +1359,7 @@ RSpec.describe "bundle outdated" do
bundle "outdated", raise_on_error: false
expected_output = <<~TABLE.strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
mini_portile2 2.5.2 2.5.3 >= 0 default
TABLE
diff --git a/spec/bundler/commands/platform_spec.rb b/spec/bundler/commands/platform_spec.rb
index 6e0a02bcf0..9d7354c54f 100644
--- a/spec/bundler/commands/platform_spec.rb
+++ b/spec/bundler/commands/platform_spec.rb
@@ -227,7 +227,7 @@ G
ruby 1.0.0p127
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "platform --ruby"
@@ -250,7 +250,7 @@ G
DEPENDENCIES
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "platform --ruby"
@@ -302,9 +302,9 @@ G
expect(err).to be_include("Your #{local_ruby_engine} version is #{local_engine_version}, but your Gemfile specified #{local_ruby_engine} #{not_local_engine_version}")
end
- def should_be_patchlevel_incorrect
- expect(exitstatus).to eq(18)
- expect(err).to be_include("Your Ruby patchlevel is #{RUBY_PATCHLEVEL}, but your Gemfile specified #{not_local_patchlevel}")
+ def should_ignore_patchlevel
+ expect(exitstatus).to eq(0)
+ expect(err).to eq("")
end
def should_be_patchlevel_fixnum
@@ -382,7 +382,7 @@ G
should_be_engine_version_incorrect
end
- it "doesn't install when patchlevel doesn't match" do
+ it "does install even when patchlevel doesn't match" do
install_gemfile <<-G, raise_on_error: false
source "https://gem.repo1"
gem "myrack"
@@ -390,8 +390,8 @@ G
#{patchlevel_incorrect}
G
- expect(bundled_app_lock).not_to exist
- should_be_patchlevel_incorrect
+ expect(bundled_app_lock).to exist
+ should_ignore_patchlevel
end
end
@@ -481,7 +481,7 @@ G
should_be_engine_version_incorrect
end
- it "fails when patchlevel doesn't match" do
+ it "checks fine even when patchlevel doesn't match" do
install_gemfile <<-G
source "https://gem.repo1"
gem "myrack"
@@ -494,8 +494,8 @@ G
#{patchlevel_incorrect}
G
- bundle :check, raise_on_error: false
- should_be_patchlevel_incorrect
+ bundle :check
+ should_ignore_patchlevel
end
end
@@ -598,10 +598,10 @@ G
should_be_engine_version_incorrect
end
- it "fails when patchlevel doesn't match" do
+ it "updates fine even when patchlevel doesn't match" do
gemfile <<-G
- source "https://gem.repo1"
- gem "myrack"
+ source "https://gem.repo2"
+ gem "activesupport"
#{patchlevel_incorrect}
G
@@ -609,8 +609,9 @@ G
build_gem "activesupport", "3.0"
end
- bundle :update, all: true, raise_on_error: false
- should_be_patchlevel_incorrect
+ bundle :update, all: true
+ should_ignore_patchlevel
+ expect(the_bundle).to include_gems "activesupport 3.0"
end
end
@@ -646,7 +647,7 @@ G
expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s)
end
- it "fails if ruby version doesn't match", bundler: "< 3" do
+ it "fails if ruby version doesn't match" do
gemfile <<-G
source "https://gem.repo1"
gem "rails"
@@ -658,7 +659,7 @@ G
should_be_ruby_version_incorrect
end
- it "fails if engine doesn't match", bundler: "< 3" do
+ it "fails if engine doesn't match" do
gemfile <<-G
source "https://gem.repo1"
gem "rails"
@@ -670,7 +671,7 @@ G
should_be_engine_incorrect
end
- it "fails if engine version doesn't match", bundler: "< 3", jruby_only: true do
+ it "fails if engine version doesn't match", jruby_only: true do
gemfile <<-G
source "https://gem.repo1"
gem "rails"
@@ -682,19 +683,17 @@ G
should_be_engine_version_incorrect
end
- it "fails when patchlevel doesn't match", bundler: "< 3" do
+ it "prints path even when patchlevel doesn't match" do
gemfile <<-G
source "https://gem.repo1"
- gem "myrack"
+ gem "rails"
#{patchlevel_incorrect}
G
- update_repo2 do
- build_gem "activesupport", "3.0"
- end
- bundle "show rails", raise_on_error: false
- should_be_patchlevel_incorrect
+ bundle "show rails"
+ should_ignore_patchlevel
+ expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s)
end
end
@@ -766,7 +765,7 @@ G
should_be_engine_version_incorrect
end
- it "fails when patchlevel doesn't match" do
+ it "copies the .gem file to vendor/cache even when patchlevel doesn't match" do
gemfile <<-G
source "https://gem.repo1"
gem "myrack"
@@ -774,8 +773,9 @@ G
#{patchlevel_incorrect}
G
- bundle :cache, raise_on_error: false
- should_be_patchlevel_incorrect
+ bundle :cache
+ should_ignore_patchlevel
+ expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
end
end
@@ -847,7 +847,7 @@ G
should_be_engine_version_incorrect
end
- it "fails when patchlevel doesn't match" do
+ it "copies the .gem file to vendor/cache even when patchlevel doesn't match" do
gemfile <<-G
source "https://gem.repo1"
gem "myrack"
@@ -855,8 +855,9 @@ G
#{patchlevel_incorrect}
G
- bundle :cache, raise_on_error: false
- should_be_patchlevel_incorrect
+ bundle :cache
+ should_ignore_patchlevel
+ expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
end
end
@@ -926,7 +927,7 @@ G
should_be_engine_version_incorrect
end
- it "fails when patchlevel doesn't match" do
+ it "activates the correct gem even when patchlevel doesn't match" do
gemfile <<-G
source "https://gem.repo1"
gem "myrack"
@@ -934,15 +935,21 @@ G
#{patchlevel_incorrect}
G
- bundle "exec myrackup", raise_on_error: false
- should_be_patchlevel_incorrect
+ bundle "exec myrackup"
+ should_ignore_patchlevel
+ expect(out).to include("1.0.0")
end
end
- context "bundle console", bundler: "< 3" do
+ context "bundle console" do
before do
+ build_repo2 do
+ build_dummy_irb
+ end
+
install_gemfile <<-G
- source "https://gem.repo1"
+ source "https://gem.repo2"
+ gem "irb"
gem "myrack"
gem "activesupport", :group => :test
gem "myrack_middleware", :group => :development
@@ -950,14 +957,7 @@ G
end
it "starts IRB with the default group loaded when ruby version matches", :readline do
- gemfile <<-G
- source "https://gem.repo1"
- gem "myrack"
- gem "activesupport", :group => :test
- gem "myrack_middleware", :group => :development
-
- #{ruby_version_correct}
- G
+ gemfile gemfile + "\n\n#{ruby_version_correct}\n"
bundle "console" do |input, _, _|
input.puts("puts MYRACK")
@@ -967,14 +967,7 @@ G
end
it "starts IRB with the default group loaded when ruby version matches", :readline, :jruby_only do
- gemfile <<-G
- source "https://gem.repo1"
- gem "myrack"
- gem "activesupport", :group => :test
- gem "myrack_middleware", :group => :development
-
- #{ruby_version_correct_engineless}
- G
+ gemfile gemfile + "\n\n#{ruby_version_correct_engineless}\n"
bundle "console" do |input, _, _|
input.puts("puts MYRACK")
@@ -984,59 +977,35 @@ G
end
it "fails when ruby version doesn't match" do
- gemfile <<-G
- source "https://gem.repo1"
- gem "myrack"
- gem "activesupport", :group => :test
- gem "myrack_middleware", :group => :development
-
- #{ruby_version_incorrect}
- G
+ gemfile gemfile + "\n\n#{ruby_version_incorrect}\n"
bundle "console", raise_on_error: false
should_be_ruby_version_incorrect
end
it "fails when engine doesn't match" do
- gemfile <<-G
- source "https://gem.repo1"
- gem "myrack"
- gem "activesupport", :group => :test
- gem "myrack_middleware", :group => :development
-
- #{engine_incorrect}
- G
+ gemfile gemfile + "\n\n#{engine_incorrect}\n"
bundle "console", raise_on_error: false
should_be_engine_incorrect
end
it "fails when engine version doesn't match", :jruby_only do
- gemfile <<-G
- source "https://gem.repo1"
- gem "myrack"
- gem "activesupport", :group => :test
- gem "myrack_middleware", :group => :development
-
- #{engine_version_incorrect}
- G
+ gemfile gemfile + "\n\n#{engine_version_incorrect}\n"
bundle "console", raise_on_error: false
should_be_engine_version_incorrect
end
- it "fails when patchlevel doesn't match" do
- gemfile <<-G
- source "https://gem.repo1"
- gem "myrack"
- gem "activesupport", :group => :test
- gem "myrack_middleware", :group => :development
+ it "starts IRB with the default group loaded even when patchlevel doesn't match", :readline do
+ gemfile gemfile + "\n\n#{patchlevel_incorrect}\n"
- #{patchlevel_incorrect}
- G
-
- bundle "console", raise_on_error: false
- should_be_patchlevel_incorrect
+ bundle "console" do |input, _, _|
+ input.puts("puts MYRACK")
+ input.puts("exit")
+ end
+ should_ignore_patchlevel
+ expect(out).to include("0.9.1")
end
end
@@ -1132,7 +1101,7 @@ G
should_be_engine_version_incorrect
end
- it "fails when patchlevel doesn't match" do
+ it "makes a Gemfile.lock even when patchlevel doesn't match" do
install_gemfile <<-G, raise_on_error: false
source "https://gem.repo1"
gem "yard"
@@ -1143,10 +1112,10 @@ G
FileUtils.rm(bundled_app_lock)
- ruby "require 'bundler/setup'", env: { "BUNDLER_VERSION" => Bundler::VERSION }, raise_on_error: false
+ ruby "require 'bundler/setup'", env: { "BUNDLER_VERSION" => Bundler::VERSION }
- expect(bundled_app_lock).not_to exist
- should_be_patchlevel_incorrect
+ should_ignore_patchlevel
+ expect(bundled_app_lock).to exist
end
end
@@ -1180,7 +1149,7 @@ G
bundle "outdated", raise_on_error: false
expected_output = <<~TABLE.gsub("x", "\\\h").tr(".", "\.").strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
activesupport 2.3.5 3.0 = 2.3.5 default
foo 1.0 xxxxxxx 1.0 xxxxxxx >= 0 default
TABLE
@@ -1206,7 +1175,7 @@ G
bundle "outdated", raise_on_error: false
expected_output = <<~TABLE.gsub("x", "\\\h").tr(".", "\.").strip
- Gem Current Latest Requested Groups
+ Gem Current Latest Requested Groups Release Date
activesupport 2.3.5 3.0 = 2.3.5 default
foo 1.0 xxxxxxx 1.0 xxxxxxx >= 0 default
TABLE
@@ -1268,7 +1237,7 @@ G
should_be_engine_version_incorrect
end
- it "fails when the patchlevel doesn't match", :jruby_only do
+ it "reports outdated gems even when patchlevel doesn't match" do
update_repo2 do
build_gem "activesupport", "3.0"
update_git "foo", path: lib_path("foo")
@@ -1283,25 +1252,9 @@ G
G
bundle "outdated", raise_on_error: false
- should_be_patchlevel_incorrect
- end
-
- it "fails when the patchlevel is a fixnum", :jruby_only do
- update_repo2 do
- build_gem "activesupport", "3.0"
- update_git "foo", path: lib_path("foo")
- end
-
- gemfile <<-G
- source "https://gem.repo2"
- gem "activesupport", "2.3.5"
- gem "foo", :git => "#{lib_path("foo")}"
-
- #{patchlevel_fixnum}
- G
-
- bundle "outdated", raise_on_error: false
- should_be_patchlevel_fixnum
+ expect(err).not_to include("patchlevel")
+ expect(out).to include("activesupport")
+ expect(out).to include("foo")
end
end
end
diff --git a/spec/bundler/commands/post_bundle_message_spec.rb b/spec/bundler/commands/post_bundle_message_spec.rb
index 8671504b25..088fc29fe1 100644
--- a/spec/bundler/commands/post_bundle_message_spec.rb
+++ b/spec/bundler/commands/post_bundle_message_spec.rb
@@ -18,162 +18,140 @@ RSpec.describe "post bundle message" do
let(:bundle_show_path_message) { "Bundled gems are installed into `#{bundle_path}`" }
let(:bundle_complete_message) { "Bundle complete!" }
let(:bundle_updated_message) { "Bundle updated!" }
- let(:installed_gems_stats) { "4 Gemfile dependencies, 5 gems now installed." }
- let(:bundle_show_message) { Bundler::VERSION.split(".").first.to_i < 3 ? bundle_show_system_message : bundle_show_path_message }
+ let(:installed_gems_stats) { "4 Gemfile dependencies, 4 gems now installed." }
+
+ describe "when installing to system gems" do
+ before do
+ bundle_config "path.system true"
+ end
- describe "for fresh bundle install" do
it "shows proper messages according to the configured groups" do
bundle :install
- expect(out).to include(bundle_show_message)
+ expect(out).to include(bundle_show_system_message)
expect(out).not_to include("Gems in the group")
expect(out).to include(bundle_complete_message)
expect(out).to include(installed_gems_stats)
- bundle "config set --local without emo"
+ bundle_config "without emo"
bundle :install
- expect(out).to include(bundle_show_message)
+ expect(out).to include(bundle_show_system_message)
expect(out).to include("Gems in the group 'emo' were not installed")
expect(out).to include(bundle_complete_message)
expect(out).to include(installed_gems_stats)
- bundle "config set --local without emo test"
+ bundle_config "without emo test"
bundle :install
- expect(out).to include(bundle_show_message)
+ expect(out).to include(bundle_show_system_message)
expect(out).to include("Gems in the groups 'emo' and 'test' were not installed")
expect(out).to include(bundle_complete_message)
- expect(out).to include("4 Gemfile dependencies, 3 gems now installed.")
+ expect(out).to include("4 Gemfile dependencies, 2 gems now installed.")
- bundle "config set --local without emo obama test"
+ bundle_config "without emo obama test"
bundle :install
- expect(out).to include(bundle_show_message)
+ expect(out).to include(bundle_show_system_message)
expect(out).to include("Gems in the groups 'emo', 'obama' and 'test' were not installed")
expect(out).to include(bundle_complete_message)
- expect(out).to include("4 Gemfile dependencies, 2 gems now installed.")
+ expect(out).to include("4 Gemfile dependencies, 1 gem now installed.")
end
- describe "with `path` configured" do
- let(:bundle_path) { "./vendor" }
-
- it "shows proper messages according to the configured groups" do
- bundle "config set --local path vendor"
- bundle :install
- expect(out).to include(bundle_show_path_message)
- expect(out).to_not include("Gems in the group")
- expect(out).to include(bundle_complete_message)
-
- bundle "config set --local path vendor"
- bundle "config set --local without emo"
- bundle :install
- expect(out).to include(bundle_show_path_message)
- expect(out).to include("Gems in the group 'emo' were not installed")
- expect(out).to include(bundle_complete_message)
-
- bundle "config set --local path vendor"
- bundle "config set --local without emo test"
- bundle :install
- expect(out).to include(bundle_show_path_message)
- expect(out).to include("Gems in the groups 'emo' and 'test' were not installed")
- expect(out).to include(bundle_complete_message)
-
- bundle "config set --local path vendor"
- bundle "config set --local without emo obama test"
- bundle :install
- expect(out).to include(bundle_show_path_message)
- expect(out).to include("Gems in the groups 'emo', 'obama' and 'test' were not installed")
- expect(out).to include(bundle_complete_message)
- end
- end
-
- describe "with an absolute `path` inside the cwd configured" do
- let(:bundle_path) { bundled_app("cache") }
-
- it "shows proper messages according to the configured groups" do
- bundle "config set --local path #{bundle_path}"
- bundle :install
- expect(out).to include("Bundled gems are installed into `./cache`")
- expect(out).to_not include("Gems in the group")
+ describe "for second bundle install run" do
+ it "without any options" do
+ 2.times { bundle :install }
+ expect(out).to include(bundle_show_system_message)
+ expect(out).to_not include("Gems in the groups")
expect(out).to include(bundle_complete_message)
+ expect(out).to include(installed_gems_stats)
end
end
+ end
- describe "with `path` configured to an absolute path outside the cwd" do
- let(:bundle_path) { tmp("not_bundled_app") }
+ describe "with `path` configured" do
+ let(:bundle_path) { "./vendor" }
- it "shows proper messages according to the configured groups" do
- bundle "config set --local path #{bundle_path}"
- bundle :install
- expect(out).to include("Bundled gems are installed into `#{tmp("not_bundled_app")}`")
- expect(out).to_not include("Gems in the group")
- expect(out).to include(bundle_complete_message)
- end
- end
+ it "shows proper messages according to the configured groups" do
+ bundle_config "path vendor"
+ bundle :install
+ expect(out).to include(bundle_show_path_message)
+ expect(out).to_not include("Gems in the group")
+ expect(out).to include(bundle_complete_message)
- describe "with misspelled or non-existent gem name" do
- before do
- bundle "config set force_ruby_platform true"
- end
+ bundle_config "path vendor"
+ bundle_config "without emo"
+ bundle :install
+ expect(out).to include(bundle_show_path_message)
+ expect(out).to include("Gems in the group 'emo' were not installed")
+ expect(out).to include(bundle_complete_message)
- it "should report a helpful error message" do
- install_gemfile <<-G, raise_on_error: false
- source "https://gem.repo1"
- gem "myrack"
- gem "not-a-gem", :group => :development
- G
- expect(err).to include <<-EOS.strip
-Could not find gem 'not-a-gem' in rubygems repository https://gem.repo1/ or installed locally.
- EOS
- end
+ bundle_config "path vendor"
+ bundle_config "without emo test"
+ bundle :install
+ expect(out).to include(bundle_show_path_message)
+ expect(out).to include("Gems in the groups 'emo' and 'test' were not installed")
+ expect(out).to include(bundle_complete_message)
- it "should report a helpful error message with reference to cache if available" do
- install_gemfile <<-G
- source "https://gem.repo1"
- gem "myrack"
- G
- bundle :cache
- expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
- install_gemfile <<-G, raise_on_error: false
- source "https://gem.repo1"
- gem "myrack"
- gem "not-a-gem", :group => :development
- G
- expect(err).to include("Could not find gem 'not-a-gem' in").
- and include("or in gems cached in vendor/cache.")
- end
+ bundle_config "path vendor"
+ bundle_config "without emo obama test"
+ bundle :install
+ expect(out).to include(bundle_show_path_message)
+ expect(out).to include("Gems in the groups 'emo', 'obama' and 'test' were not installed")
+ expect(out).to include(bundle_complete_message)
end
end
- describe "for second bundle install run", bundler: "< 3" do
- it "without any options" do
- 2.times { bundle :install }
- expect(out).to include(bundle_show_message)
- expect(out).to_not include("Gems in the groups")
- expect(out).to include(bundle_complete_message)
- expect(out).to include(installed_gems_stats)
- end
+ describe "with an absolute `path` inside the cwd configured" do
+ let(:bundle_path) { bundled_app("cache") }
- it "with --without one group" do
- bundle "install --without emo"
+ it "shows proper messages according to the configured groups" do
+ bundle_config "path #{bundle_path}"
bundle :install
- expect(out).to include(bundle_show_message)
- expect(out).to include("Gems in the group 'emo' were not installed")
+ expect(out).to include("Bundled gems are installed into `./cache`")
+ expect(out).to_not include("Gems in the group")
expect(out).to include(bundle_complete_message)
- expect(out).to include(installed_gems_stats)
end
+ end
- it "with --without two groups" do
- bundle "install --without emo test"
+ describe "with `path` configured to an absolute path outside the cwd" do
+ let(:bundle_path) { tmp("not_bundled_app") }
+
+ it "shows proper messages according to the configured groups" do
+ bundle_config "path #{bundle_path}"
bundle :install
- expect(out).to include(bundle_show_message)
- expect(out).to include("Gems in the groups 'emo' and 'test' were not installed")
+ expect(out).to include("Bundled gems are installed into `#{tmp("not_bundled_app")}`")
+ expect(out).to_not include("Gems in the group")
expect(out).to include(bundle_complete_message)
end
+ end
- it "with --without more groups" do
- bundle "install --without emo obama test"
- bundle :install
- expect(out).to include(bundle_show_message)
- expect(out).to include("Gems in the groups 'emo', 'obama' and 'test' were not installed")
- expect(out).to include(bundle_complete_message)
+ describe "with misspelled or non-existent gem name" do
+ before do
+ bundle_config "force_ruby_platform true"
+ end
+
+ it "should report a helpful error message" do
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
+ gem "myrack"
+ gem "not-a-gem", :group => :development
+ G
+ expect(err).to include <<~EOS.strip
+ Could not find gem 'not-a-gem' in rubygems repository https://gem.repo1/ or installed locally.
+ EOS
+ end
+
+ it "should report a helpful error message with reference to cache if available" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+ bundle :cache
+ expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
+ install_gemfile <<-G, raise_on_error: false
+ source "https://gem.repo1"
+ gem "myrack"
+ gem "not-a-gem", :group => :development
+ G
+ expect(err).to include("Could not find gem 'not-a-gem' in").
+ and include("or in gems cached in vendor/cache.")
end
end
@@ -183,19 +161,19 @@ Could not find gem 'not-a-gem' in rubygems repository https://gem.repo1/ or inst
expect(out).not_to include("Gems in the groups")
expect(out).to include(bundle_updated_message)
- bundle "config set --local without emo"
+ bundle_config "without emo"
bundle :install
bundle :update, all: true
expect(out).to include("Gems in the group 'emo' were not updated")
expect(out).to include(bundle_updated_message)
- bundle "config set --local without emo test"
+ bundle_config "without emo test"
bundle :install
bundle :update, all: true
expect(out).to include("Gems in the groups 'emo' and 'test' were not updated")
expect(out).to include(bundle_updated_message)
- bundle "config set --local without emo obama test"
+ bundle_config "without emo obama test"
bundle :install
bundle :update, all: true
expect(out).to include("Gems in the groups 'emo', 'obama' and 'test' were not updated")
diff --git a/spec/bundler/commands/pristine_spec.rb b/spec/bundler/commands/pristine_spec.rb
index 547aa12b6c..5f80b9e534 100644
--- a/spec/bundler/commands/pristine_spec.rb
+++ b/spec/bundler/commands/pristine_spec.rb
@@ -49,13 +49,7 @@ RSpec.describe "bundle pristine" do
bundle "pristine"
bundle "-v"
- expected = if Bundler::VERSION < "3.0"
- "Bundler version"
- else
- Bundler::VERSION
- end
-
- expect(out).to start_with(expected)
+ expect(out).to end_with(Bundler::VERSION)
end
end
@@ -85,7 +79,7 @@ RSpec.describe "bundle pristine" do
it "displays warning and ignores changes when a local config exists" do
spec = find_spec("foo")
- bundle "config set local.#{spec.name} #{lib_path(spec.name)}"
+ bundle_config "local.#{spec.name} #{lib_path(spec.name)}"
changes_txt = Pathname.new(spec.full_gem_path).join("lib/changes.txt")
FileUtils.touch(changes_txt)
@@ -95,6 +89,66 @@ RSpec.describe "bundle pristine" do
expect(changes_txt).to be_file
expect(err).to include("Cannot pristine #{spec.name} (#{spec.version}#{spec.git_version}). Gem is locally overridden.")
end
+
+ it "doesn't run multiple git processes for the same repository" do
+ nested_gems = [
+ "actioncable",
+ "actionmailer",
+ "actionpack",
+ "actionview",
+ "activejob",
+ "activemodel",
+ "activerecord",
+ "activestorage",
+ "activesupport",
+ "railties",
+ ]
+
+ build_repo2 do
+ nested_gems.each do |gem|
+ build_lib gem, path: lib_path("rails/#{gem}")
+ end
+
+ build_git "rails", path: lib_path("rails") do |s|
+ nested_gems.each do |gem|
+ s.add_dependency gem
+ end
+ end
+ end
+
+ install_gemfile <<-G
+ source 'https://rubygems.org'
+
+ git "#{lib_path("rails")}" do
+ gem "rails"
+ gem "actioncable"
+ gem "actionmailer"
+ gem "actionpack"
+ gem "actionview"
+ gem "activejob"
+ gem "activemodel"
+ gem "activerecord"
+ gem "activestorage"
+ gem "activesupport"
+ gem "railties"
+ end
+ G
+
+ changed_files = []
+ diff = "#Pristine spec changes"
+
+ nested_gems.each do |gem|
+ spec = find_spec(gem)
+ changed_files << Pathname.new(spec.full_gem_path).join("lib/#{gem}.rb")
+ File.open(changed_files.last, "a") {|f| f.puts diff }
+ end
+
+ bundle "pristine"
+
+ changed_files.each do |changed_file|
+ expect(File.read(changed_file)).to_not include(diff)
+ end
+ end
end
context "when sourced from gemspec" do
@@ -173,7 +227,7 @@ RSpec.describe "bundle pristine" do
let(:very_simple_binary) { find_spec("very_simple_binary") }
let(:c_ext_dir) { Pathname.new(very_simple_binary.full_gem_path).join("ext") }
let(:build_opt) { "--with-ext-lib=#{c_ext_dir}" }
- before { bundle "config set build.very_simple_binary -- #{build_opt}" }
+ before { bundle_config "build.very_simple_binary -- #{build_opt}" }
# This just verifies that the generated Makefile from the c_ext gem makes
# use of the build_args from the bundle config
@@ -190,7 +244,7 @@ RSpec.describe "bundle pristine" do
let(:git_with_ext) { find_spec("git_with_ext") }
let(:c_ext_dir) { Pathname.new(git_with_ext.full_gem_path).join("ext") }
let(:build_opt) { "--with-ext-lib=#{c_ext_dir}" }
- before { bundle "config set build.git_with_ext -- #{build_opt}" }
+ before { bundle_config "build.git_with_ext -- #{build_opt}" }
# This just verifies that the generated Makefile from the c_ext gem makes
# use of the build_args from the bundle config
diff --git a/spec/bundler/commands/remove_spec.rb b/spec/bundler/commands/remove_spec.rb
index 84505169ca..8a2e6778ea 100644
--- a/spec/bundler/commands/remove_spec.rb
+++ b/spec/bundler/commands/remove_spec.rb
@@ -43,21 +43,6 @@ RSpec.describe "bundle remove" do
end
end
- context "when --install flag is specified", bundler: "< 3" do
- it "removes gems from .bundle" do
- gemfile <<-G
- source "https://gem.repo1"
-
- gem "myrack"
- G
-
- bundle "remove myrack --install"
-
- expect(out).to include("myrack was removed.")
- expect(the_bundle).to_not include_gems "myrack"
- end
- end
-
describe "remove single gem from gemfile" do
context "when gem is present in gemfile" do
it "shows success for removed gem" do
diff --git a/spec/bundler/commands/show_spec.rb b/spec/bundler/commands/show_spec.rb
index 7f32d0563b..d0d55ffbb9 100644
--- a/spec/bundler/commands/show_spec.rb
+++ b/spec/bundler/commands/show_spec.rb
@@ -1,10 +1,12 @@
# frozen_string_literal: true
-RSpec.describe "bundle show", bundler: "< 3" do
+RSpec.describe "bundle show" do
context "with a standard Gemfile" do
before :each do
+ build_repo2
+
install_gemfile <<-G
- source "https://gem.repo1"
+ source "https://gem.repo2"
gem "rails"
G
end
@@ -35,12 +37,12 @@ RSpec.describe "bundle show", bundler: "< 3" do
expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s)
end
- it "warns if path no longer exists on disk" do
- FileUtils.rm_rf(default_bundle_path("gems", "rails-2.3.2"))
+ it "warns if specification is installed, but path does not exist on disk" do
+ FileUtils.rm_r(default_bundle_path("gems", "rails-2.3.2"))
bundle "show rails"
- expect(err).to match(/has been deleted/i)
+ expect(err).to match(/is missing/i)
expect(err).to match(default_bundle_path("gems", "rails-2.3.2").to_s)
end
@@ -86,6 +88,24 @@ RSpec.describe "bundle show", bundler: "< 3" do
\tStatus: Up to date
MSG
end
+
+ it "includes up to date status in summary of gems" do
+ update_repo2 do
+ build_gem "rails", "3.0.0"
+ end
+
+ bundle "show --verbose"
+
+ expect(out).to include <<~MSG
+ * rails (2.3.2)
+ \tSummary: This is just a fake gem for testing
+ \tHomepage: http://example.com
+ \tStatus: Outdated - 2.3.2 < 3.0.0
+ MSG
+
+ # check lockfile is not accidentally updated
+ expect(lockfile).to include("actionmailer (2.3.2)")
+ end
end
context "with a git repo in the Gemfile" do
@@ -159,7 +179,7 @@ RSpec.describe "bundle show", bundler: "< 3" do
gem "foo"
G
- bundle "config set auto_install 1"
+ bundle_config "auto_install 1"
bundle :show
expect(out).to include("Installing foo 1.0")
end
@@ -190,35 +210,8 @@ RSpec.describe "bundle show", bundler: "< 3" do
expect(err).to include("Could not find gem '#{invalid_regexp}'.")
end
end
-
- context "--outdated option" do
- # Regression test for https://github.com/rubygems/bundler/issues/5375
- before do
- build_repo2
- end
-
- it "doesn't update gems to newer versions" do
- install_gemfile <<-G
- source "https://gem.repo2"
- gem "rails"
- G
-
- expect(the_bundle).to include_gem("rails 2.3.2")
-
- update_repo2 do
- build_gem "rails", "3.0.0" do |s|
- s.executables = "rails"
- end
- end
-
- bundle "show --outdated"
-
- bundle "install"
- expect(the_bundle).to include_gem("rails 2.3.2")
- end
- end
end
-RSpec.describe "bundle show", bundler: "3" do
+RSpec.describe "bundle show", bundler: "5" do
pending "shows a friendly error about the command removal"
end
diff --git a/spec/bundler/commands/ssl_spec.rb b/spec/bundler/commands/ssl_spec.rb
new file mode 100644
index 0000000000..4220731b69
--- /dev/null
+++ b/spec/bundler/commands/ssl_spec.rb
@@ -0,0 +1,373 @@
+# frozen_string_literal: true
+
+require "bundler/cli"
+require "bundler/cli/doctor"
+require "bundler/cli/doctor/ssl"
+require_relative "../support/artifice/helpers/artifice"
+require "bundler/vendored_persistent.rb"
+
+RSpec.describe "bundle doctor ssl" do
+ before(:each) do
+ require_rack_test
+ require_relative "../support/artifice/helpers/endpoint"
+
+ @dummy_endpoint = Class.new(Endpoint) do
+ get "/" do
+ end
+ end
+
+ @previous_ui = Bundler.ui
+ Bundler.ui = Bundler::UI::Shell.new
+ Bundler.ui.level = "info"
+
+ @previous_client = Gem::Request::ConnectionPools.client
+ Artifice.activate_with(@dummy_endpoint)
+ Gem::Request::ConnectionPools.client = Gem::Net::HTTP
+ end
+
+ after(:each) do
+ Bundler.ui = @previous_ui
+ Artifice.deactivate
+ Gem::Request::ConnectionPools.client = @previous_client
+ end
+
+ context "when a diagnostic fails" do
+ it "prints the diagnostic when openssl can't be loaded" do
+ subject = Bundler::CLI::Doctor::SSL.new({})
+ allow(subject).to receive(:require).with("openssl").and_raise(LoadError)
+
+ expected_err = <<~MSG
+ Oh no! Your Ruby doesn't have OpenSSL, so it can't connect to rubygems.org.
+ You'll need to recompile or reinstall Ruby with OpenSSL support and try again.
+ MSG
+
+ expect { subject.run }.to output("").to_stdout.and output(expected_err).to_stderr
+ end
+
+ it "fails due to certificate verification", :ruby_repo do
+ net_http = Class.new(Artifice::Net::HTTP) do
+ def connect
+ raise OpenSSL::SSL::SSLError, "certificate verify failed"
+ end
+ end
+
+ Artifice.replace_net_http(net_http)
+ Gem::Request::ConnectionPools.client = net_http
+ Gem::RemoteFetcher.fetcher.close_all
+
+ expected_out = <<~MSG
+ Here's your OpenSSL environment:
+
+ OpenSSL: #{OpenSSL::VERSION}
+ Compiled with: #{OpenSSL::OPENSSL_VERSION}
+ Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
+
+ Trying connections to https://rubygems.org:
+ MSG
+
+ expected_err = <<~MSG
+ Bundler: failed (certificate verification)
+ RubyGems: failed (certificate verification)
+ Ruby net/http: failed
+
+ Unfortunately, this Ruby can't connect to rubygems.org.
+
+ Below affect only Ruby net/http connections:
+ SSL_CERT_FILE: exists #{OpenSSL::X509::DEFAULT_CERT_FILE}
+ SSL_CERT_DIR: exists #{OpenSSL::X509::DEFAULT_CERT_DIR}
+
+ Your Ruby can't connect to rubygems.org because you are missing the certificate files OpenSSL needs to verify you are connecting to the genuine rubygems.org servers.
+
+ MSG
+
+ subject = Bundler::CLI::Doctor::SSL.new({})
+ expect { subject.run }.to output(expected_out).to_stdout.and output(expected_err).to_stderr
+ end
+
+ it "fails due to a too old tls version" do
+ subject = Bundler::CLI::Doctor::SSL.new({})
+
+ net_http = Class.new(Artifice::Net::HTTP) do
+ def connect
+ raise OpenSSL::SSL::SSLError, "read server hello A"
+ end
+ end
+
+ Artifice.replace_net_http(net_http)
+ Gem::Request::ConnectionPools.client = Gem::Net::HTTP
+ Gem::RemoteFetcher.fetcher.close_all
+
+ expected_out = <<~MSG
+ Here's your OpenSSL environment:
+
+ OpenSSL: #{OpenSSL::VERSION}
+ Compiled with: #{OpenSSL::OPENSSL_VERSION}
+ Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
+
+ Trying connections to https://rubygems.org:
+ MSG
+
+ expected_err = <<~MSG
+ Bundler: failed (SSL/TLS protocol version mismatch)
+ RubyGems: failed (SSL/TLS protocol version mismatch)
+ Ruby net/http: failed
+
+ Unfortunately, this Ruby can't connect to rubygems.org.
+
+ Your Ruby can't connect to rubygems.org because your version of OpenSSL is too old.
+ You'll need to upgrade your OpenSSL install and/or recompile Ruby to use a newer OpenSSL.
+
+ MSG
+
+ expect { subject.run }.to output(expected_out).to_stdout.and output(expected_err).to_stderr
+ end
+
+ it "fails due to unsupported tls 1.3 version" do
+ net_http = Class.new(Artifice::Net::HTTP) do
+ def connect
+ raise OpenSSL::SSL::SSLError, "read server hello A"
+ end
+ end
+
+ Artifice.replace_net_http(net_http)
+ Gem::Request::ConnectionPools.client = net_http
+ Gem::RemoteFetcher.fetcher.close_all
+
+ expected_out = <<~MSG
+ Here's your OpenSSL environment:
+
+ OpenSSL: #{OpenSSL::VERSION}
+ Compiled with: #{OpenSSL::OPENSSL_VERSION}
+ Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
+
+ Trying connections to https://rubygems.org:
+ MSG
+
+ expected_err = <<~MSG
+ Bundler: failed (SSL/TLS protocol version mismatch)
+ RubyGems: failed (SSL/TLS protocol version mismatch)
+ Ruby net/http: failed
+
+ Unfortunately, this Ruby can't connect to rubygems.org.
+
+ Your Ruby can't connect to rubygems.org because TLS1_3 isn't supported yet.
+
+ MSG
+
+ subject = Bundler::CLI::Doctor::SSL.new("tls-version": "1.3")
+ expect { subject.run }.to output(expected_out).to_stdout.and output(expected_err).to_stderr
+ end
+
+ it "fails due to a bundler and rubygems connection error" do
+ endpoint = Class.new(Endpoint) do
+ get "/" do
+ raise OpenSSL::SSL::SSLError, "read server hello A"
+ end
+ end
+
+ Artifice.activate_with(endpoint)
+ Gem::Request::ConnectionPools.client = Gem::Net::HTTP
+
+ expected_out = <<~MSG
+ Here's your OpenSSL environment:
+
+ OpenSSL: #{OpenSSL::VERSION}
+ Compiled with: #{OpenSSL::OPENSSL_VERSION}
+ Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
+
+ Trying connections to https://rubygems.org:
+ Ruby net/http: success
+
+ For some reason, your Ruby installation can connect to rubygems.org, but neither RubyGems nor Bundler can.
+ The most likely fix is to manually upgrade RubyGems by following the instructions at http://ruby.to/ssl-check-failed.
+ After you've done that, run `gem install bundler` to upgrade Bundler, and then run this script again to make sure everything worked. ❣
+
+ MSG
+
+ expected_err = <<~MSG
+ Bundler: failed (SSL/TLS protocol version mismatch)
+ RubyGems: failed (SSL/TLS protocol version mismatch)
+ MSG
+
+ subject = Bundler::CLI::Doctor::SSL.new({})
+ expect { subject.run }.to output(expected_out).to_stdout.and output(expected_err).to_stderr
+ end
+
+ it "fails due to a bundler connection error" do
+ endpoint = Class.new(Endpoint) do
+ get "/" do
+ if request.user_agent.include?("bundler")
+ raise OpenSSL::SSL::SSLError, "read server hello A"
+ end
+ end
+ end
+
+ Artifice.activate_with(endpoint)
+ Gem::Request::ConnectionPools.client = Gem::Net::HTTP
+
+ expected_out = <<~MSG
+ Here's your OpenSSL environment:
+
+ OpenSSL: #{OpenSSL::VERSION}
+ Compiled with: #{OpenSSL::OPENSSL_VERSION}
+ Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
+
+ Trying connections to https://rubygems.org:
+ RubyGems: success
+ Ruby net/http: success
+
+ Although your Ruby installation and RubyGems can both connect to rubygems.org, Bundler is having trouble.
+ The most likely way to fix this is to upgrade Bundler by running `gem install bundler`.
+ Run this script again after doing that to make sure everything is all set.
+ If you're still having trouble, check out the troubleshooting guide at http://ruby.to/ssl-check-failed.
+
+ MSG
+
+ expected_err = <<~MSG
+ Bundler: failed (SSL/TLS protocol version mismatch)
+ MSG
+
+ subject = Bundler::CLI::Doctor::SSL.new({})
+ expect { subject.run }.to output(expected_out).to_stdout.and output(expected_err).to_stderr
+ end
+
+ it "fails due to a RubyGems connection error" do
+ endpoint = Class.new(Endpoint) do
+ get "/" do
+ if request.user_agent.include?("Ruby, RubyGems")
+ raise OpenSSL::SSL::SSLError, "read server hello A"
+ end
+ end
+ end
+
+ Artifice.activate_with(endpoint)
+ Gem::Request::ConnectionPools.client = Gem::Net::HTTP
+
+ expected_out = <<~MSG
+ Here's your OpenSSL environment:
+
+ OpenSSL: #{OpenSSL::VERSION}
+ Compiled with: #{OpenSSL::OPENSSL_VERSION}
+ Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
+
+ Trying connections to https://rubygems.org:
+ Bundler: success
+ Ruby net/http: success
+
+ It looks like Ruby and Bundler can connect to rubygems.org, but RubyGems itself cannot.
+ You can likely solve this by manually downloading and installing a RubyGems update.
+ Visit http://ruby.to/ssl-check-failed for instructions on how to manually upgrade RubyGems.
+
+ MSG
+
+ expected_err = <<~MSG
+ RubyGems: failed (SSL/TLS protocol version mismatch)
+ MSG
+
+ subject = Bundler::CLI::Doctor::SSL.new({})
+ expect { subject.run }.to output(expected_out).to_stdout.and output(expected_err).to_stderr
+ end
+ end
+
+ context "when no diagnostic fails" do
+ it "prints the SSL environment" do
+ expected_out = <<~MSG
+ Here's your OpenSSL environment:
+
+ OpenSSL: #{OpenSSL::VERSION}
+ Compiled with: #{OpenSSL::OPENSSL_VERSION}
+ Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
+
+ Trying connections to https://rubygems.org:
+ Bundler: success
+ RubyGems: success
+ Ruby net/http: success
+
+ Hooray! This Ruby can connect to rubygems.org.
+ You are all set to use Bundler and RubyGems.
+
+ MSG
+
+ subject = Bundler::CLI::Doctor::SSL.new({})
+ expect { subject.run }.to output(expected_out).to_stdout.and output("").to_stderr
+ end
+
+ it "uses the tls_version verify mode and host when given as option" do
+ net_http = Class.new(Artifice::Net::HTTP) do
+ class << self
+ attr_accessor :verify_mode, :min_version, :max_version
+ end
+
+ def connect
+ self.class.verify_mode = verify_mode
+ self.class.min_version = min_version
+ self.class.max_version = max_version
+
+ super
+ end
+ end
+
+ net_http.endpoint = @dummy_endpoint
+ Artifice.replace_net_http(net_http)
+ Gem::Request::ConnectionPools.client = net_http
+ Gem::RemoteFetcher.fetcher.close_all
+
+ expected_out = <<~MSG
+ Here's your OpenSSL environment:
+
+ OpenSSL: #{OpenSSL::VERSION}
+ Compiled with: #{OpenSSL::OPENSSL_VERSION}
+ Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
+
+ Trying connections to https://example.org:
+ Bundler: success
+ RubyGems: success
+ Ruby net/http: success
+
+ Hooray! This Ruby can connect to example.org.
+ You are all set to use Bundler and RubyGems.
+
+ MSG
+
+ subject = Bundler::CLI::Doctor::SSL.new("tls-version": "1.3", "verify-mode": :none, host: "example.org")
+ expect { subject.run }.to output(expected_out).to_stdout.and output("").to_stderr
+ expect(net_http.verify_mode).to eq(0)
+ expect(net_http.min_version.to_s).to eq("TLS1_3")
+ expect(net_http.max_version.to_s).to eq("TLS1_3")
+ end
+
+ it "warns when TLS1.2 is not supported" do
+ expected_out = <<~MSG
+ Here's your OpenSSL environment:
+
+ OpenSSL: #{OpenSSL::VERSION}
+ Compiled with: #{OpenSSL::OPENSSL_VERSION}
+ Loaded with: #{OpenSSL::OPENSSL_LIBRARY_VERSION}
+
+ Trying connections to https://rubygems.org:
+ Bundler: success
+ RubyGems: success
+ Ruby net/http: success
+
+ Hooray! This Ruby can connect to rubygems.org.
+ You are all set to use Bundler and RubyGems.
+
+ MSG
+
+ expected_err = <<~MSG
+
+ WARNING: Although your Ruby can connect to rubygems.org today, your OpenSSL is very old!
+ WARNING: You will need to upgrade OpenSSL to use rubygems.org.
+
+ MSG
+
+ previous_version = OpenSSL::SSL::TLS1_2_VERSION
+ OpenSSL::SSL.send(:remove_const, :TLS1_2_VERSION)
+
+ subject = Bundler::CLI::Doctor::SSL.new({})
+ expect { subject.run }.to output(expected_out).to_stdout.and output(expected_err).to_stderr
+ ensure
+ OpenSSL::SSL.const_set(:TLS1_2_VERSION, previous_version)
+ end
+ end
+end
diff --git a/spec/bundler/commands/update_spec.rb b/spec/bundler/commands/update_spec.rb
index d28c74ed62..03a3786d80 100644
--- a/spec/bundler/commands/update_spec.rb
+++ b/spec/bundler/commands/update_spec.rb
@@ -39,6 +39,23 @@ RSpec.describe "bundle update" do
end
end
+ describe "with --verbose" do
+ before do
+ build_repo2
+
+ install_gemfile <<~G
+ source "https://gem.repo2"
+ gem "myrack"
+ G
+ end
+
+ it "logs the reason for re-resolving" do
+ bundle "update --verbose"
+ expect(out).not_to include("Found changes from the lockfile")
+ expect(out).to include("Re-resolving dependencies because bundler is unlocking")
+ end
+ end
+
describe "with --all" do
before do
build_repo2
@@ -80,7 +97,7 @@ RSpec.describe "bundle update" do
end
describe "with --gemfile" do
- it "creates lock files based on the Gemfile name" do
+ it "creates lockfiles based on the Gemfile name" do
gemfile bundled_app("OmgFile"), <<-G
source "https://gem.repo1"
gem "myrack", "1.0"
@@ -93,7 +110,7 @@ RSpec.describe "bundle update" do
end
context "when update_requires_all_flag is set" do
- before { bundle "config set update_requires_all_flag true" }
+ before { bundle_config "update_requires_all_flag true" }
it "errors when passed nothing" do
install_gemfile "source 'https://gem.repo1'"
@@ -176,7 +193,7 @@ RSpec.describe "bundle update" do
end
it "should suggest alternatives" do
bundle "update platformspecific", raise_on_error: false
- expect(err).to include "Did you mean platform_specific?"
+ expect(err).to include "Did you mean 'platform_specific'?"
end
end
@@ -230,7 +247,7 @@ RSpec.describe "bundle update" do
expect(the_bundle).to include_gems("slim 3.0.9", "slim-rails 3.1.3", "slim_lint 0.16.1")
- update_repo4 do
+ build_repo4 do
build_gem "slim", "4.0.0" do |s|
s.add_dependency "tilt", [">= 2.0.6", "< 2.1"]
end
@@ -296,7 +313,7 @@ RSpec.describe "bundle update" do
country_select
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
previous_lockfile = lockfile
@@ -357,7 +374,7 @@ RSpec.describe "bundle update" do
quickbooks-ruby
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "update --conservative --verbose"
@@ -418,7 +435,7 @@ RSpec.describe "bundle update" do
sneakers
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "update --verbose"
@@ -428,6 +445,115 @@ RSpec.describe "bundle update" do
expect(out).to include("Installing sneakers 2.11.0").and include("Installing rake 13.0.6")
end
+ it "downgrades indirect dependencies if required to fulfill an explicit upgrade request" do
+ build_repo4 do
+ build_gem "rbs", "3.6.1"
+ build_gem "rbs", "3.9.4"
+
+ build_gem "solargraph", "0.56.0" do |s|
+ s.add_dependency "rbs", "~> 3.3"
+ end
+
+ build_gem "solargraph", "0.56.2" do |s|
+ s.add_dependency "rbs", "~> 3.6.1"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem 'solargraph', '~> 0.56.0'
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ rbs (3.9.4)
+ solargraph (0.56.0)
+ rbs (~> 3.3)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ solargraph (~> 0.56.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock --update solargraph"
+
+ expect(lockfile).to include("solargraph (0.56.2)")
+ end
+
+ it "does not downgrade direct dependencies unnecessarily" do
+ build_repo4 do
+ build_gem "redis", "4.8.1"
+ build_gem "redis", "5.3.0"
+
+ build_gem "sidekiq", "6.5.5" do |s|
+ s.add_dependency "redis", ">= 4.5.0"
+ end
+
+ build_gem "sidekiq", "6.5.12" do |s|
+ s.add_dependency "redis", ">= 4.5.0", "< 5"
+ end
+
+ # one version of sidekiq above Gemfile's range is needed to make the
+ # resolver choose `redis` first and trying to upgrade it, reproducing
+ # the accidental sidekiq downgrade as a result
+ build_gem "sidekiq", "7.0.0 " do |s|
+ s.add_dependency "redis", ">= 4.2.0"
+ end
+
+ build_gem "sentry-sidekiq", "5.22.0" do |s|
+ s.add_dependency "sidekiq", ">= 3.0"
+ end
+
+ build_gem "sentry-sidekiq", "5.22.4" do |s|
+ s.add_dependency "sidekiq", ">= 3.0"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "redis"
+ gem "sidekiq", "~> 6.5"
+ gem "sentry-sidekiq"
+ G
+
+ original_lockfile = <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ redis (4.8.1)
+ sentry-sidekiq (5.22.0)
+ sidekiq (>= 3.0)
+ sidekiq (6.5.12)
+ redis (>= 4.5.0, < 5)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ redis
+ sentry-sidekiq
+ sidekiq (~> 6.5)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ lockfile original_lockfile
+
+ bundle "lock --update sentry-sidekiq"
+
+ expect(lockfile).to eq(original_lockfile.sub("sentry-sidekiq (5.22.0)", "sentry-sidekiq (5.22.4)"))
+ end
+
it "does not downgrade indirect dependencies unnecessarily" do
build_repo4 do
build_gem "a" do |s|
@@ -446,7 +572,7 @@ RSpec.describe "bundle update" do
expect(the_bundle).to include_gems("a 1.0", "b 1.0", "c 2.0")
- update_repo4 do
+ build_repo4 do
build_gem "b", "2.0" do |s|
s.add_dependency "c", "< 2"
end
@@ -530,7 +656,7 @@ RSpec.describe "bundle update" do
activesupport (~> 6.0.0)
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "update activesupport"
@@ -574,7 +700,7 @@ RSpec.describe "bundle update" do
myrack
PLATFORMS
- #{local_platform}
+ x86-darwin-100
DEPENDENCIES
activesupport
@@ -582,12 +708,12 @@ RSpec.describe "bundle update" do
myrack-obama
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "install"
- FileUtils.rm_rf(gem_repo2)
+ FileUtils.rm_r(gem_repo2)
bundle "update --local --all"
expect(out).not_to include("Fetching source index")
@@ -689,33 +815,41 @@ RSpec.describe "bundle update" do
G
end
- it "should fail loudly", bundler: "< 3" do
- bundle "install --deployment"
+ it "should fail loudly" do
+ bundle_config "deployment true"
bundle "update", all: true, raise_on_error: false
expect(last_command).to be_failure
- expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/)
- expect(err).to match(/freeze by running `bundle config set frozen false`./)
+ expect(err).to eq <<~ERROR.strip
+ Bundler is unlocking, but the lockfile can't be updated because frozen mode is set
+
+ If this is a development machine, remove the Gemfile.lock freeze by running `bundle config set frozen false`.
+ ERROR
end
it "should fail loudly when frozen is set globally" do
- bundle "config set --global frozen 1"
+ bundle_config_global "frozen 1"
bundle "update", all: true, raise_on_error: false
- expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/).
- and match(/freeze by running `bundle config set frozen false`./)
+ expect(err).to eq <<~ERROR.strip
+ Bundler is unlocking, but the lockfile can't be updated because frozen mode is set
+
+ If this is a development machine, remove the Gemfile.lock freeze by running `bundle config set frozen false`.
+ ERROR
end
it "should fail loudly when deployment is set globally" do
- bundle "config set --global deployment true"
+ bundle_config_global "deployment true"
bundle "update", all: true, raise_on_error: false
- expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/).
- and match(/freeze by running `bundle config set frozen false`./)
+ expect(err).to eq <<~ERROR.strip
+ Bundler is unlocking, but the lockfile can't be updated because frozen mode is set
+
+ If this is a development machine, remove the Gemfile.lock freeze by running `bundle config set frozen false`.
+ ERROR
end
it "should not suggest any command to unfreeze bundler if frozen is set through ENV" do
bundle "update", all: true, raise_on_error: false, env: { "BUNDLE_FROZEN" => "true" }
- expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/)
- expect(err).not_to match(/by running/)
+ expect(err).to eq("Bundler is unlocking, but the lockfile can't be updated because frozen mode is set")
end
end
@@ -842,7 +976,7 @@ RSpec.describe "bundle update" do
bundle "update", all: true
expect(out).to match(/Resolving dependencies\.\.\.\.*\nBundle updated!/)
- update_repo4 do
+ build_repo4 do
build_gem "foo", "2.0"
end
@@ -903,7 +1037,7 @@ RSpec.describe "bundle update" do
request_store
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -940,71 +1074,9 @@ RSpec.describe "bundle update" do
request_store
BUNDLED WITH
- #{Bundler::VERSION}
- L
- end
- end
-
- context "with multiple, duplicated sources, with lockfile in old format", bundler: "< 3" do
- before do
- build_repo2 do
- build_gem "dotenv", "2.7.6"
-
- build_gem "oj", "3.11.3"
- build_gem "oj", "3.11.5"
-
- build_gem "vcr", "6.0.0"
- end
-
- build_repo3 do
- build_gem "pkg-gem-flowbyte-with-dep", "1.0.0" do |s|
- s.add_dependency "oj"
- end
- end
-
- gemfile <<~G
- source "https://gem.repo2"
-
- gem "dotenv"
-
- source "https://gem.repo3" do
- gem 'pkg-gem-flowbyte-with-dep'
- end
-
- gem "vcr",source: "https://gem.repo2"
- G
-
- lockfile <<~L
- GEM
- remote: https://gem.repo2/
- remote: https://gem.repo3/
- specs:
- dotenv (2.7.6)
- oj (3.11.3)
- pkg-gem-flowbyte-with-dep (1.0.0)
- oj
- vcr (6.0.0)
-
- PLATFORMS
- #{local_platform}
-
- DEPENDENCIES
- dotenv
- pkg-gem-flowbyte-with-dep!
- vcr!
-
- BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
-
- it "works" do
- bundle :install, artifice: "compact_index"
- bundle "update oj", artifice: "compact_index"
-
- expect(out).to include("Bundle updated!")
- expect(the_bundle).to include_gems "oj 3.11.5"
- end
end
end
@@ -1049,7 +1121,7 @@ RSpec.describe "bundle update in more complicated situations" do
end
bundle "update thin myrack-obama"
- expect(last_command.stdboth).to include "Bundler attempted to update myrack-obama but its version stayed the same"
+ expect(stdboth).to include "Bundler attempted to update myrack-obama but its version stayed the same"
expect(the_bundle).to include_gems "thin 2.0", "myrack 10.0", "myrack-obama 1.0"
end
@@ -1067,7 +1139,7 @@ RSpec.describe "bundle update in more complicated situations" do
bundle "update foo"
- expect(last_command.stdboth).not_to include "attempted to update"
+ expect(stdboth).not_to include "attempted to update"
end
it "will not warn when changing gem sources but not versions" do
@@ -1085,7 +1157,7 @@ RSpec.describe "bundle update in more complicated situations" do
bundle "update myrack"
- expect(last_command.stdboth).not_to include "attempted to update"
+ expect(stdboth).not_to include "attempted to update"
end
it "will update only from pinned source" do
@@ -1132,7 +1204,7 @@ RSpec.describe "bundle update in more complicated situations" do
a
L
- simulate_platform linux, &example
+ simulate_platform "x86_64-linux", &example
end
it "allows updating" do
@@ -1173,9 +1245,9 @@ RSpec.describe "bundle update in more complicated situations" do
end
it "is not updated because it is not actually included in the bundle" do
- simulate_platform linux do
+ simulate_platform "x86_64-linux" do
bundle "update a"
- expect(last_command.stdboth).to include "Bundler attempted to update a but it was not considered because it is for a different platform from the current one"
+ expect(stdboth).to include "Bundler attempted to update a but it was not considered because it is for a different platform from the current one"
expect(the_bundle).to_not include_gem "a"
end
end
@@ -1216,7 +1288,7 @@ RSpec.describe "bundle update when a gem depends on a newer version of bundler"
it "should explain that bundler conflicted and how to resolve the conflict" do
bundle "update", all: true, raise_on_error: false
- expect(last_command.stdboth).not_to match(/in snapshot/i)
+ expect(stdboth).not_to match(/in snapshot/i)
expect(err).to match(/current Bundler version/i).
and match(/Install the necessary version with `gem install bundler:9\.9\.9`/i)
end
@@ -1249,7 +1321,7 @@ RSpec.describe "bundle update --ruby" do
DEPENDENCIES
#{checksums_section_when_enabled}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -1281,10 +1353,10 @@ RSpec.describe "bundle update --ruby" do
DEPENDENCIES
#{checksums_section_when_enabled}
RUBY VERSION
- #{Bundler::RubyVersion.system}
+ #{Bundler::RubyVersion.system}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -1326,7 +1398,7 @@ RSpec.describe "bundle update --ruby" do
ruby 2.1.4p222
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
gemfile <<-G
@@ -1347,14 +1419,12 @@ RSpec.describe "bundle update --ruby" do
#{lockfile_platforms}
DEPENDENCIES
-
- CHECKSUMS
-
+ #{checksums_section_when_enabled}
RUBY VERSION
- #{Bundler::RubyVersion.system}
+ #{Bundler::RubyVersion.system}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -1388,7 +1458,7 @@ RSpec.describe "bundle update --bundler" do
myrack
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, '\11.0.0\2')
@@ -1408,7 +1478,7 @@ RSpec.describe "bundle update --bundler" do
myrack
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
expect(the_bundle).to include_gem "myrack 1.0"
@@ -1446,14 +1516,16 @@ RSpec.describe "bundle update --bundler" do
myrack
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
expect(the_bundle).to include_gem "myrack 1.0"
end
it "updates the bundler version in the lockfile even if the latest version is not installed", :ruby_repo do
- pristine_system_gems "bundler-2.99.9"
+ bundle_config "path.system true"
+
+ pristine_system_gems "bundler-9.0.0"
build_repo4 do
build_gem "myrack", "1.0"
@@ -1461,13 +1533,17 @@ RSpec.describe "bundle update --bundler" do
build_bundler "999.0.0"
end
+ checksums = checksums_section do |c|
+ c.checksum(gem_repo4, "myrack", "1.0")
+ c.checksum(gem_repo4, "bundler", "999.0.0")
+ end
+
install_gemfile <<-G
source "https://gem.repo4"
gem "myrack"
G
- lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, "2.99.9")
- bundle :update, bundler: true, verbose: true, preserve_ruby_flags: true
+ bundle :update, bundler: true, verbose: true
expect(out).to include("Updating bundler to 999.0.0")
expect(out).to include("Running `bundle update --bundler \"> 0.a\" --verbose` with bundler 999.0.0")
@@ -1484,9 +1560,9 @@ RSpec.describe "bundle update --bundler" do
DEPENDENCIES
myrack
-
+ #{checksums}
BUNDLED WITH
- 999.0.0
+ 999.0.0
L
expect(the_bundle).to include_gems "bundler 999.0.0"
@@ -1494,12 +1570,12 @@ RSpec.describe "bundle update --bundler" do
end
it "does not claim to update to Bundler version to a wrong version when cached gems are present" do
- pristine_system_gems "bundler-2.99.0"
+ pristine_system_gems "bundler-4.99.0"
build_repo4 do
build_gem "myrack", "3.0.9.1"
- build_bundler "2.99.0"
+ build_bundler "4.99.0"
end
gemfile <<~G
@@ -1544,6 +1620,7 @@ RSpec.describe "bundle update --bundler" do
checksums = checksums_section do |c|
c.checksum(gem_repo4, "myrack", "1.0")
+ c.checksum(gem_repo4, "bundler", "9.9.9")
end
install_gemfile <<-G
@@ -1568,7 +1645,7 @@ RSpec.describe "bundle update --bundler" do
myrack
#{checksums}
BUNDLED WITH
- 9.9.9
+ 9.9.9
L
expect(the_bundle).to include_gems "bundler 9.9.9"
@@ -1593,8 +1670,26 @@ RSpec.describe "bundle update --bundler" do
expect(err).to eq("The `bundle update --bundler` target version (999.999.999) does not exist")
end
+ it "errors if the explicit target version does not exist, even if auto switching is disabled" do
+ pristine_system_gems "bundler-9.9.9"
+
+ build_repo4 do
+ build_gem "myrack", "1.0"
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo4"
+ gem "myrack"
+ G
+
+ bundle :update, bundler: "999.999.999", raise_on_error: false, env: { "BUNDLER_VERSION" => "9.9.9" }
+
+ expect(last_command).to be_failure
+ expect(err).to eq("The `bundle update --bundler` target version (999.999.999) does not exist")
+ end
+
it "allows updating to development versions if already installed locally" do
- system_gems "bundler-2.3.0.dev"
+ system_gems "bundler-9.9.9"
build_repo4 do
build_gem "myrack", "1.0"
@@ -1605,11 +1700,13 @@ RSpec.describe "bundle update --bundler" do
gem "myrack"
G
- bundle :update, bundler: "2.3.0.dev", verbose: "true"
+ system_gems "bundler-9.0.0.dev", path: local_gem_path
+ bundle :update, bundler: "9.0.0.dev", verbose: "true"
checksums = checksums_section_when_enabled do |c|
c.checksum(gem_repo4, "myrack", "1.0")
end
+ checksums.delete("bundler")
expect(lockfile).to eq <<~L
GEM
@@ -1624,14 +1721,14 @@ RSpec.describe "bundle update --bundler" do
myrack
#{checksums}
BUNDLED WITH
- 2.3.0.dev
+ 9.0.0.dev
L
- expect(out).to include("Using bundler 2.3.0.dev")
+ expect(out).to include("Using bundler 9.0.0.dev")
end
it "does not touch the network if not necessary" do
- system_gems "bundler-2.3.9"
+ system_gems "bundler-9.9.9"
build_repo4 do
build_gem "myrack", "1.0"
@@ -1641,14 +1738,15 @@ RSpec.describe "bundle update --bundler" do
source "https://gem.repo4"
gem "myrack"
G
-
- bundle :update, bundler: "2.3.9", verbose: true
+ system_gems "bundler-9.0.0", path: local_gem_path
+ bundle :update, bundler: "9.0.0", verbose: true
expect(out).not_to include("Fetching gem metadata from https://rubygems.org/")
# Only updates properly on modern RubyGems.
checksums = checksums_section_when_enabled do |c|
c.checksum(gem_repo4, "myrack", "1.0")
+ c.checksum(local_gem_path, "bundler", "9.0.0", Gem::Platform::RUBY, "cache")
end
expect(lockfile).to eq <<~L
@@ -1664,14 +1762,14 @@ RSpec.describe "bundle update --bundler" do
myrack
#{checksums}
BUNDLED WITH
- 2.3.9
+ 9.0.0
L
- expect(out).to include("Using bundler 2.3.9")
+ expect(out).to include("Using bundler 9.0.0")
end
it "prints an error when trying to update bundler in frozen mode" do
- system_gems "bundler-2.3.9"
+ system_gems "bundler-9.0.0"
gemfile <<~G
source "https://gem.repo2"
@@ -1688,11 +1786,13 @@ RSpec.describe "bundle update --bundler" do
DEPENDENCIES
BUNDLED WITH
- 2.1.4
+ 9.0.0
L
- bundle "update --bundler=2.3.9", env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false
- expect(err).to include("An update to the version of bundler itself was requested, but the lockfile can't be updated because frozen mode is set")
+ system_gems "bundler-9.9.9", path: local_gem_path
+
+ bundle "update --bundler=9.9.9", env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false
+ expect(err).to include("An update to the version of Bundler itself was requested, but the lockfile can't be updated because frozen mode is set")
end
end
@@ -1736,7 +1836,7 @@ RSpec.describe "bundle update conservative" do
context "with patch set as default update level in config" do
it "should do a patch level update" do
- bundle "config set --local prefer_patch true"
+ bundle_config "prefer_patch true"
bundle "update foo"
expect(the_bundle).to include_gems "foo 1.4.5", "bar 2.1.1", "qux 1.0.0"
@@ -1855,7 +1955,7 @@ RSpec.describe "bundle update conservative" do
CHECKSUMS
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -1916,7 +2016,7 @@ RSpec.describe "bundle update conservative" do
shared_owner_b
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -1970,7 +2070,7 @@ RSpec.describe "bundle update conservative" do
nokogiri (>= 1.16.4)
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
diff --git a/spec/bundler/commands/version_spec.rb b/spec/bundler/commands/version_spec.rb
index 307058a5dd..4320ad0611 100644
--- a/spec/bundler/commands/version_spec.rb
+++ b/spec/bundler/commands/version_spec.rb
@@ -10,38 +10,56 @@ RSpec.describe "bundle version" do
end
context "with -v" do
- it "outputs the version", bundler: "< 3" do
+ it "outputs the version and virtual version if set" do
bundle "-v"
- expect(out).to eq("Bundler version #{Bundler::VERSION}")
- end
+ expect(out).to eq(Bundler::VERSION.to_s)
- it "outputs the version", bundler: "3" do
+ bundle_config "simulate_version 5"
bundle "-v"
- expect(out).to eq(Bundler::VERSION)
+ expect(out).to eq("#{Bundler::VERSION} (simulating Bundler 5)")
end
end
context "with --version" do
- it "outputs the version", bundler: "< 3" do
+ it "outputs the version and virtual version if set" do
bundle "--version"
- expect(out).to eq("Bundler version #{Bundler::VERSION}")
- end
+ expect(out).to eq(Bundler::VERSION.to_s)
- it "outputs the version", bundler: "3" do
+ bundle_config "simulate_version 5"
bundle "--version"
- expect(out).to eq(Bundler::VERSION)
+ expect(out).to eq("#{Bundler::VERSION} (simulating Bundler 5)")
end
end
context "with version" do
- it "outputs the version with build metadata", bundler: "< 3" do
- bundle "version"
- expect(out).to match(/\ABundler version #{Regexp.escape(Bundler::VERSION)} \(\d{4}-\d{2}-\d{2} commit #{COMMIT_HASH}\)\z/)
+ context "when released", :ruby_repo do
+ before do
+ system_gems "bundler-4.9.9", released: true
+ end
+
+ it "outputs the version, virtual version if set, and build metadata" do
+ bundle "version"
+ expect(out).to match(/\A4\.9\.9 \(2100-01-01 commit #{COMMIT_HASH}\)\z/)
+
+ bundle_config "simulate_version 5"
+ bundle "version"
+ expect(out).to match(/\A4\.9\.9 \(simulating Bundler 5\) \(2100-01-01 commit #{COMMIT_HASH}\)\z/)
+ end
end
- it "outputs the version with build metadata", bundler: "3" do
- bundle "version"
- expect(out).to match(/\A#{Regexp.escape(Bundler::VERSION)} \(\d{4}-\d{2}-\d{2} commit #{COMMIT_HASH}\)\z/)
+ context "when not released" do
+ before do
+ system_gems "bundler-4.9.9", released: false
+ end
+
+ it "outputs the version, virtual version if set, and build metadata" do
+ bundle "version"
+ expect(out).to match(/\A4\.9\.9 \(20\d{2}-\d{2}-\d{2} commit #{COMMIT_HASH}\)\z/)
+
+ bundle_config "simulate_version 5"
+ bundle "version"
+ expect(out).to match(/\A4\.9\.9 \(simulating Bundler 5\) \(20\d{2}-\d{2}-\d{2} commit #{COMMIT_HASH}\)\z/)
+ end
end
end
end
diff --git a/spec/bundler/commands/viz_spec.rb b/spec/bundler/commands/viz_spec.rb
deleted file mode 100644
index 712ded4bc4..0000000000
--- a/spec/bundler/commands/viz_spec.rb
+++ /dev/null
@@ -1,144 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe "bundle viz", bundler: "< 3", if: Bundler.which("dot") do
- before do
- realworld_system_gems "ruby-graphviz --version 1.2.5"
- end
-
- it "graphs gems from the Gemfile" do
- install_gemfile <<-G
- source "https://gem.repo1"
- gem "myrack"
- gem "myrack-obama"
- G
-
- bundle "viz"
- expect(out).to include("gem_graph.png")
-
- bundle "viz", format: "debug"
- expect(out).to eq(<<~DOT.strip)
- digraph Gemfile {
- concentrate = "true";
- normalize = "true";
- nodesep = "0.55";
- edge[ weight = "2"];
- node[ fontname = "Arial, Helvetica, SansSerif"];
- edge[ fontname = "Arial, Helvetica, SansSerif" , fontsize = "12"];
- default [style = "filled", fillcolor = "#B9B9D5", shape = "box3d", fontsize = "16", label = "default"];
- myrack [style = "filled", fillcolor = "#B9B9D5", label = "myrack"];
- default -> myrack [constraint = "false"];
- "myrack-obama" [style = "filled", fillcolor = "#B9B9D5", label = "myrack-obama"];
- default -> "myrack-obama" [constraint = "false"];
- "myrack-obama" -> myrack;
- }
- debugging bundle viz...
- DOT
- end
-
- it "graphs gems that are prereleases" do
- build_repo2 do
- build_gem "myrack", "1.3.pre"
- end
-
- install_gemfile <<-G
- source "https://gem.repo2"
- gem "myrack", "= 1.3.pre"
- gem "myrack-obama"
- G
-
- bundle "viz"
- expect(out).to include("gem_graph.png")
-
- bundle "viz", format: :debug, version: true
- expect(out).to eq(<<~EOS.strip)
- digraph Gemfile {
- concentrate = "true";
- normalize = "true";
- nodesep = "0.55";
- edge[ weight = "2"];
- node[ fontname = "Arial, Helvetica, SansSerif"];
- edge[ fontname = "Arial, Helvetica, SansSerif" , fontsize = "12"];
- default [style = "filled", fillcolor = "#B9B9D5", shape = "box3d", fontsize = "16", label = "default"];
- myrack [style = "filled", fillcolor = "#B9B9D5", label = "myrack\\n1.3.pre"];
- default -> myrack [constraint = "false"];
- "myrack-obama" [style = "filled", fillcolor = "#B9B9D5", label = "myrack-obama\\n1.0"];
- default -> "myrack-obama" [constraint = "false"];
- "myrack-obama" -> myrack;
- }
- debugging bundle viz...
- EOS
- end
-
- context "with another gem that has a graphviz file" do
- before do
- build_repo4 do
- build_gem "graphviz", "999" do |s|
- s.write("lib/graphviz.rb", "abort 'wrong graphviz gem loaded'")
- end
- end
-
- system_gems "graphviz-999", gem_repo: gem_repo4
- end
-
- it "loads the correct ruby-graphviz gem" do
- install_gemfile <<-G
- source "https://gem.repo1"
- gem "myrack"
- gem "myrack-obama"
- G
-
- bundle "viz", format: "debug"
- expect(out).to eq(<<~DOT.strip)
- digraph Gemfile {
- concentrate = "true";
- normalize = "true";
- nodesep = "0.55";
- edge[ weight = "2"];
- node[ fontname = "Arial, Helvetica, SansSerif"];
- edge[ fontname = "Arial, Helvetica, SansSerif" , fontsize = "12"];
- default [style = "filled", fillcolor = "#B9B9D5", shape = "box3d", fontsize = "16", label = "default"];
- myrack [style = "filled", fillcolor = "#B9B9D5", label = "myrack"];
- default -> myrack [constraint = "false"];
- "myrack-obama" [style = "filled", fillcolor = "#B9B9D5", label = "myrack-obama"];
- default -> "myrack-obama" [constraint = "false"];
- "myrack-obama" -> myrack;
- }
- debugging bundle viz...
- DOT
- end
- end
-
- context "--without option" do
- it "one group" do
- install_gemfile <<-G
- source "https://gem.repo1"
- gem "activesupport"
-
- group :rails do
- gem "rails"
- end
- G
-
- bundle "viz --without=rails"
- expect(out).to include("gem_graph.png")
- end
-
- it "two groups" do
- install_gemfile <<-G
- source "https://gem.repo1"
- gem "activesupport"
-
- group :myrack do
- gem "myrack"
- end
-
- group :rails do
- gem "rails"
- end
- G
-
- bundle "viz --without=rails:myrack"
- expect(out).to include("gem_graph.png")
- end
- end
-end
diff --git a/spec/bundler/install/allow_offline_install_spec.rb b/spec/bundler/install/allow_offline_install_spec.rb
index 21b0568f7d..c7ab7c3d7e 100644
--- a/spec/bundler/install/allow_offline_install_spec.rb
+++ b/spec/bundler/install/allow_offline_install_spec.rb
@@ -1,10 +1,6 @@
# frozen_string_literal: true
-RSpec.describe "bundle install with :allow_offline_install" do
- before do
- bundle "config set allow_offline_install true"
- end
-
+RSpec.describe "bundle install allows offline install" do
context "with no cached data locally" do
it "still installs" do
install_gemfile <<-G, artifice: "compact_index"
@@ -28,7 +24,7 @@ RSpec.describe "bundle install with :allow_offline_install" do
it "will install from the compact index" do
system_gems ["myrack-1.0.0"], path: default_bundle_path
- bundle "config set clean false"
+ bundle_config "clean false"
install_gemfile <<-G, artifice: "compact_index"
source "http://testgemserver.local"
gem "myrack-obama"
@@ -43,7 +39,7 @@ RSpec.describe "bundle install with :allow_offline_install" do
G
bundle :update, artifice: "fail", all: true
- expect(last_command.stdboth).to include "Using the cached data for the new index because of a network error"
+ expect(stdboth).to include "Using the cached data for the new index because of a network error"
expect(the_bundle).to include_gems("myrack-obama 1.0", "myrack 1.0.0")
end
diff --git a/spec/bundler/install/binstubs_spec.rb b/spec/bundler/install/binstubs_spec.rb
index 00765ac6dd..c2eccb3ef2 100644
--- a/spec/bundler/install/binstubs_spec.rb
+++ b/spec/bundler/install/binstubs_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe "bundle install" do
gem "myrack"
G
- config "BUNDLE_SYSTEM_BINDIR" => system_gem_path("altbin").to_s
+ bundle_config "BUNDLE_SYSTEM_BINDIR" => system_gem_path("altbin").to_s
bundle :install
expect(the_bundle).to include_gems "myrack 1.0.0"
expect(system_gem_path("altbin/myrackup")).to exist
diff --git a/spec/bundler/install/bundler_spec.rb b/spec/bundler/install/bundler_spec.rb
index 56f8431181..86c22dad55 100644
--- a/spec/bundler/install/bundler_spec.rb
+++ b/spec/bundler/install/bundler_spec.rb
@@ -135,7 +135,7 @@ RSpec.describe "bundle install" do
end
it "causes a conflict if child dependencies conflict" do
- bundle "config set force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
update_repo2 do
build_gem "rails_pinned_to_old_activesupport" do |s|
@@ -163,7 +163,7 @@ RSpec.describe "bundle install" do
end
it "causes a conflict if a child dependency conflicts with the Gemfile" do
- bundle "config set force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
update_repo2 do
build_gem "rails_pinned_to_old_activesupport" do |s|
@@ -216,7 +216,7 @@ RSpec.describe "bundle install" do
build_gem "rails", "7.0.4"
end
- bundle "config set path.system true"
+ bundle_config "path.system true"
install_gemfile <<-G
source "https://gem.repo4"
@@ -238,7 +238,7 @@ RSpec.describe "bundle install" do
end
it "can install dependencies with newer bundler version with system gems" do
- bundle "config set path.system true"
+ bundle_config "path.system true"
system_gems "bundler-99999999.99.1"
@@ -252,7 +252,7 @@ RSpec.describe "bundle install" do
end
it "can install dependencies with newer bundler version with a local path" do
- bundle "config set path .bundle"
+ bundle_config "path .bundle"
system_gems "bundler-99999999.99.1"
diff --git a/spec/bundler/install/cooldown_spec.rb b/spec/bundler/install/cooldown_spec.rb
new file mode 100644
index 0000000000..bad7b7cf34
--- /dev/null
+++ b/spec/bundler/install/cooldown_spec.rb
@@ -0,0 +1,433 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with the cooldown setting" do
+ before do
+ build_repo2
+ end
+
+ context "Gemfile DSL" do
+ it "accepts `source ..., cooldown: N` without error" do
+ install_gemfile <<-G, artifice: "compact_index"
+ source "https://gem.repo2", cooldown: 5
+ gem "myrack"
+ G
+
+ expect(the_bundle).to include_gems("myrack 1.0.0")
+ end
+
+ it "accepts `cooldown: 0` to disable cooldown for a source" do
+ install_gemfile <<-G, artifice: "compact_index"
+ source "https://gem.repo2", cooldown: 0
+ gem "myrack"
+ G
+
+ expect(the_bundle).to include_gems("myrack 1.0.0")
+ end
+ end
+
+ context "CLI flag" do
+ before do
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "myrack"
+ G
+ end
+
+ it "accepts --cooldown N on install" do
+ bundle "install --cooldown 7", artifice: "compact_index"
+
+ expect(the_bundle).to include_gems("myrack 1.0.0")
+ end
+
+ it "accepts --cooldown 0 as an escape hatch" do
+ bundle "install --cooldown 0", artifice: "compact_index"
+
+ expect(the_bundle).to include_gems("myrack 1.0.0")
+ end
+
+ it "rejects a negative --cooldown value" do
+ bundle "install --cooldown=-7", artifice: "compact_index", raise_on_error: false
+
+ expect(err).to match(/non-negative integer/)
+ end
+ end
+
+ context "configuration" do
+ it "reads BUNDLE_COOLDOWN as an integer" do
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "myrack"
+ G
+
+ bundle "install", env: { "BUNDLE_COOLDOWN" => "7" }, artifice: "compact_index"
+
+ expect(the_bundle).to include_gems("myrack 1.0.0")
+ end
+
+ it "reads `bundle config set cooldown N`" do
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "myrack"
+ G
+
+ bundle "config set cooldown 7"
+ bundle "install", artifice: "compact_index"
+
+ expect(the_bundle).to include_gems("myrack 1.0.0")
+ end
+ end
+
+ context "end-to-end with v2 compact index" do
+ before do
+ now = Time.now.utc
+ build_repo3 do
+ build_gem "ripe_gem", "1.0.0" do |s|
+ s.date = now - (30 * 86_400)
+ end
+ build_gem "ripe_gem", "2.0.0" do |s|
+ s.date = now - (1 * 86_400)
+ end
+
+ # parent only resolves with the in-cooldown child 2.0.0
+ build_gem "child", "1.0.0" do |s|
+ s.date = now - (30 * 86_400)
+ end
+ build_gem "child", "2.0.0" do |s|
+ s.date = now - (1 * 86_400)
+ end
+ build_gem "parent", "1.0.0" do |s|
+ s.add_dependency "child", ">= 2.0.0"
+ s.date = now - (30 * 86_400)
+ end
+
+ # a cooldown-eligible version exists above the in-cooldown locked one
+ build_gem "upgradable", "2.0.0" do |s|
+ s.date = now - (1 * 86_400)
+ end
+ build_gem "upgradable", "3.0.0" do |s|
+ s.date = now - (30 * 86_400)
+ end
+ end
+ end
+
+ it "excludes versions within the cooldown window" do
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "ripe_gem"
+ G
+
+ bundle "install --cooldown 7", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("ripe_gem 1.0.0")
+ end
+
+ it "selects the latest version when --cooldown 0 is passed" do
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "ripe_gem"
+ G
+
+ bundle "install --cooldown 0", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("ripe_gem 2.0.0")
+ end
+
+ it "applies cooldown declared per-source in the Gemfile" do
+ gemfile <<-G
+ source "https://gem.repo3", cooldown: 7
+ gem "ripe_gem"
+ G
+
+ bundle "install", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("ripe_gem 1.0.0")
+ end
+
+ it "is overridden by CLI --cooldown when Gemfile sets a different per-source value" do
+ gemfile <<-G
+ source "https://gem.repo3", cooldown: 0
+ gem "ripe_gem"
+ G
+
+ bundle "install --cooldown 7", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("ripe_gem 1.0.0")
+ end
+
+ it "bypasses cooldown when bundle install uses an existing lockfile" do
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "ripe_gem"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ ripe_gem (2.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ripe_gem
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install --cooldown 7", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("ripe_gem 2.0.0")
+ end
+
+ it "annotates in-cooldown versions in bundle outdated table output" do
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "ripe_gem", "1.0.0"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ ripe_gem (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ripe_gem (= 1.0.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "outdated --cooldown 7", artifice: "compact_index_cooldown", raise_on_error: false
+
+ expect(out).to match(/ripe_gem.*\(cooldown \d+d\)/)
+ end
+
+ it "annotates in-cooldown versions in bundle outdated --parseable output" do
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "ripe_gem", "1.0.0"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ ripe_gem (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ripe_gem (= 1.0.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "outdated --cooldown 7 --parseable", artifice: "compact_index_cooldown", raise_on_error: false
+
+ expect(out).to match(/ripe_gem.*in cooldown for \d+ more day/)
+ end
+
+ it "excludes a locally-installed version that is still within the cooldown window" do
+ system_gems "ripe_gem-2.0.0", gem_repo: gem_repo3
+
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "ripe_gem"
+ G
+
+ bundle "install --cooldown 7", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("ripe_gem 1.0.0")
+ end
+
+ it "selects a locally-installed in-cooldown version when --cooldown 0 bypasses the filter" do
+ system_gems "ripe_gem-2.0.0", gem_repo: gem_repo3
+
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "ripe_gem"
+ G
+
+ bundle "install --cooldown 0", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("ripe_gem 2.0.0")
+ end
+
+ it "surfaces a cooldown hint when bundle update filters every candidate" do
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "ripe_gem"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ ripe_gem (1.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ripe_gem
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update ripe_gem --cooldown 99999", artifice: "compact_index_cooldown", raise_on_error: false
+
+ expect(err).to match(/excluded by the cooldown setting/)
+ expect(err).to match(/--cooldown 0/)
+ end
+
+ it "keeps an in-cooldown locked version on bundle update --all instead of failing" do
+ # Lockfile written before cooldown was enabled pins the now-in-cooldown
+ # latest version. A full update must not downgrade below it, and cooldown
+ # must not filter it out, otherwise resolution becomes impossible (#9598).
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "ripe_gem"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ ripe_gem (2.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ripe_gem
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update --all --cooldown 7", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("ripe_gem 2.0.0")
+ end
+
+ it "does not fail bundle outdated when the locked version is in cooldown" do
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "ripe_gem"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ ripe_gem (2.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ripe_gem
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "outdated --cooldown 7", artifice: "compact_index_cooldown", raise_on_error: false
+
+ # exit 0 means no outdated gems and, crucially, no resolution failure (exit 7)
+ expect(exitstatus).to eq(0)
+ end
+
+ it "still applies cooldown and downgrades a gem that is updated explicitly" do
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "ripe_gem"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ ripe_gem (2.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ripe_gem
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update ripe_gem --cooldown 7", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("ripe_gem 1.0.0")
+ end
+
+ it "keeps an in-cooldown transitive dependency on bundle update --all" do
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "parent"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ child (2.0.0)
+ parent (1.0.0)
+ child (>= 2.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ parent
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update --all --cooldown 7", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("parent 1.0.0", "child 2.0.0")
+ end
+
+ it "still upgrades to a cooldown-eligible version above the locked one" do
+ gemfile <<-G
+ source "https://gem.repo3"
+ gem "upgradable"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo3/
+ specs:
+ upgradable (2.0.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ upgradable
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "update --all --cooldown 7", artifice: "compact_index_cooldown"
+
+ expect(the_bundle).to include_gems("upgradable 3.0.0")
+ end
+ end
+end
diff --git a/spec/bundler/install/deploy_spec.rb b/spec/bundler/install/deploy_spec.rb
index bd39ac5cc1..a3b4a87ecf 100644
--- a/spec/bundler/install/deploy_spec.rb
+++ b/spec/bundler/install/deploy_spec.rb
@@ -8,89 +8,23 @@ RSpec.describe "install in deployment or frozen mode" do
G
end
- context "with CLI flags", bundler: "< 3" do
- it "fails without a lockfile and says that --deployment requires a lock" do
- bundle "install --deployment", raise_on_error: false
- expect(err).to include("The --deployment flag requires a lockfile")
- end
-
- it "fails without a lockfile and says that --frozen requires a lock" do
- bundle "install --frozen", raise_on_error: false
- expect(err).to include("The --frozen flag requires a lockfile")
- end
-
- it "disallows --deployment --system" do
- bundle "install --deployment --system", raise_on_error: false
- expect(err).to include("You have specified both --deployment")
- expect(err).to include("Please choose only one option")
- expect(exitstatus).to eq(15)
- end
-
- it "disallows --deployment --path --system" do
- bundle "install --deployment --path . --system", raise_on_error: false
- expect(err).to include("You have specified both --path")
- expect(err).to include("as well as --system")
- expect(err).to include("Please choose only one option")
- expect(exitstatus).to eq(15)
- end
-
- it "doesn't mess up a subsequent `bundle install` after you try to deploy without a lock" do
- bundle "install --deployment", raise_on_error: false
- bundle :install
- expect(the_bundle).to include_gems "myrack 1.0"
- end
-
- it "installs gems by default to vendor/bundle" do
- bundle :lock
- bundle "install --deployment"
- expect(out).to include("vendor/bundle")
- end
-
- it "installs gems to custom path if specified" do
- bundle :lock
- bundle "install --path vendor/bundle2 --deployment"
- expect(out).to include("vendor/bundle2")
- end
-
- it "works with the --frozen flag" do
- bundle :lock
- bundle "install --frozen"
- end
-
- it "explodes with the --deployment flag if you make a change and don't check in the lockfile" do
- bundle :lock
- gemfile <<-G
- source "https://gem.repo1"
- gem "myrack"
- gem "myrack-obama"
- G
-
- bundle "install --deployment", raise_on_error: false
- expect(err).to include("frozen mode")
- expect(err).to include("You have added to the Gemfile")
- expect(err).to include("* myrack-obama")
- expect(err).not_to include("You have deleted from the Gemfile")
- expect(err).not_to include("You have changed in the Gemfile")
- end
- end
-
it "fails without a lockfile and says that deployment requires a lock" do
- bundle "config deployment true"
+ 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_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
- bundle "config set --local deployment true"
- bundle "config set --local path vendor/bundle"
+ pristine_system_gems
+ bundle_config "deployment true"
+ bundle_config "path vendor/bundle"
bundle "install --gemfile #{tmp}/bundled_app/Gemfile", dir: tmp
expect(the_bundle).to include_gems "myrack 1.0"
end
@@ -104,8 +38,8 @@ RSpec.describe "install in deployment or frozen mode" do
end
G
bundle :install
- bundle "config set --local deployment true"
- bundle "config set --local without test"
+ bundle_config "deployment true"
+ bundle_config "without test"
bundle :install
end
@@ -113,7 +47,7 @@ RSpec.describe "install in deployment or frozen mode" do
skip "doesn't find bundle" if Gem.win_platform?
bundle :install
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
bundle :install
bundle "exec bundle check", env: { "PATH" => path }
end
@@ -128,7 +62,7 @@ RSpec.describe "install in deployment or frozen mode" do
G
bundle :install
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
bundle :install
end
@@ -141,7 +75,7 @@ RSpec.describe "install in deployment or frozen mode" do
G
bundle :install
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
bundle :install
end
@@ -152,7 +86,7 @@ RSpec.describe "install in deployment or frozen mode" do
gem "myrack-obama", ">= 1.0"
G
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
bundle :install, artifice: "endpoint_strict_basic_authentication"
end
@@ -164,7 +98,7 @@ RSpec.describe "install in deployment or frozen mode" do
end
G
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
bundle :install
expect(the_bundle).to include_gems "myrack 1.0"
@@ -172,7 +106,7 @@ RSpec.describe "install in deployment or frozen mode" do
context "when replacing a host with the same host with credentials" do
before do
- bundle "config set --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle "install"
gemfile <<-G
source "http://user_name:password@localgemserver.test/"
@@ -192,7 +126,7 @@ RSpec.describe "install in deployment or frozen mode" do
myrack
G
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
end
it "allows the replace" do
@@ -208,7 +142,7 @@ RSpec.describe "install in deployment or frozen mode" do
end
it "installs gems by default to vendor/bundle" do
- bundle "config set deployment true"
+ bundle_config "deployment true"
expect do
bundle "install"
end.not_to change { bundled_app_lock.mtime }
@@ -216,20 +150,20 @@ RSpec.describe "install in deployment or frozen mode" do
end
it "installs gems to custom path if specified" do
- bundle "config set path vendor/bundle2"
- bundle "config set deployment true"
+ bundle_config "path vendor/bundle2"
+ bundle_config "deployment true"
bundle "install"
expect(out).to include("vendor/bundle2")
end
it "installs gems to custom path if specified, even when configured through ENV" do
- bundle "config set deployment true"
+ bundle_config "deployment true"
bundle "install", env: { "BUNDLE_PATH" => "vendor/bundle2" }
expect(out).to include("vendor/bundle2")
end
it "works with the `frozen` setting" do
- bundle "config set frozen true"
+ bundle_config "frozen true"
expect do
bundle "install"
end.not_to change { bundled_app_lock.mtime }
@@ -248,7 +182,7 @@ RSpec.describe "install in deployment or frozen mode" do
gem "myrack-obama"
G
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
bundle :install, raise_on_error: false
expect(err).to include("frozen mode")
expect(err).to include("You have added to the Gemfile")
@@ -267,9 +201,9 @@ RSpec.describe "install in deployment or frozen mode" do
expect(the_bundle).to include_gems "path_gem 1.0"
FileUtils.rm_r lib_path("path_gem-1.0")
- bundle "config set --local path .bundle"
- bundle "config set --local without development"
- bundle "config set --local deployment true"
+ bundle_config "path .bundle"
+ bundle_config "without development"
+ bundle_config "deployment true"
bundle :install, env: { "DEBUG" => "1" }
run "puts :WIN"
expect(out).to eq("WIN")
@@ -317,11 +251,11 @@ RSpec.describe "install in deployment or frozen mode" do
bar
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle :install, env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false, artifice: "compact_index"
- expect(err).to include("Your lock file is missing \"bar\", but the lockfile can't be updated because frozen mode is set")
+ expect(err).to include("Your lockfile is missing \"bar\", but can't be updated because frozen mode is set")
end
it "explodes if a path gem is missing" do
@@ -334,8 +268,8 @@ RSpec.describe "install in deployment or frozen mode" do
expect(the_bundle).to include_gems "path_gem 1.0"
FileUtils.rm_r lib_path("path_gem-1.0")
- bundle "config set --local path .bundle"
- bundle "config set --local deployment true"
+ bundle_config "path .bundle"
+ bundle_config "deployment true"
bundle :install, raise_on_error: false
expect(err).to include("The path `#{lib_path("path_gem-1.0")}` does not exist.")
end
@@ -406,7 +340,7 @@ RSpec.describe "install in deployment or frozen mode" do
gem "activesupport"
G
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
bundle :install, raise_on_error: false
expect(err).to include("frozen mode")
expect(err).to include("You have added to the Gemfile:\n* activesupport\n\n")
@@ -417,7 +351,7 @@ RSpec.describe "install in deployment or frozen mode" do
it "explodes if you remove a gem and don't check in the lockfile" do
gemfile 'source "https://gem.repo1"'
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
bundle :install, raise_on_error: false
expect(err).to include("Some dependencies were deleted")
expect(err).to include("frozen mode")
@@ -431,7 +365,7 @@ RSpec.describe "install in deployment or frozen mode" do
gem "myrack", :git => "git://hubz.com"
G
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
bundle :install, raise_on_error: false
expect(err).to include("frozen mode")
expect(err).not_to include("You have added to the Gemfile")
@@ -451,7 +385,7 @@ RSpec.describe "install in deployment or frozen mode" do
gem "myrack"
G
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
bundle :install, raise_on_error: false
expect(err).to include("frozen mode")
expect(err).not_to include("You have deleted from the Gemfile")
@@ -475,7 +409,7 @@ RSpec.describe "install in deployment or frozen mode" do
gem "foo", :git => "#{lib_path("myrack")}"
G
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
bundle :install, raise_on_error: false
expect(err).to include("frozen mode")
expect(err).to include("You have changed in the Gemfile:\n* myrack from `#{lib_path("myrack")}` to `no specified source`")
@@ -496,7 +430,7 @@ RSpec.describe "install in deployment or frozen mode" do
gem "myrack", :git => "https:/my-git-repo-for-myrack"
G
- bundle "config set --local frozen true"
+ bundle_config "frozen true"
bundle :install, raise_on_error: false
expect(err).to include("frozen mode")
expect(err).to include("You have changed in the Gemfile:\n* myrack from `#{lib_path("myrack")}` to `https:/my-git-repo-for-myrack`")
@@ -507,7 +441,7 @@ RSpec.describe "install in deployment or frozen mode" do
it "remembers that the bundle is frozen at runtime" do
bundle :lock
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
gemfile <<-G
source "https://gem.repo1"
@@ -540,17 +474,16 @@ RSpec.describe "install in deployment or frozen mode" do
bundle :install
expect(the_bundle).to include_gems "foo 1.0"
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/foo")).to be_directory
bundle "install --local"
expect(out).to include("Updating files in vendor/cache")
- simulate_new_machine
- bundle "config set --local deployment true"
+ pristine_system_gems
+ bundle_config "deployment true"
bundle "install --verbose"
- expect(out).not_to include("but the lockfile can't be updated because frozen mode is set")
+ expect(out).not_to include("can't be updated because frozen mode is set")
expect(out).not_to include("You have added to the Gemfile")
expect(out).not_to include("You have deleted from the Gemfile")
expect(out).to include("vendor/cache/foo")
diff --git a/spec/bundler/install/failure_spec.rb b/spec/bundler/install/failure_spec.rb
index 2c2773e849..32ca455439 100644
--- a/spec/bundler/install/failure_spec.rb
+++ b/spec/bundler/install/failure_spec.rb
@@ -48,4 +48,38 @@ In Gemfile:
end
end
end
+
+ context "when lockfile dependencies don't match the gemspec" do
+ before do
+ build_repo4 do
+ build_gem "myrack", "1.0.0" do |s|
+ s.add_dependency "myrack-test", "~> 1.0"
+ end
+
+ build_gem "myrack-test", "1.0.0"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo4"
+ gem "myrack"
+ G
+
+ # First install to generate lockfile
+ bundle :install
+
+ # Manually edit lockfile to have incorrect dependencies
+ lockfile_content = File.read(bundled_app_lock)
+ # Remove the myrack-test dependency from myrack
+ lockfile_content.gsub!(/^ myrack \(1\.0\.0\)\n myrack-test \(~> 1\.0\)\n/, " myrack (1.0.0)\n")
+ File.write(bundled_app_lock, lockfile_content)
+ end
+
+ it "reports the mismatch with detailed information" do
+ bundle :install, raise_on_error: false, env: { "BUNDLE_FROZEN" => "true" }
+
+ expect(err).to include("Bundler found incorrect dependencies in the lockfile for myrack-1.0.0")
+ expect(err).to include("myrack-test: gemspec specifies ~> 1.0, not in lockfile")
+ expect(err).to include("Please run `bundle install` to regenerate the lockfile.")
+ end
+ end
end
diff --git a/spec/bundler/install/redownload_spec.rb b/spec/bundler/install/force_spec.rb
index b522e22bd5..e0f6fb6364 100644
--- a/spec/bundler/install/redownload_spec.rb
+++ b/spec/bundler/install/force_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe "bundle install" do
G
end
- shared_examples_for "an option to force redownloading gems" do
+ shared_examples_for "an option to force reinstalling gems" do
it "re-installs installed gems" do
myrack_lib = default_bundle_path("gems/myrack-1.0.0/lib/myrack.rb")
@@ -57,35 +57,15 @@ RSpec.describe "bundle install" do
end
end
- describe "with --force", bundler: 2 do
- it_behaves_like "an option to force redownloading gems" do
+ describe "with --force" do
+ it_behaves_like "an option to force reinstalling gems" do
let(:flag) { "force" }
end
-
- it "shows a deprecation when single flag passed" do
- bundle "install --force"
- expect(err).to include "[DEPRECATED] The `--force` option has been renamed to `--redownload`"
- end
-
- it "shows a deprecation when multiple flags passed" do
- bundle "install --no-color --force"
- expect(err).to include "[DEPRECATED] The `--force` option has been renamed to `--redownload`"
- end
end
describe "with --redownload" do
- it_behaves_like "an option to force redownloading gems" do
+ it_behaves_like "an option to force reinstalling gems" do
let(:flag) { "redownload" }
end
-
- it "does not show a deprecation when single flag passed" do
- bundle "install --redownload"
- expect(err).not_to include "[DEPRECATED] The `--force` option has been renamed to `--redownload`"
- end
-
- it "does not show a deprecation when single multiple flags passed" do
- bundle "install --no-color --redownload"
- expect(err).not_to include "[DEPRECATED] The `--force` option has been renamed to `--redownload`"
- end
end
end
diff --git a/spec/bundler/install/gemfile/eval_gemfile_spec.rb b/spec/bundler/install/gemfile/eval_gemfile_spec.rb
index a507e52485..3afa4f5daa 100644
--- a/spec/bundler/install/gemfile/eval_gemfile_spec.rb
+++ b/spec/bundler/install/gemfile/eval_gemfile_spec.rb
@@ -85,7 +85,7 @@ RSpec.describe "bundle install with gemfile that uses eval_gemfile" do
# parsed lockfile and the evaluated gemfile.
it "bundles with deployment mode configured" do
bundle :install
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
bundle :install
end
end
diff --git a/spec/bundler/install/gemfile/force_ruby_platform_spec.rb b/spec/bundler/install/gemfile/force_ruby_platform_spec.rb
index 926e7527e6..bcc1f36823 100644
--- a/spec/bundler/install/gemfile/force_ruby_platform_spec.rb
+++ b/spec/bundler/install/gemfile/force_ruby_platform_spec.rb
@@ -117,7 +117,7 @@ RSpec.describe "bundle install with force_ruby_platform DSL option", :jruby do
platform_specific
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
simulate_platform "x86-darwin-100" do
diff --git a/spec/bundler/install/gemfile/gemspec_spec.rb b/spec/bundler/install/gemfile/gemspec_spec.rb
index be0694cb55..e51fc9247d 100644
--- a/spec/bundler/install/gemfile/gemspec_spec.rb
+++ b/spec/bundler/install/gemfile/gemspec_spec.rb
@@ -8,34 +8,6 @@ RSpec.describe "bundle install from an existing gemspec" do
end
end
- let(:x64_mingw_archs) do
- if RUBY_PLATFORM == "x64-mingw-ucrt"
-
- ["x64-mingw-ucrt", "x64-mingw32"]
-
- else
- ["x64-mingw32"]
- end
- end
-
- let(:x64_mingw_gems) do
- x64_mingw_archs.map {|p| "platform_specific (1.0-#{p})" }.join("\n ")
- end
-
- let(:x64_mingw_platforms) do
- x64_mingw_archs.join("\n ")
- end
-
- def x64_mingw_checksums(checksums)
- x64_mingw_archs.each do |arch|
- if arch == "x64-mingw-ucrt"
- checksums.no_checksum "platform_specific", "1.0", arch
- else
- checksums.checksum gem_repo2, "platform_specific", "1.0", arch
- end
- end
- end
-
it "should install runtime and development dependencies" do
build_lib("foo", path: tmp("foo")) do |s|
s.write("Gemfile", "source :rubygems\ngemspec")
@@ -154,7 +126,7 @@ RSpec.describe "bundle install from an existing gemspec" do
# ghost pass in future, and will only catch a regression if the message
# doesn't change. Exit codes should be used correctly (they can be more
# than just 0 and 1).
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
output = bundle("install", dir: tmp("foo"), artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s })
expect(output).not_to match(/You have added to the Gemfile/)
expect(output).not_to match(/You have deleted from the Gemfile/)
@@ -178,7 +150,7 @@ RSpec.describe "bundle install from an existing gemspec" do
end
it "should match a lockfile without needing to re-resolve with development dependencies" do
- simulate_platform java do
+ simulate_platform "java" do
build_lib("foo", path: tmp("foo")) do |s|
s.add_dependency "myrack"
s.add_development_dependency "thin"
@@ -220,7 +192,7 @@ RSpec.describe "bundle install from an existing gemspec" do
install_gemfile <<-G, raise_on_error: false
gemspec :path => '#{tmp("foo")}'
G
- expect(last_command.stdboth).not_to include("ahh")
+ expect(stdboth).not_to include("ahh")
end
it "allows the gemspec to activate other gems" do
@@ -288,6 +260,25 @@ RSpec.describe "bundle install from an existing gemspec" do
expect(out).to eq("WIN")
end
+ it "does not make Gem.try_activate warn when local gem has extensions" do
+ build_lib("foo", path: tmp("foo")) do |s|
+ s.version = "1.0.0"
+ s.add_c_extension
+ end
+ build_repo2
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gemspec :path => '#{tmp("foo")}'
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0.0"
+
+ run "Gem.try_activate('irb/lc/es/error.rb'); puts 'WIN'"
+ expect(out).to eq("WIN")
+ expect(err).to be_empty
+ end
+
it "handles downgrades" do
build_lib "omg", "2.0", path: lib_path("omg")
@@ -321,7 +312,7 @@ RSpec.describe "bundle install from an existing gemspec" do
s.add_dependency "activesupport", ">= 1.0.1"
end
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
bundle :install, raise_on_error: false
expect(err).to include("changed")
@@ -383,13 +374,13 @@ RSpec.describe "bundle install from an existing gemspec" do
myrack (1.0.0)
PLATFORMS
- #{generic_local_platform}
+ ruby
DEPENDENCIES
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -429,12 +420,13 @@ RSpec.describe "bundle install from an existing gemspec" do
end
build_lib "foo", path: bundled_app do |s|
- if platform_specific_type == :runtime
+ case platform_specific_type
+ when :runtime
s.add_runtime_dependency dependency
- elsif platform_specific_type == :development
+ when :development
s.add_development_dependency dependency
else
- raise "wrong dependency type #{platform_specific_type}, can only be :development or :runtime"
+ raise ArgumentError, "wrong dependency type #{platform_specific_type}, can only be :development or :runtime"
end
end
@@ -443,17 +435,18 @@ RSpec.describe "bundle install from an existing gemspec" do
gemspec
G
- bundle "config set --local force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
bundle "install"
simulate_new_machine
simulate_platform("jruby") { bundle "install" }
- simulate_platform(x64_mingw32) { bundle "install" }
+ expect(lockfile).to include("platform_specific (1.0-java)")
+ simulate_platform("x64-mingw-ucrt") { bundle "install" }
end
context "on ruby" do
before do
- bundle "config set --local force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
bundle :install
end
@@ -465,7 +458,7 @@ RSpec.describe "bundle install from an existing gemspec" do
c.no_checksum "foo", "1.0"
c.checksum gem_repo2, "platform_specific", "1.0"
c.checksum gem_repo2, "platform_specific", "1.0", "java"
- x64_mingw_checksums(c)
+ c.checksum gem_repo2, "platform_specific", "1.0", "x64-mingw-ucrt"
end
expect(lockfile).to eq <<~L
@@ -480,18 +473,18 @@ RSpec.describe "bundle install from an existing gemspec" do
specs:
platform_specific (1.0)
platform_specific (1.0-java)
- #{x64_mingw_gems}
+ platform_specific (1.0-x64-mingw-ucrt)
PLATFORMS
java
ruby
- #{x64_mingw_platforms}
+ x64-mingw-ucrt
DEPENDENCIES
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -506,7 +499,7 @@ RSpec.describe "bundle install from an existing gemspec" do
c.no_checksum "foo", "1.0"
c.checksum gem_repo2, "platform_specific", "1.0"
c.checksum gem_repo2, "platform_specific", "1.0", "java"
- x64_mingw_checksums(c)
+ c.checksum gem_repo2, "platform_specific", "1.0", "x64-mingw-ucrt"
end
expect(lockfile).to eq <<~L
@@ -520,19 +513,19 @@ RSpec.describe "bundle install from an existing gemspec" do
specs:
platform_specific (1.0)
platform_specific (1.0-java)
- #{x64_mingw_gems}
+ platform_specific (1.0-x64-mingw-ucrt)
PLATFORMS
java
ruby
- #{x64_mingw_platforms}
+ x64-mingw-ucrt
DEPENDENCIES
foo!
platform_specific
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -549,7 +542,7 @@ RSpec.describe "bundle install from an existing gemspec" do
c.checksum gem_repo2, "indirect_platform_specific", "1.0"
c.checksum gem_repo2, "platform_specific", "1.0"
c.checksum gem_repo2, "platform_specific", "1.0", "java"
- x64_mingw_checksums(c)
+ c.checksum gem_repo2, "platform_specific", "1.0", "x64-mingw-ucrt"
end
expect(lockfile).to eq <<~L
@@ -565,19 +558,19 @@ RSpec.describe "bundle install from an existing gemspec" do
platform_specific
platform_specific (1.0)
platform_specific (1.0-java)
- #{x64_mingw_gems}
+ platform_specific (1.0-x64-mingw-ucrt)
PLATFORMS
java
ruby
- #{x64_mingw_platforms}
+ x64-mingw-ucrt
DEPENDENCIES
foo!
indirect_platform_specific
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -595,7 +588,7 @@ RSpec.describe "bundle install from an existing gemspec" do
end
it "installs the ruby platform gemspec" do
- bundle "config set --local force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
install_gemfile <<-G
source "https://gem.repo1"
@@ -606,9 +599,9 @@ RSpec.describe "bundle install from an existing gemspec" do
end
it "installs the ruby platform gemspec and skips dev deps with `without development` configured" do
- bundle "config set --local force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
- bundle "config set --local without development"
+ bundle_config "without development"
install_gemfile <<-G
source "https://gem.repo1"
gemspec :path => '#{tmp("foo")}', :name => 'foo'
@@ -623,14 +616,14 @@ RSpec.describe "bundle install from an existing gemspec" do
before do
build_lib("chef", path: tmp("chef")) do |s|
s.version = "17.1.17"
- s.write "chef-universal-mingw32.gemspec", build_spec("chef", "17.1.17", "universal-mingw32") {|sw| sw.runtime "win32-api", "~> 1.5.3" }.first.to_ruby
+ s.write "chef-universal-mingw-ucrt.gemspec", build_spec("chef", "17.1.17", "universal-mingw-ucrt") {|sw| sw.runtime "win32-api", "~> 1.5.3" }.first.to_ruby
end
end
it "does not remove the platform specific specs from the lockfile when updating" do
build_repo4 do
build_gem "win32-api", "1.5.3" do |s|
- s.platform = "universal-mingw32"
+ s.platform = "universal-mingw-ucrt"
end
end
@@ -641,8 +634,8 @@ RSpec.describe "bundle install from an existing gemspec" do
checksums = checksums_section_when_enabled do |c|
c.no_checksum "chef", "17.1.17"
- c.no_checksum "chef", "17.1.17", "universal-mingw32"
- c.checksum gem_repo4, "win32-api", "1.5.3", "universal-mingw32"
+ c.no_checksum "chef", "17.1.17", "universal-mingw-ucrt"
+ c.checksum gem_repo4, "win32-api", "1.5.3", "universal-mingw-ucrt"
end
initial_lockfile = <<~L
@@ -650,24 +643,22 @@ RSpec.describe "bundle install from an existing gemspec" do
remote: ../chef
specs:
chef (17.1.17)
- chef (17.1.17-universal-mingw32)
+ chef (17.1.17-universal-mingw-ucrt)
win32-api (~> 1.5.3)
GEM
remote: https://gem.repo4/
specs:
- win32-api (1.5.3-universal-mingw32)
+ win32-api (1.5.3-universal-mingw-ucrt)
PLATFORMS
- ruby
- #{x64_mingw_platforms}
- x86-mingw32
+ #{lockfile_platforms("ruby", "x64-mingw-ucrt", "x86-mingw32")}
DEPENDENCIES
chef!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
lockfile initial_lockfile
@@ -730,7 +721,7 @@ RSpec.describe "bundle install from an existing gemspec" do
jruby-openssl
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
gemspec = tmp("activeadmin/activeadmin.gemspec")
diff --git a/spec/bundler/install/gemfile/git_spec.rb b/spec/bundler/install/gemfile/git_spec.rb
index ada74c311c..b2a82caf01 100644
--- a/spec/bundler/install/gemfile/git_spec.rb
+++ b/spec/bundler/install/gemfile/git_spec.rb
@@ -2,12 +2,8 @@
RSpec.describe "bundle install with git sources" do
describe "when floating on main" do
- before :each do
- build_git "foo" do |s|
- s.executables = "foobar"
- end
-
- install_gemfile <<-G
+ let(:base_gemfile) do
+ <<-G
source "https://gem.repo1"
git "#{lib_path("foo-1.0")}" do
gem 'foo'
@@ -15,7 +11,16 @@ RSpec.describe "bundle install with git sources" do
G
end
+ let(:install_base_gemfile) do
+ build_git "foo" do |s|
+ s.executables = "foobar"
+ end
+
+ install_gemfile base_gemfile
+ end
+
it "fetches gems" do
+ install_base_gemfile
expect(the_bundle).to include_gems("foo 1.0")
run <<-RUBY
@@ -26,20 +31,59 @@ RSpec.describe "bundle install with git sources" do
expect(out).to eq("WIN")
end
- it "caches the git repo", bundler: "< 3" do
- expect(Dir["#{default_bundle_path}/cache/bundler/git/foo-1.0-*"]).to have_attributes size: 1
+ it "does not (yet?) enforce CHECKSUMS" do
+ build_git "foo"
+ revision = revision_for(lib_path("foo-1.0"))
+
+ bundle_config "lockfile_checksums true"
+ gemfile base_gemfile
+
+ lockfile <<~L
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{revision}
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: https://gem.repo1/
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ CHECKSUMS
+ foo (1.0)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle_config "frozen true"
+
+ bundle "install"
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ it "caches the git repo" do
+ install_base_gemfile
+ expect(Dir["#{default_cache_path}/git/foo-1.0-*"]).to have_attributes size: 1
end
it "does not write to cache on bundler/setup" do
- cache_path = default_bundle_path("cache")
- FileUtils.rm_rf(cache_path)
+ install_base_gemfile
+ FileUtils.rm_r(default_cache_path)
ruby "require 'bundler/setup'"
- expect(cache_path).not_to exist
+ expect(default_cache_path).not_to exist
end
it "caches the git repo globally and properly uses the cached repo on the next invocation" do
- simulate_new_machine
- bundle "config set global_gem_cache true"
+ install_base_gemfile
+ pristine_system_gems
+ bundle_config "global_gem_cache true"
bundle :install
expect(Dir["#{home}/.bundle/cache/git/foo-1.0-*"]).to have_attributes size: 1
@@ -49,6 +93,7 @@ RSpec.describe "bundle install with git sources" do
end
it "caches the evaluated gemspec" do
+ install_base_gemfile
git = update_git "foo" do |s|
s.executables = ["foobar"] # we added this the first time, so keep it now
s.files = ["bin/foobar"] # updating git nukes the files list
@@ -67,6 +112,7 @@ RSpec.describe "bundle install with git sources" do
end
it "does not update the git source implicitly" do
+ install_base_gemfile
update_git "foo"
install_gemfile bundled_app2("Gemfile"), <<-G, dir: bundled_app2
@@ -85,6 +131,7 @@ RSpec.describe "bundle install with git sources" do
end
it "sets up git gem executables on the path" do
+ install_base_gemfile
bundle "exec foobar"
expect(out).to eq("1.0")
end
@@ -136,8 +183,8 @@ RSpec.describe "bundle install with git sources" do
end
it "still works after moving the application directory" do
- bundle "config set --local path vendor/bundle"
- bundle "install"
+ bundle_config "path vendor/bundle"
+ install_base_gemfile
FileUtils.mv bundled_app, tmp("bundled_app.bck")
@@ -145,8 +192,8 @@ RSpec.describe "bundle install with git sources" do
end
it "can still install after moving the application directory" do
- bundle "config set --local path vendor/bundle"
- bundle "install"
+ bundle_config "path vendor/bundle"
+ install_base_gemfile
FileUtils.mv bundled_app, tmp("bundled_app.bck")
@@ -334,7 +381,7 @@ RSpec.describe "bundle install with git sources" do
it "does not download random non-head refs" do
git("update-ref -m \"Bundler Spec!\" refs/bundler/1 main~1", lib_path("foo-1.0"))
- bundle "config set global_gem_cache true"
+ bundle_config "global_gem_cache true"
install_gemfile <<-G
source "https://gem.repo1"
@@ -431,7 +478,7 @@ RSpec.describe "bundle install with git sources" do
update_git("foo", path: repo, tag: tag)
install_gemfile <<-G
- source "https://gem.repo1"
+ source "https://gem.repo1"
git "#{repo}", :tag => #{tag.dump} do
gem "foo"
end
@@ -1031,7 +1078,7 @@ RSpec.describe "bundle install with git sources" do
expect(out).to eq("WIN")
end
- it "does not to a remote fetch if the revision is cached locally" do
+ it "does not do a remote fetch if the revision is cached locally" do
build_git "foo"
install_gemfile <<-G
@@ -1039,7 +1086,7 @@ RSpec.describe "bundle install with git sources" do
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
- FileUtils.rm_rf(lib_path("foo-1.0"))
+ FileUtils.rm_r(lib_path("foo-1.0"))
bundle "install"
expect(out).not_to match(/updating/i)
@@ -1152,6 +1199,30 @@ RSpec.describe "bundle install with git sources" do
expect(the_bundle).to include_gem "rails 7.1.4", "activesupport 7.1.4"
end
+
+ it "doesn't explode when adding an explicit ref to a git gem with dependencies" do
+ lib_root = lib_path("rails")
+
+ build_lib "activesupport", "7.1.4", path: lib_root.join("activesupport")
+ build_git "rails", "7.1.4", path: lib_root do |s|
+ s.add_dependency "activesupport", "= 7.1.4"
+ end
+
+ old_revision = revision_for(lib_root)
+ update_git "rails", "7.1.4", path: lib_root
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "rails", "7.1.4", :git => "#{lib_root}"
+ G
+
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "rails", :git => "#{lib_root}", :ref => "#{old_revision}"
+ G
+
+ expect(the_bundle).to include_gem "rails 7.1.4", "activesupport 7.1.4"
+ end
end
describe "bundle install after the remote has been updated" do
@@ -1218,9 +1289,9 @@ RSpec.describe "bundle install with git sources" do
gem "valim", "= 1.0", :git => "#{lib_path("valim")}"
G
- simulate_new_machine
+ pristine_system_gems
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
bundle :install
end
end
@@ -1333,7 +1404,7 @@ RSpec.describe "bundle install with git sources" do
File.open(git_reader.path.join("ext/foo.c"), "w") do |file|
file.write <<-C
#include "ruby.h"
- VALUE foo() { return INT2FIX(#{i}); }
+ VALUE foo(VALUE self) { return INT2FIX(#{i}); }
void Init_foo() { rb_define_global_function("foo", &foo, 0); }
C
end
@@ -1567,7 +1638,7 @@ In Gemfile:
rake!
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
with_path_as("") do
@@ -1603,9 +1674,9 @@ In Gemfile:
gem 'foo'
end
G
- bundle "config set --global path vendor/bundle"
+ bundle_config_global "path vendor/bundle"
bundle :install
- simulate_new_machine
+ pristine_system_gems
bundle "install", env: { "PATH" => "" }
expect(out).to_not include("You need to install git to be able to use gems from git repositories.")
@@ -1614,7 +1685,7 @@ In Gemfile:
describe "when the git source is overridden with a local git repo" do
before do
- bundle "config set --global local.foo #{lib_path("foo")}"
+ bundle_config_global "local.foo #{lib_path("foo")}"
end
describe "and git output is colorized" do
@@ -1650,7 +1721,7 @@ In Gemfile:
end
G
- expect(last_command.stdboth).to_not include("password1")
+ expect(stdboth).to_not include("password1")
expect(out).to include("Fetching https://user1@github.com/company/private-repo")
end
end
@@ -1666,7 +1737,7 @@ In Gemfile:
end
G
- expect(last_command.stdboth).to_not include("oauth_token")
+ expect(stdboth).to_not include("oauth_token")
expect(out).to include("Fetching https://x-oauth-basic@github.com/company/private-repo")
end
end
diff --git a/spec/bundler/install/gemfile/groups_spec.rb b/spec/bundler/install/gemfile/groups_spec.rb
index 71871899a2..4013b112ec 100644
--- a/spec/bundler/install/gemfile/groups_spec.rb
+++ b/spec/bundler/install/gemfile/groups_spec.rb
@@ -25,7 +25,7 @@ RSpec.describe "bundle install with groups" do
puts ACTIVESUPPORT
R
- expect(err_without_deprecations).to eq("ZOMG LOAD ERROR")
+ expect(err_without_deprecations).to match(/cannot load such file -- activesupport/)
end
it "installs gems with inline :groups into those groups" do
@@ -36,7 +36,7 @@ RSpec.describe "bundle install with groups" do
puts THIN
R
- expect(err_without_deprecations).to eq("ZOMG LOAD ERROR")
+ expect(err_without_deprecations).to match(/cannot load such file -- thin/)
end
it "sets up everything if Bundler.setup is used with no groups" do
@@ -57,7 +57,7 @@ RSpec.describe "bundle install with groups" do
puts THIN
RUBY
- expect(err_without_deprecations).to eq("ZOMG LOAD ERROR")
+ expect(err_without_deprecations).to match(/cannot load such file -- thin/)
end
it "sets up old groups when they have previously been removed" do
@@ -86,13 +86,13 @@ RSpec.describe "bundle install with groups" do
end
it "installs gems in the default group" do
- bundle "config set --local without emo"
+ bundle_config "without emo"
bundle :install
expect(the_bundle).to include_gems "myrack 1.0.0", groups: [:default]
end
it "respects global `without` configuration, but does not save it locally" do
- bundle "config set --global without emo"
+ bundle_config_global "without emo"
bundle :install
expect(the_bundle).to include_gems "myrack 1.0.0", groups: [:default]
bundle "config list"
@@ -100,34 +100,27 @@ RSpec.describe "bundle install with groups" do
expect(out).to include("Set for the current user (#{home(".bundle/config")}): [:emo]")
end
- it "allows running application where groups where configured by a different user", bundler: "< 3" do
- bundle "config set without emo"
+ it "allows running application where groups where configured by a different user" do
+ bundle_config "without emo"
bundle :install
bundle "exec ruby -e 'puts 42'", env: { "BUNDLE_USER_HOME" => tmp("new_home").to_s }
expect(out).to include("42")
end
it "does not install gems from the excluded group" do
- bundle "config set --local without emo"
+ bundle_config "without emo"
bundle :install
expect(the_bundle).not_to include_gems "activesupport 2.3.5", groups: [:default]
end
- it "remembers previous exclusion with `--without`", bundler: "< 3" do
- bundle "install --without emo"
- expect(the_bundle).not_to include_gems "activesupport 2.3.5"
- bundle :install
- expect(the_bundle).not_to include_gems "activesupport 2.3.5"
- end
-
it "does not say it installed gems from the excluded group" do
- bundle "config set --local without emo"
+ bundle_config "without emo"
bundle :install
expect(out).not_to include("activesupport")
end
it "allows Bundler.setup for specific groups" do
- bundle "config set --local without emo"
+ bundle_config "without emo"
bundle :install
run("require 'myrack'; puts MYRACK", :default)
expect(out).to eq("1.0.0")
@@ -142,7 +135,7 @@ RSpec.describe "bundle install with groups" do
end
G
- bundle "config set --local without emo"
+ bundle_config "without emo"
bundle :install
expect(the_bundle).to include_gems "activesupport 2.3.2", groups: [:default]
end
@@ -159,34 +152,13 @@ RSpec.describe "bundle install with groups" do
ENV["BUNDLE_WITHOUT"] = nil
end
- it "clears --without when passed an empty list", bundler: "< 3" do
- bundle "install --without emo"
-
- bundle "install --without ''"
- expect(the_bundle).to include_gems "activesupport 2.3.5"
- end
-
- it "doesn't clear without when nothing is passed", bundler: "< 3" do
- bundle "install --without emo"
-
- bundle :install
- expect(the_bundle).not_to include_gems "activesupport 2.3.5"
- end
-
it "does not install gems from the optional group" do
bundle :install
expect(the_bundle).not_to include_gems "thin 1.0"
end
it "installs gems from the optional group when requested" do
- bundle "config set --local with debugging"
- bundle :install
- expect(the_bundle).to include_gems "thin 1.0"
- end
-
- it "installs gems from the previously requested group", bundler: "< 3" do
- bundle "install --with debugging"
- expect(the_bundle).to include_gems "thin 1.0"
+ bundle_config "with debugging"
bundle :install
expect(the_bundle).to include_gems "thin 1.0"
end
@@ -198,30 +170,6 @@ RSpec.describe "bundle install with groups" do
ENV["BUNDLE_WITH"] = nil
end
- it "clears --with when passed an empty list", bundler: "< 3" do
- bundle "install --with debugging"
- bundle "install --with ''"
- expect(the_bundle).not_to include_gems "thin 1.0"
- end
-
- it "removes groups from without when passed at --with", bundler: "< 3" do
- bundle "config set --local without emo"
- bundle "install --with emo"
- expect(the_bundle).to include_gems "activesupport 2.3.5"
- end
-
- it "removes groups from with when passed at --without", bundler: "< 3" do
- bundle "config set --local with debugging"
- bundle "install --without debugging", raise_on_error: false
- expect(the_bundle).not_to include_gem "thin 1.0"
- end
-
- it "errors out when passing a group to with and without via CLI flags", bundler: "< 3" do
- bundle "install --with emo debugging --without emo", raise_on_error: false
- expect(last_command).to be_failure
- expect(err).to include("The offending groups are: emo")
- end
-
it "allows the BUNDLE_WITH setting to override BUNDLE_WITHOUT" do
ENV["BUNDLE_WITH"] = "debugging"
@@ -235,20 +183,14 @@ RSpec.describe "bundle install with groups" do
expect(the_bundle).to include_gem "thin 1.0"
end
- it "can add and remove a group at the same time", bundler: "< 3" do
- bundle "install --with debugging --without emo"
- expect(the_bundle).to include_gems "thin 1.0"
- expect(the_bundle).not_to include_gems "activesupport 2.3.5"
- end
-
it "has no effect when listing a not optional group in with" do
- bundle "config set --local with emo"
+ bundle_config "with emo"
bundle :install
expect(the_bundle).to include_gems "activesupport 2.3.5"
end
it "has no effect when listing an optional group in without" do
- bundle "config set --local without debugging"
+ bundle_config "without debugging"
bundle :install
expect(the_bundle).not_to include_gems "thin 1.0"
end
@@ -266,13 +208,13 @@ RSpec.describe "bundle install with groups" do
end
it "installs gems in the default group" do
- bundle "config set --local without emo lolercoaster"
+ bundle_config "without emo lolercoaster"
bundle :install
expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "installs the gem if any of its groups are installed" do
- bundle "config set --local without emo"
+ bundle_config "without emo"
bundle :install
expect(the_bundle).to include_gems "myrack 1.0.0", "activesupport 2.3.5"
end
@@ -294,15 +236,15 @@ RSpec.describe "bundle install with groups" do
end
it "installs the gem unless all groups are excluded" do
- bundle "config set --local without emo"
+ bundle_config "without emo"
bundle :install
expect(the_bundle).to include_gems "activesupport 2.3.5"
- bundle "config set --local without lolercoaster"
+ bundle_config "without lolercoaster"
bundle :install
expect(the_bundle).to include_gems "activesupport 2.3.5"
- bundle "config set --local without emo lolercoaster"
+ bundle_config "without emo lolercoaster"
bundle :install
expect(the_bundle).not_to include_gems "activesupport 2.3.5"
@@ -327,13 +269,13 @@ RSpec.describe "bundle install with groups" do
end
it "installs gems in the default group" do
- bundle "config set --local without emo lolercoaster"
+ bundle_config "without emo lolercoaster"
bundle :install
expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "installs the gem if any of its groups are installed" do
- bundle "config set --local without emo"
+ bundle_config "without emo"
bundle :install
expect(the_bundle).to include_gems "myrack 1.0.0", "activesupport 2.3.5"
end
@@ -365,13 +307,13 @@ RSpec.describe "bundle install with groups" do
end
end
- describe "when locked and installed with `without` option" do
+ describe "when locked and installed with `without` setting" do
before(:each) do
build_repo2
system_gems "myrack-0.9.1"
- bundle "config set --local without myrack"
+ bundle_config "without myrack"
install_gemfile <<-G
source "https://gem.repo2"
gem "myrack"
@@ -382,7 +324,7 @@ RSpec.describe "bundle install with groups" do
G
end
- it "uses the correct versions even if --without was used on the original" do
+ it "uses versions from excluded gems in a machine without the without configuration" do
expect(the_bundle).to include_gems "myrack 0.9.1"
expect(the_bundle).not_to include_gems "myrack_middleware 1.0"
simulate_new_machine
@@ -394,10 +336,10 @@ RSpec.describe "bundle install with groups" do
end
it "does not hit the remote a second time" do
- FileUtils.rm_rf gem_repo2
- bundle "config set --local without myrack"
+ FileUtils.rm_r gem_repo2
+ bundle_config "without myrack"
bundle :install, verbose: true
- expect(last_command.stdboth).not_to match(/fetching/i)
+ expect(stdboth).not_to match(/fetching/i)
end
end
end
diff --git a/spec/bundler/install/gemfile/install_if_spec.rb b/spec/bundler/install/gemfile/install_if_spec.rb
index 170b58c4c0..05a6d15129 100644
--- a/spec/bundler/install/gemfile/install_if_spec.rb
+++ b/spec/bundler/install/gemfile/install_if_spec.rb
@@ -45,7 +45,7 @@ RSpec.describe "bundle install with install_if conditionals" do
thin
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
diff --git a/spec/bundler/install/gemfile/lockfile_spec.rb b/spec/bundler/install/gemfile/lockfile_spec.rb
index f80b21e562..19bd7074b2 100644
--- a/spec/bundler/install/gemfile/lockfile_spec.rb
+++ b/spec/bundler/install/gemfile/lockfile_spec.rb
@@ -7,12 +7,8 @@ RSpec.describe "bundle install with a lockfile present" do
gem "myrack", "1.0.0"
G
- subject do
- install_gemfile(gf)
- end
-
it "touches the lockfile on install even when nothing has changed" do
- subject
+ install_gemfile(gf)
expect { bundle :install }.to change { bundled_app_lock.mtime }
end
@@ -21,32 +17,25 @@ RSpec.describe "bundle install with a lockfile present" do
context "with plugins disabled" do
before do
- bundle "config set plugins false"
- subject
+ bundle_config "plugins false"
end
- it "does not evaluate the gemfile twice" do
+ it "does not evaluate the gemfile twice when the gem is already installed" do
+ install_gemfile(gf)
bundle :install
with_env_vars("BUNDLER_SPEC_NO_APPEND" => "1") { expect(the_bundle).to include_gem "myrack 1.0.0" }
- # The first eval is from the initial install, we're testing that the
- # second install doesn't double-eval
expect(bundled_app("evals").read.lines.to_a.size).to eq(2)
end
- context "when the gem is not installed" do
- before { FileUtils.rm_rf bundled_app(".bundle") }
-
- it "does not evaluate the gemfile twice" do
- bundle :install
+ it "does not evaluate the gemfile twice when the gem is not installed" do
+ gemfile(gf)
+ bundle :install
- with_env_vars("BUNDLER_SPEC_NO_APPEND" => "1") { expect(the_bundle).to include_gem "myrack 1.0.0" }
+ with_env_vars("BUNDLER_SPEC_NO_APPEND" => "1") { expect(the_bundle).to include_gem "myrack 1.0.0" }
- # The first eval is from the initial install, we're testing that the
- # second install doesn't double-eval
- expect(bundled_app("evals").read.lines.to_a.size).to eq(2)
- end
+ expect(bundled_app("evals").read.lines.to_a.size).to eq(1)
end
end
end
diff --git a/spec/bundler/install/gemfile/override_spec.rb b/spec/bundler/install/gemfile/override_spec.rb
new file mode 100644
index 0000000000..02b0e7d772
--- /dev/null
+++ b/spec/bundler/install/gemfile/override_spec.rb
@@ -0,0 +1,401 @@
+# frozen_string_literal: true
+
+RSpec.describe "override DSL" do
+ context "with a version: string operation" do
+ it "replaces a direct dependency requirement with the override version spec" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ override "myrack", version: "= 0.9.1"
+ gem "myrack"
+ G
+
+ expect(the_bundle).to include_gems "myrack 0.9.1"
+ end
+
+ it "replaces a transitive dependency requirement" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ override "myrack", version: "= 1.0.0"
+ gem "myrack_middleware"
+ G
+
+ expect(the_bundle).to include_gems "myrack 1.0.0", "myrack_middleware 1.0"
+ end
+
+ it "replaces the requirement even when the Gemfile pins a different version" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ override "myrack", version: "= 0.9.1"
+ gem "myrack", "= 1.0.0"
+ G
+
+ expect(the_bundle).to include_gems "myrack 0.9.1"
+ end
+
+ it "applies the override against an existing lockfile" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ override "myrack", version: "= 0.9.1"
+ gem "myrack"
+ G
+
+ bundle :install
+
+ expect(the_bundle).to include_gems "myrack 0.9.1"
+ end
+
+ it "pins a prerelease version that the Gemfile dependency would otherwise filter out" do
+ build_repo2 do
+ build_gem "has_prerelease", "1.0"
+ build_gem "has_prerelease", "1.1.pre"
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ override "has_prerelease", version: "= 1.1.pre"
+ gem "has_prerelease"
+ G
+
+ expect(the_bundle).to include_gems "has_prerelease 1.1.pre"
+ end
+ end
+
+ context "with a version: :ignore_upper operation" do
+ it "strips a < upper bound on a direct dependency" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ override "myrack", version: :ignore_upper
+ gem "myrack", "< 1.0"
+ G
+
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+
+ it "folds ~> into >= so newer versions become reachable" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ override "myrack", version: :ignore_upper
+ gem "myrack", "~> 0.9.1"
+ G
+
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+ end
+
+ context "with a version: nil operation" do
+ it "drops a direct dependency's pin entirely" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ override "myrack", version: nil
+ gem "myrack", "= 0.9.1"
+ G
+
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+
+ it "drops a transitive dependency's pin entirely" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ override "myrack", version: nil
+ gem "myrack_middleware"
+ G
+
+ expect(the_bundle).to include_gems "myrack 1.0.0", "myrack_middleware 1.0"
+ end
+
+ it "applies a transitive-only override against an existing lockfile" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack_middleware"
+ G
+
+ expect(the_bundle).to include_gems "myrack 0.9.1", "myrack_middleware 1.0"
+
+ gemfile <<-G
+ source "https://gem.repo1"
+ override "myrack", version: "= 1.0.0"
+ gem "myrack_middleware"
+ G
+
+ bundle :install
+
+ expect(the_bundle).to include_gems "myrack 1.0.0", "myrack_middleware 1.0"
+ end
+ end
+
+ context "lockfile contents" do
+ it "does not record the override directive in Gemfile.lock" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ override "myrack", version: "= 0.9.1"
+ gem "myrack"
+ G
+
+ expect(lockfile).not_to match(/override/i)
+ end
+ end
+
+ context "with a required_ruby_version: operation" do
+ it "lets the resolver pick a gem whose required_ruby_version excludes the current Ruby with :ignore_upper" do
+ build_repo2 do
+ build_gem "needs_old_ruby", "1.0" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ override "needs_old_ruby", required_ruby_version: :ignore_upper
+ gem "needs_old_ruby"
+ G
+
+ bundle :lock
+ expect(lockfile).to include("needs_old_ruby (1.0)")
+ end
+
+ it "lets the resolver pick the gem with required_ruby_version: nil" do
+ build_repo2 do
+ build_gem "needs_old_ruby", "1.0" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ override "needs_old_ruby", required_ruby_version: nil
+ gem "needs_old_ruby"
+ G
+
+ bundle :lock
+ expect(lockfile).to include("needs_old_ruby (1.0)")
+ end
+
+ it "applies to a transitive dependency's required_ruby_version" do
+ build_repo2 do
+ build_gem "needs_old_ruby", "1.0" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ build_gem "wraps_old", "1.0" do |s|
+ s.add_dependency "needs_old_ruby"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ override "needs_old_ruby", required_ruby_version: :ignore_upper
+ gem "wraps_old"
+ G
+
+ bundle :lock
+ expect(lockfile).to include("needs_old_ruby (1.0)")
+ expect(lockfile).to include("wraps_old (1.0)")
+ end
+
+ it "re-resolves a direct dep when a metadata override is added against an existing lockfile" do
+ build_repo2 do
+ build_gem "selectable", "1.0"
+ build_gem "selectable", "2.0" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "selectable"
+ G
+
+ bundle :lock
+ expect(lockfile).to include("selectable (1.0)")
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ override "selectable", required_ruby_version: :ignore_upper
+ gem "selectable"
+ G
+
+ bundle :lock
+ expect(lockfile).to include("selectable (2.0)")
+ end
+ end
+
+ context "with a required_rubygems_version: operation" do
+ it "lets the resolver pick a gem whose required_rubygems_version excludes the current RubyGems with :ignore_upper" do
+ build_repo2 do
+ build_gem "needs_old_rubygems", "1.0" do |s|
+ s.required_rubygems_version = "< #{Gem.rubygems_version}"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ override "needs_old_rubygems", required_rubygems_version: :ignore_upper
+ gem "needs_old_rubygems"
+ G
+
+ bundle :lock
+ expect(lockfile).to include("needs_old_rubygems (1.0)")
+ end
+ end
+
+ context "with an :all target" do
+ it "applies required_ruby_version: :ignore_upper to every gem" do
+ build_repo2 do
+ build_gem "needs_old_ruby_a", "1.0" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ build_gem "needs_old_ruby_b", "1.0" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ override :all, required_ruby_version: :ignore_upper
+ gem "needs_old_ruby_a"
+ gem "needs_old_ruby_b"
+ G
+
+ bundle :lock
+ expect(lockfile).to include("needs_old_ruby_a (1.0)")
+ expect(lockfile).to include("needs_old_ruby_b (1.0)")
+ end
+
+ it "is overridden by a per-gem override on the same field" do
+ build_repo2 do
+ build_gem "permissive", "1.0" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ build_gem "still_blocked", "1.0" do |s|
+ s.required_ruby_version = "= #{Gem.ruby_version}.999"
+ end
+ end
+
+ # :all says ignore_upper (would unblock both), but per-gem on
+ # still_blocked nails it to a hard requirement that still fails.
+ gemfile <<-G
+ source "https://gem.repo2"
+ override :all, required_ruby_version: :ignore_upper
+ override "still_blocked", required_ruby_version: "= #{Gem.ruby_version}.999"
+ gem "permissive"
+ gem "still_blocked"
+ G
+
+ bundle :lock, raise_on_error: false
+ expect(err).to include("still_blocked")
+ end
+
+ it "preserves locked versions when an :all metadata override is added without bundle update" do
+ build_repo2 do
+ build_gem "selectable", "1.0"
+ build_gem "selectable", "2.0" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "selectable"
+ G
+
+ bundle :lock
+ expect(lockfile).to include("selectable (1.0)")
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ override :all, required_ruby_version: :ignore_upper
+ gem "selectable"
+ G
+
+ # :all override alone does not pre-unlock locked specs; narrow change
+ # should not trigger unrelated lockfile churn.
+ bundle :lock
+ expect(lockfile).to include("selectable (1.0)")
+
+ # bundle update opts the user into re-resolution under the override.
+ bundle "update selectable"
+ expect(lockfile).to include("selectable (2.0)")
+ end
+ end
+
+ context "diagnostic on resolve failure" do
+ it "lists active overrides with their Gemfile location" do
+ build_repo2 do
+ build_gem "needs_old_ruby", "1.0" do |s|
+ s.required_ruby_version = "= #{Gem.ruby_version}.999"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ override "needs_old_ruby", required_ruby_version: "= #{Gem.ruby_version}.999"
+ gem "needs_old_ruby"
+ G
+
+ bundle :lock, raise_on_error: false
+ expect(err).to include("Bundler applied the following overrides")
+ expect(err).to include("override \"needs_old_ruby\", required_ruby_version:")
+ expect(err).to match(/declared at Gemfile:\d+/)
+ end
+ end
+
+ context "install-time compatibility" do
+ it "installs a gem whose required_ruby_version excludes the current Ruby when an override removes the constraint" do
+ build_repo2 do
+ build_gem "needs_old_ruby", "1.0" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ override "needs_old_ruby", required_ruby_version: nil
+ gem "needs_old_ruby"
+ G
+
+ expect(the_bundle).to include_gems "needs_old_ruby 1.0"
+ end
+
+ it "installs a gem whose required_rubygems_version excludes the current RubyGems when an override removes it" do
+ build_repo2 do
+ build_gem "needs_old_rubygems", "1.0" do |s|
+ s.required_rubygems_version = "< #{Gem.rubygems_version}"
+ end
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ override "needs_old_rubygems", required_rubygems_version: nil
+ gem "needs_old_rubygems"
+ G
+
+ expect(the_bundle).to include_gems "needs_old_rubygems 1.0"
+ end
+
+ it "installs every gem when :all required_ruby_version override is in effect" do
+ build_repo2 do
+ build_gem "needs_old_ruby_a", "1.0" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ build_gem "needs_old_ruby_b", "1.0" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ override :all, required_ruby_version: :ignore_upper
+ gem "needs_old_ruby_a"
+ gem "needs_old_ruby_b"
+ G
+
+ expect(the_bundle).to include_gems "needs_old_ruby_a 1.0", "needs_old_ruby_b 1.0"
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/path_spec.rb b/spec/bundler/install/gemfile/path_spec.rb
index 7525404b24..b069488531 100644
--- a/spec/bundler/install/gemfile/path_spec.rb
+++ b/spec/bundler/install/gemfile/path_spec.rb
@@ -1,22 +1,10 @@
# frozen_string_literal: true
RSpec.describe "bundle install with explicit source paths" do
- it "fetches gems with a global path source", bundler: "< 3" do
- build_lib "foo"
-
- install_gemfile <<-G
- path "#{lib_path("foo-1.0")}"
- gem 'foo'
- G
-
- expect(the_bundle).to include_gems("foo 1.0")
- end
-
it "fetches gems" do
build_lib "foo"
install_gemfile <<-G
- source "https://gem.repo1"
path "#{lib_path("foo-1.0")}" do
gem 'foo'
end
@@ -29,7 +17,6 @@ RSpec.describe "bundle install with explicit source paths" do
build_lib "foo"
install_gemfile <<-G
- source "https://gem.repo1"
gem 'foo', :path => "#{lib_path("foo-1.0")}"
G
@@ -42,7 +29,6 @@ RSpec.describe "bundle install with explicit source paths" do
relative_path = lib_path("foo-1.0").relative_path_from(bundled_app)
install_gemfile <<-G
- source "https://gem.repo1"
gem 'foo', :path => "#{relative_path}"
G
@@ -55,7 +41,6 @@ RSpec.describe "bundle install with explicit source paths" do
relative_path = lib_path("foo-1.0").relative_path_from(Pathname.new("~").expand_path)
install_gemfile <<-G
- source "https://gem.repo1"
gem 'foo', :path => "~/#{relative_path}"
G
@@ -70,7 +55,6 @@ RSpec.describe "bundle install with explicit source paths" do
relative_path = lib_path("foo-1.0").relative_path_from(Pathname.new("/home/#{username}").expand_path)
install_gemfile <<-G, raise_on_error: false
- source "https://gem.repo1"
gem 'foo', :path => "~#{username}/#{relative_path}"
G
expect(err).to match("There was an error while trying to use the path `~#{username}/#{relative_path}`.")
@@ -81,7 +65,6 @@ RSpec.describe "bundle install with explicit source paths" do
build_lib "foo", path: bundled_app("foo-1.0")
install_gemfile <<-G
- source "https://gem.repo1"
gem 'foo', :path => "./foo-1.0"
G
@@ -126,7 +109,7 @@ RSpec.describe "bundle install with explicit source paths" do
demo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle :install, dir: lib_path("demo")
@@ -139,11 +122,10 @@ RSpec.describe "bundle install with explicit source paths" do
build_lib "foo", path: bundled_app("foo-1.0")
install_gemfile <<-G
- source "https://gem.repo1"
gem 'foo', :path => File.expand_path("foo-1.0", __dir__)
G
- bundle "config set --local frozen true"
+ bundle_config "frozen true"
bundle :install
end
@@ -159,7 +141,6 @@ RSpec.describe "bundle install with explicit source paths" do
end
install_gemfile <<-G
- source "https://gem.repo1"
gem "foo", :path => "#{lib_path("nested")}"
G
@@ -179,7 +160,6 @@ RSpec.describe "bundle install with explicit source paths" do
build_lib "foo", "1.0.0", path: lib_path("omg/foo")
install_gemfile <<-G
- source "https://gem.repo1"
gem "omg", :path => "#{lib_path("omg")}"
G
@@ -256,7 +236,6 @@ RSpec.describe "bundle install with explicit source paths" do
build_lib "omg", "2.0", path: lib_path("omg")
install_gemfile <<-G
- source "https://gem.repo1"
gem "omg", :path => "#{lib_path("omg")}"
G
@@ -280,7 +259,6 @@ RSpec.describe "bundle install with explicit source paths" do
end
install_gemfile <<-G
- source "https://gem.repo1"
gem "premailer", :path => "#{lib_path("premailer")}"
G
@@ -302,11 +280,9 @@ RSpec.describe "bundle install with explicit source paths" do
end
install_gemfile <<-G, raise_on_error: false
- source "https://gem.repo1"
gem "foo", :path => "#{lib_path("foo-1.0")}"
G
- expect(err).to_not include("Your Gemfile has no gem server sources.")
expect(err).to match(/is not valid. Please fix this gemspec./)
expect(err).to match(/The validation error was 'missing value for attribute version'/)
expect(err).to match(/You have one or more invalid gemspecs that need to be fixed/)
@@ -370,7 +346,7 @@ RSpec.describe "bundle install with explicit source paths" do
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
lockfile lockfile_path, original_lockfile
@@ -440,7 +416,6 @@ RSpec.describe "bundle install with explicit source paths" do
end
install_gemfile <<-G, raise_on_error: false
- source "https://gem.repo1"
gemspec :path => "#{lib_path("foo")}"
G
@@ -454,7 +429,6 @@ RSpec.describe "bundle install with explicit source paths" do
end
install_gemfile <<-G
- source "https://gem.repo1"
gemspec :path => "#{lib_path("foo")}", :name => "foo"
G
@@ -467,7 +441,6 @@ RSpec.describe "bundle install with explicit source paths" do
end
install_gemfile <<-G, verbose: true
- source "https://gem.repo1"
path "#{lib_path("foo-1.0")}" do
gem 'foo'
end
@@ -481,11 +454,10 @@ RSpec.describe "bundle install with explicit source paths" do
it "handles directories in bin/" do
build_lib "foo"
- lib_path("foo-1.0").join("foo.gemspec").rmtree
+ FileUtils.rm_rf lib_path("foo-1.0").join("foo.gemspec")
lib_path("foo-1.0").join("bin/performance").mkpath
install_gemfile <<-G
- source "https://gem.repo1"
gem 'foo', '1.0', :path => "#{lib_path("foo-1.0")}"
G
expect(err).to be_empty
@@ -495,7 +467,6 @@ RSpec.describe "bundle install with explicit source paths" do
build_lib "foo"
install_gemfile <<-G
- source "https://gem.repo1"
gem 'foo', :path => "#{lib_path("foo-1.0")}"
G
@@ -508,7 +479,6 @@ RSpec.describe "bundle install with explicit source paths" do
build_lib "hi2u"
install_gemfile <<-G
- source "https://gem.repo1"
path "#{lib_path}" do
gem "omg"
gem "hi2u"
@@ -527,7 +497,6 @@ RSpec.describe "bundle install with explicit source paths" do
end
install_gemfile <<-G
- source "https://gem.repo1"
gem "foo", :path => "#{lib_path("foo")}"
gem "omg", :path => "#{lib_path("omg")}"
G
@@ -539,7 +508,6 @@ RSpec.describe "bundle install with explicit source paths" do
build_lib "foo", gemspec: false
gemfile <<-G
- source "https://gem.repo1"
gem "foo", "1.0", :path => "#{lib_path("foo-1.0")}"
G
@@ -553,15 +521,11 @@ RSpec.describe "bundle install with explicit source paths" do
PATH
remote: vendor/bar
specs:
-
- GEM
- remote: http://rubygems.org/
L
FileUtils.mkdir_p(bundled_app("vendor/bar"))
install_gemfile <<-G
- source "http://rubygems.org"
gem "bar", "1.0.0", path: "vendor/bar", require: "bar/nyard"
G
end
@@ -606,7 +570,6 @@ RSpec.describe "bundle install with explicit source paths" do
end
install_gemfile <<-G
- source "https://gem.repo1"
gem "foo", :path => "#{lib_path("foo-1.0")}"
G
@@ -622,7 +585,6 @@ RSpec.describe "bundle install with explicit source paths" do
build_lib "bar", "1.0", path: lib_path("foo/bar")
install_gemfile <<-G
- source "https://gem.repo1"
gem "foo", :path => "#{lib_path("foo")}"
G
end
@@ -699,7 +661,7 @@ RSpec.describe "bundle install with explicit source paths" do
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
build_lib "foo", "1.0", path: lib_path("foo") do |s|
@@ -727,7 +689,7 @@ RSpec.describe "bundle install with explicit source paths" do
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
expect(the_bundle).to include_gems "myrack 0.9.1"
@@ -766,7 +728,7 @@ RSpec.describe "bundle install with explicit source paths" do
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
build_lib "foo", "1.0", path: lib_path("foo") do |s|
@@ -799,7 +761,7 @@ RSpec.describe "bundle install with explicit source paths" do
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
expect(the_bundle).to include_gems "myrack 0.9.1"
@@ -827,7 +789,7 @@ RSpec.describe "bundle install with explicit source paths" do
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "lock"
@@ -853,11 +815,60 @@ RSpec.describe "bundle install with explicit source paths" do
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
end
+ context "when platform specific version locked, and having less dependencies that the generic version that's actually installed" do
+ before do
+ build_repo4 do
+ build_gem "racc", "1.8.1"
+ build_gem "mini_portile2", "2.8.2"
+ end
+
+ build_lib "nokogiri", "1.18.9", path: lib_path("nokogiri") do |s|
+ s.add_dependency "mini_portile2", "~> 2.8.2"
+ s.add_dependency "racc", "~> 1.4"
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "nokogiri", path: "#{lib_path("nokogiri")}"
+ G
+
+ lockfile <<~L
+ PATH
+ remote: #{lib_path("nokogiri")}
+ specs:
+ nokogiri (1.18.9)
+ mini_portile2 (~> 2.8.2)
+ racc (~> 1.4)
+ nokogiri (1.18.9-arm64-darwin)
+ racc (~> 1.4)
+
+ GEM
+ remote: https://rubygems.org/
+ specs:
+ racc (1.8.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ nokogiri!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "works" do
+ bundle "install"
+ end
+ end
+
describe "switching sources" do
it "doesn't switch pinned git sources to rubygems when pinning the parent gem to a path source" do
build_gem "foo", "1.0", to_system: true do |s|
@@ -869,12 +880,10 @@ RSpec.describe "bundle install with explicit source paths" do
end
install_gemfile <<-G
- source "https://gem.repo1"
gem "bar", :git => "#{lib_path("bar")}"
G
install_gemfile <<-G
- source "https://gem.repo1"
gem "bar", :path => "#{lib_path("bar")}"
G
@@ -929,7 +938,6 @@ RSpec.describe "bundle install with explicit source paths" do
it "runs pre-install hooks" do
build_git "foo"
gemfile <<-G
- source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -949,7 +957,6 @@ RSpec.describe "bundle install with explicit source paths" do
it "runs post-install hooks" do
build_git "foo"
gemfile <<-G
- source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -969,7 +976,6 @@ RSpec.describe "bundle install with explicit source paths" do
it "complains if the install hook fails" do
build_git "foo"
gemfile <<-G
- source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
@@ -1000,7 +1006,6 @@ RSpec.describe "bundle install with explicit source paths" do
end
install_gemfile <<-G
- source "https://gem.repo1"
gem "foo", :path => "#{lib_path("foo-1.0")}"
gem "bar", :path => "#{lib_path("bar-1.0")}"
G
diff --git a/spec/bundler/install/gemfile/platform_spec.rb b/spec/bundler/install/gemfile/platform_spec.rb
index 1e536e6052..e12933ebcf 100644
--- a/spec/bundler/install/gemfile/platform_spec.rb
+++ b/spec/bundler/install/gemfile/platform_spec.rb
@@ -65,7 +65,7 @@ RSpec.describe "bundle install across platforms" do
platform_specific
G
- bundle "config set --local frozen true"
+ bundle_config "frozen true"
install_gemfile <<-G
source "https://gem.repo1"
@@ -161,8 +161,8 @@ RSpec.describe "bundle install across platforms" do
expect(the_bundle).to include_gems "nokogiri 1.4.2 java", "weakling 0.0.3"
- simulate_new_machine
- bundle "config set --local force_ruby_platform true"
+ pristine_system_gems
+ bundle_config "force_ruby_platform true"
bundle "install"
expect(the_bundle).to include_gems "nokogiri 1.4.2"
@@ -195,7 +195,7 @@ RSpec.describe "bundle install across platforms" do
build_gem("ffi", "1.9.23")
end
- simulate_platform java do
+ simulate_platform "java" do
install_gemfile <<-G
source "https://gem.repo4"
@@ -235,7 +235,7 @@ RSpec.describe "bundle install across platforms" do
pry
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "lock --add-platform ruby"
@@ -269,7 +269,7 @@ RSpec.describe "bundle install across platforms" do
pry
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
expect(lockfile).to eq good_lockfile
@@ -330,7 +330,7 @@ RSpec.describe "bundle install across platforms" do
end
it "works with gems with platform-specific dependency having different requirements order" do
- simulate_platform x64_mac do
+ simulate_platform "x86_64-darwin-15" do
update_repo2 do
build_gem "fspath", "3"
build_gem "image_optim_pack", "1.2.3" do |s|
@@ -365,7 +365,7 @@ RSpec.describe "bundle install across platforms" do
gem "myrack", "1.0.0"
G
- bundle "config set --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle :install
FileUtils.mv(vendored_gems, bundled_app("vendor/bundle", Gem.ruby_engine, "1.8"))
@@ -394,7 +394,7 @@ RSpec.describe "bundle install across platforms" do
#{checksums}
G
- bundle "config set --local force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
install_gemfile <<-G
source "https://gem.repo1"
@@ -420,7 +420,7 @@ RSpec.describe "bundle install across platforms" do
platform_specific
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
end
@@ -486,7 +486,7 @@ RSpec.describe "bundle install with platform conditionals" do
tzinfo (~> 1.2)
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "install --verbose"
@@ -542,7 +542,7 @@ RSpec.describe "bundle install with platform conditionals" do
end
it "does not attempt to install gems from :rbx when using --local" do
- bundle "config set --local force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
gemfile <<-G
source "https://gem.repo1"
@@ -554,7 +554,7 @@ RSpec.describe "bundle install with platform conditionals" do
end
it "does not attempt to install gems from other rubies when using --local" do
- bundle "config set --local force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
gemfile <<-G
source "https://gem.repo1"
gem "some_gem", platform: :ruby_22
@@ -565,12 +565,12 @@ RSpec.describe "bundle install with platform conditionals" do
end
it "does not print a warning when a dependency is unused on a platform different from the current one" do
- bundle "config set --local force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
gemfile <<-G
source "https://gem.repo1"
- gem "myrack", :platform => [:windows, :mswin, :mswin64, :mingw, :x64_mingw, :jruby]
+ gem "myrack", :platform => [:windows, :jruby]
G
bundle "install"
@@ -589,12 +589,12 @@ RSpec.describe "bundle install with platform conditionals" do
myrack
#{checksums_section_when_enabled}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
it "resolves fine when a dependency is unused on a platform different from the current one, but reintroduced transitively" do
- bundle "config set --local force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
build_repo4 do
build_gem "listen", "3.7.1" do |s|
@@ -616,7 +616,7 @@ end
RSpec.describe "when a gem has no architecture" do
it "still installs correctly" do
- simulate_platform x86_mswin32 do
+ simulate_platform "x86-mswin32" do
build_repo2 do
# The rcov gem is platform mswin32, but has no arch
build_gem "rcov" do |s|
diff --git a/spec/bundler/install/gemfile/ruby_spec.rb b/spec/bundler/install/gemfile/ruby_spec.rb
index 3e15d82bbe..d937abd714 100644
--- a/spec/bundler/install/gemfile/ruby_spec.rb
+++ b/spec/bundler/install/gemfile/ruby_spec.rb
@@ -83,10 +83,10 @@ RSpec.describe "ruby requirement" do
myrack
RUBY VERSION
- ruby 2.1.4p422
+ ruby 2.1.4
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
diff --git a/spec/bundler/install/gemfile/sources_spec.rb b/spec/bundler/install/gemfile/sources_spec.rb
index 84af5c0d06..654d638e1f 100644
--- a/spec/bundler/install/gemfile/sources_spec.rb
+++ b/spec/bundler/install/gemfile/sources_spec.rb
@@ -4,153 +4,6 @@ RSpec.describe "bundle install with gems on multiple sources" do
# repo1 is built automatically before all of the specs run
# it contains myrack-obama 1.0.0 and myrack 0.9.1 & 1.0.0 amongst other gems
- context "without source affinity" do
- before do
- # Oh no! Someone evil is trying to hijack myrack :(
- # need this to be broken to check for correct source ordering
- build_repo3 do
- build_gem "myrack", repo3_myrack_version do |s|
- s.write "lib/myrack.rb", "MYRACK = 'FAIL'"
- end
- end
- end
-
- context "with multiple toplevel sources" do
- let(:repo3_myrack_version) { "1.0.0" }
-
- before do
- gemfile <<-G
- source "https://gem.repo3"
- source "https://gem.repo1"
- gem "myrack-obama"
- gem "myrack"
- G
- end
-
- it "refuses to install mismatched checksum because one gem has been tampered with", bundler: "< 3" do
- lockfile <<~L
- GEM
- remote: https://gem.repo3/
- remote: https://gem.repo1/
- specs:
- myrack (1.0.0)
-
- PLATFORMS
- #{local_platform}
-
- DEPENDENCIES
- depends_on_myrack!
-
- BUNDLED WITH
- #{Bundler::VERSION}
- L
-
- bundle :install, artifice: "compact_index", raise_on_error: false
-
- expect(exitstatus).to eq(37)
- expect(err).to eq <<~E.strip
- [DEPRECATED] Your Gemfile contains multiple global sources. Using `source` more than once without a block is a security risk, and may result in installing unexpected gems. To resolve this warning, use a block to indicate which gems should come from the secondary source.
- Bundler found mismatched checksums. This is a potential security risk.
- #{checksum_to_lock(gem_repo1, "myrack", "1.0.0")}
- from the API at https://gem.repo1/
- #{checksum_to_lock(gem_repo3, "myrack", "1.0.0")}
- from the API at https://gem.repo3/
-
- Mismatched checksums each have an authoritative source:
- 1. the API at https://gem.repo1/
- 2. the API at https://gem.repo3/
- You may need to alter your Gemfile sources to resolve this issue.
-
- To ignore checksum security warnings, disable checksum validation with
- `bundle config set --local disable_checksum_validation true`
- E
- end
-
- context "when checksum validation is disabled" do
- before do
- bundle "config set --local disable_checksum_validation true"
- end
-
- it "warns about ambiguous gems, but installs anyway, prioritizing sources last to first", bundler: "< 3" do
- bundle :install, artifice: "compact_index"
-
- expect(err).to include("Warning: the gem 'myrack' was found in multiple sources.")
- expect(err).to include("Installed from: https://gem.repo1")
- expect(the_bundle).to include_gems("myrack-obama 1.0.0", "myrack 1.0.0", source: "remote1")
- end
-
- it "does not use the full index unnecessarily", bundler: "< 3" do
- bundle :install, artifice: "compact_index", verbose: true
-
- expect(out).to include("https://gem.repo1/versions")
- expect(out).to include("https://gem.repo3/versions")
- expect(out).not_to include("https://gem.repo1/quick/Marshal.4.8/")
- expect(out).not_to include("https://gem.repo3/quick/Marshal.4.8/")
- end
-
- it "fails", bundler: "3" do
- bundle :install, artifice: "compact_index", raise_on_error: false
- expect(err).to include("Each source after the first must include a block")
- expect(exitstatus).to eq(4)
- end
- end
- end
-
- context "when different versions of the same gem are in multiple sources" do
- let(:repo3_myrack_version) { "1.2" }
-
- before do
- gemfile <<-G
- source "https://gem.repo3"
- source "https://gem.repo1"
- gem "myrack-obama"
- gem "myrack", "1.0.0" # force it to install the working version in repo1
- G
- end
-
- it "warns about ambiguous gems, but installs anyway", bundler: "< 3" do
- bundle :install, artifice: "compact_index"
- expect(err).to include("Warning: the gem 'myrack' was found in multiple sources.")
- expect(err).to include("Installed from: https://gem.repo1")
- expect(the_bundle).to include_gems("myrack-obama 1.0.0", "myrack 1.0.0", source: "remote1")
- end
-
- it "fails", bundler: "3" do
- bundle :install, artifice: "compact_index", raise_on_error: false
- expect(err).to include("Each source after the first must include a block")
- expect(exitstatus).to eq(4)
- end
- end
- end
-
- context "without source affinity, and a stdlib gem present in one of the sources", :ruby_repo do
- let(:default_json_version) { ruby "gem 'json'; require 'json'; puts JSON::VERSION" }
-
- before do
- build_repo2 do
- build_gem "json", default_json_version
- end
-
- build_repo4 do
- build_gem "foo" do |s|
- s.add_dependency "json", default_json_version
- end
- end
-
- gemfile <<-G
- source "https://gem.repo2"
- source "https://gem.repo4"
-
- gem "foo"
- G
- end
-
- it "works in standalone mode", bundler: "< 3" do
- gem_checksum = checksum_digest(gem_repo4, "foo", "1.0")
- bundle "install --standalone", artifice: "compact_index", env: { "BUNDLER_SPEC_FOO_CHECKSUM" => gem_checksum }
- end
- end
-
context "with source affinity" do
context "with sources given by a block" do
before do
@@ -189,7 +42,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
expect(bundled_app("vendor/cache/myrack-1.0.0.gem")).to exist
expect(bundled_app("vendor/cache/myrack-obama-1.0.gem")).to exist
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
bundle :install, artifice: "compact_index"
expect(the_bundle).to include_gems("myrack-obama 1.0.0", "myrack 1.0.0")
@@ -273,7 +126,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
expect(the_bundle).to include_gems("depends_on_myrack 1.0.1", "myrack 1.0.0", source: "remote3")
# In https://github.com/bundler/bundler/issues/3585 this failed
- # when there is already a lock file, and the gems are missing, so try again
+ # when there is already a lockfile, and the gems are missing, so try again
system_gems []
bundle :install, artifice: "compact_index"
@@ -313,189 +166,6 @@ RSpec.describe "bundle install with gems on multiple sources" do
expect(the_bundle).to include_gems("depends_on_myrack 1.0.1", "myrack 1.0.0")
end
end
-
- context "and in yet another source" do
- before do
- gemfile <<-G
- source "https://gem.repo1"
- source "https://gem.repo2"
- source "https://gem.repo3" do
- gem "depends_on_myrack"
- end
- G
- end
-
- it "fails when the two sources don't have the same checksum", bundler: "< 3" do
- bundle :install, artifice: "compact_index", raise_on_error: false
-
- expect(err).to eq(<<~E.strip)
- [DEPRECATED] Your Gemfile contains multiple global sources. Using `source` more than once without a block is a security risk, and may result in installing unexpected gems. To resolve this warning, use a block to indicate which gems should come from the secondary source.
- Bundler found mismatched checksums. This is a potential security risk.
- #{checksum_to_lock(gem_repo2, "myrack", "1.0.0")}
- from the API at https://gem.repo2/
- #{checksum_to_lock(gem_repo1, "myrack", "1.0.0")}
- from the API at https://gem.repo1/
-
- Mismatched checksums each have an authoritative source:
- 1. the API at https://gem.repo2/
- 2. the API at https://gem.repo1/
- You may need to alter your Gemfile sources to resolve this issue.
-
- To ignore checksum security warnings, disable checksum validation with
- `bundle config set --local disable_checksum_validation true`
- E
- expect(exitstatus).to eq(37)
- end
-
- it "fails when the two sources agree, but the local gem calculates a different checksum", bundler: "< 3" do
- myrack_checksum = "c0ffee11" * 8
- bundle :install, artifice: "compact_index", env: { "BUNDLER_SPEC_MYRACK_CHECKSUM" => myrack_checksum }, raise_on_error: false
-
- expect(err).to eq(<<~E.strip)
- [DEPRECATED] Your Gemfile contains multiple global sources. Using `source` more than once without a block is a security risk, and may result in installing unexpected gems. To resolve this warning, use a block to indicate which gems should come from the secondary source.
- Bundler found mismatched checksums. This is a potential security risk.
- myrack (1.0.0) sha256=#{myrack_checksum}
- from the API at https://gem.repo2/
- and the API at https://gem.repo1/
- #{checksum_to_lock(gem_repo2, "myrack", "1.0.0")}
- from the gem at #{default_bundle_path("cache", "myrack-1.0.0.gem")}
-
- If you trust the API at https://gem.repo2/, to resolve this issue you can:
- 1. remove the gem at #{default_bundle_path("cache", "myrack-1.0.0.gem")}
- 2. run `bundle install`
-
- To ignore checksum security warnings, disable checksum validation with
- `bundle config set --local disable_checksum_validation true`
- E
- expect(exitstatus).to eq(37)
- end
-
- it "installs from the other source and warns about ambiguous gems when the sources have the same checksum", bundler: "< 3" do
- gem_checksum = checksum_digest(gem_repo2, "myrack", "1.0.0")
- bundle :install, artifice: "compact_index", env: { "BUNDLER_SPEC_MYRACK_CHECKSUM" => gem_checksum, "DEBUG" => "1" }
-
- expect(err).to include("Warning: the gem 'myrack' was found in multiple sources.")
- expect(err).to include("Installed from: https://gem.repo2")
-
- checksums = checksums_section_when_enabled do |c|
- c.checksum gem_repo3, "depends_on_myrack", "1.0.1"
- c.checksum gem_repo2, "myrack", "1.0.0"
- end
-
- expect(lockfile).to eq <<~L
- GEM
- remote: https://gem.repo1/
- remote: https://gem.repo2/
- specs:
- myrack (1.0.0)
-
- GEM
- remote: https://gem.repo3/
- specs:
- depends_on_myrack (1.0.1)
- myrack
-
- PLATFORMS
- #{lockfile_platforms}
-
- DEPENDENCIES
- depends_on_myrack!
- #{checksums}
- BUNDLED WITH
- #{Bundler::VERSION}
- L
-
- previous_lockfile = lockfile
- expect(the_bundle).to include_gems("depends_on_myrack 1.0.1", "myrack 1.0.0")
- expect(lockfile).to eq(previous_lockfile)
- end
-
- it "installs from the other source and warns about ambiguous gems when checksum validation is disabled", bundler: "< 3" do
- bundle "config set --local disable_checksum_validation true"
- bundle :install, artifice: "compact_index"
-
- expect(err).to include("Warning: the gem 'myrack' was found in multiple sources.")
- expect(err).to include("Installed from: https://gem.repo2")
-
- checksums = checksums_section_when_enabled do |c|
- c.no_checksum "depends_on_myrack", "1.0.1"
- c.no_checksum "myrack", "1.0.0"
- end
-
- expect(lockfile).to eq <<~L
- GEM
- remote: https://gem.repo1/
- remote: https://gem.repo2/
- specs:
- myrack (1.0.0)
-
- GEM
- remote: https://gem.repo3/
- specs:
- depends_on_myrack (1.0.1)
- myrack
-
- PLATFORMS
- #{lockfile_platforms}
-
- DEPENDENCIES
- depends_on_myrack!
- #{checksums}
- BUNDLED WITH
- #{Bundler::VERSION}
- L
-
- previous_lockfile = lockfile
- expect(the_bundle).to include_gems("depends_on_myrack 1.0.1", "myrack 1.0.0")
- expect(lockfile).to eq(previous_lockfile)
- end
-
- it "fails", bundler: "3" do
- bundle :install, artifice: "compact_index", raise_on_error: false
- expect(err).to include("Each source after the first must include a block")
- expect(exitstatus).to eq(4)
- end
- end
-
- context "and only the dependency is pinned" do
- before do
- # need this to be broken to check for correct source ordering
- build_repo gem_repo2 do
- build_gem "myrack", "1.0.0" do |s|
- s.write "lib/myrack.rb", "MYRACK = 'FAIL'"
- end
- end
-
- gemfile <<-G
- source "https://gem.repo3" # contains depends_on_myrack
- source "https://gem.repo2" # contains broken myrack
-
- gem "depends_on_myrack" # installed from gem_repo3
- gem "myrack", :source => "https://gem.repo1"
- G
- end
-
- it "installs the dependency from the pinned source without warning", bundler: "< 3" do
- bundle :install, artifice: "compact_index"
-
- expect(err).not_to include("Warning: the gem 'myrack' was found in multiple sources.")
- expect(the_bundle).to include_gems("depends_on_myrack 1.0.1", "myrack 1.0.0")
-
- # In https://github.com/rubygems/bundler/issues/3585 this failed
- # when there is already a lock file, and the gems are missing, so try again
- system_gems []
- bundle :install, artifice: "compact_index"
-
- expect(err).not_to include("Warning: the gem 'myrack' was found in multiple sources.")
- expect(the_bundle).to include_gems("depends_on_myrack 1.0.1", "myrack 1.0.0")
- end
-
- it "fails", bundler: "3" do
- bundle :install, artifice: "compact_index", raise_on_error: false
- expect(err).to include("Each source after the first must include a block")
- expect(exitstatus).to eq(4)
- end
- end
end
context "when a top-level gem can only be found in an scoped source" do
@@ -524,39 +194,6 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
end
- context "when an indirect dependency can't be found in the aggregate rubygems source", bundler: "< 3" do
- before do
- build_repo2
-
- build_repo3 do
- build_gem "depends_on_missing", "1.0.1" do |s|
- s.add_dependency "missing"
- end
- end
-
- gemfile <<-G
- source "https://gem.repo2"
-
- source "https://gem.repo3"
-
- gem "depends_on_missing"
- G
- end
-
- it "fails" do
- bundle :install, artifice: "compact_index", raise_on_error: false
- expect(err).to end_with <<~E.strip
- Could not find compatible versions
-
- Because every version of depends_on_missing depends on missing >= 0
- and missing >= 0 could not be found in any of the sources,
- depends_on_missing cannot be used.
- So, because Gemfile depends on depends_on_missing >= 0,
- version solving has failed.
- E
- end
- end
-
context "when a top-level gem has an indirect dependency" do
before do
build_repo gem_repo2 do
@@ -714,337 +351,6 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
end
- context "when the lockfile has aggregated rubygems sources and newer versions of dependencies are available" do
- before do
- build_repo gem_repo2 do
- build_gem "activesupport", "6.0.3.4" do |s|
- s.add_dependency "concurrent-ruby", "~> 1.0", ">= 1.0.2"
- s.add_dependency "i18n", ">= 0.7", "< 2"
- s.add_dependency "minitest", "~> 5.1"
- s.add_dependency "tzinfo", "~> 1.1"
- s.add_dependency "zeitwerk", "~> 2.2", ">= 2.2.2"
- end
-
- build_gem "activesupport", "6.1.2.1" do |s|
- s.add_dependency "concurrent-ruby", "~> 1.0", ">= 1.0.2"
- s.add_dependency "i18n", ">= 1.6", "< 2"
- s.add_dependency "minitest", ">= 5.1"
- s.add_dependency "tzinfo", "~> 2.0"
- s.add_dependency "zeitwerk", "~> 2.3"
- end
-
- build_gem "concurrent-ruby", "1.1.8"
- build_gem "concurrent-ruby", "1.1.9"
- build_gem "connection_pool", "2.2.3"
-
- build_gem "i18n", "1.8.9" do |s|
- s.add_dependency "concurrent-ruby", "~> 1.0"
- end
-
- build_gem "minitest", "5.14.3"
- build_gem "myrack", "2.2.3"
- build_gem "redis", "4.2.5"
-
- build_gem "sidekiq", "6.1.3" do |s|
- s.add_dependency "connection_pool", ">= 2.2.2"
- s.add_dependency "myrack", "~> 2.0"
- s.add_dependency "redis", ">= 4.2.0"
- end
-
- build_gem "thread_safe", "0.3.6"
-
- build_gem "tzinfo", "1.2.9" do |s|
- s.add_dependency "thread_safe", "~> 0.1"
- end
-
- build_gem "tzinfo", "2.0.4" do |s|
- s.add_dependency "concurrent-ruby", "~> 1.0"
- end
-
- build_gem "zeitwerk", "2.4.2"
- end
-
- 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"
- end
- end
-
- gemfile <<-G
- # frozen_string_literal: true
-
- source "https://gem.repo2"
-
- gem "activesupport"
-
- source "https://gem.repo3" do
- gem "sidekiq-pro"
- end
- G
-
- @locked_checksums = checksums_section_when_enabled do |c|
- c.checksum gem_repo2, "activesupport", "6.0.3.4"
- c.checksum gem_repo2, "concurrent-ruby", "1.1.8"
- c.checksum gem_repo2, "connection_pool", "2.2.3"
- c.checksum gem_repo2, "i18n", "1.8.9"
- c.checksum gem_repo2, "minitest", "5.14.3"
- c.checksum gem_repo2, "myrack", "2.2.3"
- c.checksum gem_repo2, "redis", "4.2.5"
- c.checksum gem_repo2, "sidekiq", "6.1.3"
- c.checksum gem_repo3, "sidekiq-pro", "5.2.1"
- c.checksum gem_repo2, "thread_safe", "0.3.6"
- c.checksum gem_repo2, "tzinfo", "1.2.9"
- c.checksum gem_repo2, "zeitwerk", "2.4.2"
- end
-
- lockfile <<~L
- GEM
- remote: https://gem.repo2/
- remote: https://gem.repo3/
- specs:
- activesupport (6.0.3.4)
- concurrent-ruby (~> 1.0, >= 1.0.2)
- i18n (>= 0.7, < 2)
- minitest (~> 5.1)
- tzinfo (~> 1.1)
- zeitwerk (~> 2.2, >= 2.2.2)
- concurrent-ruby (1.1.8)
- connection_pool (2.2.3)
- i18n (1.8.9)
- concurrent-ruby (~> 1.0)
- minitest (5.14.3)
- myrack (2.2.3)
- redis (4.2.5)
- sidekiq (6.1.3)
- connection_pool (>= 2.2.2)
- myrack (~> 2.0)
- redis (>= 4.2.0)
- sidekiq-pro (5.2.1)
- connection_pool (>= 2.2.3)
- sidekiq (>= 6.1.0)
- thread_safe (0.3.6)
- tzinfo (1.2.9)
- thread_safe (~> 0.1)
- zeitwerk (2.4.2)
-
- PLATFORMS
- #{lockfile_platforms}
-
- DEPENDENCIES
- activesupport
- sidekiq-pro!
- #{@locked_checksums}
- BUNDLED WITH
- #{Bundler::VERSION}
- L
- end
-
- it "does not install newer versions but updates the lockfile format when running bundle install in non frozen mode, and doesn't warn" do
- bundle :install, artifice: "compact_index"
- expect(err).to be_empty
-
- expect(the_bundle).to include_gems("activesupport 6.0.3.4")
- expect(the_bundle).not_to include_gems("activesupport 6.1.2.1")
- expect(the_bundle).to include_gems("tzinfo 1.2.9")
- expect(the_bundle).not_to include_gems("tzinfo 2.0.4")
- expect(the_bundle).to include_gems("concurrent-ruby 1.1.8")
- expect(the_bundle).not_to include_gems("concurrent-ruby 1.1.9")
-
- expect(lockfile).to eq <<~L
- GEM
- remote: https://gem.repo2/
- specs:
- activesupport (6.0.3.4)
- concurrent-ruby (~> 1.0, >= 1.0.2)
- i18n (>= 0.7, < 2)
- minitest (~> 5.1)
- tzinfo (~> 1.1)
- zeitwerk (~> 2.2, >= 2.2.2)
- concurrent-ruby (1.1.8)
- connection_pool (2.2.3)
- i18n (1.8.9)
- concurrent-ruby (~> 1.0)
- minitest (5.14.3)
- myrack (2.2.3)
- redis (4.2.5)
- sidekiq (6.1.3)
- connection_pool (>= 2.2.2)
- myrack (~> 2.0)
- redis (>= 4.2.0)
- thread_safe (0.3.6)
- tzinfo (1.2.9)
- thread_safe (~> 0.1)
- zeitwerk (2.4.2)
-
- GEM
- remote: https://gem.repo3/
- specs:
- sidekiq-pro (5.2.1)
- connection_pool (>= 2.2.3)
- sidekiq (>= 6.1.0)
-
- PLATFORMS
- #{lockfile_platforms}
-
- DEPENDENCIES
- activesupport
- sidekiq-pro!
- #{@locked_checksums}
- BUNDLED WITH
- #{Bundler::VERSION}
- L
- end
-
- it "does not install newer versions or generate lockfile changes when running bundle install in frozen mode, and warns", bundler: "< 3" do
- initial_lockfile = lockfile
-
- bundle "config set --local frozen true"
- bundle :install, artifice: "compact_index"
-
- expect(err).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure.")
-
- expect(the_bundle).to include_gems("activesupport 6.0.3.4")
- expect(the_bundle).not_to include_gems("activesupport 6.1.2.1")
- expect(the_bundle).to include_gems("tzinfo 1.2.9")
- expect(the_bundle).not_to include_gems("tzinfo 2.0.4")
- expect(the_bundle).to include_gems("concurrent-ruby 1.1.8")
- expect(the_bundle).not_to include_gems("concurrent-ruby 1.1.9")
-
- expect(lockfile).to eq(initial_lockfile)
- end
-
- it "fails when running bundle install in frozen mode", bundler: "3" do
- initial_lockfile = lockfile
-
- bundle "config set --local frozen true"
- bundle :install, artifice: "compact_index", raise_on_error: false
-
- expect(err).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure.")
-
- expect(lockfile).to eq(initial_lockfile)
- end
-
- it "splits sections and upgrades gems when running bundle update, and doesn't warn" do
- bundle "update --all", artifice: "compact_index"
- expect(err).to be_empty
-
- expect(the_bundle).not_to include_gems("activesupport 6.0.3.4")
- expect(the_bundle).to include_gems("activesupport 6.1.2.1")
- @locked_checksums.checksum gem_repo2, "activesupport", "6.1.2.1"
-
- expect(the_bundle).not_to include_gems("tzinfo 1.2.9")
- expect(the_bundle).to include_gems("tzinfo 2.0.4")
- @locked_checksums.checksum gem_repo2, "tzinfo", "2.0.4"
- @locked_checksums.delete "thread_safe"
-
- expect(the_bundle).not_to include_gems("concurrent-ruby 1.1.8")
- expect(the_bundle).to include_gems("concurrent-ruby 1.1.9")
- @locked_checksums.checksum gem_repo2, "concurrent-ruby", "1.1.9"
-
- expect(lockfile).to eq <<~L
- GEM
- remote: https://gem.repo2/
- specs:
- activesupport (6.1.2.1)
- concurrent-ruby (~> 1.0, >= 1.0.2)
- i18n (>= 1.6, < 2)
- minitest (>= 5.1)
- tzinfo (~> 2.0)
- zeitwerk (~> 2.3)
- concurrent-ruby (1.1.9)
- connection_pool (2.2.3)
- i18n (1.8.9)
- concurrent-ruby (~> 1.0)
- minitest (5.14.3)
- myrack (2.2.3)
- redis (4.2.5)
- sidekiq (6.1.3)
- connection_pool (>= 2.2.2)
- myrack (~> 2.0)
- redis (>= 4.2.0)
- tzinfo (2.0.4)
- concurrent-ruby (~> 1.0)
- zeitwerk (2.4.2)
-
- GEM
- remote: https://gem.repo3/
- specs:
- sidekiq-pro (5.2.1)
- connection_pool (>= 2.2.3)
- sidekiq (>= 6.1.0)
-
- PLATFORMS
- #{lockfile_platforms}
-
- DEPENDENCIES
- activesupport
- sidekiq-pro!
- #{@locked_checksums}
- BUNDLED WITH
- #{Bundler::VERSION}
- L
- end
-
- it "upgrades the lockfile format and upgrades the requested gem when running bundle update with an argument" do
- bundle "update concurrent-ruby", artifice: "compact_index"
- expect(err).to be_empty
-
- expect(the_bundle).to include_gems("activesupport 6.0.3.4")
- expect(the_bundle).not_to include_gems("activesupport 6.1.2.1")
- expect(the_bundle).to include_gems("tzinfo 1.2.9")
- expect(the_bundle).not_to include_gems("tzinfo 2.0.4")
- expect(the_bundle).to include_gems("concurrent-ruby 1.1.9")
- expect(the_bundle).not_to include_gems("concurrent-ruby 1.1.8")
-
- @locked_checksums.checksum gem_repo2, "concurrent-ruby", "1.1.9"
-
- expect(lockfile).to eq <<~L
- GEM
- remote: https://gem.repo2/
- specs:
- activesupport (6.0.3.4)
- concurrent-ruby (~> 1.0, >= 1.0.2)
- i18n (>= 0.7, < 2)
- minitest (~> 5.1)
- tzinfo (~> 1.1)
- zeitwerk (~> 2.2, >= 2.2.2)
- concurrent-ruby (1.1.9)
- connection_pool (2.2.3)
- i18n (1.8.9)
- concurrent-ruby (~> 1.0)
- minitest (5.14.3)
- myrack (2.2.3)
- redis (4.2.5)
- sidekiq (6.1.3)
- connection_pool (>= 2.2.2)
- myrack (~> 2.0)
- redis (>= 4.2.0)
- thread_safe (0.3.6)
- tzinfo (1.2.9)
- thread_safe (~> 0.1)
- zeitwerk (2.4.2)
-
- GEM
- remote: https://gem.repo3/
- specs:
- sidekiq-pro (5.2.1)
- connection_pool (>= 2.2.3)
- sidekiq (>= 6.1.0)
-
- PLATFORMS
- #{lockfile_platforms}
-
- DEPENDENCIES
- activesupport
- sidekiq-pro!
- #{@locked_checksums}
- BUNDLED WITH
- #{Bundler::VERSION}
- L
- end
- end
-
context "when a top-level gem has an indirect dependency present in the default source, but with a different version from the one resolved" do
before do
build_lib "activesupport", "7.0.0.alpha", path: lib_path("rails/activesupport")
@@ -1134,7 +440,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
nokogiri
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "install --verbose", artifice: "compact_index"
@@ -1207,112 +513,6 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
end
- context "with a lockfile with aggregated rubygems sources" do
- let(:aggregate_gem_section_lockfile) do
- <<~L
- GEM
- remote: https://gem.repo1/
- remote: https://gem.repo3/
- specs:
- myrack (0.9.1)
-
- PLATFORMS
- #{lockfile_platforms}
-
- DEPENDENCIES
- myrack!
- #{checksums_section}
- BUNDLED WITH
- #{Bundler::VERSION}
- L
- end
-
- let(:split_gem_section_lockfile) do
- <<~L
- GEM
- remote: https://gem.repo1/
- specs:
-
- GEM
- remote: https://gem.repo3/
- specs:
- myrack (0.9.1)
-
- PLATFORMS
- #{lockfile_platforms}
-
- DEPENDENCIES
- myrack!
-
- BUNDLED WITH
- #{Bundler::VERSION}
- L
- end
-
- before do
- build_repo3 do
- build_gem "myrack", "0.9.1"
- end
-
- gemfile <<-G
- source "https://gem.repo1"
- source "https://gem.repo3" do
- gem 'myrack'
- end
- G
-
- lockfile aggregate_gem_section_lockfile
- end
-
- it "installs the existing lockfile but prints a warning when checksum validation is disabled", bundler: "< 3" do
- bundle "config set --local deployment true"
- bundle "config set --local disable_checksum_validation true"
-
- bundle "install", artifice: "compact_index"
-
- expect(lockfile).to eq(aggregate_gem_section_lockfile)
- expect(err).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure.")
- expect(the_bundle).to include_gems("myrack 0.9.1", source: "remote3")
- end
-
- it "prints a checksum warning when the checksums from both sources do not match", bundler: "< 3" do
- bundle "config set --local deployment true"
-
- bundle "install", artifice: "compact_index", raise_on_error: false
-
- api_checksum1 = checksum_digest(gem_repo1, "myrack", "0.9.1")
- api_checksum3 = checksum_digest(gem_repo3, "myrack", "0.9.1")
-
- expect(exitstatus).to eq(37)
- expect(err).to eq(<<~E.strip)
- [DEPRECATED] Your lockfile contains a single rubygems source section with multiple remotes, which is insecure. Make sure you run `bundle install` in non frozen mode and commit the result to make your lockfile secure.
- Bundler found mismatched checksums. This is a potential security risk.
- myrack (0.9.1) sha256=#{api_checksum3}
- from the API at https://gem.repo3/
- myrack (0.9.1) sha256=#{api_checksum1}
- from the API at https://gem.repo1/
-
- Mismatched checksums each have an authoritative source:
- 1. the API at https://gem.repo3/
- 2. the API at https://gem.repo1/
- You may need to alter your Gemfile sources to resolve this issue.
-
- To ignore checksum security warnings, disable checksum validation with
- `bundle config set --local disable_checksum_validation true`
- E
- end
-
- it "refuses to install the existing lockfile and prints an error", bundler: "3" do
- bundle "config set --local deployment true"
-
- bundle "install", artifice: "compact_index", raise_on_error: false
-
- expect(lockfile).to eq(aggregate_gem_section_lockfile)
- expect(err).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure.")
- expect(out).to be_empty
- end
- end
-
context "with a path gem in the same Gemfile" do
before do
build_lib "foo"
@@ -1366,11 +566,11 @@ RSpec.describe "bundle install with gems on multiple sources" do
gem 'bar', '~> 0.1', :source => 'https://gem.repo4'
G
- bundle "config set --local path ../gems/system"
+ bundle_config "path ../gems/system"
bundle :install, artifice: "compact_index"
# And then we add some new versions...
- update_repo4 do
+ build_repo4 do
build_gem "foo", "0.2"
build_gem "bar", "0.3"
end
@@ -1583,38 +783,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
expect(err).to include("Could not reach host gem.repo4. Check your network connection and try again.")
end
- context "when an indirect dependency is available from multiple ambiguous sources", bundler: "< 3" do
- it "succeeds but warns, suggesting a source block" do
- build_repo4 do
- build_gem "depends_on_myrack" do |s|
- s.add_dependency "myrack"
- end
- build_gem "myrack"
- end
-
- install_gemfile <<-G, artifice: "compact_index_extra_api", raise_on_error: false
- source "https://global.source"
-
- source "https://scoped.source/extra" do
- gem "depends_on_myrack"
- end
-
- source "https://scoped.source" do
- gem "thin"
- end
- G
- expect(err).to eq <<~EOS.strip
- Warning: The gem 'myrack' was found in multiple relevant sources.
- * rubygems repository https://scoped.source/
- * rubygems repository https://scoped.source/extra/
- You should add this gem to the source block for the source you wish it to be installed from.
- EOS
- expect(last_command).to be_success
- expect(the_bundle).to be_locked
- end
- end
-
- context "when an indirect dependency is available from multiple ambiguous sources", bundler: "3" do
+ context "when an indirect dependency is available from multiple ambiguous sources" do
it "raises, suggesting a source block" do
build_repo4 do
build_gem "depends_on_myrack" do |s|
@@ -1645,83 +814,6 @@ RSpec.describe "bundle install with gems on multiple sources" do
end
end
- context "when upgrading a lockfile suffering from dependency confusion" do
- before do
- build_repo4 do
- build_gem "mime-types", "3.0.0"
- end
-
- build_repo2 do
- build_gem "capybara", "2.5.0" do |s|
- s.add_dependency "mime-types", ">= 1.16"
- end
-
- build_gem "mime-types", "3.3.1"
- end
-
- gemfile <<~G
- source "https://gem.repo2"
-
- gem "capybara", "~> 2.5.0"
-
- source "https://gem.repo4" do
- gem "mime-types", "~> 3.0"
- end
- G
-
- lockfile <<-L
- GEM
- remote: https://gem.repo2/
- remote: https://gem.repo4/
- specs:
- capybara (2.5.0)
- mime-types (>= 1.16)
- mime-types (3.3.1)
-
- PLATFORMS
- #{lockfile_platforms}
-
- DEPENDENCIES
- capybara (~> 2.5.0)
- mime-types (~> 3.0)!
-
- CHECKSUMS
- L
- end
-
- it "upgrades the lockfile correctly" do
- bundle "lock --update", artifice: "compact_index"
-
- checksums = checksums_section_when_enabled do |c|
- c.checksum gem_repo2, "capybara", "2.5.0"
- c.checksum gem_repo4, "mime-types", "3.0.0"
- end
-
- expect(lockfile).to eq <<~L
- GEM
- remote: https://gem.repo2/
- specs:
- capybara (2.5.0)
- mime-types (>= 1.16)
-
- GEM
- remote: https://gem.repo4/
- specs:
- mime-types (3.0.0)
-
- PLATFORMS
- #{lockfile_platforms}
-
- DEPENDENCIES
- capybara (~> 2.5.0)
- mime-types (~> 3.0)!
- #{checksums}
- BUNDLED WITH
- #{Bundler::VERSION}
- L
- end
- end
-
context "when default source includes old gems with nil required_ruby_version" do
before do
build_repo2 do
@@ -1775,7 +867,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
ruport (= 1.7.0.3)!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -1833,7 +925,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
ruport (= 1.7.0.3)!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -1878,7 +970,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
pdf-writer (= 1.1.8)
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -1946,7 +1038,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
foo!
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -1987,4 +1079,235 @@ RSpec.describe "bundle install with gems on multiple sources" do
expect(lockfile).to eq original_lockfile.gsub("bigdecimal (1.0.0)", "bigdecimal (3.3.1)")
end
end
+
+ context "when switching a gem with components from rubygems to git source" do
+ before do
+ build_repo2 do
+ build_gem "rails", "7.0.0" do |s|
+ s.add_dependency "actionpack", "7.0.0"
+ s.add_dependency "activerecord", "7.0.0"
+ end
+ build_gem "actionpack", "7.0.0"
+ build_gem "activerecord", "7.0.0"
+ # propshaft also depends on actionpack, creating the conflict
+ build_gem "propshaft", "1.0.0" do |s|
+ s.add_dependency "actionpack", ">= 7.0.0"
+ end
+ end
+
+ build_git "rails", "7.0.0", path: lib_path("rails") do |s|
+ s.add_dependency "actionpack", "7.0.0"
+ s.add_dependency "activerecord", "7.0.0"
+ end
+
+ build_git "actionpack", "7.0.0", path: lib_path("rails")
+ build_git "activerecord", "7.0.0", path: lib_path("rails")
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "rails", "7.0.0"
+ gem "propshaft"
+ G
+ end
+
+ it "moves component gems to the git source in the lockfile" do
+ expect(lockfile).to include("remote: https://gem.repo2")
+ expect(lockfile).to include("rails (7.0.0)")
+ expect(lockfile).to include("actionpack (7.0.0)")
+ expect(lockfile).to include("activerecord (7.0.0)")
+ expect(lockfile).to include("propshaft (1.0.0)")
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "rails", git: "#{lib_path("rails")}"
+ gem "propshaft"
+ G
+
+ bundle "install"
+
+ expect(lockfile).to include("remote: #{lib_path("rails")}")
+ expect(lockfile).to include("rails (7.0.0)")
+ expect(lockfile).to include("actionpack (7.0.0)")
+ expect(lockfile).to include("activerecord (7.0.0)")
+
+ # Component gems should NOT remain in the GEM section
+ # Extract just the GEM section by splitting on GIT first, then GEM
+ gem_section = lockfile.split("GEM\n").last.split(/\n(PLATFORMS|DEPENDENCIES)/)[0]
+ expect(gem_section).not_to include("actionpack (7.0.0)")
+ expect(gem_section).not_to include("activerecord (7.0.0)")
+ end
+ end
+
+ context "when switching a gem with components from rubygems to path source" do
+ before do
+ build_repo2 do
+ build_gem "rails", "7.0.0" do |s|
+ s.add_dependency "actionpack", "7.0.0"
+ s.add_dependency "activerecord", "7.0.0"
+ end
+ build_gem "actionpack", "7.0.0"
+ build_gem "activerecord", "7.0.0"
+ # propshaft also depends on actionpack, creating the conflict
+ build_gem "propshaft", "1.0.0" do |s|
+ s.add_dependency "actionpack", ">= 7.0.0"
+ end
+ end
+
+ build_lib "rails", "7.0.0", path: lib_path("rails") do |s|
+ s.add_dependency "actionpack", "7.0.0"
+ s.add_dependency "activerecord", "7.0.0"
+ end
+
+ build_lib "actionpack", "7.0.0", path: lib_path("rails")
+ build_lib "activerecord", "7.0.0", path: lib_path("rails")
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "rails", "7.0.0"
+ gem "propshaft"
+ G
+ end
+
+ it "moves component gems to the path source in the lockfile" do
+ expect(lockfile).to include("remote: https://gem.repo2")
+ expect(lockfile).to include("rails (7.0.0)")
+ expect(lockfile).to include("actionpack (7.0.0)")
+ expect(lockfile).to include("activerecord (7.0.0)")
+ expect(lockfile).to include("propshaft (1.0.0)")
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "rails", path: "#{lib_path("rails")}"
+ gem "propshaft"
+ G
+
+ bundle "install"
+
+ expect(lockfile).to include("remote: #{lib_path("rails")}")
+ expect(lockfile).to include("rails (7.0.0)")
+ expect(lockfile).to include("actionpack (7.0.0)")
+ expect(lockfile).to include("activerecord (7.0.0)")
+
+ # Component gems should NOT remain in the GEM section
+ # Extract just the GEM section by splitting appropriately
+ gem_section = lockfile.split("GEM\n").last.split(/\n(PLATFORMS|DEPENDENCIES)/)[0]
+ expect(gem_section).not_to include("actionpack (7.0.0)")
+ expect(gem_section).not_to include("activerecord (7.0.0)")
+ end
+ end
+
+ context "when a scoped rubygems source is missing a transitive dependency" do
+ before do
+ build_repo2 do
+ build_gem "fallback_dep", "1.0.0"
+ build_gem "foo", "1.0.0"
+ end
+
+ build_repo3 do
+ build_gem "private_parent", "1.0.0" do |s|
+ s.add_dependency "fallback_dep"
+ end
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+
+ gem "foo"
+
+ source "https://gem.repo3" do
+ gem "private_parent", "1.0.0"
+ end
+ G
+
+ bundle :install, artifice: "compact_index"
+ end
+
+ it "falls back to the default rubygems source for that dependency" do
+ build_repo2 do
+ build_gem "foo", "2.0.0"
+ end
+
+ system_gems []
+
+ bundle "update foo", artifice: "compact_index"
+
+ expect(the_bundle).to include_gems("private_parent 1.0.0", "fallback_dep 1.0.0", "foo 2.0.0")
+ expect(the_bundle).to include_gems("private_parent 1.0.0", source: "remote3")
+ expect(the_bundle).to include_gems("fallback_dep 1.0.0", source: "remote2")
+ end
+ end
+
+ context "when a path gem has a transitive dependency that does not exist in the path source" do
+ before do
+ build_repo2 do
+ build_gem "missing_dep", "1.0.0"
+ build_gem "foo", "1.0.0"
+ end
+
+ build_lib "parent_gem", "1.0.0", path: lib_path("parent_gem") do |s|
+ s.add_dependency "missing_dep"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+
+ gem "foo"
+
+ gem "parent_gem", path: "#{lib_path("parent_gem")}"
+ G
+
+ bundle :install, artifice: "compact_index"
+ end
+
+ it "falls back to the default rubygems source for that dependency when updating" do
+ build_repo2 do
+ build_gem "foo", "2.0.0"
+ end
+
+ system_gems []
+
+ bundle "update foo", artifice: "compact_index"
+
+ expect(the_bundle).to include_gems("parent_gem 1.0.0", "missing_dep 1.0.0", "foo 2.0.0")
+ expect(the_bundle).to include_gems("parent_gem 1.0.0", source: "path@#{lib_path("parent_gem")}")
+ expect(the_bundle).to include_gems("missing_dep 1.0.0", source: "remote2")
+ end
+ end
+
+ context "when a git gem has a transitive dependency that does not exist in the git source" do
+ before do
+ build_repo2 do
+ build_gem "missing_dep", "1.0.0"
+ build_gem "foo", "1.0.0"
+ end
+
+ build_git "parent_gem", "1.0.0", path: lib_path("parent_gem") do |s|
+ s.add_dependency "missing_dep"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+
+ gem "foo"
+
+ gem "parent_gem", git: "#{lib_path("parent_gem")}"
+ G
+
+ bundle :install, artifice: "compact_index"
+ end
+
+ it "falls back to the default rubygems source for that dependency when updating" do
+ build_repo2 do
+ build_gem "foo", "2.0.0"
+ end
+
+ system_gems []
+
+ bundle "update foo", artifice: "compact_index"
+
+ expect(the_bundle).to include_gems("parent_gem 1.0.0", "missing_dep 1.0.0", "foo 2.0.0")
+ expect(the_bundle).to include_gems("parent_gem 1.0.0", source: "git@#{lib_path("parent_gem")}")
+ expect(the_bundle).to include_gems("missing_dep 1.0.0", source: "remote2")
+ end
+ end
end
diff --git a/spec/bundler/install/gemfile/specific_platform_spec.rb b/spec/bundler/install/gemfile/specific_platform_spec.rb
index 04495e2b72..97b1d233bf 100644
--- a/spec/bundler/install/gemfile/specific_platform_spec.rb
+++ b/spec/bundler/install/gemfile/specific_platform_spec.rb
@@ -11,7 +11,7 @@ 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 include(pl("x86_64-darwin-15"))
+ expect(the_bundle.locked_platforms).to include("universal-darwin")
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 include(
"google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin"
@@ -54,7 +54,7 @@ RSpec.describe "bundle install with specific platforms" do
sass-embedded
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "install --verbose"
@@ -70,7 +70,7 @@ RSpec.describe "bundle install with specific platforms" do
setup_multiplatform_gem
# Consistent location to install and look for gems
- bundle "config set --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
install_gemfile(google_protobuf)
@@ -88,11 +88,11 @@ RSpec.describe "bundle install with specific platforms" do
google-protobuf
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
- # force strict usage of the lock file by setting frozen mode
- bundle "config set --local frozen true"
+ # force strict usage of the lockfile by setting frozen mode
+ bundle_config "frozen true"
# make sure the platform that got actually installed with the old bundler is used
expect(the_bundle).to include_gem("google-protobuf 3.0.0.alpha.5.0.5.1 universal-darwin")
@@ -104,7 +104,7 @@ RSpec.describe "bundle install with specific platforms" do
setup_multiplatform_gem
# Consistent location to install and look for gems
- bundle "config set --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
gemfile google_protobuf
@@ -126,7 +126,7 @@ RSpec.describe "bundle install with specific platforms" do
google-protobuf
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "update"
@@ -151,12 +151,17 @@ RSpec.describe "bundle install with specific platforms" do
google-protobuf
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
context "when running on a legacy lockfile locked only to ruby" do
+ # Exercises the legacy lockfile path (use_exact_resolved_specifications? = false)
+ # because most_specific_locked_platform is ruby, matching the generic platform.
+ # Key insight: when target (arm64-darwin-22) != platform (ruby), the code tries
+ # both platforms before falling back, preserving lockfile integrity.
+
around do |example|
build_repo4 do
build_gem "nokogiri", "1.3.10"
@@ -185,20 +190,76 @@ RSpec.describe "bundle install with specific platforms" do
nokogiri
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
simulate_platform "arm64-darwin-22", &example
end
it "still installs the generic ruby variant if necessary" do
- bundle "install --verbose"
- expect(out).to include("Installing nokogiri 1.3.10")
+ bundle "install"
+ expect(the_bundle).to include_gem("nokogiri 1.3.10")
+ expect(the_bundle).not_to include_gem("nokogiri 1.3.10 arm64-darwin")
end
it "still installs the generic ruby variant if necessary, even in frozen mode" do
- bundle "install --verbose", env: { "BUNDLE_FROZEN" => "true" }
- expect(out).to include("Installing nokogiri 1.3.10")
+ bundle "install", env: { "BUNDLE_FROZEN" => "true" }
+ expect(the_bundle).to include_gem("nokogiri 1.3.10")
+ expect(the_bundle).not_to include_gem("nokogiri 1.3.10 arm64-darwin")
+ end
+ end
+
+ context "when platform-specific gem has incompatible required_ruby_version" do
+ # Key insight: candidate_platforms tries [target, platform, ruby] in order.
+ # Ruby platform is last since it requires compilation, but works when
+ # precompiled gems are incompatible with the current Ruby version.
+ #
+ # Note: This fix requires the lockfile to include both ruby and platform-
+ # specific variants (typical after `bundle lock --add-platform`). If the
+ # lockfile only has platform-specific gems, frozen mode cannot help because
+ # Bundler.setup would still expect the locked (incompatible) gem.
+
+ # Exercises the exact spec path (use_exact_resolved_specifications? = true)
+ # because lockfile has platform-specific entry as most_specific_locked_platform
+ it "falls back to ruby platform in frozen mode when lockfile includes both variants" do
+ build_repo4 do
+ build_gem "nokogiri", "1.18.10"
+ build_gem "nokogiri", "1.18.10" do |s|
+ s.platform = "x86_64-linux"
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+ G
+
+ # Lockfile has both ruby and platform-specific gem (typical after `bundle lock --add-platform`)
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.18.10)
+ nokogiri (1.18.10-x86_64-linux)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "x86_64-linux" do
+ bundle "install", env: { "BUNDLE_FROZEN" => "true" }
+ expect(the_bundle).to include_gem("nokogiri 1.18.10")
+ expect(the_bundle).not_to include_gem("nokogiri 1.18.10 x86_64-linux")
+ end
end
end
@@ -214,7 +275,7 @@ RSpec.describe "bundle install with specific platforms" do
end
# Consistent location to install and look for gems
- bundle "config set --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
gemfile <<-G
source "https://gem.repo2"
@@ -235,7 +296,7 @@ RSpec.describe "bundle install with specific platforms" do
libv8
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "install --verbose"
@@ -273,7 +334,7 @@ RSpec.describe "bundle install with specific platforms" do
grpc
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "install --verbose", artifice: "compact_index_precompiled_before"
@@ -298,7 +359,7 @@ RSpec.describe "bundle install with specific platforms" do
simulate_platform "x86_64-darwin-15" do
setup_multiplatform_gem
gemfile(google_protobuf)
- bundle "config set --local cache_all_platforms true"
+ bundle_config "cache_all_platforms true"
bundle "cache"
expect(cached_gem("google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin")).to exist
@@ -333,10 +394,9 @@ RSpec.describe "bundle install with specific platforms" do
pg_array_parser!
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
- bundle "config set --local cache_all true"
bundle "cache --all-platforms"
expect(err).to be_empty
@@ -351,7 +411,7 @@ 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 include(pl("x86_64-darwin-15"))
+ expect(the_bundle.locked_platforms).to include("universal-darwin")
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 include("CFPropertyList-1.0",
"facter-2.4.6-universal-darwin")
@@ -367,12 +427,12 @@ RSpec.describe "bundle install with specific platforms" do
simulate_platform "x86_64-darwin-15" do
setup_multiplatform_gem
install_gemfile(google_protobuf)
- bundle "lock --add-platform=#{x64_mingw32}"
+ bundle "lock --add-platform=x64-mingw-ucrt"
- expect(the_bundle.locked_gems.platforms).to include(x64_mingw32, pl("x86_64-darwin-15"))
+ expect(the_bundle.locked_platforms).to include("x64-mingw-ucrt", "universal-darwin")
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
+ google-protobuf-3.0.0.alpha.5.0.5.1-x64-mingw-ucrt
])
end
end
@@ -381,9 +441,9 @@ RSpec.describe "bundle install with specific platforms" do
simulate_platform "x86_64-darwin-15" do
setup_multiplatform_gem
install_gemfile(google_protobuf)
- bundle "lock --add-platform=#{java}"
+ bundle "lock --add-platform=java"
- expect(the_bundle.locked_gems.platforms).to include(java, pl("x86_64-darwin-15"))
+ expect(the_bundle.locked_platforms).to include("java", "universal-darwin")
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"
@@ -392,7 +452,23 @@ RSpec.describe "bundle install with specific platforms" do
end
end
- it "installs sorbet-static, which does not provide a pure ruby variant, just fine", :truffleruby do
+ it "installs sorbet-static, which does not provide a pure ruby variant, in absence of a lockfile, just fine", :truffleruby do
+ skip "does not apply to Windows" if Gem.win_platform?
+
+ build_repo2 do
+ build_gem("sorbet-static", "0.5.6403") {|s| s.platform = Bundler.local_platform }
+ end
+
+ gemfile <<~G
+ source "https://gem.repo2"
+
+ gem "sorbet-static", "0.5.6403"
+ G
+
+ bundle "install --verbose"
+ end
+
+ it "installs sorbet-static, which does not provide a pure ruby variant, in presence of a lockfile, just fine", :truffleruby do
skip "does not apply to Windows" if Gem.win_platform?
build_repo2 do
@@ -418,7 +494,7 @@ RSpec.describe "bundle install with specific platforms" do
sorbet-static (= 0.5.6403)
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "install --verbose"
@@ -459,6 +535,41 @@ RSpec.describe "bundle install with specific platforms" do
expect(err).to include(error_message).once
end
+ it "shows a platform mismatch hint when the current platform is not in the lockfile's platforms" do
+ build_repo4 do
+ build_gem("sorbet-static", "0.5.6433") {|s| s.platform = "x86_64-linux-musl" }
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "sorbet-static", "0.5.6433"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ sorbet-static (0.5.6433-x86_64-linux-musl)
+
+ PLATFORMS
+ x86_64-linux-musl
+
+ DEPENDENCIES
+ sorbet-static (= 0.5.6433)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ simulate_platform "x86_64-linux" do
+ bundle "install", raise_on_error: false
+ end
+
+ expect(err).to include("Your current platform (x86_64-linux) is not included in the lockfile's platforms (x86_64-linux-musl)")
+ expect(err).to include("bundle lock --add-platform x86_64-linux")
+ end
+
it "does not resolve if the current platform does not match any of available platform specific variants for a transitive dependency" do
build_repo4 do
build_gem("sorbet", "0.5.6433") {|s| s.add_dependency "sorbet-static", "= 0.5.6433" }
@@ -565,7 +676,7 @@ RSpec.describe "bundle install with specific platforms" do
sorbet-static-and-runtime
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "update"
@@ -596,7 +707,7 @@ RSpec.describe "bundle install with specific platforms" do
sorbet-static-and-runtime
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -648,7 +759,7 @@ RSpec.describe "bundle install with specific platforms" do
sorbet-static
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
simulate_platform "x86_64-darwin-22" do
@@ -670,7 +781,7 @@ RSpec.describe "bundle install with specific platforms" do
sorbet-static
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -717,7 +828,7 @@ RSpec.describe "bundle install with specific platforms" do
sorbet-static-and-runtime
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "update"
@@ -748,10 +859,84 @@ RSpec.describe "bundle install with specific platforms" do
sorbet-static-and-runtime
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
+ it "automatically fixes the lockfile when adding a gem that introduces dependencies with no ruby platform variants transitively" do
+ simulate_platform "x86_64-linux" do
+ build_repo4 do
+ build_gem "nokogiri", "1.18.2"
+
+ build_gem "nokogiri", "1.18.2" do |s|
+ s.platform = "x86_64-linux"
+ end
+
+ build_gem("sorbet", "0.5.11835") do |s|
+ s.add_dependency "sorbet-static", "= 0.5.11835"
+ end
+
+ build_gem "sorbet-static", "0.5.11835" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ gemfile <<~G
+ source "https://gem.repo4"
+
+ gem "nokogiri"
+ gem "sorbet"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.18.2)
+ nokogiri (1.18.2-x86_64-linux)
+
+ PLATFORMS
+ ruby
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "lock"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo4, "nokogiri", "1.18.2", "x86_64-linux"
+ c.checksum gem_repo4, "sorbet", "0.5.11835"
+ c.checksum gem_repo4, "sorbet-static", "0.5.11835", "x86_64-linux"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo4/
+ specs:
+ nokogiri (1.18.2)
+ nokogiri (1.18.2-x86_64-linux)
+ sorbet (0.5.11835)
+ sorbet-static (= 0.5.11835)
+ sorbet-static (0.5.11835-x86_64-linux)
+
+ PLATFORMS
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+ sorbet
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
it "automatically fixes the lockfile if multiple platforms locked, but no valid versions of direct dependencies for all of them" do
simulate_platform "x86_64-linux" do
build_repo4 do
@@ -792,7 +977,7 @@ RSpec.describe "bundle install with specific platforms" do
sorbet-static
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "update"
@@ -817,7 +1002,7 @@ RSpec.describe "bundle install with specific platforms" do
sorbet-static
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -860,7 +1045,7 @@ RSpec.describe "bundle install with specific platforms" do
sorbet-static (= 0.5.10549)
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "install"
@@ -879,7 +1064,7 @@ RSpec.describe "bundle install with specific platforms" do
sorbet-static (= 0.5.10549)
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -916,7 +1101,7 @@ RSpec.describe "bundle install with specific platforms" do
ibandit (~> 0.7.0)
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "lock --update i18n"
@@ -936,7 +1121,7 @@ RSpec.describe "bundle install with specific platforms" do
ibandit (~> 0.7.0)
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -977,7 +1162,7 @@ RSpec.describe "bundle install with specific platforms" do
tzinfo (~> 1.2)
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
lockfile original_lockfile
@@ -1000,7 +1185,7 @@ RSpec.describe "bundle install with specific platforms" do
gem "nokogiri"
- gem "tzinfo", "~> 1.2", platforms: %i[mingw mswin x64_mingw jruby]
+ gem "tzinfo", "~> 1.2", platforms: %i[windows jruby]
G
checksums = checksums_section_when_enabled do |c|
@@ -1021,7 +1206,7 @@ RSpec.describe "bundle install with specific platforms" do
tzinfo (~> 1.2)
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
lockfile original_lockfile
@@ -1064,7 +1249,7 @@ RSpec.describe "bundle install with specific platforms" do
concurrent-ruby
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "lock"
@@ -1077,14 +1262,14 @@ RSpec.describe "bundle install with specific platforms" do
myrack (3.0.7)
PLATFORMS
- #{lockfile_platforms("ruby", generic_local_platform, defaults: [])}
+ #{lockfile_platforms(generic_default_locked_platform || local_platform, defaults: ["ruby"])}
DEPENDENCIES
concurrent-ruby
myrack
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -1115,7 +1300,7 @@ RSpec.describe "bundle install with specific platforms" do
my-precompiled-gem
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle :install
@@ -1160,7 +1345,7 @@ RSpec.describe "bundle install with specific platforms" do
nokogiri (= 1.14.0)
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle :install
@@ -1184,7 +1369,7 @@ RSpec.describe "bundle install with specific platforms" do
nokogiri (= 1.14.0)
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -1218,7 +1403,7 @@ RSpec.describe "bundle install with specific platforms" do
DEPENDENCIES
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "lock"
@@ -1238,7 +1423,7 @@ RSpec.describe "bundle install with specific platforms" do
sorbet
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -1254,7 +1439,7 @@ RSpec.describe "bundle install with specific platforms" do
s.platform = "arm-linux"
end
build_gem "nokogiri", "1.14.0" do |s|
- s.platform = "x64-mingw32"
+ s.platform = "x64-mingw-ucrt"
end
build_gem "nokogiri", "1.14.0" do |s|
s.platform = "java"
@@ -1300,7 +1485,7 @@ RSpec.describe "bundle install with specific platforms" do
nokogiri
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
gemfile <<~G
@@ -1337,12 +1522,12 @@ RSpec.describe "bundle install with specific platforms" do
sorbet-static
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
- it "does not fail when a platform variant is incompatible with the current ruby and another equivalent platform specific variant is part of the resolution", rubygems: ">= 3.3.21" do
+ it "does not fail when a platform variant is incompatible with the current ruby and another equivalent platform specific variant is part of the resolution" do
build_repo4 do
build_gem "nokogiri", "1.15.5"
@@ -1392,7 +1577,7 @@ RSpec.describe "bundle install with specific platforms" do
sass-embedded
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -1434,7 +1619,7 @@ RSpec.describe "bundle install with specific platforms" do
nokogiri
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -1481,14 +1666,14 @@ RSpec.describe "bundle install with specific platforms" do
rcee_precompiled (= 0.5.0)
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
end
end
- it "adds current musl platform, when there are also gnu variants", rubygems: ">= 3.3.21" do
+ it "adds current musl platform, when there are also gnu variants" do
build_repo4 do
build_gem "rcee_precompiled", "0.5.0" do |s|
s.platform = "x86_64-linux-gnu"
@@ -1528,7 +1713,7 @@ RSpec.describe "bundle install with specific platforms" do
rcee_precompiled (= 0.5.0)
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -1566,7 +1751,7 @@ RSpec.describe "bundle install with specific platforms" do
rcee_precompiled (= 0.5.0)
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -1595,7 +1780,7 @@ RSpec.describe "bundle install with specific platforms" do
nokogiri!
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
simulate_platform "arm64-darwin-23" do
@@ -1638,7 +1823,7 @@ RSpec.describe "bundle install with specific platforms" do
nokogiri
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
lockfile original_lockfile
@@ -1689,7 +1874,7 @@ RSpec.describe "bundle install with specific platforms" do
nokogiri
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
lockfile original_lockfile
@@ -1731,7 +1916,7 @@ RSpec.describe "bundle install with specific platforms" do
google-protobuf (~> 3.0)
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
lockfile original_lockfile
@@ -1749,11 +1934,11 @@ RSpec.describe "bundle install with specific platforms" do
build_repo2 do
build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1")
build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x86_64-linux" }
- build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x64-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x64-mingw-ucrt" }
build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "universal-darwin" }
build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x86_64-linux" }
- build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x64-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x64-mingw-ucrt" }
build_gem("google-protobuf", "3.0.0.alpha.5.0.5")
build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "universal-darwin" }
diff --git a/spec/bundler/install/gemfile_spec.rb b/spec/bundler/install/gemfile_spec.rb
index 96ed174e9b..83875a3d0e 100644
--- a/spec/bundler/install/gemfile_spec.rb
+++ b/spec/bundler/install/gemfile_spec.rb
@@ -27,6 +27,35 @@ RSpec.describe "bundle install" do
ENV["BUNDLE_GEMFILE"] = "NotGemfile"
expect(the_bundle).to include_gems "myrack 1.0.0"
end
+
+ it "respects lockfile and BUNDLE_LOCKFILE" do
+ gemfile bundled_app("NotGemfile"), <<-G
+ lockfile "ReallyNotGemfile.lock"
+ source "https://gem.repo1"
+ gem 'myrack'
+ G
+
+ bundle :install, gemfile: bundled_app("NotGemfile")
+
+ ENV["BUNDLE_GEMFILE"] = "NotGemfile"
+ ENV["BUNDLE_LOCKFILE"] = "ReallyNotGemfile.lock"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+
+ it "respects BUNDLE_LOCKFILE during bundle install" do
+ ENV["BUNDLE_LOCKFILE"] = "ReallyNotGemfile.lock"
+
+ gemfile bundled_app("NotGemfile"), <<-G
+ source "https://gem.repo1"
+ gem 'myrack'
+ G
+
+ bundle :install, gemfile: bundled_app("NotGemfile")
+ expect(bundled_app("ReallyNotGemfile.lock")).to exist
+
+ ENV["BUNDLE_GEMFILE"] = "NotGemfile"
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
end
context "with gemfile set via config" do
@@ -36,7 +65,7 @@ RSpec.describe "bundle install" do
gem 'myrack'
G
- bundle "config set --local gemfile #{bundled_app("NotGemfile")}"
+ bundle_config "gemfile #{bundled_app("NotGemfile")}"
end
it "uses the gemfile to install" do
bundle "install"
@@ -53,17 +82,37 @@ RSpec.describe "bundle install" do
end
end
- context "with deprecated features" do
- it "reports that lib is an invalid option" do
- gemfile <<-G
- source "https://gem.repo1"
+ it "reports that lib is an invalid option" do
+ gemfile <<-G
+ source "https://gem.repo1"
- gem "myrack", :lib => "myrack"
- G
+ gem "myrack", :lib => "myrack"
+ G
- bundle :install, raise_on_error: false
- expect(err).to match(/You passed :lib as an option for gem 'myrack', but it is invalid/)
- end
+ bundle :install, raise_on_error: false
+ expect(err).to match(/You passed :lib as an option for gem 'myrack', but it is invalid/)
+ end
+
+ it "reports that type is an invalid option" do
+ gemfile <<-G
+ source "https://gem.repo1"
+
+ gem "myrack", :type => "development"
+ G
+
+ bundle :install, raise_on_error: false
+ expect(err).to match(/You passed :type as an option for gem 'myrack', but it is invalid/)
+ end
+
+ it "reports that gemfile is an invalid option" do
+ gemfile <<-G
+ source "https://gem.repo1"
+
+ gem "myrack", :gemfile => "foo"
+ G
+
+ bundle :install, raise_on_error: false
+ expect(err).to match(/You passed :gemfile as an option for gem 'myrack', but it is invalid/)
end
context "when an internal error happens" do
diff --git a/spec/bundler/install/gems/compact_index_spec.rb b/spec/bundler/install/gems/compact_index_spec.rb
index cf661ee284..9db73b84b5 100644
--- a/spec/bundler/install/gems/compact_index_spec.rb
+++ b/spec/bundler/install/gems/compact_index_spec.rb
@@ -95,8 +95,7 @@ RSpec.describe "compact index api" do
G
bundle :install, artifice: "compact_index"
- bundle "config set --local deployment true"
- bundle "config set --local path vendor/bundle"
+ bundle_config "deployment true"
bundle :install, artifice: "compact_index"
expect(out).to include("Fetching gem metadata from #{source_uri}")
expect(the_bundle).to include_gems "myrack 1.0.0"
@@ -133,7 +132,7 @@ RSpec.describe "compact index api" do
bundle :install, artifice: "compact_index"
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
bundle :install, artifice: "compact_index"
expect(the_bundle).to include_gems("rails 2.3.2")
@@ -147,7 +146,7 @@ RSpec.describe "compact index api" do
G
bundle "install", artifice: "compact_index"
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
bundle :install, artifice: "compact_index"
expect(the_bundle).to include_gems("foo 1.0")
@@ -183,8 +182,7 @@ RSpec.describe "compact index api" do
gem "myrack"
G
- versions = Pathname.new(Bundler.rubygems.user_home).join(
- ".bundle", "cache", "compact_index",
+ versions = compact_index_cache_path.join(
"localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions"
)
versions.dirname.mkpath
@@ -304,7 +302,7 @@ RSpec.describe "compact index api" do
end
system_gems %w[myrack-1.0.0 thin-1.0 net_a-1.0], gem_repo: gem_repo2
- bundle "config set --local path.system true"
+ bundle_config "path.system true"
ENV["BUNDLER_SPEC_ALL_REQUESTS"] = <<~EOS.strip
#{source_uri}/versions
#{source_uri}/info/myrack
@@ -315,33 +313,15 @@ RSpec.describe "compact index api" do
gem "myrack"
G
- expect(last_command.stdboth).not_to include "Double checking"
+ expect(stdboth).not_to include "Double checking"
end
- it "fetches again when more dependencies are found in subsequent sources", bundler: "< 3" do
+ it "fetches again when more dependencies are found in subsequent sources" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
end
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
- end
-
- gemfile <<-G
- source "#{source_uri}"
- source "#{source_uri}/extra"
- gem "back_deps"
- G
-
- bundle :install, artifice: "compact_index_extra"
- expect(the_bundle).to include_gems "back_deps 1.0", "foo 1.0"
- end
-
- it "fetches again when more dependencies are found in subsequent sources with source blocks" do
- build_repo2 do
- build_gem "back_deps" do |s|
- s.add_dependency "foo"
- end
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ FileUtils.rm_r Dir[gem_repo2("gems/foo-*.gem")]
end
install_gemfile <<-G, artifice: "compact_index_extra", verbose: true
@@ -377,11 +357,13 @@ RSpec.describe "compact index api" do
expect(the_bundle).to include_gems "myrack 1.2"
end
- it "considers all possible versions of dependencies from all api gem sources", bundler: "< 3" do
- # In this scenario, the gem "somegem" only exists in repo4. It depends on specific version of activesupport that
- # exists only in repo1. There happens also be a version of activesupport in repo4, but not the one that version 1.0.0
- # of somegem wants. This test makes sure that bundler actually finds version 1.2.3 of active support in the other
- # repo and installs it.
+ it "resolves indirect dependencies to the most scoped source that includes them" do
+ # In this scenario, the gem "somegem" only exists in repo4. It depends on
+ # specific version of activesupport that exists only in repo1. There
+ # happens also be a version of activesupport in repo4, but not the one that
+ # version 1.0.0 of somegem wants. This test makes sure that bundler tries to
+ # use the version in the most scoped source, even if not compatible, and
+ # gives a resolution error
build_repo4 do
build_gem "activesupport", "1.2.0"
build_gem "somegem", "1.0.0" do |s|
@@ -391,14 +373,14 @@ RSpec.describe "compact index api" do
gemfile <<-G
source "#{source_uri}"
- source "#{source_uri}/extra"
- gem 'somegem', '1.0.0'
+ source "#{source_uri}/extra" do
+ gem 'somegem', '1.0.0'
+ end
G
- bundle :install, artifice: "compact_index_extra_api"
+ bundle :install, artifice: "compact_index_extra_api", raise_on_error: false
- expect(the_bundle).to include_gems "somegem 1.0.0"
- expect(the_bundle).to include_gems "activesupport 1.2.3"
+ expect(err).to include("Could not find compatible versions")
end
it "prints API output properly with back deps" do
@@ -406,7 +388,7 @@ RSpec.describe "compact index api" do
build_gem "back_deps" do |s|
s.add_dependency "foo"
end
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ FileUtils.rm_r Dir[gem_repo2("gems/foo-*.gem")]
end
gemfile <<-G
@@ -429,7 +411,7 @@ RSpec.describe "compact index api" do
end
build_gem "missing"
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ FileUtils.rm_r Dir[gem_repo2("gems/foo-*.gem")]
end
install_gemfile <<-G, artifice: "compact_index_extra_missing"
@@ -449,7 +431,7 @@ RSpec.describe "compact index api" do
end
build_gem "missing"
- FileUtils.rm_rf Dir[gem_repo4("gems/foo-*.gem")]
+ FileUtils.rm_r Dir[gem_repo4("gems/foo-*.gem")]
end
install_gemfile <<-G, artifice: "compact_index_extra_api_missing"
@@ -473,32 +455,12 @@ RSpec.describe "compact index api" do
expect(the_bundle).to include_gems "foo 1.0"
end
- it "fetches again when more dependencies are found in subsequent sources using deployment mode", bundler: "< 3" do
+ it "fetches again when more dependencies are found in subsequent sources using deployment mode" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
end
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
- end
-
- gemfile <<-G
- source "#{source_uri}"
- source "#{source_uri}/extra"
- gem "back_deps"
- G
-
- bundle :install, artifice: "compact_index_extra"
- bundle "config --set local deployment true"
- bundle :install, artifice: "compact_index_extra"
- expect(the_bundle).to include_gems "back_deps 1.0"
- end
-
- it "fetches again when more dependencies are found in subsequent sources using deployment mode with blocks" do
- build_repo2 do
- build_gem "back_deps" do |s|
- s.add_dependency "foo"
- end
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ FileUtils.rm_r Dir[gem_repo2("gems/foo-*.gem")]
end
gemfile <<-G
@@ -509,7 +471,7 @@ RSpec.describe "compact index api" do
G
bundle :install, artifice: "compact_index_extra"
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
bundle :install, artifice: "compact_index_extra"
expect(the_bundle).to include_gems "back_deps 1.0"
end
@@ -531,40 +493,6 @@ RSpec.describe "compact index api" do
expect(out).to include("Fetching gem metadata from #{source_uri}")
end
- it "installs the binstubs", bundler: "< 3" do
- gemfile <<-G
- source "#{source_uri}"
- gem "myrack"
- G
-
- bundle "install --binstubs", artifice: "compact_index"
-
- gembin "myrackup"
- expect(out).to eq("1.0.0")
- end
-
- it "installs the bins when using --path and uses autoclean", bundler: "< 3" do
- gemfile <<-G
- source "#{source_uri}"
- gem "myrack"
- G
-
- bundle "install --path vendor/bundle", artifice: "compact_index"
-
- expect(vendored_gems("bin/myrackup")).to exist
- end
-
- it "installs the bins when using --path and uses bundle clean", bundler: "< 3" do
- gemfile <<-G
- source "#{source_uri}"
- gem "myrack"
- G
-
- bundle "install --path vendor/bundle --no-clean", artifice: "compact_index"
-
- expect(vendored_gems("bin/myrackup")).to exist
- end
-
it "prints post_install_messages" do
gemfile <<-G
source "#{source_uri}"
@@ -619,19 +547,6 @@ RSpec.describe "compact index api" do
expect(the_bundle).to include_gems "myrack 1.0.0"
end
- it "strips http basic auth creds when warning about ambiguous sources", bundler: "< 3" do
- gemfile <<-G
- source "#{basic_auth_source_uri}"
- source "#{file_uri_for(gem_repo1)}"
- gem "myrack"
- G
-
- bundle :install, artifice: "compact_index_basic_authentication"
- expect(err).to include("Warning: the gem 'myrack' was found in multiple sources.")
- expect(err).not_to include("#{user}:#{password}")
- expect(the_bundle).to include_gems "myrack 1.0.0"
- end
-
it "does not pass the user / password to different hosts on redirect" do
gemfile <<-G
source "#{basic_auth_source_uri}"
@@ -744,7 +659,7 @@ RSpec.describe "compact index api" do
gem "myrack"
G
- bundle :install, env: { "RUBYOPT" => opt_add("-I#{bundled_app("broken_ssl")}", ENV["RUBYOPT"]) }, raise_on_error: false, artifice: nil
+ bundle :install, env: { "RUBYOPT" => "-I#{bundled_app("broken_ssl")}" }, raise_on_error: false, artifice: nil
expect(err).to include("recompile Ruby").and include("cannot load such file")
end
end
@@ -783,14 +698,13 @@ RSpec.describe "compact index api" do
bundle :install, artifice: "compact_index_forbidden"
ensure
- home(".gemrc").rmtree
+ FileUtils.rm_rf home(".gemrc")
end
end
end
it "performs update with etag not-modified" do
- versions_etag = Pathname.new(Bundler.rubygems.user_home).join(
- ".bundle", "cache", "compact_index",
+ versions_etag = compact_index_cache_path.join(
"localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions.etag"
)
expect(versions_etag.file?).to eq(false)
@@ -833,8 +747,7 @@ RSpec.describe "compact index api" do
gem 'myrack', '1.0.0'
G
- versions = Pathname.new(Bundler.rubygems.user_home).join(
- ".bundle", "cache", "compact_index",
+ versions = compact_index_cache_path.join(
"localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions"
)
# Modify the cached file. The ranged request will be based on this but,
@@ -857,7 +770,7 @@ RSpec.describe "compact index api" do
gem 'myrack', '0.9.1'
G
- update_repo4 do
+ build_repo4 do
build_gem "myrack", "1.0.0"
end
@@ -876,8 +789,7 @@ RSpec.describe "compact index api" do
G
# Create a partial cache versions file
- versions = Pathname.new(Bundler.rubygems.user_home).join(
- ".bundle", "cache", "compact_index",
+ versions = compact_index_cache_path.join(
"localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions"
)
versions.dirname.mkpath
@@ -899,7 +811,7 @@ RSpec.describe "compact index api" do
gem 'myrack', '0.9.1'
G
- update_repo4 do
+ build_repo4 do
build_gem "myrack", "1.0.0"
end
@@ -921,7 +833,7 @@ RSpec.describe "compact index api" do
gem 'myrack', '0.9.1'
G
- update_repo4 do
+ build_repo4 do
build_gem "myrack", "1.0.0"
end
@@ -941,7 +853,7 @@ RSpec.describe "compact index api" do
bundle :install, artifice: "compact_index"
- cache_path = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index", "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5")
+ cache_path = compact_index_cache_path.join("localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5")
# We must remove the etag so that we don't ignore the range and get a 304 Not Modified.
myrack_info_etag_path = File.join(cache_path, "info-etags", "myrack-92f3313ce5721296f14445c3a6b9c073")
@@ -993,7 +905,7 @@ RSpec.describe "compact index api" do
DEPENDENCIES
#{checksums_section}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -1015,11 +927,7 @@ RSpec.describe "compact index api" do
gem "myrack"
G
- gem_path = if Bundler.feature_flag.global_gem_cache?
- default_cache_path.dirname.join("cache", "gems", "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "myrack-1.0.0.gem")
- else
- default_cache_path.dirname.join("myrack-1.0.0.gem")
- end
+ gem_path = default_cache_path.dirname.join("myrack-1.0.0.gem")
expect(exitstatus).to eq(37)
expect(err).to eq <<~E.strip
@@ -1048,7 +956,7 @@ RSpec.describe "compact index api" do
end
it "does not raise when disable_checksum_validation is set" do
- bundle "config set disable_checksum_validation true"
+ bundle_config "disable_checksum_validation true"
install_gemfile <<-G, artifice: "compact_index_wrong_gem_checksum"
source "#{source_uri}"
gem "myrack"
@@ -1089,9 +997,16 @@ Running `bundle update rails` should fix the problem.
gem "activemerchant"
end
G
- gem_command "uninstall activemerchant"
+ uninstall_gem("activemerchant")
bundle "update rails", artifice: "compact_index"
count = lockfile.match?("CHECKSUMS") ? 2 : 1 # Once in the specs, and once in CHECKSUMS
expect(lockfile.scan(/activemerchant \(/).size).to eq(count)
end
+
+ it "handles an API that does not provide checksums info (undocumented, support may get removed)" do
+ install_gemfile <<-G, artifice: "compact_index_no_checksums"
+ source "https://gem.repo1"
+ gem "rake"
+ G
+ end
end
diff --git a/spec/bundler/install/gems/dependency_api_fallback_spec.rb b/spec/bundler/install/gems/dependency_api_fallback_spec.rb
index 42239311e2..c7b0c537e4 100644
--- a/spec/bundler/install/gems/dependency_api_fallback_spec.rb
+++ b/spec/bundler/install/gems/dependency_api_fallback_spec.rb
@@ -3,44 +3,16 @@
RSpec.describe "gemcutter's dependency API" do
context "when Gemcutter API takes too long to respond" do
before do
- require_rack
-
- port = find_unused_port
- @server_uri = "http://127.0.0.1:#{port}"
-
- require_relative "../../support/artifice/endpoint_timeout"
- require_relative "../../support/silent_logger"
-
- require "rackup/server"
-
- @t = Thread.new do
- server = Rackup::Server.start(app: EndpointTimeout,
- Host: "0.0.0.0",
- Port: port,
- server: "webrick",
- AccessLog: [],
- Logger: Spec::SilentLogger.new)
- server.start
- end
- @t.run
-
- wait_for_server("127.0.0.1", port)
- bundle "config set timeout 1"
- end
-
- after do
- Artifice.deactivate
- @t.kill
- @t.join
+ bundle_config "timeout 1"
end
it "times out and falls back on the modern index" do
- install_gemfile <<-G, artifice: nil, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s }
- source "#{@server_uri}"
+ install_gemfile <<-G, artifice: "endpoint_timeout"
+ source "https://gem.repo1"
gem "myrack"
G
- expect(out).to include("Fetching source index from #{@server_uri}/")
+ expect(out).to include("Fetching source index from https://gem.repo1/")
expect(the_bundle).to include_gems "myrack 1.0.0"
end
end
diff --git a/spec/bundler/install/gems/dependency_api_spec.rb b/spec/bundler/install/gems/dependency_api_spec.rb
index 4b561064d9..32a1b98b6d 100644
--- a/spec/bundler/install/gems/dependency_api_spec.rb
+++ b/spec/bundler/install/gems/dependency_api_spec.rb
@@ -60,8 +60,7 @@ RSpec.describe "gemcutter's dependency API" do
G
bundle :install, artifice: "endpoint"
- bundle "config set --local deployment true"
- bundle "config set --local path vendor/bundle"
+ bundle_config "deployment true"
bundle :install, artifice: "endpoint"
expect(out).to include("Fetching gem metadata from #{source_uri}")
expect(the_bundle).to include_gems "myrack 1.0.0"
@@ -98,7 +97,7 @@ RSpec.describe "gemcutter's dependency API" do
bundle :install, artifice: "endpoint"
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
bundle :install, artifice: "endpoint"
expect(the_bundle).to include_gems("rails 2.3.2")
@@ -112,14 +111,14 @@ RSpec.describe "gemcutter's dependency API" do
G
bundle "install", artifice: "endpoint"
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
bundle :install, artifice: "endpoint"
expect(the_bundle).to include_gems("foo 1.0")
end
it "falls back when the API errors out" do
- simulate_platform x86_mswin32 do
+ simulate_platform "x86-mswin32" do
build_repo2 do
# The rcov gem is platform mswin32, but has no arch
build_gem "rcov" do |s|
@@ -255,30 +254,12 @@ RSpec.describe "gemcutter's dependency API" do
end
end
- it "fetches again when more dependencies are found in subsequent sources", bundler: "< 3" do
+ it "fetches again when more dependencies are found in subsequent sources" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
end
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
- end
-
- gemfile <<-G
- source "#{source_uri}"
- source "#{source_uri}/extra"
- gem "back_deps"
- G
-
- bundle :install, artifice: "endpoint_extra"
- expect(the_bundle).to include_gems "back_deps 1.0", "foo 1.0"
- end
-
- it "fetches again when more dependencies are found in subsequent sources using blocks" do
- build_repo2 do
- build_gem "back_deps" do |s|
- s.add_dependency "foo"
- end
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ FileUtils.rm_r Dir[gem_repo2("gems/foo-*.gem")]
end
gemfile <<-G
@@ -314,11 +295,13 @@ RSpec.describe "gemcutter's dependency API" do
expect(the_bundle).to include_gems "myrack 1.2"
end
- it "considers all possible versions of dependencies from all api gem sources", bundler: "< 3" do
- # In this scenario, the gem "somegem" only exists in repo4. It depends on specific version of activesupport that
- # exists only in repo1. There happens also be a version of activesupport in repo4, but not the one that version 1.0.0
- # of somegem wants. This test makes sure that bundler actually finds version 1.2.3 of active support in the other
- # repo and installs it.
+ it "resolves indirect dependencies to the most scoped source that includes them" do
+ # In this scenario, the gem "somegem" only exists in repo4. It depends on
+ # specific version of activesupport that exists only in repo1. There
+ # happens also be a version of activesupport in repo4, but not the one that
+ # version 1.0.0 of somegem wants. This test makes sure that bundler tries to
+ # use the version in the most scoped source, even if not compatible, and
+ # gives a resolution error
build_repo4 do
build_gem "activesupport", "1.2.0"
build_gem "somegem", "1.0.0" do |s|
@@ -328,14 +311,14 @@ RSpec.describe "gemcutter's dependency API" do
gemfile <<-G
source "#{source_uri}"
- source "#{source_uri}/extra"
- gem 'somegem', '1.0.0'
+ source "#{source_uri}/extra" do
+ gem 'somegem', '1.0.0'
+ end
G
- bundle :install, artifice: "endpoint_extra_api"
+ bundle :install, artifice: "compact_index_extra_api", raise_on_error: false
- expect(the_bundle).to include_gems "somegem 1.0.0"
- expect(the_bundle).to include_gems "activesupport 1.2.3"
+ expect(err).to include("Could not find compatible versions")
end
it "prints API output properly with back deps" do
@@ -343,7 +326,7 @@ RSpec.describe "gemcutter's dependency API" do
build_gem "back_deps" do |s|
s.add_dependency "foo"
end
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ FileUtils.rm_r Dir[gem_repo2("gems/foo-*.gem")]
end
gemfile <<-G
@@ -359,33 +342,14 @@ RSpec.describe "gemcutter's dependency API" do
expect(out).to include("Fetching source index from http://localgemserver.test/extra")
end
- it "does not fetch every spec when doing back deps", bundler: "< 3" do
- build_repo2 do
- build_gem "back_deps" do |s|
- s.add_dependency "foo"
- end
- build_gem "missing"
-
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
- end
-
- install_gemfile <<-G, artifice: "endpoint_extra_missing"
- source "#{source_uri}"
- source "#{source_uri}/extra"
- gem "back_deps"
- G
-
- expect(the_bundle).to include_gems "back_deps 1.0"
- end
-
- it "does not fetch every spec when doing back deps using blocks" do
+ it "does not fetch every spec when doing back deps" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
end
build_gem "missing"
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ FileUtils.rm_r Dir[gem_repo2("gems/foo-*.gem")]
end
install_gemfile <<-G, artifice: "endpoint_extra_missing"
@@ -398,32 +362,12 @@ RSpec.describe "gemcutter's dependency API" do
expect(the_bundle).to include_gems "back_deps 1.0"
end
- it "fetches again when more dependencies are found in subsequent sources using deployment mode", bundler: "< 3" do
- build_repo2 do
- build_gem "back_deps" do |s|
- s.add_dependency "foo"
- end
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
- end
-
- gemfile <<-G
- source "#{source_uri}"
- source "#{source_uri}/extra"
- gem "back_deps"
- G
-
- bundle :install, artifice: "endpoint_extra"
- bundle "config set --local deployment true"
- bundle :install, artifice: "endpoint_extra"
- expect(the_bundle).to include_gems "back_deps 1.0"
- end
-
- it "fetches again when more dependencies are found in subsequent sources using deployment mode with blocks" do
+ it "fetches again when more dependencies are found in subsequent sources using deployment mode" do
build_repo2 do
build_gem "back_deps" do |s|
s.add_dependency "foo"
end
- FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ FileUtils.rm_r Dir[gem_repo2("gems/foo-*.gem")]
end
gemfile <<-G
@@ -434,7 +378,7 @@ RSpec.describe "gemcutter's dependency API" do
G
bundle :install, artifice: "endpoint_extra"
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
bundle "install", artifice: "endpoint_extra"
expect(the_bundle).to include_gems "back_deps 1.0"
end
@@ -472,40 +416,6 @@ RSpec.describe "gemcutter's dependency API" do
expect(out).to include("Fetching gem metadata from #{source_uri}")
end
- it "installs the binstubs", bundler: "< 3" do
- gemfile <<-G
- source "#{source_uri}"
- gem "myrack"
- G
-
- bundle "install --binstubs", artifice: "endpoint"
-
- gembin "myrackup"
- expect(out).to eq("1.0.0")
- end
-
- it "installs the bins when using --path and uses autoclean", bundler: "< 3" do
- gemfile <<-G
- source "#{source_uri}"
- gem "myrack"
- G
-
- bundle "install --path vendor/bundle", artifice: "endpoint"
-
- expect(vendored_gems("bin/myrackup")).to exist
- end
-
- it "installs the bins when using --path and uses bundle clean", bundler: "< 3" do
- gemfile <<-G
- source "#{source_uri}"
- gem "myrack"
- G
-
- bundle "install --path vendor/bundle --no-clean", artifice: "endpoint"
-
- expect(vendored_gems("bin/myrackup")).to exist
- end
-
it "prints post_install_messages" do
gemfile <<-G
source "#{source_uri}"
@@ -581,19 +491,6 @@ RSpec.describe "gemcutter's dependency API" do
expect(out).not_to include("#{user}:#{password}")
end
- it "strips http basic auth creds when warning about ambiguous sources", bundler: "< 3" do
- gemfile <<-G
- source "#{basic_auth_source_uri}"
- source "#{file_uri_for(gem_repo1)}"
- gem "myrack"
- G
-
- bundle :install, artifice: "endpoint_basic_authentication"
- expect(err).to include("Warning: the gem 'myrack' was found in multiple sources.")
- expect(err).not_to include("#{user}:#{password}")
- expect(the_bundle).to include_gems "myrack 1.0.0"
- end
-
it "does not pass the user / password to different hosts on redirect" do
gemfile <<-G
source "#{basic_auth_source_uri}"
@@ -713,7 +610,7 @@ RSpec.describe "gemcutter's dependency API" do
gem "myrack"
G
- bundle :install, artifice: "fail", env: { "RUBYOPT" => opt_add("-I#{bundled_app("broken_ssl")}", ENV["RUBYOPT"]) }, raise_on_error: false
+ bundle :install, artifice: "fail", env: { "RUBYOPT" => "-I#{bundled_app("broken_ssl")}" }, raise_on_error: false
expect(err).to include("recompile Ruby").and include("cannot load such file")
end
end
@@ -752,7 +649,7 @@ RSpec.describe "gemcutter's dependency API" do
bundle "install", artifice: "endpoint_marshal_fail"
ensure
- home(".gemrc").rmtree
+ FileUtils.rm_rf home(".gemrc")
end
end
end
diff --git a/spec/bundler/install/gems/flex_spec.rb b/spec/bundler/install/gems/flex_spec.rb
index b7e1b5f1bd..a30b53d6ad 100644
--- a/spec/bundler/install/gems/flex_spec.rb
+++ b/spec/bundler/install/gems/flex_spec.rb
@@ -191,7 +191,7 @@ RSpec.describe "bundle flex_install" do
end
it "discards the locked gems when the Gemfile requires different versions than the lock" do
- bundle "config set force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
nice_error = <<~E.strip
Could not find compatible versions
@@ -208,7 +208,7 @@ RSpec.describe "bundle flex_install" do
end
it "does not include conflicts with a single requirement tree, because that can't possibly be a conflict" do
- bundle "config set force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
bad_error = <<~E.strip
Bundler could not find compatible versions for gem "myrack-obama":
@@ -289,12 +289,36 @@ RSpec.describe "bundle flex_install" do
myrack-obama
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
it "should work when you update" do
bundle "update myrack"
+
+ checksums = checksums_section_when_enabled do |c|
+ c.checksum gem_repo1, "myrack", "0.9.1"
+ c.checksum gem_repo1, "myrack-obama", "1.0"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: https://gem.repo1/
+ specs:
+ myrack (0.9.1)
+ myrack-obama (1.0)
+ myrack
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack (= 0.9.1)
+ myrack-obama
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
end
end
@@ -334,7 +358,7 @@ RSpec.describe "bundle flex_install" do
myrack
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
diff --git a/spec/bundler/install/gems/fund_spec.rb b/spec/bundler/install/gems/fund_spec.rb
index 0855a62b86..8a3a51270a 100644
--- a/spec/bundler/install/gems/fund_spec.rb
+++ b/spec/bundler/install/gems/fund_spec.rb
@@ -54,7 +54,7 @@ RSpec.describe "bundle install" do
context "when gems include a fund URI but `ignore_funding_requests` is configured" do
before do
- bundle "config set ignore_funding_requests true"
+ bundle_config "ignore_funding_requests true"
end
it "does not display the plural fund message after installing" do
diff --git a/spec/bundler/install/gems/gemfile_source_header_spec.rb b/spec/bundler/install/gems/gemfile_source_header_spec.rb
new file mode 100644
index 0000000000..dc35c8d741
--- /dev/null
+++ b/spec/bundler/install/gems/gemfile_source_header_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+RSpec.describe "fetching dependencies with a mirrored source" do
+ let(:mirror) { "https://server.example.org" }
+
+ before do
+ build_repo2
+
+ gemfile <<-G
+ source "#{mirror}"
+ gem 'weakling'
+ G
+
+ bundle_config "mirror.#{mirror} https://gem.repo2"
+ end
+
+ it "sets the 'X-Gemfile-Source' and 'User-Agent' headers and bundles successfully" do
+ bundle :install, artifice: "endpoint_mirror_source"
+
+ expect(out).to include("Installing weakling")
+ expect(out).to include("Bundle complete")
+ expect(the_bundle).to include_gems "weakling 0.0.3"
+ end
+end
diff --git a/spec/bundler/install/gems/mirror_probe_spec.rb b/spec/bundler/install/gems/mirror_probe_spec.rb
new file mode 100644
index 0000000000..564062ccf6
--- /dev/null
+++ b/spec/bundler/install/gems/mirror_probe_spec.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+RSpec.describe "fetching dependencies with a not available mirror" do
+ before do
+ build_repo2
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem 'weakling'
+ G
+ end
+
+ context "with a specific fallback timeout" do
+ before do
+ bundle_config_global("BUNDLE_MIRROR__HTTPS://GEM__REPO2/__FALLBACK_TIMEOUT/" => "true",
+ "BUNDLE_MIRROR__HTTPS://GEM__REPO2/" => "https://gem.mirror")
+ end
+
+ it "install a gem using the original uri when the mirror is not responding" do
+ bundle :install, env: { "BUNDLER_SPEC_FAKE_RESOLVE" => "gem.mirror" }, verbose: true
+
+ expect(out).to include("Installing weakling")
+ expect(out).to include("Bundle complete")
+ expect(the_bundle).to include_gems "weakling 0.0.3"
+ end
+ end
+
+ context "with a global fallback timeout" do
+ before do
+ bundle_config_global("BUNDLE_MIRROR__ALL__FALLBACK_TIMEOUT/" => "1",
+ "BUNDLE_MIRROR__ALL" => "https://gem.mirror")
+ end
+
+ it "install a gem using the original uri when the mirror is not responding" do
+ bundle :install, env: { "BUNDLER_SPEC_FAKE_RESOLVE" => "gem.mirror" }
+
+ expect(out).to include("Installing weakling")
+ expect(out).to include("Bundle complete")
+ expect(the_bundle).to include_gems "weakling 0.0.3"
+ end
+ end
+
+ context "with a specific mirror without a fallback timeout" do
+ before do
+ bundle_config_global("BUNDLE_MIRROR__HTTPS://GEM__REPO2/" => "https://gem.mirror")
+ end
+
+ it "fails to install the gem with a timeout error when the mirror is not responding" do
+ bundle :install, artifice: "compact_index_mirror_down", raise_on_error: false
+
+ expect(out).to be_empty
+ expect(err).to eq("Could not reach host gem.mirror. Check your network connection and try again.")
+ end
+ end
+
+ context "with a global mirror without a fallback timeout" do
+ before do
+ bundle_config_global("BUNDLE_MIRROR__ALL" => "https://gem.mirror")
+ end
+
+ it "fails to install the gem with a timeout error when the mirror is not responding" do
+ bundle :install, artifice: "compact_index_mirror_down", raise_on_error: false
+
+ expect(out).to be_empty
+ expect(err).to eq("Could not reach host gem.mirror. Check your network connection and try again.")
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/mirror_spec.rb b/spec/bundler/install/gems/mirror_spec.rb
index 70c0da50ef..e1fbeac454 100644
--- a/spec/bundler/install/gems/mirror_spec.rb
+++ b/spec/bundler/install/gems/mirror_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe "bundle install with a mirror configured" do
gem "myrack"
G
- bundle "config set --local mirror.http://gems.example.org http://gem-mirror.example.org"
+ bundle_config "mirror.http://gems.example.org http://gem-mirror.example.org"
end
it "installs from the normal location" do
@@ -26,7 +26,7 @@ RSpec.describe "bundle install with a mirror configured" do
gem "myrack"
G
- bundle "config set --local mirror.https://gem.repo2 https://gem.repo1"
+ bundle_config "mirror.https://gem.repo2 https://gem.repo1"
end
it "installs the gem from the mirror" do
diff --git a/spec/bundler/install/gems/native_extensions_spec.rb b/spec/bundler/install/gems/native_extensions_spec.rb
index 874818fa87..d5b10d2c8f 100644
--- a/spec/bundler/install/gems/native_extensions_spec.rb
+++ b/spec/bundler/install/gems/native_extensions_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe "installing a gem with native extensions" do
require "mkmf"
name = "c_extension_bundle"
dir_config(name)
- raise "OMG" unless with_config("c_extension") == "hello"
+ raise ArgumentError unless with_config("c_extension") == "hello"
create_makefile(name)
E
@@ -37,7 +37,7 @@ RSpec.describe "installing a gem with native extensions" do
gem "c_extension"
G
- bundle "config set build.c_extension --with-c_extension=hello"
+ bundle_config "build.c_extension --with-c_extension=hello"
bundle "install"
expect(out).to include("Installing c_extension 1.0 with native extensions")
@@ -53,7 +53,7 @@ RSpec.describe "installing a gem with native extensions" do
require "mkmf"
name = "c_extension_bundle"
dir_config(name)
- raise "OMG" unless with_config("c_extension") == "hello"
+ raise ArgumentError unless with_config("c_extension") == "hello"
create_makefile(name)
E
@@ -75,7 +75,7 @@ RSpec.describe "installing a gem with native extensions" do
C
end
- bundle "config set build.c_extension --with-c_extension=hello"
+ bundle_config "build.c_extension --with-c_extension=hello"
install_gemfile <<-G
source "https://gem.repo1"
@@ -97,7 +97,7 @@ RSpec.describe "installing a gem with native extensions" do
require "mkmf"
name = "c_extension_bundle_#{n}"
dir_config(name)
- raise "OMG" unless with_config("c_extension_#{n}") == "#{n}"
+ raise ArgumentError unless with_config("c_extension_#{n}") == "#{n}"
create_makefile(name)
E
@@ -122,8 +122,8 @@ RSpec.describe "installing a gem with native extensions" do
build_git "gems", path: lib_path("gems"), gemspec: false
end
- bundle "config set build.c_extension_one --with-c_extension_one=one"
- bundle "config set build.c_extension_two --with-c_extension_two=two"
+ bundle_config "build.c_extension_one --with-c_extension_one=one"
+ bundle_config "build.c_extension_two --with-c_extension_two=two"
# 1st time, require only one gem -- only one of the extensions gets built.
install_gemfile <<-G
@@ -149,7 +149,7 @@ RSpec.describe "installing a gem with native extensions" do
require "mkmf"
name = "c_extension_bundle"
dir_config(name)
- raise "OMG" unless with_config("c_extension") == "hello" && with_config("c_extension_bundle-dir") == "hola"
+ raise ArgumentError unless with_config("c_extension") == "hello" && with_config("c_extension_bundle-dir") == "hola"
create_makefile(name)
E
@@ -171,7 +171,7 @@ RSpec.describe "installing a gem with native extensions" do
C
end
- bundle "config set build.c_extension --with-c_extension=hello --with-c_extension_bundle-dir=hola"
+ bundle_config "build.c_extension --with-c_extension=hello --with-c_extension_bundle-dir=hola"
install_gemfile <<-G
source "https://gem.repo1"
diff --git a/spec/bundler/install/gems/no_build_extension_spec.rb b/spec/bundler/install/gems/no_build_extension_spec.rb
new file mode 100644
index 0000000000..31f0170433
--- /dev/null
+++ b/spec/bundler/install/gems/no_build_extension_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with --no-build-extension" do
+ before do
+ build_repo2 do
+ build_gem "with_extension" do |s|
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("lib", __dir__)
+ FileUtils.mkdir_p(path)
+ File.open("\#{path}/with_extension.rb", "w") do |f|
+ f.puts "WITH_EXTENSION = 'YES'"
+ end
+ end
+ RUBY
+ end
+ end
+ end
+
+ it "skips building native extensions and warns when no_build_extension is set" do
+ bundle_config "no_build_extension true"
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "with_extension"
+ gem "rake"
+ G
+
+ bundle :install
+
+ build_complete = default_bundle_path("extensions").join(
+ Gem::Platform.local.to_s,
+ Gem.extension_api_version.to_s,
+ "with_extension-1.0",
+ "gem.build_complete"
+ )
+ expect(build_complete).not_to exist
+ expect(err).to include("with_extension-1.0 contains native extensions that were not built")
+ expect(err).to include("unset no_build_extension and run `bundle pristine with_extension`")
+ end
+
+ it "builds native extensions by default" do
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "with_extension"
+ gem "rake"
+ G
+
+ bundle :install
+
+ expect(out).to include("Installing with_extension 1.0 with native extensions")
+ end
+end
diff --git a/spec/bundler/install/gems/no_install_plugin_spec.rb b/spec/bundler/install/gems/no_install_plugin_spec.rb
new file mode 100644
index 0000000000..e040e6b813
--- /dev/null
+++ b/spec/bundler/install/gems/no_install_plugin_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle install with --no-install-plugin" do
+ before do
+ build_repo2 do
+ build_gem "with_plugin", "1.0" do |s|
+ s.write "lib/rubygems_plugin.rb", "# plugin code"
+ end
+
+ build_gem "with_plugin", "2.0"
+ end
+ end
+
+ let(:plugin_path) { default_bundle_path("plugins", "with_plugin_plugin.rb") }
+
+ it "does not generate the plugin wrapper and warns when no_install_plugin is set" do
+ bundle_config "no_install_plugin true"
+
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "with_plugin", "1.0"
+ G
+
+ expect(plugin_path).not_to exist
+ expect(err).to include("with_plugin-1.0 contains plugins that were not installed")
+ expect(err).to include("unset no_install_plugin and run `bundle pristine with_plugin`")
+ end
+
+ it "removes a stale plugin wrapper from a prior version when no_install_plugin is set" do
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "with_plugin", "1.0"
+ G
+ expect(plugin_path).to exist
+
+ bundle_config "no_install_plugin true"
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "with_plugin", "2.0"
+ G
+
+ expect(plugin_path).not_to exist
+ end
+
+ it "generates the plugin wrapper by default" do
+ install_gemfile <<-G
+ source "https://gem.repo2"
+ gem "with_plugin", "1.0"
+ G
+
+ expect(plugin_path).to exist
+ end
+end
diff --git a/spec/bundler/install/gems/post_install_spec.rb b/spec/bundler/install/gems/post_install_spec.rb
index af753dba3e..e49fd2a9a3 100644
--- a/spec/bundler/install/gems/post_install_spec.rb
+++ b/spec/bundler/install/gems/post_install_spec.rb
@@ -127,7 +127,7 @@ RSpec.describe "bundle install" do
gem "myrack"
G
- bundle "config set ignore_messages.myrack true"
+ bundle_config "ignore_messages.myrack true"
bundle :install
expect(out).not_to include("Post-install message")
@@ -141,7 +141,7 @@ RSpec.describe "bundle install" do
gem "myrack"
G
- bundle "config set ignore_messages true"
+ bundle_config "ignore_messages true"
bundle :install
expect(out).not_to include("Post-install message")
diff --git a/spec/bundler/install/gems/resolving_spec.rb b/spec/bundler/install/gems/resolving_spec.rb
index b4af8ab6d4..111d361aab 100644
--- a/spec/bundler/install/gems/resolving_spec.rb
+++ b/spec/bundler/install/gems/resolving_spec.rb
@@ -275,7 +275,7 @@ RSpec.describe "bundle install with install-time dependencies" do
parallel_tests
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -299,17 +299,16 @@ RSpec.describe "bundle install with install-time dependencies" do
parallel_tests
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
it "gives a meaningful error if we're in frozen mode" do
expect do
- bundle "install --verbose", env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false
+ bundle "install", env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false
end.not_to change { lockfile }
- expect(err).to include("parallel_tests-3.8.0 requires ruby version >= #{next_ruby_minor}")
- expect(err).not_to include("That means the author of parallel_tests (3.8.0) has removed it.")
+ expect(err).to eq("parallel_tests-3.8.0 requires ruby version >= #{next_ruby_minor}, which is incompatible with the current version, #{Gem.ruby_version}")
end
end
@@ -359,10 +358,10 @@ RSpec.describe "bundle install with install-time dependencies" do
#{lockfile_platforms}
DEPENDENCIES
- parallel_tests
+ rubocop
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -389,12 +388,12 @@ RSpec.describe "bundle install with install-time dependencies" do
rubocop
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
- context "with a Gemfile and lock file that don't resolve under the current platform" do
+ context "with a Gemfile and lockfile that don't resolve under the current platform" do
before do
build_repo4 do
build_gem "sorbet", "0.5.10554" do |s|
@@ -426,7 +425,7 @@ RSpec.describe "bundle install with install-time dependencies" do
sorbet (= 0.5.10554)
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -441,7 +440,9 @@ RSpec.describe "bundle install with install-time dependencies" do
The source contains the following gems matching 'sorbet-static (= 0.5.10554)':
* sorbet-static-0.5.10554-universal-darwin-21
E
- expect(err).to end_with(nice_error)
+ expect(err).to include(nice_error)
+ expect(err).to include("Your current platform (aarch64-linux) is not included in the lockfile's platforms (arm64-darwin-21)")
+ expect(err).to include("bundle lock --add-platform aarch64-linux")
end
end
@@ -476,7 +477,7 @@ RSpec.describe "bundle install with install-time dependencies" do
nokogiri
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
gemfile <<~G
@@ -518,7 +519,7 @@ RSpec.describe "bundle install with install-time dependencies" do
nokogiri
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -540,7 +541,7 @@ RSpec.describe "bundle install with install-time dependencies" do
lockfile original_lockfile
end
- it "keeps both variants in the lockfile, and uses the generic one since it's compatible" do
+ it "keeps both variants in the lockfile when installing, and uses the generic one since it's compatible" do
simulate_platform "x86_64-linux" do
bundle "install --verbose"
@@ -548,6 +549,15 @@ RSpec.describe "bundle install with install-time dependencies" do
expect(the_bundle).to include_gems("nokogiri 1.16.3")
end
end
+
+ it "keeps both variants in the lockfile when updating, and uses the generic one since it's compatible" do
+ simulate_platform "x86_64-linux" do
+ bundle "update --verbose"
+
+ expect(lockfile).to eq(original_lockfile)
+ expect(the_bundle).to include_gems("nokogiri 1.16.3")
+ end
+ end
end
it "gives a meaningful error on ruby version mismatches between dependencies" do
@@ -606,13 +616,13 @@ RSpec.describe "bundle install with install-time dependencies" do
s.required_ruby_version = "> 9000"
end
build_gem "myrack", "1.2" do |s|
- s.platform = x86_mingw32
+ s.platform = "x86-mingw32"
s.required_ruby_version = "> 9000"
end
build_gem "myrack", "1.2"
end
- simulate_platform x86_mingw32 do
+ simulate_platform "x86-mingw32" do
install_gemfile <<-G, artifice: "compact_index"
ruby "#{Gem.ruby_version}"
source "https://gem.repo4"
@@ -768,7 +778,7 @@ RSpec.describe "bundle install with install-time dependencies" do
foo
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
diff --git a/spec/bundler/install/gems/standalone_spec.rb b/spec/bundler/install/gems/standalone_spec.rb
index abd77d33de..96a305bb76 100644
--- a/spec/bundler/install/gems/standalone_spec.rb
+++ b/spec/bundler/install/gems/standalone_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_examples "bundle install --standalone" do
+RSpec.describe "bundle install --standalone" do
shared_examples "common functionality" do
it "still makes the gems available to normal bundler" do
args = expected_gems.map {|k, v| "#{k} #{v}" }
@@ -42,7 +42,7 @@ RSpec.shared_examples "bundle install --standalone" do
testrb << "\nrequire \"#{k}\""
testrb << "\nputs #{k.upcase}"
end
- sys_exec %(#{Gem.ruby} --disable-gems -w -e #{testrb.shellescape})
+ in_bundled_app %(#{Gem.ruby} --disable-gems -w -e #{testrb.shellescape})
expect(out).to eq(expected_gems.values.join("\n"))
end
@@ -113,7 +113,7 @@ RSpec.shared_examples "bundle install --standalone" do
testrb << "\nrequire \"#{k}\""
testrb << "\nputs #{k.upcase}"
end
- sys_exec %(#{Gem.ruby} -w -e #{testrb.shellescape})
+ in_bundled_app %(#{Gem.ruby} -w -e #{testrb.shellescape})
expect(out).to eq(expected_gems.values.join("\n"))
end
@@ -125,7 +125,7 @@ RSpec.shared_examples "bundle install --standalone" do
source "https://gem.repo1"
gem "rails"
G
- bundle "config set --local path #{bundled_app("bundle")}"
+ bundle_config "path #{bundled_app("bundle")}"
bundle :install, standalone: true, dir: cwd
end
@@ -140,15 +140,8 @@ RSpec.shared_examples "bundle install --standalone" do
end
describe "with default gems and a lockfile", :ruby_repo do
- before do
- necessary_system_gems = ["tsort --version 0.1.0"]
- realworld_system_gems(*necessary_system_gems)
- end
-
it "works and points to the vendored copies, not to the default copies" do
- necessary_gems_in_bundle_path = ["optparse --version 0.1.1", "psych --version 3.3.2", "logger --version 1.4.3", "etc --version 1.4.3", "stringio --version 3.1.0"]
- necessary_gems_in_bundle_path += ["yaml --version 0.1.1"] if Gem.rubygems_version < Gem::Version.new("3.4.a")
- realworld_system_gems(*necessary_gems_in_bundle_path, path: scoped_gem_path(bundled_app("bundle")))
+ base_system_gems "stringio", "psych", "etc", path: scoped_gem_path(bundled_app("bundle"))
build_gem "foo", "1.0.0", to_system: true, default: true do |s|
s.add_dependency "bar"
@@ -171,17 +164,9 @@ RSpec.shared_examples "bundle install --standalone" do
bundle "lock", dir: cwd
- bundle "config set --local path #{bundled_app("bundle")}"
+ bundle_config "path #{bundled_app("bundle")}"
- # Make sure rubyinstaller2 does not activate the etc gem in its
- # `operating_system.rb` file, but completely disable that since it's not
- # really needed here
- if Gem.win_platform?
- FileUtils.mkdir_p bundled_app("rubygems/defaults")
- FileUtils.touch bundled_app("rubygems/defaults/operating_system.rb")
- end
-
- bundle :install, standalone: true, dir: cwd, env: { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s }, load_path: bundled_app
+ bundle :install, standalone: true, dir: cwd, env: { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s }
load_path_lines = bundled_app("bundle/bundler/setup.rb").read.split("\n").select {|line| line.start_with?("$:.unshift") }
@@ -193,9 +178,7 @@ RSpec.shared_examples "bundle install --standalone" do
it "works for gems with extensions and points to the vendored copies, not to the default copies" do
simulate_platform "arm64-darwin-23" do
- necessary_gems_in_bundle_path = ["optparse --version 0.1.1", "psych --version 3.3.2", "logger --version 1.4.3", "etc --version 1.4.3", "stringio --version 3.1.0", "shellwords --version 0.2.0", "open3 --version 0.2.1"]
- necessary_gems_in_bundle_path += ["yaml --version 0.1.1"] if Gem.rubygems_version < Gem::Version.new("3.4.a")
- realworld_system_gems(*necessary_gems_in_bundle_path, path: scoped_gem_path(bundled_app("bundle")))
+ base_system_gems "stringio", "psych", "etc", "shellwords", "open3", path: scoped_gem_path(bundled_app("bundle"))
build_gem "baz", "1.0.0", to_system: true, default: true, &:add_c_extension
@@ -208,19 +191,11 @@ RSpec.shared_examples "bundle install --standalone" do
gem "baz"
G
- bundle "config set --local path #{bundled_app("bundle")}"
+ bundle_config "path #{bundled_app("bundle")}"
bundle "lock", dir: cwd
- # Make sure rubyinstaller2 does not activate the etc gem in its
- # `operating_system.rb` file, but completely disable that since it's not
- # really needed here
- if Gem.win_platform?
- FileUtils.mkdir_p bundled_app("rubygems/defaults")
- FileUtils.touch bundled_app("rubygems/defaults/operating_system.rb")
- end
-
- bundle :install, standalone: true, dir: cwd, env: { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s }, load_path: bundled_app
+ bundle :install, standalone: true, dir: cwd, env: { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s }
end
load_path_lines = bundled_app("bundle/bundler/setup.rb").read.split("\n").select {|line| line.start_with?("$:.unshift") }
@@ -262,6 +237,8 @@ RSpec.shared_examples "bundle install --standalone" do
end
end
+ let(:cwd) { bundled_app }
+
describe "with Gemfiles using relative path sources and app moved to a different root" do
before do
FileUtils.mkdir_p bundled_app("app/vendor")
@@ -293,7 +270,7 @@ RSpec.shared_examples "bundle install --standalone" do
describe "with gems with native extension" do
before do
- bundle "config set --local path #{bundled_app("bundle")}"
+ bundle_config "path #{bundled_app("bundle")}"
install_gemfile <<-G, standalone: true, dir: cwd
source "https://gem.repo1"
gem "very_simple_binary"
@@ -331,7 +308,7 @@ RSpec.shared_examples "bundle install --standalone" do
end
G
end
- bundle "config set --local path #{bundled_app("bundle")}"
+ bundle_config "path #{bundled_app("bundle")}"
install_gemfile <<-G, standalone: true, dir: cwd, raise_on_error: false
source "https://gem.repo1"
gem "bar", :git => "#{lib_path("bar-1.0")}"
@@ -353,7 +330,7 @@ RSpec.shared_examples "bundle install --standalone" do
gem "rails"
gem "devise", :git => "#{lib_path("devise-1.0")}"
G
- bundle "config set --local path #{bundled_app("bundle")}"
+ bundle_config "path #{bundled_app("bundle")}"
bundle :install, standalone: true, dir: cwd
end
@@ -381,7 +358,7 @@ RSpec.shared_examples "bundle install --standalone" do
gem "myrack-test"
end
G
- bundle "config set --local path #{bundled_app("bundle")}"
+ bundle_config "path #{bundled_app("bundle")}"
bundle :install, standalone: true, dir: cwd
end
@@ -395,7 +372,7 @@ RSpec.shared_examples "bundle install --standalone" do
include_examples "common functionality"
it "allows creating a standalone file with limited groups" do
- bundle "config set --local path #{bundled_app("bundle")}"
+ bundle_config "path #{bundled_app("bundle")}"
bundle :install, standalone: "default", dir: cwd
load_error_ruby <<-RUBY, "spec"
@@ -408,12 +385,12 @@ RSpec.shared_examples "bundle install --standalone" do
RUBY
expect(out).to eq("2.3.2")
- expect(err).to eq("ZOMG LOAD ERROR")
+ expect(err_without_deprecations).to match(/cannot load such file -- spec/)
end
it "allows `without` configuration to limit the groups used in a standalone" do
- bundle "config set --local path #{bundled_app("bundle")}"
- bundle "config set --local without test"
+ bundle_config "path #{bundled_app("bundle")}"
+ bundle_config "without test"
bundle :install, standalone: true, dir: cwd
load_error_ruby <<-RUBY, "spec"
@@ -426,11 +403,11 @@ RSpec.shared_examples "bundle install --standalone" do
RUBY
expect(out).to eq("2.3.2")
- expect(err).to eq("ZOMG LOAD ERROR")
+ expect(err_without_deprecations).to match(/cannot load such file -- spec/)
end
it "allows `path` configuration to change the location of the standalone bundle" do
- bundle "config set --local path path/to/bundle"
+ bundle_config "path path/to/bundle"
bundle "install", standalone: true, dir: cwd
ruby <<-RUBY
@@ -445,9 +422,9 @@ RSpec.shared_examples "bundle install --standalone" do
end
it "allows `without` to limit the groups used in a standalone" do
- bundle "config set --local without test"
+ bundle_config "without test"
bundle :install, dir: cwd
- bundle "config set --local path #{bundled_app("bundle")}"
+ bundle_config "path #{bundled_app("bundle")}"
bundle :install, standalone: true, dir: cwd
load_error_ruby <<-RUBY, "spec"
@@ -460,7 +437,7 @@ RSpec.shared_examples "bundle install --standalone" do
RUBY
expect(out).to eq("2.3.2")
- expect(err).to eq("ZOMG LOAD ERROR")
+ expect(err_without_deprecations).to match(/cannot load such file -- spec/)
end
end
@@ -473,7 +450,7 @@ RSpec.shared_examples "bundle install --standalone" do
source "#{source_uri}"
gem "rails"
G
- bundle "config set --local path #{bundled_app("bundle")}"
+ bundle_config "path #{bundled_app("bundle")}"
bundle :install, standalone: true, artifice: "endpoint", dir: cwd
end
@@ -487,67 +464,37 @@ RSpec.shared_examples "bundle install --standalone" do
include_examples "common functionality"
end
end
+end
- describe "with --binstubs", bundler: "< 3" do
- before do
- gemfile <<-G
- source "https://gem.repo1"
- gem "rails"
- G
- bundle "config set --local path #{bundled_app("bundle")}"
- bundle :install, standalone: true, binstubs: true, dir: cwd
- end
+RSpec.describe "bundle install --standalone run in a subdirectory" do
+ let(:cwd) { bundled_app("bob").tap(&:mkpath) }
- let(:expected_gems) do
- {
- "actionpack" => "2.3.2",
- "rails" => "2.3.2",
- }
- end
+ before do
+ gemfile <<-G
+ source "https://gem.repo1"
+ gem "rails"
+ G
+ end
- include_examples "common functionality"
+ it "generates the script in the proper place" do
+ bundle :install, standalone: true, dir: cwd
- it "creates stubs that use the standalone load path" do
- expect(sys_exec("bin/rails -v").chomp).to eql "2.3.2"
- end
+ expect(bundled_app("bundle/bundler/setup.rb")).to exist
+ end
- it "creates stubs that can be executed from anywhere" do
- require "tmpdir"
- sys_exec(%(#{bundled_app("bin/rails")} -v), dir: Dir.tmpdir)
- expect(out).to eq("2.3.2")
+ context "when path set to a relative path" do
+ before do
+ bundle_config "path bundle"
end
- it "creates stubs that can be symlinked" do
- skip "symlinks unsupported" if Gem.win_platform?
-
- symlink_dir = tmp("symlink")
- FileUtils.mkdir_p(symlink_dir)
- symlink = File.join(symlink_dir, "rails")
-
- File.symlink(bundled_app("bin/rails"), symlink)
- sys_exec("#{symlink} -v")
- expect(out).to eq("2.3.2")
- end
+ it "generates the script in the proper place" do
+ bundle :install, standalone: true, dir: cwd
- it "creates stubs with the correct load path" do
- extension_line = File.read(bundled_app("bin/rails")).each_line.find {|line| line.include? "$:.unshift" }.strip
- expect(extension_line).to eq %($:.unshift File.expand_path "../bundle", __dir__)
+ expect(bundled_app("bundle/bundler/setup.rb")).to exist
end
end
end
-RSpec.describe "bundle install --standalone" do
- let(:cwd) { bundled_app }
-
- include_examples("bundle install --standalone")
-end
-
-RSpec.describe "bundle install --standalone run in a subdirectory" do
- let(:cwd) { bundled_app("bob").tap(&:mkpath) }
-
- include_examples("bundle install --standalone")
-end
-
RSpec.describe "bundle install --standalone --local" do
before do
gemfile <<-G
@@ -572,6 +519,6 @@ RSpec.describe "bundle install --standalone --local" do
RUBY
expect(out).to eq("1.0.0")
- expect(err).to eq("ZOMG LOAD ERROR")
+ expect(err_without_deprecations).to match(/cannot load such file -- spec/)
end
end
diff --git a/spec/bundler/install/gemspecs_spec.rb b/spec/bundler/install/gemspecs_spec.rb
index dee8e547e4..fb2271c830 100644
--- a/spec/bundler/install/gemspecs_spec.rb
+++ b/spec/bundler/install/gemspecs_spec.rb
@@ -122,7 +122,7 @@ RSpec.describe "bundle install" do
expect(the_bundle).to include_gems "foo 1.0"
end
- it "fails and complains about patchlevel on patchlevel mismatch",
+ it "installs gems ignoring the mismatch even when patchlevel is mismatch",
if: RUBY_PATCHLEVEL >= 0 do
patchlevel = RUBY_PATCHLEVEL.to_i + 1
build_lib("foo", path: bundled_app) do |s|
@@ -135,9 +135,7 @@ RSpec.describe "bundle install" do
gemspec
G
- expect(err).to include("Ruby patchlevel")
- expect(err).to include("but your Gemfile specified")
- expect(exitstatus).to eq(18)
+ expect(the_bundle).to include_gems "foo 1.0"
end
it "fails and complains about version on version mismatch" do
diff --git a/spec/bundler/install/git_spec.rb b/spec/bundler/install/git_spec.rb
index d1f6b7a7ca..1172d661ae 100644
--- a/spec/bundler/install/git_spec.rb
+++ b/spec/bundler/install/git_spec.rb
@@ -28,14 +28,14 @@ RSpec.describe "bundle install" do
end
it "displays the correct default branch", git: ">= 2.28.0" do
- build_git "foo", "1.0", path: lib_path("foo"), default_branch: "main"
+ build_git "foo", "1.0", path: lib_path("foo"), default_branch: "non-standard"
install_gemfile <<-G, verbose: true
source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo")}"
G
- expect(out).to include("Using foo 1.0 from #{lib_path("foo")} (at main@#{revision_for(lib_path("foo"))[0..6]})")
+ expect(out).to include("Using foo 1.0 from #{lib_path("foo")} (at non-standard@#{revision_for(lib_path("foo"))[0..6]})")
expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}"
end
@@ -85,8 +85,8 @@ RSpec.describe "bundle install" do
foo!
L
- bundle "config set --local path vendor/bundle"
- bundle "config set --local without development"
+ bundle_config "path vendor/bundle"
+ bundle_config "without development"
bundle :install
expect(out).to include("Bundle complete!")
@@ -157,7 +157,7 @@ RSpec.describe "bundle install" do
test!
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
# If GH#6743 is present, the first `bundle install` will change the
@@ -188,8 +188,8 @@ RSpec.describe "bundle install" do
build_git "foo", "1.0", path: lib_path("foo")
rev = revision_for(lib_path("foo"))
- bundle "config set path vendor/bundle"
- bundle "config set clean true"
+ bundle_config "path vendor/bundle"
+ bundle_config "clean true"
install_gemfile <<-G, verbose: true
source "https://gem.repo1"
gem "foo", :git => "#{lib_path("foo")}"
@@ -214,5 +214,156 @@ RSpec.describe "bundle install" do
expect(out).to include("Using foo 1.0 from #{lib_path("foo")} (at main@#{rev[0..6]})")
expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}"
end
+
+ context "when install directory exists" do
+ let(:checkout_confirmation_log_message) { "Checking out revision" }
+ let(:using_foo_confirmation_log_message) { "Using foo 1.0 from #{lib_path("foo")} (at main@#{revision_for(lib_path("foo"))[0..6]})" }
+
+ context "and no contents besides .git directory are present" do
+ it "reinstalls gem" do
+ build_git "foo", "1.0", path: lib_path("foo")
+
+ gemfile = <<-G
+ source "https://gem.repo1"
+ gem "foo", :git => "#{lib_path("foo")}"
+ G
+
+ install_gemfile gemfile, verbose: true
+
+ expect(out).to include(checkout_confirmation_log_message)
+ expect(out).to include(using_foo_confirmation_log_message)
+ expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}"
+
+ # validate that the installed directory exists and has some expected contents
+ install_directory = default_bundle_path("bundler/gems/foo-#{revision_for(lib_path("foo"))[0..11]}")
+ dot_git_directory = install_directory.join(".git")
+ lib_directory = install_directory.join("lib")
+ gemspec = install_directory.join("foo.gemspec")
+ expect([install_directory, dot_git_directory, lib_directory, gemspec]).to all exist
+
+ # remove all elements in the install directory except .git directory
+ FileUtils.rm_r(lib_directory)
+ gemspec.delete
+
+ expect(dot_git_directory).to exist
+ expect(lib_directory).not_to exist
+ expect(gemspec).not_to exist
+
+ # rerun bundle install
+ install_gemfile gemfile, verbose: true
+
+ expect(out).to include(checkout_confirmation_log_message)
+ expect(out).to include(using_foo_confirmation_log_message)
+ expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}"
+
+ # validate that it reinstalls all components
+ expect([install_directory, dot_git_directory, lib_directory, gemspec]).to all exist
+ end
+ end
+
+ context "and contents besides .git directory are present" do
+ # we want to confirm that the change to try to detect partial installs and reinstall does not
+ # result in repeatedly reinstalling the gem when it is fully installed
+ it "does not reinstall gem" do
+ build_git "foo", "1.0", path: lib_path("foo")
+
+ gemfile = <<-G
+ source "https://gem.repo1"
+ gem "foo", :git => "#{lib_path("foo")}"
+ G
+
+ install_gemfile gemfile, verbose: true
+
+ expect(out).to include(checkout_confirmation_log_message)
+ expect(out).to include(using_foo_confirmation_log_message)
+ expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}"
+
+ # rerun bundle install
+ install_gemfile gemfile, verbose: true
+
+ # it isn't altogether straight-forward to validate that bundle didn't do soething on the second run, however,
+ # the presence of the 2nd log message confirms install got past the point that it would have logged the above if
+ # it was going to
+ expect(out).not_to include(checkout_confirmation_log_message)
+ expect(out).to include(using_foo_confirmation_log_message)
+ end
+ end
+ end
+ end
+
+ describe "with excluded groups" do
+ it "works if you exclude a group with a git gem", ruby: ">= 3.3" do
+ build_git "production_gem", "1.0"
+ build_git "development_gem", "1.0"
+
+ gemfile <<-G
+ source "https://gem.repo1"
+
+ gem "production_gem", :git => "#{lib_path("production_gem-1.0")}"
+
+ group :development do
+ gem "development_gem", :git => "#{lib_path("development_gem-1.0")}"
+ end
+ G
+
+ # First install all groups to create lockfile
+ bundle :install
+
+ # Set without and reinstall
+ bundle_config "without development"
+ bundle :install
+
+ # Verify only production gem is available
+ 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 "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 "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/spec/bundler/install/global_cache_spec.rb b/spec/bundler/install/global_cache_spec.rb
index df4559c42e..4cffa65b2a 100644
--- a/spec/bundler/install/global_cache_spec.rb
+++ b/spec/bundler/install/global_cache_spec.rb
@@ -1,18 +1,29 @@
# frozen_string_literal: true
RSpec.describe "global gem caching" do
+ # Uses subprocess because this setting must apply across multiple app directories (bundled_app and bundled_app2)
before { bundle "config set global_gem_cache true" }
describe "using the cross-application user cache" do
let(:source) { "http://localgemserver.test" }
let(:source2) { "http://gemserver.example.org" }
+ def cache_base
+ # Use the unified global gem cache path if available (from RubyGems),
+ # otherwise fall back to the Bundler-specific cache location
+ if Gem.respond_to?(:global_gem_cache_path)
+ Pathname.new(Gem.global_gem_cache_path)
+ else
+ home(".bundle", "cache", "gems")
+ end
+ end
+
def source_global_cache(*segments)
- home(".bundle", "cache", "gems", "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", *segments)
+ cache_base.join("localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", *segments)
end
def source2_global_cache(*segments)
- home(".bundle", "cache", "gems", "gemserver.example.org.80.1ae1663619ffe0a3c9d97712f44c705b", *segments)
+ cache_base.join("gemserver.example.org.80.1ae1663619ffe0a3c9d97712f44c705b", *segments)
end
it "caches gems into the global cache on download" do
@@ -38,6 +49,8 @@ RSpec.describe "global gem caching" do
end
it "shows a proper error message if a cached gem is corrupted" do
+ skip "This example is not working on ruby/ruby repo" if ruby_core?
+
source_global_cache.mkpath
FileUtils.touch(source_global_cache("myrack-1.0.0.gem"))
@@ -49,14 +62,49 @@ RSpec.describe "global gem caching" do
expect(err).to include("Gem::Package::FormatError: package metadata is missing in #{source_global_cache("myrack-1.0.0.gem")}")
end
+ it "uses a shorter path for the cache to not hit filesystem limits" do
+ install_gemfile <<-G, artifice: "compact_index", verbose: true
+ source "http://#{"a" * 255}.test"
+ gem "myrack"
+ G
+
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ source_segment = "a" * 222 + ".a3cb26de2edfce9f509a65c611d99c4b"
+ source_cache = cache_base.join(source_segment)
+ cached_gem = source_cache.join("myrack-1.0.0.gem")
+ expect(cached_gem).to exist
+ ensure
+ # We cleanup dummy files created by this spec manually because due to a
+ # Ruby on Windows bug, `FileUtils.rm_rf` (run in our global after hook)
+ # cannot traverse directories with such long names. So we delete
+ # everything explicitly to workaround the bug. An alternative workaround
+ # would be to shell out to `rm -rf`. That also works fine, but I went with
+ # the more verbose and explicit approach. This whole ensure block can be
+ # removed once/if https://bugs.ruby-lang.org/issues/21177 is fixed, and
+ # once the fix propagates to all supported rubies.
+ File.delete cached_gem
+ Dir.rmdir source_cache
+
+ File.delete compact_index_cache_path.join(source_segment, "info", "myrack")
+ Dir.rmdir compact_index_cache_path.join(source_segment, "info")
+ File.delete compact_index_cache_path.join(source_segment, "info-etags", "myrack-92f3313ce5721296f14445c3a6b9c073")
+ Dir.rmdir compact_index_cache_path.join(source_segment, "info-etags")
+ Dir.rmdir compact_index_cache_path.join(source_segment, "info-special-characters")
+ File.delete compact_index_cache_path.join(source_segment, "versions")
+ File.delete compact_index_cache_path.join(source_segment, "versions.etag")
+ Dir.rmdir compact_index_cache_path.join(source_segment)
+ end
+
describe "when the same gem from different sources is installed" do
it "should use the appropriate one from the global cache" do
+ bundle_config "path.system true"
+
install_gemfile <<-G, artifice: "compact_index"
source "#{source}"
gem "myrack"
G
- simulate_new_machine
+ pristine_system_gems
expect(the_bundle).not_to include_gems "myrack 1.0.0"
expect(source_global_cache("myrack-1.0.0.gem")).to exist
# myrack 1.0.0 is not installed and it is in the global cache
@@ -66,7 +114,7 @@ RSpec.describe "global gem caching" do
gem "myrack", "0.9.1"
G
- simulate_new_machine
+ pristine_system_gems
expect(the_bundle).not_to include_gems "myrack 0.9.1"
expect(source2_global_cache("myrack-0.9.1.gem")).to exist
# myrack 0.9.1 is not installed and it is in the global cache
@@ -80,7 +128,7 @@ RSpec.describe "global gem caching" do
# myrack 1.0.0 is installed and myrack 0.9.1 is not
expect(the_bundle).to include_gems "myrack 1.0.0"
expect(the_bundle).not_to include_gems "myrack 0.9.1"
- simulate_new_machine
+ pristine_system_gems
gemfile <<-G
source "#{source2}"
@@ -94,13 +142,15 @@ RSpec.describe "global gem caching" do
end
it "should not install if the wrong source is provided" do
+ bundle_config "path.system true"
+
gemfile <<-G
source "#{source}"
gem "myrack"
G
bundle :install, artifice: "compact_index"
- simulate_new_machine
+ pristine_system_gems
expect(the_bundle).not_to include_gems "myrack 1.0.0"
expect(source_global_cache("myrack-1.0.0.gem")).to exist
# myrack 1.0.0 is not installed and it is in the global cache
@@ -111,7 +161,7 @@ RSpec.describe "global gem caching" do
G
bundle :install, artifice: "compact_index"
- simulate_new_machine
+ pristine_system_gems
expect(the_bundle).not_to include_gems "myrack 0.9.1"
expect(source2_global_cache("myrack-0.9.1.gem")).to exist
# myrack 0.9.1 is not installed and it is in the global cache
@@ -150,6 +200,8 @@ RSpec.describe "global gem caching" do
describe "when installing gems from a different directory" do
it "uses the global cache as a source" do
+ bundle_config "path.system true"
+
install_gemfile <<-G, artifice: "compact_index"
source "#{source}"
gem "myrack"
@@ -161,7 +213,7 @@ RSpec.describe "global gem caching" do
expect(the_bundle).to include_gems "activesupport 2.3.5"
expect(source_global_cache("myrack-1.0.0.gem")).to exist
expect(source_global_cache("activesupport-2.3.5.gem")).to exist
- simulate_new_machine
+ pristine_system_gems
# Both gems are now only in the global cache
expect(the_bundle).not_to include_gems "myrack 1.0.0"
expect(the_bundle).not_to include_gems "activesupport 2.3.5"
@@ -205,6 +257,7 @@ RSpec.describe "global gem caching" do
describe "extension caching" do
it "works" do
skip "gets incorrect ref in path" if Gem.win_platform?
+ skip "fails for unknown reason when run by ruby-core" if ruby_core?
build_git "very_simple_git_binary", &:add_c_extension
build_lib "very_simple_path_binary", &:add_c_extension
@@ -232,12 +285,12 @@ RSpec.describe "global gem caching" do
R
expect(out).to eq "VERY_SIMPLE_BINARY_IN_C\nVERY_SIMPLE_GIT_BINARY_IN_C"
- FileUtils.rm_rf Dir[home(".bundle", "cache", "extensions", "**", "*binary_c*")]
+ FileUtils.rm_r Dir[home(".bundle", "cache", "extensions", "**", "*binary_c*")]
gem_binary_cache.join("very_simple_binary_c.rb").open("w") {|f| f << "puts File.basename(__FILE__)" }
git_binary_cache.join("very_simple_git_binary_c.rb").open("w") {|f| f << "puts File.basename(__FILE__)" }
- bundle "config set --local path different_path"
+ bundle_config "path different_path"
bundle :install
expect(Dir[home(".bundle", "cache", "extensions", "**", "*binary_c*")]).to all(end_with(".rb"))
diff --git a/spec/bundler/install/path_spec.rb b/spec/bundler/install/path_spec.rb
index 8d32e033d6..49360e511e 100644
--- a/spec/bundler/install/path_spec.rb
+++ b/spec/bundler/install/path_spec.rb
@@ -14,14 +14,14 @@ RSpec.describe "bundle install" do
end
it "does not use available system gems with `vendor/bundle" do
- bundle "config set --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle :install
expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "uses system gems with `path.system` configured with more priority than `path`" do
- bundle "config set --local path.system true"
- bundle "config set --global path vendor/bundle"
+ bundle_config "path.system true"
+ bundle_config_global "path vendor/bundle"
bundle :install
run "require 'myrack'", raise_on_error: false
expect(out).to include("FAIL")
@@ -31,57 +31,45 @@ RSpec.describe "bundle install" do
dir = bundled_app("bun++dle")
dir.mkpath
- bundle "config set --local path #{dir.join("vendor/bundle")}"
+ bundle_config "path #{dir.join("vendor/bundle")}"
bundle :install, dir: dir
expect(out).to include("installed into `./vendor/bundle`")
- dir.rmtree
+ FileUtils.rm_rf dir
end
it "prints a message to let the user know where gems where installed" do
- bundle "config set --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle :install
expect(out).to include("gems are installed into `./vendor/bundle`")
end
- it "disallows --path vendor/bundle --system", bundler: "< 3" do
- bundle "install --path vendor/bundle --system", raise_on_error: false
- expect(err).to include("Please choose only one option.")
- expect(exitstatus).to eq(15)
+ it "installs the bundle relatively to repository root, when Bundler run from the same directory" do
+ bundle "config set path vendor/bundle", dir: bundled_app.parent
+ bundle "install --gemfile='#{bundled_app}/Gemfile'", dir: bundled_app.parent
+ expect(out).to include("installed into `./bundled_app/vendor/bundle`")
+ expect(bundled_app("vendor/bundle")).to be_directory
+ expect(the_bundle).to include_gems "myrack 1.0.0"
end
- it "remembers to disable system gems after the first time with bundle --path vendor/bundle", bundler: "< 3" do
- bundle "install --path vendor/bundle"
- FileUtils.rm_rf bundled_app("vendor")
- bundle "install"
-
- expect(vendored_gems("gems/myrack-1.0.0")).to be_directory
+ it "installs the bundle relatively to repository root, when Bundler run from a different directory" do
+ bundle "config set path vendor/bundle", dir: bundled_app
+ bundle "install --gemfile='#{bundled_app}/Gemfile'", dir: bundled_app.parent
+ expect(out).to include("installed into `./bundled_app/vendor/bundle`")
+ expect(bundled_app("vendor/bundle")).to be_directory
expect(the_bundle).to include_gems "myrack 1.0.0"
end
- context "with path_relative_to_cwd set to true" do
- before { bundle "config set path_relative_to_cwd true" }
-
- it "installs the bundle relatively to current working directory", bundler: "< 3" do
- bundle "install --gemfile='#{bundled_app}/Gemfile' --path vendor/bundle", dir: bundled_app.parent
- expect(out).to include("installed into `./vendor/bundle`")
- expect(bundled_app("../vendor/bundle")).to be_directory
- expect(the_bundle).to include_gems "myrack 1.0.0"
- end
-
- it "installs the standalone bundle relative to the cwd" do
- bundle :install, gemfile: bundled_app_gemfile, standalone: true, dir: bundled_app.parent
- expect(out).to include("installed into `./bundled_app/bundle`")
- expect(bundled_app("bundle")).to be_directory
- expect(bundled_app("bundle/ruby")).to be_directory
+ it "installs the standalone bundle relative to the cwd" do
+ bundle :install, gemfile: bundled_app_gemfile, standalone: true, dir: bundled_app.parent
+ expect(out).to include("installed into `./bundled_app/bundle`")
+ expect(bundled_app("bundle")).to be_directory
+ expect(bundled_app("bundle/ruby")).to be_directory
- bundle "config unset path"
-
- bundle :install, gemfile: bundled_app_gemfile, standalone: true, dir: bundled_app("subdir").tap(&:mkpath)
- expect(out).to include("installed into `../bundle`")
- expect(bundled_app("bundle")).to be_directory
- expect(bundled_app("bundle/ruby")).to be_directory
- end
+ bundle :install, gemfile: bundled_app_gemfile, standalone: true, dir: bundled_app("subdir").tap(&:mkpath)
+ expect(out).to include("installed into `../bundle`")
+ expect(bundled_app("bundle")).to be_directory
+ expect(bundled_app("bundle/ruby")).to be_directory
end
end
@@ -109,7 +97,7 @@ RSpec.describe "bundle install" do
context "when set via #{type}" do
it "installs gems to a path if one is specified" do
set_bundle_path(type, bundled_app("vendor2").to_s)
- bundle "config set --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle :install
expect(vendored_gems("gems/myrack-1.0.0")).to be_directory
@@ -119,7 +107,7 @@ RSpec.describe "bundle install" do
it "installs gems to ." do
set_bundle_path(type, ".")
- bundle "config set --global disable_shared_gems true"
+ bundle_config_global "disable_shared_gems true"
bundle :install
@@ -150,7 +138,7 @@ RSpec.describe "bundle install" do
end
it "installs gems to BUNDLE_PATH from .bundle/config" do
- config "BUNDLE_PATH" => bundled_app("vendor/bundle").to_s
+ bundle_config "BUNDLE_PATH" => bundled_app("vendor/bundle").to_s
bundle :install
@@ -159,7 +147,7 @@ RSpec.describe "bundle install" do
end
it "sets BUNDLE_PATH as the first argument to bundle install" do
- bundle "config set --local path ./vendor/bundle"
+ bundle_config "path ./vendor/bundle"
bundle :install
expect(vendored_gems("gems/myrack-1.0.0")).to be_directory
@@ -169,7 +157,7 @@ RSpec.describe "bundle install" do
it "disables system gems when passing a path to install" do
# This is so that vendored gems can be distributed to others
build_gem "myrack", "1.1.0", to_system: true
- bundle "config set --local path ./vendor/bundle"
+ bundle_config "path ./vendor/bundle"
bundle :install
expect(vendored_gems("gems/myrack-1.0.0")).to be_directory
@@ -186,19 +174,19 @@ RSpec.describe "bundle install" do
gem "very_simple_binary"
G
- bundle "config set --local path ./vendor/bundle"
+ bundle_config "path ./vendor/bundle"
bundle :install
expect(vendored_gems("gems/very_simple_binary-1.0")).to be_directory
expect(vendored_gems("extensions")).to be_directory
expect(the_bundle).to include_gems "very_simple_binary 1.0", source: "remote1"
- vendored_gems("extensions").rmtree
+ FileUtils.rm_rf vendored_gems("extensions")
run "require 'very_simple_binary_c'", raise_on_error: false
expect(err).to include("Bundler::GemNotFound")
- bundle "config set --local path ./vendor/bundle"
+ bundle_config "path ./vendor/bundle"
bundle :install
expect(vendored_gems("gems/very_simple_binary-1.0")).to be_directory
@@ -218,7 +206,7 @@ RSpec.describe "bundle install" do
gem "myrack"
G
- bundle "config set --local path bundle"
+ bundle_config "path bundle"
bundle :install, raise_on_error: false
expect(err).to include("file already exists")
end
diff --git a/spec/bundler/install/prereleases_spec.rb b/spec/bundler/install/prereleases_spec.rb
index 57764ce722..9f764d127c 100644
--- a/spec/bundler/install/prereleases_spec.rb
+++ b/spec/bundler/install/prereleases_spec.rb
@@ -41,7 +41,7 @@ RSpec.describe "bundle install" do
build_repo3 do
build_gem "myrack"
end
- FileUtils.rm_rf Dir[gem_repo3("prerelease*")]
+ FileUtils.rm_r Dir[gem_repo3("prerelease*")]
install_gemfile <<-G
source "https://gem.repo3"
diff --git a/spec/bundler/install/process_lock_spec.rb b/spec/bundler/install/process_lock_spec.rb
index 707d2fde5e..b096291d1a 100644
--- a/spec/bundler/install/process_lock_spec.rb
+++ b/spec/bundler/install/process_lock_spec.rb
@@ -21,20 +21,93 @@ RSpec.describe "process lock spec" do
expect(the_bundle).to include_gems "myrack 1.0"
end
+ context "when creating a lock raises Errno::ENOTSUP" do
+ before { allow(File).to receive(:open).and_raise(Errno::ENOTSUP) }
+
+ it "skips creating the lockfile and yields" do
+ processed = false
+ Bundler::ProcessLock.lock(default_bundle_path) { processed = true }
+
+ expect(processed).to eq true
+ end
+ end
+
context "when creating a lock raises Errno::EPERM" do
before { allow(File).to receive(:open).and_raise(Errno::EPERM) }
- it "raises a friendly error" do
- expect { Bundler::ProcessLock.lock(default_bundle_path) }.to raise_error(Bundler::GenericSystemCallError)
+ it "skips creating the lockfile and yields" do
+ processed = false
+ Bundler::ProcessLock.lock(default_bundle_path) { processed = true }
+
+ expect(processed).to eq true
end
end
context "when creating a lock raises Errno::EROFS" do
before { allow(File).to receive(:open).and_raise(Errno::EROFS) }
- it "raises a friendly error" do
- expect { Bundler::ProcessLock.lock(default_bundle_path) }.to raise_error(Bundler::GenericSystemCallError)
+ it "skips creating the lockfile and yields" do
+ processed = false
+ Bundler::ProcessLock.lock(default_bundle_path) { processed = true }
+
+ expect(processed).to eq true
end
end
+
+ it "refreshes gem specification cache after waiting for lock" do
+ build_repo2 do
+ build_gem "myrack", "1.0.0"
+ end
+
+ gemfile <<-G
+ source "https://gem.repo2"
+ gem "myrack"
+ G
+
+ # First, install the gem so it's available
+ bundle "install"
+ expect(out).to include("Installing myrack")
+
+ # Queue for thread-safe communication
+ lock_acquired = Queue.new
+ can_release_lock = Queue.new
+ install_output = Queue.new
+
+ # Thread holds lock (simulating another bundle process that just finished installing)
+ thread = Thread.new do
+ Bundler::ProcessLock.lock(default_bundle_path) do
+ # Signal that we have the lock
+ lock_acquired << true
+ # Wait until main thread signals we can release
+ can_release_lock.pop
+ end
+ end
+
+ # Wait for thread to acquire lock
+ lock_acquired.pop
+
+ # Start another install in a thread - it will wait for the lock
+ install_thread = Thread.new do
+ bundle "install", verbose: true
+ install_output << out
+ end
+
+ # Give subprocess time to start and begin waiting for lock
+ sleep 0.5
+
+ # Signal thread to release the lock
+ can_release_lock << true
+
+ # Wait for both threads to complete
+ thread.join
+ install_thread.join
+
+ second_install_out = install_output.pop
+
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ # The second install should have refreshed its cache after acquiring
+ # the lock and seen that myrack was already installed
+ expect(second_install_out).to include("Using myrack")
+ end
end
end
diff --git a/spec/bundler/install/yanked_spec.rb b/spec/bundler/install/yanked_spec.rb
index 6919662671..c92af7bfb0 100644
--- a/spec/bundler/install/yanked_spec.rb
+++ b/spec/bundler/install/yanked_spec.rb
@@ -1,13 +1,11 @@
# frozen_string_literal: true
RSpec.context "when installing a bundle that includes yanked gems" do
- before(:each) do
+ it "throws an error when the original gem version is yanked" do
build_repo4 do
build_gem "foo", "9.0.0"
end
- end
- it "throws an error when the original gem version is yanked" do
lockfile <<-L
GEM
remote: https://gem.repo4
@@ -49,7 +47,7 @@ RSpec.context "when installing a bundle that includes yanked gems" do
foo (= 1.0.0)
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -116,7 +114,11 @@ RSpec.context "when installing a bundle that includes yanked gems" do
end
it "throws the original error when only the Gemfile specifies a gem version that doesn't exist" do
- bundle "config set force_ruby_platform true"
+ build_repo4 do
+ build_gem "foo", "9.0.0"
+ end
+
+ bundle_config "force_ruby_platform true"
install_gemfile <<-G, raise_on_error: false
source "https://gem.repo4"
@@ -152,7 +154,7 @@ RSpec.context "when resolving a bundle that includes yanked gems, but unlocking
bar
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
gemfile <<-G
@@ -180,7 +182,7 @@ RSpec.context "when resolving a bundle that includes yanked gems, but unlocking
foo
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
diff --git a/spec/bundler/lock/git_spec.rb b/spec/bundler/lock/git_spec.rb
index 0e08b7ee30..c9f76115dc 100644
--- a/spec/bundler/lock/git_spec.rb
+++ b/spec/bundler/lock/git_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe "bundle lock with git gems" do
it "doesn't print errors even if running lock after removing the cache" do
install_gemfile_with_foo_as_a_git_dependency
- FileUtils.rm_rf(Dir[default_cache_path("git/foo-1.0-*")].first)
+ FileUtils.rm_r(Dir[default_cache_path("git/foo-1.0-*")].first)
bundle "lock --verbose"
@@ -60,7 +60,7 @@ RSpec.describe "bundle lock with git gems" do
foo!
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "install", raise_on_error: false
@@ -123,7 +123,7 @@ RSpec.describe "bundle lock with git gems" do
foo!
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "install"
@@ -161,7 +161,7 @@ RSpec.describe "bundle lock with git gems" do
foo!
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "install"
@@ -206,7 +206,7 @@ RSpec.describe "bundle lock with git gems" do
activesupport
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
gemfile <<~G
@@ -220,4 +220,39 @@ RSpec.describe "bundle lock with git gems" do
expect(lockfile).to include("securerandom (0.3.2)")
end
+
+ it "does not lock versions that don't exist in the repository when changing a GIT direct dep to a GEM direct dep" do
+ build_repo4 do
+ build_gem "ruby-lsp", "0.16.1"
+ end
+
+ path = lib_path("ruby-lsp")
+ revision = build_git("ruby-lsp", "0.16.2", path: path).ref_for("HEAD")
+
+ lockfile <<~L
+ GIT
+ remote: #{path}
+ revision: #{revision}
+ specs:
+ ruby-lsp (0.16.2)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ ruby-lsp!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ gemfile <<~G
+ source "https://gem.repo4"
+ gem "ruby-lsp"
+ G
+
+ bundle "lock"
+
+ expect(lockfile).to include("ruby-lsp (0.16.1)")
+ end
end
diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb
index 2fa9425914..654ac02aa7 100644
--- a/spec/bundler/lock/lockfile_spec.rb
+++ b/spec/bundler/lock/lockfile_spec.rb
@@ -29,7 +29,7 @@ RSpec.describe "the lockfile format" do
myrack
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -80,7 +80,7 @@ RSpec.describe "the lockfile format" do
myrack
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -109,7 +109,7 @@ RSpec.describe "the lockfile format" do
#{version}
L
- install_gemfile <<-G, verbose: true, preserve_ruby_flags: true
+ install_gemfile <<-G, verbose: true
source "https://gem.repo4"
gem "myrack"
@@ -168,7 +168,7 @@ RSpec.describe "the lockfile format" do
myrack (> 0)
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -215,7 +215,7 @@ RSpec.describe "the lockfile format" do
myrack
BUNDLED WITH
- #{current_version}
+ #{current_version}
G
end
@@ -246,7 +246,7 @@ RSpec.describe "the lockfile format" do
myrack-obama
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -277,7 +277,7 @@ RSpec.describe "the lockfile format" do
myrack-obama (>= 1.0)
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -324,7 +324,7 @@ RSpec.describe "the lockfile format" do
myrack-obama (>= 1.0)!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -371,7 +371,7 @@ RSpec.describe "the lockfile format" do
myrack-obama (>= 1.0)!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
lockfile lockfile_without_credentials
@@ -432,7 +432,7 @@ RSpec.describe "the lockfile format" do
myrack-obama (>= 1.0)!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
lockfile lockfile_with_credentials
@@ -468,7 +468,7 @@ RSpec.describe "the lockfile format" do
net-sftp
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
expect(the_bundle).to include_gems "net-sftp 1.1.1", "net-ssh 1.0.0"
@@ -504,7 +504,7 @@ RSpec.describe "the lockfile format" do
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -540,7 +540,7 @@ RSpec.describe "the lockfile format" do
myrack
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "install"
@@ -579,7 +579,7 @@ RSpec.describe "the lockfile format" do
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -615,7 +615,7 @@ RSpec.describe "the lockfile format" do
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -651,7 +651,7 @@ RSpec.describe "the lockfile format" do
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -710,7 +710,7 @@ RSpec.describe "the lockfile format" do
ckeditor!
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "lock"
@@ -737,7 +737,7 @@ RSpec.describe "the lockfile format" do
ckeditor!
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -770,7 +770,7 @@ RSpec.describe "the lockfile format" do
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -786,7 +786,6 @@ RSpec.describe "the lockfile format" do
c.no_checksum "foo", "1.0"
end
- bundle "config set cache_all true"
bundle :cache
bundle :install, local: true
@@ -807,7 +806,7 @@ RSpec.describe "the lockfile format" do
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -855,7 +854,7 @@ RSpec.describe "the lockfile format" do
myrack
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -883,7 +882,7 @@ RSpec.describe "the lockfile format" do
myrack!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -926,7 +925,7 @@ RSpec.describe "the lockfile format" do
thin
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -975,7 +974,7 @@ RSpec.describe "the lockfile format" do
rails
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -983,7 +982,7 @@ RSpec.describe "the lockfile format" do
update_repo2 do
# Capistrano did this (at least until version 2.5.10)
# RubyGems 2.2 doesn't allow the specifying of a dependency twice
- # See https://github.com/rubygems/rubygems/commit/03dbac93a3396a80db258d9bc63500333c25bd2f
+ # See https://github.com/ruby/rubygems/commit/03dbac93a3396a80db258d9bc63500333c25bd2f
build_gem "double_deps", "1.0", skip_validation: true do |s|
s.add_dependency "net-ssh", ">= 1.0.0"
s.add_dependency "net-ssh"
@@ -1016,7 +1015,7 @@ RSpec.describe "the lockfile format" do
double_deps
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -1047,7 +1046,7 @@ RSpec.describe "the lockfile format" do
myrack-obama (>= 1.0)
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -1078,7 +1077,7 @@ RSpec.describe "the lockfile format" do
myrack-obama (>= 1.0)
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -1113,7 +1112,7 @@ RSpec.describe "the lockfile format" do
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -1148,7 +1147,7 @@ RSpec.describe "the lockfile format" do
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -1183,7 +1182,7 @@ RSpec.describe "the lockfile format" do
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -1216,7 +1215,7 @@ RSpec.describe "the lockfile format" do
foo!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -1238,7 +1237,7 @@ RSpec.describe "the lockfile format" do
myrack
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
install_gemfile <<-G
@@ -1262,7 +1261,7 @@ RSpec.describe "the lockfile format" do
myrack
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -1306,7 +1305,7 @@ RSpec.describe "the lockfile format" do
google-protobuf
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -1341,7 +1340,7 @@ RSpec.describe "the lockfile format" do
platform_specific
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
end
@@ -1378,7 +1377,7 @@ RSpec.describe "the lockfile format" do
myrack
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -1406,7 +1405,7 @@ RSpec.describe "the lockfile format" do
myrack
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -1434,7 +1433,7 @@ RSpec.describe "the lockfile format" do
myrack (= 1.0)
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -1462,7 +1461,7 @@ RSpec.describe "the lockfile format" do
myrack (= 1.0)
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -1511,7 +1510,7 @@ RSpec.describe "the lockfile format" do
myrack (> 0.9, < 1.0)
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -1539,10 +1538,10 @@ RSpec.describe "the lockfile format" do
myrack (> 0.9, < 1.0)
#{checksums}
RUBY VERSION
- #{Bundler::RubyVersion.system}
+ #{Bundler::RubyVersion.system}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -1560,7 +1559,7 @@ RSpec.describe "the lockfile format" do
myrack_middleware
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
install_gemfile <<-G
@@ -1583,10 +1582,104 @@ RSpec.describe "the lockfile format" do
myrack_middleware
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
+ it "raises a clear error when frozen mode is set and lockfile is missing deps, and does not install any gems" do
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ myrack_middleware (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack_middleware
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ install_gemfile <<-G, env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false
+ source "https://gem.repo2"
+ gem "myrack_middleware"
+ G
+
+ expect(err).to include("Bundler found incorrect dependencies in the lockfile for myrack_middleware-1.0")
+ expect(err).to include("myrack: gemspec specifies = 0.9.1, not in lockfile")
+ expect(the_bundle).not_to include_gems "myrack_middleware 1.0"
+ end
+
+ it "raises a clear error when frozen mode is set and lockfile is missing entries in CHECKSUMS section, and does not install any gems" do
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ myrack_middleware (1.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack_middleware
+
+ CHECKSUMS
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ install_gemfile <<-G, env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false
+ source "https://gem.repo2"
+ gem "myrack_middleware"
+ G
+
+ expect(err).to eq <<~L.strip
+ Your lockfile is missing a CHECKSUMS entry for \"myrack_middleware\", but can't be updated because frozen mode is set
+
+ Run `bundle install` elsewhere and add the updated Gemfile.lock to version control.
+ L
+
+ expect(the_bundle).not_to include_gems "myrack_middleware 1.0"
+ end
+
+ it "raises a clear error when frozen mode is set and lockfile has empty checksums in CHECKSUMS section, and does not install any gems" do
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo2/
+ specs:
+ myrack (0.9.1)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ myrack
+
+ CHECKSUMS
+ myrack (0.9.1)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ install_gemfile <<-G, env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false
+ source "https://gem.repo2"
+ gem "myrack"
+ G
+
+ expect(err).to eq <<~L.strip
+ Your lockfile has an empty CHECKSUMS entry for \"myrack\", but can't be updated because frozen mode is set
+
+ Run `bundle install` elsewhere and add the updated Gemfile.lock to version control.
+ L
+
+ expect(the_bundle).not_to include_gems "myrack 0.9.1"
+ end
+
it "automatically fixes the lockfile when it's missing deps, they conflict with other locked deps, but conflicts are fixable" do
build_repo4 do
build_gem "other_dep", "0.9"
@@ -1615,7 +1708,7 @@ RSpec.describe "the lockfile format" do
other_dep
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
install_gemfile <<-G
@@ -1642,7 +1735,7 @@ RSpec.describe "the lockfile format" do
other_dep
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -1677,7 +1770,7 @@ RSpec.describe "the lockfile format" do
another_dep_middleware
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
install_gemfile <<-G
@@ -1705,7 +1798,7 @@ RSpec.describe "the lockfile format" do
myrack_middleware
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -1737,7 +1830,7 @@ RSpec.describe "the lockfile format" do
other_dep
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
install_gemfile <<-G, raise_on_error: false
@@ -1780,7 +1873,7 @@ RSpec.describe "the lockfile format" do
direct_dependency
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
install_gemfile <<-G
@@ -1798,13 +1891,13 @@ RSpec.describe "the lockfile format" do
indirect_dependency (1.2.3)
PLATFORMS
- #{lockfile_platforms("ruby", generic_local_platform, defaults: [])}
+ #{lockfile_platforms(generic_default_locked_platform || local_platform, defaults: ["ruby"])}
DEPENDENCIES
direct_dependency
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -1822,7 +1915,7 @@ RSpec.describe "the lockfile format" do
myrack_middleware
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
install_gemfile <<-G
@@ -1845,7 +1938,245 @@ RSpec.describe "the lockfile format" do
myrack_middleware
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "automatically fixes the lockfile when it includes a gem under the correct GIT section, but also under an incorrect GEM section, with a higher version, and with no explicit Gemfile requirement" do
+ git = build_git "foo"
+
+ gemfile <<~G
+ source "https://gem.repo1/"
+ gem "foo", git: "#{lib_path("foo-1.0")}"
+ G
+
+ # If the lockfile erroneously lists platform versions of the gem
+ # that don't match the locked version of the git repo we should remove them.
+
+ lockfile <<~L
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("main")}
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: https://gem.repo1/
+ specs:
+ foo (1.1-x86_64-linux-gnu)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
+
+ expect(lockfile).to eq <<~L
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("main")}
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: https://gem.repo1/
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "automatically fixes the lockfile when it includes a gem under the correct GIT section, but also under an incorrect GEM section, with a higher version" do
+ git = build_git "foo"
+
+ gemfile <<~G
+ source "https://gem.repo1/"
+ gem "foo", "= 1.0", git: "#{lib_path("foo-1.0")}"
+ G
+
+ # If the lockfile erroneously lists platform versions of the gem
+ # that don't match the locked version of the git repo we should remove them.
+
+ lockfile <<~L
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("main")}
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: https://gem.repo1/
+ specs:
+ foo (1.1-x86_64-linux-gnu)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo (= 1.0)!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
+
+ expect(lockfile).to eq <<~L
+ GIT
+ remote: #{lib_path("foo-1.0")}
+ revision: #{git.ref_for("main")}
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: https://gem.repo1/
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ foo (= 1.0)!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "automatically fixes the lockfile when it has incorrect deps, keeping the locked version" do
+ build_repo4 do
+ build_gem "net-smtp", "0.5.0" do |s|
+ s.add_dependency "net-protocol"
+ end
+
+ build_gem "net-smtp", "0.5.1" do |s|
+ s.add_dependency "net-protocol"
+ end
+
+ build_gem "net-protocol", "0.2.2"
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "net-smtp"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ net-protocol (0.2.2)
+ net-smtp (0.5.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ net-smtp
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ net-protocol (0.2.2)
+ net-smtp (0.5.0)
+ net-protocol
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ net-smtp
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "successfully updates the lockfile when a new gem is added in the Gemfile includes a gem that shouldn't be included" do
+ build_repo4 do
+ build_gem "logger", "1.7.0"
+ build_gem "rack", "3.2.0"
+ build_gem "net-smtp", "0.5.0"
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem "logger"
+ gem "net-smtp"
+
+ install_if -> { false } do
+ gem 'rack', github: 'rack/rack'
+ end
+ G
+
+ lockfile <<~L
+ GIT
+ remote: https://github.com/rack/rack.git
+ revision: 2fface9ac09fc582a81386becd939c987ad33f99
+ specs:
+ rack (3.2.0)
+
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ logger (1.7.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ logger
+ rack!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ bundle "install"
+
+ expect(lockfile).to eq <<~L
+ GIT
+ remote: https://github.com/rack/rack.git
+ revision: 2fface9ac09fc582a81386becd939c987ad33f99
+ specs:
+ rack (3.2.0)
+
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ logger (1.7.0)
+ net-smtp (0.5.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ logger
+ net-smtp
+ rack!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
L
end
@@ -1878,7 +2209,7 @@ RSpec.describe "the lockfile format" do
minitest-bisect
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
cache_gems "minitest-bisect-1.6.0", "path_expander-1.1.1", gem_repo: gem_repo4
@@ -1899,7 +2230,7 @@ RSpec.describe "the lockfile format" do
minitest-bisect
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
end
@@ -1944,11 +2275,11 @@ RSpec.describe "the lockfile format" do
minitest-bisect
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "install --verbose"
- expect(out).to include("re-resolving dependencies because your lock file includes \"minitest-bisect\" but not some of its dependencies")
+ expect(out).to include("re-resolving dependencies because your lockfile includes \"minitest-bisect\" but not some of its dependencies")
expect(lockfile).to eq <<~L
GEM
@@ -1965,7 +2296,7 @@ RSpec.describe "the lockfile format" do
minitest-bisect
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
end
@@ -2065,7 +2396,7 @@ RSpec.describe "the lockfile format" do
myrack
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
install_gemfile <<-G, raise_on_error: false
@@ -2079,19 +2410,7 @@ RSpec.describe "the lockfile format" do
private
- def prerelease?(version)
- Gem::Version.new(version).prerelease?
- end
-
def previous_major(version)
version.split(".").map.with_index {|v, i| i == 0 ? v.to_i - 1 : v }.join(".")
end
-
- def bump_minor(version)
- bump(version, 1)
- end
-
- def bump(version, segment)
- version.split(".").map.with_index {|v, i| i == segment ? v.to_i + 1 : v }.join(".")
- end
end
diff --git a/spec/bundler/other/cli_dispatch_spec.rb b/spec/bundler/other/cli_dispatch_spec.rb
index 48b285045a..a2c745b070 100644
--- a/spec/bundler/other/cli_dispatch_spec.rb
+++ b/spec/bundler/other/cli_dispatch_spec.rb
@@ -4,17 +4,17 @@ RSpec.describe "bundle command names" do
it "work when given fully" do
bundle "install", raise_on_error: false
expect(err).to eq("Could not locate Gemfile")
- expect(last_command.stdboth).not_to include("Ambiguous command")
+ expect(stdboth).not_to include("Ambiguous command")
end
it "work when not ambiguous" do
bundle "ins", raise_on_error: false
expect(err).to eq("Could not locate Gemfile")
- expect(last_command.stdboth).not_to include("Ambiguous command")
+ expect(stdboth).not_to include("Ambiguous command")
end
it "print a friendly error when ambiguous" do
bundle "in", raise_on_error: false
- expect(err).to eq("Ambiguous command in matches [info, init, inject, install]")
+ expect(err).to eq("Ambiguous command in matches [info, init, install]")
end
end
diff --git a/spec/bundler/other/cli_man_pages_spec.rb b/spec/bundler/other/cli_man_pages_spec.rb
index 84ffca14e6..4e8f155309 100644
--- a/spec/bundler/other/cli_man_pages_spec.rb
+++ b/spec/bundler/other/cli_man_pages_spec.rb
@@ -1,49 +1,78 @@
# frozen_string_literal: true
RSpec.describe "bundle commands" do
- it "expects all commands to have a man page" do
- Bundler::CLI.all_commands.each_key do |command_name|
- next if command_name == "cli_help"
+ it "expects all commands to have all options and subcommands documented" do
+ check_commands!(Bundler::CLI)
- expect(man_page(command_name)).to exist
+ Bundler::CLI.subcommand_classes.each_value do |klass|
+ check_commands!(klass)
end
end
- it "expects all commands to have all options documented" do
- Bundler::CLI.all_commands.each do |command_name, command|
- next if command_name == "cli_help"
+ private
- man_page_content = man_page(command_name).read
+ def check_commands!(command_class)
+ command_class.commands.each do |command_name, command|
+ if command.is_a?(Bundler::Thor::HiddenCommand)
+ man_page = man_page(command_name)
+ expect(man_page).not_to exist
+ expect(main_man_page.read).not_to include("bundle #{command_name}")
+ elsif command_class == Bundler::CLI
+ man_page = man_page(command_name)
+ expect(man_page).to exist
- command.options.each do |_, option|
- aliases = option.aliases
- formatted_aliases = aliases.sort.map {|name| "`#{name}`" }.join(", ") if aliases
+ check_options!(command, man_page)
+ else
+ man_page = man_page(command.ancestor_name)
+ expect(man_page).to exist
- help = if option.type == :boolean
- "* #{append_aliases("`#{option.switch_name}`", formatted_aliases)}:"
- elsif option.enum
- formatted_aliases = "`#{option.switch_name}`" if aliases.empty? && option.lazy_default
- "* #{prepend_aliases(option.enum.sort.map {|enum| "`#{option.switch_name}=#{enum}`" }.join(", "), formatted_aliases)}:"
- else
- names = [option.switch_name, *aliases]
- value =
- case option.type
- when :array then "<list>"
- when :numeric then "<number>"
- else option.name.upcase
- end
+ check_options!(command, man_page)
+ check_subcommand!(command_name, man_page)
+ end
+ end
+ end
- value = option.type != :numeric && option.lazy_default ? "[=#{value}]" : "=#{value}"
+ def check_options!(command, man_page)
+ command.options.each do |_, option|
+ check_option!(option, man_page)
+ end
+ end
+
+ def check_option!(option, man_page)
+ man_page_content = man_page.read
- "* #{names.map {|name| "`#{name}#{value}`" }.join(", ")}:"
+ aliases = option.aliases
+ formatted_aliases = aliases.sort.map {|name| "`#{name}`" }.join(", ") if aliases
+
+ help = if option.type == :boolean
+ "* #{append_aliases("`#{option.switch_name}`", formatted_aliases)}:"
+ elsif option.enum
+ formatted_aliases = "`#{option.switch_name}`" if aliases.empty? && option.lazy_default
+ "* #{prepend_aliases(option.enum.sort.map {|enum| "`#{option.switch_name}=#{enum}`" }.join(", "), formatted_aliases)}:"
+ else
+ names = [option.switch_name, *aliases]
+ value =
+ case option.type
+ when :array then "<list>"
+ when :numeric then "<number>"
+ else option.name.upcase
end
- expect(man_page_content).to include(help)
- end
+ value = option.type != :numeric && option.lazy_default ? "[=#{value}]" : "=#{value}"
+
+ "* #{names.map {|name| "`#{name}#{value}`" }.join(", ")}:"
+ end
+
+ if option.banner.include?("(removed)")
+ expect(man_page_content).not_to include(help)
+ else
+ expect(man_page_content).to include(help)
end
end
- private
+ def check_subcommand!(name, man_page)
+ expect(man_page.read).to match(name)
+ end
def append_aliases(text, aliases)
return text if aliases.empty?
@@ -57,7 +86,15 @@ RSpec.describe "bundle commands" do
"#{aliases}, #{text}"
end
+ def man_page_content(command_name)
+ man_page(command_name).read
+ end
+
def man_page(command_name)
source_root.join("lib/bundler/man/bundle-#{command_name}.1.ronn")
end
+
+ def main_man_page
+ source_root.join("lib/bundler/man/bundle.1.ronn")
+ end
end
diff --git a/spec/bundler/other/ext_spec.rb b/spec/bundler/other/ext_spec.rb
index 9fc0414b4d..a883eefe06 100644
--- a/spec/bundler/other/ext_spec.rb
+++ b/spec/bundler/other/ext_spec.rb
@@ -1,55 +1,20 @@
# frozen_string_literal: true
-RSpec.describe "Gem::Specification#match_platform" do
+RSpec.describe "Gem::Specification#installable_on_platform?" do
it "does not match platforms other than the gem platform" do
darwin = gem "lol", "1.0", "platform_specific-1.0-x86-darwin-10"
- expect(darwin.match_platform(pl("java"))).to eq(false)
+ expect(darwin.installable_on_platform?(pl("java"))).to eq(false)
end
context "when platform is a string" do
it "matches when platform is a string" do
lazy_spec = Bundler::LazySpecification.new("lol", "1.0", "universal-mingw32")
- expect(lazy_spec.match_platform(pl("x86-mingw32"))).to eq(true)
- expect(lazy_spec.match_platform(pl("x64-mingw32"))).to eq(true)
+ expect(lazy_spec.installable_on_platform?(pl("x86-mingw32"))).to eq(true)
+ expect(lazy_spec.installable_on_platform?(pl("x64-mingw32"))).to eq(true)
end
end
end
-RSpec.describe "Bundler::GemHelpers#generic" do
- include Bundler::GemHelpers
-
- it "converts non-windows platforms into ruby" do
- expect(generic(pl("x86-darwin-10"))).to eq(pl("ruby"))
- expect(generic(pl("ruby"))).to eq(pl("ruby"))
- end
-
- it "converts java platform variants into java" do
- expect(generic(pl("universal-java-17"))).to eq(pl("java"))
- expect(generic(pl("java"))).to eq(pl("java"))
- end
-
- it "converts mswin platform variants into x86-mswin32" do
- expect(generic(pl("mswin32"))).to eq(pl("x86-mswin32"))
- expect(generic(pl("i386-mswin32"))).to eq(pl("x86-mswin32"))
- expect(generic(pl("x86-mswin32"))).to eq(pl("x86-mswin32"))
- end
-
- it "converts 32-bit mingw platform variants into x86-mingw32" do
- expect(generic(pl("mingw32"))).to eq(pl("x86-mingw32"))
- expect(generic(pl("i386-mingw32"))).to eq(pl("x86-mingw32"))
- expect(generic(pl("x86-mingw32"))).to eq(pl("x86-mingw32"))
- end
-
- it "converts 64-bit mingw platform variants into x64-mingw32" do
- expect(generic(pl("x64-mingw32"))).to eq(pl("x64-mingw32"))
- expect(generic(pl("x86_64-mingw32"))).to eq(pl("x64-mingw32"))
- end
-
- it "converts 64-bit mingw UCRT platform variants into x64-mingw-ucrt" do
- expect(generic(pl("x64-mingw-ucrt"))).to eq(pl("x64-mingw-ucrt"))
- end
-end
-
RSpec.describe "Gem::SourceIndex#refresh!" do
before do
install_gemfile <<-G
diff --git a/spec/bundler/other/major_deprecation_spec.rb b/spec/bundler/other/major_deprecation_spec.rb
index 036c855c4e..ab7589d698 100644
--- a/spec/bundler/other/major_deprecation_spec.rb
+++ b/spec/bundler/other/major_deprecation_spec.rb
@@ -14,81 +14,67 @@ RSpec.describe "major deprecations" do
describe ".clean_env" do
before do
source = "Bundler.clean_env"
- bundle "exec ruby -e #{source.dump}"
+ bundle "exec ruby -e #{source.dump}", raise_on_error: false
end
- it "is deprecated in favor of .unbundled_env", bundler: "< 3" do
- expect(deprecations).to include \
- "`Bundler.clean_env` has been deprecated in favor of `Bundler.unbundled_env`. " \
- "If you instead want the environment before bundler was originally loaded, use `Bundler.original_env` " \
- "(called at -e:1)"
+ it "is removed in favor of .unbundled_env and shows a helpful error message about it" do
+ expect(err).to include \
+ "`Bundler.clean_env` has been removed in favor of `Bundler.unbundled_env`. " \
+ "If you instead want the environment before bundler was originally loaded, use `Bundler.original_env`" \
end
-
- pending "is removed and shows a helpful error message about it", bundler: "3"
end
describe ".with_clean_env" do
before do
source = "Bundler.with_clean_env {}"
- bundle "exec ruby -e #{source.dump}"
+ bundle "exec ruby -e #{source.dump}", raise_on_error: false
end
- it "is deprecated in favor of .unbundled_env", bundler: "< 3" do
- expect(deprecations).to include(
- "`Bundler.with_clean_env` has been deprecated in favor of `Bundler.with_unbundled_env`. " \
- "If you instead want the environment before bundler was originally loaded, use `Bundler.with_original_env` " \
- "(called at -e:1)"
+ it "is removed in favor of .unbundled_env and shows a helpful error message about it" do
+ expect(err).to include(
+ "`Bundler.with_clean_env` has been removed in favor of `Bundler.with_unbundled_env`. " \
+ "If you instead want the environment before bundler was originally loaded, use `Bundler.with_original_env`"
)
end
-
- pending "is removed and shows a helpful error message about it", bundler: "3"
end
describe ".clean_system" do
before do
source = "Bundler.clean_system('ls')"
- bundle "exec ruby -e #{source.dump}"
+ bundle "exec ruby -e #{source.dump}", raise_on_error: false
end
- it "is deprecated in favor of .unbundled_system", bundler: "< 3" do
- expect(deprecations).to include(
- "`Bundler.clean_system` has been deprecated in favor of `Bundler.unbundled_system`. " \
- "If you instead want to run the command in the environment before bundler was originally loaded, use `Bundler.original_system` " \
- "(called at -e:1)"
+ it "is removed in favor of .unbundled_system and shows a helpful error message about it" do
+ expect(err).to include(
+ "`Bundler.clean_system` has been removed in favor of `Bundler.unbundled_system`. " \
+ "If you instead want to run the command in the environment before bundler was originally loaded, use `Bundler.original_system`" \
)
end
-
- pending "is removed and shows a helpful error message about it", bundler: "3"
end
describe ".clean_exec" do
before do
source = "Bundler.clean_exec('ls')"
- bundle "exec ruby -e #{source.dump}"
+ bundle "exec ruby -e #{source.dump}", raise_on_error: false
end
- it "is deprecated in favor of .unbundled_exec", bundler: "< 3" do
- expect(deprecations).to include(
- "`Bundler.clean_exec` has been deprecated in favor of `Bundler.unbundled_exec`. " \
- "If you instead want to exec to a command in the environment before bundler was originally loaded, use `Bundler.original_exec` " \
- "(called at -e:1)"
+ it "is removed in favor of .unbundled_exec and shows a helpful error message about it" do
+ expect(err).to include(
+ "`Bundler.clean_exec` has been removed in favor of `Bundler.unbundled_exec`. " \
+ "If you instead want to exec to a command in the environment before bundler was originally loaded, use `Bundler.original_exec`" \
)
end
-
- pending "is removed and shows a helpful error message about it", bundler: "3"
end
describe ".environment" do
before do
source = "Bundler.environment"
- bundle "exec ruby -e #{source.dump}"
+ bundle "exec ruby -e #{source.dump}", raise_on_error: false
end
- it "is deprecated in favor of .load", bundler: "< 3" do
- expect(deprecations).to include "Bundler.environment has been removed in favor of Bundler.load (called at -e:1)"
+ it "is removed in favor of .load and shows a helpful error message about it" do
+ expect(err).to include "Bundler.environment has been removed in favor of Bundler.load"
end
-
- pending "is removed and shows a helpful error message about it", bundler: "3"
end
end
@@ -97,11 +83,9 @@ RSpec.describe "major deprecations" do
bundle "exec --no-keep-file-descriptors -e 1", raise_on_error: false
end
- it "is deprecated", bundler: "< 3" do
- expect(deprecations).to include "The `--no-keep-file-descriptors` has been deprecated. `bundle exec` no longer mess with your file descriptors. Close them in the exec'd script if you need to"
+ it "is removed and shows a helpful error message about it" do
+ expect(err).to include "The `--no-keep-file-descriptors` has been removed. `bundle exec` no longer mess with your file descriptors. Close them in the exec'd script if you need to"
end
-
- pending "is removed and shows a helpful error message about it", bundler: "3"
end
describe "bundle update --quiet" do
@@ -121,16 +105,14 @@ RSpec.describe "major deprecations" do
bundle "check --path vendor/bundle", raise_on_error: false
end
- it "should print a deprecation warning", bundler: "< 3" do
- expect(deprecations).to include(
- "The `--path` flag is deprecated because it relies on being " \
- "remembered across bundler invocations, which bundler will no " \
- "longer do in future versions. Instead please use `bundle config set " \
- "path 'vendor/bundle'`, and stop using this flag"
+ it "fails with a helpful error" do
+ expect(err).to include(
+ "The `--path` flag has been removed because it relied on being " \
+ "remembered across bundler invocations, which bundler no longer " \
+ "does. Instead please use `bundle config set path 'vendor/bundle'`, " \
+ "and stop using this flag"
)
end
-
- pending "fails with a helpful error", bundler: "3"
end
context "bundle check --path=" do
@@ -143,16 +125,34 @@ RSpec.describe "major deprecations" do
bundle "check --path=vendor/bundle", raise_on_error: false
end
- it "should print a deprecation warning", bundler: "< 3" do
- expect(deprecations).to include(
- "The `--path` flag is deprecated because it relies on being " \
- "remembered across bundler invocations, which bundler will no " \
- "longer do in future versions. Instead please use `bundle config set " \
- "path 'vendor/bundle'`, and stop using this flag"
+ it "fails with a helpful error" do
+ expect(err).to include(
+ "The `--path` flag has been removed because it relied on being " \
+ "remembered across bundler invocations, which bundler no longer " \
+ "does. Instead please use `bundle config set path 'vendor/bundle'`, " \
+ "and stop using this flag"
)
end
+ end
- pending "fails with a helpful error", bundler: "3"
+ context "bundle binstubs --path=" do
+ before do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ bundle "binstubs myrack --path=binpath", raise_on_error: false
+ end
+
+ it "fails with a helpful error" do
+ expect(err).to include(
+ "The `--path` flag has been removed because it relied on being " \
+ "remembered across bundler invocations, which bundler no longer " \
+ "does. Instead please use `bundle config set bin 'binpath'`, " \
+ "and stop using this flag"
+ )
+ end
end
context "bundle cache --all" do
@@ -162,19 +162,37 @@ RSpec.describe "major deprecations" do
gem "myrack"
G
- bundle "cache --all", raise_on_error: false
+ bundle "cache --all --verbose", raise_on_error: false
end
- it "should print a deprecation warning", bundler: "< 3" do
- expect(deprecations).to include(
- "The `--all` flag is deprecated because it relies on being " \
- "remembered across bundler invocations, which bundler will no " \
- "longer do in future versions. Instead please use `bundle config set " \
- "cache_all true`, and stop using this flag"
+ it "fails with a helpful error" do
+ expect(err).to include(
+ "The `--all` flag has been removed because it relied on being " \
+ "remembered across bundler invocations, which bundler no longer " \
+ "does. Instead please use `bundle config set cache_all true`, " \
+ "and stop using this flag"
)
end
+ end
+
+ context "bundle cache --no-all" do
+ before do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
- pending "fails with a helpful error", bundler: "3"
+ bundle "cache --no-all", raise_on_error: false
+ end
+
+ it "fails with a helpful error" do
+ expect(err).to include(
+ "The `--no-all` flag has been removed because it relied on being " \
+ "remembered across bundler invocations, which bundler no longer " \
+ "does. Instead please use `bundle config set cache_all false`, " \
+ "and stop using this flag"
+ )
+ end
end
context "bundle cache --path" do
@@ -187,16 +205,74 @@ RSpec.describe "major deprecations" do
bundle "cache --path foo", raise_on_error: false
end
- it "should print a deprecation warning", bundler: "< 3" do
- expect(deprecations).to include(
- "The `--path` flag is deprecated because its semantics are unclear. " \
+ it "should print a removal error" do
+ expect(err).to include(
+ "The `--path` flag has been removed because its semantics were unclear. " \
+ "Use `bundle config cache_path` to configure the path of your cache of gems, " \
+ "and `bundle config path` to configure the path where your gems are installed, " \
+ "and stop using this flag"
+ )
+ end
+ end
+
+ context "bundle cache --path=" do
+ before do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ bundle "cache --path=foo", raise_on_error: false
+ end
+
+ it "should print a deprecation warning" do
+ expect(err).to include(
+ "The `--path` flag has been removed because its semantics were unclear. " \
"Use `bundle config cache_path` to configure the path of your cache of gems, " \
"and `bundle config path` to configure the path where your gems are installed, " \
"and stop using this flag"
)
end
+ end
+
+ context "bundle cache --frozen" do
+ before do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ bundle "cache --frozen", raise_on_error: false
+ end
+
+ it "fails with a helpful error" do
+ expect(err).to include(
+ "The `--frozen` flag has been removed because it relied on being " \
+ "remembered across bundler invocations, which bundler no longer " \
+ "does. Instead please use `bundle config set frozen true`, " \
+ "and stop using this flag"
+ )
+ end
+ end
- pending "fails with a helpful error", bundler: "3"
+ context "bundle cache --no-prune" do
+ before do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+
+ bundle "cache --no-prune", raise_on_error: false
+ end
+
+ it "fails with a helpful error" do
+ expect(err).to include(
+ "The `--no-prune` flag has been removed because it relied on being " \
+ "remembered across bundler invocations, which bundler no longer " \
+ "does. Instead please use `bundle config set no_prune true`, " \
+ "and stop using this flag"
+ )
+ end
end
describe "bundle config" do
@@ -205,23 +281,23 @@ RSpec.describe "major deprecations" do
bundle "config"
end
- it "warns", bundler: "3" do
+ it "warns", bundler: "4" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config list` instead.")
end
- pending "fails with a helpful error", bundler: "3"
+ pending "fails with a helpful error", bundler: "5"
end
describe "old get interface" do
before do
- bundle "config waka"
+ bundle "config waka", raise_on_error: false
end
- it "warns", bundler: "3" do
+ it "warns", bundler: "4" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config get waka` instead.")
end
- pending "fails with a helpful error", bundler: "3"
+ pending "fails with a helpful error", bundler: "5"
end
describe "old set interface" do
@@ -229,11 +305,11 @@ RSpec.describe "major deprecations" do
bundle "config waka wakapun"
end
- it "warns", bundler: "3" do
+ it "warns", bundler: "4" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config set waka wakapun` instead.")
end
- pending "fails with a helpful error", bundler: "3"
+ pending "fails with a helpful error", bundler: "5"
end
describe "old set interface with --local" do
@@ -241,11 +317,11 @@ RSpec.describe "major deprecations" do
bundle "config --local waka wakapun"
end
- it "warns", bundler: "3" do
+ it "warns", bundler: "4" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config set --local waka wakapun` instead.")
end
- pending "fails with a helpful error", bundler: "3"
+ pending "fails with a helpful error", bundler: "5"
end
describe "old set interface with --global" do
@@ -253,11 +329,11 @@ RSpec.describe "major deprecations" do
bundle "config --global waka wakapun"
end
- it "warns", bundler: "3" do
+ it "warns", bundler: "4" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config set --global waka wakapun` instead.")
end
- pending "fails with a helpful error", bundler: "3"
+ pending "fails with a helpful error", bundler: "5"
end
describe "old unset interface" do
@@ -265,11 +341,11 @@ RSpec.describe "major deprecations" do
bundle "config --delete waka"
end
- it "warns", bundler: "3" do
+ it "warns", bundler: "4" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config unset waka` instead.")
end
- pending "fails with a helpful error", bundler: "3"
+ pending "fails with a helpful error", bundler: "5"
end
describe "old unset interface with --local" do
@@ -277,11 +353,11 @@ RSpec.describe "major deprecations" do
bundle "config --delete --local waka"
end
- it "warns", bundler: "3" do
+ it "warns", bundler: "4" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config unset --local waka` instead.")
end
- pending "fails with a helpful error", bundler: "3"
+ pending "fails with a helpful error", bundler: "5"
end
describe "old unset interface with --global" do
@@ -289,11 +365,11 @@ RSpec.describe "major deprecations" do
bundle "config --delete --global waka"
end
- it "warns", bundler: "3" do
+ it "warns", bundler: "4" do
expect(deprecations).to include("Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle config unset --global waka` instead.")
end
- pending "fails with a helpful error", bundler: "3"
+ pending "fails with a helpful error", bundler: "5"
end
end
@@ -305,12 +381,12 @@ RSpec.describe "major deprecations" do
G
end
- it "warns when no options are given", bundler: "3" do
+ it "warns when no options are given", bundler: "4" do
bundle "update"
expect(deprecations).to include("Pass --all to `bundle update` to update everything")
end
- pending "fails with a helpful error when no options are given", bundler: "3"
+ pending "fails with a helpful error when no options are given", bundler: "5"
it "does not warn when --all is passed" do
bundle "update --all"
@@ -320,17 +396,15 @@ RSpec.describe "major deprecations" do
describe "bundle install --binstubs" do
before do
- install_gemfile <<-G, binstubs: true
+ install_gemfile <<-G, binstubs: true, raise_on_error: false
source "https://gem.repo1"
gem "myrack"
G
end
- it "should output a deprecation warning", bundler: "< 3" do
- expect(deprecations).to include("The --binstubs option will be removed in favor of `bundle binstubs --all`")
+ it "fails with a helpful error" do
+ expect(err).to include("The --binstubs option has been removed in favor of `bundle binstubs --all`")
end
-
- pending "fails with a helpful error", bundler: "3"
end
context "bundle install with both gems.rb and Gemfile present" do
@@ -361,7 +435,7 @@ RSpec.describe "major deprecations" do
context "bundle install with flags" do
before do
- bundle "config set --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
install_gemfile <<-G
source "https://gem.repo1"
@@ -383,76 +457,85 @@ RSpec.describe "major deprecations" do
}.each do |name, expectations|
option_name, value = *expectations
flag_name = "--#{name}"
+ args = %w[true false].include?(value) ? flag_name : "#{flag_name} #{value}"
context "with the #{flag_name} flag" do
before do
bundle "install" # to create a lockfile, which deployment or frozen need
- bundle "install #{flag_name} #{value}"
+
+ bundle "install #{args}", raise_on_error: false
end
- it "should print a deprecation warning", bundler: "< 3" do
- expect(deprecations).to include(
- "The `#{flag_name}` flag is deprecated because it relies on " \
- "being remembered across bundler invocations, which bundler " \
- "will no longer do in future versions. Instead please use " \
- "`bundle config set #{option_name} #{value}`, and stop using this flag"
+ it "fails with a helpful error" do
+ expect(err).to include(
+ "The `#{flag_name}` flag has been removed because it relied on " \
+ "being remembered across bundler invocations, which bundler no " \
+ "longer does. Instead please use `bundle config set " \
+ "#{option_name} #{value}`, and stop using this flag"
)
end
-
- pending "fails with a helpful error", bundler: "3"
end
end
end
context "bundle install with multiple sources" do
before do
- install_gemfile <<-G
+ install_gemfile <<-G, raise_on_error: false
source "https://gem.repo3"
source "https://gem.repo1"
G
end
- it "shows a deprecation", bundler: "< 3" do
- expect(deprecations).to include(
- "Your Gemfile contains multiple global sources. " \
- "Using `source` more than once without a block is a security risk, and " \
- "may result in installing unexpected gems. To resolve this warning, use " \
- "a block to indicate which gems should come from the secondary source."
+ it "fails with a helpful error" do
+ expect(err).to include(
+ "This Gemfile contains multiple global sources. " \
+ "Each source after the first must include a block to indicate which gems " \
+ "should come from that source"
)
end
- it "doesn't show lockfile deprecations if there's a lockfile", bundler: "< 3" do
- bundle "install"
+ it "doesn't show lockfile deprecations if there's a lockfile" do
+ lockfile <<~L
+ GEM
+ remote: https://gem.repo3/
+ remote: https://gem.repo1/
+ specs:
+
+ PLATFORMS
+ #{lockfile_platforms}
- expect(deprecations).to include(
- "Your Gemfile contains multiple global sources. " \
- "Using `source` more than once without a block is a security risk, and " \
- "may result in installing unexpected gems. To resolve this warning, use " \
- "a block to indicate which gems should come from the secondary source."
+ DEPENDENCIES
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ bundle "install", raise_on_error: false
+
+ expect(err).to include(
+ "This Gemfile contains multiple global sources. " \
+ "Each source after the first must include a block to indicate which gems " \
+ "should come from that source"
)
- expect(deprecations).not_to include(
+ expect(err).not_to include(
"Your lockfile contains a single rubygems source section with multiple remotes, which is insecure. " \
"Make sure you run `bundle install` in non frozen mode and commit the result to make your lockfile secure."
)
- bundle "config set --local frozen true"
- bundle "install"
-
- expect(deprecations).to include(
- "Your Gemfile contains multiple global sources. " \
- "Using `source` more than once without a block is a security risk, and " \
- "may result in installing unexpected gems. To resolve this warning, use " \
- "a block to indicate which gems should come from the secondary source."
+ bundle_config "frozen true"
+ bundle "install", raise_on_error: false
+
+ expect(err).to include(
+ "This Gemfile contains multiple global sources. " \
+ "Each source after the first must include a block to indicate which gems " \
+ "should come from that source"
)
- expect(deprecations).not_to include(
+ expect(err).not_to include(
"Your lockfile contains a single rubygems source section with multiple remotes, which is insecure. " \
"Make sure you run `bundle install` in non frozen mode and commit the result to make your lockfile secure."
)
end
-
- pending "fails with a helpful error", bundler: "3"
end
- context "bundle install in frozen mode with a lockfile with a single rubygems section with multiple remotes" do
+ context "bundle install with a lockfile with a single rubygems section with multiple remotes" do
before do
build_repo3 do
build_gem "myrack", "0.9.1"
@@ -479,19 +562,62 @@ RSpec.describe "major deprecations" do
myrack!
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
+ end
- bundle "config set --local frozen true"
+ it "shows an error" do
+ bundle "install", raise_on_error: false
+
+ expect(err).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure. Make sure you run `bundle install` in non frozen mode and commit the result to make your lockfile secure.")
end
+ end
- it "shows a deprecation", bundler: "< 3" do
- bundle "install"
+ context "bundle install with a lockfile including X64_MINGW_LEGACY platform" do
+ before do
+ gemfile <<~G
+ source "https://gem.repo1"
+ gem "rake"
+ G
+
+ lockfile <<~L
+ GEM
+ remote: https://rubygems.org/
+ specs:
+ rake (10.3.2)
+
+ PLATFORMS
+ ruby
+ x64-mingw32
- expect(deprecations).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure. Make sure you run `bundle install` in non frozen mode and commit the result to make your lockfile secure.")
+ DEPENDENCIES
+ rake
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
end
- pending "fails with a helpful error", bundler: "3"
+ it "warns a helpful error" do
+ bundle "install", raise_on_error: false
+
+ expect(err).to include("Found x64-mingw32 in lockfile, which is deprecated and will be removed in the future.")
+ end
+ end
+
+ context "with a global path source" do
+ before do
+ build_lib "foo"
+
+ install_gemfile <<-G, raise_on_error: false
+ path "#{lib_path("foo-1.0")}"
+ gem 'foo'
+ G
+ end
+
+ it "shows an error" do
+ expect(err).to include("You can no longer specify a path source by itself")
+ end
end
context "when Bundler.setup is run in a ruby script" do
@@ -519,19 +645,41 @@ RSpec.describe "major deprecations" do
context "when `bundler/deployment` is required in a ruby script" do
before do
- ruby <<-RUBY
+ ruby <<-RUBY, raise_on_error: false
require 'bundler/deployment'
RUBY
end
- it "should print a capistrano deprecation warning", bundler: "< 3" do
- expect(deprecations).to include("Bundler no longer integrates " \
+ it "should print a capistrano deprecation warning" do
+ expect(err).to include("Bundler no longer integrates " \
"with Capistrano, but Capistrano provides " \
"its own integration with Bundler via the " \
"capistrano-bundler gem. Use it instead.")
end
+ end
- pending "fails with a helpful error", bundler: "3"
+ context "when `bundler/capistrano` is required in a ruby script" do
+ before do
+ ruby <<-RUBY, raise_on_error: false
+ require 'bundler/capistrano'
+ RUBY
+ end
+
+ it "fails with a helpful error" do
+ expect(err).to include("[REMOVED] The Bundler task for Capistrano. Please use https://github.com/capistrano/bundler")
+ end
+ end
+
+ context "when `bundler/vlad` is required in a ruby script" do
+ before do
+ ruby <<-RUBY, raise_on_error: false
+ require 'bundler/vlad'
+ RUBY
+ end
+
+ it "fails with a helpful error" do
+ expect(err).to include("[REMOVED] The Bundler task for Vlad")
+ end
end
context "bundle show" do
@@ -544,14 +692,12 @@ RSpec.describe "major deprecations" do
context "with --outdated flag" do
before do
- bundle "show --outdated"
+ bundle "show --outdated", raise_on_error: false
end
- it "prints a deprecation warning informing about its removal", bundler: "< 3" do
- expect(deprecations).to include("the `--outdated` flag to `bundle show` was undocumented and will be removed without replacement")
+ it "fails with a helpful message" do
+ expect(err).to include("the `--outdated` flag to `bundle show` has been removed in favor of `bundle show --verbose`")
end
-
- pending "fails with a helpful message", bundler: "3"
end
end
@@ -564,28 +710,32 @@ RSpec.describe "major deprecations" do
end
context "with --install" do
- it "shows a deprecation warning", bundler: "< 3" do
- bundle "remove myrack --install"
+ it "fails with a helpful message" do
+ bundle "remove myrack --install", raise_on_error: false
- expect(err).to include "[DEPRECATED] The `--install` flag has been deprecated. `bundle install` is triggered by default."
+ expect(err).to include "The `--install` flag has been removed. `bundle install` is triggered by default."
end
-
- pending "fails with a helpful message", bundler: "3"
end
end
- context "bundle viz", :realworld do
+ context "bundle viz" do
before do
- realworld_system_gems "ruby-graphviz --version 1.2.5"
- create_file "gems.rb", "source 'https://gem.repo1'"
- bundle "viz"
+ bundle "viz", raise_on_error: false
end
- it "prints a deprecation warning", bundler: "< 3" do
- expect(deprecations).to include "The `viz` command has been renamed to `graph` and moved to a plugin. See https://github.com/rubygems/bundler-graph"
+ it "fails with a helpful message" do
+ expect(err).to include "The `viz` command has been renamed to `graph` and moved to a plugin. See https://github.com/rubygems/bundler-graph"
end
+ end
- pending "fails with a helpful message", bundler: "3"
+ context "bundle inject" do
+ before do
+ bundle "inject", raise_on_error: false
+ end
+
+ it "fails with a helpful message" do
+ expect(err).to include "The `inject` command has been replaced by the `add` command"
+ end
end
context "bundle plugin install --local_git" do
@@ -595,20 +745,20 @@ RSpec.describe "major deprecations" do
end
end
- it "prints a deprecation warning", bundler: "< 3" do
- bundle "plugin install foo --local_git #{lib_path("foo-1.0")}"
+ it "fails with a helpful message" do
+ bundle "plugin install foo --local_git #{lib_path("foo-1.0")}", raise_on_error: false
- expect(out).to include("Installed plugin foo")
- expect(deprecations).to include "--local_git is deprecated, use --git"
+ expect(err).to include "--local_git has been removed, use --git"
end
-
- pending "fails with a helpful message", bundler: "3"
end
- describe "deprecating rubocop" do
+ describe "removing rubocop" do
before do
- global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "false",
- "BUNDLE_GEM__CI" => "false", "BUNDLE_GEM__CHANGELOG" => "false"
+ bundle_config_global "gem.mit false"
+ bundle_config_global "gem.test false"
+ bundle_config_global "gem.coc false"
+ bundle_config_global "gem.ci false"
+ bundle_config_global "gem.changelog false"
end
context "bundle gem --rubocop" do
@@ -616,9 +766,9 @@ RSpec.describe "major deprecations" do
bundle "gem my_new_gem --rubocop", raise_on_error: false
end
- it "prints a deprecation warning", bundler: "< 3" do
- expect(deprecations).to include \
- "--rubocop is deprecated, use --linter=rubocop"
+ it "prints an error" do
+ expect(err).to include \
+ "--rubocop has been removed, use --linter=rubocop"
end
end
@@ -627,32 +777,22 @@ RSpec.describe "major deprecations" do
bundle "gem my_new_gem --no-rubocop", raise_on_error: false
end
- it "prints a deprecation warning", bundler: "< 3" do
- expect(deprecations).to include \
- "--no-rubocop is deprecated, use --linter"
+ it "prints an error" do
+ expect(err).to include \
+ "--no-rubocop has been removed, use --no-linter"
end
end
+ end
- context "bundle gem with gem.rubocop set to true" do
- before do
- bundle "gem my_new_gem", env: { "BUNDLE_GEM__RUBOCOP" => "true" }, raise_on_error: false
- end
-
- it "prints a deprecation warning", bundler: "< 3" do
- expect(deprecations).to include \
- "config gem.rubocop is deprecated; we've updated your config to use gem.linter instead"
- end
+ context " bundle gem --ext parameter with no value" do
+ it "prints error when used before gem name" do
+ bundle "gem --ext foo", raise_on_error: false
+ expect(err).to include "Extensions can now be generated using C or Rust, so `--ext` with no arguments has been removed. Please select a language, e.g. `--ext=rust` to generate a Rust extension."
end
- context "bundle gem with gem.rubocop set to false" do
- before do
- bundle "gem my_new_gem", env: { "BUNDLE_GEM__RUBOCOP" => "false" }, raise_on_error: false
- end
-
- it "prints a deprecation warning", bundler: "< 3" do
- expect(deprecations).to include \
- "config gem.rubocop is deprecated; we've updated your config to use gem.linter instead"
- end
+ it "prints error when used after gem name" do
+ bundle "gem foo --ext", raise_on_error: false
+ expect(err).to include "Extensions can now be generated using C or Rust, so `--ext` with no arguments has been removed. Please select a language, e.g. `--ext=rust` to generate a Rust extension."
end
end
end
diff --git a/spec/bundler/plugins/command_spec.rb b/spec/bundler/plugins/command_spec.rb
index f8dacb0e51..05d535a70c 100644
--- a/spec/bundler/plugins/command_spec.rb
+++ b/spec/bundler/plugins/command_spec.rb
@@ -53,6 +53,40 @@ RSpec.describe "command plugins" do
expect(out).to eq("You gave me tacos, tofu, lasange")
end
+ it "passes help flag to plugin" do
+ update_repo2 do
+ build_plugin "helpful" do |s|
+ s.write "plugins.rb", <<-RUBY
+ module Helpful
+ class Command
+ Bundler::Plugin::API.command "greet", self
+
+ def exec(command, args)
+ if args.include?("--help") || args.include?("-h")
+ puts "Usage: bundle greet [NAME]"
+ else
+ puts "Hello"
+ end
+ end
+ end
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install helpful --source https://gem.repo2"
+ expect(out).to include("Installed plugin helpful")
+
+ bundle "greet --help"
+ expect(out).to eq("Usage: bundle greet [NAME]")
+
+ bundle "greet -h"
+ expect(out).to eq("Usage: bundle greet [NAME]")
+
+ bundle "help greet"
+ expect(out).to eq("Usage: bundle greet [NAME]")
+ end
+
it "raises error on redeclaration of command" do
update_repo2 do
build_plugin "copycat" do |s|
diff --git a/spec/bundler/plugins/hook_spec.rb b/spec/bundler/plugins/hook_spec.rb
index 3f9053bbc8..ad8a4daeff 100644
--- a/spec/bundler/plugins/hook_spec.rb
+++ b/spec/bundler/plugins/hook_spec.rb
@@ -193,6 +193,129 @@ RSpec.describe "hook plugins" do
end
end
+ context "before-eval hook" do
+ before do
+ build_repo2 do
+ build_plugin "before-eval-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_BEFORE_EVAL do |gemfile, lockfile|
+ puts "hooked eval start of \#{File.basename(gemfile)} to \#{File.basename(lockfile)}"
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install before-eval-plugin --source https://gem.repo2"
+ end
+
+ it "runs before the Gemfile is evaluated" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "rake"
+ G
+
+ expect(out).to include "hooked eval start of Gemfile to Gemfile.lock"
+ end
+ end
+
+ context "after-eval hook" do
+ before do
+ build_repo2 do
+ build_plugin "after-eval-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_AFTER_EVAL do |defn|
+ puts "hooked eval after with gems \#{defn.dependencies.map(&:name).join(", ")}"
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install after-eval-plugin --source https://gem.repo2"
+ end
+
+ it "runs after the Gemfile is evaluated" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ gem "rake"
+ G
+
+ expect(out).to include "hooked eval after with gems myrack, rake"
+ end
+ end
+
+ context "before-fetch and after-fetch hooks" do
+ before do
+ build_repo2 do
+ build_plugin "fetch-timing-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ @timing_start = nil
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_BEFORE_FETCH do |spec|
+ @timing_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ puts "gem \#{spec.name} started fetch at \#{@timing_start}"
+ end
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GEM_AFTER_FETCH do |spec|
+ timing_end = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ puts "gem \#{spec.name} took \#{timing_end - @timing_start} to fetch"
+ @timing_start = nil
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install fetch-timing-plugin --source https://gem.repo2"
+ end
+
+ it "runs around each gem download" do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "rake"
+ gem "myrack"
+ G
+
+ expect(out).to include "gem rake started fetch at"
+ expect(out).to match(/gem rake took \d+\.\d+ to fetch/)
+ expect(out).to include "gem myrack started fetch at"
+ expect(out).to match(/gem myrack took \d+\.\d+ to fetch/)
+ end
+ end
+
+ context "before-git-fetch and after-git-fetch hooks" do
+ before do
+ build_repo2 do
+ build_plugin "git-fetch-timing-plugin" do |s|
+ s.write "plugins.rb", <<-RUBY
+ @timing_start = nil
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GIT_BEFORE_FETCH do |source|
+ @timing_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ puts "git source \#{source.name} started fetch at \#{@timing_start}"
+ end
+ Bundler::Plugin::API.hook Bundler::Plugin::Events::GIT_AFTER_FETCH do |source|
+ timing_end = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ puts "git source \#{source.name} took \#{timing_end - @timing_start} to fetch"
+ @timing_start = nil
+ end
+ RUBY
+ end
+ end
+
+ bundle "plugin install git-fetch-timing-plugin --source https://gem.repo2"
+ end
+
+ it "runs around each git source fetch" 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 "git source foo started fetch at"
+ expect(out).to match(/git source foo took \d+\.\d+ to fetch/)
+ end
+ end
+
def install_gemfile_and_bundler_require
install_gemfile <<-G
source "https://gem.repo1"
diff --git a/spec/bundler/plugins/install_spec.rb b/spec/bundler/plugins/install_spec.rb
index d0de607e6c..dcacf764be 100644
--- a/spec/bundler/plugins/install_spec.rb
+++ b/spec/bundler/plugins/install_spec.rb
@@ -168,7 +168,7 @@ RSpec.describe "bundler plugin install" do
build_repo2 do
build_plugin "chaplin" do |s|
s.write "plugins.rb", <<-RUBY
- raise "I got you man"
+ raise RuntimeError, "threw exception on load"
RUBY
end
end
@@ -203,13 +203,6 @@ RSpec.describe "bundler plugin install" do
expect(out).to include("Installed plugin foo")
plugin_should_be_installed("foo")
end
-
- it "raises an error when both git and local git sources are specified", bundler: "< 3" do
- bundle "plugin install foo --git /phony/path/project --local_git git@gitphony.com:/repo/project", raise_on_error: false
-
- expect(exitstatus).not_to eq(0)
- expect(err).to eq("Remote and local plugin git sources can't be both specified")
- end
end
context "path plugins" do
@@ -272,6 +265,43 @@ RSpec.describe "bundler plugin install" do
plugin_should_be_installed("foo")
end
+ it "overrides the index with the new plugin version" do
+ gemfile <<-G
+ source 'https://gem.repo2'
+ plugin 'foo', "1.0"
+ gem 'myrack', "1.0.0"
+ G
+
+ bundle "install"
+
+ update_repo2 do
+ build_plugin "foo", "2.0.0"
+ end
+
+ gemfile <<-G
+ source 'https://gem.repo2'
+ plugin 'foo', "2.0"
+ gem 'myrack', "1.0.0"
+ G
+
+ bundle "install"
+
+ expected = local_plugin_gem("foo-2.0.0", "lib").to_s
+ expect(Bundler::Plugin.index.load_paths("foo")).to eq([expected])
+ end
+
+ it "respects bundler groups" do
+ gemfile <<-G
+ source 'https://gem.repo2'
+ plugin 'foo'
+ gem 'myrack', "1.0.0"
+ G
+
+ bundle "install", env: { "BUNDLE_WITHOUT" => "default" }
+
+ expect(out).to include("Bundle complete! 1 Gemfile dependency, 0 gems now installed.")
+ end
+
it "accepts plugin version" do
update_repo2 do
build_plugin "foo", "1.1.0"
@@ -341,7 +371,7 @@ RSpec.describe "bundler plugin install" do
gem 'myrack', "1.0.0"
G
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
install_gemfile <<-G
source 'https://gem.repo2'
plugin 'foo'
diff --git a/spec/bundler/plugins/source/example_spec.rb b/spec/bundler/plugins/source/example_spec.rb
index 32940bf849..4cd4a1a931 100644
--- a/spec/bundler/plugins/source/example_spec.rb
+++ b/spec/bundler/plugins/source/example_spec.rb
@@ -67,7 +67,7 @@ RSpec.describe "real source plugins" do
expect(the_bundle).to include_gems("a-path-gem 1.0")
end
- it "writes to lock file" do
+ it "writes to lockfile" do
bundle "install"
checksums = checksums_section_when_enabled do |c|
@@ -92,7 +92,7 @@ RSpec.describe "real source plugins" do
a-path-gem!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -124,38 +124,35 @@ RSpec.describe "real source plugins" do
let(:uri_hash) { Digest(:SHA1).hexdigest(lib_path("a-path-gem-1.0").to_s) }
it "copies repository to vendor cache and uses it" do
bundle "install"
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/a-path-gem-1.0-#{uri_hash}")).to exist
expect(bundled_app("vendor/cache/a-path-gem-1.0-#{uri_hash}/.git")).not_to exist
expect(bundled_app("vendor/cache/a-path-gem-1.0-#{uri_hash}/.bundlecache")).to be_file
- FileUtils.rm_rf lib_path("a-path-gem-1.0")
+ FileUtils.rm_r lib_path("a-path-gem-1.0")
expect(the_bundle).to include_gems("a-path-gem 1.0")
end
it "copies repository to vendor cache and uses it even when installed with `path` configured" do
- bundle "config set --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle :install
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/a-path-gem-1.0-#{uri_hash}")).to exist
- FileUtils.rm_rf lib_path("a-path-gem-1.0")
+ FileUtils.rm_r lib_path("a-path-gem-1.0")
expect(the_bundle).to include_gems("a-path-gem 1.0")
end
it "bundler package copies repository to vendor cache" do
- bundle "config set --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle :install
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/a-path-gem-1.0-#{uri_hash}")).to exist
- FileUtils.rm_rf lib_path("a-path-gem-1.0")
+ FileUtils.rm_r lib_path("a-path-gem-1.0")
expect(the_bundle).to include_gems("a-path-gem 1.0")
end
end
@@ -180,7 +177,7 @@ RSpec.describe "real source plugins" do
a-path-gem!
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -336,7 +333,7 @@ RSpec.describe "real source plugins" do
expect(the_bundle).to include_gems("ma-gitp-gem 1.0")
end
- it "writes to lock file" do
+ it "writes to lockfile" do
revision = revision_for(lib_path("ma-gitp-gem-1.0"))
bundle "install"
@@ -363,7 +360,7 @@ RSpec.describe "real source plugins" do
ma-gitp-gem!
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -389,7 +386,7 @@ RSpec.describe "real source plugins" do
ma-gitp-gem!
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
@@ -446,13 +443,12 @@ RSpec.describe "real source plugins" do
end
G
- bundle "config set cache_all true"
bundle :cache
expect(bundled_app("vendor/cache/foo-1.0-#{ref}")).to exist
expect(bundled_app("vendor/cache/foo-1.0-#{ref}/.git")).not_to exist
expect(bundled_app("vendor/cache/foo-1.0-#{ref}/.bundlecache")).to be_file
- FileUtils.rm_rf lib_path("foo-1.0")
+ FileUtils.rm_r lib_path("foo-1.0")
expect(the_bundle).to include_gems "foo 1.0"
end
end
diff --git a/spec/bundler/quality_es_spec.rb b/spec/bundler/quality_es_spec.rb
index 0dbd77e451..e68674c030 100644
--- a/spec/bundler/quality_es_spec.rb
+++ b/spec/bundler/quality_es_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe "La biblioteca si misma" do
]
pattern = /\b#{Regexp.union(useless_words)}\b/i
- File.readlines(filename).each_with_index do |line, number|
+ File.readlines(File.expand_path(filename, source_root)).each_with_index do |line, number|
next unless word_found = pattern.match(line)
failing_line_message << "#{filename}:#{number.succ} contiene '#{word_found}'. Esta palabra tiene un significado subjetivo y es mejor obviarla en textos técnicos."
end
@@ -29,7 +29,7 @@ RSpec.describe "La biblioteca si misma" do
failing_line_message = []
specific_pronouns = /\b(él|ella|ellos|ellas)\b/i
- File.readlines(filename).each_with_index do |line, number|
+ File.readlines(File.expand_path(filename, source_root)).each_with_index do |line, number|
next unless word_found = specific_pronouns.match(line)
failing_line_message << "#{filename}:#{number.succ} contiene '#{word_found}'. Use pronombres más genéricos en la documentación."
end
diff --git a/spec/bundler/quality_spec.rb b/spec/bundler/quality_spec.rb
index c7fce17b62..16b7f18788 100644
--- a/spec/bundler/quality_spec.rb
+++ b/spec/bundler/quality_spec.rb
@@ -20,6 +20,9 @@ RSpec.describe "The library itself" do
end
def check_for_tab_characters(filename)
+ # Because Go uses hard tabs
+ return if filename.end_with?(".go.tt")
+
failing_lines = []
each_line(filename) do |line, number|
failing_lines << number + 1 if line.include?("\t")
@@ -106,7 +109,7 @@ RSpec.describe "The library itself" do
it "does not include any unresolved merge conflicts" do
error_messages = []
- exempt = %r{lock/lockfile_spec|quality_spec|vcr_cassettes|\.ronn|lockfile_parser\.rb}
+ exempt = %r{lock/lockfile_spec|quality_spec|vcr_cassettes|\.ronn|lockfile_parser}
tracked_files.each do |filename|
next if filename&.match?(exempt)
error_messages << check_for_git_merge_conflicts(filename)
@@ -115,10 +118,8 @@ RSpec.describe "The library itself" do
end
it "maintains language quality of the documentation" do
- included = /ronn/
error_messages = []
man_tracked_files.each do |filename|
- next unless filename&.match?(included)
error_messages << check_for_expendable_words(filename)
error_messages << check_for_specific_pronouns(filename)
end
@@ -138,12 +139,12 @@ RSpec.describe "The library itself" do
it "documents all used settings" do
exemptions = %w[
- forget_cli_options
gem.changelog
gem.ci
gem.coc
gem.linter
gem.mit
+ gem.bundle
gem.rubocop
gem.test
git.allow_insecure
@@ -165,7 +166,8 @@ RSpec.describe "The library itself" do
line.scan(/Bundler\.settings\[:#{key_pattern}\]/).flatten.each {|s| all_settings[s] << "referenced at `#{filename}:#{number.succ}`" }
end
end
- documented_settings = File.read("lib/bundler/man/bundle-config.1.ronn")[/LIST OF AVAILABLE KEYS.*/m].scan(/^\* `#{key_pattern}`/).flatten
+ settings_section = File.read(source_root.join("lib/bundler/man/bundle-config.1.ronn")).split(/^## /).find {|section| section.start_with?("LIST OF AVAILABLE KEYS") }
+ documented_settings = settings_section.scan(/^\* `#{key_pattern}`/).flatten
documented_settings.each do |s|
all_settings.delete(s)
@@ -185,8 +187,8 @@ RSpec.describe "The library itself" do
end
it "can still be built" do
- with_built_bundler do |_gem_path|
- expect(err).to be_empty, "bundler should build as a gem without warnings, but\n#{err}"
+ with_built_bundler do |gem_path|
+ expect(File.exist?(gem_path)).to be true
end
end
@@ -216,7 +218,7 @@ RSpec.describe "The library itself" do
end
end
- warnings = last_command.stdboth.split("\n")
+ warnings = stdboth.split("\n")
# ignore warnings around deprecated Object#=~ method in RubyGems
warnings.reject! {|w| w =~ %r{rubygems\/version.rb.*deprecated\ Object#=~} }
@@ -254,6 +256,6 @@ RSpec.describe "The library itself" do
private
def each_line(filename, &block)
- File.readlines(filename, encoding: "UTF-8").each_with_index(&block)
+ File.readlines(File.expand_path(filename, source_root), encoding: "UTF-8").each_with_index(&block)
end
end
diff --git a/spec/bundler/realworld/edgecases_spec.rb b/spec/bundler/realworld/edgecases_spec.rb
index fb434aba70..391aa0cef6 100644
--- a/spec/bundler/realworld/edgecases_spec.rb
+++ b/spec/bundler/realworld/edgecases_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe "real world edgecases", realworld: true do
index.search(#{name.dump}).select {|spec| requirement.satisfied_by?(spec.version) }.last
end
if rubygem.nil?
- raise "Could not find #{name} (#{requirement}) on rubygems.org!\n" \
+ raise ArgumentError, "Could not find #{name} (#{requirement}) on rubygems.org!\n" \
"Found specs:\n\#{index.send(:specs).inspect}"
end
puts "#{name} (\#{rubygem.version})"
@@ -63,141 +63,6 @@ RSpec.describe "real world edgecases", realworld: true do
expect(lockfile).to include(rubygems_version("activesupport", "~> 3.0"))
end
- it "is able to update a top-level dependency when there is a conflict on a shared transitive child" do
- # from https://github.com/rubygems/bundler/issues/5031
-
- pristine_system_gems "bundler-1.99.0"
-
- gemfile <<-G
- source "https://rubygems.org"
- gem 'rails', '~> 4.2.7.1'
- gem 'paperclip', '~> 5.1.0'
- G
-
- lockfile <<-L
- GEM
- remote: https://rubygems.org/
- specs:
- actionmailer (4.2.7.1)
- actionpack (= 4.2.7.1)
- actionview (= 4.2.7.1)
- activejob (= 4.2.7.1)
- mail (~> 2.5, >= 2.5.4)
- rails-dom-testing (~> 1.0, >= 1.0.5)
- actionpack (4.2.7.1)
- actionview (= 4.2.7.1)
- activesupport (= 4.2.7.1)
- rack (~> 1.6)
- rack-test (~> 0.6.2)
- rails-dom-testing (~> 1.0, >= 1.0.5)
- rails-html-sanitizer (~> 1.0, >= 1.0.2)
- actionview (4.2.7.1)
- activesupport (= 4.2.7.1)
- builder (~> 3.1)
- erubis (~> 2.7.0)
- rails-dom-testing (~> 1.0, >= 1.0.5)
- rails-html-sanitizer (~> 1.0, >= 1.0.2)
- activejob (4.2.7.1)
- activesupport (= 4.2.7.1)
- globalid (>= 0.3.0)
- activemodel (4.2.7.1)
- activesupport (= 4.2.7.1)
- builder (~> 3.1)
- activerecord (4.2.7.1)
- activemodel (= 4.2.7.1)
- activesupport (= 4.2.7.1)
- arel (~> 6.0)
- activesupport (4.2.7.1)
- i18n (~> 0.7)
- json (~> 1.7, >= 1.7.7)
- minitest (~> 5.1)
- thread_safe (~> 0.3, >= 0.3.4)
- tzinfo (~> 1.1)
- arel (6.0.3)
- builder (3.2.2)
- climate_control (0.0.3)
- activesupport (>= 3.0)
- cocaine (0.5.8)
- climate_control (>= 0.0.3, < 1.0)
- concurrent-ruby (1.0.2)
- erubis (2.7.0)
- globalid (0.3.7)
- activesupport (>= 4.1.0)
- i18n (0.7.0)
- json (1.8.3)
- loofah (2.0.3)
- nokogiri (>= 1.5.9)
- mail (2.6.4)
- mime-types (>= 1.16, < 4)
- mime-types (3.1)
- mime-types-data (~> 3.2015)
- mime-types-data (3.2016.0521)
- mimemagic (0.3.2)
- mini_portile2 (2.1.0)
- minitest (5.9.1)
- nokogiri (1.6.8)
- mini_portile2 (~> 2.1.0)
- pkg-config (~> 1.1.7)
- paperclip (5.1.0)
- activemodel (>= 4.2.0)
- activesupport (>= 4.2.0)
- cocaine (~> 0.5.5)
- mime-types
- mimemagic (~> 0.3.0)
- pkg-config (1.1.7)
- rack (1.6.4)
- rack-test (0.6.3)
- rack (>= 1.0)
- rails (4.2.7.1)
- actionmailer (= 4.2.7.1)
- actionpack (= 4.2.7.1)
- actionview (= 4.2.7.1)
- activejob (= 4.2.7.1)
- activemodel (= 4.2.7.1)
- activerecord (= 4.2.7.1)
- activesupport (= 4.2.7.1)
- bundler (>= 1.3.0, < 2.0)
- railties (= 4.2.7.1)
- sprockets-rails
- rails-deprecated_sanitizer (1.0.3)
- activesupport (>= 4.2.0.alpha)
- rails-dom-testing (1.0.7)
- activesupport (>= 4.2.0.beta, < 5.0)
- nokogiri (~> 1.6.0)
- rails-deprecated_sanitizer (>= 1.0.1)
- rails-html-sanitizer (1.0.3)
- loofah (~> 2.0)
- railties (4.2.7.1)
- actionpack (= 4.2.7.1)
- activesupport (= 4.2.7.1)
- rake (>= 0.8.7)
- thor (>= 0.18.1, < 2.0)
- rake (11.3.0)
- sprockets (3.7.0)
- concurrent-ruby (~> 1.0)
- rack (> 1, < 3)
- sprockets-rails (3.2.0)
- actionpack (>= 4.0)
- activesupport (>= 4.0)
- sprockets (>= 3.0.0)
- thor (0.19.1)
- thread_safe (0.3.5)
- tzinfo (1.2.2)
- thread_safe (~> 0.1)
-
- PLATFORMS
- ruby
-
- DEPENDENCIES
- paperclip (~> 5.1.0)
- rails (~> 4.2.7.1)
- L
-
- bundle "lock --update paperclip", env: { "BUNDLER_VERSION" => "1.99.0" }
-
- expect(lockfile).to include(rubygems_version("paperclip", "~> 5.1.0"))
- end
-
it "outputs a helpful warning when gems have a gemspec with invalid `require_paths`" do
install_gemfile <<-G, standalone: true, env: { "BUNDLE_FORCE_RUBY_PLATFORM" => "1" }
source 'https://rubygems.org'
diff --git a/spec/bundler/realworld/ffi_spec.rb b/spec/bundler/realworld/ffi_spec.rb
index 5f40b43a0f..bede372b41 100644
--- a/spec/bundler/realworld/ffi_spec.rb
+++ b/spec/bundler/realworld/ffi_spec.rb
@@ -42,12 +42,12 @@ RSpec.describe "loading dynamically linked library on a bundle exec context", re
}
C
- sys_exec "gcc -g -o libfoo.so -shared -fpic libfoo.c"
+ in_bundled_app "gcc -g -o libfoo.so -shared -fpic libfoo.c"
install_gemfile <<-G
source "https://rubygems.org"
- gem 'ffi'
+ gem 'ffi', force_ruby_platform: true
G
bundle "exec ruby foo.rb"
diff --git a/spec/bundler/realworld/fixtures/tapioca/Gemfile.lock b/spec/bundler/realworld/fixtures/tapioca/Gemfile.lock
index c9ded0fd78..c2df2f9229 100644
--- a/spec/bundler/realworld/fixtures/tapioca/Gemfile.lock
+++ b/spec/bundler/realworld/fixtures/tapioca/Gemfile.lock
@@ -31,8 +31,8 @@ GEM
spoom (>= 1.2.0)
thor (>= 1.2.0)
yard-sorbet
- thor (1.3.2)
- yard (0.9.37)
+ thor (1.4.0)
+ yard (0.9.42)
yard-sorbet (0.9.0)
sorbet-runtime
yard
@@ -46,4 +46,4 @@ DEPENDENCIES
tapioca
BUNDLED WITH
- 2.7.0.dev
+ 4.1.0.dev
diff --git a/spec/bundler/realworld/fixtures/warbler/Gemfile b/spec/bundler/realworld/fixtures/warbler/Gemfile
index e4d3e8ea96..5687bbd975 100644
--- a/spec/bundler/realworld/fixtures/warbler/Gemfile
+++ b/spec/bundler/realworld/fixtures/warbler/Gemfile
@@ -3,5 +3,5 @@
source "https://rubygems.org"
gem "demo", path: "./demo"
-gem "jruby-jars", "~> 9.4"
-gem "warbler", "~> 2.0"
+gem "jruby-jars", "~> 10.0"
+gem "warbler", "~> 2.1"
diff --git a/spec/bundler/realworld/fixtures/warbler/Gemfile.lock b/spec/bundler/realworld/fixtures/warbler/Gemfile.lock
index 497726eba2..05f3bc4e3f 100644
--- a/spec/bundler/realworld/fixtures/warbler/Gemfile.lock
+++ b/spec/bundler/realworld/fixtures/warbler/Gemfile.lock
@@ -6,25 +6,30 @@ PATH
GEM
remote: https://rubygems.org/
specs:
- jruby-jars (9.4.9.0)
- jruby-rack (1.1.21)
- rake (13.0.1)
- rubyzip (1.3.0)
- warbler (2.0.5)
- jruby-jars (>= 9.0.0.0)
- jruby-rack (>= 1.1.1, < 1.3)
- rake (>= 10.1.0)
- rubyzip (~> 1.0, < 1.4)
+ jruby-jars (10.0.0.1)
+ jruby-rack (1.2.7)
+ ostruct (0.6.3)
+ rake (13.3.0)
+ rexml (3.4.2)
+ rubyzip (3.3.0)
+ warbler (2.1.0)
+ jruby-jars (>= 9.4, < 10.1)
+ jruby-rack (>= 1.2.3, < 1.3)
+ ostruct (~> 0.6.2)
+ rake (~> 13.0, >= 13.0.3)
+ rexml (~> 3.0)
+ rubyzip (>= 3.0.0)
PLATFORMS
+ arm64-darwin
java
ruby
- universal-java-11
+ universal-java
DEPENDENCIES
demo!
- jruby-jars (~> 9.4)
- warbler (~> 2.0)
+ jruby-jars (~> 10.0)
+ warbler (~> 2.1)
BUNDLED WITH
- 2.7.0.dev
+ 4.1.0.dev
diff --git a/spec/bundler/realworld/gemfile_source_header_spec.rb b/spec/bundler/realworld/gemfile_source_header_spec.rb
deleted file mode 100644
index c532c6a867..0000000000
--- a/spec/bundler/realworld/gemfile_source_header_spec.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe "fetching dependencies with a mirrored source", realworld: true do
- let(:mirror) { "https://server.example.org" }
- let(:original) { "http://127.0.0.1:#{@port}" }
-
- before do
- setup_server
- bundle "config set --local mirror.#{mirror} #{original}"
- end
-
- after do
- Artifice.deactivate
- @t.kill
- @t.join
- end
-
- it "sets the 'X-Gemfile-Source' and 'User-Agent' headers and bundles successfully" do
- gemfile <<-G
- source "#{mirror}"
- gem 'weakling'
- G
-
- bundle :install, artifice: nil
-
- expect(out).to include("Installing weakling")
- expect(out).to include("Bundle complete")
- expect(the_bundle).to include_gems "weakling 0.0.3"
- end
-
- private
-
- def setup_server
- require_rack
- @port = find_unused_port
- @server_uri = "http://127.0.0.1:#{@port}"
-
- require_relative "../support/artifice/endpoint_mirror_source"
- require_relative "../support/silent_logger"
-
- require "rackup/server"
-
- @t = Thread.new do
- Rackup::Server.start(app: EndpointMirrorSource,
- Host: "0.0.0.0",
- Port: @port,
- server: "webrick",
- AccessLog: [],
- Logger: Spec::SilentLogger.new)
- end.run
-
- wait_for_server("127.0.0.1", @port)
- end
-end
diff --git a/spec/bundler/realworld/mirror_probe_spec.rb b/spec/bundler/realworld/mirror_probe_spec.rb
deleted file mode 100644
index 66a553da28..0000000000
--- a/spec/bundler/realworld/mirror_probe_spec.rb
+++ /dev/null
@@ -1,132 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe "fetching dependencies with a not available mirror", realworld: true do
- let(:mirror) { @mirror_uri }
- let(:original) { @server_uri }
- let(:server_port) { @server_port }
- let(:host) { "127.0.0.1" }
-
- before do
- require_rack
- setup_server
- setup_mirror
- end
-
- after do
- Artifice.deactivate
- @server_thread.kill
- @server_thread.join
- end
-
- context "with a specific fallback timeout" do
- before do
- global_config("BUNDLE_MIRROR__HTTP://127__0__0__1:#{server_port}/__FALLBACK_TIMEOUT/" => "true",
- "BUNDLE_MIRROR__HTTP://127__0__0__1:#{server_port}/" => mirror)
- end
-
- it "install a gem using the original uri when the mirror is not responding" do
- gemfile <<-G
- source "#{original}"
- gem 'weakling'
- G
-
- bundle :install, artifice: nil
-
- expect(out).to include("Installing weakling")
- expect(out).to include("Bundle complete")
- expect(the_bundle).to include_gems "weakling 0.0.3"
- end
- end
-
- context "with a global fallback timeout" do
- before do
- global_config("BUNDLE_MIRROR__ALL__FALLBACK_TIMEOUT/" => "1",
- "BUNDLE_MIRROR__ALL" => mirror)
- end
-
- it "install a gem using the original uri when the mirror is not responding" do
- gemfile <<-G
- source "#{original}"
- gem 'weakling'
- G
-
- bundle :install, artifice: nil
-
- expect(out).to include("Installing weakling")
- expect(out).to include("Bundle complete")
- expect(the_bundle).to include_gems "weakling 0.0.3"
- end
- end
-
- context "with a specific mirror without a fallback timeout" do
- before do
- global_config("BUNDLE_MIRROR__HTTP://127__0__0__1:#{server_port}/" => mirror)
- end
-
- it "fails to install the gem with a timeout error" do
- gemfile <<-G
- source "#{original}"
- gem 'weakling'
- G
-
- bundle :install, artifice: nil, raise_on_error: false
-
- expect(out).to include("Fetching source index from #{mirror}")
-
- err_lines = err.split("\n")
- expect(err_lines).to include(%r{\ARetrying fetcher due to error \(2/4\): Bundler::HTTPError Could not fetch specs from #{mirror}/ due to underlying error <})
- expect(err_lines).to include(%r{\ARetrying fetcher due to error \(3/4\): Bundler::HTTPError Could not fetch specs from #{mirror}/ due to underlying error <})
- expect(err_lines).to include(%r{\ARetrying fetcher due to error \(4/4\): Bundler::HTTPError Could not fetch specs from #{mirror}/ due to underlying error <})
- expect(err_lines).to include(%r{\ACould not fetch specs from #{mirror}/ due to underlying error <})
- end
- end
-
- context "with a global mirror without a fallback timeout" do
- before do
- global_config("BUNDLE_MIRROR__ALL" => mirror)
- end
-
- it "fails to install the gem with a timeout error" do
- gemfile <<-G
- source "#{original}"
- gem 'weakling'
- G
-
- bundle :install, artifice: nil, raise_on_error: false
-
- expect(out).to include("Fetching source index from #{mirror}")
-
- err_lines = err.split("\n")
- expect(err_lines).to include(%r{\ARetrying fetcher due to error \(2/4\): Bundler::HTTPError Could not fetch specs from #{mirror}/ due to underlying error <})
- expect(err_lines).to include(%r{\ARetrying fetcher due to error \(3/4\): Bundler::HTTPError Could not fetch specs from #{mirror}/ due to underlying error <})
- expect(err_lines).to include(%r{\ARetrying fetcher due to error \(4/4\): Bundler::HTTPError Could not fetch specs from #{mirror}/ due to underlying error <})
- expect(err_lines).to include(%r{\ACould not fetch specs from #{mirror}/ due to underlying error <})
- end
- end
-
- def setup_server
- @server_port = find_unused_port
- @server_uri = "http://#{host}:#{@server_port}"
-
- require_relative "../support/artifice/endpoint"
- require_relative "../support/silent_logger"
-
- require "rackup/server"
-
- @server_thread = Thread.new do
- Rackup::Server.start(app: Endpoint,
- Host: host,
- Port: @server_port,
- server: "webrick",
- AccessLog: [],
- Logger: Spec::SilentLogger.new)
- end.run
-
- wait_for_server(host, @server_port)
- end
-
- def setup_mirror
- @mirror_port = find_unused_port
- @mirror_uri = "http://#{host}:#{@mirror_port}"
- end
-end
diff --git a/spec/bundler/realworld/slow_perf_spec.rb b/spec/bundler/realworld/slow_perf_spec.rb
index 32e266ff1b..5d36ba7455 100644
--- a/spec/bundler/realworld/slow_perf_spec.rb
+++ b/spec/bundler/realworld/slow_perf_spec.rb
@@ -30,115 +30,6 @@ RSpec.describe "bundle install with complex dependencies", realworld: true do
G
bundle "lock", env: { "DEBUG_RESOLVER" => "1" }
- expect(out).to include("Solution found after 1 attempts")
- end
-
- it "resolves big gemfile quickly" do
- gemfile <<~G
- # frozen_string_literal: true
-
- source "https://rubygems.org"
-
- gem "rails"
- gem "pg", ">= 0.18", "< 2.0"
- gem "goldiloader"
- gem "awesome_nested_set"
- gem "circuitbox"
- gem "passenger"
- gem "globalid"
- gem "rack-cors"
- gem "rails-pg-extras"
- gem "linear_regression_trend"
- gem "rack-protection"
- gem "pundit"
- gem "remote_ip_proxy_scrubber"
- gem "bcrypt"
- gem "searchkick"
- gem "excon"
- gem "faraday_middleware-aws-sigv4"
- gem "typhoeus"
- gem "sidekiq"
- gem "sidekiq-undertaker"
- gem "sidekiq-cron"
- gem "storext"
- gem "appsignal"
- gem "fcm"
- gem "business_time"
- gem "tzinfo"
- gem "holidays"
- gem "bigdecimal"
- gem "progress_bar"
- gem "redis"
- gem "hiredis"
- gem "state_machines"
- gem "state_machines-audit_trail"
- gem "state_machines-activerecord"
- gem "interactor"
- gem "ar_transaction_changes"
- gem "redis-rails"
- gem "seed_migration"
- gem "lograge"
- gem "graphiql-rails", group: :development
- gem "graphql"
- gem "pusher"
- gem "rbnacl"
- gem "jwt"
- gem "json-schema"
- gem "discard"
- gem "money"
- gem "strip_attributes"
- gem "validates_email_format_of"
- gem "audited"
- gem "concurrent-ruby"
- gem "with_advisory_lock"
-
- group :test do
- gem "rspec-sidekiq"
- gem "simplecov", require: false
- end
-
- group :development, :test do
- gem "byebug", platform: :mri
- gem "guard"
- gem "guard-bundler"
- gem "guard-rspec"
- gem "rb-fsevent"
- gem "rspec_junit_formatter"
- gem "rspec-collection_matchers"
- gem "rspec-rails"
- gem "rspec-retry"
- gem "state_machines-rspec"
- gem "dotenv-rails"
- gem "database_cleaner-active_record"
- gem "database_cleaner-redis"
- gem "timecop"
- end
-
- gem "factory_bot_rails"
- gem "faker"
-
- group :development do
- gem "listen"
- gem "sql_queries_count"
- gem "rubocop"
- gem "rubocop-performance"
- gem "rubocop-rspec"
- gem "rubocop-rails"
- gem "brakeman"
- gem "bundler-audit"
- gem "solargraph"
- gem "annotate"
- end
- G
-
- if Bundler.feature_flag.bundler_3_mode?
- bundle "lock", env: { "DEBUG_RESOLVER" => "1" }, raise_on_error: false
-
- expect(out).to include("backtracking").exactly(26).times
- else
- bundle "lock", env: { "DEBUG_RESOLVER" => "1" }
-
- expect(out).to include("Solution found after 10 attempts")
- end
+ expect(out).to include("Solution found after 2 attempts")
end
end
diff --git a/spec/bundler/resolver/basic_spec.rb b/spec/bundler/resolver/basic_spec.rb
index 0d3eadbaf8..185df1b1c7 100644
--- a/spec/bundler/resolver/basic_spec.rb
+++ b/spec/bundler/resolver/basic_spec.rb
@@ -211,12 +211,12 @@ RSpec.describe "Resolving" do
it "resolves all gems to latest patch" do
# strict is not set, so bar goes up a minor version due to dependency from foo 1.4.5
- should_conservative_resolve_and_include :patch, [], %w[foo-1.4.5 bar-2.1.1]
+ should_conservative_resolve_and_include :patch, true, %w[foo-1.4.5 bar-2.1.1]
end
it "resolves all gems to latest patch strict" do
# strict is set, so foo can only go up to 1.4.4 to avoid bar going up a minor version, and bar can go up to 2.0.5
- should_conservative_resolve_and_include [:patch, :strict], [], %w[foo-1.4.4 bar-2.0.5]
+ should_conservative_resolve_and_include [:patch, :strict], true, %w[foo-1.4.4 bar-2.0.5]
end
it "resolves foo only to latest patch - same dependency case" do
@@ -238,7 +238,7 @@ RSpec.describe "Resolving" do
it "resolves foo only to latest patch - changing dependency declared case" do
# bar is locked AND a declared dependency in the Gemfile, so it will not move, and therefore
# foo can only move up to 1.4.4.
- @base << Bundler::LazySpecification.new("bar", Gem::Version.new("2.0.3"), nil)
+ @base = Bundler::SpecSet.new([Bundler::LazySpecification.new("bar", Gem::Version.new("2.0.3"), nil)])
should_conservative_resolve_and_include :patch, ["foo"], %w[foo-1.4.4 bar-2.0.3]
end
@@ -256,20 +256,20 @@ RSpec.describe "Resolving" do
it "resolves all gems to latest minor" do
# strict is not set, so bar goes up a major version due to dependency from foo 1.4.5
- should_conservative_resolve_and_include :minor, [], %w[foo-1.5.1 bar-3.0.0]
+ should_conservative_resolve_and_include :minor, true, %w[foo-1.5.1 bar-3.0.0]
end
it "resolves all gems to latest minor strict" do
# strict is set, so foo can only go up to 1.5.0 to avoid bar going up a major version
- should_conservative_resolve_and_include [:minor, :strict], [], %w[foo-1.5.0 bar-2.1.1]
+ should_conservative_resolve_and_include [:minor, :strict], true, %w[foo-1.5.0 bar-2.1.1]
end
it "resolves all gems to latest major" do
- should_conservative_resolve_and_include :major, [], %w[foo-2.0.0 bar-3.0.0]
+ should_conservative_resolve_and_include :major, true, %w[foo-2.0.0 bar-3.0.0]
end
it "resolves all gems to latest major strict" do
- should_conservative_resolve_and_include [:major, :strict], [], %w[foo-2.0.0 bar-3.0.0]
+ should_conservative_resolve_and_include [:major, :strict], true, %w[foo-2.0.0 bar-3.0.0]
end
# Why would this happen in real life? If bar 2.2 has a bug that the author of foo wants to bypass
@@ -292,21 +292,21 @@ RSpec.describe "Resolving" do
end
it "could revert to a previous version level patch" do
- should_conservative_resolve_and_include :patch, [], %w[foo-1.4.4 bar-2.1.1]
+ should_conservative_resolve_and_include :patch, true, %w[foo-1.4.4 bar-2.1.1]
end
it "cannot revert to a previous version in strict mode level patch" do
# fall back to the locked resolution since strict means we can't regress either version
- should_conservative_resolve_and_include [:patch, :strict], [], %w[foo-1.4.3 bar-2.2.3]
+ should_conservative_resolve_and_include [:patch, :strict], true, %w[foo-1.4.3 bar-2.2.3]
end
it "could revert to a previous version level minor" do
- should_conservative_resolve_and_include :minor, [], %w[foo-1.5.0 bar-2.0.5]
+ should_conservative_resolve_and_include :minor, true, %w[foo-1.5.0 bar-2.0.5]
end
it "cannot revert to a previous version in strict mode level minor" do
# fall back to the locked resolution since strict means we can't regress either version
- should_conservative_resolve_and_include [:minor, :strict], [], %w[foo-1.4.3 bar-2.2.3]
+ should_conservative_resolve_and_include [:minor, :strict], true, %w[foo-1.4.3 bar-2.2.3]
end
end
end
@@ -379,4 +379,44 @@ RSpec.describe "Resolving" do
should_resolve_without_dependency_api %w[myrack-3.0.0 standalone_migrations-2.0.4]
end
+
+ it "resolves fine cases that need joining unbounded disjoint ranges" do
+ @index = build_index do
+ gem "inspec", "5.22.3" do
+ dep "ruby", ">= 3.2.2"
+ dep "train-kubernetes", ">= 0.1.7"
+ end
+
+ gem "ruby", "3.2.2"
+
+ gem "train-kubernetes", "0.1.12" do
+ dep "k8s-ruby", ">= 0.14.0"
+ end
+
+ gem "train-kubernetes", "0.1.10" do
+ dep "k8s-ruby", "= 0.10.5"
+ end
+
+ gem "train-kubernetes", "0.1.7" do
+ dep "k8s-ruby", ">= 0.10.5"
+ end
+
+ gem "k8s-ruby", "0.10.5" do
+ dep "ruby","< 3.2.2"
+ end
+
+ gem "k8s-ruby", "0.11.0" do
+ dep "ruby", ">= 3.2.2"
+ end
+
+ gem "k8s-ruby", "0.14.0" do
+ dep "ruby", "< 3.2.2"
+ end
+ end
+
+ dep "inspec", "5.22.3"
+ dep "ruby", "3.2.2"
+
+ should_resolve_as %w[inspec-5.22.3 ruby-3.2.2 train-kubernetes-0.1.7 k8s-ruby-0.11.0]
+ end
end
diff --git a/spec/bundler/resolver/platform_spec.rb b/spec/bundler/resolver/platform_spec.rb
index 8e51911bbd..a1d095d024 100644
--- a/spec/bundler/resolver/platform_spec.rb
+++ b/spec/bundler/resolver/platform_spec.rb
@@ -48,11 +48,11 @@ RSpec.describe "Resolving platform craziness" do
it "takes the latest ruby gem, even if an older platform specific version is available" do
@index = build_index do
gem "foo", "1.0.0"
- gem "foo", "1.0.0", "x64-mingw32"
+ gem "foo", "1.0.0", "x64-mingw-ucrt"
gem "foo", "1.1.0"
end
dep "foo"
- platforms "x64-mingw32"
+ platforms "x64-mingw-ucrt"
should_resolve_as %w[foo-1.1.0]
end
@@ -61,12 +61,12 @@ RSpec.describe "Resolving platform craziness" do
@index = build_index do
gem "bar", "1.0.0"
gem "foo", "1.0.0"
- gem "foo", "1.0.0", "x64-mingw32" do
+ gem "foo", "1.0.0", "x64-mingw-ucrt" do
dep "bar", "< 1"
end
end
dep "foo"
- platforms "x64-mingw32"
+ platforms "x64-mingw-ucrt"
should_resolve_as %w[foo-1.0.0]
end
@@ -74,12 +74,12 @@ RSpec.describe "Resolving platform craziness" do
it "prefers the platform specific gem to the ruby version" do
@index = build_index do
gem "foo", "1.0.0"
- gem "foo", "1.0.0", "x64-mingw32"
+ gem "foo", "1.0.0", "x64-mingw-ucrt"
end
dep "foo"
- platforms "x64-mingw32"
+ platforms "x64-mingw-ucrt"
- should_resolve_as %w[foo-1.0.0-x64-mingw32]
+ should_resolve_as %w[foo-1.0.0-x64-mingw-ucrt]
end
describe "on a linux platform" do
@@ -159,15 +159,15 @@ RSpec.describe "Resolving platform craziness" do
before do
@index = build_index do
gem "foo", "1.0.0"
- gem "foo", "1.0.0", "x64-mingw32"
+ gem "foo", "1.0.0", "x64-mingw-ucrt"
gem "foo", "1.1.0"
- gem "foo", "1.1.0", "x64-mingw32" do |s|
+ gem "foo", "1.1.0", "x64-mingw-ucrt" do |s|
s.required_ruby_version = [">= 2.0", "< 2.4"]
end
gem "Ruby\0", "2.5.1"
end
dep "Ruby\0", "2.5.1"
- platforms "x64-mingw32"
+ platforms "x64-mingw-ucrt"
end
it "takes the latest ruby gem" do
@@ -186,18 +186,18 @@ RSpec.describe "Resolving platform craziness" do
it "takes the latest ruby gem with required_ruby_version if the platform specific gem doesn't match the required_ruby_version" do
@index = build_index do
gem "foo", "1.0.0"
- gem "foo", "1.0.0", "x64-mingw32"
+ gem "foo", "1.0.0", "x64-mingw-ucrt"
gem "foo", "1.1.0" do |s|
s.required_ruby_version = [">= 2.0"]
end
- gem "foo", "1.1.0", "x64-mingw32" do |s|
+ gem "foo", "1.1.0", "x64-mingw-ucrt" do |s|
s.required_ruby_version = [">= 2.0", "< 2.4"]
end
gem "Ruby\0", "2.5.1"
end
dep "foo"
dep "Ruby\0", "2.5.1"
- platforms "x64-mingw32"
+ platforms "x64-mingw-ucrt"
should_resolve_as %w[foo-1.1.0]
end
@@ -205,18 +205,18 @@ RSpec.describe "Resolving platform craziness" do
it "takes the latest ruby gem if the platform specific gem doesn't match the required_ruby_version with multiple platforms" do
@index = build_index do
gem "foo", "1.0.0"
- gem "foo", "1.0.0", "x64-mingw32"
+ gem "foo", "1.0.0", "x64-mingw-ucrt"
gem "foo", "1.1.0" do |s|
s.required_ruby_version = [">= 2.0"]
end
- gem "foo", "1.1.0", "x64-mingw32" do |s|
+ gem "foo", "1.1.0", "x64-mingw-ucrt" do |s|
s.required_ruby_version = [">= 2.0", "< 2.4"]
end
gem "Ruby\0", "2.5.1"
end
dep "foo"
dep "Ruby\0", "2.5.1"
- platforms "x86_64-linux", "x64-mingw32"
+ platforms "x86_64-linux", "x64-mingw-ucrt"
should_resolve_as %w[foo-1.1.0]
end
@@ -342,7 +342,7 @@ RSpec.describe "Resolving platform craziness" do
describe "with mingw32" do
before :each do
@index = build_index do
- platforms "mingw32 mswin32 x64-mingw32 x64-mingw-ucrt" do |platform|
+ platforms "mingw32 mswin32 x64-mingw-ucrt" do |platform|
gem "thin", "1.2.7", platform
end
gem "win32-api", "1.5.1", "universal-mingw32"
@@ -363,10 +363,10 @@ RSpec.describe "Resolving platform craziness" do
should_resolve_as %w[thin-1.2.7-mingw32]
end
- it "finds x64-mingw32 gems" do
- platforms "x64-mingw32"
+ it "finds x64-mingw-ucrt gems" do
+ platforms "x64-mingw-ucrt"
dep "thin"
- should_resolve_as %w[thin-1.2.7-x64-mingw32]
+ should_resolve_as %w[thin-1.2.7-x64-mingw-ucrt]
end
it "finds universal-mingw gems on x86-mingw" do
@@ -376,7 +376,7 @@ RSpec.describe "Resolving platform craziness" do
end
it "finds universal-mingw gems on x64-mingw" do
- platform "x64-mingw32"
+ platform "x64-mingw-ucrt"
dep "win32-api"
should_resolve_as %w[win32-api-1.5.1-universal-mingw32]
end
@@ -387,7 +387,7 @@ RSpec.describe "Resolving platform craziness" do
should_resolve_as %w[thin-1.2.7-x64-mingw-ucrt]
end
- it "finds universal-mingw gems on x64-mingw-ucrt", rubygems: ">= 3.3.18" do
+ it "finds universal-mingw gems on x64-mingw-ucrt" do
platform "x64-mingw-ucrt"
dep "win32-api"
should_resolve_as %w[win32-api-1.5.1-universal-mingw32]
diff --git a/spec/bundler/runtime/env_helpers_spec.rb b/spec/bundler/runtime/env_helpers_spec.rb
index a1607cd057..c4ebdd1fd2 100644
--- a/spec/bundler/runtime/env_helpers_spec.rb
+++ b/spec/bundler/runtime/env_helpers_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe "env helpers" do
path = `getconf PATH`.strip + "#{File::PATH_SEPARATOR}/foo"
with_path_as(path) do
bundle_exec_ruby(bundled_app("source.rb").to_s)
- expect(last_command.stdboth).to eq(path)
+ expect(stdboth).to eq(path)
end
end
@@ -35,7 +35,7 @@ RSpec.describe "env helpers" do
gem_path = ENV["GEM_PATH"] + "#{File::PATH_SEPARATOR}/foo"
with_gem_path_as(gem_path) do
bundle_exec_ruby(bundled_app("source.rb").to_s)
- expect(last_command.stdboth).to eq(gem_path)
+ expect(stdboth).to eq(gem_path)
end
end
@@ -62,9 +62,6 @@ RSpec.describe "env helpers" do
end
it "removes variables that bundler added", :ruby_repo do
- # Simulate bundler has not yet been loaded
- ENV.replace(ENV.to_hash.delete_if {|k, _v| k.start_with?(Bundler::EnvironmentPreserver::BUNDLER_PREFIX) })
-
original = ruby('puts ENV.to_a.map {|e| e.join("=") }.sort.join("\n")', artifice: "fail")
create_file("source.rb", <<-RUBY)
puts Bundler.original_env.to_a.map {|e| e.join("=") }.sort.join("\n")
@@ -74,77 +71,65 @@ RSpec.describe "env helpers" do
end
end
- shared_examples_for "an unbundling helper" do
+ describe "Bundler.unbundled_env" do
it "should delete BUNDLE_PATH" do
create_file("source.rb", <<-RUBY)
- print #{modified_env}.has_key?('BUNDLE_PATH')
+ print Bundler.unbundled_env.has_key?('BUNDLE_PATH')
RUBY
ENV["BUNDLE_PATH"] = "./foo"
bundle_exec_ruby bundled_app("source.rb")
- expect(last_command.stdboth).to include "false"
+ expect(stdboth).to include "false"
end
it "should remove absolute path to 'bundler/setup' from RUBYOPT even if it was present in original env" do
create_file("source.rb", <<-RUBY)
- print #{modified_env}['RUBYOPT']
+ print Bundler.unbundled_env['RUBYOPT']
RUBY
setup_require = "-r#{lib_dir}/bundler/setup"
ENV["BUNDLER_ORIG_RUBYOPT"] = "-W2 #{setup_require} #{ENV["RUBYOPT"]}"
bundle_exec_ruby bundled_app("source.rb")
- expect(last_command.stdboth).not_to include(setup_require)
+ expect(stdboth).not_to include(setup_require)
end
it "should remove relative path to 'bundler/setup' from RUBYOPT even if it was present in original env" do
create_file("source.rb", <<-RUBY)
- print #{modified_env}['RUBYOPT']
+ print Bundler.unbundled_env['RUBYOPT']
RUBY
ENV["BUNDLER_ORIG_RUBYOPT"] = "-W2 -rbundler/setup #{ENV["RUBYOPT"]}"
bundle_exec_ruby bundled_app("source.rb")
- expect(last_command.stdboth).not_to include("-rbundler/setup")
+ expect(stdboth).not_to include("-rbundler/setup")
end
it "should delete BUNDLER_SETUP even if it was present in original env" do
create_file("source.rb", <<-RUBY)
- print #{modified_env}.has_key?('BUNDLER_SETUP')
+ print Bundler.unbundled_env.has_key?('BUNDLER_SETUP')
RUBY
ENV["BUNDLER_ORIG_BUNDLER_SETUP"] = system_gem_path("gems/bundler-#{Bundler::VERSION}/lib/bundler/setup").to_s
bundle_exec_ruby bundled_app("source.rb")
- expect(last_command.stdboth).to include "false"
+ expect(stdboth).to include "false"
end
it "should restore RUBYLIB", :ruby_repo do
create_file("source.rb", <<-RUBY)
- print #{modified_env}['RUBYLIB']
+ print Bundler.unbundled_env['RUBYLIB']
RUBY
ENV["RUBYLIB"] = lib_dir.to_s + File::PATH_SEPARATOR + "/foo"
ENV["BUNDLER_ORIG_RUBYLIB"] = lib_dir.to_s + File::PATH_SEPARATOR + "/foo-original"
bundle_exec_ruby bundled_app("source.rb")
- expect(last_command.stdboth).to include("/foo-original")
+ expect(stdboth).to include("/foo-original")
end
it "should restore the original MANPATH" do
create_file("source.rb", <<-RUBY)
- print #{modified_env}['MANPATH']
+ print Bundler.unbundled_env['MANPATH']
RUBY
ENV["MANPATH"] = "/foo"
ENV["BUNDLER_ORIG_MANPATH"] = "/foo-original"
bundle_exec_ruby bundled_app("source.rb")
- expect(last_command.stdboth).to include("/foo-original")
+ expect(stdboth).to include("/foo-original")
end
end
- describe "Bundler.unbundled_env" do
- let(:modified_env) { "Bundler.unbundled_env" }
-
- it_behaves_like "an unbundling helper"
- end
-
- describe "Bundler.clean_env", bundler: 2 do
- let(:modified_env) { "Bundler.clean_env" }
-
- it_behaves_like "an unbundling helper"
- end
-
describe "Bundler.with_original_env" do
it "should set ENV to original_env in the block" do
expected = Bundler.original_env
@@ -161,26 +146,6 @@ RSpec.describe "env helpers" do
end
end
- describe "Bundler.with_clean_env", bundler: 2 do
- it "should set ENV to unbundled_env in the block" do
- expected = Bundler.unbundled_env
-
- actual = Bundler.ui.silence do
- Bundler.with_clean_env { ENV.to_hash }
- end
-
- expect(actual).to eq(expected)
- end
-
- it "should restore the environment after execution" do
- Bundler.ui.silence do
- Bundler.with_clean_env { ENV["FOO"] = "hello" }
- end
-
- expect(ENV).not_to have_key("FOO")
- end
- end
-
describe "Bundler.with_unbundled_env" do
it "should set ENV to unbundled_env in the block" do
expected = Bundler.unbundled_env
@@ -212,21 +177,6 @@ RSpec.describe "env helpers" do
end
end
- describe "Bundler.clean_system", bundler: 2 do
- before do
- create_file("source.rb", <<-'RUBY')
- Bundler.ui.silence { Bundler.clean_system("ruby", "-e", "exit(42) unless ENV['BUNDLE_FOO'] == 'bar'") }
-
- exit $?.exitstatus
- RUBY
- end
-
- it "runs system inside with_clean_env" do
- run_bundler_script({ "BUNDLE_FOO" => "bar" }, bundled_app("source.rb"))
- expect($?.exitstatus).to eq(42)
- end
- end
-
describe "Bundler.unbundled_system" do
before do
create_file("source.rb", <<-'RUBY')
@@ -263,27 +213,6 @@ RSpec.describe "env helpers" do
end
end
- describe "Bundler.clean_exec", bundler: 2 do
- before do
- create_file("source.rb", <<-'RUBY')
- Process.fork do
- exit Bundler.ui.silence { Bundler.clean_exec(%(test "\$BUNDLE_FOO" = "bar")) }
- end
-
- _, status = Process.wait2
-
- exit(status.exitstatus)
- RUBY
- end
-
- it "runs exec inside with_clean_env" do
- skip "Fork not implemented" if Gem.win_platform?
-
- run_bundler_script({ "BUNDLE_FOO" => "bar" }, bundled_app("source.rb"))
- expect($?.exitstatus).to eq(1)
- end
- end
-
describe "Bundler.unbundled_exec" do
before do
create_file("source.rb", <<-'RUBY')
@@ -297,7 +226,7 @@ RSpec.describe "env helpers" do
RUBY
end
- it "runs exec inside with_clean_env" do
+ it "runs exec inside with_unbundled_env" do
skip "Fork not implemented" if Gem.win_platform?
run_bundler_script({ "BUNDLE_FOO" => "bar" }, bundled_app("source.rb"))
diff --git a/spec/bundler/runtime/executable_spec.rb b/spec/bundler/runtime/executable_spec.rb
index cf57805708..89cee21b00 100644
--- a/spec/bundler/runtime/executable_spec.rb
+++ b/spec/bundler/runtime/executable_spec.rb
@@ -19,8 +19,9 @@ RSpec.describe "Running bin/* commands" do
expect(out).to eq("1.0.0")
end
- it "allows the location of the gem stubs to be specified" do
- bundle "binstubs myrack", path: "gbin"
+ it "allows the location of the gem stubs to be configured" do
+ bundle_config "bin gbin"
+ bundle "binstubs myrack"
expect(bundled_app("bin")).not_to exist
expect(bundled_app("gbin/myrackup")).to exist
@@ -30,7 +31,8 @@ RSpec.describe "Running bin/* commands" do
end
it "allows absolute paths as a specification of where to install bin stubs" do
- bundle "binstubs myrack", path: tmp("bin")
+ bundle_config "bin #{tmp("bin")}"
+ bundle "binstubs myrack"
gembin tmp("bin/myrackup")
expect(out).to eq("1.0.0")
@@ -77,7 +79,7 @@ RSpec.describe "Running bin/* commands" do
expect(out).to eq("1.0")
end
- it "creates a bundle binstub" do
+ it "does not create a bundle binstub" do
gemfile <<-G
source "https://gem.repo1"
gem "bundler"
@@ -85,7 +87,9 @@ RSpec.describe "Running bin/* commands" do
bundle "binstubs bundler"
- expect(bundled_app("bin/bundle")).to exist
+ expect(bundled_app("bin/bundle")).not_to exist
+
+ expect(err).to include("Bundler itself does not use binstubs because its version is selected by RubyGems")
end
it "does not generate bin stubs if the option was not specified" do
@@ -94,38 +98,6 @@ RSpec.describe "Running bin/* commands" do
expect(bundled_app("bin/myrackup")).not_to exist
end
- it "allows you to stop installing binstubs", bundler: "< 3" do
- skip "delete permission error" if Gem.win_platform?
-
- bundle "install --binstubs bin/"
- bundled_app("bin/myrackup").rmtree
- bundle "install --binstubs \"\""
-
- expect(bundled_app("bin/myrackup")).not_to exist
-
- bundle "config bin"
- expect(out).to include("You have not configured a value for `bin`")
- end
-
- it "remembers that the option was specified", bundler: "< 3" do
- gemfile <<-G
- source "https://gem.repo1"
- gem "activesupport"
- G
-
- bundle :install, binstubs: "bin"
-
- gemfile <<-G
- source "https://gem.repo1"
- gem "activesupport"
- gem "myrack"
- G
-
- bundle "install"
-
- expect(bundled_app("bin/myrackup")).to exist
- end
-
it "rewrites bins on binstubs with --force option" do
install_gemfile <<-G
source "https://gem.repo1"
diff --git a/spec/bundler/runtime/gem_tasks_spec.rb b/spec/bundler/runtime/gem_tasks_spec.rb
index 046300391b..b855142e60 100644
--- a/spec/bundler/runtime/gem_tasks_spec.rb
+++ b/spec/bundler/runtime/gem_tasks_spec.rb
@@ -66,9 +66,7 @@ RSpec.describe "require 'bundler/gem_tasks'" do
it "includes the relevant tasks" do
define_local_gem_using_gem_tasks
- with_gem_path_as(base_system_gem_path.to_s) do
- sys_exec "#{rake} -T", env: { "GEM_HOME" => system_gem_path.to_s }
- end
+ in_bundled_app "rake -T"
expect(err).to be_empty
expected_tasks = [
@@ -85,9 +83,7 @@ RSpec.describe "require 'bundler/gem_tasks'" do
it "defines a working `rake install` task", :ruby_repo do
define_local_gem_using_gem_tasks
- with_gem_path_as(base_system_gem_path.to_s) do
- sys_exec "#{rake} install", env: { "GEM_HOME" => system_gem_path.to_s }
- end
+ in_bundled_app "rake install"
expect(err).to be_empty
@@ -136,7 +132,7 @@ RSpec.describe "require 'bundler/gem_tasks'" do
before do
define_local_gem_using_gem_tasks
- bundle "config set path vendor/bundle"
+ bundle_config "path vendor/bundle"
end
it "works", :ruby_repo do
@@ -155,9 +151,8 @@ RSpec.describe "require 'bundler/gem_tasks'" do
it "adds 'pkg' to rake/clean's CLOBBER" do
define_local_gem_using_gem_tasks
- with_gem_path_as(base_system_gem_path.to_s) do
- sys_exec %(#{rake} -e 'load "Rakefile"; puts CLOBBER.inspect'), env: { "GEM_HOME" => system_gem_path.to_s }
- end
+ in_bundled_app %(rake -e 'load "Rakefile"; puts CLOBBER.inspect')
+
expect(out).to eq '["pkg"]'
end
end
diff --git a/spec/bundler/runtime/inline_spec.rb b/spec/bundler/runtime/inline_spec.rb
index 48385b452d..c6f9bbdbd7 100644
--- a/spec/bundler/runtime/inline_spec.rb
+++ b/spec/bundler/runtime/inline_spec.rb
@@ -380,7 +380,7 @@ RSpec.describe "bundler/inline#gemfile" do
rake
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
script <<-RUBY
@@ -414,7 +414,7 @@ RSpec.describe "bundler/inline#gemfile" do
rake
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
script <<-RUBY
@@ -499,7 +499,7 @@ RSpec.describe "bundler/inline#gemfile" do
end
it "skips platform warnings" do
- bundle "config set --local force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
script <<-RUBY
gemfile(true) do
@@ -512,7 +512,7 @@ RSpec.describe "bundler/inline#gemfile" do
end
it "still installs if the application has `bundle package` no_install config set" do
- bundle "config set --local no_install true"
+ bundle_config "no_install true"
script <<-RUBY
gemfile do
@@ -590,37 +590,10 @@ RSpec.describe "bundler/inline#gemfile" do
expect(err).to be_empty
end
- it "when requiring fileutils after does not show redefinition warnings" do
- Dir.mkdir tmp("path_without_gemfile")
-
- default_fileutils_version = ruby "gem 'fileutils', '< 999999'; require 'fileutils'; puts FileUtils::VERSION", raise_on_error: false
- skip "fileutils isn't a default gem" if default_fileutils_version.empty?
-
- realworld_system_gems "fileutils --version 1.4.1"
-
- realworld_system_gems "pathname --version 0.2.0"
-
- script <<-RUBY, dir: tmp("path_without_gemfile"), env: { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s, "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
- require "bundler/inline"
-
- gemfile(true) do
- source "https://gem.repo2"
- end
-
- require "fileutils"
- RUBY
-
- expect(err).to eq("The Gemfile specifies no dependencies")
- end
-
- it "does not load default timeout" do
+ it "does not load default timeout", rubygems: ">= 3.5.0" do
default_timeout_version = ruby "gem 'timeout', '< 999999'; require 'timeout'; puts Timeout::VERSION", raise_on_error: false
skip "timeout isn't a default gem" if default_timeout_version.empty?
- # This only works on RubyGems 3.5.0 or higher
- ruby "require 'rubygems/timeout'", raise_on_error: false
- skip "rubygems under test does not yet vendor timeout" unless last_command.success?
-
build_repo4 do
build_gem "timeout", "999"
end
@@ -705,8 +678,54 @@ RSpec.describe "bundler/inline#gemfile" do
expect(out).to include("Installing psych 999")
expect(out).to include("Installing stringio 999")
- expect(out).to include("The psych gem was resolved to 999")
- expect(out).to include("The stringio gem was resolved to 999")
+ if Gem.respond_to?(:use_psych?) && Gem.use_psych?
+ expect(out).to include("The psych gem was resolved to 999")
+ expect(out).to include("The stringio gem was resolved to 999")
+ end
+ end
+
+ it "installs a conflicting default gem and non-default gems together" do
+ build_repo4 do
+ build_gem "securerandom", "999"
+ build_gem "myrack", "1.0.0"
+ end
+
+ script <<-RUBY, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ gemfile(true) do
+ source "https://gem.repo4"
+ gem "securerandom"
+ gem "myrack"
+ end
+
+ puts MYRACK
+ RUBY
+
+ expect(out).to include("Installing securerandom 999")
+ expect(out).to include("Installing myrack 1.0.0")
+ expect(out).to include("1.0.0")
+ expect(err).to be_empty
+ end
+
+ it "installs a conflicting default gem alongside git sources" do
+ build_repo4 do
+ build_gem "securerandom", "999"
+ end
+
+ build_git "foo", "1.0.0"
+
+ script <<-RUBY, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ gemfile(true) do
+ source "https://gem.repo4"
+ gem "securerandom"
+ gem "foo", :git => #{lib_path("foo-1.0.0").to_s.dump}
+ end
+
+ puts FOO
+ RUBY
+
+ expect(out).to include("Installing securerandom 999")
+ expect(out).to include("1.0.0")
+ expect(err).to be_empty
end
it "leaves a lockfile in the same directory as the inline script alone" do
diff --git a/spec/bundler/runtime/load_spec.rb b/spec/bundler/runtime/load_spec.rb
index 15f3d0eb5b..472cde87c5 100644
--- a/spec/bundler/runtime/load_spec.rb
+++ b/spec/bundler/runtime/load_spec.rb
@@ -68,7 +68,7 @@ RSpec.describe "Bundler.load" do
begin
expect { Bundler.load }.to raise_error(Bundler::GemfileNotFound)
ensure
- bundler_gemfile.rmtree if @remove_bundler_gemfile
+ FileUtils.rm_rf bundler_gemfile if @remove_bundler_gemfile
end
end
end
diff --git a/spec/bundler/runtime/platform_spec.rb b/spec/bundler/runtime/platform_spec.rb
index 007733d3de..6d96758956 100644
--- a/spec/bundler/runtime/platform_spec.rb
+++ b/spec/bundler/runtime/platform_spec.rb
@@ -100,7 +100,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
nokogiri (~> 1.11)
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
gemfile <<-G
@@ -143,7 +143,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
nokogiri
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "install"
@@ -219,7 +219,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
gem "platform_specific"
G
- bundle "config set force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
bundle "install"
@@ -233,7 +233,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
gem "platform_specific"
G
- bundle "config set force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
bundle "install"
@@ -247,7 +247,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
gem "platform_specific"
G
- bundle "config set force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
bundle "install"
@@ -282,7 +282,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
platform_specific
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "install"
@@ -316,7 +316,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
libv8
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "install"
@@ -344,7 +344,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
platform_specific
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
bundle "install"
@@ -370,14 +370,14 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
end
it "allows specifying only-ruby-platform on windows with dependency platforms" do
- simulate_windows do
+ simulate_platform "x86-mswin32" do
install_gemfile <<-G
source "https://gem.repo1"
- gem "nokogiri", :platforms => [:windows, :mswin, :mswin64, :mingw, :x64_mingw, :jruby]
+ gem "nokogiri", :platforms => [:windows, :jruby]
gem "platform_specific"
G
- bundle "config set force_ruby_platform true"
+ bundle_config "force_ruby_platform true"
bundle "install"
@@ -397,8 +397,8 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
G
bundle :lock
- simulate_windows do
- bundle "config set force_ruby_platform true"
+ simulate_platform "x86-mswin32" do
+ bundle_config "force_ruby_platform true"
bundle "install"
expect(the_bundle).to include_gems "myrack 1.0"
@@ -411,7 +411,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
s.add_dependency "platform_specific"
end
end
- simulate_windows x64_mingw32 do
+ simulate_platform "x64-mingw-ucrt" do
lockfile <<-L
GEM
remote: https://gem.repo2/
@@ -421,7 +421,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
platform_specific
PLATFORMS
- x64-mingw32
+ x64-mingw-ucrt
x86-mingw32
DEPENDENCIES
@@ -434,15 +434,13 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
G
expect(out).to include("lockfile does not have all gems needed for the current platform")
- expect(the_bundle).to include_gem "platform_specific 1.0 x64-mingw32"
+ expect(the_bundle).to include_gem "platform_specific 1.0 x64-mingw-ucrt"
end
end
- %w[x86-mswin32 x64-mswin64 x86-mingw32 x64-mingw32 x64-mingw-ucrt].each do |arch|
- it "allows specifying platform windows on #{arch} arch" do
- platform = send(arch.tr("-", "_"))
-
- simulate_windows platform do
+ %w[x86-mswin32 x64-mswin64 x86-mingw32 x64-mingw-ucrt aarch64-mingw-ucrt].each do |platform|
+ it "allows specifying platform windows on #{platform} platform" do
+ simulate_platform platform do
lockfile <<-L
GEM
remote: https://gem.repo1/
@@ -463,8 +461,6 @@ RSpec.describe "Bundler.setup with multi platform stuff" do
gem "platform_specific", :platforms => [:windows]
G
- bundle "install"
-
expect(the_bundle).to include_gems "platform_specific 1.0 #{platform}"
end
end
diff --git a/spec/bundler/runtime/require_spec.rb b/spec/bundler/runtime/require_spec.rb
index ece6679eb2..46613286d2 100644
--- a/spec/bundler/runtime/require_spec.rb
+++ b/spec/bundler/runtime/require_spec.rb
@@ -119,11 +119,9 @@ RSpec.describe "Bundler.require" do
end
G
- load_error_run <<-R, "fail"
- Bundler.require
- R
+ run "Bundler.require", raise_on_error: false
- expect(err_without_deprecations).to eq("ZOMG LOAD ERROR")
+ expect(err_without_deprecations).to include("cannot load such file -- fail")
end
it "displays a helpful message if the required gem throws an error" do
@@ -155,16 +153,9 @@ RSpec.describe "Bundler.require" do
end
G
- cmd = <<-RUBY
- begin
- Bundler.require
- rescue LoadError => e
- warn "ZOMG LOAD ERROR: \#{e.message}"
- end
- RUBY
- run(cmd)
+ run "Bundler.require", raise_on_error: false
- expect(err_without_deprecations).to eq("ZOMG LOAD ERROR: cannot load such file -- load-bar")
+ expect(err_without_deprecations).to include("cannot load such file -- load-bar")
end
describe "with namespaced gems" do
@@ -215,10 +206,9 @@ RSpec.describe "Bundler.require" do
end
G
- load_error_run <<-R, "jquery-rails"
- Bundler.require
- R
- expect(err_without_deprecations).to eq("ZOMG LOAD ERROR")
+ run "Bundler.require", raise_on_error: false
+
+ expect(err_without_deprecations).to include("cannot load such file -- jquery-rails")
end
it "handles the case where regex fails" do
@@ -233,16 +223,9 @@ RSpec.describe "Bundler.require" do
end
G
- cmd = <<-RUBY
- begin
- Bundler.require
- rescue LoadError => e
- warn "ZOMG LOAD ERROR" if e.message.include?("Could not open library 'libfuuu-1.0'")
- end
- RUBY
- run(cmd)
+ run "Bundler.require", raise_on_error: false
- expect(err_without_deprecations).to eq("ZOMG LOAD ERROR")
+ expect(err_without_deprecations).to include("libfuuu-1.0").and include("cannot open shared object file")
end
it "doesn't swallow the error when the library has an unrelated error" do
@@ -257,16 +240,9 @@ RSpec.describe "Bundler.require" do
end
G
- cmd = <<-RUBY
- begin
- Bundler.require
- rescue LoadError => e
- warn "ZOMG LOAD ERROR: \#{e.message}"
- end
- RUBY
- run(cmd)
+ run "Bundler.require", raise_on_error: false
- expect(err_without_deprecations).to eq("ZOMG LOAD ERROR: cannot load such file -- load-bar")
+ expect(err_without_deprecations).to include("cannot load such file -- load-bar")
end
end
@@ -375,10 +351,9 @@ RSpec.describe "Bundler.require" do
gem "busted_require"
G
- load_error_run <<-R, "no_such_file_omg"
- Bundler.require
- R
- expect(err_without_deprecations).to eq("ZOMG LOAD ERROR")
+ run "Bundler.require", raise_on_error: false
+
+ expect(err_without_deprecations).to include("cannot load such file -- no_such_file_omg")
end
end
end
diff --git a/spec/bundler/runtime/requiring_spec.rb b/spec/bundler/runtime/requiring_spec.rb
index 1f32269622..f0e0aeacaf 100644
--- a/spec/bundler/runtime/requiring_spec.rb
+++ b/spec/bundler/runtime/requiring_spec.rb
@@ -2,14 +2,14 @@
RSpec.describe "Requiring bundler" do
it "takes care of requiring rubygems when entrypoint is bundler/setup" do
- sys_exec("#{Gem.ruby} -I#{lib_dir} -rbundler/setup -e'puts true'", env: { "RUBYOPT" => opt_add("--disable=gems", ENV["RUBYOPT"]) })
+ sys_exec("#{Gem.ruby} -I#{lib_dir} -rbundler/setup -e'puts true'", env: { "RUBYOPT" => "--disable=gems" })
- expect(last_command.stdboth).to eq("true")
+ expect(stdboth).to eq("true")
end
it "takes care of requiring rubygems when requiring just bundler" do
- sys_exec("#{Gem.ruby} -I#{lib_dir} -rbundler -e'puts true'", env: { "RUBYOPT" => opt_add("--disable=gems", ENV["RUBYOPT"]) })
+ sys_exec("#{Gem.ruby} -I#{lib_dir} -rbundler -e'puts true'", env: { "RUBYOPT" => "--disable=gems" })
- expect(last_command.stdboth).to eq("true")
+ expect(stdboth).to eq("true")
end
end
diff --git a/spec/bundler/runtime/self_management_spec.rb b/spec/bundler/runtime/self_management_spec.rb
index a0b2d83d0e..176c2a3121 100644
--- a/spec/bundler/runtime/self_management_spec.rb
+++ b/spec/bundler/runtime/self_management_spec.rb
@@ -3,11 +3,11 @@
RSpec.describe "Self management" do
describe "auto switching" do
let(:previous_minor) do
- "2.3.0"
+ "9.3.0"
end
let(:current_version) do
- "2.4.0"
+ "9.4.0"
end
before do
@@ -24,22 +24,24 @@ RSpec.describe "Self management" do
gem "myrack"
G
+
+ pristine_system_gems "bundler-#{current_version}"
end
it "installs locked version when using system path and uses it" do
lockfile_bundled_with(previous_minor)
- bundle "config set --local path.system true"
- bundle "install", preserve_ruby_flags: true
- expect(out).to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
+ bundle_config "path.system true"
+ bundle "install"
+ expect(out).to include("Bundler #{current_version} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
# It uninstalls the older system bundler
bundle "clean --force", artifice: nil
- expect(out).to eq("Removing bundler (#{Bundler::VERSION})")
+ expect(out).to eq("Removing bundler (#{current_version})")
# App now uses locked version
bundle "-v", artifice: nil
- expect(out).to end_with(previous_minor[0] == "2" ? "Bundler version #{previous_minor}" : previous_minor)
+ expect(out).to eq(previous_minor)
# ruby-core test setup has always "lib" in $LOAD_PATH so `require "bundler/setup"` always activate the local version rather than using RubyGems gem activation stuff
unless ruby_core?
@@ -48,26 +50,26 @@ RSpec.describe "Self management" do
create_file file, <<-RUBY
#!#{Gem.ruby}
require 'bundler/setup'
- puts Bundler::VERSION
+ puts '#{previous_minor}'
RUBY
file.chmod(0o777)
cmd = Gem.win_platform? ? "#{Gem.ruby} bin/bundle_version.rb" : "bin/bundle_version.rb"
- sys_exec cmd, artifice: nil
+ in_bundled_app cmd
expect(out).to eq(previous_minor)
end
# Subsequent installs use the locked version without reinstalling
bundle "install --verbose", artifice: nil
expect(out).to include("Using bundler #{previous_minor}")
- expect(out).not_to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
+ expect(out).not_to include("Bundler #{current_version} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
end
it "installs locked version when using local path and uses it" do
lockfile_bundled_with(previous_minor)
- bundle "config set --local path vendor/bundle"
- bundle "install", preserve_ruby_flags: true
- expect(out).to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
+ bundle_config "path vendor/bundle"
+ bundle "install"
+ expect(out).to include("Bundler #{current_version} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
expect(vendored_gems("gems/bundler-#{previous_minor}")).to exist
# It does not uninstall the locked bundler
@@ -76,7 +78,11 @@ RSpec.describe "Self management" do
# App now uses locked version
bundle "-v"
- expect(out).to end_with(previous_minor[0] == "2" ? "Bundler version #{previous_minor}" : previous_minor)
+ expect(out).to eq(previous_minor)
+
+ # Preserves original gem home when auto-switching
+ bundle "exec ruby -e 'puts Bundler.original_env[\"GEM_HOME\"]'"
+ expect(out).to eq(ENV["GEM_HOME"])
# ruby-core test setup has always "lib" in $LOAD_PATH so `require "bundler/setup"` always activate the local version rather than using RubyGems gem activation stuff
unless ruby_core?
@@ -85,26 +91,26 @@ RSpec.describe "Self management" do
create_file file, <<-RUBY
#!#{Gem.ruby}
require 'bundler/setup'
- puts Bundler::VERSION
+ puts '#{previous_minor}'
RUBY
file.chmod(0o777)
cmd = Gem.win_platform? ? "#{Gem.ruby} bin/bundle_version.rb" : "bin/bundle_version.rb"
- sys_exec cmd, artifice: nil
+ in_bundled_app cmd
expect(out).to eq(previous_minor)
end
# Subsequent installs use the locked version without reinstalling
bundle "install --verbose"
expect(out).to include("Using bundler #{previous_minor}")
- expect(out).not_to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
+ expect(out).not_to include("Bundler #{current_version} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
end
it "installs locked version when using deployment option and uses it" do
lockfile_bundled_with(previous_minor)
- bundle "config set --local deployment true"
- bundle "install", preserve_ruby_flags: true
- expect(out).to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
+ bundle_config "deployment true"
+ bundle "install"
+ expect(out).to include("Bundler #{current_version} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
expect(vendored_gems("gems/bundler-#{previous_minor}")).to exist
# It does not uninstall the locked bundler
@@ -113,12 +119,12 @@ RSpec.describe "Self management" do
# App now uses locked version
bundle "-v"
- expect(out).to end_with(previous_minor[0] == "2" ? "Bundler version #{previous_minor}" : previous_minor)
+ expect(out).to eq(previous_minor)
# Subsequent installs use the locked version without reinstalling
bundle "install --verbose"
expect(out).to include("Using bundler #{previous_minor}")
- expect(out).not_to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
+ expect(out).not_to include("Bundler #{current_version} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
end
it "does not try to install a development version" do
@@ -128,76 +134,92 @@ RSpec.describe "Self management" do
expect(out).not_to match(/restarting using that version/)
bundle "-v"
- expect(out).to eq(Bundler::VERSION[0] == "2" ? "Bundler version #{Bundler::VERSION}" : Bundler::VERSION)
+ expect(out).to eq(current_version)
end
it "does not try to install when --local is passed" do
lockfile_bundled_with(previous_minor)
- system_gems "myrack-1.0.0", path: default_bundle_path
+ system_gems "myrack-1.0.0", path: local_gem_path
bundle "install --local"
expect(out).not_to match(/Installing Bundler/)
bundle "-v"
- expect(out).to eq(Bundler::VERSION[0] == "2" ? "Bundler version #{Bundler::VERSION}" : Bundler::VERSION)
+ expect(out).to eq(current_version)
end
it "shows a discrete message if locked bundler does not exist" do
- missing_minor = "#{Bundler::VERSION[0]}.999.999"
+ missing_minor = "#{current_version[0]}.999.999"
lockfile_bundled_with(missing_minor)
bundle "install"
- expect(err).to eq("Your lockfile is locked to a version of bundler (#{missing_minor}) that doesn't exist at https://rubygems.org/. Going on using #{Bundler::VERSION}")
+ expect(err).to eq("Your lockfile is locked to a version of bundler (#{missing_minor}) that doesn't exist at https://rubygems.org/. Going on using #{current_version}")
bundle "-v"
- expect(out).to eq(Bundler::VERSION[0] == "2" ? "Bundler version #{Bundler::VERSION}" : Bundler::VERSION)
+ expect(out).to eq(current_version)
end
it "installs BUNDLE_VERSION version when using bundle config version x.y.z" do
lockfile_bundled_with(current_version)
- bundle "config set --local version #{previous_minor}"
- bundle "install", preserve_ruby_flags: true
- expect(out).to include("Bundler #{Bundler::VERSION} is running, but your configuration was #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
+ bundle_config "version #{previous_minor}"
+ bundle "install"
+ expect(out).to include("Bundler #{current_version} is running, but your configuration was #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.")
bundle "-v"
- expect(out).to eq(previous_minor[0] == "2" ? "Bundler version #{previous_minor}" : previous_minor)
+ expect(out).to eq(previous_minor)
+ end
+
+ it "requires the right bundler version from the config and run bundle CLI without re-exec" do
+ unless Bundler.rubygems.provides?(">= 4.1.0.dev")
+ skip "This spec can only run when Gem::BundlerVersionFinder.bundler_versions reads bundler configs"
+ end
+
+ lockfile_bundled_with(current_version)
+
+ bundle_config "version #{previous_minor}"
+ bundle_config "path.system true"
+ bundle "install"
+
+ script = bundled_app("script.rb")
+ create_file(script, "p 'executed once'")
+
+ bundle "-v", env: { "RUBYOPT" => "-r#{script}" }
+ expect(out).to eq(%("executed once"\n9.3.0))
end
it "does not try to install when using bundle config version global" do
lockfile_bundled_with(previous_minor)
- bundle "config set version system"
+ bundle_config "version system"
bundle "install"
expect(out).not_to match(/restarting using that version/)
bundle "-v"
- expect(out).to eq(Bundler::VERSION[0] == "2" ? "Bundler version #{Bundler::VERSION}" : Bundler::VERSION)
+ expect(out).to eq(current_version)
end
it "does not try to install when using bundle config version <dev-version>" do
lockfile_bundled_with(previous_minor)
- bundle "config set version #{previous_minor}.dev"
+ bundle_config "version #{previous_minor}.dev"
bundle "install"
expect(out).not_to match(/restarting using that version/)
bundle "-v"
- expect(out).to eq(Bundler::VERSION[0] == "2" ? "Bundler version #{Bundler::VERSION}" : Bundler::VERSION)
+ expect(out).to eq(current_version)
end
it "ignores malformed lockfile version" do
lockfile_bundled_with("2.3.")
bundle "install --verbose"
- expect(out).to include("Using bundler #{Bundler::VERSION}")
+ expect(out).to include("Using bundler #{current_version}")
end
it "uses the right original script when re-execing, if `$0` has been changed to something that's not a script", :ruby_repo do
- bundle "config path vendor/bundle"
-
- system_gems "bundler-9.9.9", path: vendored_gems
+ system_gems "bundler-9.9.9", path: local_gem_path
test = bundled_app("test.rb")
@@ -208,15 +230,13 @@ RSpec.describe "Self management" do
lockfile_bundled_with("9.9.9")
- sys_exec "#{Gem.ruby} #{test}", artifice: nil, raise_on_error: false
+ in_bundled_app "#{Gem.ruby} #{test}", raise_on_error: false
expect(err).to include("Could not find myrack-1.0.0")
expect(err).not_to include("this is the program name")
end
it "uses modified $0 when re-execing, if `$0` has been changed to a script", :ruby_repo do
- bundle "config path vendor/bundle"
-
- system_gems "bundler-9.9.9", path: vendored_gems
+ system_gems "bundler-9.9.9", path: local_gem_path
runner = bundled_app("runner.rb")
@@ -232,7 +252,7 @@ RSpec.describe "Self management" do
lockfile_bundled_with("9.9.9")
- sys_exec "#{Gem.ruby} #{runner} #{script}", artifice: nil, raise_on_error: false
+ in_bundled_app "#{Gem.ruby} #{runner} #{script}", raise_on_error: false
expect(err).to include("Could not find myrack-1.0.0")
end
diff --git a/spec/bundler/runtime/setup_spec.rb b/spec/bundler/runtime/setup_spec.rb
index 106f1edc57..ceb6fcf66a 100644
--- a/spec/bundler/runtime/setup_spec.rb
+++ b/spec/bundler/runtime/setup_spec.rb
@@ -135,7 +135,7 @@ RSpec.describe "Bundler.setup" do
end
it "orders the load path correctly when there are dependencies" do
- bundle "config set path.system true"
+ bundle_config "path.system true"
install_gemfile <<-G
source "https://gem.repo1"
@@ -163,7 +163,7 @@ RSpec.describe "Bundler.setup" do
end
it "falls back to order the load path alphabetically for backwards compatibility" do
- bundle "config set path.system true"
+ bundle_config "path.system true"
install_gemfile <<-G
source "https://gem.repo1"
@@ -286,7 +286,7 @@ RSpec.describe "Bundler.setup" do
G
bundle "install"
- bundle "config set --local deployment true"
+ bundle_config "deployment true"
ENV["BUNDLE_GEMFILE"] = "Gemfile"
ruby <<-R
@@ -303,6 +303,32 @@ RSpec.describe "Bundler.setup" do
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 "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
@@ -458,14 +484,13 @@ RSpec.describe "Bundler.setup" do
end
it "works even when the cache directory has been deleted" do
- bundle "config set --local path vendor/bundle"
bundle :install
- FileUtils.rm_rf vendored_gems("cache")
+ FileUtils.rm_r default_cache_path
expect(the_bundle).to include_gems "myrack 1.0.0"
end
it "does not randomly change the path when specifying --path and the bundle directory becomes read only" do
- bundle "config set --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle :install
with_read_only("#{bundled_app}/**/*") do
@@ -474,7 +499,7 @@ RSpec.describe "Bundler.setup" do
end
it "finds git gem when default bundle path becomes read only" do
- bundle "config set --local path .bundle"
+ bundle_config "path .bundle"
bundle "install"
with_read_only("#{bundled_app(".bundle")}/**/*") do
@@ -497,7 +522,7 @@ RSpec.describe "Bundler.setup" do
bundle %(config set local.myrack #{lib_path("local-myrack")})
bundle :install
- FileUtils.rm_rf(lib_path("local-myrack"))
+ FileUtils.rm_r(lib_path("local-myrack"))
run "require 'myrack'", raise_on_error: false
expect(err).to match(/Cannot use local override for myrack-0.8 because #{Regexp.escape(lib_path("local-myrack").to_s)} does not exist/)
end
@@ -569,7 +594,7 @@ RSpec.describe "Bundler.setup" do
describe "when excluding groups" do
it "doesn't change the resolve if --without is used" do
- bundle "config set --local without rails"
+ bundle_config "without rails"
install_gemfile <<-G
source "https://gem.repo1"
gem "activesupport"
@@ -585,7 +610,7 @@ RSpec.describe "Bundler.setup" do
end
it "remembers --without and does not bail on bare Bundler.setup" do
- bundle "config set --local without rails"
+ bundle_config "without rails"
install_gemfile <<-G
source "https://gem.repo1"
gem "activesupport"
@@ -601,7 +626,7 @@ RSpec.describe "Bundler.setup" do
end
it "remembers --without and does not bail on bare Bundler.setup, even in the case of path gems no longer available" do
- bundle "config set --local without development"
+ bundle_config "without development"
path = bundled_app(File.join("vendor", "foo"))
build_lib "foo", path: path
@@ -612,7 +637,7 @@ RSpec.describe "Bundler.setup" do
gem 'foo', :path => 'vendor/foo', :group => :development
G
- FileUtils.rm_rf(path)
+ FileUtils.rm_r(path)
ruby "require 'bundler'; Bundler.setup", env: { "DEBUG" => "1" }
expect(out).to include("Assuming that source at `vendor/foo` has not changed since fetching its specs errored")
@@ -667,7 +692,7 @@ RSpec.describe "Bundler.setup" do
end
it "remembers --without and does not include groups passed to Bundler.setup" do
- bundle "config set --local without rails"
+ bundle_config "without rails"
install_gemfile <<-G
source "https://gem.repo1"
gem "activesupport"
@@ -729,46 +754,52 @@ RSpec.describe "Bundler.setup" do
G
run <<-R
- File.open(File.join(Gem.dir, "specifications", "broken.gemspec"), "w") do |f|
+ File.open(File.join(Gem.dir, "specifications", "invalid.gemspec"), "w") do |f|
f.write <<-RUBY
# -*- encoding: utf-8 -*-
-# stub: broken 1.0.0 ruby lib
+# stub: invalid 1.0.0 ruby lib
Gem::Specification.new do |s|
- s.name = "broken"
+ s.name = "invalid"
s.version = "1.0.0"
- raise "BROKEN GEMSPEC"
+ s.authors = ["Invalid Author"]
+ s.files = ["lib/invalid.rb"]
+ s.add_dependency "nonexistent-gem", "~> 999.999.999"
+ s.validate!
end
RUBY
end
R
run <<-R
- File.open(File.join(Gem.dir, "specifications", "broken-ext.gemspec"), "w") do |f|
+ File.open(File.join(Gem.dir, "specifications", "invalid-ext.gemspec"), "w") do |f|
f.write <<-RUBY
# -*- encoding: utf-8 -*-
-# stub: broken-ext 1.0.0 ruby lib
+# stub: invalid-ext 1.0.0 ruby lib
# stub: a.ext\\0b.ext
Gem::Specification.new do |s|
- s.name = "broken-ext"
+ s.name = "invalid-ext"
s.version = "1.0.0"
- raise "BROKEN GEMSPEC EXT"
+ s.authors = ["Invalid Author"]
+ s.files = ["lib/invalid.rb"]
+ s.required_ruby_version = "~> 0.8.0"
+ s.validate!
end
RUBY
end
# Need to write the gem.build_complete file,
# otherwise the full spec is loaded to check the installed_by_version
extensions_dir = Gem.default_ext_dir_for(Gem.dir) || File.join(Gem.dir, "extensions", Gem::Platform.local.to_s, Gem.extension_api_version)
- Bundler::FileUtils.mkdir_p(File.join(extensions_dir, "broken-ext-1.0.0"))
- File.open(File.join(extensions_dir, "broken-ext-1.0.0", "gem.build_complete"), "w") {}
+ Bundler::FileUtils.mkdir_p(File.join(extensions_dir, "invalid-ext-1.0.0"))
+ File.open(File.join(extensions_dir, "invalid-ext-1.0.0", "gem.build_complete"), "w") {}
R
run <<-R
- puts "WIN"
+ puts "Success"
R
- expect(out).to eq("WIN")
+ expect(out).to eq("Success")
end
it "ignores empty gem paths" do
@@ -1083,7 +1114,7 @@ end
describe "with system gems in the bundle" do
before :each do
- bundle "config set path.system true"
+ bundle_config "path.system true"
system_gems "myrack-1.0.0"
install_gemfile <<-G
@@ -1152,7 +1183,7 @@ end
bundler_module = class << Bundler; self; end
bundler_module.send(:remove_method, :require)
def Bundler.require(path)
- raise "LOSE"
+ raise StandardError, "didn't use binding from top level"
end
Bundler.load
RUBY
@@ -1197,7 +1228,7 @@ end
end
before do
- bundle "config set --local path.system true"
+ bundle_config "path.system true"
install_gemfile <<-G
source "https://gem.repo1"
@@ -1262,7 +1293,7 @@ end
lock += <<~L
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
L
lock
@@ -1278,7 +1309,12 @@ end
end
context "is not present" do
- it "does not change the lock" do
+ # Skipped on ruby-core because `ruby "require 'bundler/setup'"` does not
+ # activate bundler as a gem there, so Source::Metadata falls back to a
+ # synthetic spec whose cache_file does not exist on disk and
+ # LockfileGenerator#bundler_checksum drops the bundler checksum, while
+ # the on-disk lockfile still has it.
+ it "does not change the lock", :ruby_repo do
expect { ruby "require 'bundler/setup'" }.not_to change { lockfile }
end
end
@@ -1324,7 +1360,7 @@ end
gem "bar", :git => "#{lib_path("bar-1.0")}"
G
- bundle :install
+ bundle :install, env: { "BUNDLE_LOCKFILE_CHECKSUMS" => "false" }
ruby <<-RUBY, artifice: nil
require 'bundler/setup'
@@ -1407,7 +1443,6 @@ end
describe "default gem activation" do
let(:exemptions) do
exempts = %w[did_you_mean bundler uri pathname]
- exempts << "etc" if (Gem.ruby_version < Gem::Version.new("3.2") || Gem.ruby_version >= Gem::Version.new("3.3.2")) && Gem.win_platform?
exempts << "error_highlight" # added in Ruby 3.1 as a default gem
exempts << "ruby2_keywords" # added in Ruby 3.1 as a default gem
exempts << "syntax_suggest" # added in Ruby 3.2 as a default gem
@@ -1466,7 +1501,7 @@ end
install_gemfile "source 'https://gem.repo1'"
create_file("script.rb", "#!/usr/bin/env ruby\n\n#{code}")
FileUtils.chmod(0o777, bundled_app("script.rb"))
- bundle "exec ./script.rb", artifice: nil, env: { "RUBYOPT" => activation_warning_hack_rubyopt }
+ bundle "exec ./script.rb", env: { "RUBYOPT" => activation_warning_hack_rubyopt }
expect(out).to eq("{}")
end
@@ -1482,7 +1517,7 @@ end
gem "net-http-pipeline", "1.0.1"
G
- bundle "config set --local path vendor/bundle"
+ bundle_config "path vendor/bundle"
bundle :install
@@ -1526,22 +1561,7 @@ end
end
describe "after setup" do
- it "allows calling #gem on random objects", bundler: "< 3" do
- install_gemfile <<-G
- source "https://gem.repo1"
- gem "myrack"
- G
-
- ruby <<-RUBY
- require "bundler/setup"
- Object.new.gem "myrack"
- puts Gem.loaded_specs["myrack"].full_name
- RUBY
-
- expect(out).to eq("myrack-1.0.0")
- end
-
- it "keeps Kernel#gem private", bundler: "3" do
+ it "keeps Kernel#gem private" do
install_gemfile <<-G
source "https://gem.repo1"
gem "myrack"
@@ -1553,7 +1573,7 @@ end
puts "FAIL"
RUBY
- expect(last_command.stdboth).not_to include "FAIL"
+ expect(stdboth).not_to include "FAIL"
expect(err).to match(/private method [`']gem'/)
end
@@ -1569,7 +1589,7 @@ end
puts "FAIL"
RUBY
- expect(last_command.stdboth).not_to include "FAIL"
+ expect(stdboth).not_to include "FAIL"
expect(err).to match(/private method [`']require'/)
end
@@ -1648,7 +1668,7 @@ end
gem "myrack", :group => :test
G
- bundle "config set auto_install 1"
+ bundle_config "auto_install 1"
ruby <<-RUBY, artifice: "compact_index"
require 'bundler/setup'
@@ -1656,4 +1676,35 @@ end
expect(err).to be_empty
expect(out).to include("Installing myrack 1.0.0")
end
+
+ context "in a read-only filesystem" do
+ before do
+ gemfile <<-G
+ source "https://gem.repo4"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: https://gem.repo4/
+
+ PLATFORMS
+ x86_64-darwin-19
+
+ DEPENDENCIES
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "should fail loudly if the lockfile platforms don't include the current platform" do
+ simulate_platform "x86_64-linux" do
+ ruby <<-RUBY, raise_on_error: false, env: { "BUNDLER_SPEC_READ_ONLY" => "true", "BUNDLER_FORCE_TTY" => "true" }
+ require "bundler/setup"
+ RUBY
+ end
+
+ expect(err).to include("Your lockfile is missing the current platform, but can't be updated because file system is read-only")
+ end
+ end
end
diff --git a/spec/bundler/spec_helper.rb b/spec/bundler/spec_helper.rb
index bf7604659c..27ddc6a771 100644
--- a/spec/bundler/spec_helper.rb
+++ b/spec/bundler/spec_helper.rb
@@ -9,18 +9,26 @@ if File.expand_path(__FILE__) =~ %r{([^\w/\.:\-])}
abort "The bundler specs cannot be run from a path that contains special characters (particularly #{$1.inspect})"
end
-# Bundler CLI will have different help text depending on whether this variable
-# is set, since the `-e` flag `bundle gem` with require an explicit value if
-# `EDITOR` is not set, but will use `EDITOR` by default is set. So make sure
-# it's `nil` before loading bundler to get a consistent help text, since some
-# tests rely on that.
+# Bundler CLI will have different help text depending on whether any of these
+# variables is set, since the `-e` flag `bundle gem` with require an explicit
+# value if they are not set, but will use their value by default if set. So make
+# sure they are `nil` before loading bundler to get a consistent help text,
+# since some tests rely on that.
ENV["EDITOR"] = nil
+ENV["VISUAL"] = nil
+ENV["BUNDLER_EDITOR"] = nil
require "bundler"
+# If we use shared GEM_HOME and install multiple versions, it may cause
+# unexpected test failures.
+gem "diff-lcs", "< 2.0"
+
require "rspec/core"
require "rspec/expectations"
require "rspec/mocks"
require "rspec/support/differ"
+gem "rubygems-generate_index"
+require "rubygems/indexer"
require_relative "support/builders"
require_relative "support/checksums"
@@ -30,6 +38,38 @@ require_relative "support/indexes"
require_relative "support/matchers"
require_relative "support/permissions"
require_relative "support/platforms"
+require_relative "support/shards"
+
+begin
+ raise LoadError if File.exist?(File.expand_path("../../lib/bundler/bundler.gemspec", __dir__))
+
+ gem "simplecov_json_formatter"
+ require "simplecov"
+
+ SimpleCov.start do
+ command_name "bundler:#{Process.pid}"
+ root File.expand_path("../bundler", __dir__)
+ coverage_dir File.expand_path("../coverage", __dir__)
+
+ add_filter "/spec/"
+ add_filter "/test/"
+ add_filter "/lib/rubygems/"
+ add_filter "/lib/bundler/vendor/"
+ add_filter "/tool/"
+ add_filter "/tmp/"
+ add_filter ".gemspec"
+ end
+
+ SimpleCov.print_error_status = false
+ SimpleCov.at_exit do
+ $stdout = File.open(File::NULL, "w")
+ SimpleCov.result.format!
+ ensure
+ $stdout = STDOUT
+ end
+rescue LoadError
+ # SimpleCov is not installed
+end
$debug = false
@@ -48,6 +88,7 @@ RSpec.configure do |config|
config.include Spec::Path
config.include Spec::Platforms
config.include Spec::Permissions
+ config.include Spec::Shards
# Enable flags like --only-failures and --next-failure
config.example_status_persistence_file_path = ".rspec_status"
@@ -82,28 +123,42 @@ RSpec.configure do |config|
require_relative "support/rubygems_ext"
Spec::Rubygems.test_setup
+
+ # Disable retry delays in tests to speed them up
+ Bundler::Retry.default_base_delay = 0
+
+ # Simulate bundler has not yet been loaded
+ ENV.replace(ENV.to_hash.delete_if {|k, _v| k.start_with?(Bundler::EnvironmentPreserver::BUNDLER_PREFIX) })
+
ENV["BUNDLER_SPEC_RUN"] = "true"
ENV["BUNDLE_USER_CONFIG"] = ENV["BUNDLE_USER_CACHE"] = ENV["BUNDLE_USER_PLUGIN"] = nil
ENV["BUNDLE_APP_CONFIG"] = nil
ENV["BUNDLE_SILENCE_ROOT_WARNING"] = nil
ENV["RUBYGEMS_GEMDEPS"] = nil
ENV["XDG_CONFIG_HOME"] = nil
+ ENV["XDG_CACHE_HOME"] = nil
ENV["GEMRC"] = nil
+ # Prevent tests from modifying the user's global git config.
+ # GIT_CONFIG_GLOBAL and GIT_CONFIG_NOSYSTEM are available since Git 2.32.
+ git_version = `git --version`[/(\d+\.\d+\.\d+)/, 1]
+ if Gem::Version.new(git_version) >= Gem::Version.new("2.32")
+ ENV["GIT_CONFIG_GLOBAL"] = File.join(ENV["HOME"], ".gitconfig")
+ ENV["GIT_CONFIG_NOSYSTEM"] = "1"
+ end
+
# Don't wrap output in tests
ENV["THOR_COLUMNS"] = "10000"
extend(Spec::Builders)
- check_test_gems!
-
build_repo1
- reset_paths!
+ reset!
end
config.around :each do |example|
- FileUtils.cp_r pristine_system_gem_path, system_gem_path
+ default_system_gems
with_gem_path_as(system_gem_path) do
Bundler.ui.silence { example.run }
@@ -120,7 +175,18 @@ RSpec.configure do |config|
reset!
end
- config.after :suite do
- FileUtils.rm_rf Spec::Path.pristine_system_gem_path
+ Spec::Shards::EXAMPLE_MAPPINGS.each do |tag, file_paths|
+ file_pattern = Regexp.union(file_paths.map {|path| Regexp.new(Regexp.escape(path) + "$") })
+
+ config.define_derived_metadata(file_path: file_pattern) do |metadata|
+ metadata[tag] = true
+ end
end
+
+ config.before(:context) do |example|
+ metadata = example.class.metadata
+ if metadata[:type] != :aruba && !metadata[:realworld] && metadata.keys.none? {|k| Spec::Shards::EXAMPLE_MAPPINGS.keys.include?(k) }
+ warn "#{metadata[:file_path]} is not assigned to any shard. see spec/support/shards.rb for details."
+ end
+ end unless Spec::Path.ruby_core?
end
diff --git a/spec/bundler/support/artifice/compact_index_cooldown.rb b/spec/bundler/support/artifice/compact_index_cooldown.rb
new file mode 100644
index 0000000000..85e3173c98
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_cooldown.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+require_relative "helpers/compact_index_cooldown"
+require_relative "helpers/artifice"
+
+Artifice.activate_with(CompactIndexCooldownAPI)
diff --git a/spec/bundler/support/artifice/compact_index_etag_match.rb b/spec/bundler/support/artifice/compact_index_etag_match.rb
index 08d7b5ec53..6c62166051 100644
--- a/spec/bundler/support/artifice/compact_index_etag_match.rb
+++ b/spec/bundler/support/artifice/compact_index_etag_match.rb
@@ -4,7 +4,7 @@ require_relative "helpers/compact_index"
class CompactIndexEtagMatch < CompactIndexAPI
get "/versions" do
- raise "ETag header should be present" unless env["HTTP_IF_NONE_MATCH"]
+ raise ArgumentError, "ETag header should be present" unless env["HTTP_IF_NONE_MATCH"]
headers "ETag" => env["HTTP_IF_NONE_MATCH"]
status 304
body ""
diff --git a/spec/bundler/support/artifice/compact_index_mirror_down.rb b/spec/bundler/support/artifice/compact_index_mirror_down.rb
new file mode 100644
index 0000000000..88983c715d
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_mirror_down.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require_relative "helpers/compact_index"
+require_relative "helpers/artifice"
+require_relative "helpers/rack_request"
+
+module Artifice
+ module Net
+ class HTTPMirrorDown < HTTP
+ def connect
+ raise SocketError if address == "gem.mirror"
+
+ super
+ end
+ end
+
+ HTTP.endpoint = CompactIndexAPI
+ end
+
+ replace_net_http(Net::HTTPMirrorDown)
+end
diff --git a/spec/bundler/support/artifice/compact_index_no_checksums.rb b/spec/bundler/support/artifice/compact_index_no_checksums.rb
new file mode 100644
index 0000000000..ecb7fc7d7c
--- /dev/null
+++ b/spec/bundler/support/artifice/compact_index_no_checksums.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require_relative "helpers/compact_index"
+
+class CompactIndexNoChecksums < CompactIndexAPI
+ get "/info/:name" do
+ etag_response do
+ gem = gems.find {|g| g.name == params[:name] }
+ gem.versions.map(&:number).join("\n")
+ end
+ end
+end
+
+require_relative "helpers/artifice"
+
+Artifice.activate_with(CompactIndexNoChecksums)
diff --git a/spec/bundler/support/artifice/helpers/compact_index.rb b/spec/bundler/support/artifice/helpers/compact_index.rb
index ba331e483f..e684aa8628 100644
--- a/spec/bundler/support/artifice/helpers/compact_index.rb
+++ b/spec/bundler/support/artifice/helpers/compact_index.rb
@@ -2,7 +2,7 @@
require_relative "endpoint"
-$LOAD_PATH.unshift Dir[Spec::Path.base_system_gem_path.join("gems/compact_index*/lib")].first.to_s
+$LOAD_PATH.unshift Spec::Path.tmp_root.join("compact_index/lib").to_s
require "compact_index"
require "digest"
@@ -90,13 +90,17 @@ class CompactIndexAPI < Endpoint
rescue StandardError
checksum = nil
end
- CompactIndex::GemVersion.new(spec.version.version, spec.platform.to_s, checksum, nil,
- deps, spec.required_ruby_version.to_s, spec.required_rubygems_version.to_s)
+ build_gem_version(spec, deps, checksum)
end
CompactIndex::Gem.new(name, gem_versions)
end
end
end
+
+ def build_gem_version(spec, deps, checksum)
+ CompactIndex::GemVersion.new(spec.version.version, spec.platform.to_s, checksum, nil,
+ deps, spec.required_ruby_version.to_s, spec.required_rubygems_version.to_s)
+ end
end
get "/names" do
diff --git a/spec/bundler/support/artifice/helpers/compact_index_cooldown.rb b/spec/bundler/support/artifice/helpers/compact_index_cooldown.rb
new file mode 100644
index 0000000000..9920fd2c95
--- /dev/null
+++ b/spec/bundler/support/artifice/helpers/compact_index_cooldown.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require_relative "compact_index"
+
+class CompactIndexCooldownAPI < CompactIndexAPI
+ helpers do
+ def build_gem_version(spec, deps, checksum)
+ created_at = spec.date&.utc&.iso8601
+ CompactIndex::GemVersionV2.new(spec.version.version, spec.platform.to_s, checksum, nil,
+ deps, spec.required_ruby_version.to_s, spec.required_rubygems_version.to_s, created_at)
+ end
+ end
+end
diff --git a/spec/bundler/support/artifice/helpers/endpoint.rb b/spec/bundler/support/artifice/helpers/endpoint.rb
index 1ceadb5900..9590611dfe 100644
--- a/spec/bundler/support/artifice/helpers/endpoint.rb
+++ b/spec/bundler/support/artifice/helpers/endpoint.rb
@@ -27,7 +27,7 @@ class Endpoint < Sinatra::Base
set :raise_errors, true
set :show_exceptions, false
- set :host_authorization, permitted_hosts: [".example.org", ".local", ".repo", ".repo1", ".repo2", ".repo3", ".repo4", ".rubygems.org", ".security", ".source", ".test", "127.0.0.1"]
+ set :host_authorization, permitted_hosts: [".example.org", ".local", ".mirror", ".repo", ".repo1", ".repo2", ".repo3", ".repo4", ".rubygems.org", ".security", ".source", ".test", "127.0.0.1"]
def call!(*)
super.tap do
diff --git a/spec/bundler/support/artifice/vcr.rb b/spec/bundler/support/artifice/vcr.rb
index 2386a4c6b7..0bf5ade8f6 100644
--- a/spec/bundler/support/artifice/vcr.rb
+++ b/spec/bundler/support/artifice/vcr.rb
@@ -24,7 +24,7 @@ class BundlerVCRHTTP < Gem::Net::HTTP
end
File.open(USED_CASSETTES_PATH, "a+") do |f|
- f.puts request_pair_paths.map {|path| Pathname.new(path).relative_path_from(Spec::Path.source_root).to_s }.join("\n")
+ f.puts request_pair_paths.map {|path| Pathname.new(path).relative_path_from(Spec::Path.git_root).to_s }.join("\n")
end
if recorded_response?
diff --git a/spec/bundler/support/build_metadata.rb b/spec/bundler/support/build_metadata.rb
index 189100edb7..2eade4137b 100644
--- a/spec/bundler/support/build_metadata.rb
+++ b/spec/bundler/support/build_metadata.rb
@@ -8,11 +8,10 @@ module Spec
include Spec::Path
include Spec::Helpers
- def write_build_metadata(dir: source_root)
+ def write_build_metadata(dir: source_root, version: Bundler::VERSION)
build_metadata = {
git_commit_sha: git_commit_sha,
- built_at: loaded_gemspec.date.utc.strftime("%Y-%m-%d"),
- release: true,
+ built_at: release_date_for(version, dir: dir),
}
replace_build_metadata(build_metadata, dir: dir)
@@ -20,7 +19,7 @@ module Spec
def reset_build_metadata(dir: source_root)
build_metadata = {
- release: false,
+ built_at: nil,
}
replace_build_metadata(build_metadata, dir: dir)
@@ -44,6 +43,11 @@ module Spec
ruby_core_tarball? ? "unknown" : git("rev-parse --short HEAD", source_root).strip
end
+ def release_date_for(version, dir:)
+ changelog = File.expand_path("CHANGELOG.md", dir)
+ File.readlines(changelog)[2].scan(/^## #{Regexp.escape(version)} \((.*)\)/).first&.first if File.exist?(changelog)
+ end
+
extend self
end
end
diff --git a/spec/bundler/support/builders.rb b/spec/bundler/support/builders.rb
index 340b3f5dd9..43ab7e053d 100644
--- a/spec/bundler/support/builders.rb
+++ b/spec/bundler/support/builders.rb
@@ -2,6 +2,8 @@
require "bundler/shared_helpers"
require "shellwords"
+require "fileutils"
+require "rubygems/package"
require_relative "build_metadata"
@@ -24,10 +26,6 @@ module Spec
Gem::Platform.new(platform)
end
- def rake_version
- "13.2.1"
- end
-
def build_repo1
build_repo gem_repo1 do
FileUtils.cp rake_path, "#{gem_repo1}/gems/"
@@ -111,11 +109,11 @@ module Spec
end
build_gem "platform_specific" do |s|
- s.platform = "x64-mingw32"
+ s.platform = "x64-mingw-ucrt"
end
build_gem "platform_specific" do |s|
- s.platform = "x64-mingw-ucrt"
+ s.platform = "aarch64-mingw-ucrt"
end
build_gem "platform_specific" do |s|
@@ -183,32 +181,41 @@ module Spec
end
def build_repo2(**kwargs, &blk)
- FileUtils.rm_rf gem_repo2
- FileUtils.cp_r gem_repo1, gem_repo2
+ FileUtils.cp_r gem_repo1, gem_repo2, remove_destination: true
update_repo2(**kwargs, &blk) if block_given?
end
# A repo that has no pre-installed gems included. (The caller completely
# determines the contents with the block.)
+ #
+ # If the repo already exists, `#update_repo` will be called.
def build_repo3(**kwargs, &blk)
- build_empty_repo gem_repo3, **kwargs, &blk
+ if File.exist?(gem_repo3)
+ update_repo(gem_repo3, &blk)
+ else
+ build_repo gem_repo3, **kwargs, &blk
+ end
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.
+ #
+ # If the repo already exists, `#udpate_repo` will be called
def build_repo4(**kwargs, &blk)
- build_empty_repo gem_repo4, **kwargs, &blk
- end
-
- def update_repo4(&blk)
- update_repo(gem_repo4, &blk)
+ if File.exist?(gem_repo4)
+ update_repo gem_repo4, &blk
+ else
+ build_repo gem_repo4, **kwargs, &blk
+ end
end
def update_repo2(**kwargs, &blk)
update_repo(gem_repo2, **kwargs, &blk)
end
+ def update_repo3(&blk)
+ update_repo(gem_repo3, &blk)
+ end
+
def build_security_repo
build_repo security_repo do
build_gem "myrack"
@@ -224,6 +231,41 @@ module Spec
end
end
+ # A minimal fake irb console
+ def build_dummy_irb(version = "9.9.9")
+ build_gem "irb", version do |s|
+ s.write "lib/irb.rb", <<-RUBY
+ class IRB
+ class << self
+ def toplevel_binding
+ unless defined?(@toplevel_binding) && @toplevel_binding
+ TOPLEVEL_BINDING.eval %{
+ def self.__irb__; binding; end
+ IRB.instance_variable_set(:@toplevel_binding, __irb__)
+ class << self; undef __irb__; end
+ }
+ end
+ @toplevel_binding.eval('private')
+ @toplevel_binding
+ end
+
+ def __irb__
+ while line = gets
+ begin
+ puts eval(line, toplevel_binding).inspect.sub(/^"(.*)"$/, '=> \\1')
+ rescue Exception => e
+ puts "\#{e.class}: \#{e.message}"
+ puts e.backtrace.first
+ end
+ end
+ end
+ alias start __irb__
+ end
+ end
+ RUBY
+ end
+ end
+
def build_repo(path, **kwargs, &blk)
return if File.directory?(path)
@@ -232,17 +274,8 @@ module Spec
update_repo(path,**kwargs, &blk)
end
- def check_test_gems!
- if rake_path.nil?
- FileUtils.rm_rf(base_system_gems)
- Spec::Rubygems.install_test_deps
- end
-
- Helpers.install_dev_bundler unless pristine_system_gem_path.exist?
- end
-
def update_repo(path, build_compact_index: true)
- exempted_caller = Gem.ruby_version >= Gem::Version.new("3.4.0.dev") ? "#{Module.nesting.first}#build_repo" : "build_repo"
+ exempted_caller = Gem.ruby_version >= Gem::Version.new("3.4.0.dev") && RUBY_ENGINE != "jruby" ? "#{Module.nesting.first}#build_repo" : "build_repo"
if path == gem_repo1 && caller_locations(1, 1).first.label != exempted_caller
raise "Updating gem_repo1 is unsupported -- use gem_repo2 instead"
end
@@ -250,14 +283,8 @@ module Spec
@_build_path = "#{path}/gems"
@_build_repo = File.basename(path)
yield
- with_gem_path_as base_system_gem_path do
- Dir[base_system_gem_path.join("gems/rubygems-generate_index*/lib")].first ||
- raise("Could not find rubygems-generate_index lib directory in #{base_system_gem_path}")
-
- command = "generate_index"
- command += " --no-compact" if !build_compact_index && gem_command(command + " --help").include?("--[no-]compact")
- gem_command command, dir: path
- end
+ options = { build_compact: build_compact_index }
+ Gem::Indexer.new(path, options).generate_index
ensure
@_build_path = nil
@_build_repo = nil
@@ -313,11 +340,6 @@ 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
@@ -401,18 +423,23 @@ module Spec
end
class BundlerBuilder
- attr_writer :required_ruby_version
-
def initialize(context, name, version)
- raise "can only build bundler" unless name == "bundler"
-
@context = context
- @version = version || Bundler::VERSION
+ @spec = Spec::Path.loaded_gemspec.dup
+ @spec.version = version || Bundler::VERSION
+ end
+
+ def required_ruby_version
+ @spec.required_ruby_version
+ end
+
+ def required_ruby_version=(x)
+ @spec.required_ruby_version = x
end
def _build(options = {})
- full_name = "bundler-#{@version}"
- build_path = @context.tmp + full_name
+ full_name = "bundler-#{@spec.version}"
+ build_path = (options[:build_path] || @context.tmp) + full_name
bundler_path = build_path + "#{full_name}.gem"
FileUtils.mkdir_p build_path
@@ -423,15 +450,19 @@ module Spec
target_shipped_file = build_path + target_shipped_file
target_shipped_dir = File.dirname(target_shipped_file)
FileUtils.mkdir_p target_shipped_dir unless File.directory?(target_shipped_dir)
- FileUtils.cp shipped_file, target_shipped_file, preserve: true
+ FileUtils.cp File.expand_path(shipped_file, @context.source_root), target_shipped_file, preserve: true
end
- @context.replace_version_file(@version, dir: build_path)
- @context.replace_required_ruby_version(@required_ruby_version, dir: build_path) if @required_ruby_version
+ @context.replace_version_file(@spec.version, dir: build_path)
+ @context.replace_changelog(@spec.version, dir: build_path) if options[:released]
- Spec::BuildMetadata.write_build_metadata(dir: build_path)
+ Spec::BuildMetadata.write_build_metadata(dir: build_path, version: @spec.version.to_s)
- @context.gem_command "build #{@context.relative_gemspec}", dir: build_path
+ Dir.chdir build_path do
+ Gem::DefaultUserInteraction.use_ui(Gem::SilentUI.new) do
+ Gem::Package.build(@spec)
+ end
+ end
if block_given?
yield(bundler_path)
@@ -439,7 +470,7 @@ module Spec
FileUtils.mv bundler_path, options[:path]
end
ensure
- build_path.rmtree
+ FileUtils.rm_rf build_path
end
end
@@ -526,10 +557,8 @@ module Spec
when false
# do nothing
when :yaml
- @spec.files << "#{name}.gemspec"
@files["#{name}.gemspec"] = @spec.to_yaml
else
- @spec.files << "#{name}.gemspec"
@files["#{name}.gemspec"] = @spec.to_ruby
end
@@ -630,14 +659,14 @@ module Spec
destination = opts[:path] || _default_path
FileUtils.mkdir_p(lib_path.join(destination))
- if opts[:gemspec] == :yaml || opts[:gemspec] == false
+ if [:yaml, false].include?(opts[:gemspec])
Dir.chdir(lib_path) do
Bundler.rubygems.build(@spec, opts[:skip_validation])
end
elsif opts[:skip_validation]
- @context.gem_command "build --force #{@spec.name}", dir: lib_path
+ Dir.chdir(lib_path) { Gem::Package.build(@spec, true) }
else
- @context.gem_command "build #{@spec.name}", dir: lib_path
+ Dir.chdir(lib_path) { Gem::Package.build(@spec) }
end
gem_path = File.expand_path("#{@spec.full_name}.gem", lib_path)
@@ -666,54 +695,54 @@ module Spec
TEST_CERT = <<~CERT
-----BEGIN CERTIFICATE-----
- MIIDMjCCAhqgAwIBAgIBATANBgkqhkiG9w0BAQUFADAnMQwwCgYDVQQDDAN5b3Ux
+ MIIDNTCCAh2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAnMQwwCgYDVQQDDAN5b3Ux
FzAVBgoJkiaJk/IsZAEZFgdleGFtcGxlMB4XDTE1MDIwODAwMTIyM1oXDTQyMDYy
NTAwMTIyM1owJzEMMAoGA1UEAwwDeW91MRcwFQYKCZImiZPyLGQBGRYHZXhhbXBs
- ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANlvFdpN43c4DMS9Jo06
- m0a7k3bQ3HWQ1yrYhZMi77F1F73NpBknYHIzDktQpGn6hs/4QFJT4m4zNEBF47UL
- jHU5nTK5rjkS3niGYUjvh3ZEzVeo9zHUlD/UwflDo4ALl3TSo2KY/KdPS/UTdLXL
- ajkQvaVJtEDgBPE3DPhlj5whp+Ik3mDHej7qpV6F502leAwYaFyOtlEG/ZGNG+nZ
- L0clH0j77HpP42AylHDi+vakEM3xcjo9BeWQ6Vkboic93c9RTt6CWBWxMQP7Nol1
- MOebz9XOSQclxpxWteXNfPRtMdAhmRl76SMI8ywzThNPpa4EH/yz34ftebVOgKyM
- nd0CAwEAAaNpMGcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFA7D
- n9qo0np23qi3aOYuAAPn/5IdMBYGA1UdEQQPMA2BC3lvdUBleGFtcGxlMBYGA1Ud
- EgQPMA2BC3lvdUBleGFtcGxlMA0GCSqGSIb3DQEBBQUAA4IBAQA7Gyk62sWOUX/N
- vk4tJrgKESph6Ns8+E36A7n3jt8zCep8ldzMvwTWquf9iqhsC68FilEoaDnUlWw7
- d6oNuaFkv7zfrWGLlvqQJC+cu2X5EpcCksg5oRp8VNbwJysJ6JgwosxzROII8eXc
- R+j1j6mDvQYqig2QOnzf480pjaqbP+tspfDFZbhKPrgM3Blrb3ZYuFpv4zkqI7aB
- 6fuk2DUhNO1CuwrJA84TqC+jGo73bDKaT5hrIDiaJRrN5+zcWja2uEWrj5jSbep4
- oXdEdyH73hOHMBP40uds3PqnUsxEJhzjB2sCCe1geV24kw9J4m7EQXPVkUKDgKrt
- LlpDmOoo
+ ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMkupYkg3Nd1oXM3fo0d
+ mVJBWNrni88lKDuIIQXwcKe6XCgiloZG708ecLTOws9+o9MkTl9Wtpf/WGXT98NK
+ EPUYakd2Fv1SuD1jWYlP7iDR6hB3RkWBm5ziujYftVJ4ZrPD42PLjDASvlh75Tvr
+ MeM7yq/qkcgNsd9dQyUvMNPks3tla9je7Dt7Auli2IN3CNXys7gIOfwJH0Bb/M6t
+ y7oUfpoUKAfLzwe61abztgDu1lSNgdFBM1kcxYflyh/FkX5TlAcWeAXzLrnxAXGR
+ UxXrxW4oPC+kZi/pDRBd7X4zQDx7bCmr1+FsS3M05i3w5E08Tt9iKRk4V8nCmE4i
+ k6UCAwEAAaNsMGowCQYDVR0TBAIwADAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0OBBYE
+ FOOOFw5TNAqt/TcRRZEU3Dg/58XuMBYGA1UdEQQPMA2BC3lvdUBleGFtcGxlMBYG
+ A1UdEgQPMA2BC3lvdUBleGFtcGxlMA0GCSqGSIb3DQEBCwUAA4IBAQAy3xnmobxU
+ 1SyhHvoIXTJmG0wt1DQ/Dqwjy362LpEf1UHt29wtg1Mph58eVtl93z5Vd2t4/O77
+ E2BHpSu9ujc6/Br4+2uA/Qk/xRyLBtZAwty6J4uFvOOg985HonN+RCUZbKSUTmtA
+ TZvNtIDAZFQ8Tu75K4gIBxDcz7biGi4i1VJ3F3GNCNeossr9IQwKvb+UWFq14U5R
+ IzUnGgMIzcjUG2kKQvddRD1CjS+egtcLvShbOfm5bs4w4rfQ2FPF+Aaf9v7fxa/c
+ Jrf3K+cB19eAy7O4nlPG1xurvnZd0QpqRk++werrBuKe1Pgga7YBLePfJhzwqcZv
+ wVOSsB870yeO
-----END CERTIFICATE-----
CERT
TEST_PKEY = <<~PKEY
-----BEGIN RSA PRIVATE KEY-----
- MIIEowIBAAKCAQEA2W8V2k3jdzgMxL0mjTqbRruTdtDcdZDXKtiFkyLvsXUXvc2k
- GSdgcjMOS1CkafqGz/hAUlPibjM0QEXjtQuMdTmdMrmuORLeeIZhSO+HdkTNV6j3
- MdSUP9TB+UOjgAuXdNKjYpj8p09L9RN0tctqORC9pUm0QOAE8TcM+GWPnCGn4iTe
- YMd6PuqlXoXnTaV4DBhoXI62UQb9kY0b6dkvRyUfSPvsek/jYDKUcOL69qQQzfFy
- Oj0F5ZDpWRuiJz3dz1FO3oJYFbExA/s2iXUw55vP1c5JByXGnFa15c189G0x0CGZ
- GXvpIwjzLDNOE0+lrgQf/LPfh+15tU6ArIyd3QIDAQABAoIBACbDqz20TS1gDMa2
- gj0DidNedbflHKjJHdNBru7Ad8NHgOgR1YO2hXdWquG6itVqGMbTF4SV9/R1pIcg
- 7qvEV1I+50u31tvOBWOvcYCzU48+TO2n7gowQA3xPHPYHzog1uu48fAOHl0lwgD7
- av9OOK3b0jO5pC08wyTOD73pPWU0NrkTh2+N364leIi1pNuI1z4V+nEuIIm7XpVd
- 5V4sXidMTiEMJwE6baEDfTjHKaoRndXrrPo3ryIXmcX7Ag1SwAQwF5fBCRToCgIx
- dszEZB1bJD5gA6r+eGnJLB/F60nK607az5o3EdguoB2LKa6q6krpaRCmZU5svvoF
- J7xgBPECgYEA8RIzHAQ3zbaibKdnllBLIgsqGdSzebTLKheFuigRotEV3Or/z5Lg
- k/nVnThWVkTOSRqXTNpJAME6a4KTdcVSxYP+SdZVO1esazHrGb7xPVb7MWSE1cqp
- WEk3Yy8OUOPoPQMc4dyGzd30Mi8IBB6gnFIYOTrpUo0XtkBv8rGGhfsCgYEA5uYn
- 6QgL4NqNT84IXylmMb5ia3iBt6lhxI/A28CDtQvfScl4eYK0IjBwdfG6E1vJgyzg
- nJzv3xEVo9bz+Kq7CcThWpK5JQaPnsV0Q74Wjk0ShHet15txOdJuKImnh5F6lylC
- GTLR9gnptytfMH/uuw4ws0Q2kcg4l5NHKOWOnAcCgYEAvAwIVkhsB0n59Wu4gCZu
- FUZENxYWUk/XUyQ6KnZrG2ih90xQ8+iMyqFOIm/52R2fFKNrdoWoALC6E3ct8+ZS
- pMRLrelFXx8K3it4SwMJR2H8XBEfFW4bH0UtsW7Zafv+AunUs9LETP5gKG1LgXsq
- qgXX43yy2LQ61O365YPZfdUCgYBVbTvA3MhARbvYldrFEnUL3GtfZbNgdxuD9Mee
- xig0eJMBIrgfBLuOlqtVB70XYnM4xAbKCso4loKSHnofO1N99siFkRlM2JOUY2tz
- kMWZmmxKdFjuF0WZ5f/5oYxI/QsFGC+rUQEbbWl56mMKd5qkvEhKWudxoklF0yiV
- ufC8SwKBgDWb8iWqWN5a/kfvKoxFcDM74UHk/SeKMGAL+ujKLf58F+CbweM5pX9C
- EUsxeoUEraVWTiyFVNqD81rCdceus9TdBj0ZIK1vUttaRZyrMAwF0uQSfjtxsOpd
- l69BkyvzjgDPkmOHVGiSZDLi3YDvypbUpo6LOy4v5rVg5U2F/A0v
+ MIIEowIBAAKCAQEAyS6liSDc13Whczd+jR2ZUkFY2ueLzyUoO4ghBfBwp7pcKCKW
+ hkbvTx5wtM7Cz36j0yROX1a2l/9YZdP3w0oQ9RhqR3YW/VK4PWNZiU/uINHqEHdG
+ RYGbnOK6Nh+1Unhms8PjY8uMMBK+WHvlO+sx4zvKr+qRyA2x311DJS8w0+Sze2Vr
+ 2N7sO3sC6WLYg3cI1fKzuAg5/AkfQFv8zq3LuhR+mhQoB8vPB7rVpvO2AO7WVI2B
+ 0UEzWRzFh+XKH8WRflOUBxZ4BfMuufEBcZFTFevFbig8L6RmL+kNEF3tfjNAPHts
+ KavX4WxLczTmLfDkTTxO32IpGThXycKYTiKTpQIDAQABAoIBABpyrHEWRed5X7aN
+ kXCBzKSN/LLChT8VNnB6bppLnV501yVbmV2hDlg2EJZkfCMvwIptwnPcKs2uqZ4G
+ u2gMC6X9Bgkg/YK4u4nZJBiIzoMNYEUL48wYGYS1dcokaapO3nQ8M1+XjyAexrFL
+ 5btL1IIisScRTQWiGe6FtzcN43sSNkBISyDF5zG4Kodynqi0ekITmMl2q5XLWcsM
+ KBnmZcRFEmFae2YYczVy8SXNApkZEvN69znvAX1iDNnZ3sJFchXo1nRPt4stOOKw
+ mydgIYqaNQ22aF3OkblvoA4Y4m+X2Qt1sfkryKa5xTT7DSE81GmmazNI64EWqtES
+ 6Xde6P0CgYEA+V1vuSnE5fWX188abWMbVwNMC71WfHbntFmI+qwWYPEpickm+RGX
+ DDfXs5unlVX4KUmjfplgavO29op1GZTuD9TlRnUAV0+0aJnNq4DY6XsHfD84qsBr
+ gQGEHeJ1cMGNDnZR/EV3eudMalj9Qjpx9NoXNzMykb0/SUYZQemiqwcCgYEAzokC
+ s0GoHVJqan4dfU0h0G5QPncrajW9DGG1ySxK/A2eqbVB8W2ZQx39OS26/Gydb31p
+ cR7zm8PZpNbzLqlIMEbD4F6q22xxvYVtDx/HHPjxHMi87yxwQ9uLDUHoMa/LciTO
+ djv3D1xTDDGxbpjmsdmINetunAs3htxku7JY5PMCgYBs3/TVvXzwgmhHm28Ib4sS
+ VKgxP/uw4CGORsFd4SDsNp9SP3c6rAltFjyheMaUlzKApFwz/DdyuvIZdp5mCvZe
+ BzALsS3y8SPtv6lixiDu3/6GqvvM4bKOYuESQzvPfVJfDB4DrTjben2MuUnqTqZO
+ p6IXQc1EgIJPNcH1W1LgpQKBgAKZlPAevngIBpDqn4JpSyititMOevxuSr/yJvCu
+ Xw9HOJ0YTAk3APvoT7y9h6IP1/eEU6R56EUotP+vOQZ4WRFKgsK7TllOxyvElzfe
+ hYom1BoxqLc2Dv+7rsdu8fZWKTB5qCOy44xM9DquEXa79AN/IojTOuQ5++v1sErw
+ ls/jAoGBANneGe9ogN51mYkrLyg1fhU1i24gFRq+sPGEvsCUoE6Vjw/lawQQ80T8
+ v45TFqvhoGpgznqy3qxDJyguquZg6HN2yW6HE2Dvk7uk3XogcjdXgNDmWqb2j0eE
+ z9pKzHCqfwNVPuYf44Znyo2YeyZ2kHn42MU73oXuFshUs3QHcH+P
-----END RSA PRIVATE KEY-----
PKEY
end
diff --git a/spec/bundler/support/bundle b/spec/bundler/support/bundle
new file mode 100755
index 0000000000..8f8b535295
--- /dev/null
+++ b/spec/bundler/support/bundle
@@ -0,0 +1,6 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+require_relative "../bundler/support/activate"
+
+load File.expand_path("bundle", Spec::Path.exedir)
diff --git a/spec/bundler/support/bundle.rb b/spec/bundler/support/bundle.rb
index 5d6d658040..aa7b121706 100644
--- a/spec/bundler/support/bundle.rb
+++ b/spec/bundler/support/bundle.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
-require_relative "activate"
+require_relative "path"
-load File.expand_path("bundle", Spec::Path.bindir)
+warn "#{__FILE__} is deprecated. Please use #{Spec::Path.dev_binstub} instead"
+
+load Spec::Path.dev_binstub
diff --git a/spec/bundler/support/checksums.rb b/spec/bundler/support/checksums.rb
index f3aa13ca9f..7b69bba668 100644
--- a/spec/bundler/support/checksums.rb
+++ b/spec/bundler/support/checksums.rb
@@ -3,6 +3,8 @@
module Spec
module Checksums
class ChecksumsBuilder
+ attr_reader :bundler_registered
+
def initialize(enabled = true, &block)
@enabled = enabled
@checksums = {}
@@ -14,9 +16,11 @@ module Spec
@checksums = @checksums.dup
end
- def checksum(repo, name, version, platform = Gem::Platform::RUBY)
+ def checksum(repo, name, version, platform = Gem::Platform::RUBY, folder = "gems")
+ @bundler_registered = true if name == "bundler"
+
name_tuple = Gem::NameTuple.new(name, version, platform)
- gem_file = File.join(repo, "gems", "#{name_tuple.full_name}.gem")
+ gem_file = File.join(repo, folder, "#{name_tuple.full_name}.gem")
File.open(gem_file, "rb") do |f|
register(name_tuple, Bundler::Checksum.from_gem(f, "#{gem_file} (via ChecksumsBuilder#checksum)"))
end
@@ -50,21 +54,26 @@ module Spec
end
end
- def checksums_section(enabled = true, &block)
- ChecksumsBuilder.new(enabled, &block)
+ def checksums_section(enabled = true, bundler_checksum: true, &block)
+ ChecksumsBuilder.new(enabled, &block).tap do |builder|
+ next if builder.bundler_registered || !bundler_checksum
+
+ next if Bundler::VERSION.to_s.end_with?(".dev")
+ builder.checksum(system_gem_path, "bundler", Bundler::VERSION, Gem::Platform::RUBY, "cache")
+ end
end
def checksums_section_when_enabled(target_lockfile = nil, &block)
begin
enabled = (target_lockfile || lockfile).match?(/^CHECKSUMS$/)
rescue Errno::ENOENT
- enabled = Bundler.feature_flag.bundler_3_mode?
+ enabled = true
end
checksums_section(enabled, &block)
end
def checksum_to_lock(*args)
- checksums_section do |c|
+ checksums_section(true, bundler_checksum: false) do |c|
c.checksum(*args)
end.to_s.sub(/^CHECKSUMS\n/, "").strip
end
diff --git a/spec/bundler/support/command_execution.rb b/spec/bundler/support/command_execution.rb
index 02726744d3..e2915b996d 100644
--- a/spec/bundler/support/command_execution.rb
+++ b/spec/bundler/support/command_execution.rb
@@ -2,9 +2,8 @@
module Spec
class CommandExecution
- def initialize(command, working_directory:, timeout:)
+ def initialize(command, timeout:)
@command = command
- @working_directory = working_directory
@timeout = timeout
@original_stdout = String.new
@original_stderr = String.new
@@ -73,7 +72,7 @@ module Spec
attr_reader :failure_reason
def normalize(string)
- string.force_encoding(Encoding::UTF_8).strip.gsub("\r\n", "\n")
+ string.dup.force_encoding(Encoding::UTF_8).scrub.strip.gsub("\r\n", "\n")
end
end
end
diff --git a/spec/bundler/support/filters.rb b/spec/bundler/support/filters.rb
index f52cb02588..2be25b4a78 100644
--- a/spec/bundler/support/filters.rb
+++ b/spec/bundler/support/filters.rb
@@ -1,11 +1,11 @@
# frozen_string_literal: true
class RequirementChecker < Proc
- def self.against(present)
- provided = Gem::Version.new(present)
-
+ def self.against(provided)
new do |required|
- !Gem::Requirement.new(required).satisfied_by?(provided)
+ requirement = Gem::Requirement.new(required)
+
+ !requirement.satisfied_by?(provided)
end.tap do |checker|
checker.provided = provided
end
@@ -18,11 +18,13 @@ class RequirementChecker < Proc
end
end
+git_version = Gem::Version.new(`git --version`[/(\d+\.\d+\.\d+)/, 1])
+
RSpec.configure do |config|
config.filter_run_excluding realworld: true
- config.filter_run_excluding bundler: RequirementChecker.against(Bundler::VERSION.split(".")[0])
- config.filter_run_excluding rubygems: RequirementChecker.against(Gem::VERSION)
+ config.filter_run_excluding rubygems: RequirementChecker.against(Gem.rubygems_version)
+ config.filter_run_excluding git: RequirementChecker.against(git_version)
config.filter_run_excluding ruby_repo: !ENV["GEM_COMMAND"].nil?
config.filter_run_excluding no_color_tty: Gem.win_platform? || !ENV["GITHUB_ACTION"].nil?
config.filter_run_excluding permissions: Gem.win_platform?
@@ -30,6 +32,11 @@ RSpec.configure do |config|
config.filter_run_excluding jruby_only: RUBY_ENGINE != "jruby"
config.filter_run_excluding truffleruby_only: RUBY_ENGINE != "truffleruby"
config.filter_run_excluding man: Gem.win_platform?
+ config.filter_run_excluding mri_only: RUBY_ENGINE != "ruby"
config.filter_run_when_matching :focus unless ENV["CI"]
+
+ config.before(:each, :bundler) do |example|
+ bundle_config "simulate_version #{example.metadata[:bundler]}"
+ end
end
diff --git a/spec/bundler/support/hax.rb b/spec/bundler/support/hax.rb
index 492c6ca8ed..46718f5fa4 100644
--- a/spec/bundler/support/hax.rb
+++ b/spec/bundler/support/hax.rb
@@ -19,21 +19,56 @@ module Gem
@default_specifications_dir = nil
end
- if ENV["BUNDLER_SPEC_WINDOWS"]
- @@win_platform = true # rubocop:disable Style/ClassVars
- end
+ spec_platform = ENV["BUNDLER_SPEC_PLATFORM"]
+ if spec_platform
+ if /mingw|mswin/.match?(spec_platform)
+ @@win_platform = nil # rubocop:disable Style/ClassVars
+ RbConfig::CONFIG["host_os"] = spec_platform.gsub(/^[^-]+-/, "").tr("-", "_")
+ end
- if ENV["BUNDLER_SPEC_PLATFORM"]
- previous_platforms = @platforms
- previous_local = Platform.local
+ RbConfig::CONFIG["arch"] = spec_platform
class Platform
- @local = new(ENV["BUNDLER_SPEC_PLATFORM"])
+ @local = nil
end
- @platforms = previous_platforms.map {|platform| platform == previous_local ? Platform.local : platform }
+ @platforms = []
end
if ENV["BUNDLER_SPEC_GEM_SOURCES"]
self.sources = [ENV["BUNDLER_SPEC_GEM_SOURCES"]]
end
+
+ if ENV["BUNDLER_SPEC_READ_ONLY"]
+ module ReadOnly
+ def open(file, mode)
+ if file != IO::NULL && mode == "wb"
+ raise Errno::EROFS
+ else
+ super
+ end
+ end
+ end
+
+ File.singleton_class.prepend ReadOnly
+ end
+
+ if ENV["BUNDLER_SPEC_FAKE_RESOLVE"]
+ module FakeResolv
+ def getaddrinfo(host, port)
+ if host == ENV["BUNDLER_SPEC_FAKE_RESOLVE"]
+ [["AF_INET", port, "127.0.0.1", "127.0.0.1", 2, 2, 17]]
+ else
+ super
+ end
+ end
+ end
+
+ Socket.singleton_class.prepend FakeResolv
+ end
+end
+
+# mise installed rubygems_plugin.rb to system wide `site_ruby` directory.
+# This empty module avoid to call `mise` command.
+module ReshimInstaller
+ def self.reshim; end
end
diff --git a/spec/bundler/support/helpers.rb b/spec/bundler/support/helpers.rb
index 294b932ea1..b0d4b5008b 100644
--- a/spec/bundler/support/helpers.rb
+++ b/spec/bundler/support/helpers.rb
@@ -20,20 +20,17 @@ module Spec
def reset!
Dir.glob("#{tmp}/{gems/*,*}", File::FNM_DOTMATCH).each do |dir|
next if %w[base base_system remote1 rubocop standard gems rubygems . ..].include?(File.basename(dir))
- FileUtils.rm_rf(dir)
+ FileUtils.rm_r(dir)
end
FileUtils.mkdir_p(home)
FileUtils.mkdir_p(tmpdir)
- reset_paths!
- end
-
- def reset_paths!
Bundler.reset!
+ Bundler::Source::Git::GitProxy.reset
Gem.clear_paths
end
- def the_bundle(*args)
- TheBundle.new(*args)
+ def the_bundle
+ TheBundle.new
end
MAJOR_DEPRECATION = /^\[DEPRECATED\]\s*/
@@ -43,7 +40,7 @@ module Spec
end
def deprecations
- err.split("\n").select {|l| l =~ MAJOR_DEPRECATION }.join("\n").split(MAJOR_DEPRECATION)
+ err.split("\n").filter_map {|l| l.sub(MAJOR_DEPRECATION, "") if l.match?(MAJOR_DEPRECATION) }
end
def run(cmd, *args)
@@ -58,7 +55,7 @@ module Spec
begin
#{ruby}
rescue LoadError => e
- warn "ZOMG LOAD ERROR" if e.message.include?("-- #{name}")
+ warn e.message if e.message.include?("-- #{name}")
end
RUBY
opts = args.last.is_a?(Hash) ? args.pop : {}
@@ -66,12 +63,15 @@ module Spec
run(cmd, *args)
end
+ def in_bundled_app(cmd, options = {})
+ sys_exec(cmd, dir: bundled_app, raise_on_error: options[:raise_on_error])
+ end
+
def bundle(cmd, options = {}, &block)
bundle_bin = options.delete(:bundle_bin)
bundle_bin ||= installed_bindir.join("bundle")
env = options.delete(:env) || {}
- preserve_ruby_flags = options.delete(:preserve_ruby_flags)
requires = options.delete(:requires) || []
@@ -79,15 +79,14 @@ module Spec
custom_load_path = options.delete(:load_path)
load_path = []
- load_path << spec_dir
load_path << custom_load_path if custom_load_path
- build_ruby_options = { load_path: load_path, requires: requires, env: env }
- build_ruby_options.merge!(artifice: options.delete(:artifice)) if options.key?(:artifice)
+ build_env_options = { load_path: load_path, requires: requires, env: env }
+ build_env_options.merge!(artifice: options.delete(:artifice)) if options.key?(:artifice) || cmd.start_with?("exec")
match_source(cmd)
- env, ruby_cmd = build_ruby_cmd(build_ruby_options)
+ env = build_env(build_env_options)
raise_on_error = options.delete(:raise_on_error)
@@ -102,8 +101,7 @@ module Spec
end
end.join
- cmd = "#{ruby_cmd} #{bundle_bin} #{cmd}#{args}"
- env["BUNDLER_SPEC_ORIGINAL_CMD"] = "#{ruby_cmd} #{bundle_bin}" if preserve_ruby_flags
+ cmd = "#{Gem.ruby} #{bundle_bin} #{cmd}#{args}"
sys_exec(cmd, { env: env, dir: dir, raise_on_error: raise_on_error }, &block)
end
@@ -123,10 +121,11 @@ module Spec
end
def ruby(ruby, options = {})
- env, ruby_cmd = build_ruby_cmd({ artifice: nil }.merge(options))
+ env = build_env({ artifice: nil }.merge(options))
escaped_ruby = ruby.shellescape
options[:env] = env if env
- sys_exec(%(#{ruby_cmd} -w -e #{escaped_ruby}), options)
+ options[:dir] ||= bundled_app
+ sys_exec(%(#{Gem.ruby} -w -e #{escaped_ruby}), options)
end
def load_error_ruby(ruby, name, opts = {})
@@ -134,22 +133,24 @@ module Spec
begin
#{ruby}
rescue LoadError => e
- warn "ZOMG LOAD ERROR" if e.message.include?("-- #{name}")
+ warn e.message if e.message.include?("-- #{name}")
end
R
end
- def build_ruby_cmd(options = {})
- libs = options.delete(:load_path)
- lib_option = libs ? "-I#{libs.join(File::PATH_SEPARATOR)}" : []
-
+ def build_env(options = {})
env = options.delete(:env) || {}
+ libs = options.delete(:load_path) || []
+ env["RUBYOPT"] = opt_add("-I#{libs.join(File::PATH_SEPARATOR)}", env["RUBYOPT"]) if libs.any?
+
current_example = RSpec.current_example
main_source = @gemfile_source if defined?(@gemfile_source)
compact_index_main_source = main_source&.start_with?("https://gem.repo", "https://gems.security")
requires = options.delete(:requires) || []
+ requires << hax
+
artifice = options.delete(:artifice) do
if current_example && current_example.metadata[:realworld]
"vcr"
@@ -172,11 +173,9 @@ module Spec
requires << "#{Path.spec_dir}/support/artifice/#{artifice}.rb"
end
- requires << "#{Path.spec_dir}/support/hax.rb"
+ requires.each {|r| env["RUBYOPT"] = opt_add("-r#{r}", env["RUBYOPT"]) }
- require_option = requires.map {|r| "-r#{r}" }
-
- [env, [Gem.ruby, *lib_option, *require_option].compact.join(" ")]
+ env
end
def gembin(cmd, options = {})
@@ -184,36 +183,20 @@ module Spec
sys_exec(cmd.to_s, options)
end
- def gem_command(command, options = {})
- env = options[:env] || {}
- env["RUBYOPT"] = opt_add(opt_add("-r#{spec_dir}/support/hax.rb", env["RUBYOPT"]), ENV["RUBYOPT"])
- options[:env] = env
-
- # Sometimes `gem install` commands hang at dns resolution, which has a
- # default timeout of 60 seconds. When that happens, the timeout for a
- # command is expired too. So give `gem install` commands a bit more time.
- options[:timeout] = 120
-
- output = sys_exec("#{Path.gem_bin} #{command}", options)
- stderr = last_command.stderr
- raise stderr if stderr.include?("WARNING") && !allowed_rubygems_warning?(stderr)
- output
- end
-
- def rake
- "#{Gem.ruby} -S #{ENV["GEM_PATH"]}/bin/rake"
- end
-
def sys_exec(cmd, options = {}, &block)
env = options[:env] || {}
env["RUBYOPT"] = opt_add(opt_add("-r#{spec_dir}/support/switch_rubygems.rb", env["RUBYOPT"]), ENV["RUBYOPT"])
options[:env] = env
- options[:dir] ||= bundled_app
sh(cmd, options, &block)
end
- def config(config = nil, path = bundled_app(".bundle/config"))
+ def bundle_config(config = nil, path = bundled_app(".bundle/config"))
+ if config.is_a?(String)
+ key, value = config.split(" ", 2)
+ config = { Bundler::Settings.key_for(key) => value }
+ end
+
current = File.exist?(path) ? Psych.load_file(path) : {}
return current unless config
@@ -230,8 +213,8 @@ module Spec
new_config
end
- def global_config(config = nil)
- config(config, home(".bundle/config"))
+ def bundle_config_global(config = nil)
+ bundle_config(config, home(".bundle/config"))
end
def create_file(path, contents = "")
@@ -303,6 +286,10 @@ module Spec
bundle :lock, opts
end
+ def base_system_gems(*names, **options)
+ system_gems names.map {|name| find_base_path(name) }, **options
+ end
+
def system_gems(*gems)
gems = gems.flatten
options = gems.last.is_a?(Hash) ? gems.pop : {}
@@ -310,9 +297,10 @@ module Spec
default = options.fetch(:default, false)
gems.each do |g|
gem_name = g.to_s
- if gem_name.start_with?("bundler")
- version = gem_name.match(/\Abundler-(?<version>.*)\z/)[:version] if gem_name != "bundler"
- with_built_bundler(version) {|gem_path| install_gem(gem_path, install_dir, default) }
+ bundler = gem_name.match(/\Abundler-(?<version>.*)\z/)
+
+ if bundler
+ with_built_bundler(bundler[:version], released: options.fetch(:released, false)) {|gem_path| install_gem(gem_path, install_dir, default) }
elsif %r{\A(?:[a-zA-Z]:)?/.*\.gem\z}.match?(gem_name)
install_gem(gem_name, install_dir, default)
else
@@ -325,22 +313,96 @@ module Spec
def self.install_dev_bundler
extend self
- system_gems :bundler, path: pristine_system_gem_path
+ with_built_bundler(nil, build_path: tmp_root) {|gem_path| install_gem(gem_path, pristine_system_gem_path) }
end
def install_gem(path, install_dir, default = false)
- raise "OMG `#{path}` does not exist!" unless File.exist?(path)
+ raise ArgumentError, "`#{path}` does not exist!" unless File.exist?(path)
+
+ require "rubygems/installer"
+
+ with_simulated_platform do
+ installer = Gem::Installer.at(
+ path.to_s,
+ install_dir: install_dir.to_s,
+ document: [],
+ ignore_dependencies: true,
+ wrappers: true,
+ env_shebang: true,
+ force: true
+ )
+ installer.install
+ end
+
+ if default
+ gem = Pathname.new(path).basename.to_s.match(/(.*)\.gem/)[1]
+
+ # Revert Gem::Installer#write_spec and apply Gem::Installer#write_default_spec
+ FileUtils.mkdir_p File.join(install_dir, "specifications", "default")
+ File.rename File.join(install_dir, "specifications", gem + ".gemspec"),
+ File.join(install_dir, "specifications", "default", gem + ".gemspec")
+
+ # Revert Gem::Installer#write_cache_file
+ File.delete File.join(install_dir, "cache", gem + ".gem")
+ end
+ end
- args = "--no-document --ignore-dependencies --verbose --local --install-dir #{install_dir}"
- args += " --default" if default
+ def uninstall_gem(name, options = {})
+ require "rubygems/uninstaller"
- gem_command "install #{args} '#{path}'"
+ gem_home = options.dig(:env, "GEM_HOME") || system_gem_path.to_s
+
+ with_env_vars("GEM_HOME" => gem_home) do
+ Gem.clear_paths
+
+ uninstaller = Gem::Uninstaller.new(
+ name,
+ ignore: true,
+ executables: true,
+ all: true
+ )
+ uninstaller.uninstall
+ ensure
+ Gem.clear_paths
+ end
end
- def with_built_bundler(version = nil, &block)
+ def installed_gems_list(options = {})
+ gem_home = options.dig(:env, "GEM_HOME") || system_gem_path.to_s
+
+ # Temporarily set GEM_HOME for the command
+ old_gem_home = ENV["GEM_HOME"]
+ ENV["GEM_HOME"] = gem_home
+ Gem.clear_paths
+
+ begin
+ require "rubygems/commands/list_command"
+
+ # Capture output from the list command
+ require "stringio"
+ output_io = StringIO.new
+ cmd = Gem::Commands::ListCommand.new
+ cmd.ui = Gem::StreamUI.new(StringIO.new, output_io, StringIO.new, false)
+ cmd.invoke
+ output = output_io.string.strip
+ ensure
+ ENV["GEM_HOME"] = old_gem_home
+ Gem.clear_paths
+ end
+
+ # Create a fake command execution so `out` helper works
+ command_execution = Spec::CommandExecution.new("gem list", timeout: 60)
+ command_execution.original_stdout << output
+ command_execution.exitstatus = 0
+ command_executions << command_execution
+
+ output
+ end
+
+ def with_built_bundler(version = nil, opts = {}, &block)
require_relative "builders"
- Builders::BundlerBuilder.new(self, "bundler", version)._build(&block)
+ Builders::BundlerBuilder.new(self, "bundler", version)._build(opts, &block)
end
def with_gem_path_as(path)
@@ -368,6 +430,36 @@ module Spec
ENV.replace(backup)
end
+ # Simulate the platform set by BUNDLER_SPEC_PLATFORM for in-process
+ # operations, mirroring what hax.rb does for subprocesses.
+ def with_simulated_platform
+ spec_platform = ENV["BUNDLER_SPEC_PLATFORM"]
+ unless spec_platform
+ return yield
+ end
+
+ old_arch = RbConfig::CONFIG["arch"]
+ old_host_os = RbConfig::CONFIG["host_os"]
+
+ if /mingw|mswin/.match?(spec_platform)
+ Gem.class_variable_set(:@@win_platform, nil) # rubocop:disable Style/ClassVars
+ RbConfig::CONFIG["host_os"] = spec_platform.gsub(/^[^-]+-/, "").tr("-", "_")
+ end
+
+ RbConfig::CONFIG["arch"] = spec_platform
+ Gem::Platform.instance_variable_set(:@local, nil)
+ Gem.instance_variable_set(:@platforms, [])
+
+ yield
+ ensure
+ if spec_platform
+ RbConfig::CONFIG["arch"] = old_arch
+ RbConfig::CONFIG["host_os"] = old_host_os
+ Gem::Platform.instance_variable_set(:@local, nil)
+ Gem.instance_variable_set(:@platforms, [])
+ end
+ end
+
def with_path_added(path)
with_path_as([path.to_s, ENV["PATH"]].join(File::PATH_SEPARATOR)) do
yield
@@ -393,37 +485,34 @@ module Spec
end
def pristine_system_gems(*gems)
- FileUtils.rm_rf(system_gem_path)
+ FileUtils.rm_r(system_gem_path)
- system_gems(*gems)
- end
-
- def realworld_system_gems(*gems)
- gems = gems.flatten
- opts = gems.last.is_a?(Hash) ? gems.pop : {}
- path = opts.fetch(:path, system_gem_path)
-
- gems.each do |gem|
- gem_command "install --no-document --verbose --install-dir #{path} #{gem}"
+ if gems.any?
+ system_gems(*gems)
+ else
+ default_system_gems
end
end
def cache_gems(*gems, gem_repo: gem_repo1)
gems = gems.flatten
- FileUtils.rm_rf("#{bundled_app}/vendor/cache")
FileUtils.mkdir_p("#{bundled_app}/vendor/cache")
gems.each do |g|
path = "#{gem_repo}/gems/#{g}.gem"
- raise "OMG `#{path}` does not exist!" unless File.exist?(path)
+ raise ArgumentError, "`#{path}` does not exist!" unless File.exist?(path)
FileUtils.cp(path, "#{bundled_app}/vendor/cache")
end
end
def simulate_new_machine
- FileUtils.rm_rf bundled_app(".bundle")
- pristine_system_gems :bundler
+ FileUtils.rm_r bundled_app(".bundle")
+ pristine_system_gems
+ end
+
+ def default_system_gems
+ FileUtils.cp_r pristine_system_gem_path, system_gem_path
end
def simulate_ruby_platform(ruby_platform)
@@ -442,16 +531,6 @@ module Spec
ENV["BUNDLER_SPEC_PLATFORM"] = old if block_given?
end
- def simulate_windows(platform = x86_mswin32)
- old = ENV["BUNDLER_SPEC_WINDOWS"]
- ENV["BUNDLER_SPEC_WINDOWS"] = "true"
- simulate_platform platform do
- yield
- end
- ensure
- ENV["BUNDLER_SPEC_WINDOWS"] = old
- end
-
def current_ruby_minor
Gem.ruby_version.segments.tap {|s| s.delete_at(2) }.join(".")
end
@@ -460,12 +539,6 @@ module Spec
ruby_major_minor.map.with_index {|s, i| i == 1 ? s + 1 : s }.join(".")
end
- def previous_ruby_minor
- return "2.7" if ruby_major_minor == [3, 0]
-
- ruby_major_minor.map.with_index {|s, i| i == 1 ? s - 1 : s }.join(".")
- end
-
def ruby_major_minor
Gem.ruby_version.segments[0..1]
end
@@ -523,45 +596,27 @@ module Spec
end
end
- def require_rack
- # need to hack, so we can require rack
+ def require_rack_test
+ # need to hack, so we can require rack for testing
old_gem_home = ENV["GEM_HOME"]
- ENV["GEM_HOME"] = Spec::Path.base_system_gem_path.to_s
- require "rack"
+ ENV["GEM_HOME"] = Spec::Path.scoped_base_system_gem_path.to_s
+ require "rack/test"
ENV["GEM_HOME"] = old_gem_home
end
- def wait_for_server(host, port, seconds = 15)
- tries = 0
- sleep 0.5
- TCPSocket.new(host, port)
- rescue StandardError => e
- raise(e) if tries > (seconds * 2)
- tries += 1
- retry
- end
-
- def find_unused_port
- port = 21_453
- begin
- port += 1 while TCPSocket.new("127.0.0.1", port)
- rescue StandardError
- false
- end
- port
- end
-
def exit_status_for_signal(signal_number)
# For details see: https://en.wikipedia.org/wiki/Exit_status#Shell_and_scripts
128 + signal_number
end
- private
+ def empty_repo4
+ FileUtils.rm_r gem_repo4
- def allowed_rubygems_warning?(text)
- text.include?("open-ended") || text.include?("is a symlink") || text.include?("rake based") || text.include?("expected RubyGems version")
+ build_repo4 {}
end
+ private
+
def match_source(contents)
match = /source ["']?(?<source>http[^"']+)["']?/.match(contents)
return unless match
diff --git a/spec/bundler/support/indexes.rb b/spec/bundler/support/indexes.rb
index 7960bae59f..1fbdd49abe 100644
--- a/spec/bundler/support/indexes.rb
+++ b/spec/bundler/support/indexes.rb
@@ -66,7 +66,6 @@ module Spec
end
def should_conservative_resolve_and_include(opts, unlock, specs)
- # empty unlock means unlock all
opts = Array(opts)
search = Bundler::GemVersionPromoter.new.tap do |s|
s.level = opts.first
@@ -123,7 +122,7 @@ module Spec
end
versions "1.0 1.2 1.2.1 1.2.2 1.3 1.3.0.1 1.3.5 1.4.0 1.4.2 1.4.2.1" do |version|
- platforms "ruby java mswin32 mingw32 x64-mingw32" do |platform|
+ platforms "ruby java mswin32 mingw32 x64-mingw-ucrt" do |platform|
next if version == v("1.4.2.1") && platform != pl("x86-mswin32")
next if version == v("1.4.2") && platform == pl("x86-mswin32")
gem "nokogiri", version, platform do
diff --git a/spec/bundler/support/matchers.rb b/spec/bundler/support/matchers.rb
index 9f311fc0d7..5a3c38a4db 100644
--- a/spec/bundler/support/matchers.rb
+++ b/spec/bundler/support/matchers.rb
@@ -52,7 +52,7 @@ module Spec
end
def self.define_compound_matcher(matcher, preconditions, &declarations)
- raise "Must have preconditions to define a compound matcher" if preconditions.empty?
+ raise ArgumentError, "Must have preconditions to define a compound matcher" if preconditions.empty?
define_method(matcher) do |*expected, &block_arg|
Precondition.new(
RSpec::Matchers::DSL::Matcher.new(matcher, declarations, self, *expected, &block_arg),
diff --git a/spec/bundler/support/path.rb b/spec/bundler/support/path.rb
index 76e28db10b..2e6486412f 100644
--- a/spec/bundler/support/path.rb
+++ b/spec/bundler/support/path.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require "pathname"
+require "pathname" unless defined?(Pathname)
require "rbconfig"
require_relative "env"
@@ -10,7 +10,7 @@ module Spec
include Spec::Env
def source_root
- @source_root ||= Pathname.new(ruby_core? ? "../../.." : "../..").expand_path(__dir__)
+ @source_root ||= Pathname.new(ruby_core? ? "../../.." : "../../bundler").expand_path(__dir__)
end
def root
@@ -25,12 +25,8 @@ module Spec
@relative_gemspec ||= ruby_core? ? "lib/bundler/bundler.gemspec" : "bundler.gemspec"
end
- def gemspec_dir
- @gemspec_dir ||= gemspec.parent
- end
-
def loaded_gemspec
- @loaded_gemspec ||= Gem::Specification.load(gemspec.to_s)
+ @loaded_gemspec ||= Dir.chdir(source_root) { Gem::Specification.load(gemspec.to_s) }
end
def test_gemfile
@@ -49,8 +45,16 @@ module Spec
@dev_gemfile ||= tool_dir.join("dev_gems.rb")
end
+ def dev_binstub
+ @dev_binstub ||= bindir.join("bundle")
+ end
+
def bindir
- @bindir ||= source_root.join(ruby_core? ? "libexec" : "exe")
+ @bindir ||= source_root.join(ruby_core? ? "spec/bin" : "../bin")
+ end
+
+ def exedir
+ @exedir ||= source_root.join(ruby_core? ? "libexec" : "exe")
end
def installed_bindir
@@ -67,18 +71,22 @@ module Spec
def path
env_path = ENV["PATH"]
- env_path = env_path.split(File::PATH_SEPARATOR).reject {|path| path == bindir.to_s }.join(File::PATH_SEPARATOR) if ruby_core?
+ env_path = env_path.split(File::PATH_SEPARATOR).reject {|path| path == exedir.to_s }.join(File::PATH_SEPARATOR) if ruby_core?
env_path
end
def spec_dir
- @spec_dir ||= source_root.join(ruby_core? ? "spec/bundler" : "spec")
+ @spec_dir ||= source_root.join(ruby_core? ? "spec/bundler" : "../spec")
end
def man_dir
@man_dir ||= lib_dir.join("bundler/man")
end
+ def hax
+ @hax ||= spec_dir.join("support/hax.rb")
+ end
+
def tracked_files
@tracked_files ||= git_ls_files(tracked_files_glob)
end
@@ -102,18 +110,28 @@ module Spec
end
def tmp(*path)
- tmp_root(scope).join(*path)
- end
-
- def tmp_root(scope)
- source_root.join("tmp", "#{test_env_version}.#{scope}")
+ tmp_root.join("#{test_env_version}.#{scope}").join(*path)
+ end
+
+ def tmp_root
+ if ruby_core? && (tmpdir = ENV["TMPDIR"])
+ # Use realpath to resolve any symlinks in TMPDIR (e.g., on macOS /var -> /private/var)
+ real = begin
+ File.realpath(tmpdir)
+ rescue Errno::ENOENT, Errno::EACCES
+ tmpdir
+ end
+ Pathname(real)
+ else
+ (ruby_core? ? source_root : source_root.parent).join("tmp")
+ end
end
# Bump this version whenever you make a breaking change to the spec setup
# that requires regenerating tmp/.
def test_env_version
- 1
+ 2
end
def scope
@@ -128,19 +146,15 @@ module Spec
end
def default_bundle_path(*path)
- if Bundler.feature_flag.default_install_uses_path?
- local_gem_path(*path)
- else
- system_gem_path(*path)
- end
+ system_gem_path(*path)
end
def default_cache_path(*path)
- if Bundler.feature_flag.global_gem_cache?
- home(".bundle/cache", *path)
- else
- default_bundle_path("cache/bundler", *path)
- end
+ default_bundle_path("cache/bundler", *path)
+ end
+
+ def compact_index_cache_path
+ home(".bundle/cache/compact_index")
end
def bundled_app(*path)
@@ -171,20 +185,20 @@ module Spec
bundled_app("Gemfile.lock")
end
- def base_system_gem_path
- scoped_gem_path(base_system_gems)
+ def scoped_base_system_gem_path
+ scoped_gem_path(base_system_gem_path)
end
- def base_system_gems
- tmp("gems/base")
+ def base_system_gem_path
+ tmp_root.join("gems/base")
end
- def rubocop_gems
- tmp("gems/rubocop")
+ def rubocop_gem_path
+ tmp_root.join("gems/rubocop")
end
- def standard_gems
- tmp("gems/standard")
+ def standard_gem_path
+ tmp_root.join("gems/standard")
end
def file_uri_for(path)
@@ -195,35 +209,35 @@ module Spec
end
def gem_repo1(*args)
- tmp("gems/remote1", *args)
+ gem_path("remote1", *args)
end
def gem_repo_missing(*args)
- tmp("gems/missing", *args)
+ gem_path("missing", *args)
end
def gem_repo2(*args)
- tmp("gems/remote2", *args)
+ gem_path("remote2", *args)
end
def gem_repo3(*args)
- tmp("gems/remote3", *args)
+ gem_path("remote3", *args)
end
def gem_repo4(*args)
- tmp("gems/remote4", *args)
+ gem_path("remote4", *args)
end
def security_repo(*args)
- tmp("gems/security_repo", *args)
+ gem_path("security_repo", *args)
end
def system_gem_path(*path)
- tmp("gems/system", *path)
+ gem_path("system", *path)
end
def pristine_system_gem_path
- tmp("gems/base_system")
+ tmp_root.join("gems/pristine_system")
end
def local_gem_path(*path, base: bundled_app)
@@ -234,6 +248,10 @@ module Spec
base.join(Gem.ruby_engine, RbConfig::CONFIG["ruby_version"])
end
+ def gem_path(*args)
+ tmp("gems", *args)
+ end
+
def lib_path(*args)
tmp("libs", *args)
end
@@ -261,7 +279,7 @@ module Spec
def replace_version_file(version, dir: source_root)
version_file = File.expand_path("lib/bundler/version.rb", dir)
contents = File.read(version_file)
- contents.sub!(/(^\s+VERSION\s*=\s*)"#{Gem::Version::VERSION_PATTERN}"/, %(\\1"#{version}"))
+ contents.sub!(/(^\s+VERSION\s*=\s*).*$/, %(\\1"#{version}"))
File.open(version_file, "w") {|f| f << contents }
end
@@ -272,29 +290,54 @@ module Spec
File.open(gemspec_file, "w") {|f| f << contents }
end
+ def replace_changelog(version, dir:)
+ changelog = File.expand_path("CHANGELOG.md", dir)
+ contents = File.readlines(changelog)
+ contents = [contents[0], contents[1], "## #{version} (2100-01-01)\n", *contents[3..-1]].join
+ File.open(changelog, "w") {|f| f << contents }
+ end
+
def git_root
ruby_core? ? source_root : source_root.parent
end
def rake_path
- Dir["#{base_system_gems}/#{Bundler.ruby_scope}/**/rake*.gem"].first
+ find_base_path("rake")
+ end
+
+ def rake_version
+ File.basename(rake_path).delete_prefix("rake-").delete_suffix(".gem")
end
def sinatra_dependency_paths
deps = %w[
mustermann
rack
+ rack-protection
+ rack-session
tilt
sinatra
- ruby2_keywords
base64
logger
+ compact_index
]
- Dir[base_system_gem_path.join("gems/{#{deps.join(",")}}-*/lib")].map(&:to_s)
+ path = if deps.all? {|dep| !Dir[scoped_base_system_gem_path.join("gems/#{dep}-*")].empty? }
+ scoped_base_system_gem_path
+ elsif ruby_core? && deps.all? {|dep| !Dir[source_root.join(".bundle/gems/#{dep}-*")].empty? }
+ source_root.join(".bundle")
+ else
+ scoped_base_system_gem_path
+ end
+
+ Dir[path.join("gems/{#{deps.join(",")}}-*/lib")].map(&:to_s)
end
private
+ def find_base_path(name)
+ Dir["#{scoped_base_system_gem_path}/**/#{name}-*.gem"].first
+ end
+
def git_ls_files(glob)
skip "Not running on a git context, since running tests from a tarball" if ruby_core_tarball?
@@ -302,7 +345,7 @@ module Spec
end
def tracked_files_glob
- ruby_core? ? "libexec/bundle* lib/bundler lib/bundler.rb spec/bundler man/bundle*" : "lib exe spec CHANGELOG.md LICENSE.md README.md bundler.gemspec"
+ ruby_core? ? "libexec/bundle* lib/bundler lib/bundler.rb spec/bundler man/bundle*" : "lib exe CHANGELOG.md LICENSE.md README.md bundler.gemspec"
end
def lib_tracked_files_glob
@@ -310,7 +353,7 @@ module Spec
end
def man_tracked_files_glob
- ruby_core? ? "man/bundle* man/gemfile*" : "lib/bundler/man/bundle*.1 lib/bundler/man/gemfile*.5"
+ "lib/bundler/man/bundle*.1.ronn lib/bundler/man/gemfile*.5.ronn"
end
def ruby_core_tarball?
diff --git a/spec/bundler/support/platforms.rb b/spec/bundler/support/platforms.rb
index 526e1c09a9..56a0843005 100644
--- a/spec/bundler/support/platforms.rb
+++ b/spec/bundler/support/platforms.rb
@@ -2,64 +2,22 @@
module Spec
module Platforms
- include Bundler::GemHelpers
-
- def rb
- Gem::Platform::RUBY
- end
-
- def mac
- Gem::Platform.new("x86-darwin-10")
- end
-
- def x64_mac
- Gem::Platform.new("x86_64-darwin-15")
- end
-
- def java
- Gem::Platform.new([nil, "java", nil])
- end
-
- def linux
- Gem::Platform.new("x86_64-linux")
- end
-
- def x86_mswin32
- Gem::Platform.new(["x86", "mswin32", nil])
- end
-
- def x64_mswin64
- Gem::Platform.new(["x64", "mswin64", nil])
- end
-
- def x86_mingw32
- Gem::Platform.new(["x86", "mingw32", nil])
- end
-
- def x64_mingw32
- Gem::Platform.new(["x64", "mingw32", nil])
- end
-
- def x64_mingw_ucrt
- Gem::Platform.new(["x64", "mingw", "ucrt"])
- end
-
- def windows_platforms
- [x86_mswin32, x64_mswin64, x86_mingw32, x64_mingw32, x64_mingw_ucrt]
+ def not_local
+ generic_local_platform == Gem::Platform::RUBY ? "java" : Gem::Platform::RUBY
end
- def all_platforms
- [rb, java, linux, windows_platforms].flatten
+ def local_platform
+ Bundler.local_platform
end
- def not_local
- all_platforms.find {|p| p != generic_local_platform }
+ def generic_local_platform
+ Gem::Platform.generic(local_platform)
end
def local_tag
- if RUBY_PLATFORM == "java"
+ if Gem.java_platform?
:jruby
- elsif ["x64-mingw32", "x64-mingw-ucrt"].include?(RUBY_PLATFORM)
+ elsif Gem.win_platform?
:windows
else
:ruby
@@ -96,16 +54,22 @@ module Spec
end
def default_platform_list(*extra, defaults: default_locked_platforms)
- defaults.concat(extra).uniq
+ defaults.concat(extra).map(&:to_s).uniq
end
def lockfile_platforms(*extra, defaults: default_locked_platforms)
platforms = default_platform_list(*extra, defaults: defaults)
- platforms.map(&:to_s).sort.join("\n ")
+ platforms.sort.join("\n ")
end
def default_locked_platforms
- [local_platform, generic_local_platform]
+ [local_platform, generic_default_locked_platform].compact
+ end
+
+ def generic_default_locked_platform
+ return unless Bundler::MatchPlatform.generic_local_platform_is_ruby?
+
+ Gem::Platform::RUBY
end
end
end
diff --git a/spec/bundler/support/rubygems_ext.rb b/spec/bundler/support/rubygems_ext.rb
index 82b2819858..812dc4deaa 100644
--- a/spec/bundler/support/rubygems_ext.rb
+++ b/spec/bundler/support/rubygems_ext.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-abort "RubyGems only supports Ruby 3.1 or higher" if RUBY_VERSION < "3.1.0"
+abort "RubyGems only supports Ruby 3.2 or higher" if RUBY_VERSION < "3.2.0"
require_relative "path"
@@ -10,10 +10,6 @@ module Spec
module Rubygems
extend self
- def dev_setup
- install_gems(dev_gemfile)
- end
-
def gem_load(gem_name, bin_container)
require_relative "switch_rubygems"
@@ -32,6 +28,9 @@ module Spec
end
def test_setup
+ # Install test dependencies unless parallel-rspec is being used, since in that case they should be setup already
+ install_test_deps unless ENV["RSPEC_FORMATTER_OUTPUT_ID"]
+
setup_test_paths
require "fileutils"
@@ -44,48 +43,78 @@ module Spec
# sign extension bundles on macOS, to avoid trying to find the specified key
# from the fake $HOME/Library/Keychains directory.
ENV.delete "RUBY_CODESIGN"
- ENV["TMPDIR"] = Path.tmpdir.to_s
+ if Path.ruby_core?
+ if (tmpdir = ENV["TMPDIR"])
+ tmpdir_real = begin
+ File.realpath(tmpdir)
+ rescue Errno::ENOENT, Errno::EACCES
+ tmpdir
+ end
+ ENV["TMPDIR"] = tmpdir_real if tmpdir_real != tmpdir
+ end
+ else
+ ENV["TMPDIR"] = Path.tmpdir.to_s
+ end
require "rubygems/user_interaction"
Gem::DefaultUserInteraction.ui = Gem::SilentUI.new
end
- def install_parallel_test_deps
- Gem.clear_paths
-
- require "parallel"
- require "fileutils"
-
- install_test_deps
-
- (2..Parallel.processor_count).each do |n|
- source = Path.tmp_root("1")
- destination = Path.tmp_root(n.to_s)
-
- FileUtils.rm_rf destination
- FileUtils.cp_r source, destination
- end
- end
-
def setup_test_paths
- Gem.clear_paths
-
ENV["BUNDLE_PATH"] = nil
- ENV["GEM_HOME"] = ENV["GEM_PATH"] = Path.base_system_gem_path.to_s
ENV["PATH"] = [Path.system_gem_path("bin"), ENV["PATH"]].join(File::PATH_SEPARATOR)
- ENV["PATH"] = [Path.bindir, ENV["PATH"]].join(File::PATH_SEPARATOR) if Path.ruby_core?
+ ENV["PATH"] = [Path.exedir, ENV["PATH"]].join(File::PATH_SEPARATOR) if Path.ruby_core?
end
def install_test_deps
- Gem.clear_paths
+ dev_bundle("install", gemfile: test_gemfile, path: Path.base_system_gem_path.to_s)
+ dev_bundle("install", gemfile: rubocop_gemfile, path: Path.rubocop_gem_path.to_s)
+ dev_bundle("install", gemfile: standard_gemfile, path: Path.standard_gem_path.to_s)
- install_gems(test_gemfile, Path.base_system_gems.to_s)
- install_gems(rubocop_gemfile, Path.rubocop_gems.to_s)
- install_gems(standard_gemfile, Path.standard_gems.to_s)
+ require_relative "helpers"
+ Helpers.install_dev_bundler
- # For some reason, doing this here crashes on JRuby + Windows. So defer to
- # when the test suite is running in that case.
- Helpers.install_dev_bundler unless Gem.win_platform? && RUBY_ENGINE == "jruby"
+ install_vendored_compact_index
+ end
+
+ # Vendor `rubygems/rubygems.org#lib/compact_index/` under `tmp/compact_index/`
+ # so the artifice can serve compact-index responses without a runtime gem
+ # dependency. Pinned to a reviewed commit; override with COMPACT_INDEX_REF
+ # to refresh against another ref (the existing vendor copy is discarded).
+ def install_vendored_compact_index
+ target_root = Path.tmp_root.join("compact_index")
+ require "fileutils"
+ FileUtils.mkdir_p(Path.tmp_root)
+
+ files = %w[
+ lib/compact_index.rb
+ lib/compact_index/dependency.rb
+ lib/compact_index/gem.rb
+ lib/compact_index/gem_version.rb
+ lib/compact_index/versions_file.rb
+ ]
+
+ # Serialize installs so parallel test setups don't race on the same
+ # vendor tree, and only skip the download when every file is present so
+ # an interrupted run can't leave a partial copy behind.
+ File.open(Path.tmp_root.join("compact_index.lock"), File::CREAT | File::RDWR) do |lock|
+ lock.flock(File::LOCK_EX)
+
+ FileUtils.rm_rf(target_root) if ENV["COMPACT_INDEX_REF"]
+
+ next if files.all? {|path| File.exist?(target_root.join(path)) }
+
+ require "open-uri"
+ ref = ENV["COMPACT_INDEX_REF"] || "7c68a7b39761c61a66f9299f85b889ec39afc02c"
+ files.each do |path|
+ url = "https://raw.githubusercontent.com/rubygems/rubygems.org/#{ref}/#{path}"
+ target = target_root.join(path)
+ FileUtils.mkdir_p(File.dirname(target))
+ tmp = "#{target}.tmp"
+ File.write(tmp, URI.parse(url).open(&:read))
+ File.rename(tmp, target)
+ end
+ end
end
def check_source_control_changes(success_message:, error_message:)
@@ -108,6 +137,36 @@ module Spec
end
end
+ def dev_bundle(*args, gemfile: dev_gemfile, path: nil)
+ old_gemfile = ENV["BUNDLE_GEMFILE"]
+ old_orig_gemfile = ENV["BUNDLER_ORIG_BUNDLE_GEMFILE"]
+ ENV["BUNDLE_GEMFILE"] = gemfile.to_s
+ ENV["BUNDLER_ORIG_BUNDLE_GEMFILE"] = nil
+
+ if path
+ old_path = ENV["BUNDLE_PATH"]
+ ENV["BUNDLE_PATH"] = path
+ else
+ old_path__system = ENV["BUNDLE_PATH__SYSTEM"]
+ ENV["BUNDLE_PATH__SYSTEM"] = "true"
+ end
+
+ require "shellwords"
+ # We don't use `Open3` here because it does not work on JRuby + Windows
+ output = `ruby #{Path.dev_binstub} #{args.shelljoin}`
+ raise output unless $?.success?
+ output
+ ensure
+ if path
+ ENV["BUNDLE_PATH"] = old_path
+ else
+ ENV["BUNDLE_PATH__SYSTEM"] = old_path__system
+ end
+
+ ENV["BUNDLER_ORIG_BUNDLE_GEMFILE"] = old_orig_gemfile
+ ENV["BUNDLE_GEMFILE"] = old_gemfile
+ end
+
private
def gem_load_and_activate(gem_name, bin_container)
@@ -136,34 +195,6 @@ module Spec
gem gem_name, gem_requirement
end
- def install_gems(gemfile, path = nil)
- old_gemfile = ENV["BUNDLE_GEMFILE"]
- old_orig_gemfile = ENV["BUNDLER_ORIG_BUNDLE_GEMFILE"]
- ENV["BUNDLE_GEMFILE"] = gemfile.to_s
- ENV["BUNDLER_ORIG_BUNDLE_GEMFILE"] = nil
-
- if path
- old_path = ENV["BUNDLE_PATH"]
- ENV["BUNDLE_PATH"] = path
- else
- old_path__system = ENV["BUNDLE_PATH__SYSTEM"]
- ENV["BUNDLE_PATH__SYSTEM"] = "true"
- end
-
- # We don't use `Open3` here because it does not work on JRuby + Windows
- output = `#{Gem.ruby} #{File.expand_path("support/bundle.rb", Path.spec_dir)} install`
- raise output unless $?.success?
- ensure
- if path
- ENV["BUNDLE_PATH"] = old_path
- else
- ENV["BUNDLE_PATH__SYSTEM"] = old_path__system
- end
-
- ENV["BUNDLER_ORIG_BUNDLE_GEMFILE"] = old_orig_gemfile
- ENV["BUNDLE_GEMFILE"] = old_gemfile
- end
-
def test_gemfile
Path.test_gemfile
end
diff --git a/spec/bundler/support/setup.rb b/spec/bundler/support/setup.rb
new file mode 100644
index 0000000000..4ac2e5b472
--- /dev/null
+++ b/spec/bundler/support/setup.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require_relative "switch_rubygems"
+
+require_relative "rubygems_ext"
+Spec::Rubygems.install_test_deps
+
+require_relative "path"
+$LOAD_PATH.unshift(File.expand_path("../../lib", __dir__)) if Spec::Path.ruby_core?
diff --git a/spec/bundler/support/shards.rb b/spec/bundler/support/shards.rb
new file mode 100644
index 0000000000..ce33896539
--- /dev/null
+++ b/spec/bundler/support/shards.rb
@@ -0,0 +1,200 @@
+# frozen_string_literal: true
+
+# This classifies test files into 4 shards by running `bin/rspec --profile 10000`
+# to ensure balanced execution times. When adding new test files, it is recommended to
+# re-aggregate and adjust the shards to keep them balanced.
+# For now, please add new files to shard 'shard_d'.
+
+module Spec
+ module Shards
+ EXAMPLE_MAPPINGS = {
+ shard_a: [
+ "spec/runtime/setup_spec.rb",
+ "spec/commands/install_spec.rb",
+ "spec/commands/add_spec.rb",
+ "spec/install/gems/compact_index_spec.rb",
+ "spec/commands/config_spec.rb",
+ "spec/commands/pristine_spec.rb",
+ "spec/install/gemfile/path_spec.rb",
+ "spec/update/git_spec.rb",
+ "spec/commands/open_spec.rb",
+ "spec/commands/remove_spec.rb",
+ "spec/commands/show_spec.rb",
+ "spec/plugins/source/example_spec.rb",
+ "spec/commands/console_spec.rb",
+ "spec/runtime/require_spec.rb",
+ "spec/runtime/env_helpers_spec.rb",
+ "spec/runtime/gem_tasks_spec.rb",
+ "spec/install/gemfile_spec.rb",
+ "spec/commands/fund_spec.rb",
+ "spec/commands/init_spec.rb",
+ "spec/bundler/ruby_dsl_spec.rb",
+ "spec/bundler/mirror_spec.rb",
+ "spec/bundler/source/git/git_proxy_spec.rb",
+ "spec/bundler/source_list_spec.rb",
+ "spec/bundler/plugin/installer_spec.rb",
+ "spec/bundler/errors_spec.rb",
+ "spec/bundler/friendly_errors_spec.rb",
+ "spec/resolver/platform_spec.rb",
+ "spec/bundler/fetcher/downloader_spec.rb",
+ "spec/update/force_spec.rb",
+ "spec/bundler/env_spec.rb",
+ "spec/install/gems/mirror_spec.rb",
+ "spec/install/failure_spec.rb",
+ "spec/bundler/yaml_serializer_spec.rb",
+ "spec/bundler/environment_preserver_spec.rb",
+ "spec/install/gemfile/install_if_spec.rb",
+ "spec/install/gems/gemfile_source_header_spec.rb",
+ "spec/bundler/fetcher/base_spec.rb",
+ "spec/bundler/rubygems_integration_spec.rb",
+ "spec/bundler/worker_spec.rb",
+ "spec/bundler/dependency_spec.rb",
+ "spec/bundler/ui_spec.rb",
+ "spec/bundler/plugin/source_list_spec.rb",
+ "spec/bundler/source/path_spec.rb",
+ ],
+ shard_b: [
+ "spec/install/gemfile/git_spec.rb",
+ "spec/install/gems/standalone_spec.rb",
+ "spec/commands/lock_spec.rb",
+ "spec/cache/gems_spec.rb",
+ "spec/other/major_deprecation_spec.rb",
+ "spec/install/gems/dependency_api_spec.rb",
+ "spec/install/gemfile/gemspec_spec.rb",
+ "spec/plugins/install_spec.rb",
+ "spec/commands/binstubs_spec.rb",
+ "spec/install/gems/flex_spec.rb",
+ "spec/runtime/inline_spec.rb",
+ "spec/commands/post_bundle_message_spec.rb",
+ "spec/runtime/executable_spec.rb",
+ "spec/lock/git_spec.rb",
+ "spec/plugins/hook_spec.rb",
+ "spec/install/allow_offline_install_spec.rb",
+ "spec/install/gems/post_install_spec.rb",
+ "spec/install/gemfile/ruby_spec.rb",
+ "spec/install/security_policy_spec.rb",
+ "spec/install/yanked_spec.rb",
+ "spec/update/gemfile_spec.rb",
+ "spec/runtime/load_spec.rb",
+ "spec/plugins/command_spec.rb",
+ "spec/commands/version_spec.rb",
+ "spec/install/prereleases_spec.rb",
+ "spec/bundler/uri_credentials_filter_spec.rb",
+ "spec/bundler/plugin_spec.rb",
+ "spec/install/gems/mirror_probe_spec.rb",
+ "spec/plugins/list_spec.rb",
+ "spec/bundler/compact_index_client/parser_spec.rb",
+ "spec/bundler/gem_version_promoter_spec.rb",
+ "spec/other/cli_dispatch_spec.rb",
+ "spec/bundler/source/rubygems_spec.rb",
+ "spec/cache/platform_spec.rb",
+ "spec/update/gems/fund_spec.rb",
+ "spec/bundler/stub_specification_spec.rb",
+ "spec/bundler/retry_spec.rb",
+ "spec/bundler/installer/spec_installation_spec.rb",
+ "spec/bundler/spec_set_spec.rb",
+ "spec/quality_es_spec.rb",
+ "spec/bundler/index_spec.rb",
+ "spec/other/cli_man_pages_spec.rb",
+ ],
+ shard_c: [
+ "spec/commands/newgem_spec.rb",
+ "spec/commands/exec_spec.rb",
+ "spec/commands/clean_spec.rb",
+ "spec/commands/platform_spec.rb",
+ "spec/cache/git_spec.rb",
+ "spec/install/gemfile/groups_spec.rb",
+ "spec/commands/cache_spec.rb",
+ "spec/commands/check_spec.rb",
+ "spec/commands/list_spec.rb",
+ "spec/install/path_spec.rb",
+ "spec/bundler/cli_spec.rb",
+ "spec/install/bundler_spec.rb",
+ "spec/install/git_spec.rb",
+ "spec/commands/doctor_spec.rb",
+ "spec/bundler/dsl_spec.rb",
+ "spec/install/gems/fund_spec.rb",
+ "spec/install/gems/env_spec.rb",
+ "spec/bundler/ruby_version_spec.rb",
+ "spec/bundler/definition_spec.rb",
+ "spec/install/gemfile/eval_gemfile_spec.rb",
+ "spec/plugins/source_spec.rb",
+ "spec/install/gems/dependency_api_fallback_spec.rb",
+ "spec/plugins/uninstall_spec.rb",
+ "spec/bundler/plugin/index_spec.rb",
+ "spec/bundler/bundler_spec.rb",
+ "spec/bundler/fetcher_spec.rb",
+ "spec/bundler/source/rubygems/remote_spec.rb",
+ "spec/bundler/lockfile_parser_spec.rb",
+ "spec/cache/cache_path_spec.rb",
+ "spec/bundler/source/git_spec.rb",
+ "spec/bundler/source_spec.rb",
+ "spec/commands/ssl_spec.rb",
+ "spec/bundler/fetcher/compact_index_spec.rb",
+ "spec/bundler/plugin/api_spec.rb",
+ "spec/bundler/endpoint_specification_spec.rb",
+ "spec/bundler/fetcher/index_spec.rb",
+ "spec/bundler/settings/validator_spec.rb",
+ "spec/bundler/build_metadata_spec.rb",
+ "spec/bundler/current_ruby_spec.rb",
+ "spec/bundler/installer/gem_installer_spec.rb",
+ "spec/bundler/installer/parallel_installer_spec.rb",
+ "spec/bundler/cli_common_spec.rb",
+ "spec/bundler/ci_detector_spec.rb",
+ ],
+ shard_d: [
+ "spec/bundler/rubygems_ext_spec.rb",
+ "spec/bundler/resolver/cooldown_spec.rb",
+ "spec/install/cooldown_spec.rb",
+ "spec/commands/outdated_spec.rb",
+ "spec/commands/update_spec.rb",
+ "spec/lock/lockfile_spec.rb",
+ "spec/install/deploy_spec.rb",
+ "spec/install/gemfile/sources_spec.rb",
+ "spec/runtime/self_management_spec.rb",
+ "spec/install/gemfile/specific_platform_spec.rb",
+ "spec/commands/info_spec.rb",
+ "spec/install/gems/resolving_spec.rb",
+ "spec/install/gemfile/platform_spec.rb",
+ "spec/bundler/gem_helper_spec.rb",
+ "spec/install/global_cache_spec.rb",
+ "spec/runtime/platform_spec.rb",
+ "spec/update/gems/post_install_spec.rb",
+ "spec/install/gems/native_extensions_spec.rb",
+ "spec/install/force_spec.rb",
+ "spec/cache/path_spec.rb",
+ "spec/install/gemspecs_spec.rb",
+ "spec/commands/help_spec.rb",
+ "spec/bundler/shared_helpers_spec.rb",
+ "spec/bundler/settings_spec.rb",
+ "spec/resolver/basic_spec.rb",
+ "spec/install/gemfile/force_ruby_platform_spec.rb",
+ "spec/commands/licenses_spec.rb",
+ "spec/install/gemfile/lockfile_spec.rb",
+ "spec/bundler/fetcher/dependency_spec.rb",
+ "spec/quality_spec.rb",
+ "spec/bundler/remote_specification_spec.rb",
+ "spec/install/process_lock_spec.rb",
+ "spec/install/binstubs_spec.rb",
+ "spec/bundler/compact_index_client/updater_spec.rb",
+ "spec/bundler/ui/shell_spec.rb",
+ "spec/other/ext_spec.rb",
+ "spec/commands/issue_spec.rb",
+ "spec/update/path_spec.rb",
+ "spec/bundler/plugin/api/source_spec.rb",
+ "spec/install/gems/win32_spec.rb",
+ "spec/bundler/plugin/dsl_spec.rb",
+ "spec/runtime/requiring_spec.rb",
+ "spec/bundler/plugin/events_spec.rb",
+ "spec/bundler/resolver/candidate_spec.rb",
+ "spec/bundler/digest_spec.rb",
+ "spec/bundler/fetcher/gem_remote_fetcher_spec.rb",
+ "spec/bundler/uri_normalizer_spec.rb",
+ "spec/install/gems/no_build_extension_spec.rb",
+ "spec/install/gems/no_install_plugin_spec.rb",
+ "spec/bundler/override_spec.rb",
+ "spec/install/gemfile/override_spec.rb",
+ ],
+ }.freeze
+ end
+end
diff --git a/spec/bundler/support/silent_logger.rb b/spec/bundler/support/silent_logger.rb
deleted file mode 100644
index 4b270330fd..0000000000
--- a/spec/bundler/support/silent_logger.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-# frozen_string_literal: true
-
-require "webrick"
-module Spec
- class SilentLogger < WEBrick::BasicLog
- def initialize(log_file = nil, level = nil)
- super(log_file, level || FATAL)
- end
- end
-end
diff --git a/spec/bundler/support/subprocess.rb b/spec/bundler/support/subprocess.rb
index a4842166b9..91db80da48 100644
--- a/spec/bundler/support/subprocess.rb
+++ b/spec/bundler/support/subprocess.rb
@@ -22,6 +22,10 @@ module Spec
last_command.stderr
end
+ def stdboth
+ last_command.stdboth
+ end
+
def exitstatus
last_command.exitstatus
end
@@ -34,11 +38,14 @@ module Spec
dir = options[:dir]
env = options[:env] || {}
- command_execution = CommandExecution.new(cmd.to_s, working_directory: dir, timeout: options[:timeout] || 60)
+ command_execution = CommandExecution.new(cmd.to_s, timeout: options[:timeout] || 60)
+
+ open3_opts = {}
+ open3_opts[:chdir] = dir if dir
require "open3"
require "shellwords"
- Open3.popen3(env, *cmd.shellsplit, chdir: dir) do |stdin, stdout, stderr, wait_thr|
+ Open3.popen3(env, *cmd.shellsplit, **open3_opts) do |stdin, stdout, stderr, wait_thr|
yield stdin, stdout, wait_thr if block_given?
stdin.close
diff --git a/spec/bundler/support/switch_rubygems.rb b/spec/bundler/support/switch_rubygems.rb
index a138d22333..640b9f83b7 100644
--- a/spec/bundler/support/switch_rubygems.rb
+++ b/spec/bundler/support/switch_rubygems.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
require_relative "rubygems_version_manager"
+ENV["RGV"] ||= "."
RubygemsVersionManager.new(ENV["RGV"]).switch
diff --git a/spec/bundler/support/the_bundle.rb b/spec/bundler/support/the_bundle.rb
index f252a4515b..452abd7d41 100644
--- a/spec/bundler/support/the_bundle.rb
+++ b/spec/bundler/support/the_bundle.rb
@@ -8,10 +8,8 @@ module Spec
attr_accessor :bundle_dir
- def initialize(opts = {})
- opts = opts.dup
- @bundle_dir = Pathname.new(opts.delete(:bundle_dir) { bundled_app })
- raise "Too many options! #{opts}" unless opts.empty?
+ def initialize
+ @bundle_dir = Pathname.new(bundled_app)
end
def to_s
@@ -28,8 +26,16 @@ module Spec
end
def locked_gems
- raise "Cannot read lockfile if it doesn't exist" unless locked?
+ raise ArgumentError, "Cannot read lockfile if it doesn't exist" unless locked?
Bundler::LockfileParser.new(lockfile.read)
end
+
+ def locked_specs
+ locked_gems.specs.map(&:full_name)
+ end
+
+ def locked_platforms
+ locked_gems.platforms.map(&:to_s)
+ end
end
end
diff --git a/spec/bundler/update/force_spec.rb b/spec/bundler/update/force_spec.rb
new file mode 100644
index 0000000000..325f58088a
--- /dev/null
+++ b/spec/bundler/update/force_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+RSpec.describe "bundle update" do
+ before :each do
+ install_gemfile <<-G
+ source "https://gem.repo1"
+ gem "myrack"
+ G
+ end
+
+ it "re-installs installed gems with --force" do
+ myrack_lib = default_bundle_path("gems/myrack-1.0.0/lib/myrack.rb")
+ myrack_lib.open("w") {|f| f.write("blah blah blah") }
+ bundle :update, force: true
+
+ expect(out).to include "Installing myrack 1.0.0"
+ expect(myrack_lib.open(&:read)).to eq("MYRACK = '1.0.0'\n")
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+
+ it "re-installs installed gems with --redownload" do
+ myrack_lib = default_bundle_path("gems/myrack-1.0.0/lib/myrack.rb")
+ myrack_lib.open("w") {|f| f.write("blah blah blah") }
+ bundle :update, redownload: true
+
+ expect(out).to include "Installing myrack 1.0.0"
+ expect(myrack_lib.open(&:read)).to eq("MYRACK = '1.0.0'\n")
+ expect(the_bundle).to include_gems "myrack 1.0.0"
+ end
+end
diff --git a/spec/bundler/update/gemfile_spec.rb b/spec/bundler/update/gemfile_spec.rb
index cb94799061..f8849640b6 100644
--- a/spec/bundler/update/gemfile_spec.rb
+++ b/spec/bundler/update/gemfile_spec.rb
@@ -25,7 +25,7 @@ RSpec.describe "bundle update" do
gem 'myrack'
G
- bundle "config set --local gemfile #{bundled_app("NotGemfile")}"
+ bundle_config "gemfile #{bundled_app("NotGemfile")}"
bundle :install
end
diff --git a/spec/bundler/update/git_spec.rb b/spec/bundler/update/git_spec.rb
index 64124e0920..526e988ab7 100644
--- a/spec/bundler/update/git_spec.rb
+++ b/spec/bundler/update/git_spec.rb
@@ -87,7 +87,7 @@ RSpec.describe "bundle update" do
gem "foo", "1.0", :git => "#{lib_path("foo_one")}"
G
- FileUtils.rm_rf lib_path("foo_one")
+ FileUtils.rm_r lib_path("foo_one")
install_gemfile <<-G
source "https://gem.repo1"
@@ -199,7 +199,7 @@ RSpec.describe "bundle update" do
gem "foo", :git => "#{lib_path("foo-1.0")}"
G
- lib_path("foo-1.0").join(".git").rmtree
+ FileUtils.rm_rf lib_path("foo-1.0").join(".git")
bundle :update, all: true, raise_on_error: false
expect(err).to include(lib_path("foo-1.0").to_s).
@@ -334,7 +334,7 @@ RSpec.describe "bundle update" do
myrack
#{checksums}
BUNDLED WITH
- #{Bundler::VERSION}
+ #{Bundler::VERSION}
G
end
end
diff --git a/spec/bundler/update/redownload_spec.rb b/spec/bundler/update/redownload_spec.rb
deleted file mode 100644
index 66437fb938..0000000000
--- a/spec/bundler/update/redownload_spec.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.describe "bundle update" do
- before :each do
- install_gemfile <<-G
- source "https://gem.repo1"
- gem "myrack"
- G
- end
-
- describe "with --force" do
- it "shows a deprecation when single flag passed", bundler: 2 do
- bundle "update myrack --force"
- expect(err).to include "[DEPRECATED] The `--force` option has been renamed to `--redownload`"
- end
-
- it "shows a deprecation when multiple flags passed", bundler: 2 do
- bundle "update myrack --no-color --force"
- expect(err).to include "[DEPRECATED] The `--force` option has been renamed to `--redownload`"
- end
- end
-
- describe "with --redownload" do
- it "does not show a deprecation when single flag passed" do
- bundle "update myrack --redownload"
- expect(err).not_to include "[DEPRECATED] The `--force` option has been renamed to `--redownload`"
- end
-
- it "does not show a deprecation when single multiple flags passed" do
- bundle "update myrack --no-color --redownload"
- expect(err).not_to include "[DEPRECATED] The `--force` option has been renamed to `--redownload`"
- end
-
- it "re-installs installed gems" do
- myrack_lib = default_bundle_path("gems/myrack-1.0.0/lib/myrack.rb")
- myrack_lib.open("w") {|f| f.write("blah blah blah") }
- bundle :update, redownload: true
-
- expect(out).to include "Installing myrack 1.0.0"
- expect(myrack_lib.open(&:read)).to eq("MYRACK = '1.0.0'\n")
- expect(the_bundle).to include_gems "myrack 1.0.0"
- end
- end
-end