Commit e73adda2 authored by Fabio Pitino's avatar Fabio Pitino

Filter out child pipelines in view

Fix include config handling

Define child and parent pipeline in model

* Define scopes for child and parent pipelines
* Define child? and parent? methods in Ci::Pipeline
* Expose the project in pipeline.json so that it can
  be compared with downstream and upstream pipelines

Refactor same_family_pipeline_ids
parent 0ea2d1de
...@@ -17,7 +17,7 @@ class PipelinesFinder ...@@ -17,7 +17,7 @@ class PipelinesFinder
return Ci::Pipeline.none return Ci::Pipeline.none
end end
items = pipelines items = pipelines.no_child
items = by_scope(items) items = by_scope(items)
items = by_status(items) items = by_status(items)
items = by_ref(items) items = by_ref(items)
......
...@@ -61,7 +61,9 @@ module Ci ...@@ -61,7 +61,9 @@ module Ci
has_one :chat_data, class_name: 'Ci::PipelineChatData' has_one :chat_data, class_name: 'Ci::PipelineChatData'
has_many :triggered_pipelines, through: :sourced_pipelines, source: :pipeline has_many :triggered_pipelines, through: :sourced_pipelines, source: :pipeline
has_many :child_pipelines, -> { merge(Ci::Sources::Pipeline.same_project) }, through: :sourced_pipelines, source: :pipeline
has_one :triggered_by_pipeline, through: :source_pipeline, source: :source_pipeline has_one :triggered_by_pipeline, through: :source_pipeline, source: :source_pipeline
has_one :parent_pipeline, -> { merge(Ci::Sources::Pipeline.same_project) }, through: :source_pipeline, source: :source_pipeline
has_one :source_job, through: :source_pipeline, source: :source_job has_one :source_job, through: :source_pipeline, source: :source_job
has_one :pipeline_config, class_name: 'Ci::PipelineConfig', inverse_of: :pipeline has_one :pipeline_config, class_name: 'Ci::PipelineConfig', inverse_of: :pipeline
...@@ -213,6 +215,7 @@ module Ci ...@@ -213,6 +215,7 @@ module Ci
end end
scope :internal, -> { where(source: internal_sources) } scope :internal, -> { where(source: internal_sources) }
scope :no_child, -> { where.not(source: :parent_pipeline) }
scope :ci_sources, -> { where(config_source: ::Ci::PipelineEnums.ci_config_sources_values) } scope :ci_sources, -> { where(config_source: ::Ci::PipelineEnums.ci_config_sources_values) }
scope :for_user, -> (user) { where(user: user) } scope :for_user, -> (user) { where(user: user) }
scope :for_sha, -> (sha) { where(sha: sha) } scope :for_sha, -> (sha) { where(sha: sha) }
...@@ -697,16 +700,21 @@ module Ci ...@@ -697,16 +700,21 @@ module Ci
# If pipeline is a child of another pipeline, include the parent # If pipeline is a child of another pipeline, include the parent
# and the siblings, otherwise return only itself. # and the siblings, otherwise return only itself.
def same_family_pipeline_ids def same_family_pipeline_ids
upstream_pipeline = triggered_by_pipeline if (parent = parent_pipeline)
[parent.id] + parent.child_pipelines.pluck(:id)
if upstream_pipeline && upstream_pipeline.project == self.project
child_pipeline_ids = upstream_pipeline&.triggered_pipelines&.pluck(:id) || []
child_pipeline_ids + [upstream_pipeline.id]
else else
[self.id] [self.id]
end end
end end
def child?
parent_pipeline.present?
end
def parent?
child_pipelines.exists?
end
def detailed_status(current_user) def detailed_status(current_user)
Gitlab::Ci::Status::Pipeline::Factory Gitlab::Ci::Status::Pipeline::Factory
.new(self, current_user) .new(self, current_user)
......
...@@ -18,6 +18,8 @@ module Ci ...@@ -18,6 +18,8 @@ module Ci
validates :source_project, presence: true validates :source_project, presence: true
validates :source_job, presence: true validates :source_job, presence: true
validates :source_pipeline, presence: true validates :source_pipeline, presence: true
scope :same_project, -> { where(arel_table[:source_project_id].eq(arel_table[:project_id])) }
end end
end end
end end
......
# frozen_string_literal: true # frozen_string_literal: true
class PipelineDetailsEntity < PipelineEntity class PipelineDetailsEntity < PipelineEntity
expose :project, using: ProjectEntity
expose :flags do expose :flags do
expose :latest?, as: :latest expose :latest?, as: :latest
end end
......
...@@ -41,6 +41,7 @@ class PipelineSerializer < BaseSerializer ...@@ -41,6 +41,7 @@ class PipelineSerializer < BaseSerializer
def preloaded_relations def preloaded_relations
[ [
:latest_statuses_ordered_by_stage, :latest_statuses_ordered_by_stage,
:project,
:stages, :stages,
{ {
failed_builds: %i(project metadata) failed_builds: %i(project metadata)
......
---
title: Introduce "trigger:include" CI YAML syntax to create a sub-pipeline
merge_request: 21041
author:
type: added
...@@ -63,6 +63,10 @@ module EE ...@@ -63,6 +63,10 @@ module EE
entry :include, ::Gitlab::Ci::Config::Entry::Includes, entry :include, ::Gitlab::Ci::Config::Entry::Includes,
description: 'List of external YAML files to include.', description: 'List of external YAML files to include.',
reserved: true reserved: true
def value
@config
end
end end
class UnknownStrategy < ::Gitlab::Config::Entry::Node class UnknownStrategy < ::Gitlab::Config::Entry::Node
......
...@@ -170,7 +170,7 @@ describe Ci::CreatePipelineService, '#execute' do ...@@ -170,7 +170,7 @@ describe Ci::CreatePipelineService, '#execute' do
expect(bridge.stage).to eq 'deploy' expect(bridge.stage).to eq 'deploy'
expect(pipeline.statuses).to match_array [test, bridge] expect(pipeline.statuses).to match_array [test, bridge]
expect(bridge.options).to eq( expect(bridge.options).to eq(
'trigger' => { 'include' => { '0' => { 'local' => 'path/to/child.yml' } } } 'trigger' => { 'include' => [{ 'local' => 'path/to/child.yml' }] }
) )
expect(bridge.yaml_variables) expect(bridge.yaml_variables)
.to include(key: 'CROSS', value: 'downstream', public: true) .to include(key: 'CROSS', value: 'downstream', public: true)
......
...@@ -64,6 +64,19 @@ describe PipelinesFinder do ...@@ -64,6 +64,19 @@ describe PipelinesFinder do
end end
end end
context 'when project has child pipelines' do
let!(:parent_pipeline) { create(:ci_pipeline, project: project) }
let!(:child_pipeline) { create(:ci_pipeline, project: project, source: :parent_pipeline) }
let!(:pipeline_source) do
create(:ci_sources_pipeline, pipeline: child_pipeline, source_pipeline: parent_pipeline)
end
it 'filters out child pipelines and show only the parents' do
is_expected.to eq([parent_pipeline])
end
end
HasStatus::AVAILABLE_STATUSES.each do |target| HasStatus::AVAILABLE_STATUSES.each do |target|
context "when status is #{target}" do context "when status is #{target}" do
let(:params) { { status: target } } let(:params) { { status: target } }
......
...@@ -189,6 +189,8 @@ ci_pipelines: ...@@ -189,6 +189,8 @@ ci_pipelines:
- sourced_pipelines - sourced_pipelines
- triggered_by_pipeline - triggered_by_pipeline
- triggered_pipelines - triggered_pipelines
- child_pipelines
- parent_pipeline
- downstream_bridges - downstream_bridges
- job_artifacts - job_artifacts
- vulnerabilities_occurrence_pipelines - vulnerabilities_occurrence_pipelines
......
...@@ -2716,4 +2716,114 @@ describe Ci::Pipeline, :mailer do ...@@ -2716,4 +2716,114 @@ describe Ci::Pipeline, :mailer do
end end
end end
end end
describe '#parent_pipeline' do
let(:project) { create(:project) }
let(:pipeline) { create(:ci_pipeline, project: project) }
context 'when pipeline is triggered by a pipeline from the same project' do
let(:upstream_pipeline) { create(:ci_pipeline, project: pipeline.project) }
before do
create(:ci_sources_pipeline,
source_pipeline: upstream_pipeline,
source_project: project,
pipeline: pipeline,
project: project)
end
it 'returns the parent pipeline' do
expect(pipeline.parent_pipeline).to eq(upstream_pipeline)
end
it 'is child' do
expect(pipeline).to be_child
end
end
context 'when pipeline is triggered by a pipeline from another project' do
let(:upstream_pipeline) { create(:ci_pipeline) }
before do
create(:ci_sources_pipeline,
source_pipeline: upstream_pipeline,
source_project: upstream_pipeline.project,
pipeline: pipeline,
project: project)
end
it 'returns nil' do
expect(pipeline.parent_pipeline).to be_nil
end
it 'is not child' do
expect(pipeline).not_to be_child
end
end
context 'when pipeline is not triggered by a pipeline' do
it 'returns nil' do
expect(pipeline.parent_pipeline).to be_nil
end
it 'is not child' do
expect(pipeline).not_to be_child
end
end
end
describe '#child_pipelines' do
let(:project) { create(:project) }
let(:pipeline) { create(:ci_pipeline, project: project) }
context 'when pipeline triggered other pipelines on same project' do
let(:downstream_pipeline) { create(:ci_pipeline, project: pipeline.project) }
before do
create(:ci_sources_pipeline,
source_pipeline: pipeline,
source_project: pipeline.project,
pipeline: downstream_pipeline,
project: pipeline.project)
end
it 'returns the child pipelines' do
expect(pipeline.child_pipelines).to eq [downstream_pipeline]
end
it 'is parent' do
expect(pipeline).to be_parent
end
end
context 'when pipeline triggered other pipelines on another project' do
let(:downstream_pipeline) { create(:ci_pipeline) }
before do
create(:ci_sources_pipeline,
source_pipeline: pipeline,
source_project: pipeline.project,
pipeline: downstream_pipeline,
project: downstream_pipeline.project)
end
it 'returns empty array' do
expect(pipeline.child_pipelines).to be_empty
end
it 'is not parent' do
expect(pipeline).not_to be_parent
end
end
context 'when pipeline did not trigger any pipelines' do
it 'returns empty array' do
expect(pipeline.child_pipelines).to be_empty
end
it 'is not parent' do
expect(pipeline).not_to be_parent
end
end
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