From ebd8e4333a263138abf2113dd315a97352851cbe Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 25 Jun 2018 13:06:10 -0700 Subject: WIP: Add support for Bitbucket Server imports --- lib/bitbucket_server/client.rb | 59 ++++++++++++++++++ lib/bitbucket_server/connection.rb | 35 +++++++++++ lib/bitbucket_server/page.rb | 34 +++++++++++ lib/bitbucket_server/paginator.rb | 36 +++++++++++ lib/bitbucket_server/representation/base.rb | 15 +++++ lib/bitbucket_server/representation/comment.rb | 27 ++++++++ lib/bitbucket_server/representation/issue.rb | 53 ++++++++++++++++ .../representation/pull_request.rb | 65 ++++++++++++++++++++ .../representation/pull_request_comment.rb | 39 ++++++++++++ lib/bitbucket_server/representation/repo.rb | 71 ++++++++++++++++++++++ lib/bitbucket_server/representation/user.rb | 9 +++ lib/gitlab/import_sources.rb | 3 +- 12 files changed, 445 insertions(+), 1 deletion(-) create mode 100644 lib/bitbucket_server/client.rb create mode 100644 lib/bitbucket_server/connection.rb create mode 100644 lib/bitbucket_server/page.rb create mode 100644 lib/bitbucket_server/paginator.rb create mode 100644 lib/bitbucket_server/representation/base.rb create mode 100644 lib/bitbucket_server/representation/comment.rb create mode 100644 lib/bitbucket_server/representation/issue.rb create mode 100644 lib/bitbucket_server/representation/pull_request.rb create mode 100644 lib/bitbucket_server/representation/pull_request_comment.rb create mode 100644 lib/bitbucket_server/representation/repo.rb create mode 100644 lib/bitbucket_server/representation/user.rb (limited to 'lib') diff --git a/lib/bitbucket_server/client.rb b/lib/bitbucket_server/client.rb new file mode 100644 index 00000000000..1f2f03790dd --- /dev/null +++ b/lib/bitbucket_server/client.rb @@ -0,0 +1,59 @@ +module BitbucketServer + class Client + attr_reader :connection + + def initialize(options = {}) + @connection = Connection.new(options) + end + + def issues(repo) + path = "/repositories/#{repo}/issues" + get_collection(path, :issue) + end + + def issue_comments(repo, issue_id) + path = "/repositories/#{repo}/issues/#{issue_id}/comments" + get_collection(path, :comment) + end + + def pull_requests(repo) + path = "/repositories/#{repo}/pullrequests?state=ALL" + get_collection(path, :pull_request) + end + + def pull_request_comments(repo, pull_request) + path = "/repositories/#{repo}/pullrequests/#{pull_request}/comments" + get_collection(path, :pull_request_comment) + end + + def pull_request_diff(repo, pull_request) + path = "/repositories/#{repo}/pullrequests/#{pull_request}/diff" + connection.get(path) + end + + def repo(project, repo_name) + parsed_response = connection.get("/projects/#{project}/repos/#{repo_name}") + # XXXX TODO Handle failure + BitbucketServer::Representation::Repo.new(parsed_response) + end + + def repos + path = "/repos" + get_collection(path, :repo) + end + + def user + @user ||= begin + parsed_response = connection.get('/user') + BitbucketServer::Representation::User.new(parsed_response) + end + end + + private + + def get_collection(path, type) + paginator = BitbucketServer::Paginator.new(connection, path, type) + BitbucketServer::Collection.new(paginator) + end + end +end diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb new file mode 100644 index 00000000000..64f12527a8d --- /dev/null +++ b/lib/bitbucket_server/connection.rb @@ -0,0 +1,35 @@ +module BitbucketServer + class Connection + DEFAULT_API_VERSION = '1.0'.freeze + + attr_reader :api_version, :base_uri, :username, :token + + def initialize(options = {}) + @api_version = options.fetch(:api_version, DEFAULT_API_VERSION) + @base_uri = options[:base_uri] + @username = options[:username] + @token = options[:personal_access_token] + end + + def get(path, extra_query = {}) + auth = { username: username, password: token } + response = Gitlab::HTTP.get(build_url(path), + basic_auth: auth, + params: extra_query) + ## Handle failure + response.parsed_response + end + + private + + def build_url(path) + return path if path.starts_with?(root_url) + + "#{root_url}#{path}" + end + + def root_url + "#{base_uri}/rest/api/#{api_version}" + end + end +end diff --git a/lib/bitbucket_server/page.rb b/lib/bitbucket_server/page.rb new file mode 100644 index 00000000000..17be8cbb860 --- /dev/null +++ b/lib/bitbucket_server/page.rb @@ -0,0 +1,34 @@ +module BitbucketServer + class Page + attr_reader :attrs, :items + + def initialize(raw, type) + @attrs = parse_attrs(raw) + @items = parse_values(raw, representation_class(type)) + end + + def next? + !attrs.fetch(:isLastPage, true) + end + + def next + attrs.fetch(:nextPageStart) + end + + private + + def parse_attrs(raw) + raw.slice(*%w(size nextPageStart isLastPage)).symbolize_keys + end + + def parse_values(raw, bitbucket_rep_class) + return [] unless raw['values'] && raw['values'].is_a?(Array) + + bitbucket_rep_class.decorate(raw['values']) + end + + def representation_class(type) + BitbucketServer::Representation.const_get(type.to_s.camelize) + end + end +end diff --git a/lib/bitbucket_server/paginator.rb b/lib/bitbucket_server/paginator.rb new file mode 100644 index 00000000000..c995cf4c3bd --- /dev/null +++ b/lib/bitbucket_server/paginator.rb @@ -0,0 +1,36 @@ +module BitbucketServer + class Paginator + PAGE_LENGTH = 25 # The minimum length is 10 and the maximum is 100. + + def initialize(connection, url, type) + @connection = connection + @type = type + @url = url + @page = nil + end + + def items + raise StopIteration unless has_next_page? + + @page = fetch_next_page + @page.items + end + + private + + attr_reader :connection, :page, :url, :type + + def has_next_page? + page.nil? || page.next? + end + + def next_url + page.nil? ? url : page.next + end + + def fetch_next_page + parsed_response = connection.get(next_url, pagelen: PAGE_LENGTH, sort: :created_on) + Page.new(parsed_response, type) + end + end +end diff --git a/lib/bitbucket_server/representation/base.rb b/lib/bitbucket_server/representation/base.rb new file mode 100644 index 00000000000..11b32b70c4c --- /dev/null +++ b/lib/bitbucket_server/representation/base.rb @@ -0,0 +1,15 @@ +module BitbucketServer + module Representation + class Base + attr_reader :raw + + def initialize(raw) + @raw = raw + end + + def self.decorate(entries) + entries.map { |entry| new(entry)} + end + end + end +end diff --git a/lib/bitbucket_server/representation/comment.rb b/lib/bitbucket_server/representation/comment.rb new file mode 100644 index 00000000000..4937aa9728f --- /dev/null +++ b/lib/bitbucket_server/representation/comment.rb @@ -0,0 +1,27 @@ +module Bitbucket + module Representation + class Comment < Representation::Base + def author + user['username'] + end + + def note + raw.fetch('content', {}).fetch('raw', nil) + end + + def created_at + raw['created_on'] + end + + def updated_at + raw['updated_on'] || raw['created_on'] + end + + private + + def user + raw.fetch('user', {}) + end + end + end +end diff --git a/lib/bitbucket_server/representation/issue.rb b/lib/bitbucket_server/representation/issue.rb new file mode 100644 index 00000000000..44bcbc250b3 --- /dev/null +++ b/lib/bitbucket_server/representation/issue.rb @@ -0,0 +1,53 @@ +module Bitbucket + module Representation + class Issue < Representation::Base + CLOSED_STATUS = %w(resolved invalid duplicate wontfix closed).freeze + + def iid + raw['id'] + end + + def kind + raw['kind'] + end + + def author + raw.dig('reporter', 'username') + end + + def description + raw.fetch('content', {}).fetch('raw', nil) + end + + def state + closed? ? 'closed' : 'opened' + end + + def title + raw['title'] + end + + def milestone + raw['milestone']['name'] if raw['milestone'].present? + end + + def created_at + raw['created_on'] + end + + def updated_at + raw['edited_on'] + end + + def to_s + iid + end + + private + + def closed? + CLOSED_STATUS.include?(raw['state']) + end + end + end +end diff --git a/lib/bitbucket_server/representation/pull_request.rb b/lib/bitbucket_server/representation/pull_request.rb new file mode 100644 index 00000000000..3553f3adbc7 --- /dev/null +++ b/lib/bitbucket_server/representation/pull_request.rb @@ -0,0 +1,65 @@ +module BitbucketServer + module Representation + class PullRequest < Representation::Base + def author + raw.fetch('author', {}).fetch('username', nil) + end + + def description + raw['description'] + end + + def iid + raw['id'] + end + + def state + if raw['state'] == 'MERGED' + 'merged' + elsif raw['state'] == 'DECLINED' + 'closed' + else + 'opened' + end + end + + def created_at + raw['created_on'] + end + + def updated_at + raw['updated_on'] + end + + def title + raw['title'] + end + + def source_branch_name + source_branch.fetch('branch', {}).fetch('name', nil) + end + + def source_branch_sha + source_branch.fetch('commit', {}).fetch('hash', nil) + end + + def target_branch_name + target_branch.fetch('branch', {}).fetch('name', nil) + end + + def target_branch_sha + target_branch.fetch('commit', {}).fetch('hash', nil) + end + + private + + def source_branch + raw['source'] + end + + def target_branch + raw['destination'] + end + end + end +end diff --git a/lib/bitbucket_server/representation/pull_request_comment.rb b/lib/bitbucket_server/representation/pull_request_comment.rb new file mode 100644 index 00000000000..c52acbc3ddc --- /dev/null +++ b/lib/bitbucket_server/representation/pull_request_comment.rb @@ -0,0 +1,39 @@ +module Bitbucket + module Representation + class PullRequestComment < Comment + def iid + raw['id'] + end + + def file_path + inline.fetch('path') + end + + def old_pos + inline.fetch('from') + end + + def new_pos + inline.fetch('to') + end + + def parent_id + raw.fetch('parent', {}).fetch('id', nil) + end + + def inline? + raw.key?('inline') + end + + def has_parent? + raw.key?('parent') + end + + private + + def inline + raw.fetch('inline', {}) + end + end + end +end diff --git a/lib/bitbucket_server/representation/repo.rb b/lib/bitbucket_server/representation/repo.rb new file mode 100644 index 00000000000..f4bdb277d28 --- /dev/null +++ b/lib/bitbucket_server/representation/repo.rb @@ -0,0 +1,71 @@ +module BitbucketServer + module Representation + class Repo < Representation::Base + attr_reader :owner, :slug + + def initialize(raw) + super(raw) + end + + def owner + project['name'] + end + + def slug + raw['slug'] + end + + def clone_url(token = nil) + url = raw['links']['clone'].find { |link| link['name'].starts_with?('http') }.fetch('href') + + if token.present? + clone_url = URI.parse(url) + clone_url.user = "x-token-auth:#{token}" + clone_url.to_s + else + url + end + end + + def description + project['description'] + end + + def full_name + "#{owner}/#{name}" + end + + def issues_enabled? + true + end + + def name + raw['name'] + end + + def valid? + raw['scmId'] == 'git' + end + + def has_wiki? + false + end + + def visibility_level + if project['public'] + Gitlab::VisibilityLevel::PUBLIC + else + Gitlab::VisibilityLevel::PRIVATE + end + end + + def project + raw['project'] + end + + def to_s + full_name + end + end + end +end diff --git a/lib/bitbucket_server/representation/user.rb b/lib/bitbucket_server/representation/user.rb new file mode 100644 index 00000000000..174f3a55f2c --- /dev/null +++ b/lib/bitbucket_server/representation/user.rb @@ -0,0 +1,9 @@ +module BitbucketServer + module Representation + class User < Representation::Base + def username + raw['username'] + end + end + end +end diff --git a/lib/gitlab/import_sources.rb b/lib/gitlab/import_sources.rb index 60d5fa4d29a..10289af6b25 100644 --- a/lib/gitlab/import_sources.rb +++ b/lib/gitlab/import_sources.rb @@ -10,7 +10,8 @@ module Gitlab # We exclude `bare_repository` here as it has no import class associated ImportTable = [ ImportSource.new('github', 'GitHub', Gitlab::GithubImport::ParallelImporter), - ImportSource.new('bitbucket', 'Bitbucket', Gitlab::BitbucketImport::Importer), + ImportSource.new('bitbucket', 'Bitbucket Cloud', Gitlab::BitbucketImport::Importer), + ImportSource.new('bitbucket_server', 'Bitbucket Server', Gitlab::BitbucketServerImport::Importer), ImportSource.new('gitlab', 'GitLab.com', Gitlab::GitlabImport::Importer), ImportSource.new('google_code', 'Google Code', Gitlab::GoogleCodeImport::Importer), ImportSource.new('fogbugz', 'FogBugz', Gitlab::FogbugzImport::Importer), -- cgit v1.2.3 From c9deb7cef8d0d85a8773b0e6d26ae12a3ff25a0e Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 25 Jun 2018 13:13:59 -0700 Subject: Copy and paste importer from Bitbucket Importer --- lib/gitlab/bitbucket_server_import/importer.rb | 247 +++++++++++++++++++++ .../bitbucket_server_import/project_creator.rb | 37 +++ 2 files changed, 284 insertions(+) create mode 100644 lib/gitlab/bitbucket_server_import/importer.rb create mode 100644 lib/gitlab/bitbucket_server_import/project_creator.rb (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb new file mode 100644 index 00000000000..d85e2ae2ca7 --- /dev/null +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -0,0 +1,247 @@ +module Gitlab + module BitbucketServerImport + class Importer + include Gitlab::ShellAdapter + + LABELS = [{ title: 'bug', color: '#FF0000' }, + { title: 'enhancement', color: '#428BCA' }, + { title: 'proposal', color: '#69D100' }, + { title: 'task', color: '#7F8C8D' }].freeze + + attr_reader :project, :client, :errors, :users + + def initialize(project) + @project = project + @client = BitbucketServer::Client.new(project.import_data.credentials) + @formatter = Gitlab::ImportFormatter.new + @labels = {} + @errors = [] + @users = {} + end + + def execute + import_issues + import_pull_requests + handle_errors + + true + end + + private + + def handle_errors + return unless errors.any? + + project.update_column(:import_error, { + message: 'The remote data could not be fully imported.', + errors: errors + }.to_json) + end + + def gitlab_user_id(project, username) + find_user_id(username) || project.creator_id + end + + def find_user_id(username) + return nil unless username + + return users[username] if users.key?(username) + + users[username] = User.select(:id) + .joins(:identities) + .find_by("identities.extern_uid = ? AND identities.provider = 'bitbucket'", username) + .try(:id) + end + + def repo + @repo ||= client.repo(project.import_source) + end + + def import_issues + return unless repo.issues_enabled? + + create_labels + + client.issues(repo).each do |issue| + begin + description = '' + description += @formatter.author_line(issue.author) unless find_user_id(issue.author) + description += issue.description + + label_name = issue.kind + milestone = issue.milestone ? project.milestones.find_or_create_by(title: issue.milestone) : nil + + gitlab_issue = project.issues.create!( + iid: issue.iid, + title: issue.title, + description: description, + state: issue.state, + author_id: gitlab_user_id(project, issue.author), + milestone: milestone, + created_at: issue.created_at, + updated_at: issue.updated_at + ) + + gitlab_issue.labels << @labels[label_name] + + import_issue_comments(issue, gitlab_issue) if gitlab_issue.persisted? + rescue StandardError => e + errors << { type: :issue, iid: issue.iid, errors: e.message } + end + end + end + + def import_issue_comments(issue, gitlab_issue) + client.issue_comments(repo, issue.iid).each do |comment| + # The note can be blank for issue service messages like "Changed title: ..." + # We would like to import those comments as well but there is no any + # specific parameter that would allow to process them, it's just an empty comment. + # To prevent our importer from just crashing or from creating useless empty comments + # we do this check. + next unless comment.note.present? + + note = '' + note += @formatter.author_line(comment.author) unless find_user_id(comment.author) + note += comment.note + + begin + gitlab_issue.notes.create!( + project: project, + note: note, + author_id: gitlab_user_id(project, comment.author), + created_at: comment.created_at, + updated_at: comment.updated_at + ) + rescue StandardError => e + errors << { type: :issue_comment, iid: issue.iid, errors: e.message } + end + end + end + + def create_labels + LABELS.each do |label_params| + label = ::Labels::CreateService.new(label_params).execute(project: project) + if label.valid? + @labels[label_params[:title]] = label + else + raise "Failed to create label \"#{label_params[:title]}\" for project \"#{project.full_name}\"" + end + end + end + + def import_pull_requests + pull_requests = client.pull_requests(repo) + + pull_requests.each do |pull_request| + begin + description = '' + description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author) + description += pull_request.description + + source_branch_sha = pull_request.source_branch_sha + target_branch_sha = pull_request.target_branch_sha + source_branch_sha = project.repository.commit(source_branch_sha)&.sha || source_branch_sha + target_branch_sha = project.repository.commit(target_branch_sha)&.sha || target_branch_sha + + merge_request = project.merge_requests.create!( + iid: pull_request.iid, + title: pull_request.title, + description: description, + source_project: project, + source_branch: pull_request.source_branch_name, + source_branch_sha: source_branch_sha, + target_project: project, + target_branch: pull_request.target_branch_name, + target_branch_sha: target_branch_sha, + state: pull_request.state, + author_id: gitlab_user_id(project, pull_request.author), + assignee_id: nil, + created_at: pull_request.created_at, + updated_at: pull_request.updated_at + ) + + import_pull_request_comments(pull_request, merge_request) if merge_request.persisted? + rescue StandardError => e + errors << { type: :pull_request, iid: pull_request.iid, errors: e.message, trace: e.backtrace.join("\n"), raw_response: pull_request.raw } + end + end + end + + def import_pull_request_comments(pull_request, merge_request) + comments = client.pull_request_comments(repo, pull_request.iid) + + inline_comments, pr_comments = comments.partition(&:inline?) + + import_inline_comments(inline_comments, pull_request, merge_request) + import_standalone_pr_comments(pr_comments, merge_request) + end + + def import_inline_comments(inline_comments, pull_request, merge_request) + line_code_map = {} + + children, parents = inline_comments.partition(&:has_parent?) + + # The BitbucketServer API returns threaded replies as parent-child + # relationships. We assume that the child can appear in any order in + # the JSON. + parents.each do |comment| + line_code_map[comment.iid] = generate_line_code(comment) + end + + children.each do |comment| + line_code_map[comment.iid] = line_code_map.fetch(comment.parent_id, nil) + end + + inline_comments.each do |comment| + begin + attributes = pull_request_comment_attributes(comment) + attributes.merge!( + position: build_position(merge_request, comment), + line_code: line_code_map.fetch(comment.iid), + type: 'DiffNote') + + merge_request.notes.create!(attributes) + rescue StandardError => e + errors << { type: :pull_request, iid: comment.iid, errors: e.message } + end + end + end + + def build_position(merge_request, pr_comment) + params = { + diff_refs: merge_request.diff_refs, + old_path: pr_comment.file_path, + new_path: pr_comment.file_path, + old_line: pr_comment.old_pos, + new_line: pr_comment.new_pos + } + + Gitlab::Diff::Position.new(params) + end + + def import_standalone_pr_comments(pr_comments, merge_request) + pr_comments.each do |comment| + begin + merge_request.notes.create!(pull_request_comment_attributes(comment)) + rescue StandardError => e + errors << { type: :pull_request, iid: comment.iid, errors: e.message } + end + end + end + + def generate_line_code(pr_comment) + Gitlab::Git.diff_line_code(pr_comment.file_path, pr_comment.new_pos, pr_comment.old_pos) + end + + def pull_request_comment_attributes(comment) + { + project: project, + note: comment.note, + author_id: gitlab_user_id(project, comment.author), + created_at: comment.created_at, + updated_at: comment.updated_at + } + end + end + end +end diff --git a/lib/gitlab/bitbucket_server_import/project_creator.rb b/lib/gitlab/bitbucket_server_import/project_creator.rb new file mode 100644 index 00000000000..8cec5152155 --- /dev/null +++ b/lib/gitlab/bitbucket_server_import/project_creator.rb @@ -0,0 +1,37 @@ +module Gitlab + module BitbucketServerImport + class ProjectCreator + attr_reader :repo, :name, :namespace, :current_user, :session_data + + def initialize(repo, name, namespace, current_user, session_data) + @repo = repo + @name = name + @namespace = namespace + @current_user = current_user + @session_data = session_data + end + + def execute + ::Projects::CreateService.new( + current_user, + name: name, + path: name, + description: repo.description, + namespace_id: namespace.id, + visibility_level: repo.visibility_level, + import_type: 'bitbucket', + import_source: repo.full_name, + import_url: repo.clone_url(session_data[:token]), + import_data: { credentials: session_data }, + skip_wiki: skip_wiki + ).execute + end + + private + + def skip_wiki + repo.has_wiki? + end + end + end +end -- cgit v1.2.3 From 046a5e398d202be5865a850cf778fedd9bd39c47 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 25 Jun 2018 22:40:11 -0700 Subject: More work towards supporting Bitbucket Server --- lib/bitbucket_server/client.rb | 4 +- lib/bitbucket_server/connection.rb | 2 +- lib/bitbucket_server/representation/issue.rb | 53 -------------- .../representation/pull_request.rb | 18 ++--- lib/bitbucket_server/representation/repo.rb | 12 +--- lib/gitlab/bitbucket_server_import/importer.rb | 81 ++-------------------- .../bitbucket_server_import/project_creator.rb | 24 +++---- 7 files changed, 31 insertions(+), 163 deletions(-) delete mode 100644 lib/bitbucket_server/representation/issue.rb (limited to 'lib') diff --git a/lib/bitbucket_server/client.rb b/lib/bitbucket_server/client.rb index 1f2f03790dd..27117fa0dcd 100644 --- a/lib/bitbucket_server/client.rb +++ b/lib/bitbucket_server/client.rb @@ -16,8 +16,8 @@ module BitbucketServer get_collection(path, :comment) end - def pull_requests(repo) - path = "/repositories/#{repo}/pullrequests?state=ALL" + def pull_requests(project_key, repository_slug) + path = "/projects/#{project}/repos/#{repo}/pull-requests?state=ALL" get_collection(path, :pull_request) end diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb index 64f12527a8d..7374b73fa60 100644 --- a/lib/bitbucket_server/connection.rb +++ b/lib/bitbucket_server/connection.rb @@ -8,7 +8,7 @@ module BitbucketServer @api_version = options.fetch(:api_version, DEFAULT_API_VERSION) @base_uri = options[:base_uri] @username = options[:username] - @token = options[:personal_access_token] + @token = options[:password] end def get(path, extra_query = {}) diff --git a/lib/bitbucket_server/representation/issue.rb b/lib/bitbucket_server/representation/issue.rb deleted file mode 100644 index 44bcbc250b3..00000000000 --- a/lib/bitbucket_server/representation/issue.rb +++ /dev/null @@ -1,53 +0,0 @@ -module Bitbucket - module Representation - class Issue < Representation::Base - CLOSED_STATUS = %w(resolved invalid duplicate wontfix closed).freeze - - def iid - raw['id'] - end - - def kind - raw['kind'] - end - - def author - raw.dig('reporter', 'username') - end - - def description - raw.fetch('content', {}).fetch('raw', nil) - end - - def state - closed? ? 'closed' : 'opened' - end - - def title - raw['title'] - end - - def milestone - raw['milestone']['name'] if raw['milestone'].present? - end - - def created_at - raw['created_on'] - end - - def updated_at - raw['edited_on'] - end - - def to_s - iid - end - - private - - def closed? - CLOSED_STATUS.include?(raw['state']) - end - end - end -end diff --git a/lib/bitbucket_server/representation/pull_request.rb b/lib/bitbucket_server/representation/pull_request.rb index 3553f3adbc7..6e248802a07 100644 --- a/lib/bitbucket_server/representation/pull_request.rb +++ b/lib/bitbucket_server/representation/pull_request.rb @@ -2,7 +2,7 @@ module BitbucketServer module Representation class PullRequest < Representation::Base def author - raw.fetch('author', {}).fetch('username', nil) + raw.fetch('author', {}).fetch('user', {}).fetch('name') end def description @@ -24,11 +24,11 @@ module BitbucketServer end def created_at - raw['created_on'] + raw['createdDate'] end def updated_at - raw['updated_on'] + raw['updatedDate'] end def title @@ -36,29 +36,29 @@ module BitbucketServer end def source_branch_name - source_branch.fetch('branch', {}).fetch('name', nil) + source_branch['id'] end def source_branch_sha - source_branch.fetch('commit', {}).fetch('hash', nil) + # XXX Not implemented? end def target_branch_name - target_branch.fetch('branch', {}).fetch('name', nil) + target_branch['id'] end def target_branch_sha - target_branch.fetch('commit', {}).fetch('hash', nil) + # XXX Not implemented? end private def source_branch - raw['source'] + raw['fromRef'] || {} end def target_branch - raw['destination'] + raw['toRef'] || {} end end end diff --git a/lib/bitbucket_server/representation/repo.rb b/lib/bitbucket_server/representation/repo.rb index f4bdb277d28..38778645966 100644 --- a/lib/bitbucket_server/representation/repo.rb +++ b/lib/bitbucket_server/representation/repo.rb @@ -15,16 +15,8 @@ module BitbucketServer raw['slug'] end - def clone_url(token = nil) - url = raw['links']['clone'].find { |link| link['name'].starts_with?('http') }.fetch('href') - - if token.present? - clone_url = URI.parse(url) - clone_url.user = "x-token-auth:#{token}" - clone_url.to_s - else - url - end + def clone_url + raw['links']['clone'].find { |link| link['name'].starts_with?('http') }.fetch('href') end def description diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index d85e2ae2ca7..5aec6dd9843 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -8,10 +8,12 @@ module Gitlab { title: 'proposal', color: '#69D100' }, { title: 'task', color: '#7F8C8D' }].freeze - attr_reader :project, :client, :errors, :users + attr_reader :project_key, :repository_slug, :client, :errors, :users def initialize(project) @project = project + @project_key = project.import_data.data['project_key'] + @repository_slug = project.import_data.data['repo_slug'] @client = BitbucketServer::Client.new(project.import_data.credentials) @formatter = Gitlab::ImportFormatter.new @labels = {} @@ -20,7 +22,6 @@ module Gitlab end def execute - import_issues import_pull_requests handle_errors @@ -49,7 +50,7 @@ module Gitlab users[username] = User.select(:id) .joins(:identities) - .find_by("identities.extern_uid = ? AND identities.provider = 'bitbucket'", username) + .find_by("identities.extern_uid = ? AND identities.provider = 'bitbucket_server'", username) .try(:id) end @@ -57,80 +58,8 @@ module Gitlab @repo ||= client.repo(project.import_source) end - def import_issues - return unless repo.issues_enabled? - - create_labels - - client.issues(repo).each do |issue| - begin - description = '' - description += @formatter.author_line(issue.author) unless find_user_id(issue.author) - description += issue.description - - label_name = issue.kind - milestone = issue.milestone ? project.milestones.find_or_create_by(title: issue.milestone) : nil - - gitlab_issue = project.issues.create!( - iid: issue.iid, - title: issue.title, - description: description, - state: issue.state, - author_id: gitlab_user_id(project, issue.author), - milestone: milestone, - created_at: issue.created_at, - updated_at: issue.updated_at - ) - - gitlab_issue.labels << @labels[label_name] - - import_issue_comments(issue, gitlab_issue) if gitlab_issue.persisted? - rescue StandardError => e - errors << { type: :issue, iid: issue.iid, errors: e.message } - end - end - end - - def import_issue_comments(issue, gitlab_issue) - client.issue_comments(repo, issue.iid).each do |comment| - # The note can be blank for issue service messages like "Changed title: ..." - # We would like to import those comments as well but there is no any - # specific parameter that would allow to process them, it's just an empty comment. - # To prevent our importer from just crashing or from creating useless empty comments - # we do this check. - next unless comment.note.present? - - note = '' - note += @formatter.author_line(comment.author) unless find_user_id(comment.author) - note += comment.note - - begin - gitlab_issue.notes.create!( - project: project, - note: note, - author_id: gitlab_user_id(project, comment.author), - created_at: comment.created_at, - updated_at: comment.updated_at - ) - rescue StandardError => e - errors << { type: :issue_comment, iid: issue.iid, errors: e.message } - end - end - end - - def create_labels - LABELS.each do |label_params| - label = ::Labels::CreateService.new(label_params).execute(project: project) - if label.valid? - @labels[label_params[:title]] = label - else - raise "Failed to create label \"#{label_params[:title]}\" for project \"#{project.full_name}\"" - end - end - end - def import_pull_requests - pull_requests = client.pull_requests(repo) + pull_requests = client.pull_requests(project_key, repository_slug) pull_requests.each do |pull_request| begin diff --git a/lib/gitlab/bitbucket_server_import/project_creator.rb b/lib/gitlab/bitbucket_server_import/project_creator.rb index 8cec5152155..3dc4bd80f32 100644 --- a/lib/gitlab/bitbucket_server_import/project_creator.rb +++ b/lib/gitlab/bitbucket_server_import/project_creator.rb @@ -1,9 +1,11 @@ module Gitlab module BitbucketServerImport class ProjectCreator - attr_reader :repo, :name, :namespace, :current_user, :session_data + attr_reader :project_key, :repo_slug, :repo, :name, :namespace, :current_user, :session_data - def initialize(repo, name, namespace, current_user, session_data) + def initialize(project_key, repo_slug, repo, name, namespace, current_user, session_data) + @project_key = project_key + @repo_slug = repo_slug @repo = repo @name = name @namespace = namespace @@ -19,19 +21,17 @@ module Gitlab description: repo.description, namespace_id: namespace.id, visibility_level: repo.visibility_level, - import_type: 'bitbucket', + import_type: 'bitbucket_server', import_source: repo.full_name, - import_url: repo.clone_url(session_data[:token]), - import_data: { credentials: session_data }, - skip_wiki: skip_wiki + import_url: repo.clone_url, + import_data: { + credentials: session_data, + data: { project_key: project_key, + repo_slug: repo_slug }, + }, + skip_wiki: true ).execute end - - private - - def skip_wiki - repo.has_wiki? - end end end end -- cgit v1.2.3 From 14d0bd5913127dbd1c1fffa56bd0e7f78ab2d906 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 25 Jun 2018 23:12:28 -0700 Subject: Add source/target branch SHA --- lib/bitbucket_server/representation/pull_request.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/representation/pull_request.rb b/lib/bitbucket_server/representation/pull_request.rb index 6e248802a07..7c03e9a401a 100644 --- a/lib/bitbucket_server/representation/pull_request.rb +++ b/lib/bitbucket_server/representation/pull_request.rb @@ -40,7 +40,7 @@ module BitbucketServer end def source_branch_sha - # XXX Not implemented? + source_branch['latestCommit'] end def target_branch_name @@ -48,7 +48,7 @@ module BitbucketServer end def target_branch_sha - # XXX Not implemented? + target_branch['latestCommit'] end private -- cgit v1.2.3 From 7b94e805c70a57c53e669f714b2ea565a2d82499 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 25 Jun 2018 23:13:32 -0700 Subject: Fix pull request URLs --- lib/bitbucket_server/client.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/client.rb b/lib/bitbucket_server/client.rb index 27117fa0dcd..9f6958b568e 100644 --- a/lib/bitbucket_server/client.rb +++ b/lib/bitbucket_server/client.rb @@ -16,18 +16,18 @@ module BitbucketServer get_collection(path, :comment) end - def pull_requests(project_key, repository_slug) - path = "/projects/#{project}/repos/#{repo}/pull-requests?state=ALL" + def pull_requests(project_key, repo) + path = "/projects/#{project_key}/repos/#{repo}/pull-requests?state=ALL" get_collection(path, :pull_request) end - def pull_request_comments(repo, pull_request) - path = "/repositories/#{repo}/pullrequests/#{pull_request}/comments" + def pull_request_comments(project_key, repo, pull_request) + path = "/projects/#{project_key}/repos/#{repo}/pull-requests/#{pull_request}/comments" get_collection(path, :pull_request_comment) end - def pull_request_diff(repo, pull_request) - path = "/repositories/#{repo}/pullrequests/#{pull_request}/diff" + def pull_request_diff(project_key, repo, pull_request) + path = "/projects/#{project_key}/repos/#{repo}/pull-requests/#{pull_request}/diff" connection.get(path) end -- cgit v1.2.3 From d7c79839d3e993c8166907fbe277752115897fee Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 25 Jun 2018 23:32:28 -0700 Subject: wip --- lib/gitlab/bitbucket_server_import/importer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index 5aec6dd9843..dec393f053f 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -89,7 +89,7 @@ module Gitlab updated_at: pull_request.updated_at ) - import_pull_request_comments(pull_request, merge_request) if merge_request.persisted? +# import_pull_request_comments(pull_request, merge_request) if merge_request.persisted? rescue StandardError => e errors << { type: :pull_request, iid: pull_request.iid, errors: e.message, trace: e.backtrace.join("\n"), raw_response: pull_request.raw } end -- cgit v1.2.3 From 615e1807998b986582f3baf08aa3f1693294c52a Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 26 Jun 2018 15:59:34 -0700 Subject: More work towards importing pull requests --- lib/bitbucket_server/client.rb | 7 ++++--- lib/gitlab/bitbucket_server_import/importer.rb | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/client.rb b/lib/bitbucket_server/client.rb index 9f6958b568e..eb8b592e833 100644 --- a/lib/bitbucket_server/client.rb +++ b/lib/bitbucket_server/client.rb @@ -21,9 +21,10 @@ module BitbucketServer get_collection(path, :pull_request) end - def pull_request_comments(project_key, repo, pull_request) - path = "/projects/#{project_key}/repos/#{repo}/pull-requests/#{pull_request}/comments" - get_collection(path, :pull_request_comment) + def activities(project_key, repo, pull_request) + path = "/projects/#{project_key}/repos/#{repo}/pull-requests/#{pull_request}/activities" + + collection = get_collection(path, :activities) end def pull_request_diff(project_key, repo, pull_request) diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index dec393f053f..8cb0c7d1b1d 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -97,9 +97,9 @@ module Gitlab end def import_pull_request_comments(pull_request, merge_request) - comments = client.pull_request_comments(repo, pull_request.iid) + comments = client.activities(repo, pull_request.iid).select(&:commment?) - inline_comments, pr_comments = comments.partition(&:inline?) + inline_comments, pr_comments = comments.partition(&:inline_comment?) import_inline_comments(inline_comments, pull_request, merge_request) import_standalone_pr_comments(pr_comments, merge_request) -- cgit v1.2.3 From 5728ffbf12c8f5f767bc6d6b9a453b2add974e96 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 27 Jun 2018 14:25:09 -0700 Subject: Import standalone pull request comments --- lib/bitbucket_server/client.rb | 2 +- lib/bitbucket_server/representation/activity.rb | 84 ++++++++++++++++++++++ .../representation/pull_request.rb | 4 ++ lib/gitlab/bitbucket_server_import/importer.rb | 54 +++++++++----- 4 files changed, 125 insertions(+), 19 deletions(-) create mode 100644 lib/bitbucket_server/representation/activity.rb (limited to 'lib') diff --git a/lib/bitbucket_server/client.rb b/lib/bitbucket_server/client.rb index eb8b592e833..c3cb1608187 100644 --- a/lib/bitbucket_server/client.rb +++ b/lib/bitbucket_server/client.rb @@ -24,7 +24,7 @@ module BitbucketServer def activities(project_key, repo, pull_request) path = "/projects/#{project_key}/repos/#{repo}/pull-requests/#{pull_request}/activities" - collection = get_collection(path, :activities) + collection = get_collection(path, :activity) end def pull_request_diff(project_key, repo, pull_request) diff --git a/lib/bitbucket_server/representation/activity.rb b/lib/bitbucket_server/representation/activity.rb new file mode 100644 index 00000000000..207bc848b28 --- /dev/null +++ b/lib/bitbucket_server/representation/activity.rb @@ -0,0 +1,84 @@ +module BitbucketServer + module Representation + class Activity < Representation::Base + def action + raw['action'] + end + + def comment? + action == 'COMMENTED'.freeze + end + + def inline_comment? + comment? && raw['commentAnchor'] + end + + def id + raw['id'] + end + + def note + comment['text'] + end + + def author_username + author['name'] + end + + def author_email + author['emailAddress'] + end + + def merge_event? + action == 'MERGED' + end + + def commiter_user + commit.fetch('committer', {})['displayName'] + end + + def commiter_email + commit.fetch('committer', {})['emailAddress'] + end + + def merge_timestamp + timestamp = commit.fetch('committer', {})['commiterTimestamp'] + + Time.at(timestamp / 1000.0) if timestamp.is_a?(Integer) + end + + def commit + raw.fetch('commit', {}) + end + + def created_at + Time.at(created_date / 1000) if created_date.is_a?(Integer) + end + + def updated_at + Time.at(updated_date / 1000) if created_date.is_a?(Integer) + end + + private + + def comment + raw.fetch('comment', {}) + end + + def author + comment.fetch('author', {}) + end + + # Anchor hash: + # {u'toHash': u'a4c2164330f2549f67c13f36a93884cf66e976be', u'fromHash': u'c5f4288162e2e6218180779c7f6ac1735bb56eab', u'fileType': u'FROM', u'diffType': u'EFFECTIVE', u'lineType': u'CONTEXT', u'path': u'CHANGELOG.md', u'line': 3, u'orphaned': False} + + def created_date + comment['createdDate'] + end + + def updated_date + comment['updatedDate'] + end + end + end +end diff --git a/lib/bitbucket_server/representation/pull_request.rb b/lib/bitbucket_server/representation/pull_request.rb index 7c03e9a401a..96a27564641 100644 --- a/lib/bitbucket_server/representation/pull_request.rb +++ b/lib/bitbucket_server/representation/pull_request.rb @@ -5,6 +5,10 @@ module BitbucketServer raw.fetch('author', {}).fetch('user', {}).fetch('name') end + def author_email + raw.fetch('author', {}).fetch('user', {}).fetch('emailAddress') + end + def description raw['description'] end diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index 8cb0c7d1b1d..43a52a61ee5 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -8,7 +8,7 @@ module Gitlab { title: 'proposal', color: '#69D100' }, { title: 'task', color: '#7F8C8D' }].freeze - attr_reader :project_key, :repository_slug, :client, :errors, :users + attr_reader :project, :project_key, :repository_slug, :client, :errors, :users def initialize(project) @project = project @@ -39,23 +39,20 @@ module Gitlab }.to_json) end - def gitlab_user_id(project, username) - find_user_id(username) || project.creator_id + def gitlab_user_id(project, email) + find_user_id(email) || project.creator_id end - def find_user_id(username) - return nil unless username + def find_user_id(email) + return nil unless email - return users[username] if users.key?(username) + return users[email] if users.key?(email) - users[username] = User.select(:id) - .joins(:identities) - .find_by("identities.extern_uid = ? AND identities.provider = 'bitbucket_server'", username) - .try(:id) + users[email] = User.find_by_any_email(email) end def repo - @repo ||= client.repo(project.import_source) + @repo ||= client.repo(project_key, repository_slug) end def import_pull_requests @@ -64,13 +61,14 @@ module Gitlab pull_requests.each do |pull_request| begin description = '' - description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author) + description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author_email) description += pull_request.description source_branch_sha = pull_request.source_branch_sha target_branch_sha = pull_request.target_branch_sha source_branch_sha = project.repository.commit(source_branch_sha)&.sha || source_branch_sha target_branch_sha = project.repository.commit(target_branch_sha)&.sha || target_branch_sha + project.merge_requests.find_by(iid: pull_request.iid)&.destroy merge_request = project.merge_requests.create!( iid: pull_request.iid, @@ -83,13 +81,13 @@ module Gitlab target_branch: pull_request.target_branch_name, target_branch_sha: target_branch_sha, state: pull_request.state, - author_id: gitlab_user_id(project, pull_request.author), + author_id: gitlab_user_id(project, pull_request.author_email), assignee_id: nil, created_at: pull_request.created_at, updated_at: pull_request.updated_at ) -# import_pull_request_comments(pull_request, merge_request) if merge_request.persisted? + import_pull_request_comments(pull_request, merge_request) if merge_request.persisted? rescue StandardError => e errors << { type: :pull_request, iid: pull_request.iid, errors: e.message, trace: e.backtrace.join("\n"), raw_response: pull_request.raw } end @@ -97,14 +95,33 @@ module Gitlab end def import_pull_request_comments(pull_request, merge_request) - comments = client.activities(repo, pull_request.iid).select(&:commment?) + # XXX This is inefficient since we are making multiple requests to the activities endpoint + merge_event = client.activities(project_key, repository_slug, pull_request.iid).find(&:merge_event?) + + import_merge_event(merge_request, merge_event) if merge_event + + comments = client.activities(project_key, repository_slug, pull_request.iid).select(&:comment?) inline_comments, pr_comments = comments.partition(&:inline_comment?) - import_inline_comments(inline_comments, pull_request, merge_request) +# import_inline_comments(inline_comments, pull_request, merge_request) import_standalone_pr_comments(pr_comments, merge_request) end + def import_merge_event(merge_request, merge_event) + committer = merge_event.commiter_email + + return unless committer + + user_id = find_user_id(committer) if committer + timestamp = merge_event.merge_timestamp + + return unless user_id + + event = Event.create(merged_by_id: user_id, merged_at: timestamp) + MergeRequestMetricsService.new(merge_request.metrics).merge(event) + end + def import_inline_comments(inline_comments, pull_request, merge_request) line_code_map = {} @@ -153,7 +170,7 @@ module Gitlab begin merge_request.notes.create!(pull_request_comment_attributes(comment)) rescue StandardError => e - errors << { type: :pull_request, iid: comment.iid, errors: e.message } + errors << { type: :pull_request, iid: comment.id, errors: e.message } end end end @@ -163,10 +180,11 @@ module Gitlab end def pull_request_comment_attributes(comment) + byebug { project: project, note: comment.note, - author_id: gitlab_user_id(project, comment.author), + author_id: gitlab_user_id(project, comment.author_email), created_at: comment.created_at, updated_at: comment.updated_at } -- cgit v1.2.3 From 014abc9c07c1389e64ccf336559b488835730a45 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 28 Jun 2018 00:27:04 -0700 Subject: Handle threaded comments and prepare for inline comments --- lib/bitbucket_server/representation/activity.rb | 30 ++++------ lib/bitbucket_server/representation/comment.rb | 69 +++++++++++++++++++--- .../representation/pull_request_comment.rb | 55 +++++++++++------ lib/gitlab/bitbucket_server_import/importer.rb | 9 ++- 4 files changed, 118 insertions(+), 45 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/representation/activity.rb b/lib/bitbucket_server/representation/activity.rb index 207bc848b28..aa168aca8af 100644 --- a/lib/bitbucket_server/representation/activity.rb +++ b/lib/bitbucket_server/representation/activity.rb @@ -13,31 +13,27 @@ module BitbucketServer comment? && raw['commentAnchor'] end - def id - raw['id'] - end - - def note - comment['text'] - end - - def author_username - author['name'] - end + def comment + return unless comment? - def author_email - author['emailAddress'] + @comment ||= + if inline_comment? + PullRequestComment.new(raw_comment) + else + Comment.new(raw_comment) + end end + # XXX Move this into MergeEvent def merge_event? action == 'MERGED' end - def commiter_user + def committer_user commit.fetch('committer', {})['displayName'] end - def commiter_email + def committer_email commit.fetch('committer', {})['emailAddress'] end @@ -61,12 +57,12 @@ module BitbucketServer private - def comment + def raw_comment raw.fetch('comment', {}) end def author - comment.fetch('author', {}) + raw_comment.fetch('author', {}) end # Anchor hash: diff --git a/lib/bitbucket_server/representation/comment.rb b/lib/bitbucket_server/representation/comment.rb index 4937aa9728f..bd6f9f08e3c 100644 --- a/lib/bitbucket_server/representation/comment.rb +++ b/lib/bitbucket_server/representation/comment.rb @@ -1,26 +1,79 @@ -module Bitbucket +module BitbucketServer module Representation + # A general comment with the structure: + # "comment": { + # "author": { + # "active": true, + # "displayName": "root", + # "emailAddress": "stanhu+bitbucket@gitlab.com", + # "id": 1, + # "links": { + # "self": [ + # { + # "href": "http://localhost:7990/users/root" + # } + # ] + # }, + # "name": "root", + # "slug": "root", + # "type": "NORMAL" + # } + # } + # } class Comment < Representation::Base - def author - user['username'] + def id + raw['id'] + end + + def author_username + author['username'] + end + + def author_email + author['displayName'] end def note - raw.fetch('content', {}).fetch('raw', nil) + raw['text'] end def created_at - raw['created_on'] + Time.at(created_date / 1000) if created_date.is_a?(Integer) end def updated_at - raw['updated_on'] || raw['created_on'] + Time.at(updated_date / 1000) if created_date.is_a?(Integer) + end + + def comments + workset = [raw['comments']].compact + all_comments = [] + + until workset.empty? + comments = workset.pop + + comments.each do |comment| + new_comments = comment.delete('comments') + workset << new_comments if new_comments + all_comments << Comment.new(comment) + end + end + + all_comments end private - def user - raw.fetch('user', {}) + def author + raw.fetch('author', {}) + end + + def created_date + raw['createdDate'] + end + + def updated_date + raw['updatedDate'] end end end diff --git a/lib/bitbucket_server/representation/pull_request_comment.rb b/lib/bitbucket_server/representation/pull_request_comment.rb index c52acbc3ddc..a04c120b0b0 100644 --- a/lib/bitbucket_server/representation/pull_request_comment.rb +++ b/lib/bitbucket_server/representation/pull_request_comment.rb @@ -1,38 +1,59 @@ -module Bitbucket +module BitbucketServer module Representation + # An inline comment with the following structure that identifies + # the part of the diff: + # + # "commentAnchor": { + # "diffType": "EFFECTIVE", + # "fileType": "TO", + # "fromHash": "c5f4288162e2e6218180779c7f6ac1735bb56eab", + # "line": 1, + # "lineType": "ADDED", + # "orphaned": false, + # "path": "CHANGELOG.md", + # "toHash": "a4c2164330f2549f67c13f36a93884cf66e976be" + # } class PullRequestComment < Comment - def iid - raw['id'] + def file_type + comment_anchor['fileType'] end - def file_path - inline.fetch('path') + def from_sha + comment_anchor['fromHash'] end - def old_pos - inline.fetch('from') + def to_sha + comment_anchor['toHash'] end - def new_pos - inline.fetch('to') + def to? + file_type == 'TO' + end + + def from? + file_type == 'FROM' end - def parent_id - raw.fetch('parent', {}).fetch('id', nil) + def new_pos + return unless to? + + comment_anchor['line'] end - def inline? - raw.key?('inline') + def old_pos + return unless from? + + comment_anchor['line'] end - def has_parent? - raw.key?('parent') + def file_path + comment_anchor.fetch('path') end private - def inline - raw.fetch('inline', {}) + def comment_anchor + raw.fetch('commentAnchor', {}) end end end diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index 43a52a61ee5..f8871ade6e7 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -105,11 +105,11 @@ module Gitlab inline_comments, pr_comments = comments.partition(&:inline_comment?) # import_inline_comments(inline_comments, pull_request, merge_request) - import_standalone_pr_comments(pr_comments, merge_request) + import_standalone_pr_comments(pr_comments.map(&:comment), merge_request) end def import_merge_event(merge_request, merge_event) - committer = merge_event.commiter_email + committer = merge_event.committer_email return unless committer @@ -169,6 +169,10 @@ module Gitlab pr_comments.each do |comment| begin merge_request.notes.create!(pull_request_comment_attributes(comment)) + + comment.comments.each do |replies| + merge_request.notes.create!(pull_request_comment_attributes(replies)) + end rescue StandardError => e errors << { type: :pull_request, iid: comment.id, errors: e.message } end @@ -180,7 +184,6 @@ module Gitlab end def pull_request_comment_attributes(comment) - byebug { project: project, note: comment.note, -- cgit v1.2.3 From c4dbe61a846ddc97f8161e14771a58f406f65c81 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 28 Jun 2018 00:50:10 -0700 Subject: First iteration of importing diff notes --- lib/bitbucket_server/representation/activity.rb | 13 +++++++------ lib/bitbucket_server/representation/comment.rb | 16 ++++++++++------ lib/gitlab/bitbucket_server_import/importer.rb | 22 +++++++++------------- 3 files changed, 26 insertions(+), 25 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/representation/activity.rb b/lib/bitbucket_server/representation/activity.rb index aa168aca8af..7c552c7f428 100644 --- a/lib/bitbucket_server/representation/activity.rb +++ b/lib/bitbucket_server/representation/activity.rb @@ -10,7 +10,7 @@ module BitbucketServer end def inline_comment? - comment? && raw['commentAnchor'] + comment? && comment_anchor end def comment @@ -18,9 +18,9 @@ module BitbucketServer @comment ||= if inline_comment? - PullRequestComment.new(raw_comment) + PullRequestComment.new(raw) else - Comment.new(raw_comment) + Comment.new(raw) end end @@ -61,13 +61,14 @@ module BitbucketServer raw.fetch('comment', {}) end + def comment_anchor + raw['commentAnchor'] + end + def author raw_comment.fetch('author', {}) end - # Anchor hash: - # {u'toHash': u'a4c2164330f2549f67c13f36a93884cf66e976be', u'fromHash': u'c5f4288162e2e6218180779c7f6ac1735bb56eab', u'fileType': u'FROM', u'diffType': u'EFFECTIVE', u'lineType': u'CONTEXT', u'path': u'CHANGELOG.md', u'line': 3, u'orphaned': False} - def created_date comment['createdDate'] end diff --git a/lib/bitbucket_server/representation/comment.rb b/lib/bitbucket_server/representation/comment.rb index bd6f9f08e3c..990905ac5a4 100644 --- a/lib/bitbucket_server/representation/comment.rb +++ b/lib/bitbucket_server/representation/comment.rb @@ -22,7 +22,7 @@ module BitbucketServer # } class Comment < Representation::Base def id - raw['id'] + raw_comment['id'] end def author_username @@ -34,7 +34,7 @@ module BitbucketServer end def note - raw['text'] + raw_comment['text'] end def created_at @@ -46,7 +46,7 @@ module BitbucketServer end def comments - workset = [raw['comments']].compact + workset = [raw_comment['comments']].compact all_comments = [] until workset.empty? @@ -64,16 +64,20 @@ module BitbucketServer private + def raw_comment + raw.fetch('comment', {}) + end + def author - raw.fetch('author', {}) + raw_comment.fetch('author', {}) end def created_date - raw['createdDate'] + raw_comment['createdDate'] end def updated_date - raw['updatedDate'] + raw_comment['updatedDate'] end end end diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index f8871ade6e7..fcf75dc493b 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -104,7 +104,7 @@ module Gitlab inline_comments, pr_comments = comments.partition(&:inline_comment?) -# import_inline_comments(inline_comments, pull_request, merge_request) + import_inline_comments(inline_comments.map(&:comment), pull_request, merge_request) import_standalone_pr_comments(pr_comments.map(&:comment), merge_request) end @@ -125,17 +125,13 @@ module Gitlab def import_inline_comments(inline_comments, pull_request, merge_request) line_code_map = {} - children, parents = inline_comments.partition(&:has_parent?) - - # The BitbucketServer API returns threaded replies as parent-child - # relationships. We assume that the child can appear in any order in - # the JSON. - parents.each do |comment| - line_code_map[comment.iid] = generate_line_code(comment) - end + inline_comments.each do |comment| + line_code = generate_line_code(comment) + line_code_map[comment.id] = line_code - children.each do |comment| - line_code_map[comment.iid] = line_code_map.fetch(comment.parent_id, nil) + comment.comments.each do |reply| + line_code_map[reply.id] = line_code + end end inline_comments.each do |comment| @@ -143,12 +139,12 @@ module Gitlab attributes = pull_request_comment_attributes(comment) attributes.merge!( position: build_position(merge_request, comment), - line_code: line_code_map.fetch(comment.iid), + line_code: line_code_map.fetch(comment.id), type: 'DiffNote') merge_request.notes.create!(attributes) rescue StandardError => e - errors << { type: :pull_request, iid: comment.iid, errors: e.message } + errors << { type: :pull_request, id: comment.id, errors: e.message } end end end -- cgit v1.2.3 From ea727862df2861c0dd8ac874b40fbd96009e8ea9 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 28 Jun 2018 01:21:28 -0700 Subject: Fix line code map omitting replies --- lib/bitbucket_server/representation/comment.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/bitbucket_server/representation/comment.rb b/lib/bitbucket_server/representation/comment.rb index 990905ac5a4..07a8b359db7 100644 --- a/lib/bitbucket_server/representation/comment.rb +++ b/lib/bitbucket_server/representation/comment.rb @@ -55,7 +55,7 @@ module BitbucketServer comments.each do |comment| new_comments = comment.delete('comments') workset << new_comments if new_comments - all_comments << Comment.new(comment) + all_comments << Comment.new({ 'comment' => comment}) end end -- cgit v1.2.3 From 5817c67014576fab899fac3d8478f3620af4dda2 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 28 Jun 2018 01:49:35 -0700 Subject: Import threaded discussions in diffs --- lib/gitlab/bitbucket_server_import/importer.rb | 27 ++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index fcf75dc493b..33bd1aeb127 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -135,20 +135,35 @@ module Gitlab end inline_comments.each do |comment| - begin - attributes = pull_request_comment_attributes(comment) + parent = build_diff_note(merge_request, comment, line_code_map) + + next unless parent&.persisted? + + comment.comments.each do |reply| + attributes = pull_request_comment_attributes(reply) attributes.merge!( position: build_position(merge_request, comment), - line_code: line_code_map.fetch(comment.id), + line_code: line_code_map.fetch(reply.id) + discussion_id: parent.discussion_id, type: 'DiffNote') - merge_request.notes.create!(attributes) - rescue StandardError => e - errors << { type: :pull_request, id: comment.id, errors: e.message } end end end + def build_diff_note(merge_request, comment, line_code_map) + attributes = pull_request_comment_attributes(comment) + attributes.merge!( + position: build_position(merge_request, comment), + line_code: line_code_map.fetch(comment.id), + type: 'DiffNote') + + merge_request.notes.create!(attributes) + rescue StandardError => e + errors << { type: :pull_request, id: comment.id, errors: e.message } + nil + end + def build_position(merge_request, pr_comment) params = { diff_refs: merge_request.diff_refs, -- cgit v1.2.3 From ee5f8f2830f7bdfcbd36b2368dd15b109bf29c69 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 28 Jun 2018 13:48:25 -0700 Subject: Fix generation of diff line positions --- .../representation/pull_request_comment.rb | 45 ++++++++++++++++++++-- lib/gitlab/bitbucket_server_import/importer.rb | 2 +- 2 files changed, 42 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/representation/pull_request_comment.rb b/lib/bitbucket_server/representation/pull_request_comment.rb index a04c120b0b0..3763ba13816 100644 --- a/lib/bitbucket_server/representation/pull_request_comment.rb +++ b/lib/bitbucket_server/representation/pull_request_comment.rb @@ -34,16 +34,26 @@ module BitbucketServer file_type == 'FROM' end + def added? + line_type == 'ADDED' + end + + def removed? + line_type == 'REMOVED' + end + def new_pos - return unless to? + return if removed? + return unless line_position - comment_anchor['line'] + line_position[1] end def old_pos - return unless from? + return if added? + return unless line_position - comment_anchor['line'] + line_position[0] end def file_path @@ -52,9 +62,36 @@ module BitbucketServer private + def line_type + comment_anchor['lineType'] + end + + def line_position + @line_position ||= + diff_hunks.each do |hunk| + segments = hunk.fetch('segments', []) + segments.each do |segment| + lines = segment.fetch('lines', []) + lines.each do |line| + if line['commentIds']&.include?(id) + return [line['source'], line['destination']] + end + end + end + end + end + def comment_anchor raw.fetch('commentAnchor', {}) end + + def diff + raw.fetch('diff', {}) + end + + def diff_hunks + diff.fetch('hunks', []) + end end end end diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index 33bd1aeb127..f035cc58acf 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -143,7 +143,7 @@ module Gitlab attributes = pull_request_comment_attributes(reply) attributes.merge!( position: build_position(merge_request, comment), - line_code: line_code_map.fetch(reply.id) + line_code: line_code_map.fetch(reply.id), discussion_id: parent.discussion_id, type: 'DiffNote') merge_request.notes.create!(attributes) -- cgit v1.2.3 From 7b67350a0c7ed11343420b4d01258c09ce2fa49c Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 28 Jun 2018 15:18:53 -0700 Subject: Remove unnecessary code and fix branch names --- lib/gitlab/bitbucket_server_import/importer.rb | 46 +++++++++----------------- 1 file changed, 15 insertions(+), 31 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index f035cc58acf..5f6230438bc 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -2,12 +2,6 @@ module Gitlab module BitbucketServerImport class Importer include Gitlab::ShellAdapter - - LABELS = [{ title: 'bug', color: '#FF0000' }, - { title: 'enhancement', color: '#428BCA' }, - { title: 'proposal', color: '#69D100' }, - { title: 'task', color: '#7F8C8D' }].freeze - attr_reader :project, :project_key, :repository_slug, :client, :errors, :users def initialize(project) @@ -16,7 +10,6 @@ module Gitlab @repository_slug = project.import_data.data['repo_slug'] @client = BitbucketServer::Client.new(project.import_data.credentials) @formatter = Gitlab::ImportFormatter.new - @labels = {} @errors = [] @users = {} end @@ -75,10 +68,10 @@ module Gitlab title: pull_request.title, description: description, source_project: project, - source_branch: pull_request.source_branch_name, - source_branch_sha: source_branch_sha, + source_branch: Gitlab::Git.ref_name(pull_request.source_branch_name), + source_branch_sha: pull_request.source_branch_sha, target_project: project, - target_branch: pull_request.target_branch_name, + target_branch: Gitlab::Git.ref_name(pull_request.target_branch_name), target_branch_sha: target_branch_sha, state: pull_request.state, author_id: gitlab_user_id(project, pull_request.author_email), @@ -123,39 +116,30 @@ module Gitlab end def import_inline_comments(inline_comments, pull_request, merge_request) - line_code_map = {} - - inline_comments.each do |comment| - line_code = generate_line_code(comment) - line_code_map[comment.id] = line_code - - comment.comments.each do |reply| - line_code_map[reply.id] = line_code - end - end - inline_comments.each do |comment| - parent = build_diff_note(merge_request, comment, line_code_map) + parent = build_diff_note(merge_request, comment) next unless parent&.persisted? comment.comments.each do |reply| - attributes = pull_request_comment_attributes(reply) - attributes.merge!( - position: build_position(merge_request, comment), - line_code: line_code_map.fetch(reply.id), - discussion_id: parent.discussion_id, - type: 'DiffNote') - merge_request.notes.create!(attributes) + begin + attributes = pull_request_comment_attributes(reply) + attributes.merge!( + position: build_position(merge_request, comment), + discussion_id: parent.discussion_id, + type: 'DiffNote') + merge_request.notes.create!(attributes) + rescue StandardError => e + errors << { type: :pull_request, id: comment.id, errors: e.message } + end end end end - def build_diff_note(merge_request, comment, line_code_map) + def build_diff_note(merge_request, comment) attributes = pull_request_comment_attributes(comment) attributes.merge!( position: build_position(merge_request, comment), - line_code: line_code_map.fetch(comment.id), type: 'DiffNote') merge_request.notes.create!(attributes) -- cgit v1.2.3 From d987546e81e0ad4319a8c4f901cbd876d1cf4a87 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 28 Jun 2018 15:37:06 -0700 Subject: Add missing file --- lib/bitbucket_server/error/unauthorized.rb | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 lib/bitbucket_server/error/unauthorized.rb (limited to 'lib') diff --git a/lib/bitbucket_server/error/unauthorized.rb b/lib/bitbucket_server/error/unauthorized.rb new file mode 100644 index 00000000000..96ed469e913 --- /dev/null +++ b/lib/bitbucket_server/error/unauthorized.rb @@ -0,0 +1,5 @@ +module BitbucketServer + module Error + Unauthorized = Class.new(StandardError) + end +end -- cgit v1.2.3 From bba5975a728002f7db8ca1fabfac8d3d719b6053 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 28 Jun 2018 22:18:42 -0700 Subject: Prune unused code --- lib/bitbucket_server/representation/user.rb | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 lib/bitbucket_server/representation/user.rb (limited to 'lib') diff --git a/lib/bitbucket_server/representation/user.rb b/lib/bitbucket_server/representation/user.rb deleted file mode 100644 index 174f3a55f2c..00000000000 --- a/lib/bitbucket_server/representation/user.rb +++ /dev/null @@ -1,9 +0,0 @@ -module BitbucketServer - module Representation - class User < Representation::Base - def username - raw['username'] - end - end - end -end -- cgit v1.2.3 From a4cbbfe465d54422613171bfd38ffd64d42c8249 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 28 Jun 2018 22:26:33 -0700 Subject: Fix Rubocop complaints --- lib/bitbucket_server/client.rb | 25 +--------------------- lib/bitbucket_server/representation/comment.rb | 2 +- .../representation/pull_request_comment.rb | 19 ++++++++-------- lib/bitbucket_server/representation/repo.rb | 2 -- lib/gitlab/bitbucket_server_import/importer.rb | 2 +- .../bitbucket_server_import/project_creator.rb | 2 +- 6 files changed, 13 insertions(+), 39 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/client.rb b/lib/bitbucket_server/client.rb index c3cb1608187..e632adea68c 100644 --- a/lib/bitbucket_server/client.rb +++ b/lib/bitbucket_server/client.rb @@ -6,16 +6,6 @@ module BitbucketServer @connection = Connection.new(options) end - def issues(repo) - path = "/repositories/#{repo}/issues" - get_collection(path, :issue) - end - - def issue_comments(repo, issue_id) - path = "/repositories/#{repo}/issues/#{issue_id}/comments" - get_collection(path, :comment) - end - def pull_requests(project_key, repo) path = "/projects/#{project_key}/repos/#{repo}/pull-requests?state=ALL" get_collection(path, :pull_request) @@ -23,13 +13,7 @@ module BitbucketServer def activities(project_key, repo, pull_request) path = "/projects/#{project_key}/repos/#{repo}/pull-requests/#{pull_request}/activities" - - collection = get_collection(path, :activity) - end - - def pull_request_diff(project_key, repo, pull_request) - path = "/projects/#{project_key}/repos/#{repo}/pull-requests/#{pull_request}/diff" - connection.get(path) + get_collection(path, :activity) end def repo(project, repo_name) @@ -43,13 +27,6 @@ module BitbucketServer get_collection(path, :repo) end - def user - @user ||= begin - parsed_response = connection.get('/user') - BitbucketServer::Representation::User.new(parsed_response) - end - end - private def get_collection(path, type) diff --git a/lib/bitbucket_server/representation/comment.rb b/lib/bitbucket_server/representation/comment.rb index 07a8b359db7..44f76139fc4 100644 --- a/lib/bitbucket_server/representation/comment.rb +++ b/lib/bitbucket_server/representation/comment.rb @@ -55,7 +55,7 @@ module BitbucketServer comments.each do |comment| new_comments = comment.delete('comments') workset << new_comments if new_comments - all_comments << Comment.new({ 'comment' => comment}) + all_comments << Comment.new({ 'comment' => comment }) end end diff --git a/lib/bitbucket_server/representation/pull_request_comment.rb b/lib/bitbucket_server/representation/pull_request_comment.rb index 3763ba13816..c7d08e604fd 100644 --- a/lib/bitbucket_server/representation/pull_request_comment.rb +++ b/lib/bitbucket_server/representation/pull_request_comment.rb @@ -35,7 +35,7 @@ module BitbucketServer end def added? - line_type == 'ADDED' + line_type == 'ADDED' end def removed? @@ -67,17 +67,16 @@ module BitbucketServer end def line_position - @line_position ||= - diff_hunks.each do |hunk| - segments = hunk.fetch('segments', []) - segments.each do |segment| - lines = segment.fetch('lines', []) - lines.each do |line| - if line['commentIds']&.include?(id) - return [line['source'], line['destination']] - end + @line_position ||= diff_hunks.each do |hunk| + segments = hunk.fetch('segments', []) + segments.each do |segment| + lines = segment.fetch('lines', []) + lines.each do |line| + if line['commentIds']&.include?(id) + return [line['source'], line['destination']] end end + end end end diff --git a/lib/bitbucket_server/representation/repo.rb b/lib/bitbucket_server/representation/repo.rb index 38778645966..2f3a60b204b 100644 --- a/lib/bitbucket_server/representation/repo.rb +++ b/lib/bitbucket_server/representation/repo.rb @@ -1,8 +1,6 @@ module BitbucketServer module Representation class Repo < Representation::Base - attr_reader :owner, :slug - def initialize(raw) super(raw) end diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index 5f6230438bc..9bc6de2ea5d 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -69,7 +69,7 @@ module Gitlab description: description, source_project: project, source_branch: Gitlab::Git.ref_name(pull_request.source_branch_name), - source_branch_sha: pull_request.source_branch_sha, + source_branch_sha: source_branch_sha, target_project: project, target_branch: Gitlab::Git.ref_name(pull_request.target_branch_name), target_branch_sha: target_branch_sha, diff --git a/lib/gitlab/bitbucket_server_import/project_creator.rb b/lib/gitlab/bitbucket_server_import/project_creator.rb index 3dc4bd80f32..031ae29120d 100644 --- a/lib/gitlab/bitbucket_server_import/project_creator.rb +++ b/lib/gitlab/bitbucket_server_import/project_creator.rb @@ -27,7 +27,7 @@ module Gitlab import_data: { credentials: session_data, data: { project_key: project_key, - repo_slug: repo_slug }, + repo_slug: repo_slug } }, skip_wiki: true ).execute -- cgit v1.2.3 From 3735ce43fc5adfb82fb22730978ba0bca696c37d Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 1 Jul 2018 05:13:15 -0700 Subject: Add a Reconfigure button and improve layout of import screen --- lib/bitbucket_server/connection.rb | 2 +- lib/bitbucket_server/representation/pull_request.rb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb index 7374b73fa60..5c3bea3b743 100644 --- a/lib/bitbucket_server/connection.rb +++ b/lib/bitbucket_server/connection.rb @@ -7,7 +7,7 @@ module BitbucketServer def initialize(options = {}) @api_version = options.fetch(:api_version, DEFAULT_API_VERSION) @base_uri = options[:base_uri] - @username = options[:username] + @username = options[:user] @token = options[:password] end diff --git a/lib/bitbucket_server/representation/pull_request.rb b/lib/bitbucket_server/representation/pull_request.rb index 96a27564641..3e460f04064 100644 --- a/lib/bitbucket_server/representation/pull_request.rb +++ b/lib/bitbucket_server/representation/pull_request.rb @@ -27,6 +27,10 @@ module BitbucketServer end end + def merged? + state == 'merged' + end + def created_at raw['createdDate'] end -- cgit v1.2.3 From 8377057364f70108c4cc77f1a3ead33779c83cec Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 1 Jul 2018 05:55:23 -0700 Subject: Fix URL link in Bitbucket status --- lib/bitbucket_server/representation/repo.rb | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib') diff --git a/lib/bitbucket_server/representation/repo.rb b/lib/bitbucket_server/representation/repo.rb index 2f3a60b204b..52dbe040a60 100644 --- a/lib/bitbucket_server/representation/repo.rb +++ b/lib/bitbucket_server/representation/repo.rb @@ -13,6 +13,10 @@ module BitbucketServer raw['slug'] end + def browse_url + link = raw.dig('project', 'links', 'self').first.fetch('href') + end + def clone_url raw['links']['clone'].find { |link| link['name'].starts_with?('http') }.fetch('href') end -- cgit v1.2.3 From f4f4e02564dcfa94ebf25e680a1778a5239d150d Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 1 Jul 2018 08:26:30 -0700 Subject: Add merge commit SHA --- lib/gitlab/bitbucket_server_import/importer.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index 9bc6de2ea5d..84d16074891 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -63,7 +63,7 @@ module Gitlab target_branch_sha = project.repository.commit(target_branch_sha)&.sha || target_branch_sha project.merge_requests.find_by(iid: pull_request.iid)&.destroy - merge_request = project.merge_requests.create!( + attributes = { iid: pull_request.iid, title: pull_request.title, description: description, @@ -78,8 +78,10 @@ module Gitlab assignee_id: nil, created_at: pull_request.created_at, updated_at: pull_request.updated_at - ) + } + attributes[:merge_commit_sha] = target_branch_sha if pull_request.merged? + merge_request = project.merge_requests.create!(attributes) import_pull_request_comments(pull_request, merge_request) if merge_request.persisted? rescue StandardError => e errors << { type: :pull_request, iid: pull_request.iid, errors: e.message, trace: e.backtrace.join("\n"), raw_response: pull_request.raw } -- cgit v1.2.3 From d3c75ea4d916e0c81de2e8f7c5b1d748af9fa45e Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 3 Jul 2018 16:37:17 -0700 Subject: Support creating a remote branch to import closed pull requests --- lib/bitbucket_server/client.rb | 10 +++++ lib/bitbucket_server/connection.rb | 16 ++++++- lib/gitlab/bitbucket_server_import/importer.rb | 58 +++++++++++++++++++++++++- 3 files changed, 82 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/client.rb b/lib/bitbucket_server/client.rb index e632adea68c..49654e45226 100644 --- a/lib/bitbucket_server/client.rb +++ b/lib/bitbucket_server/client.rb @@ -27,6 +27,16 @@ module BitbucketServer get_collection(path, :repo) end + def create_branch(project_key, repo, branch_name, sha) + payload = { + name: branch_name, + startPoint: sha, + message: 'GitLab temporary branch for import' + } + + connection.post("/projects/#{project_key}/repos/#{repo}/branches", payload.to_json) + end + private def get_collection(path, type) diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb index 5c3bea3b743..21807c8a229 100644 --- a/lib/bitbucket_server/connection.rb +++ b/lib/bitbucket_server/connection.rb @@ -12,7 +12,6 @@ module BitbucketServer end def get(path, extra_query = {}) - auth = { username: username, password: token } response = Gitlab::HTTP.get(build_url(path), basic_auth: auth, params: extra_query) @@ -20,8 +19,23 @@ module BitbucketServer response.parsed_response end + def post(path, body) + Gitlab::HTTP.post(build_url(path), + basic_auth: auth, + headers: post_headers, + body: body) + end + private + def auth + @auth ||= { username: username, password: token } + end + + def post_headers + @post_headers ||= { 'Content-Type' => 'application/json' } + end + def build_url(path) return path if path.starts_with?(root_url) diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index 84d16074891..2a33feb68a9 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -4,6 +4,16 @@ module Gitlab include Gitlab::ShellAdapter attr_reader :project, :project_key, :repository_slug, :client, :errors, :users + REMOTE_NAME = 'bitbucket_server'.freeze + + def self.imports_repository? + true + end + + def self.refmap + [:heads, :tags, '+refs/pull-requests/*/to:refs/merge-requests/*/head'] + end + def initialize(project) @project = project @project_key = project.import_data.data['project_key'] @@ -12,9 +22,11 @@ module Gitlab @formatter = Gitlab::ImportFormatter.new @errors = [] @users = {} + @temp_branches = [] end def execute + import_repository import_pull_requests handle_errors @@ -48,11 +60,55 @@ module Gitlab @repo ||= client.repo(project_key, repository_slug) end + def sha_exists?(sha) + project.repository.commit(sha) + end + + def track_temp_branch(pull_request, index) + temp_branch_name = "gitlab/import/pull-request/#{pull_request.iid}-#{index}" + + @temp_branches << temp_branch_name + temp_branch_name + end + + def restore_branches(pull_request) + shas_to_restore = [pull_request.source_branch_sha, pull_request.target_branch_sha] + resync = false + + shas_to_restore.each_with_index do |sha, index| + next if sha_exists?(sha) + + branch_name = track_temp_branch(pull_request, index) + response = client.create_branch(project_key, repository_slug, branch_name, sha) + + if response.success? + resync = true + else + Rails.logger.warn("BitbucketServerImporter: Unable to recreate branch for SHA #{sha}: #{response.code}") + end + end + + import_repository if resync + end + + def import_repository + project.ensure_repository + project.repository.fetch_as_mirror(project.import_url, refmap: self.class.refmap, remote_name: REMOTE_NAME) + rescue Gitlab::Shell::Error, Gitlab::Git::RepositoryMirroring::RemoteError => e + # Expire cache to prevent scenarios such as: + # 1. First import failed, but the repo was imported successfully, so +exists?+ returns true + # 2. Retried import, repo is broken or not imported but +exists?+ still returns true + project.repository.expire_content_cache if project.repository_exists? + + raise RuntimeError, e.message + end + def import_pull_requests pull_requests = client.pull_requests(project_key, repository_slug) - pull_requests.each do |pull_request| begin + restore_branches(pull_request) + description = '' description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author_email) description += pull_request.description -- cgit v1.2.3 From ee22da98f0d310b341782e55331036cca9560ec5 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 3 Jul 2018 16:44:32 -0700 Subject: Assign merge event to ghost user if committer cannot be found --- lib/gitlab/bitbucket_server_import/importer.rb | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index 2a33feb68a9..b451aa9d77c 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -164,6 +164,13 @@ module Gitlab return unless committer + user_id = + if committer + find_user_id(committer) + else + User.ghost + end + user_id = find_user_id(committer) if committer timestamp = merge_event.merge_timestamp -- cgit v1.2.3 From a78e36abab6d30d2fc7571ab095a2c08bd52dd24 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 5 Jul 2018 14:09:01 -0700 Subject: Improve error handling of Bitbucket login errors --- lib/bitbucket_server/connection.rb | 23 +++++++++++++++++++++-- lib/bitbucket_server/representation/repo.rb | 4 ++++ 2 files changed, 25 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb index 21807c8a229..092c878c93b 100644 --- a/lib/bitbucket_server/connection.rb +++ b/lib/bitbucket_server/connection.rb @@ -1,5 +1,7 @@ module BitbucketServer class Connection + include ActionView::Helpers::SanitizeHelper + DEFAULT_API_VERSION = '1.0'.freeze attr_reader :api_version, :base_uri, :username, :token @@ -15,19 +17,36 @@ module BitbucketServer response = Gitlab::HTTP.get(build_url(path), basic_auth: auth, params: extra_query) - ## Handle failure + + check_errors!(response) response.parsed_response end def post(path, body) - Gitlab::HTTP.post(build_url(path), + response = Gitlab::HTTP.post(build_url(path), basic_auth: auth, headers: post_headers, body: body) + + check_errors!(response) + response end private + def check_errors!(response) + if response.code != 200 + error = + if response.parsed_response + sanitize(response.parsed_response.dig('errors', 0, 'message')) + end + + message = "Error #{response.code}" + message += ": #{error}" if error + raise ::BitbucketServer::Error::Unauthorized, message + end + end + def auth @auth ||= { username: username, password: token } end diff --git a/lib/bitbucket_server/representation/repo.rb b/lib/bitbucket_server/representation/repo.rb index 52dbe040a60..cf64f0ac8fa 100644 --- a/lib/bitbucket_server/representation/repo.rb +++ b/lib/bitbucket_server/representation/repo.rb @@ -5,6 +5,10 @@ module BitbucketServer super(raw) end + def project_name + raw.dig('project', 'name') + end + def owner project['name'] end -- cgit v1.2.3 From 022a0c2fdee1cb802b2a4999a472c1c2b760b312 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 5 Jul 2018 21:11:03 -0700 Subject: Fix Rubocop errors --- lib/bitbucket_server/representation/repo.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/bitbucket_server/representation/repo.rb b/lib/bitbucket_server/representation/repo.rb index cf64f0ac8fa..6e15f7e2fce 100644 --- a/lib/bitbucket_server/representation/repo.rb +++ b/lib/bitbucket_server/representation/repo.rb @@ -18,7 +18,7 @@ module BitbucketServer end def browse_url - link = raw.dig('project', 'links', 'self').first.fetch('href') + raw.dig('project', 'links', 'self').first.fetch('href') end def clone_url -- cgit v1.2.3 From 12b031ce8f1240785cd74f10cfdafb151c3f924b Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 5 Jul 2018 21:11:16 -0700 Subject: Fix pagination --- lib/bitbucket_server/connection.rb | 2 +- lib/bitbucket_server/paginator.rb | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb index 092c878c93b..c5e40a3964c 100644 --- a/lib/bitbucket_server/connection.rb +++ b/lib/bitbucket_server/connection.rb @@ -16,7 +16,7 @@ module BitbucketServer def get(path, extra_query = {}) response = Gitlab::HTTP.get(build_url(path), basic_auth: auth, - params: extra_query) + query: extra_query) check_errors!(response) response.parsed_response diff --git a/lib/bitbucket_server/paginator.rb b/lib/bitbucket_server/paginator.rb index c995cf4c3bd..a17045be97e 100644 --- a/lib/bitbucket_server/paginator.rb +++ b/lib/bitbucket_server/paginator.rb @@ -1,6 +1,6 @@ module BitbucketServer class Paginator - PAGE_LENGTH = 25 # The minimum length is 10 and the maximum is 100. + PAGE_LENGTH = 25 def initialize(connection, url, type) @connection = connection @@ -24,12 +24,12 @@ module BitbucketServer page.nil? || page.next? end - def next_url - page.nil? ? url : page.next + def next_offset + page.nil? ? 0 : page.next end def fetch_next_page - parsed_response = connection.get(next_url, pagelen: PAGE_LENGTH, sort: :created_on) + parsed_response = connection.get(@url, start: next_offset, limit: PAGE_LENGTH) Page.new(parsed_response, type) end end -- cgit v1.2.3 From cc4a39cc54bbf8e3312d50ae9823292d34675b78 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 5 Jul 2018 21:11:29 -0700 Subject: Reduce number of API requests and fix merge request events --- lib/gitlab/bitbucket_server_import/importer.rb | 95 ++++++++++++-------------- 1 file changed, 43 insertions(+), 52 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index b451aa9d77c..73b84cac97a 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -100,59 +100,61 @@ module Gitlab # 2. Retried import, repo is broken or not imported but +exists?+ still returns true project.repository.expire_content_cache if project.repository_exists? - raise RuntimeError, e.message + raise e.message end def import_pull_requests pull_requests = client.pull_requests(project_key, repository_slug) pull_requests.each do |pull_request| begin - restore_branches(pull_request) - - description = '' - description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author_email) - description += pull_request.description - - source_branch_sha = pull_request.source_branch_sha - target_branch_sha = pull_request.target_branch_sha - source_branch_sha = project.repository.commit(source_branch_sha)&.sha || source_branch_sha - target_branch_sha = project.repository.commit(target_branch_sha)&.sha || target_branch_sha - project.merge_requests.find_by(iid: pull_request.iid)&.destroy - - attributes = { - iid: pull_request.iid, - title: pull_request.title, - description: description, - source_project: project, - source_branch: Gitlab::Git.ref_name(pull_request.source_branch_name), - source_branch_sha: source_branch_sha, - target_project: project, - target_branch: Gitlab::Git.ref_name(pull_request.target_branch_name), - target_branch_sha: target_branch_sha, - state: pull_request.state, - author_id: gitlab_user_id(project, pull_request.author_email), - assignee_id: nil, - created_at: pull_request.created_at, - updated_at: pull_request.updated_at - } - - attributes[:merge_commit_sha] = target_branch_sha if pull_request.merged? - merge_request = project.merge_requests.create!(attributes) - import_pull_request_comments(pull_request, merge_request) if merge_request.persisted? + import_bitbucket_pull_request(pull_request) rescue StandardError => e errors << { type: :pull_request, iid: pull_request.iid, errors: e.message, trace: e.backtrace.join("\n"), raw_response: pull_request.raw } end end end + def import_bitbucket_pull_request(pull_request) + restore_branches(pull_request) + + description = '' + description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author_email) + description += pull_request.description + + source_branch_sha = pull_request.source_branch_sha + target_branch_sha = pull_request.target_branch_sha + source_branch_sha = project.repository.commit(source_branch_sha)&.sha || source_branch_sha + target_branch_sha = project.repository.commit(target_branch_sha)&.sha || target_branch_sha + project.merge_requests.find_by(iid: pull_request.iid)&.destroy + + attributes = { + iid: pull_request.iid, + title: pull_request.title, + description: description, + source_project: project, + source_branch: Gitlab::Git.ref_name(pull_request.source_branch_name), + source_branch_sha: source_branch_sha, + target_project: project, + target_branch: Gitlab::Git.ref_name(pull_request.target_branch_name), + target_branch_sha: target_branch_sha, + state: pull_request.state, + author_id: gitlab_user_id(project, pull_request.author_email), + assignee_id: nil, + created_at: pull_request.created_at, + updated_at: pull_request.updated_at + } + + attributes[:merge_commit_sha] = target_branch_sha if pull_request.merged? + merge_request = project.merge_requests.create!(attributes) + import_pull_request_comments(pull_request, merge_request) if merge_request.persisted? + end + def import_pull_request_comments(pull_request, merge_request) - # XXX This is inefficient since we are making multiple requests to the activities endpoint - merge_event = client.activities(project_key, repository_slug, pull_request.iid).find(&:merge_event?) + comments, other_activities = client.activities(project_key, repository_slug, pull_request.iid).partition(&:comment?) + merge_event = other_activities.find(&:merge_event?) import_merge_event(merge_request, merge_event) if merge_event - comments = client.activities(project_key, repository_slug, pull_request.iid).select(&:comment?) - inline_comments, pr_comments = comments.partition(&:inline_comment?) import_inline_comments(inline_comments.map(&:comment), pull_request, merge_request) @@ -162,22 +164,11 @@ module Gitlab def import_merge_event(merge_request, merge_event) committer = merge_event.committer_email - return unless committer - - user_id = - if committer - find_user_id(committer) - else - User.ghost - end - - user_id = find_user_id(committer) if committer + user = User.ghost + user ||= find_user_id(committer) if committer timestamp = merge_event.merge_timestamp - - return unless user_id - - event = Event.create(merged_by_id: user_id, merged_at: timestamp) - MergeRequestMetricsService.new(merge_request.metrics).merge(event) + metric = MergeRequest::Metrics.find_or_initialize_by(merge_request: merge_request) + metric.update_attributes(merged_by: user, merged_at: timestamp) end def import_inline_comments(inline_comments, pull_request, merge_request) -- cgit v1.2.3 From 8ab13e1497f3341185c31101fffc5b93925ee7af Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 5 Jul 2018 21:51:12 -0700 Subject: Batch re-creating branches and fetching the remote --- lib/gitlab/bitbucket_server_import/importer.rb | 75 +++++++++++++++++--------- 1 file changed, 51 insertions(+), 24 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index 73b84cac97a..e948a038e8e 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -5,6 +5,7 @@ module Gitlab attr_reader :project, :project_key, :repository_slug, :client, :errors, :users REMOTE_NAME = 'bitbucket_server'.freeze + BATCH_SIZE = 100 def self.imports_repository? true @@ -64,31 +65,42 @@ module Gitlab project.repository.commit(sha) end - def track_temp_branch(pull_request, index) - temp_branch_name = "gitlab/import/pull-request/#{pull_request.iid}-#{index}" + def temp_branch_name(pull_request, suffix) + "gitlab/import/pull-request/#{pull_request.iid}/#{suffix}" + end + + def restore_branches(pull_requests) + shas_to_restore = [] + pull_requests.each do |pull_request| + shas_to_restore << { + temp_branch_name(pull_request, :from) => pull_request.source_branch_sha, + temp_branch_name(pull_request, :to) => pull_request.target_branch_sha + } + end - @temp_branches << temp_branch_name - temp_branch_name + created_branches = restore_branch_shas(shas_to_restore) + @temp_branches << created_branches + import_repository unless created_branches.empty? end - def restore_branches(pull_request) - shas_to_restore = [pull_request.source_branch_sha, pull_request.target_branch_sha] - resync = false + def restore_branch_shas(shas_to_restore) + branches_created = [] - shas_to_restore.each_with_index do |sha, index| - next if sha_exists?(sha) + shas_to_restore.each_with_index do |shas, index| + shas.each do |branch_name, sha| + next if sha_exists?(sha) - branch_name = track_temp_branch(pull_request, index) - response = client.create_branch(project_key, repository_slug, branch_name, sha) + response = client.create_branch(project_key, repository_slug, branch_name, sha) - if response.success? - resync = true - else - Rails.logger.warn("BitbucketServerImporter: Unable to recreate branch for SHA #{sha}: #{response.code}") + if response.success? + branches_created << branch_name + else + Rails.logger.warn("BitbucketServerImporter: Unable to recreate branch for SHA #{sha}: #{response.code}") + end end end - import_repository if resync + branches_created end def import_repository @@ -103,20 +115,35 @@ module Gitlab raise e.message end + # Bitbucket Server keeps tracks of references for open pull requests in + # refs/heads/pull-requests, but closed and merged requests get moved + # into hidden internal refs under stash-refs/pull-requests. Unless the + # SHAs involved are at the tip of a branch or tag, there is no way to + # retrieve the server for those commits. + # + # To avoid losing history, we use the Bitbucket API to re-create the branch + # on the remote server. Then we have to issue a `git fetch` to download these + # branches. def import_pull_requests - pull_requests = client.pull_requests(project_key, repository_slug) - pull_requests.each do |pull_request| - begin - import_bitbucket_pull_request(pull_request) - rescue StandardError => e - errors << { type: :pull_request, iid: pull_request.iid, errors: e.message, trace: e.backtrace.join("\n"), raw_response: pull_request.raw } + pull_requests = client.pull_requests(project_key, repository_slug).to_a + + # Creating branches on the server and fetching the newly-created branches + # may take a number of network round-trips. Do this in batches so that we can + # avoid doing a git fetch for every new branch. + pull_requests.each_slice(BATCH_SIZE) do |batch| + restore_branches(batch) + + batch.each do |pull_request| + begin + import_bitbucket_pull_request(pull_request) + rescue StandardError => e + errors << { type: :pull_request, iid: pull_request.iid, errors: e.message, trace: e.backtrace.join("\n"), raw_response: pull_request.raw } + end end end end def import_bitbucket_pull_request(pull_request) - restore_branches(pull_request) - description = '' description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author_email) description += pull_request.description -- cgit v1.2.3 From bb98ed112c509c6265ba4dff81d7987c52a8748c Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 10 Jul 2018 23:21:12 -0700 Subject: Add missing file --- lib/bitbucket_server/collection.rb | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 lib/bitbucket_server/collection.rb (limited to 'lib') diff --git a/lib/bitbucket_server/collection.rb b/lib/bitbucket_server/collection.rb new file mode 100644 index 00000000000..1f199c99854 --- /dev/null +++ b/lib/bitbucket_server/collection.rb @@ -0,0 +1,21 @@ +module BitbucketServer + class Collection < Enumerator + def initialize(paginator) + super() do |yielder| + loop do + paginator.items.each { |item| yielder << item } + end + end + + lazy + end + + def method_missing(method, *args) + return super unless self.respond_to?(method) + + self.__send__(method, *args) do |item| # rubocop:disable GitlabSecurity/PublicSend + block_given? ? yield(item) : item + end + end + end +end -- cgit v1.2.3 From f85712fb9814170aac631c7c24ca287fe5a0eb04 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 12 Jul 2018 15:26:37 -0700 Subject: Use browse URL of project to link imports --- lib/bitbucket_server/representation/repo.rb | 2 +- lib/gitlab/bitbucket_server_import/project_creator.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/representation/repo.rb b/lib/bitbucket_server/representation/repo.rb index 6e15f7e2fce..8e163e8a1fc 100644 --- a/lib/bitbucket_server/representation/repo.rb +++ b/lib/bitbucket_server/representation/repo.rb @@ -18,7 +18,7 @@ module BitbucketServer end def browse_url - raw.dig('project', 'links', 'self').first.fetch('href') + raw.dig('links', 'self').first.fetch('href') end def clone_url diff --git a/lib/gitlab/bitbucket_server_import/project_creator.rb b/lib/gitlab/bitbucket_server_import/project_creator.rb index 031ae29120d..49b81b24856 100644 --- a/lib/gitlab/bitbucket_server_import/project_creator.rb +++ b/lib/gitlab/bitbucket_server_import/project_creator.rb @@ -22,7 +22,7 @@ module Gitlab namespace_id: namespace.id, visibility_level: repo.visibility_level, import_type: 'bitbucket_server', - import_source: repo.full_name, + import_source: repo.browse_url, import_url: repo.clone_url, import_data: { credentials: session_data, -- cgit v1.2.3 From 15220291ae1752b9e36ad0b2320180614ec37c59 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 12 Jul 2018 22:33:36 -0700 Subject: Add spec for BitbucketServer::Connection --- lib/bitbucket_server/client.rb | 1 - lib/bitbucket_server/connection.rb | 8 ++++++-- lib/bitbucket_server/error/unauthorized.rb | 5 ----- 3 files changed, 6 insertions(+), 8 deletions(-) delete mode 100644 lib/bitbucket_server/error/unauthorized.rb (limited to 'lib') diff --git a/lib/bitbucket_server/client.rb b/lib/bitbucket_server/client.rb index 49654e45226..802fe7953ce 100644 --- a/lib/bitbucket_server/client.rb +++ b/lib/bitbucket_server/client.rb @@ -18,7 +18,6 @@ module BitbucketServer def repo(project, repo_name) parsed_response = connection.get("/projects/#{project}/repos/#{repo_name}") - # XXXX TODO Handle failure BitbucketServer::Representation::Repo.new(parsed_response) end diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb index c5e40a3964c..0e7817fc82e 100644 --- a/lib/bitbucket_server/connection.rb +++ b/lib/bitbucket_server/connection.rb @@ -6,6 +6,8 @@ module BitbucketServer attr_reader :api_version, :base_uri, :username, :token + ConnectionError = Class.new(StandardError) + def initialize(options = {}) @api_version = options.fetch(:api_version, DEFAULT_API_VERSION) @base_uri = options[:base_uri] @@ -19,6 +21,7 @@ module BitbucketServer query: extra_query) check_errors!(response) + response.parsed_response end @@ -29,6 +32,7 @@ module BitbucketServer body: body) check_errors!(response) + response end @@ -37,13 +41,13 @@ module BitbucketServer def check_errors!(response) if response.code != 200 error = - if response.parsed_response + if response.parsed_response && response.parsed_response.is_a?(Hash) sanitize(response.parsed_response.dig('errors', 0, 'message')) end message = "Error #{response.code}" message += ": #{error}" if error - raise ::BitbucketServer::Error::Unauthorized, message + raise ConnectionError, message end end diff --git a/lib/bitbucket_server/error/unauthorized.rb b/lib/bitbucket_server/error/unauthorized.rb deleted file mode 100644 index 96ed469e913..00000000000 --- a/lib/bitbucket_server/error/unauthorized.rb +++ /dev/null @@ -1,5 +0,0 @@ -module BitbucketServer - module Error - Unauthorized = Class.new(StandardError) - end -end -- cgit v1.2.3 From ec4109d413f6161b43ad4cc1a6251af30d3bcf03 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 12 Jul 2018 23:17:05 -0700 Subject: Make Connection#post consistent with Connection#get and add specs --- lib/bitbucket_server/connection.rb | 22 +++++++++++----------- lib/gitlab/bitbucket_server_import/importer.rb | 9 ++++----- 2 files changed, 15 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb index 0e7817fc82e..853d09b776a 100644 --- a/lib/bitbucket_server/connection.rb +++ b/lib/bitbucket_server/connection.rb @@ -33,22 +33,22 @@ module BitbucketServer check_errors!(response) - response + response.parsed_response end private def check_errors!(response) - if response.code != 200 - error = - if response.parsed_response && response.parsed_response.is_a?(Hash) - sanitize(response.parsed_response.dig('errors', 0, 'message')) - end - - message = "Error #{response.code}" - message += ": #{error}" if error - raise ConnectionError, message - end + return if response.code == 200 + + details = + if response.parsed_response && response.parsed_response.is_a?(Hash) + sanitize(response.parsed_response.dig('errors', 0, 'message')) + end + + message = "Error #{response.code}" + message += ": #{details}" if details + raise ConnectionError, message end def auth diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index e948a038e8e..580dc0bb852 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -90,12 +90,11 @@ module Gitlab shas.each do |branch_name, sha| next if sha_exists?(sha) - response = client.create_branch(project_key, repository_slug, branch_name, sha) - - if response.success? + begin + client.create_branch(project_key, repository_slug, branch_name, sha) branches_created << branch_name - else - Rails.logger.warn("BitbucketServerImporter: Unable to recreate branch for SHA #{sha}: #{response.code}") + rescue BitbucketServer::Connection::ConnectionError => e + Rails.logger.warn("BitbucketServerImporter: Unable to recreate branch for SHA #{sha}: #{e}") end end end -- cgit v1.2.3 From c793252e8b33a1b7d071aca90c9c05e54ad973b5 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 13 Jul 2018 15:39:24 -0700 Subject: Add support for deleting branches via the Bitbucket Server API --- lib/bitbucket_server/client.rb | 9 +++++++++ lib/bitbucket_server/connection.rb | 27 ++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/bitbucket_server/client.rb b/lib/bitbucket_server/client.rb index 802fe7953ce..bfa8301f643 100644 --- a/lib/bitbucket_server/client.rb +++ b/lib/bitbucket_server/client.rb @@ -36,6 +36,15 @@ module BitbucketServer connection.post("/projects/#{project_key}/repos/#{repo}/branches", payload.to_json) end + def delete_branch(project_key, repo, branch_name, sha) + payload = { + name: Gitlab::Git::BRANCH_REF_PREFIX + branch_name, + dryRun: false + } + + connection.delete(:branches, "/projects/#{project_key}/repos/#{repo}/branches", payload.to_json) + end + private def get_collection(path, type) diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb index 853d09b776a..0b65203a824 100644 --- a/lib/bitbucket_server/connection.rb +++ b/lib/bitbucket_server/connection.rb @@ -36,10 +36,27 @@ module BitbucketServer response.parsed_response end + # We need to support two different APIs for deletion: + # + # /rest/api/1.0/projects/{projectKey}/repos/{repositorySlug}/branches/default + # /rest/branch-utils/1.0/projects/{projectKey}/repos/{repositorySlug}/branches + def delete(resource, path, body) + url = delete_url(resource, path) + + response = Gitlab::HTTP.delete(url, + basic_auth: auth, + headers: post_headers, + body: body) + + check_errors!(response) + + response.parsed_response + end + private def check_errors!(response) - return if response.code == 200 + return if response.code >= 200 && response.code < 300 details = if response.parsed_response && response.parsed_response.is_a?(Hash) @@ -68,5 +85,13 @@ module BitbucketServer def root_url "#{base_uri}/rest/api/#{api_version}" end + + def delete_url(resource, path) + if resource == :branches + "#{base_uri}/branch-utils/#{api_version}#{path}" + else + build_url(path) + end + end end end -- cgit v1.2.3 From 0d9f74d2f5510b6ba449ac20956f96dc555a46c2 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 13 Jul 2018 15:43:15 -0700 Subject: Delete temporary branches after an import --- lib/gitlab/bitbucket_server_import/importer.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index 580dc0bb852..cc21e7aad9e 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -29,6 +29,7 @@ module Gitlab def execute import_repository import_pull_requests + delete_temp_branches handle_errors true @@ -142,6 +143,17 @@ module Gitlab end end + def delete_temp_branches + @temp_branches.each do |branch_name| + begin + client.delete_branch(project_key, repository_slug, branch_name) + project.repository.delete_branch(branch_name) + rescue BitbucketServer::Connection::ConnectionError => e + @errors << { type: :delete_temp_branches, branch_name: branch_name, errors: e.message } + end + end + end + def import_bitbucket_pull_request(pull_request) description = '' description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author_email) -- cgit v1.2.3 From 57c9a89313ff4fbaaf50e434ef37e956d0e80d0e Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 15 Jul 2018 15:36:18 -0700 Subject: Fix assorted bugs and write spec for importing merge event --- lib/gitlab/bitbucket_server_import/importer.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index cc21e7aad9e..36069f0f168 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -163,6 +163,8 @@ module Gitlab target_branch_sha = pull_request.target_branch_sha source_branch_sha = project.repository.commit(source_branch_sha)&.sha || source_branch_sha target_branch_sha = project.repository.commit(target_branch_sha)&.sha || target_branch_sha + author = gitlab_user_id(project, pull_request.author_email) || User.ghost + project.merge_requests.find_by(iid: pull_request.iid)&.destroy attributes = { @@ -176,7 +178,7 @@ module Gitlab target_branch: Gitlab::Git.ref_name(pull_request.target_branch_name), target_branch_sha: target_branch_sha, state: pull_request.state, - author_id: gitlab_user_id(project, pull_request.author_email), + author_id: author.id, assignee_id: nil, created_at: pull_request.created_at, updated_at: pull_request.updated_at @@ -202,8 +204,8 @@ module Gitlab def import_merge_event(merge_request, merge_event) committer = merge_event.committer_email - user = User.ghost - user ||= find_user_id(committer) if committer + user = find_user_id(committer) if committer + user ||= User.ghost timestamp = merge_event.merge_timestamp metric = MergeRequest::Metrics.find_or_initialize_by(merge_request: merge_request) metric.update_attributes(merged_by: user, merged_at: timestamp) -- cgit v1.2.3 From 11dd390c116f163a2e8a46af83e0d1bb194fb1f7 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sun, 15 Jul 2018 22:07:39 -0700 Subject: Add tests for pull request comments --- lib/gitlab/bitbucket_server_import/importer.rb | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index 36069f0f168..c07525cef96 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -55,7 +55,10 @@ module Gitlab return users[email] if users.key?(email) - users[email] = User.find_by_any_email(email) + user = User.find_by_any_email(email) + users[email] = user&.id if user + + user&.id end def repo @@ -163,7 +166,7 @@ module Gitlab target_branch_sha = pull_request.target_branch_sha source_branch_sha = project.repository.commit(source_branch_sha)&.sha || source_branch_sha target_branch_sha = project.repository.commit(target_branch_sha)&.sha || target_branch_sha - author = gitlab_user_id(project, pull_request.author_email) || User.ghost + author_id = gitlab_user_id(project, pull_request.author_email) || User.ghost.id project.merge_requests.find_by(iid: pull_request.iid)&.destroy @@ -178,7 +181,7 @@ module Gitlab target_branch: Gitlab::Git.ref_name(pull_request.target_branch_name), target_branch_sha: target_branch_sha, state: pull_request.state, - author_id: author.id, + author_id: author_id, assignee_id: nil, created_at: pull_request.created_at, updated_at: pull_request.updated_at @@ -204,11 +207,11 @@ module Gitlab def import_merge_event(merge_request, merge_event) committer = merge_event.committer_email - user = find_user_id(committer) if committer - user ||= User.ghost + user_id = find_user_id(committer) if committer + user_id ||= User.ghost.id timestamp = merge_event.merge_timestamp metric = MergeRequest::Metrics.find_or_initialize_by(merge_request: merge_request) - metric.update_attributes(merged_by: user, merged_at: timestamp) + metric.update(merged_by_id: user_id, merged_at: timestamp) end def import_inline_comments(inline_comments, pull_request, merge_request) -- cgit v1.2.3 From 7b288105ed3dbf6265041f0af38ff36ccb338e33 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 17 Jul 2018 13:46:09 -0700 Subject: Remove unneeded method --- lib/gitlab/bitbucket_server_import/importer.rb | 4 ---- 1 file changed, 4 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index c07525cef96..a0b6dedb23c 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -273,10 +273,6 @@ module Gitlab end end - def generate_line_code(pr_comment) - Gitlab::Git.diff_line_code(pr_comment.file_path, pr_comment.new_pos, pr_comment.old_pos) - end - def pull_request_comment_attributes(comment) { project: project, -- cgit v1.2.3 From 78f23d3c17d1cabe92d3587d9fbc41c719e74def Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 17 Jul 2018 13:53:48 -0700 Subject: Simplify user lookups --- lib/gitlab/bitbucket_server_import/importer.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index a0b6dedb23c..9005d9b1334 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -46,7 +46,7 @@ module Gitlab }.to_json) end - def gitlab_user_id(project, email) + def gitlab_user_id(email) find_user_id(email) || project.creator_id end @@ -166,7 +166,7 @@ module Gitlab target_branch_sha = pull_request.target_branch_sha source_branch_sha = project.repository.commit(source_branch_sha)&.sha || source_branch_sha target_branch_sha = project.repository.commit(target_branch_sha)&.sha || target_branch_sha - author_id = gitlab_user_id(project, pull_request.author_email) || User.ghost.id + author_id = gitlab_user_id(pull_request.author_email) project.merge_requests.find_by(iid: pull_request.iid)&.destroy @@ -207,8 +207,7 @@ module Gitlab def import_merge_event(merge_request, merge_event) committer = merge_event.committer_email - user_id = find_user_id(committer) if committer - user_id ||= User.ghost.id + user_id = gitlab_user_id(committer) timestamp = merge_event.merge_timestamp metric = MergeRequest::Metrics.find_or_initialize_by(merge_request: merge_request) metric.update(merged_by_id: user_id, merged_at: timestamp) @@ -277,7 +276,7 @@ module Gitlab { project: project, note: comment.note, - author_id: gitlab_user_id(project, comment.author_email), + author_id: gitlab_user_id(comment.author_email), created_at: comment.created_at, updated_at: comment.updated_at } -- cgit v1.2.3 From da02df04ec8e961598a941d23782bbd8c7a6bc99 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 17 Jul 2018 15:01:33 -0700 Subject: Code cleanup and test threaded discussions --- lib/gitlab/bitbucket_server_import/importer.rb | 27 +++++++++++--------------- 1 file changed, 11 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index 9005d9b1334..10a2bcabb60 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -83,7 +83,7 @@ module Gitlab end created_branches = restore_branch_shas(shas_to_restore) - @temp_branches << created_branches + @temp_branches += created_branches import_repository unless created_branches.empty? end @@ -200,7 +200,7 @@ module Gitlab inline_comments, pr_comments = comments.partition(&:inline_comment?) - import_inline_comments(inline_comments.map(&:comment), pull_request, merge_request) + import_inline_comments(inline_comments.map(&:comment), merge_request) import_standalone_pr_comments(pr_comments.map(&:comment), merge_request) end @@ -213,32 +213,27 @@ module Gitlab metric.update(merged_by_id: user_id, merged_at: timestamp) end - def import_inline_comments(inline_comments, pull_request, merge_request) + def import_inline_comments(inline_comments, merge_request) inline_comments.each do |comment| - parent = build_diff_note(merge_request, comment) + position = build_position(merge_request, comment) + parent = build_diff_note(merge_request, comment, position) next unless parent&.persisted? + discussion_id = parent.discussion_id + comment.comments.each do |reply| - begin - attributes = pull_request_comment_attributes(reply) - attributes.merge!( - position: build_position(merge_request, comment), - discussion_id: parent.discussion_id, - type: 'DiffNote') - merge_request.notes.create!(attributes) - rescue StandardError => e - errors << { type: :pull_request, id: comment.id, errors: e.message } - end + build_diff_note(merge_request, reply, position, discussion_id) end end end - def build_diff_note(merge_request, comment) + def build_diff_note(merge_request, comment, position, discussion_id = nil) attributes = pull_request_comment_attributes(comment) attributes.merge!( - position: build_position(merge_request, comment), + position: position, type: 'DiffNote') + attributes[:discussion_id] = discussion_id if discussion_id merge_request.notes.create!(attributes) rescue StandardError => e -- cgit v1.2.3 From 099f9fcd13b2d3e5ad8e2a7adbe8255e424cea50 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 17 Jul 2018 15:48:01 -0700 Subject: Fallback to creating a note if DiffNote fails to import --- lib/gitlab/bitbucket_server_import/importer.rb | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index 10a2bcabb60..133b8f763ec 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -216,31 +216,49 @@ module Gitlab def import_inline_comments(inline_comments, merge_request) inline_comments.each do |comment| position = build_position(merge_request, comment) - parent = build_diff_note(merge_request, comment, position) + parent = create_diff_note(merge_request, comment, position) next unless parent&.persisted? discussion_id = parent.discussion_id comment.comments.each do |reply| - build_diff_note(merge_request, reply, position, discussion_id) + create_diff_note(merge_request, reply, position, discussion_id) end end end - def build_diff_note(merge_request, comment, position, discussion_id = nil) + def create_diff_note(merge_request, comment, position, discussion_id = nil) attributes = pull_request_comment_attributes(comment) attributes.merge!( position: position, type: 'DiffNote') attributes[:discussion_id] = discussion_id if discussion_id - merge_request.notes.create!(attributes) + note = merge_request.notes.build(attributes) + + if note.valid? + note.save + return note + end + + # Fallback to a regular comment + create_fallback_diff_note(merge_request, comment) rescue StandardError => e errors << { type: :pull_request, id: comment.id, errors: e.message } nil end + # Bitbucket Server supports the ability to comment on any line, not just the + # line in the diff. If we can't add the note as a DiffNote, fallback to creating + # a regular note. + def create_fallback_diff_note(merge_request, comment) + attributes = pull_request_comment_attributes(comment) + attributes[:note] = "Comment on file: #{comment.file_path}, old position: #{comment.old_pos}, new_position: #{comment.new_pos}\n\n" + attributes[:note] + + merge_request.notes.create!(attributes) + end + def build_position(merge_request, pr_comment) params = { diff_refs: merge_request.diff_refs, -- cgit v1.2.3 From bb0603935d33f532d65547161aaf9d1823acaff3 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 17 Jul 2018 15:58:12 -0700 Subject: Some formatting cleanup --- lib/gitlab/bitbucket_server_import/importer.rb | 11 ++++------- lib/gitlab/bitbucket_server_import/project_creator.rb | 3 +-- 2 files changed, 5 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index 133b8f763ec..93fa7a0f4d4 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -230,9 +230,7 @@ module Gitlab def create_diff_note(merge_request, comment, position, discussion_id = nil) attributes = pull_request_comment_attributes(comment) - attributes.merge!( - position: position, - type: 'DiffNote') + attributes.merge!(position: position, type: 'DiffNote') attributes[:discussion_id] = discussion_id if discussion_id note = merge_request.notes.build(attributes) @@ -242,16 +240,15 @@ module Gitlab return note end - # Fallback to a regular comment + # Bitbucket Server supports the ability to comment on any line, not just the + # line in the diff. If we can't add the note as a DiffNote, fallback to creating + # a regular note. create_fallback_diff_note(merge_request, comment) rescue StandardError => e errors << { type: :pull_request, id: comment.id, errors: e.message } nil end - # Bitbucket Server supports the ability to comment on any line, not just the - # line in the diff. If we can't add the note as a DiffNote, fallback to creating - # a regular note. def create_fallback_diff_note(merge_request, comment) attributes = pull_request_comment_attributes(comment) attributes[:note] = "Comment on file: #{comment.file_path}, old position: #{comment.old_pos}, new_position: #{comment.new_pos}\n\n" + attributes[:note] diff --git a/lib/gitlab/bitbucket_server_import/project_creator.rb b/lib/gitlab/bitbucket_server_import/project_creator.rb index 49b81b24856..35e8cd7e0ab 100644 --- a/lib/gitlab/bitbucket_server_import/project_creator.rb +++ b/lib/gitlab/bitbucket_server_import/project_creator.rb @@ -26,8 +26,7 @@ module Gitlab import_url: repo.clone_url, import_data: { credentials: session_data, - data: { project_key: project_key, - repo_slug: repo_slug } + data: { project_key: project_key, repo_slug: repo_slug } }, skip_wiki: true ).execute -- cgit v1.2.3 From a19a5b57e464e53ad6e92c69f1e16f7cc0b27718 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 17 Jul 2018 21:42:07 -0700 Subject: Improve formatting of diff notes that could not be imported --- lib/gitlab/bitbucket_server_import/importer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index 93fa7a0f4d4..3db3ebfc80a 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -251,7 +251,7 @@ module Gitlab def create_fallback_diff_note(merge_request, comment) attributes = pull_request_comment_attributes(comment) - attributes[:note] = "Comment on file: #{comment.file_path}, old position: #{comment.old_pos}, new_position: #{comment.new_pos}\n\n" + attributes[:note] + attributes[:note] = "*Comment on file: #{comment.file_path}, old line: #{comment.old_pos}, new line: #{comment.new_pos}*\n\n" + attributes[:note] merge_request.notes.create!(attributes) end -- cgit v1.2.3 From 19cd1ba7bc2b5e501770abf9a61e3ffc54f682e7 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 17 Jul 2018 22:29:18 -0700 Subject: Fix identation in lib/bitbucket_server/connection.rb --- lib/bitbucket_server/connection.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb index 0b65203a824..13bbc240c99 100644 --- a/lib/bitbucket_server/connection.rb +++ b/lib/bitbucket_server/connection.rb @@ -27,9 +27,9 @@ module BitbucketServer def post(path, body) response = Gitlab::HTTP.post(build_url(path), - basic_auth: auth, - headers: post_headers, - body: body) + basic_auth: auth, + headers: post_headers, + body: body) check_errors!(response) @@ -44,9 +44,9 @@ module BitbucketServer url = delete_url(resource, path) response = Gitlab::HTTP.delete(url, - basic_auth: auth, - headers: post_headers, - body: body) + basic_auth: auth, + headers: post_headers, + body: body) check_errors!(response) -- cgit v1.2.3 From 0fcaf0344914bd62d5061cc5ebf07e3986da44a8 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 17 Jul 2018 22:50:08 -0700 Subject: Escape username and password in UrlSanitizer#full_url If a user uses a password with certain characters (e.g. /, #, +, etc.) UrlSanitizer#full_url will generate an invalid URL that cannot be parsed properly by Addressable::URI. If used with UrlBlocker, this will be flagged as an invalid URI. --- lib/gitlab/url_sanitizer.rb | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/url_sanitizer.rb b/lib/gitlab/url_sanitizer.rb index de8b6ec69ce..4a6a2212f48 100644 --- a/lib/gitlab/url_sanitizer.rb +++ b/lib/gitlab/url_sanitizer.rb @@ -71,12 +71,12 @@ module Gitlab def generate_full_url return @url unless valid_credentials? - @full_url = @url.dup + generated = @url.dup - @full_url.password = credentials[:password] if credentials[:password].present? - @full_url.user = credentials[:user] if credentials[:user].present? + generated.password = encode_percent(credentials[:password]) if credentials[:password].present? + generated.user = encode_percent(credentials[:user]) if credentials[:user].present? - @full_url + generated end def safe_url @@ -89,5 +89,10 @@ module Gitlab def valid_credentials? credentials && credentials.is_a?(Hash) && credentials.any? end + + def encode_percent(string) + # CGI.escape converts spaces to +, but this doesn't work for git clone + CGI.escape(string).gsub('+', '%20') + end end end -- cgit v1.2.3 From 9d59616b95889fc7f0f2ba84ba2c9d9de8205c71 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 18 Jul 2018 23:15:24 -0700 Subject: Fix URL for deleting branches --- lib/bitbucket_server/connection.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb index 13bbc240c99..3918944aa81 100644 --- a/lib/bitbucket_server/connection.rb +++ b/lib/bitbucket_server/connection.rb @@ -88,7 +88,7 @@ module BitbucketServer def delete_url(resource, path) if resource == :branches - "#{base_uri}/branch-utils/#{api_version}#{path}" + "#{base_uri}/rest/branch-utils/#{api_version}#{path}" else build_url(path) end -- cgit v1.2.3 From f673923d3fccc56317aa5a88026bf364b66c5252 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 18 Jul 2018 23:20:10 -0700 Subject: Fix and clean up code for deleting branches --- lib/gitlab/bitbucket_server_import/importer.rb | 47 +++++++++++++++----------- 1 file changed, 27 insertions(+), 20 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index 3db3ebfc80a..b33923dd80c 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -7,6 +7,8 @@ module Gitlab REMOTE_NAME = 'bitbucket_server'.freeze BATCH_SIZE = 100 + TempBranch = Struct.new(:name, :sha) + def self.imports_repository? true end @@ -73,37 +75,42 @@ module Gitlab "gitlab/import/pull-request/#{pull_request.iid}/#{suffix}" end + # This method restores required SHAs that GitLab needs to create diffs + # into branch names as the following: + # + # gitlab/import/pull-request/N/{to,from} def restore_branches(pull_requests) shas_to_restore = [] + pull_requests.each do |pull_request| - shas_to_restore << { - temp_branch_name(pull_request, :from) => pull_request.source_branch_sha, - temp_branch_name(pull_request, :to) => pull_request.target_branch_sha - } + shas_to_restore << TempBranch.new(temp_branch_name(pull_request, :from), + pull_request.source_branch_sha) + shas_to_restore << TempBranch.new(temp_branch_name(pull_request, :to), + pull_request.target_branch_sha) end + # Create the branches on the Bitbucket Server first created_branches = restore_branch_shas(shas_to_restore) + @temp_branches += created_branches + # Now sync the repository so we get the new branches import_repository unless created_branches.empty? end def restore_branch_shas(shas_to_restore) - branches_created = [] + shas_to_restore.each_with_object([]) do |temp_branch, branches_created| + branch_name = temp_branch.name + sha = temp_branch.sha - shas_to_restore.each_with_index do |shas, index| - shas.each do |branch_name, sha| - next if sha_exists?(sha) + next if sha_exists?(sha) - begin - client.create_branch(project_key, repository_slug, branch_name, sha) - branches_created << branch_name - rescue BitbucketServer::Connection::ConnectionError => e - Rails.logger.warn("BitbucketServerImporter: Unable to recreate branch for SHA #{sha}: #{e}") - end + begin + client.create_branch(project_key, repository_slug, branch_name, sha) + branches_created << temp_branch + rescue BitbucketServer::Connection::ConnectionError => e + Rails.logger.warn("BitbucketServerImporter: Unable to recreate branch for SHA #{sha}: #{e}") end end - - branches_created end def import_repository @@ -147,12 +154,12 @@ module Gitlab end def delete_temp_branches - @temp_branches.each do |branch_name| + @temp_branches.each do |branch| begin - client.delete_branch(project_key, repository_slug, branch_name) - project.repository.delete_branch(branch_name) + client.delete_branch(project_key, repository_slug, branch.name, branch.sha) + project.repository.delete_branch(branch.name) rescue BitbucketServer::Connection::ConnectionError => e - @errors << { type: :delete_temp_branches, branch_name: branch_name, errors: e.message } + @errors << { type: :delete_temp_branches, branch_name: branch.name, errors: e.message } end end end -- cgit v1.2.3 From a2a21c5e61886083f04e175a3f218ddf73a40499 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 17 Jul 2018 22:50:08 -0700 Subject: Escape username and password in UrlSanitizer#full_url If a user uses a password with certain characters (e.g. /, #, +, etc.) UrlSanitizer#full_url will generate an invalid URL that cannot be parsed properly by Addressable::URI. If used with UrlBlocker, this will be flagged as an invalid URI. --- lib/gitlab/url_sanitizer.rb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/url_sanitizer.rb b/lib/gitlab/url_sanitizer.rb index 4a6a2212f48..308a95d2f09 100644 --- a/lib/gitlab/url_sanitizer.rb +++ b/lib/gitlab/url_sanitizer.rb @@ -71,12 +71,10 @@ module Gitlab def generate_full_url return @url unless valid_credentials? - generated = @url.dup - - generated.password = encode_percent(credentials[:password]) if credentials[:password].present? - generated.user = encode_percent(credentials[:user]) if credentials[:user].present? - - generated + @url.dup.tap do |generated| + generated.password = encode_percent(credentials[:password]) if credentials[:password].present? + generated.user = encode_percent(credentials[:user]) if credentials[:user].present? + end end def safe_url -- cgit v1.2.3 From 69fe32a51c1f0299663177cad47641c50af5caec Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 20 Jul 2018 09:45:59 -0700 Subject: Disable recovery of missing SHAs by default --- lib/gitlab/bitbucket_server_import/importer.rb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index b33923dd80c..aa164f1a61a 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -2,6 +2,7 @@ module Gitlab module BitbucketServerImport class Importer include Gitlab::ShellAdapter + attr_reader :recover_missing_commits attr_reader :project, :project_key, :repository_slug, :client, :errors, :users REMOTE_NAME = 'bitbucket_server'.freeze @@ -17,8 +18,14 @@ module Gitlab [:heads, :tags, '+refs/pull-requests/*/to:refs/merge-requests/*/head'] end - def initialize(project) + # Unlike GitHub, you can't grab the commit SHAs for pull requests that + # have been closed but not merged even though Bitbucket has these + # commits internally. We can recover these pull requests by creating a + # branch with the Bitbucket REST API, but by default we turn this + # behavior off. + def initialize(project, recover_missing_commits: false) @project = project + @recover_missing_commits = recover_missing_commits @project_key = project.import_data.data['project_key'] @repository_slug = project.import_data.data['repo_slug'] @client = BitbucketServer::Client.new(project.import_data.credentials) @@ -141,7 +148,7 @@ module Gitlab # may take a number of network round-trips. Do this in batches so that we can # avoid doing a git fetch for every new branch. pull_requests.each_slice(BATCH_SIZE) do |batch| - restore_branches(batch) + restore_branches(batch) if recover_missing_commits batch.each do |pull_request| begin -- cgit v1.2.3 From 7c1aaf68f82f05fd34f567be2481b8283e3328c2 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 20 Jul 2018 21:46:01 -0700 Subject: Add spec for BitbucketServer::Representation::Repo --- lib/bitbucket_server/representation/repo.rb | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/representation/repo.rb b/lib/bitbucket_server/representation/repo.rb index 8e163e8a1fc..1338f877fc1 100644 --- a/lib/bitbucket_server/representation/repo.rb +++ b/lib/bitbucket_server/representation/repo.rb @@ -9,10 +9,6 @@ module BitbucketServer raw.dig('project', 'name') end - def owner - project['name'] - end - def slug raw['slug'] end @@ -30,7 +26,7 @@ module BitbucketServer end def full_name - "#{owner}/#{name}" + "#{project_name}/#{name}" end def issues_enabled? @@ -45,10 +41,6 @@ module BitbucketServer raw['scmId'] == 'git' end - def has_wiki? - false - end - def visibility_level if project['public'] Gitlab::VisibilityLevel::PUBLIC -- cgit v1.2.3 From 3fd704b239cbf008f48ca3ca2e6119ca993794b7 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 20 Jul 2018 22:30:28 -0700 Subject: Add spec for BitBucketServer pull reuest parsing --- .../representation/pull_request.rb | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/representation/pull_request.rb b/lib/bitbucket_server/representation/pull_request.rb index 3e460f04064..344b6806a91 100644 --- a/lib/bitbucket_server/representation/pull_request.rb +++ b/lib/bitbucket_server/representation/pull_request.rb @@ -2,11 +2,11 @@ module BitbucketServer module Representation class PullRequest < Representation::Base def author - raw.fetch('author', {}).fetch('user', {}).fetch('name') + raw.dig('author', 'user', 'name') end def author_email - raw.fetch('author', {}).fetch('user', {}).fetch('emailAddress') + raw.dig('author', 'user', 'emailAddress') end def description @@ -32,11 +32,11 @@ module BitbucketServer end def created_at - raw['createdDate'] + Time.at(created_date / 1000) if created_date.is_a?(Integer) end def updated_at - raw['updatedDate'] + Time.at(updated_date / 1000) if created_date.is_a?(Integer) end def title @@ -44,29 +44,29 @@ module BitbucketServer end def source_branch_name - source_branch['id'] + dig('fromRef', 'id') end def source_branch_sha - source_branch['latestCommit'] + dig('fromRef', 'latestCommit') end def target_branch_name - target_branch['id'] + dig('toRef', 'id') end def target_branch_sha - target_branch['latestCommit'] + dig('toRef', 'latestCommit') end private - def source_branch - raw['fromRef'] || {} + def created_date + raw['createdDate'] end - def target_branch - raw['toRef'] || {} + def updated_date + raw['updatedDate'] end end end -- cgit v1.2.3 From f94b52256d1bedfe6b01ef31f0bed0615b10d918 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 20 Jul 2018 22:31:17 -0700 Subject: Remove merge request deletion code --- lib/gitlab/bitbucket_server_import/importer.rb | 2 -- 1 file changed, 2 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index aa164f1a61a..f5febd266cb 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -182,8 +182,6 @@ module Gitlab target_branch_sha = project.repository.commit(target_branch_sha)&.sha || target_branch_sha author_id = gitlab_user_id(pull_request.author_email) - project.merge_requests.find_by(iid: pull_request.iid)&.destroy - attributes = { iid: pull_request.iid, title: pull_request.title, -- cgit v1.2.3 From 4160a8dd81ecb1d062b20de44720f280902e940b Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 25 Jul 2018 05:22:53 -0700 Subject: Enable frozen_string_literal --- lib/bitbucket_server/client.rb | 2 ++ lib/bitbucket_server/collection.rb | 2 ++ lib/bitbucket_server/connection.rb | 4 +++- lib/bitbucket_server/page.rb | 2 ++ lib/bitbucket_server/paginator.rb | 2 ++ lib/bitbucket_server/representation/activity.rb | 4 +++- lib/bitbucket_server/representation/base.rb | 2 ++ lib/bitbucket_server/representation/comment.rb | 2 ++ lib/bitbucket_server/representation/pull_request.rb | 2 ++ lib/bitbucket_server/representation/pull_request_comment.rb | 2 ++ lib/bitbucket_server/representation/repo.rb | 2 ++ 11 files changed, 24 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/client.rb b/lib/bitbucket_server/client.rb index bfa8301f643..85c9757339e 100644 --- a/lib/bitbucket_server/client.rb +++ b/lib/bitbucket_server/client.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module BitbucketServer class Client attr_reader :connection diff --git a/lib/bitbucket_server/collection.rb b/lib/bitbucket_server/collection.rb index 1f199c99854..b50c5dde352 100644 --- a/lib/bitbucket_server/collection.rb +++ b/lib/bitbucket_server/collection.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module BitbucketServer class Collection < Enumerator def initialize(paginator) diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb index 3918944aa81..8a3775bd152 100644 --- a/lib/bitbucket_server/connection.rb +++ b/lib/bitbucket_server/connection.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + module BitbucketServer class Connection include ActionView::Helpers::SanitizeHelper - DEFAULT_API_VERSION = '1.0'.freeze + DEFAULT_API_VERSION = '1.0' attr_reader :api_version, :base_uri, :username, :token diff --git a/lib/bitbucket_server/page.rb b/lib/bitbucket_server/page.rb index 17be8cbb860..a91ccad5738 100644 --- a/lib/bitbucket_server/page.rb +++ b/lib/bitbucket_server/page.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module BitbucketServer class Page attr_reader :attrs, :items diff --git a/lib/bitbucket_server/paginator.rb b/lib/bitbucket_server/paginator.rb index a17045be97e..c351fb2f11f 100644 --- a/lib/bitbucket_server/paginator.rb +++ b/lib/bitbucket_server/paginator.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module BitbucketServer class Paginator PAGE_LENGTH = 25 diff --git a/lib/bitbucket_server/representation/activity.rb b/lib/bitbucket_server/representation/activity.rb index 7c552c7f428..8c2b0be559e 100644 --- a/lib/bitbucket_server/representation/activity.rb +++ b/lib/bitbucket_server/representation/activity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module BitbucketServer module Representation class Activity < Representation::Base @@ -6,7 +8,7 @@ module BitbucketServer end def comment? - action == 'COMMENTED'.freeze + action == 'COMMENTED' end def inline_comment? diff --git a/lib/bitbucket_server/representation/base.rb b/lib/bitbucket_server/representation/base.rb index 11b32b70c4c..0f3f2b60f5a 100644 --- a/lib/bitbucket_server/representation/base.rb +++ b/lib/bitbucket_server/representation/base.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module BitbucketServer module Representation class Base diff --git a/lib/bitbucket_server/representation/comment.rb b/lib/bitbucket_server/representation/comment.rb index 44f76139fc4..8e442fb4e8e 100644 --- a/lib/bitbucket_server/representation/comment.rb +++ b/lib/bitbucket_server/representation/comment.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module BitbucketServer module Representation # A general comment with the structure: diff --git a/lib/bitbucket_server/representation/pull_request.rb b/lib/bitbucket_server/representation/pull_request.rb index 344b6806a91..a39753fa7c8 100644 --- a/lib/bitbucket_server/representation/pull_request.rb +++ b/lib/bitbucket_server/representation/pull_request.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module BitbucketServer module Representation class PullRequest < Representation::Base diff --git a/lib/bitbucket_server/representation/pull_request_comment.rb b/lib/bitbucket_server/representation/pull_request_comment.rb index c7d08e604fd..dea244bdec5 100644 --- a/lib/bitbucket_server/representation/pull_request_comment.rb +++ b/lib/bitbucket_server/representation/pull_request_comment.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module BitbucketServer module Representation # An inline comment with the following structure that identifies diff --git a/lib/bitbucket_server/representation/repo.rb b/lib/bitbucket_server/representation/repo.rb index 1338f877fc1..ba90a15f11f 100644 --- a/lib/bitbucket_server/representation/repo.rb +++ b/lib/bitbucket_server/representation/repo.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module BitbucketServer module Representation class Repo < Representation::Base -- cgit v1.2.3 From bf2d029a3d7a347cafbca4e2d5d879940d91cb90 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 25 Jul 2018 15:38:45 -0700 Subject: Add spec for pull request activity --- lib/bitbucket_server/representation/activity.rb | 36 ++++++------------------- 1 file changed, 8 insertions(+), 28 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/representation/activity.rb b/lib/bitbucket_server/representation/activity.rb index 8c2b0be559e..fab4b426ae2 100644 --- a/lib/bitbucket_server/representation/activity.rb +++ b/lib/bitbucket_server/representation/activity.rb @@ -3,16 +3,12 @@ module BitbucketServer module Representation class Activity < Representation::Base - def action - raw['action'] - end - def comment? action == 'COMMENTED' end def inline_comment? - comment? && comment_anchor + !!(comment? && comment_anchor) end def comment @@ -26,57 +22,41 @@ module BitbucketServer end end - # XXX Move this into MergeEvent + # TODO Move this into MergeEvent def merge_event? action == 'MERGED' end def committer_user - commit.fetch('committer', {})['displayName'] + raw.dig('commit', 'committer', 'displayName') end def committer_email - commit.fetch('committer', {})['emailAddress'] + raw.dig('commit', 'committer', 'emailAddress') end def merge_timestamp - timestamp = commit.fetch('committer', {})['commiterTimestamp'] + timestamp = raw.dig('commit', 'committerTimestamp') Time.at(timestamp / 1000.0) if timestamp.is_a?(Integer) end - def commit - raw.fetch('commit', {}) - end - def created_at Time.at(created_date / 1000) if created_date.is_a?(Integer) end - def updated_at - Time.at(updated_date / 1000) if created_date.is_a?(Integer) - end - private - def raw_comment - raw.fetch('comment', {}) + def action + raw['action'] end def comment_anchor raw['commentAnchor'] end - def author - raw_comment.fetch('author', {}) - end - def created_date - comment['createdDate'] - end - - def updated_date - comment['updatedDate'] + raw.dig('createdDate') end end end -- cgit v1.2.3 From 771dec84704d04fa515157882f6a3f2cc0ee74d1 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 25 Jul 2018 20:53:25 -0700 Subject: Clean up implementation --- lib/bitbucket_server/representation/activity.rb | 2 +- lib/bitbucket_server/representation/comment.rb | 25 +++++++++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/representation/activity.rb b/lib/bitbucket_server/representation/activity.rb index fab4b426ae2..2e6082bea87 100644 --- a/lib/bitbucket_server/representation/activity.rb +++ b/lib/bitbucket_server/representation/activity.rb @@ -56,7 +56,7 @@ module BitbucketServer end def created_date - raw.dig('createdDate') + raw['createdDate'] end end end diff --git a/lib/bitbucket_server/representation/comment.rb b/lib/bitbucket_server/representation/comment.rb index 8e442fb4e8e..7a25ac8aed1 100644 --- a/lib/bitbucket_server/representation/comment.rb +++ b/lib/bitbucket_server/representation/comment.rb @@ -28,11 +28,11 @@ module BitbucketServer end def author_username - author['username'] + author['displayName'] end def author_email - author['displayName'] + author['emailAddress'] end def note @@ -44,9 +44,22 @@ module BitbucketServer end def updated_at - Time.at(updated_date / 1000) if created_date.is_a?(Integer) + Time.at(updated_date / 1000) if updated_date.is_a?(Integer) end + # Bitbucket Server supports the ability to reply to any comment + # and created multiple threads. It represents these as a linked list + # of comments within comments. For example: + # + # "comments": [ + # { + # "author" : ... + # "comments": [ + # { + # "author": ... + # + # Since GitLab only supports a single thread, we flatten all these + # comments into a single discussion. def comments workset = [raw_comment['comments']].compact all_comments = [] @@ -71,15 +84,15 @@ module BitbucketServer end def author - raw_comment.fetch('author', {}) + raw.dig('comment', 'author') end def created_date - raw_comment['createdDate'] + raw.dig('comment', 'createdDate') end def updated_date - raw_comment['updatedDate'] + raw.dig('comment', 'updatedDate') end end end -- cgit v1.2.3 From 930b5e7a5b6a6cdbedae327c3bb2fa1e260744b3 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 25 Jul 2018 21:39:52 -0700 Subject: Add spec for pull request comments and add comments --- .../representation/pull_request_comment.rb | 32 +++++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/representation/pull_request_comment.rb b/lib/bitbucket_server/representation/pull_request_comment.rb index dea244bdec5..a2b3873a397 100644 --- a/lib/bitbucket_server/representation/pull_request_comment.rb +++ b/lib/bitbucket_server/representation/pull_request_comment.rb @@ -15,11 +15,9 @@ module BitbucketServer # "path": "CHANGELOG.md", # "toHash": "a4c2164330f2549f67c13f36a93884cf66e976be" # } + # + # More details in https://docs.atlassian.com/bitbucket-server/rest/5.12.0/bitbucket-rest.html. class PullRequestComment < Comment - def file_type - comment_anchor['fileType'] - end - def from_sha comment_anchor['fromHash'] end @@ -44,6 +42,12 @@ module BitbucketServer line_type == 'REMOVED' end + # There are three line comment types: added, removed, or context. + # + # 1. An added type means a new line was inserted, so there is no old position. + # 2. A removed type means a line was removed, so there is no new position. + # 3. A context type means the line was unmodified, so there is both a + # old and new position. def new_pos return if removed? return unless line_position @@ -64,10 +68,30 @@ module BitbucketServer private + def file_type + comment_anchor['fileType'] + end + def line_type comment_anchor['lineType'] end + # Each comment contains the following information about the diff: + # + # hunks: [ + # { + # segments: [ + # { + # "lines": [ + # { + # "commentIds": [ N ], + # "source": X, + # "destination": Y + # }, ... + # ] .... + # + # To determine the line position of a comment, we search all the lines + # entries until we find this comment ID. def line_position @line_position ||= diff_hunks.each do |hunk| segments = hunk.fetch('segments', []) -- cgit v1.2.3 From f7a7a753a4217fb38f74b1761d077e490d615dae Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 25 Jul 2018 21:45:13 -0700 Subject: Add comment about why using `first` --- lib/bitbucket_server/representation/repo.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/bitbucket_server/representation/repo.rb b/lib/bitbucket_server/representation/repo.rb index ba90a15f11f..9c518341e66 100644 --- a/lib/bitbucket_server/representation/repo.rb +++ b/lib/bitbucket_server/representation/repo.rb @@ -16,6 +16,8 @@ module BitbucketServer end def browse_url + # The JSON reponse contains an array of 1 element. Not sure if there + # are cases where multiple links would be provided. raw.dig('links', 'self').first.fetch('href') end -- cgit v1.2.3 From e52f6bf5f9c30f7bdd7d05c1d692746bf9428a3d Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 25 Jul 2018 22:32:10 -0700 Subject: Fix failing spec --- lib/bitbucket_server/representation/pull_request.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/representation/pull_request.rb b/lib/bitbucket_server/representation/pull_request.rb index a39753fa7c8..43f61683ddd 100644 --- a/lib/bitbucket_server/representation/pull_request.rb +++ b/lib/bitbucket_server/representation/pull_request.rb @@ -46,19 +46,19 @@ module BitbucketServer end def source_branch_name - dig('fromRef', 'id') + raw.dig('fromRef', 'id') end def source_branch_sha - dig('fromRef', 'latestCommit') + raw.dig('fromRef', 'latestCommit') end def target_branch_name - dig('toRef', 'id') + raw.dig('toRef', 'id') end def target_branch_sha - dig('toRef', 'latestCommit') + raw.dig('toRef', 'latestCommit') end private -- cgit v1.2.3 From 7ee9c3c436c892d67fc36ec226528e9b70ce0e64 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 26 Jul 2018 15:45:38 -0700 Subject: Use project key instead of project name for imports --- lib/bitbucket_server/representation/repo.rb | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib') diff --git a/lib/bitbucket_server/representation/repo.rb b/lib/bitbucket_server/representation/repo.rb index 9c518341e66..6c494b79166 100644 --- a/lib/bitbucket_server/representation/repo.rb +++ b/lib/bitbucket_server/representation/repo.rb @@ -7,6 +7,10 @@ module BitbucketServer super(raw) end + def project_key + raw.dig('project', 'key') + end + def project_name raw.dig('project', 'name') end -- cgit v1.2.3 From 3f715bb438f81d8847e48d4fa76d1827b2df88eb Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 26 Jul 2018 23:05:22 -0700 Subject: Consolidate server errors and add specs --- lib/bitbucket_server/client.rb | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/client.rb b/lib/bitbucket_server/client.rb index 85c9757339e..15e59f93141 100644 --- a/lib/bitbucket_server/client.rb +++ b/lib/bitbucket_server/client.rb @@ -4,6 +4,18 @@ module BitbucketServer class Client attr_reader :connection + ServerError = Class.new(StandardError) + + SERVER_ERRORS = [SocketError, + OpenSSL::SSL::SSLError, + Errno::ECONNRESET, + Errno::ECONNREFUSED, + Errno::EHOSTUNREACH, + Net::OpenTimeout, + Net::ReadTimeout, + Gitlab::HTTP::BlockedUrlError, + BitbucketServer::Connection::ConnectionError].freeze + def initialize(options = {}) @connection = Connection.new(options) end @@ -13,8 +25,8 @@ module BitbucketServer get_collection(path, :pull_request) end - def activities(project_key, repo, pull_request) - path = "/projects/#{project_key}/repos/#{repo}/pull-requests/#{pull_request}/activities" + def activities(project_key, repo, pull_request_id) + path = "/projects/#{project_key}/repos/#{repo}/pull-requests/#{pull_request_id}/activities" get_collection(path, :activity) end @@ -50,8 +62,10 @@ module BitbucketServer private def get_collection(path, type) - paginator = BitbucketServer::Paginator.new(connection, path, type) + paginator = BitbucketServer::Paginator.new(connection, Addressable::URI.escape(path), type) BitbucketServer::Collection.new(paginator) + rescue *SERVER_ERRORS => e + raise ServerError, e end end end -- cgit v1.2.3 From 450030e9029f5de6fcfb850e5940838c2a76c81e Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 27 Jul 2018 12:58:03 -0700 Subject: Add Accept header for JSON --- lib/bitbucket_server/connection.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb index 8a3775bd152..2d438ae8ca1 100644 --- a/lib/bitbucket_server/connection.rb +++ b/lib/bitbucket_server/connection.rb @@ -20,6 +20,7 @@ module BitbucketServer def get(path, extra_query = {}) response = Gitlab::HTTP.get(build_url(path), basic_auth: auth, + headers: accept_headers, query: extra_query) check_errors!(response) @@ -74,8 +75,12 @@ module BitbucketServer @auth ||= { username: username, password: token } end + def accept_headers + @accept_headers ||= { 'Accept' => 'application/json' } + end + def post_headers - @post_headers ||= { 'Content-Type' => 'application/json' } + @post_headers ||= accept_headers.merge({ 'Content-Type' => 'application/json' }) end def build_url(path) -- cgit v1.2.3 From 57d1b60f61a790c034b8d43ce2358371464d2d67 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 27 Jul 2018 14:29:05 -0700 Subject: Handle invalid JSON from server --- lib/bitbucket_server/connection.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb index 2d438ae8ca1..ee0888eeecf 100644 --- a/lib/bitbucket_server/connection.rb +++ b/lib/bitbucket_server/connection.rb @@ -59,16 +59,17 @@ module BitbucketServer private def check_errors!(response) - return if response.code >= 200 && response.code < 300 + raise ConnectionError, "Response is not valid JSON" unless response.parsed_response.is_a?(Hash) - details = - if response.parsed_response && response.parsed_response.is_a?(Hash) - sanitize(response.parsed_response.dig('errors', 0, 'message')) - end + return if response.code >= 200 && response.code < 300 + details = sanitize(response.parsed_response.dig('errors', 0, 'message')) message = "Error #{response.code}" - message += ": #{details}" if details + message += ": #{details}" + raise ConnectionError, message + rescue JSON::ParserError + raise ConnectionError, "Unable to parse the server response as JSON" end def auth -- cgit v1.2.3 From dd937377cf4cb8cfd2c4af713347d83c67278456 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sat, 28 Jul 2018 21:48:37 -0700 Subject: Use a class method to consolidate timestamp conversion --- lib/bitbucket_server/representation/activity.rb | 4 ++-- lib/bitbucket_server/representation/base.rb | 4 ++++ lib/bitbucket_server/representation/comment.rb | 4 ++-- lib/bitbucket_server/representation/pull_request.rb | 4 ++-- 4 files changed, 10 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/representation/activity.rb b/lib/bitbucket_server/representation/activity.rb index 2e6082bea87..a594b0881f6 100644 --- a/lib/bitbucket_server/representation/activity.rb +++ b/lib/bitbucket_server/representation/activity.rb @@ -38,11 +38,11 @@ module BitbucketServer def merge_timestamp timestamp = raw.dig('commit', 'committerTimestamp') - Time.at(timestamp / 1000.0) if timestamp.is_a?(Integer) + self.class.convert_timestamp(timestamp) end def created_at - Time.at(created_date / 1000) if created_date.is_a?(Integer) + self.class.convert_timestamp(created_date) end private diff --git a/lib/bitbucket_server/representation/base.rb b/lib/bitbucket_server/representation/base.rb index 0f3f2b60f5a..a1961bae6ef 100644 --- a/lib/bitbucket_server/representation/base.rb +++ b/lib/bitbucket_server/representation/base.rb @@ -12,6 +12,10 @@ module BitbucketServer def self.decorate(entries) entries.map { |entry| new(entry)} end + + def self.convert_timestamp(time_usec) + Time.at(time_usec / 1000) if time_usec.is_a?(Integer) + end end end end diff --git a/lib/bitbucket_server/representation/comment.rb b/lib/bitbucket_server/representation/comment.rb index 7a25ac8aed1..59c44589aae 100644 --- a/lib/bitbucket_server/representation/comment.rb +++ b/lib/bitbucket_server/representation/comment.rb @@ -40,11 +40,11 @@ module BitbucketServer end def created_at - Time.at(created_date / 1000) if created_date.is_a?(Integer) + self.class.convert_timestamp(created_date) end def updated_at - Time.at(updated_date / 1000) if updated_date.is_a?(Integer) + self.class.convert_timestamp(created_date) end # Bitbucket Server supports the ability to reply to any comment diff --git a/lib/bitbucket_server/representation/pull_request.rb b/lib/bitbucket_server/representation/pull_request.rb index 43f61683ddd..4bb00d22b0c 100644 --- a/lib/bitbucket_server/representation/pull_request.rb +++ b/lib/bitbucket_server/representation/pull_request.rb @@ -34,11 +34,11 @@ module BitbucketServer end def created_at - Time.at(created_date / 1000) if created_date.is_a?(Integer) + self.class.convert_timestamp(created_date) end def updated_at - Time.at(updated_date / 1000) if created_date.is_a?(Integer) + self.class.convert_timestamp(updated_date) end def title -- cgit v1.2.3 From 6408cd00208c66f8657d68d904bc71d58f1d5000 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sat, 28 Jul 2018 21:50:48 -0700 Subject: Use raw_comment --- lib/bitbucket_server/representation/comment.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/representation/comment.rb b/lib/bitbucket_server/representation/comment.rb index 59c44589aae..a4da195f4ca 100644 --- a/lib/bitbucket_server/representation/comment.rb +++ b/lib/bitbucket_server/representation/comment.rb @@ -84,15 +84,15 @@ module BitbucketServer end def author - raw.dig('comment', 'author') + raw_comment['author'] end def created_date - raw.dig('comment', 'createdDate') + raw_comment['createdDate'] end def updated_date - raw.dig('comment', 'updatedDate') + raw_comment['updatedDate'] end end end -- cgit v1.2.3 From 7b23b5500583abdc9b3f53724e6f3fb5238f3449 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Sat, 28 Jul 2018 22:25:10 -0700 Subject: Restrict to confirmed users and allow memoization of unkonwn e-mails --- lib/gitlab/bitbucket_server_import/importer.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index f5febd266cb..0da572f24fc 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -64,8 +64,8 @@ module Gitlab return users[email] if users.key?(email) - user = User.find_by_any_email(email) - users[email] = user&.id if user + user = User.find_by_any_email(email, confirmed: true) + users[email] = user&.id user&.id end -- cgit v1.2.3 From 7c88c4af0e31081b4137bc1b93e40d22ef6a93c6 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 30 Jul 2018 23:19:36 -0700 Subject: No need to look up commit SHA since Bitbucket has a full-length SHA --- lib/gitlab/bitbucket_server_import/importer.rb | 2 -- 1 file changed, 2 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index 0da572f24fc..a96dd260221 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -178,8 +178,6 @@ module Gitlab source_branch_sha = pull_request.source_branch_sha target_branch_sha = pull_request.target_branch_sha - source_branch_sha = project.repository.commit(source_branch_sha)&.sha || source_branch_sha - target_branch_sha = project.repository.commit(target_branch_sha)&.sha || target_branch_sha author_id = gitlab_user_id(pull_request.author_email) attributes = { -- cgit v1.2.3 From 3cda8c93d879282af2ab5b21ca2d89daf0238f17 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Mon, 30 Jul 2018 23:37:54 -0700 Subject: Fix merge request SHA --- lib/bitbucket_server/representation/activity.rb | 14 +++++++++++--- lib/gitlab/bitbucket_server_import/importer.rb | 2 +- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/representation/activity.rb b/lib/bitbucket_server/representation/activity.rb index a594b0881f6..08bf30a5d1e 100644 --- a/lib/bitbucket_server/representation/activity.rb +++ b/lib/bitbucket_server/representation/activity.rb @@ -28,25 +28,33 @@ module BitbucketServer end def committer_user - raw.dig('commit', 'committer', 'displayName') + commit.dig('committer', 'displayName') end def committer_email - raw.dig('commit', 'committer', 'emailAddress') + commit.dig('committer', 'emailAddress') end def merge_timestamp - timestamp = raw.dig('commit', 'committerTimestamp') + timestamp = commit['committerTimestamp'] self.class.convert_timestamp(timestamp) end + def merge_commit + commit['id'] + end + def created_at self.class.convert_timestamp(created_date) end private + def commit + raw.fetch('commit', {}) + end + def action raw['action'] end diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index a96dd260221..d956968c013 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -197,7 +197,6 @@ module Gitlab updated_at: pull_request.updated_at } - attributes[:merge_commit_sha] = target_branch_sha if pull_request.merged? merge_request = project.merge_requests.create!(attributes) import_pull_request_comments(pull_request, merge_request) if merge_request.persisted? end @@ -219,6 +218,7 @@ module Gitlab user_id = gitlab_user_id(committer) timestamp = merge_event.merge_timestamp + merge_request.update_attributes({ merge_commit_sha: merge_event.merge_commit }) metric = MergeRequest::Metrics.find_or_initialize_by(merge_request: merge_request) metric.update(merged_by_id: user_id, merged_at: timestamp) end -- cgit v1.2.3 From 89d164fbe9097248342b05850ee5edc9ed997ab8 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 31 Jul 2018 08:58:45 -0700 Subject: Fix Rubocop complaint about update_attributes --- lib/gitlab/bitbucket_server_import/importer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index d956968c013..339b15ab13a 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -218,7 +218,7 @@ module Gitlab user_id = gitlab_user_id(committer) timestamp = merge_event.merge_timestamp - merge_request.update_attributes({ merge_commit_sha: merge_event.merge_commit }) + merge_request.update({ merge_commit_sha: merge_event.merge_commit }) metric = MergeRequest::Metrics.find_or_initialize_by(merge_request: merge_request) metric.update(merged_by_id: user_id, merged_at: timestamp) end -- cgit v1.2.3 From 710d82104d457ff40cc820e5953f8cd8777c49d1 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 31 Jul 2018 11:12:10 -0700 Subject: Track parental relationships in comments --- lib/bitbucket_server/representation/comment.rb | 45 ++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/representation/comment.rb b/lib/bitbucket_server/representation/comment.rb index a4da195f4ca..99b97a3b181 100644 --- a/lib/bitbucket_server/representation/comment.rb +++ b/lib/bitbucket_server/representation/comment.rb @@ -23,6 +23,16 @@ module BitbucketServer # } # } class Comment < Representation::Base + attr_reader :parent_comment + + CommentNode = Struct.new(:raw_comments, :parent) + + def initialize(raw, parent_comment: nil) + super(raw) + + @parent_comment = parent_comment + end + def id raw_comment['id'] end @@ -61,24 +71,45 @@ module BitbucketServer # Since GitLab only supports a single thread, we flatten all these # comments into a single discussion. def comments - workset = [raw_comment['comments']].compact + @comments ||= flatten_comments + end + + private + + # In order to provide context for each reply, we need to track + # the parent of each comment. This method works as follows: + # + # 1. Insert the root comment into the workset. The root element is the current note. + # 2. For each node in the workset: + # a. Examine if it has replies to that comment. If it does, + # insert that node into the workset. + # b. Parse that note into a Comment structure and add it to a flat list. + def flatten_comments + comments = raw_comment['comments'] + workset = + if comments + [CommentNode.new(comments, self)] + else + [] + end + all_comments = [] until workset.empty? - comments = workset.pop + node = workset.pop + parent = node.parent - comments.each do |comment| + node.raw_comments.each do |comment| new_comments = comment.delete('comments') - workset << new_comments if new_comments - all_comments << Comment.new({ 'comment' => comment }) + current_comment = Comment.new({ 'comment' => comment }, parent_comment: parent) + all_comments << current_comment + workset << CommentNode.new(new_comments, current_comment) if new_comments end end all_comments end - private - def raw_comment raw.fetch('comment', {}) end -- cgit v1.2.3 From 0085b76fb387237af02f717a894c89f7227cb81e Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 31 Jul 2018 11:33:37 -0700 Subject: Give some context on threaded discussions --- lib/gitlab/bitbucket_server_import/importer.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index 339b15ab13a..74ae6bc1ba5 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -293,9 +293,17 @@ module Gitlab end def pull_request_comment_attributes(comment) + note = + # Provide some context for replying + if comment.parent_comment + "> #{comment.parent_comment.note.truncate(80)}\n\n#{comment.note}" + else + comment.note + end + { project: project, - note: comment.note, + note: note, author_id: gitlab_user_id(comment.author_email), created_at: comment.created_at, updated_at: comment.updated_at -- cgit v1.2.3 From dbef9f21511f1b06135763e93bac484d49c4d72c Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 31 Jul 2018 12:58:26 -0700 Subject: Hide error details if there are none --- lib/bitbucket_server/connection.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb index ee0888eeecf..e100d8bde1c 100644 --- a/lib/bitbucket_server/connection.rb +++ b/lib/bitbucket_server/connection.rb @@ -65,7 +65,7 @@ module BitbucketServer details = sanitize(response.parsed_response.dig('errors', 0, 'message')) message = "Error #{response.code}" - message += ": #{details}" + message += ": #{details}" if details raise ConnectionError, message rescue JSON::ParserError -- cgit v1.2.3 From fc5df829bdbad7a47eae9ba1e14b55d3bb9ec39a Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 31 Jul 2018 13:22:40 -0700 Subject: Use URI.join to avoid slash headaches --- lib/bitbucket_server/connection.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb index e100d8bde1c..a43b4364cab 100644 --- a/lib/bitbucket_server/connection.rb +++ b/lib/bitbucket_server/connection.rb @@ -87,16 +87,16 @@ module BitbucketServer def build_url(path) return path if path.starts_with?(root_url) - "#{root_url}#{path}" + URI.join(root_url, path).to_s end def root_url - "#{base_uri}/rest/api/#{api_version}" + URI.join(base_uri, "/rest/api/#{api_version}").to_s end def delete_url(resource, path) if resource == :branches - "#{base_uri}/rest/branch-utils/#{api_version}#{path}" + URI.join(base_uri, "/rest/branch-utils/#{api_version}#{path}").to_s else build_url(path) end -- cgit v1.2.3 From 0b91a53302b4800a626fe4c930e08eebaa34b3da Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 31 Jul 2018 13:59:45 -0700 Subject: Handle empty description field --- lib/gitlab/bitbucket_server_import/importer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index 74ae6bc1ba5..a10cd860639 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -174,7 +174,7 @@ module Gitlab def import_bitbucket_pull_request(pull_request) description = '' description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author_email) - description += pull_request.description + description += pull_request.description if pull_request.description source_branch_sha = pull_request.source_branch_sha target_branch_sha = pull_request.target_branch_sha -- cgit v1.2.3 From 2aa4d7f10fe3f43095b5864cc797b67a84740922 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 31 Jul 2018 14:30:01 -0700 Subject: Fix bug where fallback diff notes would not have an associated position --- lib/gitlab/bitbucket_server_import/importer.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index a10cd860639..b7d9b4982c1 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -253,15 +253,15 @@ module Gitlab # Bitbucket Server supports the ability to comment on any line, not just the # line in the diff. If we can't add the note as a DiffNote, fallback to creating # a regular note. - create_fallback_diff_note(merge_request, comment) + create_fallback_diff_note(merge_request, comment, position) rescue StandardError => e errors << { type: :pull_request, id: comment.id, errors: e.message } nil end - def create_fallback_diff_note(merge_request, comment) + def create_fallback_diff_note(merge_request, comment, position) attributes = pull_request_comment_attributes(comment) - attributes[:note] = "*Comment on file: #{comment.file_path}, old line: #{comment.old_pos}, new line: #{comment.new_pos}*\n\n" + attributes[:note] + attributes[:note] = "*Comment on #{position.old_path}:#{position.old_line} -> #{position.new_path}:#{position.new_line}*\n\n" + attributes[:note] merge_request.notes.create!(attributes) end -- cgit v1.2.3 From a9d359b48463c2a77529b9a5f289f9d1abf45484 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 31 Jul 2018 15:33:08 -0700 Subject: Remove unnecessary lookup for merge request SHAs --- lib/gitlab/bitbucket_import/importer.rb | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index f3999e690fa..a6514b621f4 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -149,21 +149,16 @@ module Gitlab description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author) description += pull_request.description - source_branch_sha = pull_request.source_branch_sha - target_branch_sha = pull_request.target_branch_sha - source_branch_sha = project.repository.commit(source_branch_sha)&.sha || source_branch_sha - target_branch_sha = project.repository.commit(target_branch_sha)&.sha || target_branch_sha - merge_request = project.merge_requests.create!( iid: pull_request.iid, title: pull_request.title, description: description, source_project: project, source_branch: pull_request.source_branch_name, - source_branch_sha: source_branch_sha, + source_branch_sha: pull_request.source_branch_sha, target_project: project, target_branch: pull_request.target_branch_name, - target_branch_sha: target_branch_sha, + target_branch_sha: pull_request.target_branch_sha, state: pull_request.state, author_id: gitlab_user_id(project, pull_request.author), assignee_id: nil, -- cgit v1.2.3 From da88f61b8221b68add99250e33be88f2903b233e Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 31 Jul 2018 15:33:21 -0700 Subject: Improve formatting of fallback diff note --- lib/gitlab/bitbucket_server_import/importer.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index b7d9b4982c1..69f9cc398c8 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -261,8 +261,13 @@ module Gitlab def create_fallback_diff_note(merge_request, comment, position) attributes = pull_request_comment_attributes(comment) - attributes[:note] = "*Comment on #{position.old_path}:#{position.old_line} -> #{position.new_path}:#{position.new_line}*\n\n" + attributes[:note] + note = "*Comment on " + note += " #{position.old_path}:#{position.old_line} -->" if position.old_line + note += " #{position.new_path}:#{position.new_line}" if position.new_line + new += "*\n\n#{comment.note}" + + attributes[:note] = note merge_request.notes.create!(attributes) end -- cgit v1.2.3 From db4ff688da690f3b7c182e5e9f4f9146518c1d69 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 31 Jul 2018 15:38:14 -0700 Subject: Fix failing spec in fallback diff note change --- lib/gitlab/bitbucket_server_import/importer.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index 69f9cc398c8..eb3b76409cd 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -261,11 +261,11 @@ module Gitlab def create_fallback_diff_note(merge_request, comment, position) attributes = pull_request_comment_attributes(comment) - note = "*Comment on " + note = "*Comment on" note += " #{position.old_path}:#{position.old_line} -->" if position.old_line note += " #{position.new_path}:#{position.new_line}" if position.new_line - new += "*\n\n#{comment.note}" + note += "*\n\n#{comment.note}" attributes[:note] = note merge_request.notes.create!(attributes) -- cgit v1.2.3 From 7704404335814b39922d32eacf6b6f0961b9820f Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 31 Jul 2018 15:54:32 -0700 Subject: Revert "Remove unnecessary lookup for merge request SHAs" This reverts commit a9d359b48463c2a77529b9a5f289f9d1abf45484. --- lib/gitlab/bitbucket_import/importer.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb index a6514b621f4..f3999e690fa 100644 --- a/lib/gitlab/bitbucket_import/importer.rb +++ b/lib/gitlab/bitbucket_import/importer.rb @@ -149,16 +149,21 @@ module Gitlab description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author) description += pull_request.description + source_branch_sha = pull_request.source_branch_sha + target_branch_sha = pull_request.target_branch_sha + source_branch_sha = project.repository.commit(source_branch_sha)&.sha || source_branch_sha + target_branch_sha = project.repository.commit(target_branch_sha)&.sha || target_branch_sha + merge_request = project.merge_requests.create!( iid: pull_request.iid, title: pull_request.title, description: description, source_project: project, source_branch: pull_request.source_branch_name, - source_branch_sha: pull_request.source_branch_sha, + source_branch_sha: source_branch_sha, target_project: project, target_branch: pull_request.target_branch_name, - target_branch_sha: pull_request.target_branch_sha, + target_branch_sha: target_branch_sha, state: pull_request.state, author_id: gitlab_user_id(project, pull_request.author), assignee_id: nil, -- cgit v1.2.3 From d2fd6d23513b18b2fd6fe93fe76e5ea4c97120b3 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Tue, 31 Jul 2018 21:14:36 -0700 Subject: Deal with subpaths and trailing slashes properly --- lib/bitbucket_server/connection.rb | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb index a43b4364cab..a3150cc38d5 100644 --- a/lib/bitbucket_server/connection.rb +++ b/lib/bitbucket_server/connection.rb @@ -5,6 +5,7 @@ module BitbucketServer include ActionView::Helpers::SanitizeHelper DEFAULT_API_VERSION = '1.0' + SEPARATOR = '/' attr_reader :api_version, :base_uri, :username, :token @@ -87,19 +88,35 @@ module BitbucketServer def build_url(path) return path if path.starts_with?(root_url) - URI.join(root_url, path).to_s + url_join_paths(root_url, path) end def root_url - URI.join(base_uri, "/rest/api/#{api_version}").to_s + url_join_paths(base_uri, "/rest/api/#{api_version}") end def delete_url(resource, path) if resource == :branches - URI.join(base_uri, "/rest/branch-utils/#{api_version}#{path}").to_s + url_join_paths(base_uri, "/rest/branch-utils/#{api_version}#{path}") else build_url(path) end end + + # URI.join is stupid in that slashes are important: + # + # # URI.join('http://example.com/subpath', 'hello') + # => http://example.com/hello + # + # We really want http://example.com/subpath/hello + # + def url_join_paths(*paths) + paths.map { |path| strip_slashes(path) }.join(SEPARATOR) + end + + def strip_slashes(path) + path = path[1..-1] if path.starts_with?(SEPARATOR) + path.chomp(SEPARATOR) + end end end -- cgit v1.2.3 From aa5821e571620e09229721a1a235d93a554db6a3 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Wed, 1 Aug 2018 10:44:45 -0700 Subject: Assorted Bitbucket Server importer code cleanup based on review --- lib/bitbucket_server/connection.rb | 8 ++++---- lib/bitbucket_server/page.rb | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb index a3150cc38d5..45a437844bd 100644 --- a/lib/bitbucket_server/connection.rb +++ b/lib/bitbucket_server/connection.rb @@ -12,10 +12,10 @@ module BitbucketServer ConnectionError = Class.new(StandardError) def initialize(options = {}) - @api_version = options.fetch(:api_version, DEFAULT_API_VERSION) - @base_uri = options[:base_uri] - @username = options[:user] - @token = options[:password] + @api_version = options.fetch(:api_version, DEFAULT_API_VERSION) + @base_uri = options[:base_uri] + @username = options[:user] + @token = options[:password] end def get(path, extra_query = {}) diff --git a/lib/bitbucket_server/page.rb b/lib/bitbucket_server/page.rb index a91ccad5738..5d9a3168876 100644 --- a/lib/bitbucket_server/page.rb +++ b/lib/bitbucket_server/page.rb @@ -20,7 +20,7 @@ module BitbucketServer private def parse_attrs(raw) - raw.slice(*%w(size nextPageStart isLastPage)).symbolize_keys + raw.slice('size', 'nextPageStart', 'isLastPage').symbolize_keys end def parse_values(raw, bitbucket_rep_class) -- cgit v1.2.3 From a662670b90b67b814a91390998feae015138e753 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 2 Aug 2018 14:43:32 -0700 Subject: Keep alignment in lib/gitlab/import_sources.rb --- lib/gitlab/import_sources.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/import_sources.rb b/lib/gitlab/import_sources.rb index f901d1fdb5a..5fc21878761 100644 --- a/lib/gitlab/import_sources.rb +++ b/lib/gitlab/import_sources.rb @@ -9,16 +9,16 @@ module Gitlab # We exclude `bare_repository` here as it has no import class associated ImportTable = [ - ImportSource.new('github', 'GitHub', Gitlab::GithubImport::ParallelImporter), - ImportSource.new('bitbucket', 'Bitbucket Cloud', Gitlab::BitbucketImport::Importer), + ImportSource.new('github', 'GitHub', Gitlab::GithubImport::ParallelImporter), + ImportSource.new('bitbucket', 'Bitbucket Cloud', Gitlab::BitbucketImport::Importer), ImportSource.new('bitbucket_server', 'Bitbucket Server', Gitlab::BitbucketServerImport::Importer), - ImportSource.new('gitlab', 'GitLab.com', Gitlab::GitlabImport::Importer), - ImportSource.new('google_code', 'Google Code', Gitlab::GoogleCodeImport::Importer), - ImportSource.new('fogbugz', 'FogBugz', Gitlab::FogbugzImport::Importer), - ImportSource.new('git', 'Repo by URL', nil), - ImportSource.new('gitlab_project', 'GitLab export', Gitlab::ImportExport::Importer), - ImportSource.new('gitea', 'Gitea', Gitlab::LegacyGithubImport::Importer), - ImportSource.new('manifest', 'Manifest file', nil) + ImportSource.new('gitlab', 'GitLab.com', Gitlab::GitlabImport::Importer), + ImportSource.new('google_code', 'Google Code', Gitlab::GoogleCodeImport::Importer), + ImportSource.new('fogbugz', 'FogBugz', Gitlab::FogbugzImport::Importer), + ImportSource.new('git', 'Repo by URL', nil), + ImportSource.new('gitlab_project', 'GitLab export', Gitlab::ImportExport::Importer), + ImportSource.new('gitea', 'Gitea', Gitlab::LegacyGithubImport::Importer), + ImportSource.new('manifest', 'Manifest file', nil) ].freeze class << self -- cgit v1.2.3 From f2a99398bfd66a1971c2d26461fec508106e6eb9 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 2 Aug 2018 14:45:54 -0700 Subject: Use a case statement to determine pull request state --- lib/bitbucket_server/representation/pull_request.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/bitbucket_server/representation/pull_request.rb b/lib/bitbucket_server/representation/pull_request.rb index 4bb00d22b0c..c3e927d8de7 100644 --- a/lib/bitbucket_server/representation/pull_request.rb +++ b/lib/bitbucket_server/representation/pull_request.rb @@ -20,9 +20,10 @@ module BitbucketServer end def state - if raw['state'] == 'MERGED' + case raw['state'] + when 'MERGED' 'merged' - elsif raw['state'] == 'DECLINED' + when 'DECLINED' 'closed' else 'opened' -- cgit v1.2.3 From 61f5c2e38c9699f9ce9307997b2b0f4ca44205f6 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Thu, 2 Aug 2018 15:52:11 -0700 Subject: Add a comment when user can't be identified --- lib/gitlab/bitbucket_server_import/importer.rb | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index eb3b76409cd..c67797501c6 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -298,7 +298,15 @@ module Gitlab end def pull_request_comment_attributes(comment) - note = + author = find_user_id(comment.author_email) + note = '' + + unless author + author = project.creator_id + note = "*By #{comment.author_username} (#{comment.author_email}) on #{comment.created_at}*\n\n" + end + + note += # Provide some context for replying if comment.parent_comment "> #{comment.parent_comment.note.truncate(80)}\n\n#{comment.note}" @@ -309,7 +317,7 @@ module Gitlab { project: project, note: note, - author_id: gitlab_user_id(comment.author_email), + author_id: author, created_at: comment.created_at, updated_at: comment.updated_at } -- cgit v1.2.3 From d8ce4f942bd8005a7eb7beb8f5f9e812e5a7e763 Mon Sep 17 00:00:00 2001 From: Stan Hu Date: Fri, 3 Aug 2018 06:07:37 -0700 Subject: Remove date from author line since that is already present in `created_at` --- lib/gitlab/bitbucket_server_import/importer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb index c67797501c6..268d21a77d1 100644 --- a/lib/gitlab/bitbucket_server_import/importer.rb +++ b/lib/gitlab/bitbucket_server_import/importer.rb @@ -303,7 +303,7 @@ module Gitlab unless author author = project.creator_id - note = "*By #{comment.author_username} (#{comment.author_email}) on #{comment.created_at}*\n\n" + note = "*By #{comment.author_username} (#{comment.author_email})*\n\n" end note += -- cgit v1.2.3