diff --git a/app/jobs/clone_projects_job.rb b/app/jobs/clone_projects_job.rb index 767639b..3e9ee60 100644 --- a/app/jobs/clone_projects_job.rb +++ b/app/jobs/clone_projects_job.rb @@ -22,10 +22,10 @@ class CloneProjectsJob < ApplicationJob if Dir.exist?(target_dir) && Dir.exist?(File.join(target_dir, ".git")) Rails.logger.info "Pulling latest for #{org}/#{repo}..." - system("cd '#{target_dir}' && git pull") + run_git_command([ "git", "-C", target_dir, "pull" ]) else Rails.logger.info "Cloning #{org}/#{repo}..." - system("git clone #{url} '#{target_dir}'") + run_git_command([ "git", "clone", url, target_dir ]) end end end @@ -35,4 +35,47 @@ class CloneProjectsJob < ApplicationJob Rails.logger.info "Done cloning and updating all projects!" end + + private + + def run_git_command(command) + pid = nil + begin + pid = Process.spawn(*command, + out: :close, + err: :close, + pgroup: true + ) + + # Wait for process with timeout + Timeout.timeout(300) do # 5 minute timeout + Process.wait(pid) + end + + unless $?.success? + Rails.logger.warn "Git command failed: #{command.join(' ')}" + end + rescue Timeout::Error + Rails.logger.error "Git command timed out: #{command.join(' ')}" + if pid + begin + Process.kill("TERM", -Process.getpgid(pid)) # Kill process group + sleep(5) + Process.kill("KILL", -Process.getpgid(pid)) # Force kill if still running + rescue Errno::ESRCH, Errno::EPERM + # Process already dead or no permission + end + end + rescue => e + Rails.logger.error "Git command error: #{e.message}" + ensure + if pid + begin + Process.wait(pid, Process::WNOHANG) # Clean up zombie if still there + rescue Errno::ECHILD + # Process already reaped + end + end + end + end end