Commit 59b36f203274bedf6d455968d49af8fdba5e40ef
1 parent
b1e42551
Exists in
master
and in
4 other branches
Use gitlab-shell to move repos. Requires gitlab-shell v1.1.0
Showing
7 changed files
with
182 additions
and
236 deletions
Show diff stats
app/services/project_transfer_service.rb
... | ... | @@ -3,29 +3,27 @@ |
3 | 3 | # Used for transfer project to another namespace |
4 | 4 | # |
5 | 5 | class ProjectTransferService |
6 | + include Gitolited | |
7 | + | |
6 | 8 | attr_accessor :project |
7 | 9 | |
8 | 10 | def transfer(project, new_namespace) |
9 | 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 | 15 | if Project.where(path: project.path, namespace_id: new_namespace.try(:id)).present? |
23 | 16 | raise TransferError.new("Project with same path in target namespace already exists") |
24 | 17 | end |
25 | 18 | |
26 | - Gitlab::ProjectMover.new(project, old_dir, new_dir).execute | |
27 | - | |
19 | + project.namespace = new_namespace | |
28 | 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 | 27 | end |
30 | 28 | rescue Gitlab::ProjectMover::ProjectMoveError => ex |
31 | 29 | raise Project::TransferError.new(ex.message) | ... | ... |
lib/gitlab/backend/shell.rb
... | ... | @@ -24,6 +24,18 @@ module Gitlab |
24 | 24 | system("#{gitlab_shell_user_home}/gitlab-shell/bin/gitlab-projects import-project #{name}.git #{url}") |
25 | 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 | 39 | # Remove repository from file system |
28 | 40 | # |
29 | 41 | # name - project path with namespace |
... | ... | @@ -56,7 +68,7 @@ module Gitlab |
56 | 68 | def url_to_repo path |
57 | 69 | Gitlab.config.gitlab_shell.ssh_path_prefix + "#{path}.git" |
58 | 70 | end |
59 | - | |
71 | + | |
60 | 72 | def gitlab_shell_user_home |
61 | 73 | File.expand_path("~#{Gitlab.config.gitlab_shell.ssh_user}") |
62 | 74 | end | ... | ... |
lib/gitlab/project_mover.rb
... | ... | @@ -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 | -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 | -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 | - |
... | ... | @@ -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 | + | ... | ... |
... | ... | @@ -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 | + | ... | ... |