Commit f001097f authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'ce-to-ee' into 'master'

CE Upstream - Monday

See merge request !2453
parents 9bd93670 63174d23
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
## Test coverage ## Test coverage
- [![Ruby coverage](https://gitlab.com/gitlab-org/gitlab-ee/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ee/coverage-ruby) Ruby - [![Ruby coverage](https://gitlab.com/gitlab-org/gitlab-ee/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ee/coverage-ruby) Ruby
- [![JavaScript coverage](https://gitlab.com/gitlab-org/gitlab-ee/badges/master/coverage.svg?job=rake+karma)](https://gitlab-org.gitlab.io/gitlab-ee/coverage-javascript) JavaScript - [![JavaScript coverage](https://gitlab.com/gitlab-org/gitlab-ee/badges/master/coverage.svg?job=karma)](https://gitlab-org.gitlab.io/gitlab-ee/coverage-javascript) JavaScript
## Canonical source ## Canonical source
......
...@@ -149,7 +149,6 @@ import './star'; ...@@ -149,7 +149,6 @@ import './star';
import './subscription'; import './subscription';
import './subscription_select'; import './subscription_select';
import './syntax_highlight'; import './syntax_highlight';
import './user';
// EE-only scripts // EE-only scripts
import './admin_email_select'; import './admin_email_select';
......
/* eslint-disable func-names, space-before-function-paren, no-var, wrap-iife, camelcase, vars-on-top, object-shorthand, comma-dangle, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, max-len, class-methods-use-this */
import d3 from 'd3';
export default class ActivityCalendar {
constructor(timestamps, calendar_activities_path) {
this.calendar_activities_path = calendar_activities_path;
this.clickDay = this.clickDay.bind(this);
this.currentSelectedDate = '';
this.daySpace = 1;
this.daySize = 15;
this.daySizeWithSpace = this.daySize + (this.daySpace * 2);
this.monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
this.months = [];
// Loop through the timestamps to create a group of objects
// The group of objects will be grouped based on the day of the week they are
this.timestampsTmp = [];
var group = 0;
var today = new Date();
today.setHours(0, 0, 0, 0, 0);
var oneYearAgo = new Date(today);
oneYearAgo.setFullYear(today.getFullYear() - 1);
var days = gl.utils.getDayDifference(oneYearAgo, today);
for (var i = 0; i <= days; i += 1) {
var date = new Date(oneYearAgo);
date.setDate(date.getDate() + i);
var day = date.getDay();
var count = timestamps[date.format('yyyy-mm-dd')];
// Create a new group array if this is the first day of the week
// or if is first object
if ((day === 0 && i !== 0) || i === 0) {
this.timestampsTmp.push([]);
group += 1;
}
var innerArray = this.timestampsTmp[group - 1];
// Push to the inner array the values that will be used to render map
innerArray.push({
count: count || 0,
date: date,
day: day
});
}
// Init color functions
this.colorKey = this.initColorKey();
this.color = this.initColor();
// Init the svg element
this.renderSvg(group);
this.renderDays();
this.renderMonths();
this.renderDayTitles();
this.renderKey();
this.initTooltips();
}
// Add extra padding for the last month label if it is also the last column
getExtraWidthPadding(group) {
var extraWidthPadding = 0;
var lastColMonth = this.timestampsTmp[group - 1][0].date.getMonth();
var secondLastColMonth = this.timestampsTmp[group - 2][0].date.getMonth();
if (lastColMonth != secondLastColMonth) {
extraWidthPadding = 3;
}
return extraWidthPadding;
}
renderSvg(group) {
var width = (group + 1) * this.daySizeWithSpace + this.getExtraWidthPadding(group);
return this.svg = d3.select('.js-contrib-calendar').append('svg').attr('width', width).attr('height', 167).attr('class', 'contrib-calendar');
}
renderDays() {
return this.svg.selectAll('g').data(this.timestampsTmp).enter().append('g').attr('transform', (function(_this) {
return function(group, i) {
_.each(group, function(stamp, a) {
var lastMonth, lastMonthX, month, x;
if (a === 0 && stamp.day === 0) {
month = stamp.date.getMonth();
x = (_this.daySizeWithSpace * i + 1) + _this.daySizeWithSpace;
lastMonth = _.last(_this.months);
if (lastMonth != null) {
lastMonthX = lastMonth.x;
}
if (lastMonth == null) {
return _this.months.push({
month: month,
x: x
});
} else if (month !== lastMonth.month && x - _this.daySizeWithSpace !== lastMonthX) {
return _this.months.push({
month: month,
x: x
});
}
}
});
return "translate(" + ((_this.daySizeWithSpace * i + 1) + _this.daySizeWithSpace) + ", 18)";
};
})(this)).selectAll('rect').data(function(stamp) {
return stamp;
}).enter().append('rect').attr('x', '0').attr('y', (function(_this) {
return function(stamp, i) {
return _this.daySizeWithSpace * stamp.day;
};
})(this)).attr('width', this.daySize).attr('height', this.daySize).attr('title', (function(_this) {
return function(stamp) {
var contribText, date, dateText;
date = new Date(stamp.date);
contribText = 'No contributions';
if (stamp.count > 0) {
contribText = stamp.count + " contribution" + (stamp.count > 1 ? 's' : '');
}
dateText = date.format('mmm d, yyyy');
return contribText + "<br />" + (gl.utils.getDayName(date)) + " " + dateText;
};
})(this)).attr('class', 'user-contrib-cell js-tooltip').attr('fill', (function(_this) {
return function(stamp) {
if (stamp.count !== 0) {
return _this.color(Math.min(stamp.count, 40));
} else {
return '#ededed';
}
};
})(this)).attr('data-container', 'body').on('click', this.clickDay);
}
renderDayTitles() {
var days;
days = [
{
text: 'M',
y: 29 + (this.daySizeWithSpace * 1)
}, {
text: 'W',
y: 29 + (this.daySizeWithSpace * 3)
}, {
text: 'F',
y: 29 + (this.daySizeWithSpace * 5)
}
];
return this.svg.append('g').selectAll('text').data(days).enter().append('text').attr('text-anchor', 'middle').attr('x', 8).attr('y', function(day) {
return day.y;
}).text(function(day) {
return day.text;
}).attr('class', 'user-contrib-text');
}
renderMonths() {
return this.svg.append('g').attr('direction', 'ltr').selectAll('text').data(this.months).enter().append('text').attr('x', function(date) {
return date.x;
}).attr('y', 10).attr('class', 'user-contrib-text').text((function(_this) {
return function(date) {
return _this.monthNames[date.month];
};
})(this));
}
renderKey() {
const keyValues = ['no contributions', '1-9 contributions', '10-19 contributions', '20-29 contributions', '30+ contributions'];
const keyColors = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)];
this.svg.append('g')
.attr('transform', `translate(18, ${this.daySizeWithSpace * 8 + 16})`)
.selectAll('rect')
.data(keyColors)
.enter()
.append('rect')
.attr('width', this.daySize)
.attr('height', this.daySize)
.attr('x', (color, i) => this.daySizeWithSpace * i)
.attr('y', 0)
.attr('fill', color => color)
.attr('class', 'js-tooltip')
.attr('title', (color, i) => keyValues[i])
.attr('data-container', 'body');
}
initColor() {
var colorRange;
colorRange = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)];
return d3.scale.threshold().domain([0, 10, 20, 30]).range(colorRange);
}
initColorKey() {
return d3.scale.linear().range(['#acd5f2', '#254e77']).domain([0, 3]);
}
clickDay(stamp) {
var formatted_date;
if (this.currentSelectedDate !== stamp.date) {
this.currentSelectedDate = stamp.date;
formatted_date = this.currentSelectedDate.getFullYear() + "-" + (this.currentSelectedDate.getMonth() + 1) + "-" + this.currentSelectedDate.getDate();
return $.ajax({
url: this.calendar_activities_path,
data: {
date: formatted_date
},
cache: false,
dataType: 'html',
beforeSend: function() {
return $('.user-calendar-activities').html('<div class="text-center"><i class="fa fa-spinner fa-spin user-calendar-activities-loading"></i></div>');
},
success: function(data) {
return $('.user-calendar-activities').html(data);
}
});
} else {
this.currentSelectedDate = '';
return $('.user-calendar-activities').html('');
}
}
initTooltips() {
return $('.js-contrib-calendar .js-tooltip').tooltip({
html: true
});
}
}
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, camelcase, vars-on-top, object-shorthand, comma-dangle, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, max-len */
import d3 from 'd3';
(function() {
this.Calendar = (function() {
function Calendar(timestamps, calendar_activities_path) {
this.calendar_activities_path = calendar_activities_path;
this.clickDay = this.clickDay.bind(this);
this.currentSelectedDate = '';
this.daySpace = 1;
this.daySize = 15;
this.daySizeWithSpace = this.daySize + (this.daySpace * 2);
this.monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
this.months = [];
// Loop through the timestamps to create a group of objects
// The group of objects will be grouped based on the day of the week they are
this.timestampsTmp = [];
var group = 0;
var today = new Date();
today.setHours(0, 0, 0, 0, 0);
var oneYearAgo = new Date(today);
oneYearAgo.setFullYear(today.getFullYear() - 1);
var days = gl.utils.getDayDifference(oneYearAgo, today);
for (var i = 0; i <= days; i += 1) {
var date = new Date(oneYearAgo);
date.setDate(date.getDate() + i);
var day = date.getDay();
var count = timestamps[date.format('yyyy-mm-dd')];
// Create a new group array if this is the first day of the week
// or if is first object
if ((day === 0 && i !== 0) || i === 0) {
this.timestampsTmp.push([]);
group += 1;
}
var innerArray = this.timestampsTmp[group - 1];
// Push to the inner array the values that will be used to render map
innerArray.push({
count: count || 0,
date: date,
day: day
});
}
// Init color functions
this.colorKey = this.initColorKey();
this.color = this.initColor();
// Init the svg element
this.renderSvg(group);
this.renderDays();
this.renderMonths();
this.renderDayTitles();
this.renderKey();
this.initTooltips();
}
// Add extra padding for the last month label if it is also the last column
Calendar.prototype.getExtraWidthPadding = function(group) {
var extraWidthPadding = 0;
var lastColMonth = this.timestampsTmp[group - 1][0].date.getMonth();
var secondLastColMonth = this.timestampsTmp[group - 2][0].date.getMonth();
if (lastColMonth != secondLastColMonth) {
extraWidthPadding = 3;
}
return extraWidthPadding;
};
Calendar.prototype.renderSvg = function(group) {
var width = (group + 1) * this.daySizeWithSpace + this.getExtraWidthPadding(group);
return this.svg = d3.select('.js-contrib-calendar').append('svg').attr('width', width).attr('height', 167).attr('class', 'contrib-calendar');
};
Calendar.prototype.renderDays = function() {
return this.svg.selectAll('g').data(this.timestampsTmp).enter().append('g').attr('transform', (function(_this) {
return function(group, i) {
_.each(group, function(stamp, a) {
var lastMonth, lastMonthX, month, x;
if (a === 0 && stamp.day === 0) {
month = stamp.date.getMonth();
x = (_this.daySizeWithSpace * i + 1) + _this.daySizeWithSpace;
lastMonth = _.last(_this.months);
if (lastMonth != null) {
lastMonthX = lastMonth.x;
}
if (lastMonth == null) {
return _this.months.push({
month: month,
x: x
});
} else if (month !== lastMonth.month && x - _this.daySizeWithSpace !== lastMonthX) {
return _this.months.push({
month: month,
x: x
});
}
}
});
return "translate(" + ((_this.daySizeWithSpace * i + 1) + _this.daySizeWithSpace) + ", 18)";
};
})(this)).selectAll('rect').data(function(stamp) {
return stamp;
}).enter().append('rect').attr('x', '0').attr('y', (function(_this) {
return function(stamp, i) {
return _this.daySizeWithSpace * stamp.day;
};
})(this)).attr('width', this.daySize).attr('height', this.daySize).attr('title', (function(_this) {
return function(stamp) {
var contribText, date, dateText;
date = new Date(stamp.date);
contribText = 'No contributions';
if (stamp.count > 0) {
contribText = stamp.count + " contribution" + (stamp.count > 1 ? 's' : '');
}
dateText = date.format('mmm d, yyyy');
return contribText + "<br />" + (gl.utils.getDayName(date)) + " " + dateText;
};
})(this)).attr('class', 'user-contrib-cell js-tooltip').attr('fill', (function(_this) {
return function(stamp) {
if (stamp.count !== 0) {
return _this.color(Math.min(stamp.count, 40));
} else {
return '#ededed';
}
};
})(this)).attr('data-container', 'body').on('click', this.clickDay);
};
Calendar.prototype.renderDayTitles = function() {
var days;
days = [
{
text: 'M',
y: 29 + (this.daySizeWithSpace * 1)
}, {
text: 'W',
y: 29 + (this.daySizeWithSpace * 3)
}, {
text: 'F',
y: 29 + (this.daySizeWithSpace * 5)
}
];
return this.svg.append('g').selectAll('text').data(days).enter().append('text').attr('text-anchor', 'middle').attr('x', 8).attr('y', function(day) {
return day.y;
}).text(function(day) {
return day.text;
}).attr('class', 'user-contrib-text');
};
Calendar.prototype.renderMonths = function() {
return this.svg.append('g').attr('direction', 'ltr').selectAll('text').data(this.months).enter().append('text').attr('x', function(date) {
return date.x;
}).attr('y', 10).attr('class', 'user-contrib-text').text((function(_this) {
return function(date) {
return _this.monthNames[date.month];
};
})(this));
};
Calendar.prototype.renderKey = function() {
const keyValues = ['no contributions', '1-9 contributions', '10-19 contributions', '20-29 contributions', '30+ contributions'];
const keyColors = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)];
this.svg.append('g')
.attr('transform', `translate(18, ${this.daySizeWithSpace * 8 + 16})`)
.selectAll('rect')
.data(keyColors)
.enter()
.append('rect')
.attr('width', this.daySize)
.attr('height', this.daySize)
.attr('x', (color, i) => this.daySizeWithSpace * i)
.attr('y', 0)
.attr('fill', color => color)
.attr('class', 'js-tooltip')
.attr('title', (color, i) => keyValues[i])
.attr('data-container', 'body');
};
Calendar.prototype.initColor = function() {
var colorRange;
colorRange = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)];
return d3.scale.threshold().domain([0, 10, 20, 30]).range(colorRange);
};
Calendar.prototype.initColorKey = function() {
return d3.scale.linear().range(['#acd5f2', '#254e77']).domain([0, 3]);
};
Calendar.prototype.clickDay = function(stamp) {
var formatted_date;
if (this.currentSelectedDate !== stamp.date) {
this.currentSelectedDate = stamp.date;
formatted_date = this.currentSelectedDate.getFullYear() + "-" + (this.currentSelectedDate.getMonth() + 1) + "-" + this.currentSelectedDate.getDate();
return $.ajax({
url: this.calendar_activities_path,
data: {
date: formatted_date
},
cache: false,
dataType: 'html',
beforeSend: function() {
return $('.user-calendar-activities').html('<div class="text-center"><i class="fa fa-spinner fa-spin user-calendar-activities-loading"></i></div>');
},
success: function(data) {
return $('.user-calendar-activities').html(data);
}
});
} else {
this.currentSelectedDate = '';
return $('.user-calendar-activities').html('');
}
};
Calendar.prototype.initTooltips = function() {
return $('.js-contrib-calendar .js-tooltip').tooltip({
html: true
});
};
return Calendar;
})();
}).call(window);
import ActivityCalendar from './activity_calendar';
import User from './user';
// use legacy exports until embedded javascript is refactored
window.Calendar = ActivityCalendar;
window.gl = window.gl || {};
window.gl.User = User;
/* eslint-disable class-methods-use-this, comma-dangle, arrow-parens, no-param-reassign */ /* eslint-disable class-methods-use-this */
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import UserTabs from './user_tabs'; import UserTabs from './user_tabs';
class User { export default class User {
constructor({ action }) { constructor({ action }) {
this.action = action; this.action = action;
this.placeProfileAvatarsToTop(); this.placeProfileAvatarsToTop();
...@@ -13,25 +13,22 @@ class User { ...@@ -13,25 +13,22 @@ class User {
placeProfileAvatarsToTop() { placeProfileAvatarsToTop() {
$('.profile-groups-avatars').tooltip({ $('.profile-groups-avatars').tooltip({
placement: 'top' placement: 'top',
}); });
} }
initTabs() { initTabs() {
return new UserTabs({ return new UserTabs({
parentEl: '.user-profile', parentEl: '.user-profile',
action: this.action action: this.action,
}); });
} }
hideProjectLimitMessage() { hideProjectLimitMessage() {
$('.hide-project-limit-message').on('click', e => { $('.hide-project-limit-message').on('click', (e) => {
e.preventDefault(); e.preventDefault();
Cookies.set('hide_project_limit_message', 'false'); Cookies.set('hide_project_limit_message', 'false');
$(this).parents('.project-limit-message').remove(); $(this).parents('.project-limit-message').remove();
}); });
} }
} }
window.gl = window.gl || {};
window.gl.User = User;
...@@ -110,10 +110,10 @@ $well-light-text-color: #5b6169; ...@@ -110,10 +110,10 @@ $well-light-text-color: #5b6169;
* Text * Text
*/ */
$gl-font-size: 14px; $gl-font-size: 14px;
$gl-text-color: rgba(0, 0, 0, .85); $gl-text-color: #2e2e2e;
$gl-text-color-light: rgba(0, 0, 0, .7); $gl-text-color-secondary: #707070;
$gl-text-color-secondary: rgba(0, 0, 0, .55); $gl-text-color-tertiary: #949494;
$gl-text-color-disabled: rgba(0, 0, 0, .35); $gl-text-color-quaternary: #d6d6d6;
$gl-text-color-inverted: rgba(255, 255, 255, 1.0); $gl-text-color-inverted: rgba(255, 255, 255, 1.0);
$gl-text-color-secondary-inverted: rgba(255, 255, 255, .85); $gl-text-color-secondary-inverted: rgba(255, 255, 255, .85);
$gl-text-green: $green-600; $gl-text-green: $green-600;
...@@ -127,7 +127,7 @@ $gl-gray-dark: #313236; ...@@ -127,7 +127,7 @@ $gl-gray-dark: #313236;
$gl-gray-light: #5c5c5c; $gl-gray-light: #5c5c5c;
$gl-header-color: #4c4e54; $gl-header-color: #4c4e54;
$gl-header-nav-hover-color: #434343; $gl-header-nav-hover-color: #434343;
$placeholder-text-color: rgba(0, 0, 0, .42); $placeholder-text-color: $gl-text-color-tertiary;
/* /*
* Lists * Lists
...@@ -135,7 +135,7 @@ $placeholder-text-color: rgba(0, 0, 0, .42); ...@@ -135,7 +135,7 @@ $placeholder-text-color: rgba(0, 0, 0, .42);
$list-font-size: $gl-font-size; $list-font-size: $gl-font-size;
$list-title-color: $gl-text-color; $list-title-color: $gl-text-color;
$list-text-color: $gl-text-color; $list-text-color: $gl-text-color;
$list-text-disabled-color: $gl-text-color-disabled; $list-text-disabled-color: $gl-text-color-tertiary;
$list-border-light: #eee; $list-border-light: #eee;
$list-border: rgba(0, 0, 0, 0.05); $list-border: rgba(0, 0, 0, 0.05);
$list-text-height: 42px; $list-text-height: 42px;
......
...@@ -284,7 +284,7 @@ header.navbar-gitlab-new { ...@@ -284,7 +284,7 @@ header.navbar-gitlab-new {
position: relative; position: relative;
top: -1px; top: -1px;
padding: 0 5px; padding: 0 5px;
color: rgba($black, .65); color: $gl-text-color-secondary;
font-size: 10px; font-size: 10px;
line-height: 1; line-height: 1;
background: none; background: none;
...@@ -310,10 +310,10 @@ header.navbar-gitlab-new { ...@@ -310,10 +310,10 @@ header.navbar-gitlab-new {
.breadcrumbs-links { .breadcrumbs-links {
flex: 1; flex: 1;
align-self: center; align-self: center;
color: $black-transparent; color: $gl-text-color-quaternary;
a { a {
color: rgba($black, .65); color: $gl-text-color-secondary;
&:not(:first-child), &:not(:first-child),
&.group-path { &.group-path {
...@@ -368,9 +368,10 @@ header.navbar-gitlab-new { ...@@ -368,9 +368,10 @@ header.navbar-gitlab-new {
} }
.breadcrumbs-sub-title { .breadcrumbs-sub-title {
margin: 2px 0 0; margin: 2px 0;
font-size: 16px; font-size: 16px;
font-weight: normal; font-weight: normal;
line-height: 1;
ul { ul {
margin: 0; margin: 0;
......
...@@ -35,6 +35,7 @@ $new-sidebar-width: 220px; ...@@ -35,6 +35,7 @@ $new-sidebar-width: 220px;
.avatar-container { .avatar-container {
flex: 0 0 40px; flex: 0 0 40px;
background-color: $white-light;
} }
&:hover { &:hover {
......
...@@ -315,7 +315,7 @@ ...@@ -315,7 +315,7 @@
text { text {
font-size: 13px; font-size: 13px;
fill: $gl-text-color-disabled; fill: $gl-text-color-tertiary;
} }
rect { rect {
......
...@@ -5,10 +5,10 @@ class AutocompleteController < ApplicationController ...@@ -5,10 +5,10 @@ class AutocompleteController < ApplicationController
def users def users
@users = @users.non_ldap if params[:skip_ldap] == 'true' @users = @users.non_ldap if params[:skip_ldap] == 'true'
@users = @users.search(params[:search]) if params[:search].present?
@users = @users.where.not(id: params[:skip_users]) if params[:skip_users].present?
@users = @users.active @users = @users.active
@users = @users.reorder(:name) @users = @users.reorder(:name)
@users = @users.search(params[:search]) if params[:search].present?
@users = @users.where.not(id: params[:skip_users]) if params[:skip_users].present?
@users = load_users_by_ability || @users.page(params[:page]).per(params[:per_page]) @users = load_users_by_ability || @users.page(params[:page]).per(params[:per_page])
if params[:todo_filter].present? && current_user if params[:todo_filter].present? && current_user
......
...@@ -50,10 +50,13 @@ class ProjectsController < Projects::ApplicationController ...@@ -50,10 +50,13 @@ class ProjectsController < Projects::ApplicationController
respond_to do |format| respond_to do |format|
if result[:status] == :success if result[:status] == :success
flash[:notice] = _("Project '%{project_name}' was successfully updated.") % { project_name: @project.name } flash[:notice] = _("Project '%{project_name}' was successfully updated.") % { project_name: @project.name }
format.html do format.html do
redirect_to(edit_project_path(@project)) redirect_to(edit_project_path(@project))
end end
else else
flash[:alert] = result[:message]
format.html { render 'edit' } format.html { render 'edit' }
end end
......
...@@ -49,7 +49,7 @@ class SessionsController < Devise::SessionsController ...@@ -49,7 +49,7 @@ class SessionsController < Devise::SessionsController
private private
def login_counter def login_counter
@login_counter ||= Gitlab::Metrics.counter(:user_session_logins, 'User sign in count') @login_counter ||= Gitlab::Metrics.counter(:user_session_logins_total, 'User sign in count')
end end
# Handle an "initial setup" state, where there's only one user, it's an admin, # Handle an "initial setup" state, where there's only one user, it's an admin,
......
...@@ -485,7 +485,9 @@ class Project < ActiveRecord::Base ...@@ -485,7 +485,9 @@ class Project < ActiveRecord::Base
end end
def has_container_registry_tags? def has_container_registry_tags?
container_repositories.to_a.any?(&:has_tags?) || return @images if defined?(@images)
@images = container_repositories.to_a.any?(&:has_tags?) ||
has_root_container_repository_tags? has_root_container_repository_tags?
end end
...@@ -976,8 +978,6 @@ class Project < ActiveRecord::Base ...@@ -976,8 +978,6 @@ class Project < ActiveRecord::Base
Rails.logger.error "Attempting to rename #{old_path_with_namespace} -> #{new_path_with_namespace}" Rails.logger.error "Attempting to rename #{old_path_with_namespace} -> #{new_path_with_namespace}"
expire_caches_before_rename(old_path_with_namespace)
if has_container_registry_tags? if has_container_registry_tags?
Rails.logger.error "Project #{old_path_with_namespace} cannot be renamed because container registry tags are present!" Rails.logger.error "Project #{old_path_with_namespace} cannot be renamed because container registry tags are present!"
...@@ -985,6 +985,8 @@ class Project < ActiveRecord::Base ...@@ -985,6 +985,8 @@ class Project < ActiveRecord::Base
raise StandardError.new('Project cannot be renamed, because images are present in its container registry') raise StandardError.new('Project cannot be renamed, because images are present in its container registry')
end end
expire_caches_before_rename(old_path_with_namespace)
if gitlab_shell.mv_repository(repository_storage_path, old_path_with_namespace, new_path_with_namespace) if gitlab_shell.mv_repository(repository_storage_path, old_path_with_namespace, new_path_with_namespace)
# If repository moved successfully we need to send update instructions to users. # If repository moved successfully we need to send update instructions to users.
# However we cannot allow rollback since we moved repository # However we cannot allow rollback since we moved repository
......
...@@ -328,7 +328,7 @@ class User < ActiveRecord::Base ...@@ -328,7 +328,7 @@ class User < ActiveRecord::Base
table[:name].matches(pattern) table[:name].matches(pattern)
.or(table[:email].matches(pattern)) .or(table[:email].matches(pattern))
.or(table[:username].matches(pattern)) .or(table[:username].matches(pattern))
).reorder(order % { query: ActiveRecord::Base.connection.quote(query) }, id: :desc) ).reorder(order % { query: ActiveRecord::Base.connection.quote(query) }, :name)
end end
# searches user by given pattern # searches user by given pattern
......
module Projects module Projects
class UpdateService < BaseService class UpdateService < BaseService
def execute def execute
# check that user is allowed to set specified visibility_level
new_visibility = params[:visibility_level]
if new_visibility && new_visibility.to_i != project.visibility_level
unless can?(current_user, :change_visibility_level, project) &&
Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
deny_visibility_level(project, new_visibility)
return error('Visibility level unallowed')
end
end
# Repository size limit comes as MB from the view # Repository size limit comes as MB from the view
limit = params.delete(:repository_size_limit) limit = params.delete(:repository_size_limit)
project.repository_size_limit = Gitlab::Utils.try_megabytes_to_bytes(limit) if limit project.repository_size_limit = Gitlab::Utils.try_megabytes_to_bytes(limit) if limit
new_branch = params.delete(:default_branch) unless visibility_level_allowed?
new_repository_storage = params.delete(:repository_storage) return error('New visibility level not allowed!')
end
if project.repository.exists? if changing_storage_size?
if new_branch && new_branch != project.default_branch project.change_repository_storage(params.delete(:repository_storage))
project.change_head(new_branch) end
end
if new_repository_storage && can?(current_user, :change_repository_storage, project) if project.has_container_registry_tags?
project.change_repository_storage(new_repository_storage) return error('Cannot rename project because it contains container registry tags!')
end
end end
if project.update_attributes(params) if changing_default_branch?
project.change_head(params[:default_branch])
end
if project.update_attributes(params.except(:default_branch))
if project.previous_changes.include?('path') if project.previous_changes.include?('path')
project.rename_repo project.rename_repo
else else
...@@ -39,8 +30,40 @@ module Projects ...@@ -39,8 +30,40 @@ module Projects
success success
else else
error('Project could not be updated') error('Project could not be updated!')
end end
end end
private
def visibility_level_allowed?
# check that user is allowed to set specified visibility_level
new_visibility = params[:visibility_level]
if new_visibility && new_visibility.to_i != project.visibility_level
unless can?(current_user, :change_visibility_level, project) &&
Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
deny_visibility_level(project, new_visibility)
return false
end
end
true
end
def changing_storage_size?
new_repository_storage = params[:repository_storage]
new_repository_storage && project.repository.exists? &&
can?(current_user, :change_repository_storage, project)
end
def changing_default_branch?
new_branch = params[:default_branch]
new_branch && project.repository.exists? &&
new_branch != project.default_branch
end
end end
end end
---
title: Exact matches of username and email are now on top of the user search
merge_request: 12868
author:
---
title: Recover from renaming project that has container images
merge_request: 12840
author:
---
title: Pass before_script and script as-is preserving arrays
merge_request:
author:
...@@ -145,7 +145,7 @@ Gitlab::Metrics::UnicornSampler.initialize_instance(Settings.monitoring.unicorn_ ...@@ -145,7 +145,7 @@ Gitlab::Metrics::UnicornSampler.initialize_instance(Settings.monitoring.unicorn_
Gitlab::Application.configure do |config| Gitlab::Application.configure do |config|
# 0 should be Sentry to catch errors in this middleware # 0 should be Sentry to catch errors in this middleware
config.middleware.insert(1, Gitlab::Metrics::ConnectionRackMiddleware) config.middleware.insert(1, Gitlab::Metrics::RequestsRackMiddleware)
end end
if Gitlab::Metrics.enabled? if Gitlab::Metrics.enabled?
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
- aws_elb_request_count_sum - aws_elb_request_count_sum
weight: 1 weight: 1
queries: queries:
- query_range: 'sum(aws_elb_request_count_sum{%{environment_filter}}) * 60' - query_range: 'sum(aws_elb_request_count_sum{%{environment_filter}}) / 60'
label: Total label: Total
unit: req / sec unit: req / sec
- title: "Latency" - title: "Latency"
......
...@@ -72,7 +72,7 @@ var config = { ...@@ -72,7 +72,7 @@ var config = {
stl_viewer: './blob/stl_viewer.js', stl_viewer: './blob/stl_viewer.js',
terminal: './terminal/terminal_bundle.js', terminal: './terminal/terminal_bundle.js',
u2f: ['vendor/u2f'], u2f: ['vendor/u2f'],
users: './users/users_bundle.js', users: './users/index.js',
raven: './raven/index.js', raven: './raven/index.js',
vue_merge_request_widget: './vue_merge_request_widget/index.js', vue_merge_request_widget: './vue_merge_request_widget/index.js',
test: './test.js', test: './test.js',
......
...@@ -250,9 +250,17 @@ Tip: If you want to limit access to the nested members of an Active Directory ...@@ -250,9 +250,17 @@ Tip: If you want to limit access to the nested members of an Active Directory
group you can use the following syntax: group you can use the following syntax:
``` ```
(memberOf=CN=My Group,DC=Example,DC=com) (memberOf:1.2.840.113556.1.4.1941=CN=My Group,DC=Example,DC=com)
``` ```
Find more information about this "LDAP_MATCHING_RULE_IN_CHAIN" filter at
https://msdn.microsoft.com/en-us/library/aa746475(v=vs.85).aspx. Support for
nested members in the user filter should not be confused with
[group sync nested groups support (EE only)](https://docs.gitlab.com/ee/administration/auth/ldap-ee.html#supported-ldap-group-types-attributes).
Please note that GitLab does not support the custom filter syntax used by
omniauth-ldap.
### Escaping special characters ### Escaping special characters
If the `user_filter` DN contains a special characters. For example a comma If the `user_filter` DN contains a special characters. For example a comma
...@@ -271,9 +279,6 @@ As an example the above DN would look like ...@@ -271,9 +279,6 @@ As an example the above DN would look like
OU=GitLab\\5C\\2C Inc,DC=gitlab,DC=com OU=GitLab\\5C\\2C Inc,DC=gitlab,DC=com
``` ```
Please note that GitLab does not support the custom filter syntax used by
omniauth-ldap.
## Enabling LDAP sign-in for existing GitLab users ## Enabling LDAP sign-in for existing GitLab users
When a user signs in to GitLab with LDAP for the first time, and their LDAP When a user signs in to GitLab with LDAP for the first time, and their LDAP
......
...@@ -26,21 +26,24 @@ server, because the embedded server configuration is overwritten once every ...@@ -26,21 +26,24 @@ server, because the embedded server configuration is overwritten once every
In this experimental phase, only a few metrics are available: In this experimental phase, only a few metrics are available:
| Metric | Type | Description | | Metric | Type | Description |
| ------ | ---- | ----------- | | --------------------------------- | --------- | ----------- |
| db_ping_timeout | Gauge | Whether or not the last database ping timed out | | db_ping_timeout | Gauge | Whether or not the last database ping timed out |
| db_ping_success | Gauge | Whether or not the last database ping succeeded | | db_ping_success | Gauge | Whether or not the last database ping succeeded |
| db_ping_latency | Gauge | Round trip time of the database ping | | db_ping_latency_seconds | Gauge | Round trip time of the database ping |
| redis_ping_timeout | Gauge | Whether or not the last redis ping timed out | | filesystem_access_latency_seconds | Gauge | Latency in accessing a specific filesystem |
| redis_ping_success | Gauge | Whether or not the last redis ping succeeded | | filesystem_accessible | Gauge | Whether or not a specific filesystem is accessible |
| redis_ping_latency | Gauge | Round trip time of the redis ping | | filesystem_write_latency_seconds | Gauge | Write latency of a specific filesystem |
| filesystem_access_latency | gauge | Latency in accessing a specific filesystem | | filesystem_writable | Gauge | Whether or not the filesystem is writable |
| filesystem_accessible | gauge | Whether or not a specific filesystem is accessible | | filesystem_read_latency_seconds | Gauge | Read latency of a specific filesystem |
| filesystem_write_latency | gauge | Write latency of a specific filesystem | | filesystem_readable | Gauge | Whether or not the filesystem is readable |
| filesystem_writable | gauge | Whether or not the filesystem is writable | | http_requests_total | Counter | Rack request count |
| filesystem_read_latency | gauge | Read latency of a specific filesystem | | http_request_duration_seconds | Histogram | HTTP response time from rack middleware |
| filesystem_readable | gauge | Whether or not the filesystem is readable | | rack_uncaught_errors_total | Counter | Rack connections handling uncaught errors count |
| user_sessions_logins | Counter | Counter of how many users have logged in | | redis_ping_timeout | Gauge | Whether or not the last redis ping timed out |
| redis_ping_success | Gauge | Whether or not the last redis ping succeeded |
| redis_ping_latency_seconds | Gauge | Round trip time of the redis ping |
| user_session_logins_total | Counter | Counter of how many users have logged in |
[← Back to the main Prometheus page](index.md) [← Back to the main Prometheus page](index.md)
......
...@@ -388,8 +388,8 @@ the style below as a guide: ...@@ -388,8 +388,8 @@ the style below as a guide:
1. Save the file and [restart] GitLab for the changes to take effect. 1. Save the file and [restart] GitLab for the changes to take effect.
[reconfigure]: path/to/administration/gitlab_restart.md#omnibus-gitlab-reconfigure [reconfigure]: path/to/administration/restart_gitlab.md#omnibus-gitlab-reconfigure
[restart]: path/to/administration/gitlab_restart.md#installations-from-source [restart]: path/to/administration/restart_gitlab.md#installations-from-source
```` ````
In this case: In this case:
......
...@@ -2,13 +2,13 @@ ...@@ -2,13 +2,13 @@
![GCP landing page](img/gcp_landing.png) ![GCP landing page](img/gcp_landing.png)
>**Important note:**
GitLab has no official images in Google Cloud Platform yet. This guide serves
as a template for when the GitLab VM will be available.
The fastest way to get started on [Google Cloud Platform (GCP)][gcp] is through The fastest way to get started on [Google Cloud Platform (GCP)][gcp] is through
the [Google Cloud Launcher][launcher] program. the [Google Cloud Launcher][launcher] program.
GitLab's official Google Launcher apps:
1. [GitLab Community Edition](https://console.cloud.google.com/launcher/details/gitlab-public/gitlab-community-edition?project=gitlab-public)
2. [GitLab Enterprise Edition](https://console.cloud.google.com/launcher/details/gitlab-public/gitlab-enterprise-edition?project=gitlab-public)
## Prerequisites ## Prerequisites
There are only two prerequisites in order to install GitLab on GCP: There are only two prerequisites in order to install GitLab on GCP:
......
...@@ -224,8 +224,11 @@ To enable this feature, navigate to the group settings page. Select ...@@ -224,8 +224,11 @@ To enable this feature, navigate to the group settings page. Select
access each project's settings, and remove any project from the same screen. access each project's settings, and remove any project from the same screen.
- **Webhooks**: configure [webhooks](../project/integrations/webhooks.md) - **Webhooks**: configure [webhooks](../project/integrations/webhooks.md)
and [push rules](../../push_rules/push_rules.md) to your group and [push rules](../../push_rules/push_rules.md) to your group
(Push Rules is available in [GitLab Enteprise Edition Starter][ee].)
- **Audit Events**: view [Audit Events](../../administration/audit_events.md) - **Audit Events**: view [Audit Events](../../administration/audit_events.md)
for the group (GitLab admins only) for the group (GitLab admins only, available in [GitLab Enterprise Edition Starter][ee])
- **Pipelines quota**: keep track of the [pipeline quota](../admin_area/settings/continuous_integration.md) for the group - **Pipelines quota**: keep track of the
[pipeline quota](../admin_area/settings/continuous_integration.md) for the group
[permissions]: ../permissions.md#permissions [permissions]: ../permissions.md#permissions
[ee]: https://about.gitlab.com/products/
...@@ -123,7 +123,7 @@ Multiple Issue Boards enables you to create more than one Issue Board per projec ...@@ -123,7 +123,7 @@ Multiple Issue Boards enables you to create more than one Issue Board per projec
It's great for large projects with more than one team or in situations where a It's great for large projects with more than one team or in situations where a
repository is used to host the code of multiple products. repository is used to host the code of multiple products.
_[Multiple Issue Boards](../issue_board.html#multiple-issue-boards) _[Multiple Issue Boards](../issue_board.md#multiple-issue-boards)
are available only in [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee/)._ are available only in [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee/)._
### Export Issues to CSV (EES/EEP) ### Export Issues to CSV (EES/EEP)
......
...@@ -26,6 +26,7 @@ With GitLab merge requests, you can: ...@@ -26,6 +26,7 @@ With GitLab merge requests, you can:
With **[GitLab Enterprise Edition][ee]**, you can also: With **[GitLab Enterprise Edition][ee]**, you can also:
- View the deployment process across projects with [Multi-Project Pipeline Graphs](../../../ci/multi_project_pipeline_graphs.md) (available only in GitLab Enterprise Edition Premium) - View the deployment process across projects with [Multi-Project Pipeline Graphs](../../../ci/multi_project_pipeline_graphs.md) (available only in GitLab Enterprise Edition Premium)
- Request [approvals](#merge-request-approvals) from your managers (available in GitLab Enterprise Edition Starter) - Request [approvals](#merge-request-approvals) from your managers (available in GitLab Enterprise Edition Starter)
- Enable [fast-forward merge requests](#fast-forward-merge-requests) (available in GitLab Enterprise Edition Starter) - Enable [fast-forward merge requests](#fast-forward-merge-requests) (available in GitLab Enterprise Edition Starter)
...@@ -189,6 +190,16 @@ specific commit page. ...@@ -189,6 +190,16 @@ specific commit page.
You can append `?w=1` while on the diffs page of a merge request to ignore any You can append `?w=1` while on the diffs page of a merge request to ignore any
whitespace changes. whitespace changes.
## Live preview with Review Apps
If you configured [Review Apps](https://about.gitlab.com/features/review-apps/) for your project,
you can preview the changes submitted to a feature-branch through a merge request
in a per-branch basis. No need to checkout the branch, install and preview locally;
all your changes will be available to preview by anyone with the Review Apps link.
[Read more about Review Apps.](../../../ci/review_apps/index.md)
## Tips ## Tips
Here are some tips that will help you be more efficient with merge requests in Here are some tips that will help you be more efficient with merge requests in
......
...@@ -94,7 +94,8 @@ GitLab Pages for this project, the site will live under ...@@ -94,7 +94,8 @@ GitLab Pages for this project, the site will live under
Once you enable GitLab Pages for your project, your website Once you enable GitLab Pages for your project, your website
will be published under `https://john.gitlab.io`. will be published under `https://john.gitlab.io`.
- Under your group `websites`, you created a project called - Under your group `websites`, you created a project called
`websites.gitlab.io`. your project's URL will be `https://gitlab.com/websites/websites.gitlab.io`. Once you enable GitLab Pages for your project, `websites.gitlab.io`. your project's URL will be `https://gitlab.com/websites/websites.gitlab.io`.
Once you enable GitLab Pages for your project,
your website will be published under `https://websites.gitlab.io`. your website will be published under `https://websites.gitlab.io`.
>**Note:** >**Note:**
......
This document was moved to [another location](../user/group/index.md). This document was moved to [another location](../user/group/index.md).
...@@ -80,6 +80,8 @@ module Ci ...@@ -80,6 +80,8 @@ module Ci
artifacts: job[:artifacts], artifacts: job[:artifacts],
cache: job[:cache], cache: job[:cache],
dependencies: job[:dependencies], dependencies: job[:dependencies],
before_script: job[:before_script],
script: job[:script],
after_script: job[:after_script], after_script: job[:after_script],
environment: job[:environment] environment: job[:environment]
}.compact } }.compact }
......
...@@ -12,7 +12,8 @@ module Gitlab ...@@ -12,7 +12,8 @@ module Gitlab
class << self class << self
def from_commands(job) def from_commands(job)
self.new(:script).tap do |step| self.new(:script).tap do |step|
step.script = job.commands.split("\n") step.script = job.options[:before_script].to_a + job.options[:script].to_a
step.script = job.commands.split("\n") if step.script.empty?
step.timeout = job.timeout step.timeout = job.timeout
step.when = WHEN_ON_SUCCESS step.when = WHEN_ON_SUCCESS
end end
......
...@@ -35,9 +35,9 @@ module Gitlab ...@@ -35,9 +35,9 @@ module Gitlab
repository_storages.flat_map do |storage_name| repository_storages.flat_map do |storage_name|
tmp_file_path = tmp_file_path(storage_name) tmp_file_path = tmp_file_path(storage_name)
[ [
operation_metrics(:filesystem_accessible, :filesystem_access_latency, -> { storage_stat_test(storage_name) }, shard: storage_name), operation_metrics(:filesystem_accessible, :filesystem_access_latency_seconds, -> { storage_stat_test(storage_name) }, shard: storage_name),
operation_metrics(:filesystem_writable, :filesystem_write_latency, -> { storage_write_test(tmp_file_path) }, shard: storage_name), operation_metrics(:filesystem_writable, :filesystem_write_latency_seconds, -> { storage_write_test(tmp_file_path) }, shard: storage_name),
operation_metrics(:filesystem_readable, :filesystem_read_latency, -> { storage_read_test(tmp_file_path) }, shard: storage_name) operation_metrics(:filesystem_readable, :filesystem_read_latency_seconds, -> { storage_read_test(tmp_file_path) }, shard: storage_name)
].flatten ].flatten
end end
end end
......
...@@ -20,7 +20,7 @@ module Gitlab ...@@ -20,7 +20,7 @@ module Gitlab
[ [
metric("#{metric_prefix}_timeout", result.is_a?(Timeout::Error) ? 1 : 0), metric("#{metric_prefix}_timeout", result.is_a?(Timeout::Error) ? 1 : 0),
metric("#{metric_prefix}_success", is_successful?(result) ? 1 : 0), metric("#{metric_prefix}_success", is_successful?(result) ? 1 : 0),
metric("#{metric_prefix}_latency", elapsed) metric("#{metric_prefix}_latency_seconds", elapsed)
] ]
end end
end end
......
module Gitlab module Gitlab
module Metrics module Metrics
class ConnectionRackMiddleware class RequestsRackMiddleware
def initialize(app) def initialize(app)
@app = app @app = app
end end
def self.rack_request_count def self.http_request_total
@rack_request_count ||= Gitlab::Metrics.counter(:rack_request, 'Rack request count') @http_request_total ||= Gitlab::Metrics.counter(:http_requests_total, 'Request count')
end
def self.rack_response_count
@rack_response_count ||= Gitlab::Metrics.counter(:rack_response, 'Rack response count')
end end
def self.rack_uncaught_errors_count def self.rack_uncaught_errors_count
@rack_uncaught_errors_count ||= Gitlab::Metrics.counter(:rack_uncaught_errors, 'Rack connections handling uncaught errors count') @rack_uncaught_errors_count ||= Gitlab::Metrics.counter(:rack_uncaught_errors_total, 'Request handling uncaught errors count')
end end
def self.rack_execution_time def self.http_request_duration_seconds
@rack_execution_time ||= Gitlab::Metrics.histogram(:rack_execution_time, 'Rack connection handling execution time', @http_request_duration_seconds ||= Gitlab::Metrics.histogram(:http_request_duration_seconds, 'Request handling execution time',
{}, [0.05, 0.1, 0.25, 0.5, 0.7, 1, 1.5, 2, 2.5, 3, 5, 7, 10]) {}, [0.05, 0.1, 0.25, 0.5, 0.7, 1, 2.5, 5, 10, 25])
end end
def call(env) def call(env)
method = env['REQUEST_METHOD'].downcase method = env['REQUEST_METHOD'].downcase
started = Time.now.to_f started = Time.now.to_f
begin begin
ConnectionRackMiddleware.rack_request_count.increment(method: method) RequestsRackMiddleware.http_request_total.increment(method: method)
status, headers, body = @app.call(env) status, headers, body = @app.call(env)
ConnectionRackMiddleware.rack_response_count.increment(method: method, status: status) elapsed = Time.now.to_f - started
RequestsRackMiddleware.http_request_duration_seconds.observe({ method: method, status: status }, elapsed)
[status, headers, body] [status, headers, body]
rescue rescue
ConnectionRackMiddleware.rack_uncaught_errors_count.increment RequestsRackMiddleware.rack_uncaught_errors_count.increment
raise raise
ensure
elapsed = Time.now.to_f - started
ConnectionRackMiddleware.rack_execution_time.observe({}, elapsed)
end end
end end
end end
......
...@@ -15,11 +15,11 @@ msgstr "" ...@@ -15,11 +15,11 @@ msgstr ""
"X-Generator: Zanata 3.9.6\n" "X-Generator: Zanata 3.9.6\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n"
msgid "%d additional commit has been omitted to prevent performance issues." msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "" msgid_plural ""
"%d additional commits have been omitted to prevent performance issues." "%s additional commits have been omitted to prevent performance issues."
msgstr[0] "%d подаване беше пропуснато, за да не се натоварва системата." msgstr[0] "%s подаване беше пропуснато, за да не се натоварва системата."
msgstr[1] "%d подавания бяха пропуснати, за да не се натоварва системата." msgstr[1] "%s подавания бяха пропуснати, за да не се натоварва системата."
msgid "%d commit" msgid "%d commit"
msgid_plural "%d commits" msgid_plural "%d commits"
......
...@@ -17,8 +17,8 @@ msgstr "" ...@@ -17,8 +17,8 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=n != 1;\n" "Plural-Forms: nplurals=2; plural=n != 1;\n"
"\n" "\n"
msgid "%d additional commit has been omitted to prevent performance issues." msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%d additional commits have been omitted to prevent performance issues." msgid_plural "%s additional commits have been omitted to prevent performance issues."
msgstr[0] "" msgstr[0] ""
msgstr[1] "" msgstr[1] ""
......
...@@ -15,11 +15,11 @@ msgstr "" ...@@ -15,11 +15,11 @@ msgstr ""
"X-Generator: Zanata 3.9.6\n" "X-Generator: Zanata 3.9.6\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n"
msgid "%d additional commit has been omitted to prevent performance issues." msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "" msgid_plural ""
"%d additional commits have been omitted to prevent performance issues." "%s additional commits have been omitted to prevent performance issues."
msgstr[0] "%d enmetado estis transsaltita, por ne troŝarĝi la sistemon." msgstr[0] "%s enmetado estis transsaltita, por ne troŝarĝi la sistemon."
msgstr[1] "%d enmetadoj estis transsaltitaj, por ne troŝarĝi la sistemon." msgstr[1] "%s enmetadoj estis transsaltitaj, por ne troŝarĝi la sistemon."
msgid "%d commit" msgid "%d commit"
msgid_plural "%d commits" msgid_plural "%d commits"
......
...@@ -15,14 +15,14 @@ msgstr "" ...@@ -15,14 +15,14 @@ msgstr ""
"X-Generator: Zanata 3.9.6\n" "X-Generator: Zanata 3.9.6\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n"
msgid "%d additional commit has been omitted to prevent performance issues." msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "" msgid_plural ""
"%d additional commits have been omitted to prevent performance issues." "%s additional commits have been omitted to prevent performance issues."
msgstr[0] "" msgstr[0] ""
"%d commit aggiuntivo è stato omesso per evitare degradi di prestazioni negli " "%s commit aggiuntivo è stato omesso per evitare degradi di prestazioni negli "
"issues." "issues."
msgstr[1] "" msgstr[1] ""
"%d commit aggiuntivi sono stati omessi per evitare degradi di prestazioni " "%s commit aggiuntivi sono stati omessi per evitare degradi di prestazioni "
"negli issues." "negli issues."
msgid "%d commit" msgid "%d commit"
......
...@@ -15,10 +15,10 @@ msgstr "" ...@@ -15,10 +15,10 @@ msgstr ""
"X-Generator: Zanata 3.9.6\n" "X-Generator: Zanata 3.9.6\n"
"Plural-Forms: nplurals=1; plural=0\n" "Plural-Forms: nplurals=1; plural=0\n"
msgid "%d additional commit has been omitted to prevent performance issues." msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "" msgid_plural ""
"%d additional commits have been omitted to prevent performance issues." "%s additional commits have been omitted to prevent performance issues."
msgstr[0] "为提高页面加载速度及性能,已省略了 %d 次提交。" msgstr[0] "为提高页面加载速度及性能,已省略了 %s 次提交。"
msgid "%d commit" msgid "%d commit"
msgid_plural "%d commits" msgid_plural "%d commits"
......
...@@ -14,10 +14,10 @@ msgstr "" ...@@ -14,10 +14,10 @@ msgstr ""
"X-Generator: Zanata 3.9.6\n" "X-Generator: Zanata 3.9.6\n"
"Plural-Forms: nplurals=1; plural=0\n" "Plural-Forms: nplurals=1; plural=0\n"
msgid "%d additional commit has been omitted to prevent performance issues." msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "" msgid_plural ""
"%d additional commits have been omitted to prevent performance issues." "%s additional commits have been omitted to prevent performance issues."
msgstr[0] "為提高頁面加載速度及性能,已省略了 %d 次提交。" msgstr[0] "為提高頁面加載速度及性能,已省略了 %s 次提交。"
msgid "%d commit" msgid "%d commit"
msgid_plural "%d commits" msgid_plural "%d commits"
......
...@@ -18,10 +18,10 @@ msgstr "" ...@@ -18,10 +18,10 @@ msgstr ""
"X-Generator: Zanata 3.9.6\n" "X-Generator: Zanata 3.9.6\n"
"Plural-Forms: nplurals=1; plural=0\n" "Plural-Forms: nplurals=1; plural=0\n"
msgid "%d additional commit has been omitted to prevent performance issues." msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "" msgid_plural ""
"%d additional commits have been omitted to prevent performance issues." "%s additional commits have been omitted to prevent performance issues."
msgstr[0] "因效能考量,不顯示 %d 個更動 (commit)。" msgstr[0] "因效能考量,不顯示 %s 個更動 (commit)。"
msgid "%d commit" msgid "%d commit"
msgid_plural "%d commits" msgid_plural "%d commits"
......
...@@ -137,6 +137,21 @@ describe AutocompleteController do ...@@ -137,6 +137,21 @@ describe AutocompleteController do
it { expect(body.size).to eq User.count } it { expect(body.size).to eq User.count }
end end
context 'user order' do
it 'shows exact matches first' do
reported_user = create(:user, username: 'reported_user', name: 'Doug')
user = create(:user, username: 'user', name: 'User')
user1 = create(:user, username: 'user1', name: 'Ian')
sign_in(user)
get(:users, search: 'user')
response_usernames = JSON.parse(response.body).map { |user| user['username'] }
expect(response_usernames.take(3)).to match_array([user.username, reported_user.username, user1.username])
end
end
context 'limited users per page' do context 'limited users per page' do
let(:per_page) { 2 } let(:per_page) { 2 }
......
...@@ -24,7 +24,7 @@ describe MetricsController do ...@@ -24,7 +24,7 @@ describe MetricsController do
expect(response.body).to match(/^db_ping_timeout 0$/) expect(response.body).to match(/^db_ping_timeout 0$/)
expect(response.body).to match(/^db_ping_success 1$/) expect(response.body).to match(/^db_ping_success 1$/)
expect(response.body).to match(/^db_ping_latency [0-9\.]+$/) expect(response.body).to match(/^db_ping_latency_seconds [0-9\.]+$/)
end end
it 'returns Redis ping metrics' do it 'returns Redis ping metrics' do
...@@ -32,7 +32,7 @@ describe MetricsController do ...@@ -32,7 +32,7 @@ describe MetricsController do
expect(response.body).to match(/^redis_ping_timeout 0$/) expect(response.body).to match(/^redis_ping_timeout 0$/)
expect(response.body).to match(/^redis_ping_success 1$/) expect(response.body).to match(/^redis_ping_success 1$/)
expect(response.body).to match(/^redis_ping_latency [0-9\.]+$/) expect(response.body).to match(/^redis_ping_latency_seconds [0-9\.]+$/)
end end
it 'returns Caching ping metrics' do it 'returns Caching ping metrics' do
...@@ -40,7 +40,7 @@ describe MetricsController do ...@@ -40,7 +40,7 @@ describe MetricsController do
expect(response.body).to match(/^redis_cache_ping_timeout 0$/) expect(response.body).to match(/^redis_cache_ping_timeout 0$/)
expect(response.body).to match(/^redis_cache_ping_success 1$/) expect(response.body).to match(/^redis_cache_ping_success 1$/)
expect(response.body).to match(/^redis_cache_ping_latency [0-9\.]+$/) expect(response.body).to match(/^redis_cache_ping_latency_seconds [0-9\.]+$/)
end end
it 'returns Queues ping metrics' do it 'returns Queues ping metrics' do
...@@ -48,7 +48,7 @@ describe MetricsController do ...@@ -48,7 +48,7 @@ describe MetricsController do
expect(response.body).to match(/^redis_queues_ping_timeout 0$/) expect(response.body).to match(/^redis_queues_ping_timeout 0$/)
expect(response.body).to match(/^redis_queues_ping_success 1$/) expect(response.body).to match(/^redis_queues_ping_success 1$/)
expect(response.body).to match(/^redis_queues_ping_latency [0-9\.]+$/) expect(response.body).to match(/^redis_queues_ping_latency_seconds [0-9\.]+$/)
end end
it 'returns SharedState ping metrics' do it 'returns SharedState ping metrics' do
...@@ -56,17 +56,17 @@ describe MetricsController do ...@@ -56,17 +56,17 @@ describe MetricsController do
expect(response.body).to match(/^redis_shared_state_ping_timeout 0$/) expect(response.body).to match(/^redis_shared_state_ping_timeout 0$/)
expect(response.body).to match(/^redis_shared_state_ping_success 1$/) expect(response.body).to match(/^redis_shared_state_ping_success 1$/)
expect(response.body).to match(/^redis_shared_state_ping_latency [0-9\.]+$/) expect(response.body).to match(/^redis_shared_state_ping_latency_seconds [0-9\.]+$/)
end end
it 'returns file system check metrics' do it 'returns file system check metrics' do
get :index get :index
expect(response.body).to match(/^filesystem_access_latency{shard="default"} [0-9\.]+$/) expect(response.body).to match(/^filesystem_access_latency_seconds{shard="default"} [0-9\.]+$/)
expect(response.body).to match(/^filesystem_accessible{shard="default"} 1$/) expect(response.body).to match(/^filesystem_accessible{shard="default"} 1$/)
expect(response.body).to match(/^filesystem_write_latency{shard="default"} [0-9\.]+$/) expect(response.body).to match(/^filesystem_write_latency_seconds{shard="default"} [0-9\.]+$/)
expect(response.body).to match(/^filesystem_writable{shard="default"} 1$/) expect(response.body).to match(/^filesystem_writable{shard="default"} 1$/)
expect(response.body).to match(/^filesystem_read_latency{shard="default"} [0-9\.]+$/) expect(response.body).to match(/^filesystem_read_latency_seconds{shard="default"} [0-9\.]+$/)
expect(response.body).to match(/^filesystem_readable{shard="default"} 1$/) expect(response.body).to match(/^filesystem_readable{shard="default"} 1$/)
end end
......
...@@ -234,24 +234,43 @@ describe ProjectsController do ...@@ -234,24 +234,43 @@ describe ProjectsController do
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:new_path) { 'renamed_path' }
let(:project_params) { { path: new_path } }
before do before do
sign_in(admin) sign_in(admin)
end end
it "sets the repository to the right path after a rename" do context 'when only renaming a project path' do
controller.instance_variable_set(:@project, project) it "sets the repository to the right path after a rename" do
expect { update_project path: 'renamed_path' }
.to change { project.reload.path }
put :update, expect(project.path).to include 'renamed_path'
namespace_id: project.namespace, expect(assigns(:repository).path).to include project.path
id: project.id, expect(response).to have_http_status(302)
project: project_params end
end
expect(project.repository.path).to include(new_path) context 'when project has container repositories with tags' do
expect(assigns(:repository).path).to eq(project.repository.path) before do
expect(response).to have_http_status(302) stub_container_registry_config(enabled: true)
stub_container_registry_tags(repository: /image/, tags: %w[rc1])
create(:container_repository, project: project, name: :image)
end
it 'does not allow to rename the project' do
expect { update_project path: 'renamed_path' }
.not_to change { project.reload.path }
expect(controller).to set_flash[:alert].to(/container registry tags/)
expect(response).to have_http_status(200)
end
end
def update_project(**parameters)
put :update,
namespace_id: project.namespace.path,
id: project.path,
project: parameters
end end
end end
......
...@@ -163,7 +163,10 @@ module Ci ...@@ -163,7 +163,10 @@ module Ci
commands: "pwd\nrspec", commands: "pwd\nrspec",
coverage_regex: nil, coverage_regex: nil,
tag_list: [], tag_list: [],
options: {}, options: {
before_script: ["pwd"],
script: ["rspec"]
},
allow_failure: false, allow_failure: false,
when: "on_success", when: "on_success",
environment: nil, environment: nil,
...@@ -616,10 +619,12 @@ module Ci ...@@ -616,10 +619,12 @@ module Ci
coverage_regex: nil, coverage_regex: nil,
tag_list: [], tag_list: [],
options: { options: {
image: { name: "ruby:2.1", entrypoint: ["/usr/local/bin/init", "run"] }, before_script: ["pwd"],
services: [{ name: "mysql" }, script: ["rspec"],
{ name: "docker:dind", alias: "docker", entrypoint: ["/usr/local/bin/init", "run"], image: { name: "ruby:2.1", entrypoint: ["/usr/local/bin/init", "run"] },
command: ["/usr/local/bin/init", "run"] }] services: [{ name: "mysql" },
{ name: "docker:dind", alias: "docker", entrypoint: ["/usr/local/bin/init", "run"],
command: ["/usr/local/bin/init", "run"] }]
}, },
allow_failure: false, allow_failure: false,
when: "on_success", when: "on_success",
...@@ -649,10 +654,12 @@ module Ci ...@@ -649,10 +654,12 @@ module Ci
coverage_regex: nil, coverage_regex: nil,
tag_list: [], tag_list: [],
options: { options: {
image: { name: "ruby:2.5", entrypoint: ["/usr/local/bin/init", "run"] }, before_script: ["pwd"],
services: [{ name: "postgresql", alias: "db-pg", entrypoint: ["/usr/local/bin/init", "run"], script: ["rspec"],
command: ["/usr/local/bin/init", "run"] }, image: { name: "ruby:2.5", entrypoint: ["/usr/local/bin/init", "run"] },
{ name: "docker:dind" }] services: [{ name: "postgresql", alias: "db-pg", entrypoint: ["/usr/local/bin/init", "run"],
command: ["/usr/local/bin/init", "run"] },
{ name: "docker:dind" }]
}, },
allow_failure: false, allow_failure: false,
when: "on_success", when: "on_success",
...@@ -680,6 +687,8 @@ module Ci ...@@ -680,6 +687,8 @@ module Ci
coverage_regex: nil, coverage_regex: nil,
tag_list: [], tag_list: [],
options: { options: {
before_script: ["pwd"],
script: ["rspec"],
image: { name: "ruby:2.1" }, image: { name: "ruby:2.1" },
services: [{ name: "mysql" }, { name: "docker:dind" }] services: [{ name: "mysql" }, { name: "docker:dind" }]
}, },
...@@ -707,8 +716,10 @@ module Ci ...@@ -707,8 +716,10 @@ module Ci
coverage_regex: nil, coverage_regex: nil,
tag_list: [], tag_list: [],
options: { options: {
image: { name: "ruby:2.5" }, before_script: ["pwd"],
services: [{ name: "postgresql" }, { name: "docker:dind" }] script: ["rspec"],
image: { name: "ruby:2.5" },
services: [{ name: "postgresql" }, { name: "docker:dind" }]
}, },
allow_failure: false, allow_failure: false,
when: "on_success", when: "on_success",
...@@ -951,6 +962,8 @@ module Ci ...@@ -951,6 +962,8 @@ module Ci
coverage_regex: nil, coverage_regex: nil,
tag_list: [], tag_list: [],
options: { options: {
before_script: ["pwd"],
script: ["rspec"],
image: { name: "ruby:2.1" }, image: { name: "ruby:2.1" },
services: [{ name: "mysql" }], services: [{ name: "mysql" }],
artifacts: { artifacts: {
...@@ -1162,7 +1175,9 @@ module Ci ...@@ -1162,7 +1175,9 @@ module Ci
commands: "test", commands: "test",
coverage_regex: nil, coverage_regex: nil,
tag_list: [], tag_list: [],
options: {}, options: {
script: ["test"]
},
when: "on_success", when: "on_success",
allow_failure: false, allow_failure: false,
environment: nil, environment: nil,
...@@ -1208,7 +1223,9 @@ module Ci ...@@ -1208,7 +1223,9 @@ module Ci
commands: "execute-script-for-job", commands: "execute-script-for-job",
coverage_regex: nil, coverage_regex: nil,
tag_list: [], tag_list: [],
options: {}, options: {
script: ["execute-script-for-job"]
},
when: "on_success", when: "on_success",
allow_failure: false, allow_failure: false,
environment: nil, environment: nil,
...@@ -1221,7 +1238,9 @@ module Ci ...@@ -1221,7 +1238,9 @@ module Ci
commands: "execute-script-for-job", commands: "execute-script-for-job",
coverage_regex: nil, coverage_regex: nil,
tag_list: [], tag_list: [],
options: {}, options: {
script: ["execute-script-for-job"]
},
when: "on_success", when: "on_success",
allow_failure: false, allow_failure: false,
environment: nil, environment: nil,
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::Ci::Build::Step do describe Gitlab::Ci::Build::Step do
let(:job) { create(:ci_build, :no_options, commands: "ls -la\ndate") }
describe '#from_commands' do describe '#from_commands' do
subject { described_class.from_commands(job) } shared_examples 'has correct script' do
subject { described_class.from_commands(job) }
it 'fabricates an object' do
expect(subject.name).to eq(:script) it 'fabricates an object' do
expect(subject.script).to eq(['ls -la', 'date']) expect(subject.name).to eq(:script)
expect(subject.timeout).to eq(job.timeout) expect(subject.script).to eq(script)
expect(subject.when).to eq('on_success') expect(subject.timeout).to eq(job.timeout)
expect(subject.allow_failure).to be_falsey expect(subject.when).to eq('on_success')
expect(subject.allow_failure).to be_falsey
end
end
context 'when commands are specified' do
it_behaves_like 'has correct script' do
let(:job) { create(:ci_build, :no_options, commands: "ls -la\ndate") }
let(:script) { ['ls -la', 'date'] }
end
end
context 'when script option is specified' do
it_behaves_like 'has correct script' do
let(:job) { create(:ci_build, :no_options, options: { script: ["ls -la\necho aaa", "date"] }) }
let(:script) { ["ls -la\necho aaa", 'date'] }
end
end
context 'when before and script option is specified' do
it_behaves_like 'has correct script' do
let(:job) do
create(:ci_build, options: {
before_script: ["ls -la\necho aaa"],
script: ["date"]
})
end
let(:script) { ["ls -la\necho aaa", 'date'] }
end
end end
end end
describe '#from_after_script' do describe '#from_after_script' do
let(:job) { create(:ci_build) }
subject { described_class.from_after_script(job) } subject { described_class.from_after_script(job) }
context 'when after_script is empty' do context 'when after_script is empty' do
......
...@@ -109,9 +109,9 @@ describe Gitlab::HealthChecks::FsShardsCheck do ...@@ -109,9 +109,9 @@ describe Gitlab::HealthChecks::FsShardsCheck do
expect(subject).to include(an_object_having_attributes(name: :filesystem_readable, value: 0)) expect(subject).to include(an_object_having_attributes(name: :filesystem_readable, value: 0))
expect(subject).to include(an_object_having_attributes(name: :filesystem_writable, value: 0)) expect(subject).to include(an_object_having_attributes(name: :filesystem_writable, value: 0))
expect(subject).to include(an_object_having_attributes(name: :filesystem_access_latency, value: be >= 0)) expect(subject).to include(an_object_having_attributes(name: :filesystem_access_latency_seconds, value: be >= 0))
expect(subject).to include(an_object_having_attributes(name: :filesystem_read_latency, value: be >= 0)) expect(subject).to include(an_object_having_attributes(name: :filesystem_read_latency_seconds, value: be >= 0))
expect(subject).to include(an_object_having_attributes(name: :filesystem_write_latency, value: be >= 0)) expect(subject).to include(an_object_having_attributes(name: :filesystem_write_latency_seconds, value: be >= 0))
end end
end end
...@@ -127,9 +127,9 @@ describe Gitlab::HealthChecks::FsShardsCheck do ...@@ -127,9 +127,9 @@ describe Gitlab::HealthChecks::FsShardsCheck do
expect(subject).to include(an_object_having_attributes(name: :filesystem_readable, value: 1)) expect(subject).to include(an_object_having_attributes(name: :filesystem_readable, value: 1))
expect(subject).to include(an_object_having_attributes(name: :filesystem_writable, value: 1)) expect(subject).to include(an_object_having_attributes(name: :filesystem_writable, value: 1))
expect(subject).to include(an_object_having_attributes(name: :filesystem_access_latency, value: be >= 0)) expect(subject).to include(an_object_having_attributes(name: :filesystem_access_latency_seconds, value: be >= 0))
expect(subject).to include(an_object_having_attributes(name: :filesystem_read_latency, value: be >= 0)) expect(subject).to include(an_object_having_attributes(name: :filesystem_read_latency_seconds, value: be >= 0))
expect(subject).to include(an_object_having_attributes(name: :filesystem_write_latency, value: be >= 0)) expect(subject).to include(an_object_having_attributes(name: :filesystem_write_latency_seconds, value: be >= 0))
end end
end end
end end
...@@ -159,9 +159,9 @@ describe Gitlab::HealthChecks::FsShardsCheck do ...@@ -159,9 +159,9 @@ describe Gitlab::HealthChecks::FsShardsCheck do
expect(subject).to include(an_object_having_attributes(name: :filesystem_readable, value: 0)) expect(subject).to include(an_object_having_attributes(name: :filesystem_readable, value: 0))
expect(subject).to include(an_object_having_attributes(name: :filesystem_writable, value: 0)) expect(subject).to include(an_object_having_attributes(name: :filesystem_writable, value: 0))
expect(subject).to include(an_object_having_attributes(name: :filesystem_access_latency, value: be >= 0)) expect(subject).to include(an_object_having_attributes(name: :filesystem_access_latency_seconds, value: be >= 0))
expect(subject).to include(an_object_having_attributes(name: :filesystem_read_latency, value: be >= 0)) expect(subject).to include(an_object_having_attributes(name: :filesystem_read_latency_seconds, value: be >= 0))
expect(subject).to include(an_object_having_attributes(name: :filesystem_write_latency, value: be >= 0)) expect(subject).to include(an_object_having_attributes(name: :filesystem_write_latency_seconds, value: be >= 0))
end end
end end
end end
......
...@@ -8,7 +8,7 @@ shared_context 'simple_check' do |metrics_prefix, check_name, success_result| ...@@ -8,7 +8,7 @@ shared_context 'simple_check' do |metrics_prefix, check_name, success_result|
it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_success", value: 1)) } it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_success", value: 1)) }
it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_timeout", value: 0)) } it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_timeout", value: 0)) }
it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_latency", value: be >= 0)) } it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_latency_seconds", value: be >= 0)) }
end end
context 'Check is misbehaving' do context 'Check is misbehaving' do
...@@ -18,7 +18,7 @@ shared_context 'simple_check' do |metrics_prefix, check_name, success_result| ...@@ -18,7 +18,7 @@ shared_context 'simple_check' do |metrics_prefix, check_name, success_result|
it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_success", value: 0)) } it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_success", value: 0)) }
it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_timeout", value: 0)) } it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_timeout", value: 0)) }
it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_latency", value: be >= 0)) } it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_latency_seconds", value: be >= 0)) }
end end
context 'Check is timeouting' do context 'Check is timeouting' do
...@@ -28,7 +28,7 @@ shared_context 'simple_check' do |metrics_prefix, check_name, success_result| ...@@ -28,7 +28,7 @@ shared_context 'simple_check' do |metrics_prefix, check_name, success_result|
it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_success", value: 0)) } it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_success", value: 0)) }
it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_timeout", value: 1)) } it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_timeout", value: 1)) }
it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_latency", value: be >= 0)) } it { is_expected.to include(have_attributes(name: "#{metrics_prefix}_latency_seconds", value: be >= 0)) }
end end
end end
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::Metrics::ConnectionRackMiddleware do describe Gitlab::Metrics::RequestsRackMiddleware do
let(:app) { double('app') } let(:app) { double('app') }
subject { described_class.new(app) } subject { described_class.new(app) }
...@@ -22,14 +22,8 @@ describe Gitlab::Metrics::ConnectionRackMiddleware do ...@@ -22,14 +22,8 @@ describe Gitlab::Metrics::ConnectionRackMiddleware do
allow(app).to receive(:call).and_return([200, nil, nil]) allow(app).to receive(:call).and_return([200, nil, nil])
end end
it 'increments response count with status label' do
expect(described_class).to receive_message_chain(:rack_response_count, :increment).with(include(status: 200, method: 'get'))
subject.call(env)
end
it 'increments requests count' do it 'increments requests count' do
expect(described_class).to receive_message_chain(:rack_request_count, :increment).with(method: 'get') expect(described_class).to receive_message_chain(:http_request_total, :increment).with(method: 'get')
subject.call(env) subject.call(env)
end end
...@@ -38,20 +32,21 @@ describe Gitlab::Metrics::ConnectionRackMiddleware do ...@@ -38,20 +32,21 @@ describe Gitlab::Metrics::ConnectionRackMiddleware do
execution_time = 10 execution_time = 10
allow(app).to receive(:call) do |*args| allow(app).to receive(:call) do |*args|
Timecop.freeze(execution_time.seconds) Timecop.freeze(execution_time.seconds)
[200, nil, nil]
end end
expect(described_class).to receive_message_chain(:rack_execution_time, :observe).with({}, execution_time) expect(described_class).to receive_message_chain(:http_request_duration_seconds, :observe).with({ status: 200, method: 'get' }, execution_time)
subject.call(env) subject.call(env)
end end
end end
context '@app.call throws exception' do context '@app.call throws exception' do
let(:rack_response_count) { double('rack_response_count') } let(:http_request_duration_seconds) { double('http_request_duration_seconds') }
before do before do
allow(app).to receive(:call).and_raise(StandardError) allow(app).to receive(:call).and_raise(StandardError)
allow(described_class).to receive(:rack_response_count).and_return(rack_response_count) allow(described_class).to receive(:http_request_duration_seconds).and_return(http_request_duration_seconds)
end end
it 'increments exceptions count' do it 'increments exceptions count' do
...@@ -61,25 +56,13 @@ describe Gitlab::Metrics::ConnectionRackMiddleware do ...@@ -61,25 +56,13 @@ describe Gitlab::Metrics::ConnectionRackMiddleware do
end end
it 'increments requests count' do it 'increments requests count' do
expect(described_class).to receive_message_chain(:rack_request_count, :increment).with(method: 'get') expect(described_class).to receive_message_chain(:http_request_total, :increment).with(method: 'get')
expect { subject.call(env) }.to raise_error(StandardError)
end
it "does't increment response count" do
expect(described_class.rack_response_count).not_to receive(:increment)
expect { subject.call(env) }.to raise_error(StandardError) expect { subject.call(env) }.to raise_error(StandardError)
end end
it 'measures execution time' do it "does't measure request execution time" do
execution_time = 10 expect(described_class.http_request_duration_seconds).not_to receive(:increment)
allow(app).to receive(:call) do |*args|
Timecop.freeze(execution_time.seconds)
raise StandardError
end
expect(described_class).to receive_message_chain(:rack_execution_time, :observe).with({}, execution_time)
expect { subject.call(env) }.to raise_error(StandardError) expect { subject.call(env) }.to raise_error(StandardError)
end end
......
...@@ -1444,7 +1444,7 @@ describe Project, models: true do ...@@ -1444,7 +1444,7 @@ describe Project, models: true do
subject { project.rename_repo } subject { project.rename_repo }
it { expect{subject}.to raise_error(Exception) } it { expect{subject}.to raise_error(StandardError) }
end end
end end
......
...@@ -791,7 +791,7 @@ describe User, models: true do ...@@ -791,7 +791,7 @@ describe User, models: true do
end end
it 'returns users with a partially matching name' do it 'returns users with a partially matching name' do
expect(described_class.search(user.name[0..2])).to eq([user2, user]) expect(described_class.search(user.name[0..2])).to eq([user, user2])
end end
it 'returns users with a matching name regardless of the casing' do it 'returns users with a matching name regardless of the casing' do
...@@ -805,7 +805,7 @@ describe User, models: true do ...@@ -805,7 +805,7 @@ describe User, models: true do
end end
it 'returns users with a partially matching Email' do it 'returns users with a partially matching Email' do
expect(described_class.search(user.email[0..2])).to eq([user2, user]) expect(described_class.search(user.email[0..2])).to eq([user, user2])
end end
it 'returns users with a matching Email regardless of the casing' do it 'returns users with a matching Email regardless of the casing' do
...@@ -819,7 +819,7 @@ describe User, models: true do ...@@ -819,7 +819,7 @@ describe User, models: true do
end end
it 'returns users with a partially matching username' do it 'returns users with a partially matching username' do
expect(described_class.search(user.username[0..2])).to eq([user2, user]) expect(described_class.search(user.username[0..2])).to eq([user, user2])
end end
it 'returns users with a matching username regardless of the casing' do it 'returns users with a matching username regardless of the casing' do
......
require 'spec_helper' require 'spec_helper'
describe Projects::UpdateService, services: true do describe Projects::UpdateService, '#execute', :services do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
let(:project) { create(:empty_project, creator_id: user.id, namespace: user.namespace) }
describe 'update_by_user' do let(:project) do
create(:empty_project, creator: user, namespace: user.namespace)
end
context 'when changing visibility level' do
context 'when visibility_level is INTERNAL' do context 'when visibility_level is INTERNAL' do
it 'updates the project to internal' do it 'updates the project to internal' do
result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::INTERNAL) result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::INTERNAL)
...@@ -40,7 +43,7 @@ describe Projects::UpdateService, services: true do ...@@ -40,7 +43,7 @@ describe Projects::UpdateService, services: true do
it 'does not update the project to public' do it 'does not update the project to public' do
result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::PUBLIC) result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
expect(result).to eq({ status: :error, message: 'Visibility level unallowed' }) expect(result).to eq({ status: :error, message: 'New visibility level not allowed!' })
expect(project).to be_private expect(project).to be_private
end end
...@@ -55,12 +58,13 @@ describe Projects::UpdateService, services: true do ...@@ -55,12 +58,13 @@ describe Projects::UpdateService, services: true do
end end
end end
describe 'visibility_level' do describe 'when updating project that has forks' do
let(:project) { create(:empty_project, :internal) } let(:project) { create(:empty_project, :internal) }
let(:forked_project) { create(:forked_project_with_submodules, :internal) } let(:forked_project) { create(:forked_project_with_submodules, :internal) }
before do before do
forked_project.build_forked_project_link(forked_to_project_id: forked_project.id, forked_from_project_id: project.id) forked_project.build_forked_project_link(forked_to_project_id: forked_project.id,
forked_from_project_id: project.id)
forked_project.save forked_project.save
end end
...@@ -89,10 +93,38 @@ describe Projects::UpdateService, services: true do ...@@ -89,10 +93,38 @@ describe Projects::UpdateService, services: true do
end end
end end
it 'returns an error result when record cannot be updated' do context 'when updating a default branch' do
result = update_project(project, admin, { name: 'foo&bar' }) let(:project) { create(:project, :repository) }
it 'changes a default branch' do
update_project(project, admin, default_branch: 'feature')
expect(result).to eq({ status: :error, message: 'Project could not be updated' }) expect(Project.find(project.id).default_branch).to eq 'feature'
end
end
context 'when renaming project that contains container images' do
before do
stub_container_registry_config(enabled: true)
stub_container_registry_tags(repository: /image/, tags: %w[rc1])
create(:container_repository, project: project, name: :image)
end
it 'does not allow to rename the project' do
result = update_project(project, admin, path: 'renamed')
expect(result).to include(status: :error)
expect(result[:message]).to match(/contains container registry tags/)
end
end
context 'when passing invalid parameters' do
it 'returns an error result when record cannot be updated' do
result = update_project(project, admin, { name: 'foo&bar' })
expect(result).to eq({ status: :error,
message: 'Project could not be updated!' })
end
end end
describe 'repository_storage' do describe 'repository_storage' do
...@@ -158,7 +190,7 @@ describe Projects::UpdateService, services: true do ...@@ -158,7 +190,7 @@ describe Projects::UpdateService, services: true do
it 'returns an error result when record cannot be updated' do it 'returns an error result when record cannot be updated' do
result = update_project(project, admin, { name: 'foo&bar' }) result = update_project(project, admin, { name: 'foo&bar' })
expect(result).to eq({ status: :error, message: 'Project could not be updated' }) expect(result).to eq({ status: :error, message: 'Project could not be updated!' })
end end
def update_project(project, user, opts) def update_project(project, user, opts)
......
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