metadata.rb 2.88 KB
Newer Older
1 2 3 4 5 6 7 8
require 'zlib'
require 'json'

module Gitlab
  module Ci
    module Build
      module Artifacts
        class Metadata
9 10
          VERSION_PATTERN = '[\w\s]+(\d+\.\d+\.\d+)'
          attr_reader :file, :path, :full_version
11

12
          def initialize(file, path)
13
            @file, @path = file, path
14
            @full_version = read_version
15 16 17
          end

          def version
18
            @full_version.match(/#{VERSION_PATTERN}/).captures.first
19 20 21
          end

          def errors
22
            gzip do |gz|
23
              read_string(gz) # version
24 25 26
              errors = read_string(gz)
              raise StandardError, 'Errors field not found!' unless errors
              JSON.parse(errors)
27 28 29
            end
          end

30
          def match!
31
            gzip do |gz|
32 33
              2.times { read_string(gz) } # version and errors fields
              match_entries(gz)
34
            end
35 36
          end

37 38 39
          def to_entry
            entires, metadata = match!
            Entry.new(@path, entires, metadata)
40 41 42 43
          end

          private

44
          def match_entries(gz)
45
            paths, metadata = [], []
46
            match_pattern = %r{^#{Regexp.escape(@path)}[^/]*/?$}
47
            invalid_pattern = %r{(^\.?\.?/)|(/\.?\.?/)}
48

49 50
            until gz.eof? do
              begin
51 52
                path = read_string(gz).force_encoding('UTF-8')
                meta = read_string(gz).force_encoding('UTF-8')
53
               
54
                next unless path.valid_encoding? && meta.valid_encoding?
55
                next unless path =~ match_pattern
56
                next if path =~ invalid_pattern
57

58
                paths.push(path)
59
                metadata.push(JSON.parse(meta, symbolize_names: true))
60
              rescue JSON::ParserError, Encoding::CompatibilityError
61 62 63 64 65 66 67
                next
              end
            end

            [paths, metadata]
          end

68
          def read_version
69
            gzip do |gz|
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
              version_string = read_string(gz)

              unless version_string
                raise StandardError, 'Artifacts metadata file empty!'
              end

              unless version_string =~ /^#{VERSION_PATTERN}/
                raise StandardError, 'Invalid version!'
              end

              version_string.chomp
            end
          end

          def read_uint32(gz)
85 86 87 88 89
            binary = gz.read(4)
            binary.unpack('L>')[0] if binary
          end

          def read_string(gz)
90
            string_size = read_uint32(gz)
91
            return nil unless string_size
92
            gz.read(string_size)
93 94 95
          end

          def gzip
96 97
            open do |file|
              gzip = Zlib::GzipReader.new(file)
98 99 100 101 102
              begin
                yield gzip
              ensure
                gzip.close
              end
103 104 105 106 107 108 109 110 111 112 113
            end
          end

          def open
            File.open(@file) { |file| yield file }
          end
        end
      end
    end
  end
end