diff options
Diffstat (limited to 'lib/gitlab_projects.rb')
-rw-r--r-- | lib/gitlab_projects.rb | 108 |
1 files changed, 78 insertions, 30 deletions
diff --git a/lib/gitlab_projects.rb b/lib/gitlab_projects.rb index bea5686..82ae519 100644 --- a/lib/gitlab_projects.rb +++ b/lib/gitlab_projects.rb @@ -1,10 +1,12 @@ -require 'open3' require 'fileutils' +require 'timeout' require_relative 'gitlab_config' require_relative 'gitlab_logger' class GitlabProjects + GLOBAL_HOOKS_DIRECTORY = File.join(ROOT_PATH, 'hooks') + # Project name is a directory name for repository with .git at the end # It may be namespaced or not. Like repo.git or gitlab/repo.git attr_reader :project_name @@ -17,11 +19,19 @@ class GitlabProjects # Ex /home/git/repositories/test.git attr_reader :full_path + def self.create_hooks(path) + local_hooks_directory = File.join(path, 'hooks') + unless File.realpath(local_hooks_directory) == File.realpath(GLOBAL_HOOKS_DIRECTORY) + FileUtils.mv(local_hooks_directory, "#{local_hooks_directory}.old.#{Time.now.to_i}") + FileUtils.ln_s(GLOBAL_HOOKS_DIRECTORY, local_hooks_directory) + end + end + def initialize @command = ARGV.shift @project_name = ARGV.shift @repos_path = GitlabConfig.new.repos_path - @full_path = File.join(@repos_path, @project_name) + @full_path = File.join(@repos_path, @project_name) unless @project_name.nil? end def exec @@ -31,6 +41,7 @@ class GitlabProjects when 'create-tag'; create_tag when 'rm-tag'; rm_tag when 'add-project'; add_project + when 'list-projects'; puts list_projects when 'rm-project'; rm_project when 'mv-project'; mv_project when 'import-project'; import_project @@ -48,38 +59,46 @@ class GitlabProjects def create_branch branch_name = ARGV.shift ref = ARGV.shift || "HEAD" - cmd = "cd #{full_path} && git branch #{branch_name} #{ref}" - system(cmd) + cmd = %W(git --git-dir=#{full_path} branch -- #{branch_name} #{ref}) + system(*cmd) end def rm_branch branch_name = ARGV.shift - cmd = "cd #{full_path} && git branch -D #{branch_name}" - system(cmd) + cmd = %W(git --git-dir=#{full_path} branch -D #{branch_name}) + system(*cmd) end def create_tag tag_name = ARGV.shift ref = ARGV.shift || "HEAD" - cmd = "cd #{full_path} && git tag #{tag_name} #{ref}" - system(cmd) + cmd = %W(git --git-dir=#{full_path} tag) + if ARGV.size > 0 + msg = ARGV.shift + cmd += %W(-a -m #{msg}) + end + cmd += %W(-- #{tag_name} #{ref}) + system(*cmd) end def rm_tag tag_name = ARGV.shift - cmd = "cd #{full_path} && git tag -d #{tag_name}" - system(cmd) + cmd = %W(git --git-dir=#{full_path} tag -d #{tag_name}) + system(*cmd) end def add_project $logger.info "Adding project #{@project_name} at <#{full_path}>." FileUtils.mkdir_p(full_path, mode: 0770) - cmd = "cd #{full_path} && git init --bare && #{create_hooks_cmd}" - system(cmd) + cmd = %W(git --git-dir=#{full_path} init --bare) + system(*cmd) && self.class.create_hooks(full_path) end - def create_hooks_cmd - create_hooks_to(full_path) + def list_projects + $logger.info 'Listing projects' + Dir.chdir(repos_path) do + next Dir.glob('**/*.git') + end end def rm_project @@ -87,13 +106,53 @@ class GitlabProjects FileUtils.rm_rf(full_path) end + def mask_password_in_url(url) + result = URI(url) + result.password = "*****" unless result.password.nil? + result + rescue + url + end + + def remove_origin_in_repo + cmd = %W(git --git-dir=#{full_path} remote rm origin) + pid = Process.spawn(*cmd) + Process.wait(pid) + end + # Import project via git clone --bare # URL must be publicly cloneable def import_project + # Skip import if repo already exists + return false if File.exists?(full_path) + @source = ARGV.shift - $logger.info "Importing project #{@project_name} from <#{@source}> to <#{full_path}>." - cmd = "cd #{repos_path} && git clone --bare #{@source} #{project_name} && #{create_hooks_cmd}" - system(cmd) + masked_source = mask_password_in_url(@source) + + # timeout for clone + timeout = (ARGV.shift || 120).to_i + $logger.info "Importing project #{@project_name} from <#{masked_source}> to <#{full_path}>." + cmd = %W(git clone --bare -- #{@source} #{full_path}) + + pid = Process.spawn(*cmd) + + begin + Timeout.timeout(timeout) do + Process.wait(pid) + end + rescue Timeout::Error + $logger.error "Importing project #{@project_name} from <#{masked_source}> failed due to timeout." + + Process.kill('KILL', pid) + Process.wait + FileUtils.rm_rf(full_path) + false + else + self.class.create_hooks(full_path) + # The project was imported successfully. + # Remove the origin URL since it may contain password. + remove_origin_in_repo + end end # Move repository from one directory to another @@ -154,8 +213,8 @@ class GitlabProjects end $logger.info "Forking project from <#{full_path}> to <#{full_destination_path}>." - cmd = "cd #{namespaced_path} && git clone --bare #{full_path} && #{create_hooks_to(full_destination_path)}" - system(cmd) + cmd = %W(git clone --bare -- #{full_path} #{full_destination_path}) + system(*cmd) && self.class.create_hooks(full_destination_path) end def update_head @@ -166,11 +225,6 @@ class GitlabProjects return false end - unless File.exists?(File.join(full_path, 'refs/heads', new_head)) - $logger.error "update-head failed: specified branch does not exist in ref/heads." - return false - end - File.open(File.join(full_path, 'HEAD'), 'w') do |f| f.write("ref: refs/heads/#{new_head}") end @@ -178,10 +232,4 @@ class GitlabProjects $logger.info "Update head in project #{project_name} to <#{new_head}>." true end - - private - - def create_hooks_to(dest_path) - "ln -s #{File.join(ROOT_PATH, 'hooks', 'update')} #{dest_path}/hooks/update" - end end |