From 4ffd471ed18527248cc7886071a9da0cf9641ae9 Mon Sep 17 00:00:00 2001 From: Lamont Granquist Date: Mon, 26 Nov 2018 23:25:04 -0800 Subject: gem_package provider supports --no-document and rubygems 3.x should still maintain backcompat for rubygems < 2.0 for RHEL6 and other old platforms. Signed-off-by: Lamont Granquist --- lib/chef/provider/package/rubygems.rb | 54 +++++++++++++++++++--------- spec/unit/provider/package/rubygems_spec.rb | 56 ++++++++++++++++++++++++----- 2 files changed, 85 insertions(+), 25 deletions(-) diff --git a/lib/chef/provider/package/rubygems.rb b/lib/chef/provider/package/rubygems.rb index d99dce8972..cd595e64f4 100644 --- a/lib/chef/provider/package/rubygems.rb +++ b/lib/chef/provider/package/rubygems.rb @@ -1,7 +1,7 @@ # # Author:: Adam Jacob () # Author:: Daniel DeLeo () -# Copyright:: Copyright 2008-2016, 2010-2017, Chef Software Inc. +# Copyright:: Copyright 2008-2016, 2010-2018, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -49,42 +49,44 @@ class Chef DEFAULT_UNINSTALLER_OPTS = { ignore: true, executables: true }.freeze - ## # The paths where rubygems should search for installed gems. # Implemented by subclasses. def gem_paths raise NotImplementedError end - ## # A rubygems source index containing the list of gemspecs for all # available gems in the gem installation. # Implemented by subclasses - # === Returns - # Gem::SourceIndex + # + # @return [Gem::SourceIndex] + # def gem_source_index raise NotImplementedError end - ## # A rubygems specification object containing the list of gemspecs for all # available gems in the gem installation. # Implemented by subclasses # For rubygems >= 1.8.0 - # === Returns - # Gem::Specification + # + # @return [Gem::Specification] + # def gem_specification raise NotImplementedError end - ## + def rubygems_version + raise NotImplementedError + end + # Lists the installed versions of +gem_name+, constrained by the # version spec in +gem_dep+ - # === Arguments - # Gem::Dependency +gem_dep+ is a Gem::Dependency object, its version - # specification constrains which gems are returned. - # === Returns - # [Gem::Specification] an array of Gem::Specification objects + # + # @param gem_dep [Gem::Dependency] the version specification that constrains + # which gems are used. + # @return [Array] an array of Gem::Specification objects + # def installed_versions(gem_dep) rubygems_version = Gem::Version.new(Gem::VERSION) if rubygems_version >= Gem::Version.new("2.7") @@ -266,6 +268,10 @@ class Chef Gem::Specification end + def rubygems_version + Gem::VERSION + end + def candidate_version_from_remote(gem_dependency, *sources) with_gem_sources(*sources) do find_newest_remote_version(gem_dependency, *sources) @@ -293,6 +299,10 @@ class Chef @gem_binary_location = gem_binary_location end + def rubygems_version + @rubygems_version ||= shell_out!("#{@gem_binary_location} --version").stdout.chomp + end + def gem_paths if self.class.gempath_cache.key?(@gem_binary_location) self.class.gempath_cache[@gem_binary_location] @@ -547,9 +557,9 @@ class Chef end src_str = src.empty? ? "" : " #{src.join(" ")}" if !version.nil? && !version.empty? - shell_out!("#{gem_binary_path} install #{name} -q --no-rdoc --no-ri -v \"#{version}\"#{src_str}#{opts}", env: nil) + shell_out!("#{gem_binary_path} install #{name} -q #{rdoc_string} -v \"#{version}\"#{src_str}#{opts}", env: nil) else - shell_out!("#{gem_binary_path} install \"#{name}\" -q --no-rdoc --no-ri #{src_str}#{opts}", env: nil) + shell_out!("#{gem_binary_path} install \"#{name}\" -q #{rdoc_string} #{src_str}#{opts}", env: nil) end end @@ -585,6 +595,18 @@ class Chef private + def rdoc_string + if needs_nodocument? + "--no-document" + else + "--no-rdoc --no-ri" + end + end + + def needs_nodocument? + Gem::Requirement.new(">= 3.0.0.beta1").satisfied_by?(Gem::Version.new(gem_env.rubygems_version)) + end + def opts expand_options(new_resource.options) end diff --git a/spec/unit/provider/package/rubygems_spec.rb b/spec/unit/provider/package/rubygems_spec.rb index 2e9888fb78..1bafefe5e8 100644 --- a/spec/unit/provider/package/rubygems_spec.rb +++ b/spec/unit/provider/package/rubygems_spec.rb @@ -396,6 +396,10 @@ describe Chef::Provider::Package::Rubygems do allow(RbConfig::CONFIG).to receive(:[]).with("arch").and_call_original allow(File).to receive(:executable?).and_return false allow(File).to receive(:executable?).with("#{bindir}/gem").and_return true + # XXX: we can't stub the provider object directly here because referencing it will create it and that + # will break later tests that want to test the initialize method, so we stub any instance + # (yet more evidence that initialize methods should be thin and do very little work) + allow_any_instance_of(Chef::Provider::Package::Rubygems).to receive(:needs_nodocument?).and_return true end describe "when new_resource version is nil" do @@ -696,7 +700,25 @@ describe Chef::Provider::Package::Rubygems do let(:options) { "-i /alt/install/location" } it "installs the gem by shelling out when options are provided as a String" do + expected = "gem install rspec-core -q --no-document -v \"#{target_version}\" --source=https://www.rubygems.org #{options}" + expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900) + provider.run_action(:install) + expect(new_resource).to be_updated_by_last_action + end + + it "unmockening needs_nodocument?" do + expected = "gem install rspec-core -q --no-document -v \"#{target_version}\" --source=https://www.rubygems.org #{options}" + expect(provider).to receive(:needs_nodocument?).and_call_original + stub_const("Gem::VERSION", "3.0.0") + expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900) + provider.run_action(:install) + expect(new_resource).to be_updated_by_last_action + end + + it "when the rubygems_version is old it uses the old flags" do expected = "gem install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\" --source=https://www.rubygems.org #{options}" + expect(provider).to receive(:needs_nodocument?).and_call_original + stub_const("Gem::VERSION", "2.8.0") expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900) provider.run_action(:install) expect(new_resource).to be_updated_by_last_action @@ -709,7 +731,7 @@ describe Chef::Provider::Package::Rubygems do it "installs the gem with rubygems.org as an added source" do Chef::Config[:rubygems_url] = "https://mirror1" expect(provider.gem_env).to receive(:candidate_version_from_remote).with(gem_dep, Chef::Config[:rubygems_url]).and_return(version) - expected = "#{gem_binary} install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\" --source=https://mirror1" + expected = "#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --source=https://mirror1" expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900) provider.run_action(:install) expect(new_resource).to be_updated_by_last_action @@ -721,7 +743,7 @@ describe Chef::Provider::Package::Rubygems do let(:gem_binary) { "/foo/bar" } it "installs the gem with rubygems.org as an added source" do - expected = "#{gem_binary} install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\" --source=#{source} --source=https://www.rubygems.org" + expected = "#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --source=#{source} --source=https://www.rubygems.org" expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900) provider.run_action(:install) expect(new_resource).to be_updated_by_last_action @@ -732,7 +754,7 @@ describe Chef::Provider::Package::Rubygems do it "ignores the Chef::Config setting" do Chef::Config[:rubygems_url] = "https://ignored" - expected = "#{gem_binary} install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\" --source=#{source}" + expected = "#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --source=#{source}" expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900) provider.run_action(:install) expect(new_resource).to be_updated_by_last_action @@ -745,7 +767,7 @@ describe Chef::Provider::Package::Rubygems do let(:gem_binary) { "/foo/bar" } it "installs the gem with an array as an added source" do - expected = "#{gem_binary} install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\" --source=https://mirror1 --source=https://mirror2 --source=https://www.rubygems.org" + expected = "#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --source=https://mirror1 --source=https://mirror2 --source=https://www.rubygems.org" expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900) provider.run_action(:install) expect(new_resource).to be_updated_by_last_action @@ -756,7 +778,7 @@ describe Chef::Provider::Package::Rubygems do it "ignores the Chef::Config setting" do Chef::Config[:rubygems_url] = "https://ignored" - expected = "#{gem_binary} install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\" --source=https://mirror1 --source=https://mirror2" + expected = "#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --source=https://mirror1 --source=https://mirror2" expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900) provider.run_action(:install) expect(new_resource).to be_updated_by_last_action @@ -770,7 +792,7 @@ describe Chef::Provider::Package::Rubygems do it "installs the gem" do new_resource.clear_sources(true) - expected = "#{gem_binary} install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\" --clear-sources --source=#{source} --source=https://www.rubygems.org" + expected = "#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --clear-sources --source=#{source} --source=https://www.rubygems.org" expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900) provider.run_action(:install) expect(new_resource).to be_updated_by_last_action @@ -782,7 +804,7 @@ describe Chef::Provider::Package::Rubygems do let(:options) { "-i /alt/install/location" } it "installs the gem by shelling out when options are provided but no version is given" do - expected = "gem install rspec-core -q --no-rdoc --no-ri -v \"#{candidate_version}\" --source=https://www.rubygems.org #{options}" + expected = "gem install rspec-core -q --no-document -v \"#{candidate_version}\" --source=https://www.rubygems.org #{options}" expect(provider).to receive(:shell_out_compacted!).with(expected, env: nil, timeout: 900) provider.run_action(:install) expect(new_resource).to be_updated_by_last_action @@ -846,6 +868,22 @@ describe Chef::Provider::Package::Rubygems do let(:gem_binary) { "/usr/weird/bin/gem" } it "installs the gem by shelling out to gem install" do + expect(provider).to receive(:shell_out_compacted!).with("#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --source=https://www.rubygems.org", env: nil, timeout: 900) + provider.run_action(:install) + expect(new_resource).to be_updated_by_last_action + end + + it "unmockening needs_nodocument?" do + expect(provider).to receive(:needs_nodocument?).and_call_original + expect(provider.gem_env).to receive(:shell_out!).with("#{gem_binary} --version").and_return(instance_double(Mixlib::ShellOut, stdout: "3.0.0\n")) + expect(provider).to receive(:shell_out_compacted!).with("#{gem_binary} install rspec-core -q --no-document -v \"#{target_version}\" --source=https://www.rubygems.org", env: nil, timeout: 900) + provider.run_action(:install) + expect(new_resource).to be_updated_by_last_action + end + + it "when the rubygems_version is old it uses the old flags" do + expect(provider).to receive(:needs_nodocument?).and_call_original + expect(provider.gem_env).to receive(:shell_out!).with("#{gem_binary} --version").and_return(instance_double(Mixlib::ShellOut, stdout: "2.8.0\n")) expect(provider).to receive(:shell_out_compacted!).with("#{gem_binary} install rspec-core -q --no-rdoc --no-ri -v \"#{target_version}\" --source=https://www.rubygems.org", env: nil, timeout: 900) provider.run_action(:install) expect(new_resource).to be_updated_by_last_action @@ -856,7 +894,7 @@ describe Chef::Provider::Package::Rubygems do let(:target_version) { ">= 0" } it "installs the gem by shelling out to gem install" do - expect(provider).to receive(:shell_out_compacted!).with("#{gem_binary} install #{source} -q --no-rdoc --no-ri -v \"#{target_version}\"", env: nil, timeout: 900) + expect(provider).to receive(:shell_out_compacted!).with("#{gem_binary} install #{source} -q --no-document -v \"#{target_version}\"", env: nil, timeout: 900) provider.run_action(:install) expect(new_resource).to be_updated_by_last_action end @@ -868,7 +906,7 @@ describe Chef::Provider::Package::Rubygems do it "installs the gem from file by shelling out to gem install when the package is a path and the source is nil" do expect(new_resource.source).to eq(gem_name) - expect(provider).to receive(:shell_out_compacted!).with("#{gem_binary} install #{gem_name} -q --no-rdoc --no-ri -v \"#{target_version}\"", env: nil, timeout: 900) + expect(provider).to receive(:shell_out_compacted!).with("#{gem_binary} install #{gem_name} -q --no-document -v \"#{target_version}\"", env: nil, timeout: 900) provider.run_action(:install) expect(new_resource).to be_updated_by_last_action end -- cgit v1.2.1