Merge branch '22348-gitea-importer' into 'master'
It adds a brand new importer for Gitea! This is a continuation of !6945 started by @bkc. Gitea aims to be 100% GitHub-compatible but there's a few differences: - Gitea is not an OAuth provider (yet): https://github.com/go-gitea/gitea/issues/27 - This means we cannot map Gitea users given an assignee ID => assignees are not set on imported issues and merge requests - No releases API for now: https://github.com/go-gitea/gitea/issues/330 - API version is `v1` (GitHub is `v3`) - The IID field for milestones is `id` compared to `number` in GitHub. - Issues, PRs, milestones, labels don't have a `url` field (the importer now fallback to `''` in that case) **Known issues:** - Comments are not imported because comments JSON always have a blank `html_url`/`issue_url`/`pull_request_url`, so the IID cannot be extracted and the issuable cannot be found... :( This is tracked in https://github.com/go-gitea/gitea/issues/401, and solved by https://github.com/gogits/gogs/pull/3624 but this needs to be submitted / merged in Gitea. This is noted in the documentation. ## Are there points in the code the reviewer needs to double check? 1. I've made `Import::GiteaController` inherit from `Import::GithubController` since both controllers should be identical in the long-term and their current differences are small. 1. I've added a base `IssuableFormatter` class from which `IssueFormatter` & `PullRequestFormatter` inherit 1. I've added shared examples for GitHub/Gitea importer classes 1. I've made `Gitlab::ImportSources` more robust and tested! 🎄 1. I've added routing specs for import routes! 🎄 Closes #22348 See merge request !8116
This commit is contained in:
commit
367b68a01d
|
@ -25,7 +25,7 @@ class ApplicationController < ActionController::Base
|
|||
protect_from_forgery with: :exception
|
||||
|
||||
helper_method :can?, :current_application_settings
|
||||
helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?, :gitlab_project_import_enabled?
|
||||
helper_method :import_sources_enabled?, :github_import_enabled?, :gitea_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?, :gitlab_project_import_enabled?
|
||||
|
||||
rescue_from Encoding::CompatibilityError do |exception|
|
||||
log_exception(exception)
|
||||
|
@ -245,6 +245,10 @@ class ApplicationController < ActionController::Base
|
|||
current_application_settings.import_sources.include?('github')
|
||||
end
|
||||
|
||||
def gitea_import_enabled?
|
||||
current_application_settings.import_sources.include?('gitea')
|
||||
end
|
||||
|
||||
def github_import_configured?
|
||||
Gitlab::OAuth::Provider.enabled?(:github)
|
||||
end
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
class Import::GiteaController < Import::GithubController
|
||||
def new
|
||||
if session[access_token_key].present? && session[host_key].present?
|
||||
redirect_to status_import_url
|
||||
end
|
||||
end
|
||||
|
||||
def personal_access_token
|
||||
session[host_key] = params[host_key]
|
||||
super
|
||||
end
|
||||
|
||||
def status
|
||||
@gitea_host_url = session[host_key]
|
||||
super
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def host_key
|
||||
:"#{provider}_host_url"
|
||||
end
|
||||
|
||||
# Overriden methods
|
||||
def provider
|
||||
:gitea
|
||||
end
|
||||
|
||||
# Gitea is not yet an OAuth provider
|
||||
# See https://github.com/go-gitea/gitea/issues/27
|
||||
def logged_in_with_provider?
|
||||
false
|
||||
end
|
||||
|
||||
def provider_auth
|
||||
if session[access_token_key].blank? || session[host_key].blank?
|
||||
redirect_to new_import_gitea_url,
|
||||
alert: 'You need to specify both an Access Token and a Host URL.'
|
||||
end
|
||||
end
|
||||
|
||||
def client_options
|
||||
{ host: session[host_key], api_version: 'v1' }
|
||||
end
|
||||
end
|
|
@ -1,39 +1,37 @@
|
|||
class Import::GithubController < Import::BaseController
|
||||
before_action :verify_github_import_enabled
|
||||
before_action :github_auth, only: [:status, :jobs, :create]
|
||||
before_action :verify_import_enabled
|
||||
before_action :provider_auth, only: [:status, :jobs, :create]
|
||||
|
||||
rescue_from Octokit::Unauthorized, with: :github_unauthorized
|
||||
|
||||
helper_method :logged_in_with_github?
|
||||
rescue_from Octokit::Unauthorized, with: :provider_unauthorized
|
||||
|
||||
def new
|
||||
if logged_in_with_github?
|
||||
go_to_github_for_permissions
|
||||
elsif session[:github_access_token]
|
||||
redirect_to status_import_github_url
|
||||
if logged_in_with_provider?
|
||||
go_to_provider_for_permissions
|
||||
elsif session[access_token_key]
|
||||
redirect_to status_import_url
|
||||
end
|
||||
end
|
||||
|
||||
def callback
|
||||
session[:github_access_token] = client.get_token(params[:code])
|
||||
redirect_to status_import_github_url
|
||||
session[access_token_key] = client.get_token(params[:code])
|
||||
redirect_to status_import_url
|
||||
end
|
||||
|
||||
def personal_access_token
|
||||
session[:github_access_token] = params[:personal_access_token]
|
||||
redirect_to status_import_github_url
|
||||
session[access_token_key] = params[:personal_access_token]
|
||||
redirect_to status_import_url
|
||||
end
|
||||
|
||||
def status
|
||||
@repos = client.repos
|
||||
@already_added_projects = current_user.created_projects.where(import_type: "github")
|
||||
@already_added_projects = current_user.created_projects.where(import_type: provider)
|
||||
already_added_projects_names = @already_added_projects.pluck(:import_source)
|
||||
|
||||
@repos.reject!{ |repo| already_added_projects_names.include? repo.full_name }
|
||||
@repos.reject! { |repo| already_added_projects_names.include? repo.full_name }
|
||||
end
|
||||
|
||||
def jobs
|
||||
jobs = current_user.created_projects.where(import_type: "github").to_json(only: [:id, :import_status])
|
||||
jobs = current_user.created_projects.where(import_type: provider).to_json(only: [:id, :import_status])
|
||||
render json: jobs
|
||||
end
|
||||
|
||||
|
@ -44,8 +42,8 @@ class Import::GithubController < Import::BaseController
|
|||
namespace_path = params[:target_namespace].presence || current_user.namespace_path
|
||||
@target_namespace = find_or_create_namespace(namespace_path, current_user.namespace_path)
|
||||
|
||||
if current_user.can?(:create_projects, @target_namespace)
|
||||
@project = Gitlab::GithubImport::ProjectCreator.new(repo, @project_name, @target_namespace, current_user, access_params).execute
|
||||
if can?(current_user, :create_projects, @target_namespace)
|
||||
@project = Gitlab::GithubImport::ProjectCreator.new(repo, @project_name, @target_namespace, current_user, access_params, type: provider).execute
|
||||
else
|
||||
render 'unauthorized'
|
||||
end
|
||||
|
@ -54,34 +52,63 @@ class Import::GithubController < Import::BaseController
|
|||
private
|
||||
|
||||
def client
|
||||
@client ||= Gitlab::GithubImport::Client.new(session[:github_access_token])
|
||||
@client ||= Gitlab::GithubImport::Client.new(session[access_token_key], client_options)
|
||||
end
|
||||
|
||||
def verify_github_import_enabled
|
||||
render_404 unless github_import_enabled?
|
||||
def verify_import_enabled
|
||||
render_404 unless import_enabled?
|
||||
end
|
||||
|
||||
def github_auth
|
||||
if session[:github_access_token].blank?
|
||||
go_to_github_for_permissions
|
||||
end
|
||||
def go_to_provider_for_permissions
|
||||
redirect_to client.authorize_url(callback_import_url)
|
||||
end
|
||||
|
||||
def go_to_github_for_permissions
|
||||
redirect_to client.authorize_url(callback_import_github_url)
|
||||
def import_enabled?
|
||||
__send__("#{provider}_import_enabled?")
|
||||
end
|
||||
|
||||
def github_unauthorized
|
||||
session[:github_access_token] = nil
|
||||
redirect_to new_import_github_url,
|
||||
alert: 'Access denied to your GitHub account.'
|
||||
def new_import_url
|
||||
public_send("new_import_#{provider}_url")
|
||||
end
|
||||
|
||||
def logged_in_with_github?
|
||||
current_user.identities.exists?(provider: 'github')
|
||||
def status_import_url
|
||||
public_send("status_import_#{provider}_url")
|
||||
end
|
||||
|
||||
def callback_import_url
|
||||
public_send("callback_import_#{provider}_url")
|
||||
end
|
||||
|
||||
def provider_unauthorized
|
||||
session[access_token_key] = nil
|
||||
redirect_to new_import_url,
|
||||
alert: "Access denied to your #{Gitlab::ImportSources.title(provider.to_s)} account."
|
||||
end
|
||||
|
||||
def access_token_key
|
||||
:"#{provider}_access_token"
|
||||
end
|
||||
|
||||
def access_params
|
||||
{ github_access_token: session[:github_access_token] }
|
||||
{ github_access_token: session[access_token_key] }
|
||||
end
|
||||
|
||||
# The following methods are overriden in subclasses
|
||||
def provider
|
||||
:github
|
||||
end
|
||||
|
||||
def logged_in_with_provider?
|
||||
current_user.identities.exists?(provider: provider)
|
||||
end
|
||||
|
||||
def provider_auth
|
||||
if session[access_token_key].blank?
|
||||
go_to_provider_for_permissions
|
||||
end
|
||||
end
|
||||
|
||||
def client_options
|
||||
{}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,8 +4,10 @@ module ImportHelper
|
|||
"#{namespace}/#{name}"
|
||||
end
|
||||
|
||||
def github_project_link(path_with_namespace)
|
||||
link_to path_with_namespace, github_project_url(path_with_namespace), target: '_blank'
|
||||
def provider_project_link(provider, path_with_namespace)
|
||||
url = __send__("#{provider}_project_url", path_with_namespace)
|
||||
|
||||
link_to path_with_namespace, url, target: '_blank'
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -20,4 +22,8 @@ module ImportHelper
|
|||
provider = Gitlab.config.omniauth.providers.find { |p| p.name == 'github' }
|
||||
@github_url = provider.fetch('url', 'https://github.com') if provider
|
||||
end
|
||||
|
||||
def gitea_project_url(path_with_namespace)
|
||||
"#{@gitea_host_url.sub(%r{/+\z}, '')}/#{path_with_namespace}"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -533,6 +533,10 @@ class Project < ActiveRecord::Base
|
|||
import_type == 'gitlab_project'
|
||||
end
|
||||
|
||||
def gitea_import?
|
||||
import_type == 'gitea'
|
||||
end
|
||||
|
||||
def check_limit
|
||||
unless creator.can_create_project? or namespace.kind == 'group'
|
||||
projects_limit = creator.projects_limit
|
||||
|
|
|
@ -4,15 +4,6 @@ module Projects
|
|||
|
||||
class Error < StandardError; end
|
||||
|
||||
ALLOWED_TYPES = [
|
||||
'bitbucket',
|
||||
'fogbugz',
|
||||
'gitlab',
|
||||
'github',
|
||||
'google_code',
|
||||
'gitlab_project'
|
||||
]
|
||||
|
||||
def execute
|
||||
add_repository_to_project unless project.gitlab_project_import?
|
||||
|
||||
|
@ -64,14 +55,11 @@ module Projects
|
|||
end
|
||||
|
||||
def has_importer?
|
||||
ALLOWED_TYPES.include?(project.import_type)
|
||||
Gitlab::ImportSources.importer_names.include?(project.import_type)
|
||||
end
|
||||
|
||||
def importer
|
||||
return Gitlab::ImportExport::Importer.new(project) if @project.gitlab_project_import?
|
||||
|
||||
class_name = "Gitlab::#{project.import_type.camelize}Import::Importer"
|
||||
class_name.constantize.new(project)
|
||||
Gitlab::ImportSources.importer(project.import_type).new(project)
|
||||
end
|
||||
|
||||
def unknown_url?
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
- provider = local_assigns.fetch(:provider)
|
||||
- provider_title = Gitlab::ImportSources.title(provider)
|
||||
|
||||
%p.light
|
||||
Select projects you want to import.
|
||||
%hr
|
||||
%p
|
||||
= button_tag class: "btn btn-import btn-success js-import-all" do
|
||||
Import all projects
|
||||
= icon("spinner spin", class: "loading-icon")
|
||||
|
||||
.table-responsive
|
||||
%table.table.import-jobs
|
||||
%colgroup.import-jobs-from-col
|
||||
%colgroup.import-jobs-to-col
|
||||
%colgroup.import-jobs-status-col
|
||||
%thead
|
||||
%tr
|
||||
%th= "From #{provider_title}"
|
||||
%th To GitLab
|
||||
%th Status
|
||||
%tbody
|
||||
- @already_added_projects.each do |project|
|
||||
%tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
|
||||
%td
|
||||
= provider_project_link(provider, project.import_source)
|
||||
%td
|
||||
= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
|
||||
%td.job-status
|
||||
- if project.import_status == 'finished'
|
||||
%span
|
||||
%i.fa.fa-check
|
||||
done
|
||||
- elsif project.import_status == 'started'
|
||||
%i.fa.fa-spinner.fa-spin
|
||||
started
|
||||
- else
|
||||
= project.human_import_status_name
|
||||
|
||||
- @repos.each do |repo|
|
||||
%tr{id: "repo_#{repo.id}"}
|
||||
%td
|
||||
= provider_project_link(provider, repo.full_name)
|
||||
%td.import-target
|
||||
%fieldset.row
|
||||
.input-group
|
||||
.project-path.input-group-btn
|
||||
- if current_user.can_select_namespace?
|
||||
- selected = params[:namespace_id] || :current_user
|
||||
- opts = current_user.can_create_group? ? { extra_group: Group.new(name: repo.owner.login, path: repo.owner.login) } : {}
|
||||
= select_tag :namespace_id, namespaces_options(selected, opts.merge({ display_path: true })), { class: 'select2 js-select-namespace', tabindex: 1 }
|
||||
- else
|
||||
= text_field_tag :path, current_user.namespace_path, class: "input-large form-control", tabindex: 1, disabled: true
|
||||
%span.input-group-addon /
|
||||
= text_field_tag :path, repo.name, class: "input-mini form-control", tabindex: 2, autofocus: true, required: true
|
||||
%td.import-actions.job-status
|
||||
= button_tag class: "btn btn-import js-add-to-import" do
|
||||
Import
|
||||
= icon("spinner spin", class: "loading-icon")
|
||||
|
||||
.js-importer-status{ data: { jobs_import_path: "#{url_for([:jobs, :import, provider])}", import_path: "#{url_for([:import, provider])}" } }
|
|
@ -0,0 +1,23 @@
|
|||
- page_title "Gitea Import"
|
||||
- header_title "Projects", root_path
|
||||
|
||||
%h3.page-title
|
||||
= custom_icon('go_logo')
|
||||
Import Projects from Gitea
|
||||
|
||||
%p
|
||||
To get started, please enter your Gitea Host URL and a
|
||||
= succeed '.' do
|
||||
= link_to 'Personal Access Token', 'https://github.com/gogits/go-gogs-client/wiki#access-token'
|
||||
|
||||
= form_tag personal_access_token_import_gitea_path, class: 'form-horizontal' do
|
||||
.form-group
|
||||
= label_tag :gitea_host_url, 'Gitea Host URL', class: 'control-label'
|
||||
.col-sm-4
|
||||
= text_field_tag :gitea_host_url, nil, placeholder: 'https://try.gitea.io', class: 'form-control'
|
||||
.form-group
|
||||
= label_tag :personal_access_token, 'Personal Access Token', class: 'control-label'
|
||||
.col-sm-4
|
||||
= text_field_tag :personal_access_token, nil, class: 'form-control'
|
||||
.form-actions
|
||||
= submit_tag 'List Your Gitea Repositories', class: 'btn btn-create'
|
|
@ -0,0 +1,7 @@
|
|||
- page_title "Gitea Import"
|
||||
- header_title "Projects", root_path
|
||||
%h3.page-title
|
||||
= custom_icon('go_logo')
|
||||
Import Projects from Gitea
|
||||
|
||||
= render 'import/githubish_status', provider: 'gitea'
|
|
@ -1,64 +1,6 @@
|
|||
- 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.
|
||||
%hr
|
||||
%p
|
||||
= button_tag class: "btn btn-import btn-success js-import-all" do
|
||||
Import all projects
|
||||
= icon("spinner spin", class: "loading-icon")
|
||||
|
||||
.table-responsive
|
||||
%table.table.import-jobs
|
||||
%colgroup.import-jobs-from-col
|
||||
%colgroup.import-jobs-to-col
|
||||
%colgroup.import-jobs-status-col
|
||||
%thead
|
||||
%tr
|
||||
%th From GitHub
|
||||
%th To GitLab
|
||||
%th Status
|
||||
%tbody
|
||||
- @already_added_projects.each do |project|
|
||||
%tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
|
||||
%td
|
||||
= github_project_link(project.import_source)
|
||||
%td
|
||||
= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
|
||||
%td.job-status
|
||||
- if project.import_status == 'finished'
|
||||
%span
|
||||
%i.fa.fa-check
|
||||
done
|
||||
- elsif project.import_status == 'started'
|
||||
%i.fa.fa-spinner.fa-spin
|
||||
started
|
||||
- else
|
||||
= project.human_import_status_name
|
||||
|
||||
- @repos.each do |repo|
|
||||
%tr{id: "repo_#{repo.id}"}
|
||||
%td
|
||||
= github_project_link(repo.full_name)
|
||||
%td.import-target
|
||||
%fieldset.row
|
||||
.input-group
|
||||
.project-path.input-group-btn
|
||||
- if current_user.can_select_namespace?
|
||||
- selected = params[:namespace_id] || :current_user
|
||||
- opts = current_user.can_create_group? ? { extra_group: Group.new(name: repo.owner.login, path: repo.owner.login) } : {}
|
||||
= select_tag :namespace_id, namespaces_options(selected, opts.merge({ display_path: true })), { class: 'select2 js-select-namespace', tabindex: 1 }
|
||||
- else
|
||||
= text_field_tag :path, current_user.namespace_path, class: "input-large form-control", tabindex: 1, disabled: true
|
||||
%span.input-group-addon /
|
||||
= text_field_tag :path, repo.name, class: "input-mini form-control", tabindex: 2, autofocus: true, required: true
|
||||
%td.import-actions.job-status
|
||||
= button_tag class: "btn btn-import js-add-to-import" do
|
||||
Import
|
||||
= icon("spinner spin", class: "loading-icon")
|
||||
|
||||
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_github_path}", import_path: "#{import_github_path}" } }
|
||||
= render 'import/githubish_status', provider: 'github'
|
||||
|
|
|
@ -68,6 +68,11 @@
|
|||
- if fogbugz_import_enabled?
|
||||
= link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do
|
||||
= icon('bug', text: 'Fogbugz')
|
||||
%div
|
||||
- if gitea_import_enabled?
|
||||
= link_to new_import_gitea_url, class: 'btn import_gitea' do
|
||||
= custom_icon('go_logo')
|
||||
Gitea
|
||||
%div
|
||||
- if git_import_enabled?
|
||||
= link_to "#", class: 'btn js-toggle-button import_git' do
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="<%= size %>" height="<%= size %>" viewBox="0 0 16 16"><g fill-rule="evenodd" transform="translate(0 1)"><path d="m14 15.01h1v-8.02c0-3.862-3.134-6.991-7-6.991-3.858 0-7 3.13-7 6.991v8.02h1v-8.02c0-3.306 2.691-5.991 6-5.991 3.314 0 6 2.682 6 5.991v8.02m-10.52-13.354c-.366-.402-.894-.655-1.48-.655-1.105 0-2 .895-2 2 0 .868.552 1.606 1.325 1.883.102-.321.226-.631.371-.93-.403-.129-.695-.507-.695-.953 0-.552.448-1 1-1 .306 0 .58.138.764.354.222-.25.461-.483.717-.699m9.04-.002c.366-.401.893-.653 1.479-.653 1.105 0 2 .895 2 2 0 .867-.552 1.606-1.324 1.883-.101-.321-.225-.632-.37-.931.403-.129.694-.507.694-.952 0-.552-.448-1-1-1-.305 0-.579.137-.762.353-.222-.25-.461-.483-.717-.699"/><path d="m5.726 7.04h1.557v.124c0 .283-.033.534-.1.752-.065.202-.175.391-.33.566-.35.394-.795.591-1.335.591-.527 0-.979-.19-1.355-.571-.376-.382-.564-.841-.564-1.377 0-.547.191-1.01.574-1.391.382-.382.848-.574 1.396-.574.295 0 .57.06.825.181.244.12.484.316.72.586l-.405.388c-.309-.412-.686-.618-1.13-.618-.399 0-.733.138-1 .413-.27.27-.405.609-.405 1.015 0 .42.151.766.452 1.037.282.252.587.378.915.378.28 0 .531-.094.754-.283.223-.19.347-.418.373-.683h-.94v-.535m2.884.061c0-.53.194-.986.583-1.367.387-.381.853-.571 1.396-.571.537 0 .998.192 1.382.576.386.384.578.845.578 1.384 0 .542-.194 1-.581 1.379-.389.379-.858.569-1.408.569-.487 0-.923-.168-1.311-.505-.426-.373-.64-.861-.64-1.465m.574.007c0 .417.14.759.42 1.028.278.269.6.403.964.403.395 0 .729-.137 1-.41.272-.277.408-.613.408-1.01 0-.402-.134-.739-.403-1.01-.267-.273-.597-.41-.991-.41-.392 0-.723.137-.993.41-.27.27-.405.604-.405 1m-.184 3.918c.525.026.812.063.812.063.271.025.324-.096.116-.273 0 0-.775-.813-1.933-.813-1.159 0-1.923.813-1.923.813-.211.174-.153.3.12.273 0 0 .286-.037.81-.063v.477c0 .268.224.5.5.5.268 0 .5-.223.5-.498v-.252.25c0 .268.224.5.5.5.268 0 .5-.223.5-.498v-.478m-1-1.023c.552 0 1-.224 1-.5 0-.276-.448-.5-1-.5-.552 0-1 .224-1 .5 0 .276.448.5 1 .5"/></g></svg>
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: New Gitea importer
|
||||
merge_request: 8116
|
||||
author:
|
|
@ -213,7 +213,7 @@ Settings.gitlab.default_projects_features['builds'] = true if Settin
|
|||
Settings.gitlab.default_projects_features['container_registry'] = true if Settings.gitlab.default_projects_features['container_registry'].nil?
|
||||
Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE)
|
||||
Settings.gitlab['domain_whitelist'] ||= []
|
||||
Settings.gitlab['import_sources'] ||= %w[github bitbucket gitlab google_code fogbugz git gitlab_project]
|
||||
Settings.gitlab['import_sources'] ||= %w[github bitbucket gitlab google_code fogbugz git gitlab_project gitea]
|
||||
Settings.gitlab['trusted_proxies'] ||= []
|
||||
Settings.gitlab['no_todos_messages'] ||= YAML.load_file(Rails.root.join('config', 'no_todos_messages.yml'))
|
||||
|
||||
|
|
|
@ -6,6 +6,12 @@ namespace :import do
|
|||
get :jobs
|
||||
end
|
||||
|
||||
resource :gitea, only: [:create, :new], controller: :gitea do
|
||||
post :personal_access_token
|
||||
get :status
|
||||
get :jobs
|
||||
end
|
||||
|
||||
resource :gitlab, only: [:create], controller: :gitlab do
|
||||
get :status
|
||||
get :callback
|
||||
|
|
|
@ -45,7 +45,7 @@ module Gitlab
|
|||
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
|
||||
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
|
||||
domain_whitelist: Settings.gitlab['domain_whitelist'],
|
||||
import_sources: %w[github bitbucket gitlab google_code fogbugz git gitlab_project],
|
||||
import_sources: %w[gitea github bitbucket gitlab google_code fogbugz git gitlab_project],
|
||||
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
|
||||
max_artifacts_size: Settings.artifacts['max_size'],
|
||||
require_two_factor_authentication: false,
|
||||
|
|
|
@ -15,6 +15,10 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def url
|
||||
raw_data.url || ''
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def gitlab_user_id(github_id)
|
||||
|
|
|
@ -4,10 +4,12 @@ module Gitlab
|
|||
GITHUB_SAFE_REMAINING_REQUESTS = 100
|
||||
GITHUB_SAFE_SLEEP_TIME = 500
|
||||
|
||||
attr_reader :access_token
|
||||
attr_reader :access_token, :host, :api_version
|
||||
|
||||
def initialize(access_token)
|
||||
def initialize(access_token, host: nil, api_version: 'v3')
|
||||
@access_token = access_token
|
||||
@host = host.to_s.sub(%r{/+\z}, '')
|
||||
@api_version = api_version
|
||||
|
||||
if access_token
|
||||
::Octokit.auto_paginate = false
|
||||
|
@ -17,7 +19,7 @@ module Gitlab
|
|||
def api
|
||||
@api ||= ::Octokit::Client.new(
|
||||
access_token: access_token,
|
||||
api_endpoint: github_options[:site],
|
||||
api_endpoint: api_endpoint,
|
||||
# If there is no config, we're connecting to github.com and we
|
||||
# should verify ssl.
|
||||
connection_options: {
|
||||
|
@ -64,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
|
||||
|
|
|
@ -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
|
||||
|
||||
if credentials
|
||||
@client = Client.new(credentials[:user])
|
||||
else
|
||||
raise Projects::ImportService::Error, "Unable to find project import data credentials for project ID: #{@project.id}"
|
||||
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
|
||||
|
||||
opts = {}
|
||||
# Gitea plan to be GitHub compliant
|
||||
if project.gitea_import?
|
||||
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.gitea_import?
|
||||
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
|
||||
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
|
||||
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
|
||||
|
@ -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 = 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?
|
||||
|
||||
pull_request.create!
|
||||
merge_request = gh_pull_request.create!
|
||||
|
||||
# Gitea doesn't return PR in the Issue API endpoint, so labels must be assigned at this stage
|
||||
if project.gitea_import?
|
||||
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
|
||||
|
@ -233,7 +263,7 @@ module Gitlab
|
|||
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
|
||||
|
|
|
@ -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
|
|
@ -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.number)
|
||||
end
|
||||
end
|
||||
|
||||
def state
|
||||
raw_data.state == 'closed' ? 'closed' : 'opened'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,7 +3,7 @@ module Gitlab
|
|||
class MilestoneFormatter < BaseFormatter
|
||||
def attributes
|
||||
{
|
||||
iid: raw_data.number,
|
||||
iid: number,
|
||||
project: project,
|
||||
title: raw_data.title,
|
||||
description: raw_data.description,
|
||||
|
@ -19,7 +19,15 @@ module Gitlab
|
|||
end
|
||||
|
||||
def find_condition
|
||||
{ iid: raw_data.number }
|
||||
{ iid: number }
|
||||
end
|
||||
|
||||
def number
|
||||
if project.gitea_import?
|
||||
raw_data.id
|
||||
else
|
||||
raw_data.number
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
module Gitlab
|
||||
module GithubImport
|
||||
class ProjectCreator
|
||||
attr_reader :repo, :name, :namespace, :current_user, :session_data
|
||||
attr_reader :repo, :name, :namespace, :current_user, :session_data, :type
|
||||
|
||||
def initialize(repo, name, namespace, current_user, session_data)
|
||||
def initialize(repo, name, namespace, current_user, session_data, type: 'github')
|
||||
@repo = repo
|
||||
@name = name
|
||||
@namespace = namespace
|
||||
@current_user = current_user
|
||||
@session_data = session_data
|
||||
@type = type
|
||||
end
|
||||
|
||||
def execute
|
||||
|
@ -19,7 +20,7 @@ module Gitlab
|
|||
description: repo.description,
|
||||
namespace_id: namespace.id,
|
||||
visibility_level: visibility_level,
|
||||
import_type: "github",
|
||||
import_type: type,
|
||||
import_source: repo.full_name,
|
||||
import_url: import_url,
|
||||
skip_wiki: skip_wiki
|
||||
|
@ -29,7 +30,7 @@ module Gitlab
|
|||
private
|
||||
|
||||
def import_url
|
||||
repo.clone_url.sub('https://', "https://#{session_data[:github_access_token]}@")
|
||||
repo.clone_url.sub('://', "://#{session_data[:github_access_token]}@")
|
||||
end
|
||||
|
||||
def visibility_level
|
||||
|
|
|
@ -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,56 +52,14 @@ 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
|
||||
else
|
||||
formatter.author_line(author) + body
|
||||
end
|
||||
end
|
||||
|
||||
def milestone
|
||||
if raw_data.milestone.present?
|
||||
project.milestones.find_by(iid: raw_data.milestone.number)
|
||||
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
|
||||
if raw_data.state == 'closed' && raw_data.merged_at.present?
|
||||
'merged'
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,21 +7,38 @@ module Gitlab
|
|||
module ImportSources
|
||||
extend CurrentSettings
|
||||
|
||||
ImportSource = Struct.new(:name, :title, :importer)
|
||||
|
||||
ImportTable = [
|
||||
ImportSource.new('github', 'GitHub', Gitlab::GithubImport::Importer),
|
||||
ImportSource.new('bitbucket', 'Bitbucket', Gitlab::BitbucketImport::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::GithubImport::Importer)
|
||||
].freeze
|
||||
|
||||
class << self
|
||||
def values
|
||||
options.values
|
||||
def options
|
||||
@options ||= Hash[ImportTable.map { |importer| [importer.title, importer.name] }]
|
||||
end
|
||||
|
||||
def options
|
||||
{
|
||||
'GitHub' => 'github',
|
||||
'Bitbucket' => 'bitbucket',
|
||||
'GitLab.com' => 'gitlab',
|
||||
'Google Code' => 'google_code',
|
||||
'FogBugz' => 'fogbugz',
|
||||
'Repo by URL' => 'git',
|
||||
'GitLab export' => 'gitlab_project'
|
||||
}
|
||||
def values
|
||||
@values ||= ImportTable.map(&:name)
|
||||
end
|
||||
|
||||
def importer_names
|
||||
@importer_names ||= ImportTable.select(&:importer).map(&:name)
|
||||
end
|
||||
|
||||
def importer(name)
|
||||
ImportTable.find { |import_source| import_source.name == name }.importer
|
||||
end
|
||||
|
||||
def title(name)
|
||||
options.key(name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Import::GiteaController do
|
||||
include ImportSpecHelper
|
||||
|
||||
let(:provider) { :gitea }
|
||||
let(:host_url) { 'https://try.gitea.io' }
|
||||
|
||||
include_context 'a GitHub-ish import controller'
|
||||
|
||||
def assign_host_url
|
||||
session[:gitea_host_url] = host_url
|
||||
end
|
||||
|
||||
describe "GET new" do
|
||||
it_behaves_like 'a GitHub-ish import controller: GET new' do
|
||||
before do
|
||||
assign_host_url
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST personal_access_token" do
|
||||
it_behaves_like 'a GitHub-ish import controller: POST personal_access_token'
|
||||
end
|
||||
|
||||
describe "GET status" do
|
||||
it_behaves_like 'a GitHub-ish import controller: GET status' do
|
||||
before do
|
||||
assign_host_url
|
||||
end
|
||||
let(:extra_assign_expectations) { { gitea_host_url: host_url } }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST create' do
|
||||
it_behaves_like 'a GitHub-ish import controller: POST create' do
|
||||
before do
|
||||
assign_host_url
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,35 +3,19 @@ require 'spec_helper'
|
|||
describe Import::GithubController do
|
||||
include ImportSpecHelper
|
||||
|
||||
let(:user) { create(:user) }
|
||||
let(:token) { "asdasd12345" }
|
||||
let(:access_params) { { github_access_token: token } }
|
||||
let(:provider) { :github }
|
||||
|
||||
def assign_session_token
|
||||
session[:github_access_token] = token
|
||||
end
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
allow(controller).to receive(:github_import_enabled?).and_return(true)
|
||||
end
|
||||
include_context 'a GitHub-ish import controller'
|
||||
|
||||
describe "GET new" do
|
||||
it_behaves_like 'a GitHub-ish import controller: GET new'
|
||||
|
||||
it "redirects to GitHub for an access token if logged in with GitHub" do
|
||||
allow(controller).to receive(:logged_in_with_github?).and_return(true)
|
||||
expect(controller).to receive(:go_to_github_for_permissions)
|
||||
allow(controller).to receive(:logged_in_with_provider?).and_return(true)
|
||||
expect(controller).to receive(:go_to_provider_for_permissions)
|
||||
|
||||
get :new
|
||||
end
|
||||
|
||||
it "redirects to status if we already have a token" do
|
||||
assign_session_token
|
||||
allow(controller).to receive(:logged_in_with_github?).and_return(false)
|
||||
|
||||
get :new
|
||||
|
||||
expect(controller).to redirect_to(status_import_github_url)
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET callback" do
|
||||
|
@ -51,196 +35,14 @@ describe Import::GithubController do
|
|||
end
|
||||
|
||||
describe "POST personal_access_token" do
|
||||
it "updates access token" do
|
||||
token = "asdfasdf9876"
|
||||
|
||||
allow_any_instance_of(Gitlab::GithubImport::Client).
|
||||
to receive(:user).and_return(true)
|
||||
|
||||
post :personal_access_token, personal_access_token: token
|
||||
|
||||
expect(session[:github_access_token]).to eq(token)
|
||||
expect(controller).to redirect_to(status_import_github_url)
|
||||
end
|
||||
it_behaves_like 'a GitHub-ish import controller: POST personal_access_token'
|
||||
end
|
||||
|
||||
describe "GET status" do
|
||||
before do
|
||||
@repo = OpenStruct.new(login: 'vim', full_name: 'asd/vim')
|
||||
@org = OpenStruct.new(login: 'company')
|
||||
@org_repo = OpenStruct.new(login: 'company', full_name: 'company/repo')
|
||||
assign_session_token
|
||||
end
|
||||
|
||||
it "assigns variables" do
|
||||
@project = create(:project, import_type: 'github', creator_id: user.id)
|
||||
stub_client(repos: [@repo, @org_repo], orgs: [@org], org_repos: [@org_repo])
|
||||
|
||||
get :status
|
||||
|
||||
expect(assigns(:already_added_projects)).to eq([@project])
|
||||
expect(assigns(:repos)).to eq([@repo, @org_repo])
|
||||
end
|
||||
|
||||
it "does not show already added project" do
|
||||
@project = create(:project, import_type: 'github', creator_id: user.id, import_source: 'asd/vim')
|
||||
stub_client(repos: [@repo], orgs: [])
|
||||
|
||||
get :status
|
||||
|
||||
expect(assigns(:already_added_projects)).to eq([@project])
|
||||
expect(assigns(:repos)).to eq([])
|
||||
end
|
||||
|
||||
it "handles an invalid access token" do
|
||||
allow_any_instance_of(Gitlab::GithubImport::Client).
|
||||
to receive(:repos).and_raise(Octokit::Unauthorized)
|
||||
|
||||
get :status
|
||||
|
||||
expect(session[:github_access_token]).to eq(nil)
|
||||
expect(controller).to redirect_to(new_import_github_url)
|
||||
expect(flash[:alert]).to eq('Access denied to your GitHub account.')
|
||||
end
|
||||
it_behaves_like 'a GitHub-ish import controller: GET status'
|
||||
end
|
||||
|
||||
describe "POST create" do
|
||||
let(:github_username) { user.username }
|
||||
let(:github_user) { OpenStruct.new(login: github_username) }
|
||||
let(:github_repo) do
|
||||
OpenStruct.new(
|
||||
name: 'vim',
|
||||
full_name: "#{github_username}/vim",
|
||||
owner: OpenStruct.new(login: github_username)
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
stub_client(user: github_user, repo: github_repo)
|
||||
assign_session_token
|
||||
end
|
||||
|
||||
context "when the repository owner is the GitHub user" do
|
||||
context "when the GitHub user and GitLab user's usernames match" do
|
||||
it "takes the current user's namespace" do
|
||||
expect(Gitlab::GithubImport::ProjectCreator).
|
||||
to receive(:new).with(github_repo, github_repo.name, user.namespace, user, access_params).
|
||||
and_return(double(execute: true))
|
||||
|
||||
post :create, format: :js
|
||||
end
|
||||
end
|
||||
|
||||
context "when the GitHub user and GitLab user's usernames don't match" do
|
||||
let(:github_username) { "someone_else" }
|
||||
|
||||
it "takes the current user's namespace" do
|
||||
expect(Gitlab::GithubImport::ProjectCreator).
|
||||
to receive(:new).with(github_repo, github_repo.name, user.namespace, user, access_params).
|
||||
and_return(double(execute: true))
|
||||
|
||||
post :create, format: :js
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when the repository owner is not the GitHub user" do
|
||||
let(:other_username) { "someone_else" }
|
||||
|
||||
before do
|
||||
github_repo.owner = OpenStruct.new(login: other_username)
|
||||
assign_session_token
|
||||
end
|
||||
|
||||
context "when a namespace with the GitHub user's username already exists" do
|
||||
let!(:existing_namespace) { create(:namespace, name: other_username, owner: user) }
|
||||
|
||||
context "when the namespace is owned by the GitLab user" do
|
||||
it "takes the existing namespace" do
|
||||
expect(Gitlab::GithubImport::ProjectCreator).
|
||||
to receive(:new).with(github_repo, github_repo.name, existing_namespace, user, access_params).
|
||||
and_return(double(execute: true))
|
||||
|
||||
post :create, format: :js
|
||||
end
|
||||
end
|
||||
|
||||
context "when the namespace is not owned by the GitLab user" do
|
||||
before do
|
||||
existing_namespace.owner = create(:user)
|
||||
existing_namespace.save
|
||||
end
|
||||
|
||||
it "creates a project using user's namespace" do
|
||||
expect(Gitlab::GithubImport::ProjectCreator).
|
||||
to receive(:new).with(github_repo, github_repo.name, user.namespace, user, access_params).
|
||||
and_return(double(execute: true))
|
||||
|
||||
post :create, format: :js
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when a namespace with the GitHub user's username doesn't exist" do
|
||||
context "when current user can create namespaces" do
|
||||
it "creates the namespace" do
|
||||
expect(Gitlab::GithubImport::ProjectCreator).
|
||||
to receive(:new).and_return(double(execute: true))
|
||||
|
||||
expect { post :create, target_namespace: github_repo.name, format: :js }.to change(Namespace, :count).by(1)
|
||||
end
|
||||
|
||||
it "takes the new namespace" do
|
||||
expect(Gitlab::GithubImport::ProjectCreator).
|
||||
to receive(:new).with(github_repo, github_repo.name, an_instance_of(Group), user, access_params).
|
||||
and_return(double(execute: true))
|
||||
|
||||
post :create, target_namespace: github_repo.name, format: :js
|
||||
end
|
||||
end
|
||||
|
||||
context "when current user can't create namespaces" do
|
||||
before do
|
||||
user.update_attribute(:can_create_group, false)
|
||||
end
|
||||
|
||||
it "doesn't create the namespace" do
|
||||
expect(Gitlab::GithubImport::ProjectCreator).
|
||||
to receive(:new).and_return(double(execute: true))
|
||||
|
||||
expect { post :create, format: :js }.not_to change(Namespace, :count)
|
||||
end
|
||||
|
||||
it "takes the current user's namespace" do
|
||||
expect(Gitlab::GithubImport::ProjectCreator).
|
||||
to receive(:new).with(github_repo, github_repo.name, user.namespace, user, access_params).
|
||||
and_return(double(execute: true))
|
||||
|
||||
post :create, format: :js
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'user has chosen a namespace and name for the project' do
|
||||
let(:test_namespace) { create(:namespace, name: 'test_namespace', owner: user) }
|
||||
let(:test_name) { 'test_name' }
|
||||
|
||||
it 'takes the selected namespace and name' do
|
||||
expect(Gitlab::GithubImport::ProjectCreator).
|
||||
to receive(:new).with(github_repo, test_name, test_namespace, user, access_params).
|
||||
and_return(double(execute: true))
|
||||
|
||||
post :create, { target_namespace: test_namespace.name, new_name: test_name, format: :js }
|
||||
end
|
||||
|
||||
it 'takes the selected name and default namespace' do
|
||||
expect(Gitlab::GithubImport::ProjectCreator).
|
||||
to receive(:new).with(github_repo, test_name, user.namespace, user, access_params).
|
||||
and_return(double(execute: true))
|
||||
|
||||
post :create, { new_name: test_name, format: :js }
|
||||
end
|
||||
end
|
||||
end
|
||||
it_behaves_like 'a GitHub-ish import controller: POST create'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -25,24 +25,37 @@ describe ImportHelper do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#github_project_link' do
|
||||
context 'when provider does not specify a custom URL' do
|
||||
it 'uses default GitHub URL' do
|
||||
allow(Gitlab.config.omniauth).to receive(:providers).
|
||||
describe '#provider_project_link' do
|
||||
context 'when provider is "github"' do
|
||||
context 'when provider does not specify a custom URL' do
|
||||
it 'uses default GitHub URL' do
|
||||
allow(Gitlab.config.omniauth).to receive(:providers).
|
||||
and_return([Settingslogic.new('name' => 'github')])
|
||||
|
||||
expect(helper.github_project_link('octocat/Hello-World')).
|
||||
expect(helper.provider_project_link('github', 'octocat/Hello-World')).
|
||||
to include('href="https://github.com/octocat/Hello-World"')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when provider specify a custom URL' do
|
||||
it 'uses custom URL' do
|
||||
allow(Gitlab.config.omniauth).to receive(:providers).
|
||||
and_return([Settingslogic.new('name' => 'github', 'url' => 'https://github.company.com')])
|
||||
|
||||
expect(helper.provider_project_link('github', 'octocat/Hello-World')).
|
||||
to include('href="https://github.company.com/octocat/Hello-World"')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when provider specify a custom URL' do
|
||||
it 'uses custom URL' do
|
||||
allow(Gitlab.config.omniauth).to receive(:providers).
|
||||
and_return([Settingslogic.new('name' => 'github', 'url' => 'https://github.company.com')])
|
||||
context 'when provider is "gitea"' do
|
||||
before do
|
||||
assign(:gitea_host_url, 'https://try.gitea.io/')
|
||||
end
|
||||
|
||||
expect(helper.github_project_link('octocat/Hello-World')).
|
||||
to include('href="https://github.company.com/octocat/Hello-World"')
|
||||
it 'uses given host' do
|
||||
expect(helper.provider_project_link('gitea', 'octocat/Hello-World')).
|
||||
to include('href="https://try.gitea.io/octocat/Hello-World"')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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/'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when provider specify a custom API endpoint' do
|
||||
before do
|
||||
github_provider['args']['client_options']['site'] = 'https://github.company.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
|
||||
|
||||
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 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
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -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) }
|
||||
it 'calls import methods' do
|
||||
importer = described_class.new(project)
|
||||
|
||||
let(:label1) do
|
||||
double(
|
||||
name: 'Bug',
|
||||
color: 'ff0000',
|
||||
url: 'https://api.github.com/repos/octocat/Hello-World/labels/bug'
|
||||
expected_called = [
|
||||
:import_labels, :import_milestones, :import_pull_requests, :import_issues,
|
||||
:import_wiki, :import_releases, :handle_errors
|
||||
]
|
||||
|
||||
expected_called -= expected_not_called
|
||||
|
||||
aggregate_failures do
|
||||
expected_called.each do |method_name|
|
||||
expect(importer).to receive(method_name)
|
||||
end
|
||||
|
||||
expect(importer).to receive(:import_comments).with(:issues)
|
||||
expect(importer).to receive(:import_comments).with(:pull_requests)
|
||||
|
||||
expected_not_called.each do |method_name|
|
||||
expect(importer).not_to receive(method_name)
|
||||
end
|
||||
end
|
||||
|
||||
importer.execute
|
||||
end
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
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.gitea_import?
|
||||
error[:errors] << { type: :release, url: "#{api_root}/repos/octocat/Hello-World/releases/2", errors: "Validation failed: Description can't be blank" }
|
||||
end
|
||||
|
||||
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],
|
||||
{}
|
||||
)
|
||||
end
|
||||
|
||||
let(:label2) do
|
||||
double(
|
||||
name: nil,
|
||||
color: 'ff0000',
|
||||
url: 'https://api.github.com/repos/octocat/Hello-World/labels/bug'
|
||||
described_class.new(project).client
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
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' }
|
||||
)
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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',
|
||||
)
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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: "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" }
|
||||
]
|
||||
}
|
||||
|
||||
described_class.new(project).execute
|
||||
|
||||
expect(project.import_error).to eq error.to_json
|
||||
described_class.new(project).client
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -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
|
|
@ -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)) }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) }
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::ImportSources do
|
||||
describe '.options' do
|
||||
it 'returns a hash' do
|
||||
expected =
|
||||
{
|
||||
'GitHub' => 'github',
|
||||
'Bitbucket' => 'bitbucket',
|
||||
'GitLab.com' => 'gitlab',
|
||||
'Google Code' => 'google_code',
|
||||
'FogBugz' => 'fogbugz',
|
||||
'Repo by URL' => 'git',
|
||||
'GitLab export' => 'gitlab_project',
|
||||
'Gitea' => 'gitea'
|
||||
}
|
||||
|
||||
expect(described_class.options).to eq(expected)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.values' do
|
||||
it 'returns an array' do
|
||||
expected =
|
||||
[
|
||||
'github',
|
||||
'bitbucket',
|
||||
'gitlab',
|
||||
'google_code',
|
||||
'fogbugz',
|
||||
'git',
|
||||
'gitlab_project',
|
||||
'gitea'
|
||||
]
|
||||
|
||||
expect(described_class.values).to eq(expected)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.importer_names' do
|
||||
it 'returns an array of importer names' do
|
||||
expected =
|
||||
[
|
||||
'github',
|
||||
'bitbucket',
|
||||
'gitlab',
|
||||
'google_code',
|
||||
'fogbugz',
|
||||
'gitlab_project',
|
||||
'gitea'
|
||||
]
|
||||
|
||||
expect(described_class.importer_names).to eq(expected)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.importer' do
|
||||
import_sources = {
|
||||
'github' => Gitlab::GithubImport::Importer,
|
||||
'bitbucket' => Gitlab::BitbucketImport::Importer,
|
||||
'gitlab' => Gitlab::GitlabImport::Importer,
|
||||
'google_code' => Gitlab::GoogleCodeImport::Importer,
|
||||
'fogbugz' => Gitlab::FogbugzImport::Importer,
|
||||
'git' => nil,
|
||||
'gitlab_project' => Gitlab::ImportExport::Importer,
|
||||
'gitea' => Gitlab::GithubImport::Importer
|
||||
}
|
||||
|
||||
import_sources.each do |name, klass|
|
||||
it "returns #{klass} when given #{name}" do
|
||||
expect(described_class.importer(name)).to eq(klass)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.title' do
|
||||
import_sources = {
|
||||
'github' => 'GitHub',
|
||||
'bitbucket' => 'Bitbucket',
|
||||
'gitlab' => 'GitLab.com',
|
||||
'google_code' => 'Google Code',
|
||||
'fogbugz' => 'FogBugz',
|
||||
'git' => 'Repo by URL',
|
||||
'gitlab_project' => 'GitLab export',
|
||||
'gitea' => 'Gitea'
|
||||
}
|
||||
|
||||
import_sources.each do |name, title|
|
||||
it "returns #{title} when given #{name}" do
|
||||
expect(described_class.title(name)).to eq(title)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1458,6 +1458,18 @@ describe Project, models: true do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#gitlab_project_import?' do
|
||||
subject(:project) { build(:project, import_type: 'gitlab_project') }
|
||||
|
||||
it { expect(project.gitlab_project_import?).to be true }
|
||||
end
|
||||
|
||||
describe '#gitea_import?' do
|
||||
subject(:project) { build(:project, import_type: 'gitea') }
|
||||
|
||||
it { expect(project.gitea_import?).to be true }
|
||||
end
|
||||
|
||||
describe '#lfs_enabled?' do
|
||||
let(:project) { create(:project) }
|
||||
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
require 'spec_helper'
|
||||
|
||||
# Shared examples for a resource inside a Project
|
||||
#
|
||||
# By default it tests all the default REST actions: index, create, new, edit,
|
||||
# show, update, and destroy. You can remove actions by customizing the
|
||||
# `actions` variable.
|
||||
#
|
||||
# It also expects a `controller` variable to be available which defines both
|
||||
# the path to the resource as well as the controller name.
|
||||
#
|
||||
# Examples
|
||||
#
|
||||
# # Default behavior
|
||||
# it_behaves_like 'RESTful project resources' do
|
||||
# let(:controller) { 'issues' }
|
||||
# end
|
||||
#
|
||||
# # Customizing actions
|
||||
# it_behaves_like 'RESTful project resources' do
|
||||
# let(:actions) { [:index] }
|
||||
# let(:controller) { 'issues' }
|
||||
# end
|
||||
shared_examples 'importer routing' do
|
||||
let(:except_actions) { [] }
|
||||
|
||||
it 'to #create' do
|
||||
expect(post("/import/#{provider}")).to route_to("import/#{provider}#create") unless except_actions.include?(:create)
|
||||
end
|
||||
|
||||
it 'to #new' do
|
||||
expect(get("/import/#{provider}/new")).to route_to("import/#{provider}#new") unless except_actions.include?(:new)
|
||||
end
|
||||
|
||||
it 'to #status' do
|
||||
expect(get("/import/#{provider}/status")).to route_to("import/#{provider}#status") unless except_actions.include?(:status)
|
||||
end
|
||||
|
||||
it 'to #callback' do
|
||||
expect(get("/import/#{provider}/callback")).to route_to("import/#{provider}#callback") unless except_actions.include?(:callback)
|
||||
end
|
||||
|
||||
it 'to #jobs' do
|
||||
expect(get("/import/#{provider}/jobs")).to route_to("import/#{provider}#jobs") unless except_actions.include?(:jobs)
|
||||
end
|
||||
end
|
||||
|
||||
# personal_access_token_import_github POST /import/github/personal_access_token(.:format) import/github#personal_access_token
|
||||
# status_import_github GET /import/github/status(.:format) import/github#status
|
||||
# callback_import_github GET /import/github/callback(.:format) import/github#callback
|
||||
# jobs_import_github GET /import/github/jobs(.:format) import/github#jobs
|
||||
# import_github POST /import/github(.:format) import/github#create
|
||||
# new_import_github GET /import/github/new(.:format) import/github#new
|
||||
describe Import::GithubController, 'routing' do
|
||||
it_behaves_like 'importer routing' do
|
||||
let(:provider) { 'github' }
|
||||
end
|
||||
|
||||
it 'to #personal_access_token' do
|
||||
expect(post('/import/github/personal_access_token')).to route_to('import/github#personal_access_token')
|
||||
end
|
||||
end
|
||||
|
||||
# personal_access_token_import_gitea POST /import/gitea/personal_access_token(.:format) import/gitea#personal_access_token
|
||||
# status_import_gitea GET /import/gitea/status(.:format) import/gitea#status
|
||||
# jobs_import_gitea GET /import/gitea/jobs(.:format) import/gitea#jobs
|
||||
# import_gitea POST /import/gitea(.:format) import/gitea#create
|
||||
# new_import_gitea GET /import/gitea/new(.:format) import/gitea#new
|
||||
describe Import::GiteaController, 'routing' do
|
||||
it_behaves_like 'importer routing' do
|
||||
let(:except_actions) { [:callback] }
|
||||
let(:provider) { 'gitea' }
|
||||
end
|
||||
|
||||
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
|
||||
# callback_import_gitlab GET /import/gitlab/callback(.:format) import/gitlab#callback
|
||||
# jobs_import_gitlab GET /import/gitlab/jobs(.:format) import/gitlab#jobs
|
||||
# import_gitlab POST /import/gitlab(.:format) import/gitlab#create
|
||||
describe Import::GitlabController, 'routing' do
|
||||
it_behaves_like 'importer routing' do
|
||||
let(:except_actions) { [:new] }
|
||||
let(:provider) { 'gitlab' }
|
||||
end
|
||||
end
|
||||
|
||||
# status_import_bitbucket GET /import/bitbucket/status(.:format) import/bitbucket#status
|
||||
# callback_import_bitbucket GET /import/bitbucket/callback(.:format) import/bitbucket#callback
|
||||
# jobs_import_bitbucket GET /import/bitbucket/jobs(.:format) import/bitbucket#jobs
|
||||
# import_bitbucket POST /import/bitbucket(.:format) import/bitbucket#create
|
||||
describe Import::BitbucketController, 'routing' do
|
||||
it_behaves_like 'importer routing' do
|
||||
let(:except_actions) { [:new] }
|
||||
let(:provider) { 'bitbucket' }
|
||||
end
|
||||
end
|
||||
|
||||
# status_import_google_code GET /import/google_code/status(.:format) import/google_code#status
|
||||
# callback_import_google_code POST /import/google_code/callback(.:format) import/google_code#callback
|
||||
# jobs_import_google_code GET /import/google_code/jobs(.:format) import/google_code#jobs
|
||||
# new_user_map_import_google_code GET /import/google_code/user_map(.:format) import/google_code#new_user_map
|
||||
# create_user_map_import_google_code POST /import/google_code/user_map(.:format) import/google_code#create_user_map
|
||||
# import_google_code POST /import/google_code(.:format) import/google_code#create
|
||||
# new_import_google_code GET /import/google_code/new(.:format) import/google_code#new
|
||||
describe Import::GoogleCodeController, 'routing' do
|
||||
it_behaves_like 'importer routing' do
|
||||
let(:except_actions) { [:callback] }
|
||||
let(:provider) { 'google_code' }
|
||||
end
|
||||
|
||||
it 'to #callback' do
|
||||
expect(post("/import/google_code/callback")).to route_to("import/google_code#callback")
|
||||
end
|
||||
|
||||
it 'to #new_user_map' do
|
||||
expect(get('/import/google_code/user_map')).to route_to('import/google_code#new_user_map')
|
||||
end
|
||||
|
||||
it 'to #create_user_map' do
|
||||
expect(post('/import/google_code/user_map')).to route_to('import/google_code#create_user_map')
|
||||
end
|
||||
end
|
||||
|
||||
# status_import_fogbugz GET /import/fogbugz/status(.:format) import/fogbugz#status
|
||||
# callback_import_fogbugz POST /import/fogbugz/callback(.:format) import/fogbugz#callback
|
||||
# jobs_import_fogbugz GET /import/fogbugz/jobs(.:format) import/fogbugz#jobs
|
||||
# new_user_map_import_fogbugz GET /import/fogbugz/user_map(.:format) import/fogbugz#new_user_map
|
||||
# create_user_map_import_fogbugz POST /import/fogbugz/user_map(.:format) import/fogbugz#create_user_map
|
||||
# import_fogbugz POST /import/fogbugz(.:format) import/fogbugz#create
|
||||
# new_import_fogbugz GET /import/fogbugz/new(.:format) import/fogbugz#new
|
||||
describe Import::FogbugzController, 'routing' do
|
||||
it_behaves_like 'importer routing' do
|
||||
let(:except_actions) { [:callback] }
|
||||
let(:provider) { 'fogbugz' }
|
||||
end
|
||||
|
||||
it 'to #callback' do
|
||||
expect(post("/import/fogbugz/callback")).to route_to("import/fogbugz#callback")
|
||||
end
|
||||
|
||||
it 'to #new_user_map' do
|
||||
expect(get('/import/fogbugz/user_map')).to route_to('import/fogbugz#new_user_map')
|
||||
end
|
||||
|
||||
it 'to #create_user_map' do
|
||||
expect(post('/import/fogbugz/user_map')).to route_to('import/fogbugz#create_user_map')
|
||||
end
|
||||
end
|
||||
|
||||
# import_gitlab_project POST /import/gitlab_project(.:format) import/gitlab_projects#create
|
||||
# POST /import/gitlab_project(.:format) import/gitlab_projects#create
|
||||
# new_import_gitlab_project GET /import/gitlab_project/new(.:format) import/gitlab_projects#new
|
||||
describe Import::GitlabProjectsController, 'routing' do
|
||||
it 'to #create' do
|
||||
expect(post('/import/gitlab_project')).to route_to('import/gitlab_projects#create')
|
||||
end
|
||||
|
||||
it 'to #new' do
|
||||
expect(get('/import/gitlab_project/new')).to route_to('import/gitlab_projects#new')
|
||||
end
|
||||
end
|
|
@ -0,0 +1,10 @@
|
|||
shared_context 'a GitHub-ish import controller' do
|
||||
let(:user) { create(:user) }
|
||||
let(:token) { "asdasd12345" }
|
||||
let(:access_params) { { github_access_token: token } }
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
allow(controller).to receive(:"#{provider}_import_enabled?").and_return(true)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,232 @@
|
|||
# Specifications for behavior common to all objects with an email attribute.
|
||||
# Takes a list of email-format attributes and requires:
|
||||
# - subject { "the object with a attribute= setter" }
|
||||
# Note: You have access to `email_value` which is the email address value
|
||||
# being currently tested).
|
||||
|
||||
def assign_session_token(provider)
|
||||
session[:"#{provider}_access_token"] = 'asdasd12345'
|
||||
end
|
||||
|
||||
shared_examples 'a GitHub-ish import controller: POST personal_access_token' do
|
||||
let(:status_import_url) { public_send("status_import_#{provider}_url") }
|
||||
|
||||
it "updates access token" do
|
||||
token = 'asdfasdf9876'
|
||||
|
||||
allow_any_instance_of(Gitlab::GithubImport::Client).
|
||||
to receive(:user).and_return(true)
|
||||
|
||||
post :personal_access_token, personal_access_token: token
|
||||
|
||||
expect(session[:"#{provider}_access_token"]).to eq(token)
|
||||
expect(controller).to redirect_to(status_import_url)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'a GitHub-ish import controller: GET new' do
|
||||
let(:status_import_url) { public_send("status_import_#{provider}_url") }
|
||||
|
||||
it "redirects to status if we already have a token" do
|
||||
assign_session_token(provider)
|
||||
allow(controller).to receive(:logged_in_with_provider?).and_return(false)
|
||||
|
||||
get :new
|
||||
|
||||
expect(controller).to redirect_to(status_import_url)
|
||||
end
|
||||
|
||||
it "renders the :new page if no token is present in session" do
|
||||
get :new
|
||||
|
||||
expect(response).to render_template(:new)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'a GitHub-ish import controller: GET status' do
|
||||
let(:new_import_url) { public_send("new_import_#{provider}_url") }
|
||||
let(:user) { create(:user) }
|
||||
let(:repo) { OpenStruct.new(login: 'vim', full_name: 'asd/vim') }
|
||||
let(:org) { OpenStruct.new(login: 'company') }
|
||||
let(:org_repo) { OpenStruct.new(login: 'company', full_name: 'company/repo') }
|
||||
let(:extra_assign_expectations) { {} }
|
||||
|
||||
before do
|
||||
assign_session_token(provider)
|
||||
end
|
||||
|
||||
it "assigns variables" do
|
||||
project = create(:empty_project, import_type: provider, creator_id: user.id)
|
||||
stub_client(repos: [repo, org_repo], orgs: [org], org_repos: [org_repo])
|
||||
|
||||
get :status
|
||||
|
||||
expect(assigns(:already_added_projects)).to eq([project])
|
||||
expect(assigns(:repos)).to eq([repo, org_repo])
|
||||
extra_assign_expectations.each do |key, value|
|
||||
expect(assigns(key)).to eq(value)
|
||||
end
|
||||
end
|
||||
|
||||
it "does not show already added project" do
|
||||
project = create(:empty_project, import_type: provider, creator_id: user.id, import_source: 'asd/vim')
|
||||
stub_client(repos: [repo], orgs: [])
|
||||
|
||||
get :status
|
||||
|
||||
expect(assigns(:already_added_projects)).to eq([project])
|
||||
expect(assigns(:repos)).to eq([])
|
||||
end
|
||||
|
||||
it "handles an invalid access token" do
|
||||
allow_any_instance_of(Gitlab::GithubImport::Client).
|
||||
to receive(:repos).and_raise(Octokit::Unauthorized)
|
||||
|
||||
get :status
|
||||
|
||||
expect(session[:"#{provider}_access_token"]).to be_nil
|
||||
expect(controller).to redirect_to(new_import_url)
|
||||
expect(flash[:alert]).to eq("Access denied to your #{Gitlab::ImportSources.title(provider.to_s)} account.")
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'a GitHub-ish import controller: POST create' do
|
||||
let(:user) { create(:user) }
|
||||
let(:provider_username) { user.username }
|
||||
let(:provider_user) { OpenStruct.new(login: provider_username) }
|
||||
let(:provider_repo) do
|
||||
OpenStruct.new(
|
||||
name: 'vim',
|
||||
full_name: "#{provider_username}/vim",
|
||||
owner: OpenStruct.new(login: provider_username)
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
stub_client(user: provider_user, repo: provider_repo)
|
||||
assign_session_token(provider)
|
||||
end
|
||||
|
||||
context "when the repository owner is the provider user" do
|
||||
context "when the provider user and GitLab user's usernames match" do
|
||||
it "takes the current user's namespace" do
|
||||
expect(Gitlab::GithubImport::ProjectCreator).
|
||||
to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider).
|
||||
and_return(double(execute: true))
|
||||
|
||||
post :create, format: :js
|
||||
end
|
||||
end
|
||||
|
||||
context "when the provider user and GitLab user's usernames don't match" do
|
||||
let(:provider_username) { "someone_else" }
|
||||
|
||||
it "takes the current user's namespace" do
|
||||
expect(Gitlab::GithubImport::ProjectCreator).
|
||||
to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider).
|
||||
and_return(double(execute: true))
|
||||
|
||||
post :create, format: :js
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when the repository owner is not the provider user" do
|
||||
let(:other_username) { "someone_else" }
|
||||
|
||||
before do
|
||||
provider_repo.owner = OpenStruct.new(login: other_username)
|
||||
assign_session_token(provider)
|
||||
end
|
||||
|
||||
context "when a namespace with the provider user's username already exists" do
|
||||
let!(:existing_namespace) { create(:namespace, name: other_username, owner: user) }
|
||||
|
||||
context "when the namespace is owned by the GitLab user" do
|
||||
it "takes the existing namespace" do
|
||||
expect(Gitlab::GithubImport::ProjectCreator).
|
||||
to receive(:new).with(provider_repo, provider_repo.name, existing_namespace, user, access_params, type: provider).
|
||||
and_return(double(execute: true))
|
||||
|
||||
post :create, format: :js
|
||||
end
|
||||
end
|
||||
|
||||
context "when the namespace is not owned by the GitLab user" do
|
||||
before do
|
||||
existing_namespace.owner = create(:user)
|
||||
existing_namespace.save
|
||||
end
|
||||
|
||||
it "creates a project using user's namespace" do
|
||||
expect(Gitlab::GithubImport::ProjectCreator).
|
||||
to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider).
|
||||
and_return(double(execute: true))
|
||||
|
||||
post :create, format: :js
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when a namespace with the provider user's username doesn't exist" do
|
||||
context "when current user can create namespaces" do
|
||||
it "creates the namespace" do
|
||||
expect(Gitlab::GithubImport::ProjectCreator).
|
||||
to receive(:new).and_return(double(execute: true))
|
||||
|
||||
expect { post :create, target_namespace: provider_repo.name, format: :js }.to change(Namespace, :count).by(1)
|
||||
end
|
||||
|
||||
it "takes the new namespace" do
|
||||
expect(Gitlab::GithubImport::ProjectCreator).
|
||||
to receive(:new).with(provider_repo, provider_repo.name, an_instance_of(Group), user, access_params, type: provider).
|
||||
and_return(double(execute: true))
|
||||
|
||||
post :create, target_namespace: provider_repo.name, format: :js
|
||||
end
|
||||
end
|
||||
|
||||
context "when current user can't create namespaces" do
|
||||
before do
|
||||
user.update_attribute(:can_create_group, false)
|
||||
end
|
||||
|
||||
it "doesn't create the namespace" do
|
||||
expect(Gitlab::GithubImport::ProjectCreator).
|
||||
to receive(:new).and_return(double(execute: true))
|
||||
|
||||
expect { post :create, format: :js }.not_to change(Namespace, :count)
|
||||
end
|
||||
|
||||
it "takes the current user's namespace" do
|
||||
expect(Gitlab::GithubImport::ProjectCreator).
|
||||
to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider).
|
||||
and_return(double(execute: true))
|
||||
|
||||
post :create, format: :js
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'user has chosen a namespace and name for the project' do
|
||||
let(:test_namespace) { create(:namespace, name: 'test_namespace', owner: user) }
|
||||
let(:test_name) { 'test_name' }
|
||||
|
||||
it 'takes the selected namespace and name' do
|
||||
expect(Gitlab::GithubImport::ProjectCreator).
|
||||
to receive(:new).with(provider_repo, test_name, test_namespace, user, access_params, type: provider).
|
||||
and_return(double(execute: true))
|
||||
|
||||
post :create, { target_namespace: test_namespace.name, new_name: test_name, format: :js }
|
||||
end
|
||||
|
||||
it 'takes the selected name and default namespace' do
|
||||
expect(Gitlab::GithubImport::ProjectCreator).
|
||||
to receive(:new).with(provider_repo, test_name, user.namespace, user, access_params, type: provider).
|
||||
and_return(double(execute: true))
|
||||
|
||||
post :create, { new_name: test_name, format: :js }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue