Commit 5ab28549 authored by Douwe Maan's avatar Douwe Maan

Merge branch 'feature/gb/cross-project-pipeline-trigger' into 'master'

Cross-project pipeline triggers /CE

See merge request gitlab-org/gitlab-ce!24664
parents f4386181 2d154cee
...@@ -2,11 +2,13 @@ ...@@ -2,11 +2,13 @@
module Ci module Ci
class Bridge < CommitStatus class Bridge < CommitStatus
include Ci::Processable
include Importable include Importable
include AfterCommitQueue include AfterCommitQueue
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
belongs_to :project belongs_to :project
belongs_to :trigger_request
validates :ref, presence: true validates :ref, presence: true
def self.retry(bridge, current_user) def self.retry(bridge, current_user)
...@@ -23,6 +25,21 @@ module Ci ...@@ -23,6 +25,21 @@ module Ci
.fabricate! .fabricate!
end end
def schedulable?
false
end
def action?
false
end
def artifacts?
false
end
def expanded_environment_name
end
def predefined_variables def predefined_variables
raise NotImplementedError raise NotImplementedError
end end
...@@ -30,5 +47,9 @@ module Ci ...@@ -30,5 +47,9 @@ module Ci
def execute_hooks def execute_hooks
raise NotImplementedError raise NotImplementedError
end end
def to_partial_path
'projects/generic_commit_statuses/generic_commit_status'
end
end end
end end
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
module Ci module Ci
class Build < CommitStatus class Build < CommitStatus
prepend ArtifactMigratable prepend ArtifactMigratable
include Ci::Processable
include TokenAuthenticatable include TokenAuthenticatable
include AfterCommitQueue include AfterCommitQueue
include ObjectStorage::BackgroundMove include ObjectStorage::BackgroundMove
...@@ -638,10 +639,6 @@ module Ci ...@@ -638,10 +639,6 @@ module Ci
super || project.try(:build_coverage_regex) super || project.try(:build_coverage_regex)
end end
def when
read_attribute(:when) || 'on_success'
end
def options def options
read_metadata_attribute(:options, :config_options, {}) read_metadata_attribute(:options, :config_options, {})
end end
......
...@@ -25,6 +25,8 @@ module Ci ...@@ -25,6 +25,8 @@ module Ci
has_many :stages, -> { order(position: :asc) }, inverse_of: :pipeline has_many :stages, -> { order(position: :asc) }, inverse_of: :pipeline
has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline
has_many :processables, -> { processables },
class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline
has_many :builds, foreign_key: :commit_id, inverse_of: :pipeline has_many :builds, foreign_key: :commit_id, inverse_of: :pipeline
has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id # rubocop:disable Cop/ActiveRecordDependent has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id # rubocop:disable Cop/ActiveRecordDependent
has_many :variables, class_name: 'Ci::PipelineVariable' has_many :variables, class_name: 'Ci::PipelineVariable'
......
...@@ -14,6 +14,7 @@ module Ci ...@@ -14,6 +14,7 @@ module Ci
has_many :statuses, class_name: 'CommitStatus', foreign_key: :stage_id has_many :statuses, class_name: 'CommitStatus', foreign_key: :stage_id
has_many :builds, foreign_key: :stage_id has_many :builds, foreign_key: :stage_id
has_many :bridges, foreign_key: :stage_id
with_options unless: :importing? do with_options unless: :importing? do
validates :project, presence: true validates :project, presence: true
......
...@@ -41,6 +41,7 @@ class CommitStatus < ActiveRecord::Base ...@@ -41,6 +41,7 @@ class CommitStatus < ActiveRecord::Base
scope :latest_ordered, -> { latest.ordered.includes(project: :namespace) } scope :latest_ordered, -> { latest.ordered.includes(project: :namespace) }
scope :retried_ordered, -> { retried.ordered.includes(project: :namespace) } scope :retried_ordered, -> { retried.ordered.includes(project: :namespace) }
scope :after_stage, -> (index) { where('stage_idx > ?', index) } scope :after_stage, -> (index) { where('stage_idx > ?', index) }
scope :processables, -> { where(type: %w[Ci::Build Ci::Bridge]) }
# We use `CommitStatusEnums.failure_reasons` here so that EE can more easily # We use `CommitStatusEnums.failure_reasons` here so that EE can more easily
# extend this `Hash` with new values. # extend this `Hash` with new values.
......
# frozen_string_literal: true
module Ci
##
# This module implements methods that need to be implemented by CI/CD
# entities that are supposed to go through pipeline processing
# services.
#
#
module Processable
def schedulable?
raise NotImplementedError
end
def action?
raise NotImplementedError
end
def when
read_attribute(:when) || 'on_success'
end
def expanded_environment_name
raise NotImplementedError
end
end
end
...@@ -10,7 +10,7 @@ module Ci ...@@ -10,7 +10,7 @@ module Ci
update_retried update_retried
new_builds = new_builds =
stage_indexes_of_created_builds.map do |index| stage_indexes_of_created_processables.map do |index|
process_stage(index) process_stage(index)
end end
...@@ -27,7 +27,7 @@ module Ci ...@@ -27,7 +27,7 @@ module Ci
return if HasStatus::BLOCKED_STATUS.include?(current_status) return if HasStatus::BLOCKED_STATUS.include?(current_status)
if HasStatus::COMPLETED_STATUSES.include?(current_status) if HasStatus::COMPLETED_STATUSES.include?(current_status)
created_builds_in_stage(index).select do |build| created_processables_in_stage(index).select do |build|
Gitlab::OptimisticLocking.retry_lock(build) do |subject| Gitlab::OptimisticLocking.retry_lock(build) do |subject|
Ci::ProcessBuildService.new(project, @user) Ci::ProcessBuildService.new(project, @user)
.execute(build, current_status) .execute(build, current_status)
...@@ -43,19 +43,19 @@ module Ci ...@@ -43,19 +43,19 @@ module Ci
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def stage_indexes_of_created_builds def stage_indexes_of_created_processables
created_builds.order(:stage_idx).pluck('distinct stage_idx') created_processables.order(:stage_idx).pluck('distinct stage_idx')
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def created_builds_in_stage(index) def created_processables_in_stage(index)
created_builds.where(stage_idx: index) created_processables.where(stage_idx: index)
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def created_builds def created_processables
pipeline.builds.created pipeline.processables.created
end end
# This method is for compatibility and data consistency and should be removed with 9.3 version of GitLab # This method is for compatibility and data consistency and should be removed with 9.3 version of GitLab
......
...@@ -67,7 +67,7 @@ module Gitlab ...@@ -67,7 +67,7 @@ module Gitlab
entry :only, Entry::Policy, entry :only, Entry::Policy,
description: 'Refs policy this job will be executed for.', description: 'Refs policy this job will be executed for.',
default: { refs: %w[branches tags] } default: Entry::Policy::DEFAULT_ONLY
entry :except, Entry::Policy, entry :except, Entry::Policy,
description: 'Refs policy this job will be executed for.' description: 'Refs policy this job will be executed for.'
......
...@@ -28,11 +28,15 @@ module Gitlab ...@@ -28,11 +28,15 @@ module Gitlab
name.to_s.start_with?('.') name.to_s.start_with?('.')
end end
def node_type(name)
hidden?(name) ? Entry::Hidden : Entry::Job
end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def compose!(deps = nil) def compose!(deps = nil)
super do super do
@config.each do |name, config| @config.each do |name, config|
node = hidden?(name) ? Entry::Hidden : Entry::Job node = node_type(name)
factory = ::Gitlab::Config::Entry::Factory.new(node) factory = ::Gitlab::Config::Entry::Factory.new(node)
.value(config || {}) .value(config || {})
......
...@@ -11,6 +11,8 @@ module Gitlab ...@@ -11,6 +11,8 @@ module Gitlab
strategy :RefsPolicy, if: -> (config) { config.is_a?(Array) } strategy :RefsPolicy, if: -> (config) { config.is_a?(Array) }
strategy :ComplexPolicy, if: -> (config) { config.is_a?(Hash) } strategy :ComplexPolicy, if: -> (config) { config.is_a?(Hash) }
DEFAULT_ONLY = { refs: %w[branches tags] }.freeze
class RefsPolicy < ::Gitlab::Config::Entry::Node class RefsPolicy < ::Gitlab::Config::Entry::Node
include ::Gitlab::Config::Entry::Validatable include ::Gitlab::Config::Entry::Validatable
......
...@@ -38,8 +38,15 @@ module Gitlab ...@@ -38,8 +38,15 @@ module Gitlab
) )
end end
def bridge?
@attributes.to_h.dig(:options, :trigger).present?
end
def to_resource def to_resource
strong_memoize(:resource) do strong_memoize(:resource) do
if bridge?
::Ci::Bridge.new(attributes)
else
::Ci::Build.new(attributes) ::Ci::Build.new(attributes)
end end
end end
...@@ -47,4 +54,5 @@ module Gitlab ...@@ -47,4 +54,5 @@ module Gitlab
end end
end end
end end
end
end end
...@@ -39,7 +39,13 @@ module Gitlab ...@@ -39,7 +39,13 @@ module Gitlab
def to_resource def to_resource
strong_memoize(:stage) do strong_memoize(:stage) do
::Ci::Stage.new(attributes).tap do |stage| ::Ci::Stage.new(attributes).tap do |stage|
seeds.each { |seed| stage.builds << seed.to_resource } seeds.each do |seed|
if seed.bridge?
stage.bridges << seed.to_resource
else
stage.builds << seed.to_resource
end
end
end end
end end
end end
......
...@@ -18,7 +18,6 @@ module Gitlab ...@@ -18,7 +18,6 @@ module Gitlab
end end
def details_path def details_path
raise NotImplementedError
end end
end end
end end
......
...@@ -33,7 +33,7 @@ module Gitlab ...@@ -33,7 +33,7 @@ module Gitlab
{ stage_idx: @stages.index(job[:stage]), { stage_idx: @stages.index(job[:stage]),
stage: job[:stage], stage: job[:stage],
tag_list: job[:tags] || [], tag_list: job[:tags],
name: job[:name].to_s, name: job[:name].to_s,
allow_failure: job[:ignore], allow_failure: job[:ignore],
when: job[:when] || 'on_success', when: job[:when] || 'on_success',
...@@ -53,8 +53,9 @@ module Gitlab ...@@ -53,8 +53,9 @@ module Gitlab
retry: job[:retry], retry: job[:retry],
parallel: job[:parallel], parallel: job[:parallel],
instance: job[:instance], instance: job[:instance],
start_in: job[:start_in] start_in: job[:start_in],
}.compact } trigger: job[:trigger]
}.compact }.compact
end end
def stage_builds_attributes(stage) def stage_builds_attributes(stage)
......
...@@ -10,8 +10,16 @@ FactoryBot.define do ...@@ -10,8 +10,16 @@ FactoryBot.define do
pipeline factory: :ci_pipeline pipeline factory: :ci_pipeline
transient { downstream nil }
after(:build) do |bridge, evaluator| after(:build) do |bridge, evaluator|
bridge.project ||= bridge.pipeline.project bridge.project ||= bridge.pipeline.project
if evaluator.downstream.present?
bridge.options = bridge.options.to_h.merge(
trigger: { project: evaluator.downstream.full_path }
)
end
end end
end end
end end
...@@ -286,6 +286,49 @@ describe 'Pipeline', :js do ...@@ -286,6 +286,49 @@ describe 'Pipeline', :js do
end end
end end
context 'when a bridge job exists' do
include_context 'pipeline builds'
let(:project) { create(:project, :repository) }
let(:downstream) { create(:project, :repository) }
let(:pipeline) do
create(:ci_pipeline, project: project,
ref: 'master',
sha: project.commit.id,
user: user)
end
let!(:bridge) do
create(:ci_bridge, pipeline: pipeline,
name: 'cross-build',
user: user,
downstream: downstream)
end
describe 'GET /:project/pipelines/:id' do
before do
visit project_pipeline_path(project, pipeline)
end
it 'shows the pipeline with a bridge job' do
expect(page).to have_selector('.pipeline-visualization')
expect(page).to have_content('cross-build')
end
end
describe 'GET /:project/pipelines/:id/builds' do
before do
visit builds_project_pipeline_path(project, pipeline)
end
it 'shows a bridge job on a list' do
expect(page).to have_content('cross-build')
expect(page).to have_content(bridge.id)
end
end
end
describe 'GET /:project/pipelines/:id/builds' do describe 'GET /:project/pipelines/:id/builds' do
include_context 'pipeline builds' include_context 'pipeline builds'
......
...@@ -163,14 +163,14 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do ...@@ -163,14 +163,14 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do
->(pipeline) { pipeline.variables.create!(key: 'VAR', value: '123') } ->(pipeline) { pipeline.variables.create!(key: 'VAR', value: '123') }
end end
it 'raises exception' do it 'wastes pipeline iid' do
expect { step.perform! }.to raise_error(ActiveRecord::RecordNotSaved) expect { step.perform! }.to raise_error(ActiveRecord::RecordNotSaved)
end
it 'wastes pipeline iid' do last_iid = InternalId.ci_pipelines
expect { step.perform! }.to raise_error .where(project_id: project.id)
.last.last_value
expect(InternalId.ci_pipelines.where(project_id: project.id).last.last_value).to be > 0 expect(last_iid).to be > 0
end end
end end
end end
......
...@@ -5,8 +5,7 @@ describe Gitlab::Ci::Pipeline::Seed::Build do ...@@ -5,8 +5,7 @@ describe Gitlab::Ci::Pipeline::Seed::Build do
let(:pipeline) { create(:ci_empty_pipeline, project: project) } let(:pipeline) { create(:ci_empty_pipeline, project: project) }
let(:attributes) do let(:attributes) do
{ name: 'rspec', { name: 'rspec', ref: 'master' }
ref: 'master' }
end end
subject do subject do
...@@ -21,11 +20,46 @@ describe Gitlab::Ci::Pipeline::Seed::Build do ...@@ -21,11 +20,46 @@ describe Gitlab::Ci::Pipeline::Seed::Build do
end end
end end
describe '#bridge?' do
context 'when job is a bridge' do
let(:attributes) do
{ name: 'rspec', ref: 'master', options: { trigger: 'my/project' } }
end
it { is_expected.to be_bridge }
end
context 'when trigger definition is empty' do
let(:attributes) do
{ name: 'rspec', ref: 'master', options: { trigger: '' } }
end
it { is_expected.not_to be_bridge }
end
context 'when job is not a bridge' do
it { is_expected.not_to be_bridge }
end
end
describe '#to_resource' do describe '#to_resource' do
context 'when job is not a bridge' do
it 'returns a valid build resource' do it 'returns a valid build resource' do
expect(subject.to_resource).to be_a(::Ci::Build) expect(subject.to_resource).to be_a(::Ci::Build)
expect(subject.to_resource).to be_valid expect(subject.to_resource).to be_valid
end end
end
context 'when job is a bridge' do
let(:attributes) do
{ name: 'rspec', ref: 'master', options: { trigger: 'my/project' } }
end
it 'returns a valid bridge resource' do
expect(subject.to_resource).to be_a(::Ci::Bridge)
expect(subject.to_resource).to be_valid
end
end
it 'memoizes a resource object' do it 'memoizes a resource object' do
build = subject.to_resource build = subject.to_resource
......
...@@ -62,8 +62,18 @@ describe Gitlab::Ci::Pipeline::Seed::Stage do ...@@ -62,8 +62,18 @@ describe Gitlab::Ci::Pipeline::Seed::Stage do
expect(subject.seeds.map(&:attributes)).to all(include(ref: 'master')) expect(subject.seeds.map(&:attributes)).to all(include(ref: 'master'))
expect(subject.seeds.map(&:attributes)).to all(include(tag: false)) expect(subject.seeds.map(&:attributes)).to all(include(tag: false))
expect(subject.seeds.map(&:attributes)).to all(include(project: pipeline.project)) expect(subject.seeds.map(&:attributes)).to all(include(project: pipeline.project))
end
context 'when a legacy trigger exists' do
before do
create(:ci_trigger_request, pipeline: pipeline)
end
it 'returns build seeds including legacy trigger' do
expect(pipeline.legacy_trigger).not_to be_nil
expect(subject.seeds.map(&:attributes)) expect(subject.seeds.map(&:attributes))
.to all(include(trigger_request: pipeline.trigger_requests.first)) .to all(include(trigger_request: pipeline.legacy_trigger))
end
end end
context 'when a ref is protected' do context 'when a ref is protected' do
......
...@@ -21,15 +21,12 @@ module Gitlab ...@@ -21,15 +21,12 @@ module Gitlab
stage: "test", stage: "test",
stage_idx: 1, stage_idx: 1,
name: "rspec", name: "rspec",
coverage_regex: nil,
tag_list: [],
options: { options: {
before_script: ["pwd"], before_script: ["pwd"],
script: ["rspec"] script: ["rspec"]
}, },
allow_failure: false, allow_failure: false,
when: "on_success", when: "on_success",
environment: nil,
yaml_variables: [] yaml_variables: []
}) })
end end
...@@ -154,12 +151,9 @@ module Gitlab ...@@ -154,12 +151,9 @@ module Gitlab
builds: builds:
[{ stage_idx: 1, [{ stage_idx: 1,
stage: "test", stage: "test",
tag_list: [],
name: "rspec", name: "rspec",
allow_failure: false, allow_failure: false,
when: "on_success", when: "on_success",
environment: nil,
coverage_regex: nil,
yaml_variables: [], yaml_variables: [],
options: { script: ["rspec"] }, options: { script: ["rspec"] },
only: { refs: ["branches"] }, only: { refs: ["branches"] },
...@@ -169,12 +163,9 @@ module Gitlab ...@@ -169,12 +163,9 @@ module Gitlab
builds: builds:
[{ stage_idx: 2, [{ stage_idx: 2,
stage: "deploy", stage: "deploy",
tag_list: [],
name: "prod", name: "prod",
allow_failure: false, allow_failure: false,
when: "on_success", when: "on_success",
environment: nil,
coverage_regex: nil,
yaml_variables: [], yaml_variables: [],
options: { script: ["cap prod"] }, options: { script: ["cap prod"] },
only: { refs: ["tags"] }, only: { refs: ["tags"] },
...@@ -344,8 +335,6 @@ module Gitlab ...@@ -344,8 +335,6 @@ module Gitlab
stage: "test", stage: "test",
stage_idx: 1, stage_idx: 1,
name: "rspec", name: "rspec",
coverage_regex: nil,
tag_list: [],
options: { options: {
before_script: ["pwd"], before_script: ["pwd"],
script: ["rspec"], script: ["rspec"],
...@@ -356,7 +345,6 @@ module Gitlab ...@@ -356,7 +345,6 @@ module Gitlab
}, },
allow_failure: false, allow_failure: false,
when: "on_success", when: "on_success",
environment: nil,
yaml_variables: [] yaml_variables: []
}) })
end end
...@@ -378,8 +366,6 @@ module Gitlab ...@@ -378,8 +366,6 @@ module Gitlab
stage: "test", stage: "test",
stage_idx: 1, stage_idx: 1,
name: "rspec", name: "rspec",
coverage_regex: nil,
tag_list: [],
options: { options: {
before_script: ["pwd"], before_script: ["pwd"],
script: ["rspec"], script: ["rspec"],
...@@ -390,7 +376,6 @@ module Gitlab ...@@ -390,7 +376,6 @@ module Gitlab
}, },
allow_failure: false, allow_failure: false,
when: "on_success", when: "on_success",
environment: nil,
yaml_variables: [] yaml_variables: []
}) })
end end
...@@ -410,8 +395,6 @@ module Gitlab ...@@ -410,8 +395,6 @@ module Gitlab
stage: "test", stage: "test",
stage_idx: 1, stage_idx: 1,
name: "rspec", name: "rspec",
coverage_regex: nil,
tag_list: [],
options: { options: {
before_script: ["pwd"], before_script: ["pwd"],
script: ["rspec"], script: ["rspec"],
...@@ -420,7 +403,6 @@ module Gitlab ...@@ -420,7 +403,6 @@ module Gitlab
}, },
allow_failure: false, allow_failure: false,
when: "on_success", when: "on_success",
environment: nil,
yaml_variables: [] yaml_variables: []
}) })
end end
...@@ -438,8 +420,6 @@ module Gitlab ...@@ -438,8 +420,6 @@ module Gitlab
stage: "test", stage: "test",
stage_idx: 1, stage_idx: 1,
name: "rspec", name: "rspec",
coverage_regex: nil,
tag_list: [],
options: { options: {
before_script: ["pwd"], before_script: ["pwd"],
script: ["rspec"], script: ["rspec"],
...@@ -448,7 +428,6 @@ module Gitlab ...@@ -448,7 +428,6 @@ module Gitlab
}, },
allow_failure: false, allow_failure: false,
when: "on_success", when: "on_success",
environment: nil,
yaml_variables: [] yaml_variables: []
}) })
end end
...@@ -763,8 +742,6 @@ module Gitlab ...@@ -763,8 +742,6 @@ module Gitlab
stage: "test", stage: "test",
stage_idx: 1, stage_idx: 1,
name: "rspec", name: "rspec",
coverage_regex: nil,
tag_list: [],
options: { options: {
before_script: ["pwd"], before_script: ["pwd"],
script: ["rspec"], script: ["rspec"],
...@@ -779,7 +756,6 @@ module Gitlab ...@@ -779,7 +756,6 @@ module Gitlab
}, },
when: "on_success", when: "on_success",
allow_failure: false, allow_failure: false,
environment: nil,
yaml_variables: [] yaml_variables: []
}) })
end end
...@@ -976,14 +952,11 @@ module Gitlab ...@@ -976,14 +952,11 @@ module Gitlab
stage: "test", stage: "test",
stage_idx: 1, stage_idx: 1,
name: "normal_job", name: "normal_job",
coverage_regex: nil,
tag_list: [],
options: { options: {
script: ["test"] script: ["test"]
}, },
when: "on_success", when: "on_success",
allow_failure: false, allow_failure: false,
environment: nil,
yaml_variables: [] yaml_variables: []
}) })
end end
...@@ -1023,28 +996,22 @@ module Gitlab ...@@ -1023,28 +996,22 @@ module Gitlab
stage: "build", stage: "build",
stage_idx: 0, stage_idx: 0,
name: "job1", name: "job1",
coverage_regex: nil,
tag_list: [],
options: { options: {
script: ["execute-script-for-job"] script: ["execute-script-for-job"]
}, },
when: "on_success", when: "on_success",
allow_failure: false, allow_failure: false,
environment: nil,
yaml_variables: [] yaml_variables: []
}) })
expect(subject.second).to eq({ expect(subject.second).to eq({
stage: "build", stage: "build",
stage_idx: 0, stage_idx: 0,
name: "job2", name: "job2",
coverage_regex: nil,
tag_list: [],
options: { options: {
script: ["execute-script-for-job"] script: ["execute-script-for-job"]
}, },
when: "on_success", when: "on_success",
allow_failure: false, allow_failure: false,
environment: nil,
yaml_variables: [] yaml_variables: []
}) })
end end
......
...@@ -114,6 +114,7 @@ ci_pipelines: ...@@ -114,6 +114,7 @@ ci_pipelines:
- stages - stages
- statuses - statuses
- builds - builds
- processables
- trigger_requests - trigger_requests
- variables - variables
- auto_canceled_by - auto_canceled_by
...@@ -137,6 +138,7 @@ stages: ...@@ -137,6 +138,7 @@ stages:
- pipeline - pipeline
- statuses - statuses
- builds - builds
- bridges
statuses: statuses:
- project - project
- pipeline - pipeline
......
...@@ -39,6 +39,29 @@ describe Ci::Pipeline, :mailer do ...@@ -39,6 +39,29 @@ describe Ci::Pipeline, :mailer do
end end
end end
describe '.processables' do
before do
create(:ci_build, name: 'build', pipeline: pipeline)
create(:ci_bridge, name: 'bridge', pipeline: pipeline)
create(:commit_status, name: 'commit status', pipeline: pipeline)
create(:generic_commit_status, name: 'generic status', pipeline: pipeline)
end
it 'has an association with processable CI/CD entities' do
pipeline.processables.pluck('name').yield_self do |processables|
expect(processables).to match_array %w[build bridge]
end
end
it 'makes it possible to append a new processable' do
pipeline.processables << build(:ci_bridge)
pipeline.save!
expect(pipeline.processables.reload.count).to eq 3
end
end
describe '.sort_by_merge_request_pipelines' do describe '.sort_by_merge_request_pipelines' do
subject { described_class.sort_by_merge_request_pipelines } subject { described_class.sort_by_merge_request_pipelines }
......
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