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

K
Kamil Trzcinski 已提交
3
class CommitStatus < ActiveRecord::Base
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

46 47 48
  # 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
49

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

63
  state_machine :status do
64
    event :process do
65
      transition [:skipped, :manual] => :created
66 67
    end

68
    event :enqueue do
69
      transition [:created, :skipped, :manual, :scheduled] => :pending
70 71
    end

K
Kamil Trzcinski 已提交
72 73 74 75
    event :run do
      transition pending: :running
    end

76 77 78 79
    event :skip do
      transition [:created, :pending] => :skipped
    end

K
Kamil Trzcinski 已提交
80
    event :drop do
81
      transition [:created, :pending, :running, :scheduled] => :failed
K
Kamil Trzcinski 已提交
82 83 84
    end

    event :success do
85
      transition [:created, :pending, :running] => :success
K
Kamil Trzcinski 已提交
86 87 88
    end

    event :cancel do
89
      transition [:created, :pending, :running, :manual, :scheduled] => :canceled
K
Kamil Trzcinski 已提交
90 91
    end

92
    before_transition [:created, :skipped, :manual, :scheduled] => :pending do |commit_status|
K
Kamil Trzcinski 已提交
93
      commit_status.queued_at = Time.now
K
Kamil Trzcinski 已提交
94 95
    end

K
Kamil Trzcinski 已提交
96 97
    before_transition [:created, :pending] => :running do |commit_status|
      commit_status.started_at = Time.now
K
Kamil Trzcinski 已提交
98 99
    end

K
Kamil Trzcinski 已提交
100 101
    before_transition any => [:success, :failed, :canceled] do |commit_status|
      commit_status.finished_at = Time.now
K
Kamil Trzcinski 已提交
102 103
    end

104 105
    before_transition any => :failed do |commit_status, transition|
      failure_reason = transition.args.first
106
      commit_status.failure_reason = CommitStatus.failure_reasons[failure_reason]
107 108
    end

109
    after_transition do |commit_status, transition|
110
      next unless commit_status.project
111
      next if transition.loopback?
112

113
      commit_status.run_after_commit do
114
        if pipeline_id
115
          if complete? || manual?
116
            PipelineProcessWorker.perform_async(pipeline_id)
117
          else
118
            PipelineUpdateWorker.perform_async(pipeline_id)
119
          end
120
        end
121

122 123
        StageUpdateWorker.perform_async(stage_id)
        ExpireJobCacheWorker.perform_async(id)
124
      end
125 126
    end

127
    after_transition any => :failed do |commit_status|
128 129
      next unless commit_status.project

130
      # rubocop: disable CodeReuse/ServiceClass
131
      commit_status.run_after_commit do
D
Douwe Maan 已提交
132
        MergeRequests::AddTodoWhenBuildFailsService
133
          .new(project, nil).execute(self)
134
      end
135
      # rubocop: enable CodeReuse/ServiceClass
136
    end
K
Kamil Trzcinski 已提交
137 138
  end

139 140 141 142
  def locking_enabled?
    status_changed?
  end

K
Kamil Trzcinski 已提交
143
  def before_sha
144
    pipeline.before_sha || Gitlab::Git::BLANK_SHA
K
Kamil Trzcinski 已提交
145
  end
K
Kamil Trzcinski 已提交
146

K
Kamil Trzcinski 已提交
147
  def group_name
148
    name.to_s.gsub(%r{\d+[\s:/\\]+\d+\s*}, '').strip
K
Kamil Trzcinski 已提交
149 150
  end

151
  def failed_but_allowed?
152
    allow_failure? && (failed? || canceled?)
153 154
  end

155 156 157 158
  def duration
    calculate_duration
  end

K
Kamil Trzcinski 已提交
159 160 161 162
  def playable?
    false
  end

163 164 165 166
  def retryable?
    false
  end

167 168 169 170
  def cancelable?
    false
  end

171 172 173 174
  def archived?
    false
  end

175 176
  def stuck?
    false
K
Kamil Trzcinski 已提交
177
  end
K
Kamil Trzcinski 已提交
178

179
  def has_trace?
180 181
    false
  end
K
Kamil Trzcinski 已提交
182

183 184 185 186
  def auto_canceled?
    canceled? && auto_canceled_by_id?
  end

187
  def detailed_status(current_user)
D
Douwe Maan 已提交
188 189 190
    Gitlab::Ci::Status::Factory
      .new(self, current_user)
      .fabricate!
K
Kamil Trzcinski 已提交
191
  end
192

M
Mike Greiling 已提交
193
  def sortable_name
194
    name.to_s.split(/(\d+)/).map do |v|
195 196 197
      v =~ /\d+/ ? v.to_i : v
    end
  end
K
Kamil Trzcinski 已提交
198
end