Commit 4290e9c8 authored by James Lopez's avatar James Lopez

Merge branches 'feature/project-export' and 'feature/project-import' of...

Merge branches 'feature/project-export' and 'feature/project-import' of gitlab.com:gitlab-org/gitlab-ce into feature/project-import

# Conflicts:
#	lib/gitlab/import_export/import_export.yml
parents 9f5dd2de b6ab4a31
...@@ -5,10 +5,7 @@ module Projects ...@@ -5,10 +5,7 @@ module Projects
def execute(options = {}) def execute(options = {})
@shared = Gitlab::ImportExport::Shared.new(relative_path: File.join(project.path_with_namespace, 'work')) @shared = Gitlab::ImportExport::Shared.new(relative_path: File.join(project.path_with_namespace, 'work'))
# TODO handle errors # TODO handle errors
save_project_tree save_all if [save_project_tree, bundle_repo, bundle_wiki_repo].all?
bundle_repo
bundle_wiki_repo
save_all
end end
private private
......
...@@ -6,7 +6,7 @@ module Gitlab ...@@ -6,7 +6,7 @@ module Gitlab
File.join(storage_path, relative_path) File.join(storage_path, relative_path)
end end
def project_atts def project_attributes
%i(name path description issues_enabled wall_enabled merge_requests_enabled wiki_enabled snippets_enabled visibility_level archived) %i(name path description issues_enabled wall_enabled merge_requests_enabled wiki_enabled snippets_enabled visibility_level archived)
end end
......
module Gitlab module Gitlab
module ImportExport module ImportExport
module CommandLineUtil module CommandLineUtil
def tar_cf(archive:, dir:) def tar_czf(archive:, dir:)
tar_with_options(archive: archive, dir: dir, options: 'cf') tar_with_options(archive: archive, dir: dir, options: 'czf')
end end
def untar_zxf(archive:, dir:) def untar_zxf(archive:, dir:)
untar_with_options(archive: archive, dir: dir, options: 'zxf') untar_with_options(archive: archive, dir: dir, options: 'zxf')
end end
def untar_xf(archive:, dir:) def git_bundle(git_bin_path: Gitlab.config.git.bin_path, repo_path:, bundle_path:)
untar_with_options(archive: archive, dir: dir, options: 'xf') execute(%W(#{git_bin_path} --git-dir=#{repo_path} bundle create #{bundle_path} --all))
end end
def tar_czf(archive:, dir:) def git_unbundle(git_bin_path: Gitlab.config.git.bin_path, repo_path:, bundle_path:)
tar_with_options(archive: archive, dir: dir, options: 'czf') execute(%W(#{git_bin_path} clone --bare #{bundle_path} #{repo_path}))
end end
def git_bundle(git_bin_path: Gitlab.config.git.bin_path, repo_path:, bundle_path:) private
cmd = %W(#{git_bin_path} --git-dir=#{repo_path} bundle create #{bundle_path} --all)
_output, status = Gitlab::Popen.popen(cmd)
status.zero?
end
def git_unbundle(git_bin_path: Gitlab.config.git.bin_path, repo_path:, bundle_path:) def git_unbundle(git_bin_path: Gitlab.config.git.bin_path, repo_path:, bundle_path:)
cmd = %W(#{git_bin_path} clone --bare #{bundle_path} #{repo_path}) cmd = %W(#{git_bin_path} clone --bare #{bundle_path} #{repo_path})
...@@ -30,13 +26,14 @@ module Gitlab ...@@ -30,13 +26,14 @@ module Gitlab
end end
def tar_with_options(archive:, dir:, options:) def tar_with_options(archive:, dir:, options:)
cmd = %W(tar -#{options} #{archive} -C #{dir} .) execute(%W(tar -#{options} #{archive} -C #{dir} .))
_output, status = Gitlab::Popen.popen(cmd)
status.zero?
end end
def untar_with_options(archive:, dir:, options:) def untar_with_options(archive:, dir:, options:)
cmd = %W(tar -#{options} #{archive} -C #{dir}) execute(%W(tar -#{options} #{archive} -C #{dir}))
end
def execute(cmd)
_output, status = Gitlab::Popen.popen(cmd) _output, status = Gitlab::Popen.popen(cmd)
status.zero? status.zero?
end end
......
# Model relationships to be included in the project import/export # Model relationships to be included in the project import/export
:project_tree: project_tree:
- :issues: - issues:
- :notes - :notes
- :labels - :labels
- :milestones - :milestones
- :snippets - :snippets
- :releases - :releases
- :events - :events
- :project_members: - project_members:
- :user - :user
- :merge_requests: - merge_requests:
- :merge_request_diff - :merge_request_diff
- :notes - :notes
- :ci_commits: - ci_commits:
- :statuses - :statuses
# Only include the following attributes for the models specified. # Only include the following attributes for the models specified.
:attributes_only: included_attributes:
:project: project:
- :name - :name
- :path - :path
- :description - :description
...@@ -28,12 +28,12 @@ ...@@ -28,12 +28,12 @@
- :snippets_enabled - :snippets_enabled
- :visibility_level - :visibility_level
- :archived - :archived
:user: user:
- :id - :id
- :email - :email
- :username - :username
# Do not include the following attributes for the models specified. # Do not include the following attributes for the models specified.
:attributes_except: excluded_attributes:
:snippets: snippets:
- :expired_at - :expired_at
\ No newline at end of file
...@@ -4,7 +4,7 @@ module Gitlab ...@@ -4,7 +4,7 @@ module Gitlab
extend self extend self
def project_tree def project_tree
{ only: atts_only[:project], include: build_hash(tree) } { only: included_attributes[:project], include: build_hash(tree) }
end end
def tree def tree
...@@ -14,24 +14,24 @@ module Gitlab ...@@ -14,24 +14,24 @@ module Gitlab
private private
def config def config
@config ||= YAML.load_file('lib/gitlab/import_export/import_export.yml') @config ||= YAML.load_file('lib/gitlab/import_export/import_export.yml').with_indifferent_access
end end
def atts_only def included_attributes
config[:attributes_only] config[:included_attributes] || {}
end end
def atts_except def excluded_attributes
config[:attributes_except] config[:excluded_attributes] || {}
end end
def build_hash(array) def build_hash(array)
array.map do |el| array.map do |model_object|
if el.is_a?(Hash) if model_object.is_a?(Hash)
process_include(el) process_include(model_object)
else else
only_except_hash = check_only_and_except(el) only_except_hash = check_only_and_except(model_object)
only_except_hash.empty? ? el : { el => only_except_hash } only_except_hash.empty? ? model_object : { model_object => only_except_hash }
end end
end end
end end
...@@ -82,12 +82,12 @@ module Gitlab ...@@ -82,12 +82,12 @@ module Gitlab
def check_only(value) def check_only(value)
key = key_from_hash(value) key = key_from_hash(value)
atts_only[key].nil? ? {} : { only: atts_only[key] } included_attributes[key].nil? ? {} : { only: included_attributes[key] }
end end
def check_except(value) def check_except(value)
key = key_from_hash(value) key = key_from_hash(value)
atts_except[key].nil? ? {} : { except: atts_except[key] } excluded_attributes[key].nil? ? {} : { except: excluded_attributes[key] }
end end
def key_from_hash(value) def key_from_hash(value)
......
...@@ -3,19 +3,13 @@ module Gitlab ...@@ -3,19 +3,13 @@ module Gitlab
class ProjectTreeSaver class ProjectTreeSaver
attr_reader :full_path attr_reader :full_path
def initialize(project: , shared: ) def initialize(project:, shared:)
@project = project @project = project
@export_path = shared.export_path @export_path = shared.export_path
end
def save
@full_path = File.join(@export_path, project_filename) @full_path = File.join(@export_path, project_filename)
save_to_disk
end end
private def save
def save_to_disk
FileUtils.mkdir_p(@export_path) FileUtils.mkdir_p(@export_path)
File.write(full_path, project_json_tree) File.write(full_path, project_json_tree)
true true
...@@ -24,6 +18,8 @@ module Gitlab ...@@ -24,6 +18,8 @@ module Gitlab
false false
end end
private
# TODO remove magic keyword and move it to a shared config # TODO remove magic keyword and move it to a shared config
def project_filename def project_filename
"project.json" "project.json"
......
require 'rspec' require 'spec_helper'
describe Gitlab::ImportExport::ImportExportReader do describe Gitlab::ImportExport::ImportExportReader do
...@@ -17,7 +17,7 @@ describe Gitlab::ImportExport::ImportExportReader do ...@@ -17,7 +17,7 @@ describe Gitlab::ImportExport::ImportExportReader do
end end
it 'should generate hash from project tree config' do it 'should generate hash from project tree config' do
allow(described_class).to receive(:config).and_return(YAML.load_file(test_config)) allow(described_class).to receive(:config).and_return(YAML.load_file(test_config).deep_symbolize_keys)
expect(described_class.project_tree).to eq(project_tree_hash) expect(described_class.project_tree).to eq(project_tree_hash)
end end
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::ImportExport::ProjectTreeSaver, services: true do describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
describe :save do describe 'saves the project tree into a json object' do
# TODO refactor this into a setup method
let(:user) { create(:user) }
let(:issue) { create(:issue, assignee: user) }
let(:merge_request) { create(:merge_request) }
let(:label) { create(:label) }
let(:snippet) { create(:project_snippet) }
let(:commit_status) { create(:commit_status) }
let(:release) { create(:release) }
let!(:project) do
create(:project,
:public,
name: 'searchable_project',
issues: [issue],
merge_requests: [merge_request],
labels: [label],
snippets: [snippet],
releases: [release]
)
end
let!(:ci_commit) { create(:ci_commit, project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch, statuses: [commit_status]) }
let!(:milestone) { create(:milestone, title: "Milestone v1.2", project: project) }
let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" }
let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) } let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) }
let(:project_tree_saver) { Gitlab::ImportExport::ProjectTreeSaver.new(project: project, shared: shared) } let(:project_tree_saver) { described_class.new(project: project, shared: shared) }
let!(:issue_note) { create(:note, note: ":+1: issue", noteable: issue) } let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" }
let!(:merge_request_note) { create(:note, note: ":+1: merge_request", noteable: merge_request) } let(:user) { create(:user) }
let(:project) { setup_project }
before(:each) do before do
project.team << [user, :master] project.team << [user, :master]
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
end end
after(:each) do after do
FileUtils.rm_rf(export_path) FileUtils.rm_rf(export_path)
end end
...@@ -109,6 +87,30 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do ...@@ -109,6 +87,30 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
end end
end end
def setup_project
issue = create(:issue, assignee: user)
merge_request = create(:merge_request)
label = create(:label)
snippet = create(:project_snippet)
commit_status = create(:commit_status)
release = create(:release)
project = create(:project,
:public,
issues: [issue],
merge_requests: [merge_request],
labels: [label],
snippets: [snippet],
releases: [release]
)
create(:ci_commit, project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch, statuses: [commit_status])
create(:milestone, project: project)
create(:note, noteable: issue)
create(:note, noteable: merge_request)
project
end
def project_json(filename) def project_json(filename)
JSON.parse(IO.read(filename)) JSON.parse(IO.read(filename))
end end
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::ImportExport::RepoBundler, services: true do describe Gitlab::ImportExport::RepoBundler, services: true do
describe :bundle do describe 'bundle a project Git repo' do
let(:user) { create(:user) } let(:user) { create(:user) }
let!(:project) { create(:project, :public, name: 'searchable_project') } let!(:project) { create(:project, :public, name: 'searchable_project') }
let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" } let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" }
let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) } let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) }
let(:bundler) { Gitlab::ImportExport::RepoBundler.new(project: project, shared: shared) } let(:bundler) { described_class.new(project: project, shared: shared) }
before(:each) do before do
project.team << [user, :master] project.team << [user, :master]
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
end end
after(:each) do after do
FileUtils.rm_rf(export_path) FileUtils.rm_rf(export_path)
end end
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::ImportExport::WikiRepoBundler, services: true do describe Gitlab::ImportExport::WikiRepoBundler, services: true do
describe :bundle do describe 'bundle a wiki Git repo' do
let(:user) { create(:user) } let(:user) { create(:user) }
let!(:project) { create(:project, :public, name: 'searchable_project') } let!(:project) { create(:project, :public, name: 'searchable_project') }
let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" } let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" }
let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) } let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.path_with_namespace) }
let(:wiki_bundler) { Gitlab::ImportExport::WikiRepoBundler.new(project: project, shared: shared) } let(:wiki_bundler) { described_class.new(project: project, shared: shared) }
let!(:project_wiki) { ProjectWiki.new(project, user) } let!(:project_wiki) { ProjectWiki.new(project, user) }
before(:each) do before do
project.team << [user, :master] project.team << [user, :master]
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path) allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
project_wiki.wiki project_wiki.wiki
project_wiki.create_page("index", "test content") project_wiki.create_page("index", "test content")
end end
after(:each) do after do
FileUtils.rm_rf(export_path) FileUtils.rm_rf(export_path)
end end
......
# Class relationships to be included in the project import/export # Class relationships to be included in the project import/export
:project_tree: project_tree:
- :issues - :issues
- :labels - :labels
- :merge_requests: - merge_requests:
- :merge_request_diff - :merge_request_diff
- :merge_request_test - :merge_request_test
- :commit_statuses: - commit_statuses:
- :commit - :commit
:attributes_only: included_attributes:
:project: project:
- :name - :name
- :path - :path
:merge_requests: merge_requests:
- :id - :id
:attributes_except: excluded_attributes:
:merge_requests: merge_requests:
- :iid - :iid
\ No newline at end of file
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