debian-mirror-gitlab/lib/gitlab/ci/trace/stream.rb

152 lines
3.8 KiB
Ruby
Raw Normal View History

2018-12-13 13:39:08 +05:30
# frozen_string_literal: true
2017-08-17 22:00:37 +05:30
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
2020-11-24 15:15:51 +05:30
attr_reader :stream, :metrics
2017-08-17 22:00:37 +05:30
2018-05-09 12:01:36 +05:30
delegate :close, :tell, :seek, :size, :url, :truncate, to: :stream, allow_nil: true
2017-08-17 22:00:37 +05:30
2018-10-15 14:42:47 +05:30
delegate :valid?, to: :stream, allow_nil: true
alias_method :present?, :valid?
2017-08-17 22:00:37 +05:30
2020-11-24 15:15:51 +05:30
def initialize(metrics = Trace::Metrics.new)
2017-08-17 22:00:37 +05:30
@stream = yield
@stream&.binmode
2020-11-24 15:15:51 +05:30
@metrics = metrics
2017-08-17 22:00:37 +05:30
end
def valid?
self.stream.present?
end
def file?
self.path.present?
end
2018-05-09 12:01:36 +05:30
def path
self.stream.path if self.stream.respond_to?(:path)
end
2017-08-17 22:00:37 +05:30
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)
2018-10-15 14:42:47 +05:30
data = data.force_encoding(Encoding::BINARY)
2020-11-24 15:15:51 +05:30
metrics.increment_trace_operation(operation: :streamed)
metrics.increment_trace_bytes(data.bytesize)
2019-02-15 15:39:39 +05:30
stream.seek(offset, IO::SEEK_SET)
2017-08-17 22:00:37 +05:30
stream.write(data)
2019-02-15 15:39:39 +05:30
stream.truncate(offset + data.bytesize)
2019-03-02 22:35:43 +05:30
stream.flush
2017-08-17 22:00:37 +05:30
end
def set(data)
2019-02-15 15:39:39 +05:30
append(data, 0)
2017-08-17 22:00:37 +05:30
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(last_lines: nil)
text = raw(last_lines: last_lines)
buffer = StringIO.new(text)
2018-03-17 18:26:18 +05:30
::Gitlab::Ci::Ansi2html.convert(buffer).html
2017-08-17 22:00:37 +05:30
end
def extract_coverage(regex)
return unless valid?
2017-09-10 17:25:29 +05:30
return unless regex.present?
2017-08-17 22:00:37 +05:30
regex = Gitlab::UntrustedRegexp.new(regex)
match = ""
reverse_line do |line|
2017-09-10 17:25:29 +05:30
line.chomp!
2017-08-17 22:00:37 +05:30
matches = regex.scan(line)
next unless matches.is_a?(Array)
next if matches.empty?
match = matches.flatten.last
coverage = match.gsub(/\d+(\.\d+)?/).first
2018-10-15 14:42:47 +05:30
return coverage if coverage.present? # rubocop:disable Cop/AvoidReturnFromBlocks
2017-08-17 22:00:37 +05:30
end
nil
rescue
# if bad regex or something goes wrong we dont want to interrupt transition
# so we just silently ignore error for now
end
2018-03-17 18:26:18 +05:30
def extract_sections
return [] unless valid?
lines = to_enum(:each_line_with_pos)
parser = SectionParser.new(lines)
parser.parse!
parser.sections
end
2017-08-17 22:00:37 +05:30
private
2018-03-17 18:26:18 +05:30
def each_line_with_pos
stream.seek(0, IO::SEEK_SET)
stream.each_line do |line|
yield [line, stream.pos - line.bytesize]
end
end
2017-08-17 22:00:37 +05:30
def read_last_lines(limit)
to_enum(:reverse_line).first(limit).reverse.join
end
def reverse_line
stream.seek(0, IO::SEEK_END)
debris = ''
until (buf = read_backward(BUFFER_SIZE)).empty?
2018-12-13 13:39:08 +05:30
debris, *lines = (buf + debris).each_line.to_a
2017-08-17 22:00:37 +05:30
lines.reverse_each do |line|
2018-10-15 14:42:47 +05:30
yield(line.force_encoding(Encoding.default_external))
2017-08-17 22:00:37 +05:30
end
end
2018-10-15 14:42:47 +05:30
yield(debris.force_encoding(Encoding.default_external)) unless debris.empty?
2017-08-17 22:00:37 +05:30
end
def read_backward(length)
cur_offset = stream.tell
start = cur_offset - length
start = 0 if start < 0
stream.seek(start, IO::SEEK_SET)
stream.read(cur_offset - start).tap do
stream.seek(start, IO::SEEK_SET)
end
end
end
end
end
end