diff options
author | Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> | 2018-07-02 12:16:24 +0300 |
---|---|---|
committer | Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> | 2018-07-11 12:22:57 +0300 |
commit | 88e640374a82c3058b2a8cb54407fdc1784484d9 (patch) | |
tree | c6d0451ca3947e92a56c4d39e00cbf5b0272d257 /lib | |
parent | e68a547bc790d44a1df3c9ae8b07b004ab8dd47e (diff) |
Add manifest import feature
It allows user to automatically import multiple repositories
with nested structure by uploading a manifest xml file.
AOSP project was used as an example during development of this feature.
Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/gitlab/import_sources.rb | 3 | ||||
-rw-r--r-- | lib/gitlab/manifest_import/importer.rb | 46 | ||||
-rw-r--r-- | lib/gitlab/manifest_import/manifest.rb | 77 |
3 files changed, 125 insertions, 1 deletions
diff --git a/lib/gitlab/import_sources.rb b/lib/gitlab/import_sources.rb index 60d5fa4d29a..a13ee371e99 100644 --- a/lib/gitlab/import_sources.rb +++ b/lib/gitlab/import_sources.rb @@ -16,7 +16,8 @@ module Gitlab ImportSource.new('fogbugz', 'FogBugz', Gitlab::FogbugzImport::Importer), ImportSource.new('git', 'Repo by URL', nil), ImportSource.new('gitlab_project', 'GitLab export', Gitlab::ImportExport::Importer), - ImportSource.new('gitea', 'Gitea', Gitlab::LegacyGithubImport::Importer) + ImportSource.new('gitea', 'Gitea', Gitlab::LegacyGithubImport::Importer), + ImportSource.new('manifest', 'Manifest', Gitlab::ManifestImport::Importer) ].freeze class << self diff --git a/lib/gitlab/manifest_import/importer.rb b/lib/gitlab/manifest_import/importer.rb new file mode 100644 index 00000000000..35b3bb1e0ca --- /dev/null +++ b/lib/gitlab/manifest_import/importer.rb @@ -0,0 +1,46 @@ +module Gitlab + module ManifestImport + class Importer + attr_reader :repository, :destination, :user + + def initialize(repository, destination, user) + @repository = repository + @destination = destination + @user = user + end + + def execute + import_project + end + + private + + def import_project + group_full_path, _, project_path = repository[:path].rpartition('/') + group_full_path = File.join(destination.path, group_full_path) if destination + group = Group.find_by_full_path(group_full_path) || + create_group_with_parents(group_full_path) + + params = { + import_url: repository[:url], + import_type: 'manifest', + namespace_id: group.id, + path: project_path, + name: project_path, + visibility_level: destination.visibility_level + } + + Projects::CreateService.new(user, params).execute + end + + def create_group_with_parents(full_path) + params = { + group_path: full_path, + visibility_level: destination.visibility_level + } + + Groups::NestedCreateService.new(user, params).execute + end + end + end +end diff --git a/lib/gitlab/manifest_import/manifest.rb b/lib/gitlab/manifest_import/manifest.rb new file mode 100644 index 00000000000..87959d4ae7f --- /dev/null +++ b/lib/gitlab/manifest_import/manifest.rb @@ -0,0 +1,77 @@ +# Class to parse manifest file to import multiple projects at once +# +# <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 +# 3. For each nested keyword in path a corresponding group will be created. +# For example if a path is 'foo/bar' then GitLab will create a group 'foo' +# and a project 'bar' in it. +module Gitlab + module ManifestImport + class Manifest + attr_reader :parsed_xml, :errors + + def initialize(file) + @parsed_xml = File.open(file) { |f| Nokogiri::XML(f) } + @errors = [] + 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? + 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) + URI.join(remote, name).to_s + end + + def remote + @remote ||= parsed_xml.css('manifest > remote').first['review'] + end + + def raw_projects + @raw_projects ||= parsed_xml.css('manifest > project') + end + end + end +end |