Read about our upcoming Code of Conduct on this issue

This instance will be upgraded to Heptapod 0.28.1 on 2022-01-26 at 16:00 UTC+1 (a few minutes of down time)

has_status.rb 4.86 KB
Newer Older
1
2
# frozen_string_literal: true

3
module HasStatus
Kamil Trzcinski's avatar
Kamil Trzcinski committed
4
5
  extend ActiveSupport::Concern

Douwe Maan's avatar
Douwe Maan committed
6
  DEFAULT_STATUS = 'created'.freeze
7
  BLOCKED_STATUS = %w[manual scheduled].freeze
Tiger's avatar
Tiger committed
8
  AVAILABLE_STATUSES = %w[created preparing pending running success failed canceled skipped manual scheduled].freeze
9
  STARTED_STATUSES = %w[running success failed skipped manual scheduled].freeze
Tiger's avatar
Tiger committed
10
  ACTIVE_STATUSES = %w[preparing pending running].freeze
Douwe Maan's avatar
Douwe Maan committed
11
  COMPLETED_STATUSES = %w[success failed canceled skipped].freeze
Tiger's avatar
Tiger committed
12
  ORDERED_STATUSES = %w[failed preparing pending running manual scheduled canceled success skipped created].freeze
13
  STATUSES_ENUM = { created: 0, pending: 1, running: 2, success: 3,
14
                    failed: 4, canceled: 5, skipped: 6, manual: 7,
Tiger's avatar
Tiger committed
15
                    scheduled: 8, preparing: 9 }.freeze
16

17
18
  UnknownStatusError = Class.new(StandardError)

19
  class_methods do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
20
    def status_sql
21
22
      scope_relevant = respond_to?(:exclude_ignored) ? exclude_ignored : all
      scope_warnings = respond_to?(:failed_but_allowed) ? failed_but_allowed : none
23

24
25
26
27
      builds = scope_relevant.select('count(*)').to_sql
      created = scope_relevant.created.select('count(*)').to_sql
      success = scope_relevant.success.select('count(*)').to_sql
      manual = scope_relevant.manual.select('count(*)').to_sql
28
      scheduled = scope_relevant.scheduled.select('count(*)').to_sql
Tiger's avatar
Tiger committed
29
      preparing = scope_relevant.preparing.select('count(*)').to_sql
30
31
32
33
34
      pending = scope_relevant.pending.select('count(*)').to_sql
      running = scope_relevant.running.select('count(*)').to_sql
      skipped = scope_relevant.skipped.select('count(*)').to_sql
      canceled = scope_relevant.canceled.select('count(*)').to_sql
      warnings = scope_warnings.select('count(*) > 0').to_sql.presence || 'false'
Kamil Trzcinski's avatar
Kamil Trzcinski committed
35

Heinrich Lee Yu's avatar
Heinrich Lee Yu committed
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
      Arel.sql(
        "(CASE
          WHEN (#{builds})=(#{skipped}) AND (#{warnings}) THEN 'success'
          WHEN (#{builds})=(#{skipped}) THEN 'skipped'
          WHEN (#{builds})=(#{success}) THEN 'success'
          WHEN (#{builds})=(#{created}) THEN 'created'
          WHEN (#{builds})=(#{preparing}) THEN 'preparing'
          WHEN (#{builds})=(#{success})+(#{skipped}) THEN 'success'
          WHEN (#{builds})=(#{success})+(#{skipped})+(#{canceled}) THEN 'canceled'
          WHEN (#{builds})=(#{created})+(#{skipped})+(#{pending}) THEN 'pending'
          WHEN (#{running})+(#{pending})>0 THEN 'running'
          WHEN (#{manual})>0 THEN 'manual'
          WHEN (#{scheduled})>0 THEN 'scheduled'
          WHEN (#{preparing})>0 THEN 'preparing'
          WHEN (#{created})>0 THEN 'running'
          ELSE 'failed'
        END)"
      )
Kamil Trzcinski's avatar
Kamil Trzcinski committed
54
55
    end

Kamil Trzcinski's avatar
Kamil Trzcinski committed
56
    def status
57
      all.pluck(status_sql).first
Kamil Trzcinski's avatar
Kamil Trzcinski committed
58
59
    end

60
61
62
63
64
    def started_at
      all.minimum(:started_at)
    end

    def finished_at
65
      all.maximum(:finished_at)
66
    end
67
68
69
70

    def all_state_names
      state_machines.values.flat_map(&:states).flat_map { |s| s.map(&:name) }
    end
Matija Čupić's avatar
Matija Čupić committed
71
72
73
74

    def completed_statuses
      COMPLETED_STATUSES.map(&:to_sym)
    end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
75
76
77
  end

  included do
78
    validates :status, inclusion: { in: AVAILABLE_STATUSES }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
79

80
81
    state_machine :status, initial: :created do
      state :created, value: 'created'
Tiger's avatar
Tiger committed
82
      state :preparing, value: 'preparing'
Kamil Trzcinski's avatar
Kamil Trzcinski committed
83
84
85
86
87
      state :pending, value: 'pending'
      state :running, value: 'running'
      state :failed, value: 'failed'
      state :success, value: 'success'
      state :canceled, value: 'canceled'
Kamil Trzcinski's avatar
Kamil Trzcinski committed
88
      state :skipped, value: 'skipped'
89
      state :manual, value: 'manual'
90
      state :scheduled, value: 'scheduled'
Kamil Trzcinski's avatar
Kamil Trzcinski committed
91
92
    end

Heinrich Lee Yu's avatar
Heinrich Lee Yu committed
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
    scope :created, -> { with_status(:created) }
    scope :preparing, -> { with_status(:preparing) }
    scope :relevant, -> { without_status(:created) }
    scope :running, -> { with_status(:running) }
    scope :pending, -> { with_status(:pending) }
    scope :success, -> { with_status(:success) }
    scope :failed, -> { with_status(:failed) }
    scope :canceled, -> { with_status(:canceled) }
    scope :skipped, -> { with_status(:skipped) }
    scope :manual, -> { with_status(:manual) }
    scope :scheduled, -> { with_status(:scheduled) }
    scope :alive, -> { with_status(:created, :preparing, :pending, :running) }
    scope :created_or_pending, -> { with_status(:created, :pending) }
    scope :running_or_pending, -> { with_status(:running, :pending) }
    scope :finished, -> { with_status(:success, :failed, :canceled) }
    scope :failed_or_canceled, -> { with_status(:failed, :canceled) }
109
110

    scope :cancelable, -> do
Tiger's avatar
Tiger committed
111
      where(status: [:running, :preparing, :pending, :created, :scheduled])
112
    end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
113
114
115
  end

  def started?
116
    STARTED_STATUSES.include?(status) && started_at
Kamil Trzcinski's avatar
Kamil Trzcinski committed
117
118
119
  end

  def active?
120
    ACTIVE_STATUSES.include?(status)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
121
122
123
  end

  def complete?
124
    COMPLETED_STATUSES.include?(status)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
125
  end
126

127
  def blocked?
128
    BLOCKED_STATUS.include?(status)
129
130
  end

131
132
133
134
135
136
  private

  def calculate_duration
    if started_at && finished_at
      finished_at - started_at
    elsif started_at
137
      Time.now - started_at
138
139
    end
  end
140
end