diff options
619 files changed, 3871 insertions, 1494 deletions
diff --git a/app/assets/javascripts/commons/polyfills.js b/app/assets/javascripts/commons/polyfills.js index 7a6ad3dc771..daa941a63cd 100644 --- a/app/assets/javascripts/commons/polyfills.js +++ b/app/assets/javascripts/commons/polyfills.js @@ -12,6 +12,7 @@ import 'core-js/es/promise/finally'; import 'core-js/es/string/code-point-at'; import 'core-js/es/string/from-code-point'; import 'core-js/es/string/includes'; +import 'core-js/es/string/repeat'; import 'core-js/es/string/starts-with'; import 'core-js/es/string/ends-with'; import 'core-js/es/symbol'; diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js index a66555838ba..b98fe9f6ce2 100644 --- a/app/assets/javascripts/gl_form.js +++ b/app/assets/javascripts/gl_form.js @@ -3,9 +3,16 @@ import autosize from 'autosize'; import GfmAutoComplete, { defaultAutocompleteConfig } from 'ee_else_ce/gfm_auto_complete'; import dropzoneInput from './dropzone_input'; import { addMarkdownListeners, removeMarkdownListeners } from './lib/utils/text_markdown'; +import IndentHelper from './helpers/indent_helper'; +import { keystroke } from './lib/utils/common_utils'; +import * as keys from './lib/utils/keycodes'; +import UndoStack from './lib/utils/undo_stack'; export default class GLForm { constructor(form, enableGFM = {}) { + this.handleKeyShortcuts = this.handleKeyShortcuts.bind(this); + this.setState = this.setState.bind(this); + this.form = form; this.textarea = this.form.find('textarea.js-gfm-input'); this.enableGFM = Object.assign({}, defaultAutocompleteConfig, enableGFM); @@ -16,6 +23,10 @@ export default class GLForm { this.enableGFM[item] = Boolean(dataSources[item]); } }); + + this.undoStack = new UndoStack(); + this.indentHelper = new IndentHelper(this.textarea[0]); + // Before we start, we should clean up any previous data for this form this.destroy(); // Set up the form @@ -85,9 +96,84 @@ export default class GLForm { clearEventListeners() { this.textarea.off('focus'); this.textarea.off('blur'); + this.textarea.off('keydown'); removeMarkdownListeners(this.form); } + setState(state) { + const selection = [this.textarea[0].selectionStart, this.textarea[0].selectionEnd]; + this.textarea.val(state); + this.textarea[0].setSelectionRange(selection[0], selection[1]); + } + + /* + Handle keypresses for a custom undo/redo stack. + We need this because the toolbar buttons and indentation helpers mess with the browser's + native undo/redo capability. + */ + handleUndo(event) { + const content = this.textarea.val(); + const { selectionStart, selectionEnd } = this.textarea[0]; + const stack = this.undoStack; + + if (stack.isEmpty()) { + // ==== Save initial state in undo history ==== + stack.save(content); + } + + if (keystroke(event, keys.Z_KEY_CODE, 'l')) { + // ==== Undo ==== + event.preventDefault(); + stack.save(content); + if (stack.canUndo()) { + this.setState(stack.undo()); + } + } else if (keystroke(event, keys.Z_KEY_CODE, 'ls') || keystroke(event, keys.Y_KEY_CODE, 'l')) { + // ==== Redo ==== + event.preventDefault(); + if (stack.canRedo()) { + this.setState(stack.redo()); + } + } else if ( + keystroke(event, keys.SPACE_KEY_CODE) || + keystroke(event, keys.ENTER_KEY_CODE) || + selectionStart !== selectionEnd + ) { + // ==== Save after finishing a word or before deleting a large selection ==== + stack.save(content); + } else if (content === '') { + // ==== Save after deleting everything ==== + stack.save(''); + } else { + // ==== Save after 1 second of inactivity ==== + stack.scheduleSave(content); + } + } + + handleIndent(event) { + if (keystroke(event, keys.LEFT_BRACKET_KEY_CODE, 'l')) { + // ==== Unindent selected lines ==== + event.preventDefault(); + this.indentHelper.unindent(); + } else if (keystroke(event, keys.RIGHT_BRACKET_KEY_CODE, 'l')) { + // ==== Indent selected lines ==== + event.preventDefault(); + this.indentHelper.indent(); + } else if (keystroke(event, keys.ENTER_KEY_CODE)) { + // ==== Auto-indent new lines ==== + event.preventDefault(); + this.indentHelper.newline(); + } else if (keystroke(event, keys.BACKSPACE_KEY_CODE)) { + // ==== Auto-delete indents at the beginning of the line ==== + this.indentHelper.backspace(event); + } + } + + handleKeyShortcuts(event) { + this.handleIndent(event); + this.handleUndo(event); + } + addEventListeners() { this.textarea.on('focus', function focusTextArea() { $(this) @@ -99,5 +185,6 @@ export default class GLForm { .closest('.md-area') .removeClass('is-focused'); }); + this.textarea.on('keydown', e => this.handleKeyShortcuts(e.originalEvent)); } } diff --git a/app/assets/javascripts/helpers/indent_helper.js b/app/assets/javascripts/helpers/indent_helper.js new file mode 100644 index 00000000000..a8815fac04e --- /dev/null +++ b/app/assets/javascripts/helpers/indent_helper.js @@ -0,0 +1,182 @@ +const INDENT_SEQUENCE = ' '; + +function countLeftSpaces(text) { + const i = text.split('').findIndex(c => c !== ' '); + return i === -1 ? text.length : i; +} + +/** + * IndentHelper provides methods that allow manual and smart indentation in + * textareas. It supports line indent/unindent, selection indent/unindent, + * auto indentation on newlines, and smart deletion of indents with backspace. + */ +export default class IndentHelper { + /** + * Creates a new IndentHelper and binds it to the given `textarea`. You can provide a custom indent sequence in the second parameter, but the `newline` and `backspace` operations may work funny if the indent sequence isn't spaces only. + */ + constructor(textarea, indentSequence = INDENT_SEQUENCE) { + this.element = textarea; + this.seq = indentSequence; + } + + getSelection() { + return { start: this.element.selectionStart, end: this.element.selectionEnd }; + } + + isRangeSelection() { + return this.element.selectionStart !== this.element.selectionEnd; + } + + /** + * Re-implementation of textarea's setRangeText method, because IE/Edge don't support it. + * + * @see https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-textarea%2Finput-setrangetext + */ + setRangeText(replacement, start, end, selectMode) { + // Disable eslint to remain as faithful as possible to the above linked spec + /* eslint-disable no-param-reassign, no-case-declarations */ + const text = this.element.value; + + if (start > end) { + throw new RangeError('setRangeText: start index must be less than or equal to end index'); + } + + // Clamp to [0, len] + start = Math.max(0, Math.min(start, text.length)); + end = Math.max(0, Math.min(end, text.length)); + + let selection = { start: this.element.selectionStart, end: this.element.selectionEnd }; + + this.element.value = text.slice(0, start) + replacement + text.slice(end); + + const newLength = replacement.length; + const newEnd = start + newLength; + + switch (selectMode) { + case 'select': + selection = { start, newEnd }; + break; + case 'start': + selection = { start, end: start }; + break; + case 'end': + selection = { start: newEnd, end: newEnd }; + break; + case 'preserve': + default: + const oldLength = end - start; + const delta = newLength - oldLength; + if (selection.start > end) { + selection.start += delta; + } else if (selection.start > start) { + selection.start = start; + } + if (selection.end > end) { + selection.end += delta; + } else if (selection.end > start) { + selection.end = newEnd; + } + } + + this.element.setSelectionRange(selection.start, selection.end); + + /* eslint-enable no-param-reassign, no-case-declarations */ + } + + /** + * Returns an array of lines in the textarea, with information about their + * start/end offsets and whether they are included in the current selection. + */ + splitLines() { + const { start, end } = this.getSelection(); + + const lines = this.element.value.split('\n'); + let textStart = 0; + const lineObjects = []; + lines.forEach(line => { + const lineObj = { + text: line, + start: textStart, + end: textStart + line.length, + }; + lineObj.inSelection = lineObj.start <= end && lineObj.end >= start; + lineObjects.push(lineObj); + textStart += line.length + 1; + }); + return lineObjects; + } + + /** + * Indents selected lines by one level. + */ + indent() { + const { start } = this.getSelection(); + + const selectedLines = this.splitLines().filter(line => line.inSelection); + if (!this.isRangeSelection() && start === selectedLines[0].start) { + // Special case: if cursor is at the beginning of the line, move it one + // indent right. + const line = selectedLines[0]; + this.setRangeText(this.seq, line.start, line.start, 'end'); + } else { + selectedLines.reverse(); + selectedLines.forEach(line => { + this.setRangeText(INDENT_SEQUENCE, line.start, line.start, 'preserve'); + }); + } + } + + /** + * Unindents selected lines by one level. + */ + unindent() { + const lines = this.splitLines().filter(line => line.inSelection); + lines.reverse(); + lines + .filter(line => line.text.startsWith(this.seq)) + .forEach(line => { + this.setRangeText('', line.start, line.start + this.seq.length, 'preserve'); + }); + } + + /** + * Emulates a newline keypress, automatically indenting the new line. + */ + newline() { + const { start, end } = this.getSelection(); + + if (this.isRangeSelection()) { + // Manually kill the selection before calculating the indent + this.setRangeText('', start, end, 'start'); + } + + // Auto-indent the next line + const currentLine = this.splitLines().find(line => line.end >= start); + const spaces = countLeftSpaces(currentLine.text); + this.setRangeText(`\n${' '.repeat(spaces)}`, start, start, 'end'); + } + + /** + * If the cursor is positioned at the end of a line's leading indents, + * emulates a backspace keypress by deleting a single level of indents. + * @param event The DOM KeyboardEvent that triggers this action, or null. + */ + backspace(event) { + const { start } = this.getSelection(); + + // If the cursor is at the end of leading indents, delete an indent. + if (!this.isRangeSelection()) { + const currentLine = this.splitLines().find(line => line.end >= start); + const cursorPosition = start - currentLine.start; + if (countLeftSpaces(currentLine.text) === cursorPosition && cursorPosition > 0) { + if (event) event.preventDefault(); + + let spacesToDelete = cursorPosition % this.seq.length; + if (spacesToDelete === 0) { + spacesToDelete = this.seq.length; + } + this.setRangeText('', start - spacesToDelete, start, 'start'); + } + } + } +} diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index 5e90893b684..1a94aee2398 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -203,6 +203,71 @@ export const isMetaKey = e => e.metaKey || e.ctrlKey || e.altKey || e.shiftKey; // 3) Middle-click or Mouse Wheel Click (e.which is 2) export const isMetaClick = e => e.metaKey || e.ctrlKey || e.which === 2; +export const getPlatformLeaderKey = () => { + // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + if (navigator && navigator.platform && navigator.platform.startsWith('Mac')) { + return 'meta'; + } + return 'ctrl'; +}; + +export const getPlatformLeaderKeyHTML = () => { + if (getPlatformLeaderKey() === 'meta') { + return '⌘'; + } + // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + return 'Ctrl'; +}; + +export const isPlatformLeaderKey = e => { + if (getPlatformLeaderKey() === 'meta') { + return Boolean(e.metaKey); + } + return Boolean(e.ctrlKey); +}; + +/** + * Tests if a KeyboardEvent corresponds exactly to a keystroke. + * + * This function avoids hacking around an old version of Mousetrap, which we ship at the moment. It should be removed after we upgrade to the newest Mousetrap. See: + * - https://gitlab.com/gitlab-org/gitlab-ce/issues/63182 + * - https://gitlab.com/gitlab-org/gitlab-ce/issues/64246 + * + * @example + * // Matches the enter key with exactly zero modifiers + * keystroke(event, 13) + * + * @example + * // Matches Control-Shift-Z + * keystroke(event, 90, 'cs') + * + * @param e The KeyboardEvent to test. + * @param keyCode The key code of the key to test. Why keycodes? IE/Edge don't support the more convenient `key` and `code` properties. + * @param modifiers A string of modifiers keys. Each modifier key is represented by one character. The set of pressed modifier keys must match the given string exactly. Available options are 'a' for Alt/Option, 'c' for Control, 'm' for Meta/Command, 's' for Shift, and 'l' for the leader key (Meta on MacOS and Control otherwise). + * @returns {boolean} True if the KeyboardEvent corresponds to the given keystroke. + */ +export const keystroke = (e, keyCode, modifiers = '') => { + if (!e || !keyCode) { + return false; + } + + const leader = getPlatformLeaderKey(); + const mods = modifiers.toLowerCase().replace('l', leader.charAt(0)); + + // Match depressed modifier keys + if ( + e.altKey !== mods.includes('a') || + e.ctrlKey !== mods.includes('c') || + e.metaKey !== mods.includes('m') || + e.shiftKey !== mods.includes('s') + ) { + return false; + } + + // Match the depressed key + return keyCode === (e.keyCode || e.which); +}; + export const contentTop = () => { const perfBar = $('#js-peek').outerHeight() || 0; const mrTabsHeight = $('.merge-request-tabs').outerHeight() || 0; diff --git a/app/assets/javascripts/lib/utils/keycodes.js b/app/assets/javascripts/lib/utils/keycodes.js index 5e0f9b612a2..e24fcf47d71 100644 --- a/app/assets/javascripts/lib/utils/keycodes.js +++ b/app/assets/javascripts/lib/utils/keycodes.js @@ -1,4 +1,10 @@ -export const UP_KEY_CODE = 38; -export const DOWN_KEY_CODE = 40; +export const BACKSPACE_KEY_CODE = 8; export const ENTER_KEY_CODE = 13; export const ESC_KEY_CODE = 27; +export const SPACE_KEY_CODE = 32; +export const UP_KEY_CODE = 38; +export const DOWN_KEY_CODE = 40; +export const Y_KEY_CODE = 89; +export const Z_KEY_CODE = 90; +export const LEFT_BRACKET_KEY_CODE = 219; +export const RIGHT_BRACKET_KEY_CODE = 221; diff --git a/app/assets/javascripts/lib/utils/undo_stack.js b/app/assets/javascripts/lib/utils/undo_stack.js new file mode 100644 index 00000000000..6cfdc2a0a0f --- /dev/null +++ b/app/assets/javascripts/lib/utils/undo_stack.js @@ -0,0 +1,105 @@ +/** + * UndoStack provides a custom implementation of an undo/redo engine. It was originally written for GitLab's Markdown editor (`gl_form.js`), whose rich text editing capabilities broke native browser undo/redo behaviour. + * + * UndoStack supports predictable undos/redos, debounced saves, maximum history length, and duplicate detection. + * + * Usage: + * - `stack = new UndoStack();` + * - Saves a state to the stack with `stack.save(state)`. + * - Get the current state with `stack.current()`. + * - Revert to the previous state with `stack.undo()`. + * - Redo a previous undo with `stack.redo()`; + * - Queue a future save with `stack.scheduleSave(state, delay)`. Useful for text editors. + * - See the full undo history in `stack.history`. + */ +export default class UndoStack { + constructor(maxLength = 1000) { + this.clear(); + this.maxLength = maxLength; + + // If you're storing reference-types in the undo stack, you might want to + // reassign this property to some deep-equals function. + this.comparator = (a, b) => a === b; + } + + current() { + if (this.cursor === -1) { + return undefined; + } + return this.history[this.cursor]; + } + + isEmpty() { + return this.history.length === 0; + } + + clear() { + this.clearPending(); + this.history = []; + this.cursor = -1; + } + + save(state) { + this.clearPending(); + if (this.comparator(state, this.current())) { + // Don't save state if it's the same as the current state + return; + } + + this.history.length = this.cursor + 1; + this.history.push(state); + this.cursor += 1; + + if (this.history.length > this.maxLength) { + this.history.shift(); + this.cursor -= 1; + } + } + + scheduleSave(state, delay = 1000) { + this.clearPending(); + this.pendingState = state; + this.timeout = setTimeout(this.saveNow.bind(this), delay); + } + + saveNow() { + // Persists scheduled saves immediately + this.save(this.pendingState); + this.clearPending(); + } + + clearPending() { + // Cancels any scheduled saves + if (this.timeout) { + clearTimeout(this.timeout); + delete this.timeout; + delete this.pendingState; + } + } + + canUndo() { + return this.cursor > 0; + } + + undo() { + this.clearPending(); + if (!this.canUndo()) { + return undefined; + } + this.cursor -= 1; + return this.history[this.cursor]; + } + + canRedo() { + return this.cursor >= 0 && this.cursor < this.history.length - 1; + } + + redo() { + this.clearPending(); + if (!this.canRedo()) { + return undefined; + } + this.cursor += 1; + return this.history[this.cursor]; + } +} diff --git a/app/assets/javascripts/pages/projects/clusters/show/index.js b/app/assets/javascripts/pages/projects/clusters/show/index.js index 8001d2dd1da..f091c01fc98 100644 --- a/app/assets/javascripts/pages/projects/clusters/show/index.js +++ b/app/assets/javascripts/pages/projects/clusters/show/index.js @@ -1,5 +1,7 @@ import ClustersBundle from '~/clusters/clusters_bundle'; +import initGkeNamespace from '~/projects/gke_cluster_namespace'; document.addEventListener('DOMContentLoaded', () => { new ClustersBundle(); // eslint-disable-line no-new + initGkeNamespace(); }); diff --git a/app/assets/javascripts/pages/projects/index.js b/app/assets/javascripts/pages/projects/index.js index d4bd02c14e9..55c377ebec0 100644 --- a/app/assets/javascripts/pages/projects/index.js +++ b/app/assets/javascripts/pages/projects/index.js @@ -1,4 +1,5 @@ import initGkeDropdowns from '~/projects/gke_cluster_dropdowns'; +import initGkeNamespace from '~/projects/gke_cluster_namespace'; import PersistentUserCallout from '../../persistent_user_callout'; import Project from './project'; import ShortcutsNavigation from '../../behaviors/shortcuts/shortcuts_navigation'; @@ -16,6 +17,7 @@ document.addEventListener('DOMContentLoaded', () => { PersistentUserCallout.factory(callout); initGkeDropdowns(); + initGkeNamespace(); } new Project(); // eslint-disable-line no-new diff --git a/app/assets/javascripts/projects/gke_cluster_namespace/index.js b/app/assets/javascripts/projects/gke_cluster_namespace/index.js new file mode 100644 index 00000000000..288740203ad --- /dev/null +++ b/app/assets/javascripts/projects/gke_cluster_namespace/index.js @@ -0,0 +1,35 @@ +/** + * Disables & hides the namespace inputs when the gitlab-managed checkbox is checked/unchecked. + */ + +const setDisabled = (el, isDisabled) => { + if (isDisabled) { + el.classList.add('hidden'); + el.querySelector('input').setAttribute('disabled', true); + } else { + el.classList.remove('hidden'); + el.querySelector('input').removeAttribute('disabled'); + } +}; + +const setState = glManagedCheckbox => { + const glManaged = document.querySelector('.js-namespace-prefixed'); + const selfManaged = document.querySelector('.js-namespace'); + + if (glManagedCheckbox.checked) { + setDisabled(glManaged, false); + setDisabled(selfManaged, true); + } else { + setDisabled(glManaged, true); + setDisabled(selfManaged, false); + } +}; + +const initGkeNamespace = () => { + const glManagedCheckbox = document.querySelector('.js-gl-managed'); + + setState(glManagedCheckbox); // this is needed in order to set the initial state + glManagedCheckbox.addEventListener('change', () => setState(glManagedCheckbox)); +}; + +export default initGkeNamespace; diff --git a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue index 8ce5b615795..21c44b59520 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue @@ -1,5 +1,6 @@ <script> import { GlLink } from '@gitlab/ui'; +import { s__, sprintf } from '~/locale'; export default { components: { @@ -22,8 +23,28 @@ export default { }, }, computed: { - hasQuickActionsDocsPath() { - return this.quickActionsDocsPath !== ''; + toolbarHelpHtml() { + const mdLinkStart = `<a href="${this.markdownDocsPath}" target="_blank" rel="noopener noreferrer" tabindex="-1">`; + const actionsLinkStart = `<a href="${this.quickActionsDocsPath}" target="_blank" rel="noopener noreferrer" tabindex="-1">`; + const linkEnd = '</a>'; + + if (this.markdownDocsPath && !this.quickActionsDocsPath) { + return sprintf( + s__('Editor|%{mdLinkStart}Markdown is supported%{mdLinkEnd}'), + { mdLinkStart, mdLinkEnd: linkEnd }, + false, + ); + } else if (this.markdownDocsPath && this.quickActionsDocsPath) { + return sprintf( + s__( + 'Editor|%{mdLinkStart}Markdown%{mdLinkEnd} and %{actionsLinkStart}quick actions%{actionsLinkEnd} are supported', + ), + { mdLinkStart, mdLinkEnd: linkEnd, actionsLinkStart, actionsLinkEnd: linkEnd }, + false, + ); + } + + return null; }, }, }; @@ -32,21 +53,7 @@ export default { <template> <div class="comment-toolbar clearfix"> <div class="toolbar-text"> - <template v-if="!hasQuickActionsDocsPath && markdownDocsPath"> - <gl-link :href="markdownDocsPath" target="_blank" tabindex="-1">{{ - __('Markdown is supported') - }}</gl-link> - </template> - <template v-if="hasQuickActionsDocsPath && markdownDocsPath"> - <gl-link :href="markdownDocsPath" target="_blank" tabindex="-1">{{ - __('Markdown') - }}</gl-link> - and - <gl-link :href="quickActionsDocsPath" target="_blank" tabindex="-1">{{ - __('quick actions') - }}</gl-link> - are supported - </template> + <span v-html="toolbarHelpHtml"></span> </div> <span v-if="canAttachFile" class="uploading-container"> <span class="uploading-progress-container hide"> diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 61ab0476c42..6b44834cc52 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -441,6 +441,7 @@ img.emoji { .mw-460 { max-width: 460px; } .mw-6em { max-width: 6em; } .mw-70p { max-width: 70%; } +.mw-90p { max-width: 90%; } .min-height-0 { min-height: 0; } diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss index 460d9ea9526..ecd32dcd0ce 100644 --- a/app/assets/stylesheets/framework/lists.scss +++ b/app/assets/stylesheets/framework/lists.scss @@ -285,3 +285,19 @@ ul.indent-list { max-width: 350px; } } + +.horizontal-list { + padding-left: 0; + list-style: none; + + > li { + float: left; + } + + &.list-items-separated { + > li:not(:last-child)::after { + content: '\00b7'; + margin: 0 $gl-padding-4; + } + } +} diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 0c80a276fce..1d55a073f3b 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -421,7 +421,7 @@ class ApplicationController < ActionController::Base end def manifest_import_enabled? - Group.supports_nested_objects? && Gitlab::CurrentSettings.import_sources.include?('manifest') + Gitlab::CurrentSettings.import_sources.include?('manifest') end def phabricator_import_enabled? diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb index 4926062f9ca..8c8f0b3a22e 100644 --- a/app/controllers/concerns/authenticates_with_two_factor.rb +++ b/app/controllers/concerns/authenticates_with_two_factor.rb @@ -55,7 +55,7 @@ module AuthenticatesWithTwoFactor remember_me(user) if user_params[:remember_me] == '1' user.save! - sign_in(user, message: :two_factor_authenticated) + sign_in(user, message: :two_factor_authenticated, event: :authentication) else user.increment_failed_attempts! Gitlab::AppLogger.info("Failed Login: user=#{user.username} ip=#{request.remote_ip} method=OTP") @@ -72,7 +72,7 @@ module AuthenticatesWithTwoFactor session.delete(:challenge) remember_me(user) if user_params[:remember_me] == '1' - sign_in(user, message: :two_factor_authenticated) + sign_in(user, message: :two_factor_authenticated, event: :authentication) else user.increment_failed_attempts! Gitlab::AppLogger.info("Failed Login: user=#{user.username} ip=#{request.remote_ip} method=U2F") diff --git a/app/controllers/concerns/group_tree.rb b/app/controllers/concerns/group_tree.rb index e9a7d6a3152..d076c62c707 100644 --- a/app/controllers/concerns/group_tree.rb +++ b/app/controllers/concerns/group_tree.rb @@ -32,18 +32,14 @@ module GroupTree def filtered_groups_with_ancestors(groups) filtered_groups = groups.search(params[:filter]).page(params[:page]) - if Group.supports_nested_objects? - # We find the ancestors by ID of the search results here. - # Otherwise the ancestors would also have filters applied, - # which would cause them not to be preloaded. - # - # Pagination needs to be applied before loading the ancestors to - # make sure ancestors are not cut off by pagination. - Gitlab::ObjectHierarchy.new(Group.where(id: filtered_groups.select(:id))) - .base_and_ancestors - else - filtered_groups - end + # We find the ancestors by ID of the search results here. + # Otherwise the ancestors would also have filters applied, + # which would cause them not to be preloaded. + # + # Pagination needs to be applied before loading the ancestors to + # make sure ancestors are not cut off by pagination. + Gitlab::ObjectHierarchy.new(Group.where(id: filtered_groups.select(:id))) + .base_and_ancestors end # rubocop: enable CodeReuse/ActiveRecord end diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 2a8dd997d04..b1efa767154 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -139,7 +139,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController if user.two_factor_enabled? && !auth_user.bypass_two_factor? prompt_for_two_factor(user) else - sign_in_and_redirect(user) + sign_in_and_redirect(user, event: :authentication) end else fail_login(user) diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index 2ef18d900f2..da8a371acaa 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -92,7 +92,10 @@ class Projects::ArtifactsController < Projects::ApplicationController def build_from_ref return unless @ref_name - project.latest_successful_build_for(params[:job], @ref_name) + commit = project.commit(@ref_name) + return unless commit + + project.latest_successful_build_for_sha(params[:job], commit.id) end def artifacts_file diff --git a/app/controllers/projects/build_artifacts_controller.rb b/app/controllers/projects/build_artifacts_controller.rb index 4274c356227..99f4524eec5 100644 --- a/app/controllers/projects/build_artifacts_controller.rb +++ b/app/controllers/projects/build_artifacts_controller.rb @@ -51,6 +51,6 @@ class Projects::BuildArtifactsController < Projects::ApplicationController def job_from_ref return unless @ref_name - project.latest_successful_build_for(params[:job], @ref_name) + project.latest_successful_build_for_ref(params[:job], @ref_name) end end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 7604b31467a..1880bead3ee 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -26,6 +26,17 @@ class SessionsController < Devise::SessionsController after_action :log_failed_login, if: -> { action_name == 'new' && failed_login? } helper_method :captcha_enabled? + # protect_from_forgery is already prepended in ApplicationController but + # authenticate_with_two_factor which signs in the user is prepended before + # that here. + # We need to make sure CSRF token is verified before authenticating the user + # because Devise.clean_up_csrf_token_on_authentication is set to true by + # default to avoid CSRF token fixation attacks. Authenticating the user first + # would cause the CSRF token to be cleared and then + # RequestForgeryProtection#verify_authenticity_token would fail because of + # token mismatch. + protect_from_forgery with: :exception, prepend: true + CAPTCHA_HEADER = 'X-GitLab-Show-Login-Captcha'.freeze def new diff --git a/app/finders/group_descendants_finder.rb b/app/finders/group_descendants_finder.rb index ec340f38450..4e489a9c930 100644 --- a/app/finders/group_descendants_finder.rb +++ b/app/finders/group_descendants_finder.rb @@ -132,8 +132,6 @@ class GroupDescendantsFinder end def subgroups - return Group.none unless Group.supports_nested_objects? - # When filtering subgroups, we want to find all matches within the tree of # descendants to show to the user groups = if params[:filter] diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb index 530aecc2bf9..66b41919914 100644 --- a/app/graphql/types/group_type.rb +++ b/app/graphql/types/group_type.rb @@ -14,10 +14,8 @@ module Types group.avatar_url(only_path: false) end - if ::Group.supports_nested_objects? - field :parent, GroupType, - null: true, - resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, obj.parent_id).find } - end + field :parent, GroupType, + null: true, + resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Group, obj.parent_id).find } end end diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index 5aed7e313e6..160f9ac4793 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -127,10 +127,6 @@ module GroupsHelper groups.to_json end - def supports_nested_groups? - Group.supports_nested_objects? - end - private def get_group_sidebar_links diff --git a/app/helpers/submodule_helper.rb b/app/helpers/submodule_helper.rb index 9a281065b90..e683e2959d1 100644 --- a/app/helpers/submodule_helper.rb +++ b/app/helpers/submodule_helper.rb @@ -13,6 +13,8 @@ module SubmoduleHelper end def submodule_links_for_url(submodule_item_id, url, repository) + return [nil, nil] unless url + if url == '.' || url == './' url = File.join(Gitlab.config.gitlab.url, repository.project.full_path) end diff --git a/app/mailers/emails/members.rb b/app/mailers/emails/members.rb index 2bfa59774d7..76fa7236ab1 100644 --- a/app/mailers/emails/members.rb +++ b/app/mailers/emails/members.rb @@ -9,11 +9,11 @@ module Emails helper_method :member_source, :member end - def member_access_requested_email(member_source_type, member_id, recipient_notification_email) + def member_access_requested_email(member_source_type, member_id, recipient_id) @member_source_type = member_source_type @member_id = member_id - mail(to: recipient_notification_email, + mail(to: recipient(recipient_id, notification_group), subject: subject("Request to join the #{member_source.human_name} #{member_source.model_name.singular}")) end @@ -21,16 +21,15 @@ module Emails @member_source_type = member_source_type @member_id = member_id - mail(to: member.user.notification_email, + mail(to: recipient(member.user, notification_group), subject: subject("Access to the #{member_source.human_name} #{member_source.model_name.singular} was granted")) end def member_access_denied_email(member_source_type, source_id, user_id) @member_source_type = member_source_type @member_source = member_source_class.find(source_id) - requester = User.find(user_id) - mail(to: requester.notification_email, + mail(to: recipient(user_id, notification_group), subject: subject("Access to the #{member_source.human_name} #{member_source.model_name.singular} was denied")) end @@ -48,7 +47,7 @@ module Emails @member_id = member_id return unless member.created_by - mail(to: member.created_by.notification_email, + mail(to: recipient(member.created_by, notification_group), subject: subject('Invitation accepted')) end @@ -59,7 +58,7 @@ module Emails @member_source = member_source_class.find(source_id) @invite_email = invite_email - mail(to: recipient(created_by_id, member_source_type == 'Project' ? @member_source.group : @member_source), + mail(to: recipient(created_by_id, notification_group), subject: subject('Invitation declined')) end @@ -71,6 +70,10 @@ module Emails @member_source ||= member.source end + def notification_group + @member_source_type.casecmp?('project') ? member_source.group : member_source + end + private def member_source_class diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index 8ef20a03541..5d292094a05 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -71,14 +71,18 @@ class Notify < BaseMailer address.format end - # Look up a User by their ID and return their email address + # Look up a User's notification email for a particular context. + # Can look up by their ID or can accept a User object. # - # recipient_id - User ID + # recipient - User object OR a User ID # notification_group - The parent group of the notification # # Returns a String containing the User's email address. - def recipient(recipient_id, notification_group = nil) - User.find(recipient_id).notification_email_for(notification_group) + def recipient(recipient, notification_group = nil) + user = recipient if recipient.is_a?(User) + user ||= User.find(recipient) + + user.notification_email_for(notification_group) end # Formats arguments into a String suitable for use as an email subject diff --git a/app/mailers/previews/notify_preview.rb b/app/mailers/previews/notify_preview.rb index 80e0a17c312..b3fab930922 100644 --- a/app/mailers/previews/notify_preview.rb +++ b/app/mailers/previews/notify_preview.rb @@ -105,11 +105,11 @@ class NotifyPreview < ActionMailer::Preview end def member_access_granted_email - Notify.member_access_granted_email('project', user.id).message + Notify.member_access_granted_email(member.source_type, member.id).message end def member_access_requested_email - Notify.member_access_requested_email('group', user.id, 'some@example.com').message + Notify.member_access_requested_email('group', user.id, user.id).message end def member_invite_accepted_email @@ -183,6 +183,10 @@ class NotifyPreview < ActionMailer::Preview @user ||= User.last end + def member + @member ||= Member.last + end + def create_note(params) Notes::CreateService.new(project, user, params).execute end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index c2eb51ba100..1c76f401690 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -229,10 +229,12 @@ module Ci # # ref - The name (or names) of the branch(es)/tag(s) to limit the list of # pipelines to. + # sha - The commit SHA (or mutliple SHAs) to limit the list of pipelines to. # limit - This limits a backlog search, default to 100. - def self.newest_first(ref: nil, limit: 100) + def self.newest_first(ref: nil, sha: nil, limit: 100) relation = order(id: :desc) relation = relation.where(ref: ref) if ref + relation = relation.where(sha: sha) if sha if limit ids = relation.limit(limit).select(:id) @@ -246,10 +248,14 @@ module Ci newest_first(ref: ref).pluck(:status).first end - def self.latest_successful_for(ref) + def self.latest_successful_for_ref(ref) newest_first(ref: ref).success.take end + def self.latest_successful_for_sha(sha) + newest_first(sha: sha).success.take + end + def self.latest_successful_for_refs(refs) relation = newest_first(ref: refs).success diff --git a/app/models/concerns/descendant.rb b/app/models/concerns/descendant.rb deleted file mode 100644 index 4c436522122..00000000000 --- a/app/models/concerns/descendant.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -module Descendant - extend ActiveSupport::Concern - - class_methods do - def supports_nested_objects? - Gitlab::Database.postgresql? - end - end -end diff --git a/app/models/concerns/protected_ref.rb b/app/models/concerns/protected_ref.rb index af387c99f3d..0648b4a78e1 100644 --- a/app/models/concerns/protected_ref.rb +++ b/app/models/concerns/protected_ref.rb @@ -47,7 +47,7 @@ module ProtectedRef def access_levels_for_ref(ref, action:, protected_refs: nil) self.matching(ref, protected_refs: protected_refs) - .map(&:"#{action}_access_levels").flatten + .flat_map(&:"#{action}_access_levels") end # Returns all protected refs that match the given ref name. diff --git a/app/models/deployment_metrics.rb b/app/models/deployment_metrics.rb index cfe762ca25e..2056c8bc59c 100644 --- a/app/models/deployment_metrics.rb +++ b/app/models/deployment_metrics.rb @@ -44,18 +44,7 @@ class DeploymentMetrics end end - # TODO remove fallback case to deployment_platform_cluster. - # Otherwise we will continue to pay the performance penalty described in - # https://gitlab.com/gitlab-org/gitlab-ce/issues/63475 - # - # Removal issue: https://gitlab.com/gitlab-org/gitlab-ce/issues/64105 def cluster_prometheus - cluster_with_fallback = cluster || deployment_platform_cluster - - cluster_with_fallback.application_prometheus if cluster_with_fallback&.application_prometheus_available? - end - - def deployment_platform_cluster - deployment.environment.deployment_platform&.cluster + cluster.application_prometheus if cluster&.application_prometheus_available? end end diff --git a/app/models/group.rb b/app/models/group.rb index 26ce2957e9b..74eb556b1b5 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -10,7 +10,6 @@ class Group < Namespace include Referable include SelectForProjectAuthorization include LoadedInGroupList - include Descendant include GroupDescendant include TokenAuthenticatable include WithUploads @@ -388,7 +387,7 @@ class Group < Namespace variables = Ci::GroupVariable.where(group: list_of_ids) variables = variables.unprotected unless project.protected_for?(ref) variables = variables.group_by(&:group_id) - list_of_ids.reverse.map { |group| variables[group.id] }.compact.flatten + list_of_ids.reverse.flat_map { |group| variables[group.id] }.compact end def group_member(user) diff --git a/app/models/namespace.rb b/app/models/namespace.rb index b8d7348268a..058350b16ce 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -332,8 +332,6 @@ class Namespace < ApplicationRecord end def force_share_with_group_lock_on_descendants - return unless Group.supports_nested_objects? - # We can't use `descendants.update_all` since Rails will throw away the WITH # RECURSIVE statement. We also can't use WHERE EXISTS since we can't use # different table aliases, hence we're just using WHERE IN. Since we have a diff --git a/app/models/project.rb b/app/models/project.rb index 0020e423628..cca7da8c49a 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -719,16 +719,27 @@ class Project < ApplicationRecord repository.commits_by(oids: oids) end - # ref can't be HEAD, can only be branch/tag name or SHA - def latest_successful_build_for(job_name, ref = default_branch) - latest_pipeline = ci_pipelines.latest_successful_for(ref) + # ref can't be HEAD, can only be branch/tag name + def latest_successful_build_for_ref(job_name, ref = default_branch) + return unless ref + + latest_pipeline = ci_pipelines.latest_successful_for_ref(ref) + return unless latest_pipeline + + latest_pipeline.builds.latest.with_artifacts_archive.find_by(name: job_name) + end + + def latest_successful_build_for_sha(job_name, sha) + return unless sha + + latest_pipeline = ci_pipelines.latest_successful_for_sha(sha) return unless latest_pipeline latest_pipeline.builds.latest.with_artifacts_archive.find_by(name: job_name) end - def latest_successful_build_for!(job_name, ref = default_branch) - latest_successful_build_for(job_name, ref) || raise(ActiveRecord::RecordNotFound.new("Couldn't find job #{job_name}")) + def latest_successful_build_for_ref!(job_name, ref = default_branch) + latest_successful_build_for_ref(job_name, ref) || raise(ActiveRecord::RecordNotFound.new("Couldn't find job #{job_name}")) end def merge_base_commit(first_commit_id, second_commit_id) @@ -1503,12 +1514,12 @@ class Project < ApplicationRecord end @latest_successful_pipeline_for_default_branch = - ci_pipelines.latest_successful_for(default_branch) + ci_pipelines.latest_successful_for_ref(default_branch) end def latest_successful_pipeline_for(ref = nil) if ref && ref != default_branch - ci_pipelines.latest_successful_for(ref) + ci_pipelines.latest_successful_for_ref(ref) else latest_successful_pipeline_for_default_branch end diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index 0add8bfad31..84b1873c05d 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -16,8 +16,6 @@ class GroupPolicy < BasePolicy condition(:maintainer) { access_level >= GroupMember::MAINTAINER } condition(:reporter) { access_level >= GroupMember::REPORTER } - condition(:nested_groups_supported, scope: :global) { Group.supports_nested_objects? } - condition(:has_parent, scope: :subject) { @subject.has_parent? } condition(:share_with_group_locked, scope: :subject) { @subject.share_with_group_lock? } condition(:parent_share_with_group_locked, scope: :subject) { @subject.parent&.share_with_group_lock? } @@ -108,8 +106,8 @@ class GroupPolicy < BasePolicy enable :read_nested_project_resources end - rule { owner & nested_groups_supported }.enable :create_subgroup - rule { maintainer & maintainer_can_create_group & nested_groups_supported }.enable :create_subgroup + rule { owner }.enable :create_subgroup + rule { maintainer & maintainer_can_create_group }.enable :create_subgroup rule { public_group | logged_in_viewable }.enable :view_globally diff --git a/app/serializers/stage_entity.rb b/app/serializers/stage_entity.rb index 029dd3d0684..0b0454c5282 100644 --- a/app/serializers/stage_entity.rb +++ b/app/serializers/stage_entity.rb @@ -59,14 +59,14 @@ class StageEntity < Grape::Entity end def latest_statuses - HasStatus::ORDERED_STATUSES.map do |ordered_status| + HasStatus::ORDERED_STATUSES.flat_map do |ordered_status| grouped_statuses.fetch(ordered_status, []) - end.flatten + end end def retried_statuses - HasStatus::ORDERED_STATUSES.map do |ordered_status| + HasStatus::ORDERED_STATUSES.flat_map do |ordered_status| grouped_retried_statuses.fetch(ordered_status, []) - end.flatten + end end end diff --git a/app/services/ci/process_pipeline_service.rb b/app/services/ci/process_pipeline_service.rb index aaf56048b5c..207cc5017d0 100644 --- a/app/services/ci/process_pipeline_service.rb +++ b/app/services/ci/process_pipeline_service.rb @@ -10,13 +10,13 @@ module Ci update_retried new_builds = - stage_indexes_of_created_processables.map do |index| + stage_indexes_of_created_processables.flat_map do |index| process_stage(index) end @pipeline.update_status - new_builds.flatten.any? + new_builds.any? end private diff --git a/app/services/groups/nested_create_service.rb b/app/services/groups/nested_create_service.rb index 01bd685712b..a51ac9aa593 100644 --- a/app/services/groups/nested_create_service.rb +++ b/app/services/groups/nested_create_service.rb @@ -18,10 +18,6 @@ module Groups return namespace end - if group_path.include?('/') && !Group.supports_nested_objects? - raise 'Nested groups are not supported on MySQL' - end - create_group_path end diff --git a/app/services/groups/transfer_service.rb b/app/services/groups/transfer_service.rb index 98e7c311572..fe7e07ef9f0 100644 --- a/app/services/groups/transfer_service.rb +++ b/app/services/groups/transfer_service.rb @@ -43,7 +43,6 @@ module Groups def ensure_allowed_transfer raise_transfer_error(:group_is_already_root) if group_is_already_root? - raise_transfer_error(:database_not_supported) unless Group.supports_nested_objects? raise_transfer_error(:same_parent_as_current) if same_parent? raise_transfer_error(:invalid_policies) unless valid_policies? raise_transfer_error(:namespace_with_same_path) if namespace_with_same_path? diff --git a/app/services/members/destroy_service.rb b/app/services/members/destroy_service.rb index c8d5e563cd8..0164760920f 100644 --- a/app/services/members/destroy_service.rb +++ b/app/services/members/destroy_service.rb @@ -31,7 +31,7 @@ module Members return unless member.is_a?(GroupMember) && member.user && member.group delete_project_members(member) - delete_subgroup_members(member) if Group.supports_nested_objects? + delete_subgroup_members(member) end def delete_project_members(member) diff --git a/app/services/merge_requests/push_options_handler_service.rb b/app/services/merge_requests/push_options_handler_service.rb index 6d70b5106c7..b210004e6e1 100644 --- a/app/services/merge_requests/push_options_handler_service.rb +++ b/app/services/merge_requests/push_options_handler_service.rb @@ -118,7 +118,14 @@ module MergeRequests end def base_params - params = {} + params = { + title: push_options[:title], + description: push_options[:description], + target_branch: push_options[:target], + force_remove_source_branch: push_options[:remove_source_branch] + } + + params.compact! if push_options.key?(:merge_when_pipeline_succeeds) params.merge!( @@ -127,14 +134,6 @@ module MergeRequests ) end - if push_options.key?(:remove_source_branch) - params[:force_remove_source_branch] = push_options[:remove_source_branch] - end - - if push_options.key?(:target) - params[:target_branch] = push_options[:target] - end - params end diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index a55771ed538..21fab22e0d4 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -595,7 +595,7 @@ class NotificationService end def deliver_access_request_email(recipient, member) - mailer.member_access_requested_email(member.real_source_type, member.id, recipient.user.notification_email).deliver_later + mailer.member_access_requested_email(member.real_source_type, member.id, recipient.user.id).deliver_later end def fallback_to_group_owners_maintainers?(recipients, member) diff --git a/app/services/users/refresh_authorized_projects_service.rb b/app/services/users/refresh_authorized_projects_service.rb index 4a26d2be2af..ae67b4f5256 100644 --- a/app/services/users/refresh_authorized_projects_service.rb +++ b/app/services/users/refresh_authorized_projects_service.rb @@ -102,13 +102,7 @@ module Users end def fresh_authorizations - klass = if Group.supports_nested_objects? - Gitlab::ProjectAuthorizations::WithNestedGroups - else - Gitlab::ProjectAuthorizations::WithoutNestedGroups - end - - klass.new(user).calculate + Gitlab::ProjectAuthorizations.new(user).calculate end end end diff --git a/app/views/clusters/clusters/_namespace.html.haml b/app/views/clusters/clusters/_namespace.html.haml new file mode 100644 index 00000000000..0c64819ad62 --- /dev/null +++ b/app/views/clusters/clusters/_namespace.html.haml @@ -0,0 +1,14 @@ +- managed_namespace_help_text = s_('ClusterIntegration|Choose a prefix to be used for your namespaces. Defaults to your project path.') +- non_managed_namespace_help_text = s_('ClusterIntegration|The namespace associated with your project. This will be used for deploy boards, pod logs, and Web terminals.') +- managed_namespace_help_link = link_to _('More information'), help_page_path('user/project/clusters/index.md', + anchor: 'gitlab-managed-clusters'), target: '_blank' + +.js-namespace-prefixed + = platform_field.text_field :namespace, + label: s_('ClusterIntegration|Project namespace prefix (optional, unique)'), label_class: 'label-bold', + help: '%{help_text} %{help_link}'.html_safe % { help_text: managed_namespace_help_text, help_link: managed_namespace_help_link } +.js-namespace.hidden + = platform_field.text_field :namespace, + label: s_('ClusterIntegration|Project namespace (optional, unique)'), label_class: 'label-bold', + help: '%{help_text}'.html_safe % { help_text: non_managed_namespace_help_text }, + disabled: true diff --git a/app/views/clusters/clusters/user/_form.html.haml b/app/views/clusters/clusters/user/_form.html.haml index f2fc5ac93fb..1d212553c3b 100644 --- a/app/views/clusters/clusters/user/_form.html.haml +++ b/app/views/clusters/clusters/user/_form.html.haml @@ -34,10 +34,6 @@ autocomplete: 'off', label_class: 'label-bold', help: '%{help_text} %{help_link}'.html_safe % { help_text: token_help_text, help_link: more_info_link } - - if @user_cluster.allow_user_defined_namespace? - = platform_kubernetes_field.text_field :namespace, - label: s_('ClusterIntegration|Project namespace (optional, unique)'), label_class: 'label-bold' - = platform_kubernetes_field.form_group :authorization_type, { help: '%{help_text} %{help_link}'.html_safe % { help_text: rbac_help_text, help_link: rbac_help_link } } do = platform_kubernetes_field.check_box :authorization_type, @@ -46,10 +42,15 @@ .form-group = field.check_box :managed, { label: s_('ClusterIntegration|GitLab-managed cluster'), + class: 'js-gl-managed', label_class: 'label-bold' } .form-text.text-muted = s_('ClusterIntegration|Allow GitLab to manage namespace and service accounts for this cluster.') = link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'gitlab-managed-clusters'), target: '_blank' + = field.fields_for :platform_kubernetes, @user_cluster.platform_kubernetes do |platform_kubernetes_field| + - if @user_cluster.allow_user_defined_namespace? + = render('clusters/clusters/namespace', platform_field: platform_kubernetes_field) + .form-group = field.submit s_('ClusterIntegration|Add Kubernetes cluster'), class: 'btn btn-success' diff --git a/app/views/clusters/platforms/kubernetes/_form.html.haml b/app/views/clusters/platforms/kubernetes/_form.html.haml index f2e44462226..e50c573bd90 100644 --- a/app/views/clusters/platforms/kubernetes/_form.html.haml +++ b/app/views/clusters/platforms/kubernetes/_form.html.haml @@ -36,10 +36,6 @@ label: s_('ClusterIntegration|Service Token'), label_class: 'label-bold', input_group_class: 'gl-field-error-anchor', append: show_token_btn + copy_token_btn - - if cluster.allow_user_defined_namespace? - = platform_field.text_field :namespace, label: s_('ClusterIntegration|Project namespace (optional, unique)'), - label_class: 'label-bold' - = platform_field.form_group :authorization_type do = platform_field.check_box :authorization_type, { disabled: true, label: s_('ClusterIntegration|RBAC-enabled cluster'), label_class: 'label-bold', inline: true }, 'rbac', 'abac' @@ -49,10 +45,14 @@ .form-group = field.check_box :managed, { label: s_('ClusterIntegration|GitLab-managed cluster'), + class: 'js-gl-managed', label_class: 'label-bold' } .form-text.text-muted = s_('ClusterIntegration|Allow GitLab to manage namespace and service accounts for this cluster.') = link_to _('More information'), help_page_path('user/project/clusters/index.md', anchor: 'gitlab-managed-clusters'), target: '_blank' + - if cluster.allow_user_defined_namespace? + = render('clusters/clusters/namespace', platform_field: platform_field) + .form-group = field.submit s_('ClusterIntegration|Save changes'), class: 'btn btn-success' diff --git a/app/views/groups/settings/_advanced.html.haml b/app/views/groups/settings/_advanced.html.haml index 5d211d0e186..d1eb6478997 100644 --- a/app/views/groups/settings/_advanced.html.haml +++ b/app/views/groups/settings/_advanced.html.haml @@ -23,20 +23,19 @@ = f.submit 'Change group path', class: 'btn btn-warning' -- if supports_nested_groups? - .sub-section - %h4.warning-title Transfer group - = form_for @group, url: transfer_group_path(@group), method: :put do |f| - .form-group - = dropdown_tag('Select parent group', options: { toggle_class: 'js-groups-dropdown', title: 'Parent Group', filter: true, dropdown_class: 'dropdown-open-top dropdown-group-transfer', placeholder: 'Search groups', data: { data: parent_group_options(@group) } }) - = hidden_field_tag 'new_parent_group_id' +.sub-section + %h4.warning-title Transfer group + = form_for @group, url: transfer_group_path(@group), method: :put do |f| + .form-group + = dropdown_tag('Select parent group', options: { toggle_class: 'js-groups-dropdown', title: 'Parent Group', filter: true, dropdown_class: 'dropdown-open-top dropdown-group-transfer', placeholder: 'Search groups', data: { data: parent_group_options(@group) } }) + = hidden_field_tag 'new_parent_group_id' - %ul - %li Be careful. Changing a group's parent can have unintended #{link_to 'side effects', 'https://docs.gitlab.com/ce/user/project/index.html#redirects-when-changing-repository-paths', target: 'blank'}. - %li You can only transfer the group to a group you manage. - %li You will need to update your local repositories to point to the new location. - %li If the parent group's visibility is lower than the group current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility. - = f.submit 'Transfer group', class: 'btn btn-warning' + %ul + %li Be careful. Changing a group's parent can have unintended #{link_to 'side effects', 'https://docs.gitlab.com/ce/user/project/index.html#redirects-when-changing-repository-paths', target: 'blank'}. + %li You can only transfer the group to a group you manage. + %li You will need to update your local repositories to point to the new location. + %li If the parent group's visibility is lower than the group current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility. + = f.submit 'Transfer group', class: 'btn btn-warning' .sub-section %h4.danger-title= _('Remove group') diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml index d1634eb62c0..92dd558885a 100644 --- a/app/views/layouts/nav/sidebar/_project.html.haml +++ b/app/views/layouts/nav/sidebar/_project.html.haml @@ -9,7 +9,7 @@ = @project.name %ul.sidebar-top-level-items = nav_link(path: sidebar_projects_paths, html_options: { class: 'home' }) do - = link_to project_path(@project), class: 'shortcuts-project qa-link-project' do + = link_to project_path(@project), class: 'shortcuts-project', data: { qa_selector: 'project_link' } do .nav-icon-container = sprite_icon('home') %span.nav-item-name @@ -26,7 +26,7 @@ %span= _('Details') = nav_link(path: 'projects#activity') do - = link_to activity_project_path(@project), title: _('Activity'), class: 'shortcuts-project-activity qa-activity-link' do + = link_to activity_project_path(@project), title: _('Activity'), class: 'shortcuts-project-activity', data: { qa_selector: 'activity_link' } do %span= _('Activity') - if project_nav_tab?(:releases) @@ -146,7 +146,7 @@ - if project_nav_tab? :merge_requests = nav_link(controller: @project.issues_enabled? ? :merge_requests : [:merge_requests, :labels, :milestones]) do - = link_to project_merge_requests_path(@project), class: 'shortcuts-merge_requests qa-merge-requests-link' do + = link_to project_merge_requests_path(@project), class: 'shortcuts-merge_requests', data: { qa_selector: 'merge_requests_link' } do .nav-icon-container = sprite_icon('git-merge') %span.nav-item-name#js-onboarding-mr-link @@ -277,7 +277,7 @@ - if project_nav_tab? :wiki - wiki_url = project_wiki_path(@project, :home) = nav_link(controller: :wikis) do - = link_to wiki_url, class: 'shortcuts-wiki qa-wiki-link' do + = link_to wiki_url, class: 'shortcuts-wiki', data: { qa_selector: 'wiki_link' } do .nav-icon-container = sprite_icon('book') %span.nav-item-name diff --git a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml index fe74dc122c3..04b77fb987a 100644 --- a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml +++ b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml @@ -8,7 +8,7 @@ .card.auto-devops-card .card-body .form-check - = form.check_box :enabled, class: 'form-check-input js-toggle-extra-settings', checked: auto_devops_enabled + = form.check_box :enabled, class: 'form-check-input js-toggle-extra-settings', checked: auto_devops_enabled, data: { qa_selector: 'enable_autodevops_checkbox' } = form.label :enabled, class: 'form-check-label' do %strong= s_('CICD|Default to Auto DevOps pipeline') - if auto_devops_enabled @@ -42,4 +42,4 @@ = s_('CICD|Automatic deployment to staging, manual deployment to production') = link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'incremental-rollout-to-production'), target: '_blank' - = f.submit _('Save changes'), class: "btn btn-success prepend-top-15" + = f.submit _('Save changes'), class: "btn btn-success prepend-top-15", data: { qa_selector: 'save_changes_button' } diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml index 5e3e1076c2c..293f0a241eb 100644 --- a/app/views/projects/settings/ci_cd/show.html.haml +++ b/app/views/projects/settings/ci_cd/show.html.haml @@ -16,7 +16,7 @@ .settings-content = render 'form' -%section.qa-autodevops-settings.settings#autodevops-settings.no-animate{ class: ('expanded' if expanded) } +%section.settings#autodevops-settings.no-animate{ class: ('expanded' if expanded), data: { qa_selector: 'autodevops_settings_content' } } .settings-header %h4 = s_('CICD|Auto DevOps') @@ -30,7 +30,7 @@ = render_if_exists 'projects/settings/ci_cd/protected_environments', expanded: expanded -%section.qa-runners-settings.settings.no-animate#js-runners-settings{ class: ('expanded' if expanded) } +%section.settings.no-animate#js-runners-settings{ class: ('expanded' if expanded), data: { qa_selector: 'runners_settings_content' } } .settings-header %h4 = _("Runners") @@ -41,7 +41,7 @@ .settings-content = render 'projects/runners/index' -%section.qa-variables-settings.settings.no-animate{ class: ('expanded' if expanded) } +%section.settings.no-animate{ class: ('expanded' if expanded), data: { qa_selector: 'variables_settings_content' } } .settings-header = render 'ci/variables/header', expanded: expanded .settings-content diff --git a/app/views/shared/notes/_hints.html.haml b/app/views/shared/notes/_hints.html.haml index fae7d6526e8..72ede50dd8c 100644 --- a/app/views/shared/notes/_hints.html.haml +++ b/app/views/shared/notes/_hints.html.haml @@ -1,14 +1,13 @@ - supports_quick_actions = local_assigns.fetch(:supports_quick_actions, false) .comment-toolbar.clearfix .toolbar-text - = link_to _('Markdown'), help_page_path('user/markdown'), target: '_blank', tabindex: -1 + - md_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer" tabindex="-1">'.html_safe % { url: help_page_path('user/markdown') } + - actions_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer" tabindex="-1">'.html_safe % { url: help_page_path('user/project/quick_actions') } + - link_end = '</a>'.html_safe - if supports_quick_actions - and - = link_to _('quick actions'), help_page_path('user/project/quick_actions'), target: '_blank', tabindex: -1 - are + = s_('Editor|%{mdLinkStart}Markdown%{mdLinkEnd} and %{actionsLinkStart}quick actions%{actionsLinkEnd} are supported').html_safe % { mdLinkStart: md_link_start, mdLinkEnd: link_end, actionsLinkStart: actions_link_start, actionsLinkEnd: link_end } - else - is - supported + = s_('Editor|%{mdLinkStart}Markdown is supported%{mdLinkEnd}').html_safe % { mdLinkStart: md_link_start, mdLinkEnd: link_end } %span.uploading-container %span.uploading-progress-container.hide diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index c4d1bdad2c4..f40a9cffb29 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -89,4 +89,6 @@ %span.icon-wrapper.pipeline-status = render 'ci/status/icon', status: project.commit.last_pipeline.detailed_status(current_user), type: 'commit', tooltip_placement: 'top', path: pipeline_path .updated-note - %span #{_('Updated')} #{updated_tooltip} + %span + = _('Updated') + = updated_tooltip diff --git a/changelogs/unreleased/59325-units-are-not-shown-on-the-performance-dashboard-2.yml b/changelogs/unreleased/59325-units-are-not-shown-on-the-performance-dashboard-2.yml new file mode 100644 index 00000000000..38cfa0f273e --- /dev/null +++ b/changelogs/unreleased/59325-units-are-not-shown-on-the-performance-dashboard-2.yml @@ -0,0 +1,5 @@ +--- +title: 'fix: updates to include units for the y axis label' +merge_request: 30330 +author: +type: fixed diff --git a/changelogs/unreleased/63568-access-email-notifications-custom-email.yml b/changelogs/unreleased/63568-access-email-notifications-custom-email.yml new file mode 100644 index 00000000000..ece6442d7cf --- /dev/null +++ b/changelogs/unreleased/63568-access-email-notifications-custom-email.yml @@ -0,0 +1,5 @@ +--- +title: Respect group notification email when sending group access notifications +merge_request: 31089 +author: +type: fixed diff --git a/changelogs/unreleased/64257-warden_set_user_fix.yml b/changelogs/unreleased/64257-warden_set_user_fix.yml new file mode 100644 index 00000000000..7b6818876fb --- /dev/null +++ b/changelogs/unreleased/64257-warden_set_user_fix.yml @@ -0,0 +1,5 @@ +--- +title: Ensure Warden triggers after_authentication callback +merge_request: 31138 +author: +type: fixed diff --git a/changelogs/unreleased/64972-fix-unicorn-workers-metric.yml b/changelogs/unreleased/64972-fix-unicorn-workers-metric.yml new file mode 100644 index 00000000000..f80111f0bd9 --- /dev/null +++ b/changelogs/unreleased/64972-fix-unicorn-workers-metric.yml @@ -0,0 +1,5 @@ +--- +title: Fix pid discovery for Unicorn processes in `PidProvider` +merge_request: 31056 +author: +type: fixed diff --git a/changelogs/unreleased/65088-incorrect-message-interpolation-on-project-listing.yml b/changelogs/unreleased/65088-incorrect-message-interpolation-on-project-listing.yml new file mode 100644 index 00000000000..dd74b8443bc --- /dev/null +++ b/changelogs/unreleased/65088-incorrect-message-interpolation-on-project-listing.yml @@ -0,0 +1,5 @@ +--- +title: Fix incorrect use of message interpolation +merge_request: 31121 +author: +type: fixed diff --git a/changelogs/unreleased/delete-designs-v2.yml b/changelogs/unreleased/delete-designs-v2.yml new file mode 100644 index 00000000000..a678e4f93b9 --- /dev/null +++ b/changelogs/unreleased/delete-designs-v2.yml @@ -0,0 +1,4 @@ +--- +title: Adds event enum column to DesignsVersions join table +merge_request: 30745 +type: added diff --git a/changelogs/unreleased/dm-submodule-links-nil.yml b/changelogs/unreleased/dm-submodule-links-nil.yml new file mode 100644 index 00000000000..c09ca41d01d --- /dev/null +++ b/changelogs/unreleased/dm-submodule-links-nil.yml @@ -0,0 +1,5 @@ +--- +title: Fix error rendering submodules in MR diffs when there is no .gitmodules +merge_request: 31162 +author: +type: fixed diff --git a/changelogs/unreleased/extract_auto_deploy_into_base_image.yml b/changelogs/unreleased/extract_auto_deploy_into_base_image.yml new file mode 100644 index 00000000000..ff0d1f3bd71 --- /dev/null +++ b/changelogs/unreleased/extract_auto_deploy_into_base_image.yml @@ -0,0 +1,5 @@ +--- +title: Extract Auto DevOps deploy functions into a base image +merge_request: 30404 +author: +type: changed diff --git a/changelogs/unreleased/je-separate-namespace-fe.yml b/changelogs/unreleased/je-separate-namespace-fe.yml new file mode 100644 index 00000000000..fb6be071eb6 --- /dev/null +++ b/changelogs/unreleased/je-separate-namespace-fe.yml @@ -0,0 +1,5 @@ +--- +title: Update namespace label for GitLab-managed clusters +merge_request: 30935 +author: +type: added diff --git a/changelogs/unreleased/label-descr-push-opts.yml b/changelogs/unreleased/label-descr-push-opts.yml new file mode 100644 index 00000000000..9b01cfdaed2 --- /dev/null +++ b/changelogs/unreleased/label-descr-push-opts.yml @@ -0,0 +1,5 @@ +--- +title: Support setting of merge request title and description using git push options +merge_request: 31068 +author: +type: added diff --git a/changelogs/unreleased/mh-editor-indents.yml b/changelogs/unreleased/mh-editor-indents.yml new file mode 100644 index 00000000000..a282c0f505d --- /dev/null +++ b/changelogs/unreleased/mh-editor-indents.yml @@ -0,0 +1,5 @@ +--- +title: Markdown editors now have indentation shortcuts and auto-indentation +merge_request: 28914 +author: +type: added diff --git a/changelogs/unreleased/remove_deployment_metrics_deployment_platform_fallback.yml b/changelogs/unreleased/remove_deployment_metrics_deployment_platform_fallback.yml new file mode 100644 index 00000000000..d32cbd1d8e0 --- /dev/null +++ b/changelogs/unreleased/remove_deployment_metrics_deployment_platform_fallback.yml @@ -0,0 +1,6 @@ +--- +title: Remove incorrect fallback when determining which cluster to use when retrieving + MR performance metrics +merge_request: 31126 +author: +type: changed diff --git a/config/prometheus/cluster_metrics.yml b/config/prometheus/cluster_metrics.yml index 3df76b0974f..f2a41e4c337 100644 --- a/config/prometheus/cluster_metrics.yml +++ b/config/prometheus/cluster_metrics.yml @@ -2,12 +2,12 @@ priority: 1 metrics: - title: "CPU Usage" - y_label: "CPU" + y_label: "CPU (cores)" required_metrics: ['container_cpu_usage_seconds_total'] weight: 1 queries: - query_range: 'avg(sum(rate(container_cpu_usage_seconds_total{id="/"}[15m])) by (job)) without (job)' - label: Usage + label: Usage (cores) unit: "cores" appearance: line: @@ -15,7 +15,7 @@ area: opacity: 0 - query_range: 'sum(kube_pod_container_resource_requests_cpu_cores{kubernetes_namespace="gitlab-managed-apps"})' - label: Requested + label: Requested (cores) unit: "cores" appearance: line: @@ -23,7 +23,7 @@ area: opacity: 0 - query_range: 'sum(kube_node_status_capacity_cpu_cores{kubernetes_namespace="gitlab-managed-apps"})' - label: Capacity + label: Capacity (cores) unit: "cores" appearance: line: @@ -32,12 +32,12 @@ area: opacity: 0 - title: "Memory usage" - y_label: "Memory" + y_label: "Memory (GiB)" required_metrics: ['container_memory_usage_bytes'] weight: 1 queries: - query_range: 'avg(sum(container_memory_usage_bytes{id="/"}) by (job)) without (job) / 2^30' - label: Usage + label: Usage (GiB) unit: "GiB" appearance: line: @@ -45,7 +45,7 @@ area: opacity: 0 - query_range: 'sum(kube_pod_container_resource_requests_memory_bytes{kubernetes_namespace="gitlab-managed-apps"})/2^30' - label: Requested + label: Requested (GiB) unit: "GiB" appearance: line: @@ -53,7 +53,7 @@ area: opacity: 0 - query_range: 'sum(kube_node_status_capacity_memory_bytes{kubernetes_namespace="gitlab-managed-apps"})/2^30' - label: Capacity + label: Capacity (GiB) unit: "GiB" appearance: line: diff --git a/config/prometheus/common_metrics.yml b/config/prometheus/common_metrics.yml index f9ce5a6f365..32475ef8380 100644 --- a/config/prometheus/common_metrics.yml +++ b/config/prometheus/common_metrics.yml @@ -21,16 +21,16 @@ panel_groups: metrics: - id: response_metrics_nginx_ingress_latency_pod_average query_range: 'avg(nginx_upstream_response_msecs_avg{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"})' - label: Pod average + label: Pod average (ms) unit: ms - title: "HTTP Error Rate" type: "area-chart" - y_label: "HTTP Errors" + y_label: "HTTP Errors (%)" weight: 1 metrics: - id: response_metrics_nginx_ingress_http_error_rate query_range: 'sum(rate(nginx_upstream_responses_total{status_code="5xx", upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) / sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) * 100' - label: 5xx Errors + label: 5xx Errors (%) unit: "%" # NGINX Ingress metrics for post-0.16.0 versions - group: Response metrics (NGINX Ingress) @@ -52,16 +52,16 @@ panel_groups: metrics: - id: response_metrics_nginx_ingress_16_latency_pod_average query_range: 'sum(rate(nginx_ingress_controller_ingress_upstream_latency_seconds_sum{namespace="%{kube_namespace}",ingress=~".*%{ci_environment_slug}.*"}[2m])) / sum(rate(nginx_ingress_controller_ingress_upstream_latency_seconds_count{namespace="%{kube_namespace}",ingress=~".*%{ci_environment_slug}.*"}[2m])) * 1000' - label: Pod average + label: Pod average (ms) unit: ms - title: "HTTP Error Rate" type: "area-chart" - y_label: "HTTP Errors" + y_label: "HTTP Errors (%)" weight: 1 metrics: - id: response_metrics_nginx_ingress_16_http_error_rate query_range: 'sum(rate(nginx_ingress_controller_requests{status=~"5.*",namespace="%{kube_namespace}",ingress=~".*%{ci_environment_slug}.*"}[2m])) / sum(rate(nginx_ingress_controller_requests{namespace="%{kube_namespace}",ingress=~".*%{ci_environment_slug}.*"}[2m])) * 100' - label: 5xx Errors + label: 5xx Errors (%) unit: "%" - group: Response metrics (HA Proxy) priority: 10 @@ -82,7 +82,7 @@ panel_groups: metrics: - id: response_metrics_ha_proxy_http_error_rate query_range: 'sum(rate(haproxy_frontend_http_responses_total{code="5xx",%{environment_filter}}[2m])) / sum(rate(haproxy_frontend_http_responses_total{%{environment_filter}}[2m]))' - label: HTTP Errors + label: HTTP Errors (%) unit: "%" - group: Response metrics (AWS ELB) priority: 10 @@ -94,7 +94,7 @@ panel_groups: metrics: - id: response_metrics_aws_elb_throughput_requests query_range: 'sum(aws_elb_request_count_sum{%{environment_filter}}) / 60' - label: Total + label: Total (req/sec) unit: req / sec - title: "Latency" type: "area-chart" @@ -103,7 +103,7 @@ panel_groups: metrics: - id: response_metrics_aws_elb_latency_average query_range: 'avg(aws_elb_latency_average{%{environment_filter}}) * 1000' - label: Average + label: Average (ms) unit: ms - title: "HTTP Error Rate" type: "area-chart" @@ -112,7 +112,7 @@ panel_groups: metrics: - id: response_metrics_aws_elb_http_error_rate query_range: 'sum(aws_elb_httpcode_backend_5_xx_sum{%{environment_filter}}) / sum(aws_elb_request_count_sum{%{environment_filter}})' - label: HTTP Errors + label: HTTP Errors (%) unit: "%" - group: Response metrics (NGINX) priority: 10 @@ -133,9 +133,9 @@ panel_groups: metrics: - id: response_metrics_nginx_latency query_range: 'avg(nginx_server_requestMsec{%{environment_filter}})' - label: Upstream + label: Upstream (ms) unit: ms - - title: "HTTP Error Rate" + - title: "HTTP Error Rate (Errors / Sec)" type: "area-chart" y_label: "HTTP 500 Errors / Sec" weight: 1 @@ -149,12 +149,12 @@ panel_groups: panels: - title: "Memory Usage (Total)" type: "area-chart" - y_label: "Total Memory Used" + y_label: "Total Memory Used (GB)" weight: 4 metrics: - id: system_metrics_kubernetes_container_memory_total query_range: 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) /1024/1024/1024' - label: Total + label: Total (GB) unit: GB - title: "Core Usage (Total)" type: "area-chart" @@ -163,25 +163,25 @@ panel_groups: metrics: - id: system_metrics_kubernetes_container_cores_total query_range: 'avg(sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}[15m])) by (job)) without (job)' - label: Total + label: Total (cores) unit: "cores" - title: "Memory Usage (Pod average)" type: "area-chart" - y_label: "Memory Used per Pod" + y_label: "Memory Used per Pod (MB)" weight: 2 metrics: - id: system_metrics_kubernetes_container_memory_average query_range: 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-([^c].*|c([^a]|a([^n]|n([^a]|a([^r]|r[^y])))).*|)-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) / count(avg(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-([^c].*|c([^a]|a([^n]|n([^a]|a([^r]|r[^y])))).*|)-(.*)",namespace="%{kube_namespace}"}) without (job)) /1024/1024' - label: Pod average + label: Pod average (MB) unit: MB - title: "Canary: Memory Usage (Pod Average)" type: "area-chart" - y_label: "Memory Used per Pod" + y_label: "Memory Used per Pod (MB)" weight: 2 metrics: - id: system_metrics_kubernetes_container_memory_average_canary query_range: 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-canary-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) / count(avg(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-canary-(.*)",namespace="%{kube_namespace}"}) without (job)) /1024/1024' - label: Pod average + label: Pod average (MB) unit: MB track: canary - title: "Core Usage (Pod Average)" @@ -191,7 +191,7 @@ panel_groups: metrics: - id: system_metrics_kubernetes_container_core_usage query_range: 'avg(sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name=~"^%{ci_environment_slug}-([^c].*|c([^a]|a([^n]|n([^a]|a([^r]|r[^y])))).*|)-(.*)",namespace="%{kube_namespace}"}[15m])) by (job)) without (job) / count(sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name=~"^%{ci_environment_slug}-([^c].*|c([^a]|a([^n]|n([^a]|a([^r]|r[^y])))).*|)-(.*)",namespace="%{kube_namespace}"}[15m])) by (pod_name))' - label: Pod average + label: Pod average (cores) unit: "cores" - title: "Canary: Core Usage (Pod Average)" type: "area-chart" @@ -200,7 +200,7 @@ panel_groups: metrics: - id: system_metrics_kubernetes_container_core_usage_canary query_range: 'avg(sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name=~"^%{ci_environment_slug}-canary-(.*)",namespace="%{kube_namespace}"}[15m])) by (job)) without (job) / count(sum(rate(container_cpu_usage_seconds_total{container_name!="POD",pod_name=~"^%{ci_environment_slug}-canary-(.*)",namespace="%{kube_namespace}"}[15m])) by (pod_name))' - label: Pod average + label: Pod average (cores) unit: "cores" track: canary - title: "Knative function invocations" diff --git a/db/migrate/20190709220014_import_common_metrics_y_axis.rb b/db/migrate/20190709220014_import_common_metrics_y_axis.rb new file mode 100644 index 00000000000..89ecf32ecc1 --- /dev/null +++ b/db/migrate/20190709220014_import_common_metrics_y_axis.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class ImportCommonMetricsYAxis < ActiveRecord::Migration[5.1] + DOWNTIME = false + + def up + ::Gitlab::DatabaseImporters::CommonMetrics::Importer.new.execute + end + + def down + # no-op + end +end diff --git a/db/migrate/20190715140740_add_event_type_to_design_management_designs_versions.rb b/db/migrate/20190715140740_add_event_type_to_design_management_designs_versions.rb new file mode 100644 index 00000000000..81a8b0a3271 --- /dev/null +++ b/db/migrate/20190715140740_add_event_type_to_design_management_designs_versions.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +# This migration sets up a event enum on the DesignsVersions join table +class AddEventTypeToDesignManagementDesignsVersions < ActiveRecord::Migration[5.2] + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + # We disable these cops here because adding this column is safe. The table does not + # have any data in it. + # rubocop: disable Migration/AddIndex + # rubocop: disable Migration/AddColumn + def up + add_column(:design_management_designs_versions, :event, :integer, + limit: 2, + null: false, + default: 0) + add_index(:design_management_designs_versions, :event) + end + + # rubocop: disable Migration/RemoveIndex + def down + remove_index(:design_management_designs_versions, :event) + remove_column(:design_management_designs_versions, :event) + end +end diff --git a/db/migrate/20190725012225_change_outbound_local_requests_whitelist_default.rb b/db/migrate/20190725012225_change_outbound_local_requests_whitelist_default.rb new file mode 100644 index 00000000000..21b00e0b7d9 --- /dev/null +++ b/db/migrate/20190725012225_change_outbound_local_requests_whitelist_default.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +class ChangeOutboundLocalRequestsWhitelistDefault < ActiveRecord::Migration[5.2] + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + class ApplicationSetting < ActiveRecord::Base + self.table_name = 'application_settings' + end + + def up + default_value = [] + + change_column_default(:application_settings, :outbound_local_requests_whitelist, default_value) + + ApplicationSetting + .where(outbound_local_requests_whitelist: nil) + .update(outbound_local_requests_whitelist: default_value) + + change_column_null(:application_settings, :outbound_local_requests_whitelist, false) + end + + def down + change_column_null(:application_settings, :outbound_local_requests_whitelist, true) + + change_column_default(:application_settings, :outbound_local_requests_whitelist, nil) + end +end diff --git a/db/schema.rb b/db/schema.rb index 2ec8fcce8f8..67479937b47 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2019_07_15_142138) do +ActiveRecord::Schema.define(version: 2019_07_25_012225) do # These are extensions that must be enabled in order to support this database enable_extension "pg_trgm" @@ -228,7 +228,7 @@ ActiveRecord::Schema.define(version: 2019_07_15_142138) do t.boolean "lock_memberships_to_ldap", default: false, null: false t.boolean "time_tracking_limit_to_hours", default: false, null: false t.string "grafana_url", default: "/-/grafana", null: false - t.string "outbound_local_requests_whitelist", limit: 255, array: true + t.string "outbound_local_requests_whitelist", limit: 255, default: [], null: false, array: true t.integer "raw_blob_request_limit", default: 300, null: false t.index ["custom_project_templates_group_id"], name: "index_application_settings_on_custom_project_templates_group_id" t.index ["file_template_project_id"], name: "index_application_settings_on_file_template_project_id" @@ -1097,8 +1097,10 @@ ActiveRecord::Schema.define(version: 2019_07_15_142138) do create_table "design_management_designs_versions", id: false, force: :cascade do |t| t.bigint "design_id", null: false t.bigint "version_id", null: false + t.integer "event", limit: 2, default: 0, null: false t.index ["design_id", "version_id"], name: "design_management_designs_versions_uniqueness", unique: true t.index ["design_id"], name: "index_design_management_designs_versions_on_design_id" + t.index ["event"], name: "index_design_management_designs_versions_on_event" t.index ["version_id"], name: "index_design_management_designs_versions_on_version_id" end diff --git a/doc/administration/logs.md b/doc/administration/logs.md index 44a33eb7bcf..31876dd178a 100644 --- a/doc/administration/logs.md +++ b/doc/administration/logs.md @@ -242,7 +242,7 @@ I, [2015-02-13T06:17:00.679433 #9291] INFO -- : Moving existing hooks directory User clone/fetch activity using ssh transport appears in this log as `executing git command <gitaly-upload-pack...`. -## `unicorn\_stderr.log` +## `unicorn_stderr.log` This file lives in `/var/log/gitlab/unicorn/unicorn_stderr.log` for Omnibus GitLab packages or in `/home/git/gitlab/log/unicorn_stderr.log` for diff --git a/doc/ci/README.md b/doc/ci/README.md index 7048ceaac41..6923d07bb1d 100644 --- a/doc/ci/README.md +++ b/doc/ci/README.md @@ -13,6 +13,11 @@ through the [continuous methodologies](introduction/index.md#introduction-to-cic - Continuous Delivery (CD) - Continuous Deployment (CD) +NOTE: **Out-of-the-box management systems can decrease hours spent on maintaining toolchains by 10% or more.** +Watch our +["Mastering continuous software development"](https://about.gitlab.com/webcast/mastering-ci-cd/) +webcast to learn about continuous methods and how GitLab’s built-in CI can help you simplify and scale software development. + ## Overview Continuous Integration works by pushing small code chunks to your @@ -155,6 +160,7 @@ for your CI/CD infrastructure: - [Why we chose GitLab CI for our CI/CD solution](https://about.gitlab.com/2016/10/17/gitlab-ci-oohlala/) - [Building our web-app on GitLab CI](https://about.gitlab.com/2016/07/22/building-our-web-app-on-gitlab-ci/) +- [5 Teams that made the switch to GitLab CI/CD](https://about.gitlab.com/2019/04/25/5-teams-that-made-the-switch-to-gitlab-ci-cd/) See also the [Why CI/CD?](https://docs.google.com/presentation/d/1OGgk2Tcxbpl7DJaIOzCX4Vqg3dlwfELC3u2jEeCBbDk) presentation. diff --git a/doc/ci/introduction/index.md b/doc/ci/introduction/index.md index 6401ff90a0a..366aca3442e 100644 --- a/doc/ci/introduction/index.md +++ b/doc/ci/introduction/index.md @@ -9,6 +9,11 @@ In this document we'll present an overview of the concepts of Continuous Integra Continuous Delivery, and Continuous Deployment, as well as an introduction to GitLab CI/CD. +NOTE: **Out-of-the-box management systems can decrease hours spent on maintaining toolchains by 10% or more.** +Watch our +["Mastering continuous software development"](https://about.gitlab.com/webcast/mastering-ci-cd/) +webcast to learn about continuous methods and how GitLab’s built-in CI can help you simplify and scale software development. + ## Introduction to CI/CD methodologies The continuous methodologies of software development are based on diff --git a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md index f63b17a9e5a..ad07c662965 100644 --- a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md +++ b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/index.md @@ -62,18 +62,32 @@ CAUTION: **Warning:** Make sure your `gitlab-ci.yml` file is [configured properly for pipelines for merge requests](../index.md#configuring-pipelines-for-merge-requests), otherwise pipelines for merged results won't run and your merge requests will be stuck in an unresolved state. -## Merge Trains **(PREMIUM)** +## Troubleshooting -Read the [documentation on Merge Trains](merge_trains/index.md). +### Pipelines for merged results not created even with new change pushed to merge request -<!-- ## Troubleshooting +Can be caused by some disabled feature flags. Please make sure that +the following feature flags are enabled on your GitLab instance: -Include any troubleshooting steps that you can foresee. If you know beforehand what issues -one might have when setting this up, or when something is changed, or on upgrading, it's -important to describe those, too. Think of things that may go wrong and include them here. -This is important to minimize requests for support, and to avoid doc comments with -questions that you know someone might ask. +- `:ci_use_merge_request_ref` +- `:merge_ref_auto_sync` -Each scenario can be a third-level heading, e.g. `### Getting error message X`. -If you have none to add when creating a doc, leave this section in place -but commented out to help encourage others to add to it in the future. --> +To check these feature flag values, please ask administrator to execute the following commands: + +```shell +> sudo gitlab-rails console # Login to Rails console of GitLab instance. +> Feature.enabled?(:ci_use_merge_request_ref) # Check if it's enabled or not. +> Feature.enable(:ci_use_merge_request_ref) # Enable the feature flag. +``` + +## Using Merge Trains **(PREMIUM)** + +By enabling [Pipelines for merged results](#pipelines-for-merged-results-premium), +GitLab will [automatically display](merge_trains/index.md#how-to-add-a-merge-request-to-a-merge-train) +a **Start/Add Merge Train button** as the most recommended merge strategy. + +Generally, this is a safer option than merging merge requests immediately as your +merge request will be evaluated with an expected post-merge result before the actual +merge happens. + +For more information, read the [documentation on Merge Trains](merge_trains/index.md). diff --git a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/img/merge_train_failure.png b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/img/merge_train_failure.png Binary files differnew file mode 100644 index 00000000000..a8916e5721c --- /dev/null +++ b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/img/merge_train_failure.png diff --git a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/img/merge_train_immediate_merge.png b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/img/merge_train_immediate_merge.png Binary files differnew file mode 100644 index 00000000000..65ff7e3d674 --- /dev/null +++ b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/img/merge_train_immediate_merge.png diff --git a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md index 44cbcde264c..80a1c264bc4 100644 --- a/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md +++ b/doc/ci/merge_request_pipelines/pipelines_for_merged_results/merge_trains/index.md @@ -80,14 +80,65 @@ button while the latest pipeline is running. ![Add to merge train when pipeline succeeds](img/merge_train_start_when_pipeline_succeeds_v12_0.png) -<!-- ## Troubleshooting +## Immediately merge a merge request with a merge train -Include any troubleshooting steps that you can foresee. If you know beforehand what issues -one might have when setting this up, or when something is changed, or on upgrading, it's -important to describe those, too. Think of things that may go wrong and include them here. -This is important to minimize requests for support, and to avoid doc comments with -questions that you know someone might ask. +In case, you have a high-priority merge request (e.g. critical patch) to be merged urgently, +you can use **Merge Immediately** option for bypassing the merge train. +This is the fastest option to get the change merged into the target branch. -Each scenario can be a third-level heading, e.g. `### Getting error message X`. -If you have none to add when creating a doc, leave this section in place -but commented out to help encourage others to add to it in the future. --> +![Merge Immediately](img/merge_train_immediate_merge.png) + +However, every time you merge a merge request immediately, it could affect the +existing merge train to be reconstructed, specifically, it regenerates expected +merge commits and pipelines. This means, merging immediately essentially wastes +CI resources. + +## Troubleshooting + +### Merge request dropped from the merge train immediately + +If a merge request is not mergeable (for example, it's WIP, there is a merge +conflict, etc), your merge request will be dropped from the merge train automatically. + +In these cases, the reason for dropping the merge request is in the **system notes**. + +To check the reason: + +1. Open the merge request that was dropped from the merge train. +1. Open the **Discussion** tab. +1. Find a system note that includes either: + - The text **... removed this merge request from the merge train because ...** + - **... aborted this merge request from the merge train because ...** + The reason is given in the text after the **because ...** phrase. + +![Merge Train Failure](img/merge_train_failure.png) + +### Merge When Pipeline Succeeds cannot be chosen + +[Merge When Pipeline Succeeds](../../../../user/project/merge_requests/merge_when_pipeline_succeeds.md) +is unavailable when +[Pipelines for Merged Results is enabled](../index.md#enabling-pipelines-for-merged-results). + +Follow [this issue](https://gitlab.com/gitlab-org/gitlab-ee/issues/12267) to +track progress on this issue. + +### Merge Train disturbs your workflow + +First of all, please check if [merge immediately](#immediately-merge-a-merge-request-with-a-merge-train) +is available as a workaround in your workflow. This is the most recommended +workaround you'd be able to take immediately. If it's not available or acceptable, +please read through this section. + +Merge train is enabled by default when you enable [Pipelines for merged results](../index.md), +however, you can forcibly disable this feature by disabling the feature flag `:merge_trains_enabled`. +After you disabled this feature, all the existing merge trains will be aborted and +you will no longer see the **Start/Add Merge Train** button in merge requests. + +To check if the feature flag is enabled on your GitLab instance, +please ask administrator to execute the following commands: + +```shell +> sudo gitlab-rails console # Login to Rails console of GitLab instance. +> Feature.enabled?(:merge_trains_enabled) # Check if it's enabled or not. +> Feature.disable(:merge_trains_enabled) # Disable the feature flag. +```
\ No newline at end of file diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md index 98f30350968..be8f66c741f 100644 --- a/doc/ci/pipelines.md +++ b/doc/ci/pipelines.md @@ -6,6 +6,11 @@ type: reference > Introduced in GitLab 8.8. +NOTE: **Tip:** +Watch our +["Mastering continuous software development"](https://about.gitlab.com/webcast/mastering-ci-cd/) +webcast to see a comprehensive demo of GitLab CI/CD pipeline. + ## Introduction Pipelines are the top-level component of continuous integration, delivery, and deployment. diff --git a/doc/development/README.md b/doc/development/README.md index ea5d9e10e2c..99c88146be5 100644 --- a/doc/development/README.md +++ b/doc/development/README.md @@ -113,6 +113,10 @@ description: 'Learn how to contribute to GitLab.' - [Database helper modules](database_helpers.md) - [Code comments](code_comments.md) +## Case studies + +- [Database case study: Filtering by label](filtering_by_label.md) + ## Integration guides - [Jira Connect app](integrations/jira_connect.md) diff --git a/doc/development/database_review.md b/doc/development/database_review.md index 1413c2f69fb..3d10a0c84e5 100644 --- a/doc/development/database_review.md +++ b/doc/development/database_review.md @@ -68,6 +68,17 @@ make sure you have applied the ~database label and rerun the `danger-review` CI job, or pick someone from the [`@gl-database` team](https://gitlab.com/groups/gl-database/-/group_members). +### How to prepare for speedy database reviews + +In order to make reviewing easier and therefore faster, please consider preparing a comment +and details for a database reviewer: + +- Provide queries in SQL form rather than ActiveRecord. +- Format any queries with a SQL query formatter, for example with [sqlformat.darold.net](http://sqlformat.darold.net). +- Consider providing query plans via a link to [explain.depesz.com](https://explain.depesz.com) or another tool instead of textual form. +- For query changes, it is best to provide the SQL query along with a plan *before* and *after* the change. This helps to spot differences quickly. +- When providing query plans, make sure to use good parameter values, so that the query executed is a good example and also hits enough data. Usually, the `gitlab-org` namespace (`namespace_id = 9970`) and the `gitlab-org/gitlab-ce` project (`project_id = 13083`) provides enough data to serve as a good example. + ### How to review for database - Check migrations diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md index 36ffc02644e..dd798777c12 100644 --- a/doc/development/documentation/styleguide.md +++ b/doc/development/documentation/styleguide.md @@ -1092,6 +1092,6 @@ curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" --data "domain_ [cURL]: http://curl.haxx.se/ "cURL website" [single spaces]: http://www.slate.com/articles/technology/technology/2011/01/space_invaders.html -[gfm]: https://docs.gitlab.com/ce/user/markdown.html#newlines "GitLab flavored markdown documentation" +[gfm]: ../../user/markdown.md#newlines "GitLab flavored markdown documentation" [ce-1242]: https://gitlab.com/gitlab-org/gitlab-ce/issues/1242 [doc-restart]: ../../administration/restart_gitlab.md "GitLab restart documentation" diff --git a/doc/development/filtering_by_label.md b/doc/development/filtering_by_label.md new file mode 100644 index 00000000000..6e6b71b1787 --- /dev/null +++ b/doc/development/filtering_by_label.md @@ -0,0 +1,166 @@ +# Filtering by label + +## Introduction + +GitLab has [labels](../user/project/labels.md) that can be assigned to issues, +merge requests, and epics. Labels on those objects are a many-to-many relation +through the polymorphic `label_links` table. + +To filter these objects by multiple labels - for instance, 'all open +issues with the label ~Plan and the label ~backend' - we generate a +query containing a `GROUP BY` clause. In a simple form, this looks like: + +```sql +SELECT + issues.* +FROM + issues + INNER JOIN label_links ON label_links.target_id = issues.id + AND label_links.target_type = 'Issue' + INNER JOIN labels ON labels.id = label_links.label_id +WHERE + issues.project_id = 13083 + AND (issues.state IN ('opened')) + AND labels.title IN ('Plan', + 'backend') +GROUP BY + issues.id +HAVING (COUNT(DISTINCT labels.title) = 2) +ORDER BY + issues.updated_at DESC, + issues.id DESC +LIMIT 20 OFFSET 0 +``` + +In particular, note that: + +1. We `GROUP BY issues.id` so that we can ... +2. Use the `HAVING (COUNT(DISTINCT labels.title) = 2)` condition to ensure that + all matched issues have both labels. + +This is more complicated than is ideal. It makes the query construction more +prone to errors (such as +[gitlab-org/gitlab-ce#15557](https://gitlab.com/gitlab-org/gitlab-ce/issues/15557)). + +## Attempt A: WHERE EXISTS + +### Attempt A1: use multiple subqueries with WHERE EXISTS + +In +[gitlab-org/gitlab-ce#37137](https://gitlab.com/gitlab-org/gitlab-ce/issues/37137) +and its associated merge request +[gitlab-org/gitlab-ce!14022](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14022), +we tried to replace the `GROUP BY` with multiple uses of `WHERE EXISTS`. For the +example above, this would give: + +```sql +WHERE (EXISTS ( + SELECT + TRUE + FROM + label_links + INNER JOIN labels ON labels.id = label_links.label_id + WHERE + labels.title = 'Plan' + AND target_type = 'Issue' + AND target_id = issues.id)) +AND (EXISTS ( + SELECT + TRUE + FROM + label_links + INNER JOIN labels ON labels.id = label_links.label_id + WHERE + labels.title = 'backend' + AND target_type = 'Issue' + AND target_id = issues.id)) +``` + +While this worked without schema changes, and did improve readability somewhat, +it did not improve query performance. + +## Attempt B: Denormalize using an array column + +Having [removed MySQL support in GitLab +12.1](https://about.gitlab.com/2019/06/27/removing-mysql-support/), using +[Postgres's arrays](https://www.postgresql.org/docs/9.6/arrays.html) became more +tractable as we didn't have to support two databases. We discussed denormalizing +the `label_links` table for querying in +[gitlab-org/gitlab-ce#49651](https://gitlab.com/gitlab-org/gitlab-ce/issues/49651), +with two options: label IDs and titles. + +We can think of both of those as array columns on `issues`, `merge_requests`, +and `epics`: `issues.label_ids` would be an array column of label IDs, and +`issues.label_titles` would be an array of label titles. + +These array columns can be complemented with [GIN +indexes](https://www.postgresql.org/docs/9.6/gin-intro.html) to improve +matching. + +### Attempt B1: store label IDs for each object + +This has some strong advantages over titles: + +1. Unless a label is deleted, or a project is moved, we never need to + bulk-update the denormalized column. +2. It uses less storage than the titles. + +Unfortunately, our application design makes this hard. If we were able to query +just by label ID easily, we wouldn't need the `INNER JOIN labels` in the initial +query at the start of this document. GitLab allows users to filter by label +title across projects and even across groups, so a filter by the label ~Plan may +include labels with multiple distinct IDs. + +We do not want users to have to know about the different IDs, which means that +given this data set: + +| Project | ~Plan label ID | ~backend label ID | +| --- | --- | --- | +| A | 11 | 12 | +| B | 21 | 22 | +| C | 31 | 32 | + +We would need something like: + +```sql +WHERE + label_ids @> ARRAY[11, 12] + OR label_ids @> ARRAY[21, 22] + OR label_ids @> ARRAY[31, 32] +``` + +This can get even more complicated when we consider that in some cases, there +might be two ~backend labels - with different IDs - that could apply to the same +object, so the number of combinations would balloon further. + +### Attempt B2: store label titles for each object + +From the perspective of updating the labelable object, this is the worst +option. We have to bulk update the objects when: + +1. The objects are moved from one project to another. +1. The project is moved from one group to another. +1. The label is renamed. +1. The label is deleted. + +It also uses much more storage. Querying is simple, though: + +```sql +WHERE + label_titles @> ARRAY['Plan', 'backend'] +``` + +And our [tests in +gitlab-org/gitlab-ce#49651](https://gitlab.com/gitlab-org/gitlab-ce/issues/49651#note_188777346) +showed that this could be fast. + +However, at present, the disadvantages outweigh the advantages. + +## Conclusion + +We have yet to find a method that is demonstratably better than the current +method, when considering: + +1. Query performance. +1. Readability. +1. Ease of maintaining schema consistency. diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md index ff28c2ea5e2..2985278cc92 100644 --- a/doc/development/testing_guide/frontend_testing.md +++ b/doc/development/testing_guide/frontend_testing.md @@ -588,7 +588,7 @@ end [jasmine-focus]: https://jasmine.github.io/2.5/focused_specs.html [karma]: http://karma-runner.github.io/ -[vue-test]: https://docs.gitlab.com/ce/development/fe_guide/vue.html#testing-vue-components +[vue-test]: ../fe_guide/vue.md#testing-vue-components [rspec]: https://github.com/rspec/rspec-rails#feature-specs [capybara]: https://github.com/teamcapybara/capybara [jasmine]: https://jasmine.github.io/ diff --git a/doc/install/azure/index.md b/doc/install/azure/index.md index c0e1b0ebbc8..543a222bd25 100644 --- a/doc/install/azure/index.md +++ b/doc/install/azure/index.md @@ -70,7 +70,7 @@ The first items we need to configure are the basic settings of the underlying vi > **Note:** if you're unsure which authentication type to use, select **Password** 1. If you chose **SSH public key** - enter your `SSH public key` into the field provided - _(read the [SSH documentation][GitLab-Docs-SSH] to learn more about how to set up SSH + _(read the [SSH documentation](../../ssh/README.md) to learn more about how to set up SSH public keys)_ 1. If you chose **Password** - enter the password you wish to use _(this is the password that you will use later in this tutorial to [SSH] into the VM, so make sure it's a strong password/passphrase)_ @@ -407,7 +407,7 @@ on any cloud service you choose. ## Where to next? -Check out our other [Technical Articles][GitLab-Technical-Articles] or browse the [GitLab Documentation][GitLab-Docs] to learn more about GitLab. +Check out our other [Technical Articles](../../articles/index.md) or browse the [GitLab Documentation][GitLab-Docs](../../README.md) to learn more about GitLab. ### Useful links @@ -423,9 +423,6 @@ Check out our other [Technical Articles][GitLab-Technical-Articles] or browse th - [SSH], [PuTTY] and [Using SSH in PuTTY][Using-SSH-In-Putty] [Original-Blog-Post]: https://about.gitlab.com/2016/07/13/how-to-setup-a-gitlab-instance-on-microsoft-azure/ "How to Set up a GitLab Instance on Microsoft Azure" -[GitLab-Docs]: https://docs.gitlab.com/ce/README.html "GitLab Documentation" -[GitLab-Technical-Articles]: https://docs.gitlab.com/ce/articles/index.html "GitLab Technical Articles" -[GitLab-Docs-SSH]: https://docs.gitlab.com/ce/ssh/README.html "GitLab Documentation: SSH" [CE]: https://about.gitlab.com/features/ [EE]: https://about.gitlab.com/features/#ee-starter diff --git a/doc/integration/README.md b/doc/integration/README.md index 135952a1b08..55f9666e3a3 100644 --- a/doc/integration/README.md +++ b/doc/integration/README.md @@ -27,7 +27,7 @@ See the documentation below for details on how to configure these services. - [SAML](saml.md) Configure GitLab as a SAML 2.0 Service Provider - [Trello](trello_power_up.md) Integrate Trello with GitLab -> GitLab Enterprise Edition contains [advanced Jenkins support][jenkins]. +> GitLab Enterprise Edition contains [advanced Jenkins support](jenkins.md). ## Project services @@ -70,5 +70,3 @@ After that restart GitLab with: ```bash sudo gitlab-ctl restart ``` - -[jenkins]: https://docs.gitlab.com/ee/integration/jenkins.html diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md index 296ab63166f..c6219126b30 100644 --- a/doc/topics/autodevops/index.md +++ b/doc/topics/autodevops/index.md @@ -192,10 +192,10 @@ The following table is an example of how the three different clusters would be configured. | Cluster name | Cluster environment scope | `KUBE_INGRESS_BASE_DOMAIN` variable value | Variable environment scope | Notes | -| ------------ | -------------- | ----------------------------- | ------------- | ------ | -| review | `review/*` | `review.example.com` | `review/*` | The review cluster which will run all [Review Apps](../../ci/review_apps/index.md). `*` is a wildcard, which means it will be used by every environment name starting with `review/`. | -| staging | `staging` | `staging.example.com` | `staging` | (Optional) The staging cluster which will run the deployments of the staging environments. You need to [enable it first](#deploy-policy-for-staging-and-production-environments). | -| production | `production` | `example.com` | `production` | The production cluster which will run the deployments of the production environment. You can use [incremental rollouts](#incremental-rollout-to-production-premium). | +|--------------|---------------------------|-------------------------------------------|----------------------------|---| +| review | `review/*` | `review.example.com` | `review/*` | The review cluster which will run all [Review Apps](../../ci/review_apps/index.md). `*` is a wildcard, which means it will be used by every environment name starting with `review/`. | +| staging | `staging` | `staging.example.com` | `staging` | (Optional) The staging cluster which will run the deployments of the staging environments. You need to [enable it first](#deploy-policy-for-staging-and-production-environments). | +| production | `production` | `example.com` | `production` | The production cluster which will run the deployments of the production environment. You can use [incremental rollouts](#incremental-rollout-to-production-premium). | To add a different cluster for each environment: @@ -721,47 +721,47 @@ The following variables can be used for setting up the Auto DevOps domain, providing a custom Helm chart, or scaling your application. PostgreSQL can also be customized, and you can easily use a [custom buildpack](#custom-buildpacks). -| **Variable** | **Description** | -| ------------ | --------------- | -| `AUTO_DEVOPS_CHART` | The Helm Chart used to deploy your apps; defaults to the one [provided by GitLab](https://gitlab.com/gitlab-org/charts/auto-deploy-app). | -| `AUTO_DEVOPS_CHART_REPOSITORY` | The Helm Chart repository used to search for charts; defaults to `https://charts.gitlab.io`. | -| `AUTO_DEVOPS_CHART_REPOSITORY_NAME` | From Gitlab 11.11, this variable can be used to set the name of the helm repository; defaults to "gitlab" | +| **Variable** | **Description** | +|-----------------------------------------|------------------------------------| +| `AUTO_DEVOPS_CHART` | The Helm Chart used to deploy your apps; defaults to the one [provided by GitLab](https://gitlab.com/gitlab-org/charts/auto-deploy-app). | +| `AUTO_DEVOPS_CHART_REPOSITORY` | The Helm Chart repository used to search for charts; defaults to `https://charts.gitlab.io`. | +| `AUTO_DEVOPS_CHART_REPOSITORY_NAME` | From Gitlab 11.11, this variable can be used to set the name of the helm repository; defaults to "gitlab" | | `AUTO_DEVOPS_CHART_REPOSITORY_USERNAME` | From Gitlab 11.11, this variable can be used to set a username to connect to the helm repository. Defaults to no credentials. (Also set AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD) | | `AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD` | From Gitlab 11.11, this variable can be used to set a password to connect to the helm repository. Defaults to no credentials. (Also set AUTO_DEVOPS_CHART_REPOSITORY_USERNAME) | -| `REPLICAS` | The number of replicas to deploy; defaults to 1. | -| `PRODUCTION_REPLICAS` | The number of replicas to deploy in the production environment. Takes precedence over `REPLICAS` and defaults to 1. For zero downtime upgrades, set to 2 or greater. | -| `CANARY_REPLICAS` | The number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md); defaults to 1 | -| `CANARY_PRODUCTION_REPLICAS` | The number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md) in the production environment. This takes precedence over `CANARY_REPLICAS`; defaults to 1 | -| `ADDITIONAL_HOSTS` | Fully qualified domain names specified as a comma-separated list that are added to the ingress hosts. | -| `<ENVIRONMENT>_ADDITIONAL_HOSTS` | For a specific environment, the fully qualified domain names specified as a comma-separated list that are added to the ingress hosts. This takes precedence over `ADDITIONAL_HOSTS`. | -| `POSTGRES_ENABLED` | Whether PostgreSQL is enabled; defaults to `"true"`. Set to `false` to disable the automatic deployment of PostgreSQL. | -| `POSTGRES_USER` | The PostgreSQL user; defaults to `user`. Set it to use a custom username. | -| `POSTGRES_PASSWORD` | The PostgreSQL password; defaults to `testing-password`. Set it to use a custom password. | -| `POSTGRES_DB` | The PostgreSQL database name; defaults to the value of [`$CI_ENVIRONMENT_SLUG`](../../ci/variables/README.md#predefined-environment-variables). Set it to use a custom database name. | -| `POSTGRES_VERSION` | Tag for the [`postgres` Docker image](https://hub.docker.com/_/postgres) to use. Defaults to `9.6.2`. | -| `BUILDPACK_URL` | The buildpack's full URL. It can point to either Git repositories or a tarball URL. For Git repositories, it is possible to point to a specific `ref`, for example `https://github.com/heroku/heroku-buildpack-ruby.git#v142` | -| `SAST_CONFIDENCE_LEVEL` | The minimum confidence level of security issues you want to be reported; `1` for Low, `2` for Medium, `3` for High; defaults to `3`.| -| `DEP_SCAN_DISABLE_REMOTE_CHECKS` | Whether remote Dependency Scanning checks are disabled; defaults to `"false"`. Set to `"true"` to disable checks that send data to GitLab central servers. [Read more about remote checks](https://gitlab.com/gitlab-org/security-products/dependency-scanning#remote-checks).| -| `DB_INITIALIZE` | From GitLab 11.4, this variable can be used to specify the command to run to initialize the application's PostgreSQL database. It runs inside the application pod. | -| `DB_MIGRATE` | From GitLab 11.4, this variable can be used to specify the command to run to migrate the application's PostgreSQL database. It runs inside the application pod. | -| `STAGING_ENABLED` | From GitLab 10.8, this variable can be used to define a [deploy policy for staging and production environments](#deploy-policy-for-staging-and-production-environments). | -| `CANARY_ENABLED` | From GitLab 11.0, this variable can be used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments-premium). | -| `INCREMENTAL_ROLLOUT_MODE`| From GitLab 11.4, this variable, if present, can be used to enable an [incremental rollout](#incremental-rollout-to-production-premium) of your application for the production environment. Set to `manual` for manual deployment jobs or `timed` for automatic rollout deployments with a 5 minute delay each one. | -| `TEST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `test` job. If the variable is present, the job will not be created. | -| `CODE_QUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `codequality` job. If the variable is present, the job will not be created. | -| `LICENSE_MANAGEMENT_DISABLED` | From GitLab 11.0, this variable can be used to disable the `license_management` job. If the variable is present, the job will not be created. | -| `SAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast` job. If the variable is present, the job will not be created. | -| `DEPENDENCY_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dependency_scanning` job. If the variable is present, the job will not be created. | -| `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast:container` job. If the variable is present, the job will not be created. | -| `REVIEW_DISABLED` | From GitLab 11.0, this variable can be used to disable the `review` and the manual `review:stop` job. If the variable is present, these jobs will not be created. | -| `DAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dast` job. If the variable is present, the job will not be created. | -| `PERFORMANCE_DISABLED` | From GitLab 11.0, this variable can be used to disable the `performance` job. If the variable is present, the job will not be created. | -| `K8S_SECRET_*` | From GitLab 11.7, any variable prefixed with [`K8S_SECRET_`](#application-secret-variables) will be made available by Auto DevOps as environment variables to the deployed application. | -| `KUBE_INGRESS_BASE_DOMAIN` | From GitLab 11.8, this variable can be used to set a domain per cluster. See [cluster domains](../../user/project/clusters/index.md#base-domain) for more information. | -| `ROLLOUT_RESOURCE_TYPE` | From GitLab 11.9, this variable allows specification of the resource type being deployed when using a custom helm chart. Default value is `deployment`. | -| `ROLLOUT_STATUS_DISABLED` | From GitLab 12.0, this variable allows to disable rollout status check because it doesn't support all resource types, for example, `cronjob`. | -| `HELM_UPGRADE_EXTRA_ARGS` | From GitLab 11.11, this variable allows extra arguments in `helm` commands when deploying the application. Note that using quotes will not prevent word splitting. **Tip:** you can use this variable to [customize the Auto Deploy helm chart](https://docs.gitlab.com/ee/topics/autodevops/index.html#custom-helm-chart) by applying custom override values with `--values my-values.yaml`. | -| `HELM_RELEASE_NAME` | From GitLab 12.1, this variable allows the `helm` release name to be overridden, this can be used to assign unique release names when deploying multiple projects to a single namespace | +| `REPLICAS` | The number of replicas to deploy; defaults to 1. | +| `PRODUCTION_REPLICAS` | The number of replicas to deploy in the production environment. Takes precedence over `REPLICAS` and defaults to 1. For zero downtime upgrades, set to 2 or greater. | +| `CANARY_REPLICAS` | The number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md); defaults to 1. | +| `CANARY_PRODUCTION_REPLICAS` | The number of canary replicas to deploy for [Canary Deployments](../../user/project/canary_deployments.md) in the production environment. This takes precedence over `CANARY_REPLICAS`; defaults to 1. | +| `ADDITIONAL_HOSTS` | Fully qualified domain names specified as a comma-separated list that are added to the ingress hosts. | +| `<ENVIRONMENT>_ADDITIONAL_HOSTS` | For a specific environment, the fully qualified domain names specified as a comma-separated list that are added to the ingress hosts. This takes precedence over `ADDITIONAL_HOSTS`. | +| `POSTGRES_ENABLED` | Whether PostgreSQL is enabled; defaults to `"true"`. Set to `false` to disable the automatic deployment of PostgreSQL. | +| `POSTGRES_USER` | The PostgreSQL user; defaults to `user`. Set it to use a custom username. | +| `POSTGRES_PASSWORD` | The PostgreSQL password; defaults to `testing-password`. Set it to use a custom password. | +| `POSTGRES_DB` | The PostgreSQL database name; defaults to the value of [`$CI_ENVIRONMENT_SLUG`](../../ci/variables/README.md#predefined-environment-variables). Set it to use a custom database name. | +| `POSTGRES_VERSION` | Tag for the [`postgres` Docker image](https://hub.docker.com/_/postgres) to use. Defaults to `9.6.2`. | +| `BUILDPACK_URL` | The buildpack's full URL. It can point to either Git repositories or a tarball URL. For Git repositories, it is possible to point to a specific `ref`, for example `https://github.com/heroku/heroku-buildpack-ruby.git#v142`. | +| `SAST_CONFIDENCE_LEVEL` | The minimum confidence level of security issues you want to be reported; `1` for Low, `2` for Medium, `3` for High; defaults to `3`. | +| `DS_DISABLE_REMOTE_CHECKS` | Whether remote Dependency Scanning checks are disabled; defaults to `"false"`. Set to `"true"` to disable checks that send data to GitLab central servers. [Read more about remote checks](../../user/application_security/dependency_scanning/index.md#remote-checks). | +| `DB_INITIALIZE` | From GitLab 11.4, this variable can be used to specify the command to run to initialize the application's PostgreSQL database. It runs inside the application pod. | +| `DB_MIGRATE` | From GitLab 11.4, this variable can be used to specify the command to run to migrate the application's PostgreSQL database. It runs inside the application pod. | +| `STAGING_ENABLED` | From GitLab 10.8, this variable can be used to define a [deploy policy for staging and production environments](#deploy-policy-for-staging-and-production-environments). | +| `CANARY_ENABLED` | From GitLab 11.0, this variable can be used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments-premium). | +| `INCREMENTAL_ROLLOUT_MODE` | From GitLab 11.4, this variable, if present, can be used to enable an [incremental rollout](#incremental-rollout-to-production-premium) of your application for the production environment. Set to `manual` for manual deployment jobs or `timed` for automatic rollout deployments with a 5 minute delay each one. | +| `TEST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `test` job. If the variable is present, the job will not be created. | +| `CODE_QUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `codequality` job. If the variable is present, the job will not be created. | +| `LICENSE_MANAGEMENT_DISABLED` | From GitLab 11.0, this variable can be used to disable the `license_management` job. If the variable is present, the job will not be created. | +| `SAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast` job. If the variable is present, the job will not be created. | +| `DEPENDENCY_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dependency_scanning` job. If the variable is present, the job will not be created. | +| `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast:container` job. If the variable is present, the job will not be created. | +| `REVIEW_DISABLED` | From GitLab 11.0, this variable can be used to disable the `review` and the manual `review:stop` job. If the variable is present, these jobs will not be created. | +| `DAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dast` job. If the variable is present, the job will not be created. | +| `PERFORMANCE_DISABLED` | From GitLab 11.0, this variable can be used to disable the `performance` job. If the variable is present, the job will not be created. | +| `K8S_SECRET_*` | From GitLab 11.7, any variable prefixed with [`K8S_SECRET_`](#application-secret-variables) will be made available by Auto DevOps as environment variables to the deployed application. | +| `KUBE_INGRESS_BASE_DOMAIN` | From GitLab 11.8, this variable can be used to set a domain per cluster. See [cluster domains](../../user/project/clusters/index.md#base-domain) for more information. | +| `ROLLOUT_RESOURCE_TYPE` | From GitLab 11.9, this variable allows specification of the resource type being deployed when using a custom helm chart. Default value is `deployment`. | +| `ROLLOUT_STATUS_DISABLED` | From GitLab 12.0, this variable allows to disable rollout status check because it doesn't support all resource types, for example, `cronjob`. | +| `HELM_UPGRADE_EXTRA_ARGS` | From GitLab 11.11, this variable allows extra arguments in `helm` commands when deploying the application. Note that using quotes will not prevent word splitting. **Tip:** you can use this variable to [customize the Auto Deploy helm chart](#custom-helm-chart) by applying custom override values with `--values my-values.yaml`. | +| `HELM_RELEASE_NAME` | From GitLab 12.1, this variable allows the `helm` release name to be overridden, this can be used to assign unique release names when deploying multiple projects to a single namespace. | TIP: **Tip:** Set up the replica variables using a diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md index 61c30e0b0ef..e609fe43507 100644 --- a/doc/user/project/integrations/prometheus.md +++ b/doc/user/project/integrations/prometheus.md @@ -323,8 +323,11 @@ Once enabled, an issue will be opened automatically when an alert is triggered w - `starts_at`: Alert start time via `startsAt` - `full_query`: Alert query extracted from `generatorURL` - Optional list of attached annotations extracted from `annotations/*` +- Alert [GFM](../../markdown.md): GitLab Flavored Markdown from `annotations/gitlab_incident_markdown` -To further customize the issue, you can add labels, mentions, or any other supported [quick action](../quick_actions.md) in the selected issue template. +To further customize the issue, you can add labels, mentions, or any other supported [quick action](../quick_actions.md) in the selected issue template, which will apply to all incidents. To limit quick actions or other information to only specific types of alerts, use the `annotations/gitlab_incident_markdown` field. + +Since [version 12.2](https://gitlab.com/gitlab-org/gitlab-ce/issues/63373), GitLab will tag each incident issue with the `incident` label automatically. If the label does not yet exist, it will be created automatically as well. If the metric exceeds the threshold of the alert for over 5 minutes, an email will be sent to all [Maintainers and Owners](../../permissions.md#project-members-permissions) of the project. diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md index d5ca853eff5..f78ec9d96e6 100644 --- a/doc/user/project/merge_requests/index.md +++ b/doc/user/project/merge_requests/index.md @@ -343,6 +343,30 @@ git push -o merge_request.remove_source_branch You can also use this push option in addition to the `merge_request.create` push option. +### Set merge request title using git push options + +To set the title of an existing merge request, use +the `merge_request.title` push option: + +```sh +git push -o merge_request.title="The title I want" +``` + +You can also use this push option in addition to the +`merge_request.create` push option. + +### Set merge request description using git push options + +To set the description of an existing merge request, use +the `merge_request.description` push option: + +```sh +git push -o merge_request.description="The description I want" +``` + +You can also use this push option in addition to the +`merge_request.create` push option. + ## Find the merge request that introduced a change > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/2383) in GitLab 10.5. diff --git a/doc/user/project/protected_branches.md b/doc/user/project/protected_branches.md index 20a03dff2da..9a3c02e1f50 100644 --- a/doc/user/project/protected_branches.md +++ b/doc/user/project/protected_branches.md @@ -180,6 +180,5 @@ for details about the pipelines security model. [ce-4892]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/4892 "Allow developers to merge into a protected branch without having push access" [ce-5081]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5081 "Allow creating protected branches that can't be pushed to" [ce-21393]: https://gitlab.com/gitlab-org/gitlab-ce/issues/21393 -[ee-restrict]: https://docs.gitlab.com/ee/user/project/protected_branches.html#restricting-push-and-merge-access-to-certain-users [perm]: ../permissions.md [ee]: https://about.gitlab.com/pricing/ diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 10b4f8934d7..2e78331df6c 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -366,10 +366,7 @@ module API end expose :request_access_enabled expose :full_name, :full_path - - if ::Group.supports_nested_objects? - expose :parent_id - end + expose :parent_id expose :custom_attributes, using: 'API::Entities::CustomAttribute', if: :with_custom_attributes diff --git a/lib/api/groups.rb b/lib/api/groups.rb index ec1020c7c78..f545f33c06b 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -114,10 +114,7 @@ module API params do requires :name, type: String, desc: 'The name of the group' requires :path, type: String, desc: 'The path of the group' - - if ::Group.supports_nested_objects? - optional :parent_id, type: Integer, desc: 'The parent group id for creating nested group' - end + optional :parent_id, type: Integer, desc: 'The parent group id for creating nested group' use :optional_params end diff --git a/lib/api/job_artifacts.rb b/lib/api/job_artifacts.rb index e7fed55170e..b35aa952f81 100644 --- a/lib/api/job_artifacts.rb +++ b/lib/api/job_artifacts.rb @@ -27,7 +27,7 @@ module API requirements: { ref_name: /.+/ } do authorize_download_artifacts! - latest_build = user_project.latest_successful_build_for!(params[:job], params[:ref_name]) + latest_build = user_project.latest_successful_build_for_ref!(params[:job], params[:ref_name]) present_carrierwave_file!(latest_build.artifacts_file) end @@ -45,7 +45,7 @@ module API requirements: { ref_name: /.+/ } do authorize_download_artifacts! - build = user_project.latest_successful_build_for!(params[:job], params[:ref_name]) + build = user_project.latest_successful_build_for_ref!(params[:job], params[:ref_name]) path = Gitlab::Ci::Build::Artifacts::Path .new(params[:artifact_path]) diff --git a/lib/api/validations/types/labels_list.rb b/lib/api/validations/types/labels_list.rb index 47cd83c29cf..60277b99106 100644 --- a/lib/api/validations/types/labels_list.rb +++ b/lib/api/validations/types/labels_list.rb @@ -10,7 +10,7 @@ module API when String value.split(',').map(&:strip) when Array - value.map { |v| v.to_s.split(',').map(&:strip) }.flatten + value.flat_map { |v| v.to_s.split(',').map(&:strip) } when LabelsList value else diff --git a/lib/banzai/reference_redactor.rb b/lib/banzai/reference_redactor.rb index eb5c35da375..936436982e7 100644 --- a/lib/banzai/reference_redactor.rb +++ b/lib/banzai/reference_redactor.rb @@ -33,7 +33,7 @@ module Banzai # # data - An Array of a Hashes mapping an HTML document to nodes to redact. def redact_document_nodes(all_document_nodes) - all_nodes = all_document_nodes.map { |x| x[:nodes] }.flatten + all_nodes = all_document_nodes.flat_map { |x| x[:nodes] } visible = nodes_visible_to_user(all_nodes) metadata = [] diff --git a/lib/gitlab/auth/activity.rb b/lib/gitlab/auth/activity.rb index 558628b5422..988ff196193 100644 --- a/lib/gitlab/auth/activity.rb +++ b/lib/gitlab/auth/activity.rb @@ -37,14 +37,17 @@ module Gitlab def user_authenticated! self.class.user_authenticated_counter_increment! + + case @opts[:message] + when :two_factor_authenticated + self.class.user_two_factor_authenticated_counter_increment! + end end def user_session_override! self.class.user_session_override_counter_increment! case @opts[:message] - when :two_factor_authenticated - self.class.user_two_factor_authenticated_counter_increment! when :sessionless_sign_in self.class.user_sessionless_authentication_counter_increment! end diff --git a/lib/gitlab/auth/o_auth/auth_hash.rb b/lib/gitlab/auth/o_auth/auth_hash.rb index 72a187377d0..91b9ddc0d00 100644 --- a/lib/gitlab/auth/o_auth/auth_hash.rb +++ b/lib/gitlab/auth/o_auth/auth_hash.rb @@ -60,8 +60,7 @@ module Gitlab def get_info(key) value = info[key] - Gitlab::Utils.force_utf8(value) if value - value + value.is_a?(String) ? Gitlab::Utils.force_utf8(value) : value end def username_and_email diff --git a/lib/gitlab/background_migration/backfill_project_repositories.rb b/lib/gitlab/background_migration/backfill_project_repositories.rb index c8d83cc1803..1d9aa050041 100644 --- a/lib/gitlab/background_migration/backfill_project_repositories.rb +++ b/lib/gitlab/background_migration/backfill_project_repositories.rb @@ -40,7 +40,7 @@ module Gitlab end def reload! - @shards = Hash[*Shard.all.map { |shard| [shard.name, shard.id] }.flatten] + @shards = Hash[*Shard.all.flat_map { |shard| [shard.name, shard.id] }] end end diff --git a/lib/gitlab/badge/coverage/report.rb b/lib/gitlab/badge/coverage/report.rb index 7f7cc62c8ef..15cccc6f287 100644 --- a/lib/gitlab/badge/coverage/report.rb +++ b/lib/gitlab/badge/coverage/report.rb @@ -14,7 +14,7 @@ module Gitlab @ref = ref @job = job - @pipeline = @project.ci_pipelines.latest_successful_for(@ref) + @pipeline = @project.ci_pipelines.latest_successful_for_ref(@ref) end def entity diff --git a/lib/gitlab/ci/config/normalizer.rb b/lib/gitlab/ci/config/normalizer.rb index 191f5d09645..99356226ef9 100644 --- a/lib/gitlab/ci/config/normalizer.rb +++ b/lib/gitlab/ci/config/normalizer.rb @@ -46,7 +46,7 @@ module Gitlab parallelized_job_names = @parallelized_jobs.keys.map(&:to_s) parallelized_config.each_with_object({}) do |(job_name, config), hash| if config[:dependencies] && (intersection = config[:dependencies] & parallelized_job_names).any? - parallelized_deps = intersection.map { |dep| @parallelized_jobs[dep.to_sym].map(&:first) }.flatten + parallelized_deps = intersection.flat_map { |dep| @parallelized_jobs[dep.to_sym].map(&:first) } deps = config[:dependencies] - intersection + parallelized_deps hash[job_name] = config.merge(dependencies: deps) else diff --git a/lib/gitlab/ci/status/factory.rb b/lib/gitlab/ci/status/factory.rb index 3446644eff8..2a0bf060c9b 100644 --- a/lib/gitlab/ci/status/factory.rb +++ b/lib/gitlab/ci/status/factory.rb @@ -34,11 +34,9 @@ module Gitlab def extended_statuses return @extended_statuses if defined?(@extended_statuses) - groups = self.class.extended_statuses.map do |group| + @extended_statuses = self.class.extended_statuses.flat_map do |group| Array(group).find { |status| status.matches?(@subject, @user) } - end - - @extended_statuses = groups.flatten.compact + end.compact end def self.extended_statuses diff --git a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml index 7b9a169a91f..5c1c0c142e5 100644 --- a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml @@ -50,9 +50,6 @@ variables: POSTGRES_DB: $CI_ENVIRONMENT_SLUG POSTGRES_VERSION: 9.6.2 - KUBERNETES_VERSION: 1.11.10 - HELM_VERSION: 2.14.0 - DOCKER_DRIVER: overlay2 ROLLOUT_RESOURCE_TYPE: deployment diff --git a/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml index 6ead127e7b6..a8ec2d4781d 100644 --- a/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml @@ -1,14 +1,17 @@ +.auto-deploy: + image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-deploy-image:v0.1.0" + review: + extends: .auto-deploy stage: review script: - - check_kube_domain - - install_dependencies - - download_chart - - ensure_namespace - - initialize_tiller - - create_secret - - deploy - - persist_environment_url + - auto-deploy check_kube_domain + - auto-deploy download_chart + - auto-deploy ensure_namespace + - auto-deploy initialize_tiller + - auto-deploy create_secret + - auto-deploy deploy + - auto-deploy persist_environment_url environment: name: review/$CI_COMMIT_REF_NAME url: http://$CI_PROJECT_ID-$CI_ENVIRONMENT_SLUG.$KUBE_INGRESS_BASE_DOMAIN @@ -27,13 +30,13 @@ review: - $REVIEW_DISABLED stop_review: + extends: .auto-deploy stage: cleanup variables: GIT_STRATEGY: none script: - - install_dependencies - - initialize_tiller - - delete + - auto-deploy initialize_tiller + - auto-deploy delete environment: name: review/$CI_COMMIT_REF_NAME action: stop @@ -57,15 +60,15 @@ stop_review: # STAGING_ENABLED. staging: + extends: .auto-deploy stage: staging script: - - check_kube_domain - - install_dependencies - - download_chart - - ensure_namespace - - initialize_tiller - - create_secret - - deploy + - auto-deploy check_kube_domain + - auto-deploy download_chart + - auto-deploy ensure_namespace + - auto-deploy initialize_tiller + - auto-deploy create_secret + - auto-deploy deploy environment: name: staging url: http://$CI_PROJECT_PATH_SLUG-staging.$KUBE_INGRESS_BASE_DOMAIN @@ -81,15 +84,15 @@ staging: # CANARY_ENABLED. canary: + extends: .auto-deploy stage: canary script: - - check_kube_domain - - install_dependencies - - download_chart - - ensure_namespace - - initialize_tiller - - create_secret - - deploy canary + - auto-deploy check_kube_domain + - auto-deploy download_chart + - auto-deploy ensure_namespace + - auto-deploy initialize_tiller + - auto-deploy create_secret + - auto-deploy deploy canary environment: name: production url: http://$CI_PROJECT_PATH_SLUG.$KUBE_INGRESS_BASE_DOMAIN @@ -102,18 +105,18 @@ canary: - $CANARY_ENABLED .production: &production_template + extends: .auto-deploy stage: production script: - - check_kube_domain - - install_dependencies - - download_chart - - ensure_namespace - - initialize_tiller - - create_secret - - deploy - - delete canary - - delete rollout - - persist_environment_url + - auto-deploy check_kube_domain + - auto-deploy download_chart + - auto-deploy ensure_namespace + - auto-deploy initialize_tiller + - auto-deploy create_secret + - auto-deploy deploy + - auto-deploy delete canary + - auto-deploy delete rollout + - auto-deploy persist_environment_url environment: name: production url: http://$CI_PROJECT_PATH_SLUG.$KUBE_INGRESS_BASE_DOMAIN @@ -152,17 +155,17 @@ production_manual: # This job implements incremental rollout on for every push to `master`. .rollout: &rollout_template + extends: .auto-deploy script: - - check_kube_domain - - install_dependencies - - download_chart - - ensure_namespace - - initialize_tiller - - create_secret - - deploy rollout $ROLLOUT_PERCENTAGE - - scale stable $((100-ROLLOUT_PERCENTAGE)) - - delete canary - - persist_environment_url + - auto-deploy check_kube_domain + - auto-deploy download_chart + - auto-deploy ensure_namespace + - auto-deploy initialize_tiller + - auto-deploy create_secret + - auto-deploy deploy rollout $ROLLOUT_PERCENTAGE + - auto-deploy scale stable $((100-ROLLOUT_PERCENTAGE)) + - auto-deploy delete canary + - auto-deploy persist_environment_url environment: name: production url: http://$CI_PROJECT_PATH_SLUG.$KUBE_INGRESS_BASE_DOMAIN @@ -240,331 +243,3 @@ rollout 100%: <<: *manual_rollout_template <<: *production_template allow_failure: false - -.deploy_helpers: &deploy_helpers | - [[ "$TRACE" ]] && set -x - export RELEASE_NAME=${HELM_RELEASE_NAME:-$CI_ENVIRONMENT_SLUG} - auto_database_url=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${RELEASE_NAME}-postgres:5432/${POSTGRES_DB} - export DATABASE_URL=${DATABASE_URL-$auto_database_url} - export TILLER_NAMESPACE=$KUBE_NAMESPACE - - function get_replicas() { - track="${1:-stable}" - percentage="${2:-100}" - - env_track=$( echo $track | tr -s '[:lower:]' '[:upper:]' ) - env_slug=$( echo ${CI_ENVIRONMENT_SLUG//-/_} | tr -s '[:lower:]' '[:upper:]' ) - - if [[ "$track" == "stable" ]] || [[ "$track" == "rollout" ]]; then - # for stable track get number of replicas from `PRODUCTION_REPLICAS` - eval new_replicas=\$${env_slug}_REPLICAS - if [[ -z "$new_replicas" ]]; then - new_replicas=$REPLICAS - fi - else - # for all tracks get number of replicas from `CANARY_PRODUCTION_REPLICAS` - eval new_replicas=\$${env_track}_${env_slug}_REPLICAS - if [[ -z "$new_replicas" ]]; then - eval new_replicas=\${env_track}_REPLICAS - fi - fi - - replicas="${new_replicas:-1}" - replicas="$(($replicas * $percentage / 100))" - - # always return at least one replicas - if [[ $replicas -gt 0 ]]; then - echo "$replicas" - else - echo 1 - fi - } - - # Extracts variables prefixed with K8S_SECRET_ - # and creates a Kubernetes secret. - # - # e.g. If we have the following environment variables: - # K8S_SECRET_A=value1 - # K8S_SECRET_B=multi\ word\ value - # - # Then we will create a secret with the following key-value pairs: - # data: - # A: dmFsdWUxCg== - # B: bXVsdGkgd29yZCB2YWx1ZQo= - function create_application_secret() { - track="${1-stable}" - export APPLICATION_SECRET_NAME=$(application_secret_name "$track") - - env | sed -n "s/^K8S_SECRET_\(.*\)$/\1/p" > k8s_prefixed_variables - - kubectl create secret \ - -n "$KUBE_NAMESPACE" generic "$APPLICATION_SECRET_NAME" \ - --from-env-file k8s_prefixed_variables -o yaml --dry-run | - kubectl replace -n "$KUBE_NAMESPACE" --force -f - - - export APPLICATION_SECRET_CHECKSUM=$(cat k8s_prefixed_variables | sha256sum | cut -d ' ' -f 1) - - rm k8s_prefixed_variables - } - - function deploy_name() { - name="$RELEASE_NAME" - track="${1-stable}" - - if [[ "$track" != "stable" ]]; then - name="$name-$track" - fi - - echo $name - } - - function application_secret_name() { - track="${1-stable}" - name=$(deploy_name "$track") - - echo "${name}-secret" - } - - function deploy() { - track="${1-stable}" - percentage="${2:-100}" - name=$(deploy_name "$track") - - if [[ -z "$CI_COMMIT_TAG" ]]; then - image_repository=${CI_APPLICATION_REPOSITORY:-$CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG} - image_tag=${CI_APPLICATION_TAG:-$CI_COMMIT_SHA} - else - image_repository=${CI_APPLICATION_REPOSITORY:-$CI_REGISTRY_IMAGE} - image_tag=${CI_APPLICATION_TAG:-$CI_COMMIT_TAG} - fi - - service_enabled="true" - postgres_enabled="$POSTGRES_ENABLED" - - # if track is different than stable, - # re-use all attached resources - if [[ "$track" != "stable" ]]; then - service_enabled="false" - postgres_enabled="false" - fi - - replicas=$(get_replicas "$track" "$percentage") - - if [[ "$CI_PROJECT_VISIBILITY" != "public" ]]; then - secret_name='gitlab-registry' - else - secret_name='' - fi - - create_application_secret "$track" - - env_slug=$(echo ${CI_ENVIRONMENT_SLUG//-/_} | tr -s '[:lower:]' '[:upper:]') - eval env_ADDITIONAL_HOSTS=\$${env_slug}_ADDITIONAL_HOSTS - if [ -n "$env_ADDITIONAL_HOSTS" ]; then - additional_hosts="{$env_ADDITIONAL_HOSTS}" - elif [ -n "$ADDITIONAL_HOSTS" ]; then - additional_hosts="{$ADDITIONAL_HOSTS}" - fi - - if [[ -n "$DB_INITIALIZE" && -z "$(helm ls -q "^$name$")" ]]; then - echo "Deploying first release with database initialization..." - helm upgrade --install \ - --wait \ - --set service.enabled="$service_enabled" \ - --set gitlab.app="$CI_PROJECT_PATH_SLUG" \ - --set gitlab.env="$CI_ENVIRONMENT_SLUG" \ - --set releaseOverride="$RELEASE_NAME" \ - --set image.repository="$image_repository" \ - --set image.tag="$image_tag" \ - --set image.pullPolicy=IfNotPresent \ - --set image.secrets[0].name="$secret_name" \ - --set application.track="$track" \ - --set application.database_url="$DATABASE_URL" \ - --set application.secretName="$APPLICATION_SECRET_NAME" \ - --set application.secretChecksum="$APPLICATION_SECRET_CHECKSUM" \ - --set service.commonName="le-$CI_PROJECT_ID.$KUBE_INGRESS_BASE_DOMAIN" \ - --set service.url="$CI_ENVIRONMENT_URL" \ - --set service.additionalHosts="$additional_hosts" \ - --set replicaCount="$replicas" \ - --set postgresql.enabled="$postgres_enabled" \ - --set postgresql.nameOverride="postgres" \ - --set postgresql.postgresUser="$POSTGRES_USER" \ - --set postgresql.postgresPassword="$POSTGRES_PASSWORD" \ - --set postgresql.postgresDatabase="$POSTGRES_DB" \ - --set postgresql.imageTag="$POSTGRES_VERSION" \ - --set application.initializeCommand="$DB_INITIALIZE" \ - $HELM_UPGRADE_EXTRA_ARGS \ - --namespace="$KUBE_NAMESPACE" \ - "$name" \ - chart/ - - echo "Deploying second release..." - helm upgrade --reuse-values \ - --wait \ - --set application.initializeCommand="" \ - --set application.migrateCommand="$DB_MIGRATE" \ - $HELM_UPGRADE_EXTRA_ARGS \ - --namespace="$KUBE_NAMESPACE" \ - "$name" \ - chart/ - else - echo "Deploying new release..." - helm upgrade --install \ - --wait \ - --set service.enabled="$service_enabled" \ - --set gitlab.app="$CI_PROJECT_PATH_SLUG" \ - --set gitlab.env="$CI_ENVIRONMENT_SLUG" \ - --set releaseOverride="$RELEASE_NAME" \ - --set image.repository="$image_repository" \ - --set image.tag="$image_tag" \ - --set image.pullPolicy=IfNotPresent \ - --set image.secrets[0].name="$secret_name" \ - --set application.track="$track" \ - --set application.database_url="$DATABASE_URL" \ - --set application.secretName="$APPLICATION_SECRET_NAME" \ - --set application.secretChecksum="$APPLICATION_SECRET_CHECKSUM" \ - --set service.commonName="le-$CI_PROJECT_ID.$KUBE_INGRESS_BASE_DOMAIN" \ - --set service.url="$CI_ENVIRONMENT_URL" \ - --set service.additionalHosts="$additional_hosts" \ - --set replicaCount="$replicas" \ - --set postgresql.enabled="$postgres_enabled" \ - --set postgresql.nameOverride="postgres" \ - --set postgresql.postgresUser="$POSTGRES_USER" \ - --set postgresql.postgresPassword="$POSTGRES_PASSWORD" \ - --set postgresql.postgresDatabase="$POSTGRES_DB" \ - --set postgresql.imageTag="$POSTGRES_VERSION" \ - --set application.migrateCommand="$DB_MIGRATE" \ - $HELM_UPGRADE_EXTRA_ARGS \ - --namespace="$KUBE_NAMESPACE" \ - "$name" \ - chart/ - fi - - if [[ -z "$ROLLOUT_STATUS_DISABLED" ]]; then - kubectl rollout status -n "$KUBE_NAMESPACE" -w "$ROLLOUT_RESOURCE_TYPE/$name" - fi - } - - function scale() { - track="${1-stable}" - percentage="${2-100}" - name=$(deploy_name "$track") - - replicas=$(get_replicas "$track" "$percentage") - - if [[ -n "$(helm ls -q "^$name$")" ]]; then - helm upgrade --reuse-values \ - --wait \ - --set replicaCount="$replicas" \ - --namespace="$KUBE_NAMESPACE" \ - "$name" \ - chart/ - fi - } - - function install_dependencies() { - apk add -U openssl curl tar gzip bash ca-certificates git - curl -sSL -o /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub - curl -sSL -O https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.28-r0/glibc-2.28-r0.apk - apk add glibc-2.28-r0.apk - rm glibc-2.28-r0.apk - - curl -sS "https://kubernetes-helm.storage.googleapis.com/helm-v${HELM_VERSION}-linux-amd64.tar.gz" | tar zx - mv linux-amd64/helm /usr/bin/ - mv linux-amd64/tiller /usr/bin/ - helm version --client - tiller -version - - curl -sSL -o /usr/bin/kubectl "https://storage.googleapis.com/kubernetes-release/release/v${KUBERNETES_VERSION}/bin/linux/amd64/kubectl" - chmod +x /usr/bin/kubectl - kubectl version --client - } - - function download_chart() { - if [[ ! -d chart ]]; then - auto_chart=${AUTO_DEVOPS_CHART:-gitlab/auto-deploy-app} - auto_chart_name=$(basename $auto_chart) - auto_chart_name=${auto_chart_name%.tgz} - auto_chart_name=${auto_chart_name%.tar.gz} - else - auto_chart="chart" - auto_chart_name="chart" - fi - - helm init --client-only - helm repo add ${AUTO_DEVOPS_CHART_REPOSITORY_NAME:-gitlab} ${AUTO_DEVOPS_CHART_REPOSITORY:-https://charts.gitlab.io} ${AUTO_DEVOPS_CHART_REPOSITORY_USERNAME:+"--username" "$AUTO_DEVOPS_CHART_REPOSITORY_USERNAME"} ${AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD:+"--password" "$AUTO_DEVOPS_CHART_REPOSITORY_PASSWORD"} - if [[ ! -d "$auto_chart" ]]; then - helm fetch ${auto_chart} --untar - fi - if [ "$auto_chart_name" != "chart" ]; then - mv ${auto_chart_name} chart - fi - - helm dependency update chart/ - helm dependency build chart/ - } - - function ensure_namespace() { - kubectl get namespace "$KUBE_NAMESPACE" || kubectl create namespace "$KUBE_NAMESPACE" - } - - function check_kube_domain() { - if [[ -z "$KUBE_INGRESS_BASE_DOMAIN" ]]; then - echo "In order to deploy or use Review Apps," - echo "KUBE_INGRESS_BASE_DOMAIN variables must be set" - echo "From 11.8, you can set KUBE_INGRESS_BASE_DOMAIN in cluster settings" - echo "or by defining a variable at group or project level." - echo "You can also manually add it in .gitlab-ci.yml" - false - else - true - fi - } - - function initialize_tiller() { - echo "Checking Tiller..." - - export HELM_HOST="localhost:44134" - tiller -listen ${HELM_HOST} -alsologtostderr > /dev/null 2>&1 & - echo "Tiller is listening on ${HELM_HOST}" - - if ! helm version --debug; then - echo "Failed to init Tiller." - return 1 - fi - echo "" - } - - function create_secret() { - echo "Create secret..." - if [[ "$CI_PROJECT_VISIBILITY" == "public" ]]; then - return - fi - - kubectl create secret -n "$KUBE_NAMESPACE" \ - docker-registry gitlab-registry \ - --docker-server="$CI_REGISTRY" \ - --docker-username="${CI_DEPLOY_USER:-$CI_REGISTRY_USER}" \ - --docker-password="${CI_DEPLOY_PASSWORD:-$CI_REGISTRY_PASSWORD}" \ - --docker-email="$GITLAB_USER_EMAIL" \ - -o yaml --dry-run | kubectl replace -n "$KUBE_NAMESPACE" --force -f - - } - - function persist_environment_url() { - echo $CI_ENVIRONMENT_URL > environment_url.txt - } - - function delete() { - track="${1-stable}" - name=$(deploy_name "$track") - - if [[ -n "$(helm ls -q "^$name$")" ]]; then - helm delete --purge "$name" - fi - - secret_name=$(application_secret_name "$track") - kubectl delete secret --ignore-not-found -n "$KUBE_NAMESPACE" "$secret_name" - } - -before_script: - - *deploy_helpers diff --git a/lib/gitlab/object_hierarchy.rb b/lib/gitlab/object_hierarchy.rb index 38b32770e90..c06f106ffe1 100644 --- a/lib/gitlab/object_hierarchy.rb +++ b/lib/gitlab/object_hierarchy.rb @@ -32,11 +32,6 @@ module Gitlab # Returns the maximum depth starting from the base # A base object with no children has a maximum depth of `1` def max_descendants_depth - unless hierarchy_supported? - # This makes the return value consistent with the case where hierarchy is supported - return descendants_base.exists? ? 1 : nil - end - base_and_descendants(with_depth: true).maximum(DEPTH_COLUMN) end @@ -66,8 +61,6 @@ module Gitlab # each parent. # rubocop: disable CodeReuse/ActiveRecord def base_and_ancestors(upto: nil, hierarchy_order: nil) - return ancestors_base unless hierarchy_supported? - recursive_query = base_and_ancestors_cte(upto, hierarchy_order).apply_to(model.all) recursive_query = recursive_query.order(depth: hierarchy_order) if hierarchy_order @@ -81,10 +74,6 @@ module Gitlab # When `with_depth` is `true`, a `depth` column is included where it starts with `1` for the base objects # and incremented as we go down the descendant tree def base_and_descendants(with_depth: false) - unless hierarchy_supported? - return with_depth ? descendants_base.select("1 as #{DEPTH_COLUMN}", objects_table[Arel.star]) : descendants_base - end - read_only(base_and_descendants_cte(with_depth: with_depth).apply_to(model.all)) end @@ -112,8 +101,6 @@ module Gitlab # If nested objects are not supported, ancestors_base is returned. # rubocop: disable CodeReuse/ActiveRecord def all_objects - return ancestors_base unless hierarchy_supported? - ancestors = base_and_ancestors_cte descendants = base_and_descendants_cte @@ -135,10 +122,6 @@ module Gitlab private - def hierarchy_supported? - Gitlab::Database.postgresql? - end - # rubocop: disable CodeReuse/ActiveRecord def base_and_ancestors_cte(stop_id = nil, hierarchy_order = nil) cte = SQL::RecursiveCTE.new(:base_and_ancestors) diff --git a/lib/gitlab/project_authorizations.rb b/lib/gitlab/project_authorizations.rb new file mode 100644 index 00000000000..a9270cd536e --- /dev/null +++ b/lib/gitlab/project_authorizations.rb @@ -0,0 +1,121 @@ +# frozen_string_literal: true + +# This class relies on Common Table Expressions to efficiently get all data, +# including data for nested groups. +module Gitlab + class ProjectAuthorizations + attr_reader :user + + # user - The User object for which to calculate the authorizations. + def initialize(user) + @user = user + end + + def calculate + cte = recursive_cte + cte_alias = cte.table.alias(Group.table_name) + projects = Project.arel_table + links = ProjectGroupLink.arel_table + + relations = [ + # The project a user has direct access to. + user.projects.select_for_project_authorization, + + # The personal projects of the user. + user.personal_projects.select_as_maintainer_for_project_authorization, + + # Projects that belong directly to any of the groups the user has + # access to. + Namespace + .unscoped + .select([alias_as_column(projects[:id], 'project_id'), + cte_alias[:access_level]]) + .from(cte_alias) + .joins(:projects), + + # Projects shared with any of the namespaces the user has access to. + Namespace + .unscoped + .select([ + links[:project_id], + least(cte_alias[:access_level], links[:group_access], 'access_level') + ]) + .from(cte_alias) + .joins('INNER JOIN project_group_links ON project_group_links.group_id = namespaces.id') + .joins('INNER JOIN projects ON projects.id = project_group_links.project_id') + .joins('INNER JOIN namespaces p_ns ON p_ns.id = projects.namespace_id') + .where('p_ns.share_with_group_lock IS FALSE') + ] + + ProjectAuthorization + .unscoped + .with + .recursive(cte.to_arel) + .select_from_union(relations) + end + + private + + # Builds a recursive CTE that gets all the groups the current user has + # access to, including any nested groups. + def recursive_cte + cte = Gitlab::SQL::RecursiveCTE.new(:namespaces_cte) + members = Member.arel_table + namespaces = Namespace.arel_table + + # Namespaces the user is a member of. + cte << user.groups + .select([namespaces[:id], members[:access_level]]) + .except(:order) + + # Sub groups of any groups the user is a member of. + cte << Group.select([ + namespaces[:id], + greatest(members[:access_level], cte.table[:access_level], 'access_level') + ]) + .joins(join_cte(cte)) + .joins(join_members) + .except(:order) + + cte + end + + # Builds a LEFT JOIN to join optional memberships onto the CTE. + def join_members + members = Member.arel_table + namespaces = Namespace.arel_table + + cond = members[:source_id] + .eq(namespaces[:id]) + .and(members[:source_type].eq('Namespace')) + .and(members[:requested_at].eq(nil)) + .and(members[:user_id].eq(user.id)) + + Arel::Nodes::OuterJoin.new(members, Arel::Nodes::On.new(cond)) + end + + # Builds an INNER JOIN to join namespaces onto the CTE. + def join_cte(cte) + namespaces = Namespace.arel_table + cond = cte.table[:id].eq(namespaces[:parent_id]) + + Arel::Nodes::InnerJoin.new(cte.table, Arel::Nodes::On.new(cond)) + end + + def greatest(left, right, column_alias) + sql_function('GREATEST', [left, right], column_alias) + end + + def least(left, right, column_alias) + sql_function('LEAST', [left, right], column_alias) + end + + def sql_function(name, args, column_alias) + alias_as_column(Arel::Nodes::NamedFunction.new(name, args), column_alias) + end + + def alias_as_column(value, alias_to) + Arel::Nodes::As.new(value, Arel::Nodes::SqlLiteral.new(alias_to)) + end + end +end diff --git a/lib/gitlab/project_authorizations/with_nested_groups.rb b/lib/gitlab/project_authorizations/with_nested_groups.rb deleted file mode 100644 index 2372a316ab0..00000000000 --- a/lib/gitlab/project_authorizations/with_nested_groups.rb +++ /dev/null @@ -1,125 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module ProjectAuthorizations - # Calculating new project authorizations when supporting nested groups. - # - # This class relies on Common Table Expressions to efficiently get all data, - # including data for nested groups. As a result this class can only be used - # on PostgreSQL. - class WithNestedGroups - attr_reader :user - - # user - The User object for which to calculate the authorizations. - def initialize(user) - @user = user - end - - def calculate - cte = recursive_cte - cte_alias = cte.table.alias(Group.table_name) - projects = Project.arel_table - links = ProjectGroupLink.arel_table - - relations = [ - # The project a user has direct access to. - user.projects.select_for_project_authorization, - - # The personal projects of the user. - user.personal_projects.select_as_maintainer_for_project_authorization, - - # Projects that belong directly to any of the groups the user has - # access to. - Namespace - .unscoped - .select([alias_as_column(projects[:id], 'project_id'), - cte_alias[:access_level]]) - .from(cte_alias) - .joins(:projects), - - # Projects shared with any of the namespaces the user has access to. - Namespace - .unscoped - .select([links[:project_id], - least(cte_alias[:access_level], - links[:group_access], - 'access_level')]) - .from(cte_alias) - .joins('INNER JOIN project_group_links ON project_group_links.group_id = namespaces.id') - .joins('INNER JOIN projects ON projects.id = project_group_links.project_id') - .joins('INNER JOIN namespaces p_ns ON p_ns.id = projects.namespace_id') - .where('p_ns.share_with_group_lock IS FALSE') - ] - - ProjectAuthorization - .unscoped - .with - .recursive(cte.to_arel) - .select_from_union(relations) - end - - private - - # Builds a recursive CTE that gets all the groups the current user has - # access to, including any nested groups. - def recursive_cte - cte = Gitlab::SQL::RecursiveCTE.new(:namespaces_cte) - members = Member.arel_table - namespaces = Namespace.arel_table - - # Namespaces the user is a member of. - cte << user.groups - .select([namespaces[:id], members[:access_level]]) - .except(:order) - - # Sub groups of any groups the user is a member of. - cte << Group.select([namespaces[:id], - greatest(members[:access_level], - cte.table[:access_level], 'access_level')]) - .joins(join_cte(cte)) - .joins(join_members) - .except(:order) - - cte - end - - # Builds a LEFT JOIN to join optional memberships onto the CTE. - def join_members - members = Member.arel_table - namespaces = Namespace.arel_table - - cond = members[:source_id] - .eq(namespaces[:id]) - .and(members[:source_type].eq('Namespace')) - .and(members[:requested_at].eq(nil)) - .and(members[:user_id].eq(user.id)) - - Arel::Nodes::OuterJoin.new(members, Arel::Nodes::On.new(cond)) - end - - # Builds an INNER JOIN to join namespaces onto the CTE. - def join_cte(cte) - namespaces = Namespace.arel_table - cond = cte.table[:id].eq(namespaces[:parent_id]) - - Arel::Nodes::InnerJoin.new(cte.table, Arel::Nodes::On.new(cond)) - end - - def greatest(left, right, column_alias) - sql_function('GREATEST', [left, right], column_alias) - end - - def least(left, right, column_alias) - sql_function('LEAST', [left, right], column_alias) - end - - def sql_function(name, args, column_alias) - alias_as_column(Arel::Nodes::NamedFunction.new(name, args), column_alias) - end - - def alias_as_column(value, alias_to) - Arel::Nodes::As.new(value, Arel::Nodes::SqlLiteral.new(alias_to)) - end - end - end -end diff --git a/lib/gitlab/project_authorizations/without_nested_groups.rb b/lib/gitlab/project_authorizations/without_nested_groups.rb deleted file mode 100644 index 50b41b17649..00000000000 --- a/lib/gitlab/project_authorizations/without_nested_groups.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module ProjectAuthorizations - # Calculating new project authorizations when not supporting nested groups. - class WithoutNestedGroups - attr_reader :user - - # user - The User object for which to calculate the authorizations. - def initialize(user) - @user = user - end - - def calculate - relations = [ - # Projects the user is a direct member of - user.projects.select_for_project_authorization, - - # Personal projects - user.personal_projects.select_as_maintainer_for_project_authorization, - - # Projects of groups the user is a member of - user.groups_projects.select_for_project_authorization, - - # Projects shared with groups the user is a member of - user.groups.joins(:shared_projects).select_for_project_authorization - ] - - ProjectAuthorization - .unscoped - .select_from_union(relations) - end - end - end -end diff --git a/lib/gitlab/push_options.rb b/lib/gitlab/push_options.rb index b96590af08e..682edfc4259 100644 --- a/lib/gitlab/push_options.rb +++ b/lib/gitlab/push_options.rb @@ -6,9 +6,11 @@ module Gitlab merge_request: { keys: [ :create, + :description, :merge_when_pipeline_succeeds, :remove_source_branch, - :target + :target, + :title ] }, ci: { diff --git a/lib/gitlab/sidekiq_middleware/memory_killer.rb b/lib/gitlab/sidekiq_middleware/memory_killer.rb index 671d795ec33..4b10f921ed8 100644 --- a/lib/gitlab/sidekiq_middleware/memory_killer.rb +++ b/lib/gitlab/sidekiq_middleware/memory_killer.rb @@ -14,9 +14,12 @@ module Gitlab # shut Sidekiq down MUTEX = Mutex.new + attr_reader :worker + def call(worker, job, queue) yield + @worker = worker current_rss = get_rss return unless MAX_RSS > 0 && current_rss > MAX_RSS @@ -25,9 +28,11 @@ module Gitlab # Return if another thread is already waiting to shut Sidekiq down next unless MUTEX.try_lock - Sidekiq.logger.warn "Sidekiq worker PID-#{pid} current RSS #{current_rss}"\ - " exceeds maximum RSS #{MAX_RSS} after finishing job #{worker.class} JID-#{job['jid']}" - Sidekiq.logger.warn "Sidekiq worker PID-#{pid} will stop fetching new jobs in #{GRACE_TIME} seconds, and will be shut down #{SHUTDOWN_WAIT} seconds later" + warn("Sidekiq worker PID-#{pid} current RSS #{current_rss}"\ + " exceeds maximum RSS #{MAX_RSS} after finishing job #{worker.class} JID-#{job['jid']}") + + warn("Sidekiq worker PID-#{pid} will stop fetching new jobs"\ + " in #{GRACE_TIME} seconds, and will be shut down #{SHUTDOWN_WAIT} seconds later") # Wait `GRACE_TIME` to give the memory intensive job time to finish. # Then, tell Sidekiq to stop fetching new jobs. @@ -59,24 +64,28 @@ module Gitlab def wait_and_signal_pgroup(time, signal, explanation) return wait_and_signal(time, signal, explanation) unless Process.getpgrp == pid - Sidekiq.logger.warn "waiting #{time} seconds before sending Sidekiq worker PGRP-#{pid} #{signal} (#{explanation})" + warn("waiting #{time} seconds before sending Sidekiq worker PGRP-#{pid} #{signal} (#{explanation})", signal: signal) sleep(time) - Sidekiq.logger.warn "sending Sidekiq worker PGRP-#{pid} #{signal} (#{explanation})" + warn("sending Sidekiq worker PGRP-#{pid} #{signal} (#{explanation})", signal: signal) Process.kill(signal, 0) end def wait_and_signal(time, signal, explanation) - Sidekiq.logger.warn "waiting #{time} seconds before sending Sidekiq worker PID-#{pid} #{signal} (#{explanation})" + warn("waiting #{time} seconds before sending Sidekiq worker PID-#{pid} #{signal} (#{explanation})", signal: signal) sleep(time) - Sidekiq.logger.warn "sending Sidekiq worker PID-#{pid} #{signal} (#{explanation})" + warn("sending Sidekiq worker PID-#{pid} #{signal} (#{explanation})", signal: signal) Process.kill(signal, pid) end def pid Process.pid end + + def warn(message, signal: nil) + Sidekiq.logger.warn(class: worker.class, pid: pid, signal: signal, message: message) + end end end end diff --git a/lib/gitlab/submodule_links.rb b/lib/gitlab/submodule_links.rb index a6c0369d864..18fd604a3b0 100644 --- a/lib/gitlab/submodule_links.rb +++ b/lib/gitlab/submodule_links.rb @@ -9,7 +9,7 @@ module Gitlab end def for(submodule, sha) - submodule_url = submodule_url_for(sha)[submodule.path] + submodule_url = submodule_url_for(sha, submodule.path) SubmoduleHelper.submodule_links_for_url(submodule.id, submodule_url, repository) end @@ -17,10 +17,15 @@ module Gitlab attr_reader :repository - def submodule_url_for(sha) - strong_memoize(:"submodule_links_for_#{sha}") do + def submodule_urls_for(sha) + strong_memoize(:"submodule_urls_for_#{sha}") do repository.submodule_urls_for(sha) end end + + def submodule_url_for(sha, path) + urls = submodule_urls_for(sha) + urls && urls[path] + end end end diff --git a/lib/gitlab/utils.rb b/lib/gitlab/utils.rb index 31c9e18c996..c66ce0434a4 100644 --- a/lib/gitlab/utils.rb +++ b/lib/gitlab/utils.rb @@ -22,7 +22,7 @@ module Gitlab end def force_utf8(str) - str.force_encoding(Encoding::UTF_8) + str.dup.force_encoding(Encoding::UTF_8) end def ensure_utf8_size(str, bytes:) diff --git a/lib/prometheus/pid_provider.rb b/lib/prometheus/pid_provider.rb index c92522c73c5..e0f7e7e0a9e 100644 --- a/lib/prometheus/pid_provider.rb +++ b/lib/prometheus/pid_provider.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require 'prometheus/client/support/unicorn' - module Prometheus module PidProvider extend self @@ -10,29 +8,38 @@ module Prometheus if Sidekiq.server? 'sidekiq' elsif defined?(Unicorn::Worker) - "unicorn_#{unicorn_worker_id}" + unicorn_worker_id elsif defined?(::Puma) - "puma_#{puma_worker_id}" + puma_worker_id else - "process_#{Process.pid}" + unknown_process_id end end private - # This is not fully accurate as we don't really know if the nil returned - # is actually means we're on master or not. - # Follow up issue was created to address this problem and - # to introduce more structrured approach to a current process discovery: - # https://gitlab.com/gitlab-org/gitlab-ce/issues/64740 def unicorn_worker_id - ::Prometheus::Client::Support::Unicorn.worker_id || 'master' + if matches = process_name.match(/unicorn.*worker\[([0-9]+)\]/) + "unicorn_#{matches[1]}" + elsif process_name =~ /unicorn/ + "unicorn_master" + else + unknown_process_id + end end - # See the comment for #unicorn_worker_id def puma_worker_id - match = process_name.match(/cluster worker ([0-9]+):/) - match ? match[1] : 'master' + if matches = process_name.match(/puma.*cluster worker ([0-9]+):/) + "puma_#{matches[1]}" + elsif process_name =~ /puma/ + "puma_master" + else + unknown_process_id + end + end + + def unknown_process_id + "process_#{Process.pid}" end def process_name diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 996f082f197..114d245b688 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -2383,6 +2383,9 @@ msgstr "" msgid "ClusterIntegration|Certificate Authority bundle (PEM format)" msgstr "" +msgid "ClusterIntegration|Choose a prefix to be used for your namespaces. Defaults to your project path." +msgstr "" + msgid "ClusterIntegration|Choose which applications to install on your Kubernetes cluster. Helm Tiller is required to install any of the following applications." msgstr "" @@ -2620,6 +2623,9 @@ msgstr "" msgid "ClusterIntegration|Project namespace (optional, unique)" msgstr "" +msgid "ClusterIntegration|Project namespace prefix (optional, unique)" +msgstr "" + msgid "ClusterIntegration|Prometheus" msgstr "" @@ -2728,6 +2734,9 @@ msgstr "" msgid "ClusterIntegration|The endpoint is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time." msgstr "" +msgid "ClusterIntegration|The namespace associated with your project. This will be used for deploy boards, pod logs, and Web terminals." +msgstr "" + msgid "ClusterIntegration|There was a problem authenticating with your cluster. Please ensure your CA Certificate and Token are valid." msgstr "" @@ -3932,6 +3941,12 @@ msgstr "" msgid "Edit public deploy key" msgstr "" +msgid "Editor|%{mdLinkStart}Markdown is supported%{mdLinkEnd}" +msgstr "" + +msgid "Editor|%{mdLinkStart}Markdown%{mdLinkEnd} and %{actionsLinkStart}quick actions%{actionsLinkEnd} are supported" +msgstr "" + msgid "Email" msgstr "" @@ -6373,18 +6388,12 @@ msgstr "" msgid "Mark to do as done" msgstr "" -msgid "Markdown" -msgstr "" - msgid "Markdown Help" msgstr "" msgid "Markdown enabled" msgstr "" -msgid "Markdown is supported" -msgstr "" - msgid "Marks this issue as a duplicate of %{duplicate_reference}." msgstr "" @@ -13305,9 +13314,6 @@ msgstr "" msgid "project avatar" msgstr "" -msgid "quick actions" -msgstr "" - msgid "register" msgstr "" diff --git a/package.json b/package.json index 4264064c93d..773918524f9 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "@babel/preset-env": "^7.4.4", "@gitlab/csslab": "^1.9.0", "@gitlab/svgs": "^1.67.0", - "@gitlab/ui": "^5.7.1", + "@gitlab/ui": "^5.9.0", "apollo-cache-inmemory": "^1.5.1", "apollo-client": "^2.5.1", "apollo-link": "^1.2.11", @@ -99,7 +99,7 @@ "mermaid": "^8.2.3", "monaco-editor": "^0.15.6", "monaco-editor-webpack-plugin": "^1.7.0", - "mousetrap": "^1.4.6", + "mousetrap": "1.4.6", "pdfjs-dist": "^2.0.943", "pikaday": "^1.6.1", "popper.js": "^1.14.7", diff --git a/qa/qa/page/project/issue/show.rb b/qa/qa/page/project/issue/show.rb index b59540d0377..507dccb52d0 100644 --- a/qa/qa/page/project/issue/show.rb +++ b/qa/qa/page/project/issue/show.rb @@ -70,7 +70,10 @@ module QA end def select_labels_and_refresh(labels) - click_element(:edit_link_labels) + Support::Retrier.retry_until do + click_element(:edit_link_labels) + has_element?(:dropdown_menu_labels, text: labels.first) + end labels.each do |label| within_element(:dropdown_menu_labels, text: label) do diff --git a/qa/qa/page/project/settings/ci_cd.rb b/qa/qa/page/project/settings/ci_cd.rb index b8c5c563da6..ae826fb3a32 100644 --- a/qa/qa/page/project/settings/ci_cd.rb +++ b/qa/qa/page/project/settings/ci_cd.rb @@ -8,33 +8,32 @@ module QA include Common view 'app/views/projects/settings/ci_cd/show.html.haml' do - element :autodevops_settings - element :runners_settings - element :variables_settings + element :autodevops_settings_content + element :runners_settings_content + element :variables_settings_content end view 'app/views/projects/settings/ci_cd/_autodevops_form.html.haml' do - element :enable_auto_devops_field, 'check_box :enabled' # rubocop:disable QA/ElementWithPattern - element :enable_auto_devops_button, "%strong= s_('CICD|Default to Auto DevOps pipeline')" # rubocop:disable QA/ElementWithPattern - element :save_changes_button, "submit _('Save changes')" # rubocop:disable QA/ElementWithPattern + element :enable_autodevops_checkbox + element :save_changes_button end def expand_runners_settings(&block) - expand_section(:runners_settings) do + expand_section(:runners_settings_content) do Settings::Runners.perform(&block) end end def expand_ci_variables(&block) - expand_section(:variables_settings) do + expand_section(:variables_settings_content) do Settings::CiVariables.perform(&block) end end def enable_auto_devops - expand_section(:autodevops_settings) do - check 'Default to Auto DevOps pipeline' - click_on 'Save changes' + expand_section(:autodevops_settings_content) do + check_element :enable_autodevops_checkbox + click_element :save_changes_button end end end diff --git a/qa/qa/page/project/sub_menus/project.rb b/qa/qa/page/project/sub_menus/project.rb index 5e0ee3c274a..6f1bc131f84 100644 --- a/qa/qa/page/project/sub_menus/project.rb +++ b/qa/qa/page/project/sub_menus/project.rb @@ -10,7 +10,7 @@ module QA def self.included(base) base.class_eval do view 'app/views/layouts/nav/sidebar/_project.html.haml' do - element :link_project + element :project_link end end end @@ -18,7 +18,7 @@ module QA def click_project retry_on_exception do within_sidebar do - click_element(:link_project) + click_element(:project_link) end end end diff --git a/spec/controllers/boards/issues_controller_spec.rb b/spec/controllers/boards/issues_controller_spec.rb index 0db58fbefc1..d54f7ad33cf 100644 --- a/spec/controllers/boards/issues_controller_spec.rb +++ b/spec/controllers/boards/issues_controller_spec.rb @@ -85,7 +85,7 @@ describe Boards::IssuesController do expect { list_issues(user: user, board: group_board, list: list3) }.not_to exceed_query_limit(control_count + (2 * 8 - 1)) end - it 'avoids N+1 database queries when adding a subgroup, project, and issue', :nested_groups do + it 'avoids N+1 database queries when adding a subgroup, project, and issue' do create(:project, group: sub_group_1) create(:labeled_issue, project: project, labels: [development]) control_count = ActiveRecord::QueryRecorder.new { list_issues(user: user, board: group_board, list: list3) }.count diff --git a/spec/controllers/concerns/group_tree_spec.rb b/spec/controllers/concerns/group_tree_spec.rb index aa3cd690e3f..835c3d9b3af 100644 --- a/spec/controllers/concerns/group_tree_spec.rb +++ b/spec/controllers/concerns/group_tree_spec.rb @@ -30,7 +30,7 @@ describe GroupTree do expect(assigns(:groups)).to contain_exactly(other_group) end - context 'for subgroups', :nested_groups do + context 'for subgroups' do it 'only renders root groups when no parent was given' do create(:group, :public, parent: group) @@ -85,7 +85,7 @@ describe GroupTree do expect(json_response.first['id']).to eq(group.id) end - context 'nested groups', :nested_groups do + context 'nested groups' do it 'expands the tree when filtering' do subgroup = create(:group, :public, parent: group, name: 'filter') diff --git a/spec/controllers/dashboard/groups_controller_spec.rb b/spec/controllers/dashboard/groups_controller_spec.rb index 48373d29412..20a0951423b 100644 --- a/spec/controllers/dashboard/groups_controller_spec.rb +++ b/spec/controllers/dashboard/groups_controller_spec.rb @@ -26,7 +26,7 @@ describe Dashboard::GroupsController do expect(assigns(:groups)).to contain_exactly(member_of_group) end - context 'when rendering an expanded hierarchy with public groups you are not a member of', :nested_groups do + context 'when rendering an expanded hierarchy with public groups you are not a member of' do let!(:top_level_result) { create(:group, name: 'chef-top') } let!(:top_level_a) { create(:group, name: 'top-a') } let!(:sub_level_result_a) { create(:group, name: 'chef-sub-a', parent: top_level_a) } diff --git a/spec/controllers/groups/children_controller_spec.rb b/spec/controllers/groups/children_controller_spec.rb index 02fb971bd9a..bced300a24c 100644 --- a/spec/controllers/groups/children_controller_spec.rb +++ b/spec/controllers/groups/children_controller_spec.rb @@ -46,7 +46,7 @@ describe Groups::ChildrenController do end end - context 'for subgroups', :nested_groups do + context 'for subgroups' do let!(:public_subgroup) { create(:group, :public, parent: group) } let!(:private_subgroup) { create(:group, :private, parent: group) } let!(:public_project) { create(:project, :public, namespace: group) } @@ -292,7 +292,7 @@ describe Groups::ChildrenController do end end - context 'with subgroups and projects', :nested_groups do + context 'with subgroups and projects' do let!(:first_page_subgroups) { create_list(:group, per_page, :public, parent: group) } let!(:other_subgroup) { create(:group, :public, parent: group) } let!(:next_page_projects) { create_list(:project, per_page, :public, namespace: group) } diff --git a/spec/controllers/groups/labels_controller_spec.rb b/spec/controllers/groups/labels_controller_spec.rb index 3cc6fc6f066..98a4c50fc49 100644 --- a/spec/controllers/groups/labels_controller_spec.rb +++ b/spec/controllers/groups/labels_controller_spec.rb @@ -24,7 +24,7 @@ describe Groups::LabelsController do expect(label_ids).to match_array([label_1.title, group_label_1.title]) end - context 'with ancestor group', :nested_groups do + context 'with ancestor group' do set(:subgroup) { create(:group, parent: group) } set(:subgroup_label_1) { create(:group_label, group: subgroup, title: 'subgroup_label_1') } @@ -32,7 +32,7 @@ describe Groups::LabelsController do subgroup.add_owner(user) end - it 'returns ancestor group labels', :nested_groups do + it 'returns ancestor group labels' do get :index, params: { group_id: subgroup, include_ancestor_groups: true, only_group_labels: true }, format: :json label_ids = json_response.map {|label| label['title']} diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb index d2faef5b12b..404e61c5271 100644 --- a/spec/controllers/groups_controller_spec.rb +++ b/spec/controllers/groups_controller_spec.rb @@ -89,7 +89,7 @@ describe GroupsController do end describe 'GET #new' do - context 'when creating subgroups', :nested_groups do + context 'when creating subgroups' do [true, false].each do |can_create_group_status| context "and can_create_group is #{can_create_group_status}" do before do @@ -166,7 +166,7 @@ describe GroupsController do end end - context 'when creating subgroups', :nested_groups do + context 'when creating subgroups' do [true, false].each do |can_create_group_status| context "and can_create_group is #{can_create_group_status}" do context 'and logged in as Owner' do @@ -584,7 +584,7 @@ describe GroupsController do end end - describe 'PUT transfer', :postgresql do + describe 'PUT transfer' do before do sign_in(user) end diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb index 64a66502732..38388c21749 100644 --- a/spec/controllers/import/bitbucket_controller_spec.rb +++ b/spec/controllers/import/bitbucket_controller_spec.rb @@ -231,7 +231,7 @@ describe Import::BitbucketController do end end - context 'user has chosen an existing nested namespace and name for the project', :postgresql do + context 'user has chosen an existing nested namespace and name for the project' do let(:parent_namespace) { create(:group, name: 'foo') } let(:nested_namespace) { create(:group, name: 'bar', parent: parent_namespace) } let(:test_name) { 'test_name' } @@ -250,7 +250,7 @@ describe Import::BitbucketController do end end - context 'user has chosen a non-existent nested namespaces and name for the project', :postgresql do + context 'user has chosen a non-existent nested namespaces and name for the project' do let(:test_name) { 'test_name' } it 'takes the selected namespace and name' do @@ -281,7 +281,7 @@ describe Import::BitbucketController do end end - context 'user has chosen existent and non-existent nested namespaces and name for the project', :postgresql do + context 'user has chosen existent and non-existent nested namespaces and name for the project' do let(:test_name) { 'test_name' } let!(:parent_namespace) { create(:group, name: 'foo') } diff --git a/spec/controllers/import/gitlab_controller_spec.rb b/spec/controllers/import/gitlab_controller_spec.rb index 5af7572e74e..e465eca6c71 100644 --- a/spec/controllers/import/gitlab_controller_spec.rb +++ b/spec/controllers/import/gitlab_controller_spec.rb @@ -197,7 +197,7 @@ describe Import::GitlabController do end end - context 'user has chosen an existing nested namespace for the project', :postgresql do + context 'user has chosen an existing nested namespace for the project' do let(:parent_namespace) { create(:group, name: 'foo') } let(:nested_namespace) { create(:group, name: 'bar', parent: parent_namespace) } @@ -215,7 +215,7 @@ describe Import::GitlabController do end end - context 'user has chosen a non-existent nested namespaces for the project', :postgresql do + context 'user has chosen a non-existent nested namespaces for the project' do let(:test_name) { 'test_name' } it 'takes the selected namespace and name' do @@ -246,7 +246,7 @@ describe Import::GitlabController do end end - context 'user has chosen existent and non-existent nested namespaces and name for the project', :postgresql do + context 'user has chosen existent and non-existent nested namespaces and name for the project' do let(:test_name) { 'test_name' } let!(:parent_namespace) { create(:group, name: 'foo') } diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index f11880122b1..fa71d9b61b1 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -885,10 +885,9 @@ describe Projects::MergeRequestsController do environment2 = create(:environment, project: forked) create(:deployment, :succeed, environment: environment2, sha: sha, ref: 'master', deployable: build) - # TODO address the last 11 queries + # TODO address the last 5 queries # See https://gitlab.com/gitlab-org/gitlab-ce/issues/63952 (5 queries) - # And https://gitlab.com/gitlab-org/gitlab-ce/issues/64105 (6 queries) - leeway = 11 + leeway = 5 expect { get_ci_environments_status }.not_to exceed_all_query_limit(control_count + leeway) end end diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb index 767cee7d54a..9b2025b836c 100644 --- a/spec/controllers/projects/milestones_controller_spec.rb +++ b/spec/controllers/projects/milestones_controller_spec.rb @@ -115,7 +115,7 @@ describe Projects::MilestonesController do end end - context 'with nested groups', :nested_groups do + context 'with nested groups' do let!(:subgroup) { create(:group, :public, parent: group) } let!(:subgroup_milestone) { create(:milestone, group: subgroup) } diff --git a/spec/factories/abuse_reports.rb b/spec/factories/abuse_reports.rb index 021971ac421..578af9ed895 100644 --- a/spec/factories/abuse_reports.rb +++ b/spec/factories/abuse_reports.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :abuse_report do reporter factory: :user diff --git a/spec/factories/appearances.rb b/spec/factories/appearances.rb index dd5129229d4..bdd5964fb93 100644 --- a/spec/factories/appearances.rb +++ b/spec/factories/appearances.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Read about factories at https://github.com/thoughtbot/factory_bot FactoryBot.define do diff --git a/spec/factories/application_settings.rb b/spec/factories/application_settings.rb index 00c063c49f8..90b6b9e648a 100644 --- a/spec/factories/application_settings.rb +++ b/spec/factories/application_settings.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :application_setting do default_projects_limit 42 diff --git a/spec/factories/award_emoji.rb b/spec/factories/award_emoji.rb index 43753fa650c..a8bb806381e 100644 --- a/spec/factories/award_emoji.rb +++ b/spec/factories/award_emoji.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :award_emoji do name "thumbsup" diff --git a/spec/factories/badge.rb b/spec/factories/badge.rb index b87ece946cb..1d4e29014cc 100644 --- a/spec/factories/badge.rb +++ b/spec/factories/badge.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do trait :base_badge do link_url { generate(:url) } diff --git a/spec/factories/boards.rb b/spec/factories/boards.rb index 6524bb4830c..a5aff5c7504 100644 --- a/spec/factories/boards.rb +++ b/spec/factories/boards.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :board do transient do diff --git a/spec/factories/broadcast_messages.rb b/spec/factories/broadcast_messages.rb index 1a2be5e9552..2a30e2034b1 100644 --- a/spec/factories/broadcast_messages.rb +++ b/spec/factories/broadcast_messages.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :broadcast_message do message "MyText" diff --git a/spec/factories/chat_names.rb b/spec/factories/chat_names.rb index 56993e5da18..07bf990162f 100644 --- a/spec/factories/chat_names.rb +++ b/spec/factories/chat_names.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :chat_name, class: ChatName do user factory: :user diff --git a/spec/factories/chat_teams.rb b/spec/factories/chat_teams.rb index d048c46d6b6..52628e6d53d 100644 --- a/spec/factories/chat_teams.rb +++ b/spec/factories/chat_teams.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :chat_team, class: ChatTeam do sequence(:team_id) { |n| "abcdefghijklm#{n}" } diff --git a/spec/factories/ci/bridge.rb b/spec/factories/ci/bridge.rb index 7cb5900f2b7..6491b9dca19 100644 --- a/spec/factories/ci/bridge.rb +++ b/spec/factories/ci/bridge.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :ci_bridge, class: Ci::Bridge do name 'bridge' diff --git a/spec/factories/ci/build_trace_chunks.rb b/spec/factories/ci/build_trace_chunks.rb index 3e8e2736423..492dc47f083 100644 --- a/spec/factories/ci/build_trace_chunks.rb +++ b/spec/factories/ci/build_trace_chunks.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :ci_build_trace_chunk, class: Ci::BuildTraceChunk do build factory: :ci_build diff --git a/spec/factories/ci/build_trace_section_names.rb b/spec/factories/ci/build_trace_section_names.rb index ce07e716dde..e52694ef3dc 100644 --- a/spec/factories/ci/build_trace_section_names.rb +++ b/spec/factories/ci/build_trace_section_names.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :ci_build_trace_section_name, class: Ci::BuildTraceSectionName do sequence(:name) { |n| "section_#{n}" } diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index 5f7c75a3a92..e3b7c64176a 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + include ActionDispatch::TestProcess FactoryBot.define do diff --git a/spec/factories/ci/group_variables.rb b/spec/factories/ci/group_variables.rb index 9bf520a2c0a..13c2b78e61b 100644 --- a/spec/factories/ci/group_variables.rb +++ b/spec/factories/ci/group_variables.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :ci_group_variable, class: Ci::GroupVariable do sequence(:key) { |n| "VARIABLE_#{n}" } diff --git a/spec/factories/ci/job_artifacts.rb b/spec/factories/ci/job_artifacts.rb index 542fa9775cd..2d68a8e9fe3 100644 --- a/spec/factories/ci/job_artifacts.rb +++ b/spec/factories/ci/job_artifacts.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + include ActionDispatch::TestProcess FactoryBot.define do diff --git a/spec/factories/ci/pipeline_schedule.rb b/spec/factories/ci/pipeline_schedule.rb index 4b83ba2ac1b..8fae6986869 100644 --- a/spec/factories/ci/pipeline_schedule.rb +++ b/spec/factories/ci/pipeline_schedule.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :ci_pipeline_schedule, class: Ci::PipelineSchedule do cron '0 1 * * *' diff --git a/spec/factories/ci/pipeline_schedule_variables.rb b/spec/factories/ci/pipeline_schedule_variables.rb index c85b97fbfc7..fd7cfada65b 100644 --- a/spec/factories/ci/pipeline_schedule_variables.rb +++ b/spec/factories/ci/pipeline_schedule_variables.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :ci_pipeline_schedule_variable, class: Ci::PipelineScheduleVariable do sequence(:key) { |n| "VARIABLE_#{n}" } diff --git a/spec/factories/ci/pipeline_variables.rb b/spec/factories/ci/pipeline_variables.rb index b18055d7b3a..af0982124d7 100644 --- a/spec/factories/ci/pipeline_variables.rb +++ b/spec/factories/ci/pipeline_variables.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :ci_pipeline_variable, class: Ci::PipelineVariable do sequence(:key) { |n| "VARIABLE_#{n}" } diff --git a/spec/factories/ci/pipelines.rb b/spec/factories/ci/pipelines.rb index aa5ccbda6cd..9652b0000a9 100644 --- a/spec/factories/ci/pipelines.rb +++ b/spec/factories/ci/pipelines.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :ci_empty_pipeline, class: Ci::Pipeline do source :push diff --git a/spec/factories/ci/runner_projects.rb b/spec/factories/ci/runner_projects.rb index ec15972c423..bc28544a839 100644 --- a/spec/factories/ci/runner_projects.rb +++ b/spec/factories/ci/runner_projects.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :ci_runner_project, class: Ci::RunnerProject do runner factory: [:ci_runner, :project] diff --git a/spec/factories/ci/runners.rb b/spec/factories/ci/runners.rb index 24e70913b87..1e4344b814d 100644 --- a/spec/factories/ci/runners.rb +++ b/spec/factories/ci/runners.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :ci_runner, class: Ci::Runner do sequence(:description) { |n| "My runner#{n}" } diff --git a/spec/factories/ci/stages.rb b/spec/factories/ci/stages.rb index ce61e6bf759..88ff8d7dc53 100644 --- a/spec/factories/ci/stages.rb +++ b/spec/factories/ci/stages.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :ci_stage, class: Ci::LegacyStage do skip_create diff --git a/spec/factories/ci/trigger_requests.rb b/spec/factories/ci/trigger_requests.rb index 0e9fc3d0014..d63bf9868c9 100644 --- a/spec/factories/ci/trigger_requests.rb +++ b/spec/factories/ci/trigger_requests.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :ci_trigger_request, class: Ci::TriggerRequest do trigger factory: :ci_trigger diff --git a/spec/factories/ci/triggers.rb b/spec/factories/ci/triggers.rb index 742d9efba2d..6f628ed5435 100644 --- a/spec/factories/ci/triggers.rb +++ b/spec/factories/ci/triggers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :ci_trigger_without_token, class: Ci::Trigger do owner diff --git a/spec/factories/ci/variables.rb b/spec/factories/ci/variables.rb index 97a7c9ba252..55d11085040 100644 --- a/spec/factories/ci/variables.rb +++ b/spec/factories/ci/variables.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :ci_variable, class: Ci::Variable do sequence(:key) { |n| "VARIABLE_#{n}" } diff --git a/spec/factories/clusters/applications/helm.rb b/spec/factories/clusters/applications/helm.rb index d78f01828d7..7fe9ea6801a 100644 --- a/spec/factories/clusters/applications/helm.rb +++ b/spec/factories/clusters/applications/helm.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :clusters_applications_helm, class: Clusters::Applications::Helm do cluster factory: %i(cluster provided_by_gcp) diff --git a/spec/factories/clusters/clusters.rb b/spec/factories/clusters/clusters.rb index 171f5256d2b..b0d14b672f4 100644 --- a/spec/factories/clusters/clusters.rb +++ b/spec/factories/clusters/clusters.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :cluster, class: Clusters::Cluster do user diff --git a/spec/factories/clusters/platforms/kubernetes.rb b/spec/factories/clusters/platforms/kubernetes.rb index bf30a9c3a61..d5dc288fddb 100644 --- a/spec/factories/clusters/platforms/kubernetes.rb +++ b/spec/factories/clusters/platforms/kubernetes.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :cluster_platform_kubernetes, class: Clusters::Platforms::Kubernetes do cluster diff --git a/spec/factories/clusters/providers/gcp.rb b/spec/factories/clusters/providers/gcp.rb index 186c7c8027c..22462651b6a 100644 --- a/spec/factories/clusters/providers/gcp.rb +++ b/spec/factories/clusters/providers/gcp.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :cluster_provider_gcp, class: Clusters::Providers::Gcp do cluster diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb index 848a31e96c1..a76da30217e 100644 --- a/spec/factories/commit_statuses.rb +++ b/spec/factories/commit_statuses.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :commit_status, class: CommitStatus do name 'default' diff --git a/spec/factories/commits.rb b/spec/factories/commits.rb index 2bcc4b6cf52..d1554426a76 100644 --- a/spec/factories/commits.rb +++ b/spec/factories/commits.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative '../support/helpers/repo_helpers' FactoryBot.define do diff --git a/spec/factories/container_repositories.rb b/spec/factories/container_repositories.rb index 00fad7975c9..a9771200d6e 100644 --- a/spec/factories/container_repositories.rb +++ b/spec/factories/container_repositories.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :container_repository do name 'test_image' diff --git a/spec/factories/conversational_development_index_metrics.rb b/spec/factories/conversational_development_index_metrics.rb index abf37fb861e..ea5816684c6 100644 --- a/spec/factories/conversational_development_index_metrics.rb +++ b/spec/factories/conversational_development_index_metrics.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :conversational_development_index_metric, class: ConversationalDevelopmentIndex::Metric do leader_issues 9.256 diff --git a/spec/factories/deploy_keys_projects.rb b/spec/factories/deploy_keys_projects.rb index 4350652fb79..7f82902dee7 100644 --- a/spec/factories/deploy_keys_projects.rb +++ b/spec/factories/deploy_keys_projects.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :deploy_keys_project do deploy_key diff --git a/spec/factories/deploy_tokens.rb b/spec/factories/deploy_tokens.rb index 017e866e69c..a96258f5cbe 100644 --- a/spec/factories/deploy_tokens.rb +++ b/spec/factories/deploy_tokens.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :deploy_token do token { SecureRandom.hex(50) } diff --git a/spec/factories/deployments.rb b/spec/factories/deployments.rb index 1c7787bc1a6..89ff1c527df 100644 --- a/spec/factories/deployments.rb +++ b/spec/factories/deployments.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :deployment, class: Deployment do sha 'b83d6e391c22777fca1ed3012fce84f633d7fed0' diff --git a/spec/factories/emails.rb b/spec/factories/emails.rb index feacd5ccf15..284ba631c37 100644 --- a/spec/factories/emails.rb +++ b/spec/factories/emails.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :email do user diff --git a/spec/factories/environments.rb b/spec/factories/environments.rb index 9d9e3d693b8..b5c8f0ca4f0 100644 --- a/spec/factories/environments.rb +++ b/spec/factories/environments.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :environment, class: Environment do sequence(:name) { |n| "environment#{n}" } diff --git a/spec/factories/events.rb b/spec/factories/events.rb index bf8411b1894..b15eb1592fc 100644 --- a/spec/factories/events.rb +++ b/spec/factories/events.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :event do project diff --git a/spec/factories/file_uploaders.rb b/spec/factories/file_uploaders.rb index 8404985bfea..ec8f5c9af2d 100644 --- a/spec/factories/file_uploaders.rb +++ b/spec/factories/file_uploaders.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :file_uploader do skip_create diff --git a/spec/factories/fork_network_members.rb b/spec/factories/fork_network_members.rb index 850e3f158f1..d34de72ab60 100644 --- a/spec/factories/fork_network_members.rb +++ b/spec/factories/fork_network_members.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :fork_network_member do association :project diff --git a/spec/factories/fork_networks.rb b/spec/factories/fork_networks.rb index 813b1943eb2..12ea688bbce 100644 --- a/spec/factories/fork_networks.rb +++ b/spec/factories/fork_networks.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :fork_network do association :root_project, factory: :project diff --git a/spec/factories/gitaly/commit.rb b/spec/factories/gitaly/commit.rb index 5034c3d0e48..954b5338846 100644 --- a/spec/factories/gitaly/commit.rb +++ b/spec/factories/gitaly/commit.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do sequence(:gitaly_commit_id) { Digest::SHA1.hexdigest(Time.now.to_f.to_s) } diff --git a/spec/factories/gitaly/commit_author.rb b/spec/factories/gitaly/commit_author.rb index aaf634ce08b..51dcd8a623b 100644 --- a/spec/factories/gitaly/commit_author.rb +++ b/spec/factories/gitaly/commit_author.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :gitaly_commit_author, class: Gitaly::CommitAuthor do skip_create diff --git a/spec/factories/gitaly/tag.rb b/spec/factories/gitaly/tag.rb index e99776d524a..a7a84753090 100644 --- a/spec/factories/gitaly/tag.rb +++ b/spec/factories/gitaly/tag.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :gitaly_tag, class: Gitaly::Tag do skip_create diff --git a/spec/factories/gpg_key_subkeys.rb b/spec/factories/gpg_key_subkeys.rb index 6c7db5379a9..9daf26d930d 100644 --- a/spec/factories/gpg_key_subkeys.rb +++ b/spec/factories/gpg_key_subkeys.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :gpg_key_subkey do gpg_key diff --git a/spec/factories/gpg_keys.rb b/spec/factories/gpg_keys.rb index 3c0f43cc1b6..9f321643174 100644 --- a/spec/factories/gpg_keys.rb +++ b/spec/factories/gpg_keys.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative '../support/helpers/gpg_helpers' FactoryBot.define do diff --git a/spec/factories/gpg_signature.rb b/spec/factories/gpg_signature.rb index b89e6ffc4b3..a0fc1740d77 100644 --- a/spec/factories/gpg_signature.rb +++ b/spec/factories/gpg_signature.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :gpg_signature do commit_sha { Digest::SHA1.hexdigest(SecureRandom.hex) } diff --git a/spec/factories/group_custom_attributes.rb b/spec/factories/group_custom_attributes.rb index d2f45d5d3ce..895bc42c84b 100644 --- a/spec/factories/group_custom_attributes.rb +++ b/spec/factories/group_custom_attributes.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :group_custom_attribute do group diff --git a/spec/factories/group_members.rb b/spec/factories/group_members.rb index 077c6ddc5ae..3bf9cdef253 100644 --- a/spec/factories/group_members.rb +++ b/spec/factories/group_members.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :group_member do access_level { GroupMember::OWNER } diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb index b67ab955ffc..334c0f369cd 100644 --- a/spec/factories/groups.rb +++ b/spec/factories/groups.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :group, class: Group, parent: :namespace do sequence(:name) { |n| "group#{n}" } diff --git a/spec/factories/identities.rb b/spec/factories/identities.rb index 122d0d42938..21cfe7fe623 100644 --- a/spec/factories/identities.rb +++ b/spec/factories/identities.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :identity do provider 'ldapmain' diff --git a/spec/factories/import_export_uploads.rb b/spec/factories/import_export_uploads.rb index 7750d49b1d0..8521411e0e8 100644 --- a/spec/factories/import_export_uploads.rb +++ b/spec/factories/import_export_uploads.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :import_export_upload do project { create(:project) } diff --git a/spec/factories/import_states.rb b/spec/factories/import_states.rb index d6de26dccbc..8e778200389 100644 --- a/spec/factories/import_states.rb +++ b/spec/factories/import_states.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :import_state, class: ProjectImportState do status :none diff --git a/spec/factories/instance_configuration.rb b/spec/factories/instance_configuration.rb index 31866a9c221..daa0be3f404 100644 --- a/spec/factories/instance_configuration.rb +++ b/spec/factories/instance_configuration.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :instance_configuration do skip_create diff --git a/spec/factories/internal_ids.rb b/spec/factories/internal_ids.rb index fbde07a391a..df5c5beeb42 100644 --- a/spec/factories/internal_ids.rb +++ b/spec/factories/internal_ids.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :internal_id do project diff --git a/spec/factories/issues.rb b/spec/factories/issues.rb index ab27fee33dc..434225f7022 100644 --- a/spec/factories/issues.rb +++ b/spec/factories/issues.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :issue do title { generate(:title) } diff --git a/spec/factories/keys.rb b/spec/factories/keys.rb index 3f0c60f32b7..087d2521836 100644 --- a/spec/factories/keys.rb +++ b/spec/factories/keys.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative '../support/helpers/key_generator_helper' FactoryBot.define do diff --git a/spec/factories/label_links.rb b/spec/factories/label_links.rb index 007847d9cf4..9b7c0a158bd 100644 --- a/spec/factories/label_links.rb +++ b/spec/factories/label_links.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :label_link do label diff --git a/spec/factories/label_priorities.rb b/spec/factories/label_priorities.rb index c4824faad53..2f9f2ddfa89 100644 --- a/spec/factories/label_priorities.rb +++ b/spec/factories/label_priorities.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :label_priority do project diff --git a/spec/factories/labels.rb b/spec/factories/labels.rb index f759b6d499d..3eed750be03 100644 --- a/spec/factories/labels.rb +++ b/spec/factories/labels.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do trait :base_label do title { generate(:label_title) } diff --git a/spec/factories/lfs_file_locks.rb b/spec/factories/lfs_file_locks.rb index b9d24f82b65..73675d076ab 100644 --- a/spec/factories/lfs_file_locks.rb +++ b/spec/factories/lfs_file_locks.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :lfs_file_lock do user diff --git a/spec/factories/lfs_objects.rb b/spec/factories/lfs_objects.rb index 08ac7fb69e7..631d87cfb12 100644 --- a/spec/factories/lfs_objects.rb +++ b/spec/factories/lfs_objects.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + include ActionDispatch::TestProcess FactoryBot.define do diff --git a/spec/factories/lfs_objects_projects.rb b/spec/factories/lfs_objects_projects.rb index 4804d0bb884..7b55cc57f75 100644 --- a/spec/factories/lfs_objects_projects.rb +++ b/spec/factories/lfs_objects_projects.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :lfs_objects_project do lfs_object diff --git a/spec/factories/lists.rb b/spec/factories/lists.rb index 210c58b21e9..e68611ec518 100644 --- a/spec/factories/lists.rb +++ b/spec/factories/lists.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :list do board diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb index 0b6a43b13a9..3d12ff98257 100644 --- a/spec/factories/merge_requests.rb +++ b/spec/factories/merge_requests.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :merge_request do title { generate(:title) } diff --git a/spec/factories/merge_requests_closing_issues.rb b/spec/factories/merge_requests_closing_issues.rb index ee0606a72b6..94f8b27358c 100644 --- a/spec/factories/merge_requests_closing_issues.rb +++ b/spec/factories/merge_requests_closing_issues.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :merge_requests_closing_issues do issue diff --git a/spec/factories/milestones.rb b/spec/factories/milestones.rb index 027349b2f8e..7d623000fc9 100644 --- a/spec/factories/milestones.rb +++ b/spec/factories/milestones.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :milestone do title diff --git a/spec/factories/namespaces.rb b/spec/factories/namespaces.rb index 0cfc6e3aa46..09dbe16ef9e 100644 --- a/spec/factories/namespaces.rb +++ b/spec/factories/namespaces.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :namespace do sequence(:name) { |n| "namespace#{n}" } diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb index c51f2f958f9..5b9a7e6f864 100644 --- a/spec/factories/notes.rb +++ b/spec/factories/notes.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative '../support/helpers/repo_helpers' include ActionDispatch::TestProcess diff --git a/spec/factories/notification_settings.rb b/spec/factories/notification_settings.rb index 5116ef33f5d..c16b0e456ba 100644 --- a/spec/factories/notification_settings.rb +++ b/spec/factories/notification_settings.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :notification_setting do source factory: :project diff --git a/spec/factories/oauth_access_grants.rb b/spec/factories/oauth_access_grants.rb index 02c51cd9899..7a46ae6d71d 100644 --- a/spec/factories/oauth_access_grants.rb +++ b/spec/factories/oauth_access_grants.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :oauth_access_grant do resource_owner_id { create(:user).id } diff --git a/spec/factories/oauth_access_tokens.rb b/spec/factories/oauth_access_tokens.rb index eabfd6cd830..8d1075dacbb 100644 --- a/spec/factories/oauth_access_tokens.rb +++ b/spec/factories/oauth_access_tokens.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :oauth_access_token do resource_owner diff --git a/spec/factories/oauth_applications.rb b/spec/factories/oauth_applications.rb index 4427da1d6c7..4748b320298 100644 --- a/spec/factories/oauth_applications.rb +++ b/spec/factories/oauth_applications.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :oauth_application, class: 'Doorkeeper::Application', aliases: [:application] do sequence(:name) { |n| "OAuth App #{n}" } diff --git a/spec/factories/pages_domains.rb b/spec/factories/pages_domains.rb index e441dfcf229..ee5be82cd19 100644 --- a/spec/factories/pages_domains.rb +++ b/spec/factories/pages_domains.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :pages_domain, class: 'PagesDomain' do sequence(:domain) { |n| "my#{n}.domain.com" } diff --git a/spec/factories/personal_access_tokens.rb b/spec/factories/personal_access_tokens.rb index e7fd22a96b2..cc9b2328705 100644 --- a/spec/factories/personal_access_tokens.rb +++ b/spec/factories/personal_access_tokens.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :personal_access_token do user diff --git a/spec/factories/pool_repositories.rb b/spec/factories/pool_repositories.rb index 8cac666069c..7a8946d47b0 100644 --- a/spec/factories/pool_repositories.rb +++ b/spec/factories/pool_repositories.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :pool_repository do shard { Shard.by_name("default") } diff --git a/spec/factories/programming_languages.rb b/spec/factories/programming_languages.rb index d3511d42b6c..ee8e7765ec9 100644 --- a/spec/factories/programming_languages.rb +++ b/spec/factories/programming_languages.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :programming_language do name 'Ruby' diff --git a/spec/factories/project_auto_devops.rb b/spec/factories/project_auto_devops.rb index 1de42512402..4cc59c7095c 100644 --- a/spec/factories/project_auto_devops.rb +++ b/spec/factories/project_auto_devops.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :project_auto_devops do project diff --git a/spec/factories/project_custom_attributes.rb b/spec/factories/project_custom_attributes.rb index 099d2d7ff19..c9ed86cc0ac 100644 --- a/spec/factories/project_custom_attributes.rb +++ b/spec/factories/project_custom_attributes.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :project_custom_attribute do project diff --git a/spec/factories/project_deploy_tokens.rb b/spec/factories/project_deploy_tokens.rb index 4866cb58d88..fbf5454b0fe 100644 --- a/spec/factories/project_deploy_tokens.rb +++ b/spec/factories/project_deploy_tokens.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :project_deploy_token do project diff --git a/spec/factories/project_group_links.rb b/spec/factories/project_group_links.rb index 59c77627ee5..b02d167a950 100644 --- a/spec/factories/project_group_links.rb +++ b/spec/factories/project_group_links.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :project_group_link do project diff --git a/spec/factories/project_hooks.rb b/spec/factories/project_hooks.rb index a448d565e4b..96c9742c7d0 100644 --- a/spec/factories/project_hooks.rb +++ b/spec/factories/project_hooks.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :project_hook do url { generate(:url) } diff --git a/spec/factories/project_members.rb b/spec/factories/project_members.rb index c72e0487895..6dcac0400ca 100644 --- a/spec/factories/project_members.rb +++ b/spec/factories/project_members.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :project_member do user diff --git a/spec/factories/project_statistics.rb b/spec/factories/project_statistics.rb index 3d4174eb852..f084d9d5cbf 100644 --- a/spec/factories/project_statistics.rb +++ b/spec/factories/project_statistics.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :project_statistics do project diff --git a/spec/factories/project_wikis.rb b/spec/factories/project_wikis.rb index 4d21ed47f39..401402614f4 100644 --- a/spec/factories/project_wikis.rb +++ b/spec/factories/project_wikis.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :project_wiki do skip_create diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index 0e8810b73a1..afe27aaf1fb 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative '../support/helpers/test_env' FactoryBot.define do diff --git a/spec/factories/protected_branches.rb b/spec/factories/protected_branches.rb index 5457c0d2a8f..741615bc0d3 100644 --- a/spec/factories/protected_branches.rb +++ b/spec/factories/protected_branches.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :protected_branch do name diff --git a/spec/factories/protected_tags.rb b/spec/factories/protected_tags.rb index 2b81d089549..6ff2a245b58 100644 --- a/spec/factories/protected_tags.rb +++ b/spec/factories/protected_tags.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :protected_tag do name diff --git a/spec/factories/redirect_routes.rb b/spec/factories/redirect_routes.rb index 774232d0b34..8fa0aec007c 100644 --- a/spec/factories/redirect_routes.rb +++ b/spec/factories/redirect_routes.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :redirect_route do sequence(:path) { |n| "redirect#{n}" } diff --git a/spec/factories/releases.rb b/spec/factories/releases.rb index 4cacc77c182..34794f57284 100644 --- a/spec/factories/releases.rb +++ b/spec/factories/releases.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :release do tag "v1.1.0" diff --git a/spec/factories/remote_mirrors.rb b/spec/factories/remote_mirrors.rb index adc7da27522..ff1d751c86c 100644 --- a/spec/factories/remote_mirrors.rb +++ b/spec/factories/remote_mirrors.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :remote_mirror, class: 'RemoteMirror' do association :project, :repository diff --git a/spec/factories/repository_languages.rb b/spec/factories/repository_languages.rb index 1757ba6766c..b2b17ebbce2 100644 --- a/spec/factories/repository_languages.rb +++ b/spec/factories/repository_languages.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :repository_language do project diff --git a/spec/factories/sent_notifications.rb b/spec/factories/sent_notifications.rb index b0174dd06b7..11116af7dbb 100644 --- a/spec/factories/sent_notifications.rb +++ b/spec/factories/sent_notifications.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :sent_notification do project diff --git a/spec/factories/sequences.rb b/spec/factories/sequences.rb index f2b6e7a11f9..b6f2d6d8389 100644 --- a/spec/factories/sequences.rb +++ b/spec/factories/sequences.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do sequence(:username) { |n| "user#{n}" } sequence(:name) { |n| "John Doe#{n}" } diff --git a/spec/factories/service_hooks.rb b/spec/factories/service_hooks.rb index c907862b4f6..ff819f4f8d0 100644 --- a/spec/factories/service_hooks.rb +++ b/spec/factories/service_hooks.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :service_hook do url { generate(:url) } diff --git a/spec/factories/services.rb b/spec/factories/services.rb index daf842e3075..5ef39b3e818 100644 --- a/spec/factories/services.rb +++ b/spec/factories/services.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :service do project diff --git a/spec/factories/shards.rb b/spec/factories/shards.rb index c095fa5f0a0..c30a38180e8 100644 --- a/spec/factories/shards.rb +++ b/spec/factories/shards.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :shard do name "default" diff --git a/spec/factories/snippets.rb b/spec/factories/snippets.rb index dc12b562108..9c3a0fbe9b3 100644 --- a/spec/factories/snippets.rb +++ b/spec/factories/snippets.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :snippet do author diff --git a/spec/factories/spam_logs.rb b/spec/factories/spam_logs.rb index a467f850a80..42a856832e7 100644 --- a/spec/factories/spam_logs.rb +++ b/spec/factories/spam_logs.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :spam_log do user diff --git a/spec/factories/subscriptions.rb b/spec/factories/subscriptions.rb index 8f7ab74ec70..2b652cd57bf 100644 --- a/spec/factories/subscriptions.rb +++ b/spec/factories/subscriptions.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :subscription do project diff --git a/spec/factories/system_hooks.rb b/spec/factories/system_hooks.rb index 9e00eeb6ef1..a2d163a060f 100644 --- a/spec/factories/system_hooks.rb +++ b/spec/factories/system_hooks.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :system_hook do url { generate(:url) } diff --git a/spec/factories/system_note_metadata.rb b/spec/factories/system_note_metadata.rb index e913068da40..8cd4b77799c 100644 --- a/spec/factories/system_note_metadata.rb +++ b/spec/factories/system_note_metadata.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :system_note_metadata do note diff --git a/spec/factories/term_agreements.rb b/spec/factories/term_agreements.rb index 3c4eebd0196..b7e259bd44b 100644 --- a/spec/factories/term_agreements.rb +++ b/spec/factories/term_agreements.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :term_agreement do term diff --git a/spec/factories/terms.rb b/spec/factories/terms.rb index 5ffca365a5f..b890261d293 100644 --- a/spec/factories/terms.rb +++ b/spec/factories/terms.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :term, class: ApplicationSetting::Term do terms "Lorem ipsum dolor sit amet, consectetur adipiscing elit." diff --git a/spec/factories/timelogs.rb b/spec/factories/timelogs.rb index b45f06b9a0a..056a8833c46 100644 --- a/spec/factories/timelogs.rb +++ b/spec/factories/timelogs.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Read about factories at https://github.com/thoughtbot/factory_bot FactoryBot.define do diff --git a/spec/factories/todos.rb b/spec/factories/todos.rb index ed3d87eb76b..2ff024112a4 100644 --- a/spec/factories/todos.rb +++ b/spec/factories/todos.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :todo do project diff --git a/spec/factories/trending_project.rb b/spec/factories/trending_project.rb index f7c634fd21f..354b8c1e002 100644 --- a/spec/factories/trending_project.rb +++ b/spec/factories/trending_project.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do # TrendingProject factory :trending_project, class: 'TrendingProject' do diff --git a/spec/factories/u2f_registrations.rb b/spec/factories/u2f_registrations.rb index 26090b08966..c968468834b 100644 --- a/spec/factories/u2f_registrations.rb +++ b/spec/factories/u2f_registrations.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :u2f_registration do certificate { FFaker::BaconIpsum.characters(728) } diff --git a/spec/factories/uploads.rb b/spec/factories/uploads.rb index 426abdc2a6c..2ede92c8af0 100644 --- a/spec/factories/uploads.rb +++ b/spec/factories/uploads.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :upload do model { build(:project) } diff --git a/spec/factories/user_agent_details.rb b/spec/factories/user_agent_details.rb index 7183a8e1140..055aea50585 100644 --- a/spec/factories/user_agent_details.rb +++ b/spec/factories/user_agent_details.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :user_agent_detail do ip_address '127.0.0.1' diff --git a/spec/factories/user_callouts.rb b/spec/factories/user_callouts.rb index 528e442c14b..c4a217fd357 100644 --- a/spec/factories/user_callouts.rb +++ b/spec/factories/user_callouts.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :user_callout do feature_name :gke_cluster_integration diff --git a/spec/factories/user_custom_attributes.rb b/spec/factories/user_custom_attributes.rb index a184a2e0f17..7bd5c06f4ef 100644 --- a/spec/factories/user_custom_attributes.rb +++ b/spec/factories/user_custom_attributes.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :user_custom_attribute do user diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 4f3392cdcbf..b2c8bdab013 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :user, aliases: [:author, :assignee, :recipient, :owner, :resource_owner] do email { generate(:email) } diff --git a/spec/factories/users_star_projects.rb b/spec/factories/users_star_projects.rb index 6afd08a2084..a1299024fe4 100644 --- a/spec/factories/users_star_projects.rb +++ b/spec/factories/users_star_projects.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :users_star_project do project diff --git a/spec/factories/web_hook_log.rb b/spec/factories/web_hook_log.rb index 17837260a4b..5750af85662 100644 --- a/spec/factories/web_hook_log.rb +++ b/spec/factories/web_hook_log.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :web_hook_log do web_hook factory: :project_hook diff --git a/spec/factories/wiki_directories.rb b/spec/factories/wiki_directories.rb index b105c82b19d..de23cf110b5 100644 --- a/spec/factories/wiki_directories.rb +++ b/spec/factories/wiki_directories.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + FactoryBot.define do factory :wiki_directory do skip_create diff --git a/spec/factories/wiki_pages.rb b/spec/factories/wiki_pages.rb index ae257d769e8..761ba58edb2 100644 --- a/spec/factories/wiki_pages.rb +++ b/spec/factories/wiki_pages.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'ostruct' FactoryBot.define do diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb index fb76e2b0014..bdab3c63244 100644 --- a/spec/features/dashboard/groups_list_spec.rb +++ b/spec/features/dashboard/groups_list_spec.rb @@ -27,7 +27,7 @@ describe 'Dashboard Groups page', :js do expect(page).not_to have_content(another_group.name) end - it 'shows subgroups the user is member of', :nested_groups do + it 'shows subgroups the user is member of' do group.add_owner(user) nested_group.add_owner(user) @@ -40,7 +40,7 @@ describe 'Dashboard Groups page', :js do expect(page).to have_content(nested_group.name) end - context 'when filtering groups', :nested_groups do + context 'when filtering groups' do before do group.add_owner(user) nested_group.add_owner(user) @@ -79,7 +79,7 @@ describe 'Dashboard Groups page', :js do end end - context 'with subgroups', :nested_groups do + context 'with subgroups' do let!(:subgroup) { create(:group, :public, parent: group) } before do diff --git a/spec/features/groups/empty_states_spec.rb b/spec/features/groups/empty_states_spec.rb index e4eb0d355d1..3fbc0f78d38 100644 --- a/spec/features/groups/empty_states_spec.rb +++ b/spec/features/groups/empty_states_spec.rb @@ -110,7 +110,7 @@ describe 'Group empty states' do end context 'group without a project' do - context 'group has a subgroup', :nested_groups do + context 'group has a subgroup' do let(:subgroup) { create(:group, parent: group) } let(:subgroup_project) { create(:project, namespace: subgroup) } diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb index 676769c25fe..257aeae7e23 100644 --- a/spec/features/groups/group_settings_spec.rb +++ b/spec/features/groups/group_settings_spec.rb @@ -121,7 +121,7 @@ describe 'Edit group settings' do expect(find(:css, '.group-root-path').text).to eq(root_url) end - it 'has a parent group URL label for a subgroup group', :postgresql do + it 'has a parent group URL label for a subgroup group' do subgroup = create(:group, parent: group) visit edit_group_path(subgroup) diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb index 0ada530781c..a16004c79ca 100644 --- a/spec/features/groups/issues_spec.rb +++ b/spec/features/groups/issues_spec.rb @@ -50,7 +50,7 @@ describe 'Group issues page' do end end - context 'issues list', :nested_groups do + context 'issues list' do let(:subgroup) { create(:group, parent: group) } let(:subgroup_project) { create(:project, :public, group: subgroup)} let(:user_in_group) { create(:group_member, :maintainer, user: create(:user), group: group ).user } diff --git a/spec/features/groups/members/list_members_spec.rb b/spec/features/groups/members/list_members_spec.rb index 4ba7161601e..ffff755793f 100644 --- a/spec/features/groups/members/list_members_spec.rb +++ b/spec/features/groups/members/list_members_spec.rb @@ -13,7 +13,7 @@ describe 'Groups > Members > List members' do sign_in(user1) end - it 'show members from current group and parent', :nested_groups do + it 'show members from current group and parent' do group.add_developer(user1) nested_group.add_developer(user2) @@ -23,7 +23,7 @@ describe 'Groups > Members > List members' do expect(second_row.text).to include(user2.name) end - it 'show user once if member of both current group and parent', :nested_groups do + it 'show user once if member of both current group and parent' do group.add_developer(user1) nested_group.add_developer(user1) diff --git a/spec/features/groups/share_lock_spec.rb b/spec/features/groups/share_lock_spec.rb index 704d9f12888..86b523fbdd9 100644 --- a/spec/features/groups/share_lock_spec.rb +++ b/spec/features/groups/share_lock_spec.rb @@ -9,7 +9,7 @@ describe 'Group share with group lock' do sign_in(root_owner) end - context 'with a subgroup', :nested_groups do + context 'with a subgroup' do let!(:subgroup) { create(:group, parent: root_group) } context 'when enabling the parent group share with group lock' do diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb index bed998a0859..0d78bd23973 100644 --- a/spec/features/groups/show_spec.rb +++ b/spec/features/groups/show_spec.rb @@ -75,11 +75,7 @@ describe 'Group show page' do sign_in(owner) end - context 'when subgroups are supported', :nested_groups do - before do - allow(Group).to receive(:supports_nested_objects?) { true } - end - + context 'when subgroups are supported' do it 'allows creating subgroups' do visit path @@ -87,19 +83,6 @@ describe 'Group show page' do .to have_css("li[data-text='New subgroup']", visible: false) end end - - context 'when subgroups are not supported' do - before do - allow(Group).to receive(:supports_nested_objects?) { false } - end - - it 'does not allow creating subgroups' do - visit path - - expect(page) - .not_to have_selector("li[data-text='New subgroup']", visible: false) - end - end end context 'for maintainers' do @@ -107,11 +90,7 @@ describe 'Group show page' do sign_in(maintainer) end - context 'when subgroups are supported', :nested_groups do - before do - allow(Group).to receive(:supports_nested_objects?) { true } - end - + context 'when subgroups are supported' do context 'when subgroup_creation_level is set to maintainers' do before do relaxed_group.add_maintainer(maintainer) @@ -141,19 +120,6 @@ describe 'Group show page' do end end end - - context 'when subgroups are not supported' do - before do - allow(Group).to receive(:supports_nested_objects?) { false } - end - - it 'does not allow creating subgroups' do - visit path - - expect(page) - .not_to have_selector("li[data-text='New subgroup']", visible: false) - end - end end end diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb index 8e7f78cab81..ad645643919 100644 --- a/spec/features/groups_spec.rb +++ b/spec/features/groups_spec.rb @@ -105,7 +105,7 @@ describe 'Group' do end end - describe 'create a nested group', :nested_groups, :js do + describe 'create a nested group', :js do let(:group) { create(:group, path: 'foo') } context 'as admin' do @@ -231,7 +231,7 @@ describe 'Group' do end end - describe 'group page with nested groups', :nested_groups, :js do + describe 'group page with nested groups', :js do let!(:group) { create(:group) } let!(:nested_group) { create(:group, parent: group) } let!(:project) { create(:project, namespace: group) } diff --git a/spec/features/ide_spec.rb b/spec/features/ide_spec.rb index 6eb59ef72c2..65989c36c1e 100644 --- a/spec/features/ide_spec.rb +++ b/spec/features/ide_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe 'IDE', :js do - describe 'sub-groups', :nested_groups do + describe 'sub-groups' do let(:user) { create(:user) } let(:group) { create(:group) } let(:subgroup) { create(:group, parent: group) } diff --git a/spec/features/import/manifest_import_spec.rb b/spec/features/import/manifest_import_spec.rb index a90cdd8d920..90b499efd72 100644 --- a/spec/features/import/manifest_import_spec.rb +++ b/spec/features/import/manifest_import_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Import multiple repositories by uploading a manifest file', :js, :postgresql do +describe 'Import multiple repositories by uploading a manifest file', :js do include Select2Helper let(:user) { create(:admin) } diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb index 489651fea15..45bd2218ee6 100644 --- a/spec/features/labels_hierarchy_spec.rb +++ b/spec/features/labels_hierarchy_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Labels Hierarchy', :js, :nested_groups do +describe 'Labels Hierarchy', :js do include FilteredSearchHelpers let!(:user) { create(:user) } diff --git a/spec/features/markdown/metrics_spec.rb b/spec/features/markdown/metrics_spec.rb new file mode 100644 index 00000000000..aa53ac50c78 --- /dev/null +++ b/spec/features/markdown/metrics_spec.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'Metrics rendering', :js, :use_clean_rails_memory_store_caching do + include PrometheusHelpers + + let(:user) { create(:user) } + let(:project) { create(:prometheus_project) } + let(:environment) { create(:environment, project: project) } + let(:issue) { create(:issue, project: project, description: description) } + let(:description) { "See [metrics dashboard](#{metrics_url}) for info." } + let(:metrics_url) { metrics_project_environment_url(project, environment) } + + before do + configure_host + import_common_metrics + stub_any_prometheus_request_with_response + + project.add_developer(user) + + sign_in(user) + end + + after do + restore_host + end + + context 'with deployments and related deployable present' do + it 'shows embedded metrics' do + visit project_issue_path(project, issue) + + expect(page).to have_css('div.prometheus-graph') + expect(page).to have_text('Memory Usage (Total)') + expect(page).to have_text('Core Usage (Total)') + end + end + + def import_common_metrics + ::Gitlab::DatabaseImporters::CommonMetrics::Importer.new.execute + end + + def configure_host + @original_default_host = default_url_options[:host] + @original_gitlab_url = Gitlab.config.gitlab[:url] + + # Ensure we create a metrics url with the right host. + # Configure host for route helpers in specs (also updates root_url): + default_url_options[:host] = Capybara.server_host + + # Ensure we identify urls with the appropriate host. + # Configure host to include port in app: + Gitlab.config.gitlab[:url] = root_url.chomp('/') + end + + def restore_host + default_url_options[:host] = @original_default_host + Gitlab.config.gitlab[:url] = @original_gitlab_url + end +end diff --git a/spec/features/oauth_login_spec.rb b/spec/features/oauth_login_spec.rb index 86331728f88..54705cd51b0 100644 --- a/spec/features/oauth_login_spec.rb +++ b/spec/features/oauth_login_spec.rb @@ -34,6 +34,7 @@ describe 'OAuth Login', :js, :allow_forgery_protection do before do stub_omniauth_config(provider) + expect(ActiveSession).to receive(:cleanup).with(user).at_least(:once).and_call_original end context 'when two-factor authentication is disabled' do diff --git a/spec/features/projects/artifacts/user_downloads_artifacts_spec.rb b/spec/features/projects/artifacts/user_downloads_artifacts_spec.rb index 5cb015e80be..69296ef00fd 100644 --- a/spec/features/projects/artifacts/user_downloads_artifacts_spec.rb +++ b/spec/features/projects/artifacts/user_downloads_artifacts_spec.rb @@ -1,8 +1,8 @@ require "spec_helper" describe "User downloads artifacts" do - set(:project) { create(:project, :public) } - set(:pipeline) { create(:ci_empty_pipeline, status: :success, project: project) } + set(:project) { create(:project, :repository, :public) } + set(:pipeline) { create(:ci_empty_pipeline, status: :success, sha: project.commit.id, project: project) } set(:job) { create(:ci_build, :artifacts, :success, pipeline: pipeline) } shared_examples "downloading" do diff --git a/spec/features/projects/commits/user_browses_commits_spec.rb b/spec/features/projects/commits/user_browses_commits_spec.rb index fc74a370e72..818939c1b96 100644 --- a/spec/features/projects/commits/user_browses_commits_spec.rb +++ b/spec/features/projects/commits/user_browses_commits_spec.rb @@ -13,7 +13,7 @@ describe 'User browses commits' do it 'renders commit' do visit project_commit_path(project, sample_commit.id) - expect(page).to have_content(sample_commit.message.gsub!(/\s+/, ' ')) + expect(page).to have_content(sample_commit.message.gsub(/\s+/, ' ')) .and have_content("Showing #{sample_commit.files_changed_count} changed files") .and have_content('Side-by-side') end diff --git a/spec/features/projects/members/invite_group_spec.rb b/spec/features/projects/members/invite_group_spec.rb index 7432c600c1e..c5423ec4662 100644 --- a/spec/features/projects/members/invite_group_spec.rb +++ b/spec/features/projects/members/invite_group_spec.rb @@ -58,7 +58,7 @@ describe 'Project > Members > Invite group', :js do end end - context 'for a project in a subgroup', :nested_groups do + context 'for a project in a subgroup' do let!(:group_to_share_with) { create(:group) } let(:root_group) { create(:group) } let(:subgroup) { create(:group, parent: root_group) } @@ -181,7 +181,7 @@ describe 'Project > Members > Invite group', :js do group_to_share_with.add_maintainer(maintainer) end - it 'the groups dropdown does not show ancestors', :nested_groups do + it 'the groups dropdown does not show ancestors' do visit project_settings_members_path(project) click_on 'invite-group-tab' diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb index 7602935b47e..8cdaed43fbc 100644 --- a/spec/features/projects/new_project_spec.rb +++ b/spec/features/projects/new_project_spec.rb @@ -294,7 +294,7 @@ describe 'New project' do end end - context 'from manifest file', :postgresql do + context 'from manifest file' do before do first('.import_manifest').click end diff --git a/spec/features/projects/settings/pipelines_settings_spec.rb b/spec/features/projects/settings/pipelines_settings_spec.rb index bf0c0de89b2..31fdd308292 100644 --- a/spec/features/projects/settings/pipelines_settings_spec.rb +++ b/spec/features/projects/settings/pipelines_settings_spec.rb @@ -126,7 +126,7 @@ describe "Projects > Settings > Pipelines settings" do end end - context 'when auto devops is turned on group parent level', :nested_groups do + context 'when auto devops is turned on group parent level' do before do group = create(:group, parent: create(:group, :auto_devops_enabled)) project.update!(namespace: group) diff --git a/spec/features/projects/settings/user_transfers_a_project_spec.rb b/spec/features/projects/settings/user_transfers_a_project_spec.rb index 2fdbc04fa62..9af2e47a334 100644 --- a/spec/features/projects/settings/user_transfers_a_project_spec.rb +++ b/spec/features/projects/settings/user_transfers_a_project_spec.rb @@ -68,7 +68,7 @@ describe 'Projects > Settings > User transfers a project', :js do end end - context 'when nested groups are available', :nested_groups do + context 'when nested groups are available' do it 'allows transferring a project to a subgroup' do subgroup = create(:group, parent: group) diff --git a/spec/features/projects/sub_group_issuables_spec.rb b/spec/features/projects/sub_group_issuables_spec.rb index 50e7e934cf6..8afbcf72ca7 100644 --- a/spec/features/projects/sub_group_issuables_spec.rb +++ b/spec/features/projects/sub_group_issuables_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'Subgroup Issuables', :js, :nested_groups do +describe 'Subgroup Issuables', :js do let!(:group) { create(:group, name: 'group') } let!(:subgroup) { create(:group, parent: group, name: 'subgroup') } let!(:project) { create(:project, namespace: subgroup, name: 'project') } diff --git a/spec/features/projects/user_creates_project_spec.rb b/spec/features/projects/user_creates_project_spec.rb index c0932539131..19262a635ac 100644 --- a/spec/features/projects/user_creates_project_spec.rb +++ b/spec/features/projects/user_creates_project_spec.rb @@ -26,7 +26,7 @@ describe 'User creates a project', :js do expect(page).to have_content(project.url_to_repo) end - context 'in a subgroup they do not own', :nested_groups do + context 'in a subgroup they do not own' do let(:parent) { create(:group) } let!(:subgroup) { create(:group, parent: parent) } diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb index aac095bfa6b..80741ace5d6 100644 --- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb @@ -132,9 +132,15 @@ describe "User creates wiki page" do fill_in(:wiki_content, with: ascii_content) - page.within(".wiki-form") do - click_button("Create page") - end + # This is the dumbest bug in the world: + # When the #wiki_content textarea is filled in, JS captures the `Enter` keydown event in order to do + # auto-indentation and manually inserts a newline. However, for whatever reason, when you try to click on the + # submit button in Capybara, it will not trigger the `click` event if a \n or \r character has been manually + # added to the textarea. It will, however, trigger ALL OTHER EVENTS, including `mouseover`/down/up, focus, and + # blur. Just not `click`. But only when you manually insert \n or \r - if you manually insert any other sequence + # then `click` is fired normally. And it's only Capybara. Browsers and JSDOM don't have this issue. + # So that's why the next line performs the click via JS. + page.execute_script("document.querySelector('.qa-create-page-button').click()") page.within ".md" do expect(page).to have_selector(".katex", count: 3).and have_content("2+2 is 4") diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index b5112758475..d1e2d17e9cc 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -314,7 +314,7 @@ describe 'Project' do end end - context 'for subgroups', :js, :nested_groups do + context 'for subgroups', :js do let(:group) { create(:group) } let(:subgroup) { create(:group, parent: group) } let(:project) { create(:project, :repository, group: subgroup) } diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb index efba303033b..1ea88010c89 100644 --- a/spec/features/users/login_spec.rb +++ b/spec/features/users/login_spec.rb @@ -132,7 +132,6 @@ describe 'Login' do it 'does not show a "You are already signed in." error message' do expect(authentication_metrics) .to increment(:user_authenticated_counter) - .and increment(:user_session_override_counter) .and increment(:user_two_factor_authenticated_counter) enter_code(user.current_otp) @@ -144,7 +143,6 @@ describe 'Login' do it 'allows login with valid code' do expect(authentication_metrics) .to increment(:user_authenticated_counter) - .and increment(:user_session_override_counter) .and increment(:user_two_factor_authenticated_counter) enter_code(user.current_otp) @@ -170,7 +168,6 @@ describe 'Login' do it 'allows login with invalid code, then valid code' do expect(authentication_metrics) .to increment(:user_authenticated_counter) - .and increment(:user_session_override_counter) .and increment(:user_two_factor_authenticated_counter) enter_code('foo') @@ -179,6 +176,15 @@ describe 'Login' do enter_code(user.current_otp) expect(current_path).to eq root_path end + + it 'triggers ActiveSession.cleanup for the user' do + expect(authentication_metrics) + .to increment(:user_authenticated_counter) + .and increment(:user_two_factor_authenticated_counter) + expect(ActiveSession).to receive(:cleanup).with(user).once.and_call_original + + enter_code(user.current_otp) + end end context 'using backup code' do @@ -195,7 +201,6 @@ describe 'Login' do it 'allows login' do expect(authentication_metrics) .to increment(:user_authenticated_counter) - .and increment(:user_session_override_counter) .and increment(:user_two_factor_authenticated_counter) enter_code(codes.sample) @@ -206,7 +211,6 @@ describe 'Login' do it 'invalidates the used code' do expect(authentication_metrics) .to increment(:user_authenticated_counter) - .and increment(:user_session_override_counter) .and increment(:user_two_factor_authenticated_counter) expect { enter_code(codes.sample) } @@ -216,7 +220,6 @@ describe 'Login' do it 'invalidates backup codes twice in a row' do expect(authentication_metrics) .to increment(:user_authenticated_counter).twice - .and increment(:user_session_override_counter).twice .and increment(:user_two_factor_authenticated_counter).twice .and increment(:user_session_destroyed_counter) @@ -230,6 +233,15 @@ describe 'Login' do expect { enter_code(codes.sample) } .to change { user.reload.otp_backup_codes.size }.by(-1) end + + it 'triggers ActiveSession.cleanup for the user' do + expect(authentication_metrics) + .to increment(:user_authenticated_counter) + .and increment(:user_two_factor_authenticated_counter) + expect(ActiveSession).to receive(:cleanup).with(user).once.and_call_original + + enter_code(codes.sample) + end end context 'with invalid code' do @@ -274,7 +286,7 @@ describe 'Login' do expect(authentication_metrics) .to increment(:user_authenticated_counter) - .and increment(:user_session_override_counter) + expect(ActiveSession).to receive(:cleanup).with(user).once.and_call_original sign_in_using_saml! @@ -287,8 +299,8 @@ describe 'Login' do it 'shows 2FA prompt after OAuth login' do expect(authentication_metrics) .to increment(:user_authenticated_counter) - .and increment(:user_session_override_counter) .and increment(:user_two_factor_authenticated_counter) + expect(ActiveSession).to receive(:cleanup).with(user).once.and_call_original sign_in_using_saml! @@ -329,6 +341,14 @@ describe 'Login' do expect(page).not_to have_content(I18n.t('devise.failure.already_authenticated')) end + + it 'triggers ActiveSession.cleanup for the user' do + expect(authentication_metrics) + .to increment(:user_authenticated_counter) + expect(ActiveSession).to receive(:cleanup).with(user).once.and_call_original + + gitlab_sign_in(user) + end end context 'with invalid username and password' do @@ -649,7 +669,6 @@ describe 'Login' do it 'asks the user to accept the terms' do expect(authentication_metrics) .to increment(:user_authenticated_counter) - .and increment(:user_session_override_counter) .and increment(:user_two_factor_authenticated_counter) visit new_user_session_path @@ -708,7 +727,6 @@ describe 'Login' do it 'asks the user to accept the terms before setting an email' do expect(authentication_metrics) .to increment(:user_authenticated_counter) - .and increment(:user_session_override_counter) gitlab_sign_in_via('saml', user, 'my-uid') diff --git a/spec/features/users/user_browses_projects_on_user_page_spec.rb b/spec/features/users/user_browses_projects_on_user_page_spec.rb index 6a9b281fb4c..5768f42c888 100644 --- a/spec/features/users/user_browses_projects_on_user_page_spec.rb +++ b/spec/features/users/user_browses_projects_on_user_page_spec.rb @@ -53,6 +53,19 @@ describe 'Users > User browses projects on user page', :js do expect(page).to have_content(project2.name) end + it 'does not have incorrectly interpolated message', :js do + project = create(:project, namespace: user.namespace, updated_at: 2.minutes.since) + + sign_in(user) + visit user_path(user) + click_nav_link('Personal projects') + + wait_for_requests + + expect(page).to have_content(project.name) + expect(page).not_to have_content("_('Updated')") + end + context 'when not signed in' do it 'renders user public project' do visit user_path(user) diff --git a/spec/finders/autocomplete/users_finder_spec.rb b/spec/finders/autocomplete/users_finder_spec.rb index bcde115b1a6..f3b54ca0461 100644 --- a/spec/finders/autocomplete/users_finder_spec.rb +++ b/spec/finders/autocomplete/users_finder_spec.rb @@ -50,7 +50,7 @@ describe Autocomplete::UsersFinder do it { is_expected.to match_array([user1]) } end - context 'when passed a subgroup', :nested_groups do + context 'when passed a subgroup' do let(:grandparent) { create(:group, :public) } let(:parent) { create(:group, :public, parent: grandparent) } let(:child) { create(:group, :public, parent: parent) } diff --git a/spec/finders/cluster_ancestors_finder_spec.rb b/spec/finders/cluster_ancestors_finder_spec.rb index 750042b6b54..4aedb41d446 100644 --- a/spec/finders/cluster_ancestors_finder_spec.rb +++ b/spec/finders/cluster_ancestors_finder_spec.rb @@ -32,7 +32,7 @@ describe ClusterAncestorsFinder, '#execute' do is_expected.to eq([project_cluster, group_cluster, instance_cluster]) end - context 'nested groups', :nested_groups do + context 'nested groups' do let(:group) { create(:group, parent: parent_group) } let(:parent_group) { create(:group) } @@ -65,7 +65,7 @@ describe ClusterAncestorsFinder, '#execute' do is_expected.to eq([group_cluster, instance_cluster]) end - context 'nested groups', :nested_groups do + context 'nested groups' do let(:group) { create(:group, parent: parent_group) } let(:parent_group) { create(:group) } diff --git a/spec/finders/group_descendants_finder_spec.rb b/spec/finders/group_descendants_finder_spec.rb index c28fd7cad11..5fb6739d6e2 100644 --- a/spec/finders/group_descendants_finder_spec.rb +++ b/spec/finders/group_descendants_finder_spec.rb @@ -19,7 +19,7 @@ describe GroupDescendantsFinder do expect(finder.has_children?).to be_truthy end - context 'when there are subgroups', :nested_groups do + context 'when there are subgroups' do it 'is true when there are projects' do create(:group, parent: group) @@ -99,7 +99,7 @@ describe GroupDescendantsFinder do ) end - context 'with nested groups', :nested_groups do + context 'with nested groups' do let!(:subgroup1) { create(:group, parent: group, name: 'a', path: 'sub-a') } let!(:subgroup2) { create(:group, parent: group, name: 'z', path: 'sub-z') } @@ -126,7 +126,7 @@ describe GroupDescendantsFinder do end end - context 'with nested groups', :nested_groups do + context 'with nested groups' do let!(:project) { create(:project, namespace: group) } let!(:subgroup) { create(:group, :private, parent: group) } diff --git a/spec/finders/group_members_finder_spec.rb b/spec/finders/group_members_finder_spec.rb index 8975ea0f063..49b0e14241e 100644 --- a/spec/finders/group_members_finder_spec.rb +++ b/spec/finders/group_members_finder_spec.rb @@ -18,7 +18,7 @@ describe GroupMembersFinder, '#execute' do expect(result.to_a).to match_array([member3, member2, member1]) end - it 'returns members for nested group', :nested_groups do + it 'returns members for nested group' do group.add_developer(user2) nested_group.request_access(user4) member1 = group.add_maintainer(user1) @@ -30,7 +30,7 @@ describe GroupMembersFinder, '#execute' do expect(result.to_a).to match_array([member1, member3, member4]) end - it 'returns members for descendant groups if requested', :nested_groups do + it 'returns members for descendant groups if requested' do member1 = group.add_maintainer(user2) member2 = group.add_maintainer(user1) nested_group.add_maintainer(user2) diff --git a/spec/finders/group_projects_finder_spec.rb b/spec/finders/group_projects_finder_spec.rb index f8fcc2d0e40..f4bd8a3f6ba 100644 --- a/spec/finders/group_projects_finder_spec.rb +++ b/spec/finders/group_projects_finder_spec.rb @@ -19,7 +19,7 @@ describe GroupProjectsFinder do context "only owned" do let(:options) { { only_owned: true } } - context 'with subgroups projects', :nested_groups do + context 'with subgroups projects' do before do options[:include_subgroups] = true end @@ -33,7 +33,7 @@ describe GroupProjectsFinder do end context "all" do - context 'with subgroups projects', :nested_groups do + context 'with subgroups projects' do before do options[:include_subgroups] = true end @@ -78,7 +78,7 @@ describe GroupProjectsFinder do subgroup_private_project.add_maintainer(current_user) end - context 'with subgroups projects', :nested_groups do + context 'with subgroups projects' do before do options[:include_subgroups] = true end @@ -96,7 +96,7 @@ describe GroupProjectsFinder do current_user.update(external: true) end - context 'with subgroups projects', :nested_groups do + context 'with subgroups projects' do before do options[:include_subgroups] = true end @@ -111,7 +111,7 @@ describe GroupProjectsFinder do end context "all" do - context 'with subgroups projects', :nested_groups do + context 'with subgroups projects' do before do options[:include_subgroups] = true end @@ -153,7 +153,7 @@ describe GroupProjectsFinder do context "only owned" do let(:options) { { only_owned: true } } - context 'with subgroups projects', :nested_groups do + context 'with subgroups projects' do before do options[:include_subgroups] = true end diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb index 367ca43bdfe..c8875d1f92d 100644 --- a/spec/finders/groups_finder_spec.rb +++ b/spec/finders/groups_finder_spec.rb @@ -65,7 +65,7 @@ describe GroupsFinder do end end - context 'subgroups', :nested_groups do + context 'subgroups' do let(:user) { create(:user) } let!(:parent_group) { create(:group, :public) } let!(:public_subgroup) { create(:group, :public, parent: parent_group) } diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb index bf38d083ca6..7fbe27c1939 100644 --- a/spec/finders/issues_finder_spec.rb +++ b/spec/finders/issues_finder_spec.rb @@ -51,7 +51,7 @@ describe IssuesFinder do end end - context 'when include_subgroup param is true', :nested_groups do + context 'when include_subgroup param is true' do before do params[:include_subgroups] = true end diff --git a/spec/finders/labels_finder_spec.rb b/spec/finders/labels_finder_spec.rb index 98b4933fef6..ba41ded112a 100644 --- a/spec/finders/labels_finder_spec.rb +++ b/spec/finders/labels_finder_spec.rb @@ -89,7 +89,7 @@ describe LabelsFinder do end end - context 'when including labels from group ancestors', :nested_groups do + context 'when including labels from group ancestors' do it 'returns labels from group and its ancestors' do private_group_1.add_developer(user) private_subgroup_1.add_developer(user) @@ -108,7 +108,7 @@ describe LabelsFinder do end end - context 'when including labels from group descendants', :nested_groups do + context 'when including labels from group descendants' do it 'returns labels from group and its descendants' do private_group_1.add_developer(user) private_subgroup_1.add_developer(user) @@ -128,7 +128,7 @@ describe LabelsFinder do end end - context 'filtering by project_id', :nested_groups do + context 'filtering by project_id' do context 'when include_ancestor_groups is true' do let!(:sub_project) { create(:project, namespace: private_subgroup_1 ) } let!(:project_label) { create(:label, project: sub_project, title: 'Label 5') } diff --git a/spec/finders/members_finder_spec.rb b/spec/finders/members_finder_spec.rb index 83348457caa..4203f58fe81 100644 --- a/spec/finders/members_finder_spec.rb +++ b/spec/finders/members_finder_spec.rb @@ -9,7 +9,7 @@ describe MembersFinder, '#execute' do set(:user3) { create(:user) } set(:user4) { create(:user) } - it 'returns members for project and parent groups', :nested_groups do + it 'returns members for project and parent groups' do nested_group.request_access(user1) member1 = group.add_maintainer(user2) member2 = nested_group.add_maintainer(user3) @@ -20,7 +20,7 @@ describe MembersFinder, '#execute' do expect(result.to_a).to match_array([member1, member2, member3]) end - it 'includes nested group members if asked', :nested_groups do + it 'includes nested group members if asked' do project = create(:project, namespace: group) nested_group.request_access(user1) member1 = group.add_maintainer(user2) @@ -32,7 +32,7 @@ describe MembersFinder, '#execute' do expect(result.to_a).to match_array([member1, member2, member3]) end - context 'when include_invited_groups_members == true', :nested_groups do + context 'when include_invited_groups_members == true' do subject { described_class.new(project, user2).execute(include_invited_groups_members: true) } set(:linked_group) { create(:group, :public, :access_requestable) } @@ -40,7 +40,7 @@ describe MembersFinder, '#execute' do set(:linked_group_member) { linked_group.add_developer(user1) } set(:nested_linked_group_member) { nested_linked_group.add_developer(user2) } - it 'includes all the invited_groups members including members inherited from ancestor groups', :nested_groups do + it 'includes all the invited_groups members including members inherited from ancestor groups' do create(:project_group_link, project: project, group: nested_linked_group) expect(subject).to contain_exactly(linked_group_member, nested_linked_group_member) diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb index da5e9dab058..78224f0b9da 100644 --- a/spec/finders/merge_requests_finder_spec.rb +++ b/spec/finders/merge_requests_finder_spec.rb @@ -42,7 +42,7 @@ describe MergeRequestsFinder do expect(merge_requests).to contain_exactly(merge_request1, merge_request2) end - it 'filters by group including subgroups', :nested_groups do + it 'filters by group including subgroups' do params = { group_id: group.id, include_subgroups: true } merge_requests = described_class.new(user, params).execute diff --git a/spec/finders/todos_finder_spec.rb b/spec/finders/todos_finder_spec.rb index 22318a9946a..f7b35e76925 100644 --- a/spec/finders/todos_finder_spec.rb +++ b/spec/finders/todos_finder_spec.rb @@ -36,7 +36,7 @@ describe TodosFinder do expect(todos).to match_array([todo1]) end - context 'with subgroups', :nested_groups do + context 'with subgroups' do let(:subgroup) { create(:group, parent: group) } let!(:todo3) { create(:todo, user: user, group: subgroup, target: issue) } diff --git a/spec/fixtures/security-reports/dependency_list/gl-dependency-scanning-report.json b/spec/fixtures/security-reports/dependency_list/gl-dependency-scanning-report.json index 1e62d020026..8fb66f6652b 100644 --- a/spec/fixtures/security-reports/dependency_list/gl-dependency-scanning-report.json +++ b/spec/fixtures/security-reports/dependency_list/gl-dependency-scanning-report.json @@ -7,7 +7,7 @@ "message": "Vulnerabilities in libxml2 in nokogiri", "description": " The version of libxml2 packaged with Nokogiri contains several vulnerabilities.\r\n Nokogiri has mitigated these issues by upgrading to libxml 2.9.5.\r\n\r\n It was discovered that a type confusion error existed in libxml2. An\r\n attacker could use this to specially construct XML data that\r\n could cause a denial of service or possibly execute arbitrary\r\n code. (CVE-2017-0663)\r\n\r\n It was discovered that libxml2 did not properly validate parsed entity\r\n references. An attacker could use this to specially construct XML\r\n data that could expose sensitive information. (CVE-2017-7375)\r\n\r\n It was discovered that a buffer overflow existed in libxml2 when\r\n handling HTTP redirects. An attacker could use this to specially\r\n construct XML data that could cause a denial of service or possibly\r\n execute arbitrary code. (CVE-2017-7376)\r\n\r\n Marcel Böhme and Van-Thuan Pham discovered a buffer overflow in\r\n libxml2 when handling elements. An attacker could use this to specially\r\n construct XML data that could cause a denial of service or possibly\r\n execute arbitrary code. (CVE-2017-9047)\r\n\r\n Marcel Böhme and Van-Thuan Pham discovered a buffer overread\r\n in libxml2 when handling elements. An attacker could use this\r\n to specially construct XML data that could cause a denial of\r\n service. (CVE-2017-9048)\r\n\r\n Marcel Böhme and Van-Thuan Pham discovered multiple buffer overreads\r\n in libxml2 when handling parameter-entity references. An attacker\r\n could use these to specially construct XML data that could cause a\r\n denial of service. (CVE-2017-9049, CVE-2017-9050)", "cve": "rails/Gemfile.lock:nokogiri:gemnasium:06565b64-486d-4326-b906-890d9915804d", - "severity": "Unknown", + "severity": "High", "solution": "Upgrade to latest version.", "scanner": { "id": "gemnasium", @@ -48,7 +48,7 @@ "message": "Infinite recursion in parameter entities in nokogiri", "description": "libxml2 incorrectly handles certain parameter entities. An attacker can leverage this with specially constructed XML data to cause libxml2 to consume resources, leading to a denial of service.", "cve": "rails/Gemfile.lock:nokogiri:gemnasium:6a0d56f6-2441-492a-9b14-edb95ac31919", - "severity": "Unknown", + "severity": "High", "solution": "Upgrade to latest version.", "scanner": { "id": "gemnasium", diff --git a/spec/frontend/helpers/indent_helper_spec.js b/spec/frontend/helpers/indent_helper_spec.js new file mode 100644 index 00000000000..fca12f0d1ef --- /dev/null +++ b/spec/frontend/helpers/indent_helper_spec.js @@ -0,0 +1,371 @@ +import IndentHelper from '~/helpers/indent_helper'; + +function createMockTextarea() { + const el = document.createElement('textarea'); + el.setCursor = pos => el.setSelectionRange(pos, pos); + el.setCursorToEnd = () => el.setCursor(el.value.length); + el.selection = () => [el.selectionStart, el.selectionEnd]; + el.cursor = () => { + const [start, end] = el.selection(); + return start === end ? start : undefined; + }; + return el; +} + +describe('indent_helper', () => { + let element; + let ih; + + beforeEach(() => { + element = createMockTextarea(); + ih = new IndentHelper(element); + }); + + describe('indents', () => { + describe('a single line', () => { + it('when on an empty line; and cursor follows', () => { + element.value = ''; + ih.indent(); + expect(element.value).toBe(' '); + expect(element.cursor()).toBe(4); + ih.indent(); + expect(element.value).toBe(' '); + expect(element.cursor()).toBe(8); + }); + + it('when at the start of a line; and cursor stays at start', () => { + element.value = 'foobar'; + element.setCursor(0); + ih.indent(); + expect(element.value).toBe(' foobar'); + expect(element.cursor()).toBe(4); + }); + + it('when the cursor is in the middle; and cursor follows', () => { + element.value = 'foobar'; + element.setCursor(3); + ih.indent(); + expect(element.value).toBe(' foobar'); + expect(element.cursor()).toBe(7); + }); + }); + + describe('several lines', () => { + it('when everything is selected; and everything remains selected', () => { + element.value = 'foo\nbar\nbaz'; + element.setSelectionRange(0, 11); + ih.indent(); + expect(element.value).toBe(' foo\n bar\n baz'); + expect(element.selection()).toEqual([0, 23]); + }); + + it('when all lines are partially selected; and the selection adapts', () => { + element.value = 'foo\nbar\nbaz'; + element.setSelectionRange(2, 9); + ih.indent(); + expect(element.value).toBe(' foo\n bar\n baz'); + expect(element.selection()).toEqual([6, 21]); + }); + + it('when some lines are entirely selected; and entire lines remain selected', () => { + element.value = 'foo\nbar\nbaz'; + element.setSelectionRange(4, 11); + ih.indent(); + expect(element.value).toBe('foo\n bar\n baz'); + expect(element.selection()).toEqual([4, 19]); + }); + + it('when some lines are partially selected; and the selection adapts', () => { + element.value = 'foo\nbar\nbaz'; + element.setSelectionRange(5, 9); + ih.indent(); + expect(element.value).toBe('foo\n bar\n baz'); + expect(element.selection()).toEqual([5 + 4, 9 + 2 * 4]); + }); + + it('having different indentation when some lines are entirely selected; and entire lines remain selected', () => { + element.value = ' foo\nbar\n baz'; + element.setSelectionRange(8, 19); + ih.indent(); + expect(element.value).toBe(' foo\n bar\n baz'); + expect(element.selection()).toEqual([8, 27]); + }); + + it('having different indentation when some lines are partially selected; and the selection adapts', () => { + element.value = ' foo\nbar\n baz'; + element.setSelectionRange(9, 14); + ih.indent(); + expect(element.value).toBe(' foo\n bar\n baz'); + expect(element.selection()).toEqual([13, 22]); + }); + }); + }); + + describe('unindents', () => { + describe('a single line', () => { + it('but does nothing if there is not indent', () => { + element.value = 'foobar'; + element.setCursor(2); + ih.unindent(); + expect(element.value).toBe('foobar'); + expect(element.cursor()).toBe(2); + }); + + it('but does nothing if there is a partial indent', () => { + element.value = ' foobar'; + element.setCursor(1); + ih.unindent(); + expect(element.value).toBe(' foobar'); + expect(element.cursor()).toBe(1); + }); + + it('when the cursor is in the line text; cursor follows', () => { + element.value = ' foobar'; + element.setCursor(6); + ih.unindent(); + expect(element.value).toBe('foobar'); + expect(element.cursor()).toBe(2); + }); + + it('when the cursor is in the indent; and cursor goes to start', () => { + element.value = ' foobar'; + element.setCursor(2); + ih.unindent(); + expect(element.value).toBe('foobar'); + expect(element.cursor()).toBe(0); + }); + + it('when the cursor is at line start; and cursor stays at start', () => { + element.value = ' foobar'; + element.setCursor(0); + ih.unindent(); + expect(element.value).toBe('foobar'); + expect(element.cursor()).toBe(0); + }); + + it('when a selection includes part of the indent and text', () => { + element.value = ' foobar'; + element.setSelectionRange(2, 8); + ih.unindent(); + expect(element.value).toBe('foobar'); + expect(element.selection()).toEqual([0, 4]); + }); + + it('when a selection includes part of the indent only', () => { + element.value = ' foobar'; + element.setSelectionRange(0, 4); + ih.unindent(); + expect(element.value).toBe('foobar'); + expect(element.cursor()).toBe(0); + + element.value = ' foobar'; + element.setSelectionRange(1, 3); + ih.unindent(); + expect(element.value).toBe('foobar'); + expect(element.cursor()).toBe(0); + }); + }); + + describe('several lines', () => { + it('when everything is selected', () => { + element.value = ' foo\n bar\n baz'; + element.setSelectionRange(0, 27); + ih.unindent(); + expect(element.value).toBe('foo\n bar\nbaz'); + expect(element.selection()).toEqual([0, 15]); + }); + + it('when all lines are partially selected', () => { + element.value = ' foo\n bar\n baz'; + element.setSelectionRange(5, 26); + ih.unindent(); + expect(element.value).toBe('foo\n bar\nbaz'); + expect(element.selection()).toEqual([1, 14]); + }); + + it('when all lines are entirely selected', () => { + element.value = ' foo\n bar\n baz'; + element.setSelectionRange(8, 27); + ih.unindent(); + expect(element.value).toBe(' foo\n bar\nbaz'); + expect(element.selection()).toEqual([8, 19]); + }); + + it('when some lines are entirely selected', () => { + element.value = ' foo\n bar\n baz'; + element.setSelectionRange(8, 27); + ih.unindent(); + expect(element.value).toBe(' foo\n bar\nbaz'); + expect(element.selection()).toEqual([8, 19]); + }); + + it('when some lines are partially selected', () => { + element.value = ' foo\n bar\n baz'; + element.setSelectionRange(17, 26); + ih.unindent(); + expect(element.value).toBe(' foo\n bar\nbaz'); + expect(element.selection()).toEqual([13, 18]); + }); + + it('when some lines are partially selected within their indents', () => { + element.value = ' foo\n bar\n baz'; + element.setSelectionRange(10, 22); + ih.unindent(); + expect(element.value).toBe(' foo\n bar\nbaz'); + expect(element.selection()).toEqual([8, 16]); + }); + }); + }); + + describe('newline', () => { + describe('on a single line', () => { + it('auto-indents the new line', () => { + element.value = 'foo\n bar\n baz\n qux'; + + element.setCursor(3); + ih.newline(); + expect(element.value).toBe('foo\n\n bar\n baz\n qux'); + expect(element.cursor()).toBe(4); + + element.setCursor(9); + ih.newline(); + expect(element.value).toBe('foo\n\n bar\n \n baz\n qux'); + expect(element.cursor()).toBe(11); + + element.setCursor(19); + ih.newline(); + expect(element.value).toBe('foo\n\n bar\n \n baz\n \n qux'); + expect(element.cursor()).toBe(24); + + element.setCursor(36); + ih.newline(); + expect(element.value).toBe('foo\n\n bar\n \n baz\n \n qux\n '); + expect(element.cursor()).toBe(45); + }); + + it('splits a line and auto-indents', () => { + element.value = ' foobar'; + element.setCursor(7); + ih.newline(); + expect(element.value).toBe(' foo\n bar'); + expect(element.cursor()).toBe(12); + }); + + it('replaces selection with an indented newline', () => { + element.value = ' foobarbaz'; + element.setSelectionRange(7, 10); + ih.newline(); + expect(element.value).toBe(' foo\n baz'); + expect(element.cursor()).toBe(12); + }); + }); + + it('on several lines.replaces selection with indented newline', () => { + element.value = ' foo\n bar\n baz'; + element.setSelectionRange(4, 17); + ih.newline(); + expect(element.value).toBe(' fo\n az'); + expect(element.cursor()).toBe(7); + }); + }); + + describe('backspace', () => { + let event; + + // This suite tests only the special indent-removing behaviour of the + // backspace() method, since non-special cases are handled natively as a + // backspace keypress. + + beforeEach(() => { + event = { preventDefault: jest.fn() }; + }); + + describe('on a single line', () => { + it('does nothing special if in the line text', () => { + element.value = ' foobar'; + element.setCursor(7); + ih.backspace(event); + expect(event.preventDefault).not.toHaveBeenCalled(); + }); + + it('does nothing special if after a non-leading indent', () => { + element.value = ' foo bar'; + element.setCursor(11); + ih.backspace(event); + expect(event.preventDefault).not.toHaveBeenCalled(); + }); + + it('deletes one leading indent', () => { + element.value = ' foo'; + element.setCursor(8); + ih.backspace(event); + expect(event.preventDefault).toHaveBeenCalled(); + expect(element.value).toBe(' foo'); + expect(element.cursor()).toBe(4); + }); + + it('does nothing if cursor is inside the leading indent', () => { + element.value = ' foo'; + element.setCursor(4); + ih.backspace(event); + expect(event.preventDefault).not.toHaveBeenCalled(); + }); + + it('does nothing if cursor is at the start of the line', () => { + element.value = ' foo'; + element.setCursor(0); + ih.backspace(event); + expect(event.preventDefault).not.toHaveBeenCalled(); + }); + + it('deletes one partial indent', () => { + element.value = ' foo'; + element.setCursor(6); + ih.backspace(event); + expect(event.preventDefault).toHaveBeenCalled(); + expect(element.value).toBe(' foo'); + expect(element.cursor()).toBe(4); + }); + + it('deletes indents sequentially', () => { + element.value = ' foo'; + element.setCursor(10); + ih.backspace(event); + ih.backspace(event); + ih.backspace(event); + expect(event.preventDefault).toHaveBeenCalled(); + expect(element.value).toBe('foo'); + expect(element.cursor()).toBe(0); + }); + }); + + describe('on several lines', () => { + it('deletes indent only on its own line', () => { + element.value = ' foo\n bar\n baz'; + element.setCursor(16); + ih.backspace(event); + expect(event.preventDefault).toHaveBeenCalled(); + expect(element.value).toBe(' foo\n bar\n baz'); + expect(element.cursor()).toBe(12); + }); + + it('has no special behaviour with any range selection', () => { + const text = ' foo\n bar\n baz'; + for (let start = 0; start < text.length; start += 1) { + for (let end = start + 1; end < text.length; end += 1) { + element.value = text; + element.setSelectionRange(start, end); + ih.backspace(event); + expect(event.preventDefault).not.toHaveBeenCalled(); + + // Ensure that the backspace() method doesn't change state + // In reality, these two statements won't hold because the browser + // will natively process the backspace event. + expect(element.value).toBe(text); + expect(element.selection()).toEqual([start, end]); + } + } + }); + }); + }); +}); diff --git a/spec/frontend/lib/utils/common_utils_spec.js b/spec/frontend/lib/utils/common_utils_spec.js new file mode 100644 index 00000000000..e3d3b82d2f3 --- /dev/null +++ b/spec/frontend/lib/utils/common_utils_spec.js @@ -0,0 +1,180 @@ +import * as cu from '~/lib/utils/common_utils'; + +const CMD_ENTITY = '⌘'; + +// Redefine `navigator.platform` because it's unsettable by default in JSDOM. +let platform; +Object.defineProperty(navigator, 'platform', { + configurable: true, + get: () => platform, + set: val => { + platform = val; + }, +}); + +describe('common_utils', () => { + describe('platform leader key helpers', () => { + const CTRL_EVENT = { ctrlKey: true }; + const META_EVENT = { metaKey: true }; + const BOTH_EVENT = { ctrlKey: true, metaKey: true }; + + it('should return "ctrl" if navigator.platform is unset', () => { + expect(cu.getPlatformLeaderKey()).toBe('ctrl'); + expect(cu.getPlatformLeaderKeyHTML()).toBe('Ctrl'); + expect(cu.isPlatformLeaderKey(CTRL_EVENT)).toBe(true); + expect(cu.isPlatformLeaderKey(META_EVENT)).toBe(false); + expect(cu.isPlatformLeaderKey(BOTH_EVENT)).toBe(true); + }); + + it('should return "meta" on MacOS', () => { + navigator.platform = 'MacIntel'; + expect(cu.getPlatformLeaderKey()).toBe('meta'); + expect(cu.getPlatformLeaderKeyHTML()).toBe(CMD_ENTITY); + expect(cu.isPlatformLeaderKey(CTRL_EVENT)).toBe(false); + expect(cu.isPlatformLeaderKey(META_EVENT)).toBe(true); + expect(cu.isPlatformLeaderKey(BOTH_EVENT)).toBe(true); + }); + + it('should return "ctrl" on Linux', () => { + navigator.platform = 'Linux is great'; + expect(cu.getPlatformLeaderKey()).toBe('ctrl'); + expect(cu.getPlatformLeaderKeyHTML()).toBe('Ctrl'); + expect(cu.isPlatformLeaderKey(CTRL_EVENT)).toBe(true); + expect(cu.isPlatformLeaderKey(META_EVENT)).toBe(false); + expect(cu.isPlatformLeaderKey(BOTH_EVENT)).toBe(true); + }); + + it('should return "ctrl" on Windows', () => { + navigator.platform = 'Win32'; + expect(cu.getPlatformLeaderKey()).toBe('ctrl'); + expect(cu.getPlatformLeaderKeyHTML()).toBe('Ctrl'); + expect(cu.isPlatformLeaderKey(CTRL_EVENT)).toBe(true); + expect(cu.isPlatformLeaderKey(META_EVENT)).toBe(false); + expect(cu.isPlatformLeaderKey(BOTH_EVENT)).toBe(true); + }); + }); + + describe('keystroke', () => { + const CODE_BACKSPACE = 8; + const CODE_TAB = 9; + const CODE_ENTER = 13; + const CODE_SPACE = 32; + const CODE_4 = 52; + const CODE_F = 70; + const CODE_Z = 90; + + // Helper function that quickly creates KeyboardEvents + const k = (code, modifiers = '') => ({ + keyCode: code, + which: code, + altKey: modifiers.includes('a'), + ctrlKey: modifiers.includes('c'), + metaKey: modifiers.includes('m'), + shiftKey: modifiers.includes('s'), + }); + + const EV_F = k(CODE_F); + const EV_ALT_F = k(CODE_F, 'a'); + const EV_CONTROL_F = k(CODE_F, 'c'); + const EV_META_F = k(CODE_F, 'm'); + const EV_SHIFT_F = k(CODE_F, 's'); + const EV_CONTROL_SHIFT_F = k(CODE_F, 'cs'); + const EV_ALL_F = k(CODE_F, 'scma'); + const EV_ENTER = k(CODE_ENTER); + const EV_TAB = k(CODE_TAB); + const EV_SPACE = k(CODE_SPACE); + const EV_BACKSPACE = k(CODE_BACKSPACE); + const EV_4 = k(CODE_4); + const EV_$ = k(CODE_4, 's'); + + const { keystroke } = cu; + + it('short-circuits with bad arguments', () => { + expect(keystroke()).toBe(false); + expect(keystroke({})).toBe(false); + }); + + it('handles keystrokes using key codes', () => { + // Test a letter key with modifiers + expect(keystroke(EV_F, CODE_F)).toBe(true); + expect(keystroke(EV_F, CODE_F, '')).toBe(true); + expect(keystroke(EV_ALT_F, CODE_F, 'a')).toBe(true); + expect(keystroke(EV_CONTROL_F, CODE_F, 'c')).toBe(true); + expect(keystroke(EV_META_F, CODE_F, 'm')).toBe(true); + expect(keystroke(EV_SHIFT_F, CODE_F, 's')).toBe(true); + expect(keystroke(EV_CONTROL_SHIFT_F, CODE_F, 'cs')).toBe(true); + expect(keystroke(EV_ALL_F, CODE_F, 'acms')).toBe(true); + + // Test non-letter keys + expect(keystroke(EV_TAB, CODE_TAB)).toBe(true); + expect(keystroke(EV_ENTER, CODE_ENTER)).toBe(true); + expect(keystroke(EV_SPACE, CODE_SPACE)).toBe(true); + expect(keystroke(EV_BACKSPACE, CODE_BACKSPACE)).toBe(true); + + // Test a number/symbol key + expect(keystroke(EV_4, CODE_4)).toBe(true); + expect(keystroke(EV_$, CODE_4, 's')).toBe(true); + + // Test wrong input + expect(keystroke(EV_F, CODE_Z)).toBe(false); + expect(keystroke(EV_SHIFT_F, CODE_F)).toBe(false); + expect(keystroke(EV_SHIFT_F, CODE_F, 'c')).toBe(false); + }); + + it('is case-insensitive', () => { + expect(keystroke(EV_ALL_F, CODE_F, 'ACMS')).toBe(true); + }); + + it('handles bogus inputs', () => { + expect(keystroke(EV_F, 'not a keystroke')).toBe(false); + expect(keystroke(EV_F, null)).toBe(false); + }); + + it('handles exact modifier keys, in any order', () => { + // Test permutations of modifiers + expect(keystroke(EV_ALL_F, CODE_F, 'acms')).toBe(true); + expect(keystroke(EV_ALL_F, CODE_F, 'smca')).toBe(true); + expect(keystroke(EV_ALL_F, CODE_F, 'csma')).toBe(true); + expect(keystroke(EV_CONTROL_SHIFT_F, CODE_F, 'cs')).toBe(true); + expect(keystroke(EV_CONTROL_SHIFT_F, CODE_F, 'sc')).toBe(true); + + // Test wrong modifiers + expect(keystroke(EV_ALL_F, CODE_F, 'smca')).toBe(true); + expect(keystroke(EV_ALL_F, CODE_F)).toBe(false); + expect(keystroke(EV_ALL_F, CODE_F, '')).toBe(false); + expect(keystroke(EV_ALL_F, CODE_F, 'c')).toBe(false); + expect(keystroke(EV_ALL_F, CODE_F, 'ca')).toBe(false); + expect(keystroke(EV_ALL_F, CODE_F, 'ms')).toBe(false); + expect(keystroke(EV_CONTROL_SHIFT_F, CODE_F, 'cs')).toBe(true); + expect(keystroke(EV_CONTROL_SHIFT_F, CODE_F, 'c')).toBe(false); + expect(keystroke(EV_CONTROL_SHIFT_F, CODE_F, 's')).toBe(false); + expect(keystroke(EV_CONTROL_SHIFT_F, CODE_F, 'csa')).toBe(false); + expect(keystroke(EV_CONTROL_SHIFT_F, CODE_F, 'm')).toBe(false); + expect(keystroke(EV_SHIFT_F, CODE_F, 's')).toBe(true); + expect(keystroke(EV_SHIFT_F, CODE_F, 'c')).toBe(false); + expect(keystroke(EV_SHIFT_F, CODE_F, 'csm')).toBe(false); + }); + + it('handles the platform-dependent leader key', () => { + navigator.platform = 'Win32'; + let EV_UNDO = k(CODE_Z, 'c'); + let EV_REDO = k(CODE_Z, 'cs'); + expect(keystroke(EV_UNDO, CODE_Z, 'l')).toBe(true); + expect(keystroke(EV_UNDO, CODE_Z, 'c')).toBe(true); + expect(keystroke(EV_UNDO, CODE_Z, 'm')).toBe(false); + expect(keystroke(EV_REDO, CODE_Z, 'sl')).toBe(true); + expect(keystroke(EV_REDO, CODE_Z, 'sc')).toBe(true); + expect(keystroke(EV_REDO, CODE_Z, 'sm')).toBe(false); + + navigator.platform = 'MacIntel'; + EV_UNDO = k(CODE_Z, 'm'); + EV_REDO = k(CODE_Z, 'ms'); + expect(keystroke(EV_UNDO, CODE_Z, 'l')).toBe(true); + expect(keystroke(EV_UNDO, CODE_Z, 'c')).toBe(false); + expect(keystroke(EV_UNDO, CODE_Z, 'm')).toBe(true); + expect(keystroke(EV_REDO, CODE_Z, 'sl')).toBe(true); + expect(keystroke(EV_REDO, CODE_Z, 'sc')).toBe(false); + expect(keystroke(EV_REDO, CODE_Z, 'sm')).toBe(true); + }); + }); +}); diff --git a/spec/frontend/lib/utils/undo_stack_spec.js b/spec/frontend/lib/utils/undo_stack_spec.js new file mode 100644 index 00000000000..31ad0e77d6f --- /dev/null +++ b/spec/frontend/lib/utils/undo_stack_spec.js @@ -0,0 +1,237 @@ +import UndoStack from '~/lib/utils/undo_stack'; + +import { isEqual } from 'underscore'; + +describe('UndoStack', () => { + let stack; + + beforeEach(() => { + stack = new UndoStack(); + }); + + afterEach(() => { + // Make sure there's not pending saves + const history = Array.from(stack.history); + jest.runAllTimers(); + expect(stack.history).toEqual(history); + }); + + it('is blank on construction', () => { + expect(stack.isEmpty()).toBe(true); + expect(stack.history).toEqual([]); + expect(stack.cursor).toBe(-1); + expect(stack.canUndo()).toBe(false); + expect(stack.canRedo()).toBe(false); + }); + + it('handles simple undo/redo behaviour', () => { + stack.save(10); + stack.save(11); + stack.save(12); + + expect(stack.history).toEqual([10, 11, 12]); + expect(stack.cursor).toBe(2); + expect(stack.current()).toBe(12); + expect(stack.isEmpty()).toBe(false); + expect(stack.canUndo()).toBe(true); + expect(stack.canRedo()).toBe(false); + + stack.undo(); + expect(stack.history).toEqual([10, 11, 12]); + expect(stack.current()).toBe(11); + expect(stack.canUndo()).toBe(true); + expect(stack.canRedo()).toBe(true); + + stack.undo(); + expect(stack.current()).toBe(10); + expect(stack.canUndo()).toBe(false); + expect(stack.canRedo()).toBe(true); + + stack.redo(); + expect(stack.current()).toBe(11); + + stack.redo(); + expect(stack.current()).toBe(12); + expect(stack.isEmpty()).toBe(false); + expect(stack.canUndo()).toBe(true); + expect(stack.canRedo()).toBe(false); + + // Saving should clear the redo stack + stack.undo(); + stack.save(13); + expect(stack.history).toEqual([10, 11, 13]); + expect(stack.current()).toBe(13); + }); + + it('clear() should clear the undo history', () => { + stack.save(0); + stack.save(1); + stack.save(2); + stack.clear(); + expect(stack.history).toEqual([]); + expect(stack.current()).toBeUndefined(); + }); + + it('undo and redo are no-ops if unavailable', () => { + stack.save(10); + expect(stack.canRedo()).toBe(false); + expect(stack.canUndo()).toBe(false); + + stack.save(11); + expect(stack.canRedo()).toBe(false); + expect(stack.canUndo()).toBe(true); + + expect(stack.redo()).toBeUndefined(); + expect(stack.history).toEqual([10, 11]); + expect(stack.current()).toBe(11); + expect(stack.canRedo()).toBe(false); + expect(stack.canUndo()).toBe(true); + + expect(stack.undo()).toBe(10); + expect(stack.undo()).toBeUndefined(); + expect(stack.history).toEqual([10, 11]); + expect(stack.current()).toBe(10); + expect(stack.canRedo()).toBe(true); + expect(stack.canUndo()).toBe(false); + }); + + it('should not save a duplicate state', () => { + stack.save(10); + stack.save(11); + stack.save(11); + stack.save(10); + stack.save(10); + + expect(stack.history).toEqual([10, 11, 10]); + }); + + it('uses the === operator to detect duplicates', () => { + stack.save(10); + stack.save(10); + expect(stack.history).toEqual([10]); + + // eslint-disable-next-line eqeqeq + expect(2 == '2' && '2' == 2).toBe(true); + stack.clear(); + stack.save(2); + stack.save(2); + stack.save('2'); + stack.save('2'); + stack.save(2); + expect(stack.history).toEqual([2, '2', 2]); + + const obj = {}; + stack.clear(); + stack.save(obj); + stack.save(obj); + stack.save({}); + stack.save({}); + expect(stack.history).toEqual([{}, {}, {}]); + }); + + it('should allow custom comparators', () => { + stack.comparator = isEqual; + const obj = {}; + stack.clear(); + stack.save(obj); + stack.save(obj); + stack.save({}); + stack.save({}); + expect(stack.history).toEqual([{}]); + }); + + it('should enforce a max number of undo states', () => { + // Try 2000 saves. Only the last 1000 should be preserved. + const sequence = Array(2000) + .fill(0) + .map((el, i) => i); + sequence.forEach(stack.save.bind(stack)); + expect(stack.history.length).toBe(1000); + expect(stack.history).toEqual(sequence.slice(1000)); + expect(stack.current()).toBe(1999); + expect(stack.canUndo()).toBe(true); + expect(stack.canRedo()).toBe(false); + + // Saving drops the oldest elements from the stack + stack.save('end'); + expect(stack.history.length).toBe(1000); + expect(stack.current()).toBe('end'); + expect(stack.history).toEqual([...sequence.slice(1001), 'end']); + + // If states were undone but the history is full, can still add. + stack.undo(); + stack.undo(); + expect(stack.current()).toBe(1998); + stack.save(3000); + expect(stack.history.length).toBe(999); + // should be [1001, 1002, ..., 1998, 3000] + expect(stack.history).toEqual([...sequence.slice(1001, 1999), 3000]); + + // Try a different max length + stack = new UndoStack(2); + stack.save(0); + expect(stack.history).toEqual([0]); + stack.save(1); + expect(stack.history).toEqual([0, 1]); + stack.save(2); + expect(stack.history).toEqual([1, 2]); + }); + + describe('scheduled saves', () => { + it('should work', () => { + // Schedules 1000 ms ahead by default + stack.save(0); + stack.scheduleSave(1); + expect(stack.history).toEqual([0]); + jest.advanceTimersByTime(999); + expect(stack.history).toEqual([0]); + jest.advanceTimersByTime(1); + expect(stack.history).toEqual([0, 1]); + }); + + it('should have an adjustable delay', () => { + stack.scheduleSave(2, 100); + jest.advanceTimersByTime(100); + expect(stack.history).toEqual([2]); + }); + + it('should cancel previous scheduled saves', () => { + stack.scheduleSave(3); + jest.advanceTimersByTime(100); + stack.scheduleSave(4); + jest.runAllTimers(); + expect(stack.history).toEqual([4]); + }); + + it('should be canceled by explicit saves', () => { + stack.scheduleSave(5); + stack.save(6); + jest.runAllTimers(); + expect(stack.history).toEqual([6]); + }); + + it('should be canceled by undos and redos', () => { + stack.save(1); + stack.save(2); + stack.scheduleSave(3); + stack.undo(); + jest.runAllTimers(); + expect(stack.history).toEqual([1, 2]); + expect(stack.current()).toBe(1); + + stack.scheduleSave(4); + stack.redo(); + jest.runAllTimers(); + expect(stack.history).toEqual([1, 2]); + expect(stack.current()).toBe(2); + }); + + it('should be persisted immediately with saveNow()', () => { + stack.scheduleSave(7); + stack.scheduleSave(8); + stack.saveNow(); + jest.runAllTimers(); + expect(stack.history).toEqual([8]); + }); + }); +}); diff --git a/spec/frontend/projects/gke_cluster_namespace/gke_cluster_namespace_spec.js b/spec/frontend/projects/gke_cluster_namespace/gke_cluster_namespace_spec.js new file mode 100644 index 00000000000..7b8df03d3c3 --- /dev/null +++ b/spec/frontend/projects/gke_cluster_namespace/gke_cluster_namespace_spec.js @@ -0,0 +1,61 @@ +import initGkeNamespace from '~/projects/gke_cluster_namespace'; + +describe('GKE cluster namespace', () => { + const changeEvent = new Event('change'); + const isHidden = el => el.classList.contains('hidden'); + const hasDisabledInput = el => el.querySelector('input').disabled; + + let glManagedCheckbox; + let selfManaged; + let glManaged; + + beforeEach(() => { + setFixtures(` + <input class="js-gl-managed" type="checkbox" value="1" checked /> + <div class="js-namespace"> + <input type="text" /> + </div> + <div class="js-namespace-prefixed"> + <input type="text" /> + </div> + `); + + glManagedCheckbox = document.querySelector('.js-gl-managed'); + selfManaged = document.querySelector('.js-namespace'); + glManaged = document.querySelector('.js-namespace-prefixed'); + + initGkeNamespace(); + }); + + describe('GKE cluster namespace toggles', () => { + it('initially displays the GitLab-managed label and input', () => { + expect(isHidden(glManaged)).toEqual(false); + expect(hasDisabledInput(glManaged)).toEqual(false); + + expect(isHidden(selfManaged)).toEqual(true); + expect(hasDisabledInput(selfManaged)).toEqual(true); + }); + + it('displays the self-managed label and input when the Gitlab-managed checkbox is unchecked', () => { + glManagedCheckbox.checked = false; + glManagedCheckbox.dispatchEvent(changeEvent); + + expect(isHidden(glManaged)).toEqual(true); + expect(hasDisabledInput(glManaged)).toEqual(true); + + expect(isHidden(selfManaged)).toEqual(false); + expect(hasDisabledInput(selfManaged)).toEqual(false); + }); + + it('displays the GitLab-managed label and input when the Gitlab-managed checkbox is checked', () => { + glManagedCheckbox.checked = true; + glManagedCheckbox.dispatchEvent(changeEvent); + + expect(isHidden(glManaged)).toEqual(false); + expect(hasDisabledInput(glManaged)).toEqual(false); + + expect(isHidden(selfManaged)).toEqual(true); + expect(hasDisabledInput(selfManaged)).toEqual(true); + }); + }); +}); diff --git a/spec/graphql/resolvers/namespace_projects_resolver_spec.rb b/spec/graphql/resolvers/namespace_projects_resolver_spec.rb index 20e197e9f73..47591445fc0 100644 --- a/spec/graphql/resolvers/namespace_projects_resolver_spec.rb +++ b/spec/graphql/resolvers/namespace_projects_resolver_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Resolvers::NamespaceProjectsResolver, :nested_groups do +describe Resolvers::NamespaceProjectsResolver do include GraphqlHelpers let(:current_user) { create(:user) } diff --git a/spec/helpers/auto_devops_helper_spec.rb b/spec/helpers/auto_devops_helper_spec.rb index d2540696b17..e80388f9ea7 100644 --- a/spec/helpers/auto_devops_helper_spec.rb +++ b/spec/helpers/auto_devops_helper_spec.rb @@ -118,7 +118,7 @@ describe AutoDevopsHelper do it { is_expected.to eq('instance enabled') } end - context 'with groups', :nested_groups do + context 'with groups' do before do receiver.update(parent: parent) end @@ -170,7 +170,7 @@ describe AutoDevopsHelper do it { is_expected.to eq('instance enabled') } end - context 'with groups', :nested_groups do + context 'with groups' do let(:receiver) { create(:project, :repository, namespace: group) } before do @@ -203,7 +203,7 @@ describe AutoDevopsHelper do it { is_expected.to be_nil } end - context 'with groups', :nested_groups do + context 'with groups' do let(:receiver) { create(:project, :repository, namespace: group) } context 'when auto devops is disabled on group level' do diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb index 1763c46389a..037b16c90ed 100644 --- a/spec/helpers/groups_helper_spec.rb +++ b/spec/helpers/groups_helper_spec.rb @@ -86,7 +86,7 @@ describe GroupsHelper do end end - describe 'group_title', :nested_groups do + describe 'group_title' do let(:group) { create(:group) } let(:nested_group) { create(:group, parent: group) } let(:deep_nested_group) { create(:group, parent: nested_group) } @@ -99,7 +99,7 @@ describe GroupsHelper do end # rubocop:disable Layout/SpaceBeforeComma - describe '#share_with_group_lock_help_text', :nested_groups do + describe '#share_with_group_lock_help_text' do let!(:root_group) { create(:group) } let!(:subgroup) { create(:group, parent: root_group) } let!(:sub_subgroup) { create(:group, parent: subgroup) } @@ -230,7 +230,7 @@ describe GroupsHelper do end end - describe 'parent_group_options', :nested_groups do + describe 'parent_group_options' do let(:current_user) { create(:user) } let(:group) { create(:group, name: 'group') } let(:group2) { create(:group, name: 'group2') } diff --git a/spec/helpers/namespaces_helper_spec.rb b/spec/helpers/namespaces_helper_spec.rb index 601f864ef36..e38513f6d94 100644 --- a/spec/helpers/namespaces_helper_spec.rb +++ b/spec/helpers/namespaces_helper_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe NamespacesHelper, :postgresql do +describe NamespacesHelper do let!(:admin) { create(:admin) } let!(:admin_project_creation_level) { nil } let!(:admin_group) do @@ -109,7 +109,7 @@ describe NamespacesHelper, :postgresql do expect(options).to include(user_group.name) end - context 'when nested groups are available', :nested_groups do + context 'when nested groups are available' do it 'includes groups nested in groups the user can administer' do allow(helper).to receive(:current_user).and_return(user) child_group = create(:group, :private, parent: user_group) diff --git a/spec/helpers/submodule_helper_spec.rb b/spec/helpers/submodule_helper_spec.rb index b922b910c89..ab4ef899119 100644 --- a/spec/helpers/submodule_helper_spec.rb +++ b/spec/helpers/submodule_helper_spec.rb @@ -229,6 +229,19 @@ describe SubmoduleHelper do end end end + + context 'unknown submodule' do + before do + # When there is no `.gitmodules` file, or if `.gitmodules` does not + # know the submodule at the specified path, + # `Repository#submodule_url_for` returns `nil` + stub_url(nil) + end + + it 'returns no links' do + expect(subject).to eq([nil, nil]) + end + end end context 'as view helpers in view context' do diff --git a/spec/javascripts/badges/components/badge_list_spec.js b/spec/javascripts/badges/components/badge_list_spec.js index 2f72c9ed89d..2fa807657de 100644 --- a/spec/javascripts/badges/components/badge_list_spec.js +++ b/spec/javascripts/badges/components/badge_list_spec.js @@ -60,7 +60,7 @@ describe('BadgeList component', () => { Vue.nextTick() .then(() => { - const loadingIcon = vm.$el.querySelector('.spinner'); + const loadingIcon = vm.$el.querySelector('.gl-spinner'); expect(loadingIcon).toBeVisible(); }) diff --git a/spec/javascripts/badges/components/badge_spec.js b/spec/javascripts/badges/components/badge_spec.js index 4e4d1ae2e99..c82a03a628a 100644 --- a/spec/javascripts/badges/components/badge_spec.js +++ b/spec/javascripts/badges/components/badge_spec.js @@ -15,7 +15,7 @@ describe('Badge component', () => { const buttons = vm.$el.querySelectorAll('button'); return { badgeImage: vm.$el.querySelector('img.project-badge'), - loadingIcon: vm.$el.querySelector('.spinner'), + loadingIcon: vm.$el.querySelector('.gl-spinner'), reloadButton: buttons[buttons.length - 1], }; }; diff --git a/spec/javascripts/boards/board_list_spec.js b/spec/javascripts/boards/board_list_spec.js index 9c9b435d7fd..6774a46ed58 100644 --- a/spec/javascripts/boards/board_list_spec.js +++ b/spec/javascripts/boards/board_list_spec.js @@ -148,7 +148,7 @@ describe('Board list component', () => { component.list.loadingMore = true; Vue.nextTick(() => { - expect(component.$el.querySelector('.board-list-count .spinner')).not.toBeNull(); + expect(component.$el.querySelector('.board-list-count .gl-spinner')).not.toBeNull(); done(); }); diff --git a/spec/javascripts/registry/components/app_spec.js b/spec/javascripts/registry/components/app_spec.js index 7b9b8d2b039..e7675669f7a 100644 --- a/spec/javascripts/registry/components/app_spec.js +++ b/spec/javascripts/registry/components/app_spec.js @@ -106,7 +106,7 @@ describe('Registry List', () => { it('should render a loading spinner', done => { Vue.nextTick(() => { - expect(vm.$el.querySelector('.spinner')).not.toBe(null); + expect(vm.$el.querySelector('.gl-spinner')).not.toBe(null); done(); }); }); diff --git a/spec/javascripts/reports/components/grouped_test_reports_app_spec.js b/spec/javascripts/reports/components/grouped_test_reports_app_spec.js index a17494966a3..1f1e626ed33 100644 --- a/spec/javascripts/reports/components/grouped_test_reports_app_spec.js +++ b/spec/javascripts/reports/components/grouped_test_reports_app_spec.js @@ -34,7 +34,7 @@ describe('Grouped Test Reports App', () => { it('renders success summary text', done => { setTimeout(() => { - expect(vm.$el.querySelector('.fa-spinner')).toBeNull(); + expect(vm.$el.querySelector('.gl-spinner')).toBeNull(); expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual( 'Test summary contained no changed test results out of 11 total tests', ); @@ -61,7 +61,7 @@ describe('Grouped Test Reports App', () => { it('renders success summary text', done => { setTimeout(() => { - expect(vm.$el.querySelector('.spinner')).not.toBeNull(); + expect(vm.$el.querySelector('.gl-spinner')).not.toBeNull(); expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual( 'Test summary results are being parsed', ); @@ -81,7 +81,7 @@ describe('Grouped Test Reports App', () => { it('renders failed summary text + new badge', done => { setTimeout(() => { - expect(vm.$el.querySelector('.spinner')).toBeNull(); + expect(vm.$el.querySelector('.gl-spinner')).toBeNull(); expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual( 'Test summary contained 2 failed test results out of 11 total tests', ); @@ -109,7 +109,7 @@ describe('Grouped Test Reports App', () => { it('renders summary text', done => { setTimeout(() => { - expect(vm.$el.querySelector('.spinner')).toBeNull(); + expect(vm.$el.querySelector('.gl-spinner')).toBeNull(); expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual( 'Test summary contained 2 failed test results and 2 fixed test results out of 11 total tests', ); @@ -137,7 +137,7 @@ describe('Grouped Test Reports App', () => { it('renders summary text', done => { setTimeout(() => { - expect(vm.$el.querySelector('.spinner')).toBeNull(); + expect(vm.$el.querySelector('.gl-spinner')).toBeNull(); expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual( 'Test summary contained 2 fixed test results out of 11 total tests', ); @@ -190,7 +190,7 @@ describe('Grouped Test Reports App', () => { }); it('renders loading summary text with loading icon', done => { - expect(vm.$el.querySelector('.spinner')).not.toBeNull(); + expect(vm.$el.querySelector('.gl-spinner')).not.toBeNull(); expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual( 'Test summary results are being parsed', ); diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js index f622f52a7b9..5aac37d28df 100644 --- a/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js +++ b/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js @@ -18,7 +18,7 @@ describe('MR widget status icon component', () => { it('renders loading icon', () => { vm = mountComponent(Component, { status: 'loading' }); - expect(vm.$el.querySelector('.mr-widget-icon span').classList).toContain('spinner'); + expect(vm.$el.querySelector('.mr-widget-icon span').classList).toContain('gl-spinner'); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js index d93badf8cd3..55a11a72551 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js @@ -38,7 +38,9 @@ describe('MRWidgetAutoMergeFailed', () => { Vue.nextTick(() => { expect(vm.$el.querySelector('button').getAttribute('disabled')).toEqual('disabled'); - expect(vm.$el.querySelector('button .loading-container span').classList).toContain('spinner'); + expect(vm.$el.querySelector('button .loading-container span').classList).toContain( + 'gl-spinner', + ); done(); }); }); diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js index 96e512d222a..70c70eca746 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js @@ -20,7 +20,7 @@ describe('MRWidgetChecking', () => { }); it('renders loading icon', () => { - expect(vm.$el.querySelector('.mr-widget-icon span').classList).toContain('spinner'); + expect(vm.$el.querySelector('.mr-widget-icon span').classList).toContain('gl-spinner'); }); it('renders information about merging', () => { diff --git a/spec/javascripts/vue_shared/components/file_icon_spec.js b/spec/javascripts/vue_shared/components/file_icon_spec.js index 5bea8c43da3..1f61e19fa84 100644 --- a/spec/javascripts/vue_shared/components/file_icon_spec.js +++ b/spec/javascripts/vue_shared/components/file_icon_spec.js @@ -72,7 +72,7 @@ describe('File Icon component', () => { const { classList } = vm.$el.querySelector('.loading-container span'); - expect(classList.contains('spinner')).toEqual(true); + expect(classList.contains('gl-spinner')).toEqual(true); }); it('should add a special class and a size class', () => { diff --git a/spec/javascripts/vue_shared/components/header_ci_component_spec.js b/spec/javascripts/vue_shared/components/header_ci_component_spec.js index a9c1a67b39b..2b059e5e9f4 100644 --- a/spec/javascripts/vue_shared/components/header_ci_component_spec.js +++ b/spec/javascripts/vue_shared/components/header_ci_component_spec.js @@ -88,7 +88,7 @@ describe('Header CI Component', () => { vm.actions[0].isLoading = true; Vue.nextTick(() => { - expect(vm.$el.querySelector('.btn .spinner').getAttribute('style')).toBeFalsy(); + expect(vm.$el.querySelector('.btn .gl-spinner').getAttribute('style')).toBeFalsy(); done(); }); }); diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb index f0a5dc8d0d7..91edadfa234 100644 --- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb @@ -363,7 +363,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do expect(doc.css('a')).to be_empty end - it 'supports parent group references', :nested_groups do + it 'supports parent group references' do milestone.update!(group: parent_group) doc = reference_filter("See #{reference}") @@ -396,7 +396,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do context 'when group milestone' do let(:group_milestone) { create(:milestone, title: 'group_milestone', group: group) } - context 'for subgroups', :nested_groups do + context 'for subgroups' do let(:sub_group) { create(:group, parent: group) } let(:sub_group_milestone) { create(:milestone, title: 'sub_group_milestone', group: sub_group) } diff --git a/spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb b/spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb index 40001cea22e..a5436149818 100644 --- a/spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb +++ b/spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb @@ -40,7 +40,11 @@ describe Gitlab::Auth::OAuth::AuthHash do last_name: last_name_ascii, name: name_ascii, nickname: nickname_ascii, - uid: uid_ascii + uid: uid_ascii, + address: { + locality: 'some locality', + country: 'some country' + } } end @@ -51,6 +55,7 @@ describe Gitlab::Auth::OAuth::AuthHash do it { expect(auth_hash.username).to eql nickname_utf8 } it { expect(auth_hash.name).to eql name_utf8 } it { expect(auth_hash.password).not_to be_empty } + it { expect(auth_hash.location).to eq 'some locality, some country' } end context 'email not provided' do diff --git a/spec/lib/gitlab/background_migration/fix_cross_project_label_links_spec.rb b/spec/lib/gitlab/background_migration/fix_cross_project_label_links_spec.rb index 20af63bc6c8..84ccd57a50b 100644 --- a/spec/lib/gitlab/background_migration/fix_cross_project_label_links_spec.rb +++ b/spec/lib/gitlab/background_migration/fix_cross_project_label_links_spec.rb @@ -75,7 +75,7 @@ describe Gitlab::BackgroundMigration::FixCrossProjectLabelLinks, :migration, sch create_resource(target_type, 1, 2) end - it 'ignores label links referencing ancestor group labels', :nested_groups do + it 'ignores label links referencing ancestor group labels' do labels_table.create(id: 4, title: 'bug', color: 'red', project_id: 2, type: 'ProjectLabel') label_links_table.create(label_id: 4, target_type: target_type, target_id: 1) link = label_links_table.create(label_id: 1, target_type: target_type, target_id: 1) @@ -85,7 +85,7 @@ describe Gitlab::BackgroundMigration::FixCrossProjectLabelLinks, :migration, sch expect(link.reload.label_id).to eq(1) end - it 'checks also issues and MRs in subgroups', :nested_groups do + it 'checks also issues and MRs in subgroups' do link = label_links_table.create(label_id: 2, target_type: target_type, target_id: 1) subject.perform(1, 100) diff --git a/spec/lib/gitlab/bare_repository_import/importer_spec.rb b/spec/lib/gitlab/bare_repository_import/importer_spec.rb index f4759b69538..ebd3c28f130 100644 --- a/spec/lib/gitlab/bare_repository_import/importer_spec.rb +++ b/spec/lib/gitlab/bare_repository_import/importer_spec.rb @@ -103,7 +103,7 @@ describe Gitlab::BareRepositoryImport::Importer, :seed_helper do end end - context 'with subgroups', :nested_groups do + context 'with subgroups' do let(:project_path) { 'a-group/a-sub-group/a-project' } let(:existing_group) do @@ -188,20 +188,6 @@ describe Gitlab::BareRepositoryImport::Importer, :seed_helper do end end - context 'when subgroups are not available' do - let(:project_path) { 'a-group/a-sub-group/a-project' } - - before do - expect(Group).to receive(:supports_nested_objects?) { false } - end - - describe '#create_project_if_needed' do - it 'raises an error' do - expect { importer.create_project_if_needed }.to raise_error('Nested groups are not supported on MySQL') - end - end - end - def prepare_repository(project_path, source_project) repo_path = File.join(base_dir, project_path) diff --git a/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb b/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb index a528707c9dc..959f3fdc289 100644 --- a/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb +++ b/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb @@ -8,7 +8,7 @@ describe Gitlab::Database::Count::ReltuplesCountStrategy do subject { described_class.new(models).count } - describe '#count', :postgresql do + describe '#count' do let(:models) { [Project, Identity] } context 'when reltuples is up to date' do diff --git a/spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb b/spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb index a57f033b5ed..cc9d778e579 100644 --- a/spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb +++ b/spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb @@ -12,7 +12,7 @@ describe Gitlab::Database::Count::TablesampleCountStrategy do subject { strategy.count } - describe '#count', :postgresql do + describe '#count' do let(:estimates) do { Project => threshold + 1, diff --git a/spec/lib/gitlab/database/grant_spec.rb b/spec/lib/gitlab/database/grant_spec.rb index 5ebf3f399b6..9e62dc14d77 100644 --- a/spec/lib/gitlab/database/grant_spec.rb +++ b/spec/lib/gitlab/database/grant_spec.rb @@ -8,7 +8,7 @@ describe Gitlab::Database::Grant do expect(described_class.create_and_execute_trigger?('users')).to eq(true) end - it 'returns false when the user can not create and/or execute a trigger', :postgresql do + it 'returns false when the user can not create and/or execute a trigger' do # In case of MySQL the user may have SUPER permissions, making it # impossible to have `false` returned when running tests; hence we only # run these tests on PostgreSQL. diff --git a/spec/lib/gitlab/group_search_results_spec.rb b/spec/lib/gitlab/group_search_results_spec.rb index 2734fcef0a0..53a91a35ec9 100644 --- a/spec/lib/gitlab/group_search_results_spec.rb +++ b/spec/lib/gitlab/group_search_results_spec.rb @@ -20,7 +20,7 @@ describe Gitlab::GroupSearchResults do expect(result).to eq [user1] end - it 'returns the user belonging to the subgroup matching the search query', :nested_groups do + it 'returns the user belonging to the subgroup matching the search query' do user1 = create(:user, username: 'gob_bluth') subgroup = create(:group, parent: group) create(:group_member, :developer, user: user1, group: subgroup) @@ -32,7 +32,7 @@ describe Gitlab::GroupSearchResults do expect(result).to eq [user1] end - it 'returns the user belonging to the parent group matching the search query', :nested_groups do + it 'returns the user belonging to the parent group matching the search query' do user1 = create(:user, username: 'gob_bluth') parent_group = create(:group, children: [group]) create(:group_member, :developer, user: user1, group: parent_group) @@ -44,7 +44,7 @@ describe Gitlab::GroupSearchResults do expect(result).to eq [user1] end - it 'does not return the user belonging to the private subgroup', :nested_groups do + it 'does not return the user belonging to the private subgroup' do user1 = create(:user, username: 'gob_bluth') subgroup = create(:group, :private, parent: group) create(:group_member, :developer, user: user1, group: subgroup) diff --git a/spec/lib/gitlab/manifest_import/manifest_spec.rb b/spec/lib/gitlab/manifest_import/manifest_spec.rb index ab305fb2316..ded93e23c08 100644 --- a/spec/lib/gitlab/manifest_import/manifest_spec.rb +++ b/spec/lib/gitlab/manifest_import/manifest_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::ManifestImport::Manifest, :postgresql do +describe Gitlab::ManifestImport::Manifest do let(:file) { File.open(Rails.root.join('spec/fixtures/aosp_manifest.xml')) } let(:manifest) { described_class.new(file) } diff --git a/spec/lib/gitlab/manifest_import/project_creator_spec.rb b/spec/lib/gitlab/manifest_import/project_creator_spec.rb index 1d01d437535..a7487972f51 100644 --- a/spec/lib/gitlab/manifest_import/project_creator_spec.rb +++ b/spec/lib/gitlab/manifest_import/project_creator_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::ManifestImport::ProjectCreator, :postgresql do +describe Gitlab::ManifestImport::ProjectCreator do let(:group) { create(:group) } let(:user) { create(:user) } let(:repository) do diff --git a/spec/lib/gitlab/object_hierarchy_spec.rb b/spec/lib/gitlab/object_hierarchy_spec.rb index e6e9ae3223e..bfd456cdd7e 100644 --- a/spec/lib/gitlab/object_hierarchy_spec.rb +++ b/spec/lib/gitlab/object_hierarchy_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::ObjectHierarchy, :postgresql do +describe Gitlab::ObjectHierarchy do let!(:parent) { create(:group) } let!(:child1) { create(:group, parent: parent) } let!(:child2) { create(:group, parent: child1) } diff --git a/spec/lib/gitlab/performance_bar_spec.rb b/spec/lib/gitlab/performance_bar_spec.rb index ee3c571c9c0..71c109db1f1 100644 --- a/spec/lib/gitlab/performance_bar_spec.rb +++ b/spec/lib/gitlab/performance_bar_spec.rb @@ -96,7 +96,7 @@ describe Gitlab::PerformanceBar do end end - context 'when allowed group is nested', :nested_groups do + context 'when allowed group is nested' do let!(:nested_my_group) { create(:group, parent: create(:group, path: 'my-org'), path: 'my-group') } before do @@ -110,7 +110,7 @@ describe Gitlab::PerformanceBar do end end - context 'when a nested group has the same path', :nested_groups do + context 'when a nested group has the same path' do before do create(:group, :nested, path: 'my-group').add_developer(user) end diff --git a/spec/lib/gitlab/project_authorizations_spec.rb b/spec/lib/gitlab/project_authorizations_spec.rb index bd0bc2c9044..75e2d5e1319 100644 --- a/spec/lib/gitlab/project_authorizations_spec.rb +++ b/spec/lib/gitlab/project_authorizations_spec.rb @@ -20,13 +20,7 @@ describe Gitlab::ProjectAuthorizations do end let(:authorizations) do - klass = if Group.supports_nested_objects? - Gitlab::ProjectAuthorizations::WithNestedGroups - else - Gitlab::ProjectAuthorizations::WithoutNestedGroups - end - - klass.new(user).calculate + described_class.new(user).calculate end it 'returns the correct number of authorizations' do @@ -46,28 +40,26 @@ describe Gitlab::ProjectAuthorizations do expect(mapping[group_project.id]).to eq(Gitlab::Access::DEVELOPER) end - if Group.supports_nested_objects? - context 'with nested groups' do - let!(:nested_group) { create(:group, parent: group) } - let!(:nested_project) { create(:project, namespace: nested_group) } + context 'with nested groups' do + let!(:nested_group) { create(:group, parent: group) } + let!(:nested_project) { create(:project, namespace: nested_group) } - it 'includes nested groups' do - expect(authorizations.pluck(:project_id)).to include(nested_project.id) - end + it 'includes nested groups' do + expect(authorizations.pluck(:project_id)).to include(nested_project.id) + end - it 'inherits access levels when the user is not a member of a nested group' do - mapping = map_access_levels(authorizations) + it 'inherits access levels when the user is not a member of a nested group' do + mapping = map_access_levels(authorizations) - expect(mapping[nested_project.id]).to eq(Gitlab::Access::DEVELOPER) - end + expect(mapping[nested_project.id]).to eq(Gitlab::Access::DEVELOPER) + end - it 'uses the greatest access level when a user is a member of a nested group' do - nested_group.add_maintainer(user) + it 'uses the greatest access level when a user is a member of a nested group' do + nested_group.add_maintainer(user) - mapping = map_access_levels(authorizations) + mapping = map_access_levels(authorizations) - expect(mapping[nested_project.id]).to eq(Gitlab::Access::MAINTAINER) - end + expect(mapping[nested_project.id]).to eq(Gitlab::Access::MAINTAINER) end end end diff --git a/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb b/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb index 1a5a38b5d99..b451844f06c 100644 --- a/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb +++ b/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb @@ -45,6 +45,9 @@ describe Gitlab::SidekiqMiddleware::MemoryKiller do expect(subject).to receive(:sleep).with(10).ordered expect(Process).to receive(:kill).with('SIGKILL', pid).ordered + expect(Sidekiq.logger) + .to receive(:warn).with(class: 'TestWorker', message: anything, pid: pid, signal: anything).at_least(:once) + run end diff --git a/spec/lib/gitlab/sql/cte_spec.rb b/spec/lib/gitlab/sql/cte_spec.rb index d6763c7b2e1..5d2164491b5 100644 --- a/spec/lib/gitlab/sql/cte_spec.rb +++ b/spec/lib/gitlab/sql/cte_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::SQL::CTE, :postgresql do +describe Gitlab::SQL::CTE do describe '#to_arel' do it 'generates an Arel relation for the CTE body' do relation = User.where(id: 1) diff --git a/spec/lib/gitlab/sql/recursive_cte_spec.rb b/spec/lib/gitlab/sql/recursive_cte_spec.rb index 7fe39dd5a96..407a4d8a247 100644 --- a/spec/lib/gitlab/sql/recursive_cte_spec.rb +++ b/spec/lib/gitlab/sql/recursive_cte_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::SQL::RecursiveCTE, :postgresql do +describe Gitlab::SQL::RecursiveCTE do let(:cte) { described_class.new(:cte_name) } describe '#to_arel' do diff --git a/spec/lib/gitlab/submodule_links_spec.rb b/spec/lib/gitlab/submodule_links_spec.rb new file mode 100644 index 00000000000..a84602cd07d --- /dev/null +++ b/spec/lib/gitlab/submodule_links_spec.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::SubmoduleLinks do + let(:submodule_item) { double(id: 'hash', path: 'gitlab-ce') } + let(:repo) { double } + let(:links) { described_class.new(repo) } + + describe '#for' do + subject { links.for(submodule_item, 'ref') } + + context 'when there is no .gitmodules file' do + before do + stub_urls(nil) + end + + it 'returns no links' do + expect(subject).to eq([nil, nil]) + end + end + + context 'when the submodule is unknown' do + before do + stub_urls({ 'path' => 'url' }) + end + + it 'returns no links' do + expect(subject).to eq([nil, nil]) + end + end + + context 'when the submodule is known' do + before do + stub_urls({ 'gitlab-ce' => 'git@gitlab.com:gitlab-org/gitlab-ce.git' }) + end + + it 'returns links' do + expect(subject).to eq(['https://gitlab.com/gitlab-org/gitlab-ce', 'https://gitlab.com/gitlab-org/gitlab-ce/tree/hash']) + end + end + end + + def stub_urls(urls) + allow(repo).to receive(:submodule_urls_for).and_return(urls) + end +end diff --git a/spec/lib/prometheus/pid_provider_spec.rb b/spec/lib/prometheus/pid_provider_spec.rb index e7d500612b1..ba843b27254 100644 --- a/spec/lib/prometheus/pid_provider_spec.rb +++ b/spec/lib/prometheus/pid_provider_spec.rb @@ -25,22 +25,60 @@ describe Prometheus::PidProvider do before do stub_const('Unicorn::Worker', Class.new) hide_const('Puma') + + expect(described_class).to receive(:process_name) + .at_least(:once) + .and_return(process_name) end - context 'when `Prometheus::Client::Support::Unicorn` provides worker_id' do - before do - expect(::Prometheus::Client::Support::Unicorn).to receive(:worker_id).and_return(1) + context 'when unicorn master is specified in process name' do + context 'when running in Omnibus' do + context 'before the process was renamed' do + let(:process_name) { "/opt/gitlab/embedded/bin/unicorn"} + + it { is_expected.to eq 'unicorn_master' } + end + + context 'after the process was renamed' do + let(:process_name) { "unicorn master -D -E production -c /var/opt/gitlab/gitlab-rails/etc/unicorn.rb /opt/gitlab/embedded/service/gitlab-rails/config.ru" } + + it { is_expected.to eq 'unicorn_master' } + end end - it { is_expected.to eq 'unicorn_1' } + context 'when in development env' do + context 'before the process was renamed' do + let(:process_name) { "path_to_bindir/bin/unicorn_rails"} + + it { is_expected.to eq 'unicorn_master' } + end + + context 'after the process was renamed' do + let(:process_name) { "unicorn_rails master -c /gitlab_dir/config/unicorn.rb -E development" } + + it { is_expected.to eq 'unicorn_master' } + end + end end - context 'when no worker_id is provided from `Prometheus::Client::Support::Unicorn`' do - before do - expect(::Prometheus::Client::Support::Unicorn).to receive(:worker_id).and_return(nil) + context 'when unicorn worker id is specified in process name' do + context 'when running in Omnibus' do + let(:process_name) { "unicorn worker[1] -D -E production -c /var/opt/gitlab/gitlab-rails/etc/unicorn.rb /opt/gitlab/embedded/service/gitlab-rails/config.ru" } + + it { is_expected.to eq 'unicorn_1' } end - it { is_expected.to eq 'unicorn_master' } + context 'when in development env' do + let(:process_name) { "unicorn_rails worker[1] -c gitlab_dir/config/unicorn.rb -E development" } + + it { is_expected.to eq 'unicorn_1' } + end + end + + context 'when no specified unicorn master or worker id in process name' do + let(:process_name) { "bin/unknown_process"} + + it { is_expected.to eq "process_#{Process.pid}" } end end @@ -48,20 +86,20 @@ describe Prometheus::PidProvider do before do stub_const('Puma', Module.new) hide_const('Unicorn::Worker') + + expect(described_class).to receive(:process_name) + .at_least(:once) + .and_return(process_name) end context 'when cluster worker id is specified in process name' do - before do - expect(described_class).to receive(:process_name).and_return('puma: cluster worker 1: 17483 [gitlab-puma-worker]') - end + let(:process_name) { 'puma: cluster worker 1: 17483 [gitlab-puma-worker]' } it { is_expected.to eq 'puma_1' } end context 'when no worker id is specified in process name' do - before do - expect(described_class).to receive(:process_name).and_return('bin/puma') - end + let(:process_name) { 'bin/puma' } it { is_expected.to eq 'puma_master' } end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 56bbcc4c306..dcc4b70a382 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -640,7 +640,7 @@ describe Notify do project.request_access(user) project.requesters.find_by(user_id: user.id) end - subject { described_class.member_access_requested_email('project', project_member.id, recipient.notification_email) } + subject { described_class.member_access_requested_email('project', project_member.id, recipient.id) } it_behaves_like 'an email sent from GitLab' it_behaves_like 'it should not have Gmail Actions links' @@ -737,9 +737,9 @@ describe Notify do describe 'project invitation accepted' do let(:invited_user) { create(:user, name: 'invited user') } - let(:maintainer) { create(:user).tap { |u| project.add_maintainer(u) } } + let(:recipient) { create(:user).tap { |u| project.add_maintainer(u) } } let(:project_member) do - invitee = invite_to_project(project, inviter: maintainer) + invitee = invite_to_project(project, inviter: recipient) invitee.accept_invite!(invited_user) invitee end @@ -747,6 +747,7 @@ describe Notify do subject { described_class.member_invite_accepted_email('project', project_member.id) } it_behaves_like 'an email sent from GitLab' + it_behaves_like 'an email sent to a user' it_behaves_like 'it should not have Gmail Actions links' it_behaves_like "a user cannot unsubscribe through footer link" it_behaves_like 'appearance header and footer enabled' @@ -762,16 +763,17 @@ describe Notify do end describe 'project invitation declined' do - let(:maintainer) { create(:user).tap { |u| project.add_maintainer(u) } } + let(:recipient) { create(:user).tap { |u| project.add_maintainer(u) } } let(:project_member) do - invitee = invite_to_project(project, inviter: maintainer) + invitee = invite_to_project(project, inviter: recipient) invitee.decline_invite! invitee end - subject { described_class.member_invite_declined_email('Project', project.id, project_member.invite_email, maintainer.id) } + subject { described_class.member_invite_declined_email('Project', project.id, project_member.invite_email, recipient.id) } it_behaves_like 'an email sent from GitLab' + it_behaves_like 'an email sent to a user' it_behaves_like 'it should not have Gmail Actions links' it_behaves_like "a user cannot unsubscribe through footer link" it_behaves_like 'appearance header and footer enabled' @@ -1087,9 +1089,10 @@ describe Notify do group.request_access(user) group.requesters.find_by(user_id: user.id) end - subject { described_class.member_access_requested_email('group', group_member.id, recipient.notification_email) } + subject { described_class.member_access_requested_email('group', group_member.id, recipient.id) } it_behaves_like 'an email sent from GitLab' + it_behaves_like 'an email sent to a user' it_behaves_like 'it should not have Gmail Actions links' it_behaves_like "a user cannot unsubscribe through footer link" it_behaves_like 'appearance header and footer enabled' @@ -1111,9 +1114,11 @@ describe Notify do group.request_access(user) group.requesters.find_by(user_id: user.id) end + let(:recipient) { user } subject { described_class.member_access_denied_email('group', group.id, user.id) } it_behaves_like 'an email sent from GitLab' + it_behaves_like 'an email sent to a user' it_behaves_like 'it should not have Gmail Actions links' it_behaves_like "a user cannot unsubscribe through footer link" it_behaves_like 'appearance header and footer enabled' @@ -1128,10 +1133,12 @@ describe Notify do describe 'group access changed' do let(:group_member) { create(:group_member, group: group, user: user) } + let(:recipient) { user } subject { described_class.member_access_granted_email('group', group_member.id) } it_behaves_like 'an email sent from GitLab' + it_behaves_like 'an email sent to a user' it_behaves_like 'it should not have Gmail Actions links' it_behaves_like "a user cannot unsubscribe through footer link" it_behaves_like 'appearance header and footer enabled' diff --git a/spec/migrations/change_outbound_local_requests_whitelist_default_spec.rb b/spec/migrations/change_outbound_local_requests_whitelist_default_spec.rb new file mode 100644 index 00000000000..232f6f090c3 --- /dev/null +++ b/spec/migrations/change_outbound_local_requests_whitelist_default_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require 'spec_helper' +require Rails.root.join('db', 'migrate', '20190725012225_change_outbound_local_requests_whitelist_default.rb') + +describe ChangeOutboundLocalRequestsWhitelistDefault, :migration do + let(:application_settings) { table(:application_settings) } + + it 'defaults to empty array' do + setting = application_settings.create! + setting_with_value = application_settings.create!(outbound_local_requests_whitelist: '{a,b}') + + expect(application_settings.where(outbound_local_requests_whitelist: nil).count).to eq(1) + + migrate! + + expect(application_settings.where(outbound_local_requests_whitelist: nil).count).to eq(0) + expect(setting.reload.outbound_local_requests_whitelist).to eq([]) + expect(setting_with_value.reload.outbound_local_requests_whitelist).to eq(%w[a b]) + end +end diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index e24bbc39761..1fb83fbb088 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -1799,7 +1799,7 @@ describe Ci::Pipeline, :mailer do end end - describe '.latest_successful_for' do + describe '.latest_successful_for_ref' do include_context 'with some outdated pipelines' let!(:latest_successful_pipeline) do @@ -1807,7 +1807,20 @@ describe Ci::Pipeline, :mailer do end it 'returns the latest successful pipeline' do - expect(described_class.latest_successful_for('ref')) + expect(described_class.latest_successful_for_ref('ref')) + .to eq(latest_successful_pipeline) + end + end + + describe '.latest_successful_for_sha' do + include_context 'with some outdated pipelines' + + let!(:latest_successful_pipeline) do + create_pipeline(:success, 'ref', 'awesomesha', project) + end + + it 'returns the latest successful pipeline' do + expect(described_class.latest_successful_for_sha('awesomesha')) .to eq(latest_successful_pipeline) end end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 24ea059e871..78b151631c1 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -146,7 +146,7 @@ describe Ci::Runner do expect(described_class.belonging_to_parent_group_of_project(project.id)).to contain_exactly(runner) end - context 'with a parent group with a runner', :nested_groups do + context 'with a parent group with a runner' do let(:runner) { create(:ci_runner, :group, groups: [parent_group]) } let(:project) { create(:project, group: group) } let(:group) { create(:group, parent: parent_group) } diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb index 52661178d76..8f2f1b200e4 100644 --- a/spec/models/clusters/cluster_spec.rb +++ b/spec/models/clusters/cluster_spec.rb @@ -342,7 +342,7 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do end end - context 'when sub-group has configured kubernetes cluster', :nested_groups do + context 'when sub-group has configured kubernetes cluster' do let(:sub_group_cluster) { create(:cluster, :provided_by_gcp, :group) } let(:sub_group) { sub_group_cluster.group } let(:project) { create(:project, group: sub_group) } diff --git a/spec/models/concerns/deployment_platform_spec.rb b/spec/models/concerns/deployment_platform_spec.rb index c4f9f62ece5..27f535487c8 100644 --- a/spec/models/concerns/deployment_platform_spec.rb +++ b/spec/models/concerns/deployment_platform_spec.rb @@ -45,7 +45,7 @@ describe DeploymentPlatform do is_expected.to eq(group_cluster.platform_kubernetes) end - context 'when child group has configured kubernetes cluster', :nested_groups do + context 'when child group has configured kubernetes cluster' do let(:child_group1) { create(:group, parent: group) } let!(:child_group1_cluster) { create(:cluster_for_group, groups: [child_group1]) } diff --git a/spec/models/concerns/group_descendant_spec.rb b/spec/models/concerns/group_descendant_spec.rb index 194caac3fce..192e884f3e8 100644 --- a/spec/models/concerns/group_descendant_spec.rb +++ b/spec/models/concerns/group_descendant_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe GroupDescendant, :nested_groups do +describe GroupDescendant do let(:parent) { create(:group) } let(:subgroup) { create(:group, parent: parent) } let(:subsub_group) { create(:group, parent: subgroup) } @@ -84,7 +84,7 @@ describe GroupDescendant, :nested_groups do it 'tracks the exception when a parent was not preloaded' do expect(Gitlab::Sentry).to receive(:track_exception).and_call_original - expect { GroupDescendant.build_hierarchy([subsub_group]) }.to raise_error(ArgumentError) + expect { described_class.build_hierarchy([subsub_group]) }.to raise_error(ArgumentError) end it 'recovers if a parent was not reloaded by querying for the parent' do @@ -93,7 +93,7 @@ describe GroupDescendant, :nested_groups do # this does not raise in production, so stubbing it here. allow(Gitlab::Sentry).to receive(:track_exception) - expect(GroupDescendant.build_hierarchy([subsub_group])).to eq(expected_hierarchy) + expect(described_class.build_hierarchy([subsub_group])).to eq(expected_hierarchy) end it 'raises an error if not all elements were preloaded' do diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index e19da41c3fe..39680c0e51a 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -128,7 +128,7 @@ describe Issuable do expect(build_issuable(milestone.id).milestone_available?).to be_truthy end - it 'returns true with a milestone from the the parent of the issue project group', :nested_groups do + it 'returns true with a milestone from the the parent of the issue project group' do parent = create(:group) group.update(parent: parent) milestone = create(:milestone, group: parent) diff --git a/spec/models/deployment_metrics_spec.rb b/spec/models/deployment_metrics_spec.rb index 0aadb1f3a5e..7c574a8b6c8 100644 --- a/spec/models/deployment_metrics_spec.rb +++ b/spec/models/deployment_metrics_spec.rb @@ -49,18 +49,6 @@ describe DeploymentMetrics do it { is_expected.to be_truthy } end - - context 'fallback deployment platform' do - let(:cluster) { create(:cluster, :provided_by_user, environment_scope: '*', projects: [deployment.project]) } - let!(:prometheus) { create(:clusters_applications_prometheus, :installed, cluster: cluster) } - - before do - expect(deployment.project).to receive(:deployment_platform).and_return(cluster.platform) - expect(cluster.application_prometheus).to receive(:can_query?).and_return(true) - end - - it { is_expected.to be_truthy } - end end end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 90e0900445e..7e9bbf5a407 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -71,7 +71,7 @@ describe Group do end end - describe '#notification_settings', :nested_groups do + describe '#notification_settings' do let(:user) { create(:user) } let(:group) { create(:group) } let(:sub_group) { create(:group, parent_id: group.id) } @@ -237,7 +237,7 @@ describe Group do it { is_expected.to match_array([private_group, internal_group, group]) } end - context 'when user is a member of private subgroup', :postgresql do + context 'when user is a member of private subgroup' do let!(:private_subgroup) { create(:group, :private, parent: private_group) } before do @@ -416,7 +416,7 @@ describe Group do it { expect(group.last_owner?(@members[:owner])).to be_falsy } end - context 'with owners from a parent', :postgresql do + context 'with owners from a parent' do before do parent_group = create(:group) create(:group_member, :owner, group: parent_group) @@ -524,7 +524,7 @@ describe Group do it { expect(subject.parent).to be_kind_of(described_class) } end - describe '#members_with_parents', :nested_groups do + describe '#members_with_parents' do let!(:group) { create(:group, :nested) } let!(:maintainer) { group.parent.add_user(create(:user), GroupMember::MAINTAINER) } let!(:developer) { group.add_user(create(:user), GroupMember::DEVELOPER) } @@ -535,7 +535,7 @@ describe Group do end end - describe '#direct_and_indirect_members', :nested_groups do + describe '#direct_and_indirect_members' do let!(:group) { create(:group, :nested) } let!(:sub_group) { create(:group, parent: group) } let!(:maintainer) { group.parent.add_user(create(:user), GroupMember::MAINTAINER) } @@ -552,7 +552,7 @@ describe Group do end end - describe '#users_with_descendants', :nested_groups do + describe '#users_with_descendants' do let(:user_a) { create(:user) } let(:user_b) { create(:user) } @@ -571,7 +571,7 @@ describe Group do end end - describe '#direct_and_indirect_users', :nested_groups do + describe '#direct_and_indirect_users' do let(:user_a) { create(:user) } let(:user_b) { create(:user) } let(:user_c) { create(:user) } @@ -601,7 +601,7 @@ describe Group do end end - describe '#project_users_with_descendants', :nested_groups do + describe '#project_users_with_descendants' do let(:user_a) { create(:user) } let(:user_b) { create(:user) } let(:user_c) { create(:user) } @@ -678,7 +678,7 @@ describe Group do end end - context 'sub groups and projects', :nested_groups do + context 'sub groups and projects' do it 'enables two_factor_requirement for group member' do group.add_user(user, GroupMember::OWNER) @@ -687,7 +687,7 @@ describe Group do expect(user.reload.require_two_factor_authentication_from_group).to be_truthy end - context 'expanded group members', :nested_groups do + context 'expanded group members' do let(:indirect_user) { create(:user) } it 'enables two_factor_requirement for subgroup member' do @@ -720,7 +720,7 @@ describe Group do expect(user.reload.require_two_factor_authentication_from_group).to be_falsey end - it 'does not enable two_factor_requirement for subgroup child project member', :nested_groups do + it 'does not enable two_factor_requirement for subgroup child project member' do subgroup = create(:group, :nested, parent: group) project = create(:project, group: subgroup) project.add_maintainer(user) @@ -820,7 +820,7 @@ describe Group do it_behaves_like 'ref is protected' end - context 'when group has children', :postgresql do + context 'when group has children' do let(:group_child) { create(:group, parent: group) } let(:group_child_2) { create(:group, parent: group_child) } let(:group_child_3) { create(:group, parent: group_child_2) } @@ -843,7 +843,7 @@ describe Group do end end - describe '#highest_group_member', :nested_groups do + describe '#highest_group_member' do let(:nested_group) { create(:group, parent: group) } let(:nested_group_2) { create(:group, parent: nested_group) } let(:user) { create(:user) } @@ -932,7 +932,7 @@ describe Group do it { is_expected.to eq(config) } end - context 'with parent groups', :nested_groups do + context 'with parent groups' do where(:instance_value, :parent_value, :group_value, :config) do # Instance level enabled true | nil | nil | { status: true, scope: :instance } diff --git a/spec/models/members/group_member_spec.rb b/spec/models/members/group_member_spec.rb index f227abd3dae..ebb0bfca369 100644 --- a/spec/models/members/group_member_spec.rb +++ b/spec/models/members/group_member_spec.rb @@ -69,7 +69,7 @@ describe GroupMember do end end - context 'access levels', :nested_groups do + context 'access levels' do context 'with parent group' do it_behaves_like 'inherited access level as a member of entity' do let(:entity) { create(:group, parent: parent_entity) } diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb index 497764b6825..79c39b81196 100644 --- a/spec/models/members/project_member_spec.rb +++ b/spec/models/members/project_member_spec.rb @@ -130,7 +130,7 @@ describe ProjectMember do end end - context 'with parent group and a subgroup', :nested_groups do + context 'with parent group and a subgroup' do it_behaves_like 'inherited access level as a member of entity' do let(:subgroup) { create(:group, parent: parent_entity) } let(:entity) { create(:project, group: subgroup) } diff --git a/spec/models/namespace/root_storage_statistics_spec.rb b/spec/models/namespace/root_storage_statistics_spec.rb index 3229a32234e..5341278db7c 100644 --- a/spec/models/namespace/root_storage_statistics_spec.rb +++ b/spec/models/namespace/root_storage_statistics_spec.rb @@ -56,7 +56,7 @@ RSpec.describe Namespace::RootStorageStatistics, type: :model do it_behaves_like 'data refresh' - context 'with subgroups', :nested_groups do + context 'with subgroups' do let(:subgroup1) { create(:group, parent: namespace)} let(:subgroup2) { create(:group, parent: subgroup1)} diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index f908f3504e0..2b9c3c43af9 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -191,7 +191,7 @@ describe Namespace do end end - describe '#ancestors_upto', :nested_groups do + describe '#ancestors_upto' do let(:parent) { create(:group) } let(:child) { create(:group, parent: parent) } let(:child2) { create(:group, parent: child) } @@ -271,7 +271,7 @@ describe Namespace do end end - context 'with subgroups', :nested_groups do + context 'with subgroups' do let(:parent) { create(:group, name: 'parent', path: 'parent') } let(:new_parent) { create(:group, name: 'new_parent', path: 'new_parent') } let(:child) { create(:group, name: 'child', path: 'child', parent: parent) } @@ -475,7 +475,7 @@ describe Namespace do end end - describe '#self_and_hierarchy', :nested_groups do + describe '#self_and_hierarchy' do let!(:group) { create(:group, path: 'git_lab') } let!(:nested_group) { create(:group, parent: group) } let!(:deep_nested_group) { create(:group, parent: nested_group) } @@ -490,7 +490,7 @@ describe Namespace do end end - describe '#ancestors', :nested_groups do + describe '#ancestors' do let(:group) { create(:group) } let(:nested_group) { create(:group, parent: group) } let(:deep_nested_group) { create(:group, parent: nested_group) } @@ -504,7 +504,7 @@ describe Namespace do end end - describe '#self_and_ancestors', :nested_groups do + describe '#self_and_ancestors' do let(:group) { create(:group) } let(:nested_group) { create(:group, parent: group) } let(:deep_nested_group) { create(:group, parent: nested_group) } @@ -518,7 +518,7 @@ describe Namespace do end end - describe '#descendants', :nested_groups do + describe '#descendants' do let!(:group) { create(:group, path: 'git_lab') } let!(:nested_group) { create(:group, parent: group) } let!(:deep_nested_group) { create(:group, parent: nested_group) } @@ -534,7 +534,7 @@ describe Namespace do end end - describe '#self_and_descendants', :nested_groups do + describe '#self_and_descendants' do let!(:group) { create(:group, path: 'git_lab') } let!(:nested_group) { create(:group, parent: group) } let!(:deep_nested_group) { create(:group, parent: nested_group) } @@ -550,7 +550,7 @@ describe Namespace do end end - describe '#users_with_descendants', :nested_groups do + describe '#users_with_descendants' do let(:user_a) { create(:user) } let(:user_b) { create(:user) } @@ -597,7 +597,7 @@ describe Namespace do it { expect(group.all_pipelines.to_a).to match_array([pipeline1, pipeline2]) } end - describe '#share_with_group_lock with subgroups', :nested_groups do + describe '#share_with_group_lock with subgroups' do context 'when creating a subgroup' do let(:subgroup) { create(:group, parent: root_group )} @@ -738,7 +738,7 @@ describe Namespace do end describe '#root_ancestor' do - it 'returns the top most ancestor', :nested_groups do + it 'returns the top most ancestor' do root_group = create(:group) nested_group = create(:group, parent: root_group) deep_nested_group = create(:group, parent: nested_group) diff --git a/spec/models/notification_recipient_spec.rb b/spec/models/notification_recipient_spec.rb index 20278d81f6d..4122736c148 100644 --- a/spec/models/notification_recipient_spec.rb +++ b/spec/models/notification_recipient_spec.rb @@ -49,7 +49,7 @@ describe NotificationRecipient do end context '#notification_setting' do - context 'for child groups', :nested_groups do + context 'for child groups' do let!(:moved_group) { create(:group) } let(:group) { create(:group) } let(:sub_group_1) { create(:group, parent: group) } diff --git a/spec/models/postgresql/replication_slot_spec.rb b/spec/models/postgresql/replication_slot_spec.rb index 95ae204a8a8..d435fccc09a 100644 --- a/spec/models/postgresql/replication_slot_spec.rb +++ b/spec/models/postgresql/replication_slot_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe Postgresql::ReplicationSlot, :postgresql do +describe Postgresql::ReplicationSlot do describe '.in_use?' do it 'returns true when replication slots are present' do expect(described_class).to receive(:exists?).and_return(true) diff --git a/spec/models/project_group_link_spec.rb b/spec/models/project_group_link_spec.rb index dad5506900b..cd997224122 100644 --- a/spec/models/project_group_link_spec.rb +++ b/spec/models/project_group_link_spec.rb @@ -25,7 +25,7 @@ describe ProjectGroupLink do expect(project_group_link).not_to be_valid end - it "doesn't allow a project to be shared with an ancestor of the group it is in", :nested_groups do + it "doesn't allow a project to be shared with an ancestor of the group it is in" do project_group_link.group = parent_group expect(project_group_link).not_to be_valid diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 7d458324c20..24dfd0c5605 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -2019,62 +2019,33 @@ describe Project do end end - describe '#latest_successful_build_for' do + describe '#latest_successful_build_for_ref' do let(:project) { create(:project, :repository) } let(:pipeline) { create_pipeline(project) } - context 'with many builds' do - it 'gives the latest builds from latest pipeline' do - pipeline1 = create_pipeline(project) - pipeline2 = create_pipeline(project) - create_build(pipeline1, 'test') - create_build(pipeline1, 'test2') - build1_p2 = create_build(pipeline2, 'test') - create_build(pipeline2, 'test2') - - expect(project.latest_successful_build_for(build1_p2.name)) - .to eq(build1_p2) - end - end + it_behaves_like 'latest successful build for sha or ref' - context 'with succeeded pipeline' do - let!(:build) { create_build } + subject { project.latest_successful_build_for_ref(build_name) } - context 'standalone pipeline' do - it 'returns builds for ref for default_branch' do - expect(project.latest_successful_build_for(build.name)) - .to eq(build) - end + context 'with a specified ref' do + let(:build) { create_build } - it 'returns empty relation if the build cannot be found' do - expect(project.latest_successful_build_for('TAIL')) - .to be_nil - end - end - - context 'with some pending pipeline' do - before do - create_build(create_pipeline(project, 'pending')) - end + subject { project.latest_successful_build_for_ref(build.name, project.default_branch) } - it 'gives the latest build from latest pipeline' do - expect(project.latest_successful_build_for(build.name)) - .to eq(build) - end - end + it { is_expected.to eq(build) } end + end - context 'with pending pipeline' do - it 'returns empty relation' do - pipeline.update(status: 'pending') - pending_build = create_build(pipeline) + describe '#latest_successful_build_for_sha' do + let(:project) { create(:project, :repository) } + let(:pipeline) { create_pipeline(project) } - expect(project.latest_successful_build_for(pending_build.name)).to be_nil - end - end + it_behaves_like 'latest successful build for sha or ref' + + subject { project.latest_successful_build_for_sha(build_name, project.commit.sha) } end - describe '#latest_successful_build_for!' do + describe '#latest_successful_build_for_ref!' do let(:project) { create(:project, :repository) } let(:pipeline) { create_pipeline(project) } @@ -2087,7 +2058,7 @@ describe Project do build1_p2 = create_build(pipeline2, 'test') create_build(pipeline2, 'test2') - expect(project.latest_successful_build_for(build1_p2.name)) + expect(project.latest_successful_build_for_ref!(build1_p2.name)) .to eq(build1_p2) end end @@ -2097,12 +2068,12 @@ describe Project do context 'standalone pipeline' do it 'returns builds for ref for default_branch' do - expect(project.latest_successful_build_for!(build.name)) + expect(project.latest_successful_build_for_ref!(build.name)) .to eq(build) end it 'returns exception if the build cannot be found' do - expect { project.latest_successful_build_for!(build.name, 'TAIL') } + expect { project.latest_successful_build_for_ref!(build.name, 'TAIL') } .to raise_error(ActiveRecord::RecordNotFound) end end @@ -2113,7 +2084,7 @@ describe Project do end it 'gives the latest build from latest pipeline' do - expect(project.latest_successful_build_for!(build.name)) + expect(project.latest_successful_build_for_ref!(build.name)) .to eq(build) end end @@ -2124,7 +2095,7 @@ describe Project do pipeline.update(status: 'pending') pending_build = create_build(pipeline) - expect { project.latest_successful_build_for!(pending_build.name) } + expect { project.latest_successful_build_for_ref!(pending_build.name) } .to raise_error(ActiveRecord::RecordNotFound) end end @@ -2292,7 +2263,7 @@ describe Project do end end - describe '#ancestors_upto', :nested_groups do + describe '#ancestors_upto' do let(:parent) { create(:group) } let(:child) { create(:group, parent: parent) } let(:child2) { create(:group, parent: child) } @@ -2331,7 +2302,7 @@ describe Project do it { is_expected.to eq(group) } end - context 'in a nested group', :nested_groups do + context 'in a nested group' do let(:root) { create(:group) } let(:child) { create(:group, parent: root) } let(:project) { create(:project, group: child) } @@ -2479,7 +2450,7 @@ describe Project do expect(forked_project.in_fork_network_of?(project)).to be_truthy end - it 'is true for a fork of a fork', :postgresql do + it 'is true for a fork of a fork' do other_fork = fork_project(forked_project) expect(other_fork.in_fork_network_of?(project)).to be_truthy @@ -3801,7 +3772,7 @@ describe Project do end end - context 'when enabled on root parent', :nested_groups do + context 'when enabled on root parent' do let(:parent_group) { create(:group, parent: create(:group, :auto_devops_enabled)) } context 'when auto devops instance enabled' do @@ -3821,7 +3792,7 @@ describe Project do end end - context 'when disabled on root parent', :nested_groups do + context 'when disabled on root parent' do let(:parent_group) { create(:group, parent: create(:group, :auto_devops_disabled)) } context 'when auto devops instance enabled' do @@ -4033,7 +4004,7 @@ describe Project do context 'with a ref that is not the default branch' do it 'returns the latest successful pipeline for the given ref' do - expect(project.ci_pipelines).to receive(:latest_successful_for).with('foo') + expect(project.ci_pipelines).to receive(:latest_successful_for_ref).with('foo') project.latest_successful_pipeline_for('foo') end @@ -4061,7 +4032,7 @@ describe Project do it 'memoizes and returns the latest successful pipeline for the default branch' do pipeline = double(:pipeline) - expect(project.ci_pipelines).to receive(:latest_successful_for) + expect(project.ci_pipelines).to receive(:latest_successful_for_ref) .with(project.default_branch) .and_return(pipeline) .once @@ -4264,18 +4235,16 @@ describe Project do expect(project.badges.count).to eq 3 end - if Group.supports_nested_objects? - context 'with nested_groups' do - let(:parent_group) { create(:group) } + context 'with nested_groups' do + let(:parent_group) { create(:group) } - before do - create_list(:group_badge, 2, group: project_group) - project_group.update(parent: parent_group) - end + before do + create_list(:group_badge, 2, group: project_group) + project_group.update(parent: parent_group) + end - it 'returns the project and the project nested groups badges' do - expect(project.badges.count).to eq 5 - end + it 'returns the project and the project nested groups badges' do + expect(project.badges.count).to eq 5 end end end diff --git a/spec/models/todo_spec.rb b/spec/models/todo_spec.rb index b5bf294790a..9aeef7c3b4b 100644 --- a/spec/models/todo_spec.rb +++ b/spec/models/todo_spec.rb @@ -262,11 +262,7 @@ describe Todo do todo2 = create(:todo, group: child_group) todos = described_class.for_group_and_descendants(parent_group) - expect(todos).to include(todo1) - - # Nested groups only work on PostgreSQL, so on MySQL todo2 won't be - # present. - expect(todos).to include(todo2) if Gitlab::Database.postgresql? + expect(todos).to contain_exactly(todo1, todo2) end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 2d20f8c78cc..35c335c5b5c 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -985,7 +985,7 @@ describe User do it { expect(user.namespaces).to contain_exactly(user.namespace, group) } it { expect(user.manageable_namespaces).to contain_exactly(user.namespace, group) } - context 'with child groups', :nested_groups do + context 'with child groups' do let!(:subgroup) { create(:group, parent: group) } describe '#manageable_namespaces' do @@ -2082,11 +2082,7 @@ describe User do subject { user.membership_groups } - if Group.supports_nested_objects? - it { is_expected.to contain_exactly parent_group, child_group } - else - it { is_expected.to contain_exactly parent_group } - end + it { is_expected.to contain_exactly parent_group, child_group } end describe '#authorizations_for_projects' do @@ -2386,7 +2382,7 @@ describe User do it_behaves_like :member end - context 'with subgroup with different owner for project runner', :nested_groups do + context 'with subgroup with different owner for project runner' do let(:group) { create(:group) } let(:another_user) { create(:user) } let(:subgroup) { create(:group, parent: group) } @@ -2490,22 +2486,16 @@ describe User do group.add_owner(user) end - if Group.supports_nested_objects? - it 'returns all groups' do - is_expected.to match_array [ - group, - nested_group_1, nested_group_1_1, - nested_group_2, nested_group_2_1 - ] - end - else - it 'returns the top-level groups' do - is_expected.to match_array [group] - end + it 'returns all groups' do + is_expected.to match_array [ + group, + nested_group_1, nested_group_1_1, + nested_group_2, nested_group_2_1 + ] end end - context 'user is member of the first child (internal node), branch 1', :nested_groups do + context 'user is member of the first child (internal node), branch 1' do before do nested_group_1.add_owner(user) end @@ -2518,7 +2508,7 @@ describe User do end end - context 'user is member of the first child (internal node), branch 2', :nested_groups do + context 'user is member of the first child (internal node), branch 2' do before do nested_group_2.add_owner(user) end @@ -2531,7 +2521,7 @@ describe User do end end - context 'user is member of the last child (leaf node)', :nested_groups do + context 'user is member of the last child (leaf node)' do before do nested_group_1_1.add_owner(user) end @@ -2687,7 +2677,7 @@ describe User do end end - context 'with 2FA requirement from expanded groups', :nested_groups do + context 'with 2FA requirement from expanded groups' do let!(:group1) { create :group, require_two_factor_authentication: true } let!(:group1a) { create :group, parent: group1 } @@ -2702,7 +2692,7 @@ describe User do end end - context 'with 2FA requirement on nested child group', :nested_groups do + context 'with 2FA requirement on nested child group' do let!(:group1) { create :group, require_two_factor_authentication: false } let!(:group1a) { create :group, require_two_factor_authentication: true, parent: group1 } diff --git a/spec/policies/group_member_policy_spec.rb b/spec/policies/group_member_policy_spec.rb index 7bd7184cffe..a4f3301a064 100644 --- a/spec/policies/group_member_policy_spec.rb +++ b/spec/policies/group_member_policy_spec.rb @@ -58,7 +58,7 @@ describe GroupMemberPolicy do end end - context 'with the group parent', :postgresql do + context 'with the group parent' do let(:current_user) { create :user } let(:subgroup) { create(:group, :private, parent: group)} diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index dc3675a7b9e..be55d94daec 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -51,7 +51,7 @@ describe GroupPolicy do it { expect_allowed(:read_label, :read_list) } - context 'in subgroups', :nested_groups do + context 'in subgroups' do let(:subgroup) { create(:group, :private, parent: group) } let(:project) { create(:project, namespace: subgroup) } @@ -104,8 +104,6 @@ describe GroupPolicy do end it 'allows every maintainer permission plus creating subgroups' do - allow(Group).to receive(:supports_nested_objects?).and_return(true) - create_subgroup_permission = [:create_subgroup] updated_maintainer_permissions = maintainer_permissions + create_subgroup_permission @@ -122,8 +120,6 @@ describe GroupPolicy do context 'with subgroup_creation_level set to owner' do it 'allows every maintainer permission' do - allow(Group).to receive(:supports_nested_objects?).and_return(true) - expect_allowed(*guest_permissions) expect_allowed(*reporter_permissions) expect_allowed(*developer_permissions) @@ -137,8 +133,6 @@ describe GroupPolicy do let(:current_user) { owner } it do - allow(Group).to receive(:supports_nested_objects?).and_return(true) - expect_allowed(*guest_permissions) expect_allowed(*reporter_permissions) expect_allowed(*developer_permissions) @@ -151,8 +145,6 @@ describe GroupPolicy do let(:current_user) { admin } it do - allow(Group).to receive(:supports_nested_objects?).and_return(true) - expect_allowed(*guest_permissions) expect_allowed(*reporter_permissions) expect_allowed(*developer_permissions) @@ -161,52 +153,7 @@ describe GroupPolicy do end end - describe 'when nested group support feature is disabled' do - before do - allow(Group).to receive(:supports_nested_objects?).and_return(false) - end - - context 'admin' do - let(:current_user) { admin } - - it 'allows every owner permission except creating subgroups' do - create_subgroup_permission = [:create_subgroup] - updated_owner_permissions = - owner_permissions - create_subgroup_permission - - expect_disallowed(*create_subgroup_permission) - expect_allowed(*updated_owner_permissions) - end - end - - context 'owner' do - let(:current_user) { owner } - - it 'allows every owner permission except creating subgroups' do - create_subgroup_permission = [:create_subgroup] - updated_owner_permissions = - owner_permissions - create_subgroup_permission - - expect_disallowed(*create_subgroup_permission) - expect_allowed(*updated_owner_permissions) - end - end - - context 'maintainer' do - let(:current_user) { maintainer } - - it 'allows every maintainer permission except creating subgroups' do - create_subgroup_permission = [:create_subgroup] - updated_maintainer_permissions = - maintainer_permissions - create_subgroup_permission - - expect_disallowed(*create_subgroup_permission) - expect_allowed(*updated_maintainer_permissions) - end - end - end - - describe 'private nested group use the highest access level from the group and inherited permissions', :nested_groups do + describe 'private nested group use the highest access level from the group and inherited permissions' do let(:nested_group) do create(:group, :private, :owner_subgroup_creation_only, parent: group) end @@ -289,8 +236,6 @@ describe GroupPolicy do let(:current_user) { owner } it do - allow(Group).to receive(:supports_nested_objects?).and_return(true) - expect_allowed(*guest_permissions) expect_allowed(*reporter_permissions) expect_allowed(*developer_permissions) diff --git a/spec/presenters/clusters/cluster_presenter_spec.rb b/spec/presenters/clusters/cluster_presenter_spec.rb index 7054a70e2ed..6b988e2645b 100644 --- a/spec/presenters/clusters/cluster_presenter_spec.rb +++ b/spec/presenters/clusters/cluster_presenter_spec.rb @@ -43,7 +43,7 @@ describe Clusters::ClusterPresenter do end shared_examples 'ancestor clusters' do - context 'ancestor clusters', :nested_groups do + context 'ancestor clusters' do let(:root_group) { create(:group, name: 'Root Group') } let(:parent) { create(:group, name: 'parent', parent: root_group) } let(:child) { create(:group, name: 'child', parent: parent) } diff --git a/spec/requests/api/boards_spec.rb b/spec/requests/api/boards_spec.rb index de79e8c4c5c..0b9c0c2ebe9 100644 --- a/spec/requests/api/boards_spec.rb +++ b/spec/requests/api/boards_spec.rb @@ -63,7 +63,7 @@ describe API::Boards do end end - describe "POST /groups/:id/boards/lists", :nested_groups do + describe "POST /groups/:id/boards/lists" do set(:group) { create(:group) } set(:board_parent) { create(:group, parent: group ) } let(:url) { "/groups/#{board_parent.id}/boards/#{board.id}/lists" } diff --git a/spec/requests/api/graphql/namespace/projects_spec.rb b/spec/requests/api/graphql/namespace/projects_spec.rb index 63fa16c79ca..815e9531ecf 100644 --- a/spec/requests/api/graphql/namespace/projects_spec.rb +++ b/spec/requests/api/graphql/namespace/projects_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -describe 'getting projects', :nested_groups do +describe 'getting projects' do include GraphqlHelpers let(:group) { create(:group) } diff --git a/spec/requests/api/group_labels_spec.rb b/spec/requests/api/group_labels_spec.rb index 3769f8b78e4..fcea57d9df7 100644 --- a/spec/requests/api/group_labels_spec.rb +++ b/spec/requests/api/group_labels_spec.rb @@ -94,7 +94,7 @@ describe API::GroupLabels do expect(response).to have_gitlab_http_status(400) end - it "does not delete parent's group labels", :nested_groups do + it "does not delete parent's group labels" do subgroup = create(:group, parent: group) subgroup_label = create(:group_label, title: 'feature', group: subgroup) @@ -127,7 +127,7 @@ describe API::GroupLabels do expect(json_response['description']).to eq('test') end - it "does not update parent's group label", :nested_groups do + it "does not update parent's group label" do subgroup = create(:group, parent: group) subgroup_label = create(:group_label, title: 'feature', group: subgroup) diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 52d926d5484..50f36141aed 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -530,7 +530,7 @@ describe API::Groups do expect(json_response.length).to eq(2) end - it "returns projects including those in subgroups", :nested_groups do + it "returns projects including those in subgroups" do subgroup = create(:group, parent: group1) create(:project, group: subgroup) create(:project, group: subgroup) @@ -642,7 +642,7 @@ describe API::Groups do end end - describe 'GET /groups/:id/subgroups', :nested_groups do + describe 'GET /groups/:id/subgroups' do let!(:subgroup1) { create(:group, parent: group1) } let!(:subgroup2) { create(:group, :private, parent: group1) } let!(:subgroup3) { create(:group, :private, parent: group2) } @@ -786,7 +786,7 @@ describe API::Groups do expect(response).to have_gitlab_http_status(403) end - context 'as owner', :nested_groups do + context 'as owner' do before do group2.add_owner(user1) end @@ -798,7 +798,7 @@ describe API::Groups do end end - context 'as maintainer', :nested_groups do + context 'as maintainer' do before do group2.add_maintainer(user1) end @@ -825,7 +825,7 @@ describe API::Groups do expect(json_response["visibility"]).to eq(Gitlab::VisibilityLevel.string_level(Gitlab::CurrentSettings.current_application_settings.default_group_visibility)) end - it "creates a nested group", :nested_groups do + it "creates a nested group" do parent = create(:group) parent.add_owner(user3) group = attributes_for(:group, { parent_id: parent.id }) diff --git a/spec/requests/api/issues/get_group_issues_spec.rb b/spec/requests/api/issues/get_group_issues_spec.rb index 9a41d790945..5916bb11516 100644 --- a/spec/requests/api/issues/get_group_issues_spec.rb +++ b/spec/requests/api/issues/get_group_issues_spec.rb @@ -82,7 +82,7 @@ describe API::Issues do end end - context 'when group has subgroups', :nested_groups do + context 'when group has subgroups' do let(:subgroup_1) { create(:group, parent: group) } let(:subgroup_2) { create(:group, parent: subgroup_1) } diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb index 55f38079b1f..26f6e705528 100644 --- a/spec/requests/api/members_spec.rb +++ b/spec/requests/api/members_spec.rb @@ -99,7 +99,7 @@ describe API::Members do end end - describe 'GET /:source_type/:id/members/all', :nested_groups do + describe 'GET /:source_type/:id/members/all' do let(:nested_user) { create(:user) } let(:project_user) { create(:user) } let(:linked_group_user) { create(:user) } @@ -238,7 +238,7 @@ describe API::Members do end context 'access levels' do - it 'does not create the member if group level is higher', :nested_groups do + it 'does not create the member if group level is higher' do parent = create(:group) group.update(parent: parent) @@ -252,7 +252,7 @@ describe API::Members do expect(json_response['message']['access_level']).to eq(["should be greater than or equal to Developer inherited membership from group #{parent.name}"]) end - it 'creates the member if group level is lower', :nested_groups do + it 'creates the member if group level is lower' do parent = create(:group) group.update(parent: parent) diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index ced853caab4..7a6f1cd548c 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -723,7 +723,7 @@ describe API::MergeRequests do it_behaves_like 'merge requests list' - context 'when have subgroups', :nested_groups do + context 'when have subgroups' do let!(:group) { create(:group, :public) } let!(:subgroup) { create(:group, parent: group) } let!(:project) { create(:project, :public, :repository, creator: user, namespace: subgroup, only_allow_merge_if_pipeline_succeeds: false) } diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index fee300e9d7a..5b3a2412aff 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -1357,7 +1357,7 @@ describe API::Projects do end end - context 'nested group project', :nested_groups do + context 'nested group project' do let(:group) { create(:group) } let(:nested_group) { create(:group, parent: group) } let(:project2) { create(:project, group: nested_group) } diff --git a/spec/requests/openid_connect_spec.rb b/spec/requests/openid_connect_spec.rb index 86e41cbdf00..025568d8bea 100644 --- a/spec/requests/openid_connect_spec.rb +++ b/spec/requests/openid_connect_spec.rb @@ -104,7 +104,7 @@ describe 'OpenID Connect requests' do expect(json_response).to match(id_token_claims.merge(user_info_claims)) expected_groups = [group1.full_path, group3.full_path] - expected_groups << group4.full_path if Group.supports_nested_objects? + expected_groups << group4.full_path expect(json_response['groups']).to match_array(expected_groups) end diff --git a/spec/serializers/group_child_entity_spec.rb b/spec/serializers/group_child_entity_spec.rb index b58d95ccb43..00e2f931549 100644 --- a/spec/serializers/group_child_entity_spec.rb +++ b/spec/serializers/group_child_entity_spec.rb @@ -62,7 +62,7 @@ describe GroupChildEntity do it_behaves_like 'group child json' end - describe 'for a group', :nested_groups do + describe 'for a group' do let(:description) { 'Awesomeness' } let(:object) do create(:group, :nested, :with_avatar, diff --git a/spec/serializers/group_child_serializer_spec.rb b/spec/serializers/group_child_serializer_spec.rb index 5541ada3750..c9e8535585b 100644 --- a/spec/serializers/group_child_serializer_spec.rb +++ b/spec/serializers/group_child_serializer_spec.rb @@ -16,7 +16,7 @@ describe GroupChildSerializer do end end - context 'with a hierarchy', :nested_groups do + context 'with a hierarchy' do let(:parent) { create(:group) } subject(:serializer) do @@ -75,7 +75,7 @@ describe GroupChildSerializer do expect(serializer.represent(build_list(:project, 2))).to be_kind_of(Array) end - context 'with a hierarchy', :nested_groups do + context 'with a hierarchy' do let(:parent) { create(:group) } subject(:serializer) do diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb index 54e6abc2d3a..7f9827329b3 100644 --- a/spec/serializers/pipeline_serializer_spec.rb +++ b/spec/serializers/pipeline_serializer_spec.rb @@ -126,7 +126,7 @@ describe PipelineSerializer do expect(subject.all? { |entry| entry[:merge_request].present? }).to be_truthy end - it 'preloads related merge requests', :postgresql do + it 'preloads related merge requests' do recorded = ActiveRecord::QueryRecorder.new { subject } expect(recorded.log) diff --git a/spec/services/boards/issues/list_service_spec.rb b/spec/services/boards/issues/list_service_spec.rb index 40878e24cb4..931b67b2950 100644 --- a/spec/services/boards/issues/list_service_spec.rb +++ b/spec/services/boards/issues/list_service_spec.rb @@ -112,7 +112,7 @@ describe Boards::Issues::ListService do it_behaves_like 'issues list service' end - context 'and group is an ancestor', :nested_groups do + context 'and group is an ancestor' do let(:parent) { create(:group) } let(:group) { create(:group, parent: parent) } let!(:backlog) { create(:backlog_list, board: board) } diff --git a/spec/services/groups/auto_devops_service_spec.rb b/spec/services/groups/auto_devops_service_spec.rb index 7f8ab517cef..7591b2f6f12 100644 --- a/spec/services/groups/auto_devops_service_spec.rb +++ b/spec/services/groups/auto_devops_service_spec.rb @@ -47,7 +47,7 @@ describe Groups::AutoDevopsService, '#execute' do expect(subgroup_1.auto_devops_enabled?).to eq(false) end - context 'when subgroups have projects', :nested_groups do + context 'when subgroups have projects' do it 'reflects changes on projects' do subgroup_1 = create(:group, parent: group) project_1 = create(:project, namespace: subgroup_1) diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb index a7c95428485..0f9f20de586 100644 --- a/spec/services/groups/create_service_spec.rb +++ b/spec/services/groups/create_service_spec.rb @@ -44,7 +44,7 @@ describe Groups::CreateService, '#execute' do end end - describe 'creating subgroup', :nested_groups do + describe 'creating subgroup' do let!(:group) { create(:group) } let!(:service) { described_class.new(user, group_params.merge(parent_id: group.id)) } @@ -54,47 +54,31 @@ describe Groups::CreateService, '#execute' do end it { is_expected.to be_persisted } + end - context 'when nested groups feature is disabled' do - it 'does not save group and returns an error' do - allow(Group).to receive(:supports_nested_objects?).and_return(false) + context 'as guest' do + it 'does not save group and returns an error' do + is_expected.not_to be_persisted - is_expected.not_to be_persisted - expect(subject.errors[:parent_id]).to include('You don’t have permission to create a subgroup in this group.') - expect(subject.parent_id).to be_nil - end + expect(subject.errors[:parent_id].first).to eq('You don’t have permission to create a subgroup in this group.') + expect(subject.parent_id).to be_nil end end - context 'when nested groups feature is enabled' do + context 'as owner' do before do - allow(Group).to receive(:supports_nested_objects?).and_return(true) - end - - context 'as guest' do - it 'does not save group and returns an error' do - is_expected.not_to be_persisted - - expect(subject.errors[:parent_id].first).to eq('You don’t have permission to create a subgroup in this group.') - expect(subject.parent_id).to be_nil - end + group.add_owner(user) end - context 'as owner' do - before do - group.add_owner(user) - end + it { is_expected.to be_persisted } + end - it { is_expected.to be_persisted } + context 'as maintainer' do + before do + group.add_maintainer(user) end - context 'as maintainer' do - before do - group.add_maintainer(user) - end - - it { is_expected.to be_persisted } - end + it { is_expected.to be_persisted } end end diff --git a/spec/services/groups/nested_create_service_spec.rb b/spec/services/groups/nested_create_service_spec.rb index 13acf9e055b..b30392c1b12 100644 --- a/spec/services/groups/nested_create_service_spec.rb +++ b/spec/services/groups/nested_create_service_spec.rb @@ -28,35 +28,7 @@ describe Groups::NestedCreateService do end end - describe 'without subgroups' do - let(:params) { { group_path: 'a-group' } } - - before do - allow(Group).to receive(:supports_nested_objects?) { false } - end - - it 'creates the group' do - group = service.execute - - expect(group).to be_persisted - end - - it 'returns the group if it already existed' do - existing_group = create(:group, path: 'a-group') - - expect(service.execute).to eq(existing_group) - end - - it 'raises an error when tring to create a subgroup' do - service = described_class.new(user, group_path: 'a-group/a-sub-group') - - expect { service.execute }.to raise_error('Nested groups are not supported on MySQL') - end - - it_behaves_like 'with a visibility level' - end - - describe 'with subgroups', :nested_groups do + describe 'with subgroups' do let(:params) { { group_path: 'a-group/a-sub-group' } } describe "#execute" do diff --git a/spec/services/groups/transfer_service_spec.rb b/spec/services/groups/transfer_service_spec.rb index b5708ebba76..f3af8cf5f3b 100644 --- a/spec/services/groups/transfer_service_spec.rb +++ b/spec/services/groups/transfer_service_spec.rb @@ -2,28 +2,13 @@ require 'rails_helper' -describe Groups::TransferService, :postgresql do +describe Groups::TransferService do let(:user) { create(:user) } let(:new_parent_group) { create(:group, :public) } let!(:group_member) { create(:group_member, :owner, group: group, user: user) } let(:transfer_service) { described_class.new(group, user) } shared_examples 'ensuring allowed transfer for a group' do - context 'with other database than PostgreSQL' do - before do - allow(Group).to receive(:supports_nested_objects?).and_return(false) - end - - it 'returns false' do - expect(transfer_service.execute(new_parent_group)).to be_falsy - end - - it 'adds an error on group' do - transfer_service.execute(new_parent_group) - expect(transfer_service.error).to eq('Transfer failed: Database is not supported.') - end - end - context "when there's an exception on GitLab shell directories" do let(:new_parent_group) { create(:group, :public) } diff --git a/spec/services/groups/update_service_spec.rb b/spec/services/groups/update_service_spec.rb index d081c20f669..5d4576139f7 100644 --- a/spec/services/groups/update_service_spec.rb +++ b/spec/services/groups/update_service_spec.rb @@ -133,7 +133,7 @@ describe Groups::UpdateService do end end - context 'for a subgroup', :nested_groups do + context 'for a subgroup' do let(:subgroup) { create(:group, :private, parent: private_group) } context 'when the parent group share_with_group_lock is enabled' do diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index 68b79132096..3ae7c7b1c1d 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -116,7 +116,7 @@ describe Issues::UpdateService, :mailer do expect(issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position) end - context 'when moving issue between issues from different projects', :nested_groups do + context 'when moving issue between issues from different projects' do let(:group) { create(:group) } let(:subgroup) { create(:group, parent: group) } @@ -703,7 +703,7 @@ describe Issues::UpdateService, :mailer do end end - context 'when moving an issue ', :nested_groups do + context 'when moving an issue ' do it 'raises an error for invalid move ids within a project' do opts = { move_between_ids: [9000, 9999] } diff --git a/spec/services/members/destroy_service_spec.rb b/spec/services/members/destroy_service_spec.rb index 52f9a305d8f..7dce7f035d4 100644 --- a/spec/services/members/destroy_service_spec.rb +++ b/spec/services/members/destroy_service_spec.rb @@ -267,15 +267,15 @@ describe Members::DestroyService do expect(group.members.map(&:user)).not_to include(member_user) end - it 'removes the subgroup membership', :postgresql do + it 'removes the subgroup membership' do expect(subgroup.members.map(&:user)).not_to include(member_user) end - it 'removes the subsubgroup membership', :postgresql do + it 'removes the subsubgroup membership' do expect(subsubgroup.members.map(&:user)).not_to include(member_user) end - it 'removes the subsubproject membership', :postgresql do + it 'removes the subsubproject membership' do expect(subsubproject.members.map(&:user)).not_to include(member_user) end diff --git a/spec/services/merge_requests/push_options_handler_service_spec.rb b/spec/services/merge_requests/push_options_handler_service_spec.rb index ac40cf02c48..a27fea0c90f 100644 --- a/spec/services/merge_requests/push_options_handler_service_spec.rb +++ b/spec/services/merge_requests/push_options_handler_service_spec.rb @@ -11,6 +11,8 @@ describe MergeRequests::PushOptionsHandlerService do let(:service) { described_class.new(project, user, changes, push_options) } let(:source_branch) { 'fix' } let(:target_branch) { 'feature' } + let(:title) { 'my title' } + let(:description) { 'my description' } let(:new_branch_changes) { "#{Gitlab::Git::BLANK_SHA} 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/#{source_branch}" } let(:existing_branch_changes) { "d14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/#{source_branch}" } let(:deleted_branch_changes) { "d14d6c0abdd253381df51a723d58691b2ee1ab08 #{Gitlab::Git::BLANK_SHA} refs/heads/#{source_branch}" } @@ -73,6 +75,26 @@ describe MergeRequests::PushOptionsHandlerService do end end + shared_examples_for 'a service that can set the title of a merge request' do + subject(:last_mr) { MergeRequest.last } + + it 'sets the title' do + service.execute + + expect(last_mr.title).to eq(title) + end + end + + shared_examples_for 'a service that can set the description of a merge request' do + subject(:last_mr) { MergeRequest.last } + + it 'sets the description' do + service.execute + + expect(last_mr.description).to eq(description) + end + end + shared_examples_for 'a service that can set the merge request to merge when pipeline succeeds' do subject(:last_mr) { MergeRequest.last } @@ -350,6 +372,138 @@ describe MergeRequests::PushOptionsHandlerService do end end + describe '`title` push option' do + let(:push_options) { { title: title } } + + context 'with a new branch' do + let(:changes) { new_branch_changes } + + it_behaves_like 'a service that does not create a merge request' + + it 'adds an error to the service' do + error = "A merge_request.create push option is required to create a merge request for branch #{source_branch}" + + service.execute + + expect(service.errors).to include(error) + end + + context 'when coupled with the `create` push option' do + let(:push_options) { { create: true, title: title } } + + it_behaves_like 'a service that can create a merge request' + it_behaves_like 'a service that can set the title of a merge request' + end + end + + context 'with an existing branch but no open MR' do + let(:changes) { existing_branch_changes } + + it_behaves_like 'a service that does not create a merge request' + + it 'adds an error to the service' do + error = "A merge_request.create push option is required to create a merge request for branch #{source_branch}" + + service.execute + + expect(service.errors).to include(error) + end + + context 'when coupled with the `create` push option' do + let(:push_options) { { create: true, title: title } } + + it_behaves_like 'a service that can create a merge request' + it_behaves_like 'a service that can set the title of a merge request' + end + end + + context 'with an existing branch that has a merge request open' do + let(:changes) { existing_branch_changes } + let!(:merge_request) { create(:merge_request, source_project: project, source_branch: source_branch)} + + it_behaves_like 'a service that does not create a merge request' + it_behaves_like 'a service that can set the title of a merge request' + end + + context 'with a deleted branch' do + let(:changes) { deleted_branch_changes } + + it_behaves_like 'a service that does nothing' + end + + context 'with the project default branch' do + let(:changes) { default_branch_changes } + + it_behaves_like 'a service that does nothing' + end + end + + describe '`description` push option' do + let(:push_options) { { description: description } } + + context 'with a new branch' do + let(:changes) { new_branch_changes } + + it_behaves_like 'a service that does not create a merge request' + + it 'adds an error to the service' do + error = "A merge_request.create push option is required to create a merge request for branch #{source_branch}" + + service.execute + + expect(service.errors).to include(error) + end + + context 'when coupled with the `create` push option' do + let(:push_options) { { create: true, description: description } } + + it_behaves_like 'a service that can create a merge request' + it_behaves_like 'a service that can set the description of a merge request' + end + end + + context 'with an existing branch but no open MR' do + let(:changes) { existing_branch_changes } + + it_behaves_like 'a service that does not create a merge request' + + it 'adds an error to the service' do + error = "A merge_request.create push option is required to create a merge request for branch #{source_branch}" + + service.execute + + expect(service.errors).to include(error) + end + + context 'when coupled with the `create` push option' do + let(:push_options) { { create: true, description: description } } + + it_behaves_like 'a service that can create a merge request' + it_behaves_like 'a service that can set the description of a merge request' + end + end + + context 'with an existing branch that has a merge request open' do + let(:changes) { existing_branch_changes } + let!(:merge_request) { create(:merge_request, source_project: project, source_branch: source_branch)} + + it_behaves_like 'a service that does not create a merge request' + it_behaves_like 'a service that can set the description of a merge request' + end + + context 'with a deleted branch' do + let(:changes) { deleted_branch_changes } + + it_behaves_like 'a service that does nothing' + end + + context 'with the project default branch' do + let(:changes) { default_branch_changes } + + it_behaves_like 'a service that does nothing' + end + end + describe 'multiple pushed branches' do let(:push_options) { { create: true } } let(:changes) do diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index c20de1fd079..1dcade1de0d 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -111,7 +111,7 @@ describe NotificationService, :mailer do should_email(participant) end - context 'for subgroups', :nested_groups do + context 'for subgroups' do before do build_group(project) end @@ -337,7 +337,7 @@ describe NotificationService, :mailer do it_behaves_like 'new note notifications' - context 'which is a subgroup', :nested_groups do + context 'which is a subgroup' do let!(:parent) { create(:group) } let!(:group) { create(:group, parent: parent) } @@ -388,7 +388,7 @@ describe NotificationService, :mailer do should_email(admin) end - context 'on project that belongs to subgroup', :nested_groups do + context 'on project that belongs to subgroup' do let(:group_reporter) { create(:user) } let(:group_guest) { create(:user) } let(:parent_group) { create(:group) } @@ -458,7 +458,7 @@ describe NotificationService, :mailer do should_not_email_nested_group_user(@pg_disabled) end - it 'notifies parent group members with mention level', :nested_groups do + it 'notifies parent group members with mention level' do note = create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: "@#{@pg_mention.username}") notification.new_note(note) @@ -2410,52 +2410,40 @@ describe NotificationService, :mailer do group end - # Creates a nested group only if supported - # to avoid errors on MySQL def create_nested_group(visibility) - if Group.supports_nested_objects? - parent_group = create(:group, visibility) - child_group = create(:group, visibility, parent: parent_group) + parent_group = create(:group, visibility) + child_group = create(:group, visibility, parent: parent_group) - # Parent group member: global=disabled, parent_group=watch, child_group=global - @pg_watcher ||= create_user_with_notification(:watch, 'parent_group_watcher', parent_group) - @pg_watcher.notification_settings_for(nil).disabled! + # Parent group member: global=disabled, parent_group=watch, child_group=global + @pg_watcher ||= create_user_with_notification(:watch, 'parent_group_watcher', parent_group) + @pg_watcher.notification_settings_for(nil).disabled! - # Parent group member: global=global, parent_group=disabled, child_group=global - @pg_disabled ||= create_user_with_notification(:disabled, 'parent_group_disabled', parent_group) - @pg_disabled.notification_settings_for(nil).global! + # Parent group member: global=global, parent_group=disabled, child_group=global + @pg_disabled ||= create_user_with_notification(:disabled, 'parent_group_disabled', parent_group) + @pg_disabled.notification_settings_for(nil).global! - # Parent group member: global=global, parent_group=mention, child_group=global - @pg_mention ||= create_user_with_notification(:mention, 'parent_group_mention', parent_group) - @pg_mention.notification_settings_for(nil).global! + # Parent group member: global=global, parent_group=mention, child_group=global + @pg_mention ||= create_user_with_notification(:mention, 'parent_group_mention', parent_group) + @pg_mention.notification_settings_for(nil).global! - # Parent group member: global=global, parent_group=participating, child_group=global - @pg_participant ||= create_user_with_notification(:participating, 'parent_group_participant', parent_group) - @pg_mention.notification_settings_for(nil).global! + # Parent group member: global=global, parent_group=participating, child_group=global + @pg_participant ||= create_user_with_notification(:participating, 'parent_group_participant', parent_group) + @pg_mention.notification_settings_for(nil).global! - child_group - else - create(:group, visibility) - end + child_group end def add_member_for_parent_group(user, project) - return unless Group.supports_nested_objects? - project.reload project.group.parent.add_maintainer(user) end def should_email_nested_group_user(user, times: 1, recipients: email_recipients) - return unless Group.supports_nested_objects? - should_email(user, times: 1, recipients: email_recipients) end def should_not_email_nested_group_user(user, recipients: email_recipients) - return unless Group.supports_nested_objects? - should_not_email(user, recipients: email_recipients) end diff --git a/spec/services/projects/autocomplete_service_spec.rb b/spec/services/projects/autocomplete_service_spec.rb index 2f70c8ea94d..b625653bc77 100644 --- a/spec/services/projects/autocomplete_service_spec.rb +++ b/spec/services/projects/autocomplete_service_spec.rb @@ -118,7 +118,7 @@ describe Projects::AutocompleteService do expect(milestone_titles).to eq([group_milestone2.title, group_milestone1.title]) end - context 'with nested groups', :nested_groups do + context 'with nested groups' do let(:subgroup) { create(:group, :public, parent: group) } let!(:subgroup_milestone) { create(:milestone, group: subgroup) } diff --git a/spec/services/todos/destroy/entity_leave_service_spec.rb b/spec/services/todos/destroy/entity_leave_service_spec.rb index 2a553e18807..ce809bbf6c5 100644 --- a/spec/services/todos/destroy/entity_leave_service_spec.rb +++ b/spec/services/todos/destroy/entity_leave_service_spec.rb @@ -176,7 +176,7 @@ describe Todos::Destroy::EntityLeaveService do end end - context 'with nested groups', :nested_groups do + context 'with nested groups' do let(:subgroup) { create(:group, :private, parent: group) } let(:subgroup2) { create(:group, :private, parent: group) } let(:subproject) { create(:project, group: subgroup) } diff --git a/spec/services/todos/destroy/group_private_service_spec.rb b/spec/services/todos/destroy/group_private_service_spec.rb index a1798686d7c..7dd495847b3 100644 --- a/spec/services/todos/destroy/group_private_service_spec.rb +++ b/spec/services/todos/destroy/group_private_service_spec.rb @@ -35,7 +35,7 @@ describe Todos::Destroy::GroupPrivateService do expect(project_member.todos).to match_array([todo_project_member]) end - context 'with nested groups', :nested_groups do + context 'with nested groups' do let(:parent_group) { create(:group) } let(:subgroup) { create(:group, :private, parent: group) } let(:subproject) { create(:project, group: subgroup) } diff --git a/spec/services/users/refresh_authorized_projects_service_spec.rb b/spec/services/users/refresh_authorized_projects_service_spec.rb index 0287a24808d..f5a914bb482 100644 --- a/spec/services/users/refresh_authorized_projects_service_spec.rb +++ b/spec/services/users/refresh_authorized_projects_service_spec.rb @@ -135,7 +135,7 @@ describe Users::RefreshAuthorizedProjectsService do end end - context 'projects of subgroups of groups the user is a member of', :nested_groups do + context 'projects of subgroups of groups the user is a member of' do let(:group) { create(:group) } let(:nested_group) { create(:group, parent: group) } let!(:other_project) { create(:project, group: nested_group) } @@ -163,7 +163,7 @@ describe Users::RefreshAuthorizedProjectsService do end end - context 'projects shared with subgroups of groups the user is a member of', :nested_groups do + context 'projects shared with subgroups of groups the user is a member of' do let(:group) { create(:group) } let(:nested_group) { create(:group, parent: group) } let(:other_project) { create(:project) } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a44b5069ade..6ef5f46c81b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -258,14 +258,6 @@ RSpec.configure do |config| Gitlab::CurrentSettings.clear_in_memory_application_settings! end - config.around(:each, :nested_groups) do |example| - example.run if Group.supports_nested_objects? - end - - config.around(:each, :postgresql) do |example| - example.run if Gitlab::Database.postgresql? - end - # This makes sure the `ApplicationController#can?` method is stubbed with the # original implementation for all view specs. config.before(:each, type: :view) do diff --git a/spec/support/api/boards_shared_examples.rb b/spec/support/api/boards_shared_examples.rb index 3abb5096a7a..b7aff32460d 100644 --- a/spec/support/api/boards_shared_examples.rb +++ b/spec/support/api/boards_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples_for 'group and project boards' do |route_definition, ee = false| let(:root_url) { route_definition.gsub(":id", board_parent.id.to_s) } diff --git a/spec/support/api/issues_resolving_discussions_shared_examples.rb b/spec/support/api/issues_resolving_discussions_shared_examples.rb index d2d6260dfa8..4c44f1bd103 100644 --- a/spec/support/api/issues_resolving_discussions_shared_examples.rb +++ b/spec/support/api/issues_resolving_discussions_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'creating an issue resolving discussions through the API' do it 'creates a new project issue' do expect(response).to have_gitlab_http_status(:created) diff --git a/spec/support/api/members_shared_examples.rb b/spec/support/api/members_shared_examples.rb index 8d910e52eda..603efd4fc75 100644 --- a/spec/support/api/members_shared_examples.rb +++ b/spec/support/api/members_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'a 404 response when source is private' do before do source.update_column(:visibility_level, Gitlab::VisibilityLevel::PRIVATE) diff --git a/spec/support/api/milestones_shared_examples.rb b/spec/support/api/milestones_shared_examples.rb index 63b719be03e..d6439f77408 100644 --- a/spec/support/api/milestones_shared_examples.rb +++ b/spec/support/api/milestones_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples_for 'group and project milestones' do |route_definition| let(:resource_route) { "#{route}/#{milestone.id}" } let(:label_1) { create(:label, title: 'label_1', project: project, priority: 1) } diff --git a/spec/support/api/repositories_shared_context.rb b/spec/support/api/repositories_shared_context.rb index f1341804e56..346015106e3 100644 --- a/spec/support/api/repositories_shared_context.rb +++ b/spec/support/api/repositories_shared_context.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_context 'disabled repository' do before do project.project_feature.update!( diff --git a/spec/support/api/schema_matcher.rb b/spec/support/api/schema_matcher.rb index 4cf34d43117..ebbd57c8115 100644 --- a/spec/support/api/schema_matcher.rb +++ b/spec/support/api/schema_matcher.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module SchemaPath def self.expand(schema, dir = nil) if Gitlab.ee? && dir.nil? diff --git a/spec/support/api/scopes/read_user_shared_examples.rb b/spec/support/api/scopes/read_user_shared_examples.rb index 683234264a8..3786a8012f9 100644 --- a/spec/support/api/scopes/read_user_shared_examples.rb +++ b/spec/support/api/scopes/read_user_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples_for 'allows the "read_user" scope' do |api_version| let(:version) { api_version || 'v4' } diff --git a/spec/support/api/time_tracking_shared_examples.rb b/spec/support/api/time_tracking_shared_examples.rb index 15037222630..3bd1b145433 100644 --- a/spec/support/api/time_tracking_shared_examples.rb +++ b/spec/support/api/time_tracking_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'an unauthorized API user' do it { is_expected.to eq(403) } end diff --git a/spec/support/banzai/reference_filter_shared_examples.rb b/spec/support/banzai/reference_filter_shared_examples.rb index 476d80f3a93..27765652f28 100644 --- a/spec/support/banzai/reference_filter_shared_examples.rb +++ b/spec/support/banzai/reference_filter_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Specs for reference links containing HTML. # # Requires a reference: diff --git a/spec/support/batch_loader.rb b/spec/support/batch_loader.rb index bb790e660a6..0eb8f279d29 100644 --- a/spec/support/batch_loader.rb +++ b/spec/support/batch_loader.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.configure do |config| config.after do BatchLoader::Executor.clear_current diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb index 60990879fe2..8accc5c1df5 100644 --- a/spec/support/capybara.rb +++ b/spec/support/capybara.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # rubocop:disable Style/GlobalVars require 'capybara/rails' require 'capybara/rspec' diff --git a/spec/support/carrierwave.rb b/spec/support/carrierwave.rb index b376822d530..8da55514c78 100644 --- a/spec/support/carrierwave.rb +++ b/spec/support/carrierwave.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + CarrierWave.root = File.expand_path('tmp/tests/public', Rails.root) RSpec.configure do |config| diff --git a/spec/support/chunked_io/chunked_io_helpers.rb b/spec/support/chunked_io/chunked_io_helpers.rb index fec1f951563..278f577f3cb 100644 --- a/spec/support/chunked_io/chunked_io_helpers.rb +++ b/spec/support/chunked_io/chunked_io_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ChunkedIOHelpers def sample_trace_raw @sample_trace_raw ||= File.read(expand_fixture_path('trace/sample_trace')) diff --git a/spec/support/commit_trailers_spec_helper.rb b/spec/support/commit_trailers_spec_helper.rb index efa317fd2f9..a958e259368 100644 --- a/spec/support/commit_trailers_spec_helper.rb +++ b/spec/support/commit_trailers_spec_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module CommitTrailersSpecHelper extend ActiveSupport::Concern diff --git a/spec/support/controllers/githubish_import_controller_shared_context.rb b/spec/support/controllers/githubish_import_controller_shared_context.rb index e71994edec6..3706178ee34 100644 --- a/spec/support/controllers/githubish_import_controller_shared_context.rb +++ b/spec/support/controllers/githubish_import_controller_shared_context.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_context 'a GitHub-ish import controller' do let(:user) { create(:user) } let(:token) { "asdasd12345" } diff --git a/spec/support/controllers/githubish_import_controller_shared_examples.rb b/spec/support/controllers/githubish_import_controller_shared_examples.rb index 5bb1269a19d..718d9857b18 100644 --- a/spec/support/controllers/githubish_import_controller_shared_examples.rb +++ b/spec/support/controllers/githubish_import_controller_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Specifications for behavior common to all objects with an email attribute. # Takes a list of email-format attributes and requires: # - subject { "the object with a attribute= setter" } @@ -321,7 +323,7 @@ shared_examples 'a GitHub-ish import controller: POST create' do end end - context 'user has chosen an existing nested namespace and name for the project', :postgresql do + context 'user has chosen an existing nested namespace and name for the project' do let(:parent_namespace) { create(:group, name: 'foo') } let(:nested_namespace) { create(:group, name: 'bar', parent: parent_namespace) } let(:test_name) { 'test_name' } @@ -340,7 +342,7 @@ shared_examples 'a GitHub-ish import controller: POST create' do end end - context 'user has chosen a non-existent nested namespaces and name for the project', :postgresql do + context 'user has chosen a non-existent nested namespaces and name for the project' do let(:test_name) { 'test_name' } it 'takes the selected namespace and name' do @@ -371,7 +373,7 @@ shared_examples 'a GitHub-ish import controller: POST create' do end end - context 'user has chosen existent and non-existent nested namespaces and name for the project', :postgresql do + context 'user has chosen existent and non-existent nested namespaces and name for the project' do let(:test_name) { 'test_name' } let!(:parent_namespace) { create(:group, name: 'foo') } diff --git a/spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb b/spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb index a0c77eecb61..d636c1cf6cd 100644 --- a/spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb +++ b/spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' shared_context 'Ldap::OmniauthCallbacksController' do diff --git a/spec/support/controllers/sessionless_auth_controller_shared_examples.rb b/spec/support/controllers/sessionless_auth_controller_shared_examples.rb index 355555d9d19..b5149a0fcb1 100644 --- a/spec/support/controllers/sessionless_auth_controller_shared_examples.rb +++ b/spec/support/controllers/sessionless_auth_controller_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'authenticates sessionless user' do |path, format, params| params ||= {} diff --git a/spec/support/cycle_analytics_helpers/test_generation.rb b/spec/support/cycle_analytics_helpers/test_generation.rb index be1c2bc3046..c57abbd96c6 100644 --- a/spec/support/cycle_analytics_helpers/test_generation.rb +++ b/spec/support/cycle_analytics_helpers/test_generation.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # rubocop:disable Metrics/AbcSize # Note: The ABC size is large here because we have a method generating test cases with diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb index 08622dff6d9..041ffa25535 100644 --- a/spec/support/db_cleaner.rb +++ b/spec/support/db_cleaner.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module DbCleaner def delete_from_all_tables!(except: nil) DatabaseCleaner.clean_with(:deletion, cache_tables: false, except: except) diff --git a/spec/support/external_authorization_service_helpers.rb b/spec/support/external_authorization_service_helpers.rb index 79dd9a3d58e..f4214d800cf 100644 --- a/spec/support/external_authorization_service_helpers.rb +++ b/spec/support/external_authorization_service_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ExternalAuthorizationServiceHelpers def enable_external_authorization_service_check stub_application_setting(external_authorization_service_enabled: true) diff --git a/spec/support/features/discussion_comments_shared_example.rb b/spec/support/features/discussion_comments_shared_example.rb index 7c8e57702ae..5590bf0fb7e 100644 --- a/spec/support/features/discussion_comments_shared_example.rb +++ b/spec/support/features/discussion_comments_shared_example.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'thread comments' do |resource_name| let(:form_selector) { '.js-main-target-form' } let(:dropdown_selector) { "#{form_selector} .comment-type-dropdown" } diff --git a/spec/support/features/reportable_note_shared_examples.rb b/spec/support/features/reportable_note_shared_examples.rb index 5d5a0a7b5d2..2f9208e6ed5 100644 --- a/spec/support/features/reportable_note_shared_examples.rb +++ b/spec/support/features/reportable_note_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' shared_examples 'reportable note' do |type| diff --git a/spec/support/features/resolving_discussions_in_issues_shared_examples.rb b/spec/support/features/resolving_discussions_in_issues_shared_examples.rb index 8d0e03134d0..d4f8a87d0d8 100644 --- a/spec/support/features/resolving_discussions_in_issues_shared_examples.rb +++ b/spec/support/features/resolving_discussions_in_issues_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'creating an issue for a thread' do it 'shows an issue with the title filled in' do title_field = page.find_field('issue[title]') diff --git a/spec/support/features/rss_shared_examples.rb b/spec/support/features/rss_shared_examples.rb index 0de92aedba5..c97eeba87db 100644 --- a/spec/support/features/rss_shared_examples.rb +++ b/spec/support/features/rss_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples "an autodiscoverable RSS feed with current_user's feed token" do it "has an RSS autodiscovery link tag with current_user's feed token" do expect(page).to have_css("link[type*='atom+xml'][href*='feed_token=#{user.feed_token}']", visible: false) diff --git a/spec/support/features/variable_list_shared_examples.rb b/spec/support/features/variable_list_shared_examples.rb index 01531864c1f..0f8ad2c6536 100644 --- a/spec/support/features/variable_list_shared_examples.rb +++ b/spec/support/features/variable_list_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'variable list' do it 'shows list of variables' do page.within('.js-ci-variable-list-section') do diff --git a/spec/support/forgery_protection.rb b/spec/support/forgery_protection.rb index fa87d5fa881..1d6ea013292 100644 --- a/spec/support/forgery_protection.rb +++ b/spec/support/forgery_protection.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ForgeryProtection def with_forgery_protection ActionController::Base.allow_forgery_protection = true diff --git a/spec/support/google_api/cloud_platform_helpers.rb b/spec/support/google_api/cloud_platform_helpers.rb index 2fdbddd40c2..a1328ef0d13 100644 --- a/spec/support/google_api/cloud_platform_helpers.rb +++ b/spec/support/google_api/cloud_platform_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module GoogleApi module CloudPlatformHelpers def stub_google_api_validate_token diff --git a/spec/support/helpers/api_helpers.rb b/spec/support/helpers/api_helpers.rb index 4a9ce9beb78..aff0f87b6e4 100644 --- a/spec/support/helpers/api_helpers.rb +++ b/spec/support/helpers/api_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ApiHelpers # Public: Prepend a request path with the path to the API # @@ -30,11 +32,12 @@ module ApiHelpers end if query_string - full_path << (path.index('?') ? '&' : '?') - full_path << query_string - end + separator = path.index('?') ? '&' : '?' - full_path + full_path + separator + query_string + else + full_path + end end def expect_paginated_array_response(items) diff --git a/spec/support/helpers/assets_helpers.rb b/spec/support/helpers/assets_helpers.rb index 09bbf451671..fa24ad9ad2a 100644 --- a/spec/support/helpers/assets_helpers.rb +++ b/spec/support/helpers/assets_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module AssetsHelpers # In a CI environment the assets are not compiled, as there is a CI job # `compile-assets` that compiles them in the prepare stage for all following diff --git a/spec/support/helpers/bare_repo_operations.rb b/spec/support/helpers/bare_repo_operations.rb index 3f4a4243cb6..099610f087d 100644 --- a/spec/support/helpers/bare_repo_operations.rb +++ b/spec/support/helpers/bare_repo_operations.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'zlib' class BareRepoOperations diff --git a/spec/support/helpers/board_helpers.rb b/spec/support/helpers/board_helpers.rb index b85fde222ea..683ee3e4bf2 100644 --- a/spec/support/helpers/board_helpers.rb +++ b/spec/support/helpers/board_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module BoardHelpers def click_card(card) within card do diff --git a/spec/support/helpers/capybara_helpers.rb b/spec/support/helpers/capybara_helpers.rb index bcc2df44708..5abbc1e2951 100644 --- a/spec/support/helpers/capybara_helpers.rb +++ b/spec/support/helpers/capybara_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module CapybaraHelpers # Execute a block a certain number of times before considering it a failure # diff --git a/spec/support/helpers/ci_artifact_metadata_generator.rb b/spec/support/helpers/ci_artifact_metadata_generator.rb index ef638d59d2d..e02501565a9 100644 --- a/spec/support/helpers/ci_artifact_metadata_generator.rb +++ b/spec/support/helpers/ci_artifact_metadata_generator.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # frozen_sting_literal: true # This generates fake CI metadata .gz for testing diff --git a/spec/support/helpers/cookie_helper.rb b/spec/support/helpers/cookie_helper.rb index 5ff7b0b68c9..ea4be12355b 100644 --- a/spec/support/helpers/cookie_helper.rb +++ b/spec/support/helpers/cookie_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Helper for setting cookies in Selenium/WebDriver # module CookieHelper diff --git a/spec/support/helpers/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb index 100e439ef44..575b2e779c5 100644 --- a/spec/support/helpers/cycle_analytics_helpers.rb +++ b/spec/support/helpers/cycle_analytics_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module CycleAnalyticsHelpers include GitHelpers diff --git a/spec/support/helpers/database_connection_helpers.rb b/spec/support/helpers/database_connection_helpers.rb index 763329499f0..10ea7b5de91 100644 --- a/spec/support/helpers/database_connection_helpers.rb +++ b/spec/support/helpers/database_connection_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module DatabaseConnectionHelpers def run_with_new_database_connection pool = ActiveRecord::Base.connection_pool diff --git a/spec/support/helpers/devise_helpers.rb b/spec/support/helpers/devise_helpers.rb index fb2a110422a..70fc6a48414 100644 --- a/spec/support/helpers/devise_helpers.rb +++ b/spec/support/helpers/devise_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module DeviseHelpers # explicitly tells Devise which mapping to use # this is needed when we are testing a Devise controller bypassing the router diff --git a/spec/support/helpers/drag_to_helper.rb b/spec/support/helpers/drag_to_helper.rb index 6d53ad0b602..6099f87323f 100644 --- a/spec/support/helpers/drag_to_helper.rb +++ b/spec/support/helpers/drag_to_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module DragTo def drag_to(list_from_index: 0, from_index: 0, to_index: 0, list_to_index: 0, selector: '', scrollable: 'body', duration: 1000) evaluate_script("simulateDrag({scrollable: $('#{scrollable}').get(0), duration: #{duration}, from: {el: $('#{selector}').eq(#{list_from_index}).get(0), index: #{from_index}}, to: {el: $('#{selector}').eq(#{list_to_index}).get(0), index: #{to_index}}});") diff --git a/spec/support/helpers/dropzone_helper.rb b/spec/support/helpers/dropzone_helper.rb index fe72d320fcf..a0f261b312e 100644 --- a/spec/support/helpers/dropzone_helper.rb +++ b/spec/support/helpers/dropzone_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module DropzoneHelper # Provides a way to perform `attach_file` for a Dropzone-based file input # diff --git a/spec/support/helpers/email_helpers.rb b/spec/support/helpers/email_helpers.rb index ed049daba80..83ba654fab3 100644 --- a/spec/support/helpers/email_helpers.rb +++ b/spec/support/helpers/email_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module EmailHelpers def sent_to_user(user, recipients: email_recipients) recipients.count { |to| to == user.notification_email } diff --git a/spec/support/helpers/exclusive_lease_helpers.rb b/spec/support/helpers/exclusive_lease_helpers.rb index 383cc7dee81..77703e20602 100644 --- a/spec/support/helpers/exclusive_lease_helpers.rb +++ b/spec/support/helpers/exclusive_lease_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ExclusiveLeaseHelpers def stub_exclusive_lease(key = nil, uuid = 'uuid', renew: false, timeout: nil) key ||= instance_of(String) diff --git a/spec/support/helpers/expect_next_instance_of.rb b/spec/support/helpers/expect_next_instance_of.rb index b95046b2b42..749d2cb2a56 100644 --- a/spec/support/helpers/expect_next_instance_of.rb +++ b/spec/support/helpers/expect_next_instance_of.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ExpectNextInstanceOf def expect_next_instance_of(klass, *new_args) receive_new = receive(:new) diff --git a/spec/support/helpers/expect_offense.rb b/spec/support/helpers/expect_offense.rb index 35718ba90c5..76301fe19ff 100644 --- a/spec/support/helpers/expect_offense.rb +++ b/spec/support/helpers/expect_offense.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'rubocop/rspec/support' # https://github.com/backus/rubocop-rspec/blob/master/spec/support/expect_offense.rb diff --git a/spec/support/helpers/fake_blob_helpers.rb b/spec/support/helpers/fake_blob_helpers.rb index 801ca8b7412..ef4740638ff 100644 --- a/spec/support/helpers/fake_blob_helpers.rb +++ b/spec/support/helpers/fake_blob_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module FakeBlobHelpers class FakeBlob include BlobLike diff --git a/spec/support/helpers/fake_migration_classes.rb b/spec/support/helpers/fake_migration_classes.rb index c7766df7a52..6c066b3b199 100644 --- a/spec/support/helpers/fake_migration_classes.rb +++ b/spec/support/helpers/fake_migration_classes.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class FakeRenameReservedPathMigrationV1 < ActiveRecord::Migration[4.2] include Gitlab::Database::RenameReservedPathsMigration::V1 diff --git a/spec/support/helpers/fake_u2f_device.rb b/spec/support/helpers/fake_u2f_device.rb index 22cd8152d77..f765b277175 100644 --- a/spec/support/helpers/fake_u2f_device.rb +++ b/spec/support/helpers/fake_u2f_device.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class FakeU2fDevice attr_reader :name diff --git a/spec/support/helpers/features/branches_helpers.rb b/spec/support/helpers/features/branches_helpers.rb index df88fd425c9..2a50b41cb4e 100644 --- a/spec/support/helpers/features/branches_helpers.rb +++ b/spec/support/helpers/features/branches_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # These helpers allow you to manipulate with sorting features. # # Usage: diff --git a/spec/support/helpers/features/notes_helpers.rb b/spec/support/helpers/features/notes_helpers.rb index 8a139fafac2..a2d8d71b541 100644 --- a/spec/support/helpers/features/notes_helpers.rb +++ b/spec/support/helpers/features/notes_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # These helpers allow you to manipulate with notes. # # Usage: diff --git a/spec/support/helpers/features/sorting_helpers.rb b/spec/support/helpers/features/sorting_helpers.rb index 003ecb251fe..a6428bf8573 100644 --- a/spec/support/helpers/features/sorting_helpers.rb +++ b/spec/support/helpers/features/sorting_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # These helpers allow you to manipulate with sorting features. # # Usage: diff --git a/spec/support/helpers/filter_spec_helper.rb b/spec/support/helpers/filter_spec_helper.rb index 721d359c2ee..95c24d76dcd 100644 --- a/spec/support/helpers/filter_spec_helper.rb +++ b/spec/support/helpers/filter_spec_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Helper methods for Banzai filter specs # # Must be included into specs manually diff --git a/spec/support/helpers/filtered_search_helpers.rb b/spec/support/helpers/filtered_search_helpers.rb index 34ef185ea27..39c818b1763 100644 --- a/spec/support/helpers/filtered_search_helpers.rb +++ b/spec/support/helpers/filtered_search_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module FilteredSearchHelpers def filtered_search page.find('.filtered-search') diff --git a/spec/support/helpers/fixture_helpers.rb b/spec/support/helpers/fixture_helpers.rb index 611d19f36a0..7b3b8ae5f7a 100644 --- a/spec/support/helpers/fixture_helpers.rb +++ b/spec/support/helpers/fixture_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module FixtureHelpers def fixture_file(filename, dir: '') return '' if filename.blank? diff --git a/spec/support/helpers/git_http_helpers.rb b/spec/support/helpers/git_http_helpers.rb index c83860d7b51..de8bb9ac8e3 100644 --- a/spec/support/helpers/git_http_helpers.rb +++ b/spec/support/helpers/git_http_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative 'workhorse_helpers' module GitHttpHelpers diff --git a/spec/support/helpers/gitlab_verify_helpers.rb b/spec/support/helpers/gitlab_verify_helpers.rb index 5df4bf24ec2..9901ce374ed 100644 --- a/spec/support/helpers/gitlab_verify_helpers.rb +++ b/spec/support/helpers/gitlab_verify_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module GitlabVerifyHelpers def collect_ranges(args = {}) verifier = described_class.new(args.merge(batch_size: 1)) diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb index ec3c460cd37..ae1b859ae3f 100644 --- a/spec/support/helpers/graphql_helpers.rb +++ b/spec/support/helpers/graphql_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module GraphqlHelpers MutationDefinition = Struct.new(:query, :variables) diff --git a/spec/support/helpers/import_spec_helper.rb b/spec/support/helpers/import_spec_helper.rb index d4eced724fa..d8fb2ba08af 100644 --- a/spec/support/helpers/import_spec_helper.rb +++ b/spec/support/helpers/import_spec_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'ostruct' # Helper methods for controller specs in the Import namespace diff --git a/spec/support/helpers/input_helper.rb b/spec/support/helpers/input_helper.rb index acbb42274ec..5136f8e9cb8 100644 --- a/spec/support/helpers/input_helper.rb +++ b/spec/support/helpers/input_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # see app/assets/javascripts/test_utils/simulate_input.js module InputHelper diff --git a/spec/support/helpers/inspect_requests.rb b/spec/support/helpers/inspect_requests.rb index 88ddc5c7f6c..4a1d9cb8539 100644 --- a/spec/support/helpers/inspect_requests.rb +++ b/spec/support/helpers/inspect_requests.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative './wait_for_requests' module InspectRequests diff --git a/spec/support/helpers/issue_helpers.rb b/spec/support/helpers/issue_helpers.rb index ffd72515f37..82b954a92e2 100644 --- a/spec/support/helpers/issue_helpers.rb +++ b/spec/support/helpers/issue_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module IssueHelpers def visit_issues(project, opts = {}) visit project_issues_path project, opts diff --git a/spec/support/helpers/javascript_fixtures_helpers.rb b/spec/support/helpers/javascript_fixtures_helpers.rb index e9129bd263e..7ec65318ec5 100644 --- a/spec/support/helpers/javascript_fixtures_helpers.rb +++ b/spec/support/helpers/javascript_fixtures_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'action_dispatch/testing/test_request' require 'fileutils' diff --git a/spec/support/helpers/jira_service_helper.rb b/spec/support/helpers/jira_service_helper.rb index 7e955f3d593..57c33c81ea3 100644 --- a/spec/support/helpers/jira_service_helper.rb +++ b/spec/support/helpers/jira_service_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module JiraServiceHelper JIRA_URL = "http://jira.example.net".freeze JIRA_API = JIRA_URL + "/rest/api/2" diff --git a/spec/support/helpers/key_generator_helper.rb b/spec/support/helpers/key_generator_helper.rb index 768b8c127b9..59c8eeb3692 100644 --- a/spec/support/helpers/key_generator_helper.rb +++ b/spec/support/helpers/key_generator_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Spec module Support module Helpers diff --git a/spec/support/helpers/kubernetes_helpers.rb b/spec/support/helpers/kubernetes_helpers.rb index 278264f3df5..538a5b8ef3c 100644 --- a/spec/support/helpers/kubernetes_helpers.rb +++ b/spec/support/helpers/kubernetes_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module KubernetesHelpers include Gitlab::Kubernetes diff --git a/spec/support/helpers/ldap_helpers.rb b/spec/support/helpers/ldap_helpers.rb index 66ca5d7f0a3..dce8a3803f5 100644 --- a/spec/support/helpers/ldap_helpers.rb +++ b/spec/support/helpers/ldap_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module LdapHelpers def ldap_adapter(provider = 'ldapmain', ldap = double(:ldap)) ::Gitlab::Auth::LDAP::Adapter.new(provider, ldap) diff --git a/spec/support/helpers/live_debugger.rb b/spec/support/helpers/live_debugger.rb index 911eb48a8ca..d6091035b59 100644 --- a/spec/support/helpers/live_debugger.rb +++ b/spec/support/helpers/live_debugger.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'io/console' module LiveDebugger diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb index 0cb99b4e087..2b508ee6f2c 100644 --- a/spec/support/helpers/login_helpers.rb +++ b/spec/support/helpers/login_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative 'devise_helpers' module LoginHelpers diff --git a/spec/support/helpers/markdown_feature.rb b/spec/support/helpers/markdown_feature.rb index 96401379cf0..eea03fb9325 100644 --- a/spec/support/helpers/markdown_feature.rb +++ b/spec/support/helpers/markdown_feature.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This is a helper class used by the GitLab Markdown feature spec # # Because the feature spec only cares about the output of the Markdown, and the diff --git a/spec/support/helpers/merge_request_diff_helpers.rb b/spec/support/helpers/merge_request_diff_helpers.rb index 3b49d0b3319..49beecc6d4b 100644 --- a/spec/support/helpers/merge_request_diff_helpers.rb +++ b/spec/support/helpers/merge_request_diff_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module MergeRequestDiffHelpers def click_diff_line(line_holder, diff_side = nil) line = get_line_components(line_holder, diff_side) diff --git a/spec/support/helpers/merge_request_helpers.rb b/spec/support/helpers/merge_request_helpers.rb index 772adff4626..3c359bc9353 100644 --- a/spec/support/helpers/merge_request_helpers.rb +++ b/spec/support/helpers/merge_request_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module MergeRequestHelpers def visit_merge_requests(project, opts = {}) visit project_merge_requests_path project, opts diff --git a/spec/support/helpers/migrations_helpers.rb b/spec/support/helpers/migrations_helpers.rb index 272b24f7541..2727ab7fb1e 100644 --- a/spec/support/helpers/migrations_helpers.rb +++ b/spec/support/helpers/migrations_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module MigrationsHelpers def active_record_base ActiveRecord::Base diff --git a/spec/support/helpers/mobile_helpers.rb b/spec/support/helpers/mobile_helpers.rb index 4230d315d9b..94dbd2fb1b7 100644 --- a/spec/support/helpers/mobile_helpers.rb +++ b/spec/support/helpers/mobile_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module MobileHelpers def resize_screen_xs resize_window(575, 768) diff --git a/spec/support/helpers/note_interaction_helpers.rb b/spec/support/helpers/note_interaction_helpers.rb index 79a0aa174b1..a4322618cd3 100644 --- a/spec/support/helpers/note_interaction_helpers.rb +++ b/spec/support/helpers/note_interaction_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module NoteInteractionHelpers def open_more_actions_dropdown(note) note_element = find("#note_#{note.id}") diff --git a/spec/support/helpers/notification_helpers.rb b/spec/support/helpers/notification_helpers.rb index 44c2051598c..16ecb338f6e 100644 --- a/spec/support/helpers/notification_helpers.rb +++ b/spec/support/helpers/notification_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module NotificationHelpers extend self diff --git a/spec/support/helpers/project_forks_helper.rb b/spec/support/helpers/project_forks_helper.rb index bcb11a09b36..b2d22853e4c 100644 --- a/spec/support/helpers/project_forks_helper.rb +++ b/spec/support/helpers/project_forks_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ProjectForksHelper def fork_project(project, user = nil, params = {}) Gitlab::GitalyClient.allow_n_plus_1_calls do diff --git a/spec/support/helpers/prometheus_helpers.rb b/spec/support/helpers/prometheus_helpers.rb index db662836013..7c03746a395 100644 --- a/spec/support/helpers/prometheus_helpers.rb +++ b/spec/support/helpers/prometheus_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module PrometheusHelpers def prometheus_memory_query(environment_slug) %{avg(container_memory_usage_bytes{container_name!="POD",environment="#{environment_slug}"}) / 2^20} @@ -74,6 +76,14 @@ module PrometheusHelpers WebMock.stub_request(:any, /prometheus.example.com/) end + def stub_any_prometheus_request_with_response(status: 200, headers: {}, body: nil) + stub_any_prometheus_request.to_return({ + status: status, + headers: { 'Content-Type' => 'application/json' }.merge(headers), + body: body || prometheus_values_body.to_json + }) + end + def stub_all_prometheus_requests(environment_slug, body: nil, status: 200) stub_prometheus_request( prometheus_query_with_time_url(prometheus_memory_query(environment_slug), Time.now.utc), diff --git a/spec/support/helpers/query_recorder.rb b/spec/support/helpers/query_recorder.rb index f77b43391dd..d936dc6de41 100644 --- a/spec/support/helpers/query_recorder.rb +++ b/spec/support/helpers/query_recorder.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord class QueryRecorder attr_reader :log, :skip_cached, :cached diff --git a/spec/support/helpers/quick_actions_helpers.rb b/spec/support/helpers/quick_actions_helpers.rb index 361190aa352..cb853f5363f 100644 --- a/spec/support/helpers/quick_actions_helpers.rb +++ b/spec/support/helpers/quick_actions_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module QuickActionsHelpers def write_note(text) Sidekiq::Testing.fake! do diff --git a/spec/support/helpers/rake_helpers.rb b/spec/support/helpers/rake_helpers.rb index 7d8d7750bf3..d8f354a69da 100644 --- a/spec/support/helpers/rake_helpers.rb +++ b/spec/support/helpers/rake_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module RakeHelpers def run_rake_task(task_name, *args) Rake::Task[task_name].reenable diff --git a/spec/support/helpers/reactive_caching_helpers.rb b/spec/support/helpers/reactive_caching_helpers.rb index 528da37e8cf..aa9d3b3a199 100644 --- a/spec/support/helpers/reactive_caching_helpers.rb +++ b/spec/support/helpers/reactive_caching_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ReactiveCachingHelpers def reactive_cache_key(subject, *qualifiers) ([subject.class.reactive_cache_key.call(subject)].flatten + qualifiers).join(':') diff --git a/spec/support/helpers/redis_without_keys.rb b/spec/support/helpers/redis_without_keys.rb index 6220167dee6..e030f1028f7 100644 --- a/spec/support/helpers/redis_without_keys.rb +++ b/spec/support/helpers/redis_without_keys.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Redis ForbiddenCommand = Class.new(StandardError) diff --git a/spec/support/helpers/reference_parser_helpers.rb b/spec/support/helpers/reference_parser_helpers.rb index 9f27502aa52..f96a01d15b5 100644 --- a/spec/support/helpers/reference_parser_helpers.rb +++ b/spec/support/helpers/reference_parser_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ReferenceParserHelpers def empty_html_link Nokogiri::HTML.fragment('<a></a>').children[0] diff --git a/spec/support/helpers/repo_helpers.rb b/spec/support/helpers/repo_helpers.rb index 44d95a029af..ca4d2acbf2c 100644 --- a/spec/support/helpers/repo_helpers.rb +++ b/spec/support/helpers/repo_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module RepoHelpers extend self diff --git a/spec/support/helpers/routes_helpers.rb b/spec/support/helpers/routes_helpers.rb index c4129606418..2a7cd81cbe3 100644 --- a/spec/support/helpers/routes_helpers.rb +++ b/spec/support/helpers/routes_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module RoutesHelpers def fake_routes(&block) @routes = @routes.dup diff --git a/spec/support/helpers/search_helpers.rb b/spec/support/helpers/search_helpers.rb index abbbb636d66..815337f8615 100644 --- a/spec/support/helpers/search_helpers.rb +++ b/spec/support/helpers/search_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module SearchHelpers def select_filter(name) find(:xpath, "//ul[contains(@class, 'search-filter')]//a[contains(.,'#{name}')]").click diff --git a/spec/support/helpers/seed_repo.rb b/spec/support/helpers/seed_repo.rb index 71f1a86b0c1..20738b45129 100644 --- a/spec/support/helpers/seed_repo.rb +++ b/spec/support/helpers/seed_repo.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This file is generated by generate-seed-repo-rb. Do not edit this file manually. # # Seed repo: diff --git a/spec/support/helpers/select2_helper.rb b/spec/support/helpers/select2_helper.rb index 87672c8896d..9c42c2b0d8b 100644 --- a/spec/support/helpers/select2_helper.rb +++ b/spec/support/helpers/select2_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative 'wait_for_requests' # Select2 ajax programmatic helper diff --git a/spec/support/helpers/selection_helper.rb b/spec/support/helpers/selection_helper.rb index b4725b137b2..a5f9ca76f6e 100644 --- a/spec/support/helpers/selection_helper.rb +++ b/spec/support/helpers/selection_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module SelectionHelper def select_element(selector) find(selector) diff --git a/spec/support/helpers/sorting_helper.rb b/spec/support/helpers/sorting_helper.rb index e505a6b7258..3801d25fb63 100644 --- a/spec/support/helpers/sorting_helper.rb +++ b/spec/support/helpers/sorting_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Helper allows you to sort items # # Params diff --git a/spec/support/helpers/stub_configuration.rb b/spec/support/helpers/stub_configuration.rb index 049702be1f6..c8b2bf040e6 100644 --- a/spec/support/helpers/stub_configuration.rb +++ b/spec/support/helpers/stub_configuration.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'active_support/core_ext/hash/transform_values' require 'active_support/hash_with_indifferent_access' require 'active_support/dependencies' diff --git a/spec/support/helpers/stub_env.rb b/spec/support/helpers/stub_env.rb index 1c2f474a015..8107ffc939f 100644 --- a/spec/support/helpers/stub_env.rb +++ b/spec/support/helpers/stub_env.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Inspired by https://github.com/ljkbennett/stub_env/blob/master/lib/stub_env/helpers.rb module StubENV def stub_env(key_or_hash, value = nil) diff --git a/spec/support/helpers/stub_feature_flags.rb b/spec/support/helpers/stub_feature_flags.rb index 48258692304..6c3efff7262 100644 --- a/spec/support/helpers/stub_feature_flags.rb +++ b/spec/support/helpers/stub_feature_flags.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module StubFeatureFlags # Stub Feature flags with `flag_name: true/false` # diff --git a/spec/support/helpers/stub_gitlab_calls.rb b/spec/support/helpers/stub_gitlab_calls.rb index 4cb3b18df85..badea94352a 100644 --- a/spec/support/helpers/stub_gitlab_calls.rb +++ b/spec/support/helpers/stub_gitlab_calls.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module StubGitlabCalls def stub_gitlab_calls stub_user diff --git a/spec/support/helpers/stub_gitlab_data.rb b/spec/support/helpers/stub_gitlab_data.rb index fa402f35b95..ed518393c03 100644 --- a/spec/support/helpers/stub_gitlab_data.rb +++ b/spec/support/helpers/stub_gitlab_data.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module StubGitlabData def gitlab_ci_yaml File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) diff --git a/spec/support/helpers/stub_metrics.rb b/spec/support/helpers/stub_metrics.rb index 64983fdf222..e347955efbb 100644 --- a/spec/support/helpers/stub_metrics.rb +++ b/spec/support/helpers/stub_metrics.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module StubMetrics def authentication_metrics Gitlab::Auth::Activity diff --git a/spec/support/helpers/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb index d31f9908714..e5b8bb712bb 100644 --- a/spec/support/helpers/stub_object_storage.rb +++ b/spec/support/helpers/stub_object_storage.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module StubObjectStorage def stub_object_storage_uploader( config:, diff --git a/spec/support/helpers/stub_requests.rb b/spec/support/helpers/stub_requests.rb index 5cad35282c0..6eb8007ed26 100644 --- a/spec/support/helpers/stub_requests.rb +++ b/spec/support/helpers/stub_requests.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module StubRequests IP_ADDRESS_STUB = '8.8.8.9'.freeze diff --git a/spec/support/helpers/stub_worker.rb b/spec/support/helpers/stub_worker.rb index 58b7ee93dff..cac839ed5fe 100644 --- a/spec/support/helpers/stub_worker.rb +++ b/spec/support/helpers/stub_worker.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Inspired by https://github.com/ljkbennett/stub_env/blob/master/lib/stub_env/helpers.rb module StubWorker def stub_worker(queue:) diff --git a/spec/support/helpers/terms_helper.rb b/spec/support/helpers/terms_helper.rb index a00ec14138b..a61bae18f9a 100644 --- a/spec/support/helpers/terms_helper.rb +++ b/spec/support/helpers/terms_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module TermsHelper def enforce_terms stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb index b062631b995..a4acf76e1a3 100644 --- a/spec/support/helpers/test_env.rb +++ b/spec/support/helpers/test_env.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'rspec/mocks' require 'toml-rb' diff --git a/spec/support/helpers/upload_helpers.rb b/spec/support/helpers/upload_helpers.rb index 5eead80c935..60e14a8673b 100644 --- a/spec/support/helpers/upload_helpers.rb +++ b/spec/support/helpers/upload_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'fileutils' module UploadHelpers diff --git a/spec/support/helpers/wait_for_requests.rb b/spec/support/helpers/wait_for_requests.rb index 45b9faa0fea..3bb2f7c5b51 100644 --- a/spec/support/helpers/wait_for_requests.rb +++ b/spec/support/helpers/wait_for_requests.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module WaitForRequests extend self diff --git a/spec/support/helpers/wait_helpers.rb b/spec/support/helpers/wait_helpers.rb index 7e8e25798e8..a8c4408db59 100644 --- a/spec/support/helpers/wait_helpers.rb +++ b/spec/support/helpers/wait_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module WaitHelpers extend self diff --git a/spec/support/helpers/wiki_helpers.rb b/spec/support/helpers/wiki_helpers.rb index 8165403cb60..06cea728b42 100644 --- a/spec/support/helpers/wiki_helpers.rb +++ b/spec/support/helpers/wiki_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module WikiHelpers extend self diff --git a/spec/support/helpers/workhorse_helpers.rb b/spec/support/helpers/workhorse_helpers.rb index ef1f9f68671..4488e5f227e 100644 --- a/spec/support/helpers/workhorse_helpers.rb +++ b/spec/support/helpers/workhorse_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module WorkhorseHelpers extend self diff --git a/spec/support/http_io/http_io_helpers.rb b/spec/support/http_io/http_io_helpers.rb index 42144870eb5..0193db81fa9 100644 --- a/spec/support/http_io/http_io_helpers.rb +++ b/spec/support/http_io/http_io_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module HttpIOHelpers def stub_remote_url_206(url, file_path) WebMock.stub_request(:get, url) diff --git a/spec/support/import_export/common_util.rb b/spec/support/import_export/common_util.rb index 2542a59bb00..ac6840dbcfc 100644 --- a/spec/support/import_export/common_util.rb +++ b/spec/support/import_export/common_util.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ImportExport module CommonUtil def setup_symlink(tmpdir, symlink_name) diff --git a/spec/support/import_export/configuration_helper.rb b/spec/support/import_export/configuration_helper.rb index b4164cff922..122df7f27f0 100644 --- a/spec/support/import_export/configuration_helper.rb +++ b/spec/support/import_export/configuration_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ConfigurationHelper # Returns a list of models from hashes/arrays contained in +project_tree+ def names_from_tree(project_tree) diff --git a/spec/support/import_export/export_file_helper.rb b/spec/support/import_export/export_file_helper.rb index 388b88f0331..f862a9bc1a4 100644 --- a/spec/support/import_export/export_file_helper.rb +++ b/spec/support/import_export/export_file_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require './spec/support/import_export/configuration_helper' module ExportFileHelper diff --git a/spec/support/inspect_squelch.rb b/spec/support/inspect_squelch.rb index 8ee6732370b..90475204889 100644 --- a/spec/support/inspect_squelch.rb +++ b/spec/support/inspect_squelch.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This class can generate a lot of output if it fails, # so squelch the instance variable output. class ActiveSupport::Cache::NullStore diff --git a/spec/support/issuables_requiring_filter_shared_examples.rb b/spec/support/issuables_requiring_filter_shared_examples.rb index 71bcc82ee58..ee25df00dfb 100644 --- a/spec/support/issuables_requiring_filter_shared_examples.rb +++ b/spec/support/issuables_requiring_filter_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'issuables requiring filter' do |action| it "doesn't load any issuables if no filter is set" do expect_any_instance_of(described_class).not_to receive(:issuables_collection) diff --git a/spec/support/json_response.rb b/spec/support/json_response.rb index 43d8ab73dde..55bdce0cfe9 100644 --- a/spec/support/json_response.rb +++ b/spec/support/json_response.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.configure do |config| config.include_context 'JSON response', type: :controller config.include_context 'JSON response', type: :request diff --git a/spec/support/matchers/abort_matcher.rb b/spec/support/matchers/abort_matcher.rb index ce1dd140210..64fed2ca069 100644 --- a/spec/support/matchers/abort_matcher.rb +++ b/spec/support/matchers/abort_matcher.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :abort_execution do match do |code_block| @captured_stderr = StringIO.new diff --git a/spec/support/matchers/access_matchers.rb b/spec/support/matchers/access_matchers.rb index e6899e2d23c..c9ff777f604 100644 --- a/spec/support/matchers/access_matchers.rb +++ b/spec/support/matchers/access_matchers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # AccessMatchers # # The custom matchers contained in this module are used to test a user's access diff --git a/spec/support/matchers/access_matchers_for_controller.rb b/spec/support/matchers/access_matchers_for_controller.rb index 429401a5da8..401bf6c196e 100644 --- a/spec/support/matchers/access_matchers_for_controller.rb +++ b/spec/support/matchers/access_matchers_for_controller.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # AccessMatchersForController # # For testing authorize_xxx in controller. diff --git a/spec/support/matchers/background_migrations_matchers.rb b/spec/support/matchers/background_migrations_matchers.rb index f4127efc6ae..c38aa7ad6a6 100644 --- a/spec/support/matchers/background_migrations_matchers.rb +++ b/spec/support/matchers/background_migrations_matchers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :be_scheduled_delayed_migration do |delay, *expected| match do |migration| BackgroundMigrationWorker.jobs.any? do |job| diff --git a/spec/support/matchers/be_a_binary_string.rb b/spec/support/matchers/be_a_binary_string.rb index f041ae76167..6195c6c7554 100644 --- a/spec/support/matchers/be_a_binary_string.rb +++ b/spec/support/matchers/be_a_binary_string.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :be_a_binary_string do |_| match do |actual| actual.is_a?(String) && actual.encoding == Encoding.find('ASCII-8BIT') diff --git a/spec/support/matchers/be_like_time.rb b/spec/support/matchers/be_like_time.rb index 1f27390eab7..b449f7a7ffb 100644 --- a/spec/support/matchers/be_like_time.rb +++ b/spec/support/matchers/be_like_time.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :be_like_time do |expected| match do |actual| expect(actual).to be_within(1.second).of(expected) diff --git a/spec/support/matchers/be_url.rb b/spec/support/matchers/be_url.rb index f8096af1b22..7bd0e7fada4 100644 --- a/spec/support/matchers/be_url.rb +++ b/spec/support/matchers/be_url.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :be_url do |_| match do |actual| URI.parse(actual) rescue false diff --git a/spec/support/matchers/be_utf8.rb b/spec/support/matchers/be_utf8.rb index ea806352422..4fa55539a49 100644 --- a/spec/support/matchers/be_utf8.rb +++ b/spec/support/matchers/be_utf8.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :be_utf8 do |_| match do |actual| actual.is_a?(String) && actual.encoding == Encoding.find('UTF-8') diff --git a/spec/support/matchers/be_valid_commit.rb b/spec/support/matchers/be_valid_commit.rb index 3696e4d5f03..b59339de622 100644 --- a/spec/support/matchers/be_valid_commit.rb +++ b/spec/support/matchers/be_valid_commit.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :be_valid_commit do match do |actual| actual && diff --git a/spec/support/matchers/disallow_request_matchers.rb b/spec/support/matchers/disallow_request_matchers.rb index db4d90e4fd0..a161e3660cd 100644 --- a/spec/support/matchers/disallow_request_matchers.rb +++ b/spec/support/matchers/disallow_request_matchers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :disallow_request do match do |middleware| alert = middleware.env['rack.session'].to_hash diff --git a/spec/support/matchers/exceed_query_limit.rb b/spec/support/matchers/exceed_query_limit.rb index cd042401f3a..40cf85eb8e5 100644 --- a/spec/support/matchers/exceed_query_limit.rb +++ b/spec/support/matchers/exceed_query_limit.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ExceedQueryLimitHelpers def with_threshold(threshold) @threshold = threshold diff --git a/spec/support/matchers/execute_check.rb b/spec/support/matchers/execute_check.rb index 7232fad52fb..d3c0751f0dc 100644 --- a/spec/support/matchers/execute_check.rb +++ b/spec/support/matchers/execute_check.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :execute_check do |expected| match do |actual| expect(actual).to eq(SystemCheck) diff --git a/spec/support/matchers/gitaly_matchers.rb b/spec/support/matchers/gitaly_matchers.rb index 933ed22b5d0..c2b3ebe3422 100644 --- a/spec/support/matchers/gitaly_matchers.rb +++ b/spec/support/matchers/gitaly_matchers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :gitaly_request_with_path do |storage_name, relative_path| match do |actual| repository = actual.repository diff --git a/spec/support/matchers/gitlab_git_matchers.rb b/spec/support/matchers/gitlab_git_matchers.rb index c840cd4bf2d..aea1603db05 100644 --- a/spec/support/matchers/gitlab_git_matchers.rb +++ b/spec/support/matchers/gitlab_git_matchers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :gitlab_git_repository_with do |values| match do |actual| actual.is_a?(Gitlab::Git::Repository) && diff --git a/spec/support/matchers/graphql_matchers.rb b/spec/support/matchers/graphql_matchers.rb index 7894484f590..4d48b4b5389 100644 --- a/spec/support/matchers/graphql_matchers.rb +++ b/spec/support/matchers/graphql_matchers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :require_graphql_authorizations do |*expected| match do |field| expect(field.metadata[:authorize]).to eq(*expected) diff --git a/spec/support/matchers/have_emoji.rb b/spec/support/matchers/have_emoji.rb index 23fb8e9c1c4..273bd0b7f40 100644 --- a/spec/support/matchers/have_emoji.rb +++ b/spec/support/matchers/have_emoji.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :have_emoji do |emoji_name| match do |actual| expect(actual).to have_selector("gl-emoji[data-name='#{emoji_name}']") diff --git a/spec/support/matchers/have_gitlab_http_status.rb b/spec/support/matchers/have_gitlab_http_status.rb index e7e418cdde4..13a64a58218 100644 --- a/spec/support/matchers/have_gitlab_http_status.rb +++ b/spec/support/matchers/have_gitlab_http_status.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :have_gitlab_http_status do |expected| match do |actual| expect(actual).to have_http_status(expected) diff --git a/spec/support/matchers/have_issuable_counts.rb b/spec/support/matchers/have_issuable_counts.rb index 92cf3de5448..049cfc022fb 100644 --- a/spec/support/matchers/have_issuable_counts.rb +++ b/spec/support/matchers/have_issuable_counts.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :have_issuable_counts do |opts| expected_counts = opts.map do |state, count| "#{state.to_s.humanize} #{count}" diff --git a/spec/support/matchers/include_module.rb b/spec/support/matchers/include_module.rb index 0a78af1e90e..9b6970cf061 100644 --- a/spec/support/matchers/include_module.rb +++ b/spec/support/matchers/include_module.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :include_module do |expected| match do described_class.included_modules.include?(expected) diff --git a/spec/support/matchers/issuable_matchers.rb b/spec/support/matchers/issuable_matchers.rb index 62f510b0fbd..743f0b8c932 100644 --- a/spec/support/matchers/issuable_matchers.rb +++ b/spec/support/matchers/issuable_matchers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :have_header_with_correct_id_and_link do |level, text, id, parent = ".md"| match do |actual| node = find("#{parent} h#{level} a#user-content-#{id}") diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb index ec4ec6f4038..12e8fa83a60 100644 --- a/spec/support/matchers/markdown_matchers.rb +++ b/spec/support/matchers/markdown_matchers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # MarkdownMatchers # # Custom matchers for our custom HTML::Pipeline filters. These are used to test diff --git a/spec/support/matchers/match_file.rb b/spec/support/matchers/match_file.rb index d1888b3376a..4e522b52912 100644 --- a/spec/support/matchers/match_file.rb +++ b/spec/support/matchers/match_file.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :match_file do |expected| match do |actual| expect(Digest::MD5.hexdigest(actual)).to eq(Digest::MD5.hexdigest(File.read(expected))) diff --git a/spec/support/matchers/match_ids.rb b/spec/support/matchers/match_ids.rb index 1cb6b74acac..7bc41949937 100644 --- a/spec/support/matchers/match_ids.rb +++ b/spec/support/matchers/match_ids.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :match_ids do |*expected| match do |actual| actual_ids = map_ids(actual) diff --git a/spec/support/matchers/metric_counter_matcher.rb b/spec/support/matchers/metric_counter_matcher.rb index 22d5cd17e3f..f0d52b9b149 100644 --- a/spec/support/matchers/metric_counter_matcher.rb +++ b/spec/support/matchers/metric_counter_matcher.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :increment do |counter| match do |adapter| expect(adapter.send(counter)) diff --git a/spec/support/matchers/navigation_matcher.rb b/spec/support/matchers/navigation_matcher.rb index 63f59b9654c..ad73c96031e 100644 --- a/spec/support/matchers/navigation_matcher.rb +++ b/spec/support/matchers/navigation_matcher.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :have_active_navigation do |expected| match do |page| expect(page).to have_selector('.sidebar-top-level-items > li.active', count: 1) diff --git a/spec/support/matchers/pagination_matcher.rb b/spec/support/matchers/pagination_matcher.rb index 9a7697e2bfc..a3e9c3b8474 100644 --- a/spec/support/matchers/pagination_matcher.rb +++ b/spec/support/matchers/pagination_matcher.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :include_pagination_headers do |expected| match do |actual| expect(actual.headers).to include('X-Total', 'X-Total-Pages', 'X-Per-Page', 'X-Page', 'X-Next-Page', 'X-Prev-Page', 'Link') diff --git a/spec/support/matchers/query_matcher.rb b/spec/support/matchers/query_matcher.rb index bb0feca7c43..3e47fe241bc 100644 --- a/spec/support/matchers/query_matcher.rb +++ b/spec/support/matchers/query_matcher.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :make_queries_matching do |matcher, expected_count = nil| supports_block_expectations diff --git a/spec/support/matchers/satisfy_matchers.rb b/spec/support/matchers/satisfy_matchers.rb index 585915bac93..dd1920ee2b2 100644 --- a/spec/support/matchers/satisfy_matchers.rb +++ b/spec/support/matchers/satisfy_matchers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # These matchers are a syntactic hack to provide more readable expectations for # an Enumerable object. # diff --git a/spec/support/matchers/security_header_matcher.rb b/spec/support/matchers/security_header_matcher.rb index f8518d13ebb..760b1fddd06 100644 --- a/spec/support/matchers/security_header_matcher.rb +++ b/spec/support/matchers/security_header_matcher.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec::Matchers.define :include_security_headers do |expected| match do |actual| expect(actual.headers).to include('X-Content-Type-Options') diff --git a/spec/support/migrations_helpers/track_untracked_uploads_helpers.rb b/spec/support/migrations_helpers/track_untracked_uploads_helpers.rb index 016bcfa9b1b..656be3b6d4d 100644 --- a/spec/support/migrations_helpers/track_untracked_uploads_helpers.rb +++ b/spec/support/migrations_helpers/track_untracked_uploads_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module MigrationsHelpers module TrackUntrackedUploadsHelpers PUBLIC_DIR = File.join(Rails.root, 'tmp', 'tests', 'public') diff --git a/spec/support/omni_auth.rb b/spec/support/omni_auth.rb index 0b1af4052ff..64aa3855a6f 100644 --- a/spec/support/omni_auth.rb +++ b/spec/support/omni_auth.rb @@ -1 +1,3 @@ +# frozen_string_literal: true + OmniAuth.config.test_mode = true diff --git a/spec/support/prometheus/additional_metrics_shared_examples.rb b/spec/support/prometheus/additional_metrics_shared_examples.rb index de21e808932..82582630dee 100644 --- a/spec/support/prometheus/additional_metrics_shared_examples.rb +++ b/spec/support/prometheus/additional_metrics_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.shared_examples 'additional metrics query' do include Prometheus::MetricBuilders diff --git a/spec/support/prometheus/metric_builders.rb b/spec/support/prometheus/metric_builders.rb index c8d056d3fc8..512e32a44d0 100644 --- a/spec/support/prometheus/metric_builders.rb +++ b/spec/support/prometheus/metric_builders.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Prometheus module MetricBuilders def simple_query(suffix = 'a', **opts) diff --git a/spec/support/protected_tags/access_control_ce_shared_examples.rb b/spec/support/protected_tags/access_control_ce_shared_examples.rb index 71eec9f3217..8666c19481c 100644 --- a/spec/support/protected_tags/access_control_ce_shared_examples.rb +++ b/spec/support/protected_tags/access_control_ce_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.shared_examples "protected tags > access control > CE" do ProtectedRefAccess::HUMAN_ACCESS_LEVELS.each do |(access_type_id, access_type_name)| it "allows creating protected tags that #{access_type_name} can create" do diff --git a/spec/support/redis/redis_helpers.rb b/spec/support/redis/redis_helpers.rb index 0457e8487d8..7c571738a01 100644 --- a/spec/support/redis/redis_helpers.rb +++ b/spec/support/redis/redis_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module RedisHelpers # config/README.md diff --git a/spec/support/redis/redis_shared_examples.rb b/spec/support/redis/redis_shared_examples.rb index 6aa59960092..7e47cdae866 100644 --- a/spec/support/redis/redis_shared_examples.rb +++ b/spec/support/redis/redis_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.shared_examples "redis_shared_examples" do include StubENV diff --git a/spec/support/rspec.rb b/spec/support/rspec.rb index b38c5dfe60b..1c9f9e5161e 100644 --- a/spec/support/rspec.rb +++ b/spec/support/rspec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative "helpers/stub_configuration" require_relative "helpers/stub_metrics" require_relative "helpers/stub_object_storage" diff --git a/spec/support/seed.rb b/spec/support/seed.rb index bea2e9c3044..36cb819763b 100644 --- a/spec/support/seed.rb +++ b/spec/support/seed.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.configure do |config| config.include SeedHelper, :seed_helper diff --git a/spec/support/services/clusters/create_service_shared.rb b/spec/support/services/clusters/create_service_shared.rb index b0bf942aa09..6ec8750ce87 100644 --- a/spec/support/services/clusters/create_service_shared.rb +++ b/spec/support/services/clusters/create_service_shared.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_context 'valid cluster create params' do let(:params) do { diff --git a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb index 8b4cffaac19..4c3644e6724 100644 --- a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb +++ b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Specifications for behavior common to all objects with executable attributes. # It can take a `default_params`. diff --git a/spec/support/services/issuable_update_service_shared_examples.rb b/spec/support/services/issuable_update_service_shared_examples.rb index ffbce6c42bf..5e5acd0e40a 100644 --- a/spec/support/services/issuable_update_service_shared_examples.rb +++ b/spec/support/services/issuable_update_service_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'issuable update service' do def update_issuable(opts) described_class.new(project, user, opts).execute(open_issuable) diff --git a/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb b/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb index 1284415da1f..65236f13e27 100644 --- a/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb +++ b/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "spec_helper" shared_examples "migrating a deleted user's associated records to the ghost user" do |record_class, fields| diff --git a/spec/support/setup_builds_storage.rb b/spec/support/setup_builds_storage.rb index 1d2a4856724..bbe442f07b0 100644 --- a/spec/support/setup_builds_storage.rb +++ b/spec/support/setup_builds_storage.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.configure do |config| def builds_path Rails.root.join('tmp/tests/builds') diff --git a/spec/support/shared_contexts/email_shared_context.rb b/spec/support/shared_contexts/email_shared_context.rb index 4f5d53f9317..b4d061a8215 100644 --- a/spec/support/shared_contexts/email_shared_context.rb +++ b/spec/support/shared_contexts/email_shared_context.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_context :email_shared_context do let(:mail_key) { "59d8df8370b7e95c5a49fbf86aeb2c93" } let(:receiver) { Gitlab::Email::Receiver.new(email_raw) } diff --git a/spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb b/spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb index a0d994c4d8d..38f6011646e 100644 --- a/spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb +++ b/spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' RSpec.shared_context 'GroupProjectsFinder context' do diff --git a/spec/support/shared_contexts/finders/issues_finder_shared_contexts.rb b/spec/support/shared_contexts/finders/issues_finder_shared_contexts.rb index b8a9554f55f..26ab6fbd400 100644 --- a/spec/support/shared_contexts/finders/issues_finder_shared_contexts.rb +++ b/spec/support/shared_contexts/finders/issues_finder_shared_contexts.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' RSpec.shared_context 'IssuesFinder context' do diff --git a/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb b/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb index ab6687f1d07..ef1e65d2577 100644 --- a/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb +++ b/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' RSpec.shared_context 'MergeRequestsFinder multiple projects with merge requests context' do diff --git a/spec/support/shared_contexts/finders/users_finder_shared_contexts.rb b/spec/support/shared_contexts/finders/users_finder_shared_contexts.rb index 9e1f89ee0ed..d6404b2ee4b 100644 --- a/spec/support/shared_contexts/finders/users_finder_shared_contexts.rb +++ b/spec/support/shared_contexts/finders/users_finder_shared_contexts.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' RSpec.shared_context 'UsersFinder#execute filter by project context' do diff --git a/spec/support/shared_contexts/json_response_shared_context.rb b/spec/support/shared_contexts/json_response_shared_context.rb index df5fc288089..bd37c97ed35 100644 --- a/spec/support/shared_contexts/json_response_shared_context.rb +++ b/spec/support/shared_contexts/json_response_shared_context.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_context 'JSON response' do let(:json_response) { JSON.parse(response.body) } end diff --git a/spec/support/shared_contexts/merge_requests_allowing_collaboration.rb b/spec/support/shared_contexts/merge_requests_allowing_collaboration.rb index 05424d08b9d..276ebf973c8 100644 --- a/spec/support/shared_contexts/merge_requests_allowing_collaboration.rb +++ b/spec/support/shared_contexts/merge_requests_allowing_collaboration.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_context 'merge request allowing collaboration' do include ProjectForksHelper diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index 74389c4d82b..c11725c63d2 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -31,7 +31,7 @@ RSpec.shared_context 'GroupPolicy context' do :admin_group_member, :change_visibility_level, :set_note_created_at, - (Gitlab::Database.postgresql? ? :create_subgroup : nil) + :create_subgroup ].compact end diff --git a/spec/support/shared_contexts/services_shared_context.rb b/spec/support/shared_contexts/services_shared_context.rb index 0c3a24d206f..4d176ab5fca 100644 --- a/spec/support/shared_contexts/services_shared_context.rb +++ b/spec/support/shared_contexts/services_shared_context.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Service.available_services_names.each do |service| shared_context service do let(:dashed_service) { service.dasherize } diff --git a/spec/support/shared_contexts/url_shared_context.rb b/spec/support/shared_contexts/url_shared_context.rb index 1b1f67daac3..560cd500ecd 100644 --- a/spec/support/shared_contexts/url_shared_context.rb +++ b/spec/support/shared_contexts/url_shared_context.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_context 'invalid urls' do let(:urls_with_CRLF) do ["http://127.0.0.1:333/pa\rth", diff --git a/spec/support/shared_examples/chat_slash_commands_shared_examples.rb b/spec/support/shared_examples/chat_slash_commands_shared_examples.rb index dc97a39f051..82975027e5b 100644 --- a/spec/support/shared_examples/chat_slash_commands_shared_examples.rb +++ b/spec/support/shared_examples/chat_slash_commands_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.shared_examples 'chat slash commands service' do describe "Associations" do it { is_expected.to respond_to :token } diff --git a/spec/support/shared_examples/ci_trace_shared_examples.rb b/spec/support/shared_examples/ci_trace_shared_examples.rb index 68c2b6e10e2..6fd4b14d51d 100644 --- a/spec/support/shared_examples/ci_trace_shared_examples.rb +++ b/spec/support/shared_examples/ci_trace_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples_for 'common trace features' do describe '#html' do before do diff --git a/spec/support/shared_examples/common_system_notes_examples.rb b/spec/support/shared_examples/common_system_notes_examples.rb index da5a4f3e319..75f93a32d78 100644 --- a/spec/support/shared_examples/common_system_notes_examples.rb +++ b/spec/support/shared_examples/common_system_notes_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'system note creation' do |update_params, note_text| subject { described_class.new(project, user).execute(issuable, old_labels: []) } diff --git a/spec/support/shared_examples/controllers/external_authorization_service_shared_examples.rb b/spec/support/shared_examples/controllers/external_authorization_service_shared_examples.rb index 8dd78fd0a25..2faa0cf8c1c 100644 --- a/spec/support/shared_examples/controllers/external_authorization_service_shared_examples.rb +++ b/spec/support/shared_examples/controllers/external_authorization_service_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' shared_examples 'disabled when using an external authorization service' do diff --git a/spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb b/spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb index f4b02dc5350..fb22498f84f 100644 --- a/spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb +++ b/spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'issuable notes filter' do let(:params) do if issuable_parent.is_a?(Project) diff --git a/spec/support/shared_examples/controllers/set_sort_order_from_user_preference_shared_examples.rb b/spec/support/shared_examples/controllers/set_sort_order_from_user_preference_shared_examples.rb index eb051166a69..1cd14ea2251 100644 --- a/spec/support/shared_examples/controllers/set_sort_order_from_user_preference_shared_examples.rb +++ b/spec/support/shared_examples/controllers/set_sort_order_from_user_preference_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'set sort order from user preference' do describe '#set_sort_order_from_user_preference' do # There is no issuable_sorting_field defined in any CE controllers yet, diff --git a/spec/support/shared_examples/controllers/todos_shared_examples.rb b/spec/support/shared_examples/controllers/todos_shared_examples.rb index bafd9bac8d0..f3f9abb7da2 100644 --- a/spec/support/shared_examples/controllers/todos_shared_examples.rb +++ b/spec/support/shared_examples/controllers/todos_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'todos actions' do context 'when authorized' do before do diff --git a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb index 59708173716..97b2a01576c 100644 --- a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb +++ b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'handle uploads' do let(:user) { create(:user) } let(:jpg) { fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg') } diff --git a/spec/support/shared_examples/controllers/variables_shared_examples.rb b/spec/support/shared_examples/controllers/variables_shared_examples.rb index e80722857ec..78666e677ef 100644 --- a/spec/support/shared_examples/controllers/variables_shared_examples.rb +++ b/spec/support/shared_examples/controllers/variables_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'GET #show lists all variables' do it 'renders the variables as json' do subject diff --git a/spec/support/shared_examples/dirty_submit_form_shared_examples.rb b/spec/support/shared_examples/dirty_submit_form_shared_examples.rb index 4e45e2921e7..60c8899d349 100644 --- a/spec/support/shared_examples/dirty_submit_form_shared_examples.rb +++ b/spec/support/shared_examples/dirty_submit_form_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'dirty submit form' do |selector_args| selectors = selector_args.is_a?(Array) ? selector_args : [selector_args] diff --git a/spec/support/shared_examples/email_format_shared_examples.rb b/spec/support/shared_examples/email_format_shared_examples.rb index b924a208e71..22d6c2b38e3 100644 --- a/spec/support/shared_examples/email_format_shared_examples.rb +++ b/spec/support/shared_examples/email_format_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Specifications for behavior common to all objects with an email attribute. # Takes a list of email-format attributes and requires: # - subject { "the object with a attribute= setter" } diff --git a/spec/support/shared_examples/fast_destroy_all.rb b/spec/support/shared_examples/fast_destroy_all.rb index a8079b6d864..a64259c03f2 100644 --- a/spec/support/shared_examples/fast_destroy_all.rb +++ b/spec/support/shared_examples/fast_destroy_all.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples_for 'fast destroyable' do describe 'Forbid #destroy and #destroy_all' do it 'does not delete database rows and associted external data' do diff --git a/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb b/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb index 2b36955a3c4..f24e47f4638 100644 --- a/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb +++ b/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'comment on merge request file' do it 'adds a comment' do click_diff_line(find("[id='#{sample_commit.line_code}']")) diff --git a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb index ec1b1754cf0..c0db4cdde72 100644 --- a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb +++ b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.shared_examples 'a creatable merge request' do include WaitForRequests diff --git a/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb index a6121fcc50a..964c80007b0 100644 --- a/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb +++ b/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.shared_examples 'an editable merge request' do it 'updates merge request', :js do find('.js-assignee-search').click diff --git a/spec/support/shared_examples/features/issuable_sidebar_shared_examples.rb b/spec/support/shared_examples/features/issuable_sidebar_shared_examples.rb index 96c821b26f7..09a48533ee3 100644 --- a/spec/support/shared_examples/features/issuable_sidebar_shared_examples.rb +++ b/spec/support/shared_examples/features/issuable_sidebar_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'issue sidebar stays collapsed on mobile' do before do resize_screen_xs diff --git a/spec/support/shared_examples/features/issuables_user_dropdown_behaviors_shared_examples.rb b/spec/support/shared_examples/features/issuables_user_dropdown_behaviors_shared_examples.rb index c92c7f603d6..63ed37cde03 100644 --- a/spec/support/shared_examples/features/issuables_user_dropdown_behaviors_shared_examples.rb +++ b/spec/support/shared_examples/features/issuables_user_dropdown_behaviors_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'issuable user dropdown behaviors' do include FilteredSearchHelpers diff --git a/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb b/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb index d87e5fcaa88..8e1d24c4be2 100644 --- a/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb +++ b/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.shared_examples 'Maintainer manages access requests' do let(:user) { create(:user) } let(:maintainer) { create(:user) } diff --git a/spec/support/shared_examples/features/project_features_apply_to_issuables_shared_examples.rb b/spec/support/shared_examples/features/project_features_apply_to_issuables_shared_examples.rb index 64c3b80136d..51559c0b110 100644 --- a/spec/support/shared_examples/features/project_features_apply_to_issuables_shared_examples.rb +++ b/spec/support/shared_examples/features/project_features_apply_to_issuables_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'project features apply to issuables' do |klass| let(:described_class) { klass } diff --git a/spec/support/shared_examples/features/protected_branches_access_control_ce.rb b/spec/support/shared_examples/features/protected_branches_access_control_ce.rb index a8f2c2e7a5a..867a1774aa9 100644 --- a/spec/support/shared_examples/features/protected_branches_access_control_ce.rb +++ b/spec/support/shared_examples/features/protected_branches_access_control_ce.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples "protected branches > access control > CE" do ProtectedRefAccess::HUMAN_ACCESS_LEVELS.each do |(access_type_id, access_type_name)| it "allows creating protected branches that #{access_type_name} can push to" do diff --git a/spec/support/shared_examples/features/search_shared_examples.rb b/spec/support/shared_examples/features/search_shared_examples.rb index 25ebbf011d5..e27d6700cbf 100644 --- a/spec/support/shared_examples/features/search_shared_examples.rb +++ b/spec/support/shared_examples/features/search_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'top right search form' do it 'does not show top right search form' do expect(page).not_to have_selector('.search') diff --git a/spec/support/shared_examples/file_finder.rb b/spec/support/shared_examples/file_finder.rb index 0dc351b5149..984a06ccd1a 100644 --- a/spec/support/shared_examples/file_finder.rb +++ b/spec/support/shared_examples/file_finder.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'file finder' do let(:query) { 'files' } let(:search_results) { subject.find(query) } diff --git a/spec/support/shared_examples/finders/finder_with_external_authorization_enabled.rb b/spec/support/shared_examples/finders/finder_with_external_authorization_enabled.rb index d7e17cc0b70..b8b0079e36d 100644 --- a/spec/support/shared_examples/finders/finder_with_external_authorization_enabled.rb +++ b/spec/support/shared_examples/finders/finder_with_external_authorization_enabled.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' shared_examples 'a finder with external authorization service' do diff --git a/spec/support/shared_examples/gitlab_verify.rb b/spec/support/shared_examples/gitlab_verify.rb index 560913ca92f..721ea3b4c88 100644 --- a/spec/support/shared_examples/gitlab_verify.rb +++ b/spec/support/shared_examples/gitlab_verify.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.shared_examples 'Gitlab::Verify::BatchVerifier subclass' do describe 'batching' do let(:first_batch) { objects[0].id..objects[0].id } diff --git a/spec/support/shared_examples/graphql/issuable_state_shared_examples.rb b/spec/support/shared_examples/graphql/issuable_state_shared_examples.rb index 713f0a879c1..145c476c7f7 100644 --- a/spec/support/shared_examples/graphql/issuable_state_shared_examples.rb +++ b/spec/support/shared_examples/graphql/issuable_state_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.shared_examples 'issuable state' do it 'exposes all the existing issuable states' do expect(described_class.values.keys).to include(*%w[opened closed locked]) diff --git a/spec/support/shared_examples/group_members_shared_example.rb b/spec/support/shared_examples/group_members_shared_example.rb index 547c83c7955..4f7d496741d 100644 --- a/spec/support/shared_examples/group_members_shared_example.rb +++ b/spec/support/shared_examples/group_members_shared_example.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.shared_examples 'members and requesters associations' do describe '#members_and_requesters' do it 'includes members and requesters' do diff --git a/spec/support/shared_examples/helm_generated_script.rb b/spec/support/shared_examples/helm_generated_script.rb index 01bee603274..17f495ebe46 100644 --- a/spec/support/shared_examples/helm_generated_script.rb +++ b/spec/support/shared_examples/helm_generated_script.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'helm commands' do describe '#generate_script' do let(:helm_setup) do diff --git a/spec/support/shared_examples/issuable_shared_examples.rb b/spec/support/shared_examples/issuable_shared_examples.rb index d97b21f71cd..3460a8ba297 100644 --- a/spec/support/shared_examples/issuable_shared_examples.rb +++ b/spec/support/shared_examples/issuable_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'cache counters invalidator' do it 'invalidates counter cache for assignees' do expect_any_instance_of(User).to receive(:invalidate_merge_request_cache_counts) diff --git a/spec/support/shared_examples/issuables_list_metadata_shared_examples.rb b/spec/support/shared_examples/issuables_list_metadata_shared_examples.rb index 244f4766a84..52d90b5f183 100644 --- a/spec/support/shared_examples/issuables_list_metadata_shared_examples.rb +++ b/spec/support/shared_examples/issuables_list_metadata_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'issuables list meta-data' do |issuable_type, action = nil| include ProjectForksHelper diff --git a/spec/support/shared_examples/issue_tracker_service_shared_example.rb b/spec/support/shared_examples/issue_tracker_service_shared_example.rb index a6ab03cb808..0a483fd30ba 100644 --- a/spec/support/shared_examples/issue_tracker_service_shared_example.rb +++ b/spec/support/shared_examples/issue_tracker_service_shared_example.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.shared_examples 'issue tracker service URL attribute' do |url_attr| it { is_expected.to allow_value('https://example.com').for(url_attr) } diff --git a/spec/support/shared_examples/ldap_shared_examples.rb b/spec/support/shared_examples/ldap_shared_examples.rb index 52c34e78965..0a70ce7ea0c 100644 --- a/spec/support/shared_examples/ldap_shared_examples.rb +++ b/spec/support/shared_examples/ldap_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples_for 'normalizes a DN' do using RSpec::Parameterized::TableSyntax diff --git a/spec/support/shared_examples/legacy_path_redirect_shared_examples.rb b/spec/support/shared_examples/legacy_path_redirect_shared_examples.rb index f326e502092..22e5698825d 100644 --- a/spec/support/shared_examples/legacy_path_redirect_shared_examples.rb +++ b/spec/support/shared_examples/legacy_path_redirect_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'redirecting a legacy path' do |source, target| include RSpec::Rails::RequestExampleGroup diff --git a/spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_examples.rb b/spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_examples.rb index dcf7c1a90c2..2cbc0c2bdf2 100644 --- a/spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_examples.rb +++ b/spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'backfill migration for project repositories' do |storage| describe '#perform' do let(:storage_versions) { storage == :legacy ? [nil, 0] : [1, 2] } diff --git a/spec/support/shared_examples/malicious_regexp_shared_examples.rb b/spec/support/shared_examples/malicious_regexp_shared_examples.rb index a86050e2cf2..96c02260d53 100644 --- a/spec/support/shared_examples/malicious_regexp_shared_examples.rb +++ b/spec/support/shared_examples/malicious_regexp_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'timeout' shared_examples 'malicious regexp' do diff --git a/spec/support/shared_examples/mentionable_shared_examples.rb b/spec/support/shared_examples/mentionable_shared_examples.rb index fea52c2eeb2..93a8c4709a6 100644 --- a/spec/support/shared_examples/mentionable_shared_examples.rb +++ b/spec/support/shared_examples/mentionable_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Specifications for behavior common to all Mentionable implementations. # Requires a shared context containing: # - subject { "the mentionable implementation" } diff --git a/spec/support/shared_examples/milestone_tabs_examples.rb b/spec/support/shared_examples/milestone_tabs_examples.rb index 8b757586941..bda4b978737 100644 --- a/spec/support/shared_examples/milestone_tabs_examples.rb +++ b/spec/support/shared_examples/milestone_tabs_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'milestone tabs' do def go(path, extra_params = {}) params = diff --git a/spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb b/spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb index a248f60d23e..b837ca87256 100644 --- a/spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb +++ b/spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' shared_examples_for 'AtomicInternalId' do |validate_presence: true| diff --git a/spec/support/shared_examples/models/chat_service_shared_examples.rb b/spec/support/shared_examples/models/chat_service_shared_examples.rb index 0a302e7d030..71c0c57f302 100644 --- a/spec/support/shared_examples/models/chat_service_shared_examples.rb +++ b/spec/support/shared_examples/models/chat_service_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "spec_helper" shared_examples_for "chat service" do |service_name| diff --git a/spec/support/shared_examples/models/cluster_application_core_shared_examples.rb b/spec/support/shared_examples/models/cluster_application_core_shared_examples.rb index d6490a808ce..8e58cc7ba22 100644 --- a/spec/support/shared_examples/models/cluster_application_core_shared_examples.rb +++ b/spec/support/shared_examples/models/cluster_application_core_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'cluster application core specs' do |application_name| it { is_expected.to belong_to(:cluster) } it { is_expected.to validate_presence_of(:cluster) } diff --git a/spec/support/shared_examples/models/cluster_application_helm_cert_examples.rb b/spec/support/shared_examples/models/cluster_application_helm_cert_examples.rb index bd3661471f8..7ddb3b11c85 100644 --- a/spec/support/shared_examples/models/cluster_application_helm_cert_examples.rb +++ b/spec/support/shared_examples/models/cluster_application_helm_cert_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'cluster application helm specs' do |application_name| let(:application) { create(application_name) } diff --git a/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb b/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb index 4525c03837f..5341aacb445 100644 --- a/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb +++ b/spec/support/shared_examples/models/cluster_application_status_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'cluster application status specs' do |application_name| describe '#status' do let(:cluster) { create(:cluster, :provided_by_gcp) } diff --git a/spec/support/shared_examples/models/issuable_hook_data_shared_examples.rb b/spec/support/shared_examples/models/issuable_hook_data_shared_examples.rb index a4762b68858..7ea2bb265cc 100644 --- a/spec/support/shared_examples/models/issuable_hook_data_shared_examples.rb +++ b/spec/support/shared_examples/models/issuable_hook_data_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # This shared example requires a `builder` and `user` variable shared_examples 'issuable hook data' do |kind| let(:data) { builder.build(user: user) } diff --git a/spec/support/shared_examples/models/members_notifications_shared_example.rb b/spec/support/shared_examples/models/members_notifications_shared_example.rb index ef5cea3f2a5..050d710f1de 100644 --- a/spec/support/shared_examples/models/members_notifications_shared_example.rb +++ b/spec/support/shared_examples/models/members_notifications_shared_example.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.shared_examples 'members notifications' do |entity_type| let(:notification_service) { double('NotificationService').as_null_object } diff --git a/spec/support/shared_examples/models/project_hook_data_shared_examples.rb b/spec/support/shared_examples/models/project_hook_data_shared_examples.rb index f0264878811..03d10c10e3c 100644 --- a/spec/support/shared_examples/models/project_hook_data_shared_examples.rb +++ b/spec/support/shared_examples/models/project_hook_data_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'project hook data with deprecateds' do |project_key: :project| it 'contains project data' do expect(data[project_key][:name]).to eq(project.name) diff --git a/spec/support/shared_examples/models/with_uploads_shared_examples.rb b/spec/support/shared_examples/models/with_uploads_shared_examples.rb index 43033a2d256..eb1ade03017 100644 --- a/spec/support/shared_examples/models/with_uploads_shared_examples.rb +++ b/spec/support/shared_examples/models/with_uploads_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' shared_examples_for 'model with uploads' do |supports_fileuploads| diff --git a/spec/support/shared_examples/notify_shared_examples.rb b/spec/support/shared_examples/notify_shared_examples.rb index 4452b1c82cb..ca031df000e 100644 --- a/spec/support/shared_examples/notify_shared_examples.rb +++ b/spec/support/shared_examples/notify_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_context 'gitlab email notification' do set(:group) { create(:group) } set(:subgroup) { create(:group, parent: group) } @@ -50,7 +52,7 @@ shared_examples 'an email sent to a user' do it 'is sent to user\'s group notification email' do group_notification_email = 'user+group@example.com' - create(:notification_setting, user: recipient, source: project.group, notification_email: group_notification_email) + create(:notification_setting, user: recipient, source: group, notification_email: group_notification_email) expect(subject).to deliver_to(group_notification_email) end diff --git a/spec/support/shared_examples/position_formatters.rb b/spec/support/shared_examples/position_formatters.rb index ffc9456dbc7..30b6b8d24f0 100644 --- a/spec/support/shared_examples/position_formatters.rb +++ b/spec/support/shared_examples/position_formatters.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples_for "position formatter" do let(:formatter) { described_class.new(attrs) } diff --git a/spec/support/shared_examples/project_latest_successful_build_for_examples.rb b/spec/support/shared_examples/project_latest_successful_build_for_examples.rb new file mode 100644 index 00000000000..a9bd23e9fc9 --- /dev/null +++ b/spec/support/shared_examples/project_latest_successful_build_for_examples.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +shared_examples 'latest successful build for sha or ref' do + context 'with many builds' do + let(:other_pipeline) { create_pipeline(project) } + let(:other_build) { create_build(other_pipeline, 'test') } + let(:build_name) { other_build.name } + + before do + pipeline1 = create_pipeline(project) + pipeline2 = create_pipeline(project) + create_build(pipeline1, 'test') + create_build(pipeline1, 'test2') + create_build(pipeline2, 'test2') + end + + it 'gives the latest builds from latest pipeline' do + expect(subject).to eq(other_build) + end + end + + context 'with succeeded pipeline' do + let!(:build) { create_build } + let(:build_name) { build.name } + + context 'standalone pipeline' do + it 'returns builds for ref for default_branch' do + expect(subject).to eq(build) + end + + context 'with nonexistent build' do + let(:build_name) { 'TAIL' } + + it 'returns empty relation if the build cannot be found' do + expect(subject).to be_nil + end + end + end + + context 'with some pending pipeline' do + before do + create_build(create_pipeline(project, 'pending')) + end + + it 'gives the latest build from latest pipeline' do + expect(subject).to eq(build) + end + end + end + + context 'with pending pipeline' do + let!(:pending_build) { create_build(pipeline) } + let(:build_name) { pending_build.name } + + before do + pipeline.update(status: 'pending') + end + + it 'returns empty relation' do + expect(subject).to be_nil + end + end +end diff --git a/spec/support/shared_examples/reference_parser_shared_examples.rb b/spec/support/shared_examples/reference_parser_shared_examples.rb index baf8bcc04b8..d903c0f10e0 100644 --- a/spec/support/shared_examples/reference_parser_shared_examples.rb +++ b/spec/support/shared_examples/reference_parser_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.shared_examples "referenced feature visibility" do |*related_features| let(:feature_fields) do related_features.map { |feature| (feature + "_access_level").to_sym } diff --git a/spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb b/spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb index 8a7fcf856a1..776a0bdd29e 100644 --- a/spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'custom attributes endpoints' do |attributable_name| let!(:custom_attribute1) { attributable.custom_attributes.create key: 'foo', value: 'foo' } let!(:custom_attribute2) { attributable.custom_attributes.create key: 'bar', value: 'bar' } diff --git a/spec/support/shared_examples/requests/api/diff_discussions.rb b/spec/support/shared_examples/requests/api/diff_discussions.rb index 366c2955359..76c6c93964a 100644 --- a/spec/support/shared_examples/requests/api/diff_discussions.rb +++ b/spec/support/shared_examples/requests/api/diff_discussions.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'diff discussions API' do |parent_type, noteable_type, id_name| describe "GET /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions" do it "includes diff discussions" do diff --git a/spec/support/shared_examples/requests/api/discussions.rb b/spec/support/shared_examples/requests/api/discussions.rb index c3132c41f5b..fc72287f265 100644 --- a/spec/support/shared_examples/requests/api/discussions.rb +++ b/spec/support/shared_examples/requests/api/discussions.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'discussions API' do |parent_type, noteable_type, id_name, can_reply_to_individual_notes: false| describe "GET /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions" do it "returns an array of discussions" do diff --git a/spec/support/shared_examples/requests/api/issuable_participants_examples.rb b/spec/support/shared_examples/requests/api/issuable_participants_examples.rb index 96d59e0c472..9fe6288d53f 100644 --- a/spec/support/shared_examples/requests/api/issuable_participants_examples.rb +++ b/spec/support/shared_examples/requests/api/issuable_participants_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'issuable participants endpoint' do let(:area) { entity.class.name.underscore.pluralize } diff --git a/spec/support/shared_examples/requests/api/issues/merge_requests_count_shared_examples.rb b/spec/support/shared_examples/requests/api/issues/merge_requests_count_shared_examples.rb index 5f4e178f2e5..90c1ed8d09b 100644 --- a/spec/support/shared_examples/requests/api/issues/merge_requests_count_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/issues/merge_requests_count_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + def get_issue json_response.is_a?(Array) ? json_response.detect {|issue| issue['id'] == target_issue.id} : json_response end diff --git a/spec/support/shared_examples/requests/api/notes.rb b/spec/support/shared_examples/requests/api/notes.rb index 57eefd5ef01..354ae7288b1 100644 --- a/spec/support/shared_examples/requests/api/notes.rb +++ b/spec/support/shared_examples/requests/api/notes.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'noteable API' do |parent_type, noteable_type, id_name| describe "GET /#{parent_type}/:id/#{noteable_type}/:noteable_id/notes" do context 'sorting' do diff --git a/spec/support/shared_examples/requests/api/resolvable_discussions.rb b/spec/support/shared_examples/requests/api/resolvable_discussions.rb index 7e2416b23f3..42054a273f3 100644 --- a/spec/support/shared_examples/requests/api/resolvable_discussions.rb +++ b/spec/support/shared_examples/requests/api/resolvable_discussions.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'resolvable discussions API' do |parent_type, noteable_type, id_name| describe "PUT /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions/:discussion_id" do it "resolves discussion if resolved is true" do diff --git a/spec/support/shared_examples/requests/api/status_shared_examples.rb b/spec/support/shared_examples/requests/api/status_shared_examples.rb index ebfc5fed3bb..eebed7e42c1 100644 --- a/spec/support/shared_examples/requests/api/status_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/status_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Specs for status checking. # # Requires an API request: diff --git a/spec/support/shared_examples/requests/graphql_shared_examples.rb b/spec/support/shared_examples/requests/graphql_shared_examples.rb index 04140cad3f0..2a38d56141a 100644 --- a/spec/support/shared_examples/requests/graphql_shared_examples.rb +++ b/spec/support/shared_examples/requests/graphql_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' shared_examples 'a working graphql query' do diff --git a/spec/support/shared_examples/serializers/note_entity_examples.rb b/spec/support/shared_examples/serializers/note_entity_examples.rb index ec208aba2a9..bfcaa2f1bd5 100644 --- a/spec/support/shared_examples/serializers/note_entity_examples.rb +++ b/spec/support/shared_examples/serializers/note_entity_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'note entity' do subject { entity.as_json } diff --git a/spec/support/shared_examples/services/boards/boards_create_service.rb b/spec/support/shared_examples/services/boards/boards_create_service.rb index 5bdc04f660f..19818a6091b 100644 --- a/spec/support/shared_examples/services/boards/boards_create_service.rb +++ b/spec/support/shared_examples/services/boards/boards_create_service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'boards create service' do context 'when parent does not have a board' do it 'creates a new board' do diff --git a/spec/support/shared_examples/services/boards/boards_list_service.rb b/spec/support/shared_examples/services/boards/boards_list_service.rb index e0d5a7c61f2..566e5050f8e 100644 --- a/spec/support/shared_examples/services/boards/boards_list_service.rb +++ b/spec/support/shared_examples/services/boards/boards_list_service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'boards list service' do context 'when parent does not have a board' do it 'creates a new parent board' do diff --git a/spec/support/shared_examples/services/boards/issues_list_service.rb b/spec/support/shared_examples/services/boards/issues_list_service.rb index 8b879cef084..75733c774ef 100644 --- a/spec/support/shared_examples/services/boards/issues_list_service.rb +++ b/spec/support/shared_examples/services/boards/issues_list_service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'issues list service' do it 'delegates search to IssuesFinder' do params = { board_id: board.id, id: list1.id } diff --git a/spec/support/shared_examples/services/boards/issues_move_service.rb b/spec/support/shared_examples/services/boards/issues_move_service.rb index 5359831f8f8..d3fa8084185 100644 --- a/spec/support/shared_examples/services/boards/issues_move_service.rb +++ b/spec/support/shared_examples/services/boards/issues_move_service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'issues move service' do |group| shared_examples 'updating timestamps' do it 'updates updated_at' do diff --git a/spec/support/shared_examples/services/boards/lists_destroy_service.rb b/spec/support/shared_examples/services/boards/lists_destroy_service.rb index 62b6ffe1836..95725078f9d 100644 --- a/spec/support/shared_examples/services/boards/lists_destroy_service.rb +++ b/spec/support/shared_examples/services/boards/lists_destroy_service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'lists destroy service' do context 'when list type is label' do it 'removes list from board' do diff --git a/spec/support/shared_examples/services/boards/lists_list_service.rb b/spec/support/shared_examples/services/boards/lists_list_service.rb index 0a8220111ab..29784f6da08 100644 --- a/spec/support/shared_examples/services/boards/lists_list_service.rb +++ b/spec/support/shared_examples/services/boards/lists_list_service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'lists list service' do context 'when the board has a backlog list' do let!(:backlog_list) { create(:backlog_list, board: board) } diff --git a/spec/support/shared_examples/services/boards/lists_move_service.rb b/spec/support/shared_examples/services/boards/lists_move_service.rb index 2cdb968a45d..0b3bfd8e2a8 100644 --- a/spec/support/shared_examples/services/boards/lists_move_service.rb +++ b/spec/support/shared_examples/services/boards/lists_move_service.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'lists move service' do let!(:planning) { create(:list, board: board, position: 0) } let!(:development) { create(:list, board: board, position: 1) } diff --git a/spec/support/shared_examples/services/check_ingress_ip_address_service_shared_examples.rb b/spec/support/shared_examples/services/check_ingress_ip_address_service_shared_examples.rb index 02de47a96dd..1e0ac8b7615 100644 --- a/spec/support/shared_examples/services/check_ingress_ip_address_service_shared_examples.rb +++ b/spec/support/shared_examples/services/check_ingress_ip_address_service_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'check ingress ip executions' do |app_name| describe '#execute' do let(:application) { create(app_name, :installed) } diff --git a/spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb b/spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb index b8db35a6ef9..1c3fa5644d3 100644 --- a/spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb +++ b/spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'gitlab projects import validations' do context 'with an invalid path' do let(:path) { '/invalid-path/' } diff --git a/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb b/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb index 36c486dbdd6..52fef1c13e1 100644 --- a/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb +++ b/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + Dir[Rails.root.join("app/models/project_services/chat_message/*.rb")].each { |f| require f } RSpec.shared_examples 'slack or mattermost notifications' do diff --git a/spec/support/shared_examples/snippet_visibility_shared_examples.rb b/spec/support/shared_examples/snippet_visibility_shared_examples.rb index 833c31a57cb..b5321c6db34 100644 --- a/spec/support/shared_examples/snippet_visibility_shared_examples.rb +++ b/spec/support/shared_examples/snippet_visibility_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.shared_examples 'snippet visibility' do using RSpec::Parameterized::TableSyntax diff --git a/spec/support/shared_examples/snippets_shared_examples.rb b/spec/support/shared_examples/snippets_shared_examples.rb index 85f0facd5c3..5c35617bd36 100644 --- a/spec/support/shared_examples/snippets_shared_examples.rb +++ b/spec/support/shared_examples/snippets_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # These shared examples expect a `snippets` array of snippets RSpec.shared_examples 'paginated snippets' do |remote: false| it "is limited to #{Snippet.default_per_page} items per page" do diff --git a/spec/support/shared_examples/taskable_shared_examples.rb b/spec/support/shared_examples/taskable_shared_examples.rb index 4a1df1ce380..f04f509f3d2 100644 --- a/spec/support/shared_examples/taskable_shared_examples.rb +++ b/spec/support/shared_examples/taskable_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Specs for task state functionality for issues and merge requests. # # Requires a context containing: diff --git a/spec/support/shared_examples/throttled_touch.rb b/spec/support/shared_examples/throttled_touch.rb index eba990d4037..aaaa590862d 100644 --- a/spec/support/shared_examples/throttled_touch.rb +++ b/spec/support/shared_examples/throttled_touch.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples_for 'throttled touch' do describe '#touch' do it 'updates the updated_at timestamp' do diff --git a/spec/support/shared_examples/unique_ip_check_shared_examples.rb b/spec/support/shared_examples/unique_ip_check_shared_examples.rb index e5c8ac6a004..65d86ddee9e 100644 --- a/spec/support/shared_examples/unique_ip_check_shared_examples.rb +++ b/spec/support/shared_examples/unique_ip_check_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_context 'unique ips sign in limit' do include StubENV before do diff --git a/spec/support/shared_examples/update_invalid_issuable.rb b/spec/support/shared_examples/update_invalid_issuable.rb index 4cb6d001b9b..b7ac08372f9 100644 --- a/spec/support/shared_examples/update_invalid_issuable.rb +++ b/spec/support/shared_examples/update_invalid_issuable.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples 'update invalid issuable' do |klass| let(:params) do { diff --git a/spec/support/shared_examples/updating_mentions_shared_examples.rb b/spec/support/shared_examples/updating_mentions_shared_examples.rb index 5e3f19ba19e..ef385f94cc2 100644 --- a/spec/support/shared_examples/updating_mentions_shared_examples.rb +++ b/spec/support/shared_examples/updating_mentions_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.shared_examples 'updating mentions' do |service_class| let(:mentioned_user) { create(:user) } let(:service_class) { service_class } diff --git a/spec/support/shared_examples/uploaders/gitlab_uploader_shared_examples.rb b/spec/support/shared_examples/uploaders/gitlab_uploader_shared_examples.rb index 1190863d88e..9263aaff89a 100644 --- a/spec/support/shared_examples/uploaders/gitlab_uploader_shared_examples.rb +++ b/spec/support/shared_examples/uploaders/gitlab_uploader_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_examples "matches the method pattern" do |method| let(:target) { subject } let(:args) { nil } diff --git a/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb b/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb index 1bd176280c5..5d605dd811b 100644 --- a/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb +++ b/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + shared_context 'with storage' do |store, **stub_params| before do subject.object_store = store diff --git a/spec/support/shared_examples/url_validator_examples.rb b/spec/support/shared_examples/url_validator_examples.rb index 25277ccd9aa..16fceddb605 100644 --- a/spec/support/shared_examples/url_validator_examples.rb +++ b/spec/support/shared_examples/url_validator_examples.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.shared_examples 'url validator examples' do |schemes| let(:validator) { described_class.new(attributes: [:link_url], **options) } let!(:badge) { build(:badge, link_url: 'http://www.example.com') } diff --git a/spec/support/sidekiq.rb b/spec/support/sidekiq.rb index d1a765f27b9..585c458a64e 100644 --- a/spec/support/sidekiq.rb +++ b/spec/support/sidekiq.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'sidekiq/testing/inline' # If Sidekiq::Testing.inline! is used, SQL transactions done inside diff --git a/spec/support/stored_repositories.rb b/spec/support/stored_repositories.rb index 55212355daa..95f0f971787 100644 --- a/spec/support/stored_repositories.rb +++ b/spec/support/stored_repositories.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + RSpec.configure do |config| config.before(:each, :broken_storage) do allow(Gitlab::GitalyClient).to receive(:call) do diff --git a/spec/support/test_reports/test_reports_helper.rb b/spec/support/test_reports/test_reports_helper.rb index 6840fb9a860..6ba50c83b25 100644 --- a/spec/support/test_reports/test_reports_helper.rb +++ b/spec/support/test_reports/test_reports_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module TestReportsHelper def create_test_case_rspec_success(name = 'test_spec') Gitlab::Ci::Reports::TestCase.new( diff --git a/spec/support/trace/trace_helpers.rb b/spec/support/trace/trace_helpers.rb index c7802bbcb94..9255715ff71 100644 --- a/spec/support/trace/trace_helpers.rb +++ b/spec/support/trace/trace_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module TraceHelpers def create_legacy_trace(build, content) File.open(legacy_trace_path(build), 'wb') { |stream| stream.write(content) } diff --git a/spec/support/webmock.rb b/spec/support/webmock.rb index 9ac7e7fc515..32b88edc2df 100644 --- a/spec/support/webmock.rb +++ b/spec/support/webmock.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'webmock' require 'webmock/rspec' diff --git a/spec/views/groups/edit.html.haml_spec.rb b/spec/views/groups/edit.html.haml_spec.rb index 29e15960fb8..47804411b9d 100644 --- a/spec/views/groups/edit.html.haml_spec.rb +++ b/spec/views/groups/edit.html.haml_spec.rb @@ -35,7 +35,7 @@ describe 'groups/edit.html.haml' do it_behaves_like '"Share with group lock" setting', { disabled: false, checked: false } end - context 'for a subgroup', :nested_groups do + context 'for a subgroup' do let!(:subgroup) { create(:group, parent: root_group) } let(:sub_owner) { create(:user) } let(:test_group) { subgroup } diff --git a/spec/views/layouts/header/_new_dropdown.haml_spec.rb b/spec/views/layouts/header/_new_dropdown.haml_spec.rb index 2e19d0cec26..26e429ac5d0 100644 --- a/spec/views/layouts/header/_new_dropdown.haml_spec.rb +++ b/spec/views/layouts/header/_new_dropdown.haml_spec.rb @@ -28,7 +28,7 @@ describe 'layouts/header/_new_dropdown' do ) end - it 'has a "New subgroup" link', :nested_groups do + it 'has a "New subgroup" link' do render expect(rendered).to have_link( diff --git a/yarn.lock b/yarn.lock index 4fa7665b000..7095ff706c2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -996,10 +996,10 @@ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.67.0.tgz#c7b94eca13b99fd3aaa737fb6dcc0abc41d3c579" integrity sha512-hJOmWEs6RkjzyKkb1vc9wwKGZIBIP0coHkxu/KgOoxhBVudpGk4CH7xJ6UuB2TKpb0SEh5CC1CzRZfBYaFhsaA== -"@gitlab/ui@^5.7.1": - version "5.7.1" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-5.7.1.tgz#e55d04052dd6e50ed1e90676aacc64290d62c0b6" - integrity sha512-F06/6z6/69LbKIK0PYRDTB/teSPUnF7LijHl4JiuYHXn7Y2/iVoLsAMikhT89RVR84orHPGnw16vtCPjSjBDrA== +"@gitlab/ui@^5.9.0": + version "5.9.0" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-5.9.0.tgz#a38b1b57c365608b100b95969ae7a57ce1707542" + integrity sha512-cgvEPWVerYZNLqkHjg5dd0VhEDBWj8aNoISZCaGOWI9K4yVtpMPVRUv19o/xYm4vUexfFsG9vg9lBgd+4ZU6Yw== dependencies: "@babel/standalone" "^7.0.0" "@gitlab/vue-toasted" "^1.2.1" @@ -8340,7 +8340,7 @@ monaco-editor@^0.15.6: resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.15.6.tgz#d63b3b06f86f803464f003b252627c3eb4a09483" integrity sha512-JoU9V9k6KqT9R9Tiw1RTU8ohZ+Xnf9DMg6Ktqqw5hILumwmq7xqa/KLXw513uTUsWbhtnHoSJYYR++u3pkyxJg== -mousetrap@^1.4.6: +mousetrap@1.4.6: version "1.4.6" resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.4.6.tgz#eaca72e22e56d5b769b7555873b688c3332e390a" integrity sha1-6spy4i5W1bdpt1VYc7aIwzMuOQo= |