summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Barbosa Alexandre <dbalexandre@gmail.com>2017-04-11 22:36:33 -0300
committerDouglas Barbosa Alexandre <dbalexandre@gmail.com>2017-04-24 16:17:52 -0300
commitf76363e445153ea831fd508e366c0f31b24fa53e (patch)
tree01c3131715099235baa560a2a58557d816f72d7c
parentb43ecca9060b3d7ffd84d700caecf5f35fd403a9 (diff)
downloadgitlab-ce-f76363e445153ea831fd508e366c0f31b24fa53e.tar.gz
Add basic importer for GitHub repositories
-rw-r--r--lib/github/import.rb166
1 files changed, 166 insertions, 0 deletions
diff --git a/lib/github/import.rb b/lib/github/import.rb
new file mode 100644
index 00000000000..89f597551f9
--- /dev/null
+++ b/lib/github/import.rb
@@ -0,0 +1,166 @@
+module Github
+ class Import
+ class MergeRequest < ::MergeRequest
+ self.table_name = 'merge_requests'
+
+ self.reset_callbacks :save
+ self.reset_callbacks :commit
+ self.reset_callbacks :update
+ self.reset_callbacks :validate
+ end
+
+ attr_reader :project, :repository, :cached_user_ids, :errors
+
+ def initialize(project)
+ @project = project
+ @repository = project.repository
+ @cached_user_ids = {}
+ @errors = []
+ end
+
+ def execute(owner, repo, token)
+ # Fetch repository
+ begin
+ project.create_repository
+ project.repository.add_remote('github', "https://#{token}@github.com/#{owner}/#{repo}.git")
+ project.repository.set_remote_as_mirror('github')
+ project.repository.fetch_remote('github', forced: true)
+ project.repository.remove_remote('github')
+ rescue Gitlab::Shell::Error => e
+ error(:project, "https://github.com/#{owner}/#{repo}.git", e.message)
+ end
+
+ # Fetch labels
+ labels = Github::Labels.new(owner, repo).fetch
+
+ labels.each do |raw|
+ begin
+ label = Github::Representation::Label.new(raw)
+ project.labels.create!(title: label.title, color: label.color)
+ rescue => e
+ error(:label, label.url, e.message)
+ end
+ end
+
+ # Fetch milestones
+ milestones = Github::Milestones.new(owner, repo).fetch
+
+ milestones.each do |raw|
+ begin
+ milestone = Github::Representation::Milestone.new(raw)
+
+ project.milestones.create!(
+ iid: milestone.iid,
+ title: milestone.title,
+ description: milestone.description,
+ due_date: milestone.due_date,
+ state: milestone.state,
+ created_at: milestone.created_at,
+ updated_at: milestone.updated_at
+ )
+ rescue => e
+ error(:milestone, milestone.url, e.message)
+ end
+ end
+
+ # Fetch pull requests
+ pull_requests = Github::PullRequests.new(owner, repo).fetch
+
+ pull_requests.each do |raw|
+ pull_request = Github::Representation::PullRequest.new(project, raw)
+ next unless pull_request.valid?
+
+ begin
+ restore_source_branch(pull_request) unless pull_request.source_branch_exists?
+ restore_target_branch(pull_request) unless pull_request.target_branch_exists?
+
+ merge_request = MergeRequest.find_or_initialize_by(iid: pull_request.iid, source_project_id: project.id) do |record|
+ record.iid = pull_request.iid
+ record.title = pull_request.title
+ record.description = pull_request.description
+ record.source_project = pull_request.source_project
+ record.source_branch = pull_request.source_branch_name
+ record.source_branch_sha = pull_request.source_branch_sha
+ record.target_project = pull_request.target_project
+ record.target_branch = pull_request.target_branch_name
+ record.target_branch_sha = pull_request.target_branch_sha
+ record.state = pull_request.state
+ record.milestone_id = milestone_id(pull_request.milestone)
+ record.author_id = user_id(pull_request.author, project.creator_id)
+ record.assignee_id = user_id(pull_request.assignee)
+ record.created_at = pull_request.created_at
+ record.updated_at = pull_request.updated_at
+ end
+
+ merge_request.save(validate: false)
+ merge_request.merge_request_diffs.create
+ rescue => e
+ error(:pull_request, pull_request.url, "#{e.message}\n\n#{e.exception.backtrace.join('\n')}")
+ ensure
+ clean_up_restored_branches(pull_request)
+ end
+ end
+
+ repository.expire_content_cache
+
+ errors
+ end
+
+ private
+
+ def restore_source_branch(pull_request)
+ repository.create_branch(pull_request.source_branch_name, pull_request.source_branch_sha)
+ end
+
+ def restore_target_branch(pull_request)
+ repository.create_branch(pull_request.target_branch_name, pull_request.target_branch_sha)
+ end
+
+ def remove_branch(name)
+ repository.delete_branch(name)
+ rescue Rugged::ReferenceError
+ errors << { type: :branch, url: nil, error: "Could not clean up restored branch: #{name}" }
+ end
+
+ def clean_up_restored_branches(pull_request)
+ return if pull_request.opened?
+
+ remove_branch(pull_request.source_branch_name) unless pull_request.source_branch_exists?
+ remove_branch(pull_request.target_branch_name) unless pull_request.target_branch_exists?
+ end
+
+ def milestone_id(milestone)
+ return unless milestone.present?
+
+ project.milestones.select(:id).find_by(iid: milestone.iid)&.id
+ end
+
+ def user_id(user, fallback_id = nil)
+ return unless user.present?
+ return cached_user_ids[user.id] if cached_user_ids.key?(user.id)
+
+ cached_user_ids[user.id] = find_by_external_uid(user.id) || find_by_email(user.email) || fallback_id
+ end
+
+ def find_by_email(email)
+ return nil unless email
+
+ ::User.find_by_any_email(email)&.id
+ end
+
+ def find_by_external_uid(id)
+ return nil unless id
+
+ identities = ::Identity.arel_table
+
+ ::User.select(:id)
+ .joins(:identities)
+ .where(identities[:provider].eq(:github).and(identities[:extern_uid].eq(id)))
+ .first&.id
+ end
+
+ def error(type, url, message)
+ errors << { type: type, url: Gitlab::UrlSanitizer.sanitize(url), error: message }
+ end
+ end
+end