summaryrefslogtreecommitdiff
path: root/lib/gitlab/github_import
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gitlab/github_import')
-rw-r--r--lib/gitlab/github_import/base_formatter.rb4
-rw-r--r--lib/gitlab/github_import/client.rb12
-rw-r--r--lib/gitlab/github_import/importer.rb115
-rw-r--r--lib/gitlab/github_import/issue_formatter.rb8
-rw-r--r--lib/gitlab/github_import/label_formatter.rb8
-rw-r--r--lib/gitlab/github_import/milestone_formatter.rb8
-rw-r--r--lib/gitlab/github_import/pull_request_formatter.rb8
-rw-r--r--lib/gitlab/github_import/release_formatter.rb8
8 files changed, 135 insertions, 36 deletions
diff --git a/lib/gitlab/github_import/base_formatter.rb b/lib/gitlab/github_import/base_formatter.rb
index 8cacf4f4925..6dbae64a9fe 100644
--- a/lib/gitlab/github_import/base_formatter.rb
+++ b/lib/gitlab/github_import/base_formatter.rb
@@ -10,7 +10,9 @@ module Gitlab
end
def create!
- self.klass.create!(self.attributes)
+ project.public_send(project_association).find_or_create_by!(find_condition) do |record|
+ record.attributes = attributes
+ end
end
private
diff --git a/lib/gitlab/github_import/client.rb b/lib/gitlab/github_import/client.rb
index 7f424b74efb..85df6547a67 100644
--- a/lib/gitlab/github_import/client.rb
+++ b/lib/gitlab/github_import/client.rb
@@ -105,18 +105,20 @@ module Gitlab
data = api.send(method, *args)
return data unless data.is_a?(Array)
+ last_response = api.last_response
+
if block_given?
yield data
- each_response_page(&block)
+ # api.last_response could change while we're yielding (e.g. fetching labels for each PR)
+ # so we cache our own last response
+ each_response_page(last_response, &block)
else
- each_response_page { |page| data.concat(page) }
+ each_response_page(last_response) { |page| data.concat(page) }
data
end
end
- def each_response_page
- last_response = api.last_response
-
+ def each_response_page(last_response)
while last_response.rels[:next]
sleep rate_limit_sleep_time if rate_limit_exceed?
last_response = last_response.rels[:next].get
diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb
index 4b70f33a851..ecc28799737 100644
--- a/lib/gitlab/github_import/importer.rb
+++ b/lib/gitlab/github_import/importer.rb
@@ -24,7 +24,8 @@ module Gitlab
import_milestones
import_issues
import_pull_requests
- import_comments
+ import_comments(:issues)
+ import_comments(:pull_requests)
import_wiki
import_releases
handle_errors
@@ -48,7 +49,7 @@ module Gitlab
end
def import_labels
- client.labels(repo, per_page: 100) do |labels|
+ fetch_resources(:labels, repo, per_page: 100) do |labels|
labels.each do |raw|
begin
label = LabelFormatter.new(project, raw).create!
@@ -61,7 +62,7 @@ module Gitlab
end
def import_milestones
- client.milestones(repo, state: :all, per_page: 100) do |milestones|
+ fetch_resources(:milestones, repo, state: :all, per_page: 100) do |milestones|
milestones.each do |raw|
begin
MilestoneFormatter.new(project, raw).create!
@@ -73,7 +74,7 @@ module Gitlab
end
def import_issues
- client.issues(repo, state: :all, sort: :created, direction: :asc, per_page: 100) do |issues|
+ fetch_resources(:issues, repo, state: :all, sort: :created, direction: :asc, per_page: 100) do |issues|
issues.each do |raw|
gh_issue = IssueFormatter.new(project, raw)
@@ -90,7 +91,7 @@ module Gitlab
end
def import_pull_requests
- client.pull_requests(repo, state: :all, sort: :created, direction: :asc, per_page: 100) do |pull_requests|
+ fetch_resources(:pull_requests, repo, state: :all, sort: :created, direction: :asc, per_page: 100) do |pull_requests|
pull_requests.each do |raw|
pull_request = PullRequestFormatter.new(project, raw)
next unless pull_request.valid?
@@ -132,8 +133,15 @@ module Gitlab
end
def apply_labels(issuable, raw_issuable)
- if raw_issuable.labels.count > 0
- label_ids = raw_issuable.labels
+ # GH returns labels for issues but not for pull requests!
+ labels = if issuable.is_a?(MergeRequest)
+ client.labels_for_issue(repo, raw_issuable.number)
+ else
+ raw_issuable.labels
+ end
+
+ if labels.count > 0
+ label_ids = labels
.map { |attrs| @labels[attrs.name] }
.compact
@@ -141,23 +149,35 @@ module Gitlab
end
end
- def import_comments
- client.issues_comments(repo, per_page: 100) do |comments|
- create_comments(comments, :issue)
- end
+ def import_comments(issuable_type)
+ resource_type = "#{issuable_type}_comments".to_sym
+
+ # Two notes here:
+ # 1. We don't have a distinctive attribute for comments (unlike issues iid), so we fetch the last inserted note,
+ # compare it against every comment in the current imported page until we find match, and that's where start importing
+ # 2. GH returns comments for _both_ issues and PRs through issues_comments API, while pull_requests_comments returns
+ # only comments on diffs, so select last note not based on noteable_type but on line_code
+ line_code_is = issuable_type == :pull_requests ? 'NOT NULL' : 'NULL'
+ last_note = project.notes.where("line_code IS #{line_code_is}").last
+
+ fetch_resources(resource_type, repo, per_page: 100) do |comments|
+ if last_note
+ discard_inserted_comments(comments, last_note)
+ last_note = nil
+ end
- client.pull_requests_comments(repo, per_page: 100) do |comments|
- create_comments(comments, :pull_request)
+ create_comments(comments)
end
end
- def create_comments(comments, issuable_type)
+ def create_comments(comments)
ActiveRecord::Base.no_touching do
comments.each do |raw|
begin
- comment = CommentFormatter.new(project, raw)
- issuable_class = issuable_type == :issue ? Issue : MergeRequest
- iid = raw.send("#{issuable_type}_url").split('/').last # GH doesn't return parent ID directly
+ comment = CommentFormatter.new(project, raw)
+ # GH does not return info about comment's parent, so we guess it by checking its URL!
+ *_, parent, iid = URI(raw.html_url).path.split('/')
+ issuable_class = parent == 'issues' ? Issue : MergeRequest
issuable = issuable_class.find_by_iid(iid)
next unless issuable
@@ -169,6 +189,24 @@ module Gitlab
end
end
+ def discard_inserted_comments(comments, last_note)
+ last_note_attrs = nil
+
+ cut_off_index = comments.find_index do |raw|
+ comment = CommentFormatter.new(project, raw)
+ comment_attrs = comment.attributes
+ last_note_attrs ||= last_note.slice(*comment_attrs.keys)
+
+ comment_attrs.with_indifferent_access == last_note_attrs
+ end
+
+ # No matching resource in the collection, which means we got halted right on the end of the last page, so all good
+ return unless cut_off_index
+
+ # Otherwise, remove the resources we've already inserted
+ comments.shift(cut_off_index + 1)
+ end
+
def import_wiki
unless project.wiki.repository_exists?
wiki = WikiFormatter.new(project)
@@ -184,7 +222,7 @@ module Gitlab
end
def import_releases
- client.releases(repo, per_page: 100) do |releases|
+ fetch_resources(:releases, repo, per_page: 100) do |releases|
releases.each do |raw|
begin
gh_release = ReleaseFormatter.new(project, raw)
@@ -195,6 +233,47 @@ module Gitlab
end
end
end
+
+ def fetch_resources(resource_type, *opts)
+ return if imported?(resource_type)
+
+ opts.last.merge!(page: current_page(resource_type))
+
+ client.public_send(resource_type, *opts) do |resources|
+ yield resources
+ increment_page(resource_type)
+ end
+
+ imported!(resource_type)
+ end
+
+ def imported?(resource_type)
+ Rails.cache.read("#{cache_key_prefix}:#{resource_type}:imported")
+ end
+
+ def imported!(resource_type)
+ Rails.cache.write("#{cache_key_prefix}:#{resource_type}:imported", true, ex: 1.day)
+ end
+
+ def increment_page(resource_type)
+ key = "#{cache_key_prefix}:#{resource_type}:current-page"
+
+ # Rails.cache.increment calls INCRBY directly on the value stored under the key, which is
+ # a serialized ActiveSupport::Cache::Entry, so it will return an error by Redis, hence this ugly work-around
+ page = Rails.cache.read(key)
+ page += 1
+ Rails.cache.write(key, page)
+
+ page
+ end
+
+ def current_page(resource_type)
+ Rails.cache.fetch("#{cache_key_prefix}:#{resource_type}:current-page", ex: 1.day) { 1 }
+ end
+
+ def cache_key_prefix
+ @cache_key_prefix ||= "github-import:#{project.id}"
+ end
end
end
end
diff --git a/lib/gitlab/github_import/issue_formatter.rb b/lib/gitlab/github_import/issue_formatter.rb
index 77621de9f4c..8c32ac59fc5 100644
--- a/lib/gitlab/github_import/issue_formatter.rb
+++ b/lib/gitlab/github_import/issue_formatter.rb
@@ -20,8 +20,12 @@ module Gitlab
raw_data.comments > 0
end
- def klass
- Issue
+ def project_association
+ :issues
+ end
+
+ def find_condition
+ { iid: number }
end
def number
diff --git a/lib/gitlab/github_import/label_formatter.rb b/lib/gitlab/github_import/label_formatter.rb
index 942dfb3312b..211ccdc51bb 100644
--- a/lib/gitlab/github_import/label_formatter.rb
+++ b/lib/gitlab/github_import/label_formatter.rb
@@ -9,14 +9,14 @@ module Gitlab
}
end
- def klass
- Label
+ def project_association
+ :labels
end
def create!
params = attributes.except(:project)
- service = ::Labels::FindOrCreateService.new(project.owner, project, params)
- label = service.execute
+ service = ::Labels::FindOrCreateService.new(nil, project, params)
+ label = service.execute(skip_authorization: true)
raise ActiveRecord::RecordInvalid.new(label) unless label.persisted?
diff --git a/lib/gitlab/github_import/milestone_formatter.rb b/lib/gitlab/github_import/milestone_formatter.rb
index b2fa524cf5b..401dd962521 100644
--- a/lib/gitlab/github_import/milestone_formatter.rb
+++ b/lib/gitlab/github_import/milestone_formatter.rb
@@ -14,8 +14,12 @@ module Gitlab
}
end
- def klass
- Milestone
+ def project_association
+ :milestones
+ end
+
+ def find_condition
+ { iid: raw_data.number }
end
private
diff --git a/lib/gitlab/github_import/pull_request_formatter.rb b/lib/gitlab/github_import/pull_request_formatter.rb
index 1408683100f..b9a227fb11a 100644
--- a/lib/gitlab/github_import/pull_request_formatter.rb
+++ b/lib/gitlab/github_import/pull_request_formatter.rb
@@ -24,8 +24,12 @@ module Gitlab
}
end
- def klass
- MergeRequest
+ def project_association
+ :merge_requests
+ end
+
+ def find_condition
+ { iid: number }
end
def number
diff --git a/lib/gitlab/github_import/release_formatter.rb b/lib/gitlab/github_import/release_formatter.rb
index 73d643b00ad..1ad702a6058 100644
--- a/lib/gitlab/github_import/release_formatter.rb
+++ b/lib/gitlab/github_import/release_formatter.rb
@@ -11,8 +11,12 @@ module Gitlab
}
end
- def klass
- Release
+ def project_association
+ :releases
+ end
+
+ def find_condition
+ { tag: raw_data.tag_name }
end
def valid?