Refine implementation of pipeline stage seeds
This commit is contained in:
parent
aa0d6b07b6
commit
fe0b2f81c7
12 changed files with 172 additions and 205 deletions
|
@ -11,9 +11,7 @@ module Ci
|
||||||
belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline'
|
belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline'
|
||||||
belongs_to :pipeline_schedule, class_name: 'Ci::PipelineSchedule'
|
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 :stages
|
||||||
has_many :auto_canceled_jobs, class_name: 'CommitStatus', foreign_key: 'auto_canceled_by_id'
|
|
||||||
|
|
||||||
has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id
|
has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id
|
||||||
has_many :builds, foreign_key: :commit_id
|
has_many :builds, foreign_key: :commit_id
|
||||||
has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id
|
has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id
|
||||||
|
@ -28,6 +26,9 @@ module Ci
|
||||||
has_many :manual_actions, -> { latest.manual_actions }, foreign_key: :commit_id, class_name: 'Ci::Build'
|
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 :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
|
delegate :id, to: :project, prefix: true
|
||||||
|
|
||||||
validates :sha, presence: { unless: :importing? }
|
validates :sha, presence: { unless: :importing? }
|
||||||
|
@ -296,17 +297,13 @@ module Ci
|
||||||
end
|
end
|
||||||
|
|
||||||
def stage_seeds
|
def stage_seeds
|
||||||
return unless config_processor
|
return [] unless config_processor
|
||||||
|
|
||||||
seeds_scope = { ref: ref, tag: tag?, trigger: trigger_requests.first }
|
@stage_seeds ||= config_processor.stage_seeds(self)
|
||||||
|
|
||||||
@seeds ||= config_processor.stage_seeds(seeds_scope).tap do |seeds|
|
|
||||||
seeds.pipeline = self
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def has_stages?
|
def has_stage_seeds?
|
||||||
stage_seeds&.has_stages?
|
stage_seeds.any?
|
||||||
end
|
end
|
||||||
|
|
||||||
def has_warnings?
|
def has_warnings?
|
||||||
|
|
11
app/models/ci/stage.rb
Normal file
11
app/models/ci/stage.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
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
|
|
@ -5,10 +5,10 @@ class CommitStatus < ActiveRecord::Base
|
||||||
|
|
||||||
self.table_name = 'ci_builds'
|
self.table_name = 'ci_builds'
|
||||||
|
|
||||||
|
belongs_to :user
|
||||||
belongs_to :project
|
belongs_to :project
|
||||||
belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id
|
belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id
|
||||||
belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline'
|
belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline'
|
||||||
belongs_to :user
|
|
||||||
|
|
||||||
delegate :commit, to: :pipeline
|
delegate :commit, to: :pipeline
|
||||||
delegate :sha, :short_sha, to: :pipeline
|
delegate :sha, :short_sha, to: :pipeline
|
||||||
|
@ -18,7 +18,7 @@ class CommitStatus < ActiveRecord::Base
|
||||||
validates :name, presence: true
|
validates :name, presence: true
|
||||||
|
|
||||||
alias_attribute :author, :user
|
alias_attribute :author, :user
|
||||||
|
|
||||||
scope :failed_but_allowed, -> do
|
scope :failed_but_allowed, -> do
|
||||||
where(allow_failure: true, status: [:failed, :canceled])
|
where(allow_failure: true, status: [:failed, :canceled])
|
||||||
end
|
end
|
||||||
|
|
|
@ -42,7 +42,7 @@ module Ci
|
||||||
return pipeline
|
return pipeline
|
||||||
end
|
end
|
||||||
|
|
||||||
unless pipeline.has_stages?
|
if pipeline.has_stage_seeds?
|
||||||
return error('No stages / jobs for this pipeline.')
|
return error('No stages / jobs for this pipeline.')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,45 +1,20 @@
|
||||||
module Ci
|
module Ci
|
||||||
class CreatePipelineStagesService < BaseService
|
class CreatePipelineStagesService < BaseService
|
||||||
attr_reader :pipeline
|
|
||||||
|
|
||||||
def execute(pipeline)
|
def execute(pipeline)
|
||||||
@pipeline = pipeline
|
pipeline.stage_seeds.each do |seed|
|
||||||
|
seed.user = current_user
|
||||||
|
|
||||||
new_builds.map do |build_attributes|
|
seed.create! do |build|
|
||||||
create_build(build_attributes)
|
##
|
||||||
|
# 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
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
|
@ -50,14 +50,17 @@ module Ci
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def stage_seeds(ref:, tag: false, trigger: nil)
|
def stage_seeds(pipeline)
|
||||||
Gitlab::Ci::Stage::Seeds.new.tap do |seeds|
|
trigger_request = pipeline.trigger_requests.first
|
||||||
@stages.uniq.each do |stage|
|
|
||||||
builds = builds_for_stage_and_ref(stage, ref, tag, trigger)
|
|
||||||
|
|
||||||
seeds.append_stage(stage, builds) if builds.any?
|
seeds = @stages.uniq.map do |stage|
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
|
seeds.compact
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_attributes(name)
|
def build_attributes(name)
|
||||||
|
|
49
lib/gitlab/ci/stage/seed.rb
Normal file
49
lib/gitlab/ci/stage/seed.rb
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
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
|
|
@ -1,62 +0,0 @@
|
||||||
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
|
|
|
@ -91,15 +91,17 @@ module Ci
|
||||||
spinach: { stage: 'test', script: 'spinach' })
|
spinach: { stage: 'test', script: 'spinach' })
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns correctly fabricated stage seeds object' do
|
let(:pipeline) { create(:ci_empty_pipeline) }
|
||||||
seeds = subject.stage_seeds(ref: 'master')
|
|
||||||
|
|
||||||
expect(seeds.stages.size).to eq 2
|
it 'correctly fabricates a stage seeds object' do
|
||||||
expect(seeds.stages.dig(0, :name)).to eq 'test'
|
seeds = subject.stage_seeds(pipeline)
|
||||||
expect(seeds.stages.dig(1, :name)).to eq 'deploy'
|
|
||||||
expect(seeds.jobs.dig(0, :name)).to eq 'rspec'
|
expect(seeds.size).to eq 2
|
||||||
expect(seeds.jobs.dig(1, :name)).to eq 'spinach'
|
expect(seeds.first.stage[:name]).to eq 'test'
|
||||||
expect(seeds.jobs.dig(2, :name)).to eq 'production'
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -109,12 +111,16 @@ module Ci
|
||||||
spinach: { stage: 'test', script: 'spinach', only: ['tags'] })
|
spinach: { stage: 'test', script: 'spinach', only: ['tags'] })
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns stage seeds only assigned to master to master' do
|
let(:pipeline) do
|
||||||
seeds = subject.stage_seeds(ref: 'feature', tag: true)
|
create(:ci_empty_pipeline, ref: 'feature', tag: true)
|
||||||
|
end
|
||||||
|
|
||||||
expect(seeds.stages.size).to eq 1
|
it 'returns stage seeds only assigned to master to master' do
|
||||||
expect(seeds.stages.dig(0, :name)).to eq 'test'
|
seeds = subject.stage_seeds(pipeline)
|
||||||
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
|
end
|
||||||
end
|
end
|
||||||
|
|
54
spec/lib/gitlab/ci/stage/seed_spec.rb
Normal file
54
spec/lib/gitlab/ci/stage/seed_spec.rb
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
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
|
|
@ -1,66 +0,0 @@
|
||||||
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
|
|
|
@ -208,8 +208,8 @@ describe Ci::Pipeline, models: true do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns preseeded stage seeds object' do
|
it 'returns preseeded stage seeds object' do
|
||||||
expect(pipeline.stage_seeds).to be_a Gitlab::Ci::Stage::Seeds
|
expect(pipeline.stage_seeds).to all(be_a Gitlab::Ci::Stage::Seed)
|
||||||
expect(pipeline.stage_seeds.stages).to all(include(pipeline: pipeline))
|
expect(pipeline.stage_seeds.count).to eq 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -513,17 +513,17 @@ describe Ci::Pipeline, models: true do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#has_stages?' do
|
describe '#has_stage_seedss?' do
|
||||||
context 'when pipeline has stages' do
|
context 'when pipeline has stage seeds' do
|
||||||
subject { create(:ci_pipeline_with_one_job) }
|
subject { create(:ci_pipeline_with_one_job) }
|
||||||
|
|
||||||
it { is_expected.to have_stages }
|
it { is_expected.to have_stage_seeds }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when pipeline does not have stages' do
|
context 'when pipeline does not have stage seeds' do
|
||||||
subject { create(:ci_pipeline_without_jobs) }
|
subject { create(:ci_pipeline_without_jobs) }
|
||||||
|
|
||||||
it { is_expected.not_to have_stages }
|
it { is_expected.not_to have_stage_seeds }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue