commit_status.rb 5.4 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

K
Kamil Trzcinski 已提交
10 11
  self.table_name = 'ci_builds'

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

17
  delegate :commit, to: :pipeline
D
Douwe Maan 已提交
18
  delegate :sha, :short_sha, to: :pipeline
19

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

K
Kamil Trzcinski 已提交
23
  alias_attribute :author, :user
24
  alias_attribute :pipeline_id, :commit_id
25

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

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

K
Kamil Trzcinski 已提交
38
  scope :latest, -> { where(retried: [false, nil]) }
39
  scope :retried, -> { where(retried: true) }
40
  scope :ordered, -> { order(:name) }
41 42
  scope :latest_ordered, -> { latest.ordered.includes(project: :namespace) }
  scope :retried_ordered, -> { retried.ordered.includes(project: :namespace) }
43
  scope :after_stage, -> (index) { where('stage_idx > ?', index) }
44
  scope :processables, -> { where(type: %w[Ci::Build Ci::Bridge]) }
K
Kamil Trzcinski 已提交
45

K
Kamil Trzciński 已提交
46 47 48 49 50 51
  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

52 53 54
  # 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
55

56 57 58 59 60
  ##
  # We still create some CommitStatuses outside of CreatePipelineService.
  #
  # These are pages deployments and external statuses.
  #
61
  before_create unless: :importing? do
62
    # rubocop: disable CodeReuse/ServiceClass
63 64
    Ci::EnsureStageService.new(project, user).execute(self) do |stage|
      self.run_after_commit { StageUpdateWorker.perform_async(stage.id) }
65
    end
66
    # rubocop: enable CodeReuse/ServiceClass
67 68
  end

69
  state_machine :status do
70
    event :process do
71
      transition [:skipped, :manual] => :created
72 73
    end

74
    event :enqueue do
T
Tiger 已提交
75 76 77 78
      # 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?
79 80
    end

K
Kamil Trzcinski 已提交
81 82 83 84
    event :run do
      transition pending: :running
    end

85
    event :skip do
T
Tiger 已提交
86
      transition [:created, :preparing, :pending] => :skipped
87 88
    end

K
Kamil Trzcinski 已提交
89
    event :drop do
T
Tiger 已提交
90
      transition [:created, :preparing, :pending, :running, :scheduled] => :failed
K
Kamil Trzcinski 已提交
91 92 93
    end

    event :success do
T
Tiger 已提交
94
      transition [:created, :preparing, :pending, :running] => :success
K
Kamil Trzcinski 已提交
95 96 97
    end

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

T
Tiger 已提交
101
    before_transition [:created, :preparing, :skipped, :manual, :scheduled] => :pending do |commit_status|
K
Kamil Trzcinski 已提交
102
      commit_status.queued_at = Time.now
K
Kamil Trzcinski 已提交
103 104
    end

T
Tiger 已提交
105
    before_transition [:created, :preparing, :pending] => :running do |commit_status|
K
Kamil Trzcinski 已提交
106
      commit_status.started_at = Time.now
K
Kamil Trzcinski 已提交
107 108
    end

K
Kamil Trzcinski 已提交
109 110
    before_transition any => [:success, :failed, :canceled] do |commit_status|
      commit_status.finished_at = Time.now
K
Kamil Trzcinski 已提交
111 112
    end

113 114
    before_transition any => :failed do |commit_status, transition|
      failure_reason = transition.args.first
115
      commit_status.failure_reason = CommitStatus.failure_reasons[failure_reason]
116 117
    end

118
    after_transition do |commit_status, transition|
119
      next unless commit_status.project
120
      next if transition.loopback?
121

122
      commit_status.run_after_commit do
123
        if pipeline_id
124
          if complete? || manual?
K
Kamil Trzciński 已提交
125
            BuildProcessWorker.perform_async(id)
126
          else
127
            PipelineUpdateWorker.perform_async(pipeline_id)
128
          end
129
        end
130

131 132
        StageUpdateWorker.perform_async(stage_id)
        ExpireJobCacheWorker.perform_async(id)
133
      end
134 135
    end

136
    after_transition any => :failed do |commit_status|
137 138
      next unless commit_status.project

139
      # rubocop: disable CodeReuse/ServiceClass
140
      commit_status.run_after_commit do
D
Douwe Maan 已提交
141
        MergeRequests::AddTodoWhenBuildFailsService
142
          .new(project, nil).execute(self)
143
      end
144
      # rubocop: enable CodeReuse/ServiceClass
145
    end
K
Kamil Trzcinski 已提交
146 147
  end

148
  def locking_enabled?
149
    will_save_change_to_status?
150 151
  end

K
Kamil Trzcinski 已提交
152
  def before_sha
153
    pipeline.before_sha || Gitlab::Git::BLANK_SHA
K
Kamil Trzcinski 已提交
154
  end
K
Kamil Trzcinski 已提交
155

K
Kamil Trzcinski 已提交
156
  def group_name
157
    name.to_s.gsub(%r{\d+[\s:/\\]+\d+\s*}, '').strip
K
Kamil Trzcinski 已提交
158 159
  end

160
  def failed_but_allowed?
161
    allow_failure? && (failed? || canceled?)
162 163
  end

164 165 166 167
  def duration
    calculate_duration
  end

K
Kamil Trzcinski 已提交
168 169 170 171
  def playable?
    false
  end

172 173 174 175
  def retryable?
    false
  end

176 177 178 179
  def cancelable?
    false
  end

180 181 182 183
  def archived?
    false
  end

184 185
  def stuck?
    false
K
Kamil Trzcinski 已提交
186
  end
K
Kamil Trzcinski 已提交
187

188
  def has_trace?
189 190
    false
  end
K
Kamil Trzcinski 已提交
191

T
Tiger 已提交
192 193 194 195
  def any_unmet_prerequisites?
    false
  end

196 197 198 199
  def auto_canceled?
    canceled? && auto_canceled_by_id?
  end

200
  def detailed_status(current_user)
D
Douwe Maan 已提交
201 202 203
    Gitlab::Ci::Status::Factory
      .new(self, current_user)
      .fabricate!
K
Kamil Trzcinski 已提交
204
  end
205

M
Mike Greiling 已提交
206
  def sortable_name
207
    name.to_s.split(/(\d+)/).map do |v|
208 209 210
      v =~ /\d+/ ? v.to_i : v
    end
  end
K
Kamil Trzcinski 已提交
211
end