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 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   -
spec/services/git_push_service_spec.rb 0 → 100644
... ... @@ -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 @@
  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 +
... ...