Skip to content
Snippets Groups Projects
Commit fe0b2f81 authored by Grzegorz Bizon's avatar Grzegorz Bizon
Browse files

Refine implementation of pipeline stage seeds

parent aa0d6b07
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -11,9 +11,7 @@ module Ci
belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline'
belongs_to :pipeline_schedule, class_name: 'Ci::PipelineSchedule'
 
has_many :auto_canceled_pipelines, class_name: 'Ci::Pipeline', foreign_key: 'auto_canceled_by_id'
has_many :auto_canceled_jobs, class_name: 'CommitStatus', foreign_key: 'auto_canceled_by_id'
has_many :stages
has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id
has_many :builds, foreign_key: :commit_id
has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id
Loading
Loading
@@ -28,6 +26,9 @@ module Ci
has_many :manual_actions, -> { latest.manual_actions }, foreign_key: :commit_id, class_name: 'Ci::Build'
has_many :artifacts, -> { latest.with_artifacts_not_expired }, foreign_key: :commit_id, class_name: 'Ci::Build'
 
has_many :auto_canceled_pipelines, class_name: 'Ci::Pipeline', foreign_key: 'auto_canceled_by_id'
has_many :auto_canceled_jobs, class_name: 'CommitStatus', foreign_key: 'auto_canceled_by_id'
delegate :id, to: :project, prefix: true
 
validates :sha, presence: { unless: :importing? }
Loading
Loading
@@ -296,17 +297,13 @@ module Ci
end
 
def stage_seeds
return unless config_processor
seeds_scope = { ref: ref, tag: tag?, trigger: trigger_requests.first }
return [] unless config_processor
 
@seeds ||= config_processor.stage_seeds(seeds_scope).tap do |seeds|
seeds.pipeline = self
end
@stage_seeds ||= config_processor.stage_seeds(self)
end
 
def has_stages?
stage_seeds&.has_stages?
def has_stage_seeds?
stage_seeds.any?
end
 
def has_warnings?
Loading
Loading
module Ci
class Stage < ActiveRecord::Base
extend Ci::Model
belongs_to :project
belongs_to :pipeline
has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id
has_many :builds, foreign_key: :commit_id
end
end
Loading
Loading
@@ -5,10 +5,10 @@ class CommitStatus < ActiveRecord::Base
 
self.table_name = 'ci_builds'
 
belongs_to :user
belongs_to :project
belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id
belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline'
belongs_to :user
 
delegate :commit, to: :pipeline
delegate :sha, :short_sha, to: :pipeline
Loading
Loading
@@ -18,7 +18,7 @@ class CommitStatus < ActiveRecord::Base
validates :name, presence: true
 
alias_attribute :author, :user
scope :failed_but_allowed, -> do
where(allow_failure: true, status: [:failed, :canceled])
end
Loading
Loading
Loading
Loading
@@ -42,7 +42,7 @@ module Ci
return pipeline
end
 
unless pipeline.has_stages?
if pipeline.has_stage_seeds?
return error('No stages / jobs for this pipeline.')
end
 
Loading
Loading
module Ci
class CreatePipelineStagesService < BaseService
attr_reader :pipeline
def execute(pipeline)
@pipeline = pipeline
new_builds.map do |build_attributes|
create_build(build_attributes)
pipeline.stage_seeds.each do |seed|
seed.user = current_user
seed.create! do |build|
##
# Create the environment before the build starts. This sets its slug and
# makes it available as an environment variable
#
if build.has_environment?
environment_name = build.expanded_environment_name
project.environments.find_or_create_by(name: environment_name)
end
end
end
end
delegate :project, to: :pipeline
private
def create_build(build_attributes)
build_attributes = build_attributes.merge(
pipeline: pipeline,
project: project,
ref: pipeline.ref,
tag: pipeline.tag,
user: current_user,
trigger_request: trigger_request
)
build = pipeline.builds.create(build_attributes)
# Create the environment before the build starts. This sets its slug and
# makes it available as an environment variable
project.environments.find_or_create_by(name: build.expanded_environment_name) if
build.has_environment?
build
end
def new_builds
@new_builds ||= pipeline.config_builds_attributes
end
def trigger_request
@trigger_request ||= pipeline.trigger_requests.first
end
end
end
Loading
Loading
@@ -50,14 +50,17 @@ module Ci
end
end
 
def stage_seeds(ref:, tag: false, trigger: nil)
Gitlab::Ci::Stage::Seeds.new.tap do |seeds|
@stages.uniq.each do |stage|
builds = builds_for_stage_and_ref(stage, ref, tag, trigger)
def stage_seeds(pipeline)
trigger_request = pipeline.trigger_requests.first
 
seeds.append_stage(stage, builds) if builds.any?
end
seeds = @stages.uniq.map do |stage|
builds = builds_for_stage_and_ref(
stage, pipeline.ref, pipeline.tag?, trigger_request)
Gitlab::Ci::Stage::Seed.new(pipeline, stage, builds) if builds.any?
end
seeds.compact
end
 
def build_attributes(name)
Loading
Loading
module Gitlab
module Ci
module Stage
class Seed
attr_reader :pipeline
delegate :project, to: :pipeline
def initialize(pipeline, stage, jobs)
@pipeline = pipeline
@stage = { name: stage }
@jobs = jobs.to_a.dup
end
def user=(current_user)
@jobs.map! do |attributes|
attributes.merge(user: current_user)
end
end
def stage
@stage.merge(project: project)
end
def builds
trigger = pipeline.trigger_requests.first
@jobs.map do |attributes|
attributes.merge(project: project,
ref: pipeline.ref,
tag: pipeline.tag,
trigger_request: trigger)
end
end
def create!
pipeline.stages.create!(stage).tap do |stage|
builds_attributes = builds.map do |attributes|
attributes.merge(stage_id: stage.id)
end
pipeline.builds.create!(builds_attributes).each do |build|
yield build if block_given?
end
end
end
end
end
end
end
module Gitlab
module Ci
module Stage
class Seeds
Seed = Struct.new(:stage, :jobs)
def initialize
@stages = []
end
def has_stages?
@stages.any?
end
def stages
@stages.map(&:stage)
end
def jobs
@stages.map(&:jobs).flatten
end
def append_stage(stage, jobs)
@stages << Seed.new({ name: stage }, jobs)
end
def pipeline=(pipeline)
trigger_request = pipeline.trigger_requests.first
stages.each do |attributes|
attributes.merge!(
pipeline: pipeline,
project: pipeline.project,
)
end
jobs.each do |attributes|
attributes.merge!(
pipeline: pipeline,
project: pipeline.project,
ref: pipeline.ref,
tag: pipeline.tag,
trigger_request: trigger_request
)
end
end
def user=(current_user)
jobs.each do |attributes|
attributes.merge!(user: current_user)
end
end
def to_attributes
@stages.map do |seed|
seed.stage.merge(builds_attributes: seed.jobs)
end
end
end
end
end
end
Loading
Loading
@@ -91,15 +91,17 @@ module Ci
spinach: { stage: 'test', script: 'spinach' })
end
 
it 'returns correctly fabricated stage seeds object' do
seeds = subject.stage_seeds(ref: 'master')
let(:pipeline) { create(:ci_empty_pipeline) }
 
expect(seeds.stages.size).to eq 2
expect(seeds.stages.dig(0, :name)).to eq 'test'
expect(seeds.stages.dig(1, :name)).to eq 'deploy'
expect(seeds.jobs.dig(0, :name)).to eq 'rspec'
expect(seeds.jobs.dig(1, :name)).to eq 'spinach'
expect(seeds.jobs.dig(2, :name)).to eq 'production'
it 'correctly fabricates a stage seeds object' do
seeds = subject.stage_seeds(pipeline)
expect(seeds.size).to eq 2
expect(seeds.first.stage[:name]).to eq 'test'
expect(seeds.second.stage[:name]).to eq 'deploy'
expect(seeds.first.builds.dig(0, :name)).to eq 'rspec'
expect(seeds.first.builds.dig(1, :name)).to eq 'spinach'
expect(seeds.second.builds.dig(0, :name)).to eq 'production'
end
end
 
Loading
Loading
@@ -109,12 +111,16 @@ module Ci
spinach: { stage: 'test', script: 'spinach', only: ['tags'] })
end
 
let(:pipeline) do
create(:ci_empty_pipeline, ref: 'feature', tag: true)
end
it 'returns stage seeds only assigned to master to master' do
seeds = subject.stage_seeds(ref: 'feature', tag: true)
seeds = subject.stage_seeds(pipeline)
 
expect(seeds.stages.size).to eq 1
expect(seeds.stages.dig(0, :name)).to eq 'test'
expect(seeds.jobs.dig(0, :name)).to eq 'spinach'
expect(seeds.size).to eq 1
expect(seeds.first.stage[:name]).to eq 'test'
expect(seeds.first.builds.dig(0, :name)).to eq 'spinach'
end
end
end
Loading
Loading
require 'spec_helper'
describe Gitlab::Ci::Stage::Seed do
let(:pipeline) { create(:ci_empty_pipeline) }
let(:builds) do
[{ name: 'rspec' }, { name: 'spinach' }]
end
subject do
described_class.new(pipeline, 'test', builds)
end
describe '#stage' do
it 'returns hash attributes of a stage' do
expect(subject.stage).to be_a Hash
expect(subject.stage).to include(:name, :project)
end
end
describe '#builds' do
it 'returns hash attributes of all builds' do
expect(subject.builds.size).to eq 2
expect(subject.builds).to all(include(pipeline: pipeline))
expect(subject.builds).to all(include(project: pipeline.project))
expect(subject.builds).to all(include(ref: 'master'))
expect(subject.builds).to all(include(tag: false))
expect(subject.builds)
.to all(include(trigger_request: pipeline.trigger_requests.first))
end
end
describe '#user=' do
let(:user) { create(:user) }
it 'assignes relevant pipeline attributes' do
subject.user = user
expect(subject.builds).to all(include(user: user))
end
end
describe '#create!' do
it 'creates all stages and builds' do
subject.create!
expect(pipeline.reload.stages.count).to eq 1
expect(pipeline.reload.builds.count).to eq 2
expect(pipeline.builds).to all(satisfy { |job| job.stage_id.present? })
expect(pipeline.builds).to all(satisfy { |job| job.pipeline.present? })
expect(pipeline.builds).to all(satisfy { |job| job.project.present? })
end
end
end
require 'spec_helper'
describe Gitlab::Ci::Stage::Seeds do
before do
subject.append_stage('test', [{ name: 'rspec' }, { name: 'spinach' }])
subject.append_stage('deploy', [{ name: 'prod', script: 'cap deploy' }])
end
describe '#has_stages?' do
it { is_expected.to have_stages }
end
describe '#stages' do
it 'returns hashes of all stages' do
expect(subject.stages.size).to eq 2
expect(subject.stages).to all(be_a Hash)
end
end
describe '#jobs' do
it 'returns all jobs in all stages' do
expect(subject.jobs.size).to eq 3
end
end
describe '#pipeline=' do
let(:pipeline) do
create(:ci_empty_pipeline, ref: 'feature', tag: true)
end
it 'assignes relevant pipeline attributes' do
trigger_request = pipeline.trigger_requests.first
subject.pipeline = pipeline
expect(subject.stages).to all(include(pipeline: pipeline))
expect(subject.stages).to all(include(project: pipeline.project))
expect(subject.jobs).to all(include(pipeline: pipeline))
expect(subject.jobs).to all(include(project: pipeline.project))
expect(subject.jobs).to all(include(ref: 'feature'))
expect(subject.jobs).to all(include(tag: true))
expect(subject.jobs).to all(include(trigger_request: trigger_request))
end
end
describe '#user=' do
let(:user) { create(:user) }
it 'assignes relevant pipeline attributes' do
subject.user = user
expect(subject.jobs).to all(include(user: user))
end
end
describe '#to_attributes' do
it 'exposes stage attributes with nested jobs' do
attributes = [{ name: 'test', builds_attributes:
[{ name: 'rspec' }, { name: 'spinach' }] },
{ name: 'deploy', builds_attributes:
[{ name: 'prod', script: 'cap deploy' }] }]
expect(subject.to_attributes).to eq attributes
end
end
end
Loading
Loading
@@ -208,8 +208,8 @@ describe Ci::Pipeline, models: true do
end
 
it 'returns preseeded stage seeds object' do
expect(pipeline.stage_seeds).to be_a Gitlab::Ci::Stage::Seeds
expect(pipeline.stage_seeds.stages).to all(include(pipeline: pipeline))
expect(pipeline.stage_seeds).to all(be_a Gitlab::Ci::Stage::Seed)
expect(pipeline.stage_seeds.count).to eq 1
end
end
 
Loading
Loading
@@ -513,17 +513,17 @@ describe Ci::Pipeline, models: true do
end
end
 
describe '#has_stages?' do
context 'when pipeline has stages' do
describe '#has_stage_seedss?' do
context 'when pipeline has stage seeds' do
subject { create(:ci_pipeline_with_one_job) }
 
it { is_expected.to have_stages }
it { is_expected.to have_stage_seeds }
end
 
context 'when pipeline does not have stages' do
context 'when pipeline does not have stage seeds' do
subject { create(:ci_pipeline_without_jobs) }
 
it { is_expected.not_to have_stages }
it { is_expected.not_to have_stage_seeds }
end
end
 
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment