Commit 59ed6e46 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge remote-tracking branch 'ce-com/master' into ce-to-ee

Signed-off-by: default avatarDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
parents 09a68a2e b2700e64
...@@ -363,4 +363,4 @@ gem 'vmstat', '~> 2.3.0' ...@@ -363,4 +363,4 @@ gem 'vmstat', '~> 2.3.0'
gem 'sys-filesystem', '~> 1.1.6' gem 'sys-filesystem', '~> 1.1.6'
# Gitaly GRPC client # Gitaly GRPC client
gem 'gitaly', '~> 0.3.0' gem 'gitaly', '~> 0.5.0'
...@@ -277,7 +277,7 @@ GEM ...@@ -277,7 +277,7 @@ GEM
json json
get_process_mem (0.2.0) get_process_mem (0.2.0)
gherkin-ruby (0.3.2) gherkin-ruby (0.3.2)
gitaly (0.3.0) gitaly (0.5.0)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
grpc (~> 1.0) grpc (~> 1.0)
github-linguist (4.7.6) github-linguist (4.7.6)
...@@ -940,7 +940,7 @@ DEPENDENCIES ...@@ -940,7 +940,7 @@ DEPENDENCIES
fuubar (~> 2.0.0) fuubar (~> 2.0.0)
gemnasium-gitlab-service (~> 0.2) gemnasium-gitlab-service (~> 0.2)
gemojione (~> 3.0) gemojione (~> 3.0)
gitaly (~> 0.3.0) gitaly (~> 0.5.0)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
gitlab-elasticsearch-git (= 1.1.1) gitlab-elasticsearch-git (= 1.1.1)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
......
...@@ -263,7 +263,7 @@ ...@@ -263,7 +263,7 @@
}); });
/** /**
* Updates the search parameter of a URL given the parameter and values provided. * Updates the search parameter of a URL given the parameter and value provided.
* *
* If no search params are present we'll add it. * If no search params are present we'll add it.
* If param for page is already present, we'll update it * If param for page is already present, we'll update it
...@@ -278,17 +278,24 @@ ...@@ -278,17 +278,24 @@
let search; let search;
const locationSearch = window.location.search; const locationSearch = window.location.search;
if (locationSearch.length === 0) { if (locationSearch.length) {
search = `?${param}=${value}`; const parameters = locationSearch.substring(1, locationSearch.length)
} .split('&')
.reduce((acc, element) => {
const val = element.split('=');
acc[val[0]] = decodeURIComponent(val[1]);
return acc;
}, {});
if (locationSearch.indexOf(param) !== -1) { parameters[param] = value;
const regex = new RegExp(param + '=\\d');
search = locationSearch.replace(regex, `${param}=${value}`);
}
if (locationSearch.length && locationSearch.indexOf(param) === -1) { const toString = Object.keys(parameters)
search = `${locationSearch}&${param}=${value}`; .map(val => `${val}=${encodeURIComponent(parameters[val])}`)
.join('&');
search = `?${toString}`;
} else {
search = `?${param}=${value}`;
} }
return search; return search;
......
...@@ -21,6 +21,7 @@ export default { ...@@ -21,6 +21,7 @@ export default {
<li v-for="artifact in artifacts"> <li v-for="artifact in artifacts">
<a <a
rel="nofollow" rel="nofollow"
download
:href="artifact.path"> :href="artifact.path">
<i class="fa fa-download" aria-hidden="true"></i> <i class="fa fa-download" aria-hidden="true"></i>
<span>Download {{artifact.name}} artifacts</span> <span>Download {{artifact.name}} artifacts</span>
......
...@@ -146,6 +146,10 @@ ...@@ -146,6 +146,10 @@
display: block; display: block;
} }
&.scrolling-tabs {
float: left;
}
li a { li a {
padding: 16px 15px 11px; padding: 16px 15px 11px;
} }
...@@ -480,6 +484,10 @@ ...@@ -480,6 +484,10 @@
.inner-page-scroll-tabs { .inner-page-scroll-tabs {
position: relative; position: relative;
.nav-links {
padding-bottom: 1px;
}
.fade-right { .fade-right {
@include fade(left, $white-light); @include fade(left, $white-light);
right: 0; right: 0;
......
class Admin::AbuseReportsController < Admin::ApplicationController class Admin::AbuseReportsController < Admin::ApplicationController
def index def index
@abuse_reports = AbuseReport.order(id: :desc).page(params[:page]) @abuse_reports = AbuseReport.order(id: :desc).page(params[:page])
@abuse_reports.includes(:reporter, :user)
end end
def destroy def destroy
......
...@@ -17,6 +17,7 @@ class Groups::GroupMembersController < Groups::ApplicationController ...@@ -17,6 +17,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
@members = @members.search(params[:search]) if params[:search].present? @members = @members.search(params[:search]) if params[:search].present?
@members = @members.sort(@sort) @members = @members.sort(@sort)
@members = @members.page(params[:page]).per(50) @members = @members.page(params[:page]).per(50)
@members.includes(:user)
@requesters = AccessRequestsFinder.new(@group).execute(current_user) @requesters = AccessRequestsFinder.new(@group).execute(current_user)
......
class Profiles::AccountsController < Profiles::ApplicationController class Profiles::AccountsController < Profiles::ApplicationController
include AuthHelper
def show def show
@user = current_user @user = current_user
end end
def unlink def unlink
provider = params[:provider] provider = params[:provider]
current_user.identities.find_by(provider: provider).destroy unless provider.to_s == 'saml' identity = current_user.identities.find_by(provider: provider)
return render_404 unless identity
if unlink_allowed?(provider)
identity.destroy
else
flash[:alert] = "You are not allowed to unlink your primary login account"
end
redirect_to profile_account_path redirect_to profile_account_path
end end
end end
...@@ -42,6 +42,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -42,6 +42,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@collection_type = "MergeRequest" @collection_type = "MergeRequest"
@merge_requests = merge_requests_collection @merge_requests = merge_requests_collection
@merge_requests = @merge_requests.page(params[:page]) @merge_requests = @merge_requests.page(params[:page])
@merge_requests = @merge_requests.includes(merge_request_diff: :merge_request)
@issuable_meta_data = issuable_meta_data(@merge_requests, @collection_type) @issuable_meta_data = issuable_meta_data(@merge_requests, @collection_type)
if @merge_requests.out_of_range? && @merge_requests.total_pages != 0 if @merge_requests.out_of_range? && @merge_requests.total_pages != 0
......
...@@ -21,9 +21,9 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -21,9 +21,9 @@ class Projects::MilestonesController < Projects::ApplicationController
@sort = params[:sort] || 'due_date_asc' @sort = params[:sort] || 'due_date_asc'
@milestones = @milestones.sort(@sort) @milestones = @milestones.sort(@sort)
@milestones = @milestones.includes(:project)
respond_to do |format| respond_to do |format|
format.html do format.html do
@milestones = @milestones.includes(:project)
@milestones = @milestones.page(params[:page]) @milestones = @milestones.page(params[:page])
end end
format.json do format.json do
......
...@@ -25,12 +25,12 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -25,12 +25,12 @@ class RegistrationsController < Devise::RegistrationsController
end end
def destroy def destroy
Users::DestroyService.new(current_user).execute(current_user) DeleteUserWorker.perform_async(current_user.id, current_user.id)
respond_to do |format| respond_to do |format|
format.html do format.html do
session.try(:destroy) session.try(:destroy)
redirect_to new_user_session_path, notice: "Account successfully removed." redirect_to new_user_session_path, notice: "Account scheduled for removal."
end end
end end
end end
......
...@@ -6,20 +6,16 @@ class SearchController < ApplicationController ...@@ -6,20 +6,16 @@ class SearchController < ApplicationController
layout 'search' layout 'search'
def show def show
if params[:project_id].present? search_service = SearchService.new(current_user, params)
@project = Project.find_by(id: params[:project_id])
@project = nil unless can?(current_user, :download_code, @project)
end
if params[:group_id].present? @project = search_service.project
@group = Group.find_by(id: params[:group_id]) @group = search_service.group
@group = nil unless can?(current_user, :read_group, @group)
end
return if params[:search].blank? return if params[:search].blank?
@search_term = params[:search] @search_term = params[:search]
<<<<<<< HEAD
@scope = params[:scope] @scope = params[:scope]
@show_snippets = params[:snippets].eql? 'true' @show_snippets = params[:snippets].eql? 'true'
...@@ -46,6 +42,12 @@ class SearchController < ApplicationController ...@@ -46,6 +42,12 @@ class SearchController < ApplicationController
end end
@search_objects = @search_results.objects(@scope, params[:page]) @search_objects = @search_results.objects(@scope, params[:page])
=======
@scope = search_service.scope
@show_snippets = search_service.show_snippets?
@search_results = search_service.search_results
@search_objects = search_service.search_objects
>>>>>>> ce-com/master
check_single_commit_result check_single_commit_result
end end
......
...@@ -79,7 +79,16 @@ class SessionsController < Devise::SessionsController ...@@ -79,7 +79,16 @@ class SessionsController < Devise::SessionsController
def store_redirect_path def store_redirect_path
redirect_uri = redirect_uri =
if request.referer.present? && (params['redirect_to_referer'] == 'yes') if request.referer.present? && (params['redirect_to_referer'] == 'yes')
<<<<<<< HEAD
URI(request.referer) URI(request.referer)
=======
referer_uri = URI(request.referer)
if referer_uri.host == Gitlab.config.gitlab.host
referer_uri.request_uri
else
request.fullpath
end
>>>>>>> ce-com/master
else else
URI(request.url) URI(request.url)
end end
......
...@@ -80,5 +80,9 @@ module AuthHelper ...@@ -80,5 +80,9 @@ module AuthHelper
(current_user.otp_grace_period_started_at + current_application_settings.two_factor_grace_period.hours) < Time.current (current_user.otp_grace_period_started_at + current_application_settings.two_factor_grace_period.hours) < Time.current
end end
def unlink_allowed?(provider)
%w(saml cas3).exclude?(provider.to_s)
end
extend self extend self
end end
...@@ -148,10 +148,13 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -148,10 +148,13 @@ class ApplicationSetting < ActiveRecord::Base
presence: true, presence: true,
numericality: { greater_than_or_equal_to: 0 } numericality: { greater_than_or_equal_to: 0 }
<<<<<<< HEAD
validates :minimum_mirror_sync_time, validates :minimum_mirror_sync_time,
presence: true, presence: true,
inclusion: { in: Gitlab::Mirror::SYNC_TIME_OPTIONS.values } inclusion: { in: Gitlab::Mirror::SYNC_TIME_OPTIONS.values }
=======
>>>>>>> ce-com/master
validates_each :restricted_visibility_levels do |record, attr, value| validates_each :restricted_visibility_levels do |record, attr, value|
value&.each do |level| value&.each do |level|
unless Gitlab::VisibilityLevel.options.has_value?(level) unless Gitlab::VisibilityLevel.options.has_value?(level)
...@@ -258,6 +261,7 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -258,6 +261,7 @@ class ApplicationSetting < ActiveRecord::Base
two_factor_grace_period: 48, two_factor_grace_period: 48,
user_default_external: false, user_default_external: false,
polling_interval_multiplier: 1 polling_interval_multiplier: 1
<<<<<<< HEAD
} }
end end
...@@ -269,6 +273,8 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -269,6 +273,8 @@ class ApplicationSetting < ActiveRecord::Base
usage_ping_enabled: true, usage_ping_enabled: true,
minimum_mirror_sync_time: Gitlab::Mirror::FIFTEEN, minimum_mirror_sync_time: Gitlab::Mirror::FIFTEEN,
repository_size_limit: 0 repository_size_limit: 0
=======
>>>>>>> ce-com/master
} }
end end
......
...@@ -210,7 +210,7 @@ module Ci ...@@ -210,7 +210,7 @@ module Ci
end end
def stuck? def stuck?
builds.pending.any?(&:stuck?) builds.pending.includes(:project).any?(&:stuck?)
end end
def retryable? def retryable?
......
...@@ -1065,7 +1065,13 @@ class Repository ...@@ -1065,7 +1065,13 @@ class Repository
end end
def is_ancestor?(ancestor_id, descendant_id) def is_ancestor?(ancestor_id, descendant_id)
merge_base(ancestor_id, descendant_id) == ancestor_id Gitlab::GitalyClient.migrate(:is_ancestor) do |is_enabled|
if is_enabled
raw_repository.is_ancestor?(ancestor_id, descendant_id)
else
merge_base_commit(ancestor_id, descendant_id) == ancestor_id
end
end
end end
def empty_repo? def empty_repo?
......
...@@ -31,5 +31,9 @@ module Search ...@@ -31,5 +31,9 @@ module Search
Gitlab::SearchResults.new(current_user, projects, params[:search]) Gitlab::SearchResults.new(current_user, projects, params[:search])
end end
end end
def scope
@scope ||= %w[issues merge_requests milestones].delete(params[:scope]) { 'projects' }
end
end end
end end
...@@ -21,5 +21,9 @@ module Search ...@@ -21,5 +21,9 @@ module Search
params[:repository_ref]) params[:repository_ref])
end end
end end
def scope
@scope ||= %w[notes issues merge_requests milestones wiki_blobs commits].delete(params[:scope]) { 'blobs' }
end
end end
end end
...@@ -16,5 +16,9 @@ module Search ...@@ -16,5 +16,9 @@ module Search
Gitlab::SnippetSearchResults.new(snippets, params[:search]) Gitlab::SnippetSearchResults.new(snippets, params[:search])
end end
end end
def scope
@scope ||= %w[snippet_titles].delete(params[:scope]) { 'snippet_blobs' }
end
end end
end end
class SearchService
include Gitlab::Allowable
def initialize(current_user, params = {})
@current_user = current_user
@params = params.dup
end
def project
return @project if defined?(@project)
@project =
if params[:project_id].present?
the_project = Project.find_by(id: params[:project_id])
can?(current_user, :download_code, the_project) ? the_project : nil
else
nil
end
end
def group
return @group if defined?(@group)
@group =
if params[:group_id].present?
the_group = Group.find_by(id: params[:group_id])
can?(current_user, :read_group, the_group) ? the_group : nil
else
nil
end
end
def show_snippets?
return @show_snippets if defined?(@show_snippets)
@show_snippets = params[:snippets] == 'true'
end
delegate :scope, to: :search_service
def search_results
@search_results ||= search_service.execute
end
def search_objects
@search_objects ||= search_results.objects(scope, params[:page])
end
private
def search_service
@search_service ||=
if project
Search::ProjectService.new(project, current_user, params)
elsif show_snippets?
Search::SnippetService.new(current_user, params)
else
Search::GlobalService.new(current_user, params)
end
end
attr_reader :current_user, :params
end
...@@ -212,7 +212,7 @@ class TodoService ...@@ -212,7 +212,7 @@ class TodoService
# Only update those that are not really on that state # Only update those that are not really on that state
todos = todos.where.not(state: state) todos = todos.where.not(state: state)
todos_ids = todos.pluck(:id) todos_ids = todos.pluck(:id)
todos.update_all(state: state) todos.unscope(:order).update_all(state: state)
current_user.update_todos_count_cache current_user.update_todos_count_cache
todos_ids todos_ids
end end
......
...@@ -20,10 +20,10 @@ module Users ...@@ -20,10 +20,10 @@ module Users
Groups::DestroyService.new(group, current_user).execute Groups::DestroyService.new(group, current_user).execute
end end
user.personal_projects.each do |project| user.personal_projects.with_deleted.each do |project|
# Skip repository removal because we remove directory with namespace # Skip repository removal because we remove directory with namespace
# that contain all this repositories # that contain all this repositories
::Projects::DestroyService.new(project, current_user, skip_repo: true).async_execute ::Projects::DestroyService.new(project, current_user, skip_repo: true).execute
end end
move_issues_to_ghost_user(user) move_issues_to_ghost_user(user)
......
...@@ -656,6 +656,7 @@ ...@@ -656,6 +656,7 @@
The default value (1) is a reasonable choice for the majority of GitLab The default value (1) is a reasonable choice for the majority of GitLab
installations. Set to 0 to completely disable polling. installations. Set to 0 to completely disable polling.
<<<<<<< HEAD
- if Gitlab::Geo.license_allows? - if Gitlab::Geo.license_allows?
%fieldset %fieldset
%legend GitLab Geo %legend GitLab Geo
...@@ -669,5 +670,7 @@ ...@@ -669,5 +670,7 @@
The amount of seconds after which a request to get a secondary node The amount of seconds after which a request to get a secondary node
status will time out. status will time out.
=======
>>>>>>> ce-com/master
.form-actions .form-actions
= f.submit 'Save', class: 'btn btn-save' = f.submit 'Save', class: 'btn btn-save'
...@@ -35,6 +35,15 @@ ...@@ -35,6 +35,15 @@
%li %li
= link_to admin_root_path, title: 'Admin Area', aria: { label: "Admin Area" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = link_to admin_root_path, title: 'Admin Area', aria: { label: "Admin Area" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('wrench fw') = icon('wrench fw')
- if current_user.can_create_project?
%li
= link_to new_project_path, title: 'New project', aria: { label: "New project" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('plus fw')
- if Gitlab::Sherlock.enabled?
%li
= link_to sherlock_transactions_path, title: 'Sherlock Transactions',
data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('tachometer fw')
%li %li
= link_to assigned_issues_dashboard_path, title: 'Issues', aria: { label: "Issues" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = link_to assigned_issues_dashboard_path, title: 'Issues', aria: { label: "Issues" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('hashtag fw') = icon('hashtag fw')
...@@ -50,6 +59,7 @@ ...@@ -50,6 +59,7 @@
= icon('check-circle fw') = icon('check-circle fw')
%span.badge.todos-count %span.badge.todos-count
= todos_count_format(todos_pending_count) = todos_count_format(todos_pending_count)
<<<<<<< HEAD
- if current_user.can_create_project? - if current_user.can_create_project?
%li %li
= link_to new_project_path, title: 'New project', aria: { label: "New project" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = link_to new_project_path, title: 'New project', aria: { label: "New project" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
...@@ -65,6 +75,8 @@ ...@@ -65,6 +75,8 @@
= link_to sherlock_transactions_path, title: 'Sherlock Transactions', = link_to sherlock_transactions_path, title: 'Sherlock Transactions',
data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('tachometer fw') = icon('tachometer fw')
=======
>>>>>>> ce-com/master
%li.header-user.dropdown %li.header-user.dropdown
= link_to current_user, class: "header-user-dropdown-toggle", data: { toggle: "dropdown" } do = link_to current_user, class: "header-user-dropdown-toggle", data: { toggle: "dropdown" } do
= image_tag avatar_icon(current_user, 26), width: 26, height: 26, class: "header-user-avatar" = image_tag avatar_icon(current_user, 26), width: 26, height: 26, class: "header-user-avatar"
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
Project #{@project.name} was exported successfully. Project #{@project.name} was exported successfully.
%p %p
The project export can be downloaded from: The project export can be downloaded from:
= link_to download_export_namespace_project_url(@project.namespace, @project) do = link_to download_export_namespace_project_url(@project.namespace, @project), rel: 'nofollow', download: '', do
= @project.name_with_namespace + " export" = @project.name_with_namespace + " export"
%p %p
The download link will expire in 24 hours. The download link will expire in 24 hours.
...@@ -75,12 +75,12 @@ ...@@ -75,12 +75,12 @@
.provider-btn-image .provider-btn-image
= provider_image_tag(provider) = provider_image_tag(provider)
- if auth_active?(provider) - if auth_active?(provider)
- if provider.to_s == 'saml' - if unlink_allowed?(provider)
%a.provider-btn
Active
- else
= link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'provider-btn' do = link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'provider-btn' do
Disconnect Disconnect
- else
%a.provider-btn
Active
- else - else
= link_to omniauth_authorize_path(:user, provider), method: :post, class: 'provider-btn not-active' do = link_to omniauth_authorize_path(:user, provider), method: :post, class: 'provider-btn not-active' do
Connect Connect
......
- empty_repo = @project.empty_repo? - empty_repo = @project.empty_repo?
.project-home-panel.text-center{ class: ("empty-project" if empty_repo) } .project-home-panel.text-center{ class: ("empty-project" if empty_repo) }
%div{ class: container_class } .limit-container-width{ class: container_class }
.avatar-container.s70.project-avatar .avatar-container.s70.project-avatar
= project_icon(@project, alt: @project.name, class: 'avatar s70 avatar-tile') = project_icon(@project, alt: @project.name, class: 'avatar s70 avatar-tile')
%h1.project-title %h1.project-title
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
.top-block.row-content-block.clearfix .top-block.row-content-block.clearfix
.pull-right .pull-right
= link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, @build), = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, @build),
class: 'btn btn-default download' do rel: 'nofollow', download: '', class: 'btn btn-default download' do
= icon('download') = icon('download')
Download artifacts archive Download artifacts archive
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
= link_to keep_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default', method: :post do = link_to keep_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default', method: :post do
Keep Keep
= link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default' do = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, @build), rel: 'nofollow', download: '', class: 'btn btn-sm btn-default' do
Download Download
- if @build.artifacts_metadata? - if @build.artifacts_metadata?
......
...@@ -94,7 +94,7 @@ ...@@ -94,7 +94,7 @@
%td %td
.pull-right .pull-right
- if can?(current_user, :read_build, build) && build.artifacts? - if can?(current_user, :read_build, build) && build.artifacts?
= link_to download_namespace_project_build_artifacts_path(build.project.namespace, build.project, build), title: 'Download artifacts', class: 'btn btn-build' do = link_to download_namespace_project_build_artifacts_path(build.project.namespace, build.project, build), rel: 'nofollow', download: '', title: 'Download artifacts', class: 'btn btn-build' do
= icon('download') = icon('download')
- if can?(current_user, :update_build, build) - if can?(current_user, :update_build, build)
- if build.active? - if build.active?
......
...@@ -174,7 +174,7 @@ ...@@ -174,7 +174,7 @@
- if @project.export_project_path - if @project.export_project_path
= link_to 'Download export', download_export_namespace_project_path(@project.namespace, @project), = link_to 'Download export', download_export_namespace_project_path(@project.namespace, @project),
method: :get, class: "btn btn-default" rel: 'nofollow', download: '', method: :get, class: "btn btn-default"
= link_to 'Generate new export', generate_new_export_namespace_project_path(@project.namespace, @project), = link_to 'Generate new export', generate_new_export_namespace_project_path(@project.namespace, @project),
method: :post, class: "btn btn-default" method: :post, class: "btn btn-default"
- else - else
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
= render "home_panel" = render "home_panel"
- if current_user && can?(current_user, :download_code, @project) - if current_user && can?(current_user, :download_code, @project)
%nav.project-stats{ class: container_class } %nav.project-stats.limit-container-width{ class: container_class }
%ul.nav %ul.nav
%li %li
= link_to project_files_path(@project) do = link_to project_files_path(@project) do
...@@ -77,11 +77,11 @@ ...@@ -77,11 +77,11 @@
Set up auto deploy Set up auto deploy
- if @repository.commit - if @repository.commit
%div{ class: container_class } .limit-container-width{ class: container_class }
.project-last-commit .project-last-commit
= render 'projects/last_commit', commit: @repository.commit, ref: current_ref, project: @project = render 'projects/last_commit', commit: @repository.commit, ref: current_ref, project: @project
%div{ class: container_class } .limit-container-width{ class: container_class }
- if @project.archived? - if @project.archived?
.text-warning.center.prepend-top-20 .text-warning.center.prepend-top-20
%p %p
......
---
title: Limit line length for project home page
merge_request:
author:
---
title: Fix symlink icon in project tree
merge_request: 9780
author: mhasbini
---
title: Prevent users from disconnecting GitLab account from CAS
merge_request: 10282
author:
---
title: Fix GitHub Importer for PRs of deleted forked repositories
merge_request: 9992
author:
---
title: Fix redirection after login when the referer have params
merge_request:
author: mhasbini
---
title: Fixes method not replacing URL parameters correctly and breaking pipelines
pagination
merge_request:
author:
---
title: Move issue, mr, todos next to profile dropdown in top nav
merge_request:
author:
---
title: Use Gitaly for Repository#is_ancestor
merge_request: 9864
author:
---
title: Fix race condition where a namespace would be deleted before a project was
deleted
merge_request:
author:
---
title: Relax constraint on Wiki IDs, since subdirectories can contain spaces
merge_request:
author:
---
title: Remove unnecessary ORDER BY clause when updating todos
merge_request:
author: mhasbini
WIKI_SLUG_ID = { id: /\S+/ }.freeze unless defined? WIKI_SLUG_ID
scope(controller: :wikis) do scope(controller: :wikis) do
scope(path: 'wikis', as: :wikis) do scope(path: 'wikis', as: :wikis) do
get :git_access get :git_access
...@@ -8,7 +6,7 @@ scope(controller: :wikis) do ...@@ -8,7 +6,7 @@ scope(controller: :wikis) do
post '/', to: 'wikis#create' post '/', to: 'wikis#create'
end end
scope(path: 'wikis/*id', as: :wiki, constraints: WIKI_SLUG_ID, format: false) do scope(path: 'wikis/*id', as: :wiki, format: false) do
get :edit get :edit
get :history get :history
post :preview_markdown post :preview_markdown
......
...@@ -122,6 +122,7 @@ ActiveRecord::Schema.define(version: 20170329124448) do ...@@ -122,6 +122,7 @@ ActiveRecord::Schema.define(version: 20170329124448) do
t.integer "unique_ips_limit_per_user" t.integer "unique_ips_limit_per_user"
t.integer "unique_ips_limit_time_window" t.integer "unique_ips_limit_time_window"
t.boolean "unique_ips_limit_enabled", default: false, null: false t.boolean "unique_ips_limit_enabled", default: false, null: false
<<<<<<< HEAD
t.integer "minimum_mirror_sync_time", default: 15, null: false t.integer "minimum_mirror_sync_time", default: 15, null: false
t.string "default_artifacts_expire_in", default: "0", null: false t.string "default_artifacts_expire_in", default: "0", null: false
t.string "elasticsearch_url", default: "http://localhost:9200" t.string "elasticsearch_url", default: "http://localhost:9200"
...@@ -157,6 +158,9 @@ ActiveRecord::Schema.define(version: 20170329124448) do ...@@ -157,6 +158,9 @@ ActiveRecord::Schema.define(version: 20170329124448) do
t.integer "user_id", null: false t.integer "user_id", null: false
t.datetime "created_at" t.datetime "created_at"
t.datetime "updated_at" t.datetime "updated_at"
=======
t.decimal "polling_interval_multiplier", default: 1.0, null: false
>>>>>>> ce-com/master
end end
add_index "approvers", ["target_id", "target_type"], name: "index_approvers_on_target_id_and_target_type", using: :btree add_index "approvers", ["target_id", "target_type"], name: "index_approvers_on_target_id_and_target_type", using: :btree
......
...@@ -352,7 +352,7 @@ Example values: ...@@ -352,7 +352,7 @@ Example values:
export CI_JOB_ID="50" export CI_JOB_ID="50"
export CI_COMMIT_SHA="1ecfd275763eff1d6b4844ea3168962458c9f27a" export CI_COMMIT_SHA="1ecfd275763eff1d6b4844ea3168962458c9f27a"
export CI_COMMIT_REF_NAME="master" export CI_COMMIT_REF_NAME="master"
export CI_REPOSITORY_URL="https://gitab-ci-token:abcde-1234ABCD5678ef@example.com/gitlab-org/gitlab-ce.git" export CI_REPOSITORY_URL="https://gitlab-ci-token:abcde-1234ABCD5678ef@example.com/gitlab-org/gitlab-ce.git"
export CI_COMMIT_TAG="1.0.0" export CI_COMMIT_TAG="1.0.0"
export CI_JOB_NAME="spec:other" export CI_JOB_NAME="spec:other"
export CI_JOB_STAGE="test" export CI_JOB_STAGE="test"
......
...@@ -120,6 +120,14 @@ There are new configuration options available for [`gitlab.yml`][yaml]. View the ...@@ -120,6 +120,14 @@ There are new configuration options available for [`gitlab.yml`][yaml]. View the
git diff origin/8-2-stable:config/gitlab.yml.example origin/8-3-stable:config/gitlab.yml.example git diff origin/8-2-stable:config/gitlab.yml.example origin/8-3-stable:config/gitlab.yml.example
``` ```
#### GitLab default file
The value of the `gitlab_workhorse_options` variable should be updated within the default gitlab file (`/etc/default/gitlab`) according to the following diff:
```sh
git diff origin/8-2-stable:lib/support/init.d/gitlab.default.example origin/8-3-stable:lib/support/init.d/gitlab.default.example
```
#### Nginx configuration #### Nginx configuration
GitLab 8.3 introduces major changes in the NGINX configuration. GitLab 8.3 introduces major changes in the NGINX configuration.
......
...@@ -3,6 +3,7 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps ...@@ -3,6 +3,7 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps
include SharedPaths include SharedPaths
include SharedProject include SharedProject
include SharedUser include SharedUser
include WaitForAjax
step '"John Doe" is a developer of project "Shop"' do step '"John Doe" is a developer of project "Shop"' do
project.team << [john_doe, :developer] project.team << [john_doe, :developer]
...@@ -138,6 +139,8 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps ...@@ -138,6 +139,8 @@ class Spinach::Features::DashboardTodos < Spinach::FeatureSteps
step 'I should be directed to the corresponding page' do step 'I should be directed to the corresponding page' do
page.should have_css('.identifier', text: 'Merge Request !1') page.should have_css('.identifier', text: 'Merge Request !1')
# Merge request page loads and issues a number of Ajax requests
wait_for_ajax
end end
def should_see_todo(position, title, body, state: :pending) def should_see_todo(position, title, body, state: :pending)
......
...@@ -230,7 +230,7 @@ module API ...@@ -230,7 +230,7 @@ module API
expose :id, :name, :type, :path expose :id, :name, :type, :path
expose :mode do |obj, options| expose :mode do |obj, options|
filemode = obj.mode.to_s(8) filemode = obj.mode
filemode = "0" + filemode if filemode.length < 6 filemode = "0" + filemode if filemode.length < 6
filemode filemode
end end
......
...@@ -109,6 +109,7 @@ module API ...@@ -109,6 +109,7 @@ module API
requires :housekeeping_full_repack_period, type: Integer, desc: "Number of Git pushes after which a full 'git repack' is run." requires :housekeeping_full_repack_period, type: Integer, desc: "Number of Git pushes after which a full 'git repack' is run."
requires :housekeeping_gc_period, type: Integer, desc: "Number of Git pushes after which 'git gc' is run." requires :housekeeping_gc_period, type: Integer, desc: "Number of Git pushes after which 'git gc' is run."
end end
<<<<<<< HEAD
optional :polling_interval_multiplier, type: BigDecimal, desc: 'Interval multiplier used by endpoints that perform polling. Set to 0 to disable polling.' optional :polling_interval_multiplier, type: BigDecimal, desc: 'Interval multiplier used by endpoints that perform polling. Set to 0 to disable polling.'
# GitLab-EE specific settings # GitLab-EE specific settings
optional :help_text, type: String, desc: 'GitLab server administrator information' optional :help_text, type: String, desc: 'GitLab server administrator information'
...@@ -126,6 +127,10 @@ module API ...@@ -126,6 +127,10 @@ module API
optional :usage_ping_enabled, type: Boolean, desc: 'Every week GitLab will report license usage back to GitLab, Inc.' optional :usage_ping_enabled, type: Boolean, desc: 'Every week GitLab will report license usage back to GitLab, Inc.'
optional :repository_storages, type: Array[String], desc: 'A list of names of enabled storage paths, taken from `gitlab.yml`. New projects will be created in one of these stores, chosen at random.' optional :repository_storages, type: Array[String], desc: 'A list of names of enabled storage paths, taken from `gitlab.yml`. New projects will be created in one of these stores, chosen at random.'
optional :repository_size_limit, type: Integer, desc: 'Size limit per repository (MB)' optional :repository_size_limit, type: Integer, desc: 'Size limit per repository (MB)'
=======
optional :terminal_max_session_time, type: Integer, desc: 'Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time.'
optional :polling_interval_multiplier, type: BigDecimal, desc: 'Interval multiplier used by endpoints that perform polling. Set to 0 to disable polling.'
>>>>>>> ce-com/master
at_least_one_of :default_branch_protection, :default_project_visibility, :default_snippet_visibility, at_least_one_of :default_branch_protection, :default_project_visibility, :default_snippet_visibility,
:default_group_visibility, :restricted_visibility_levels, :import_sources, :default_group_visibility, :restricted_visibility_levels, :import_sources,
:enabled_git_access_protocol, :gravatar_enabled, :default_projects_limit, :enabled_git_access_protocol, :gravatar_enabled, :default_projects_limit,
...@@ -141,10 +146,14 @@ module API ...@@ -141,10 +146,14 @@ module API
:akismet_enabled, :admin_notification_email, :sentry_enabled, :akismet_enabled, :admin_notification_email, :sentry_enabled,
:repository_checks_enabled, :koding_enabled, :housekeeping_enabled, :terminal_max_session_time, :plantuml_enabled, :repository_checks_enabled, :koding_enabled, :housekeeping_enabled, :terminal_max_session_time, :plantuml_enabled,
:version_check_enabled, :email_author_in_body, :html_emails_enabled, :version_check_enabled, :email_author_in_body, :html_emails_enabled,
<<<<<<< HEAD
:polling_interval_multiplier, :polling_interval_multiplier,
# GitLab-EE specific settings # GitLab-EE specific settings
:help_text, :elasticsearch_indexing, :usage_ping_enabled, :help_text, :elasticsearch_indexing, :usage_ping_enabled,
:repository_storages, :repository_size_limit :repository_storages, :repository_size_limit
=======
:housekeeping_enabled, :terminal_max_session_time, :polling_interval_multiplier
>>>>>>> ce-com/master
end end
put "application/settings" do put "application/settings" do
attrs = declared_params(include_missing: false) attrs = declared_params(include_missing: false)
......
...@@ -295,7 +295,7 @@ module API ...@@ -295,7 +295,7 @@ module API
user = User.find_by(id: params[:id]) user = User.find_by(id: params[:id])
not_found!('User') unless user not_found!('User') unless user
::Users::DestroyService.new(current_user).execute(user) DeleteUserWorker.perform_async(current_user.id, user.id)
end end
desc 'Block a user. Available only for admins.' desc 'Block a user. Available only for admins.'
......
...@@ -411,6 +411,11 @@ module Gitlab ...@@ -411,6 +411,11 @@ module Gitlab
rugged.merge_base(from, to) rugged.merge_base(from, to)
end end
# Returns true is +from+ is direct ancestor to +to+, otherwise false
def is_ancestor?(from, to)
Gitlab::GitalyClient::Commit.is_ancestor(self, from, to)
end
# Return an array of Diff objects that represent the diff # Return an array of Diff objects that represent the diff
# between +from+ and +to+. See Diff::filter_diff_options for the allowed # between +from+ and +to+. See Diff::filter_diff_options for the allowed
# diff options. The +options+ hash can also include :break_rewrites to # diff options. The +options+ hash can also include :break_rewrites to
......
...@@ -33,7 +33,7 @@ module Gitlab ...@@ -33,7 +33,7 @@ module Gitlab
root_id: root_tree.oid, root_id: root_tree.oid,
name: entry[:name], name: entry[:name],
type: entry[:type], type: entry[:type],
mode: entry[:filemode], mode: entry[:filemode].to_s(8),
path: path ? File.join(path, entry[:name]) : entry[:name], path: path ? File.join(path, entry[:name]) : entry[:name],
commit_id: sha, commit_id: sha,
) )
......
...@@ -21,6 +21,20 @@ module Gitlab ...@@ -21,6 +21,20 @@ module Gitlab
Gitlab::Git::DiffCollection.new(stub.commit_diff(request), options) Gitlab::Git::DiffCollection.new(stub.commit_diff(request), options)
end end
def is_ancestor(repository, ancestor_id, child_id)
project = Project.find_by_path(repository.path)
channel = GitalyClient.get_channel(project.repository_storage)
stub = Gitaly::Commit::Stub.new(nil, nil, channel_override: channel)
repo = Gitaly::Repository.new(path: repository.path_to_repo)
request = Gitaly::CommitIsAncestorRequest.new(
repository: repo,
ancestor_id: ancestor_id,
child_id: child_id
)
stub.commit_is_ancestor(request).value
end
end end
end end
end end
......
...@@ -11,6 +11,14 @@ module Gitlab ...@@ -11,6 +11,14 @@ module Gitlab
sha.present? && ref.present? sha.present? && ref.present?
end end
def user
raw_data.user&.login || 'unknown'
end
def short_sha
Commit.truncate_sha(sha)
end
private private
def branch_exists? def branch_exists?
......
module Gitlab module Gitlab
module GithubImport module GithubImport
class PullRequestFormatter < IssuableFormatter class PullRequestFormatter < IssuableFormatter
delegate :exists?, :project, :ref, :repo, :sha, to: :source_branch, prefix: true delegate :user, :project, :ref, :repo, :sha, to: :source_branch, prefix: true
delegate :exists?, :project, :ref, :repo, :sha, to: :target_branch, prefix: true delegate :user, :exists?, :project, :ref, :repo, :sha, :short_sha, to: :target_branch, prefix: true
def attributes def attributes
{ {
...@@ -37,13 +37,20 @@ module Gitlab ...@@ -37,13 +37,20 @@ module Gitlab
end end
def source_branch_name def source_branch_name
@source_branch_name ||= begin @source_branch_name ||=
if cross_project? if cross_project? || !source_branch_exists?
"pull/#{number}/#{source_branch_repo.full_name}/#{source_branch_ref}" source_branch_name_prefixed
else else
source_branch_exists? ? source_branch_ref : "pull/#{number}/#{source_branch_ref}" source_branch_ref
end end
end end
def source_branch_name_prefixed
"gh-#{target_branch_short_sha}/#{number}/#{source_branch_user}/#{source_branch_ref}"
end
def source_branch_exists?
!cross_project? && source_branch.exists?
end end
def target_branch def target_branch
...@@ -51,13 +58,17 @@ module Gitlab ...@@ -51,13 +58,17 @@ module Gitlab
end end
def target_branch_name def target_branch_name
@target_branch_name ||= begin @target_branch_name ||= target_branch_exists? ? target_branch_ref : target_branch_name_prefixed
target_branch_exists? ? target_branch_ref : "pull/#{number}/#{target_branch_ref}"
end end
def target_branch_name_prefixed
"gl-#{target_branch_short_sha}/#{number}/#{target_branch_user}/#{target_branch_ref}"
end end
def cross_project? def cross_project?
source_branch.repo.id != target_branch.repo.id return true if source_branch_repo.nil?
source_branch_repo.id != target_branch_repo.id
end end
def opened? def opened?
......
...@@ -17,14 +17,22 @@ module Gitlab ...@@ -17,14 +17,22 @@ module Gitlab
class << self class << self
def git_http_ok(repository, user) def git_http_ok(repository, user)
repo_path = repository.path_to_repo
params = { params = {
GL_ID: Gitlab::GlId.gl_id(user), GL_ID: Gitlab::GlId.gl_id(user),
RepoPath: repository.path_to_repo, RepoPath: repo_path,
} }
if Gitlab.config.gitaly.enabled if Gitlab.config.gitaly.enabled
address = Gitlab::GitalyClient.get_address(repository.project.repository_storage) storage = repository.project.repository_storage
address = Gitlab::GitalyClient.get_address(storage)
params[:GitalySocketPath] = URI(address).path params[:GitalySocketPath] = URI(address).path
# TODO: use GitalyClient code to assemble the Repository message
params[:Repository] = Gitaly::Repository.new(
path: repo_path,
storage_name: storage,
relative_path: Gitlab::RepoPath.strip_storage_path(repo_path),
).to_h
end end
params params
......
require 'spec_helper' require 'spec_helper'
describe Profiles::AccountsController do describe Profiles::AccountsController do
let(:user) { create(:omniauth_user, provider: 'saml') } describe 'DELETE unlink' do
let(:user) { create(:omniauth_user) }
before do before do
sign_in(user) sign_in(user)
end end
it 'does not allow to unlink SAML connected account' do it 'renders 404 if someone tries to unlink a non existent provider' do
delete :unlink, provider: 'github'
expect(response).to have_http_status(404)
end
[:saml, :cas3].each do |provider|
describe "#{provider} provider" do
let(:user) { create(:omniauth_user, provider: provider.to_s) }
it "does not allow to unlink connected account" do
identity = user.identities.last identity = user.identities.last
delete :unlink, provider: 'saml'
updated_user = User.find(user.id) delete :unlink, provider: provider.to_s
expect(response).to have_http_status(302) expect(response).to have_http_status(302)
expect(updated_user.identities.size).to eq(1) expect(user.reload.identities).to include(identity)
expect(updated_user.identities).to include(identity) end
end
end end
it 'does allow to delete other linked accounts' do [:twitter, :facebook, :google_oauth2, :gitlab, :github, :bitbucket, :crowd, :auth0].each do |provider|
user.identities.create(provider: 'twitter', extern_uid: 'twitter_123') describe "#{provider} provider" do
let(:user) { create(:omniauth_user, provider: provider.to_s) }
expect { delete :unlink, provider: 'twitter' }.to change(Identity.all, :size).by(-1) it 'allows to unlink connected account' do
identity = user.identities.last
delete :unlink, provider: provider.to_s
expect(response).to have_http_status(302)
expect(user.reload.identities).not_to include(identity)
end
end
end
end end
end end
...@@ -68,4 +68,20 @@ describe RegistrationsController do ...@@ -68,4 +68,20 @@ describe RegistrationsController do
end end
end end
end end
describe '#destroy' do
let(:user) { create(:user) }
before do
sign_in(user)
end
it 'schedules the user for destruction' do
expect(DeleteUserWorker).to receive(:perform_async).with(user.id, user.id)
post(:destroy)
expect(response.status).to eq(302)
end
end
end end
...@@ -219,4 +219,20 @@ describe SessionsController do ...@@ -219,4 +219,20 @@ describe SessionsController do
end end
end end
end end
describe '#new' do
before do
@request.env['devise.mapping'] = Devise.mappings[:user]
end
it 'redirects correctly for referer on same host with params' do
search_path = '/search?search=seed_project'
allow(controller.request).to receive(:referer).
and_return('http://%{host}%{path}' % { host: Gitlab.config.gitlab.host, path: search_path })
get(:new, redirect_to_referer: :yes)
expect(controller.stored_location_for(:redirect)).to eq(search_path)
end
end
end end
...@@ -9,7 +9,7 @@ describe IssuesFinder do ...@@ -9,7 +9,7 @@ describe IssuesFinder do
let(:label) { create(:label, project: project2) } let(:label) { create(:label, project: project2) }
let(:issue1) { create(:issue, author: user, assignee: user, project: project1, milestone: milestone, title: 'gitlab') } let(:issue1) { create(:issue, author: user, assignee: user, project: project1, milestone: milestone, title: 'gitlab') }
let(:issue2) { create(:issue, author: user, assignee: user, project: project2, description: 'gitlab') } let(:issue2) { create(:issue, author: user, assignee: user, project: project2, description: 'gitlab') }
let(:issue3) { create(:issue, author: user2, assignee: user2, project: project2) } let(:issue3) { create(:issue, author: user2, assignee: user2, project: project2, title: 'tanuki', description: 'tanuki') }
describe '#execute' do describe '#execute' do
let(:closed_issue) { create(:issue, author: user2, assignee: user2, project: project2, state: 'closed') } let(:closed_issue) { create(:issue, author: user2, assignee: user2, project: project2, state: 'closed') }
......
...@@ -62,4 +62,18 @@ describe AuthHelper do ...@@ -62,4 +62,18 @@ describe AuthHelper do
end end
end end
end end
describe 'unlink_allowed?' do
[:saml, :cas3].each do |provider|
it "returns true if the provider is #{provider}" do
expect(helper.unlink_allowed?(provider)).to be false
end
end
[:twitter, :facebook, :google_oauth2, :gitlab, :github, :bitbucket, :crowd, :auth0].each do |provider|
it "returns false if the provider is #{provider}" do
expect(helper.unlink_allowed?(provider)).to be true
end
end
end
end end
...@@ -91,6 +91,10 @@ describe('Environment', () => { ...@@ -91,6 +91,10 @@ describe('Environment', () => {
}); });
describe('pagination', () => { describe('pagination', () => {
afterEach(() => {
window.history.pushState({}, null, '');
});
it('should render pagination', (done) => { it('should render pagination', (done) => {
setTimeout(() => { setTimeout(() => {
expect( expect(
......
...@@ -46,6 +46,10 @@ require('~/lib/utils/common_utils'); ...@@ -46,6 +46,10 @@ require('~/lib/utils/common_utils');
spyOn(window.document, 'getElementById').and.callThrough(); spyOn(window.document, 'getElementById').and.callThrough();
}); });
afterEach(() => {
window.history.pushState({}, null, '');
});
function expectGetElementIdToHaveBeenCalledWith(elementId) { function expectGetElementIdToHaveBeenCalledWith(elementId) {
expect(window.document.getElementById).toHaveBeenCalledWith(elementId); expect(window.document.getElementById).toHaveBeenCalledWith(elementId);
} }
...@@ -75,11 +79,56 @@ require('~/lib/utils/common_utils'); ...@@ -75,11 +79,56 @@ require('~/lib/utils/common_utils');
}); });
}); });
describe('gl.utils.setParamInURL', () => {
afterEach(() => {
window.history.pushState({}, null, '');
});
it('should return the parameter', () => {
window.history.replaceState({}, null, '');
expect(gl.utils.setParamInURL('page', 156)).toBe('?page=156');
expect(gl.utils.setParamInURL('page', '156')).toBe('?page=156');
});
it('should update the existing parameter when its a number', () => {
window.history.pushState({}, null, '?page=15');
expect(gl.utils.setParamInURL('page', 16)).toBe('?page=16');
expect(gl.utils.setParamInURL('page', '16')).toBe('?page=16');
expect(gl.utils.setParamInURL('page', true)).toBe('?page=true');
});
it('should update the existing parameter when its a string', () => {
window.history.pushState({}, null, '?scope=all');
expect(gl.utils.setParamInURL('scope', 'finished')).toBe('?scope=finished');
});
it('should update the existing parameter when more than one parameter exists', () => {
window.history.pushState({}, null, '?scope=all&page=15');
expect(gl.utils.setParamInURL('scope', 'finished')).toBe('?scope=finished&page=15');
});
it('should add a new parameter to the end of the existing ones', () => {
window.history.pushState({}, null, '?scope=all');
expect(gl.utils.setParamInURL('page', 16)).toBe('?scope=all&page=16');
expect(gl.utils.setParamInURL('page', '16')).toBe('?scope=all&page=16');
expect(gl.utils.setParamInURL('page', true)).toBe('?scope=all&page=true');
});
});
describe('gl.utils.getParameterByName', () => { describe('gl.utils.getParameterByName', () => {
beforeEach(() => { beforeEach(() => {
window.history.pushState({}, null, '?scope=all&p=2'); window.history.pushState({}, null, '?scope=all&p=2');
}); });
afterEach(() => {
window.history.replaceState({}, null, null);
});
it('should return valid parameter', () => { it('should return valid parameter', () => {
const value = gl.utils.getParameterByName('scope'); const value = gl.utils.getParameterByName('scope');
expect(value).toBe('all'); expect(value).toBe('all');
......
...@@ -124,6 +124,10 @@ describe('Pagination component', () => { ...@@ -124,6 +124,10 @@ describe('Pagination component', () => {
}); });
describe('paramHelper', () => { describe('paramHelper', () => {
afterEach(() => {
window.history.pushState({}, null, '');
});
it('can parse url parameters correctly', () => { it('can parse url parameters correctly', () => {
window.history.pushState({}, null, '?scope=all&p=2'); window.history.pushState({}, null, '?scope=all&p=2');
......
...@@ -19,6 +19,7 @@ describe Gitlab::Git::Tree, seed_helper: true do ...@@ -19,6 +19,7 @@ describe Gitlab::Git::Tree, seed_helper: true do
it { expect(dir.commit_id).to eq(SeedRepo::Commit::ID) } it { expect(dir.commit_id).to eq(SeedRepo::Commit::ID) }
it { expect(dir.name).to eq('encoding') } it { expect(dir.name).to eq('encoding') }
it { expect(dir.path).to eq('encoding') } it { expect(dir.path).to eq('encoding') }
it { expect(dir.mode).to eq('40000') }
context :subdir do context :subdir do
let(:subdir) { Gitlab::Git::Tree.where(repository, SeedRepo::Commit::ID, 'files').first } let(:subdir) { Gitlab::Git::Tree.where(repository, SeedRepo::Commit::ID, 'files').first }
......
...@@ -215,9 +215,9 @@ describe Gitlab::GithubImport::Importer, lib: true do ...@@ -215,9 +215,9 @@ describe Gitlab::GithubImport::Importer, lib: true do
let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') }
let(:repository) { double(id: 1, fork: false) } let(:repository) { double(id: 1, fork: false) }
let(:source_sha) { create(:commit, project: project).id } let(:source_sha) { create(:commit, project: project).id }
let(:source_branch) { double(ref: 'branch-merged', repo: repository, sha: source_sha) } let(:source_branch) { double(ref: 'branch-merged', repo: repository, sha: source_sha, user: octocat) }
let(:target_sha) { create(:commit, project: project, git_commit: RepoHelpers.another_sample_commit).id } 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(:target_branch) { double(ref: 'master', repo: repository, sha: target_sha, user: octocat) }
let(:pull_request) do let(:pull_request) do
double( double(
number: 1347, number: 1347,
......
...@@ -4,15 +4,18 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -4,15 +4,18 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
let(:client) { double } let(:client) { double }
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:source_sha) { create(:commit, project: project).id } let(:source_sha) { create(:commit, project: project).id }
let(:target_sha) { create(:commit, project: project, git_commit: RepoHelpers.another_sample_commit).id } let(:target_commit) { create(:commit, project: project, git_commit: RepoHelpers.another_sample_commit) }
let(:target_sha) { target_commit.id }
let(:target_short_sha) { target_commit.id.to_s[0..7] }
let(:repository) { double(id: 1, fork: false) } let(:repository) { double(id: 1, fork: false) }
let(:source_repo) { repository } let(:source_repo) { repository }
let(:source_branch) { double(ref: 'branch-merged', repo: source_repo, sha: source_sha) } let(:source_branch) { double(ref: 'branch-merged', repo: source_repo, sha: source_sha) }
let(:forked_source_repo) { double(id: 2, fork: true, name: 'otherproject', full_name: 'company/otherproject') } let(:forked_source_repo) { double(id: 2, fork: true, name: 'otherproject', full_name: 'company/otherproject') }
let(:target_repo) { repository } let(:target_repo) { repository }
let(:target_branch) { double(ref: 'master', repo: target_repo, sha: target_sha) } let(:target_branch) { double(ref: 'master', repo: target_repo, sha: target_sha, user: octocat) }
let(:removed_branch) { double(ref: 'removed-branch', repo: source_repo, sha: '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b') } let(:removed_branch) { double(ref: 'removed-branch', repo: source_repo, sha: '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b', user: octocat) }
let(:forked_branch) { double(ref: 'master', repo: forked_source_repo, sha: '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b') } let(:forked_branch) { double(ref: 'master', repo: forked_source_repo, sha: '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b', user: octocat) }
let(:branch_deleted_repo) { double(ref: 'master', repo: nil, sha: '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b', user: octocat) }
let(:octocat) { double(id: 123456, login: 'octocat', email: 'octocat@example.com') } let(:octocat) { double(id: 123456, login: 'octocat', email: 'octocat@example.com') }
let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') }
let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') }
...@@ -203,16 +206,24 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -203,16 +206,24 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
context 'when source branch does not exist' do context 'when source branch does not exist' do
let(:raw_data) { double(base_data.merge(head: removed_branch)) } let(:raw_data) { double(base_data.merge(head: removed_branch)) }
it 'prefixes branch name with pull request number' do it 'prefixes branch name with gh-:short_sha/:number/:user pattern to avoid collision' do
expect(pull_request.source_branch_name).to eq 'pull/1347/removed-branch' expect(pull_request.source_branch_name).to eq "gh-#{target_short_sha}/1347/octocat/removed-branch"
end end
end end
context 'when source branch is from a fork' do context 'when source branch is from a fork' do
let(:raw_data) { double(base_data.merge(head: forked_branch)) } let(:raw_data) { double(base_data.merge(head: forked_branch)) }
it 'prefixes branch name with pull request number and project with namespace to avoid collision' do it 'prefixes branch name with gh-:short_sha/:number/:user pattern to avoid collision' do
expect(pull_request.source_branch_name).to eq 'pull/1347/company/otherproject/master' expect(pull_request.source_branch_name).to eq "gh-#{target_short_sha}/1347/octocat/master"
end
end
context 'when source branch is from a deleted fork' do
let(:raw_data) { double(base_data.merge(head: branch_deleted_repo)) }
it 'prefixes branch name with gh-:short_sha/:number/:user pattern to avoid collision' do
expect(pull_request.source_branch_name).to eq "gh-#{target_short_sha}/1347/octocat/master"
end end
end end
end end
...@@ -229,8 +240,8 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -229,8 +240,8 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
context 'when target branch does not exist' do context 'when target branch does not exist' do
let(:raw_data) { double(base_data.merge(base: removed_branch)) } let(:raw_data) { double(base_data.merge(base: removed_branch)) }
it 'prefixes branch name with pull request number' do it 'prefixes branch name with gh-:short_sha/:number/:user pattern to avoid collision' do
expect(pull_request.target_branch_name).to eq 'pull/1347/removed-branch' expect(pull_request.target_branch_name).to eq 'gl-2e5d3239/1347/octocat/removed-branch'
end end
end end
end end
...@@ -290,6 +301,14 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -290,6 +301,14 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
end end
end end
context 'when source repository does not exist anymore' do
let(:raw_data) { double(base_data.merge(head: branch_deleted_repo)) }
it 'returns true' do
expect(pull_request.cross_project?).to eq true
end
end
context 'when source and target repositories are the same' do context 'when source and target repositories are the same' do
let(:raw_data) { double(base_data.merge(head: source_branch)) } let(:raw_data) { double(base_data.merge(head: source_branch)) }
...@@ -299,6 +318,14 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do ...@@ -299,6 +318,14 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
end end
end end
describe '#source_branch_exists?' do
let(:raw_data) { double(base_data.merge(head: forked_branch)) }
it 'returns false when is a cross_project' do
expect(pull_request.source_branch_exists?).to eq false
end
end
describe '#url' do describe '#url' do
let(:raw_data) { double(base_data) } let(:raw_data) { double(base_data) }
......
...@@ -179,10 +179,11 @@ describe Gitlab::Workhorse, lib: true do ...@@ -179,10 +179,11 @@ describe Gitlab::Workhorse, lib: true do
describe '.git_http_ok' do describe '.git_http_ok' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:repo_path) { repository.path_to_repo }
subject { described_class.git_http_ok(repository, user) } subject { described_class.git_http_ok(repository, user) }
it { expect(subject).to eq({ GL_ID: "user-#{user.id}", RepoPath: repository.path_to_repo }) } it { expect(subject).to eq({ GL_ID: "user-#{user.id}", RepoPath: repo_path }) }
context 'when Gitaly is enabled' do context 'when Gitaly is enabled' do
before do before do
...@@ -192,6 +193,11 @@ describe Gitlab::Workhorse, lib: true do ...@@ -192,6 +193,11 @@ describe Gitlab::Workhorse, lib: true do
it 'includes Gitaly params in the returned value' do it 'includes Gitaly params in the returned value' do
gitaly_socket_path = URI(Gitlab::GitalyClient.get_address('default')).path gitaly_socket_path = URI(Gitlab::GitalyClient.get_address('default')).path
expect(subject).to include({ GitalySocketPath: gitaly_socket_path }) expect(subject).to include({ GitalySocketPath: gitaly_socket_path })
expect(subject[:Repository]).to include({
path: repo_path,
storage_name: 'default',
relative_path: project.full_path + '.git',
})
end end
end end
end end
......
...@@ -1995,6 +1995,7 @@ describe Repository, models: true do ...@@ -1995,6 +1995,7 @@ describe Repository, models: true do
end end
end end
<<<<<<< HEAD
describe '#after_sync' do describe '#after_sync' do
it 'expires repository cache' do it 'expires repository cache' do
expect(repository).to receive(:expire_all_method_caches) expect(repository).to receive(:expire_all_method_caches)
...@@ -2009,4 +2010,18 @@ describe Repository, models: true do ...@@ -2009,4 +2010,18 @@ describe Repository, models: true do
rugged = repository.rugged rugged = repository.rugged
rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", target.id) rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", target.id)
end end
=======
describe '#is_ancestor?' do
context 'Gitaly is_ancestor feature enabled' do
it 'asks Gitaly server if it\'s an ancestor' do
commit = repository.commit
allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(:is_ancestor).and_return(true)
expect(Gitlab::GitalyClient::Commit).to receive(:is_ancestor).
with(repository.raw_repository, commit.id, commit.id).and_return(true)
expect(repository.is_ancestor?(commit.id, commit.id)).to be true
end
end
end
>>>>>>> ce-com/master
end end
...@@ -688,7 +688,7 @@ describe API::Users, api: true do ...@@ -688,7 +688,7 @@ describe API::Users, api: true do
before { admin } before { admin }
it "deletes user" do it "deletes user" do
delete api("/users/#{user.id}", admin) Sidekiq::Testing.inline! { delete api("/users/#{user.id}", admin) }
expect(response).to have_http_status(204) expect(response).to have_http_status(204)
expect { User.find(user.id) }.to raise_error ActiveRecord::RecordNotFound expect { User.find(user.id) }.to raise_error ActiveRecord::RecordNotFound
...@@ -696,23 +696,23 @@ describe API::Users, api: true do ...@@ -696,23 +696,23 @@ describe API::Users, api: true do
end end
it "does not delete for unauthenticated user" do it "does not delete for unauthenticated user" do
delete api("/users/#{user.id}") Sidekiq::Testing.inline! { delete api("/users/#{user.id}") }
expect(response).to have_http_status(401) expect(response).to have_http_status(401)
end end
it "is not available for non admin users" do it "is not available for non admin users" do
delete api("/users/#{user.id}", user) Sidekiq::Testing.inline! { delete api("/users/#{user.id}", user) }
expect(response).to have_http_status(403) expect(response).to have_http_status(403)
end end
it "returns 404 for non-existing user" do it "returns 404 for non-existing user" do
delete api("/users/999999", admin) Sidekiq::Testing.inline! { delete api("/users/999999", admin) }
expect(response).to have_http_status(404) expect(response).to have_http_status(404)
expect(json_response['message']).to eq('404 User Not Found') expect(json_response['message']).to eq('404 User Not Found')
end end
it "returns a 404 for invalid ID" do it "returns a 404 for invalid ID" do
delete api("/users/ASDF", admin) Sidekiq::Testing.inline! { delete api("/users/ASDF", admin) }
expect(response).to have_http_status(404) expect(response).to have_http_status(404)
end end
......
require 'spec_helper'
describe Search::GlobalService, services: true do
let(:user) { create(:user) }
let(:internal_user) { create(:user) }
let!(:found_project) { create(:empty_project, :private, name: 'searchable_project') }
let!(:unfound_project) { create(:empty_project, :private, name: 'unfound_project') }
let!(:internal_project) { create(:empty_project, :internal, name: 'searchable_internal_project') }
let!(:public_project) { create(:empty_project, :public, name: 'searchable_public_project') }
before do
found_project.add_master(user)
end
describe '#execute' do
context 'unauthenticated' do
it 'returns public projects only' do
results = Search::GlobalService.new(nil, search: "searchable").execute
expect(results.objects('projects')).to match_array [public_project]
end
end
context 'authenticated' do
it 'returns public, internal and private projects' do
results = Search::GlobalService.new(user, search: "searchable").execute
expect(results.objects('projects')).to match_array [public_project, found_project, internal_project]
end
it 'returns only public & internal projects' do
results = Search::GlobalService.new(internal_user, search: "searchable").execute
expect(results.objects('projects')).to match_array [internal_project, public_project]
end
it 'namespace name is searchable' do
results = Search::GlobalService.new(user, search: found_project.namespace.path).execute
expect(results.objects('projects')).to match_array [found_project]
end
context 'nested group' do
let!(:nested_group) { create(:group, :nested) }
let!(:project) { create(:empty_project, namespace: nested_group) }
before do
project.add_master(user)
end
it 'returns result from nested group' do
results = Search::GlobalService.new(user, search: project.path).execute
expect(results.objects('projects')).to match_array [project]
end
it 'returns result from descendants when search inside group' do
results = Search::GlobalService.new(user, search: project.path, group_id: nested_group.parent).execute
expect(results.objects('projects')).to match_array [project]
end
end
end
end
end
This diff is collapsed.
...@@ -17,13 +17,28 @@ describe Users::DestroyService, services: true do ...@@ -17,13 +17,28 @@ describe Users::DestroyService, services: true do
expect { Namespace.with_deleted.find(user.namespace.id) }.to raise_error(ActiveRecord::RecordNotFound) expect { Namespace.with_deleted.find(user.namespace.id) }.to raise_error(ActiveRecord::RecordNotFound)
end end
it 'will delete the project in the near future' do it 'will delete the project' do
expect_any_instance_of(Projects::DestroyService).to receive(:async_execute).once expect_any_instance_of(Projects::DestroyService).to receive(:execute).once
service.execute(user) service.execute(user)
end end
end end
context 'projects in pending_delete' do
before do
project.pending_delete = true
project.save
end
it 'destroys a project in pending_delete' do
expect_any_instance_of(Projects::DestroyService).to receive(:execute).once
service.execute(user)
expect { Project.find(project.id) }.to raise_error(ActiveRecord::RecordNotFound)
end
end
context "a deleted user's issues" do context "a deleted user's issues" do
let(:project) { create(:project) } let(:project) { create(:project) }
......
...@@ -133,9 +133,11 @@ module TestEnv ...@@ -133,9 +133,11 @@ module TestEnv
set_repo_refs(repo_path, branch_sha) set_repo_refs(repo_path, branch_sha)
unless File.directory?(repo_path_bare)
# We must copy bare repositories because we will push to them. # We must copy bare repositories because we will push to them.
system(git_env, *%W(#{Gitlab.config.git.bin_path} clone -q --bare #{repo_path} #{repo_path_bare})) system(git_env, *%W(#{Gitlab.config.git.bin_path} clone -q --bare #{repo_path} #{repo_path_bare}))
end end
end
def copy_repo(project) def copy_repo(project)
base_repo_path = File.expand_path(factory_repo_path_bare) base_repo_path = File.expand_path(factory_repo_path_bare)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment