diff options
author | Tim Smith <tsmith@chef.io> | 2018-03-06 22:00:30 -0800 |
---|---|---|
committer | Tim Smith <tsmith@chef.io> | 2018-03-20 09:21:54 -0700 |
commit | 88e30272dffd6740e6d0383b002b7ad518fe1d0c (patch) | |
tree | 5fc45e89ef6436ba6b384331e9659ad0048dcd65 | |
parent | 51c59ecb887e9122aa849d8153b9984df74b2b09 (diff) | |
download | chef-88e30272dffd6740e6d0383b002b7ad518fe1d0c.tar.gz |
Add dmg_package, homebrew_cask, and homebrew_tap resources
Pull in resources from the dmg and homebrew cookbooks
Signed-off-by: Tim Smith <tsmith@chef.io>
-rw-r--r-- | lib/chef/resource/dmg_package.rb | 142 | ||||
-rw-r--r-- | lib/chef/resource/homebrew_cask.rb | 76 | ||||
-rw-r--r-- | lib/chef/resource/homebrew_tap.rb | 73 | ||||
-rw-r--r-- | lib/chef/resources.rb | 3 | ||||
-rw-r--r-- | spec/unit/resource/dmg_package_spec.rb | 35 |
5 files changed, 329 insertions, 0 deletions
diff --git a/lib/chef/resource/dmg_package.rb b/lib/chef/resource/dmg_package.rb new file mode 100644 index 0000000000..e427784990 --- /dev/null +++ b/lib/chef/resource/dmg_package.rb @@ -0,0 +1,142 @@ +# +# Author:: Joshua Timberman (<jtimberman@chef.io>) +# Copyright:: 2011-2017, Chef Software, Inc. +# +# 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 "chef/resource" + +class Chef + class Resource + class DmgPackage < Chef::Resource + resource_name :dmg_package + provides :dmg_package + + description "" + introduced "14.0" + + property :app, String, + name_property: true + + property :source, String + + property :file, String + + property :owner, String + + property :destination, String, + default: "/Applications" + + property :checksum, String + + property :volumes_dir, String + + property :dmg_name, String + + property :type, String, + default: "app" + + property :installed, [true, false], + default: false, desired_state: false + + property :package_id, String + + property :dmg_passphrase, String + + property :accept_eula, [true, false], + default: false + + property :headers, [Hash, nil], + default: nil + + property :allow_untrusted, [true, false], + default: false + + load_current_value do |new_resource| + if ::File.directory?("#{new_resource.destination}/#{new_resource.app}.app") + Chef::Log.info "Already installed; to upgrade, remove \"#{new_resource.destination}/#{new_resource.app}.app\"" + installed true + elsif shell_out("pkgutil --pkgs='#{new_resource.package_id}'").exitstatus == 0 + Chef::Log.info "Already installed; to upgrade, try \"sudo pkgutil --forget '#{new_resource.package_id}'\"" + installed true + else + installed false + end + end + + action :install do + unless current_resource.installed + + volumes_dir = new_resource.volumes_dir ? new_resource.volumes_dir : new_resource.app + dmg_name = new_resource.dmg_name ? new_resource.dmg_name : new_resource.app + + if new_resource.source + declare_resource(:remote_file, "#{dmg_file} - #{new_resource.name}") do + path dmg_file + source new_resource.source + headers new_resource.headers if new_resource.headers + checksum new_resource.checksum if new_resource.checksum + end + end + + passphrase_cmd = new_resource.dmg_passphrase ? "-passphrase #{new_resource.dmg_passphrase}" : "" + ruby_block "attach #{dmg_file}" do + block do + cmd = shell_out("hdiutil imageinfo #{passphrase_cmd} '#{dmg_file}' | grep -q 'Software License Agreement: true'") + software_license_agreement = cmd.exitstatus == 0 + raise "Requires EULA Acceptance; add 'accept_eula true' to package resource" if software_license_agreement && !new_resource.accept_eula + accept_eula_cmd = new_resource.accept_eula ? "echo Y | PAGER=true" : "" + shell_out!("#{accept_eula_cmd} hdiutil attach #{passphrase_cmd} '#{dmg_file}' -mountpoint '/Volumes/#{volumes_dir}' -quiet") + end + not_if "hdiutil info #{passphrase_cmd} | grep -q 'image-path.*#{dmg_file}'" + end + + case new_resource.type + when "app" + declare_resource(:execute, "rsync --force --recursive --links --perms --executability --owner --group --times '/Volumes/#{volumes_dir}/#{new_resource.app}.app' '#{new_resource.destination}'") do + user new_resource.owner if new_resource.owner + end + + declare_resource(:file, "#{new_resource.destination}/#{new_resource.app}.app/Contents/MacOS/#{new_resource.app}") do + mode "755" + ignore_failure true + end + when "mpkg", "pkg" + install_cmd = "installation_file=$(ls '/Volumes/#{volumes_dir}' | grep '.#{new_resource.type}$') && sudo installer -pkg \"/Volumes/#{volumes_dir}/$installation_file\" -target /" + install_cmd += " -allowUntrusted" if new_resource.allow_untrusted + + declare_resource(:execute, install_cmd) do + # Prevent cfprefsd from holding up hdiutil detach for certain disk images + environment("__CFPREFERENCES_AVOID_DAEMON" => "1") + end + end + + declare_resource(:execute, "hdiutil detach '/Volumes/#{volumes_dir}' || hdiutil detach '/Volumes/#{volumes_dir}' -force") + end + end + + action_class do + def dmg_file + @dmg_file ||= begin + if new_resource.file.nil? + "#{Chef::Config[:file_cache_path]}/#{dmg_name}.dmg" + else + new_resource.file + end + end + end + end + end + end +end diff --git a/lib/chef/resource/homebrew_cask.rb b/lib/chef/resource/homebrew_cask.rb new file mode 100644 index 0000000000..9a053c0a68 --- /dev/null +++ b/lib/chef/resource/homebrew_cask.rb @@ -0,0 +1,76 @@ +# +# Author:: Joshua Timberman (<jtimberman@chef.io>) +# Author:: Graeme Mathieson (<mathie@woss.name>) +# +# Copyright:: 2011-2018, Chef Software, Inc. +# +# 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 "chef/resource" +require "chef/mixin/homebrew_user" + +class Chef + class Resource + class HomebrewCask < Chef::Resource + resource_name :homebrew_cask + provides :homebrew_cask + + description "" + introduced "14.0" + + include Chef::Mixin::HomebrewUser + + property :name, String, + regex: %r{^[\w/-]+$}, + name_property: true + + property :options, String + + property :install_cask, [true, false], + default: true + + action :install do + homebrew_tap "caskroom/cask" if new_resource.install_cask + + declare_resource(:execute, "installing cask #{new_resource.name}") do + command "/usr/local/bin/brew cask install #{new_resource.name} #{new_resource.options}" + user find_homebrew_uid + environment lazy { { "HOME" => ::Dir.home(find_homebrew_uid), "USER" => find_homebrew_uid } } + not_if { casked? } + end + end + + action :uninstall do + homebrew_tap "caskroom/cask" if new_resource.install_cask + + declare_resource(:execute, "uninstalling cask #{new_resource.name}") do + command "/usr/local/bin/brew cask uninstall #{new_resource.name}" + user find_homebrew_uid + environment lazy { { "HOME" => ::Dir.home(find_homebrew_uid), "USER" => find_homebrew_uid } } + only_if { casked? } + end + end + + action_class do + alias_method :action_cask, :action_install + alias_method :action_uncask, :action_uninstall + + def casked? + unscoped_name = new_resource.name.split("/").last + shell_out("/usr/local/bin/brew cask list 2>/dev/null", user: find_homebrew_uid).stdout.split.include?(unscoped_name) + end + end + end + end +end diff --git a/lib/chef/resource/homebrew_tap.rb b/lib/chef/resource/homebrew_tap.rb new file mode 100644 index 0000000000..ad58f73dd3 --- /dev/null +++ b/lib/chef/resource/homebrew_tap.rb @@ -0,0 +1,73 @@ +# +# Author:: Joshua Timberman (<jtimberman@chef.io>) +# Author:: Graeme Mathieson (<mathie@woss.name>) +# +# Copyright:: 2011-2018, Chef Software, Inc. +# +# 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 "chef/resource" +require "chef/mixin/homebrew_user" + +class Chef + class Resource + class HomebrewTap < Chef::Resource + resource_name :homebrew_tap + provides :homebrew_tap + + description "" + introduced "14.0" + + include Chef::Mixin::HomebrewUser + + property :name, String, + regex: %r{^[\w-]+(?:\/[\w-]+)+$}, + name_property: true + + property :url, String + + property :full, [TrueClass, FalseClass], + default: false + + action :tap do + unless tapped?(new_resource.name) + declare_resource(:execute, "tapping #{new_resource.name}") do + command "/usr/local/bin/brew tap #{new_resource.full ? '--full' : ''} #{new_resource.name} #{new_resource.url || ''}" + environment lazy { { "HOME" => ::Dir.home(find_homebrew_uid), "USER" => find_homebrew_uid } } + not_if "/usr/local/bin/brew tap | grep #{new_resource.name}" + user find_homebrew_uid + end + end + end + + action :untap do + if tapped?(new_resource.name) + declare_resource(:execute, "untapping #{new_resource.name}") do + command "/usr/local/bin/brew untap #{new_resource.name}" + environment lazy { { "HOME" => ::Dir.home(find_homebrew_uid), "USER" => find_homebrew_uid } } + only_if "/usr/local/bin/brew tap | grep #{new_resource.name}" + user find_homebrew_uid + end + end + end + + action_class do + def tapped?(name) + tap_dir = name.gsub("/", "/homebrew-") + ::File.directory?("/usr/local/Homebrew/Library/Taps/#{tap_dir}") + end + end + end + end +end diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb index dc235deb06..a5d5423bfe 100644 --- a/lib/chef/resources.rb +++ b/lib/chef/resources.rb @@ -31,6 +31,7 @@ require "chef/resource/chocolatey_package" require "chef/resource/cron" require "chef/resource/csh" require "chef/resource/directory" +require "chef/resource/dmg_package" require "chef/resource/dpkg_package" require "chef/resource/dnf_package" require "chef/resource/dsc_script" @@ -44,7 +45,9 @@ require "chef/resource/git" require "chef/resource/group" require "chef/resource/http_request" require "chef/resource/hostname" +require "chef/resource/homebrew_cask" require "chef/resource/homebrew_package" +require "chef/resource/homebrew_tap" require "chef/resource/ifconfig" require "chef/resource/ksh" require "chef/resource/launchd" diff --git a/spec/unit/resource/dmg_package_spec.rb b/spec/unit/resource/dmg_package_spec.rb new file mode 100644 index 0000000000..d1d8a71188 --- /dev/null +++ b/spec/unit/resource/dmg_package_spec.rb @@ -0,0 +1,35 @@ +# +# Copyright:: Copyright 2018, 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 "spec_helper" + +describe Chef::Resource::DmgPackage do + + let(:resource) { Chef::Resource::DmgPackage.new("myapp") } + + it "has a resource name of :dmg_package" do + expect(resource.resource_name).to eql(:dmg_package) + end + + it "has a default action of install" do + expect(resource.action).to eql([:install]) + end + + it "the app property is the name property" do + expect(resource.app).to eql("myapp") + end +end |