summaryrefslogtreecommitdiff
path: root/lib/gitlab/manifest_import/manifest.rb
blob: 7208fe5bbc5b8d5b4c7281cb2ec612eb6b1dfb6e (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
74
75
76
77
78
79
80
81
82
83
# frozen_string_literal: true

# Class to parse manifest file and build a list of repositories for import
#
# <manifest>
#   <remote review="https://android-review.googlesource.com/" />
#   <project path="platform-common" name="platform" />
#   <project path="platform/art" name="platform/art" />
#   <project path="platform/device" name="platform/device" />
# </manifest>
#
# 1. Project path must be uniq and can't be part of other project path.
#    For example, you can't have projects with 'foo' and 'foo/bar' paths.
# 2. Remote must be present with review attribute so GitLab knows
#    where to fetch source code
module Gitlab
  module ManifestImport
    class Manifest
      attr_reader :parsed_xml, :errors

      def initialize(file)
        @parsed_xml = Nokogiri::XML(file) { |config| config.strict }
        @errors = []
      rescue Nokogiri::XML::SyntaxError
        @errors = ['The uploaded file is not a valid XML file.']
      end

      def projects
        raw_projects.each_with_index.map do |project, i|
          {
            id: i,
            name: project['name'],
            path: project['path'],
            url: repository_url(project['name'])
          }
        end
      end

      def valid?
        return false if @errors.any?

        unless validate_remote
          @errors << 'Make sure a <remote> tag is present and is valid.'
        end

        unless validate_projects
          @errors << 'Make sure every <project> tag has name and path attributes.'
        end

        @errors.empty?
      end

      private

      def validate_remote
        remote.present? && URI.parse(remote).host
      rescue URI::Error
        false
      end

      def validate_projects
        raw_projects.all? do |project|
          project['name'] && project['path']
        end
      end

      def repository_url(name)
        Gitlab::Utils.append_path(remote, name)
      end

      def remote
        return @remote if defined?(@remote)

        remote_tag = parsed_xml.css('manifest > remote').first
        @remote = remote_tag['review'] if remote_tag
      end

      def raw_projects
        @raw_projects ||= parsed_xml.css('manifest > project')
      end
    end
  end
end