summaryrefslogtreecommitdiff
path: root/lib/chef/cookbook/gem_installer.rb
blob: 8817bbf60feae3719dfa07aa05b6be313848c755 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#--
# Copyright:: Copyright (c) 2010-2016 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require "bundler/inline"

class Chef
  class Cookbook
    class GemInstaller

      # @return [Chef::EventDispatch::Dispatcher] the client event dispatcher
      attr_accessor :events
      # @return [Chef::CookbookCollection] the cookbook collection
      attr_accessor :cookbook_collection

      def initialize(cookbook_collection, events)
        @cookbook_collection = cookbook_collection
        @events = events
      end

      # Installs the gems into the omnibus gemset.
      #
      def install
        cookbook_gems = []

        cookbook_collection.each do |cookbook_name, cookbook_version|
          cookbook_gems += cookbook_version.metadata.gems
        end

        events.cookbook_gem_start(cookbook_gems)

        unless cookbook_gems.empty?
          begin
            inline_gemfile do
              source Chef::Config[:rubygems_url]
              cookbook_gems.each do |args|
                gem(*args)
              end
            end
          rescue Exception => e
            events.cookbook_gem_failed(e)
            raise
          end
        end

        events.cookbook_gem_finished
      end

      # Bundler::UI object so that we can intercept and log the output
      # of the in-memory bundle install that we are going to do.
      #
      class ChefBundlerUI < Bundler::UI::Silent
        attr_accessor :events

        def initialize(events)
          @events = events
          super()
        end

        def confirm(msg, newline = nil)
          # looks like "Installing time_ago_in_words 0.1.1" when installing
          if msg =~ /Installing\s+(\S+)\s+(\S+)/
            events.cookbook_gem_installing($1, $2)
          end
          Chef::Log.info(msg)
        end

        def error(msg, newline = nil)
          Chef::Log.error(msg)
        end

        def debug(msg, newline = nil)
          Chef::Log.debug(msg)
        end

        def info(msg, newline = nil)
          # looks like "Using time_ago_in_words 0.1.1" when using, plus other misc output
          if msg =~ /Using\s+(\S+)\s+(\S+)/
            events.cookbook_gem_using($1, $2)
          end
          Chef::Log.info(msg)
        end

        def warn(msg, newline = nil)
          Chef::Log.warn(msg)
        end
      end

      private

      # Helper to handle older bundler versions that do not support injecting the UI
      # object.  On older bundler versions, we work, but you get no output other than
      # on STDOUT.
      #
      def inline_gemfile(&block)
        # requires https://github.com/bundler/bundler/pull/4245
        gemfile(true, ui: ChefBundlerUI.new(events), &block)
      rescue ArgumentError # Method#arity doesn't inspect optional arguments, so we rescue
        # requires bundler 1.10.0
        gemfile(true, &block)
      end
    end
  end
end