Commit 7bab81b199936597e1c762278bd16167de073342

Authored by Dmitriy Zaporozhets
1 parent 9611640e

Move git post push logic to service

app/models/project.rb
@@ -295,53 +295,6 @@ class Project < ActiveRecord::Base @@ -295,53 +295,6 @@ class Project < ActiveRecord::Base
295 end 295 end
296 end 296 end
297 297
298 - # This method will be called after each post receive and only if the provided  
299 - # user is present in GitLab.  
300 - #  
301 - # All callbacks for post receive should be placed here.  
302 - def trigger_post_receive(oldrev, newrev, ref, user)  
303 - data = post_receive_data(oldrev, newrev, ref, user)  
304 -  
305 - # Create satellite  
306 - self.satellite.create unless self.satellite.exists?  
307 -  
308 - # Create push event  
309 - self.observe_push(data)  
310 -  
311 - if push_to_branch? ref, oldrev  
312 - # Close merged MR  
313 - self.update_merge_requests(oldrev, newrev, ref, user)  
314 -  
315 - # Execute web hooks  
316 - self.execute_hooks(data.dup)  
317 -  
318 - # Execute project services  
319 - self.execute_services(data.dup)  
320 - end  
321 -  
322 - # Discover the default branch, but only if it hasn't already been set to  
323 - # something else  
324 - if repository && default_branch.nil?  
325 - update_attributes(default_branch: self.repository.discover_default_branch)  
326 - end  
327 - end  
328 -  
329 - def push_to_branch? ref, oldrev  
330 - ref_parts = ref.split('/')  
331 -  
332 - # Return if this is not a push to a branch (e.g. new commits)  
333 - !(ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000")  
334 - end  
335 -  
336 - def observe_push(data)  
337 - Event.create(  
338 - project: self,  
339 - action: Event::PUSHED,  
340 - data: data,  
341 - author_id: data[:user_id]  
342 - )  
343 - end  
344 -  
345 def execute_hooks(data) 298 def execute_hooks(data)
346 hooks.each { |hook| hook.async_execute(data) } 299 hooks.each { |hook| hook.async_execute(data) }
347 end 300 end
@@ -354,68 +307,12 @@ class Project < ActiveRecord::Base @@ -354,68 +307,12 @@ class Project < ActiveRecord::Base
354 end 307 end
355 end 308 end
356 309
357 - # Produce a hash of post-receive data  
358 - #  
359 - # data = {  
360 - # before: String,  
361 - # after: String,  
362 - # ref: String,  
363 - # user_id: String,  
364 - # user_name: String,  
365 - # repository: {  
366 - # name: String,  
367 - # url: String,  
368 - # description: String,  
369 - # homepage: String,  
370 - # },  
371 - # commits: Array,  
372 - # total_commits_count: Fixnum  
373 - # }  
374 - #  
375 - def post_receive_data(oldrev, newrev, ref, user)  
376 -  
377 - push_commits = repository.commits_between(oldrev, newrev)  
378 -  
379 - # Total commits count  
380 - push_commits_count = push_commits.size  
381 -  
382 - # Get latest 20 commits ASC  
383 - push_commits_limited = push_commits.last(20)  
384 -  
385 - # Hash to be passed as post_receive_data  
386 - data = {  
387 - before: oldrev,  
388 - after: newrev,  
389 - ref: ref,  
390 - user_id: user.id,  
391 - user_name: user.name,  
392 - repository: {  
393 - name: name,  
394 - url: url_to_repo,  
395 - description: description,  
396 - homepage: web_url,  
397 - },  
398 - commits: [],  
399 - total_commits_count: push_commits_count  
400 - }  
401 -  
402 - # For perfomance purposes maximum 20 latest commits  
403 - # will be passed as post receive hook data.  
404 - #  
405 - push_commits_limited.each do |commit|  
406 - data[:commits] << {  
407 - id: commit.id,  
408 - message: commit.safe_message,  
409 - timestamp: commit.date.xmlschema,  
410 - url: "#{Gitlab.config.gitlab.url}/#{path_with_namespace}/commit/#{commit.id}",  
411 - author: {  
412 - name: commit.author_name,  
413 - email: commit.author_email  
414 - }  
415 - } 310 + def discover_default_branch
  311 + # Discover the default branch, but only if it hasn't already been set to
  312 + # something else
  313 + if repository && default_branch.nil?
  314 + update_attributes(default_branch: self.repository.discover_default_branch)
416 end 315 end
417 -  
418 - data  
419 end 316 end
420 317
421 def update_merge_requests(oldrev, newrev, ref, user) 318 def update_merge_requests(oldrev, newrev, ref, user)
@@ -446,6 +343,10 @@ class Project &lt; ActiveRecord::Base @@ -446,6 +343,10 @@ class Project &lt; ActiveRecord::Base
446 !repository || repository.empty? 343 !repository || repository.empty?
447 end 344 end
448 345
  346 + def ensure_satellite_exists
  347 + self.satellite.create unless self.satellite.exists?
  348 + end
  349 +
449 def satellite 350 def satellite
450 @satellite ||= Gitlab::Satellite::Satellite.new(self) 351 @satellite ||= Gitlab::Satellite::Satellite.new(self)
451 end 352 end
app/services/git_push_service.rb 0 → 100644
@@ -0,0 +1,114 @@ @@ -0,0 +1,114 @@
  1 +class GitPushService
  2 + attr_accessor :project, :user, :push_data
  3 +
  4 + # This method will be called after each git update
  5 + # and only if the provided user and project is present in GitLab.
  6 + #
  7 + # All callbacks for post receive action should be placed here.
  8 + #
  9 + # Now this method do next:
  10 + # 1. Ensure project satellite exists
  11 + # 2. Update merge requests
  12 + # 3. Execute project web hooks
  13 + # 4. Execute project services
  14 + # 5. Create Push Event
  15 + #
  16 + def execute(project, user, oldrev, newrev, ref)
  17 + @project, @user = project, user
  18 +
  19 + # Collect data for this git push
  20 + @push_data = post_receive_data(oldrev, newrev, ref)
  21 +
  22 + project.ensure_satellite_exists
  23 + project.discover_default_branch
  24 +
  25 + if push_to_branch?(ref, oldrev)
  26 + project.update_merge_requests(oldrev, newrev, ref, @user)
  27 + project.execute_hooks(@push_data.dup)
  28 + project.execute_services(@push_data.dup)
  29 + end
  30 +
  31 + create_push_event
  32 + end
  33 +
  34 + protected
  35 +
  36 + def create_push_event
  37 + Event.create(
  38 + project: project,
  39 + action: Event::PUSHED,
  40 + data: push_data,
  41 + author_id: push_data[:user_id]
  42 + )
  43 + end
  44 +
  45 + # Produce a hash of post-receive data
  46 + #
  47 + # data = {
  48 + # before: String,
  49 + # after: String,
  50 + # ref: String,
  51 + # user_id: String,
  52 + # user_name: String,
  53 + # repository: {
  54 + # name: String,
  55 + # url: String,
  56 + # description: String,
  57 + # homepage: String,
  58 + # },
  59 + # commits: Array,
  60 + # total_commits_count: Fixnum
  61 + # }
  62 + #
  63 + def post_receive_data(oldrev, newrev, ref)
  64 + push_commits = project.repository.commits_between(oldrev, newrev)
  65 +
  66 + # Total commits count
  67 + push_commits_count = push_commits.size
  68 +
  69 + # Get latest 20 commits ASC
  70 + push_commits_limited = push_commits.last(20)
  71 +
  72 + # Hash to be passed as post_receive_data
  73 + data = {
  74 + before: oldrev,
  75 + after: newrev,
  76 + ref: ref,
  77 + user_id: user.id,
  78 + user_name: user.name,
  79 + repository: {
  80 + name: project.name,
  81 + url: project.url_to_repo,
  82 + description: project.description,
  83 + homepage: project.web_url,
  84 + },
  85 + commits: [],
  86 + total_commits_count: push_commits_count
  87 + }
  88 +
  89 + # For perfomance purposes maximum 20 latest commits
  90 + # will be passed as post receive hook data.
  91 + #
  92 + push_commits_limited.each do |commit|
  93 + data[:commits] << {
  94 + id: commit.id,
  95 + message: commit.safe_message,
  96 + timestamp: commit.date.xmlschema,
  97 + url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/#{commit.id}",
  98 + author: {
  99 + name: commit.author_name,
  100 + email: commit.author_email
  101 + }
  102 + }
  103 + end
  104 +
  105 + data
  106 + end
  107 +
  108 + def push_to_branch? ref, oldrev
  109 + ref_parts = ref.split('/')
  110 +
  111 + # Return if this is not a push to a branch (e.g. new commits)
  112 + !(ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000")
  113 + end
  114 +end
app/workers/post_receive.rb
@@ -42,6 +42,6 @@ class PostReceive @@ -42,6 +42,6 @@ class PostReceive
42 return false 42 return false
43 end 43 end
44 44
45 - project.trigger_post_receive(oldrev, newrev, ref, user) 45 + GitPushService.new.execute(project, user, oldrev, newrev, ref)
46 end 46 end
47 end 47 end
spec/models/project_hooks_spec.rb
@@ -1,128 +0,0 @@ @@ -1,128 +0,0 @@
1 -require 'spec_helper'  
2 -  
3 -describe Project, "Hooks" do  
4 - let(:project) { create(:project) }  
5 -  
6 - before do  
7 - @key = create(:key, user: project.owner)  
8 - @user = @key.user  
9 - @key_id = @key.identifier  
10 - end  
11 -  
12 - describe "Post Receive Event" do  
13 - it "should create push event" do  
14 - oldrev, newrev, ref = '00000000000000000000000000000000', 'newrev', 'refs/heads/master'  
15 - data = project.post_receive_data(oldrev, newrev, ref, @user)  
16 -  
17 - project.observe_push(data)  
18 - event = Event.last  
19 -  
20 - event.should_not be_nil  
21 - event.project.should == project  
22 - event.action.should == Event::PUSHED  
23 - event.data.should == data  
24 - end  
25 - end  
26 -  
27 - describe "Project hooks" do  
28 - context "with no web hooks" do  
29 - it "raises no errors" do  
30 - lambda {  
31 - project.execute_hooks({})  
32 - }.should_not raise_error  
33 - end  
34 - end  
35 -  
36 - context "with web hooks" do  
37 - before do  
38 - @project_hook = create(:project_hook)  
39 - @project_hook_2 = create(:project_hook)  
40 - project.hooks << [@project_hook, @project_hook_2]  
41 -  
42 - stub_request(:post, @project_hook.url)  
43 - stub_request(:post, @project_hook_2.url)  
44 - end  
45 -  
46 - it "executes multiple web hook" do  
47 - @project_hook.should_receive(:async_execute).once  
48 - @project_hook_2.should_receive(:async_execute).once  
49 -  
50 - project.trigger_post_receive('oldrev', 'newrev', 'refs/heads/master', @user)  
51 - end  
52 - end  
53 -  
54 - context "does not execute web hooks" do  
55 - before do  
56 - @project_hook = create(:project_hook)  
57 - project.hooks << [@project_hook]  
58 - end  
59 -  
60 - it "when pushing a branch for the first time" do  
61 - @project_hook.should_not_receive(:execute)  
62 - project.trigger_post_receive('00000000000000000000000000000000', 'newrev', 'refs/heads/master', @user)  
63 - end  
64 -  
65 - it "when pushing tags" do  
66 - @project_hook.should_not_receive(:execute)  
67 - project.trigger_post_receive('oldrev', 'newrev', 'refs/tags/v1.0.0', @user)  
68 - end  
69 - end  
70 -  
71 - context "when pushing new branches" do  
72 -  
73 - end  
74 -  
75 - context "when gathering commit data" do  
76 - before do  
77 - @oldrev, @newrev, @ref = project.repository.fresh_commits(2).last.sha,  
78 - project.repository.fresh_commits(2).first.sha, 'refs/heads/master'  
79 - @commit = project.repository.fresh_commits(2).first  
80 -  
81 - # Fill nil/empty attributes  
82 - project.description = "This is a description"  
83 -  
84 - @data = project.post_receive_data(@oldrev, @newrev, @ref, @user)  
85 - end  
86 -  
87 - subject { @data }  
88 -  
89 - it { should include(before: @oldrev) }  
90 - it { should include(after: @newrev) }  
91 - it { should include(ref: @ref) }  
92 - it { should include(user_id: project.owner.id) }  
93 - it { should include(user_name: project.owner.name) }  
94 -  
95 - context "with repository data" do  
96 - subject { @data[:repository] }  
97 -  
98 - it { should include(name: project.name) }  
99 - it { should include(url: project.url_to_repo) }  
100 - it { should include(description: project.description) }  
101 - it { should include(homepage: project.web_url) }  
102 - end  
103 -  
104 - context "with commits" do  
105 - subject { @data[:commits] }  
106 -  
107 - it { should be_an(Array) }  
108 - it { should have(1).element }  
109 -  
110 - context "the commit" do  
111 - subject { @data[:commits].first }  
112 -  
113 - it { should include(id: @commit.id) }  
114 - it { should include(message: @commit.safe_message) }  
115 - it { should include(timestamp: @commit.date.xmlschema) }  
116 - it { should include(url: "#{Gitlab.config.gitlab.url}/#{project.code}/commit/#{@commit.id}") }  
117 -  
118 - context "with a author" do  
119 - subject { @data[:commits].first[:author] }  
120 -  
121 - it { should include(name: @commit.author_name) }  
122 - it { should include(email: @commit.author_email) }  
123 - end  
124 - end  
125 - end  
126 - end  
127 - end  
128 -end  
spec/models/project_spec.rb
@@ -72,11 +72,8 @@ describe Project do @@ -72,11 +72,8 @@ describe Project do
72 it { should respond_to(:url_to_repo) } 72 it { should respond_to(:url_to_repo) }
73 it { should respond_to(:repo_exists?) } 73 it { should respond_to(:repo_exists?) }
74 it { should respond_to(:satellite) } 74 it { should respond_to(:satellite) }
75 - it { should respond_to(:observe_push) }  
76 it { should respond_to(:update_merge_requests) } 75 it { should respond_to(:update_merge_requests) }
77 it { should respond_to(:execute_hooks) } 76 it { should respond_to(:execute_hooks) }
78 - it { should respond_to(:post_receive_data) }  
79 - it { should respond_to(:trigger_post_receive) }  
80 it { should respond_to(:transfer) } 77 it { should respond_to(:transfer) }
81 it { should respond_to(:name_with_namespace) } 78 it { should respond_to(:name_with_namespace) }
82 it { should respond_to(:namespace_owner) } 79 it { should respond_to(:namespace_owner) }
spec/services/git_push_service.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 +