Commit 24012a7e authored by Alessio Caiazza's avatar Alessio Caiazza

Merge branch 'fix-child-parent-pipeline-for-mr' into 'master'

Fix merge request child pipelines

See merge request gitlab-org/gitlab!23884
parents 3ace1e66 072887b0
...@@ -186,7 +186,8 @@ module Ci ...@@ -186,7 +186,8 @@ module Ci
}, },
execute_params: { execute_params: {
ignore_skip_ci: true, ignore_skip_ci: true,
bridge: self bridge: self,
merge_request: parent_pipeline.merge_request
} }
} }
end end
......
...@@ -77,9 +77,7 @@ module Ci ...@@ -77,9 +77,7 @@ module Ci
validates :sha, presence: { unless: :importing? } validates :sha, presence: { unless: :importing? }
validates :ref, presence: { unless: :importing? } validates :ref, presence: { unless: :importing? }
validates :merge_request, presence: { if: :merge_request_event? } validates :tag, inclusion: { in: [false], if: :merge_request? }
validates :merge_request, absence: { unless: :merge_request_event? }
validates :tag, inclusion: { in: [false], if: :merge_request_event? }
validates :external_pull_request, presence: { if: :external_pull_request_event? } validates :external_pull_request, presence: { if: :external_pull_request_event? }
validates :external_pull_request, absence: { unless: :external_pull_request_event? } validates :external_pull_request, absence: { unless: :external_pull_request_event? }
...@@ -662,7 +660,7 @@ module Ci ...@@ -662,7 +660,7 @@ module Ci
variables.concat(predefined_commit_variables) variables.concat(predefined_commit_variables)
if merge_request_event? && merge_request if merge_request?
variables.append(key: 'CI_MERGE_REQUEST_EVENT_TYPE', value: merge_request_event_type.to_s) variables.append(key: 'CI_MERGE_REQUEST_EVENT_TYPE', value: merge_request_event_type.to_s)
variables.append(key: 'CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', value: source_sha.to_s) variables.append(key: 'CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', value: source_sha.to_s)
variables.append(key: 'CI_MERGE_REQUEST_TARGET_BRANCH_SHA', value: target_sha.to_s) variables.append(key: 'CI_MERGE_REQUEST_TARGET_BRANCH_SHA', value: target_sha.to_s)
...@@ -720,7 +718,7 @@ module Ci ...@@ -720,7 +718,7 @@ module Ci
# All the merge requests for which the current pipeline runs/ran against # All the merge requests for which the current pipeline runs/ran against
def all_merge_requests def all_merge_requests
@all_merge_requests ||= @all_merge_requests ||=
if merge_request_event? if merge_request?
MergeRequest.where(id: merge_request_id) MergeRequest.where(id: merge_request_id)
else else
MergeRequest.where(source_project_id: project_id, source_branch: ref) MergeRequest.where(source_project_id: project_id, source_branch: ref)
...@@ -812,7 +810,7 @@ module Ci ...@@ -812,7 +810,7 @@ module Ci
# * nil: Modified path can not be evaluated # * nil: Modified path can not be evaluated
def modified_paths def modified_paths
strong_memoize(:modified_paths) do strong_memoize(:modified_paths) do
if merge_request_event? if merge_request?
merge_request.modified_paths merge_request.modified_paths
elsif branch_updated? elsif branch_updated?
push_details.modified_paths push_details.modified_paths
...@@ -836,12 +834,12 @@ module Ci ...@@ -836,12 +834,12 @@ module Ci
ref == project.default_branch ref == project.default_branch
end end
def triggered_by_merge_request? def merge_request?
merge_request_event? && merge_request_id.present? merge_request_id.present?
end end
def detached_merge_request_pipeline? def detached_merge_request_pipeline?
triggered_by_merge_request? && target_sha.nil? merge_request? && target_sha.nil?
end end
def legacy_detached_merge_request_pipeline? def legacy_detached_merge_request_pipeline?
...@@ -849,7 +847,7 @@ module Ci ...@@ -849,7 +847,7 @@ module Ci
end end
def merge_request_pipeline? def merge_request_pipeline?
triggered_by_merge_request? && target_sha.present? merge_request? && target_sha.present?
end end
def merge_request_ref? def merge_request_ref?
...@@ -865,7 +863,7 @@ module Ci ...@@ -865,7 +863,7 @@ module Ci
end end
def source_ref def source_ref
if triggered_by_merge_request? if merge_request?
merge_request.source_branch merge_request.source_branch
else else
ref ref
...@@ -885,7 +883,7 @@ module Ci ...@@ -885,7 +883,7 @@ module Ci
end end
def merge_request_event_type def merge_request_event_type
return unless merge_request_event? return unless merge_request?
strong_memoize(:merge_request_event_type) do strong_memoize(:merge_request_event_type) do
if merge_request_pipeline? if merge_request_pipeline?
...@@ -918,7 +916,7 @@ module Ci ...@@ -918,7 +916,7 @@ module Ci
def git_ref def git_ref
strong_memoize(:git_ref) do strong_memoize(:git_ref) do
if merge_request_event? if merge_request?
## ##
# In the future, we're going to change this ref to # In the future, we're going to change this ref to
# merge request's merged reference, such as "refs/merge-requests/:iid/merge". # merge request's merged reference, such as "refs/merge-requests/:iid/merge".
......
...@@ -11,7 +11,7 @@ module Ci ...@@ -11,7 +11,7 @@ module Ci
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
delegate :merge_request_event?, delegate :merge_request?,
:merge_request_ref?, :merge_request_ref?,
:legacy_detached_merge_request_pipeline?, :legacy_detached_merge_request_pipeline?,
:merge_train_pipeline?, to: :pipeline :merge_train_pipeline?, to: :pipeline
......
...@@ -7,7 +7,7 @@ module HasRef ...@@ -7,7 +7,7 @@ module HasRef
extend ActiveSupport::Concern extend ActiveSupport::Concern
def branch? def branch?
!tag? && !merge_request_event? !tag? && !merge_request?
end end
def git_ref def git_ref
......
...@@ -1163,7 +1163,7 @@ class MergeRequest < ApplicationRecord ...@@ -1163,7 +1163,7 @@ class MergeRequest < ApplicationRecord
# Since deployments run on a merge request ref (e.g. `refs/merge-requests/:iid/head`), # Since deployments run on a merge request ref (e.g. `refs/merge-requests/:iid/head`),
# we cannot look up environments with source branch name. # we cannot look up environments with source branch name.
def environments def environments
return Environment.none unless actual_head_pipeline&.triggered_by_merge_request? return Environment.none unless actual_head_pipeline&.merge_request?
actual_head_pipeline.environments actual_head_pipeline.environments
end end
......
...@@ -61,6 +61,8 @@ class MergeRequest::Pipelines ...@@ -61,6 +61,8 @@ class MergeRequest::Pipelines
pipelines.joins(shas_table) pipelines.joins(shas_table)
end end
# NOTE: this method returns only parent merge request pipelines.
# Child merge request pipelines have a different source.
def triggered_by_merge_request def triggered_by_merge_request
source_project.ci_pipelines source_project.ci_pipelines
.where(source: :merge_request_event, merge_request: merge_request) .where(source: :merge_request_event, merge_request: merge_request)
......
...@@ -116,7 +116,7 @@ module Ci ...@@ -116,7 +116,7 @@ module Ci
def merge_request_presenter def merge_request_presenter
strong_memoize(:merge_request_presenter) do strong_memoize(:merge_request_presenter) do
if pipeline.triggered_by_merge_request? if pipeline.merge_request?
pipeline.merge_request.present(current_user: current_user) pipeline.merge_request.present(current_user: current_user)
end end
end end
......
...@@ -25,7 +25,7 @@ class PipelineEntity < Grape::Entity ...@@ -25,7 +25,7 @@ class PipelineEntity < Grape::Entity
expose :flags do expose :flags do
expose :stuck?, as: :stuck expose :stuck?, as: :stuck
expose :auto_devops_source?, as: :auto_devops expose :auto_devops_source?, as: :auto_devops
expose :merge_request_event?, as: :merge_request expose :merge_request?, as: :merge_request
expose :has_yaml_errors?, as: :yaml_errors expose :has_yaml_errors?, as: :yaml_errors
expose :can_retry?, as: :retryable expose :can_retry?, as: :retryable
expose :can_cancel?, as: :cancelable expose :can_cancel?, as: :cancelable
...@@ -59,11 +59,11 @@ class PipelineEntity < Grape::Entity ...@@ -59,11 +59,11 @@ class PipelineEntity < Grape::Entity
expose :tag?, as: :tag expose :tag?, as: :tag
expose :branch?, as: :branch expose :branch?, as: :branch
expose :merge_request_event?, as: :merge_request expose :merge_request?, as: :merge_request
end end
expose :commit, using: CommitEntity expose :commit, using: CommitEntity
expose :merge_request_event_type, if: -> (pipeline, _) { pipeline.merge_request_event? } expose :merge_request_event_type, if: -> (pipeline, _) { pipeline.merge_request? }
expose :source_sha, if: -> (pipeline, _) { pipeline.merge_request_pipeline? } expose :source_sha, if: -> (pipeline, _) { pipeline.merge_request_pipeline? }
expose :target_sha, if: -> (pipeline, _) { pipeline.merge_request_pipeline? } expose :target_sha, if: -> (pipeline, _) { pipeline.merge_request_pipeline? }
expose :yaml_errors, if: -> (pipeline, _) { pipeline.has_yaml_errors? } expose :yaml_errors, if: -> (pipeline, _) { pipeline.has_yaml_errors? }
...@@ -104,7 +104,7 @@ class PipelineEntity < Grape::Entity ...@@ -104,7 +104,7 @@ class PipelineEntity < Grape::Entity
end end
def has_presentable_merge_request? def has_presentable_merge_request?
pipeline.triggered_by_merge_request? && pipeline.merge_request? &&
can?(request.current_user, :read_merge_request, pipeline.merge_request) can?(request.current_user, :read_merge_request, pipeline.merge_request)
end end
......
...@@ -24,7 +24,7 @@ module MergeRequests ...@@ -24,7 +24,7 @@ module MergeRequests
## ##
# UpdateMergeRequestsWorker could be retried by an exception. # UpdateMergeRequestsWorker could be retried by an exception.
# pipelines for merge request should not be recreated in such case. # pipelines for merge request should not be recreated in such case.
return false if !allow_duplicate && merge_request.find_actual_head_pipeline&.triggered_by_merge_request? return false if !allow_duplicate && merge_request.find_actual_head_pipeline&.merge_request?
return false if merge_request.has_no_commits? return false if merge_request.has_no_commits?
true true
......
---
title: Allow running child pipelines as merge request pipelines
merge_request: 23884
author:
type: fixed
...@@ -132,7 +132,7 @@ module EE ...@@ -132,7 +132,7 @@ module EE
override :merge_request_event_type override :merge_request_event_type
def merge_request_event_type def merge_request_event_type
return unless merge_request_event? return unless merge_request?
strong_memoize(:merge_request_event_type) do strong_memoize(:merge_request_event_type) do
merge_train_pipeline? ? :merge_train : super merge_train_pipeline? ? :merge_train : super
......
...@@ -36,7 +36,7 @@ describe MergeRequests::CreateService do ...@@ -36,7 +36,7 @@ describe MergeRequests::CreateService do
context 'report approvers' do context 'report approvers' do
let(:sha) { project.repository.commits(opts[:source_branch], limit: 1).first.id } let(:sha) { project.repository.commits(opts[:source_branch], limit: 1).first.id }
let(:pipeline) { instance_double(Ci::Pipeline, id: 42, project_id: project.id, triggered_by_merge_request?: true) } let(:pipeline) { instance_double(Ci::Pipeline, id: 42, project_id: project.id, merge_request?: true) }
it 'refreshes report approvers for the merge request' do it 'refreshes report approvers for the merge request' do
expect_next_instance_of(::MergeRequests::SyncReportApproverApprovalRules) do |service| expect_next_instance_of(::MergeRequests::SyncReportApproverApprovalRules) do |service|
......
...@@ -33,7 +33,7 @@ describe Ci::Build do ...@@ -33,7 +33,7 @@ describe Ci::Build do
it { is_expected.to respond_to(:has_trace?) } it { is_expected.to respond_to(:has_trace?) }
it { is_expected.to respond_to(:trace) } it { is_expected.to respond_to(:trace) }
it { is_expected.to delegate_method(:merge_request_event?).to(:pipeline) } it { is_expected.to delegate_method(:merge_request?).to(:pipeline) }
it { is_expected.to delegate_method(:merge_request_ref?).to(:pipeline) } it { is_expected.to delegate_method(:merge_request_ref?).to(:pipeline) }
it { is_expected.to delegate_method(:legacy_detached_merge_request_pipeline?).to(:pipeline) } it { is_expected.to delegate_method(:legacy_detached_merge_request_pipeline?).to(:pipeline) }
......
...@@ -162,6 +162,23 @@ describe Ci::Pipeline, :mailer do ...@@ -162,6 +162,23 @@ describe Ci::Pipeline, :mailer do
end end
end end
describe '#merge_request?' do
let(:pipeline) { create(:ci_pipeline, merge_request: merge_request) }
let(:merge_request) { create(:merge_request) }
it 'returns true' do
expect(pipeline).to be_merge_request
end
context 'when merge request is nil' do
let(:merge_request) { nil }
it 'returns false' do
expect(pipeline).not_to be_merge_request
end
end
end
describe '#detached_merge_request_pipeline?' do describe '#detached_merge_request_pipeline?' do
subject { pipeline.detached_merge_request_pipeline? } subject { pipeline.detached_merge_request_pipeline? }
...@@ -367,48 +384,6 @@ describe Ci::Pipeline, :mailer do ...@@ -367,48 +384,6 @@ describe Ci::Pipeline, :mailer do
end end
end end
describe 'Validations for merge request pipelines' do
let(:pipeline) do
build(:ci_pipeline, source: source, merge_request: merge_request)
end
let(:merge_request) do
create(:merge_request,
source_project: project,
source_branch: 'feature',
target_project: project,
target_branch: 'master')
end
context 'when source is merge request' do
let(:source) { :merge_request_event }
context 'when merge request is specified' do
it { expect(pipeline).to be_valid }
end
context 'when merge request is empty' do
let(:merge_request) { nil }
it { expect(pipeline).not_to be_valid }
end
end
context 'when source is web' do
let(:source) { :web }
context 'when merge request is specified' do
it { expect(pipeline).not_to be_valid }
end
context 'when merge request is empty' do
let(:merge_request) { nil }
it { expect(pipeline).to be_valid }
end
end
end
describe 'modules' do describe 'modules' do
it_behaves_like 'AtomicInternalId', validate_presence: false do it_behaves_like 'AtomicInternalId', validate_presence: false do
let(:internal_id_attribute) { :iid } let(:internal_id_attribute) { :iid }
...@@ -612,9 +587,9 @@ describe Ci::Pipeline, :mailer do ...@@ -612,9 +587,9 @@ describe Ci::Pipeline, :mailer do
] ]
end end
context 'when source is merge request' do context 'when pipeline is merge request' do
let(:pipeline) do let(:pipeline) do
create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request) create(:ci_pipeline, merge_request: merge_request)
end end
let(:merge_request) do let(:merge_request) do
...@@ -651,7 +626,7 @@ describe Ci::Pipeline, :mailer do ...@@ -651,7 +626,7 @@ describe Ci::Pipeline, :mailer do
'CI_MERGE_REQUEST_TITLE' => merge_request.title, 'CI_MERGE_REQUEST_TITLE' => merge_request.title,
'CI_MERGE_REQUEST_ASSIGNEES' => merge_request.assignee_username_list, 'CI_MERGE_REQUEST_ASSIGNEES' => merge_request.assignee_username_list,
'CI_MERGE_REQUEST_MILESTONE' => milestone.title, 'CI_MERGE_REQUEST_MILESTONE' => milestone.title,
'CI_MERGE_REQUEST_LABELS' => labels.map(&:title).join(','), 'CI_MERGE_REQUEST_LABELS' => labels.map(&:title).sort.join(','),
'CI_MERGE_REQUEST_EVENT_TYPE' => pipeline.merge_request_event_type.to_s) 'CI_MERGE_REQUEST_EVENT_TYPE' => pipeline.merge_request_event_type.to_s)
end end
...@@ -1263,9 +1238,9 @@ describe Ci::Pipeline, :mailer do ...@@ -1263,9 +1238,9 @@ describe Ci::Pipeline, :mailer do
is_expected.to be_truthy is_expected.to be_truthy
end end
context 'when source is merge request' do context 'when pipeline is merge request' do
let(:pipeline) do let(:pipeline) do
create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request) create(:ci_pipeline, merge_request: merge_request)
end end
let(:merge_request) do let(:merge_request) do
......
...@@ -148,6 +148,12 @@ describe Ci::CreateCrossProjectPipelineService, '#execute' do ...@@ -148,6 +148,12 @@ describe Ci::CreateCrossProjectPipelineService, '#execute' do
end end
context 'when "include" is provided' do context 'when "include" is provided' do
let(:file_content) do
YAML.dump(
rspec: { script: 'rspec' },
echo: { script: 'echo' })
end
shared_examples 'creates a child pipeline' do shared_examples 'creates a child pipeline' do
it 'creates only one new pipeline' do it 'creates only one new pipeline' do
expect { service.execute(bridge) } expect { service.execute(bridge) }
...@@ -189,9 +195,6 @@ describe Ci::CreateCrossProjectPipelineService, '#execute' do ...@@ -189,9 +195,6 @@ describe Ci::CreateCrossProjectPipelineService, '#execute' do
end end
before do before do
file_content = YAML.dump(
rspec: { script: 'rspec' },
echo: { script: 'echo' })
upstream_project.repository.create_file( upstream_project.repository.create_file(
user, 'child-pipeline.yml', file_content, message: 'message', branch_name: 'master') user, 'child-pipeline.yml', file_content, message: 'message', branch_name: 'master')
...@@ -218,6 +221,29 @@ describe Ci::CreateCrossProjectPipelineService, '#execute' do ...@@ -218,6 +221,29 @@ describe Ci::CreateCrossProjectPipelineService, '#execute' do
it_behaves_like 'creates a child pipeline' it_behaves_like 'creates a child pipeline'
end end
context 'when the parent is a merge request pipeline' do
let(:merge_request) { create(:merge_request, source_project: bridge.project, target_project: bridge.project) }
let(:file_content) do
YAML.dump(
workflow: { rules: [{ if: '$CI_MERGE_REQUEST_ID' }] },
rspec: { script: 'rspec' },
echo: { script: 'echo' })
end
before do
bridge.pipeline.update!(source: :merge_request_event, merge_request: merge_request)
end
it_behaves_like 'creates a child pipeline'
it 'propagates the merge request to the child pipeline' do
pipeline = service.execute(bridge)
expect(pipeline.merge_request).to eq(merge_request)
expect(pipeline).to be_merge_request
end
end
context 'when upstream pipeline is a child pipeline' do context 'when upstream pipeline is a child pipeline' do
let!(:pipeline_source) do let!(:pipeline_source) do
create(:ci_sources_pipeline, create(:ci_sources_pipeline,
......
...@@ -1473,15 +1473,6 @@ describe Ci::CreatePipelineService do ...@@ -1473,15 +1473,6 @@ describe Ci::CreatePipelineService do
end end
end end
end end
context 'when merge request is not specified' do
let(:merge_request) { nil }
it 'does not create a detached merge request pipeline' do
expect(pipeline).not_to be_persisted
expect(pipeline.errors[:merge_request]).to eq(["can't be blank"])
end
end
end end
context "when config does not have merge_requests keywords" do context "when config does not have merge_requests keywords" do
...@@ -1518,17 +1509,6 @@ describe Ci::CreatePipelineService do ...@@ -1518,17 +1509,6 @@ describe Ci::CreatePipelineService do
.to eq(['No stages / jobs for this pipeline.']) .to eq(['No stages / jobs for this pipeline.'])
end end
end end
context 'when merge request is not specified' do
let(:merge_request) { nil }
it 'does not create a detached merge request pipeline' do
expect(pipeline).not_to be_persisted
expect(pipeline.errors[:base])
.to eq(['No stages / jobs for this pipeline.'])
end
end
end end
context "when config uses regular expression for only keyword" do context "when config uses regular expression for only keyword" do
...@@ -1623,6 +1603,7 @@ describe Ci::CreatePipelineService do ...@@ -1623,6 +1603,7 @@ describe Ci::CreatePipelineService do
context 'when source is web' do context 'when source is web' do
let(:source) { :web } let(:source) { :web }
let(:merge_request) { nil }
context "when config has merge_requests keywords" do context "when config has merge_requests keywords" do
let(:config) do let(:config) do
...@@ -1644,24 +1625,6 @@ describe Ci::CreatePipelineService do ...@@ -1644,24 +1625,6 @@ describe Ci::CreatePipelineService do
} }
end end
context 'when merge request is specified' do
let(:merge_request) do
create(:merge_request,
source_project: project,
source_branch: Gitlab::Git.ref_name(ref_name),
target_project: project,
target_branch: 'master')
end
it 'does not create a merge request pipeline' do
expect(pipeline).not_to be_persisted
expect(pipeline.errors[:merge_request]).to eq(["must be blank"])
end
end
context 'when merge request is not specified' do
let(:merge_request) { nil }
it 'creates a branch pipeline' do it 'creates a branch pipeline' do
expect(pipeline).to be_persisted expect(pipeline).to be_persisted
expect(pipeline).to be_web expect(pipeline).to be_web
...@@ -1671,7 +1634,6 @@ describe Ci::CreatePipelineService do ...@@ -1671,7 +1634,6 @@ describe Ci::CreatePipelineService do
end end
end end
end end
end
context 'when needs is used' do context 'when needs is used' do
let(:pipeline) { execute_service } let(:pipeline) { execute_service }
......
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