summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG2
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock4
-rw-r--r--app/controllers/application_controller.rb6
-rw-r--r--app/controllers/import/gitlab_projects_controller.rb57
-rw-r--r--app/controllers/projects_controller.rb25
-rw-r--r--app/helpers/projects_helper.rb6
-rw-r--r--app/mailers/emails/projects.rb13
-rw-r--r--app/models/application_setting.rb2
-rw-r--r--app/models/project.rb14
-rw-r--r--app/services/notification_service.rb8
-rw-r--r--app/services/projects/create_service.rb2
-rw-r--r--app/services/projects/import_export/export_service.rb15
-rw-r--r--app/services/projects/import_service.rb7
-rw-r--r--app/views/import/gitlab_projects/new.html.haml23
-rw-r--r--app/views/notify/project_was_exported_email.html.haml8
-rw-r--r--app/views/notify/project_was_exported_email.text.erb6
-rw-r--r--app/views/notify/project_was_not_exported_email.html.haml7
-rw-r--r--app/views/notify/project_was_not_exported_email.text.erb5
-rw-r--r--app/views/projects/edit.html.haml31
-rw-r--r--app/views/projects/new.html.haml22
-rw-r--r--app/workers/project_export_worker.rb1
-rw-r--r--app/workers/project_import_worker.rb8
-rw-r--r--config/routes.rb2
-rw-r--r--features/dashboard/new_project.feature2
-rw-r--r--features/steps/dashboard/new_project.rb5
-rw-r--r--lib/gitlab/current_settings.rb2
-rw-r--r--lib/gitlab/gitlab_import/project_creator.rb4
-rw-r--r--lib/gitlab/import_export/file_importer.rb30
-rw-r--r--lib/gitlab/import_export/import_export.yml2
-rw-r--r--lib/gitlab/import_export/import_service.rb69
-rw-r--r--lib/gitlab/import_export/importer.rb78
-rw-r--r--lib/gitlab/import_export/project_creator.rb24
-rw-r--r--lib/gitlab/import_export/project_factory.rb41
-rw-r--r--lib/gitlab/import_export/project_tree_restorer.rb17
-rw-r--r--lib/gitlab/import_sources.rb3
-rw-r--r--spec/features/projects/import_export/import_file_spec.rb48
-rw-r--r--spec/features/projects/import_export/test_project_export.tar.gzbin0 -> 672360 bytes
38 files changed, 430 insertions, 171 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 364690286e1..e7f38c93be9 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -176,6 +176,8 @@ v 8.8.0
- API support for the 'since' and 'until' operators on commit requests (Paco Guzman)
- Fix Gravatar hint in user profile when Gravatar is disabled. !3988 (Artem Sidorenko)
- Expire repository exists? and has_visible_content? caches after a push if necessary
+ - Fix unintentional filtering bug in issues sorted by milestone due (Takuya Noguchi)
+ - GitLab project import and export functionality
- Fix unintentional filtering bug in Issue/MR sorted by milestone due (Takuya Noguchi)
- Fix adding a todo for private group members (Ahmad Sherif)
- Bump ace-rails-ap gem version from 2.0.1 to 4.0.2 which upgrades Ace Editor from 1.1.2 to 1.2.3
diff --git a/Gemfile b/Gemfile
index b2660144f2b..d5634bcaa62 100644
--- a/Gemfile
+++ b/Gemfile
@@ -218,7 +218,7 @@ gem 'jquery-turbolinks', '~> 2.1.0'
gem 'addressable', '~> 2.3.8'
gem 'bootstrap-sass', '~> 3.3.0'
-gem 'font-awesome-rails', '~> 4.2'
+gem 'font-awesome-rails', '~> 4.6.1'
gem 'gitlab_emoji', '~> 0.3.0'
gem 'gon', '~> 6.0.1'
gem 'jquery-atwho-rails', '~> 1.3.2'
diff --git a/Gemfile.lock b/Gemfile.lock
index dfc15700494..fe6785249fb 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -253,7 +253,7 @@ GEM
fog-xml (0.1.2)
fog-core
nokogiri (~> 1.5, >= 1.5.11)
- font-awesome-rails (4.5.0.1)
+ font-awesome-rails (4.6.1.0)
railties (>= 3.2, < 5.1)
foreman (0.78.0)
thor (~> 0.19.1)
@@ -881,7 +881,7 @@ DEPENDENCIES
fog-google (~> 0.3)
fog-local (~> 0.3)
fog-openstack (~> 0.1)
- font-awesome-rails (~> 4.2)
+ font-awesome-rails (~> 4.6.1)
foreman
fuubar (~> 2.0.0)
gemnasium-gitlab-service (~> 0.2)
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index cd6ae507cf1..726acbdb3ed 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -24,7 +24,7 @@ class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
helper_method :abilities, :can?, :current_application_settings
- helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?
+ helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?, :gitlab_project_import_enabled?
rescue_from Encoding::CompatibilityError do |exception|
log_exception(exception)
@@ -326,6 +326,10 @@ class ApplicationController < ActionController::Base
current_application_settings.import_sources.include?('git')
end
+ def gitlab_project_import_enabled?
+ current_application_settings.import_sources.include?('gitlab_project')
+ end
+
def two_factor_authentication_required?
current_application_settings.require_two_factor_authentication
end
diff --git a/app/controllers/import/gitlab_projects_controller.rb b/app/controllers/import/gitlab_projects_controller.rb
new file mode 100644
index 00000000000..d4063747da4
--- /dev/null
+++ b/app/controllers/import/gitlab_projects_controller.rb
@@ -0,0 +1,57 @@
+class Import::GitlabProjectsController < Import::BaseController
+ before_action :verify_gitlab_project_import_enabled
+ before_action :verify_project_and_namespace_access
+
+ def new
+ @namespace_id = project_params[:namespace_id]
+ @path = project_params[:path]
+ end
+
+ def create
+ unless file_is_valid?
+ return redirect_back_or_default(options: { alert: "You need to upload a GitLab project export archive." })
+ end
+
+ @project = Gitlab::ImportExport::ProjectCreator.new(Namespace.find(project_params[:namespace_id]),
+ current_user,
+ File.expand_path(params[:file].path),
+ project_params[:path]).execute
+
+ flash[:notice] = "The project import has been started."
+
+ if @project.saved?
+ redirect_to(
+ project_path(@project),
+ notice: "Project '#{@project.name}' is being imported."
+ )
+ else
+ render 'new'
+ end
+ end
+
+ private
+
+ def file_is_valid?
+ params[:file].respond_to?(:read) && params[:file].content_type == 'application/x-gzip'
+ end
+
+ def verify_project_and_namespace_access
+ unless namespace_access?
+ render_403
+ end
+ end
+
+ def namespace_access?
+ can?(current_user, :create_projects, Namespace.find(project_params[:namespace_id]))
+ end
+
+ def verify_gitlab_project_import_enabled
+ render_404 unless gitlab_project_import_enabled?
+ end
+
+ def project_params
+ params.permit(
+ :path, :namespace_id,
+ )
+ end
+end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index a6479c42d94..9a6d4be2fc8 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -7,7 +7,7 @@ class ProjectsController < Projects::ApplicationController
before_action :assign_ref_vars, :tree, only: [:show], if: :repo_exists?
# Authorize
- before_action :authorize_admin_project!, only: [:edit, :update, :housekeeping]
+ before_action :authorize_admin_project!, only: [:edit, :update, :housekeeping, :download_export, :export]
before_action :event_filter, only: [:show, :activity]
layout :determine_layout
@@ -185,6 +185,23 @@ class ProjectsController < Projects::ApplicationController
)
end
+ def export
+ @project.add_export_job(current_user_id: current_user.id)
+
+ redirect_to(
+ edit_project_path(@project),
+ notice: "Project export started."
+ )
+ end
+
+ def download_export
+ if export_project_path
+ send_file export_project_path, disposition: 'attachment'
+ else
+ render_404
+ end
+ end
+
def toggle_star
current_user.toggle_star(@project)
@project.reload
@@ -247,4 +264,10 @@ class ProjectsController < Projects::ApplicationController
def get_id
project.repository.root_ref
end
+
+ def export_project_path
+ # TODO: move this, probably to ImportExport and refactor
+ folder = File.join(Settings.shared['path'], 'tmp/project_exports', @project.path_with_namespace)
+ Dir.glob("#{folder}/*export.tar.gz").max_by {|f| File.ctime(f)}
+ end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 5e5d170a9f3..aeebdf90ba6 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -348,4 +348,10 @@ module ProjectsHelper
message.strip.gsub(Gitlab.config.gitlab_shell.repos_path.chomp('/'), "[REPOS PATH]")
end
+
+ def db_export_list
+ YAML.load_file(Gitlab::ImportExport.config_file)['project_tree'].map do |relation|
+ relation.is_a?(Hash) ? relation.keys.first.to_s : relation.to_s
+ end + ['notes', 'merge_request_diffs']
+ end
end
diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb
index fdf1e9f5afc..0d9558db68c 100644
--- a/app/mailers/emails/projects.rb
+++ b/app/mailers/emails/projects.rb
@@ -50,6 +50,19 @@ module Emails
subject: subject("Invitation declined"))
end
+ def project_was_exported_email(current_user, project)
+ @project = project
+ mail(to: current_user.notification_email,
+ subject: subject("Project was exported"))
+ end
+
+ def project_was_not_exported_email(current_user, project, errors)
+ @project = project
+ @errors = errors
+ mail(to: current_user.notification_email,
+ subject: subject("Project export error"))
+ end
+
def project_was_moved_email(project_id, user_id, old_path_with_namespace)
@current_user = @user = User.find user_id
@project = Project.find project_id
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index a744f937918..b2fb54ba5e7 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -123,7 +123,7 @@ class ApplicationSetting < ActiveRecord::Base
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
restricted_signup_domains: Settings.gitlab['restricted_signup_domains'],
- import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'],
+ import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git', 'gitlab_project'],
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
max_artifacts_size: Settings.artifacts['max_size'],
require_two_factor_authentication: false,
diff --git a/app/models/project.rb b/app/models/project.rb
index ab7947a0880..323bde5d5c4 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -348,16 +348,6 @@ class Project < ActiveRecord::Base
joins(join_body).reorder('join_note_counts.amount DESC')
end
-
- def create_from_import_job(current_user_id:, tmp_file:, namespace_id:, project_path:)
- job_id = ProjectImportWorker.perform_async(current_user_id, tmp_file, namespace_id, project_path)
-
- if job_id
- Rails.logger.info "Import job started for export #{tmp_file} with job ID #{job_id}"
- else
- Rails.logger.error "Import job failed to start for #{tmp_file}"
- end
- end
end
def team
@@ -492,6 +482,10 @@ class Project < ActiveRecord::Base
Gitlab::UrlSanitizer.new(import_url).masked_url
end
+ def gitlab_project_import?
+ import_type == 'gitlab_project'
+ end
+
def check_limit
unless creator.can_create_project? or namespace.kind == 'group'
projects_limit = creator.projects_limit
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 91ca82ed3b7..79534a06a1d 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -246,6 +246,14 @@ class NotificationService
end
end
+ def project_exported(project, current_user)
+ mailer.project_was_exported_email(current_user, project).deliver_later
+ end
+
+ def project_not_exported(project, current_user, errors)
+ mailer.project_was_not_exported_email(current_user, project, errors).deliver_later
+ end
+
protected
# Get project users with WATCH notification level
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index 61cac5419ad..ffac357e91d 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -54,7 +54,7 @@ module Projects
@project.import_start if @project.import?
- after_create_actions if @project.persisted?
+ after_create_actions if @project.persisted? && !@project.gitlab_project_import?
if @project.errors.empty?
@project.add_import_job if @project.import?
diff --git a/app/services/projects/import_export/export_service.rb b/app/services/projects/import_export/export_service.rb
index 25524c1c060..d6752377ce5 100644
--- a/app/services/projects/import_export/export_service.rb
+++ b/app/services/projects/import_export/export_service.rb
@@ -12,8 +12,9 @@ module Projects
def save_all
if [version_saver, project_tree_saver, uploads_saver, repo_saver, wiki_repo_saver].all?(&:save)
Gitlab::ImportExport::Saver.save(shared: @shared)
+ notify_success
else
- cleanup_and_notify_worker
+ cleanup_and_notify
end
end
@@ -37,10 +38,20 @@ module Projects
Gitlab::ImportExport::WikiRepoSaver.new(project: project, shared: @shared)
end
- def cleanup_and_notify_worker
+ def cleanup_and_notify
FileUtils.rm_rf(@shared.export_path)
+
+ notify_error
raise Gitlab::ImportExport::Error.new(@shared.errors.join(', '))
end
+
+ def notify_success
+ notification_service.project_exported(@project, @current_user)
+ end
+
+ def notify_error
+ notification_service.project_not_exported(@project, @current_user, @shared.errors.join(', '))
+ end
end
end
end
diff --git a/app/services/projects/import_service.rb b/app/services/projects/import_service.rb
index c4838d31f2f..b4b4c34b012 100644
--- a/app/services/projects/import_service.rb
+++ b/app/services/projects/import_service.rb
@@ -9,7 +9,8 @@ module Projects
'fogbugz',
'gitlab',
'github',
- 'google_code'
+ 'google_code',
+ 'gitlab_project'
]
def execute
@@ -37,7 +38,7 @@ module Projects
def import_repository
begin
- gitlab_shell.import_repository(project.path_with_namespace, project.import_url)
+ gitlab_shell.import_repository(project.path_with_namespace, project.import_url) unless @project.gitlab_project_import?
rescue Gitlab::Shell::Error => e
raise Error, "Error importing repository #{project.import_url} into #{project.path_with_namespace} - #{e.message}"
end
@@ -58,6 +59,8 @@ module Projects
end
def importer
+ return Gitlab::ImportExport::Importer if @project.gitlab_project_import?
+
class_name = "Gitlab::#{project.import_type.camelize}Import::Importer"
class_name.constantize.new(project)
end
diff --git a/app/views/import/gitlab_projects/new.html.haml b/app/views/import/gitlab_projects/new.html.haml
new file mode 100644
index 00000000000..1e7a65715b0
--- /dev/null
+++ b/app/views/import/gitlab_projects/new.html.haml
@@ -0,0 +1,23 @@
+- page_title "GitLab Import"
+- header_title "Projects", root_path
+%h3.page-title
+ = icon('gitlab')
+ Import projects from GitLab
+%hr
+
+= form_tag import_gitlab_project_path, class: 'form-horizontal', multipart: true do
+ %p
+ Project will be imported to path #{@path}
+
+ %p
+ To get started add your exported project file below:
+ .form-group
+ = hidden_field_tag :namespace_id, @namespace_id
+ = hidden_field_tag :path, @path
+ = label_tag :file, class: 'control-label' do
+ %span GitLab project export
+ .col-sm-10
+ = file_field_tag :file, class: ''
+
+ .form-actions
+ = submit_tag 'Continue to the next step', class: 'btn btn-create'
diff --git a/app/views/notify/project_was_exported_email.html.haml b/app/views/notify/project_was_exported_email.html.haml
new file mode 100644
index 00000000000..fb6f4ac3efc
--- /dev/null
+++ b/app/views/notify/project_was_exported_email.html.haml
@@ -0,0 +1,8 @@
+%p
+ Project #{@project.name} was exported succesfully
+%p
+ The project export can be downloaded from:
+ = link_to download_export_namespace_project_url(@project.namespace, @project) do
+ = @project.name_with_namespace + " export"
+%p
+ The download link will expire in 24 hours.
diff --git a/app/views/notify/project_was_exported_email.text.erb b/app/views/notify/project_was_exported_email.text.erb
new file mode 100644
index 00000000000..fcd801c237c
--- /dev/null
+++ b/app/views/notify/project_was_exported_email.text.erb
@@ -0,0 +1,6 @@
+Project <%= @project.name %> was exported succesfully
+
+The project export can be downloaded from:
+<%= download_export_namespace_project_url(@project.namespace, @project) %>
+
+The download link will expire in 24 hours.
diff --git a/app/views/notify/project_was_not_exported_email.html.haml b/app/views/notify/project_was_not_exported_email.html.haml
new file mode 100644
index 00000000000..78d4751b64a
--- /dev/null
+++ b/app/views/notify/project_was_not_exported_email.html.haml
@@ -0,0 +1,7 @@
+%p
+ Project #{@project.name} couldn't be exported.
+%p
+ The errors we encountered were:
+
+ %h3{style: "background: black; color: red;"}
+ #{@errors}
diff --git a/app/views/notify/project_was_not_exported_email.text.erb b/app/views/notify/project_was_not_exported_email.text.erb
new file mode 100644
index 00000000000..ad730d2162b
--- /dev/null
+++ b/app/views/notify/project_was_not_exported_email.text.erb
@@ -0,0 +1,5 @@
+Project <%= @project.name %> couldn't be exported.
+
+The errors we encountered were:
+
+<%= @errors %>
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 8449fe1e4e0..2441e71a5d2 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -120,6 +120,37 @@
= link_to 'Housekeeping', housekeeping_namespace_project_path(@project.namespace, @project),
method: :post, class: "btn btn-save"
%hr
+ .row.prepend-top-default
+ .col-lg-3
+ %h4.prepend-top-0
+ Export project
+ %p.append-bottom-0
+ %p
+ Generates a compressed export file of the project and sends a link to download the export.
+ .col-lg-9
+
+ = link_to 'Export project', export_namespace_project_path(@project.namespace, @project),
+ method: :post, class: "btn btn-default"
+
+ %p.append-bottom-0
+ %p
+ .row.prepend-top-default
+ Clicking on Export project, will produce a compressed file that will be sent as a link to your registered e-mail address.
+
+ .bs-callout.bs-callout-info
+ %p.append-bottom-0
+ %p
+ The following items will be exported:
+ %ul
+ %li Project and wiki repository
+ %li Project uploads
+ %li DB items, including configuration
+ %ul
+ - db_export_list.each do |export_relation|
+ %li
+ %code #{export_relation}
+
+ %hr
- if can? current_user, :archive_project, @project
.row.prepend-top-default
.col-lg-3
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index f9ac16b32f3..79ef9284268 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -88,7 +88,12 @@
- if git_import_enabled?
= link_to "#", class: 'btn js-toggle-button import_git' do
%i.fa.fa-git
- %span Any repo by URL
+ %span Repo by URL
+
+ - if gitlab_project_import_enabled?
+ = link_to new_import_gitlab_project_path, class: 'btn import_gitlab_project project-submit' do
+ %i.fa.fa-gitlab
+ %span GitLab project
.js-toggle-content.hide
= render "shared/import_form", f: f
@@ -122,3 +127,18 @@
$('.modal-header .close').bind('click', function() {
$(".modal").hide();
});
+ $('.import_gitlab_project').bind('click', function() {
+ var _href = $("a.import_gitlab_project").attr("href");
+ $(".import_gitlab_project").attr("href", _href + '?namespace_id=' + $("#project_namespace_id").val() + '&path=' + $("#project_path").val());
+ });
+ $('.import_gitlab_project').attr('disabled',true)
+ $('.import_gitlab_project').attr('title', 'Project path required.');
+ $('#project_path').keyup(function(){
+ if($(this).val().length !=0) {
+ $('.import_gitlab_project').attr('disabled', false);
+ $('.import_gitlab_project').attr('title','');
+ } else {
+ $('.import_gitlab_project').attr('disabled',true);
+ $('.import_gitlab_project').attr('title', 'Project path required.');
+ }
+ })
diff --git a/app/workers/project_export_worker.rb b/app/workers/project_export_worker.rb
index 3616b37d2ad..d2dcdb46219 100644
--- a/app/workers/project_export_worker.rb
+++ b/app/workers/project_export_worker.rb
@@ -7,6 +7,7 @@ class ProjectExportWorker
def perform(current_user_id, project_id)
current_user = User.find(current_user_id)
project = Project.find(project_id)
+
::Projects::ImportExport::ExportService.new(project, current_user).execute
end
end
diff --git a/app/workers/project_import_worker.rb b/app/workers/project_import_worker.rb
index b2902c3278e..b18d453702e 100644
--- a/app/workers/project_import_worker.rb
+++ b/app/workers/project_import_worker.rb
@@ -7,10 +7,10 @@ class ProjectImportWorker
def perform(current_user_id, tmp_file, namespace_id, path)
current_user = User.find(current_user_id)
- project = Gitlab::ImportExport::ImportService.execute(archive_file: tmp_file,
- owner: current_user,
- namespace_id: namespace_id,
- project_path: path)
+ project = Gitlab::ImportExport::Importer.execute(archive_file: tmp_file,
+ owner: current_user,
+ namespace_id: namespace_id,
+ project_path: path)
if project
project.repository.after_import
else
diff --git a/config/routes.rb b/config/routes.rb
index e2492fbab46..bc1112de0f5 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -454,6 +454,8 @@ Rails.application.routes.draw do
post :housekeeping
post :toggle_star
post :markdown_preview
+ post :export
+ post :download_export
get :autocomplete_sources
get :activity
end
diff --git a/features/dashboard/new_project.feature b/features/dashboard/new_project.feature
index 76392068357..56b4a639c01 100644
--- a/features/dashboard/new_project.feature
+++ b/features/dashboard/new_project.feature
@@ -14,7 +14,7 @@ Background:
@javascript
Scenario: I should see instructions on how to import from Git URL
Given I see "New Project" page
- When I click on "Any repo by URL"
+ When I click on "Repo by URL"
Then I see instructions on how to import from Git URL
@javascript
diff --git a/features/steps/dashboard/new_project.rb b/features/steps/dashboard/new_project.rb
index a0aad66184d..ad20201c2c5 100644
--- a/features/steps/dashboard/new_project.rb
+++ b/features/steps/dashboard/new_project.rb
@@ -19,7 +19,8 @@ class Spinach::Features::NewProject < Spinach::FeatureSteps
expect(page).to have_link('GitLab.com')
expect(page).to have_link('Gitorious.org')
expect(page).to have_link('Google Code')
- expect(page).to have_link('Any repo by URL')
+ expect(page).to have_link('Repo by URL')
+ expect(page).to have_link('GitLab project')
end
step 'I click on "Import project from GitHub"' do
@@ -36,7 +37,7 @@ class Spinach::Features::NewProject < Spinach::FeatureSteps
end
end
- step 'I click on "Any repo by URL"' do
+ step 'I click on "Repo by URL"' do
first('.import_git').click
end
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index 5e7532f57ae..38c0f1aba47 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -36,7 +36,7 @@ module Gitlab
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
restricted_signup_domains: Settings.gitlab['restricted_signup_domains'],
- import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'],
+ import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git', 'gitlab_project'],
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
max_artifacts_size: Settings.artifacts['max_size'],
require_two_factor_authentication: false,
diff --git a/lib/gitlab/gitlab_import/project_creator.rb b/lib/gitlab/gitlab_import/project_creator.rb
index 77c33db4b59..3d0418261bb 100644
--- a/lib/gitlab/gitlab_import/project_creator.rb
+++ b/lib/gitlab/gitlab_import/project_creator.rb
@@ -11,7 +11,7 @@ module Gitlab
end
def execute
- project = ::Projects::CreateService.new(
+ ::Projects::CreateService.new(
current_user,
name: repo["name"],
path: repo["path"],
@@ -22,8 +22,6 @@ module Gitlab
import_source: repo["path_with_namespace"],
import_url: repo["http_url_to_repo"].sub("://", "://oauth2:#{@session_data[:gitlab_access_token]}@")
).execute
-
- project
end
end
end
diff --git a/lib/gitlab/import_export/file_importer.rb b/lib/gitlab/import_export/file_importer.rb
new file mode 100644
index 00000000000..0e70d9282d5
--- /dev/null
+++ b/lib/gitlab/import_export/file_importer.rb
@@ -0,0 +1,30 @@
+module Gitlab
+ module ImportExport
+ class FileImporter
+ include Gitlab::ImportExport::CommandLineUtil
+
+ def self.import(*args)
+ new(*args).import
+ end
+
+ def initialize(archive_file:, shared:)
+ @archive_file = archive_file
+ @shared = shared
+ end
+
+ def import
+ FileUtils.mkdir_p(@shared.export_path)
+ decompress_archive
+ rescue => e
+ @shared.error(e)
+ false
+ end
+
+ private
+
+ def decompress_archive
+ untar_zxf(archive: @archive_file, dir: @shared.export_path)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/import_export/import_export.yml b/lib/gitlab/import_export/import_export.yml
index 3796fc8cd02..164ab6238c4 100644
--- a/lib/gitlab/import_export/import_export.yml
+++ b/lib/gitlab/import_export/import_export.yml
@@ -30,8 +30,6 @@ project_tree:
# Only include the following attributes for the models specified.
included_attributes:
project:
- - :name
- - :path
- :description
- :issues_enabled
- :merge_requests_enabled
diff --git a/lib/gitlab/import_export/import_service.rb b/lib/gitlab/import_export/import_service.rb
deleted file mode 100644
index db71f72efec..00000000000
--- a/lib/gitlab/import_export/import_service.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-module Gitlab
- module ImportExport
- class ImportService
-
- def self.execute(*args)
- new(*args).execute
- end
-
- def initialize(archive_file:, owner:, namespace_id:, project_path:)
- @archive_file = archive_file
- @current_user = owner
- @namespace = Namespace.find(namespace_id)
- @shared = Gitlab::ImportExport::Shared.new(relative_path: path_with_namespace(project_path), project_path: project_path)
- end
-
- def execute
- Gitlab::ImportExport::Importer.import(archive_file: @archive_file,
- shared: @shared)
- if check_version! && [project_tree, repo_restorer, wiki_restorer, uploads_restorer].all?(&:restore)
- project_tree.project
- else
- project_tree.project.destroy if project_tree.project
- nil
- end
- end
-
- private
-
- def check_version!
- Gitlab::ImportExport::VersionChecker.check!(shared: @shared)
- end
-
- def project_tree
- @project_tree ||= Gitlab::ImportExport::ProjectTreeRestorer.new(user: @current_user,
- shared: @shared,
- namespace_id: @namespace.id)
- end
-
- def repo_restorer
- Gitlab::ImportExport::RepoRestorer.new(path_to_bundle: repo_path,
- shared: @shared,
- project: project_tree.project)
- end
-
- def wiki_restorer
- Gitlab::ImportExport::RepoRestorer.new(path_to_bundle: wiki_repo_path,
- shared: @shared,
- project: ProjectWiki.new(project_tree.project),
- wiki: true)
- end
-
- def uploads_restorer
- Gitlab::ImportExport::UploadsRestorer.new(project: project_tree.project, shared: @shared)
- end
-
- def path_with_namespace(project_path)
- File.join(@namespace.path, project_path)
- end
-
- def repo_path
- File.join(@shared.export_path, 'project.bundle')
- end
-
- def wiki_repo_path
- File.join(@shared.export_path, 'project.wiki.bundle')
- end
- end
- end
-end
diff --git a/lib/gitlab/import_export/importer.rb b/lib/gitlab/import_export/importer.rb
index 8020aab3da9..d096e17bdf0 100644
--- a/lib/gitlab/import_export/importer.rb
+++ b/lib/gitlab/import_export/importer.rb
@@ -1,29 +1,79 @@
module Gitlab
module ImportExport
class Importer
- include Gitlab::ImportExport::CommandLineUtil
- def self.import(*args)
- new(*args).import
+ def self.execute(*args)
+ new(*args).execute
end
- def initialize(archive_file:, shared:)
- @archive_file = archive_file
- @shared = shared
+ def initialize(project)
+ @archive_file = project.import_source
+ @current_user = project.creator
+ @shared = Gitlab::ImportExport::Shared.new(relative_path: path_with_namespace(@project.path))
end
- def import
- FileUtils.mkdir_p(@shared.export_path)
- decompress_archive
- rescue => e
- @shared.error(e)
- false
+ def execute
+ Gitlab::ImportExport::FileImporter.import(archive_file: @archive_file,
+ shared: @shared)
+ if check_version! && [project_tree, repo_restorer, wiki_restorer, uploads_restorer].all?(&:restore)
+ project_tree.project
+ else
+ project_tree.project.destroy if project_tree.project
+ nil
+ end
end
private
- def decompress_archive
- untar_zxf(archive: @archive_file, dir: @shared.export_path)
+ def check_version!
+ Gitlab::ImportExport::VersionChecker.check!(shared: @shared)
+ end
+
+ def project_tree
+ @project_tree ||= Gitlab::ImportExport::ProjectTreeRestorer.new(user: @current_user,
+ shared: @shared,
+ project: @project)
+ end
+
+ def repo_restorer
+ Gitlab::ImportExport::RepoRestorer.new(path_to_bundle: repo_path,
+ shared: @shared,
+ project: project_tree.project)
+ end
+
+ def wiki_restorer
+ Gitlab::ImportExport::RepoRestorer.new(path_to_bundle: wiki_repo_path,
+ shared: @shared,
+ project: ProjectWiki.new(project_tree.project),
+ wiki: true)
+ end
+
+ def uploads_restorer
+ Gitlab::ImportExport::UploadsRestorer.new(project: project_tree.project, shared: @shared)
+ end
+
+ def path_with_namespace(project_path)
+ File.join(@namespace.path, project_path)
+ end
+
+ def repo_path
+ File.join(@shared.export_path, 'project.bundle')
+ end
+
+ def wiki_repo_path
+ File.join(@shared.export_path, 'project.wiki.bundle')
+ end
+
+ def attributes_for_todo
+ { user_id: @current_user.id,
+ project_id: project_tree.project.id,
+ target_type: 'Project',
+ target: project_tree.project,
+ action: Todo::IMPORTED,
+ author_id: @current_user.id,
+ state: :pending,
+ target_id: project_tree.project.id
+ }
end
end
end
diff --git a/lib/gitlab/import_export/project_creator.rb b/lib/gitlab/import_export/project_creator.rb
new file mode 100644
index 00000000000..6f1e3867efb
--- /dev/null
+++ b/lib/gitlab/import_export/project_creator.rb
@@ -0,0 +1,24 @@
+module Gitlab
+ module ImportExport
+ class ProjectCreator
+
+ def initialize(namespace_id, current_user, file, project_path)
+ @namespace_id = namespace_id
+ @current_user = current_user
+ @file = file
+ @project_path = project_path
+ end
+
+ def execute
+ ::Projects::CreateService.new(
+ current_user,
+ name: @project_path,
+ path: @project_path,
+ namespace_id: namespace_id,
+ import_type: "gitlab_project",
+ import_source: @file
+ ).execute
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/import_export/project_factory.rb b/lib/gitlab/import_export/project_factory.rb
deleted file mode 100644
index 6cd4736649b..00000000000
--- a/lib/gitlab/import_export/project_factory.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-module Gitlab
- module ImportExport
- module ProjectFactory
- extend self
-
- def create(project_params:, user:, namespace_id:)
- project = Project.new(project_params.except('id'))
- project.creator = user
- check_namespace(namespace_id, project, user)
- end
-
- def check_namespace(namespace_id, project, user)
- if namespace_id
- # Find matching namespace and check if it allowed
- # for current user if namespace_id passed.
- if allowed_namespace?(user, namespace_id)
- project.namespace_id = namespace_id
- else
- project.namespace_id = nil
- deny_namespace(project)
- end
- else
- # Set current user namespace if namespace_id is nil
- project.namespace_id = user.namespace_id
- end
- project
- end
-
- private
-
- def allowed_namespace?(user, namespace_id)
- namespace = Namespace.find_by(id: namespace_id)
- user.can?(:create_projects, namespace)
- end
-
- def deny_namespace(project)
- project.errors.add(:namespace, "is not valid")
- end
- end
- end
-end
diff --git a/lib/gitlab/import_export/project_tree_restorer.rb b/lib/gitlab/import_export/project_tree_restorer.rb
index 75a261bb121..92727528d01 100644
--- a/lib/gitlab/import_export/project_tree_restorer.rb
+++ b/lib/gitlab/import_export/project_tree_restorer.rb
@@ -2,12 +2,11 @@ module Gitlab
module ImportExport
class ProjectTreeRestorer
- def initialize(user:, shared:, namespace_id:)
+ def initialize(user:, shared:, project:)
@path = File.join(shared.export_path, 'project.json')
@user = user
- @project_path = shared.opts[:project_path]
- @namespace_id = namespace_id
@shared = shared
+ @project = project
end
def restore
@@ -21,7 +20,7 @@ module Gitlab
end
def project
- @project ||= create_project
+ @restored_project ||= restore_project
end
private
@@ -57,14 +56,10 @@ module Gitlab
end
end
- def create_project
+ def restore_project
project_params = @tree_hash.reject { |_key, value| value.is_a?(Array) }
- project = Gitlab::ImportExport::ProjectFactory.create(
- project_params: project_params, user: @user, namespace_id: @namespace_id)
- project.path = @project_path
- project.name = @project_path
- project.save!
- project
+ @project.update(project_params)
+ @project
end
# Given a relation hash containing one or more models and its relationships,
diff --git a/lib/gitlab/import_sources.rb b/lib/gitlab/import_sources.rb
index ccfdfbe73e8..4cae819d356 100644
--- a/lib/gitlab/import_sources.rb
+++ b/lib/gitlab/import_sources.rb
@@ -20,7 +20,8 @@ module Gitlab
'Gitorious.org' => 'gitorious',
'Google Code' => 'google_code',
'FogBugz' => 'fogbugz',
- 'Any repo by URL' => 'git',
+ 'Repo by URL' => 'git',
+ 'GitLab project' => 'gitlab_project'
}
end
diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb
new file mode 100644
index 00000000000..03298149b60
--- /dev/null
+++ b/spec/features/projects/import_export/import_file_spec.rb
@@ -0,0 +1,48 @@
+require 'spec_helper'
+
+feature 'project import', feature: true, js: true do
+ include Select2Helper
+
+ let(:user) { create(:admin) }
+ let!(:namespace) { create(:namespace, name: "asd", owner: user) }
+ let(:file) { File.join(Rails.root, 'spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') }
+ let(:export_path) { "#{Dir::tmpdir}/import_file_spec" }
+ let(:project) { Project.last }
+
+ background do
+ allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ login_as(user)
+ end
+
+ after(:each) do
+ FileUtils.rm_rf(export_path, secure: true)
+ end
+
+ scenario 'user imports an exported project successfully' do
+ expect(Project.all.count).to be_zero
+
+ visit new_project_path
+
+ select2('2', from: '#project_namespace_id')
+ fill_in :project_path, with:'test-project-path', visible: true
+ click_link 'GitLab project'
+
+ expect(page).to have_content('GitLab project export')
+ expect(URI.parse(current_url).query).to eq('namespace_id=2&path=test-project-path')
+
+ attach_file('file', file)
+
+ click_on 'Continue to the next step' # import starts
+
+ expect(project).not_to be_nil
+ expect(project.issues).not_to be_empty
+ expect(project.merge_requests).not_to be_empty
+ expect(project.repo_exists?).to be true
+ expect(wiki_exists?).to be true
+ end
+
+ def wiki_exists?
+ wiki = ProjectWiki.new(project)
+ File.exists?(wiki.repository.path_to_repo) && !wiki.repository.empty?
+ end
+end
diff --git a/spec/features/projects/import_export/test_project_export.tar.gz b/spec/features/projects/import_export/test_project_export.tar.gz
new file mode 100644
index 00000000000..b85055070d8
--- /dev/null
+++ b/spec/features/projects/import_export/test_project_export.tar.gz
Binary files differ