diff options
| author | Kamil Trzcinski <ayufan@ayufan.eu> | 2016-08-11 15:22:35 +0200 |
|---|---|---|
| committer | Kamil Trzcinski <ayufan@ayufan.eu> | 2016-08-11 15:22:35 +0200 |
| commit | 39203f1adfc6fee3eca50f0cab99ffc597865200 (patch) | |
| tree | 74a06153802ebb0161e3cd5dbeb6195efac89440 /app/models/ci | |
| parent | 2facade8f0700a5832dca94c86e512c1c276d601 (diff) | |
| download | gitlab-ce-39203f1adfc6fee3eca50f0cab99ffc597865200.tar.gz | |
Pre-create all builds for Pipeline when a trigger is received
This change simplifies a Pipeline processing by introducing a special new status: created.
This status is used for all builds that are created for a pipeline.
We are then processing next stages and queueing some of the builds (created -> pending) or skipping them (created -> skipped).
This makes it possible to simplify and solve a few ordering problems with how previously builds were scheduled.
This also allows us to visualise a full pipeline (with created builds).
This also removes an after_touch used for updating a pipeline state parameters.
Right now in various places we explicitly call a reload_status! on pipeline to force it to be updated and saved.
Diffstat (limited to 'app/models/ci')
| -rw-r--r-- | app/models/ci/build.rb | 12 | ||||
| -rw-r--r-- | app/models/ci/pipeline.rb | 84 |
2 files changed, 30 insertions, 66 deletions
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 08f396210c9..88a340379b8 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -16,7 +16,7 @@ module Ci scope :with_artifacts_not_expired, ->() { with_artifacts.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.now) } scope :with_expired_artifacts, ->() { with_artifacts.where('artifacts_expire_at < ?', Time.now) } scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) } - scope :manual_actions, ->() { where(when: :manual) } + scope :manual_actions, ->() { where(when: :manual).relevant } mount_uploader :artifacts_file, ArtifactUploader mount_uploader :artifacts_metadata, ArtifactUploader @@ -65,17 +65,11 @@ module Ci end end - state_machine :status, initial: :pending do + state_machine :status do after_transition pending: :running do |build| build.execute_hooks end - # We use around_transition to create builds for next stage as soon as possible, before the `after_*` is executed - around_transition any => [:success, :failed, :canceled] do |build, block| - block.call - build.pipeline.create_next_builds(build) if build.pipeline - end - after_transition any => [:success, :failed, :canceled] do |build| build.update_coverage build.execute_hooks @@ -461,7 +455,7 @@ module Ci def build_attributes_from_config return {} unless pipeline.config_processor - + pipeline.config_processor.build_attributes(name) end end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index bce6a992af6..718fe3290c1 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -13,11 +13,10 @@ module Ci has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest', foreign_key: :commit_id validates_presence_of :sha + validates_presence_of :ref validates_presence_of :status validate :valid_commit_sha - # Invalidate object and save if when touched - after_touch :update_state after_save :keep_around_commits # ref can't be HEAD or SHA, can only be branch/tag name @@ -90,12 +89,16 @@ module Ci def cancel_running builds.running_or_pending.each(&:cancel) + + reload_status! end def retry_failed(user) builds.latest.failed.select(&:retryable?).each do |build| Ci::Build.retry(build, user) end + + reload_status! end def latest? @@ -109,37 +112,6 @@ module Ci trigger_requests.any? end - def create_builds(user, trigger_request = nil) - ## - # We persist pipeline only if there are builds available - # - return unless config_processor - - build_builds_for_stages(config_processor.stages, user, - 'success', trigger_request) && save - end - - def create_next_builds(build) - return unless config_processor - - # don't create other builds if this one is retried - latest_builds = builds.latest - return unless latest_builds.exists?(build.id) - - # get list of stages after this build - next_stages = config_processor.stages.drop_while { |stage| stage != build.stage } - next_stages.delete(build.stage) - - # get status for all prior builds - prior_builds = latest_builds.where.not(stage: next_stages) - prior_status = prior_builds.status - - # build builds for next stage that has builds available - # and save pipeline if we have builds - build_builds_for_stages(next_stages, build.user, prior_status, - build.trigger_request) && save - end - def retried @retried ||= (statuses.order(id: :desc) - statuses.latest) end @@ -151,6 +123,14 @@ module Ci end end + def config_builds_attributes + return [] unless config_processor + + config_processor. + builds_for_ref(ref, tag?, trigger_requests.first). + sort_by { |build| build[:stage_idx] } + end + def has_warnings? builds.latest.ignored.any? end @@ -182,10 +162,6 @@ module Ci end end - def skip_ci? - git_commit_message =~ /\[(ci skip|skip ci)\]/i if git_commit_message - end - def environments builds.where.not(environment: nil).success.pluck(:environment).uniq end @@ -207,39 +183,33 @@ module Ci Note.for_commit_id(sha) end + def process! + Ci::ProcessPipelineService.new(project, user).execute(self) + reload_status! + end + def predefined_variables [ { key: 'CI_PIPELINE_ID', value: id.to_s, public: true } ] end - private - - def build_builds_for_stages(stages, user, status, trigger_request) - ## - # Note that `Array#any?` implements a short circuit evaluation, so we - # build builds only for the first stage that has builds available. - # - stages.any? do |stage| - CreateBuildsService.new(self). - execute(stage, user, status, trigger_request). - any?(&:active?) - end - end - - def update_state + def reload_status! statuses.reload - self.status = if yaml_errors.blank? - statuses.latest.status || 'skipped' - else - 'failed' - end + self.status = + if yaml_errors.blank? + statuses.latest.status || 'skipped' + else + 'failed' + end self.started_at = statuses.started_at self.finished_at = statuses.finished_at self.duration = statuses.latest.duration save end + private + def keep_around_commits return unless project |
