gitlab-org--gitlab-foss/lib/gitlab/ci/trace/stream.rb

129 lines
3.1 KiB
Ruby
Raw Normal View History

module Gitlab
module Ci
class Trace
# This was inspired from: http://stackoverflow.com/a/10219411/1520132
class Stream
BUFFER_SIZE = 4096
LIMIT_SIZE = 500.kilobytes
attr_reader :stream
delegate :close, :tell, :seek, :size, :path, :truncate, to: :stream, allow_nil: true
delegate :valid?, to: :stream, as: :present?, allow_nil: true
def initialize
@stream = yield
@stream&.binmode
end
def valid?
self.stream.present?
end
def file?
self.path.present?
end
def limit(last_bytes = LIMIT_SIZE)
if last_bytes < size
stream.seek(-last_bytes, IO::SEEK_END)
stream.readline
end
end
def append(data, offset)
stream.truncate(offset)
stream.seek(0, IO::SEEK_END)
stream.write(data)
stream.flush()
end
def set(data)
truncate(0)
stream.write(data)
stream.flush()
end
def raw(last_lines: nil)
return unless valid?
if last_lines.to_i > 0
read_last_lines(last_lines)
else
stream.read
end.force_encoding(Encoding.default_external)
end
def html_with_state(state = nil)
::Ci::Ansi2html.convert(stream, state)
end
def html(last_lines: nil)
text = raw(last_lines: last_lines)
buffer = StringIO.new(text)
::Ci::Ansi2html.convert(buffer).html
end
def extract_coverage(regex)
return unless valid?
return unless regex
regex = Regexp.new(regex)
match = ""
2017-05-05 14:16:39 -04:00
reverse_line do |line|
2017-05-17 04:53:31 -04:00
matches = line.force_encoding(regex.encoding).scan(regex)
next unless matches.is_a?(Array)
next if matches.empty?
match = matches.flatten.last
coverage = match.gsub(/\d+(\.\d+)?/).first
return coverage if coverage.present?
end
nil
rescue
# if bad regex or something goes wrong we dont want to interrupt transition
2017-05-17 04:53:31 -04:00
# so we just silently ignore error for now
end
private
def read_last_lines(limit)
2017-05-17 05:26:38 -04:00
to_enum(:reverse_line).first(limit).reverse.join
end
2017-05-05 14:16:39 -04:00
def reverse_line
2017-05-06 04:38:56 -04:00
pos = BUFFER_SIZE
2017-05-05 14:16:39 -04:00
max = stream.size
2017-05-09 13:40:34 -04:00
debris = ''
2017-05-05 14:16:39 -04:00
2017-05-06 04:38:56 -04:00
while pos < max
stream.seek(-pos, IO::SEEK_END)
2017-05-09 13:40:34 -04:00
stream.read(BUFFER_SIZE).tap do |buf|
buf = buf + debris
debris, *lines = buf.each_line.to_a
lines.reverse_each do |line|
2017-05-09 13:40:34 -04:00
yield(line)
end
end
2017-05-05 14:16:39 -04:00
pos += BUFFER_SIZE
end
2017-05-06 04:38:56 -04:00
# Reached the head, read only left
stream.seek(0)
2017-05-09 13:40:34 -04:00
last = (max > BUFFER_SIZE) ? (max % BUFFER_SIZE) : max
stream.read(last).tap do |buf|
buf = buf + debris
buf.each_line.reverse_each do |line|
2017-05-09 13:40:34 -04:00
yield(line)
end
end
2017-05-05 14:16:39 -04:00
end
end
end
end
end