Merge branch 'dm-cleanup-fetch-and-mirror-methods' into 'master'
Clean up repository fetch and mirror methods See merge request gitlab-org/gitlab-ce!15424
This commit is contained in:
		
						commit
						dd11f0e053
					
				| 
						 | 
				
			
			@ -966,6 +966,19 @@ class Repository
 | 
			
		|||
    run_git(args).first.lines.map(&:strip)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def fetch_as_mirror(url, forced: false, refmap: :all_refs, remote_name: nil)
 | 
			
		||||
    unless remote_name
 | 
			
		||||
      remote_name = "tmp-#{SecureRandom.hex}"
 | 
			
		||||
      tmp_remote_name = true
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    add_remote(remote_name, url)
 | 
			
		||||
    set_remote_as_mirror(remote_name, refmap: refmap)
 | 
			
		||||
    fetch_remote(remote_name, forced: forced)
 | 
			
		||||
  ensure
 | 
			
		||||
    remove_remote(remote_name) if tmp_remote_name
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def fetch_remote(remote, forced: false, ssh_auth: nil, no_tags: false)
 | 
			
		||||
    gitlab_shell.fetch_remote(raw_repository, remote, ssh_auth: ssh_auth, forced: forced, no_tags: no_tags)
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			@ -1063,6 +1076,10 @@ class Repository
 | 
			
		|||
    raw_repository.fetch_ref(source_repository.raw_repository, source_ref: source_ref, target_ref: target_ref)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def repository_storage_path
 | 
			
		||||
    @project.repository_storage_path
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  # TODO Generice finder, later split this on finders by Ref or Oid
 | 
			
		||||
| 
						 | 
				
			
			@ -1128,10 +1145,6 @@ class Repository
 | 
			
		|||
    raw_repository.run_git_with_timeout(args, Gitlab::Git::Popen::FAST_GIT_PROCESS_TIMEOUT).first.strip
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def repository_storage_path
 | 
			
		||||
    @project.repository_storage_path
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def initialize_raw_repository
 | 
			
		||||
    Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git', Gitlab::GlRepository.gl_repository(project, is_wiki))
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,10 +51,13 @@ module Projects
 | 
			
		|||
 | 
			
		||||
    def import_repository
 | 
			
		||||
      begin
 | 
			
		||||
        if project.gitea_import?
 | 
			
		||||
          fetch_repository
 | 
			
		||||
        refmap = importer_class.try(:refmap) if has_importer?
 | 
			
		||||
 | 
			
		||||
        if refmap
 | 
			
		||||
          project.ensure_repository
 | 
			
		||||
          project.repository.fetch_as_mirror(project.import_url, refmap: refmap)
 | 
			
		||||
        else
 | 
			
		||||
          clone_repository
 | 
			
		||||
          gitlab_shell.import_repository(project.repository_storage_path, project.disk_path, project.import_url)
 | 
			
		||||
        end
 | 
			
		||||
      rescue Gitlab::Shell::Error, Gitlab::Git::RepositoryMirroring::RemoteError => e
 | 
			
		||||
        # Expire cache to prevent scenarios such as:
 | 
			
		||||
| 
						 | 
				
			
			@ -66,17 +69,6 @@ module Projects
 | 
			
		|||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def clone_repository
 | 
			
		||||
      gitlab_shell.import_repository(project.repository_storage_path, project.disk_path, project.import_url)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def fetch_repository
 | 
			
		||||
      project.ensure_repository
 | 
			
		||||
      project.repository.add_remote(project.import_type, project.import_url)
 | 
			
		||||
      project.repository.set_remote_as_mirror(project.import_type)
 | 
			
		||||
      project.repository.fetch_remote(project.import_type, forced: true)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def import_data
 | 
			
		||||
      return unless has_importer?
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1150,10 +1150,12 @@ module Gitlab
 | 
			
		|||
        @has_visible_content = has_local_branches?
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def fetch(remote = 'origin')
 | 
			
		||||
        args = %W(#{Gitlab.config.git.bin_path} fetch #{remote})
 | 
			
		||||
 | 
			
		||||
        popen(args, @path).last.zero?
 | 
			
		||||
      # Like all public `Gitlab::Git::Repository` methods, this method is part
 | 
			
		||||
      # of `Repository`'s interface through `method_missing`.
 | 
			
		||||
      # `Repository` has its own `fetch_remote` which uses `gitlab-shell` and
 | 
			
		||||
      # takes some extra attributes, so we qualify this method name to prevent confusion.
 | 
			
		||||
      def fetch_remote_without_shell(remote = 'origin')
 | 
			
		||||
        run_git(['fetch', remote]).last.zero?
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def blob_at(sha, path)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,38 +1,47 @@
 | 
			
		|||
module Gitlab
 | 
			
		||||
  module Git
 | 
			
		||||
    module RepositoryMirroring
 | 
			
		||||
      IMPORT_HEAD_REFS = '+refs/heads/*:refs/heads/*'.freeze
 | 
			
		||||
      IMPORT_TAG_REFS = '+refs/tags/*:refs/tags/*'.freeze
 | 
			
		||||
      MIRROR_REMOTE = 'mirror'.freeze
 | 
			
		||||
      REFMAPS = {
 | 
			
		||||
        # With `:all_refs`, the repository is equivalent to the result of `git clone --mirror`
 | 
			
		||||
        all_refs: '+refs/*:refs/*',
 | 
			
		||||
        heads: '+refs/heads/*:refs/heads/*',
 | 
			
		||||
        tags: '+refs/tags/*:refs/tags/*'
 | 
			
		||||
      }.freeze
 | 
			
		||||
 | 
			
		||||
      RemoteError = Class.new(StandardError)
 | 
			
		||||
 | 
			
		||||
      def set_remote_as_mirror(remote_name)
 | 
			
		||||
        # This is used to define repository as equivalent as "git clone --mirror"
 | 
			
		||||
        rugged.config["remote.#{remote_name}.fetch"] = 'refs/*:refs/*'
 | 
			
		||||
        rugged.config["remote.#{remote_name}.mirror"] = true
 | 
			
		||||
        rugged.config["remote.#{remote_name}.prune"] = true
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def set_import_remote_as_mirror(remote_name)
 | 
			
		||||
        # Add first fetch with Rugged so it does not create its own.
 | 
			
		||||
        rugged.config["remote.#{remote_name}.fetch"] = IMPORT_HEAD_REFS
 | 
			
		||||
 | 
			
		||||
        add_remote_fetch_config(remote_name, IMPORT_TAG_REFS)
 | 
			
		||||
      def set_remote_as_mirror(remote_name, refmap: :all_refs)
 | 
			
		||||
        set_remote_refmap(remote_name, refmap)
 | 
			
		||||
 | 
			
		||||
        rugged.config["remote.#{remote_name}.mirror"] = true
 | 
			
		||||
        rugged.config["remote.#{remote_name}.prune"] = true
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def add_remote_fetch_config(remote_name, refspec)
 | 
			
		||||
        run_git(%W[config --add remote.#{remote_name}.fetch #{refspec}])
 | 
			
		||||
      def set_remote_refmap(remote_name, refmap)
 | 
			
		||||
        Array(refmap).each_with_index do |refspec, i|
 | 
			
		||||
          refspec = REFMAPS[refspec] || refspec
 | 
			
		||||
 | 
			
		||||
          # We need multiple `fetch` entries, but Rugged only allows replacing a config, not adding to it.
 | 
			
		||||
          # To make sure we start from scratch, we set the first using rugged, and use `git` for any others
 | 
			
		||||
          if i == 0
 | 
			
		||||
            rugged.config["remote.#{remote_name}.fetch"] = refspec
 | 
			
		||||
          else
 | 
			
		||||
            run_git(%W[config --add remote.#{remote_name}.fetch #{refspec}])
 | 
			
		||||
          end
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def fetch_mirror(url)
 | 
			
		||||
        add_remote(MIRROR_REMOTE, url)
 | 
			
		||||
        set_remote_as_mirror(MIRROR_REMOTE)
 | 
			
		||||
        fetch(MIRROR_REMOTE)
 | 
			
		||||
        remove_remote(MIRROR_REMOTE)
 | 
			
		||||
      # Like all_refs public `Gitlab::Git::Repository` methods, this method is part
 | 
			
		||||
      # of `Repository`'s interface through `method_missing`.
 | 
			
		||||
      # `Repository` has its own `fetch_as_mirror` which uses `gitlab-shell` and
 | 
			
		||||
      # takes some extra attributes, so we qualify this method name to prevent confusion.
 | 
			
		||||
      def fetch_as_mirror_without_shell(url)
 | 
			
		||||
        remote_name = "tmp-#{SecureRandom.hex}"
 | 
			
		||||
        add_remote(remote_name, url)
 | 
			
		||||
        set_remote_as_mirror(remote_name)
 | 
			
		||||
        fetch_remote_without_shell(remote_name)
 | 
			
		||||
      ensure
 | 
			
		||||
        remove_remote(remote_name) if remote_name
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def remote_tags(remote)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,9 @@
 | 
			
		|||
module Gitlab
 | 
			
		||||
  module GithubImport
 | 
			
		||||
    def self.refmap
 | 
			
		||||
      [:heads, :tags, '+refs/pull/*/head:refs/merge-requests/*/head']
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def self.new_client_for(project, token: nil, parallel: true)
 | 
			
		||||
      token_to_use = token || project.import_data&.credentials&.fetch(:user)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,27 +45,14 @@ module Gitlab
 | 
			
		|||
        def import_repository
 | 
			
		||||
          project.ensure_repository
 | 
			
		||||
 | 
			
		||||
          configure_repository_remote
 | 
			
		||||
 | 
			
		||||
          project.repository.fetch_remote('github', forced: true)
 | 
			
		||||
          refmap = Gitlab::GithubImport.refmap
 | 
			
		||||
          project.repository.fetch_as_mirror(project.import_url, refmap: refmap, forced: true, remote_name: 'github')
 | 
			
		||||
 | 
			
		||||
          true
 | 
			
		||||
        rescue Gitlab::Git::Repository::NoRepository, Gitlab::Shell::Error => e
 | 
			
		||||
          fail_import("Failed to import the repository: #{e.message}")
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def configure_repository_remote
 | 
			
		||||
          return if project.repository.remote_exists?('github')
 | 
			
		||||
 | 
			
		||||
          project.repository.add_remote('github', project.import_url)
 | 
			
		||||
          project.repository.set_import_remote_as_mirror('github')
 | 
			
		||||
 | 
			
		||||
          project.repository.add_remote_fetch_config(
 | 
			
		||||
            'github',
 | 
			
		||||
            '+refs/pull/*/head:refs/merge-requests/*/head'
 | 
			
		||||
          )
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        def import_wiki_repository
 | 
			
		||||
          wiki_path = "#{project.disk_path}.wiki"
 | 
			
		||||
          wiki_url = project.import_url.sub(/\.git\z/, '.wiki.git')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,10 @@ module Gitlab
 | 
			
		|||
    class Importer
 | 
			
		||||
      include Gitlab::ShellAdapter
 | 
			
		||||
 | 
			
		||||
      def self.refmap
 | 
			
		||||
        Gitlab::GithubImport.refmap
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      attr_reader :errors, :project, :repo, :repo_url
 | 
			
		||||
 | 
			
		||||
      def initialize(project)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -588,12 +588,12 @@ describe Gitlab::Git::Repository, seed_helper: true do
 | 
			
		|||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe '#fetch_mirror' do
 | 
			
		||||
  describe '#fetch_as_mirror_without_shell' do
 | 
			
		||||
    let(:new_repository) do
 | 
			
		||||
      Gitlab::Git::Repository.new('default', 'my_project.git', '')
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    subject { new_repository.fetch_mirror(repository.path) }
 | 
			
		||||
    subject { new_repository.fetch_as_mirror_without_shell(repository.path) }
 | 
			
		||||
 | 
			
		||||
    before do
 | 
			
		||||
      Gitlab::Shell.new.add_repository('default', 'my_project')
 | 
			
		||||
| 
						 | 
				
			
			@ -1652,15 +1652,15 @@ describe Gitlab::Git::Repository, seed_helper: true do
 | 
			
		|||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe '#fetch' do
 | 
			
		||||
  describe '#fetch_remote_without_shell' do
 | 
			
		||||
    let(:git_path) { Gitlab.config.git.bin_path }
 | 
			
		||||
    let(:remote_name) { 'my_remote' }
 | 
			
		||||
 | 
			
		||||
    subject { repository.fetch(remote_name) }
 | 
			
		||||
    subject { repository.fetch_remote_without_shell(remote_name) }
 | 
			
		||||
 | 
			
		||||
    it 'fetches the remote and returns true if the command was successful' do
 | 
			
		||||
      expect(repository).to receive(:popen)
 | 
			
		||||
        .with(%W(#{git_path} fetch #{remote_name}), repository.path)
 | 
			
		||||
        .with(%W(#{git_path} fetch #{remote_name}), repository.path, {})
 | 
			
		||||
        .and_return(['', 0])
 | 
			
		||||
 | 
			
		||||
      expect(subject).to be(true)
 | 
			
		||||
| 
						 | 
				
			
			@ -1777,21 +1777,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
 | 
			
		|||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe '#fetch' do
 | 
			
		||||
    let(:git_path) { Gitlab.config.git.bin_path }
 | 
			
		||||
    let(:remote_name) { 'my_remote' }
 | 
			
		||||
 | 
			
		||||
    subject { repository.fetch(remote_name) }
 | 
			
		||||
 | 
			
		||||
    it 'fetches the remote and returns true if the command was successful' do
 | 
			
		||||
      expect(repository).to receive(:popen)
 | 
			
		||||
        .with(%W(#{git_path} fetch #{remote_name}), repository.path)
 | 
			
		||||
        .and_return(['', 0])
 | 
			
		||||
 | 
			
		||||
      expect(subject).to be(true)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe '#delete_all_refs_except' do
 | 
			
		||||
    let(:repository) do
 | 
			
		||||
      Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -164,12 +164,9 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do
 | 
			
		|||
      expect(project)
 | 
			
		||||
        .to receive(:ensure_repository)
 | 
			
		||||
 | 
			
		||||
      expect(importer)
 | 
			
		||||
        .to receive(:configure_repository_remote)
 | 
			
		||||
 | 
			
		||||
      expect(repository)
 | 
			
		||||
        .to receive(:fetch_remote)
 | 
			
		||||
        .with('github', forced: true)
 | 
			
		||||
        .to receive(:fetch_as_mirror)
 | 
			
		||||
        .with(project.import_url, refmap: Gitlab::GithubImport.refmap, forced: true, remote_name: 'github')
 | 
			
		||||
 | 
			
		||||
      expect(importer.import_repository).to eq(true)
 | 
			
		||||
    end
 | 
			
		||||
| 
						 | 
				
			
			@ -186,40 +183,6 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do
 | 
			
		|||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe '#configure_repository_remote' do
 | 
			
		||||
    it 'configures the remote details' do
 | 
			
		||||
      expect(repository)
 | 
			
		||||
        .to receive(:remote_exists?)
 | 
			
		||||
        .with('github')
 | 
			
		||||
        .and_return(false)
 | 
			
		||||
 | 
			
		||||
      expect(repository)
 | 
			
		||||
        .to receive(:add_remote)
 | 
			
		||||
        .with('github', 'foo.git')
 | 
			
		||||
 | 
			
		||||
      expect(repository)
 | 
			
		||||
        .to receive(:set_import_remote_as_mirror)
 | 
			
		||||
        .with('github')
 | 
			
		||||
 | 
			
		||||
      expect(repository)
 | 
			
		||||
        .to receive(:add_remote_fetch_config)
 | 
			
		||||
 | 
			
		||||
      importer.configure_repository_remote
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it 'does not configure the remote if already configured' do
 | 
			
		||||
      expect(repository)
 | 
			
		||||
        .to receive(:remote_exists?)
 | 
			
		||||
        .with('github')
 | 
			
		||||
        .and_return(true)
 | 
			
		||||
 | 
			
		||||
      expect(repository)
 | 
			
		||||
        .not_to receive(:add_remote)
 | 
			
		||||
 | 
			
		||||
      importer.configure_repository_remote
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  describe '#import_wiki_repository' do
 | 
			
		||||
    it 'imports the wiki repository' do
 | 
			
		||||
      expect(importer.gitlab_shell)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue