squashed - Fix DB exceptions raised importing some specific projects.
Better import of labels, milestones and protected branches. Updated relevant specs. Loose pipeline validation on importing, so it does not fail when there are missing fields, which are not validated at DB level. Also, updated spec with relevant test.
This commit is contained in:
parent
1d51bc7dfd
commit
e74b7d665b
10 changed files with 145 additions and 25 deletions
|
@ -158,6 +158,9 @@ v 8.11.6
|
||||||
- Restore SSH Key title auto-population behavior. !6186
|
- Restore SSH Key title auto-population behavior. !6186
|
||||||
- Fix DB schema to match latest migration. !6256
|
- Fix DB schema to match latest migration. !6256
|
||||||
- Exclude some pending or inactivated rows in Member scopes.
|
- Exclude some pending or inactivated rows in Member scopes.
|
||||||
|
- Fix Import/Export issues importing protected branches and some specific models
|
||||||
|
|
||||||
|
v 8.11.6 (unreleased)
|
||||||
|
|
||||||
v 8.11.5
|
v 8.11.5
|
||||||
- Optimize branch lookups and force a repository reload for Repository#find_branch. !6087
|
- Optimize branch lookups and force a repository reload for Repository#find_branch. !6087
|
||||||
|
|
|
@ -2,6 +2,7 @@ module Ci
|
||||||
class Pipeline < ActiveRecord::Base
|
class Pipeline < ActiveRecord::Base
|
||||||
extend Ci::Model
|
extend Ci::Model
|
||||||
include HasStatus
|
include HasStatus
|
||||||
|
include Importable
|
||||||
|
|
||||||
self.table_name = 'ci_commits'
|
self.table_name = 'ci_commits'
|
||||||
|
|
||||||
|
@ -12,12 +13,12 @@ module Ci
|
||||||
has_many :builds, class_name: 'Ci::Build', foreign_key: :commit_id
|
has_many :builds, class_name: 'Ci::Build', foreign_key: :commit_id
|
||||||
has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest', foreign_key: :commit_id
|
has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest', foreign_key: :commit_id
|
||||||
|
|
||||||
validates_presence_of :sha
|
validates_presence_of :sha, unless: :importing?
|
||||||
validates_presence_of :ref
|
validates_presence_of :ref, unless: :importing?
|
||||||
validates_presence_of :status
|
validates_presence_of :status, unless: :importing?
|
||||||
validate :valid_commit_sha
|
validate :valid_commit_sha, unless: :importing?
|
||||||
|
|
||||||
after_save :keep_around_commits
|
after_save :keep_around_commits, unless: :importing?
|
||||||
|
|
||||||
delegate :stages, to: :statuses
|
delegate :stages, to: :statuses
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ module Gitlab
|
||||||
module ImportExport
|
module ImportExport
|
||||||
extend self
|
extend self
|
||||||
|
|
||||||
VERSION = '0.1.3'
|
VERSION = '0.1.4'
|
||||||
FILENAME_LIMIT = 50
|
FILENAME_LIMIT = 50
|
||||||
|
|
||||||
def export_path(relative_path:)
|
def export_path(relative_path:)
|
||||||
|
|
|
@ -35,7 +35,9 @@ project_tree:
|
||||||
- :deploy_keys
|
- :deploy_keys
|
||||||
- :services
|
- :services
|
||||||
- :hooks
|
- :hooks
|
||||||
- :protected_branches
|
- protected_branches:
|
||||||
|
- :merge_access_levels
|
||||||
|
- :push_access_levels
|
||||||
- :labels
|
- :labels
|
||||||
- milestones:
|
- milestones:
|
||||||
- :events
|
- :events
|
||||||
|
|
|
@ -7,7 +7,9 @@ module Gitlab
|
||||||
variables: 'Ci::Variable',
|
variables: 'Ci::Variable',
|
||||||
triggers: 'Ci::Trigger',
|
triggers: 'Ci::Trigger',
|
||||||
builds: 'Ci::Build',
|
builds: 'Ci::Build',
|
||||||
hooks: 'ProjectHook' }.freeze
|
hooks: 'ProjectHook',
|
||||||
|
merge_access_levels: 'ProtectedBranch::MergeAccessLevel',
|
||||||
|
push_access_levels: 'ProtectedBranch::PushAccessLevel' }.freeze
|
||||||
|
|
||||||
USER_REFERENCES = %w[author_id assignee_id updated_by_id user_id].freeze
|
USER_REFERENCES = %w[author_id assignee_id updated_by_id user_id].freeze
|
||||||
|
|
||||||
|
@ -17,6 +19,8 @@ module Gitlab
|
||||||
|
|
||||||
EXISTING_OBJECT_CHECK = %i[milestone milestones label labels].freeze
|
EXISTING_OBJECT_CHECK = %i[milestone milestones label labels].freeze
|
||||||
|
|
||||||
|
FINDER_ATTRIBUTES = %w[title color project_id].freeze
|
||||||
|
|
||||||
def self.create(*args)
|
def self.create(*args)
|
||||||
new(*args).create
|
new(*args).create
|
||||||
end
|
end
|
||||||
|
@ -149,7 +153,7 @@ module Gitlab
|
||||||
end
|
end
|
||||||
|
|
||||||
def parsed_relation_hash
|
def parsed_relation_hash
|
||||||
@relation_hash.reject { |k, _v| !relation_class.attribute_method?(k) }
|
@parsed_relation_hash ||= @relation_hash.reject { |k, _v| !relation_class.attribute_method?(k) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_st_diffs
|
def set_st_diffs
|
||||||
|
@ -161,14 +165,30 @@ module Gitlab
|
||||||
# Otherwise always create the record, skipping the extra SELECT clause.
|
# Otherwise always create the record, skipping the extra SELECT clause.
|
||||||
@existing_or_new_object ||= begin
|
@existing_or_new_object ||= begin
|
||||||
if EXISTING_OBJECT_CHECK.include?(@relation_name)
|
if EXISTING_OBJECT_CHECK.include?(@relation_name)
|
||||||
existing_object = relation_class.find_or_initialize_by(parsed_relation_hash.slice('title', 'project_id'))
|
events = parsed_relation_hash.delete('events')
|
||||||
existing_object.assign_attributes(parsed_relation_hash)
|
|
||||||
|
unless events.blank?
|
||||||
|
existing_object.assign_attributes(events: events)
|
||||||
|
end
|
||||||
|
|
||||||
existing_object
|
existing_object
|
||||||
else
|
else
|
||||||
relation_class.new(parsed_relation_hash)
|
relation_class.new(parsed_relation_hash)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def existing_object
|
||||||
|
@existing_object ||=
|
||||||
|
begin
|
||||||
|
finder_hash = parsed_relation_hash.slice(*FINDER_ATTRIBUTES)
|
||||||
|
existing_object = relation_class.find_or_create_by(finder_hash)
|
||||||
|
# Done in two steps, as MySQL behaves differently than PostgreSQL using
|
||||||
|
# the +find_or_create_by+ method and does not return the ID the second time.
|
||||||
|
existing_object.update(parsed_relation_hash)
|
||||||
|
existing_object
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,8 +24,8 @@ module Gitlab
|
||||||
end
|
end
|
||||||
|
|
||||||
def verify_version!(version)
|
def verify_version!(version)
|
||||||
if Gem::Version.new(version) > Gem::Version.new(Gitlab::ImportExport.version)
|
if Gem::Version.new(version) != Gem::Version.new(Gitlab::ImportExport.version)
|
||||||
raise Gitlab::ImportExport::Error.new("Import version mismatch: Required <= #{Gitlab::ImportExport.version} but was #{version}")
|
raise Gitlab::ImportExport::Error.new("Import version mismatch: Required #{Gitlab::ImportExport.version} but was #{version}")
|
||||||
else
|
else
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
Binary file not shown.
|
@ -24,7 +24,7 @@
|
||||||
"test_ee_field": "test",
|
"test_ee_field": "test",
|
||||||
"milestone": {
|
"milestone": {
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"title": "v0.0",
|
"title": "test milestone",
|
||||||
"project_id": 8,
|
"project_id": 8,
|
||||||
"description": "test milestone",
|
"description": "test milestone",
|
||||||
"due_date": null,
|
"due_date": null,
|
||||||
|
@ -51,7 +51,7 @@
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"label_id": 2,
|
"label_id": 2,
|
||||||
"target_id": 3,
|
"target_id": 40,
|
||||||
"target_type": "Issue",
|
"target_type": "Issue",
|
||||||
"created_at": "2016-07-22T08:57:02.840Z",
|
"created_at": "2016-07-22T08:57:02.840Z",
|
||||||
"updated_at": "2016-07-22T08:57:02.840Z",
|
"updated_at": "2016-07-22T08:57:02.840Z",
|
||||||
|
@ -281,6 +281,31 @@
|
||||||
"deleted_at": null,
|
"deleted_at": null,
|
||||||
"due_date": null,
|
"due_date": null,
|
||||||
"moved_to_id": null,
|
"moved_to_id": null,
|
||||||
|
"milestone": {
|
||||||
|
"id": 1,
|
||||||
|
"title": "test milestone",
|
||||||
|
"project_id": 8,
|
||||||
|
"description": "test milestone",
|
||||||
|
"due_date": null,
|
||||||
|
"created_at": "2016-06-14T15:02:04.415Z",
|
||||||
|
"updated_at": "2016-06-14T15:02:04.415Z",
|
||||||
|
"state": "active",
|
||||||
|
"iid": 1,
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"id": 487,
|
||||||
|
"target_type": "Milestone",
|
||||||
|
"target_id": 1,
|
||||||
|
"title": null,
|
||||||
|
"data": null,
|
||||||
|
"project_id": 46,
|
||||||
|
"created_at": "2016-06-14T15:02:04.418Z",
|
||||||
|
"updated_at": "2016-06-14T15:02:04.418Z",
|
||||||
|
"action": 1,
|
||||||
|
"author_id": 18
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"notes": [
|
"notes": [
|
||||||
{
|
{
|
||||||
"id": 359,
|
"id": 359,
|
||||||
|
@ -494,6 +519,27 @@
|
||||||
"deleted_at": null,
|
"deleted_at": null,
|
||||||
"due_date": null,
|
"due_date": null,
|
||||||
"moved_to_id": null,
|
"moved_to_id": null,
|
||||||
|
"label_links": [
|
||||||
|
{
|
||||||
|
"id": 99,
|
||||||
|
"label_id": 2,
|
||||||
|
"target_id": 38,
|
||||||
|
"target_type": "Issue",
|
||||||
|
"created_at": "2016-07-22T08:57:02.840Z",
|
||||||
|
"updated_at": "2016-07-22T08:57:02.840Z",
|
||||||
|
"label": {
|
||||||
|
"id": 2,
|
||||||
|
"title": "test2",
|
||||||
|
"color": "#428bca",
|
||||||
|
"project_id": 8,
|
||||||
|
"created_at": "2016-07-22T08:55:44.161Z",
|
||||||
|
"updated_at": "2016-07-22T08:55:44.161Z",
|
||||||
|
"template": false,
|
||||||
|
"description": "",
|
||||||
|
"priority": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
"notes": [
|
"notes": [
|
||||||
{
|
{
|
||||||
"id": 367,
|
"id": 367,
|
||||||
|
@ -6478,7 +6524,7 @@
|
||||||
{
|
{
|
||||||
"id": 37,
|
"id": 37,
|
||||||
"project_id": 5,
|
"project_id": 5,
|
||||||
"ref": "master",
|
"ref": null,
|
||||||
"sha": "048721d90c449b244b7b4c53a9186b04330174ec",
|
"sha": "048721d90c449b244b7b4c53a9186b04330174ec",
|
||||||
"before_sha": null,
|
"before_sha": null,
|
||||||
"push_data": null,
|
"push_data": null,
|
||||||
|
@ -7301,6 +7347,30 @@
|
||||||
|
|
||||||
],
|
],
|
||||||
"protected_branches": [
|
"protected_branches": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"project_id": 9,
|
||||||
|
"name": "master",
|
||||||
|
"created_at": "2016-08-30T07:32:52.426Z",
|
||||||
|
"updated_at": "2016-08-30T07:32:52.426Z",
|
||||||
|
"merge_access_levels": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"protected_branch_id": 1,
|
||||||
|
"access_level": 40,
|
||||||
|
"created_at": "2016-08-30T07:32:52.458Z",
|
||||||
|
"updated_at": "2016-08-30T07:32:52.458Z"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"push_access_levels": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"protected_branch_id": 1,
|
||||||
|
"access_level": 40,
|
||||||
|
"created_at": "2016-08-30T07:32:52.490Z",
|
||||||
|
"updated_at": "2016-08-30T07:32:52.490Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -29,12 +29,30 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do
|
||||||
expect(project.project_feature.merge_requests_access_level).to eq(ProjectFeature::ENABLED)
|
expect(project.project_feature.merge_requests_access_level).to eq(ProjectFeature::ENABLED)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'has the same label associated to two issues' do
|
||||||
|
restored_project_json
|
||||||
|
|
||||||
|
expect(Label.first.issues.count).to eq(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has milestones associated to two separate issues' do
|
||||||
|
restored_project_json
|
||||||
|
|
||||||
|
expect(Milestone.find_by_description('test milestone').issues.count).to eq(2)
|
||||||
|
end
|
||||||
|
|
||||||
it 'creates a valid pipeline note' do
|
it 'creates a valid pipeline note' do
|
||||||
restored_project_json
|
restored_project_json
|
||||||
|
|
||||||
expect(Ci::Pipeline.first.notes).not_to be_empty
|
expect(Ci::Pipeline.first.notes).not_to be_empty
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'restores pipelines with missing ref' do
|
||||||
|
restored_project_json
|
||||||
|
|
||||||
|
expect(Ci::Pipeline.where(ref: nil)).not_to be_empty
|
||||||
|
end
|
||||||
|
|
||||||
it 'restores the correct event with symbolised data' do
|
it 'restores the correct event with symbolised data' do
|
||||||
restored_project_json
|
restored_project_json
|
||||||
|
|
||||||
|
@ -49,6 +67,18 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do
|
||||||
expect(issue.reload.updated_at.to_s).to eq('2016-06-14 15:02:47 UTC')
|
expect(issue.reload.updated_at.to_s).to eq('2016-06-14 15:02:47 UTC')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'contains the merge access levels on a protected branch' do
|
||||||
|
restored_project_json
|
||||||
|
|
||||||
|
expect(ProtectedBranch.first.merge_access_levels).not_to be_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'contains the push access levels on a protected branch' do
|
||||||
|
restored_project_json
|
||||||
|
|
||||||
|
expect(ProtectedBranch.first.push_access_levels).not_to be_empty
|
||||||
|
end
|
||||||
|
|
||||||
context 'event at forth level of the tree' do
|
context 'event at forth level of the tree' do
|
||||||
let(:event) { Event.where(title: 'test levels').first }
|
let(:event) { Event.where(title: 'test levels').first }
|
||||||
|
|
||||||
|
@ -77,12 +107,6 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do
|
||||||
expect(Label.first.label_links.first.target).not_to be_nil
|
expect(Label.first.label_links.first.target).not_to be_nil
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'has milestones associated to issues' do
|
|
||||||
restored_project_json
|
|
||||||
|
|
||||||
expect(Milestone.find_by_description('test milestone').issues).not_to be_empty
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'Merge requests' do
|
context 'Merge requests' do
|
||||||
before do
|
before do
|
||||||
restored_project_json
|
restored_project_json
|
||||||
|
|
|
@ -23,7 +23,7 @@ describe Gitlab::ImportExport::VersionChecker, services: true do
|
||||||
it 'shows the correct error message' do
|
it 'shows the correct error message' do
|
||||||
described_class.check!(shared: shared)
|
described_class.check!(shared: shared)
|
||||||
|
|
||||||
expect(shared.errors.first).to eq("Import version mismatch: Required <= #{Gitlab::ImportExport.version} but was #{version}")
|
expect(shared.errors.first).to eq("Import version mismatch: Required #{Gitlab::ImportExport.version} but was #{version}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue