diff --git a/lib/puppet/provider/package/gem.rb b/lib/puppet/provider/package/gem.rb index 331615f22..4888d50bc 100755 --- a/lib/puppet/provider/package/gem.rb +++ b/lib/puppet/provider/package/gem.rb @@ -1,121 +1,123 @@ require 'puppet/provider/package' require 'uri' # Ruby gems support. Puppet::Type.type(:package).provide :gem, :parent => Puppet::Provider::Package do desc "Ruby Gem support. If a URL is passed via `source`, then that URL is used as the remote gem repository; if a source is present but is not a valid URL, it will be interpreted as the path to a local gem file. If source is not present at all, the gem will be installed from the default gem repositories." has_feature :versionable commands :gemcmd => "gem" def self.gemlist(options) gem_list_command = [command(:gemcmd), "list"] if options[:local] gem_list_command << "--local" else gem_list_command << "--remote" end if name = options[:justme] gem_list_command << name + "$" end begin - list = execute(gem_list_command).lines.map {|set| gemsplit(set) } + list = execute(gem_list_command).lines. + map {|set| gemsplit(set) }. + reject {|x| x.nil? } rescue Puppet::ExecutionFailure => detail raise Puppet::Error, "Could not list gems: #{detail}" end if options[:justme] return list.shift else return list end end def self.gemsplit(desc) # `gem list` when output console has a line like: # *** LOCAL GEMS *** # but when it's not to the console that line # and all blank lines are stripped # so we don't need to check for them if desc =~ /^(\S+)\s+\((.+)\)/ name = $1 versions = $2.split(/,\s*/) { :name => name, :ensure => versions, :provider => :gem } else Puppet.warning "Could not match #{desc}" nil end end def self.instances(justme = false) gemlist(:local => true).collect do |hash| new(hash) end end def install(useversion = true) command = [command(:gemcmd), "install"] command << "-v" << resource[:ensure] if (! resource[:ensure].is_a? Symbol) and useversion # Always include dependencies command << "--include-dependencies" if source = resource[:source] begin uri = URI.parse(source) rescue => detail fail "Invalid source '#{uri}': #{detail}" end case uri.scheme when nil # no URI scheme => interpret the source as a local file command << source when /file/i command << uri.path when 'puppet' # we don't support puppet:// URLs (yet) raise Puppet::Error.new("puppet:// URLs are not supported as gem sources") else # interpret it as a gem repository command << "--source" << "#{source}" << resource[:name] end else command << "--no-rdoc" << "--no-ri" << resource[:name] end output = execute(command) # Apparently some stupid gem versions don't exit non-0 on failure self.fail "Could not install: #{output.chomp}" if output.include?("ERROR") end def latest # This always gets the latest version available. hash = self.class.gemlist(:justme => resource[:name]) hash[:ensure][0] end def query self.class.gemlist(:justme => resource[:name], :local => true) end def uninstall gemcmd "uninstall", "-x", "-a", resource[:name] end def update self.install(false) end end diff --git a/spec/fixtures/unit/provider/package/gem/line-with-1.8.5-warning b/spec/fixtures/unit/provider/package/gem/line-with-1.8.5-warning new file mode 100644 index 000000000..07a27e7b8 --- /dev/null +++ b/spec/fixtures/unit/provider/package/gem/line-with-1.8.5-warning @@ -0,0 +1,14 @@ +/home/jenkins/.rvm/gems/ruby-1.8.5-p231@global/gems/rubygems-bundler-0.9.0/lib/rubygems-bundler/regenerate_binstubs_command.rb:34: warning: parenthesize argument(s) for future version + +*** LOCAL GEMS *** + +columnize (0.3.2) +diff-lcs (1.1.3) +metaclass (0.0.1) +mocha (0.10.5) +rake (0.8.7) +rspec-core (2.9.0) +rspec-expectations (2.9.1) +rspec-mocks (2.9.0) +rubygems-bundler (0.9.0) +rvm (1.11.3.3) diff --git a/spec/unit/provider/package/gem_spec.rb b/spec/unit/provider/package/gem_spec.rb index 516e57926..8e519d6f4 100755 --- a/spec/unit/provider/package/gem_spec.rb +++ b/spec/unit/provider/package/gem_spec.rb @@ -1,127 +1,143 @@ #!/usr/bin/env rspec require 'spec_helper' provider_class = Puppet::Type.type(:package).provider(:gem) describe provider_class do let(:resource) do Puppet::Type.type(:package).new( :name => 'myresource', :ensure => :installed ) end let(:provider) do provider = provider_class.new provider.resource = resource provider end describe "when installing" do it "should use the path to the gem" do provider_class.stubs(:command).with(:gemcmd).returns "/my/gem" provider.expects(:execute).with { |args| args[0] == "/my/gem" }.returns "" provider.install end it "should specify that the gem is being installed" do provider.expects(:execute).with { |args| args[1] == "install" }.returns "" provider.install end it "should specify that dependencies should be included" do provider.expects(:execute).with { |args| args[2] == "--include-dependencies" }.returns "" provider.install end it "should specify that documentation should not be included" do provider.expects(:execute).with { |args| args[3] == "--no-rdoc" }.returns "" provider.install end it "should specify that RI should not be included" do provider.expects(:execute).with { |args| args[4] == "--no-ri" }.returns "" provider.install end it "should specify the package name" do provider.expects(:execute).with { |args| args[5] == "myresource" }.returns "" provider.install end describe "when a source is specified" do describe "as a normal file" do it "should use the file name instead of the gem name" do resource[:source] = "/my/file" provider.expects(:execute).with { |args| args[3] == "/my/file" }.returns "" provider.install end end describe "as a file url" do it "should use the file name instead of the gem name" do resource[:source] = "file:///my/file" provider.expects(:execute).with { |args| args[3] == "/my/file" }.returns "" provider.install end end describe "as a puppet url" do it "should fail" do resource[:source] = "puppet://my/file" lambda { provider.install }.should raise_error(Puppet::Error) end end describe "as a non-file and non-puppet url" do it "should treat the source as a gem repository" do resource[:source] = "http://host/my/file" provider.expects(:execute).with { |args| args[3..5] == ["--source", "http://host/my/file", "myresource"] }.returns "" provider.install end end describe "with an invalid uri" do it "should fail" do URI.expects(:parse).raises(ArgumentError) resource[:source] = "http:::::uppet:/:/my/file" lambda { provider.install }.should raise_error(Puppet::Error) end end end end describe "#latest" do it "should return a single value for 'latest'" do #gemlist is used for retrieving both local and remote version numbers, and there are cases # (particularly local) where it makes sense for it to return an array. That doesn't make # sense for '#latest', though. provider.class.expects(:gemlist).with({ :justme => 'myresource'}).returns({ :name => 'myresource', :ensure => ["3.0"], :provider => :gem, }) provider.latest.should == "3.0" end end - describe "#instances" do before do provider_class.stubs(:command).with(:gemcmd).returns "/my/gem" end it "should return an empty array when no gems installed" do provider_class.expects(:execute).with(%w{/my/gem list --local}).returns("\n") provider_class.instances.should == [] end it "should return ensure values as an array of installed versions" do provider_class.expects(:execute).with(%w{/my/gem list --local}).returns <<-HEREDOC.gsub(/ /, '') systemu (1.2.0) vagrant (0.8.7, 0.6.9) HEREDOC provider_class.instances.map {|p| p.properties}.should == [ {:ensure => ["1.2.0"], :provider => :gem, :name => 'systemu'}, {:ensure => ["0.8.7", "0.6.9"], :provider => :gem, :name => 'vagrant'} ] end + + it "should not fail when an unmatched line is returned" do + provider_class.expects(:execute).with(%w{/my/gem list --local}). + returns(File.read(my_fixture('line-with-1.8.5-warning'))) + + provider_class.instances.map {|p| p.properties}. + should == [{:provider=>:gem, :ensure=>["0.3.2"], :name=>"columnize"}, + {:provider=>:gem, :ensure=>["1.1.3"], :name=>"diff-lcs"}, + {:provider=>:gem, :ensure=>["0.0.1"], :name=>"metaclass"}, + {:provider=>:gem, :ensure=>["0.10.5"], :name=>"mocha"}, + {:provider=>:gem, :ensure=>["0.8.7"], :name=>"rake"}, + {:provider=>:gem, :ensure=>["2.9.0"], :name=>"rspec-core"}, + {:provider=>:gem, :ensure=>["2.9.1"], :name=>"rspec-expectations"}, + {:provider=>:gem, :ensure=>["2.9.0"], :name=>"rspec-mocks"}, + {:provider=>:gem, :ensure=>["0.9.0"], :name=>"rubygems-bundler"}, + {:provider=>:gem, :ensure=>["1.11.3.3"], :name=>"rvm"}] + end end end