#
# Author:: Christopher Webber (<cwebber@chef.io>)
# Copyright:: Copyright (c) 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_relative"../knife"classChefclassKnifeclassSupermarketInstall<Knifedepsdorequire"chef/exceptions"unlessdefined?(Chef::Exceptions)require"shellwords"unlessdefined?(Shellwords)require"mixlib/archive"unlessdefined?(Mixlib::Archive)require_relative"core/cookbook_scm_repo"require"chef/cookbook/metadata"unlessdefined?(Chef::Cookbook::Metadata)endbanner"knife supermarket install COOKBOOK [VERSION] (options)"category"supermarket"option:no_deps,short: "-D",long: "--skip-dependencies",boolean: true,default: false,description: "Skips automatic dependency installation."option:cookbook_path,short: "-o PATH:PATH",long: "--cookbook-path PATH:PATH",description: "A colon-separated path to look for cookbooks in.",proc: lambda{|o|o.split(":")}option:default_branch,short: "-B BRANCH",long: "--branch BRANCH",description: "Default branch to work with.",default: "master"option:use_current_branch,short: "-b",long: "--use-current-branch",description: "Use the current branch.",boolean: true,default: falseoption:supermarket_site,short: "-m SUPERMARKET_SITE",long: "--supermarket-site SUPERMARKET_SITE",description: "The URL of the Supermarket site.",default: "https://supermarket.chef.io"attr_reader:cookbook_nameattr_reader:vendor_pathdefrunifconfig[:cookbook_path]Chef::Config[:cookbook_path]=config[:cookbook_path]elseconfig[:cookbook_path]=Chef::Config[:cookbook_path]end@cookbook_name=parse_name_args!# Check to ensure we have a valid source of cookbooks before continuing
#
@install_path=File.expand_path(Array(config[:cookbook_path]).first)ui.info"Installing #{@cookbook_name} to #{@install_path}"@repo=CookbookSCMRepo.new(@install_path,ui,config)# cookbook_path = File.join(vendor_path, name_args[0])
upstream_file=File.join(@install_path,"#{@cookbook_name}.tar.gz")@repo.sanity_checkunlessconfig[:use_current_branch]@repo.reset_to_default_state@repo.prepare_to_import(@cookbook_name)enddownloader=download_cookbook_to(upstream_file)clear_existing_files(File.join(@install_path,@cookbook_name))extract_cookbook(upstream_file,downloader.version)# TODO: it'd be better to store these outside the cookbook repo and
# keep them around, e.g., in ~/Library/Caches on macOS.
ui.info("Removing downloaded tarball")File.unlink(upstream_file)if@repo.finalize_updates_to(@cookbook_name,downloader.version)unlessconfig[:use_current_branch]@repo.reset_to_default_stateend@repo.merge_updates_from(@cookbook_name,downloader.version)elseunlessconfig[:use_current_branch]@repo.reset_to_default_stateendendunlessconfig[:no_deps]preferred_metadata.dependencies.each_keydo|cookbook|# Doesn't do versions.. yet
nv=self.class.newnv.config=confignv.name_args=[cookbook]nv.runendendenddefparse_name_args!ifname_args.empty?ui.error("Please specify a cookbook to download and install.")exit1elsifname_args.size>=2unlessname_args.last.match(/^(\d+)(\.\d+){1,2}$/)&&name_args.size==2ui.error("Installing multiple cookbooks at once is not supported.")exit1endendname_args.firstenddefdownload_cookbook_to(download_path)downloader=Chef::Knife::SupermarketDownload.newdownloader.config[:file]=download_pathdownloader.config[:supermarket_site]=config[:supermarket_site]downloader.name_args=name_argsdownloader.rundownloaderenddefextract_cookbook(upstream_file,version)ui.info("Uncompressing #{@cookbook_name} version #{version}.")Mixlib::Archive.new(convert_path(upstream_file)).extract(@install_path,perms: false)enddefclear_existing_files(cookbook_path)ui.info("Removing pre-existing version.")FileUtils.rmtree(cookbook_path)ifFile.directory?(cookbook_path)enddefconvert_path(upstream_file)# converts a Windows path (C:\foo) to a mingw path (/c/foo)
ifENV["MSYSTEM"]==("MINGW32"||"UCRT64")upstream_file.sub(/^([[:alpha:]]):/,'/\1')elseShellwords.escapeupstream_fileendend# Get the preferred metadata path on disk. Chef prefers the metadata.rb
# over the metadata.json.
#
# @raise if there is no metadata in the cookbook
#
# @return [Chef::Cookbook::Metadata]
defpreferred_metadatamd=Chef::Cookbook::Metadata.newrb=File.join(@install_path,@cookbook_name,"metadata.rb")ifFile.exist?(rb)md.from_file(rb)returnmdendjson=File.join(@install_path,@cookbook_name,"metadata.json")ifFile.exist?(json)json=IO.read(json)md.from_json(json)returnmdendraiseChef::Exceptions::MetadataNotFound.new(@install_path,@cookbook_name)endendendend