Class: Dash::Build::ProgressParser

Inherits:
Object
  • Object
show all
Defined in:
lib/dash/build/progress_parser.rb

Overview

Reads docker buildx build --progress=plain as it streams past and turns it into a per-step report. It is an SSHKit interaction handler, so it sees the same bytes SSHKit is already printing — no second process, no docker buildx history, no extra command.

The stream is line-oriented but arrives in chunks (the SSH backend splits on packet boundaries, not newlines), so data is buffered and only whole lines are parsed. Every line belongs to a vertex — #12 — and the first line for a vertex names it; the rest are events on it (DONE, CACHED, ERROR) or its own stdout, which is ignored.

Nothing here may raise into a build. A parse error stops the parsing and is reported once by the caller; the deploy keeps whatever was collected up to that point.

Constant Summary collapse

VERTEX =
/\A#(?<number>\d+)(?: (?<rest>.*))?\z/
STEP =

[linux/amd64 build 2/3] RUN … — the platform prefix only appears on multi-platform builds, and the stage name only when the Dockerfile named it with AS.

%r{\A\[(?:(?<platform>[a-z0-9]+/[a-z0-9][\w./-]*) )?(?:(?<stage>[A-Za-z0-9][\w.-]*) )?(?<ordinal>\d+)/(?<steps_in_stage>\d+)\] (?<instruction>.+)\z}
INTERNAL =
/\A\[(?:\S+ )?internal\] (?<what>.+)\z/
DONE =
/\ADONE (?<seconds>\d+(?:\.\d+)?)s\z/
ERROR =
/\AERROR: (?<message>.+)\z/
TRANSFERRING_CONTEXT =
/\Atransferring context: (?<size>[\d.]+)(?<unit>[a-zA-Z]+)(?: (?<seconds>\d+(?:\.\d+)?)s)? done\z/
PUSHING =
/\Apushing .*?(?<seconds>\d+(?:\.\d+)?)s done\z/
BYTE_UNITS =
{ "b" => 1, "kb" => 1_000, "mb" => 1_000_000, "gb" => 1_000_000_000, "tb" => 1_000_000_000_000 }.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize ⇒ ProgressParser

Returns a new instance of ProgressParser.



28
29
30
31
32
33
34
# File 'lib/dash/build/progress_parser.rb', line 28

def initialize
  @steps = {}
  @order = []
  @buffer = +""
  @push_seconds = 0.0
  @mutex = Mutex.new
end

Instance Attribute Details

#error ⇒ Object (readonly)

Returns the value of attribute error.



26
27
28
# File 'lib/dash/build/progress_parser.rb', line 26

def error
  @error
end

Instance Method Details

#finish ⇒ Object

The last line of a build has no trailing newline when the command dies mid-write.



51
52
53
54
55
56
57
58
59
60
# File 'lib/dash/build/progress_parser.rb', line 51

def finish
  @mutex.synchronize do
    next if @error

    parse_line @buffer.chomp unless @buffer.empty?
    @buffer = +""
  rescue StandardError => e
    @error = e
  end
end

#on_data(_command, _stream_name, data, _channel) ⇒ Object

SSHKit's interaction-handler contract.



37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/dash/build/progress_parser.rb', line 37

def on_data(_command, _stream_name, data, _channel)
  @mutex.synchronize do
    next if @error

    @buffer << data.to_s
    while (newline = @buffer.index("\n"))
      parse_line @buffer.slice!(0..newline).chomp
    end
  rescue StandardError => e
    @error = e
  end
end

#result ⇒ Object



62
63
64
65
66
# File 'lib/dash/build/progress_parser.rb', line 62

def result
  @mutex.synchronize do
    Dash::Build::Report.new(steps: @order.map { |number| @steps[number] }, push_seconds: @push_seconds)
  end
end