Commit 637f0537 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

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

Signed-off-by: default avatarDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
parents 300898f0 5f32bd77
......@@ -8,8 +8,13 @@
## Test coverage
<<<<<<< HEAD
- [![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
=======
- [![Ruby coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby) Ruby
- [![JavaScript coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=karma)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-javascript) JavaScript
>>>>>>> ce-com/master
## Canonical source
......
......@@ -18,13 +18,26 @@ window.gl.CommitPipelinesTable = CommitPipelinesTable;
document.addEventListener('DOMContentLoaded', () => {
const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
if (pipelineTableViewEl && pipelineTableViewEl.dataset.disableInitialization === undefined) {
const table = new CommitPipelinesTable({
propsData: {
endpoint: pipelineTableViewEl.dataset.endpoint,
helpPagePath: pipelineTableViewEl.dataset.helpPagePath,
},
}).$mount();
pipelineTableViewEl.appendChild(table.$el);
if (pipelineTableViewEl) {
// Update MR and Commits tabs
pipelineTableViewEl.addEventListener('update-pipelines-count', (event) => {
if (event.detail.pipelines &&
event.detail.pipelines.count &&
event.detail.pipelines.count.all) {
const badge = document.querySelector('.js-pipelines-mr-count');
badge.textContent = event.detail.pipelines.count.all;
}
});
if (pipelineTableViewEl.dataset.disableInitialization === undefined) {
const table = new CommitPipelinesTable({
propsData: {
endpoint: pipelineTableViewEl.dataset.endpoint,
helpPagePath: pipelineTableViewEl.dataset.helpPagePath,
},
}).$mount();
pipelineTableViewEl.appendChild(table.$el);
}
}
});
......@@ -55,6 +55,17 @@
// depending of the endpoint the response can either bring a `pipelines` key or not.
const pipelines = response.pipelines || response;
this.setCommonData(pipelines);
const updatePipelinesEvent = new CustomEvent('update-pipelines-count', {
detail: {
pipelines: response,
},
});
// notifiy to update the count in tabs
if (this.$el.parentElement) {
this.$el.parentElement.dispatchEvent(updatePipelinesEvent);
}
});
},
},
......
......@@ -149,7 +149,6 @@ import './star';
import './subscription';
import './subscription_select';
import './syntax_highlight';
import './user';
// EE-only scripts
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 UserTabs from './user_tabs';
class User {
export default class User {
constructor({ action }) {
this.action = action;
this.placeProfileAvatarsToTop();
......@@ -13,25 +13,22 @@ class User {
placeProfileAvatarsToTop() {
$('.profile-groups-avatars').tooltip({
placement: 'top'
placement: 'top',
});
}
initTabs() {
return new UserTabs({
parentEl: '.user-profile',
action: this.action
action: this.action,
});
}
hideProjectLimitMessage() {
$('.hide-project-limit-message').on('click', e => {
$('.hide-project-limit-message').on('click', (e) => {
e.preventDefault();
Cookies.set('hide_project_limit_message', 'false');
$(this).parents('.project-limit-message').remove();
});
}
}
window.gl = window.gl || {};
window.gl.User = User;
......@@ -110,10 +110,10 @@ $well-light-text-color: #5b6169;
* Text
*/
$gl-font-size: 14px;
$gl-text-color: rgba(0, 0, 0, .85);
$gl-text-color-light: rgba(0, 0, 0, .7);
$gl-text-color-secondary: rgba(0, 0, 0, .55);
$gl-text-color-disabled: rgba(0, 0, 0, .35);
$gl-text-color: #2e2e2e;
$gl-text-color-secondary: #707070;
$gl-text-color-tertiary: #949494;
$gl-text-color-quaternary: #d6d6d6;
$gl-text-color-inverted: rgba(255, 255, 255, 1.0);
$gl-text-color-secondary-inverted: rgba(255, 255, 255, .85);
$gl-text-green: $green-600;
......@@ -127,7 +127,7 @@ $gl-gray-dark: #313236;
$gl-gray-light: #5c5c5c;
$gl-header-color: #4c4e54;
$gl-header-nav-hover-color: #434343;
$placeholder-text-color: rgba(0, 0, 0, .42);
$placeholder-text-color: $gl-text-color-tertiary;
/*
* Lists
......@@ -135,7 +135,7 @@ $placeholder-text-color: rgba(0, 0, 0, .42);
$list-font-size: $gl-font-size;
$list-title-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: rgba(0, 0, 0, 0.05);
$list-text-height: 42px;
......
......@@ -284,7 +284,7 @@ header.navbar-gitlab-new {
position: relative;
top: -1px;
padding: 0 5px;
color: rgba($black, .65);
color: $gl-text-color-secondary;
font-size: 10px;
line-height: 1;
background: none;
......@@ -310,10 +310,10 @@ header.navbar-gitlab-new {
.breadcrumbs-links {
flex: 1;
align-self: center;
color: $black-transparent;
color: $gl-text-color-quaternary;
a {
color: rgba($black, .65);
color: $gl-text-color-secondary;
&:not(:first-child),
&.group-path {
......@@ -368,9 +368,10 @@ header.navbar-gitlab-new {
}
.breadcrumbs-sub-title {
margin: 2px 0 0;
margin: 2px 0;
font-size: 16px;
font-weight: normal;
line-height: 1;
ul {
margin: 0;
......
......@@ -35,6 +35,7 @@ $new-sidebar-width: 220px;
.avatar-container {
flex: 0 0 40px;
background-color: $white-light;
}
&:hover {
......
......@@ -4,12 +4,21 @@ class AutocompleteController < ApplicationController
before_action :find_users, only: [:users]
def users
<<<<<<< HEAD
@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.reorder(:name)
@users = load_users_by_ability || @users.page(params[:page]).per(params[:per_page])
=======
@users ||= User.none
@users = @users.active
@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 = @users.page(params[:page]).per(params[:per_page])
>>>>>>> ce-com/master
if params[:todo_filter].present? && current_user
@users = @users.todo_authors(current_user.id, params[:todo_state_filter])
......
......@@ -38,9 +38,14 @@ class Projects::CommitController < Projects::ApplicationController
format.json do
Gitlab::PollingInterval.set_header(response, interval: 10_000)
render json: PipelineSerializer
.new(project: @project, current_user: @current_user)
.represent(@pipelines)
render json: {
pipelines: PipelineSerializer
.new(project: @project, current_user: @current_user)
.represent(@pipelines),
count: {
all: @pipelines.count
}
}
end
end
end
......
......@@ -114,9 +114,14 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
Gitlab::PollingInterval.set_header(response, interval: 10_000)
render json: PipelineSerializer
.new(project: @project, current_user: @current_user)
.represent(@pipelines)
render json: {
pipelines: PipelineSerializer
.new(project: @project, current_user: @current_user)
.represent(@pipelines),
count: {
all: @pipelines.count
}
}
end
def edit
......
......@@ -50,10 +50,13 @@ class ProjectsController < Projects::ApplicationController
respond_to do |format|
if result[:status] == :success
flash[:notice] = _("Project '%{project_name}' was successfully updated.") % { project_name: @project.name }
format.html do
redirect_to(edit_project_path(@project))
end
else
flash[:alert] = result[:message]
format.html { render 'edit' }
end
......
......@@ -49,7 +49,7 @@ class SessionsController < Devise::SessionsController
private
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
# Handle an "initial setup" state, where there's only one user, it's an admin,
......
......@@ -485,7 +485,9 @@ class Project < ActiveRecord::Base
end
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?
end
......@@ -976,8 +978,6 @@ class Project < ActiveRecord::Base
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?
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
raise StandardError.new('Project cannot be renamed, because images are present in its container registry')
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 repository moved successfully we need to send update instructions to users.
# However we cannot allow rollback since we moved repository
......
......@@ -328,7 +328,7 @@ class User < ActiveRecord::Base
table[:name].matches(pattern)
.or(table[:email].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
# searches user by given pattern
......
module Projects
class UpdateService < BaseService
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
unless visibility_level_allowed?
return error('New visibility level not allowed!')
end
<<<<<<< HEAD
# Repository size limit comes as MB from the view
limit = params.delete(:repository_size_limit)
project.repository_size_limit = Gitlab::Utils.try_megabytes_to_bytes(limit) if limit
......@@ -28,6 +21,14 @@ module Projects
if new_repository_storage && can?(current_user, :change_repository_storage, project)
project.change_repository_storage(new_repository_storage)
end
=======
if project.has_container_registry_tags?
return error('Cannot rename project because it contains container registry tags!')
end
if changing_default_branch?
project.change_head(params[:default_branch])
>>>>>>> ce-com/master
end
if project.update_attributes(params)
......@@ -39,8 +40,33 @@ module Projects
success
else
error('Project could not be updated')
error('Project could not be updated!')
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_default_branch?
new_branch = params[:default_branch]
project.repository.exists? &&
new_branch && new_branch != project.default_branch
end
end
end
......@@ -7,4 +7,4 @@
= nav_link(path: 'commit#pipelines') do
= link_to pipelines_project_commit_path(@project, @commit.id) do
Pipelines
%span.badge= @commit.pipelines.size
%span.badge.js-pipelines-mr-count= @commit.pipelines.size
......@@ -55,7 +55,7 @@
%li.pipelines-tab
= link_to pipelines_project_merge_request_path(@project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do
Pipelines
%span.badge= @pipelines.size
%span.badge.js-pipelines-mr-count= @pipelines.size
%li.diffs-tab
= link_to diffs_project_merge_request_path(@project, @merge_request), data: { target: 'div#diffs', action: 'diffs', toggle: 'tab' } do
Changes
......
---
title: Update Pipeline's badge count in Merge Request and Commits view to match real-time
content
merge_request:
author:
---
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_
Gitlab::Application.configure do |config|
# 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
if Gitlab::Metrics.enabled?
......
......@@ -7,7 +7,7 @@
- aws_elb_request_count_sum
weight: 1
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
unit: req / sec
- title: "Latency"
......
......@@ -72,7 +72,7 @@ var config = {
stl_viewer: './blob/stl_viewer.js',
terminal: './terminal/terminal_bundle.js',
u2f: ['vendor/u2f'],
users: './users/users_bundle.js',
users: './users/index.js',
raven: './raven/index.js',
vue_merge_request_widget: './vue_merge_request_widget/index.js',
test: './test.js',
......
......@@ -51,7 +51,11 @@ Shortcuts to GitLab's most visited docs:
- [Importing and exporting projects between instances](user/project/settings/import_export.md).
- [Project access](public_access/public_access.md): Setting up your project's visibility to public, internal, or private.
- [Groups](user/group/index.md): Organize your projects in groups.
<<<<<<< HEAD
- [Subgroups](user/group/subgroups/index.md): nest groups in subgroups.
=======
- [GitLab Subgroups](user/group/subgroups/index.md)
>>>>>>> ce-com/master
- [Search through GitLab](user/search/index.md): Search for issues, merge requests, projects, groups, todos, and issues in Issue Boards.
- **(EES/EEP)** [Advanced Global Search](user/search/advanced_global_search.md): Leverage Elasticsearch for faster, more advanced code search across your entire GitLab instance.
- **(EES/EEP)** [Advanced Syntax Search](user/search/advanced_search_syntax.md): Use advanced queries for more targeted search results.
......
......@@ -250,9 +250,10 @@ Tip: If you want to limit access to the nested members of an Active Directory
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)
```
<<<<<<< HEAD
### Escaping special characters
If the `user_filter` DN contains a special characters. For example a comma
......@@ -270,6 +271,12 @@ As an example the above DN would look like
```
OU=GitLab\\5C\\2C Inc,DC=gitlab,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).
>>>>>>> ce-com/master
Please note that GitLab does not support the custom filter syntax used by
omniauth-ldap.
......
......@@ -26,21 +26,24 @@ server, because the embedded server configuration is overwritten once every
In this experimental phase, only a few metrics are available:
| Metric | Type | Description |
| ------ | ---- | ----------- |
| 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_latency | Gauge | Round trip time of the database ping |
| 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 | Gauge | Round trip time of the redis ping |
| filesystem_access_latency | gauge | Latency in accessing a specific filesystem |
| filesystem_accessible | gauge | Whether or not a specific filesystem is accessible |
| filesystem_write_latency | gauge | Write latency of a specific filesystem |
| filesystem_writable | gauge | Whether or not the filesystem is writable |
| filesystem_read_latency | gauge | Read latency of a specific filesystem |
| filesystem_readable | gauge | Whether or not the filesystem is readable |
| user_sessions_logins | Counter | Counter of how many users have logged in |
| Metric | Type | Description |
| --------------------------------- | --------- | ----------- |
| 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_latency_seconds | Gauge | Round trip time of the database ping |
| filesystem_access_latency_seconds | Gauge | Latency in accessing a specific filesystem |
| filesystem_accessible | Gauge | Whether or not a specific filesystem is accessible |
| filesystem_write_latency_seconds | Gauge | Write latency of a specific filesystem |
| filesystem_writable | Gauge | Whether or not the filesystem is writable |
| filesystem_read_latency_seconds | Gauge | Read latency of a specific filesystem |
| filesystem_readable | Gauge | Whether or not the filesystem is readable |
| http_requests_total | Counter | Rack request count |
| http_request_duration_seconds | Histogram | HTTP response time from rack middleware |
| rack_uncaught_errors_total | Counter | Rack connections handling uncaught errors count |
| 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)
......
......@@ -388,8 +388,8 @@ the style below as a guide:
1. Save the file and [restart] GitLab for the changes to take effect.
[reconfigure]: path/to/administration/gitlab_restart.md#omnibus-gitlab-reconfigure
[restart]: path/to/administration/gitlab_restart.md#installations-from-source
[reconfigure]: path/to/administration/restart_gitlab.md#omnibus-gitlab-reconfigure
[restart]: path/to/administration/restart_gitlab.md#installations-from-source
````
In this case:
......
......@@ -2,13 +2,13 @@
![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 [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
There are only two prerequisites in order to install GitLab on GCP:
......
......@@ -183,6 +183,7 @@ Available in [GitLab Enterprise Edition Starter](https://about.gitlab.com/gitlab
with **Member Lock** it is possible to lock membership in project to the
level of members in group.
<<<<<<< HEAD
Member Lock lets a group owner to lock down any new project membership to all the
projects within the group, allowing tighter control over project membership.
......@@ -198,6 +199,9 @@ and **Save group**.
This will disable the option for all users who previously had permissions to
operate project memberships so no new users can be added. Furthermore, any
request to add new user to project through API will not be possible.
=======
Learn more about [Member Lock](https://docs.gitlab.com/ee/user/group/index.html#member-lock-ees-eep).
>>>>>>> ce-com/master
#### Share with group lock (EES/EEP)
......@@ -206,6 +210,7 @@ it is possible to prevent projects in a group from [sharing
a project with another group](../../workflow/share_projects_with_other_groups.md).
This allows for tighter control over project access.
<<<<<<< HEAD
For example, consider you have two distinct teams (Group A and Group B)
working together in a project.
To inherit the group membership, you share the project between the
......@@ -217,15 +222,28 @@ To enable this feature, navigate to the group settings page. Select
**Share with group lock** and **Save the group**.
![Checkbox for share with group lock](img/share_with_group_lock.png)
=======
Learn more about [Share with group lock](https://docs.gitlab.com/ee/user/group/index.html#share-with-group-lock-ees-eep).
>>>>>>> ce-com/master
### Advanced settings
- **Projects**: view all projects within that group, add members to each project,
access each project's settings, and remove any project from the same screen.
- **Webhooks**: configure [webhooks](../project/integrations/webhooks.md)
<<<<<<< HEAD
and [push rules](../../push_rules/push_rules.md) to your group
- **Audit Events**: view [Audit Events](../../administration/audit_events.md)
for the group (GitLab admins only)
- **Pipelines quota**: keep track of the [pipeline quota](../admin_area/settings/continuous_integration.md) for the group
[permissions]: ../permissions.md#permissions
=======
and [push rules](https://docs.gitlab.com/ee/push_rules/push_rules.html#push-rules) to your group (Push Rules is available in [GitLab Enteprise Edition Starter][ee].)
- **Audit Events**: view [Audit Events](https://docs.gitlab.com/ee/administration/audit_events.html#audit-events)
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
[permissions]: ../permissions.md#permissions
[ee]: https://about.gitlab.com/products/
>>>>>>> ce-com/master
......@@ -34,7 +34,11 @@ your project public, open to collaboration.
### Streamline collaboration
<<<<<<< HEAD
With [Multiple Assignees for Issues](multiple_assignees_for_issues.md),
=======
With [Multiple Assignees for Issues](https://docs.gitlab.com/ee/user/project/issues/multiple_assignees_for_issues.html),
>>>>>>> ce-com/master
available in [GitLab Enterprise Edition Starter](https://about.gitlab.com/gitlab-ee/)
you can streamline collaboration and allow shared responsibilities to be clearly displayed.
All assignees are shown across your workflows and receive notifications (as they
......@@ -117,6 +121,7 @@ Find GitLab Issue Boards by navigating to your **Project's Dashboard** > **Issue
Read through the documentation for [Issue Boards](../issue_board.md)
to find out more about this feature.
<<<<<<< HEAD
#### Multiple Issue Boards (EES/EEP)
Multiple Issue Boards enables you to create more than one Issue Board per project.
......@@ -139,6 +144,10 @@ and appear in a block below the issue description. Issues can be across groups
and projects.
Read more about [Related Issues](related_issues.md).
=======
With [GitLab Enterprise Edition Starter](https://about.gitlab.com/gitlab-ee/), you can also
create various boards per project with [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards).
>>>>>>> ce-com/master
### Issue's API
......
......@@ -43,6 +43,11 @@ assigned to them if they created the issue themselves.
##### 3.1. Multiple Assignees (EES/EEP)
<<<<<<< HEAD
=======
Multiple Assignees are only available in [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee/).
>>>>>>> ce-com/master
Often multiple people likely work on the same issue together,
which can especially be difficult to track in large teams
where there is shared ownership of an issue.
......@@ -50,7 +55,11 @@ where there is shared ownership of an issue.
In GitLab Enterprise Edition, you can also select multiple assignees
to an issue.
<<<<<<< HEAD
Learn more on the [Multiple Assignees documentation](multiple_assignees_for_issues.md).
=======
Learn more on the [Multiple Assignees documentation](https://docs.gitlab.com/ee/user/project/issues/multiple_assignees_for_issues.html).
>>>>>>> ce-com/master
#### 4. Milestone
......
......@@ -26,12 +26,21 @@ With GitLab merge requests, you can:
With **[GitLab Enterprise Edition][ee]**, you can also:
<<<<<<< HEAD
- 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)
- Enable [fast-forward merge requests](#fast-forward-merge-requests) (available in GitLab Enterprise Edition Starter)
- [Squash and merge](#squash-and-merge) for a cleaner commit history (available in GitLab Enterprise Edition Starter)
- Enable [semi-linear history merge requests](#semi-linear-history-merge-requests) as another security layer to guarantee the pipeline is passing in the target branch (available in GitLab Enterprise Edition Starter)
- Analyze the impact of your changes with [Code Quality reports](#code-quality-reports) (available in GitLab Enterprise Edition Starter)
=======
- View the deployment process across projects with [Multi-Project Pipeline Graphs](https://docs.gitlab.com/ee/ci/multi_project_pipeline_graphs.html#multi-project-pipeline-graphs) (available only in GitLab Enterprise Edition Premium)
- Request [approvals](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) from your managers (available in GitLab Enterprise Edition Starter)
- Enable [fast-forward merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/fast_forward_merge.html) (available in GitLab Enterprise Edition Starter)
- [Squash and merge](https://docs.gitlab.com/ee/user/project/merge_requests/squash_and_merge.html) for a cleaner commit history (available in GitLab Enterprise Edition Starter)
- Enable [semi-linear history merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/index.html#semi-linear-history-merge-requests) as another security layer to guarantee the pipeline is passing in the target branch (available in GitLab Enterprise Edition Starter)
- Analise the impact of your changes with [Code Quality reports](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html) (available in GitLab Enterprise Edition Starter)
>>>>>>> ce-com/master
## Use cases
......@@ -39,6 +48,7 @@ A. Consider you are a software developer working in a team:
1. You checkout a new branch, and submit your changes through a merge request
1. You gather feedback from your team
<<<<<<< HEAD
1. You work on the implementation optimizing code with [Code Quality reports](#code-quality-reports)
1. You build and test your changes with GitLab CI/CD
1. You request the [approval](#merge-request-approvals) from your manager
......@@ -47,13 +57,28 @@ A. Consider you are a software developer working in a team:
1. Your implementations were successfully shipped to your customer
B. Consider you're a web developer writing a webpage for your company's website:
=======
1. You work on the implementation optimizing code with [Code Quality reports](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html) (available in GitLab Enterprise Edition Starter)
1. You build and test your changes with GitLab CI/CD
1. You request the approval from your manager
1. Your manager pushes a commit with his final review, [approves the merge request](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html), and set it to [merge when pipeline succeeds](#merge-when-pipeline-succeeds) (Merge Request Approvals are available in GitLab Enterprise Edition Starter)
1. Your changes get deployed to production with [manual actions](../../../ci/yaml/README.md#manual-actions) for GitLab CI/CD
1. Your implementations were successfully shipped to your customer
B. Consider you're a web developer writing a webpage for your company's:
>>>>>>> ce-com/master
1. You checkout a new branch, and submit a new page through a merge request
1. You gather feedback from your reviewers
1. Your changes are previewed with [Review Apps](../../../ci/review_apps/index.md)
1. You request your web designers for their implementation
<<<<<<< HEAD
1. You request the [approval](#merge-request-approvals) from your manager
1. Once approved, your merge request is [squashed and merged](#squash-and-merge), and [deployed to staging with GitLab Pages](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/)
=======
1. You request the [approval](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) from your manager (available in GitLab Enterprise Edition Starter)
1. Once approved, your merge request is [squashed and merged](https://docs.gitlab.com/ee/user/project/merge_requests/squash_and_merge.html), and [deployed to staging with GitLab Pages](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/) (Squash and Merge is available in GitLab Enterprise Edition Starter)
>>>>>>> ce-com/master
1. Your production team [cherry picks](#cherry-pick-changes) the merge commit into production
## Authorization for merge requests
......@@ -189,6 +214,16 @@ specific commit page.
You can append `?w=1` while on the diffs page of a merge request to ignore any
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
Here are some tips that will help you be more efficient with merge requests in
......@@ -277,8 +312,11 @@ git checkout origin/merge-requests/1
```
[protected branches]: ../protected_branches.md
<<<<<<< HEAD
[products]: https://about.gitlab.com/products/ "GitLab products page"
[ci]: ../../../ci/README.md
[cc]: https://codeclimate.com/
[cd]: https://hub.docker.com/r/codeclimate/codeclimate/
=======
>>>>>>> ce-com/master
[ee]: https://about.gitlab.com/gitlab-ee/ "GitLab Enterprise Edition"
......@@ -94,7 +94,8 @@ GitLab Pages for this project, the site will live under
Once you enable GitLab Pages for your project, your website
will be published under `https://john.gitlab.io`.
- 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`.
>**Note:**
......
<<<<<<< HEAD
=======
>>>>>>> ce-com/master
This document was moved to [another location](../user/group/index.md).
......@@ -80,6 +80,8 @@ module Ci
artifacts: job[:artifacts],
cache: job[:cache],
dependencies: job[:dependencies],
before_script: job[:before_script],
script: job[:script],
after_script: job[:after_script],
environment: job[:environment]
}.compact }
......
......@@ -12,7 +12,8 @@ module Gitlab
class << self
def from_commands(job)
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.when = WHEN_ON_SUCCESS
end
......
......@@ -35,9 +35,9 @@ module Gitlab
repository_storages.flat_map do |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_writable, :filesystem_write_latency, -> { 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_accessible, :filesystem_access_latency_seconds, -> { storage_stat_test(storage_name) }, 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_seconds, -> { storage_read_test(tmp_file_path) }, shard: storage_name)
].flatten
end
end
......
......@@ -20,7 +20,7 @@ module Gitlab
[
metric("#{metric_prefix}_timeout", result.is_a?(Timeout::Error) ? 1 : 0),
metric("#{metric_prefix}_success", is_successful?(result) ? 1 : 0),
metric("#{metric_prefix}_latency", elapsed)
metric("#{metric_prefix}_latency_seconds", elapsed)
]
end
end
......
module Gitlab
module Metrics
class ConnectionRackMiddleware
class RequestsRackMiddleware
def initialize(app)
@app = app
end
def self.rack_request_count
@rack_request_count ||= Gitlab::Metrics.counter(:rack_request, 'Rack request count')
end
def self.rack_response_count
@rack_response_count ||= Gitlab::Metrics.counter(:rack_response, 'Rack response count')
def self.http_request_total
@http_request_total ||= Gitlab::Metrics.counter(:http_requests_total, 'Request count')
end
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
def self.rack_execution_time
@rack_execution_time ||= Gitlab::Metrics.histogram(:rack_execution_time, 'Rack connection handling execution time',
{}, [0.05, 0.1, 0.25, 0.5, 0.7, 1, 1.5, 2, 2.5, 3, 5, 7, 10])
def self.http_request_duration_seconds
@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, 2.5, 5, 10, 25])
end
def call(env)
method = env['REQUEST_METHOD'].downcase
started = Time.now.to_f
begin
ConnectionRackMiddleware.rack_request_count.increment(method: method)
RequestsRackMiddleware.http_request_total.increment(method: method)
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]
rescue
ConnectionRackMiddleware.rack_uncaught_errors_count.increment
RequestsRackMiddleware.rack_uncaught_errors_count.increment
raise
ensure
elapsed = Time.now.to_f - started
ConnectionRackMiddleware.rack_execution_time.observe({}, elapsed)
end
end
end
......
......@@ -15,11 +15,11 @@ msgstr ""
"X-Generator: Zanata 3.9.6\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 ""
"%d additional commits have been omitted to prevent performance issues."
msgstr[0] "%d подаване беше пропуснато, за да не се натоварва системата."
msgstr[1] "%d подавания бяха пропуснати, за да не се натоварва системата."
"%s additional commits have been omitted to prevent performance issues."
msgstr[0] "%s подаване беше пропуснато, за да не се натоварва системата."
msgstr[1] "%s подавания бяха пропуснати, за да не се натоварва системата."
msgid "%d commit"
msgid_plural "%d commits"
......
......@@ -17,8 +17,8 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"\n"
msgid "%d additional commit has been omitted to prevent performance issues."
msgid_plural "%d additional commits have been omitted to prevent performance issues."
msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues."
msgstr[0] ""
msgstr[1] ""
......
......@@ -15,11 +15,11 @@ msgstr ""
"X-Generator: Zanata 3.9.6\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 ""
"%d additional commits have been omitted to prevent performance issues."
msgstr[0] "%d enmetado estis transsaltita, por ne troŝarĝi la sistemon."
msgstr[1] "%d enmetadoj estis transsaltitaj, por ne troŝarĝi la sistemon."
"%s additional commits have been omitted to prevent performance issues."
msgstr[0] "%s enmetado estis transsaltita, por ne troŝarĝi la sistemon."
msgstr[1] "%s enmetadoj estis transsaltitaj, por ne troŝarĝi la sistemon."
msgid "%d commit"
msgid_plural "%d commits"
......
......@@ -15,14 +15,14 @@ msgstr ""
"X-Generator: Zanata 3.9.6\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 ""
"%d additional commits have been omitted to prevent performance issues."
"%s additional commits have been omitted to prevent performance issues."
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."
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."
msgid "%d commit"
......
......@@ -15,10 +15,10 @@ msgstr ""
"X-Generator: Zanata 3.9.6\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 ""
"%d additional commits have been omitted to prevent performance issues."
msgstr[0] "为提高页面加载速度及性能,已省略了 %d 次提交。"
"%s additional commits have been omitted to prevent performance issues."
msgstr[0] "为提高页面加载速度及性能,已省略了 %s 次提交。"
msgid "%d commit"
msgid_plural "%d commits"
......
......@@ -14,10 +14,10 @@ msgstr ""
"X-Generator: Zanata 3.9.6\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 ""
"%d additional commits have been omitted to prevent performance issues."
msgstr[0] "為提高頁面加載速度及性能,已省略了 %d 次提交。"
"%s additional commits have been omitted to prevent performance issues."
msgstr[0] "為提高頁面加載速度及性能,已省略了 %s 次提交。"
msgid "%d commit"
msgid_plural "%d commits"
......
......@@ -18,10 +18,10 @@ msgstr ""
"X-Generator: Zanata 3.9.6\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 ""
"%d additional commits have been omitted to prevent performance issues."
msgstr[0] "因效能考量,不顯示 %d 個更動 (commit)。"
"%s additional commits have been omitted to prevent performance issues."
msgstr[0] "因效能考量,不顯示 %s 個更動 (commit)。"
msgid "%d commit"
msgid_plural "%d commits"
......
......@@ -137,6 +137,21 @@ describe AutocompleteController do
it { expect(body.size).to eq User.count }
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
let(:per_page) { 2 }
......
......@@ -24,7 +24,7 @@ describe MetricsController do
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_latency [0-9\.]+$/)
expect(response.body).to match(/^db_ping_latency_seconds [0-9\.]+$/)
end
it 'returns Redis ping metrics' do
......@@ -32,7 +32,7 @@ describe MetricsController do
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_latency [0-9\.]+$/)
expect(response.body).to match(/^redis_ping_latency_seconds [0-9\.]+$/)
end
it 'returns Caching ping metrics' 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_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
it 'returns Queues ping metrics' 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_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
it 'returns SharedState ping metrics' 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_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
it 'returns file system check metrics' do
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_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_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$/)
end
......
......@@ -343,7 +343,8 @@ describe Projects::CommitController do
get_pipelines(id: commit.id, format: :json)
expect(response).to be_ok
expect(JSON.parse(response.body)).not_to be_empty
expect(JSON.parse(response.body)['pipelines']).not_to be_empty
expect(JSON.parse(response.body)['count']['all']).to eq 1
end
end
end
......
......@@ -482,7 +482,8 @@ describe Projects::MergeRequestsController do
end
it 'responds with serialized pipelines' do
expect(json_response).not_to be_empty
expect(json_response['pipelines']).not_to be_empty
expect(json_response['count']['all']).to eq 1
end
end
......
......@@ -234,24 +234,43 @@ describe ProjectsController do
let(:admin) { create(:admin) }
let(:project) { create(:project, :repository) }
let(:new_path) { 'renamed_path' }
let(:project_params) { { path: new_path } }
before do
sign_in(admin)
end
it "sets the repository to the right path after a rename" do
controller.instance_variable_set(:@project, project)
context 'when only renaming a project path' do
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,
namespace_id: project.namespace,
id: project.id,
project: project_params
expect(project.path).to include 'renamed_path'
expect(assigns(:repository).path).to include project.path
expect(response).to have_http_status(302)
end
end
expect(project.repository.path).to include(new_path)
expect(assigns(:repository).path).to eq(project.repository.path)
expect(response).to have_http_status(302)
context 'when project has container repositories with tags' 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
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
......
......@@ -85,6 +85,41 @@ describe('Pipelines table in Commits and Merge requests', () => {
}, 0);
});
});
describe('pipeline badge counts', () => {
const pipelinesResponse = (request, next) => {
next(request.respondWith(JSON.stringify([pipeline]), {
status: 200,
}));
};
beforeEach(() => {
Vue.http.interceptors.push(pipelinesResponse);
});
afterEach(() => {
Vue.http.interceptors = _.without(Vue.http.interceptors, pipelinesResponse);
this.component.$destroy();
});
it('should receive update-pipelines-count event', (done) => {
const element = document.createElement('div');
document.body.appendChild(element);
element.addEventListener('update-pipelines-count', (event) => {
expect(event.detail.pipelines).toEqual([pipeline]);
done();
});
this.component = new PipelinesTable({
propsData: {
endpoint: 'endpoint',
helpPagePath: 'foo',
},
}).$mount();
element.appendChild(this.component.$el);
});
});
});
describe('unsuccessfull request', () => {
......
......@@ -163,7 +163,10 @@ module Ci
commands: "pwd\nrspec",
coverage_regex: nil,
tag_list: [],
options: {},
options: {
before_script: ["pwd"],
script: ["rspec"]
},
allow_failure: false,
when: "on_success",
environment: nil,
......@@ -616,10 +619,12 @@ module Ci
coverage_regex: nil,
tag_list: [],
options: {
image: { name: "ruby:2.1", entrypoint: ["/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"] }]
before_script: ["pwd"],
script: ["rspec"],
image: { name: "ruby:2.1", entrypoint: ["/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,
when: "on_success",
......@@ -649,10 +654,12 @@ module Ci
coverage_regex: nil,
tag_list: [],
options: {
image: { name: "ruby:2.5", entrypoint: ["/usr/local/bin/init", "run"] },
services: [{ name: "postgresql", alias: "db-pg", entrypoint: ["/usr/local/bin/init", "run"],
command: ["/usr/local/bin/init", "run"] },
{ name: "docker:dind" }]
before_script: ["pwd"],
script: ["rspec"],
image: { name: "ruby:2.5", entrypoint: ["/usr/local/bin/init", "run"] },
services: [{ name: "postgresql", alias: "db-pg", entrypoint: ["/usr/local/bin/init", "run"],
command: ["/usr/local/bin/init", "run"] },
{ name: "docker:dind" }]
},
allow_failure: false,
when: "on_success",
......@@ -680,6 +687,8 @@ module Ci
coverage_regex: nil,
tag_list: [],
options: {
before_script: ["pwd"],
script: ["rspec"],
image: { name: "ruby:2.1" },
services: [{ name: "mysql" }, { name: "docker:dind" }]
},
......@@ -707,8 +716,10 @@ module Ci
coverage_regex: nil,
tag_list: [],
options: {
image: { name: "ruby:2.5" },
services: [{ name: "postgresql" }, { name: "docker:dind" }]
before_script: ["pwd"],
script: ["rspec"],
image: { name: "ruby:2.5" },
services: [{ name: "postgresql" }, { name: "docker:dind" }]
},
allow_failure: false,
when: "on_success",
......@@ -951,6 +962,8 @@ module Ci
coverage_regex: nil,
tag_list: [],
options: {
before_script: ["pwd"],
script: ["rspec"],
image: { name: "ruby:2.1" },
services: [{ name: "mysql" }],
artifacts: {
......@@ -1162,7 +1175,9 @@ module Ci
commands: "test",
coverage_regex: nil,
tag_list: [],
options: {},
options: {
script: ["test"]
},
when: "on_success",
allow_failure: false,
environment: nil,
......@@ -1208,7 +1223,9 @@ module Ci
commands: "execute-script-for-job",
coverage_regex: nil,
tag_list: [],
options: {},
options: {
script: ["execute-script-for-job"]
},
when: "on_success",
allow_failure: false,
environment: nil,
......@@ -1221,7 +1238,9 @@ module Ci
commands: "execute-script-for-job",
coverage_regex: nil,
tag_list: [],
options: {},
options: {
script: ["execute-script-for-job"]
},
when: "on_success",
allow_failure: false,
environment: nil,
......
require 'spec_helper'
describe Gitlab::Ci::Build::Step do
let(:job) { create(:ci_build, :no_options, commands: "ls -la\ndate") }
describe '#from_commands' do
subject { described_class.from_commands(job) }
it 'fabricates an object' do
expect(subject.name).to eq(:script)
expect(subject.script).to eq(['ls -la', 'date'])
expect(subject.timeout).to eq(job.timeout)
expect(subject.when).to eq('on_success')
expect(subject.allow_failure).to be_falsey
shared_examples 'has correct script' do
subject { described_class.from_commands(job) }
it 'fabricates an object' do
expect(subject.name).to eq(:script)
expect(subject.script).to eq(script)
expect(subject.timeout).to eq(job.timeout)
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
describe '#from_after_script' do
let(:job) { create(:ci_build) }
subject { described_class.from_after_script(job) }
context 'when after_script is empty' 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_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_read_latency, 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_access_latency_seconds, 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_seconds, value: be >= 0))
end
end
......@@ -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_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_read_latency, 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_access_latency_seconds, 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_seconds, value: be >= 0))
end
end
end
......@@ -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_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_read_latency, 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_access_latency_seconds, 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_seconds, value: be >= 0))
end
end
end
......
......@@ -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}_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
context 'Check is misbehaving' do
......@@ -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}_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
context 'Check is timeouting' do
......@@ -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}_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
......
require 'spec_helper'
describe Gitlab::Metrics::ConnectionRackMiddleware do
describe Gitlab::Metrics::RequestsRackMiddleware do
let(:app) { double('app') }
subject { described_class.new(app) }
......@@ -22,14 +22,8 @@ describe Gitlab::Metrics::ConnectionRackMiddleware do
allow(app).to receive(:call).and_return([200, nil, nil])
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
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)
end
......@@ -38,20 +32,21 @@ describe Gitlab::Metrics::ConnectionRackMiddleware do
execution_time = 10
allow(app).to receive(:call) do |*args|
Timecop.freeze(execution_time.seconds)
[200, nil, nil]
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)
end
end
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
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
it 'increments exceptions count' do
......@@ -61,25 +56,13 @@ describe Gitlab::Metrics::ConnectionRackMiddleware do
end
it 'increments requests count' do
expect(described_class).to receive_message_chain(:rack_request_count, :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(described_class).to receive_message_chain(:http_request_total, :increment).with(method: 'get')
expect { subject.call(env) }.to raise_error(StandardError)
end
it 'measures execution time' do
execution_time = 10
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)
it "does't measure request execution time" do
expect(described_class.http_request_duration_seconds).not_to receive(:increment)
expect { subject.call(env) }.to raise_error(StandardError)
end
......
......@@ -1444,7 +1444,7 @@ describe Project, models: true do
subject { project.rename_repo }
it { expect{subject}.to raise_error(Exception) }
it { expect{subject}.to raise_error(StandardError) }
end
end
......
......@@ -791,7 +791,7 @@ describe User, models: true do
end
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
it 'returns users with a matching name regardless of the casing' do
......@@ -805,7 +805,7 @@ describe User, models: true do
end
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
it 'returns users with a matching Email regardless of the casing' do
......@@ -819,7 +819,7 @@ describe User, models: true do
end
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
it 'returns users with a matching username regardless of the casing' do
......
require 'spec_helper'
describe Projects::UpdateService, services: true do
describe Projects::UpdateService, '#execute', :services do
let(:user) { create(:user) }
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
it 'updates the project to internal' do
result = update_project(project, user, visibility_level: Gitlab::VisibilityLevel::INTERNAL)
......@@ -40,7 +43,7 @@ describe Projects::UpdateService, services: true do
it 'does not update the project to public' do
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
end
......@@ -55,12 +58,13 @@ describe Projects::UpdateService, services: true do
end
end
describe 'visibility_level' do
describe 'when updating project that has forks' do
let(:project) { create(:empty_project, :internal) }
let(:forked_project) { create(:forked_project_with_submodules, :internal) }
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
end
......@@ -89,10 +93,38 @@ describe Projects::UpdateService, services: true do
end
end
it 'returns an error result when record cannot be updated' do
result = update_project(project, admin, { name: 'foo&bar' })
context 'when updating a default branch' do
let(:project) { create(:project, :repository) }
expect(result).to eq({ status: :error, message: 'Project could not be updated' })
it 'changes a default branch' do
update_project(project, admin, default_branch: 'feature')
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
describe 'repository_storage' do
......
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