diff options
174 files changed, 978 insertions, 762 deletions
diff --git a/.eslintrc.yml b/.eslintrc.yml index f851e3b67e6..b9c5973d7ac 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -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 diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock index 51a4d954e9c..f9d9391ef80 100644 --- a/Gemfile.rails5.lock +++ b/Gemfile.rails5.lock @@ -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) diff --git a/app/assets/javascripts/blob/viewer/index.js b/app/assets/javascripts/blob/viewer/index.js index f61c0be9230..5485248cfaf 100644 --- a/app/assets/javascripts/blob/viewer/index.js +++ b/app/assets/javascripts/blob/viewer/index.js @@ -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'; } diff --git a/app/assets/javascripts/boards/components/board.js b/app/assets/javascripts/boards/components/board.js index 86b888c66c8..7920e08e4d8 100644 --- a/app/assets/javascripts/boards/components/board.js +++ b/app/assets/javascripts/boards/components/board.js @@ -1,6 +1,5 @@ /* 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 () { diff --git a/app/assets/javascripts/boards/components/board_delete.js b/app/assets/javascripts/boards/components/board_delete.js index 4482b3b3e70..4dd9aebeed9 100644 --- a/app/assets/javascripts/boards/components/board_delete.js +++ b/app/assets/javascripts/boards/components/board_delete.js @@ -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(); } } diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js index 7dc83843e9b..ffe86468b12 100644 --- a/app/assets/javascripts/boards/stores/boards_store.js +++ b/app/assets/javascripts/boards/stores/boards_store.js @@ -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}`); } }; diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js index 7e2a3573f81..9a3ea7a55b6 100644 --- a/app/assets/javascripts/commits.js +++ b/app/assets/javascripts/commits.js @@ -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); }) diff --git a/app/assets/javascripts/deploy_keys/components/app.vue b/app/assets/javascripts/deploy_keys/components/app.vue index 2cfa13fdc75..d91e4809126 100644 --- a/app/assets/javascripts/deploy_keys/components/app.vue +++ b/app/assets/javascripts/deploy_keys/components/app.vue @@ -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) diff --git a/app/assets/javascripts/environments/components/environment_stop.vue b/app/assets/javascripts/environments/components/environment_stop.vue index faaaf899a0d..eba58bedd6d 100644 --- a/app/assets/javascripts/environments/components/environment_stop.vue +++ b/app/assets/javascripts/environments/components/environment_stop.vue @@ -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'); diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list.vue b/app/assets/javascripts/ide/components/commit_sidebar/list.vue index 3d59410cbc2..d0fb0e3d99e 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/list.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/list.vue @@ -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 diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue index 2ecf9af4bf0..5cda7967130 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue @@ -1,5 +1,6 @@ <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> diff --git a/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue b/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue index a786ec80ac2..7014b9f605e 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue @@ -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> diff --git a/app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue b/app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue index 34b366f63ac..9cec73ec00e 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue @@ -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 diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue index 01df0019fd4..c2c678ff0be 100644 --- a/app/assets/javascripts/ide/components/repo_commit_section.vue +++ b/app/assets/javascripts/ide/components/repo_commit_section.vue @@ -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 diff --git a/app/assets/javascripts/ide/lib/diff/diff_worker.js b/app/assets/javascripts/ide/lib/diff/diff_worker.js index e74c4046330..f09930e8158 100644 --- a/app/assets/javascripts/ide/lib/diff/diff_worker.js +++ b/app/assets/javascripts/ide/lib/diff/diff_worker.js @@ -1,8 +1,10 @@ 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), diff --git a/app/assets/javascripts/ide/lib/editor_options.js b/app/assets/javascripts/ide/lib/editor_options.js index 9f895d49f2e..e35595ab1fd 100644 --- a/app/assets/javascripts/ide/lib/editor_options.js +++ b/app/assets/javascripts/ide/lib/editor_options.js @@ -12,5 +12,6 @@ export const defaultEditorOptions = { export default [ { readOnly: model => !!model.file.file_lock, + quickSuggestions: model => !(model.language === 'markdown'), }, ]; diff --git a/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js b/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js index 0a1c253c637..fa35c215880 100644 --- a/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js +++ b/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js @@ -1,6 +1,7 @@ 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), diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue index e87a8ed7fea..b6364318537 100644 --- a/app/assets/javascripts/issue_show/components/app.vue +++ b/app/assets/javascripts/issue_show/components/app.vue @@ -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); } diff --git a/app/assets/javascripts/issue_show/components/edit_actions.vue b/app/assets/javascripts/issue_show/components/edit_actions.vue index 8cb0ab22bfb..597c6d69a81 100644 --- a/app/assets/javascripts/issue_show/components/edit_actions.vue +++ b/app/assets/javascripts/issue_show/components/edit_actions.vue @@ -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'); diff --git a/app/assets/javascripts/issue_show/components/locked_warning.vue b/app/assets/javascripts/issue_show/components/locked_warning.vue index 1c2789f154a..ad0d40faf32 100644 --- a/app/assets/javascripts/issue_show/components/locked_warning.vue +++ b/app/assets/javascripts/issue_show/components/locked_warning.vue @@ -2,7 +2,7 @@ export default { computed: { currentPath() { - return location.pathname; + return window.location.pathname; }, }, }; diff --git a/app/assets/javascripts/lazy_loader.js b/app/assets/javascripts/lazy_loader.js index dbbf1637a47..9482d131344 100644 --- a/app/assets/javascripts/lazy_loader.js +++ b/app/assets/javascripts/lazy_loader.js @@ -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) => { diff --git a/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js b/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js index 3873f4528ce..c28ed04f94f 100644 --- a/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js +++ b/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js @@ -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; diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index d55d0585031..d0b0e5e1ba1 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -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')); diff --git a/app/assets/javascripts/lib/utils/number_utils.js b/app/assets/javascripts/lib/utils/number_utils.js index a02c79b787e..f086d962221 100644 --- a/app/assets/javascripts/lib/utils/number_utils.js +++ b/app/assets/javascripts/lib/utils/number_utils.js @@ -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: diff --git a/app/assets/javascripts/line_highlighter.js b/app/assets/javascripts/line_highlighter.js index f2323f57455..303c5d8a894 100644 --- a/app/assets/javascripts/line_highlighter.js +++ b/app/assets/javascripts/line_highlighter.js @@ -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 diff --git a/app/assets/javascripts/milestone.js b/app/assets/javascripts/milestone.js index 325fa570f37..6da04020881 100644 --- a/app/assets/javascripts/milestone.js +++ b/app/assets/javascripts/milestone.js @@ -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'); diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js index d269c45203a..334279137d8 100644 --- a/app/assets/javascripts/milestone_select.js +++ b/app/assets/javascripts/milestone_select.js @@ -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)); diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue index 21934021852..e1c8b6a6d4a 100644 --- a/app/assets/javascripts/monitoring/components/dashboard.vue +++ b/app/assets/javascripts/monitoring/components/dashboard.vue @@ -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; diff --git a/app/assets/javascripts/monitoring/components/graph.vue b/app/assets/javascripts/monitoring/components/graph.vue index 20400154100..e5680a0499f 100644 --- a/app/assets/javascripts/monitoring/components/graph.vue +++ b/app/assets/javascripts/monitoring/components/graph.vue @@ -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); diff --git a/app/assets/javascripts/monitoring/components/graph/flag.vue b/app/assets/javascripts/monitoring/components/graph/flag.vue index 282c5c24384..92fe98508ad 100644 --- a/app/assets/javascripts/monitoring/components/graph/flag.vue +++ b/app/assets/javascripts/monitoring/components/graph/flag.vue @@ -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}`; diff --git a/app/assets/javascripts/monitoring/utils/multiple_time_series.js b/app/assets/javascripts/monitoring/utils/multiple_time_series.js index 4d3f1f1a7cc..ed3a27dd68b 100644 --- a/app/assets/javascripts/monitoring/utils/multiple_time_series.js +++ b/app/assets/javascripts/monitoring/utils/multiple_time_series.js @@ -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() diff --git a/app/assets/javascripts/network/branch_graph.js b/app/assets/javascripts/network/branch_graph.js index bd007c707f2..e4096ddb00d 100644 --- a/app/assets/javascripts/network/branch_graph.js +++ b/app/assets/javascripts/network/branch_graph.js @@ -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]]; diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js index 55f1d0b496c..27c5dedcf0b 100644 --- a/app/assets/javascripts/notes.js +++ b/app/assets/javascripts/notes.js @@ -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; } diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue index f9f5041a9f9..3f865431155 100644 --- a/app/assets/javascripts/notes/components/noteable_discussion.vue +++ b/app/assets/javascripts/notes/components/noteable_discussion.vue @@ -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; } } diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue index ec3ee407f0a..713f93456b1 100644 --- a/app/assets/javascripts/notes/components/noteable_note.vue +++ b/app/assets/javascripts/notes/components/noteable_note.vue @@ -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(); diff --git a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js index 653e2502d01..37336a8cb69 100644 --- a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js +++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js @@ -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)); diff --git a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js index 77135ad1f0e..165446a4db6 100644 --- a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js +++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js @@ -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)) { diff --git a/app/assets/javascripts/pages/users/user_tabs.js b/app/assets/javascripts/pages/users/user_tabs.js index f88a6b27c18..a2ca03536f2 100644 --- a/app/assets/javascripts/pages/users/user_tabs.js +++ b/app/assets/javascripts/pages/users/user_tabs.js @@ -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, }, diff --git a/app/assets/javascripts/pipelines/components/graph/action_component.vue b/app/assets/javascripts/pipelines/components/graph/action_component.vue index db0505a55fe..1f152ed438d 100644 --- a/app/assets/javascripts/pipelines/components/graph/action_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/action_component.vue @@ -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"/> diff --git a/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue b/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue index 04120d45834..e047d10ac93 100644 --- a/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue @@ -87,6 +87,7 @@ export default { data-toggle="dropdown" data-container="body" data-boundary="viewport" + data-display="static" class="dropdown-menu-toggle build-content" > diff --git a/app/assets/javascripts/pipelines/components/stage.vue b/app/assets/javascripts/pipelines/components/stage.vue index 4dd2fb9fbed..b9231c002fd 100644 --- a/app/assets/javascripts/pipelines/components/stage.vue +++ b/app/assets/javascripts/pipelines/components/stage.vue @@ -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" diff --git a/app/assets/javascripts/profile/gl_crop.js b/app/assets/javascripts/profile/gl_crop.js index 8f93156cdd1..ba120c4bbdf 100644 --- a/app/assets/javascripts/profile/gl_crop.js +++ b/app/assets/javascripts/profile/gl_crop.js @@ -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)); diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js index 4c4acd487f8..17497283695 100644 --- a/app/assets/javascripts/project_find_file.js +++ b/app/assets/javascripts/project_find_file.js @@ -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() { diff --git a/app/assets/javascripts/project_import.js b/app/assets/javascripts/project_import.js index d2d26d6f67e..5a0d2b642eb 100644 --- a/app/assets/javascripts/project_import.js +++ b/app/assets/javascripts/project_import.js @@ -2,7 +2,7 @@ import { visitUrl } from './lib/utils/url_utility'; export default function projectImport() { setTimeout(() => { - visitUrl(location.href); + visitUrl(window.location.href); }, 5000); } diff --git a/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js b/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js index 1a75fdd75db..078ccbbbac2 100644 --- a/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js +++ b/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js @@ -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 { diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js index 2da022fde63..ef3c71eeafe 100644 --- a/app/assets/javascripts/search_autocomplete.js +++ b/app/assets/javascripts/search_autocomplete.js @@ -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') { diff --git a/app/assets/javascripts/settings_panels.js b/app/assets/javascripts/settings_panels.js index eecde4550f9..37b4a2a4c63 100644 --- a/app/assets/javascripts/settings_panels.js +++ b/app/assets/javascripts/settings_panels.js @@ -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); } diff --git a/app/assets/javascripts/shortcuts_find_file.js b/app/assets/javascripts/shortcuts_find_file.js index 1e246a56b85..8658081c6c2 100644 --- a/app/assets/javascripts/shortcuts_find_file.js +++ b/app/assets/javascripts/shortcuts_find_file.js @@ -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; } diff --git a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue index 1b866cca4b1..2b8d6207dea 100644 --- a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue +++ b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue @@ -54,7 +54,7 @@ export default { updateConfidentialAttribute(confidential) { this.service .update('issue', { confidential }) - .then(() => location.reload()) + .then(() => window.location.reload()) .catch(() => { Flash( __( diff --git a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue index 10bedf8af1f..8bbc59f623a 100644 --- a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue +++ b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue @@ -76,7 +76,7 @@ export default { .update(this.issuableType, { discussion_locked: locked, }) - .then(() => location.reload()) + .then(() => window.location.reload()) .catch(() => Flash( this.__( diff --git a/app/assets/javascripts/sidebar/sidebar_mediator.js b/app/assets/javascripts/sidebar/sidebar_mediator.js index d86557e870a..d9ca5e46770 100644 --- a/app/assets/javascripts/sidebar/sidebar_mediator.js +++ b/app/assets/javascripts/sidebar/sidebar_mediator.js @@ -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); } }); diff --git a/app/assets/javascripts/snippet/snippet_embed.js b/app/assets/javascripts/snippet/snippet_embed.js index 81ec483f2d9..873a506a92f 100644 --- a/app/assets/javascripts/snippet/snippet_embed.js +++ b/app/assets/javascripts/snippet/snippet_embed.js @@ -1,5 +1,5 @@ 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'); diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js index cd954f75613..349614460e1 100644 --- a/app/assets/javascripts/users_select.js +++ b/app/assets/javascripts/users_select.js @@ -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) { diff --git a/app/assets/javascripts/vue_merge_request_widget/components/memory_usage.vue b/app/assets/javascripts/vue_merge_request_widget/components/memory_usage.vue index f012f9c6772..5e76f6b1cac 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/memory_usage.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/memory_usage.vue @@ -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 { diff --git a/app/assets/stylesheets/bootstrap_migration.scss b/app/assets/stylesheets/bootstrap_migration.scss index 5da0e672288..6017e5554d8 100644 --- a/app/assets/stylesheets/bootstrap_migration.scss +++ b/app/assets/stylesheets/bootstrap_migration.scss @@ -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, diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss index 55c0bc76f23..52b5f059f20 100644 --- a/app/assets/stylesheets/framework/layout.scss +++ b/app/assets/stylesheets/framework/layout.scss @@ -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 { diff --git a/app/assets/stylesheets/framework/secondary_navigation_elements.scss b/app/assets/stylesheets/framework/secondary_navigation_elements.scss index 2d9e9e6a67d..9dbb04e5443 100644 --- a/app/assets/stylesheets/framework/secondary_navigation_elements.scss +++ b/app/assets/stylesheets/framework/secondary_navigation_elements.scss @@ -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` diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index 04d2a049f7d..f30f296d41f 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -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; diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index a4ca82de90e..dc8842212e0 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -193,6 +193,10 @@ display: inline-flex; } + .ci-status-icon svg { + vertical-align: text-bottom; + } + > .ci-status-link, > .btn, > .commit-sha-group { diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 73eb399d7bb..79cac7f4ff0 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -280,7 +280,7 @@ width: 150px; flex-shrink: 0; - .label { + .badge { overflow: hidden; text-overflow: ellipsis; max-width: 100%; diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index 4e1768f556a..52332ac97dd 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -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)) { diff --git a/app/assets/stylesheets/pages/profiles/preferences.scss b/app/assets/stylesheets/pages/profiles/preferences.scss index babe81cb0f7..a353f301d07 100644 --- a/app/assets/stylesheets/pages/profiles/preferences.scss +++ b/app/assets/stylesheets/pages/profiles/preferences.scss @@ -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%; } } diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 7ac0eaec645..aa83e5bdebc 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -858,7 +858,6 @@ pre.light-well { .git-clone-holder { width: 380px; - height: 28px; .btn-clipboard { border: 1px solid $border-color; diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss index 4b8a3db1d06..0a56153203c 100644 --- a/app/assets/stylesheets/pages/repo.scss +++ b/app/assets/stylesheets/pages/repo.scss @@ -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 { diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss index 4abb145067a..33a974e0176 100644 --- a/app/assets/stylesheets/pages/settings.scss +++ b/app/assets/stylesheets/pages/settings.scss @@ -296,7 +296,8 @@ } .btn-clipboard { - margin-left: 5px; + background-color: $white-light; + border: 1px solid $theme-gray-200; } .deploy-token-help-block { diff --git a/app/controllers/concerns/issues_action.rb b/app/controllers/concerns/issues_action.rb index b6eb7d292fc..9d58656773d 100644 --- a/app/controllers/concerns/issues_action.rb +++ b/app/controllers/concerns/issues_action.rb @@ -1,6 +1,7 @@ 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 diff --git a/app/controllers/concerns/issues_calendar.rb b/app/controllers/concerns/issues_calendar.rb new file mode 100644 index 00000000000..671a204621d --- /dev/null +++ b/app/controllers/concerns/issues_calendar.rb @@ -0,0 +1,24 @@ +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 diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index abc283d7aa9..6484a713f8e 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -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 diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 7c65f1f5dfe..a8c0a68fc17 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -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 diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 35c36c725e2..7c897b2d86c 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -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 diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index b452bfd7e6f..38918b3cd52 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -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 diff --git a/app/serializers/merge_request_basic_entity.rb b/app/serializers/merge_request_basic_entity.rb index e4aec977f01..1c06691026d 100644 --- a/app/serializers/merge_request_basic_entity.rb +++ b/app/serializers/merge_request_basic_entity.rb @@ -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 diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb index 925775aea0b..9bdbb2c0d99 100644 --- a/app/services/ci/register_job_service.rb +++ b/app/services/ci/register_job_service.rb @@ -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| diff --git a/app/services/projects/autocomplete_service.rb b/app/services/projects/autocomplete_service.rb index 3e38a8a12d4..aa60661f7f2 100644 --- a/app/services/projects/autocomplete_service.rb +++ b/app/services/projects/autocomplete_service.rb @@ -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 diff --git a/app/uploaders/favicon_uploader.rb b/app/uploaders/favicon_uploader.rb index 09afc63a5aa..3639375d474 100644 --- a/app/uploaders/favicon_uploader.rb +++ b/app/uploaders/favicon_uploader.rb @@ -1,17 +1,6 @@ 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 diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml index 94db374040c..a0861870ba4 100644 --- a/app/views/admin/appearances/_form.html.haml +++ b/app/views/admin/appearances/_form.html.haml @@ -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 diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml index 845f4046d0d..6abb56ba6d2 100644 --- a/app/views/explore/projects/_filter.html.haml +++ b/app/views/explore/projects/_filter.html.haml @@ -1,6 +1,6 @@ - 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? diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 8b0ef3cd87a..5a88619f769 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -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 } } diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml index de8369ed7b9..b32b602ceb3 100644 --- a/app/views/help/ui.html.haml +++ b/app/views/help/ui.html.haml @@ -443,8 +443,6 @@ .col-md-6 .alert.alert-success = lorem - .alert.alert-primary - = lorem .alert.alert-info = lorem .col-md-6 diff --git a/app/views/import/gitlab_projects/new.html.haml b/app/views/import/gitlab_projects/new.html.haml index f311ac98ac6..cc672a5ea7c 100644 --- a/app/views/import/gitlab_projects/new.html.haml +++ b/app/views/import/gitlab_projects/new.html.haml @@ -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 diff --git a/app/views/layouts/header/_new_dropdown.haml b/app/views/layouts/header/_new_dropdown.haml index db8137cc248..d35df706036 100644 --- a/app/views/layouts/header/_new_dropdown.haml +++ b/app/views/layouts/header/_new_dropdown.haml @@ -1,5 +1,5 @@ %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 diff --git a/app/views/projects/_new_project_fields.html.haml b/app/views/projects/_new_project_fields.html.haml index cfbd0459e3e..6f957533287 100644 --- a/app/views/projects/_new_project_fields.html.haml +++ b/app/views/projects/_new_project_fields.html.haml @@ -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 diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index c75093c4c24..f7551434d47 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -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') diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml index 84245d72f4a..8b9c52f0802 100644 --- a/app/views/projects/buttons/_dropdown.html.haml +++ b/app/views/projects/buttons/_dropdown.html.haml @@ -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 diff --git a/app/views/projects/clusters/gcp/_form.html.haml b/app/views/projects/clusters/gcp/_form.html.haml index 59c4eeec17a..b8e40b0a38b 100644 --- a/app/views/projects/clusters/gcp/_form.html.haml +++ b/app/views/projects/clusters/gcp/_form.html.haml @@ -1,4 +1,10 @@ = 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 .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 diff --git a/app/views/projects/deploy_tokens/_index.html.haml b/app/views/projects/deploy_tokens/_index.html.haml index 50e5950ced4..725720d2222 100644 --- a/app/views/projects/deploy_tokens/_index.html.haml +++ b/app/views/projects/deploy_tokens/_index.html.haml @@ -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 diff --git a/app/views/projects/deploy_tokens/_new_deploy_token.html.haml b/app/views/projects/deploy_tokens/_new_deploy_token.html.haml index 1e715681e59..5dd9ffba074 100644 --- a/app/views/projects/deploy_tokens/_new_deploy_token.html.haml +++ b/app/views/projects/deploy_tokens/_new_deploy_token.html.haml @@ -1,14 +1,18 @@ -.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.") diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index 69753427d17..d47dc3d8143 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -1,3 +1,4 @@ +- @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 diff --git a/app/views/projects/issues/_new_branch.html.haml b/app/views/projects/issues/_new_branch.html.haml index 76438fae663..acb1a446ec4 100644 --- a/app/views/projects/issues/_new_branch.html.haml +++ b/app/views/projects/issues/_new_branch.html.haml @@ -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 diff --git a/app/views/projects/merge_requests/diffs/_version_controls.html.haml b/app/views/projects/merge_requests/diffs/_version_controls.html.haml index 1c26f0405d2..52bf584d550 100644 --- a/app/views/projects/merge_requests/diffs/_version_controls.html.haml +++ b/app/views/projects/merge_requests/diffs/_version_controls.html.haml @@ -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 diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml index 35c7dc2984a..d80d2957466 100644 --- a/app/views/projects/wikis/edit.html.haml +++ b/app/views/projects/wikis/edit.html.haml @@ -1,4 +1,4 @@ -- @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) diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml index 909987d8090..8c2cbd495a0 100644 --- a/app/views/projects/wikis/git_access.html.haml +++ b/app/views/projects/wikis/git_access.html.haml @@ -1,4 +1,4 @@ -- @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 diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml index ff72c8bb75d..a08973c7f32 100644 --- a/app/views/projects/wikis/show.html.haml +++ b/app/views/projects/wikis/show.html.haml @@ -1,4 +1,4 @@ -- @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") diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml index 5fc02ba3160..3655c2a1d42 100644 --- a/app/views/shared/_clone_panel.html.haml +++ b/app/views/shared/_clone_panel.html.haml @@ -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 diff --git a/app/views/shared/boards/components/sidebar/_labels.html.haml b/app/views/shared/boards/components/sidebar/_labels.html.haml index a112a9f1f7e..daee691e358 100644 --- a/app/views/shared/boards/components/sidebar/_labels.html.haml +++ b/app/views/shared/boards/components/sidebar/_labels.html.haml @@ -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 diff --git a/app/views/shared/issuable/_milestone_dropdown.html.haml b/app/views/shared/issuable/_milestone_dropdown.html.haml index 955b8866c2c..37625a4a163 100644 --- a/app/views/shared/issuable/_milestone_dropdown.html.haml +++ b/app/views/shared/issuable/_milestone_dropdown.html.haml @@ -1,6 +1,8 @@ - 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? diff --git a/app/views/shared/issuable/_sidebar_assignees.html.haml b/app/views/shared/issuable/_sidebar_assignees.html.haml index ed3ef6155db..8a13c7a3b83 100644 --- a/app/views/shared/issuable/_sidebar_assignees.html.haml +++ b/app/views/shared/issuable/_sidebar_assignees.html.haml @@ -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) diff --git a/app/views/shared/issuable/form/_metadata.html.haml b/app/views/shared/issuable/form/_metadata.html.haml index 01fbc163a14..bd87bb38e77 100644 --- a/app/views/shared/issuable/form/_metadata.html.haml +++ b/app/views/shared/issuable/form/_metadata.html.haml @@ -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 diff --git a/app/views/shared/notifications/_button.html.haml b/app/views/shared/notifications/_button.html.haml index e99d8d0973f..09ddf732ada 100644 --- a/app/views/shared/notifications/_button.html.haml +++ b/app/views/shared/notifications/_button.html.haml @@ -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") diff --git a/changelogs/unreleased/45557-machine-type-help-links.yml b/changelogs/unreleased/45557-machine-type-help-links.yml new file mode 100644 index 00000000000..870a650e10b --- /dev/null +++ b/changelogs/unreleased/45557-machine-type-help-links.yml @@ -0,0 +1,6 @@ +--- +title: Add machine type and pricing documentation links, add class to labels to make + bold +merge_request: +author: +type: changed diff --git a/changelogs/unreleased/46429-creating-a-deploy-token-doesn-t-bring-back-to-the-creation-page.yml b/changelogs/unreleased/46429-creating-a-deploy-token-doesn-t-bring-back-to-the-creation-page.yml new file mode 100644 index 00000000000..b564fb0174f --- /dev/null +++ b/changelogs/unreleased/46429-creating-a-deploy-token-doesn-t-bring-back-to-the-creation-page.yml @@ -0,0 +1,5 @@ +--- +title: Allows you to create another deploy token dimmediately after creating one +merge_request: 19639 +author: +type: changed diff --git a/changelogs/unreleased/47672-set_inline_content_type_for_ics.yml b/changelogs/unreleased/47672-set_inline_content_type_for_ics.yml new file mode 100644 index 00000000000..4bc883d41bd --- /dev/null +++ b/changelogs/unreleased/47672-set_inline_content_type_for_ics.yml @@ -0,0 +1,5 @@ +--- +title: Render calendar feed inline when accessed from GitLab +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/blackst0ne-rails5-expected-search-search-seed_project-got-nil.yml b/changelogs/unreleased/blackst0ne-rails5-expected-search-search-seed_project-got-nil.yml new file mode 100644 index 00000000000..e7bb2703b03 --- /dev/null +++ b/changelogs/unreleased/blackst0ne-rails5-expected-search-search-seed_project-got-nil.yml @@ -0,0 +1,5 @@ +--- +title: "[Rails5] Fix sessions_controller_spec" +merge_request: 19936 +author: "@blackst0ne" +type: fixed diff --git a/changelogs/unreleased/blackst0ne-rails5-expected-the-response-to-have-status-code-ok-but-it-was-404.yml b/changelogs/unreleased/blackst0ne-rails5-expected-the-response-to-have-status-code-ok-but-it-was-404.yml new file mode 100644 index 00000000000..fad15de2dd5 --- /dev/null +++ b/changelogs/unreleased/blackst0ne-rails5-expected-the-response-to-have-status-code-ok-but-it-was-404.yml @@ -0,0 +1,5 @@ +--- +title: "[Rails5] Set request.format for artifacts_controller" +merge_request: 19937 +author: "@blackst0ne" +type: fixed diff --git a/changelogs/unreleased/blackst0ne-rails5-fix-blob-requests-format.yml b/changelogs/unreleased/blackst0ne-rails5-fix-blob-requests-format.yml new file mode 100644 index 00000000000..a83aa03606a --- /dev/null +++ b/changelogs/unreleased/blackst0ne-rails5-fix-blob-requests-format.yml @@ -0,0 +1,5 @@ +--- +title: "[Rails5] Explicitly set request.format for blob_controller" +merge_request: 19876 +author: "@blackst0ne" +type: fixed diff --git a/changelogs/unreleased/blackst0ne-rails5-fix-optimistic-lock-values.yml b/changelogs/unreleased/blackst0ne-rails5-fix-optimistic-lock-values.yml new file mode 100644 index 00000000000..1915dff73ab --- /dev/null +++ b/changelogs/unreleased/blackst0ne-rails5-fix-optimistic-lock-values.yml @@ -0,0 +1,5 @@ +--- +title: "[Rails5] Fix optimistic lock value" +merge_request: 19878 +author: "@blackst0ne" +type: fixed diff --git a/changelogs/unreleased/blackst0ne-rails5-fix-pipeline-schedules-controller-spec.yml b/changelogs/unreleased/blackst0ne-rails5-fix-pipeline-schedules-controller-spec.yml new file mode 100644 index 00000000000..7a2b19ad681 --- /dev/null +++ b/changelogs/unreleased/blackst0ne-rails5-fix-pipeline-schedules-controller-spec.yml @@ -0,0 +1,5 @@ +--- +title: "[Rails5] Fix pipeline_schedules_controller_spec" +merge_request: 19919 +author: "@blackst0ne" +type: fixed diff --git a/changelogs/unreleased/blackst0ne-rails5-invalid-single-table-inheritance-type-group-is-not-a-subclass-of-namespace.yml b/changelogs/unreleased/blackst0ne-rails5-invalid-single-table-inheritance-type-group-is-not-a-subclass-of-namespace.yml new file mode 100644 index 00000000000..92e6ce35941 --- /dev/null +++ b/changelogs/unreleased/blackst0ne-rails5-invalid-single-table-inheritance-type-group-is-not-a-subclass-of-namespace.yml @@ -0,0 +1,6 @@ +--- +title: "[Rails5] Invalid single-table inheritance type: Group is not a subclass of + Namespace" +merge_request: 19918 +author: "@blackst0ne" +type: fixed diff --git a/changelogs/unreleased/fix-web-ide-disable-markdown-autocomplete.yml b/changelogs/unreleased/fix-web-ide-disable-markdown-autocomplete.yml new file mode 100644 index 00000000000..6a4d9b6c8c4 --- /dev/null +++ b/changelogs/unreleased/fix-web-ide-disable-markdown-autocomplete.yml @@ -0,0 +1,5 @@ +--- +title: Disabled Web IDE autocomplete suggestions for Markdown files. +merge_request: +author: Isaac Smith +type: fixed diff --git a/changelogs/unreleased/ide-commit-actions-update.yml b/changelogs/unreleased/ide-commit-actions-update.yml new file mode 100644 index 00000000000..35bee94e156 --- /dev/null +++ b/changelogs/unreleased/ide-commit-actions-update.yml @@ -0,0 +1,5 @@ +--- +title: Improve Web IDE commit flow +merge_request: +author: +type: changed diff --git a/changelogs/unreleased/optimise-paused-runners.yml b/changelogs/unreleased/optimise-paused-runners.yml new file mode 100644 index 00000000000..13097e507d3 --- /dev/null +++ b/changelogs/unreleased/optimise-paused-runners.yml @@ -0,0 +1,5 @@ +--- +title: Optimise paused runners to reduce amount of used requests +merge_request: +author: +type: performance diff --git a/changelogs/unreleased/rails5-fix-47960.yml b/changelogs/unreleased/rails5-fix-47960.yml new file mode 100644 index 00000000000..2229511ccd6 --- /dev/null +++ b/changelogs/unreleased/rails5-fix-47960.yml @@ -0,0 +1,5 @@ +--- +title: Rails5 fix update_attribute usage not causing a save +merge_request: 19881 +author: Jasper Maes +type: fixed diff --git a/changelogs/unreleased/rails5-fix-48009.yml b/changelogs/unreleased/rails5-fix-48009.yml new file mode 100644 index 00000000000..7ade9ef2b7d --- /dev/null +++ b/changelogs/unreleased/rails5-fix-48009.yml @@ -0,0 +1,5 @@ +--- +title: Rails5 update Gemfile.rails5.lock +merge_request: 19921 +author: Jasper Maes +type: fixed diff --git a/changelogs/unreleased/rails5-fix-48012.yml b/changelogs/unreleased/rails5-fix-48012.yml new file mode 100644 index 00000000000..953ccbd8af7 --- /dev/null +++ b/changelogs/unreleased/rails5-fix-48012.yml @@ -0,0 +1,6 @@ +--- +title: Rails5 fix passing Group objects array into for_projects_and_groups milestone + scope +merge_request: 19920 +author: Jasper Maes +type: fixed diff --git a/changelogs/unreleased/rails5-fix-db-check.yml b/changelogs/unreleased/rails5-fix-db-check.yml new file mode 100644 index 00000000000..ccac4619ea7 --- /dev/null +++ b/changelogs/unreleased/rails5-fix-db-check.yml @@ -0,0 +1,5 @@ +--- +title: Rails5 fix connection execute return integer instead of string +merge_request: 19901 +author: Jasper Maes +type: fixed diff --git a/changelogs/unreleased/remove-ci_job_request_with_tags_matcher.yml b/changelogs/unreleased/remove-ci_job_request_with_tags_matcher.yml new file mode 100644 index 00000000000..b86512445d5 --- /dev/null +++ b/changelogs/unreleased/remove-ci_job_request_with_tags_matcher.yml @@ -0,0 +1,5 @@ +--- +title: Remove the ci_job_request_with_tags_matcher +merge_request: +author: +type: performance diff --git a/changelogs/unreleased/remove-small-container-width.yml b/changelogs/unreleased/remove-small-container-width.yml new file mode 100644 index 00000000000..1af8aafa87e --- /dev/null +++ b/changelogs/unreleased/remove-small-container-width.yml @@ -0,0 +1,5 @@ +--- +title: Remove small container width +merge_request: 19893 +author: George Tsiolis +type: changed diff --git a/config/initializers/active_record_locking.rb b/config/initializers/active_record_locking.rb index 3e7111fd063..0861544c5a4 100644 --- a/config/initializers/active_record_locking.rb +++ b/config/initializers/active_record_locking.rb @@ -1,73 +1,80 @@ # 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 diff --git a/config/routes.rb b/config/routes.rb index 52726f94753..e0a9139b1b4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -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 diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 8e13ceb9257..e11eb840265 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -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 diff --git a/doc/development/new_fe_guide/style/prettier.md b/doc/development/new_fe_guide/style/prettier.md index eb18189282b..6395af6f815 100644 --- a/doc/development/new_fe_guide/style/prettier.md +++ b/doc/development/new_fe_guide/style/prettier.md @@ -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. diff --git a/doc/integration/google.md b/doc/integration/google.md index ae1d848f439..8906f91b6b4 100644 --- a/doc/integration/google.md +++ b/doc/integration/google.md @@ -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: diff --git a/doc/topics/git/index.md b/doc/topics/git/index.md index 2ca2bf743fb..7707d56764e 100644 --- a/doc/topics/git/index.md +++ b/doc/topics/git/index.md @@ -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) diff --git a/doc/university/high-availability/aws/README.md b/doc/university/high-availability/aws/README.md index f340164b882..dc045961ed7 100644 --- a/doc/university/high-availability/aws/README.md +++ b/doc/university/high-availability/aws/README.md @@ -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 diff --git a/doc/user/reserved_names.md b/doc/user/reserved_names.md index 6c1378560ef..918daee5d9f 100644 --- a/doc/user/reserved_names.md +++ b/doc/user/reserved_names.md @@ -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 diff --git a/lib/api/runner.rb b/lib/api/runner.rb index dc102259ca8..96a02914faa 100644 --- a/lib/api/runner.rb +++ b/lib/api/runner.rb @@ -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] diff --git a/lib/gitlab/database/rename_reserved_paths_migration/v1/migration_classes.rb b/lib/gitlab/database/rename_reserved_paths_migration/v1/migration_classes.rb index 62d4d0a92a6..26ae6966746 100644 --- a/lib/gitlab/database/rename_reserved_paths_migration/v1/migration_classes.rb +++ b/lib/gitlab/database/rename_reserved_paths_migration/v1/migration_classes.rb @@ -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 diff --git a/lib/gitlab/favicon.rb b/lib/gitlab/favicon.rb index 451c9daf761..faf7016d73a 100644 --- a/lib/gitlab/favicon.rb +++ b/lib/gitlab/favicon.rb @@ -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']) diff --git a/lib/gitlab/git/wiki.rb b/lib/gitlab/git/wiki.rb index 1ab8c4e0229..8ee46b59830 100644 --- a/lib/gitlab/git/wiki.rb +++ b/lib/gitlab/git/wiki.rb @@ -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 diff --git a/lib/gitlab/health_checks/db_check.rb b/lib/gitlab/health_checks/db_check.rb index e27e16ddaf6..08495c0a59e 100644 --- a/lib/gitlab/health_checks/db_check.rb +++ b/lib/gitlab/health_checks/db_check.rb @@ -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 diff --git a/lib/gitlab/path_regex.rb b/lib/gitlab/path_regex.rb index e5191f5c7f9..61653044433 100644 --- a/lib/gitlab/path_regex.rb +++ b/lib/gitlab/path_regex.rb @@ -30,6 +30,7 @@ module Gitlab dashboard deploy.html explore + favicon.ico favicon.png files groups diff --git a/lib/gitlab/profiler.rb b/lib/gitlab/profiler.rb index 18540e64d4c..ecff6ab5d5e 100644 --- a/lib/gitlab/profiler.rb +++ b/lib/gitlab/profiler.rb @@ -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 diff --git a/package.json b/package.json index 4e1ca905ee8..6a52af10dc3 100644 --- a/package.json +++ b/package.json @@ -130,7 +130,7 @@ "karma-sourcemap-loader": "^0.3.7", "karma-webpack": "3.0.0", "nodemon": "^1.17.3", - "prettier": "1.11.1", + "prettier": "1.12.1", "webpack-dev-server": "^3.1.4" } } diff --git a/public/favicon.png b/public/favicon.png Binary files differdeleted file mode 100644 index 845e0ec34a5..00000000000 --- a/public/favicon.png +++ /dev/null diff --git a/scripts/frontend/prettier.js b/scripts/frontend/prettier.js index 39de77bc333..6e4e36b9b2d 100644 --- a/scripts/frontend/prettier.js +++ b/scripts/frontend/prettier.js @@ -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); +} diff --git a/spec/controllers/projects/imports_controller_spec.rb b/spec/controllers/projects/imports_controller_spec.rb index 011843baffc..812833cc86b 100644 --- a/spec/controllers/projects/imports_controller_spec.rb +++ b/spec/controllers/projects/imports_controller_spec.rb @@ -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 diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 22858de0475..a412e74581d 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -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 diff --git a/spec/controllers/projects/pipeline_schedules_controller_spec.rb b/spec/controllers/projects/pipeline_schedules_controller_spec.rb index 3506305f755..4cdaa54e0bc 100644 --- a/spec/controllers/projects/pipeline_schedules_controller_spec.rb +++ b/spec/controllers/projects/pipeline_schedules_controller_spec.rb @@ -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 diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb index 555b186fe31..2b61e0d4a85 100644 --- a/spec/controllers/sessions_controller_spec.rb +++ b/spec/controllers/sessions_controller_spec.rb @@ -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) diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb index 1df2c954893..eb94d395a9e 100644 --- a/spec/controllers/uploads_controller_spec.rb +++ b/spec/controllers/uploads_controller_spec.rb @@ -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 diff --git a/spec/features/ics/dashboard_issues_spec.rb b/spec/features/ics/dashboard_issues_spec.rb index 5d6cd44ad1c..90d02f7e40f 100644 --- a/spec/features/ics/dashboard_issues_spec.rb +++ b/spec/features/ics/dashboard_issues_spec.rb @@ -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 diff --git a/spec/features/ics/group_issues_spec.rb b/spec/features/ics/group_issues_spec.rb index 0a049be2ffe..24de5b4b7c6 100644 --- a/spec/features/ics/group_issues_spec.rb +++ b/spec/features/ics/group_issues_spec.rb @@ -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 diff --git a/spec/features/ics/project_issues_spec.rb b/spec/features/ics/project_issues_spec.rb index b99e9607f1d..2ca3d52a5be 100644 --- a/spec/features/ics/project_issues_spec.rb +++ b/spec/features/ics/project_issues_spec.rb @@ -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 diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb index dacca494755..17818beb947 100644 --- a/spec/features/issues/user_uses_slash_commands_spec.rb +++ b/spec/features/issues/user_uses_slash_commands_spec.rb @@ -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 diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb index 4700ada1aae..5573148f8bc 100644 --- a/spec/features/labels_hierarchy_spec.rb +++ b/spec/features/labels_hierarchy_spec.rb @@ -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 diff --git a/spec/fixtures/api/schemas/entities/merge_request_basic.json b/spec/fixtures/api/schemas/entities/merge_request_basic.json index f7bc137c90c..cf257ac00de 100644 --- a/spec/fixtures/api/schemas/entities/merge_request_basic.json +++ b/spec/fixtures/api/schemas/entities/merge_request_basic.json @@ -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 } diff --git a/spec/javascripts/blob/viewer/index_spec.js b/spec/javascripts/blob/viewer/index_spec.js index f920c4ca945..8b79624d9f4 100644 --- a/spec/javascripts/blob/viewer/index_spec.js +++ b/spec/javascripts/blob/viewer/index_spec.js @@ -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(); diff --git a/spec/javascripts/bootstrap_linked_tabs_spec.js b/spec/javascripts/bootstrap_linked_tabs_spec.js index 93dc60d59fe..6f679369289 100644 --- a/spec/javascripts/bootstrap_linked_tabs_spec.js +++ b/spec/javascripts/bootstrap_linked_tabs_spec.js @@ -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', diff --git a/spec/javascripts/commits_spec.js b/spec/javascripts/commits_spec.js index 60d100e8544..28b89157bd3 100644 --- a/spec/javascripts/commits_spec.js +++ b/spec/javascripts/commits_spec.js @@ -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, { diff --git a/spec/javascripts/environments/environments_app_spec.js b/spec/javascripts/environments/environments_app_spec.js index 615168645b4..6968fbc7ce7 100644 --- a/spec/javascripts/environments/environments_app_spec.js +++ b/spec/javascripts/environments/environments_app_spec.js @@ -220,7 +220,7 @@ describe('Environment', () => { ); component = mountComponent(EnvironmentsComponent, mockData); - spyOn(history, 'pushState').and.stub(); + spyOn(window.history, 'pushState').and.stub(); }); describe('updateContent', () => { diff --git a/spec/javascripts/environments/folder/environments_folder_view_spec.js b/spec/javascripts/environments/folder/environments_folder_view_spec.js index f5ce4df0bfe..51d4213c38f 100644 --- a/spec/javascripts/environments/folder/environments_folder_view_spec.js +++ b/spec/javascripts/environments/folder/environments_folder_view_spec.js @@ -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', () => { diff --git a/spec/javascripts/gl_field_errors_spec.js b/spec/javascripts/gl_field_errors_spec.js index 4e93fd91751..108e0064c47 100644 --- a/spec/javascripts/gl_field_errors_spec.js +++ b/spec/javascripts/gl_field_errors_spec.js @@ -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); }); diff --git a/spec/javascripts/helpers/user_mock_data_helper.js b/spec/javascripts/helpers/user_mock_data_helper.js index 323fee3767e..f6c3ce5aecc 100644 --- a/spec/javascripts/helpers/user_mock_data_helper.js +++ b/spec/javascripts/helpers/user_mock_data_helper.js @@ -1,7 +1,7 @@ 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', diff --git a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js index 8f7cf24c22f..bf96170f703 100644 --- a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js +++ b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js @@ -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(); }); diff --git a/spec/javascripts/ide/components/commit_sidebar/list_spec.js b/spec/javascripts/ide/components/commit_sidebar/list_spec.js index 6fb52378386..b786be55019 100644 --- a/spec/javascripts/ide/components/commit_sidebar/list_spec.js +++ b/spec/javascripts/ide/components/commit_sidebar/list_spec.js @@ -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); }); }); diff --git a/spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js b/spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js index 6bf8710bda7..a5b906da8a1 100644 --- a/spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js +++ b/spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js @@ -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); }); diff --git a/spec/javascripts/ide/components/repo_commit_section_spec.js b/spec/javascripts/ide/components/repo_commit_section_spec.js index 531bcd6e540..6bf309fb4bf 100644 --- a/spec/javascripts/ide/components/repo_commit_section_spec.js +++ b/spec/javascripts/ide/components/repo_commit_section_spec.js @@ -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(); }); diff --git a/spec/javascripts/ide/lib/editor_spec.js b/spec/javascripts/ide/lib/editor_spec.js index c1932284d53..c2cb964ea87 100644 --- a/spec/javascripts/ide/lib/editor_spec.js +++ b/spec/javascripts/ide/lib/editor_spec.js @@ -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, + }); + }); }); diff --git a/spec/javascripts/ide/stores/actions/file_spec.js b/spec/javascripts/ide/stores/actions/file_spec.js index 7bebc2288e3..5746683917e 100644 --- a/spec/javascripts/ide/stores/actions/file_spec.js +++ b/spec/javascripts/ide/stores/actions/file_spec.js @@ -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(); }) diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js index bf1f0c822fe..eb5e0bddb74 100644 --- a/spec/javascripts/issue_show/components/app_spec.js +++ b/spec/javascripts/issue_show/components/app_spec.js @@ -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, }, }); diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js index 2d7cc3443cf..a9ec7f42a9d 100644 --- a/spec/javascripts/lib/utils/common_utils_spec.js +++ b/spec/javascripts/lib/utils/common_utils_spec.js @@ -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('', '', '?'); }); }); diff --git a/spec/javascripts/pipelines/pipelines_spec.js b/spec/javascripts/pipelines/pipelines_spec.js index ff17602da2b..50141bd99b4 100644 --- a/spec/javascripts/pipelines/pipelines_spec.js +++ b/spec/javascripts/pipelines/pipelines_spec.js @@ -427,7 +427,7 @@ describe('Pipelines', () => { describe('methods', () => { beforeEach(() => { - spyOn(history, 'pushState').and.stub(); + spyOn(window.history, 'pushState').and.stub(); }); describe('updateContent', () => { diff --git a/spec/javascripts/profile/account/components/update_username_spec.js b/spec/javascripts/profile/account/components/update_username_spec.js index bac306edf5a..5311499fb73 100644 --- a/spec/javascripts/profile/account/components/update_username_spec.js +++ b/spec/javascripts/profile/account/components/update_username_spec.js @@ -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(() => { diff --git a/spec/javascripts/settings_panels_spec.js b/spec/javascripts/settings_panels_spec.js index 4fba36bd4de..c1a69bd7018 100644 --- a/spec/javascripts/settings_panels_spec.js +++ b/spec/javascripts/settings_panels_spec.js @@ -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 diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index 2411d33a496..994011b262a 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -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 || {}; diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb index 08c4a474217..f36111a4946 100644 --- a/spec/lib/gitlab/favicon_spec.rb +++ b/spec/lib/gitlab/favicon_spec.rb @@ -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 diff --git a/spec/lib/gitlab/git/committer_with_hooks_spec.rb b/spec/lib/gitlab/git/committer_with_hooks_spec.rb index 267056b96e6..2100690f873 100644 --- a/spec/lib/gitlab/git/committer_with_hooks_spec.rb +++ b/spec/lib/gitlab/git/committer_with_hooks_spec.rb @@ -1,154 +1,156 @@ 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 diff --git a/spec/lib/gitlab/git/wiki_spec.rb b/spec/lib/gitlab/git/wiki_spec.rb index 35b06b14620..b63658e1b3b 100644 --- a/spec/lib/gitlab/git/wiki_spec.rb +++ b/spec/lib/gitlab/git/wiki_spec.rb @@ -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 diff --git a/spec/lib/gitlab/profiler_spec.rb b/spec/lib/gitlab/profiler_spec.rb index 548eb28fe4d..4059188fba1 100644 --- a/spec/lib/gitlab/profiler_spec.rb +++ b/spec/lib/gitlab/profiler_spec.rb @@ -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 diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index b9f1c7dd5df..6c637533c6b 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -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 diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index 16e6f19773f..e7639599874 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -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 diff --git a/spec/uploaders/favicon_uploader_spec.rb b/spec/uploaders/favicon_uploader_spec.rb deleted file mode 100644 index 37deea8ab90..00000000000 --- a/spec/uploaders/favicon_uploader_spec.rb +++ /dev/null @@ -1,29 +0,0 @@ -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 diff --git a/spec/workers/every_sidekiq_worker_spec.rb b/spec/workers/every_sidekiq_worker_spec.rb index 9e3b99b3502..2106959e23c 100644 --- a/spec/workers/every_sidekiq_worker_spec.rb +++ b/spec/workers/every_sidekiq_worker_spec.rb @@ -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 diff --git a/yarn.lock b/yarn.lock index 65d78173a5b..cefd7c9a62e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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" |