1# frozen_string_literal: true 2 3module ProjectForksHelper 4 def fork_project(project, user = nil, params = {}) 5 Gitlab::GitalyClient.allow_n_plus_1_calls do 6 fork_project_direct(project, user, params) 7 end 8 end 9 10 def fork_project_direct(project, user = nil, params = {}) 11 # Load the `fork_network` for the project to fork as there might be one that 12 # wasn't loaded yet. 13 project.reload unless project.fork_network 14 15 unless user 16 user = create(:user) 17 project.add_developer(user) 18 end 19 20 unless params[:namespace] 21 params[:namespace] = create(:group) 22 params[:namespace].add_owner(user) 23 end 24 25 namespace = params[:namespace] 26 create_repository = params.delete(:repository) 27 28 unless params[:target_project] || params[:using_service] 29 target_level = [project.visibility_level, namespace.visibility_level].min 30 visibility_level = Gitlab::VisibilityLevel.closest_allowed_level(target_level) 31 # Builds and MRs can't have higher visibility level than repository access level. 32 builds_access_level = [project.builds_access_level, project.repository_access_level].min 33 34 params[:target_project] = 35 create(:project, 36 (:repository if create_repository), 37 visibility_level: visibility_level, 38 builds_access_level: builds_access_level, 39 creator: user, namespace: namespace) 40 end 41 42 service = Projects::ForkService.new(project, user, params) 43 44 # Avoid creating a repository 45 unless create_repository 46 allow(RepositoryForkWorker).to receive(:perform_async).and_return(true) 47 shell = double('gitlab_shell', fork_repository: true) 48 allow(service).to receive(:gitlab_shell).and_return(shell) 49 end 50 51 forked_project = service.execute(params[:target_project]) 52 53 # Reload the both projects so they know about their newly created fork_network 54 if forked_project.persisted? 55 project.reload 56 forked_project.reload 57 end 58 59 if create_repository 60 # The call to project.repository.after_import in RepositoryForkWorker does 61 # not reset the @exists variable of this forked_project.repository 62 # so we have to explicitly call this method to clear the @exists variable. 63 # of the instance we're returning here. 64 forked_project.repository.expire_content_cache 65 end 66 67 forked_project 68 end 69 70 def fork_project_with_submodules(project, user = nil, params = {}) 71 Gitlab::GitalyClient.allow_n_plus_1_calls do 72 forked_project = fork_project_direct(project, user, params) 73 TestEnv.copy_repo( 74 forked_project, 75 bare_repo: TestEnv.forked_repo_path_bare, 76 refs: TestEnv::FORKED_BRANCH_SHA 77 ) 78 forked_project.repository.expire_content_cache 79 80 forked_project 81 end 82 end 83end 84