Commit 6a6a6720 authored by Luke "Jared" Bennett's avatar Luke "Jared" Bennett

Merge remote-tracking branch 'origin/master' into droplab-templating-xss-fix

parents 597c5c76 3c6fad64
...@@ -49,7 +49,7 @@ eslint-report.html ...@@ -49,7 +49,7 @@ eslint-report.html
/tags /tags
/tmp/* /tmp/*
/vendor/bundle/* /vendor/bundle/*
/builds/* /builds*
/shared/* /shared/*
/.gitlab_workhorse_secret /.gitlab_workhorse_secret
/webpack-report/ /webpack-report/
...@@ -59,7 +59,7 @@ stages: ...@@ -59,7 +59,7 @@ stages:
.only-master-and-ee-or-mysql: &only-master-and-ee-or-mysql .only-master-and-ee-or-mysql: &only-master-and-ee-or-mysql
only: only:
- /\-(?i)mysql$/ - /mysql/
- master@gitlab-org/gitlab-ce - master@gitlab-org/gitlab-ce
- master@gitlab/gitlabhq - master@gitlab/gitlabhq
- tags@gitlab-org/gitlab-ce - tags@gitlab-org/gitlab-ce
......
...@@ -716,7 +716,7 @@ GEM ...@@ -716,7 +716,7 @@ GEM
rack rack
shoulda-matchers (2.8.0) shoulda-matchers (2.8.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
sidekiq (4.2.7) sidekiq (4.2.10)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
connection_pool (~> 2.2, >= 2.2.0) connection_pool (~> 2.2, >= 2.2.0)
rack-protection (>= 1.5.0) rack-protection (>= 1.5.0)
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import './breakpoints'; import './breakpoints';
import './flash'; import './flash';
import BlobForkSuggestion from './blob/blob_fork_suggestion';
/* eslint-disable max-len */ /* eslint-disable max-len */
// MergeRequestTabs // MergeRequestTabs
...@@ -266,6 +267,17 @@ import './flash'; ...@@ -266,6 +267,17 @@ import './flash';
new gl.Diff(); new gl.Diff();
this.scrollToElement('#diffs'); this.scrollToElement('#diffs');
$('.diff-file').each((i, el) => {
new BlobForkSuggestion({
openButtons: $(el).find('.js-edit-blob-link-fork-toggler'),
forkButtons: $(el).find('.js-fork-suggestion-button'),
cancelButtons: $(el).find('.js-cancel-fork-suggestion-button'),
suggestionSections: $(el).find('.js-file-fork-suggestion-section'),
actionTextPieces: $(el).find('.js-file-fork-suggestion-section-action'),
})
.init();
});
}, },
}); });
} }
......
...@@ -22,6 +22,7 @@ class PrometheusGraph { ...@@ -22,6 +22,7 @@ class PrometheusGraph {
const hasMetrics = $prometheusContainer.data('has-metrics'); const hasMetrics = $prometheusContainer.data('has-metrics');
this.docLink = $prometheusContainer.data('doc-link'); this.docLink = $prometheusContainer.data('doc-link');
this.integrationLink = $prometheusContainer.data('prometheus-integration'); this.integrationLink = $prometheusContainer.data('prometheus-integration');
this.state = '';
$(document).ajaxError(() => {}); $(document).ajaxError(() => {});
...@@ -38,8 +39,9 @@ class PrometheusGraph { ...@@ -38,8 +39,9 @@ class PrometheusGraph {
this.configureGraph(); this.configureGraph();
this.init(); this.init();
} else { } else {
const prevState = this.state;
this.state = '.js-getting-started'; this.state = '.js-getting-started';
this.updateState(); this.updateState(prevState);
} }
} }
...@@ -53,26 +55,26 @@ class PrometheusGraph { ...@@ -53,26 +55,26 @@ class PrometheusGraph {
} }
init() { init() {
this.getData().then((metricsResponse) => { return this.getData().then((metricsResponse) => {
let enoughData = true; let enoughData = true;
if (typeof metricsResponse === 'undefined') {
enoughData = false;
} else {
Object.keys(metricsResponse.metrics).forEach((key) => { Object.keys(metricsResponse.metrics).forEach((key) => {
let currentKey;
if (key === 'cpu_values' || key === 'memory_values') { if (key === 'cpu_values' || key === 'memory_values') {
currentKey = metricsResponse.metrics[key]; const currentData = (metricsResponse.metrics[key])[0];
if (Object.keys(currentKey).length === 0) { if (currentData.values.length <= 2) {
enoughData = false; enoughData = false;
} }
} }
}); });
if (!enoughData) { }
this.state = '.js-loading'; if (enoughData) {
this.updateState(); $(prometheusStatesContainer).hide();
} else { $(prometheusParentGraphContainer).show();
this.transformData(metricsResponse); this.transformData(metricsResponse);
this.createGraph(); this.createGraph();
} }
}).catch(() => {
new Flash('An error occurred when trying to load metrics. Please try again.');
}); });
} }
...@@ -342,6 +344,8 @@ class PrometheusGraph { ...@@ -342,6 +344,8 @@ class PrometheusGraph {
getData() { getData() {
const maxNumberOfRequests = 3; const maxNumberOfRequests = 3;
this.state = '.js-loading';
this.updateState();
return gl.utils.backOff((next, stop) => { return gl.utils.backOff((next, stop) => {
$.ajax({ $.ajax({
url: metricsEndpoint, url: metricsEndpoint,
...@@ -352,12 +356,11 @@ class PrometheusGraph { ...@@ -352,12 +356,11 @@ class PrometheusGraph {
this.backOffRequestCounter = this.backOffRequestCounter += 1; this.backOffRequestCounter = this.backOffRequestCounter += 1;
if (this.backOffRequestCounter < maxNumberOfRequests) { if (this.backOffRequestCounter < maxNumberOfRequests) {
next(); next();
} else { } else if (this.backOffRequestCounter >= maxNumberOfRequests) {
stop({ stop(new Error('loading'));
status: resp.status,
metrics: data,
});
} }
} else if (!data.success) {
stop(new Error('loading'));
} else { } else {
stop({ stop({
status: resp.status, status: resp.status,
...@@ -373,8 +376,9 @@ class PrometheusGraph { ...@@ -373,8 +376,9 @@ class PrometheusGraph {
return resp.metrics; return resp.metrics;
}) })
.catch(() => { .catch(() => {
const prevState = this.state;
this.state = '.js-unable-to-connect'; this.state = '.js-unable-to-connect';
this.updateState(); this.updateState(prevState);
}); });
} }
...@@ -382,19 +386,20 @@ class PrometheusGraph { ...@@ -382,19 +386,20 @@ class PrometheusGraph {
Object.keys(metricsResponse.metrics).forEach((key) => { Object.keys(metricsResponse.metrics).forEach((key) => {
if (key === 'cpu_values' || key === 'memory_values') { if (key === 'cpu_values' || key === 'memory_values') {
const metricValues = (metricsResponse.metrics[key])[0]; const metricValues = (metricsResponse.metrics[key])[0];
if (metricValues !== undefined) {
this.graphSpecificProperties[key].data = metricValues.values.map(metric => ({ this.graphSpecificProperties[key].data = metricValues.values.map(metric => ({
time: new Date(metric[0] * 1000), time: new Date(metric[0] * 1000),
value: metric[1], value: metric[1],
})); }));
} }
}
}); });
} }
updateState() { updateState(prevState) {
const $statesContainer = $(prometheusStatesContainer); const $statesContainer = $(prometheusStatesContainer);
$(prometheusParentGraphContainer).hide(); $(prometheusParentGraphContainer).hide();
if (prevState) {
$(`${prevState}`, $statesContainer).addClass('hidden');
}
$(`${this.state}`, $statesContainer).removeClass('hidden'); $(`${this.state}`, $statesContainer).removeClass('hidden');
$(prometheusStatesContainer).show(); $(prometheusStatesContainer).show();
} }
......
...@@ -108,8 +108,7 @@ ...@@ -108,8 +108,7 @@
} }
.award-control { .award-control {
margin: 3px 5px 3px 0; margin-right: 5px;
padding: .35em .4em;
outline: 0; outline: 0;
&.disabled { &.disabled {
......
...@@ -70,7 +70,7 @@ pre { ...@@ -70,7 +70,7 @@ pre {
} }
hr { hr {
margin: $gl-padding 0; margin: 24px 0;
border-top: 1px solid darken($gray-normal, 8%); border-top: 1px solid darken($gray-normal, 8%);
} }
......
...@@ -73,14 +73,6 @@ ...@@ -73,14 +73,6 @@
&.wiki { &.wiki {
padding: 30px $gl-padding; padding: 30px $gl-padding;
.highlight {
margin-bottom: 9px;
> pre {
margin: 0;
}
}
} }
&.blob-no-preview { &.blob-no-preview {
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
padding: 0; padding: 0;
.timeline-entry { .timeline-entry {
padding: $gl-padding $gl-btn-padding 14px; padding: $gl-padding $gl-btn-padding 0;
border-color: $white-normal; border-color: $white-normal;
color: $gl-text-color; color: $gl-text-color;
border-bottom: 1px solid $border-white-light; border-bottom: 1px solid $border-white-light;
......
...@@ -8,6 +8,13 @@ ...@@ -8,6 +8,13 @@
img { img {
max-width: 100%; max-width: 100%;
margin: 0 0 8px;
}
p a:not(.no-attachment-icon) img {
// Remove bottom padding because
// <p> already has $gl-padding bottom
margin-bottom: 0;
} }
*:first-child:not(.katex-display) { *:first-child:not(.katex-display) {
...@@ -47,44 +54,50 @@ ...@@ -47,44 +54,50 @@
h1 { h1 {
font-size: 1.75em; font-size: 1.75em;
font-weight: 600; font-weight: 600;
margin: 16px 0 10px; margin: 24px 0 16px;
padding: 0 0 0.3em; padding-bottom: 0.3em;
border-bottom: 1px solid $white-dark; border-bottom: 1px solid $white-dark;
color: $gl-text-color; color: $gl-text-color;
&:first-child {
margin-top: 0;
}
} }
h2 { h2 {
font-size: 1.5em; font-size: 1.5em;
font-weight: 600; font-weight: 600;
margin: 16px 0 10px; margin: 24px 0 16px;
padding-bottom: 0.3em;
border-bottom: 1px solid $white-dark;
color: $gl-text-color; color: $gl-text-color;
} }
h3 { h3 {
margin: 16px 0 10px; margin: 24px 0 16px;
font-size: 1.3em; font-size: 1.3em;
} }
h4 { h4 {
margin: 16px 0 10px; margin: 24px 0 16px;
font-size: 1.2em; font-size: 1.2em;
} }
h5 { h5 {
margin: 16px 0 10px; margin: 24px 0 16px;
font-size: 1em; font-size: 1em;
} }
h6 { h6 {
margin: 16px 0 10px; margin: 24px 0 16px;
font-size: 0.95em; font-size: 0.95em;
} }
blockquote { blockquote {
color: $gl-grayish-blue; color: $gl-grayish-blue;
font-size: inherit; font-size: inherit;
padding: 8px 21px; padding: 8px 24px;
margin: 12px 0; margin: 16px 0;
border-left: 3px solid $white-dark; border-left: 3px solid $white-dark;
} }
...@@ -95,19 +108,20 @@ ...@@ -95,19 +108,20 @@
blockquote p { blockquote p {
color: $gl-grayish-blue !important; color: $gl-grayish-blue !important;
margin: 0;
font-size: inherit; font-size: inherit;
line-height: 1.5; line-height: 1.5;
} }
p { p {
color: $gl-text-color; color: $gl-text-color;
margin: 6px 0 0; margin: 0 0 16px;
} }
table { table {
@extend .table; @extend .table;
@extend .table-bordered; @extend .table-bordered;
margin: 12px 0; margin: 16px 0;
color: $gl-text-color; color: $gl-text-color;
th { th {
...@@ -120,7 +134,7 @@ ...@@ -120,7 +134,7 @@
} }
pre { pre {
margin: 12px 0; margin-bottom: 16px;
font-size: 13px; font-size: 13px;
line-height: 1.6em; line-height: 1.6em;
overflow-x: auto; overflow-x: auto;
...@@ -134,7 +148,7 @@ ...@@ -134,7 +148,7 @@
ul, ul,
ol { ol {
padding: 0; padding: 0;
margin: 3px 0 !important; margin: 0 0 16px !important;
} }
ul:dir(rtl), ul:dir(rtl),
......
...@@ -29,11 +29,5 @@ ...@@ -29,11 +29,5 @@
.description { .description {
margin-top: 6px; margin-top: 6px;
p {
&:last-child {
margin-bottom: 0;
}
}
} }
} }
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
.title { .title {
padding: 0; padding: 0;
margin: 0; margin-bottom: 16px;
border-bottom: none; border-bottom: none;
} }
...@@ -357,6 +357,8 @@ ...@@ -357,6 +357,8 @@
} }
.detail-page-description { .detail-page-description {
padding: 16px 0 0;
small { small {
color: $gray-darkest; color: $gray-darkest;
} }
...@@ -364,6 +366,8 @@ ...@@ -364,6 +366,8 @@
.edited-text { .edited-text {
color: $gray-darkest; color: $gray-darkest;
display: block;
margin: 0 0 16px;
.author_link { .author_link {
color: $gray-darkest; color: $gray-darkest;
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
.note-edit-form { .note-edit-form {
.note-form-actions { .note-form-actions {
position: relative; position: relative;
margin-top: $gl-padding; margin: $gl-padding 0;
} }
.note-preview-holder { .note-preview-holder {
......
...@@ -102,13 +102,12 @@ ul.notes { ...@@ -102,13 +102,12 @@ ul.notes {
.note-awards { .note-awards {
.js-awards-block { .js-awards-block {
padding: 2px; margin-bottom: 16px;
margin-top: 10px;
} }
} }
.note-header { .note-header {
padding-bottom: 3px; padding-bottom: 8px;
padding-right: 20px; padding-right: 20px;
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
...@@ -151,6 +150,10 @@ ul.notes { ...@@ -151,6 +150,10 @@ ul.notes {
margin-left: 65px; margin-left: 65px;
} }
.note-header {
padding-bottom: 0;
}
&.timeline-entry::after { &.timeline-entry::after {
clear: none; clear: none;
} }
......
...@@ -28,7 +28,7 @@ class Admin::GroupsController < Admin::ApplicationController ...@@ -28,7 +28,7 @@ class Admin::GroupsController < Admin::ApplicationController
if @group.save if @group.save
@group.add_owner(current_user) @group.add_owner(current_user)
redirect_to [:admin, @group], notice: 'Group was successfully created.' redirect_to [:admin, @group], notice: "Group '#{@group.name}' was successfully created."
else else
render "new" render "new"
end end
......
...@@ -29,7 +29,7 @@ module BlobHelper ...@@ -29,7 +29,7 @@ module BlobHelper
link_to 'Edit', edit_path(project, ref, path, options), class: "#{common_classes} btn-sm" link_to 'Edit', edit_path(project, ref, path, options), class: "#{common_classes} btn-sm"
elsif current_user && can?(current_user, :fork_project, project) elsif current_user && can?(current_user, :fork_project, project)
continue_params = { continue_params = {
to: edit_path, to: edit_path(project, ref, path, options),
notice: edit_in_new_fork_notice, notice: edit_in_new_fork_notice,
notice_now: edit_in_new_fork_notice_now notice_now: edit_in_new_fork_notice_now
} }
......
...@@ -191,22 +191,23 @@ class MergeRequest < ActiveRecord::Base ...@@ -191,22 +191,23 @@ class MergeRequest < ActiveRecord::Base
merge_request_diff ? merge_request_diff.raw_diffs(*args) : compare.raw_diffs(*args) merge_request_diff ? merge_request_diff.raw_diffs(*args) : compare.raw_diffs(*args)
end end
def diffs(diff_options = nil) def diffs(diff_options = {})
if compare if compare
compare.diffs(diff_options) # When saving MR diffs, `no_collapse` is implicitly added (because we need
# to save the entire contents to the DB), so add that here for
# consistency.
compare.diffs(diff_options.merge(no_collapse: true))
else else
merge_request_diff.diffs(diff_options) merge_request_diff.diffs(diff_options)
end end
end end
def diff_size def diff_size
# The `#diffs` method ends up at an instance of a class inheriting from # Calling `merge_request_diff.diffs.real_size` will also perform
# `Gitlab::Diff::FileCollection::Base`, so use those options as defaults # highlighting, which we don't need here.
# here too, to get the same diff size without performing highlighting. return real_size if merge_request_diff
#
opts = Gitlab::Diff::FileCollection::Base.default_options.merge(diff_options || {})
raw_diffs(opts).size diffs.real_size
end end
def diff_base_commit def diff_base_commit
......
...@@ -260,7 +260,7 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -260,7 +260,7 @@ class MergeRequestDiff < ActiveRecord::Base
new_attributes[:state] = :empty new_attributes[:state] = :empty
else else
diff_collection = compare.diffs(Commit.max_diff_options) diff_collection = compare.diffs(Commit.max_diff_options)
new_attributes[:real_size] = compare.diffs.real_size new_attributes[:real_size] = diff_collection.real_size
if diff_collection.any? if diff_collection.any?
new_diffs = dump_diffs(diff_collection) new_diffs = dump_diffs(diff_collection)
......
...@@ -6,8 +6,8 @@ module Users ...@@ -6,8 +6,8 @@ module Users
@params = params.dup @params = params.dup
end end
def execute def execute(skip_authorization: false)
raise Gitlab::Access::AccessDeniedError unless can_create_user? raise Gitlab::Access::AccessDeniedError unless skip_authorization || can_create_user?
user = User.new(build_user_params) user = User.new(build_user_params)
......
...@@ -6,8 +6,8 @@ module Users ...@@ -6,8 +6,8 @@ module Users
@params = params.dup @params = params.dup
end end
def execute def execute(skip_authorization: false)
user = Users::BuildService.new(current_user, params).execute user = Users::BuildService.new(current_user, params).execute(skip_authorization: skip_authorization)
@reset_token = user.generate_reset_token if user.recently_sent_password_reset? @reset_token = user.generate_reset_token if user.recently_sent_password_reset?
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
.bs-callout.bs-callout-warning.clearfix .bs-callout.bs-callout-warning.clearfix
%p %p
User cohorts are only shown when the User cohorts are only shown when the
= link_to 'usage ping', help_page_path('user/admin_area/usage_statistics'), target: '_blank' = link_to 'usage ping', help_page_path('user/admin_area/settings/usage_statistics', anchor: 'usage-ping'), target: '_blank'
is enabled. To enable it and see user cohorts, is enabled. To enable it and see user cohorts,
visit visit
= succeed '.' do = succeed '.' do
......
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
Snippets Snippets
- if project_nav_tab? :settings - if project_nav_tab? :settings
= nav_link(path: %w[projects#edit members#show integrations#show repository#show ci_cd#show pages#show]) do = nav_link(path: %w[projects#edit members#show integrations#show services#edit repository#show ci_cd#show pages#show]) do
= link_to edit_project_path(@project), title: 'Settings', class: 'shortcuts-tree' do = link_to edit_project_path(@project), title: 'Settings', class: 'shortcuts-tree' do
%span %span
Settings Settings
......
- if current_user
.js-file-fork-suggestion-section.file-fork-suggestion.hidden
%span.file-fork-suggestion-note
You're not allowed to
%span.js-file-fork-suggestion-section-action
edit
files in this project directly. Please fork this project,
make your changes there, and submit a merge request.
= link_to 'Fork', nil, method: :post, class: 'js-fork-suggestion-button btn btn-grouped btn-inverted btn-new'
%button.js-cancel-fork-suggestion-button.btn.btn-grouped{ type: 'button' }
Cancel
...@@ -39,14 +39,4 @@ ...@@ -39,14 +39,4 @@
= replace_blob_link = replace_blob_link
= delete_blob_link = delete_blob_link
- if current_user = render 'projects/fork_suggestion'
.js-file-fork-suggestion-section.file-fork-suggestion.hidden
%span.file-fork-suggestion-note
You're not allowed to
%span.js-file-fork-suggestion-section-action
edit
files in this project directly. Please fork this project,
make your changes there, and submit a merge request.
= link_to 'Fork', nil, method: :post, class: 'js-fork-suggestion-button btn btn-grouped btn-inverted btn-new'
%button.js-cancel-fork-suggestion-button.btn.btn-grouped{ type: 'button' }
Cancel
...@@ -18,4 +18,6 @@ ...@@ -18,4 +18,6 @@
= view_file_button(diff_commit.id, diff_file.new_path, project) = view_file_button(diff_commit.id, diff_file.new_path, project)
= view_on_environment_button(diff_commit.id, diff_file.new_path, environment) if environment = view_on_environment_button(diff_commit.id, diff_file.new_path, environment) if environment
= render 'projects/fork_suggestion'
= render 'projects/diffs/content', diff_file: diff_file, diff_commit: diff_commit, blob: blob, project: project = render 'projects/diffs/content', diff_file: diff_file, diff_commit: diff_commit, blob: blob, project: project
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
= link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'hidden-xs hidden-sm btn btn-grouped issuable-edit' = link_to 'Edit', edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'hidden-xs hidden-sm btn btn-grouped issuable-edit'
.issue-details.issuable-details .issue-details.issuable-details
.detail-page-description.content-block{ class: ('hide-bottom-border' unless @issue.description.present? ) } .detail-page-description.content-block
.issue-title-data.hidden{ "data" => { "initial-title" => markdown_field(@issue, :title), .issue-title-data.hidden{ "data" => { "initial-title" => markdown_field(@issue, :title),
"endpoint" => rendered_title_namespace_project_issue_path(@project.namespace, @project, @issue), "endpoint" => rendered_title_namespace_project_issue_path(@project.namespace, @project, @issue),
} } } }
......
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
%a.btn.btn-default.btn-grouped.pull-right.visible-xs-block.js-sidebar-toggle{ href: "#" } %a.btn.btn-default.btn-grouped.pull-right.visible-xs-block.js-sidebar-toggle{ href: "#" }
= icon('angle-double-left') = icon('angle-double-left')
.detail-page-description.milestone-detail{ class: ('hide-bottom-border' unless @milestone.description.present? ) } .detail-page-description.milestone-detail
%h2.title %h2.title
= markdown_field(@milestone, :title) = markdown_field(@milestone, :title)
%div %div
......
- page_title @service.title, "Services" - page_title @service.title, "Services"
= render "projects/settings/head"
= render 'form' = render 'form'
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
%span %span
Members Members
- if can_edit - if can_edit
= nav_link(controller: :integrations) do = nav_link(controller: [:integrations, :services]) do
= link_to project_settings_integrations_path(@project), title: 'Integrations' do = link_to project_settings_integrations_path(@project), title: 'Integrations' do
%span %span
Integrations Integrations
......
---
title: Show group name on flash container when group is created from Admin area.
merge_request: 10905
author:
---
title: Cleanup markdown spacing
merge_request:
author:
---
title: Don't display the is_admin flag in most API responses
merge_request: 10846
author:
---
title: Add sub-nav for Project Integration Services edit page
merge_request: 10813
author:
---
title: Fix OAuth, LDAP and SAML SSO when regular sign-ups are disabled
merge_request:
author:
---
title: Fix usage ping docs link from empty cohorts page
merge_request:
author:
---
title: Add index to webhooks type column
merge_request:
author:
---
title: Fixed Prometheus monitoring graphs not showing empty states in certain scenarios
merge_request:
author:
---
title: Show sizes correctly in merge requests when diffs overflow
merge_request:
author:
---
title: Upgrade Sidekiq to 4.2.10
merge_request:
author:
class AddIndexToWebHooksType < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :web_hooks, :type
end
def down
remove_concurrent_index :web_hooks, :type
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20170423064036) do ActiveRecord::Schema.define(version: 20170424142900) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -1371,6 +1371,7 @@ ActiveRecord::Schema.define(version: 20170423064036) do ...@@ -1371,6 +1371,7 @@ ActiveRecord::Schema.define(version: 20170423064036) do
end end
add_index "web_hooks", ["project_id"], name: "index_web_hooks_on_project_id", using: :btree add_index "web_hooks", ["project_id"], name: "index_web_hooks_on_project_id", using: :btree
add_index "web_hooks", ["type"], name: "index_web_hooks_on_type", using: :btree
add_foreign_key "boards", "projects" add_foreign_key "boards", "projects"
add_foreign_key "chat_teams", "namespaces", on_delete: :cascade add_foreign_key "chat_teams", "namespaces", on_delete: :cascade
......
...@@ -48,7 +48,6 @@ Example of response ...@@ -48,7 +48,6 @@ Example of response
"bio": null, "bio": null,
"created_at": "2016-08-11T07:09:20.351Z", "created_at": "2016-08-11T07:09:20.351Z",
"id": 1, "id": 1,
"is_admin": true,
"linkedin": "", "linkedin": "",
"location": null, "location": null,
"name": "Administrator", "name": "Administrator",
...@@ -106,7 +105,6 @@ Example of response ...@@ -106,7 +105,6 @@ Example of response
"bio": null, "bio": null,
"created_at": "2016-08-11T07:09:20.351Z", "created_at": "2016-08-11T07:09:20.351Z",
"id": 1, "id": 1,
"is_admin": true,
"linkedin": "", "linkedin": "",
"location": null, "location": null,
"name": "Administrator", "name": "Administrator",
...@@ -195,7 +193,6 @@ Example of response ...@@ -195,7 +193,6 @@ Example of response
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon", "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://localhost:3000/root", "web_url": "http://localhost:3000/root",
"created_at": "2016-08-11T07:09:20.351Z", "created_at": "2016-08-11T07:09:20.351Z",
"is_admin": true,
"bio": null, "bio": null,
"location": null, "location": null,
"skype": "", "skype": "",
......
...@@ -57,7 +57,6 @@ Example of response ...@@ -57,7 +57,6 @@ Example of response
"bio": null, "bio": null,
"created_at": "2015-12-21T13:14:24.077Z", "created_at": "2015-12-21T13:14:24.077Z",
"id": 1, "id": 1,
"is_admin": true,
"linkedin": "", "linkedin": "",
"name": "Administrator", "name": "Administrator",
"skype": "", "skype": "",
...@@ -101,7 +100,6 @@ Example of response ...@@ -101,7 +100,6 @@ Example of response
"bio": null, "bio": null,
"created_at": "2015-12-21T13:14:24.077Z", "created_at": "2015-12-21T13:14:24.077Z",
"id": 1, "id": 1,
"is_admin": true,
"linkedin": "", "linkedin": "",
"name": "Administrator", "name": "Administrator",
"skype": "", "skype": "",
...@@ -173,7 +171,6 @@ Example of response ...@@ -173,7 +171,6 @@ Example of response
"bio": null, "bio": null,
"created_at": "2015-12-21T13:14:24.077Z", "created_at": "2015-12-21T13:14:24.077Z",
"id": 1, "id": 1,
"is_admin": true,
"linkedin": "", "linkedin": "",
"name": "Administrator", "name": "Administrator",
"skype": "", "skype": "",
...@@ -217,7 +214,6 @@ Example of response ...@@ -217,7 +214,6 @@ Example of response
"bio": null, "bio": null,
"created_at": "2015-12-21T13:14:24.077Z", "created_at": "2015-12-21T13:14:24.077Z",
"id": 1, "id": 1,
"is_admin": true,
"linkedin": "", "linkedin": "",
"name": "Administrator", "name": "Administrator",
"skype": "", "skype": "",
...@@ -284,7 +280,6 @@ Example of response ...@@ -284,7 +280,6 @@ Example of response
"bio": null, "bio": null,
"created_at": "2015-12-21T13:14:24.077Z", "created_at": "2015-12-21T13:14:24.077Z",
"id": 1, "id": 1,
"is_admin": true,
"linkedin": "", "linkedin": "",
"name": "Administrator", "name": "Administrator",
"skype": "", "skype": "",
......
...@@ -26,7 +26,6 @@ Parameters: ...@@ -26,7 +26,6 @@ Parameters:
"avatar_url": "http://www.gravatar.com/avatar/cfa35b8cd2ec278026357769582fa563?s=40\u0026d=identicon", "avatar_url": "http://www.gravatar.com/avatar/cfa35b8cd2ec278026357769582fa563?s=40\u0026d=identicon",
"web_url": "http://localhost:3000/john_smith", "web_url": "http://localhost:3000/john_smith",
"created_at": "2015-09-03T07:24:01.670Z", "created_at": "2015-09-03T07:24:01.670Z",
"is_admin": false,
"bio": null, "bio": null,
"skype": "", "skype": "",
"linkedin": "", "linkedin": "",
......
...@@ -62,7 +62,6 @@ GET /users ...@@ -62,7 +62,6 @@ GET /users
"avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg", "avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg",
"web_url": "http://localhost:3000/john_smith", "web_url": "http://localhost:3000/john_smith",
"created_at": "2012-05-23T08:00:58Z", "created_at": "2012-05-23T08:00:58Z",
"is_admin": false,
"bio": null, "bio": null,
"location": null, "location": null,
"skype": "", "skype": "",
...@@ -95,7 +94,6 @@ GET /users ...@@ -95,7 +94,6 @@ GET /users
"avatar_url": "http://localhost:3000/uploads/user/avatar/2/index.jpg", "avatar_url": "http://localhost:3000/uploads/user/avatar/2/index.jpg",
"web_url": "http://localhost:3000/jack_smith", "web_url": "http://localhost:3000/jack_smith",
"created_at": "2012-05-23T08:01:01Z", "created_at": "2012-05-23T08:01:01Z",
"is_admin": false,
"bio": null, "bio": null,
"location": null, "location": null,
"skype": "", "skype": "",
...@@ -169,7 +167,6 @@ Parameters: ...@@ -169,7 +167,6 @@ Parameters:
"avatar_url": "http://localhost:3000/uploads/user/avatar/1/cd8.jpeg", "avatar_url": "http://localhost:3000/uploads/user/avatar/1/cd8.jpeg",
"web_url": "http://localhost:3000/john_smith", "web_url": "http://localhost:3000/john_smith",
"created_at": "2012-05-23T08:00:58Z", "created_at": "2012-05-23T08:00:58Z",
"is_admin": false,
"bio": null, "bio": null,
"location": null, "location": null,
"skype": "", "skype": "",
...@@ -200,7 +197,6 @@ Parameters: ...@@ -200,7 +197,6 @@ Parameters:
"avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg", "avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg",
"web_url": "http://localhost:3000/john_smith", "web_url": "http://localhost:3000/john_smith",
"created_at": "2012-05-23T08:00:58Z", "created_at": "2012-05-23T08:00:58Z",
"is_admin": false,
"bio": null, "bio": null,
"location": null, "location": null,
"skype": "", "skype": "",
...@@ -325,7 +321,6 @@ GET /user ...@@ -325,7 +321,6 @@ GET /user
"avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg", "avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg",
"web_url": "http://localhost:3000/john_smith", "web_url": "http://localhost:3000/john_smith",
"created_at": "2012-05-23T08:00:58Z", "created_at": "2012-05-23T08:00:58Z",
"is_admin": false,
"bio": null, "bio": null,
"location": null, "location": null,
"skype": "", "skype": "",
......
...@@ -33,7 +33,6 @@ ...@@ -33,7 +33,6 @@
## Backend howtos ## Backend howtos
- [Architecture](architecture.md) of GitLab - [Architecture](architecture.md) of GitLab
- [CI setup](ci_setup.md) for testing GitLab
- [Gotchas](gotchas.md) to avoid - [Gotchas](gotchas.md) to avoid
- [How to dump production data to staging](db_dump.md) - [How to dump production data to staging](db_dump.md)
- [Instrumentation](instrumentation.md) - [Instrumentation](instrumentation.md)
......
# CI setup
This document describes what services we use for testing GitLab and GitLab CI.
We currently use four CI services to test GitLab:
1. GitLab CI on [GitHost.io](https://gitlab-ce.githost.io/projects/4/) for the [GitLab.com repo](https://gitlab.com/gitlab-org/gitlab-ce)
2. GitLab CI at ci.gitlab.org to test the private GitLab B.V. repo at dev.gitlab.org
3. [Semephore](https://semaphoreapp.com/gitlabhq/gitlabhq/) for [GitHub.com repo](https://github.com/gitlabhq/gitlabhq)
4. [Mock CI Service](../user/project/integrations/mock_ci.md) for local development
| Software @ configuration being tested | GitLab CI (ci.gitlab.org) | GitLab CI (GitHost.io) | Semaphore |
|---------------------------------------|---------------------------|---------------------------------------------------------------------------|-----------|
| GitLab CE @ MySQL | ✓ | ✓ [Core team can trigger builds](https://gitlab-ce.githost.io/projects/4) | |
| GitLab CE @ PostgreSQL | | | ✓ [Core team can trigger builds](https://semaphoreapp.com/gitlabhq/gitlabhq/branches/master) |
| GitLab EE @ MySQL | ✓ | | |
| GitLab CI @ MySQL | ✓ | | |
| GitLab CI @ PostgreSQL | | | ✓ |
| GitLab CI Runner | ✓ | | ✓ |
| GitLab Shell | ✓ | | ✓ |
| GitLab Shell | ✓ | | ✓ |
Core team has access to trigger builds if needed for GitLab CE.
We use [these build scripts](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml) for testing with GitLab CI.
# Build configuration on [Semaphore](https://semaphoreapp.com/gitlabhq/gitlabhq/) for testing the [GitHub.com repo](https://github.com/gitlabhq/gitlabhq)
- Language: Ruby
- Ruby version: 2.1.8
- database.yml: pg
Build commands
```bash
sudo apt-get install cmake libicu-dev -y (Setup)
bundle install --deployment --path vendor/bundle (Setup)
cp config/gitlab.yml.example config/gitlab.yml (Setup)
bundle exec rake db:create (Setup)
bundle exec rake spinach (Thread #1)
bundle exec rake spec (thread #2)
bundle exec rake rubocop (thread #3)
bundle exec rake brakeman (thread #4)
bundle exec rake jasmine:ci (thread #5)
```
Use rubygems mirror.
...@@ -448,13 +448,22 @@ is used for Spinach tests as well. ...@@ -448,13 +448,22 @@ is used for Spinach tests as well.
### Monitoring ### Monitoring
The GitLab test suite is [monitored] and a [public dashboard] is available for The GitLab test suite is [monitored] for the `master` branch, and any branch
everyone to see. Feel free to look at the slowest test files and try to improve that includes `rspec-profile` in their name.
them.
A [public dashboard] is available for everyone to see. Feel free to look at the
slowest test files and try to improve them.
[monitored]: ./performance.md#rspec-profiling [monitored]: ./performance.md#rspec-profiling
[public dashboard]: https://redash.gitlab.com/public/dashboards/l1WhHXaxrCWM5Ai9D7YDqHKehq6OU3bx5gssaiWe?org_slug=default [public dashboard]: https://redash.gitlab.com/public/dashboards/l1WhHXaxrCWM5Ai9D7YDqHKehq6OU3bx5gssaiWe?org_slug=default
## CI setup
- On CE, the test suite only runs against PostgreSQL by default. We additionally
run the suite against MySQL for tags, `master`, and any branch that includes
`mysql` in the name.
- On EE, the test suite always runs both PostgreSQL and MySQL.
## Spinach (feature) tests ## Spinach (feature) tests
GitLab [moved from Cucumber to Spinach](https://github.com/gitlabhq/gitlabhq/pull/1426) GitLab [moved from Cucumber to Spinach](https://github.com/gitlabhq/gitlabhq/pull/1426)
......
...@@ -12,7 +12,7 @@ You can leave a comment in the following places: ...@@ -12,7 +12,7 @@ You can leave a comment in the following places:
The comment area supports [Markdown] and [slash commands]. One can edit their The comment area supports [Markdown] and [slash commands]. One can edit their
own comment at any time, and anyone with [Master access level][permissions] or own comment at any time, and anyone with [Master access level][permissions] or
higher can also a comment made by someone else. higher can also edit a comment made by someone else.
Apart from the standard comments, you also have the option to create a comment Apart from the standard comments, you also have the option to create a comment
in the form of a resolvable or threaded discussion. in the form of a resolvable or threaded discussion.
......
...@@ -431,7 +431,7 @@ Emphasis, aka italics, with *asterisks* or _underscores_. ...@@ -431,7 +431,7 @@ Emphasis, aka italics, with *asterisks* or _underscores_.
Strong emphasis, aka bold, with **asterisks** or __underscores__. Strong emphasis, aka bold, with **asterisks** or __underscores__.
Combined emphasis with **_asterisks and underscores_**. Combined emphasis with **asterisks and _underscores_**.
Strikethrough uses two tildes. ~~Scratch this.~~ Strikethrough uses two tildes. ~~Scratch this.~~
``` ```
...@@ -640,10 +640,11 @@ Here's a line for us to start with. ...@@ -640,10 +640,11 @@ Here's a line for us to start with.
This line is separated from the one above by two newlines, so it will be a *separate paragraph*. This line is separated from the one above by two newlines, so it will be a *separate paragraph*.
This line is also a separate paragraph, but... This line is also a separate paragraph, but...
This line is only separated by a single newline, so it's a separate line in the *same paragraph*. This line is only separated by a single newline, so it *does not break* and just follows the previous line in the *same paragraph*.
This line is also a separate paragraph, and... This line is also a separate paragraph, and...
This line is on its own line, because the previous line ends with two This line is *on its own line*, because the previous line ends with two spaces. (but still in the *same paragraph*)
spaces. spaces.
``` ```
...@@ -651,11 +652,12 @@ Here's a line for us to start with. ...@@ -651,11 +652,12 @@ Here's a line for us to start with.
This line is separated from the one above by two newlines, so it will be a *separate paragraph*. This line is separated from the one above by two newlines, so it will be a *separate paragraph*.
This line is also begins a separate paragraph, but... This line is also a separate paragraph, but...
This line is only separated by a single newline, so it's a separate line in the *same paragraph*. This line is only separated by a single newline, so it *does not break* and just follows the previous line in the *same paragraph*.
This line is also a separate paragraph, and... This line is also a separate paragraph, and...
This line is on its own line, because the previous line ends with two This line is *on its own line*, because the previous line ends with two spaces. (but still in the *same paragraph*)
spaces. spaces.
### Tables ### Tables
......
...@@ -14,7 +14,6 @@ module API ...@@ -14,7 +14,6 @@ module API
class User < UserBasic class User < UserBasic
expose :created_at expose :created_at
expose :admin?, as: :is_admin
expose :bio, :location, :skype, :linkedin, :twitter, :website_url, :organization expose :bio, :location, :skype, :linkedin, :twitter, :website_url, :organization
end end
...@@ -41,8 +40,9 @@ module API ...@@ -41,8 +40,9 @@ module API
expose :external expose :external
end end
class UserWithPrivateToken < UserPublic class UserWithPrivateDetails < UserPublic
expose :private_token expose :private_token
expose :admin?, as: :is_admin
end end
class Email < Grape::Entity class Email < Grape::Entity
......
module API module API
class Session < Grape::API class Session < Grape::API
desc 'Login to get token' do desc 'Login to get token' do
success Entities::UserWithPrivateToken success Entities::UserWithPrivateDetails
end end
params do params do
optional :login, type: String, desc: 'The username' optional :login, type: String, desc: 'The username'
...@@ -14,7 +14,7 @@ module API ...@@ -14,7 +14,7 @@ module API
return unauthorized! unless user return unauthorized! unless user
return render_api_error!('401 Unauthorized. You have 2FA enabled. Please use a personal access token to access the API', 401) if user.two_factor_enabled? return render_api_error!('401 Unauthorized. You have 2FA enabled. Please use a personal access token to access the API', 401) if user.two_factor_enabled?
present user, with: Entities::UserWithPrivateToken present user, with: Entities::UserWithPrivateDetails
end end
end end
end end
...@@ -433,7 +433,7 @@ module API ...@@ -433,7 +433,7 @@ module API
success Entities::UserPublic success Entities::UserPublic
end end
get do get do
present current_user, with: sudo? ? Entities::UserWithPrivateToken : Entities::UserPublic present current_user, with: sudo? ? Entities::UserWithPrivateDetails : Entities::UserPublic
end end
desc "Get the currently authenticated user's SSH keys" do desc "Get the currently authenticated user's SSH keys" do
......
...@@ -15,6 +15,10 @@ module Gitlab ...@@ -15,6 +15,10 @@ module Gitlab
super.tap { |_| store_highlight_cache } super.tap { |_| store_highlight_cache }
end end
def real_size
@merge_request_diff.real_size
end
private private
# Extracted method to highlight in the same iteration to the diff_collection. # Extracted method to highlight in the same iteration to the diff_collection.
......
...@@ -148,7 +148,7 @@ module Gitlab ...@@ -148,7 +148,7 @@ module Gitlab
def build_new_user def build_new_user
user_params = user_attributes.merge(extern_uid: auth_hash.uid, provider: auth_hash.provider, skip_confirmation: true) user_params = user_attributes.merge(extern_uid: auth_hash.uid, provider: auth_hash.provider, skip_confirmation: true)
Users::BuildService.new(nil, user_params).execute Users::BuildService.new(nil, user_params).execute(skip_authorization: true)
end end
def user_attributes def user_attributes
......
require 'spec_helper' require 'spec_helper'
feature 'Diffs URL', js: true, feature: true do feature 'Diffs URL', js: true, feature: true do
before do let(:project) { create(:project, :public) }
login_as :admin let(:merge_request) { create(:merge_request, source_project: project) }
@merge_request = create(:merge_request)
@project = @merge_request.source_project
end
context 'when visit with */* as accept header' do context 'when visit with */* as accept header' do
before(:each) do before(:each) do
...@@ -13,9 +10,9 @@ feature 'Diffs URL', js: true, feature: true do ...@@ -13,9 +10,9 @@ feature 'Diffs URL', js: true, feature: true do
end end
it 'renders the notes' do it 'renders the notes' do
create :note_on_merge_request, project: @project, noteable: @merge_request, note: 'Rebasing with master' create :note_on_merge_request, project: project, noteable: merge_request, note: 'Rebasing with master'
visit diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request) visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
# Load notes and diff through AJAX # Load notes and diff through AJAX
expect(page).to have_css('.note-text', visible: false, text: 'Rebasing with master') expect(page).to have_css('.note-text', visible: false, text: 'Rebasing with master')
...@@ -25,10 +22,9 @@ feature 'Diffs URL', js: true, feature: true do ...@@ -25,10 +22,9 @@ feature 'Diffs URL', js: true, feature: true do
context 'when merge request has overflow' do context 'when merge request has overflow' do
it 'displays warning' do it 'displays warning' do
allow_any_instance_of(MergeRequestDiff).to receive(:overflow?).and_return(true) allow(Commit).to receive(:max_diff_options).and_return(max_files: 3)
allow(Commit).to receive(:max_diff_options).and_return(max_files: 20, max_lines: 20)
visit diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request) visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
page.within('.alert') do page.within('.alert') do
expect(page).to have_text("Too many changes to show. Plain diff Email patch To preserve expect(page).to have_text("Too many changes to show. Plain diff Email patch To preserve
...@@ -36,4 +32,35 @@ feature 'Diffs URL', js: true, feature: true do ...@@ -36,4 +32,35 @@ feature 'Diffs URL', js: true, feature: true do
end end
end end
end end
context 'when editing file' do
let(:author_user) { create(:user) }
let(:user) { create(:user) }
let(:forked_project) { Projects::ForkService.new(project, author_user).execute }
let(:merge_request) { create(:merge_request_with_diffs, source_project: forked_project, target_project: project, author: author_user) }
let(:changelog_id) { Digest::SHA1.hexdigest("CHANGELOG") }
context 'as author' do
it 'shows direct edit link' do
login_as(author_user)
visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
# Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax
expect(page).to have_selector("[id=\"#{changelog_id}\"] a.js-edit-blob")
end
end
context 'as user who needs to fork' do
it 'shows fork/cancel confirmation' do
login_as(user)
visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
# Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax
find("[id=\"#{changelog_id}\"] .js-edit-blob").click
expect(page).to have_selector('.js-fork-suggestion-button', count: 1)
expect(page).to have_selector('.js-cancel-fork-suggestion-button', count: 1)
end
end
end
end end
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
"avatar_url", "avatar_url",
"web_url", "web_url",
"created_at", "created_at",
"is_admin",
"bio", "bio",
"location", "location",
"skype", "skype",
...@@ -43,7 +42,6 @@ ...@@ -43,7 +42,6 @@
"avatar_url": { "type": "string" }, "avatar_url": { "type": "string" },
"web_url": { "type": "string" }, "web_url": { "type": "string" },
"created_at": { "type": "date" }, "created_at": { "type": "date" },
"is_admin": { "type": "boolean" },
"bio": { "type": ["string", "null"] }, "bio": { "type": ["string", "null"] },
"location": { "type": ["string", "null"] }, "location": { "type": ["string", "null"] },
"skype": { "type": "string" }, "skype": { "type": "string" },
......
...@@ -108,6 +108,18 @@ describe Gitlab::LDAP::User, lib: true do ...@@ -108,6 +108,18 @@ describe Gitlab::LDAP::User, lib: true do
it "creates a new user if not found" do it "creates a new user if not found" do
expect{ ldap_user.save }.to change{ User.count }.by(1) expect{ ldap_user.save }.to change{ User.count }.by(1)
end end
context 'when signup is disabled' do
before do
stub_application_setting signup_enabled: false
end
it 'creates the user' do
ldap_user.save
expect(gl_user).to be_persisted
end
end
end end
describe 'updating email' do describe 'updating email' do
......
...@@ -40,6 +40,20 @@ describe Gitlab::OAuth::User, lib: true do ...@@ -40,6 +40,20 @@ describe Gitlab::OAuth::User, lib: true do
let(:provider) { 'twitter' } let(:provider) { 'twitter' }
describe 'signup' do describe 'signup' do
context 'when signup is disabled' do
before do
stub_application_setting signup_enabled: false
end
it 'creates the user' do
stub_omniauth_config(allow_single_sign_on: ['twitter'])
oauth_user.save
expect(gl_user).to be_persisted
end
end
it 'marks user as having password_automatically_set' do it 'marks user as having password_automatically_set' do
stub_omniauth_config(allow_single_sign_on: ['twitter'], external_providers: ['twitter']) stub_omniauth_config(allow_single_sign_on: ['twitter'], external_providers: ['twitter'])
......
...@@ -211,6 +211,18 @@ describe Gitlab::Saml::User, lib: true do ...@@ -211,6 +211,18 @@ describe Gitlab::Saml::User, lib: true do
end end
end end
end end
context 'when signup is disabled' do
before do
stub_application_setting signup_enabled: false
end
it 'creates the user' do
saml_user.save
expect(gl_user).to be_persisted
end
end
end end
describe 'blocking' do describe 'blocking' do
......
...@@ -199,10 +199,10 @@ describe MergeRequest, models: true do ...@@ -199,10 +199,10 @@ describe MergeRequest, models: true do
end end
context 'when there are no MR diffs' do context 'when there are no MR diffs' do
it 'delegates to the compare object' do it 'delegates to the compare object, setting no_collapse: true' do
merge_request.compare = double(:compare) merge_request.compare = double(:compare)
expect(merge_request.compare).to receive(:diffs).with(options) expect(merge_request.compare).to receive(:diffs).with(options.merge(no_collapse: true))
merge_request.diffs(options) merge_request.diffs(options)
end end
...@@ -215,15 +215,22 @@ describe MergeRequest, models: true do ...@@ -215,15 +215,22 @@ describe MergeRequest, models: true do
end end
context 'when there are MR diffs' do context 'when there are MR diffs' do
before do it 'returns the correct count' do
merge_request.save merge_request.save
expect(merge_request.diff_size).to eq('105')
end end
it 'returns the correct count' do it 'returns the correct overflow count' do
expect(merge_request.diff_size).to eq(105) allow(Commit).to receive(:max_diff_options).and_return(max_files: 2)
merge_request.save
expect(merge_request.diff_size).to eq('2+')
end end
it 'does not perform highlighting' do it 'does not perform highlighting' do
merge_request.save
expect(Gitlab::Diff::Highlight).not_to receive(:new) expect(Gitlab::Diff::Highlight).not_to receive(:new)
merge_request.diff_size merge_request.diff_size
...@@ -231,7 +238,7 @@ describe MergeRequest, models: true do ...@@ -231,7 +238,7 @@ describe MergeRequest, models: true do
end end
context 'when there are no MR diffs' do context 'when there are no MR diffs' do
before do def set_compare(merge_request)
merge_request.compare = CompareService.new( merge_request.compare = CompareService.new(
merge_request.source_project, merge_request.source_project,
merge_request.source_branch merge_request.source_branch
...@@ -242,10 +249,21 @@ describe MergeRequest, models: true do ...@@ -242,10 +249,21 @@ describe MergeRequest, models: true do
end end
it 'returns the correct count' do it 'returns the correct count' do
expect(merge_request.diff_size).to eq(105) set_compare(merge_request)
expect(merge_request.diff_size).to eq('105')
end
it 'returns the correct overflow count' do
allow(Commit).to receive(:max_diff_options).and_return(max_files: 2)
set_compare(merge_request)
expect(merge_request.diff_size).to eq('2+')
end end
it 'does not perform highlighting' do it 'does not perform highlighting' do
set_compare(merge_request)
expect(Gitlab::Diff::Highlight).not_to receive(:new) expect(Gitlab::Diff::Highlight).not_to receive(:new)
merge_request.diff_size merge_request.diff_size
......
...@@ -32,6 +32,12 @@ describe API::Keys do ...@@ -32,6 +32,12 @@ describe API::Keys do
expect(json_response['user']['id']).to eq(user.id) expect(json_response['user']['id']).to eq(user.id)
expect(json_response['user']['username']).to eq(user.username) expect(json_response['user']['username']).to eq(user.username)
end end
it "does not include the user's `is_admin` flag" do
get api("/keys/#{key.id}", admin)
expect(json_response['user']['is_admin']).to be_nil
end
end end
end end
end end
...@@ -135,6 +135,12 @@ describe API::Users do ...@@ -135,6 +135,12 @@ describe API::Users do
expect(json_response['username']).to eq(user.username) expect(json_response['username']).to eq(user.username)
end end
it "does not return the user's `is_admin` flag" do
get api("/users/#{user.id}", user)
expect(json_response['is_admin']).to be_nil
end
it "returns a 401 if unauthenticated" do it "returns a 401 if unauthenticated" do
get api("/users/9998") get api("/users/9998")
expect(response).to have_http_status(401) expect(response).to have_http_status(401)
...@@ -397,7 +403,6 @@ describe API::Users do ...@@ -397,7 +403,6 @@ describe API::Users do
it "updates admin status" do it "updates admin status" do
put api("/users/#{user.id}", admin), { admin: true } put api("/users/#{user.id}", admin), { admin: true }
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response['is_admin']).to eq(true)
expect(user.reload.admin).to eq(true) expect(user.reload.admin).to eq(true)
end end
...@@ -411,7 +416,6 @@ describe API::Users do ...@@ -411,7 +416,6 @@ describe API::Users do
it "does not update admin status" do it "does not update admin status" do
put api("/users/#{admin_user.id}", admin), { can_create_group: false } put api("/users/#{admin_user.id}", admin), { can_create_group: false }
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response['is_admin']).to eq(true)
expect(admin_user.reload.admin).to eq(true) expect(admin_user.reload.admin).to eq(true)
expect(admin_user.can_create_group).to eq(false) expect(admin_user.can_create_group).to eq(false)
end end
......
...@@ -274,5 +274,11 @@ describe API::V3::Users do ...@@ -274,5 +274,11 @@ describe API::V3::Users do
expect(new_user).to be_confirmed expect(new_user).to be_confirmed
end end
it 'does not reveal the `is_admin` flag of the user' do
post v3_api('/users', admin), attributes_for(:user)
expect(json_response['is_admin']).to be_nil
end
end end
end end
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