Commit 579aaee9 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Improve manifest feature after backend review

Signed-off-by: default avatarDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
parent 51e758b1
...@@ -366,7 +366,7 @@ class ApplicationController < ActionController::Base ...@@ -366,7 +366,7 @@ class ApplicationController < ActionController::Base
end end
def manifest_import_enabled? def manifest_import_enabled?
Gitlab::CurrentSettings.import_sources.include?('manifest') Group.supports_nested_groups? && Gitlab::CurrentSettings.import_sources.include?('manifest')
end end
# U2F (universal 2nd factor) devices need a unique identifier for the application # U2F (universal 2nd factor) devices need a unique identifier for the application
......
...@@ -27,8 +27,8 @@ class Import::ManifestController < Import::BaseController ...@@ -27,8 +27,8 @@ class Import::ManifestController < Import::BaseController
manifest = Gitlab::ManifestImport::Manifest.new(params[:manifest].tempfile) manifest = Gitlab::ManifestImport::Manifest.new(params[:manifest].tempfile)
if manifest.valid? if manifest.valid?
session[:repositories] = manifest.projects session[:manifest_import_repositories] = manifest.projects
session[:group_id] = group.id session[:manifest_import_group_id] = group.id
redirect_to status_import_manifest_path redirect_to status_import_manifest_path
else else
...@@ -65,11 +65,11 @@ class Import::ManifestController < Import::BaseController ...@@ -65,11 +65,11 @@ class Import::ManifestController < Import::BaseController
end end
def group def group
@group ||= Group.find_by(id: session[:group_id]) @group ||= Group.find_by(id: session[:manifest_import_group_id])
end end
def repositories def repositories
@repositories ||= session[:repositories] @repositories ||= session[:manifest_import_repositories]
end end
def find_jobs def find_jobs
......
...@@ -21,23 +21,13 @@ ...@@ -21,23 +21,13 @@
%th= _('Status') %th= _('Status')
%tbody %tbody
- @already_added_projects.each do |project| - @already_added_projects.each do |project|
%tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" } %tr{ id: "project_#{project.id}", class: project_status_css_class(project.import_status) }
%td %td
= provider_project_link(provider, project.import_source) = provider_project_link(provider, project.import_source)
%td %td
= link_to project.full_path, [project.namespace.becomes(Namespace), project] = link_to project.full_path, [project.namespace.becomes(Namespace), project]
%td.job-status %td.job-status
- if project.import_status == 'finished' = render 'import/project_status', project: project
%span
%i.fa.fa-check
= _('Done')
- elsif project.import_status == 'started'
%i.fa.fa-spinner.fa-spin
= _('Started')
- elsif project.import_status == 'failed'
= _('Failed')
- else
= project.human_import_status_name
- @repos.each do |repo| - @repos.each do |repo|
%tr{ id: "repo_#{repo.id}", data: { qa: { repo_path: repo.full_name } } } %tr{ id: "repo_#{repo.id}", data: { qa: { repo_path: repo.full_name } } }
...@@ -61,6 +51,6 @@ ...@@ -61,6 +51,6 @@
= has_ci_cd_only_params? ? _('Connect') : _('Import') = has_ci_cd_only_params? ? _('Connect') : _('Import')
= icon("spinner spin", class: "loading-icon") = icon("spinner spin", class: "loading-icon")
.js-importer-status{ data: { jobs_import_path: "#{url_for([:jobs, :import, provider])}", .js-importer-status{ data: { jobs_import_path: url_for([:jobs, :import, provider]),
import_path: "#{url_for([:import, provider])}", import_path: url_for([:import, provider]),
ci_cd_only: "#{has_ci_cd_only_params?}" } } ci_cd_only: has_ci_cd_only_params? } }
- case project.import_status
- when 'finished'
= icon('check')
= _('Done')
- when 'started'
= icon("spinner spin")
= _('Started')
- when 'failed'
= _('Failed')
- else
= project.human_import_status_name
= form_tag upload_import_manifest_path, multipart: true do = form_tag upload_import_manifest_path, multipart: true do
.form-group
= label_tag :manifest, class: 'label-light' do
= _('Manifest')
= file_field_tag :manifest, class: 'form-control-file', required: true
.form-text.text-muted
= _('Import multiple repositories by uploading a manifest file.')
.form-group .form-group
= label_tag :group_id, nil, class: 'label-light' do = label_tag :group_id, nil, class: 'label-light' do
= _('Group') = _('Group')
...@@ -17,6 +10,14 @@ ...@@ -17,6 +10,14 @@
.form-text.text-muted .form-text.text-muted
= _('Choose the top-level group for your repository imports.') = _('Choose the top-level group for your repository imports.')
.form-group
= label_tag :manifest, class: 'label-light' do
= _('Manifest')
= file_field_tag :manifest, class: 'form-control-file', required: true
.form-text.text-muted
= _('Import multiple repositories by uploading a manifest file.')
= link_to icon('question-circle'), help_page_path('user/project/import/manifest')
.append-bottom-10 .append-bottom-10
= submit_tag _('List available repositories'), class: 'btn btn-success' = submit_tag _('List available repositories'), class: 'btn btn-success'
= link_to _('Cancel'), new_project_path, class: 'btn btn-cancel' = link_to _('Cancel'), new_project_path, class: 'btn btn-cancel'
...@@ -19,23 +19,13 @@ ...@@ -19,23 +19,13 @@
%th= _('Status') %th= _('Status')
%tbody %tbody
- @already_added_projects.each do |project| - @already_added_projects.each do |project|
%tr{ id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}" } %tr{ id: "project_#{project.id}", class: project_status_css_class(project.import_status) }
%td %td
= project.import_url = project.import_url
%td %td
= link_to_project project = link_to_project project
%td.job-status %td.job-status
- if project.import_status == 'finished' = render 'import/project_status', project: project
%span
= icon('check')
= _('Done')
- elsif project.import_status == 'started'
= icon("spinner spin")
= _('Started')
- elsif project.import_status == 'failed'
= _('Failed')
- else
= project.human_import_status_name
- @pending_repositories.each do |repository| - @pending_repositories.each do |repository|
%tr{ id: "repo_#{repository[:id]}" } %tr{ id: "repo_#{repository[:id]}" }
...@@ -48,6 +38,5 @@ ...@@ -48,6 +38,5 @@
= _('Import') = _('Import')
= icon("spinner spin", class: "loading-icon") = icon("spinner spin", class: "loading-icon")
.js-importer-status{ data: { jobs_import_path: "#{url_for([:jobs, :import, provider])}", .js-importer-status{ data: { jobs_import_path: url_for([:jobs, :import, provider]),
import_path: "#{url_for([:import, provider])}", import_path: url_for([:import, provider]) } }
ci_cd_only: "#{has_ci_cd_only_params?}" } }
# Import multiple repositories by uploading a manifest file # Import multiple repositories by uploading a manifest file
GitLab allows you to import all the required git repositories based on the GitLab allows you to import all the required git repositories
Android repository manifest file. based a manifest file like the one used by the Android repository.
>**Note:**
This feature requires [subgroups](user/group/subgroups/index.md) to be supported by your database.
You can do it by following next steps: You can do it by following next steps:
......
# Class to parse manifest file to import multiple projects at once # Class to parse manifest file and build a list of repositories for import
# #
# <manifest> # <manifest>
# <remote review="https://android-review.googlesource.com/" /> # <remote review="https://android-review.googlesource.com/" />
...@@ -11,17 +11,16 @@ ...@@ -11,17 +11,16 @@
# For example, you can't have projects with 'foo' and 'foo/bar' paths. # For example, you can't have projects with 'foo' and 'foo/bar' paths.
# 2. Remote must be present with review attribute so GitLab knows # 2. Remote must be present with review attribute so GitLab knows
# where to fetch source code # 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 Gitlab
module ManifestImport module ManifestImport
class Manifest class Manifest
attr_reader :parsed_xml, :errors attr_reader :parsed_xml, :errors
def initialize(file) def initialize(file)
@parsed_xml = File.open(file) { |f| Nokogiri::XML(f) } @parsed_xml = Nokogiri::XML(file) { |config| config.strict }
@errors = [] @errors = []
rescue Nokogiri::XML::SyntaxError
@errors = ['The uploaded file is not a valid XML file.']
end end
def projects def projects
...@@ -36,6 +35,8 @@ module Gitlab ...@@ -36,6 +35,8 @@ module Gitlab
end end
def valid? def valid?
return false if @errors.any?
unless validate_remote unless validate_remote
@errors << 'Make sure a <remote> tag is present and is valid.' @errors << 'Make sure a <remote> tag is present and is valid.'
end end
......
...@@ -12,8 +12,7 @@ module Gitlab ...@@ -12,8 +12,7 @@ module Gitlab
def execute def execute
group_full_path, _, project_path = repository[:path].rpartition('/') group_full_path, _, project_path = repository[:path].rpartition('/')
group_full_path = File.join(destination.full_path, group_full_path) if destination group_full_path = File.join(destination.full_path, group_full_path) if destination
group = Group.find_by_full_path(group_full_path) || group = create_group_with_parents(group_full_path)
create_group_with_parents(group_full_path)
params = { params = {
import_url: repository[:url], import_url: repository[:url],
......
...@@ -36,6 +36,15 @@ describe 'Import multiple repositories by uploading a manifest file', :js, :post ...@@ -36,6 +36,15 @@ describe 'Import multiple repositories by uploading a manifest file', :js, :post
end end
end end
it 'renders an error if invalid file was provided' do
visit new_import_manifest_path
attach_file('manifest', Rails.root.join('spec/fixtures/banana_sample.gif'))
click_on 'List available repositories'
expect(page).to have_content 'The uploaded file is not a valid XML file.'
end
def first_row def first_row
page.all('table.import-jobs tbody tr')[0] page.all('table.import-jobs tbody tr')[0]
end end
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::ManifestImport::Manifest, :postgresql do describe Gitlab::ManifestImport::Manifest, :postgresql do
let(:file) { Rails.root.join('spec/fixtures/aosp_manifest.xml') } let(:file) { File.open(Rails.root.join('spec/fixtures/aosp_manifest.xml')) }
let(:manifest) { described_class.new(file) } let(:manifest) { described_class.new(file) }
describe '#valid?' do describe '#valid?' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment