class Toys::Utils::GitCache
This object provides cached access to remote git data. Given a remote repository, a path, and a commit, it makes the files availble in the local filesystem. Access is cached, so repeated requests do not hit the remote repository again.
This class is used by the Loader
to load tools from git. Tools can also use the `:git_cache` mixin for direct access to this class.
Attributes
The cache directory.
@return [String]
Public Class Methods
Access a git cache.
@param cache_dir
[String] The path to the cache directory. Defaults to
a specific directory in the user's XDG cache.
# File lib/toys/utils/git_cache.rb, line 51 def initialize(cache_dir: nil) @cache_dir = ::File.expand_path(cache_dir || default_cache_dir) @exec = Utils::Exec.new(out: :capture, err: :capture) end
Public Instance Methods
Find the given git-based files from the git cache, loading from the remote repo if necessary.
@param remote [String] The URL of the git repo. Required. @param path [String] The path to the file or directory within the repo.
Optional. Defaults to the entire repo.
@param commit [String] The commit reference, which may be a SHA or any
git ref such as a branch or tag. Optional. Defaults to `HEAD`.
@param update [Boolean] Force update of non-SHA commit references, even
if it has previously been loaded.
@return [String] The full path to the cached files.
# File lib/toys/utils/git_cache.rb, line 70 def find(remote, path: nil, commit: nil, update: false) path ||= "" commit ||= "HEAD" dir = ensure_dir(remote) lock_repo(dir) do ensure_repo(dir, remote) sha = ensure_commit(dir, commit, update) ensure_source(dir, sha, path.to_s) end end
@private Used for testing
# File lib/toys/utils/git_cache.rb, line 89 def repo_dir_for(remote) ::File.join(@cache_dir, remote_dir_name(remote), "repo") end
Private Instance Methods
# File lib/toys/utils/git_cache.rb, line 167 def commit_exists?(repo_dir, commit) result = git(repo_dir, ["cat-file", "-t", commit]) result.success? && result.captured_out.strip == "commit" end
# File lib/toys/utils/git_cache.rb, line 108 def default_cache_dir ::File.join(XDG.new.cache_home, "toys", "git") end
# File lib/toys/utils/git_cache.rb, line 154 def ensure_commit(dir, commit, update = false) local_commit = "toys-git-cache/#{commit}" repo_dir = ::File.join(dir, repo_dir_name) is_sha = commit =~ /^[0-9a-f]{40}$/ if update && !is_sha || !commit_exists?(repo_dir, local_commit) git(repo_dir, ["fetch", "--depth=1", "--force", "origin", "#{commit}:#{local_commit}"], error_message: "Unable to to fetch commit: #{commit}") end result = git(repo_dir, ["rev-parse", local_commit], error_message: "Unable to retrieve commit: #{local_commit}") result.captured_out.strip end
# File lib/toys/utils/git_cache.rb, line 126 def ensure_dir(remote) dir = ::File.join(@cache_dir, remote_dir_name(remote)) ::FileUtils.mkdir_p(dir) dir end
# File lib/toys/utils/git_cache.rb, line 140 def ensure_repo(dir, remote) repo_dir = ::File.join(dir, repo_dir_name) ::FileUtils.mkdir_p(repo_dir) result = git(repo_dir, ["remote", "get-url", "origin"]) unless result.success? && result.captured_out.strip == remote ::FileUtils.rm_rf(repo_dir) ::FileUtils.mkdir_p(repo_dir) git(repo_dir, ["init"], error_message: "Unable to initialize git repository") git(repo_dir, ["remote", "add", "origin", remote], error_message: "Unable to add git remote") end end
# File lib/toys/utils/git_cache.rb, line 172 def ensure_source(dir, sha, path) source_path = ::File.join(dir, source_name(sha, path)) unless ::File.exist?(source_path) repo_dir = ::File.join(dir, repo_dir_name) git(repo_dir, ["checkout", sha]) from_path = ::File.join(repo_dir, path) ::FileUtils.cp_r(from_path, source_path) end source_path end
# File lib/toys/utils/git_cache.rb, line 112 def git(dir, cmd, error_message: nil) result = @exec.exec(["git"] + cmd, chdir: dir) if result.failed? raise GitCache::Error.new("Could not run git command line", result) end if block_given? yield result elsif result.error? && error_message raise GitCache::Error.new(error_message, result) else result end end
# File lib/toys/utils/git_cache.rb, line 132 def lock_repo(dir) lock_path = ::File.join(dir, "repo.lock") ::File.open(lock_path, ::File::RDWR | ::File::CREAT) do |file| file.flock(::File::LOCK_EX) yield end end
# File lib/toys/utils/git_cache.rb, line 95 def remote_dir_name(remote) ::Digest::MD5.hexdigest(remote) end
# File lib/toys/utils/git_cache.rb, line 104 def repo_dir_name "repo" end
# File lib/toys/utils/git_cache.rb, line 99 def source_name(sha, path) digest = ::Digest::MD5.hexdigest("#{sha}#{path}") "#{digest}#{::File.extname(path)}" end