Commit e097cf43 authored by Valery Sizov's avatar Valery Sizov

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into orderable-issues

parents 3b69196a f7b0805c
...@@ -23,6 +23,7 @@ AllCops: ...@@ -23,6 +23,7 @@ AllCops:
- 'tmp/**/*' - 'tmp/**/*'
- 'bin/**/*' - 'bin/**/*'
- 'generator_templates/**/*' - 'generator_templates/**/*'
- 'builds/**/*'
# Gems in consecutive lines should be alphabetically sorted # Gems in consecutive lines should be alphabetically sorted
Bundler/OrderedGems: Bundler/OrderedGems:
......
...@@ -2,6 +2,21 @@ ...@@ -2,6 +2,21 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. 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) ## 8.17.0 (2017-02-22)
- API: Fix file downloading. !0 (8267) - API: Fix file downloading. !0 (8267)
...@@ -182,6 +197,12 @@ entry. ...@@ -182,6 +197,12 @@ entry.
- Remove deprecated GitlabCiService. - Remove deprecated GitlabCiService.
- Requeue pending deletion projects. - 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) ## 8.16.6 (2017-02-17)
- API: Fix file downloading. !0 (8267) - API: Fix file downloading. !0 (8267)
......
...@@ -68,7 +68,7 @@ gem 'gollum-rugged_adapter', '~> 0.4.2', require: false ...@@ -68,7 +68,7 @@ gem 'gollum-rugged_adapter', '~> 0.4.2', require: false
gem 'github-linguist', '~> 4.7.0', require: 'linguist' gem 'github-linguist', '~> 4.7.0', require: 'linguist'
# API # API
gem 'grape', '~> 0.18.0' gem 'grape', '~> 0.19.0'
gem 'grape-entity', '~> 0.6.0' gem 'grape-entity', '~> 0.6.0'
gem 'rack-cors', '~> 0.4.0', require: 'rack/cors' gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
......
...@@ -304,7 +304,7 @@ GEM ...@@ -304,7 +304,7 @@ GEM
multi_json (~> 1.11) multi_json (~> 1.11)
os (~> 0.9) os (~> 0.9)
signet (~> 0.7) signet (~> 0.7)
grape (0.18.0) grape (0.19.1)
activesupport activesupport
builder builder
hashie (>= 2.1.0) hashie (>= 2.1.0)
...@@ -353,8 +353,8 @@ GEM ...@@ -353,8 +353,8 @@ GEM
json (~> 1.8) json (~> 1.8)
multi_xml (>= 0.5.2) multi_xml (>= 0.5.2)
httpclient (2.8.2) httpclient (2.8.2)
i18n (0.8.0) i18n (0.8.1)
ice_nine (0.11.1) ice_nine (0.11.2)
influxdb (0.2.3) influxdb (0.2.3)
cause cause
json json
...@@ -417,7 +417,7 @@ GEM ...@@ -417,7 +417,7 @@ GEM
minitest (5.7.0) minitest (5.7.0)
mousetrap-rails (1.4.6) mousetrap-rails (1.4.6)
multi_json (1.12.1) multi_json (1.12.1)
multi_xml (0.5.5) multi_xml (0.6.0)
multipart-post (2.0.0) multipart-post (2.0.0)
mustermann (0.4.0) mustermann (0.4.0)
tool (~> 0.2) tool (~> 0.2)
...@@ -758,7 +758,7 @@ GEM ...@@ -758,7 +758,7 @@ GEM
eventmachine (~> 1.0, >= 1.0.4) eventmachine (~> 1.0, >= 1.0.4)
rack (>= 1, < 3) rack (>= 1, < 3)
thor (0.19.4) thor (0.19.4)
thread_safe (0.3.5) thread_safe (0.3.6)
tilt (2.0.6) tilt (2.0.6)
timecop (0.8.1) timecop (0.8.1)
timfel-krb5-auth (0.8.3) timfel-krb5-auth (0.8.3)
...@@ -886,7 +886,7 @@ DEPENDENCIES ...@@ -886,7 +886,7 @@ DEPENDENCIES
gollum-rugged_adapter (~> 0.4.2) gollum-rugged_adapter (~> 0.4.2)
gon (~> 6.1.0) gon (~> 6.1.0)
google-api-client (~> 0.8.6) google-api-client (~> 0.8.6)
grape (~> 0.18.0) grape (~> 0.19.0)
grape-entity (~> 0.6.0) grape-entity (~> 0.6.0)
haml_lint (~> 0.21.0) haml_lint (~> 0.21.0)
hamlit (~> 2.6.1) hamlit (~> 2.6.1)
...@@ -1011,4 +1011,4 @@ DEPENDENCIES ...@@ -1011,4 +1011,4 @@ DEPENDENCIES
wikicloth (= 0.8.1) wikicloth (= 0.8.1)
BUNDLED WITH BUNDLED WITH
1.14.3 1.14.4
...@@ -7,8 +7,6 @@ ...@@ -7,8 +7,6 @@
/* global Aside */ /* global Aside */
window.$ = window.jQuery = require('jquery'); window.$ = window.jQuery = require('jquery');
require('jquery-ui/ui/draggable');
require('jquery-ui/ui/sortable');
require('jquery-ujs'); require('jquery-ujs');
require('vendor/jquery.endless-scroll'); require('vendor/jquery.endless-scroll');
require('vendor/jquery.highlight'); require('vendor/jquery.highlight');
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
var DOWN_BUILD_TRACE = '#down-build-trace'; var DOWN_BUILD_TRACE = '#down-build-trace';
this.Build = (function() { this.Build = (function() {
Build.interval = null; Build.timeout = null;
Build.state = null; Build.state = null;
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
this.$scrollBottomBtn = $('#scroll-bottom'); this.$scrollBottomBtn = $('#scroll-bottom');
this.$buildRefreshAnimation = $('.js-build-refresh'); this.$buildRefreshAnimation = $('.js-build-refresh');
clearInterval(Build.interval); clearTimeout(Build.timeout);
// Init breakpoint checker // Init breakpoint checker
this.bp = Breakpoints.get(); this.bp = Breakpoints.get();
...@@ -52,17 +52,7 @@ ...@@ -52,17 +52,7 @@
this.getInitialBuildTrace(); this.getInitialBuildTrace();
this.initScrollButtonAffix(); this.initScrollButtonAffix();
} }
if (this.buildStatus === "running" || this.buildStatus === "pending") { this.invokeBuildTrace();
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);
}
} }
Build.prototype.initSidebar = function() { Build.prototype.initSidebar = function() {
...@@ -75,6 +65,22 @@ ...@@ -75,6 +65,22 @@
return window.location.href.split("#")[0]; 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() { Build.prototype.getInitialBuildTrace = function() {
var removeRefreshStatuses = ['success', 'failed', 'canceled', 'skipped']; var removeRefreshStatuses = ['success', 'failed', 'canceled', 'skipped'];
...@@ -86,7 +92,7 @@ ...@@ -86,7 +92,7 @@
if (window.location.hash === DOWN_BUILD_TRACE) { if (window.location.hash === DOWN_BUILD_TRACE) {
$("html,body").scrollTop(this.$buildTrace.height()); $("html,body").scrollTop(this.$buildTrace.height());
} }
if (removeRefreshStatuses.indexOf(buildData.status) >= 0) { if (removeRefreshStatuses.indexOf(buildData.status) !== -1) {
this.$buildRefreshAnimation.remove(); this.$buildRefreshAnimation.remove();
return this.initScrollMonitor(); return this.initScrollMonitor();
} }
...@@ -105,6 +111,7 @@ ...@@ -105,6 +111,7 @@
if (log.state) { if (log.state) {
_this.state = log.state; _this.state = log.state;
} }
_this.invokeBuildTrace();
if (log.status === "running") { if (log.status === "running") {
if (log.append) { if (log.append) {
$('.js-build-output').append(log.html); $('.js-build-output').append(log.html);
......
...@@ -52,6 +52,30 @@ ...@@ -52,6 +52,30 @@
return this.views[viewMode].call(this); 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) { prepareFrames = function(view) {
var maxHeight, maxWidth; var maxHeight, maxWidth;
maxWidth = 0; maxWidth = 0;
...@@ -96,26 +120,30 @@ ...@@ -96,26 +120,30 @@
maxHeight = 0; maxHeight = 0;
return $('.swipe.view', this.file).each((function(_this) { return $('.swipe.view', this.file).each((function(_this) {
return function(index, view) { return function(index, view) {
var ref; var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding, ref;
ref = prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1]; 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, width: maxWidth + 16,
height: maxHeight + 28 height: maxHeight + 28
}); });
$('.swipe-wrap', view).css({ $swipeWrap.css({
width: maxWidth + 1, width: maxWidth + 1,
height: maxHeight + 2 height: maxHeight + 2
}); });
return $('.swipe-bar', view).css({ $swipeBar.css({
left: 0 left: 0
}).draggable({ });
axis: 'x',
containment: 'parent', wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10);
drag: function(event) {
return $('.swipe-wrap', view).width((maxWidth + 1) - $(this).position().left); _this.initDraggable($swipeBar, wrapPadding, function(e, left) {
}, if (left > 0 && left < $swipeFrame.width() - (wrapPadding * 2)) {
stop: function(event) { $swipeWrap.width((maxWidth + 1) - left);
return $('.swipe-wrap', view).width((maxWidth + 1) - $(this).position().left); $swipeBar.css('left', left);
} }
}); });
}; };
...@@ -128,9 +156,14 @@ ...@@ -128,9 +156,14 @@
dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width(); dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width();
return $('.onion-skin.view', this.file).each((function(_this) { return $('.onion-skin.view', this.file).each((function(_this) {
return function(index, view) { return function(index, view) {
var ref; var $frame, $track, $dragger, $frameAdded, framePadding, ref, dragging = false;
ref = prepareFrames(view), maxWidth = ref[0], maxHeight = ref[1]; 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, width: maxWidth + 16,
height: maxHeight + 28 height: maxHeight + 28
}); });
...@@ -138,16 +171,18 @@ ...@@ -138,16 +171,18 @@
width: maxWidth + 1, width: maxWidth + 1,
height: maxHeight + 2 height: maxHeight + 2
}); });
return $('.dragger', view).css({ $dragger.css({
left: dragTrackWidth left: dragTrackWidth
}).draggable({ });
axis: 'x',
containment: 'parent', framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10);
drag: function(event) {
return $('.frame.added', view).css('opacity', $(this).position().left / dragTrackWidth); _this.initDraggable($dragger, framePadding, function(e, left) {
}, var opacity = left / dragTrackWidth;
stop: function(event) {
return $('.frame.added', view).css('opacity', $(this).position().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', { ...@@ -145,7 +145,7 @@ module.exports = Vue.component('environment-component', {
</div> </div>
</div> </div>
<div class="environments-container"> <div class="content-list environments-container">
<div class="environments-list-loading text-center" v-if="isLoading"> <div class="environments-list-loading text-center" v-if="isLoading">
<i class="fa fa-spinner fa-spin"></i> <i class="fa fa-spinner fa-spin"></i>
</div> </div>
...@@ -181,12 +181,12 @@ module.exports = Vue.component('environment-component', { ...@@ -181,12 +181,12 @@ module.exports = Vue.component('environment-component', {
:terminal-icon-svg="terminalIconSvg" :terminal-icon-svg="terminalIconSvg"
:commit-icon-svg="commitIconSvg"> :commit-icon-svg="commitIconSvg">
</environment-table> </environment-table>
<table-pagination v-if="state.paginationInformation && state.paginationInformation.totalPages > 1"
:change="changePage"
:pageInfo="state.paginationInformation">
</table-pagination>
</div> </div>
<table-pagination v-if="state.paginationInformation && state.paginationInformation.totalPages > 1"
:change="changePage"
:pageInfo="state.paginationInformation">
</table-pagination>
</div> </div>
</div> </div>
`, `,
......
...@@ -503,32 +503,30 @@ module.exports = Vue.component('environment-item', { ...@@ -503,32 +503,30 @@ module.exports = Vue.component('environment-item', {
</span> </span>
</td> </td>
<td class="hidden-xs"> <td class="environments-actions">
<div v-if="!model.isFolder"> <div v-if="!model.isFolder" class="btn-group pull-right" role="group">
<div class="btn-group" role="group"> <actions-component v-if="hasManualActions && canCreateDeployment"
<actions-component v-if="hasManualActions && canCreateDeployment" :play-icon-svg="playIconSvg"
:play-icon-svg="playIconSvg" :actions="manualActions">
:actions="manualActions"> </actions-component>
</actions-component>
<external-url-component v-if="externalURL && canReadEnvironment"
<external-url-component v-if="externalURL && canReadEnvironment" :external-url="externalURL">
:external-url="externalURL"> </external-url-component>
</external-url-component>
<stop-component v-if="hasStopAction && canCreateDeployment"
<stop-component v-if="hasStopAction && canCreateDeployment" :stop-url="model.stop_path">
:stop-url="model.stop_path"> </stop-component>
</stop-component>
<terminal-button-component v-if="model && model.terminal_path"
<terminal-button-component v-if="model && model.terminal_path" :terminal-icon-svg="terminalIconSvg"
:terminal-icon-svg="terminalIconSvg" :terminal-path="model.terminal_path">
:terminal-path="model.terminal_path"> </terminal-button-component>
</terminal-button-component>
<rollback-component v-if="canRetry && canCreateDeployment"
<rollback-component v-if="canRetry && canCreateDeployment" :is-last-deployment="isLastDeployment"
:is-last-deployment="isLastDeployment" :retry-url="retryUrl">
:retry-url="retryUrl"> </rollback-component>
</rollback-component>
</div>
</div> </div>
</td> </td>
</tr> </tr>
......
...@@ -46,7 +46,7 @@ module.exports = Vue.component('environment-table-component', { ...@@ -46,7 +46,7 @@ module.exports = Vue.component('environment-table-component', {
}, },
template: ` template: `
<table class="table ci-table environments"> <table class="table ci-table">
<thead> <thead>
<tr> <tr>
<th class="environments-name">Environment</th> <th class="environments-name">Environment</th>
...@@ -54,7 +54,7 @@ module.exports = Vue.component('environment-table-component', { ...@@ -54,7 +54,7 @@ module.exports = Vue.component('environment-table-component', {
<th class="environments-build">Job</th> <th class="environments-build">Job</th>
<th class="environments-commit">Commit</th> <th class="environments-commit">Commit</th>
<th class="environments-date">Updated</th> <th class="environments-date">Updated</th>
<th class="hidden-xs environments-actions"></th> <th class="environments-actions"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
......
/* 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 */ /* 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 FilesCommentButton */
/* global notes */
(function() { (function() {
let $commentButtonTemplate;
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; }; var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
this.FilesCommentButton = (function() { 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_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_HOLDER_CLASS = '.line_holder';
LINE_NUMBER_CLASS = 'diff-line-num'; LINE_NUMBER_CLASS = 'diff-line-num';
...@@ -27,26 +27,29 @@ ...@@ -27,26 +27,29 @@
TEXT_FILE_SELECTOR = '.text-file'; TEXT_FILE_SELECTOR = '.text-file';
DEBOUNCE_TIMEOUT_DURATION = 100;
function FilesCommentButton(filesContainerElement) { function FilesCommentButton(filesContainerElement) {
var debounce;
this.filesContainerElement = filesContainerElement;
this.destroy = bind(this.destroy, this);
this.render = bind(this.render, this); this.render = bind(this.render, this);
this.VIEW_TYPE = $('input#view[type=hidden]').val(); this.hideButton = bind(this.hideButton, this);
debounce = _.debounce(this.render, DEBOUNCE_TIMEOUT_DURATION); this.isParallelView = notes.isParallelView();
$(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); filesContainerElement.on('mouseover', LINE_COLUMN_CLASSES, this.render)
.on('mouseleave', LINE_COLUMN_CLASSES, this.hideButton);
} }
FilesCommentButton.prototype.render = function(e) { FilesCommentButton.prototype.render = function(e) {
var $currentTarget, buttonParentElement, lineContentElement, textFileElement; var $currentTarget, buttonParentElement, lineContentElement, textFileElement, $button;
$currentTarget = $(e.currentTarget); $currentTarget = $(e.currentTarget);
buttonParentElement = this.getButtonParent($currentTarget);
if (!this.validateButtonParent(buttonParentElement)) return;
lineContentElement = this.getLineContent($currentTarget); 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); textFileElement = this.getTextFileElement($currentTarget);
buttonParentElement.append(this.buildButton({ buttonParentElement.append(this.buildButton({
...@@ -61,19 +64,16 @@ ...@@ -61,19 +64,16 @@
})); }));
}; };
FilesCommentButton.prototype.destroy = function(e) { FilesCommentButton.prototype.hideButton = function(e) {
if (this.isMovingToSameType(e)) { var $currentTarget = $(e.currentTarget);
return; var buttonParentElement = this.getButtonParent($currentTarget);
}
$(COMMENT_BUTTON_CLASS, this.getButtonParent($(e.currentTarget))).remove(); buttonParentElement.removeClass('is-over')
.nextUntil(`.${LINE_CONTENT_CLASS}`).removeClass('is-over');
}; };
FilesCommentButton.prototype.buildButton = function(buttonAttributes) { FilesCommentButton.prototype.buildButton = function(buttonAttributes) {
var initializedButtonTemplate; return $commentButtonTemplate.clone().attr({
initializedButtonTemplate = COMMENT_BUTTON_TEMPLATE({
COMMENT_BUTTON_CLASS: COMMENT_BUTTON_CLASS.substr(1)
});
return $(initializedButtonTemplate).attr({
'data-noteable-type': buttonAttributes.noteableType, 'data-noteable-type': buttonAttributes.noteableType,
'data-noteable-id': buttonAttributes.noteableID, 'data-noteable-id': buttonAttributes.noteableID,
'data-commit-id': buttonAttributes.commitID, 'data-commit-id': buttonAttributes.commitID,
...@@ -86,14 +86,14 @@ ...@@ -86,14 +86,14 @@
}; };
FilesCommentButton.prototype.getTextFileElement = function(hoveredElement) { FilesCommentButton.prototype.getTextFileElement = function(hoveredElement) {
return $(hoveredElement.closest(TEXT_FILE_SELECTOR)); return hoveredElement.closest(TEXT_FILE_SELECTOR);
}; };
FilesCommentButton.prototype.getLineContent = function(hoveredElement) { FilesCommentButton.prototype.getLineContent = function(hoveredElement) {
if (hoveredElement.hasClass(LINE_CONTENT_CLASS)) { if (hoveredElement.hasClass(LINE_CONTENT_CLASS)) {
return hoveredElement; return hoveredElement;
} }
if (this.VIEW_TYPE === 'inline') { if (!this.isParallelView) {
return $(hoveredElement).closest(LINE_HOLDER_CLASS).find("." + LINE_CONTENT_CLASS); return $(hoveredElement).closest(LINE_HOLDER_CLASS).find("." + LINE_CONTENT_CLASS);
} else { } else {
return $(hoveredElement).next("." + LINE_CONTENT_CLASS); return $(hoveredElement).next("." + LINE_CONTENT_CLASS);
...@@ -101,7 +101,7 @@ ...@@ -101,7 +101,7 @@
}; };
FilesCommentButton.prototype.getButtonParent = function(hoveredElement) { FilesCommentButton.prototype.getButtonParent = function(hoveredElement) {
if (this.VIEW_TYPE === 'inline') { if (!this.isParallelView) {
if (hoveredElement.hasClass(OLD_LINE_CLASS)) { if (hoveredElement.hasClass(OLD_LINE_CLASS)) {
return hoveredElement; return hoveredElement;
} }
...@@ -114,17 +114,8 @@ ...@@ -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) { 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) { FilesCommentButton.prototype.validateLineContent = function(lineContentElement) {
...@@ -135,6 +126,8 @@ ...@@ -135,6 +126,8 @@
})(); })();
$.fn.filesCommentButton = function() { $.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))) { if (!(this && (this.parent().data('can-create-note') != null))) {
return; return;
} }
......
...@@ -48,7 +48,11 @@ ...@@ -48,7 +48,11 @@
} }
setOffset(offset = 0) { 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) { renderContent(forceShowList = false) {
......
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
} }
GitLabDropdownFilter.prototype.shouldBlur = function(keyCode) { GitLabDropdownFilter.prototype.shouldBlur = function(keyCode) {
return BLUR_KEYCODES.indexOf(keyCode) >= 0; return BLUR_KEYCODES.indexOf(keyCode) !== -1;
}; };
GitLabDropdownFilter.prototype.filter = function(search_text) { GitLabDropdownFilter.prototype.filter = function(search_text) {
...@@ -605,7 +605,7 @@ ...@@ -605,7 +605,7 @@
var occurrences; var occurrences;
occurrences = fuzzaldrinPlus.match(text, term); occurrences = fuzzaldrinPlus.match(text, term);
return text.split('').map(function(character, i) { return text.split('').map(function(character, i) {
if (indexOf.call(occurrences, i) >= 0) { if (indexOf.call(occurrences, i) !== -1) {
return "<b>" + character + "</b>"; return "<b>" + character + "</b>";
} else { } else {
return character; return character;
...@@ -748,7 +748,7 @@ ...@@ -748,7 +748,7 @@
return function(e) { return function(e) {
var $listItems, PREV_INDEX, currentKeyCode; var $listItems, PREV_INDEX, currentKeyCode;
currentKeyCode = e.which; currentKeyCode = e.which;
if (ARROW_KEY_CODES.indexOf(currentKeyCode) >= 0) { if (ARROW_KEY_CODES.indexOf(currentKeyCode) !== -1) {
e.preventDefault(); e.preventDefault();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
PREV_INDEX = currentIndex; PREV_INDEX = currentIndex;
......
...@@ -116,7 +116,7 @@ ...@@ -116,7 +116,7 @@
formData = $.param(formData); formData = $.param(formData);
formAction = form.attr('action'); formAction = form.attr('action');
issuesUrl = formAction; issuesUrl = formAction;
issuesUrl += "" + (formAction.indexOf('?') < 0 ? '?' : '&'); issuesUrl += "" + (formAction.indexOf('?') === -1 ? '?' : '&');
issuesUrl += formData; issuesUrl += formData;
return gl.utils.visitUrl(issuesUrl); return gl.utils.visitUrl(issuesUrl);
}; };
......
...@@ -329,17 +329,18 @@ ...@@ -329,17 +329,18 @@
* ``` * ```
*/ */
w.gl.utils.backOff = (fn, timeout = 60000) => { w.gl.utils.backOff = (fn, timeout = 60000) => {
const maxInterval = 32000;
let nextInterval = 2000; let nextInterval = 2000;
const startTime = (+new Date()); const startTime = Date.now();
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const stop = arg => ((arg instanceof Error) ? reject(arg) : resolve(arg)); const stop = arg => ((arg instanceof Error) ? reject(arg) : resolve(arg));
const next = () => { const next = () => {
if (new Date().getTime() - startTime < timeout) { if (Date.now() - startTime < timeout) {
setTimeout(fn.bind(null, next, stop), nextInterval); setTimeout(fn.bind(null, next, stop), nextInterval);
nextInterval *= 2; nextInterval = Math.min(nextInterval + nextInterval, maxInterval);
} else { } else {
reject(new Error('BACKOFF_TIMEOUT')); reject(new Error('BACKOFF_TIMEOUT'));
} }
......
/**
* exports HTTP status codes
*/
const statusCodes = {
NO_CONTENT: 204,
OK: 200,
};
module.exports = statusCodes;
...@@ -83,7 +83,7 @@ require('./smart_interval'); ...@@ -83,7 +83,7 @@ require('./smart_interval');
return function() { return function() {
var page; var page;
page = $('body').data('page').split(':').last(); page = $('body').data('page').split(':').last();
if (allowedPages.indexOf(page) < 0) { if (allowedPages.indexOf(page) === -1) {
return _this.clearEventListeners(); return _this.clearEventListeners();
} }
}; };
...@@ -233,7 +233,7 @@ require('./smart_interval'); ...@@ -233,7 +233,7 @@ require('./smart_interval');
} }
$('.ci_widget').hide(); $('.ci_widget').hide();
allowed_states = ["failed", "canceled", "running", "pending", "success", "success_with_warnings", "skipped", "not_found"]; 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(); $('.ci_widget.ci-' + state).show();
switch (state) { switch (state) {
case "failed": case "failed":
......
...@@ -81,7 +81,7 @@ ...@@ -81,7 +81,7 @@
var errorMessage, errors, formatter, unique, validator; var errorMessage, errors, formatter, unique, validator;
this.branchNameError.empty(); this.branchNameError.empty();
unique = function(values, value) { unique = function(values, value) {
if (indexOf.call(values, value) < 0) { if (indexOf.call(values, value) === -1) {
values.push(value); values.push(value);
} }
return values; return values;
......
...@@ -84,13 +84,14 @@ ...@@ -84,13 +84,14 @@
} }
$(function() { $(function() {
$(document).on('focusout.ssh_key', '#key_key', function() { $(document).on('input.ssh_key', '#key_key', function() {
const $title = $('#key_title'); const $title = $('#key_title');
const comment = $(this).val().match(/^\S+ \S+ (.+)\n?$/); 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(); return $title.val(comment[1]).change();
} }
// Extract the SSH Key title from its comment
}); });
if (global.utils.getPagePath() === 'profiles') { if (global.utils.getPagePath() === 'profiles') {
return new Profile(); return new Profile();
......
...@@ -116,7 +116,7 @@ ...@@ -116,7 +116,7 @@
if ($('input[name="ref"]').length) { if ($('input[name="ref"]').length) {
var $form = $dropdown.closest('form'); var $form = $dropdown.closest('form');
var action = $form.attr('action'); var action = $form.attr('action');
var divider = action.indexOf('?') < 0 ? '?' : '&'; var divider = action.indexOf('?') === -1 ? '?' : '&';
gl.utils.visitUrl(action + '' + divider + '' + $form.serialize()); gl.utils.visitUrl(action + '' + divider + '' + $form.serialize());
} }
} }
......
/* global Flash */
require('vendor/task_list'); require('vendor/task_list');
class TaskList { class TaskList {
...@@ -6,6 +7,16 @@ class TaskList { ...@@ -6,6 +7,16 @@ class TaskList {
this.dataType = options.dataType; this.dataType = options.dataType;
this.fieldName = options.fieldName; this.fieldName = options.fieldName;
this.onSuccess = options.onSuccess || (() => {}); 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(); this.init();
} }
...@@ -32,6 +43,7 @@ class TaskList { ...@@ -32,6 +43,7 @@ class TaskList {
url: $target.data('update-url') || $('form.js-issuable-update').attr('action'), url: $target.data('update-url') || $('form.js-issuable-update').attr('action'),
data: patchData, data: patchData,
success: this.onSuccess, success: this.onSuccess,
error: this.onError,
}); });
} }
} }
......
...@@ -32,19 +32,17 @@ ...@@ -32,19 +32,17 @@
}, },
}, },
template: ` template: `
<td class="pipeline-actions hidden-xs"> <td class="pipeline-actions">
<div class="controls pull-right"> <div class="pull-right">
<div class="btn-group inline"> <div class="btn-group">
<div class="btn-group"> <div class="btn-group" v-if="actions">
<button <button
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 job" title="Manual job"
data-placement="top" data-placement="top"
aria-label="Manual job" aria-label="Manual job">
> <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>
</button> </button>
<ul class="dropdown-menu dropdown-menu-align-right"> <ul class="dropdown-menu dropdown-menu-align-right">
...@@ -52,23 +50,21 @@ ...@@ -52,23 +50,21 @@
<a <a
rel="nofollow" rel="nofollow"
data-method="post" data-method="post"
:href='action.path' :href="action.path">
> <span v-html="svgs.iconPlay" aria-hidden="true"></span>
<span v-html='svgs.iconPlay' aria-hidden="true"></span>
<span>{{action.name}}</span> <span>{{action.name}}</span>
</a> </a>
</li> </li>
</ul> </ul>
</div> </div>
<div class="btn-group">
<div class="btn-group" v-if="artifacts">
<button <button
v-if='artifacts'
class="dropdown-toggle btn btn-default build-artifacts has-tooltip js-pipeline-dropdown-download" class="dropdown-toggle btn btn-default build-artifacts has-tooltip js-pipeline-dropdown-download"
title="Artifacts" title="Artifacts"
data-placement="top" data-placement="top"
data-toggle="dropdown" 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>
<i class="fa fa-caret-down" aria-hidden="true"></i> <i class="fa fa-caret-down" aria-hidden="true"></i>
</button> </button>
...@@ -76,42 +72,39 @@ ...@@ -76,42 +72,39 @@
<li v-for='artifact in pipeline.details.artifacts'> <li v-for='artifact in pipeline.details.artifacts'>
<a <a
rel="nofollow" rel="nofollow"
download :href="artifact.path">
:href='artifact.path'
>
<i class="fa fa-download" aria-hidden="true"></i> <i class="fa fa-download" aria-hidden="true"></i>
<span>{{download(artifact.name)}}</span> <span>{{download(artifact.name)}}</span>
</a> </a>
</li> </li>
</ul> </ul>
</div> </div>
</div> <div class="btn-group" v-if="pipeline.flags.retryable">
<div class="cancel-retry-btns inline"> <a
<a class="btn btn-default btn-retry has-tooltip"
v-if='pipeline.flags.retryable' title="Retry"
class="btn has-tooltip" rel="nofollow"
title="Retry" data-method="post"
rel="nofollow" data-placement="top"
data-method="post" data-toggle="dropdown"
data-placement="top" :href='pipeline.retry_path'
data-toggle="dropdown" aria-label="Retry">
:href='pipeline.retry_path' <i class="fa fa-repeat" aria-hidden="true"></i>
aria-label="Retry"> </a>
<i class="fa fa-repeat" aria-hidden="true"></i> </div>
</a> <div class="btn-group" v-if="pipeline.flags.cancelable">
<a <a
v-if='pipeline.flags.cancelable' class="btn btn-remove has-tooltip"
@click="confirmAction" title="Cancel"
class="btn btn-remove has-tooltip" rel="nofollow"
title="Cancel" data-method="post"
rel="nofollow" data-placement="top"
data-method="post" data-toggle="dropdown"
data-placement="top" :href='pipeline.cancel_path'
data-toggle="dropdown" aria-label="Cancel">
:href='pipeline.cancel_path' <i class="fa fa-remove" aria-hidden="true"></i>
aria-label="Cancel"> </a>
<i class="fa fa-remove" aria-hidden="true"></i> </div>
</a>
</div> </div>
</div> </div>
</td> </td>
......
...@@ -54,7 +54,7 @@ require('../lib/utils/datetime_utility'); ...@@ -54,7 +54,7 @@ require('../lib/utils/datetime_utility');
}, },
}, },
template: ` template: `
<td> <td class="pipelines-time-ago">
<p class="duration" v-if='duration'> <p class="duration" v-if='duration'>
<span v-html='svgs.iconTimer'></span> <span v-html='svgs.iconTimer'></span>
{{duration}} {{duration}}
...@@ -65,8 +65,7 @@ require('../lib/utils/datetime_utility'); ...@@ -65,8 +65,7 @@ require('../lib/utils/datetime_utility');
data-toggle="tooltip" data-toggle="tooltip"
data-placement="top" data-placement="top"
data-container="body" data-container="body"
:data-original-title='localTimeFinished' :data-original-title='localTimeFinished'>
>
{{timeStopped.words}} {{timeStopped.words}}
</time> </time>
</p> </p>
......
...@@ -44,7 +44,7 @@ require('./pipelines_table_row'); ...@@ -44,7 +44,7 @@ require('./pipelines_table_row');
<th class="js-pipeline-commit pipeline-commit">Commit</th> <th class="js-pipeline-commit pipeline-commit">Commit</th>
<th class="js-pipeline-stages pipeline-stages">Stages</th> <th class="js-pipeline-stages pipeline-stages">Stages</th>
<th class="js-pipeline-date pipeline-date"></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> </tr>
</thead> </thead>
<tbody> <tbody>
......
...@@ -229,7 +229,7 @@ ...@@ -229,7 +229,7 @@
.controls { .controls {
float: right; float: right;
margin-top: 8px; margin-top: 8px;
padding-bottom: 7px; padding-bottom: 8px;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
} }
} }
......
...@@ -26,6 +26,11 @@ ...@@ -26,6 +26,11 @@
.filtered-search-container { .filtered-search-container {
display: -webkit-flex; display: -webkit-flex;
display: flex; display: flex;
@media (max-width: $screen-xs-min) {
-webkit-flex-direction: column;
flex-direction: column;
}
} }
.filtered-search-input-container { .filtered-search-input-container {
...@@ -34,6 +39,20 @@ ...@@ -34,6 +39,20 @@
position: relative; position: relative;
width: 100%; 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 { .form-control {
padding-left: 25px; padding-left: 25px;
padding-right: 25px; padding-right: 25px;
...@@ -79,6 +98,31 @@ ...@@ -79,6 +98,31 @@
overflow: auto; 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 { %filter-dropdown-item-btn-hover {
background-color: $dropdown-hover-color; background-color: $dropdown-hover-color;
color: $white-light; color: $white-light;
...@@ -148,4 +192,4 @@ ...@@ -148,4 +192,4 @@
.filter-dropdown-loading { .filter-dropdown-loading {
padding: 8px 16px; padding: 8px 16px;
} }
\ No newline at end of file
...@@ -148,16 +148,11 @@ header { ...@@ -148,16 +148,11 @@ header {
} }
.header-logo { .header-logo {
position: absolute; display: inline-block;
left: 50%; margin: 0 8px 0 3px;
position: relative;
top: 7px; top: 7px;
transition-duration: .3s; transition-duration: .3s;
z-index: 999;
#logo {
position: relative;
left: -50%;
}
svg, svg,
img { img {
...@@ -167,15 +162,6 @@ header { ...@@ -167,15 +162,6 @@ header {
&:hover { &:hover {
cursor: pointer; cursor: pointer;
} }
@media (max-width: $screen-xs-max) {
right: 20px;
left: auto;
#logo {
left: auto;
}
}
} }
.title { .title {
...@@ -183,7 +169,6 @@ header { ...@@ -183,7 +169,6 @@ header {
padding-right: 20px; padding-right: 20px;
margin: 0; margin: 0;
font-size: 18px; font-size: 18px;
max-width: 385px;
display: inline-block; display: inline-block;
line-height: $header-height; line-height: $header-height;
font-weight: normal; font-weight: normal;
...@@ -193,14 +178,18 @@ header { ...@@ -193,14 +178,18 @@ header {
vertical-align: top; vertical-align: top;
white-space: nowrap; white-space: nowrap;
@media (min-width: $screen-sm-min) and (max-width: $screen-sm-max) {
max-width: 300px;
}
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
max-width: 190px; 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 { a {
color: $gl-text-color; color: $gl-text-color;
......
...@@ -20,6 +20,7 @@ $dark-highlight-bg: #ffe792; ...@@ -20,6 +20,7 @@ $dark-highlight-bg: #ffe792;
$dark-highlight-color: $black; $dark-highlight-color: $black;
$dark-pre-hll-bg: #373b41; $dark-pre-hll-bg: #373b41;
$dark-hll-bg: #373b41; $dark-hll-bg: #373b41;
$dark-over-bg: #9f9ab5;
$dark-c: #969896; $dark-c: #969896;
$dark-err: #c66; $dark-err: #c66;
$dark-k: #b294bb; $dark-k: #b294bb;
...@@ -139,6 +140,18 @@ $dark-il: #de935f; ...@@ -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 { .line_content.match {
@include dark-diff-match-line; @include dark-diff-match-line;
} }
......
...@@ -13,6 +13,7 @@ $monokai-line-empty-bg: #49483e; ...@@ -13,6 +13,7 @@ $monokai-line-empty-bg: #49483e;
$monokai-line-empty-border: darken($monokai-line-empty-bg, 15%); $monokai-line-empty-border: darken($monokai-line-empty-bg, 15%);
$monokai-diff-border: #808080; $monokai-diff-border: #808080;
$monokai-highlight-bg: #ffe792; $monokai-highlight-bg: #ffe792;
$monokai-over-bg: #9f9ab5;
$monokai-new-bg: rgba(166, 226, 46, 0.1); $monokai-new-bg: rgba(166, 226, 46, 0.1);
$monokai-new-idiff: rgba(166, 226, 46, 0.15); $monokai-new-idiff: rgba(166, 226, 46, 0.15);
...@@ -139,6 +140,18 @@ $monokai-gi: #a6e22e; ...@@ -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 { .line_content.match {
@include dark-diff-match-line; @include dark-diff-match-line;
} }
......
...@@ -17,6 +17,7 @@ $solarized-dark-line-color-new: #5a766c; ...@@ -17,6 +17,7 @@ $solarized-dark-line-color-new: #5a766c;
$solarized-dark-line-color-old: #7a6c71; $solarized-dark-line-color-old: #7a6c71;
$solarized-dark-highlight: #094554; $solarized-dark-highlight: #094554;
$solarized-dark-hll-bg: #174652; $solarized-dark-hll-bg: #174652;
$solarized-dark-over-bg: #9f9ab5;
$solarized-dark-c: #586e75; $solarized-dark-c: #586e75;
$solarized-dark-err: #93a1a1; $solarized-dark-err: #93a1a1;
$solarized-dark-g: #93a1a1; $solarized-dark-g: #93a1a1;
...@@ -143,6 +144,18 @@ $solarized-dark-il: #2aa198; ...@@ -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 { .line_content.match {
@include dark-diff-match-line; @include dark-diff-match-line;
} }
......
...@@ -18,6 +18,7 @@ $solarized-light-line-color-new: #a1a080; ...@@ -18,6 +18,7 @@ $solarized-light-line-color-new: #a1a080;
$solarized-light-line-color-old: #ad9186; $solarized-light-line-color-old: #ad9186;
$solarized-light-highlight: #eee8d5; $solarized-light-highlight: #eee8d5;
$solarized-light-hll-bg: #ddd8c5; $solarized-light-hll-bg: #ddd8c5;
$solarized-light-over-bg: #ded7fc;
$solarized-light-c: #93a1a1; $solarized-light-c: #93a1a1;
$solarized-light-err: #586e75; $solarized-light-err: #586e75;
$solarized-light-g: #586e75; $solarized-light-g: #586e75;
...@@ -150,6 +151,18 @@ $solarized-light-il: #2aa198; ...@@ -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 { .line_content.match {
@include matchLine; @include matchLine;
} }
......
...@@ -7,6 +7,7 @@ $white-code-color: $gl-text-color; ...@@ -7,6 +7,7 @@ $white-code-color: $gl-text-color;
$white-highlight: #fafe3d; $white-highlight: #fafe3d;
$white-pre-hll-bg: #f8eec7; $white-pre-hll-bg: #f8eec7;
$white-hll-bg: #f8f8f8; $white-hll-bg: #f8f8f8;
$white-over-bg: #ded7fc;
$white-c: #998; $white-c: #998;
$white-err: #a61717; $white-err: #a61717;
$white-err-bg: #e3d2d2; $white-err-bg: #e3d2d2;
...@@ -123,6 +124,16 @@ $white-gc-bg: #eaf2f5; ...@@ -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) { &.hll:not(.empty-cell) {
background-color: $line-number-select; background-color: $line-number-select;
border-color: $line-select-yellow-dark; border-color: $line-select-yellow-dark;
......
...@@ -89,6 +89,10 @@ ...@@ -89,6 +89,10 @@
.diff-line-num { .diff-line-num {
width: 50px; width: 50px;
a {
transition: none;
}
} }
.line_holder td { .line_holder td {
...@@ -109,10 +113,6 @@ ...@@ -109,10 +113,6 @@
td.line_content.parallel { td.line_content.parallel {
width: 46%; width: 46%;
} }
.add-diff-note {
margin-left: -65px;
}
} }
.old_line, .old_line,
......
...@@ -15,112 +15,97 @@ ...@@ -15,112 +15,97 @@
padding-top: 20px; padding-top: 20px;
} }
@media (max-width: $screen-xs-max) { .environments-container {
.environments-container { .table-holder {
width: 100%; width: 100%;
overflow: auto; 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, .table.ci-table {
.environments-deploy, .environments-actions {
.environments-build { min-width: 200px;
width: 15%;
}
.environment-name,
.environments-build-cell,
.deployment-column {
word-break: break-all;
}
.deployment-column {
.avatar {
float: none;
} }
}
.btn-group { .environments-commit,
.environments-actions {
width: 20%;
}
> a { .environments-date {
color: $gl-text-color-secondary; width: 10%;
} }
svg path { .environments-name,
fill: $gl-text-color-secondary; .environments-deploy,
.environments-build {
width: 15%;
} }
.dropdown { .deployment-column {
outline: none; > span {
word-break: break-all;
}
.avatar {
float: none;
}
} }
}
.btn-group {
.commit-title { > a {
margin: 0; color: $gl-text-color-secondary;
} }
.avatar-image-container { svg path {
text-decoration: none; fill: $gl-text-color-secondary;
} }
.icon-play { .dropdown {
height: 13px; outline: none;
width: 12px; }
} }
.external-url, .commit-title {
.dropdown-new { margin: 0;
color: $gl-text-color-secondary; }
}
.dropdown-menu { .avatar-image-container {
text-decoration: none;
}
.fa { .icon-play {
margin-right: 6px; height: 13px;
color: $gl-text-color-secondary; width: 12px;
} }
}
.build-link, .external-url,
.branch-name { .dropdown-new {
color: $gl-text-color; color: $gl-text-color-secondary;
} }
.stop-env-link, .dropdown-menu {
.external-url { .fa {
color: $gl-text-color-secondary; margin-right: 6px;
color: $gl-text-color-secondary;
}
}
.stop-env-icon { .build-link,
font-size: 14px; .branch-name {
color: $gl-text-color;
} }
}
.deployment { .stop-env-link,
.build-column { .external-url {
color: $gl-text-color-secondary;
.stop-env-icon {
font-size: 14px;
}
}
.deployment .build-column {
.build-link { .build-link {
color: $gl-text-color; color: $gl-text-color;
} }
...@@ -129,34 +114,32 @@ ...@@ -129,34 +114,32 @@
float: none; float: none;
} }
} }
}
.folder-icon {
margin-right: 3px;
color: $gl-text-color-secondary;
display: inline-block;
.fa:nth-child(1) { .folder-icon {
margin-right: 3px; margin-right: 3px;
color: $gl-text-color-secondary;
display: inline-block;
.fa:nth-child(1) {
margin-right: 3px;
}
} }
}
.folder-name { .folder-name {
cursor: pointer; cursor: pointer;
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
display: inline-block; display: inline-block;
} }
}
.table.ci-table.environments { .icon-container {
.icon-container { width: 20px;
width: 20px; text-align: center;
text-align: center; }
}
.branch-commit { .branch-commit {
.commit-id { .commit-id {
margin-right: 0; margin-right: 0;
}
} }
} }
} }
...@@ -452,36 +452,37 @@ ul.notes { ...@@ -452,36 +452,37 @@ ul.notes {
* Line note button on the side of diffs * Line note button on the side of diffs
*/ */
.diff-file tr.line_holder { .add-diff-note {
@mixin show-add-diff-note { display: none;
display: inline-block; 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;
.add-diff-note { &:hover {
margin-top: -8px; background: $gl-info;
border-radius: 40px; color: $white-light;
background: $white-light; transform: scale(1.15);
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 { &:active {
background: $gl-info; outline: 0;
color: $white-light;
@include show-add-diff-note;
}
} }
}
// "show" the icon also if we just hover somewhere over the line .diff-file {
&:hover > td { .is-over {
.add-diff-note { .add-diff-note {
@include show-add-diff-note; display: inline-block;
} }
} }
} }
......
...@@ -13,21 +13,16 @@ ...@@ -13,21 +13,16 @@
white-space: nowrap; white-space: nowrap;
} }
.commit-title { .table-holder {
margin: 0; width: 100%;
} overflow: auto;
.controls {
white-space: nowrap;
} }
.btn { .commit-title {
margin: 4px; margin: 0;
} }
.table.ci-table { .table.ci-table {
min-width: 1200px;
table-layout: fixed;
.label { .label {
margin-bottom: 3px; margin-bottom: 3px;
...@@ -37,16 +32,72 @@ ...@@ -37,16 +32,72 @@
color: $black; color: $black;
} }
.pipeline-date, .stage-cell {
.pipeline-status { min-width: 130px; // Guarantees we show at least 4 stages in line
width: 10%; width: 20%;
}
.pipelines-time-ago {
text-align: right;
} }
.pipeline-info,
.pipeline-commit,
.pipeline-stages,
.pipeline-actions { .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 @@ ...@@ -54,6 +105,7 @@
@media (max-width: $screen-md-max) { @media (max-width: $screen-md-max) {
.content-list { .content-list {
&.pipelines, &.pipelines,
&.environments-container,
&.builds-content-list { &.builds-content-list {
width: 100%; width: 100%;
overflow: auto; overflow: auto;
...@@ -61,27 +113,10 @@ ...@@ -61,27 +113,10 @@
} }
} }
.content-list.pipelines .table-holder {
min-height: 300px;
}
.pipeline-holder {
width: 100%;
overflow: auto;
}
.table.ci-table { .table.ci-table {
min-width: 900px;
&.pipeline {
min-width: 650px;
}
&.builds-page {
tr { &.builds-page tr {
height: 71px; height: 71px;
}
} }
tr { tr {
...@@ -94,12 +129,16 @@ ...@@ -94,12 +129,16 @@
padding: 10px 8px; padding: 10px 8px;
} }
td.environments-actions {
padding-right: 0;
}
td.stage-cell { td.stage-cell {
padding: 10px 0; padding: 10px 0;
} }
.commit-link { .commit-link {
padding: 9px 8px 10px; padding: 9px 8px 10px 2px;
} }
} }
...@@ -206,72 +245,8 @@ ...@@ -206,72 +245,8 @@
} }
} }
.pipeline-actions { .build-link a {
min-width: 140px; color: $gl-text-color;
.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;
}
} }
.btn-group.open .dropdown-toggle { .btn-group.open .dropdown-toggle {
...@@ -335,31 +310,8 @@ ...@@ -335,31 +310,8 @@
} }
.tab-pane { .tab-pane {
&.pipelines { &.builds .ci-table tr {
.ci-table { height: 71px;
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;
}
}
} }
} }
......
...@@ -638,14 +638,6 @@ pre.light-well { ...@@ -638,14 +638,6 @@ pre.light-well {
margin: 0; margin: 0;
} }
.activity-filter-block {
.controls {
padding-bottom: 7px;
margin-top: 8px;
border-bottom: 1px solid $border-color;
}
}
.commits-search-form { .commits-search-form {
.input-short { .input-short {
min-width: 200px; min-width: 200px;
......
...@@ -26,6 +26,23 @@ module IssuableActions ...@@ -26,6 +26,23 @@ module IssuableActions
private 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 def labels
@labels ||= LabelsFinder.new(current_user, project_id: @project.id).execute @labels ||= LabelsFinder.new(current_user, project_id: @project.id).execute
end end
......
...@@ -33,6 +33,7 @@ module ServiceParams ...@@ -33,6 +33,7 @@ module ServiceParams
:issues_url, :issues_url,
:jira_issue_transition_id, :jira_issue_transition_id,
:merge_requests_events, :merge_requests_events,
:mock_service_url,
:namespace, :namespace,
:new_issue_url, :new_issue_url,
:notify, :notify,
......
...@@ -134,8 +134,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -134,8 +134,7 @@ class Projects::IssuesController < Projects::ApplicationController
end end
rescue ActiveRecord::StaleObjectError rescue ActiveRecord::StaleObjectError
@conflict = true render_conflict_response
render :edit
end end
def referenced_merge_requests def referenced_merge_requests
......
...@@ -296,22 +296,21 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -296,22 +296,21 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def update def update
@merge_request = MergeRequests::UpdateService.new(project, current_user, merge_request_params).execute(@merge_request) @merge_request = MergeRequests::UpdateService.new(project, current_user, merge_request_params).execute(@merge_request)
if @merge_request.valid? respond_to do |format|
respond_to do |format| format.html do
format.html do if @merge_request.valid?
redirect_to([@merge_request.target_project.namespace.becomes(Namespace), redirect_to([@merge_request.target_project.namespace.becomes(Namespace), @merge_request.target_project, @merge_request])
@merge_request.target_project, @merge_request]) else
end 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
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 end
rescue ActiveRecord::StaleObjectError rescue ActiveRecord::StaleObjectError
@conflict = true render_conflict_response
render :edit
end end
def remove_wip def remove_wip
......
...@@ -15,4 +15,11 @@ module BuildsHelper ...@@ -15,4 +15,11 @@ module BuildsHelper
log_state: @build.trace_with_state[:state].to_s log_state: @build.trace_with_state[:state].to_s
} }
end end
def build_failed_issue_options
{
title: "Build Failed ##{@build.id}",
description: namespace_project_build_url(@project.namespace, @project, @build)
}
end
end end
...@@ -34,7 +34,7 @@ module ButtonHelper ...@@ -34,7 +34,7 @@ module ButtonHelper
content_tag (append_link ? :a : :span), protocol, content_tag (append_link ? :a : :span), protocol,
class: klass, class: klass,
href: (project.http_url_to_repo if append_link), href: (project.http_url_to_repo(current_user) if append_link),
data: { data: {
html: true, html: true,
placement: placement, placement: placement,
......
...@@ -241,7 +241,7 @@ module ProjectsHelper ...@@ -241,7 +241,7 @@ module ProjectsHelper
when 'ssh' when 'ssh'
project.ssh_url_to_repo project.ssh_url_to_repo
else else
project.http_url_to_repo project.http_url_to_repo(current_user)
end end
end end
......
...@@ -91,10 +91,6 @@ class MergeRequest < ActiveRecord::Base ...@@ -91,10 +91,6 @@ class MergeRequest < ActiveRecord::Base
around_transition do |merge_request, transition, block| around_transition do |merge_request, transition, block|
Gitlab::Timeless.timeless(merge_request, &block) Gitlab::Timeless.timeless(merge_request, &block)
end end
after_transition unchecked: :cannot_be_merged do |merge_request, transition|
TodoService.new.merge_request_became_unmergeable(merge_request)
end
end end
validates :source_project, presence: true, unless: [:allow_broken, :importing?, :closed_without_fork?] validates :source_project, presence: true, unless: [:allow_broken, :importing?, :closed_without_fork?]
......
...@@ -869,8 +869,14 @@ class Project < ActiveRecord::Base ...@@ -869,8 +869,14 @@ class Project < ActiveRecord::Base
url_to_repo url_to_repo
end end
def http_url_to_repo def http_url_to_repo(user = nil)
"#{web_url}.git" url = web_url
if user
url.sub!(%r{\Ahttps?://}) { |protocol| "#{protocol}#{user.username}@" }
end
"#{url}.git"
end end
# Check if current branch name is marked as protected in the system # Check if current branch name is marked as protected in the system
......
...@@ -33,8 +33,15 @@ class ProjectGroupLink < ActiveRecord::Base ...@@ -33,8 +33,15 @@ class ProjectGroupLink < ActiveRecord::Base
private private
def different_group def different_group
if self.group && self.project && self.project.group == self.group return unless self.group && self.project
errors.add(:base, "Project cannot be shared with the project it is in.")
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
end end
......
# 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 ...@@ -210,7 +210,7 @@ class Service < ActiveRecord::Base
end end
def self.available_services_names def self.available_services_names
%w[ service_names = %w[
asana asana
assembla assembla
bamboo bamboo
...@@ -238,6 +238,9 @@ class Service < ActiveRecord::Base ...@@ -238,6 +238,9 @@ class Service < ActiveRecord::Base
slack slack
teamcity teamcity
] ]
service_names << 'mock_ci' if Rails.env.development?
service_names.sort_by(&:downcase)
end end
def self.build_from_template(project_id, template) def self.build_from_template(project_id, template)
......
...@@ -474,7 +474,7 @@ class User < ActiveRecord::Base ...@@ -474,7 +474,7 @@ class User < ActiveRecord::Base
Group.member_descendants(id) Group.member_descendants(id)
end end
def nested_projects def nested_groups_projects
Project.joins(:namespace).where('namespaces.parent_id IS NOT NULL'). Project.joins(:namespace).where('namespaces.parent_id IS NOT NULL').
member_descendants(id) member_descendants(id)
end end
......
...@@ -18,7 +18,8 @@ module Groups ...@@ -18,7 +18,8 @@ module Groups
end end
group.children.each do |group| 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 end
group.really_destroy! group.really_destroy!
......
...@@ -6,6 +6,8 @@ module MergeRequests ...@@ -6,6 +6,8 @@ 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
MergeError = Class.new(StandardError)
attr_reader :merge_request, :source attr_reader :merge_request, :source
def execute(merge_request) def execute(merge_request)
...@@ -27,6 +29,8 @@ module MergeRequests ...@@ -27,6 +29,8 @@ module MergeRequests
success success
end end
end end
rescue MergeError => e
log_merge_error(e.message, save_message_on_model: true)
end end
private private
...@@ -42,19 +46,13 @@ module MergeRequests ...@@ -42,19 +46,13 @@ module MergeRequests
commit_id = repository.merge(current_user, source, merge_request, options) commit_id = repository.merge(current_user, source, merge_request, options)
if commit_id raise MergeError, 'Conflicts detected during merge' unless commit_id
merge_request.update(merge_commit_sha: commit_id)
else merge_request.update(merge_commit_sha: commit_id)
log_merge_error('Conflicts detected during merge', save_message_on_model: true)
false
end
rescue GitHooksService::PreReceiveError => e rescue GitHooksService::PreReceiveError => e
log_merge_error(e.message, save_message_on_model: true) raise MergeError, e.message
false
rescue StandardError => e rescue StandardError => e
merge_request.update(merge_error: "Something went wrong during merge: #{e.message}") raise MergeError, "Something went wrong during merge: #{e.message}"
log_merge_error(e.message)
false
ensure ensure
merge_request.update(in_progress_merge_commit_sha: nil) merge_request.update(in_progress_merge_commit_sha: nil)
end end
......
...@@ -24,7 +24,11 @@ module MergeRequests ...@@ -24,7 +24,11 @@ module MergeRequests
pipeline_merge_requests(pipeline) do |merge_request| pipeline_merge_requests(pipeline) do |merge_request|
next unless merge_request.merge_when_build_succeeds? 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) MergeWorker.perform_async(merge_request.id, merge_request.merge_user_id, merge_request.merge_params)
end end
......
...@@ -115,11 +115,23 @@ module Users ...@@ -115,11 +115,23 @@ module Users
# Returns a union query of projects that the user is authorized to access # Returns a union query of projects that the user is authorized to access
def project_authorizations_union def project_authorizations_union
relations = [ relations = [
# Personal projects
user.personal_projects.select("#{user.id} AS user_id, projects.id AS project_id, #{Gitlab::Access::MASTER} AS access_level"), 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, 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.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) Gitlab::SQL::Union.new(relations)
......
...@@ -27,10 +27,6 @@ class ArtifactUploader < GitlabUploader ...@@ -27,10 +27,6 @@ class ArtifactUploader < GitlabUploader
File.join(self.class.artifacts_cache_path, @build.artifacts_path) File.join(self.class.artifacts_cache_path, @build.artifacts_path)
end end
def file_storage?
self.class.storage == CarrierWave::Storage::File
end
def filename def filename
file.try(:filename) file.try(:filename)
end end
......
...@@ -4,6 +4,6 @@ class AttachmentUploader < GitlabUploader ...@@ -4,6 +4,6 @@ class AttachmentUploader < GitlabUploader
storage :file storage :file
def store_dir 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
end end
...@@ -4,7 +4,7 @@ class AvatarUploader < GitlabUploader ...@@ -4,7 +4,7 @@ class AvatarUploader < GitlabUploader
storage :file storage :file
def store_dir 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
def exists? def exists?
......
...@@ -4,15 +4,12 @@ class FileUploader < GitlabUploader ...@@ -4,15 +4,12 @@ class FileUploader < GitlabUploader
storage :file storage :file
attr_accessor :project, :secret attr_accessor :project
attr_reader :secret
def initialize(project, secret = nil) def initialize(project, secret = nil)
@project = project @project = project
@secret = secret || self.class.generate_secret @secret = secret || generate_secret
end
def base_dir
"uploads"
end end
def store_dir def store_dir
...@@ -23,10 +20,6 @@ class FileUploader < GitlabUploader ...@@ -23,10 +20,6 @@ class FileUploader < GitlabUploader
File.join(base_dir, 'tmp', @project.path_with_namespace, @secret) File.join(base_dir, 'tmp', @project.path_with_namespace, @secret)
end end
def secure_url
File.join("/uploads", @secret, file.filename)
end
def to_markdown def to_markdown
to_h[:markdown] to_h[:markdown]
end end
...@@ -35,17 +28,23 @@ class FileUploader < GitlabUploader ...@@ -35,17 +28,23 @@ class FileUploader < GitlabUploader
filename = image_or_video? ? self.file.basename : self.file.filename filename = image_or_video? ? self.file.basename : self.file.filename
escaped_filename = filename.gsub("]", "\\]") escaped_filename = filename.gsub("]", "\\]")
markdown = "[#{escaped_filename}](#{self.secure_url})" markdown = "[#{escaped_filename}](#{secure_url})"
markdown.prepend("!") if image_or_video? || dangerous? markdown.prepend("!") if image_or_video? || dangerous?
{ {
alt: filename, alt: filename,
url: self.secure_url, url: secure_url,
markdown: markdown markdown: markdown
} }
end end
def self.generate_secret private
def generate_secret
SecureRandom.hex SecureRandom.hex
end end
def secure_url
File.join('/uploads', @secret, file.filename)
end
end end
class GitlabUploader < CarrierWave::Uploader::Base 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 # Reduce disk IO
def move_to_cache def move_to_cache
true true
......
...@@ -27,6 +27,8 @@ module UploaderHelper ...@@ -27,6 +27,8 @@ module UploaderHelper
extension_match?(DANGEROUS_EXT) extension_match?(DANGEROUS_EXT)
end end
private
def extension_match?(extensions) def extension_match?(extensions)
return false unless file return false unless file
...@@ -40,8 +42,4 @@ module UploaderHelper ...@@ -40,8 +42,4 @@ module UploaderHelper
extensions.include?(extension.downcase) extensions.include?(extension.downcase)
end end
def file_storage?
self.class.storage == CarrierWave::Storage::File
end
end end
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
= runner.short_sha = runner.short_sha
%td %td
= runner.description = runner.description
%td
= runner.version
%td %td
- if runner.shared? - if runner.shared?
n/a n/a
......
...@@ -67,6 +67,7 @@ ...@@ -67,6 +67,7 @@
%th Type %th Type
%th Runner token %th Runner token
%th Description %th Description
%th Version
%th Projects %th Projects
%th Jobs %th Jobs
%th Tags %th Tags
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
.nav-block .nav-block
- if current_user - if current_user
.controls .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 %i.fa.fa-rss
= render 'shared/event_filter' = render 'shared/event_filter'
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
.nav-block .nav-block
- if current_user - if current_user
.controls .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 %i.fa.fa-rss
= render 'shared/event_filter' = render 'shared/event_filter'
......
...@@ -36,6 +36,10 @@ ...@@ -36,6 +36,10 @@
= icon('bell fw') = icon('bell fw')
%span.badge.todos-pending-count{ class: ("hidden" if todos_pending_count == 0) } %span.badge.todos-pending-count{ class: ("hidden" if todos_pending_count == 0) }
= todos_count_format(todos_pending_count) = 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? - if Gitlab::Sherlock.enabled?
%li %li
= link_to sherlock_transactions_path, title: 'Sherlock Transactions', = link_to sherlock_transactions_path, title: 'Sherlock Transactions',
...@@ -61,12 +65,12 @@ ...@@ -61,12 +65,12 @@
%div %div
= link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-success' = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-success'
%h1.title= title
.header-logo .header-logo
= link_to root_path, class: 'home', title: 'Dashboard', id: 'logo' do = link_to root_path, class: 'home', title: 'Dashboard', id: 'logo' do
= brand_header_logo = brand_header_logo
%h1.title= title
= yield :header_content = yield :header_content
= render 'shared/outdated_browser' = render 'shared/outdated_browser'
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
.nav-block.activity-filter-block .nav-block.activity-filter-block
- if current_user - if current_user
.controls .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') = icon('rss')
= render 'shared/event_filter' = render 'shared/event_filter'
......
.content-block.build-header .content-block.build-header.top-area
.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
Job Job
...@@ -16,7 +16,10 @@ ...@@ -16,7 +16,10 @@
- if @build.user - if @build.user
= 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? .nav-controls
= link_to "Retry job", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-inverted-secondary pull-right', method: :post - 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" } %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')
- 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 @@ ...@@ -3,7 +3,7 @@
= render "projects/commits/head" = render "projects/commits/head"
.flex-list{ class: container_class } .flex-list{ class: container_class }
.top-area.flex-row .top-area.adjust
.nav-text.row-main-content .nav-text.row-main-content
Tags give the ability to mark specific points in history as being important Tags give the ability to mark specific points in history as being important
......
...@@ -82,7 +82,7 @@ ...@@ -82,7 +82,7 @@
%span.dropdown-label-box{ style: 'background: {{color}}' } %span.dropdown-label-box{ style: 'background: {{color}}' }
%span.label-title.js-data-value %span.label-title.js-data-value
{{title}} {{title}}
.pull-right .pull-right.filter-dropdown-container
= render 'shared/sort_dropdown' = render 'shared/sort_dropdown'
- if @bulk_edit - if @bulk_edit
......
---
title: Add runner version to /admin/runners view
merge_request: 8733
author: Jonathon Reinhart
---
title: Add the Username to the HTTP(S) clone URL of a Repository
merge_request: 9347
author: Jan Christophersen
---
title: Add button to create issue for failing build
merge_request: 9391
author: Alex Sanford
---
title: 'Add performance query regression fix for !9088 affecting #27267'
merge_request:
author:
--- ---
title: Disable invalid service templates title: Re-add the New Project button in nav bar
merge_request: merge_request:
author: author:
---
title: Enhanced filter issues layout for better mobile experiance
merge_request: 9280
author: Pratik Borsadiya
--- ---
title: Fixed commit search UI title: Left align logo
merge_request: merge_request:
author: author:
---
title: Disable unused tags count cache for Projects, Builds and Runners
merge_request:
author:
---
title: Allow searching issues for strings containing colons
merge_request:
author:
---
title: Keep consistent in handling indexOf results
merge_request: 9531
author: Takuya Noguchi
---
title: 'API project create: Make name or path required'
merge_request: 9416
author:
---
title: Disabled tooltip on add issues button in usse boards
merge_request:
author:
---
title: 'API: Return 204 for all delete endpoints'
merge_request: 9397
author: Robert Schilling
---
title: Fix MR changes tab size count when there are over 100 files in the diff
merge_request:
author:
---
title: Fix 'New Tag' layout on Tags page
merge_request:
author: Robert Marcano
---
title: Fix issuable stale object error handler for js when updating tasklists
merge_request:
author:
---
title: Use default branch as target_branch when parameter is missing
merge_request:
author:
---
title: Add Mock CI service/integration for development
merge_request:
author:
--- ---
title: Upgrade GitLab Pages to v0.3.2 title: Improved diff comment button UX
merge_request: merge_request:
author: author:
---
title: Only create unmergeable todos once when MR fails to merge
merge_request:
author:
--- ---
title: Spam check and reCAPTCHA improvements title: Fixed RSS button alignment on activity pages
merge_request: merge_request:
author: author:
---
title: SSH key field updates title after pasting key
merge_request:
author:
---
title: Chat slash commands show labels correctly
merge_request:
author:
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
class MigrateUsersNotificationLevel < ActiveRecord::Migration class MigrateUsersNotificationLevel < ActiveRecord::Migration
DOWNTIME = false
# Migrates only users who changed their default notification level :participating # Migrates only users who changed their default notification level :participating
# creating a new record on notification settings table # 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 ...@@ -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 project, you can disable it from your project's settings. Read the user guide
on how to achieve that. 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 ## Storage limitations
Currently, there is no storage limitation, which means a user can upload an Currently, there is no storage limitation, which means a user can upload an
......
...@@ -159,6 +159,7 @@ The following table shows the possible return codes for API requests. ...@@ -159,6 +159,7 @@ The following table shows the possible return codes for API requests.
| Return values | Description | | Return values | Description |
| ------------- | ----------- | | ------------- | ----------- |
| `200 OK` | The `GET`, `PUT` or `DELETE` request was successful, the resource(s) itself is returned as JSON. | | `200 OK` | The `GET`, `PUT` or `DELETE` request was successful, the resource(s) itself is returned as JSON. |
| `204 OK` | The server has successfully fulfilled the request and that there is no additional content to send in the response payload body. |
| `201 Created` | The `POST` request was successful and the resource is returned as JSON. | | `201 Created` | The `POST` request was successful and the resource is returned as JSON. |
| `304 Not Modified` | Indicates that the resource has not been modified since the last request. | | `304 Not Modified` | Indicates that the resource has not been modified since the last request. |
| `400 Bad Request` | A required attribute of the API request is missing, e.g., the title of an issue is not given. | | `400 Bad Request` | A required attribute of the API request is missing, e.g., the title of an issue is not given. |
......
...@@ -178,27 +178,6 @@ Parameters: ...@@ -178,27 +178,6 @@ Parameters:
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/award_emoji/344 curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/award_emoji/344
``` ```
Example Response:
```json
{
"id": 344,
"name": "blowfish",
"user": {
"name": "Administrator",
"username": "root",
"id": 1,
"state": "active",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://gitlab.example.com/root"
},
"created_at": "2016-06-17T17:47:29.266Z",
"updated_at": "2016-06-17T17:47:29.266Z",
"awardable_id": 80,
"awardable_type": "Issue"
}
```
## Award Emoji on Notes ## Award Emoji on Notes
The endpoints documented above are available for Notes as well. Notes The endpoints documented above are available for Notes as well. Notes
...@@ -350,25 +329,4 @@ Parameters: ...@@ -350,25 +329,4 @@ Parameters:
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/award_emoji/345 curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" http://gitlab.example.com/api/v3/projects/1/issues/80/award_emoji/345
``` ```
Example Response:
```json
{
"id": 345,
"name": "rocket",
"user": {
"name": "Administrator",
"username": "root",
"id": 1,
"state": "active",
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
"web_url": "http://gitlab.example.com/root"
},
"created_at": "2016-06-17T19:59:55.888Z",
"updated_at": "2016-06-17T19:59:55.888Z",
"awardable_id": 1,
"awardable_type": "Note"
}
```
[ce-4575]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4575 [ce-4575]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4575
...@@ -226,16 +226,3 @@ DELETE /projects/:id/boards/:board_id/lists/:list_id ...@@ -226,16 +226,3 @@ DELETE /projects/:id/boards/:board_id/lists/:list_id
```bash ```bash
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/boards/1/lists/1 curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/boards/1/lists/1
``` ```
Example response:
```json
{
"id" : 1,
"label" : {
"name" : "Testing",
"color" : "#F0AD4E",
"description" : null
},
"position" : 1
}
```
...@@ -244,14 +244,6 @@ In case of an error, an explaining message is provided. ...@@ -244,14 +244,6 @@ In case of an error, an explaining message is provided.
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/repository/branches/newbranch" curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/repository/branches/newbranch"
``` ```
Example response:
```json
{
"branch_name": "newbranch"
}
```
## Delete merged branches ## Delete merged branches
Will delete all branches that are merged into the project's default branch. Will delete all branches that are merged into the project's default branch.
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment