Move git post push logic to service
This commit is contained in:
parent
9611640e38
commit
7bab81b199
6 changed files with 235 additions and 240 deletions
|
@ -295,53 +295,6 @@ class Project < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# This method will be called after each post receive and only if the provided
|
|
||||||
# user is present in GitLab.
|
|
||||||
#
|
|
||||||
# All callbacks for post receive should be placed here.
|
|
||||||
def trigger_post_receive(oldrev, newrev, ref, user)
|
|
||||||
data = post_receive_data(oldrev, newrev, ref, user)
|
|
||||||
|
|
||||||
# Create satellite
|
|
||||||
self.satellite.create unless self.satellite.exists?
|
|
||||||
|
|
||||||
# Create push event
|
|
||||||
self.observe_push(data)
|
|
||||||
|
|
||||||
if push_to_branch? ref, oldrev
|
|
||||||
# Close merged MR
|
|
||||||
self.update_merge_requests(oldrev, newrev, ref, user)
|
|
||||||
|
|
||||||
# Execute web hooks
|
|
||||||
self.execute_hooks(data.dup)
|
|
||||||
|
|
||||||
# Execute project services
|
|
||||||
self.execute_services(data.dup)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Discover the default branch, but only if it hasn't already been set to
|
|
||||||
# something else
|
|
||||||
if repository && default_branch.nil?
|
|
||||||
update_attributes(default_branch: self.repository.discover_default_branch)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def push_to_branch? ref, oldrev
|
|
||||||
ref_parts = ref.split('/')
|
|
||||||
|
|
||||||
# Return if this is not a push to a branch (e.g. new commits)
|
|
||||||
!(ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000")
|
|
||||||
end
|
|
||||||
|
|
||||||
def observe_push(data)
|
|
||||||
Event.create(
|
|
||||||
project: self,
|
|
||||||
action: Event::PUSHED,
|
|
||||||
data: data,
|
|
||||||
author_id: data[:user_id]
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def execute_hooks(data)
|
def execute_hooks(data)
|
||||||
hooks.each { |hook| hook.async_execute(data) }
|
hooks.each { |hook| hook.async_execute(data) }
|
||||||
end
|
end
|
||||||
|
@ -354,68 +307,12 @@ class Project < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Produce a hash of post-receive data
|
def discover_default_branch
|
||||||
#
|
# Discover the default branch, but only if it hasn't already been set to
|
||||||
# data = {
|
# something else
|
||||||
# before: String,
|
if repository && default_branch.nil?
|
||||||
# after: String,
|
update_attributes(default_branch: self.repository.discover_default_branch)
|
||||||
# ref: String,
|
|
||||||
# user_id: String,
|
|
||||||
# user_name: String,
|
|
||||||
# repository: {
|
|
||||||
# name: String,
|
|
||||||
# url: String,
|
|
||||||
# description: String,
|
|
||||||
# homepage: String,
|
|
||||||
# },
|
|
||||||
# commits: Array,
|
|
||||||
# total_commits_count: Fixnum
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
def post_receive_data(oldrev, newrev, ref, user)
|
|
||||||
|
|
||||||
push_commits = repository.commits_between(oldrev, newrev)
|
|
||||||
|
|
||||||
# Total commits count
|
|
||||||
push_commits_count = push_commits.size
|
|
||||||
|
|
||||||
# Get latest 20 commits ASC
|
|
||||||
push_commits_limited = push_commits.last(20)
|
|
||||||
|
|
||||||
# Hash to be passed as post_receive_data
|
|
||||||
data = {
|
|
||||||
before: oldrev,
|
|
||||||
after: newrev,
|
|
||||||
ref: ref,
|
|
||||||
user_id: user.id,
|
|
||||||
user_name: user.name,
|
|
||||||
repository: {
|
|
||||||
name: name,
|
|
||||||
url: url_to_repo,
|
|
||||||
description: description,
|
|
||||||
homepage: web_url,
|
|
||||||
},
|
|
||||||
commits: [],
|
|
||||||
total_commits_count: push_commits_count
|
|
||||||
}
|
|
||||||
|
|
||||||
# For perfomance purposes maximum 20 latest commits
|
|
||||||
# will be passed as post receive hook data.
|
|
||||||
#
|
|
||||||
push_commits_limited.each do |commit|
|
|
||||||
data[:commits] << {
|
|
||||||
id: commit.id,
|
|
||||||
message: commit.safe_message,
|
|
||||||
timestamp: commit.date.xmlschema,
|
|
||||||
url: "#{Gitlab.config.gitlab.url}/#{path_with_namespace}/commit/#{commit.id}",
|
|
||||||
author: {
|
|
||||||
name: commit.author_name,
|
|
||||||
email: commit.author_email
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
data
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_merge_requests(oldrev, newrev, ref, user)
|
def update_merge_requests(oldrev, newrev, ref, user)
|
||||||
|
@ -446,6 +343,10 @@ class Project < ActiveRecord::Base
|
||||||
!repository || repository.empty?
|
!repository || repository.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def ensure_satellite_exists
|
||||||
|
self.satellite.create unless self.satellite.exists?
|
||||||
|
end
|
||||||
|
|
||||||
def satellite
|
def satellite
|
||||||
@satellite ||= Gitlab::Satellite::Satellite.new(self)
|
@satellite ||= Gitlab::Satellite::Satellite.new(self)
|
||||||
end
|
end
|
||||||
|
|
114
app/services/git_push_service.rb
Normal file
114
app/services/git_push_service.rb
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
class GitPushService
|
||||||
|
attr_accessor :project, :user, :push_data
|
||||||
|
|
||||||
|
# This method will be called after each git update
|
||||||
|
# and only if the provided user and project is present in GitLab.
|
||||||
|
#
|
||||||
|
# All callbacks for post receive action should be placed here.
|
||||||
|
#
|
||||||
|
# Now this method do next:
|
||||||
|
# 1. Ensure project satellite exists
|
||||||
|
# 2. Update merge requests
|
||||||
|
# 3. Execute project web hooks
|
||||||
|
# 4. Execute project services
|
||||||
|
# 5. Create Push Event
|
||||||
|
#
|
||||||
|
def execute(project, user, oldrev, newrev, ref)
|
||||||
|
@project, @user = project, user
|
||||||
|
|
||||||
|
# Collect data for this git push
|
||||||
|
@push_data = post_receive_data(oldrev, newrev, ref)
|
||||||
|
|
||||||
|
project.ensure_satellite_exists
|
||||||
|
project.discover_default_branch
|
||||||
|
|
||||||
|
if push_to_branch?(ref, oldrev)
|
||||||
|
project.update_merge_requests(oldrev, newrev, ref, @user)
|
||||||
|
project.execute_hooks(@push_data.dup)
|
||||||
|
project.execute_services(@push_data.dup)
|
||||||
|
end
|
||||||
|
|
||||||
|
create_push_event
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def create_push_event
|
||||||
|
Event.create(
|
||||||
|
project: project,
|
||||||
|
action: Event::PUSHED,
|
||||||
|
data: push_data,
|
||||||
|
author_id: push_data[:user_id]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Produce a hash of post-receive data
|
||||||
|
#
|
||||||
|
# data = {
|
||||||
|
# before: String,
|
||||||
|
# after: String,
|
||||||
|
# ref: String,
|
||||||
|
# user_id: String,
|
||||||
|
# user_name: String,
|
||||||
|
# repository: {
|
||||||
|
# name: String,
|
||||||
|
# url: String,
|
||||||
|
# description: String,
|
||||||
|
# homepage: String,
|
||||||
|
# },
|
||||||
|
# commits: Array,
|
||||||
|
# total_commits_count: Fixnum
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
def post_receive_data(oldrev, newrev, ref)
|
||||||
|
push_commits = project.repository.commits_between(oldrev, newrev)
|
||||||
|
|
||||||
|
# Total commits count
|
||||||
|
push_commits_count = push_commits.size
|
||||||
|
|
||||||
|
# Get latest 20 commits ASC
|
||||||
|
push_commits_limited = push_commits.last(20)
|
||||||
|
|
||||||
|
# Hash to be passed as post_receive_data
|
||||||
|
data = {
|
||||||
|
before: oldrev,
|
||||||
|
after: newrev,
|
||||||
|
ref: ref,
|
||||||
|
user_id: user.id,
|
||||||
|
user_name: user.name,
|
||||||
|
repository: {
|
||||||
|
name: project.name,
|
||||||
|
url: project.url_to_repo,
|
||||||
|
description: project.description,
|
||||||
|
homepage: project.web_url,
|
||||||
|
},
|
||||||
|
commits: [],
|
||||||
|
total_commits_count: push_commits_count
|
||||||
|
}
|
||||||
|
|
||||||
|
# For perfomance purposes maximum 20 latest commits
|
||||||
|
# will be passed as post receive hook data.
|
||||||
|
#
|
||||||
|
push_commits_limited.each do |commit|
|
||||||
|
data[:commits] << {
|
||||||
|
id: commit.id,
|
||||||
|
message: commit.safe_message,
|
||||||
|
timestamp: commit.date.xmlschema,
|
||||||
|
url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/#{commit.id}",
|
||||||
|
author: {
|
||||||
|
name: commit.author_name,
|
||||||
|
email: commit.author_email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
data
|
||||||
|
end
|
||||||
|
|
||||||
|
def push_to_branch? ref, oldrev
|
||||||
|
ref_parts = ref.split('/')
|
||||||
|
|
||||||
|
# Return if this is not a push to a branch (e.g. new commits)
|
||||||
|
!(ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000")
|
||||||
|
end
|
||||||
|
end
|
|
@ -42,6 +42,6 @@ class PostReceive
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
project.trigger_post_receive(oldrev, newrev, ref, user)
|
GitPushService.new.execute(project, user, oldrev, newrev, ref)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,128 +0,0 @@
|
||||||
require 'spec_helper'
|
|
||||||
|
|
||||||
describe Project, "Hooks" do
|
|
||||||
let(:project) { create(:project) }
|
|
||||||
|
|
||||||
before do
|
|
||||||
@key = create(:key, user: project.owner)
|
|
||||||
@user = @key.user
|
|
||||||
@key_id = @key.identifier
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "Post Receive Event" do
|
|
||||||
it "should create push event" do
|
|
||||||
oldrev, newrev, ref = '00000000000000000000000000000000', 'newrev', 'refs/heads/master'
|
|
||||||
data = project.post_receive_data(oldrev, newrev, ref, @user)
|
|
||||||
|
|
||||||
project.observe_push(data)
|
|
||||||
event = Event.last
|
|
||||||
|
|
||||||
event.should_not be_nil
|
|
||||||
event.project.should == project
|
|
||||||
event.action.should == Event::PUSHED
|
|
||||||
event.data.should == data
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "Project hooks" do
|
|
||||||
context "with no web hooks" do
|
|
||||||
it "raises no errors" do
|
|
||||||
lambda {
|
|
||||||
project.execute_hooks({})
|
|
||||||
}.should_not raise_error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with web hooks" do
|
|
||||||
before do
|
|
||||||
@project_hook = create(:project_hook)
|
|
||||||
@project_hook_2 = create(:project_hook)
|
|
||||||
project.hooks << [@project_hook, @project_hook_2]
|
|
||||||
|
|
||||||
stub_request(:post, @project_hook.url)
|
|
||||||
stub_request(:post, @project_hook_2.url)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "executes multiple web hook" do
|
|
||||||
@project_hook.should_receive(:async_execute).once
|
|
||||||
@project_hook_2.should_receive(:async_execute).once
|
|
||||||
|
|
||||||
project.trigger_post_receive('oldrev', 'newrev', 'refs/heads/master', @user)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "does not execute web hooks" do
|
|
||||||
before do
|
|
||||||
@project_hook = create(:project_hook)
|
|
||||||
project.hooks << [@project_hook]
|
|
||||||
end
|
|
||||||
|
|
||||||
it "when pushing a branch for the first time" do
|
|
||||||
@project_hook.should_not_receive(:execute)
|
|
||||||
project.trigger_post_receive('00000000000000000000000000000000', 'newrev', 'refs/heads/master', @user)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "when pushing tags" do
|
|
||||||
@project_hook.should_not_receive(:execute)
|
|
||||||
project.trigger_post_receive('oldrev', 'newrev', 'refs/tags/v1.0.0', @user)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when pushing new branches" do
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when gathering commit data" do
|
|
||||||
before do
|
|
||||||
@oldrev, @newrev, @ref = project.repository.fresh_commits(2).last.sha,
|
|
||||||
project.repository.fresh_commits(2).first.sha, 'refs/heads/master'
|
|
||||||
@commit = project.repository.fresh_commits(2).first
|
|
||||||
|
|
||||||
# Fill nil/empty attributes
|
|
||||||
project.description = "This is a description"
|
|
||||||
|
|
||||||
@data = project.post_receive_data(@oldrev, @newrev, @ref, @user)
|
|
||||||
end
|
|
||||||
|
|
||||||
subject { @data }
|
|
||||||
|
|
||||||
it { should include(before: @oldrev) }
|
|
||||||
it { should include(after: @newrev) }
|
|
||||||
it { should include(ref: @ref) }
|
|
||||||
it { should include(user_id: project.owner.id) }
|
|
||||||
it { should include(user_name: project.owner.name) }
|
|
||||||
|
|
||||||
context "with repository data" do
|
|
||||||
subject { @data[:repository] }
|
|
||||||
|
|
||||||
it { should include(name: project.name) }
|
|
||||||
it { should include(url: project.url_to_repo) }
|
|
||||||
it { should include(description: project.description) }
|
|
||||||
it { should include(homepage: project.web_url) }
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with commits" do
|
|
||||||
subject { @data[:commits] }
|
|
||||||
|
|
||||||
it { should be_an(Array) }
|
|
||||||
it { should have(1).element }
|
|
||||||
|
|
||||||
context "the commit" do
|
|
||||||
subject { @data[:commits].first }
|
|
||||||
|
|
||||||
it { should include(id: @commit.id) }
|
|
||||||
it { should include(message: @commit.safe_message) }
|
|
||||||
it { should include(timestamp: @commit.date.xmlschema) }
|
|
||||||
it { should include(url: "#{Gitlab.config.gitlab.url}/#{project.code}/commit/#{@commit.id}") }
|
|
||||||
|
|
||||||
context "with a author" do
|
|
||||||
subject { @data[:commits].first[:author] }
|
|
||||||
|
|
||||||
it { should include(name: @commit.author_name) }
|
|
||||||
it { should include(email: @commit.author_email) }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -72,11 +72,8 @@ describe Project do
|
||||||
it { should respond_to(:url_to_repo) }
|
it { should respond_to(:url_to_repo) }
|
||||||
it { should respond_to(:repo_exists?) }
|
it { should respond_to(:repo_exists?) }
|
||||||
it { should respond_to(:satellite) }
|
it { should respond_to(:satellite) }
|
||||||
it { should respond_to(:observe_push) }
|
|
||||||
it { should respond_to(:update_merge_requests) }
|
it { should respond_to(:update_merge_requests) }
|
||||||
it { should respond_to(:execute_hooks) }
|
it { should respond_to(:execute_hooks) }
|
||||||
it { should respond_to(:post_receive_data) }
|
|
||||||
it { should respond_to(:trigger_post_receive) }
|
|
||||||
it { should respond_to(:transfer) }
|
it { should respond_to(:transfer) }
|
||||||
it { should respond_to(:name_with_namespace) }
|
it { should respond_to(:name_with_namespace) }
|
||||||
it { should respond_to(:namespace_owner) }
|
it { should respond_to(:namespace_owner) }
|
||||||
|
|
111
spec/services/git_push_service.rb
Normal file
111
spec/services/git_push_service.rb
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe GitPushService do
|
||||||
|
let (:user) { create :user }
|
||||||
|
let (:project) { create :project }
|
||||||
|
let (:service) { GitPushService.new }
|
||||||
|
|
||||||
|
before do
|
||||||
|
@oldrev = 'b98a310def241a6fd9c9a9a3e7934c48e498fe81'
|
||||||
|
@newrev = 'b19a04f53caeebf4fe5ec2327cb83e9253dc91bb'
|
||||||
|
@ref = 'refs/heads/master'
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Git Push Data" do
|
||||||
|
before do
|
||||||
|
service.execute(project, user, @oldrev, @newrev, @ref)
|
||||||
|
@push_data = service.push_data
|
||||||
|
@commit = project.repository.commit(@newrev)
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { @push_data }
|
||||||
|
|
||||||
|
it { should include(before: @oldrev) }
|
||||||
|
it { should include(after: @newrev) }
|
||||||
|
it { should include(ref: @ref) }
|
||||||
|
it { should include(user_id: user.id) }
|
||||||
|
it { should include(user_name: user.name) }
|
||||||
|
|
||||||
|
context "with repository data" do
|
||||||
|
subject { @push_data[:repository] }
|
||||||
|
|
||||||
|
it { should include(name: project.name) }
|
||||||
|
it { should include(url: project.url_to_repo) }
|
||||||
|
it { should include(description: project.description) }
|
||||||
|
it { should include(homepage: project.web_url) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with commits" do
|
||||||
|
subject { @push_data[:commits] }
|
||||||
|
|
||||||
|
it { should be_an(Array) }
|
||||||
|
it { should have(1).element }
|
||||||
|
|
||||||
|
context "the commit" do
|
||||||
|
subject { @push_data[:commits].first }
|
||||||
|
|
||||||
|
it { should include(id: @commit.id) }
|
||||||
|
it { should include(message: @commit.safe_message) }
|
||||||
|
it { should include(timestamp: @commit.date.xmlschema) }
|
||||||
|
it { should include(url: "#{Gitlab.config.gitlab.url}/#{project.code}/commit/#{@commit.id}") }
|
||||||
|
|
||||||
|
context "with a author" do
|
||||||
|
subject { @push_data[:commits].first[:author] }
|
||||||
|
|
||||||
|
it { should include(name: @commit.author_name) }
|
||||||
|
it { should include(email: @commit.author_email) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Push Event" do
|
||||||
|
before do
|
||||||
|
service.execute(project, user, @oldrev, @newrev, @ref)
|
||||||
|
@event = Event.last
|
||||||
|
end
|
||||||
|
|
||||||
|
it { @event.should_not be_nil }
|
||||||
|
it { @event.project.should == project }
|
||||||
|
it { @event.action.should == Event::PUSHED }
|
||||||
|
it { @event.data.should == service.push_data }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Web Hooks" do
|
||||||
|
context "with web hooks" do
|
||||||
|
before do
|
||||||
|
@project_hook = create(:project_hook)
|
||||||
|
@project_hook_2 = create(:project_hook)
|
||||||
|
project.hooks << [@project_hook, @project_hook_2]
|
||||||
|
|
||||||
|
stub_request(:post, @project_hook.url)
|
||||||
|
stub_request(:post, @project_hook_2.url)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "executes multiple web hook" do
|
||||||
|
@project_hook.should_receive(:async_execute).once
|
||||||
|
@project_hook_2.should_receive(:async_execute).once
|
||||||
|
|
||||||
|
service.execute(project, user, @oldrev, @newrev, @ref)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "does not execute web hooks" do
|
||||||
|
before do
|
||||||
|
@project_hook = create(:project_hook)
|
||||||
|
project.hooks << [@project_hook]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "when pushing a branch for the first time" do
|
||||||
|
@project_hook.should_not_receive(:execute)
|
||||||
|
service.execute(project, user, '00000000000000000000000000000000', 'newrev', 'refs/heads/master')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "when pushing tags" do
|
||||||
|
@project_hook.should_not_receive(:execute)
|
||||||
|
service.execute(project, user, 'newrev', 'newrev', 'refs/tags/v1.0.0')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
Loading…
Reference in a new issue