Commit 59b36f203274bedf6d455968d49af8fdba5e40ef

Authored by Dmitriy Zaporozhets
1 parent b1e42551

Use gitlab-shell to move repos. Requires gitlab-shell v1.1.0

app/services/project_transfer_service.rb
@@ -3,29 +3,27 @@ @@ -3,29 +3,27 @@
3 # Used for transfer project to another namespace 3 # Used for transfer project to another namespace
4 # 4 #
5 class ProjectTransferService 5 class ProjectTransferService
  6 + include Gitolited
  7 +
6 attr_accessor :project 8 attr_accessor :project
7 9
8 def transfer(project, new_namespace) 10 def transfer(project, new_namespace)
9 Project.transaction do 11 Project.transaction do
10 - old_namespace = project.namespace  
11 - project.namespace = new_namespace  
12 -  
13 - old_dir = old_namespace.try(:path) || ''  
14 - new_dir = new_namespace.try(:path) || ''  
15 -  
16 - old_repo = if old_dir.present?  
17 - File.join(old_dir, project.path)  
18 - else  
19 - project.path  
20 - end 12 + old_path = project.path_with_namespace
  13 + new_path = File.join(new_namespace.try(:path) || '', project.path)
21 14
22 if Project.where(path: project.path, namespace_id: new_namespace.try(:id)).present? 15 if Project.where(path: project.path, namespace_id: new_namespace.try(:id)).present?
23 raise TransferError.new("Project with same path in target namespace already exists") 16 raise TransferError.new("Project with same path in target namespace already exists")
24 end 17 end
25 18
26 - Gitlab::ProjectMover.new(project, old_dir, new_dir).execute  
27 - 19 + project.namespace = new_namespace
28 project.save! 20 project.save!
  21 +
  22 + unless gitlab_shell.mv_repository(old_path, new_path)
  23 + raise TransferError.new('Cannot move project')
  24 + end
  25 +
  26 + true
29 end 27 end
30 rescue Gitlab::ProjectMover::ProjectMoveError => ex 28 rescue Gitlab::ProjectMover::ProjectMoveError => ex
31 raise Project::TransferError.new(ex.message) 29 raise Project::TransferError.new(ex.message)
lib/gitlab/backend/shell.rb
@@ -24,6 +24,18 @@ module Gitlab @@ -24,6 +24,18 @@ module Gitlab
24 system("#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects import-project #{name}.git #{url}") 24 system("#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects import-project #{name}.git #{url}")
25 end 25 end
26 26
  27 + # Move repository
  28 + #
  29 + # path - project path with namespace
  30 + # new_path - new project path with namespace
  31 + #
  32 + # Ex.
  33 + # mv_repository("gitlab/gitlab-ci", "randx/gitlab-ci-new.git")
  34 + #
  35 + def mv_repository(path, new_path)
  36 + system("#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects mv-project #{path}.git #{new_path}.git")
  37 + end
  38 +
27 # Remove repository from file system 39 # Remove repository from file system
28 # 40 #
29 # name - project path with namespace 41 # name - project path with namespace
@@ -56,7 +68,7 @@ module Gitlab @@ -56,7 +68,7 @@ module Gitlab
56 def url_to_repo path 68 def url_to_repo path
57 Gitlab.config.gitlab_shell.ssh_path_prefix + "#{path}.git" 69 Gitlab.config.gitlab_shell.ssh_path_prefix + "#{path}.git"
58 end 70 end
59 - 71 +
60 def gitlab_shell_user_home 72 def gitlab_shell_user_home
61 File.expand_path("~#{Gitlab.config.gitlab_shell.ssh_user}") 73 File.expand_path("~#{Gitlab.config.gitlab_shell.ssh_user}")
62 end 74 end
lib/gitlab/project_mover.rb
@@ -1,45 +0,0 @@ @@ -1,45 +0,0 @@
1 -# ProjectMover class  
2 -#  
3 -# Used for moving project repositories from one subdir to another  
4 -module Gitlab  
5 - class ProjectMover  
6 - class ProjectMoveError < StandardError; end  
7 -  
8 - attr_reader :project, :old_dir, :new_dir  
9 -  
10 - def initialize(project, old_dir, new_dir)  
11 - @project = project  
12 - @old_dir = old_dir  
13 - @new_dir = new_dir  
14 - end  
15 -  
16 - def execute  
17 - # Create new dir if missing  
18 - new_dir_path = File.join(Gitlab.config.gitlab_shell.repos_path, new_dir)  
19 - FileUtils.mkdir( new_dir_path, mode: 0770 ) unless File.exists?(new_dir_path)  
20 -  
21 - old_path = File.join(Gitlab.config.gitlab_shell.repos_path, old_dir, "#{project.path}.git")  
22 - new_path = File.join(new_dir_path, "#{project.path}.git")  
23 -  
24 - if File.exists? new_path  
25 - raise ProjectMoveError.new("Destination #{new_path} already exists")  
26 - end  
27 -  
28 - begin  
29 - FileUtils.mv( old_path, new_path )  
30 - log_info "Project #{project.name} was moved from #{old_path} to #{new_path}"  
31 - true  
32 - rescue Exception => e  
33 - message = "Project #{project.name} cannot be moved from #{old_path} to #{new_path}"  
34 - log_info "Error! #{message} (#{e.message})"  
35 - raise ProjectMoveError.new(message)  
36 - end  
37 - end  
38 -  
39 - protected  
40 -  
41 - def log_info message  
42 - Gitlab::AppLogger.info message  
43 - end  
44 - end  
45 -end  
spec/lib/project_mover_spec.rb
@@ -1,66 +0,0 @@ @@ -1,66 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe Gitlab::ProjectMover do  
4 - let(:base_path) { Rails.root.join('tmp', 'rspec-sandbox') }  
5 -  
6 - before do  
7 - FileUtils.rm_rf base_path if File.exists? base_path  
8 - FileUtils.mkdir_p base_path  
9 -  
10 - Gitlab.config.gitlab_shell.stub(repos_path: base_path)  
11 -  
12 - @project = create(:project)  
13 - end  
14 -  
15 - after do  
16 - FileUtils.rm_rf base_path  
17 - end  
18 -  
19 - it "should move project to subdir" do  
20 - mk_dir base_path, '', @project.path  
21 - mover = Gitlab::ProjectMover.new(@project, '', 'opensource')  
22 -  
23 - mover.execute.should be_true  
24 - moved?('opensource', @project.path).should be_true  
25 - end  
26 -  
27 - it "should move project from one subdir to another" do  
28 - mk_dir base_path, 'vsizov', @project.path  
29 - mover = Gitlab::ProjectMover.new(@project, 'vsizov', 'randx')  
30 -  
31 - mover.execute.should be_true  
32 - moved?('randx', @project.path).should be_true  
33 - end  
34 -  
35 - it "should move project from subdir to base" do  
36 - mk_dir base_path, 'vsizov', @project.path  
37 - mover = Gitlab::ProjectMover.new(@project, 'vsizov', '')  
38 -  
39 - mover.execute.should be_true  
40 - moved?('', @project.path).should be_true  
41 - end  
42 -  
43 - it "should raise if destination exists" do  
44 - mk_dir base_path, '', @project.path  
45 - mk_dir base_path, 'vsizov', @project.path  
46 - mover = Gitlab::ProjectMover.new(@project, 'vsizov', '')  
47 -  
48 - expect { mover.execute }.to raise_error(Gitlab::ProjectMover::ProjectMoveError)  
49 - end  
50 -  
51 - it "should raise if move failed" do  
52 - mk_dir base_path  
53 - mover = Gitlab::ProjectMover.new(@project, 'vsizov', '')  
54 -  
55 - expect { mover.execute }.to raise_error(Gitlab::ProjectMover::ProjectMoveError)  
56 - end  
57 -  
58 -  
59 - def mk_dir base_path, namespace = '', project_path = ''  
60 - FileUtils.mkdir_p File.join(base_path, namespace, project_path + ".git")  
61 - end  
62 -  
63 - def moved? namespace, path  
64 - File.exists?(File.join(base_path, namespace, path + '.git'))  
65 - end  
66 -end  
spec/services/git_push_service.rb
@@ -1,111 +0,0 @@ @@ -1,111 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe GitPushService do  
4 - let (:user) { create :user }  
5 - let (:project) { create :project }  
6 - let (:service) { GitPushService.new }  
7 -  
8 - before do  
9 - @oldrev = 'b98a310def241a6fd9c9a9a3e7934c48e498fe81'  
10 - @newrev = 'b19a04f53caeebf4fe5ec2327cb83e9253dc91bb'  
11 - @ref = 'refs/heads/master'  
12 - end  
13 -  
14 - describe "Git Push Data" do  
15 - before do  
16 - service.execute(project, user, @oldrev, @newrev, @ref)  
17 - @push_data = service.push_data  
18 - @commit = project.repository.commit(@newrev)  
19 - end  
20 -  
21 - subject { @push_data }  
22 -  
23 - it { should include(before: @oldrev) }  
24 - it { should include(after: @newrev) }  
25 - it { should include(ref: @ref) }  
26 - it { should include(user_id: user.id) }  
27 - it { should include(user_name: user.name) }  
28 -  
29 - context "with repository data" do  
30 - subject { @push_data[:repository] }  
31 -  
32 - it { should include(name: project.name) }  
33 - it { should include(url: project.url_to_repo) }  
34 - it { should include(description: project.description) }  
35 - it { should include(homepage: project.web_url) }  
36 - end  
37 -  
38 - context "with commits" do  
39 - subject { @push_data[:commits] }  
40 -  
41 - it { should be_an(Array) }  
42 - it { should have(1).element }  
43 -  
44 - context "the commit" do  
45 - subject { @push_data[:commits].first }  
46 -  
47 - it { should include(id: @commit.id) }  
48 - it { should include(message: @commit.safe_message) }  
49 - it { should include(timestamp: @commit.date.xmlschema) }  
50 - it { should include(url: "#{Gitlab.config.gitlab.url}/#{project.code}/commit/#{@commit.id}") }  
51 -  
52 - context "with a author" do  
53 - subject { @push_data[:commits].first[:author] }  
54 -  
55 - it { should include(name: @commit.author_name) }  
56 - it { should include(email: @commit.author_email) }  
57 - end  
58 - end  
59 - end  
60 - end  
61 -  
62 - describe "Push Event" do  
63 - before do  
64 - service.execute(project, user, @oldrev, @newrev, @ref)  
65 - @event = Event.last  
66 - end  
67 -  
68 - it { @event.should_not be_nil }  
69 - it { @event.project.should == project }  
70 - it { @event.action.should == Event::PUSHED }  
71 - it { @event.data.should == service.push_data }  
72 - end  
73 -  
74 - describe "Web Hooks" do  
75 - context "with web hooks" do  
76 - before do  
77 - @project_hook = create(:project_hook)  
78 - @project_hook_2 = create(:project_hook)  
79 - project.hooks << [@project_hook, @project_hook_2]  
80 -  
81 - stub_request(:post, @project_hook.url)  
82 - stub_request(:post, @project_hook_2.url)  
83 - end  
84 -  
85 - it "executes multiple web hook" do  
86 - @project_hook.should_receive(:async_execute).once  
87 - @project_hook_2.should_receive(:async_execute).once  
88 -  
89 - service.execute(project, user, @oldrev, @newrev, @ref)  
90 - end  
91 - end  
92 -  
93 - context "does not execute web hooks" do  
94 - before do  
95 - @project_hook = create(:project_hook)  
96 - project.hooks << [@project_hook]  
97 - end  
98 -  
99 - it "when pushing a branch for the first time" do  
100 - @project_hook.should_not_receive(:execute)  
101 - service.execute(project, user, '00000000000000000000000000000000', 'newrev', 'refs/heads/master')  
102 - end  
103 -  
104 - it "when pushing tags" do  
105 - @project_hook.should_not_receive(:execute)  
106 - service.execute(project, user, 'newrev', 'newrev', 'refs/tags/v1.0.0')  
107 - end  
108 - end  
109 - end  
110 -end  
111 -  
spec/services/git_push_service_spec.rb 0 → 100644
@@ -0,0 +1,111 @@ @@ -0,0 +1,111 @@
  1 +require 'spec_helper'
  2 +
  3 +describe GitPushService do
  4 + let (:user) { create :user }
  5 + let (:project) { create :project }
  6 + let (:service) { GitPushService.new }
  7 +
  8 + before do
  9 + @oldrev = 'b98a310def241a6fd9c9a9a3e7934c48e498fe81'
  10 + @newrev = 'b19a04f53caeebf4fe5ec2327cb83e9253dc91bb'
  11 + @ref = 'refs/heads/master'
  12 + end
  13 +
  14 + describe "Git Push Data" do
  15 + before do
  16 + service.execute(project, user, @oldrev, @newrev, @ref)
  17 + @push_data = service.push_data
  18 + @commit = project.repository.commit(@newrev)
  19 + end
  20 +
  21 + subject { @push_data }
  22 +
  23 + it { should include(before: @oldrev) }
  24 + it { should include(after: @newrev) }
  25 + it { should include(ref: @ref) }
  26 + it { should include(user_id: user.id) }
  27 + it { should include(user_name: user.name) }
  28 +
  29 + context "with repository data" do
  30 + subject { @push_data[:repository] }
  31 +
  32 + it { should include(name: project.name) }
  33 + it { should include(url: project.url_to_repo) }
  34 + it { should include(description: project.description) }
  35 + it { should include(homepage: project.web_url) }
  36 + end
  37 +
  38 + context "with commits" do
  39 + subject { @push_data[:commits] }
  40 +
  41 + it { should be_an(Array) }
  42 + it { should have(1).element }
  43 +
  44 + context "the commit" do
  45 + subject { @push_data[:commits].first }
  46 +
  47 + it { should include(id: @commit.id) }
  48 + it { should include(message: @commit.safe_message) }
  49 + it { should include(timestamp: @commit.date.xmlschema) }
  50 + it { should include(url: "#{Gitlab.config.gitlab.url}/#{project.code}/commit/#{@commit.id}") }
  51 +
  52 + context "with a author" do
  53 + subject { @push_data[:commits].first[:author] }
  54 +
  55 + it { should include(name: @commit.author_name) }
  56 + it { should include(email: @commit.author_email) }
  57 + end
  58 + end
  59 + end
  60 + end
  61 +
  62 + describe "Push Event" do
  63 + before do
  64 + service.execute(project, user, @oldrev, @newrev, @ref)
  65 + @event = Event.last
  66 + end
  67 +
  68 + it { @event.should_not be_nil }
  69 + it { @event.project.should == project }
  70 + it { @event.action.should == Event::PUSHED }
  71 + it { @event.data.should == service.push_data }
  72 + end
  73 +
  74 + describe "Web Hooks" do
  75 + context "with web hooks" do
  76 + before do
  77 + @project_hook = create(:project_hook)
  78 + @project_hook_2 = create(:project_hook)
  79 + project.hooks << [@project_hook, @project_hook_2]
  80 +
  81 + stub_request(:post, @project_hook.url)
  82 + stub_request(:post, @project_hook_2.url)
  83 + end
  84 +
  85 + it "executes multiple web hook" do
  86 + @project_hook.should_receive(:async_execute).once
  87 + @project_hook_2.should_receive(:async_execute).once
  88 +
  89 + service.execute(project, user, @oldrev, @newrev, @ref)
  90 + end
  91 + end
  92 +
  93 + context "does not execute web hooks" do
  94 + before do
  95 + @project_hook = create(:project_hook)
  96 + project.hooks << [@project_hook]
  97 + end
  98 +
  99 + it "when pushing a branch for the first time" do
  100 + @project_hook.should_not_receive(:execute)
  101 + service.execute(project, user, '00000000000000000000000000000000', 'newrev', 'refs/heads/master')
  102 + end
  103 +
  104 + it "when pushing tags" do
  105 + @project_hook.should_not_receive(:execute)
  106 + service.execute(project, user, 'newrev', 'newrev', 'refs/tags/v1.0.0')
  107 + end
  108 + end
  109 + end
  110 +end
  111 +
spec/services/project_transfer_service_spec.rb 0 → 100644
@@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
  1 +require 'spec_helper'
  2 +
  3 +describe ProjectTransferService do
  4 + context 'namespace -> namespace' do
  5 + let(:user) { create(:user) }
  6 + let(:group) { create(:group) }
  7 + let(:project) { create(:project, namespace: user.namespace) }
  8 +
  9 + before do
  10 + @result = service.transfer(project, group)
  11 + end
  12 +
  13 + it { @result.should be_true }
  14 + it { project.namespace.should == group }
  15 + end
  16 +
  17 + context 'namespace -> no namespace' do
  18 + let(:user) { create(:user) }
  19 + let(:project) { create(:project, namespace: user.namespace) }
  20 +
  21 + before do
  22 + @result = service.transfer(project, nil)
  23 + end
  24 +
  25 + it { @result.should be_true }
  26 + it { project.namespace.should == nil }
  27 + end
  28 +
  29 + context 'no namespace -> namespace' do
  30 + let(:project) { create(:project) }
  31 + let(:user) { create(:user) }
  32 +
  33 + before do
  34 + @result = service.transfer(project, user.namespace)
  35 + end
  36 +
  37 + it { @result.should be_true }
  38 + it { project.namespace.should == user.namespace }
  39 + end
  40 +
  41 + def service
  42 + service = ProjectTransferService.new
  43 + service.gitlab_shell.stub(mv_repository: true)
  44 + service
  45 + end
  46 +end
  47 +