Commit 039b0c0d authored by Douwe Maan's avatar Douwe Maan

Merge branch 'master' into 'security-fj-bumping-sanitize-gem'

# Conflicts:
#   Gemfile.rails5.lock
parents b4c10804 3c725a2a
......@@ -71,7 +71,3 @@ rules:
body: 1
## Destructuring: https://eslint.org/docs/rules/prefer-destructuring
prefer-destructuring: off
## no-restricted-globals: https://eslint.org/docs/rules/no-restricted-globals
no-restricted-globals: off
## no-multi-assign: https://eslint.org/docs/rules/no-multi-assign
no-multi-assign: off
......@@ -307,7 +307,7 @@ GEM
rouge (~> 3.1)
sanitize (~> 4.6.4)
stringex (~> 2.6)
gitlab-gollum-rugged_adapter (0.4.4)
gitlab-gollum-rugged_adapter (0.4.4.1)
mime-types (>= 1.15)
rugged (~> 0.25)
gitlab-grit (2.8.2)
......
......@@ -70,7 +70,7 @@ export default class BlobViewer {
const initialViewer = this.$fileHolder[0].querySelector('.blob-viewer:not(.hidden)');
let initialViewerName = initialViewer.getAttribute('data-type');
if (this.switcher && location.hash.indexOf('#L') === 0) {
if (this.switcher && window.location.hash.indexOf('#L') === 0) {
initialViewerName = 'simple';
}
......
/* eslint-disable comma-dangle, space-before-function-paren, one-var */
import $ from 'jquery';
import Sortable from 'sortablejs';
import Vue from 'vue';
import AccessorUtilities from '../../lib/utils/accessor';
......@@ -57,40 +56,6 @@ gl.issueBoards.Board = Vue.extend({
});
},
deep: true,
},
detailIssue: {
handler () {
if (!Object.keys(this.detailIssue.issue).length) return;
const issue = this.list.findIssue(this.detailIssue.issue.id);
if (issue) {
const offsetLeft = this.$el.offsetLeft;
const boardsList = document.querySelectorAll('.boards-list')[0];
const left = boardsList.scrollLeft - offsetLeft;
let right = (offsetLeft + this.$el.offsetWidth);
if (window.innerWidth > 768 && boardsList.classList.contains('is-compact')) {
// -290 here because width of boardsList is animating so therefore
// getting the width here is incorrect
// 290 is the width of the sidebar
right -= (boardsList.offsetWidth - 290);
} else {
right -= boardsList.offsetWidth;
}
if (right - boardsList.scrollLeft > 0) {
$(boardsList).animate({
scrollLeft: right
}, this.sortableOptions.animation);
} else if (left > 0) {
$(boardsList).animate({
scrollLeft: offsetLeft
}, this.sortableOptions.animation);
}
}
},
deep: true
}
},
mounted () {
......
......@@ -17,7 +17,7 @@ gl.issueBoards.BoardDelete = Vue.extend({
deleteBoard () {
$(this.$el).tooltip('hide');
if (confirm('Are you sure you want to delete this list?')) {
if (window.confirm('Are you sure you want to delete this list?')) {
this.list.destroy();
}
}
......
......@@ -145,6 +145,6 @@ gl.issueBoards.BoardsStore = {
return filteredList[0];
},
updateFiltersUrl () {
history.pushState(null, null, `?${this.filter.path}`);
window.history.pushState(null, null, `?${this.filter.path}`);
}
};
......@@ -45,7 +45,7 @@ export default class CommitsList {
this.content.fadeTo('fast', 1.0);
// Change url so if user reload a page - search results are saved
history.replaceState({
window.history.replaceState({
page: commitsUrl,
}, document.title, commitsUrl);
})
......
......@@ -98,7 +98,7 @@ export default {
},
disableKey(deployKey, callback) {
// eslint-disable-next-line no-alert
if (confirm(s__('DeployKeys|You are going to remove this deploy key. Are you sure?'))) {
if (window.confirm(s__('DeployKeys|You are going to remove this deploy key. Are you sure?'))) {
this.service
.disableKey(deployKey.id)
.then(this.fetchKeys)
......
......@@ -40,7 +40,7 @@
methods: {
onClick() {
// eslint-disable-next-line no-alert
if (confirm('Are you sure you want to stop this environment?')) {
if (window.confirm('Are you sure you want to stop this environment?')) {
this.isLoading = true;
$(this.$el).tooltip('dispose');
......
......@@ -34,6 +34,10 @@ export default {
type: String,
required: true,
},
actionBtnIcon: {
type: String,
required: true,
},
itemActionComponent: {
type: String,
required: true,
......@@ -53,26 +57,21 @@ export default {
required: true,
},
},
data() {
return {
showActionButton: false,
};
},
computed: {
titleText() {
return sprintf(__('%{title} changes'), {
title: this.title,
});
},
filesLength() {
return this.fileList.length;
},
},
methods: {
...mapActions(['stageAllChanges', 'unstageAllChanges']),
actionBtnClicked() {
this[this.action]();
},
setShowActionButton(show) {
this.showActionButton = show;
},
},
};
</script>
......@@ -83,8 +82,6 @@ export default {
>
<header
class="multi-file-commit-panel-header"
@mouseenter="setShowActionButton(true)"
@mouseleave="setShowActionButton(false)"
>
<div
class="multi-file-commit-panel-header-title"
......@@ -95,24 +92,40 @@ export default {
:size="18"
/>
{{ titleText }}
<span
v-show="!showActionButton"
class="ide-commit-file-count"
>
{{ fileList.length }}
</span>
<button
v-show="showActionButton"
type="button"
class="btn btn-blank btn-link ide-staged-action-btn"
@click="actionBtnClicked"
>
{{ actionBtnText }}
</button>
<div class="d-flex ml-auto">
<button
v-tooltip
v-show="filesLength"
:class="{
'd-flex': filesLength
}"
:title="actionBtnText"
type="button"
class="btn btn-default ide-staged-action-btn p-0 order-1 align-items-center"
data-placement="bottom"
data-container="body"
data-boundary="viewport"
@click="actionBtnClicked"
>
<icon
:name="actionBtnIcon"
:size="12"
class="ml-auto mr-auto"
/>
</button>
<span
:class="{
'rounded-right': !filesLength
}"
class="ide-commit-file-count order-0 rounded-left text-center"
>
{{ filesLength }}
</span>
</div>
</div>
</header>
<ul
v-if="fileList.length"
v-if="filesLength"
class="multi-file-commit-list list-unstyled append-bottom-0"
>
<li
......
<script>
import { mapActions } from 'vuex';
import tooltip from '~/vue_shared/directives/tooltip';
import Icon from '~/vue_shared/components/icon.vue';
import StageButton from './stage_button.vue';
import UnstageButton from './unstage_button.vue';
......@@ -11,6 +12,9 @@ export default {
StageButton,
UnstageButton,
},
directives: {
tooltip,
},
props: {
file: {
type: Object,
......@@ -50,6 +54,9 @@ export default {
isActive() {
return this.activeFileKey === this.fullKey;
},
tooltipTitle() {
return this.file.path === this.file.name ? '' : this.file.path;
},
},
methods: {
...mapActions([
......@@ -81,29 +88,30 @@ export default {
</script>
<template>
<div
:class="{
'is-active': isActive
}"
class="multi-file-commit-list-item"
>
<div class="multi-file-commit-list-item position-relative">
<button
v-tooltip
:title="tooltipTitle"
:class="{
'is-active': isActive
}"
type="button"
class="multi-file-commit-list-path"
class="multi-file-commit-list-path w-100 border-0 ml-0 mr-0"
@dblclick="fileAction"
@click="openFileInEditor"
>
<span class="multi-file-commit-list-file-path">
<span class="multi-file-commit-list-file-path d-flex align-items-center">
<icon
:name="iconName"
:size="16"
:css-classes="iconClass"
/>{{ file.path }}
/>{{ file.name }}
</span>
</button>
<component
:is="actionComponent"
:path="file.path"
class="d-flex position-absolute"
/>
</div>
</template>
......@@ -25,15 +25,17 @@ export default {
<template>
<div
v-once
class="multi-file-discard-btn"
class="multi-file-discard-btn dropdown"
>
<button
v-tooltip
:aria-label="__('Stage changes')"
:title="__('Stage changes')"
type="button"
class="btn btn-blank append-right-5"
class="btn btn-blank append-right-5 d-flex align-items-center"
data-container="body"
data-boundary="viewport"
data-placement="bottom"
@click.stop="stageChange(path)"
>
<icon
......@@ -43,17 +45,31 @@ export default {
</button>
<button
v-tooltip
:aria-label="__('Discard changes')"
:title="__('Discard changes')"
:title="__('More actions')"
type="button"
class="btn btn-blank"
class="btn btn-blank d-flex align-items-center"
data-container="body"
@click.stop="discardFileChanges(path)"
data-boundary="viewport"
data-placement="bottom"
data-toggle="dropdown"
data-display="static"
>
<icon
:size="12"
name="remove"
name="more"
/>
</button>
<div class="dropdown-menu dropdown-menu-right">
<ul>
<li>
<button
type="button"
@click.stop="discardFileChanges(path)"
>
{{ __('Discard changes') }}
</button>
</li>
</ul>
</div>
</div>
</template>
......@@ -32,8 +32,10 @@ export default {
:aria-label="__('Unstage changes')"
:title="__('Unstage changes')"
type="button"
class="btn btn-blank"
class="btn btn-blank d-flex align-items-center"
data-container="body"
data-boundary="viewport"
data-placement="bottom"
@click="unstageChange(path)"
>
<icon
......
......@@ -93,23 +93,25 @@ export default {
:title="__('Unstaged')"
:key-prefix="$options.stageKeys.unstaged"
:file-list="changedFiles"
:action-btn-text="__('Stage all')"
:action-btn-text="__('Stage all changes')"
:active-file-key="activeFileKey"
class="is-first"
icon-name="unstaged"
action="stageAllChanges"
action-btn-icon="mobile-issue-close"
item-action-component="stage-button"
class="is-first"
icon-name="unstaged"
/>
<commit-files-list
:title="__('Staged')"
:key-prefix="$options.stageKeys.staged"
:file-list="stagedFiles"
:action-btn-text="__('Unstage all')"
:action-btn-text="__('Unstage all changes')"
:staged-list="true"
:active-file-key="activeFileKey"
icon-name="staged"
action="unstageAllChanges"
action-btn-icon="history"
item-action-component="unstage-button"
icon-name="staged"
/>
</template>
<empty-state
......
import { computeDiff } from './diff';
// eslint-disable-next-line no-restricted-globals
self.addEventListener('message', (e) => {
const data = e.data;
// eslint-disable-next-line no-restricted-globals
self.postMessage({
path: data.path,
changes: computeDiff(data.originalContent, data.newContent),
......
......@@ -12,5 +12,6 @@ export const defaultEditorOptions = {
export default [
{
readOnly: model => !!model.file.file_lock,
quickSuggestions: model => !(model.language === 'markdown'),
},
];
import { viewerInformationForPath } from '~/vue_shared/components/content_viewer/lib/viewer_utils';
import { decorateData, sortTree } from '../utils';
// eslint-disable-next-line no-restricted-globals
self.addEventListener('message', e => {
const { data, projectId, branchId, tempFile = false, content = '', base64 = false } = e.data;
......@@ -89,6 +90,7 @@ self.addEventListener('message', e => {
return acc;
}, {});
// eslint-disable-next-line no-restricted-globals
self.postMessage({
entries,
treeList: sortTree(treeList),
......
......@@ -226,7 +226,7 @@
.then(res => res.data)
.then(data => this.checkForSpam(data))
.then((data) => {
if (location.pathname !== data.web_url) {
if (window.location.pathname !== data.web_url) {
visitUrl(data.web_url);
}
......
......@@ -38,7 +38,7 @@
},
deleteIssuable() {
// eslint-disable-next-line no-alert
if (confirm('Issue will be removed! Are you sure?')) {
if (window.confirm('Issue will be removed! Are you sure?')) {
this.deleteLoading = true;
eventHub.$emit('delete.issuable');
......
......@@ -2,7 +2,7 @@
export default {
computed: {
currentPath() {
return location.pathname;
return window.location.pathname;
},
},
};
......
......@@ -44,8 +44,8 @@ export default class LazyLoader {
requestAnimationFrame(() => this.checkElementsInView());
}
checkElementsInView() {
const scrollTop = pageYOffset;
const visHeight = scrollTop + innerHeight + SCROLL_THRESHOLD;
const scrollTop = window.pageYOffset;
const visHeight = scrollTop + window.innerHeight + SCROLL_THRESHOLD;
// Loading Images which are in the current viewport or close to them
this.lazyImages = this.lazyImages.filter((selectedImage) => {
......
......@@ -93,7 +93,7 @@ export default class LinkedTabs {
const newState = `${copySource}${this.currentLocation.search}${this.currentLocation.hash}`;
history.replaceState({
window.history.replaceState({
url: newState,
}, document.title, newState);
return newState;
......
......@@ -197,7 +197,10 @@ export const insertText = (target, text) => {
// eslint-disable-next-line no-param-reassign
target.value = newText;
// eslint-disable-next-line no-param-reassign
target.selectionStart = target.selectionEnd = selectionStart + insertedText.length;
target.selectionStart = selectionStart + insertedText.length;
// eslint-disable-next-line no-param-reassign
target.selectionEnd = selectionStart + insertedText.length;
// Trigger autosave
target.dispatchEvent(new Event('input'));
......
......@@ -12,7 +12,7 @@ export function formatRelevantDigits(number) {
let digitsLeft = '';
let relevantDigits = 0;
let formattedNumber = '';
if (!isNaN(Number(number))) {
if (!Number.isNaN(Number(number))) {
digitsLeft = number.toString().split('.')[0];
switch (digitsLeft.length) {
case 1:
......
......@@ -35,7 +35,7 @@ const LineHighlighter = function(options = {}) {
options.highlightLineClass = options.highlightLineClass || 'hll';
options.fileHolderSelector = options.fileHolderSelector || '.file-holder';
options.scrollFileHolder = options.scrollFileHolder || false;
options.hash = options.hash || location.hash;
options.hash = options.hash || window.location.hash;
this.options = options;
this._hash = options.hash;
......@@ -145,6 +145,8 @@ LineHighlighter.prototype.highlightRange = function(range) {
var i, lineNumber, ref, ref1, results;
if (range[1]) {
results = [];
// eslint-disable-next-line no-multi-assign
for (lineNumber = i = ref = range[0], ref1 = range[1]; ref <= ref1 ? i <= ref1 : i >= ref1; lineNumber = ref <= ref1 ? (i += 1) : (i -= 1)) {
results.push(this.highlightLine(lineNumber));
}
......@@ -170,7 +172,7 @@ LineHighlighter.prototype.setHash = function(firstLineNumber, lastLineNumber) {
//
// This method is stubbed in tests.
LineHighlighter.prototype.__setLocationHash__ = function(value) {
return history.pushState({
return window.history.pushState({
url: value
// We're using pushState instead of assigning location.hash directly to
// prevent the page from scrolling on the hashchange event
......
......@@ -18,13 +18,13 @@ export default class Milestone {
return $('a[data-toggle="tab"]').on('show.bs.tab', (e) => {
const $target = $(e.target);
location.hash = $target.attr('href');
window.location.hash = $target.attr('href');
this.loadTab($target);
});
}
// eslint-disable-next-line class-methods-use-this
loadInitialTab() {
const $target = $(`.js-milestone-tabs a[href="${location.hash}"]`);
const $target = $(`.js-milestone-tabs a[href="${window.location.hash}"]`);
if ($target.length) {
$target.tab('show');
......
......@@ -16,10 +16,10 @@ export default class MilestoneSelect {
typeof currentProject === 'string' ? JSON.parse(currentProject) : currentProject;
}
this.init(els, options);
MilestoneSelect.init(els, options);
}
init(els, options) {
static init(els, options) {
let $els = $(els);
if (!els) {
......@@ -224,7 +224,6 @@ export default class MilestoneSelect {
$selectBox.hide();
$value.css('display', '');
if (data.milestone != null) {
data.milestone.full_path = this.currentProject.full_path;
data.milestone.remaining = timeFor(data.milestone.due_date);
data.milestone.name = data.milestone.title;
$value.html(milestoneLinkTemplate(data.milestone));
......
......@@ -139,7 +139,7 @@ export default {
this.updateAspectRatio = true;
},
toggleAspectRatio() {
this.updatedAspectRatios = this.updatedAspectRatios += 1;
this.updatedAspectRatios += 1;
if (this.store.getMetricsCount() === this.updatedAspectRatios) {
this.updateAspectRatio = !this.updateAspectRatio;
this.updatedAspectRatios = 0;
......
......@@ -154,7 +154,7 @@ export default {
point.x = e.clientX;
point.y = e.clientY;
point = point.matrixTransform(this.$refs.graphData.getScreenCTM().inverse());
point.x = point.x += 7;
point.x += 7;
const firstTimeSeries = this.timeSeries[0];
const timeValueOverlay = firstTimeSeries.timeSeriesScaleX.invert(point.x);
const overlayIndex = bisectDate(firstTimeSeries.values, timeValueOverlay, 1);
......
......@@ -97,7 +97,7 @@ export default {
? this.deploymentFlagData.seriesIndex
: indexFromCoordinates;
const value = series.values[index] && series.values[index].value;
if (isNaN(value)) {
if (Number.isNaN(value)) {
return '-';
}
return `${formatRelevantDigits(value)}${this.unitOfDisplay}`;
......
......@@ -73,7 +73,7 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
timeSeriesScaleX.ticks(d3.timeMinute, 60);
timeSeriesScaleY.domain(yDom);
const defined = d => !isNaN(d.value) && d.value != null;
const defined = d => !Number.isNaN(d.value) && d.value != null;
const lineFunction = d3
.line()
......
......@@ -112,6 +112,8 @@ export default (function() {
fill: "#444"
});
ref = this.days;
// eslint-disable-next-line no-multi-assign
for (mm = j = 0, len = ref.length; j < len; mm = (j += 1)) {
day = ref[mm];
if (cuday !== day[0] || cumonth !== day[1]) {
......@@ -285,6 +287,8 @@ export default (function() {
r = this.r;
ref = commit.parents;
results = [];
// eslint-disable-next-line no-multi-assign
for (i = j = 0, len = ref.length; j < len; i = (j += 1)) {
parent = ref[i];
parentCommit = this.preparedCommits[parent[0]];
......
......@@ -315,7 +315,7 @@ export default class Notes {
if (discussionNoteForm.length) {
if ($textarea.val() !== '') {
if (
!confirm('Are you sure you want to cancel creating this comment?')
!window.confirm('Are you sure you want to cancel creating this comment?')
) {
return;
}
......@@ -329,7 +329,7 @@ export default class Notes {
newText = $textarea.val();
if (originalText !== newText) {
if (
!confirm('Are you sure you want to cancel editing this comment?')
!window.confirm('Are you sure you want to cancel editing this comment?')
) {
return;
}
......
......@@ -152,7 +152,7 @@ export default {
const msg = 'Are you sure you want to cancel creating this comment?';
// eslint-disable-next-line no-alert
if (!confirm(msg)) {
if (!window.confirm(msg)) {
return;
}
}
......
......@@ -77,7 +77,7 @@ export default {
},
deleteHandler() {
// eslint-disable-next-line no-alert
if (confirm('Are you sure you want to delete this comment?')) {
if (window.confirm('Are you sure you want to delete this comment?')) {
this.isDeleting = true;
this.deleteNote(this.note)
......@@ -129,7 +129,7 @@ export default {
formCancelHandler(shouldConfirm, isDirty) {
if (shouldConfirm && isDirty) {
// eslint-disable-next-line no-alert
if (!confirm('Are you sure you want to cancel editing this comment?'))
if (!window.confirm('Are you sure you want to cancel editing this comment?'))
return;
}
this.$refs.noteBody.resetAutoSave();
......
......@@ -36,7 +36,9 @@ export default (function() {
var author_graph, author_header;
author_header = _this.create_author_header(d);
$(".contributors-list").append(author_header);
_this.authors[d.author_name] = author_graph = new ContributorsAuthorGraph(d.dates);
author_graph = new ContributorsAuthorGraph(d.dates);
_this.authors[d.author_name] = author_graph;
return author_graph.draw();
};
})(this));
......
......@@ -111,10 +111,15 @@ export default {
parse_log_entry: function(log_entry, field, date_range) {
var parsed_entry;
parsed_entry = {};
parsed_entry.author_name = log_entry.author_name;
parsed_entry.author_email = log_entry.author_email;
parsed_entry.dates = {};
parsed_entry.commits = parsed_entry.additions = parsed_entry.deletions = 0;
parsed_entry.commits = 0;
parsed_entry.additions = 0;
parsed_entry.deletions = 0;
_.each(_.omit(log_entry, 'author_name', 'author_email'), (function(_this) {
return function(value, key) {
if (_this.in_range(value.date, date_range)) {
......
......@@ -187,7 +187,7 @@ export default class UserTabs {
let newState = source;
newState = newState.replace(/\/+$/, '');
newState += this.windowLocation.search + this.windowLocation.hash;
history.replaceState(
window.history.replaceState(
{
url: newState,
},
......
......@@ -91,6 +91,7 @@ export default {
class="js-ci-action btn btn-blank
btn-transparent ci-action-icon-container ci-action-icon-wrapper"
data-container="body"
data-boundary="viewport"
@click="onClickAction"
>
<icon :name="actionIcon"/>
......
......@@ -87,6 +87,7 @@ export default {
data-toggle="dropdown"
data-container="body"
data-boundary="viewport"
data-display="static"
class="dropdown-menu-toggle build-content"
>
......
......@@ -165,6 +165,7 @@ export default {
class="mini-pipeline-graph-dropdown-toggle js-builds-dropdown-button"
data-placement="top"
data-toggle="dropdown"
data-display="static"
type="button"
aria-haspopup="true"
aria-expanded="false"
......
......@@ -139,6 +139,8 @@ import _ from 'underscore';
var array, binary, i, k, len, v;
binary = atob(dataURL.split(',')[1]);
array = [];
// eslint-disable-next-line no-multi-assign
for (k = i = 0, len = binary.length; i < len; k = (i += 1)) {
v = binary[k];
array.push(binary.charCodeAt(k));
......
......@@ -91,6 +91,8 @@ export default class ProjectFindFile {
var blobItemUrl, filePath, html, i, j, len, matches, results;
this.element.find(".tree-table > tbody").empty();
results = [];
// eslint-disable-next-line no-multi-assign
for (i = j = 0, len = filePaths.length; j < len; i = (j += 1)) {
filePath = filePaths[i];
if (i === 20) {
......@@ -150,7 +152,7 @@ export default class ProjectFindFile {
}
goToTree() {
return location.href = this.options.treeUrl;
return window.location.href = this.options.treeUrl;
}
goToBlob() {
......
......@@ -2,7 +2,7 @@ import { visitUrl } from './lib/utils/url_utility';
export default function projectImport() {
setTimeout(() => {
visitUrl(location.href);
visitUrl(window.location.href);
}, 5000);
}
......@@ -107,7 +107,7 @@ export default class PrometheusMetrics {
if (data && data.success) {
stop(data);
} else {
this.backOffRequestCounter = this.backOffRequestCounter += 1;
this.backOffRequestCounter += 1;
if (this.backOffRequestCounter < 3) {
next();
} else {
......
......@@ -438,7 +438,7 @@ export default class SearchAutocomplete {
}
onClick(item, $el, e) {
if (location.pathname.indexOf(item.url) !== -1) {
if (window.location.pathname.indexOf(item.url) !== -1) {
if (!e.metaKey) e.preventDefault();
if (!this.badgePresent) {
if (item.category === 'Projects') {
......
......@@ -42,8 +42,8 @@ export default function initSettingsPanels() {
}
});
if (location.hash) {
const $target = $(location.hash);
if (window.location.hash) {
const $target = $(window.location.hash);
if ($target.length && $target.hasClass('settings')) {
expandSection($target);
}
......
......@@ -13,8 +13,8 @@ export default class ShortcutsFindFile extends ShortcutsNavigation {
element === this.projectFindFile.inputElement[0] &&
(combo === 'up' || combo === 'down' || combo === 'esc' || combo === 'enter')
) {
// when press up/down key in textbox, cusor prevent to move to home/end
event.preventDefault();
// when press up/down key in textbox, cursor prevent to move to home/end
e.preventDefault();
return false;
}
......
......@@ -54,7 +54,7 @@ export default {
updateConfidentialAttribute(confidential) {
this.service
.update('issue', { confidential })
.then(() => location.reload())
.then(() => window.location.reload())
.catch(() => {
Flash(
__(
......
......@@ -76,7 +76,7 @@ export default {
.update(this.issuableType, {
discussion_locked: locked,
})
.then(() => location.reload())
.then(() => window.location.reload())
.catch(() =>
Flash(
this.__(
......
......@@ -80,7 +80,7 @@ export default class SidebarMediator {
return this.service.moveIssue(this.store.moveToProjectId)
.then(response => response.json())
.then((data) => {
if (location.pathname !== data.web_url) {
if (window.location.pathname !== data.web_url) {
visitUrl(data.web_url);
}
});
......
export default () => {
const { protocol, host, pathname } = location;
const { protocol, host, pathname } = window.location;
const shareBtn = document.querySelector('.js-share-btn');
const embedBtn = document.querySelector('.js-embed-btn');
const snippetUrlArea = document.querySelector('.js-snippet-url-area');
......
......@@ -259,6 +259,7 @@ function UsersSelect(currentUser, els, options = {}) {
showDivider = 0;
if (firstUser) {
// Move current user to the front of the list
// eslint-disable-next-line no-multi-assign
for (index = j = 0, len = users.length; j < len; index = (j += 1)) {
obj = users[index];
if (obj.username === firstUser) {
......@@ -561,6 +562,8 @@ function UsersSelect(currentUser, els, options = {}) {
if (firstUser) {
// Move current user to the front of the list
ref = data.results;
// eslint-disable-next-line no-multi-assign
for (index = j = 0, len = ref.length; j < len; index = (j += 1)) {
obj = ref[index];
if (obj.username === firstUser) {
......
......@@ -105,7 +105,7 @@ export default {
MRWidgetService.fetchMetrics(this.metricsUrl)
.then((res) => {
if (res.status === statusCodes.NO_CONTENT) {
this.backOffRequestCounter = this.backOffRequestCounter += 1;
this.backOffRequestCounter += 1;
/* eslint-disable no-unused-expressions */
this.backOffRequestCounter < 3 ? next() : stop(res);
} else {
......
......@@ -128,11 +128,6 @@ table {
border-spacing: 0;
}
.tooltip {
// Fix bootstrap4 bug whereby tooltips flicker when they are hovered over their borders
pointer-events: none;
}
.popover {
font-size: 14px;
}
......@@ -213,6 +208,15 @@ table {
&:not(:last-of-type) {
border-bottom: 1px solid $well-inner-border;
}
p,
ol,
ul,
.form-group {
&:last-of-type {
margin-bottom: 0;
}
}
}
.badge.badge-gray {
......@@ -264,15 +268,36 @@ pre code {
white-space: pre-wrap;
}
.alert,
.flash-notice {
border-radius: 0;
}
.alert-success {
background-color: $green-500;
border-color: $green-500;
}
.alert-info {
background-color: $blue-500;
border-color: $blue-500;
}
.alert-warning {
background-color: $orange-500;
border-color: $orange-500;
}
.alert-danger {
background-color: $red-500;
border-color: $red-500;
}
.alert-success,
.alert-info,
.alert-warning,
.alert-danger,
.flash-notice {
border-radius: 0;
color: $white-light;
h4,
......
......@@ -54,10 +54,6 @@ body {
&.limit-container-width {
max-width: $limited-layout-width;
}
&.limit-container-width-sm {
max-width: $limited-layout-width-sm;
}
}
.alert-wrapper {
......
......@@ -347,7 +347,7 @@
.empty-state .project-item-select-holder.btn-group {
float: none;
display: inline-block;
justify-content: center;
.btn {
// overrides styles applied to plain `.empty-state .btn`
......
......@@ -244,10 +244,11 @@ $tooltip-font-size: 12px;
/*
* Padding
*/
$gl-padding-24: 24px;
$gl-padding: 16px;
$gl-padding-8: 8px;
$gl-padding-4: 4px;
$gl-padding-8: 8px;
$gl-padding: 16px;
$gl-padding-24: 24px;
$gl-padding-32: 32px;
$gl-col-padding: 15px;
$gl-input-padding: 10px;
$gl-vert-padding: 6px;
......@@ -265,7 +266,6 @@ $header-height: 40px;
$ide-statusbar-height: 25px;
$fixed-layout-width: 1280px;
$limited-layout-width: 990px;
$limited-layout-width-sm: 790px;
$container-text-max-width: 540px;
$gl-avatar-size: 40px;
$error-exclamation-point: $red-500;
......@@ -834,3 +834,4 @@ $font-family-sans-serif: $regular_font;
$font-family-monospace: $monospace_font;
$input-line-height: 20px;
$btn-line-height: 20px;
$table-accent-bg: $gray-light;
......@@ -193,6 +193,10 @@
display: inline-flex;
}
.ci-status-icon svg {
vertical-align: text-bottom;
}
> .ci-status-link,
> .btn,
> .commit-sha-group {
......
......@@ -280,7 +280,7 @@
width: 150px;
flex-shrink: 0;
.label {
.badge {
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
......
......@@ -1001,7 +1001,7 @@ button.mini-pipeline-graph-dropdown-toggle {
/**
* Center dropdown menu in mini graph
*/
&.dropdown-menu {
.dropdown &.dropdown-menu {
transform: translate(-80%, 0);
@media (min-width: map-get($grid-breakpoints, md)) {
......
......@@ -16,7 +16,7 @@
.application-theme {
label {
margin: 0 $gl-padding $gl-padding 0;
margin: 0 $gl-padding-32 $gl-padding 0;
text-align: center;
}
......@@ -24,7 +24,7 @@
font-size: 0;
height: 48px;
border-radius: 4px;
min-width: 135px;
min-width: 112px;
margin-bottom: $gl-padding-8;
&.ui-indigo {
......@@ -75,7 +75,8 @@
.syntax-theme {
label {
margin-right: 20px;
margin-right: $gl-padding-32;
margin-bottom: $gl-padding;
text-align: center;
.preview {
......@@ -84,7 +85,6 @@
img {
border-radius: 4px;
max-width: 100%;
}
}
......
......@@ -858,7 +858,6 @@ pre.light-well {
.git-clone-holder {
width: 380px;
height: 28px;
.btn-clipboard {
border: 1px solid $border-color;
......
......@@ -540,36 +540,12 @@
margin-right: -$grid-size;
min-height: 60px;
.multi-file-commit-list-item {
margin-left: 0;
margin-right: 0;
}
&.form-text.text-muted {
margin-left: 0;
right: 0;
}
}
.multi-file-commit-list-item {
&.is-active {
background-color: $white-normal;
}
.multi-file-discard-btn {
display: none;
margin-top: -2px;
margin-left: auto;
color: $gl-link-color;
}
&:hover {
.multi-file-discard-btn {
display: flex;
}
}
}
.multi-file-addition,
.multi-file-addition-solid {
color: $green-500;
......@@ -599,7 +575,7 @@
}
}
.multi-file-commit-list-item,
.multi-file-commit-list-path,
.ide-file-list .file {
display: flex;
align-items: center;
......@@ -616,11 +592,9 @@
}
.multi-file-commit-list-path {
padding: 0;
background: none;
border: 0;
text-align: left;
width: 100%;
&.is-active {
background-color: $white-normal;
}
&:hover,
&:focus {
......@@ -635,7 +609,7 @@
}
.multi-file-commit-list-file-path {
@include str-truncated(100%);
@include str-truncated(calc(100% - 30px));
&:hover {
text-decoration: underline;
......@@ -646,6 +620,16 @@
}
}
.multi-file-discard-btn {
top: 4px;
right: 8px;
bottom: 4px;
svg {
top: 0;
}
}
.multi-file-commit-form {
position: relative;
background-color: $white-light;
......@@ -840,18 +824,20 @@
}
.ide-staged-action-btn {
margin-left: auto;
line-height: 22px;
width: 22px;
margin-left: -1px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
> svg {
top: 0;
}
}
.ide-commit-file-count {
min-width: 22px;
margin-left: auto;
background-color: $gray-light;
border-radius: $border-radius-default;
border: 1px solid $white-dark;
line-height: 20px;
text-align: center;
}
.ide-commit-radios {
......
......@@ -296,7 +296,8 @@
}
.btn-clipboard {
margin-left: 5px;
background-color: $white-light;
border: 1px solid $theme-gray-200;
}
.deploy-token-help-block {
......
module IssuesAction
extend ActiveSupport::Concern
include IssuableCollections
include IssuesCalendar
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def issues
......@@ -17,18 +18,9 @@ module IssuesAction
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def issues_calendar
@issues = issuables_collection
.non_archived
.with_due_date
.limit(100)
respond_to do |format|
format.ics { response.headers['Content-Disposition'] = 'inline' }
end
render_issues_calendar(issuables_collection)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
private
......
module IssuesCalendar
extend ActiveSupport::Concern
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def render_issues_calendar(issuables)
@issues = issuables
.non_archived
.with_due_date
.limit(100)
respond_to do |format|
format.ics do
# NOTE: with text/calendar as Content-Type, the browser always downloads
# the content as a file (even ignoring the Content-Disposition
# header). We want to display the content inline when accessed
# from GitLab, similarly to the RSS feed.
if request.referer&.start_with?(::Settings.gitlab.base_url)
response.headers['Content-Type'] = 'text/plain'
end
end
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
end
......@@ -7,6 +7,7 @@ class Projects::ArtifactsController < Projects::ApplicationController
before_action :authorize_read_build!
before_action :authorize_update_build!, only: [:keep]
before_action :extract_ref_name_and_path
before_action :set_request_format, only: [:file]
before_action :validate_artifacts!
before_action :entry, only: [:file]
......@@ -101,4 +102,12 @@ class Projects::ArtifactsController < Projects::ApplicationController
render_404 unless @entry.exists?
end
def set_request_format
request.format = :html if set_request_format?
end
def set_request_format?
request.format != :json
end
end
......@@ -7,6 +7,7 @@ class Projects::BlobController < Projects::ApplicationController
prepend_before_action :authenticate_user!, only: [:edit]
before_action :set_request_format, only: [:edit, :show, :update]
before_action :require_non_empty_project, except: [:new, :create]
before_action :authorize_download_code!
......@@ -188,6 +189,18 @@ class Projects::BlobController < Projects::ApplicationController
.last_for_path(@repository, @ref, @path).sha
end
# In Rails 4.2 if params[:format] is empty, Rails set it to :html
# But since Rails 5.0 the framework now looks for an extension.
# E.g. for `blob/master/CHANGELOG.md` in Rails 4 the format would be `:html`, but in Rails 5 on it'd be `:md`
# This before_action explicitly sets the `:html` format for all requests unless `:format` is set by a client e.g. by JS for XHR requests.
def set_request_format
request.format = :html if set_request_format?
end
def set_request_format?
params[:id].present? && params[:format].blank? && request.format != "json"
end
def show_html
environment_params = @repository.branch_exists?(@ref) ? { ref: @ref } : { commit: @commit }
@environment = EnvironmentsFinder.new(@project, current_user, environment_params).execute.last
......
......@@ -4,6 +4,7 @@ class Projects::IssuesController < Projects::ApplicationController
include IssuableActions
include ToggleAwardEmoji
include IssuableCollections
include IssuesCalendar
include SpammableActions
prepend_before_action :authenticate_user!, only: [:new]
......@@ -40,14 +41,7 @@ class Projects::IssuesController < Projects::ApplicationController
end
def calendar
@issues = @issuables
.non_archived
.with_due_date
.limit(100)
respond_to do |format|
format.ics { response.headers['Content-Disposition'] = 'inline' }
end
render_issues_calendar(@issuables)
end
def new
......
......@@ -115,7 +115,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
end
format.json do
render json: @merge_request.to_json(include: { milestone: {}, assignee: { only: [:name, :username], methods: [:avatar_url] }, labels: { methods: :text_color } }, methods: [:task_status, :task_status_short])
render json: serializer.represent(@merge_request, serializer: 'basic')
end
end
rescue ActiveRecord::StaleObjectError
......
......@@ -5,4 +5,8 @@ class MergeRequestBasicEntity < IssuableSidebarEntity
expose :state
expose :source_branch_exists?, as: :source_branch_exists
expose :rebase_in_progress?, as: :rebase_in_progress
expose :milestone, using: API::Entities::Milestone
expose :labels, using: LabelEntity
expose :assignee, using: API::Entities::UserBasic
expose :task_status, :task_status_short
end
......@@ -25,14 +25,12 @@ module Ci
valid = true
if Feature.enabled?('ci_job_request_with_tags_matcher')
# pick builds that does not have other tags than runner's one
builds = builds.matches_tag_ids(runner.tags.ids)
# pick builds that does not have other tags than runner's one
builds = builds.matches_tag_ids(runner.tags.ids)
# pick builds that have at least one tag
unless runner.run_untagged?
builds = builds.with_any_tags
end
# pick builds that have at least one tag
unless runner.run_untagged?
builds = builds.with_any_tags
end
builds.find do |build|
......
......@@ -11,7 +11,7 @@ module Projects
order: { due_date: :asc, title: :asc }
}
finder_params[:group_ids] = @project.group.self_and_ancestors.select(:id) if @project.group
finder_params[:group_ids] = @project.group.self_and_ancestors_ids if @project.group
MilestonesFinder.new(finder_params).execute.select([:iid, :title])
end
......
class FaviconUploader < AttachmentUploader
EXTENSION_WHITELIST = %w[png ico].freeze
include CarrierWave::MiniMagick
version :favicon_main do
process resize_to_fill: [32, 32]
process convert: 'png'
def full_filename(filename)
filename_for_different_format(super(filename), 'png')
end
end
def extension_whitelist
EXTENSION_WHITELIST
end
......
......@@ -25,7 +25,7 @@
= f.label :favicon, 'Favicon', class: 'col-sm-2 col-form-label'
.col-sm-10
- if @appearance.favicon?
= image_tag @appearance.favicon.favicon_main.url, class: 'appearance-light-logo-preview'
= image_tag @appearance.favicon_url, class: 'appearance-light-logo-preview'
- if @appearance.persisted?
%br
= link_to 'Remove favicon', favicon_admin_appearances_path, data: { confirm: "Favicon will be removed. Are you sure?"}, method: :delete, class: "btn btn-inverted btn-remove btn-sm remove-logo"
......@@ -33,9 +33,9 @@
= f.hidden_field :favicon_cache
= f.file_field :favicon, class: ''
.hint
Maximum file size is 1MB. Allowed image formats are #{favicon_extension_whitelist}.
Maximum file size is 1MB. Image size must be 32x32px. Allowed image formats are #{favicon_extension_whitelist}.
%br
The resulting favicons will be cropped to be square and scaled down to a size of 32x32 px.
Images with incorrect dimensions are not resized automatically, and may result in unexpected behavior.
%fieldset.sign-in
%legend
......
- if current_user
.dropdown
%button.dropdown-toggle{ href: '#', "data-toggle" => "dropdown" }
%button.dropdown-toggle{ href: '#', "data-toggle" => "dropdown", 'data-display' => 'static' }
= icon('globe')
%span.light Visibility:
- if params[:visibility_level].present?
......
......@@ -18,7 +18,7 @@
- if can_create_subgroups
.btn-group.new-project-subgroup.droplab-dropdown.js-new-project-subgroup{ data: { project_path: new_project_path(namespace_id: @group.id), subgroup_path: new_group_path(parent_id: @group.id) } }
%input.btn.btn-success.dropdown-primary.js-new-group-child{ type: "button", value: new_project_label, data: { action: "new-project" } }
%button.btn.btn-success.dropdown-toggle.js-dropdown-toggle{ type: "button", data: { "dropdown-trigger" => "#new-project-or-subgroup-dropdown" } }
%button.btn.btn-success.dropdown-toggle.js-dropdown-toggle{ type: "button", data: { "dropdown-trigger" => "#new-project-or-subgroup-dropdown", 'display' => 'static' } }
= icon("caret-down", class: "dropdown-btn-icon")
%ul#new-project-or-subgroup-dropdown.dropdown-menu.dropdown-menu-right{ data: { dropdown: true } }
%li.droplab-item-selected{ role: "button", data: { value: "new-project", text: new_project_label } }
......
......@@ -443,8 +443,6 @@
.col-md-6
.alert.alert-success
= lorem
.alert.alert-primary
= lorem
.alert.alert-info
= lorem
.col-md-6
......
......@@ -20,7 +20,7 @@
- else
.input-group-prepend.static-namespace.has-tooltip{ title: user_url(current_user.username) + '/' }
.input-group-text
.input-group-text.border-0
#{user_url(current_user.username)}/
= hidden_field_tag :namespace_id, value: current_user.namespace_id
.form-group.col-12.col-sm-6.project-path
......
%li.header-new.dropdown
= link_to new_project_path, class: "header-new-dropdown-toggle has-tooltip qa-new-menu-toggle", title: "New...", ref: 'tooltip', aria: { label: "New..." }, data: { toggle: 'dropdown', placement: 'bottom', container: 'body' } do
= link_to new_project_path, class: "header-new-dropdown-toggle has-tooltip qa-new-menu-toggle", title: "New...", ref: 'tooltip', aria: { label: "New..." }, data: { toggle: 'dropdown', placement: 'bottom', container: 'body', display: 'static' } do
= sprite_icon('plus-square', size: 16)
= sprite_icon('angle-down', css_class: 'caret-down')
.dropdown-menu.dropdown-menu-right
......
......@@ -16,7 +16,7 @@
- else
.input-group-prepend.static-namespace.has-tooltip{ title: user_url(current_user.username) + '/' }
.input-group-text
.input-group-text.border-0
#{user_url(current_user.username)}/
= f.hidden_field :namespace_id, value: current_user.namespace_id
.form-group.project-path.col-sm-6
......
......@@ -3,7 +3,7 @@
- if !project.empty_repo? && can?(current_user, :download_code, project)
- archive_prefix = "#{project.path}-#{ref.tr('/', '-')}"
.project-action-button.dropdown.inline>
%button.btn.has-tooltip{ title: s_('DownloadSource|Download'), 'data-toggle' => 'dropdown', 'aria-label' => s_('DownloadSource|Download') }
%button.btn.has-tooltip{ title: s_('DownloadSource|Download'), 'data-toggle' => 'dropdown', 'aria-label' => s_('DownloadSource|Download'), 'data-display' => 'static' }
= sprite_icon('download')
= icon("caret-down")
%span.sr-only= _('Select Archive Format')
......
......@@ -8,7 +8,7 @@
- if show_menu
.project-action-button.dropdown.inline
%a.btn.dropdown-toggle.has-tooltip{ href: '#', title: _('Create new...'), 'data-toggle' => 'dropdown', 'data-container' => 'body', 'aria-label' => _('Create new...') }
%a.btn.dropdown-toggle.has-tooltip{ href: '#', title: _('Create new...'), 'data-toggle' => 'dropdown', 'data-container' => 'body', 'aria-label' => _('Create new...'), 'data-display' => 'static' }
= icon('plus')
= icon("caret-down")
%ul.dropdown-menu.dropdown-menu-right.project-home-dropdown
......
= javascript_include_tag 'https://apis.google.com/js/api.js'
- external_link_icon = icon('external-link')
- zones_link_url = 'https://cloud.google.com/compute/docs/regions-zones/regions-zones'
- machine_type_link_url = 'https://cloud.google.com/compute/docs/machine-types'
- pricing_link_url = 'https://cloud.google.com/compute/pricing#machinetype'
- help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe
- help_link_end = ' %{external_link_icon}</a>'.html_safe % { external_link_icon: external_link_icon }
%p
- link_to_help_page = link_to(s_('ClusterIntegration|help page'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
......@@ -7,15 +13,15 @@
= form_for @cluster, html: { class: 'js-gke-cluster-creation prepend-top-20', data: { token: token_in_session } }, url: gcp_namespace_project_clusters_path(@project.namespace, @project), as: :cluster do |field|
= form_errors(@cluster)
.form-group
= field.label :name, s_('ClusterIntegration|Kubernetes cluster name')
= field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-light'
= field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name')
.form-group
= field.label :environment_scope, s_('ClusterIntegration|Environment scope')
= field.label :environment_scope, s_('ClusterIntegration|Environment scope'), class: 'label-light'
= field.text_field :environment_scope, class: 'form-control', readonly: !has_multiple_clusters?(@project), placeholder: s_('ClusterIntegration|Environment scope')
= field.fields_for :provider_gcp, @cluster.provider_gcp do |provider_gcp_field|
.form-group
= provider_gcp_field.label :gcp_project_id, s_('ClusterIntegration|Google Cloud Platform project')
= provider_gcp_field.label :gcp_project_id, s_('ClusterIntegration|Google Cloud Platform project'), class: 'label-light'
.js-gcp-project-id-dropdown-entry-point{ data: { docsUrl: 'https://console.cloud.google.com/home/dashboard' } }
= provider_gcp_field.hidden_field :gcp_project_id
.dropdown
......@@ -26,8 +32,7 @@
%span.form-text.text-muted &nbsp;
.form-group
= provider_gcp_field.label :zone, s_('ClusterIntegration|Zone')
= link_to(s_('ClusterIntegration|See zones'), 'https://cloud.google.com/compute/docs/regions-zones/regions-zones', target: '_blank', rel: 'noopener noreferrer')
= provider_gcp_field.label :zone, s_('ClusterIntegration|Zone'), class: 'label-light'
.js-gcp-zone-dropdown-entry-point
= provider_gcp_field.hidden_field :zone
.dropdown
......@@ -35,13 +40,15 @@
%span.dropdown-toggle-text
= _('Select project to choose zone')
= icon('chevron-down')
%p.form-text.text-muted
= s_('ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}.').html_safe % { help_link_start: help_link_start % { url: zones_link_url }, help_link_end: help_link_end }
.form-group
= provider_gcp_field.label :num_nodes, s_('ClusterIntegration|Number of nodes')
= provider_gcp_field.label :num_nodes, s_('ClusterIntegration|Number of nodes'), class: 'label-light'
= provider_gcp_field.text_field :num_nodes, class: 'form-control', placeholder: '3'
.form-group
= provider_gcp_field.label :machine_type, s_('ClusterIntegration|Machine type')
= provider_gcp_field.label :machine_type, s_('ClusterIntegration|Machine type'), class: 'label-light'
.js-gcp-machine-type-dropdown-entry-point
= provider_gcp_field.hidden_field :machine_type
.dropdown
......@@ -49,6 +56,8 @@
%span.dropdown-toggle-text
= _('Select project and zone to choose machine type')
= icon('chevron-down')
%p.form-text.text-muted
= s_('ClusterIntegration|Learn more about %{help_link_start_machine_type}machine types%{help_link_end} and %{help_link_start_pricing}pricing%{help_link_end}.').html_safe % { help_link_start_machine_type: help_link_start % { url: machine_type_link_url }, help_link_start_pricing: help_link_start % { url: pricing_link_url }, help_link_end: help_link_end }
.form-group
= field.submit s_('ClusterIntegration|Create Kubernetes cluster'), class: 'js-gke-cluster-creation-submit btn btn-success', disabled: true
......@@ -10,9 +10,8 @@
.settings-content
- if @new_deploy_token.persisted?
= render 'projects/deploy_tokens/new_deploy_token', deploy_token: @new_deploy_token
- else
%h5.prepend-top-0
= s_('DeployTokens|Add a deploy token')
= render 'projects/deploy_tokens/form', project: @project, token: @new_deploy_token, presenter: @deploy_tokens
%hr
%h5.prepend-top-0
= s_('DeployTokens|Add a deploy token')
= render 'projects/deploy_tokens/form', project: @project, token: @new_deploy_token, presenter: @deploy_tokens
%hr
= render 'projects/deploy_tokens/table', project: @project, active_tokens: @deploy_tokens
.created-deploy-token-container
%h5.prepend-top-0
= s_('DeployTokens|Your New Deploy Token')
.created-deploy-token-container.info-well
.well-segment
%h5.prepend-top-0
= s_('DeployTokens|Your New Deploy Token')
.form-group
= text_field_tag 'deploy-token-user', deploy_token.username, readonly: true, class: 'deploy-token-field form-control js-select-on-focus'
= clipboard_button(text: deploy_token.username, title: s_('DeployTokens|Copy username to clipboard'), placement: 'left')
%span.deploy-token-help-block.prepend-top-5.text-success= s_("DeployTokens|Use this username as a login.")
.form-group
.input-group
= text_field_tag 'deploy-token-user', deploy_token.username, readonly: true, class: 'deploy-token-field form-control js-select-on-focus'
.input-group-append
= clipboard_button(text: deploy_token.username, title: s_('DeployTokens|Copy username to clipboard'), placement: 'left')
%span.deploy-token-help-block.prepend-top-5.text-success= s_("DeployTokens|Use this username as a login.")
.form-group
= text_field_tag 'deploy-token', deploy_token.token, readonly: true, class: 'deploy-token-field form-control js-select-on-focus'
= clipboard_button(text: deploy_token.token, title: s_('DeployTokens|Copy deploy token to clipboard'), placement: 'left')
%span.deploy-token-help-block.prepend-top-5.text-danger= s_("DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again.")
%hr
.form-group
.input-group
= text_field_tag 'deploy-token', deploy_token.token, readonly: true, class: 'deploy-token-field form-control js-select-on-focus'
.input-group-append
= clipboard_button(text: deploy_token.token, title: s_('DeployTokens|Copy deploy token to clipboard'), placement: 'left')
%span.deploy-token-help-block.prepend-top-5.text-danger= s_("DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again.")
- @content_class = "limit-container-width" unless fluid_layout
- @no_container = true
- breadcrumb_title _("Details")
......@@ -6,7 +7,7 @@
= render "home_panel"
.project-empty-note-panel
%div{ class: [container_class, ("limit-container-width-sm" unless fluid_layout)] }
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
.prepend-top-20
%h4
= _('The repository for this project is empty')
......@@ -36,7 +37,7 @@
= render 'stat_anchor_list', anchors: @project.empty_repo_statistics_buttons
- if can?(current_user, :push_code, @project)
%div{ class: [container_class, ("limit-container-width-sm" unless fluid_layout)] }
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
.prepend-top-20
.empty_wrapper
%h3#repo-command-line-instructions.page-title-empty
......
......@@ -18,7 +18,7 @@
%button.btn.js-create-merge-request.btn-success.btn-inverted{ type: 'button', data: { action: data_action } }
= value
%button.btn.create-merge-request-dropdown-toggle.dropdown-toggle.btn-success.btn-inverted.js-dropdown-toggle{ type: 'button', data: { dropdown: { trigger: '#create-merge-request-dropdown' } } }
%button.btn.create-merge-request-dropdown-toggle.dropdown-toggle.btn-success.btn-inverted.js-dropdown-toggle{ type: 'button', data: { dropdown: { trigger: '#create-merge-request-dropdown' }, display: 'static' } }
= icon('caret-down')
.droplab-dropdown
......
......@@ -3,7 +3,7 @@
.mr-version-menus-container.content-block
Changes between
%span.dropdown.inline.mr-version-dropdown
%a.dropdown-toggle.btn.btn-default{ data: {toggle: :dropdown} }
%a.dropdown-toggle.btn.btn-default{ data: { toggle: :dropdown, display: 'static' } }
%span
- if @merge_request_diff.latest?
latest version
......@@ -36,7 +36,7 @@
- if @merge_request_diff.base_commit_sha
and
%span.dropdown.inline.mr-version-compare-dropdown
%a.btn.btn-default.dropdown-toggle{ data: {toggle: :dropdown} }
%a.btn.btn-default.dropdown-toggle{ data: { toggle: :dropdown, display: 'static' } }
- if @start_version
version #{version_index(@start_version)}
- else
......
- @content_class = "limit-container-width limit-container-width-sm" unless fluid_layout
- @content_class = "limit-container-width" unless fluid_layout
- page_title _("Edit"), @page.title.capitalize, _("Wiki")
= wiki_page_errors(@error)
......
- @content_class = "limit-container-width limit-container-width-sm" unless fluid_layout
- @content_class = "limit-container-width" unless fluid_layout
- page_title s_("WikiClone|Git Access"), _("Wiki")
.wiki-page-header.has-sidebar-toggle
......
- @content_class = "limit-container-width limit-container-width-sm" unless fluid_layout
- @content_class = "limit-container-width" unless fluid_layout
- breadcrumb_title @page.title.capitalize
- wiki_breadcrumb_dropdown_links(@page.slug)
- page_title @page.title.capitalize, _("Wiki")
......
......@@ -11,7 +11,7 @@
%span
= default_clone_protocol.upcase
= icon('caret-down')
%ul.dropdown-menu.dropdown-menu-selectable.dropdown-menu-right.clone-options-dropdown
%ul.dropdown-menu.dropdown-menu-selectable.clone-options-dropdown
%li
= ssh_clone_button(project)
%li
......
......@@ -9,7 +9,7 @@
None
%a{ href: "#",
"v-for" => "label in issue.labels" }
%span.badge.color-label.has-tooltip{ ":style" => "{ backgroundColor: label.color, color: label.textColor }" }
.badge.color-label.has-tooltip{ ":style" => "{ backgroundColor: label.color, color: label.textColor }" }
{{ label.title }}
- if can_admin_issue?
.selectbox
......
- project = @target_project || @project
- extra_class = extra_class || ''
- show_menu_above = show_menu_above || false
- selected = local_assigns.fetch(:selected, nil)
- selected_text = selected.try(:title) || params[:milestone_title]
- dropdown_title = local_assigns.fetch(:dropdown_title, "Filter by milestone")
- if selected.present? || params[:milestone_title].present?
......
......@@ -50,7 +50,7 @@
- data[:multi_select] = true
- data['dropdown-title'] = title
- data['dropdown-header'] = dropdown_options[:data][:'dropdown-header']
- data['max-select'] = dropdown_options[:data][:'max-select']
- data['max-select'] = dropdown_options[:data][:'max-select'] if dropdown_options[:data][:'max-select']
- options[:data].merge!(data)
= dropdown_tag(title, options: options)
......@@ -25,7 +25,10 @@
= form.hidden_field :label_ids, multiple: true, value: ''
.col-sm-10{ class: "#{"col-md-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" }
.issuable-form-select-holder
= render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false}, dropdown_title: "Select label"
= render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false }, dropdown_title: "Select label"
= render_if_exists "shared/issuable/form/weight", issuable: issuable, form: form
- if has_due_date
.col-lg-6
.form-group.row
......
......@@ -6,14 +6,14 @@
.js-notification-toggle-btns
%div{ class: ("btn-group" if notification_setting.custom?) }
- if notification_setting.custom?
%button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: "Notification setting", "aria-label" => "Notification setting: #{notification_title(notification_setting.level)}", data: { container: "body", toggle: "modal", target: "#" + notifications_menu_identifier("modal", notification_setting) } }
%button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: "Notification setting", "aria-label" => "Notification setting: #{notification_title(notification_setting.level)}", data: { container: "body", toggle: "modal", target: "#" + notifications_menu_identifier("modal", notification_setting), display: 'static' } }
= icon("bell", class: "js-notification-loading")
= notification_title(notification_setting.level)
%button.btn.dropdown-toggle{ data: { toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting) } }
= icon('caret-down')
.sr-only Toggle dropdown
- else
%button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: "Notification setting", "aria-label" => "Notification setting: #{notification_title(notification_setting.level)}", data: { container: "body", toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting) } }
%button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: "Notification setting", "aria-label" => "Notification setting: #{notification_title(notification_setting.level)}", data: { container: "body", toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), display: 'static' } }
= icon("bell", class: "js-notification-loading")
= notification_title(notification_setting.level)
= icon("caret-down")
......
---
title: Add machine type and pricing documentation links, add class to labels to make
bold
merge_request:
author:
type: changed
---
title: Allows you to create another deploy token dimmediately after creating one
merge_request: 19639
author:
type: changed
---
title: Render calendar feed inline when accessed from GitLab
merge_request:
author:
type: fixed
---
title: "[Rails5] Fix sessions_controller_spec"
merge_request: 19936
author: "@blackst0ne"
type: fixed
---
title: "[Rails5] Set request.format for artifacts_controller"
merge_request: 19937
author: "@blackst0ne"
type: fixed
---
title: "[Rails5] Explicitly set request.format for blob_controller"
merge_request: 19876
author: "@blackst0ne"
type: fixed
---
title: "[Rails5] Fix optimistic lock value"
merge_request: 19878
author: "@blackst0ne"
type: fixed
---
title: "[Rails5] Fix pipeline_schedules_controller_spec"
merge_request: 19919
author: "@blackst0ne"
type: fixed
---
title: "[Rails5] Invalid single-table inheritance type: Group is not a subclass of
Namespace"
merge_request: 19918
author: "@blackst0ne"
type: fixed
---
title: Disabled Web IDE autocomplete suggestions for Markdown files.
merge_request:
author: Isaac Smith
type: fixed
---
title: Improve Web IDE commit flow
merge_request:
author:
type: changed
---
title: Optimise paused runners to reduce amount of used requests
merge_request:
author:
type: performance
---
title: Rails5 fix update_attribute usage not causing a save
merge_request: 19881
author: Jasper Maes
type: fixed
---
title: Rails5 update Gemfile.rails5.lock
merge_request: 19921
author: Jasper Maes
type: fixed
---
title: Rails5 fix passing Group objects array into for_projects_and_groups milestone
scope
merge_request: 19920
author: Jasper Maes
type: fixed
---
title: Rails5 fix connection execute return integer instead of string
merge_request: 19901
author: Jasper Maes
type: fixed
---
title: Remove the ci_job_request_with_tags_matcher
merge_request:
author:
type: performance
---
title: Remove small container width
merge_request: 19893
author: George Tsiolis
type: changed
# rubocop:disable Lint/RescueException
# Remove this entire initializer when we are at rails 5.0.
# This file fixes the bug (see below) which has been fixed in the upstream.
unless Gitlab.rails5?
# This patch fixes https://github.com/rails/rails/issues/26024
# TODO: Remove it when it's no longer necessary
module ActiveRecord
module Locking
module Optimistic
# We overwrite this method because we don't want to have default value
# for newly created records
def _create_record(attribute_names = self.attribute_names, *) # :nodoc:
super
end
# Remove this monkey-patch when all lock_version values are converted from NULLs to zeros.
# See https://gitlab.com/gitlab-org/gitlab-ce/issues/25228
module ActiveRecord
module Locking
module Optimistic
# We overwrite this method because we don't want to have default value
# for newly created records
def _create_record(attribute_names = self.attribute_names, *) # :nodoc:
super
end
def _update_record(attribute_names = self.attribute_names) #:nodoc:
return super unless locking_enabled?
return 0 if attribute_names.empty?
def _update_record(attribute_names = self.attribute_names) #:nodoc:
return super unless locking_enabled?
return 0 if attribute_names.empty?
lock_col = self.class.locking_column
lock_col = self.class.locking_column
previous_lock_value = send(lock_col).to_i # rubocop:disable GitlabSecurity/PublicSend
previous_lock_value = send(lock_col).to_i # rubocop:disable GitlabSecurity/PublicSend
# This line is added as a patch
previous_lock_value = nil if previous_lock_value == '0' || previous_lock_value == 0
# This line is added as a patch
previous_lock_value = nil if previous_lock_value == '0' || previous_lock_value == 0
increment_lock
increment_lock
attribute_names += [lock_col]
attribute_names.uniq!
attribute_names += [lock_col]
attribute_names.uniq!
begin
relation = self.class.unscoped
begin
relation = self.class.unscoped
affected_rows = relation.where(
self.class.primary_key => id,
lock_col => previous_lock_value
).update_all(
attributes_for_update(attribute_names).map do |name|
[name, _read_attribute(name)]
end.to_h
)
affected_rows = relation.where(
self.class.primary_key => id,
lock_col => previous_lock_value
).update_all(
attributes_for_update(attribute_names).map do |name|
[name, _read_attribute(name)]
end.to_h
)
unless affected_rows == 1
raise ActiveRecord::StaleObjectError.new(self, "update")
end
unless affected_rows == 1
raise ActiveRecord::StaleObjectError.new(self, "update")
end
affected_rows
affected_rows
# If something went wrong, revert the version.
rescue Exception
send(lock_col + '=', previous_lock_value) # rubocop:disable GitlabSecurity/PublicSend
raise
end
# If something went wrong, revert the version.
rescue Exception
send(lock_col + '=', previous_lock_value) # rubocop:disable GitlabSecurity/PublicSend
raise
end
end
# This is patched because we need it to query `lock_version IS NULL`
# rather than `lock_version = 0` whenever lock_version is NULL.
def relation_for_destroy
return super unless locking_enabled?
# This is patched because we need it to query `lock_version IS NULL`
# rather than `lock_version = 0` whenever lock_version is NULL.
def relation_for_destroy
return super unless locking_enabled?
column_name = self.class.locking_column
super.where(self.class.arel_table[column_name].eq(self[column_name]))
end
column_name = self.class.locking_column
super.where(self.class.arel_table[column_name].eq(self[column_name]))
end
end
# This is patched because we want `lock_version` default to `NULL`
# rather than `0`
if Gitlab.rails5?
class LockingType
def deserialize(value)
super
end
# This is patched because we want `lock_version` default to `NULL`
# rather than `0`
def serialize(value)
super
end
end
else
class LockingType < SimpleDelegator
def type_cast_from_database(value)
super
......
......@@ -11,6 +11,12 @@ Rails.application.routes.draw do
post :toggle_award_emoji, on: :member
end
favicon_redirect = redirect do |_params, _request|
ActionController::Base.helpers.asset_url(Gitlab::Favicon.main)
end
get 'favicon.png', to: favicon_redirect
get 'favicon.ico', to: favicon_redirect
draw :sherlock
draw :development
draw :ci
......
......@@ -1406,6 +1406,43 @@ variables:
You can set it globally or per-job in the [`variables`](#variables) section.
### Custom build directories
> [Introduced][gitlab-runner-876] in Gitlab Runner 11.1
NOTE: **Note:**
This can only be used when `custom_build_dir` is set to true in the [Runner's
configuration](https://docs.gitlab.com/runner/configuration/advanced-configuration.html).
By default, GitLab Runner clones the repository in the `/builds` directory,
but sometimes your project might require to have the code in a specific
directory, like the GO projects for example. In that case, you can specify
the `CI_PROJECT_DIR` variable to tell the Runner in which directory to clone
the repository:
```yml
image: golang:1.10-alpine3.7
variables:
CI_PROJECT_DIR: /go/src/gitlab.com/namespace/project-name
stages:
- test
dir:
stage: test
script:
- pwd # /go/src/gitlab.com/namespace/project-name
```
The following executors may use this feature only when
[concurrent](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-global-section)
is set to `1`:
- `shell`
- `ssh`
- `docker`, `docker+machine` when the job's working directory is mounted as a host volume.
## Special YAML features
It's possible to use special YAML features like anchors (`&`), aliases (`*`)
......@@ -1599,5 +1636,6 @@ CI with various languages.
[ce-7983]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7983
[ce-7447]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7447
[ce-12909]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12909
[gitlab-runner-876]: https://gitlab.com/gitlab-org/gitlab-runner/merge_requests/876
[schedules]: ../../user/project/pipelines/schedules.md
[variables-expressions]: ../variables/README.md#variables-expressions
......@@ -43,3 +43,17 @@ yarn prettier-all-save
Formats all files in the repository with Prettier. (This should only be used to test global rule updates otherwise you would end up with huge MR's).
The source of these Yarn scripts can be found in `/scripts/frontend/prettier.js`.
### Scripts during Conversion period
```
node ./scripts/frontend/prettier.js check ./vendor/
```
This will go over all files in a specific folder check it.
```
node ./scripts/frontend/prettier.js save ./vendor/
```
This will go over all files in a specific folder and save it.
......@@ -35,7 +35,12 @@ In Google's side:
1. You should now be able to see a Client ID and Client secret. Note them down
or keep this page open as you will need them later.
1. From the **Dashboard** select **ENABLE APIS AND SERVICES > Compute > Google Kubernetes Engine API > Enable**
1. From the **Dashboard** select **ENABLE APIS AND SERVICES > Compute > Google+ API > Enable**
1. To enable projects to access [Google Kubernetes Engine](../user/project/clusters/index.md), you must also
enable these APIs:
- Google Kubernetes Engine API
- Cloud Resource Manager API
- Cloud Billing API
On your GitLab server:
......
......@@ -17,7 +17,7 @@ We've gathered some resources to help you to get the best from Git with GitLab.
- [How to install Git](how_to_install_git/index.md)
- [Start using Git on the command line](../../gitlab-basics/start-using-git.md)
- [Command Line basic commands](../../gitlab-basics/command-line-commands.md)
- [GitLab Git Cheat Sheet (download)](https://gitlab.com/gitlab-com/marketing/raw/master/design/print/git-cheatsheet/print-pdf/git-cheatsheet.pdf)
- [GitLab Git Cheat Sheet (download)](https://about.gitlab.com/images/press/git-cheat-sheet.pdf)
- Commits
- [Revert a commit](../../user/project/merge_requests/revert_changes.md#reverting-a-commit)
- [Cherry-picking a commit](../../user/project/merge_requests/cherry_pick_changes.md#cherry-picking-a-commit)
......
......@@ -2,6 +2,10 @@
comments: false
---
DANGER: This guide exists for reference of how an AWS deployment could work.
We are currently seeing very slow EFS access performance which causes GitLab to
be 5-10x slower than using NFS or Local disk. We _do not_ recommend follow this
guide at this time.
# High Availability on AWS
......
......@@ -58,6 +58,7 @@ Currently the following names are reserved as top level groups:
- dashboard
- deploy.html
- explore
- favicon.ico
- favicon.png
- groups
- header_logo_dark.png
......
......@@ -84,7 +84,11 @@ module API
end
post '/request' do
authenticate_runner!
no_content! unless current_runner.active?
unless current_runner.active?
header 'X-GitLab-Last-Update', current_runner.ensure_runner_queue_value
break no_content!
end
if current_runner.runner_queue_value_latest?(params[:last_update])
header 'X-GitLab-Last-Update', params[:last_update]
......
......@@ -37,6 +37,7 @@ module Gitlab
class Namespace < ActiveRecord::Base
include MigrationClasses::Routable
self.table_name = 'namespaces'
self.inheritance_column = :_type_disabled
belongs_to :parent,
class_name: "#{MigrationClasses.name}::Namespace"
has_one :route, as: :source
......
......@@ -2,7 +2,7 @@ module Gitlab
class Favicon
class << self
def main
return appearance_favicon.favicon_main.url if appearance_favicon.exists?
return appearance_favicon.url if appearance_favicon.exists?
image_name =
if Gitlab::Utils.to_boolean(ENV['CANARY'])
......
......@@ -27,63 +27,38 @@ module Gitlab
end
def write_page(name, format, content, commit_details)
@repository.gitaly_migrate(:wiki_write_page) do |is_enabled|
if is_enabled
gitaly_write_page(name, format, content, commit_details)
else
gollum_write_page(name, format, content, commit_details)
end
@repository.wrapped_gitaly_errors do
gitaly_write_page(name, format, content, commit_details)
end
end
def delete_page(page_path, commit_details)
@repository.gitaly_migrate(:wiki_delete_page) do |is_enabled|
if is_enabled
gitaly_delete_page(page_path, commit_details)
else
gollum_delete_page(page_path, commit_details)
end
@repository.wrapped_gitaly_errors do
gitaly_delete_page(page_path, commit_details)
end
end
def update_page(page_path, title, format, content, commit_details)
@repository.gitaly_migrate(:wiki_update_page) do |is_enabled|
if is_enabled
gitaly_update_page(page_path, title, format, content, commit_details)
else
gollum_update_page(page_path, title, format, content, commit_details)
end
@repository.wrapped_gitaly_errors do
gitaly_update_page(page_path, title, format, content, commit_details)
end
end
def pages(limit: nil)
@repository.gitaly_migrate(:wiki_get_all_pages) do |is_enabled|
if is_enabled
gitaly_get_all_pages
else
gollum_get_all_pages(limit: limit)
end
@repository.wrapped_gitaly_errors do
gitaly_get_all_pages
end
end
def page(title:, version: nil, dir: nil)
@repository.gitaly_migrate(:wiki_find_page,
status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
gitaly_find_page(title: title, version: version, dir: dir)
else
gollum_find_page(title: title, version: version, dir: dir)
end
@repository.wrapped_gitaly_errors do
gitaly_find_page(title: title, version: version, dir: dir)
end
end
def file(name, version)
@repository.gitaly_migrate(:wiki_find_file) do |is_enabled|
if is_enabled
gitaly_find_file(name, version)
else
gollum_find_file(name, version)
end
@repository.wrapped_gitaly_errors do
gitaly_find_file(name, version)
end
end
......@@ -92,24 +67,15 @@ module Gitlab
# :per_page - The number of items per page.
# :limit - Total number of items to return.
def page_versions(page_path, options = {})
@repository.gitaly_migrate(:wiki_page_versions) do |is_enabled|
if is_enabled
versions = gitaly_wiki_client.page_versions(page_path, options)
# Gitaly uses gollum-lib to get the versions. Gollum defaults to 20
# per page, but also fetches 20 if `limit` or `per_page` < 20.
# Slicing returns an array with the expected number of items.
slice_bound = options[:limit] || options[:per_page] || Gollum::Page.per_page
versions[0..slice_bound]
else
current_page = gollum_page_by_path(page_path)
commits_from_page(current_page, options).map do |gitlab_git_commit|
gollum_page = gollum_wiki.page(current_page.title, gitlab_git_commit.id)
Gitlab::Git::WikiPageVersion.new(gitlab_git_commit, gollum_page&.format)
end
end
versions = @repository.wrapped_gitaly_errors do
gitaly_wiki_client.page_versions(page_path, options)
end
# Gitaly uses gollum-lib to get the versions. Gollum defaults to 20
# per page, but also fetches 20 if `limit` or `per_page` < 20.
# Slicing returns an array with the expected number of items.
slice_bound = options[:limit] || options[:per_page] || Gollum::Page.per_page
versions[0..slice_bound]
end
def count_page_versions(page_path)
......@@ -131,46 +97,13 @@ module Gitlab
def page_formatted_data(title:, dir: nil, version: nil)
version = version&.id
@repository.gitaly_migrate(:wiki_page_formatted_data, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
gitaly_wiki_client.get_formatted_data(title: title, dir: dir, version: version)
else
# We don't use #page because if wiki_find_page feature is enabled, we would
# get a page without formatted_data.
gollum_find_page(title: title, dir: dir, version: version)&.formatted_data
end
@repository.wrapped_gitaly_errors do
gitaly_wiki_client.get_formatted_data(title: title, dir: dir, version: version)
end
end
def gollum_wiki
@gollum_wiki ||= Gollum::Wiki.new(@repository.path)
end
private
# options:
# :page - The Integer page number.
# :per_page - The number of items per page.
# :limit - Total number of items to return.
def commits_from_page(gollum_page, options = {})
unless options[:limit]
options[:offset] = ([1, options.delete(:page).to_i].max - 1) * Gollum::Page.per_page
options[:limit] = (options.delete(:per_page) || Gollum::Page.per_page).to_i
end
@repository.log(ref: gollum_page.last_version.id,
path: gollum_page.path,
limit: options[:limit],
offset: options[:offset])
end
def gollum_page_by_path(page_path)
page_name = Gollum::Page.canonicalize_filename(page_path)
page_dir = File.split(page_path).first
gollum_wiki.paged(page_name, page_dir)
end
def new_page(gollum_page)
Gitlab::Git::WikiPage.new(gollum_page, new_version(gollum_page, gollum_page.version.id))
end
......@@ -199,65 +132,6 @@ module Gitlab
@gitaly_wiki_client ||= Gitlab::GitalyClient::WikiService.new(@repository)
end
def gollum_write_page(name, format, content, commit_details)
assert_type!(format, Symbol)
assert_type!(commit_details, CommitDetails)
with_committer_with_hooks(commit_details) do |committer|
filename = File.basename(name)
dir = (tmp_dir = File.dirname(name)) == '.' ? '' : tmp_dir
gollum_wiki.write_page(filename, format, content, { committer: committer }, dir)
end
rescue Gollum::DuplicatePageError => e
raise Gitlab::Git::Wiki::DuplicatePageError, e.message
end
def gollum_delete_page(page_path, commit_details)
assert_type!(commit_details, CommitDetails)
with_committer_with_hooks(commit_details) do |committer|
gollum_wiki.delete_page(gollum_page_by_path(page_path), committer: committer)
end
end
def gollum_update_page(page_path, title, format, content, commit_details)
assert_type!(format, Symbol)
assert_type!(commit_details, CommitDetails)
with_committer_with_hooks(commit_details) do |committer|
page = gollum_page_by_path(page_path)
# Instead of performing two renames if the title has changed,
# the update_page will only update the format and content and
# the rename_page will do anything related to moving/renaming
gollum_wiki.update_page(page, page.name, format, content, committer: committer)
gollum_wiki.rename_page(page, title, committer: committer)
end
end
def gollum_find_page(title:, version: nil, dir: nil)
if version
version = Gitlab::Git::Commit.find(@repository, version).id
end
gollum_page = gollum_wiki.page(title, version, dir)
return unless gollum_page
new_page(gollum_page)
end
def gollum_find_file(name, version)
version ||= self.class.default_ref
gollum_file = gollum_wiki.file(name, version)
return unless gollum_file
Gitlab::Git::WikiFile.new(gollum_file)
end
def gollum_get_all_pages(limit: nil)
gollum_wiki.pages(limit: limit).map { |gollum_page| new_page(gollum_page) }
end
def gitaly_write_page(name, format, content, commit_details)
gitaly_wiki_client.write_page(name, format, content, commit_details)
end
......
......@@ -17,7 +17,7 @@ module Gitlab
def check
catch_timeout 10.seconds do
if Gitlab::Database.postgresql?
ActiveRecord::Base.connection.execute('SELECT 1 as ping')&.first&.[]('ping')
ActiveRecord::Base.connection.execute('SELECT 1 as ping')&.first&.[]('ping')&.to_s
else
ActiveRecord::Base.connection.execute('SELECT 1 as ping')&.first&.first&.to_s
end
......
......@@ -30,6 +30,7 @@ module Gitlab
dashboard
deploy.html
explore
favicon.ico
favicon.png
files
groups
......
......@@ -11,6 +11,7 @@ module Gitlab
lib/gitlab/etag_caching/
lib/gitlab/metrics/
lib/gitlab/middleware/
ee/lib/gitlab/middleware/
lib/gitlab/performance_bar/
lib/gitlab/request_profiler/
lib/gitlab/profiler.rb
......@@ -98,11 +99,7 @@ module Gitlab
super
backtrace = Rails.backtrace_cleaner.clean(caller)
backtrace.each do |caller_line|
next if caller_line.match(Regexp.union(IGNORE_BACKTRACES))
Gitlab::Profiler.clean_backtrace(caller).each do |caller_line|
stripped_caller_line = caller_line.sub("#{Rails.root}/", '')
super(" ↳ #{stripped_caller_line}")
......@@ -112,6 +109,12 @@ module Gitlab
end
end
def self.clean_backtrace(backtrace)
Array(Rails.backtrace_cleaner.clean(backtrace)).reject do |line|
line.match(Regexp.union(IGNORE_BACKTRACES))
end
end
def self.with_custom_logger(logger)
original_colorize_logging = ActiveSupport::LogSubscriber.colorize_logging
original_activerecord_logger = ActiveRecord::Base.logger
......
......@@ -9,6 +9,8 @@ const getStagedFiles = require('./frontend_script_utils').getStagedFiles;
const mode = process.argv[2] || 'check';
const shouldSave = mode === 'save' || mode === 'save-all';
const allFiles = mode === 'check-all' || mode === 'save-all';
let dirPath = process.argv[3] || '';
if (dirPath && dirPath.charAt(dirPath.length - 1) !== '/') dirPath += '/';
const config = {
patterns: ['**/*.js', '**/*.vue', '**/*.scss'],
......@@ -39,9 +41,10 @@ prettierIgnore.add(
const availableExtensions = Object.keys(config.parsers);
console.log(`Loading ${allFiles ? 'All' : 'Staged'} Files ...`);
console.log(`Loading ${allFiles ? 'All' : 'Selected'} Files ...`);
const stagedFiles = allFiles ? null : getStagedFiles(availableExtensions.map(ext => `*.${ext}`));
const stagedFiles =
allFiles || dirPath ? null : getStagedFiles(availableExtensions.map(ext => `*.${ext}`));
if (stagedFiles) {
if (!stagedFiles.length || (stagedFiles.length === 1 && !stagedFiles[0])) {
......@@ -60,6 +63,13 @@ if (allFiles) {
const patterns = config.patterns;
const globPattern = patterns.length > 1 ? `{${patterns.join(',')}}` : `${patterns.join(',')}`;
files = glob.sync(globPattern, { ignore }).filter(f => allFiles || stagedFiles.includes(f));
} else if (dirPath) {
const ignore = config.ignore;
const patterns = config.patterns.map(item => {
return dirPath + item;
});
const globPattern = patterns.length > 1 ? `{${patterns.join(',')}}` : `${patterns.join(',')}`;
files = glob.sync(globPattern, { ignore });
} else {
files = stagedFiles.filter(f => availableExtensions.includes(f.split('.').pop()));
}
......@@ -73,12 +83,11 @@ if (!files.length) {
console.log(`${shouldSave ? 'Updating' : 'Checking'} ${files.length} file(s)`);
prettier
.resolveConfig('.')
.then(options => {
console.log('Found options : ', options);
files.forEach(file => {
try {
files.forEach(file => {
try {
prettier
.resolveConfig(file)
.then(options => {
const fileExtension = file.split('.').pop();
Object.assign(options, {
parser: config.parsers[fileExtension],
......@@ -101,17 +110,17 @@ prettier
}
console.log(`Prettify Manually : ${file}`);
}
} catch (error) {
didError = true;
console.log(`\n\nError with ${file}: ${error.message}`);
}
});
if (didWarn || didError) {
process.exit(1);
}
})
.catch(e => {
console.log(`Error on loading the Config File: ${e.message}`);
process.exit(1);
});
})
.catch(e => {
console.log(`Error on loading the Config File: ${e.message}`);
process.exit(1);
});
} catch (error) {
didError = true;
console.log(`\n\nError with ${file}: ${error.message}`);
}
});
if (didWarn || didError) {
process.exit(1);
}
......@@ -29,7 +29,7 @@ describe Projects::ImportsController do
context 'when import is in progress' do
before do
project.update_attribute(:import_status, :started)
project.update_attributes(import_status: :started)
end
it 'renders template' do
......@@ -47,7 +47,7 @@ describe Projects::ImportsController do
context 'when import failed' do
before do
project.update_attribute(:import_status, :failed)
project.update_attributes(import_status: :failed)
end
it 'redirects to new_namespace_project_import_path' do
......@@ -59,7 +59,7 @@ describe Projects::ImportsController do
context 'when import finished' do
before do
project.update_attribute(:import_status, :finished)
project.update_attributes(import_status: :finished)
end
context 'when project is a fork' do
......@@ -108,7 +108,7 @@ describe Projects::ImportsController do
context 'when import never happened' do
before do
project.update_attribute(:import_status, :none)
project.update_attributes(import_status: :none)
end
it 'redirects to namespace_project_path' do
......
......@@ -234,7 +234,7 @@ describe Projects::MergeRequestsController do
body = JSON.parse(response.body)
expect(body['assignee'].keys)
.to match_array(%w(name username avatar_url))
.to match_array(%w(name username avatar_url id state web_url))
end
end
......
......@@ -310,9 +310,19 @@ describe Projects::PipelineSchedulesController do
end
def go
put :update, namespace_id: project.namespace.to_param,
project_id: project, id: pipeline_schedule,
schedule: schedule
if Gitlab.rails5?
put :update, params: { namespace_id: project.namespace.to_param,
project_id: project,
id: pipeline_schedule,
schedule: schedule },
as: :html
else
put :update, namespace_id: project.namespace.to_param,
project_id: project,
id: pipeline_schedule,
schedule: schedule
end
end
end
......
......@@ -257,15 +257,15 @@ describe SessionsController do
end
end
describe '#new' do
describe "#new" do
before do
set_devise_mapping(context: @request)
end
it 'redirects correctly for referer on same host with params' do
search_path = '/search?search=seed_project'
allow(controller.request).to receive(:referer)
.and_return('http://%{host}%{path}' % { host: 'test.host', path: search_path })
it "redirects correctly for referer on same host with params" do
host = "test.host"
search_path = "/search?search=seed_project"
request.headers[:HTTP_REFERER] = "http://#{host}#{search_path}"
get(:new, redirect_to_referer: :yes)
......
......@@ -580,23 +580,6 @@ describe UploadsController do
expect(response).to have_gitlab_http_status(404)
end
end
context 'has a valid filename on the version file' do
it 'successfully returns the file' do
get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'favicon_main_dk.png'
expect(response).to have_gitlab_http_status(200)
expect(response.header['Content-Disposition']).to end_with 'filename="favicon_main_dk.png"'
end
end
context 'has an invalid filename on the version file' do
it 'returns a 404' do
get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'favicon_bogusversion_dk.png'
expect(response).to have_gitlab_http_status(404)
end
end
end
end
end
......@@ -11,13 +11,25 @@ describe 'Dashboard Issues Calendar Feed' do
end
context 'when authenticated' do
it 'renders calendar feed' do
sign_in user
visit issues_dashboard_path(:ics)
context 'with no referer' do
it 'renders calendar feed' do
sign_in user
visit issues_dashboard_path(:ics)
expect(response_headers['Content-Type']).to have_content('text/calendar')
expect(response_headers['Content-Disposition']).to have_content('inline')
expect(body).to have_text('BEGIN:VCALENDAR')
expect(response_headers['Content-Type']).to have_content('text/calendar')
expect(body).to have_text('BEGIN:VCALENDAR')
end
end
context 'with GitLab as the referer' do
it 'renders calendar feed as text/plain' do
sign_in user
page.driver.header('Referer', issues_dashboard_url(host: Settings.gitlab.base_url))
visit issues_dashboard_path(:ics)
expect(response_headers['Content-Type']).to have_content('text/plain')
expect(body).to have_text('BEGIN:VCALENDAR')
end
end
end
......@@ -28,7 +40,6 @@ describe 'Dashboard Issues Calendar Feed' do
visit issues_dashboard_path(:ics, private_token: personal_access_token.token)
expect(response_headers['Content-Type']).to have_content('text/calendar')
expect(response_headers['Content-Disposition']).to have_content('inline')
expect(body).to have_text('BEGIN:VCALENDAR')
end
end
......@@ -38,7 +49,6 @@ describe 'Dashboard Issues Calendar Feed' do
visit issues_dashboard_path(:ics, feed_token: user.feed_token)
expect(response_headers['Content-Type']).to have_content('text/calendar')
expect(response_headers['Content-Disposition']).to have_content('inline')
expect(body).to have_text('BEGIN:VCALENDAR')
end
end
......
......@@ -13,13 +13,25 @@ describe 'Group Issues Calendar Feed' do
end
context 'when authenticated' do
it 'renders calendar feed' do
sign_in user
visit issues_group_path(group, :ics)
context 'with no referer' do
it 'renders calendar feed' do
sign_in user
visit issues_group_path(group, :ics)
expect(response_headers['Content-Type']).to have_content('text/calendar')
expect(response_headers['Content-Disposition']).to have_content('inline')
expect(body).to have_text('BEGIN:VCALENDAR')
expect(response_headers['Content-Type']).to have_content('text/calendar')
expect(body).to have_text('BEGIN:VCALENDAR')
end
end
context 'with GitLab as the referer' do
it 'renders calendar feed as text/plain' do
sign_in user
page.driver.header('Referer', issues_group_url(group, host: Settings.gitlab.base_url))
visit issues_group_path(group, :ics)
expect(response_headers['Content-Type']).to have_content('text/plain')
expect(body).to have_text('BEGIN:VCALENDAR')
end
end
end
......@@ -30,7 +42,6 @@ describe 'Group Issues Calendar Feed' do
visit issues_group_path(group, :ics, private_token: personal_access_token.token)
expect(response_headers['Content-Type']).to have_content('text/calendar')
expect(response_headers['Content-Disposition']).to have_content('inline')
expect(body).to have_text('BEGIN:VCALENDAR')
end
end
......@@ -40,7 +51,6 @@ describe 'Group Issues Calendar Feed' do
visit issues_group_path(group, :ics, feed_token: user.feed_token)
expect(response_headers['Content-Type']).to have_content('text/calendar')
expect(response_headers['Content-Disposition']).to have_content('inline')
expect(body).to have_text('BEGIN:VCALENDAR')
end
end
......
......@@ -12,13 +12,25 @@ describe 'Project Issues Calendar Feed' do
end
context 'when authenticated' do
it 'renders calendar feed' do
sign_in user
visit project_issues_path(project, :ics)
context 'with no referer' do
it 'renders calendar feed' do
sign_in user
visit project_issues_path(project, :ics)
expect(response_headers['Content-Type']).to have_content('text/calendar')
expect(response_headers['Content-Disposition']).to have_content('inline')
expect(body).to have_text('BEGIN:VCALENDAR')
expect(response_headers['Content-Type']).to have_content('text/calendar')
expect(body).to have_text('BEGIN:VCALENDAR')
end
end
context 'with GitLab as the referer' do
it 'renders calendar feed as text/plain' do
sign_in user
page.driver.header('Referer', project_issues_url(project, host: Settings.gitlab.base_url))
visit project_issues_path(project, :ics)
expect(response_headers['Content-Type']).to have_content('text/plain')
expect(body).to have_text('BEGIN:VCALENDAR')
end
end
end
......@@ -29,7 +41,6 @@ describe 'Project Issues Calendar Feed' do
visit project_issues_path(project, :ics, private_token: personal_access_token.token)
expect(response_headers['Content-Type']).to have_content('text/calendar')
expect(response_headers['Content-Disposition']).to have_content('inline')
expect(body).to have_text('BEGIN:VCALENDAR')
end
end
......@@ -39,7 +50,6 @@ describe 'Project Issues Calendar Feed' do
visit project_issues_path(project, :ics, feed_token: user.feed_token)
expect(response_headers['Content-Type']).to have_content('text/calendar')
expect(response_headers['Content-Disposition']).to have_content('inline')
expect(body).to have_text('BEGIN:VCALENDAR')
end
end
......
......@@ -226,7 +226,9 @@ feature 'Issues > User uses quick actions', :js do
it 'does not move the issue' do
add_note("/move #{project_unauthorized.full_path}")
expect(page).not_to have_content 'Commands applied'
wait_for_requests
expect(page).to have_content 'Commands applied'
expect(issue.reload).to be_open
end
end
......
......@@ -34,7 +34,7 @@ feature 'Labels Hierarchy', :js, :nested_groups do
wait_for_requests
expect(page).to have_selector('span.badge', text: label.title)
expect(page).to have_selector('.badge', text: label.title)
end
end
......@@ -45,7 +45,7 @@ feature 'Labels Hierarchy', :js, :nested_groups do
wait_for_requests
expect(page).not_to have_selector('span.badge', text: child_group_label.title)
expect(page).not_to have_selector('.badge', text: child_group_label.title)
end
end
......
......@@ -14,7 +14,21 @@
"subscribed": { "type": ["boolean", "null"] },
"participants": { "type": "array" },
"allow_collaboration": { "type": "boolean"},
"allow_maintainer_to_push": { "type": "boolean"}
"allow_maintainer_to_push": { "type": "boolean"},
"assignee": {
"oneOf": [
{ "type": "null" },
{ "$ref": "user.json" }
]
},
"milestone": {
"type": [ "object", "null" ]
},
"labels": {
"type": [ "array", "null" ]
},
"task_status": { "type": "string" },
"task_status_short": { "type": "string" }
},
"additionalProperties": false
}
......@@ -32,7 +32,7 @@ describe('Blob viewer', () => {
afterEach(() => {
mock.restore();
location.hash = '';
window.location.hash = '';
});
it('loads source file after switching views', (done) => {
......@@ -49,7 +49,7 @@ describe('Blob viewer', () => {
});
it('loads source file when line number is in hash', (done) => {
location.hash = '#L1';
window.location.hash = '#L1';
new BlobViewer();
......
......@@ -36,7 +36,7 @@ import LinkedTabs from '~/lib/utils/bootstrap_linked_tabs';
describe('on click', () => {
it('should change the url according to the clicked tab', () => {
const historySpy = spyOn(history, 'replaceState').and.callFake(() => {});
const historySpy = spyOn(window.history, 'replaceState').and.callFake(() => {});
const linkedTabs = new LinkedTabs({
action: 'show',
......
......@@ -56,7 +56,7 @@ describe('Commits List', () => {
beforeEach(() => {
commitsList.searchField.val('');
spyOn(history, 'replaceState').and.stub();
spyOn(window.history, 'replaceState').and.stub();
mock = new MockAdapter(axios);
mock.onGet('/h5bp/html5-boilerplate/commits/master').reply(200, {
......
......@@ -220,7 +220,7 @@ describe('Environment', () => {
);
component = mountComponent(EnvironmentsComponent, mockData);
spyOn(history, 'pushState').and.stub();
spyOn(window.history, 'pushState').and.stub();
});
describe('updateContent', () => {
......
......@@ -177,7 +177,7 @@ describe('Environments Folder View', () => {
});
component = mountComponent(Component, mockData);
spyOn(history, 'pushState').and.stub();
spyOn(window.history, 'pushState').and.stub();
});
describe('updateContent', () => {
......
......@@ -8,7 +8,9 @@ describe('GL Style Field Errors', function() {
beforeEach(function() {
loadFixtures('static/gl_field_errors.html.raw');
const $form = this.$form = $('form.gl-show-field-errors');
const $form = $('form.gl-show-field-errors');
this.$form = $form;
this.fieldErrors = new GlFieldErrors($form);
});
......
export default {
createNumberRandomUsers(numberUsers) {
const users = [];
for (let i = 0; i < numberUsers; i = i += 1) {
for (let i = 0; i < numberUsers; i += 1) {
users.push(
{
avatar: 'https://gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
......
......@@ -93,14 +93,14 @@ describe('Multi-file editor commit sidebar list item', () => {
describe('is active', () => {
it('does not add active class when dont keys match', () => {
expect(vm.$el.classList).not.toContain('is-active');
expect(vm.$el.querySelector('.is-active')).toBe(null);
});
it('adds active class when keys match', done => {
vm.keyPrefix = 'staged';
vm.$nextTick(() => {
expect(vm.$el.classList).toContain('is-active');
expect(vm.$el.querySelector('.is-active')).not.toBe(null);
done();
});
......
......@@ -16,6 +16,7 @@ describe('Multi-file editor commit sidebar list', () => {
iconName: 'staged',
action: 'stageAllChanges',
actionBtnText: 'stage all',
actionBtnIcon: 'history',
itemActionComponent: 'stage-button',
activeFileKey: 'staged-testing',
keyPrefix: 'staged',
......@@ -42,7 +43,7 @@ describe('Multi-file editor commit sidebar list', () => {
});
it('renders list', () => {
expect(vm.$el.querySelectorAll('li').length).toBe(1);
expect(vm.$el.querySelectorAll('.multi-file-commit-list > li').length).toBe(1);
});
});
......
......@@ -39,7 +39,7 @@ describe('IDE stage file button', () => {
});
it('calls store with discard button', () => {
vm.$el.querySelectorAll('.btn')[1].click();
vm.$el.querySelector('.dropdown-menu button').click();
expect(vm.discardFileChanges).toHaveBeenCalledWith(f.path);
});
......
......@@ -111,7 +111,7 @@ describe('RepoCommitSection', () => {
});
it('renders a commit section', () => {
const changedFileElements = [...vm.$el.querySelectorAll('.multi-file-commit-list li')];
const changedFileElements = [...vm.$el.querySelectorAll('.multi-file-commit-list > li')];
const allFiles = vm.$store.state.changedFiles.concat(vm.$store.state.stagedFiles);
expect(changedFileElements.length).toEqual(4);
......@@ -140,22 +140,26 @@ describe('RepoCommitSection', () => {
vm.$el.querySelector('.multi-file-discard-btn .btn').click();
Vue.nextTick(() => {
expect(vm.$el.querySelector('.ide-commit-list-container').querySelectorAll('li').length).toBe(
1,
);
expect(
vm.$el
.querySelector('.ide-commit-list-container')
.querySelectorAll('.multi-file-commit-list > li').length,
).toBe(1);
done();
});
});
it('discards a single file', done => {
vm.$el.querySelectorAll('.multi-file-discard-btn .btn')[1].click();
vm.$el.querySelector('.multi-file-discard-btn .dropdown-menu button').click();
Vue.nextTick(() => {
expect(vm.$el.querySelector('.ide-commit-list-container').textContent).not.toContain('file1');
expect(vm.$el.querySelector('.ide-commit-list-container').querySelectorAll('li').length).toBe(
1,
);
expect(
vm.$el
.querySelector('.ide-commit-list-container')
.querySelectorAll('.multi-file-commit-list > li').length,
).toBe(1);
done();
});
......
......@@ -263,4 +263,23 @@ describe('Multi-file editor library', () => {
expect(instance.isDiffEditorType).toBe(false);
});
});
it('sets quickSuggestions to false when language is markdown', () => {
instance.createInstance(holder);
spyOn(instance.instance, 'updateOptions').and.callThrough();
const model = instance.createModel({
...file(),
key: 'index.md',
path: 'index.md',
});
instance.attachModel(model);
expect(instance.instance.updateOptions).toHaveBeenCalledWith({
readOnly: false,
quickSuggestions: false,
});
});
});
......@@ -166,12 +166,12 @@ describe('IDE store file actions', () => {
});
it('resets location.hash for line highlighting', done => {
location.hash = 'test';
window.location.hash = 'test';
store
.dispatch('setFileActive', localFile.path)
.then(() => {
expect(location.hash).not.toBe('test');
expect(window.location.hash).not.toBe('test');
done();
})
......
......@@ -145,7 +145,7 @@ describe('Issuable output', () => {
resolve({
data: {
confidential: false,
web_url: location.pathname,
web_url: window.location.pathname,
},
});
}));
......@@ -177,7 +177,7 @@ describe('Issuable output', () => {
spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => {
resolve({
data: {
web_url: location.pathname,
web_url: window.location.pathname,
confidential: vm.isConfidential,
},
});
......
......@@ -40,13 +40,13 @@ describe('common_utils', () => {
});
it('should decode params', () => {
history.pushState('', '', '?label_name%5B%5D=test');
window.history.pushState('', '', '?label_name%5B%5D=test');
expect(
commonUtils.getUrlParamsArray()[0],
).toBe('label_name[]=test');
history.pushState('', '', '?');
window.history.pushState('', '', '?');
});
});
......
......@@ -427,7 +427,7 @@ describe('Pipelines', () => {
describe('methods', () => {
beforeEach(() => {
spyOn(history, 'pushState').and.stub();
spyOn(window.history, 'pushState').and.stub();
});
describe('updateContent', () => {
......
......@@ -90,7 +90,8 @@ describe('UpdateUsername component', () => {
it('confirmation modal should escape usernames properly', done => {
const { modalBody } = findElements();
vm.username = vm.newUsername = '<i>Italic</i>';
vm.username = '<i>Italic</i>';
vm.newUsername = vm.username;
Vue.nextTick()
.then(() => {
......
......@@ -9,11 +9,11 @@ describe('Settings Panels', () => {
describe('initSettingsPane', () => {
afterEach(() => {
location.hash = '';
window.location.hash = '';
});
it('should expand linked hash fragment panel', () => {
location.hash = '#autodevops-settings';
window.location.hash = '#autodevops-settings';
const pipelineSettingsPanel = document.querySelector('#autodevops-settings');
// Our test environment automatically expands everything so we need to clear that out first
......
......@@ -39,7 +39,8 @@ jasmine.getJSONFixtures().fixturesPath = FIXTURES_PATH;
beforeAll(() => jasmine.addMatchers(customMatchers));
// globalize common libraries
window.$ = window.jQuery = $;
window.$ = $;
window.jQuery = window.$;
// stub expected globals
window.gl = window.gl || {};
......
......@@ -19,7 +19,7 @@ RSpec.describe Gitlab::Favicon, :request_store do
it 'uses the custom favicon if a favicon appearance is present' do
create :appearance, favicon: fixture_file_upload('spec/fixtures/dk.png')
expect(described_class.main).to match %r{/uploads/-/system/appearance/favicon/\d+/favicon_main_dk.png}
expect(described_class.main).to match %r{/uploads/-/system/appearance/favicon/\d+/dk.png}
end
end
......
require 'spec_helper'
describe Gitlab::Git::CommitterWithHooks, seed_helper: true do
shared_examples 'calling wiki hooks' do
let(:project) { create(:project) }
let(:user) { project.owner }
let(:project_wiki) { ProjectWiki.new(project, user) }
let(:wiki) { project_wiki.wiki }
let(:options) do
{
id: user.id,
username: user.username,
name: user.name,
email: user.email,
message: 'commit message'
}
end
subject { described_class.new(wiki, options) }
# TODO https://gitlab.com/gitlab-org/gitaly/issues/1234
skip 'needs to be moved to gitaly-ruby test suite' do
shared_examples 'calling wiki hooks' do
let(:project) { create(:project) }
let(:user) { project.owner }
let(:project_wiki) { ProjectWiki.new(project, user) }
let(:wiki) { project_wiki.wiki }
let(:options) do
{
id: user.id,
username: user.username,
name: user.name,
email: user.email,
message: 'commit message'
}
end
before do
project_wiki.create_page('home', 'test content')
end
subject { described_class.new(wiki, options) }
shared_examples 'failing pre-receive hook' do
before do
expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('pre-receive').and_return([false, ''])
expect_any_instance_of(Gitlab::Git::HooksService).not_to receive(:run_hook).with('update')
expect_any_instance_of(Gitlab::Git::HooksService).not_to receive(:run_hook).with('post-receive')
project_wiki.create_page('home', 'test content')
end
it 'raises exception' do
expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError)
end
shared_examples 'failing pre-receive hook' do
before do
expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('pre-receive').and_return([false, ''])
expect_any_instance_of(Gitlab::Git::HooksService).not_to receive(:run_hook).with('update')
expect_any_instance_of(Gitlab::Git::HooksService).not_to receive(:run_hook).with('post-receive')
end
it 'does not create a new commit inside the repository' do
current_rev = find_current_rev
it 'raises exception' do
expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError)
end
expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError)
it 'does not create a new commit inside the repository' do
current_rev = find_current_rev
expect(current_rev).to eq find_current_rev
end
end
expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError)
shared_examples 'failing update hook' do
before do
expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('pre-receive').and_return([true, ''])
expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('update').and_return([false, ''])
expect_any_instance_of(Gitlab::Git::HooksService).not_to receive(:run_hook).with('post-receive')
expect(current_rev).to eq find_current_rev
end
end
it 'raises exception' do
expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError)
end
shared_examples 'failing update hook' do
before do
expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('pre-receive').and_return([true, ''])
expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('update').and_return([false, ''])
expect_any_instance_of(Gitlab::Git::HooksService).not_to receive(:run_hook).with('post-receive')
end
it 'does not create a new commit inside the repository' do
current_rev = find_current_rev
it 'raises exception' do
expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError)
end
expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError)
it 'does not create a new commit inside the repository' do
current_rev = find_current_rev
expect(current_rev).to eq find_current_rev
end
end
expect { subject.commit }.to raise_error(Gitlab::Git::Wiki::OperationError)
shared_examples 'failing post-receive hook' do
before do
expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('pre-receive').and_return([true, ''])
expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('update').and_return([true, ''])
expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('post-receive').and_return([false, ''])
expect(current_rev).to eq find_current_rev
end
end
it 'does not raise exception' do
expect { subject.commit }.not_to raise_error
end
shared_examples 'failing post-receive hook' do
before do
expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('pre-receive').and_return([true, ''])
expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('update').and_return([true, ''])
expect_any_instance_of(Gitlab::Git::HooksService).to receive(:run_hook).with('post-receive').and_return([false, ''])
end
it 'does not raise exception' do
expect { subject.commit }.not_to raise_error
end
it 'creates the commit' do
current_rev = find_current_rev
it 'creates the commit' do
current_rev = find_current_rev
subject.commit
subject.commit
expect(current_rev).not_to eq find_current_rev
expect(current_rev).not_to eq find_current_rev
end
end
end
shared_examples 'when hooks call succceeds' do
let(:hook) { double(:hook) }
shared_examples 'when hooks call succceeds' do
let(:hook) { double(:hook) }
it 'calls the three hooks' do
expect(Gitlab::Git::Hook).to receive(:new).exactly(3).times.and_return(hook)
expect(hook).to receive(:trigger).exactly(3).times.and_return([true, nil])
it 'calls the three hooks' do
expect(Gitlab::Git::Hook).to receive(:new).exactly(3).times.and_return(hook)
expect(hook).to receive(:trigger).exactly(3).times.and_return([true, nil])
subject.commit
end
subject.commit
end
it 'creates the commit' do
current_rev = find_current_rev
it 'creates the commit' do
current_rev = find_current_rev
subject.commit
subject.commit
expect(current_rev).not_to eq find_current_rev
expect(current_rev).not_to eq find_current_rev
end
end
end
context 'when creating a page' do
before do
project_wiki.create_page('index', 'test content')
context 'when creating a page' do
before do
project_wiki.create_page('index', 'test content')
end
it_behaves_like 'failing pre-receive hook'
it_behaves_like 'failing update hook'
it_behaves_like 'failing post-receive hook'
it_behaves_like 'when hooks call succceeds'
end
it_behaves_like 'failing pre-receive hook'
it_behaves_like 'failing update hook'
it_behaves_like 'failing post-receive hook'
it_behaves_like 'when hooks call succceeds'
end
context 'when updating a page' do
before do
project_wiki.update_page(find_page('home'), content: 'some other content', format: :markdown)
end
context 'when updating a page' do
before do
project_wiki.update_page(find_page('home'), content: 'some other content', format: :markdown)
it_behaves_like 'failing pre-receive hook'
it_behaves_like 'failing update hook'
it_behaves_like 'failing post-receive hook'
it_behaves_like 'when hooks call succceeds'
end
it_behaves_like 'failing pre-receive hook'
it_behaves_like 'failing update hook'
it_behaves_like 'failing post-receive hook'
it_behaves_like 'when hooks call succceeds'
end
context 'when deleting a page' do
before do
project_wiki.delete_page(find_page('home'))
end
context 'when deleting a page' do
before do
project_wiki.delete_page(find_page('home'))
it_behaves_like 'failing pre-receive hook'
it_behaves_like 'failing update hook'
it_behaves_like 'failing post-receive hook'
it_behaves_like 'when hooks call succceeds'
end
it_behaves_like 'failing pre-receive hook'
it_behaves_like 'failing update hook'
it_behaves_like 'failing post-receive hook'
it_behaves_like 'when hooks call succceeds'
end
def find_current_rev
wiki.gollum_wiki.repo.commits.first&.sha
end
def find_current_rev
wiki.gollum_wiki.repo.commits.first&.sha
def find_page(name)
wiki.page(title: name)
end
end
def find_page(name)
wiki.page(title: name)
context 'when Gitaly is enabled' do
it_behaves_like 'calling wiki hooks'
end
end
# TODO: Uncomment once Gitaly updates the ruby vendor code
# context 'when Gitaly is enabled' do
# it_behaves_like 'calling wiki hooks'
# end
context 'when Gitaly is disabled', :skip_gitaly_mock do
it_behaves_like 'calling wiki hooks'
context 'when Gitaly is disabled', :disable_gitaly do
it_behaves_like 'calling wiki hooks'
end
end
end
......@@ -6,9 +6,7 @@ describe Gitlab::Git::Wiki do
let(:project_wiki) { ProjectWiki.new(project, user) }
subject { project_wiki.wiki }
# Remove skip_gitaly_mock flag when gitaly_find_page when
# https://gitlab.com/gitlab-org/gitlab-ce/issues/42039 is solved
describe '#page', :skip_gitaly_mock do
describe '#page' do
before do
create_page('page1', 'content')
create_page('foo/page1', 'content foo/page1')
......@@ -25,7 +23,7 @@ describe Gitlab::Git::Wiki do
end
end
describe '#delete_page', :skip_gitaly_mock do
describe '#delete_page' do
after do
destroy_page('page1')
end
......
......@@ -135,6 +135,51 @@ describe Gitlab::Profiler do
end
end
describe '.clean_backtrace' do
it 'uses the Rails backtrace cleaner' do
backtrace = []
expect(Rails.backtrace_cleaner).to receive(:clean).with(backtrace)
described_class.clean_backtrace(backtrace)
end
it 'removes lines from IGNORE_BACKTRACES' do
backtrace = [
"lib/gitlab/gitaly_client.rb:294:in `block (2 levels) in migrate'",
"lib/gitlab/gitaly_client.rb:331:in `allow_n_plus_1_calls'",
"lib/gitlab/gitaly_client.rb:280:in `block in migrate'",
"lib/gitlab/metrics/influx_db.rb:103:in `measure'",
"lib/gitlab/gitaly_client.rb:278:in `migrate'",
"lib/gitlab/git/repository.rb:1451:in `gitaly_migrate'",
"lib/gitlab/git/commit.rb:66:in `find'",
"app/models/repository.rb:1047:in `find_commit'",
"lib/gitlab/metrics/instrumentation.rb:159:in `block in find_commit'",
"lib/gitlab/metrics/method_call.rb:36:in `measure'",
"lib/gitlab/metrics/instrumentation.rb:159:in `find_commit'",
"app/models/repository.rb:113:in `commit'",
"lib/gitlab/i18n.rb:50:in `with_locale'",
"lib/gitlab/middleware/multipart.rb:95:in `call'",
"lib/gitlab/request_profiler/middleware.rb:14:in `call'",
"ee/lib/gitlab/database/load_balancing/rack_middleware.rb:37:in `call'",
"ee/lib/gitlab/jira/middleware.rb:15:in `call'"
]
expect(described_class.clean_backtrace(backtrace))
.to eq([
"lib/gitlab/gitaly_client.rb:294:in `block (2 levels) in migrate'",
"lib/gitlab/gitaly_client.rb:331:in `allow_n_plus_1_calls'",
"lib/gitlab/gitaly_client.rb:280:in `block in migrate'",
"lib/gitlab/gitaly_client.rb:278:in `migrate'",
"lib/gitlab/git/repository.rb:1451:in `gitaly_migrate'",
"lib/gitlab/git/commit.rb:66:in `find'",
"app/models/repository.rb:1047:in `find_commit'",
"app/models/repository.rb:113:in `commit'",
"ee/lib/gitlab/jira/middleware.rb:15:in `call'"
])
end
end
describe '.with_custom_logger' do
context 'when the logger is set' do
it 'uses the replacement logger for the duration of the block' do
......
......@@ -478,7 +478,7 @@ describe JiraService do
create :appearance, favicon: fixture_file_upload('spec/fixtures/dk.png')
props = described_class.new.send(:build_remote_link_props, url: 'http://example.com', title: 'title')
expect(props[:object][:icon][:url16x16]).to match %r{^http://localhost/uploads/-/system/appearance/favicon/\d+/favicon_main_dk.png$}
expect(props[:object][:icon][:url16x16]).to match %r{^http://localhost/uploads/-/system/appearance/favicon/\d+/dk.png$}
end
end
end
......@@ -351,11 +351,13 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
context 'when valid token is provided' do
context 'when Runner is not active' do
let(:runner) { create(:ci_runner, :inactive) }
let(:update_value) { runner.ensure_runner_queue_value }
it 'returns 204 error' do
request_job
expect(response).to have_gitlab_http_status 204
expect(response).to have_gitlab_http_status(204)
expect(response.header['X-GitLab-Last-Update']).to eq(update_value)
end
end
......
require 'spec_helper'
RSpec.describe FaviconUploader do
include CarrierWave::Test::Matchers
let(:uploader) { described_class.new(build_stubbed(:user)) }
after do
uploader.remove!
end
def upload_fixture(filename)
fixture_file_upload("spec/fixtures/#{filename}")
end
context 'versions' do
before do
uploader.store!(upload_fixture('dk.png'))
end
it 'has the correct format' do
expect(uploader.favicon_main).to be_format('png')
end
it 'has the correct dimensions' do
expect(uploader.favicon_main).to have_dimensions(32, 32)
end
end
end
......@@ -13,7 +13,7 @@ describe 'Every Sidekiq worker' do
file_worker_queues = Gitlab::SidekiqConfig.worker_queues.to_set
worker_queues = Gitlab::SidekiqConfig.workers.map(&:queue).to_set
worker_queues << ActionMailer::DeliveryJob.queue_name
worker_queues << ActionMailer::DeliveryJob.new.queue_name
worker_queues << 'default'
missing_from_file = worker_queues - file_worker_queues
......
......@@ -270,11 +270,7 @@ acorn@^3.0.4:
version "3.3.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
acorn@^5.0.0, acorn@^5.3.0:
version "5.5.3"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9"
acorn@^5.5.0:
acorn@^5.0.0, acorn@^5.3.0, acorn@^5.5.0:
version "5.6.2"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.6.2.tgz#b1da1d7be2ac1b4a327fb9eab851702c5045b4e7"
......@@ -4002,14 +3998,10 @@ icss-utils@^2.1.0:
dependencies:
postcss "^6.0.1"
ieee754@^1.1.11:
ieee754@^1.1.11, ieee754@^1.1.4:
version "1.1.11"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.11.tgz#c16384ffe00f5b7835824e67b6f2bd44a5229455"
ieee754@^1.1.4:
version "1.1.8"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4"
iferr@^0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501"
......@@ -6247,11 +6239,7 @@ preserve@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
prettier@1.11.1:
version "1.11.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.11.1.tgz#61e43fc4cd44e68f2b0dfc2c38cd4bb0fccdcc75"
prettier@^1.11.1:
prettier@1.12.1, prettier@^1.11.1:
version "1.12.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.1.tgz#c1ad20e803e7749faf905a409d2367e06bbe7325"
......
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