docker_manager/spec/support/git_helpers.rb

220 lines
5.9 KiB
Ruby

# frozen_string_literal: true
module GitHelpers
CLONE_FULL = :full
CLONE_SHALLOW = :shallow
CLONE_TREELESS = :treeless
class RemoteGitRepo
attr_reader :url, :root_path, :work_path, :remote_path
@@caches = {}
def initialize(initial_branch: "main", cache_key: nil, force: false, &blk)
@initial_branch = initial_branch
@local_clone_count = 0
@root_path = Dir.mktmpdir
@remote_path = File.join(@root_path, "remote.git")
@work_path = File.join(@root_path, "work")
@url = "file://#{@remote_path}"
if !force
@@caches[cache_key] ||= self.class.new(initial_branch:, cache_key:, force: true, &blk)
FileUtils.cp_r(@@caches[cache_key].root_path + "/.", @root_path)
Dir.chdir(@work_path) { git "remote set-url origin #{@url}" }
return
end
Dir.mkdir(@remote_path)
Dir.chdir(@remote_path) do
git "init --bare --initial-branch=#{initial_branch}"
git "config core.sharedrepository 1"
git "config receive.denyNonFastforwards true"
git "config receive.denyCurrentBranch ignore"
git "config uploadpack.allowFilter true"
git "config commit.gpgsign false"
end
Dir.mkdir(@work_path)
Dir.chdir(@work_path) do
git "init . --initial-branch=#{initial_branch}"
git "config commit.gpgsign false"
git "remote add origin #{@url}"
File.write("README.md", "This is a git repo for testing docker_manager.")
File.write("version.txt", "")
git "add ."
git "commit -m 'Initial commit'"
git "push --set-upstream origin #{initial_branch}"
end
yield(self) if block_given?
end
def destroy
FileUtils.rm_rf(@root_path)
end
def create_local_clone(method:)
path = File.join(@root_path, "local_#{@local_clone_count}")
Dir.mkdir(path)
Dir.chdir(path) do
case (method)
when CLONE_FULL
git "clone #{@url} ."
when CLONE_SHALLOW
git "clone --depth 1 #{@url} ."
when CLONE_TREELESS
git "clone --filter=tree:0 #{@url} ."
end
yield(method(:git)) if block_given?
end
@local_clone_count += 1
LocalGitRepo.new(path, method)
end
def in_working_directory
Dir.chdir(@work_path) { yield(method(:git)) }
end
def in_remote_repo
Dir.chdir(@remote_path) { yield(method(:git)) }
end
def commit(filename:, commits:, branch: nil)
branch ||= @initial_branch
Dir.chdir(@work_path) do
git "checkout #{branch}"
commits.each do |commit|
env = commit[:date] ? build_env(date: commit[:date]) : nil
File.write(filename, commit[:content])
git "add #{filename}"
git "commit -m 'Update #{filename} with #{commit[:content].truncate(10)}'", env: env
if commit[:tags]
commit[:tags].each do |tag|
if git("tag -l #{tag}", raise_exception: false)
git "tag -d #{tag}"
git "push --delete origin #{tag}"
end
git "tag -a #{tag} -m 'Tagging #{tag}'"
end
end
end
git "push --follow-tags"
end
end
def create_branches(*branch_names)
Dir.chdir(@work_path) do
branch_names.each do |branch|
git "checkout #{@initial_branch}"
git "checkout -b #{branch}"
git "push --set-upstream origin #{branch}"
end
end
end
def delete_branches(*branch_names)
Dir.chdir(@work_path) do
branch_names.each do |branch|
git "checkout #{@initial_branch}"
git "branch -d #{branch}"
git "push origin --delete #{branch}"
end
end
end
def rename_branch(old_name:, new_name:)
Dir.chdir(@work_path) do
git "checkout #{old_name}"
git "branch -m #{new_name}"
git "push origin -u #{new_name}"
if old_name == @initial_branch
Dir.chdir(@remote_path) { git "symbolic-ref HEAD refs/heads/#{new_name}" }
end
git "push origin --delete #{old_name}"
end
end
def rebase(source_branch:, target_branch:)
Dir.chdir(@work_path) do
git "checkout #{target_branch}"
git "rebase #{source_branch}"
git "push --force"
end
end
def delete_tags(*tag_names)
Dir.chdir(@work_path) do
tag_names.each do |tag|
git "tag -d #{tag}"
git "push --delete origin #{tag}"
end
end
end
private
def git(command, env: "", raise_exception: true)
env = build_env if command.start_with?("commit") && env.blank?
stdout, stderr, status = Open3.capture3("#{env} git #{command}".strip)
if status.success? || !raise_exception
stdout.presence
else
raise RuntimeError.new("stderr while running #{command}: #{stderr}")
end
end
def build_env(name: "Alice", email: "alice@example.com", date: "2023-03-06T20:27:03Z")
"GIT_AUTHOR_NAME='#{name}' GIT_AUTHOR_EMAIL='#{email}' GIT_AUTHOR_DATE='#{date}' " \
"GIT_COMMITTER_NAME='#{name}' GIT_COMMITTER_EMAIL='#{email}' GIT_COMMITTER_DATE='#{date}'"
end
end
class LocalGitRepo
attr_reader :path
def initialize(path, clone_method)
@path = path
@clone_method = clone_method
end
def git(*commands)
Dir.chdir(@path) do
commands.map do |command|
stdout, _, status = Open3.capture3("git #{command}".strip)
status.success? ? stdout : (raise RuntimeError)
end
end
end
def commit_count
git("rev-list --all --count").first.to_i
end
def checkout(ref)
if @clone_method == CLONE_SHALLOW
git "remote set-branches origin #{ref}"
git "fetch --depth 1 origin #{ref}"
else
git "fetch origin #{ref}"
end
git "-c advice.detachedHead=false checkout #{ref}"
end
end
end