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 @@ ...@@ -8,8 +8,13 @@
## Test coverage ## 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 - [![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=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 ## Canonical source
......
...@@ -18,7 +18,19 @@ window.gl.CommitPipelinesTable = CommitPipelinesTable; ...@@ -18,7 +18,19 @@ window.gl.CommitPipelinesTable = CommitPipelinesTable;
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view'); const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
if (pipelineTableViewEl && pipelineTableViewEl.dataset.disableInitialization === undefined) { 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({ const table = new CommitPipelinesTable({
propsData: { propsData: {
endpoint: pipelineTableViewEl.dataset.endpoint, endpoint: pipelineTableViewEl.dataset.endpoint,
...@@ -27,4 +39,5 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -27,4 +39,5 @@ document.addEventListener('DOMContentLoaded', () => {
}).$mount(); }).$mount();
pipelineTableViewEl.appendChild(table.$el); pipelineTableViewEl.appendChild(table.$el);
} }
}
}); });
...@@ -55,6 +55,17 @@ ...@@ -55,6 +55,17 @@
// depending of the endpoint the response can either bring a `pipelines` key or not. // depending of the endpoint the response can either bring a `pipelines` key or not.
const pipelines = response.pipelines || response; const pipelines = response.pipelines || response;
this.setCommonData(pipelines); 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'; ...@@ -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 {
......
...@@ -4,12 +4,21 @@ class AutocompleteController < ApplicationController ...@@ -4,12 +4,21 @@ class AutocompleteController < ApplicationController
before_action :find_users, only: [:users] before_action :find_users, only: [:users]
def users def users
<<<<<<< HEAD
@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.search(params[:search]) if params[:search].present?
@users = @users.where.not(id: params[:skip_users]) if params[:skip_users].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 = 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])
=======
@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 if params[:todo_filter].present? && current_user
@users = @users.todo_authors(current_user.id, params[:todo_state_filter]) @users = @users.todo_authors(current_user.id, params[:todo_state_filter])
......
...@@ -38,9 +38,14 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -38,9 +38,14 @@ class Projects::CommitController < Projects::ApplicationController
format.json do format.json do
Gitlab::PollingInterval.set_header(response, interval: 10_000) Gitlab::PollingInterval.set_header(response, interval: 10_000)
render json: PipelineSerializer render json: {
pipelines: PipelineSerializer
.new(project: @project, current_user: @current_user) .new(project: @project, current_user: @current_user)
.represent(@pipelines) .represent(@pipelines),
count: {
all: @pipelines.count
}
}
end end
end end
end end
......
...@@ -114,9 +114,14 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo ...@@ -114,9 +114,14 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
Gitlab::PollingInterval.set_header(response, interval: 10_000) Gitlab::PollingInterval.set_header(response, interval: 10_000)
render json: PipelineSerializer render json: {
pipelines: PipelineSerializer
.new(project: @project, current_user: @current_user) .new(project: @project, current_user: @current_user)
.represent(@pipelines) .represent(@pipelines),
count: {
all: @pipelines.count
}
}
end end
def edit def edit
......
...@@ -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 unless visibility_level_allowed?
new_visibility = params[:visibility_level] return error('New visibility level not allowed!')
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 end
<<<<<<< HEAD
# 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
...@@ -28,6 +21,14 @@ module Projects ...@@ -28,6 +21,14 @@ module Projects
if new_repository_storage && can?(current_user, :change_repository_storage, project) if new_repository_storage && can?(current_user, :change_repository_storage, project)
project.change_repository_storage(new_repository_storage) project.change_repository_storage(new_repository_storage)
end 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 end
if project.update_attributes(params) if project.update_attributes(params)
...@@ -39,8 +40,33 @@ module Projects ...@@ -39,8 +40,33 @@ module Projects
success success
else 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
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
end end
...@@ -7,4 +7,4 @@ ...@@ -7,4 +7,4 @@
= nav_link(path: 'commit#pipelines') do = nav_link(path: 'commit#pipelines') do
= link_to pipelines_project_commit_path(@project, @commit.id) do = link_to pipelines_project_commit_path(@project, @commit.id) do
Pipelines Pipelines
%span.badge= @commit.pipelines.size %span.badge.js-pipelines-mr-count= @commit.pipelines.size
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
%li.pipelines-tab %li.pipelines-tab
= link_to pipelines_project_merge_request_path(@project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do = link_to pipelines_project_merge_request_path(@project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do
Pipelines Pipelines
%span.badge= @pipelines.size %span.badge.js-pipelines-mr-count= @pipelines.size
%li.diffs-tab %li.diffs-tab
= link_to diffs_project_merge_request_path(@project, @merge_request), data: { target: 'div#diffs', action: 'diffs', toggle: 'tab' } do = link_to diffs_project_merge_request_path(@project, @merge_request), data: { target: 'div#diffs', action: 'diffs', toggle: 'tab' } do
Changes 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_ ...@@ -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',
......
...@@ -51,7 +51,11 @@ Shortcuts to GitLab's most visited docs: ...@@ -51,7 +51,11 @@ Shortcuts to GitLab's most visited docs:
- [Importing and exporting projects between instances](user/project/settings/import_export.md). - [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. - [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. - [Groups](user/group/index.md): Organize your projects in groups.
<<<<<<< HEAD
- [Subgroups](user/group/subgroups/index.md): nest groups in subgroups. - [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. - [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 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. - **(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 ...@@ -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: 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 ### 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
...@@ -270,6 +271,12 @@ As an example the above DN would look like ...@@ -270,6 +271,12 @@ 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
``` ```
=======
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 Please note that GitLab does not support the custom filter syntax used by
omniauth-ldap. omniauth-ldap.
......
...@@ -27,20 +27,23 @@ server, because the embedded server configuration is overwritten once every ...@@ -27,20 +27,23 @@ 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 |
| 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_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_success | Gauge | Whether or not the last redis ping succeeded |
| redis_ping_latency | Gauge | Round trip time of the redis ping | | redis_ping_latency_seconds | Gauge | Round trip time of the redis ping |
| filesystem_access_latency | gauge | Latency in accessing a specific filesystem | | user_session_logins_total | Counter | Counter of how many users have logged in |
| 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 |
[← 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:
......
...@@ -183,6 +183,7 @@ Available in [GitLab Enterprise Edition Starter](https://about.gitlab.com/gitlab ...@@ -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 with **Member Lock** it is possible to lock membership in project to the
level of members in group. level of members in group.
<<<<<<< HEAD
Member Lock lets a group owner to lock down any new project membership to all the 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. projects within the group, allowing tighter control over project membership.
...@@ -198,6 +199,9 @@ and **Save group**. ...@@ -198,6 +199,9 @@ and **Save group**.
This will disable the option for all users who previously had permissions to 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 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. 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) #### Share with group lock (EES/EEP)
...@@ -206,6 +210,7 @@ it is possible to prevent projects in a group from [sharing ...@@ -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). a project with another group](../../workflow/share_projects_with_other_groups.md).
This allows for tighter control over project access. This allows for tighter control over project access.
<<<<<<< HEAD
For example, consider you have two distinct teams (Group A and Group B) For example, consider you have two distinct teams (Group A and Group B)
working together in a project. working together in a project.
To inherit the group membership, you share the project between the 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 ...@@ -217,15 +222,28 @@ To enable this feature, navigate to the group settings page. Select
**Share with group lock** and **Save the group**. **Share with group lock** and **Save the group**.
![Checkbox for share with group lock](img/share_with_group_lock.png) ![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 ### Advanced settings
- **Projects**: view all projects within that group, add members to each project, - **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. 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)
<<<<<<< HEAD
and [push rules](../../push_rules/push_rules.md) to your group and [push rules](../../push_rules/push_rules.md) to your group
- **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)
- **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
=======
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. ...@@ -34,7 +34,11 @@ your project public, open to collaboration.
### Streamline collaboration ### Streamline collaboration
<<<<<<< HEAD
With [Multiple Assignees for Issues](multiple_assignees_for_issues.md), 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/) available in [GitLab Enterprise Edition Starter](https://about.gitlab.com/gitlab-ee/)
you can streamline collaboration and allow shared responsibilities to be clearly displayed. you can streamline collaboration and allow shared responsibilities to be clearly displayed.
All assignees are shown across your workflows and receive notifications (as they 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 ...@@ -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) Read through the documentation for [Issue Boards](../issue_board.md)
to find out more about this feature. to find out more about this feature.
<<<<<<< HEAD
#### Multiple Issue Boards (EES/EEP) #### Multiple Issue Boards (EES/EEP)
Multiple Issue Boards enables you to create more than one Issue Board per project. 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 ...@@ -139,6 +144,10 @@ and appear in a block below the issue description. Issues can be across groups
and projects. and projects.
Read more about [Related Issues](related_issues.md). 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 ### Issue's API
......
...@@ -43,6 +43,11 @@ assigned to them if they created the issue themselves. ...@@ -43,6 +43,11 @@ assigned to them if they created the issue themselves.
##### 3.1. Multiple Assignees (EES/EEP) ##### 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, Often multiple people likely work on the same issue together,
which can especially be difficult to track in large teams which can especially be difficult to track in large teams
where there is shared ownership of an issue. where there is shared ownership of an issue.
...@@ -50,7 +55,11 @@ 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 In GitLab Enterprise Edition, you can also select multiple assignees
to an issue. to an issue.
<<<<<<< HEAD
Learn more on the [Multiple Assignees documentation](multiple_assignees_for_issues.md). 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 #### 4. Milestone
......
...@@ -26,12 +26,21 @@ With GitLab merge requests, you can: ...@@ -26,12 +26,21 @@ With GitLab merge requests, you can:
With **[GitLab Enterprise Edition][ee]**, you can also: 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) - 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)
- [Squash and merge](#squash-and-merge) for a cleaner commit history (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) - 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) - 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 ## Use cases
...@@ -39,6 +48,7 @@ A. Consider you are a software developer working in a team: ...@@ -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 checkout a new branch, and submit your changes through a merge request
1. You gather feedback from your team 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 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 build and test your changes with GitLab CI/CD
1. You request the [approval](#merge-request-approvals) from your manager 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: ...@@ -47,13 +57,28 @@ A. Consider you are a software developer working in a team:
1. Your implementations were successfully shipped to your customer 1. Your implementations were successfully shipped to your customer
B. Consider you're a web developer writing a webpage for your company's website: 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 checkout a new branch, and submit a new page through a merge request
1. You gather feedback from your reviewers 1. You gather feedback from your reviewers
1. Your changes are previewed with [Review Apps](../../../ci/review_apps/index.md) 1. Your changes are previewed with [Review Apps](../../../ci/review_apps/index.md)
1. You request your web designers for their implementation 1. You request your web designers for their implementation
<<<<<<< HEAD
1. You request the [approval](#merge-request-approvals) from your manager 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. 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 1. Your production team [cherry picks](#cherry-pick-changes) the merge commit into production
## Authorization for merge requests ## Authorization for merge requests
...@@ -189,6 +214,16 @@ specific commit page. ...@@ -189,6 +214,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
...@@ -277,8 +312,11 @@ git checkout origin/merge-requests/1 ...@@ -277,8 +312,11 @@ git checkout origin/merge-requests/1
``` ```
[protected branches]: ../protected_branches.md [protected branches]: ../protected_branches.md
<<<<<<< HEAD
[products]: https://about.gitlab.com/products/ "GitLab products page" [products]: https://about.gitlab.com/products/ "GitLab products page"
[ci]: ../../../ci/README.md [ci]: ../../../ci/README.md
[cc]: https://codeclimate.com/ [cc]: https://codeclimate.com/
[cd]: https://hub.docker.com/r/codeclimate/codeclimate/ [cd]: https://hub.docker.com/r/codeclimate/codeclimate/
=======
>>>>>>> ce-com/master
[ee]: https://about.gitlab.com/gitlab-ee/ "GitLab Enterprise Edition" [ee]: https://about.gitlab.com/gitlab-ee/ "GitLab Enterprise Edition"
...@@ -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:**
......
<<<<<<< HEAD
=======
>>>>>>> ce-com/master
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
......
...@@ -343,7 +343,8 @@ describe Projects::CommitController do ...@@ -343,7 +343,8 @@ describe Projects::CommitController do
get_pipelines(id: commit.id, format: :json) get_pipelines(id: commit.id, format: :json)
expect(response).to be_ok 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 end
end end
......
...@@ -482,7 +482,8 @@ describe Projects::MergeRequestsController do ...@@ -482,7 +482,8 @@ describe Projects::MergeRequestsController do
end end
it 'responds with serialized pipelines' do 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
end end
......
...@@ -234,27 +234,46 @@ describe ProjectsController do ...@@ -234,27 +234,46 @@ 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
context 'when only renaming a project path' do
it "sets the repository to the right path after a rename" do it "sets the repository to the right path after a rename" do
controller.instance_variable_set(:@project, project) 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,
project: project_params
expect(project.repository.path).to include(new_path)
expect(assigns(:repository).path).to eq(project.repository.path)
expect(response).to have_http_status(302) expect(response).to have_http_status(302)
end end
end end
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
describe '#transfer' do describe '#transfer' do
render_views render_views
......
...@@ -85,6 +85,41 @@ describe('Pipelines table in Commits and Merge requests', () => { ...@@ -85,6 +85,41 @@ describe('Pipelines table in Commits and Merge requests', () => {
}, 0); }, 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', () => { describe('unsuccessfull request', () => {
......
...@@ -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,6 +619,8 @@ module Ci ...@@ -616,6 +619,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", entrypoint: ["/usr/local/bin/init", "run"] }, image: { name: "ruby:2.1", entrypoint: ["/usr/local/bin/init", "run"] },
services: [{ name: "mysql" }, services: [{ name: "mysql" },
{ name: "docker:dind", alias: "docker", entrypoint: ["/usr/local/bin/init", "run"], { name: "docker:dind", alias: "docker", entrypoint: ["/usr/local/bin/init", "run"],
...@@ -649,6 +654,8 @@ module Ci ...@@ -649,6 +654,8 @@ module Ci
coverage_regex: nil, coverage_regex: nil,
tag_list: [], tag_list: [],
options: { options: {
before_script: ["pwd"],
script: ["rspec"],
image: { name: "ruby:2.5", entrypoint: ["/usr/local/bin/init", "run"] }, image: { name: "ruby:2.5", entrypoint: ["/usr/local/bin/init", "run"] },
services: [{ name: "postgresql", alias: "db-pg", entrypoint: ["/usr/local/bin/init", "run"], services: [{ name: "postgresql", alias: "db-pg", entrypoint: ["/usr/local/bin/init", "run"],
command: ["/usr/local/bin/init", "run"] }, command: ["/usr/local/bin/init", "run"] },
...@@ -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,6 +716,8 @@ module Ci ...@@ -707,6 +716,8 @@ module Ci
coverage_regex: nil, coverage_regex: nil,
tag_list: [], tag_list: [],
options: { options: {
before_script: ["pwd"],
script: ["rspec"],
image: { name: "ruby:2.5" }, image: { name: "ruby:2.5" },
services: [{ name: "postgresql" }, { name: "docker:dind" }] services: [{ name: "postgresql" }, { name: "docker:dind" }]
}, },
...@@ -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
shared_examples 'has correct script' do
subject { described_class.from_commands(job) } subject { described_class.from_commands(job) }
it 'fabricates an object' do it 'fabricates an object' do
expect(subject.name).to eq(:script) expect(subject.name).to eq(:script)
expect(subject.script).to eq(['ls -la', 'date']) expect(subject.script).to eq(script)
expect(subject.timeout).to eq(job.timeout) expect(subject.timeout).to eq(job.timeout)
expect(subject.when).to eq('on_success') expect(subject.when).to eq('on_success')
expect(subject.allow_failure).to be_falsey expect(subject.allow_failure).to be_falsey
end end
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 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
context 'when updating a default branch' do
let(:project) { create(:project, :repository) }
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 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 end
describe 'repository_storage' do 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