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 295 end
296 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 298 def execute_hooks(data)
346 299 hooks.each { |hook| hook.async_execute(data) }
347 300 end
... ... @@ -354,68 +307,12 @@ class Project < ActiveRecord::Base
354 307 end
355 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 315 end
417   -
418   - data
419 316 end
420 317  
421 318 def update_merge_requests(oldrev, newrev, ref, user)
... ... @@ -446,6 +343,10 @@ class Project &lt; ActiveRecord::Base
446 343 !repository || repository.empty?
447 344 end
448 345  
  346 + def ensure_satellite_exists
  347 + self.satellite.create unless self.satellite.exists?
  348 + end
  349 +
449 350 def satellite
450 351 @satellite ||= Gitlab::Satellite::Satellite.new(self)
451 352 end
... ...
app/services/git_push_service.rb 0 → 100644
... ... @@ -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 42 return false
43 43 end
44 44  
45   - project.trigger_post_receive(oldrev, newrev, ref, user)
  45 + GitPushService.new.execute(project, user, oldrev, newrev, ref)
46 46 end
47 47 end
... ...
spec/models/project_hooks_spec.rb
... ... @@ -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 72 it { should respond_to(:url_to_repo) }
73 73 it { should respond_to(:repo_exists?) }
74 74 it { should respond_to(:satellite) }
75   - it { should respond_to(:observe_push) }
76 75 it { should respond_to(:update_merge_requests) }
77 76 it { should respond_to(:execute_hooks) }
78   - it { should respond_to(:post_receive_data) }
79   - it { should respond_to(:trigger_post_receive) }
80 77 it { should respond_to(:transfer) }
81 78 it { should respond_to(:name_with_namespace) }
82 79 it { should respond_to(:namespace_owner) }
... ...
spec/services/git_push_service.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 +
... ...