Commit 072887b0 authored by Fabio Pitino's avatar Fabio Pitino Committed by Alessio Caiazza

Fix merge request child pipelines

Allow child pipeline to run in the context of
merge requests
parent ad938b48
......@@ -186,7 +186,8 @@ module Ci
},
execute_params: {
ignore_skip_ci: true,
bridge: self
bridge: self,
merge_request: parent_pipeline.merge_request
}
}
end
......
......@@ -77,9 +77,7 @@ module Ci
validates :sha, presence: { unless: :importing? }
validates :ref, presence: { unless: :importing? }
validates :merge_request, presence: { if: :merge_request_event? }
validates :merge_request, absence: { unless: :merge_request_event? }
validates :tag, inclusion: { in: [false], if: :merge_request_event? }
validates :tag, inclusion: { in: [false], if: :merge_request? }
validates :external_pull_request, presence: { if: :external_pull_request_event? }
validates :external_pull_request, absence: { unless: :external_pull_request_event? }
......@@ -662,7 +660,7 @@ module Ci
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_SOURCE_BRANCH_SHA', value: source_sha.to_s)
variables.append(key: 'CI_MERGE_REQUEST_TARGET_BRANCH_SHA', value: target_sha.to_s)
......@@ -720,7 +718,7 @@ module Ci
# All the merge requests for which the current pipeline runs/ran against
def all_merge_requests
@all_merge_requests ||=
if merge_request_event?
if merge_request?
MergeRequest.where(id: merge_request_id)
else
MergeRequest.where(source_project_id: project_id, source_branch: ref)
......@@ -812,7 +810,7 @@ module Ci
# * nil: Modified path can not be evaluated
def modified_paths
strong_memoize(:modified_paths) do
if merge_request_event?
if merge_request?
merge_request.modified_paths
elsif branch_updated?
push_details.modified_paths
......@@ -836,12 +834,12 @@ module Ci
ref == project.default_branch
end
def triggered_by_merge_request?
merge_request_event? && merge_request_id.present?
def merge_request?
merge_request_id.present?
end
def detached_merge_request_pipeline?
triggered_by_merge_request? && target_sha.nil?
merge_request? && target_sha.nil?
end
def legacy_detached_merge_request_pipeline?
......@@ -849,7 +847,7 @@ module Ci
end
def merge_request_pipeline?
triggered_by_merge_request? && target_sha.present?
merge_request? && target_sha.present?
end
def merge_request_ref?
......@@ -865,7 +863,7 @@ module Ci
end
def source_ref
if triggered_by_merge_request?
if merge_request?
merge_request.source_branch
else
ref
......@@ -885,7 +883,7 @@ module Ci
end
def merge_request_event_type
return unless merge_request_event?
return unless merge_request?
strong_memoize(:merge_request_event_type) do
if merge_request_pipeline?
......@@ -918,7 +916,7 @@ module Ci
def git_ref
strong_memoize(:git_ref) do
if merge_request_event?
if merge_request?
##
# In the future, we're going to change this ref to
# merge request's merged reference, such as "refs/merge-requests/:iid/merge".
......
......@@ -11,7 +11,7 @@ module Ci
extend ActiveSupport::Concern
included do
delegate :merge_request_event?,
delegate :merge_request?,
:merge_request_ref?,
:legacy_detached_merge_request_pipeline?,
:merge_train_pipeline?, to: :pipeline
......
......@@ -7,7 +7,7 @@ module HasRef
extend ActiveSupport::Concern
def branch?
!tag? && !merge_request_event?
!tag? && !merge_request?
end
def git_ref
......
......@@ -1163,7 +1163,7 @@ class MergeRequest < ApplicationRecord
# Since deployments run on a merge request ref (e.g. `refs/merge-requests/:iid/head`),
# we cannot look up environments with source branch name.
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
end
......
......@@ -61,6 +61,8 @@ class MergeRequest::Pipelines
pipelines.joins(shas_table)
end
# NOTE: this method returns only parent merge request pipelines.
# Child merge request pipelines have a different source.
def triggered_by_merge_request
source_project.ci_pipelines
.where(source: :merge_request_event, merge_request: merge_request)
......
......@@ -116,7 +116,7 @@ module Ci
def merge_request_presenter
strong_memoize(:merge_request_presenter) do
if pipeline.triggered_by_merge_request?
if pipeline.merge_request?
pipeline.merge_request.present(current_user: current_user)
end
end
......
......@@ -25,7 +25,7 @@ class PipelineEntity < Grape::Entity
expose :flags do
expose :stuck?, as: :stuck
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 :can_retry?, as: :retryable
expose :can_cancel?, as: :cancelable
......@@ -59,11 +59,11 @@ class PipelineEntity < Grape::Entity
expose :tag?, as: :tag
expose :branch?, as: :branch
expose :merge_request_event?, as: :merge_request
expose :merge_request?, as: :merge_request
end
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 :target_sha, if: -> (pipeline, _) { pipeline.merge_request_pipeline? }
expose :yaml_errors, if: -> (pipeline, _) { pipeline.has_yaml_errors? }
......@@ -104,7 +104,7 @@ class PipelineEntity < Grape::Entity
end
def has_presentable_merge_request?
pipeline.triggered_by_merge_request? &&
pipeline.merge_request? &&
can?(request.current_user, :read_merge_request, pipeline.merge_request)
end
......
......@@ -24,7 +24,7 @@ module MergeRequests
##
# UpdateMergeRequestsWorker could be retried by an exception.
# 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?
true
......
---
title: Allow running child pipelines as merge request pipelines
merge_request: 23884
author:
type: fixed
......@@ -132,7 +132,7 @@ module EE
override :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
merge_train_pipeline? ? :merge_train : super
......
......@@ -36,7 +36,7 @@ describe MergeRequests::CreateService do
context 'report approvers' do
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
expect_next_instance_of(::MergeRequests::SyncReportApproverApprovalRules) do |service|
......
......@@ -33,7 +33,7 @@ describe Ci::Build do
it { is_expected.to respond_to(:has_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(:legacy_detached_merge_request_pipeline?).to(:pipeline) }
......
......@@ -162,6 +162,23 @@ describe Ci::Pipeline, :mailer do
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
subject { pipeline.detached_merge_request_pipeline? }
......@@ -367,48 +384,6 @@ describe Ci::Pipeline, :mailer do
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
it_behaves_like 'AtomicInternalId', validate_presence: false do
let(:internal_id_attribute) { :iid }
......@@ -612,9 +587,9 @@ describe Ci::Pipeline, :mailer do
]
end
context 'when source is merge request' do
context 'when pipeline is merge request' do
let(:pipeline) do
create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request)
create(:ci_pipeline, merge_request: merge_request)
end
let(:merge_request) do
......@@ -651,7 +626,7 @@ describe Ci::Pipeline, :mailer do
'CI_MERGE_REQUEST_TITLE' => merge_request.title,
'CI_MERGE_REQUEST_ASSIGNEES' => merge_request.assignee_username_list,
'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)
end
......@@ -1263,9 +1238,9 @@ describe Ci::Pipeline, :mailer do
is_expected.to be_truthy
end
context 'when source is merge request' do
context 'when pipeline is merge request' do
let(:pipeline) do
create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request)
create(:ci_pipeline, merge_request: merge_request)
end
let(:merge_request) do
......
......@@ -148,6 +148,12 @@ describe Ci::CreateCrossProjectPipelineService, '#execute' do
end
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
it 'creates only one new pipeline' do
expect { service.execute(bridge) }
......@@ -189,9 +195,6 @@ describe Ci::CreateCrossProjectPipelineService, '#execute' do
end
before do
file_content = YAML.dump(
rspec: { script: 'rspec' },
echo: { script: 'echo' })
upstream_project.repository.create_file(
user, 'child-pipeline.yml', file_content, message: 'message', branch_name: 'master')
......@@ -218,6 +221,29 @@ describe Ci::CreateCrossProjectPipelineService, '#execute' do
it_behaves_like 'creates a child pipeline'
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
let!(:pipeline_source) do
create(:ci_sources_pipeline,
......
......@@ -1473,15 +1473,6 @@ describe Ci::CreatePipelineService do
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
context "when config does not have merge_requests keywords" do
......@@ -1518,17 +1509,6 @@ describe Ci::CreatePipelineService do
.to eq(['No stages / jobs for this pipeline.'])
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
context "when config uses regular expression for only keyword" do
......@@ -1623,6 +1603,7 @@ describe Ci::CreatePipelineService do
context 'when source is web' do
let(:source) { :web }
let(:merge_request) { nil }
context "when config has merge_requests keywords" do
let(:config) do
......@@ -1644,30 +1625,11 @@ describe Ci::CreatePipelineService do
}
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
expect(pipeline).to be_persisted
expect(pipeline).to be_web
expect(pipeline.merge_request).to be_nil
expect(pipeline.builds.order(:stage_id).pluck(:name)).to eq(%w[build pages])
end
it 'creates a branch pipeline' do
expect(pipeline).to be_persisted
expect(pipeline).to be_web
expect(pipeline.merge_request).to be_nil
expect(pipeline.builds.order(:stage_id).pluck(:name)).to eq(%w[build pages])
end
end
end
......
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