commit_status.rb 6.2 KB
Newer Older
1 2
# frozen_string_literal: true

3
class CommitStatus < ApplicationRecord
4
  include HasStatus
5
  include Importable
6
  include AfterCommitQueue
7
  include Presentable
J
Jan Provaznik 已提交
8
  include EnumWithNil
K
Kamil Trzcinski 已提交
9

10 11
  prepend_if_ee('::EE::CommitStatus') # rubocop: disable Cop/InjectEnterpriseEditionModule

K
Kamil Trzcinski 已提交
12 13
  self.table_name = 'ci_builds'

14
  belongs_to :user
K
Kamil Trzciński 已提交
15
  belongs_to :project
16
  belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id
17
  belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline'
K
Kamil Trzcinski 已提交
18

19
  delegate :commit, to: :pipeline
D
Douwe Maan 已提交
20
  delegate :sha, :short_sha, to: :pipeline
21

22
  validates :pipeline, presence: true, unless: :importing?
23
  validates :name, presence: true, unless: :importing?
K
Kamil Trzcinski 已提交
24

K
Kamil Trzcinski 已提交
25
  alias_attribute :author, :user
26
  alias_attribute :pipeline_id, :commit_id
27

28
  scope :failed_but_allowed, -> do
29
    where(allow_failure: true, status: [:failed, :canceled])
30
  end
S
Shinya Maeda 已提交
31

32
  scope :exclude_ignored, -> do
33 34 35
    # We want to ignore failed but allowed to fail jobs.
    #
    # TODO, we also skip ignored optional manual actions.
36
    where("allow_failure = ? OR status IN (?)",
37
      false, all_state_names - [:failed, :canceled, :manual])
38
  end
L
Lin Jen-Shin 已提交
39

K
Kamil Trzcinski 已提交
40
  scope :latest, -> { where(retried: [false, nil]) }
41
  scope :retried, -> { where(retried: true) }
42
  scope :ordered, -> { order(:name) }
43 44
  scope :latest_ordered, -> { latest.ordered.includes(project: :namespace) }
  scope :retried_ordered, -> { retried.ordered.includes(project: :namespace) }
K
Kamil Trzciński 已提交
45 46
  scope :before_stage, -> (index) { where('stage_idx < ?', index) }
  scope :for_stage, -> (index) { where(stage_idx: index) }
47
  scope :after_stage, -> (index) { where('stage_idx > ?', index) }
48
  scope :processables, -> { where(type: %w[Ci::Build Ci::Bridge]) }
K
Kamil Trzciński 已提交
49
  scope :for_ids, -> (ids) { where(id: ids) }
K
Kamil Trzcinski 已提交
50

51 52 53 54
  scope :with_preloads, -> do
    preload(:project, :user)
  end

K
Kamil Trzciński 已提交
55 56 57 58 59 60
  scope :with_needs, -> (names = nil) do
    needs = Ci::BuildNeed.scoped_build.select(1)
    needs = needs.where(name: names) if names
    where('EXISTS (?)', needs).preload(:needs)
  end

K
Kamil Trzciński 已提交
61 62 63 64
  scope :without_needs, -> (names = nil) do
    needs = Ci::BuildNeed.scoped_build.select(1)
    needs = needs.where(name: names) if names
    where('NOT EXISTS (?)', needs)
65 66
  end

67 68 69
  # We use `CommitStatusEnums.failure_reasons` here so that EE can more easily
  # extend this `Hash` with new values.
  enum_with_nil failure_reason: ::CommitStatusEnums.failure_reasons
70

71 72 73 74 75
  ##
  # We still create some CommitStatuses outside of CreatePipelineService.
  #
  # These are pages deployments and external statuses.
  #
76
  before_create unless: :importing? do
77
    # rubocop: disable CodeReuse/ServiceClass
78 79
    Ci::EnsureStageService.new(project, user).execute(self) do |stage|
      self.run_after_commit { StageUpdateWorker.perform_async(stage.id) }
80
    end
81
    # rubocop: enable CodeReuse/ServiceClass
82 83
  end

84
  state_machine :status do
85
    event :process do
86
      transition [:skipped, :manual] => :created
87 88
    end

89
    event :enqueue do
T
Tiger 已提交
90 91 92 93
      # A CommitStatus will never have prerequisites, but this event
      # is shared by Ci::Build, which cannot progress unless prerequisites
      # are satisfied.
      transition [:created, :preparing, :skipped, :manual, :scheduled] => :pending, unless: :any_unmet_prerequisites?
94 95
    end

K
Kamil Trzcinski 已提交
96 97 98 99
    event :run do
      transition pending: :running
    end

100
    event :skip do
T
Tiger 已提交
101
      transition [:created, :preparing, :pending] => :skipped
102 103
    end

K
Kamil Trzcinski 已提交
104
    event :drop do
T
Tiger 已提交
105
      transition [:created, :preparing, :pending, :running, :scheduled] => :failed
K
Kamil Trzcinski 已提交
106 107 108
    end

    event :success do
T
Tiger 已提交
109
      transition [:created, :preparing, :pending, :running] => :success
K
Kamil Trzcinski 已提交
110 111 112
    end

    event :cancel do
T
Tiger 已提交
113
      transition [:created, :preparing, :pending, :running, :manual, :scheduled] => :canceled
K
Kamil Trzcinski 已提交
114 115
    end

T
Tiger 已提交
116
    before_transition [:created, :preparing, :skipped, :manual, :scheduled] => :pending do |commit_status|
K
Kamil Trzcinski 已提交
117
      commit_status.queued_at = Time.now
K
Kamil Trzcinski 已提交
118 119
    end

T
Tiger 已提交
120
    before_transition [:created, :preparing, :pending] => :running do |commit_status|
K
Kamil Trzcinski 已提交
121
      commit_status.started_at = Time.now
K
Kamil Trzcinski 已提交
122 123
    end

K
Kamil Trzcinski 已提交
124 125
    before_transition any => [:success, :failed, :canceled] do |commit_status|
      commit_status.finished_at = Time.now
K
Kamil Trzcinski 已提交
126 127
    end

128 129
    before_transition any => :failed do |commit_status, transition|
      failure_reason = transition.args.first
130
      commit_status.failure_reason = CommitStatus.failure_reasons[failure_reason]
131 132
    end

133
    after_transition do |commit_status, transition|
134
      next unless commit_status.project
135
      next if transition.loopback?
136

137
      commit_status.run_after_commit do
138
        if pipeline_id
139
          if complete? || manual?
140
            PipelineProcessWorker.perform_async(pipeline_id, [id])
141
          else
142
            PipelineUpdateWorker.perform_async(pipeline_id)
143
          end
144
        end
145

146 147
        StageUpdateWorker.perform_async(stage_id)
        ExpireJobCacheWorker.perform_async(id)
148
      end
149 150
    end

151
    after_transition any => :failed do |commit_status|
152 153
      next unless commit_status.project

154
      # rubocop: disable CodeReuse/ServiceClass
155
      commit_status.run_after_commit do
D
Douwe Maan 已提交
156
        MergeRequests::AddTodoWhenBuildFailsService
157
          .new(project, nil).execute(self)
158
      end
159
      # rubocop: enable CodeReuse/ServiceClass
160
    end
K
Kamil Trzcinski 已提交
161 162
  end

K
Kamil Trzciński 已提交
163 164 165 166 167
  def self.names
    select(:name)
  end

  def self.status_for_prior_stages(index)
168
    before_stage(index).latest.slow_composite_status || 'success'
K
Kamil Trzciński 已提交
169 170 171
  end

  def self.status_for_names(names)
172
    where(name: names).latest.slow_composite_status || 'success'
K
Kamil Trzciński 已提交
173 174
  end

175
  def locking_enabled?
176
    will_save_change_to_status?
177 178
  end

K
Kamil Trzcinski 已提交
179
  def before_sha
180
    pipeline.before_sha || Gitlab::Git::BLANK_SHA
K
Kamil Trzcinski 已提交
181
  end
K
Kamil Trzcinski 已提交
182

K
Kamil Trzcinski 已提交
183
  def group_name
184
    name.to_s.gsub(%r{\d+[\s:/\\]+\d+\s*}, '').strip
K
Kamil Trzcinski 已提交
185 186
  end

187
  def failed_but_allowed?
188
    allow_failure? && (failed? || canceled?)
189 190
  end

191 192 193 194
  def duration
    calculate_duration
  end

K
Kamil Trzcinski 已提交
195 196 197 198
  def playable?
    false
  end

199 200 201 202
  def retryable?
    false
  end

203 204 205 206
  def cancelable?
    false
  end

207 208 209 210
  def archived?
    false
  end

211 212
  def stuck?
    false
K
Kamil Trzcinski 已提交
213
  end
K
Kamil Trzcinski 已提交
214

215
  def has_trace?
216 217
    false
  end
K
Kamil Trzcinski 已提交
218

T
Tiger 已提交
219 220 221 222
  def any_unmet_prerequisites?
    false
  end

223 224 225 226
  def auto_canceled?
    canceled? && auto_canceled_by_id?
  end

227
  def detailed_status(current_user)
D
Douwe Maan 已提交
228 229 230
    Gitlab::Ci::Status::Factory
      .new(self, current_user)
      .fabricate!
K
Kamil Trzcinski 已提交
231
  end
232

M
Mike Greiling 已提交
233
  def sortable_name
234
    name.to_s.split(/(\d+)/).map do |v|
235 236 237
      v =~ /\d+/ ? v.to_i : v
    end
  end
K
Kamil Trzcinski 已提交
238
end