Commit 2a8e066a authored by Valery Sizov's avatar Valery Sizov

Merge branch 'ce_upstream' into 'master'

CE upstream

See merge request !991
parents 03624742 08e187b9
......@@ -33,6 +33,7 @@ gem 'omniauth-shibboleth', '~> 1.2.0'
gem 'omniauth-twitter', '~> 1.2.0'
gem 'omniauth_crowd', '~> 2.2.0'
gem 'gssapi', group: :kerberos
gem 'omniauth-authentiq', '~> 0.2.0'
gem 'rack-oauth2', '~> 1.2.1'
gem 'jwt'
......
......@@ -452,6 +452,8 @@ GEM
rack (>= 1.0, < 3)
omniauth-auth0 (1.4.1)
omniauth-oauth2 (~> 1.1)
omniauth-authentiq (0.2.2)
omniauth-oauth2 (~> 1.3, >= 1.3.1)
omniauth-azure-oauth2 (0.0.6)
jwt (~> 1.0)
omniauth (~> 1.0)
......@@ -930,6 +932,7 @@ DEPENDENCIES
oj (~> 2.17.4)
omniauth (~> 1.3.1)
omniauth-auth0 (~> 1.4.1)
omniauth-authentiq (~> 0.2.0)
omniauth-azure-oauth2 (~> 0.0.6)
omniauth-cas3 (~> 1.1.2)
omniauth-facebook (~> 4.0.0)
......
......@@ -4,6 +4,7 @@
(function() {
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
var AUTO_SCROLL_OFFSET = 75;
this.Build = (function() {
Build.interval = null;
......@@ -19,6 +20,17 @@
this.buildStage = options.buildStage;
this.updateDropdown = bind(this.updateDropdown, this);
this.$document = $(document);
this.$body = $('body');
this.$buildTrace = $('#build-trace');
this.$autoScrollContainer = $('.autoscroll-container');
this.$autoScrollStatus = $('#autoscroll-status');
this.$autoScrollStatusText = this.$autoScrollStatus.find('.status-text');
this.$upBuildTrace = $('#up-build-trace');
this.$downBuildTrace = $('#down-build-trace');
this.$scrollTopBtn = $('#scroll-top');
this.$scrollBottomBtn = $('#scroll-bottom');
this.$buildRefreshAnimation = $('.js-build-refresh');
clearInterval(Build.interval);
// Init breakpoint checker
this.bp = Breakpoints.get();
......@@ -32,6 +44,7 @@
this.$document.off('click', '.js-sidebar-build-toggle').on('click', '.js-sidebar-build-toggle', this.sidebarOnClick.bind(this));
this.$document.off('click', '.stage-item').on('click', '.stage-item', this.updateDropdown);
this.$document.on('scroll', this.initScrollMonitor.bind(this));
$(window).off('resize.build').on('resize.build', this.sidebarOnResize.bind(this));
$('a', this.$buildScroll).off('click.stepTrace').on('click.stepTrace', this.stepTrace);
this.updateArtifactRemoveDate();
......@@ -40,18 +53,6 @@
this.initScrollButtonAffix();
}
if (this.buildStatus === "running" || this.buildStatus === "pending") {
// Bind autoscroll button to follow build output
$('#autoscroll-button').on('click', function() {
var state;
state = $(this).data("state");
if ("enabled" === state) {
$(this).data("state", "disabled");
return $(this).text("Enable autoscroll");
} else {
$(this).data("state", "enabled");
return $(this).text("Disable autoscroll");
}
});
Build.interval = setInterval((function(_this) {
// Check for new build output if user still watching build page
// Only valid for runnig build when output changes during time
......@@ -91,9 +92,10 @@
success: function(buildData) {
$('.js-build-output').html(buildData.trace_html);
if (removeRefreshStatuses.indexOf(buildData.status) >= 0) {
return $('.js-build-refresh').remove();
this.initScrollMonitor();
return this.$buildRefreshAnimation.remove();
}
}
}.bind(this)
});
};
......@@ -122,22 +124,95 @@
};
Build.prototype.checkAutoscroll = function() {
if ("enabled" === $("#autoscroll-button").data("state")) {
return $("html,body").scrollTop($("#build-trace").height());
if (this.$autoScrollStatus.data("state") === "enabled") {
return $("html,body").scrollTop(this.$buildTrace.height());
}
// Handle a situation where user started new build
// but never scrolled a page
if (!this.$scrollTopBtn.is(':visible') &&
!this.$scrollBottomBtn.is(':visible') &&
!gl.utils.isInViewport(this.$downBuildTrace.get(0))) {
this.$scrollBottomBtn.show();
}
};
Build.prototype.initScrollButtonAffix = function() {
var $body, $buildTrace;
$body = $('body');
$buildTrace = $('#build-trace');
return this.$buildScroll.affix({
offset: {
bottom: function() {
return $body.outerHeight() - ($buildTrace.outerHeight() + $buildTrace.offset().top);
}
// Hide everything initially
this.$scrollTopBtn.hide();
this.$scrollBottomBtn.hide();
this.$autoScrollContainer.hide();
}
// Page scroll listener to detect if user has scrolling page
// and handle following cases
// 1) User is at Top of Build Log;
// - Hide Top Arrow button
// - Show Bottom Arrow button
// - Disable Autoscroll and hide indicator (when build is running)
// 2) User is at Bottom of Build Log;
// - Show Top Arrow button
// - Hide Bottom Arrow button
// - Enable Autoscroll and show indicator (when build is running)
// 3) User is somewhere in middle of Build Log;
// - Show Top Arrow button
// - Show Bottom Arrow button
// - Disable Autoscroll and hide indicator (when build is running)
Build.prototype.initScrollMonitor = function() {
if (!gl.utils.isInViewport(this.$upBuildTrace.get(0)) && !gl.utils.isInViewport(this.$downBuildTrace.get(0))) {
// User is somewhere in middle of Build Log
this.$scrollTopBtn.show();
if (this.buildStatus === 'success' || this.buildStatus === 'failed') { // Check if Build is completed
this.$scrollBottomBtn.show();
} else if (this.$buildRefreshAnimation.is(':visible') && !gl.utils.isInViewport(this.$buildRefreshAnimation.get(0))) {
this.$scrollBottomBtn.show();
} else {
this.$scrollBottomBtn.hide();
}
});
// Hide Autoscroll Status Indicator
if (this.$scrollBottomBtn.is(':visible')) {
this.$autoScrollContainer.hide();
this.$autoScrollStatusText.removeClass('animate');
} else {
this.$autoScrollContainer.css({ top: this.$body.outerHeight() - AUTO_SCROLL_OFFSET }).show();
this.$autoScrollStatusText.addClass('animate');
}
} else if (gl.utils.isInViewport(this.$upBuildTrace.get(0)) && !gl.utils.isInViewport(this.$downBuildTrace.get(0))) {
// User is at Top of Build Log
this.$scrollTopBtn.hide();
this.$scrollBottomBtn.show();
this.$autoScrollContainer.hide();
this.$autoScrollStatusText.removeClass('animate');
} else if ((!gl.utils.isInViewport(this.$upBuildTrace.get(0)) && gl.utils.isInViewport(this.$downBuildTrace.get(0))) ||
(this.$buildRefreshAnimation.is(':visible') && gl.utils.isInViewport(this.$buildRefreshAnimation.get(0)))) {
// User is at Bottom of Build Log
this.$scrollTopBtn.show();
this.$scrollBottomBtn.hide();
// Show and Reposition Autoscroll Status Indicator
this.$autoScrollContainer.css({ top: this.$body.outerHeight() - AUTO_SCROLL_OFFSET }).show();
this.$autoScrollStatusText.addClass('animate');
} else if (gl.utils.isInViewport(this.$upBuildTrace.get(0)) && gl.utils.isInViewport(this.$downBuildTrace.get(0))) {
// Build Log height is small
this.$scrollTopBtn.hide();
this.$scrollBottomBtn.hide();
// Hide Autoscroll Status Indicator
this.$autoScrollContainer.hide();
this.$autoScrollStatusText.removeClass('animate');
}
if (this.buildStatus === "running" || this.buildStatus === "pending") {
// Check if Refresh Animation is in Viewport and enable Autoscroll, disable otherwise.
this.$autoScrollStatus.data("state", gl.utils.isInViewport(this.$buildRefreshAnimation.get(0)) ? 'enabled' : 'disabled');
}
};
Build.prototype.shouldHideSidebarForViewport = function() {
......
......@@ -141,7 +141,6 @@
new MergedButtons();
break;
case 'projects:merge_requests:commits':
case 'projects:merge_requests:builds':
new MergedButtons();
break;
case 'projects:merge_requests:pipelines':
......@@ -171,9 +170,6 @@
container: '.js-pipeline-table',
});
break;
case 'projects:commit:builds':
new gl.Pipelines();
break;
case 'projects:commits:show':
case 'projects:activity':
shortcut_handler = new ShortcutsNavigation();
......
......@@ -112,7 +112,6 @@
return value.path != null ? this.Emoji.template : this.Loading.template;
}.bind(this),
insertTpl: ':${name}:',
startWithSpace: false,
skipSpecialCharacterTest: true,
data: this.defaultLoadingData,
callbacks: {
......@@ -129,7 +128,6 @@
}.bind(this),
insertTpl: '${atwho-at}${username}',
searchKey: 'search',
startWithSpace: false,
alwaysHighlightFirst: true,
skipSpecialCharacterTest: true,
data: this.defaultLoadingData,
......@@ -172,7 +170,6 @@
}.bind(this),
data: this.defaultLoadingData,
insertTpl: '${atwho-at}${id}',
startWithSpace: false,
callbacks: {
sorter: this.DefaultOptions.sorter,
filter: this.DefaultOptions.filter,
......@@ -200,7 +197,6 @@
displayTpl: function(value) {
return value.title != null ? this.Milestones.template : this.Loading.template;
}.bind(this),
startWithSpace: false,
data: this.defaultLoadingData,
callbacks: {
matcher: this.DefaultOptions.matcher,
......@@ -225,7 +221,6 @@
at: '!',
alias: 'mergerequests',
searchKey: 'search',
startWithSpace: false,
displayTpl: function(value) {
return value.title != null ? this.Issues.template : this.Loading.template;
}.bind(this),
......@@ -259,7 +254,6 @@
return this.isLoading(value) ? this.Loading.template : this.Labels.template;
}.bind(this),
insertTpl: '${atwho-at}${title}',
startWithSpace: false,
callbacks: {
matcher: this.DefaultOptions.matcher,
sorter: this.DefaultOptions.sorter,
......@@ -379,14 +373,7 @@
togglePreventSelection(isPrevented = !!this.setting.tabSelectsMatch) {
this.setting.tabSelectsMatch = !isPrevented;
this.setting.spaceSelectsMatch = !isPrevented;
const eventListenerAction = `${isPrevented ? 'add' : 'remove'}EventListener`;
this.$inputor[0][eventListenerAction]('keydown', gl.GfmAutoComplete.preventSpaceTabEnter);
},
preventSpaceTabEnter(e) {
const key = e.which || e.keyCode;
const preventables = [9, 13, 32];
if (preventables.indexOf(key) > -1) e.preventDefault();
}
};
}).call(this);
/* eslint-disable func-names, no-var, camelcase, no-unused-vars, object-shorthand, space-before-function-paren, no-return-assign, comma-dangle, consistent-return, one-var, one-var-declaration-per-line, quotes, prefer-template, prefer-arrow-callback, prefer-const, padded-blocks, wrap-iife, max-len */
/* eslint-disable no-param-reassign, func-names, no-var, camelcase, no-unused-vars, object-shorthand, space-before-function-paren, no-return-assign, comma-dangle, consistent-return, one-var, one-var-declaration-per-line, quotes, prefer-template, prefer-arrow-callback, prefer-const, padded-blocks, wrap-iife, max-len */
/* global Issuable */
/* global Turbolinks */
(function() {
((global) => {
var issuable_created;
issuable_created = false;
this.Issuable = {
global.Issuable = {
init: function() {
Issuable.initTemplates();
Issuable.initSearch();
......@@ -111,7 +111,11 @@
filterResults: (function(_this) {
return function(form) {
var formAction, formData, issuesUrl;
formData = form.serialize();
formData = form.serializeArray();
formData = formData.filter(function(data) {
return data.value !== '';
});
formData = $.param(formData);
formAction = form.attr('action');
issuesUrl = formAction;
issuesUrl += "" + (formAction.indexOf('?') < 0 ? '?' : '&');
......@@ -184,4 +188,4 @@
}
};
}).call(this);
})(window);
......@@ -93,6 +93,19 @@
}
};
// Check if element scrolled into viewport from above or below
// Courtesy http://stackoverflow.com/a/7557433/414749
w.gl.utils.isInViewport = function(el) {
var rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= window.innerHeight &&
rect.right <= window.innerWidth
);
};
gl.utils.getPagePath = function() {
return $('body').data('page').split(':')[0];
};
......
......@@ -59,16 +59,13 @@
class MergeRequestTabs {
constructor({ action, setUrl, buildsLoaded, stubLocation } = {}) {
constructor({ action, setUrl, stubLocation } = {}) {
this.diffsLoaded = false;
this.buildsLoaded = false;
this.pipelinesLoaded = false;
this.commitsLoaded = false;
this.fixedLayoutPref = null;
this.setUrl = setUrl !== undefined ? setUrl : true;
this.buildsLoaded = buildsLoaded || false;
this.setCurrentAction = this.setCurrentAction.bind(this);
this.tabShown = this.tabShown.bind(this);
this.showTab = this.showTab.bind(this);
......@@ -119,10 +116,6 @@
$.scrollTo('.merge-request-details .merge-request-tabs', {
offset: -navBarHeight,
});
} else if (action === 'builds') {
this.loadBuilds($target.attr('href'));
this.expandView();
this.resetViewContainer();
} else if (action === 'pipelines') {
this.loadPipelines($target.attr('href'));
this.expandView();
......@@ -180,8 +173,8 @@
setCurrentAction(action) {
this.currentAction = action === 'show' ? 'notes' : action;
// Remove a trailing '/commits' '/diffs' '/builds' '/pipelines' '/new' '/new/diffs'
let newState = location.pathname.replace(/\/(commits|diffs|builds|pipelines|new|new\/diffs)(\.html)?\/?$/, '');
// Remove a trailing '/commits' '/diffs' '/pipelines' '/new' '/new/diffs'
let newState = location.pathname.replace(/\/(commits|diffs|pipelines|new|new\/diffs)(\.html)?\/?$/, '');
// Append the new action if we're on a tab other than 'notes'
if (this.currentAction !== 'notes') {
......@@ -255,22 +248,6 @@
});
}
loadBuilds(source) {
if (this.buildsLoaded) {
return;
}
this.ajaxGet({
url: `${source}.json`,
success: (data) => {
document.querySelector('div#builds').innerHTML = data.html;
gl.utils.localTimeAgo($('.js-timeago', 'div#builds'));
this.buildsLoaded = true;
new gl.Pipelines();
this.scrollToElement('#builds');
},
});
}
loadPipelines(source) {
if (this.pipelinesLoaded) {
return;
......
......@@ -74,7 +74,7 @@
MergeRequestWidget.prototype.addEventListeners = function() {
var allowedPages;
allowedPages = ['show', 'commits', 'builds', 'pipelines', 'changes'];
allowedPages = ['show', 'commits', 'pipelines', 'changes'];
$(document).on('page:change.merge_request', (function(_this) {
return function() {
var page;
......@@ -190,7 +190,6 @@
message = message.replace('{{title}}', data.title);
notify(title, message, _this.opts.gitlab_icon, function() {
this.close();
return Turbolinks.visit(_this.opts.builds_path);
});
}
}
......
/* global merge_request_widget */
(() => {
$(() => {
/* TODO: This needs a better home, or should be refactored. It was previously contained
* in a script tag in app/views/projects/merge_requests/widget/open/_accept.html.haml,
* but Vue chokes on script tags and prevents their execution. So it was moved here
* temporarily.
* */
if ($('.accept-mr-form').length) {
$('.accept-mr-form').on('ajax:send', () => {
$('.accept-mr-form :input').disable();
});
$('.accept_merge_request').on('click', () => {
$('.js-merge-button').html('<i class="fa fa-spinner fa-spin"></i> Merge in progress');
});
$('.merge_when_build_succeeds').on('click', () => {
$('#merge_when_build_succeeds').val('1');
});
$('.js-merge-dropdown a').on('click', (e) => {
e.preventDefault();
$(this).closest('form').submit();
});
} else if ($('.rebase-in-progress').length) {
merge_request_widget.rebaseInProgress();
} else if ($('.rebase-mr-form').length) {
$('.rebase-mr-form').on('ajax:send', () => {
$('.rebase-mr-form :input').disable();
});
$('.js-rebase-button').on('click', () => {
$('.js-rebase-button').html("<i class='fa fa-spinner fa-spin'></i> Rebase in progress");
});
} else {
merge_request_widget.getMergeStatus();
}
});
})();
......@@ -9,6 +9,7 @@
@import "framework/asciidoctor.scss";
@import "framework/blocks.scss";
@import "framework/buttons.scss";
@import "framework/badges.scss";
@import "framework/calendar.scss";
@import "framework/callout.scss";
@import "framework/common.scss";
......
.badge {
font-weight: normal;
background-color: $badge-bg;
color: $badge-color;
vertical-align: baseline;
}
.badge-dark {
background-color: $badge-bg-dark;
color: $badge-color-dark;
}
......@@ -9,7 +9,7 @@
padding: 20px;
color: $gl-gray;
font-weight: normal;
font-size: 16px;
font-size: 14px;
line-height: 36px;
&.diff-collapsed {
......
......@@ -76,13 +76,6 @@
color: $black;
}
}
.badge {
font-weight: normal;
background-color: $nav-badge-bg;
color: $gl-gray-light;
vertical-align: baseline;
}
}
&.sub-nav {
......@@ -434,4 +427,4 @@
border-bottom: none;
}
}
}
}
\ No newline at end of file
......@@ -33,7 +33,7 @@
@import "bootstrap/labels";
@import "bootstrap/badges";
@import "bootstrap/alerts";
// @import "bootstrap/progress-bars";
@import "bootstrap/progress-bars";
@import "bootstrap/list-group";
@import "bootstrap/wells";
@import "bootstrap/close";
......
......@@ -136,8 +136,8 @@ $md-area-border: #ddd;
/*
* Code
*/
$code_font_size: 13px;
$code_line_height: 1.5;
$code_font_size: 12px;
$code_line_height: 1.6;
/*
* Padding
......@@ -286,6 +286,14 @@ $btn-active-gray: #ececec;
$btn-active-gray-light: e4e7ed;
$btn-white-active: #848484;
/*
* Badges
*/
$badge-bg: #f3f3f3;
$badge-bg-dark: #eee;
$badge-color: #929292;
$badge-color-dark: #8f8f8f;
/*
* Award emoji
*/
......
@keyframes fade-out-status {
0%, 50% { opacity: 1; }
100% { opacity: 0; }
}
@keyframes blinking-dots {
0% {
background-color: rgba($white-light, 1);
box-shadow: 12px 0 0 0 rgba($white-light,0.2),
24px 0 0 0 rgba($white-light,0.2);
}
25% {
background-color: rgba($white-light, 0.4);
box-shadow: 12px 0 0 0 rgba($white-light,2),
24px 0 0 0 rgba($white-light,0.2);
}
75% {
background-color: rgba($white-light, 0.4);
box-shadow: 12px 0 0 0 rgba($white-light,0.2),
24px 0 0 0 rgba($white-light,1);
}
100% {
background-color: rgba($white-light, 1);
box-shadow: 12px 0 0 0 rgba($white-light,0.2),
24px 0 0 0 rgba($white-light,0.2);
}
}
.build-page {
pre.trace {
background: $builds-trace-bg;
......@@ -14,47 +45,101 @@
}
}
.scroll-controls {
.scroll-step {
width: 31px;
margin: 0 0 0 auto;
.environment-information {
background-color: $gray-light;
border: 1px solid $border-color;
padding: 12px $gl-padding;
border-radius: $border-radius-default;
svg {
position: relative;
top: 1px;
margin-right: 5px;
}
}
}
.scroll-controls {
height: 100%;
.scroll-step {
width: 31px;
margin: 0 0 0 auto;
}
.scroll-link,
.autoscroll-container {
right: 25px;
z-index: 1;
}
.scroll-link {
position: fixed;
display: block;
margin-bottom: 10px;
&.affix-bottom {
position: absolute;
right: 25px;
&.scroll-top .gitlab-icon-scroll-up-hover,
&.scroll-top:hover .gitlab-icon-scroll-up,
&.scroll-bottom .gitlab-icon-scroll-down-hover,
&.scroll-bottom:hover .gitlab-icon-scroll-down {
display: none;
}
&.affix {
right: 25px;
bottom: 15px;
z-index: 1;
&.scroll-top:hover .gitlab-icon-scroll-up-hover,
&.scroll-bottom:hover .gitlab-icon-scroll-down-hover {
display: inline-block;
}
&.sidebar-expanded {
right: #{$gutter_width + ($gl-padding * 2)};
&.scroll-top {
top: 110px;
}
a {
display: block;
margin-bottom: 10px;
&.scroll-bottom {
bottom: -2px;
}
}
.environment-information {
background-color: $gray-light;
border: 1px solid $border-color;
padding: 12px $gl-padding;
border-radius: $border-radius-default;
.autoscroll-container {
position: absolute;
}
svg {
position: relative;
top: 1px;
margin-right: 5px;
&.sidebar-expanded {
.scroll-link,
.autoscroll-container {
right: ($gutter_width + ($gl-padding * 2));
}
}
}
.status-message {
display: inline-block;
color: $white-light;
.status-icon {
display: inline-block;
width: 16px;
height: 33px;
}
.status-text {
float: left;
opacity: 0;
margin-right: 10px;
font-weight: normal;
line-height: 1.8;
transition: opacity 1s ease-out;
&.animate {
animation: fade-out-status 2s ease;
}
}
&:hover .status-text {
opacity: 1;
}
}
.build-header {
position: relative;
padding: 0;
......@@ -109,6 +194,15 @@
.bash {
display: block;
}
.build-loader-animation {
position: relative;
width: 6px;
height: 6px;
margin: auto auto 12px 2px;
border-radius: 50%;
animation: blinking-dots 1s linear infinite;
}
}
.right-sidebar.build-sidebar {
......@@ -248,6 +342,12 @@
}
}
.build-sidebar {
.container-fluid.container-limited {
max-width: 100%;
}
}
.build-detail-row {
margin-bottom: 5px;
......
......@@ -121,13 +121,6 @@
.folder-name {
cursor: pointer;
.badge {
font-weight: normal;
background-color: $gray-darker;
color: $gl-gray-light;
vertical-align: baseline;
}
}
}
......@@ -142,4 +135,4 @@
margin-right: 0;
}
}
}
}
\ No newline at end of file
......@@ -434,6 +434,7 @@
.issuable-meta {
display: inline-block;
line-height: 18px;
font-size: 14px;
}
.js-issuable-selector-wrap {
......
......@@ -21,6 +21,14 @@
display: inline-block;
float: left;
.btn-success.dropdown-toggle .fa {
color: inherit;
}
.btn-success.dropdown-toggle:disabled {
background-color: $gl-success;
}
.accept_merge_request {
&.ci-pending,
&.ci-running {
......@@ -96,7 +104,7 @@
.mr-widget-body {
h4 {
font-weight: 600;
font-size: 17px;
font-size: 16px;
margin: 5px 0;
color: $gl-gray-dark;
......
......@@ -43,7 +43,7 @@ ul.notes {
}
.system-note-message {
display: inline-block;
display: inline;
&::first-letter {
text-transform: lowercase;
......@@ -55,7 +55,7 @@ ul.notes {
}
p {
display: inline-block;
display: inline;
margin: 0;
&::first-letter {
......@@ -151,6 +151,10 @@ ul.notes {
}
}
}
.note-headline-light {
display: inline;
}
}
.discussion-body {
......
......@@ -80,6 +80,10 @@
td {
padding: 10px 8px;
}
.commit-link {
padding: 9px 8px 10px;
}
}
tbody {
......@@ -193,7 +197,7 @@
width: 8px;
position: absolute;
right: -7px;
bottom: 9px;
bottom: 10px;
border-bottom: 2px solid $border-color;
}
}
......@@ -499,15 +503,10 @@
> .ci-action-icon-container {
position: absolute;
right: 4px;
right: 5px;
top: 5px;
}
.ci-status-icon {
position: relative;
top: 1px;
}
.ci-status-icon svg {
height: 20px;
width: 20px;
......@@ -614,6 +613,10 @@
a {
display: inline-block;
}
.build-content {
width: 138px;
&:hover {
background-color: $stage-hover-bg;
......@@ -623,15 +626,24 @@
ul {
max-height: 245px;
overflow: auto;
margin: 5px 0;
margin: 3px 0;
li {
padding-top: 2px;
margin: 0 5px;
margin: 4px 7px;
padding: 0 3px;
padding-left: 0;
padding-bottom: 0;
margin-bottom: 0;
line-height: 1.2;
line-height: 0;
.ci-action-icon-container:hover {
background-color: transparent;
}
.ci-status-icon {
position: relative;
top: 2px;
}
}
}
}
......@@ -680,11 +692,15 @@
.dropdown-build {
color: $gl-text-color-light;
.build-content {
padding: 3px 7px 6px;
}
.ci-action-icon-container {
padding: 0;
font-size: 11px;
float: right;
margin-top: 4px;
margin-top: 3px;
display: inline-block;
position: relative;
......@@ -694,16 +710,10 @@
}
}
&:hover {
background-color: $stage-hover-bg;
border-radius: 3px;
color: $gl-text-color;
}
.ci-action-icon-container {
i {
width: 25px;
height: 25px;
width: 24px;
height: 24px;
&::before {
top: 1px;
......@@ -740,6 +750,10 @@
margin: 0;
}
.dropdown-build .build-content {
padding: 3px 7px 7px;
}
.builds-dropdown-loading {
margin: 10px auto;
width: 18px;
......@@ -788,26 +802,32 @@
.mini-pipeline-graph-icon-container .ci-status-icon {
display: inline-block;
border: 1px solid;
border-radius: 20px;
border-radius: 22px;
margin-right: 1px;
width: 20px;
height: 20px;
width: 22px;
height: 22px;
position: relative;
z-index: 2;
transition: all 0.2s cubic-bezier(0.25, 0, 1, 1);
svg {
top: -1px;
left: -1px;
}
}
.stage-cell .mini-pipeline-graph-icon-container .ci-status-icon svg {
width: 22px;
height: 22px;
}
.builds-dropdown {
&:focus {
outline: none;
margin-right: -8px;
.ci-status-icon {
width: 28px;
width: 32px;
padding: 0 8px 0 0;
transition: width 0.2s cubic-bezier(0.25, 0, 1, 1);
......@@ -851,7 +871,7 @@
.mini-pipeline-graph-icon-container {
.ci-status-icon:hover,
.ci-status-icon:focus {
width: 28px;
width: 32px;
padding: 0 8px 0 0;
+ .dropdown-caret {
......@@ -863,7 +883,7 @@
font-size: 11px;
position: relative;
top: 3px;
left: -11px;
left: -14px;
margin-right: -6px;
display: none;
z-index: 2;
......
......@@ -929,3 +929,23 @@ a.allowed-to-push {
margin-right: 40px;
}
}
.services-installation-info .row {
margin-bottom: 10px;
}
.service-installation {
padding: 32px;
margin: 32px;
border-radius: 3px;
background-color: $white-light;
h3 {
margin-top: 0;
}
hr {
margin: 32px 0;
border-color: $border-color;
}
}
.container-fluid {
.ci-status {
padding: 2px 7px;
padding: 2px 7px 4px;
margin-right: 10px;
border: 1px solid $gray-darker;
white-space: nowrap;
......
......@@ -82,6 +82,8 @@ class GroupsController < Groups::ApplicationController
if Groups::UpdateService.new(@group, current_user, group_params).execute
redirect_to edit_group_path(@group), notice: "Group '#{@group.name}' was successfully updated."
else
@group.reset_path!
render action: "edit"
end
end
......
......@@ -8,13 +8,11 @@ class Projects::CommitController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project
before_action :authorize_download_code!, except: [:cancel_builds, :retry_builds]
before_action :authorize_update_build!, only: [:cancel_builds, :retry_builds]
before_action :authorize_download_code!
before_action :authorize_read_pipeline!, only: [:pipelines]
before_action :authorize_read_commit_status!, only: [:builds]
before_action :commit
before_action :define_commit_vars, only: [:show, :diff_for_path, :builds, :pipelines]
before_action :define_status_vars, only: [:show, :builds, :pipelines]
before_action :define_commit_vars, only: [:show, :diff_for_path, :pipelines]
before_action :define_status_vars, only: [:show, :pipelines]
before_action :define_note_vars, only: [:show, :diff_for_path]
before_action :authorize_edit_tree!, only: [:revert, :cherry_pick]
......@@ -35,25 +33,6 @@ class Projects::CommitController < Projects::ApplicationController
def pipelines
end
def builds
end
def cancel_builds
ci_builds.running_or_pending.each(&:cancel)
redirect_back_or_default default: builds_namespace_project_commit_path(project.namespace, project, commit.sha)
end
def retry_builds
ci_builds.latest.failed.each do |build|
if build.retryable?
Ci::Build.retry(build, current_user)
end
end
redirect_back_or_default default: builds_namespace_project_commit_path(project.namespace, project, commit.sha)
end
def branches
@branches = @project.repository.branch_names_contains(commit.id)
@tags = @project.repository.tag_names_contains(commit.id)
......@@ -98,10 +77,6 @@ class Projects::CommitController < Projects::ApplicationController
@noteable = @commit ||= @project.commit(params[:id])
end
def ci_builds
@ci_builds ||= Ci::Build.where(pipeline: pipelines)
end
def define_commit_vars
return git_not_found! unless commit
......@@ -133,8 +108,6 @@ class Projects::CommitController < Projects::ApplicationController
def define_status_vars
@ci_pipelines = project.pipelines.where(sha: commit.sha)
@statuses = CommitStatus.where(pipeline: @ci_pipelines).relevant
@builds = Ci::Build.where(pipeline: @ci_pipelines).relevant
end
def assign_change_commit_vars(mr_source_branch)
......
class Projects::MattermostsController < Projects::ApplicationController
include TriggersHelper
include ActionView::Helpers::AssetUrlHelper
layout 'project_settings'
before_action :authorize_admin_project!
before_action :service
before_action :teams, only: [:new]
def new
end
def create
result, message = @service.configure(current_user, configure_params)
if result
flash[:notice] = 'This service is now configured'
redirect_to edit_namespace_project_service_path(
@project.namespace, @project, service)
else
flash[:alert] = message || 'Failed to configure service'
redirect_to new_namespace_project_mattermost_path(
@project.namespace, @project)
end
end
private
def configure_params
params.require(:mattermost).permit(:trigger, :team_id).merge(
url: service_trigger_url(@service),
icon_url: asset_url('gitlab_logo.png'))
end
def teams
@teams ||= @service.list_teams(current_user)
end
def service
@service ||= @project.find_or_initialize_service('mattermost_slash_commands')
end
end
......@@ -9,11 +9,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
before_action :module_enabled
before_action :merge_request, only: [
:edit, :update, :show, :diffs, :commits, :conflicts, :conflict_for_path, :builds, :pipelines, :merge, :merge_check,
:edit, :update, :show, :diffs, :commits, :conflicts, :conflict_for_path, :pipelines, :merge, :merge_check,
:ci_status, :ci_environments_status, :toggle_subscription, :cancel_merge_when_build_succeeds, :remove_wip, :resolve_conflicts, :assign_related_issues,
:approve, :rebase
]
before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds, :pipelines]
before_action :validates_merge_request, only: [:show, :diffs, :commits, :pipelines]
before_action :define_show_vars, only: [:show, :diffs, :commits, :conflicts, :conflict_for_path, :builds, :pipelines]
before_action :define_widget_vars, only: [:merge, :cancel_merge_when_build_succeeds, :merge_check]
before_action :define_commit_vars, only: [:diffs]
......@@ -203,17 +203,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
end
def builds
respond_to do |format|
format.html do
define_discussion_vars
render 'show'
end
format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_builds') } }
end
end
def pipelines
@pipelines = @merge_request.all_pipelines
......
module AuthHelper
PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook azure_oauth2).freeze
PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook azure_oauth2 authentiq).freeze
FORM_BASED_PROVIDERS = [/\Aldap/, 'kerberos', 'crowd'].freeze
def ldap_enabled?
......
......@@ -188,7 +188,7 @@ module BlobHelper
end
def gitlab_ci_ymls
@gitlab_ci_ymls ||= Gitlab::Template::GitlabCiYmlTemplate.dropdown_names
@gitlab_ci_ymls ||= Gitlab::Template::GitlabCiYmlTemplate.dropdown_names(params[:context])
end
def dockerfile_names
......
module CiStatusHelper
def ci_status_path(pipeline)
project = pipeline.project
builds_namespace_project_commit_path(project.namespace, project, pipeline.sha)
namespace_project_pipeline_path(project.namespace, project, pipeline)
end
# Is used by Commit and Merge Request Widget
......
module MattermostHelper
def mattermost_teams_options(teams)
teams_options = teams.map do |id, options|
[options['display_name'] || options['name'], id]
end
teams_options.compact.unshift(['Select team...', '0'])
end
end
......@@ -303,13 +303,15 @@ module ProjectsHelper
end
end
def add_special_file_path(project, file_name:, commit_message: nil)
def add_special_file_path(project, file_name:, commit_message: nil, target_branch: nil, context: nil)
namespace_project_new_blob_path(
project.namespace,
project,
project.default_branch || 'master',
file_name: file_name,
commit_message: commit_message || "Add #{file_name.downcase}"
commit_message: commit_message || "Add #{file_name.downcase}",
target_branch: target_branch,
context: context
)
end
......
......@@ -98,7 +98,7 @@ class Namespace < ActiveRecord::Base
def move_dir
if any_project_has_container_registry_tags?
raise Exception.new('Namespace cannot be moved, because at least one project has tags in container registry')
raise Gitlab::UpdatePathError.new('Namespace cannot be moved, because at least one project has tags in container registry')
end
# Move the namespace directory in all storages paths used by member projects
......@@ -111,7 +111,7 @@ class Namespace < ActiveRecord::Base
# if we cannot move namespace directory we should rollback
# db changes in order to prevent out of sync between db and fs
raise Exception.new('namespace directory cannot be moved')
raise Gitlab::UpdatePathError.new('namespace directory cannot be moved')
end
end
......
......@@ -18,4 +18,34 @@ class MattermostSlashCommandsService < ChatSlashCommandsService
def to_param
'mattermost_slash_commands'
end
def configure(user, params)
token = Mattermost::Command.new(user).
create(command(params))
update(active: true, token: token) if token
rescue Mattermost::Error => e
[false, e.message]
end
def list_teams(user)
Mattermost::Team.new(user).all
rescue Mattermost::Error => e
[[], e.message]
end
private
def command(params)
pretty_project_name = project.name_with_namespace
params.merge(
auto_complete: true,
auto_complete_desc: "Perform common operations on: #{pretty_project_name}",
auto_complete_hint: '[help]',
description: "Perform common operations on: #{pretty_project_name}",
display_name: "GitLab / #{pretty_project_name}",
method: 'P',
user_name: 'GitLab')
end
end
......@@ -14,7 +14,13 @@ module Groups
group.assign_attributes(params)
group.save
begin
group.save
rescue Gitlab::UpdatePathError => e
group.errors.add(:base, e.message)
false
end
end
end
end
......@@ -14,7 +14,8 @@ class ProjectPathValidator < ActiveModel::EachValidator
# without tree as reserved name routing can match 'group/project' as group name,
# 'tree' as project name and 'deploy_keys' as route.
#
RESERVED = (NamespaceValidator::RESERVED +
RESERVED = (NamespaceValidator::RESERVED -
%w[dashboard help ci admin search notes services] +
%w[tree commits wikis new edit create update logs_tree
preview blob blame raw files create_dir find_file]).freeze
......
......@@ -33,7 +33,7 @@
= link_to admin_abuse_reports_path, title: "Abuse Reports" do
%span
Abuse Reports
%span.badge.count= number_with_delimiter(AbuseReport.count(:all))
%span.badge.badge-dark.count= number_with_delimiter(AbuseReport.count(:all))
= nav_link(controller: :licenses) do
= link_to admin_license_path, title: 'License' do
......
......@@ -26,13 +26,13 @@
%span
Issues
- issues = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute
%span.badge.count= number_with_delimiter(issues.count)
%span.badge.badge-dark.count= number_with_delimiter(issues.count)
= nav_link(path: 'groups#merge_requests') do
= link_to merge_requests_group_path(@group), title: 'Merge Requests' do
%span
Merge Requests
- merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute
%span.badge.count= number_with_delimiter(merge_requests.count)
%span.badge.badge-dark.count= number_with_delimiter(merge_requests.count)
= nav_link(controller: [:group_members]) do
= link_to group_group_members_path(@group), title: 'Members' do
%span
......
......@@ -61,14 +61,14 @@
%span
Issues
- if @project.default_issues_tracker?
%span.badge.count.issue_counter= number_with_delimiter(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count)
%span.badge.badge-dark.count.issue_counter= number_with_delimiter(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count)
- if project_nav_tab? :merge_requests
= nav_link(controller: :merge_requests) do
= link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do
%span
Merge Requests
%span.badge.count.merge_counter= number_with_delimiter(MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.count)
%span.badge.badge-dark.count.merge_counter= number_with_delimiter(MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.count)
- if project_nav_tab? :wiki
= nav_link(controller: :wikis) do
......
- ref = local_assigns.fetch(:ref)
- status = commit.status(ref)
- if status
= link_to builds_namespace_project_commit_path(commit.project.namespace, commit.project, commit), class: "ci-status ci-#{status}" do
= link_to pipelines_namespace_project_commit_path(commit.project.namespace, commit.project, commit), class: "ci-status ci-#{status}" do
= ci_icon_for_status(status)
= ci_label_for_status(status)
......
......@@ -7,7 +7,7 @@
= link_to pipeline_path(@build.pipeline) do
%strong ##{@build.pipeline.id}
for commit
= link_to ci_status_path(@build.pipeline) do
= link_to namespace_project_commit_path(@project.namespace, @project, @build.pipeline.sha) do
%strong= @build.pipeline.short_sha
from
= link_to namespace_project_commits_path(@project.namespace, @project, @build.ref) do
......
......@@ -56,17 +56,22 @@
- else
#js-build-scroll.scroll-controls
.scroll-step
= link_to '#build-trace', class: 'btn' do
%i.fa.fa-angle-up
= link_to '#down-build-trace', class: 'btn' do
%i.fa.fa-angle-down
%a{ href: '#up-build-trace', id: 'scroll-top', class: 'scroll-link scroll-top', title: 'Scroll to top' }
= custom_icon('scroll_up')
= custom_icon('scroll_up_hover_active')
%a{ href: '#down-build-trace', id: 'scroll-bottom', class: 'scroll-link scroll-bottom', title: 'Scroll to bottom' }
= custom_icon('scroll_down')
= custom_icon('scroll_down_hover_active')
- if @build.active?
.autoscroll-container
%button.btn.btn-sm#autoscroll-button{:type => "button", :data => {:state => 'disabled'}}
Enable autoscroll
%span.status-message#autoscroll-status{ data: { state: 'disabled' } }
%span.status-text Autoscroll active
%i.status-icon
= custom_icon('scroll_down_hover_active')
#up-build-trace
%pre.build-trace#build-trace
%code.bash.js-build-output
= icon("refresh spin", class: "js-build-refresh")
.build-loader-animation.js-build-refresh
#down-build-trace
......
- @ci_pipelines.each do |pipeline|
= render "pipeline", pipeline: pipeline, pipeline_details: true
......@@ -8,7 +8,3 @@
= link_to pipelines_namespace_project_commit_path(@project.namespace, @project, @commit.id) do
Pipelines
%span.badge= @ci_pipelines.count
= nav_link(path: 'commit#builds') do
= link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id) do
Builds
%span.badge= @statuses.count
- @no_container = true
- page_title "Builds", "#{@commit.title} (#{@commit.short_id})", "Commits"
= render "projects/commits/head"
%div{ class: container_class }
= render "commit_box"
= render "ci_menu"
= render "builds"
%p
You aren’t a member of any team on the Mattermost instance at
%strong= Gitlab.config.mattermost.host
%p
To install this service,
= link_to "#{Gitlab.config.mattermost.host}/select_team", target: '__blank' do
join a team
= icon('external-link')
and try again.
%hr
.clearfix
= link_to 'Go back', edit_namespace_project_service_path(@project.namespace, @project, @service), class: 'btn btn-lg pull-right'
%p
This service will be installed on the Mattermost instance at
%strong= link_to Gitlab.config.mattermost.host, Gitlab.config.mattermost.host
%hr
= form_for(:mattermost, method: :post, url: namespace_project_mattermost_path(@project.namespace, @project)) do |f|
%h4 Team
%p
= @teams.one? ? 'The team' : 'Select the team'
where the slash commands will be used in
- selected_id = @teams.keys.first if @teams.one?
= f.select(:team_id, mattermost_teams_options(@teams), {}, { class: 'form-control', selected: "#{selected_id}", disabled: @teams.one? })
.help-block
- if @teams.one?
This is the only team where you are an administrator.
- else
The list shows teams where you are administrator
To create a team, ask your Mattermost system administrator.
To create a team,
= link_to "#{Gitlab.config.mattermost.host}/create_team" do
use Mattermost's interface
= icon('external-link')
%hr
%h4 Command trigger word
%p Choose the word that will trigger commands
= f.text_field(:trigger, value: @project.path, class: 'form-control')
.help-block
%p
Trigger word must be unique, and can't begin with a slash or contain any spaces.
Use the word that works best for your team.
%p
Suggestions:
%code= 'gitlab'
%code= @project.path # Path contains no spaces, but dashes
%code= @project.path_with_namespace
%p
Reserved:
= link_to 'https://docs.mattermost.com/help/messaging/executing-commands.html#built-in-commands', target: '__blank' do
see list of built-in slash commands
= icon('external-link')
%hr
.clearfix
.pull-right
= link_to 'Cancel', edit_namespace_project_service_path(@project.namespace, @project, @service), class: 'btn btn-lg'
= f.submit 'Install', class: 'btn btn-save btn-lg'
.service-installation
.inline.pull-right
= custom_icon('mattermost_logo', size: 48)
%h3 Install Mattermost Command
- if @teams.empty?
= render 'no_teams'
- else
= render 'team_selection'
......@@ -34,11 +34,6 @@
= link_to url_for(params), data: {target: 'div#pipelines', action: 'pipelines', toggle: 'tab'} do
Pipelines
%span.badge= @pipelines.size
- if @pipeline.present?
%li.builds-tab
= link_to url_for(params), data: {target: 'div#builds', action: 'builds', toggle: 'tab'} do
Builds
%span.badge= @statuses_count
%li.diffs-tab
= link_to url_for(params.merge(action: 'new_diffs')), data: {target: 'div#diffs', action: 'new/diffs', toggle: 'tab'} do
Changes
......@@ -49,9 +44,6 @@
= render "projects/merge_requests/show/commits"
#diffs.diffs.tab-pane
- # This tab is always loaded via AJAX
- if @pipeline.present?
#builds.builds.tab-pane
= render "projects/merge_requests/show/builds"
- if @pipelines.any?
#pipelines.pipelines.tab-pane
= render "projects/merge_requests/show/pipelines"
......@@ -66,6 +58,5 @@
});
:javascript
var merge_request = new MergeRequest({
action: "#{(@show_changes_tab ? 'new/diffs' : 'new')}",
buildsLoaded: "#{@pipeline.present? ? 'true' : 'false'}"
action: "#{(@show_changes_tab ? 'new/diffs' : 'new')}"
});
......@@ -66,11 +66,6 @@
= link_to pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do
Pipelines
%span.badge= @pipelines.size
- if @pipeline.present?
%li.builds-tab
= link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#builds', action: 'builds', toggle: 'tab' } do
Builds
%span.badge= @statuses_count
%li.diffs-tab
= link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#diffs', action: 'diffs', toggle: 'tab' } do
Changes
......@@ -99,8 +94,6 @@
#commits.commits.tab-pane
- # This tab is always loaded via AJAX
#builds.builds.tab-pane
- # This tab is always loaded via AJAX
#pipelines.pipelines.tab-pane
- # This tab is always loaded via AJAX
#diffs.diffs.tab-pane
......
= render "projects/commit/pipeline", pipeline: @pipeline, link_to_commit: true
......@@ -17,7 +17,7 @@
- # TODO, remove in later versions when services like Jenkins will set CI status via Commit status API
.mr-widget-heading
- %w[success skipped canceled failed running pending].each do |status|
.ci_widget{class: "ci-#{status}", style: "display:none"}
.ci_widget{class: "ci-#{status} ci-status-icon-#{status}", style: "display:none"}
= ci_icon_for_status(status)
%span
CI build
......
......@@ -24,12 +24,10 @@
preparing: "{{status}} build",
normal: "Build {{status}}"
},
builds_path: "#{builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
pipelines_path: "#{pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}"
};
if (typeof merge_request_widget !== 'undefined') {
clearInterval(merge_request_widget.fetchBuildStatusInterval);
merge_request_widget.cancelPolling();
merge_request_widget.clearEventListeners();
}
......
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('merge_request_widget/ci_bundle.js')
- status_class = @pipeline ? " ci-#{@pipeline.status}" : nil
= form_for [:merge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post, html: { class: 'accept-mr-form js-quick-submit js-requires-input' } do |f|
......@@ -53,21 +56,3 @@
rows: 14, hint: true
= hidden_field_tag :merge_when_build_succeeds, "", autocomplete: "off"
:javascript
$('.accept-mr-form').on('ajax:send', function() {
$(".accept-mr-form :input").disable();
});
$('.accept_merge_request').on('click', function() {
$('.js-merge-button').html("<i class='fa fa-spinner fa-spin'></i> Merge in progress");
});
$('.merge_when_build_succeeds').on('click', function() {
$("#merge_when_build_succeeds").val("1");
});
$('.js-merge-dropdown a').on('click', function(e) {
e.preventDefault();
$(this).closest("form").submit();
});
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('merge_request_widget/ci_bundle.js')
%strong
= icon("spinner spin")
Checking ability to merge automatically&hellip;
:javascript
$(function() {
merge_request_widget.getMergeStatus();
});
......@@ -8,7 +8,6 @@
.col-lg-9
= form_for(@service, as: :service, url: namespace_project_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |form|
= render 'shared/service_settings', form: form, subject: @service
.footer-block.row-content-block
= form.submit 'Save changes', class: 'btn btn-save'
&nbsp;
......
- run_actions_text = "Perform common operations on this project: #{@project.name_with_namespace}"
To setup this service:
%ul.list-unstyled
%li
1.
= link_to 'Enable custom slash commands', 'https://docs.mattermost.com/developer/slash-commands.html#enabling-custom-commands'
on your Mattermost installation
%li
2.
= link_to 'Add a slash command', 'https://docs.mattermost.com/developer/slash-commands.html#set-up-a-custom-command'
in Mattermost with these options:
%hr
.help-form
.form-group
= label_tag :display_name, 'Display name', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :display_name, "GitLab / #{@project.name_with_namespace}", class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#display_name')
.form-group
= label_tag :description, 'Description', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :description, run_actions_text, class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#description')
.form-group
= label_tag nil, 'Command trigger word', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.text-block
%p Fill in the word that works best for your team.
%p
Suggestions:
%code= 'gitlab'
%code= @project.path # Path contains no spaces, but dashes
%code= @project.path_with_namespace
.form-group
= label_tag :request_url, 'Request URL', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :request_url, service_trigger_url(subject), class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#request_url')
.form-group
= label_tag nil, 'Request method', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.text-block POST
.form-group
= label_tag :response_username, 'Response username', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :response_username, 'GitLab', class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#response_username')
.form-group
= label_tag :response_icon, 'Response icon', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :response_icon, asset_url('gitlab_logo.png'), class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#response_icon')
.form-group
= label_tag nil, 'Autocomplete', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.text-block Yes
.form-group
= label_tag :autocomplete_hint, 'Autocomplete hint', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :autocomplete_hint, '[help]', class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#autocomplete_hint')
.form-group
= label_tag :autocomplete_description, 'Autocomplete description', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :autocomplete_description, run_actions_text, class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#autocomplete_description')
%hr
%ul.list-unstyled
%li
3. After adding the slash command, paste the
%strong token
into the field below
- run_actions_text = "Perform common operations on this project: #{@project.name_with_namespace}"
- enabled = Gitlab.config.mattermost.enabled
.well
This service allows GitLab users to perform common operations on this
......@@ -7,93 +7,9 @@
See list of available commands in Mattermost after setting up this service,
by entering
%code /&lt;command_trigger_word&gt; help
%br
%br
To setup this service:
%ul.list-unstyled
%li
1.
= link_to 'Enable custom slash commands', 'https://docs.mattermost.com/developer/slash-commands.html#enabling-custom-commands'
on your Mattermost installation
%li
2.
= link_to 'Add a slash command', 'https://docs.mattermost.com/developer/slash-commands.html#set-up-a-custom-command'
in Mattermost with these options:
%hr
.help-form
.form-group
= label_tag :display_name, 'Display name', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :display_name, "GitLab / #{@project.name_with_namespace}", class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#display_name')
.form-group
= label_tag :description, 'Description', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :description, run_actions_text, class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#description')
.form-group
= label_tag nil, 'Command trigger word', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.text-block
%p Fill in the word that works best for your team.
%p
Suggestions:
%code= 'gitlab'
%code= @project.path # Path contains no spaces, but dashes
%code= @project.path_with_namespace
.form-group
= label_tag :request_url, 'Request URL', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :request_url, service_trigger_url(subject), class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#request_url')
.form-group
= label_tag nil, 'Request method', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.text-block POST
.form-group
= label_tag :response_username, 'Response username', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :response_username, 'GitLab', class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#response_username')
.form-group
= label_tag :response_icon, 'Response icon', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :response_icon, asset_url('gitlab_logo.png'), class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#response_icon')
.form-group
= label_tag nil, 'Autocomplete', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.text-block Yes
.form-group
= label_tag :autocomplete_hint, 'Autocomplete hint', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :autocomplete_hint, '[help]', class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#autocomplete_hint')
.form-group
= label_tag :autocomplete_description, 'Autocomplete description', class: 'col-sm-2 col-xs-12 control-label'
.col-sm-10.col-xs-12.input-group
= text_field_tag :autocomplete_description, run_actions_text, class: 'form-control input-sm', readonly: 'readonly'
.input-group-btn
= clipboard_button(clipboard_target: '#autocomplete_description')
%hr
- unless enabled
= render 'projects/services/mattermost_slash_commands/detailed_help', subject: @service
%ul.list-unstyled
%li
3. After adding the slash command, paste the
%strong token
into the field below
- if enabled
= render 'projects/services/mattermost_slash_commands/installation_info', subject: @service
.services-installation-info
- unless @service.activated?
.row
.col-sm-9.col-sm-offset-3
= link_to new_namespace_project_mattermost_path(@project.namespace, @project), class: 'btn btn-lg' do
= custom_icon('mattermost_logo', size: 15)
= 'Add to Mattermost'
......@@ -70,6 +70,10 @@
- if koding_enabled? && @repository.koding_yml.blank?
%li.missing
= link_to 'Set up Koding', add_koding_stack_path(@project)
- if @repository.gitlab_ci_yml.blank? && @project.deployment_service.present?
%li.missing
= link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml', commit_message: 'Set up autodeploy', target_branch: 'autodeploy', context: 'autodeploy') do
Set up autodeploy
- if @repository.commit
.project-last-commit{ class: container_class }
......
......@@ -10,10 +10,10 @@
.text-content
- if has_button && current_user
%h4
The Issue Tracker is a good place to add things that need to be improved or solved in a project!
The Issue Tracker is the place to add things that need to be improved or solved in a project
%p
An issue can be a bug, a todo or a feature request that needs to be discussed in a project.
Besides, issues are searchable and filterable.
Issues can be bugs, tasks or ideas to be discussed.
Also, issues are searchable and filterable.
- if project_select_button
= render 'shared/new_project_item_select', path: 'issues/new', label: 'New issue'
- else
......
<svg xmlns="http://www.w3.org/2000/svg" width="<%= size %>" height="<%= size %>" version="1" viewBox="0 0 501 501"><path d="M236 .7C137.7 7.5 54 68.2 18.2 158.5c-32 81-19.6 172.8 33 242.5 39.8 53 97.2 87 164.3 97 16.5 2.7 48 3.2 63.5 1.2 48.7-6.3 92.2-24.6 129-54.2 13-10.5 33-31.2 42.2-43.7 26.4-35.5 42.8-75.8 49-120.3 1.6-12.3 1.6-48.7 0-61-4-28.3-12-54.8-24.2-79.5-12.8-26-26.5-45.3-46.8-65.8C417.8 64 400.2 49 398.4 49c-.6 0-.4 10.5.3 26l1.3 26 7 8.7c19 23.7 32.8 53.5 38.2 83 2.5 14 3 43 1 55.8-4.5 27.8-15.2 54-31 76.5-8.6 12.2-28 31.6-40.2 40.2-24 17-50 27.6-80 33-10 1.8-49 1.8-59 0-43-7.7-78.8-26-107.2-54.8-29.3-29.7-46.5-64-52.4-104.4-2-14-1.5-42 1-55C90 121.4 132 72 192 49.7c8-3 18.4-5.8 29.5-8.2 1.7-.4 34.4-38 35.3-40.6.3-1-10.2-1-20.8-.4z"/><path d="M322.2 24.6c-1.3.8-8.4 9.3-16 18.7-7.4 9.5-22.4 28-33.2 41.2-51 62.2-66 81.6-70.6 91-6 12-8.4 21-9 33-1.2 19.8 5 36 19 50C222 268 230 273 243 277.2c9 3 10.4 3.2 24 3.2 13.8 0 15 0 22.6-3 23.2-9 39-28.4 45-55.7 2-8.2 2-28.7.4-79.7l-2-72c-1-36.8-1.4-41.8-3-44-2-3-4.8-3.6-7.8-1.4z"/></svg>
<svg width="16" height="33" class="gitlab-icon-scroll-down" viewBox="0 0 16 33" xmlns="http://www.w3.org/2000/svg">
<path fill="#ffffff" d="M1.385 5.534v12.47a4.145 4.145 0 0 0 4.144 4.15h4.942a4.151 4.151 0 0 0 4.144-4.15V5.535a4.145 4.145 0 0 0-4.144-4.15H5.53a4.151 4.151 0 0 0-4.144 4.15zM8.88 30.27v-4.351a.688.688 0 0 0-.69-.688.687.687 0 0 0-.69.688v4.334l-1.345-1.346a.69.69 0 0 0-.976.976l2.526 2.526a.685.685 0 0 0 .494.2.685.685 0 0 0 .493-.2l2.526-2.526a.69.69 0 1 0-.976-.976L8.88 30.27zM0 5.534A5.536 5.536 0 0 1 5.529 0h4.942A5.53 5.53 0 0 1 16 5.534v12.47a5.536 5.536 0 0 1-5.529 5.534H5.53A5.53 5.53 0 0 1 0 18.005V5.534zm7 1.01a1 1 0 1 1 2 0v2.143a1 1 0 1 1-2 0V6.544z" fill-rule="evenodd"/>
</svg>
<svg width="16" height="33" class="gitlab-icon-scroll-down-hover" viewBox="0 0 16 33" xmlns="http://www.w3.org/2000/svg">
<path fill="#ffffff" d="M8.88 30.27v-4.351a.688.688 0 0 0-.69-.688.687.687 0 0 0-.69.688v4.334l-1.345-1.346a.69.69 0 0 0-.976.976l2.526 2.526a.685.685 0 0 0 .494.2.685.685 0 0 0 .493-.2l2.526-2.526a.69.69 0 1 0-.976-.976L8.88 30.27zM0 5.534A5.536 5.536 0 0 1 5.529 0h4.942A5.53 5.53 0 0 1 16 5.534v12.47a5.536 5.536 0 0 1-5.529 5.534H5.53A5.53 5.53 0 0 1 0 18.005V5.534zm7 1.01a1 1 0 1 1 2 0v2.143a1 1 0 1 1-2 0V6.544z" fill-rule="evenodd"/>
</svg>
<svg width="16" height="33" class="gitlab-icon-scroll-up" viewBox="0 0 16 33" xmlns="http://www.w3.org/2000/svg">
<path fill="#ffffff" d="M1.385 14.534v12.47a4.145 4.145 0 0 0 4.144 4.15h4.942a4.151 4.151 0 0 0 4.144-4.15v-12.47a4.145 4.145 0 0 0-4.144-4.15H5.53a4.151 4.151 0 0 0-4.144 4.15zM8.88 2.609V6.96a.688.688 0 0 1-.69.688.687.687 0 0 1-.69-.688V2.627L6.155 3.972a.69.69 0 0 1-.976-.976L7.705.47a.685.685 0 0 1 .494-.2.685.685 0 0 1 .493.2l2.526 2.526a.69.69 0 1 1-.976.976L8.88 2.609zM0 14.534A5.536 5.536 0 0 1 5.529 9h4.942A5.53 5.53 0 0 1 16 14.534v12.47a5.536 5.536 0 0 1-5.529 5.534H5.53A5.53 5.53 0 0 1 0 27.005V14.534zm7 1.01a1 1 0 1 1 2 0v2.143a1 1 0 1 1-2 0v-2.143z" fill-rule="evenodd"/>
</svg>
<svg width="16" height="33" class="gitlab-icon-scroll-up-hover" viewBox="0 0 16 33" xmlns="http://www.w3.org/2000/svg">
<path fill="#ffffff" d="M8.88 2.646l1.362 1.362a.69.69 0 0 0 .976-.976L8.692.507A.685.685 0 0 0 8.2.306a.685.685 0 0 0-.494.2L5.179 3.033a.69.69 0 1 0 .976.976L7.5 2.663v4.179c0 .38.306.688.69.688.381 0 .69-.306.69-.688V2.646zM0 14.534A5.536 5.536 0 0 1 5.529 9h4.942A5.53 5.53 0 0 1 16 14.534v12.47a5.536 5.536 0 0 1-5.529 5.534H5.53A5.53 5.53 0 0 1 0 27.005V14.534zm7 1.01a1 1 0 1 1 2 0v2.143a1 1 0 1 1-2 0v-2.143z" fill-rule="evenodd"/>
</svg>
......@@ -3,7 +3,7 @@
- show_menu_above = show_menu_above || false
- selected_text = selected.try(:title) || params[:milestone_title]
- dropdown_title = local_assigns.fetch(:dropdown_title, "Filter by milestone")
- if selected.present?
- if selected.present? || params[:milestone_title].present?
= hidden_field_tag(name, name == :milestone_title ? selected_text : selected.id)
= dropdown_tag(milestone_dropdown_label(selected_text), options: { title: dropdown_title, toggle_class: "js-milestone-select js-filter-submit #{extra_class}", filter: true, dropdown_class: "dropdown-menu-selectable dropdown-menu-milestone",
placeholder: "Search milestones", footer_content: project.present?, data: { show_no: true, show_menu_above: show_menu_above, show_any: show_any, show_upcoming: show_upcoming, field_name: name, selected: selected.try(:title), project_id: project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do
......
---
title: Improve Build Log scrolling experience
merge_request: 7895
author:
---
title: 'Filter protocol-relative URLs in ExternalLinkFilter. Fixes issue #22742'
merge_request: 6635
author: Makoto Scott-Hinkle
---
title: Resolve "Remove Builds tab from Merge Requests and Commits"
merge_request: 7763
author:
---
title: Fix lookup of project by unknown ref when caching is enabled
merge_request: 7988
author:
---
title: Fixes left align issue for long system notes
merge_request: 7982
author:
---
title: Ensure nil User-Agent doesn't break the CI API
merge_request:
author:
---
title: Adds CSS class to status icon on MR widget to prevent non-colored icon
merge_request: 8219
author:
---
title: Adds background color for disabled state to merge when succeeds dropdown
merge_request: 8222
author:
---
title: Standardises font-size for titles in Issues, Merge Requests and Merge Request widget
merge_request: 8235
author:
---
title: Use Grape's new Route methods
merge_request:
author:
---
title: Adds back CSS for progress-bars
merge_request: 8237
author:
---
title: Add Authentiq as Oauth provider
merge_request: 8038
author: Alexandros Keramidas
---
title: Introduce "Set up autodeploy" button to help configure GitLab CI for deployment
merge_request: 8135
author:
---
title: Added lighter count badge background-color for on white backgrounds
merge_request: 7873
author:
---
title: Rename groups with .git in the end of the path
merge_request: 8199
author:
---
title: Allow projects with 'dashboard' as path
merge_request:
author:
---
title: 'Whitelist next project names: notes, services'
merge_request:
author:
---
title: 'Whitelist next project names: help, ci, admin, search'
merge_request: 8227
author:
---
title: Improve copy in Issue Tracker empty state
merge_request: 8202
author:
---
title: Fix 500 error renaming group
merge_request:
author:
---
title: Allow to auto-configure Mattermost
merge_request: 8070
author:
---
title: Remove unused and void services from the database
merge_request:
author:
......@@ -86,6 +86,7 @@ module Gitlab
# Enable the asset pipeline
config.assets.enabled = true
config.assets.paths << Gemojione.images_path
config.assets.paths << "vendor/assets/fonts"
config.assets.precompile << "*.png"
config.assets.precompile << "print.css"
config.assets.precompile << "notify.css"
......@@ -101,6 +102,7 @@ module Gitlab
config.assets.precompile << "protected_branches/protected_branches_bundle.js"
config.assets.precompile << "diff_notes/diff_notes_bundle.js"
config.assets.precompile << "issuable/issuable_bundle.js"
config.assets.precompile << "merge_request_widget/ci_bundle.js"
config.assets.precompile << "boards/boards_bundle.js"
config.assets.precompile << "cycle_analytics/cycle_analytics_bundle.js"
config.assets.precompile << "merge_conflicts/merge_conflicts_bundle.js"
......@@ -112,6 +114,7 @@ module Gitlab
config.assets.precompile << "lib/utils/*.js"
config.assets.precompile << "lib/*.js"
config.assets.precompile << "u2f.js"
config.assets.precompile << "vendor/assets/fonts/*"
# Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0'
......
......@@ -468,6 +468,16 @@ production: &base
# login_url: '/cas/login',
# service_validate_url: '/cas/p3/serviceValidate',
# logout_url: '/cas/logout'} }
# - { name: 'authentiq',
# # for client credentials (client ID and secret), go to https://www.authentiq.com/
# app_id: 'YOUR_CLIENT_ID',
# app_secret: 'YOUR_CLIENT_SECRET',
# args: {
# scope: 'aq:name email~rs address aq:push'
# # redirect_uri parameter is optional except when 'gitlab.host' in this file is set to 'localhost'
# # redirect_uri: 'YOUR_REDIRECT_URI'
# }
# }
# - { name: 'github',
# app_id: 'YOUR_APP_ID',
# app_secret: 'YOUR_APP_SECRET',
......@@ -681,4 +691,4 @@ test:
admin_group: ''
staging:
<<: *base
<<: *base
\ No newline at end of file
......@@ -32,10 +32,7 @@ constraints(ProjectUrlConstrainer.new) do
resources :commit, only: [:show], constraints: { id: /\h{7,40}/ } do
member do
get :branches
get :builds
get :pipelines
post :cancel_builds
post :retry_builds
post :revert
post :cherry_pick
get :diff_for_path
......@@ -82,6 +79,8 @@ constraints(ProjectUrlConstrainer.new) do
end
end
resource :mattermost, only: [:new, :create]
resources :deploy_keys, constraints: { id: /\d+/ }, only: [:index, :new, :create] do
member do
put :enable
......@@ -98,7 +97,6 @@ constraints(ProjectUrlConstrainer.new) do
get :diffs
get :conflicts
get :conflict_for_path
get :builds
get :pipelines
get :merge_check
post :merge
......
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class RemoveDotGitFromGroupNames < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
include Gitlab::ShellAdapter
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
def up
invalid_groups.each do |group|
path_was = group['path']
path_was_wildcard = quote_string("#{path_was}/%")
path = quote_string(rename_path(path_was))
move_namespace(group['id'], path_was, path)
execute "UPDATE routes SET path = '#{path}' WHERE source_type = 'Namespace' AND source_id = #{group['id']}"
execute "UPDATE namespaces SET path = '#{path}' WHERE id = #{group['id']}"
select_all("SELECT id, path FROM routes WHERE path LIKE '#{path_was_wildcard}'").each do |route|
new_path = "#{path}/#{route['path'].split('/').last}"
execute "UPDATE routes SET path = '#{new_path}' WHERE id = #{route['id']}"
end
end
end
def down
# nothing to do here
end
private
def invalid_groups
select_all("SELECT id, path FROM namespaces WHERE type = 'Group' AND path LIKE '%.git'")
end
def route_exists?(path)
select_all("SELECT id, path FROM routes WHERE path = '#{quote_string(path)}'").present?
end
# Accepts invalid path like test.git and returns test_git or
# test_git1 if test_git already taken
def rename_path(path)
# To stay closer with original name and reduce risk of duplicates
# we rename suffix instead of removing it
path = path.sub(/\.git\z/, '_git')
counter = 0
base = path
while route_exists?(path)
counter += 1
path = "#{base}#{counter}"
end
path
end
def move_namespace(group_id, path_was, path)
repository_storage_paths = select_all("SELECT distinct(repository_storage) FROM projects WHERE namespace_id = #{group_id}").map do |row|
Gitlab.config.repositories.storages[row['repository_storage']]
end.compact
# Move the namespace directory in all storages paths used by member projects
repository_storage_paths.each do |repository_storage_path|
# Ensure old directory exists before moving it
gitlab_shell.add_namespace(repository_storage_path, path_was)
unless gitlab_shell.mv_namespace(repository_storage_path, path_was, path)
Rails.logger.error "Exception moving path #{repository_storage_path} from #{path_was} to #{path}"
# if we cannot move namespace directory we should rollback
# db changes in order to prevent out of sync between db and fs
raise Exception.new('namespace directory cannot be moved')
end
end
Gitlab::UploadsTransfer.new.rename_namespace(path_was, path)
end
end
class RemoveUnneededServices < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
execute("DELETE FROM services WHERE active = false AND properties = '{}';")
end
def down
# noop
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20161213172958) do
ActiveRecord::Schema.define(version: 20161221140236) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -981,7 +981,7 @@ ActiveRecord::Schema.define(version: 20161213172958) do
t.datetime "expires_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "scopes", default: "--- []\n", null: false
t.string "scopes", default: "--- []\n", null: false
end
add_index "personal_access_tokens", ["token"], name: "index_personal_access_tokens_on_token", unique: true, using: :btree
......
......@@ -6,7 +6,7 @@ providers.
- [LDAP](ldap.md) Includes Active Directory, Apple Open Directory, Open LDAP,
and 389 Server
- [OmniAuth](../../integration/omniauth.md) Sign in via Twitter, GitHub, GitLab.com, Google,
Bitbucket, Facebook, Shibboleth, Crowd and Azure
Bitbucket, Facebook, Shibboleth, Crowd, Azure and Authentiq ID
- [CAS](../../integration/cas.md) Configure GitLab to sign in using CAS
- [SAML](../../integration/saml.md) Configure GitLab as a SAML 2.0 Service Provider
- [Okta](okta.md) Configure GitLab to sign in using Okta
# Authentiq OmniAuth Provider
To enable the Authentiq OmniAuth provider for passwordless authentication you must register an application with Authentiq.
Authentiq will generate a Client ID and the accompanying Client Secret for you to use.
1. Get your Client credentials (Client ID and Client Secret) at [Authentiq](https://www.authentiq.com/register).
2. On your GitLab server, open the configuration file:
For omnibus installation
```sh
sudo editor /etc/gitlab/gitlab.rb
```
For installations from source:
```sh
sudo -u git -H editor /home/git/gitlab/config/gitlab.yml
```
3. See [Initial OmniAuth Configuration](../../integration/omniauth.md#initial-omniauth-configuration) for initial settings to enable single sign-on and add Authentiq as an OAuth provider.
4. Add the provider configuration for Authentiq:
For Omnibus packages:
```ruby
gitlab_rails['omniauth_providers'] = [
{
"name" => "authentiq",
"app_id" => "YOUR_CLIENT_ID",
"app_secret" => "YOUR_CLIENT_SECRET",
"args" => {
scope: 'aq:name email~rs aq:push'
}
}
]
```
For installations from source:
```yaml
- { name: 'authentiq',
app_id: 'YOUR_CLIENT_ID',
app_secret: 'YOUR_CLIENT_SECRET',
args: {
scope: 'aq:name email~rs aq:push'
}
}
```
5. The `scope` is set to request the user's name, email (required and signed), and permission to send push notifications to sign in on subsequent visits.
See [OmniAuth Authentiq strategy](https://github.com/AuthentiqID/omniauth-authentiq#scopes-and-redirect-uri-configuration) for more information on scopes and modifiers.
6. Change 'YOUR_CLIENT_ID' and 'YOUR_CLIENT_SECRET' to the Client credentials you received in step 1.
7. Save the configuration file.
8. [Reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart GitLab](../restart_gitlab.md#installations-from-source)
for the changes to take effect if you installed GitLab via Omnibus or from source respectively.
On the sign in page there should now be an Authentiq icon below the regular sign in form. Click the icon to begin the authentication process.
- If the user has the Authentiq ID app installed in their iOS or Android device, they can scan the QR code, decide what personal details to share and sign in to your GitLab installation.
- If not they will be prompted to download the app and then follow the procedure above.
If everything goes right, the user will be returned to GitLab and will be signed in.
\ No newline at end of file
......@@ -23,6 +23,7 @@
- [CI/CD pipelines settings](../user/project/pipelines/settings.md)
- [Review Apps](review_apps/index.md)
- [Git submodules](git_submodules.md) Using Git submodules in your CI jobs
- [Autodeploy](autodeploy/index.md)
## Breaking changes
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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