From 99ddd1dcbed35b642d7bd8a52cc6e5e5453b9f8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Thu, 15 Dec 2016 17:36:53 +0100 Subject: [PATCH] Modify GithubImport to support Gitea MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The reason is that Gitea plan to be GitHub-compatible so it makes sense to just modify GitHubImport a bit for now, and hopefully we can change it to GitHubishImport once Gitea is 100%-compatible. Signed-off-by: Rémy Coutable --- app/controllers/import/gitea_controller.rb | 2 +- app/helpers/import_helper.rb | 8 +- app/views/import/gitea/status.html.haml | 8 +- app/views/import/github/status.html.haml | 5 +- .../unreleased/22348-gitea-importer.yml | 2 +- lib/gitlab/github_import/base_formatter.rb | 4 + lib/gitlab/github_import/client.rb | 16 +- lib/gitlab/github_import/importer.rb | 80 ++-- .../github_import/issuable_formatter.rb | 60 +++ lib/gitlab/github_import/issue_formatter.rb | 52 +-- .../github_import/milestone_formatter.rb | 12 +- .../github_import/pull_request_formatter.rb | 60 +-- .../import/gitea_controller_spec.rb | 2 +- spec/lib/gitlab/github_import/client_spec.rb | 46 ++- .../lib/gitlab/github_import/importer_spec.rb | 370 +++++++++++------- .../github_import/issuable_formatter_spec.rb | 21 + .../github_import/issue_formatter_spec.rb | 38 +- .../github_import/milestone_formatter_spec.rb | 27 +- .../pull_request_formatter_spec.rb | 34 +- spec/routing/import_routing_spec.rb | 1 - ...hubish_import_controller_shared_context.rb | 0 ...ubish_import_controller_shared_examples.rb | 2 +- 22 files changed, 513 insertions(+), 337 deletions(-) create mode 100644 lib/gitlab/github_import/issuable_formatter.rb create mode 100644 spec/lib/gitlab/github_import/issuable_formatter_spec.rb rename spec/support/{ => controllers}/githubish_import_controller_shared_context.rb (100%) rename spec/support/{ => controllers}/githubish_import_controller_shared_examples.rb (99%) diff --git a/app/controllers/import/gitea_controller.rb b/app/controllers/import/gitea_controller.rb index c82a20be04c..3bc21e62a1e 100644 --- a/app/controllers/import/gitea_controller.rb +++ b/app/controllers/import/gitea_controller.rb @@ -11,7 +11,7 @@ class Import::GiteaController < Import::GithubController end def status - @gitea_root_url = session[:host_url] + @gitea_host_url = session[:host_url] super end diff --git a/app/helpers/import_helper.rb b/app/helpers/import_helper.rb index fb79e2a4eef..f52a0f176e9 100644 --- a/app/helpers/import_helper.rb +++ b/app/helpers/import_helper.rb @@ -8,8 +8,8 @@ module ImportHelper link_to path_with_namespace, github_project_url(path_with_namespace), target: '_blank' end - def gitea_project_link(root_url, path_with_namespace) - link_to path_with_namespace, gitea_project_url(root_url, path_with_namespace), target: '_blank' + def gitea_project_link(path_with_namespace) + link_to path_with_namespace, gitea_project_url(path_with_namespace), target: '_blank' end private @@ -25,7 +25,7 @@ module ImportHelper @github_url = provider.fetch('url', 'https://github.com') if provider end - def gitea_project_url(root_url, path_with_namespace) - "#{root_url}/#{path_with_namespace}" + def gitea_project_url(path_with_namespace) + "#{@gitea_host_url.sub(%r{/+\z}, '')}/#{path_with_namespace}" end end diff --git a/app/views/import/gitea/status.html.haml b/app/views/import/gitea/status.html.haml index 2b25892c0da..a7321632994 100644 --- a/app/views/import/gitea/status.html.haml +++ b/app/views/import/gitea/status.html.haml @@ -1,8 +1,8 @@ -- page_title "Gitea import" +- page_title "Gitea Import" - header_title "Projects", root_path %h3.page-title = custom_icon('go_logo') - Import projects from Gitea + Import Projects from Gitea %p.light Select projects you want to import. @@ -26,7 +26,7 @@ - @already_added_projects.each do |project| %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"} %td - = gitea_project_link(@gitea_root_url, project.import_source) + = gitea_project_link(project.import_source) %td = link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project] %td.job-status @@ -43,7 +43,7 @@ - @repos.each do |repo| %tr{id: "repo_#{repo.id}"} %td - = gitea_project_link(@gitea_root_url, repo.full_name) + = gitea_project_link(repo.full_name) %td.import-target %fieldset.row .input-group diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml index 4c721d40b55..70b18ee217d 100644 --- a/app/views/import/github/status.html.haml +++ b/app/views/import/github/status.html.haml @@ -1,8 +1,7 @@ -- page_title "GitHub import" +- page_title "GitHub Import" - header_title "Projects", root_path %h3.page-title - %i.fa.fa-github - Import projects from GitHub + = icon 'github', text: 'Import Projects from GitHub' %p.light Select projects you want to import. diff --git a/changelogs/unreleased/22348-gitea-importer.yml b/changelogs/unreleased/22348-gitea-importer.yml index ce81a3cfefb..2aeefb0b259 100644 --- a/changelogs/unreleased/22348-gitea-importer.yml +++ b/changelogs/unreleased/22348-gitea-importer.yml @@ -1,4 +1,4 @@ --- title: New Gitea importer -merge_request: 6945 +merge_request: 8116 author: diff --git a/lib/gitlab/github_import/base_formatter.rb b/lib/gitlab/github_import/base_formatter.rb index 6dbae64a9fe..95dba9a327b 100644 --- a/lib/gitlab/github_import/base_formatter.rb +++ b/lib/gitlab/github_import/base_formatter.rb @@ -15,6 +15,10 @@ module Gitlab end end + def url + raw_data.url || '' + end + private def gitlab_user_id(github_id) diff --git a/lib/gitlab/github_import/client.rb b/lib/gitlab/github_import/client.rb index a96e0bc63dd..ba869faa92e 100644 --- a/lib/gitlab/github_import/client.rb +++ b/lib/gitlab/github_import/client.rb @@ -8,7 +8,7 @@ module Gitlab def initialize(access_token, host: nil, api_version: 'v3') @access_token = access_token - @host = host + @host = host.to_s.sub(%r{/+\z}, '') @api_version = api_version if access_token @@ -16,10 +16,6 @@ module Gitlab end end - def api_endpoint - host.present? && api_version.present? ? "#{host}/api/#{api_version}" : github_options[:site] - end - def api @api ||= ::Octokit::Client.new( access_token: access_token, @@ -27,7 +23,7 @@ module Gitlab # If there is no config, we're connecting to github.com and we # should verify ssl. connection_options: { - ssl: { verify: config ? config['verify_ssl'] : false } + ssl: { verify: config ? config['verify_ssl'] : true } } ) end @@ -70,6 +66,14 @@ module Gitlab private + def api_endpoint + if host.present? && api_version.present? + "#{host}/api/#{api_version}" + else + github_options[:site] + end + end + def config Gitlab.config.omniauth.providers.find { |provider| provider.name == "github" } end diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index c32e78cae03..c53cd1a928d 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -3,7 +3,7 @@ module Gitlab class Importer include Gitlab::ShellAdapter - attr_reader :client, :errors, :project, :repo, :repo_url + attr_reader :errors, :project, :repo, :repo_url def initialize(project) @project = project @@ -11,12 +11,27 @@ module Gitlab @repo_url = project.import_url @errors = [] @labels = {} + end + + def client + return @client if defined?(@client) + unless credentials + raise Projects::ImportService::Error, + "Unable to find project import data credentials for project ID: #{@project.id}" + end - if credentials - @client = Client.new(credentials[:user]) - else - raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}" + opts = {} + # Gitea plan to be GitHub compliant + if project.import_type == 'gitea' + uri = URI.parse(project.import_url) + host = "#{uri.scheme}://#{uri.host}:#{uri.port}#{uri.path}".sub(%r{/?[\w-]+/[\w-]+\.git\z}, '') + opts = { + host: host, + api_version: 'v1' + } end + + @client = Client.new(credentials[:user], opts) end def execute @@ -35,7 +50,13 @@ module Gitlab import_comments(:issues) import_comments(:pull_requests) import_wiki - import_releases + + # Gitea doesn't have a Release API yet + # See https://github.com/go-gitea/gitea/issues/330 + unless project.import_type == 'gitea' + import_releases + end + handle_errors true @@ -44,7 +65,9 @@ module Gitlab private def credentials - @credentials ||= project.import_data.credentials if project.import_data + return @credentials if defined?(@credentials) + + @credentials = project.import_data ? project.import_data.credentials : nil end def handle_errors @@ -60,9 +83,10 @@ module Gitlab fetch_resources(:labels, repo, per_page: 100) do |labels| labels.each do |raw| begin - GithubImport::LabelFormatter.new(project, raw).create! + gh_label = LabelFormatter.new(project, raw) + gh_label.create! rescue => e - errors << { type: :label, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message } + errors << { type: :label, url: Gitlab::UrlSanitizer.sanitize(gh_label.url), errors: e.message } end end end @@ -74,9 +98,10 @@ module Gitlab fetch_resources(:milestones, repo, state: :all, per_page: 100) do |milestones| milestones.each do |raw| begin - GithubImport::MilestoneFormatter.new(project, raw).create! + gh_milestone = MilestoneFormatter.new(project, raw) + gh_milestone.create! rescue => e - errors << { type: :milestone, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message } + errors << { type: :milestone, url: Gitlab::UrlSanitizer.sanitize(gh_milestone.url), errors: e.message } end end end @@ -85,7 +110,7 @@ module Gitlab def import_issues fetch_resources(:issues, repo, state: :all, sort: :created, direction: :asc, per_page: 100) do |issues| issues.each do |raw| - gh_issue = GithubImport::IssueFormatter.new(project, raw) + gh_issue = IssueFormatter.new(project, raw) begin issuable = @@ -97,7 +122,7 @@ module Gitlab apply_labels(issuable, raw) rescue => e - errors << { type: :issue, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message } + errors << { type: :issue, url: Gitlab::UrlSanitizer.sanitize(gh_issue.url), errors: e.message } end end end @@ -106,18 +131,23 @@ module Gitlab def import_pull_requests fetch_resources(:pull_requests, repo, state: :all, sort: :created, direction: :asc, per_page: 100) do |pull_requests| pull_requests.each do |raw| - pull_request = GithubImport::PullRequestFormatter.new(project, raw) - next unless pull_request.valid? + gh_pull_request = PullRequestFormatter.new(project, raw) + next unless gh_pull_request.valid? begin - restore_source_branch(pull_request) unless pull_request.source_branch_exists? - restore_target_branch(pull_request) unless pull_request.target_branch_exists? + restore_source_branch(gh_pull_request) unless gh_pull_request.source_branch_exists? + restore_target_branch(gh_pull_request) unless gh_pull_request.target_branch_exists? + + merge_request = gh_pull_request.create! - pull_request.create! + # Gitea doesn't return PR in the Issue API endpoint, so labels must be assigned at this stage + if project.import_type == 'gitea' + apply_labels(merge_request, raw) + end rescue => e - errors << { type: :pull_request, url: Gitlab::UrlSanitizer.sanitize(pull_request.url), errors: e.message } + errors << { type: :pull_request, url: Gitlab::UrlSanitizer.sanitize(gh_pull_request.url), errors: e.message } ensure - clean_up_restored_branches(pull_request) + clean_up_restored_branches(gh_pull_request) end end end @@ -179,7 +209,7 @@ module Gitlab ActiveRecord::Base.no_touching do comments.each do |raw| begin - comment = GithubImport::CommentFormatter.new(project, raw) + comment = CommentFormatter.new(project, raw) # GH does not return info about comment's parent, so we guess it by checking its URL! *_, parent, iid = URI(raw.html_url).path.split('/') issuable_class = parent == 'issues' ? Issue : MergeRequest @@ -198,7 +228,7 @@ module Gitlab last_note_attrs = nil cut_off_index = comments.find_index do |raw| - comment = GithubImport::CommentFormatter.new(project, raw) + comment = CommentFormatter.new(project, raw) comment_attrs = comment.attributes last_note_attrs ||= last_note.slice(*comment_attrs.keys) @@ -214,7 +244,7 @@ module Gitlab def import_wiki unless project.wiki.repository_exists? - wiki = GithubImport::WikiFormatter.new(project) + wiki = WikiFormatter.new(project) gitlab_shell.import_repository(project.repository_storage_path, wiki.path_with_namespace, wiki.import_url) end rescue Gitlab::Shell::Error => e @@ -230,10 +260,10 @@ module Gitlab fetch_resources(:releases, repo, per_page: 100) do |releases| releases.each do |raw| begin - gh_release = GithubImport::ReleaseFormatter.new(project, raw) + gh_release = ReleaseFormatter.new(project, raw) gh_release.create! if gh_release.valid? rescue => e - errors << { type: :release, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message } + errors << { type: :release, url: Gitlab::UrlSanitizer.sanitize(gh_release.url), errors: e.message } end end end diff --git a/lib/gitlab/github_import/issuable_formatter.rb b/lib/gitlab/github_import/issuable_formatter.rb new file mode 100644 index 00000000000..256f360efc7 --- /dev/null +++ b/lib/gitlab/github_import/issuable_formatter.rb @@ -0,0 +1,60 @@ +module Gitlab + module GithubImport + class IssuableFormatter < BaseFormatter + def project_association + raise NotImplementedError + end + + def number + raw_data.number + end + + def find_condition + { iid: number } + end + + private + + def state + raw_data.state == 'closed' ? 'closed' : 'opened' + end + + def assigned? + raw_data.assignee.present? + end + + def assignee_id + if assigned? + gitlab_user_id(raw_data.assignee.id) + end + end + + def author + raw_data.user.login + end + + def author_id + gitlab_author_id || project.creator_id + end + + def body + raw_data.body || "" + end + + def description + if gitlab_author_id + body + else + formatter.author_line(author) + body + end + end + + def milestone + if raw_data.milestone.present? + milestone = MilestoneFormatter.new(project, raw_data.milestone) + project.milestones.find_by(milestone.find_condition) + end + end + end + end +end diff --git a/lib/gitlab/github_import/issue_formatter.rb b/lib/gitlab/github_import/issue_formatter.rb index 21a3dee203b..6f5ac4dac0d 100644 --- a/lib/gitlab/github_import/issue_formatter.rb +++ b/lib/gitlab/github_import/issue_formatter.rb @@ -1,6 +1,6 @@ module Gitlab module GithubImport - class IssueFormatter < BaseFormatter + class IssueFormatter < IssuableFormatter def attributes { iid: number, @@ -24,59 +24,9 @@ module Gitlab :issues end - def find_condition - { iid: number } - end - - def number - raw_data.number - end - def pull_request? raw_data.pull_request.present? end - - private - - def assigned? - raw_data.assignee.present? - end - - def assignee_id - if assigned? - gitlab_user_id(raw_data.assignee.id) - end - end - - def author - raw_data.user.login - end - - def author_id - gitlab_author_id || project.creator_id - end - - def body - raw_data.body || "" - end - - def description - if gitlab_author_id - body - else - formatter.author_line(author) + body - end - end - - def milestone - if raw_data.milestone.present? - project.milestones.find_by(iid: raw_data.milestone.public_send("Gitlab::#{project.import_type.camelize}Import::MilestoneFormatter".constantize.iid_attr)) - end - end - - def state - raw_data.state == 'closed' ? 'closed' : 'opened' - end end end end diff --git a/lib/gitlab/github_import/milestone_formatter.rb b/lib/gitlab/github_import/milestone_formatter.rb index 678d56b830b..fe172b854d5 100644 --- a/lib/gitlab/github_import/milestone_formatter.rb +++ b/lib/gitlab/github_import/milestone_formatter.rb @@ -3,7 +3,7 @@ module Gitlab class MilestoneFormatter < BaseFormatter def attributes { - iid: raw_data.public_send("Gitlab::#{project.import_type.camelize}Import::MilestoneFormatter".constantize.iid_attr), + iid: number, project: project, title: raw_data.title, description: raw_data.description, @@ -19,11 +19,15 @@ module Gitlab end def find_condition - { iid: raw_data.public_send("Gitlab::#{project.import_type.camelize}Import::MilestoneFormatter".constantize.iid_attr) } + { iid: number } end - def self.iid_attr - :number + def number + if project.import_type == 'gitea' + raw_data.id + else + raw_data.number + end end private diff --git a/lib/gitlab/github_import/pull_request_formatter.rb b/lib/gitlab/github_import/pull_request_formatter.rb index ea8768fded7..4ea0200e89b 100644 --- a/lib/gitlab/github_import/pull_request_formatter.rb +++ b/lib/gitlab/github_import/pull_request_formatter.rb @@ -1,6 +1,6 @@ module Gitlab module GithubImport - class PullRequestFormatter < BaseFormatter + class PullRequestFormatter < IssuableFormatter delegate :exists?, :project, :ref, :repo, :sha, to: :source_branch, prefix: true delegate :exists?, :project, :ref, :repo, :sha, to: :target_branch, prefix: true @@ -28,14 +28,6 @@ module Gitlab :merge_requests end - def find_condition - { iid: number } - end - - def number - raw_data.number - end - def valid? source_branch.valid? && target_branch.valid? end @@ -60,57 +52,15 @@ module Gitlab end end - def url - raw_data.url - end - private - def assigned? - raw_data.assignee.present? - end - - def assignee_id - if assigned? - gitlab_user_id(raw_data.assignee.id) - end - end - - def author - raw_data.user.login - end - - def author_id - gitlab_author_id || project.creator_id - end - - def body - raw_data.body || "" - end - - def description - if gitlab_author_id - body + def state + if raw_data.state == 'closed' && raw_data.merged_at.present? + 'merged' else - formatter.author_line(author) + body + super end end - - def milestone - if raw_data.milestone.present? - project.milestones.find_by(iid: raw_data.milestone.public_send("Gitlab::#{project.import_type.camelize}Import::MilestoneFormatter".constantize.iid_attr)) - end - end - - def state - @state ||= if raw_data.state == 'closed' && raw_data.merged_at.present? - 'merged' - elsif raw_data.state == 'closed' - 'closed' - else - 'opened' - end - end end end end diff --git a/spec/controllers/import/gitea_controller_spec.rb b/spec/controllers/import/gitea_controller_spec.rb index 3064d1dd58a..3643386ffbc 100644 --- a/spec/controllers/import/gitea_controller_spec.rb +++ b/spec/controllers/import/gitea_controller_spec.rb @@ -29,7 +29,7 @@ describe Import::GiteaController do before do assign_host_url end - let(:extra_assign_expectations) { { gitea_root_url: host_url } } + let(:extra_assign_expectations) { { gitea_host_url: host_url } } end end diff --git a/spec/lib/gitlab/github_import/client_spec.rb b/spec/lib/gitlab/github_import/client_spec.rb index e829b936343..21f2a9e225b 100644 --- a/spec/lib/gitlab/github_import/client_spec.rb +++ b/spec/lib/gitlab/github_import/client_spec.rb @@ -45,20 +45,46 @@ describe Gitlab::GithubImport::Client, lib: true do end end - context 'when provider does not specity an API endpoint' do - it 'uses GitHub root API endpoint' do - expect(client.api.api_endpoint).to eq 'https://api.github.com/' + describe '#api_endpoint' do + context 'when provider does not specity an API endpoint' do + it 'uses GitHub root API endpoint' do + expect(client.api.api_endpoint).to eq 'https://api.github.com/' + end end - end - context 'when provider specify a custom API endpoint' do - before do - github_provider['args']['client_options']['site'] = 'https://github.company.com/' + context 'when provider specify a custom API endpoint' do + before do + github_provider['args']['client_options']['site'] = 'https://github.company.com/' + end + + it 'uses the custom API endpoint' do + expect(OmniAuth::Strategies::GitHub).not_to receive(:default_options) + expect(client.api.api_endpoint).to eq 'https://github.company.com/' + end + end + + context 'when given a host' do + subject(:client) { described_class.new(token, host: 'https://try.gitea.io/') } + + it 'builds a endpoint with the given host and the default API version' do + expect(client.api.api_endpoint).to eq 'https://try.gitea.io/api/v3/' + end end - it 'uses the custom API endpoint' do - expect(OmniAuth::Strategies::GitHub).not_to receive(:default_options) - expect(client.api.api_endpoint).to eq 'https://github.company.com/' + context 'when given an API version' do + subject(:client) { described_class.new(token, api_version: 'v3') } + + it 'does not use the API version without a host' do + expect(client.api.api_endpoint).to eq 'https://api.github.com/' + end + end + + context 'when given a host and version' do + subject(:client) { described_class.new(token, host: 'https://try.gitea.io/', api_version: 'v3') } + + it 'builds a endpoint with the given options' do + expect(client.api.api_endpoint).to eq 'https://try.gitea.io/api/v3/' + end end end diff --git a/spec/lib/gitlab/github_import/importer_spec.rb b/spec/lib/gitlab/github_import/importer_spec.rb index 9e027839f59..0a03b7353f6 100644 --- a/spec/lib/gitlab/github_import/importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer_spec.rb @@ -1,169 +1,251 @@ require 'spec_helper' describe Gitlab::GithubImport::Importer, lib: true do - describe '#execute' do + shared_examples 'Gitlab::GithubImport::Importer#execute' do + let(:expected_not_called) { [] } + before do - allow(Rails).to receive(:cache).and_return(ActiveSupport::Cache::MemoryStore.new) + allow(project).to receive(:import_data).and_return(double.as_null_object) end - context 'when an error occurs' do - let(:project) { create(:project, import_url: 'https://github.com/octocat/Hello-World.git', wiki_access_level: ProjectFeature::DISABLED) } - let(:octocat) { double(id: 123456, login: 'octocat') } - let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } - let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } - let(:repository) { double(id: 1, fork: false) } - let(:source_sha) { create(:commit, project: project).id } - let(:source_branch) { double(ref: 'feature', repo: repository, sha: source_sha) } - let(:target_sha) { create(:commit, project: project, git_commit: RepoHelpers.another_sample_commit).id } - let(:target_branch) { double(ref: 'master', repo: repository, sha: target_sha) } - - let(:label1) do - double( - name: 'Bug', - color: 'ff0000', - url: 'https://api.github.com/repos/octocat/Hello-World/labels/bug' - ) - end + it 'calls import methods' do + importer = described_class.new(project) - let(:label2) do - double( - name: nil, - color: 'ff0000', - url: 'https://api.github.com/repos/octocat/Hello-World/labels/bug' - ) - end + expected_called = [ + :import_labels, :import_milestones, :import_pull_requests, :import_issues, + :import_wiki, :import_releases, :handle_errors + ] - let(:milestone) do - double( - number: 1347, - state: 'open', - title: '1.0', - description: 'Version 1.0', - due_on: nil, - created_at: created_at, - updated_at: updated_at, - closed_at: nil, - url: 'https://api.github.com/repos/octocat/Hello-World/milestones/1' - ) - end + expected_called -= expected_not_called - let(:issue1) do - double( - number: 1347, - milestone: nil, - state: 'open', - title: 'Found a bug', - body: "I'm having a problem with this.", - assignee: nil, - user: octocat, - comments: 0, - pull_request: nil, - created_at: created_at, - updated_at: updated_at, - closed_at: nil, - url: 'https://api.github.com/repos/octocat/Hello-World/issues/1347', - labels: [double(name: 'Label #1')], - ) - end + aggregate_failures do + expected_called.each do |method_name| + expect(importer).to receive(method_name) + end - let(:issue2) do - double( - number: 1348, - milestone: nil, - state: 'open', - title: nil, - body: "I'm having a problem with this.", - assignee: nil, - user: octocat, - comments: 0, - pull_request: nil, - created_at: created_at, - updated_at: updated_at, - closed_at: nil, - url: 'https://api.github.com/repos/octocat/Hello-World/issues/1348', - labels: [double(name: 'Label #2')], - ) - end + expect(importer).to receive(:import_comments).with(:issues) + expect(importer).to receive(:import_comments).with(:pull_requests) - let(:pull_request) do - double( - number: 1347, - milestone: nil, - state: 'open', - title: 'New feature', - body: 'Please pull these awesome changes', - head: source_branch, - base: target_branch, - assignee: nil, - user: octocat, - created_at: created_at, - updated_at: updated_at, - closed_at: nil, - merged_at: nil, - url: 'https://api.github.com/repos/octocat/Hello-World/pulls/1347', - ) + expected_not_called.each do |method_name| + expect(importer).not_to receive(method_name) + end end - let(:release1) do - double( - tag_name: 'v1.0.0', - name: 'First release', - body: 'Release v1.0.0', - draft: false, - created_at: created_at, - updated_at: updated_at, - url: 'https://api.github.com/repos/octocat/Hello-World/releases/1' - ) - end + importer.execute + end + end - let(:release2) do - double( - tag_name: 'v2.0.0', - name: 'Second release', - body: nil, - draft: false, - created_at: created_at, - updated_at: updated_at, - url: 'https://api.github.com/repos/octocat/Hello-World/releases/2' - ) - end + shared_examples 'Gitlab::GithubImport::Importer#execute an error occurs' do + before do + allow(project).to receive(:import_data).and_return(double.as_null_object) - before do - allow(project).to receive(:import_data).and_return(double.as_null_object) - allow_any_instance_of(Octokit::Client).to receive(:rate_limit!).and_raise(Octokit::NotFound) - allow_any_instance_of(Octokit::Client).to receive(:labels).and_return([label1, label2]) - allow_any_instance_of(Octokit::Client).to receive(:milestones).and_return([milestone, milestone]) - allow_any_instance_of(Octokit::Client).to receive(:issues).and_return([issue1, issue2]) - allow_any_instance_of(Octokit::Client).to receive(:pull_requests).and_return([pull_request, pull_request]) - allow_any_instance_of(Octokit::Client).to receive(:issues_comments).and_return([]) - allow_any_instance_of(Octokit::Client).to receive(:pull_requests_comments).and_return([]) - allow_any_instance_of(Octokit::Client).to receive(:last_response).and_return(double(rels: { next: nil })) - allow_any_instance_of(Octokit::Client).to receive(:releases).and_return([release1, release2]) - allow_any_instance_of(Gitlab::Shell).to receive(:import_repository).and_raise(Gitlab::Shell::Error) - end + allow(Rails).to receive(:cache).and_return(ActiveSupport::Cache::MemoryStore.new) + + allow_any_instance_of(Octokit::Client).to receive(:rate_limit!).and_raise(Octokit::NotFound) + allow_any_instance_of(Gitlab::Shell).to receive(:import_repository).and_raise(Gitlab::Shell::Error) + + allow_any_instance_of(Octokit::Client).to receive(:labels).and_return([label1, label2]) + allow_any_instance_of(Octokit::Client).to receive(:milestones).and_return([milestone, milestone]) + allow_any_instance_of(Octokit::Client).to receive(:issues).and_return([issue1, issue2]) + allow_any_instance_of(Octokit::Client).to receive(:pull_requests).and_return([pull_request, pull_request]) + allow_any_instance_of(Octokit::Client).to receive(:issues_comments).and_return([]) + allow_any_instance_of(Octokit::Client).to receive(:pull_requests_comments).and_return([]) + allow_any_instance_of(Octokit::Client).to receive(:last_response).and_return(double(rels: { next: nil })) + allow_any_instance_of(Octokit::Client).to receive(:releases).and_return([release1, release2]) + end + let(:octocat) { double(id: 123456, login: 'octocat') } + let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } + let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } + let(:label1) do + double( + name: 'Bug', + color: 'ff0000', + url: "#{api_root}/repos/octocat/Hello-World/labels/bug" + ) + end + + let(:label2) do + double( + name: nil, + color: 'ff0000', + url: "#{api_root}/repos/octocat/Hello-World/labels/bug" + ) + end + + let(:milestone) do + double( + id: 1347, # For Gitea + number: 1347, + state: 'open', + title: '1.0', + description: 'Version 1.0', + due_on: nil, + created_at: created_at, + updated_at: updated_at, + closed_at: nil, + url: "#{api_root}/repos/octocat/Hello-World/milestones/1" + ) + end - it 'returns true' do - expect(described_class.new(project).execute).to eq true + let(:issue1) do + double( + number: 1347, + milestone: nil, + state: 'open', + title: 'Found a bug', + body: "I'm having a problem with this.", + assignee: nil, + user: octocat, + comments: 0, + pull_request: nil, + created_at: created_at, + updated_at: updated_at, + closed_at: nil, + url: "#{api_root}/repos/octocat/Hello-World/issues/1347", + labels: [double(name: 'Label #1')] + ) + end + + let(:issue2) do + double( + number: 1348, + milestone: nil, + state: 'open', + title: nil, + body: "I'm having a problem with this.", + assignee: nil, + user: octocat, + comments: 0, + pull_request: nil, + created_at: created_at, + updated_at: updated_at, + closed_at: nil, + url: "#{api_root}/repos/octocat/Hello-World/issues/1348", + labels: [double(name: 'Label #2')] + ) + end + + let(:repository) { double(id: 1, fork: false) } + let(:source_sha) { create(:commit, project: project).id } + let(:source_branch) { double(ref: 'feature', repo: repository, sha: source_sha) } + let(:target_sha) { create(:commit, project: project, git_commit: RepoHelpers.another_sample_commit).id } + let(:target_branch) { double(ref: 'master', repo: repository, sha: target_sha) } + let(:pull_request) do + double( + number: 1347, + milestone: nil, + state: 'open', + title: 'New feature', + body: 'Please pull these awesome changes', + head: source_branch, + base: target_branch, + assignee: nil, + user: octocat, + created_at: created_at, + updated_at: updated_at, + closed_at: nil, + merged_at: nil, + url: "#{api_root}/repos/octocat/Hello-World/pulls/1347", + labels: [double(name: 'Label #2')] + ) + end + + let(:release1) do + double( + tag_name: 'v1.0.0', + name: 'First release', + body: 'Release v1.0.0', + draft: false, + created_at: created_at, + updated_at: updated_at, + url: "#{api_root}/repos/octocat/Hello-World/releases/1" + ) + end + + let(:release2) do + double( + tag_name: 'v2.0.0', + name: 'Second release', + body: nil, + draft: false, + created_at: created_at, + updated_at: updated_at, + url: "#{api_root}/repos/octocat/Hello-World/releases/2" + ) + end + + it 'returns true' do + expect(described_class.new(project).execute).to eq true + end + + it 'does not raise an error' do + expect { described_class.new(project).execute }.not_to raise_error + end + + it 'stores error messages' do + error = { + message: 'The remote data could not be fully imported.', + errors: [ + { type: :label, url: "#{api_root}/repos/octocat/Hello-World/labels/bug", errors: "Validation failed: Title can't be blank, Title is invalid" }, + { type: :issue, url: "#{api_root}/repos/octocat/Hello-World/issues/1348", errors: "Validation failed: Title can't be blank" }, + { type: :wiki, errors: "Gitlab::Shell::Error" } + ] + } + + unless project.import_type == 'gitea' + error[:errors] << { type: :release, url: "#{api_root}/repos/octocat/Hello-World/releases/2", errors: "Validation failed: Description can't be blank" } end - it 'does not raise an error' do - expect { described_class.new(project).execute }.not_to raise_error + described_class.new(project).execute + + expect(project.import_error).to eq error.to_json + end + end + + let(:project) { create(:project, import_url: "#{repo_root}/octocat/Hello-World.git", wiki_access_level: ProjectFeature::DISABLED) } + let(:credentials) { { user: 'joe' } } + + context 'when importing a GitHub project' do + let(:api_root) { 'https://api.github.com' } + let(:repo_root) { 'https://github.com' } + + it_behaves_like 'Gitlab::GithubImport::Importer#execute' + it_behaves_like 'Gitlab::GithubImport::Importer#execute an error occurs' + + describe '#client' do + it 'instantiates a Client' do + allow(project).to receive(:import_data).and_return(double(credentials: credentials)) + expect(Gitlab::GithubImport::Client).to receive(:new).with( + credentials[:user], + {} + ) + + described_class.new(project).client end + end + end - it 'stores error messages' do - error = { - message: 'The remote data could not be fully imported.', - errors: [ - { type: :label, url: "https://api.github.com/repos/octocat/Hello-World/labels/bug", errors: "Validation failed: Title can't be blank, Title is invalid" }, - { type: :issue, url: "https://api.github.com/repos/octocat/Hello-World/issues/1348", errors: "Validation failed: Title can't be blank" }, - { type: :wiki, errors: "Gitlab::Shell::Error" }, - { type: :release, url: 'https://api.github.com/repos/octocat/Hello-World/releases/2', errors: "Validation failed: Description can't be blank" } - ] - } + context 'when importing a Gitea project' do + let(:api_root) { 'https://try.gitea.io/api/v1' } + let(:repo_root) { 'https://try.gitea.io' } + before do + project.update(import_type: 'gitea', import_url: "#{repo_root}/foo/group/project.git") + end - described_class.new(project).execute + it_behaves_like 'Gitlab::GithubImport::Importer#execute' do + let(:expected_not_called) { [:import_releases] } + end + it_behaves_like 'Gitlab::GithubImport::Importer#execute an error occurs' + + describe '#client' do + it 'instantiates a Client' do + allow(project).to receive(:import_data).and_return(double(credentials: credentials)) + expect(Gitlab::GithubImport::Client).to receive(:new).with( + credentials[:user], + { host: "#{repo_root}:443/foo", api_version: 'v1' } + ) - expect(project.import_error).to eq error.to_json + described_class.new(project).client end end end diff --git a/spec/lib/gitlab/github_import/issuable_formatter_spec.rb b/spec/lib/gitlab/github_import/issuable_formatter_spec.rb new file mode 100644 index 00000000000..6bc5f98ed2c --- /dev/null +++ b/spec/lib/gitlab/github_import/issuable_formatter_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe Gitlab::GithubImport::IssuableFormatter, lib: true do + let(:raw_data) do + double(number: 42) + end + let(:project) { double(import_type: 'github') } + let(:issuable_formatter) { described_class.new(project, raw_data) } + + describe '#project_association' do + it { expect { issuable_formatter.project_association }.to raise_error(NotImplementedError) } + end + + describe '#number' do + it { expect(issuable_formatter.number).to eq(42) } + end + + describe '#find_condition' do + it { expect(issuable_formatter.find_condition).to eq({ iid: 42 }) } + end +end diff --git a/spec/lib/gitlab/github_import/issue_formatter_spec.rb b/spec/lib/gitlab/github_import/issue_formatter_spec.rb index 95339e2f128..e31ed9c1fa0 100644 --- a/spec/lib/gitlab/github_import/issue_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/issue_formatter_spec.rb @@ -23,9 +23,9 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do } end - subject(:issue) { described_class.new(project, raw_data)} + subject(:issue) { described_class.new(project, raw_data) } - describe '#attributes' do + shared_examples 'Gitlab::GithubImport::IssueFormatter#attributes' do context 'when issue is open' do let(:raw_data) { double(base_data.merge(state: 'open')) } @@ -83,7 +83,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do end context 'when it has a milestone' do - let(:milestone) { double(number: 45) } + let(:milestone) { double(id: 42, number: 42) } let(:raw_data) { double(base_data.merge(milestone: milestone)) } it 'returns nil when milestone does not exist' do @@ -91,7 +91,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do end it 'returns milestone when it exists' do - milestone = create(:milestone, project: project, iid: 45) + milestone = create(:milestone, project: project, iid: 42) expect(issue.attributes.fetch(:milestone)).to eq milestone end @@ -118,6 +118,28 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do end end + shared_examples 'Gitlab::GithubImport::IssueFormatter#number' do + let(:raw_data) { double(base_data.merge(number: 1347)) } + + it 'returns issue number' do + expect(issue.number).to eq 1347 + end + end + + context 'when importing a GitHub project' do + it_behaves_like 'Gitlab::GithubImport::IssueFormatter#attributes' + it_behaves_like 'Gitlab::GithubImport::IssueFormatter#number' + end + + context 'when importing a Gitea project' do + before do + project.update(import_type: 'gitea') + end + + it_behaves_like 'Gitlab::GithubImport::IssueFormatter#attributes' + it_behaves_like 'Gitlab::GithubImport::IssueFormatter#number' + end + describe '#has_comments?' do context 'when number of comments is greater than zero' do let(:raw_data) { double(base_data.merge(comments: 1)) } @@ -136,14 +158,6 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do end end - describe '#number' do - let(:raw_data) { double(base_data.merge(number: 1347)) } - - it 'returns pull request number' do - expect(issue.number).to eq 1347 - end - end - describe '#pull_request?' do context 'when mention a pull request' do let(:raw_data) { double(base_data.merge(pull_request: double)) } diff --git a/spec/lib/gitlab/github_import/milestone_formatter_spec.rb b/spec/lib/gitlab/github_import/milestone_formatter_spec.rb index 09337c99a07..6d38041c468 100644 --- a/spec/lib/gitlab/github_import/milestone_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/milestone_formatter_spec.rb @@ -6,7 +6,6 @@ describe Gitlab::GithubImport::MilestoneFormatter, lib: true do let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } let(:base_data) do { - number: 1347, state: 'open', title: '1.0', description: 'Version 1.0', @@ -16,12 +15,15 @@ describe Gitlab::GithubImport::MilestoneFormatter, lib: true do closed_at: nil } end + let(:iid_attr) { :number } - subject(:formatter) { described_class.new(project, raw_data)} + subject(:formatter) { described_class.new(project, raw_data) } + + shared_examples 'Gitlab::GithubImport::MilestoneFormatter#attributes' do + let(:data) { base_data.merge(iid_attr => 1347) } - describe '#attributes' do context 'when milestone is open' do - let(:raw_data) { double(base_data.merge(state: 'open')) } + let(:raw_data) { double(data.merge(state: 'open')) } it 'returns formatted attributes' do expected = { @@ -40,7 +42,7 @@ describe Gitlab::GithubImport::MilestoneFormatter, lib: true do end context 'when milestone is closed' do - let(:raw_data) { double(base_data.merge(state: 'closed')) } + let(:raw_data) { double(data.merge(state: 'closed')) } it 'returns formatted attributes' do expected = { @@ -60,7 +62,7 @@ describe Gitlab::GithubImport::MilestoneFormatter, lib: true do context 'when milestone has a due date' do let(:due_date) { DateTime.strptime('2011-01-28T19:01:12Z') } - let(:raw_data) { double(base_data.merge(due_on: due_date)) } + let(:raw_data) { double(data.merge(due_on: due_date)) } it 'returns formatted attributes' do expected = { @@ -78,4 +80,17 @@ describe Gitlab::GithubImport::MilestoneFormatter, lib: true do end end end + + context 'when importing a GitHub project' do + it_behaves_like 'Gitlab::GithubImport::MilestoneFormatter#attributes' + end + + context 'when importing a Gitea project' do + let(:iid_attr) { :id } + before do + project.update(import_type: 'gitea') + end + + it_behaves_like 'Gitlab::GithubImport::MilestoneFormatter#attributes' + end end diff --git a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb index 302f0fc0623..2b3256edcb2 100644 --- a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb @@ -32,9 +32,9 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do } end - subject(:pull_request) { described_class.new(project, raw_data)} + subject(:pull_request) { described_class.new(project, raw_data) } - describe '#attributes' do + shared_examples 'Gitlab::GithubImport::PullRequestFormatter#attributes' do context 'when pull request is open' do let(:raw_data) { double(base_data.merge(state: 'open')) } @@ -149,7 +149,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end context 'when it has a milestone' do - let(:milestone) { double(number: 45) } + let(:milestone) { double(id: 42, number: 42) } let(:raw_data) { double(base_data.merge(milestone: milestone)) } it 'returns nil when milestone does not exist' do @@ -157,22 +157,22 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end it 'returns milestone when it exists' do - milestone = create(:milestone, project: project, iid: 45) + milestone = create(:milestone, project: project, iid: 42) expect(pull_request.attributes.fetch(:milestone)).to eq milestone end end end - describe '#number' do - let(:raw_data) { double(base_data.merge(number: 1347)) } + shared_examples 'Gitlab::GithubImport::PullRequestFormatter#number' do + let(:raw_data) { double(base_data) } it 'returns pull request number' do expect(pull_request.number).to eq 1347 end end - describe '#source_branch_name' do + shared_examples 'Gitlab::GithubImport::PullRequestFormatter#source_branch_name' do context 'when source branch exists' do let(:raw_data) { double(base_data) } @@ -190,7 +190,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end end - describe '#target_branch_name' do + shared_examples 'Gitlab::GithubImport::PullRequestFormatter#target_branch_name' do context 'when source branch exists' do let(:raw_data) { double(base_data) } @@ -208,6 +208,24 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end end + context 'when importing a GitHub project' do + it_behaves_like 'Gitlab::GithubImport::PullRequestFormatter#attributes' + it_behaves_like 'Gitlab::GithubImport::PullRequestFormatter#number' + it_behaves_like 'Gitlab::GithubImport::PullRequestFormatter#source_branch_name' + it_behaves_like 'Gitlab::GithubImport::PullRequestFormatter#target_branch_name' + end + + context 'when importing a Gitea project' do + before do + project.update(import_type: 'gitea') + end + + it_behaves_like 'Gitlab::GithubImport::PullRequestFormatter#attributes' + it_behaves_like 'Gitlab::GithubImport::PullRequestFormatter#number' + it_behaves_like 'Gitlab::GithubImport::PullRequestFormatter#source_branch_name' + it_behaves_like 'Gitlab::GithubImport::PullRequestFormatter#target_branch_name' + end + describe '#valid?' do context 'when source, and target repos are not a fork' do let(:raw_data) { double(base_data) } diff --git a/spec/routing/import_routing_spec.rb b/spec/routing/import_routing_spec.rb index f1234ff470b..78ff9c6e6fd 100644 --- a/spec/routing/import_routing_spec.rb +++ b/spec/routing/import_routing_spec.rb @@ -75,7 +75,6 @@ describe Import::GiteaController, 'routing' do it 'to #personal_access_token' do expect(post('/import/gitea/personal_access_token')).to route_to('import/gitea#personal_access_token') end - end # status_import_gitlab GET /import/gitlab/status(.:format) import/gitlab#status diff --git a/spec/support/githubish_import_controller_shared_context.rb b/spec/support/controllers/githubish_import_controller_shared_context.rb similarity index 100% rename from spec/support/githubish_import_controller_shared_context.rb rename to spec/support/controllers/githubish_import_controller_shared_context.rb diff --git a/spec/support/githubish_import_controller_shared_examples.rb b/spec/support/controllers/githubish_import_controller_shared_examples.rb similarity index 99% rename from spec/support/githubish_import_controller_shared_examples.rb rename to spec/support/controllers/githubish_import_controller_shared_examples.rb index aa2d8aed0bd..e11ab802095 100644 --- a/spec/support/githubish_import_controller_shared_examples.rb +++ b/spec/support/controllers/githubish_import_controller_shared_examples.rb @@ -82,7 +82,7 @@ shared_examples 'a GitHub-ish import controller: GET status' do expect(session[:access_token]).to eq(nil) expect(controller).to redirect_to(new_import_url) - expect(flash[:alert]).to eq("Access denied to your #{Gitlab::ImportSources.options.key(provider.to_s)} account.") + expect(flash[:alert]).to eq("Access denied to your #{Gitlab::ImportSources.title(provider.to_s)} account.") end end -- GitLab