class ChefCLI::Policyfile::GitLockFetcher
@since 3.0
@author Daniel DeLeo
@author Ryan Hass
A Policyfile lock fetcher that can read a lock file from a git repository.
def apply_locked_source_options(options_from_lock)
-
options_from_lock
(Hash
) -- The source options loaded from a policyfile lock
def apply_locked_source_options(options_from_lock) options = options_from_lock.inject({}) do |acc, (key, value)| acc[key.to_sym] = value acc end source_options.merge!(options) raise ChefCLI::InvalidLockfile, "Invalid source_options provided from lock data: #{options_from_lock_file.inspect}" unless valid? end
def cache_path
-
(Pathname)
-
def cache_path Pathname.new(File.expand_path(File.join(ChefCLI::Helpers.package_home, "cache"))) .join(".cache", "git", Digest::SHA1.hexdigest(uri)) end
def cached?
-
(Boolean)
-
def cached? cache_path.exist? end
def errors
-
(Array
- A list of errors found)
def errors error_messages = [] [:git].each do |key| error_messages << "include_policy for #{name} is missing key #{key}" unless source_options[key] end error_messages end
def fetch_lock_data
def fetch_lock_data install unless installed? FFI_Yajl::Parser.parse( show_file(rev_parse, lockfile_path) ) end
def git(command, options = {})
-
(String)
-
Parameters:
-
error
(Boolean
) -- -
command
(String
) --
def git(command, options = {}) error = options[:error] || true unless which("git") || which("git.exe") || which("git.bat") raise GitNotInstalled end response = Mixlib::ShellOut.new(%{git #{command}}, options) response.run_command if error && response.error? raise GitError.new "#{command} #{cache_path}: #{response.stderr}" end response.stdout.strip end
def initialize(name, source_options, storage_config)
-
source_options
(Hash
) -- A hash with a :path key pointing at the location -
name
(String
) -- The name of the policyfile
def initialize(name, source_options, storage_config) @name = name @storage_config = storage_config @source_options = symbolize_keys(source_options) @revision = @source_options[:revision] @path = @source_options[:path] || @source_options[:rel] @uri = @source_options[:git] @branch = @source_options[:branch] @tag = @source_options[:tag] @ref = @source_options[:ref] # The revision to parse @rev_parse = @source_options[:ref] || @source_options[:branch] || @source_options[:tag] || "master" end
def install
Install into the chefcli cookbook store. This method leverages a cached
then munged since we do not have Policyfile validation in scope.
COPYPASTA from CookbookOmnifetch::GitLocation and Berkshelf::GitLocation
def install if cached? Dir.chdir(cache_path) do git %{fetch --force --tags #{uri} "refs/heads/*:refs/heads/*"} end else git %{clone #{uri} "#{cache_path}" --bare --no-hardlinks} end Dir.chdir(cache_path) do @revision ||= git %{rev-parse #{rev_parse}} end end
def installed?
def installed? !!(revision && cache_path.exist?) end
def lock_data
-
(Hash)
- of the policyfile lock data
def lock_data @lock_data ||= fetch_lock_data.tap do |data| data["cookbook_locks"].each do |cookbook_name, cookbook_lock| if cookbook_lock["source_options"].key?("path") cookbook_lock["source_options"].tap do |opt| opt["git"] = uri unless opt.key?("git") opt["revision"] = revision unless opt.key?("revision") opt["branch"] = branch unless opt.key?("branch") || branch.nil? opt["tag"] = tag unless opt.key?("tag") || branch.nil? opt["ref"] = ref unless opt.key?("ref") || ref.nil? path_keys = %w{path rel}.map { |path_key| path_key if opt.key?(path_key) }.compact path_keys.each do |name| # We can safely grab the entire cookbook when the Policyfile defines a cookbook path of itself (".") if opt[name] == "." opt.delete(name) next end # Mutate the path key to a rel key so that we identify the source_type # as a git repo and not a local directory. Git also doesn't like paths # prefixed with `./` and cannot use relative paths outside the repo. # http://rubular.com/r/JYpdYHT19p pattern = %r{(^../)|(^./)} opt["rel"] = opt[name].gsub(pattern, "") end # Delete the path key if present to ensure we use the git source_type opt.delete("path") end end # cookbook_lock["source_options"] end # data["cookbook_locks"].each end # fetch_lock_data.tap @lock_data end
def lockfile_path
def lockfile_path @path.nil? ? "Policyfile.lock.json" : @path end
def rev_parse
def rev_parse source_options[:revision] || @rev_parse end
def show_file(version, file)
-
(String)
- Content of specified file for a given revision.
Parameters:
-
file
() -- Full path to file including filename in repository
-
version
() -- Git version as a tag, branch, or ref.
def show_file(version, file) git("show #{version}:#{file}", cwd: cache_path.to_s) end
def source_options_for_lock
-
(Hash)
- The source_options that describe how to fetch this exact lock again
def source_options_for_lock source_options.merge({ revision:, }) end
def symbolize_keys(hash)
-
(Hash)
- Hash with only symbols as keys.
Parameters:
-
hash
(Hash
) -- Hash with symbols and/or strings as keys.
def symbolize_keys(hash) hash.inject({}) { |memo, (k, v)| memo[k.to_sym] = v; memo } end
def valid?
-
(False)
- if there were errors with the provided source_options -
(True)
- if there were no errors with the provided source_options
def valid? errors.empty? end
def which(executable)
-
(String, nil)
-
def which(executable) if File.file?(executable) && File.executable?(executable) executable elsif ENV["PATH"] path = ENV["PATH"].split(File::PATH_SEPARATOR).find do |p| File.executable?(File.join(p, executable)) end path && File.expand_path(executable, path) end end