lib/berkshelf/locations/git.rb
module Berkshelf
class GitLocation < BaseLocation
include Mixin::Git
attr_reader :uri
attr_reader :branch
attr_reader :tag
attr_reader :ref
attr_reader :revision
attr_reader :rel
def initialize(dependency, options = {})
super
@uri = options[:git]
@branch = options[:branch]
@tag = options[:tag]
@ref = options[:ref]
@revision = options[:revision]
@rel = options[:rel]
# The revision to parse
@rev_parse = options[:ref] || options[:branch] || options[:tag] || "master"
end
# @see BaseLoation#installed?
def installed?
!!(revision && install_path.exist?)
end
# Install this git cookbook into the cookbook store. This method leverages
# a cached git copy and a scratch directory to prevent bad cookbooks from
# making their way into the cookbook store.
#
# @see BaseLocation#install
def install
scratch_path = Pathname.new(Dir.mktmpdir)
if cached?
git(%{fetch --force --tags #{uri} "refs/heads/*:refs/heads/*"}, cwd: cache_path.to_s)
else
git %{clone #{uri} "#{cache_path}" --bare --no-hardlinks}
end
@revision ||= git(%{rev-parse #{@rev_parse}}, cwd: cache_path.to_s)
# Clone into a scratch directory for validations
git %{clone --no-checkout "#{cache_path}" "#{scratch_path}"}
# Make sure the scratch directory is up-to-date and account for rel paths
git(%{fetch --force --tags "#{cache_path}"}, cwd: scratch_path.to_s)
git(%{reset --hard #{@revision}}, cwd: scratch_path.to_s)
if rel
git(%{filter-branch --subdirectory-filter "#{rel}" --force}, cwd: scratch_path.to_s)
end
# Validate the scratched path is a valid cookbook
validate_cached!(scratch_path)
# If we got this far, we should atomically move
FileUtils.rm_rf(install_path) if install_path.exist?
FileUtils.mv(scratch_path, install_path)
# Remove the git history
FileUtils.rm_rf(File.join(install_path, ".git"))
install_path.chmod(0777 & ~File.umask)
ensure
# Ensure the scratch directory is cleaned up
FileUtils.rm_rf(scratch_path)
end
# @see BaseLocation#cached_cookbook
def cached_cookbook
if installed?
@cached_cookbook ||= CachedCookbook.from_path(install_path)
else
nil
end
end
def ==(other)
other.is_a?(GitLocation) &&
other.uri == uri &&
other.branch == branch &&
other.tag == tag &&
other.shortref == shortref &&
other.rel == rel
end
def to_s
info = tag || branch || shortref || @rev_parse
if rel
"#{uri} (at #{info}/#{rel})"
else
"#{uri} (at #{info})"
end
end
def to_lock
out = " git: #{uri}\n"
out << " revision: #{revision}\n"
out << " ref: #{shortref}\n" if shortref
out << " branch: #{branch}\n" if branch
out << " tag: #{tag}\n" if tag
out << " rel: #{rel}\n" if rel
out
end
protected
# The short ref (if one was given).
#
# @return [String, nil]
def shortref
ref && ref[0...7]
end
private
# Determine if this git repo has already been downloaded.
#
# @return [Boolean]
def cached?
cache_path.exist?
end
# The path where this cookbook would live in the store, if it were
# installed.
#
# @return [Pathname, nil]
def install_path
Berkshelf.cookbook_store.storage_path
.join("#{dependency.name}-#{revision}")
end
# The path where this git repository is cached.
#
# @return [Pathname]
def cache_path
Pathname.new(Berkshelf.berkshelf_path)
.join(".cache", "git", Digest::SHA1.hexdigest(uri))
end
end
end