Skip to content
Snippets Groups Projects
Commit 83ab2898 authored by John Jarvis's avatar John Jarvis
Browse files

Merge branch 'master' into 11-10-stable

parents bfec819f a6e9175f
No related branches found
No related tags found
No related merge requests found
Showing
with 139 additions and 76 deletions
Loading
@@ -1018,10 +1018,8 @@ schedule:review-build-cng:
Loading
@@ -1018,10 +1018,8 @@ schedule:review-build-cng:
   
.review-deploy-base: &review-deploy-base .review-deploy-base: &review-deploy-base
<<: *review-base <<: *review-base
retry: 2
allow_failure: true allow_failure: true
variables: variables:
GIT_DEPTH: "1"
HOST_SUFFIX: "${CI_ENVIRONMENT_SLUG}" HOST_SUFFIX: "${CI_ENVIRONMENT_SLUG}"
DOMAIN: "-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}" DOMAIN: "-${CI_ENVIRONMENT_SLUG}.${REVIEW_APPS_DOMAIN}"
GITLAB_HELM_CHART_REF: "master" GITLAB_HELM_CHART_REF: "master"
Loading
Loading
# Backend Maintainers are the default for all ruby files # Backend Maintainers are the default for all ruby files
*.rb @ayufan @dbalexandre @DouweM @dzaporozhets @godfat @grzesiek @nick.thomas @rspeicher @rymai @smcgivern *.rb @ayufan @dbalexandre @DouweM @dzaporozhets @godfat @grzesiek @mkozono @nick.thomas @rspeicher @rymai @smcgivern
*.rake @ayufan @dbalexandre @DouweM @dzaporozhets @godfat @grzesiek @nick.thomas @rspeicher @rymai @smcgivern *.rake @ayufan @dbalexandre @DouweM @dzaporozhets @godfat @grzesiek @mkozono @nick.thomas @rspeicher @rymai @smcgivern
   
# Technical writing team are the default reviewers for everything in `doc/` # Technical writing team are the default reviewers for everything in `doc/`
/doc/ @axil @marcia /doc/ @axil @marcia
Loading
Loading
Loading
@@ -2,6 +2,19 @@
Loading
@@ -2,6 +2,19 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
   
## 11.9.5 (2019-04-03)
### Fixed (3 changes)
- Force to recreate all MR diffs on import. !26480
- Fix API /project/:id/branches not returning correct merge status. !26785
- Avoid excessive recursive calls with Rugged TreeEntries. !26813
### Performance (1 change)
- Force a full GC after importing a project. !26803
## 11.9.3 (2019-03-27) ## 11.9.3 (2019-03-27)
   
### Security (8 changes) ### Security (8 changes)
Loading
Loading
Loading
@@ -13,3 +13,4 @@ danger.import_dangerfile(path: 'danger/prettier')
Loading
@@ -13,3 +13,4 @@ danger.import_dangerfile(path: 'danger/prettier')
danger.import_dangerfile(path: 'danger/eslint') danger.import_dangerfile(path: 'danger/eslint')
danger.import_dangerfile(path: 'danger/roulette') danger.import_dangerfile(path: 'danger/roulette')
danger.import_dangerfile(path: 'danger/single_codebase') danger.import_dangerfile(path: 'danger/single_codebase')
danger.import_dangerfile(path: 'danger/gitlab_ui_wg')
Loading
@@ -54,7 +54,10 @@ export default Vue.extend({
Loading
@@ -54,7 +54,10 @@ export default Vue.extend({
return `${n__('%d issue', '%d issues', issuesSize)}`; return `${n__('%d issue', '%d issues', issuesSize)}`;
}, },
isNewIssueShown() { isNewIssueShown() {
return this.list.type === 'backlog' || (!this.disabled && this.list.type !== 'closed'); return (
this.list.type === 'backlog' ||
(!this.disabled && this.list.type !== 'closed' && this.list.type !== 'blank')
);
}, },
}, },
watch: { watch: {
Loading
Loading
Loading
@@ -40,6 +40,12 @@ export default class VariableList {
Loading
@@ -40,6 +40,12 @@ export default class VariableList {
// converted. we need the value as a string. // converted. we need the value as a string.
default: $('.js-ci-variable-input-protected').attr('data-default'), default: $('.js-ci-variable-input-protected').attr('data-default'),
}, },
masked: {
selector: '.js-ci-variable-input-masked',
// use `attr` instead of `data` as we don't want the value to be
// converted. we need the value as a string.
default: $('.js-ci-variable-input-masked').attr('data-default'),
},
environment_scope: { environment_scope: {
// We can't use a `.js-` class here because // We can't use a `.js-` class here because
// gl_dropdown replaces the <input> and doesn't copy over the class // gl_dropdown replaces the <input> and doesn't copy over the class
Loading
@@ -88,13 +94,16 @@ export default class VariableList {
Loading
@@ -88,13 +94,16 @@ export default class VariableList {
} }
}); });
   
// Always make sure there is an empty last row this.$container.on('input trigger-change', inputSelector, e => {
this.$container.on('input trigger-change', inputSelector, () => { // Always make sure there is an empty last row
const $lastRow = this.$container.find('.js-row').last(); const $lastRow = this.$container.find('.js-row').last();
   
if (this.checkIfRowTouched($lastRow)) { if (this.checkIfRowTouched($lastRow)) {
this.insertRow($lastRow); this.insertRow($lastRow);
} }
// If masked, validate value against regex
this.validateMaskability($(e.currentTarget).closest('.js-row'));
}); });
} }
   
Loading
@@ -171,12 +180,33 @@ export default class VariableList {
Loading
@@ -171,12 +180,33 @@ export default class VariableList {
   
checkIfRowTouched($row) { checkIfRowTouched($row) {
return Object.keys(this.inputMap).some(name => { return Object.keys(this.inputMap).some(name => {
// Row should not qualify as touched if only switches have been touched
if (['protected', 'masked'].includes(name)) return false;
const entry = this.inputMap[name]; const entry = this.inputMap[name];
const $el = $row.find(entry.selector); const $el = $row.find(entry.selector);
return $el.length && $el.val() !== entry.default; return $el.length && $el.val() !== entry.default;
}); });
} }
   
validateMaskability($row) {
const invalidInputClass = 'gl-field-error-outline';
const maskableRegex = /^\w{8,}$/; // Eight or more alphanumeric characters plus underscores
const variableValue = $row.find(this.inputMap.secret_value.selector).val();
const isValueMaskable = maskableRegex.test(variableValue) || variableValue === '';
const isMaskedChecked = $row.find(this.inputMap.masked.selector).val() === 'true';
// Show a validation error if the user wants to mask an unmaskable variable value
$row
.find(this.inputMap.secret_value.selector)
.toggleClass(invalidInputClass, isMaskedChecked && !isValueMaskable);
$row
.find('.js-secret-value-placeholder')
.toggleClass(invalidInputClass, isMaskedChecked && !isValueMaskable);
$row.find('.masking-validation-error').toggle(isMaskedChecked && !isValueMaskable);
}
toggleEnableRow(isEnabled = true) { toggleEnableRow(isEnabled = true) {
this.$container.find(this.inputMap.key.selector).attr('disabled', !isEnabled); this.$container.find(this.inputMap.key.selector).attr('disabled', !isEnabled);
this.$container.find('.js-row-remove-button').attr('disabled', !isEnabled); this.$container.find('.js-row-remove-button').attr('disabled', !isEnabled);
Loading
Loading
Loading
@@ -188,10 +188,6 @@ export default {
Loading
@@ -188,10 +188,6 @@ export default {
/> />
</div> </div>
</template> </template>
<div v-if="isFileTooLarge" class="nothing-here-block diff-collapsed js-too-large-diff">
{{ __('This source diff could not be displayed because it is too large.') }}
<span v-html="viewBlobLink"></span>
</div>
</div> </div>
</template> </template>
   
Loading
Loading
import { viewerInformationForPath } from '~/vue_shared/components/content_viewer/lib/viewer_utils'; import { viewerInformationForPath } from '~/vue_shared/components/content_viewer/lib/viewer_utils';
import { decorateData, sortTree } from '../stores/utils'; import { decorateData, sortTree } from '../stores/utils';
   
export const escapeFileUrl = fileUrl => encodeURIComponent(fileUrl).replace(/%2F/g, '/');
export const splitParent = path => { export const splitParent = path => {
const idx = path.lastIndexOf('/'); const idx = path.lastIndexOf('/');
   
Loading
@@ -45,7 +47,7 @@ export const decorateFiles = ({
Loading
@@ -45,7 +47,7 @@ export const decorateFiles = ({
id: path, id: path,
name, name,
path, path,
url: `/${projectId}/tree/${branchId}/-/${path}/`, url: `/${projectId}/tree/${branchId}/-/${escapeFileUrl(path)}/`,
type: 'tree', type: 'tree',
parentTreeUrl: parentFolder ? parentFolder.url : `/${projectId}/tree/${branchId}/`, parentTreeUrl: parentFolder ? parentFolder.url : `/${projectId}/tree/${branchId}/`,
tempFile, tempFile,
Loading
@@ -81,7 +83,7 @@ export const decorateFiles = ({
Loading
@@ -81,7 +83,7 @@ export const decorateFiles = ({
id: path, id: path,
name, name,
path, path,
url: `/${projectId}/blob/${branchId}/-/${path}`, url: `/${projectId}/blob/${branchId}/-/${escapeFileUrl(path)}`,
type: 'blob', type: 'blob',
parentTreeUrl: fileFolder ? fileFolder.url : `/${projectId}/blob/${branchId}`, parentTreeUrl: fileFolder ? fileFolder.url : `/${projectId}/blob/${branchId}`,
tempFile, tempFile,
Loading
Loading
Loading
@@ -92,7 +92,7 @@ export const getTimeago = () => {
Loading
@@ -92,7 +92,7 @@ export const getTimeago = () => {
   
const timeAgoLocaleRemaining = [ const timeAgoLocaleRemaining = [
() => [s__('Timeago|just now'), s__('Timeago|right now')], () => [s__('Timeago|just now'), s__('Timeago|right now')],
() => [s__('Timeago|%s seconds ago'), s__('Timeago|%s seconds remaining')], () => [s__('Timeago|just now'), s__('Timeago|%s seconds remaining')],
() => [s__('Timeago|1 minute ago'), s__('Timeago|1 minute remaining')], () => [s__('Timeago|1 minute ago'), s__('Timeago|1 minute remaining')],
() => [s__('Timeago|%s minutes ago'), s__('Timeago|%s minutes remaining')], () => [s__('Timeago|%s minutes ago'), s__('Timeago|%s minutes remaining')],
() => [s__('Timeago|1 hour ago'), s__('Timeago|1 hour remaining')], () => [s__('Timeago|1 hour ago'), s__('Timeago|1 hour remaining')],
Loading
@@ -121,7 +121,7 @@ export const getTimeago = () => {
Loading
@@ -121,7 +121,7 @@ export const getTimeago = () => {
   
const timeAgoLocale = [ const timeAgoLocale = [
() => [s__('Timeago|just now'), s__('Timeago|right now')], () => [s__('Timeago|just now'), s__('Timeago|right now')],
() => [s__('Timeago|%s seconds ago'), s__('Timeago|in %s seconds')], () => [s__('Timeago|just now'), s__('Timeago|in %s seconds')],
() => [s__('Timeago|1 minute ago'), s__('Timeago|in 1 minute')], () => [s__('Timeago|1 minute ago'), s__('Timeago|in 1 minute')],
() => [s__('Timeago|%s minutes ago'), s__('Timeago|in %s minutes')], () => [s__('Timeago|%s minutes ago'), s__('Timeago|in %s minutes')],
() => [s__('Timeago|1 hour ago'), s__('Timeago|in 1 hour')], () => [s__('Timeago|1 hour ago'), s__('Timeago|in 1 hour')],
Loading
@@ -160,7 +160,11 @@ export const getTimeago = () => {
Loading
@@ -160,7 +160,11 @@ export const getTimeago = () => {
* @param {Boolean} setTimeago * @param {Boolean} setTimeago
*/ */
export const localTimeAgo = ($timeagoEls, setTimeago = true) => { export const localTimeAgo = ($timeagoEls, setTimeago = true) => {
getTimeago().render($timeagoEls, timeagoLanguageCode); getTimeago();
$timeagoEls.each((i, el) => {
$(el).text(timeagoInstance.format($(el).attr('datetime'), timeagoLanguageCode));
});
   
if (!setTimeago) { if (!setTimeago) {
return; return;
Loading
Loading
Loading
@@ -86,9 +86,6 @@ export default {
Loading
@@ -86,9 +86,6 @@ export default {
}, },
computed: { computed: {
...mapGetters(['getUserDataByProp']), ...mapGetters(['getUserDataByProp']),
showReplyButton() {
return gon.features && gon.features.replyToIndividualNotes && this.showReply;
},
shouldShowActionsDropdown() { shouldShowActionsDropdown() {
return this.currentUserId && (this.canEdit || this.canReportAsAbuse); return this.currentUserId && (this.canEdit || this.canReportAsAbuse);
}, },
Loading
@@ -167,7 +164,7 @@ export default {
Loading
@@ -167,7 +164,7 @@ export default {
</a> </a>
</div> </div>
<reply-button <reply-button
v-if="showReplyButton" v-if="showReply"
ref="replyButton" ref="replyButton"
class="js-reply-button" class="js-reply-button"
@startReplying="$emit('startReplying')" @startReplying="$emit('startReplying')"
Loading
Loading
Loading
@@ -96,7 +96,7 @@ export default {
Loading
@@ -96,7 +96,7 @@ export default {
return ''; return '';
} }
   
// We need to do this to ensure we have the currect sentence order // We need to do this to ensure we have the correct sentence order
// when translating this as the sentence order may change from one // when translating this as the sentence order may change from one
// language to the next. See: // language to the next. See:
// https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/24427#note_133713771 // https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/24427#note_133713771
Loading
Loading
import $ from 'jquery'; import $ from 'jquery';
import Flash from './flash'; import Flash from './flash';
import { __ } from '~/locale';
   
export default function notificationsDropdown() { export default function notificationsDropdown() {
$(document).on('click', '.update-notification', function updateNotificationCallback(e) { $(document).on('click', '.update-notification', function updateNotificationCallback(e) {
Loading
@@ -27,7 +28,7 @@ export default function notificationsDropdown() {
Loading
@@ -27,7 +28,7 @@ export default function notificationsDropdown() {
.closest('.js-notification-dropdown') .closest('.js-notification-dropdown')
.replaceWith(data.html); .replaceWith(data.html);
} else { } else {
Flash('Failed to save new settings', 'alert'); Flash(__('Failed to save new settings'), 'alert');
} }
}); });
} }
/* eslint-disable class-methods-use-this */ const defaultTimezone = 'UTC';
export const formatUtcOffset = offset => {
const parsed = parseInt(offset, 10);
if (Number.isNaN(parsed) || parsed === 0) {
return `0`;
}
const prefix = offset > 0 ? '+' : '-';
return `${prefix} ${Math.abs(offset / 3600)}`;
};
   
import $ from 'jquery'; export const formatTimezone = item => `[UTC ${formatUtcOffset(item.offset)}] ${item.name}`;
   
const defaultTimezone = 'UTC'; const defaults = {
$inputEl: null,
$dropdownEl: null,
onSelectTimezone: null,
};
   
export default class TimezoneDropdown { export default class TimezoneDropdown {
constructor() { constructor({ $dropdownEl, $inputEl, onSelectTimezone } = defaults) {
this.$dropdown = $('.js-timezone-dropdown'); this.$dropdown = $dropdownEl;
this.$dropdownToggle = this.$dropdown.find('.dropdown-toggle-text'); this.$dropdownToggle = this.$dropdown.find('.dropdown-toggle-text');
this.$input = $('#schedule_cron_timezone'); this.$input = $inputEl;
this.timezoneData = this.$dropdown.data('data'); this.timezoneData = this.$dropdown.data('data');
this.initDefaultTimezone(); this.initDefaultTimezone();
this.initDropdown(); this.initDropdown();
this.onSelectTimezone = onSelectTimezone;
} }
   
initDropdown() { initDropdown() {
Loading
@@ -24,28 +40,12 @@ export default class TimezoneDropdown {
Loading
@@ -24,28 +40,12 @@ export default class TimezoneDropdown {
fields: ['name'], fields: ['name'],
}, },
clicked: cfg => this.updateInputValue(cfg), clicked: cfg => this.updateInputValue(cfg),
text: item => this.formatTimezone(item), text: item => formatTimezone(item),
}); });
   
this.setDropdownToggle(); this.setDropdownToggle();
} }
   
formatUtcOffset(offset) {
let prefix = '';
if (offset > 0) {
prefix = '+';
} else if (offset < 0) {
prefix = '-';
}
return `${prefix} ${Math.abs(offset / 3600)}`;
}
formatTimezone(item) {
return `[UTC ${this.formatUtcOffset(item.offset)}] ${item.name}`;
}
initDefaultTimezone() { initDefaultTimezone() {
const initialValue = this.$input.val(); const initialValue = this.$input.val();
   
Loading
@@ -56,13 +56,14 @@ export default class TimezoneDropdown {
Loading
@@ -56,13 +56,14 @@ export default class TimezoneDropdown {
   
setDropdownToggle() { setDropdownToggle() {
const initialValue = this.$input.val(); const initialValue = this.$input.val();
this.$dropdownToggle.text(initialValue); this.$dropdownToggle.text(initialValue);
} }
   
updateInputValue({ selectedObj, e }) { updateInputValue({ selectedObj, e }) {
e.preventDefault(); e.preventDefault();
this.$input.val(selectedObj.identifier); this.$input.val(selectedObj.identifier);
gl.pipelineScheduleFieldErrors.updateFormValidityState(); if (this.onSelectTimezone) {
this.onSelectTimezone({ selectedObj, e });
}
} }
} }
Loading
@@ -41,7 +41,13 @@ export default () => {
Loading
@@ -41,7 +41,13 @@ export default () => {
   
const formElement = document.getElementById('new-pipeline-schedule-form'); const formElement = document.getElementById('new-pipeline-schedule-form');
   
gl.timezoneDropdown = new TimezoneDropdown(); gl.timezoneDropdown = new TimezoneDropdown({
$dropdownEl: $('.js-timezone-dropdown'),
$inputEl: $('#schedule_cron_timezone'),
onSelectTimezone: () => {
gl.pipelineScheduleFieldErrors.updateFormValidityState();
},
});
gl.targetBranchDropdown = new TargetBranchDropdown(); gl.targetBranchDropdown = new TargetBranchDropdown();
gl.pipelineScheduleFieldErrors = new GlFieldErrors(formElement); gl.pipelineScheduleFieldErrors = new GlFieldErrors(formElement);
   
Loading
Loading
Loading
@@ -50,6 +50,9 @@ export default {
Loading
@@ -50,6 +50,9 @@ export default {
buttonLabel() { buttonLabel() {
return this.isTodo ? MARK_TEXT : TODO_TEXT; return this.isTodo ? MARK_TEXT : TODO_TEXT;
}, },
buttonTooltip() {
return !this.collapsed ? undefined : this.buttonLabel;
},
collapsedButtonIconClasses() { collapsedButtonIconClasses() {
return this.isTodo ? 'todo-undone' : ''; return this.isTodo ? 'todo-undone' : '';
}, },
Loading
@@ -69,7 +72,7 @@ export default {
Loading
@@ -69,7 +72,7 @@ export default {
<button <button
v-tooltip v-tooltip
:class="buttonClasses" :class="buttonClasses"
:title="buttonLabel" :title="buttonTooltip"
:aria-label="buttonLabel" :aria-label="buttonLabel"
:data-issuable-id="issuableId" :data-issuable-id="issuableId"
:data-issuable-type="issuableType" :data-issuable-type="issuableType"
Loading
Loading
Loading
@@ -109,29 +109,31 @@ export default {
Loading
@@ -109,29 +109,31 @@ export default {
></div> ></div>
</div> </div>
   
<div v-if="mr.isOpen" class="branch-actions d-flex"> <div class="branch-actions d-flex">
<a <template v-if="mr.isOpen">
v-if="!mr.sourceBranchRemoved" <a
v-tooltip v-if="!mr.sourceBranchRemoved"
:href="webIdePath" v-tooltip
:title="ideButtonTitle" :href="webIdePath"
:class="{ disabled: !mr.canPushToSourceBranch }" :title="ideButtonTitle"
class="btn btn-default js-web-ide d-none d-md-inline-block append-right-8" :class="{ disabled: !mr.canPushToSourceBranch }"
data-placement="bottom" class="btn btn-default js-web-ide d-none d-md-inline-block append-right-8"
tabindex="0" data-placement="bottom"
role="button" tabindex="0"
> role="button"
{{ s__('mrWidget|Open in Web IDE') }} >
</a> {{ s__('mrWidget|Open in Web IDE') }}
<button </a>
:disabled="mr.sourceBranchRemoved" <button
data-target="#modal_merge_info" :disabled="mr.sourceBranchRemoved"
data-toggle="modal" data-target="#modal_merge_info"
class="btn btn-default js-check-out-branch append-right-default" data-toggle="modal"
type="button" class="btn btn-default js-check-out-branch append-right-default"
> type="button"
{{ s__('mrWidget|Check out branch') }} >
</button> {{ s__('mrWidget|Check out branch') }}
</button>
</template>
<span class="dropdown"> <span class="dropdown">
<button <button
type="button" type="button"
Loading
Loading
Loading
@@ -102,7 +102,7 @@ export default {
Loading
@@ -102,7 +102,7 @@ export default {
:style="{ :style="{
width: onionMaxPixelWidth, width: onionMaxPixelWidth,
height: onionMaxPixelHeight, height: onionMaxPixelHeight,
'user-select': dragging === true ? 'none' : '', 'user-select': dragging ? 'none' : null,
}" }"
class="onion-skin-frame" class="onion-skin-frame"
> >
Loading
Loading
Loading
@@ -68,12 +68,10 @@ export default {
Loading
@@ -68,12 +68,10 @@ export default {
}, },
startDrag() { startDrag() {
this.dragging = true; this.dragging = true;
document.body.style.userSelect = 'none';
document.body.addEventListener('mousemove', this.dragMove); document.body.addEventListener('mousemove', this.dragMove);
}, },
stopDrag() { stopDrag() {
this.dragging = false; this.dragging = false;
document.body.style.userSelect = '';
document.body.removeEventListener('mousemove', this.dragMove); document.body.removeEventListener('mousemove', this.dragMove);
}, },
prepareSwipe() { prepareSwipe() {
Loading
@@ -104,7 +102,13 @@ export default {
Loading
@@ -104,7 +102,13 @@ export default {
   
<template> <template>
<div class="swipe view"> <div class="swipe view">
<div ref="swipeFrame" class="swipe-frame"> <div
ref="swipeFrame"
:style="{
'user-select': dragging ? 'none' : null,
}"
class="swipe-frame"
>
<image-viewer <image-viewer
key="swipeOldImg" key="swipeOldImg"
ref="swipeOldImg" ref="swipeOldImg"
Loading
Loading
Loading
@@ -66,6 +66,7 @@
Loading
@@ -66,6 +66,7 @@
} }
} }
   
.ci-variable-masked-item,
.ci-variable-protected-item { .ci-variable-protected-item {
flex: 0 1 auto; flex: 0 1 auto;
display: flex; display: flex;
Loading
Loading
Loading
@@ -14,6 +14,7 @@
Loading
@@ -14,6 +14,7 @@
@include str-truncated(100%); @include str-truncated(100%);
margin-top: -1px; margin-top: -1px;
margin-bottom: 0; margin-bottom: 0;
font-size: $gl-font-size-small;
} }
} }
   
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment