# # Author:: Adam Jacob () # Copyright:: Copyright (c) 2010 Opscode, 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 'chef/knife' class Chef class Knife class CookbookSiteVendor < Knife deps do require 'chef/mixin/shell_out' require 'chef/cookbook/metadata' end banner "knife cookbook site vendor COOKBOOK [VERSION] (options)" category "cookbook site" option :deps, :short => "-d", :long => "--dependencies", :boolean => true, :description => "Grab dependencies automatically" 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 :branch_default, :short => "-B BRANCH", :long => "--branch BRANCH", :description => "Default branch to work with", :default => "master" def run extend Chef::Mixin::ShellOut if config[:cookbook_path] Chef::Config[:cookbook_path] = config[:cookbook_path] else config[:cookbook_path] = Chef::Config[:cookbook_path] end # Check to ensure we have a valid source of cookbooks before continuing unless File.directory?(config[:cookbook_path].first) ui.error( File.join(config[:cookbook_path].first, " doesn't exist!. Make sure you have cookbook_path configured correctly")) exit 1 end vendor_path = File.expand_path(File.join(config[:cookbook_path].first)) cookbook_path = File.join(vendor_path, name_args[0]) upstream_file = File.join(vendor_path, "#{name_args[0]}.tar.gz") branch_name = "chef-vendor-#{name_args[0]}" download = Chef::Knife::CookbookSiteDownload.new download.config[:file] = upstream_file download.name_args = name_args download.run ui.info("Checking out the #{config[:branch_default]} branch.") shell_out!("git checkout #{config[:branch_default]}", :cwd => vendor_path) ui.info("Checking the status of the vendor branch.") status, branch_output, branch_error = Chef::Mixin::Command.output_of_command("git branch --no-color | grep #{branch_name}", :cwd => vendor_path) if branch_output =~ /#{Regexp.escape(branch_name)}$/m ui.info("Vendor branch found.") shell_out!("git checkout #{branch_name}", :cwd => vendor_path) else ui.info("Creating vendor branch.") shell_out!("git checkout -b #{branch_name}", :cwd => vendor_path) end ui.info("Removing pre-existing version.") shell_out!("rm -r #{cookbook_path}", :cwd => vendor_path) if File.directory?(cookbook_path) ui.info("Uncompressing #{name_args[0]} version #{download.version}.") shell_out!("tar zxvf #{upstream_file}", :cwd => vendor_path) shell_out!("rm #{upstream_file}", :cwd => vendor_path) ui.info("Adding changes.") shell_out!("git add #{name_args[0]}", :cwd => vendor_path) ui.info("Committing changes.") changes = true begin shell_out!("git commit -a -m 'Import #{name_args[0]} version #{download.version}'", :cwd => vendor_path) rescue Chef::Exceptions::Exec => e ui.warn("Checking out the #{config[:branch_default]} branch.") ui.warn("No changes from current vendor #{name_args[0]}") shell_out!("git checkout #{config[:branch_default]}", :cwd => vendor_path) changes = false end if changes ui.info("Creating tag chef-vendor-#{name_args[0]}-#{download.version}.") shell_out!("git tag -f chef-vendor-#{name_args[0]}-#{download.version}", :cwd => vendor_path) ui.info("Checking out the #{config[:branch_default]} branch.") shell_out!("git checkout #{config[:branch_default]}", :cwd => vendor_path) ui.info("Merging changes from #{name_args[0]} version #{download.version}.") Dir.chdir(vendor_path) do if system("git merge #{branch_name}") ui.info("Cookbook #{name_args[0]} version #{download.version} successfully vendored!") else ui.error("You have merge conflicts - please resolve manually!") ui.error("(Hint: cd #{vendor_path}; git status)") exit 1 end end end if config[:deps] md = Chef::Cookbook::Metadata.new md.from_file(File.join(cookbook_path, "metadata.rb")) md.dependencies.each do |cookbook, version_list| # Doesn't do versions.. yet nv = Chef::Knife::CookbookSiteVendor.new nv.config = config nv.name_args = [ cookbook ] nv.run end end end end end end