summaryrefslogtreecommitdiff
path: root/app/services/projects/fork_service.rb
blob: 4ec98696a656e6564ada3d5a40883bab146a86a6 (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
module Projects
  class ForkService < BaseService
    include Gitlab::ShellAdapter

    def execute
      @from_project = @project

      project_params = {
        visibility_level: @from_project.visibility_level,
        description: @from_project.description,
      }

      project = Project.new(project_params)
      project.name = @from_project.name
      project.path = @from_project.path
      project.creator = @current_user
      if @from_project.avatar.present? && @from_project.avatar.image?
        project.avatar = @from_project.avatar
      end

      if namespace = @params[:namespace]
        project.namespace = namespace
      else
        project.namespace = @current_user.namespace
      end

      unless @current_user.can?(:create_projects, project.namespace)
        project.errors.add(:namespace, 'insufficient access rights')
        return project
      end

      # If the project cannot save, we do not want to trigger the project destroy
      # as this can have the side effect of deleting a repo attached to an existing
      # project with the same name and namespace
      if project.valid?
        begin
          Project.transaction do
            #First save the DB entries as they can be rolled back if the repo fork fails
            project.build_forked_project_link(forked_to_project_id: project.id, forked_from_project_id: @from_project.id)
            if project.save
              project.team << [@current_user, :master]
            end

            #Now fork the repo
            unless gitlab_shell.fork_repository(@from_project.path_with_namespace, project.namespace.path)
              raise 'forking failed in gitlab-shell'
            end

            project.ensure_satellite_exists
          end

          if @from_project.gitlab_ci?
            ForkRegistrationWorker.perform_async(@from_project.id, project.id, @current_user.private_token)
          end
        rescue => ex
          project.errors.add(:base, 'Fork transaction failed.')
          project.destroy
        end
      else
        project.errors.add(:base, 'Invalid fork destination')
      end

      project
    end
  end
end