diff options
169 files changed, 2429 insertions, 1575 deletions
@@ -399,7 +399,7 @@ gem 'mail_room', '~> 0.9.1' gem 'email_reply_trimmer', '~> 0.1' gem 'html2text' -gem 'ruby-prof', '~> 0.17.0' +gem 'ruby-prof', '~> 1.0.0' gem 'rbtrace', '~> 0.4', require: false gem 'memory_profiler', '~> 0.9', require: false gem 'benchmark-memory', '~> 0.1', require: false @@ -438,6 +438,7 @@ gem 'toml-rb', '~> 1.0.0', require: false gem 'flipper', '~> 0.13.0' gem 'flipper-active_record', '~> 0.13.0' gem 'flipper-active_support_cache_store', '~> 0.13.0' +gem 'unleash', '~> 0.1.5' # Structured logging gem 'lograge', '~> 0.5' diff --git a/Gemfile.lock b/Gemfile.lock index d9a32027f3c..a6a44cc6960 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -535,6 +535,7 @@ GEM multi_json (1.13.1) multi_xml (0.6.0) multipart-post (2.0.0) + murmurhash3 (0.1.6) mustermann (1.0.3) mustermann-grape (1.0.0) mustermann (~> 1.0.0) @@ -836,7 +837,7 @@ GEM i18n ruby-fogbugz (0.2.1) crack (~> 0.4) - ruby-prof (0.17.0) + ruby-prof (1.0.0) ruby-progressbar (1.10.1) ruby-saml (1.7.2) nokogiri (>= 1.5.10) @@ -972,6 +973,8 @@ GEM get_process_mem (~> 0) unicorn (>= 4, < 6) uniform_notifier (1.10.0) + unleash (0.1.5) + murmurhash3 (~> 0.1.6) unparser (0.4.5) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) @@ -1215,7 +1218,7 @@ DEPENDENCIES rubocop-performance (~> 1.1.0) rubocop-rspec (~> 1.22.1) ruby-fogbugz (~> 0.2.1) - ruby-prof (~> 0.17.0) + ruby-prof (~> 1.0.0) ruby-progressbar ruby_parser (~> 3.8) rubyzip (~> 1.2.2) @@ -1251,6 +1254,7 @@ DEPENDENCIES unf (~> 0.1.4) unicorn (~> 5.4.1) unicorn-worker-killer (~> 0.4.4) + unleash (~> 0.1.5) validates_hostname (~> 1.0.6) version_sorter (~> 2.2.4) vmstat (~> 2.3.0) diff --git a/app/assets/javascripts/monitoring/components/charts/time_series.vue b/app/assets/javascripts/monitoring/components/charts/time_series.vue index 2fdc75f63ca..02e7a7ba0a6 100644 --- a/app/assets/javascripts/monitoring/components/charts/time_series.vue +++ b/app/assets/javascripts/monitoring/components/charts/time_series.vue @@ -48,6 +48,11 @@ export default { required: false, default: false, }, + singleEmbed: { + type: Boolean, + required: false, + default: false, + }, thresholds: { type: Array, required: false, @@ -267,7 +272,10 @@ export default { </script> <template> - <div class="prometheus-graph col-12 col-lg-6" :class="[showBorder ? 'p-2' : 'p-0']"> + <div + class="prometheus-graph col-12" + :class="[showBorder ? 'p-2' : 'p-0', { 'col-lg-6': !singleEmbed }]" + > <div :class="{ 'prometheus-graph-embed w-100 p-3': showBorder }"> <div class="prometheus-graph-header"> <h5 class="prometheus-graph-title js-graph-title">{{ graphData.title }}</h5> diff --git a/app/assets/javascripts/monitoring/components/embed.vue b/app/assets/javascripts/monitoring/components/embed.vue index e3256147618..b516a82c170 100644 --- a/app/assets/javascripts/monitoring/components/embed.vue +++ b/app/assets/javascripts/monitoring/components/embed.vue @@ -2,7 +2,7 @@ import { mapActions, mapState } from 'vuex'; import { getParameterValues, removeParams } from '~/lib/utils/url_utility'; import GraphGroup from './graph_group.vue'; -import MonitorAreaChart from './charts/area.vue'; +import MonitorTimeSeriesChart from './charts/time_series.vue'; import { sidebarAnimationDuration } from '../constants'; import { getTimeDiff } from '../utils'; @@ -11,7 +11,7 @@ let sidebarMutationObserver; export default { components: { GraphGroup, - MonitorAreaChart, + MonitorTimeSeriesChart, }, props: { dashboardUrl: { @@ -92,7 +92,7 @@ export default { <template> <div class="metrics-embed" :class="{ 'd-inline-flex col-lg-6 p-0': isSingleChart }"> <div v-if="charts.length" class="row w-100 m-n2 pb-4"> - <monitor-area-chart + <monitor-time-series-chart v-for="graphData in charts" :key="graphData.title" :graph-data="graphData" diff --git a/app/assets/javascripts/monitoring/components/panel_type.vue b/app/assets/javascripts/monitoring/components/panel_type.vue index 96f62bc85ee..73ff651d510 100644 --- a/app/assets/javascripts/monitoring/components/panel_type.vue +++ b/app/assets/javascripts/monitoring/components/panel_type.vue @@ -10,14 +10,14 @@ import { GlTooltipDirective, } from '@gitlab/ui'; import Icon from '~/vue_shared/components/icon.vue'; -import MonitorAreaChart from './charts/area.vue'; +import MonitorTimeSeriesChart from './charts/time_series.vue'; import MonitorSingleStatChart from './charts/single_stat.vue'; import MonitorEmptyChart from './charts/empty_chart.vue'; export default { components: { - MonitorAreaChart, MonitorSingleStatChart, + MonitorTimeSeriesChart, MonitorEmptyChart, Icon, GlDropdown, @@ -92,7 +92,7 @@ export default { v-if="isPanelType('single-stat') && graphDataHasMetrics" :graph-data="graphData" /> - <monitor-area-chart + <monitor-time-series-chart v-else-if="graphDataHasMetrics" :graph-data="graphData" :deployment-data="deploymentData" @@ -136,6 +136,6 @@ export default { </gl-dropdown-item> </gl-dropdown> </div> - </monitor-area-chart> + </monitor-time-series-chart> <monitor-empty-chart v-else :graph-title="graphData.title" /> </template> diff --git a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue index 03281aa1317..12ee1ce2f0c 100644 --- a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue +++ b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue @@ -38,7 +38,9 @@ export default { }, computed: { statusTitle() { - return sprintf(s__('Commits|Commit: %{commitText}'), { commitText: this.ciStatus.text }); + return sprintf(s__('PipelineStatusTooltip|Pipeline: %{ciStatus}'), { + ciStatus: this.ciStatus.text, + }); }, }, mounted() { diff --git a/app/assets/javascripts/visual_review_toolbar/components/comment.js b/app/assets/javascripts/visual_review_toolbar/components/comment.js deleted file mode 100644 index a03dc14b319..00000000000 --- a/app/assets/javascripts/visual_review_toolbar/components/comment.js +++ /dev/null @@ -1,39 +0,0 @@ -import { nextView } from '../store'; -import { localStorage, COMMENT_BOX, LOGOUT, STORAGE_MR_ID, STORAGE_TOKEN } from '../shared'; -import { clearNote } from './note'; -import { buttonClearStyles } from './utils'; -import { addForm } from './wrapper'; -import { changeSelectedMr, selectedMrNote } from './comment_mr_note'; -import postComment from './comment_post'; -import { saveComment, getSavedComment } from './comment_storage'; - -const comment = state => { - const savedComment = getSavedComment(); - - return ` - <div> - <textarea id="${COMMENT_BOX}" name="${COMMENT_BOX}" rows="3" placeholder="Enter your feedback or idea" class="gitlab-input" aria-required="true">${savedComment}</textarea> - ${selectedMrNote(state)} - <p class="gitlab-metadata-note">Additional metadata will be included: browser, OS, current page, user agent, and viewport dimensions.</p> - </div> - <div class="gitlab-button-wrapper"> - <button class="gitlab-button gitlab-button-success" style="${buttonClearStyles}" type="button" id="gitlab-comment-button"> Send feedback </button> - <button class="gitlab-button gitlab-button-secondary" style="${buttonClearStyles}" type="button" id="${LOGOUT}"> Log out </button> - </div> - `; -}; - -// This function is here becaause it is called only from the comment view -// If we reach a design where we can logout from multiple views, promote this -// to it's own package -const logoutUser = state => { - localStorage.removeItem(STORAGE_TOKEN); - localStorage.removeItem(STORAGE_MR_ID); - state.token = ''; - state.mergeRequestId = ''; - - clearNote(); - addForm(nextView(state, COMMENT_BOX)); -}; - -export { changeSelectedMr, comment, logoutUser, postComment, saveComment }; diff --git a/app/assets/javascripts/visual_review_toolbar/components/comment_mr_note.js b/app/assets/javascripts/visual_review_toolbar/components/comment_mr_note.js deleted file mode 100644 index da67763261c..00000000000 --- a/app/assets/javascripts/visual_review_toolbar/components/comment_mr_note.js +++ /dev/null @@ -1,31 +0,0 @@ -import { nextView } from '../store'; -import { localStorage, CHANGE_MR_ID_BUTTON, COMMENT_BOX, STORAGE_MR_ID } from '../shared'; -import { clearNote } from './note'; -import { buttonClearStyles } from './utils'; -import { addForm } from './wrapper'; - -const selectedMrNote = state => { - const { mrUrl, projectPath, mergeRequestId } = state; - - const mrLink = `${mrUrl}/${projectPath}/merge_requests/${mergeRequestId}`; - - return ` - <p class="gitlab-metadata-note"> - This posts to merge request <a class="gitlab-link" href="${mrLink}">!${mergeRequestId}</a>. - <button style="${buttonClearStyles}" type="button" id="${CHANGE_MR_ID_BUTTON}" class="gitlab-link gitlab-link-button">Change</button> - </p> - `; -}; - -const clearMrId = state => { - localStorage.removeItem(STORAGE_MR_ID); - state.mergeRequestId = ''; -}; - -const changeSelectedMr = state => { - clearMrId(state); - clearNote(); - addForm(nextView(state, COMMENT_BOX)); -}; - -export { changeSelectedMr, selectedMrNote }; diff --git a/app/assets/javascripts/visual_review_toolbar/components/comment_post.js b/app/assets/javascripts/visual_review_toolbar/components/comment_post.js deleted file mode 100644 index ee5f2b62425..00000000000 --- a/app/assets/javascripts/visual_review_toolbar/components/comment_post.js +++ /dev/null @@ -1,145 +0,0 @@ -import { BLACK, COMMENT_BOX, MUTED } from '../shared'; -import { clearSavedComment } from './comment_storage'; -import { clearNote, postError } from './note'; -import { selectCommentBox, selectCommentButton, selectNote, selectNoteContainer } from './utils'; - -const resetCommentButton = () => { - const commentButton = selectCommentButton(); - - /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ - commentButton.innerText = 'Send feedback'; - commentButton.classList.replace('gitlab-button-secondary', 'gitlab-button-success'); - commentButton.style.opacity = 1; -}; - -const resetCommentBox = () => { - const commentBox = selectCommentBox(); - commentBox.style.pointerEvents = 'auto'; - commentBox.style.color = BLACK; -}; - -const resetCommentText = () => { - const commentBox = selectCommentBox(); - commentBox.value = ''; - clearSavedComment(); -}; - -const resetComment = () => { - resetCommentButton(); - resetCommentBox(); - resetCommentText(); -}; - -const confirmAndClear = feedbackInfo => { - const commentButton = selectCommentButton(); - const currentNote = selectNote(); - const noteContainer = selectNoteContainer(); - - /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ - commentButton.innerText = 'Feedback sent'; - noteContainer.style.visibility = 'visible'; - currentNote.insertAdjacentHTML('beforeend', feedbackInfo); - - setTimeout(resetComment, 1000); - setTimeout(clearNote, 6000); -}; - -const setInProgressState = () => { - const commentButton = selectCommentButton(); - const commentBox = selectCommentBox(); - - /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ - commentButton.innerText = 'Sending feedback'; - commentButton.classList.replace('gitlab-button-success', 'gitlab-button-secondary'); - commentButton.style.opacity = 0.5; - commentBox.style.color = MUTED; - commentBox.style.pointerEvents = 'none'; -}; - -const commentErrors = error => { - switch (error.status) { - case 401: - /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ - return 'Unauthorized. You may have entered an incorrect authentication token.'; - case 404: - /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ - return 'Not found. You may have entered an incorrect merge request ID.'; - default: - /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ - return `Your comment could not be sent. Please try again. Error: ${error.message}`; - } -}; - -const postComment = ({ - platform, - browser, - userAgent, - innerWidth, - innerHeight, - projectId, - projectPath, - mergeRequestId, - mrUrl, - token, -}) => { - // Clear any old errors - clearNote(COMMENT_BOX); - - setInProgressState(); - - const commentText = selectCommentBox().value.trim(); - // Get the href at the last moment to support SPAs - const { href } = window.location; - - if (!commentText) { - /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ - postError('Your comment appears to be empty.', COMMENT_BOX); - resetCommentBox(); - resetCommentButton(); - return; - } - - const detailText = ` - \n -<details> - <summary>Metadata</summary> - Posted from ${href} | ${platform} | ${browser} | ${innerWidth} x ${innerHeight}. - <br /><br /> - <em>User agent: ${userAgent}</em> -</details> - `; - - const url = ` - ${mrUrl}/api/v4/projects/${projectId}/merge_requests/${mergeRequestId}/discussions`; - - const body = `${commentText} ${detailText}`; - - fetch(url, { - method: 'POST', - headers: { - 'PRIVATE-TOKEN': token, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ body }), - }) - .then(response => { - if (response.ok) { - return response.json(); - } - - throw response; - }) - .then(data => { - const commentId = data.notes[0].id; - const feedbackLink = `${mrUrl}/${projectPath}/merge_requests/${mergeRequestId}#note_${commentId}`; - const feedbackInfo = `Feedback sent. View at <a class="gitlab-link" href="${feedbackLink}">${projectPath} !${mergeRequestId} (comment ${commentId})</a>`; - confirmAndClear(feedbackInfo); - }) - .catch(err => { - postError(commentErrors(err), COMMENT_BOX); - resetCommentBox(); - resetCommentButton(); - }); -}; - -export default postComment; diff --git a/app/assets/javascripts/visual_review_toolbar/components/comment_storage.js b/app/assets/javascripts/visual_review_toolbar/components/comment_storage.js deleted file mode 100644 index 49c9400437e..00000000000 --- a/app/assets/javascripts/visual_review_toolbar/components/comment_storage.js +++ /dev/null @@ -1,20 +0,0 @@ -import { selectCommentBox } from './utils'; -import { sessionStorage, STORAGE_COMMENT } from '../shared'; - -const getSavedComment = () => sessionStorage.getItem(STORAGE_COMMENT) || ''; - -const saveComment = () => { - const currentComment = selectCommentBox(); - - // This may be added to any view via top-level beforeunload listener - // so let's skip if it does not apply - if (currentComment && currentComment.value) { - sessionStorage.setItem(STORAGE_COMMENT, currentComment.value); - } -}; - -const clearSavedComment = () => { - sessionStorage.removeItem(STORAGE_COMMENT); -}; - -export { getSavedComment, saveComment, clearSavedComment }; diff --git a/app/assets/javascripts/visual_review_toolbar/components/form_elements.js b/app/assets/javascripts/visual_review_toolbar/components/form_elements.js deleted file mode 100644 index 608488a6fea..00000000000 --- a/app/assets/javascripts/visual_review_toolbar/components/form_elements.js +++ /dev/null @@ -1,17 +0,0 @@ -import { REMEMBER_ITEM } from '../shared'; -import { buttonClearStyles } from './utils'; - -/* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ -const rememberBox = (rememberText = 'Remember me') => ` - <div class="gitlab-checkbox-wrapper"> - <input type="checkbox" id="${REMEMBER_ITEM}" name="${REMEMBER_ITEM}" value="remember"> - <label for="${REMEMBER_ITEM}" class="gitlab-checkbox-label">${rememberText}</label> - </div> -`; - -const submitButton = buttonId => ` - <div class="gitlab-button-wrapper"> - <button class="gitlab-button-wide gitlab-button gitlab-button-success" style="${buttonClearStyles}" type="button" id="${buttonId}"> Submit </button> - </div> -`; -export { rememberBox, submitButton }; diff --git a/app/assets/javascripts/visual_review_toolbar/components/index.js b/app/assets/javascripts/visual_review_toolbar/components/index.js deleted file mode 100644 index e88b3637ad8..00000000000 --- a/app/assets/javascripts/visual_review_toolbar/components/index.js +++ /dev/null @@ -1,23 +0,0 @@ -import { changeSelectedMr, comment, logoutUser, postComment, saveComment } from './comment'; -import { authorizeUser, login } from './login'; -import { addMr, mrForm } from './mr_id'; -import { note } from './note'; -import { selectContainer, selectForm } from './utils'; -import { buttonAndForm, toggleForm } from './wrapper'; - -export { - addMr, - authorizeUser, - buttonAndForm, - changeSelectedMr, - comment, - login, - logoutUser, - mrForm, - note, - postComment, - saveComment, - selectContainer, - selectForm, - toggleForm, -}; diff --git a/app/assets/javascripts/visual_review_toolbar/components/login.js b/app/assets/javascripts/visual_review_toolbar/components/login.js deleted file mode 100644 index 20ab01bc690..00000000000 --- a/app/assets/javascripts/visual_review_toolbar/components/login.js +++ /dev/null @@ -1,47 +0,0 @@ -import { nextView } from '../store'; -import { localStorage, LOGIN, TOKEN_BOX, STORAGE_TOKEN } from '../shared'; -import { clearNote, postError } from './note'; -import { rememberBox, submitButton } from './form_elements'; -import { selectRemember, selectToken } from './utils'; -import { addForm } from './wrapper'; - -const labelText = ` - Enter your <a class="gitlab-link" href="https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html">personal access token</a> -`; - -const login = ` - <div> - <label for="${TOKEN_BOX}" class="gitlab-label">${labelText}</label> - <input class="gitlab-input" type="password" id="${TOKEN_BOX}" name="${TOKEN_BOX}" autocomplete="current-password" aria-required="true"> - </div> - ${rememberBox()} - ${submitButton(LOGIN)} -`; - -const storeToken = (token, state) => { - const rememberMe = selectRemember().checked; - - if (rememberMe) { - localStorage.setItem(STORAGE_TOKEN, token); - } - - state.token = token; -}; - -const authorizeUser = state => { - // Clear any old errors - clearNote(TOKEN_BOX); - - const token = selectToken().value; - - if (!token) { - /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ - postError('Please enter your token.', TOKEN_BOX); - return; - } - - storeToken(token, state); - addForm(nextView(state, LOGIN)); -}; - -export { authorizeUser, login, storeToken }; diff --git a/app/assets/javascripts/visual_review_toolbar/components/mr_id.js b/app/assets/javascripts/visual_review_toolbar/components/mr_id.js deleted file mode 100644 index 695b3af8aa0..00000000000 --- a/app/assets/javascripts/visual_review_toolbar/components/mr_id.js +++ /dev/null @@ -1,63 +0,0 @@ -import { nextView } from '../store'; -import { MR_ID, MR_ID_BUTTON, STORAGE_MR_ID, localStorage } from '../shared'; -import { clearNote, postError } from './note'; -import { rememberBox, submitButton } from './form_elements'; -import { selectForm, selectMrBox, selectRemember } from './utils'; -import { addForm } from './wrapper'; - -/* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ -const mrLabel = `Enter your merge request ID`; -/* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ -const mrRememberText = `Remember this number`; - -const mrForm = ` - <div> - <label for="${MR_ID}" class="gitlab-label">${mrLabel}</label> - <input class="gitlab-input" type="text" pattern="[1-9][0-9]*" id="${MR_ID}" name="${MR_ID}" placeholder="e.g., 321" aria-required="true"> - </div> - ${rememberBox(mrRememberText)} - ${submitButton(MR_ID_BUTTON)} -`; - -const storeMR = (id, state) => { - const rememberMe = selectRemember().checked; - - if (rememberMe) { - localStorage.setItem(STORAGE_MR_ID, id); - } - - state.mergeRequestId = id; -}; - -const getFormError = (mrNumber, form) => { - if (!mrNumber) { - /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ - return 'Please enter your merge request ID number.'; - } - - if (!form.checkValidity()) { - /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ - return 'Please remove any non-number values from the field.'; - } - - return null; -}; - -const addMr = state => { - // Clear any old errors - clearNote(MR_ID); - - const mrNumber = selectMrBox().value; - const form = selectForm(); - const formError = getFormError(mrNumber, form); - - if (formError) { - postError(formError, MR_ID); - return; - } - - storeMR(mrNumber, state); - addForm(nextView(state, MR_ID)); -}; - -export { addMr, mrForm, storeMR }; diff --git a/app/assets/javascripts/visual_review_toolbar/components/note.js b/app/assets/javascripts/visual_review_toolbar/components/note.js deleted file mode 100644 index 9cddcb710f2..00000000000 --- a/app/assets/javascripts/visual_review_toolbar/components/note.js +++ /dev/null @@ -1,35 +0,0 @@ -import { NOTE, NOTE_CONTAINER, RED } from '../shared'; -import { selectById, selectNote, selectNoteContainer } from './utils'; - -const note = ` - <div id="${NOTE_CONTAINER}" style="visibility: hidden;"> - <p id="${NOTE}" class="gitlab-message"></p> - </div> -`; - -const clearNote = inputId => { - const currentNote = selectNote(); - const noteContainer = selectNoteContainer(); - - currentNote.innerText = ''; - currentNote.style.color = ''; - noteContainer.style.visibility = 'hidden'; - - if (inputId) { - const field = document.getElementById(inputId); - field.style.borderColor = ''; - } -}; - -const postError = (message, inputId) => { - const currentNote = selectNote(); - const noteContainer = selectNoteContainer(); - const field = selectById(inputId); - field.style.borderColor = RED; - currentNote.style.color = RED; - currentNote.innerText = message; - noteContainer.style.visibility = 'visible'; - setTimeout(clearNote.bind(null, inputId), 5000); -}; - -export { clearNote, note, postError }; diff --git a/app/assets/javascripts/visual_review_toolbar/components/utils.js b/app/assets/javascripts/visual_review_toolbar/components/utils.js deleted file mode 100644 index 4ec9bd4a32a..00000000000 --- a/app/assets/javascripts/visual_review_toolbar/components/utils.js +++ /dev/null @@ -1,51 +0,0 @@ -/* global document */ - -import { - COLLAPSE_BUTTON, - COMMENT_BOX, - COMMENT_BUTTON, - FORM, - FORM_CONTAINER, - MR_ID, - NOTE, - NOTE_CONTAINER, - REMEMBER_ITEM, - REVIEW_CONTAINER, - TOKEN_BOX, -} from '../shared'; - -// this style must be applied inline in a handful of components -/* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ -const buttonClearStyles = ` - -webkit-appearance: none; -`; - -// selector functions to abstract out a little -const selectById = id => document.getElementById(id); -const selectCollapseButton = () => document.getElementById(COLLAPSE_BUTTON); -const selectCommentBox = () => document.getElementById(COMMENT_BOX); -const selectCommentButton = () => document.getElementById(COMMENT_BUTTON); -const selectContainer = () => document.getElementById(REVIEW_CONTAINER); -const selectForm = () => document.getElementById(FORM); -const selectFormContainer = () => document.getElementById(FORM_CONTAINER); -const selectMrBox = () => document.getElementById(MR_ID); -const selectNote = () => document.getElementById(NOTE); -const selectNoteContainer = () => document.getElementById(NOTE_CONTAINER); -const selectRemember = () => document.getElementById(REMEMBER_ITEM); -const selectToken = () => document.getElementById(TOKEN_BOX); - -export { - buttonClearStyles, - selectById, - selectCollapseButton, - selectContainer, - selectCommentBox, - selectCommentButton, - selectForm, - selectFormContainer, - selectMrBox, - selectNote, - selectNoteContainer, - selectRemember, - selectToken, -}; diff --git a/app/assets/javascripts/visual_review_toolbar/components/wrapper.js b/app/assets/javascripts/visual_review_toolbar/components/wrapper.js deleted file mode 100644 index fdf8ad7c41f..00000000000 --- a/app/assets/javascripts/visual_review_toolbar/components/wrapper.js +++ /dev/null @@ -1,79 +0,0 @@ -import { CLEAR, FORM, FORM_CONTAINER, WHITE } from '../shared'; -import { - selectCollapseButton, - selectForm, - selectFormContainer, - selectNoteContainer, -} from './utils'; -import { collapseButton, commentIcon, compressIcon } from './wrapper_icons'; - -const form = content => ` - <form id="${FORM}" novalidate> - ${content} - </form> -`; - -const buttonAndForm = content => ` - <div id="${FORM_CONTAINER}" class="gitlab-form-open"> - ${collapseButton} - ${form(content)} - </div> -`; - -const addForm = nextForm => { - const formWrapper = selectForm(); - formWrapper.innerHTML = nextForm; -}; - -function toggleForm() { - const toggleButton = selectCollapseButton(); - const currentForm = selectForm(); - const formContainer = selectFormContainer(); - const noteContainer = selectNoteContainer(); - const OPEN = 'open'; - const CLOSED = 'closed'; - - /* - You may wonder why we spread the arrays before we reverse them. - In the immortal words of MDN, - Careful: reverse is destructive. It also changes the original array - */ - - const openButtonClasses = ['gitlab-collapse-closed', 'gitlab-collapse-open']; - const closedButtonClasses = [...openButtonClasses].reverse(); - const openContainerClasses = ['gitlab-wrapper-closed', 'gitlab-wrapper-open']; - const closedContainerClasses = [...openContainerClasses].reverse(); - - const stateVals = { - [OPEN]: { - buttonClasses: openButtonClasses, - containerClasses: openContainerClasses, - icon: compressIcon, - display: 'flex', - backgroundColor: WHITE, - }, - [CLOSED]: { - buttonClasses: closedButtonClasses, - containerClasses: closedContainerClasses, - icon: commentIcon, - display: 'none', - backgroundColor: CLEAR, - }, - }; - - const nextState = toggleButton.classList.contains('gitlab-collapse-open') ? CLOSED : OPEN; - const currentVals = stateVals[nextState]; - - formContainer.classList.replace(...currentVals.containerClasses); - formContainer.style.backgroundColor = currentVals.backgroundColor; - formContainer.classList.toggle('gitlab-form-open'); - currentForm.style.display = currentVals.display; - toggleButton.classList.replace(...currentVals.buttonClasses); - toggleButton.innerHTML = currentVals.icon; - - if (noteContainer && noteContainer.innerText.length > 0) { - noteContainer.style.display = currentVals.display; - } -} - -export { addForm, buttonAndForm, toggleForm }; diff --git a/app/assets/javascripts/visual_review_toolbar/components/wrapper_icons.js b/app/assets/javascripts/visual_review_toolbar/components/wrapper_icons.js deleted file mode 100644 index b686fd4f5c2..00000000000 --- a/app/assets/javascripts/visual_review_toolbar/components/wrapper_icons.js +++ /dev/null @@ -1,15 +0,0 @@ -import { buttonClearStyles } from './utils'; - -const commentIcon = ` - <svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>icn/comment</title><path d="M4 11.132l1.446-.964A1 1 0 0 1 6 10h5a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v6.132zM6.303 12l-2.748 1.832A1 1 0 0 1 2 13V5a3 3 0 0 1 3-3h6a3 3 0 0 1 3 3v4a3 3 0 0 1-3 3H6.303z" id="gitlab-comment-icon"/></svg> -`; - -const compressIcon = ` - <svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>icn/compress</title><path d="M5.27 12.182l-1.562 1.561a1 1 0 0 1-1.414 0h-.001a1 1 0 0 1 0-1.415l1.56-1.56L2.44 9.353a.5.5 0 0 1 .353-.854H7.09a.5.5 0 0 1 .5.5v4.294a.5.5 0 0 1-.853.353l-1.467-1.465zm6.911-6.914l1.464 1.464a.5.5 0 0 1-.353.854H8.999a.5.5 0 0 1-.5-.5V2.793a.5.5 0 0 1 .854-.354l1.414 1.415 1.56-1.561a1 1 0 1 1 1.415 1.414l-1.561 1.56z" id="gitlab-compress-icon"/></svg> -`; - -const collapseButton = ` - <button id='gitlab-collapse' style='${buttonClearStyles}' class='gitlab-button gitlab-button-secondary gitlab-collapse gitlab-collapse-open'>${compressIcon}</button> -`; - -export { commentIcon, compressIcon, collapseButton }; diff --git a/app/assets/javascripts/visual_review_toolbar/index.js b/app/assets/javascripts/visual_review_toolbar/index.js deleted file mode 100644 index 67b3fadd772..00000000000 --- a/app/assets/javascripts/visual_review_toolbar/index.js +++ /dev/null @@ -1,51 +0,0 @@ -import './styles/toolbar.css'; - -import { buttonAndForm, note, selectForm, selectContainer } from './components'; -import { REVIEW_CONTAINER } from './shared'; -import { eventLookup, getInitialView, initializeGlobalListeners, initializeState } from './store'; - -/* - - Welcome to the visual review toolbar files. A few useful notes: - - - These files build a static script that is served from our webpack - assets folder. (https://gitlab.com/assets/webpack/visual_review_toolbar.js) - - - To compile this file, run `yarn webpack-vrt`. - - - Vue is not used in these files because we do not want to ask users to - install another library at this time. It's all pure vanilla javascript. - -*/ - -window.addEventListener('load', () => { - initializeState(window, document); - - const mainContent = buttonAndForm(getInitialView()); - const container = document.createElement('div'); - container.setAttribute('id', REVIEW_CONTAINER); - container.insertAdjacentHTML('beforeend', note); - container.insertAdjacentHTML('beforeend', mainContent); - - document.body.insertBefore(container, document.body.firstChild); - - selectContainer().addEventListener('click', event => { - eventLookup(event.target.id)(); - }); - - selectForm().addEventListener('submit', event => { - // this is important to prevent the form from adding data - // as URL params and inadvertently revealing secrets - event.preventDefault(); - - const id = - event.target.querySelector('.gitlab-button-wrapper') && - event.target.querySelector('.gitlab-button-wrapper').getElementsByTagName('button')[0] && - event.target.querySelector('.gitlab-button-wrapper').getElementsByTagName('button')[0].id; - - // even if this is called with false, it's ok; it will get the default no-op - eventLookup(id)(); - }); - - initializeGlobalListeners(); -}); diff --git a/app/assets/javascripts/visual_review_toolbar/shared/constants.js b/app/assets/javascripts/visual_review_toolbar/shared/constants.js deleted file mode 100644 index 0d5b666ab0a..00000000000 --- a/app/assets/javascripts/visual_review_toolbar/shared/constants.js +++ /dev/null @@ -1,56 +0,0 @@ -// component selectors -const CHANGE_MR_ID_BUTTON = 'gitlab-change-mr'; -const COLLAPSE_BUTTON = 'gitlab-collapse'; -const COMMENT_BOX = 'gitlab-comment'; -const COMMENT_BUTTON = 'gitlab-comment-button'; -const FORM = 'gitlab-form'; -const FORM_CONTAINER = 'gitlab-form-wrapper'; -const LOGIN = 'gitlab-login-button'; -const LOGOUT = 'gitlab-logout-button'; -const MR_ID = 'gitlab-submit-mr'; -const MR_ID_BUTTON = 'gitlab-submit-mr-button'; -const NOTE = 'gitlab-validation-note'; -const NOTE_CONTAINER = 'gitlab-note-wrapper'; -const REMEMBER_ITEM = 'gitlab-remember-item'; -const REVIEW_CONTAINER = 'gitlab-review-container'; -const TOKEN_BOX = 'gitlab-token'; - -// Storage keys -const STORAGE_PREFIX = '--gitlab'; // Using `--` to make the prefix more unique -const STORAGE_MR_ID = `${STORAGE_PREFIX}-merge-request-id`; -const STORAGE_TOKEN = `${STORAGE_PREFIX}-token`; -const STORAGE_COMMENT = `${STORAGE_PREFIX}-comment`; - -// colors — these are applied programmatically -// rest of styles belong in ./styles -const BLACK = 'rgba(46, 46, 46, 1)'; -const CLEAR = 'rgba(255, 255, 255, 0)'; -const MUTED = 'rgba(223, 223, 223, 0.5)'; -const RED = 'rgba(219, 59, 33, 1)'; -const WHITE = 'rgba(250, 250, 250, 1)'; - -export { - CHANGE_MR_ID_BUTTON, - COLLAPSE_BUTTON, - COMMENT_BOX, - COMMENT_BUTTON, - FORM, - FORM_CONTAINER, - LOGIN, - LOGOUT, - MR_ID, - MR_ID_BUTTON, - NOTE, - NOTE_CONTAINER, - REMEMBER_ITEM, - REVIEW_CONTAINER, - TOKEN_BOX, - STORAGE_MR_ID, - STORAGE_TOKEN, - STORAGE_COMMENT, - BLACK, - CLEAR, - MUTED, - RED, - WHITE, -}; diff --git a/app/assets/javascripts/visual_review_toolbar/shared/index.js b/app/assets/javascripts/visual_review_toolbar/shared/index.js deleted file mode 100644 index d8ccb170592..00000000000 --- a/app/assets/javascripts/visual_review_toolbar/shared/index.js +++ /dev/null @@ -1,55 +0,0 @@ -import { - CHANGE_MR_ID_BUTTON, - COLLAPSE_BUTTON, - COMMENT_BOX, - COMMENT_BUTTON, - FORM, - FORM_CONTAINER, - LOGIN, - LOGOUT, - MR_ID, - MR_ID_BUTTON, - NOTE, - NOTE_CONTAINER, - REMEMBER_ITEM, - REVIEW_CONTAINER, - TOKEN_BOX, - STORAGE_MR_ID, - STORAGE_TOKEN, - STORAGE_COMMENT, - BLACK, - CLEAR, - MUTED, - RED, - WHITE, -} from './constants'; - -import { localStorage, sessionStorage } from './storage_utils'; - -export { - localStorage, - sessionStorage, - CHANGE_MR_ID_BUTTON, - COLLAPSE_BUTTON, - COMMENT_BOX, - COMMENT_BUTTON, - FORM, - FORM_CONTAINER, - LOGIN, - LOGOUT, - MR_ID, - MR_ID_BUTTON, - NOTE, - NOTE_CONTAINER, - REMEMBER_ITEM, - REVIEW_CONTAINER, - TOKEN_BOX, - STORAGE_MR_ID, - STORAGE_TOKEN, - STORAGE_COMMENT, - BLACK, - CLEAR, - MUTED, - RED, - WHITE, -}; diff --git a/app/assets/javascripts/visual_review_toolbar/shared/storage_utils.js b/app/assets/javascripts/visual_review_toolbar/shared/storage_utils.js deleted file mode 100644 index 00456d3536e..00000000000 --- a/app/assets/javascripts/visual_review_toolbar/shared/storage_utils.js +++ /dev/null @@ -1,42 +0,0 @@ -import { setUsingGracefulStorageFlag } from '../store/state'; - -const TEST_KEY = 'gitlab-storage-test'; - -const createStorageStub = () => { - const items = {}; - - return { - getItem(key) { - return items[key]; - }, - setItem(key, value) { - items[key] = value; - }, - removeItem(key) { - delete items[key]; - }, - }; -}; - -const hasStorageSupport = storage => { - // Support test taken from https://stackoverflow.com/a/11214467/1708147 - try { - storage.setItem(TEST_KEY, TEST_KEY); - storage.removeItem(TEST_KEY); - setUsingGracefulStorageFlag(true); - - return true; - } catch (err) { - setUsingGracefulStorageFlag(false); - return false; - } -}; - -const useGracefulStorage = storage => - // If a browser does not support local storage, let's return a graceful implementation. - hasStorageSupport(storage) ? storage : createStorageStub(); - -const localStorage = useGracefulStorage(window.localStorage); -const sessionStorage = useGracefulStorage(window.sessionStorage); - -export { localStorage, sessionStorage }; diff --git a/app/assets/javascripts/visual_review_toolbar/store/events.js b/app/assets/javascripts/visual_review_toolbar/store/events.js deleted file mode 100644 index c9095c77ef1..00000000000 --- a/app/assets/javascripts/visual_review_toolbar/store/events.js +++ /dev/null @@ -1,73 +0,0 @@ -import { - addMr, - authorizeUser, - changeSelectedMr, - logoutUser, - postComment, - saveComment, - toggleForm, -} from '../components'; - -import { - CHANGE_MR_ID_BUTTON, - COLLAPSE_BUTTON, - COMMENT_BUTTON, - LOGIN, - LOGOUT, - MR_ID_BUTTON, -} from '../shared'; - -import { state } from './state'; -import debounce from './utils'; - -const noop = () => {}; - -// State needs to be bound here to be acted on -// because these are called by click events and -// as such are called with only the `event` object -const eventLookup = id => { - switch (id) { - case CHANGE_MR_ID_BUTTON: - return () => { - saveComment(); - changeSelectedMr(state); - }; - case COLLAPSE_BUTTON: - return toggleForm; - case COMMENT_BUTTON: - return postComment.bind(null, state); - case LOGIN: - return authorizeUser.bind(null, state); - case LOGOUT: - return () => { - saveComment(); - logoutUser(state); - }; - case MR_ID_BUTTON: - return addMr.bind(null, state); - default: - return noop; - } -}; - -const updateWindowSize = wind => { - state.innerWidth = wind.innerWidth; - state.innerHeight = wind.innerHeight; -}; - -const initializeGlobalListeners = () => { - window.addEventListener('resize', debounce(updateWindowSize.bind(null, window), 200)); - window.addEventListener('beforeunload', event => { - if (state.usingGracefulStorage) { - // if there is no browser storage support, reloading will lose the comment; this way, the user will be warned - // we assign the return value because it is required by Chrome see: https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload#Example, - event.preventDefault(); - /* eslint-disable-next-line no-param-reassign */ - event.returnValue = ''; - } - - saveComment(); - }); -}; - -export { eventLookup, initializeGlobalListeners }; diff --git a/app/assets/javascripts/visual_review_toolbar/store/index.js b/app/assets/javascripts/visual_review_toolbar/store/index.js deleted file mode 100644 index 07c8dd6f1d2..00000000000 --- a/app/assets/javascripts/visual_review_toolbar/store/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import { eventLookup, initializeGlobalListeners } from './events'; -import { nextView, getInitialView, initializeState, setUsingGracefulStorageFlag } from './state'; - -export { - eventLookup, - getInitialView, - initializeGlobalListeners, - initializeState, - nextView, - setUsingGracefulStorageFlag, -}; diff --git a/app/assets/javascripts/visual_review_toolbar/store/state.js b/app/assets/javascripts/visual_review_toolbar/store/state.js deleted file mode 100644 index b7853bb0723..00000000000 --- a/app/assets/javascripts/visual_review_toolbar/store/state.js +++ /dev/null @@ -1,95 +0,0 @@ -import { comment, login, mrForm } from '../components'; -import { localStorage, COMMENT_BOX, LOGIN, MR_ID, STORAGE_MR_ID, STORAGE_TOKEN } from '../shared'; - -const state = { - browser: '', - usingGracefulStorage: '', - innerWidth: '', - innerHeight: '', - mergeRequestId: '', - mrUrl: '', - platform: '', - projectId: '', - userAgent: '', - token: '', -}; - -// adapted from https://developer.mozilla.org/en-US/docs/Web/API/Window/navigator#Example_2_Browser_detect_and_return_an_index -const getBrowserId = sUsrAg => { - /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ - const aKeys = ['MSIE', 'Edge', 'Firefox', 'Safari', 'Chrome', 'Opera']; - let nIdx = aKeys.length - 1; - - for (nIdx; nIdx > -1 && sUsrAg.indexOf(aKeys[nIdx]) === -1; nIdx -= 1); - return aKeys[nIdx]; -}; - -const nextView = (appState, form = 'none') => { - const formsList = { - [COMMENT_BOX]: currentState => (currentState.token ? mrForm : login), - [LOGIN]: currentState => (currentState.mergeRequestId ? comment(currentState) : mrForm), - [MR_ID]: currentState => (currentState.token ? comment(currentState) : login), - none: currentState => { - if (!currentState.token) { - return login; - } - - if (currentState.token && !currentState.mergeRequestId) { - return mrForm; - } - - return comment(currentState); - }, - }; - - return formsList[form](appState); -}; - -const initializeState = (wind, doc) => { - const { - innerWidth, - innerHeight, - navigator: { platform, userAgent }, - } = wind; - - const browser = getBrowserId(userAgent); - - const scriptEl = doc.getElementById('review-app-toolbar-script'); - const { projectId, mergeRequestId, mrUrl, projectPath } = scriptEl.dataset; - - // This mutates our default state object above. It's weird but it makes the linter happy. - Object.assign(state, { - browser, - innerWidth, - innerHeight, - mergeRequestId, - mrUrl, - platform, - projectId, - projectPath, - userAgent, - }); - - return state; -}; - -const getInitialView = () => { - const token = localStorage.getItem(STORAGE_TOKEN); - const mrId = localStorage.getItem(STORAGE_MR_ID); - - if (token) { - state.token = token; - } - - if (mrId) { - state.mergeRequestId = mrId; - } - - return nextView(state); -}; - -const setUsingGracefulStorageFlag = flag => { - state.usingGracefulStorage = !flag; -}; - -export { initializeState, getInitialView, nextView, setUsingGracefulStorageFlag, state }; diff --git a/app/assets/javascripts/visual_review_toolbar/store/utils.js b/app/assets/javascripts/visual_review_toolbar/store/utils.js deleted file mode 100644 index 5cf145351b3..00000000000 --- a/app/assets/javascripts/visual_review_toolbar/store/utils.js +++ /dev/null @@ -1,15 +0,0 @@ -const debounce = (fn, time) => { - let current; - - const debounced = () => { - if (current) { - clearTimeout(current); - } - - current = setTimeout(fn, time); - }; - - return debounced; -}; - -export default debounce; diff --git a/app/assets/javascripts/visual_review_toolbar/styles/toolbar.css b/app/assets/javascripts/visual_review_toolbar/styles/toolbar.css deleted file mode 100644 index d1a8d66ef40..00000000000 --- a/app/assets/javascripts/visual_review_toolbar/styles/toolbar.css +++ /dev/null @@ -1,188 +0,0 @@ -/* - As a standalone script, the toolbar has its own css - */ - -#gitlab-collapse > * { - pointer-events: none; -} - -#gitlab-comment { - background-color: #fafafa; -} - -#gitlab-form { - display: flex; - flex-direction: column; - width: 100%; - margin-bottom: 0; -} - -#gitlab-note-wrapper { - display: flex; - flex-direction: column; - background-color: #fafafa; - border-radius: 4px; - margin-bottom: .5rem; - padding: 1rem; -} - -#gitlab-form-wrapper { - overflow: auto; - display: flex; - flex-direction: row-reverse; - border-radius: 4px; -} - -#gitlab-review-container { - max-width: 22rem; - max-height: 22rem; - overflow: auto; - display: flex; - flex-direction: column; - position: fixed; - bottom: 1rem; - right: 1rem; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Noto Sans', Ubuntu, Cantarell, - 'Helvetica Neue', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', - 'Noto Color Emoji'; - font-size: .8rem; - font-weight: 400; - color: #2e2e2e; - z-index: 9999; /* toolbar should always be on top */ -} - -.gitlab-wrapper-open { - max-width: 22rem; - max-height: 22rem; -} - -.gitlab-wrapper-closed { - max-width: 3.4rem; - max-height: 3.4rem; -} - -.gitlab-button { - cursor: pointer; - transition: background-color 100ms linear, border-color 100ms linear, color 100ms linear, box-shadow 100ms linear; -} - -.gitlab-button-secondary { - background: none #fafafa; - margin: 0 .5rem; - border: 1px solid #e3e3e3; -} - -.gitlab-button-secondary:hover { - background-color: #f0f0f0; - border-color: #e3e3e3; - color: #2e2e2e; -} - -.gitlab-button-secondary:active { - color: #2e2e2e; - background-color: #e1e1e1; - border-color: #dadada; -} - -.gitlab-button-success:hover { - color: #fff; - background-color: #137e3f; - border-color: #127339; -} - -.gitlab-button-success:active { - background-color: #168f48; - border-color: #12753a; - color: #fff; -} - -.gitlab-button-success { - background-color: #1aaa55; - border: 1px solid #168f48; - color: #fff; -} - -.gitlab-button-wide { - width: 100%; -} - -.gitlab-button-wrapper { - margin-top: 0.5rem; - display: flex; - align-items: baseline; - /* - this makes sure the hit enter to submit picks the correct button - on the comment view - */ - flex-direction: row-reverse; -} - -.gitlab-collapse { - width: 2.4rem; - height: 2.2rem; - margin-left: 1rem; - padding: .5rem; -} - -.gitlab-collapse-closed { - align-self: center; -} - -.gitlab-checkbox-label { - padding: 0 .2rem; -} - -.gitlab-checkbox-wrapper { - display: flex; - align-items: baseline; -} - -.gitlab-form-open { - padding: 1rem; - background-color: #fafafa; -} - -.gitlab-label { - font-weight: 600; - display: inline-block; - width: 100%; -} - -.gitlab-link { - color: #1b69b6; - text-decoration: none; - background-color: transparent; - background-image: none; -} - -.gitlab-link:hover { - text-decoration: underline; -} - -.gitlab-link-button { - border: none; - cursor: pointer; - padding: 0 .15rem; -} - -.gitlab-message { - padding: .25rem 0; - margin: 0; - line-height: 1.2rem; -} - -.gitlab-metadata-note { - font-size: .7rem; - line-height: 1rem; - color: #666; - margin-bottom: .5rem; -} - -.gitlab-input { - width: 100%; - border: 1px solid #dfdfdf; - border-radius: 4px; - padding: .1rem .2rem; - min-height: 2rem; - max-width: 17rem; -} diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index e6e3a440925..499d4918899 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -180,7 +180,7 @@ class Projects::PipelinesController < Projects::ApplicationController else project .all_pipelines - .includes(user: :status) + .includes(builds: :tags, user: :status) .find_by!(id: params[:id]) .present(current_user: current_user) end diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index f2b5b82b013..144df676304 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -105,14 +105,13 @@ module CiStatusHelper path = pipelines_project_commit_path(project, commit, ref: ref) render_status_with_link( - 'commit', commit.status(ref), path, tooltip_placement: tooltip_placement, icon_size: 24) end - def render_status_with_link(type, status, path = nil, tooltip_placement: 'left', cssclass: '', container: 'body', icon_size: 16) + def render_status_with_link(status, path = nil, type: _('pipeline'), tooltip_placement: 'left', cssclass: '', container: 'body', icon_size: 16) klass = "ci-status-link ci-status-icon-#{status.dasherize} d-inline-flex #{cssclass}" title = "#{type.titleize}: #{ci_label_for_status(status)}" data = { toggle: 'tooltip', placement: tooltip_placement, container: container } diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb index 5678304ffcf..8855e0cdd70 100644 --- a/app/helpers/notifications_helper.rb +++ b/app/helpers/notifications_helper.rb @@ -106,9 +106,9 @@ module NotificationsHelper end end - def notification_setting_icon(notification_setting) + def notification_setting_icon(notification_setting = nil) sprite_icon( - notification_setting.disabled? ? "notifications-off" : "notifications", + !notification_setting.present? || notification_setting.disabled? ? "notifications-off" : "notifications", css_class: "icon notifications-icon js-notifications-icon" ) end diff --git a/app/models/analytics/cycle_analytics/project_stage.rb b/app/models/analytics/cycle_analytics/project_stage.rb index 88c8cb40ccb..a312bd24e78 100644 --- a/app/models/analytics/cycle_analytics/project_stage.rb +++ b/app/models/analytics/cycle_analytics/project_stage.rb @@ -3,7 +3,12 @@ module Analytics module CycleAnalytics class ProjectStage < ApplicationRecord + include Analytics::CycleAnalytics::Stage + + validates :project, presence: true belongs_to :project + + alias_attribute :parent, :project end end end diff --git a/app/models/concerns/analytics/cycle_analytics/stage.rb b/app/models/concerns/analytics/cycle_analytics/stage.rb new file mode 100644 index 00000000000..0c603c2d5e6 --- /dev/null +++ b/app/models/concerns/analytics/cycle_analytics/stage.rb @@ -0,0 +1,68 @@ +# frozen_string_literal: true + +module Analytics + module CycleAnalytics + module Stage + extend ActiveSupport::Concern + + included do + validates :name, presence: true + validates :start_event_identifier, presence: true + validates :end_event_identifier, presence: true + validate :validate_stage_event_pairs + + enum start_event_identifier: Gitlab::Analytics::CycleAnalytics::StageEvents.to_enum, _prefix: :start_event_identifier + enum end_event_identifier: Gitlab::Analytics::CycleAnalytics::StageEvents.to_enum, _prefix: :end_event_identifier + + alias_attribute :custom_stage?, :custom + end + + def parent=(_) + raise NotImplementedError + end + + def parent + raise NotImplementedError + end + + def start_event + Gitlab::Analytics::CycleAnalytics::StageEvents[start_event_identifier].new(params_for_start_event) + end + + def end_event + Gitlab::Analytics::CycleAnalytics::StageEvents[end_event_identifier].new(params_for_end_event) + end + + def params_for_start_event + {} + end + + def params_for_end_event + {} + end + + def default_stage? + !custom + end + + # The model that is going to be queried, Issue or MergeRequest + def subject_model + start_event.object_type + end + + private + + def validate_stage_event_pairs + return if start_event_identifier.nil? || end_event_identifier.nil? + + unless pairing_rules.fetch(start_event.class, []).include?(end_event.class) + errors.add(:end_event, :not_allowed_for_the_given_start_event) + end + end + + def pairing_rules + Gitlab::Analytics::CycleAnalytics::StageEvents.pairing_rules + end + end + end +end diff --git a/app/models/deployment.rb b/app/models/deployment.rb index 68586e7a1fd..bff5d348ca0 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -162,6 +162,14 @@ class Deployment < ApplicationRecord deployed_at&.to_time&.in_time_zone&.to_s(:medium) end + def deployed_by + # We use deployable's user if available because Ci::PlayBuildService + # does not update the deployment's user, just the one for the deployable. + # TODO: use deployment's user once https://gitlab.com/gitlab-org/gitlab-ce/issues/66442 + # is completed. + deployable&.user || user + end + private def ref_path diff --git a/app/serializers/deployment_entity.rb b/app/serializers/deployment_entity.rb index d6466ad1cdd..8b967459173 100644 --- a/app/serializers/deployment_entity.rb +++ b/app/serializers/deployment_entity.rb @@ -21,7 +21,7 @@ class DeploymentEntity < Grape::Entity expose :deployed_at expose :tag expose :last? - expose :user, using: UserEntity + expose :deployed_by, as: :user, using: UserEntity expose :deployable do |deployment, opts| deployment.deployable.yield_self do |deployable| diff --git a/app/services/self_monitoring/project/create_service.rb b/app/services/self_monitoring/project/create_service.rb deleted file mode 100644 index c925c6a1610..00000000000 --- a/app/services/self_monitoring/project/create_service.rb +++ /dev/null @@ -1,219 +0,0 @@ -# frozen_string_literal: true - -module SelfMonitoring - module Project - class CreateService < ::BaseService - include Stepable - include Gitlab::Utils::StrongMemoize - - VISIBILITY_LEVEL = Gitlab::VisibilityLevel::INTERNAL - PROJECT_NAME = 'GitLab Instance Administration' - PROJECT_DESCRIPTION = <<~HEREDOC - This project is automatically generated and will be used to help monitor this GitLab instance. - HEREDOC - - GROUP_NAME = 'GitLab Instance Administrators' - GROUP_PATH = 'gitlab-instance-administrators' - - steps :validate_admins, - :create_group, - :create_project, - :save_project_id, - :add_group_members, - :add_to_whitelist, - :add_prometheus_manual_configuration - - def initialize - super(nil) - end - - def execute - execute_steps - end - - private - - def validate_admins - unless instance_admins.any? - log_error('No active admin user found') - return error('No active admin user found') - end - - success - end - - def create_group - if project_created? - log_info(_('Instance administrators group already exists')) - @group = application_settings.instance_administration_project.owner - return success(group: @group) - end - - admin_user = group_owner - @group = ::Groups::CreateService.new(admin_user, create_group_params).execute - - if @group.persisted? - success(group: @group) - else - error('Could not create group') - end - end - - def create_project - if project_created? - log_info(_('Instance administration project already exists')) - @project = application_settings.instance_administration_project - return success(project: project) - end - - admin_user = group_owner - @project = ::Projects::CreateService.new(admin_user, create_project_params).execute - - if project.persisted? - success(project: project) - else - log_error(_("Could not create instance administration project. Errors: %{errors}") % { errors: project.errors.full_messages }) - error(_('Could not create project')) - end - end - - def save_project_id - return success if project_created? - - result = ApplicationSettings::UpdateService.new( - application_settings, - group_owner, - { instance_administration_project_id: @project.id } - ).execute - - if result - success - else - log_error(_("Could not save instance administration project ID, errors: %{errors}") % { errors: application_settings.errors.full_messages }) - error(_('Could not save project ID')) - end - end - - def add_group_members - members = @group.add_users(group_maintainers, Gitlab::Access::MAINTAINER) - errors = members.flat_map { |member| member.errors.full_messages } - - if errors.any? - log_error("Could not add admins as members to self-monitoring project. Errors: #{errors}") - error('Could not add admins as members') - else - success - end - end - - def add_to_whitelist - return success unless prometheus_enabled? - return success unless prometheus_listen_address.present? - - uri = parse_url(internal_prometheus_listen_address_uri) - return error(_('Prometheus listen_address is not a valid URI')) unless uri - - result = ApplicationSettings::UpdateService.new( - application_settings, - group_owner, - add_to_outbound_local_requests_whitelist: [uri.normalized_host] - ).execute - - if result - success - else - log_error(_("Could not add prometheus URL to whitelist, errors: %{errors}") % { errors: application_settings.errors.full_messages }) - error(_('Could not add prometheus URL to whitelist')) - end - end - - def add_prometheus_manual_configuration - return success unless prometheus_enabled? - return success unless prometheus_listen_address.present? - - service = project.find_or_initialize_service('prometheus') - - unless service.update(prometheus_service_attributes) - log_error("Could not save prometheus manual configuration for self-monitoring project. Errors: #{service.errors.full_messages}") - return error('Could not save prometheus manual configuration') - end - - success - end - - def application_settings - strong_memoize(:application_settings) do - Gitlab::CurrentSettings.expire_current_application_settings - Gitlab::CurrentSettings.current_application_settings - end - end - - def project_created? - application_settings.instance_administration_project.present? - end - - def parse_url(uri_string) - Addressable::URI.parse(uri_string) - rescue Addressable::URI::InvalidURIError, TypeError - end - - def prometheus_enabled? - Gitlab.config.prometheus.enable - rescue Settingslogic::MissingSetting - false - end - - def prometheus_listen_address - Gitlab.config.prometheus.listen_address - rescue Settingslogic::MissingSetting - end - - def instance_admins - @instance_admins ||= User.admins.active - end - - def group_owner - instance_admins.first - end - - def group_maintainers - # Exclude the first so that the group_owner is not added again as a member. - instance_admins - [group_owner] - end - - def create_group_params - { - name: GROUP_NAME, - path: "#{GROUP_PATH}-#{SecureRandom.hex(4)}", - visibility_level: VISIBILITY_LEVEL - } - end - - def create_project_params - { - initialize_with_readme: true, - visibility_level: VISIBILITY_LEVEL, - name: PROJECT_NAME, - description: PROJECT_DESCRIPTION, - namespace_id: @group.id - } - end - - def internal_prometheus_listen_address_uri - if prometheus_listen_address.starts_with?('http') - prometheus_listen_address - else - 'http://' + prometheus_listen_address - end - end - - def prometheus_service_attributes - { - api_url: internal_prometheus_listen_address_uri, - manual_configuration: true, - active: true - } - end - end - end -end diff --git a/app/views/ci/status/_icon.html.haml b/app/views/ci/status/_icon.html.haml index 1249b98221f..fdaacb732c7 100644 --- a/app/views/ci/status/_icon.html.haml +++ b/app/views/ci/status/_icon.html.haml @@ -1,13 +1,10 @@ - status = local_assigns.fetch(:status) - size = local_assigns.fetch(:size, 16) -- type = local_assigns.fetch(:type, 'pipeline') - tooltip_placement = local_assigns.fetch(:tooltip_placement, "left") - path = local_assigns.fetch(:path, status.has_details? ? status.details_path : nil) - option_css_classes = local_assigns.fetch(:option_css_classes, '') - css_classes = "ci-status-link ci-status-icon ci-status-icon-#{status.group} has-tooltip #{option_css_classes}" - title = s_("PipelineStatusTooltip|Pipeline: %{ci_status}") % {ci_status: status.label} -- if type == 'commit' - - title = s_("PipelineStatusTooltip|Commit: %{ci_status}") % {ci_status: status.label} - if path = link_to path, class: css_classes, title: title, data: { placement: tooltip_placement } do diff --git a/app/views/projects/deployments/_deployment.html.haml b/app/views/projects/deployments/_deployment.html.haml index a11e23b6daa..752be02443c 100644 --- a/app/views/projects/deployments/_deployment.html.haml +++ b/app/views/projects/deployments/_deployment.html.haml @@ -15,10 +15,10 @@ .flex-truncate-child = link_to [@project.namespace.becomes(Namespace), @project, deployment.deployable], class: 'build-link' do #{deployment.deployable.name} (##{deployment.deployable.id}) - - if deployment.user + - if deployment.deployed_by %div by - = user_avatar(user: deployment.user, size: 20, css_class: "mr-0 float-none") + = user_avatar(user: deployment.deployed_by, size: 20, css_class: "mr-0 float-none") .table-section.section-15{ role: 'gridcell' } .table-mobile-header{ role: 'rowheader' }= _("Created") diff --git a/app/views/projects/serverless/functions/index.html.haml b/app/views/projects/serverless/functions/index.html.haml index 9c69aedfbfc..bac6c76684b 100644 --- a/app/views/projects/serverless/functions/index.html.haml +++ b/app/views/projects/serverless/functions/index.html.haml @@ -14,5 +14,5 @@ .js-serverless-functions-notice .flash-container - .top-area.adjust + .top-area.adjust.d-flex.justify-content-center .serverless-functions-table#js-serverless-functions diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index aea09bf8d4d..837707707a9 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -139,7 +139,9 @@ - if signed_in - if issuable_sidebar[:project_emails_disabled] .block.js-emails-disabled - = notification_description(:owner_disabled) + .sidebar-collapsed-icon.has-tooltip{ title: notification_description(:owner_disabled), data: { placement: "left", container: "body", boundary: 'viewport' } } + = notification_setting_icon + .hide-collapsed= notification_description(:owner_disabled) - else .js-sidebar-subscriptions-entry-point diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index b7474d891dc..573ed36d7f4 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -89,7 +89,7 @@ - if pipeline_status && can?(current_user, :read_cross_project) && project.pipeline_status.has_status? && can?(current_user, :read_build, project) - pipeline_path = pipelines_project_commit_path(project.pipeline_status.project, project.pipeline_status.sha, ref: project.pipeline_status.ref) %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 + = render 'ci/status/icon', status: project.commit.last_pipeline.detailed_status(current_user), tooltip_placement: 'top', path: pipeline_path .updated-note %span = _('Updated') diff --git a/changelogs/unreleased/55360-redundant-index-in-the-releases-table_v2.yml b/changelogs/unreleased/55360-redundant-index-in-the-releases-table_v2.yml new file mode 100644 index 00000000000..91a1fb5e6c9 --- /dev/null +++ b/changelogs/unreleased/55360-redundant-index-in-the-releases-table_v2.yml @@ -0,0 +1,5 @@ +--- +title: Removed redundant index on releases table +merge_request: 31487 +author: +type: removed diff --git a/changelogs/unreleased/55999-misleading-pipeline-tooltip-messages-and-misleading-ci-status-icons.yml b/changelogs/unreleased/55999-misleading-pipeline-tooltip-messages-and-misleading-ci-status-icons.yml new file mode 100644 index 00000000000..a937614be38 --- /dev/null +++ b/changelogs/unreleased/55999-misleading-pipeline-tooltip-messages-and-misleading-ci-status-icons.yml @@ -0,0 +1,5 @@ +--- +title: Remove "Commit" from pipeline status tooltips +merge_request: 31861 +author: +type: fixed diff --git a/changelogs/unreleased/56883-migration.yml b/changelogs/unreleased/56883-migration.yml new file mode 100644 index 00000000000..d6eb49e62e1 --- /dev/null +++ b/changelogs/unreleased/56883-migration.yml @@ -0,0 +1,5 @@ +--- +title: Create a project for self-monitoring the GitLab instance +merge_request: 31389 +author: +type: added diff --git a/changelogs/unreleased/64269-pipeline-api-fails-with-401.yml b/changelogs/unreleased/64269-pipeline-api-fails-with-401.yml new file mode 100644 index 00000000000..582339901ae --- /dev/null +++ b/changelogs/unreleased/64269-pipeline-api-fails-with-401.yml @@ -0,0 +1,5 @@ +--- +title: Read pipelines from public projects through API without an access token +merge_request: 31816 +author: +type: fixed diff --git a/changelogs/unreleased/64764-fix-serverless-layout.yml b/changelogs/unreleased/64764-fix-serverless-layout.yml new file mode 100644 index 00000000000..9717062df93 --- /dev/null +++ b/changelogs/unreleased/64764-fix-serverless-layout.yml @@ -0,0 +1,5 @@ +--- +title: Fix serverless entry page layout +merge_request: 32029 +author: +type: fixed diff --git a/changelogs/unreleased/66037-deployment-user.yml b/changelogs/unreleased/66037-deployment-user.yml new file mode 100644 index 00000000000..8a61b8145af --- /dev/null +++ b/changelogs/unreleased/66037-deployment-user.yml @@ -0,0 +1,5 @@ +--- +title: Return correct user for manual deployments +merge_request: 32004 +author: +type: fixed diff --git a/changelogs/unreleased/66073-use-time-series-chart-instead-of-area-chart-in-panel_types.yml b/changelogs/unreleased/66073-use-time-series-chart-instead-of-area-chart-in-panel_types.yml new file mode 100644 index 00000000000..8d7af96a7d8 --- /dev/null +++ b/changelogs/unreleased/66073-use-time-series-chart-instead-of-area-chart-in-panel_types.yml @@ -0,0 +1,5 @@ +--- +title: Enable line charts in dashbaord panels and embedded charts +merge_request: 31920 +author: +type: added diff --git a/changelogs/unreleased/66402-use-visual-review-tools-npm-package.yml b/changelogs/unreleased/66402-use-visual-review-tools-npm-package.yml new file mode 100644 index 00000000000..46773e12002 --- /dev/null +++ b/changelogs/unreleased/66402-use-visual-review-tools-npm-package.yml @@ -0,0 +1,5 @@ +--- +title: Move visual review toolbar code to NPM +merge_request: 32159 +author: +type: fixed diff --git a/changelogs/unreleased/fe-fix-issuable-sidebar-icon-of-notification-disabled.yml b/changelogs/unreleased/fe-fix-issuable-sidebar-icon-of-notification-disabled.yml new file mode 100644 index 00000000000..736e12ff694 --- /dev/null +++ b/changelogs/unreleased/fe-fix-issuable-sidebar-icon-of-notification-disabled.yml @@ -0,0 +1,5 @@ +--- +title: Fix issuable sidebar icon on notification disabled +merge_request: 32134 +author: +type: fixed diff --git a/config/webpack.config.js b/config/webpack.config.js index 442b4b4c21e..969a84e85dd 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -298,6 +298,13 @@ module.exports = { from: path.join(ROOT_PATH, 'node_modules/pdfjs-dist/cmaps/'), to: path.join(ROOT_PATH, 'public/assets/webpack/cmaps/'), }, + { + from: path.join( + ROOT_PATH, + 'node_modules/@gitlab/visual-review-tools/dist/visual_review_toolbar.js', + ), + to: path.join(ROOT_PATH, 'public/assets/webpack'), + }, ]), // compression can require a lot of compute time and is disabled in CI diff --git a/config/webpack.config.review_toolbar.js b/config/webpack.config.review_toolbar.js deleted file mode 100644 index baaba7ed387..00000000000 --- a/config/webpack.config.review_toolbar.js +++ /dev/null @@ -1,58 +0,0 @@ -const path = require('path'); -const CompressionPlugin = require('compression-webpack-plugin'); - -const ROOT_PATH = path.resolve(__dirname, '..'); -const CACHE_PATH = process.env.WEBPACK_CACHE_PATH || path.join(ROOT_PATH, 'tmp/cache'); -const NO_SOURCEMAPS = process.env.NO_SOURCEMAPS; -const IS_PRODUCTION = process.env.NODE_ENV === 'production'; - -const devtool = IS_PRODUCTION ? 'source-map' : 'cheap-module-eval-source-map'; - -const alias = { - vendor: path.join(ROOT_PATH, 'vendor/assets/javascripts'), - spec: path.join(ROOT_PATH, 'spec/javascripts'), -}; - -module.exports = { - mode: IS_PRODUCTION ? 'production' : 'development', - - context: path.join(ROOT_PATH, 'app/assets/javascripts'), - - name: 'visual_review_toolbar', - - entry: './visual_review_toolbar', - - output: { - path: path.join(ROOT_PATH, 'public/assets/webpack'), - filename: 'visual_review_toolbar.js', - library: 'VisualReviewToolbar', - libraryTarget: 'var', - }, - - resolve: { - alias, - }, - - module: { - rules: [ - { - test: /\.js$/, - loader: 'babel-loader', - options: { - cacheDirectory: path.join(CACHE_PATH, 'babel-loader'), - }, - }, - { - test: /\.css$/, - use: ['style-loader', 'css-loader'], - }, - ], - }, - - plugins: [ - // compression can require a lot of compute time and is disabled in CI - new CompressionPlugin(), - ].filter(Boolean), - - devtool: NO_SOURCEMAPS ? false : devtool, -}; diff --git a/db/migrate/20171230123729_init_schema.rb b/db/migrate/20171230123729_init_schema.rb index fa90b37954f..a474ea2f925 100644 --- a/db/migrate/20171230123729_init_schema.rb +++ b/db/migrate/20171230123729_init_schema.rb @@ -5,6 +5,7 @@ # rubocop:disable Metrics/AbcSize # rubocop:disable Migration/AddConcurrentForeignKey # rubocop:disable Style/WordArray +# rubocop:disable Migration/AddLimitToStringColumns class InitSchema < ActiveRecord::Migration[4.2] DOWNTIME = false @@ -1852,3 +1853,4 @@ class InitSchema < ActiveRecord::Migration[4.2] raise ActiveRecord::IrreversibleMigration, "The initial migration is not revertable" end end +# rubocop:enable Migration/AddLimitToStringColumns diff --git a/db/migrate/20180101160629_create_prometheus_metrics.rb b/db/migrate/20180101160629_create_prometheus_metrics.rb index e3b1ed710d6..a098b999a0a 100644 --- a/db/migrate/20180101160629_create_prometheus_metrics.rb +++ b/db/migrate/20180101160629_create_prometheus_metrics.rb @@ -4,6 +4,7 @@ class CreatePrometheusMetrics < ActiveRecord::Migration[4.2] DOWNTIME = false def change + # rubocop:disable Migration/AddLimitToStringColumns create_table :prometheus_metrics do |t| t.references :project, index: true, foreign_key: { on_delete: :cascade }, null: false t.string :title, null: false @@ -14,5 +15,6 @@ class CreatePrometheusMetrics < ActiveRecord::Migration[4.2] t.integer :group, null: false, index: true t.timestamps_with_timezone null: false end + # rubocop:enable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20180122162010_add_auto_devops_domain_to_application_settings.rb b/db/migrate/20180122162010_add_auto_devops_domain_to_application_settings.rb index c76dc5b3a68..eb446ad0d72 100644 --- a/db/migrate/20180122162010_add_auto_devops_domain_to_application_settings.rb +++ b/db/migrate/20180122162010_add_auto_devops_domain_to_application_settings.rb @@ -8,6 +8,6 @@ class AddAutoDevopsDomainToApplicationSettings < ActiveRecord::Migration[4.2] DOWNTIME = false def change - add_column :application_settings, :auto_devops_domain, :string + add_column :application_settings, :auto_devops_domain, :string # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20180129193323_add_uploads_builder_context.rb b/db/migrate/20180129193323_add_uploads_builder_context.rb index c7227bf0f1e..710fa7b3ba8 100644 --- a/db/migrate/20180129193323_add_uploads_builder_context.rb +++ b/db/migrate/20180129193323_add_uploads_builder_context.rb @@ -8,7 +8,9 @@ class AddUploadsBuilderContext < ActiveRecord::Migration[4.2] DOWNTIME = false def change + # rubocop:disable Migration/AddLimitToStringColumns add_column :uploads, :mount_point, :string add_column :uploads, :secret, :string + # rubocop:enable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20180212030105_add_external_ip_to_clusters_applications_ingress.rb b/db/migrate/20180212030105_add_external_ip_to_clusters_applications_ingress.rb index e2a9a68b1ad..78aa2014601 100644 --- a/db/migrate/20180212030105_add_external_ip_to_clusters_applications_ingress.rb +++ b/db/migrate/20180212030105_add_external_ip_to_clusters_applications_ingress.rb @@ -4,6 +4,6 @@ class AddExternalIpToClustersApplicationsIngress < ActiveRecord::Migration[4.2] DOWNTIME = false def change - add_column :clusters_applications_ingress, :external_ip, :string + add_column :clusters_applications_ingress, :external_ip, :string # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20180214093516_create_badges.rb b/db/migrate/20180214093516_create_badges.rb index 66e017b115a..fe27d465571 100644 --- a/db/migrate/20180214093516_create_badges.rb +++ b/db/migrate/20180214093516_create_badges.rb @@ -2,6 +2,7 @@ class CreateBadges < ActiveRecord::Migration[4.2] DOWNTIME = false def change + # rubocop:disable Migration/AddLimitToStringColumns create_table :badges do |t| t.string :link_url, null: false t.string :image_url, null: false @@ -11,6 +12,7 @@ class CreateBadges < ActiveRecord::Migration[4.2] t.timestamps_with_timezone null: false end + # rubocop:enable Migration/AddLimitToStringColumns # rubocop:disable Migration/AddConcurrentForeignKey add_foreign_key :badges, :namespaces, column: :group_id, on_delete: :cascade diff --git a/db/migrate/20180214155405_create_clusters_applications_runners.rb b/db/migrate/20180214155405_create_clusters_applications_runners.rb index ce594c91890..f611fefbb0d 100644 --- a/db/migrate/20180214155405_create_clusters_applications_runners.rb +++ b/db/migrate/20180214155405_create_clusters_applications_runners.rb @@ -13,7 +13,7 @@ class CreateClustersApplicationsRunners < ActiveRecord::Migration[4.2] t.index :cluster_id, unique: true t.integer :status, null: false t.timestamps_with_timezone null: false - t.string :version, null: false + t.string :version, null: false # rubocop:disable Migration/AddLimitToStringColumns t.text :status_reason end diff --git a/db/migrate/20180216120000_add_pages_domain_verification.rb b/db/migrate/20180216120000_add_pages_domain_verification.rb index f709f5a5809..b2f19f2e1a9 100644 --- a/db/migrate/20180216120000_add_pages_domain_verification.rb +++ b/db/migrate/20180216120000_add_pages_domain_verification.rb @@ -3,6 +3,6 @@ class AddPagesDomainVerification < ActiveRecord::Migration[4.2] def change add_column :pages_domains, :verified_at, :datetime_with_timezone - add_column :pages_domains, :verification_code, :string + add_column :pages_domains, :verification_code, :string # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20180222043024_add_ip_address_to_runner.rb b/db/migrate/20180222043024_add_ip_address_to_runner.rb index b52366c0be1..08fb0bd900c 100644 --- a/db/migrate/20180222043024_add_ip_address_to_runner.rb +++ b/db/migrate/20180222043024_add_ip_address_to_runner.rb @@ -4,6 +4,6 @@ class AddIpAddressToRunner < ActiveRecord::Migration[4.2] DOWNTIME = false def change - add_column :ci_runners, :ip_address, :string + add_column :ci_runners, :ip_address, :string # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20180308125206_add_user_internal_regex_to_application_setting.rb b/db/migrate/20180308125206_add_user_internal_regex_to_application_setting.rb index 5e4bf96f86f..9bdd44baf58 100644 --- a/db/migrate/20180308125206_add_user_internal_regex_to_application_setting.rb +++ b/db/migrate/20180308125206_add_user_internal_regex_to_application_setting.rb @@ -4,7 +4,7 @@ class AddUserInternalRegexToApplicationSetting < ActiveRecord::Migration[4.2] DOWNTIME = false def up - add_column :application_settings, :user_default_internal_regex, :string, null: true + add_column :application_settings, :user_default_internal_regex, :string, null: true # rubocop:disable Migration/AddLimitToStringColumns end def down diff --git a/db/migrate/20180315160435_add_external_auth_mutual_tls_fields_to_project_settings.rb b/db/migrate/20180315160435_add_external_auth_mutual_tls_fields_to_project_settings.rb index ee3d1078f5e..c379d207ff0 100644 --- a/db/migrate/20180315160435_add_external_auth_mutual_tls_fields_to_project_settings.rb +++ b/db/migrate/20180315160435_add_external_auth_mutual_tls_fields_to_project_settings.rb @@ -2,6 +2,7 @@ class AddExternalAuthMutualTlsFieldsToProjectSettings < ActiveRecord::Migration[ DOWNTIME = false def change + # rubocop:disable Migration/AddLimitToStringColumns add_column :application_settings, :external_auth_client_cert, :text add_column :application_settings, @@ -12,5 +13,6 @@ class AddExternalAuthMutualTlsFieldsToProjectSettings < ActiveRecord::Migration[ :encrypted_external_auth_client_key_pass, :string add_column :application_settings, :encrypted_external_auth_client_key_pass_iv, :string + # rubocop:enable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20180319190020_create_deploy_tokens.rb b/db/migrate/20180319190020_create_deploy_tokens.rb index a4d797679c5..f444521b3ae 100644 --- a/db/migrate/20180319190020_create_deploy_tokens.rb +++ b/db/migrate/20180319190020_create_deploy_tokens.rb @@ -2,6 +2,7 @@ class CreateDeployTokens < ActiveRecord::Migration[4.2] DOWNTIME = false def change + # rubocop:disable Migration/AddLimitToStringColumns create_table :deploy_tokens do |t| t.boolean :revoked, default: false t.boolean :read_repository, null: false, default: false @@ -15,5 +16,6 @@ class CreateDeployTokens < ActiveRecord::Migration[4.2] t.index [:token, :expires_at, :id], where: "(revoked IS FALSE)" end + # rubocop:enable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20180502122856_create_project_mirror_data.rb b/db/migrate/20180502122856_create_project_mirror_data.rb index 9781815a97b..04367e1c98b 100644 --- a/db/migrate/20180502122856_create_project_mirror_data.rb +++ b/db/migrate/20180502122856_create_project_mirror_data.rb @@ -3,6 +3,7 @@ class CreateProjectMirrorData < ActiveRecord::Migration[4.2] DOWNTIME = false + # rubocop:disable Migration/AddLimitToStringColumns def up if table_exists?(:project_mirror_data) add_column :project_mirror_data, :status, :string unless column_exists?(:project_mirror_data, :status) @@ -17,6 +18,7 @@ class CreateProjectMirrorData < ActiveRecord::Migration[4.2] end end end + # rubocop:enable Migration/AddLimitToStringColumns def down remove_column :project_mirror_data, :status diff --git a/db/migrate/20180503131624_create_remote_mirrors.rb b/db/migrate/20180503131624_create_remote_mirrors.rb index 288ae365f0f..a079c1b3328 100644 --- a/db/migrate/20180503131624_create_remote_mirrors.rb +++ b/db/migrate/20180503131624_create_remote_mirrors.rb @@ -5,6 +5,7 @@ class CreateRemoteMirrors < ActiveRecord::Migration[4.2] disable_ddl_transaction! + # rubocop:disable Migration/AddLimitToStringColumns def up return if table_exists?(:remote_mirrors) @@ -27,6 +28,7 @@ class CreateRemoteMirrors < ActiveRecord::Migration[4.2] t.timestamps null: false end end + # rubocop:enable Migration/AddLimitToStringColumns def down # ee/db/migrate/20160321161032_create_remote_mirrors_ee.rb will remove the table diff --git a/db/migrate/20180503175053_ensure_missing_columns_to_project_mirror_data.rb b/db/migrate/20180503175053_ensure_missing_columns_to_project_mirror_data.rb index 3775b3a08c9..f00493ed515 100644 --- a/db/migrate/20180503175053_ensure_missing_columns_to_project_mirror_data.rb +++ b/db/migrate/20180503175053_ensure_missing_columns_to_project_mirror_data.rb @@ -4,8 +4,8 @@ class EnsureMissingColumnsToProjectMirrorData < ActiveRecord::Migration[4.2] DOWNTIME = false def up - add_column :project_mirror_data, :status, :string unless column_exists?(:project_mirror_data, :status) - add_column :project_mirror_data, :jid, :string unless column_exists?(:project_mirror_data, :jid) + add_column :project_mirror_data, :status, :string unless column_exists?(:project_mirror_data, :status) # rubocop:disable Migration/AddLimitToStringColumns + add_column :project_mirror_data, :jid, :string unless column_exists?(:project_mirror_data, :jid) # rubocop:disable Migration/AddLimitToStringColumns add_column :project_mirror_data, :last_error, :text unless column_exists?(:project_mirror_data, :last_error) end diff --git a/db/migrate/20180511131058_create_clusters_applications_jupyter.rb b/db/migrate/20180511131058_create_clusters_applications_jupyter.rb index 749aeeb4792..4633d930e2d 100644 --- a/db/migrate/20180511131058_create_clusters_applications_jupyter.rb +++ b/db/migrate/20180511131058_create_clusters_applications_jupyter.rb @@ -7,17 +7,19 @@ class CreateClustersApplicationsJupyter < ActiveRecord::Migration[4.2] DOWNTIME = false def change + # rubocop:disable Migration/AddLimitToStringColumns create_table :clusters_applications_jupyter do |t| t.references :cluster, null: false, unique: true, foreign_key: { on_delete: :cascade } t.references :oauth_application, foreign_key: { on_delete: :nullify } t.integer :status, null: false - t.string :version, null: false - t.string :hostname + t.string :version, null: false # rubocop:disable Migration/AddLimitToStringColumns + t.string :hostname # rubocop:disable Migration/AddLimitToStringColumns t.timestamps_with_timezone null: false t.text :status_reason end + # rubocop:enable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20180515121227_create_notes_diff_files.rb b/db/migrate/20180515121227_create_notes_diff_files.rb index e50324d8599..5f6dba11ff9 100644 --- a/db/migrate/20180515121227_create_notes_diff_files.rb +++ b/db/migrate/20180515121227_create_notes_diff_files.rb @@ -4,6 +4,7 @@ class CreateNotesDiffFiles < ActiveRecord::Migration[4.2] disable_ddl_transaction! def change + # rubocop:disable Migration/AddLimitToStringColumns create_table :note_diff_files do |t| t.references :diff_note, references: :notes, null: false, index: { unique: true } t.text :diff, null: false @@ -18,5 +19,6 @@ class CreateNotesDiffFiles < ActiveRecord::Migration[4.2] # rubocop:disable Migration/AddConcurrentForeignKey add_foreign_key :note_diff_files, :notes, column: :diff_note_id, on_delete: :cascade + # rubocop:enable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20180529093006_ensure_remote_mirror_columns.rb b/db/migrate/20180529093006_ensure_remote_mirror_columns.rb index 207e1f089fb..a0a1150f022 100644 --- a/db/migrate/20180529093006_ensure_remote_mirror_columns.rb +++ b/db/migrate/20180529093006_ensure_remote_mirror_columns.rb @@ -8,7 +8,7 @@ class EnsureRemoteMirrorColumns < ActiveRecord::Migration[4.2] def up # rubocop:disable Migration/Datetime add_column :remote_mirrors, :last_update_started_at, :datetime unless column_exists?(:remote_mirrors, :last_update_started_at) - add_column :remote_mirrors, :remote_name, :string unless column_exists?(:remote_mirrors, :remote_name) + add_column :remote_mirrors, :remote_name, :string unless column_exists?(:remote_mirrors, :remote_name) # rubocop:disable Migration/AddLimitToStringColumns unless column_exists?(:remote_mirrors, :only_protected_branches) add_column_with_default(:remote_mirrors, diff --git a/db/migrate/20180531185349_add_repository_languages.rb b/db/migrate/20180531185349_add_repository_languages.rb index 26a01c3bb26..d517c21c26c 100644 --- a/db/migrate/20180531185349_add_repository_languages.rb +++ b/db/migrate/20180531185349_add_repository_languages.rb @@ -4,6 +4,7 @@ class AddRepositoryLanguages < ActiveRecord::Migration[4.2] DOWNTIME = false def up + # rubocop:disable Migration/AddLimitToStringColumns create_table(:programming_languages) do |t| t.string :name, null: false t.string :color, null: false @@ -19,6 +20,7 @@ class AddRepositoryLanguages < ActiveRecord::Migration[4.2] add_index :programming_languages, :name, unique: true add_index :repository_languages, [:project_id, :programming_language_id], unique: true, name: "index_repository_languages_on_project_and_languages_id" + # rubocop:enable Migration/AddLimitToStringColumns end def down diff --git a/db/migrate/20180613081317_create_ci_builds_runner_session.rb b/db/migrate/20180613081317_create_ci_builds_runner_session.rb index eb41f76b105..68af38834d2 100644 --- a/db/migrate/20180613081317_create_ci_builds_runner_session.rb +++ b/db/migrate/20180613081317_create_ci_builds_runner_session.rb @@ -8,6 +8,7 @@ class CreateCiBuildsRunnerSession < ActiveRecord::Migration[4.2] DOWNTIME = false def change + # rubocop:disable Migration/AddLimitToStringColumns create_table :ci_builds_runner_session, id: :bigserial do |t| t.integer :build_id, null: false t.string :url, null: false @@ -17,5 +18,6 @@ class CreateCiBuildsRunnerSession < ActiveRecord::Migration[4.2] t.foreign_key :ci_builds, column: :build_id, on_delete: :cascade t.index :build_id, unique: true end + # rubocop:enable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20180713092803_create_user_statuses.rb b/db/migrate/20180713092803_create_user_statuses.rb index 43b96805c1e..3abab4e45a9 100644 --- a/db/migrate/20180713092803_create_user_statuses.rb +++ b/db/migrate/20180713092803_create_user_statuses.rb @@ -6,6 +6,7 @@ class CreateUserStatuses < ActiveRecord::Migration[4.2] DOWNTIME = false def change + # rubocop:disable Migration/AddLimitToStringColumns create_table :user_statuses, id: false, primary_key: :user_id do |t| t.references :user, foreign_key: { on_delete: :cascade }, @@ -16,5 +17,6 @@ class CreateUserStatuses < ActiveRecord::Migration[4.2] t.string :message, limit: 100 t.string :message_html end + # rubocop:enable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20180814153625_add_commit_email_to_users.rb b/db/migrate/20180814153625_add_commit_email_to_users.rb index 4d9217ea504..98bafc14a03 100644 --- a/db/migrate/20180814153625_add_commit_email_to_users.rb +++ b/db/migrate/20180814153625_add_commit_email_to_users.rb @@ -28,6 +28,6 @@ class AddCommitEmailToUsers < ActiveRecord::Migration[4.2] # disable_ddl_transaction! def change - add_column :users, :commit_email, :string + add_column :users, :commit_email, :string # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20180831164908_add_identifier_to_prometheus_metric.rb b/db/migrate/20180831164908_add_identifier_to_prometheus_metric.rb index 7aa5950249c..8f30363c310 100644 --- a/db/migrate/20180831164908_add_identifier_to_prometheus_metric.rb +++ b/db/migrate/20180831164908_add_identifier_to_prometheus_metric.rb @@ -6,6 +6,6 @@ class AddIdentifierToPrometheusMetric < ActiveRecord::Migration[4.2] DOWNTIME = false def change - add_column :prometheus_metrics, :identifier, :string + add_column :prometheus_metrics, :identifier, :string # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20180910115836_add_attr_encrypted_columns_to_web_hook.rb b/db/migrate/20180910115836_add_attr_encrypted_columns_to_web_hook.rb index ca8dbdba2bb..9757f7fdc79 100644 --- a/db/migrate/20180910115836_add_attr_encrypted_columns_to_web_hook.rb +++ b/db/migrate/20180910115836_add_attr_encrypted_columns_to_web_hook.rb @@ -6,10 +6,12 @@ class AddAttrEncryptedColumnsToWebHook < ActiveRecord::Migration[4.2] DOWNTIME = false def change + # rubocop:disable Migration/AddLimitToStringColumns add_column :web_hooks, :encrypted_token, :string add_column :web_hooks, :encrypted_token_iv, :string add_column :web_hooks, :encrypted_url, :string add_column :web_hooks, :encrypted_url_iv, :string + # rubocop:enable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20180910153412_add_token_digest_to_personal_access_tokens.rb b/db/migrate/20180910153412_add_token_digest_to_personal_access_tokens.rb index 142e454832f..52923f52499 100644 --- a/db/migrate/20180910153412_add_token_digest_to_personal_access_tokens.rb +++ b/db/migrate/20180910153412_add_token_digest_to_personal_access_tokens.rb @@ -8,7 +8,7 @@ class AddTokenDigestToPersonalAccessTokens < ActiveRecord::Migration[4.2] def up change_column :personal_access_tokens, :token, :string, null: true - add_column :personal_access_tokens, :token_digest, :string + add_column :personal_access_tokens, :token_digest, :string # rubocop:disable Migration/AddLimitToStringColumns end def down diff --git a/db/migrate/20180912111628_add_knative_application.rb b/db/migrate/20180912111628_add_knative_application.rb index 86d9100d2e7..7c55de02d1c 100644 --- a/db/migrate/20180912111628_add_knative_application.rb +++ b/db/migrate/20180912111628_add_knative_application.rb @@ -6,6 +6,7 @@ class AddKnativeApplication < ActiveRecord::Migration[4.2] DOWNTIME = false def change + # rubocop:disable Migration/AddLimitToStringColumns create_table "clusters_applications_knative" do |t| t.references :cluster, null: false, unique: true, foreign_key: { on_delete: :cascade } @@ -16,5 +17,6 @@ class AddKnativeApplication < ActiveRecord::Migration[4.2] t.string "hostname" t.text "status_reason" end + # rubocop:enable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20181009190428_create_clusters_kubernetes_namespaces.rb b/db/migrate/20181009190428_create_clusters_kubernetes_namespaces.rb index 62ad6c63d0a..b6ffb2866aa 100644 --- a/db/migrate/20181009190428_create_clusters_kubernetes_namespaces.rb +++ b/db/migrate/20181009190428_create_clusters_kubernetes_namespaces.rb @@ -5,6 +5,7 @@ class CreateClustersKubernetesNamespaces < ActiveRecord::Migration[4.2] INDEX_NAME = 'kubernetes_namespaces_cluster_and_namespace' def change + # rubocop:disable Migration/AddLimitToStringColumns create_table :clusters_kubernetes_namespaces, id: :bigserial do |t| t.references :cluster, null: false, index: true, foreign_key: { on_delete: :cascade } t.references :project, index: true, foreign_key: { on_delete: :nullify } @@ -20,5 +21,6 @@ class CreateClustersKubernetesNamespaces < ActiveRecord::Migration[4.2] t.index [:cluster_id, :namespace], name: INDEX_NAME, unique: true end + # rubocop:enable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20181019032400_add_shards_table.rb b/db/migrate/20181019032400_add_shards_table.rb index e31af97cc94..82287e5c3b5 100644 --- a/db/migrate/20181019032400_add_shards_table.rb +++ b/db/migrate/20181019032400_add_shards_table.rb @@ -5,7 +5,7 @@ class AddShardsTable < ActiveRecord::Migration[4.2] def change create_table :shards do |t| - t.string :name, null: false, index: { unique: true } + t.string :name, null: false, index: { unique: true } # rubocop:disable Migration/AddLimitToStringColumns end end end diff --git a/db/migrate/20181019032408_add_repositories_table.rb b/db/migrate/20181019032408_add_repositories_table.rb index 2153c1c9fc6..dd510b44084 100644 --- a/db/migrate/20181019032408_add_repositories_table.rb +++ b/db/migrate/20181019032408_add_repositories_table.rb @@ -6,7 +6,7 @@ class AddRepositoriesTable < ActiveRecord::Migration[4.2] def change create_table :repositories, id: :bigserial do |t| t.references :shard, null: false, index: true, foreign_key: { on_delete: :restrict } - t.string :disk_path, null: false, index: { unique: true } + t.string :disk_path, null: false, index: { unique: true } # rubocop:disable Migration/AddLimitToStringColumns end add_column :projects, :pool_repository_id, :bigint diff --git a/db/migrate/20181025115728_add_private_commit_email_hostname_to_application_settings.rb b/db/migrate/20181025115728_add_private_commit_email_hostname_to_application_settings.rb index 052a344f182..c0e4897b8d7 100644 --- a/db/migrate/20181025115728_add_private_commit_email_hostname_to_application_settings.rb +++ b/db/migrate/20181025115728_add_private_commit_email_hostname_to_application_settings.rb @@ -6,6 +6,6 @@ class AddPrivateCommitEmailHostnameToApplicationSettings < ActiveRecord::Migrati DOWNTIME = false def change - add_column(:application_settings, :commit_email_hostname, :string, null: true) + add_column(:application_settings, :commit_email_hostname, :string, null: true) # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20181031190559_drop_gcp_clusters_table.rb b/db/migrate/20181031190559_drop_gcp_clusters_table.rb index 597fe49f4c8..850fa93c75a 100644 --- a/db/migrate/20181031190559_drop_gcp_clusters_table.rb +++ b/db/migrate/20181031190559_drop_gcp_clusters_table.rb @@ -10,6 +10,7 @@ class DropGcpClustersTable < ActiveRecord::Migration[4.2] end def down + # rubocop:disable Migration/AddLimitToStringColumns create_table :gcp_clusters do |t| # Order columns by best align scheme t.references :project, null: false, index: { unique: true }, foreign_key: { on_delete: :cascade } @@ -49,5 +50,6 @@ class DropGcpClustersTable < ActiveRecord::Migration[4.2] t.text :encrypted_gcp_token t.string :encrypted_gcp_token_iv end + # rubocop:enable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20181101191341_create_clusters_applications_cert_manager.rb b/db/migrate/20181101191341_create_clusters_applications_cert_manager.rb index 0b6155356d9..3bc20046311 100644 --- a/db/migrate/20181101191341_create_clusters_applications_cert_manager.rb +++ b/db/migrate/20181101191341_create_clusters_applications_cert_manager.rb @@ -6,6 +6,7 @@ class CreateClustersApplicationsCertManager < ActiveRecord::Migration[4.2] DOWNTIME = false def change + # rubocop:disable Migration/AddLimitToStringColumns create_table :clusters_applications_cert_managers do |t| t.references :cluster, null: false, index: false, foreign_key: { on_delete: :cascade } t.integer :status, null: false @@ -15,5 +16,6 @@ class CreateClustersApplicationsCertManager < ActiveRecord::Migration[4.2] t.text :status_reason t.index :cluster_id, unique: true end + # rubocop:enable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20181115140140_add_encrypted_runners_token_to_settings.rb b/db/migrate/20181115140140_add_encrypted_runners_token_to_settings.rb index 5b2bb4f6b08..124eedf7933 100644 --- a/db/migrate/20181115140140_add_encrypted_runners_token_to_settings.rb +++ b/db/migrate/20181115140140_add_encrypted_runners_token_to_settings.rb @@ -6,6 +6,6 @@ class AddEncryptedRunnersTokenToSettings < ActiveRecord::Migration[4.2] DOWNTIME = false def change - add_column :application_settings, :runners_registration_token_encrypted, :string + add_column :application_settings, :runners_registration_token_encrypted, :string # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20181116050532_knative_external_ip.rb b/db/migrate/20181116050532_knative_external_ip.rb index 5645b040a23..4634b411108 100644 --- a/db/migrate/20181116050532_knative_external_ip.rb +++ b/db/migrate/20181116050532_knative_external_ip.rb @@ -9,6 +9,6 @@ class KnativeExternalIp < ActiveRecord::Migration[4.2] DOWNTIME = false def change - add_column :clusters_applications_knative, :external_ip, :string + add_column :clusters_applications_knative, :external_ip, :string # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20181116141415_add_encrypted_runners_token_to_namespaces.rb b/db/migrate/20181116141415_add_encrypted_runners_token_to_namespaces.rb index dcf565cd6c0..0a8ed912891 100644 --- a/db/migrate/20181116141415_add_encrypted_runners_token_to_namespaces.rb +++ b/db/migrate/20181116141415_add_encrypted_runners_token_to_namespaces.rb @@ -6,6 +6,6 @@ class AddEncryptedRunnersTokenToNamespaces < ActiveRecord::Migration[4.2] DOWNTIME = false def change - add_column :namespaces, :runners_token_encrypted, :string + add_column :namespaces, :runners_token_encrypted, :string # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20181116141504_add_encrypted_runners_token_to_projects.rb b/db/migrate/20181116141504_add_encrypted_runners_token_to_projects.rb index 13cd80e5c8b..3083dff49b8 100644 --- a/db/migrate/20181116141504_add_encrypted_runners_token_to_projects.rb +++ b/db/migrate/20181116141504_add_encrypted_runners_token_to_projects.rb @@ -6,6 +6,6 @@ class AddEncryptedRunnersTokenToProjects < ActiveRecord::Migration[4.2] DOWNTIME = false def change - add_column :projects, :runners_token_encrypted, :string + add_column :projects, :runners_token_encrypted, :string # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20181120151656_add_token_encrypted_to_ci_runners.rb b/db/migrate/20181120151656_add_token_encrypted_to_ci_runners.rb index 8b990451adc..2270246dfb4 100644 --- a/db/migrate/20181120151656_add_token_encrypted_to_ci_runners.rb +++ b/db/migrate/20181120151656_add_token_encrypted_to_ci_runners.rb @@ -6,6 +6,6 @@ class AddTokenEncryptedToCiRunners < ActiveRecord::Migration[4.2] DOWNTIME = false def change - add_column :ci_runners, :token_encrypted, :string + add_column :ci_runners, :token_encrypted, :string # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20181122160027_create_project_repositories.rb b/db/migrate/20181122160027_create_project_repositories.rb index e42cef9b1c6..3f123daa150 100644 --- a/db/migrate/20181122160027_create_project_repositories.rb +++ b/db/migrate/20181122160027_create_project_repositories.rb @@ -11,7 +11,7 @@ class CreateProjectRepositories < ActiveRecord::Migration[5.0] def change create_table :project_repositories, id: :bigserial do |t| t.references :shard, null: false, index: true, foreign_key: { on_delete: :restrict } - t.string :disk_path, null: false, index: { unique: true } + t.string :disk_path, null: false, index: { unique: true } # rubocop:disable Migration/AddLimitToStringColumns t.references :project, null: false, index: { unique: true }, foreign_key: { on_delete: :cascade } end end diff --git a/db/migrate/20181123144235_create_suggestions.rb b/db/migrate/20181123144235_create_suggestions.rb index 1723f6de7eb..78888517db5 100644 --- a/db/migrate/20181123144235_create_suggestions.rb +++ b/db/migrate/20181123144235_create_suggestions.rb @@ -8,7 +8,7 @@ class CreateSuggestions < ActiveRecord::Migration[5.0] t.references :note, foreign_key: { on_delete: :cascade }, null: false t.integer :relative_order, null: false, limit: 2 t.boolean :applied, null: false, default: false - t.string :commit_id + t.string :commit_id # rubocop:disable Migration/AddLimitToStringColumns t.text :from_content, null: false t.text :to_content, null: false diff --git a/db/migrate/20181128123704_add_state_to_pool_repository.rb b/db/migrate/20181128123704_add_state_to_pool_repository.rb index 714232ede56..04bbcf24f62 100644 --- a/db/migrate/20181128123704_add_state_to_pool_repository.rb +++ b/db/migrate/20181128123704_add_state_to_pool_repository.rb @@ -9,7 +9,7 @@ class AddStateToPoolRepository < ActiveRecord::Migration[5.0] # the transactions don't have to be disabled # rubocop: disable Migration/AddConcurrentForeignKey, Migration/AddIndex def change - add_column(:pool_repositories, :state, :string, null: true) + add_column(:pool_repositories, :state, :string, null: true) # rubocop:disable Migration/AddLimitToStringColumns add_column :pool_repositories, :source_project_id, :integer add_index :pool_repositories, :source_project_id, unique: true diff --git a/db/migrate/20181129104854_add_token_encrypted_to_ci_builds.rb b/db/migrate/20181129104854_add_token_encrypted_to_ci_builds.rb index 11b98203793..62a7421eae0 100644 --- a/db/migrate/20181129104854_add_token_encrypted_to_ci_builds.rb +++ b/db/migrate/20181129104854_add_token_encrypted_to_ci_builds.rb @@ -6,6 +6,6 @@ class AddTokenEncryptedToCiBuilds < ActiveRecord::Migration[5.0] DOWNTIME = false def change - add_column :ci_builds, :token_encrypted, :string + add_column :ci_builds, :token_encrypted, :string # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20181203002526_add_project_bfg_object_map_column.rb b/db/migrate/20181203002526_add_project_bfg_object_map_column.rb index 8b42cd6f941..5e6d416895c 100644 --- a/db/migrate/20181203002526_add_project_bfg_object_map_column.rb +++ b/db/migrate/20181203002526_add_project_bfg_object_map_column.rb @@ -4,6 +4,6 @@ class AddProjectBfgObjectMapColumn < ActiveRecord::Migration[5.0] DOWNTIME = false def change - add_column :projects, :bfg_object_map, :string + add_column :projects, :bfg_object_map, :string # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20181211092510_add_name_author_id_and_sha_to_releases.rb b/db/migrate/20181211092510_add_name_author_id_and_sha_to_releases.rb index 60815e0c31a..3ab808ba667 100644 --- a/db/migrate/20181211092510_add_name_author_id_and_sha_to_releases.rb +++ b/db/migrate/20181211092510_add_name_author_id_and_sha_to_releases.rb @@ -7,7 +7,7 @@ class AddNameAuthorIdAndShaToReleases < ActiveRecord::Migration[5.0] def change add_column :releases, :author_id, :integer - add_column :releases, :name, :string - add_column :releases, :sha, :string + add_column :releases, :name, :string # rubocop:disable Migration/AddLimitToStringColumns + add_column :releases, :sha, :string # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20181212171634_create_error_tracking_settings.rb b/db/migrate/20181212171634_create_error_tracking_settings.rb index 18c38bd2c47..950b9a88005 100644 --- a/db/migrate/20181212171634_create_error_tracking_settings.rb +++ b/db/migrate/20181212171634_create_error_tracking_settings.rb @@ -6,6 +6,7 @@ class CreateErrorTrackingSettings < ActiveRecord::Migration[5.0] DOWNTIME = false def change + # rubocop:disable Migration/AddLimitToStringColumns create_table :project_error_tracking_settings, id: :int, primary_key: :project_id, default: nil do |t| t.boolean :enabled, null: false, default: true t.string :api_url, null: false @@ -13,5 +14,6 @@ class CreateErrorTrackingSettings < ActiveRecord::Migration[5.0] t.string :encrypted_token_iv t.foreign_key :projects, column: :project_id, on_delete: :cascade end + # rubocop:enable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20181228175414_create_releases_link_table.rb b/db/migrate/20181228175414_create_releases_link_table.rb index 03558202137..168c4722cc1 100644 --- a/db/migrate/20181228175414_create_releases_link_table.rb +++ b/db/migrate/20181228175414_create_releases_link_table.rb @@ -6,6 +6,7 @@ class CreateReleasesLinkTable < ActiveRecord::Migration[5.0] DOWNTIME = false def change + # rubocop:disable Migration/AddLimitToStringColumns create_table :release_links, id: :bigserial do |t| t.references :release, null: false, index: false, foreign_key: { on_delete: :cascade } t.string :url, null: false @@ -15,5 +16,6 @@ class CreateReleasesLinkTable < ActiveRecord::Migration[5.0] t.index [:release_id, :url], unique: true t.index [:release_id, :name], unique: true end + # rubocop:enable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20190109153125_add_merge_request_external_diffs.rb b/db/migrate/20190109153125_add_merge_request_external_diffs.rb index c67903c7f67..a680856a3d8 100644 --- a/db/migrate/20190109153125_add_merge_request_external_diffs.rb +++ b/db/migrate/20190109153125_add_merge_request_external_diffs.rb @@ -11,7 +11,7 @@ class AddMergeRequestExternalDiffs < ActiveRecord::Migration[5.0] def change # Allow the merge request diff to store details about an external file - add_column :merge_request_diffs, :external_diff, :string + add_column :merge_request_diffs, :external_diff, :string # rubocop:disable Migration/AddLimitToStringColumns add_column :merge_request_diffs, :external_diff_store, :integer add_column :merge_request_diffs, :stored_externally, :boolean diff --git a/db/migrate/20190114172110_add_domain_to_cluster.rb b/db/migrate/20190114172110_add_domain_to_cluster.rb index 58d7664b8c0..d8f10af9cad 100644 --- a/db/migrate/20190114172110_add_domain_to_cluster.rb +++ b/db/migrate/20190114172110_add_domain_to_cluster.rb @@ -4,6 +4,6 @@ class AddDomainToCluster < ActiveRecord::Migration[5.0] DOWNTIME = false def change - add_column :clusters, :domain, :string + add_column :clusters, :domain, :string # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20190115092821_add_columns_project_error_tracking_settings.rb b/db/migrate/20190115092821_add_columns_project_error_tracking_settings.rb index 190b6f958fd..afed929cce4 100644 --- a/db/migrate/20190115092821_add_columns_project_error_tracking_settings.rb +++ b/db/migrate/20190115092821_add_columns_project_error_tracking_settings.rb @@ -6,8 +6,8 @@ class AddColumnsProjectErrorTrackingSettings < ActiveRecord::Migration[5.0] DOWNTIME = false def change - add_column :project_error_tracking_settings, :project_name, :string - add_column :project_error_tracking_settings, :organization_name, :string + add_column :project_error_tracking_settings, :project_name, :string # rubocop:disable Migration/AddLimitToStringColumns + add_column :project_error_tracking_settings, :organization_name, :string # rubocop:disable Migration/AddLimitToStringColumns change_column_default :project_error_tracking_settings, :enabled, from: true, to: false diff --git a/db/migrate/20190116234221_add_sorting_fields_to_user_preference.rb b/db/migrate/20190116234221_add_sorting_fields_to_user_preference.rb index 7bf581fe9b0..39aab600546 100644 --- a/db/migrate/20190116234221_add_sorting_fields_to_user_preference.rb +++ b/db/migrate/20190116234221_add_sorting_fields_to_user_preference.rb @@ -10,8 +10,8 @@ class AddSortingFieldsToUserPreference < ActiveRecord::Migration[5.0] DOWNTIME = false def up - add_column :user_preferences, :issues_sort, :string - add_column :user_preferences, :merge_requests_sort, :string + add_column :user_preferences, :issues_sort, :string # rubocop:disable Migration/AddLimitToStringColumns + add_column :user_preferences, :merge_requests_sort, :string # rubocop:disable Migration/AddLimitToStringColumns end def down diff --git a/db/migrate/20190301182457_add_external_hostname_to_ingress_and_knative.rb b/db/migrate/20190301182457_add_external_hostname_to_ingress_and_knative.rb index 2c3a54b12a9..37ba1090cf0 100644 --- a/db/migrate/20190301182457_add_external_hostname_to_ingress_and_knative.rb +++ b/db/migrate/20190301182457_add_external_hostname_to_ingress_and_knative.rb @@ -4,7 +4,7 @@ class AddExternalHostnameToIngressAndKnative < ActiveRecord::Migration[5.0] DOWNTIME = false def change - add_column :clusters_applications_ingress, :external_hostname, :string - add_column :clusters_applications_knative, :external_hostname, :string + add_column :clusters_applications_ingress, :external_hostname, :string # rubocop:disable Migration/AddLimitToStringColumns + add_column :clusters_applications_knative, :external_hostname, :string # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20190320174702_add_lets_encrypt_notification_email_to_application_settings.rb b/db/migrate/20190320174702_add_lets_encrypt_notification_email_to_application_settings.rb index e9cf2af84a5..aeabf4e3cb4 100644 --- a/db/migrate/20190320174702_add_lets_encrypt_notification_email_to_application_settings.rb +++ b/db/migrate/20190320174702_add_lets_encrypt_notification_email_to_application_settings.rb @@ -10,6 +10,6 @@ class AddLetsEncryptNotificationEmailToApplicationSettings < ActiveRecord::Migra DOWNTIME = false def change - add_column :application_settings, :lets_encrypt_notification_email, :string + add_column :application_settings, :lets_encrypt_notification_email, :string # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20190325105715_add_fields_to_user_preferences.rb b/db/migrate/20190325105715_add_fields_to_user_preferences.rb index 9ea3b4f9cd8..4da5c496147 100644 --- a/db/migrate/20190325105715_add_fields_to_user_preferences.rb +++ b/db/migrate/20190325105715_add_fields_to_user_preferences.rb @@ -11,7 +11,7 @@ class AddFieldsToUserPreferences < ActiveRecord::Migration[5.0] DOWNTIME = false def up - add_column(:user_preferences, :timezone, :string) + add_column(:user_preferences, :timezone, :string) # rubocop:disable Migration/AddLimitToStringColumns add_column(:user_preferences, :time_display_relative, :boolean) add_column(:user_preferences, :time_format_in_24h, :boolean) end diff --git a/db/migrate/20190327163904_add_notification_email_to_notification_settings.rb b/db/migrate/20190327163904_add_notification_email_to_notification_settings.rb index 2f3069032a1..d912f922510 100644 --- a/db/migrate/20190327163904_add_notification_email_to_notification_settings.rb +++ b/db/migrate/20190327163904_add_notification_email_to_notification_settings.rb @@ -6,6 +6,6 @@ class AddNotificationEmailToNotificationSettings < ActiveRecord::Migration[5.0] DOWNTIME = false def change - add_column :notification_settings, :notification_email, :string + add_column :notification_settings, :notification_email, :string # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20190402150158_backport_enterprise_schema.rb b/db/migrate/20190402150158_backport_enterprise_schema.rb index 8762cc53ed7..3f13b68c2f3 100644 --- a/db/migrate/20190402150158_backport_enterprise_schema.rb +++ b/db/migrate/20190402150158_backport_enterprise_schema.rb @@ -2,6 +2,7 @@ # rubocop: disable Metrics/AbcSize # rubocop: disable Migration/Datetime +# rubocop: disable Migration/AddLimitToStringColumns class BackportEnterpriseSchema < ActiveRecord::Migration[5.0] include Gitlab::Database::MigrationHelpers @@ -2190,3 +2191,4 @@ class BackportEnterpriseSchema < ActiveRecord::Migration[5.0] end # rubocop: enable Metrics/AbcSize # rubocop: enable Migration/Datetime +# rubocop: enable Migration/AddLimitToStringColumns diff --git a/db/migrate/20190409224933_add_name_to_geo_nodes.rb b/db/migrate/20190409224933_add_name_to_geo_nodes.rb index 2dff81b429c..65c01683995 100644 --- a/db/migrate/20190409224933_add_name_to_geo_nodes.rb +++ b/db/migrate/20190409224933_add_name_to_geo_nodes.rb @@ -10,7 +10,7 @@ class AddNameToGeoNodes < ActiveRecord::Migration[5.0] DOWNTIME = false def up - add_column :geo_nodes, :name, :string + add_column :geo_nodes, :name, :string # rubocop:disable Migration/AddLimitToStringColumns # url is also unique, and its type and size is identical to the name column, # so this is safe. diff --git a/db/migrate/20190422082247_create_project_metrics_settings.rb b/db/migrate/20190422082247_create_project_metrics_settings.rb index 3e21dd0a934..a0a2ed64820 100644 --- a/db/migrate/20190422082247_create_project_metrics_settings.rb +++ b/db/migrate/20190422082247_create_project_metrics_settings.rb @@ -7,7 +7,7 @@ class CreateProjectMetricsSettings < ActiveRecord::Migration[5.0] def change create_table :project_metrics_settings, id: :int, primary_key: :project_id, default: nil do |t| - t.string :external_dashboard_url, null: false + t.string :external_dashboard_url, null: false # rubocop:disable Migration/AddLimitToStringColumns t.foreign_key :projects, column: :project_id, on_delete: :cascade end end diff --git a/db/migrate/20190429082448_create_pages_domain_acme_orders.rb b/db/migrate/20190429082448_create_pages_domain_acme_orders.rb index af811e83518..ca1796d054c 100644 --- a/db/migrate/20190429082448_create_pages_domain_acme_orders.rb +++ b/db/migrate/20190429082448_create_pages_domain_acme_orders.rb @@ -10,6 +10,7 @@ class CreatePagesDomainAcmeOrders < ActiveRecord::Migration[5.1] DOWNTIME = false def change + # rubocop:disable Migration/AddLimitToStringColumns create_table :pages_domain_acme_orders do |t| t.references :pages_domain, null: false, index: true, foreign_key: { on_delete: :cascade }, type: :integer @@ -24,5 +25,6 @@ class CreatePagesDomainAcmeOrders < ActiveRecord::Migration[5.1] t.text :encrypted_private_key, null: false t.text :encrypted_private_key_iv, null: false end + # rubocop:enable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20190430131225_create_issue_tracker_data.rb b/db/migrate/20190430131225_create_issue_tracker_data.rb index 7859bea9c22..d2134ad82c7 100644 --- a/db/migrate/20190430131225_create_issue_tracker_data.rb +++ b/db/migrate/20190430131225_create_issue_tracker_data.rb @@ -9,6 +9,7 @@ class CreateIssueTrackerData < ActiveRecord::Migration[5.1] DOWNTIME = false def change + # rubocop:disable Migration/AddLimitToStringColumns create_table :issue_tracker_data do |t| t.references :service, foreign_key: { on_delete: :cascade }, type: :integer, index: true, null: false t.timestamps_with_timezone @@ -19,5 +20,6 @@ class CreateIssueTrackerData < ActiveRecord::Migration[5.1] t.string :encrypted_new_issue_url t.string :encrypted_new_issue_url_iv end + # rubocop:enable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20190430142025_create_jira_tracker_data.rb b/db/migrate/20190430142025_create_jira_tracker_data.rb index d328ad63854..5e53e5a701a 100644 --- a/db/migrate/20190430142025_create_jira_tracker_data.rb +++ b/db/migrate/20190430142025_create_jira_tracker_data.rb @@ -9,6 +9,7 @@ class CreateJiraTrackerData < ActiveRecord::Migration[5.1] DOWNTIME = false def change + # rubocop:disable Migration/AddLimitToStringColumns create_table :jira_tracker_data do |t| t.references :service, foreign_key: { on_delete: :cascade }, type: :integer, index: true, null: false t.timestamps_with_timezone @@ -22,5 +23,6 @@ class CreateJiraTrackerData < ActiveRecord::Migration[5.1] t.string :encrypted_password_iv t.string :jira_issue_transition_id end + # rubocop:enable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20190514105711_create_ip_restriction.rb b/db/migrate/20190514105711_create_ip_restriction.rb index dfafbe32ad1..69f8c1b8c4e 100644 --- a/db/migrate/20190514105711_create_ip_restriction.rb +++ b/db/migrate/20190514105711_create_ip_restriction.rb @@ -12,7 +12,7 @@ class CreateIpRestriction < ActiveRecord::Migration[5.1] type: :integer, null: false, index: true - t.string :range, null: false + t.string :range, null: false # rubocop:disable Migration/AddLimitToStringColumns end add_foreign_key(:ip_restrictions, :namespaces, column: :group_id, on_delete: :cascade) # rubocop: disable Migration/AddConcurrentForeignKey diff --git a/db/migrate/20190527011309_add_required_template_name_to_application_settings.rb b/db/migrate/20190527011309_add_required_template_name_to_application_settings.rb index 6cbac0ed507..5c47e6f33c2 100644 --- a/db/migrate/20190527011309_add_required_template_name_to_application_settings.rb +++ b/db/migrate/20190527011309_add_required_template_name_to_application_settings.rb @@ -10,6 +10,6 @@ class AddRequiredTemplateNameToApplicationSettings < ActiveRecord::Migration[5.1 DOWNTIME = false def change - add_column :application_settings, :required_instance_ci_template, :string, null: true + add_column :application_settings, :required_instance_ci_template, :string, null: true # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20190606054742_add_token_encrypted_to_operations_feature_flags_clients.rb b/db/migrate/20190606054742_add_token_encrypted_to_operations_feature_flags_clients.rb index 024b5bd2ba5..2db4dc85750 100644 --- a/db/migrate/20190606054742_add_token_encrypted_to_operations_feature_flags_clients.rb +++ b/db/migrate/20190606054742_add_token_encrypted_to_operations_feature_flags_clients.rb @@ -6,6 +6,6 @@ class AddTokenEncryptedToOperationsFeatureFlagsClients < ActiveRecord::Migration DOWNTIME = false def change - add_column :operations_feature_flags_clients, :token_encrypted, :string + add_column :operations_feature_flags_clients, :token_encrypted, :string # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20190613044655_add_username_to_deploy_tokens.rb b/db/migrate/20190613044655_add_username_to_deploy_tokens.rb index 793553afe35..a0acb02013b 100644 --- a/db/migrate/20190613044655_add_username_to_deploy_tokens.rb +++ b/db/migrate/20190613044655_add_username_to_deploy_tokens.rb @@ -4,6 +4,6 @@ class AddUsernameToDeployTokens < ActiveRecord::Migration[5.1] DOWNTIME = false def change - add_column :deploy_tokens, :username, :string + add_column :deploy_tokens, :username, :string # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20190613073003_create_project_aliases.rb b/db/migrate/20190613073003_create_project_aliases.rb index 5a2c2ba0cf2..896d3ca5813 100644 --- a/db/migrate/20190613073003_create_project_aliases.rb +++ b/db/migrate/20190613073003_create_project_aliases.rb @@ -8,7 +8,7 @@ class CreateProjectAliases < ActiveRecord::Migration[5.1] def change create_table :project_aliases do |t| t.references :project, null: false, index: true, foreign_key: { on_delete: :cascade }, type: :integer - t.string :name, null: false, index: { unique: true } + t.string :name, null: false, index: { unique: true } # rubocop:disable Migration/AddLimitToStringColumns t.timestamps_with_timezone null: false end diff --git a/db/migrate/20190621151636_add_merge_request_rebase_jid.rb b/db/migrate/20190621151636_add_merge_request_rebase_jid.rb index 1fed5690ead..6c1081732e8 100644 --- a/db/migrate/20190621151636_add_merge_request_rebase_jid.rb +++ b/db/migrate/20190621151636_add_merge_request_rebase_jid.rb @@ -4,6 +4,6 @@ class AddMergeRequestRebaseJid < ActiveRecord::Migration[5.1] DOWNTIME = false def change - add_column :merge_requests, :rebase_jid, :string + add_column :merge_requests, :rebase_jid, :string # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/migrate/20190624123615_add_grafana_url_to_settings.rb b/db/migrate/20190624123615_add_grafana_url_to_settings.rb index 61efe64a7a1..835ec4e9094 100644 --- a/db/migrate/20190624123615_add_grafana_url_to_settings.rb +++ b/db/migrate/20190624123615_add_grafana_url_to_settings.rb @@ -8,8 +8,10 @@ class AddGrafanaUrlToSettings < ActiveRecord::Migration[5.1] DOWNTIME = false def up + # rubocop:disable Migration/AddLimitToStringColumns add_column_with_default(:application_settings, :grafana_url, :string, default: '/-/grafana', allow_null: false) + # rubocop:enable Migration/AddLimitToStringColumns end def down diff --git a/db/migrate/20190711124721_create_job_variables.rb b/db/migrate/20190711124721_create_job_variables.rb index a860522f39e..4ff4b031d8f 100644 --- a/db/migrate/20190711124721_create_job_variables.rb +++ b/db/migrate/20190711124721_create_job_variables.rb @@ -10,6 +10,7 @@ class CreateJobVariables < ActiveRecord::Migration[5.1] DOWNTIME = false def change + # rubocop:disable Migration/AddLimitToStringColumns create_table :ci_job_variables do |t| t.string :key, null: false t.text :encrypted_value @@ -17,6 +18,7 @@ class CreateJobVariables < ActiveRecord::Migration[5.1] t.references :job, null: false, index: true, foreign_key: { to_table: :ci_builds, on_delete: :cascade } t.integer :variable_type, null: false, limit: 2, default: 1 end + # rubocop:enable Migration/AddLimitToStringColumns add_index :ci_job_variables, [:key, :job_id], unique: true end diff --git a/db/migrate/20190726101050_rename_allow_local_requests_from_hooks_and_services_application_setting.rb b/db/migrate/20190726101050_rename_allow_local_requests_from_hooks_and_services_application_setting.rb index ac65e8d745c..cce8942128c 100644 --- a/db/migrate/20190726101050_rename_allow_local_requests_from_hooks_and_services_application_setting.rb +++ b/db/migrate/20190726101050_rename_allow_local_requests_from_hooks_and_services_application_setting.rb @@ -12,6 +12,6 @@ class RenameAllowLocalRequestsFromHooksAndServicesApplicationSetting < ActiveRec end def down - cleanup_concurrent_column_rename :application_settings, :allow_local_requests_from_web_hooks_and_services, :allow_local_requests_from_hooks_and_services + undo_rename_column_concurrently :application_settings, :allow_local_requests_from_hooks_and_services, :allow_local_requests_from_web_hooks_and_services end end diff --git a/db/migrate/20190805140353_remove_rendundant_index_from_releases.rb b/db/migrate/20190805140353_remove_rendundant_index_from_releases.rb new file mode 100644 index 00000000000..fc4bc1a423b --- /dev/null +++ b/db/migrate/20190805140353_remove_rendundant_index_from_releases.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class RemoveRendundantIndexFromReleases < ActiveRecord::Migration[5.2] + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + disable_ddl_transaction! + + def up + remove_concurrent_index :releases, :project_id + end + + def down + add_concurrent_index :releases, :project_id + end +end diff --git a/db/post_migrate/20181013005024_remove_koding_from_application_settings.rb b/db/post_migrate/20181013005024_remove_koding_from_application_settings.rb index 550ad94f4ab..b6e5473e896 100644 --- a/db/post_migrate/20181013005024_remove_koding_from_application_settings.rb +++ b/db/post_migrate/20181013005024_remove_koding_from_application_settings.rb @@ -12,6 +12,6 @@ class RemoveKodingFromApplicationSettings < ActiveRecord::Migration[4.2] def down add_column :application_settings, :koding_enabled, :boolean # rubocop:disable Migration/SaferBooleanColumn - add_column :application_settings, :koding_url, :string + add_column :application_settings, :koding_url, :string # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/post_migrate/20190404231137_remove_alternate_url_from_geo_nodes.rb b/db/post_migrate/20190404231137_remove_alternate_url_from_geo_nodes.rb index 785ceb2fb28..8e7ef0ec54f 100644 --- a/db/post_migrate/20190404231137_remove_alternate_url_from_geo_nodes.rb +++ b/db/post_migrate/20190404231137_remove_alternate_url_from_geo_nodes.rb @@ -16,6 +16,6 @@ class RemoveAlternateUrlFromGeoNodes < ActiveRecord::Migration[5.0] end def down - add_column :geo_nodes, :alternate_url, :string + add_column :geo_nodes, :alternate_url, :string # rubocop:disable Migration/AddLimitToStringColumns end end diff --git a/db/post_migrate/20190625184066_remove_sentry_from_application_settings.rb b/db/post_migrate/20190625184066_remove_sentry_from_application_settings.rb index 427df343193..9d71bfafffb 100644 --- a/db/post_migrate/20190625184066_remove_sentry_from_application_settings.rb +++ b/db/post_migrate/20190625184066_remove_sentry_from_application_settings.rb @@ -32,7 +32,7 @@ class RemoveSentryFromApplicationSettings < ActiveRecord::Migration[5.0] end SENTRY_DSN_COLUMNS.each do |column| - add_column(:application_settings, column, :string) unless column_exists?(:application_settings, column) + add_column(:application_settings, column, :string) unless column_exists?(:application_settings, column) # rubocop:disable Migration/AddLimitToStringColumns end end end diff --git a/db/post_migrate/20190801072937_add_gitlab_instance_administration_project.rb b/db/post_migrate/20190801072937_add_gitlab_instance_administration_project.rb new file mode 100644 index 00000000000..8b2cf7b3d76 --- /dev/null +++ b/db/post_migrate/20190801072937_add_gitlab_instance_administration_project.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class AddGitlabInstanceAdministrationProject < ActiveRecord::Migration[5.2] + DOWNTIME = false + + def up + Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService.new.execute! + end + + def down + ApplicationSetting.current_without_cache + &.instance_administration_project + &.owner + &.destroy! + end +end diff --git a/db/post_migrate/20190801114109_cleanup_allow_local_requests_from_hooks_and_services_application_setting_rename.rb b/db/post_migrate/20190801114109_cleanup_allow_local_requests_from_hooks_and_services_application_setting_rename.rb index 127e44254ac..cb86f843f9c 100644 --- a/db/post_migrate/20190801114109_cleanup_allow_local_requests_from_hooks_and_services_application_setting_rename.rb +++ b/db/post_migrate/20190801114109_cleanup_allow_local_requests_from_hooks_and_services_application_setting_rename.rb @@ -12,6 +12,6 @@ class CleanupAllowLocalRequestsFromHooksAndServicesApplicationSettingRename < Ac end def down - rename_column_concurrently :application_settings, :allow_local_requests_from_web_hooks_and_services, :allow_local_requests_from_hooks_and_services + undo_cleanup_concurrent_column_rename :application_settings, :allow_local_requests_from_hooks_and_services, :allow_local_requests_from_web_hooks_and_services end end diff --git a/db/post_migrate/20190809072552_set_self_monitoring_project_alerting_token.rb b/db/post_migrate/20190809072552_set_self_monitoring_project_alerting_token.rb new file mode 100644 index 00000000000..0c4faebc548 --- /dev/null +++ b/db/post_migrate/20190809072552_set_self_monitoring_project_alerting_token.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +class SetSelfMonitoringProjectAlertingToken < ActiveRecord::Migration[5.2] + DOWNTIME = false + + module Migratable + module Alerting + class ProjectAlertingSetting < ApplicationRecord + self.table_name = 'project_alerting_settings' + + belongs_to :project + + validates :token, presence: true + + attr_encrypted :token, + mode: :per_attribute_iv, + key: Settings.attr_encrypted_db_key_base_truncated, + algorithm: 'aes-256-gcm' + + before_validation :ensure_token + + private + + def ensure_token + self.token ||= generate_token + end + + def generate_token + SecureRandom.hex + end + end + end + + class Project < ApplicationRecord + has_one :alerting_setting, inverse_of: :project, class_name: 'Alerting::ProjectAlertingSetting' + end + + class ApplicationSetting < ApplicationRecord + self.table_name = 'application_settings' + + belongs_to :instance_administration_project, class_name: 'Project' + + def self.current_without_cache + last + end + end + end + + def setup_alertmanager_token(project) + return unless License.feature_available?(:prometheus_alerts) + + project.create_alerting_setting! + end + + def up + Gitlab.ee do + project = Migratable::ApplicationSetting.current_without_cache&.instance_administration_project + + if project + setup_alertmanager_token(project) + end + end + end + + def down + Gitlab.ee do + Migratable::ApplicationSetting.current_without_cache + &.instance_administration_project + &.alerting_setting + &.destroy! + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 3f7917654cf..944d23e5365 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -3015,7 +3015,6 @@ ActiveRecord::Schema.define(version: 2019_08_15_093949) do t.datetime_with_timezone "released_at", null: false t.index ["author_id"], name: "index_releases_on_author_id" t.index ["project_id", "tag"], name: "index_releases_on_project_id_and_tag" - t.index ["project_id"], name: "index_releases_on_project_id" end create_table "remote_mirrors", id: :serial, force: :cascade do |t| diff --git a/doc/development/documentation/feature-change-workflow.md b/doc/development/documentation/feature-change-workflow.md index ac93ada5a4b..00c76fe0f1b 100644 --- a/doc/development/documentation/feature-change-workflow.md +++ b/doc/development/documentation/feature-change-workflow.md @@ -69,7 +69,7 @@ To follow a consistent workflow every month, documentation changes involve the Product Managers, the developer who shipped the feature, and the technical writer for the DevOps stage. Each role is described below. -The Documentation items in the GitLab CE/EE [Feature Proposal issue template](https://gitlab.com/gitlab-org/gitlab-ce/raw/template-improvements-for-documentation/.gitlab/issue_templates/Feature%20proposal.md) +The Documentation items in the GitLab CE/EE [Feature Proposal issue template](https://gitlab.com/gitlab-org/gitlab-ce/raw/master/.gitlab/issue_templates/Feature%20proposal.md) and default merge request template will assist you with following this process. ### Product Manager role diff --git a/lib/api/pipelines.rb b/lib/api/pipelines.rb index 667bf1ec801..9e888368e7b 100644 --- a/lib/api/pipelines.rb +++ b/lib/api/pipelines.rb @@ -4,7 +4,7 @@ module API class Pipelines < Grape::API include PaginationParams - before { authenticate! } + before { authenticate_non_get! } params do requires :id, type: String, desc: 'The project ID' @@ -32,6 +32,7 @@ module API end get ':id/pipelines' do authorize! :read_pipeline, user_project + authorize! :read_build, user_project pipelines = PipelinesFinder.new(user_project, current_user, params).execute present paginate(pipelines), with: Entities::PipelineBasic diff --git a/lib/feature/gitaly.rb b/lib/feature/gitaly.rb index be397a478cd..656becbffd3 100644 --- a/lib/feature/gitaly.rb +++ b/lib/feature/gitaly.rb @@ -10,6 +10,7 @@ class Feature get_commit_signatures cache_invalidator inforef_uploadpack_cache + get_all_lfs_pointers_go ].freeze DEFAULT_ON_FLAGS = Set.new([]).freeze diff --git a/lib/gitlab/analytics/cycle_analytics/default_stages.rb b/lib/gitlab/analytics/cycle_analytics/default_stages.rb new file mode 100644 index 00000000000..286c393005f --- /dev/null +++ b/lib/gitlab/analytics/cycle_analytics/default_stages.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +# This module represents the default Cycle Analytics stages that are currently provided by CE +# Each method returns a hash that can be used to build a new stage object. +# +# Example: +# +# params = Gitlab::Analytics::CycleAnalytics::DefaultStages.params_for_issue_stage +# Analytics::CycleAnalytics::ProjectStage.new(params) +module Gitlab + module Analytics + module CycleAnalytics + module DefaultStages + def self.all + [ + params_for_issue_stage, + params_for_plan_stage, + params_for_code_stage, + params_for_test_stage, + params_for_review_stage, + params_for_staging_stage, + params_for_production_stage + ] + end + + def self.params_for_issue_stage + { + name: 'issue', + custom: false, # this stage won't be customizable, we provide it as it is + relative_position: 1, # when opening the CycleAnalytics page in CE, this stage will be the first item + start_event_identifier: :issue_created, # IssueCreated class is used as start event + end_event_identifier: :issue_stage_end # IssueStageEnd class is used as end event + } + end + + def self.params_for_plan_stage + { + name: 'plan', + custom: false, + relative_position: 2, + start_event_identifier: :plan_stage_start, + end_event_identifier: :issue_first_mentioned_in_commit + } + end + + def self.params_for_code_stage + { + name: 'code', + custom: false, + relative_position: 3, + start_event_identifier: :code_stage_start, + end_event_identifier: :merge_request_created + } + end + + def self.params_for_test_stage + { + name: 'test', + custom: false, + relative_position: 4, + start_event_identifier: :merge_request_last_build_started, + end_event_identifier: :merge_request_last_build_finished + } + end + + def self.params_for_review_stage + { + name: 'review', + custom: false, + relative_position: 5, + start_event_identifier: :merge_request_created, + end_event_identifier: :merge_request_merged + } + end + + def self.params_for_staging_stage + { + name: 'staging', + custom: false, + relative_position: 6, + start_event_identifier: :merge_request_merged, + end_event_identifier: :merge_request_first_deployed_to_production + } + end + + def self.params_for_production_stage + { + name: 'production', + custom: false, + relative_position: 7, + start_event_identifier: :merge_request_merged, + end_event_identifier: :merge_request_first_deployed_to_production + } + end + end + end + end +end diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events.rb b/lib/gitlab/analytics/cycle_analytics/stage_events.rb new file mode 100644 index 00000000000..d21f344f483 --- /dev/null +++ b/lib/gitlab/analytics/cycle_analytics/stage_events.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +module Gitlab + module Analytics + module CycleAnalytics + module StageEvents + # Convention: + # Issue: < 100 + # MergeRequest: >= 100 && < 1000 + # Custom events for default stages: >= 1000 (legacy) + ENUM_MAPPING = { + StageEvents::IssueCreated => 1, + StageEvents::IssueFirstMentionedInCommit => 2, + StageEvents::MergeRequestCreated => 100, + StageEvents::MergeRequestFirstDeployedToProduction => 101, + StageEvents::MergeRequestLastBuildFinished => 102, + StageEvents::MergeRequestLastBuildStarted => 103, + StageEvents::MergeRequestMerged => 104, + StageEvents::CodeStageStart => 1_000, + StageEvents::IssueStageEnd => 1_001, + StageEvents::PlanStageStart => 1_002 + }.freeze + + EVENTS = ENUM_MAPPING.keys.freeze + + # Defines which start_event and end_event pairs are allowed + PAIRING_RULES = { + StageEvents::PlanStageStart => [ + StageEvents::IssueFirstMentionedInCommit + ], + StageEvents::CodeStageStart => [ + StageEvents::MergeRequestCreated + ], + StageEvents::IssueCreated => [ + StageEvents::IssueStageEnd + ], + StageEvents::MergeRequestCreated => [ + StageEvents::MergeRequestMerged + ], + StageEvents::MergeRequestLastBuildStarted => [ + StageEvents::MergeRequestLastBuildFinished + ], + StageEvents::MergeRequestMerged => [ + StageEvents::MergeRequestFirstDeployedToProduction + ] + }.freeze + + def [](identifier) + events.find { |e| e.identifier.to_s.eql?(identifier.to_s) } || raise(KeyError) + end + + # hash for defining ActiveRecord enum: identifier => number + def to_enum + ENUM_MAPPING.each_with_object({}) { |(k, v), hash| hash[k.identifier] = v } + end + + # will be overridden in EE with custom events + def pairing_rules + PAIRING_RULES + end + + # will be overridden in EE with custom events + def events + EVENTS + end + + module_function :[], :to_enum, :pairing_rules, :events + end + end + end +end diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/code_stage_start.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/code_stage_start.rb new file mode 100644 index 00000000000..ff9c8a79225 --- /dev/null +++ b/lib/gitlab/analytics/cycle_analytics/stage_events/code_stage_start.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Gitlab + module Analytics + module CycleAnalytics + module StageEvents + class CodeStageStart < SimpleStageEvent + def self.name + s_("CycleAnalyticsEvent|Issue first mentioned in a commit") + end + + def self.identifier + :code_stage_start + end + + def object_type + MergeRequest + end + end + end + end + end +end diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/issue_created.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/issue_created.rb new file mode 100644 index 00000000000..a601c9797f8 --- /dev/null +++ b/lib/gitlab/analytics/cycle_analytics/stage_events/issue_created.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Gitlab + module Analytics + module CycleAnalytics + module StageEvents + class IssueCreated < SimpleStageEvent + def self.name + s_("CycleAnalyticsEvent|Issue created") + end + + def self.identifier + :issue_created + end + + def object_type + Issue + end + end + end + end + end +end diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/issue_first_mentioned_in_commit.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/issue_first_mentioned_in_commit.rb new file mode 100644 index 00000000000..7424043ef7b --- /dev/null +++ b/lib/gitlab/analytics/cycle_analytics/stage_events/issue_first_mentioned_in_commit.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Gitlab + module Analytics + module CycleAnalytics + module StageEvents + class IssueFirstMentionedInCommit < SimpleStageEvent + def self.name + s_("CycleAnalyticsEvent|Issue first mentioned in a commit") + end + + def self.identifier + :issue_first_mentioned_in_commit + end + + def object_type + Issue + end + end + end + end + end +end diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/issue_stage_end.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/issue_stage_end.rb new file mode 100644 index 00000000000..ceb229c552f --- /dev/null +++ b/lib/gitlab/analytics/cycle_analytics/stage_events/issue_stage_end.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Gitlab + module Analytics + module CycleAnalytics + module StageEvents + class IssueStageEnd < SimpleStageEvent + def self.name + PlanStageStart.name + end + + def self.identifier + :issue_stage_end + end + + def object_type + Issue + end + end + end + end + end +end diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_created.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_created.rb new file mode 100644 index 00000000000..8be00831b4f --- /dev/null +++ b/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_created.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Gitlab + module Analytics + module CycleAnalytics + module StageEvents + class MergeRequestCreated < SimpleStageEvent + def self.name + s_("CycleAnalyticsEvent|Merge request created") + end + + def self.identifier + :merge_request_created + end + + def object_type + MergeRequest + end + end + end + end + end +end diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_first_deployed_to_production.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_first_deployed_to_production.rb new file mode 100644 index 00000000000..6d7a2c023ff --- /dev/null +++ b/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_first_deployed_to_production.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Gitlab + module Analytics + module CycleAnalytics + module StageEvents + class MergeRequestFirstDeployedToProduction < SimpleStageEvent + def self.name + s_("CycleAnalyticsEvent|Merge request first deployed to production") + end + + def self.identifier + :merge_request_first_deployed_to_production + end + + def object_type + MergeRequest + end + end + end + end + end +end diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_finished.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_finished.rb new file mode 100644 index 00000000000..12d82fe2c62 --- /dev/null +++ b/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_finished.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Gitlab + module Analytics + module CycleAnalytics + module StageEvents + class MergeRequestLastBuildFinished < SimpleStageEvent + def self.name + s_("CycleAnalyticsEvent|Merge request last build finish time") + end + + def self.identifier + :merge_request_last_build_finished + end + + def object_type + MergeRequest + end + end + end + end + end +end diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_started.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_started.rb new file mode 100644 index 00000000000..9e749b0fdfa --- /dev/null +++ b/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_started.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Gitlab + module Analytics + module CycleAnalytics + module StageEvents + class MergeRequestLastBuildStarted < SimpleStageEvent + def self.name + s_("CycleAnalyticsEvent|Merge request last build start time") + end + + def self.identifier + :merge_request_last_build_started + end + + def object_type + MergeRequest + end + end + end + end + end +end diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_merged.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_merged.rb new file mode 100644 index 00000000000..bbfb5d12992 --- /dev/null +++ b/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_merged.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Gitlab + module Analytics + module CycleAnalytics + module StageEvents + class MergeRequestMerged < SimpleStageEvent + def self.name + s_("CycleAnalyticsEvent|Merge request merged") + end + + def self.identifier + :merge_request_merged + end + + def object_type + MergeRequest + end + end + end + end + end +end diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/plan_stage_start.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/plan_stage_start.rb new file mode 100644 index 00000000000..803317d8b55 --- /dev/null +++ b/lib/gitlab/analytics/cycle_analytics/stage_events/plan_stage_start.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Gitlab + module Analytics + module CycleAnalytics + module StageEvents + class PlanStageStart < SimpleStageEvent + def self.name + s_("CycleAnalyticsEvent|Issue first associated with a milestone or issue first added to a board") + end + + def self.identifier + :plan_stage_start + end + + def object_type + Issue + end + end + end + end + end +end diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/simple_stage_event.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/simple_stage_event.rb new file mode 100644 index 00000000000..253c489d822 --- /dev/null +++ b/lib/gitlab/analytics/cycle_analytics/stage_events/simple_stage_event.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Gitlab + module Analytics + module CycleAnalytics + module StageEvents + # Represents a simple event that usually refers to one database column and does not require additional user input + class SimpleStageEvent < StageEvent + end + end + end + end +end diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event.rb new file mode 100644 index 00000000000..a55eee048c2 --- /dev/null +++ b/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Gitlab + module Analytics + module CycleAnalytics + module StageEvents + # Base class for expressing an event that can be used for a stage. + class StageEvent + def initialize(params) + @params = params + end + + def self.name + raise NotImplementedError + end + + def self.identifier + raise NotImplementedError + end + + def object_type + raise NotImplementedError + end + end + end + end + end +end diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb index 9bba4f6ce1e..57a413f8e04 100644 --- a/lib/gitlab/database/migration_helpers.rb +++ b/lib/gitlab/database/migration_helpers.rb @@ -470,7 +470,7 @@ module Gitlab # We set the default value _after_ adding the column so we don't end up # updating any existing data with the default value. This isn't # necessary since we copy over old values further down. - change_column_default(table, new, old_col.default) if old_col.default + change_column_default(table, new, old_col.default) unless old_col.default.nil? install_rename_triggers(table, old, new) @@ -482,6 +482,16 @@ module Gitlab copy_foreign_keys(table, old, new) end + def undo_rename_column_concurrently(table, old, new) + trigger_name = rename_trigger_name(table, old, new) + + check_trigger_permissions!(table) + + remove_rename_triggers_for_postgresql(table, trigger_name) + + remove_column(table, new) + end + # Installs triggers in a table that keep a new column in sync with an old # one. # @@ -547,6 +557,35 @@ module Gitlab remove_column(table, old) end + def undo_cleanup_concurrent_column_rename(table, old, new, type: nil) + if transaction_open? + raise 'undo_cleanup_concurrent_column_rename can not be run inside a transaction' + end + + check_trigger_permissions!(table) + + new_column = column_for(table, new) + + add_column(table, old, type || new_column.type, + limit: new_column.limit, + precision: new_column.precision, + scale: new_column.scale) + + # We set the default value _after_ adding the column so we don't end up + # updating any existing data with the default value. This isn't + # necessary since we copy over old values further down. + change_column_default(table, old, new_column.default) unless new_column.default.nil? + + install_rename_triggers(table, old, new) + + update_column_in_batches(table, old, Arel::Table.new(table)[new]) + + change_column_null(table, old, false) unless new_column.null + + copy_indexes(table, new, old) + copy_foreign_keys(table, new, old) + end + # Changes the column type of a table using a background migration. # # Because this method uses a background migration it's more suitable for @@ -747,6 +786,11 @@ module Gitlab EOF execute <<-EOF.strip_heredoc + DROP TRIGGER IF EXISTS #{trigger} + ON #{table} + EOF + + execute <<-EOF.strip_heredoc CREATE TRIGGER #{trigger} BEFORE INSERT OR UPDATE ON #{table} diff --git a/lib/gitlab/database_importers/self_monitoring/project/create_service.rb b/lib/gitlab/database_importers/self_monitoring/project/create_service.rb new file mode 100644 index 00000000000..164854e1e1a --- /dev/null +++ b/lib/gitlab/database_importers/self_monitoring/project/create_service.rb @@ -0,0 +1,251 @@ +# frozen_string_literal: true + +module Gitlab + module DatabaseImporters + module SelfMonitoring + module Project + include Stepable + + class CreateService < ::BaseService + include Stepable + + STEPS_ALLOWED_TO_FAIL = [ + :validate_application_settings, :validate_project_created, :validate_admins + ].freeze + + VISIBILITY_LEVEL = Gitlab::VisibilityLevel::INTERNAL + PROJECT_NAME = 'GitLab Instance Administration' + + steps :validate_application_settings, + :validate_project_created, + :validate_admins, + :create_group, + :create_project, + :save_project_id, + :add_group_members, + :add_to_whitelist, + :add_prometheus_manual_configuration + + def initialize + super(nil) + end + + def execute! + result = execute_steps + + if result[:status] == :success + result + elsif STEPS_ALLOWED_TO_FAIL.include?(result[:failed_step]) + success + else + raise StandardError, result[:message] + end + end + + private + + def validate_application_settings + return success if application_settings + + log_error(_('No application_settings found')) + error(_('No application_settings found')) + end + + def validate_project_created + return success unless project_created? + + log_error(_('Project already created')) + error(_('Project already created')) + end + + def validate_admins + unless instance_admins.any? + log_error(_('No active admin user found')) + return error(_('No active admin user found')) + end + + success + end + + def create_group + if project_created? + log_info(_('Instance administrators group already exists')) + @group = application_settings.instance_administration_project.owner + return success(group: @group) + end + + @group = ::Groups::CreateService.new(group_owner, create_group_params).execute + + if @group.persisted? + success(group: @group) + else + error(_('Could not create group')) + end + end + + def create_project + if project_created? + log_info(_('Instance administration project already exists')) + @project = application_settings.instance_administration_project + return success(project: project) + end + + @project = ::Projects::CreateService.new(group_owner, create_project_params).execute + + if project.persisted? + success(project: project) + else + log_error(_("Could not create instance administration project. Errors: %{errors}") % { errors: project.errors.full_messages }) + error(_('Could not create project')) + end + end + + def save_project_id + return success if project_created? + + result = application_settings.update(instance_administration_project_id: @project.id) + + if result + success + else + log_error(_("Could not save instance administration project ID, errors: %{errors}") % { errors: application_settings.errors.full_messages }) + error(_('Could not save project ID')) + end + end + + def add_group_members + members = @group.add_users(members_to_add, Gitlab::Access::MAINTAINER) + errors = members.flat_map { |member| member.errors.full_messages } + + if errors.any? + log_error(_('Could not add admins as members to self-monitoring project. Errors: %{errors}') % { errors: errors }) + error(_('Could not add admins as members')) + else + success + end + end + + def add_to_whitelist + return success unless prometheus_enabled? + return success unless prometheus_listen_address.present? + + uri = parse_url(internal_prometheus_listen_address_uri) + return error(_('Prometheus listen_address is not a valid URI')) unless uri + + application_settings.add_to_outbound_local_requests_whitelist([uri.normalized_host]) + result = application_settings.save + + if result + # Expire the Gitlab::CurrentSettings cache after updating the whitelist. + # This happens automatically in an after_commit hook, but in migrations, + # the after_commit hook only runs at the end of the migration. + Gitlab::CurrentSettings.expire_current_application_settings + success + else + log_error(_("Could not add prometheus URL to whitelist, errors: %{errors}") % { errors: application_settings.errors.full_messages }) + error(_('Could not add prometheus URL to whitelist')) + end + end + + def add_prometheus_manual_configuration + return success unless prometheus_enabled? + return success unless prometheus_listen_address.present? + + service = project.find_or_initialize_service('prometheus') + + unless service.update(prometheus_service_attributes) + log_error(_('Could not save prometheus manual configuration for self-monitoring project. Errors: %{errors}') % { errors: service.errors.full_messages }) + return error(_('Could not save prometheus manual configuration')) + end + + success + end + + def application_settings + @application_settings ||= ApplicationSetting.current_without_cache + end + + def project_created? + application_settings.instance_administration_project.present? + end + + def parse_url(uri_string) + Addressable::URI.parse(uri_string) + rescue Addressable::URI::InvalidURIError, TypeError + end + + def prometheus_enabled? + Gitlab.config.prometheus.enable + rescue Settingslogic::MissingSetting + log_error(_('prometheus.enable is not present in gitlab.yml')) + + false + end + + def prometheus_listen_address + Gitlab.config.prometheus.listen_address + rescue Settingslogic::MissingSetting + log_error(_('prometheus.listen_address is not present in gitlab.yml')) + + nil + end + + def instance_admins + @instance_admins ||= User.admins.active + end + + def group_owner + instance_admins.first + end + + def members_to_add + # Exclude admins who are already members of group because + # `@group.add_users(users)` returns an error if the users parameter contains + # users who are already members of the group. + instance_admins - @group.members.collect(&:user) + end + + def create_group_params + { + name: 'GitLab Instance Administrators', + path: "gitlab-instance-administrators-#{SecureRandom.hex(4)}", + visibility_level: VISIBILITY_LEVEL + } + end + + def docs_path + Rails.application.routes.url_helpers.help_page_path( + 'administration/monitoring/gitlab_instance_administration_project/index' + ) + end + + def create_project_params + { + initialize_with_readme: true, + visibility_level: VISIBILITY_LEVEL, + name: PROJECT_NAME, + description: "This project is automatically generated and will be used to help monitor this GitLab instance. [More information](#{docs_path})", + namespace_id: @group.id + } + end + + def internal_prometheus_listen_address_uri + if prometheus_listen_address.starts_with?('http') + prometheus_listen_address + else + 'http://' + prometheus_listen_address + end + end + + def prometheus_service_attributes + { + api_url: internal_prometheus_listen_address_uri, + manual_configuration: true, + active: true + } + end + end + end + end + end +end diff --git a/lib/tasks/gitlab/assets.rake b/lib/tasks/gitlab/assets.rake index a07ae3a418a..7a42e4e92a0 100644 --- a/lib/tasks/gitlab/assets.rake +++ b/lib/tasks/gitlab/assets.rake @@ -10,15 +10,9 @@ namespace :gitlab do rake:assets:precompile webpack:compile gitlab:assets:fix_urls - gitlab:assets:compile_vrt ].each(&Gitlab::TaskHelpers.method(:invoke_and_time_task)) end - desc 'GitLab | Assets | Compile visual review toolbar' - task :compile_vrt do - system 'yarn', 'webpack-vrt' - end - desc 'GitLab | Assets | Clean up old compiled frontend assets' task clean: ['rake:assets:clean'] diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 4f3c8e8046d..61642fbbd59 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -3353,6 +3353,12 @@ msgstr "" msgid "Copy token to clipboard" msgstr "" +msgid "Could not add admins as members" +msgstr "" + +msgid "Could not add admins as members to self-monitoring project. Errors: %{errors}" +msgstr "" + msgid "Could not add prometheus URL to whitelist" msgstr "" @@ -3371,6 +3377,9 @@ msgstr "" msgid "Could not create Wiki Repository at this time. Please try again later." msgstr "" +msgid "Could not create group" +msgstr "" + msgid "Could not create instance administration project. Errors: %{errors}" msgstr "" @@ -3398,6 +3407,12 @@ msgstr "" msgid "Could not save project ID" msgstr "" +msgid "Could not save prometheus manual configuration" +msgstr "" + +msgid "Could not save prometheus manual configuration for self-monitoring project. Errors: %{errors}" +msgstr "" + msgid "Coverage" msgstr "" @@ -3602,6 +3617,30 @@ msgstr "" msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project." msgstr "" +msgid "CycleAnalyticsEvent|Issue created" +msgstr "" + +msgid "CycleAnalyticsEvent|Issue first associated with a milestone or issue first added to a board" +msgstr "" + +msgid "CycleAnalyticsEvent|Issue first mentioned in a commit" +msgstr "" + +msgid "CycleAnalyticsEvent|Merge request created" +msgstr "" + +msgid "CycleAnalyticsEvent|Merge request first deployed to production" +msgstr "" + +msgid "CycleAnalyticsEvent|Merge request last build finish time" +msgstr "" + +msgid "CycleAnalyticsEvent|Merge request last build start time" +msgstr "" + +msgid "CycleAnalyticsEvent|Merge request merged" +msgstr "" + msgid "CycleAnalyticsStage|Code" msgstr "" @@ -7376,9 +7415,15 @@ msgstr "" msgid "No Tag" msgstr "" +msgid "No active admin user found" +msgstr "" + msgid "No activities found" msgstr "" +msgid "No application_settings found" +msgstr "" + msgid "No available namespaces to fork the project." msgstr "" @@ -8002,7 +8047,7 @@ msgstr "" msgid "PipelineSheduleIntervalPattern|Custom" msgstr "" -msgid "PipelineStatusTooltip|Commit: %{ci_status}" +msgid "PipelineStatusTooltip|Pipeline: %{ciStatus}" msgstr "" msgid "PipelineStatusTooltip|Pipeline: %{ci_status}" @@ -8722,6 +8767,9 @@ msgstr "" msgid "Project access must be granted explicitly to each user." msgstr "" +msgid "Project already created" +msgstr "" + msgid "Project and wiki repositories" msgstr "" @@ -13919,6 +13967,9 @@ msgstr "" msgid "pending comment" msgstr "" +msgid "pipeline" +msgstr "" + msgid "private" msgstr "" @@ -13934,6 +13985,12 @@ msgstr "" msgid "project avatar" msgstr "" +msgid "prometheus.enable is not present in gitlab.yml" +msgstr "" + +msgid "prometheus.listen_address is not present in gitlab.yml" +msgstr "" + msgid "quick actions" msgstr "" diff --git a/package.json b/package.json index b21b448e741..e076f9e6664 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,7 @@ "stylelint-create-utility-map": "node scripts/frontend/stylelint/stylelint-utility-map.js", "test": "node scripts/frontend/test", "webpack": "NODE_OPTIONS=\"--max-old-space-size=3584\" webpack --config config/webpack.config.js", - "webpack-prod": "NODE_OPTIONS=\"--max-old-space-size=3584\" NODE_ENV=production webpack --config config/webpack.config.js", - "webpack-vrt": "NODE_OPTIONS=\"--max-old-space-size=3584\" NODE_ENV=production webpack --config config/webpack.config.review_toolbar.js" + "webpack-prod": "NODE_OPTIONS=\"--max-old-space-size=3584\" NODE_ENV=production webpack --config config/webpack.config.js" }, "dependencies": { "@babel/core": "^7.5.5", @@ -40,6 +39,7 @@ "@gitlab/csslab": "^1.9.0", "@gitlab/svgs": "^1.70.0", "@gitlab/ui": "5.18.0", + "@gitlab/visual-review-tools": "^1.0.0", "apollo-cache-inmemory": "^1.5.1", "apollo-client": "^2.5.1", "apollo-link": "^1.2.11", diff --git a/rubocop/cop/migration/add_limit_to_string_columns.rb b/rubocop/cop/migration/add_limit_to_string_columns.rb new file mode 100644 index 00000000000..30affcbb089 --- /dev/null +++ b/rubocop/cop/migration/add_limit_to_string_columns.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +require_relative '../../migration_helpers' + +module RuboCop + module Cop + module Migration + # Cop that enforces length constraints to string columns + class AddLimitToStringColumns < RuboCop::Cop::Cop + include MigrationHelpers + + ADD_COLUMNS_METHODS = %i(add_column add_column_with_default).freeze + + MSG = 'String columns should have a limit constraint. 255 is suggested'.freeze + + def on_def(node) + return unless in_migration?(node) + + node.each_descendant(:send) do |send_node| + next unless string_operation?(send_node) + + add_offense(send_node, location: :selector) unless limit_on_string_column?(send_node) + end + end + + private + + def string_operation?(node) + modifier = node.children[0] + migration_method = node.children[1] + + if migration_method == :string + modifier.type == :lvar + elsif ADD_COLUMNS_METHODS.include?(migration_method) + modifier.nil? && string_column?(node.children[4]) + end + end + + def string_column?(column_type) + column_type.type == :sym && column_type.value == :string + end + + def limit_on_string_column?(node) + migration_method = node.children[1] + + if migration_method == :string + limit_present?(node.children) + elsif ADD_COLUMNS_METHODS.include?(migration_method) + limit_present?(node) + end + end + + def limit_present?(statement) + !(statement.to_s =~ /:limit/).nil? + end + end + end + end +end diff --git a/rubocop/rubocop.rb b/rubocop/rubocop.rb index 58a7ead6f13..d1328c4eb38 100644 --- a/rubocop/rubocop.rb +++ b/rubocop/rubocop.rb @@ -17,6 +17,7 @@ require_relative 'cop/migration/add_column' require_relative 'cop/migration/add_concurrent_foreign_key' require_relative 'cop/migration/add_concurrent_index' require_relative 'cop/migration/add_index' +require_relative 'cop/migration/add_limit_to_string_columns' require_relative 'cop/migration/add_reference' require_relative 'cop/migration/add_timestamps' require_relative 'cop/migration/datetime' diff --git a/spec/factories/ci/job_artifacts.rb b/spec/factories/ci/job_artifacts.rb index 2d68a8e9fe3..6f553cadfa3 100644 --- a/spec/factories/ci/job_artifacts.rb +++ b/spec/factories/ci/job_artifacts.rb @@ -8,6 +8,10 @@ FactoryBot.define do file_type :archive file_format :zip + trait :expired do + expire_at { Date.yesterday } + end + trait :remote_store do file_store JobArtifactUploader::Store::REMOTE end diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb index e2100c8562b..e4728f37217 100644 --- a/spec/features/dashboard/projects_spec.rb +++ b/spec/features/dashboard/projects_spec.rb @@ -169,7 +169,7 @@ describe 'Dashboard Projects' do expect(page).to have_xpath("//a[@href='#{pipelines_project_commit_path(project, project.commit, ref: pipeline.ref)}']") expect(page).to have_css('.ci-status-link') expect(page).to have_css('.ci-status-icon-success') - expect(page).to have_link('Commit: passed') + expect(page).to have_link('Pipeline: passed') end end @@ -189,7 +189,7 @@ describe 'Dashboard Projects' do expect(page).not_to have_xpath("//a[@href='#{pipelines_project_commit_path(project, project.commit, ref: pipeline.ref)}']") expect(page).not_to have_css('.ci-status-link') expect(page).not_to have_css('.ci-status-icon-success') - expect(page).not_to have_link('Commit: passed') + expect(page).not_to have_link('Pipeline: passed') end end end diff --git a/spec/features/projects/show/user_sees_last_commit_ci_status_spec.rb b/spec/features/projects/show/user_sees_last_commit_ci_status_spec.rb index a1cad261875..fdc238d55cf 100644 --- a/spec/features/projects/show/user_sees_last_commit_ci_status_spec.rb +++ b/spec/features/projects/show/user_sees_last_commit_ci_status_spec.rb @@ -18,7 +18,7 @@ describe 'Projects > Show > User sees last commit CI status' do page.within '.blob-commit-info' do expect(page).to have_content(project.commit.sha[0..6]) - expect(page).to have_link('Commit: skipped') + expect(page).to have_link('Pipeline: skipped') end end end diff --git a/spec/frontend/monitoring/embed/embed_spec.js b/spec/frontend/monitoring/embed/embed_spec.js index 3b18a0f77c7..1ce14e2418a 100644 --- a/spec/frontend/monitoring/embed/embed_spec.js +++ b/spec/frontend/monitoring/embed/embed_spec.js @@ -1,7 +1,7 @@ import { createLocalVue, shallowMount } from '@vue/test-utils'; import Vuex from 'vuex'; import Embed from '~/monitoring/components/embed.vue'; -import MonitorAreaChart from '~/monitoring/components/charts/area.vue'; +import MonitorTimeSeriesChart from '~/monitoring/components/charts/time_series.vue'; import { TEST_HOST } from 'helpers/test_constants'; import { groups, initialState, metricsData, metricsWithData } from './mock_data'; @@ -55,7 +55,7 @@ describe('Embed', () => { it('shows an empty state when no metrics are present', () => { expect(wrapper.find('.metrics-embed').exists()).toBe(true); - expect(wrapper.find(MonitorAreaChart).exists()).toBe(false); + expect(wrapper.find(MonitorTimeSeriesChart).exists()).toBe(false); }); }); @@ -71,8 +71,8 @@ describe('Embed', () => { it('shows a chart when metrics are present', () => { wrapper.setProps({}); expect(wrapper.find('.metrics-embed').exists()).toBe(true); - expect(wrapper.find(MonitorAreaChart).exists()).toBe(true); - expect(wrapper.findAll(MonitorAreaChart).length).toBe(2); + expect(wrapper.find(MonitorTimeSeriesChart).exists()).toBe(true); + expect(wrapper.findAll(MonitorTimeSeriesChart).length).toBe(2); }); }); }); diff --git a/spec/helpers/ci_status_helper_spec.rb b/spec/helpers/ci_status_helper_spec.rb index bc2422aba90..4f665dc0514 100644 --- a/spec/helpers/ci_status_helper_spec.rb +++ b/spec/helpers/ci_status_helper_spec.rb @@ -53,4 +53,80 @@ describe CiStatusHelper do expect(helper.pipeline_status_cache_key(pipeline_status)).to eq("pipeline-status/123abc-success") end end + + describe "#render_status_with_link" do + subject { helper.render_status_with_link("success") } + + it "renders a passed status icon" do + is_expected.to include("<span class=\"ci-status-link ci-status-icon-success d-inline-flex") + end + + it "has 'Pipeline' as the status type in the title" do + is_expected.to include("title=\"Pipeline: passed\"") + end + + it "has the success status icon" do + is_expected.to include("ci-status-icon-success") + end + + context "when pipeline has commit path" do + subject { helper.render_status_with_link("success", "/commit-path") } + + it "links to commit" do + is_expected.to include("href=\"/commit-path\"") + end + + it "does not contain a span element" do + is_expected.not_to include("<span") + end + + it "has 'Pipeline' as the status type in the title" do + is_expected.to include("title=\"Pipeline: passed\"") + end + + it "has the correct status icon" do + is_expected.to include("ci-status-icon-success") + end + end + + context "when different type than pipeline is provided" do + subject { helper.render_status_with_link("success", type: "commit") } + + it "has the provided type in the title" do + is_expected.to include("title=\"Commit: passed\"") + end + end + + context "when tooltip_placement is provided" do + subject { helper.render_status_with_link("success", tooltip_placement: "right") } + + it "has the provided tooltip placement" do + is_expected.to include("data-placement=\"right\"") + end + end + + context "when additional CSS classes are provided" do + subject { helper.render_status_with_link("success", cssclass: "extra-class") } + + it "has appended extra class to icon classes" do + is_expected.to include("class=\"ci-status-link ci-status-icon-success d-inline-flex extra-class\"") + end + end + + context "when container is provided" do + subject { helper.render_status_with_link("success", container: "my-container") } + + it "has the provided container in data" do + is_expected.to include("data-container=\"my-container\"") + end + end + + context "when icon_size is provided" do + subject { helper.render_status_with_link("success", icon_size: 24) } + + it "has the svg class to change size" do + is_expected.to include("<svg class=\"s24\">") + end + end + end end diff --git a/spec/javascripts/monitoring/panel_type_spec.js b/spec/javascripts/monitoring/panel_type_spec.js index 086be628093..a2366e74d43 100644 --- a/spec/javascripts/monitoring/panel_type_spec.js +++ b/spec/javascripts/monitoring/panel_type_spec.js @@ -1,7 +1,7 @@ import { shallowMount } from '@vue/test-utils'; import PanelType from '~/monitoring/components/panel_type.vue'; import EmptyChart from '~/monitoring/components/charts/empty_chart.vue'; -import AreaChart from '~/monitoring/components/charts/area.vue'; +import TimeSeriesChart from '~/monitoring/components/charts/time_series.vue'; import { graphDataPrometheusQueryRange } from './mock_data'; import { createStore } from '~/monitoring/stores'; @@ -62,9 +62,10 @@ describe('Panel Type component', () => { }); }); - describe('Area Chart panel type', () => { + describe('Time Series Chart panel type', () => { it('is rendered', () => { - expect(panelType.find(AreaChart).exists()).toBe(true); + expect(panelType.find(TimeSeriesChart).isVueInstance()).toBe(true); + expect(panelType.find(TimeSeriesChart).exists()).toBe(true); }); it('sets clipboard text on the dropdown', () => { diff --git a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event_spec.rb new file mode 100644 index 00000000000..29f4be76a65 --- /dev/null +++ b/spec/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event_spec.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Gitlab::Analytics::CycleAnalytics::StageEvents::StageEvent do + it { expect(described_class).to respond_to(:name) } + it { expect(described_class).to respond_to(:identifier) } + + it { expect(described_class.new({})).to respond_to(:object_type) } +end diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb index 2731fc8573f..cff4eb398bf 100644 --- a/spec/lib/gitlab/database/migration_helpers_spec.rb +++ b/spec/lib/gitlab/database/migration_helpers_spec.rb @@ -576,6 +576,38 @@ describe Gitlab::Database::MigrationHelpers do model.rename_column_concurrently(:users, :old, :new) end + + context 'when default is false' do + let(:old_column) do + double(:column, + type: :boolean, + limit: nil, + default: false, + null: false, + precision: nil, + scale: nil) + end + + it 'copies the default to the new column' do + expect(model).to receive(:change_column_default) + .with(:users, :new, old_column.default) + + model.rename_column_concurrently(:users, :old, :new) + end + end + end + end + + describe '#undo_rename_column_concurrently' do + it 'reverses the operations of rename_column_concurrently' do + expect(model).to receive(:check_trigger_permissions!).with(:users) + + expect(model).to receive(:remove_rename_triggers_for_postgresql) + .with(:users, /trigger_.{12}/) + + expect(model).to receive(:remove_column).with(:users, :new) + + model.undo_rename_column_concurrently(:users, :old, :new) end end @@ -592,6 +624,80 @@ describe Gitlab::Database::MigrationHelpers do end end + describe '#undo_cleanup_concurrent_column_rename' do + context 'in a transaction' do + it 'raises RuntimeError' do + allow(model).to receive(:transaction_open?).and_return(true) + + expect { model.undo_cleanup_concurrent_column_rename(:users, :old, :new) } + .to raise_error(RuntimeError) + end + end + + context 'outside a transaction' do + let(:new_column) do + double(:column, + type: :integer, + limit: 8, + default: 0, + null: false, + precision: 5, + scale: 1) + end + + let(:trigger_name) { model.rename_trigger_name(:users, :old, :new) } + + before do + allow(model).to receive(:transaction_open?).and_return(false) + allow(model).to receive(:column_for).and_return(new_column) + end + + it 'reverses the operations of cleanup_concurrent_column_rename' do + expect(model).to receive(:check_trigger_permissions!).with(:users) + + expect(model).to receive(:install_rename_triggers_for_postgresql) + .with(trigger_name, '"users"', '"old"', '"new"') + + expect(model).to receive(:add_column) + .with(:users, :old, :integer, + limit: new_column.limit, + precision: new_column.precision, + scale: new_column.scale) + + expect(model).to receive(:change_column_default) + .with(:users, :old, new_column.default) + + expect(model).to receive(:update_column_in_batches) + + expect(model).to receive(:change_column_null).with(:users, :old, false) + + expect(model).to receive(:copy_indexes).with(:users, :new, :old) + expect(model).to receive(:copy_foreign_keys).with(:users, :new, :old) + + model.undo_cleanup_concurrent_column_rename(:users, :old, :new) + end + + context 'when default is false' do + let(:new_column) do + double(:column, + type: :boolean, + limit: nil, + default: false, + null: false, + precision: nil, + scale: nil) + end + + it 'copies the default to the old column' do + expect(model).to receive(:change_column_default) + .with(:users, :old, new_column.default) + + model.undo_cleanup_concurrent_column_rename(:users, :old, :new) + end + end + end + end + describe '#change_column_type_concurrently' do it 'changes the column type' do expect(model).to receive(:rename_column_concurrently) @@ -619,10 +725,18 @@ describe Gitlab::Database::MigrationHelpers do .with(/CREATE OR REPLACE FUNCTION foo()/m) expect(model).to receive(:execute) + .with(/DROP TRIGGER IF EXISTS foo/m) + + expect(model).to receive(:execute) .with(/CREATE TRIGGER foo/m) model.install_rename_triggers_for_postgresql('foo', :users, :old, :new) end + + it 'does not fail if trigger already exists' do + model.install_rename_triggers_for_postgresql('foo', :users, :old, :new) + model.install_rename_triggers_for_postgresql('foo', :users, :old, :new) + end end describe '#remove_rename_triggers_for_postgresql' do diff --git a/spec/services/self_monitoring/project/create_service_spec.rb b/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb index def20448bd9..9bd0d800086 100644 --- a/spec/services/self_monitoring/project/create_service_spec.rb +++ b/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb @@ -2,29 +2,48 @@ require 'spec_helper' -describe SelfMonitoring::Project::CreateService do +describe Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService do describe '#execute' do - let(:result) { subject.execute } + let(:result) { subject.execute! } let(:prometheus_settings) do - OpenStruct.new( + { enable: true, listen_address: 'localhost:9090' - ) + } end before do - allow(Gitlab.config).to receive(:prometheus).and_return(prometheus_settings) + stub_config(prometheus: prometheus_settings) + end + + context 'without application_settings' do + it 'does not fail' do + expect(subject).to receive(:log_error).and_call_original + expect(result).to eq( + status: :success + ) + + expect(Project.count).to eq(0) + expect(Group.count).to eq(0) + end end context 'without admin users' do - it 'returns error' do + let(:application_setting) { Gitlab::CurrentSettings.current_application_settings } + + before do + allow(ApplicationSetting).to receive(:current_without_cache) { application_setting } + end + + it 'does not fail' do expect(subject).to receive(:log_error).and_call_original expect(result).to eq( - status: :error, - message: 'No active admin user found', - failed_step: :validate_admins + status: :success ) + + expect(Project.count).to eq(0) + expect(Group.count).to eq(0) end end @@ -36,6 +55,7 @@ describe SelfMonitoring::Project::CreateService do let!(:user) { create(:user, :admin) } before do + allow(ApplicationSetting).to receive(:current_without_cache) { application_setting } application_setting.allow_local_requests_from_web_hooks_and_services = true end @@ -56,8 +76,8 @@ describe SelfMonitoring::Project::CreateService do it 'creates group' do expect(result[:status]).to eq(:success) expect(group).to be_persisted - expect(group.name).to eq(described_class::GROUP_NAME) - expect(group.path).to start_with(described_class::GROUP_PATH) + expect(group.name).to eq('GitLab Instance Administrators') + expect(group.path).to start_with('gitlab-instance-administrators') expect(group.path.split('-').last.length).to eq(8) expect(group.visibility_level).to eq(described_class::VISIBILITY_LEVEL) end @@ -77,9 +97,16 @@ describe SelfMonitoring::Project::CreateService do end it 'creates project with correct name and description' do + path = 'administration/monitoring/gitlab_instance_administration_project/index' + docs_path = Rails.application.routes.url_helpers.help_page_path(path) + expect(result[:status]).to eq(:success) expect(project.name).to eq(described_class::PROJECT_NAME) - expect(project.description).to eq(described_class::PROJECT_DESCRIPTION) + expect(project.description).to eq( + 'This project is automatically generated and will be used to help monitor this GitLab instance. ' \ + "[More information](#{docs_path})" + ) + expect(File).to exist("doc/#{path}.md") end it 'adds all admins as maintainers' do @@ -105,19 +132,30 @@ describe SelfMonitoring::Project::CreateService do it 'returns error when saving project ID fails' do allow(application_setting).to receive(:update) { false } - expect(result[:status]).to eq(:error) - expect(result[:failed_step]).to eq(:save_project_id) - expect(result[:message]).to eq('Could not save project ID') + expect { result }.to raise_error(StandardError, 'Could not save project ID') end - it 'does not fail when a project already exists' do - expect(result[:status]).to eq(:success) + context 'when project already exists' do + let(:existing_group) { create(:group) } + let(:existing_project) { create(:project, namespace: existing_group) } - second_result = subject.execute + before do + admin1 = create(:user, :admin) + admin2 = create(:user, :admin) + + existing_group.add_owner(user) + existing_group.add_users([admin1, admin2], Gitlab::Access::MAINTAINER) + + application_setting.instance_administration_project_id = existing_project.id + end + + it 'does not fail' do + expect(subject).to receive(:log_error).and_call_original + expect(result[:status]).to eq(:success) - expect(second_result[:status]).to eq(:success) - expect(second_result[:project]).to eq(project) - expect(second_result[:group]).to eq(group) + expect(Project.count).to eq(1) + expect(Group.count).to eq(1) + end end context 'when local requests from hooks and services are not allowed' do @@ -138,8 +176,11 @@ describe SelfMonitoring::Project::CreateService do end context 'with non default prometheus address' do - before do - prometheus_settings.listen_address = 'https://localhost:9090' + let(:prometheus_settings) do + { + enable: true, + listen_address: 'https://localhost:9090' + } end it_behaves_like 'has prometheus service', 'https://localhost:9090' @@ -157,8 +198,11 @@ describe SelfMonitoring::Project::CreateService do end context 'when prometheus setting is disabled in gitlab.yml' do - before do - prometheus_settings.enable = false + let(:prometheus_settings) do + { + enable: false, + listen_address: 'http://localhost:9090' + } end it 'does not configure prometheus' do @@ -168,9 +212,7 @@ describe SelfMonitoring::Project::CreateService do end context 'when prometheus listen address is blank in gitlab.yml' do - before do - prometheus_settings.listen_address = '' - end + let(:prometheus_settings) { { enable: true, listen_address: '' } } it 'does not configure prometheus' do expect(result).to include(status: :success) @@ -192,11 +234,7 @@ describe SelfMonitoring::Project::CreateService do it 'returns error' do expect(subject).to receive(:log_error).and_call_original - expect(result).to eq({ - status: :error, - message: 'Could not create project', - failed_step: :create_project - }) + expect { result }.to raise_error(StandardError, 'Could not create project') end end @@ -207,26 +245,21 @@ describe SelfMonitoring::Project::CreateService do it 'returns error' do expect(subject).to receive(:log_error).and_call_original - expect(result).to eq({ - status: :error, - message: 'Could not add admins as members', - failed_step: :add_group_members - }) + expect { result }.to raise_error(StandardError, 'Could not add admins as members') end end context 'when prometheus manual configuration cannot be saved' do - before do - prometheus_settings.listen_address = 'httpinvalid://localhost:9090' + let(:prometheus_settings) do + { + enable: true, + listen_address: 'httpinvalid://localhost:9090' + } end it 'returns error' do expect(subject).to receive(:log_error).and_call_original - expect(result).to eq( - status: :error, - message: 'Could not save prometheus manual configuration', - failed_step: :add_prometheus_manual_configuration - ) + expect { result }.to raise_error(StandardError, 'Could not save prometheus manual configuration') end end end diff --git a/spec/migrations/add_gitlab_instance_administration_project_spec.rb b/spec/migrations/add_gitlab_instance_administration_project_spec.rb new file mode 100644 index 00000000000..08e20a4e8ff --- /dev/null +++ b/spec/migrations/add_gitlab_instance_administration_project_spec.rb @@ -0,0 +1,252 @@ +# frozen_string_literal: true + +require 'spec_helper' +require Rails.root.join('db', 'post_migrate', '20190801072937_add_gitlab_instance_administration_project.rb') + +describe AddGitlabInstanceAdministrationProject, :migration do + let(:application_settings) { table(:application_settings) } + let(:users) { table(:users) } + let(:projects) { table(:projects) } + let(:namespaces) { table(:namespaces) } + let(:members) { table(:members) } + + let(:service_class) do + Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService + end + + let(:prometheus_settings) do + { + enable: true, + listen_address: 'localhost:9090' + } + end + + before do + stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') + + stub_config(prometheus: prometheus_settings) + end + + describe 'down' do + let!(:application_setting) { application_settings.create! } + let!(:user) { users.create!(admin: true, email: 'admin1@example.com', projects_limit: 10, state: :active) } + + it 'deletes group and project' do + migrate! + + expect(Project.count).to eq(1) + expect(Group.count).to eq(1) + + schema_migrate_down! + + expect(Project.count).to eq(0) + expect(Group.count).to eq(0) + end + end + + describe 'up' do + context 'without application_settings' do + it 'does not fail' do + migrate! + + expect(Project.count).to eq(0) + end + end + + context 'without admin users' do + let!(:application_setting) { application_settings.create! } + + it 'does not fail' do + migrate! + + expect(Project.count).to eq(0) + end + end + + context 'with admin users' do + let(:project) { Project.last } + let(:group) { Group.last } + let!(:application_setting) { application_settings.create! } + let!(:user) { users.create!(admin: true, email: 'admin1@example.com', projects_limit: 10, state: :active) } + + before do + stub_application_setting(allow_local_requests_from_web_hooks_and_services: true) + end + + shared_examples 'has prometheus service' do |listen_address| + it do + migrate! + + prometheus = project.prometheus_service + expect(prometheus).to be_persisted + expect(prometheus).not_to eq(nil) + expect(prometheus.api_url).to eq(listen_address) + expect(prometheus.active).to eq(true) + expect(prometheus.manual_configuration).to eq(true) + end + end + + it_behaves_like 'has prometheus service', 'http://localhost:9090' + + it 'creates GitLab Instance Administrator group' do + migrate! + + expect(group).to be_persisted + expect(group.name).to eq('GitLab Instance Administrators') + expect(group.path).to start_with('gitlab-instance-administrators') + expect(group.path.split('-').last.length).to eq(8) + expect(group.visibility_level).to eq(service_class::VISIBILITY_LEVEL) + end + + it 'creates project with internal visibility' do + migrate! + + expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL) + expect(project).to be_persisted + end + + it 'creates project with correct name and description' do + migrate! + + path = 'administration/monitoring/gitlab_instance_administration_project/index' + docs_path = Rails.application.routes.url_helpers.help_page_path(path) + + expect(project.name).to eq(service_class::PROJECT_NAME) + expect(project.description).to eq( + 'This project is automatically generated and will be used to help monitor this GitLab instance. ' \ + "[More information](#{docs_path})" + ) + expect(File).to exist("doc/#{path}.md") + end + + it 'adds all admins as maintainers' do + admin1 = users.create!(admin: true, email: 'admin2@example.com', projects_limit: 10, state: :active) + admin2 = users.create!(admin: true, email: 'admin3@example.com', projects_limit: 10, state: :active) + users.create!(email: 'nonadmin1@example.com', projects_limit: 10, state: :active) + + migrate! + + expect(project.owner).to eq(group) + expect(group.members.collect(&:user).collect(&:id)).to contain_exactly(user.id, admin1.id, admin2.id) + expect(group.members.collect(&:access_level)).to contain_exactly( + Gitlab::Access::OWNER, + Gitlab::Access::MAINTAINER, + Gitlab::Access::MAINTAINER + ) + end + + it 'saves the project id' do + migrate! + + application_setting.reload + expect(application_setting.instance_administration_project_id).to eq(project.id) + end + + it 'does not fail when a project already exists' do + group = namespaces.create!( + path: 'gitlab-instance-administrators', + name: 'GitLab Instance Administrators', + type: 'Group' + ) + project = projects.create!( + namespace_id: group.id, + name: 'GitLab Instance Administration' + ) + + admin1 = users.create!(admin: true, email: 'admin4@example.com', projects_limit: 10, state: :active) + admin2 = users.create!(admin: true, email: 'admin5@example.com', projects_limit: 10, state: :active) + + members.create!( + user_id: admin1.id, + source_id: group.id, + source_type: 'Namespace', + type: 'GroupMember', + access_level: GroupMember::MAINTAINER, + notification_level: NotificationSetting.levels[:global] + ) + members.create!( + user_id: admin2.id, + source_id: group.id, + source_type: 'Namespace', + type: 'GroupMember', + access_level: GroupMember::MAINTAINER, + notification_level: NotificationSetting.levels[:global] + ) + + stub_application_setting(instance_administration_project: project) + + migrate! + + expect(Project.last.id).to eq(project.id) + expect(Group.last.id).to eq(group.id) + end + + context 'when local requests from hooks and services are not allowed' do + before do + stub_application_setting(allow_local_requests_from_web_hooks_and_services: false) + end + + it_behaves_like 'has prometheus service', 'http://localhost:9090' + + it 'does not overwrite the existing whitelist' do + application_setting.update!(outbound_local_requests_whitelist: ['example.com']) + + migrate! + + application_setting.reload + expect(application_setting.outbound_local_requests_whitelist).to contain_exactly( + 'example.com', 'localhost' + ) + end + end + + context 'with non default prometheus address' do + let(:prometheus_settings) do + { + enable: true, + listen_address: 'https://localhost:9090' + } + end + + it_behaves_like 'has prometheus service', 'https://localhost:9090' + end + + context 'when prometheus setting is not present in gitlab.yml' do + before do + allow(Gitlab.config).to receive(:prometheus).and_raise(Settingslogic::MissingSetting) + end + + it 'does not fail' do + migrate! + + expect(project.prometheus_service).to be_nil + end + end + + context 'when prometheus setting is disabled in gitlab.yml' do + let(:prometheus_settings) do + { + enable: false, + listen_address: 'localhost:9090' + } + end + + it 'does not configure prometheus' do + migrate! + + expect(project.prometheus_service).to be_nil + end + end + + context 'when prometheus listen address is blank in gitlab.yml' do + let(:prometheus_settings) { { enable: true, listen_address: '' } } + + it 'does not configure prometheus' do + migrate! + + expect(project.prometheus_service).to be_nil + end + end + end + end +end diff --git a/spec/models/analytics/cycle_analytics/project_stage_spec.rb b/spec/models/analytics/cycle_analytics/project_stage_spec.rb index 4e3923e82b1..83d6ff754c5 100644 --- a/spec/models/analytics/cycle_analytics/project_stage_spec.rb +++ b/spec/models/analytics/cycle_analytics/project_stage_spec.rb @@ -6,4 +6,18 @@ describe Analytics::CycleAnalytics::ProjectStage do describe 'associations' do it { is_expected.to belong_to(:project) } end + + it 'default stages must be valid' do + project = create(:project) + + Gitlab::Analytics::CycleAnalytics::DefaultStages.all.each do |params| + stage = described_class.new(params.merge(project: project)) + expect(stage).to be_valid + end + end + + it_behaves_like "cycle analytics stage" do + let(:parent) { create(:project) } + let(:parent_name) { :project } + end end diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb index d4e631f109b..51ed8e9421b 100644 --- a/spec/models/deployment_spec.rb +++ b/spec/models/deployment_spec.rb @@ -322,4 +322,30 @@ describe Deployment do end end end + + describe '#deployed_by' do + it 'returns the deployment user if there is no deployable' do + deployment_user = create(:user) + deployment = create(:deployment, deployable: nil, user: deployment_user) + + expect(deployment.deployed_by).to eq(deployment_user) + end + + it 'returns the deployment user if the deployable have no user' do + deployment_user = create(:user) + build = create(:ci_build, user: nil) + deployment = create(:deployment, deployable: build, user: deployment_user) + + expect(deployment.deployed_by).to eq(deployment_user) + end + + it 'returns the deployable user if there is one' do + build_user = create(:user) + deployment_user = create(:user) + build = create(:ci_build, user: build_user) + deployment = create(:deployment, deployable: build, user: deployment_user) + + expect(deployment.deployed_by).to eq(build_user) + end + end end diff --git a/spec/requests/api/pipelines_spec.rb b/spec/requests/api/pipelines_spec.rb index 35b3dd219f7..174b3214d13 100644 --- a/spec/requests/api/pipelines_spec.rb +++ b/spec/requests/api/pipelines_spec.rb @@ -17,6 +17,8 @@ describe API::Pipelines do end describe 'GET /projects/:id/pipelines ' do + it_behaves_like 'pipelines visibility table' + context 'authorized user' do it 'returns project pipelines' do get api("/projects/#{project.id}/pipelines", user) @@ -401,6 +403,15 @@ describe API::Pipelines do end describe 'GET /projects/:id/pipelines/:pipeline_id' do + it_behaves_like 'pipelines visibility table' do + let(:pipelines_api_path) do + "/projects/#{project.id}/pipelines/#{pipeline.id}" + end + + let(:api_response) { response_status == 200 ? response : json_response } + let(:response_200) { match_response_schema('public_api/v4/pipeline/detail') } + end + context 'authorized user' do it 'exposes known attributes' do get api("/projects/#{project.id}/pipelines/#{pipeline.id}", user) diff --git a/spec/rubocop/cop/migration/add_limit_to_string_columns_spec.rb b/spec/rubocop/cop/migration/add_limit_to_string_columns_spec.rb new file mode 100644 index 00000000000..97a3ae8f2bc --- /dev/null +++ b/spec/rubocop/cop/migration/add_limit_to_string_columns_spec.rb @@ -0,0 +1,268 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'rubocop' +require 'rubocop/rspec/support' + +require_relative '../../../../rubocop/cop/migration/add_limit_to_string_columns' + +describe RuboCop::Cop::Migration::AddLimitToStringColumns do + include CopHelper + + subject(:cop) { described_class.new } + + context 'in migration' do + before do + allow(cop).to receive(:in_migration?).and_return(true) + + inspect_source(migration) + end + + context 'when creating a table' do + context 'with string columns and limit' do + let(:migration) do + %q( + class CreateUsers < ActiveRecord::Migration[5.2] + DOWNTIME = false + + def change + create_table :users do |t| + t.string :username, null: false, limit: 255 + t.timestamps_with_timezone null: true + end + end + end + ) + end + + it 'register no offense' do + expect(cop.offenses.size).to eq(0) + end + + context 'with limit in a different position' do + let(:migration) do + %q( + class CreateUsers < ActiveRecord::Migration[5.2] + DOWNTIME = false + + def change + create_table :users do |t| + t.string :username, limit: 255, null: false + t.timestamps_with_timezone null: true + end + end + end + ) + end + + it 'registers an offense' do + expect(cop.offenses.size).to eq(0) + end + end + end + + context 'with string columns and no limit' do + let(:migration) do + %q( + class CreateUsers < ActiveRecord::Migration[5.2] + DOWNTIME = false + + def change + create_table :users do |t| + t.string :username, null: false + t.timestamps_with_timezone null: true + end + end + end + ) + end + + it 'registers an offense' do + expect(cop.offenses.size).to eq(1) + expect(cop.offenses.first.message) + .to eq('String columns should have a limit constraint. 255 is suggested') + end + end + + context 'with no string columns' do + let(:migration) do + %q( + class CreateMilestoneReleases < ActiveRecord::Migration[5.2] + DOWNTIME = false + + def change + create_table :milestone_releases do |t| + t.integer :milestone_id + t.integer :release_id + end + end + end + ) + end + + it 'register no offense' do + expect(cop.offenses.size).to eq(0) + end + end + end + + context 'when adding columns' do + context 'with string columns with limit' do + let(:migration) do + %q( + class AddEmailToUsers < ActiveRecord::Migration[5.2] + DOWNTIME = false + + def change + add_column :users, :email, :string, limit: 255 + end + end + ) + end + + it 'registers no offense' do + expect(cop.offenses.size).to eq(0) + end + + context 'with limit in a different position' do + let(:migration) do + %q( + class AddEmailToUsers < ActiveRecord::Migration[5.2] + DOWNTIME = false + + def change + add_column :users, :email, :string, limit: 255, default: 'example@email.com' + end + end + ) + end + + it 'registers no offense' do + expect(cop.offenses.size).to eq(0) + end + end + end + + context 'with string columns with no limit' do + let(:migration) do + %q( + class AddEmailToUsers < ActiveRecord::Migration[5.2] + DOWNTIME = false + + def change + add_column :users, :email, :string + end + end + ) + end + + it 'registers offense' do + expect(cop.offenses.size).to eq(1) + expect(cop.offenses.first.message) + .to eq('String columns should have a limit constraint. 255 is suggested') + end + end + + context 'with no string columns' do + let(:migration) do + %q( + class AddEmailToUsers < ActiveRecord::Migration[5.2] + DOWNTIME = false + + def change + add_column :users, :active, :boolean, default: false + end + end + ) + end + + it 'registers no offense' do + expect(cop.offenses.size).to eq(0) + end + end + end + + context 'with add_column_with_default' do + context 'with a limit' do + let(:migration) do + %q( + class AddRuleTypeToApprovalMergeRequestRules < ActiveRecord::Migration[5.2] + DOWNTIME = false + + def change + add_column_with_default(:approval_merge_request_rules, :rule_type, :string, limit: 2, default: 1) + end + end + ) + end + + it 'registers no offense' do + expect(cop.offenses.size).to eq(0) + end + end + + context 'without a limit' do + let(:migration) do + %q( + class AddRuleTypeToApprovalMergeRequestRules < ActiveRecord::Migration[5.2] + DOWNTIME = false + + def change + add_column_with_default(:approval_merge_request_rules, :rule_type, :string, default: 1) + end + end + ) + end + + it 'registers an offense' do + expect(cop.offenses.size).to eq(1) + end + end + end + + context 'with methods' do + let(:migration) do + %q( + class AddEmailToUsers < ActiveRecord::Migration[5.2] + DOWNTIME = false + + def change + add_column_if_table_not_exists :users, :first_name, :string, limit: 255 + search_namespace(user_name) + end + + def add_column_if_not_exists(table, name, *args) + add_column(table, name, *args) unless column_exists?(table, name) + end + + def search_namespace(username) + Uniquify.new.string(username) do |str| + query = "SELECT id FROM namespaces WHERE parent_id IS NULL AND path='#{str}' LIMIT 1" + connection.exec_query(query) + end + end + end + ) + end + + it 'registers no offense' do + expect(cop.offenses.size).to eq(0) + end + end + end + + context 'outside of migrations' do + let(:active_record_model) do + %q( + class User < ApplicationRecord + end + ) + end + + it 'registers no offense' do + inspect_source(active_record_model) + + expect(cop.offenses.size).to eq(0) + end + end +end diff --git a/spec/support/helpers/stub_configuration.rb b/spec/support/helpers/stub_configuration.rb index c8b2bf040e6..dec7898d8d2 100644 --- a/spec/support/helpers/stub_configuration.rb +++ b/spec/support/helpers/stub_configuration.rb @@ -30,6 +30,10 @@ module StubConfiguration allow(Gitlab.config.gitlab).to receive_messages(to_settings(messages)) end + def stub_config(messages) + allow(Gitlab.config).to receive_messages(to_settings(messages)) + end + def stub_default_url_options(host: "localhost", protocol: "http") url_options = { host: host, protocol: protocol } allow(Rails.application.routes).to receive(:default_url_options).and_return(url_options) diff --git a/spec/support/shared_examples/cycle_analytics_stage_examples.rb b/spec/support/shared_examples/cycle_analytics_stage_examples.rb new file mode 100644 index 00000000000..151f5325e84 --- /dev/null +++ b/spec/support/shared_examples/cycle_analytics_stage_examples.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +shared_examples_for 'cycle analytics stage' do + let(:valid_params) do + { + name: 'My Stage', + parent: parent, + start_event_identifier: :merge_request_created, + end_event_identifier: :merge_request_merged + } + end + + describe 'validation' do + it 'is valid' do + expect(described_class.new(valid_params)).to be_valid + end + + it 'validates presence of parent' do + stage = described_class.new(valid_params.except(:parent)) + + expect(stage).not_to be_valid + expect(stage.errors.details[parent_name]).to eq([{ error: :blank }]) + end + + it 'validates presence of start_event_identifier' do + stage = described_class.new(valid_params.except(:start_event_identifier)) + + expect(stage).not_to be_valid + expect(stage.errors.details[:start_event_identifier]).to eq([{ error: :blank }]) + end + + it 'validates presence of end_event_identifier' do + stage = described_class.new(valid_params.except(:end_event_identifier)) + + expect(stage).not_to be_valid + expect(stage.errors.details[:end_event_identifier]).to eq([{ error: :blank }]) + end + + it 'is invalid when end_event is not allowed for the given start_event' do + invalid_params = valid_params.merge( + start_event_identifier: :merge_request_merged, + end_event_identifier: :merge_request_created + ) + stage = described_class.new(invalid_params) + + expect(stage).not_to be_valid + expect(stage.errors.details[:end_event]).to eq([{ error: :not_allowed_for_the_given_start_event }]) + end + end + + describe '#subject_model' do + it 'infers the model from the start event' do + stage = described_class.new(valid_params) + + expect(stage.subject_model).to eq(MergeRequest) + end + end + + describe '#start_event' do + it 'builds start_event object based on start_event_identifier' do + stage = described_class.new(start_event_identifier: 'merge_request_created') + + expect(stage.start_event).to be_a_kind_of(Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestCreated) + end + end + + describe '#end_event' do + it 'builds end_event object based on end_event_identifier' do + stage = described_class.new(end_event_identifier: 'merge_request_merged') + + expect(stage.end_event).to be_a_kind_of(Gitlab::Analytics::CycleAnalytics::StageEvents::MergeRequestMerged) + end + end +end diff --git a/spec/support/shared_examples/requests/api/pipelines/visibility_table_examples.rb b/spec/support/shared_examples/requests/api/pipelines/visibility_table_examples.rb new file mode 100644 index 00000000000..dfd07176b1c --- /dev/null +++ b/spec/support/shared_examples/requests/api/pipelines/visibility_table_examples.rb @@ -0,0 +1,235 @@ +# frozen_string_literal: true + +shared_examples 'pipelines visibility table' do + using RSpec::Parameterized::TableSyntax + + let(:ci_user) { create(:user) } + let(:api_user) { user_role && ci_user } + + let(:pipelines_api_path) do + "/projects/#{project.id}/pipelines" + end + + let(:response_200) do + a_collection_containing_exactly( + a_hash_including('sha', 'ref', 'status', 'web_url', 'id' => pipeline.id) + ) + end + + let(:response_40x) do + a_hash_including('message') + end + + let(:expected_response) do + if response_status == 200 + response_200 + else + response_40x + end + end + + let(:api_response) { json_response } + + let(:visibility_levels) do + { + private: Gitlab::VisibilityLevel::PRIVATE, + internal: Gitlab::VisibilityLevel::INTERNAL, + public: Gitlab::VisibilityLevel::PUBLIC + } + end + + let(:builds_access_levels) do + { + enabled: ProjectFeature::ENABLED, + private: ProjectFeature::PRIVATE + } + end + + let(:project_attributes) do + { + visibility_level: visibility_levels[visibility_level], + public_builds: public_builds + } + end + + let(:project_feature_attributes) do + { + builds_access_level: builds_access_levels[builds_access_level] + } + end + + where(:visibility_level, :builds_access_level, :public_builds, :is_admin, :user_role, :response_status) do + :private | :enabled | true | true | :non_member | 200 + :private | :enabled | true | true | :guest | 200 + :private | :enabled | true | true | :reporter | 200 + :private | :enabled | true | true | :developer | 200 + :private | :enabled | true | true | :maintainer | 200 + + :private | :enabled | true | false | nil | 404 + :private | :enabled | true | false | :non_member | 404 + :private | :enabled | true | false | :guest | 200 + :private | :enabled | true | false | :reporter | 200 + :private | :enabled | true | false | :developer | 200 + :private | :enabled | true | false | :maintainer | 200 + + :private | :enabled | false | true | :non_member | 200 + :private | :enabled | false | true | :guest | 200 + :private | :enabled | false | true | :reporter | 200 + :private | :enabled | false | true | :developer | 200 + :private | :enabled | false | true | :maintainer | 200 + + :private | :enabled | false | false | nil | 404 + :private | :enabled | false | false | :non_member | 404 + :private | :enabled | false | false | :guest | 403 + :private | :enabled | false | false | :reporter | 200 + :private | :enabled | false | false | :developer | 200 + :private | :enabled | false | false | :maintainer | 200 + + :private | :private | true | true | :non_member | 200 + :private | :private | true | true | :guest | 200 + :private | :private | true | true | :reporter | 200 + :private | :private | true | true | :developer | 200 + :private | :private | true | true | :maintainer | 200 + + :private | :private | true | false | nil | 404 + :private | :private | true | false | :non_member | 404 + :private | :private | true | false | :guest | 200 + :private | :private | true | false | :reporter | 200 + :private | :private | true | false | :developer | 200 + :private | :private | true | false | :maintainer | 200 + + :private | :private | false | true | :non_member | 200 + :private | :private | false | true | :guest | 200 + :private | :private | false | true | :reporter | 200 + :private | :private | false | true | :developer | 200 + :private | :private | false | true | :maintainer | 200 + + :private | :private | false | false | nil | 404 + :private | :private | false | false | :non_member | 404 + :private | :private | false | false | :guest | 403 + :private | :private | false | false | :reporter | 200 + :private | :private | false | false | :developer | 200 + :private | :private | false | false | :maintainer | 200 + + :internal | :enabled | true | true | :non_member | 200 + :internal | :enabled | true | true | :guest | 200 + :internal | :enabled | true | true | :reporter | 200 + :internal | :enabled | true | true | :developer | 200 + :internal | :enabled | true | true | :maintainer | 200 + + :internal | :enabled | true | false | nil | 404 + :internal | :enabled | true | false | :non_member | 200 + :internal | :enabled | true | false | :guest | 200 + :internal | :enabled | true | false | :reporter | 200 + :internal | :enabled | true | false | :developer | 200 + :internal | :enabled | true | false | :maintainer | 200 + + :internal | :enabled | false | true | :non_member | 200 + :internal | :enabled | false | true | :guest | 200 + :internal | :enabled | false | true | :reporter | 200 + :internal | :enabled | false | true | :developer | 200 + :internal | :enabled | false | true | :maintainer | 200 + + :internal | :enabled | false | false | nil | 404 + :internal | :enabled | false | false | :non_member | 403 + :internal | :enabled | false | false | :guest | 403 + :internal | :enabled | false | false | :reporter | 200 + :internal | :enabled | false | false | :developer | 200 + :internal | :enabled | false | false | :maintainer | 200 + + :internal | :private | true | true | :non_member | 200 + :internal | :private | true | true | :guest | 200 + :internal | :private | true | true | :reporter | 200 + :internal | :private | true | true | :developer | 200 + :internal | :private | true | true | :maintainer | 200 + + :internal | :private | true | false | nil | 404 + :internal | :private | true | false | :non_member | 403 + :internal | :private | true | false | :guest | 200 + :internal | :private | true | false | :reporter | 200 + :internal | :private | true | false | :developer | 200 + :internal | :private | true | false | :maintainer | 200 + + :internal | :private | false | true | :non_member | 200 + :internal | :private | false | true | :guest | 200 + :internal | :private | false | true | :reporter | 200 + :internal | :private | false | true | :developer | 200 + :internal | :private | false | true | :maintainer | 200 + + :internal | :private | false | false | nil | 404 + :internal | :private | false | false | :non_member | 403 + :internal | :private | false | false | :guest | 403 + :internal | :private | false | false | :reporter | 200 + :internal | :private | false | false | :developer | 200 + :internal | :private | false | false | :maintainer | 200 + + :public | :enabled | true | true | :non_member | 200 + :public | :enabled | true | true | :guest | 200 + :public | :enabled | true | true | :reporter | 200 + :public | :enabled | true | true | :developer | 200 + :public | :enabled | true | true | :maintainer | 200 + + :public | :enabled | true | false | nil | 200 + :public | :enabled | true | false | :non_member | 200 + :public | :enabled | true | false | :guest | 200 + :public | :enabled | true | false | :reporter | 200 + :public | :enabled | true | false | :developer | 200 + :public | :enabled | true | false | :maintainer | 200 + + :public | :enabled | false | true | :non_member | 200 + :public | :enabled | false | true | :guest | 200 + :public | :enabled | false | true | :reporter | 200 + :public | :enabled | false | true | :developer | 200 + :public | :enabled | false | true | :maintainer | 200 + + :public | :enabled | false | false | nil | 403 + :public | :enabled | false | false | :non_member | 403 + :public | :enabled | false | false | :guest | 403 + :public | :enabled | false | false | :reporter | 200 + :public | :enabled | false | false | :developer | 200 + :public | :enabled | false | false | :maintainer | 200 + + :public | :private | true | true | :non_member | 200 + :public | :private | true | true | :guest | 200 + :public | :private | true | true | :reporter | 200 + :public | :private | true | true | :developer | 200 + :public | :private | true | true | :maintainer | 200 + + :public | :private | true | false | nil | 403 + :public | :private | true | false | :non_member | 403 + :public | :private | true | false | :guest | 200 + :public | :private | true | false | :reporter | 200 + :public | :private | true | false | :developer | 200 + :public | :private | true | false | :maintainer | 200 + + :public | :private | false | true | :non_member | 200 + :public | :private | false | true | :guest | 200 + :public | :private | false | true | :reporter | 200 + :public | :private | false | true | :developer | 200 + :public | :private | false | true | :maintainer | 200 + + :public | :private | false | false | nil | 403 + :public | :private | false | false | :non_member | 403 + :public | :private | false | false | :guest | 403 + :public | :private | false | false | :reporter | 200 + :public | :private | false | false | :developer | 200 + :public | :private | false | false | :maintainer | 200 + end + + with_them do + before do + ci_user.update!(admin: is_admin) if user_role + + project.update!(project_attributes) + project.project_feature.update!(project_feature_attributes) + project.add_role(ci_user, user_role) if user_role && user_role != :non_member + + get api(pipelines_api_path, api_user) + end + + it do + expect(response).to have_gitlab_http_status(response_status) + expect(api_response).to match(expected_response) + end + end +end diff --git a/yarn.lock b/yarn.lock index f1b819eb52f..ef66861488c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1021,6 +1021,11 @@ vue "^2.6.10" vue-loader "^15.4.2" +"@gitlab/visual-review-tools@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@gitlab/visual-review-tools/-/visual-review-tools-1.0.0.tgz#6012e0a19797c1f5dad34ccf4dacdaf38e400a73" + integrity sha512-xMvz9IwrXisQ1MH+Tj6lfbQcQSiQy88nTPuQV6OTLBGuV+vIQeVwXeIkQeTKuSpd0GqZvigPdRqxyQCa3blpIg== + "@gitlab/vue-toasted@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@gitlab/vue-toasted/-/vue-toasted-1.2.1.tgz#f407b5aa710863e5b7f021f4a1f66160331ab263" |