Commit 5f1038c8 authored by Nikolay Novikov's avatar Nikolay Novikov Committed by Fabio Pitino

Skip TeamCity on branch delete and add MR trigger

This is a take over from a community contribution:
https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20990

Skip TeamCity push event when related to branch delete

Support merge request events for TeamCity service.
Add checkbox for merge request triggers in the TeamCity
configuration page.
parent 671d7cdc
...@@ -19,6 +19,25 @@ class TeamcityService < CiService ...@@ -19,6 +19,25 @@ class TeamcityService < CiService
after_save :compose_service_hook, if: :activated? after_save :compose_service_hook, if: :activated?
before_update :reset_password before_update :reset_password
class << self
def to_param
'teamcity'
end
def supported_events
%w(push merge_request)
end
def event_description(event)
case event
when 'push', 'push_events'
'TeamCity CI will be triggered after every push to the repository except branch delete'
when 'merge_request', 'merge_request_events'
'TeamCity CI will be triggered after a merge request has been created or updated'
end
end
end
def compose_service_hook def compose_service_hook
hook = service_hook || build_service_hook hook = service_hook || build_service_hook
hook.save hook.save
...@@ -43,10 +62,6 @@ class TeamcityService < CiService ...@@ -43,10 +62,6 @@ class TeamcityService < CiService
'requests build, that setting is in the vsc root advanced settings.' 'requests build, that setting is in the vsc root advanced settings.'
end end
def self.to_param
'teamcity'
end
def fields def fields
[ [
{ type: 'text', name: 'teamcity_url', { type: 'text', name: 'teamcity_url',
...@@ -76,21 +91,14 @@ class TeamcityService < CiService ...@@ -76,21 +91,14 @@ class TeamcityService < CiService
def execute(data) def execute(data)
return unless supported_events.include?(data[:object_kind]) return unless supported_events.include?(data[:object_kind])
auth = { case data[:object_kind]
username: username, when 'push'
password: password
}
branch = Gitlab::Git.ref_name(data[:ref]) branch = Gitlab::Git.ref_name(data[:ref])
post_to_build_queue(data, branch) if push_valid?(data)
Gitlab::HTTP.post( when 'merge_request'
build_url('httpAuth/app/rest/buildQueue'), branch = data[:object_attributes][:source_branch]
body: "<build branchName=\"#{branch}\">"\ post_to_build_queue(data, branch) if merge_request_valid?(data)
"<buildType id=\"#{build_type}\"/>"\ end
'</build>',
headers: { 'Content-type' => 'application/xml' },
basic_auth: auth
)
end end
private private
...@@ -134,10 +142,44 @@ class TeamcityService < CiService ...@@ -134,10 +142,44 @@ class TeamcityService < CiService
end end
def get_path(path) def get_path(path)
Gitlab::HTTP.get(build_url(path), verify: false, Gitlab::HTTP.get(build_url(path), verify: false, basic_auth: basic_auth)
basic_auth: { end
username: username,
password: password def post_to_build_queue(data, branch)
}) Gitlab::HTTP.post(
build_url('httpAuth/app/rest/buildQueue'),
body: "<build branchName=#{branch.encode(xml: :attr)}>"\
"<buildType id=#{build_type.encode(xml: :attr)}/>"\
'</build>',
headers: { 'Content-type' => 'application/xml' },
basic_auth: basic_auth
)
end
def basic_auth
{ username: username, password: password }
end
def push_valid?(data)
data[:total_commits_count] > 0 &&
!branch_removed?(data) &&
no_open_merge_requests?(data)
end
def merge_request_valid?(data)
data.dig(:object_attributes, :state) == 'opened' &&
MergeRequest.state_machines[:merge_status].check_state?(data.dig(:object_attributes, :merge_status))
end
def branch_removed?(data)
Gitlab::Git.blank_ref?(data[:after])
end
def no_open_merge_requests?(data)
!project.merge_requests
.opened
.from_project(project)
.from_source_branches(Gitlab::Git.ref_name(data[:ref]))
.exists?
end end
end end
---
title: Protect TeamCity builds from triggering when a branch has been deleted. And a MR-option
merge_request: 29836
author: Nikolay Novikov
type: fixed
...@@ -15,6 +15,8 @@ describe 'User activates JetBrains TeamCity CI' do ...@@ -15,6 +15,8 @@ describe 'User activates JetBrains TeamCity CI' do
it 'activates service' do it 'activates service' do
check('Active') check('Active')
check('Push')
check('Merge request')
fill_in('Teamcity url', with: 'http://teamcity.example.com') fill_in('Teamcity url', with: 'http://teamcity.example.com')
fill_in('Build type', with: 'GitlabTest_Build') fill_in('Build type', with: 'GitlabTest_Build')
fill_in('Username', with: 'user') fill_in('Username', with: 'user')
......
...@@ -207,6 +207,91 @@ describe TeamcityService, :use_clean_rails_memory_store_caching do ...@@ -207,6 +207,91 @@ describe TeamcityService, :use_clean_rails_memory_store_caching do
end end
end end
describe '#execute' do
context 'when push' do
let(:data) do
{
object_kind: 'push',
ref: 'refs/heads/dev-123_branch',
after: '0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e',
total_commits_count: 1
}
end
it 'handles push request correctly' do
stub_post_to_build_queue(branch: 'dev-123_branch')
expect(service.execute(data)).to include('Ok')
end
it 'returns nil when ref is blank' do
data[:after] = "0000000000000000000000000000000000000000"
expect(service.execute(data)).to be_nil
end
it 'returns nil when there is no content' do
data[:total_commits_count] = 0
expect(service.execute(data)).to be_nil
end
end
context 'when merge_request' do
let(:data) do
{
object_kind: 'merge_request',
ref: 'refs/heads/dev-123_branch',
after: '0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e',
total_commits_count: 1,
object_attributes: {
state: 'opened',
source_branch: 'dev-123_branch',
merge_status: 'unchecked'
}
}
end
it 'handles merge request correctly' do
stub_post_to_build_queue(branch: 'dev-123_branch')
expect(service.execute(data)).to include('Ok')
end
it 'returns nil when merge request is not opened' do
data[:object_attributes][:state] = 'closed'
expect(service.execute(data)).to be_nil
end
it 'returns nil when merge request is not unchecked or cannot_be_merged_recheck' do
data[:object_attributes][:merge_status] = 'checked'
expect(service.execute(data)).to be_nil
end
end
it 'returns nil when event is not supported' do
data = { object_kind: 'foo' }
expect(service.execute(data)).to be_nil
end
end
def stub_post_to_build_queue(branch:)
teamcity_full_url = 'http://gitlab.com/teamcity/httpAuth/app/rest/buildQueue'
body ||= %Q(<build branchName=\"#{branch}\"><buildType id=\"foo\"/></build>)
auth = %w(mic password)
stub_full_request(teamcity_full_url, method: :post).with(
basic_auth: auth,
body: body,
headers: {
'Content-Type' => 'application/xml'
}
).to_return(status: 200, body: 'Ok', headers: {})
end
def stub_request(status: 200, body: nil, build_status: 'success') def stub_request(status: 200, body: nil, build_status: 'success')
teamcity_full_url = 'http://gitlab.com/teamcity/httpAuth/app/rest/builds/branch:unspecified:any,revision:123' teamcity_full_url = 'http://gitlab.com/teamcity/httpAuth/app/rest/builds/branch:unspecified:any,revision:123'
auth = %w(mic password) auth = %w(mic password)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment