Commit ba0b7c55 authored by Valery Sizov's avatar Valery Sizov

Merge branch 'ce_upstream' into 'master'

CE upstream

Closes #1376 and gitlab-ce#26114

See merge request !1014
parents 017288f4 38199935
...@@ -24,7 +24,7 @@ entry. ...@@ -24,7 +24,7 @@ entry.
## 8.15.2 (2016-12-27) ## 8.15.2 (2016-12-27)
- No changes. - Fix finding the latest pipeline. !8301
- Fix mr list timestamp alignment. !8271 - Fix mr list timestamp alignment. !8271
- Fix discussion overlap text in regular screens. !8273 - Fix discussion overlap text in regular screens. !8273
- Fixes mini-pipeline-graph dropdown animation and stage position in chrome, firefox and safari. !8282 - Fixes mini-pipeline-graph dropdown animation and stage position in chrome, firefox and safari. !8282
......
...@@ -343,7 +343,7 @@ gem 'octokit', '~> 4.3.0' ...@@ -343,7 +343,7 @@ gem 'octokit', '~> 4.3.0'
gem 'mail_room', '~> 0.9.0' gem 'mail_room', '~> 0.9.0'
gem 'email_reply_parser', '~> 0.5.8' gem 'email_reply_trimmer', '~> 0.1'
gem 'html2text' gem 'html2text'
gem 'ruby-prof', '~> 0.16.2' gem 'ruby-prof', '~> 0.16.2'
...@@ -358,5 +358,5 @@ gem 'paranoia', '~> 2.2' ...@@ -358,5 +358,5 @@ gem 'paranoia', '~> 2.2'
gem 'health_check', '~> 2.2.0' gem 'health_check', '~> 2.2.0'
# System information # System information
gem 'vmstat', '~> 2.2' gem 'vmstat', '~> 2.3.0'
gem 'sys-filesystem', '~> 1.1.6' gem 'sys-filesystem', '~> 1.1.6'
...@@ -180,7 +180,7 @@ GEM ...@@ -180,7 +180,7 @@ GEM
elasticsearch-transport (1.0.15) elasticsearch-transport (1.0.15)
faraday faraday
multi_json multi_json
email_reply_parser (0.5.8) email_reply_trimmer (0.1.6)
email_spec (1.6.0) email_spec (1.6.0)
launchy (~> 2.1) launchy (~> 2.1)
mail (~> 2.2) mail (~> 2.2)
...@@ -800,7 +800,7 @@ GEM ...@@ -800,7 +800,7 @@ GEM
coercible (~> 1.0) coercible (~> 1.0)
descendants_tracker (~> 0.0, >= 0.0.3) descendants_tracker (~> 0.0, >= 0.0.3)
equalizer (~> 0.0, >= 0.0.9) equalizer (~> 0.0, >= 0.0.9)
vmstat (2.2.0) vmstat (2.3.0)
warden (1.2.6) warden (1.2.6)
rack (>= 1.0) rack (>= 1.0)
web-console (2.3.0) web-console (2.3.0)
...@@ -868,7 +868,7 @@ DEPENDENCIES ...@@ -868,7 +868,7 @@ DEPENDENCIES
dropzonejs-rails (~> 0.7.1) dropzonejs-rails (~> 0.7.1)
elasticsearch-model elasticsearch-model
elasticsearch-rails elasticsearch-rails
email_reply_parser (~> 0.5.8) email_reply_trimmer (~> 0.1)
email_spec (~> 1.6.0) email_spec (~> 1.6.0)
factory_girl_rails (~> 4.7.0) factory_girl_rails (~> 4.7.0)
ffaker (~> 2.0.0) ffaker (~> 2.0.0)
...@@ -1016,7 +1016,7 @@ DEPENDENCIES ...@@ -1016,7 +1016,7 @@ DEPENDENCIES
validates_hostname (~> 1.0.6) validates_hostname (~> 1.0.6)
version_sorter (~> 2.1.0) version_sorter (~> 2.1.0)
virtus (~> 1.0.1) virtus (~> 1.0.1)
vmstat (~> 2.2) vmstat (~> 2.3.0)
web-console (~> 2.0) web-console (~> 2.0)
webmock (~> 1.21.0) webmock (~> 1.21.0)
wikicloth (= 0.8.1) wikicloth (= 0.8.1)
......
...@@ -69,7 +69,8 @@ to add details to the issue. ...@@ -69,7 +69,8 @@ to add details to the issue.
- ~UX needs help from a UX designer - ~UX needs help from a UX designer
- ~Frontend needs help from a Front-end engineer. Please follow the - ~Frontend needs help from a Front-end engineer. Please follow the
["Implement design & UI elements" guidelines]. ["Implement design & UI elements" guidelines].
- ~up-for-grabs is an issue suitable for first-time contributors, of reasonable difficulty and size. Not exclusive with other labels. - ~"Accepting Merge Requests" is a low priority, well-defined issue that we
encourage people to contribute to. Not exclusive with other labels.
- ~"feature proposal" is a proposal for a new feature for GitLab. People are encouraged to vote - ~"feature proposal" is a proposal for a new feature for GitLab. People are encouraged to vote
in support or comment for further detail. Do not use `feature request`. in support or comment for further detail. Do not use `feature request`.
- ~bug is an issue reporting undesirable or incorrect behavior. - ~bug is an issue reporting undesirable or incorrect behavior.
......
...@@ -91,6 +91,14 @@ ...@@ -91,6 +91,14 @@
// Set the default path for all cookies to GitLab's root directory // Set the default path for all cookies to GitLab's root directory
Cookies.defaults.path = gon.relative_url_root || '/'; Cookies.defaults.path = gon.relative_url_root || '/';
// `hashchange` is not triggered when link target is already in window.location
$body.on('click', 'a[href^="#"]', function() {
var href = this.getAttribute('href');
if (href.substr(1) === gl.utils.getLocationHash()) {
setTimeout(gl.utils.handleLocationHash, 1);
}
});
// prevent default action for disabled buttons // prevent default action for disabled buttons
$('.btn').click(function(e) { $('.btn').click(function(e) {
if ($(this).hasClass('disabled')) { if ($(this).hasClass('disabled')) {
......
...@@ -71,3 +71,5 @@ class ListIssue { ...@@ -71,3 +71,5 @@ class ListIssue {
return Vue.http.patch(url, data); return Vue.http.patch(url, data);
} }
} }
window.ListIssue = ListIssue;
...@@ -10,3 +10,5 @@ class ListLabel { ...@@ -10,3 +10,5 @@ class ListLabel {
this.priority = (obj.priority !== null) ? obj.priority : Infinity; this.priority = (obj.priority !== null) ? obj.priority : Infinity;
} }
} }
window.ListLabel = ListLabel;
...@@ -148,3 +148,5 @@ class List { ...@@ -148,3 +148,5 @@ class List {
}); });
} }
} }
window.List = List;
...@@ -6,3 +6,5 @@ class ListMilestone { ...@@ -6,3 +6,5 @@ class ListMilestone {
this.title = obj.title; this.title = obj.title;
} }
} }
window.ListMilestone = ListMilestone;
...@@ -8,3 +8,5 @@ class ListUser { ...@@ -8,3 +8,5 @@ class ListUser {
this.avatar = user.avatar_url; this.avatar = user.avatar_url;
} }
} }
window.ListUser = ListUser;
...@@ -78,4 +78,6 @@ class BoardService { ...@@ -78,4 +78,6 @@ class BoardService {
issue issue
}); });
} }
}; }
window.BoardService = BoardService;
...@@ -59,9 +59,11 @@ ...@@ -59,9 +59,11 @@
}, },
methods: { methods: {
updateTooltip: function () { updateTooltip: function () {
$(this.$refs.button) this.$nextTick(() => {
.tooltip('hide') $(this.$refs.button)
.tooltip('fixTitle'); .tooltip('hide')
.tooltip('fixTitle');
});
}, },
resolve: function () { resolve: function () {
if (!this.canResolve) return; if (!this.canResolve) return;
...@@ -90,7 +92,7 @@ ...@@ -90,7 +92,7 @@
new Flash('An error occurred when trying to resolve a comment. Please try again.', 'alert'); new Flash('An error occurred when trying to resolve a comment. Please try again.', 'alert');
} }
this.$nextTick(this.updateTooltip); this.updateTooltip();
}); });
} }
}, },
......
...@@ -92,3 +92,5 @@ class DiscussionModel { ...@@ -92,3 +92,5 @@ class DiscussionModel {
return false; return false;
} }
} }
window.DiscussionModel = DiscussionModel;
...@@ -9,3 +9,5 @@ class NoteModel { ...@@ -9,3 +9,5 @@ class NoteModel {
this.resolved_by = resolved_by; this.resolved_by = resolved_by;
} }
} }
window.NoteModel = NoteModel;
...@@ -20,3 +20,5 @@ class EnvironmentsService { ...@@ -20,3 +20,5 @@ class EnvironmentsService {
return this.environments.get(); return this.environments.get();
} }
} }
window.EnvironmentsService = EnvironmentsService;
...@@ -77,7 +77,7 @@ ...@@ -77,7 +77,7 @@
var _a, _y, regexp, match, atSymbolsWithBar, atSymbolsWithoutBar; var _a, _y, regexp, match, atSymbolsWithBar, atSymbolsWithoutBar;
atSymbolsWithBar = Object.keys(this.app.controllers).join('|'); atSymbolsWithBar = Object.keys(this.app.controllers).join('|');
atSymbolsWithoutBar = Object.keys(this.app.controllers).join(''); atSymbolsWithoutBar = Object.keys(this.app.controllers).join('');
subtext = subtext.split(' ').pop(); subtext = subtext.split(/\s+/g).pop();
flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
_a = decodeURI("%C3%80"); _a = decodeURI("%C3%80");
......
/* eslint-disable func-names, space-before-function-paren, no-var, one-var, one-var-declaration-per-line, wrap-iife, no-else-return, consistent-return, object-shorthand, comma-dangle, no-param-reassign, padded-blocks, camelcase, prefer-arrow-callback, max-len */ /* eslint-disable func-names, no-var, object-shorthand, comma-dangle, prefer-arrow-callback */
// MarkdownPreview // MarkdownPreview
// //
// Handles toggling the "Write" and "Preview" tab clicks, rendering the preview, // Handles toggling the "Write" and "Preview" tab clicks, rendering the preview,
// and showing a warning when more than `x` users are referenced. // and showing a warning when more than `x` users are referenced.
// //
(function() { (function () {
var lastTextareaPreviewed, markdownPreview, previewButtonSelector, writeButtonSelector; var lastTextareaPreviewed;
var markdownPreview;
var previewButtonSelector;
var writeButtonSelector;
window.MarkdownPreview = (function() { window.MarkdownPreview = (function () {
function MarkdownPreview() {} function MarkdownPreview() {}
// Minimum number of users referenced before triggering a warning // Minimum number of users referenced before triggering a warning
...@@ -16,73 +19,71 @@ ...@@ -16,73 +19,71 @@
MarkdownPreview.prototype.ajaxCache = {}; MarkdownPreview.prototype.ajaxCache = {};
MarkdownPreview.prototype.showPreview = function(form) { MarkdownPreview.prototype.showPreview = function ($form) {
var mdText, preview; var mdText;
preview = form.find('.js-md-preview'); var preview = $form.find('.js-md-preview');
mdText = form.find('textarea.markdown-area').val(); if (preview.hasClass('md-preview-loading')) {
return;
}
mdText = $form.find('textarea.markdown-area').val();
if (mdText.trim().length === 0) { if (mdText.trim().length === 0) {
preview.text('Nothing to preview.'); preview.text('Nothing to preview.');
return this.hideReferencedUsers(form); this.hideReferencedUsers($form);
} else { } else {
preview.text('Loading...'); preview.addClass('md-preview-loading').text('Loading...');
return this.renderMarkdown(mdText, (function(_this) { this.fetchMarkdownPreview(mdText, (function (response) {
return function(response) { preview.removeClass('md-preview-loading').html(response.body);
preview.html(response.body); preview.renderGFM();
preview.renderGFM(); this.renderReferencedUsers(response.references.users, $form);
return _this.renderReferencedUsers(response.references.users, form); }).bind(this));
};
})(this));
} }
}; };
MarkdownPreview.prototype.renderMarkdown = function(text, success) { MarkdownPreview.prototype.fetchMarkdownPreview = function (text, success) {
if (!window.preview_markdown_path) { if (!window.preview_markdown_path) {
return; return;
} }
if (text === this.ajaxCache.text) { if (text === this.ajaxCache.text) {
return success(this.ajaxCache.response); success(this.ajaxCache.response);
return;
} }
return $.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: window.preview_markdown_path, url: window.preview_markdown_path,
data: { data: {
text: text text: text
}, },
dataType: 'json', dataType: 'json',
success: (function(_this) { success: (function (response) {
return function(response) { this.ajaxCache = {
_this.ajaxCache = { text: text,
text: text, response: response
response: response
};
return success(response);
}; };
})(this) success(response);
}).bind(this)
}); });
}; };
MarkdownPreview.prototype.hideReferencedUsers = function(form) { MarkdownPreview.prototype.hideReferencedUsers = function ($form) {
var referencedUsers; $form.find('.referenced-users').hide();
referencedUsers = form.find('.referenced-users');
return referencedUsers.hide();
}; };
MarkdownPreview.prototype.renderReferencedUsers = function(users, form) { MarkdownPreview.prototype.renderReferencedUsers = function (users, $form) {
var referencedUsers; var referencedUsers;
referencedUsers = form.find('.referenced-users'); referencedUsers = $form.find('.referenced-users');
if (referencedUsers.length) { if (referencedUsers.length) {
if (users.length >= this.referenceThreshold) { if (users.length >= this.referenceThreshold) {
referencedUsers.show(); referencedUsers.show();
return referencedUsers.find('.js-referenced-users-count').text(users.length); referencedUsers.find('.js-referenced-users-count').text(users.length);
} else { } else {
return referencedUsers.hide(); referencedUsers.hide();
} }
} }
}; };
return MarkdownPreview; return MarkdownPreview;
}());
})();
markdownPreview = new window.MarkdownPreview(); markdownPreview = new window.MarkdownPreview();
...@@ -92,19 +93,14 @@ ...@@ -92,19 +93,14 @@
lastTextareaPreviewed = null; lastTextareaPreviewed = null;
$.fn.setupMarkdownPreview = function() { $.fn.setupMarkdownPreview = function () {
var $form, form_textarea; var $form = $(this);
$form = $(this); $form.find('textarea.markdown-area').on('input', function () {
form_textarea = $form.find('textarea.markdown-area'); markdownPreview.hideReferencedUsers($form);
form_textarea.on('input', function() {
return markdownPreview.hideReferencedUsers($form);
});
return form_textarea.on('blur', function() {
return markdownPreview.showPreview($form);
}); });
}; };
$(document).on('markdown-preview:show', function(e, $form) { $(document).on('markdown-preview:show', function (e, $form) {
if (!$form) { if (!$form) {
return; return;
} }
...@@ -115,10 +111,10 @@ ...@@ -115,10 +111,10 @@
// toggle content // toggle content
$form.find('.md-write-holder').hide(); $form.find('.md-write-holder').hide();
$form.find('.md-preview-holder').show(); $form.find('.md-preview-holder').show();
return markdownPreview.showPreview($form); markdownPreview.showPreview($form);
}); });
$(document).on('markdown-preview:hide', function(e, $form) { $(document).on('markdown-preview:hide', function (e, $form) {
if (!$form) { if (!$form) {
return; return;
} }
...@@ -129,34 +125,33 @@ ...@@ -129,34 +125,33 @@
// toggle content // toggle content
$form.find('.md-write-holder').show(); $form.find('.md-write-holder').show();
$form.find('textarea.markdown-area').focus(); $form.find('textarea.markdown-area').focus();
return $form.find('.md-preview-holder').hide(); $form.find('.md-preview-holder').hide();
}); });
$(document).on('markdown-preview:toggle', function(e, keyboardEvent) { $(document).on('markdown-preview:toggle', function (e, keyboardEvent) {
var $target; var $target;
$target = $(keyboardEvent.target); $target = $(keyboardEvent.target);
if ($target.is('textarea.markdown-area')) { if ($target.is('textarea.markdown-area')) {
$(document).triggerHandler('markdown-preview:show', [$target.closest('form')]); $(document).triggerHandler('markdown-preview:show', [$target.closest('form')]);
return keyboardEvent.preventDefault(); keyboardEvent.preventDefault();
} else if (lastTextareaPreviewed) { } else if (lastTextareaPreviewed) {
$target = lastTextareaPreviewed; $target = lastTextareaPreviewed;
$(document).triggerHandler('markdown-preview:hide', [$target.closest('form')]); $(document).triggerHandler('markdown-preview:hide', [$target.closest('form')]);
return keyboardEvent.preventDefault(); keyboardEvent.preventDefault();
} }
}); });
$(document).on('click', previewButtonSelector, function(e) { $(document).on('click', previewButtonSelector, function (e) {
var $form; var $form;
e.preventDefault(); e.preventDefault();
$form = $(this).closest('form'); $form = $(this).closest('form');
return $(document).triggerHandler('markdown-preview:show', [$form]); $(document).triggerHandler('markdown-preview:show', [$form]);
}); });
$(document).on('click', writeButtonSelector, function(e) { $(document).on('click', writeButtonSelector, function (e) {
var $form; var $form;
e.preventDefault(); e.preventDefault();
$form = $(this).closest('form'); $form = $(this).closest('form');
return $(document).triggerHandler('markdown-preview:hide', [$form]); $(document).triggerHandler('markdown-preview:hide', [$form]);
}); });
}());
}).call(this);
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
}, },
data: function(term, callback) { data: function(term, callback) {
var finalCallback, projectsCallback; var finalCallback, projectsCallback;
var orderBy = $dropdown.data('order-by');
finalCallback = function(projects) { finalCallback = function(projects) {
return callback(projects); return callback(projects);
}; };
...@@ -34,7 +35,7 @@ ...@@ -34,7 +35,7 @@
if (this.groupId) { if (this.groupId) {
return Api.groupProjects(this.groupId, term, projectsCallback); return Api.groupProjects(this.groupId, term, projectsCallback);
} else { } else {
return Api.projects(term, this.orderBy, projectsCallback); return Api.projects(term, orderBy, projectsCallback);
} }
}, },
url: function(project) { url: function(project) {
......
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
}); });
// Protected branch dropdown // Protected branch dropdown
new gl.ProtectedBranchDropdown({ new window.ProtectedBranchDropdown({
$dropdown: this.$wrap.find('.js-protected-branch-select'), $dropdown: this.$wrap.find('.js-protected-branch-select'),
onSelect: this.onSelectCallback onSelect: this.onSelectCallback
}); });
......
/* eslint-disable */ /* eslint-disable comma-dangle, no-unused-vars */
(global => {
global.gl = global.gl || {};
class ProtectedBranchDropdown { class ProtectedBranchDropdown {
constructor(options) { constructor(options) {
this.onSelect = options.onSelect; this.onSelect = options.onSelect;
this.$dropdown = options.$dropdown; this.$dropdown = options.$dropdown;
this.$dropdownContainer = this.$dropdown.parent(); this.$dropdownContainer = this.$dropdown.parent();
this.$dropdownFooter = this.$dropdownContainer.find('.dropdown-footer'); this.$dropdownFooter = this.$dropdownContainer.find('.dropdown-footer');
this.$protectedBranch = this.$dropdownContainer.find('.create-new-protected-branch'); this.$protectedBranch = this.$dropdownContainer.find('.create-new-protected-branch');
this.buildDropdown(); this.buildDropdown();
this.bindEvents(); this.bindEvents();
// Hide footer // Hide footer
this.$dropdownFooter.addClass('hidden'); this.$dropdownFooter.addClass('hidden');
} }
buildDropdown() { buildDropdown() {
this.$dropdown.glDropdown({ this.$dropdown.glDropdown({
data: this.getProtectedBranches.bind(this), data: this.getProtectedBranches.bind(this),
filterable: true, filterable: true,
remote: false, remote: false,
search: { search: {
fields: ['title'] fields: ['title']
}, },
selectable: true, selectable: true,
toggleLabel(selected) { toggleLabel(selected) {
return (selected && 'id' in selected) ? selected.title : 'Protected Branch'; return (selected && 'id' in selected) ? selected.title : 'Protected Branch';
}, },
fieldName: 'protected_branch[name]', fieldName: 'protected_branch[name]',
text(protectedBranch) { text(protectedBranch) {
return _.escape(protectedBranch.title); return _.escape(protectedBranch.title);
}, },
id(protectedBranch) { id(protectedBranch) {
return _.escape(protectedBranch.id); return _.escape(protectedBranch.id);
}, },
onFilter: this.toggleCreateNewButton.bind(this), onFilter: this.toggleCreateNewButton.bind(this),
clicked: (item, $el, e) => { clicked: (item, $el, e) => {
e.preventDefault(); e.preventDefault();
this.onSelect(); this.onSelect();
} }
}); });
} }
onClickCreateWildcard() { onClickCreateWildcard() {
// Refresh the dropdown's data, which ends up calling `getProtectedBranches` // Refresh the dropdown's data, which ends up calling `getProtectedBranches`
this.$dropdown.data('glDropdown').remote.execute(); this.$dropdown.data('glDropdown').remote.execute();
this.$dropdown.data('glDropdown').selectRowAtIndex(0); this.$dropdown.data('glDropdown').selectRowAtIndex(0);
} }
bindEvents() { bindEvents() {
this.$protectedBranch.on('click', this.onClickCreateWildcard.bind(this)); this.$protectedBranch.on('click', this.onClickCreateWildcard.bind(this));
} }
getProtectedBranches(term, callback) { getProtectedBranches(term, callback) {
if (this.selectedBranch) { if (this.selectedBranch) {
callback(gon.open_branches.concat(this.selectedBranch)); callback(gon.open_branches.concat(this.selectedBranch));
} else { } else {
callback(gon.open_branches); callback(gon.open_branches);
}
} }
}
toggleCreateNewButton(branchName) { toggleCreateNewButton(branchName) {
this.selectedBranch = { this.selectedBranch = {
title: branchName, title: branchName,
id: branchName, id: branchName,
text: branchName text: branchName
}; };
if (branchName) {
this.$dropdownContainer
.find('.create-new-protected-branch code')
.text(branchName);
}
this.$dropdownFooter.toggleClass('hidden', !branchName); if (branchName) {
this.$dropdownContainer
.find('.create-new-protected-branch code')
.text(branchName);
} }
this.$dropdownFooter.toggleClass('hidden', !branchName);
} }
}
global.gl.ProtectedBranchDropdown = ProtectedBranchDropdown; window.ProtectedBranchDropdown = ProtectedBranchDropdown;
})(window);
...@@ -57,16 +57,33 @@ pre { ...@@ -57,16 +57,33 @@ pre {
border-radius: 0; border-radius: 0;
color: $well-pre-color; color: $well-pre-color;
} }
&.wrap {
word-break: break-word;
white-space: pre-wrap;
}
} }
hr { hr {
margin: $gl-padding 0; margin: $gl-padding 0;
border-top: 1px solid darken($gray-normal, 8%);
} }
.str-truncated { .str-truncated {
@include str-truncated; @include str-truncated;
} }
.block-truncated {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
> div,
.str-truncated {
display: inline;
}
}
.item-title { font-weight: 600; } .item-title { font-weight: 600; }
/** FLASH message **/ /** FLASH message **/
......
...@@ -87,25 +87,25 @@ ...@@ -87,25 +87,25 @@
} }
} }
$theme-charcoal: #3d454d; $theme-charcoal-light: #b9bbbe;
$theme-charcoal-light: #485157; $theme-charcoal: #485157;
$theme-charcoal-dark: #383f45; $theme-charcoal-dark: #3d454d;
$theme-charcoal-text: #b9bbbe; $theme-charcoal-darker: #383f45;
$theme-blue-light: #becde9; $theme-blue-light: #becde9;
$theme-blue: #2980b9; $theme-blue: #2980b9;
$theme-blue-dark: #1970a9; $theme-blue-dark: #1970a9;
$theme-blue-darker: #096099; $theme-blue-darker: #096099;
$theme-graphite-lighter: #ccc; $theme-graphite-light: #ccc;
$theme-graphite-light: #777; $theme-graphite: #777;
$theme-graphite: #666; $theme-graphite-dark: #666;
$theme-graphite-dark: #555; $theme-graphite-darker: #555;
$theme-gray-light: #979797; $theme-black-light: #979797;
$theme-gray: #373737; $theme-black: #373737;
$theme-gray-dark: #272727; $theme-black-dark: #272727;
$theme-gray-darker: #222; $theme-black-darker: #222;
$theme-green-light: #adc; $theme-green-light: #adc;
$theme-green: #019875; $theme-green: #019875;
...@@ -123,15 +123,15 @@ body { ...@@ -123,15 +123,15 @@ body {
} }
&.ui_charcoal { &.ui_charcoal {
@include gitlab-theme($theme-charcoal-text, $theme-charcoal-light, $theme-charcoal, $theme-charcoal-dark); @include gitlab-theme($theme-charcoal-light, $theme-charcoal, $theme-charcoal-dark, $theme-charcoal-darker);
} }
&.ui_graphite { &.ui_graphite {
@include gitlab-theme($theme-graphite-lighter, $theme-graphite-light, $theme-graphite, $theme-graphite-dark); @include gitlab-theme($theme-graphite-light, $theme-graphite, $theme-graphite-dark, $theme-graphite-darker);
} }
&.ui_gray { &.ui_black {
@include gitlab-theme($theme-gray-light, $theme-gray, $theme-gray-dark, $theme-gray-darker); @include gitlab-theme($theme-black-light, $theme-black, $theme-black-dark, $theme-black-darker);
} }
&.ui_green { &.ui_green {
......
...@@ -205,6 +205,7 @@ ul.content-list { ...@@ -205,6 +205,7 @@ ul.content-list {
display: -webkit-flex; display: -webkit-flex;
display: -ms-flexbox; display: -ms-flexbox;
display: flex; display: flex;
align-items: center;
white-space: nowrap; white-space: nowrap;
} }
...@@ -214,6 +215,11 @@ ul.content-list { ...@@ -214,6 +215,11 @@ ul.content-list {
padding-right: 8px; padding-right: 8px;
} }
.row-fixed-content {
flex: 0 0 auto;
margin-left: auto;
}
.row-title { .row-title {
font-weight: 600; font-weight: 600;
} }
......
...@@ -116,8 +116,8 @@ ...@@ -116,8 +116,8 @@
padding-top: 16px; padding-top: 16px;
padding-bottom: 11px; padding-bottom: 11px;
display: inline-block; display: inline-block;
width: 50%;
line-height: 28px; line-height: 28px;
white-space: normal;
/* Small devices (phones, tablets, 768px and lower) */ /* Small devices (phones, tablets, 768px and lower) */
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
...@@ -158,30 +158,24 @@ ...@@ -158,30 +158,24 @@
} }
.nav-controls { .nav-controls {
width: 50%;
display: inline-block; display: inline-block;
float: right; float: right;
text-align: right; text-align: right;
padding: 11px 0; padding: 11px 0;
margin-bottom: 0; margin-bottom: 0;
> .dropdown { > .btn,
margin-right: $gl-padding-top; > .btn-container,
display: inline-block; > .dropdown,
vertical-align: top; > input,
> form {
&:last-child {
margin-right: 0;
}
}
> .btn {
margin-right: $gl-padding-top; margin-right: $gl-padding-top;
display: inline-block; display: inline-block;
vertical-align: top; vertical-align: top;
&:last-child { &:last-child {
margin-right: 0; margin-right: 0;
float: right;
} }
} }
...@@ -189,19 +183,21 @@ ...@@ -189,19 +183,21 @@
float: none; float: none;
} }
> form {
display: inline-block;
}
.icon-label { .icon-label {
display: none; display: none;
} }
input { .btn,
.dropdown,
.dropdown-toggle,
input,
form {
height: 35px; height: 35px;
}
input {
display: inline-block; display: inline-block;
position: relative; position: relative;
margin-right: $gl-padding-top;
/* Medium devices (desktops, 992px and up) */ /* Medium devices (desktops, 992px and up) */
@media (min-width: $screen-md-min) { width: 200px; } @media (min-width: $screen-md-min) { width: 200px; }
...@@ -225,6 +221,7 @@ ...@@ -225,6 +221,7 @@
.btn, .btn,
form, form,
.dropdown, .dropdown,
.dropdown-toggle,
.dropdown-menu-toggle, .dropdown-menu-toggle,
.form-control { .form-control {
margin: 0 0 10px; margin: 0 0 10px;
...@@ -263,6 +260,10 @@ ...@@ -263,6 +260,10 @@
.nav-text, .nav-text,
.nav-controls { .nav-controls {
width: auto; width: auto;
@media (max-width: $screen-xs-max) {
width: 100%;
}
} }
} }
...@@ -275,6 +276,10 @@ ...@@ -275,6 +276,10 @@
padding: 17px 0; padding: 17px 0;
} }
} }
pre {
width: 100%;
}
} }
.layout-nav { .layout-nav {
...@@ -427,4 +432,41 @@ ...@@ -427,4 +432,41 @@
border-bottom: none; border-bottom: none;
} }
} }
} }
\ No newline at end of file
@media (max-width: $screen-xs-max) {
.top-area {
flex-flow: row wrap;
.nav-controls {
$controls-margin: $btn-xs-side-margin - 2px;
flex: 0 0 100%;
&.controls-flex {
display: flex;
flex-flow: row wrap;
align-items: center;
justify-content: center;
padding: 0 0 $gl-padding-top;
}
.controls-item,
.controls-item-full,
.controls-item:last-child {
flex: 1 1 35%;
display: block;
width: 100%;
margin: $controls-margin;
.btn,
.dropdown {
margin: 0;
}
}
.controls-item-full {
flex: 1 1 100%;
}
}
}
}
...@@ -91,7 +91,7 @@ ...@@ -91,7 +91,7 @@
// Labels // Labels
.label { .label {
padding: 4px 5px; padding: 4px 5px;
font-size: 13px; font-size: 12px;
font-style: normal; font-style: normal;
font-weight: normal; font-weight: normal;
display: inline-block; display: inline-block;
......
...@@ -496,9 +496,9 @@ $project-network-controls-color: #888; ...@@ -496,9 +496,9 @@ $project-network-controls-color: #888;
*/ */
$runner-state-shared-bg: #32b186; $runner-state-shared-bg: #32b186;
$runner-state-specific-bg: #3498db; $runner-state-specific-bg: #3498db;
$runner-status-online-color: green; $runner-status-online-color: $green-normal;
$runner-status-offline-color: gray; $runner-status-offline-color: $gray-darkest;
$runner-status-paused-color: red; $runner-status-paused-color: $red-normal;
/* /*
Stat Graph Stat Graph
......
...@@ -108,6 +108,12 @@ ...@@ -108,6 +108,12 @@
&.has-border { &.has-border {
border-top: 3px solid; border-top: 3px solid;
margin-top: -1px;
margin-right: -1px;
margin-left: -1px;
padding-top: 1px;
padding-right: 1px;
padding-left: 1px;
.board-title { .board-title {
padding-top: ($gl-padding - 3px); padding-top: ($gl-padding - 3px);
......
.deploy-keys-list {
width: 100%;
overflow: auto;
table {
border: 1px solid $table-border-color;
}
}
.deploy-keys-title {
padding-bottom: 2px;
line-height: 2;
}
// Limit MR description for side-by-side diff view
.limit-container-width {
.detail-page-header {
max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2));
margin-left: auto;
margin-right: auto;
}
.issuable-details {
.detail-page-description,
.mr-source-target,
.mr-state-widget,
.merge-manually {
max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2));
margin-left: auto;
margin-right: auto;
}
.merge-request-tabs-holder {
&.affix {
border-bottom: 1px solid $border-color;
.nav-links {
border: 0;
}
}
.container-fluid {
padding-left: 0;
padding-right: 0;
max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2));
margin-left: auto;
margin-right: auto;
}
}
}
.diffs {
.mr-version-controls,
.files-changed {
max-width: calc(#{$limited-layout-width} - (#{$gl-padding} * 2));
margin-left: auto;
margin-right: auto;
}
}
}
.issuable-details { .issuable-details {
section { section {
.issuable-discussion { .issuable-discussion {
......
...@@ -98,7 +98,7 @@ ...@@ -98,7 +98,7 @@
} }
.label { .label {
padding: 8px 9px 9px $gl-padding; padding: 8px 9px 9px;
font-size: 14px; font-size: 14px;
} }
} }
......
...@@ -105,19 +105,19 @@ ...@@ -105,19 +105,19 @@
li { li {
flex: 1; flex: 1;
text-align: center; text-align: center;
border-left: 1px solid $border-color;
&:first-of-type { &:first-of-type {
border-left: none;
border-top-left-radius: $border-radius-default; border-top-left-radius: $border-radius-default;
} }
&:last-of-type { &:last-of-type {
border-left: 1px solid $border-color;
border-top-right-radius: $border-radius-default; border-top-right-radius: $border-radius-default;
} }
&:not(.active) { &:not(.active) {
background-color: $gray-light; background-color: $gray-light;
border-left: 1px solid $border-color;
} }
a { a {
......
...@@ -424,6 +424,10 @@ ...@@ -424,6 +424,10 @@
.merge-request-tabs-holder { .merge-request-tabs-holder {
background-color: $white-light; background-color: $white-light;
.container-limited {
max-width: $limited-layout-width;
}
&.affix { &.affix {
top: 100px; top: 100px;
left: 0; left: 0;
......
...@@ -557,18 +557,14 @@ ul.notes { ...@@ -557,18 +557,14 @@ ul.notes {
&.is-active { &.is-active {
color: $gl-text-green; color: $gl-text-green;
svg path { svg {
fill: $gl-text-green; fill: $gl-text-green;
} }
} }
svg { svg {
position: relative; position: relative;
color: $gray-darkest; fill: $gray-darkest;
path {
fill: $gray-darkest;
}
} }
} }
......
...@@ -22,8 +22,8 @@ ...@@ -22,8 +22,8 @@
background: $theme-graphite; background: $theme-graphite;
} }
&.ui_gray { &.ui_black {
background: $theme-gray; background: $theme-black;
} }
&.ui_green { &.ui_green {
......
...@@ -10,7 +10,7 @@ class Admin::DeployKeysController < Admin::ApplicationController ...@@ -10,7 +10,7 @@ class Admin::DeployKeysController < Admin::ApplicationController
end end
def create def create
@deploy_key = deploy_keys.new(deploy_key_params) @deploy_key = deploy_keys.new(deploy_key_params.merge(user: current_user))
if @deploy_key.save if @deploy_key.save
redirect_to admin_deploy_keys_path redirect_to admin_deploy_keys_path
...@@ -39,6 +39,6 @@ class Admin::DeployKeysController < Admin::ApplicationController ...@@ -39,6 +39,6 @@ class Admin::DeployKeysController < Admin::ApplicationController
end end
def deploy_key_params def deploy_key_params
params.require(:deploy_key).permit(:key, :title) params.require(:deploy_key).permit(:key, :title, :can_push)
end end
end end
...@@ -9,7 +9,7 @@ class Admin::GroupsController < Admin::ApplicationController ...@@ -9,7 +9,7 @@ class Admin::GroupsController < Admin::ApplicationController
end end
def show def show
@group = Group.with_statistics.find_by_full_path(params[:id]) @group = Group.with_statistics.joins(:route).group('routes.path').find_by_full_path(params[:id])
@members = @group.members.order("access_level DESC").page(params[:members_page]) @members = @group.members.order("access_level DESC").page(params[:members_page])
@requesters = AccessRequestsFinder.new(@group).execute(current_user) @requesters = AccessRequestsFinder.new(@group).execute(current_user)
@projects = @group.projects.with_statistics.page(params[:projects_page]) @projects = @group.projects.with_statistics.page(params[:projects_page])
......
...@@ -4,6 +4,9 @@ class Dashboard::TodosController < Dashboard::ApplicationController ...@@ -4,6 +4,9 @@ class Dashboard::TodosController < Dashboard::ApplicationController
def index def index
@sort = params[:sort] @sort = params[:sort]
@todos = @todos.page(params[:page]) @todos = @todos.page(params[:page])
if @todos.out_of_range? && @todos.total_pages != 0
redirect_to url_for(params.merge(page: @todos.total_pages))
end
end end
def destroy def destroy
......
...@@ -16,7 +16,7 @@ class Projects::DeployKeysController < Projects::ApplicationController ...@@ -16,7 +16,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
end end
def create def create
@key = DeployKey.new(deploy_key_params) @key = DeployKey.new(deploy_key_params.merge(user: current_user))
set_index_vars set_index_vars
if @key.valid? && @project.deploy_keys << @key if @key.valid? && @project.deploy_keys << @key
...@@ -59,7 +59,7 @@ class Projects::DeployKeysController < Projects::ApplicationController ...@@ -59,7 +59,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
end end
def deploy_key_params def deploy_key_params
params.require(:deploy_key).permit(:key, :title) params.require(:deploy_key).permit(:key, :title, :can_push)
end end
def log_audit_event(key_title, options = {}) def log_audit_event(key_title, options = {})
......
...@@ -25,6 +25,9 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -25,6 +25,9 @@ class Projects::IssuesController < Projects::ApplicationController
def index def index
@issues = issues_collection @issues = issues_collection
@issues = @issues.page(params[:page]) @issues = @issues.page(params[:page])
if @issues.out_of_range? && @issues.total_pages != 0
return redirect_to url_for(params.merge(page: @issues.total_pages))
end
if params[:label_name].present? if params[:label_name].present?
@labels = LabelsFinder.new(current_user, project_id: @project.id, title: params[:label_name]).execute @labels = LabelsFinder.new(current_user, project_id: @project.id, title: params[:label_name]).execute
......
...@@ -40,6 +40,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -40,6 +40,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def index def index
@merge_requests = merge_requests_collection @merge_requests = merge_requests_collection
@merge_requests = @merge_requests.page(params[:page]) @merge_requests = @merge_requests.page(params[:page])
if @merge_requests.out_of_range? && @merge_requests.total_pages != 0
return redirect_to url_for(params.merge(page: @merge_requests.total_pages))
end
if params[:label_name].present? if params[:label_name].present?
labels_params = { project_id: @project.id, title: params[:label_name] } labels_params = { project_id: @project.id, title: params[:label_name] }
......
...@@ -26,6 +26,9 @@ class Projects::SnippetsController < Projects::ApplicationController ...@@ -26,6 +26,9 @@ class Projects::SnippetsController < Projects::ApplicationController
scope: params[:scope] scope: params[:scope]
) )
@snippets = @snippets.page(params[:page]) @snippets = @snippets.page(params[:page])
if @snippets.out_of_range? && @snippets.total_pages != 0
redirect_to namespace_project_snippets_path(page: @snippets.total_pages)
end
end end
def new def new
......
...@@ -8,7 +8,7 @@ class Projects::TagsController < Projects::ApplicationController ...@@ -8,7 +8,7 @@ class Projects::TagsController < Projects::ApplicationController
before_action :authorize_admin_project!, only: [:destroy] before_action :authorize_admin_project!, only: [:destroy]
def index def index
params[:sort] = params[:sort].presence || 'name' params[:sort] = params[:sort].presence || sort_value_recently_updated
@sort = params[:sort] @sort = params[:sort]
@tags = TagsFinder.new(@repository, params).execute @tags = TagsFinder.new(@repository, params).execute
......
...@@ -61,7 +61,7 @@ module ProjectsHelper ...@@ -61,7 +61,7 @@ module ProjectsHelper
project_link = link_to simple_sanitize(project.name), project_path(project), { class: "project-item-select-holder" } project_link = link_to simple_sanitize(project.name), project_path(project), { class: "project-item-select-holder" }
if current_user if current_user
project_link << button_tag(type: 'button', class: "dropdown-toggle-caret js-projects-dropdown-toggle", aria: { label: "Toggle switch project dropdown" }, data: { target: ".js-dropdown-menu-projects", toggle: "dropdown" }) do project_link << button_tag(type: 'button', class: 'dropdown-toggle-caret js-projects-dropdown-toggle', aria: { label: 'Toggle switch project dropdown' }, data: { target: '.js-dropdown-menu-projects', toggle: 'dropdown', order_by: 'last_activity_at' }) do
icon("chevron-down") icon("chevron-down")
end end
end end
...@@ -90,10 +90,12 @@ module ProjectsHelper ...@@ -90,10 +90,12 @@ module ProjectsHelper
end end
def project_for_deploy_key(deploy_key) def project_for_deploy_key(deploy_key)
if deploy_key.projects.include?(@project) if deploy_key.has_access_to?(@project)
@project @project
else else
deploy_key.projects.find { |project| can?(current_user, :read_project, project) } deploy_key.projects.find do |project|
can?(current_user, :read_project, project)
end
end end
end end
......
...@@ -20,4 +20,18 @@ class DeployKey < Key ...@@ -20,4 +20,18 @@ class DeployKey < Key
def destroyed_when_orphaned? def destroyed_when_orphaned?
self.private? self.private?
end end
def has_access_to?(project)
projects.include?(project)
end
def can_push_to?(project)
can_push? && has_access_to?(project)
end
private
# we don't want to notify the user for deploy keys
def notify_user
end
end end
...@@ -178,15 +178,17 @@ class Group < Namespace ...@@ -178,15 +178,17 @@ class Group < Namespace
end end
def has_owner?(user) def has_owner?(user)
owners.include?(user) members_with_parents.owners.where(user_id: user).any?
end end
def has_master?(user) def has_master?(user)
members.masters.where(user_id: user).any? members_with_parents.masters.where(user_id: user).any?
end end
# Check if user is a last owner of the group.
# Parent owners are ignored for nested groups.
def last_owner?(user) def last_owner?(user)
has_owner?(user) && owners.size == 1 owners.include?(user) && owners.size == 1
end end
def avatar_type def avatar_type
...@@ -239,6 +241,14 @@ class Group < Namespace ...@@ -239,6 +241,14 @@ class Group < Namespace
end end
def refresh_members_authorized_projects def refresh_members_authorized_projects
UserProjectAccessChangedService.new(users.pluck(:id)).execute UserProjectAccessChangedService.new(users_with_parents.pluck(:id)).execute
end
def members_with_parents
GroupMember.where(requested_at: nil, source_id: parents.map(&:id).push(id))
end
def users_with_parents
User.where(id: members_with_parents.select(:user_id))
end end
end end
...@@ -59,10 +59,6 @@ class Key < ActiveRecord::Base ...@@ -59,10 +59,6 @@ class Key < ActiveRecord::Base
) )
end end
def notify_user
run_after_commit { NotificationService.new.new_key(self) }
end
def post_create_hook def post_create_hook
SystemHooksService.new.execute_hooks_for(self, :create) SystemHooksService.new.execute_hooks_for(self, :create)
end end
...@@ -88,4 +84,8 @@ class Key < ActiveRecord::Base ...@@ -88,4 +84,8 @@ class Key < ActiveRecord::Base
self.fingerprint = Gitlab::KeyFingerprint.new(self.key).fingerprint self.fingerprint = Gitlab::KeyFingerprint.new(self.key).fingerprint
end end
def notify_user
run_after_commit { NotificationService.new.new_key(self) }
end
end end
...@@ -599,11 +599,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -599,11 +599,7 @@ class MergeRequest < ActiveRecord::Base
ext = Gitlab::ReferenceExtractor.new(project, current_user) ext = Gitlab::ReferenceExtractor.new(project, current_user)
ext.analyze(description) ext.analyze(description)
issues = ext.issues ext.issues - closes_issues
closing_issues = Gitlab::ClosingIssueExtractor.new(project, current_user).
closed_by_message(description)
issues - closing_issues
end end
def target_project_path def target_project_path
......
...@@ -1036,7 +1036,7 @@ class Project < ActiveRecord::Base ...@@ -1036,7 +1036,7 @@ class Project < ActiveRecord::Base
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"
# we currently doesn't support renaming repository if it contains tags in container registry # we currently doesn't support renaming repository if it contains tags in container registry
raise Exception.new('Project cannot be renamed, because tags are present in its container registry') raise StandardError.new('Project cannot be renamed, because tags are present in its container registry')
end end
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)
...@@ -1063,7 +1063,7 @@ class Project < ActiveRecord::Base ...@@ -1063,7 +1063,7 @@ class Project < ActiveRecord::Base
# if we cannot move namespace directory we should rollback # if we cannot move namespace directory we should rollback
# db changes in order to prevent out of sync between db and fs # db changes in order to prevent out of sync between db and fs
raise Exception.new('repository cannot be renamed') raise StandardError.new('repository cannot be renamed')
end end
Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}" Gitlab::AppLogger.info "Project was renamed: #{old_path_with_namespace} -> #{new_path_with_namespace}"
......
...@@ -4,7 +4,7 @@ class GroupPolicy < BasePolicy ...@@ -4,7 +4,7 @@ class GroupPolicy < BasePolicy
return unless @user return unless @user
globally_viewable = @subject.public? || (@subject.internal? && !@user.external?) globally_viewable = @subject.public? || (@subject.internal? && !@user.external?)
member = @subject.users.include?(@user) member = @subject.users_with_parents.include?(@user)
owner = @user.admin? || @subject.has_owner?(@user) owner = @user.admin? || @subject.has_owner?(@user)
master = owner || @subject.has_master?(@user) master = owner || @subject.has_master?(@user)
......
...@@ -261,7 +261,7 @@ class ProjectPolicy < BasePolicy ...@@ -261,7 +261,7 @@ class ProjectPolicy < BasePolicy
def project_group_member?(user) def project_group_member?(user)
project.group && project.group &&
( (
project.group.members.exists?(user_id: user.id) || project.group.members_with_parents.exists?(user_id: user.id) ||
project.group.requesters.exists?(user_id: user.id) project.group.requesters.exists?(user_id: user.id)
) )
end end
......
...@@ -74,7 +74,7 @@ module Users ...@@ -74,7 +74,7 @@ module Users
# remove - The IDs of the authorization rows to remove. # remove - The IDs of the authorization rows to remove.
# add - Rows to insert in the form `[user id, project id, access level]` # add - Rows to insert in the form `[user id, project id, access level]`
def update_authorizations(remove = [], add = []) def update_authorizations(remove = [], add = [])
return if remove.empty? && add.empty? return if remove.empty? && add.empty? && user.authorized_projects_populated
User.transaction do User.transaction do
user.remove_project_authorizations(remove) unless remove.empty? user.remove_project_authorizations(remove) unless remove.empty?
......
- page_title "Deploy Keys" - page_title "Deploy Keys"
.panel.panel-default.prepend-top-default
.panel-heading %h3.page-title.deploy-keys-title
Public deploy keys (#{@deploy_keys.count}) Public deploy keys (#{@deploy_keys.count})
.controls .pull-right
= link_to 'New Deploy Key', new_admin_deploy_key_path, class: "btn btn-new btn-sm" = link_to 'New Deploy Key', new_admin_deploy_key_path, class: 'btn btn-new btn-sm btn-inverted'
- if @deploy_keys.any?
.table-holder - if @deploy_keys.any?
%table.table .table-holder.deploy-keys-list
%thead.panel-heading %table.table
%thead
%tr
%th.col-sm-2 Title
%th.col-sm-4 Fingerprint
%th.col-sm-2 Write access allowed
%th.col-sm-2 Added at
%th.col-sm-2
%tbody
- @deploy_keys.each do |deploy_key|
%tr %tr
%th Title %td
%th Fingerprint %strong= deploy_key.title
%th Added at %td
%th %code.key-fingerprint= deploy_key.fingerprint
%tbody %td
- @deploy_keys.each do |deploy_key| - if deploy_key.can_push?
%tr Yes
%td - else
%strong= deploy_key.title No
%td %td
%code.key-fingerprint= deploy_key.fingerprint %span.cgray
%td added #{time_ago_with_tooltip(deploy_key.created_at)}
%span.cgray %td
added #{time_ago_with_tooltip(deploy_key.created_at)} = link_to 'Remove', admin_deploy_key_path(deploy_key), data: { confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-sm btn-remove delete-key pull-right'
%td
= link_to 'Remove', admin_deploy_key_path(deploy_key), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-sm btn-remove delete-key pull-right"
...@@ -16,6 +16,14 @@ ...@@ -16,6 +16,14 @@
Paste a machine public key here. Read more about how to generate it Paste a machine public key here. Read more about how to generate it
= link_to "here", help_page_path("ssh/README") = link_to "here", help_page_path("ssh/README")
= f.text_area :key, class: "form-control thin_area", rows: 5 = f.text_area :key, class: "form-control thin_area", rows: 5
.form-group
.control-label
.col-sm-10
= f.label :can_push do
= f.check_box :can_push
%strong Write access allowed
%p.light.append-bottom-0
Allow this key to push to repository as well? (Default only allows pull access.)
.form-actions .form-actions
= f.submit 'Create', class: "btn-create btn" = f.submit 'Create', class: "btn-create btn"
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
= render "projects/commits/head" = render "projects/commits/head"
%div{ class: container_class } %div{ class: container_class }
.top-area .top-area.adjust
.nav-text .nav-text
Protected branches can be managed in project settings Protected branches can be managed in project settings
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
= search_field_tag :search, params[:search], { placeholder: 'Filter by branch name', id: 'branch-search', class: 'form-control search-text-input input-short', spellcheck: false } = search_field_tag :search, params[:search], { placeholder: 'Filter by branch name', id: 'branch-search', class: 'form-control search-text-input input-short', spellcheck: false }
.dropdown.inline .dropdown.inline
%button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'} %button.dropdown-menu-toggle{type: 'button', 'data-toggle' => 'dropdown'}
%span.light %span.light
= projects_sort_options_hash[@sort] = projects_sort_options_hash[@sort]
= icon('chevron-down') = icon('chevron-down')
......
...@@ -6,6 +6,9 @@ ...@@ -6,6 +6,9 @@
= deploy_key.title = deploy_key.title
.description .description
= deploy_key.fingerprint = deploy_key.fingerprint
- if deploy_key.can_push?
.write-access-allowed
Write access allowed
.deploy-key-content.prepend-left-default.deploy-key-projects .deploy-key-content.prepend-left-default.deploy-key-projects
- deploy_key.projects.each do |project| - deploy_key.projects.each do |project|
- if can?(current_user, :read_project, project) - if can?(current_user, :read_project, project)
......
...@@ -10,4 +10,13 @@ ...@@ -10,4 +10,13 @@
%p.light.append-bottom-0 %p.light.append-bottom-0
Paste a machine public key here. Read more about how to generate it Paste a machine public key here. Read more about how to generate it
= link_to "here", help_page_path("ssh/README") = link_to "here", help_page_path("ssh/README")
.form-group
.checkbox
= f.label :can_push do
= f.check_box :can_push
%strong Write access allowed
.form-group
%p.light.append-bottom-0
Allow this key to push to repository as well? (Default only allows pull access.)
= f.submit "Add key", class: "btn-create btn" = f.submit "Add key", class: "btn-create btn"
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
%span.pull-right %span.pull-right
= link_to 'Change branches', mr_change_branches_path(@merge_request) = link_to 'Change branches', mr_change_branches_path(@merge_request)
%hr %hr
= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal common-note-form js-requires-input' } do |f| = form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal common-note-form js-requires-input js-quick-submit' } do |f|
= render 'shared/issuable/form', f: f, issuable: @merge_request = render 'shared/issuable/form', f: f, issuable: @merge_request
= f.hidden_field :source_project_id = f.hidden_field :source_project_id
= f.hidden_field :source_branch = f.hidden_field :source_branch
......
...@@ -50,7 +50,8 @@ ...@@ -50,7 +50,8 @@
- if mr_issues_mentioned_but_not_closing.present? - if mr_issues_mentioned_but_not_closing.present?
#{"Issue".pluralize(mr_issues_mentioned_but_not_closing.size)} #{"Issue".pluralize(mr_issues_mentioned_but_not_closing.size)}
!= markdown issues_sentence(mr_issues_mentioned_but_not_closing), pipeline: :gfm, author: @merge_request.author != markdown issues_sentence(mr_issues_mentioned_but_not_closing), pipeline: :gfm, author: @merge_request.author
#{mr_issues_mentioned_but_not_closing.size > 1 ? 'are' : 'is'} mentioned but will not closed. #{mr_issues_mentioned_but_not_closing.size > 1 ? 'are' : 'is'} mentioned but will not be closed.
- if @merge_request.approvals.any? - if @merge_request.approvals.any?
.mr-widget-footer.approved-by-users .mr-widget-footer.approved-by-users
......
- can_resolve = @merge_request.conflicts_can_be_resolved_by?(current_user)
- can_resolve_in_ui = @merge_request.conflicts_can_be_resolved_in_ui?
- can_merge = @merge_request.can_be_merged_via_command_line_by?(current_user)
%h4.has-conflicts %h4.has-conflicts
= icon("exclamation-triangle") = icon("exclamation-triangle")
This merge request contains merge conflicts This merge request contains merge conflicts
%p %p
Please To merge this request, resolve these conflicts
- if @merge_request.conflicts_can_be_resolved_by?(current_user) - if can_resolve && !can_resolve_in_ui
- if @merge_request.conflicts_can_be_resolved_in_ui? locally
= link_to "resolve these conflicts", conflicts_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)
- else
%span.has-tooltip{title: "These conflicts cannot be resolved through GitLab"}
resolve these conflicts locally
- else
resolve these conflicts
or or
- unless can_merge
ask someone with write access to this repository to
merge it locally.
- if @merge_request.can_be_merged_via_command_line_by?(current_user) - if (can_resolve && can_resolve_in_ui) || can_merge
#{link_to "merge this request manually", "#modal_merge_info", class: "how_to_merge_link vlink", "data-toggle" => "modal"}. .btn-group
- else - if can_resolve && can_resolve_in_ui
ask someone with write access to this repository to merge this request manually. = link_to "Resolve conflicts", conflicts_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: "btn"
- if can_merge
= link_to "Merge locally", "#modal_merge_info", class: "btn how_to_merge_link vlink", "data-toggle" => "modal"
- commit = @repository.commit(tag.dereferenced_target) - commit = @repository.commit(tag.dereferenced_target)
- release = @releases.find { |release| release.tag == tag.name } - release = @releases.find { |release| release.tag == tag.name }
%li %li.flex-row
%div .row-main-content.str-truncated
= link_to namespace_project_tag_path(@project.namespace, @project, tag.name) do = link_to namespace_project_tag_path(@project.namespace, @project, tag.name) do
%span.item-title %span.item-title
= icon('tag') = icon('tag')
...@@ -10,24 +10,25 @@ ...@@ -10,24 +10,25 @@
&nbsp; &nbsp;
= strip_gpg_signature(tag.message) = strip_gpg_signature(tag.message)
.controls - if commit
= render 'projects/buttons/download', project: @project, ref: tag.name .block-truncated
= render 'projects/branches/commit', commit: commit, project: @project
- else
%p
Cant find HEAD commit for this tag
- if release && release.description.present?
.description.prepend-top-default
.wiki
= preserve do
= markdown_field(release, :description)
- if can?(current_user, :push_code, @project) .row-fixed-content.controls
= link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn has-tooltip', title: "Edit release notes", data: { container: "body" } do = render 'projects/buttons/download', project: @project, ref: tag.name
= icon("pencil")
- if can?(current_user, :admin_project, @project) - if can?(current_user, :push_code, @project)
= link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-remove remove-row has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{tag.name}' tag cannot be undone. Are you sure?", container: 'body' }, remote: true do = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn has-tooltip', title: "Edit release notes", data: { container: "body" } do
= icon("trash-o") = icon("pencil")
- if commit - if can?(current_user, :admin_project, @project)
= render 'projects/branches/commit', commit: commit, project: @project = link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-remove remove-row has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{tag.name}' tag cannot be undone. Are you sure?", container: 'body' }, remote: true do
- else = icon("trash-o")
%p
Cant find HEAD commit for this tag
- if release && release.description.present?
.description.prepend-top-default
.wiki
= preserve do
= markdown_field(release, :description)
...@@ -2,16 +2,16 @@ ...@@ -2,16 +2,16 @@
- page_title "Tags" - page_title "Tags"
= render "projects/commits/head" = render "projects/commits/head"
%div{ class: container_class } %div.flex-list{ class: container_class }
.top-area .top-area.flex-row
.nav-text .nav-text.row-main-content
Tags give the ability to mark specific points in history as being important Tags give the ability to mark specific points in history as being important
.nav-controls .nav-controls.row-fixed-content
= form_tag(filter_tags_path, method: :get) do = form_tag(filter_tags_path, method: :get) do
= search_field_tag :search, params[:search], { placeholder: 'Filter by tag name', id: 'tag-search', class: 'form-control search-text-input input-short', spellcheck: false } = search_field_tag :search, params[:search], { placeholder: 'Filter by tag name', id: 'tag-search', class: 'form-control search-text-input input-short', spellcheck: false }
.dropdown.inline .dropdown
%button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown'} } %button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown'} }
%span.light %span.light
= projects_sort_options_hash[@sort] = projects_sort_options_hash[@sort]
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
.tags .tags
- if @tags.any? - if @tags.any?
%ul.content-list %ul.flex-list.content-list
= render partial: 'tag', collection: @tags = render partial: 'tag', collection: @tags
= paginate @tags, theme: 'gitlab' = paginate @tags, theme: 'gitlab'
......
...@@ -12,21 +12,23 @@ ...@@ -12,21 +12,23 @@
- else - else
Cant find HEAD commit for this tag Cant find HEAD commit for this tag
.nav-controls .nav-controls.controls-flex
- if can?(current_user, :push_code, @project) - if can?(current_user, :push_code, @project)
= link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn has-tooltip', title: 'Edit release notes' do = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn controls-item has-tooltip', title: 'Edit release notes' do
= icon("pencil") = icon("pencil")
= link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn has-tooltip', title: 'Browse files' do = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn controls-item has-tooltip', title: 'Browse files' do
= icon('files-o') = icon('files-o')
= link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn has-tooltip', title: 'Browse commits' do = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn controls-item has-tooltip', title: 'Browse commits' do
= icon('history') = icon('history')
= render 'projects/buttons/download', project: @project, ref: @tag.name .btn-container.controls-item
= render 'projects/buttons/download', project: @project, ref: @tag.name
- if can?(current_user, :admin_project, @project) - if can?(current_user, :admin_project, @project)
.pull-right .btn-container.controls-item-full
= link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do
%i.fa.fa-trash-o %i.fa.fa-trash-o
- if @tag.message.present? - if @tag.message.present?
%pre.body %pre.wrap
= strip_gpg_signature(@tag.message) = strip_gpg_signature(@tag.message)
.append-bottom-default.prepend-top-default .append-bottom-default.prepend-top-default
......
---
title: Go to a project order
merge_request: 7737
author: Jacopo Beschi @jacopo-beschi
---
title: Prevent empty pagination when list is not empty
merge_request: 8172
author:
---
title: Improve visibility of "Resolve conflicts" and "Merge locally" actions
merge_request: 8229
author:
---
title: ensure permalinks scroll to correct position on multiple clicks
merge_request: 8046
author:
---
title: fix border in login session tabs
merge_request: 8346
author:
--- ---
title: Fixed GFM autocomplete error when no data exists title: Change status colors of runners to better defaults
merge_request: merge_request:
author: author:
---
title: Fix mr list timestamp alignment
merge_request: 8271
author:
---
title: Fix discussion overlap text in regular screens
merge_request: 8273
author:
---
title: Fix timeout when MR contains large files marked as binary by .gitattributes
merge_request:
author:
---
title: Fixes mini-pipeline-graph dropdown animation and stage position in chrome, firefox and safari
merge_request: 8282
author:
---
title: Fix line breaking in nodes of the pipeline graph in firefox
merge_request: 8292
author:
---
title: Fixes confendential warning text alignment
merge_request: 8293
author:
---
title: Hide Scroll Top button for failed build page
merge_request: 8295
author:
---
title: Cache project authorizations even when user has access to zero projects
merge_request: 8327
author:
---
title: Make CTRL+Enter submits a new merge request
merge_request: 8360
author: Saad Shahd
---
title: Fixes issue boards list colored top border visual glitch
merge_request: 7898
author: Pier Paolo Ramon
---
title: Disable PostgreSQL statement timeouts when removing unneeded services
merge_request: 8322
author:
---
title: Fix 500 error when visit group from admin area if group name contains dot
merge_request:
author:
---
title: Rename users with namespace ending with .git
merge_request: 8309
author:
---
title: Rename projects wth reserved names
merge_request: 8234
author:
---
title: Allow to add deploy keys with write-access
merge_request: 5807
author: Ali Ibrahim
---
title: Fix a Grape deprecation, use `#request_method` instead of `#route_method`
merge_request:
author:
---
title: Fix finding the latest pipeline
merge_request: 8301
author:
---
title: Darkened hr border color in descriptions because of update of bootstrap
merge_request: 8333
author:
---
title: Fix a minor grammar error in merge request widget
merge_request: 8337
author:
--- ---
title: Rename "autodeploy" to "auto deploy" title: Fixed GFM dropdown not showing on new lines
merge_request: merge_request:
author: author:
---
title: change 'gray' color theme name to 'black' to match the actual color
merge_request: 7908
author: BM5k
---
title: Fix unclear closing issue behaviour on Merge Request show page
merge_request: 8345
author: Gabriel Gizotti
---
title: About GitLab link in sidebar that links to help page
merge_request: 8316
author:
...@@ -36,7 +36,7 @@ namespace :admin do ...@@ -36,7 +36,7 @@ namespace :admin do
scope(path: 'groups/*id', scope(path: 'groups/*id',
controller: :groups, controller: :groups,
constraints: { id: Gitlab::Regex.namespace_route_regex }) do constraints: { id: Gitlab::Regex.namespace_route_regex, format: /(html|json|atom)/ }) do
scope(as: :group) do scope(as: :group) do
put :members_update put :members_update
......
class AddCanPushToKeys < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
DOWNTIME = false
def up
add_column_with_default(:keys, :can_push, :boolean, default: false, allow_null: false)
end
def down
remove_column(:keys, :can_push)
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class RemoveDotGitFromUsernames < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
include Gitlab::ShellAdapter
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
def up
invalid_users.each do |user|
id = user['id']
namespace_id = user['namespace_id']
path_was = user['username']
path_was_wildcard = quote_string("#{path_was}/%")
path = quote_string(rename_path(path_was))
move_namespace(namespace_id, path_was, path)
execute "UPDATE routes SET path = '#{path}' WHERE source_type = 'Namespace' AND source_id = #{namespace_id}"
execute "UPDATE namespaces SET path = '#{path}' WHERE id = #{namespace_id}"
execute "UPDATE users SET username = '#{path}' WHERE id = #{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_users
select_all("SELECT u.id, u.username, n.path AS namespace_path, n.id AS namespace_id FROM users u
INNER JOIN namespaces n ON n.owner_id = u.id
WHERE n.type is NULL AND n.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(namespace_id, path_was, path)
repository_storage_paths = select_all("SELECT distinct(repository_storage) FROM projects WHERE namespace_id = #{namespace_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 RenameSlackAndMattermostNotificationServices < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
update_column_in_batches(:services, :type, 'SlackService') do |table, query|
query.where(table[:type].eq('SlackNotificationService'))
end
update_column_in_batches(:services, :type, 'MattermostService') do |table, query|
query.where(table[:type].eq('MattermostNotificationService'))
end
end
def down
update_column_in_batches(:services, :type, 'SlackNotificationService') do |table, query|
query.where(table[:type].eq('SlackService'))
end
update_column_in_batches(:services, :type, 'MattermostNotificationService') do |table, query|
query.where(table[:type].eq('MattermostService'))
end
end
end
require 'thread'
class RenameReservedProjectNames < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
include Gitlab::ShellAdapter
DOWNTIME = false
THREAD_COUNT = 8
KNOWN_PATHS = %w(.well-known
all
assets
blame
blob
commits
create
create_dir
edit
files
files
find_file
groups
hooks
issues
logs_tree
merge_requests
new
new
preview
profile
projects
public
raw
repository
robots.txt
s
snippets
teams
tree
u
unsubscribes
update
users
wikis)
def up
queues = Array.new(THREAD_COUNT) { Queue.new }
start = false
threads = Array.new(THREAD_COUNT) do |index|
Thread.new do
queue = queues[index]
# Wait until we have input to process.
until start; end
rename_projects(queue.pop) until queue.empty?
end
end
enum = queues.each
reserved_projects.each_slice(100) do |slice|
begin
queue = enum.next
rescue StopIteration
enum.rewind
retry
end
queue << slice
end
start = true
threads.each(&:join)
end
def down
# nothing to do here
end
private
def reserved_projects
Project.unscoped.
includes(:namespace).
where('EXISTS (SELECT 1 FROM namespaces WHERE projects.namespace_id = namespaces.id)').
where('projects.path' => KNOWN_PATHS)
end
def route_exists?(full_path)
quoted_path = ActiveRecord::Base.connection.quote_string(full_path)
ActiveRecord::Base.connection.
select_all("SELECT id, path FROM routes WHERE path = '#{quoted_path}'").present?
end
# Adds number to the end of the path that is not taken by other route
def rename_path(namespace_path, path_was)
counter = 0
path = "#{path_was}#{counter}"
while route_exists?("#{namespace_path}/#{path}")
counter += 1
path = "#{path_was}#{counter}"
end
path
end
def rename_projects(projects)
projects.each do |project|
id = project.id
path_was = project.path
namespace_path = project.namespace.path
path = rename_path(namespace_path, path_was)
begin
# Because project path update is quite complex operation we can't safely
# copy-paste all code from GitLab. As exception we use Rails code here
project.rename_repo if rename_project_row(project, path)
rescue Exception => e # rubocop: disable Lint/RescueException
Rails.logger.error "Exception when renaming project #{id}: #{e.message}"
end
end
end
def rename_project_row(project, path)
project.respond_to?(:update_attributes) &&
project.update_attributes(path: path) &&
project.respond_to?(:rename_repo)
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20161221140236) do ActiveRecord::Schema.define(version: 20161227192806) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -601,6 +601,7 @@ ActiveRecord::Schema.define(version: 20161221140236) do ...@@ -601,6 +601,7 @@ ActiveRecord::Schema.define(version: 20161221140236) do
t.string "type" t.string "type"
t.string "fingerprint" t.string "fingerprint"
t.boolean "public", default: false, null: false t.boolean "public", default: false, null: false
t.boolean "can_push", default: false, null: false
end end
add_index "keys", ["fingerprint"], name: "index_keys_on_fingerprint", unique: true, using: :btree add_index "keys", ["fingerprint"], name: "index_keys_on_fingerprint", unique: true, using: :btree
...@@ -1509,4 +1510,4 @@ ActiveRecord::Schema.define(version: 20161221140236) do ...@@ -1509,4 +1510,4 @@ ActiveRecord::Schema.define(version: 20161221140236) do
add_foreign_key "subscriptions", "projects", on_delete: :cascade add_foreign_key "subscriptions", "projects", on_delete: :cascade
add_foreign_key "trending_projects", "projects", on_delete: :cascade add_foreign_key "trending_projects", "projects", on_delete: :cascade
add_foreign_key "u2f_registrations", "users" add_foreign_key "u2f_registrations", "users"
end end
\ No newline at end of file
...@@ -20,12 +20,14 @@ Example response: ...@@ -20,12 +20,14 @@ Example response:
"id": 1, "id": 1,
"title": "Public key", "title": "Public key",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
"can_push": false,
"created_at": "2013-10-02T10:12:29Z" "created_at": "2013-10-02T10:12:29Z"
}, },
{ {
"id": 3, "id": 3,
"title": "Another Public key", "title": "Another Public key",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
"can_push": true,
"created_at": "2013-10-02T11:12:29Z" "created_at": "2013-10-02T11:12:29Z"
} }
] ]
...@@ -55,12 +57,14 @@ Example response: ...@@ -55,12 +57,14 @@ Example response:
"id": 1, "id": 1,
"title": "Public key", "title": "Public key",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
"can_push": false,
"created_at": "2013-10-02T10:12:29Z" "created_at": "2013-10-02T10:12:29Z"
}, },
{ {
"id": 3, "id": 3,
"title": "Another Public key", "title": "Another Public key",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
"can_push": false,
"created_at": "2013-10-02T11:12:29Z" "created_at": "2013-10-02T11:12:29Z"
} }
] ]
...@@ -92,6 +96,7 @@ Example response: ...@@ -92,6 +96,7 @@ Example response:
"id": 1, "id": 1,
"title": "Public key", "title": "Public key",
"key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=", "key": "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=",
"can_push": false,
"created_at": "2013-10-02T10:12:29Z" "created_at": "2013-10-02T10:12:29Z"
} }
``` ```
...@@ -107,14 +112,15 @@ project only if original one was is accessible by the same user. ...@@ -107,14 +112,15 @@ project only if original one was is accessible by the same user.
POST /projects/:id/deploy_keys POST /projects/:id/deploy_keys
``` ```
| Attribute | Type | Required | Description | | Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- | | --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of the project | | `id` | integer | yes | The ID of the project |
| `title` | string | yes | New deploy key's title | | `title` | string | yes | New deploy key's title |
| `key` | string | yes | New deploy key | | `key` | string | yes | New deploy key |
| `can_push` | boolean | no | Can deploy key push to the project's repository |
```bash ```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --header "Content-Type: application/json" --data '{"title": "My deploy key", "key": "ssh-rsa AAAA..."}' "https://gitlab.example.com/api/v3/projects/5/deploy_keys/" curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --header "Content-Type: application/json" --data '{"title": "My deploy key", "key": "ssh-rsa AAAA...", "can_push": "true"}' "https://gitlab.example.com/api/v3/projects/5/deploy_keys/"
``` ```
Example response: Example response:
...@@ -124,6 +130,7 @@ Example response: ...@@ -124,6 +130,7 @@ Example response:
"key" : "ssh-rsa AAAA...", "key" : "ssh-rsa AAAA...",
"id" : 12, "id" : 12,
"title" : "My deploy key", "title" : "My deploy key",
"can_push": true,
"created_at" : "2015-08-29T12:44:31.550Z" "created_at" : "2015-08-29T12:44:31.550Z"
} }
``` ```
......
...@@ -3,12 +3,15 @@ ...@@ -3,12 +3,15 @@
## Log arguments to Sidekiq jobs ## Log arguments to Sidekiq jobs
If you want to see what arguments are being passed to Sidekiq jobs you can set If you want to see what arguments are being passed to Sidekiq jobs you can set
the SIDEKIQ_LOG_ARGUMENTS environment variable. the `SIDEKIQ_LOG_ARGUMENTS` [environment variable]
(https://docs.gitlab.com/omnibus/settings/environment-variables.html) to `1` (true).
Example:
``` ```
SIDEKIQ_LOG_ARGUMENTS=1 bundle exec foreman start gitlab_rails['env'] = {"SIDEKIQ_LOG_ARGUMENTS" => "1"}
``` ```
It is not recommend to enable this setting in production because some Sidekiq Please note: It is not recommend to enable this setting in production because some
jobs (such as sending a password reset email) take secret arguments (for Sidekiq jobs (such as sending a password reset email) take secret arguments (for
example the password reset token). example the password reset token).
\ No newline at end of file
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.
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