Merge branch 'master' into jej-pages-picked-from-ee
This commit is contained in:
commit
1af3f3b673
|
@ -17,6 +17,7 @@ AllCops:
|
||||||
# Exclude some GitLab files
|
# Exclude some GitLab files
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'vendor/**/*'
|
- 'vendor/**/*'
|
||||||
|
- 'node_modules/**/*'
|
||||||
- 'db/*'
|
- 'db/*'
|
||||||
- 'db/fixtures/**/*'
|
- 'db/fixtures/**/*'
|
||||||
- 'tmp/**/*'
|
- 'tmp/**/*'
|
||||||
|
|
2
Gemfile
2
Gemfile
|
@ -112,7 +112,7 @@ gem 'org-ruby', '~> 0.9.12'
|
||||||
gem 'creole', '~> 0.5.0'
|
gem 'creole', '~> 0.5.0'
|
||||||
gem 'wikicloth', '0.8.1'
|
gem 'wikicloth', '0.8.1'
|
||||||
gem 'asciidoctor', '~> 1.5.2'
|
gem 'asciidoctor', '~> 1.5.2'
|
||||||
gem 'asciidoctor-plantuml', '0.0.6'
|
gem 'asciidoctor-plantuml', '0.0.7'
|
||||||
gem 'rouge', '~> 2.0'
|
gem 'rouge', '~> 2.0'
|
||||||
gem 'truncato', '~> 0.7.8'
|
gem 'truncato', '~> 0.7.8'
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ GEM
|
||||||
faraday_middleware-multi_json (~> 0.0)
|
faraday_middleware-multi_json (~> 0.0)
|
||||||
oauth2 (~> 1.0)
|
oauth2 (~> 1.0)
|
||||||
asciidoctor (1.5.3)
|
asciidoctor (1.5.3)
|
||||||
asciidoctor-plantuml (0.0.6)
|
asciidoctor-plantuml (0.0.7)
|
||||||
asciidoctor (~> 1.5)
|
asciidoctor (~> 1.5)
|
||||||
ast (2.3.0)
|
ast (2.3.0)
|
||||||
attr_encrypted (3.0.3)
|
attr_encrypted (3.0.3)
|
||||||
|
@ -844,7 +844,7 @@ DEPENDENCIES
|
||||||
allocations (~> 1.0)
|
allocations (~> 1.0)
|
||||||
asana (~> 0.4.0)
|
asana (~> 0.4.0)
|
||||||
asciidoctor (~> 1.5.2)
|
asciidoctor (~> 1.5.2)
|
||||||
asciidoctor-plantuml (= 0.0.6)
|
asciidoctor-plantuml (= 0.0.7)
|
||||||
attr_encrypted (~> 3.0.0)
|
attr_encrypted (~> 3.0.0)
|
||||||
awesome_print (~> 1.2.0)
|
awesome_print (~> 1.2.0)
|
||||||
babosa (~> 1.0.2)
|
babosa (~> 1.0.2)
|
||||||
|
|
|
@ -1,6 +1,19 @@
|
||||||
/* eslint-disable wrap-iife, func-names, space-before-function-paren, prefer-arrow-callback, vars-on-top, no-var, max-len */
|
/* eslint-disable wrap-iife, func-names, space-before-function-paren, prefer-arrow-callback, vars-on-top, no-var, max-len */
|
||||||
(function(w) {
|
(function(w) {
|
||||||
$(function() {
|
$(function() {
|
||||||
|
var toggleContainer = function(container, /* optional */toggleState) {
|
||||||
|
var $container = $(container);
|
||||||
|
|
||||||
|
$container
|
||||||
|
.find('.js-toggle-button .fa')
|
||||||
|
.toggleClass('fa-chevron-up', toggleState)
|
||||||
|
.toggleClass('fa-chevron-down', toggleState !== undefined ? !toggleState : undefined);
|
||||||
|
|
||||||
|
$container
|
||||||
|
.find('.js-toggle-content')
|
||||||
|
.toggle(toggleState);
|
||||||
|
};
|
||||||
|
|
||||||
// Toggle button. Show/hide content inside parent container.
|
// Toggle button. Show/hide content inside parent container.
|
||||||
// Button does not change visibility. If button has icon - it changes chevron style.
|
// Button does not change visibility. If button has icon - it changes chevron style.
|
||||||
//
|
//
|
||||||
|
@ -10,14 +23,7 @@
|
||||||
//
|
//
|
||||||
$('body').on('click', '.js-toggle-button', function(e) {
|
$('body').on('click', '.js-toggle-button', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
$(this)
|
toggleContainer($(this).closest('.js-toggle-container'));
|
||||||
.find('.fa')
|
|
||||||
.toggleClass('fa-chevron-down fa-chevron-up')
|
|
||||||
.end()
|
|
||||||
.closest('.js-toggle-container')
|
|
||||||
.find('.js-toggle-content')
|
|
||||||
.toggle()
|
|
||||||
;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// If we're accessing a permalink, ensure it is not inside a
|
// If we're accessing a permalink, ensure it is not inside a
|
||||||
|
@ -26,8 +32,8 @@
|
||||||
var anchor = hash && document.getElementById(hash);
|
var anchor = hash && document.getElementById(hash);
|
||||||
var container = anchor && $(anchor).closest('.js-toggle-container');
|
var container = anchor && $(anchor).closest('.js-toggle-container');
|
||||||
|
|
||||||
if (container && container.find('.js-toggle-content').is(':hidden')) {
|
if (container) {
|
||||||
container.find('.js-toggle-button').trigger('click');
|
toggleContainer(container, true);
|
||||||
anchor.scrollIntoView();
|
anchor.scrollIntoView();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -180,9 +180,9 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th class="environments-name">Environment</th>
|
<th class="environments-name">Environment</th>
|
||||||
<th class="environments-deploy">Last deployment</th>
|
<th class="environments-deploy">Last deployment</th>
|
||||||
<th class="environments-build">Build</th>
|
<th class="environments-build">Job</th>
|
||||||
<th class="environments-commit">Commit</th>
|
<th class="environments-commit">Commit</th>
|
||||||
<th class="environments-date">Created</th>
|
<th class="environments-date">Updated</th>
|
||||||
<th class="hidden-xs environments-actions"></th>
|
<th class="hidden-xs environments-actions"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
|
@ -39,8 +39,15 @@
|
||||||
getSearchInput() {
|
getSearchInput() {
|
||||||
const query = gl.DropdownUtils.getSearchInput(this.input);
|
const query = gl.DropdownUtils.getSearchInput(this.input);
|
||||||
const { lastToken } = gl.FilteredSearchTokenizer.processTokens(query);
|
const { lastToken } = gl.FilteredSearchTokenizer.processTokens(query);
|
||||||
|
let value = lastToken.value || '';
|
||||||
|
|
||||||
return lastToken.value || '';
|
// Removes the first character if it is a quotation so that we can search
|
||||||
|
// with multiple words
|
||||||
|
if (value[0] === '"' || value[0] === '\'') {
|
||||||
|
value = value.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
|
|
@ -83,12 +83,12 @@
|
||||||
_a = decodeURI("%C3%80");
|
_a = decodeURI("%C3%80");
|
||||||
_y = decodeURI("%C3%BF");
|
_y = decodeURI("%C3%BF");
|
||||||
|
|
||||||
regexp = new RegExp("^(?:\\B|[^a-zA-Z0-9_" + atSymbolsWithoutBar + "]|\\s)" + flag + "(?![" + atSymbolsWithBar + "])([A-Za-z" + _a + "-" + _y + "0-9_\'\.\+\-]*)$", 'gi');
|
regexp = new RegExp("^(?:\\B|[^a-zA-Z0-9_" + atSymbolsWithoutBar + "]|\\s)" + flag + "(?![" + atSymbolsWithBar + "])(([A-Za-z" + _a + "-" + _y + "0-9_\'\.\+\-]|[^\\x00-\\x7a])*)$", 'gi');
|
||||||
|
|
||||||
match = regexp.exec(subtext);
|
match = regexp.exec(subtext);
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
return match[2] || match[1];
|
return (match[1] || match[1] === "") ? match[1] : match[2];
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -249,7 +249,7 @@
|
||||||
_this.fullData = data;
|
_this.fullData = data;
|
||||||
_this.parseData(_this.fullData);
|
_this.parseData(_this.fullData);
|
||||||
_this.focusTextInput();
|
_this.focusTextInput();
|
||||||
if (_this.options.filterable && _this.filter && _this.filter.input && _this.filter.input.val().trim() !== '') {
|
if (_this.options.filterable && _this.filter && _this.filter.input && _this.filter.input.val() && _this.filter.input.val().trim() !== '') {
|
||||||
return _this.filter.input.trigger('input');
|
return _this.filter.input.trigger('input');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -162,6 +162,7 @@
|
||||||
|
|
||||||
w.gl.utils.getSelectedFragment = () => {
|
w.gl.utils.getSelectedFragment = () => {
|
||||||
const selection = window.getSelection();
|
const selection = window.getSelection();
|
||||||
|
if (selection.rangeCount === 0) return null;
|
||||||
const documentFragment = selection.getRangeAt(0).cloneContents();
|
const documentFragment = selection.getRangeAt(0).cloneContents();
|
||||||
if (documentFragment.textContent.length === 0) return null;
|
if (documentFragment.textContent.length === 0) return null;
|
||||||
|
|
||||||
|
|
|
@ -110,9 +110,8 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
MergeRequest.prototype.initCommitMessageListeners = function() {
|
MergeRequest.prototype.initCommitMessageListeners = function() {
|
||||||
var textarea = $('textarea.js-commit-message');
|
$(document).on('click', 'a.js-with-description-link', function(e) {
|
||||||
|
var textarea = $('textarea.js-commit-message');
|
||||||
$('a.js-with-description-link').on('click', function(e) {
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
textarea.val(textarea.data('messageWithDescription'));
|
textarea.val(textarea.data('messageWithDescription'));
|
||||||
|
@ -120,7 +119,8 @@
|
||||||
$('p.js-without-description-hint').show();
|
$('p.js-without-description-hint').show();
|
||||||
});
|
});
|
||||||
|
|
||||||
$('a.js-without-description-link').on('click', function(e) {
|
$(document).on('click', 'a.js-without-description-link', function(e) {
|
||||||
|
var textarea = $('textarea.js-commit-message');
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
textarea.val(textarea.data('messageWithoutDescription'));
|
textarea.val(textarea.data('messageWithoutDescription'));
|
||||||
|
|
|
@ -154,12 +154,22 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data.environments && data.environments.length) _this.renderEnvironments(data.environments);
|
if (data.environments && data.environments.length) _this.renderEnvironments(data.environments);
|
||||||
if (data.status !== _this.opts.ci_status && (data.status != null)) {
|
if (data.status !== _this.opts.ci_status ||
|
||||||
|
data.sha !== _this.opts.ci_sha ||
|
||||||
|
data.pipeline !== _this.opts.ci_pipeline) {
|
||||||
_this.opts.ci_status = data.status;
|
_this.opts.ci_status = data.status;
|
||||||
_this.showCIStatus(data.status);
|
_this.showCIStatus(data.status);
|
||||||
if (data.coverage) {
|
if (data.coverage) {
|
||||||
_this.showCICoverage(data.coverage);
|
_this.showCICoverage(data.coverage);
|
||||||
}
|
}
|
||||||
|
if (data.pipeline) {
|
||||||
|
_this.opts.ci_pipeline = data.pipeline;
|
||||||
|
_this.updatePipelineUrls(data.pipeline);
|
||||||
|
}
|
||||||
|
if (data.sha) {
|
||||||
|
_this.opts.ci_sha = data.sha;
|
||||||
|
_this.updateCommitUrls(data.sha);
|
||||||
|
}
|
||||||
if (showNotification) {
|
if (showNotification) {
|
||||||
status = _this.ciLabelForStatus(data.status);
|
status = _this.ciLabelForStatus(data.status);
|
||||||
if (status === "preparing") {
|
if (status === "preparing") {
|
||||||
|
@ -248,6 +258,16 @@
|
||||||
return $('.js-merge-button,.accept-action .dropdown-toggle').removeClass('btn-danger btn-info btn-create').addClass(css_class);
|
return $('.js-merge-button,.accept-action .dropdown-toggle').removeClass('btn-danger btn-info btn-create').addClass(css_class);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MergeRequestWidget.prototype.updatePipelineUrls = function(id) {
|
||||||
|
const pipelineUrl = this.opts.pipeline_path;
|
||||||
|
$('.pipeline').text(`#${id}`).attr('href', [pipelineUrl, id].join('/'));
|
||||||
|
};
|
||||||
|
|
||||||
|
MergeRequestWidget.prototype.updateCommitUrls = function(id) {
|
||||||
|
const commitsUrl = this.opts.commits_path;
|
||||||
|
$('.js-commit-link').text(`#${id}`).attr('href', [commitsUrl, id].join('/'));
|
||||||
|
};
|
||||||
|
|
||||||
return MergeRequestWidget;
|
return MergeRequestWidget;
|
||||||
})();
|
})();
|
||||||
})(window.gl || (window.gl = {}));
|
})(window.gl || (window.gl = {}));
|
||||||
|
|
|
@ -58,6 +58,11 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
Project.prototype.initRefSwitcher = function() {
|
Project.prototype.initRefSwitcher = function() {
|
||||||
|
var refListItem = document.createElement('li'),
|
||||||
|
refLink = document.createElement('a');
|
||||||
|
|
||||||
|
refLink.href = '#';
|
||||||
|
|
||||||
return $('.js-project-refs-dropdown').each(function() {
|
return $('.js-project-refs-dropdown').each(function() {
|
||||||
var $dropdown, selected;
|
var $dropdown, selected;
|
||||||
$dropdown = $(this);
|
$dropdown = $(this);
|
||||||
|
@ -67,7 +72,8 @@
|
||||||
return $.ajax({
|
return $.ajax({
|
||||||
url: $dropdown.data('refs-url'),
|
url: $dropdown.data('refs-url'),
|
||||||
data: {
|
data: {
|
||||||
ref: $dropdown.data('ref')
|
ref: $dropdown.data('ref'),
|
||||||
|
search: term
|
||||||
},
|
},
|
||||||
dataType: "json"
|
dataType: "json"
|
||||||
}).done(function(refs) {
|
}).done(function(refs) {
|
||||||
|
@ -76,16 +82,29 @@
|
||||||
},
|
},
|
||||||
selectable: true,
|
selectable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
filterRemote: true,
|
||||||
filterByText: true,
|
filterByText: true,
|
||||||
fieldName: $dropdown.data('field-name'),
|
fieldName: $dropdown.data('field-name'),
|
||||||
renderRow: function(ref) {
|
renderRow: function(ref) {
|
||||||
var link;
|
var li = refListItem.cloneNode(false);
|
||||||
|
|
||||||
if (ref.header != null) {
|
if (ref.header != null) {
|
||||||
return $('<li />').addClass('dropdown-header').text(ref.header);
|
li.className = 'dropdown-header';
|
||||||
|
li.textContent = ref.header;
|
||||||
} else {
|
} else {
|
||||||
link = $('<a />').attr('href', '#').addClass(ref === selected ? 'is-active' : '').text(ref).attr('data-ref', ref);
|
var link = refLink.cloneNode(false);
|
||||||
return $('<li />').append(link);
|
|
||||||
|
if (ref === selected) {
|
||||||
|
link.className = 'is-active';
|
||||||
|
}
|
||||||
|
|
||||||
|
link.textContent = ref;
|
||||||
|
link.dataset.ref = ref;
|
||||||
|
|
||||||
|
li.appendChild(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return li;
|
||||||
},
|
},
|
||||||
id: function(obj, $el) {
|
id: function(obj, $el) {
|
||||||
return $el.attr('data-ref');
|
return $el.attr('data-ref');
|
||||||
|
|
|
@ -49,7 +49,7 @@ class ProtectedBranchDropdown {
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
getProtectedBranches(term, callback) {
|
getProtectedBranches(term, callback) {
|
||||||
|
|
|
@ -39,17 +39,20 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
ShortcutsIssuable.prototype.replyWithSelectedText = function() {
|
ShortcutsIssuable.prototype.replyWithSelectedText = function() {
|
||||||
var quote, replyField, documentFragment, selected, separator;
|
var quote, documentFragment, selected, separator;
|
||||||
|
var replyField = $('.js-main-target-form #note_note');
|
||||||
|
|
||||||
documentFragment = window.gl.utils.getSelectedFragment();
|
documentFragment = window.gl.utils.getSelectedFragment();
|
||||||
if (!documentFragment) return;
|
if (!documentFragment) {
|
||||||
|
replyField.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// If the documentFragment contains more than just Markdown, don't copy as GFM.
|
// If the documentFragment contains more than just Markdown, don't copy as GFM.
|
||||||
if (documentFragment.querySelector('.md, .wiki')) return;
|
if (documentFragment.querySelector('.md, .wiki')) return;
|
||||||
|
|
||||||
selected = window.gl.CopyAsGFM.nodeToGFM(documentFragment);
|
selected = window.gl.CopyAsGFM.nodeToGFM(documentFragment);
|
||||||
|
|
||||||
replyField = $('.js-main-target-form #note_note');
|
|
||||||
if (selected.trim() === "") {
|
if (selected.trim() === "") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, camelcase, vars-on-top, object-shorthand, comma-dangle, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, max-len */
|
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, camelcase, vars-on-top, object-shorthand, comma-dangle, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, max-len */
|
||||||
/* global d3 */
|
/* global d3 */
|
||||||
/* global dateFormat */
|
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
|
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
|
||||||
|
@ -33,7 +32,7 @@
|
||||||
date.setDate(date.getDate() + i);
|
date.setDate(date.getDate() + i);
|
||||||
|
|
||||||
var day = date.getDay();
|
var day = date.getDay();
|
||||||
var count = timestamps[dateFormat(date, 'yyyy-mm-dd')];
|
var count = timestamps[date.format('yyyy-mm-dd')];
|
||||||
|
|
||||||
// Create a new group array if this is the first day of the week
|
// Create a new group array if this is the first day of the week
|
||||||
// or if is first object
|
// or if is first object
|
||||||
|
@ -122,7 +121,7 @@
|
||||||
if (stamp.count > 0) {
|
if (stamp.count > 0) {
|
||||||
contribText = stamp.count + " contribution" + (stamp.count > 1 ? 's' : '');
|
contribText = stamp.count + " contribution" + (stamp.count > 1 ? 's' : '');
|
||||||
}
|
}
|
||||||
dateText = dateFormat(date, 'mmm d, yyyy');
|
dateText = date.format('mmm d, yyyy');
|
||||||
return contribText + "<br />" + (gl.utils.getDayName(date)) + " " + dateText;
|
return contribText + "<br />" + (gl.utils.getDayName(date)) + " " + dateText;
|
||||||
};
|
};
|
||||||
})(this)).attr('class', 'user-contrib-cell js-tooltip').attr('fill', (function(_this) {
|
})(this)).attr('class', 'user-contrib-cell js-tooltip').attr('fill', (function(_this) {
|
||||||
|
|
|
@ -26,10 +26,9 @@
|
||||||
v-if='actions'
|
v-if='actions'
|
||||||
class="dropdown-toggle btn btn-default has-tooltip js-pipeline-dropdown-manual-actions"
|
class="dropdown-toggle btn btn-default has-tooltip js-pipeline-dropdown-manual-actions"
|
||||||
data-toggle="dropdown"
|
data-toggle="dropdown"
|
||||||
title="Manual build"
|
title="Manual job"
|
||||||
data-placement="top"
|
data-placement="top"
|
||||||
data-toggle="dropdown"
|
aria-label="Manual job"
|
||||||
aria-label="Manual build"
|
|
||||||
>
|
>
|
||||||
<span v-html='svgs.iconPlay' aria-hidden="true"></span>
|
<span v-html='svgs.iconPlay' aria-hidden="true"></span>
|
||||||
<i class="fa fa-caret-down" aria-hidden="true"></i>
|
<i class="fa fa-caret-down" aria-hidden="true"></i>
|
||||||
|
@ -54,7 +53,6 @@
|
||||||
data-toggle="dropdown"
|
data-toggle="dropdown"
|
||||||
title="Artifacts"
|
title="Artifacts"
|
||||||
data-placement="top"
|
data-placement="top"
|
||||||
data-toggle="dropdown"
|
|
||||||
aria-label="Artifacts"
|
aria-label="Artifacts"
|
||||||
>
|
>
|
||||||
<i class="fa fa-download" aria-hidden="true"></i>
|
<i class="fa fa-download" aria-hidden="true"></i>
|
||||||
|
|
|
@ -330,10 +330,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-file-option {
|
|
||||||
background: linear-gradient(180deg, $white-light 25%, $gray-light 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-build {
|
.btn-build {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
|
|
||||||
|
|
|
@ -56,15 +56,24 @@
|
||||||
&.right {
|
&.right {
|
||||||
float: right;
|
float: right;
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
|
|
||||||
a {
|
|
||||||
color: $gl-text-color;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.remove_source_checkbox {
|
.modify-merge-commit-link {
|
||||||
|
color: $gl-text-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.merge-param-checkbox {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a .fa-question-circle {
|
||||||
|
color: $gl-text-color-secondary;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
color: $link-hover-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -467,7 +467,7 @@ ul.notes {
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-diff-note {
|
.add-diff-note {
|
||||||
margin-top: -4px;
|
margin-top: -8px;
|
||||||
border-radius: 40px;
|
border-radius: 40px;
|
||||||
background: $white-light;
|
background: $white-light;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
|
|
|
@ -201,7 +201,8 @@
|
||||||
.stage-container {
|
.stage-container {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
margin-right: 6px;
|
height: 22px;
|
||||||
|
margin: 3px 6px 3px 0;
|
||||||
|
|
||||||
.tooltip {
|
.tooltip {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
|
@ -45,7 +45,7 @@ class Admin::ProjectsController < Admin::ApplicationController
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def project
|
def project
|
||||||
@project = Project.find_with_namespace(
|
@project = Project.find_by_full_path(
|
||||||
[params[:namespace_id], '/', params[:id]].join('')
|
[params[:namespace_id], '/', params[:id]].join('')
|
||||||
)
|
)
|
||||||
@project || render_404
|
@project || render_404
|
||||||
|
|
|
@ -24,7 +24,7 @@ class Admin::RunnerProjectsController < Admin::ApplicationController
|
||||||
private
|
private
|
||||||
|
|
||||||
def project
|
def project
|
||||||
@project = Project.find_with_namespace(
|
@project = Project.find_by_full_path(
|
||||||
[params[:namespace_id], '/', params[:project_id]].join('')
|
[params[:namespace_id], '/', params[:project_id]].join('')
|
||||||
)
|
)
|
||||||
@project || render_404
|
@project || render_404
|
||||||
|
|
|
@ -4,13 +4,15 @@ module CreatesCommit
|
||||||
def create_commit(service, success_path:, failure_path:, failure_view: nil, success_notice: nil)
|
def create_commit(service, success_path:, failure_path:, failure_view: nil, success_notice: nil)
|
||||||
set_commit_variables
|
set_commit_variables
|
||||||
|
|
||||||
|
start_branch = @mr_target_branch unless initial_commit?
|
||||||
commit_params = @commit_params.merge(
|
commit_params = @commit_params.merge(
|
||||||
source_project: @project,
|
start_project: @mr_target_project,
|
||||||
source_branch: @ref,
|
start_branch: start_branch,
|
||||||
target_branch: @target_branch
|
target_branch: @mr_source_branch
|
||||||
)
|
)
|
||||||
|
|
||||||
result = service.new(@tree_edit_project, current_user, commit_params).execute
|
result = service.new(
|
||||||
|
@mr_source_project, current_user, commit_params).execute
|
||||||
|
|
||||||
if result[:status] == :success
|
if result[:status] == :success
|
||||||
update_flash_notice(success_notice)
|
update_flash_notice(success_notice)
|
||||||
|
@ -89,20 +91,18 @@ module CreatesCommit
|
||||||
@mr_source_project != @mr_target_project
|
@mr_source_project != @mr_target_project
|
||||||
end
|
end
|
||||||
|
|
||||||
def different_branch?
|
|
||||||
@mr_source_branch != @mr_target_branch || different_project?
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_merge_request?
|
def create_merge_request?
|
||||||
params[:create_merge_request].present? && different_branch?
|
# XXX: Even if the field is set, if we're checking the same branch
|
||||||
|
# as the target branch in the same project,
|
||||||
|
# we don't want to create a merge request.
|
||||||
|
params[:create_merge_request].present? &&
|
||||||
|
(different_project? || @ref != @target_branch)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# TODO: We should really clean this up
|
||||||
def set_commit_variables
|
def set_commit_variables
|
||||||
@mr_source_branch ||= @target_branch
|
|
||||||
|
|
||||||
if can?(current_user, :push_code, @project)
|
if can?(current_user, :push_code, @project)
|
||||||
# Edit file in this project
|
# Edit file in this project
|
||||||
@tree_edit_project = @project
|
|
||||||
@mr_source_project = @project
|
@mr_source_project = @project
|
||||||
|
|
||||||
if @project.forked?
|
if @project.forked?
|
||||||
|
@ -112,15 +112,34 @@ module CreatesCommit
|
||||||
else
|
else
|
||||||
# Merge request to this project
|
# Merge request to this project
|
||||||
@mr_target_project = @project
|
@mr_target_project = @project
|
||||||
@mr_target_branch ||= @ref
|
@mr_target_branch = @ref || @target_branch
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
# Edit file in fork
|
|
||||||
@tree_edit_project = current_user.fork_of(@project)
|
|
||||||
# Merge request from fork to this project
|
# Merge request from fork to this project
|
||||||
@mr_source_project = @tree_edit_project
|
@mr_source_project = current_user.fork_of(@project)
|
||||||
@mr_target_project = @project
|
@mr_target_project = @project
|
||||||
@mr_target_branch ||= @ref
|
@mr_target_branch = @ref || @target_branch
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@mr_source_branch = guess_mr_source_branch
|
||||||
|
end
|
||||||
|
|
||||||
|
def initial_commit?
|
||||||
|
@mr_target_branch.nil? ||
|
||||||
|
!@mr_target_project.repository.branch_exists?(@mr_target_branch)
|
||||||
|
end
|
||||||
|
|
||||||
|
def guess_mr_source_branch
|
||||||
|
# XXX: Happens when viewing a commit without a branch. In this case,
|
||||||
|
# @target_branch would be the default branch for @mr_source_project,
|
||||||
|
# however we want a generated new branch here. Thus we can't use
|
||||||
|
# @target_branch, but should pass nil to indicate that we want a new
|
||||||
|
# branch instead of @target_branch.
|
||||||
|
return if
|
||||||
|
create_merge_request? &&
|
||||||
|
# XXX: Don't understand why rubocop prefers this indention
|
||||||
|
@mr_source_project.repository.branch_exists?(@target_branch)
|
||||||
|
|
||||||
|
@target_branch
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,7 +7,7 @@ module SpammableActions
|
||||||
|
|
||||||
def mark_as_spam
|
def mark_as_spam
|
||||||
if SpamService.new(spammable).mark_as_spam!
|
if SpamService.new(spammable).mark_as_spam!
|
||||||
redirect_to spammable, notice: "#{spammable.class} was submitted to Akismet successfully."
|
redirect_to spammable, notice: "#{spammable.spammable_entity_type.titlecase} was submitted to Akismet successfully."
|
||||||
else
|
else
|
||||||
redirect_to spammable, alert: 'Error with Akismet. Please check the logs for more info.'
|
redirect_to spammable, alert: 'Error with Akismet. Please check the logs for more info.'
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,10 +10,8 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
|
||||||
@projects = @projects.sort(@sort = params[:sort])
|
@projects = @projects.sort(@sort = params[:sort])
|
||||||
@projects = @projects.page(params[:page])
|
@projects = @projects.page(params[:page])
|
||||||
|
|
||||||
@last_push = current_user.recent_push
|
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html
|
format.html { @last_push = current_user.recent_push }
|
||||||
format.atom do
|
format.atom do
|
||||||
event_filter
|
event_filter
|
||||||
load_events
|
load_events
|
||||||
|
|
|
@ -84,7 +84,7 @@ class GroupsController < Groups::ApplicationController
|
||||||
if Groups::UpdateService.new(@group, current_user, group_params).execute
|
if Groups::UpdateService.new(@group, current_user, group_params).execute
|
||||||
redirect_to edit_group_path(@group), notice: "Group '#{@group.name}' was successfully updated."
|
redirect_to edit_group_path(@group), notice: "Group '#{@group.name}' was successfully updated."
|
||||||
else
|
else
|
||||||
@group.reset_path!
|
@group.restore_path!
|
||||||
|
|
||||||
render action: "edit"
|
render action: "edit"
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,7 +24,7 @@ class Projects::ApplicationController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
project_path = "#{namespace}/#{id}"
|
project_path = "#{namespace}/#{id}"
|
||||||
@project = Project.find_with_namespace(project_path)
|
@project = Project.find_by_full_path(project_path)
|
||||||
|
|
||||||
if can?(current_user, :read_project, @project) && !@project.pending_delete?
|
if can?(current_user, :read_project, @project) && !@project.pending_delete?
|
||||||
if @project.path_with_namespace != project_path
|
if @project.path_with_namespace != project_path
|
||||||
|
|
|
@ -50,7 +50,7 @@ class Projects::CommitController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def revert
|
def revert
|
||||||
assign_change_commit_vars(@commit.revert_branch_name)
|
assign_change_commit_vars
|
||||||
|
|
||||||
return render_404 if @target_branch.blank?
|
return render_404 if @target_branch.blank?
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ class Projects::CommitController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def cherry_pick
|
def cherry_pick
|
||||||
assign_change_commit_vars(@commit.cherry_pick_branch_name)
|
assign_change_commit_vars
|
||||||
|
|
||||||
return render_404 if @target_branch.blank?
|
return render_404 if @target_branch.blank?
|
||||||
|
|
||||||
|
@ -116,11 +116,9 @@ class Projects::CommitController < Projects::ApplicationController
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def assign_change_commit_vars(mr_source_branch)
|
def assign_change_commit_vars
|
||||||
@commit = project.commit(params[:id])
|
@commit = project.commit(params[:id])
|
||||||
@target_branch = params[:target_branch]
|
@target_branch = params[:target_branch]
|
||||||
@mr_source_branch = mr_source_branch
|
|
||||||
@mr_target_branch = @target_branch
|
|
||||||
@commit_params = {
|
@commit_params = {
|
||||||
commit: @commit,
|
commit: @commit,
|
||||||
create_merge_request: params[:create_merge_request].present? || different_project?
|
create_merge_request: params[:create_merge_request].present? || different_project?
|
||||||
|
|
|
@ -46,7 +46,8 @@ class Projects::CompareController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def define_diff_vars
|
def define_diff_vars
|
||||||
@compare = CompareService.new.execute(@project, @head_ref, @project, @start_ref)
|
@compare = CompareService.new(@project, @head_ref)
|
||||||
|
.execute(@project, @start_ref)
|
||||||
|
|
||||||
if @compare
|
if @compare
|
||||||
@commits = @compare.commits
|
@commits = @compare.commits
|
||||||
|
|
|
@ -79,7 +79,7 @@ class Projects::GitHttpClientController < Projects::ApplicationController
|
||||||
if project_id.blank?
|
if project_id.blank?
|
||||||
@project = nil
|
@project = nil
|
||||||
else
|
else
|
||||||
@project = Project.find_with_namespace("#{params[:namespace_id]}/#{project_id}")
|
@project = Project.find_by_full_path("#{params[:namespace_id]}/#{project_id}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -434,7 +434,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
||||||
title: merge_request.title,
|
title: merge_request.title,
|
||||||
sha: (merge_request.diff_head_commit.short_id if merge_request.diff_head_sha),
|
sha: (merge_request.diff_head_commit.short_id if merge_request.diff_head_sha),
|
||||||
status: status,
|
status: status,
|
||||||
coverage: coverage
|
coverage: coverage,
|
||||||
|
pipeline: pipeline.try(:id)
|
||||||
}
|
}
|
||||||
|
|
||||||
render json: response
|
render json: response
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
class Projects::SnippetsController < Projects::ApplicationController
|
class Projects::SnippetsController < Projects::ApplicationController
|
||||||
include ToggleAwardEmoji
|
include ToggleAwardEmoji
|
||||||
|
include SpammableActions
|
||||||
|
|
||||||
before_action :module_enabled
|
before_action :module_enabled
|
||||||
before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :toggle_award_emoji]
|
before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :toggle_award_emoji, :mark_as_spam]
|
||||||
|
|
||||||
# Allow read any snippet
|
# Allow read any snippet
|
||||||
before_action :authorize_read_project_snippet!, except: [:new, :create, :index]
|
before_action :authorize_read_project_snippet!, except: [:new, :create, :index]
|
||||||
|
@ -36,8 +37,8 @@ class Projects::SnippetsController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@snippet = CreateSnippetService.new(@project, current_user,
|
create_params = snippet_params.merge(request: request)
|
||||||
snippet_params).execute
|
@snippet = CreateSnippetService.new(@project, current_user, create_params).execute
|
||||||
|
|
||||||
if @snippet.valid?
|
if @snippet.valid?
|
||||||
respond_with(@snippet,
|
respond_with(@snippet,
|
||||||
|
@ -88,6 +89,7 @@ class Projects::SnippetsController < Projects::ApplicationController
|
||||||
@snippet ||= @project.snippets.find(params[:id])
|
@snippet ||= @project.snippets.find(params[:id])
|
||||||
end
|
end
|
||||||
alias_method :awardable, :snippet
|
alias_method :awardable, :snippet
|
||||||
|
alias_method :spammable, :snippet
|
||||||
|
|
||||||
def authorize_read_project_snippet!
|
def authorize_read_project_snippet!
|
||||||
return render_404 unless can?(current_user, :read_project_snippet, @snippet)
|
return render_404 unless can?(current_user, :read_project_snippet, @snippet)
|
||||||
|
|
|
@ -36,7 +36,7 @@ class Projects::UploadsController < Projects::ApplicationController
|
||||||
namespace = params[:namespace_id]
|
namespace = params[:namespace_id]
|
||||||
id = params[:project_id]
|
id = params[:project_id]
|
||||||
|
|
||||||
file_project = Project.find_with_namespace("#{namespace}/#{id}")
|
file_project = Project.find_by_full_path("#{namespace}/#{id}")
|
||||||
|
|
||||||
if file_project.nil?
|
if file_project.nil?
|
||||||
@uploader = nil
|
@uploader = nil
|
||||||
|
|
|
@ -231,12 +231,16 @@ class ProjectsController < Projects::ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def refs
|
def refs
|
||||||
|
branches = BranchesFinder.new(@repository, params).execute.map(&:name)
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
'Branches' => @repository.branch_names,
|
'Branches' => branches.take(100),
|
||||||
}
|
}
|
||||||
|
|
||||||
unless @repository.tag_count.zero?
|
unless @repository.tag_count.zero?
|
||||||
options['Tags'] = VersionSorter.rsort(@repository.tag_names)
|
tags = TagsFinder.new(@repository, params).execute.map(&:name)
|
||||||
|
|
||||||
|
options['Tags'] = tags.take(100)
|
||||||
end
|
end
|
||||||
|
|
||||||
# If reference is commit id - we should add it to branch/tag selectbox
|
# If reference is commit id - we should add it to branch/tag selectbox
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
class SnippetsController < ApplicationController
|
class SnippetsController < ApplicationController
|
||||||
include ToggleAwardEmoji
|
include ToggleAwardEmoji
|
||||||
|
include SpammableActions
|
||||||
|
|
||||||
before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :download]
|
before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :download]
|
||||||
|
|
||||||
|
@ -40,8 +41,8 @@ class SnippetsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@snippet = CreateSnippetService.new(nil, current_user,
|
create_params = snippet_params.merge(request: request)
|
||||||
snippet_params).execute
|
@snippet = CreateSnippetService.new(nil, current_user, create_params).execute
|
||||||
|
|
||||||
respond_with @snippet.becomes(Snippet)
|
respond_with @snippet.becomes(Snippet)
|
||||||
end
|
end
|
||||||
|
@ -96,6 +97,7 @@ class SnippetsController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
alias_method :awardable, :snippet
|
alias_method :awardable, :snippet
|
||||||
|
alias_method :spammable, :snippet
|
||||||
|
|
||||||
def authorize_read_snippet!
|
def authorize_read_snippet!
|
||||||
authenticate_user! unless can?(current_user, :read_personal_snippet, @snippet)
|
authenticate_user! unless can?(current_user, :read_personal_snippet, @snippet)
|
||||||
|
|
|
@ -37,7 +37,7 @@ module ApplicationHelper
|
||||||
if project_id.is_a?(Project)
|
if project_id.is_a?(Project)
|
||||||
project_id
|
project_id
|
||||||
else
|
else
|
||||||
Project.find_with_namespace(project_id)
|
Project.find_by_full_path(project_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
if project.avatar_url
|
if project.avatar_url
|
||||||
|
|
|
@ -21,7 +21,7 @@ module BlobHelper
|
||||||
options[:link_opts])
|
options[:link_opts])
|
||||||
|
|
||||||
if !on_top_of_branch?(project, ref)
|
if !on_top_of_branch?(project, ref)
|
||||||
button_tag "Edit", class: "btn disabled has-tooltip btn-file-option", title: "You can only edit files when you are on a branch", data: { container: 'body' }
|
button_tag "Edit", class: "btn disabled has-tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' }
|
||||||
elsif can_edit_blob?(blob, project, ref)
|
elsif can_edit_blob?(blob, project, ref)
|
||||||
link_to "Edit", edit_path, class: 'btn btn-sm'
|
link_to "Edit", edit_path, class: 'btn btn-sm'
|
||||||
elsif can?(current_user, :fork_project, project)
|
elsif can?(current_user, :fork_project, project)
|
||||||
|
@ -32,7 +32,7 @@ module BlobHelper
|
||||||
}
|
}
|
||||||
fork_path = namespace_project_forks_path(project.namespace, project, namespace_key: current_user.namespace.id, continue: continue_params)
|
fork_path = namespace_project_forks_path(project.namespace, project, namespace_key: current_user.namespace.id, continue: continue_params)
|
||||||
|
|
||||||
link_to "Edit", fork_path, class: 'btn btn-file-option', method: :post
|
link_to "Edit", fork_path, class: 'btn', method: :post
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -198,7 +198,7 @@ module CommitsHelper
|
||||||
link_to(
|
link_to(
|
||||||
namespace_project_blob_path(project.namespace, project,
|
namespace_project_blob_path(project.namespace, project,
|
||||||
tree_join(commit_sha, diff_new_path)),
|
tree_join(commit_sha, diff_new_path)),
|
||||||
class: 'btn view-file js-view-file btn-file-option'
|
class: 'btn view-file js-view-file'
|
||||||
) do
|
) do
|
||||||
raw('View file @') + content_tag(:span, commit_sha[0..6],
|
raw('View file @') + content_tag(:span, commit_sha[0..6],
|
||||||
class: 'commit-short-id')
|
class: 'commit-short-id')
|
||||||
|
|
|
@ -143,4 +143,16 @@ module MergeRequestsHelper
|
||||||
def different_base?(version1, version2)
|
def different_base?(version1, version2)
|
||||||
version1 && version2 && version1.base_commit_sha != version2.base_commit_sha
|
version1 && version2 && version1.base_commit_sha != version2.base_commit_sha
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def merge_params(merge_request)
|
||||||
|
{
|
||||||
|
merge_when_build_succeeds: true,
|
||||||
|
should_remove_source_branch: true,
|
||||||
|
sha: merge_request.diff_head_sha
|
||||||
|
}.merge(merge_params_ee(merge_request))
|
||||||
|
end
|
||||||
|
|
||||||
|
def merge_params_ee(merge_request)
|
||||||
|
{}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -93,10 +93,6 @@ module VisibilityLevelHelper
|
||||||
current_application_settings.default_project_visibility
|
current_application_settings.default_project_visibility
|
||||||
end
|
end
|
||||||
|
|
||||||
def default_snippet_visibility
|
|
||||||
current_application_settings.default_snippet_visibility
|
|
||||||
end
|
|
||||||
|
|
||||||
def default_group_visibility
|
def default_group_visibility
|
||||||
current_application_settings.default_group_visibility
|
current_application_settings.default_group_visibility
|
||||||
end
|
end
|
||||||
|
|
|
@ -275,29 +275,23 @@ module Ci
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_coverage
|
def update_coverage
|
||||||
return unless project
|
|
||||||
coverage_regex = project.build_coverage_regex
|
|
||||||
return unless coverage_regex
|
|
||||||
coverage = extract_coverage(trace, coverage_regex)
|
coverage = extract_coverage(trace, coverage_regex)
|
||||||
|
update_attributes(coverage: coverage) if coverage.present?
|
||||||
if coverage.is_a? Numeric
|
|
||||||
update_attributes(coverage: coverage)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def extract_coverage(text, regex)
|
def extract_coverage(text, regex)
|
||||||
begin
|
return unless regex
|
||||||
matches = text.scan(Regexp.new(regex)).last
|
|
||||||
matches = matches.last if matches.kind_of?(Array)
|
|
||||||
coverage = matches.gsub(/\d+(\.\d+)?/).first
|
|
||||||
|
|
||||||
if coverage.present?
|
matches = text.scan(Regexp.new(regex)).last
|
||||||
coverage.to_f
|
matches = matches.last if matches.kind_of?(Array)
|
||||||
end
|
coverage = matches.gsub(/\d+(\.\d+)?/).first
|
||||||
rescue
|
|
||||||
# if bad regex or something goes wrong we dont want to interrupt transition
|
if coverage.present?
|
||||||
# so we just silentrly ignore error for now
|
coverage.to_f
|
||||||
end
|
end
|
||||||
|
rescue
|
||||||
|
# if bad regex or something goes wrong we dont want to interrupt transition
|
||||||
|
# so we just silentrly ignore error for now
|
||||||
end
|
end
|
||||||
|
|
||||||
def has_trace_file?
|
def has_trace_file?
|
||||||
|
@ -523,6 +517,10 @@ module Ci
|
||||||
self.update(artifacts_expire_at: nil)
|
self.update(artifacts_expire_at: nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def coverage_regex
|
||||||
|
super || project.try(:build_coverage_regex)
|
||||||
|
end
|
||||||
|
|
||||||
def when
|
def when
|
||||||
read_attribute(:when) || build_attributes_from_config[:when] || 'on_success'
|
read_attribute(:when) || build_attributes_from_config[:when] || 'on_success'
|
||||||
end
|
end
|
||||||
|
|
|
@ -34,7 +34,13 @@ module Spammable
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_for_spam
|
def check_for_spam
|
||||||
self.errors.add(:base, "Your #{self.class.name.underscore} has been recognized as spam and has been discarded.") if spam?
|
if spam?
|
||||||
|
self.errors.add(:base, "Your #{spammable_entity_type} has been recognized as spam and has been discarded.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def spammable_entity_type
|
||||||
|
self.class.name.underscore
|
||||||
end
|
end
|
||||||
|
|
||||||
def spam_title
|
def spam_title
|
||||||
|
|
|
@ -169,7 +169,8 @@ class MergeRequestDiff < ActiveRecord::Base
|
||||||
# When compare merge request versions we want diff A..B instead of A...B
|
# When compare merge request versions we want diff A..B instead of A...B
|
||||||
# so we handle cases when user does squash and rebase of the commits between versions.
|
# so we handle cases when user does squash and rebase of the commits between versions.
|
||||||
# For this reason we set straight to true by default.
|
# For this reason we set straight to true by default.
|
||||||
CompareService.new.execute(project, head_commit_sha, project, sha, straight: straight)
|
CompareService.new(project, head_commit_sha)
|
||||||
|
.execute(project, sha, straight: straight)
|
||||||
end
|
end
|
||||||
|
|
||||||
def commits_count
|
def commits_count
|
||||||
|
|
|
@ -373,10 +373,6 @@ class Project < ActiveRecord::Base
|
||||||
def group_ids
|
def group_ids
|
||||||
joins(:namespace).where(namespaces: { type: 'Group' }).select(:namespace_id)
|
joins(:namespace).where(namespaces: { type: 'Group' }).select(:namespace_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add alias for Routable method for compatibility with old code.
|
|
||||||
# In future all calls `find_with_namespace` should be replaced with `find_by_full_path`
|
|
||||||
alias_method :find_with_namespace, :find_by_full_path
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def lfs_enabled?
|
def lfs_enabled?
|
||||||
|
@ -1395,6 +1391,6 @@ class Project < ActiveRecord::Base
|
||||||
def pending_delete_twin
|
def pending_delete_twin
|
||||||
return false unless path
|
return false unless path
|
||||||
|
|
||||||
Project.unscoped.where(pending_delete: true).find_with_namespace(path_with_namespace)
|
Project.unscoped.where(pending_delete: true).find_by_full_path(path_with_namespace)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -31,13 +31,13 @@ class ChatSlashCommandsService < Service
|
||||||
return unless valid_token?(params[:token])
|
return unless valid_token?(params[:token])
|
||||||
|
|
||||||
user = find_chat_user(params)
|
user = find_chat_user(params)
|
||||||
unless user
|
|
||||||
url = authorize_chat_name_url(params)
|
|
||||||
return presenter.authorize_chat_name(url)
|
|
||||||
end
|
|
||||||
|
|
||||||
Gitlab::ChatCommands::Command.new(project, user,
|
if user
|
||||||
params).execute
|
Gitlab::ChatCommands::Command.new(project, user, params).execute
|
||||||
|
else
|
||||||
|
url = authorize_chat_name_url(params)
|
||||||
|
Gitlab::ChatCommands::Presenters::Access.new(url).authorize
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -49,8 +49,4 @@ class ChatSlashCommandsService < Service
|
||||||
def authorize_chat_name_url(params)
|
def authorize_chat_name_url(params)
|
||||||
ChatNames::AuthorizeUserService.new(self, params).execute
|
ChatNames::AuthorizeUserService.new(self, params).execute
|
||||||
end
|
end
|
||||||
|
|
||||||
def presenter
|
|
||||||
Gitlab::ChatCommands::Presenter.new
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,4 +9,8 @@ class ProjectSnippet < Snippet
|
||||||
|
|
||||||
participant :author
|
participant :author
|
||||||
participant :notes_with_associations
|
participant :notes_with_associations
|
||||||
|
|
||||||
|
def check_for_spam?
|
||||||
|
super && project.public?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,7 +5,7 @@ class Repository
|
||||||
|
|
||||||
attr_accessor :path_with_namespace, :project
|
attr_accessor :path_with_namespace, :project
|
||||||
|
|
||||||
class CommitError < StandardError; end
|
CommitError = Class.new(StandardError)
|
||||||
|
|
||||||
# Methods that cache data from the Git repository.
|
# Methods that cache data from the Git repository.
|
||||||
#
|
#
|
||||||
|
@ -64,10 +64,6 @@ class Repository
|
||||||
@raw_repository ||= Gitlab::Git::Repository.new(path_to_repo)
|
@raw_repository ||= Gitlab::Git::Repository.new(path_to_repo)
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_autocrlf_option
|
|
||||||
raw_repository.autocrlf = :input if raw_repository.autocrlf != :input
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return absolute path to repository
|
# Return absolute path to repository
|
||||||
def path_to_repo
|
def path_to_repo
|
||||||
@path_to_repo ||= File.expand_path(
|
@path_to_repo ||= File.expand_path(
|
||||||
|
@ -168,63 +164,46 @@ class Repository
|
||||||
tags.find { |tag| tag.name == name }
|
tags.find { |tag| tag.name == name }
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_branch(user, branch_name, target)
|
def add_branch(user, branch_name, ref)
|
||||||
oldrev = Gitlab::Git::BLANK_SHA
|
newrev = commit(ref).try(:sha)
|
||||||
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name
|
|
||||||
target = commit(target).try(:id)
|
|
||||||
|
|
||||||
return false unless target
|
return false unless newrev
|
||||||
|
|
||||||
GitHooksService.new.execute(user, path_to_repo, oldrev, target, ref) do
|
GitOperationService.new(user, self).add_branch(branch_name, newrev)
|
||||||
update_ref!(ref, target, oldrev)
|
|
||||||
end
|
|
||||||
|
|
||||||
after_create_branch
|
after_create_branch
|
||||||
find_branch(branch_name)
|
find_branch(branch_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_tag(user, tag_name, target, message = nil)
|
def add_tag(user, tag_name, target, message = nil)
|
||||||
oldrev = Gitlab::Git::BLANK_SHA
|
newrev = commit(target).try(:id)
|
||||||
ref = Gitlab::Git::TAG_REF_PREFIX + tag_name
|
|
||||||
target = commit(target).try(:id)
|
|
||||||
|
|
||||||
return false unless target
|
|
||||||
|
|
||||||
options = { message: message, tagger: user_to_committer(user) } if message
|
options = { message: message, tagger: user_to_committer(user) } if message
|
||||||
|
|
||||||
GitHooksService.new.execute(user, path_to_repo, oldrev, target, ref) do |service|
|
return false unless newrev
|
||||||
raw_tag = rugged.tags.create(tag_name, target, options)
|
|
||||||
service.newrev = raw_tag.target_id
|
GitOperationService.new(user, self).add_tag(tag_name, newrev, options)
|
||||||
end
|
|
||||||
|
|
||||||
find_tag(tag_name)
|
find_tag(tag_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
def rm_branch(user, branch_name)
|
def rm_branch(user, branch_name)
|
||||||
before_remove_branch
|
before_remove_branch
|
||||||
|
|
||||||
branch = find_branch(branch_name)
|
branch = find_branch(branch_name)
|
||||||
oldrev = branch.try(:dereferenced_target).try(:id)
|
|
||||||
newrev = Gitlab::Git::BLANK_SHA
|
|
||||||
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name
|
|
||||||
|
|
||||||
GitHooksService.new.execute(user, path_to_repo, oldrev, newrev, ref) do
|
GitOperationService.new(user, self).rm_branch(branch)
|
||||||
update_ref!(ref, newrev, oldrev)
|
|
||||||
end
|
|
||||||
|
|
||||||
after_remove_branch
|
after_remove_branch
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def rm_tag(tag_name)
|
def rm_tag(user, tag_name)
|
||||||
before_remove_tag
|
before_remove_tag
|
||||||
|
tag = find_tag(tag_name)
|
||||||
|
|
||||||
begin
|
GitOperationService.new(user, self).rm_tag(tag)
|
||||||
rugged.tags.delete(tag_name)
|
|
||||||
true
|
after_remove_tag
|
||||||
rescue Rugged::ReferenceError
|
true
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def ref_names
|
def ref_names
|
||||||
|
@ -241,21 +220,6 @@ class Repository
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_ref!(name, newrev, oldrev)
|
|
||||||
# We use 'git update-ref' because libgit2/rugged currently does not
|
|
||||||
# offer 'compare and swap' ref updates. Without compare-and-swap we can
|
|
||||||
# (and have!) accidentally reset the ref to an earlier state, clobbering
|
|
||||||
# commits. See also https://github.com/libgit2/libgit2/issues/1534.
|
|
||||||
command = %W(#{Gitlab.config.git.bin_path} update-ref --stdin -z)
|
|
||||||
_, status = Gitlab::Popen.popen(command, path_to_repo) do |stdin|
|
|
||||||
stdin.write("update #{name}\x00#{newrev}\x00#{oldrev}\x00")
|
|
||||||
end
|
|
||||||
|
|
||||||
return if status.zero?
|
|
||||||
|
|
||||||
raise CommitError.new("Could not update branch #{name.sub('refs/heads/', '')}. Please refresh and try again.")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Makes sure a commit is kept around when Git garbage collection runs.
|
# Makes sure a commit is kept around when Git garbage collection runs.
|
||||||
# Git GC will delete commits from the repository that are no longer in any
|
# Git GC will delete commits from the repository that are no longer in any
|
||||||
# branches or tags, but we want to keep some of these commits around, for
|
# branches or tags, but we want to keep some of these commits around, for
|
||||||
|
@ -435,6 +399,11 @@ class Repository
|
||||||
repository_event(:remove_tag)
|
repository_event(:remove_tag)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Runs code after removing a tag.
|
||||||
|
def after_remove_tag
|
||||||
|
expire_tags_cache
|
||||||
|
end
|
||||||
|
|
||||||
def before_import
|
def before_import
|
||||||
expire_content_cache
|
expire_content_cache
|
||||||
end
|
end
|
||||||
|
@ -779,121 +748,132 @@ class Repository
|
||||||
@tags ||= raw_repository.tags
|
@tags ||= raw_repository.tags
|
||||||
end
|
end
|
||||||
|
|
||||||
def commit_dir(user, path, message, branch, author_email: nil, author_name: nil)
|
# rubocop:disable Metrics/ParameterLists
|
||||||
update_branch_with_hooks(user, branch) do |ref|
|
def commit_dir(
|
||||||
options = {
|
user, path,
|
||||||
commit: {
|
message:, branch_name:,
|
||||||
branch: ref,
|
author_email: nil, author_name: nil,
|
||||||
message: message,
|
start_branch_name: nil, start_project: project)
|
||||||
update_ref: false
|
check_tree_entry_for_dir(branch_name, path)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
options.merge!(get_committer_and_author(user, email: author_email, name: author_name))
|
if start_branch_name
|
||||||
|
start_project.repository.
|
||||||
raw_repository.mkdir(path, options)
|
check_tree_entry_for_dir(start_branch_name, path)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
commit_file(
|
||||||
|
user,
|
||||||
|
"#{path}/.gitkeep",
|
||||||
|
'',
|
||||||
|
message: message,
|
||||||
|
branch_name: branch_name,
|
||||||
|
update: false,
|
||||||
|
author_email: author_email,
|
||||||
|
author_name: author_name,
|
||||||
|
start_branch_name: start_branch_name,
|
||||||
|
start_project: start_project)
|
||||||
end
|
end
|
||||||
|
# rubocop:enable Metrics/ParameterLists
|
||||||
|
|
||||||
def commit_file(user, path, content, message, branch, update, author_email: nil, author_name: nil)
|
# rubocop:disable Metrics/ParameterLists
|
||||||
update_branch_with_hooks(user, branch) do |ref|
|
def commit_file(
|
||||||
options = {
|
user, path, content,
|
||||||
commit: {
|
message:, branch_name:, update: true,
|
||||||
branch: ref,
|
author_email: nil, author_name: nil,
|
||||||
message: message,
|
start_branch_name: nil, start_project: project)
|
||||||
update_ref: false
|
unless update
|
||||||
},
|
error_message = "Filename already exists; update not allowed"
|
||||||
file: {
|
|
||||||
content: content,
|
|
||||||
path: path,
|
|
||||||
update: update
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
options.merge!(get_committer_and_author(user, email: author_email, name: author_name))
|
if tree_entry_at(branch_name, path)
|
||||||
|
raise Gitlab::Git::Repository::InvalidBlobName.new(error_message)
|
||||||
|
end
|
||||||
|
|
||||||
Gitlab::Git::Blob.commit(raw_repository, options)
|
if start_branch_name &&
|
||||||
end
|
start_project.repository.tree_entry_at(start_branch_name, path)
|
||||||
end
|
raise Gitlab::Git::Repository::InvalidBlobName.new(error_message)
|
||||||
|
|
||||||
def update_file(user, path, content, branch:, previous_path:, message:, author_email: nil, author_name: nil)
|
|
||||||
update_branch_with_hooks(user, branch) do |ref|
|
|
||||||
options = {
|
|
||||||
commit: {
|
|
||||||
branch: ref,
|
|
||||||
message: message,
|
|
||||||
update_ref: false
|
|
||||||
},
|
|
||||||
file: {
|
|
||||||
content: content,
|
|
||||||
path: path,
|
|
||||||
update: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
options.merge!(get_committer_and_author(user, email: author_email, name: author_name))
|
|
||||||
|
|
||||||
if previous_path && previous_path != path
|
|
||||||
options[:file][:previous_path] = previous_path
|
|
||||||
Gitlab::Git::Blob.rename(raw_repository, options)
|
|
||||||
else
|
|
||||||
Gitlab::Git::Blob.commit(raw_repository, options)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
multi_action(
|
||||||
|
user: user,
|
||||||
|
message: message,
|
||||||
|
branch_name: branch_name,
|
||||||
|
author_email: author_email,
|
||||||
|
author_name: author_name,
|
||||||
|
start_branch_name: start_branch_name,
|
||||||
|
start_project: start_project,
|
||||||
|
actions: [{ action: :create,
|
||||||
|
file_path: path,
|
||||||
|
content: content }])
|
||||||
end
|
end
|
||||||
|
# rubocop:enable Metrics/ParameterLists
|
||||||
|
|
||||||
def remove_file(user, path, message, branch, author_email: nil, author_name: nil)
|
# rubocop:disable Metrics/ParameterLists
|
||||||
update_branch_with_hooks(user, branch) do |ref|
|
def update_file(
|
||||||
options = {
|
user, path, content,
|
||||||
commit: {
|
message:, branch_name:, previous_path:,
|
||||||
branch: ref,
|
author_email: nil, author_name: nil,
|
||||||
message: message,
|
start_branch_name: nil, start_project: project)
|
||||||
update_ref: false
|
action = if previous_path && previous_path != path
|
||||||
},
|
:move
|
||||||
file: {
|
else
|
||||||
path: path
|
:update
|
||||||
}
|
end
|
||||||
}
|
|
||||||
|
|
||||||
options.merge!(get_committer_and_author(user, email: author_email, name: author_name))
|
multi_action(
|
||||||
|
user: user,
|
||||||
Gitlab::Git::Blob.remove(raw_repository, options)
|
message: message,
|
||||||
end
|
branch_name: branch_name,
|
||||||
|
author_email: author_email,
|
||||||
|
author_name: author_name,
|
||||||
|
start_branch_name: start_branch_name,
|
||||||
|
start_project: start_project,
|
||||||
|
actions: [{ action: action,
|
||||||
|
file_path: path,
|
||||||
|
content: content,
|
||||||
|
previous_path: previous_path }])
|
||||||
end
|
end
|
||||||
|
# rubocop:enable Metrics/ParameterLists
|
||||||
|
|
||||||
def multi_action(user:, branch:, message:, actions:, author_email: nil, author_name: nil)
|
# rubocop:disable Metrics/ParameterLists
|
||||||
update_branch_with_hooks(user, branch) do |ref|
|
def remove_file(
|
||||||
|
user, path,
|
||||||
|
message:, branch_name:,
|
||||||
|
author_email: nil, author_name: nil,
|
||||||
|
start_branch_name: nil, start_project: project)
|
||||||
|
multi_action(
|
||||||
|
user: user,
|
||||||
|
message: message,
|
||||||
|
branch_name: branch_name,
|
||||||
|
author_email: author_email,
|
||||||
|
author_name: author_name,
|
||||||
|
start_branch_name: start_branch_name,
|
||||||
|
start_project: start_project,
|
||||||
|
actions: [{ action: :delete,
|
||||||
|
file_path: path }])
|
||||||
|
end
|
||||||
|
# rubocop:enable Metrics/ParameterLists
|
||||||
|
|
||||||
|
# rubocop:disable Metrics/ParameterLists
|
||||||
|
def multi_action(
|
||||||
|
user:, branch_name:, message:, actions:,
|
||||||
|
author_email: nil, author_name: nil,
|
||||||
|
start_branch_name: nil, start_project: project)
|
||||||
|
GitOperationService.new(user, self).with_branch(
|
||||||
|
branch_name,
|
||||||
|
start_branch_name: start_branch_name,
|
||||||
|
start_project: start_project) do |start_commit|
|
||||||
index = rugged.index
|
index = rugged.index
|
||||||
parents = []
|
|
||||||
branch = find_branch(ref)
|
|
||||||
|
|
||||||
if branch
|
parents = if start_commit
|
||||||
last_commit = branch.dereferenced_target
|
index.read_tree(start_commit.raw_commit.tree)
|
||||||
index.read_tree(last_commit.raw_commit.tree)
|
[start_commit.sha]
|
||||||
parents = [last_commit.sha]
|
else
|
||||||
end
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
actions.each do |action|
|
actions.each do |act|
|
||||||
case action[:action]
|
git_action(index, act)
|
||||||
when :create, :update, :move
|
|
||||||
mode =
|
|
||||||
case action[:action]
|
|
||||||
when :update
|
|
||||||
index.get(action[:file_path])[:mode]
|
|
||||||
when :move
|
|
||||||
index.get(action[:previous_path])[:mode]
|
|
||||||
end
|
|
||||||
mode ||= 0o100644
|
|
||||||
|
|
||||||
index.remove(action[:previous_path]) if action[:action] == :move
|
|
||||||
|
|
||||||
content = action[:encoding] == 'base64' ? Base64.decode64(action[:content]) : action[:content]
|
|
||||||
oid = rugged.write(content, :blob)
|
|
||||||
|
|
||||||
index.add(path: action[:file_path], oid: oid, mode: mode)
|
|
||||||
when :delete
|
|
||||||
index.remove(action[:file_path])
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
|
@ -906,6 +886,7 @@ class Repository
|
||||||
Rugged::Commit.create(rugged, options)
|
Rugged::Commit.create(rugged, options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
# rubocop:enable Metrics/ParameterLists
|
||||||
|
|
||||||
def get_committer_and_author(user, email: nil, name: nil)
|
def get_committer_and_author(user, email: nil, name: nil)
|
||||||
committer = user_to_committer(user)
|
committer = user_to_committer(user)
|
||||||
|
@ -918,7 +899,7 @@ class Repository
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_to_committer(user)
|
def user_to_committer(user)
|
||||||
Gitlab::Git::committer_hash(email: user.email, name: user.name)
|
Gitlab::Git.committer_hash(email: user.email, name: user.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_be_merged?(source_sha, target_branch)
|
def can_be_merged?(source_sha, target_branch)
|
||||||
|
@ -932,17 +913,18 @@ class Repository
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def merge(user, merge_request, options = {})
|
def merge(user, source, merge_request, options = {})
|
||||||
our_commit = rugged.branches[merge_request.target_branch].target
|
GitOperationService.new(user, self).with_branch(
|
||||||
their_commit = rugged.lookup(merge_request.diff_head_sha)
|
merge_request.target_branch) do |start_commit|
|
||||||
|
our_commit = start_commit.sha
|
||||||
|
their_commit = source
|
||||||
|
|
||||||
raise "Invalid merge target" if our_commit.nil?
|
raise 'Invalid merge target' unless our_commit
|
||||||
raise "Invalid merge source" if their_commit.nil?
|
raise 'Invalid merge source' unless their_commit
|
||||||
|
|
||||||
merge_index = rugged.merge_commits(our_commit, their_commit)
|
merge_index = rugged.merge_commits(our_commit, their_commit)
|
||||||
return false if merge_index.conflicts?
|
break if merge_index.conflicts?
|
||||||
|
|
||||||
update_branch_with_hooks(user, merge_request.target_branch) do
|
|
||||||
actual_options = options.merge(
|
actual_options = options.merge(
|
||||||
parents: [our_commit, their_commit],
|
parents: [our_commit, their_commit],
|
||||||
tree: merge_index.write_tree(rugged),
|
tree: merge_index.write_tree(rugged),
|
||||||
|
@ -952,34 +934,48 @@ class Repository
|
||||||
merge_request.update(in_progress_merge_commit_sha: commit_id)
|
merge_request.update(in_progress_merge_commit_sha: commit_id)
|
||||||
commit_id
|
commit_id
|
||||||
end
|
end
|
||||||
|
rescue Repository::CommitError # when merge_index.conflicts?
|
||||||
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
def revert(user, commit, base_branch, revert_tree_id = nil)
|
def revert(
|
||||||
source_sha = find_branch(base_branch).dereferenced_target.sha
|
user, commit, branch_name, revert_tree_id = nil,
|
||||||
revert_tree_id ||= check_revert_content(commit, base_branch)
|
start_branch_name: nil, start_project: project)
|
||||||
|
revert_tree_id ||= check_revert_content(commit, branch_name)
|
||||||
|
|
||||||
return false unless revert_tree_id
|
return false unless revert_tree_id
|
||||||
|
|
||||||
update_branch_with_hooks(user, base_branch) do
|
GitOperationService.new(user, self).with_branch(
|
||||||
|
branch_name,
|
||||||
|
start_branch_name: start_branch_name,
|
||||||
|
start_project: start_project) do |start_commit|
|
||||||
|
|
||||||
committer = user_to_committer(user)
|
committer = user_to_committer(user)
|
||||||
source_sha = Rugged::Commit.create(rugged,
|
|
||||||
|
Rugged::Commit.create(rugged,
|
||||||
message: commit.revert_message(user),
|
message: commit.revert_message(user),
|
||||||
author: committer,
|
author: committer,
|
||||||
committer: committer,
|
committer: committer,
|
||||||
tree: revert_tree_id,
|
tree: revert_tree_id,
|
||||||
parents: [rugged.lookup(source_sha)])
|
parents: [start_commit.sha])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def cherry_pick(user, commit, base_branch, cherry_pick_tree_id = nil)
|
def cherry_pick(
|
||||||
source_sha = find_branch(base_branch).dereferenced_target.sha
|
user, commit, branch_name, cherry_pick_tree_id = nil,
|
||||||
cherry_pick_tree_id ||= check_cherry_pick_content(commit, base_branch)
|
start_branch_name: nil, start_project: project)
|
||||||
|
cherry_pick_tree_id ||= check_cherry_pick_content(commit, branch_name)
|
||||||
|
|
||||||
return false unless cherry_pick_tree_id
|
return false unless cherry_pick_tree_id
|
||||||
|
|
||||||
update_branch_with_hooks(user, base_branch) do
|
GitOperationService.new(user, self).with_branch(
|
||||||
|
branch_name,
|
||||||
|
start_branch_name: start_branch_name,
|
||||||
|
start_project: start_project) do |start_commit|
|
||||||
|
|
||||||
committer = user_to_committer(user)
|
committer = user_to_committer(user)
|
||||||
source_sha = Rugged::Commit.create(rugged,
|
|
||||||
|
Rugged::Commit.create(rugged,
|
||||||
message: commit.message,
|
message: commit.message,
|
||||||
author: {
|
author: {
|
||||||
email: commit.author_email,
|
email: commit.author_email,
|
||||||
|
@ -988,22 +984,22 @@ class Repository
|
||||||
},
|
},
|
||||||
committer: committer,
|
committer: committer,
|
||||||
tree: cherry_pick_tree_id,
|
tree: cherry_pick_tree_id,
|
||||||
parents: [rugged.lookup(source_sha)])
|
parents: [start_commit.sha])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def resolve_conflicts(user, branch, params)
|
def resolve_conflicts(user, branch_name, params)
|
||||||
update_branch_with_hooks(user, branch) do
|
GitOperationService.new(user, self).with_branch(branch_name) do
|
||||||
committer = user_to_committer(user)
|
committer = user_to_committer(user)
|
||||||
|
|
||||||
Rugged::Commit.create(rugged, params.merge(author: committer, committer: committer))
|
Rugged::Commit.create(rugged, params.merge(author: committer, committer: committer))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_revert_content(commit, base_branch)
|
def check_revert_content(target_commit, branch_name)
|
||||||
source_sha = find_branch(base_branch).dereferenced_target.sha
|
source_sha = commit(branch_name).sha
|
||||||
args = [commit.id, source_sha]
|
args = [target_commit.sha, source_sha]
|
||||||
args << { mainline: 1 } if commit.merge_commit?
|
args << { mainline: 1 } if target_commit.merge_commit?
|
||||||
|
|
||||||
revert_index = rugged.revert_commit(*args)
|
revert_index = rugged.revert_commit(*args)
|
||||||
return false if revert_index.conflicts?
|
return false if revert_index.conflicts?
|
||||||
|
@ -1014,10 +1010,10 @@ class Repository
|
||||||
tree_id
|
tree_id
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_cherry_pick_content(commit, base_branch)
|
def check_cherry_pick_content(target_commit, branch_name)
|
||||||
source_sha = find_branch(base_branch).dereferenced_target.sha
|
source_sha = commit(branch_name).sha
|
||||||
args = [commit.id, source_sha]
|
args = [target_commit.sha, source_sha]
|
||||||
args << 1 if commit.merge_commit?
|
args << 1 if target_commit.merge_commit?
|
||||||
|
|
||||||
cherry_pick_index = rugged.cherrypick_commit(*args)
|
cherry_pick_index = rugged.cherrypick_commit(*args)
|
||||||
return false if cherry_pick_index.conflicts?
|
return false if cherry_pick_index.conflicts?
|
||||||
|
@ -1075,6 +1071,28 @@ class Repository
|
||||||
Gitlab::Popen.popen(args, path_to_repo).first.lines.map(&:strip)
|
Gitlab::Popen.popen(args, path_to_repo).first.lines.map(&:strip)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def with_repo_branch_commit(start_repository, start_branch_name)
|
||||||
|
branch_name_or_sha =
|
||||||
|
if start_repository == self
|
||||||
|
start_branch_name
|
||||||
|
else
|
||||||
|
tmp_ref = "refs/tmp/#{SecureRandom.hex}/head"
|
||||||
|
|
||||||
|
fetch_ref(
|
||||||
|
start_repository.path_to_repo,
|
||||||
|
"#{Gitlab::Git::BRANCH_REF_PREFIX}#{start_branch_name}",
|
||||||
|
tmp_ref
|
||||||
|
)
|
||||||
|
|
||||||
|
start_repository.commit(start_branch_name).sha
|
||||||
|
end
|
||||||
|
|
||||||
|
yield(commit(branch_name_or_sha))
|
||||||
|
|
||||||
|
ensure
|
||||||
|
rugged.references.delete(tmp_ref) if tmp_ref
|
||||||
|
end
|
||||||
|
|
||||||
def fetch_ref(source_path, source_ref, target_ref)
|
def fetch_ref(source_path, source_ref, target_ref)
|
||||||
args = %W(#{Gitlab.config.git.bin_path} fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref})
|
args = %W(#{Gitlab.config.git.bin_path} fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref})
|
||||||
Gitlab::Popen.popen(args, path_to_repo)
|
Gitlab::Popen.popen(args, path_to_repo)
|
||||||
|
@ -1084,39 +1102,6 @@ class Repository
|
||||||
fetch_ref(path_to_repo, ref, ref_path)
|
fetch_ref(path_to_repo, ref, ref_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_branch_with_hooks(current_user, branch)
|
|
||||||
update_autocrlf_option
|
|
||||||
|
|
||||||
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch
|
|
||||||
target_branch = find_branch(branch)
|
|
||||||
was_empty = empty?
|
|
||||||
|
|
||||||
# Make commit
|
|
||||||
newrev = yield(ref)
|
|
||||||
|
|
||||||
unless newrev
|
|
||||||
raise CommitError.new('Failed to create commit')
|
|
||||||
end
|
|
||||||
|
|
||||||
if rugged.lookup(newrev).parent_ids.empty? || target_branch.nil?
|
|
||||||
oldrev = Gitlab::Git::BLANK_SHA
|
|
||||||
else
|
|
||||||
oldrev = rugged.merge_base(newrev, target_branch.dereferenced_target.sha)
|
|
||||||
end
|
|
||||||
|
|
||||||
GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do
|
|
||||||
update_ref!(ref, newrev, oldrev)
|
|
||||||
|
|
||||||
if was_empty || !target_branch
|
|
||||||
# If repo was empty expire cache
|
|
||||||
after_create if was_empty
|
|
||||||
after_create_branch
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
newrev
|
|
||||||
end
|
|
||||||
|
|
||||||
def ls_files(ref)
|
def ls_files(ref)
|
||||||
actual_ref = ref || root_ref
|
actual_ref = ref || root_ref
|
||||||
raw_repository.ls_files(actual_ref)
|
raw_repository.ls_files(actual_ref)
|
||||||
|
@ -1175,8 +1160,76 @@ class Repository
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def tree_entry_at(branch_name, path)
|
||||||
|
branch_exists?(branch_name) &&
|
||||||
|
# tree_entry is private
|
||||||
|
raw_repository.send(:tree_entry, commit(branch_name), path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_tree_entry_for_dir(branch_name, path)
|
||||||
|
return unless branch_exists?(branch_name)
|
||||||
|
|
||||||
|
entry = tree_entry_at(branch_name, path)
|
||||||
|
|
||||||
|
return unless entry
|
||||||
|
|
||||||
|
if entry[:type] == :blob
|
||||||
|
raise Gitlab::Git::Repository::InvalidBlobName.new(
|
||||||
|
"Directory already exists as a file")
|
||||||
|
else
|
||||||
|
raise Gitlab::Git::Repository::InvalidBlobName.new(
|
||||||
|
"Directory already exists")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def git_action(index, action)
|
||||||
|
path = normalize_path(action[:file_path])
|
||||||
|
|
||||||
|
if action[:action] == :move
|
||||||
|
previous_path = normalize_path(action[:previous_path])
|
||||||
|
end
|
||||||
|
|
||||||
|
case action[:action]
|
||||||
|
when :create, :update, :move
|
||||||
|
mode =
|
||||||
|
case action[:action]
|
||||||
|
when :update
|
||||||
|
index.get(path)[:mode]
|
||||||
|
when :move
|
||||||
|
index.get(previous_path)[:mode]
|
||||||
|
end
|
||||||
|
mode ||= 0o100644
|
||||||
|
|
||||||
|
index.remove(previous_path) if action[:action] == :move
|
||||||
|
|
||||||
|
content = if action[:encoding] == 'base64'
|
||||||
|
Base64.decode64(action[:content])
|
||||||
|
else
|
||||||
|
action[:content]
|
||||||
|
end
|
||||||
|
|
||||||
|
oid = rugged.write(content, :blob)
|
||||||
|
|
||||||
|
index.add(path: path, oid: oid, mode: mode)
|
||||||
|
when :delete
|
||||||
|
index.remove(path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def normalize_path(path)
|
||||||
|
pathname = Gitlab::Git::PathHelper.normalize_path(path)
|
||||||
|
|
||||||
|
if pathname.each_filename.include?('..')
|
||||||
|
raise Gitlab::Git::Repository::InvalidBlobName.new('Invalid path')
|
||||||
|
end
|
||||||
|
|
||||||
|
pathname.to_s
|
||||||
|
end
|
||||||
|
|
||||||
def refs_directory_exists?
|
def refs_directory_exists?
|
||||||
return false unless path_with_namespace
|
return false unless path_with_namespace
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ class Snippet < ActiveRecord::Base
|
||||||
include Sortable
|
include Sortable
|
||||||
include Awardable
|
include Awardable
|
||||||
include Mentionable
|
include Mentionable
|
||||||
|
include Spammable
|
||||||
|
|
||||||
cache_markdown_field :title, pipeline: :single_line
|
cache_markdown_field :title, pipeline: :single_line
|
||||||
cache_markdown_field :content
|
cache_markdown_field :content
|
||||||
|
@ -17,7 +18,7 @@ class Snippet < ActiveRecord::Base
|
||||||
default_content_html_invalidator || file_name_changed?
|
default_content_html_invalidator || file_name_changed?
|
||||||
end
|
end
|
||||||
|
|
||||||
default_value_for :visibility_level, Snippet::PRIVATE
|
default_value_for(:visibility_level) { current_application_settings.default_snippet_visibility }
|
||||||
|
|
||||||
belongs_to :author, class_name: 'User'
|
belongs_to :author, class_name: 'User'
|
||||||
belongs_to :project
|
belongs_to :project
|
||||||
|
@ -46,6 +47,9 @@ class Snippet < ActiveRecord::Base
|
||||||
participant :author
|
participant :author
|
||||||
participant :notes_with_associations
|
participant :notes_with_associations
|
||||||
|
|
||||||
|
attr_spammable :title, spam_title: true
|
||||||
|
attr_spammable :content, spam_description: true
|
||||||
|
|
||||||
def self.reference_prefix
|
def self.reference_prefix
|
||||||
'$'
|
'$'
|
||||||
end
|
end
|
||||||
|
@ -127,6 +131,14 @@ class Snippet < ActiveRecord::Base
|
||||||
notes.includes(:author)
|
notes.includes(:author)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def check_for_spam?
|
||||||
|
public?
|
||||||
|
end
|
||||||
|
|
||||||
|
def spammable_entity_type
|
||||||
|
'snippet'
|
||||||
|
end
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
# Searches for snippets with a matching title or file name.
|
# Searches for snippets with a matching title or file name.
|
||||||
#
|
#
|
||||||
|
|
|
@ -61,7 +61,7 @@ module Auth
|
||||||
end
|
end
|
||||||
|
|
||||||
def process_repository_access(type, name, actions)
|
def process_repository_access(type, name, actions)
|
||||||
requested_project = Project.find_with_namespace(name)
|
requested_project = Project.find_by_full_path(name)
|
||||||
return unless requested_project
|
return unless requested_project
|
||||||
|
|
||||||
actions = actions.select do |action|
|
actions = actions.select do |action|
|
||||||
|
|
|
@ -4,7 +4,8 @@ module Commits
|
||||||
class ChangeError < StandardError; end
|
class ChangeError < StandardError; end
|
||||||
|
|
||||||
def execute
|
def execute
|
||||||
@source_project = params[:source_project] || @project
|
@start_project = params[:start_project] || @project
|
||||||
|
@start_branch = params[:start_branch]
|
||||||
@target_branch = params[:target_branch]
|
@target_branch = params[:target_branch]
|
||||||
@commit = params[:commit]
|
@commit = params[:commit]
|
||||||
@create_merge_request = params[:create_merge_request].present?
|
@create_merge_request = params[:create_merge_request].present?
|
||||||
|
@ -25,13 +26,28 @@ module Commits
|
||||||
def commit_change(action)
|
def commit_change(action)
|
||||||
raise NotImplementedError unless repository.respond_to?(action)
|
raise NotImplementedError unless repository.respond_to?(action)
|
||||||
|
|
||||||
into = @create_merge_request ? @commit.public_send("#{action}_branch_name") : @target_branch
|
if @create_merge_request
|
||||||
tree_id = repository.public_send("check_#{action}_content", @commit, @target_branch)
|
into = @commit.public_send("#{action}_branch_name")
|
||||||
|
tree_branch = @start_branch
|
||||||
|
else
|
||||||
|
into = tree_branch = @target_branch
|
||||||
|
end
|
||||||
|
|
||||||
|
tree_id = repository.public_send(
|
||||||
|
"check_#{action}_content", @commit, tree_branch)
|
||||||
|
|
||||||
if tree_id
|
if tree_id
|
||||||
create_target_branch(into) if @create_merge_request
|
validate_target_branch(into) if @create_merge_request
|
||||||
|
|
||||||
|
repository.public_send(
|
||||||
|
action,
|
||||||
|
current_user,
|
||||||
|
@commit,
|
||||||
|
into,
|
||||||
|
tree_id,
|
||||||
|
start_project: @start_project,
|
||||||
|
start_branch_name: @start_branch)
|
||||||
|
|
||||||
repository.public_send(action, current_user, @commit, into, tree_id)
|
|
||||||
success
|
success
|
||||||
else
|
else
|
||||||
error_msg = "Sorry, we cannot #{action.to_s.dasherize} this #{@commit.change_type_title(current_user)} automatically.
|
error_msg = "Sorry, we cannot #{action.to_s.dasherize} this #{@commit.change_type_title(current_user)} automatically.
|
||||||
|
@ -50,12 +66,12 @@ module Commits
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_target_branch(new_branch)
|
def validate_target_branch(new_branch)
|
||||||
# Temporary branch exists and contains the change commit
|
# Temporary branch exists and contains the change commit
|
||||||
return success if repository.find_branch(new_branch)
|
return if repository.find_branch(new_branch)
|
||||||
|
|
||||||
result = CreateBranchService.new(@project, current_user)
|
result = ValidateNewBranchService.new(@project, current_user)
|
||||||
.execute(new_branch, @target_branch, source_project: @source_project)
|
.execute(new_branch)
|
||||||
|
|
||||||
if result[:status] == :error
|
if result[:status] == :error
|
||||||
raise ChangeError, "There was an error creating the source branch: #{result[:message]}"
|
raise ChangeError, "There was an error creating the source branch: #{result[:message]}"
|
||||||
|
|
|
@ -3,23 +3,27 @@ require 'securerandom'
|
||||||
# Compare 2 branches for one repo or between repositories
|
# Compare 2 branches for one repo or between repositories
|
||||||
# and return Gitlab::Git::Compare object that responds to commits and diffs
|
# and return Gitlab::Git::Compare object that responds to commits and diffs
|
||||||
class CompareService
|
class CompareService
|
||||||
def execute(source_project, source_branch, target_project, target_branch, straight: false)
|
attr_reader :start_project, :start_branch_name
|
||||||
source_commit = source_project.commit(source_branch)
|
|
||||||
return unless source_commit
|
|
||||||
|
|
||||||
source_sha = source_commit.sha
|
def initialize(new_start_project, new_start_branch_name)
|
||||||
|
@start_project = new_start_project
|
||||||
|
@start_branch_name = new_start_branch_name
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute(target_project, target_branch, straight: false)
|
||||||
# If compare with other project we need to fetch ref first
|
# If compare with other project we need to fetch ref first
|
||||||
unless target_project == source_project
|
target_project.repository.with_repo_branch_commit(
|
||||||
random_string = SecureRandom.hex
|
start_project.repository,
|
||||||
|
start_branch_name) do |commit|
|
||||||
|
break unless commit
|
||||||
|
|
||||||
target_project.repository.fetch_ref(
|
compare(commit.sha, target_project, target_branch, straight)
|
||||||
source_project.repository.path_to_repo,
|
|
||||||
"refs/heads/#{source_branch}",
|
|
||||||
"refs/tmp/#{random_string}/head"
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def compare(source_sha, target_project, target_branch, straight)
|
||||||
raw_compare = Gitlab::Git::Compare.new(
|
raw_compare = Gitlab::Git::Compare.new(
|
||||||
target_project.repository.raw_repository,
|
target_project.repository.raw_repository,
|
||||||
target_branch,
|
target_branch,
|
||||||
|
|
|
@ -1,31 +1,11 @@
|
||||||
class CreateBranchService < BaseService
|
class CreateBranchService < BaseService
|
||||||
def execute(branch_name, ref, source_project: @project)
|
def execute(branch_name, ref)
|
||||||
valid_branch = Gitlab::GitRefValidator.validate(branch_name)
|
result = ValidateNewBranchService.new(project, current_user)
|
||||||
|
.execute(branch_name)
|
||||||
|
|
||||||
unless valid_branch
|
return result if result[:status] == :error
|
||||||
return error('Branch name is invalid')
|
|
||||||
end
|
|
||||||
|
|
||||||
repository = project.repository
|
new_branch = repository.add_branch(current_user, branch_name, ref)
|
||||||
existing_branch = repository.find_branch(branch_name)
|
|
||||||
|
|
||||||
if existing_branch
|
|
||||||
return error('Branch already exists')
|
|
||||||
end
|
|
||||||
|
|
||||||
new_branch = if source_project != @project
|
|
||||||
repository.fetch_ref(
|
|
||||||
source_project.repository.path_to_repo,
|
|
||||||
"refs/heads/#{ref}",
|
|
||||||
"refs/heads/#{branch_name}"
|
|
||||||
)
|
|
||||||
|
|
||||||
repository.after_create_branch
|
|
||||||
|
|
||||||
repository.find_branch(branch_name)
|
|
||||||
else
|
|
||||||
repository.add_branch(current_user, branch_name, ref)
|
|
||||||
end
|
|
||||||
|
|
||||||
if new_branch
|
if new_branch
|
||||||
success(new_branch)
|
success(new_branch)
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
class CreateSnippetService < BaseService
|
class CreateSnippetService < BaseService
|
||||||
def execute
|
def execute
|
||||||
|
request = params.delete(:request)
|
||||||
|
api = params.delete(:api)
|
||||||
|
|
||||||
snippet = if project
|
snippet = if project
|
||||||
project.snippets.build(params)
|
project.snippets.build(params)
|
||||||
else
|
else
|
||||||
|
@ -12,8 +15,12 @@ class CreateSnippetService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
snippet.author = current_user
|
snippet.author = current_user
|
||||||
|
snippet.spam = SpamService.new(snippet, request).check(api)
|
||||||
|
|
||||||
|
if snippet.save
|
||||||
|
UserAgentDetailService.new(snippet, request).create
|
||||||
|
end
|
||||||
|
|
||||||
snippet.save
|
|
||||||
snippet
|
snippet
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,7 +7,7 @@ class DeleteTagService < BaseService
|
||||||
return error('No such tag', 404)
|
return error('No such tag', 404)
|
||||||
end
|
end
|
||||||
|
|
||||||
if repository.rm_tag(tag_name)
|
if repository.rm_tag(current_user, tag_name)
|
||||||
release = project.releases.find_by(tag: tag_name)
|
release = project.releases.find_by(tag: tag_name)
|
||||||
release.destroy if release
|
release.destroy if release
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,9 @@ module Files
|
||||||
class ValidationError < StandardError; end
|
class ValidationError < StandardError; end
|
||||||
|
|
||||||
def execute
|
def execute
|
||||||
@source_project = params[:source_project] || @project
|
@start_project = params[:start_project] || @project
|
||||||
@source_branch = params[:source_branch]
|
@start_branch = params[:start_branch]
|
||||||
@target_branch = params[:target_branch]
|
@target_branch = params[:target_branch]
|
||||||
|
|
||||||
@commit_message = params[:commit_message]
|
@commit_message = params[:commit_message]
|
||||||
@file_path = params[:file_path]
|
@file_path = params[:file_path]
|
||||||
|
@ -22,10 +22,8 @@ module Files
|
||||||
# Validate parameters
|
# Validate parameters
|
||||||
validate
|
validate
|
||||||
|
|
||||||
# Create new branch if it different from source_branch
|
# Create new branch if it different from start_branch
|
||||||
if different_branch?
|
validate_target_branch if different_branch?
|
||||||
create_target_branch
|
|
||||||
end
|
|
||||||
|
|
||||||
result = commit
|
result = commit
|
||||||
if result
|
if result
|
||||||
|
@ -40,7 +38,7 @@ module Files
|
||||||
private
|
private
|
||||||
|
|
||||||
def different_branch?
|
def different_branch?
|
||||||
@source_branch != @target_branch || @source_project != @project
|
@start_branch != @target_branch || @start_project != @project
|
||||||
end
|
end
|
||||||
|
|
||||||
def file_has_changed?
|
def file_has_changed?
|
||||||
|
@ -61,22 +59,23 @@ module Files
|
||||||
end
|
end
|
||||||
|
|
||||||
unless project.empty_repo?
|
unless project.empty_repo?
|
||||||
unless @source_project.repository.branch_names.include?(@source_branch)
|
unless @start_project.repository.branch_exists?(@start_branch)
|
||||||
raise_error('You can only create or edit files when you are on a branch')
|
raise_error('You can only create or edit files when you are on a branch')
|
||||||
end
|
end
|
||||||
|
|
||||||
if different_branch?
|
if different_branch?
|
||||||
if repository.branch_names.include?(@target_branch)
|
if repository.branch_exists?(@target_branch)
|
||||||
raise_error('Branch with such name already exists. You need to switch to this branch in order to make changes')
|
raise_error('Branch with such name already exists. You need to switch to this branch in order to make changes')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_target_branch
|
def validate_target_branch
|
||||||
result = CreateBranchService.new(project, current_user).execute(@target_branch, @source_branch, source_project: @source_project)
|
result = ValidateNewBranchService.new(project, current_user).
|
||||||
|
execute(@target_branch)
|
||||||
|
|
||||||
unless result[:status] == :success
|
if result[:status] == :error
|
||||||
raise_error("Something went wrong when we tried to create #{@target_branch} for you: #{result[:message]}")
|
raise_error("Something went wrong when we tried to create #{@target_branch} for you: #{result[:message]}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
module Files
|
module Files
|
||||||
class CreateDirService < Files::BaseService
|
class CreateDirService < Files::BaseService
|
||||||
def commit
|
def commit
|
||||||
repository.commit_dir(current_user, @file_path, @commit_message, @target_branch, author_email: @author_email, author_name: @author_name)
|
repository.commit_dir(
|
||||||
|
current_user,
|
||||||
|
@file_path,
|
||||||
|
message: @commit_message,
|
||||||
|
branch_name: @target_branch,
|
||||||
|
author_email: @author_email,
|
||||||
|
author_name: @author_name,
|
||||||
|
start_project: @start_project,
|
||||||
|
start_branch_name: @start_branch)
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate
|
def validate
|
||||||
|
|
|
@ -1,7 +1,17 @@
|
||||||
module Files
|
module Files
|
||||||
class CreateService < Files::BaseService
|
class CreateService < Files::BaseService
|
||||||
def commit
|
def commit
|
||||||
repository.commit_file(current_user, @file_path, @file_content, @commit_message, @target_branch, false, author_email: @author_email, author_name: @author_name)
|
repository.commit_file(
|
||||||
|
current_user,
|
||||||
|
@file_path,
|
||||||
|
@file_content,
|
||||||
|
message: @commit_message,
|
||||||
|
branch_name: @target_branch,
|
||||||
|
update: false,
|
||||||
|
author_email: @author_email,
|
||||||
|
author_name: @author_name,
|
||||||
|
start_project: @start_project,
|
||||||
|
start_branch_name: @start_branch)
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate
|
def validate
|
||||||
|
@ -24,7 +34,7 @@ module Files
|
||||||
unless project.empty_repo?
|
unless project.empty_repo?
|
||||||
@file_path.slice!(0) if @file_path.start_with?('/')
|
@file_path.slice!(0) if @file_path.start_with?('/')
|
||||||
|
|
||||||
blob = repository.blob_at_branch(@source_branch, @file_path)
|
blob = repository.blob_at_branch(@start_branch, @file_path)
|
||||||
|
|
||||||
if blob
|
if blob
|
||||||
raise_error('Your changes could not be committed because a file with the same name already exists')
|
raise_error('Your changes could not be committed because a file with the same name already exists')
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
module Files
|
module Files
|
||||||
class DeleteService < Files::BaseService
|
class DeleteService < Files::BaseService
|
||||||
def commit
|
def commit
|
||||||
repository.remove_file(current_user, @file_path, @commit_message, @target_branch, author_email: @author_email, author_name: @author_name)
|
repository.remove_file(
|
||||||
|
current_user,
|
||||||
|
@file_path,
|
||||||
|
message: @commit_message,
|
||||||
|
branch_name: @target_branch,
|
||||||
|
author_email: @author_email,
|
||||||
|
author_name: @author_name,
|
||||||
|
start_project: @start_project,
|
||||||
|
start_branch_name: @start_branch)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,11 +5,13 @@ module Files
|
||||||
def commit
|
def commit
|
||||||
repository.multi_action(
|
repository.multi_action(
|
||||||
user: current_user,
|
user: current_user,
|
||||||
branch: @target_branch,
|
|
||||||
message: @commit_message,
|
message: @commit_message,
|
||||||
|
branch_name: @target_branch,
|
||||||
actions: params[:actions],
|
actions: params[:actions],
|
||||||
author_email: @author_email,
|
author_email: @author_email,
|
||||||
author_name: @author_name
|
author_name: @author_name,
|
||||||
|
start_project: @start_project,
|
||||||
|
start_branch_name: @start_branch
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -61,7 +63,7 @@ module Files
|
||||||
end
|
end
|
||||||
|
|
||||||
def last_commit
|
def last_commit
|
||||||
Gitlab::Git::Commit.last_for_path(repository, @source_branch, @file_path)
|
Gitlab::Git::Commit.last_for_path(repository, @start_branch, @file_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
def regex_check(file)
|
def regex_check(file)
|
||||||
|
|
|
@ -4,11 +4,13 @@ module Files
|
||||||
|
|
||||||
def commit
|
def commit
|
||||||
repository.update_file(current_user, @file_path, @file_content,
|
repository.update_file(current_user, @file_path, @file_content,
|
||||||
branch: @target_branch,
|
|
||||||
previous_path: @previous_path,
|
|
||||||
message: @commit_message,
|
message: @commit_message,
|
||||||
|
branch_name: @target_branch,
|
||||||
|
previous_path: @previous_path,
|
||||||
author_email: @author_email,
|
author_email: @author_email,
|
||||||
author_name: @author_name)
|
author_name: @author_name,
|
||||||
|
start_project: @start_project,
|
||||||
|
start_branch_name: @start_branch)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -23,7 +25,7 @@ module Files
|
||||||
|
|
||||||
def last_commit
|
def last_commit
|
||||||
@last_commit ||= Gitlab::Git::Commit.
|
@last_commit ||= Gitlab::Git::Commit.
|
||||||
last_for_path(@source_project.repository, @source_branch, @file_path)
|
last_for_path(@start_project.repository, @start_branch, @file_path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,9 +18,9 @@ class GitHooksService
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
yield self
|
yield(self).tap do
|
||||||
|
run_hook('post-receive')
|
||||||
run_hook('post-receive')
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -0,0 +1,179 @@
|
||||||
|
class GitOperationService
|
||||||
|
attr_reader :user, :repository
|
||||||
|
|
||||||
|
def initialize(new_user, new_repository)
|
||||||
|
@user = new_user
|
||||||
|
@repository = new_repository
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_branch(branch_name, newrev)
|
||||||
|
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name
|
||||||
|
oldrev = Gitlab::Git::BLANK_SHA
|
||||||
|
|
||||||
|
update_ref_in_hooks(ref, newrev, oldrev)
|
||||||
|
end
|
||||||
|
|
||||||
|
def rm_branch(branch)
|
||||||
|
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch.name
|
||||||
|
oldrev = branch.target
|
||||||
|
newrev = Gitlab::Git::BLANK_SHA
|
||||||
|
|
||||||
|
update_ref_in_hooks(ref, newrev, oldrev)
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_tag(tag_name, newrev, options = {})
|
||||||
|
ref = Gitlab::Git::TAG_REF_PREFIX + tag_name
|
||||||
|
oldrev = Gitlab::Git::BLANK_SHA
|
||||||
|
|
||||||
|
with_hooks(ref, newrev, oldrev) do |service|
|
||||||
|
# We want to pass the OID of the tag object to the hooks. For an
|
||||||
|
# annotated tag we don't know that OID until after the tag object
|
||||||
|
# (raw_tag) is created in the repository. That is why we have to
|
||||||
|
# update the value after creating the tag object. Only the
|
||||||
|
# "post-receive" hook will receive the correct value in this case.
|
||||||
|
raw_tag = repository.rugged.tags.create(tag_name, newrev, options)
|
||||||
|
service.newrev = raw_tag.target_id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def rm_tag(tag)
|
||||||
|
ref = Gitlab::Git::TAG_REF_PREFIX + tag.name
|
||||||
|
oldrev = tag.target
|
||||||
|
newrev = Gitlab::Git::BLANK_SHA
|
||||||
|
|
||||||
|
update_ref_in_hooks(ref, newrev, oldrev) do
|
||||||
|
repository.rugged.tags.delete(tag_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Whenever `start_branch_name` is passed, if `branch_name` doesn't exist,
|
||||||
|
# it would be created from `start_branch_name`.
|
||||||
|
# If `start_project` is passed, and the branch doesn't exist,
|
||||||
|
# it would try to find the commits from it instead of current repository.
|
||||||
|
def with_branch(
|
||||||
|
branch_name,
|
||||||
|
start_branch_name: nil,
|
||||||
|
start_project: repository.project,
|
||||||
|
&block)
|
||||||
|
|
||||||
|
check_with_branch_arguments!(
|
||||||
|
branch_name, start_branch_name, start_project)
|
||||||
|
|
||||||
|
update_branch_with_hooks(branch_name) do
|
||||||
|
repository.with_repo_branch_commit(
|
||||||
|
start_project.repository,
|
||||||
|
start_branch_name || branch_name,
|
||||||
|
&block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def update_branch_with_hooks(branch_name)
|
||||||
|
update_autocrlf_option
|
||||||
|
|
||||||
|
was_empty = repository.empty?
|
||||||
|
|
||||||
|
# Make commit
|
||||||
|
newrev = yield
|
||||||
|
|
||||||
|
unless newrev
|
||||||
|
raise Repository::CommitError.new('Failed to create commit')
|
||||||
|
end
|
||||||
|
|
||||||
|
branch = repository.find_branch(branch_name)
|
||||||
|
oldrev = find_oldrev_from_branch(newrev, branch)
|
||||||
|
|
||||||
|
ref = Gitlab::Git::BRANCH_REF_PREFIX + branch_name
|
||||||
|
update_ref_in_hooks(ref, newrev, oldrev)
|
||||||
|
|
||||||
|
# If repo was empty expire cache
|
||||||
|
repository.after_create if was_empty
|
||||||
|
repository.after_create_branch if
|
||||||
|
was_empty || Gitlab::Git.blank_ref?(oldrev)
|
||||||
|
|
||||||
|
newrev
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_oldrev_from_branch(newrev, branch)
|
||||||
|
return Gitlab::Git::BLANK_SHA unless branch
|
||||||
|
|
||||||
|
oldrev = branch.target
|
||||||
|
|
||||||
|
if oldrev == repository.rugged.merge_base(newrev, branch.target)
|
||||||
|
oldrev
|
||||||
|
else
|
||||||
|
raise Repository::CommitError.new('Branch diverged')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_ref_in_hooks(ref, newrev, oldrev)
|
||||||
|
with_hooks(ref, newrev, oldrev) do
|
||||||
|
update_ref(ref, newrev, oldrev)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def with_hooks(ref, newrev, oldrev)
|
||||||
|
GitHooksService.new.execute(
|
||||||
|
user,
|
||||||
|
repository.path_to_repo,
|
||||||
|
oldrev,
|
||||||
|
newrev,
|
||||||
|
ref) do |service|
|
||||||
|
|
||||||
|
yield(service)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_ref(ref, newrev, oldrev)
|
||||||
|
# We use 'git update-ref' because libgit2/rugged currently does not
|
||||||
|
# offer 'compare and swap' ref updates. Without compare-and-swap we can
|
||||||
|
# (and have!) accidentally reset the ref to an earlier state, clobbering
|
||||||
|
# commits. See also https://github.com/libgit2/libgit2/issues/1534.
|
||||||
|
command = %W[#{Gitlab.config.git.bin_path} update-ref --stdin -z]
|
||||||
|
_, status = Gitlab::Popen.popen(
|
||||||
|
command,
|
||||||
|
repository.path_to_repo) do |stdin|
|
||||||
|
stdin.write("update #{ref}\x00#{newrev}\x00#{oldrev}\x00")
|
||||||
|
end
|
||||||
|
|
||||||
|
unless status.zero?
|
||||||
|
raise Repository::CommitError.new(
|
||||||
|
"Could not update branch #{Gitlab::Git.branch_name(ref)}." \
|
||||||
|
" Please refresh and try again.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_autocrlf_option
|
||||||
|
if repository.raw_repository.autocrlf != :input
|
||||||
|
repository.raw_repository.autocrlf = :input
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_with_branch_arguments!(
|
||||||
|
branch_name, start_branch_name, start_project)
|
||||||
|
return if repository.branch_exists?(branch_name)
|
||||||
|
|
||||||
|
if repository.project != start_project
|
||||||
|
unless start_branch_name
|
||||||
|
raise ArgumentError,
|
||||||
|
'Should also pass :start_branch_name if' +
|
||||||
|
' :start_project is different from current project'
|
||||||
|
end
|
||||||
|
|
||||||
|
unless start_project.repository.branch_exists?(start_branch_name)
|
||||||
|
raise ArgumentError,
|
||||||
|
"Cannot find branch #{branch_name} nor" \
|
||||||
|
" #{start_branch_name} from" \
|
||||||
|
" #{start_project.path_with_namespace}"
|
||||||
|
end
|
||||||
|
elsif start_branch_name
|
||||||
|
unless repository.branch_exists?(start_branch_name)
|
||||||
|
raise ArgumentError,
|
||||||
|
"Cannot find branch #{branch_name} nor" \
|
||||||
|
" #{start_branch_name} from" \
|
||||||
|
" #{repository.project.path_with_namespace}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -47,9 +47,10 @@ module MergeRequests
|
||||||
end
|
end
|
||||||
|
|
||||||
def compare_branches
|
def compare_branches
|
||||||
compare = CompareService.new.execute(
|
compare = CompareService.new(
|
||||||
source_project,
|
source_project,
|
||||||
source_branch,
|
source_branch
|
||||||
|
).execute(
|
||||||
target_project,
|
target_project,
|
||||||
target_branch
|
target_branch
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,13 +6,17 @@ module MergeRequests
|
||||||
# Executed when you do merge via GitLab UI
|
# Executed when you do merge via GitLab UI
|
||||||
#
|
#
|
||||||
class MergeService < MergeRequests::BaseService
|
class MergeService < MergeRequests::BaseService
|
||||||
attr_reader :merge_request
|
attr_reader :merge_request, :source
|
||||||
|
|
||||||
def execute(merge_request)
|
def execute(merge_request)
|
||||||
@merge_request = merge_request
|
@merge_request = merge_request
|
||||||
|
|
||||||
return log_merge_error('Merge request is not mergeable', true) unless @merge_request.mergeable?
|
return log_merge_error('Merge request is not mergeable', true) unless @merge_request.mergeable?
|
||||||
|
|
||||||
|
@source = find_merge_source
|
||||||
|
|
||||||
|
return log_merge_error('No source for merge', true) unless @source
|
||||||
|
|
||||||
merge_request.in_locked_state do
|
merge_request.in_locked_state do
|
||||||
if commit
|
if commit
|
||||||
after_merge
|
after_merge
|
||||||
|
@ -34,7 +38,7 @@ module MergeRequests
|
||||||
committer: committer
|
committer: committer
|
||||||
}
|
}
|
||||||
|
|
||||||
commit_id = repository.merge(current_user, merge_request, options)
|
commit_id = repository.merge(current_user, source, merge_request, options)
|
||||||
|
|
||||||
if commit_id
|
if commit_id
|
||||||
merge_request.update(merge_commit_sha: commit_id)
|
merge_request.update(merge_commit_sha: commit_id)
|
||||||
|
@ -73,9 +77,11 @@ module MergeRequests
|
||||||
end
|
end
|
||||||
|
|
||||||
def merge_request_info
|
def merge_request_info
|
||||||
project = merge_request.project
|
merge_request.to_reference(full: true)
|
||||||
|
end
|
||||||
|
|
||||||
"#{project.to_reference}#{merge_request.to_reference}"
|
def find_merge_source
|
||||||
|
merge_request.diff_head_sha
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
require_relative 'base_service'
|
||||||
|
|
||||||
|
class ValidateNewBranchService < BaseService
|
||||||
|
def execute(branch_name)
|
||||||
|
valid_branch = Gitlab::GitRefValidator.validate(branch_name)
|
||||||
|
|
||||||
|
unless valid_branch
|
||||||
|
return error('Branch name is invalid')
|
||||||
|
end
|
||||||
|
|
||||||
|
repository = project.repository
|
||||||
|
existing_branch = repository.find_branch(branch_name)
|
||||||
|
|
||||||
|
if existing_branch
|
||||||
|
return error('Branch already exists')
|
||||||
|
end
|
||||||
|
|
||||||
|
success
|
||||||
|
rescue GitHooksService::PreReceiveError => ex
|
||||||
|
error(ex.message)
|
||||||
|
end
|
||||||
|
end
|
|
@ -212,7 +212,7 @@
|
||||||
.col-sm-10
|
.col-sm-10
|
||||||
= f.number_field :max_artifacts_size, class: 'form-control'
|
= f.number_field :max_artifacts_size, class: 'form-control'
|
||||||
.help-block
|
.help-block
|
||||||
Set the maximum file size each build's artifacts can have
|
Set the maximum file size each jobs's artifacts can have
|
||||||
= link_to "(?)", help_page_path("user/admin_area/settings/continuous_integration", anchor: "maximum-artifacts-size")
|
= link_to "(?)", help_page_path("user/admin_area/settings/continuous_integration", anchor: "maximum-artifacts-size")
|
||||||
|
|
||||||
- if Gitlab.config.registry.enabled
|
- if Gitlab.config.registry.enabled
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
= link_to 'Cancel all', cancel_all_admin_builds_path, data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
|
= link_to 'Cancel all', cancel_all_admin_builds_path, data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
|
||||||
|
|
||||||
.row-content-block.second-block
|
.row-content-block.second-block
|
||||||
#{(@scope || 'all').capitalize} builds
|
#{(@scope || 'all').capitalize} jobs
|
||||||
|
|
||||||
%ul.content-list.builds-content-list.admin-builds-table
|
%ul.content-list.builds-content-list.admin-builds-table
|
||||||
= render "projects/builds/table", builds: @builds, admin: true
|
= render "projects/builds/table", builds: @builds, admin: true
|
||||||
|
|
|
@ -20,9 +20,9 @@
|
||||||
%span
|
%span
|
||||||
Groups
|
Groups
|
||||||
= nav_link path: 'builds#index' do
|
= nav_link path: 'builds#index' do
|
||||||
= link_to admin_builds_path, title: 'Builds' do
|
= link_to admin_builds_path, title: 'Jobs' do
|
||||||
%span
|
%span
|
||||||
Builds
|
Jobs
|
||||||
= nav_link path: ['runners#index', 'runners#show'] do
|
= nav_link path: ['runners#index', 'runners#show'] do
|
||||||
= link_to admin_runners_path, title: 'Runners' do
|
= link_to admin_runners_path, title: 'Runners' do
|
||||||
%span
|
%span
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
.bs-callout
|
.bs-callout
|
||||||
%p
|
%p
|
||||||
A 'Runner' is a process which runs a build.
|
A 'Runner' is a process which runs a job.
|
||||||
You can setup as many Runners as you need.
|
You can setup as many Runners as you need.
|
||||||
%br
|
%br
|
||||||
Runners can be placed on separate users, servers, even on your local machine.
|
Runners can be placed on separate users, servers, even on your local machine.
|
||||||
|
@ -37,16 +37,16 @@
|
||||||
%ul
|
%ul
|
||||||
%li
|
%li
|
||||||
%span.label.label-success shared
|
%span.label.label-success shared
|
||||||
\- Runner runs builds from all unassigned projects
|
\- Runner runs jobs from all unassigned projects
|
||||||
%li
|
%li
|
||||||
%span.label.label-info specific
|
%span.label.label-info specific
|
||||||
\- Runner runs builds from assigned projects
|
\- Runner runs jobs from assigned projects
|
||||||
%li
|
%li
|
||||||
%span.label.label-warning locked
|
%span.label.label-warning locked
|
||||||
\- Runner cannot be assigned to other projects
|
\- Runner cannot be assigned to other projects
|
||||||
%li
|
%li
|
||||||
%span.label.label-danger paused
|
%span.label.label-danger paused
|
||||||
\- Runner will not receive any new builds
|
\- Runner will not receive any new jobs
|
||||||
|
|
||||||
.append-bottom-20.clearfix
|
.append-bottom-20.clearfix
|
||||||
.pull-left
|
.pull-left
|
||||||
|
@ -68,7 +68,7 @@
|
||||||
%th Runner token
|
%th Runner token
|
||||||
%th Description
|
%th Description
|
||||||
%th Projects
|
%th Projects
|
||||||
%th Builds
|
%th Jobs
|
||||||
%th Tags
|
%th Tags
|
||||||
%th Last contact
|
%th Last contact
|
||||||
%th
|
%th
|
||||||
|
|
|
@ -11,13 +11,13 @@
|
||||||
|
|
||||||
- if @runner.shared?
|
- if @runner.shared?
|
||||||
.bs-callout.bs-callout-success
|
.bs-callout.bs-callout-success
|
||||||
%h4 This Runner will process builds from ALL UNASSIGNED projects
|
%h4 This Runner will process jobs from ALL UNASSIGNED projects
|
||||||
%p
|
%p
|
||||||
If you want Runners to build only specific projects, enable them in the table below.
|
If you want Runners to build only specific projects, enable them in the table below.
|
||||||
Keep in mind that this is a one way transition.
|
Keep in mind that this is a one way transition.
|
||||||
- else
|
- else
|
||||||
.bs-callout.bs-callout-info
|
.bs-callout.bs-callout-info
|
||||||
%h4 This Runner will process builds only from ASSIGNED projects
|
%h4 This Runner will process jobs only from ASSIGNED projects
|
||||||
%p You can't make this a shared Runner.
|
%p You can't make this a shared Runner.
|
||||||
%hr
|
%hr
|
||||||
|
|
||||||
|
@ -70,11 +70,11 @@
|
||||||
= paginate @projects, theme: "gitlab"
|
= paginate @projects, theme: "gitlab"
|
||||||
|
|
||||||
.col-md-6
|
.col-md-6
|
||||||
%h4 Recent builds served by this Runner
|
%h4 Recent jobs served by this Runner
|
||||||
%table.table.ci-table.runner-builds
|
%table.table.ci-table.runner-builds
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
%th Build
|
%th Job
|
||||||
%th Status
|
%th Status
|
||||||
%th Project
|
%th Project
|
||||||
%th Commit
|
%th Commit
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
.col-md-4.col-lg-6
|
.col-md-4.col-lg-6
|
||||||
= users_select_tag(:user_ids, multiple: true, class: 'input-clamp', scope: :all, email_user: true)
|
= users_select_tag(:user_ids, multiple: true, class: 'input-clamp', scope: :all, email_user: true)
|
||||||
.help-block.append-bottom-10
|
.help-block.append-bottom-10
|
||||||
Search for users by name, username, or email, or invite new ones using their email address.
|
Search for members by name, username, or email, or invite new ones using their email address.
|
||||||
|
|
||||||
.col-md-3.col-lg-2
|
.col-md-3.col-lg-2
|
||||||
= select_tag :access_level, options_for_select(GroupMember.access_level_roles, @group_member.access_level), class: "form-control project-access-select"
|
= select_tag :access_level, options_for_select(GroupMember.access_level_roles, @group_member.access_level), class: "form-control project-access-select"
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
= text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Expiration date'
|
= text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Expiration date'
|
||||||
%i.clear-icon.js-clear-input
|
%i.clear-icon.js-clear-input
|
||||||
.help-block.append-bottom-10
|
.help-block.append-bottom-10
|
||||||
On this date, the user(s) will automatically lose access to this group and all of its projects.
|
On this date, the member(s) will automatically lose access to this group and all of its projects.
|
||||||
|
|
||||||
.col-md-2
|
.col-md-2
|
||||||
= f.submit 'Add to group', class: "btn btn-create btn-block"
|
= f.submit 'Add to group', class: "btn btn-create btn-block"
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
- if can?(current_user, :admin_group_member, @group)
|
- if can?(current_user, :admin_group_member, @group)
|
||||||
.project-members-new.append-bottom-default
|
.project-members-new.append-bottom-default
|
||||||
%p.clearfix
|
%p.clearfix
|
||||||
Add new user to
|
Add new member to
|
||||||
%strong= @group.name
|
%strong= @group.name
|
||||||
= render "new_group_member"
|
= render "new_group_member"
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
.append-bottom-default.clearfix
|
.append-bottom-default.clearfix
|
||||||
%h5.member.existing-title
|
%h5.member.existing-title
|
||||||
Existing users
|
Existing members
|
||||||
= form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do
|
= form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do
|
||||||
.form-group
|
.form-group
|
||||||
= search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false }
|
= search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false }
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
= render 'shared/members/sort_dropdown'
|
= render 'shared/members/sort_dropdown'
|
||||||
.panel.panel-default
|
.panel.panel-default
|
||||||
.panel-heading
|
.panel-heading
|
||||||
Users with access to
|
Members with access to
|
||||||
%strong= @group.name
|
%strong= @group.name
|
||||||
%span.badge= @members.total_count
|
%span.badge= @members.total_count
|
||||||
%ul.content-list
|
%ul.content-list
|
||||||
|
|
|
@ -143,7 +143,7 @@
|
||||||
.key g
|
.key g
|
||||||
.key b
|
.key b
|
||||||
%td
|
%td
|
||||||
Go to builds
|
Go to jobs
|
||||||
%tr
|
%tr
|
||||||
%td.shortcut
|
%td.shortcut
|
||||||
.key g
|
.key g
|
||||||
|
|
|
@ -96,8 +96,8 @@
|
||||||
-# Shortcut to builds page
|
-# Shortcut to builds page
|
||||||
- if project_nav_tab? :builds
|
- if project_nav_tab? :builds
|
||||||
%li.hidden
|
%li.hidden
|
||||||
= link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds' do
|
= link_to project_builds_path(@project), title: 'Jobs', class: 'shortcuts-builds' do
|
||||||
Builds
|
Jobs
|
||||||
|
|
||||||
-# Shortcut to commits page
|
-# Shortcut to commits page
|
||||||
- if project_nav_tab? :commits
|
- if project_nav_tab? :commits
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
- content_for :header do
|
- content_for :header do
|
||||||
%h1{ style: "background: #c40834; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;" }
|
%h1{ style: "background: #c40834; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;" }
|
||||||
GitLab (build failed)
|
GitLab (job failed)
|
||||||
|
|
||||||
%h3
|
%h3
|
||||||
Project:
|
Project:
|
||||||
|
@ -21,4 +21,4 @@
|
||||||
Message: #{@build.pipeline.git_commit_message}
|
Message: #{@build.pipeline.git_commit_message}
|
||||||
|
|
||||||
%p
|
%p
|
||||||
Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.project.namespace, @build.project, @build)}
|
Job details: #{link_to "Job #{@build.id}", namespace_project_build_url(@build.project.namespace, @build.project, @build)}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
Build failed for <%= @project.name %>
|
Job failed for <%= @project.name %>
|
||||||
|
|
||||||
Status: <%= @build.status %>
|
Status: <%= @build.status %>
|
||||||
Commit: <%= @build.pipeline.short_sha %>
|
Commit: <%= @build.pipeline.short_sha %>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
- content_for :header do
|
- content_for :header do
|
||||||
%h1{ style: "background: #38CF5B; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;" }
|
%h1{ style: "background: #38CF5B; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;" }
|
||||||
GitLab (build successful)
|
GitLab (job successful)
|
||||||
|
|
||||||
%h3
|
%h3
|
||||||
Project:
|
Project:
|
||||||
|
@ -21,4 +21,4 @@
|
||||||
Message: #{@build.pipeline.git_commit_message}
|
Message: #{@build.pipeline.git_commit_message}
|
||||||
|
|
||||||
%p
|
%p
|
||||||
Build details: #{link_to "Build #{@build.id}", namespace_project_build_url(@build.project.namespace, @build.project, @build)}
|
Job details: #{link_to "Job #{@build.id}", namespace_project_build_url(@build.project.namespace, @build.project, @build)}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
Build successful for <%= @project.name %>
|
Job successful for <%= @project.name %>
|
||||||
|
|
||||||
Status: <%= @build.status %>
|
Status: <%= @build.status %>
|
||||||
Commit: <%= @build.pipeline.short_sha %>
|
Commit: <%= @build.pipeline.short_sha %>
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Build #<%= build.id %> ( <%= pipeline_build_url(pipeline, build) %> )
|
Job #<%= build.id %> ( <%= pipeline_build_url(pipeline, build) %> )
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Build #<%= build.id %>
|
Job #<%= build.id %>
|
||||||
|
|
|
@ -3,6 +3,6 @@
|
||||||
%h4
|
%h4
|
||||||
Customize your workflow!
|
Customize your workflow!
|
||||||
%p
|
%p
|
||||||
Get started with GitLab by enabling features that work best for your project. From issues and wikis, to merge requests and builds, GitLab can help manage your workflow from idea to production!
|
Get started with GitLab by enabling features that work best for your project. From issues and wikis, to merge requests and pipelines, GitLab can help manage your workflow from idea to production!
|
||||||
- if can?(current_user, :admin_project, @project)
|
- if can?(current_user, :admin_project, @project)
|
||||||
= link_to "Get started", edit_project_path(@project), class: "btn btn-success"
|
= link_to "Get started", edit_project_path(@project), class: "btn btn-success"
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
.checkbox.builds-feature
|
.checkbox.builds-feature
|
||||||
= form.label :only_allow_merge_if_build_succeeds do
|
= form.label :only_allow_merge_if_build_succeeds do
|
||||||
= form.check_box :only_allow_merge_if_build_succeeds
|
= form.check_box :only_allow_merge_if_build_succeeds
|
||||||
%strong Only allow merge requests to be merged if the build succeeds
|
%strong Only allow merge requests to be merged if the pipeline succeeds
|
||||||
%br
|
%br
|
||||||
%span.descr
|
%span.descr
|
||||||
Builds need to be configured to enable this feature.
|
Pipelines need to be configured to enable this feature.
|
||||||
= link_to icon('question-circle'), help_page_path('user/project/merge_requests/merge_when_pipeline_succeeds', anchor: 'only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds')
|
= link_to icon('question-circle'), help_page_path('user/project/merge_requests/merge_when_pipeline_succeeds', anchor: 'only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds')
|
||||||
.checkbox
|
.checkbox
|
||||||
= form.label :only_allow_merge_if_all_discussions_are_resolved do
|
= form.label :only_allow_merge_if_all_discussions_are_resolved do
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
- page_title 'Artifacts', "#{@build.name} (##{@build.id})", 'Builds'
|
- page_title 'Artifacts', "#{@build.name} (##{@build.id})", 'Jobs'
|
||||||
|
|
||||||
.top-block.row-content-block.clearfix
|
.top-block.row-content-block.clearfix
|
||||||
.pull-right
|
.pull-right
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
.content-block.build-header
|
.content-block.build-header
|
||||||
.header-content
|
.header-content
|
||||||
= render 'ci/status/badge', status: @build.detailed_status(current_user), link: false
|
= render 'ci/status/badge', status: @build.detailed_status(current_user), link: false
|
||||||
Build
|
Job
|
||||||
%strong.js-build-id ##{@build.id}
|
%strong.js-build-id ##{@build.id}
|
||||||
in pipeline
|
in pipeline
|
||||||
= link_to pipeline_path(@build.pipeline) do
|
= link_to pipeline_path(@build.pipeline) do
|
||||||
|
@ -17,6 +17,6 @@
|
||||||
= render "user"
|
= render "user"
|
||||||
= time_ago_with_tooltip(@build.created_at)
|
= time_ago_with_tooltip(@build.created_at)
|
||||||
- if can?(current_user, :update_build, @build) && @build.retryable?
|
- if can?(current_user, :update_build, @build) && @build.retryable?
|
||||||
= link_to "Retry build", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-inverted-secondary pull-right', method: :post
|
= link_to "Retry job", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-inverted-secondary pull-right', method: :post
|
||||||
%button.btn.btn-default.pull-right.visible-xs-block.visible-sm-block.build-gutter-toggle.js-sidebar-build-toggle{ role: "button", type: "button" }
|
%button.btn.btn-default.pull-right.visible-xs-block.visible-sm-block.build-gutter-toggle.js-sidebar-build-toggle{ role: "button", type: "button" }
|
||||||
= icon('angle-double-left')
|
= icon('angle-double-left')
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
%aside.right-sidebar.right-sidebar-expanded.build-sidebar.js-build-sidebar
|
%aside.right-sidebar.right-sidebar-expanded.build-sidebar.js-build-sidebar
|
||||||
.block.build-sidebar-header.visible-xs-block.visible-sm-block.append-bottom-default
|
.block.build-sidebar-header.visible-xs-block.visible-sm-block.append-bottom-default
|
||||||
Build
|
Job
|
||||||
%strong ##{@build.id}
|
%strong ##{@build.id}
|
||||||
%a.gutter-toggle.pull-right.js-sidebar-build-toggle{ href: "#" }
|
%a.gutter-toggle.pull-right.js-sidebar-build-toggle{ href: "#" }
|
||||||
= icon('angle-double-right')
|
= icon('angle-double-right')
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
- if can?(current_user, :read_build, @project) && (@build.artifacts? || @build.artifacts_expired?)
|
- if can?(current_user, :read_build, @project) && (@build.artifacts? || @build.artifacts_expired?)
|
||||||
.block{ class: ("block-first" if !@build.coverage) }
|
.block{ class: ("block-first" if !@build.coverage) }
|
||||||
.title
|
.title
|
||||||
Build artifacts
|
Job artifacts
|
||||||
- if @build.artifacts_expired?
|
- if @build.artifacts_expired?
|
||||||
%p.build-detail-row
|
%p.build-detail-row
|
||||||
The artifacts were removed
|
The artifacts were removed
|
||||||
|
@ -42,9 +42,9 @@
|
||||||
|
|
||||||
.block{ class: ("block-first" if !@build.coverage && !(can?(current_user, :read_build, @project) && (@build.artifacts? || @build.artifacts_expired?))) }
|
.block{ class: ("block-first" if !@build.coverage && !(can?(current_user, :read_build, @project) && (@build.artifacts? || @build.artifacts_expired?))) }
|
||||||
.title
|
.title
|
||||||
Build details
|
Job details
|
||||||
- if can?(current_user, :update_build, @build) && @build.retryable?
|
- if can?(current_user, :update_build, @build) && @build.retryable?
|
||||||
= link_to "Retry build", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'pull-right retry-link', method: :post
|
= link_to "Retry job", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'pull-right retry-link', method: :post
|
||||||
- if @build.merge_request
|
- if @build.merge_request
|
||||||
%p.build-detail-row
|
%p.build-detail-row
|
||||||
%span.build-light-text Merge Request:
|
%span.build-light-text Merge Request:
|
||||||
|
@ -136,4 +136,4 @@
|
||||||
- else
|
- else
|
||||||
= build.id
|
= build.id
|
||||||
- if build.retried?
|
- if build.retried?
|
||||||
%i.fa.fa-refresh.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Build was retried' }
|
%i.fa.fa-refresh.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Job was retried' }
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
|
|
||||||
- if builds.blank?
|
- if builds.blank?
|
||||||
%div
|
%div
|
||||||
.nothing-here-block No builds to show
|
.nothing-here-block No jobs to show
|
||||||
- else
|
- else
|
||||||
.table-holder
|
.table-holder
|
||||||
%table.table.ci-table.builds-page
|
%table.table.ci-table.builds-page
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
%th Status
|
%th Status
|
||||||
%th Build
|
%th Job
|
||||||
%th Pipeline
|
%th Pipeline
|
||||||
- if admin
|
- if admin
|
||||||
%th Project
|
%th Project
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
- @no_container = true
|
- @no_container = true
|
||||||
- page_title "Builds"
|
- page_title "Jobs"
|
||||||
= render "projects/pipelines/head"
|
= render "projects/pipelines/head"
|
||||||
|
|
||||||
%div{ class: container_class }
|
%div{ class: container_class }
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
|
data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
|
||||||
|
|
||||||
- unless @repository.gitlab_ci_yml
|
- unless @repository.gitlab_ci_yml
|
||||||
= link_to 'Get started with Builds', help_page_path('ci/quick_start/README'), class: 'btn btn-info'
|
= link_to 'Get started with CI/CD Pipelines', help_page_path('ci/quick_start/README'), class: 'btn btn-info'
|
||||||
|
|
||||||
= link_to ci_lint_path, class: 'btn btn-default' do
|
= link_to ci_lint_path, class: 'btn btn-default' do
|
||||||
%span CI Lint
|
%span CI Lint
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
- @no_container = true
|
- @no_container = true
|
||||||
- page_title "#{@build.name} (##{@build.id})", "Builds"
|
- page_title "#{@build.name} (##{@build.id})", "Jobs"
|
||||||
- trace_with_state = @build.trace_with_state
|
- trace_with_state = @build.trace_with_state
|
||||||
= render "projects/pipelines/head", build_subnav: true
|
= render "projects/pipelines/head", build_subnav: true
|
||||||
|
|
||||||
|
@ -12,14 +12,14 @@
|
||||||
.bs-callout.bs-callout-warning
|
.bs-callout.bs-callout-warning
|
||||||
%p
|
%p
|
||||||
- if no_runners_for_project?(@build.project)
|
- if no_runners_for_project?(@build.project)
|
||||||
This build is stuck, because the project doesn't have any runners online assigned to it.
|
This job is stuck, because the project doesn't have any runners online assigned to it.
|
||||||
- elsif @build.tags.any?
|
- elsif @build.tags.any?
|
||||||
This build is stuck, because you don't have any active runners online with any of these tags assigned to them:
|
This job is stuck, because you don't have any active runners online with any of these tags assigned to them:
|
||||||
- @build.tags.each do |tag|
|
- @build.tags.each do |tag|
|
||||||
%span.label.label-primary
|
%span.label.label-primary
|
||||||
= tag
|
= tag
|
||||||
- else
|
- else
|
||||||
This build is stuck, because you don't have any active runners that can run this build.
|
This job is stuck, because you don't have any active runners that can run this job.
|
||||||
|
|
||||||
%br
|
%br
|
||||||
Go to
|
Go to
|
||||||
|
@ -37,14 +37,14 @@
|
||||||
- environment = environment_for_build(@build.project, @build)
|
- environment = environment_for_build(@build.project, @build)
|
||||||
- if @build.success? && @build.last_deployment.present?
|
- if @build.success? && @build.last_deployment.present?
|
||||||
- if @build.last_deployment.last?
|
- if @build.last_deployment.last?
|
||||||
This build is the most recent deployment to #{environment_link_for_build(@build.project, @build)}.
|
This job is the most recent deployment to #{environment_link_for_build(@build.project, @build)}.
|
||||||
- else
|
- else
|
||||||
This build is an out-of-date deployment to #{environment_link_for_build(@build.project, @build)}.
|
This job is an out-of-date deployment to #{environment_link_for_build(@build.project, @build)}.
|
||||||
View the most recent deployment #{deployment_link(environment.last_deployment)}.
|
View the most recent deployment #{deployment_link(environment.last_deployment)}.
|
||||||
- elsif @build.complete? && !@build.success?
|
- elsif @build.complete? && !@build.success?
|
||||||
The deployment of this build to #{environment_link_for_build(@build.project, @build)} did not succeed.
|
The deployment of this job to #{environment_link_for_build(@build.project, @build)} did not succeed.
|
||||||
- else
|
- else
|
||||||
This build is creating a deployment to #{environment_link_for_build(@build.project, @build)}
|
This job is creating a deployment to #{environment_link_for_build(@build.project, @build)}
|
||||||
- if environment.try(:last_deployment)
|
- if environment.try(:last_deployment)
|
||||||
and will overwrite the #{deployment_link(environment.last_deployment, text: 'latest deployment')}
|
and will overwrite the #{deployment_link(environment.last_deployment, text: 'latest deployment')}
|
||||||
|
|
||||||
|
@ -52,9 +52,9 @@
|
||||||
- if @build.erased?
|
- if @build.erased?
|
||||||
.erased.alert.alert-warning
|
.erased.alert.alert-warning
|
||||||
- if @build.erased_by_user?
|
- if @build.erased_by_user?
|
||||||
Build has been erased by #{link_to(@build.erased_by_name, user_path(@build.erased_by))} #{time_ago_with_tooltip(@build.erased_at)}
|
Job has been erased by #{link_to(@build.erased_by_name, user_path(@build.erased_by))} #{time_ago_with_tooltip(@build.erased_at)}
|
||||||
- else
|
- else
|
||||||
Build has been erased #{time_ago_with_tooltip(@build.erased_at)}
|
Job has been erased #{time_ago_with_tooltip(@build.erased_at)}
|
||||||
- else
|
- else
|
||||||
#js-build-scroll.scroll-controls
|
#js-build-scroll.scroll-controls
|
||||||
.scroll-step
|
.scroll-step
|
||||||
|
|
|
@ -32,10 +32,10 @@
|
||||||
= link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "commit-id monospace"
|
= link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "commit-id monospace"
|
||||||
|
|
||||||
- if build.stuck?
|
- if build.stuck?
|
||||||
= icon('warning', class: 'text-warning has-tooltip', title: 'Build is stuck. Check runners.')
|
= icon('warning', class: 'text-warning has-tooltip', title: 'Job is stuck. Check runners.')
|
||||||
|
|
||||||
- if retried
|
- if retried
|
||||||
= icon('refresh', class: 'text-warning has-tooltip', title: 'Build was retried')
|
= icon('refresh', class: 'text-warning has-tooltip', title: 'Job was retried')
|
||||||
|
|
||||||
.label-container
|
.label-container
|
||||||
- if build.tags.any?
|
- if build.tags.any?
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
- else
|
- else
|
||||||
%span.api.monospace API
|
%span.api.monospace API
|
||||||
- if pipeline.latest?
|
- if pipeline.latest?
|
||||||
%span.label.label-success.has-tooltip{ title: 'Latest build for this branch' } latest
|
%span.label.label-success.has-tooltip{ title: 'Latest job for this branch' } latest
|
||||||
- if pipeline.triggered?
|
- if pipeline.triggered?
|
||||||
%span.label.label-primary triggered
|
%span.label.label-primary triggered
|
||||||
- if pipeline.yaml_errors.present?
|
- if pipeline.yaml_errors.present?
|
||||||
|
@ -78,7 +78,7 @@
|
||||||
.btn-group.inline
|
.btn-group.inline
|
||||||
- if actions.any?
|
- if actions.any?
|
||||||
.btn-group
|
.btn-group
|
||||||
%button.dropdown-toggle.btn.btn-default.has-tooltip.js-pipeline-dropdown-manual-actions{ type: 'button', title: 'Manual build', data: { toggle: 'dropdown', placement: 'top' }, 'aria-label' => 'Manual build' }
|
%button.dropdown-toggle.btn.btn-default.has-tooltip.js-pipeline-dropdown-manual-actions{ type: 'button', title: 'Manual job', data: { toggle: 'dropdown', placement: 'top' }, 'aria-label' => 'Manual job' }
|
||||||
= custom_icon('icon_play')
|
= custom_icon('icon_play')
|
||||||
= icon('caret-down', 'aria-hidden' => 'true')
|
= icon('caret-down', 'aria-hidden' => 'true')
|
||||||
%ul.dropdown-menu.dropdown-menu-align-right
|
%ul.dropdown-menu.dropdown-menu-align-right
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
Pipeline
|
Pipeline
|
||||||
= link_to "##{pipeline.id}", namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: "monospace"
|
= link_to "##{pipeline.id}", namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: "monospace"
|
||||||
with
|
with
|
||||||
= pluralize pipeline.statuses.count(:id), "build"
|
= pluralize pipeline.statuses.count(:id), "job"
|
||||||
- if pipeline.ref
|
- if pipeline.ref
|
||||||
for
|
for
|
||||||
= link_to pipeline.ref, namespace_project_commits_path(pipeline.project.namespace, pipeline.project, pipeline.ref), class: "monospace"
|
= link_to pipeline.ref, namespace_project_commits_path(pipeline.project.namespace, pipeline.project, pipeline.ref), class: "monospace"
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
%th Status
|
%th Status
|
||||||
%th Build ID
|
%th Job ID
|
||||||
%th Name
|
%th Name
|
||||||
%th
|
%th
|
||||||
- if pipeline.project.build_coverage_enabled?
|
- if pipeline.project.build_coverage_enabled?
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
- unless diff_file.submodule?
|
- unless diff_file.submodule?
|
||||||
.file-actions.hidden-xs
|
.file-actions.hidden-xs
|
||||||
- if blob_text_viewable?(blob)
|
- if blob_text_viewable?(blob)
|
||||||
= link_to '#', class: 'js-toggle-diff-comments btn active has-tooltip btn-file-option', title: "Toggle comments for this file", disabled: @diff_notes_disabled do
|
= link_to '#', class: 'js-toggle-diff-comments btn active has-tooltip', title: "Toggle comments for this file", disabled: @diff_notes_disabled do
|
||||||
= icon('comment')
|
= icon('comment')
|
||||||
\
|
\
|
||||||
- if editable_diff?(diff_file)
|
- if editable_diff?(diff_file)
|
||||||
|
|
|
@ -63,7 +63,7 @@
|
||||||
|
|
||||||
.row
|
.row
|
||||||
.col-md-9.project-feature.nested
|
.col-md-9.project-feature.nested
|
||||||
= feature_fields.label :builds_access_level, "Builds", class: 'label-light'
|
= feature_fields.label :builds_access_level, "Pipelines", class: 'label-light'
|
||||||
%span.help-block Submit, test and deploy your changes before merge
|
%span.help-block Submit, test and deploy your changes before merge
|
||||||
.col-md-3
|
.col-md-3
|
||||||
= project_feature_access_select(:builds_access_level)
|
= project_feature_access_select(:builds_access_level)
|
||||||
|
@ -181,13 +181,13 @@
|
||||||
%p
|
%p
|
||||||
The following items will NOT be exported:
|
The following items will NOT be exported:
|
||||||
%ul
|
%ul
|
||||||
%li Build traces and artifacts
|
%li Job traces and artifacts
|
||||||
%li LFS objects
|
%li LFS objects
|
||||||
%li Container registry images
|
%li Container registry images
|
||||||
%li CI variables
|
%li CI variables
|
||||||
%li Any encrypted tokens
|
%li Any encrypted tokens
|
||||||
%hr
|
|
||||||
- if can? current_user, :archive_project, @project
|
- if can? current_user, :archive_project, @project
|
||||||
|
%hr
|
||||||
.row.prepend-top-default
|
.row.prepend-top-default
|
||||||
.col-lg-3
|
.col-lg-3
|
||||||
%h4.warning-title.prepend-top-0
|
%h4.warning-title.prepend-top-0
|
||||||
|
|
|
@ -32,8 +32,8 @@
|
||||||
%tr
|
%tr
|
||||||
%th ID
|
%th ID
|
||||||
%th Commit
|
%th Commit
|
||||||
%th Build
|
%th Job
|
||||||
%th
|
%th Created
|
||||||
%th.hidden-xs
|
%th.hidden-xs
|
||||||
|
|
||||||
= render @deployments
|
= render @deployments
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
%h4 Build charts
|
%h4 Pipelines charts
|
||||||
%p
|
%p
|
||||||
|
|
||||||
%span.cgreen
|
%span.cgreen
|
||||||
|
@ -11,19 +11,19 @@
|
||||||
|
|
||||||
.prepend-top-default
|
.prepend-top-default
|
||||||
%p.light
|
%p.light
|
||||||
Builds for last week
|
Jobs for last week
|
||||||
(#{date_from_to(Date.today - 7.days, Date.today)})
|
(#{date_from_to(Date.today - 7.days, Date.today)})
|
||||||
%canvas#weekChart{ height: 200 }
|
%canvas#weekChart{ height: 200 }
|
||||||
|
|
||||||
.prepend-top-default
|
.prepend-top-default
|
||||||
%p.light
|
%p.light
|
||||||
Builds for last month
|
Jobs for last month
|
||||||
(#{date_from_to(Date.today - 30.days, Date.today)})
|
(#{date_from_to(Date.today - 30.days, Date.today)})
|
||||||
%canvas#monthChart{ height: 200 }
|
%canvas#monthChart{ height: 200 }
|
||||||
|
|
||||||
.prepend-top-default
|
.prepend-top-default
|
||||||
%p.light
|
%p.light
|
||||||
Builds for last year
|
Jobs for last year
|
||||||
%canvas#yearChart.padded{ height: 250 }
|
%canvas#yearChart.padded{ height: 250 }
|
||||||
|
|
||||||
- [:week, :month, :year].each do |scope|
|
- [:week, :month, :year].each do |scope|
|
||||||
|
|
|
@ -8,5 +8,5 @@
|
||||||
'@click' => "onClickResolveModeButton(file, 'edit')",
|
'@click' => "onClickResolveModeButton(file, 'edit')",
|
||||||
type: 'button' }
|
type: 'button' }
|
||||||
Edit inline
|
Edit inline
|
||||||
%a.btn.view-file.btn-file-option{ ":href" => "file.blobPath" }
|
%a.btn.view-file{ ":href" => "file.blobPath" }
|
||||||
View file @{{conflictsData.shortCommitSha}}
|
View file @{{conflictsData.shortCommitSha}}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
= ci_label_for_status(status)
|
= ci_label_for_status(status)
|
||||||
for
|
for
|
||||||
= succeed "." do
|
= succeed "." do
|
||||||
= link_to @pipeline.short_sha, namespace_project_commit_path(@merge_request.source_project.namespace, @merge_request.source_project, @pipeline.sha), class: "monospace"
|
= link_to @pipeline.short_sha, namespace_project_commit_path(@merge_request.source_project.namespace, @merge_request.source_project, @pipeline.sha), class: "monospace js-commit-link"
|
||||||
%span.ci-coverage
|
%span.ci-coverage
|
||||||
|
|
||||||
- elsif @merge_request.has_ci?
|
- elsif @merge_request.has_ci?
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
.ci_widget{ class: "ci-#{status} ci-status-icon-#{status}", style: "display:none" }
|
.ci_widget{ class: "ci-#{status} ci-status-icon-#{status}", style: "display:none" }
|
||||||
= ci_icon_for_status(status)
|
= ci_icon_for_status(status)
|
||||||
%span
|
%span
|
||||||
CI build
|
CI job
|
||||||
= ci_label_for_status(status)
|
= ci_label_for_status(status)
|
||||||
for
|
for
|
||||||
- commit = @merge_request.diff_head_commit
|
- commit = @merge_request.diff_head_commit
|
||||||
|
|
|
@ -16,14 +16,18 @@
|
||||||
gitlab_icon: "#{asset_path 'gitlab_logo.png'}",
|
gitlab_icon: "#{asset_path 'gitlab_logo.png'}",
|
||||||
ci_status: "#{@merge_request.head_pipeline ? @merge_request.head_pipeline.status : ''}",
|
ci_status: "#{@merge_request.head_pipeline ? @merge_request.head_pipeline.status : ''}",
|
||||||
ci_message: {
|
ci_message: {
|
||||||
normal: "Build {{status}} for \"{{title}}\"",
|
normal: "Job {{status}} for \"{{title}}\"",
|
||||||
preparing: "{{status}} build for \"{{title}}\""
|
preparing: "{{status}} job for \"{{title}}\""
|
||||||
},
|
},
|
||||||
ci_enable: #{@project.ci_service ? "true" : "false"},
|
ci_enable: #{@project.ci_service ? "true" : "false"},
|
||||||
ci_title: {
|
ci_title: {
|
||||||
preparing: "{{status}} build",
|
preparing: "{{status}} job",
|
||||||
normal: "Build {{status}}"
|
normal: "Job {{status}}"
|
||||||
},
|
},
|
||||||
|
ci_sha: "#{@merge_request.head_pipeline ? @merge_request.head_pipeline.short_sha : ''}",
|
||||||
|
ci_pipeline: #{@merge_request.head_pipeline.try(:id).to_json},
|
||||||
|
commits_path: "#{project_commits_path(@project)}",
|
||||||
|
pipeline_path: "#{project_pipelines_path(@project)}",
|
||||||
pipelines_path: "#{pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}"
|
pipelines_path: "#{pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -35,10 +35,10 @@
|
||||||
The source branch will be removed.
|
The source branch will be removed.
|
||||||
- elsif @merge_request.can_remove_source_branch?(current_user)
|
- elsif @merge_request.can_remove_source_branch?(current_user)
|
||||||
.accept-control.checkbox
|
.accept-control.checkbox
|
||||||
= label_tag :should_remove_source_branch, class: "remove_source_checkbox" do
|
= label_tag :should_remove_source_branch, class: "merge-param-checkbox" do
|
||||||
= check_box_tag :should_remove_source_branch
|
= check_box_tag :should_remove_source_branch
|
||||||
Remove source branch
|
Remove source branch
|
||||||
.accept-control.right
|
.accept-control
|
||||||
= link_to "#", class: "modify-merge-commit-link js-toggle-button" do
|
= link_to "#", class: "modify-merge-commit-link js-toggle-button" do
|
||||||
= icon('edit')
|
= icon('edit')
|
||||||
Modify commit message
|
Modify commit message
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
%h4
|
%h4
|
||||||
= icon('exclamation-triangle')
|
= icon('exclamation-triangle')
|
||||||
The build for this merge request failed
|
The job for this merge request failed
|
||||||
|
|
||||||
%p
|
%p
|
||||||
Please retry the build or push a new commit to fix the failure.
|
Please retry the job or push a new commit to fix the failure.
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue