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 :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
|
||||
|
@ -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? }
|
||||
|
@ -296,17 +297,13 @@ module Ci
|
|||
end
|
||||
|
||||
def stage_seeds
|
||||
return unless config_processor
|
||||
return [] unless config_processor
|
||||
|
||||
seeds_scope = { ref: ref, tag: tag?, trigger: trigger_requests.first }
|
||||
|
||||
@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?
|
||||
|
|
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'
|
||||
|
||||
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
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,45 +1,20 @@
|
|||
module Ci
|
||||
class CreatePipelineStagesService < BaseService
|
||||
attr_reader :pipeline
|
||||
|
||||
def execute(pipeline)
|
||||
@pipeline = pipeline
|
||||
pipeline.stage_seeds.each do |seed|
|
||||
seed.user = current_user
|
||||
|
||||
new_builds.map do |build_attributes|
|
||||
create_build(build_attributes)
|
||||
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
|
||||
|
|
|
@ -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)
|
||||
|
|
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' })
|
||||
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
|
||||
|
||||
|
@ -109,12 +111,16 @@ module Ci
|
|||
spinach: { stage: 'test', script: 'spinach', only: ['tags'] })
|
||||
end
|
||||
|
||||
it 'returns stage seeds only assigned to master to master' do
|
||||
seeds = subject.stage_seeds(ref: 'feature', tag: true)
|
||||
let(:pipeline) do
|
||||
create(:ci_empty_pipeline, ref: 'feature', tag: true)
|
||||
end
|
||||
|
||||
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'
|
||||
it 'returns stage seeds only assigned to master to master' do
|
||||
seeds = subject.stage_seeds(pipeline)
|
||||
|
||||
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
|
||||
|
|
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
|
||||
|
||||
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
|
||||
|
||||
|
@ -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…
Reference in a new issue