summaryrefslogtreecommitdiff
path: root/app/services/projects/import_service.rb
blob: 0c426faa22de986506f0036111f8483a4daa6f46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# frozen_string_literal: true

module Projects
  class ImportService < BaseService
    include Gitlab::ShellAdapter

    Error = Class.new(StandardError)

    # Returns true if this importer is supposed to perform its work in the
    # background.
    #
    # This method will only return `true` if async importing is explicitly
    # supported by an importer class (`Gitlab::GithubImport::ParallelImporter`
    # for example).
    def async?
      has_importer? && !!importer_class.try(:async?)
    end

    def execute
      add_repository_to_project

      download_lfs_objects

      import_data

      success
    rescue => e
      error("Error importing repository #{project.safe_import_url} into #{project.full_path} - #{e.message}")
    end

    private

    def add_repository_to_project
      if project.external_import? && !unknown_url?
        begin
          Gitlab::UrlBlocker.validate!(project.import_url, ports: Project::VALID_IMPORT_PORTS)
        rescue Gitlab::UrlBlocker::BlockedUrlError => e
          raise Error, "Blocked import URL: #{e.message}"
        end
      end

      # We should skip the repository for a GitHub import or GitLab project import,
      # because these importers fetch the project repositories for us.
      return if importer_imports_repository?

      if unknown_url?
        # In this case, we only want to import issues, not a repository.
        create_repository
      elsif !project.repository_exists?
        import_repository
      end
    end

    def create_repository
      unless project.create_repository
        raise Error, 'The repository could not be created.'
      end
    end

    def import_repository
      begin
        refmap = importer_class.try(:refmap) if has_importer?

        if refmap
          project.ensure_repository
          project.repository.fetch_as_mirror(project.import_url, refmap: refmap)
        else
          gitlab_shell.import_repository(project.repository_storage, project.disk_path, project.import_url)
        end
      rescue Gitlab::Shell::Error => e
        # Expire cache to prevent scenarios such as:
        # 1. First import failed, but the repo was imported successfully, so +exists?+ returns true
        # 2. Retried import, repo is broken or not imported but +exists?+ still returns true
        project.repository.expire_content_cache if project.repository_exists?

        raise Error, e.message
      end
    end

    def download_lfs_objects
      # In this case, we only want to import issues
      return if unknown_url?

      # If it has its own repository importer, it has to implements its own lfs import download
      return if importer_imports_repository?

      return unless project.lfs_enabled?

      oids_to_download = Projects::LfsPointers::LfsImportService.new(project).execute
      download_service = Projects::LfsPointers::LfsDownloadService.new(project)

      oids_to_download.each do |oid, link|
        download_service.execute(oid, link)
      end
    rescue => e
      # Right now, to avoid aborting the importing process, we silently fail
      # if any exception raises.
      Rails.logger.error("The Lfs import process failed. #{e.message}")
    end

    def import_data
      return unless has_importer?

      project.repository.expire_content_cache unless project.gitlab_project_import?

      unless importer.execute
        raise Error, 'The remote data could not be imported.'
      end
    end

    def importer_class
      @importer_class ||= Gitlab::ImportSources.importer(project.import_type)
    end

    def has_importer?
      Gitlab::ImportSources.importer_names.include?(project.import_type)
    end

    def importer
      importer_class.new(project)
    end

    def unknown_url?
      project.import_url == Project::UNKNOWN_IMPORT_URL
    end

    def importer_imports_repository?
      has_importer? && importer_class.try(:imports_repository?)
    end
  end
end