summaryrefslogtreecommitdiff
path: root/lib/gitlab/ci/pipeline_duration.rb
blob: d8fb05724bc3e7734571f630b4dc8a5dc71b1206 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
module Gitlab
  module Ci
    class PipelineDuration
      PeriodStruct = Struct.new(:first, :last)
      class Period < SegmentStruct
        def duration
          last - first
        end
      end

      def self.from_builds(builds)
        now = Time.now

        periods = builds.map do |b|
          Period.new(b.started_at || now, b.finished_at || now)
        end

        new(periods)
      end

      attr_reader :duration, :pending_duration

      def initialize(periods)
        process(periods.sort_by(&:first))
      end

      private

      def process(periods)
        merged = process_periods(periods)

        @duration = process_duration(merged)
        @pending_duration = process_pending_duration(merged)
      end

      def process_periods(periods)
        if periods.empty?
          periods
        else
          periods.drop(1).inject([periods.first]) do |result, current|
            merged = try_merge_period(result.last, current)

            if merged
              result[-1] = merged
              result
            else
              result << current
            end
          end
        end
      end

      def try_merge_period(previous, current)
        if current.first <= previous.last
          Period.new(previous.first, [previous.last, current.last].max)
        end
      end

      def process_duration(periods)
        periods.inject(0) do |result, per|
          result + per.duration
        end
      end

      def process_pending_duration(periods)
        return 0 if periods.empty?

        total = periods.last.last - periods.first.first
        total - duration
      end
    end
  end
end