Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into orderable-issues
This commit is contained in:
commit
e097cf43da
233 changed files with 3149 additions and 1281 deletions
|
@ -23,6 +23,7 @@ AllCops:
|
|||
- 'tmp/**/*'
|
||||
- 'bin/**/*'
|
||||
- 'generator_templates/**/*'
|
||||
- 'builds/**/*'
|
||||
|
||||
# Gems in consecutive lines should be alphabetically sorted
|
||||
Bundler/OrderedGems:
|
||||
|
|
21
CHANGELOG.md
21
CHANGELOG.md
|
@ -2,6 +2,21 @@
|
|||
documentation](doc/development/changelog.md) for instructions on adding your own
|
||||
entry.
|
||||
|
||||
## 8.17.1 (2017-02-28)
|
||||
|
||||
- Replace setInterval with setTimeout to prevent highly frequent requests. !9271 (Takuya Noguchi)
|
||||
- Disable unused tags count cache for Projects, Builds and Runners.
|
||||
- Spam check and reCAPTCHA improvements.
|
||||
- Allow searching issues for strings containing colons.
|
||||
- Disabled tooltip on add issues button in usse boards.
|
||||
- Fixed commit search UI.
|
||||
- Fix MR changes tab size count when there are over 100 files in the diff.
|
||||
- Disable invalid service templates.
|
||||
- Use default branch as target_branch when parameter is missing.
|
||||
- Upgrade GitLab Pages to v0.3.2.
|
||||
- Add performance query regression fix for !9088 affecting #27267.
|
||||
- Chat slash commands show labels correctly.
|
||||
|
||||
## 8.17.0 (2017-02-22)
|
||||
|
||||
- API: Fix file downloading. !0 (8267)
|
||||
|
@ -182,6 +197,12 @@ entry.
|
|||
- Remove deprecated GitlabCiService.
|
||||
- Requeue pending deletion projects.
|
||||
|
||||
## 8.16.7 (2017-02-27)
|
||||
|
||||
- No changes.
|
||||
- No changes.
|
||||
- Fix MR changes tab size count when there are over 100 files in the diff.
|
||||
|
||||
## 8.16.6 (2017-02-17)
|
||||
|
||||
- API: Fix file downloading. !0 (8267)
|
||||
|
|
2
Gemfile
2
Gemfile
|
@ -68,7 +68,7 @@ gem 'gollum-rugged_adapter', '~> 0.4.2', require: false
|
|||
gem 'github-linguist', '~> 4.7.0', require: 'linguist'
|
||||
|
||||
# API
|
||||
gem 'grape', '~> 0.18.0'
|
||||
gem 'grape', '~> 0.19.0'
|
||||
gem 'grape-entity', '~> 0.6.0'
|
||||
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
|
||||
|
||||
|
|
14
Gemfile.lock
14
Gemfile.lock
|
@ -304,7 +304,7 @@ GEM
|
|||
multi_json (~> 1.11)
|
||||
os (~> 0.9)
|
||||
signet (~> 0.7)
|
||||
grape (0.18.0)
|
||||
grape (0.19.1)
|
||||
activesupport
|
||||
builder
|
||||
hashie (>= 2.1.0)
|
||||
|
@ -353,8 +353,8 @@ GEM
|
|||
json (~> 1.8)
|
||||
multi_xml (>= 0.5.2)
|
||||
httpclient (2.8.2)
|
||||
i18n (0.8.0)
|
||||
ice_nine (0.11.1)
|
||||
i18n (0.8.1)
|
||||
ice_nine (0.11.2)
|
||||
influxdb (0.2.3)
|
||||
cause
|
||||
json
|
||||
|
@ -417,7 +417,7 @@ GEM
|
|||
minitest (5.7.0)
|
||||
mousetrap-rails (1.4.6)
|
||||
multi_json (1.12.1)
|
||||
multi_xml (0.5.5)
|
||||
multi_xml (0.6.0)
|
||||
multipart-post (2.0.0)
|
||||
mustermann (0.4.0)
|
||||
tool (~> 0.2)
|
||||
|
@ -758,7 +758,7 @@ GEM
|
|||
eventmachine (~> 1.0, >= 1.0.4)
|
||||
rack (>= 1, < 3)
|
||||
thor (0.19.4)
|
||||
thread_safe (0.3.5)
|
||||
thread_safe (0.3.6)
|
||||
tilt (2.0.6)
|
||||
timecop (0.8.1)
|
||||
timfel-krb5-auth (0.8.3)
|
||||
|
@ -886,7 +886,7 @@ DEPENDENCIES
|
|||
gollum-rugged_adapter (~> 0.4.2)
|
||||
gon (~> 6.1.0)
|
||||
google-api-client (~> 0.8.6)
|
||||
grape (~> 0.18.0)
|
||||
grape (~> 0.19.0)
|
||||
grape-entity (~> 0.6.0)
|
||||
haml_lint (~> 0.21.0)
|
||||
hamlit (~> 2.6.1)
|
||||
|
@ -1011,4 +1011,4 @@ DEPENDENCIES
|
|||
wikicloth (= 0.8.1)
|
||||
|
||||
BUNDLED WITH
|
||||
1.14.3
|
||||
1.14.4
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
/* global Aside */
|
||||
|
||||
window.$ = window.jQuery = require('jquery');
|
||||
require('jquery-ui/ui/draggable');
|
||||
require('jquery-ui/ui/sortable');
|
||||
require('jquery-ujs');
|
||||
require('vendor/jquery.endless-scroll');
|
||||
require('vendor/jquery.highlight');
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
var DOWN_BUILD_TRACE = '#down-build-trace';
|
||||
|
||||
this.Build = (function() {
|
||||
Build.interval = null;
|
||||
Build.timeout = null;
|
||||
|
||||
Build.state = null;
|
||||
|
||||
|
@ -31,7 +31,7 @@
|
|||
this.$scrollBottomBtn = $('#scroll-bottom');
|
||||
this.$buildRefreshAnimation = $('.js-build-refresh');
|
||||
|
||||
clearInterval(Build.interval);
|
||||
clearTimeout(Build.timeout);
|
||||
// Init breakpoint checker
|
||||
this.bp = Breakpoints.get();
|
||||
|
||||
|
@ -52,17 +52,7 @@
|
|||
this.getInitialBuildTrace();
|
||||
this.initScrollButtonAffix();
|
||||
}
|
||||
if (this.buildStatus === "running" || this.buildStatus === "pending") {
|
||||
Build.interval = setInterval((function(_this) {
|
||||
// Check for new build output if user still watching build page
|
||||
// Only valid for runnig build when output changes during time
|
||||
return function() {
|
||||
if (_this.location() === _this.pageUrl) {
|
||||
return _this.getBuildTrace();
|
||||
}
|
||||
};
|
||||
})(this), 4000);
|
||||
}
|
||||
this.invokeBuildTrace();
|
||||
}
|
||||
|
||||
Build.prototype.initSidebar = function() {
|
||||
|
@ -75,6 +65,22 @@
|
|||
return window.location.href.split("#")[0];
|
||||
};
|
||||
|
||||
Build.prototype.invokeBuildTrace = function() {
|
||||
var continueRefreshStatuses = ['running', 'pending'];
|
||||
// Continue to update build trace when build is running or pending
|
||||
if (continueRefreshStatuses.indexOf(this.buildStatus) !== -1) {
|
||||
// Check for new build output if user still watching build page
|
||||
// Only valid for runnig build when output changes during time
|
||||
Build.timeout = setTimeout((function(_this) {
|
||||
return function() {
|
||||
if (_this.location() === _this.pageUrl) {
|
||||
return _this.getBuildTrace();
|
||||
}
|
||||
};
|
||||
})(this), 4000);
|
||||
}
|
||||
};
|
||||
|
||||
Build.prototype.getInitialBuildTrace = function() {
|
||||
var removeRefreshStatuses = ['success', 'failed', 'canceled', 'skipped'];
|
||||
|
||||
|
@ -86,7 +92,7 @@
|
|||
if (window.location.hash === DOWN_BUILD_TRACE) {
|
||||
$("html,body").scrollTop(this.$buildTrace.height());
|
||||
}
|
||||
if (removeRefreshStatuses.indexOf(buildData.status) >= 0) {
|
||||
if (removeRefreshStatuses.indexOf(buildData.status) !== -1) {
|
||||
this.$buildRefreshAnimation.remove();
|
||||
return this.initScrollMonitor();
|
||||
}
|
||||
|
@ -105,6 +111,7 @@
|
|||
if (log.state) {
|
||||
_this.state = log.state;
|
||||
}
|
||||
_this.invokeBuildTrace();
|
||||
if (log.status === "running") {
|
||||
if (log.append) {
|
||||
$('.js-build-output').append(log.html);
|
||||
|
|
|
@ -52,6 +52,30 @@
|
|||
return this.views[viewMode].call(this);
|
||||
};
|
||||
|
||||
ImageFile.prototype.initDraggable = function($el, padding, callback) {
|
||||
var dragging = false;
|
||||
var $body = $('body');
|
||||
var $offsetEl = $el.parent();
|
||||
|
||||
$el.off('mousedown').on('mousedown', function() {
|
||||
dragging = true;
|
||||
$body.css('user-select', 'none');
|
||||
});
|
||||
|
||||
$body.off('mouseup').off('mousemove').on('mouseup', function() {
|
||||
dragging = false;
|
||||
$body.css('user-select', '');
|
||||
})
|
||||
.on('mousemove', function(e) {
|
||||
var left;
|
||||
if (!dragging) return;
|
||||
|
||||
left = e.pageX - ($offsetEl.offset().left + padding);
|
||||
|
||||
callback(e, left);
|
||||
});
|
||||
};
|
||||
|
||||
prepareFrames = function(view) {
|
||||
var maxHeight, maxWidth;
|
||||
maxWidth = 0;
|
||||
|
@ -96,26 +120,30 @@
|
|||
maxHeight = 0;
|
||||
return $('.swipe.view', this.file).each((function(_this) {
|
||||
return function(index, view) {
|
||||
var ref;
|
||||
var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding, ref;
|
||||
ref = prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1];
|
||||
$('.swipe-frame', view).css({
|
||||
$swipeFrame = $('.swipe-frame', view);
|
||||
$swipeWrap = $('.swipe-wrap', view);
|
||||
$swipeBar = $('.swipe-bar', view);
|
||||
|
||||
$swipeFrame.css({
|
||||
width: maxWidth + 16,
|
||||
height: maxHeight + 28
|
||||
});
|
||||
$('.swipe-wrap', view).css({
|
||||
$swipeWrap.css({
|
||||
width: maxWidth + 1,
|
||||
height: maxHeight + 2
|
||||
});
|
||||
return $('.swipe-bar', view).css({
|
||||
$swipeBar.css({
|
||||
left: 0
|
||||
}).draggable({
|
||||
axis: 'x',
|
||||
containment: 'parent',
|
||||
drag: function(event) {
|
||||
return $('.swipe-wrap', view).width((maxWidth + 1) - $(this).position().left);
|
||||
},
|
||||
stop: function(event) {
|
||||
return $('.swipe-wrap', view).width((maxWidth + 1) - $(this).position().left);
|
||||
});
|
||||
|
||||
wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10);
|
||||
|
||||
_this.initDraggable($swipeBar, wrapPadding, function(e, left) {
|
||||
if (left > 0 && left < $swipeFrame.width() - (wrapPadding * 2)) {
|
||||
$swipeWrap.width((maxWidth + 1) - left);
|
||||
$swipeBar.css('left', left);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -128,9 +156,14 @@
|
|||
dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width();
|
||||
return $('.onion-skin.view', this.file).each((function(_this) {
|
||||
return function(index, view) {
|
||||
var ref;
|
||||
var $frame, $track, $dragger, $frameAdded, framePadding, ref, dragging = false;
|
||||
ref = prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1];
|
||||
$('.onion-skin-frame', view).css({
|
||||
$frame = $('.onion-skin-frame', view);
|
||||
$frameAdded = $('.frame.added', view);
|
||||
$track = $('.drag-track', view);
|
||||
$dragger = $('.dragger', $track);
|
||||
|
||||
$frame.css({
|
||||
width: maxWidth + 16,
|
||||
height: maxHeight + 28
|
||||
});
|
||||
|
@ -138,16 +171,18 @@
|
|||
width: maxWidth + 1,
|
||||
height: maxHeight + 2
|
||||
});
|
||||
return $('.dragger', view).css({
|
||||
$dragger.css({
|
||||
left: dragTrackWidth
|
||||
}).draggable({
|
||||
axis: 'x',
|
||||
containment: 'parent',
|
||||
drag: function(event) {
|
||||
return $('.frame.added', view).css('opacity', $(this).position().left / dragTrackWidth);
|
||||
},
|
||||
stop: function(event) {
|
||||
return $('.frame.added', view).css('opacity', $(this).position().left / dragTrackWidth);
|
||||
});
|
||||
|
||||
framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10);
|
||||
|
||||
_this.initDraggable($dragger, framePadding, function(e, left) {
|
||||
var opacity = left / dragTrackWidth;
|
||||
|
||||
if (opacity >= 0 && opacity <= 1) {
|
||||
$dragger.css('left', left);
|
||||
$frameAdded.css('opacity', opacity);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
@ -145,7 +145,7 @@ module.exports = Vue.component('environment-component', {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="environments-container">
|
||||
<div class="content-list environments-container">
|
||||
<div class="environments-list-loading text-center" v-if="isLoading">
|
||||
<i class="fa fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
|
@ -181,12 +181,12 @@ module.exports = Vue.component('environment-component', {
|
|||
:terminal-icon-svg="terminalIconSvg"
|
||||
:commit-icon-svg="commitIconSvg">
|
||||
</environment-table>
|
||||
|
||||
<table-pagination v-if="state.paginationInformation && state.paginationInformation.totalPages > 1"
|
||||
:change="changePage"
|
||||
:pageInfo="state.paginationInformation">
|
||||
</table-pagination>
|
||||
</div>
|
||||
|
||||
<table-pagination v-if="state.paginationInformation && state.paginationInformation.totalPages > 1"
|
||||
:change="changePage"
|
||||
:pageInfo="state.paginationInformation">
|
||||
</table-pagination>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
|
|
|
@ -503,32 +503,30 @@ module.exports = Vue.component('environment-item', {
|
|||
</span>
|
||||
</td>
|
||||
|
||||
<td class="hidden-xs">
|
||||
<div v-if="!model.isFolder">
|
||||
<div class="btn-group" role="group">
|
||||
<actions-component v-if="hasManualActions && canCreateDeployment"
|
||||
:play-icon-svg="playIconSvg"
|
||||
:actions="manualActions">
|
||||
</actions-component>
|
||||
<td class="environments-actions">
|
||||
<div v-if="!model.isFolder" class="btn-group pull-right" role="group">
|
||||
<actions-component v-if="hasManualActions && canCreateDeployment"
|
||||
:play-icon-svg="playIconSvg"
|
||||
:actions="manualActions">
|
||||
</actions-component>
|
||||
|
||||
<external-url-component v-if="externalURL && canReadEnvironment"
|
||||
:external-url="externalURL">
|
||||
</external-url-component>
|
||||
<external-url-component v-if="externalURL && canReadEnvironment"
|
||||
:external-url="externalURL">
|
||||
</external-url-component>
|
||||
|
||||
<stop-component v-if="hasStopAction && canCreateDeployment"
|
||||
:stop-url="model.stop_path">
|
||||
</stop-component>
|
||||
<stop-component v-if="hasStopAction && canCreateDeployment"
|
||||
:stop-url="model.stop_path">
|
||||
</stop-component>
|
||||
|
||||
<terminal-button-component v-if="model && model.terminal_path"
|
||||
:terminal-icon-svg="terminalIconSvg"
|
||||
:terminal-path="model.terminal_path">
|
||||
</terminal-button-component>
|
||||
<terminal-button-component v-if="model && model.terminal_path"
|
||||
:terminal-icon-svg="terminalIconSvg"
|
||||
:terminal-path="model.terminal_path">
|
||||
</terminal-button-component>
|
||||
|
||||
<rollback-component v-if="canRetry && canCreateDeployment"
|
||||
:is-last-deployment="isLastDeployment"
|
||||
:retry-url="retryUrl">
|
||||
</rollback-component>
|
||||
</div>
|
||||
<rollback-component v-if="canRetry && canCreateDeployment"
|
||||
:is-last-deployment="isLastDeployment"
|
||||
:retry-url="retryUrl">
|
||||
</rollback-component>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -46,7 +46,7 @@ module.exports = Vue.component('environment-table-component', {
|
|||
},
|
||||
|
||||
template: `
|
||||
<table class="table ci-table environments">
|
||||
<table class="table ci-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="environments-name">Environment</th>
|
||||
|
@ -54,7 +54,7 @@ module.exports = Vue.component('environment-table-component', {
|
|||
<th class="environments-build">Job</th>
|
||||
<th class="environments-commit">Commit</th>
|
||||
<th class="environments-date">Updated</th>
|
||||
<th class="hidden-xs environments-actions"></th>
|
||||
<th class="environments-actions"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, max-len, one-var, one-var-declaration-per-line, quotes, prefer-template, newline-per-chained-call, comma-dangle, new-cap, no-else-return, consistent-return */
|
||||
/* global FilesCommentButton */
|
||||
/* global notes */
|
||||
|
||||
(function() {
|
||||
let $commentButtonTemplate;
|
||||
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
|
||||
|
||||
this.FilesCommentButton = (function() {
|
||||
var COMMENT_BUTTON_CLASS, COMMENT_BUTTON_TEMPLATE, DEBOUNCE_TIMEOUT_DURATION, EMPTY_CELL_CLASS, LINE_COLUMN_CLASSES, LINE_CONTENT_CLASS, LINE_HOLDER_CLASS, LINE_NUMBER_CLASS, OLD_LINE_CLASS, TEXT_FILE_SELECTOR, UNFOLDABLE_LINE_CLASS;
|
||||
var COMMENT_BUTTON_CLASS, EMPTY_CELL_CLASS, LINE_COLUMN_CLASSES, LINE_CONTENT_CLASS, LINE_HOLDER_CLASS, LINE_NUMBER_CLASS, OLD_LINE_CLASS, TEXT_FILE_SELECTOR, UNFOLDABLE_LINE_CLASS;
|
||||
|
||||
COMMENT_BUTTON_CLASS = '.add-diff-note';
|
||||
|
||||
COMMENT_BUTTON_TEMPLATE = _.template('<button name="button" type="submit" class="btn <%- COMMENT_BUTTON_CLASS %> js-add-diff-note-button" title="Add a comment to this line"><i class="fa fa-comment-o"></i></button>');
|
||||
|
||||
LINE_HOLDER_CLASS = '.line_holder';
|
||||
|
||||
LINE_NUMBER_CLASS = 'diff-line-num';
|
||||
|
@ -27,26 +27,29 @@
|
|||
|
||||
TEXT_FILE_SELECTOR = '.text-file';
|
||||
|
||||
DEBOUNCE_TIMEOUT_DURATION = 100;
|
||||
|
||||
function FilesCommentButton(filesContainerElement) {
|
||||
var debounce;
|
||||
this.filesContainerElement = filesContainerElement;
|
||||
this.destroy = bind(this.destroy, this);
|
||||
this.render = bind(this.render, this);
|
||||
this.VIEW_TYPE = $('input#view[type=hidden]').val();
|
||||
debounce = _.debounce(this.render, DEBOUNCE_TIMEOUT_DURATION);
|
||||
$(this.filesContainerElement).off('mouseover', LINE_COLUMN_CLASSES).off('mouseleave', LINE_COLUMN_CLASSES).on('mouseover', LINE_COLUMN_CLASSES, debounce).on('mouseleave', LINE_COLUMN_CLASSES, this.destroy);
|
||||
this.hideButton = bind(this.hideButton, this);
|
||||
this.isParallelView = notes.isParallelView();
|
||||
filesContainerElement.on('mouseover', LINE_COLUMN_CLASSES, this.render)
|
||||
.on('mouseleave', LINE_COLUMN_CLASSES, this.hideButton);
|
||||
}
|
||||
|
||||
FilesCommentButton.prototype.render = function(e) {
|
||||
var $currentTarget, buttonParentElement, lineContentElement, textFileElement;
|
||||
var $currentTarget, buttonParentElement, lineContentElement, textFileElement, $button;
|
||||
$currentTarget = $(e.currentTarget);
|
||||
|
||||
buttonParentElement = this.getButtonParent($currentTarget);
|
||||
if (!this.validateButtonParent(buttonParentElement)) return;
|
||||
lineContentElement = this.getLineContent($currentTarget);
|
||||
if (!this.validateLineContent(lineContentElement)) return;
|
||||
buttonParentElement = this.getButtonParent($currentTarget);
|
||||
|
||||
if (!this.validateButtonParent(buttonParentElement) || !this.validateLineContent(lineContentElement)) return;
|
||||
|
||||
$button = $(COMMENT_BUTTON_CLASS, buttonParentElement);
|
||||
buttonParentElement.addClass('is-over')
|
||||
.nextUntil(`.${LINE_CONTENT_CLASS}`).addClass('is-over');
|
||||
|
||||
if ($button.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
textFileElement = this.getTextFileElement($currentTarget);
|
||||
buttonParentElement.append(this.buildButton({
|
||||
|
@ -61,19 +64,16 @@
|
|||
}));
|
||||
};
|
||||
|
||||
FilesCommentButton.prototype.destroy = function(e) {
|
||||
if (this.isMovingToSameType(e)) {
|
||||
return;
|
||||
}
|
||||
$(COMMENT_BUTTON_CLASS, this.getButtonParent($(e.currentTarget))).remove();
|
||||
FilesCommentButton.prototype.hideButton = function(e) {
|
||||
var $currentTarget = $(e.currentTarget);
|
||||
var buttonParentElement = this.getButtonParent($currentTarget);
|
||||
|
||||
buttonParentElement.removeClass('is-over')
|
||||
.nextUntil(`.${LINE_CONTENT_CLASS}`).removeClass('is-over');
|
||||
};
|
||||
|
||||
FilesCommentButton.prototype.buildButton = function(buttonAttributes) {
|
||||
var initializedButtonTemplate;
|
||||
initializedButtonTemplate = COMMENT_BUTTON_TEMPLATE({
|
||||
COMMENT_BUTTON_CLASS: COMMENT_BUTTON_CLASS.substr(1)
|
||||
});
|
||||
return $(initializedButtonTemplate).attr({
|
||||
return $commentButtonTemplate.clone().attr({
|
||||
'data-noteable-type': buttonAttributes.noteableType,
|
||||
'data-noteable-id': buttonAttributes.noteableID,
|
||||
'data-commit-id': buttonAttributes.commitID,
|
||||
|
@ -86,14 +86,14 @@
|
|||
};
|
||||
|
||||
FilesCommentButton.prototype.getTextFileElement = function(hoveredElement) {
|
||||
return $(hoveredElement.closest(TEXT_FILE_SELECTOR));
|
||||
return hoveredElement.closest(TEXT_FILE_SELECTOR);
|
||||
};
|
||||
|
||||
FilesCommentButton.prototype.getLineContent = function(hoveredElement) {
|
||||
if (hoveredElement.hasClass(LINE_CONTENT_CLASS)) {
|
||||
return hoveredElement;
|
||||
}
|
||||
if (this.VIEW_TYPE === 'inline') {
|
||||
if (!this.isParallelView) {
|
||||
return $(hoveredElement).closest(LINE_HOLDER_CLASS).find("." + LINE_CONTENT_CLASS);
|
||||
} else {
|
||||
return $(hoveredElement).next("." + LINE_CONTENT_CLASS);
|
||||
|
@ -101,7 +101,7 @@
|
|||
};
|
||||
|
||||
FilesCommentButton.prototype.getButtonParent = function(hoveredElement) {
|
||||
if (this.VIEW_TYPE === 'inline') {
|
||||
if (!this.isParallelView) {
|
||||
if (hoveredElement.hasClass(OLD_LINE_CLASS)) {
|
||||
return hoveredElement;
|
||||
}
|
||||
|
@ -114,17 +114,8 @@
|
|||
}
|
||||
};
|
||||
|
||||
FilesCommentButton.prototype.isMovingToSameType = function(e) {
|
||||
var newButtonParent;
|
||||
newButtonParent = this.getButtonParent($(e.toElement));
|
||||
if (!newButtonParent) {
|
||||
return false;
|
||||
}
|
||||
return newButtonParent.is(this.getButtonParent($(e.currentTarget)));
|
||||
};
|
||||
|
||||
FilesCommentButton.prototype.validateButtonParent = function(buttonParentElement) {
|
||||
return !buttonParentElement.hasClass(EMPTY_CELL_CLASS) && !buttonParentElement.hasClass(UNFOLDABLE_LINE_CLASS) && $(COMMENT_BUTTON_CLASS, buttonParentElement).length === 0;
|
||||
return !buttonParentElement.hasClass(EMPTY_CELL_CLASS) && !buttonParentElement.hasClass(UNFOLDABLE_LINE_CLASS);
|
||||
};
|
||||
|
||||
FilesCommentButton.prototype.validateLineContent = function(lineContentElement) {
|
||||
|
@ -135,6 +126,8 @@
|
|||
})();
|
||||
|
||||
$.fn.filesCommentButton = function() {
|
||||
$commentButtonTemplate = $('<button name="button" type="submit" class="add-diff-note js-add-diff-note-button" title="Add a comment to this line"><i class="fa fa-comment-o"></i></button>');
|
||||
|
||||
if (!(this && (this.parent().data('can-create-note') != null))) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -48,7 +48,11 @@
|
|||
}
|
||||
|
||||
setOffset(offset = 0) {
|
||||
this.dropdown.style.left = `${offset}px`;
|
||||
if (window.innerWidth > 480) {
|
||||
this.dropdown.style.left = `${offset}px`;
|
||||
} else {
|
||||
this.dropdown.style.left = '0px';
|
||||
}
|
||||
}
|
||||
|
||||
renderContent(forceShowList = false) {
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
}
|
||||
|
||||
GitLabDropdownFilter.prototype.shouldBlur = function(keyCode) {
|
||||
return BLUR_KEYCODES.indexOf(keyCode) >= 0;
|
||||
return BLUR_KEYCODES.indexOf(keyCode) !== -1;
|
||||
};
|
||||
|
||||
GitLabDropdownFilter.prototype.filter = function(search_text) {
|
||||
|
@ -605,7 +605,7 @@
|
|||
var occurrences;
|
||||
occurrences = fuzzaldrinPlus.match(text, term);
|
||||
return text.split('').map(function(character, i) {
|
||||
if (indexOf.call(occurrences, i) >= 0) {
|
||||
if (indexOf.call(occurrences, i) !== -1) {
|
||||
return "<b>" + character + "</b>";
|
||||
} else {
|
||||
return character;
|
||||
|
@ -748,7 +748,7 @@
|
|||
return function(e) {
|
||||
var $listItems, PREV_INDEX, currentKeyCode;
|
||||
currentKeyCode = e.which;
|
||||
if (ARROW_KEY_CODES.indexOf(currentKeyCode) >= 0) {
|
||||
if (ARROW_KEY_CODES.indexOf(currentKeyCode) !== -1) {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
PREV_INDEX = currentIndex;
|
||||
|
|
|
@ -116,7 +116,7 @@
|
|||
formData = $.param(formData);
|
||||
formAction = form.attr('action');
|
||||
issuesUrl = formAction;
|
||||
issuesUrl += "" + (formAction.indexOf('?') < 0 ? '?' : '&');
|
||||
issuesUrl += "" + (formAction.indexOf('?') === -1 ? '?' : '&');
|
||||
issuesUrl += formData;
|
||||
return gl.utils.visitUrl(issuesUrl);
|
||||
};
|
||||
|
|
|
@ -329,17 +329,18 @@
|
|||
* ```
|
||||
*/
|
||||
w.gl.utils.backOff = (fn, timeout = 60000) => {
|
||||
const maxInterval = 32000;
|
||||
let nextInterval = 2000;
|
||||
|
||||
const startTime = (+new Date());
|
||||
const startTime = Date.now();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const stop = arg => ((arg instanceof Error) ? reject(arg) : resolve(arg));
|
||||
|
||||
const next = () => {
|
||||
if (new Date().getTime() - startTime < timeout) {
|
||||
if (Date.now() - startTime < timeout) {
|
||||
setTimeout(fn.bind(null, next, stop), nextInterval);
|
||||
nextInterval *= 2;
|
||||
nextInterval = Math.min(nextInterval + nextInterval, maxInterval);
|
||||
} else {
|
||||
reject(new Error('BACKOFF_TIMEOUT'));
|
||||
}
|
||||
|
|
10
app/assets/javascripts/lib/utils/http_status.js
Normal file
10
app/assets/javascripts/lib/utils/http_status.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
/**
|
||||
* exports HTTP status codes
|
||||
*/
|
||||
|
||||
const statusCodes = {
|
||||
NO_CONTENT: 204,
|
||||
OK: 200,
|
||||
};
|
||||
|
||||
module.exports = statusCodes;
|
|
@ -83,7 +83,7 @@ require('./smart_interval');
|
|||
return function() {
|
||||
var page;
|
||||
page = $('body').data('page').split(':').last();
|
||||
if (allowedPages.indexOf(page) < 0) {
|
||||
if (allowedPages.indexOf(page) === -1) {
|
||||
return _this.clearEventListeners();
|
||||
}
|
||||
};
|
||||
|
@ -233,7 +233,7 @@ require('./smart_interval');
|
|||
}
|
||||
$('.ci_widget').hide();
|
||||
allowed_states = ["failed", "canceled", "running", "pending", "success", "success_with_warnings", "skipped", "not_found"];
|
||||
if (indexOf.call(allowed_states, state) >= 0) {
|
||||
if (indexOf.call(allowed_states, state) !== -1) {
|
||||
$('.ci_widget.ci-' + state).show();
|
||||
switch (state) {
|
||||
case "failed":
|
||||
|
|
|
@ -81,7 +81,7 @@
|
|||
var errorMessage, errors, formatter, unique, validator;
|
||||
this.branchNameError.empty();
|
||||
unique = function(values, value) {
|
||||
if (indexOf.call(values, value) < 0) {
|
||||
if (indexOf.call(values, value) === -1) {
|
||||
values.push(value);
|
||||
}
|
||||
return values;
|
||||
|
|
|
@ -84,13 +84,14 @@
|
|||
}
|
||||
|
||||
$(function() {
|
||||
$(document).on('focusout.ssh_key', '#key_key', function() {
|
||||
$(document).on('input.ssh_key', '#key_key', function() {
|
||||
const $title = $('#key_title');
|
||||
const comment = $(this).val().match(/^\S+ \S+ (.+)\n?$/);
|
||||
if (comment && comment.length > 1 && $title.val() === '') {
|
||||
|
||||
// Extract the SSH Key title from its comment
|
||||
if (comment && comment.length > 1) {
|
||||
return $title.val(comment[1]).change();
|
||||
}
|
||||
// Extract the SSH Key title from its comment
|
||||
});
|
||||
if (global.utils.getPagePath() === 'profiles') {
|
||||
return new Profile();
|
||||
|
|
|
@ -116,7 +116,7 @@
|
|||
if ($('input[name="ref"]').length) {
|
||||
var $form = $dropdown.closest('form');
|
||||
var action = $form.attr('action');
|
||||
var divider = action.indexOf('?') < 0 ? '?' : '&';
|
||||
var divider = action.indexOf('?') === -1 ? '?' : '&';
|
||||
gl.utils.visitUrl(action + '' + divider + '' + $form.serialize());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* global Flash */
|
||||
require('vendor/task_list');
|
||||
|
||||
class TaskList {
|
||||
|
@ -6,6 +7,16 @@ class TaskList {
|
|||
this.dataType = options.dataType;
|
||||
this.fieldName = options.fieldName;
|
||||
this.onSuccess = options.onSuccess || (() => {});
|
||||
this.onError = function showFlash(response) {
|
||||
let errorMessages = '';
|
||||
|
||||
if (response.responseJSON) {
|
||||
errorMessages = response.responseJSON.errors.join(' ');
|
||||
}
|
||||
|
||||
return new Flash(errorMessages || 'Update failed', 'alert');
|
||||
};
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
|
@ -32,6 +43,7 @@ class TaskList {
|
|||
url: $target.data('update-url') || $('form.js-issuable-update').attr('action'),
|
||||
data: patchData,
|
||||
success: this.onSuccess,
|
||||
error: this.onError,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,19 +32,17 @@
|
|||
},
|
||||
},
|
||||
template: `
|
||||
<td class="pipeline-actions hidden-xs">
|
||||
<div class="controls pull-right">
|
||||
<div class="btn-group inline">
|
||||
<div class="btn-group">
|
||||
<td class="pipeline-actions">
|
||||
<div class="pull-right">
|
||||
<div class="btn-group">
|
||||
<div class="btn-group" v-if="actions">
|
||||
<button
|
||||
v-if='actions'
|
||||
class="dropdown-toggle btn btn-default has-tooltip js-pipeline-dropdown-manual-actions"
|
||||
data-toggle="dropdown"
|
||||
title="Manual job"
|
||||
data-placement="top"
|
||||
aria-label="Manual job"
|
||||
>
|
||||
<span v-html='svgs.iconPlay' aria-hidden="true"></span>
|
||||
aria-label="Manual job">
|
||||
<span v-html="svgs.iconPlay" aria-hidden="true"></span>
|
||||
<i class="fa fa-caret-down" aria-hidden="true"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-align-right">
|
||||
|
@ -52,23 +50,21 @@
|
|||
<a
|
||||
rel="nofollow"
|
||||
data-method="post"
|
||||
:href='action.path'
|
||||
>
|
||||
<span v-html='svgs.iconPlay' aria-hidden="true"></span>
|
||||
:href="action.path">
|
||||
<span v-html="svgs.iconPlay" aria-hidden="true"></span>
|
||||
<span>{{action.name}}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
|
||||
<div class="btn-group" v-if="artifacts">
|
||||
<button
|
||||
v-if='artifacts'
|
||||
class="dropdown-toggle btn btn-default build-artifacts has-tooltip js-pipeline-dropdown-download"
|
||||
title="Artifacts"
|
||||
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-caret-down" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
@ -76,42 +72,39 @@
|
|||
<li v-for='artifact in pipeline.details.artifacts'>
|
||||
<a
|
||||
rel="nofollow"
|
||||
download
|
||||
:href='artifact.path'
|
||||
>
|
||||
:href="artifact.path">
|
||||
<i class="fa fa-download" aria-hidden="true"></i>
|
||||
<span>{{download(artifact.name)}}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cancel-retry-btns inline">
|
||||
<a
|
||||
v-if='pipeline.flags.retryable'
|
||||
class="btn has-tooltip"
|
||||
title="Retry"
|
||||
rel="nofollow"
|
||||
data-method="post"
|
||||
data-placement="top"
|
||||
data-toggle="dropdown"
|
||||
:href='pipeline.retry_path'
|
||||
aria-label="Retry">
|
||||
<i class="fa fa-repeat" aria-hidden="true"></i>
|
||||
</a>
|
||||
<a
|
||||
v-if='pipeline.flags.cancelable'
|
||||
@click="confirmAction"
|
||||
class="btn btn-remove has-tooltip"
|
||||
title="Cancel"
|
||||
rel="nofollow"
|
||||
data-method="post"
|
||||
data-placement="top"
|
||||
data-toggle="dropdown"
|
||||
:href='pipeline.cancel_path'
|
||||
aria-label="Cancel">
|
||||
<i class="fa fa-remove" aria-hidden="true"></i>
|
||||
</a>
|
||||
<div class="btn-group" v-if="pipeline.flags.retryable">
|
||||
<a
|
||||
class="btn btn-default btn-retry has-tooltip"
|
||||
title="Retry"
|
||||
rel="nofollow"
|
||||
data-method="post"
|
||||
data-placement="top"
|
||||
data-toggle="dropdown"
|
||||
:href='pipeline.retry_path'
|
||||
aria-label="Retry">
|
||||
<i class="fa fa-repeat" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="btn-group" v-if="pipeline.flags.cancelable">
|
||||
<a
|
||||
class="btn btn-remove has-tooltip"
|
||||
title="Cancel"
|
||||
rel="nofollow"
|
||||
data-method="post"
|
||||
data-placement="top"
|
||||
data-toggle="dropdown"
|
||||
:href='pipeline.cancel_path'
|
||||
aria-label="Cancel">
|
||||
<i class="fa fa-remove" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -54,7 +54,7 @@ require('../lib/utils/datetime_utility');
|
|||
},
|
||||
},
|
||||
template: `
|
||||
<td>
|
||||
<td class="pipelines-time-ago">
|
||||
<p class="duration" v-if='duration'>
|
||||
<span v-html='svgs.iconTimer'></span>
|
||||
{{duration}}
|
||||
|
@ -65,8 +65,7 @@ require('../lib/utils/datetime_utility');
|
|||
data-toggle="tooltip"
|
||||
data-placement="top"
|
||||
data-container="body"
|
||||
:data-original-title='localTimeFinished'
|
||||
>
|
||||
:data-original-title='localTimeFinished'>
|
||||
{{timeStopped.words}}
|
||||
</time>
|
||||
</p>
|
||||
|
|
|
@ -44,7 +44,7 @@ require('./pipelines_table_row');
|
|||
<th class="js-pipeline-commit pipeline-commit">Commit</th>
|
||||
<th class="js-pipeline-stages pipeline-stages">Stages</th>
|
||||
<th class="js-pipeline-date pipeline-date"></th>
|
||||
<th class="js-pipeline-actions pipeline-actions hidden-xs"></th>
|
||||
<th class="js-pipeline-actions pipeline-actions"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
@ -229,7 +229,7 @@
|
|||
.controls {
|
||||
float: right;
|
||||
margin-top: 8px;
|
||||
padding-bottom: 7px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid $border-color;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,11 @@
|
|||
.filtered-search-container {
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
|
||||
@media (max-width: $screen-xs-min) {
|
||||
-webkit-flex-direction: column;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.filtered-search-input-container {
|
||||
|
@ -34,6 +39,20 @@
|
|||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
@media (max-width: $screen-xs-min) {
|
||||
-webkit-flex: 1 1 100%;
|
||||
flex: 1 1 100%;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.dropdown-menu {
|
||||
width: auto;
|
||||
left: 0;
|
||||
right: 0;
|
||||
max-width: none;
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.form-control {
|
||||
padding-left: 25px;
|
||||
padding-right: 25px;
|
||||
|
@ -79,6 +98,31 @@
|
|||
overflow: auto;
|
||||
}
|
||||
|
||||
@media (max-width: $screen-xs-min) {
|
||||
.issues-details-filters {
|
||||
padding: 0 0 10px;
|
||||
background-color: $white-light;
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.filter-dropdown-container {
|
||||
.dropdown-toggle,
|
||||
.dropdown {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.fa-chevron-down {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
%filter-dropdown-item-btn-hover {
|
||||
background-color: $dropdown-hover-color;
|
||||
color: $white-light;
|
||||
|
@ -148,4 +192,4 @@
|
|||
|
||||
.filter-dropdown-loading {
|
||||
padding: 8px 16px;
|
||||
}
|
||||
}
|
|
@ -148,16 +148,11 @@ header {
|
|||
}
|
||||
|
||||
.header-logo {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
display: inline-block;
|
||||
margin: 0 8px 0 3px;
|
||||
position: relative;
|
||||
top: 7px;
|
||||
transition-duration: .3s;
|
||||
z-index: 999;
|
||||
|
||||
#logo {
|
||||
position: relative;
|
||||
left: -50%;
|
||||
}
|
||||
|
||||
svg,
|
||||
img {
|
||||
|
@ -167,15 +162,6 @@ header {
|
|||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media (max-width: $screen-xs-max) {
|
||||
right: 20px;
|
||||
left: auto;
|
||||
|
||||
#logo {
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
|
@ -183,7 +169,6 @@ header {
|
|||
padding-right: 20px;
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
max-width: 385px;
|
||||
display: inline-block;
|
||||
line-height: $header-height;
|
||||
font-weight: normal;
|
||||
|
@ -193,14 +178,18 @@ header {
|
|||
vertical-align: top;
|
||||
white-space: nowrap;
|
||||
|
||||
@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
@media (max-width: $screen-xs-max) {
|
||||
max-width: 190px;
|
||||
}
|
||||
|
||||
@media (min-width: $screen-sm-min) and (max-width: $screen-md-max) {
|
||||
max-width: 428px;
|
||||
}
|
||||
|
||||
@media (min-width: $screen-lg-min) {
|
||||
max-width: 685px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $gl-text-color;
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ $dark-highlight-bg: #ffe792;
|
|||
$dark-highlight-color: $black;
|
||||
$dark-pre-hll-bg: #373b41;
|
||||
$dark-hll-bg: #373b41;
|
||||
$dark-over-bg: #9f9ab5;
|
||||
$dark-c: #969896;
|
||||
$dark-err: #c66;
|
||||
$dark-k: #b294bb;
|
||||
|
@ -139,6 +140,18 @@ $dark-il: #de935f;
|
|||
}
|
||||
}
|
||||
|
||||
.diff-line-num {
|
||||
&.is-over,
|
||||
&.hll:not(.empty-cell).is-over {
|
||||
background-color: $dark-over-bg;
|
||||
border-color: darken($dark-over-bg, 5%);
|
||||
|
||||
a {
|
||||
color: darken($dark-over-bg, 15%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.line_content.match {
|
||||
@include dark-diff-match-line;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ $monokai-line-empty-bg: #49483e;
|
|||
$monokai-line-empty-border: darken($monokai-line-empty-bg, 15%);
|
||||
$monokai-diff-border: #808080;
|
||||
$monokai-highlight-bg: #ffe792;
|
||||
$monokai-over-bg: #9f9ab5;
|
||||
|
||||
$monokai-new-bg: rgba(166, 226, 46, 0.1);
|
||||
$monokai-new-idiff: rgba(166, 226, 46, 0.15);
|
||||
|
@ -139,6 +140,18 @@ $monokai-gi: #a6e22e;
|
|||
}
|
||||
}
|
||||
|
||||
.diff-line-num {
|
||||
&.is-over,
|
||||
&.hll:not(.empty-cell).is-over {
|
||||
background-color: $monokai-over-bg;
|
||||
border-color: darken($monokai-over-bg, 5%);
|
||||
|
||||
a {
|
||||
color: darken($monokai-over-bg, 15%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.line_content.match {
|
||||
@include dark-diff-match-line;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ $solarized-dark-line-color-new: #5a766c;
|
|||
$solarized-dark-line-color-old: #7a6c71;
|
||||
$solarized-dark-highlight: #094554;
|
||||
$solarized-dark-hll-bg: #174652;
|
||||
$solarized-dark-over-bg: #9f9ab5;
|
||||
$solarized-dark-c: #586e75;
|
||||
$solarized-dark-err: #93a1a1;
|
||||
$solarized-dark-g: #93a1a1;
|
||||
|
@ -143,6 +144,18 @@ $solarized-dark-il: #2aa198;
|
|||
}
|
||||
}
|
||||
|
||||
.diff-line-num {
|
||||
&.is-over,
|
||||
&.hll:not(.empty-cell).is-over {
|
||||
background-color: $solarized-dark-over-bg;
|
||||
border-color: darken($solarized-dark-over-bg, 5%);
|
||||
|
||||
a {
|
||||
color: darken($solarized-dark-over-bg, 15%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.line_content.match {
|
||||
@include dark-diff-match-line;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ $solarized-light-line-color-new: #a1a080;
|
|||
$solarized-light-line-color-old: #ad9186;
|
||||
$solarized-light-highlight: #eee8d5;
|
||||
$solarized-light-hll-bg: #ddd8c5;
|
||||
$solarized-light-over-bg: #ded7fc;
|
||||
$solarized-light-c: #93a1a1;
|
||||
$solarized-light-err: #586e75;
|
||||
$solarized-light-g: #586e75;
|
||||
|
@ -150,6 +151,18 @@ $solarized-light-il: #2aa198;
|
|||
}
|
||||
}
|
||||
|
||||
.diff-line-num {
|
||||
&.is-over,
|
||||
&.hll:not(.empty-cell).is-over {
|
||||
background-color: $solarized-light-over-bg;
|
||||
border-color: darken($solarized-light-over-bg, 5%);
|
||||
|
||||
a {
|
||||
color: darken($solarized-light-over-bg, 15%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.line_content.match {
|
||||
@include matchLine;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ $white-code-color: $gl-text-color;
|
|||
$white-highlight: #fafe3d;
|
||||
$white-pre-hll-bg: #f8eec7;
|
||||
$white-hll-bg: #f8f8f8;
|
||||
$white-over-bg: #ded7fc;
|
||||
$white-c: #998;
|
||||
$white-err: #a61717;
|
||||
$white-err-bg: #e3d2d2;
|
||||
|
@ -123,6 +124,16 @@ $white-gc-bg: #eaf2f5;
|
|||
}
|
||||
}
|
||||
|
||||
&.is-over,
|
||||
&.hll:not(.empty-cell).is-over {
|
||||
background-color: $white-over-bg;
|
||||
border-color: darken($white-over-bg, 5%);
|
||||
|
||||
a {
|
||||
color: darken($white-over-bg, 15%);
|
||||
}
|
||||
}
|
||||
|
||||
&.hll:not(.empty-cell) {
|
||||
background-color: $line-number-select;
|
||||
border-color: $line-select-yellow-dark;
|
||||
|
|
|
@ -89,6 +89,10 @@
|
|||
|
||||
.diff-line-num {
|
||||
width: 50px;
|
||||
|
||||
a {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
.line_holder td {
|
||||
|
@ -109,10 +113,6 @@
|
|||
td.line_content.parallel {
|
||||
width: 46%;
|
||||
}
|
||||
|
||||
.add-diff-note {
|
||||
margin-left: -65px;
|
||||
}
|
||||
}
|
||||
|
||||
.old_line,
|
||||
|
|
|
@ -15,112 +15,97 @@
|
|||
padding-top: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: $screen-xs-max) {
|
||||
.environments-container {
|
||||
.environments-container {
|
||||
.table-holder {
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.environments {
|
||||
table-layout: fixed;
|
||||
|
||||
.environments-commit,
|
||||
.environments-actions,
|
||||
.environments-deploy,
|
||||
.environments-build,
|
||||
.environments-date {
|
||||
position: static;
|
||||
float: none;
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.environments-commit,
|
||||
.environments-actions {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.environments-date {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.environments-name,
|
||||
.environments-deploy,
|
||||
.environments-build {
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
.environment-name,
|
||||
.environments-build-cell,
|
||||
.deployment-column {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.deployment-column {
|
||||
.avatar {
|
||||
float: none;
|
||||
.table.ci-table {
|
||||
.environments-actions {
|
||||
min-width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
.environments-commit,
|
||||
.environments-actions {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
> a {
|
||||
.environments-date {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.environments-name,
|
||||
.environments-deploy,
|
||||
.environments-build {
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
.deployment-column {
|
||||
> span {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
|
||||
> a {
|
||||
color: $gl-text-color-secondary;
|
||||
}
|
||||
|
||||
svg path {
|
||||
fill: $gl-text-color-secondary;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.commit-title {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.avatar-image-container {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.icon-play {
|
||||
height: 13px;
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
.external-url,
|
||||
.dropdown-new {
|
||||
color: $gl-text-color-secondary;
|
||||
}
|
||||
|
||||
svg path {
|
||||
fill: $gl-text-color-secondary;
|
||||
.dropdown-menu {
|
||||
.fa {
|
||||
margin-right: 6px;
|
||||
color: $gl-text-color-secondary;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
outline: none;
|
||||
.build-link,
|
||||
.branch-name {
|
||||
color: $gl-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.commit-title {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.avatar-image-container {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.icon-play {
|
||||
height: 13px;
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
.external-url,
|
||||
.dropdown-new {
|
||||
color: $gl-text-color-secondary;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
|
||||
.fa {
|
||||
margin-right: 6px;
|
||||
.stop-env-link,
|
||||
.external-url {
|
||||
color: $gl-text-color-secondary;
|
||||
|
||||
.stop-env-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.build-link,
|
||||
.branch-name {
|
||||
color: $gl-text-color;
|
||||
}
|
||||
|
||||
.stop-env-link,
|
||||
.external-url {
|
||||
color: $gl-text-color-secondary;
|
||||
|
||||
.stop-env-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.deployment {
|
||||
.build-column {
|
||||
|
||||
.deployment .build-column {
|
||||
.build-link {
|
||||
color: $gl-text-color;
|
||||
}
|
||||
|
@ -129,34 +114,32 @@
|
|||
float: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.folder-icon {
|
||||
margin-right: 3px;
|
||||
color: $gl-text-color-secondary;
|
||||
display: inline-block;
|
||||
|
||||
.fa:nth-child(1) {
|
||||
.folder-icon {
|
||||
margin-right: 3px;
|
||||
color: $gl-text-color-secondary;
|
||||
display: inline-block;
|
||||
|
||||
.fa:nth-child(1) {
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.folder-name {
|
||||
cursor: pointer;
|
||||
color: $gl-text-color-secondary;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.folder-name {
|
||||
cursor: pointer;
|
||||
color: $gl-text-color-secondary;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.table.ci-table.environments {
|
||||
.icon-container {
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.icon-container {
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.branch-commit {
|
||||
.commit-id {
|
||||
margin-right: 0;
|
||||
.branch-commit {
|
||||
.commit-id {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -452,36 +452,37 @@ ul.notes {
|
|||
* Line note button on the side of diffs
|
||||
*/
|
||||
|
||||
.diff-file tr.line_holder {
|
||||
@mixin show-add-diff-note {
|
||||
display: inline-block;
|
||||
.add-diff-note {
|
||||
display: none;
|
||||
margin-top: -2px;
|
||||
border-radius: 50%;
|
||||
background: $white-light;
|
||||
padding: 1px 5px;
|
||||
font-size: 12px;
|
||||
color: $gl-link-color;
|
||||
margin-left: -55px;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
border: 1px solid $border-color;
|
||||
transition: transform .1s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
background: $gl-info;
|
||||
color: $white-light;
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
.add-diff-note {
|
||||
margin-top: -8px;
|
||||
border-radius: 40px;
|
||||
background: $white-light;
|
||||
padding: 4px;
|
||||
font-size: 16px;
|
||||
color: $gl-link-color;
|
||||
margin-left: -56px;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
width: 32px;
|
||||
// "hide" it by default
|
||||
display: none;
|
||||
|
||||
&:hover {
|
||||
background: $gl-info;
|
||||
color: $white-light;
|
||||
@include show-add-diff-note;
|
||||
}
|
||||
&:active {
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// "show" the icon also if we just hover somewhere over the line
|
||||
&:hover > td {
|
||||
.diff-file {
|
||||
.is-over {
|
||||
.add-diff-note {
|
||||
@include show-add-diff-note;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,21 +13,16 @@
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.table-holder {
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.commit-title {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.controls {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.btn {
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
.table.ci-table {
|
||||
min-width: 1200px;
|
||||
table-layout: fixed;
|
||||
|
||||
.label {
|
||||
margin-bottom: 3px;
|
||||
|
@ -37,16 +32,72 @@
|
|||
color: $black;
|
||||
}
|
||||
|
||||
.pipeline-date,
|
||||
.pipeline-status {
|
||||
width: 10%;
|
||||
.stage-cell {
|
||||
min-width: 130px; // Guarantees we show at least 4 stages in line
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.pipelines-time-ago {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.pipeline-info,
|
||||
.pipeline-commit,
|
||||
.pipeline-stages,
|
||||
.pipeline-actions {
|
||||
width: 20%;
|
||||
padding-right: 0;
|
||||
min-width: 170px; //Guarantees buttons don't break in several lines.
|
||||
|
||||
.btn-default {
|
||||
color: $gl-text-color-secondary;
|
||||
}
|
||||
|
||||
.btn.btn-retry:hover,
|
||||
.btn.btn-retry:focus {
|
||||
border-color: $gray-darkest;
|
||||
background-color: $white-normal;
|
||||
}
|
||||
|
||||
svg path {
|
||||
fill: $gl-text-color-secondary;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
max-height: 250px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.dropdown-toggle,
|
||||
.dropdown-menu {
|
||||
color: $gl-text-color-secondary;
|
||||
|
||||
.fa {
|
||||
color: $gl-text-color-secondary;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
svg,
|
||||
.fa {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
&.open {
|
||||
.btn-default {
|
||||
background-color: $white-normal;
|
||||
border-color: $border-white-normal;
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
.icon-play {
|
||||
height: 13px;
|
||||
width: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,6 +105,7 @@
|
|||
@media (max-width: $screen-md-max) {
|
||||
.content-list {
|
||||
&.pipelines,
|
||||
&.environments-container,
|
||||
&.builds-content-list {
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
|
@ -61,27 +113,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.content-list.pipelines .table-holder {
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.pipeline-holder {
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.table.ci-table {
|
||||
min-width: 900px;
|
||||
|
||||
&.pipeline {
|
||||
min-width: 650px;
|
||||
}
|
||||
|
||||
&.builds-page {
|
||||
|
||||
tr {
|
||||
height: 71px;
|
||||
}
|
||||
&.builds-page tr {
|
||||
height: 71px;
|
||||
}
|
||||
|
||||
tr {
|
||||
|
@ -94,12 +129,16 @@
|
|||
padding: 10px 8px;
|
||||
}
|
||||
|
||||
td.environments-actions {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
td.stage-cell {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.commit-link {
|
||||
padding: 9px 8px 10px;
|
||||
padding: 9px 8px 10px 2px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,72 +245,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
.pipeline-actions {
|
||||
min-width: 140px;
|
||||
|
||||
.btn {
|
||||
margin: 0;
|
||||
color: $gl-text-color-secondary;
|
||||
}
|
||||
|
||||
.cancel-retry-btns {
|
||||
vertical-align: middle;
|
||||
|
||||
.btn:not(:first-child) {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
max-height: 250px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.dropdown-toggle,
|
||||
.dropdown-menu {
|
||||
color: $gl-text-color-secondary;
|
||||
|
||||
.fa {
|
||||
color: $gl-text-color-secondary;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
svg,
|
||||
.fa {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-remove {
|
||||
color: $white-light;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
&.open {
|
||||
.btn-default {
|
||||
background-color: $white-normal;
|
||||
border-color: $border-white-normal;
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
.icon-play {
|
||||
height: 13px;
|
||||
width: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.build-link {
|
||||
|
||||
a {
|
||||
color: $gl-text-color;
|
||||
}
|
||||
.build-link a {
|
||||
color: $gl-text-color;
|
||||
}
|
||||
|
||||
.btn-group.open .dropdown-toggle {
|
||||
|
@ -335,31 +310,8 @@
|
|||
}
|
||||
|
||||
.tab-pane {
|
||||
&.pipelines {
|
||||
.ci-table {
|
||||
min-width: 900px;
|
||||
}
|
||||
|
||||
.content-list.pipelines {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.stage {
|
||||
max-width: 100px;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.pipeline-actions {
|
||||
min-width: initial;
|
||||
}
|
||||
}
|
||||
|
||||
&.builds {
|
||||
.ci-table {
|
||||
tr {
|
||||
height: 71px;
|
||||
}
|
||||
}
|
||||
&.builds .ci-table tr {
|
||||
height: 71px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -638,14 +638,6 @@ pre.light-well {
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
.activity-filter-block {
|
||||
.controls {
|
||||
padding-bottom: 7px;
|
||||
margin-top: 8px;
|
||||
border-bottom: 1px solid $border-color;
|
||||
}
|
||||
}
|
||||
|
||||
.commits-search-form {
|
||||
.input-short {
|
||||
min-width: 200px;
|
||||
|
|
|
@ -26,6 +26,23 @@ module IssuableActions
|
|||
|
||||
private
|
||||
|
||||
def render_conflict_response
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
@conflict = true
|
||||
render :edit
|
||||
end
|
||||
|
||||
format.json do
|
||||
render json: {
|
||||
errors: [
|
||||
"Someone edited this #{issuable.human_class_name} at the same time you did. Please refresh your browser and make sure your changes will not unintentionally remove theirs."
|
||||
]
|
||||
}, status: 409
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def labels
|
||||
@labels ||= LabelsFinder.new(current_user, project_id: @project.id).execute
|
||||
end
|
||||
|
|
|
@ -33,6 +33,7 @@ module ServiceParams
|
|||
:issues_url,
|
||||
:jira_issue_transition_id,
|
||||
:merge_requests_events,
|
||||
:mock_service_url,
|
||||
:namespace,
|
||||
:new_issue_url,
|
||||
:notify,
|
||||
|
|
|
@ -134,8 +134,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
rescue ActiveRecord::StaleObjectError
|
||||
@conflict = true
|
||||
render :edit
|
||||
render_conflict_response
|
||||
end
|
||||
|
||||
def referenced_merge_requests
|
||||
|
|
|
@ -296,22 +296,21 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
|||
def update
|
||||
@merge_request = MergeRequests::UpdateService.new(project, current_user, merge_request_params).execute(@merge_request)
|
||||
|
||||
if @merge_request.valid?
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
redirect_to([@merge_request.target_project.namespace.becomes(Namespace),
|
||||
@merge_request.target_project, @merge_request])
|
||||
end
|
||||
format.json do
|
||||
render json: @merge_request.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }, methods: [:task_status, :task_status_short])
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
if @merge_request.valid?
|
||||
redirect_to([@merge_request.target_project.namespace.becomes(Namespace), @merge_request.target_project, @merge_request])
|
||||
else
|
||||
render :edit
|
||||
end
|
||||
end
|
||||
else
|
||||
render "edit"
|
||||
|
||||
format.json do
|
||||
render json: @merge_request.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }, methods: [:task_status, :task_status_short])
|
||||
end
|
||||
end
|
||||
rescue ActiveRecord::StaleObjectError
|
||||
@conflict = true
|
||||
render :edit
|
||||
render_conflict_response
|
||||
end
|
||||
|
||||
def remove_wip
|
||||
|
|
|
@ -15,4 +15,11 @@ module BuildsHelper
|
|||
log_state: @build.trace_with_state[:state].to_s
|
||||
}
|
||||
end
|
||||
|
||||
def build_failed_issue_options
|
||||
{
|
||||
title: "Build Failed ##{@build.id}",
|
||||
description: namespace_project_build_url(@project.namespace, @project, @build)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -34,7 +34,7 @@ module ButtonHelper
|
|||
|
||||
content_tag (append_link ? :a : :span), protocol,
|
||||
class: klass,
|
||||
href: (project.http_url_to_repo if append_link),
|
||||
href: (project.http_url_to_repo(current_user) if append_link),
|
||||
data: {
|
||||
html: true,
|
||||
placement: placement,
|
||||
|
|
|
@ -241,7 +241,7 @@ module ProjectsHelper
|
|||
when 'ssh'
|
||||
project.ssh_url_to_repo
|
||||
else
|
||||
project.http_url_to_repo
|
||||
project.http_url_to_repo(current_user)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -91,10 +91,6 @@ class MergeRequest < ActiveRecord::Base
|
|||
around_transition do |merge_request, transition, block|
|
||||
Gitlab::Timeless.timeless(merge_request, &block)
|
||||
end
|
||||
|
||||
after_transition unchecked: :cannot_be_merged do |merge_request, transition|
|
||||
TodoService.new.merge_request_became_unmergeable(merge_request)
|
||||
end
|
||||
end
|
||||
|
||||
validates :source_project, presence: true, unless: [:allow_broken, :importing?, :closed_without_fork?]
|
||||
|
|
|
@ -869,8 +869,14 @@ class Project < ActiveRecord::Base
|
|||
url_to_repo
|
||||
end
|
||||
|
||||
def http_url_to_repo
|
||||
"#{web_url}.git"
|
||||
def http_url_to_repo(user = nil)
|
||||
url = web_url
|
||||
|
||||
if user
|
||||
url.sub!(%r{\Ahttps?://}) { |protocol| "#{protocol}#{user.username}@" }
|
||||
end
|
||||
|
||||
"#{url}.git"
|
||||
end
|
||||
|
||||
# Check if current branch name is marked as protected in the system
|
||||
|
|
|
@ -33,8 +33,15 @@ class ProjectGroupLink < ActiveRecord::Base
|
|||
private
|
||||
|
||||
def different_group
|
||||
if self.group && self.project && self.project.group == self.group
|
||||
errors.add(:base, "Project cannot be shared with the project it is in.")
|
||||
return unless self.group && self.project
|
||||
|
||||
project_group = self.project.group
|
||||
return unless project_group
|
||||
|
||||
group_ids = project_group.ancestors.map(&:id).push(project_group.id)
|
||||
|
||||
if group_ids.include?(self.group.id)
|
||||
errors.add(:base, "Project cannot be shared with the group it is in or one of its ancestors.")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
82
app/models/project_services/mock_ci_service.rb
Normal file
82
app/models/project_services/mock_ci_service.rb
Normal file
|
@ -0,0 +1,82 @@
|
|||
# For an example companion mocking service, see https://gitlab.com/gitlab-org/gitlab-mock-ci-service
|
||||
class MockCiService < CiService
|
||||
ALLOWED_STATES = %w[failed canceled running pending success success_with_warnings skipped not_found].freeze
|
||||
|
||||
prop_accessor :mock_service_url
|
||||
validates :mock_service_url, presence: true, url: true, if: :activated?
|
||||
|
||||
def title
|
||||
'MockCI'
|
||||
end
|
||||
|
||||
def description
|
||||
'Mock an external CI'
|
||||
end
|
||||
|
||||
def self.to_param
|
||||
'mock_ci'
|
||||
end
|
||||
|
||||
def fields
|
||||
[
|
||||
{ type: 'text',
|
||||
name: 'mock_service_url',
|
||||
placeholder: 'http://localhost:4004' },
|
||||
]
|
||||
end
|
||||
|
||||
# Return complete url to build page
|
||||
#
|
||||
# Ex.
|
||||
# http://jenkins.example.com:8888/job/test1/scm/bySHA1/12d65c
|
||||
#
|
||||
def build_page(sha, ref)
|
||||
url = [mock_service_url,
|
||||
"#{project.namespace.path}/#{project.path}/status/#{sha}"]
|
||||
|
||||
URI.join(*url).to_s
|
||||
end
|
||||
|
||||
# Return string with build status or :error symbol
|
||||
#
|
||||
# Allowed states: 'success', 'failed', 'running', 'pending', 'skipped'
|
||||
#
|
||||
#
|
||||
# Ex.
|
||||
# @service.commit_status('13be4ac', 'master')
|
||||
# # => 'success'
|
||||
#
|
||||
# @service.commit_status('2abe4ac', 'dev')
|
||||
# # => 'running'
|
||||
#
|
||||
#
|
||||
def commit_status(sha, ref)
|
||||
response = HTTParty.get(commit_status_path(sha), verify: false)
|
||||
read_commit_status(response)
|
||||
rescue Errno::ECONNREFUSED
|
||||
:error
|
||||
end
|
||||
|
||||
def commit_status_path(sha)
|
||||
url = [mock_service_url,
|
||||
"#{project.namespace.path}/#{project.path}/status/#{sha}.json"]
|
||||
|
||||
URI.join(*url).to_s
|
||||
end
|
||||
|
||||
def read_commit_status(response)
|
||||
return :error unless response.code == 200 || response.code == 404
|
||||
|
||||
status = if response.code == 404
|
||||
'pending'
|
||||
else
|
||||
response['status']
|
||||
end
|
||||
|
||||
if status.present? && ALLOWED_STATES.include?(status)
|
||||
status
|
||||
else
|
||||
:error
|
||||
end
|
||||
end
|
||||
end
|
|
@ -210,7 +210,7 @@ class Service < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def self.available_services_names
|
||||
%w[
|
||||
service_names = %w[
|
||||
asana
|
||||
assembla
|
||||
bamboo
|
||||
|
@ -238,6 +238,9 @@ class Service < ActiveRecord::Base
|
|||
slack
|
||||
teamcity
|
||||
]
|
||||
service_names << 'mock_ci' if Rails.env.development?
|
||||
|
||||
service_names.sort_by(&:downcase)
|
||||
end
|
||||
|
||||
def self.build_from_template(project_id, template)
|
||||
|
|
|
@ -474,7 +474,7 @@ class User < ActiveRecord::Base
|
|||
Group.member_descendants(id)
|
||||
end
|
||||
|
||||
def nested_projects
|
||||
def nested_groups_projects
|
||||
Project.joins(:namespace).where('namespaces.parent_id IS NOT NULL').
|
||||
member_descendants(id)
|
||||
end
|
||||
|
|
|
@ -18,7 +18,8 @@ module Groups
|
|||
end
|
||||
|
||||
group.children.each do |group|
|
||||
DestroyService.new(group, current_user).async_execute
|
||||
# This needs to be synchronous since the namespace gets destroyed below
|
||||
DestroyService.new(group, current_user).execute
|
||||
end
|
||||
|
||||
group.really_destroy!
|
||||
|
|
|
@ -6,6 +6,8 @@ module MergeRequests
|
|||
# Executed when you do merge via GitLab UI
|
||||
#
|
||||
class MergeService < MergeRequests::BaseService
|
||||
MergeError = Class.new(StandardError)
|
||||
|
||||
attr_reader :merge_request, :source
|
||||
|
||||
def execute(merge_request)
|
||||
|
@ -27,6 +29,8 @@ module MergeRequests
|
|||
success
|
||||
end
|
||||
end
|
||||
rescue MergeError => e
|
||||
log_merge_error(e.message, save_message_on_model: true)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -42,19 +46,13 @@ module MergeRequests
|
|||
|
||||
commit_id = repository.merge(current_user, source, merge_request, options)
|
||||
|
||||
if commit_id
|
||||
merge_request.update(merge_commit_sha: commit_id)
|
||||
else
|
||||
log_merge_error('Conflicts detected during merge', save_message_on_model: true)
|
||||
false
|
||||
end
|
||||
raise MergeError, 'Conflicts detected during merge' unless commit_id
|
||||
|
||||
merge_request.update(merge_commit_sha: commit_id)
|
||||
rescue GitHooksService::PreReceiveError => e
|
||||
log_merge_error(e.message, save_message_on_model: true)
|
||||
false
|
||||
raise MergeError, e.message
|
||||
rescue StandardError => e
|
||||
merge_request.update(merge_error: "Something went wrong during merge: #{e.message}")
|
||||
log_merge_error(e.message)
|
||||
false
|
||||
raise MergeError, "Something went wrong during merge: #{e.message}"
|
||||
ensure
|
||||
merge_request.update(in_progress_merge_commit_sha: nil)
|
||||
end
|
||||
|
|
|
@ -24,7 +24,11 @@ module MergeRequests
|
|||
|
||||
pipeline_merge_requests(pipeline) do |merge_request|
|
||||
next unless merge_request.merge_when_build_succeeds?
|
||||
next unless merge_request.mergeable?
|
||||
|
||||
unless merge_request.mergeable?
|
||||
todo_service.merge_request_became_unmergeable(merge_request)
|
||||
next
|
||||
end
|
||||
|
||||
MergeWorker.perform_async(merge_request.id, merge_request.merge_user_id, merge_request.merge_params)
|
||||
end
|
||||
|
|
|
@ -115,11 +115,23 @@ module Users
|
|||
# Returns a union query of projects that the user is authorized to access
|
||||
def project_authorizations_union
|
||||
relations = [
|
||||
# Personal projects
|
||||
user.personal_projects.select("#{user.id} AS user_id, projects.id AS project_id, #{Gitlab::Access::MASTER} AS access_level"),
|
||||
user.groups_projects.select_for_project_authorization,
|
||||
|
||||
# Projects the user is a member of
|
||||
user.projects.select_for_project_authorization,
|
||||
|
||||
# Projects of groups the user is a member of
|
||||
user.groups_projects.select_for_project_authorization,
|
||||
|
||||
# Projects of subgroups of groups the user is a member of
|
||||
user.nested_groups_projects.select_for_project_authorization,
|
||||
|
||||
# Projects shared with groups the user is a member of
|
||||
user.groups.joins(:shared_projects).select_for_project_authorization,
|
||||
user.nested_projects.select_for_project_authorization
|
||||
|
||||
# Projects shared with subgroups of groups the user is a member of
|
||||
user.nested_groups.joins(:shared_projects).select_for_project_authorization
|
||||
]
|
||||
|
||||
Gitlab::SQL::Union.new(relations)
|
||||
|
|
|
@ -27,10 +27,6 @@ class ArtifactUploader < GitlabUploader
|
|||
File.join(self.class.artifacts_cache_path, @build.artifacts_path)
|
||||
end
|
||||
|
||||
def file_storage?
|
||||
self.class.storage == CarrierWave::Storage::File
|
||||
end
|
||||
|
||||
def filename
|
||||
file.try(:filename)
|
||||
end
|
||||
|
|
|
@ -4,6 +4,6 @@ class AttachmentUploader < GitlabUploader
|
|||
storage :file
|
||||
|
||||
def store_dir
|
||||
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
|
||||
"#{base_dir}/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,7 +4,7 @@ class AvatarUploader < GitlabUploader
|
|||
storage :file
|
||||
|
||||
def store_dir
|
||||
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
|
||||
"#{base_dir}/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
|
||||
end
|
||||
|
||||
def exists?
|
||||
|
|
|
@ -4,15 +4,12 @@ class FileUploader < GitlabUploader
|
|||
|
||||
storage :file
|
||||
|
||||
attr_accessor :project, :secret
|
||||
attr_accessor :project
|
||||
attr_reader :secret
|
||||
|
||||
def initialize(project, secret = nil)
|
||||
@project = project
|
||||
@secret = secret || self.class.generate_secret
|
||||
end
|
||||
|
||||
def base_dir
|
||||
"uploads"
|
||||
@secret = secret || generate_secret
|
||||
end
|
||||
|
||||
def store_dir
|
||||
|
@ -23,10 +20,6 @@ class FileUploader < GitlabUploader
|
|||
File.join(base_dir, 'tmp', @project.path_with_namespace, @secret)
|
||||
end
|
||||
|
||||
def secure_url
|
||||
File.join("/uploads", @secret, file.filename)
|
||||
end
|
||||
|
||||
def to_markdown
|
||||
to_h[:markdown]
|
||||
end
|
||||
|
@ -35,17 +28,23 @@ class FileUploader < GitlabUploader
|
|||
filename = image_or_video? ? self.file.basename : self.file.filename
|
||||
escaped_filename = filename.gsub("]", "\\]")
|
||||
|
||||
markdown = "[#{escaped_filename}](#{self.secure_url})"
|
||||
markdown = "[#{escaped_filename}](#{secure_url})"
|
||||
markdown.prepend("!") if image_or_video? || dangerous?
|
||||
|
||||
{
|
||||
alt: filename,
|
||||
url: self.secure_url,
|
||||
url: secure_url,
|
||||
markdown: markdown
|
||||
}
|
||||
end
|
||||
|
||||
def self.generate_secret
|
||||
private
|
||||
|
||||
def generate_secret
|
||||
SecureRandom.hex
|
||||
end
|
||||
|
||||
def secure_url
|
||||
File.join('/uploads', @secret, file.filename)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,14 @@
|
|||
class GitlabUploader < CarrierWave::Uploader::Base
|
||||
def self.base_dir
|
||||
'uploads'
|
||||
end
|
||||
|
||||
delegate :base_dir, to: :class
|
||||
|
||||
def file_storage?
|
||||
self.class.storage == CarrierWave::Storage::File
|
||||
end
|
||||
|
||||
# Reduce disk IO
|
||||
def move_to_cache
|
||||
true
|
||||
|
|
|
@ -27,6 +27,8 @@ module UploaderHelper
|
|||
extension_match?(DANGEROUS_EXT)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def extension_match?(extensions)
|
||||
return false unless file
|
||||
|
||||
|
@ -40,8 +42,4 @@ module UploaderHelper
|
|||
|
||||
extensions.include?(extension.downcase)
|
||||
end
|
||||
|
||||
def file_storage?
|
||||
self.class.storage == CarrierWave::Storage::File
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
= runner.short_sha
|
||||
%td
|
||||
= runner.description
|
||||
%td
|
||||
= runner.version
|
||||
%td
|
||||
- if runner.shared?
|
||||
n/a
|
||||
|
|
|
@ -67,6 +67,7 @@
|
|||
%th Type
|
||||
%th Runner token
|
||||
%th Description
|
||||
%th Version
|
||||
%th Projects
|
||||
%th Jobs
|
||||
%th Tags
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
.nav-block
|
||||
- if current_user
|
||||
.controls
|
||||
= link_to dashboard_projects_path(:atom, { private_token: current_user.private_token }), class: 'btn rss-btn' do
|
||||
= link_to dashboard_projects_path(:atom, { private_token: current_user.private_token }), class: 'btn rss-btn has-tooltip', title: 'Subscribe' do
|
||||
%i.fa.fa-rss
|
||||
= render 'shared/event_filter'
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
.nav-block
|
||||
- if current_user
|
||||
.controls
|
||||
= link_to group_path(@group, format: :atom, private_token: current_user.private_token), class: 'btn rss-btn' do
|
||||
= link_to group_path(@group, format: :atom, private_token: current_user.private_token), class: 'btn rss-btn has-tooltip' , title: 'Subscribe' do
|
||||
%i.fa.fa-rss
|
||||
= render 'shared/event_filter'
|
||||
|
||||
|
|
|
@ -36,6 +36,10 @@
|
|||
= icon('bell fw')
|
||||
%span.badge.todos-pending-count{ class: ("hidden" if todos_pending_count == 0) }
|
||||
= todos_count_format(todos_pending_count)
|
||||
- if current_user.can_create_project?
|
||||
%li
|
||||
= link_to new_project_path, title: 'New project', aria: { label: "New project" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
|
||||
= icon('plus fw')
|
||||
- if Gitlab::Sherlock.enabled?
|
||||
%li
|
||||
= link_to sherlock_transactions_path, title: 'Sherlock Transactions',
|
||||
|
@ -61,12 +65,12 @@
|
|||
%div
|
||||
= link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-success'
|
||||
|
||||
%h1.title= title
|
||||
|
||||
.header-logo
|
||||
= link_to root_path, class: 'home', title: 'Dashboard', id: 'logo' do
|
||||
= brand_header_logo
|
||||
|
||||
%h1.title= title
|
||||
|
||||
= yield :header_content
|
||||
|
||||
= render 'shared/outdated_browser'
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
.nav-block.activity-filter-block
|
||||
- if current_user
|
||||
.controls
|
||||
= link_to namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "Feed", class: 'btn rss-btn' do
|
||||
= link_to namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "Subscribe", class: 'btn rss-btn has-tooltip' do
|
||||
= icon('rss')
|
||||
|
||||
= render 'shared/event_filter'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.content-block.build-header
|
||||
.content-block.build-header.top-area
|
||||
.header-content
|
||||
= render 'ci/status/badge', status: @build.detailed_status(current_user), link: false
|
||||
Job
|
||||
|
@ -16,7 +16,10 @@
|
|||
- if @build.user
|
||||
= render "user"
|
||||
= time_ago_with_tooltip(@build.created_at)
|
||||
- if can?(current_user, :update_build, @build) && @build.retryable?
|
||||
= link_to "Retry job", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-inverted-secondary pull-right', method: :post
|
||||
.nav-controls
|
||||
- if can?(current_user, :create_issue, @project) && @build.failed?
|
||||
= link_to "New issue", new_namespace_project_issue_path(@project.namespace, @project, issue: build_failed_issue_options), class: 'btn btn-new btn-inverted'
|
||||
- if can?(current_user, :update_build, @build) && @build.retryable?
|
||||
= link_to "Retry job", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-inverted-secondary', 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" }
|
||||
= icon('angle-double-left')
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
- status = pipeline.status
|
||||
- show_commit = local_assigns.fetch(:show_commit, true)
|
||||
- show_branch = local_assigns.fetch(:show_branch, true)
|
||||
|
||||
%tr.commit
|
||||
%td.commit-link
|
||||
= render 'ci/status/badge', status: pipeline.detailed_status(current_user)
|
||||
|
||||
%td
|
||||
= link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id) do
|
||||
%span.pipeline-id ##{pipeline.id}
|
||||
%span by
|
||||
- if pipeline.user
|
||||
= user_avatar(user: pipeline.user, size: 20)
|
||||
- else
|
||||
%span.api.monospace API
|
||||
- if pipeline.latest?
|
||||
%span.label.label-success.has-tooltip{ title: 'Latest pipeline for this branch' } latest
|
||||
- if pipeline.triggered?
|
||||
%span.label.label-primary triggered
|
||||
- if pipeline.yaml_errors.present?
|
||||
%span.label.label-danger.has-tooltip{ title: "#{pipeline.yaml_errors}" } yaml invalid
|
||||
- if pipeline.builds.any?(&:stuck?)
|
||||
%span.label.label-warning stuck
|
||||
|
||||
%td.branch-commit
|
||||
- if pipeline.ref && show_branch
|
||||
.icon-container
|
||||
= pipeline.tag? ? icon('tag') : icon('code-fork')
|
||||
= link_to pipeline.ref, namespace_project_commits_path(pipeline.project.namespace, pipeline.project, pipeline.ref), class: "monospace branch-name"
|
||||
- if show_commit
|
||||
.icon-container.commit-icon
|
||||
= custom_icon("icon_commit")
|
||||
= link_to pipeline.short_sha, namespace_project_commit_path(pipeline.project.namespace, pipeline.project, pipeline.sha), class: "commit-id monospace"
|
||||
|
||||
%p.commit-title
|
||||
- if commit = pipeline.commit
|
||||
= author_avatar(commit, size: 20)
|
||||
= link_to_gfm truncate(commit.title, length: 60, escape: false), namespace_project_commit_path(pipeline.project.namespace, pipeline.project, commit.id), class: "commit-row-message"
|
||||
- else
|
||||
Cant find HEAD commit for this branch
|
||||
|
||||
%td
|
||||
= render 'shared/mini_pipeline_graph', pipeline: pipeline, klass: 'js-mini-pipeline-graph'
|
||||
|
||||
%td
|
||||
- if pipeline.duration
|
||||
%p.duration
|
||||
= custom_icon("icon_timer")
|
||||
= duration_in_numbers(pipeline.duration)
|
||||
- if pipeline.finished_at
|
||||
%p.finished-at
|
||||
= icon("calendar")
|
||||
#{time_ago_with_tooltip(pipeline.finished_at, short_format: false)}
|
||||
|
||||
%td.pipeline-actions.hidden-xs
|
||||
.controls.pull-right
|
||||
- artifacts = pipeline.builds.latest.with_artifacts_not_expired
|
||||
- actions = pipeline.manual_actions
|
||||
- if artifacts.present? || actions.any?
|
||||
.btn-group.inline
|
||||
- if actions.any?
|
||||
.btn-group
|
||||
%button.dropdown-toggle.btn.btn-default.has-tooltip.js-pipeline-dropdown-manual-actions{ type: 'button', title: 'Manual pipeline', data: { toggle: 'dropdown', placement: 'top' }, 'aria-label' => 'Manual pipeline' }
|
||||
= custom_icon('icon_play')
|
||||
= icon('caret-down', 'aria-hidden' => 'true')
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
- actions.each do |build|
|
||||
%li
|
||||
= link_to play_namespace_project_build_path(pipeline.project.namespace, pipeline.project, build), method: :post, rel: 'nofollow' do
|
||||
= custom_icon('icon_play')
|
||||
%span= build.name
|
||||
- if artifacts.present?
|
||||
.btn-group
|
||||
%button.dropdown-toggle.btn.btn-default.build-artifacts.has-tooltip.js-pipeline-dropdown-download{ type: 'button', title: 'Artifacts', data: { toggle: 'dropdown', placement: 'top' }, 'aria-label' => 'Artifacts' }
|
||||
= icon("download")
|
||||
= icon('caret-down')
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
- artifacts.each do |build|
|
||||
%li
|
||||
= link_to download_namespace_project_build_artifacts_path(pipeline.project.namespace, pipeline.project, build), rel: 'nofollow', download: '' do
|
||||
= icon("download")
|
||||
%span Download '#{build.name}' artifacts
|
||||
|
||||
- if can?(current_user, :update_pipeline, pipeline.project)
|
||||
.cancel-retry-btns.inline
|
||||
- if pipeline.retryable?
|
||||
= link_to retry_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: 'btn has-tooltip', title: 'Retry', data: { toggle: 'dropdown', placement: 'top' }, 'aria-label' => 'Retry' , method: :post do
|
||||
= icon("repeat")
|
||||
- if pipeline.cancelable?
|
||||
= link_to cancel_namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id), class: 'btn btn-remove has-tooltip', title: 'Cancel', data: { toggle: 'dropdown', placement: 'top' }, 'aria-label' => 'Cancel' , method: :post do
|
||||
= icon("remove")
|
|
@ -3,7 +3,7 @@
|
|||
= render "projects/commits/head"
|
||||
|
||||
.flex-list{ class: container_class }
|
||||
.top-area.flex-row
|
||||
.top-area.adjust
|
||||
.nav-text.row-main-content
|
||||
Tags give the ability to mark specific points in history as being important
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@
|
|||
%span.dropdown-label-box{ style: 'background: {{color}}' }
|
||||
%span.label-title.js-data-value
|
||||
{{title}}
|
||||
.pull-right
|
||||
.pull-right.filter-dropdown-container
|
||||
= render 'shared/sort_dropdown'
|
||||
|
||||
- if @bulk_edit
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Add runner version to /admin/runners view
|
||||
merge_request: 8733
|
||||
author: Jonathon Reinhart
|
4
changelogs/unreleased/1937-https-clone-url-username.yml
Normal file
4
changelogs/unreleased/1937-https-clone-url-username.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Add the Username to the HTTP(S) clone URL of a Repository
|
||||
merge_request: 9347
|
||||
author: Jan Christophersen
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Add button to create issue for failing build
|
||||
merge_request: 9391
|
||||
author: Alex Sanford
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
title: 'Add performance query regression fix for !9088 affecting #27267'
|
||||
merge_request:
|
||||
author:
|
4
changelogs/unreleased/27354-navigation-new-button.yml
Normal file
4
changelogs/unreleased/27354-navigation-new-button.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Re-add the New Project button in nav bar
|
||||
merge_request:
|
||||
author:
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Enhanced filter issues layout for better mobile experiance
|
||||
merge_request: 9280
|
||||
author: Pratik Borsadiya
|
4
changelogs/unreleased/27934-left-align-logo.yml
Normal file
4
changelogs/unreleased/27934-left-align-logo.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Left align logo
|
||||
merge_request:
|
||||
author:
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
title: Disable unused tags count cache for Projects, Builds and Runners
|
||||
merge_request:
|
||||
author:
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
title: Spam check and reCAPTCHA improvements
|
||||
merge_request:
|
||||
author:
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
title: Allow searching issues for strings containing colons
|
||||
merge_request:
|
||||
author:
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Keep consistent in handling indexOf results
|
||||
merge_request: 9531
|
||||
author: Takuya Noguchi
|
4
changelogs/unreleased/6073_project_api.yml
Normal file
4
changelogs/unreleased/6073_project_api.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: 'API project create: Make name or path required'
|
||||
merge_request: 9416
|
||||
author:
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
title: Disabled tooltip on add issues button in usse boards
|
||||
merge_request:
|
||||
author:
|
4
changelogs/unreleased/api-empty-return.yml
Normal file
4
changelogs/unreleased/api-empty-return.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: 'API: Return 204 for all delete endpoints'
|
||||
merge_request: 9397
|
||||
author: Robert Schilling
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
title: Fixed commit search UI
|
||||
merge_request:
|
||||
author:
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
title: Fix MR changes tab size count when there are over 100 files in the diff
|
||||
merge_request:
|
||||
author:
|
4
changelogs/unreleased/issue-tags-layout.yml
Normal file
4
changelogs/unreleased/issue-tags-layout.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Fix 'New Tag' layout on Tags page
|
||||
merge_request:
|
||||
author: Robert Marcano
|
4
changelogs/unreleased/issue_24815.yml
Normal file
4
changelogs/unreleased/issue_24815.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Fix issuable stale object error handler for js when updating tasklists
|
||||
merge_request:
|
||||
author:
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
title: Disable invalid service templates
|
||||
merge_request:
|
||||
author:
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
title: Use default branch as target_branch when parameter is missing
|
||||
merge_request:
|
||||
author:
|
4
changelogs/unreleased/mock-ci-service.yml
Normal file
4
changelogs/unreleased/mock-ci-service.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Add Mock CI service/integration for development
|
||||
merge_request:
|
||||
author:
|
4
changelogs/unreleased/mr-diff-comment-button.yml
Normal file
4
changelogs/unreleased/mr-diff-comment-button.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Improved diff comment button UX
|
||||
merge_request:
|
||||
author:
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Only create unmergeable todos once when MR fails to merge
|
||||
merge_request:
|
||||
author:
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
title: Upgrade GitLab Pages to v0.3.2
|
||||
merge_request:
|
||||
author:
|
4
changelogs/unreleased/rss-btn-alignment-fix.yml
Normal file
4
changelogs/unreleased/rss-btn-alignment-fix.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: Fixed RSS button alignment on activity pages
|
||||
merge_request:
|
||||
author:
|
4
changelogs/unreleased/ssh-key-paste.yml
Normal file
4
changelogs/unreleased/ssh-key-paste.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
title: SSH key field updates title after pasting key
|
||||
merge_request:
|
||||
author:
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
title: Chat slash commands show labels correctly
|
||||
merge_request:
|
||||
author:
|
69
db/fixtures/development/19_nested_groups.rb
Normal file
69
db/fixtures/development/19_nested_groups.rb
Normal file
|
@ -0,0 +1,69 @@
|
|||
require './spec/support/sidekiq'
|
||||
|
||||
def create_group_with_parents(user, full_path)
|
||||
parent_path = nil
|
||||
group = nil
|
||||
|
||||
until full_path.blank?
|
||||
path, _, full_path = full_path.partition('/')
|
||||
|
||||
if parent_path
|
||||
parent = Group.find_by_full_path(parent_path)
|
||||
|
||||
parent_path += '/'
|
||||
parent_path += path
|
||||
|
||||
group = Groups::CreateService.new(user, path: path, parent_id: parent.id).execute
|
||||
else
|
||||
parent_path = path
|
||||
|
||||
group = Group.find_by_full_path(parent_path) ||
|
||||
Groups::CreateService.new(user, path: path).execute
|
||||
end
|
||||
end
|
||||
|
||||
group
|
||||
end
|
||||
|
||||
Sidekiq::Testing.inline! do
|
||||
Gitlab::Seeder.quiet do
|
||||
project_urls = [
|
||||
'https://android.googlesource.com/platform/hardware/broadcom/libbt.git',
|
||||
'https://android.googlesource.com/platform/hardware/broadcom/wlan.git',
|
||||
'https://android.googlesource.com/platform/hardware/bsp/bootloader/intel/edison-u-boot.git',
|
||||
'https://android.googlesource.com/platform/hardware/bsp/broadcom.git',
|
||||
'https://android.googlesource.com/platform/hardware/bsp/freescale.git',
|
||||
'https://android.googlesource.com/platform/hardware/bsp/imagination.git',
|
||||
'https://android.googlesource.com/platform/hardware/bsp/intel.git',
|
||||
'https://android.googlesource.com/platform/hardware/bsp/kernel/common/v4.1.git',
|
||||
'https://android.googlesource.com/platform/hardware/bsp/kernel/common/v4.4.git'
|
||||
]
|
||||
|
||||
user = User.admins.first
|
||||
|
||||
project_urls.each_with_index do |url, i|
|
||||
full_path = url.sub('https://android.googlesource.com/', '')
|
||||
full_path = full_path.sub(/\.git\z/, '')
|
||||
full_path, _, project_path = full_path.rpartition('/')
|
||||
group = Group.find_by_full_path(full_path) || create_group_with_parents(user, full_path)
|
||||
|
||||
params = {
|
||||
import_url: url,
|
||||
namespace_id: group.id,
|
||||
path: project_path,
|
||||
name: project_path,
|
||||
description: FFaker::Lorem.sentence,
|
||||
visibility_level: Gitlab::VisibilityLevel.values.sample
|
||||
}
|
||||
|
||||
project = Projects::CreateService.new(user, params).execute
|
||||
project.send(:_run_after_commit_queue)
|
||||
|
||||
if project.valid?
|
||||
print '.'
|
||||
else
|
||||
print 'F'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,4 +1,6 @@
|
|||
class MigrateUsersNotificationLevel < ActiveRecord::Migration
|
||||
DOWNTIME = false
|
||||
|
||||
# Migrates only users who changed their default notification level :participating
|
||||
# creating a new record on notification settings table
|
||||
|
||||
|
|
|
@ -466,6 +466,46 @@ If Registry is enabled in your GitLab instance, but you don't need it for your
|
|||
project, you can disable it from your project's settings. Read the user guide
|
||||
on how to achieve that.
|
||||
|
||||
## Disable Container Registry but use GitLab as an auth endpoint
|
||||
|
||||
You can disable the embedded Container Registry to use an external one, but
|
||||
still use GitLab as an auth endpoint.
|
||||
|
||||
**Omnibus GitLab**
|
||||
1. Open `/etc/gitlab/gitlab.rb` and set necessary configurations:
|
||||
|
||||
```ruby
|
||||
registry['enable'] = false
|
||||
gitlab_rails['registry_enabled'] = true
|
||||
gitlab_rails['registry_host'] = "registry.gitlab.example.com"
|
||||
gitlab_rails['registry_port'] = "5005"
|
||||
gitlab_rails['registry_api_url'] = "http://localhost:5000"
|
||||
gitlab_rails['registry_key_path'] = "/var/opt/gitlab/gitlab-rails/certificate.key"
|
||||
gitlab_rails['registry_path'] = "/var/opt/gitlab/gitlab-rails/shared/registry"
|
||||
gitlab_rails['registry_issuer'] = "omnibus-gitlab-issuer"
|
||||
```
|
||||
|
||||
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
|
||||
|
||||
**Installations from source**
|
||||
|
||||
1. Open `/home/git/gitlab/config/gitlab.yml`, and edit the configuration settings under `registry`:
|
||||
|
||||
```
|
||||
## Container Registry
|
||||
|
||||
registry:
|
||||
enabled: true
|
||||
host: "registry.gitlab.example.com"
|
||||
port: "5005"
|
||||
api_url: "http://localhost:5000"
|
||||
path: /var/opt/gitlab/gitlab-rails/shared/registry
|
||||
key: /var/opt/gitlab/gitlab-rails/certificate.key
|
||||
issuer: omnibus-gitlab-issuer
|
||||
```
|
||||
|
||||
1. Save the file and [restart GitLab][] for the changes to take effect.
|
||||
|
||||
## Storage limitations
|
||||
|
||||
Currently, there is no storage limitation, which means a user can upload an
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue