diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 87ec0df257a..6223e7943f8 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -99,7 +99,7 @@ class Projects::PipelinesController < Projects::ApplicationController end def stage - @stage = pipeline.stage(params[:stage]) + @stage = pipeline.legacy_stage(params[:stage]) return not_found unless @stage respond_to do |format| diff --git a/app/models/ci/legacy_stage.rb b/app/models/ci/legacy_stage.rb new file mode 100644 index 00000000000..9b536af672b --- /dev/null +++ b/app/models/ci/legacy_stage.rb @@ -0,0 +1,64 @@ +module Ci + # Currently this is artificial object, constructed dynamically + # We should migrate this object to actual database record in the future + class LegacyStage + include StaticModel + + attr_reader :pipeline, :name + + delegate :project, to: :pipeline + + def initialize(pipeline, name:, status: nil, warnings: nil) + @pipeline = pipeline + @name = name + @status = status + @warnings = warnings + end + + def groups + @groups ||= statuses.ordered.latest + .sort_by(&:sortable_name).group_by(&:group_name) + .map do |group_name, grouped_statuses| + Ci::Group.new(self, name: group_name, jobs: grouped_statuses) + end + end + + def to_param + name + end + + def statuses_count + @statuses_count ||= statuses.count + end + + def status + @status ||= statuses.latest.status + end + + def detailed_status(current_user) + Gitlab::Ci::Status::Stage::Factory + .new(self, current_user) + .fabricate! + end + + def statuses + @statuses ||= pipeline.statuses.where(stage: name) + end + + def builds + @builds ||= pipeline.builds.where(stage: name) + end + + def success? + status.to_s == 'success' + end + + def has_warnings? + if @warnings.is_a?(Integer) + @warnings > 0 + else + statuses.latest.failed_but_allowed.any? + end + end + end +end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 425ca9278eb..e28ba91bd64 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -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 :source, exclusion: { in: %w(unknown), unless: :importing? }, on: :create @@ -162,21 +163,21 @@ module Ci where.not(duration: nil).sum(:duration) end - def stage(name) - stage = Ci::Stage.new(self, name: name) - stage unless stage.statuses_count.zero? - end - def stages_count statuses.select(:stage).distinct.count end - def stages_name + def stages_names statuses.order(:stage_idx).distinct. pluck(:stage, :stage_idx).map(&:first) end - def stages + def legacy_stage(name) + stage = Ci::LegacyStage.new(self, name: name) + stage unless stage.statuses_count.zero? + end + + def legacy_stages # TODO, this needs refactoring, see gitlab-ce#26481. stages_query = statuses @@ -191,7 +192,7 @@ module Ci .pluck('sg.stage', status_sql, "(#{warnings_sql})") stages_with_statuses.map do |stage| - Ci::Stage.new(self, Hash[%i[name status warnings].zip(stage)]) + Ci::LegacyStage.new(self, Hash[%i[name status warnings].zip(stage)]) end end @@ -291,12 +292,14 @@ module Ci end end - def config_builds_attributes + def stage_seeds return [] unless config_processor - config_processor. - builds_for_ref(ref, tag?, trigger_requests.first). - sort_by { |build| build[:stage_idx] } + @stage_seeds ||= config_processor.stage_seeds(self) + end + + def has_stage_seeds? + stage_seeds.any? end def has_warnings? @@ -304,7 +307,7 @@ module Ci end def config_processor - return nil unless ci_yaml_file + return unless ci_yaml_file return @config_processor if defined?(@config_processor) @config_processor ||= begin diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb index 9bda3186c30..59570924c8d 100644 --- a/app/models/ci/stage.rb +++ b/app/models/ci/stage.rb @@ -1,64 +1,11 @@ module Ci - # Currently this is artificial object, constructed dynamically - # We should migrate this object to actual database record in the future - class Stage - include StaticModel + class Stage < ActiveRecord::Base + extend Ci::Model - attr_reader :pipeline, :name + belongs_to :project + belongs_to :pipeline - delegate :project, to: :pipeline - - def initialize(pipeline, name:, status: nil, warnings: nil) - @pipeline = pipeline - @name = name - @status = status - @warnings = warnings - end - - def groups - @groups ||= statuses.ordered.latest - .sort_by(&:sortable_name).group_by(&:group_name) - .map do |group_name, grouped_statuses| - Ci::Group.new(self, name: group_name, jobs: grouped_statuses) - end - end - - def to_param - name - end - - def statuses_count - @statuses_count ||= statuses.count - end - - def status - @status ||= statuses.latest.status - end - - def detailed_status(current_user) - Gitlab::Ci::Status::Stage::Factory - .new(self, current_user) - .fabricate! - end - - def statuses - @statuses ||= pipeline.statuses.where(stage: name) - end - - def builds - @builds ||= pipeline.builds.where(stage: name) - end - - def success? - status.to_s == 'success' - end - - def has_warnings? - if @warnings.is_a?(Integer) - @warnings > 0 - else - statuses.latest.failed_but_allowed.any? - end - end + has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id + has_many :builds, foreign_key: :commit_id end end diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 8b4ed49269d..55c16f7e1fd 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -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 diff --git a/app/serializers/pipeline_details_entity.rb b/app/serializers/pipeline_details_entity.rb index d58572a5f87..130968a44c1 100644 --- a/app/serializers/pipeline_details_entity.rb +++ b/app/serializers/pipeline_details_entity.rb @@ -1,6 +1,6 @@ class PipelineDetailsEntity < PipelineEntity expose :details do - expose :stages, using: StageEntity + expose :legacy_stages, as: :stages, using: StageEntity expose :artifacts, using: BuildArtifactEntity expose :manual_actions, using: BuildActionEntity end diff --git a/app/services/ci/create_pipeline_builds_service.rb b/app/services/ci/create_pipeline_builds_service.rb deleted file mode 100644 index 70fb2c5e38f..00000000000 --- a/app/services/ci/create_pipeline_builds_service.rb +++ /dev/null @@ -1,51 +0,0 @@ -module Ci - class CreatePipelineBuildsService < BaseService - attr_reader :pipeline - - def execute(pipeline) - @pipeline = pipeline - - new_builds.map do |build_attributes| - create_build(build_attributes) - 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. - reject { |build| existing_build_names.include?(build[:name]) } - end - - def existing_build_names - @existing_build_names ||= pipeline.builds.pluck(:name) - end - - def trigger_request - return @trigger_request if defined?(@trigger_request) - - @trigger_request ||= pipeline.trigger_requests.first - end - end -end diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb index 13baa63220d..bffec216819 100644 --- a/app/services/ci/create_pipeline_service.rb +++ b/app/services/ci/create_pipeline_service.rb @@ -43,14 +43,14 @@ module Ci return pipeline end - unless pipeline.config_builds_attributes.present? - return error('No builds for this pipeline.') + unless pipeline.has_stage_seeds? + return error('No stages / jobs for this pipeline.') end Ci::Pipeline.transaction do update_merge_requests_head_pipeline if pipeline.save - Ci::CreatePipelineBuildsService + Ci::CreatePipelineStagesService .new(project, current_user) .execute(pipeline) end diff --git a/app/services/ci/create_pipeline_stages_service.rb b/app/services/ci/create_pipeline_stages_service.rb new file mode 100644 index 00000000000..f2c175adee6 --- /dev/null +++ b/app/services/ci/create_pipeline_stages_service.rb @@ -0,0 +1,20 @@ +module Ci + class CreatePipelineStagesService < BaseService + def execute(pipeline) + 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 + end +end diff --git a/app/services/ci/retry_build_service.rb b/app/services/ci/retry_build_service.rb index f51e9fd1d54..6372e5755db 100644 --- a/app/services/ci/retry_build_service.rb +++ b/app/services/ci/retry_build_service.rb @@ -1,7 +1,7 @@ module Ci class RetryBuildService < ::BaseService CLONE_ACCESSORS = %i[pipeline project ref tag options commands name - allow_failure stage stage_idx trigger_request + allow_failure stage_id stage stage_idx trigger_request yaml_variables when environment coverage_regex description tag_list].freeze diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml index 0aef5822f81..aab50310234 100644 --- a/app/views/projects/commit/_commit_box.html.haml +++ b/app/views/projects/commit/_commit_box.html.haml @@ -72,8 +72,8 @@ Pipeline = link_to "##{last_pipeline.id}", namespace_project_pipeline_path(@project.namespace, @project, last_pipeline.id) = ci_label_for_status(last_pipeline.status) - - if last_pipeline.stages.any? - with #{"stage".pluralize(last_pipeline.stages.count)} + - if last_pipeline.stages_count.nonzero? + with #{"stage".pluralize(last_pipeline.stages_count)} .mr-widget-pipeline-graph = render 'shared/mini_pipeline_graph', pipeline: last_pipeline, klass: 'js-commit-pipeline-graph' in diff --git a/app/views/projects/jobs/_sidebar.html.haml b/app/views/projects/jobs/_sidebar.html.haml index f700b5c9455..09d4ddc243b 100644 --- a/app/views/projects/jobs/_sidebar.html.haml +++ b/app/views/projects/jobs/_sidebar.html.haml @@ -111,7 +111,7 @@ %span.stage-selection More = icon('chevron-down') %ul.dropdown-menu - - @build.pipeline.stages.each do |stage| + - @build.pipeline.legacy_stages.each do |stage| %li %a.stage-item= stage.name diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml index 01cf2cc80e5..85550e8fd32 100644 --- a/app/views/projects/pipelines/_with_tabs.html.haml +++ b/app/views/projects/pipelines/_with_tabs.html.haml @@ -42,7 +42,7 @@ %th %th Coverage %th - = render partial: "projects/stage/stage", collection: pipeline.stages, as: :stage + = render partial: "projects/stage/stage", collection: pipeline.legacy_stages, as: :stage - if failed_builds.present? #js-tab-failures.build-failures.tab-pane - failed_builds.each_with_index do |build, index| diff --git a/app/views/shared/_mini_pipeline_graph.html.haml b/app/views/shared/_mini_pipeline_graph.html.haml index 07970ad9cba..aa93572bf94 100644 --- a/app/views/shared/_mini_pipeline_graph.html.haml +++ b/app/views/shared/_mini_pipeline_graph.html.haml @@ -1,5 +1,5 @@ .stage-cell - - pipeline.stages.each do |stage| + - pipeline.legacy_stages.each do |stage| - if stage.status - detailed_status = stage.detailed_status(current_user) - icon_status = "#{detailed_status.icon}_borderless" diff --git a/changelogs/unreleased/feature-gb-persist-pipeline-stages.yml b/changelogs/unreleased/feature-gb-persist-pipeline-stages.yml new file mode 100644 index 00000000000..1404b342359 --- /dev/null +++ b/changelogs/unreleased/feature-gb-persist-pipeline-stages.yml @@ -0,0 +1,4 @@ +--- +title: Persist pipeline stages in the database +merge_request: 11790 +author: diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index b06474cda7f..22af2671b18 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -50,10 +50,23 @@ module Ci end end + def stage_seeds(pipeline) + trigger_request = pipeline.trigger_requests.first + + 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) job = @jobs[name.to_sym] || {} - { - stage_idx: @stages.index(job[:stage]), + + { stage_idx: @stages.index(job[:stage]), stage: job[:stage], commands: job[:commands], tag_list: job[:tags] || [], @@ -71,8 +84,7 @@ module Ci dependencies: job[:dependencies], after_script: job[:after_script], environment: job[:environment] - }.compact - } + }.compact } end def self.validation_message(content) diff --git a/lib/gitlab/ci/stage/seed.rb b/lib/gitlab/ci/stage/seed.rb new file mode 100644 index 00000000000..f81f9347b4d --- /dev/null +++ b/lib/gitlab/ci/stage/seed.rb @@ -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 diff --git a/lib/gitlab/data_builder/pipeline.rb b/lib/gitlab/data_builder/pipeline.rb index 182a30fd74d..e47fb85b5ee 100644 --- a/lib/gitlab/data_builder/pipeline.rb +++ b/lib/gitlab/data_builder/pipeline.rb @@ -22,7 +22,7 @@ module Gitlab sha: pipeline.sha, before_sha: pipeline.before_sha, status: pipeline.status, - stages: pipeline.stages_name, + stages: pipeline.stages_names, created_at: pipeline.created_at, finished_at: pipeline.finished_at, duration: pipeline.duration diff --git a/lib/gitlab/import_export/import_export.yml b/lib/gitlab/import_export/import_export.yml index d0f3cf2b514..ff2b1d08c3c 100644 --- a/lib/gitlab/import_export/import_export.yml +++ b/lib/gitlab/import_export/import_export.yml @@ -38,6 +38,7 @@ project_tree: - notes: - :author - :events + - :stages - :statuses - :triggers - :pipeline_schedules diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb index 19e23a4715f..695852526cb 100644 --- a/lib/gitlab/import_export/relation_factory.rb +++ b/lib/gitlab/import_export/relation_factory.rb @@ -3,6 +3,7 @@ module Gitlab class RelationFactory OVERRIDES = { snippets: :project_snippets, pipelines: 'Ci::Pipeline', + stages: 'Ci::Stage', statuses: 'commit_status', triggers: 'Ci::Trigger', pipeline_schedules: 'Ci::PipelineSchedule', diff --git a/spec/factories/ci/stages.rb b/spec/factories/ci/stages.rb index d37eabb3e8c..d3c8bf9d54f 100644 --- a/spec/factories/ci/stages.rb +++ b/spec/factories/ci/stages.rb @@ -1,5 +1,5 @@ FactoryGirl.define do - factory :ci_stage, class: Ci::Stage do + factory :ci_stage, class: Ci::LegacyStage do skip_create transient do @@ -10,7 +10,9 @@ FactoryGirl.define do end initialize_with do - Ci::Stage.new(pipeline, name: name, status: status, warnings: warnings) + Ci::LegacyStage.new(pipeline, name: name, + status: status, + warnings: warnings) end end end diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index fe2c00bb2ca..72b9cde10e7 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -1,7 +1,8 @@ require 'spec_helper' module Ci - describe GitlabCiYamlProcessor, lib: true do + describe GitlabCiYamlProcessor, :lib do + subject { described_class.new(config, path) } let(:path) { 'path' } describe 'our current .gitlab-ci.yml' do @@ -82,6 +83,48 @@ module Ci end end + describe '#stage_seeds' do + context 'when no refs policy is specified' do + let(:config) do + YAML.dump(production: { stage: 'deploy', script: 'cap prod' }, + rspec: { stage: 'test', script: 'rspec' }, + spinach: { stage: 'test', script: 'spinach' }) + end + + let(:pipeline) { create(:ci_empty_pipeline) } + + 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 + + context 'when refs policy is specified' do + let(:config) do + YAML.dump(production: { stage: 'deploy', script: 'cap prod', only: ['master'] }, + 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(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 + describe "#builds_for_ref" do let(:type) { 'test' } diff --git a/spec/lib/gitlab/ci/stage/seed_spec.rb b/spec/lib/gitlab/ci/stage/seed_spec.rb new file mode 100644 index 00000000000..d7e91a5a62c --- /dev/null +++ b/spec/lib/gitlab/ci/stage/seed_spec.rb @@ -0,0 +1,57 @@ +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(ref: 'master')) + expect(subject.builds).to all(include(tag: false)) + expect(subject.builds).to all(include(project: pipeline.project)) + expect(subject.builds) + .to all(include(trigger_request: pipeline.trigger_requests.first)) + end + end + + describe '#user=' do + let(:user) { build(: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? }) + expect(pipeline.stages) + .to all(satisfy { |stage| stage.pipeline.present? }) + expect(pipeline.stages) + .to all(satisfy { |stage| stage.project.present? }) + end + end +end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 21296a36729..412eb33b35b 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -91,6 +91,7 @@ merge_request_diff: pipelines: - project - user +- stages - statuses - builds - trigger_requests @@ -104,9 +105,15 @@ pipelines: - artifacts - pipeline_schedule - merge_requests +stages: +- project +- pipeline +- statuses +- builds statuses: - project - pipeline +- stage - user - auto_canceled_by variables: diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml index de7852aa810..50ff6ecc1e0 100644 --- a/spec/lib/gitlab/import_export/safe_model_attributes.yml +++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml @@ -175,6 +175,7 @@ MergeRequestDiff: Ci::Pipeline: - id - project_id +- source - ref - sha - before_sha @@ -192,7 +193,13 @@ Ci::Pipeline: - lock_version - auto_canceled_by_id - pipeline_schedule_id -- source +Ci::Stage: +- id +- name +- project_id +- pipeline_id +- created_at +- updated_at CommitStatus: - id - project_id diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/legacy_stage_spec.rb similarity index 99% rename from spec/models/ci/stage_spec.rb rename to spec/models/ci/legacy_stage_spec.rb index 8f6ab908987..48116c7e701 100644 --- a/spec/models/ci/stage_spec.rb +++ b/spec/models/ci/legacy_stage_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::Stage, models: true do +describe Ci::LegacyStage, :models do let(:stage) { build(:ci_stage) } let(:pipeline) { stage.pipeline } let(:stage_name) { stage.name } diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index ae1b01b76ab..b50c7700bd3 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -224,8 +224,19 @@ describe Ci::Pipeline, models: true do status: 'success') end - describe '#stages' do - subject { pipeline.stages } + describe '#stage_seeds' do + let(:pipeline) do + create(:ci_pipeline, config: { rspec: { script: 'rake' } }) + end + + it 'returns preseeded stage seeds object' do + expect(pipeline.stage_seeds).to all(be_a Gitlab::Ci::Stage::Seed) + expect(pipeline.stage_seeds.count).to eq 1 + end + end + + describe '#legacy_stages' do + subject { pipeline.legacy_stages } context 'stages list' do it 'returns ordered list of stages' do @@ -274,7 +285,7 @@ describe Ci::Pipeline, models: true do end it 'populates stage with correct number of warnings' do - deploy_stage = pipeline.stages.third + deploy_stage = pipeline.legacy_stages.third expect(deploy_stage).not_to receive(:statuses) expect(deploy_stage).to have_warnings @@ -288,22 +299,22 @@ describe Ci::Pipeline, models: true do end end - describe '#stages_name' do + describe '#stages_names' do it 'returns a valid names of stages' do - expect(pipeline.stages_name).to eq(%w(build test deploy)) + expect(pipeline.stages_names).to eq(%w(build test deploy)) end end end - describe '#stage' do - subject { pipeline.stage('test') } + describe '#legacy_stage' do + subject { pipeline.legacy_stage('test') } context 'with status in stage' do before do create(:commit_status, pipeline: pipeline, stage: 'test') end - it { expect(subject).to be_a Ci::Stage } + it { expect(subject).to be_a Ci::LegacyStage } it { expect(subject.name).to eq 'test' } it { expect(subject.statuses).not_to be_empty } end @@ -524,6 +535,20 @@ describe Ci::Pipeline, models: true do end end + describe '#has_stage_seeds?' do + context 'when pipeline has stage seeds' do + subject { build(:ci_pipeline_with_one_job) } + + it { is_expected.to have_stage_seeds } + end + + context 'when pipeline does not have stage seeds' do + subject { create(:ci_pipeline_without_jobs) } + + it { is_expected.not_to have_stage_seeds } + end + end + describe '#has_warnings?' do subject { pipeline.has_warnings? } diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index 597c3947e71..e9c2b865b47 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Ci::CreatePipelineService, services: true do +describe Ci::CreatePipelineService, :services do let(:project) { create(:project, :repository) } let(:user) { create(:admin) } @@ -30,6 +30,7 @@ describe Ci::CreatePipelineService, services: true do it 'creates a pipeline' do expect(pipeline).to be_kind_of(Ci::Pipeline) expect(pipeline).to be_valid + expect(pipeline).to be_persisted expect(pipeline).to be_push expect(pipeline).to eq(project.pipelines.last) expect(pipeline).to have_attributes(user: user) diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb index 2bd5af25847..ef9927c5969 100644 --- a/spec/services/ci/retry_build_service_spec.rb +++ b/spec/services/ci/retry_build_service_spec.rb @@ -18,20 +18,31 @@ describe Ci::RetryBuildService, :services do updated_at started_at finished_at queued_at erased_by erased_at auto_canceled_by].freeze - # TODO, move stage_id accessor to CLONE_ACCESSOR in a follow-up MR. IGNORE_ACCESSORS = %i[type lock_version target_url base_tags commit_id deployments erased_by_id last_deployment project_id runner_id tag_taggings taggings tags trigger_request_id - user_id auto_canceled_by_id retried stage_id].freeze + user_id auto_canceled_by_id retried].freeze shared_examples 'build duplication' do + let(:stage) do + # TODO, we still do not have factory for new stages, we will need to + # switch existing factory to persist stages, instead of using LegacyStage + # + Ci::Stage.create!(project: project, pipeline: pipeline, name: 'test') + end + let(:build) do create(:ci_build, :failed, :artifacts_expired, :erased, :queued, :coverage, :tags, :allowed_to_fail, :on_tag, - :teardown_environment, :triggered, :trace, - description: 'some build', pipeline: pipeline, - auto_canceled_by: create(:ci_empty_pipeline)) + :triggered, :trace, :teardown_environment, + description: 'my-job', stage: 'test', pipeline: pipeline, + auto_canceled_by: create(:ci_empty_pipeline)) do |build| + ## + # TODO, workaround for FactoryGirl limitation when having both + # stage (text) and stage_id (integer) columns in the table. + build.stage_id = stage.id + end end describe 'clone accessors' do