diff options
Diffstat (limited to 'spec/bundler')
59 files changed, 1191 insertions, 426 deletions
diff --git a/spec/bundler/bundler/bundler_spec.rb b/spec/bundler/bundler/bundler_spec.rb index 6a2e435e54..4cd21375b5 100644 --- a/spec/bundler/bundler/bundler_spec.rb +++ b/spec/bundler/bundler/bundler_spec.rb @@ -233,8 +233,8 @@ RSpec.describe Bundler do allow(Bundler).to receive(:root).and_return(bundled_app) - Bundler.mkdir_p(bundled_app.join("foo", "bar")) - expect(bundled_app.join("foo", "bar")).to exist + Bundler.mkdir_p(bundled_app("foo", "bar")) + expect(bundled_app("foo", "bar")).to exist end end diff --git a/spec/bundler/bundler/compact_index_client/parser_spec.rb b/spec/bundler/bundler/compact_index_client/parser_spec.rb new file mode 100644 index 0000000000..45a08fd9ff --- /dev/null +++ b/spec/bundler/bundler/compact_index_client/parser_spec.rb @@ -0,0 +1,259 @@ +# frozen_string_literal: true + +require "bundler/compact_index_client" +require "bundler/compact_index_client/parser" + +TestCompactIndexClient = Struct.new(:names, :versions, :info_data) do + # Requiring the checksum to match the input data helps ensure + # that we are parsing the correct checksum from the versions file + def info(name, checksum) + info_data.dig(name, checksum) + end + + def set_info_data(name, value) + info_data[name] = value + end +end + +RSpec.describe Bundler::CompactIndexClient::Parser do + subject(:parser) { described_class.new(compact_index) } + + let(:compact_index) { TestCompactIndexClient.new(names, versions, info_data) } + let(:names) { "a\nb\nc\n" } + let(:versions) { <<~VERSIONS.dup } + 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 bbb222 + c 3.0.0,3.0.3,3.3.3 ccc333 + c -3.0.3 ccc333yanked + VERSIONS + let(:info_data) do + { + "a" => { "aaa111" => a_info }, + "b" => { "bbb222" => b_info }, + "c" => { "ccc333yanked" => c_info }, + } + end + let(:a_info) { <<~INFO.dup } + --- + 1.0.0 |checksum:aaa1,ruby:>= 3.0.0,rubygems:>= 3.2.3 + 1.0.1 |checksum:aaa2,ruby:>= 3.0.0,rubygems:>= 3.2.3 + 1.1.0 |checksum:aaa3,ruby:>= 3.0.0,rubygems:>= 3.2.3 + INFO + let(:b_info) { <<~INFO } + 2.0.0 a:~> 1.0&<= 3.0|checksum:bbb1 + 2.0.0-java a:~> 1.0&<= 3.0|checksum:bbb2 + 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 + INFO + + describe "#available?" do + it "returns true versions are available" do + expect(parser).to be_available + end + + it "returns true when versions has only one gem" do + compact_index.versions = +"a 1.0.0 aaa1\n" + expect(parser).to be_available + end + + it "returns true when versions has a gem and a header" do + compact_index.versions = +"---\na 1.0.0 aaa1\n" + expect(parser).to be_available + end + + it "returns true when versions has a gem and a header with header data" do + compact_index.versions = +"created_at: 2024-05-01T00:00:04Z\n---\na 1.0.0 aaa1\n" + expect(parser).to be_available + end + + it "returns false when versions has only the header" do + compact_index.versions = +"---\n" + expect(parser).not_to be_available + end + + it "returns false when versions has only the header with header data" do + compact_index.versions = +"created_at: 2024-05-01T00:00:04Z\n---\n" + expect(parser).not_to be_available + end + + it "returns false when versions index is not available" do + compact_index.versions = nil + expect(parser).not_to be_available + end + + it "returns false when versions is empty" do + compact_index.versions = +"" + expect(parser).not_to be_available + end + + it "returns false when versions ends improperly without a newline" do + compact_index.versions = "a 1.0.0 aaa1" + expect(parser).not_to be_available + end + end + + describe "#names" do + it "returns the names" do + expect(parser.names).to eq(%w[a b c]) + end + + it "returns an empty array when names is empty" do + compact_index.names = "" + expect(parser.names).to eq([]) + end + + it "returns an empty array when names is not readable" do + compact_index.names = nil + expect(parser.names).to eq([]) + end + end + + describe "#versions" do + it "returns the versions" do + expect(parser.versions).to eq( + "a" => [ + ["a", "1.0.0"], + ["a", "1.0.1"], + ["a", "1.1.0"], + ], + "b" => [ + ["b", "2.0.0"], + ["b", "2.0.0", "java"], + ], + "c" => [ + ["c", "3.0.0"], + ["c", "3.3.3"], + ], + ) + end + + it "returns an empty hash when versions is empty" do + compact_index.versions = "" + expect(parser.versions).to eq({}) + end + + it "returns an empty hash when versions is not readable" do + compact_index.versions = nil + expect(parser.versions).to eq({}) + end + end + + describe "#info" do + it "returns the info for example gem 'a' which has no deps" do + expect(parser.info("a")).to eq( + [ + [ + "a", + "1.0.0", + nil, + [], + [ + ["checksum", ["aaa1"]], + ["ruby", [">= 3.0.0"]], + ["rubygems", [">= 3.2.3"]], + ], + ], + [ + "a", + "1.0.1", + nil, + [], + [ + ["checksum", ["aaa2"]], + ["ruby", [">= 3.0.0"]], + ["rubygems", [">= 3.2.3"]], + ], + ], + [ + "a", + "1.1.0", + nil, + [], + [ + ["checksum", ["aaa3"]], + ["ruby", [">= 3.0.0"]], + ["rubygems", [">= 3.2.3"]], + ], + ], + ] + ) + end + + it "returns the info for example gem 'b' which has platform and compound deps" do + expect(parser.info("b")).to eq( + [ + [ + "b", + "2.0.0", + nil, + [ + ["a", ["~> 1.0", "<= 3.0"]], + ], + [ + ["checksum", ["bbb1"]], + ], + ], + [ + "b", + "2.0.0", + "java", + [ + ["a", ["~> 1.0", "<= 3.0"]], + ], + [ + ["checksum", ["bbb2"]], + ], + ], + ] + ) + end + + it "returns the info for example gem 'c' which has deps and yanked version (requires use of correct info checksum)" do + expect(parser.info("c")).to eq( + [ + [ + "c", + "3.0.0", + nil, + [ + ["a", ["= 1.0.0"]], + ["b", ["~> 2.0"]], + ], + [ + ["checksum", ["ccc1"]], + ["ruby", [">= 2.7.0"]], + ["rubygems", [">= 3.0.0"]], + ], + ], + [ + "c", + "3.3.3", + nil, + [ + ["a", [">= 1.1.0"]], + ["b", ["~> 2.0"]], + ], + [ + ["checksum", ["ccc3"]], + ["ruby", [">= 3.0.0"]], + ["rubygems", [">= 3.2.3"]], + ], + ], + ] + ) + end + + it "returns an empty array when the info is empty" do + compact_index.set_info_data("a", {}) + expect(parser.info("a")).to eq([]) + end + + it "returns an empty array when the info is not readable" do + expect(parser.info("d")).to eq([]) + 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 6eed88ca9e..87a73d993f 100644 --- a/spec/bundler/bundler/compact_index_client/updater_spec.rb +++ b/spec/bundler/bundler/compact_index_client/updater_spec.rb @@ -119,23 +119,7 @@ RSpec.describe Bundler::CompactIndexClient::Updater do context "without an etag file" do let(:headers) do - { - "Range" => "bytes=2-", - # This MD5 feature should be deleted after sufficient time has passed since release. - # From then on, requests that still don't have a saved etag will be made without this header. - "If-None-Match" => %("#{Digest::MD5.hexdigest(local_body)}"), - } - end - - it "saves only the etag_path if generated etag matches" do - expect(fetcher).to receive(:call).once.with(remote_path, headers).and_return(response) - allow(response).to receive(:is_a?).with(Gem::Net::HTTPPartialContent) { false } - allow(response).to receive(:is_a?).with(Gem::Net::HTTPNotModified) { true } - - updater.update(remote_path, local_path, etag_path) - - expect(local_path.read).to eq("abc") - expect(%("#{etag_path.read}")).to eq(headers["If-None-Match"]) + { "Range" => "bytes=2-" } end it "appends the file" do diff --git a/spec/bundler/bundler/dsl_spec.rb b/spec/bundler/bundler/dsl_spec.rb index 3c3b6c26c3..7b6d080593 100644 --- a/spec/bundler/bundler/dsl_spec.rb +++ b/spec/bundler/bundler/dsl_spec.rb @@ -175,7 +175,7 @@ RSpec.describe Bundler::Dsl 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 { 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 '\}'). Bundler cannot continue./) + 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 @@ -322,7 +322,7 @@ RSpec.describe Bundler::Dsl do it "will raise a Bundler::GemfileError" do gemfile "gem 'foo', :path => /unquoted/string/syntax/error" expect { Bundler::Dsl.evaluate(bundled_app_gemfile, nil, true) }. - to raise_error(Bundler::GemfileError, /There was an error parsing `Gemfile`:( compile error -)? unknown regexp options - trg.+ Bundler cannot continue./) + to raise_error(Bundler::GemfileError, /There was an error parsing `Gemfile`:( compile error -)?.+?unknown regexp options - trg.+ Bundler cannot continue./m) end end diff --git a/spec/bundler/bundler/env_spec.rb b/spec/bundler/bundler/env_spec.rb index 7997cb0c40..c15aa9af67 100644 --- a/spec/bundler/bundler/env_spec.rb +++ b/spec/bundler/bundler/env_spec.rb @@ -150,7 +150,7 @@ RSpec.describe Bundler::Env do before do gemfile("source \"#{file_uri_for(gem_repo1)}\"; gemspec") - File.open(bundled_app.join("foo.gemspec"), "wb") do |f| + File.open(bundled_app("foo.gemspec"), "wb") do |f| f.write(gemspec) end diff --git a/spec/bundler/bundler/fetcher/compact_index_spec.rb b/spec/bundler/bundler/fetcher/compact_index_spec.rb index a988171f34..aa536673d9 100644 --- a/spec/bundler/bundler/fetcher/compact_index_spec.rb +++ b/spec/bundler/bundler/fetcher/compact_index_spec.rb @@ -4,14 +4,18 @@ require "bundler/compact_index_client" RSpec.describe Bundler::Fetcher::CompactIndex do - let(:downloader) { double(:downloader) } + let(:response) { double(:response) } + let(:downloader) { double(:downloader, fetch: response) } let(:display_uri) { Gem::URI("http://sampleuri.com") } let(:remote) { double(:remote, cache_slug: "lsjdf", uri: display_uri) } let(:gem_remote_fetcher) { nil } let(:compact_index) { described_class.new(downloader, remote, display_uri, gem_remote_fetcher) } + let(:compact_index_client) { double(:compact_index_client, available?: true, info: [["lskdjf", "1", nil, [], []]]) } before do + allow(response).to receive(:is_a?).with(Gem::Net::HTTPNotModified).and_return(true) allow(compact_index).to receive(:log_specs) {} + allow(compact_index).to receive(:compact_index_client).and_return(compact_index_client) end describe "#specs_for_names" do @@ -32,11 +36,6 @@ RSpec.describe Bundler::Fetcher::CompactIndex do end describe "#available?" do - before do - allow(compact_index).to receive(:compact_index_client). - and_return(double(:compact_index_client, update_and_parse_checksums!: true)) - end - it "returns true" do expect(compact_index).to be_available end diff --git a/spec/bundler/bundler/gem_helper_spec.rb b/spec/bundler/bundler/gem_helper_spec.rb index 940e5df9de..187dab0e7c 100644 --- a/spec/bundler/bundler/gem_helper_spec.rb +++ b/spec/bundler/bundler/gem_helper_spec.rb @@ -11,7 +11,7 @@ RSpec.describe Bundler::GemHelper do 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" - sys_exec("git config --global init.defaultBranch main") + git("config --global init.defaultBranch main") bundle "gem #{app_name}" prepare_gemspec(app_gemspec_path) end @@ -253,11 +253,11 @@ RSpec.describe Bundler::GemHelper do end before do - sys_exec("git init", dir: app_path) - sys_exec("git config user.email \"you@example.com\"", dir: app_path) - sys_exec("git config user.name \"name\"", dir: app_path) - sys_exec("git config commit.gpgsign false", dir: app_path) - sys_exec("git config push.default simple", dir: app_path) + git("init", app_path) + git("config user.email \"you@example.com\"", app_path) + git("config user.name \"name\"", app_path) + git("config commit.gpgsign false", app_path) + git("config push.default simple", app_path) # silence messages allow(Bundler.ui).to receive(:confirm) @@ -271,13 +271,13 @@ RSpec.describe Bundler::GemHelper do end it "when there are uncommitted files" do - sys_exec("git add .", dir: app_path) + git("add .", app_path) expect { Rake.application["release"].invoke }. to raise_error("There are files that need to be committed first.") end it "when there is no git remote" do - sys_exec("git commit -a -m \"initial commit\"", dir: app_path) + git("commit -a -m \"initial commit\"", app_path) expect { Rake.application["release"].invoke }.to raise_error(RuntimeError) end end @@ -286,8 +286,8 @@ RSpec.describe Bundler::GemHelper do let(:repo) { build_git("foo", bare: true) } before do - sys_exec("git remote add origin #{file_uri_for(repo.path)}", dir: app_path) - sys_exec('git commit -a -m "initial commit"', dir: app_path) + git("remote add origin #{file_uri_for(repo.path)}", app_path) + git('commit -a -m "initial commit"', app_path) end context "on releasing" do @@ -296,7 +296,7 @@ RSpec.describe Bundler::GemHelper do mock_confirm_message "Tagged v#{app_version}." mock_confirm_message "Pushed git commits and release tag." - sys_exec("git push -u origin main", dir: app_path) + git("push -u origin main", app_path) end it "calls rubygem_push with proper arguments" do @@ -314,8 +314,8 @@ RSpec.describe Bundler::GemHelper do it "also works when releasing from an ambiguous reference" do # Create a branch with the same name as the tag - sys_exec("git checkout -b v#{app_version}", dir: app_path) - sys_exec("git push -u origin v#{app_version}", dir: app_path) + git("checkout -b v#{app_version}", app_path) + git("push -u origin v#{app_version}", app_path) expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s) @@ -323,7 +323,7 @@ RSpec.describe Bundler::GemHelper do end it "also works with releasing from a branch not yet pushed" do - sys_exec("git checkout -b module_function", dir: app_path) + git("checkout -b module_function", app_path) expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s) @@ -337,7 +337,7 @@ RSpec.describe Bundler::GemHelper do mock_build_message app_name, app_version mock_confirm_message "Pushed git commits and release tag." - sys_exec("git push -u origin main", dir: app_path) + git("push -u origin main", app_path) expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s) end @@ -353,7 +353,7 @@ RSpec.describe Bundler::GemHelper do mock_confirm_message "Tag v#{app_version} has already been created." expect(subject).to receive(:rubygem_push).with(app_gem_path.to_s) - sys_exec("git tag -a -m \"Version #{app_version}\" v#{app_version}", dir: app_path) + git("tag -a -m \"Version #{app_version}\" v#{app_version}", app_path) Rake.application["release"].invoke end @@ -374,10 +374,10 @@ RSpec.describe Bundler::GemHelper do end before do - sys_exec("git init", dir: app_path) - sys_exec("git config user.email \"you@example.com\"", dir: app_path) - sys_exec("git config user.name \"name\"", dir: app_path) - sys_exec("git config push.gpgsign simple", dir: app_path) + git("init", app_path) + git("config user.email \"you@example.com\"", app_path) + git("config user.name \"name\"", app_path) + git("config push.gpgsign simple", app_path) # silence messages allow(Bundler.ui).to receive(:confirm) diff --git a/spec/bundler/bundler/installer/gem_installer_spec.rb b/spec/bundler/bundler/installer/gem_installer_spec.rb index 4b6a07f344..ea506c36c8 100644 --- a/spec/bundler/bundler/installer/gem_installer_spec.rb +++ b/spec/bundler/bundler/installer/gem_installer_spec.rb @@ -14,7 +14,7 @@ RSpec.describe Bundler::GemInstaller do it "invokes install method with empty build_args" do allow(spec_source).to receive(:install).with( spec, - { force: false, ensure_builtin_gems_cached: false, build_args: [], previous_spec: nil } + { force: false, build_args: [], previous_spec: nil } ) subject.install_from_spec end @@ -28,7 +28,7 @@ RSpec.describe Bundler::GemInstaller do allow(Bundler.settings).to receive(:[]).with("build.dummy").and_return("--with-dummy-config=dummy") expect(spec_source).to receive(:install).with( spec, - { force: false, ensure_builtin_gems_cached: false, build_args: ["--with-dummy-config=dummy"], previous_spec: nil } + { force: false, build_args: ["--with-dummy-config=dummy"], previous_spec: nil } ) subject.install_from_spec end @@ -42,7 +42,7 @@ RSpec.describe Bundler::GemInstaller do 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, - { force: false, ensure_builtin_gems_cached: false, build_args: ["--with-dummy-config=dummy", "--with-another-dummy-config"], previous_spec: nil } + { force: false, build_args: ["--with-dummy-config=dummy", "--with-another-dummy-config"], previous_spec: nil } ) subject.install_from_spec end diff --git a/spec/bundler/bundler/plugin_spec.rb b/spec/bundler/bundler/plugin_spec.rb index f41b4eff3a..3ac7b251a3 100644 --- a/spec/bundler/bundler/plugin_spec.rb +++ b/spec/bundler/bundler/plugin_spec.rb @@ -247,7 +247,7 @@ RSpec.describe Bundler::Plugin do end it "returns plugin dir in app .bundle path" do - expect(subject.root).to eq(bundled_app.join(".bundle/plugin")) + expect(subject.root).to eq(bundled_app(".bundle/plugin")) end end @@ -257,7 +257,7 @@ RSpec.describe Bundler::Plugin do end it "returns plugin dir in global bundle path" do - expect(subject.root).to eq(home.join(".bundle/plugin")) + expect(subject.root).to eq(home(".bundle/plugin")) end end end diff --git a/spec/bundler/bundler/settings_spec.rb b/spec/bundler/bundler/settings_spec.rb index 634e0faf91..768372c608 100644 --- a/spec/bundler/bundler/settings_spec.rb +++ b/spec/bundler/bundler/settings_spec.rb @@ -6,12 +6,18 @@ RSpec.describe Bundler::Settings do subject(:settings) { described_class.new(bundled_app) } describe "#set_local" do - context "when the local config file is not found" do + context "root is nil" do subject(:settings) { described_class.new(nil) } - it "raises a GemfileNotFound error with explanation" do - expect { subject.set_local("foo", "bar") }. - to raise_error(Bundler::GemfileNotFound, "Could not locate Gemfile") + before do + allow(Pathname).to receive(:new).and_call_original + allow(Pathname).to receive(:new).with(".bundle").and_return home(".bundle") + end + + it "works" do + subject.set_local("foo", "bar") + + expect(subject["foo"]).to eq("bar") 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 1450316d59..f7c883eed4 100644 --- a/spec/bundler/bundler/source/git/git_proxy_spec.rb +++ b/spec/bundler/bundler/source/git/git_proxy_spec.rb @@ -197,4 +197,18 @@ RSpec.describe Bundler::Source::Git::GitProxy do expect(Pathname.new(bundled_app("canary"))).not_to exist 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) } + + it "retries without --depth when git url is http and 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]) + + subject.checkout + end + end end diff --git a/spec/bundler/cache/gems_spec.rb b/spec/bundler/cache/gems_spec.rb index abbc2c3cf2..3c734b79c3 100644 --- a/spec/bundler/cache/gems_spec.rb +++ b/spec/bundler/cache/gems_spec.rb @@ -93,68 +93,80 @@ RSpec.describe "bundle cache" do let(:default_json_version) { ruby "gem 'json'; require 'json'; puts JSON::VERSION" } before :each do - build_repo2 do - build_gem "json", default_json_version - end - build_gem "json", default_json_version, to_system: true, default: true end - it "uses builtin gems when installing to system gems" do - bundle "config set path.system true" - install_gemfile %(source "#{file_uri_for(gem_repo1)}"; gem 'json', '#{default_json_version}'), verbose: true - expect(out).to include("Using json #{default_json_version}") - end + context "when a remote gem is available for caching" do + before do + build_repo2 do + build_gem "json", default_json_version + end + end - it "caches remote and builtin gems" do - install_gemfile <<-G - source "#{file_uri_for(gem_repo2)}" - gem 'json', '#{default_json_version}' - gem 'rack', '1.0.0' - G + it "uses remote gems when installing to system gems" do + bundle "config set path.system true" + install_gemfile %(source "#{file_uri_for(gem_repo2)}"; gem 'json', '#{default_json_version}'), verbose: true + expect(out).to include("Installing json #{default_json_version}") + end - bundle :cache - expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist - expect(bundled_app("vendor/cache/json-#{default_json_version}.gem")).to exist - end + it "caches remote and builtin gems" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + gem 'json', '#{default_json_version}' + gem 'rack', '1.0.0' + G - it "caches builtin gems when cache_all_platforms is set" do - gemfile <<-G - source "#{file_uri_for(gem_repo2)}" - gem "json" - G + bundle :cache + expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist + expect(bundled_app("vendor/cache/json-#{default_json_version}.gem")).to exist + end - bundle "config set cache_all_platforms true" + it "caches builtin gems when cache_all_platforms is set" do + gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + gem "json" + G - bundle :cache - expect(bundled_app("vendor/cache/json-#{default_json_version}.gem")).to exist - end + bundle "config set cache_all_platforms true" - it "doesn't make remote request after caching the gem" do - build_gem "builtin_gem_2", "1.0.2", path: bundled_app("vendor/cache") do |s| - s.summary = "This builtin_gem is bundled with Ruby" + bundle :cache + expect(bundled_app("vendor/cache/json-#{default_json_version}.gem")).to exist end - install_gemfile <<-G - source "#{file_uri_for(gem_repo2)}" - gem 'builtin_gem_2', '1.0.2' - G + it "doesn't make remote request after caching the gem" do + build_gem "builtin_gem_2", "1.0.2", path: bundled_app("vendor/cache") do |s| + s.summary = "This builtin_gem is bundled with Ruby" + end - bundle "install --local" - expect(the_bundle).to include_gems("builtin_gem_2 1.0.2") + install_gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + gem 'builtin_gem_2', '1.0.2' + G + + bundle "install --local" + expect(the_bundle).to include_gems("builtin_gem_2 1.0.2") + end end - it "errors if the builtin gem isn't available to cache" do - bundle "config set path.system true" + context "when a remote gem is not available for caching" do + it "uses builtin gems when installing to system gems" do + bundle "config set path.system true" + install_gemfile %(source "#{file_uri_for(gem_repo1)}"; gem 'json', '#{default_json_version}'), verbose: true + expect(out).to include("Using json #{default_json_version}") + end - install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem 'json', '#{default_json_version}' - G + it "errors when explicitly caching" do + bundle "config set path.system true" - bundle :cache, raise_on_error: false - expect(exitstatus).to_not eq(0) - expect(err).to include("json-#{default_json_version} is built in to Ruby, and can't be cached") + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'json', '#{default_json_version}' + G + + bundle :cache, raise_on_error: false + expect(exitstatus).to_not eq(0) + expect(err).to include("json-#{default_json_version} is built in to Ruby, and can't be cached") + end end end diff --git a/spec/bundler/cache/git_spec.rb b/spec/bundler/cache/git_spec.rb index 4b3cd4d2eb..51897ebe20 100644 --- a/spec/bundler/cache/git_spec.rb +++ b/spec/bundler/cache/git_spec.rb @@ -164,8 +164,8 @@ RSpec.describe "bundle cache with git" do s.add_dependency "submodule" end - sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", dir: lib_path("has_submodule-1.0") - sys_exec "git commit -m \"submodulator\"", dir: lib_path("has_submodule-1.0") + git "submodule add #{lib_path("submodule-1.0")} submodule-1.0", lib_path("has_submodule-1.0") + git "commit -m \"submodulator\"", lib_path("has_submodule-1.0") install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" diff --git a/spec/bundler/commands/cache_spec.rb b/spec/bundler/commands/cache_spec.rb index 70e2c84961..37d8b3ac1a 100644 --- a/spec/bundler/commands/cache_spec.rb +++ b/spec/bundler/commands/cache_spec.rb @@ -386,6 +386,66 @@ RSpec.describe "bundle install with gem sources" do expect(the_bundle).to include_gems "rack 1.0.0" end + it "uses cached gems for secondary sources when cache_all_platforms configured" do + build_repo4 do + build_gem "foo", "1.0.0" do |s| + s.platform = "x86_64-linux" + end + + build_gem "foo", "1.0.0" do |s| + s.platform = "arm64-darwin" + end + end + + gemfile <<~G + source "https://gems.repo2" + + source "https://gems.repo4" do + gem "foo" + end + G + + lockfile <<~L + GEM + remote: https://gems.repo2/ + specs: + + GEM + remote: https://gems.repo4/ + specs: + foo (1.0.0-x86_64-linux) + foo (1.0.0-arm64-darwin) + + PLATFORMS + arm64-darwin + ruby + x86_64-linux + + DEPENDENCIES + foo + + BUNDLED WITH + #{Bundler::VERSION} + L + + simulate_platform "x86_64-linux" do + bundle "config set cache_all_platforms true" + bundle "config set 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 + + # delete compact index cache + FileUtils.rm_rf home(".bundle/cache/compact_index") + + bundle "install", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + + expect(the_bundle).to include_gems "foo 1.0.0 x86_64-linux" + end + end + it "does not reinstall already-installed gems" do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" diff --git a/spec/bundler/commands/check_spec.rb b/spec/bundler/commands/check_spec.rb index 02f9bb5b7a..4bad16a55e 100644 --- a/spec/bundler/commands/check_spec.rb +++ b/spec/bundler/commands/check_spec.rb @@ -447,7 +447,7 @@ RSpec.describe "bundle check" do build_gem "dex-dispatch-engine" end - build_lib("bundle-check-issue", path: tmp.join("bundle-check-issue")) do |s| + build_lib("bundle-check-issue", path: tmp("bundle-check-issue")) do |s| s.write "Gemfile", <<-G source "https://localgemserver.test" @@ -461,14 +461,14 @@ RSpec.describe "bundle check" do s.add_dependency "awesome_print" end - bundle "install", artifice: "compact_index_extra", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }, dir: tmp.join("bundle-check-issue") + bundle "install", artifice: "compact_index_extra", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }, dir: tmp("bundle-check-issue") end it "does not corrupt lockfile when changing version" do - version_file = tmp.join("bundle-check-issue/bundle-check-issue.gemspec") + version_file = tmp("bundle-check-issue/bundle-check-issue.gemspec") File.write(version_file, File.read(version_file).gsub(/s\.version = .+/, "s.version = '9999'")) - bundle "check --verbose", dir: tmp.join("bundle-check-issue") + bundle "check --verbose", dir: tmp("bundle-check-issue") checksums = checksums_section_when_existing do |c| c.checksum gem_repo4, "awesome_print", "1.0" @@ -476,7 +476,7 @@ RSpec.describe "bundle check" do c.checksum gem_repo2, "dex-dispatch-engine", "1.0" end - expect(File.read(tmp.join("bundle-check-issue/Gemfile.lock"))).to eq <<~L + expect(File.read(tmp("bundle-check-issue/Gemfile.lock"))).to eq <<~L PATH remote: . specs: diff --git a/spec/bundler/commands/config_spec.rb b/spec/bundler/commands/config_spec.rb index 547fd2d869..d6a30eae5b 100644 --- a/spec/bundler/commands/config_spec.rb +++ b/spec/bundler/commands/config_spec.rb @@ -79,6 +79,14 @@ RSpec.describe ".bundle/config" do expect(home(".bundle/config")).to exist end + it "does not list global settings as local" do + bundle "config set --global foo bar" + bundle "config list", dir: home + + expect(out).to include("for the current user") + expect(out).not_to include("for your local app") + end + it "works with an absolute path" do ENV["BUNDLE_APP_CONFIG"] = tmp("foo/bar").to_s bundle "config set --local path vendor/bundle" @@ -350,6 +358,12 @@ end E expect(out).to eq("http://gems.example.org/ => http://gem-mirror.example.org/") end + + it "allows configuring fallback timeout for each mirror, and does not duplicate configs", rubygems: ">= 3.5.12" do + bundle "config set --global mirror.https://rubygems.org.fallback_timeout 1" + bundle "config set --global mirror.https://rubygems.org.fallback_timeout 2" + expect(File.read(home(".bundle/config"))).to include("BUNDLE_MIRROR").once + end end describe "quoting" do @@ -439,7 +453,7 @@ E it "does not make bundler crash and ignores the configuration" do bundle "config list --parseable" - expect(out).to eq("#mirror.https://rails-assets.org/=http://localhost:9292") + expect(out).to be_empty expect(err).to be_empty ruby(<<~RUBY) diff --git a/spec/bundler/commands/exec_spec.rb b/spec/bundler/commands/exec_spec.rb index d59b690d2f..9f5f12739a 100644 --- a/spec/bundler/commands/exec_spec.rb +++ b/spec/bundler/commands/exec_spec.rb @@ -885,7 +885,7 @@ RSpec.describe "bundle exec" do let(:exit_code) { Bundler::GemNotFound.new.status_code } let(:expected) { "" } let(:expected_err) { <<-EOS.strip } -Could not find gem 'rack (= 2)' in cached gems or installed locally. +Could not find gem 'rack (= 2)' in locally installed gems. The source contains the following gems matching 'rack': * rack-0.9.1 @@ -915,7 +915,7 @@ Run `bundle install` to install missing gems. let(:exit_code) { Bundler::GemNotFound.new.status_code } let(:expected) { "" } let(:expected_err) { <<-EOS.strip } -Could not find gem 'rack (= 2)' in cached gems or installed locally. +Could not find gem 'rack (= 2)' in locally installed gems. The source contains the following gems matching 'rack': * rack-1.0.0 diff --git a/spec/bundler/commands/fund_spec.rb b/spec/bundler/commands/fund_spec.rb index 5415b88eeb..cc32a7242f 100644 --- a/spec/bundler/commands/fund_spec.rb +++ b/spec/bundler/commands/fund_spec.rb @@ -55,6 +55,42 @@ RSpec.describe "bundle fund" do expect(out).to_not include("gem_with_dependent_funding") end + it "does not consider fund information for uninstalled optional dependencies" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + group :whatever, optional: true do + gem 'has_funding_and_other_metadata' + end + gem 'has_funding' + gem 'rack-obama' + G + + bundle "fund" + + expect(out).to include("* has_funding (1.2.3)\n Funding: https://example.com/has_funding/funding") + expect(out).to_not include("has_funding_and_other_metadata") + expect(out).to_not include("rack-obama") + end + + it "considers fund information for installed optional dependencies" do + bundle "config set with whatever" + + install_gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + group :whatever, optional: true do + gem 'has_funding_and_other_metadata' + end + gem 'has_funding' + gem 'rack-obama' + G + + bundle "fund" + + expect(out).to include("* has_funding_and_other_metadata (1.0)\n Funding: https://example.com/has_funding_and_other_metadata/funding") + expect(out).to include("* has_funding (1.2.3)\n Funding: https://example.com/has_funding/funding") + expect(out).to_not include("rack-obama") + end + it "prints message if none of the gems have fund information" do install_gemfile <<-G source "#{file_uri_for(gem_repo2)}" diff --git a/spec/bundler/commands/info_spec.rb b/spec/bundler/commands/info_spec.rb index a5a09bc147..7ddc5c2363 100644 --- a/spec/bundler/commands/info_spec.rb +++ b/spec/bundler/commands/info_spec.rb @@ -215,7 +215,7 @@ RSpec.describe "bundle info" do G bundle "info rac" - expect(out).to match(/\A1 : rack\n2 : rack-obama\n0 : - exit -(\n>.*)?\z/) + expect(out).to match(/\A1 : rack\n2 : rack-obama\n0 : - exit -(\n>|\z)/) end end diff --git a/spec/bundler/commands/init_spec.rb b/spec/bundler/commands/init_spec.rb index 0a1336572a..564a4bdc2d 100644 --- a/spec/bundler/commands/init_spec.rb +++ b/spec/bundler/commands/init_spec.rb @@ -79,7 +79,7 @@ RSpec.describe "bundle init" do end context "given --gemspec option" do - let(:spec_file) { tmp.join("test.gemspec") } + let(:spec_file) { tmp("test.gemspec") } it "should generate from an existing gemspec" do File.open(spec_file, "w") do |file| @@ -160,7 +160,7 @@ RSpec.describe "bundle init" do end context "given --gemspec option" do - let(:spec_file) { tmp.join("test.gemspec") } + let(:spec_file) { tmp("test.gemspec") } before do File.open(spec_file, "w") do |file| diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb index 1e57414377..83bee136fe 100644 --- a/spec/bundler/commands/install_spec.rb +++ b/spec/bundler/commands/install_spec.rb @@ -461,8 +461,8 @@ RSpec.describe "bundle install with gem sources" do end it "includes the gem without warning if two gemspecs add it with the same requirement" do - gem1 = tmp.join("my-gem-1") - gem2 = tmp.join("my-gem-2") + gem1 = tmp("my-gem-1") + gem2 = tmp("my-gem-2") build_lib "my-gem", path: gem1 do |s| s.add_development_dependency "rubocop", "~> 1.36.0" @@ -871,6 +871,36 @@ RSpec.describe "bundle install with gem sources" do end end + describe "when bundle bin dir does not have write access", :permissions do + let(:bin_dir) { bundled_app("vendor/#{Bundler.ruby_scope}/bin") } + + before do + FileUtils.mkdir_p(bin_dir) + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'rack' + G + end + + it "should display a proper message to explain the problem" do + FileUtils.chmod("-x", bin_dir) + bundle "config set --local path vendor" + + begin + bundle :install, raise_on_error: false + ensure + FileUtils.chmod("+x", bin_dir) + end + + expect(err).not_to include("ERROR REPORT TEMPLATE") + + expect(err).to include( + "There was an error while trying to write to `#{bin_dir}`. " \ + "It is likely that you need to grant write permissions for that path." + ) + end + end + describe "when bundle extensions path does not have write access", :permissions do let(:extensions_path) { bundled_app("vendor/#{Bundler.ruby_scope}/extensions/#{Gem::Platform.local}/#{Gem.extension_api_version}") } @@ -1407,4 +1437,33 @@ RSpec.describe "bundle install with gem sources" do expect(bundled_app(".bundle/config")).not_to exist end end + + context "when bundler installation is corrupt" do + before do + system_gems "bundler-9.99.8" + + replace_version_file("9.99.9", dir: system_gem_path("gems/bundler-9.99.8")) + end + + it "shows a proper error" do + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + + BUNDLED WITH + 9.99.8 + L + + install_gemfile "source \"#{file_uri_for(gem_repo1)}\"", env: { "BUNDLER_VERSION" => "9.99.8" }, raise_on_error: false + + expect(err).not_to include("ERROR REPORT TEMPLATE") + expect(err).to include("The running version of Bundler (9.99.9) does not match the version of the specification installed for it (9.99.8)") + end + end end diff --git a/spec/bundler/commands/list_spec.rb b/spec/bundler/commands/list_spec.rb index 5ac2077d81..660935f06f 100644 --- a/spec/bundler/commands/list_spec.rb +++ b/spec/bundler/commands/list_spec.rb @@ -126,7 +126,7 @@ RSpec.describe "bundle list" do build_git "git_test", "1.0.0", path: lib_path("git_test") - build_lib("gemspec_test", path: tmp.join("gemspec_test")) do |s| + build_lib("gemspec_test", path: tmp("gemspec_test")) do |s| s.add_dependency "bar", "=1.0.0" end @@ -135,7 +135,7 @@ RSpec.describe "bundle list" do gem "rack" gem "rails" gem "git_test", :git => "#{lib_path("git_test")}" - gemspec :path => "#{tmp.join("gemspec_test")}" + gemspec :path => "#{tmp("gemspec_test")}" G end diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb index c6bb0f58af..b0d6fa9134 100644 --- a/spec/bundler/commands/lock_spec.rb +++ b/spec/bundler/commands/lock_spec.rb @@ -138,7 +138,7 @@ RSpec.describe "bundle lock" do it "does not fetch remote specs when using the --local option" do bundle "lock --update --local", raise_on_error: false - expect(err).to match(/cached gems or installed locally/) + expect(err).to match(/locally installed gems/) end it "does not fetch remote checksums with --local" do @@ -1349,7 +1349,7 @@ RSpec.describe "bundle lock" do Because rails >= 7.0.4 depends on railties = 7.0.4 and rails < 7.0.4 depends on railties = 7.0.3.1, railties = 7.0.3.1 OR = 7.0.4 is required. - So, because railties = 7.0.3.1 OR = 7.0.4 could not be found in rubygems repository #{file_uri_for(gem_repo4)}/, cached gems or installed locally, + So, because railties = 7.0.3.1 OR = 7.0.4 could not be found in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally, version solving has failed. ERR end @@ -1460,7 +1460,7 @@ RSpec.describe "bundle lock" do Thus, rails >= 7.0.2.3, < 7.0.4 cannot be used. And because rails >= 7.0.4 depends on activemodel = 7.0.4, rails >= 7.0.2.3 requires activemodel = 7.0.4. - So, because activemodel = 7.0.4 could not be found in rubygems repository #{file_uri_for(gem_repo4)}/, cached gems or installed locally + So, because activemodel = 7.0.4 could not be found in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally and Gemfile depends on rails >= 7.0.2.3, version solving has failed. ERR diff --git a/spec/bundler/commands/newgem_spec.rb b/spec/bundler/commands/newgem_spec.rb index 199340b131..9ff10158a4 100644 --- a/spec/bundler/commands/newgem_spec.rb +++ b/spec/bundler/commands/newgem_spec.rb @@ -33,25 +33,28 @@ RSpec.describe "bundle gem" do let(:minitest_test_class_name) { "class TestMygem < Minitest::Test" } before do - sys_exec("git config --global user.name 'Bundler User'") - sys_exec("git config --global user.email user@example.com") - sys_exec("git config --global github.user bundleuser") + 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" end describe "git repo initialization" do - it "generates a gem skeleton with a .git folder", :readline do + it "generates a gem skeleton with a .git folder" do bundle "gem #{gem_name}" gem_skeleton_assertions expect(bundled_app("#{gem_name}/.git")).to exist end - it "generates a gem skeleton with a .git folder when passing --git", :readline do + it "generates a gem skeleton with a .git folder when passing --git" do bundle "gem #{gem_name} --git" gem_skeleton_assertions expect(bundled_app("#{gem_name}/.git")).to exist end - it "generates a gem skeleton without a .git folder when passing --no-git", :readline do + it "generates a gem skeleton without a .git folder when passing --no-git" do bundle "gem #{gem_name} --no-git" gem_skeleton_assertions expect(bundled_app("#{gem_name}/.git")).not_to exist @@ -62,7 +65,9 @@ RSpec.describe "bundle gem" do Dir.mkdir(bundled_app("path with spaces")) end - it "properly initializes git repo", :readline do + it "properly initializes git repo" do + skip "path with spaces needs special handling on Windows" if Gem.win_platform? + bundle "gem #{gem_name}", dir: bundled_app("path with spaces") expect(bundled_app("path with spaces/#{gem_name}/.git")).to exist end @@ -104,7 +109,7 @@ RSpec.describe "bundle gem" do end it "generates the README with a section for the Code of Conduct, respecting the configured git default branch", git: ">= 2.28.0" do - sys_exec("git config --global init.defaultBranch main") + git("config --global init.defaultBranch main") bundle "gem #{gem_name} --coc" expect(bundled_app("#{gem_name}/README.md").read).to include("## Code of Conduct") @@ -131,7 +136,7 @@ RSpec.describe "bundle gem" do before do bundle "gem #{gem_name} --changelog" end - it "generates a gem skeleton with a CHANGELOG", :readline do + it "generates a gem skeleton with a CHANGELOG" do gem_skeleton_assertions expect(bundled_app("#{gem_name}/CHANGELOG.md")).to exist end @@ -141,7 +146,7 @@ RSpec.describe "bundle gem" do before do bundle "gem #{gem_name} --no-changelog" end - it "generates a gem skeleton without a CHANGELOG", :readline do + it "generates a gem skeleton without a CHANGELOG" do gem_skeleton_assertions expect(bundled_app("#{gem_name}/CHANGELOG.md")).to_not exist end @@ -150,6 +155,7 @@ RSpec.describe "bundle gem" do 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 @@ -303,49 +309,49 @@ RSpec.describe "bundle gem" do end end - it "has no rubocop offenses when using --linter=rubocop flag", :readline do + it "has no rubocop offenses when using --linter=rubocop flag" do skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core? bundle "gem #{gem_name} --linter=rubocop" bundle_exec_rubocop expect(last_command).to be_success end - it "has no rubocop offenses when using --ext=c and --linter=rubocop flag", :readline do + it "has no rubocop offenses when using --ext=c 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? bundle "gem #{gem_name} --ext=c --linter=rubocop" bundle_exec_rubocop expect(last_command).to be_success end - it "has no rubocop offenses when using --ext=c, --test=minitest, and --linter=rubocop flag", :readline do + it "has no rubocop offenses when using --ext=c, --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? bundle "gem #{gem_name} --ext=c --test=minitest --linter=rubocop" bundle_exec_rubocop expect(last_command).to be_success end - it "has no rubocop offenses when using --ext=c, --test=rspec, and --linter=rubocop flag", :readline do + it "has no rubocop offenses when using --ext=c, --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? bundle "gem #{gem_name} --ext=c --test=rspec --linter=rubocop" bundle_exec_rubocop expect(last_command).to be_success end - it "has no rubocop offenses when using --ext=c, --test=test-unit, and --linter=rubocop flag", :readline do + it "has no rubocop offenses when using --ext=c, --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? bundle "gem #{gem_name} --ext=c --test=test-unit --linter=rubocop" bundle_exec_rubocop expect(last_command).to be_success end - it "has no standard offenses when using --linter=standard flag", :readline do + it "has no standard offenses when using --linter=standard flag" do skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core? bundle "gem #{gem_name} --linter=standard" bundle_exec_standardrb expect(last_command).to be_success end - it "has no rubocop offenses when using --ext=rust and --linter=rubocop flag", :readline 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 @@ -354,7 +360,7 @@ RSpec.describe "bundle gem" do expect(last_command).to be_success end - it "has no rubocop offenses when using --ext=rust, --test=minitest, and --linter=rubocop flag", :readline 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 @@ -363,7 +369,7 @@ RSpec.describe "bundle gem" do expect(last_command).to be_success end - it "has no rubocop offenses when using --ext=rust, --test=rspec, and --linter=rubocop flag", :readline 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 @@ -372,7 +378,7 @@ RSpec.describe "bundle gem" do expect(last_command).to be_success end - it "has no rubocop offenses when using --ext=rust, --test=test-unit, and --linter=rubocop flag", :readline 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 @@ -399,7 +405,7 @@ RSpec.describe "bundle gem" do end end - context "README.md", :readline do + context "README.md" do context "git config github.user present" do before do bundle "gem #{gem_name}" @@ -413,7 +419,7 @@ RSpec.describe "bundle gem" do context "git config github.user is absent" do before do - sys_exec("git config --global --unset github.user") + git("config --global --unset github.user") bundle "gem #{gem_name}" end @@ -424,12 +430,12 @@ RSpec.describe "bundle gem" do end end - it "creates a new git repository", :readline do + it "creates a new git repository" do bundle "gem #{gem_name}" expect(bundled_app("#{gem_name}/.git")).to exist end - context "when git is not available", :readline 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] @@ -451,7 +457,7 @@ RSpec.describe "bundle gem" do end end - it "generates a valid gemspec", :readline, :ruby_repo do + it "generates a valid gemspec", :ruby_repo do bundle "gem newgem --bin" prepare_gemspec(bundled_app("newgem", "newgem.gemspec")) @@ -464,7 +470,7 @@ RSpec.describe "bundle gem" do expect(last_command.stdboth).not_to include("ERROR") end - context "gem naming with relative paths", :readline do + context "gem naming with relative paths" do it "resolves ." do create_temporary_dir("tmp") @@ -557,8 +563,12 @@ RSpec.describe "bundle gem" do 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 be_executable - expect(bundled_app("#{gem_name}/bin/console")).to be_executable + + 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("#!") end @@ -591,8 +601,8 @@ RSpec.describe "bundle gem" do context "git config user.{name,email} is not set" do before do - sys_exec("git config --global --unset user.name") - sys_exec("git config --global --unset user.email") + git("config --global --unset user.name") + git("config --global --unset user.email") bundle "gem #{gem_name}" end @@ -870,7 +880,7 @@ RSpec.describe "bundle gem" do end end - context "gem.test set to rspec and --test with no arguments", :hint_text do + 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" @@ -887,10 +897,12 @@ RSpec.describe "bundle gem" do end end - context "gem.test setting set to false and --test with no arguments", :hint_text do + 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" + bundle "gem #{gem_name} --test" do |input, _, _| + input.puts + end end it "asks to generate test files" do @@ -904,9 +916,12 @@ RSpec.describe "bundle gem" do it_behaves_like "test framework is absent" end - context "gem.test setting not set and --test with no arguments", :hint_text do + context "gem.test setting not set and --test with no arguments", :readline do before do - bundle "gem #{gem_name} --test" + global_config "BUNDLE_GEM__TEST" => nil + bundle "gem #{gem_name} --test" do |input, _, _| + input.puts + end end it "asks to generate test files" do @@ -1009,7 +1024,7 @@ RSpec.describe "bundle gem" do end end - context "gem.ci set to github and --ci with no arguments", :hint_text do + 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" @@ -1024,10 +1039,12 @@ RSpec.describe "bundle gem" do end end - context "gem.ci setting set to false and --ci with no arguments", :hint_text do + 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" + bundle "gem #{gem_name} --ci" do |input, _, _| + input.puts "github" + end end it "asks to setup CI" do @@ -1039,9 +1056,12 @@ RSpec.describe "bundle gem" do end end - context "gem.ci setting not set and --ci with no arguments", :hint_text do + context "gem.ci setting not set and --ci with no arguments", :readline do before do - bundle "gem #{gem_name} --ci" + global_config "BUNDLE_GEM__CI" => nil + bundle "gem #{gem_name} --ci" do |input, _, _| + input.puts "github" + end end it "asks to setup CI" do @@ -1111,6 +1131,7 @@ RSpec.describe "bundle gem" do context "gem.rubocop setting set to true", bundler: "< 3" do before do + global_config "BUNDLE_GEM__LINTER" => nil bundle "config set gem.rubocop true" bundle "gem #{gem_name}" end @@ -1130,7 +1151,7 @@ RSpec.describe "bundle gem" do end end - context "gem.linter set to rubocop and --linter with no arguments", :hint_text do + context "gem.linter set to rubocop and --linter with no arguments" do before do bundle "config set gem.linter rubocop" bundle "gem #{gem_name} --linter" @@ -1145,10 +1166,12 @@ RSpec.describe "bundle gem" do end end - context "gem.linter setting set to false and --linter with no arguments", :hint_text do + context "gem.linter setting set to false and --linter with no arguments", :readline do before do bundle "config set gem.linter false" - bundle "gem #{gem_name} --linter" + bundle "gem #{gem_name} --linter" do |input, _, _| + input.puts "rubocop" + end end it "asks to setup a linter" do @@ -1160,9 +1183,12 @@ RSpec.describe "bundle gem" do end end - context "gem.linter setting not set and --linter with no arguments", :hint_text do + context "gem.linter setting not set and --linter with no arguments", :readline do before do - bundle "gem #{gem_name} --linter" + global_config "BUNDLE_GEM__LINTER" => nil + bundle "gem #{gem_name} --linter" do |input, _, _| + input.puts "rubocop" + end end it "asks to setup a linter" do @@ -1185,7 +1211,7 @@ RSpec.describe "bundle gem" do end end - context "testing --mit and --coc options against bundle config settings", :readline do + context "testing --mit and --coc options against bundle config settings" do let(:gem_name) { "test-gem" } let(:require_path) { "test/gem" } @@ -1288,10 +1314,10 @@ RSpec.describe "bundle gem" do end end - context "testing --github-username option against git and bundle config settings", :readline do + context "testing --github-username option against git and bundle config settings" do context "without git config set" do before do - sys_exec("git config --global --unset github.user") + git("config --global --unset github.user") end context "with github-username option in bundle config settings set to some value" do before do @@ -1325,10 +1351,10 @@ RSpec.describe "bundle gem" do end end - context "testing github_username bundle config against git config settings", :readline do + context "testing github_username bundle config against git config settings" do context "without git config set" do before do - sys_exec("git config --global --unset github.user") + git("config --global --unset github.user") end it_behaves_like "github_username configuration" @@ -1339,7 +1365,7 @@ RSpec.describe "bundle gem" do end end - context "gem naming with underscore", :readline do + context "gem naming with underscore" do let(:gem_name) { "test_gem" } let(:require_path) { "test_gem" } @@ -1485,7 +1511,7 @@ RSpec.describe "bundle gem" do end end - context "gem naming with dashed", :readline do + context "gem naming with dashed" do let(:gem_name) { "test-gem" } let(:require_path) { "test/gem" } @@ -1506,7 +1532,7 @@ RSpec.describe "bundle gem" do end describe "uncommon gem names" do - it "can deal with two dashes", :readline do + it "can deal with two dashes" do bundle "gem a--a" expect(bundled_app("a--a/a--a.gemspec")).to exist @@ -1536,7 +1562,7 @@ Usage: "bundle gem NAME [OPTIONS]" end end - describe "#ensure_safe_gem_name", :readline do + describe "#ensure_safe_gem_name" do before do bundle "gem #{subject}", raise_on_error: false end @@ -1564,7 +1590,7 @@ Usage: "bundle gem NAME [OPTIONS]" context "on first run", :readline do it "asks about test framework" do - global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__COC" => "false" + global_config "BUNDLE_GEM__TEST" => nil bundle "gem foobar" do |input, _, _| input.puts "rspec" @@ -1587,7 +1613,7 @@ Usage: "bundle gem NAME [OPTIONS]" end it "asks about CI service" do - global_config "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__COC" => "false", "BUNDLE_GEM__LINTER" => "false" + global_config "BUNDLE_GEM__CI" => nil bundle "gem foobar" do |input, _, _| input.puts "github" @@ -1597,7 +1623,7 @@ Usage: "bundle gem NAME [OPTIONS]" end it "asks about MIT license" do - global_config "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__COC" => "false", "BUNDLE_GEM__CI" => "false", "BUNDLE_GEM__LINTER" => "false" + global_config "BUNDLE_GEM__MIT" => nil bundle "config list" @@ -1609,7 +1635,7 @@ Usage: "bundle gem NAME [OPTIONS]" end it "asks about CoC" do - global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__CI" => "false", "BUNDLE_GEM__LINTER" => "false" + global_config "BUNDLE_GEM__COC" => nil bundle "gem foobar" do |input, _, _| input.puts "yes" @@ -1619,8 +1645,7 @@ Usage: "bundle gem NAME [OPTIONS]" end it "asks about CHANGELOG" do - global_config "BUNDLE_GEM__MIT" => "false", "BUNDLE_GEM__TEST" => "false", "BUNDLE_GEM__CI" => "false", "BUNDLE_GEM__LINTER" => "false", - "BUNDLE_GEM__COC" => "false" + global_config "BUNDLE_GEM__CHANGELOG" => nil bundle "gem foobar" do |input, _, _| input.puts "yes" @@ -1630,7 +1655,7 @@ Usage: "bundle gem NAME [OPTIONS]" end end - context "on conflicts with a previously created file", :readline do + context "on conflicts with a previously created file" do it "should fail gracefully" do FileUtils.touch(bundled_app("conflict-foobar")) bundle "gem conflict-foobar", raise_on_error: false @@ -1639,7 +1664,7 @@ Usage: "bundle gem NAME [OPTIONS]" end end - context "on conflicts with a previously created directory", :readline do + context "on conflicts with a previously created directory" do it "should succeed" do FileUtils.mkdir_p(bundled_app("conflict-foobar/Gemfile")) bundle "gem conflict-foobar" diff --git a/spec/bundler/commands/open_spec.rb b/spec/bundler/commands/open_spec.rb index 97374f30c3..18f4db38c9 100644 --- a/spec/bundler/commands/open_spec.rb +++ b/spec/bundler/commands/open_spec.rb @@ -44,7 +44,7 @@ RSpec.describe "bundle open" do G bundle "open foo", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" } - expect(out).to include("editor #{default_bundle_path.join("bundler", "gems", "foo-1.0-#{ref}")}") + expect(out).to include("editor #{default_bundle_path("bundler", "gems", "foo-1.0-#{ref}")}") end it "suggests alternatives for similar-sounding gems" do @@ -164,7 +164,6 @@ RSpec.describe "bundle open" do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gem "json" G end diff --git a/spec/bundler/commands/post_bundle_message_spec.rb b/spec/bundler/commands/post_bundle_message_spec.rb index 07fd5a79e9..3c7fd3486d 100644 --- a/spec/bundler/commands/post_bundle_message_spec.rb +++ b/spec/bundler/commands/post_bundle_message_spec.rb @@ -120,7 +120,7 @@ RSpec.describe "post bundle message" do gem "not-a-gem", :group => :development G expect(err).to include <<-EOS.strip -Could not find gem 'not-a-gem' in rubygems repository #{file_uri_for(gem_repo1)}/, cached gems or installed locally. +Could not find gem 'not-a-gem' in rubygems repository #{file_uri_for(gem_repo1)}/ or installed locally. EOS end diff --git a/spec/bundler/commands/remove_spec.rb b/spec/bundler/commands/remove_spec.rb index 197fcde091..23ce7dde47 100644 --- a/spec/bundler/commands/remove_spec.rb +++ b/spec/bundler/commands/remove_spec.rb @@ -634,14 +634,14 @@ RSpec.describe "bundle remove" do context "with gemspec" do it "should not remove the gem" do - build_lib("foo", path: tmp.join("foo")) do |s| + build_lib("foo", path: tmp("foo")) do |s| s.write("foo.gemspec", "") s.add_dependency "rack" end install_gemfile(<<-G) source "#{file_uri_for(gem_repo1)}" - gemspec :path => '#{tmp.join("foo")}', :name => 'foo' + gemspec :path => '#{tmp("foo")}', :name => 'foo' G bundle "remove foo" diff --git a/spec/bundler/commands/show_spec.rb b/spec/bundler/commands/show_spec.rb index 2b6d4d2d00..4f02818cef 100644 --- a/spec/bundler/commands/show_spec.rb +++ b/spec/bundler/commands/show_spec.rb @@ -173,7 +173,7 @@ RSpec.describe "bundle show", bundler: "< 3" do G bundle "show rac" - expect(out).to match(/\A1 : rack\n2 : rack-obama\n0 : - exit -(\n>.*)?\z/) + expect(out).to match(/\A1 : rack\n2 : rack-obama\n0 : - exit -(\n>|\z)/) end end diff --git a/spec/bundler/commands/update_spec.rb b/spec/bundler/commands/update_spec.rb index 8565e27ebf..d5d38b81f1 100644 --- a/spec/bundler/commands/update_spec.rb +++ b/spec/bundler/commands/update_spec.rb @@ -849,8 +849,7 @@ RSpec.describe "bundle update" do end bundle "update", all: true - out.sub!("Removing foo (1.0)\n", "") - expect(out).to match(/Resolving dependencies\.\.\.\.*\nFetching foo 2\.0 \(was 1\.0\)\nInstalling foo 2\.0 \(was 1\.0\)\nBundle updated/) + expect(out.sub("Removing foo (1.0)\n", "")).to match(/Resolving dependencies\.\.\.\.*\nFetching foo 2\.0 \(was 1\.0\)\nInstalling foo 2\.0 \(was 1\.0\)\nBundle updated/) end it "shows error message when Gemfile.lock is not preset and gem is specified" do diff --git a/spec/bundler/install/gemfile/eval_gemfile_spec.rb b/spec/bundler/install/gemfile/eval_gemfile_spec.rb index cfa66e5986..7d2f9d46f9 100644 --- a/spec/bundler/install/gemfile/eval_gemfile_spec.rb +++ b/spec/bundler/install/gemfile/eval_gemfile_spec.rb @@ -2,7 +2,7 @@ RSpec.describe "bundle install with gemfile that uses eval_gemfile" do before do - build_lib("gunks", path: bundled_app.join("gems/gunks")) do |s| + build_lib("gunks", path: bundled_app("gems/gunks")) do |s| s.name = "gunks" s.version = "0.0.1" end diff --git a/spec/bundler/install/gemfile/gemspec_spec.rb b/spec/bundler/install/gemfile/gemspec_spec.rb index 63778567cf..2932228a54 100644 --- a/spec/bundler/install/gemfile/gemspec_spec.rb +++ b/spec/bundler/install/gemfile/gemspec_spec.rb @@ -39,14 +39,14 @@ RSpec.describe "bundle install from an existing gemspec" do end it "should install runtime and development dependencies" do - build_lib("foo", path: tmp.join("foo")) do |s| + build_lib("foo", path: tmp("foo")) do |s| s.write("Gemfile", "source :rubygems\ngemspec") s.add_dependency "bar", "=1.0.0" s.add_development_dependency "bar-dev", "=1.0.0" end install_gemfile <<-G source "#{file_uri_for(gem_repo2)}" - gemspec :path => '#{tmp.join("foo")}' + gemspec :path => '#{tmp("foo")}' G expect(the_bundle).to include_gems "bar 1.0.0" @@ -54,16 +54,16 @@ RSpec.describe "bundle install from an existing gemspec" do end it "that is hidden should install runtime and development dependencies" do - build_lib("foo", path: tmp.join("foo")) do |s| + build_lib("foo", path: tmp("foo")) do |s| s.write("Gemfile", "source :rubygems\ngemspec") s.add_dependency "bar", "=1.0.0" s.add_development_dependency "bar-dev", "=1.0.0" end - FileUtils.mv tmp.join("foo", "foo.gemspec"), tmp.join("foo", ".gemspec") + FileUtils.mv tmp("foo", "foo.gemspec"), tmp("foo", ".gemspec") install_gemfile <<-G source "#{file_uri_for(gem_repo2)}" - gemspec :path => '#{tmp.join("foo")}' + gemspec :path => '#{tmp("foo")}' G expect(the_bundle).to include_gems "bar 1.0.0" @@ -76,42 +76,42 @@ RSpec.describe "bundle install from an existing gemspec" do build_gem "baz", "1.1" end - build_lib("foo", path: tmp.join("foo")) do |s| + build_lib("foo", path: tmp("foo")) do |s| s.write("Gemfile", "source :rubygems\ngemspec") s.add_dependency "baz", ">= 1.0", "< 1.1" end install_gemfile <<-G source "#{file_uri_for(gem_repo2)}" - gemspec :path => '#{tmp.join("foo")}' + gemspec :path => '#{tmp("foo")}' G expect(the_bundle).to include_gems "baz 1.0" end it "should raise if there are no gemspecs available" do - build_lib("foo", path: tmp.join("foo"), gemspec: false) + build_lib("foo", path: tmp("foo"), gemspec: false) install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo2)}" - gemspec :path => '#{tmp.join("foo")}' + gemspec :path => '#{tmp("foo")}' G - expect(err).to match(/There are no gemspecs at #{tmp.join("foo")}/) + expect(err).to match(/There are no gemspecs at #{tmp("foo")}/) end it "should raise if there are too many gemspecs available" do - build_lib("foo", path: tmp.join("foo")) do |s| + build_lib("foo", path: tmp("foo")) do |s| s.write("foo2.gemspec", build_spec("foo", "4.0").first.to_ruby) end install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo2)}" - gemspec :path => '#{tmp.join("foo")}' + gemspec :path => '#{tmp("foo")}' G - expect(err).to match(/There are multiple gemspecs at #{tmp.join("foo")}/) + expect(err).to match(/There are multiple gemspecs at #{tmp("foo")}/) end it "should pick a specific gemspec" do - build_lib("foo", path: tmp.join("foo")) do |s| + build_lib("foo", path: tmp("foo")) do |s| s.write("foo2.gemspec", "") s.add_dependency "bar", "=1.0.0" s.add_development_dependency "bar-dev", "=1.0.0" @@ -119,7 +119,7 @@ RSpec.describe "bundle install from an existing gemspec" do install_gemfile(<<-G) source "#{file_uri_for(gem_repo2)}" - gemspec :path => '#{tmp.join("foo")}', :name => 'foo' + gemspec :path => '#{tmp("foo")}', :name => 'foo' G expect(the_bundle).to include_gems "bar 1.0.0" @@ -127,7 +127,7 @@ RSpec.describe "bundle install from an existing gemspec" do end it "should use a specific group for development dependencies" do - build_lib("foo", path: tmp.join("foo")) do |s| + build_lib("foo", path: tmp("foo")) do |s| s.write("foo2.gemspec", "") s.add_dependency "bar", "=1.0.0" s.add_development_dependency "bar-dev", "=1.0.0" @@ -135,7 +135,7 @@ RSpec.describe "bundle install from an existing gemspec" do install_gemfile(<<-G) source "#{file_uri_for(gem_repo2)}" - gemspec :path => '#{tmp.join("foo")}', :name => 'foo', :development_group => :dev + gemspec :path => '#{tmp("foo")}', :name => 'foo', :development_group => :dev G expect(the_bundle).to include_gems "bar 1.0.0" @@ -144,33 +144,33 @@ RSpec.describe "bundle install from an existing gemspec" do end it "should match a lockfile even if the gemspec defines development dependencies" do - build_lib("foo", path: tmp.join("foo")) do |s| + build_lib("foo", path: tmp("foo")) do |s| s.write("Gemfile", "source '#{file_uri_for(gem_repo1)}'\ngemspec") s.add_dependency "actionpack", "=2.3.2" s.add_development_dependency "rake", rake_version end - bundle "install", dir: tmp.join("foo") + bundle "install", dir: tmp("foo") # This should really be able to rely on $stderr, but, it's not written # right, so we can't. In fact, this is a bug negation test, and so it'll # 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" - output = bundle("install", dir: tmp.join("foo")) + output = bundle("install", dir: tmp("foo")) expect(output).not_to match(/You have added to the Gemfile/) expect(output).not_to match(/You have deleted from the Gemfile/) expect(output).not_to match(/the lockfile can't be updated because frozen mode is set/) end it "should match a lockfile without needing to re-resolve" do - build_lib("foo", path: tmp.join("foo")) do |s| + build_lib("foo", path: tmp("foo")) do |s| s.add_dependency "rack" end install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gemspec :path => '#{tmp.join("foo")}' + gemspec :path => '#{tmp("foo")}' G bundle "install", verbose: true @@ -182,14 +182,14 @@ RSpec.describe "bundle install from an existing gemspec" do it "should match a lockfile without needing to re-resolve with development dependencies" do simulate_platform java - build_lib("foo", path: tmp.join("foo")) do |s| + build_lib("foo", path: tmp("foo")) do |s| s.add_dependency "rack" s.add_development_dependency "thin" end install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gemspec :path => '#{tmp.join("foo")}' + gemspec :path => '#{tmp("foo")}' G bundle "install", verbose: true @@ -199,14 +199,14 @@ RSpec.describe "bundle install from an existing gemspec" do end it "should match a lockfile on non-ruby platforms with a transitive platform dependency", :jruby_only do - build_lib("foo", path: tmp.join("foo")) do |s| + build_lib("foo", path: tmp("foo")) do |s| s.add_dependency "platform_specific" end system_gems "platform_specific-1.0-java", path: default_bundle_path install_gemfile <<-G - gemspec :path => '#{tmp.join("foo")}' + gemspec :path => '#{tmp("foo")}' G bundle "update --bundler", artifice: "compact_index", verbose: true @@ -214,13 +214,13 @@ RSpec.describe "bundle install from an existing gemspec" do end it "should evaluate the gemspec in its directory" do - build_lib("foo", path: tmp.join("foo")) - File.open(tmp.join("foo/foo.gemspec"), "w") do |s| - s.write "raise 'ahh' unless Dir.pwd == '#{tmp.join("foo")}'" + build_lib("foo", path: tmp("foo")) + File.open(tmp("foo/foo.gemspec"), "w") do |s| + s.write "raise 'ahh' unless Dir.pwd == '#{tmp("foo")}'" end install_gemfile <<-G, raise_on_error: false - gemspec :path => '#{tmp.join("foo")}' + gemspec :path => '#{tmp("foo")}' G expect(last_command.stdboth).not_to include("ahh") end @@ -248,7 +248,7 @@ RSpec.describe "bundle install from an existing gemspec" do end it "allows conflicts" do - build_lib("foo", path: tmp.join("foo")) do |s| + build_lib("foo", path: tmp("foo")) do |s| s.version = "1.0.0" s.add_dependency "bar", "= 1.0.0" end @@ -260,14 +260,14 @@ RSpec.describe "bundle install from an existing gemspec" do install_gemfile <<-G source "#{file_uri_for(gem_repo2)}" gem "deps" - gemspec :path => '#{tmp.join("foo")}', :name => 'foo' + gemspec :path => '#{tmp("foo")}', :name => 'foo' G expect(the_bundle).to include_gems "foo 1.0.0" end it "does not break Gem.finish_resolve with conflicts" do - build_lib("foo", path: tmp.join("foo")) do |s| + build_lib("foo", path: tmp("foo")) do |s| s.version = "1.0.0" s.add_dependency "bar", "= 1.0.0" end @@ -281,7 +281,7 @@ RSpec.describe "bundle install from an existing gemspec" do install_gemfile <<-G source "#{file_uri_for(gem_repo2)}" gem "deps" - gemspec :path => '#{tmp.join("foo")}', :name => 'foo' + gemspec :path => '#{tmp("foo")}', :name => 'foo' G expect(the_bundle).to include_gems "foo 1.0.0" @@ -359,7 +359,7 @@ RSpec.describe "bundle install from an existing gemspec" do let(:source_uri) { "http://localgemserver.test" } before do - build_lib("foo", path: tmp.join("foo")) do |s| + build_lib("foo", path: tmp("foo")) do |s| s.add_dependency "rack", "=1.0.0" end @@ -398,7 +398,7 @@ RSpec.describe "bundle install from an existing gemspec" do context "using JRuby with explicit platform", :jruby_only do before do create_file( - tmp.join("foo", "foo-java.gemspec"), + tmp("foo", "foo-java.gemspec"), build_spec("foo", "1.0", "java") do dep "rack", "=1.0.0" @spec.authors = "authors" @@ -589,7 +589,7 @@ RSpec.describe "bundle install from an existing gemspec" do context "with multiple platforms" do before do - build_lib("foo", path: tmp.join("foo")) do |s| + build_lib("foo", path: tmp("foo")) do |s| s.version = "1.0.0" s.add_development_dependency "rack" s.write "foo-universal-java.gemspec", build_spec("foo", "1.0.0", "universal-java") {|sj| sj.runtime "rack", "1.0.0" }.first.to_ruby @@ -601,7 +601,7 @@ RSpec.describe "bundle install from an existing gemspec" do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gemspec :path => '#{tmp.join("foo")}', :name => 'foo' + gemspec :path => '#{tmp("foo")}', :name => 'foo' G expect(the_bundle).to include_gems "foo 1.0.0", "rack 1.0.0" @@ -613,7 +613,7 @@ RSpec.describe "bundle install from an existing gemspec" do bundle "config set --local without development" install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gemspec :path => '#{tmp.join("foo")}', :name => 'foo' + gemspec :path => '#{tmp("foo")}', :name => 'foo' G expect(the_bundle).to include_gem "foo 1.0.0" @@ -623,7 +623,7 @@ RSpec.describe "bundle install from an existing gemspec" do context "with multiple platforms and resolving for more specific platforms" do before do - build_lib("chef", path: tmp.join("chef")) do |s| + 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 end @@ -682,7 +682,7 @@ RSpec.describe "bundle install from an existing gemspec" do context "with multiple locked platforms" do before do - build_lib("activeadmin", path: tmp.join("activeadmin")) do |s| + build_lib("activeadmin", path: tmp("activeadmin")) do |s| s.version = "2.9.0" s.add_dependency "railties", ">= 5.2", "< 6.2" end @@ -735,7 +735,7 @@ RSpec.describe "bundle install from an existing gemspec" do #{Bundler::VERSION} L - gemspec = tmp.join("activeadmin/activeadmin.gemspec") + gemspec = tmp("activeadmin/activeadmin.gemspec") File.write(gemspec, File.read(gemspec).sub(">= 5.2", ">= 6.0")) previous_lockfile = lockfile diff --git a/spec/bundler/install/gemfile/git_spec.rb b/spec/bundler/install/gemfile/git_spec.rb index 45ee7b44d1..21216594b1 100644 --- a/spec/bundler/install/gemfile/git_spec.rb +++ b/spec/bundler/install/gemfile/git_spec.rb @@ -31,7 +31,7 @@ RSpec.describe "bundle install with git sources" do end it "does not write to cache on bundler/setup" do - cache_path = default_bundle_path.join("cache") + cache_path = default_bundle_path("cache") FileUtils.rm_rf(cache_path) ruby "require 'bundler/setup'" expect(cache_path).not_to exist @@ -59,7 +59,7 @@ RSpec.describe "bundle install with git sources" do bundle "update foo" sha = git.ref_for("main", 11) - spec_file = default_bundle_path.join("bundler/gems/foo-1.0-#{sha}/foo.gemspec") + spec_file = default_bundle_path("bundler/gems/foo-1.0-#{sha}/foo.gemspec") expect(spec_file).to exist ruby_code = Gem::Specification.load(spec_file.to_s).to_ruby file_code = File.read(spec_file) @@ -272,7 +272,7 @@ RSpec.describe "bundle install with git sources" do s.write("lib/foo.rb", "raise 'FAIL'") end - sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 main~1", dir: lib_path("foo-1.0")) + git("update-ref -m \"Bundler Spec!\" refs/bundler/1 main~1", lib_path("foo-1.0")) # want to ensure we don't fallback to HEAD update_git "foo", path: lib_path("foo-1.0"), branch: "rando" do |s| @@ -308,7 +308,7 @@ RSpec.describe "bundle install with git sources" do s.write("lib/foo.rb", "raise 'FAIL'") end - sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 main~1", dir: lib_path("foo-1.0")) + git("update-ref -m \"Bundler Spec!\" refs/bundler/1 main~1", lib_path("foo-1.0")) # want to ensure we don't fallback to HEAD update_git "foo", path: lib_path("foo-1.0"), branch: "rando" do |s| @@ -332,7 +332,7 @@ RSpec.describe "bundle install with git sources" do end it "does not download random non-head refs" do - sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 main~1", dir: lib_path("foo-1.0")) + git("update-ref -m \"Bundler Spec!\" refs/bundler/1 main~1", lib_path("foo-1.0")) bundle "config set global_gem_cache true" @@ -346,7 +346,7 @@ RSpec.describe "bundle install with git sources" do # ensure we also git fetch after cloning bundle :update, all: true - sys_exec("git ls-remote .", dir: Dir[home(".bundle/cache/git/foo-*")].first) + git("ls-remote .", Dir[home(".bundle/cache/git/foo-*")].first) expect(out).not_to include("refs/bundler/1") end @@ -906,7 +906,7 @@ RSpec.describe "bundle install with git sources" do bundle "update", all: true expect(the_bundle).to include_gems "forced 1.1" - sys_exec("git reset --hard HEAD^", dir: lib_path("forced-1.0")) + git("reset --hard HEAD^", lib_path("forced-1.0")) bundle "update", all: true expect(the_bundle).to include_gems "forced 1.0" @@ -920,8 +920,8 @@ RSpec.describe "bundle install with git sources" do build_git "has_submodule", "1.0" do |s| s.add_dependency "submodule" end - sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", dir: lib_path("has_submodule-1.0") - sys_exec "git commit -m \"submodulator\"", dir: lib_path("has_submodule-1.0") + git "submodule add #{lib_path("submodule-1.0")} submodule-1.0", lib_path("has_submodule-1.0") + git "commit -m \"submodulator\"", lib_path("has_submodule-1.0") install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo1)}" @@ -929,7 +929,7 @@ RSpec.describe "bundle install with git sources" do gem "has_submodule" end G - expect(err).to match(%r{submodule >= 0 could not be found in rubygems repository #{file_uri_for(gem_repo1)}/, cached gems or installed locally}) + expect(err).to match(%r{submodule >= 0 could not be found in rubygems repository #{file_uri_for(gem_repo1)}/ or installed locally}) expect(the_bundle).not_to include_gems "has_submodule 1.0" end @@ -942,8 +942,8 @@ RSpec.describe "bundle install with git sources" do build_git "has_submodule", "1.0" do |s| s.add_dependency "submodule" end - sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", dir: lib_path("has_submodule-1.0") - sys_exec "git commit -m \"submodulator\"", dir: lib_path("has_submodule-1.0") + git "submodule add #{lib_path("submodule-1.0")} submodule-1.0", lib_path("has_submodule-1.0") + git "commit -m \"submodulator\"", lib_path("has_submodule-1.0") install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -962,8 +962,8 @@ RSpec.describe "bundle install with git sources" do build_git "submodule", "1.0" build_git "has_submodule", "1.0" - sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", dir: lib_path("has_submodule-1.0") - sys_exec "git commit -m \"submodulator\"", dir: lib_path("has_submodule-1.0") + git "submodule add #{lib_path("submodule-1.0")} submodule-1.0", lib_path("has_submodule-1.0") + git "commit -m \"submodulator\"", lib_path("has_submodule-1.0") install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -1292,7 +1292,7 @@ RSpec.describe "bundle install with git sources" do void Init_foo() { rb_define_global_function("foo", &foo, 0); } C end - sys_exec("git commit -m \"commit for iteration #{i}\" ext/foo.c", dir: git_reader.path) + git("commit -m \"commit for iteration #{i}\" ext/foo.c", git_reader.path) git_commit_sha = git_reader.ref_for("HEAD") diff --git a/spec/bundler/install/gemfile/sources_spec.rb b/spec/bundler/install/gemfile/sources_spec.rb index a5ba76f4d9..3de77ff7e3 100644 --- a/spec/bundler/install/gemfile/sources_spec.rb +++ b/spec/bundler/install/gemfile/sources_spec.rb @@ -520,7 +520,7 @@ RSpec.describe "bundle install with gems on multiple sources" do it "fails" do bundle :install, artifice: "compact_index", raise_on_error: false - expect(err).to include("Could not find gem 'private_gem_1' in rubygems repository https://gem.repo2/, cached gems or installed locally.") + expect(err).to include("Could not find gem 'private_gem_1' in rubygems repository https://gem.repo2/ or installed locally.") end end @@ -611,7 +611,7 @@ RSpec.describe "bundle install with gems on multiple sources" do Could not find compatible versions Because every version of depends_on_rack depends on rack >= 0 - and rack >= 0 could not be found in rubygems repository https://gem.repo2/, cached gems or installed locally, + and rack >= 0 could not be found in rubygems repository https://gem.repo2/ or installed locally, depends_on_rack cannot be used. So, because Gemfile depends on depends_on_rack >= 0, version solving has failed. @@ -1484,14 +1484,14 @@ RSpec.describe "bundle install with gems on multiple sources" do build_gem "bar" end - build_lib("gemspec_test", path: tmp.join("gemspec_test")) do |s| + build_lib("gemspec_test", path: tmp("gemspec_test")) do |s| s.add_dependency "bar", "=1.0.0" end install_gemfile <<-G, artifice: "compact_index" source "https://gem.repo2" gem "rack" - gemspec :path => "#{tmp.join("gemspec_test")}" + gemspec :path => "#{tmp("gemspec_test")}" G end @@ -1506,7 +1506,7 @@ RSpec.describe "bundle install with gems on multiple sources" do build_gem "bar" end - build_lib("gemspec_test", path: tmp.join("gemspec_test")) do |s| + build_lib("gemspec_test", path: tmp("gemspec_test")) do |s| s.add_development_dependency "bar" end @@ -1517,7 +1517,7 @@ RSpec.describe "bundle install with gems on multiple sources" do gem "bar" end - gemspec :path => "#{tmp.join("gemspec_test")}" + gemspec :path => "#{tmp("gemspec_test")}" G end diff --git a/spec/bundler/install/gemfile/specific_platform_spec.rb b/spec/bundler/install/gemfile/specific_platform_spec.rb index 5f1b034bfc..1a33053dc6 100644 --- a/spec/bundler/install/gemfile/specific_platform_spec.rb +++ b/spec/bundler/install/gemfile/specific_platform_spec.rb @@ -395,7 +395,7 @@ RSpec.describe "bundle install with specific platforms" do G error_message = <<~ERROR.strip - Could not find gem 'sorbet-static (= 0.5.6433)' with platform 'arm64-darwin-21' in rubygems repository #{file_uri_for(gem_repo4)}/, cached gems or installed locally. + Could not find gem 'sorbet-static (= 0.5.6433)' with platform 'arm64-darwin-21' in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally. The source contains the following gems matching 'sorbet-static (= 0.5.6433)': * sorbet-static-0.5.6433-universal-darwin-20 @@ -434,7 +434,7 @@ RSpec.describe "bundle install with specific platforms" do Could not find compatible versions Because every version of sorbet depends on sorbet-static = 0.5.6433 - and sorbet-static = 0.5.6433 could not be found in rubygems repository #{file_uri_for(gem_repo4)}/, cached gems or installed locally for any resolution platforms (arm64-darwin-21), + and sorbet-static = 0.5.6433 could not be found in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally for any resolution platforms (arm64-darwin-21), sorbet cannot be used. So, because Gemfile depends on sorbet = 0.5.6433, version solving has failed. @@ -473,7 +473,7 @@ RSpec.describe "bundle install with specific platforms" do bundle "lock", raise_on_error: false, env: { "BUNDLE_FORCE_RUBY_PLATFORM" => "true" } expect(err).to include <<~ERROR.rstrip - Could not find gem 'sorbet-static (= 0.5.9889)' with platform 'ruby' in rubygems repository #{file_uri_for(gem_repo4)}/, cached gems or installed locally. + Could not find gem 'sorbet-static (= 0.5.9889)' with platform 'ruby' in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally. The source contains the following gems matching 'sorbet-static (= 0.5.9889)': * sorbet-static-0.5.9889-#{Gem::Platform.local} diff --git a/spec/bundler/install/gems/compact_index_spec.rb b/spec/bundler/install/gems/compact_index_spec.rb index 50add8743b..bd741685d9 100644 --- a/spec/bundler/install/gems/compact_index_spec.rb +++ b/spec/bundler/install/gems/compact_index_spec.rb @@ -15,6 +15,21 @@ RSpec.describe "compact index api" do expect(the_bundle).to include_gems "rack 1.0.0" end + it "has a debug mode" do + gemfile <<-G + source "#{source_uri}" + gem "rack" + G + + bundle :install, artifice: "compact_index", env: { "DEBUG_COMPACT_INDEX" => "true" } + expect(out).to include("Fetching gem metadata from #{source_uri}") + expect(err).to include("[Bundler::CompactIndexClient] available?") + expect(err).to include("[Bundler::CompactIndexClient] fetching versions") + expect(err).to include("[Bundler::CompactIndexClient] info(rack)") + expect(err).to include("[Bundler::CompactIndexClient] fetching info/rack") + expect(the_bundle).to include_gems "rack 1.0.0" + end + it "should URI encode gem names" do gemfile <<-G source "#{source_uri}" diff --git a/spec/bundler/install/gems/flex_spec.rb b/spec/bundler/install/gems/flex_spec.rb index 8ef3984975..5e0c88fc95 100644 --- a/spec/bundler/install/gems/flex_spec.rb +++ b/spec/bundler/install/gems/flex_spec.rb @@ -197,7 +197,7 @@ RSpec.describe "bundle flex_install" do Could not find compatible versions Because rack-obama >= 2.0 depends on rack = 1.2 - and rack = 1.2 could not be found in rubygems repository #{file_uri_for(gem_repo2)}/, cached gems or installed locally, + and rack = 1.2 could not be found in rubygems repository #{file_uri_for(gem_repo2)}/ or installed locally, rack-obama >= 2.0 cannot be used. So, because Gemfile depends on rack-obama = 2.0, version solving has failed. diff --git a/spec/bundler/install/gems/fund_spec.rb b/spec/bundler/install/gems/fund_spec.rb index 9aadc9ed25..c0d72b9c50 100644 --- a/spec/bundler/install/gems/fund_spec.rb +++ b/spec/bundler/install/gems/fund_spec.rb @@ -31,8 +31,8 @@ RSpec.describe "bundle install" do context "when gems include a fund URI" do it "displays the plural fund message after installing" do - install_gemfile <<-G - source "#{file_uri_for(gem_repo2)}" + install_gemfile <<-G, artifice: "compact_index" + source "https://gem.repo2" gem 'has_funding_and_other_metadata' gem 'has_funding' gem 'rack-obama' @@ -42,8 +42,8 @@ RSpec.describe "bundle install" do end it "displays the singular fund message after installing" do - install_gemfile <<-G - source "#{file_uri_for(gem_repo2)}" + install_gemfile <<-G, artifice: "compact_index" + source "https://gem.repo2" gem 'has_funding' gem 'rack-obama' G @@ -58,8 +58,8 @@ RSpec.describe "bundle install" do end it "does not display the plural fund message after installing" do - install_gemfile <<-G - source "#{file_uri_for(gem_repo2)}" + install_gemfile <<-G, artifice: "compact_index" + source "https://gem.repo2" gem 'has_funding_and_other_metadata' gem 'has_funding' gem 'rack-obama' @@ -69,8 +69,8 @@ RSpec.describe "bundle install" do end it "does not display the singular fund message after installing" do - install_gemfile <<-G - source "#{file_uri_for(gem_repo2)}" + install_gemfile <<-G, artifice: "compact_index" + source "https://gem.repo2" gem 'has_funding' gem 'rack-obama' G @@ -81,8 +81,8 @@ RSpec.describe "bundle install" do context "when gems do not include fund messages" do it "does not display any fund messages" do - install_gemfile <<-G - source "#{file_uri_for(gem_repo2)}" + install_gemfile <<-G, artifice: "compact_index" + source "https://gem.repo2" gem "activesupport" G @@ -92,8 +92,8 @@ RSpec.describe "bundle install" do context "when a dependency includes a fund message" do it "does not display the fund message" do - install_gemfile <<-G - source "#{file_uri_for(gem_repo2)}" + install_gemfile <<-G, artifice: "compact_index" + source "https://gem.repo2" gem 'gem_with_dependent_funding' G @@ -110,8 +110,8 @@ RSpec.describe "bundle install" do "funding_uri" => "https://example.com/also_has_funding/funding", } end - install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" + install_gemfile <<-G, artifice: "compact_index" + source "https://gem.repo1" gem 'also_has_funding', :git => '#{lib_path("also_has_funding-1.0")}' G @@ -124,8 +124,8 @@ RSpec.describe "bundle install" do "funding_uri" => "https://example.com/also_has_funding/funding", } end - install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" + install_gemfile <<-G, artifice: "compact_index" + source "https://gem.repo1" gem 'also_has_funding', :git => '#{lib_path("also_has_funding-1.0")}' G @@ -134,8 +134,8 @@ RSpec.describe "bundle install" do "funding_uri" => "https://example.com/also_has_funding/funding", } end - install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" + install_gemfile <<-G, artifice: "compact_index" + source "https://gem.repo1" gem 'also_has_funding', :git => '#{lib_path("also_has_funding-1.1")}' G @@ -149,14 +149,14 @@ RSpec.describe "bundle install" do } end gemfile <<-G - source "#{file_uri_for(gem_repo1)}" + source "https://gem.repo1" gem 'also_has_funding', :git => '#{lib_path("also_has_funding-1.0")}' G - bundle :install + bundle :install, artifice: "compact_index" expect(out).to include("1 installed gem you directly depend on is looking for funding.") - bundle :install + bundle :install, artifice: "compact_index" expect(out).to include("1 installed gem you directly depend on is looking for funding.") end end diff --git a/spec/bundler/install/gems/resolving_spec.rb b/spec/bundler/install/gems/resolving_spec.rb index c5f9c4a3d3..b54674898d 100644 --- a/spec/bundler/install/gems/resolving_spec.rb +++ b/spec/bundler/install/gems/resolving_spec.rb @@ -434,7 +434,7 @@ RSpec.describe "bundle install with install-time dependencies" do end nice_error = <<~E.strip - Could not find gems matching 'sorbet-static (= 0.5.10554)' valid for all resolution platforms (arm64-darwin-21, aarch64-linux) in rubygems repository #{file_uri_for(gem_repo4)}/, cached gems or installed locally. + Could not find gems matching 'sorbet-static (= 0.5.10554)' valid for all resolution platforms (arm64-darwin-21, aarch64-linux) in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally. The source contains the following gems matching 'sorbet-static (= 0.5.10554)': * sorbet-static-0.5.10554-universal-darwin-21 @@ -490,7 +490,7 @@ RSpec.describe "bundle install with install-time dependencies" do it "raises a proper error" do nice_error = <<~E.strip - Could not find gems matching 'sorbet-static' valid for all resolution platforms (arm-linux, x86_64-linux) in rubygems repository #{file_uri_for(gem_repo4)}/, cached gems or installed locally. + Could not find gems matching 'sorbet-static' valid for all resolution platforms (arm-linux, x86_64-linux) in rubygems repository #{file_uri_for(gem_repo4)}/ or installed locally. The source contains the following gems matching 'sorbet-static': * sorbet-static-0.5.10696-x86_64-linux diff --git a/spec/bundler/install/yanked_spec.rb b/spec/bundler/install/yanked_spec.rb index 7408c24327..5aeabd2f23 100644 --- a/spec/bundler/install/yanked_spec.rb +++ b/spec/bundler/install/yanked_spec.rb @@ -188,7 +188,7 @@ RSpec.context "when using gem before installing" do bundle :list, raise_on_error: false - expect(err).to include("Could not find rack-0.9.1 in cached gems or installed locally") + expect(err).to include("Could not find rack-0.9.1 in locally installed gems") expect(err).to_not include("Your bundle is locked to rack (0.9.1) from") expect(err).to_not include("If you haven't changed sources, that means the author of rack (0.9.1) has removed it.") expect(err).to_not include("You'll need to update your bundle to a different version of rack (0.9.1) that hasn't been removed in order to install.") @@ -197,7 +197,7 @@ RSpec.context "when using gem before installing" do lockfile lockfile.gsub(/PLATFORMS\n #{lockfile_platforms}/m, "PLATFORMS\n #{lockfile_platforms("ruby")}") bundle :list, raise_on_error: false - expect(err).to include("Could not find rack-0.9.1 in cached gems or installed locally") + expect(err).to include("Could not find rack-0.9.1 in locally installed gems") end it "does not suggest the author has yanked the gem when using more than one gem, but shows all gems that couldn't be found in the source" do @@ -224,7 +224,7 @@ RSpec.context "when using gem before installing" do bundle :list, raise_on_error: false - expect(err).to include("Could not find rack-0.9.1, rack_middleware-1.0 in cached gems or installed locally") + expect(err).to include("Could not find rack-0.9.1, rack_middleware-1.0 in locally installed gems") expect(err).to include("Install missing gems with `bundle install`.") expect(err).to_not include("Your bundle is locked to rack (0.9.1) from") expect(err).to_not include("If you haven't changed sources, that means the author of rack (0.9.1) has removed it.") diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb index 4fd081e7d0..521072c4ba 100644 --- a/spec/bundler/lock/lockfile_spec.rb +++ b/spec/bundler/lock/lockfile_spec.rb @@ -371,6 +371,112 @@ RSpec.describe "the lockfile format" do G end + it "does not add credentials to lockfile when it does not have them already" do + bundle "config set http://localgemserver.test/ user:pass" + + gemfile <<~G + source "#{file_uri_for(gem_repo1)}" + + source "http://localgemserver.test/" do + + end + + source "http://user:pass@othergemserver.test/" do + gem "rack-obama", ">= 1.0" + end + G + + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo2, "rack", "1.0.0" + c.checksum gem_repo2, "rack-obama", "1.0" + end + + lockfile_without_credentials = <<~L + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + + GEM + remote: http://localgemserver.test/ + specs: + + GEM + remote: http://othergemserver.test/ + specs: + rack (1.0.0) + rack-obama (1.0) + rack + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + rack-obama (>= 1.0)! + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + + lockfile lockfile_without_credentials + + bundle "install", artifice: "endpoint_strict_basic_authentication", quiet: true + + expect(lockfile).to eq lockfile_without_credentials + end + + it "keeps credentials in lockfile if already there" do + bundle "config set http://localgemserver.test/ user:pass" + + gemfile <<~G + source "#{file_uri_for(gem_repo1)}" + + source "http://localgemserver.test/" do + + end + + source "http://user:pass@othergemserver.test/" do + gem "rack-obama", ">= 1.0" + end + G + + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo2, "rack", "1.0.0" + c.checksum gem_repo2, "rack-obama", "1.0" + end + + lockfile_with_credentials = <<~L + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + + GEM + remote: http://localgemserver.test/ + specs: + + GEM + remote: http://user:pass@othergemserver.test/ + specs: + rack (1.0.0) + rack-obama (1.0) + rack + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + rack-obama (>= 1.0)! + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + + lockfile lockfile_with_credentials + + bundle "install", artifice: "endpoint_strict_basic_authentication", quiet: true + + expect(lockfile).to eq lockfile_with_credentials + end + it "generates lockfiles with multiple requirements" do install_gemfile <<-G source "#{file_uri_for(gem_repo2)}/" @@ -1117,7 +1223,7 @@ RSpec.describe "the lockfile format" do end it "stores relative paths when the path is provided for gemspec" do - build_lib("foo", path: tmp.join("foo")) + build_lib("foo", path: tmp("foo")) checksums = checksums_section_when_existing do |c| c.no_checksum "foo", "1.0" diff --git a/spec/bundler/other/major_deprecation_spec.rb b/spec/bundler/other/major_deprecation_spec.rb index 939b68a0bb..e7577d38b4 100644 --- a/spec/bundler/other/major_deprecation_spec.rb +++ b/spec/bundler/other/major_deprecation_spec.rb @@ -618,7 +618,12 @@ RSpec.describe "major deprecations" do pending "fails with a helpful message", bundler: "3" end - describe "deprecating rubocop", :readline do + describe "deprecating 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" + end + context "bundle gem --rubocop" do before do bundle "gem my_new_gem --rubocop", raise_on_error: false diff --git a/spec/bundler/plugins/hook_spec.rb b/spec/bundler/plugins/hook_spec.rb index f6ee0ba210..977fcefab3 100644 --- a/spec/bundler/plugins/hook_spec.rb +++ b/spec/bundler/plugins/hook_spec.rb @@ -69,7 +69,7 @@ RSpec.describe "hook plugins" do bundle "plugin install after-install-all-plugin --source #{file_uri_for(gem_repo2)}" end - it "runs after each rubygem is installed" do + it "runs after each all rubygems are installed" do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "rake" diff --git a/spec/bundler/runtime/gem_tasks_spec.rb b/spec/bundler/runtime/gem_tasks_spec.rb index f7afc0eb92..d086a72c9c 100644 --- a/spec/bundler/runtime/gem_tasks_spec.rb +++ b/spec/bundler/runtime/gem_tasks_spec.rb @@ -57,7 +57,7 @@ RSpec.describe "require 'bundler/gem_tasks'" do context "rake build when path has spaces", :ruby_repo do before do - spaced_bundled_app = tmp.join("bundled app") + spaced_bundled_app = tmp("bundled app") FileUtils.cp_r bundled_app, spaced_bundled_app bundle "exec rake build", dir: spaced_bundled_app end @@ -69,7 +69,7 @@ RSpec.describe "require 'bundler/gem_tasks'" do context "rake build when path has brackets", :ruby_repo do before do - bracketed_bundled_app = tmp.join("bundled[app") + bracketed_bundled_app = tmp("bundled[app") FileUtils.cp_r bundled_app, bracketed_bundled_app bundle "exec rake build", dir: bracketed_bundled_app end diff --git a/spec/bundler/runtime/require_spec.rb b/spec/bundler/runtime/require_spec.rb index 76271a5593..e630e902c9 100644 --- a/spec/bundler/runtime/require_spec.rb +++ b/spec/bundler/runtime/require_spec.rb @@ -430,6 +430,30 @@ RSpec.describe "Bundler.require" do expect(out).to eq("WIN") end + + it "does not extract gemspecs from application cache packages" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + G + + bundle :cache + + path = cached_gem("rack-1.0.0") + + run <<-R + File.open("#{path}", "w") do |f| + f.write "broken package" + end + R + + run <<-R + Bundler.require + puts "WIN" + R + + expect(out).to eq("WIN") + end end RSpec.describe "Bundler.require with platform specific dependencies" do diff --git a/spec/bundler/runtime/self_management_spec.rb b/spec/bundler/runtime/self_management_spec.rb index d15ca3189e..d489d949bb 100644 --- a/spec/bundler/runtime/self_management_spec.rb +++ b/spec/bundler/runtime/self_management_spec.rb @@ -35,6 +35,17 @@ RSpec.describe "Self management", rubygems: ">= 3.3.0.dev", realworld: true do bundle "-v", artifice: nil expect(out).to end_with(previous_minor[0] == "2" ? "Bundler version #{previous_minor}" : previous_minor) + # App now uses locked version, even when not using the CLI directly + file = bundled_app("bin/bundle_version.rb") + create_file file, <<-RUBY + #!#{Gem.ruby} + require 'bundler/setup' + puts Bundler::VERSION + RUBY + file.chmod(0o777) + sys_exec "bin/bundle_version.rb", artifice: nil + expect(out).to eq(previous_minor) + # Subsequent installs use the locked version without reinstalling bundle "install --verbose", artifice: nil expect(out).to include("Using bundler #{previous_minor}") @@ -57,6 +68,17 @@ RSpec.describe "Self management", rubygems: ">= 3.3.0.dev", realworld: true do bundle "-v" expect(out).to end_with(previous_minor[0] == "2" ? "Bundler version #{previous_minor}" : previous_minor) + # App now uses locked version, even when not using the CLI directly + file = bundled_app("bin/bundle_version.rb") + create_file file, <<-RUBY + #!#{Gem.ruby} + require 'bundler/setup' + puts Bundler::VERSION + RUBY + file.chmod(0o777) + sys_exec "bin/bundle_version.rb", artifice: nil + 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}") diff --git a/spec/bundler/runtime/setup_spec.rb b/spec/bundler/runtime/setup_spec.rb index 2d78825de4..7f4c0759d1 100644 --- a/spec/bundler/runtime/setup_spec.rb +++ b/spec/bundler/runtime/setup_spec.rb @@ -767,7 +767,7 @@ end expect(err).to be_empty end - it "can require rubygems without warnings, when using a local cache", rubygems: ">= 3.5.10" do + it "can require rubygems without warnings, when using a local cache", :truffleruby do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "rack" @@ -942,9 +942,9 @@ end G run <<-R - puts Bundler.rubygems.all_specs.map(&:name) + puts Bundler.rubygems.installed_specs.map(&:name) Gem.refresh - puts Bundler.rubygems.all_specs.map(&:name) + puts Bundler.rubygems.installed_specs.map(&:name) R expect(out).to eq("activesupport\nbundler\nactivesupport\nbundler") @@ -1376,6 +1376,24 @@ end expect(out).to eq("undefined\nconstant") end + it "activates default gems when they are part of the bundle, but not installed explicitly", :ruby_repo do + default_json_version = ruby "gem 'json'; require 'json'; puts JSON::VERSION" + + build_repo2 do + build_gem "json", default_json_version + end + + gemfile "source \"#{file_uri_for(gem_repo2)}\"; gem 'json'" + + ruby <<-RUBY + require "bundler/setup" + require "json" + puts defined?(::JSON) ? "JSON defined" : "JSON undefined" + RUBY + + expect(err).to be_empty + end + describe "default gem activation" do let(:exemptions) do exempts = %w[did_you_mean bundler uri pathname] diff --git a/spec/bundler/support/artifice/helpers/compact_index.rb b/spec/bundler/support/artifice/helpers/compact_index.rb index a803a2d30a..3fc1ce7fef 100644 --- a/spec/bundler/support/artifice/helpers/compact_index.rb +++ b/spec/bundler/support/artifice/helpers/compact_index.rb @@ -40,7 +40,7 @@ class CompactIndexAPI < Endpoint end def requested_range_for(response_body) - ranges = Rack::Utils.byte_ranges(env, response_body.bytesize) + ranges = Rack::Utils.get_byte_ranges(env["HTTP_RANGE"], response_body.bytesize) if ranges status 206 diff --git a/spec/bundler/support/build_metadata.rb b/spec/bundler/support/build_metadata.rb index 5898e7f3bd..189100edb7 100644 --- a/spec/bundler/support/build_metadata.rb +++ b/spec/bundler/support/build_metadata.rb @@ -41,7 +41,7 @@ module Spec end def git_commit_sha - ruby_core_tarball? ? "unknown" : sys_exec("git rev-parse --short HEAD", dir: source_root).strip + ruby_core_tarball? ? "unknown" : git("rev-parse --short HEAD", source_root).strip end extend self diff --git a/spec/bundler/support/builders.rb b/spec/bundler/support/builders.rb index ab2dafb0b9..8f646b9358 100644 --- a/spec/bundler/support/builders.rb +++ b/spec/bundler/support/builders.rb @@ -518,7 +518,6 @@ module Spec if options[:rubygems_version] @spec.rubygems_version = options[:rubygems_version] - def @spec.mark_version; end def @spec.validate(*); end end diff --git a/spec/bundler/support/command_execution.rb b/spec/bundler/support/command_execution.rb index 5639fda3b6..02726744d3 100644 --- a/spec/bundler/support/command_execution.rb +++ b/spec/bundler/support/command_execution.rb @@ -1,7 +1,37 @@ # frozen_string_literal: true module Spec - CommandExecution = Struct.new(:command, :working_directory, :exitstatus, :original_stdout, :original_stderr) do + class CommandExecution + def initialize(command, working_directory:, timeout:) + @command = command + @working_directory = working_directory + @timeout = timeout + @original_stdout = String.new + @original_stderr = String.new + end + + attr_accessor :exitstatus, :command, :original_stdout, :original_stderr + attr_reader :timeout + attr_writer :failure_reason + + def raise_error! + return unless failure? + + error_header = if failure_reason == :timeout + "Invoking `#{command}` was aborted after #{timeout} seconds with output:" + else + "Invoking `#{command}` failed with output:" + end + + raise <<~ERROR + #{error_header} + + ---------------------------------------------------------------------- + #{stdboth} + ---------------------------------------------------------------------- + ERROR + end + def to_s "$ #{command}" end @@ -12,16 +42,11 @@ module Spec end def stdout - original_stdout + normalize(original_stdout) end - # Can be removed once/if https://github.com/oneclick/rubyinstaller2/pull/369 is resolved def stderr - return original_stderr unless Gem.win_platform? - - original_stderr.split("\n").reject do |l| - l.include?("operating_system_defaults") - end.join("\n") + normalize(original_stderr) end def to_s_verbose @@ -42,5 +67,13 @@ module Spec return true unless exitstatus exitstatus > 0 end + + private + + attr_reader :failure_reason + + def normalize(string) + string.force_encoding(Encoding::UTF_8).strip.gsub("\r\n", "\n") + end end end diff --git a/spec/bundler/support/env.rb b/spec/bundler/support/env.rb new file mode 100644 index 0000000000..4d99c892cd --- /dev/null +++ b/spec/bundler/support/env.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Spec + module Env + def ruby_core? + !ENV["GEM_COMMAND"].nil? + end + end +end diff --git a/spec/bundler/support/filters.rb b/spec/bundler/support/filters.rb index 8e164af756..e1683ae75b 100644 --- a/spec/bundler/support/filters.rb +++ b/spec/bundler/support/filters.rb @@ -14,7 +14,7 @@ class RequirementChecker < Proc attr_accessor :provided def inspect - "\"!= #{provided}\"" + "\"#{provided}\"" end end diff --git a/spec/bundler/support/helpers.rb b/spec/bundler/support/helpers.rb index 1ad9cc78ca..392ed200dd 100644 --- a/spec/bundler/support/helpers.rb +++ b/spec/bundler/support/helpers.rb @@ -1,12 +1,17 @@ # frozen_string_literal: true -require_relative "command_execution" require_relative "the_bundle" require_relative "path" +require_relative "options" +require_relative "subprocess" module Spec module Helpers include Spec::Path + include Spec::Options + include Spec::Subprocess + + class TimeoutExceeded < StandardError; end def reset! Dir.glob("#{tmp}/{gems/*,*}", File::FNM_DOTMATCH).each do |dir| @@ -27,22 +32,6 @@ module Spec TheBundle.new(*args) end - def command_executions - @command_executions ||= [] - end - - def last_command - command_executions.last || raise("There is no last command") - end - - def out - last_command.stdout - end - - def err - last_command.stderr - end - MAJOR_DEPRECATION = /^\[DEPRECATED\]\s*/ def err_without_deprecations @@ -53,10 +42,6 @@ module Spec err.split("\n").select {|l| l =~ MAJOR_DEPRECATION }.join("\n").split(MAJOR_DEPRECATION) end - def exitstatus - last_command.exitstatus - end - def run(cmd, *args) opts = args.last.is_a?(Hash) ? args.pop : {} groups = args.map(&:inspect).join(", ") @@ -122,7 +107,7 @@ module Spec end def bundler(cmd, options = {}) - options[:bundle_bin] = system_gem_path.join("bin/bundler") + options[:bundle_bin] = system_gem_path("bin/bundler") bundle(cmd, options) end @@ -175,63 +160,30 @@ module Spec "#{Gem.ruby} -S #{ENV["GEM_PATH"]}/bin/rake" end - def git(cmd, path, options = {}) - sys_exec("git #{cmd}", options.merge(dir: path)) - end - - def sys_exec(cmd, options = {}) + 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"]) - dir = options[:dir] || bundled_app - command_execution = CommandExecution.new(cmd.to_s, dir) - - require "open3" - require "shellwords" - Open3.popen3(env, *cmd.shellsplit, chdir: dir) do |stdin, stdout, stderr, wait_thr| - yield stdin, stdout, wait_thr if block_given? - stdin.close - - stdout_read_thread = Thread.new { stdout.read } - stderr_read_thread = Thread.new { stderr.read } - command_execution.original_stdout = stdout_read_thread.value.strip - command_execution.original_stderr = stderr_read_thread.value.strip - - status = wait_thr.value - command_execution.exitstatus = if status.exited? - status.exitstatus - elsif status.signaled? - exit_status_for_signal(status.termsig) - end - end - - unless options[:raise_on_error] == false || command_execution.success? - raise <<~ERROR - - Invoking `#{cmd}` failed with output: - ---------------------------------------------------------------------- - #{command_execution.stdboth} - ---------------------------------------------------------------------- - ERROR - end - - command_executions << command_execution + options[:env] = env + options[:dir] ||= bundled_app - command_execution.stdout + sh(cmd, options, &block) end - def all_commands_output - return "" if command_executions.empty? + def config(config = nil, path = bundled_app(".bundle/config")) + current = File.exist?(path) ? Psych.load_file(path) : {} + return current unless config - "\n\nCommands:\n#{command_executions.map(&:to_s_verbose).join("\n\n")}" - end + current = {} if current.empty? - def config(config = nil, path = bundled_app(".bundle/config")) - return Psych.load_file(path) unless config FileUtils.mkdir_p(File.dirname(path)) - File.open(path, "w") do |f| - f.puts config.to_yaml + + new_config = current.merge(config).compact + + File.open(path, "w+") do |f| + f.puts new_config.to_yaml end - config + + new_config end def global_config(config = nil) @@ -361,16 +313,6 @@ module Spec end end - def opt_add(option, options) - [option.strip, options].compact.reject(&:empty?).join(" ") - end - - def opt_remove(option, options) - return unless options - - options.split(" ").reject {|opt| opt.strip == option.strip }.join(" ") - end - def break_git! FileUtils.mkdir_p(tmp("broken_path")) File.open(tmp("broken_path/git"), "w", 0o755) do |f| @@ -471,7 +413,7 @@ module Spec end def revision_for(path) - sys_exec("git rev-parse HEAD", dir: path).strip + git("rev-parse HEAD", path).strip end def with_read_only(pattern) diff --git a/spec/bundler/support/options.rb b/spec/bundler/support/options.rb new file mode 100644 index 0000000000..551fa1acd8 --- /dev/null +++ b/spec/bundler/support/options.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Spec + module Options + def opt_add(option, options) + [option.strip, options].compact.reject(&:empty?).join(" ") + end + + def opt_remove(option, options) + return unless options + + options.split(" ").reject {|opt| opt.strip == option.strip }.join(" ") + end + end +end diff --git a/spec/bundler/support/path.rb b/spec/bundler/support/path.rb index 7352d5a353..26be5488c3 100644 --- a/spec/bundler/support/path.rb +++ b/spec/bundler/support/path.rb @@ -3,8 +3,12 @@ require "pathname" require "rbconfig" +require_relative "env" + module Spec module Path + include Spec::Env + def source_root @source_root ||= Pathname.new(ruby_core? ? "../../.." : "../..").expand_path(__dir__) end @@ -58,7 +62,7 @@ module Spec end def gem_bin - @gem_bin ||= ruby_core? ? ENV["GEM_COMMAND"] : "gem" + @gem_bin ||= ENV["GEM_COMMAND"] || "gem" end def path @@ -109,7 +113,7 @@ module Spec end def home(*path) - tmp.join("home", *path) + tmp("home", *path) end def default_bundle_path(*path) @@ -129,13 +133,13 @@ module Spec end def bundled_app(*path) - root = tmp.join("bundled_app") + root = tmp("bundled_app") FileUtils.mkdir_p(root) root.join(*path) end def bundled_app2(*path) - root = tmp.join("bundled_app2") + root = tmp("bundled_app2") FileUtils.mkdir_p(root) root.join(*path) end @@ -161,15 +165,15 @@ module Spec end def base_system_gems - tmp.join("gems/base") + tmp("gems/base") end def rubocop_gems - tmp.join("gems/rubocop") + tmp("gems/rubocop") end def standard_gems - tmp.join("gems/standard") + tmp("gems/standard") end def file_uri_for(path) @@ -257,17 +261,6 @@ module Spec File.open(gemspec_file, "w") {|f| f << contents } end - def ruby_core? - # avoid to warnings - @ruby_core ||= nil - - if @ruby_core.nil? - @ruby_core = true & ENV["GEM_COMMAND"] - else - @ruby_core - end - end - def git_root ruby_core? ? source_root : source_root.parent end @@ -277,7 +270,7 @@ module Spec def git_ls_files(glob) skip "Not running on a git context, since running tests from a tarball" if ruby_core_tarball? - sys_exec("git ls-files -z -- #{glob}", dir: source_root).split("\x0") + git("ls-files -z -- #{glob}", source_root).split("\x0") end def tracked_files_glob diff --git a/spec/bundler/support/rubygems_ext.rb b/spec/bundler/support/rubygems_ext.rb index 889ebc90c3..7748234abc 100644 --- a/spec/bundler/support/rubygems_ext.rb +++ b/spec/bundler/support/rubygems_ext.rb @@ -70,7 +70,7 @@ module Spec ENV["BUNDLE_PATH"] = nil ENV["GEM_HOME"] = ENV["GEM_PATH"] = Path.base_system_gem_path.to_s - ENV["PATH"] = [Path.system_gem_path.join("bin"), ENV["PATH"]].join(File::PATH_SEPARATOR) + 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? end @@ -117,7 +117,14 @@ module Spec def gem_activate_and_possibly_install(gem_name) gem_activate(gem_name) rescue Gem::LoadError => e - Gem.install(gem_name, e.requirement) + # Windows 3.0 puts a Windows stub script as `rake` while it should be + # named `rake.bat`. RubyGems does not like that and avoids overwriting it + # unless explicitly instructed to do so with `force`. + if RUBY_VERSION.start_with?("3.0") && Gem.win_platform? + Gem.install(gem_name, e.requirement, force: true) + else + Gem.install(gem_name, e.requirement) + end retry end diff --git a/spec/bundler/support/rubygems_version_manager.rb b/spec/bundler/support/rubygems_version_manager.rb index 88da14b67e..c174c461f0 100644 --- a/spec/bundler/support/rubygems_version_manager.rb +++ b/spec/bundler/support/rubygems_version_manager.rb @@ -1,12 +1,13 @@ # frozen_string_literal: true -require "pathname" -require_relative "helpers" -require_relative "path" +require_relative "options" +require_relative "env" +require_relative "subprocess" class RubygemsVersionManager - include Spec::Helpers - include Spec::Path + include Spec::Options + include Spec::Env + include Spec::Subprocess def initialize(source) @source = source @@ -57,7 +58,7 @@ class RubygemsVersionManager cmd = [RbConfig.ruby, $0, *ARGV].compact - ENV["RUBYOPT"] = opt_add("-I#{local_copy_path.join("lib")}", opt_remove("--disable-gems", ENV["RUBYOPT"])) + ENV["RUBYOPT"] = opt_add("-I#{File.join(local_copy_path, "lib")}", opt_remove("--disable-gems", ENV["RUBYOPT"])) exec(ENV, *cmd) end @@ -65,14 +66,14 @@ class RubygemsVersionManager def switch_local_copy_if_needed return unless local_copy_switch_needed? - sys_exec("git checkout #{target_tag}", dir: local_copy_path) + git("checkout #{target_tag}", local_copy_path) - ENV["RGV"] = local_copy_path.to_s + ENV["RGV"] = local_copy_path end def rubygems_unrequire_needed? require "rubygems" - !$LOADED_FEATURES.include?(local_copy_path.join("lib/rubygems.rb").to_s) + !$LOADED_FEATURES.include?(File.join(local_copy_path, "lib/rubygems.rb")) end def local_copy_switch_needed? @@ -84,7 +85,7 @@ class RubygemsVersionManager end def local_copy_tag - sys_exec("git rev-parse --abbrev-ref HEAD", dir: local_copy_path) + git("rev-parse --abbrev-ref HEAD", local_copy_path) end def local_copy_path @@ -94,21 +95,25 @@ class RubygemsVersionManager def resolve_local_copy_path return expanded_source if source_is_path? - rubygems_path = source_root.join("tmp/rubygems") + rubygems_path = File.join(source_root, "tmp/rubygems") - unless rubygems_path.directory? - sys_exec("git clone .. #{rubygems_path}", dir: source_root) + unless File.directory?(rubygems_path) + git("clone .. #{rubygems_path}", source_root) end rubygems_path end def source_is_path? - expanded_source.directory? + File.directory?(expanded_source) end def expanded_source - @expanded_source ||= Pathname.new(@source).expand_path(source_root) + @expanded_source ||= File.expand_path(@source, source_root) + end + + def source_root + @source_root ||= File.expand_path(ruby_core? ? "../../.." : "../..", __dir__) end def resolve_target_tag diff --git a/spec/bundler/support/subprocess.rb b/spec/bundler/support/subprocess.rb new file mode 100644 index 0000000000..711bfbbeed --- /dev/null +++ b/spec/bundler/support/subprocess.rb @@ -0,0 +1,106 @@ +# frozen_string_literal: true + +require_relative "command_execution" + +module Spec + module Subprocess + def command_executions + @command_executions ||= [] + end + + def last_command + command_executions.last || raise("There is no last command") + end + + def out + last_command.stdout + end + + def err + last_command.stderr + end + + def exitstatus + last_command.exitstatus + end + + def git(cmd, path = Dir.pwd, options = {}) + sh("git #{cmd}", options.merge(dir: path)) + end + + def sh(cmd, options = {}) + dir = options[:dir] + env = options[:env] || {} + + command_execution = CommandExecution.new(cmd.to_s, working_directory: dir, timeout: 60) + + require "open3" + require "shellwords" + Open3.popen3(env, *cmd.shellsplit, chdir: dir) do |stdin, stdout, stderr, wait_thr| + yield stdin, stdout, wait_thr if block_given? + stdin.close + + stdout_handler = ->(data) { command_execution.original_stdout << data } + stderr_handler = ->(data) { command_execution.original_stderr << data } + + stdout_thread = read_stream(stdout, stdout_handler, timeout: command_execution.timeout) + stderr_thread = read_stream(stderr, stderr_handler, timeout: command_execution.timeout) + + stdout_thread.join + stderr_thread.join + + status = wait_thr.value + command_execution.exitstatus = if status.exited? + status.exitstatus + elsif status.signaled? + exit_status_for_signal(status.termsig) + end + rescue TimeoutExceeded + command_execution.failure_reason = :timeout + command_execution.exitstatus = exit_status_for_signal(Signal.list["INT"]) + end + + unless options[:raise_on_error] == false || command_execution.success? + command_execution.raise_error! + end + + command_executions << command_execution + + command_execution.stdout + end + + # Mostly copied from https://github.com/piotrmurach/tty-command/blob/49c37a895ccea107e8b78d20e4cb29de6a1a53c8/lib/tty/command/process_runner.rb#L165-L193 + def read_stream(stream, handler, timeout:) + Thread.new do + Thread.current.report_on_exception = false + cmd_start = Time.now + readers = [stream] + + while readers.any? + ready = IO.select(readers, nil, readers, timeout) + raise TimeoutExceeded if ready.nil? + + ready[0].each do |reader| + chunk = reader.readpartial(16 * 1024) + handler.call(chunk) + + # control total time spent reading + runtime = Time.now - cmd_start + time_left = timeout - runtime + raise TimeoutExceeded if time_left < 0.0 + rescue Errno::EAGAIN, Errno::EINTR + rescue EOFError, Errno::EPIPE, Errno::EIO + readers.delete(reader) + reader.close + end + end + end + end + + def all_commands_output + return "" if command_executions.empty? + + "\n\nCommands:\n#{command_executions.map(&:to_s_verbose).join("\n\n")}" + end + end +end diff --git a/spec/bundler/update/git_spec.rb b/spec/bundler/update/git_spec.rb index 3b7bbfd979..f80281e8ce 100644 --- a/spec/bundler/update/git_spec.rb +++ b/spec/bundler/update/git_spec.rb @@ -142,8 +142,8 @@ RSpec.describe "bundle update" do s.add_dependency "submodule" end - sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", dir: lib_path("has_submodule-1.0") - sys_exec "git commit -m \"submodulator\"", dir: lib_path("has_submodule-1.0") + git "submodule add #{lib_path("submodule-1.0")} submodule-1.0", lib_path("has_submodule-1.0") + git "commit -m \"submodulator\"", lib_path("has_submodule-1.0") end it "it unlocks the source when submodules are added to a git source" do |