summaryrefslogtreecommitdiff
path: root/lib/chef/provider/package/homebrew.rb
blob: d964703c87d02300ad107503f1bd92888959ea4b (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
118
119
120
121
#
# Author:: Joshua Timberman (<joshua@getchef.com>)
# Author:: Graeme Mathieson (<mathie@woss.name>)
#
# Copyright 2011-2013, Opscode, Inc.
# Copyright 2014, Chef Software, Inc <legal@getchef.com>
#
# 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 'etc'
require 'chef/mixin/homebrew_owner'

class Chef
  class Provider
    class Package
      class Homebrew < Chef::Provider::Package
        include Chef::Mixin::HomebrewOwner
        def load_current_resource
          self.current_resource = Chef::Resource::Package.new(new_resource.name)
          current_resource.package_name(new_resource.package_name)
          current_resource.version(current_installed_version)
          Chef::Log.debug("#{new_resource} current version is #{current_resource.version}") if current_resource.version

          @candidate_version = candidate_version

          Chef::Log.debug("#{new_resource} candidate version is #{@candidate_version}") if @candidate_version

          current_resource
        end

        def install_package(name, version)
          unless current_resource.version == version
            brew('install', new_resource.options, name)
          end
        end

        def upgrade_package(name, version)
          current_version = current_resource.version

          if current_version.nil? or current_version.empty?
            install_package(name, version)
          elsif current_version != version
            brew('upgrade', new_resource.options, name)
          end
        end

        def remove_package(name, version)
          if current_resource.version
            brew('uninstall', new_resource.options, name)
          end
        end

        # Homebrew doesn't really have a notion of purging, do a "force remove"
        def purge_package(name, version)
          new_resource.options((new_resource.options || '') << ' --force').strip
          remove_package(name, version)
        end

        def brew(*args)
          get_response_from_command("brew #{args.join(' ')}")
        end

        # We implement a querying method that returns the JSON-as-Hash
        # data for a formula per the Homebrew documentation. Previous
        # implementations of this provider in the homebrew cookbook
        # performed a bit of magic with the load path to get this
        # information, but that is not any more robust than using the
        # command-line interface that returns the same thing.
        #
        # https://github.com/Homebrew/homebrew/wiki/Querying-Brew
        def brew_info
          @brew_info ||= Chef::JSONCompat.from_json(brew('info', '--json=v1', new_resource.package_name)).first
        end

        # Some packages (formula) are "keg only" and aren't linked,
        # because multiple versions installed can cause conflicts. We
        # handle this by using the last installed version as the
        # "current" (as in latest). Otherwise, we will use the version
        # that brew thinks is linked as the current version.
        #
        def current_installed_version
          brew_info['keg_only'] ? brew_info['installed'].last['version'] : brew_info['linked_keg']
        end

        # Packages (formula) available to install should have a
        # "stable" version, per the Homebrew project's acceptable
        # formula documentation, so we will rely on that being the
        # case. Older implementations of this provider in the homebrew
        # cookbook would fall back to +brew_info['version']+, but the
        # schema has changed, and homebrew is a constantly rolling
        # forward project.
        #
        # https://github.com/Homebrew/homebrew/wiki/Acceptable-Formulae#stable-versions
        def candidate_version
          brew_info['versions']['stable']
        end

        private

        def get_response_from_command(command)
          home_dir = Etc.getpwnam(homebrew_owner(node)).dir

          Chef::Log.debug "Executing '#{command}' as user '#{homebrew_owner(node)}'"
          output = shell_out!(command, :timeout => 1800, :user => homebrew_owner(node), :environment => { 'HOME' => home_dir, 'RUBYOPT' => nil })
          output.stdout.chomp
        end
      end
    end
  end
end