diff options
153 files changed, 1767 insertions, 1010 deletions
diff --git a/.rubocop.yml b/.rubocop.yml index aa49f41ebf4..e5fe527e611 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -75,6 +75,7 @@ Naming/FileName: - 'qa/spec/**/*' - 'qa/qa/specs/**/*' - 'qa/bin/*' + - 'ee/bin/*' - 'config/**/*' - 'ee/config/**/*' - 'lib/generators/**/*' diff --git a/CHANGELOG.md b/CHANGELOG.md index c512585ff6a..bd4c0e479cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,26 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 11.10.1 (2019-04-23) + +### Fixed (2 changes) + +- Upgrade Gitaly to 1.34.0. !27494 +- Fix filtering of labels from system note link. !27507 + +### Changed (1 change) + +- Disable just-in-time Kubernetes resource creation for project level clusters. !27352 + +### Performance (1 change) + +- Bring back Rugged implementation of ListCommitsByOid. !27441 + +### Other (1 change) + +- Bump required Ruby version check to 2.5.3. !27495 + + ## 11.10.0 (2019-04-22) ### Security (9 changes) @@ -233,6 +253,13 @@ entry. - Removes EE differences for environment_item.vue. +## 11.9.9 (2019-04-23) + +### Performance (1 change) + +- Bring back Rugged implementation of ListCommitsByOid. !27441 + + ## 11.9.8 (2019-04-11) ### Deprecated (1 change) @@ -584,6 +611,28 @@ entry. - Removes EE differences for jobs/getters.js. +## 11.8.8 (2019-04-23) + +### Fixed (5 changes) + +- Bring back Rugged implementation of find_commit. !25477 +- Fix bug in BitBucket imports with SHA shorter than 40 chars. !26050 +- Fix health checks not working behind load balancers. !26055 +- Fix error creating a merge request when diff includes a null byte. !26190 +- Avoid excessive recursive calls with Rugged TreeEntries. !26813 + +### Performance (1 change) + +- Bring back Rugged implementation of ListCommitsByOid. !27441 + +### Other (4 changes) + +- Bring back Rugged implementation of GetTreeEntries. !25674 +- Bring back Rugged implementation of CommitIsAncestor. !25702 +- Bring back Rugged implementation of TreeEntry. !25706 +- Bring back Rugged implementation of commit_tree_entry. !25896 + + ## 11.8.3 (2019-03-19) ### Security (1 change) @@ -853,6 +902,25 @@ entry. - Creates mixin to reduce code duplication between CE and EE in graph component. +## 11.7.12 (2019-04-23) + +### Fixed (2 changes) + +- Bring back Rugged implementation of find_commit. !25477 +- Avoid excessive recursive calls with Rugged TreeEntries. !26813 + +### Performance (1 change) + +- Bring back Rugged implementation of ListCommitsByOid. !27441 + +### Other (4 changes) + +- Bring back Rugged implementation of GetTreeEntries. !25674 +- Bring back Rugged implementation of CommitIsAncestor. !25702 +- Bring back Rugged implementation of TreeEntry. !25706 +- Bring back Rugged implementation of commit_tree_entry. !25896 + + ## 11.7.11 (2019-04-09) - No changes. @@ -1128,6 +1196,30 @@ entry. - Update url placeholder for the sentry configuration page. !24338 +## 11.6.11 (2019-04-23) + +### Security (2 changes) + +- Fixed ability to see private groups by users not belonging to given group. +- Fix XSS in resolve conflicts form. + +### Fixed (2 changes) + +- Bring back Rugged implementation of find_commit. !25477 +- Avoid excessive recursive calls with Rugged TreeEntries. !26813 + +### Performance (1 change) + +- Bring back Rugged implementation of ListCommitsByOid. !27441 + +### Other (4 changes) + +- Bring back Rugged implementation of GetTreeEntries. !25674 +- Bring back Rugged implementation of CommitIsAncestor. !25702 +- Bring back Rugged implementation of TreeEntry. !25706 +- Bring back Rugged implementation of commit_tree_entry. !25896 + + ## 11.6.10 (2019-02-28) ### Security (21 changes) @@ -1499,6 +1591,25 @@ entry. - Enable Rubocop on lib/gitlab. (gfyoung) +## 11.5.11 (2019-04-23) + +### Fixed (2 changes) + +- Bring back Rugged implementation of find_commit. !25477 +- Avoid excessive recursive calls with Rugged TreeEntries. !26813 + +### Performance (1 change) + +- Bring back Rugged implementation of ListCommitsByOid. !27441 + +### Other (4 changes) + +- Bring back Rugged implementation of GetTreeEntries. !25674 +- Bring back Rugged implementation of CommitIsAncestor. !25702 +- Bring back Rugged implementation of TreeEntry. !25706 +- Bring back Rugged implementation of commit_tree_entry. !25896 + + ## 11.5.8 (2019-01-28) ### Security (21 changes) @@ -1,6 +1,6 @@ source 'https://rubygems.org' -gem 'rails', '5.0.7.2' +gem 'rails', '5.1.7' # Improves copy-on-write performance for MRI gem 'nakayoshi_fork', '~> 0.0.4' @@ -18,7 +18,7 @@ gem 'mysql2', '~> 0.4.10', group: :mysql gem 'pg', '~> 1.1', group: :postgres gem 'rugged', '~> 0.28' -gem 'grape-path-helpers', '~> 1.0' +gem 'grape-path-helpers', '~> 1.1' gem 'faraday', '~> 0.12' @@ -158,7 +158,7 @@ gem 'state_machines-activerecord', '~> 0.5.1' gem 'acts-as-taggable-on', '~> 6.0' # Background jobs -gem 'sidekiq', '~> 5.2.1' +gem 'sidekiq', '~> 5.2.7' gem 'sidekiq-cron', '~> 1.0' gem 'redis-namespace', '~> 1.6.0' gem 'gitlab-sidekiq-fetcher', '~> 0.4.0', require: 'sidekiq-reliable-fetch' diff --git a/Gemfile.lock b/Gemfile.lock index b59c5faa43b..da8f8db9528 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,41 +4,41 @@ GEM RedCloth (4.3.2) abstract_type (0.0.7) ace-rails-ap (4.1.2) - actioncable (5.0.7.2) - actionpack (= 5.0.7.2) - nio4r (>= 1.2, < 3.0) + actioncable (5.1.7) + actionpack (= 5.1.7) + nio4r (~> 2.0) websocket-driver (~> 0.6.1) - actionmailer (5.0.7.2) - actionpack (= 5.0.7.2) - actionview (= 5.0.7.2) - activejob (= 5.0.7.2) + actionmailer (5.1.7) + actionpack (= 5.1.7) + actionview (= 5.1.7) + activejob (= 5.1.7) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.0.7.2) - actionview (= 5.0.7.2) - activesupport (= 5.0.7.2) + actionpack (5.1.7) + actionview (= 5.1.7) + activesupport (= 5.1.7) rack (~> 2.0) - rack-test (~> 0.6.3) + rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.0.7.2) - activesupport (= 5.0.7.2) + actionview (5.1.7) + activesupport (= 5.1.7) builder (~> 3.1) - erubis (~> 2.7.0) + erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) - activejob (5.0.7.2) - activesupport (= 5.0.7.2) + activejob (5.1.7) + activesupport (= 5.1.7) globalid (>= 0.3.6) - activemodel (5.0.7.2) - activesupport (= 5.0.7.2) - activerecord (5.0.7.2) - activemodel (= 5.0.7.2) - activesupport (= 5.0.7.2) - arel (~> 7.0) + activemodel (5.1.7) + activesupport (= 5.1.7) + activerecord (5.1.7) + activemodel (= 5.1.7) + activesupport (= 5.1.7) + arel (~> 8.0) activerecord_sane_schema_dumper (1.0) rails (>= 5, < 6) - activesupport (5.0.7.2) + activesupport (5.1.7) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) @@ -52,7 +52,7 @@ GEM public_suffix (>= 2.0.2, < 4.0) aes_key_wrap (1.0.1) akismet (2.0.0) - arel (7.1.4) + arel (8.0.0) asana (0.8.1) faraday (~> 0.9) faraday_middleware (~> 0.9) @@ -185,8 +185,7 @@ GEM mail (~> 2.7) encryptor (3.0.0) equalizer (0.0.11) - erubi (1.7.1) - erubis (2.7.0) + erubi (1.8.0) escape_utils (1.2.1) et-orbi (1.1.7) tzinfo @@ -257,8 +256,8 @@ GEM fog-xml (0.1.3) fog-core nokogiri (>= 1.5.11, < 2.0.0) - font-awesome-rails (4.7.0.1) - railties (>= 3.2, < 5.1) + font-awesome-rails (4.7.0.4) + railties (>= 3.2, < 6.0) foreman (0.84.0) thor (~> 0.19.1) formatador (0.2.5) @@ -339,8 +338,8 @@ GEM grape-entity (0.7.1) activesupport (>= 4.0) multi_json (>= 1.3.2) - grape-path-helpers (1.0.6) - activesupport (>= 4, < 5.1) + grape-path-helpers (1.1.0) + activesupport grape (~> 1.0) rake (~> 12) grape_logging (1.7.0) @@ -658,19 +657,19 @@ GEM rack rack-proxy (0.6.0) rack - rack-test (0.6.3) - rack (>= 1.0) - rails (5.0.7.2) - actioncable (= 5.0.7.2) - actionmailer (= 5.0.7.2) - actionpack (= 5.0.7.2) - actionview (= 5.0.7.2) - activejob (= 5.0.7.2) - activemodel (= 5.0.7.2) - activerecord (= 5.0.7.2) - activesupport (= 5.0.7.2) + rack-test (1.1.0) + rack (>= 1.0, < 3) + rails (5.1.7) + actioncable (= 5.1.7) + actionmailer (= 5.1.7) + actionpack (= 5.1.7) + actionview (= 5.1.7) + activejob (= 5.1.7) + activemodel (= 5.1.7) + activerecord (= 5.1.7) + activesupport (= 5.1.7) bundler (>= 1.3.0) - railties (= 5.0.7.2) + railties (= 5.1.7) sprockets-rails (>= 2.0.0) rails-controller-testing (1.0.2) actionpack (~> 5.x, >= 5.0.1) @@ -684,9 +683,9 @@ GEM rails-i18n (5.1.1) i18n (>= 0.7, < 2) railties (>= 5.0, < 6) - railties (5.0.7.2) - actionpack (= 5.0.7.2) - activesupport (= 5.0.7.2) + railties (5.1.7) + actionpack (= 5.1.7) + activesupport (= 5.1.7) method_source rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) @@ -846,7 +845,7 @@ GEM rack shoulda-matchers (3.1.2) activesupport (>= 4.0.0) - sidekiq (5.2.5) + sidekiq (5.2.7) connection_pool (~> 2.2, >= 2.2.2) rack (>= 1.5.0) rack-protection (>= 1.5.0) @@ -1067,7 +1066,7 @@ DEPENDENCIES gpgme (~> 2.0.18) grape (~> 1.1.0) grape-entity (~> 0.7.1) - grape-path-helpers (~> 1.0) + grape-path-helpers (~> 1.1) grape_logging (~> 1.7) graphiql-rails (~> 1.4.10) graphql (~> 1.8.0) @@ -1142,7 +1141,7 @@ DEPENDENCIES rack-cors (~> 1.0.0) rack-oauth2 (~> 1.9.3) rack-proxy (~> 0.6.0) - rails (= 5.0.7.2) + rails (= 5.1.7) rails-controller-testing rails-i18n (~> 5.1) rainbow (~> 3.0) @@ -1183,7 +1182,7 @@ DEPENDENCIES settingslogic (~> 2.0.9) sham_rack (~> 1.3.6) shoulda-matchers (~> 3.1.2) - sidekiq (~> 5.2.1) + sidekiq (~> 5.2.7) sidekiq-cron (~> 1.0) simple_po_parser (~> 1.1.2) simplecov (~> 0.14.0) diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js index 6cf77705847..7e5d0e0f888 100644 --- a/app/assets/javascripts/boards/models/list.js +++ b/app/assets/javascripts/boards/models/list.js @@ -90,6 +90,7 @@ class List { this.id = data.id; this.type = data.list_type; this.position = data.position; + this.label = data.label; return this.getIssues(); }); diff --git a/app/assets/javascripts/ide/components/new_dropdown/upload.vue b/app/assets/javascripts/ide/components/new_dropdown/upload.vue index ec759043efc..188518dd419 100644 --- a/app/assets/javascripts/ide/components/new_dropdown/upload.vue +++ b/app/assets/javascripts/ide/components/new_dropdown/upload.vue @@ -57,6 +57,8 @@ export default { type: 'blob', content: result, base64: !isText, + binary: !isText, + rawPath: !isText ? target.result : '', }); }, readFile(file) { diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue index c7798ad0cd7..e15b2a6f76b 100644 --- a/app/assets/javascripts/ide/components/repo_editor.vue +++ b/app/assets/javascripts/ide/components/repo_editor.vue @@ -1,5 +1,6 @@ <script> import { mapState, mapGetters, mapActions } from 'vuex'; +import { viewerInformationForPath } from '~/vue_shared/components/content_viewer/lib/viewer_utils'; import flash from '~/flash'; import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue'; import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue'; @@ -56,6 +57,10 @@ export default { active: this.file.viewMode === 'preview', }; }, + fileType() { + const info = viewerInformationForPath(this.file.path); + return (info && info.id) || ''; + }, }, watch: { file(newVal, oldVal) { @@ -258,6 +263,7 @@ export default { :path="file.rawPath || file.path" :file-size="file.size" :project-path="file.projectId" + :type="fileType" /> <diff-viewer v-if="showDiffViewer" diff --git a/app/assets/javascripts/ide/constants.js b/app/assets/javascripts/ide/constants.js index 7c560c89695..e30670e119f 100644 --- a/app/assets/javascripts/ide/constants.js +++ b/app/assets/javascripts/ide/constants.js @@ -72,4 +72,11 @@ export const modalTypes = { tree: 'tree', }; +export const commitActionTypes = { + move: 'move', + delete: 'delete', + create: 'create', + update: 'update', +}; + export const packageJsonPath = 'package.json'; diff --git a/app/assets/javascripts/ide/lib/files.js b/app/assets/javascripts/ide/lib/files.js index df100f753d7..b8abaa41f23 100644 --- a/app/assets/javascripts/ide/lib/files.js +++ b/app/assets/javascripts/ide/lib/files.js @@ -22,6 +22,8 @@ export const decorateFiles = ({ tempFile = false, content = '', base64 = false, + binary = false, + rawPath = '', }) => { const treeList = []; const entries = {}; @@ -90,6 +92,8 @@ export const decorateFiles = ({ changed: tempFile, content, base64, + binary, + rawPath, previewMode: viewerInformationForPath(name), parentPath, }); diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js index 7b660bda081..fd678e6e10c 100644 --- a/app/assets/javascripts/ide/stores/actions.js +++ b/app/assets/javascripts/ide/stores/actions.js @@ -53,7 +53,7 @@ export const setResizingStatus = ({ commit }, resizing) => { export const createTempEntry = ( { state, commit, dispatch }, - { name, type, content = '', base64 = false }, + { name, type, content = '', base64 = false, binary = false, rawPath = '' }, ) => new Promise(resolve => { const fullName = name.slice(-1) !== '/' && type === 'tree' ? `${name}/` : name; @@ -79,8 +79,10 @@ export const createTempEntry = ( branchId: state.currentBranchId, type, tempFile: true, - base64, content, + base64, + binary, + rawPath, }); const { file, parentPath } = data; diff --git a/app/assets/javascripts/ide/stores/modules/file_templates/actions.js b/app/assets/javascripts/ide/stores/modules/file_templates/actions.js index b7090e09daf..59ead8a3dcf 100644 --- a/app/assets/javascripts/ide/stores/modules/file_templates/actions.js +++ b/app/assets/javascripts/ide/stores/modules/file_templates/actions.js @@ -23,22 +23,27 @@ export const receiveTemplateTypesError = ({ commit, dispatch }) => { export const receiveTemplateTypesSuccess = ({ commit }, templates) => commit(types.RECEIVE_TEMPLATE_TYPES_SUCCESS, templates); -export const fetchTemplateTypes = ({ dispatch, state, rootState }, page = 1) => { +export const fetchTemplateTypes = ({ dispatch, state, rootState }) => { if (!Object.keys(state.selectedTemplateType).length) return Promise.reject(); dispatch('requestTemplateTypes'); - return Api.projectTemplates(rootState.currentProjectId, state.selectedTemplateType.key, { page }) - .then(({ data, headers }) => { - const nextPage = parseInt(normalizeHeaders(headers)['X-NEXT-PAGE'], 10); + const fetchPages = (page = 1, prev = []) => + Api.projectTemplates(rootState.currentProjectId, state.selectedTemplateType.key, { + page, + per_page: 100, + }) + .then(({ data, headers }) => { + const nextPage = parseInt(normalizeHeaders(headers)['X-NEXT-PAGE'], 10); + const nextData = prev.concat(data); - dispatch('receiveTemplateTypesSuccess', data); + dispatch('receiveTemplateTypesSuccess', nextData); - if (nextPage) { - dispatch('fetchTemplateTypes', nextPage); - } - }) - .catch(() => dispatch('receiveTemplateTypesError')); + return nextPage ? fetchPages(nextPage, nextData) : nextData; + }) + .catch(() => dispatch('receiveTemplateTypesError')); + + return fetchPages(); }; export const setSelectedTemplateType = ({ commit, dispatch, rootGetters }, type) => { diff --git a/app/assets/javascripts/ide/stores/modules/file_templates/mutations.js b/app/assets/javascripts/ide/stores/modules/file_templates/mutations.js index 25a65b047f1..7fc1c9134a7 100644 --- a/app/assets/javascripts/ide/stores/modules/file_templates/mutations.js +++ b/app/assets/javascripts/ide/stores/modules/file_templates/mutations.js @@ -3,13 +3,14 @@ import * as types from './mutation_types'; export default { [types.REQUEST_TEMPLATE_TYPES](state) { state.isLoading = true; + state.templates = []; }, [types.RECEIVE_TEMPLATE_TYPES_ERROR](state) { state.isLoading = false; }, [types.RECEIVE_TEMPLATE_TYPES_SUCCESS](state, templates) { state.isLoading = false; - state.templates = state.templates.concat(templates); + state.templates = templates; }, [types.SET_SELECTED_TEMPLATE_TYPE](state, type) { state.selectedTemplateType = type; diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js index 3ab8f3f11be..bcc9ca60d9b 100644 --- a/app/assets/javascripts/ide/stores/utils.js +++ b/app/assets/javascripts/ide/stores/utils.js @@ -1,3 +1,5 @@ +import { commitActionTypes } from '../constants'; + export const dataStructure = () => ({ id: '', // Key will contain a mixture of ID and path @@ -69,6 +71,8 @@ export const decorateData = entity => { changed = false, parentTreeUrl = '', base64 = false, + binary = false, + rawPath = '', previewMode, file_lock, html, @@ -92,6 +96,8 @@ export const decorateData = entity => { renderError, content, base64, + binary, + rawPath, previewMode, file_lock, html, @@ -110,14 +116,14 @@ export const setPageTitle = title => { export const commitActionForFile = file => { if (file.prevPath) { - return 'move'; + return commitActionTypes.move; } else if (file.deleted) { - return 'delete'; + return commitActionTypes.delete; } else if (file.tempFile) { - return 'create'; + return commitActionTypes.create; } - return 'update'; + return commitActionTypes.update; }; export const getCommitFiles = stagedFiles => diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue index 89563711bcd..1e47bef7b61 100644 --- a/app/assets/javascripts/notes/components/noteable_discussion.vue +++ b/app/assets/javascripts/notes/components/noteable_discussion.vue @@ -454,8 +454,13 @@ Please check your network connection and try again.`; </component> </template> </ul> + <draft-note + v-if="showDraft(discussion.reply_id)" + :key="`draft_${discussion.id}`" + :draft="draftForDiscussion(discussion.reply_id)" + /> <div - v-if="isExpanded || !hasReplies" + v-else-if="isExpanded || !hasReplies" :class="{ 'is-replying': isReplying }" class="discussion-reply-holder" > diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/content_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/content_viewer.vue index 4155e1bab9c..1e6f4c376c1 100644 --- a/app/assets/javascripts/vue_shared/components/content_viewer/content_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/content_viewer/content_viewer.vue @@ -1,5 +1,4 @@ <script> -import { viewerInformationForPath } from './lib/viewer_utils'; import MarkdownViewer from './viewers/markdown_viewer.vue'; import ImageViewer from './viewers/image_viewer.vue'; import DownloadViewer from './viewers/download_viewer.vue'; @@ -24,15 +23,18 @@ export default { required: false, default: '', }, + type: { + type: String, + required: false, + default: '', + }, }, computed: { viewer() { if (!this.path) return null; + if (!this.type) return DownloadViewer; - const previewInfo = viewerInformationForPath(this.path); - if (!previewInfo) return DownloadViewer; - - switch (previewInfo.id) { + switch (this.type) { case 'markdown': return MarkdownViewer; case 'image': diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue index ad3b3b81ac5..8d77b156aa4 100644 --- a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue @@ -58,12 +58,11 @@ export default { const moveX = e.pageX || e.touches[0].pageX; let leftValue = moveX - this.$refs.swipeFrame.getBoundingClientRect().left; - const spaceLeft = 20; const { clientWidth } = this.$refs.swipeFrame; if (leftValue <= 0) { leftValue = 0; - } else if (leftValue > clientWidth - spaceLeft) { - leftValue = clientWidth - spaceLeft; + } else if (leftValue > clientWidth) { + leftValue = clientWidth; } this.swipeWrapWidth = (leftValue / clientWidth) * 100; @@ -80,7 +79,7 @@ export default { document.body.removeEventListener('touchmove', this.dragMove); }, prepareSwipe() { - if (this.swipeOldImgInfo && this.swipeNewImgInfo) { + if (this.swipeOldImgInfo && this.swipeNewImgInfo && this.swipeOldImgInfo.renderedWidth > 0) { // Add 2 for border width this.swipeMaxWidth = Math.max(this.swipeOldImgInfo.renderedWidth, this.swipeNewImgInfo.renderedWidth) + 2; @@ -101,6 +100,8 @@ export default { }, resize: _.throttle(function throttledResize() { this.swipeBarPos = 0; + this.swipeWrapWidth = 0; + this.prepareSwipe(); }, 400), }, }; @@ -111,6 +112,8 @@ export default { <div ref="swipeFrame" :style="{ + width: swipeMaxPixelWidth, + height: swipeMaxPixelHeight, 'user-select': dragging ? 'none' : null, }" class="swipe-frame" diff --git a/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue b/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue index bf96ce0bafb..ffde55bf083 100644 --- a/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue +++ b/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue @@ -45,10 +45,10 @@ export default { 'issuable-info-container': !canReorder, 'card-body': canReorder, }" - class="item-body" + class="item-body d-flex align-items-center p-2 p-lg-3 p-xl-2 pl-xl-3" > - <div class="item-contents"> - <div class="item-title d-flex align-items-center"> + <div class="item-contents d-flex align-items-center flex-wrap flex-grow-1 flex-xl-nowrap"> + <div class="item-title d-flex align-items-center mb-1 mb-xl-0"> <icon v-if="hasState" v-tooltip @@ -65,13 +65,15 @@ export default { name="eye-slash" :size="16" :title="__('Confidential')" - class="confidential-icon append-right-4" + class="confidential-icon append-right-4 align-self-baseline align-self-md-auto mt-xl-0" :aria-label="__('Confidential')" /> <a :href="computedPath" class="sortable-link">{{ title }}</a> </div> - <div class="item-meta"> - <div class="d-flex align-items-center item-path-id"> + <div class="item-meta d-flex flex-wrap mt-xl-0 justify-content-xl-end flex-xl-nowrap"> + <div + class="d-flex align-items-center item-path-id order-md-0 mt-md-0 mt-1 ml-xl-2 mr-xl-auto" + > <icon v-if="hasState" v-tooltip @@ -88,7 +90,9 @@ export default { }}</span> {{ pathIdSeparator }}{{ itemId }} </div> - <div class="item-meta-child d-flex align-items-center"> + <div + class="item-meta-child d-flex align-items-center order-0 flex-wrap mr-md-1 ml-md-auto ml-xl-2 flex-xl-nowrap" + > <span v-if="hasPipeline" class="mr-ci-status pr-2"> <a :href="pipelineStatus.details_path"> <ci-icon v-gl-tooltip :status="pipelineStatus" :title="pipelineStatusTooltip" /> @@ -105,7 +109,7 @@ export default { <issue-assignees v-if="assignees.length" :assignees="assignees" - class="item-assignees d-inline-flex" + class="item-assignees d-inline-flex align-items-center align-self-end ml-auto ml-md-0 mb-md-0 order-2 flex-xl-grow-0 mt-xl-0 mr-xl-1" /> </div> </div> @@ -115,7 +119,7 @@ export default { v-tooltip :disabled="removeDisabled" type="button" - class="btn btn-default btn-svg btn-item-remove js-issue-item-remove-button qa-remove-issue-button" + class="btn btn-default btn-svg btn-item-remove js-issue-item-remove-button qa-remove-issue-button mr-xl-0 align-self-xl-center" title="Remove" aria-label="Remove" @click="onRemoveRequest" diff --git a/app/assets/stylesheets/components/related_items_list.scss b/app/assets/stylesheets/components/related_items_list.scss index 0fdb11d311a..7f9cf1266b1 100644 --- a/app/assets/stylesheets/components/related_items_list.scss +++ b/app/assets/stylesheets/components/related_items_list.scss @@ -16,10 +16,7 @@ $item-weight-max-width: 48px; } .item-body { - display: flex; position: relative; - align-items: center; - padding: $gl-padding-8; line-height: $gl-line-height; .issue-token-state-icon-open { @@ -49,14 +46,11 @@ $item-weight-max-width: 48px; } .confidential-icon { - align-self: baseline; color: $orange-600; - margin-right: $gl-padding-4; } .item-title { flex-basis: 100%; - margin-bottom: $gl-padding-8; font-size: $gl-font-size-small; &.mr-title { @@ -80,24 +74,12 @@ $item-weight-max-width: 48px; } } -.item-contents { - display: flex; - align-items: center; - flex-wrap: wrap; - flex-grow: 1; -} - .item-meta { - display: flex; - flex-wrap: wrap; flex-basis: 100%; font-size: $gl-font-size-small; color: $gl-text-color-secondary; .item-meta-child { - order: 0; - display: flex; - flex-wrap: wrap; flex-basis: 100%; } @@ -117,16 +99,10 @@ $item-weight-max-width: 48px; } .item-weight { - margin-right: 0; max-width: $item-weight-max-width; } .item-assignees { - order: 2; - align-self: flex-end; - align-items: center; - margin-left: auto; - .user-avatar-link { margin-right: -$gl-padding-4; @@ -162,7 +138,6 @@ $item-weight-max-width: 48px; } .item-path-id { - margin-top: $gl-padding-4; font-size: $gl-font-size-xs; white-space: nowrap; @@ -176,8 +151,10 @@ $item-weight-max-width: 48px; display: block; } - &:not(.mr-item-path) { - order: 1; + @include media-breakpoint-down(sm) { + &:not(.mr-item-path) { + order: 1; + } } } @@ -219,15 +196,14 @@ $item-weight-max-width: 48px; .item-body { .item-contents { min-width: 0; + } - .item-title { - flex-basis: unset; - // 95% because we compensate - // for remove button which is - // positioned absolutely - width: 95%; - margin-bottom: $gl-padding-4; - } + .item-title { + flex-basis: unset; + // 95% because we compensate + // for remove button which is + // positioned absolutely + width: 95%; } .btn-item-remove { @@ -236,34 +212,19 @@ $item-weight-max-width: 48px; } .item-meta { - .item-path-id { - order: 0; - margin-top: 0; - } - .item-meta-child { flex-basis: unset; - margin-left: auto; - margin-right: $gl-padding-4; ~ .item-assignees { margin-left: $gl-padding-4; } } - - .item-assignees { - margin-bottom: 0; - margin-left: 0; - order: 2; - } } } /* Medium devices (desktops, 992px and up) */ @include media-breakpoint-up(lg) { .item-body { - padding: $gl-padding; - .item-title { font-size: $gl-font-size; } @@ -277,12 +238,7 @@ $item-weight-max-width: 48px; /* Large devices (large desktops, 1200px and up) */ @include media-breakpoint-up(xl) { .item-body { - padding: $gl-padding-8; - padding-left: $gl-padding; - .item-title { - display: flex; - margin-bottom: 0; min-width: 0; width: auto; flex-basis: unset; @@ -293,43 +249,18 @@ $item-weight-max-width: 48px; display: block; margin-right: $gl-padding-8; } - - .confidential-icon { - align-self: auto; - margin-top: 0; - } } } .item-contents { - flex-wrap: nowrap; overflow: hidden; } .item-meta { - margin-top: 0; - justify-content: flex-end; flex: 1; - flex-wrap: nowrap; - - .item-meta-child { - margin-left: $gl-padding-8; - flex-wrap: nowrap; - } - } - - .item-path-id { - order: 0; - margin-top: 0; - margin-left: $gl-padding-8; - margin-right: auto; } .item-assignees { - flex-grow: 0; - margin-top: 0; - margin-right: $gl-padding-4; - .avatar { height: $gl-padding-24; width: $gl-padding-24; @@ -345,10 +276,8 @@ $item-weight-max-width: 48px; .btn-item-remove { position: relative; - align-self: center; top: initial; right: 0; - margin-right: 0; padding: $btn-sm-side-margin; &:hover { @@ -357,10 +286,6 @@ $item-weight-max-width: 48px; } .sortable-link { - display: block; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; line-height: 1.3; } } diff --git a/app/controllers/concerns/milestone_actions.rb b/app/controllers/concerns/milestone_actions.rb index c0c0160a827..cfff154c3dd 100644 --- a/app/controllers/concerns/milestone_actions.rb +++ b/app/controllers/concerns/milestone_actions.rb @@ -31,7 +31,7 @@ module MilestoneActions format.html { redirect_to milestone_redirect_path } format.json do render json: tabs_json("shared/milestones/_labels_tab", { - labels: @milestone.labels # rubocop:disable Gitlab/ModuleWithInstanceVariables + labels: @milestone.labels.map { |label| label.present(issuable_subject: @milestone.parent) } # rubocop:disable Gitlab/ModuleWithInstanceVariables }) end end diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb index e14abbf7c78..fc708400657 100644 --- a/app/controllers/projects/branches_controller.rb +++ b/app/controllers/projects/branches_controller.rb @@ -100,14 +100,14 @@ class Projects::BranchesController < Projects::ApplicationController respond_to do |format| format.html do - flash_type = result[:status] == :error ? :alert : :notice - flash[flash_type] = result[:message] + flash_type = result.error? ? :alert : :notice + flash[flash_type] = result.message redirect_to project_branches_path(@project), status: :see_other end - format.js { head result[:return_code] } - format.json { render json: { message: result[:message] }, status: result[:return_code] } + format.js { head result.http_status } + format.json { render json: { message: result.message }, status: result.http_status } end end diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 88ec77426d5..f1dd040515f 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -53,6 +53,7 @@ class IssuableFinder assignee_username author_id author_username + label_name milestone_title my_reaction_emoji search diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index a07c3f90a91..76300e791e6 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -13,9 +13,7 @@ module LabelsHelper # Link to a Label # - # label - Label object to link to - # subject - Project/Group object which will be used as the context for the - # label's link. If omitted, defaults to the label's own group/project. + # label - LabelPresenter object to link to # type - The type of item the link will point to (:issue or # :merge_request). If omitted, defaults to :issue. # block - An optional block that will be passed to `link_to`, forming the @@ -40,8 +38,8 @@ module LabelsHelper # link_to_label(label) { "My Custom Label Text" } # # Returns a String - def link_to_label(label, subject: nil, type: :issue, tooltip: true, css_class: nil, &block) - link = label_filter_path(subject || label.subject, label, type: type) + def link_to_label(label, type: :issue, tooltip: true, css_class: nil, &block) + link = label.filter_path(type: type) if block_given? link_to link, class: css_class, &block @@ -50,34 +48,6 @@ module LabelsHelper end end - def label_filter_path(subject, label, type: :issue) - case subject - when Group - send("#{type.to_s.pluralize}_group_path", # rubocop:disable GitlabSecurity/PublicSend - subject, - label_name: [label.name]) - when Project - send("namespace_project_#{type.to_s.pluralize}_path", # rubocop:disable GitlabSecurity/PublicSend - subject.namespace, - subject, - label_name: [label.name]) - end - end - - def edit_label_path(label) - case label - when GroupLabel then edit_group_label_path(label.group, label) - when ProjectLabel then edit_project_label_path(label.project, label) - end - end - - def destroy_label_path(label) - case label - when GroupLabel then group_label_path(label.group, label) - when ProjectLabel then project_label_path(label.project, label) - end - end - def render_label(label, tooltip: true, link: nil, css: nil) # if scoped label is used then EE wraps label tag with scoped label # doc link @@ -168,10 +138,6 @@ module LabelsHelper end end - def can_subscribe_to_label_in_different_levels?(label) - defined?(@project) && label.is_a?(GroupLabel) - end - def label_subscription_status(label, project) return 'group-level' if label.subscribed?(current_user) return 'project-level' if label.subscribed?(current_user, project) @@ -241,8 +207,8 @@ module LabelsHelper "#{action} at #{level} level" end - def labels_sorted_by_title(labels) - labels.sort_by(&:title) + def presented_labels_sorted_by_title(labels, subject) + labels.sort_by(&:title).map { |label| label.present(issuable_subject: subject) } end def label_dropdown_data(project, opts = {}) @@ -276,6 +242,10 @@ module LabelsHelper klass.new(hash.slice(:color, :description, :title, :group_id, :project_id)) end + def issuable_types + ['issues', 'merge requests'] + end + # Required for Banzai::Filter::LabelReferenceFilter module_function :render_colored_label, :text_color_for_bg, :escape_once, :label_tooltip_title end diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb index 7c836c6f95c..f9cf398556d 100644 --- a/app/models/ci/job_artifact.rb +++ b/app/models/ci/job_artifact.rb @@ -56,7 +56,7 @@ module Ci update_project_statistics stat: :build_artifacts_size - after_save :update_file_store, if: :file_changed? + after_save :update_file_store, if: :saved_change_to_file? scope :with_files_stored_locally, -> { where(file_store: [nil, ::JobArtifactUploader::Store::LOCAL]) } diff --git a/app/models/clusters/platforms/kubernetes.rb b/app/models/clusters/platforms/kubernetes.rb index 2ae141190a8..a806367a49b 100644 --- a/app/models/clusters/platforms/kubernetes.rb +++ b/app/models/clusters/platforms/kubernetes.rb @@ -230,7 +230,7 @@ module Clusters end def update_kubernetes_namespace - return unless namespace_changed? + return unless saved_change_to_namespace? run_after_commit do ClusterConfigureWorker.perform_async(cluster_id) diff --git a/app/models/concerns/storage/legacy_namespace.rb b/app/models/concerns/storage/legacy_namespace.rb index 71a799f44fc..1cbe27ad03a 100644 --- a/app/models/concerns/storage/legacy_namespace.rb +++ b/app/models/concerns/storage/legacy_namespace.rb @@ -13,8 +13,8 @@ module Storage raise Gitlab::UpdatePathError.new("Namespace #{name} (#{id}) cannot be moved because at least one project (e.g. #{proj_with_tags.name} (#{proj_with_tags.id})) has tags in container registry") end - parent_was = if parent_changed? && parent_id_was.present? - Namespace.find(parent_id_was) # raise NotFound early if needed + parent_was = if parent_changed? && parent_id_before_last_save.present? + Namespace.find(parent_id_before_last_save) # raise NotFound early if needed end move_repositories diff --git a/app/models/concerns/update_project_statistics.rb b/app/models/concerns/update_project_statistics.rb index bffc711c886..67e1f0ec930 100644 --- a/app/models/concerns/update_project_statistics.rb +++ b/app/models/concerns/update_project_statistics.rb @@ -39,12 +39,12 @@ module UpdateProjectStatistics end def update_project_statistics_attribute_changed? - attribute_changed?(self.class.statistic_attribute) + saved_change_to_attribute?(self.class.statistic_attribute) end def update_project_statistics_after_save attr = self.class.statistic_attribute - delta = read_attribute(attr).to_i - attribute_was(attr).to_i + delta = read_attribute(attr).to_i - attribute_before_last_save(attr).to_i update_project_statistics(delta) end diff --git a/app/models/global_label.rb b/app/models/global_label.rb index 572cb12b26a..7c020dd3b3d 100644 --- a/app/models/global_label.rb +++ b/app/models/global_label.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class GlobalLabel + include Presentable + attr_accessor :title, :labels alias_attribute :name, :title @@ -23,4 +25,8 @@ class GlobalLabel @labels = labels @first_label = labels.find { |lbl| lbl.description.present? } || labels.first end + + def present(attributes) + super(attributes.merge(presenter_class: ::LabelPresenter)) + end end diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb index fd17745b035..59f5a7703e2 100644 --- a/app/models/global_milestone.rb +++ b/app/models/global_milestone.rb @@ -8,7 +8,9 @@ class GlobalMilestone attr_reader :milestone alias_attribute :name, :title - delegate :title, :state, :due_date, :start_date, :participants, :project, :group, :expires_at, :closed?, :iid, :group_milestone?, :safe_title, :milestoneish_id, to: :milestone + delegate :title, :state, :due_date, :start_date, :participants, :project, + :group, :expires_at, :closed?, :iid, :group_milestone?, :safe_title, + :milestoneish_id, :parent, to: :milestone def to_hash { diff --git a/app/models/group.rb b/app/models/group.rb index 8bc9b75f0a9..53331a19776 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -61,7 +61,7 @@ class Group < Namespace after_create :post_create_hook after_destroy :post_destroy_hook after_save :update_two_factor_requirement - after_update :path_changed_hook, if: :path_changed? + after_update :path_changed_hook, if: :saved_change_to_path? class << self def sort_by_attribute(method) @@ -411,7 +411,7 @@ class Group < Namespace private def update_two_factor_requirement - return unless require_two_factor_authentication_changed? || two_factor_grace_period_changed? + return unless saved_change_to_require_two_factor_authentication? || saved_change_to_two_factor_grace_period? users.find_each(&:update_two_factor_requirement) end diff --git a/app/models/label.rb b/app/models/label.rb index c7fff0d393e..e9085e8bd25 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -8,6 +8,7 @@ class Label < ApplicationRecord include OptionallySearch include Sortable include FromUnion + include Presentable cache_markdown_field :description, pipeline: :single_line @@ -233,6 +234,10 @@ class Label < ApplicationRecord attributes end + def present(attributes) + super(attributes.merge(presenter_class: ::LabelPresenter)) + end + private def issues_count(user, params = {}) diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb index e1aac691a64..5245dbc8d15 100644 --- a/app/models/lfs_object.rb +++ b/app/models/lfs_object.rb @@ -13,7 +13,7 @@ class LfsObject < ApplicationRecord mount_uploader :file, LfsObjectUploader - after_save :update_file_store, if: :file_changed? + after_save :update_file_store, if: :saved_change_to_file? def update_file_store # The file.object_store is set during `uploader.store!` diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb index 510f856087d..b266c61f002 100644 --- a/app/models/members/group_member.rb +++ b/app/models/members/group_member.rb @@ -55,7 +55,7 @@ class GroupMember < Member end def post_update_hook - if access_level_changed? + if saved_change_to_access_level? run_after_commit { notification_service.update_group_member(self) } end diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb index 5372c6084f4..c64e2669b6a 100644 --- a/app/models/members/project_member.rb +++ b/app/models/members/project_member.rb @@ -111,7 +111,7 @@ class ProjectMember < Member end def post_update_hook - if access_level_changed? + if saved_change_to_access_level? run_after_commit { notification_service.update_project_member(self) } end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 251a7ff41f5..a5b62659b24 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -698,7 +698,7 @@ class MergeRequest < ApplicationRecord end def reload_diff_if_branch_changed - if (source_branch_changed? || target_branch_changed?) && + if (saved_change_to_source_branch? || saved_change_to_target_branch?) && (source_branch_head && target_branch_head) reload_diff end diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 8638c5a9c53..7228aab2c2e 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -50,6 +50,8 @@ class Namespace < ApplicationRecord validate :nesting_level_allowed + validates_associated :runners + delegate :name, to: :owner, allow_nil: true, prefix: true delegate :avatar_url, to: :owner, allow_nil: true @@ -57,7 +59,7 @@ class Namespace < ApplicationRecord before_create :sync_share_with_group_lock_with_parent before_update :sync_share_with_group_lock_with_parent, if: :parent_changed? - after_update :force_share_with_group_lock_on_descendants, if: -> { share_with_group_lock_changed? && share_with_group_lock? } + after_update :force_share_with_group_lock_on_descendants, if: -> { saved_change_to_share_with_group_lock? && share_with_group_lock? } # Legacy Storage specific hooks @@ -292,7 +294,7 @@ class Namespace < ApplicationRecord private def path_or_parent_changed? - path_changed? || parent_changed? + saved_change_to_path? || saved_change_to_parent_id? end def refresh_access_of_projects_invited_groups diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb index 82901ceec01..d73b2889f30 100644 --- a/app/models/pages_domain.rb +++ b/app/models/pages_domain.rb @@ -147,20 +147,20 @@ class PagesDomain < ApplicationRecord # rubocop: enable CodeReuse/ServiceClass def pages_config_changed? - project_id_changed? || - domain_changed? || - certificate_changed? || - key_changed? || + saved_change_to_project_id? || + saved_change_to_domain? || + saved_change_to_certificate? || + saved_change_to_key? || became_enabled? || became_disabled? end def became_enabled? - enabled_until.present? && !enabled_until_was.present? + enabled_until.present? && !enabled_until_before_last_save.present? end def became_disabled? - !enabled_until.present? && enabled_until_was.present? + !enabled_until.present? && enabled_until_before_last_save.present? end def validate_matching_key diff --git a/app/models/project.rb b/app/models/project.rb index b7a10b3547a..626ff9e1389 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -90,7 +90,7 @@ class Project < ApplicationRecord before_save :ensure_runners_token - after_save :update_project_statistics, if: :namespace_id_changed? + after_save :update_project_statistics, if: :saved_change_to_namespace_id? after_save :create_import_state, if: ->(project) { project.import? && project.import_state.nil? } @@ -116,7 +116,7 @@ class Project < ApplicationRecord after_initialize :use_hashed_storage after_create :check_repository_absence! after_create :ensure_storage_path_exists - after_save :ensure_storage_path_exists, if: :namespace_id_changed? + after_save :ensure_storage_path_exists, if: :saved_change_to_namespace_id? acts_as_ordered_taggable @@ -1430,7 +1430,7 @@ class Project < ApplicationRecord # update visibility_level of forks def update_forks_visibility_level - return unless visibility_level < visibility_level_was + return unless visibility_level < visibility_level_before_last_save forks.each do |forked_project| if forked_project.visibility_level > visibility_level diff --git a/app/models/remote_mirror.rb b/app/models/remote_mirror.rb index b2fd5394a03..535f772b6a1 100644 --- a/app/models/remote_mirror.rb +++ b/app/models/remote_mirror.rb @@ -248,7 +248,7 @@ class RemoteMirror < ApplicationRecord # Before adding a new remote we have to delete the data from # the previous remote name - prev_remote_name = remote_name_was || fallback_remote_name + prev_remote_name = remote_name_before_last_save || fallback_remote_name run_after_commit do project.repository.async_remove_remote(prev_remote_name) end diff --git a/app/models/route.rb b/app/models/route.rb index 7e3db54d4fe..91ea2966013 100644 --- a/app/models/route.rb +++ b/app/models/route.rb @@ -14,26 +14,26 @@ class Route < ApplicationRecord before_validation :delete_conflicting_orphaned_routes after_create :delete_conflicting_redirects - after_update :delete_conflicting_redirects, if: :path_changed? + after_update :delete_conflicting_redirects, if: :saved_change_to_path? after_update :create_redirect_for_old_path after_update :rename_descendants scope :inside_path, -> (path) { where('routes.path LIKE ?', "#{sanitize_sql_like(path)}/%") } def rename_descendants - return unless path_changed? || name_changed? + return unless saved_change_to_path? || saved_change_to_name? - descendant_routes = self.class.inside_path(path_was) + descendant_routes = self.class.inside_path(path_before_last_save) descendant_routes.each do |route| attributes = {} - if path_changed? && route.path.present? - attributes[:path] = route.path.sub(path_was, path) + if saved_change_to_path? && route.path.present? + attributes[:path] = route.path.sub(path_before_last_save, path) end - if name_changed? && name_was.present? && route.name.present? - attributes[:name] = route.name.sub(name_was, name) + if saved_change_to_name? && name_before_last_save.present? && route.name.present? + attributes[:name] = route.name.sub(name_before_last_save, name) end if attributes.present? @@ -65,7 +65,7 @@ class Route < ApplicationRecord private def create_redirect_for_old_path - create_redirect(path_was) if path_changed? + create_redirect(path_before_last_save) if saved_change_to_path? end def delete_conflicting_orphaned_routes diff --git a/app/models/user.rb b/app/models/user.rb index 551eb58a4de..43039f3760e 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -194,7 +194,7 @@ class User < ApplicationRecord before_validation :ensure_namespace_correct before_save :ensure_namespace_correct # in case validation is skipped after_validation :set_username_errors - after_update :username_changed_hook, if: :username_changed? + after_update :username_changed_hook, if: :saved_change_to_username? after_destroy :post_destroy_hook after_destroy :remove_key_cache after_commit(on: :update) do diff --git a/app/presenters/label_presenter.rb b/app/presenters/label_presenter.rb new file mode 100644 index 00000000000..5227ef353c3 --- /dev/null +++ b/app/presenters/label_presenter.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +class LabelPresenter < Gitlab::View::Presenter::Delegated + presents :label + + def edit_path + case label + when GroupLabel then edit_group_label_path(label.group, label) + when ProjectLabel then edit_project_label_path(label.project, label) + end + end + + def destroy_path + case label + when GroupLabel then group_label_path(label.group, label) + when ProjectLabel then project_label_path(label.project, label) + end + end + + def filter_path(type: :issue) + case context_subject + when Group + send("#{type.to_s.pluralize}_group_path", # rubocop:disable GitlabSecurity/PublicSend + context_subject, + label_name: [label.name]) + when Project + send("namespace_project_#{type.to_s.pluralize}_path", # rubocop:disable GitlabSecurity/PublicSend + context_subject.namespace, + context_subject, + label_name: [label.name]) + end + end + + def can_subscribe_to_label_in_different_levels? + issuable_subject.is_a?(Project) && label.is_a?(GroupLabel) + end + + private + + def context_subject + issuable_subject || label.try(:subject) + end +end diff --git a/app/services/compare_service.rb b/app/services/compare_service.rb index 3adf8a0c1a1..3f0aedfbfb2 100644 --- a/app/services/compare_service.rb +++ b/app/services/compare_service.rb @@ -3,7 +3,7 @@ require 'securerandom' # Compare 2 refs for one repo or between repositories -# and return Gitlab::Git::Compare object that responds to commits and diffs +# and return Compare object that responds to commits and diffs class CompareService attr_reader :start_project, :start_ref_name @@ -15,7 +15,7 @@ class CompareService def execute(target_project, target_ref, base_sha: nil, straight: false) raw_compare = target_project.repository.compare_source_branch(target_ref, start_project.repository, start_ref_name, straight: straight) - return unless raw_compare + return unless raw_compare && raw_compare.base && raw_compare.head Compare.new(raw_compare, target_project, diff --git a/app/services/delete_branch_service.rb b/app/services/delete_branch_service.rb index 4c3ac19f754..fd41ce54486 100644 --- a/app/services/delete_branch_service.rb +++ b/app/services/delete_branch_service.rb @@ -6,27 +6,25 @@ class DeleteBranchService < BaseService branch = repository.find_branch(branch_name) unless current_user.can?(:push_code, project) - return error('You dont have push access to repo', 405) + return ServiceResponse.error( + message: 'You dont have push access to repo', + http_status: 405) end unless branch - return error('No such branch', 404) + return ServiceResponse.error( + message: 'No such branch', + http_status: 404) end if repository.rm_branch(current_user, branch_name) - success('Branch was deleted') + ServiceResponse.success(message: 'Branch was deleted') else - error('Failed to remove branch') + ServiceResponse.error( + message: 'Failed to remove branch', + http_status: 400) end rescue Gitlab::Git::PreReceiveError => ex - error(ex.message) - end - - def error(message, return_code = 400) - super(message).merge(return_code: return_code) - end - - def success(message) - super().merge(message: message) + ServiceResponse.error(message: ex.message, http_status: 400) end end diff --git a/app/services/projects/propagate_service_template.rb b/app/services/projects/propagate_service_template.rb index 633a263af7b..a2f36d2bd1b 100644 --- a/app/services/projects/propagate_service_template.rb +++ b/app/services/projects/propagate_service_template.rb @@ -80,7 +80,7 @@ module Projects value = value.is_a?(Hash) ? value.to_json : value service_hash[ActiveRecord::Base.connection.quote_column_name(key)] = - ActiveRecord::Base.sanitize(value) + ActiveRecord::Base.connection.quote(value) end end end diff --git a/app/services/service_response.rb b/app/services/service_response.rb new file mode 100644 index 00000000000..1de30e68d87 --- /dev/null +++ b/app/services/service_response.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +class ServiceResponse + def self.success(message: nil) + new(status: :success, message: message) + end + + def self.error(message:, http_status: nil) + new(status: :error, message: message, http_status: http_status) + end + + attr_reader :status, :message, :http_status + + def initialize(status:, message: nil, http_status: nil) + self.status = status + self.message = message + self.http_status = http_status + end + + def success? + status == :success + end + + def error? + status == :error + end + + private + + attr_writer :status, :message, :http_status +end diff --git a/app/views/admin/labels/_label.html.haml b/app/views/admin/labels/_label.html.haml index dbb7224f5f9..6d934654c5d 100644 --- a/app/views/admin/labels/_label.html.haml +++ b/app/views/admin/labels/_label.html.haml @@ -1,5 +1,5 @@ %li.label-list-item{ id: dom_id(label) } - = render "shared/label_row", label: label + = render "shared/label_row", label: label.present(issuable_subject: nil) .label-actions-list = link_to edit_admin_label_path(label), class: 'btn btn-transparent label-action has-tooltip', title: _('Edit'), data: { placement: 'bottom' }, aria_label: _('Edit') do = sprite_icon('pencil') diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml index 5cf3193bc62..a8358704b03 100644 --- a/app/views/groups/labels/index.html.haml +++ b/app/views/groups/labels/index.html.haml @@ -1,7 +1,6 @@ - @no_container = true -- page_title "Labels" +- page_title 'Labels' - can_admin_label = can?(current_user, :admin_label, @group) -- issuables = ['issues', 'merge requests'] - search = params[:search] - subscribed = params[:subscribed] - labels_or_filters = @labels.exists? || search.present? || subscribed.present? @@ -14,11 +13,11 @@ .labels-container.prepend-top-5 - if @labels.any? .text-muted - = _('Labels can be applied to %{features}. Group labels are available for any project within the group.') % { features: issuables.to_sentence } + = _('Labels can be applied to %{features}. Group labels are available for any project within the group.') % { features: issuable_types.to_sentence } .other-labels %h5= _('Labels') %ul.content-list.manage-labels-list.js-other-labels - = render partial: 'shared/label', subject: @group, collection: @labels, as: :label, locals: { use_label_priority: false } + = render partial: 'shared/label', collection: @labels, as: :label, locals: { use_label_priority: false, subject: @group } = paginate @labels, theme: 'gitlab' - elsif search.present? .nothing-here-block diff --git a/app/views/import/gitea/new.html.haml b/app/views/import/gitea/new.html.haml index a88b04eccbb..c4670869c93 100644 --- a/app/views/import/gitea/new.html.haml +++ b/app/views/import/gitea/new.html.haml @@ -2,18 +2,18 @@ - header_title _("Projects"), root_path %h3.page-title - = custom_icon('go_logo') + = custom_icon('gitea_logo') = _('Import Projects from Gitea') %p - - link_to_personal_token = link_to(_('Personal Access Token'), 'https://github.com/gogits/go-gogs-client/wiki#access-token') + - link_to_personal_token = link_to(_('Personal Access Token'), 'https://docs.gitea.io/en-us/api-usage/#authentication-via-the-api') = _('To get started, please enter your Gitea Host URL and a %{link_to_personal_token}.').html_safe % { link_to_personal_token: link_to_personal_token } = form_tag personal_access_token_import_gitea_path do .form-group.row = label_tag :gitea_host_url, _('Gitea Host URL'), class: 'col-form-label col-sm-2' .col-sm-4 - = text_field_tag :gitea_host_url, nil, placeholder: 'https://try.gitea.io', class: 'form-control' + = text_field_tag :gitea_host_url, nil, placeholder: 'https://gitea.com', class: 'form-control' .form-group.row = label_tag :personal_access_token, _('Personal Access Token'), class: 'col-form-label col-sm-2' .col-sm-4 diff --git a/app/views/import/gitea/status.html.haml b/app/views/import/gitea/status.html.haml index 88244fde16b..ef0693e73c3 100644 --- a/app/views/import/gitea/status.html.haml +++ b/app/views/import/gitea/status.html.haml @@ -1,7 +1,7 @@ - page_title _("Gitea Import") - header_title _("Projects"), root_path %h3.page-title - = custom_icon('go_logo') + = custom_icon('gitea_logo') = _('Import Projects from Gitea') = render 'import/githubish_status', provider: 'gitea' diff --git a/app/views/projects/_import_project_pane.html.haml b/app/views/projects/_import_project_pane.html.haml index 2b425f18389..9c854369c93 100644 --- a/app/views/projects/_import_project_pane.html.haml +++ b/app/views/projects/_import_project_pane.html.haml @@ -50,7 +50,7 @@ - if gitea_import_enabled? %div = link_to new_import_gitea_path, class: 'btn import_gitea', data: { track_label: "#{track_label}", track_event: "click_button", track_property: "gitea" } do - = custom_icon('go_logo') + = custom_icon('gitea_logo') Gitea - if git_import_enabled? diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index 377b2a6d8d9..945d1b00b08 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -36,8 +36,8 @@ = issue.due_date.to_s(:medium) - if issue.labels.any? - - labels_sorted_by_title(issue.labels).each do |label| - = link_to_label(label, subject: issue.project, css_class: 'label-link') + - presented_labels_sorted_by_title(issue.labels, issue.project).each do |label| + = link_to_label(label, css_class: 'label-link') .issuable-meta %ul.controls diff --git a/app/views/projects/issues/new.html.haml b/app/views/projects/issues/new.html.haml index 9a081a42b6f..c6ff0d50ef4 100644 --- a/app/views/projects/issues/new.html.haml +++ b/app/views/projects/issues/new.html.haml @@ -1,9 +1,9 @@ -- add_to_breadcrumbs "Issues", project_issues_path(@project) -- breadcrumb_title "New" -- page_title "New Issue" +- add_to_breadcrumbs _("Issues"), project_issues_path(@project) +- breadcrumb_title _("New") +- page_title _("New Issue") %h3.page-title - New Issue + _("New Issue") %hr = render "form" diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 4bf1d8702af..0bf664d5b66 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -1,7 +1,7 @@ - @content_class = "limit-container-width" unless fluid_layout -- add_to_breadcrumbs "Issues", project_issues_path(@project) +- add_to_breadcrumbs _("Issues"), project_issues_path(@project) - breadcrumb_title @issue.to_reference -- page_title "#{@issue.title} (#{@issue.to_reference})", "Issues" +- page_title "#{@issue.title} (#{@issue.to_reference})", _("Issues") - page_description @issue.description - page_card_attributes @issue.card_attributes @@ -77,6 +77,8 @@ = edited_time_ago_with_tooltip(@issue, placement: 'bottom', html_class: 'issue-edited-ago js-issue-edited-ago') + = render_if_exists 'projects/issues/related_issues' + #js-related-merge-requests{ data: { endpoint: expose_url(api_v4_projects_issues_related_merge_requests_path(id: @project.id, issue_iid: @issue.iid)), project_namespace: @project.namespace.path, project_path: @project.path } } - if can?(current_user, :download_code, @project) diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index 5d73d832170..511d7a82d1b 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -25,7 +25,7 @@ #js-priority-labels-empty-state.priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty? && search.blank?}" } = render 'shared/empty_states/priority_labels' - if @prioritized_labels.present? - = render partial: 'shared/label', subject: @project, collection: @prioritized_labels, as: :label, locals: { force_priority: true } + = render partial: 'shared/label', collection: @prioritized_labels, as: :label, locals: { force_priority: true, subject: @project } - elsif search.present? .nothing-here-block = _('No prioritised labels with such name or description') @@ -34,7 +34,7 @@ .other-labels %h5{ class: ('hide' if hide) }= _('Other Labels') .content-list.manage-labels-list.js-other-labels - = render partial: 'shared/label', subject: @project, collection: @labels, as: :label + = render partial: 'shared/label', collection: @labels, as: :label, locals: { subject: @project } = paginate @labels, theme: 'gitlab' - elsif search.present? .other-labels diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 47c8e3d73f5..67e5e4ca62d 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -34,8 +34,8 @@ = merge_request.target_branch - if merge_request.labels.any? - - labels_sorted_by_title(merge_request.labels).each do |label| - = link_to_label(label, subject: merge_request.project, type: :merge_request, css_class: 'label-link') + - presented_labels_sorted_by_title(merge_request.labels, merge_request.project).each do |label| + = link_to_label(label, type: :merge_request, css_class: 'label-link') .issuable-meta %ul.controls diff --git a/app/views/shared/_delete_label_modal.html.haml b/app/views/shared/_delete_label_modal.html.haml index dbd3bbb43af..6bd8cadd7d9 100644 --- a/app/views/shared/_delete_label_modal.html.haml +++ b/app/views/shared/_delete_label_modal.html.haml @@ -9,13 +9,13 @@ .modal-body %p %strong= label.name - %span will be permanently deleted from #{label.is_a?(ProjectLabel)? label.project.name : label.group.name}. This cannot be undone. + %span will be permanently deleted from #{label.subject.name}. This cannot be undone. .modal-footer %a{ href: '#', data: { dismiss: 'modal' }, class: 'btn btn-default' } Cancel = link_to 'Delete label', - destroy_label_path(label), + label.destroy_path, title: 'Delete', method: :delete, class: 'btn btn-remove' diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml index 21ea188d7b3..2b4a24a001f 100644 --- a/app/views/shared/_label.html.haml +++ b/app/views/shared/_label.html.haml @@ -1,13 +1,13 @@ +- label = label.present(issuable_subject: local_assigns[:subject]) - label_css_id = dom_id(label) - status = label_subscription_status(label, @project).inquiry if current_user -- subject = local_assigns[:subject] - use_label_priority = local_assigns.fetch(:use_label_priority, false) - force_priority = local_assigns.fetch(:force_priority, use_label_priority ? label.priority.present? : false) - toggle_subscription_path = toggle_subscription_label_path(label, @project) if current_user - tooltip_title = label_status_tooltip(label, status) if status %li.label-list-item{ id: label_css_id, data: { id: label.id } } - = render "shared/label_row", label: label, subject: subject, force_priority: force_priority + = render "shared/label_row", label: label, force_priority: force_priority %ul.label-actions-list - if @project %li.inline @@ -21,7 +21,7 @@ = sprite_icon('star') - if can?(current_user, :admin_label, label) %li.inline - = link_to edit_label_path(label), class: 'btn btn-transparent label-action edit has-tooltip', title: _('Edit'), data: { placement: 'bottom' }, aria_label: _('Edit') do + = link_to label.edit_path, class: 'btn btn-transparent label-action edit has-tooltip', title: _('Edit'), data: { placement: 'bottom' }, aria_label: _('Edit') do = sprite_icon('pencil') - if can?(current_user, :admin_label, label) %li.inline @@ -48,7 +48,7 @@ %button.text-danger.remove-row{ type: 'button' }= _('Delete') - if current_user %li.inline.label-subscription - - if can_subscribe_to_label_in_different_levels?(label) + - if label.can_subscribe_to_label_in_different_levels? %button.js-unsubscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path, toggle: 'tooltip' }, title: tooltip_title } %span= _('Unsubscribe') .dropdown.dropdown-group-label{ class: ('hidden' unless status.unsubscribed?) } diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml index 9d1648fbf70..a1aab2e6a08 100644 --- a/app/views/shared/_label_row.html.haml +++ b/app/views/shared/_label_row.html.haml @@ -1,4 +1,3 @@ -- subject = local_assigns[:subject] - force_priority = local_assigns.fetch(:force_priority, false) - subject_or_group_defined = defined?(@project) || defined?(@group) - show_label_issues_link = subject_or_group_defined && show_label_issuables_link?(label, :issues, project: @project) @@ -14,11 +13,11 @@ %ul.label-links - if show_label_issues_link %li.label-link-item.inline - = link_to_label(label, subject: subject) { 'Issues' } + = link_to_label(label) { 'Issues' } - if show_label_merge_requests_link · %li.label-link-item.inline - = link_to_label(label, subject: subject, type: :merge_request) { _('Merge requests') } + = link_to_label(label, type: :merge_request) { _('Merge requests') } - if force_priority · %li.label-link-item.priority-badge.js-priority-badge.inline.prepend-left-10 diff --git a/app/views/shared/_sidebar_toggle_button.html.haml b/app/views/shared/_sidebar_toggle_button.html.haml index 2530db986e0..aa428f9fe73 100644 --- a/app/views/shared/_sidebar_toggle_button.html.haml +++ b/app/views/shared/_sidebar_toggle_button.html.haml @@ -1,8 +1,8 @@ %a.toggle-sidebar-button.js-toggle-sidebar{ role: "button", type: "button", title: "Toggle sidebar" } = sprite_icon('angle-double-left', css_class: 'icon-angle-double-left') = sprite_icon('angle-double-right', css_class: 'icon-angle-double-right') - %span.collapse-text Collapse sidebar + %span.collapse-text _("Collapse sidebar") = button_tag class: 'close-nav-button', type: 'button' do = sprite_icon('close', size: 16) - %span.collapse-text Close sidebar + %span.collapse-text _("Close sidebar") diff --git a/app/views/shared/icons/_gitea_logo.svg.erb b/app/views/shared/icons/_gitea_logo.svg.erb new file mode 100644 index 00000000000..c8ddbc5535e --- /dev/null +++ b/app/views/shared/icons/_gitea_logo.svg.erb @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?><!-- Created with Inkscape (http://www.inkscape.org/) --><svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="<%= size %>" height="<%= size %>" viewBox="0 0 135.46667 135.46667" version="1.1" id="svg8" sodipodi:docname="logo.svg" inkscape:version="0.92.1 r15371" inkscape:export-filename="" inkscape:export-xdpi="48.000004" inkscape:export-ydpi="48.000004" style="zoom: 1;"><defs id="defs2"></defs><sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:zoom="0.70710678" inkscape:cx="418.13805" inkscape:cy="177.57445" inkscape:document-units="mm" inkscape:current-layer="layer2" showgrid="false" units="px" width="256px" showguides="false" inkscape:window-width="1920" inkscape:window-height="1137" inkscape:window-x="1912" inkscape:window-y="-8" inkscape:window-maximized="1" inkscape:pagecheckerboard="false" inkscape:measure-start="283.373,243.952" inkscape:measure-end="290.267,236.527"><sodipodi:guide position="0,0" orientation="0,512" id="guide3699" inkscape:locked="false"></sodipodi:guide><sodipodi:guide position="135.46667,0" orientation="-512,0" id="guide3701" inkscape:locked="false"></sodipodi:guide><sodipodi:guide position="135.46667,135.46667" orientation="0,-512" id="guide3703" inkscape:locked="false"></sodipodi:guide><sodipodi:guide position="0,135.46667" orientation="512,0" id="guide3705" inkscape:locked="false"></sodipodi:guide></sodipodi:namedview><metadata id="metadata5"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"></dc:type><dc:title></dc:title></cc:Work></rdf:RDF></metadata><g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(0,-161.53334)" style="display:inline"><path d="M27.709937,195.15095 c-9.546573,-0.0272 -22.3392732,6.79805 -21.6317552,23.90397 c1.105534,26.72889 25.4565952,29.20839 35.1916502,29.42301 c1.068023,5.01357 12.521798,22.30563 21.001818,23.21667 h37.15277 c22.27763,-1.66785 38.9607,-75.75671 26.59321,-76.03825 c-46.781583,2.47691 -49.995146,2.13838 -88.599758,0 c-2.495053,-0.0266 -5.972321,-0.49474 -9.707935,-0.5054 z m2.491319,9.45886 c1.351378,13.69267 3.555849,21.70359 8.018216,33.94345 c-11.382872,-1.50473 -21.069822,-5.22443 -22.851515,-19.10984 c-0.950962,-7.4112 2.390428,-15.16769 14.833299,-14.83361 z " id="path3722" sodipodi:nodetypes="sscccccsccsc" inkscape:connector-curvature="0" style="fill:#000;fill-opacity:1;stroke:#000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"></path></g><g inkscape:groupmode="layer" id="layer2" inkscape:label="Layer 2" style="display:inline"><rect style="display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.24757317;stroke-opacity:1" id="rect4599" width="34.762054" height="34.762054" x="87.508659" y="18.291576" transform="rotate(25.914715)" ry="5.4825778"></rect><path style="display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26644793px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 79.804947,57.359056 3.241146,1.609954 V 35.255731 h -3.262698 z" id="path4525" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccc"></path></g><g inkscape:groupmode="layer" id="layer3" inkscape:label="Layer 3" style="display:inline"><g style="display:inline" id="g4539"><circle style="fill:#000;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1" id="path4606" cy="90.077766" r="3.4745038" cx="49.064713" transform="rotate(-19.796137)"></circle><circle style="fill:#000;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1" id="path4606-3" cy="102.1049" r="3.4745038" cx="36.810425" transform="rotate(-19.796137)"></circle><circle style="fill:#000;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1" id="path4606-1" cy="111.43928" r="3.4745038" cx="46.484283" transform="rotate(-19.796137)"></circle><rect height="27.261492" style="fill:#000;fill-opacity:1;stroke:none;stroke-width:0.27444693;stroke-opacity:1" x="97.333458" y="18.061695" id="rect4629-8" width="2.6726954" transform="rotate(26.024158)"></rect><path d="M76.558096,68.116343 c12.97589,6.395378 13.012989,4.101862 4.890858,20.907244 " id="path4514" sodipodi:nodetypes="cc" inkscape:connector-curvature="0" style="fill:none;stroke:#000;stroke-width:2.68000007;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"></path></g></g></svg>
\ No newline at end of file diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index d4be7289a49..2c185549b24 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -106,7 +106,7 @@ .value.issuable-show-labels.dont-hide.hide-collapsed.qa-labels-block{ class: ("has-labels" if selected_labels.any?) } - if selected_labels.any? - selected_labels.each do |label_hash| - = render_label(label_from_hash(label_hash), link: sidebar_label_filter_path(issuable_sidebar[:project_issuables_path], label_hash[:title])) + = render_label(label_from_hash(label_hash).present(issuable_subject: nil), link: sidebar_label_filter_path(issuable_sidebar[:project_issuables_path], label_hash[:title])) - else %span.no-value = _('None') diff --git a/app/views/shared/milestones/_issuable.html.haml b/app/views/shared/milestones/_issuable.html.haml index 5863f52aa78..ae3ab2adfd0 100644 --- a/app/views/shared/milestones/_issuable.html.haml +++ b/app/views/shared/milestones/_issuable.html.haml @@ -21,7 +21,7 @@ %span.issuable-number= issuable.to_reference - labels.each do |label| - = render_label(label, link: polymorphic_path(issuable_type_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' })) + = render_label(label.present(issuable_subject: project), link: polymorphic_path(issuable_type_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' })) %span.assignee-icon - assignees.each do |assignee| diff --git a/changelogs/unreleased/27424-tklk-gitea-logo.yml b/changelogs/unreleased/27424-tklk-gitea-logo.yml new file mode 100644 index 00000000000..0d41bb39aad --- /dev/null +++ b/changelogs/unreleased/27424-tklk-gitea-logo.yml @@ -0,0 +1,5 @@ +--- +title: "Use official Gitea logo in importer" +merge_request: 27424 +author: Matti Ranta (@techknowlogick) +type: added
\ No newline at end of file diff --git a/changelogs/unreleased/47584-label-text-color.yml b/changelogs/unreleased/47584-label-text-color.yml new file mode 100644 index 00000000000..7d5eaa62793 --- /dev/null +++ b/changelogs/unreleased/47584-label-text-color.yml @@ -0,0 +1,5 @@ +--- +title: Resolve issue where list labels did not have the correct text color on creation +merge_request: 26794 +author: Tucker Chapman +type: fixed diff --git a/changelogs/unreleased/58252-web-ide-dropdown-duplicates.yml b/changelogs/unreleased/58252-web-ide-dropdown-duplicates.yml new file mode 100644 index 00000000000..48b03994586 --- /dev/null +++ b/changelogs/unreleased/58252-web-ide-dropdown-duplicates.yml @@ -0,0 +1,5 @@ +--- +title: Resolve Web IDE template dropdown showing duplicates +merge_request: 27237 +author: +type: fixed diff --git a/changelogs/unreleased/58850-fix-misplaced-swipe-view-26969.yml b/changelogs/unreleased/58850-fix-misplaced-swipe-view-26969.yml new file mode 100644 index 00000000000..fa3e81df4e0 --- /dev/null +++ b/changelogs/unreleased/58850-fix-misplaced-swipe-view-26969.yml @@ -0,0 +1,5 @@ +--- +title: "Fix misaligned image diff swipe view" +merge_request: 26969 +author: ftab +type: fixed diff --git a/changelogs/unreleased/59514-uploading-images-base64.yml b/changelogs/unreleased/59514-uploading-images-base64.yml new file mode 100644 index 00000000000..905b00db06a --- /dev/null +++ b/changelogs/unreleased/59514-uploading-images-base64.yml @@ -0,0 +1,5 @@ +--- +title: Show proper preview for uploaded images in Web IDE +merge_request: 27471 +author: +type: fixed diff --git a/changelogs/unreleased/60500-disable-jit-kubernetes-resource-creation-for-project-level-clusters.yml b/changelogs/unreleased/60500-disable-jit-kubernetes-resource-creation-for-project-level-clusters.yml deleted file mode 100644 index df6e6ea4be3..00000000000 --- a/changelogs/unreleased/60500-disable-jit-kubernetes-resource-creation-for-project-level-clusters.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Disable just-in-time Kubernetes resource creation for project level clusters -merge_request: 27352 -author: -type: changed diff --git a/changelogs/unreleased/jc-upgrade-gitaly-1-34-0.yml b/changelogs/unreleased/jc-upgrade-gitaly-1-34-0.yml deleted file mode 100644 index c1669a484aa..00000000000 --- a/changelogs/unreleased/jc-upgrade-gitaly-1-34-0.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Upgrade Gitaly to 1.34.0 -merge_request: 27494 -author: -type: fixed diff --git a/changelogs/unreleased/rails5-1.yml b/changelogs/unreleased/rails5-1.yml new file mode 100644 index 00000000000..da16735bb0d --- /dev/null +++ b/changelogs/unreleased/rails5-1.yml @@ -0,0 +1,5 @@ +--- +title: Upgrade to Rails 5.1 +merge_request: 27480 +author: Jasper Maes +type: other diff --git a/changelogs/unreleased/sh-avoid-fetching-temp-refs-within-project.yml b/changelogs/unreleased/sh-avoid-fetching-temp-refs-within-project.yml new file mode 100644 index 00000000000..7511543f7f6 --- /dev/null +++ b/changelogs/unreleased/sh-avoid-fetching-temp-refs-within-project.yml @@ -0,0 +1,5 @@ +--- +title: Don't create a temp reference for branch comparisons within project +merge_request: 24038 +author: +type: fixed diff --git a/changelogs/unreleased/sh-backport-list-commits-by-oid-rugged.yml b/changelogs/unreleased/sh-backport-list-commits-by-oid-rugged.yml deleted file mode 100644 index eb8774d652f..00000000000 --- a/changelogs/unreleased/sh-backport-list-commits-by-oid-rugged.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Bring back Rugged implementation of ListCommitsByOid -merge_request: 27441 -author: -type: performance diff --git a/changelogs/unreleased/sh-bump-ruby-required-version-check.yml b/changelogs/unreleased/sh-bump-ruby-required-version-check.yml deleted file mode 100644 index b5b6eb87650..00000000000 --- a/changelogs/unreleased/sh-bump-ruby-required-version-check.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Bump required Ruby version check to 2.5.3 -merge_request: 27495 -author: -type: other diff --git a/changelogs/unreleased/support-negative-matches.yml b/changelogs/unreleased/support-negative-matches.yml new file mode 100644 index 00000000000..8d3f2d3cbae --- /dev/null +++ b/changelogs/unreleased/support-negative-matches.yml @@ -0,0 +1,5 @@ +--- +title: Support negative matches +merge_request: +author: +type: added diff --git a/config/application.rb b/config/application.rb index cbcfef34e01..cddd91f267a 100644 --- a/config/application.rb +++ b/config/application.rb @@ -164,9 +164,6 @@ module Gitlab # Version of your assets, change this if you want to expire all your assets config.assets.version = '1.0' - # Can be removed once upgraded to Rails 5.1 or higher - config.action_controller.raise_on_unfiltered_parameters = true - # Nokogiri is significantly faster and uses less memory than REXML ActiveSupport::XmlMini.backend = 'Nokogiri' diff --git a/config/initializers/active_record_avoid_type_casting_in_uniqueness_validator.rb b/config/initializers/active_record_avoid_type_casting_in_uniqueness_validator.rb deleted file mode 100644 index 228ced32188..00000000000 --- a/config/initializers/active_record_avoid_type_casting_in_uniqueness_validator.rb +++ /dev/null @@ -1,94 +0,0 @@ -# This is a monkey patch which must be removed when migrating to Rails 5.1 from 5.0. -# -# In Rails 5.0 there was introduced a bug which casts types in the uniqueness validator. -# https://github.com/rails/rails/pull/23523/commits/811a4fa8eb6ceea841e61e8ac05747ffb69595ae -# -# That causes to bugs like this: -# -# 1) API::Users POST /user/:id/gpg_keys/:key_id/revoke when authenticated revokes existing key -# Failure/Error: let(:gpg_key) { create(:gpg_key, user: user) } -# -# TypeError: -# can't cast Hash -# # ./spec/requests/api/users_spec.rb:7:in `block (2 levels) in <top (required)>' -# # ./spec/requests/api/users_spec.rb:908:in `block (4 levels) in <top (required)>' -# # ------------------ -# # --- Caused by: --- -# # TypeError: -# # TypeError -# # ./spec/requests/api/users_spec.rb:7:in `block (2 levels) in <top (required)>' -# -# This bug was fixed in Rails 5.1 by https://github.com/rails/rails/pull/24745/commits/aa062318c451512035c10898a1af95943b1a3803 - -if Rails.gem_version >= Gem::Version.new("5.1") - raise "Remove this monkey patch: #{__FILE__}" -end - -# Copy-paste from https://github.com/kamipo/rails/blob/aa062318c451512035c10898a1af95943b1a3803/activerecord/lib/active_record/validations/uniqueness.rb -# including local fixes to make Rubocop happy again. -module ActiveRecord - module Validations - class UniquenessValidator < ActiveModel::EachValidator # :nodoc: - def validate_each(record, attribute, value) - finder_class = find_finder_class_for(record) - table = finder_class.arel_table - value = map_enum_attribute(finder_class, attribute, value) - - relation = build_relation(finder_class, table, attribute, value) - - if record.persisted? - if finder_class.primary_key - relation = relation.where.not(finder_class.primary_key => record.id_was || record.id) - else - raise UnknownPrimaryKey.new(finder_class, "Can not validate uniqueness for persisted record without primary key.") - end - end - - relation = scope_relation(record, table, relation) - relation = relation.merge(options[:conditions]) if options[:conditions] - - if relation.exists? - error_options = options.except(:case_sensitive, :scope, :conditions) - error_options[:value] = value - - record.errors.add(attribute, :taken, error_options) - end - rescue RangeError - end - - protected - - def build_relation(klass, table, attribute, value) #:nodoc: - if reflection = klass._reflect_on_association(attribute) - attribute = reflection.foreign_key - value = value.attributes[reflection.klass.primary_key] unless value.nil? - end - - # the attribute may be an aliased attribute - if klass.attribute_alias?(attribute) - attribute = klass.attribute_alias(attribute) - end - - attribute_name = attribute.to_s - - column = klass.columns_hash[attribute_name] - cast_type = klass.type_for_attribute(attribute_name) - - comparison = - if !options[:case_sensitive] && !value.nil? - # will use SQL LOWER function before comparison, unless it detects a case insensitive collation - klass.connection.case_insensitive_comparison(table, attribute, column, value) - else - klass.connection.case_sensitive_comparison(table, attribute, column, value) - end - - if value.nil? - klass.unscoped.where(comparison) - else - bind = Relation::QueryAttribute.new(attribute_name, value, cast_type) - klass.unscoped.where(comparison, bind) - end - end - end - end -end diff --git a/config/initializers/active_record_build_select.rb b/config/initializers/active_record_build_select.rb new file mode 100644 index 00000000000..ab5a872cac6 --- /dev/null +++ b/config/initializers/active_record_build_select.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +# rubocop:disable Gitlab/ModuleWithInstanceVariables + +# build_select only selects the required fields if the model has ignored_columns. +# This is incompatible with some migrations or background migration specs because +# rails keeps a statement cache in memory. So if a model with ignored_columns in a +# migration is used, the query with select table.col1, table.col2 is stored in the +# statement cache. If a different migration is then run and one of these columns is +# removed in the meantime, the query is invalid. + +module ActiveRecord + module QueryMethods + private + + def build_select(arel) + if select_values.any? + arel.project(*arel_columns(select_values.uniq)) + else + arel.project(@klass.arel_table[Arel.star]) + end + end + end +end diff --git a/config/initializers/active_record_locking.rb b/config/initializers/active_record_locking.rb deleted file mode 100644 index 1bd1a12e4b7..00000000000 --- a/config/initializers/active_record_locking.rb +++ /dev/null @@ -1,76 +0,0 @@ -# rubocop:disable Lint/RescueException - -# Remove this monkey patch when we move to Rails 5.1, because the bug has been fixed in https://github.com/rails/rails/pull/26050. -if Rails.gem_version >= Gem::Version.new("5.1") - raise "Remove this monkey patch: #{__FILE__}" -end - -module ActiveRecord - module Locking - module Optimistic - # We overwrite this method because we don't want to have default value - # for newly created records - def _create_record(attribute_names = self.attribute_names, *) # :nodoc: - super - end - - def _update_record(attribute_names = self.attribute_names) #:nodoc: - return super unless locking_enabled? - return 0 if attribute_names.empty? - - lock_col = self.class.locking_column - previous_lock_value = send(lock_col).to_i - increment_lock - - attribute_names += [lock_col] - attribute_names.uniq! - - begin - relation = self.class.unscoped - - affected_rows = relation.where( - self.class.primary_key => id, - # Patched because when `lock_version` is read as `0`, it may actually be `NULL` in the DB. - lock_col => previous_lock_value == 0 ? [nil, 0] : previous_lock_value - ).update_all( - attributes_for_update(attribute_names).map do |name| - [name, _read_attribute(name)] - end.to_h - ) - - unless affected_rows == 1 - raise ActiveRecord::StaleObjectError.new(self, "update") - end - - affected_rows - - # If something went wrong, revert the version. - rescue Exception - send(lock_col + '=', previous_lock_value) - raise - end - end - - # This is patched because we need it to query `lock_version IS NULL` - # rather than `lock_version = 0` whenever lock_version is NULL. - def relation_for_destroy - return super unless locking_enabled? - - column_name = self.class.locking_column - super.where(self.class.arel_table[column_name].eq(self[column_name])) - end - end - - # This is patched because we want `lock_version` default to `NULL` - # rather than `0` - class LockingType - def deserialize(value) - super - end - - def serialize(value) - super - end - end - end -end diff --git a/config/initializers/ar_native_database_types.rb b/config/initializers/ar_native_database_types.rb index 3522b1db536..6d397661f75 100644 --- a/config/initializers/ar_native_database_types.rb +++ b/config/initializers/ar_native_database_types.rb @@ -4,7 +4,8 @@ module ActiveRecord module ConnectionAdapters class AbstractMysqlAdapter NATIVE_DATABASE_TYPES.merge!( - bigserial: { name: 'bigint(20) auto_increment PRIMARY KEY' } + bigserial: { name: 'bigint(20) auto_increment PRIMARY KEY' }, + serial: { name: 'int auto_increment PRIMARY KEY' } ) end end diff --git a/config/initializers/postgresql_opclasses_support.rb b/config/initializers/postgresql_opclasses_support.rb index b066f3788ec..7e912180820 100644 --- a/config/initializers/postgresql_opclasses_support.rb +++ b/config/initializers/postgresql_opclasses_support.rb @@ -78,7 +78,7 @@ module ActiveRecord if index_name.length > max_index_length raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters" end - if data_source_exists?(table_name) && index_name_exists?(table_name, index_name, false) + if data_source_exists?(table_name) && index_name_exists?(table_name, index_name) raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists" end index_columns = quoted_columns_for_index(column_names, options).join(", ") diff --git a/db/fixtures/development/09_issues.rb b/db/fixtures/development/09_issues.rb index 926401d8b9e..582a5203d1d 100644 --- a/db/fixtures/development/09_issues.rb +++ b/db/fixtures/development/09_issues.rb @@ -1,23 +1,5 @@ require './spec/support/sidekiq' Gitlab::Seeder.quiet do - Project.all.each do |project| - 10.times do - label_ids = project.labels.pluck(:id).sample(3) - label_ids += project.group.labels.sample(3) if project.group - - issue_params = { - title: FFaker::Lorem.sentence(6), - description: FFaker::Lorem.sentence, - state: ['opened', 'closed'].sample, - milestone: project.milestones.sample, - assignees: [project.team.users.sample], - created_at: rand(12).months.ago, - label_ids: label_ids - } - - Issues::CreateService.new(project, project.team.users.sample, issue_params).execute - print '.' - end - end + Rake::Task["gitlab:seed:issues"].invoke end diff --git a/db/migrate/20161031181638_add_unique_index_to_subscriptions.rb b/db/migrate/20161031181638_add_unique_index_to_subscriptions.rb index 23a775d6282..9005b42b41f 100644 --- a/db/migrate/20161031181638_add_unique_index_to_subscriptions.rb +++ b/db/migrate/20161031181638_add_unique_index_to_subscriptions.rb @@ -9,11 +9,11 @@ class AddUniqueIndexToSubscriptions < ActiveRecord::Migration[4.2] def up add_concurrent_index :subscriptions, [:subscribable_id, :subscribable_type, :user_id, :project_id], { unique: true, name: 'index_subscriptions_on_subscribable_and_user_id_and_project_id' } - remove_index :subscriptions, name: 'subscriptions_user_id_and_ref_fields' if index_name_exists?(:subscriptions, 'subscriptions_user_id_and_ref_fields', false) + remove_index :subscriptions, name: 'subscriptions_user_id_and_ref_fields' if index_name_exists?(:subscriptions, 'subscriptions_user_id_and_ref_fields') end def down add_concurrent_index :subscriptions, [:subscribable_id, :subscribable_type, :user_id], { unique: true, name: 'subscriptions_user_id_and_ref_fields' } - remove_index :subscriptions, name: 'index_subscriptions_on_subscribable_and_user_id_and_project_id' if index_name_exists?(:subscriptions, 'index_subscriptions_on_subscribable_and_user_id_and_project_id', false) + remove_index :subscriptions, name: 'index_subscriptions_on_subscribable_and_user_id_and_project_id' if index_name_exists?(:subscriptions, 'index_subscriptions_on_subscribable_and_user_id_and_project_id') end end diff --git a/db/migrate/20190322164830_add_auto_ssl_enabled_to_pages_domain.rb b/db/migrate/20190322164830_add_auto_ssl_enabled_to_pages_domain.rb new file mode 100644 index 00000000000..e74a9535ddf --- /dev/null +++ b/db/migrate/20190322164830_add_auto_ssl_enabled_to_pages_domain.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class AddAutoSslEnabledToPagesDomain < ActiveRecord::Migration[5.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_column_with_default :pages_domains, :auto_ssl_enabled, :boolean, default: false + end + + def down + remove_column :pages_domains, :auto_ssl_enabled + end +end diff --git a/db/schema.rb b/db/schema.rb index c0399529deb..3a5d567ac57 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -16,7 +16,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do enable_extension "plpgsql" enable_extension "pg_trgm" - create_table "abuse_reports", force: :cascade do |t| + create_table "abuse_reports", id: :serial, force: :cascade do |t| t.integer "reporter_id" t.integer "user_id" t.text "message" @@ -26,7 +26,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.integer "cached_markdown_version" end - create_table "appearances", force: :cascade do |t| + create_table "appearances", id: :serial, force: :cascade do |t| t.string "title", null: false t.text "description", null: false t.string "header_logo" @@ -47,13 +47,13 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.boolean "email_header_and_footer_enabled", default: false, null: false end - create_table "application_setting_terms", force: :cascade do |t| + create_table "application_setting_terms", id: :serial, force: :cascade do |t| t.integer "cached_markdown_version" t.text "terms", null: false t.text "terms_html" end - create_table "application_settings", force: :cascade do |t| + create_table "application_settings", id: :serial, force: :cascade do |t| t.integer "default_projects_limit" t.boolean "signup_enabled" t.boolean "gravatar_enabled" @@ -190,7 +190,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["usage_stats_set_by_user_id"], name: "index_application_settings_on_usage_stats_set_by_user_id", using: :btree end - create_table "audit_events", force: :cascade do |t| + create_table "audit_events", id: :serial, force: :cascade do |t| t.integer "author_id", null: false t.string "type", null: false t.integer "entity_id", null: false @@ -201,7 +201,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["entity_id", "entity_type"], name: "index_audit_events_on_entity_id_and_entity_type", using: :btree end - create_table "award_emoji", force: :cascade do |t| + create_table "award_emoji", id: :serial, force: :cascade do |t| t.string "name" t.integer "user_id" t.integer "awardable_id" @@ -212,7 +212,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id", "name"], name: "index_award_emoji_on_user_id_and_name", using: :btree end - create_table "badges", force: :cascade do |t| + create_table "badges", id: :serial, force: :cascade do |t| t.string "link_url", null: false t.string "image_url", null: false t.integer "project_id" @@ -224,7 +224,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id"], name: "index_badges_on_project_id", using: :btree end - create_table "board_group_recent_visits", id: :bigserial, force: :cascade do |t| + create_table "board_group_recent_visits", force: :cascade do |t| t.datetime_with_timezone "created_at", null: false t.datetime_with_timezone "updated_at", null: false t.integer "user_id" @@ -236,7 +236,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id"], name: "index_board_group_recent_visits_on_user_id", using: :btree end - create_table "board_project_recent_visits", id: :bigserial, force: :cascade do |t| + create_table "board_project_recent_visits", force: :cascade do |t| t.datetime_with_timezone "created_at", null: false t.datetime_with_timezone "updated_at", null: false t.integer "user_id" @@ -248,7 +248,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id"], name: "index_board_project_recent_visits_on_user_id", using: :btree end - create_table "boards", force: :cascade do |t| + create_table "boards", id: :serial, force: :cascade do |t| t.integer "project_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false @@ -257,7 +257,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id"], name: "index_boards_on_project_id", using: :btree end - create_table "broadcast_messages", force: :cascade do |t| + create_table "broadcast_messages", id: :serial, force: :cascade do |t| t.text "message", null: false t.datetime "starts_at", null: false t.datetime "ends_at", null: false @@ -270,7 +270,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["starts_at", "ends_at", "id"], name: "index_broadcast_messages_on_starts_at_and_ends_at_and_id", using: :btree end - create_table "chat_names", force: :cascade do |t| + create_table "chat_names", id: :serial, force: :cascade do |t| t.integer "user_id", null: false t.integer "service_id", null: false t.string "team_id", null: false @@ -284,7 +284,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id", "service_id"], name: "index_chat_names_on_user_id_and_service_id", unique: true, using: :btree end - create_table "chat_teams", force: :cascade do |t| + create_table "chat_teams", id: :serial, force: :cascade do |t| t.integer "namespace_id", null: false t.string "team_id" t.string "name" @@ -293,7 +293,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["namespace_id"], name: "index_chat_teams_on_namespace_id", unique: true, using: :btree end - create_table "ci_build_trace_chunks", id: :bigserial, force: :cascade do |t| + create_table "ci_build_trace_chunks", force: :cascade do |t| t.integer "build_id", null: false t.integer "chunk_index", null: false t.integer "data_store", null: false @@ -301,13 +301,13 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["build_id", "chunk_index"], name: "index_ci_build_trace_chunks_on_build_id_and_chunk_index", unique: true, using: :btree end - create_table "ci_build_trace_section_names", force: :cascade do |t| + create_table "ci_build_trace_section_names", id: :serial, force: :cascade do |t| t.integer "project_id", null: false t.string "name", null: false t.index ["project_id", "name"], name: "index_ci_build_trace_section_names_on_project_id_and_name", unique: true, using: :btree end - create_table "ci_build_trace_sections", force: :cascade do |t| + create_table "ci_build_trace_sections", id: :serial, force: :cascade do |t| t.integer "project_id", null: false t.datetime_with_timezone "date_start", null: false t.datetime_with_timezone "date_end", null: false @@ -320,7 +320,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["section_name_id"], name: "index_ci_build_trace_sections_on_section_name_id", using: :btree end - create_table "ci_builds", force: :cascade do |t| + create_table "ci_builds", id: :serial, force: :cascade do |t| t.string "status" t.datetime "finished_at" t.text "trace" @@ -387,7 +387,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id"], name: "index_ci_builds_on_user_id", using: :btree end - create_table "ci_builds_metadata", force: :cascade do |t| + create_table "ci_builds_metadata", id: :serial, force: :cascade do |t| t.integer "build_id", null: false t.integer "project_id", null: false t.integer "timeout" @@ -398,7 +398,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id"], name: "index_ci_builds_metadata_on_project_id", using: :btree end - create_table "ci_builds_runner_session", id: :bigserial, force: :cascade do |t| + create_table "ci_builds_runner_session", force: :cascade do |t| t.integer "build_id", null: false t.string "url", null: false t.string "certificate" @@ -406,7 +406,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["build_id"], name: "index_ci_builds_runner_session_on_build_id", unique: true, using: :btree end - create_table "ci_group_variables", force: :cascade do |t| + create_table "ci_group_variables", id: :serial, force: :cascade do |t| t.string "key", null: false t.text "value" t.text "encrypted_value" @@ -420,7 +420,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["group_id", "key"], name: "index_ci_group_variables_on_group_id_and_key", unique: true, using: :btree end - create_table "ci_job_artifacts", force: :cascade do |t| + create_table "ci_job_artifacts", id: :serial, force: :cascade do |t| t.integer "project_id", null: false t.integer "job_id", null: false t.integer "file_type", null: false @@ -439,7 +439,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id"], name: "index_ci_job_artifacts_on_project_id", using: :btree end - create_table "ci_pipeline_chat_data", id: :bigserial, force: :cascade do |t| + create_table "ci_pipeline_chat_data", force: :cascade do |t| t.integer "pipeline_id", null: false t.integer "chat_name_id", null: false t.text "response_url", null: false @@ -459,7 +459,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["pipeline_schedule_id", "key"], name: "index_ci_pipeline_schedule_variables_on_schedule_id_and_key", unique: true, using: :btree end - create_table "ci_pipeline_schedules", force: :cascade do |t| + create_table "ci_pipeline_schedules", id: :serial, force: :cascade do |t| t.string "description" t.string "ref" t.string "cron" @@ -475,7 +475,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id"], name: "index_ci_pipeline_schedules_on_project_id", using: :btree end - create_table "ci_pipeline_variables", force: :cascade do |t| + create_table "ci_pipeline_variables", id: :serial, force: :cascade do |t| t.string "key", null: false t.text "value" t.text "encrypted_value" @@ -485,7 +485,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["pipeline_id", "key"], name: "index_ci_pipeline_variables_on_pipeline_id_and_key", unique: true, using: :btree end - create_table "ci_pipelines", force: :cascade do |t| + create_table "ci_pipelines", id: :serial, force: :cascade do |t| t.string "ref" t.string "sha" t.string "before_sha" @@ -525,14 +525,14 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id"], name: "index_ci_pipelines_on_user_id", using: :btree end - create_table "ci_runner_namespaces", force: :cascade do |t| + create_table "ci_runner_namespaces", id: :serial, force: :cascade do |t| t.integer "runner_id" t.integer "namespace_id" t.index ["namespace_id"], name: "index_ci_runner_namespaces_on_namespace_id", using: :btree t.index ["runner_id", "namespace_id"], name: "index_ci_runner_namespaces_on_runner_id_and_namespace_id", unique: true, using: :btree end - create_table "ci_runner_projects", force: :cascade do |t| + create_table "ci_runner_projects", id: :serial, force: :cascade do |t| t.integer "runner_id", null: false t.datetime "created_at" t.datetime "updated_at" @@ -541,7 +541,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["runner_id"], name: "index_ci_runner_projects_on_runner_id", using: :btree end - create_table "ci_runners", force: :cascade do |t| + create_table "ci_runners", id: :serial, force: :cascade do |t| t.string "token" t.datetime "created_at" t.datetime "updated_at" @@ -569,7 +569,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["token_encrypted"], name: "index_ci_runners_on_token_encrypted", using: :btree end - create_table "ci_stages", force: :cascade do |t| + create_table "ci_stages", id: :serial, force: :cascade do |t| t.integer "project_id" t.integer "pipeline_id" t.datetime "created_at" @@ -584,7 +584,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id"], name: "index_ci_stages_on_project_id", using: :btree end - create_table "ci_trigger_requests", force: :cascade do |t| + create_table "ci_trigger_requests", id: :serial, force: :cascade do |t| t.integer "trigger_id", null: false t.text "variables" t.datetime "created_at" @@ -594,7 +594,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["trigger_id"], name: "index_ci_trigger_requests_on_trigger_id", using: :btree end - create_table "ci_triggers", force: :cascade do |t| + create_table "ci_triggers", id: :serial, force: :cascade do |t| t.string "token" t.datetime "created_at" t.datetime "updated_at" @@ -606,7 +606,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id"], name: "index_ci_triggers_on_project_id", using: :btree end - create_table "ci_variables", force: :cascade do |t| + create_table "ci_variables", id: :serial, force: :cascade do |t| t.string "key", null: false t.text "value" t.text "encrypted_value" @@ -619,14 +619,14 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id", "key", "environment_scope"], name: "index_ci_variables_on_project_id_and_key_and_environment_scope", unique: true, using: :btree end - create_table "cluster_groups", force: :cascade do |t| + create_table "cluster_groups", id: :serial, force: :cascade do |t| t.integer "cluster_id", null: false t.integer "group_id", null: false t.index ["cluster_id", "group_id"], name: "index_cluster_groups_on_cluster_id_and_group_id", unique: true, using: :btree t.index ["group_id"], name: "index_cluster_groups_on_group_id", using: :btree end - create_table "cluster_platforms_kubernetes", force: :cascade do |t| + create_table "cluster_platforms_kubernetes", id: :serial, force: :cascade do |t| t.integer "cluster_id", null: false t.datetime_with_timezone "created_at", null: false t.datetime_with_timezone "updated_at", null: false @@ -642,7 +642,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["cluster_id"], name: "index_cluster_platforms_kubernetes_on_cluster_id", unique: true, using: :btree end - create_table "cluster_projects", force: :cascade do |t| + create_table "cluster_projects", id: :serial, force: :cascade do |t| t.integer "project_id", null: false t.integer "cluster_id", null: false t.datetime_with_timezone "created_at", null: false @@ -651,7 +651,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id"], name: "index_cluster_projects_on_project_id", using: :btree end - create_table "cluster_providers_gcp", force: :cascade do |t| + create_table "cluster_providers_gcp", id: :serial, force: :cascade do |t| t.integer "cluster_id", null: false t.integer "status" t.integer "num_nodes", null: false @@ -669,7 +669,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["cluster_id"], name: "index_cluster_providers_gcp_on_cluster_id", unique: true, using: :btree end - create_table "clusters", force: :cascade do |t| + create_table "clusters", id: :serial, force: :cascade do |t| t.integer "user_id" t.integer "provider_type" t.integer "platform_type" @@ -685,7 +685,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id"], name: "index_clusters_on_user_id", using: :btree end - create_table "clusters_applications_cert_managers", force: :cascade do |t| + create_table "clusters_applications_cert_managers", id: :serial, force: :cascade do |t| t.integer "cluster_id", null: false t.integer "status", null: false t.string "version", null: false @@ -696,7 +696,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["cluster_id"], name: "index_clusters_applications_cert_managers_on_cluster_id", unique: true, using: :btree end - create_table "clusters_applications_helm", force: :cascade do |t| + create_table "clusters_applications_helm", id: :serial, force: :cascade do |t| t.integer "cluster_id", null: false t.datetime_with_timezone "created_at", null: false t.datetime_with_timezone "updated_at", null: false @@ -709,7 +709,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["cluster_id"], name: "index_clusters_applications_helm_on_cluster_id", unique: true, using: :btree end - create_table "clusters_applications_ingress", force: :cascade do |t| + create_table "clusters_applications_ingress", id: :serial, force: :cascade do |t| t.integer "cluster_id", null: false t.datetime_with_timezone "created_at", null: false t.datetime_with_timezone "updated_at", null: false @@ -723,7 +723,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["cluster_id"], name: "index_clusters_applications_ingress_on_cluster_id", unique: true, using: :btree end - create_table "clusters_applications_jupyter", force: :cascade do |t| + create_table "clusters_applications_jupyter", id: :serial, force: :cascade do |t| t.integer "cluster_id", null: false t.integer "oauth_application_id" t.integer "status", null: false @@ -736,7 +736,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["oauth_application_id"], name: "index_clusters_applications_jupyter_on_oauth_application_id", using: :btree end - create_table "clusters_applications_knative", force: :cascade do |t| + create_table "clusters_applications_knative", id: :serial, force: :cascade do |t| t.integer "cluster_id", null: false t.datetime_with_timezone "created_at", null: false t.datetime_with_timezone "updated_at", null: false @@ -749,7 +749,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["cluster_id"], name: "index_clusters_applications_knative_on_cluster_id", unique: true, using: :btree end - create_table "clusters_applications_prometheus", force: :cascade do |t| + create_table "clusters_applications_prometheus", id: :serial, force: :cascade do |t| t.integer "cluster_id", null: false t.integer "status", null: false t.string "version", null: false @@ -759,7 +759,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["cluster_id"], name: "index_clusters_applications_prometheus_on_cluster_id", unique: true, using: :btree end - create_table "clusters_applications_runners", force: :cascade do |t| + create_table "clusters_applications_runners", id: :serial, force: :cascade do |t| t.integer "cluster_id", null: false t.integer "runner_id" t.integer "status", null: false @@ -772,7 +772,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["runner_id"], name: "index_clusters_applications_runners_on_runner_id", using: :btree end - create_table "clusters_kubernetes_namespaces", id: :bigserial, force: :cascade do |t| + create_table "clusters_kubernetes_namespaces", force: :cascade do |t| t.integer "cluster_id", null: false t.integer "project_id" t.integer "cluster_project_id" @@ -788,7 +788,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id"], name: "index_clusters_kubernetes_namespaces_on_project_id", using: :btree end - create_table "container_repositories", force: :cascade do |t| + create_table "container_repositories", id: :serial, force: :cascade do |t| t.integer "project_id", null: false t.string "name", null: false t.datetime "created_at", null: false @@ -797,7 +797,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id"], name: "index_container_repositories_on_project_id", using: :btree end - create_table "conversational_development_index_metrics", force: :cascade do |t| + create_table "conversational_development_index_metrics", id: :serial, force: :cascade do |t| t.float "leader_issues", null: false t.float "instance_issues", null: false t.float "leader_notes", null: false @@ -832,7 +832,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.float "percentage_service_desk_issues", default: 0.0, null: false end - create_table "deploy_keys_projects", force: :cascade do |t| + create_table "deploy_keys_projects", id: :serial, force: :cascade do |t| t.integer "deploy_key_id", null: false t.integer "project_id", null: false t.datetime "created_at" @@ -841,7 +841,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id"], name: "index_deploy_keys_projects_on_project_id", using: :btree end - create_table "deploy_tokens", force: :cascade do |t| + create_table "deploy_tokens", id: :serial, force: :cascade do |t| t.boolean "revoked", default: false t.boolean "read_repository", default: false, null: false t.boolean "read_registry", default: false, null: false @@ -853,7 +853,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["token"], name: "index_deploy_tokens_on_token", unique: true, using: :btree end - create_table "deployments", force: :cascade do |t| + create_table "deployments", id: :serial, force: :cascade do |t| t.integer "iid", null: false t.integer "project_id", null: false t.integer "environment_id", null: false @@ -879,7 +879,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id", "status"], name: "index_deployments_on_project_id_and_status", using: :btree end - create_table "emails", force: :cascade do |t| + create_table "emails", id: :serial, force: :cascade do |t| t.integer "user_id", null: false t.string "email", null: false t.datetime "created_at" @@ -892,7 +892,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id"], name: "index_emails_on_user_id", using: :btree end - create_table "environments", force: :cascade do |t| + create_table "environments", id: :serial, force: :cascade do |t| t.integer "project_id", null: false t.string "name", null: false t.datetime "created_at" @@ -905,7 +905,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id", "slug"], name: "index_environments_on_project_id_and_slug", unique: true, using: :btree end - create_table "events", force: :cascade do |t| + create_table "events", id: :serial, force: :cascade do |t| t.integer "project_id" t.integer "author_id", null: false t.integer "target_id" @@ -920,7 +920,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["target_type", "target_id"], name: "index_events_on_target_type_and_target_id", using: :btree end - create_table "feature_gates", force: :cascade do |t| + create_table "feature_gates", id: :serial, force: :cascade do |t| t.string "feature_key", null: false t.string "key", null: false t.string "value" @@ -929,14 +929,14 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["feature_key", "key", "value"], name: "index_feature_gates_on_feature_key_and_key_and_value", unique: true, using: :btree end - create_table "features", force: :cascade do |t| + create_table "features", id: :serial, force: :cascade do |t| t.string "key", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["key"], name: "index_features_on_key", unique: true, using: :btree end - create_table "fork_network_members", force: :cascade do |t| + create_table "fork_network_members", id: :serial, force: :cascade do |t| t.integer "fork_network_id", null: false t.integer "project_id", null: false t.integer "forked_from_project_id" @@ -945,13 +945,13 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id"], name: "index_fork_network_members_on_project_id", unique: true, using: :btree end - create_table "fork_networks", force: :cascade do |t| + create_table "fork_networks", id: :serial, force: :cascade do |t| t.integer "root_project_id" t.string "deleted_root_project_name" t.index ["root_project_id"], name: "index_fork_networks_on_root_project_id", unique: true, using: :btree end - create_table "forked_project_links", force: :cascade do |t| + create_table "forked_project_links", id: :serial, force: :cascade do |t| t.integer "forked_to_project_id", null: false t.integer "forked_from_project_id", null: false t.datetime "created_at" @@ -959,7 +959,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true, using: :btree end - create_table "gpg_key_subkeys", force: :cascade do |t| + create_table "gpg_key_subkeys", id: :serial, force: :cascade do |t| t.integer "gpg_key_id", null: false t.binary "keyid" t.binary "fingerprint" @@ -968,7 +968,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["keyid"], name: "index_gpg_key_subkeys_on_keyid", unique: true, using: :btree end - create_table "gpg_keys", force: :cascade do |t| + create_table "gpg_keys", id: :serial, force: :cascade do |t| t.datetime_with_timezone "created_at", null: false t.datetime_with_timezone "updated_at", null: false t.integer "user_id" @@ -980,7 +980,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id"], name: "index_gpg_keys_on_user_id", using: :btree end - create_table "gpg_signatures", force: :cascade do |t| + create_table "gpg_signatures", id: :serial, force: :cascade do |t| t.datetime_with_timezone "created_at", null: false t.datetime_with_timezone "updated_at", null: false t.integer "project_id" @@ -998,7 +998,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id"], name: "index_gpg_signatures_on_project_id", using: :btree end - create_table "group_custom_attributes", force: :cascade do |t| + create_table "group_custom_attributes", id: :serial, force: :cascade do |t| t.datetime_with_timezone "created_at", null: false t.datetime_with_timezone "updated_at", null: false t.integer "group_id", null: false @@ -1008,7 +1008,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["key", "value"], name: "index_group_custom_attributes_on_key_and_value", using: :btree end - create_table "identities", force: :cascade do |t| + create_table "identities", id: :serial, force: :cascade do |t| t.string "extern_uid" t.string "provider" t.integer "user_id" @@ -1017,7 +1017,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id"], name: "index_identities_on_user_id", using: :btree end - create_table "import_export_uploads", force: :cascade do |t| + create_table "import_export_uploads", id: :serial, force: :cascade do |t| t.datetime_with_timezone "updated_at", null: false t.integer "project_id" t.text "import_file" @@ -1026,7 +1026,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["updated_at"], name: "index_import_export_uploads_on_updated_at", using: :btree end - create_table "internal_ids", id: :bigserial, force: :cascade do |t| + create_table "internal_ids", force: :cascade do |t| t.integer "project_id" t.integer "usage", null: false t.integer "last_value", null: false @@ -1044,7 +1044,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id"], name: "index_issue_assignees_on_user_id", using: :btree end - create_table "issue_metrics", force: :cascade do |t| + create_table "issue_metrics", id: :serial, force: :cascade do |t| t.integer "issue_id", null: false t.datetime "first_mentioned_in_commit_at" t.datetime "first_associated_with_milestone_at" @@ -1054,7 +1054,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["issue_id"], name: "index_issue_metrics", using: :btree end - create_table "issues", force: :cascade do |t| + create_table "issues", id: :serial, force: :cascade do |t| t.string "title" t.integer "author_id" t.integer "project_id" @@ -1097,7 +1097,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["updated_by_id"], name: "index_issues_on_updated_by_id", where: "(updated_by_id IS NOT NULL)", using: :btree end - create_table "keys", force: :cascade do |t| + create_table "keys", id: :serial, force: :cascade do |t| t.integer "user_id" t.datetime "created_at" t.datetime "updated_at" @@ -1111,7 +1111,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id"], name: "index_keys_on_user_id", using: :btree end - create_table "label_links", force: :cascade do |t| + create_table "label_links", id: :serial, force: :cascade do |t| t.integer "label_id" t.integer "target_id" t.string "target_type" @@ -1121,7 +1121,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["target_id", "target_type"], name: "index_label_links_on_target_id_and_target_type", using: :btree end - create_table "label_priorities", force: :cascade do |t| + create_table "label_priorities", id: :serial, force: :cascade do |t| t.integer "project_id", null: false t.integer "label_id", null: false t.integer "priority", null: false @@ -1132,7 +1132,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id", "label_id"], name: "index_label_priorities_on_project_id_and_label_id", unique: true, using: :btree end - create_table "labels", force: :cascade do |t| + create_table "labels", id: :serial, force: :cascade do |t| t.string "title" t.string "color" t.integer "project_id" @@ -1151,7 +1151,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["type", "project_id"], name: "index_labels_on_type_and_project_id", using: :btree end - create_table "lfs_file_locks", force: :cascade do |t| + create_table "lfs_file_locks", id: :serial, force: :cascade do |t| t.integer "project_id", null: false t.integer "user_id", null: false t.datetime "created_at", null: false @@ -1160,7 +1160,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id"], name: "index_lfs_file_locks_on_user_id", using: :btree end - create_table "lfs_objects", force: :cascade do |t| + create_table "lfs_objects", id: :serial, force: :cascade do |t| t.string "oid", null: false t.bigint "size", null: false t.datetime "created_at" @@ -1171,7 +1171,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["oid"], name: "index_lfs_objects_on_oid", unique: true, using: :btree end - create_table "lfs_objects_projects", force: :cascade do |t| + create_table "lfs_objects_projects", id: :serial, force: :cascade do |t| t.integer "lfs_object_id", null: false t.integer "project_id", null: false t.datetime "created_at" @@ -1179,7 +1179,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id"], name: "index_lfs_objects_projects_on_project_id", using: :btree end - create_table "lists", force: :cascade do |t| + create_table "lists", id: :serial, force: :cascade do |t| t.integer "board_id", null: false t.integer "label_id" t.integer "list_type", default: 1, null: false @@ -1191,7 +1191,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["list_type"], name: "index_lists_on_list_type", using: :btree end - create_table "members", force: :cascade do |t| + create_table "members", id: :serial, force: :cascade do |t| t.integer "access_level", null: false t.integer "source_id", null: false t.string "source_type", null: false @@ -1254,7 +1254,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["merge_request_diff_id", "relative_order"], name: "index_merge_request_diff_files_on_mr_diff_id_and_order", unique: true, using: :btree end - create_table "merge_request_diffs", force: :cascade do |t| + create_table "merge_request_diffs", id: :serial, force: :cascade do |t| t.string "state" t.integer "merge_request_id", null: false t.datetime "created_at" @@ -1271,7 +1271,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["merge_request_id", "id"], name: "index_merge_request_diffs_on_merge_request_id_and_id_partial", where: "((NOT stored_externally) OR (stored_externally IS NULL))", using: :btree end - create_table "merge_request_metrics", force: :cascade do |t| + create_table "merge_request_metrics", id: :serial, force: :cascade do |t| t.integer "merge_request_id", null: false t.datetime "latest_build_started_at" t.datetime "latest_build_finished_at" @@ -1292,7 +1292,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["pipeline_id"], name: "index_merge_request_metrics_on_pipeline_id", using: :btree end - create_table "merge_requests", force: :cascade do |t| + create_table "merge_requests", id: :serial, force: :cascade do |t| t.string "target_branch", null: false t.string "source_branch", null: false t.integer "source_project_id" @@ -1350,7 +1350,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["updated_by_id"], name: "index_merge_requests_on_updated_by_id", where: "(updated_by_id IS NOT NULL)", using: :btree end - create_table "merge_requests_closing_issues", force: :cascade do |t| + create_table "merge_requests_closing_issues", id: :serial, force: :cascade do |t| t.integer "merge_request_id", null: false t.integer "issue_id", null: false t.datetime "created_at", null: false @@ -1359,7 +1359,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["merge_request_id"], name: "index_merge_requests_closing_issues_on_merge_request_id", using: :btree end - create_table "milestones", force: :cascade do |t| + create_table "milestones", id: :serial, force: :cascade do |t| t.string "title", null: false t.integer "project_id" t.text "description" @@ -1381,7 +1381,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["title"], name: "index_milestones_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"} end - create_table "namespaces", force: :cascade do |t| + create_table "namespaces", id: :serial, force: :cascade do |t| t.string "name", null: false t.string "path", null: false t.integer "owner_id" @@ -1416,7 +1416,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["type"], name: "index_namespaces_on_type", using: :btree end - create_table "note_diff_files", force: :cascade do |t| + create_table "note_diff_files", id: :serial, force: :cascade do |t| t.integer "diff_note_id", null: false t.text "diff", null: false t.boolean "new_file", null: false @@ -1429,7 +1429,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["diff_note_id"], name: "index_note_diff_files_on_diff_note_id", unique: true, using: :btree end - create_table "notes", force: :cascade do |t| + create_table "notes", id: :serial, force: :cascade do |t| t.text "note" t.string "noteable_type" t.integer "author_id" @@ -1464,7 +1464,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type", using: :btree end - create_table "notification_settings", force: :cascade do |t| + create_table "notification_settings", id: :serial, force: :cascade do |t| t.integer "user_id", null: false t.integer "source_id" t.string "source_type" @@ -1490,7 +1490,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id"], name: "index_notification_settings_on_user_id", using: :btree end - create_table "oauth_access_grants", force: :cascade do |t| + create_table "oauth_access_grants", id: :serial, force: :cascade do |t| t.integer "resource_owner_id", null: false t.integer "application_id", null: false t.string "token", null: false @@ -1502,7 +1502,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["token"], name: "index_oauth_access_grants_on_token", unique: true, using: :btree end - create_table "oauth_access_tokens", force: :cascade do |t| + create_table "oauth_access_tokens", id: :serial, force: :cascade do |t| t.integer "resource_owner_id" t.integer "application_id" t.string "token", null: false @@ -1516,7 +1516,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["token"], name: "index_oauth_access_tokens_on_token", unique: true, using: :btree end - create_table "oauth_applications", force: :cascade do |t| + create_table "oauth_applications", id: :serial, force: :cascade do |t| t.string "name", null: false t.string "uid", null: false t.string "secret", null: false @@ -1531,13 +1531,13 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree end - create_table "oauth_openid_requests", force: :cascade do |t| + create_table "oauth_openid_requests", id: :serial, force: :cascade do |t| t.integer "access_grant_id", null: false t.string "nonce", null: false t.index ["access_grant_id"], name: "index_oauth_openid_requests_on_access_grant_id", using: :btree end - create_table "pages_domains", force: :cascade do |t| + create_table "pages_domains", id: :serial, force: :cascade do |t| t.integer "project_id" t.text "certificate" t.text "encrypted_key" @@ -1548,6 +1548,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.string "verification_code", null: false t.datetime_with_timezone "enabled_until" t.datetime_with_timezone "remove_at" + t.boolean "auto_ssl_enabled", default: false, null: false t.index ["domain"], name: "index_pages_domains_on_domain", unique: true, using: :btree t.index ["project_id", "enabled_until"], name: "index_pages_domains_on_project_id_and_enabled_until", using: :btree t.index ["project_id"], name: "index_pages_domains_on_project_id", using: :btree @@ -1556,7 +1557,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["verified_at"], name: "index_pages_domains_on_verified_at", using: :btree end - create_table "personal_access_tokens", force: :cascade do |t| + create_table "personal_access_tokens", id: :serial, force: :cascade do |t| t.integer "user_id", null: false t.string "name", null: false t.boolean "revoked", default: false @@ -1570,7 +1571,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id"], name: "index_personal_access_tokens_on_user_id", using: :btree end - create_table "pool_repositories", id: :bigserial, force: :cascade do |t| + create_table "pool_repositories", force: :cascade do |t| t.integer "shard_id", null: false t.string "disk_path" t.string "state" @@ -1580,7 +1581,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["source_project_id"], name: "index_pool_repositories_on_source_project_id", unique: true, using: :btree end - create_table "programming_languages", force: :cascade do |t| + create_table "programming_languages", id: :serial, force: :cascade do |t| t.string "name", null: false t.string "color", null: false t.datetime_with_timezone "created_at", null: false @@ -1595,7 +1596,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id", "project_id", "access_level"], name: "index_project_authorizations_on_user_id_project_id_access_level", unique: true, using: :btree end - create_table "project_auto_devops", force: :cascade do |t| + create_table "project_auto_devops", id: :serial, force: :cascade do |t| t.integer "project_id", null: false t.datetime_with_timezone "created_at", null: false t.datetime_with_timezone "updated_at", null: false @@ -1605,14 +1606,14 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id"], name: "index_project_auto_devops_on_project_id", unique: true, using: :btree end - create_table "project_ci_cd_settings", force: :cascade do |t| + create_table "project_ci_cd_settings", id: :serial, force: :cascade do |t| t.integer "project_id", null: false t.boolean "group_runners_enabled", default: true, null: false t.boolean "merge_pipelines_enabled" t.index ["project_id"], name: "index_project_ci_cd_settings_on_project_id", unique: true, using: :btree end - create_table "project_custom_attributes", force: :cascade do |t| + create_table "project_custom_attributes", id: :serial, force: :cascade do |t| t.datetime_with_timezone "created_at", null: false t.datetime_with_timezone "updated_at", null: false t.integer "project_id", null: false @@ -1622,14 +1623,14 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id", "key"], name: "index_project_custom_attributes_on_project_id_and_key", unique: true, using: :btree end - create_table "project_daily_statistics", id: :bigserial, force: :cascade do |t| + create_table "project_daily_statistics", force: :cascade do |t| t.integer "project_id", null: false t.integer "fetch_count", null: false t.date "date" t.index ["project_id", "date"], name: "index_project_daily_statistics_on_project_id_and_date", unique: true, order: { date: :desc }, using: :btree end - create_table "project_deploy_tokens", force: :cascade do |t| + create_table "project_deploy_tokens", id: :serial, force: :cascade do |t| t.integer "project_id", null: false t.integer "deploy_token_id", null: false t.datetime_with_timezone "created_at", null: false @@ -1637,7 +1638,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id", "deploy_token_id"], name: "index_project_deploy_tokens_on_project_id_and_deploy_token_id", unique: true, using: :btree end - create_table "project_error_tracking_settings", primary_key: "project_id", id: :integer, force: :cascade do |t| + create_table "project_error_tracking_settings", primary_key: "project_id", id: :integer, default: nil, force: :cascade do |t| t.boolean "enabled", default: false, null: false t.string "api_url" t.string "encrypted_token" @@ -1646,7 +1647,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.string "organization_name" end - create_table "project_features", force: :cascade do |t| + create_table "project_features", id: :serial, force: :cascade do |t| t.integer "project_id", null: false t.integer "merge_requests_access_level" t.integer "issues_access_level" @@ -1660,7 +1661,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id"], name: "index_project_features_on_project_id", unique: true, using: :btree end - create_table "project_group_links", force: :cascade do |t| + create_table "project_group_links", id: :serial, force: :cascade do |t| t.integer "project_id", null: false t.integer "group_id", null: false t.datetime "created_at" @@ -1671,7 +1672,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id"], name: "index_project_group_links_on_project_id", using: :btree end - create_table "project_import_data", force: :cascade do |t| + create_table "project_import_data", id: :serial, force: :cascade do |t| t.integer "project_id" t.text "data" t.text "encrypted_credentials" @@ -1680,7 +1681,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id"], name: "index_project_import_data_on_project_id", using: :btree end - create_table "project_mirror_data", force: :cascade do |t| + create_table "project_mirror_data", id: :serial, force: :cascade do |t| t.integer "project_id", null: false t.string "status" t.string "jid" @@ -1690,7 +1691,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["status"], name: "index_project_mirror_data_on_status", using: :btree end - create_table "project_repositories", id: :bigserial, force: :cascade do |t| + create_table "project_repositories", force: :cascade do |t| t.integer "shard_id", null: false t.string "disk_path", null: false t.integer "project_id", null: false @@ -1699,7 +1700,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["shard_id"], name: "index_project_repositories_on_shard_id", using: :btree end - create_table "project_statistics", force: :cascade do |t| + create_table "project_statistics", id: :serial, force: :cascade do |t| t.integer "project_id", null: false t.integer "namespace_id", null: false t.bigint "commit_count", default: 0, null: false @@ -1711,7 +1712,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id"], name: "index_project_statistics_on_project_id", unique: true, using: :btree end - create_table "projects", force: :cascade do |t| + create_table "projects", id: :serial, force: :cascade do |t| t.string "name" t.string "path" t.text "description" @@ -1791,7 +1792,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree end - create_table "prometheus_metrics", force: :cascade do |t| + create_table "prometheus_metrics", id: :serial, force: :cascade do |t| t.integer "project_id" t.string "title", null: false t.string "query", null: false @@ -1809,7 +1810,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id"], name: "index_prometheus_metrics_on_project_id", using: :btree end - create_table "protected_branch_merge_access_levels", force: :cascade do |t| + create_table "protected_branch_merge_access_levels", id: :serial, force: :cascade do |t| t.integer "protected_branch_id", null: false t.integer "access_level", default: 40, null: false t.datetime "created_at", null: false @@ -1817,7 +1818,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["protected_branch_id"], name: "index_protected_branch_merge_access", using: :btree end - create_table "protected_branch_push_access_levels", force: :cascade do |t| + create_table "protected_branch_push_access_levels", id: :serial, force: :cascade do |t| t.integer "protected_branch_id", null: false t.integer "access_level", default: 40, null: false t.datetime "created_at", null: false @@ -1825,7 +1826,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["protected_branch_id"], name: "index_protected_branch_push_access", using: :btree end - create_table "protected_branches", force: :cascade do |t| + create_table "protected_branches", id: :serial, force: :cascade do |t| t.integer "project_id", null: false t.string "name", null: false t.datetime "created_at" @@ -1833,7 +1834,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id"], name: "index_protected_branches_on_project_id", using: :btree end - create_table "protected_tag_create_access_levels", force: :cascade do |t| + create_table "protected_tag_create_access_levels", id: :serial, force: :cascade do |t| t.integer "protected_tag_id", null: false t.integer "access_level", default: 40 t.integer "user_id" @@ -1845,7 +1846,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id"], name: "index_protected_tag_create_access_levels_on_user_id", using: :btree end - create_table "protected_tags", force: :cascade do |t| + create_table "protected_tags", id: :serial, force: :cascade do |t| t.integer "project_id", null: false t.string "name", null: false t.datetime "created_at", null: false @@ -1866,7 +1867,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["event_id"], name: "index_push_event_payloads_on_event_id", unique: true, using: :btree end - create_table "redirect_routes", force: :cascade do |t| + create_table "redirect_routes", id: :serial, force: :cascade do |t| t.integer "source_id", null: false t.string "source_type", null: false t.string "path", null: false @@ -1876,7 +1877,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["source_type", "source_id"], name: "index_redirect_routes_on_source_type_and_source_id", using: :btree end - create_table "release_links", id: :bigserial, force: :cascade do |t| + create_table "release_links", force: :cascade do |t| t.integer "release_id", null: false t.string "url", null: false t.string "name", null: false @@ -1886,7 +1887,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["release_id", "url"], name: "index_release_links_on_release_id_and_url", unique: true, using: :btree end - create_table "releases", force: :cascade do |t| + create_table "releases", id: :serial, force: :cascade do |t| t.string "tag" t.text "description" t.integer "project_id" @@ -1902,7 +1903,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id"], name: "index_releases_on_project_id", using: :btree end - create_table "remote_mirrors", force: :cascade do |t| + create_table "remote_mirrors", id: :serial, force: :cascade do |t| t.integer "project_id" t.string "url" t.boolean "enabled", default: false @@ -1930,7 +1931,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["project_id", "programming_language_id"], name: "index_repository_languages_on_project_and_languages_id", unique: true, using: :btree end - create_table "resource_label_events", id: :bigserial, force: :cascade do |t| + create_table "resource_label_events", force: :cascade do |t| t.integer "action", null: false t.integer "issue_id" t.integer "merge_request_id" @@ -1946,7 +1947,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id"], name: "index_resource_label_events_on_user_id", using: :btree end - create_table "routes", force: :cascade do |t| + create_table "routes", id: :serial, force: :cascade do |t| t.integer "source_id", null: false t.string "source_type", null: false t.string "path", null: false @@ -1958,7 +1959,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["source_type", "source_id"], name: "index_routes_on_source_type_and_source_id", unique: true, using: :btree end - create_table "sent_notifications", force: :cascade do |t| + create_table "sent_notifications", id: :serial, force: :cascade do |t| t.integer "project_id" t.integer "noteable_id" t.string "noteable_type" @@ -1972,7 +1973,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["reply_key"], name: "index_sent_notifications_on_reply_key", unique: true, using: :btree end - create_table "services", force: :cascade do |t| + create_table "services", id: :serial, force: :cascade do |t| t.string "type" t.string "title" t.integer "project_id" @@ -1999,12 +2000,12 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["type"], name: "index_services_on_type", using: :btree end - create_table "shards", force: :cascade do |t| + create_table "shards", id: :serial, force: :cascade do |t| t.string "name", null: false t.index ["name"], name: "index_shards_on_name", unique: true, using: :btree end - create_table "snippets", force: :cascade do |t| + create_table "snippets", id: :serial, force: :cascade do |t| t.string "title" t.text "content" t.integer "author_id", null: false @@ -2027,7 +2028,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["visibility_level"], name: "index_snippets_on_visibility_level", using: :btree end - create_table "spam_logs", force: :cascade do |t| + create_table "spam_logs", id: :serial, force: :cascade do |t| t.integer "user_id" t.string "source_ip" t.string "user_agent" @@ -2041,7 +2042,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.boolean "recaptcha_verified", default: false, null: false end - create_table "subscriptions", force: :cascade do |t| + create_table "subscriptions", id: :serial, force: :cascade do |t| t.integer "user_id" t.integer "subscribable_id" t.string "subscribable_type" @@ -2053,7 +2054,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["subscribable_id", "subscribable_type", "user_id", "project_id"], name: "index_subscriptions_on_subscribable_and_user_id_and_project_id", unique: true, using: :btree end - create_table "suggestions", id: :bigserial, force: :cascade do |t| + create_table "suggestions", force: :cascade do |t| t.integer "note_id", null: false t.integer "relative_order", limit: 2, null: false t.boolean "applied", default: false, null: false @@ -2066,7 +2067,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["note_id", "relative_order"], name: "index_suggestions_on_note_id_and_relative_order", unique: true, using: :btree end - create_table "system_note_metadata", force: :cascade do |t| + create_table "system_note_metadata", id: :serial, force: :cascade do |t| t.integer "note_id", null: false t.integer "commit_count" t.string "action" @@ -2075,7 +2076,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["note_id"], name: "index_system_note_metadata_on_note_id", unique: true, using: :btree end - create_table "taggings", force: :cascade do |t| + create_table "taggings", id: :serial, force: :cascade do |t| t.integer "tag_id" t.integer "taggable_id" t.string "taggable_type" @@ -2089,14 +2090,14 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["taggable_id", "taggable_type"], name: "index_taggings_on_taggable_id_and_taggable_type", using: :btree end - create_table "tags", force: :cascade do |t| + create_table "tags", id: :serial, force: :cascade do |t| t.string "name" t.integer "taggings_count", default: 0 t.index ["name"], name: "index_tags_on_name", unique: true, using: :btree t.index ["name"], name: "index_tags_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"} end - create_table "term_agreements", force: :cascade do |t| + create_table "term_agreements", id: :serial, force: :cascade do |t| t.integer "term_id", null: false t.integer "user_id", null: false t.boolean "accepted", default: false, null: false @@ -2107,7 +2108,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id"], name: "index_term_agreements_on_user_id", using: :btree end - create_table "timelogs", force: :cascade do |t| + create_table "timelogs", id: :serial, force: :cascade do |t| t.integer "time_spent", null: false t.integer "user_id" t.datetime "created_at", null: false @@ -2120,7 +2121,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id"], name: "index_timelogs_on_user_id", using: :btree end - create_table "todos", force: :cascade do |t| + create_table "todos", id: :serial, force: :cascade do |t| t.integer "user_id", null: false t.integer "project_id" t.integer "target_id" @@ -2144,12 +2145,12 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id"], name: "index_todos_on_user_id", using: :btree end - create_table "trending_projects", force: :cascade do |t| + create_table "trending_projects", id: :serial, force: :cascade do |t| t.integer "project_id", null: false t.index ["project_id"], name: "index_trending_projects_on_project_id", unique: true, using: :btree end - create_table "u2f_registrations", force: :cascade do |t| + create_table "u2f_registrations", id: :serial, force: :cascade do |t| t.text "certificate" t.string "key_handle" t.string "public_key" @@ -2162,7 +2163,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id"], name: "index_u2f_registrations_on_user_id", using: :btree end - create_table "uploads", force: :cascade do |t| + create_table "uploads", id: :serial, force: :cascade do |t| t.bigint "size", null: false t.string "path", limit: 511, null: false t.string "checksum", limit: 64 @@ -2179,7 +2180,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["uploader", "path"], name: "index_uploads_on_uploader_and_path", using: :btree end - create_table "user_agent_details", force: :cascade do |t| + create_table "user_agent_details", id: :serial, force: :cascade do |t| t.string "user_agent", null: false t.string "ip_address", null: false t.integer "subject_id", null: false @@ -2190,14 +2191,14 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["subject_id", "subject_type"], name: "index_user_agent_details_on_subject_id_and_subject_type", using: :btree end - create_table "user_callouts", force: :cascade do |t| + create_table "user_callouts", id: :serial, force: :cascade do |t| t.integer "feature_name", null: false t.integer "user_id", null: false t.index ["user_id", "feature_name"], name: "index_user_callouts_on_user_id_and_feature_name", unique: true, using: :btree t.index ["user_id"], name: "index_user_callouts_on_user_id", using: :btree end - create_table "user_custom_attributes", force: :cascade do |t| + create_table "user_custom_attributes", id: :serial, force: :cascade do |t| t.datetime_with_timezone "created_at", null: false t.datetime_with_timezone "updated_at", null: false t.integer "user_id", null: false @@ -2214,7 +2215,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id"], name: "index_user_interacted_projects_on_user_id", using: :btree end - create_table "user_preferences", force: :cascade do |t| + create_table "user_preferences", id: :serial, force: :cascade do |t| t.integer "user_id", null: false t.integer "issue_notes_filter", limit: 2, default: 0, null: false t.integer "merge_request_notes_filter", limit: 2, default: 0, null: false @@ -2226,7 +2227,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id"], name: "index_user_preferences_on_user_id", unique: true, using: :btree end - create_table "user_statuses", primary_key: "user_id", force: :cascade do |t| + create_table "user_statuses", primary_key: "user_id", id: :serial, force: :cascade do |t| t.integer "cached_markdown_version" t.string "emoji", default: "speech_balloon", null: false t.string "message", limit: 100 @@ -2234,7 +2235,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id"], name: "index_user_statuses_on_user_id", using: :btree end - create_table "user_synced_attributes_metadata", force: :cascade do |t| + create_table "user_synced_attributes_metadata", id: :serial, force: :cascade do |t| t.boolean "name_synced", default: false t.boolean "email_synced", default: false t.boolean "location_synced", default: false @@ -2243,7 +2244,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id"], name: "index_user_synced_attributes_metadata_on_user_id", unique: true, using: :btree end - create_table "users", force: :cascade do |t| + create_table "users", id: :serial, force: :cascade do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false t.string "reset_password_token" @@ -2329,7 +2330,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["username"], name: "index_users_on_username_trigram", using: :gin, opclasses: {"username"=>"gin_trgm_ops"} end - create_table "users_star_projects", force: :cascade do |t| + create_table "users_star_projects", id: :serial, force: :cascade do |t| t.integer "project_id", null: false t.integer "user_id", null: false t.datetime "created_at" @@ -2338,7 +2339,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["user_id", "project_id"], name: "index_users_star_projects_on_user_id_and_project_id", unique: true, using: :btree end - create_table "web_hook_logs", force: :cascade do |t| + create_table "web_hook_logs", id: :serial, force: :cascade do |t| t.integer "web_hook_id", null: false t.string "trigger" t.string "url" @@ -2355,7 +2356,7 @@ ActiveRecord::Schema.define(version: 20190408163745) do t.index ["web_hook_id"], name: "index_web_hook_logs_on_web_hook_id", using: :btree end - create_table "web_hooks", force: :cascade do |t| + create_table "web_hooks", id: :serial, force: :cascade do |t| t.integer "project_id" t.datetime "created_at" t.datetime "updated_at" diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md index 830f015a108..9983b015b31 100644 --- a/doc/ci/variables/README.md +++ b/doc/ci/variables/README.md @@ -434,8 +434,9 @@ Below you can find supported syntax reference: 1. Equality matching using a string > Example: `$VARIABLE == "some value"` + > Example: `$VARIABLE != "some value"` _(added in 11.11)_ - You can use equality operator `==` to compare a variable content to a + You can use equality operator `==` or `!=` to compare a variable content to a string. We support both, double quotes and single quotes to define a string value, so both `$VARIABLE == "some value"` and `$VARIABLE == 'some value'` are supported. `"some value" == $VARIABLE` is correct too. @@ -443,22 +444,26 @@ Below you can find supported syntax reference: 1. Checking for an undefined value > Example: `$VARIABLE == null` + > Example: `$VARIABLE != null` _(added in 11.11)_ It sometimes happens that you want to check whether a variable is defined or not. To do that, you can compare a variable to `null` keyword, like `$VARIABLE == null`. This expression is going to evaluate to truth if - variable is not defined. + variable is not defined when `==` is used, or to falsey if `!=` is used. 1. Checking for an empty variable > Example: `$VARIABLE == ""` + > Example: `$VARIABLE != ""` _(added in 11.11)_ If you want to check whether a variable is defined, but is empty, you can - simply compare it against an empty string, like `$VAR == ''`. + simply compare it against an empty string, like `$VAR == ''` or non-empty + string `$VARIABLE != ""`. 1. Comparing two variables > Example: `$VARIABLE_1 == $VARIABLE_2` + > Example: `$VARIABLE_1 != $VARIABLE_2` _(added in 11.11)_ It is possible to compare two variables. This is going to compare values of these variables. @@ -477,9 +482,11 @@ Below you can find supported syntax reference: 1. Pattern matching _(added in 11.0)_ > Example: `$VARIABLE =~ /^content.*/` + > Example: `$VARIABLE_1 !~ /^content.*/` _(added in 11.11)_ It is possible perform pattern matching against a variable and regular - expression. Expression like this evaluates to truth if matches are found. + expression. Expression like this evaluates to truth if matches are found + when using `=~`. It evaluates to truth if matches are not found when `!~` is used. Pattern matching is case-sensitive by default. Use `i` flag modifier, like `/pattern/i` to make a pattern case-insensitive. diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md index 846c539daab..4c562138b98 100644 --- a/doc/ci/variables/predefined_variables.md +++ b/doc/ci/variables/predefined_variables.md @@ -49,23 +49,23 @@ future GitLab releases.** | `CI_JOB_STAGE` | 9.0 | 0.5 | The name of the stage as defined in `.gitlab-ci.yml` | | `CI_JOB_TOKEN` | 9.0 | 1.2 | Token used for authenticating with the [GitLab Container Registry][registry] and downloading [dependent repositories][dependent-repositories] | | `CI_JOB_URL` | 11.1 | 0.5 | Job details URL | -| `CI_MERGE_REQUEST_ID` | 11.6 | all | The ID of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) | -| `CI_MERGE_REQUEST_IID` | 11.6 | all | The IID of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) | -| `CI_MERGE_REQUEST_PROJECT_ID` | 11.6 | all | The ID of the project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) | -| `CI_MERGE_REQUEST_PROJECT_PATH` | 11.6 | all | The path of the project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) (e.g. `namespace/awesome-project`) | -| `CI_MERGE_REQUEST_PROJECT_URL` | 11.6 | all | The URL of the project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) (e.g. `http://192.168.10.15:3000/namespace/awesome-project`) | -| `CI_MERGE_REQUEST_REF_PATH` | 11.6 | all | The ref path of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). (e.g. `refs/merge-requests/1/head`) | -| `CI_MERGE_REQUEST_SOURCE_BRANCH_NAME` | 11.6 | all | The source branch name of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) | -| `CI_MERGE_REQUEST_SOURCE_BRANCH_SHA` | 11.9 | all | The HEAD sha of the source branch of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) | -| `CI_MERGE_REQUEST_SOURCE_PROJECT_ID` | 11.6 | all | The ID of the source project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) | -| `CI_MERGE_REQUEST_SOURCE_PROJECT_PATH` | 11.6 | all | The path of the source project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) | -| `CI_MERGE_REQUEST_SOURCE_PROJECT_URL` | 11.6 | all | The URL of the source project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) | -| `CI_MERGE_REQUEST_TARGET_BRANCH_NAME` | 11.6 | all | The target branch name of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) | -| `CI_MERGE_REQUEST_TARGET_BRANCH_SHA` | 11.9 | all | The HEAD sha of the target branch of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) | -| `CI_MERGE_REQUEST_TITLE` | 11.9 | all | The title of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) | -| `CI_MERGE_REQUEST_ASSIGNEES` | 11.9 | all | Comma-separated list of usernames of assignees for the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). [Multiple assignees for merge requests](https://gitlab.com/gitlab-org/gitlab-ee/issues/2004) is scheduled for a future release | -| `CI_MERGE_REQUEST_MILESTONE` | 11.9 | all | The milestone title of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) | -| `CI_MERGE_REQUEST_LABELS` | 11.9 | all | Comma-separated label names of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) | +| `CI_MERGE_REQUEST_ID` | 11.6 | all | The ID of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. | +| `CI_MERGE_REQUEST_IID` | 11.6 | all | The IID of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. | +| `CI_MERGE_REQUEST_PROJECT_ID` | 11.6 | all | The ID of the project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. | +| `CI_MERGE_REQUEST_PROJECT_PATH` | 11.6 | all | The path of the project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) (e.g. `namespace/awesome-project`). Available only if `only: [merge_requests]` is used and the merge request is created. | +| `CI_MERGE_REQUEST_PROJECT_URL` | 11.6 | all | The URL of the project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) (e.g. `http://192.168.10.15:3000/namespace/awesome-project`). Available only if `only: [merge_requests]` is used and the merge request is created. | +| `CI_MERGE_REQUEST_REF_PATH` | 11.6 | all | The ref path of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). (e.g. `refs/merge-requests/1/head`). Available only if `only: [merge_requests]` is used and the merge request is created. | +| `CI_MERGE_REQUEST_SOURCE_BRANCH_NAME` | 11.6 | all | The source branch name of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. | +| `CI_MERGE_REQUEST_SOURCE_BRANCH_SHA` | 11.9 | all | The HEAD sha of the source branch of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. | +| `CI_MERGE_REQUEST_SOURCE_PROJECT_ID` | 11.6 | all | The ID of the source project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. | +| `CI_MERGE_REQUEST_SOURCE_PROJECT_PATH` | 11.6 | all | The path of the source project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. | +| `CI_MERGE_REQUEST_SOURCE_PROJECT_URL` | 11.6 | all | The URL of the source project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. | +| `CI_MERGE_REQUEST_TARGET_BRANCH_NAME` | 11.6 | all | The target branch name of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. | +| `CI_MERGE_REQUEST_TARGET_BRANCH_SHA` | 11.9 | all | The HEAD sha of the target branch of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. | +| `CI_MERGE_REQUEST_TITLE` | 11.9 | all | The title of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. | +| `CI_MERGE_REQUEST_ASSIGNEES` | 11.9 | all | Comma-separated list of username(s) of assignee(s) for the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. | +| `CI_MERGE_REQUEST_MILESTONE` | 11.9 | all | The milestone title of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. | +| `CI_MERGE_REQUEST_LABELS` | 11.9 | all | Comma-separated label names of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). Available only if `only: [merge_requests]` is used and the merge request is created. | | `CI_NODE_INDEX` | 11.5 | all | Index of the job in the job set. If the job is not parallelized, this variable is not set. | | `CI_NODE_TOTAL` | 11.5 | all | Total number of instances of this job running in parallel. If the job is not parallelized, this variable is set to `1`. | | `CI_API_V4_URL` | 11.7 | all | The GitLab API v4 root URL | diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md index ffb89848edb..fbca99fbfea 100644 --- a/doc/development/documentation/styleguide.md +++ b/doc/development/documentation/styleguide.md @@ -6,7 +6,7 @@ description: 'Writing styles, markup, formatting, and other standards for GitLab This document defines the standards for GitLab's documentation content and files. -For broader information about the documentation, see the [Documentation guidelines](index.md). +For broader information about the documentation, see the [Documentation guidelines](index.md). For programmatic help adhering to the guidelines, see [linting](index.md#linting). @@ -38,7 +38,7 @@ Include any media types/sources if the content is relevant to readers. You can f - If you use an image that has a separate source file (for example, a vector or diagram format), link the image to the source file so that it may be reused or updated by anyone. - Do not copy and paste content from other sources unless it is a limited quotation with the source cited. Typically it is better to either rephrase relevant information in your own words or link out to the other source. - + ### No special types In the software industry, it is a best practice to organize documentatioin in different types. For example, [Divio recommends](https://www.divio.com/blog/documentation/): @@ -77,7 +77,7 @@ and cross-link between any related content. We employ a **docs-first methodology** to help ensure that the docs remain a complete and trusted resource, and to make communicating about the use of GitLab more efficient. * If the answer to a question exists in documentation, share the link to the docs instead of rephrasing the information. -* When you encounter new information not available in GitLab’s documentation (for example, when working on a support case or testing a feature), your first step should be to create a merge request to add this information to the docs. You can then share the MR in order to communicate this information. +* When you encounter new information not available in GitLab’s documentation (for example, when working on a support case or testing a feature), your first step should be to create a merge request to add this information to the docs. You can then share the MR in order to communicate this information. New information that would be useful toward the future usage or troubleshooting of GitLab should not be written directly in a forum or other messaging system, but added to a docs MR and then referenced, as described above. Note that among any other doc changes, you can always add a Troubleshooting section to a doc if none exists, or un-comment and use the placeholder Troubleshooting section included as part of our [doc template](structure.md#template-for-new-docs), if present. @@ -96,7 +96,7 @@ The [documentation website](https://docs.gitlab.com) uses GitLab Kramdown as its The [`gitlab-kramdown`](https://gitlab.com/gitlab-org/gitlab_kramdown) Ruby gem will support all [GFM markup](../../user/markdown.md) in the future. That is, all markup that is supported for display in the GitLab application itself. For now, -use regular Markdown markup, following the rules in the linked style guide. +use regular Markdown markup, following the rules in the linked style guide. Note that Kramdown-specific markup (e.g., `{:.class}`) will not render properly on GitLab instances under [`/help`](index.md#gitlab-help). @@ -652,6 +652,7 @@ keyword "only": - For GitLab Ultimate: `**[ULTIMATE ONLY]**`. For GitLab.com only tiers (when the feature is not available for self-hosted instances): + - For GitLab Bronze and higher tiers: `**[BRONZE ONLY]**`. - For GitLab Silver and higher tiers: `**[SILVER ONLY]**`. - For GitLab Gold: `**[GOLD ONLY]**`. diff --git a/doc/development/i18n/proofreader.md b/doc/development/i18n/proofreader.md index e7098521606..aa463e467d4 100644 --- a/doc/development/i18n/proofreader.md +++ b/doc/development/i18n/proofreader.md @@ -16,14 +16,14 @@ are very appreciative of the work done by translators and proofreaders! - Chinese Simplified 简体中文 - Huang Tao - [GitLab](https://gitlab.com/htve), [Crowdin](https://crowdin.com/profile/htve) - Victor Wu - [GitLab](https://gitlab.com/victorwuky), [Crowdin](https://crowdin.com/profile/victorwu) + - Xiaogang Wen - [GitLab](https://gitlab.com/xiaogang_gitlab), [Crowdin](https://crowdin.com/profile/xiaogang_gitlab) - Chinese Traditional 繁體中文 - - Huang Tao - [GitLab](https://gitlab.com/htve), [Crowdin](https://crowdin.com/profile/htve) - Weizhe Ding - [GitLab](https://gitlab.com/d.weizhe), [Crowdin](https://crowdin.com/profile/d.weizhe) - Yi-Jyun Pan - [GitLab](https://gitlab.com/pan93412), [Crowdin](https://crowdin.com/profile/pan93412) - Victor Wu - [GitLab](https://gitlab.com/victorwuky), [Crowdin](https://crowdin.com/profile/victorwu) - Chinese Traditional, Hong Kong 繁體中文 (香港) - - Huang Tao - [GitLab](https://gitlab.com/htve), [Crowdin](https://crowdin.com/profile/htve) - Victor Wu - [GitLab](https://gitlab.com/victorwuky), [Crowdin](https://crowdin.com/profile/victorwu) + - Ivan Ip - [GitLab](https://gitlab.com/lifehome), [Crowdin](https://crowdin.com/profile/lifehome) - Czech - Proofreaders needed. - Danish @@ -59,7 +59,6 @@ are very appreciative of the work done by translators and proofreaders! - Hiroyuki Sato - [GitLab](https://gitlab.com/hiroponz), [Crowdin](https://crowdin.com/profile/hiroponz) - Korean - Chang-Ho Cha - [GitLab](https://gitlab.com/changho-cha), [Crowdin](https://crowdin.com/profile/zzazang) - - Huang Tao - [GitLab](https://gitlab.com/htve), [Crowdin](https://crowdin.com/profile/htve) - Ji Hun Oh - [GitLab](https://gitlab.com/Baw-Appie), [Crowdin](https://crowdin.com/profile/BawAppie) - Jeongwhan Choi - [GitLab](https://gitlab.com/jeongwhanchoi), [Crowdin](https://crowdin.com/profile/jeongwhanchoi) - Mongolian diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md index dcb32c89f65..1ae69127295 100644 --- a/doc/development/rake_tasks.md +++ b/doc/development/rake_tasks.md @@ -12,6 +12,22 @@ The `setup` task is an alias for `gitlab:setup`. This tasks calls `db:reset` to create the database, calls `add_limits_mysql` that adds limits to the database schema in case of a MySQL database and finally it calls `db:seed_fu` to seed the database. Note: `db:setup` calls `db:seed` but this does nothing. +### Seeding issues for all or a given project + +You can seed issues for all or a given project with the `gitlab:seed:issues` +task: + +```shell +# All projects +bin/rake gitlab:seed:issues + +# A specific project +bin/rake "gitlab:seed:issues[group-path/project-path]" +``` + +By default, this seeds an average of 2 issues per week for the last 5 weeks per +project. + ### Automation If you're very sure that you want to **wipe the current database** and refill diff --git a/doc/user/project/integrations/gitlab_slack_application.md b/doc/user/project/integrations/gitlab_slack_application.md new file mode 100644 index 00000000000..8e062ca627b --- /dev/null +++ b/doc/user/project/integrations/gitlab_slack_application.md @@ -0,0 +1,65 @@ +# GitLab Slack application **[FREE ONLY]** + +NOTE: **Note:** +The GitLab Slack application is only configurable for GitLab.com. It will **not** +work for on-premises installations where you can configure the +[Slack slash commands](slack_slash_commands.md) service instead. We're working +with Slack on making this configurable for all GitLab installations, but there's +no ETA. +It was first introduced in GitLab 9.4 and distributed to Slack App Directory in +GitLab 10.2. + +Slack provides a native application which you can enable via your project's +integrations on GitLab.com. + +## Slack App Directory + +The simplest way to enable the GitLab Slack application for your workspace is to +install the [GitLab application](https://slack-platform.slack.com/apps/A676ADMV5-gitlab) from +the [Slack App Directory](https://slack.com/apps). + +Clicking install will take you to the +[GitLab Slack application landing page](https://gitlab.com/profile/slack/edit) +where you can select a project to enable the GitLab Slack application for. + +![GitLab Slack application landing page](img/gitlab_slack_app_landing_page.png) + +## Configuration + +Alternatively, you can configure the Slack application with a project's +integration settings. + +Keep in mind that you need to have the appropriate permissions for your Slack +team in order to be able to install a new application, read more in Slack's +docs on [Adding an app to your team][slack-docs]. + +To enable GitLab's service for your Slack team: + +1. Go to your project's **Settings > Integration > Slack application** (only + visible on GitLab.com) +1. Click the "Add to Slack" button + +That's all! You can now start using the Slack slash commands. + +## Usage + +After confirming the installation, you, and everyone else in your Slack team, +can use all the [slash commands]. + +When you perform your first slash command you will be asked to authorize your +Slack user on GitLab.com. + +The only difference with the [manually configurable Slack slash commands][slack-manual] +is that all the commands should be prefixed with the `/gitlab` keyword. +We are working on making this configurable in the future. + +For example, to show the issue number `1001` under the `gitlab-org/gitlab-ce` +project, you would do: + +``` +/gitlab gitlab-org/gitlab-ce issue show 1001 +``` + +[slack-docs]: https://get.slack.help/hc/en-us/articles/202035138-Adding-apps-to-your-team +[slash commands]: ../../../integration/slash_commands.md +[slack-manual]: slack_slash_commands.md diff --git a/doc/user/project/integrations/img/gitlab_slack_app_landing_page.png b/doc/user/project/integrations/img/gitlab_slack_app_landing_page.png Binary files differnew file mode 100644 index 00000000000..57cd35c9f5d --- /dev/null +++ b/doc/user/project/integrations/img/gitlab_slack_app_landing_page.png diff --git a/doc/user/project/integrations/project_services.md b/doc/user/project/integrations/project_services.md index 42c7824a125..339e6873c41 100644 --- a/doc/user/project/integrations/project_services.md +++ b/doc/user/project/integrations/project_services.md @@ -14,8 +14,7 @@ want to configure. ![Project services list](img/project_services.png) -Below, you will find a list of the currently supported ones accompanied with -comprehensive documentation. +Below, you will find a list of the currently supported ones accompanied with comprehensive documentation. ## Services @@ -46,7 +45,8 @@ Click on the service links to see further configuration instructions and details | Packagist | Update your project on Packagist, the main Composer repository | | Pipelines emails | Email the pipeline status to a list of recipients | | [Slack Notifications](slack.md) | Send GitLab events (e.g. issue created) to Slack as notifications | -| [Slack slash commands](slack_slash_commands.md) | Use slash commands in Slack to control GitLab | +| [Slack slash commands](slack_slash_commands.md) **[CORE ONLY]** | Use slash commands in Slack to control GitLab | +| [GitLab Slack application](gitlab_slack_application.md) **[FREE ONLY]** | Use Slack's official application | | PivotalTracker | Project Management Software (Source Commits Endpoint) | | [Prometheus](prometheus.md) | Monitor the performance of your deployed apps | | Pushover | Pushover makes it easy to get real-time notifications on your Android device, iPhone, iPad, and Desktop | diff --git a/doc/user/project/integrations/slack_slash_commands.md b/doc/user/project/integrations/slack_slash_commands.md index c267da69bb3..371e78ca3a4 100644 --- a/doc/user/project/integrations/slack_slash_commands.md +++ b/doc/user/project/integrations/slack_slash_commands.md @@ -1,10 +1,15 @@ -# Slack slash commands +# Slack slash commands **[CORE ONLY]** -> Introduced in GitLab 8.15 +> Introduced in GitLab 8.15. -Slack slash commands allow you to control GitLab and view content right inside Slack, without having to leave it. This requires configurations in both Slack and GitLab. +Slack slash commands allow you to control GitLab and view content right inside +Slack, without having to leave it. This requires configurations in both Slack and GitLab. -> Note: GitLab can also send events (e.g. issue created) to Slack as notifications. This is the separately configured [Slack Notifications Service](slack.md). +GitLab can also send events (e.g., `issue created`) to Slack as notifications. +This is the separately configured [Slack Notifications Service](slack.md). + +NOTE: **Note:** +For GitLab.com, use the [Slack app](gitlab_slack_application.md) instead. ## Configuration diff --git a/doc/user/project/pipelines/job_artifacts.md b/doc/user/project/pipelines/job_artifacts.md index a3f40c20192..629b5e1fde4 100644 --- a/doc/user/project/pipelines/job_artifacts.md +++ b/doc/user/project/pipelines/job_artifacts.md @@ -55,7 +55,8 @@ For more examples on artifacts, follow the [artifacts reference in > **Note:** > With [GitLab 10.1][ce-14399], HTML files in a public project can be previewed > directly in a new tab without the need to download them when -> [GitLab Pages](../../../administration/pages/index.md) is enabled +> [GitLab Pages](../../../administration/pages/index.md) is enabled. +> The same holds for textual formats (currently supported extensions: `.txt`, `.json`, and `.log`). After a job finishes, if you visit the job's specific page, there are three buttons. You can download the artifacts archive or browse its contents, whereas diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md index 718566a539f..97ecc4c0d65 100644 --- a/doc/user/project/repository/index.md +++ b/doc/user/project/repository/index.md @@ -97,7 +97,7 @@ Some things to note about precedence: > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/2508) in GitLab 9.1 -[Jupyter][jupyter] Notebook (previously IPython Notebook) files are used for +[Jupyter](https://jupyter.org) Notebook (previously IPython Notebook) files are used for interactive computing in many fields and contain a complete record of the user's sessions and include code, narrative text, equations and rich output. @@ -220,14 +220,10 @@ Select branches to compare using the [branch filter search box](branches/index.m Find it under your project's **Repository > Compare**. -## Locked files +## Locked files **[PREMIUM]** -> Available in [GitLab Premium](https://about.gitlab.com/pricing/). - -Lock your files to prevent any conflicting changes. - -[File Locking](https://docs.gitlab.com/ee/user/project/file_lock.html) is available only in -[GitLab Premium](https://about.gitlab.com/pricing/). +Use [File Locking](https://docs.gitlab.com/ee/user/project/file_lock.html) to +lock your files to prevent any conflicting changes. ## Repository's API @@ -243,22 +239,19 @@ used for cloning your project. The button is only shown on macOS. ## Download Source Code -Source code stored in the repository can be downloaded. +> Support for directory download was [introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/24704) in GitLab 11.11. +The source code stored in a repository can be downloaded from the UI. By clicking the download icon, a dropdown will open with links to download the following: ![Download source code](img/download_source_code.png) -- **Source Code:** - This allows users to download the source code on branch they're currently - viewing. Available zip, tar, tar.gz and tar.bz2. +- **Source code:** + allows users to download the source code on branch they're currently + viewing. Available extensions: `zip`, `tar`, `tar.gz`, and `tar.bz2`. - **Directory:** - > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/24704) in GitLab 11.10 - - Only shows up when viewing a sub-directory. This allows users to download - the specific directory they're currently viewing. Also available in zip, tar, - tar.gz and tar.bz2. + only shows up when viewing a sub-directory. This allows users to download + the specific directory they're currently viewing. Also available in `zip`, + `tar`, `tar.gz`, and `tar.bz2`. - **Artifacts:** - This allows users to download the artifacts of the latest CI build. - -[jupyter]: https://jupyter.org + allows users to download the artifacts of the latest CI build. diff --git a/lib/api/branches.rb b/lib/api/branches.rb index 5c98b0ad56c..65d7f68bbf9 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -162,8 +162,8 @@ module API result = DeleteBranchService.new(user_project, current_user) .execute(params[:branch]) - if result[:status] != :success - render_api_error!(result[:message], result[:return_code]) + if result.error? + render_api_error!(result.message, result.http_status) end end end diff --git a/lib/banzai/filter/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb index 77e4c438bd0..4d67140b0a1 100644 --- a/lib/banzai/filter/label_reference_filter.rb +++ b/lib/banzai/filter/label_reference_filter.rb @@ -82,16 +82,18 @@ module Banzai def object_link_text(object, matches) label_suffix = '' + parent = project || group if project || full_path_ref?(matches) project_path = full_project_path(matches[:namespace], matches[:project]) parent_from_ref = from_ref_cached(project_path) - reference = parent_from_ref.to_human_reference(project || group) + reference = parent_from_ref.to_human_reference(parent) label_suffix = " <i>in #{reference}</i>" if reference.present? end - LabelsHelper.render_colored_label(object, label_suffix: label_suffix, title: tooltip_title(object)) + presenter = object.present(issuable_subject: parent) + LabelsHelper.render_colored_label(presenter, label_suffix: label_suffix, title: tooltip_title(presenter)) end def tooltip_title(label) diff --git a/lib/gitlab/checks/branch_check.rb b/lib/gitlab/checks/branch_check.rb index 1dbd564fb6f..4ddc1c718c7 100644 --- a/lib/gitlab/checks/branch_check.rb +++ b/lib/gitlab/checks/branch_check.rb @@ -48,7 +48,7 @@ module Gitlab if project.empty_repo? protected_branch_push_checks - elsif creation? && protected_branch_creation_enabled? + elsif creation? protected_branch_creation_checks elsif deletion? protected_branch_deletion_checks @@ -124,10 +124,6 @@ module Gitlab Gitlab::Routing.url_helpers.project_project_members_url(project) end - def protected_branch_creation_enabled? - Feature.enabled?(:protected_branch_creation, project, default_enabled: true) - end - def matching_merge_request? Checks::MatchingMergeRequest.new(newrev, branch_name, project).match? end diff --git a/lib/gitlab/checks/lfs_check.rb b/lib/gitlab/checks/lfs_check.rb index cc6a14d2d9a..67a65d61441 100644 --- a/lib/gitlab/checks/lfs_check.rb +++ b/lib/gitlab/checks/lfs_check.rb @@ -7,6 +7,7 @@ module Gitlab ERROR_MESSAGE = 'LFS objects are missing. Ensure LFS is properly set up or try a manual "git lfs push --all".'.freeze def validate! + return unless Feature.enabled?(:lfs_check, default_enabled: true) return unless project.lfs_enabled? return if skip_lfs_integrity_check diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/not_equals.rb b/lib/gitlab/ci/pipeline/expression/lexeme/not_equals.rb new file mode 100644 index 00000000000..5fcc9406cc8 --- /dev/null +++ b/lib/gitlab/ci/pipeline/expression/lexeme/not_equals.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Gitlab + module Ci + module Pipeline + module Expression + module Lexeme + class NotEquals < Lexeme::Operator + PATTERN = /!=/.freeze + + def initialize(left, right) + @left = left + @right = right + end + + def evaluate(variables = {}) + @left.evaluate(variables) != @right.evaluate(variables) + end + + def self.build(_value, behind, ahead) + new(behind, ahead) + end + end + end + end + end + end +end diff --git a/lib/gitlab/ci/pipeline/expression/lexeme/not_matches.rb b/lib/gitlab/ci/pipeline/expression/lexeme/not_matches.rb new file mode 100644 index 00000000000..14544d33e25 --- /dev/null +++ b/lib/gitlab/ci/pipeline/expression/lexeme/not_matches.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Gitlab + module Ci + module Pipeline + module Expression + module Lexeme + class NotMatches < Lexeme::Operator + PATTERN = /\!~/.freeze + + def initialize(left, right) + @left = left + @right = right + end + + def evaluate(variables = {}) + text = @left.evaluate(variables) + regexp = @right.evaluate(variables) + + regexp.scan(text.to_s).none? + end + + def self.build(_value, behind, ahead) + new(behind, ahead) + end + end + end + end + end + end +end diff --git a/lib/gitlab/ci/pipeline/expression/lexer.rb b/lib/gitlab/ci/pipeline/expression/lexer.rb index f26542361a2..e14edfae51d 100644 --- a/lib/gitlab/ci/pipeline/expression/lexer.rb +++ b/lib/gitlab/ci/pipeline/expression/lexer.rb @@ -15,7 +15,9 @@ module Gitlab Expression::Lexeme::Pattern, Expression::Lexeme::Null, Expression::Lexeme::Equals, - Expression::Lexeme::Matches + Expression::Lexeme::Matches, + Expression::Lexeme::NotEquals, + Expression::Lexeme::NotMatches ].freeze MAX_TOKENS = 100 diff --git a/lib/gitlab/ci/pipeline/expression/statement.rb b/lib/gitlab/ci/pipeline/expression/statement.rb index b03611f756e..ab5ae9caeea 100644 --- a/lib/gitlab/ci/pipeline/expression/statement.rb +++ b/lib/gitlab/ci/pipeline/expression/statement.rb @@ -8,13 +8,24 @@ module Gitlab StatementError = Class.new(Expression::ExpressionError) GRAMMAR = [ + # presence matchers %w[variable], + + # positive matchers %w[variable equals string], %w[variable equals variable], %w[variable equals null], %w[string equals variable], %w[null equals variable], - %w[variable matches pattern] + %w[variable matches pattern], + + # negative matchers + %w[variable notequals string], + %w[variable notequals variable], + %w[variable notequals null], + %w[string notequals variable], + %w[null notequals variable], + %w[variable notmatches pattern] ].freeze def initialize(statement, variables = {}) diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index a22e3c4b9dd..c12cb6a6434 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -732,18 +732,29 @@ module Gitlab end def compare_source_branch(target_branch_name, source_repository, source_branch_name, straight:) + reachable_ref = + if source_repository == self + source_branch_name + else + # If a tmp ref was created before for a separate repo comparison (forks), + # we're able to short-circuit the tmp ref re-creation: + # 1. Take the SHA from the source repo + # 2. Read that in the current "target" repo + # 3. If that SHA is still known (readable), it means GC hasn't + # cleaned it up yet, so we can use it instead re-writing the tmp ref. + source_commit_id = source_repository.commit(source_branch_name)&.sha + commit(source_commit_id)&.sha if source_commit_id + end + + return compare(target_branch_name, reachable_ref, straight: straight) if reachable_ref + tmp_ref = "refs/tmp/#{SecureRandom.hex}" return unless fetch_source_branch!(source_repository, source_branch_name, tmp_ref) - Gitlab::Git::Compare.new( - self, - target_branch_name, - tmp_ref, - straight: straight - ) + compare(target_branch_name, tmp_ref, straight: straight) ensure - delete_refs(tmp_ref) + delete_refs(tmp_ref) if tmp_ref end def write_ref(ref_path, ref, old_ref: nil) @@ -999,6 +1010,13 @@ module Gitlab private + def compare(base_ref, head_ref, straight:) + Gitlab::Git::Compare.new(self, + base_ref, + head_ref, + straight: straight) + end + def empty_diff_stats Gitlab::Git::DiffStatsCollection.new([]) end diff --git a/lib/gitlab/graphql/authorize/authorize_field_service.rb b/lib/gitlab/graphql/authorize/authorize_field_service.rb index 03d6aabb0e3..619ce100421 100644 --- a/lib/gitlab/graphql/authorize/authorize_field_service.rb +++ b/lib/gitlab/graphql/authorize/authorize_field_service.rb @@ -48,7 +48,7 @@ module Gitlab end def authorize_against(parent_typed_object, resolved_type) - if built_in_type? + if scalar_type? # The field is a built-in/scalar type, or a list of scalars # authorize using the parent's object parent_typed_object.object @@ -108,8 +108,8 @@ module Gitlab type.unwrap end - def built_in_type? - GraphQL::Schema::BUILT_IN_TYPES.has_value?(node_type_for_basic_connection(@field.type)) + def scalar_type? + node_type_for_basic_connection(@field.type).kind.scalar? end end end diff --git a/lib/gitlab/performance_bar/peek_query_tracker.rb b/lib/gitlab/performance_bar/peek_query_tracker.rb index ac392432427..16c16aa0265 100644 --- a/lib/gitlab/performance_bar/peek_query_tracker.rb +++ b/lib/gitlab/performance_bar/peek_query_tracker.rb @@ -26,11 +26,7 @@ module Gitlab subscribe('sql.active_record') do |_, start, finish, _, data| if Gitlab::SafeRequestStore.store[:peek_enabled] - # data[:cached] is only available starting from Rails 5.1.0 - # https://github.com/rails/rails/blob/v5.1.0/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb#L113 - # Before that, data[:name] was set to 'CACHE' - # https://github.com/rails/rails/blob/v4.2.9/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb#L80 - unless data.fetch(:cached, data[:name] == 'CACHE') + unless data[:cached] track_query(data[:sql].strip, data[:binds], start, finish) end end diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb index 8988b9ad7be..a29517e068f 100644 --- a/lib/gitlab/search_results.rb +++ b/lib/gitlab/search_results.rb @@ -113,7 +113,7 @@ module Gitlab issues.full_search(query) end - issues.reorder('updated_at DESC') + issues.reorder('issues.updated_at DESC') end # rubocop: enable CodeReuse/ActiveRecord @@ -121,7 +121,7 @@ module Gitlab def milestones milestones = Milestone.where(project_id: project_ids_relation) milestones = milestones.search(query) - milestones.reorder('updated_at DESC') + milestones.reorder('milestones.updated_at DESC') end # rubocop: enable CodeReuse/ActiveRecord @@ -139,7 +139,7 @@ module Gitlab merge_requests.full_search(query) end - merge_requests.reorder('updated_at DESC') + merge_requests.reorder('merge_requests.updated_at DESC') end # rubocop: enable CodeReuse/ActiveRecord diff --git a/lib/quality/seeders/issues.rb b/lib/quality/seeders/issues.rb new file mode 100644 index 00000000000..4c8cb6e97cc --- /dev/null +++ b/lib/quality/seeders/issues.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +# rubocop:disable CodeReuse/ActiveRecord +module Quality + module Seeders + class Issues + DEFAULT_BACKFILL_WEEKS = 52 + DEFAULT_AVERAGE_ISSUES_PER_WEEK = 10 + + attr_reader :project, :user + + def initialize(project:) + @project = project + end + + def seed(backfill_weeks: DEFAULT_BACKFILL_WEEKS, average_issues_per_week: DEFAULT_AVERAGE_ISSUES_PER_WEEK) + created_at = backfill_weeks.to_i.weeks.ago + team = project.team.users + created_issues_count = 0 + + loop do + rand(average_issues_per_week * 2).times do + params = { + title: FFaker::Lorem.sentence(6), + description: FFaker::Lorem.sentence, + created_at: created_at + rand(6).days, + state: %w[opened closed].sample, + milestone: project.milestones.sample, + assignee_ids: Array(team.pluck(:id).sample(3)), + labels: labels.join(',') + } + issue = ::Issues::CreateService.new(project, team.sample, params).execute + + if issue.persisted? + created_issues_count += 1 + print '.' # rubocop:disable Rails/Output + end + end + + created_at += 1.week + + break if created_at > Time.now + end + + created_issues_count + end + + private + + def labels + @labels_pool ||= project.labels.limit(rand(3)).pluck(:title).tap do |labels_array| + labels_array.concat(project.group.labels.limit(rand(3)).pluck(:title)) if project.group + end + end + end + end +end +# rubocop:enable CodeReuse/ActiveRecord diff --git a/lib/tasks/gitlab/seed.rake b/lib/tasks/gitlab/seed.rake new file mode 100644 index 00000000000..155ba979b36 --- /dev/null +++ b/lib/tasks/gitlab/seed.rake @@ -0,0 +1,34 @@ +namespace :gitlab do + namespace :seed do + desc "GitLab | Seed | Seeds issues" + task :issues, [:project_full_path] => :environment do |t, args| + projects = + if args.project_full_path + project = Project.find_by_full_path(args.project_full_path) + + unless project + error_message = "Project '#{args.project_full_path}' does not exist!" + potential_projects = Project.search(args.project_full_path) + + if potential_projects.present? + error_message += " Did you mean '#{potential_projects.first.full_path}'?" + end + + puts error_message.color(:red) + exit 1 + end + + [project] + else + Project.find_each + end + + projects.each do |project| + puts "\nSeeding issues for the '#{project.full_path}' project" + seeder = Quality::Seeders::Issues.new(project: project) + issues_created = seeder.seed(backfill_weeks: 5, average_issues_per_week: 2) + puts "\n#{issues_created} issues created!" + end + end + end +end diff --git a/package.json b/package.json index 1cb9f7a9ade..e04470109be 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "@babel/preset-env": "^7.3.1", "@gitlab/csslab": "^1.9.0", "@gitlab/svgs": "^1.59.0", - "@gitlab/ui": "^3.3.0", + "@gitlab/ui": "^3.4.0", "apollo-cache-inmemory": "^1.5.1", "apollo-client": "^2.5.1", "apollo-upload-client": "^10.0.0", diff --git a/spec/controllers/concerns/issuable_collections_spec.rb b/spec/controllers/concerns/issuable_collections_spec.rb index f9d15d04719..fb2cd5ca955 100644 --- a/spec/controllers/concerns/issuable_collections_spec.rb +++ b/spec/controllers/concerns/issuable_collections_spec.rb @@ -108,51 +108,77 @@ describe IssuableCollections do end describe '#finder_options' do - let(:params) do - { - assignee_id: '1', - assignee_username: 'user1', - author_id: '2', - author_username: 'user2', - authorized_only: 'yes', - confidential: true, - due_date: '2017-01-01', - group_id: '3', - iids: '4', - label_name: ['foo'], - milestone_title: 'bar', - my_reaction_emoji: 'thumbsup', - non_archived: 'true', - project_id: '5', - scope: 'all', - search: 'baz', - sort: 'priority', - state: 'opened', - invalid_param: 'invalid_param' - } - end - - it 'only allows whitelisted params' do + before do allow(controller).to receive(:cookies).and_return({}) allow(controller).to receive(:current_user).and_return(nil) + end + + subject { controller.send(:finder_options).to_h } + + context 'scalar params' do + let(:params) do + { + assignee_id: '1', + assignee_username: 'user1', + author_id: '2', + author_username: 'user2', + authorized_only: 'yes', + confidential: true, + due_date: '2017-01-01', + group_id: '3', + iids: '4', + label_name: 'foo', + milestone_title: 'bar', + my_reaction_emoji: 'thumbsup', + non_archived: 'true', + project_id: '5', + scope: 'all', + search: 'baz', + sort: 'priority', + state: 'opened', + invalid_param: 'invalid_param' + } + end + + it 'only allows whitelisted params' do + is_expected.to include({ + 'assignee_id' => '1', + 'assignee_username' => 'user1', + 'author_id' => '2', + 'author_username' => 'user2', + 'confidential' => true, + 'label_name' => 'foo', + 'milestone_title' => 'bar', + 'my_reaction_emoji' => 'thumbsup', + 'due_date' => '2017-01-01', + 'scope' => 'all', + 'search' => 'baz', + 'sort' => 'priority', + 'state' => 'opened' + }) + + is_expected.not_to include('invalid_param') + end + end + + context 'array params' do + let(:params) do + { + assignee_username: %w[user1 user2], + label_name: %w[label1 label2], + invalid_param: 'invalid_param', + invalid_array: ['param'] + } + end + + it 'only allows whitelisted params' do + is_expected.to include({ + 'label_name' => %w[label1 label2], + 'assignee_username' => %w[user1 user2] + }) - finder_options = controller.send(:finder_options) - - expect(finder_options).to eq(ActionController::Parameters.new({ - 'assignee_id' => '1', - 'assignee_username' => 'user1', - 'author_id' => '2', - 'author_username' => 'user2', - 'confidential' => true, - 'label_name' => ['foo'], - 'milestone_title' => 'bar', - 'my_reaction_emoji' => 'thumbsup', - 'due_date' => '2017-01-01', - 'scope' => 'all', - 'search' => 'baz', - 'sort' => 'priority', - 'state' => 'opened' - }).permit!) + is_expected.not_to include('invalid_param', 'invalid_array') + end end end end diff --git a/spec/controllers/omniauth_callbacks_controller_spec.rb b/spec/controllers/omniauth_callbacks_controller_spec.rb index 0af0eb4f942..6e374a8daa7 100644 --- a/spec/controllers/omniauth_callbacks_controller_spec.rb +++ b/spec/controllers/omniauth_callbacks_controller_spec.rb @@ -9,10 +9,14 @@ describe OmniauthCallbacksController, type: :controller do let(:user) { create(:omniauth_user, extern_uid: extern_uid, provider: provider) } before do - mock_auth_hash(provider.to_s, +extern_uid, user.email) + @original_env_config_omniauth_auth = mock_auth_hash(provider.to_s, +extern_uid, user.email) stub_omniauth_provider(provider, context: request) end + after do + Rails.application.env_config['omniauth.auth'] = @original_env_config_omniauth_auth + end + context 'when the user is on the last sign in attempt' do let(:extern_uid) { 'my-uid' } diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb index 1818809518d..92380a2bf09 100644 --- a/spec/controllers/projects/compare_controller_spec.rb +++ b/spec/controllers/projects/compare_controller_spec.rb @@ -82,7 +82,7 @@ describe Projects::CompareController do show_request expect(response).to be_success - expect(assigns(:diffs).diff_files.to_a).to eq([]) + expect(assigns(:diffs)).to eq([]) expect(assigns(:commits)).to eq([]) end end diff --git a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb index 65de0afae0c..5db54f42264 100644 --- a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb +++ b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb @@ -191,29 +191,119 @@ describe 'Merge request > User creates image diff notes', :js do end end - describe 'image view modes' do - before do - visit project_commit_path(project, '2f63565e7aac07bcdadb654e253078b727143ec4') + shared_examples 'swipe view' do + it 'moves the swipe handle' do + # Simulate dragging swipe view slider + expect { drag_and_drop_by(find('.swipe-bar'), 20, 0) } + .to change { find('.swipe-bar')['style'] } + .from(a_string_matching('left: 1px')) end - it 'resizes image in onion skin view mode' do - find('.view-modes-menu .onion-skin').click + it 'shows both images at the same position' do + drag_and_drop_by(find('.swipe-bar'), 40, 0) - expect(find('.onion-skin-frame')['style']).to match('width: 228px; height: 240px;') + expect(left_position('.frame.added img')) + .to eq(left_position('.frame.deleted img')) end + end - it 'resets onion skin view mode opacity when toggling between view modes' do - find('.view-modes-menu .onion-skin').click - + shared_examples 'onion skin' do + it 'resets opacity when toggling between view modes' do # Simulate dragging onion-skin slider drag_and_drop_by(find('.dragger'), -30, 0) expect(find('.onion-skin-frame .frame.added', visible: false)['style']).not_to match('opacity: 1;') + switch_to_swipe_view + switch_to_onion_skin + + expect(find('.onion-skin-frame .frame.added', visible: false)['style']).to match('opacity: 1;') + end + end + + describe 'changes tab image diff' do + let(:merge_request) { create(:merge_request, source_project: project, target_project: project, target_branch: 'master', source_branch: 'deleted-image-test', author: user) } + + before do + visit diffs_project_merge_request_path(project, merge_request) + click_link "Changes" + end + + def set_image_diff_sources + # set path of added and deleted images to something the spec can view + page.execute_script("document.querySelector('.frame.added img').src = '/apple-touch-icon.png';") + page.execute_script("document.querySelector('.frame.deleted img').src = '/favicon.png';") + + wait_for_requests + + expect(find('.frame.added img', visible: false)['src']).to match('/apple-touch-icon.png') + expect(find('.frame.deleted img', visible: false)['src']).to match('/favicon.png') + end + + def switch_to_swipe_view + # it isn't given the .swipe class in the merge request diff + find('.view-modes-menu li:nth-child(2)').click + expect(find('.view-modes-menu li.active')).to have_content('Swipe') + + set_image_diff_sources + end + + def switch_to_onion_skin + # it isn't given the .onion-skin class in the merge request diff + find('.view-modes-menu li:nth-child(3)').click + expect(find('.view-modes-menu li.active')).to have_content('Onion skin') + + set_image_diff_sources + end + + describe 'onion skin' do + before do + switch_to_onion_skin + end + + it_behaves_like 'onion skin' + end + + describe 'swipe view' do + before do + switch_to_swipe_view + end + + it_behaves_like 'swipe view' + end + end + + describe 'image view modes' do + before do + visit project_commit_path(project, '2f63565e7aac07bcdadb654e253078b727143ec4') + end + + def switch_to_swipe_view find('.view-modes-menu .swipe').click + end + + def switch_to_onion_skin find('.view-modes-menu .onion-skin').click + end - expect(find('.onion-skin-frame .frame.added', visible: false)['style']).to match('opacity: 1;') + describe 'onion skin' do + before do + switch_to_onion_skin + end + + it 'resizes image' do + expect(find('.onion-skin-frame')['style']).to match('width: 228px; height: 240px;') + end + + it_behaves_like 'onion skin' + end + + describe 'swipe view' do + before do + switch_to_swipe_view + end + + it_behaves_like 'swipe view' end end @@ -232,4 +322,8 @@ describe 'Merge request > User creates image diff notes', :js do click_button 'Comment' wait_for_requests end + + def left_position(element) + page.evaluate_script("document.querySelectorAll('#{element}')[0].getBoundingClientRect().left;") + end end diff --git a/spec/fixtures/api/schemas/entities/issue.json b/spec/fixtures/api/schemas/entities/issue.json index 162fb9c8108..9898819ef75 100644 --- a/spec/fixtures/api/schemas/entities/issue.json +++ b/spec/fixtures/api/schemas/entities/issue.json @@ -5,7 +5,7 @@ "iid": { "type": "integer" }, "author_id": { "type": "integer" }, "description": { "type": ["string", "null"] }, - "lock_version": { "type": ["string", "null"] }, + "lock_version": { "type": ["integer", "null"] }, "milestone_id": { "type": ["string", "null"] }, "title": { "type": "string" }, "moved_to_id": { "type": ["integer", "null"] }, diff --git a/spec/fixtures/api/schemas/entities/merge_request_basic.json b/spec/fixtures/api/schemas/entities/merge_request_basic.json index 88a600398b1..ac0a0314455 100644 --- a/spec/fixtures/api/schemas/entities/merge_request_basic.json +++ b/spec/fixtures/api/schemas/entities/merge_request_basic.json @@ -23,7 +23,7 @@ }, "task_status": { "type": "string" }, "task_status_short": { "type": "string" }, - "lock_version": { "type": ["string", "null"] } + "lock_version": { "type": ["integer", "null"] } }, "additionalProperties": false } diff --git a/spec/frontend/ide/stores/modules/file_templates/mutations_spec.js b/spec/frontend/ide/stores/modules/file_templates/mutations_spec.js index 8e8b7f06ca2..6a1a826093c 100644 --- a/spec/frontend/ide/stores/modules/file_templates/mutations_spec.js +++ b/spec/frontend/ide/stores/modules/file_templates/mutations_spec.js @@ -2,6 +2,9 @@ import createState from '~/ide/stores/modules/file_templates/state'; import * as types from '~/ide/stores/modules/file_templates/mutation_types'; import mutations from '~/ide/stores/modules/file_templates/mutations'; +const mockFileTemplates = [['MIT'], ['CC']]; +const mockTemplateType = 'test'; + describe('IDE file templates mutations', () => { let state; @@ -10,11 +13,21 @@ describe('IDE file templates mutations', () => { }); describe(`${types.REQUEST_TEMPLATE_TYPES}`, () => { - it('sets isLoading', () => { + it('sets loading to true', () => { + state.isLoading = false; + mutations[types.REQUEST_TEMPLATE_TYPES](state); expect(state.isLoading).toBe(true); }); + + it('sets templates to an empty array', () => { + state.templates = mockFileTemplates; + + mutations[types.REQUEST_TEMPLATE_TYPES](state); + + expect(state.templates).toEqual([]); + }); }); describe(`${types.RECEIVE_TEMPLATE_TYPES_ERROR}`, () => { @@ -31,29 +44,33 @@ describe('IDE file templates mutations', () => { it('sets isLoading to false', () => { state.isLoading = true; - mutations[types.RECEIVE_TEMPLATE_TYPES_SUCCESS](state, []); + mutations[types.RECEIVE_TEMPLATE_TYPES_SUCCESS](state, mockFileTemplates); expect(state.isLoading).toBe(false); }); - it('sets templates', () => { - mutations[types.RECEIVE_TEMPLATE_TYPES_SUCCESS](state, ['test']); + it('sets templates to payload', () => { + state.templates = ['test']; + + mutations[types.RECEIVE_TEMPLATE_TYPES_SUCCESS](state, mockFileTemplates); - expect(state.templates).toEqual(['test']); + expect(state.templates).toEqual(mockFileTemplates); }); }); describe(`${types.SET_SELECTED_TEMPLATE_TYPE}`, () => { - it('sets selectedTemplateType', () => { - mutations[types.SET_SELECTED_TEMPLATE_TYPE](state, 'type'); + it('sets templates type to selected type', () => { + state.selectedTemplateType = ''; - expect(state.selectedTemplateType).toBe('type'); + mutations[types.SET_SELECTED_TEMPLATE_TYPE](state, mockTemplateType); + + expect(state.selectedTemplateType).toBe(mockTemplateType); }); - it('clears templates', () => { - state.templates = ['test']; + it('sets templates to empty array', () => { + state.templates = mockFileTemplates; - mutations[types.SET_SELECTED_TEMPLATE_TYPE](state, 'type'); + mutations[types.SET_SELECTED_TEMPLATE_TYPE](state, mockTemplateType); expect(state.templates).toEqual([]); }); @@ -61,6 +78,8 @@ describe('IDE file templates mutations', () => { describe(`${types.SET_UPDATE_SUCCESS}`, () => { it('sets updateSuccess', () => { + state.updateSuccess = false; + mutations[types.SET_UPDATE_SUCCESS](state, true); expect(state.updateSuccess).toBe(true); diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb index a049b5a6133..58eaf991d6e 100644 --- a/spec/helpers/labels_helper_spec.rb +++ b/spec/helpers/labels_helper_spec.rb @@ -67,27 +67,29 @@ describe LabelsHelper do describe 'link_to_label' do let(:project) { create(:project) } let(:label) { create(:label, project: project) } + let(:subject) { nil } + let(:label_presenter) { label.present(issuable_subject: subject) } context 'without subject' do it "uses the label's project" do - expect(link_to_label(label)).to match %r{<a href="/#{label.project.full_path}/issues\?label_name%5B%5D=#{label.name}">.*</a>} + expect(link_to_label(label_presenter)).to match %r{<a href="/#{label.project.full_path}/issues\?label_name%5B%5D=#{label.name}">.*</a>} end end context 'with a project as subject' do let(:namespace) { build(:namespace, name: 'foo3') } - let(:another_project) { build(:project, namespace: namespace, name: 'bar3') } + let(:subject) { build(:project, namespace: namespace, name: 'bar3') } it 'links to project issues page' do - expect(link_to_label(label, subject: another_project)).to match %r{<a href="/foo3/bar3/issues\?label_name%5B%5D=#{label.name}">.*</a>} + expect(link_to_label(label_presenter)).to match %r{<a href="/foo3/bar3/issues\?label_name%5B%5D=#{label.name}">.*</a>} end end context 'with a group as subject' do - let(:group) { build(:group, name: 'bar') } + let(:subject) { build(:group, name: 'bar') } it 'links to group issues page' do - expect(link_to_label(label, subject: group)).to match %r{<a href="/groups/bar/-/issues\?label_name%5B%5D=#{label.name}">.*</a>} + expect(link_to_label(label_presenter)).to match %r{<a href="/groups/bar/-/issues\?label_name%5B%5D=#{label.name}">.*</a>} end end @@ -95,7 +97,7 @@ describe LabelsHelper do ['issue', :issue, 'merge_request', :merge_request].each do |type| context "set to #{type}" do it 'links to correct page' do - expect(link_to_label(label, type: type)).to match %r{<a href="/#{label.project.full_path}/#{type.to_s.pluralize}\?label_name%5B%5D=#{label.name}">.*</a>} + expect(link_to_label(label_presenter, type: type)).to match %r{<a href="/#{label.project.full_path}/#{type.to_s.pluralize}\?label_name%5B%5D=#{label.name}">.*</a>} end end end @@ -104,14 +106,14 @@ describe LabelsHelper do context 'with a tooltip argument' do context 'set to false' do it 'does not include the has-tooltip class' do - expect(link_to_label(label, tooltip: false)).not_to match /has-tooltip/ + expect(link_to_label(label_presenter, tooltip: false)).not_to match /has-tooltip/ end end end context 'with block' do it 'passes the block to link_to' do - link = link_to_label(label) { 'Foo' } + link = link_to_label(label_presenter) { 'Foo' } expect(link).to match('Foo') end end @@ -119,8 +121,8 @@ describe LabelsHelper do context 'without block' do it 'uses render_colored_label as the link content' do expect(self).to receive(:render_colored_label) - .with(label, tooltip: true).and_return('Foo') - expect(link_to_label(label)).to match('Foo') + .with(label_presenter, tooltip: true).and_return('Foo') + expect(link_to_label(label_presenter)).to match('Foo') end end end @@ -237,16 +239,24 @@ describe LabelsHelper do end end - describe 'labels_sorted_by_title' do + describe 'presented_labels_sorted_by_title' do + let(:labels) do + [build(:label, title: 'a'), + build(:label, title: 'B'), + build(:label, title: 'c'), + build(:label, title: 'D')] + end + it 'sorts labels alphabetically' do - label1 = double(:label, title: 'a') - label2 = double(:label, title: 'B') - label3 = double(:label, title: 'c') - label4 = double(:label, title: 'D') - labels = [label1, label2, label3, label4] - - expect(labels_sorted_by_title(labels)) - .to match_array([label2, label4, label1, label3]) + sorted_ids = presented_labels_sorted_by_title(labels, nil).map(&:id) + + expect(sorted_ids) + .to match_array([labels[1].id, labels[3].id, labels[0].id, labels[2].id]) + end + + it 'returns an array of label presenters' do + expect(presented_labels_sorted_by_title(labels, nil)) + .to all(be_a(LabelPresenter)) end end diff --git a/spec/javascripts/boards/list_spec.js b/spec/javascripts/boards/list_spec.js index 0d462a6f872..bb6fc6c693d 100644 --- a/spec/javascripts/boards/list_spec.js +++ b/spec/javascripts/boards/list_spec.js @@ -45,6 +45,7 @@ describe('List model', () => { id: _.random(10000), title: 'test', color: 'red', + text_color: 'white', }, }); list.save(); @@ -53,6 +54,8 @@ describe('List model', () => { expect(list.id).toBe(listObj.id); expect(list.type).toBe('label'); expect(list.position).toBe(0); + expect(list.label.color).toBe('red'); + expect(list.label.textColor).toBe('white'); done(); }, 0); }); diff --git a/spec/javascripts/boards/mock_data.js b/spec/javascripts/boards/mock_data.js index 14fff9223f4..93a0f29af0a 100644 --- a/spec/javascripts/boards/mock_data.js +++ b/spec/javascripts/boards/mock_data.js @@ -16,6 +16,7 @@ export const listObj = { title: 'Testing', color: 'red', description: 'testing;', + textColor: 'white', }, }; diff --git a/spec/javascripts/ide/components/new_dropdown/upload_spec.js b/spec/javascripts/ide/components/new_dropdown/upload_spec.js index 878e17ac805..d19af6af2d7 100644 --- a/spec/javascripts/ide/components/new_dropdown/upload_spec.js +++ b/spec/javascripts/ide/components/new_dropdown/upload_spec.js @@ -78,6 +78,8 @@ describe('new dropdown upload', () => { type: 'blob', content: 'plain text', base64: false, + binary: false, + rawPath: '', }); }); @@ -89,6 +91,8 @@ describe('new dropdown upload', () => { type: 'blob', content: binaryTarget.result.split('base64,')[1], base64: true, + binary: true, + rawPath: binaryTarget.result, }); }); }); diff --git a/spec/javascripts/ide/stores/modules/commit/actions_spec.js b/spec/javascripts/ide/stores/modules/commit/actions_spec.js index abc97f3072c..cdeb9b4b896 100644 --- a/spec/javascripts/ide/stores/modules/commit/actions_spec.js +++ b/spec/javascripts/ide/stores/modules/commit/actions_spec.js @@ -4,6 +4,7 @@ import service from '~/ide/services'; import router from '~/ide/ide_router'; import eventHub from '~/ide/eventhub'; import consts from '~/ide/stores/modules/commit/constants'; +import { commitActionTypes } from '~/ide/constants'; import { resetStore, file } from 'spec/ide/helpers'; describe('IDE commit module actions', () => { @@ -294,7 +295,7 @@ describe('IDE commit module actions', () => { commit_message: 'testing 123', actions: [ { - action: 'update', + action: commitActionTypes.update, file_path: jasmine.anything(), content: undefined, encoding: jasmine.anything(), @@ -321,7 +322,7 @@ describe('IDE commit module actions', () => { commit_message: 'testing 123', actions: [ { - action: 'update', + action: commitActionTypes.update, file_path: jasmine.anything(), content: undefined, encoding: jasmine.anything(), diff --git a/spec/javascripts/ide/stores/modules/file_templates/actions_spec.js b/spec/javascripts/ide/stores/modules/file_templates/actions_spec.js index 734233100ab..548962c7a92 100644 --- a/spec/javascripts/ide/stores/modules/file_templates/actions_spec.js +++ b/spec/javascripts/ide/stores/modules/file_templates/actions_spec.js @@ -69,18 +69,16 @@ describe('IDE file templates actions', () => { describe('fetchTemplateTypes', () => { describe('success', () => { - let nextPage; + const pages = [[{ name: 'MIT' }], [{ name: 'Apache' }], [{ name: 'CC' }]]; beforeEach(() => { - mock.onGet(/api\/(.*)\/templates\/licenses/).replyOnce(() => [ - 200, - [ - { - name: 'MIT', - }, - ], - { 'X-NEXT-PAGE': nextPage }, - ]); + mock.onGet(/api\/(.*)\/templates\/licenses/).reply(({ params }) => { + const pageNum = params.page; + const page = pages[pageNum - 1]; + const hasNextPage = pageNum < pages.length; + + return [200, page, hasNextPage ? { 'X-NEXT-PAGE': pageNum + 1 } : {}]; + }); }); it('rejects if selectedTemplateType is empty', done => { @@ -112,43 +110,15 @@ describe('IDE file templates actions', () => { }, { type: 'receiveTemplateTypesSuccess', - payload: [ - { - name: 'MIT', - }, - ], - }, - ], - done, - ); - }); - - it('dispatches actions for next page', done => { - nextPage = '2'; - state.selectedTemplateType = { - key: 'licenses', - }; - - testAction( - actions.fetchTemplateTypes, - null, - state, - [], - [ - { - type: 'requestTemplateTypes', + payload: pages[0], }, { type: 'receiveTemplateTypesSuccess', - payload: [ - { - name: 'MIT', - }, - ], + payload: pages[0].concat(pages[1]), }, { - type: 'fetchTemplateTypes', - payload: 2, + type: 'receiveTemplateTypesSuccess', + payload: pages[0].concat(pages[1]).concat(pages[2]), }, ], done, diff --git a/spec/javascripts/ide/stores/utils_spec.js b/spec/javascripts/ide/stores/utils_spec.js index c4f122efdda..debe1c4acee 100644 --- a/spec/javascripts/ide/stores/utils_spec.js +++ b/spec/javascripts/ide/stores/utils_spec.js @@ -1,4 +1,5 @@ import * as utils from '~/ide/stores/utils'; +import { commitActionTypes } from '~/ide/constants'; import { file } from '../helpers'; describe('Multi-file store utils', () => { @@ -107,7 +108,7 @@ describe('Multi-file store utils', () => { commit_message: 'commit message', actions: [ { - action: 'update', + action: commitActionTypes.update, file_path: 'staged', content: 'updated file content', encoding: 'text', @@ -115,7 +116,7 @@ describe('Multi-file store utils', () => { previous_path: undefined, }, { - action: 'create', + action: commitActionTypes.create, file_path: 'added', content: 'new file content', encoding: 'base64', @@ -123,7 +124,7 @@ describe('Multi-file store utils', () => { previous_path: undefined, }, { - action: 'delete', + action: commitActionTypes.delete, file_path: 'deletedFile', content: undefined, encoding: 'text', @@ -170,7 +171,7 @@ describe('Multi-file store utils', () => { commit_message: 'prebuilt test commit message', actions: [ { - action: 'update', + action: commitActionTypes.update, file_path: 'staged', content: 'updated file content', encoding: 'text', @@ -178,7 +179,7 @@ describe('Multi-file store utils', () => { previous_path: undefined, }, { - action: 'create', + action: commitActionTypes.create, file_path: 'added', content: 'new file content', encoding: 'base64', @@ -193,19 +194,19 @@ describe('Multi-file store utils', () => { describe('commitActionForFile', () => { it('returns deleted for deleted file', () => { - expect(utils.commitActionForFile({ deleted: true })).toBe('delete'); + expect(utils.commitActionForFile({ deleted: true })).toBe(commitActionTypes.delete); }); it('returns create for tempFile', () => { - expect(utils.commitActionForFile({ tempFile: true })).toBe('create'); + expect(utils.commitActionForFile({ tempFile: true })).toBe(commitActionTypes.create); }); it('returns move for moved file', () => { - expect(utils.commitActionForFile({ prevPath: 'test' })).toBe('move'); + expect(utils.commitActionForFile({ prevPath: 'test' })).toBe(commitActionTypes.move); }); it('returns update by default', () => { - expect(utils.commitActionForFile({})).toBe('update'); + expect(utils.commitActionForFile({})).toBe(commitActionTypes.update); }); }); diff --git a/spec/javascripts/merge_request_spec.js b/spec/javascripts/merge_request_spec.js index 431798c6ec3..cadcc15385f 100644 --- a/spec/javascripts/merge_request_spec.js +++ b/spec/javascripts/merge_request_spec.js @@ -58,7 +58,7 @@ describe('MergeRequest', function() { { merge_request: { description: '- [ ] Task List Item', - lock_version: undefined, + lock_version: 0, update_task: { line_number: lineNumber, line_source: lineSource, index, checked }, }, }, diff --git a/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js b/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js index 4da8c6196b1..bdf802052b9 100644 --- a/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js +++ b/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js @@ -4,6 +4,7 @@ import axios from '~/lib/utils/axios_utils'; import contentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue'; import mountComponent from 'spec/helpers/vue_mount_component_helper'; import { GREEN_BOX_IMAGE_URL } from 'spec/test_constants'; +import '~/behaviors/markdown/render_gfm'; describe('ContentViewer', () => { let vm; @@ -29,6 +30,7 @@ describe('ContentViewer', () => { path: 'test.md', content: '* Test', projectPath: 'testproject', + type: 'markdown', }); const previewContainer = vm.$el.querySelector('.md-previewer'); @@ -44,6 +46,7 @@ describe('ContentViewer', () => { createComponent({ path: GREEN_BOX_IMAGE_URL, fileSize: 1024, + type: 'image', }); setTimeout(() => { diff --git a/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js b/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js index 7f2e246d656..97c870f27d9 100644 --- a/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js +++ b/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js @@ -138,22 +138,6 @@ describe('ImageDiffViewer', () => { done(); }); }); - - it('drag handler is working', done => { - vm.$el.querySelector('.view-modes-menu li:nth-child(2)').click(); - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.swipe-bar').style.left).toBe('1px'); - expect(vm.$el.querySelector('.top-handle')).not.toBeNull(); - - dragSlider(vm.$el.querySelector('.swipe-bar'), 40); - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.swipe-bar').style.left).toBe('-20px'); - done(); - }); - }); - }); }); describe('onionSkin', () => { diff --git a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb index e7ff9169f1b..d3f7f1ded16 100644 --- a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb +++ b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb @@ -177,7 +177,7 @@ describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :m end before do - allow_any_instance_of(described_class::MergeRequestDiff::ActiveRecord_Relation) + allow_any_instance_of(ActiveRecord::Relation) .to receive(:update_all).and_raise(exception) end diff --git a/spec/lib/gitlab/checks/branch_check_spec.rb b/spec/lib/gitlab/checks/branch_check_spec.rb index 8d5ab27a17c..71b64a3b9df 100644 --- a/spec/lib/gitlab/checks/branch_check_spec.rb +++ b/spec/lib/gitlab/checks/branch_check_spec.rb @@ -77,117 +77,85 @@ describe Gitlab::Checks::BranchCheck do let(:oldrev) { '0000000000000000000000000000000000000000' } let(:ref) { 'refs/heads/feature' } - context 'protected branch creation feature is disabled' do + context 'user can push to branch' do before do - stub_feature_flags(protected_branch_creation: false) + allow(user_access) + .to receive(:can_push_to_branch?) + .with('feature') + .and_return(true) end - context 'user is not allowed to push to protected branch' do - before do - allow(user_access) - .to receive(:can_push_to_branch?) - .and_return(false) - end - - it 'raises an error' do - expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to push code to protected branches on this project.') - end + it 'does not raise an error' do + expect { subject.validate! }.not_to raise_error end + end - context 'user is allowed to push to protected branch' do - before do - allow(user_access) - .to receive(:can_push_to_branch?) - .and_return(true) - end - - it 'does not raise an error' do - expect { subject.validate! }.not_to raise_error - end + context 'user cannot push to branch' do + before do + allow(user_access) + .to receive(:can_push_to_branch?) + .with('feature') + .and_return(false) end - end - context 'protected branch creation feature is enabled' do - context 'user can push to branch' do + context 'user cannot merge to branch' do before do allow(user_access) - .to receive(:can_push_to_branch?) + .to receive(:can_merge_to_branch?) .with('feature') - .and_return(true) + .and_return(false) end - it 'does not raise an error' do - expect { subject.validate! }.not_to raise_error + it 'raises an error' do + expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to create protected branches on this project.') end end - context 'user cannot push to branch' do + context 'user can merge to branch' do before do allow(user_access) - .to receive(:can_push_to_branch?) + .to receive(:can_merge_to_branch?) .with('feature') - .and_return(false) + .and_return(true) + + allow(project.repository) + .to receive(:branch_names_contains_sha) + .with(newrev) + .and_return(['branch']) end - context 'user cannot merge to branch' do + context "newrev isn't in any protected branches" do before do - allow(user_access) - .to receive(:can_merge_to_branch?) - .with('feature') + allow(ProtectedBranch) + .to receive(:any_protected?) + .with(project, ['branch']) .and_return(false) end it 'raises an error' do - expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to create protected branches on this project.') + expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You can only use an existing protected branch ref as the basis of a new protected branch.') end end - context 'user can merge to branch' do + context 'newrev is included in a protected branch' do before do - allow(user_access) - .to receive(:can_merge_to_branch?) - .with('feature') + allow(ProtectedBranch) + .to receive(:any_protected?) + .with(project, ['branch']) .and_return(true) - - allow(project.repository) - .to receive(:branch_names_contains_sha) - .with(newrev) - .and_return(['branch']) end - context "newrev isn't in any protected branches" do - before do - allow(ProtectedBranch) - .to receive(:any_protected?) - .with(project, ['branch']) - .and_return(false) - end + context 'via web interface' do + let(:protocol) { 'web' } - it 'raises an error' do - expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You can only use an existing protected branch ref as the basis of a new protected branch.') + it 'allows branch creation' do + expect { subject.validate! }.not_to raise_error end end - context 'newrev is included in a protected branch' do - before do - allow(ProtectedBranch) - .to receive(:any_protected?) - .with(project, ['branch']) - .and_return(true) - end - - context 'via web interface' do - let(:protocol) { 'web' } - - it 'allows branch creation' do - expect { subject.validate! }.not_to raise_error - end - end - - context 'via SSH' do - it 'raises an error' do - expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You can only create protected branches using the web interface and API.') - end + context 'via SSH' do + it 'raises an error' do + expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You can only create protected branches using the web interface and API.') end end end diff --git a/spec/lib/gitlab/checks/lfs_check_spec.rb b/spec/lib/gitlab/checks/lfs_check_spec.rb index 35f8069c8a4..dad14e100a7 100644 --- a/spec/lib/gitlab/checks/lfs_check_spec.rb +++ b/spec/lib/gitlab/checks/lfs_check_spec.rb @@ -27,6 +27,18 @@ describe Gitlab::Checks::LfsCheck do allow(project).to receive(:lfs_enabled?).and_return(true) end + context 'with lfs_check feature disabled' do + before do + stub_feature_flags(lfs_check: false) + end + + it 'skips integrity check' do + expect_any_instance_of(Gitlab::Git::LfsChanges).not_to receive(:new_pointers) + + subject.validate! + end + end + context 'deletion' do let(:changes) { { oldrev: oldrev, ref: ref } } diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_equals_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_equals_spec.rb new file mode 100644 index 00000000000..9aa2f4efd67 --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_equals_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe Gitlab::Ci::Pipeline::Expression::Lexeme::NotEquals do + let(:left) { double('left') } + let(:right) { double('right') } + + describe '.build' do + it 'creates a new instance of the token' do + expect(described_class.build('!=', left, right)) + .to be_a(described_class) + end + end + + describe '.type' do + it 'is an operator' do + expect(described_class.type).to eq :operator + end + end + + describe '#evaluate' do + it 'returns true when left and right are not equal' do + allow(left).to receive(:evaluate).and_return(1) + allow(right).to receive(:evaluate).and_return(2) + + operator = described_class.new(left, right) + + expect(operator.evaluate(VARIABLE: 3)).to eq true + end + + it 'returns false when left and right are equal' do + allow(left).to receive(:evaluate).and_return(1) + allow(right).to receive(:evaluate).and_return(1) + + operator = described_class.new(left, right) + + expect(operator.evaluate(VARIABLE: 3)).to eq false + end + end +end diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb new file mode 100644 index 00000000000..fa3b9651fb4 --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/not_matches_spec.rb @@ -0,0 +1,80 @@ +require 'fast_spec_helper' +require_dependency 're2' + +describe Gitlab::Ci::Pipeline::Expression::Lexeme::NotMatches do + let(:left) { double('left') } + let(:right) { double('right') } + + describe '.build' do + it 'creates a new instance of the token' do + expect(described_class.build('!~', left, right)) + .to be_a(described_class) + end + end + + describe '.type' do + it 'is an operator' do + expect(described_class.type).to eq :operator + end + end + + describe '#evaluate' do + it 'returns true when left and right do not match' do + allow(left).to receive(:evaluate).and_return('my-string') + allow(right).to receive(:evaluate) + .and_return(Gitlab::UntrustedRegexp.new('something')) + + operator = described_class.new(left, right) + + expect(operator.evaluate).to eq true + end + + it 'returns false when left and right match' do + allow(left).to receive(:evaluate).and_return('my-awesome-string') + allow(right).to receive(:evaluate) + .and_return(Gitlab::UntrustedRegexp.new('awesome.string$')) + + operator = described_class.new(left, right) + + expect(operator.evaluate).to eq false + end + + it 'supports matching against a nil value' do + allow(left).to receive(:evaluate).and_return(nil) + allow(right).to receive(:evaluate) + .and_return(Gitlab::UntrustedRegexp.new('pattern')) + + operator = described_class.new(left, right) + + expect(operator.evaluate).to eq true + end + + it 'supports multiline strings' do + allow(left).to receive(:evaluate).and_return <<~TEXT + My awesome contents + + My-text-string! + TEXT + + allow(right).to receive(:evaluate) + .and_return(Gitlab::UntrustedRegexp.new('text-string')) + + operator = described_class.new(left, right) + + expect(operator.evaluate).to eq false + end + + it 'supports regexp flags' do + allow(left).to receive(:evaluate).and_return <<~TEXT + My AWESOME content + TEXT + + allow(right).to receive(:evaluate) + .and_return(Gitlab::UntrustedRegexp.new('(?i)awesome')) + + operator = described_class.new(left, right) + + expect(operator.evaluate).to eq false + end + end +end diff --git a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb index 11e73294f18..a9fd809409b 100644 --- a/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb @@ -101,6 +101,18 @@ describe Gitlab::Ci::Pipeline::Expression::Statement do "$EMPTY_VARIABLE =~ /var.*/" | false "$UNDEFINED_VARIABLE =~ /var.*/" | false "$PRESENT_VARIABLE =~ /VAR.*/i" | true + '$PRESENT_VARIABLE != "my variable"' | false + '"my variable" != $PRESENT_VARIABLE' | false + '$PRESENT_VARIABLE != null' | true + '$EMPTY_VARIABLE != null' | true + '"" != $EMPTY_VARIABLE' | false + '$UNDEFINED_VARIABLE != null' | false + 'null != $UNDEFINED_VARIABLE' | false + "$PRESENT_VARIABLE !~ /var.*e$/" | false + "$PRESENT_VARIABLE !~ /^var.*/" | true + "$EMPTY_VARIABLE !~ /var.*/" | true + "$UNDEFINED_VARIABLE !~ /var.*/" | true + "$PRESENT_VARIABLE !~ /VAR.*/i" | false end with_them do diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb index 778950c95e4..45fe5d72937 100644 --- a/spec/lib/gitlab/git/repository_spec.rb +++ b/spec/lib/gitlab/git/repository_spec.rb @@ -1966,6 +1966,70 @@ describe Gitlab::Git::Repository, :seed_helper do end end + describe '#compare_source_branch' do + let(:repository) { Gitlab::Git::Repository.new('default', TEST_GITATTRIBUTES_REPO_PATH, '', 'group/project') } + + context 'within same repository' do + it 'does not create a temp ref' do + expect(repository).not_to receive(:fetch_source_branch!) + expect(repository).not_to receive(:delete_refs) + + compare = repository.compare_source_branch('master', repository, 'feature', straight: false) + expect(compare).to be_a(Gitlab::Git::Compare) + expect(compare.commits.count).to be > 0 + end + + it 'returns empty commits when source ref does not exist' do + compare = repository.compare_source_branch('master', repository, 'non-existent-branch', straight: false) + + expect(compare.commits).to be_empty + end + end + + context 'with different repositories' do + context 'when ref is known by source repo, but not by target' do + before do + mutable_repository.write_ref('another-branch', 'feature') + end + + it 'creates temp ref' do + expect(repository).not_to receive(:fetch_source_branch!) + expect(repository).not_to receive(:delete_refs) + + compare = repository.compare_source_branch('master', mutable_repository, 'another-branch', straight: false) + expect(compare).to be_a(Gitlab::Git::Compare) + expect(compare.commits.count).to be > 0 + end + end + + context 'when ref is known by source and target repos' do + before do + mutable_repository.write_ref('another-branch', 'feature') + repository.write_ref('another-branch', 'feature') + end + + it 'does not create a temp ref' do + expect(repository).not_to receive(:fetch_source_branch!) + expect(repository).not_to receive(:delete_refs) + + compare = repository.compare_source_branch('master', mutable_repository, 'another-branch', straight: false) + expect(compare).to be_a(Gitlab::Git::Compare) + expect(compare.commits.count).to be > 0 + end + end + + context 'when ref is unknown by source repo' do + it 'returns nil when source ref does not exist' do + expect(repository).to receive(:fetch_source_branch!).and_call_original + expect(repository).to receive(:delete_refs).and_call_original + + compare = repository.compare_source_branch('master', mutable_repository, 'non-existent-branch', straight: false) + expect(compare).to be_nil + end + end + end + end + describe '#checksum' do it 'calculates the checksum for non-empty repo' do expect(repository.checksum).to eq '51d0a9662681f93e1fee547a6b7ba2bcaf716059' diff --git a/spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb b/spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb index 95a4eb296fb..aec9c4baf0a 100644 --- a/spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb +++ b/spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb @@ -45,7 +45,7 @@ describe Gitlab::Graphql::Authorize::AuthorizeFieldService do end end - context "when the field is a scalar type" do + context "when the field is a built-in scalar type" do let(:field) { type_with_field(GraphQL::STRING_TYPE, :read_field).fields["testField"].to_graphql } let(:expected_permissions) { [:read_field] } @@ -58,6 +58,20 @@ describe Gitlab::Graphql::Authorize::AuthorizeFieldService do it_behaves_like "checking permissions on the presented object" end + + context "when the field is sub-classed scalar type" do + let(:field) { type_with_field(Types::TimeType, :read_field).fields["testField"].to_graphql } + let(:expected_permissions) { [:read_field] } + + it_behaves_like "checking permissions on the presented object" + end + + context "when the field is a list of sub-classed scalar types" do + let(:field) { type_with_field([Types::TimeType], :read_field).fields["testField"].to_graphql } + let(:expected_permissions) { [:read_field] } + + it_behaves_like "checking permissions on the presented object" + end end context "when the field is a specific type" do diff --git a/spec/lib/gitlab/import_export/attribute_configuration_spec.rb b/spec/lib/gitlab/import_export/attribute_configuration_spec.rb index 87ab81d8169..ddfbb020a55 100644 --- a/spec/lib/gitlab/import_export/attribute_configuration_spec.rb +++ b/spec/lib/gitlab/import_export/attribute_configuration_spec.rb @@ -29,7 +29,7 @@ describe 'Import/Export attribute configuration' do it 'has no new columns' do relation_names.each do |relation_name| relation_class = relation_class_for_name(relation_name) - relation_attributes = relation_class.new.attributes.keys + relation_attributes = relation_class.new.attributes.keys - relation_class.encrypted_attributes.keys.map(&:to_s) current_attributes = parsed_attributes(relation_name, relation_attributes) safe_attributes = safe_model_attributes[relation_class.to_s].dup || [] diff --git a/spec/lib/quality/seeders/issues_spec.rb b/spec/lib/quality/seeders/issues_spec.rb new file mode 100644 index 00000000000..e17414a541a --- /dev/null +++ b/spec/lib/quality/seeders/issues_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Quality::Seeders::Issues do + let(:project) { create(:project) } + + subject { described_class.new(project: project) } + + describe '#seed' do + it 'seeds issues' do + issues_created = subject.seed(backfill_weeks: 1, average_issues_per_week: 1) + + expect(issues_created).to be_between(0, 2) + expect(project.issues.count).to eq(issues_created) + end + end +end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 2cb581696a0..f735a89f69f 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -73,9 +73,8 @@ describe Ci::Runner do end it 'fails to save a group assigned to a project runner even if the runner is already saved' do - group_runner - - expect { create(:group, runners: [project_runner]) } + group.runners << project_runner + expect { group.save! } .to raise_error(ActiveRecord::RecordInvalid) end end diff --git a/spec/presenters/label_presenter_spec.rb b/spec/presenters/label_presenter_spec.rb new file mode 100644 index 00000000000..fae8188670f --- /dev/null +++ b/spec/presenters/label_presenter_spec.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe LabelPresenter do + include Gitlab::Routing.url_helpers + + set(:group) { create(:group) } + set(:project) { create(:project, group: group) } + let(:label) { build_stubbed(:label, project: project).present(issuable_subject: project) } + let(:group_label) { build_stubbed(:group_label, group: group).present(issuable_subject: project) } + + describe '#edit_path' do + context 'with group label' do + subject { group_label.edit_path } + + it { is_expected.to eq(edit_group_label_path(group, group_label)) } + end + + context 'with project label' do + subject { label.edit_path } + + it { is_expected.to eq(edit_project_label_path(project, label)) } + end + end + + describe '#destroy_path' do + context 'with group label' do + subject { group_label.destroy_path } + + it { is_expected.to eq(group_label_path(group, group_label)) } + end + + context 'with project label' do + subject { label.destroy_path } + + it { is_expected.to eq(project_label_path(project, label)) } + end + end + + describe '#filter_path' do + context 'with group as context subject' do + let(:label_in_group) { build_stubbed(:label, project: project).present(issuable_subject: group) } + subject { label_in_group.filter_path } + + it { is_expected.to eq(issues_group_path(group, label_name: [label_in_group.title])) } + end + + context 'with project as context subject' do + subject { label.filter_path } + + it { is_expected.to eq(namespace_project_issues_path(group, project, label_name: [label.title])) } + end + end + + describe '#can_subscribe_to_label_in_different_levels?' do + it 'returns true for group labels in project context' do + expect(group_label.can_subscribe_to_label_in_different_levels?).to be_truthy + end + + it 'returns false for project labels in project context' do + expect(label.can_subscribe_to_label_in_different_levels?).to be_falsey + end + end +end diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb index 29515709a74..b331da1acba 100644 --- a/spec/requests/api/runner_spec.rb +++ b/spec/requests/api/runner_spec.rb @@ -68,7 +68,7 @@ describe API::Runner, :clean_gitlab_redis_shared_state do post api('/runners'), params: { token: group.runners_token } expect(response).to have_http_status 201 - expect(group.runners.size).to eq(1) + expect(group.runners.reload.size).to eq(1) runner = Ci::Runner.first expect(runner.token).not_to eq(registration_token) expect(runner.token).not_to eq(group.runners_token) diff --git a/spec/requests/rack_attack_global_spec.rb b/spec/requests/rack_attack_global_spec.rb index 49021f5d1b7..a12646ea222 100644 --- a/spec/requests/rack_attack_global_spec.rb +++ b/spec/requests/rack_attack_global_spec.rb @@ -251,8 +251,8 @@ describe 'Rack Attack global throttles' do let(:throttle_setting_prefix) { 'throttle_authenticated_web' } context 'with the token in the query string' do - let(:get_args) { [rss_url(user), nil] } - let(:other_user_get_args) { [rss_url(other_user), nil] } + let(:get_args) { [rss_url(user), params: nil] } + let(:other_user_get_args) { [rss_url(other_user), params: nil] } it_behaves_like 'rate-limited token-authenticated requests' end diff --git a/spec/services/compare_service_spec.rb b/spec/services/compare_service_spec.rb index 0e4ef69ec19..fadd43635a6 100644 --- a/spec/services/compare_service_spec.rb +++ b/spec/services/compare_service_spec.rb @@ -19,5 +19,18 @@ describe CompareService do it { expect(subject.diffs.size).to eq(3) } end + + context 'compare with target branch that does not exist' do + subject { service.execute(project, 'non-existent-ref') } + + it { expect(subject).to be_nil } + end + + context 'compare with source branch that does not exist' do + let(:service) { described_class.new(project, 'non-existent-branch') } + subject { service.execute(project, 'non-existent-ref') } + + it { expect(subject).to be_nil } + end end end diff --git a/spec/services/delete_branch_service_spec.rb b/spec/services/delete_branch_service_spec.rb index 43a70d33d2d..b8064c2cbc1 100644 --- a/spec/services/delete_branch_service_spec.rb +++ b/spec/services/delete_branch_service_spec.rb @@ -8,20 +8,24 @@ describe DeleteBranchService do let(:user) { create(:user) } let(:service) { described_class.new(project, user) } + shared_examples 'a deleted branch' do |branch_name| + it 'removes the branch' do + expect(branch_exists?(branch_name)).to be true + + result = service.execute(branch_name) + + expect(result.status).to eq :success + expect(branch_exists?(branch_name)).to be false + end + end + describe '#execute' do context 'when user has access to push to repository' do before do project.add_developer(user) end - it 'removes the branch' do - expect(branch_exists?('feature')).to be true - - result = service.execute('feature') - - expect(result[:status]).to eq :success - expect(branch_exists?('feature')).to be false - end + it_behaves_like 'a deleted branch', 'feature' end context 'when user does not have access to push to repository' do @@ -30,8 +34,8 @@ describe DeleteBranchService do result = service.execute('feature') - expect(result[:status]).to eq :error - expect(result[:message]).to eq 'You dont have push access to repo' + expect(result.status).to eq :error + expect(result.message).to eq 'You dont have push access to repo' expect(branch_exists?('feature')).to be true end end diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb index c37147e7d3b..5ad30b58511 100644 --- a/spec/services/projects/update_service_spec.rb +++ b/spec/services/projects/update_service_spec.rb @@ -440,6 +440,8 @@ describe Projects::UpdateService do context 'when auto devops is set to instance setting' do before do project.create_auto_devops!(enabled: nil) + project.reload + allow(project.auto_devops).to receive(:previous_changes).and_return('enabled' => true) end diff --git a/spec/services/service_response_spec.rb b/spec/services/service_response_spec.rb new file mode 100644 index 00000000000..30bd4d6820b --- /dev/null +++ b/spec/services/service_response_spec.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' + +ActiveSupport::Dependencies.autoload_paths << 'app/services' + +describe ServiceResponse do + describe '.success' do + it 'creates a successful response without a message' do + expect(described_class.success).to be_success + end + + it 'creates a successful response with a message' do + response = described_class.success(message: 'Good orange') + + expect(response).to be_success + expect(response.message).to eq('Good orange') + end + end + + describe '.error' do + it 'creates a failed response without HTTP status' do + response = described_class.error(message: 'Bad apple') + + expect(response).to be_error + expect(response.message).to eq('Bad apple') + end + + it 'creates a failed response with HTTP status' do + response = described_class.error(message: 'Bad apple', http_status: 400) + + expect(response).to be_error + expect(response.message).to eq('Bad apple') + expect(response.http_status).to eq(400) + end + end + + describe '#success?' do + it 'returns true for a successful response' do + expect(described_class.success.success?).to eq(true) + end + + it 'returns false for a failed response' do + expect(described_class.error(message: 'Bad apple').success?).to eq(false) + end + end + + describe '#error?' do + it 'returns false for a successful response' do + expect(described_class.success.error?).to eq(false) + end + + it 'returns true for a failed response' do + expect(described_class.error(message: 'Bad apple').error?).to eq(true) + end + end +end diff --git a/spec/services/users/destroy_service_spec.rb b/spec/services/users/destroy_service_spec.rb index 1c79af34538..4a5f4509a7b 100644 --- a/spec/services/users/destroy_service_spec.rb +++ b/spec/services/users/destroy_service_spec.rb @@ -210,6 +210,8 @@ describe Users::DestroyService do describe "calls the before/after callbacks" do it 'of project_members' do + expect_any_instance_of(ProjectMember).to receive(:run_callbacks).with(:find).once + expect_any_instance_of(ProjectMember).to receive(:run_callbacks).with(:initialize).once expect_any_instance_of(ProjectMember).to receive(:run_callbacks).with(:destroy).once service.execute(user) @@ -219,6 +221,8 @@ describe Users::DestroyService do group_member = create(:group_member) group_member.group.group_members.create(user: user, access_level: 40) + expect_any_instance_of(GroupMember).to receive(:run_callbacks).with(:find).once + expect_any_instance_of(GroupMember).to receive(:run_callbacks).with(:initialize).once expect_any_instance_of(GroupMember).to receive(:run_callbacks).with(:destroy).once service.execute(user) diff --git a/spec/services/users/migrate_to_ghost_user_service_spec.rb b/spec/services/users/migrate_to_ghost_user_service_spec.rb index b808fa6d91a..40206775aed 100644 --- a/spec/services/users/migrate_to_ghost_user_service_spec.rb +++ b/spec/services/users/migrate_to_ghost_user_service_spec.rb @@ -80,7 +80,7 @@ describe Users::MigrateToGhostUserService do context "when record migration fails with a rollback exception" do before do - expect_any_instance_of(MergeRequest::ActiveRecord_Associations_CollectionProxy) + expect_any_instance_of(ActiveRecord::Associations::CollectionProxy) .to receive(:update_all).and_raise(ActiveRecord::Rollback) end diff --git a/spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb b/spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb index 72912ffb89d..a0c77eecb61 100644 --- a/spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb +++ b/spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb @@ -25,9 +25,13 @@ shared_context 'Ldap::OmniauthCallbacksController' do described_class.define_providers! Rails.application.reload_routes! - mock_auth_hash(provider.to_s, uid, user.email) + @original_env_config_omniauth_auth = mock_auth_hash(provider.to_s, uid, user.email) stub_omniauth_provider(provider, context: request) allow(Gitlab::Auth::LDAP::Access).to receive(:allowed?).and_return(valid_login?) end + + after do + Rails.application.env_config['omniauth.auth'] = @original_env_config_omniauth_auth + end end diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb index 4a0cf62a661..0bb2d2510c2 100644 --- a/spec/support/helpers/login_helpers.rb +++ b/spec/support/helpers/login_helpers.rb @@ -118,7 +118,10 @@ module LoginHelpers response_object: response_object } }) + original_env_config_omniauth_auth = Rails.application.env_config['omniauth.auth'] Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[provider.to_sym] + + original_env_config_omniauth_auth end def saml_xml(raw_saml_response) diff --git a/spec/support/helpers/query_recorder.rb b/spec/support/helpers/query_recorder.rb index 7ce63375d34..c4ae62b25e4 100644 --- a/spec/support/helpers/query_recorder.rb +++ b/spec/support/helpers/query_recorder.rb @@ -17,7 +17,7 @@ module ActiveRecord def callback(name, start, finish, message_id, values) show_backtrace(values) if ENV['QUERY_RECORDER_DEBUG'] - if values[:name]&.include?("CACHE") && skip_cached + if values[:cached] && skip_cached @cached << values[:sql] elsif !values[:name]&.include?("SCHEMA") @log << values[:sql] diff --git a/spec/support/helpers/test_request_helpers.rb b/spec/support/helpers/test_request_helpers.rb index 5a84d67bdfc..39e5dafb059 100644 --- a/spec/support/helpers/test_request_helpers.rb +++ b/spec/support/helpers/test_request_helpers.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module TestRequestHelpers - def test_request(remote_ip: '127.0.0.1') - ActionController::TestRequest.new({ remote_ip: remote_ip }, ActionController::TestSession.new) + def test_request(remote_ip: '127.0.0.1', controller: nil) + ActionController::TestRequest.new({ remote_ip: remote_ip }, ActionController::TestSession.new, controller) end end diff --git a/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb b/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb index 62ae95df8c0..1284415da1f 100644 --- a/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb +++ b/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb @@ -45,7 +45,7 @@ shared_examples "migrating a deleted user's associated records to the ghost user context "race conditions" do context "when #{record_class_name} migration fails and is rolled back" do before do - expect_any_instance_of(record_class::ActiveRecord_Associations_CollectionProxy) + expect_any_instance_of(ActiveRecord::Associations::CollectionProxy) .to receive(:update_all).and_raise(ActiveRecord::Rollback) end @@ -66,7 +66,7 @@ shared_examples "migrating a deleted user's associated records to the ghost user context "when #{record_class_name} migration fails with a non-rollback exception" do before do - expect_any_instance_of(record_class::ActiveRecord_Associations_CollectionProxy) + expect_any_instance_of(ActiveRecord::Associations::CollectionProxy) .to receive(:update_all).and_raise(ArgumentError) end diff --git a/yarn.lock b/yarn.lock index ebbb670db0c..8a23aabba20 100644 --- a/yarn.lock +++ b/yarn.lock @@ -663,10 +663,10 @@ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.59.0.tgz#affcf9596d736836d37469bb4aea2226ac03e087" integrity sha512-dokGyyLRRsoBKO70KP1g+ZsDGyTK/RIHWDmvWI6Bx5AxQ3UqAzVXn2OIb3owjJAexyRG1uBmJrriiVVyHznQ4g== -"@gitlab/ui@^3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-3.3.0.tgz#32112c8eb53a0fd893f8fa431a02ceaf55279323" - integrity sha512-VVInZmcAe0L0lRMb6II3ED4DYm4OpzSmcxdwt18CqpDMw3EEoUqxd58EAxBFy70isgFTd6cHYhyS8rw5GOULyg== +"@gitlab/ui@^3.4.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-3.4.0.tgz#174681f210eb16c3d101a36968d5e4d163c0d014" + integrity sha512-joXNz80IHMQxEGrqcNUTEKofjfZtkOKUe34HAFI71NEeYT6H0r/lYmJ5Gcz+MmwM1CvZOVbB3DnKzxQPDbN/hQ== dependencies: "@babel/standalone" "^7.0.0" bootstrap-vue "^2.0.0-rc.11" |