summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorDouwe Maan <douwe@gitlab.com>2016-06-17 15:35:27 +0000
committerDouwe Maan <douwe@gitlab.com>2016-06-17 15:35:27 +0000
commit9b7fd74805684c09d8de314d4eee67b470cb57eb (patch)
treefc13833cc2ea4c51da242f6476cc522c333cca03 /app
parentae4491b42181f7195199fd6ac9273891d6e48263 (diff)
parent8891bef26f5e3acc7050d206670bc6fc0ddd66e2 (diff)
downloadgitlab-ce-9b7fd74805684c09d8de314d4eee67b470cb57eb.tar.gz
Merge branch 'feature/project-export' into 'master'
Export project functionality This is a MR for the export functionality of https://gitlab.com/gitlab-org/gitlab-ce/issues/3050, which adds the ability to export single projects. - [x] members - DB data - [x] issues - [x] issue comments - [x] merge requests - [x] merge request diff - [x] merge request comments - [x] labels - [x] milestones - [x] snippets - [x] releases - [x] events - [x] commit statuses - [x] CI builds - File system data - [x] Git repository - [x] wiki - [x] uploads - [ ] ~~CI build traces~~ - [ ] ~~CI build artifacts~~ - [ ] ~~LFS objects~~ - DB configuration - [x] services - [x] web hooks - [x] protected branches - [x] deploy keys - [x] CI variables - [x] CI triggers See merge request !3114
Diffstat (limited to 'app')
-rw-r--r--app/controllers/application_controller.rb6
-rw-r--r--app/controllers/import/gitlab_projects_controller.rb48
-rw-r--r--app/controllers/projects_controller.rb44
-rw-r--r--app/mailers/emails/projects.rb13
-rw-r--r--app/models/application_setting.rb2
-rw-r--r--app/models/ci/pipeline.rb4
-rw-r--r--app/models/commit_status.rb3
-rw-r--r--app/models/concerns/importable.rb6
-rw-r--r--app/models/member.rb11
-rw-r--r--app/models/merge_request.rb7
-rw-r--r--app/models/merge_request_diff.rb3
-rw-r--r--app/models/note.rb5
-rw-r--r--app/models/project.rb34
-rw-r--r--app/services/notification_service.rb8
-rw-r--r--app/services/projects/create_service.rb10
-rw-r--r--app/services/projects/import_export/export_service.rb57
-rw-r--r--app/services/projects/import_service.rb25
-rw-r--r--app/views/import/gitlab_projects/new.html.haml25
-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.haml9
-rw-r--r--app/views/notify/project_was_not_exported_email.text.erb6
-rw-r--r--app/views/projects/edit.html.haml36
-rw-r--r--app/views/projects/imports/show.html.haml2
-rw-r--r--app/views/projects/new.html.haml34
-rw-r--r--app/workers/gitlab_remove_project_export_worker.rb9
-rw-r--r--app/workers/project_export_worker.rb12
27 files changed, 402 insertions, 31 deletions
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 72d1b97bf56..dd1bc6f5d52 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)
@@ -319,6 +319,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..f99aa490d3e
--- /dev/null
+++ b/app/controllers/import/gitlab_projects_controller.rb
@@ -0,0 +1,48 @@
+class Import::GitlabProjectsController < Import::BaseController
+ before_action :verify_gitlab_project_import_enabled
+
+ def new
+ @namespace_id = project_params[:namespace_id]
+ @namespace_name = Namespace.find(project_params[:namespace_id]).name
+ @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(project_params[:namespace_id],
+ current_user,
+ File.expand_path(project_params[:file].path),
+ project_params[:path]).execute
+
+ if @project.saved?
+ redirect_to(
+ project_path(@project),
+ notice: "Project '#{@project.name}' is being imported."
+ )
+ else
+ redirect_to(
+ new_import_gitlab_project_path,
+ alert: "Project could not be imported: #{@project.errors.full_messages.join(', ')}"
+ )
+ end
+ end
+
+ private
+
+ def file_is_valid?
+ project_params[:file] && project_params[:file].respond_to?(:read)
+ end
+
+ def verify_gitlab_project_import_enabled
+ render_404 unless gitlab_project_import_enabled?
+ end
+
+ def project_params
+ params.permit(
+ :path, :namespace_id, :file
+ )
+ end
+end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 482c11cf23c..8044c637825 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, :remove_export, :generate_new_export]
before_action :event_filter, only: [:show, :activity]
layout :determine_layout
@@ -186,6 +186,48 @@ class ProjectsController < Projects::ApplicationController
)
end
+ def export
+ @project.add_export_job(current_user: current_user)
+
+ redirect_to(
+ edit_project_path(@project),
+ notice: "Project export started. A download link will be sent by email."
+ )
+ end
+
+ def download_export
+ export_project_path = @project.export_project_path
+
+ if export_project_path
+ send_file export_project_path, disposition: 'attachment'
+ else
+ redirect_to(
+ edit_project_path(@project),
+ alert: "Project export link has expired. Please generate a new export from your project settings."
+ )
+ end
+ end
+
+ def remove_export
+ if @project.remove_exports
+ flash[:notice] = "Project export has been deleted."
+ else
+ flash[:alert] = "Project export could not be deleted."
+ end
+ redirect_to(edit_project_path(@project))
+ end
+
+ def generate_new_export
+ if @project.remove_exports
+ export
+ else
+ redirect_to(
+ edit_project_path(@project),
+ alert: "Project export could not be deleted."
+ )
+ end
+ end
+
def toggle_star
current_user.toggle_star(@project)
@project.reload
diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb
index 689fb3e0ffb..e0af7081411 100644
--- a/app/mailers/emails/projects.rb
+++ b/app/mailers/emails/projects.rb
@@ -9,6 +9,19 @@ module Emails
subject: subject("Project was moved"))
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 repository_push_email(project_id, opts = {})
@message =
Gitlab::Email::Message::RepositoryPush.new(self, project_id, opts)
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index a744f937918..d914b0b26eb 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: %w[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/ci/pipeline.rb b/app/models/ci/pipeline.rb
index a5f2ac59001..5b264ecffc5 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -170,6 +170,10 @@ module Ci
builds.where.not(environment: nil).success.pluck(:environment).uniq
end
+ def notes
+ Note.for_commit_id(sha)
+ end
+
private
def build_builds_for_stages(stages, user, status, trigger_request)
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index e53c483b904..ab13db4b297 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -1,5 +1,6 @@
class CommitStatus < ActiveRecord::Base
include Statuseable
+ include Importable
self.table_name = 'ci_builds'
@@ -7,7 +8,7 @@ class CommitStatus < ActiveRecord::Base
belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id, touch: true
belongs_to :user
- validates :pipeline, presence: true
+ validates :pipeline, presence: true, unless: :importing?
validates_presence_of :name
diff --git a/app/models/concerns/importable.rb b/app/models/concerns/importable.rb
new file mode 100644
index 00000000000..019ef755849
--- /dev/null
+++ b/app/models/concerns/importable.rb
@@ -0,0 +1,6 @@
+module Importable
+ extend ActiveSupport::Concern
+
+ attr_accessor :importing
+ alias_method :importing?, :importing
+end
diff --git a/app/models/member.rb b/app/models/member.rb
index cea6d259760..4ee3f1bb5c2 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -1,5 +1,6 @@
class Member < ActiveRecord::Base
include Sortable
+ include Importable
include Gitlab::Access
attr_accessor :raw_invite_token
@@ -41,11 +42,11 @@ class Member < ActiveRecord::Base
before_validation :generate_invite_token, on: :create, if: -> (member) { member.invite_email.present? }
- after_create :send_invite, if: :invite?
- after_create :send_request, if: :request?
- after_create :create_notification_setting, unless: :pending?
- after_create :post_create_hook, unless: :pending?
- after_update :post_update_hook, unless: :pending?
+ after_create :send_invite, if: :invite?, unless: :importing?
+ after_create :send_request, if: :request?, unless: :importing?
+ after_create :create_notification_setting, unless: [:pending?, :importing?]
+ after_create :post_create_hook, unless: [:pending?, :importing?]
+ after_update :post_update_hook, unless: [:pending?, :importing?]
after_destroy :post_destroy_hook, unless: :pending?
after_destroy :post_decline_request, if: :request?
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 7b8858b24d6..73bf182ec9f 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -4,6 +4,7 @@ class MergeRequest < ActiveRecord::Base
include Referable
include Sortable
include Taskable
+ include Importable
belongs_to :target_project, foreign_key: :target_project_id, class_name: "Project"
belongs_to :source_project, foreign_key: :source_project_id, class_name: "Project"
@@ -13,7 +14,7 @@ class MergeRequest < ActiveRecord::Base
serialize :merge_params, Hash
- after_create :create_merge_request_diff
+ after_create :create_merge_request_diff, unless: :importing
after_update :update_merge_request_diff
delegate :commits, :diffs, :real_size, to: :merge_request_diff, prefix: nil
@@ -95,12 +96,12 @@ class MergeRequest < ActiveRecord::Base
end
end
- validates :source_project, presence: true, unless: :allow_broken
+ validates :source_project, presence: true, unless: [:allow_broken, :importing?]
validates :source_branch, presence: true
validates :target_project, presence: true
validates :target_branch, presence: true
validates :merge_user, presence: true, if: :merge_when_build_succeeds?
- validate :validate_branches, unless: :allow_broken
+ validate :validate_branches, unless: [:allow_broken, :importing?]
validate :validate_fork
scope :by_branch, ->(branch_name) { where("(source_branch LIKE :branch) OR (target_branch LIKE :branch)", branch: branch_name) }
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index 7d5103748f5..aca377cc600 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -1,5 +1,6 @@
class MergeRequestDiff < ActiveRecord::Base
include Sortable
+ include Importable
# Prevent store of diff if commits amount more then 500
COMMITS_SAFE_SIZE = 100
@@ -22,7 +23,7 @@ class MergeRequestDiff < ActiveRecord::Base
serialize :st_commits
serialize :st_diffs
- after_create :reload_content
+ after_create :reload_content, unless: :importing?
def reload_content
reload_commits
diff --git a/app/models/note.rb b/app/models/note.rb
index 4b6748053ff..8d164647550 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -4,6 +4,7 @@ class Note < ActiveRecord::Base
include Participable
include Mentionable
include Awardable
+ include Importable
default_value_for :system, false
@@ -28,11 +29,11 @@ class Note < ActiveRecord::Base
validates :attachment, file_size: { maximum: :max_attachment_size }
validates :noteable_type, presence: true
- validates :noteable_id, presence: true, unless: :for_commit?
+ validates :noteable_id, presence: true, unless: [:for_commit?, :importing?]
validates :commit_id, presence: true, if: :for_commit?
validates :author, presence: true
- validate unless: :for_commit? do |note|
+ validate unless: [:for_commit?, :importing?] do |note|
unless note.noteable.try(:project) == note.project
errors.add(:invalid_project, 'Note and noteable project mismatch')
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 8eef22356e2..ca3bc04e2dd 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -367,6 +367,11 @@ class Project < ActiveRecord::Base
joins(join_body).reorder('join_note_counts.amount DESC')
end
+
+ # Deletes gitlab project export files older than 24 hours
+ def remove_gitlab_exports!
+ Gitlab::Popen.popen(%W(find #{Gitlab::ImportExport.storage_path} -not -path #{Gitlab::ImportExport.storage_path} -mmin +1440 -delete))
+ end
end
def team
@@ -470,7 +475,7 @@ class Project < ActiveRecord::Base
end
def import?
- external_import? || forked?
+ external_import? || forked? || gitlab_project_import?
end
def no_import?
@@ -501,6 +506,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
@@ -1096,4 +1105,27 @@ class Project < ActiveRecord::Base
ensure
@errors = original_errors
end
+
+ def add_export_job(current_user:)
+ job_id = ProjectExportWorker.perform_async(current_user.id, self.id)
+
+ if job_id
+ Rails.logger.info "Export job started for project ID #{self.id} with job ID #{job_id}"
+ else
+ Rails.logger.error "Export job failed to start for project ID #{self.id}"
+ end
+ end
+
+ def export_path
+ File.join(Gitlab::ImportExport.storage_path, path_with_namespace)
+ end
+
+ def export_project_path
+ Dir.glob("#{export_path}/*export.tar.gz").max_by { |f| File.ctime(f) }
+ end
+
+ def remove_exports
+ _, status = Gitlab::Popen.popen(%W(find #{export_path} -not -path #{export_path} -delete))
+ status.zero?
+ end
end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index f804ac171c4..e70b400829a 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -266,6 +266,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..55956be2844 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -80,16 +80,18 @@ module Projects
def after_create_actions
log_info("#{@project.owner.name} created a new project \"#{@project.name_with_namespace}\"")
- @project.create_wiki if @project.wiki_enabled?
+ unless @project.gitlab_project_import?
+ @project.create_wiki if @project.wiki_enabled?
- @project.build_missing_services
+ @project.build_missing_services
- @project.create_labels
+ @project.create_labels
+ end
event_service.create_project(@project, current_user)
system_hook_service.execute_hooks_for(@project, :create)
- unless @project.group
+ unless @project.group || @project.gitlab_project_import?
@project.team << [current_user, :master, current_user]
end
end
diff --git a/app/services/projects/import_export/export_service.rb b/app/services/projects/import_export/export_service.rb
new file mode 100644
index 00000000000..d6752377ce5
--- /dev/null
+++ b/app/services/projects/import_export/export_service.rb
@@ -0,0 +1,57 @@
+module Projects
+ module ImportExport
+ class ExportService < BaseService
+
+ def execute(_options = {})
+ @shared = Gitlab::ImportExport::Shared.new(relative_path: File.join(project.path_with_namespace, 'work'))
+ save_all
+ end
+
+ private
+
+ 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
+ end
+ end
+
+ def version_saver
+ Gitlab::ImportExport::VersionSaver.new(shared: @shared)
+ end
+
+ def project_tree_saver
+ Gitlab::ImportExport::ProjectTreeSaver.new(project: project, shared: @shared)
+ end
+
+ def uploads_saver
+ Gitlab::ImportExport::UploadsSaver.new(project: project, shared: @shared)
+ end
+
+ def repo_saver
+ Gitlab::ImportExport::RepoSaver.new(project: project, shared: @shared)
+ end
+
+ def wiki_repo_saver
+ Gitlab::ImportExport::WikiRepoSaver.new(project: project, shared: @shared)
+ end
+
+ 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..9159ec08959 100644
--- a/app/services/projects/import_service.rb
+++ b/app/services/projects/import_service.rb
@@ -9,26 +9,31 @@ module Projects
'fogbugz',
'gitlab',
'github',
- 'google_code'
+ 'google_code',
+ 'gitlab_project'
]
def execute
- if unknown_url?
- # In this case, we only want to import issues, not a repository.
- create_repository
- else
- import_repository
- end
+ add_repository_to_project unless project.gitlab_project_import?
import_data
success
- rescue Error => e
+ rescue => e
error(e.message)
end
private
+ def add_repository_to_project
+ if unknown_url?
+ # In this case, we only want to import issues, not a repository.
+ create_repository
+ else
+ import_repository
+ end
+ end
+
def create_repository
unless project.create_repository
raise Error, 'The repository could not be created.'
@@ -46,7 +51,7 @@ module Projects
def import_data
return unless has_importer?
- project.repository.before_import
+ project.repository.before_import unless project.gitlab_project_import?
unless importer.execute
raise Error, 'The remote data could not be imported.'
@@ -58,6 +63,8 @@ module Projects
end
def importer
+ return Gitlab::ImportExport::Importer.new(project) 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..44e2653ca4a
--- /dev/null
+++ b/app/views/import/gitlab_projects/new.html.haml
@@ -0,0 +1,25 @@
+- page_title "GitLab Import"
+- header_title "Projects", root_path
+%h3.page-title
+ = icon('gitlab')
+ Import an exported GitLab project
+%hr
+
+= form_tag import_gitlab_project_path, class: 'form-horizontal', multipart: true do
+ %p
+ Project will be imported as
+ %strong
+ #{@namespace_name}/#{@path}
+
+ %p
+ To move or copy an entire GitLab project from another GitLab installation to this one, navigate to the original project's settings page, generate an export file, and upload it here.
+ .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 'Import project', 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..b28fea35ad5
--- /dev/null
+++ b/app/views/notify/project_was_exported_email.html.haml
@@ -0,0 +1,8 @@
+%p
+ Project #{@project.name} was exported successfully.
+%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..42c4d176876
--- /dev/null
+++ b/app/views/notify/project_was_exported_email.text.erb
@@ -0,0 +1,6 @@
+Project <%= @project.name %> was exported successfully.
+
+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..c9e9ade2cf1
--- /dev/null
+++ b/app/views/notify/project_was_not_exported_email.html.haml
@@ -0,0 +1,9 @@
+%p
+ Project #{@project.name} couldn't be exported.
+%p
+ The errors we encountered were:
+
+ %ul
+ - @errors.each do |error|
+ %li
+ error
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..a07f6edacf7
--- /dev/null
+++ b/app/views/notify/project_was_not_exported_email.text.erb
@@ -0,0 +1,6 @@
+Project <%= @project.name %> couldn't be exported.
+
+The errors we encountered were:
+
+- @errors.each do |error|
+<%= error %> \ No newline at end of file
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 8449fe1e4e0..27a94fe02dc 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -120,6 +120,42 @@
= 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
+ Export this project with all its related data in order to move your project to a new GitLab instance. Once the export is finished, you can import the file from the "New Project" page.
+ %p
+ Once the exported file is ready, you will receive a notification email with a download link.
+
+ .col-lg-9
+
+ - if @project.export_project_path
+ = link_to 'Download export', download_export_namespace_project_path(@project.namespace, @project),
+ method: :get, class: "btn btn-default"
+ = link_to 'Generate new export', generate_new_export_namespace_project_path(@project.namespace, @project),
+ method: :post, class: "btn btn-default"
+ - else
+ = link_to 'Export project', export_namespace_project_path(@project.namespace, @project),
+ method: :post, class: "btn btn-default"
+
+ .bs-callout.bs-callout-info
+ %p.append-bottom-0
+ %p
+ The following items will be exported:
+ %ul
+ %li Project and wiki repositories
+ %li Project uploads
+ %li Project configuration including web hooks and services
+ %li Issues with comments, merge requests with diffs and comments, labels, milestones, snippets, and other project entities
+ %p
+ The following items will NOT be exported:
+ %ul
+ %li Build traces and artifacts
+ %li LFS objects
+ %hr
- if can? current_user, :archive_project, @project
.row.prepend-top-default
.col-lg-3
diff --git a/app/views/projects/imports/show.html.haml b/app/views/projects/imports/show.html.haml
index c0d1ce0d120..4d8ee562e6a 100644
--- a/app/views/projects/imports/show.html.haml
+++ b/app/views/projects/imports/show.html.haml
@@ -7,7 +7,7 @@
Forking in progress.
- else
Import in progress.
- - unless @project.forked?
+ - if @project.external_import?
%p.monospace git clone --bare #{@project.safe_import_url}
%p Please wait while we import the repository for you. Refresh at will.
:javascript
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 7e8b8f83467..3c1c6060504 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -84,7 +84,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 export
.js-toggle-content.hide
= render "shared/import_form", f: f
@@ -115,6 +120,33 @@
e.preventDefault();
var import_modal = $(this).next(".modal").show();
});
+
$('.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.');
+
+ $('.import_gitlab_project').click(function( event ) {
+ if($('.import_gitlab_project').attr('disabled')) {
+ event.preventDefault();
+ new Flash("Please enter a path for the project to be imported to.");
+ }
+ });
+
+ $('#project_path').keyup(function(){
+ if($(this).val().length !=0) {
+ $('.import_gitlab_project').attr('disabled', false);
+ $('.import_gitlab_project').attr('title','');
+ $(".flash-container").html("")
+ } else {
+ $('.import_gitlab_project').attr('disabled',true);
+ $('.import_gitlab_project').attr('title', 'Project path required.');
+ }
+ })
diff --git a/app/workers/gitlab_remove_project_export_worker.rb b/app/workers/gitlab_remove_project_export_worker.rb
new file mode 100644
index 00000000000..1d91897d520
--- /dev/null
+++ b/app/workers/gitlab_remove_project_export_worker.rb
@@ -0,0 +1,9 @@
+class GitlabRemoveProjectExportWorker
+ include Sidekiq::Worker
+
+ sidekiq_options queue: :default
+
+ def perform
+ Project.remove_gitlab_exports!
+ end
+end
diff --git a/app/workers/project_export_worker.rb b/app/workers/project_export_worker.rb
new file mode 100644
index 00000000000..39f6037e077
--- /dev/null
+++ b/app/workers/project_export_worker.rb
@@ -0,0 +1,12 @@
+class ProjectExportWorker
+ include Sidekiq::Worker
+
+ sidekiq_options queue: :gitlab_shell, retry: true
+
+ 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