diff options
author | Stan Hu <stanhu@gmail.com> | 2018-07-27 16:58:14 -0700 |
---|---|---|
committer | Stan Hu <stanhu@gmail.com> | 2018-07-27 16:58:14 -0700 |
commit | a91ba2287ce7adebb5e0eb33f4335d4ce5f054ed (patch) | |
tree | 46343419e74ea762869225b982850ac97ed67ea4 | |
parent | 57d1b60f61a790c034b8d43ce2358371464d2d67 (diff) | |
parent | 35ce06add8ee414e2694017126f12d47e9c9bf27 (diff) | |
download | gitlab-ce-a91ba2287ce7adebb5e0eb33f4335d4ce5f054ed.tar.gz |
Merge branch 'master' into sh-support-bitbucket-server-import
426 files changed, 4055 insertions, 982 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c7ef75d1c32..fcf47421b01 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -439,13 +439,12 @@ setup-test-env: - vendor/gitaly-ruby danger-review: - image: registry.gitlab.com/gitlab-org/gitaly/dangercontainer:latest + image: registry.gitlab.com/gitlab-org/gitlab-build-images:danger stage: test allow_failure: true - before_script: - - source scripts/utils.sh - - retry gem install danger --no-ri --no-rdoc cache: {} + dependencies: [] + before_script: [] only: variables: - $DANGER_GITLAB_API_TOKEN diff --git a/.rubocop.yml b/.rubocop.yml index 0582bfe8d70..c8b1ce327e2 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -10,9 +10,9 @@ AllCops: Exclude: - 'vendor/**/*' - 'node_modules/**/*' - - 'db/**/*' - 'db/fixtures/**/*' - - 'ee/db/**/*' + - 'db/schema.rb' + - 'ee/db/geo/schema.rb' - 'tmp/**/*' - 'bin/**/*' - 'generator_templates/**/*' @@ -34,6 +34,8 @@ Style/MutableConstant: Naming/FileName: ExpectMatchingDefinition: true Exclude: + - 'db/**/*' + - 'ee/db/**/*' - 'spec/**/*' - 'features/**/*' - 'ee/spec/**/*' diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cc047c40ed..b4b672b55c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,42 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 11.1.3 (2018-07-27) + +### Fixed (8 changes, 1 of them is from the community) + +- Rework some projects table indexes around repository_storage field. !20377 +- Fix navigation to First and Next discussion on MR Changes tab. !20434 +- Fix showing outdated discussions on Changes tab. !20445 +- Fix autosave and ESC confirmation issues for MR discussions. !20569 +- Fix rendering of the context lines in MR diffs page. !20642 +- Don't overflow project/group dropdown results. !20704 (gfyoung) +- Fixed IDE not opening JSON files. !20798 +- Disable Gitaly timeouts when creating or restoring backups. !20810 + +### Performance (1 change) + +- Reduces the client side memory footprint on merge requests. !20744 + + +## 11.1.2 (2018-07-26) + +### Security (4 changes) + +- Adding CSRF protection to Hooks test action. +- Don't expose project names in GitHub counters. +- Don't expose project names in various counters. +- Fixed XSS in branch name in Web IDE. + +### Fixed (1 change) + +- Escapes milestone and label's names on flash notice when promoting them. + +### Performance (1 change) + +- Fix slow Markdown rendering. !20820 + + ## 11.1.1 (2018-07-23) ### Fixed (2 changes) @@ -253,6 +289,20 @@ entry. - Use monospaced font for MR diff commit link ref on GFM. +## 11.0.5 (2018-07-26) + +### Security (4 changes) + +- Don't expose project names in various counters. +- Don't expose project names in GitHub counters. +- Adding CSRF protection to Hooks test action. +- Fixed XSS in branch name in Web IDE. + +### Fixed (1 change) + +- Escapes milestone and label's names on flash notice when promoting them. + + ## 11.0.4 (2018-07-17) ### Security (1 change) diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index 69adf3456f8..0ee843cc604 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -7.1.5 +7.2.0 @@ -400,6 +400,7 @@ gem 'email_reply_trimmer', '~> 0.1' gem 'html2text' gem 'ruby-prof', '~> 0.17.0' +gem 'rbtrace', '~> 0.4', require: false # OAuth gem 'oauth2', '~> 1.4' diff --git a/Gemfile.lock b/Gemfile.lock index 5bb7bc53962..d147f81c81e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -375,7 +375,8 @@ GEM google-protobuf (~> 3.1) googleapis-common-protos-types (~> 1.0.0) googleauth (>= 0.5.1, < 0.7) - haml (4.0.7) + haml (5.0.4) + temple (>= 0.8.0) tilt haml_lint (0.26.0) haml (>= 4.0, < 5.1) @@ -700,6 +701,10 @@ GEM ffi (>= 0.5.0, < 2) rblineprof (0.3.6) debugger-ruby_core_source (~> 1.3) + rbtrace (0.4.10) + ffi (>= 1.0.6) + msgpack (>= 0.4.3) + trollop (>= 1.16.2) rdoc (6.0.4) re2 (1.1.1) recaptcha (3.0.0) @@ -911,6 +916,7 @@ GEM parslet (~> 1.5.0) toml-rb (1.0.0) citrus (~> 3.0, > 3.0) + trollop (2.1.3) truncato (0.7.10) htmlentities (~> 4.3.1) nokogiri (~> 1.8.0, >= 1.7.0) @@ -1133,6 +1139,7 @@ DEPENDENCIES rainbow (~> 2.2) raindrops (~> 0.18) rblineprof (~> 0.3.6) + rbtrace (~> 0.4) rdoc (~> 6.0) re2 (~> 1.1.1) recaptcha (~> 3.0) diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock index 8c14316a67a..b055b561b61 100644 --- a/Gemfile.rails5.lock +++ b/Gemfile.rails5.lock @@ -364,7 +364,7 @@ GEM grape-entity (0.7.1) activesupport (>= 4.0) multi_json (>= 1.3.2) - grape-path-helpers (1.0.5) + grape-path-helpers (1.0.6) activesupport (>= 4, < 5.1) grape (~> 1.0) rake (~> 12) @@ -378,7 +378,8 @@ GEM google-protobuf (~> 3.1) googleapis-common-protos-types (~> 1.0.0) googleauth (>= 0.5.1, < 0.7) - haml (4.0.7) + haml (5.0.4) + temple (>= 0.8.0) tilt haml_lint (0.26.0) haml (>= 4.0, < 5.1) @@ -400,7 +401,7 @@ GEM hipchat (1.5.2) httparty mimemagic - html-pipeline (2.8.3) + html-pipeline (2.8.4) activesupport (>= 2) nokogiri (>= 1.4) html2text (0.2.0) @@ -518,7 +519,7 @@ GEM net-ssh (5.0.1) netrc (0.11.0) nio4r (2.3.1) - nokogiri (1.8.3) + nokogiri (1.8.4) mini_portile2 (~> 2.3.0) nokogumbo (1.5.0) nokogiri @@ -709,6 +710,10 @@ GEM ffi (>= 0.5.0, < 2) rblineprof (0.3.6) debugger-ruby_core_source (~> 1.3) + rbtrace (0.4.10) + ffi (>= 1.0.6) + msgpack (>= 0.4.3) + trollop (>= 1.16.2) rdoc (6.0.4) re2 (1.1.1) recaptcha (3.0.0) @@ -817,7 +822,7 @@ GEM et-orbi (~> 1.0) rugged (0.27.2) safe_yaml (1.0.4) - sanitize (4.6.5) + sanitize (4.6.6) crass (~> 1.0.2) nokogiri (>= 1.4.4) nokogumbo (~> 1.4) @@ -911,13 +916,14 @@ GEM rack (>= 1, < 3) thor (0.19.4) thread_safe (0.3.6) - tilt (2.0.6) + tilt (2.0.8) timecop (0.8.1) timfel-krb5-auth (0.8.3) toml (0.1.2) parslet (~> 1.5.0) toml-rb (1.0.0) citrus (~> 3.0, > 3.0) + trollop (2.1.3) truncato (0.7.10) htmlentities (~> 4.3.1) nokogiri (~> 1.8.0, >= 1.7.0) @@ -1144,6 +1150,7 @@ DEPENDENCIES rainbow (~> 2.2) raindrops (~> 0.18) rblineprof (~> 0.3.6) + rbtrace (~> 0.4) rdoc (~> 6.0) re2 (~> 1.1.1) recaptcha (~> 3.0) diff --git a/app/assets/javascripts/badges/components/badge.vue b/app/assets/javascripts/badges/components/badge.vue index b4bfaee1d85..155c348286c 100644 --- a/app/assets/javascripts/badges/components/badge.vue +++ b/app/assets/javascripts/badges/components/badge.vue @@ -93,7 +93,7 @@ export default { <icon :size="16" class="prepend-left-8 append-right-8" - name="doc_image" + name="doc-image" aria-hidden="true" /> </div> diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js index 200d1923635..bc263cbbfea 100644 --- a/app/assets/javascripts/boards/index.js +++ b/app/assets/javascripts/boards/index.js @@ -1,5 +1,3 @@ -/* eslint-disable quote-props, comma-dangle */ - import $ from 'jquery'; import _ from 'underscore'; import Vue from 'vue'; @@ -47,7 +45,7 @@ export default () => { gl.IssueBoardsApp = new Vue({ el: $boardApp, components: { - 'board': gl.issueBoards.Board, + board: gl.issueBoards.Board, 'board-sidebar': gl.issueBoards.BoardSidebar, BoardAddIssuesModal, }, @@ -65,11 +63,11 @@ export default () => { defaultAvatar: $boardApp.dataset.defaultAvatar, }, computed: { - detailIssueVisible () { + detailIssueVisible() { return Object.keys(this.detailIssue.issue).length; }, }, - created () { + created() { gl.boardService = new BoardService({ boardsEndpoint: this.boardsEndpoint, listsEndpoint: this.listsEndpoint, @@ -89,15 +87,16 @@ export default () => { eventHub.$off('clearDetailIssue', this.clearDetailIssue); sidebarEventHub.$off('toggleSubscription', this.toggleSubscription); }, - mounted () { + mounted() { this.filterManager = new FilteredSearchBoards(Store.filter, true, Store.cantEdit); this.filterManager.setup(); Store.disabled = this.disabled; - gl.boardService.all() + gl.boardService + .all() .then(res => res.data) - .then((data) => { - data.forEach((board) => { + .then(data => { + data.forEach(board => { const list = Store.addList(board, this.defaultAvatar); if (list.type === 'closed') { @@ -126,7 +125,7 @@ export default () => { newIssue.setFetchingState('subscriptions', true); BoardService.getIssueInfo(sidebarInfoEndpoint) .then(res => res.data) - .then((data) => { + .then(data => { newIssue.setFetchingState('subscriptions', false); newIssue.updateData({ subscribed: data.subscribed, @@ -159,7 +158,7 @@ export default () => { Flash(__('An error occurred when toggling the notification subscription')); }); } - } + }, }, }); @@ -168,77 +167,81 @@ export default () => { data: { filters: Store.state.filters, }, - mounted () { + mounted() { gl.issueBoards.newListDropdownInit(); }, }); - gl.IssueBoardsModalAddBtn = new Vue({ - el: document.getElementById('js-add-issues-btn'), - mixins: [modalMixin], - data() { - return { - modal: ModalStore.store, - store: Store.state, - canAdminList: this.$options.el.hasAttribute('data-can-admin-list'), - }; - }, - computed: { - disabled() { - if (!this.store) { - return true; - } - return !this.store.lists.filter(list => !list.preset).length; + const issueBoardsModal = document.getElementById('js-add-issues-btn'); + + if (issueBoardsModal) { + gl.IssueBoardsModalAddBtn = new Vue({ + el: issueBoardsModal, + mixins: [modalMixin], + data() { + return { + modal: ModalStore.store, + store: Store.state, + canAdminList: this.$options.el.hasAttribute('data-can-admin-list'), + }; }, - tooltipTitle() { - if (this.disabled) { - return 'Please add a list to your board first'; - } + computed: { + disabled() { + if (!this.store) { + return true; + } + return !this.store.lists.filter(list => !list.preset).length; + }, + tooltipTitle() { + if (this.disabled) { + return 'Please add a list to your board first'; + } - return ''; + return ''; + }, }, - }, - watch: { - disabled() { + watch: { + disabled() { + this.updateTooltip(); + }, + }, + mounted() { this.updateTooltip(); }, - }, - mounted() { - this.updateTooltip(); - }, - methods: { - updateTooltip() { - const $tooltip = $(this.$refs.addIssuesButton); - - this.$nextTick(() => { - if (this.disabled) { - $tooltip.tooltip(); - } else { - $tooltip.tooltip('dispose'); + methods: { + updateTooltip() { + const $tooltip = $(this.$refs.addIssuesButton); + + this.$nextTick(() => { + if (this.disabled) { + $tooltip.tooltip(); + } else { + $tooltip.tooltip('dispose'); + } + }); + }, + openModal() { + if (!this.disabled) { + this.toggleModal(true); } - }); - }, - openModal() { - if (!this.disabled) { - this.toggleModal(true); - } + }, }, - }, - template: ` - <div class="board-extra-actions"> - <button - class="btn btn-create prepend-left-10" - type="button" - data-placement="bottom" - ref="addIssuesButton" - :class="{ 'disabled': disabled }" - :title="tooltipTitle" - :aria-disabled="disabled" - v-if="canAdminList" - @click="openModal"> - Add issues - </button> - </div> - `, - }); + template: ` + <div class="board-extra-actions"> + <button + class="btn btn-create prepend-left-10" + type="button" + data-placement="bottom" + ref="addIssuesButton" + :class="{ 'disabled': disabled }" + :title="tooltipTitle" + :aria-disabled="disabled" + v-if="canAdminList" + @click="openModal"> + Add issues + </button> + </div> + `, + }); + } }; diff --git a/app/assets/javascripts/commons/gitlab_ui.js b/app/assets/javascripts/commons/gitlab_ui.js new file mode 100644 index 00000000000..923c036f5a4 --- /dev/null +++ b/app/assets/javascripts/commons/gitlab_ui.js @@ -0,0 +1,4 @@ +import Vue from 'vue'; +import progressBar from '@gitlab-org/gitlab-ui/dist/base/progress_bar'; + +Vue.component('gl-progress-bar', progressBar); diff --git a/app/assets/javascripts/commons/index.js b/app/assets/javascripts/commons/index.js index 0d2fe2925d8..ea945cd3fa5 100644 --- a/app/assets/javascripts/commons/index.js +++ b/app/assets/javascripts/commons/index.js @@ -3,4 +3,5 @@ import './polyfills'; import './jquery'; import './bootstrap'; import './vue'; +import './gitlab_ui'; import '../lib/utils/axios_utils'; diff --git a/app/assets/javascripts/ide/components/changed_file_icon.vue b/app/assets/javascripts/ide/components/changed_file_icon.vue index a4e06bbbe3c..720ae11aaa6 100644 --- a/app/assets/javascripts/ide/components/changed_file_icon.vue +++ b/app/assets/javascripts/ide/components/changed_file_icon.vue @@ -3,6 +3,7 @@ import tooltip from '~/vue_shared/directives/tooltip'; import Icon from '~/vue_shared/components/icon.vue'; import { pluralize } from '~/lib/utils/text_utility'; import { __, sprintf } from '~/locale'; +import { getCommitIconMap } from '../utils'; export default { components: { @@ -34,16 +35,14 @@ export default { }, computed: { changedIcon() { - const suffix = this.file.staged && !this.showStagedIcon ? '-solid' : ''; - return this.file.tempFile && !this.forceModifiedIcon - ? `file-addition${suffix}` - : `file-modified${suffix}`; - }, - stagedIcon() { - return `${this.changedIcon}-solid`; + const suffix = !this.file.changed && this.file.staged && !this.showStagedIcon ? '-solid' : ''; + + if (this.forceModifiedIcon) return `file-modified${suffix}`; + + return `${getCommitIconMap(this.file).icon}${suffix}`; }, changedIconClass() { - return `multi-${this.changedIcon} float-left`; + return `ide-${this.changedIcon} float-left`; }, tooltipTitle() { if (!this.showTooltip) return undefined; @@ -66,6 +65,9 @@ export default { return undefined; }, + showIcon() { + return this.file.changed || this.file.tempFile || this.file.staged || this.file.deleted; + }, }, }; </script> @@ -79,7 +81,7 @@ export default { class="ide-file-changed-icon" > <icon - v-if="file.changed || file.tempFile || file.staged" + v-if="showIcon" :name="changedIcon" :size="12" :css-classes="changedIconClass" diff --git a/app/assets/javascripts/ide/components/commit_sidebar/actions.vue b/app/assets/javascripts/ide/components/commit_sidebar/actions.vue index eb7cb9745ec..a8b5c7a16d0 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/actions.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/actions.vue @@ -1,4 +1,5 @@ <script> +import _ from 'underscore'; import { mapActions, mapState, mapGetters } from 'vuex'; import { sprintf, __ } from '~/locale'; import * as consts from '../../stores/modules/commit/constants'; @@ -14,7 +15,7 @@ export default { commitToCurrentBranchText() { return sprintf( __('Commit to %{branchName} branch'), - { branchName: `<strong class="monospace">${this.currentBranchId}</strong>` }, + { branchName: `<strong class="monospace">${_.escape(this.currentBranchId)}</strong>` }, false, ); }, diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue index ee21eeda3cd..391004dcd3c 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue @@ -5,6 +5,7 @@ import Icon from '~/vue_shared/components/icon.vue'; import StageButton from './stage_button.vue'; import UnstageButton from './unstage_button.vue'; import { viewerTypes } from '../../constants'; +import { getCommitIconMap } from '../../utils'; export default { components: { @@ -42,11 +43,12 @@ export default { }, computed: { iconName() { - const prefix = this.stagedList ? '-solid' : ''; - return this.file.tempFile ? `file-addition${prefix}` : `file-modified${prefix}`; + const suffix = this.stagedList ? '-solid' : ''; + + return `${getCommitIconMap(this.file).icon}${suffix}`; }, iconClass() { - return `multi-file-${this.file.tempFile ? 'addition' : 'modified'} append-right-8`; + return `${getCommitIconMap(this.file).class} append-right-8`; }, fullKey() { return `${this.keyPrefix}-${this.file.key}`; @@ -67,6 +69,8 @@ export default { 'stageChange', ]), openFileInEditor() { + if (this.file.type === 'tree') return null; + return this.openPendingTab({ file: this.file, keyPrefix: this.keyPrefix, diff --git a/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue b/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue index 7014b9f605e..e6044401c9f 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue @@ -56,7 +56,7 @@ export default { > <icon :size="12" - name="more" + name="ellipsis_h" /> </button> <div class="dropdown-menu dropdown-menu-right"> diff --git a/app/assets/javascripts/ide/components/ide_review.vue b/app/assets/javascripts/ide/components/ide_review.vue index f9978762c45..d09c99050fe 100644 --- a/app/assets/javascripts/ide/components/ide_review.vue +++ b/app/assets/javascripts/ide/components/ide_review.vue @@ -10,7 +10,7 @@ export default { EditorModeDropdown, }, computed: { - ...mapGetters(['currentMergeRequest']), + ...mapGetters(['currentMergeRequest', 'activeFile']), ...mapState(['viewer', 'currentMergeRequestId']), showLatestChangesText() { return !this.currentMergeRequestId || this.viewer === viewerTypes.diff; @@ -23,12 +23,20 @@ export default { }, }, mounted() { + if (this.activeFile && this.activeFile.pending && !this.activeFile.deleted) { + this.$router.push(`/project${this.activeFile.url}`, () => { + this.updateViewer('editor'); + }); + } else if (this.activeFile && this.activeFile.deleted) { + this.resetOpenFiles(); + } + this.$nextTick(() => { this.updateViewer(this.currentMergeRequestId ? viewerTypes.mr : viewerTypes.diff); }); }, methods: { - ...mapActions(['updateViewer']), + ...mapActions(['updateViewer', 'resetOpenFiles']), }, }; </script> @@ -36,7 +44,6 @@ export default { <template> <ide-tree-list :viewer-type="viewer" - :disable-action-dropdown="true" header-class="ide-review-header" > <template diff --git a/app/assets/javascripts/ide/components/ide_tree.vue b/app/assets/javascripts/ide/components/ide_tree.vue index 0a95c0bb30d..e996dd9059e 100644 --- a/app/assets/javascripts/ide/components/ide_tree.vue +++ b/app/assets/javascripts/ide/components/ide_tree.vue @@ -17,14 +17,18 @@ export default { ...mapGetters(['currentProject', 'currentTree', 'activeFile']), }, mounted() { - if (this.activeFile && this.activeFile.pending) { + if (!this.activeFile) return; + + if (this.activeFile.pending && !this.activeFile.deleted) { this.$router.push(`/project${this.activeFile.url}`, () => { this.updateViewer('editor'); }); + } else if (this.activeFile.deleted) { + this.resetOpenFiles(); } }, methods: { - ...mapActions(['updateViewer', 'openNewEntryModal', 'createTempEntry']), + ...mapActions(['updateViewer', 'openNewEntryModal', 'createTempEntry', 'resetOpenFiles']), }, }; </script> diff --git a/app/assets/javascripts/ide/components/ide_tree_list.vue b/app/assets/javascripts/ide/components/ide_tree_list.vue index 0df99798d21..2e7226b727c 100644 --- a/app/assets/javascripts/ide/components/ide_tree_list.vue +++ b/app/assets/javascripts/ide/components/ide_tree_list.vue @@ -22,11 +22,6 @@ export default { required: false, default: null, }, - disableActionDropdown: { - type: Boolean, - required: false, - default: false, - }, }, computed: { ...mapState(['currentBranchId']), @@ -69,7 +64,6 @@ export default { :key="file.key" :file="file" :level="0" - :disable-action-dropdown="disableActionDropdown" /> </template> </div> diff --git a/app/assets/javascripts/ide/components/new_dropdown/index.vue b/app/assets/javascripts/ide/components/new_dropdown/index.vue index c29e49ba766..440e480d596 100644 --- a/app/assets/javascripts/ide/components/new_dropdown/index.vue +++ b/app/assets/javascripts/ide/components/new_dropdown/index.vue @@ -13,7 +13,7 @@ export default { ItemButton, }, props: { - branch: { + type: { type: String, required: true, }, @@ -45,7 +45,7 @@ export default { }, }, methods: { - ...mapActions(['createTempEntry', 'openNewEntryModal']), + ...mapActions(['createTempEntry', 'openNewEntryModal', 'deleteEntry']), createNewItem(type) { this.openNewEntryModal({ type, path: this.path }); this.dropdownOpen = false; @@ -82,28 +82,40 @@ export default { ref="dropdownMenu" class="dropdown-menu dropdown-menu-right" > + <template v-if="type === 'tree'"> + <li> + <item-button + :label="__('New file')" + class="d-flex" + icon="doc-new" + icon-classes="mr-2" + @click="createNewItem('blob')" + /> + </li> + <li> + <upload + :path="path" + @create="createTempEntry" + /> + </li> + <li> + <item-button + :label="__('New directory')" + class="d-flex" + icon="folder-new" + icon-classes="mr-2" + @click="createNewItem('tree')" + /> + </li> + <li class="divider"></li> + </template> <li> <item-button - :label="__('New file')" + :label="__('Delete')" class="d-flex" - icon="doc-new" + icon="remove" icon-classes="mr-2" - @click="createNewItem('blob')" - /> - </li> - <li> - <upload - :path="path" - @create="createTempEntry" - /> - </li> - <li> - <item-button - :label="__('New directory')" - class="d-flex" - icon="folder-new" - icon-classes="mr-2" - @click="createNewItem('tree')" + @click="deleteEntry(path)" /> </li> </ul> diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue index 50ab242ba2a..6f1a941fbc4 100644 --- a/app/assets/javascripts/ide/components/repo_commit_section.vue +++ b/app/assets/javascripts/ide/components/repo_commit_section.vue @@ -44,7 +44,7 @@ export default { }, }, mounted() { - if (this.lastOpenedFile) { + if (this.lastOpenedFile && this.lastOpenedFile.type !== 'tree') { this.openPendingTab({ file: this.lastOpenedFile, keyPrefix: this.lastOpenedFile.changed ? stageKeys.unstaged : stageKeys.staged, diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue index 08ee12fd98f..f9badb01535 100644 --- a/app/assets/javascripts/ide/components/repo_editor.vue +++ b/app/assets/javascripts/ide/components/repo_editor.vue @@ -87,7 +87,9 @@ export default { this.editor.updateDimensions(); }, viewer() { - this.createEditorInstance(); + if (!this.file.pending) { + this.createEditorInstance(); + } }, panelResizing() { if (!this.panelResizing) { @@ -109,6 +111,7 @@ export default { }, methods: { ...mapActions([ + 'getFileData', 'getRawFileData', 'changeFileContent', 'setFileLanguage', @@ -123,10 +126,16 @@ export default { this.editor.clearEditor(); - this.getRawFileData({ + this.getFileData({ path: this.file.path, - baseSha: this.currentMergeRequest ? this.currentMergeRequest.baseCommitSha : '', + makeFileActive: false, }) + .then(() => + this.getRawFileData({ + path: this.file.path, + baseSha: this.currentMergeRequest ? this.currentMergeRequest.baseCommitSha : '', + }), + ) .then(() => { this.createEditorInstance(); }) @@ -246,6 +255,8 @@ export default { ref="editor" :class="{ 'is-readonly': isCommitModeActive, + 'is-deleted': file.deleted, + 'is-added': file.tempFile }" class="multi-file-editor-holder" > diff --git a/app/assets/javascripts/ide/components/repo_file.vue b/app/assets/javascripts/ide/components/repo_file.vue index 3b4dd5ae9aa..eb4a927fe0d 100644 --- a/app/assets/javascripts/ide/components/repo_file.vue +++ b/app/assets/javascripts/ide/components/repo_file.vue @@ -34,11 +34,6 @@ export default { type: Number, required: true, }, - disableActionDropdown: { - type: Boolean, - required: false, - default: false, - }, }, data() { return { @@ -212,8 +207,7 @@ export default { /> </span> <new-dropdown - v-if="isTree && !disableActionDropdown" - :project-id="file.projectId" + :type="file.type" :branch="file.branchId" :path="file.path" :mouse-over="mouseOver" diff --git a/app/assets/javascripts/ide/components/repo_tab.vue b/app/assets/javascripts/ide/components/repo_tab.vue index 03772ae4a4c..db47b75ec5c 100644 --- a/app/assets/javascripts/ide/components/repo_tab.vue +++ b/app/assets/javascripts/ide/components/repo_tab.vue @@ -37,7 +37,7 @@ export default { return this.fileHasChanged ? !this.tabMouseOver : false; }, fileHasChanged() { - return this.tab.changed || this.tab.tempFile || this.tab.staged; + return this.tab.changed || this.tab.tempFile || this.tab.staged || this.tab.deleted; }, }, @@ -71,7 +71,8 @@ export default { <template> <li :class="{ - active: tab.active + active: tab.active, + disabled: tab.pending }" @click="clickFile(tab)" @mouseover="mouseOverTab" @@ -105,7 +106,6 @@ export default { <changed-file-icon v-else :file="tab" - :force-modified-icon="true" /> </button> </li> diff --git a/app/assets/javascripts/ide/constants.js b/app/assets/javascripts/ide/constants.js index 45d36f6f42c..0b514f31467 100644 --- a/app/assets/javascripts/ide/constants.js +++ b/app/assets/javascripts/ide/constants.js @@ -38,3 +38,18 @@ export const stageKeys = { unstaged: 'unstaged', staged: 'staged', }; + +export const commitItemIconMap = { + addition: { + icon: 'file-addition', + class: 'ide-file-addition', + }, + modified: { + icon: 'file-modified', + class: 'ide-file-modified', + }, + deleted: { + icon: 'file-deletion', + class: 'ide-file-deletion', + }, +}; diff --git a/app/assets/javascripts/ide/lib/common/model.js b/app/assets/javascripts/ide/lib/common/model.js index 78e6f632728..60bddb34977 100644 --- a/app/assets/javascripts/ide/lib/common/model.js +++ b/app/assets/javascripts/ide/lib/common/model.js @@ -7,7 +7,7 @@ export default class Model { this.disposable = new Disposable(); this.file = file; this.head = head; - this.content = file.content !== '' ? file.content : file.raw; + this.content = file.content !== '' || file.deleted ? file.content : file.raw; this.disposable.add( (this.originalModel = monacoEditor.createModel( diff --git a/app/assets/javascripts/ide/services/index.js b/app/assets/javascripts/ide/services/index.js index 49a481f25d5..cb93fba1665 100644 --- a/app/assets/javascripts/ide/services/index.js +++ b/app/assets/javascripts/ide/services/index.js @@ -18,7 +18,7 @@ export default { return axios .get(file.rawPath, { - params: { format: 'json' }, + transformResponse: [f => f], }) .then(({ data }) => data); }, @@ -33,7 +33,7 @@ export default { return axios .get(file.rawPath.replace(`/raw/${file.branchId}/${file.path}`, `/raw/${sha}/${file.path}`), { - params: { format: 'json' }, + transformResponse: [f => f], }) .then(({ data }) => data); }, diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js index b5bd6f5a6bb..2765acada48 100644 --- a/app/assets/javascripts/ide/stores/actions.js +++ b/app/assets/javascripts/ide/stores/actions.js @@ -185,6 +185,14 @@ export const openNewEntryModal = ({ commit }, { type, path = '' }) => { $('#ide-new-entry').modal('show'); }; +export const deleteEntry = ({ commit, dispatch, state }, path) => { + dispatch('burstUnusedSeal'); + dispatch('closeFile', state.entries[path]); + commit(types.DELETE_ENTRY, path); +}; + +export const resetOpenFiles = ({ commit }) => commit(types.RESET_OPEN_FILES); + export * from './actions/tree'; export * from './actions/file'; export * from './actions/project'; diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js index 6c0887e11ee..b343750f789 100644 --- a/app/assets/javascripts/ide/stores/actions/file.js +++ b/app/assets/javascripts/ide/stores/actions/file.js @@ -61,7 +61,11 @@ export const setFileActive = ({ commit, state, getters, dispatch }, path) => { export const getFileData = ({ state, commit, dispatch }, { path, makeFileActive = true }) => { const file = state.entries[path]; + + if (file.raw || file.tempFile) return Promise.resolve(); + commit(types.TOGGLE_LOADING, { entry: file }); + return service .getFileData( `${gon.relative_url_root ? gon.relative_url_root : ''}${file.url.replace('/-/', '/')}`, @@ -71,7 +75,7 @@ export const getFileData = ({ state, commit, dispatch }, { path, makeFileActive setPageTitle(decodeURI(normalizedHeaders['PAGE-TITLE'])); commit(types.SET_FILE_DATA, { data, file }); - commit(types.TOGGLE_FILE_OPEN, path); + if (makeFileActive) commit(types.TOGGLE_FILE_OPEN, path); if (makeFileActive) dispatch('setFileActive', path); commit(types.TOGGLE_LOADING, { entry: file }); }) @@ -97,7 +101,7 @@ export const getRawFileData = ({ state, commit, dispatch }, { path, baseSha }) = service .getRawFileData(file) .then(raw => { - commit(types.SET_FILE_RAW_DATA, { file, raw }); + if (!file.tempFile) commit(types.SET_FILE_RAW_DATA, { file, raw }); if (file.mrChange && file.mrChange.new_file === false) { service .getBaseRawFileData(file, baseSha) diff --git a/app/assets/javascripts/ide/stores/actions/tree.js b/app/assets/javascripts/ide/stores/actions/tree.js index ffaaaabff17..acb6ef5e6d4 100644 --- a/app/assets/javascripts/ide/stores/actions/tree.js +++ b/app/assets/javascripts/ide/stores/actions/tree.js @@ -21,14 +21,12 @@ export const showTreeEntry = ({ commit, dispatch, state }, path) => { export const handleTreeEntryAction = ({ commit, dispatch }, row) => { if (row.type === 'tree') { dispatch('toggleTreeOpen', row.path); - } else if (row.type === 'blob' && (row.opened || row.changed)) { - if (row.changed && !row.opened) { + } else if (row.type === 'blob') { + if (!row.opened) { commit(types.TOGGLE_FILE_OPEN, row.path); } dispatch('setFileActive', row.path); - } else { - dispatch('getFileData', { path: row.path }); } dispatch('showTreeEntry', row.path); diff --git a/app/assets/javascripts/ide/stores/modules/commit/actions.js b/app/assets/javascripts/ide/stores/modules/commit/actions.js index 7828c31f20e..462ca45db9b 100644 --- a/app/assets/javascripts/ide/stores/modules/commit/actions.js +++ b/app/assets/javascripts/ide/stores/modules/commit/actions.js @@ -174,11 +174,13 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo dispatch('updateActivityBarView', activityBarViews.edit, { root: true }); dispatch('updateViewer', 'editor', { root: true }); - router.push( - `/project/${rootState.currentProjectId}/blob/${getters.branchName}/-/${ - rootGetters.activeFile.path - }`, - ); + if (rootGetters.activeFile) { + router.push( + `/project/${rootState.currentProjectId}/blob/${getters.branchName}/-/${ + rootGetters.activeFile.path + }`, + ); + } } }) .then(() => dispatch('updateCommitAction', consts.COMMIT_TO_CURRENT_BRANCH)) diff --git a/app/assets/javascripts/ide/stores/modules/commit/getters.js b/app/assets/javascripts/ide/stores/modules/commit/getters.js index 3db4b2f903e..03777e6c10b 100644 --- a/app/assets/javascripts/ide/stores/modules/commit/getters.js +++ b/app/assets/javascripts/ide/stores/modules/commit/getters.js @@ -1,7 +1,15 @@ -import { sprintf, n__ } from '../../../../locale'; +import { sprintf, n__, __ } from '../../../../locale'; import * as consts from './constants'; const BRANCH_SUFFIX_COUNT = 5; +const createTranslatedTextForFiles = (files, text) => { + if (!files.length) return null; + + return sprintf(n__('%{text} %{files}', '%{text} %{files} files', files.length), { + files: files.reduce((acc, val) => acc.concat(val.path), []).join(', '), + text, + }); +}; export const discardDraftButtonDisabled = state => state.commitMessage === '' || state.submitCommitLoading; @@ -29,14 +37,16 @@ export const branchName = (state, getters, rootState) => { export const preBuiltCommitMessage = (state, _, rootState) => { if (state.commitMessage) return state.commitMessage; - const files = (rootState.stagedFiles.length - ? rootState.stagedFiles - : rootState.changedFiles - ).reduce((acc, val) => acc.concat(val.path), []); + const files = rootState.stagedFiles.length ? rootState.stagedFiles : rootState.changedFiles; + const modifiedFiles = files.filter(f => !f.deleted); + const deletedFiles = files.filter(f => f.deleted); - return sprintf(n__('Update %{files}', 'Update %{files} files', files.length), { - files: files.join(', '), - }); + return [ + createTranslatedTextForFiles(modifiedFiles, __('Update')), + createTranslatedTextForFiles(deletedFiles, __('Deleted')), + ] + .filter(t => t) + .join('\n'); }; // prevent babel-plugin-rewire from generating an invalid default during karma tests diff --git a/app/assets/javascripts/ide/stores/mutation_types.js b/app/assets/javascripts/ide/stores/mutation_types.js index 8d6f9ccaf34..dae60f4d65a 100644 --- a/app/assets/javascripts/ide/stores/mutation_types.js +++ b/app/assets/javascripts/ide/stores/mutation_types.js @@ -76,3 +76,4 @@ export const RESET_OPEN_FILES = 'RESET_OPEN_FILES'; export const SET_ERROR_MESSAGE = 'SET_ERROR_MESSAGE'; export const OPEN_NEW_ENTRY_MODAL = 'OPEN_NEW_ENTRY_MODAL'; +export const DELETE_ENTRY = 'DELETE_ENTRY'; diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js index f8091f5b5e0..799c2f51e8d 100644 --- a/app/assets/javascripts/ide/stores/mutations.js +++ b/app/assets/javascripts/ide/stores/mutations.js @@ -1,3 +1,4 @@ +/* eslint-disable no-param-reassign */ import * as types from './mutation_types'; import projectMutations from './mutations/project'; import mergeRequestMutation from './mutations/merge_request'; @@ -171,6 +172,16 @@ export default { newEntryModal: { type, path }, }); }, + [types.DELETE_ENTRY](state, path) { + const entry = state.entries[path]; + const parent = entry.parentPath + ? state.entries[entry.parentPath] + : state.trees[`${state.currentProjectId}/${state.currentBranchId}`]; + + entry.deleted = true; + state.changedFiles = state.changedFiles.concat(entry); + parent.tree = parent.tree.filter(f => f.path !== entry.path); + }, ...projectMutations, ...mergeRequestMutation, ...fileMutations, diff --git a/app/assets/javascripts/ide/stores/mutations/file.js b/app/assets/javascripts/ide/stores/mutations/file.js index 46547820425..9a87d50d6d5 100644 --- a/app/assets/javascripts/ide/stores/mutations/file.js +++ b/app/assets/javascripts/ide/stores/mutations/file.js @@ -1,5 +1,6 @@ /* eslint-disable no-param-reassign */ import * as types from '../mutation_types'; +import { sortTree } from '../utils'; import { diffModes } from '../../constants'; export default { @@ -51,9 +52,17 @@ export default { }); }, [types.SET_FILE_RAW_DATA](state, { file, raw }) { + const openPendingFile = state.openFiles.find( + f => f.path === file.path && f.pending && !f.tempFile, + ); + Object.assign(state.entries[file.path], { raw, }); + + if (openPendingFile) { + openPendingFile.raw = raw; + } }, [types.SET_FILE_BASE_RAW_DATA](state, { file, baseRaw }) { Object.assign(state.entries[file.path], { @@ -109,11 +118,22 @@ export default { }, [types.DISCARD_FILE_CHANGES](state, path) { const stagedFile = state.stagedFiles.find(f => f.path === path); + const entry = state.entries[path]; + const { deleted } = entry; Object.assign(state.entries[path], { content: stagedFile ? stagedFile.content : state.entries[path].raw, changed: false, + deleted: false, }); + + if (deleted) { + const parent = entry.parentPath + ? state.entries[entry.parentPath] + : state.trees[`${state.currentProjectId}/${state.currentBranchId}`]; + + parent.tree = sortTree(parent.tree.concat(entry)); + } }, [types.ADD_FILE_TO_CHANGED](state, path) { Object.assign(state, { diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js index 9e6b86dd844..bf7ab93ff5e 100644 --- a/app/assets/javascripts/ide/stores/utils.js +++ b/app/assets/javascripts/ide/stores/utils.js @@ -46,6 +46,7 @@ export const dataStructure = () => ({ parentPath: null, lastOpenedAt: 0, mrChange: null, + deleted: false, }); export const decorateData = entity => { @@ -105,15 +106,37 @@ export const setPageTitle = title => { document.title = title; }; +export const commitActionForFile = file => { + if (file.deleted) { + return 'delete'; + } else if (file.tempFile) { + return 'create'; + } + + return 'update'; +}; + +export const getCommitFiles = (stagedFiles, deleteTree = false) => + stagedFiles.reduce((acc, file) => { + if ((file.deleted || deleteTree) && file.type === 'tree') { + return acc.concat(getCommitFiles(file.tree, true)); + } + + return acc.concat({ + ...file, + deleted: deleteTree || file.deleted, + }); + }, []); + export const createCommitPayload = ({ branch, getters, newBranch, state, rootState }) => ({ branch, commit_message: state.commitMessage || getters.preBuiltCommitMessage, - actions: rootState.stagedFiles.map(f => ({ - action: f.tempFile ? 'create' : 'update', + actions: getCommitFiles(rootState.stagedFiles).map(f => ({ + action: commitActionForFile(f), file_path: f.path, content: f.content, encoding: f.base64 ? 'base64' : 'text', - last_commit_id: newBranch ? undefined : f.lastCommitSha, + last_commit_id: newBranch || f.deleted ? undefined : f.lastCommitSha, })), start_branch: newBranch ? rootState.currentBranchId : undefined, }); diff --git a/app/assets/javascripts/ide/utils.js b/app/assets/javascripts/ide/utils.js new file mode 100644 index 00000000000..92b15cf232d --- /dev/null +++ b/app/assets/javascripts/ide/utils.js @@ -0,0 +1,12 @@ +import { commitItemIconMap } from './constants'; + +// eslint-disable-next-line import/prefer-default-export +export const getCommitIconMap = file => { + if (file.deleted) { + return commitItemIconMap.deleted; + } else if (file.tempFile) { + return commitItemIconMap.addition; + } + + return commitItemIconMap.modified; +}; diff --git a/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue b/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue index c32dc83da8e..14518f86dc7 100644 --- a/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue @@ -1,5 +1,6 @@ <script> import $ from 'jquery'; +import _ from 'underscore'; import JobNameComponent from './job_name_component.vue'; import JobComponent from './job_component.vue'; import tooltip from '../../../vue_shared/directives/tooltip'; @@ -46,7 +47,7 @@ export default { computed: { tooltipText() { - return `${this.job.name} - ${this.job.status.label}`; + return _.escape(`${this.job.name} - ${this.job.status.label}`); }, }, diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component.vue b/app/assets/javascripts/pipelines/components/graph/graph_component.vue index 4ec67f6c01b..1952dd453f4 100644 --- a/app/assets/javascripts/pipelines/components/graph/graph_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/graph_component.vue @@ -1,4 +1,5 @@ <script> +import _ from 'underscore'; import LoadingIcon from '~/vue_shared/components/loading_icon.vue'; import StageColumnComponent from './stage_column_component.vue'; @@ -26,7 +27,8 @@ export default { methods: { capitalizeStageName(name) { - return name.charAt(0).toUpperCase() + name.slice(1); + const escapedName = _.escape(name); + return escapedName.charAt(0).toUpperCase() + escapedName.slice(1); }, isFirstColumn(index) { diff --git a/app/assets/javascripts/pipelines/components/graph/job_component.vue b/app/assets/javascripts/pipelines/components/graph/job_component.vue index 8af984ef91a..84a3d58b770 100644 --- a/app/assets/javascripts/pipelines/components/graph/job_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/job_component.vue @@ -1,4 +1,5 @@ <script> +import _ from 'underscore'; import ActionComponent from './action_component.vue'; import JobNameComponent from './job_name_component.vue'; import tooltip from '../../../vue_shared/directives/tooltip'; @@ -61,7 +62,7 @@ export default { const textBuilder = []; if (this.job.name) { - textBuilder.push(this.job.name); + textBuilder.push(_.escape(this.job.name)); } if (this.job.name && this.status.tooltip) { @@ -69,7 +70,7 @@ export default { } if (this.status.tooltip) { - textBuilder.push(`${this.job.status.tooltip}`); + textBuilder.push(this.job.status.tooltip); } return textBuilder.join(' '); diff --git a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue index 2c728582b7c..e7b2de52f76 100644 --- a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue +++ b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue @@ -1,4 +1,5 @@ <script> +import _ from 'underscore'; import JobComponent from './job_component.vue'; import DropdownJobComponent from './dropdown_job_component.vue'; @@ -37,7 +38,7 @@ export default { }, jobId(job) { - return `ci-badge-${job.name}`; + return `ci-badge-${_.escape(job.name)}`; }, buildConnnectorClass(index) { diff --git a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue index d335c3f55af..dc599e1b9fc 100644 --- a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue +++ b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue @@ -42,11 +42,14 @@ export default { return this.timeEstimate - this.timeSpent; }, timeRemainingPercent() { - return `${Math.floor((this.timeSpent / this.timeEstimate) * 100)}%`; + return Math.floor((this.timeSpent / this.timeEstimate) * 100); }, timeRemainingStatusClass() { return this.timeEstimate >= this.timeSpent ? 'within_estimate' : 'over_estimate'; }, + progressBarVariant() { + return this.timeRemainingPercent > 100 ? 'danger' : 'primary'; + }, }, }; </script> @@ -62,16 +65,10 @@ export default { data-placement="top" role="timeRemainingDisplay" > - <div - :aria-valuenow="timeRemainingPercent" - class="meter-container" - > - <div - :style="{ width: timeRemainingPercent }" - class="meter-fill" - > - </div> - </div> + <gl-progress-bar + :value="timeRemainingPercent" + :variant="progressBarVariant" + /> <div class="compare-display-container"> <div class="compare-display float-left"> <span class="compare-label"> diff --git a/app/assets/javascripts/vue_shared/components/bar_chart.vue b/app/assets/javascripts/vue_shared/components/bar_chart.vue new file mode 100644 index 00000000000..3ced4eb691a --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/bar_chart.vue @@ -0,0 +1,391 @@ +<script> +import * as d3 from 'd3'; +import tooltip from '../directives/tooltip'; +import Icon from './icon.vue'; +import SvgGradient from './svg_gradient.vue'; +import { + GRADIENT_COLORS, + GRADIENT_OPACITY, + INVERSE_GRADIENT_COLORS, + INVERSE_GRADIENT_OPACITY, +} from './bar_chart_constants'; + +/** + * Renders a bar chart that can be dragged(scrolled) when the number + * of elements to renders surpasses that of the available viewport space + * while keeping even padding and a width of 24px (customizable) + * + * It can render data with the following format: + * graphData: [{ + * name: 'element' // x domain data + * value: 1 // y domain data + * }] + * + * Used in: + * - Contribution analytics - all of the rows describing pushes, merge requests and issues + */ + +export default { + directives: { + tooltip, + }, + components: { + Icon, + SvgGradient, + }, + props: { + graphData: { + type: Array, + required: true, + }, + barWidth: { + type: Number, + required: false, + default: 24, + }, + yAxisLabel: { + type: String, + required: true, + }, + }, + data() { + return { + minX: -40, + minY: 0, + vbWidth: 0, + vbHeight: 0, + vpWidth: 0, + vpHeight: 350, + preserveAspectRatioType: 'xMidYMid meet', + containerMargin: { + leftRight: 30, + }, + viewBoxMargin: { + topBottom: 150, + }, + panX: 0, + xScale: {}, + yScale: {}, + zoom: {}, + bars: {}, + xGraphRange: 0, + isLoading: true, + paddingThreshold: 50, + showScrollIndicator: false, + showLeftScrollIndicator: false, + isGrabbed: false, + isPanAvailable: false, + gradientColors: GRADIENT_COLORS, + gradientOpacity: GRADIENT_OPACITY, + inverseGradientColors: INVERSE_GRADIENT_COLORS, + inverseGradientOpacity: INVERSE_GRADIENT_OPACITY, + maxTextWidth: 72, + rectYAxisLabelDims: {}, + xAxisTextElements: {}, + yAxisRectTransformPadding: 20, + yAxisTextTransformPadding: 10, + yAxisTextRotation: 90, + }; + }, + computed: { + svgViewBox() { + return `${this.minX} ${this.minY} ${this.vbWidth} ${this.vbHeight}`; + }, + xAxisLocation() { + return `translate(${this.panX}, ${this.vbHeight})`; + }, + barTranslationTransform() { + return `translate(${this.panX}, 0)`; + }, + scrollIndicatorTransform() { + return `translate(${this.vbWidth - 80}, 0)`; + }, + activateGrabCursor() { + return { + 'svg-graph-container-with-grab': this.isPanAvailable, + 'svg-graph-container-grabbed': this.isPanAvailable && this.isGrabbed, + }; + }, + yAxisLabelRectTransform() { + const rectWidth = + this.rectYAxisLabelDims.height != null ? this.rectYAxisLabelDims.height / 2 : 0; + const yCoord = this.vbHeight / 2 - rectWidth; + + return `translate(${this.minX - this.yAxisRectTransformPadding}, ${yCoord})`; + }, + yAxisLabelTextTransform() { + const rectWidth = + this.rectYAxisLabelDims.height != null ? this.rectYAxisLabelDims.height / 2 : 0; + const yCoord = this.vbHeight / 2 + rectWidth - 5; + + return `translate(${this.minX + this.yAxisTextTransformPadding}, ${yCoord}) rotate(-${this.yAxisTextRotation})`; + }, + }, + mounted() { + if (!this.allValuesEmpty) { + this.draw(); + } + }, + methods: { + draw() { + // update viewport + this.vpWidth = this.$refs.svgContainer.clientWidth - this.containerMargin.leftRight; + // update viewbox + this.vbWidth = this.vpWidth; + this.vbHeight = this.vpHeight - this.viewBoxMargin.topBottom; + let padding = 0; + if (this.graphData.length * this.barWidth > this.vbWidth) { + this.xGraphRange = this.graphData.length * this.barWidth; + padding = this.calculatePadding(this.barWidth); + this.showScrollIndicator = true; + this.isPanAvailable = true; + } else { + this.xGraphRange = this.vbWidth - Math.abs(this.minX); + } + + this.xScale = d3 + .scaleBand() + .range([0, this.xGraphRange]) + .round(true) + .paddingInner(padding); + this.yScale = d3.scaleLinear().rangeRound([this.vbHeight, 0]); + + this.xScale.domain(this.graphData.map(d => d.name)); + this.yScale.domain([0, d3.max(this.graphData.map(d => d.value))]); + + // Zoom/Panning Function + this.zoom = d3 + .zoom() + .translateExtent([[0, 0], [this.xGraphRange, this.vbHeight]]) + .on('zoom', this.panGraph) + .on('end', this.removeGrabStyling); + + const xAxis = d3.axisBottom().scale(this.xScale); + + const yAxis = d3 + .axisLeft() + .scale(this.yScale) + .ticks(4); + + const renderedXAxis = d3 + .select(this.$refs.baseSvg) + .select('.x-axis') + .call(xAxis); + + this.xAxisTextElements = this.$refs.xAxis.querySelectorAll('text'); + + renderedXAxis.select('.domain').remove(); + + renderedXAxis + .selectAll('text') + .style('text-anchor', 'end') + .attr('dx', '-.3em') + .attr('dy', '-.95em') + .attr('class', 'tick-text') + .attr('transform', 'rotate(-90)'); + + renderedXAxis.selectAll('line').remove(); + + const { maxTextWidth } = this; + renderedXAxis.selectAll('text').each(function formatText() { + const axisText = d3.select(this); + let textLength = axisText.node().getComputedTextLength(); + let textContent = axisText.text(); + while (textLength > maxTextWidth && textContent.length > 0) { + textContent = textContent.slice(0, -1); + axisText.text(`${textContent}...`); + textLength = axisText.node().getComputedTextLength(); + } + }); + + const width = this.vbWidth; + + const renderedYAxis = d3 + .select(this.$refs.baseSvg) + .select('.y-axis') + .call(yAxis); + + renderedYAxis.selectAll('.tick').each(function createTickLines(d, i) { + if (i > 0) { + d3 + .select(this) + .select('line') + .attr('x2', width) + .attr('class', 'axis-tick'); + } + }); + + // Add the panning capabilities + if (this.isPanAvailable) { + d3 + .select(this.$refs.baseSvg) + .call(this.zoom) + .on('wheel.zoom', null); // This disables the pan of the graph with the scroll of the mouse wheel + } + + this.isLoading = false; + // Update the yAxisLabel coordinates + const labelDims = this.$refs.yAxisLabel.getBBox(); + this.rectYAxisLabelDims = { + height: labelDims.width + 10, + }; + }, + panGraph() { + const allowedRightScroll = this.xGraphRange - this.vbWidth - this.paddingThreshold; + const graphMaxPan = Math.abs(d3.event.transform.x) < allowedRightScroll; + this.isGrabbed = true; + this.panX = d3.event.transform.x; + + if (d3.event.transform.x === 0) { + this.showLeftScrollIndicator = false; + } else { + this.showLeftScrollIndicator = true; + this.showScrollIndicator = true; + } + + if (!graphMaxPan) { + this.panX = -1 * (this.xGraphRange - this.vbWidth + this.paddingThreshold); + this.showScrollIndicator = false; + } + }, + setTooltipTitle(data) { + return data !== null ? `${data.name}: ${data.value}` : ''; + }, + calculatePadding(desiredBarWidth) { + const widthWithMargin = this.vbWidth - Math.abs(this.minX); + const dividend = widthWithMargin - this.graphData.length * desiredBarWidth; + const divisor = widthWithMargin - desiredBarWidth; + + return dividend / divisor; + }, + removeGrabStyling() { + this.isGrabbed = false; + }, + barHoveredIn(index) { + this.xAxisTextElements[index].classList.add('x-axis-text'); + }, + barHoveredOut(index) { + this.xAxisTextElements[index].classList.remove('x-axis-text'); + }, + }, +}; +</script> +<template> + <div + ref="svgContainer" + :class="activateGrabCursor" + class="svg-graph-container" + > + <svg + ref="baseSvg" + :width="vpWidth" + :height="vpHeight" + :viewBox="svgViewBox" + :preserveAspectRatio="preserveAspectRatioType"> + <g + ref="xAxis" + :transform="xAxisLocation" + class="x-axis" + /> + <g v-if="!isLoading"> + <template + v-for="(data, index) in graphData"> + <rect + v-tooltip + :key="index" + :width="xScale.bandwidth()" + :x="xScale(data.name)" + :y="yScale(data.value)" + :height="vbHeight - yScale(data.value)" + :transform="barTranslationTransform" + :title="setTooltipTitle(data)" + class="bar-rect" + data-placement="top" + @mouseover="barHoveredIn(index)" + @mouseout="barHoveredOut(index)" + /> + </template> + </g> + <rect + :height="vbHeight + 100" + transform="translate(-100, -5)" + width="100" + fill="#fff" + /> + <g class="y-axis-label"> + <line + :x1="0" + :x2="0" + :y1="0" + :y2="vbHeight" + transform="translate(-35, 0)" + stroke="black" + /> + <!--Get text length and change the height of this rect accordingly--> + <rect + :height="rectYAxisLabelDims.height" + :transform="yAxisLabelRectTransform" + :width="30" + fill="#fff" + /> + <text + ref="yAxisLabel" + :transform="yAxisLabelTextTransform" + > + {{ yAxisLabel }} + </text> + </g> + <g + class="y-axis" + /> + <g v-if="showScrollIndicator"> + <rect + :height="vbHeight + 100" + :transform="`translate(${vpWidth - 60}, -5)`" + width="40" + fill="#fff" + /> + <icon + :x="vpWidth - 50" + :y="vbHeight / 2" + :width="14" + :height="14" + name="chevron-right" + class="animate-flicker" + /> + </g> + <!--The line that shows up when the data elements surpass the available width --> + <g + v-if="showScrollIndicator" + :transform="scrollIndicatorTransform"> + <rect + :height="vbHeight" + x="0" + y="0" + width="20" + fill="url(#shadow-gradient)" + /> + </g> + <!--Left scroll indicator--> + <g + v-if="showLeftScrollIndicator" + transform="translate(0, 0)"> + <rect + :height="vbHeight" + x="0" + y="0" + width="20" + fill="url(#left-shadow-gradient)" + /> + </g> + <svg-gradient + :colors="gradientColors" + :opacity="gradientOpacity" + identifier-name="shadow-gradient"/> + <svg-gradient + :colors="inverseGradientColors" + :opacity="inverseGradientOpacity" + identifier-name="left-shadow-gradient"/> + </svg> + </div> +</template> diff --git a/app/assets/javascripts/vue_shared/components/bar_chart_constants.js b/app/assets/javascripts/vue_shared/components/bar_chart_constants.js new file mode 100644 index 00000000000..6957b112da6 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/bar_chart_constants.js @@ -0,0 +1,4 @@ +export const GRADIENT_COLORS = ['#000', '#a7a7a7']; +export const GRADIENT_OPACITY = ['0', '0.4']; +export const INVERSE_GRADIENT_COLORS = ['#a7a7a7', '#000']; +export const INVERSE_GRADIENT_OPACITY = ['0.4', '0']; diff --git a/app/assets/javascripts/vue_shared/components/panel_resizer.vue b/app/assets/javascripts/vue_shared/components/panel_resizer.vue index 8c2dcc2d902..7947ae1e4da 100644 --- a/app/assets/javascripts/vue_shared/components/panel_resizer.vue +++ b/app/assets/javascripts/vue_shared/components/panel_resizer.vue @@ -32,7 +32,7 @@ }, computed: { className() { - return `drag${this.side}`; + return `drag-${this.side}`; }, cursorStyle() { if (this.enabled) { @@ -44,8 +44,15 @@ methods: { resetSize(e) { e.preventDefault(); + this.$emit('resize-start', this.size); + this.size = this.startSize; this.$emit('update:size', this.size); + + // End resizing on next tick so that listeners can react to DOM changes + this.$nextTick(() => { + this.$emit('resize-end', this.size); + }); }, startDrag(e) { if (this.enabled) { @@ -84,7 +91,7 @@ <div :class="className" :style="cursorStyle" - class="dragHandle" + class="drag-handle" @mousedown="startDrag" @dblclick="resetSize" ></div> diff --git a/app/assets/javascripts/vue_shared/components/reports/constants.js b/app/assets/javascripts/vue_shared/components/reports/constants.js new file mode 100644 index 00000000000..dbde648bfdb --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/reports/constants.js @@ -0,0 +1,3 @@ +export const STATUS_FAILED = 'failed'; +export const STATUS_SUCCESS = 'success'; +export const STATUS_NEUTRAL = 'neutral'; diff --git a/app/assets/javascripts/vue_shared/components/reports/issue_body.js b/app/assets/javascripts/vue_shared/components/reports/issue_body.js new file mode 100644 index 00000000000..f2141e519da --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/reports/issue_body.js @@ -0,0 +1,3 @@ +export const components = {}; + +export const componentNames = {}; diff --git a/app/assets/javascripts/vue_shared/components/reports/issue_status_icon.vue b/app/assets/javascripts/vue_shared/components/reports/issue_status_icon.vue new file mode 100644 index 00000000000..f8189117ac3 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/reports/issue_status_icon.vue @@ -0,0 +1,58 @@ +<script> +import Icon from '~/vue_shared/components/icon.vue'; + +import { + STATUS_FAILED, + STATUS_NEUTRAL, + STATUS_SUCCESS, +} from '~/vue_shared/components/reports/constants'; + +export default { + name: 'IssueStatusIcon', + components: { + Icon, + }, + props: { + // failed || success + status: { + type: String, + required: true, + }, + }, + computed: { + iconName() { + if (this.isStatusFailed) { + return 'status_failed_borderless'; + } else if (this.isStatusSuccess) { + return 'status_success_borderless'; + } + + return 'status_created_borderless'; + }, + isStatusFailed() { + return this.status === STATUS_FAILED; + }, + isStatusSuccess() { + return this.status === STATUS_SUCCESS; + }, + isStatusNeutral() { + return this.status === STATUS_NEUTRAL; + }, + }, +}; +</script> +<template> + <div + :class="{ + failed: isStatusFailed, + success: isStatusSuccess, + neutral: isStatusNeutral, + }" + class="report-block-list-icon" + > + <icon + :name="iconName" + :size="32" + /> + </div> +</template> diff --git a/app/assets/javascripts/vue_shared/components/reports/issues_list.vue b/app/assets/javascripts/vue_shared/components/reports/issues_list.vue index e1e03e39ee0..c01f77c2509 100644 --- a/app/assets/javascripts/vue_shared/components/reports/issues_list.vue +++ b/app/assets/javascripts/vue_shared/components/reports/issues_list.vue @@ -1,5 +1,10 @@ <script> -import IssuesBlock from './report_issues.vue'; +import IssuesBlock from '~/vue_shared/components/reports/report_issues.vue'; +import { + STATUS_SUCCESS, + STATUS_FAILED, + STATUS_NEUTRAL, +} from '~/vue_shared/components/reports/constants'; /** * Renders block of issues @@ -9,6 +14,9 @@ export default { components: { IssuesBlock, }, + success: STATUS_SUCCESS, + failed: STATUS_FAILED, + neutral: STATUS_NEUTRAL, props: { unresolvedIssues: { type: Array, @@ -25,29 +33,10 @@ export default { required: false, default: () => [], }, - allIssues: { - type: Array, - required: false, - default: () => [], - }, - type: { + component: { type: String, - required: true, - }, - }, - data() { - return { - isFullReportVisible: false, - }; - }, - computed: { - unresolvedIssuesStatus() { - return this.type === 'license' ? 'neutral' : 'failed'; - }, - }, - methods: { - openFullReport() { - this.isFullReportVisible = true; + required: false, + default: '', }, }, }; @@ -57,43 +46,26 @@ export default { <issues-block v-if="unresolvedIssues.length" - :type="type" - :status="unresolvedIssuesStatus" + :component="component" :issues="unresolvedIssues" + :status="$options.failed" class="js-mr-code-new-issues" /> <issues-block - v-if="isFullReportVisible" - :type="type" - :issues="allIssues" - class="js-mr-code-all-issues" - status="failed" - /> - - <issues-block v-if="neutralIssues.length" - :type="type" + :component="component" :issues="neutralIssues" + :status="$options.neutral" class="js-mr-code-non-issues" - status="neutral" /> <issues-block v-if="resolvedIssues.length" - :type="type" + :component="component" :issues="resolvedIssues" + :status="$options.success" class="js-mr-code-resolved-issues" - status="success" /> - - <button - v-if="allIssues.length && !isFullReportVisible" - type="button" - class="btn-link btn-blank prepend-left-10 js-expand-full-list break-link" - @click="openFullReport" - > - {{ s__("ciReport|Show complete code vulnerabilities report") }} - </button> </div> </template> diff --git a/app/assets/javascripts/vue_shared/components/reports/report_issues.vue b/app/assets/javascripts/vue_shared/components/reports/report_issues.vue index ecffb02a3a0..2d1f3d82234 100644 --- a/app/assets/javascripts/vue_shared/components/reports/report_issues.vue +++ b/app/assets/javascripts/vue_shared/components/reports/report_issues.vue @@ -1,19 +1,23 @@ <script> -import Icon from '~/vue_shared/components/icon.vue'; +import IssueStatusIcon from '~/vue_shared/components/reports/issue_status_icon.vue'; +import { components, componentNames } from '~/vue_shared/components/reports/issue_body'; export default { name: 'ReportIssues', components: { - Icon, + IssueStatusIcon, + ...components, }, props: { issues: { type: Array, required: true, }, - type: { + component: { type: String, - required: true, + required: false, + default: '', + validator: value => value === '' || Object.values(componentNames).includes(value), }, // failed || success status: { @@ -21,26 +25,6 @@ export default { required: true, }, }, - computed: { - iconName() { - if (this.isStatusFailed) { - return 'status_failed_borderless'; - } else if (this.isStatusSuccess) { - return 'status_success_borderless'; - } - - return 'status_created_borderless'; - }, - isStatusFailed() { - return this.status === 'failed'; - }, - isStatusSuccess() { - return this.status === 'success'; - }, - isStatusNeutral() { - return this.status === 'neutral'; - }, - }, }; </script> <template> @@ -52,20 +36,17 @@ export default { :key="index" class="report-block-list-issue" > - <div - :class="{ - failed: isStatusFailed, - success: isStatusSuccess, - neutral: isStatusNeutral, - }" - class="report-block-list-icon append-right-5" - > - <icon - :name="iconName" - :size="32" - /> - </div> + <issue-status-icon + :status="issue.status || status" + class="append-right-5" + /> + <component + v-if="component" + :is="component" + :issue="issue" + :status="issue.status || status" + /> </li> </ul> </div> diff --git a/app/assets/javascripts/vue_shared/components/reports/report_section.vue b/app/assets/javascripts/vue_shared/components/reports/report_section.vue index d383ed99a0c..0124d8b5bcc 100644 --- a/app/assets/javascripts/vue_shared/components/reports/report_section.vue +++ b/app/assets/javascripts/vue_shared/components/reports/report_section.vue @@ -21,7 +21,7 @@ export default { required: false, default: false, }, - type: { + component: { type: String, required: false, default: '', @@ -59,11 +59,6 @@ export default { required: false, default: () => [], }, - allIssues: { - type: Array, - required: false, - default: () => [], - }, infoText: { type: [String, Boolean], required: false, @@ -142,18 +137,10 @@ export default { </script> <template> <section class="media-section"> - <div - class="media" - > - <status-icon - :status="statusIconName" - /> - <div - class="media-body space-children d-flex" - > - <span - class="js-code-text code-text" - > + <div class="media"> + <status-icon :status="statusIconName" /> + <div class="media-body space-children d-flex flex-align-self-center"> + <span class="js-code-text code-text"> {{ headerText }} <popover @@ -163,10 +150,12 @@ export default { /> </span> + <slot name="actionButtons"></slot> + <button v-if="isCollapsible" type="button" - class="js-collapse-btn btn bt-default float-right btn-sm" + class="js-collapse-btn btn float-right btn-sm" @click="toggleCollapsed" > {{ collapseText }} @@ -183,8 +172,8 @@ export default { <issues-list :unresolved-issues="unresolvedIssues" :resolved-issues="resolvedIssues" - :all-issues="allIssues" - :type="type" + :neutral-issues="neutralIssues" + :component="component" /> </slot> </div> diff --git a/app/assets/javascripts/vue_shared/components/svg_gradient.vue b/app/assets/javascripts/vue_shared/components/svg_gradient.vue new file mode 100644 index 00000000000..b61a1befcd6 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/svg_gradient.vue @@ -0,0 +1,37 @@ +<script> +export default { + props: { + colors: { + type: Array, + required: true, + }, + opacity: { + type: Array, + required: true, + }, + identifierName: { + type: String, + required: true, + }, + }, +}; +</script> +<template> + <svg + height="0" + width="0"> + <defs> + <linearGradient + :id="identifierName"> + <stop + :stop-color="colors[0]" + :stop-opacity="opacity[0]" + offset="0%" /> + <stop + :stop-color="colors[1]" + :stop-opacity="opacity[1]" + offset="100%" /> + </linearGradient> + </defs> + </svg> +</template> diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 637587de597..2d6dba52801 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -370,11 +370,14 @@ img.emoji { margin-right: 10px; } -.alert, -.progress { +.alert { margin-bottom: $gl-padding; } +.progress { + height: 4px; +} + .project-item-select-holder { display: inline-block; position: relative; diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 2097bcebf69..e7e13d35d8e 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -567,9 +567,6 @@ border-bottom: 1px solid $white-normal; .mx-auto { - margin: 8px 0; - text-align: center; - .tanuki-logo, img { height: 36px; diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 56307777a72..a2789021ab4 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -179,6 +179,10 @@ font-weight: inherit; } + a > code { + color: $gl-link-color; + } + dd { margin-left: $gl-padding; } diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index efc54196b75..08755b4b545 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -835,3 +835,5 @@ $font-family-monospace: $monospace-font; $input-line-height: 20px; $btn-line-height: 20px; $table-accent-bg: $gray-light; +$card-border-color: $border-color; +$card-cap-bg: $gray-light; diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/page_bundles/ide.scss index 2d76f0ce004..442aef124d3 100644 --- a/app/assets/stylesheets/pages/repo.scss +++ b/app/assets/stylesheets/page_bundles/ide.scss @@ -1,3 +1,6 @@ +@import 'framework/variables'; +@import 'framework/mixins'; + .project-refs-form, .project-refs-target-form { display: inline-block; @@ -74,6 +77,7 @@ .ide-file-icon-holder { display: flex; align-items: center; + color: $theme-gray-700; } .ide-file-changed-icon { @@ -161,12 +165,23 @@ background-color: $white-light; border-bottom-color: $white-light; } + + &:not(.disabled) { + .multi-file-tab { + cursor: pointer; + } + } + + &.disabled { + .multi-file-tab-close { + cursor: default; + } + } } } .multi-file-tab { @include str-truncated(141px); - cursor: pointer; svg { vertical-align: middle; @@ -241,6 +256,38 @@ } } + .is-deleted { + .editor.modified { + .margin-view-overlays, + .lines-content, + .decorationsOverviewRuler { + // !important to override monaco inline styles + display: none !important; + } + } + + .diffOverviewRuler.modified { + // !important to override monaco inline styles + display: none !important; + } + } + + .is-added { + .editor.original { + .margin-view-overlays, + .lines-content, + .decorationsOverviewRuler { + // !important to override monaco inline styles + display: none !important; + } + } + + .diffOverviewRuler.original { + // !important to override monaco inline styles + display: none !important; + } + } + .monaco-diff-editor.vs { .editor.modified { box-shadow: none; @@ -557,16 +604,21 @@ } } -.multi-file-addition, -.multi-file-addition-solid { +.ide-file-addition, +.ide-file-addition-solid { color: $green-500; } -.multi-file-modified, -.multi-file-modified-solid { +.ide-file-modified, +.ide-file-modified-solid { color: $orange-500; } +.ide-file-deletion, +.ide-file-deletion-solid { + color: $red-500; +} + .multi-file-commit-list-collapsed { display: flex; flex-direction: column; @@ -781,18 +833,21 @@ } } -.dragHandle { +.drag-handle { position: absolute; top: 0; bottom: 0; - width: 1px; - background-color: $white-dark; + width: 4px; + + &:hover { + background-color: $white-normal; + } - &.dragright { + &.drag-right { right: 0; } - &.dragleft { + &.drag-left { left: 0; } } @@ -1014,6 +1069,10 @@ .ide-new-btn { margin-left: auto; } + + button { + color: $gl-text-color; + } } .ide-sidebar-branch-title { diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index f030189af06..e5c38a20bf0 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -270,6 +270,7 @@ .block { width: 100%; + word-break: break-word; &:last-child { border-bottom: 1px solid $border-gray-normal; diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index b616357bb8d..591e21243ed 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -16,6 +16,7 @@ svg { vertical-align: middle; + top: -1px; } } diff --git a/app/assets/stylesheets/pages/graph.scss b/app/assets/stylesheets/pages/graph.scss index 84da9180f93..49d8a5d959b 100644 --- a/app/assets/stylesheets/pages/graph.scss +++ b/app/assets/stylesheets/pages/graph.scss @@ -31,3 +31,61 @@ color: $gl-text-red; } } + +.svg-graph-container { + width: 100%; + + .axis-tick { + opacity: 0.4; + } + + .tick-text { + fill: $gl-text-color-secondary; + } + + .x-axis-text { + fill: $theme-gray-900; + } + + .bar-rect { + fill: rgba($blue-500, 0.1); + stroke: $blue-500; + } + + .bar-rect:hover { + fill: rgba($blue-700, 0.3); + } + + .y-axis-label { + line { + stroke: $stat-graph-axis-fill; + } + + text { + font-weight: bold; + font-size: 12px; + fill: $theme-gray-800; + } + } +} + +.svg-graph-container-with-grab { + cursor: grab; + cursor: -webkit-grab; +} + +.svg-graph-container-grabbed { + cursor: grabbing; + cursor: -webkit-grabbing; +} + +@keyframes flickerAnimation { + 0% { opacity: 1; } + 50% { opacity: 0; } + 100% { opacity: 1; } +} + +.animate-flicker { + animation: flickerAnimation 1.5s infinite; + fill: $theme-gray-500; +} diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 797b106de23..d5ae2b673d9 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -834,17 +834,7 @@ } .compare-meter { - &.within_estimate { - .meter-fill { - background: $gl-primary; - } - } - &.over_estimate { - .meter-fill { - background: $red-500; - } - .time-remaining, .compare-value.spent { color: $red-500; @@ -852,18 +842,6 @@ } } - .meter-container { - background: $border-gray-light; - border-radius: 3px; - - .meter-fill { - max-width: 100%; - height: 5px; - border-radius: 3px; - background: $gl-primary; - } - } - .compare-display-container { display: flex; justify-content: space-between; diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index c1b1d2e028d..8a4a2caa6c9 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -237,7 +237,7 @@ } .login-page-broadcast { - margin-top: 50px; + margin-top: 40px; } .navless-container { diff --git a/app/assets/stylesheets/snippets.scss b/app/assets/stylesheets/snippets.scss index 64110f9c3a0..bd777c66b56 100644 --- a/app/assets/stylesheets/snippets.scss +++ b/app/assets/stylesheets/snippets.scss @@ -22,8 +22,8 @@ height: 16px; background-size: cover; - &.gl-snippet-icon-doc_code { background-position: 0 0; } - &.gl-snippet-icon-doc_text { background-position: 0 -16px; } + &.gl-snippet-icon-doc-code { background-position: 0 0; } + &.gl-snippet-icon-doc-text { background-position: 0 -16px; } &.gl-snippet-icon-download { background-position: 0 -32px; } } diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb index 9e495061f4e..36faea8056e 100644 --- a/app/controllers/projects/commits_controller.rb +++ b/app/controllers/projects/commits_controller.rb @@ -4,13 +4,17 @@ class Projects::CommitsController < Projects::ApplicationController include ExtractsPath include RendersCommits - before_action :whitelist_query_limiting + before_action :whitelist_query_limiting, except: :commits_root before_action :require_non_empty_project - before_action :assign_ref_vars + before_action :assign_ref_vars, except: :commits_root before_action :authorize_download_code! - before_action :set_commits + before_action :set_commits, except: :commits_root before_action :set_request_format, only: :show + def commits_root + redirect_to project_commits_path(@project, @project.default_branch) + end + def show @merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened .find_by(source_project: @project, source_branch: @ref, target_branch: @repository.root_ref) diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb index 21d3c918581..ce03b2d8d1d 100644 --- a/app/controllers/projects/labels_controller.rb +++ b/app/controllers/projects/labels_controller.rb @@ -112,7 +112,7 @@ class Projects::LabelsController < Projects::ApplicationController begin return render_404 unless promote_service.execute(@label) - flash[:notice] = "#{@label.title} promoted to <a href=\"#{group_labels_path(@project.group)}\">group label</a>.".html_safe + flash[:notice] = flash_notice_for(@label, @project.group) respond_to do |format| format.html do redirect_to(project_labels_path(@project), status: :see_other) @@ -135,6 +135,15 @@ class Projects::LabelsController < Projects::ApplicationController end end + def flash_notice_for(label, group) + notice = ''.html_safe + notice << label.title + notice << ' promoted to ' + notice << view_context.link_to('<u>group label</u>'.html_safe, group_labels_path(group)) + notice << '.' + notice + end + protected def label_params diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index 5e86ec93f34..b9b3dcd5a85 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -76,8 +76,8 @@ class Projects::MilestonesController < Projects::ApplicationController def promote promoted_milestone = Milestones::PromoteService.new(project, current_user).execute(milestone) + flash[:notice] = flash_notice_for(promoted_milestone, project.group) - flash[:notice] = "#{milestone.title} promoted to <a href=\"#{group_milestone_path(project.group, promoted_milestone.iid)}\"><u>group milestone</u></a>.".html_safe respond_to do |format| format.html do redirect_to project_milestones_path(project) @@ -90,6 +90,15 @@ class Projects::MilestonesController < Projects::ApplicationController redirect_to milestone, alert: error.message end + def flash_notice_for(milestone, group) + notice = ''.html_safe + notice << milestone.title + notice << ' promoted to ' + notice << view_context.link_to('<u>group milestone</u>'.html_safe, group_milestone_path(group, milestone.iid)) + notice << '.' + notice + end + def destroy return access_denied! unless can?(current_user, :admin_milestone, @project) diff --git a/app/finders/admin/projects_finder.rb b/app/finders/admin/projects_finder.rb index 53b77f5fed9..543bf1a1415 100644 --- a/app/finders/admin/projects_finder.rb +++ b/app/finders/admin/projects_finder.rb @@ -7,7 +7,7 @@ class Admin::ProjectsFinder end def execute - items = Project.without_deleted.with_statistics + items = Project.without_deleted.with_statistics.with_route items = by_namespace_id(items) items = by_visibilty_level(items) items = by_with_push(items) @@ -16,7 +16,7 @@ class Admin::ProjectsFinder items = by_archived(items) items = by_personal(items) items = by_name(items) - items = items.includes(namespace: [:owner]) + items = items.includes(namespace: [:owner, :route]) sort(items).page(params[:page]) end diff --git a/app/graphql/gitlab_schema.rb b/app/graphql/gitlab_schema.rb index d9f9129d08a..8755a1a62e7 100644 --- a/app/graphql/gitlab_schema.rb +++ b/app/graphql/gitlab_schema.rb @@ -7,5 +7,5 @@ class GitlabSchema < GraphQL::Schema query(Types::QueryType) default_max_page_size 100 - # mutation(Types::MutationType) + mutation(Types::MutationType) end diff --git a/app/graphql/mutations/base_mutation.rb b/app/graphql/mutations/base_mutation.rb new file mode 100644 index 00000000000..eb03dfe1624 --- /dev/null +++ b/app/graphql/mutations/base_mutation.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Mutations + class BaseMutation < GraphQL::Schema::RelayClassicMutation + field :errors, [GraphQL::STRING_TYPE], + null: false, + description: "Reasons why the mutation failed." + + def current_user + context[:current_user] + end + end +end diff --git a/app/graphql/mutations/concerns/mutations/resolves_project.rb b/app/graphql/mutations/concerns/mutations/resolves_project.rb new file mode 100644 index 00000000000..0dd1f264a52 --- /dev/null +++ b/app/graphql/mutations/concerns/mutations/resolves_project.rb @@ -0,0 +1,13 @@ +module Mutations + module ResolvesProject + extend ActiveSupport::Concern + + def resolve_project(full_path:) + resolver.resolve(full_path: full_path) + end + + def resolver + Resolvers::ProjectResolver.new(object: nil, context: context) + end + end +end diff --git a/app/graphql/mutations/merge_requests/base.rb b/app/graphql/mutations/merge_requests/base.rb new file mode 100644 index 00000000000..2149e72e2df --- /dev/null +++ b/app/graphql/mutations/merge_requests/base.rb @@ -0,0 +1,32 @@ +module Mutations + module MergeRequests + class Base < BaseMutation + include Gitlab::Graphql::Authorize::AuthorizeResource + include Mutations::ResolvesProject + + argument :project_path, GraphQL::ID_TYPE, + required: true, + description: "The project the merge request to mutate is in" + + argument :iid, GraphQL::ID_TYPE, + required: true, + description: "The iid of the merge request to mutate" + + field :merge_request, + Types::MergeRequestType, + null: true, + description: "The merge request after mutation" + + authorize :update_merge_request + + private + + def find_object(project_path:, iid:) + project = resolve_project(full_path: project_path) + resolver = Resolvers::MergeRequestResolver.new(object: project, context: context) + + resolver.resolve(iid: iid) + end + end + end +end diff --git a/app/graphql/mutations/merge_requests/set_wip.rb b/app/graphql/mutations/merge_requests/set_wip.rb new file mode 100644 index 00000000000..a2aa0c84ee4 --- /dev/null +++ b/app/graphql/mutations/merge_requests/set_wip.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Mutations + module MergeRequests + class SetWip < Base + graphql_name 'MergeRequestSetWip' + + argument :wip, + GraphQL::BOOLEAN_TYPE, + required: true, + description: <<~DESC + Whether or not to set the merge request as a WIP. + DESC + + def resolve(project_path:, iid:, wip: nil) + merge_request = authorized_find!(project_path: project_path, iid: iid) + project = merge_request.project + + ::MergeRequests::UpdateService.new(project, current_user, wip_event: wip_event(merge_request, wip)) + .execute(merge_request) + + { + merge_request: merge_request, + errors: merge_request.errors.full_messages + } + end + + private + + def wip_event(merge_request, wip) + wip ? 'wip' : 'unwip' + end + end + end +end diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb index 06ed91c1658..2b4ef299296 100644 --- a/app/graphql/types/mutation_type.rb +++ b/app/graphql/types/mutation_type.rb @@ -1,7 +1,11 @@ +# frozen_string_literal: true + module Types class MutationType < BaseObject + include Gitlab::Graphql::MountMutation + graphql_name "Mutation" - # TODO: Add Mutations as fields + mount_mutation Mutations::MergeRequests::SetWip end end diff --git a/app/helpers/environments_helper.rb b/app/helpers/environments_helper.rb index 4ce89f89fa9..c005ecbb56b 100644 --- a/app/helpers/environments_helper.rb +++ b/app/helpers/environments_helper.rb @@ -4,4 +4,23 @@ module EnvironmentsHelper endpoint: project_environments_path(@project, format: :json) } end + + def metrics_data(project, environment) + { + "settings-path" => edit_project_service_path(project, 'prometheus'), + "clusters-path" => project_clusters_path(project), + "current-environment-name": environment.name, + "documentation-path" => help_page_path('administration/monitoring/prometheus/index.md'), + "empty-getting-started-svg-path" => image_path('illustrations/monitoring/getting_started.svg'), + "empty-loading-svg-path" => image_path('illustrations/monitoring/loading.svg'), + "empty-no-data-svg-path" => image_path('illustrations/monitoring/no_data.svg'), + "empty-unable-to-connect-svg-path" => image_path('illustrations/monitoring/unable_to_connect.svg'), + "metrics-endpoint" => additional_metrics_project_environment_path(project, environment, format: :json), + "deployment-endpoint" => project_environment_deployments_path(project, environment, format: :json), + "environments-endpoint": project_environments_path(project, format: :json), + "project-path" => project_path(project), + "tags-path" => project_tags_path(project), + "has-metrics" => "#{environment.has_metrics?}" + } + end end diff --git a/app/helpers/hooks_helper.rb b/app/helpers/hooks_helper.rb index 551b9cca6b1..0a356ba55d2 100644 --- a/app/helpers/hooks_helper.rb +++ b/app/helpers/hooks_helper.rb @@ -10,7 +10,7 @@ module HooksHelper trigger_human_name = trigger.to_s.tr('_', ' ').camelize - link_to path, rel: 'nofollow' do + link_to path, rel: 'nofollow', method: :post do content_tag(:span, trigger_human_name) end end diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb index 733832c1bbb..a05640773ad 100644 --- a/app/helpers/snippets_helper.rb +++ b/app/helpers/snippets_helper.rb @@ -116,7 +116,7 @@ module SnippetsHelper raw_project_snippet_url(@snippet.project, @snippet) end - link_to external_snippet_icon('doc_code'), snippet_raw_url, class: 'btn', target: '_blank', rel: 'noopener noreferrer', title: 'Open raw' + link_to external_snippet_icon('doc-code'), snippet_raw_url, class: 'btn', target: '_blank', rel: 'noopener noreferrer', title: 'Open raw' end def embedded_snippet_download_button diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index 1db1482d6b7..0e1e39501f5 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -124,7 +124,7 @@ class Notify < BaseMailer fallback_reply_message_id = "<reply-#{reply_key}@#{Gitlab.config.gitlab.host}>".freeze headers['References'] ||= [] - headers['References'] << fallback_reply_message_id + headers['References'].unshift(fallback_reply_message_id) @reply_by_email = true end @@ -158,7 +158,7 @@ class Notify < BaseMailer def mail_answer_thread(model, headers = {}) headers['Message-ID'] = "<#{SecureRandom.hex}@#{Gitlab.config.gitlab.host}>" headers['In-Reply-To'] = message_id(model) - headers['References'] = message_id(model) + headers['References'] = [message_id(model)] headers[:subject]&.prepend('Re: ') diff --git a/app/models/clusters/applications/prometheus.rb b/app/models/clusters/applications/prometheus.rb index 48137c2ed68..ea6ec4d6b03 100644 --- a/app/models/clusters/applications/prometheus.rb +++ b/app/models/clusters/applications/prometheus.rb @@ -21,6 +21,14 @@ module Clusters end end + def ready_status + [:installed] + end + + def ready? + ready_status.include?(status_name) + end + def chart 'stable/prometheus' end diff --git a/app/models/concerns/prometheus_adapter.rb b/app/models/concerns/prometheus_adapter.rb index 18cbbd871a1..9c36f633395 100644 --- a/app/models/concerns/prometheus_adapter.rb +++ b/app/models/concerns/prometheus_adapter.rb @@ -24,11 +24,10 @@ module PrometheusAdapter def query(query_name, *args) return unless can_query? - query_class = Gitlab::Prometheus::Queries.const_get("#{query_name.to_s.classify}Query") + query_class = query_klass_for(query_name) + query_args = build_query_args(*args) - args.map!(&:id) - - with_reactive_cache(query_class.name, *args, &query_class.method(:transform_reactive_result)) + with_reactive_cache(query_class.name, *query_args, &query_class.method(:transform_reactive_result)) end # Cache metrics for specific environment @@ -44,5 +43,13 @@ module PrometheusAdapter rescue Gitlab::PrometheusClient::Error => err { success: false, result: err.message } end + + def query_klass_for(query_name) + Gitlab::Prometheus::Queries.const_get("#{query_name.to_s.classify}Query") + end + + def build_query_args(*args) + args.map(&:id) + end end end diff --git a/app/models/concerns/reactive_caching.rb b/app/models/concerns/reactive_caching.rb index be0a5b49012..9155d82d567 100644 --- a/app/models/concerns/reactive_caching.rb +++ b/app/models/concerns/reactive_caching.rb @@ -59,6 +59,9 @@ module ReactiveCaching raise NotImplementedError end + def reactive_cache_updated(*args) + end + def with_reactive_cache(*args, &blk) bootstrap = !within_reactive_cache_lifetime?(*args) Rails.cache.write(alive_reactive_cache_key(*args), true, expires_in: self.class.reactive_cache_lifetime) @@ -81,8 +84,11 @@ module ReactiveCaching locking_reactive_cache(*args) do if within_reactive_cache_lifetime?(*args) enqueuing_update(*args) do - value = calculate_reactive_cache(*args) - Rails.cache.write(full_reactive_cache_key(*args), value) + key = full_reactive_cache_key(*args) + new_value = calculate_reactive_cache(*args) + old_value = Rails.cache.read(key) + Rails.cache.write(key, new_value) + reactive_cache_updated(*args) if new_value != old_value end end end diff --git a/app/models/concerns/routable.rb b/app/models/concerns/routable.rb index 0176a12a131..cb91f8fbac8 100644 --- a/app/models/concerns/routable.rb +++ b/app/models/concerns/routable.rb @@ -90,34 +90,17 @@ module Routable end def full_name - if route && route.name.present? - @full_name ||= route.name # rubocop:disable Gitlab/ModuleWithInstanceVariables - else - update_route if persisted? - - build_full_name - end + route&.name || build_full_name end - # Every time `project.namespace.becomes(Namespace)` is called for polymorphic_path, - # a new instance is instantiated, and we end up duplicating the same query to retrieve - # the route. Caching this per request ensures that even if we have multiple instances, - # we will not have to duplicate work, avoiding N+1 queries in some cases. def full_path - return uncached_full_path unless RequestStore.active? && persisted? - - RequestStore[full_path_key] ||= uncached_full_path + route&.path || build_full_path end def full_path_components full_path.split('/') end - def expires_full_path_cache - RequestStore.delete(full_path_key) if RequestStore.active? - @full_path = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables - end - def build_full_path if parent && path parent.full_path + '/' + path @@ -138,16 +121,6 @@ module Routable self.errors[:path].concat(route_path_errors) if route_path_errors end - def uncached_full_path - if route && route.path.present? - @full_path ||= route.path # rubocop:disable Gitlab/ModuleWithInstanceVariables - else - update_route if persisted? - - build_full_path - end - end - def full_name_changed? name_changed? || parent_changed? end @@ -156,10 +129,6 @@ module Routable path_changed? || parent_changed? end - def full_path_key - @full_path_key ||= "routable/full_path/#{self.class.name}/#{self.id}" - end - def build_full_name if parent && name parent.human_name + ' / ' + name @@ -168,18 +137,9 @@ module Routable end end - def update_route - return if Gitlab::Database.read_only? - - prepare_route - route.save - end - def prepare_route route || build_route(source: self) route.path = build_full_path route.name = build_full_name - @full_path = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables - @full_name = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables end end diff --git a/app/models/concerns/storage/legacy_namespace.rb b/app/models/concerns/storage/legacy_namespace.rb index f66bdd529f1..f5225cd81ed 100644 --- a/app/models/concerns/storage/legacy_namespace.rb +++ b/app/models/concerns/storage/legacy_namespace.rb @@ -11,8 +11,6 @@ module Storage Namespace.find(parent_id_was) # raise NotFound early if needed end - expires_full_path_cache - move_repositories if parent_changed? @@ -34,13 +32,12 @@ module Storage begin send_update_instructions write_projects_repository_config - - true - rescue - # Returning false does not rollback after_* transaction but gives - # us information about failing some of tasks - false + rescue => e + # Raise if development/test environment, else just notify Sentry + Gitlab::Sentry.track_exception(e, extra: { full_path_was: full_path_was, full_path: full_path, action: 'move_dir' }) end + + true # false would cancel later callbacks but not rollback end # Hooks diff --git a/app/models/deploy_token.rb b/app/models/deploy_token.rb index 7ab647abe93..fdbe95059e5 100644 --- a/app/models/deploy_token.rb +++ b/app/models/deploy_token.rb @@ -1,6 +1,7 @@ class DeployToken < ActiveRecord::Base include Expirable include TokenAuthenticatable + include PolicyActor add_authentication_token_field :token AVAILABLE_SCOPES = %i(read_repository read_registry).freeze @@ -58,10 +59,6 @@ class DeployToken < ActiveRecord::Base write_attribute(:expires_at, value.presence || Forever.date) end - def admin? - false - end - private def ensure_at_least_one_scope diff --git a/app/models/email.rb b/app/models/email.rb index d6516761f0a..15bdedeac33 100644 --- a/app/models/email.rb +++ b/app/models/email.rb @@ -25,6 +25,10 @@ class Email < ActiveRecord::Base self.errors.add(:email, 'has already been taken') if User.exists?(email: self.email) end + def accept_pending_invitations! + user.accept_pending_invitations! + end + # once email is confirmed, update the gpg signatures def update_invalid_gpg_signatures user.update_invalid_gpg_signatures if confirmed? diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 7034c633268..c1dc2f55346 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -304,7 +304,6 @@ class Namespace < ActiveRecord::Base def write_projects_repository_config all_projects.find_each do |project| - project.expires_full_path_cache # we need to clear cache to validate renames correctly project.write_repository_config end end diff --git a/app/models/project.rb b/app/models/project.rb index 325dbd0197f..a452ec5fcdf 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -31,6 +31,7 @@ class Project < ActiveRecord::Base BoardLimitExceeded = Class.new(StandardError) + STATISTICS_ATTRIBUTE = 'repositories_count'.freeze NUMBER_OF_PERMITTED_BOARDS = 1 UNKNOWN_IMPORT_URL = 'http://unknown.git'.freeze # Hashed Storage versions handle rolling out new storage to project and dependents models: @@ -79,6 +80,10 @@ class Project < ActiveRecord::Base after_create :create_project_feature, unless: :project_feature + after_create -> { SiteStatistic.track(STATISTICS_ATTRIBUTE) } + before_destroy ->(project) { project.project_feature.untrack_statistics_for_deletion! } + after_destroy -> { SiteStatistic.untrack(STATISTICS_ATTRIBUTE) } + after_create :create_ci_cd_settings, unless: :ci_cd_settings, if: proc { ProjectCiCdSetting.available? } @@ -1235,8 +1240,6 @@ class Project < ActiveRecord::Base return true if skip_disk_validation return false unless repository_storage - expires_full_path_cache # we need to clear cache to validate renames correctly - # Check if repository with same path already exists on disk we can # skip this for the hashed storage because the path does not change if legacy_storage? && repository_with_same_path_already_exists? @@ -1615,7 +1618,6 @@ class Project < ActiveRecord::Base # When we import a project overwriting the original project, there # is a move operation. In that case we don't want to send the instructions. send_move_instructions(full_path_was) unless import_started? - expires_full_path_cache self.old_path_with_namespace = full_path_was SystemHooksService.new.execute_hooks_for(self, :rename) diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb index bfb8d703ec9..9c768b13f78 100644 --- a/app/models/project_feature.rb +++ b/app/models/project_feature.rb @@ -19,6 +19,7 @@ class ProjectFeature < ActiveRecord::Base ENABLED = 20 FEATURES = %i(issues merge_requests wiki snippets builds repository).freeze + STATISTICS_ATTRIBUTE = 'wikis_count'.freeze class << self def access_level_attribute(feature) @@ -52,6 +53,9 @@ class ProjectFeature < ActiveRecord::Base default_value_for :wiki_access_level, value: ENABLED, allows_nil: false default_value_for :repository_access_level, value: ENABLED, allows_nil: false + after_create ->(model) { SiteStatistic.track(STATISTICS_ATTRIBUTE) if model.wiki_enabled? } + after_update :update_site_statistics + def feature_available?(feature, user) get_permission(user, access_level(feature)) end @@ -76,8 +80,30 @@ class ProjectFeature < ActiveRecord::Base issues_access_level > DISABLED end + # This is a workaround for the removal hooks not been triggered when removing a Project. + # + # ProjectFeature is removed using database cascade index rule. + # This method is called by Project model when deletion starts. + def untrack_statistics_for_deletion! + return unless wiki_enabled? + + SiteStatistic.untrack(STATISTICS_ATTRIBUTE) + end + private + def update_site_statistics + return unless wiki_access_level_changed? + + if self.wiki_access_level_was == DISABLED + # possible new states are PRIVATE / ENABLED, both should be tracked + SiteStatistic.track(STATISTICS_ATTRIBUTE) + elsif self.wiki_access_level == DISABLED + # old state was either PRIVATE / ENABLED, only untrack if new state is DISABLED + SiteStatistic.untrack(STATISTICS_ATTRIBUTE) + end + end + # Validates builds and merge requests access level # which cannot be higher than repository access level def repository_children_level diff --git a/app/models/remote_mirror.rb b/app/models/remote_mirror.rb index 976b501e297..6172bb38881 100644 --- a/app/models/remote_mirror.rb +++ b/app/models/remote_mirror.rb @@ -48,13 +48,13 @@ class RemoteMirror < ActiveRecord::Base state :failed after_transition any => :started do |remote_mirror, _| - Gitlab::Metrics.add_event(:remote_mirrors_running, path: remote_mirror.project.full_path) + Gitlab::Metrics.add_event(:remote_mirrors_running) remote_mirror.update(last_update_started_at: Time.now) end after_transition started: :finished do |remote_mirror, _| - Gitlab::Metrics.add_event(:remote_mirrors_finished, path: remote_mirror.project.full_path) + Gitlab::Metrics.add_event(:remote_mirrors_finished) timestamp = Time.now remote_mirror.update!( @@ -63,7 +63,7 @@ class RemoteMirror < ActiveRecord::Base end after_transition started: :failed do |remote_mirror, _| - Gitlab::Metrics.add_event(:remote_mirrors_failed, path: remote_mirror.project.full_path) + Gitlab::Metrics.add_event(:remote_mirrors_failed) remote_mirror.update(last_update_at: Time.now) end diff --git a/app/models/repository.rb b/app/models/repository.rb index e248f94cbd8..9873d9a6327 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -1029,7 +1029,7 @@ class Repository end def repository_event(event, tags = {}) - Gitlab::Metrics.add_event(event, { path: full_path }.merge(tags)) + Gitlab::Metrics.add_event(event, tags) end def initialize_raw_repository diff --git a/app/models/site_statistic.rb b/app/models/site_statistic.rb new file mode 100644 index 00000000000..9c9c3172fe6 --- /dev/null +++ b/app/models/site_statistic.rb @@ -0,0 +1,74 @@ +class SiteStatistic < ActiveRecord::Base + # prevents the creation of multiple rows + default_value_for :id, 1 + + COUNTER_ATTRIBUTES = %w(repositories_count wikis_count).freeze + REQUIRED_SCHEMA_VERSION = 20180629153018 + + # Tracks specific attribute + # + # @param [String] raw_attribute must be one of the values listed in COUNTER_ATTRIBUTES + def self.track(raw_attribute) + with_statistics_available(raw_attribute) do |attribute| + SiteStatistic.update_all(["#{attribute} = #{attribute}+1"]) + end + end + + # Untracks specific attribute + # + # @param [String] raw_attribute must be one of the values listed in COUNTER_ATTRIBUTES + def self.untrack(raw_attribute) + with_statistics_available(raw_attribute) do |attribute| + SiteStatistic.update_all(["#{attribute} = #{attribute}-1 WHERE #{attribute} > 0"]) + end + end + + # Wrapper for track/untrack operations with basic validations and enforced requirements + # + # @param [String] raw_attribute must be one of the values listed in COUNTER_ATTRIBUTES + # @yield [String] attribute quoted to be used inside SQL / Arel query + def self.with_statistics_available(raw_attribute) + unless raw_attribute.in?(COUNTER_ATTRIBUTES) + raise ArgumentError, "Invalid attribute: '#{raw_attribute}' to '#{caller_locations(1, 1)[0].label}' method. " \ + "Valid attributes are: #{COUNTER_ATTRIBUTES.join(', ')}" + end + + return unless available? + + self.fetch # make sure record exists + + attribute = self.connection.quote_column_name(raw_attribute) + + # will be running on its own transaction context + yield(attribute) + end + + # Returns a site statistic record with tracked information + # + # @return [SiteStatistic] record with tracked information + def self.fetch + SiteStatistic.transaction(requires_new: true) do + SiteStatistic.first_or_create! + end + rescue ActiveRecord::RecordNotUnique + retry + end + + # Return whether required schema change is available + # + # This is needed in order to degrade gracefully when testing schema migrations + # + # @return [Boolean] whether schema is available + def self.available? + @available_flag ||= ActiveRecord::Migrator.current_version >= REQUIRED_SCHEMA_VERSION + end + + # Resets cached column information + # + # This is called during schema migration specs, in order to reset internal cache state + def self.reset_column_information + @available_flag = nil + + super + end +end diff --git a/app/policies/concerns/policy_actor.rb b/app/policies/concerns/policy_actor.rb new file mode 100644 index 00000000000..069d065280e --- /dev/null +++ b/app/policies/concerns/policy_actor.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +# Include this module if we want to pass something else than the user to +# check policies. This defines several methods which the policy checker +# would call and check. +module PolicyActor + extend ActiveSupport::Concern + + def blocked? + false + end + + def admin? + false + end + + def external? + false + end + + def internal? + false + end + + def access_locked? + false + end + + def required_terms_not_accepted? + false + end + + def can_create_group + false + end +end diff --git a/app/serializers/pipeline_serializer.rb b/app/serializers/pipeline_serializer.rb index 4a33160afa1..3205578b83e 100644 --- a/app/serializers/pipeline_serializer.rb +++ b/app/serializers/pipeline_serializer.rb @@ -11,10 +11,15 @@ class PipelineSerializer < BaseSerializer :retryable_builds, :cancelable_statuses, :trigger_requests, - :project, :manual_actions, :artifacts, - { pending_builds: :project } + { + pending_builds: :project, + project: [:route, { namespace: :route }], + artifacts: { + project: [:route, { namespace: :route }] + } + } ]) end diff --git a/app/services/clusters/applications/check_installation_progress_service.rb b/app/services/clusters/applications/check_installation_progress_service.rb index 4640c5a2d4b..a1165b0ab28 100644 --- a/app/services/clusters/applications/check_installation_progress_service.rb +++ b/app/services/clusters/applications/check_installation_progress_service.rb @@ -50,17 +50,17 @@ module Clusters end def remove_installation_pod - helm_api.delete_installation_pod!(install_command.pod_name) + helm_api.delete_pod!(install_command.pod_name) rescue # no-op end def installation_phase - helm_api.installation_status(install_command.pod_name) + helm_api.status(install_command.pod_name) end def installation_errors - helm_api.installation_log(install_command.pod_name) + helm_api.log(install_command.pod_name) end end end diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb index a4a66330546..c2a0c5fa7f3 100644 --- a/app/services/projects/transfer_service.rb +++ b/app/services/projects/transfer_service.rb @@ -77,7 +77,6 @@ module Projects Gitlab::PagesTransfer.new.move_project(project.path, @old_namespace.full_path, @new_namespace.full_path) project.old_path_with_namespace = @old_path - project.expires_full_path_cache write_repository_config(@new_path) diff --git a/app/services/prometheus/adapter_service.rb b/app/services/prometheus/adapter_service.rb index cbba79690c5..a791845ba20 100644 --- a/app/services/prometheus/adapter_service.rb +++ b/app/services/prometheus/adapter_service.rb @@ -30,7 +30,7 @@ module Prometheus return unless deployment_platform.respond_to?(:cluster) cluster = deployment_platform.cluster - return unless cluster.application_prometheus&.installed? + return unless cluster.application_prometheus&.ready? cluster.application_prometheus end diff --git a/app/views/admin/projects/_projects.html.haml b/app/views/admin/projects/_projects.html.haml index 00933d726d9..fdaacc098e0 100644 --- a/app/views/admin/projects/_projects.html.haml +++ b/app/views/admin/projects/_projects.html.haml @@ -17,7 +17,7 @@ - if project.archived %span.badge.badge-warning archived .title - = link_to [:admin, project.namespace.becomes(Namespace), project] do + = link_to(admin_namespace_project_path(project.namespace, project)) do .dash-project-avatar .avatar-container.s40 = project_icon(project, alt: '', class: 'avatar project-avatar s40') diff --git a/app/views/ide/index.html.haml b/app/views/ide/index.html.haml index 9f8b0acd763..d29dda43c89 100644 --- a/app/views/ide/index.html.haml +++ b/app/views/ide/index.html.haml @@ -1,6 +1,9 @@ - @body_class = 'ide' - page_title 'IDE' +- content_for :page_specific_javascripts do + = stylesheet_link_tag 'page_bundles/ide' + #ide.ide-loading{ data: {"empty-state-svg-path" => image_path('illustrations/multi_file_editor_empty.svg'), "no-changes-state-svg-path" => image_path('illustrations/multi-editor_no_changes_empty.svg'), "committed-state-svg-path" => image_path('illustrations/multi-editor_all_changes_committed_empty.svg'), diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index 9253a0652da..ac5916d129c 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -3,6 +3,11 @@ - site_name = "GitLab" %head{ prefix: "og: http://ogp.me/ns#" } %meta{ charset: "utf-8" } + + - if Feature.enabled?('asset_host_prefetch') && ActionController::Base.asset_host + %link{ rel: 'dns-prefetch', href: ActionController::Base.asset_host } + %link{ rel: 'preconnnect', href: ActionController::Base.asset_host, crossorigin: '' } + %meta{ 'http-equiv' => 'X-UA-Compatible', content: 'IE=edge' } -# Open Graph - http://ogp.me/ diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 97c04dda8cb..e8d31992149 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -67,5 +67,5 @@ %button.navbar-toggler.d-block.d-sm-none{ type: 'button' } %span.sr-only= _("Toggle navigation") - = sprite_icon('more', size: 12, css_class: 'more-icon js-navbar-toggle-right') + = sprite_icon('ellipsis_h', size: 12, css_class: 'more-icon js-navbar-toggle-right') = sprite_icon('close', size: 12, css_class: 'close-icon js-navbar-toggle-left') diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml index 0a3b5ec7eea..d471dd84550 100644 --- a/app/views/layouts/nav/sidebar/_group.html.haml +++ b/app/views/layouts/nav/sidebar/_group.html.haml @@ -15,7 +15,7 @@ = nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups', 'analytics#show'], html_options: { class: 'home' }) do = link_to group_path(@group) do .nav-icon-container - = sprite_icon('project') + = sprite_icon('home') %span.nav-item-name = _('Overview') diff --git a/app/views/layouts/nav/sidebar/_profile.html.haml b/app/views/layouts/nav/sidebar/_profile.html.haml index 94863a3460d..d65f153b451 100644 --- a/app/views/layouts/nav/sidebar/_profile.html.haml +++ b/app/views/layouts/nav/sidebar/_profile.html.haml @@ -110,7 +110,7 @@ = nav_link(controller: :gpg_keys) do = link_to profile_gpg_keys_path do .nav-icon-container - = sprite_icon('key-2') + = sprite_icon('key-modern') %span.nav-item-name = _('GPG Keys') %ul.sidebar-sub-level-items.is-fly-out-only diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml index 0ec61df1f0a..2c262a2b7dd 100644 --- a/app/views/layouts/nav/sidebar/_project.html.haml +++ b/app/views/layouts/nav/sidebar/_project.html.haml @@ -11,7 +11,7 @@ = nav_link(path: sidebar_projects_paths, html_options: { class: 'home' }) do = link_to project_path(@project), class: 'shortcuts-project' do .nav-icon-container - = sprite_icon('project') + = sprite_icon('home') %span.nav-item-name = _('Project') @@ -40,7 +40,7 @@ = nav_link(controller: sidebar_repository_paths) do = link_to project_tree_path(@project), class: 'shortcuts-tree' do .nav-icon-container - = sprite_icon('doc_text') + = sprite_icon('doc-text') %span.nav-item-name = _('Repository') diff --git a/app/views/projects/environments/metrics.html.haml b/app/views/projects/environments/metrics.html.haml index 290970a1045..af86b8e8e67 100644 --- a/app/views/projects/environments/metrics.html.haml +++ b/app/views/projects/environments/metrics.html.haml @@ -2,17 +2,11 @@ - page_title "Metrics for environment", @environment.name .prometheus-container{ class: container_class } - #prometheus-graphs{ data: { "settings-path": edit_project_service_path(@project, 'prometheus'), - "clusters-path": project_clusters_path(@project), - "current-environment-name": @environment.name, - "documentation-path": help_page_path('administration/monitoring/prometheus/index.md'), - "empty-getting-started-svg-path": image_path('illustrations/monitoring/getting_started.svg'), - "empty-loading-svg-path": image_path('illustrations/monitoring/loading.svg'), - "empty-no-data-svg-path": image_path('illustrations/monitoring/no_data.svg'), - "empty-unable-to-connect-svg-path": image_path('illustrations/monitoring/unable_to_connect.svg'), - "metrics-endpoint": additional_metrics_project_environment_path(@project, @environment, format: :json), - "deployment-endpoint": project_environment_deployments_path(@project, @environment, format: :json), - "environments-endpoint": project_environments_path(@project, format: :json), - "project-path": project_path(@project), - "tags-path": project_tags_path(@project), - "has-metrics": "#{@environment.has_metrics?}" } } + .top-area + .row + .col-sm-6 + %h3 + Environment: + = link_to @environment.name, environment_path(@environment) + + #prometheus-graphs{ data: metrics_data(@project, @environment) } diff --git a/app/views/projects/jobs/_sidebar.html.haml b/app/views/projects/jobs/_sidebar.html.haml index b88fe47726d..759efd4e9d4 100644 --- a/app/views/projects/jobs/_sidebar.html.haml +++ b/app/views/projects/jobs/_sidebar.html.haml @@ -86,7 +86,7 @@ - HasStatus::ORDERED_STATUSES.each do |build_status| - builds.select{|build| build.status == build_status}.each do |build| .build-job{ class: sidebar_build_class(build, @build), data: { stage: build.stage } } - - tooltip = build.tooltip_message + - tooltip = sanitize(build.tooltip_message.dup) = link_to(project_job_path(@project, build), data: { toggle: 'tooltip', html: 'true', title: tooltip, container: 'body' }) do = sprite_icon('arrow-right', size:16, css_class: 'icon-arrow-right') %span{ class: "ci-status-icon-#{build.status}" } diff --git a/app/views/projects/merge_requests/creations/_new_compare.html.haml b/app/views/projects/merge_requests/creations/_new_compare.html.haml index ca0f7d6098f..afa7eb06cb4 100644 --- a/app/views/projects/merge_requests/creations/_new_compare.html.haml +++ b/app/views/projects/merge_requests/creations/_new_compare.html.haml @@ -27,7 +27,7 @@ = dropdown_filter(_("Search branches")) = dropdown_content = dropdown_loading - .panel-footer + .card-footer .text-center= icon('spinner spin', class: 'js-source-loading') %ul.list-unstyled.mr_source_commit diff --git a/app/views/shared/snippets/_embed.html.haml b/app/views/shared/snippets/_embed.html.haml index 36f56fbad1a..c7f0511d1de 100644 --- a/app/views/shared/snippets/_embed.html.haml +++ b/app/views/shared/snippets/_embed.html.haml @@ -2,7 +2,7 @@ .gitlab-embed-snippets .js-file-title.file-title-flex-parent .file-header-content - = external_snippet_icon('doc_text') + = external_snippet_icon('doc-text') %strong.file-title-name %a.gitlab-embedded-snippets-title{ href: url_for(only_path: false, overwrite_params: nil) } diff --git a/app/workers/concerns/gitlab/github_import/object_importer.rb b/app/workers/concerns/gitlab/github_import/object_importer.rb index 100d86e38c8..eeeff6e93a0 100644 --- a/app/workers/concerns/gitlab/github_import/object_importer.rb +++ b/app/workers/concerns/gitlab/github_import/object_importer.rb @@ -22,7 +22,7 @@ module Gitlab importer_class.new(object, project, client).execute - counter.increment(project: project.full_path) + counter.increment end def counter diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb index 5ef9b744db3..68ec66e8499 100644 --- a/app/workers/repository_fork_worker.rb +++ b/app/workers/repository_fork_worker.rb @@ -23,9 +23,7 @@ class RepositoryForkWorker def fork_repository(target_project, source_repository_storage_name, source_disk_path) return unless start_fork(target_project) - Gitlab::Metrics.add_event(:fork_repository, - source_path: source_disk_path, - target_path: target_project.disk_path) + Gitlab::Metrics.add_event(:fork_repository) result = gitlab_shell.fork_repository(source_repository_storage_name, source_disk_path, target_project.repository_storage, target_project.disk_path) diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index 25fec542ac7..8c64c513c74 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -11,9 +11,7 @@ class RepositoryImportWorker return unless start_import(project) - Gitlab::Metrics.add_event(:import_repository, - import_url: project.import_url, - path: project.full_path) + Gitlab::Metrics.add_event(:import_repository) service = Projects::ImportService.new(project, project.creator) result = service.execute diff --git a/changelogs/unreleased/31576-redirect-commits-to-root-if-no-ref.yml b/changelogs/unreleased/31576-redirect-commits-to-root-if-no-ref.yml new file mode 100644 index 00000000000..21d9d25d342 --- /dev/null +++ b/changelogs/unreleased/31576-redirect-commits-to-root-if-no-ref.yml @@ -0,0 +1,5 @@ +--- +title: Redirect commits to root if no ref is provided (31576) +merge_request: 20738 +author: Kia Mei Somabes +type: added diff --git a/changelogs/unreleased/32783-api-all-members-with-ancestors.yml b/changelogs/unreleased/32783-api-all-members-with-ancestors.yml new file mode 100644 index 00000000000..ca53d02845d --- /dev/null +++ b/changelogs/unreleased/32783-api-all-members-with-ancestors.yml @@ -0,0 +1,6 @@ +--- +title: Adds API endpoint /api/v4/(project/group)/:id/members/all to list also inherited + members +merge_request: 19748 +author: Jacopo Beschi @jacopo-beschi +type: added diff --git a/changelogs/unreleased/4525-fix-project-indexes.yml b/changelogs/unreleased/4525-fix-project-indexes.yml deleted file mode 100644 index 930e3b934c2..00000000000 --- a/changelogs/unreleased/4525-fix-project-indexes.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Rework some projects table indexes around repository_storage field -merge_request: 20377 -author: -type: fixed diff --git a/changelogs/unreleased/48055-web-ide-resize-handles.yml b/changelogs/unreleased/48055-web-ide-resize-handles.yml new file mode 100644 index 00000000000..0f650cdda6f --- /dev/null +++ b/changelogs/unreleased/48055-web-ide-resize-handles.yml @@ -0,0 +1,5 @@ +--- +title: Increase width of Web IDE sidebar resize handles +merge_request: 20818 +author: +type: fixed diff --git a/changelogs/unreleased/48542-code-link.yml b/changelogs/unreleased/48542-code-link.yml new file mode 100644 index 00000000000..8d8d9bf8d74 --- /dev/null +++ b/changelogs/unreleased/48542-code-link.yml @@ -0,0 +1,5 @@ +--- +title: Fix link color in markdown code brackets +merge_request: 20841 +author: +type: fixed diff --git a/changelogs/unreleased/48617-promoting-milestone.yml b/changelogs/unreleased/48617-promoting-milestone.yml new file mode 100644 index 00000000000..7fbc15051cf --- /dev/null +++ b/changelogs/unreleased/48617-promoting-milestone.yml @@ -0,0 +1,5 @@ +--- +title: Escapes milestone and label's names on flash notice when promoting them +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/48636-new-mr-card-styles.yml b/changelogs/unreleased/48636-new-mr-card-styles.yml new file mode 100644 index 00000000000..94f62e677fb --- /dev/null +++ b/changelogs/unreleased/48636-new-mr-card-styles.yml @@ -0,0 +1,5 @@ +--- +title: Fix new MR card styles +merge_request: 20822 +author: +type: fixed diff --git a/changelogs/unreleased/48817-fix-mr-changes-discussion-navigation.yml b/changelogs/unreleased/48817-fix-mr-changes-discussion-navigation.yml deleted file mode 100644 index ec4b843b863..00000000000 --- a/changelogs/unreleased/48817-fix-mr-changes-discussion-navigation.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix navigation to First and Next discussion on MR Changes tab -merge_request: 20434 -author: -type: fixed diff --git a/changelogs/unreleased/49107-prefetching-of-assets-and-cdn-domain.yml b/changelogs/unreleased/49107-prefetching-of-assets-and-cdn-domain.yml new file mode 100644 index 00000000000..541b562adac --- /dev/null +++ b/changelogs/unreleased/49107-prefetching-of-assets-and-cdn-domain.yml @@ -0,0 +1,5 @@ +--- +title: DNS prefetching if asset_host for CDN hosting is set +merge_request: 20781 +author: +type: performance diff --git a/changelogs/unreleased/49364-fix-broadcast-margin.yml b/changelogs/unreleased/49364-fix-broadcast-margin.yml new file mode 100644 index 00000000000..821fb9df1af --- /dev/null +++ b/changelogs/unreleased/49364-fix-broadcast-margin.yml @@ -0,0 +1,5 @@ +--- +title: Fix misalignment of broadcast message on login page +merge_request: 20794 +author: Robin Naundorf +type: fixed diff --git a/changelogs/unreleased/_acet-fix-expanding-context-lines.yml b/changelogs/unreleased/_acet-fix-expanding-context-lines.yml deleted file mode 100644 index 41b4dbca5d6..00000000000 --- a/changelogs/unreleased/_acet-fix-expanding-context-lines.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix rendering of the context lines in MR diffs page -merge_request: 20642 -author: -type: fixed diff --git a/changelogs/unreleased/_acet-fix-mr-autosave.yml b/changelogs/unreleased/_acet-fix-mr-autosave.yml deleted file mode 100644 index f87b32f68e2..00000000000 --- a/changelogs/unreleased/_acet-fix-mr-autosave.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix autosave and ESC confirmation issues for MR discussions -merge_request: 20569 -author: -type: fixed diff --git a/changelogs/unreleased/_acet-fix-outdated-discussions.yml b/changelogs/unreleased/_acet-fix-outdated-discussions.yml deleted file mode 100644 index d31483b4765..00000000000 --- a/changelogs/unreleased/_acet-fix-outdated-discussions.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix showing outdated discussions on Changes tab -merge_request: 20445 -author: -type: fixed diff --git a/changelogs/unreleased/bvl-graphql-wip-mutation.yml b/changelogs/unreleased/bvl-graphql-wip-mutation.yml new file mode 100644 index 00000000000..00aa1c48677 --- /dev/null +++ b/changelogs/unreleased/bvl-graphql-wip-mutation.yml @@ -0,0 +1,5 @@ +--- +title: Add the first mutations for merge requests to GraphQL +merge_request: 20443 +author: +type: added diff --git a/changelogs/unreleased/ce-6064-geo-sql-query-for-counting-projects-with-wikis-is-very-slow.yml b/changelogs/unreleased/ce-6064-geo-sql-query-for-counting-projects-with-wikis-is-very-slow.yml new file mode 100644 index 00000000000..b76437a8773 --- /dev/null +++ b/changelogs/unreleased/ce-6064-geo-sql-query-for-counting-projects-with-wikis-is-very-slow.yml @@ -0,0 +1,5 @@ +--- +title: Tracking the number of repositories and wikis with a cached counter for site-wide statistics +merge_request: 20413 +author: +type: performance diff --git a/changelogs/custom_wiki_sidebar.yml b/changelogs/unreleased/custom_wiki_sidebar.yml index 988fccc929c..988fccc929c 100644 --- a/changelogs/custom_wiki_sidebar.yml +++ b/changelogs/unreleased/custom_wiki_sidebar.yml diff --git a/changelogs/unreleased/fix-email-confirmation-addtional-email.yml b/changelogs/unreleased/fix-email-confirmation-addtional-email.yml new file mode 100644 index 00000000000..56a2efa4d60 --- /dev/null +++ b/changelogs/unreleased/fix-email-confirmation-addtional-email.yml @@ -0,0 +1,5 @@ +--- +title: Fix email confirmation bug when user adds additional email to account +merge_request: 20084 +author: muhammadn +type: fixed diff --git a/changelogs/unreleased/full-list-of-vulnerabilities-5239.yml b/changelogs/unreleased/full-list-of-vulnerabilities-5239.yml new file mode 100644 index 00000000000..b26eb82b6c9 --- /dev/null +++ b/changelogs/unreleased/full-list-of-vulnerabilities-5239.yml @@ -0,0 +1,5 @@ +--- +title: Removes "show all" on reports and adds an actionButtons slot +merge_request: 20855 +author: +type: changed diff --git a/changelogs/unreleased/ide-delete-entries.yml b/changelogs/unreleased/ide-delete-entries.yml new file mode 100644 index 00000000000..8cbc0739406 --- /dev/null +++ b/changelogs/unreleased/ide-delete-entries.yml @@ -0,0 +1,5 @@ +--- +title: Enabled deletion of files in the Web IDE +merge_request: +author: +type: added diff --git a/changelogs/unreleased/mk-fix-callback-canceling-in-namespace-move-dir.yml b/changelogs/unreleased/mk-fix-callback-canceling-in-namespace-move-dir.yml new file mode 100644 index 00000000000..8e71377d93f --- /dev/null +++ b/changelogs/unreleased/mk-fix-callback-canceling-in-namespace-move-dir.yml @@ -0,0 +1,5 @@ +--- +title: Fix namespace move callback behavior, especially to fix Geo replication of namespace moves during certain exceptions. +merge_request: 19297 +author: +type: fixed diff --git a/changelogs/unreleased/project-dropdown-list-overflow.yml b/changelogs/unreleased/project-dropdown-list-overflow.yml deleted file mode 100644 index 9b74a68291b..00000000000 --- a/changelogs/unreleased/project-dropdown-list-overflow.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Don't overflow project/group dropdown results -merge_request: 20704 -author: gfyoung -type: fixed diff --git a/changelogs/unreleased/rails5-update-gemfile-lock-2.yml b/changelogs/unreleased/rails5-update-gemfile-lock-2.yml new file mode 100644 index 00000000000..1f3e9bd2238 --- /dev/null +++ b/changelogs/unreleased/rails5-update-gemfile-lock-2.yml @@ -0,0 +1,5 @@ +--- +title: Rails5 update Gemfile.rails5.lock +merge_request: 20858 +author: Jasper Maes +type: fixed diff --git a/changelogs/unreleased/security-fj-missing-csrf-system-hooks.yml b/changelogs/unreleased/security-fj-missing-csrf-system-hooks.yml new file mode 100644 index 00000000000..fabf48acbbc --- /dev/null +++ b/changelogs/unreleased/security-fj-missing-csrf-system-hooks.yml @@ -0,0 +1,5 @@ +--- +title: Adding CSRF protection to Hooks test action +merge_request: +author: +type: security diff --git a/changelogs/unreleased/security-ide-branch-name-xss.yml b/changelogs/unreleased/security-ide-branch-name-xss.yml new file mode 100644 index 00000000000..51742ffa4e9 --- /dev/null +++ b/changelogs/unreleased/security-ide-branch-name-xss.yml @@ -0,0 +1,5 @@ +--- +title: Fixed XSS in branch name in Web IDE +merge_request: +author: +type: security diff --git a/changelogs/unreleased/sh-bump-haml-5-0-4.yml b/changelogs/unreleased/sh-bump-haml-5-0-4.yml new file mode 100644 index 00000000000..269b1e55417 --- /dev/null +++ b/changelogs/unreleased/sh-bump-haml-5-0-4.yml @@ -0,0 +1,5 @@ +--- +title: Bump haml gem to 5.0.4 +merge_request: 20847 +author: +type: performance diff --git a/changelogs/unreleased/sh-include-rbtrace.yml b/changelogs/unreleased/sh-include-rbtrace.yml new file mode 100644 index 00000000000..41f0655e3f8 --- /dev/null +++ b/changelogs/unreleased/sh-include-rbtrace.yml @@ -0,0 +1,5 @@ +--- +title: Add rbtrace to Gemfile +merge_request: 20831 +author: +type: other diff --git a/changelogs/unreleased/sh-revert-markdown-changes.yml b/changelogs/unreleased/sh-revert-markdown-changes.yml deleted file mode 100644 index 72540f710a1..00000000000 --- a/changelogs/unreleased/sh-revert-markdown-changes.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Fix slow Markdown rendering -merge_request: 20820 -author: -type: performance diff --git a/changelogs/unreleased/stop-dynamic-routable-creation.yml b/changelogs/unreleased/stop-dynamic-routable-creation.yml new file mode 100644 index 00000000000..8bfcb5b2d11 --- /dev/null +++ b/changelogs/unreleased/stop-dynamic-routable-creation.yml @@ -0,0 +1,5 @@ +--- +title: Stop dynamically creating project and namespace routes +merge_request: 20313 +author: +type: performance diff --git a/changelogs/unreleased/tc-reorder-mail-notify-references.yml b/changelogs/unreleased/tc-reorder-mail-notify-references.yml new file mode 100644 index 00000000000..689afda0259 --- /dev/null +++ b/changelogs/unreleased/tc-reorder-mail-notify-references.yml @@ -0,0 +1,5 @@ +--- +title: Put fallback reply-key address first in the References header +merge_request: 20871 +author: +type: changed diff --git a/changelogs/unreleased/tz-mr-refactor-memory-reduction.yml b/changelogs/unreleased/tz-mr-refactor-memory-reduction.yml deleted file mode 100644 index 16003fa9cad..00000000000 --- a/changelogs/unreleased/tz-mr-refactor-memory-reduction.yml +++ /dev/null @@ -1,5 +0,0 @@ ----
-title: Reduces the client side memory footprint on merge requests
-merge_request: 20744
-author:
-type: performance
diff --git a/changelogs/unreleased/wrap-job-name-on-jobs-sidebar.yml b/changelogs/unreleased/wrap-job-name-on-jobs-sidebar.yml new file mode 100644 index 00000000000..97fa1592753 --- /dev/null +++ b/changelogs/unreleased/wrap-job-name-on-jobs-sidebar.yml @@ -0,0 +1,5 @@ +--- +title: Wrap job name on pipeline job sidebar +merge_request: 20804 +author: George Tsiolis +type: changed diff --git a/changelogs/unreleased/zj-backup-timeout.yml b/changelogs/unreleased/zj-backup-timeout.yml deleted file mode 100644 index b2ad2ed8c63..00000000000 --- a/changelogs/unreleased/zj-backup-timeout.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: Disable Gitaly timeouts when creating or restoring backups -merge_request: 20810 -author: -type: fixed diff --git a/config/application.rb b/config/application.rb index 0304f466734..b9d4f6765e3 100644 --- a/config/application.rb +++ b/config/application.rb @@ -43,10 +43,12 @@ module Gitlab #{config.root}/app/models/members #{config.root}/app/models/project_services #{config.root}/app/workers/concerns + #{config.root}/app/policies/concerns #{config.root}/app/services/concerns #{config.root}/app/serializers/concerns #{config.root}/app/finders/concerns - #{config.root}/app/graphql/resolvers/concerns]) + #{config.root}/app/graphql/resolvers/concerns + #{config.root}/app/graphql/mutations/concerns]) config.generators.templates.push("#{config.root}/generator_templates") @@ -132,6 +134,7 @@ module Gitlab config.assets.precompile << "notify.css" config.assets.precompile << "mailers/*.css" config.assets.precompile << "xterm/xterm.css" + config.assets.precompile << "page_bundles/ide.css" config.assets.precompile << "performance_bar.css" config.assets.precompile << "lib/ace.js" config.assets.precompile << "test.css" diff --git a/config/dependency_decisions.yml b/config/dependency_decisions.yml index 21c20cd5e93..73115449871 100644 --- a/config/dependency_decisions.yml +++ b/config/dependency_decisions.yml @@ -540,3 +540,9 @@ :why: https://github.com/xtuc/webassemblyjs/blob/master/LICENSE :versions: [] :when: 2018-06-08 05:30:56.764116000 Z +- - :approve + - "@gitlab-org/gitlab-ui" + - :who: Clement Ho + :why: Our own library + :versions: [] + :when: 2018-07-17 21:02:54.529227000 Z diff --git a/config/initializers/rbtrace.rb b/config/initializers/rbtrace.rb new file mode 100644 index 00000000000..3a076c99ad0 --- /dev/null +++ b/config/initializers/rbtrace.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +require 'rbtrace' if ENV['ENABLE_RBTRACE'] diff --git a/config/routes/admin.rb b/config/routes/admin.rb index ff27ceb50dc..109f00631fb 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -54,7 +54,7 @@ namespace :admin do resources :hooks, only: [:index, :create, :edit, :update, :destroy] do member do - get :test + post :test end resources :hook_logs, only: [:show] do diff --git a/config/routes/project.rb b/config/routes/project.rb index 5057e937941..8e019f8c8bb 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -301,7 +301,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do resources :hooks, only: [:index, :create, :edit, :update, :destroy], constraints: { id: /\d+/ } do member do - get :test + post :test end resources :hook_logs, only: [:show] do diff --git a/config/routes/repository.rb b/config/routes/repository.rb index e2bf8d6a7ff..d439cb9acbd 100644 --- a/config/routes/repository.rb +++ b/config/routes/repository.rb @@ -83,6 +83,7 @@ scope format: false do get '/raw/*id', to: 'raw#show', as: :raw get '/blame/*id', to: 'blame#show', as: :blame + get '/commits', to: 'commits#commits_root', as: :commits_root get '/commits/*id/signatures', to: 'commits#signatures', as: :signatures get '/commits/*id', to: 'commits#show', as: :commits diff --git a/danger/database/Dangerfile b/danger/database/Dangerfile index 6f48994945a..ad5f1c1e0f3 100644 --- a/danger/database/Dangerfile +++ b/danger/database/Dangerfile @@ -41,8 +41,8 @@ end all_files = git.added_files + git.modified_files -non_geo_db_schema_updated = !git.modified_files.grep(%r{\Adb/schema\.rb/}).empty? -geo_db_schema_updated = !git.modified_files.grep(%r{\Aee/db/geo/schema\.rb/}).empty? +non_geo_db_schema_updated = !git.modified_files.grep(%r{\Adb/schema\.rb}).empty? +geo_db_schema_updated = !git.modified_files.grep(%r{\Aee/db/geo/schema\.rb}).empty? non_geo_migration_created = !git.added_files.grep(%r{\A(db/(post_)?migrate)/}).empty? geo_migration_created = !git.added_files.grep(%r{\Aee/db/geo/(post_)?migrate/}).empty? diff --git a/danger/frozen_string/Dangerfile b/danger/frozen_string/Dangerfile index 595176d597d..b9687ef6b83 100644 --- a/danger/frozen_string/Dangerfile +++ b/danger/frozen_string/Dangerfile @@ -6,22 +6,21 @@ MAGIC_COMMENT = "# frozen_string_literal: true" def get_files_with_no_magic_comment(files) files.select do |file| file.end_with?(FILE_EXTENSION) && - !File.open(file, &:gets).start_with?(MAGIC_COMMENT) + !File.open(file, &:gets)&.start_with?(MAGIC_COMMENT) end end -files_to_check = git.added_files -files_to_fix = get_files_with_no_magic_comment(files_to_check) +files_to_fix = get_files_with_no_magic_comment(git.added_files) if files_to_fix.any? warn 'This merge request adds files that do not enforce frozen string literal. ' \ 'See https://gitlab.com/gitlab-org/gitlab-ce/issues/47424 for more information.' markdown(<<~MARKDOWN) -## Enable Frozen String Literal + ## Enable Frozen String Literal -The following files should have `#{MAGIC_COMMENT}` in the first line: + The following files should have `#{MAGIC_COMMENT}` on the first line: -* #{files_to_fix.map { |path| "`#{path}`" }.join("\n* ")} + * #{files_to_fix.map { |path| "`#{path}`" }.join("\n* ")} MARKDOWN end diff --git a/db/migrate/20140313092127_init_schema.rb b/db/migrate/20140313092127_init_schema.rb index 895298ed4ed..29fb386ad76 100644 --- a/db/migrate/20140313092127_init_schema.rb +++ b/db/migrate/20140313092127_init_schema.rb @@ -1,6 +1,7 @@ class InitSchema < ActiveRecord::Migration DOWNTIME = true + # rubocop:disable Metrics/AbcSize def up create_table "broadcast_messages", force: :cascade do |t| t.text "message", null: false @@ -157,9 +158,9 @@ class InitSchema < ActiveRecord::Migration add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree add_index "notes", ["commit_id"], name: "index_notes_on_commit_id", using: :btree add_index "notes", ["created_at"], name: "index_notes_on_created_at", using: :btree - add_index "notes", ["noteable_id", "noteable_type"], name: "index_notes_on_noteable_id_and_noteable_type", using: :btree + add_index "notes", %w[noteable_id noteable_type], name: "index_notes_on_noteable_id_and_noteable_type", using: :btree add_index "notes", ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree - add_index "notes", ["project_id", "noteable_type"], name: "index_notes_on_project_id_and_noteable_type", using: :btree + add_index "notes", %w[project_id noteable_type], name: "index_notes_on_project_id_and_noteable_type", using: :btree add_index "notes", ["project_id"], name: "index_notes_on_project_id", using: :btree create_table "project_group_links", force: :cascade do |t| t.integer "project_id", null: false @@ -241,7 +242,7 @@ class InitSchema < ActiveRecord::Migration t.datetime "created_at" end add_index "taggings", ["tag_id"], name: "index_taggings_on_tag_id", using: :btree - add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree + add_index "taggings", %w[taggable_id taggable_type context], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree create_table "tags", force: :cascade do |t| t.string "name" end @@ -292,7 +293,7 @@ class InitSchema < ActiveRecord::Migration add_index "users", ["authentication_token"], name: "index_users_on_authentication_token", unique: true, using: :btree add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree - add_index "users", ["extern_uid", "provider"], name: "index_users_on_extern_uid_and_provider", unique: true, using: :btree + add_index "users", %w[extern_uid provider], name: "index_users_on_extern_uid_and_provider", unique: true, using: :btree add_index "users", ["name"], name: "index_users_on_name", using: :btree add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree add_index "users", ["username"], name: "index_users_on_username", using: :btree diff --git a/db/migrate/20140407135544_fix_namespaces.rb b/db/migrate/20140407135544_fix_namespaces.rb index 0026ce645a6..3d2ca5c13c4 100644 --- a/db/migrate/20140407135544_fix_namespaces.rb +++ b/db/migrate/20140407135544_fix_namespaces.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class FixNamespaces < ActiveRecord::Migration DOWNTIME = false diff --git a/db/migrate/20140415124820_limits_to_mysql.rb b/db/migrate/20140415124820_limits_to_mysql.rb index c712423bcd1..3f6e62617c5 100644 --- a/db/migrate/20140415124820_limits_to_mysql.rb +++ b/db/migrate/20140415124820_limits_to_mysql.rb @@ -1,2 +1 @@ -# rubocop:disable all require_relative 'limits_to_mysql' diff --git a/db/migrate/20140729145339_migrate_project_tags.rb b/db/migrate/20140729145339_migrate_project_tags.rb index ac46847f3e6..5760e4bfeaa 100644 --- a/db/migrate/20140729145339_migrate_project_tags.rb +++ b/db/migrate/20140729145339_migrate_project_tags.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class MigrateProjectTags < ActiveRecord::Migration def up ActsAsTaggableOn::Tagging.where(taggable_type: 'Project', context: 'labels').update_all(context: 'tags') diff --git a/db/migrate/20141121133009_add_timestamps_to_members.rb b/db/migrate/20141121133009_add_timestamps_to_members.rb index 68f164cd35d..ef6d4dedf32 100644 --- a/db/migrate/20141121133009_add_timestamps_to_members.rb +++ b/db/migrate/20141121133009_add_timestamps_to_members.rb @@ -1,4 +1,3 @@ -# rubocop:disable all # In 20140914145549_migrate_to_new_members_model.rb we forgot to set the # created_at and updated_at times for new records in the 'members' table. This # became a problem after commit c8e78d972a5a628870eefca0f2ccea0199c55bda which diff --git a/db/migrate/20141223135007_add_import_data_to_project_table.rb b/db/migrate/20141223135007_add_import_data_to_project_table.rb index 9c8a483e4d5..5db78f94cc9 100644 --- a/db/migrate/20141223135007_add_import_data_to_project_table.rb +++ b/db/migrate/20141223135007_add_import_data_to_project_table.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddImportDataToProjectTable < ActiveRecord::Migration def change add_column :projects, :import_type, :string diff --git a/db/migrate/20150116234544_add_home_page_url_for_application_settings.rb b/db/migrate/20150116234544_add_home_page_url_for_application_settings.rb index 10e6549c729..aa179ce3a4d 100644 --- a/db/migrate/20150116234544_add_home_page_url_for_application_settings.rb +++ b/db/migrate/20150116234544_add_home_page_url_for_application_settings.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddHomePageUrlForApplicationSettings < ActiveRecord::Migration def change add_column :application_settings, :home_page_url, :string diff --git a/db/migrate/20150116234545_add_gitlab_access_token_to_user.rb b/db/migrate/20150116234545_add_gitlab_access_token_to_user.rb index e083973615a..c28ba3197ac 100644 --- a/db/migrate/20150116234545_add_gitlab_access_token_to_user.rb +++ b/db/migrate/20150116234545_add_gitlab_access_token_to_user.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddGitlabAccessTokenToUser < ActiveRecord::Migration def change add_column :users, :gitlab_access_token, :string diff --git a/db/migrate/20150206222854_add_notification_email_to_user.rb b/db/migrate/20150206222854_add_notification_email_to_user.rb index ebae092cac8..ab80f7e582f 100644 --- a/db/migrate/20150206222854_add_notification_email_to_user.rb +++ b/db/migrate/20150206222854_add_notification_email_to_user.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddNotificationEmailToUser < ActiveRecord::Migration def up add_column :users, :notification_email, :string diff --git a/db/migrate/20150211174341_allow_null_in_services_project_id.rb b/db/migrate/20150211174341_allow_null_in_services_project_id.rb index fea95c79adf..68f02812791 100644 --- a/db/migrate/20150211174341_allow_null_in_services_project_id.rb +++ b/db/migrate/20150211174341_allow_null_in_services_project_id.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AllowNullInServicesProjectId < ActiveRecord::Migration def change change_column :services, :project_id, :integer, null: true diff --git a/db/migrate/20150217123345_add_bitbucket_access_token_and_secret_to_user.rb b/db/migrate/20150217123345_add_bitbucket_access_token_and_secret_to_user.rb index 78e9fd0c3a9..23ac1b399ec 100644 --- a/db/migrate/20150217123345_add_bitbucket_access_token_and_secret_to_user.rb +++ b/db/migrate/20150217123345_add_bitbucket_access_token_and_secret_to_user.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddBitbucketAccessTokenAndSecretToUser < ActiveRecord::Migration def change add_column :users, :bitbucket_access_token, :string diff --git a/db/migrate/20150223022001_set_missing_last_activity_at.rb b/db/migrate/20150223022001_set_missing_last_activity_at.rb index 300381ad65b..3f6d4d83474 100644 --- a/db/migrate/20150223022001_set_missing_last_activity_at.rb +++ b/db/migrate/20150223022001_set_missing_last_activity_at.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class SetMissingLastActivityAt < ActiveRecord::Migration def up execute "UPDATE projects SET last_activity_at = updated_at WHERE last_activity_at IS NULL" diff --git a/db/migrate/20150301014758_add_restricted_visibility_levels_to_application_settings.rb b/db/migrate/20150301014758_add_restricted_visibility_levels_to_application_settings.rb index 7d8d65ef2ee..494c3033bff 100644 --- a/db/migrate/20150301014758_add_restricted_visibility_levels_to_application_settings.rb +++ b/db/migrate/20150301014758_add_restricted_visibility_levels_to_application_settings.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddRestrictedVisibilityLevelsToApplicationSettings < ActiveRecord::Migration def change add_column :application_settings, :restricted_visibility_levels, :text diff --git a/db/migrate/20150320234437_add_location_to_user.rb b/db/migrate/20150320234437_add_location_to_user.rb index df046570361..32731d37d75 100644 --- a/db/migrate/20150320234437_add_location_to_user.rb +++ b/db/migrate/20150320234437_add_location_to_user.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddLocationToUser < ActiveRecord::Migration def change add_column :users, :location, :string diff --git a/db/migrate/20150324155957_set_incorrect_assignee_id_to_null.rb b/db/migrate/20150324155957_set_incorrect_assignee_id_to_null.rb index 9f8b6f4bd59..42dc8173e46 100644 --- a/db/migrate/20150324155957_set_incorrect_assignee_id_to_null.rb +++ b/db/migrate/20150324155957_set_incorrect_assignee_id_to_null.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class SetIncorrectAssigneeIdToNull < ActiveRecord::Migration def up execute "UPDATE issues SET assignee_id = NULL WHERE assignee_id = -1" diff --git a/db/migrate/20150327150017_add_import_data_to_project.rb b/db/migrate/20150327150017_add_import_data_to_project.rb index 67b1554dfd1..12c00339eec 100644 --- a/db/migrate/20150327150017_add_import_data_to_project.rb +++ b/db/migrate/20150327150017_add_import_data_to_project.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddImportDataToProject < ActiveRecord::Migration def change add_column :projects, :import_data, :text diff --git a/db/migrate/20150327223628_add_devise_two_factor_to_users.rb b/db/migrate/20150327223628_add_devise_two_factor_to_users.rb index eccb0123e77..11b026ee8f3 100644 --- a/db/migrate/20150327223628_add_devise_two_factor_to_users.rb +++ b/db/migrate/20150327223628_add_devise_two_factor_to_users.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddDeviseTwoFactorToUsers < ActiveRecord::Migration def change add_column :users, :encrypted_otp_secret, :string diff --git a/db/migrate/20150328132231_add_max_attachment_size_to_application_settings.rb b/db/migrate/20150328132231_add_max_attachment_size_to_application_settings.rb index 4c56a2fb78b..1d161674a9a 100644 --- a/db/migrate/20150328132231_add_max_attachment_size_to_application_settings.rb +++ b/db/migrate/20150328132231_add_max_attachment_size_to_application_settings.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddMaxAttachmentSizeToApplicationSettings < ActiveRecord::Migration def change add_column :application_settings, :max_attachment_size, :integer, default: 10, null: false diff --git a/db/migrate/20150331183602_add_devise_two_factor_backupable_to_users.rb b/db/migrate/20150331183602_add_devise_two_factor_backupable_to_users.rb index fdb6d72917e..913958db7c5 100644 --- a/db/migrate/20150331183602_add_devise_two_factor_backupable_to_users.rb +++ b/db/migrate/20150331183602_add_devise_two_factor_backupable_to_users.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddDeviseTwoFactorBackupableToUsers < ActiveRecord::Migration def change add_column :users, :otp_backup_codes, :text diff --git a/db/migrate/20150411000035_fix_identities.rb b/db/migrate/20150411000035_fix_identities.rb index a10fcc001f4..d9051f9fffd 100644 --- a/db/migrate/20150411000035_fix_identities.rb +++ b/db/migrate/20150411000035_fix_identities.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class FixIdentities < ActiveRecord::Migration def up # Up until now, legacy 'ldap' references in the database were charitably diff --git a/db/migrate/20150411180045_rename_buildbox_service.rb b/db/migrate/20150411180045_rename_buildbox_service.rb index 9f3b25c3971..5a0b5d07e50 100644 --- a/db/migrate/20150411180045_rename_buildbox_service.rb +++ b/db/migrate/20150411180045_rename_buildbox_service.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class RenameBuildboxService < ActiveRecord::Migration def up execute "UPDATE services SET type = 'BuildkiteService' WHERE type = 'BuildboxService';" diff --git a/db/migrate/20150417121913_create_project_import_data.rb b/db/migrate/20150417121913_create_project_import_data.rb index fc357cbacc8..c78f5fde85e 100644 --- a/db/migrate/20150417121913_create_project_import_data.rb +++ b/db/migrate/20150417121913_create_project_import_data.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class CreateProjectImportData < ActiveRecord::Migration def change create_table :project_import_data do |t| diff --git a/db/migrate/20150423033240_add_default_project_visibililty_to_application_settings.rb b/db/migrate/20150423033240_add_default_project_visibililty_to_application_settings.rb index 129ce4d04af..50a9b2439e0 100644 --- a/db/migrate/20150423033240_add_default_project_visibililty_to_application_settings.rb +++ b/db/migrate/20150423033240_add_default_project_visibililty_to_application_settings.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddDefaultProjectVisibililtyToApplicationSettings < ActiveRecord::Migration def up add_column :application_settings, :default_project_visibility, :integer diff --git a/db/migrate/20150425164646_gitlab_change_collation_for_tag_names.acts_as_taggable_on_engine.rb b/db/migrate/20150425164646_gitlab_change_collation_for_tag_names.acts_as_taggable_on_engine.rb index 8f352414ffd..281c88d2a7d 100644 --- a/db/migrate/20150425164646_gitlab_change_collation_for_tag_names.acts_as_taggable_on_engine.rb +++ b/db/migrate/20150425164646_gitlab_change_collation_for_tag_names.acts_as_taggable_on_engine.rb @@ -1,4 +1,3 @@ -# rubocop:disable all # This migration is a duplicate of 20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb # It shold be applied before the index additions to ensure that `name` is case sensitive. diff --git a/db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb b/db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb index 88829b87711..71f2d7f4330 100644 --- a/db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb +++ b/db/migrate/20150425164650_add_missing_taggable_index.acts_as_taggable_on_engine.rb @@ -1,4 +1,3 @@ -# rubocop:disable all # This migration comes from acts_as_taggable_on_engine (originally 4) class AddMissingTaggableIndex < ActiveRecord::Migration def self.up diff --git a/db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb b/db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb index 642c4745321..bfb06bc7cda 100644 --- a/db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb +++ b/db/migrate/20150425164651_change_collation_for_tag_names.acts_as_taggable_on_engine.rb @@ -1,4 +1,3 @@ -# rubocop:disable all # This migration comes from acts_as_taggable_on_engine (originally 5) # This migration is added to circumvent issue #623 and have special characters # work properly diff --git a/db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb b/db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb index dd13def4176..8f1b0cc8935 100644 --- a/db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb +++ b/db/migrate/20150425173433_add_default_snippet_visibility_to_app_settings.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddDefaultSnippetVisibilityToAppSettings < ActiveRecord::Migration def up add_column :application_settings, :default_snippet_visibility, :integer diff --git a/db/migrate/20150429002313_remove_abandoned_group_members_records.rb b/db/migrate/20150429002313_remove_abandoned_group_members_records.rb index d2c7f3c442e..244637e1c4a 100644 --- a/db/migrate/20150429002313_remove_abandoned_group_members_records.rb +++ b/db/migrate/20150429002313_remove_abandoned_group_members_records.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class RemoveAbandonedGroupMembersRecords < ActiveRecord::Migration def up execute("DELETE FROM members WHERE type = 'GroupMember' AND source_id NOT IN(\ diff --git a/db/migrate/20150502064022_add_restricted_signup_domains_to_application_settings.rb b/db/migrate/20150502064022_add_restricted_signup_domains_to_application_settings.rb index b63ea9aec7a..184e2653610 100644 --- a/db/migrate/20150502064022_add_restricted_signup_domains_to_application_settings.rb +++ b/db/migrate/20150502064022_add_restricted_signup_domains_to_application_settings.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddRestrictedSignupDomainsToApplicationSettings < ActiveRecord::Migration def change add_column :application_settings, :restricted_signup_domains, :text diff --git a/db/migrate/20150509180749_convert_legacy_reference_notes.rb b/db/migrate/20150509180749_convert_legacy_reference_notes.rb index cd8bf90108d..b02605489be 100644 --- a/db/migrate/20150509180749_convert_legacy_reference_notes.rb +++ b/db/migrate/20150509180749_convert_legacy_reference_notes.rb @@ -1,4 +1,3 @@ -# rubocop:disable all # Convert legacy Markdown-emphasized notes to the current, non-emphasized format # # _mentioned in 54f7727c850972f0401c1312a7c4a6a380de5666_ diff --git a/db/migrate/20150529111607_add_user_oauth_applications_to_application_settings.rb b/db/migrate/20150529111607_add_user_oauth_applications_to_application_settings.rb index 9b02eda56ab..6a78294f0b2 100644 --- a/db/migrate/20150529111607_add_user_oauth_applications_to_application_settings.rb +++ b/db/migrate/20150529111607_add_user_oauth_applications_to_application_settings.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddUserOauthApplicationsToApplicationSettings < ActiveRecord::Migration def change add_column :application_settings, :user_oauth_applications, :bool, default: true diff --git a/db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb b/db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb index 1f5cf1fe5f1..61ff0af41f4 100644 --- a/db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb +++ b/db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddSessionExpireDelayForApplicationSettings < ActiveRecord::Migration def change unless column_exists?(:application_settings, :session_expire_delay) diff --git a/db/migrate/20150620233230_add_default_otp_required_for_login_value.rb b/db/migrate/20150620233230_add_default_otp_required_for_login_value.rb index da0fd457a34..8eed8678b2f 100644 --- a/db/migrate/20150620233230_add_default_otp_required_for_login_value.rb +++ b/db/migrate/20150620233230_add_default_otp_required_for_login_value.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddDefaultOtpRequiredForLoginValue < ActiveRecord::Migration def up execute %q{UPDATE users SET otp_required_for_login = FALSE WHERE otp_required_for_login IS NULL} diff --git a/db/migrate/20150730122406_add_updated_by_to_issuables_and_notes.rb b/db/migrate/20150730122406_add_updated_by_to_issuables_and_notes.rb index be30e881c74..78d45c7f96b 100644 --- a/db/migrate/20150730122406_add_updated_by_to_issuables_and_notes.rb +++ b/db/migrate/20150730122406_add_updated_by_to_issuables_and_notes.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddUpdatedByToIssuablesAndNotes < ActiveRecord::Migration def change add_column :notes, :updated_by_id, :integer diff --git a/db/migrate/20150818213832_add_sent_notifications.rb b/db/migrate/20150818213832_add_sent_notifications.rb index fa0c3ce0acf..43e8d6a1a82 100644 --- a/db/migrate/20150818213832_add_sent_notifications.rb +++ b/db/migrate/20150818213832_add_sent_notifications.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddSentNotifications < ActiveRecord::Migration def change create_table :sent_notifications do |t| diff --git a/db/migrate/20150915001905_enable_ssl_verification_by_default.rb b/db/migrate/20150915001905_enable_ssl_verification_by_default.rb index 3f070139418..6e924262a13 100644 --- a/db/migrate/20150915001905_enable_ssl_verification_by_default.rb +++ b/db/migrate/20150915001905_enable_ssl_verification_by_default.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class EnableSslVerificationByDefault < ActiveRecord::Migration def change change_column :web_hooks, :enable_ssl_verification, :boolean, default: true diff --git a/db/migrate/20150916000405_enable_ssl_verification_for_web_hooks.rb b/db/migrate/20150916000405_enable_ssl_verification_for_web_hooks.rb index ea2ab6e4093..90ce6c2db3d 100644 --- a/db/migrate/20150916000405_enable_ssl_verification_for_web_hooks.rb +++ b/db/migrate/20150916000405_enable_ssl_verification_for_web_hooks.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class EnableSslVerificationForWebHooks < ActiveRecord::Migration def up execute("UPDATE web_hooks SET enable_ssl_verification = true") diff --git a/db/migrate/20150916114643_add_help_page_text_to_application_settings.rb b/db/migrate/20150916114643_add_help_page_text_to_application_settings.rb index a504f25b1be..37a27f11935 100644 --- a/db/migrate/20150916114643_add_help_page_text_to_application_settings.rb +++ b/db/migrate/20150916114643_add_help_page_text_to_application_settings.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddHelpPageTextToApplicationSettings < ActiveRecord::Migration def change add_column :application_settings, :help_page_text, :text diff --git a/db/migrate/20150918084513_add_ci_enabled_to_application_settings.rb b/db/migrate/20150918084513_add_ci_enabled_to_application_settings.rb index c9b6e035122..6cf668a170e 100644 --- a/db/migrate/20150918084513_add_ci_enabled_to_application_settings.rb +++ b/db/migrate/20150918084513_add_ci_enabled_to_application_settings.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddCiEnabledToApplicationSettings < ActiveRecord::Migration def change add_column :application_settings, :ci_enabled, :boolean, null: false, default: true diff --git a/db/migrate/20150918161719_remove_invalid_milestones_from_merge_requests.rb b/db/migrate/20150918161719_remove_invalid_milestones_from_merge_requests.rb index e1818b566d7..0aad6fe5e6e 100644 --- a/db/migrate/20150918161719_remove_invalid_milestones_from_merge_requests.rb +++ b/db/migrate/20150918161719_remove_invalid_milestones_from_merge_requests.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class RemoveInvalidMilestonesFromMergeRequests < ActiveRecord::Migration def up execute("UPDATE merge_requests SET milestone_id = NULL where milestone_id NOT IN (SELECT id FROM milestones)") diff --git a/db/migrate/20150920010715_add_consumed_timestep_to_users.rb b/db/migrate/20150920010715_add_consumed_timestep_to_users.rb index e6975f5b9fe..c8438b3f6aa 100644 --- a/db/migrate/20150920010715_add_consumed_timestep_to_users.rb +++ b/db/migrate/20150920010715_add_consumed_timestep_to_users.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddConsumedTimestepToUsers < ActiveRecord::Migration def change add_column :users, :consumed_timestep, :integer diff --git a/db/migrate/20150920161119_add_line_code_to_sent_notification.rb b/db/migrate/20150920161119_add_line_code_to_sent_notification.rb index 1bcb06e4bda..d9af4e71751 100644 --- a/db/migrate/20150920161119_add_line_code_to_sent_notification.rb +++ b/db/migrate/20150920161119_add_line_code_to_sent_notification.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddLineCodeToSentNotification < ActiveRecord::Migration def change add_column :sent_notifications, :line_code, :string diff --git a/db/migrate/20150924125150_add_project_id_to_ci_commit.rb b/db/migrate/20150924125150_add_project_id_to_ci_commit.rb index 905332b7dc7..1a761fe0f86 100644 --- a/db/migrate/20150924125150_add_project_id_to_ci_commit.rb +++ b/db/migrate/20150924125150_add_project_id_to_ci_commit.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddProjectIdToCiCommit < ActiveRecord::Migration def up add_column :ci_commits, :gl_project_id, :integer diff --git a/db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb b/db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb index fb0e0ba1fa5..2be57b6062e 100644 --- a/db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb +++ b/db/migrate/20150924125436_migrate_project_id_for_ci_commits.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class MigrateProjectIdForCiCommits < ActiveRecord::Migration def up subquery = 'SELECT gitlab_id FROM ci_projects WHERE ci_projects.id = ci_commits.project_id' diff --git a/db/migrate/20150930001110_merge_request_error_field.rb b/db/migrate/20150930001110_merge_request_error_field.rb index 71a8ae3938a..c2ee498ef3f 100644 --- a/db/migrate/20150930001110_merge_request_error_field.rb +++ b/db/migrate/20150930001110_merge_request_error_field.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class MergeRequestErrorField < ActiveRecord::Migration def up add_column :merge_requests, :merge_error, :string diff --git a/db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb b/db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb index 229c9942b50..8d47dac6441 100644 --- a/db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb +++ b/db/migrate/20150930095736_add_null_to_name_for_ci_projects.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddNullToNameForCiProjects < ActiveRecord::Migration def up change_column_null :ci_projects, :name, true diff --git a/db/migrate/20151002112914_add_stage_idx_to_builds.rb b/db/migrate/20151002112914_add_stage_idx_to_builds.rb index 4297ba0e7c8..68a745ffef4 100644 --- a/db/migrate/20151002112914_add_stage_idx_to_builds.rb +++ b/db/migrate/20151002112914_add_stage_idx_to_builds.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddStageIdxToBuilds < ActiveRecord::Migration def change add_column :ci_builds, :stage_idx, :integer diff --git a/db/migrate/20151002122929_add_ref_and_tag_to_builds.rb b/db/migrate/20151002122929_add_ref_and_tag_to_builds.rb index 3c0fcf6c45d..e3d2ac1cea5 100644 --- a/db/migrate/20151002122929_add_ref_and_tag_to_builds.rb +++ b/db/migrate/20151002122929_add_ref_and_tag_to_builds.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddRefAndTagToBuilds < ActiveRecord::Migration def change add_column :ci_builds, :tag, :boolean diff --git a/db/migrate/20151005075649_add_user_id_to_build.rb b/db/migrate/20151005075649_add_user_id_to_build.rb index be9d403e002..0f4b92b8b79 100644 --- a/db/migrate/20151005075649_add_user_id_to_build.rb +++ b/db/migrate/20151005075649_add_user_id_to_build.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddUserIdToBuild < ActiveRecord::Migration def change add_column :ci_builds, :user_id, :integer diff --git a/db/migrate/20151008143519_add_admin_notification_email_setting.rb b/db/migrate/20151008143519_add_admin_notification_email_setting.rb index f48ec9aa4a6..0bb581efe2c 100644 --- a/db/migrate/20151008143519_add_admin_notification_email_setting.rb +++ b/db/migrate/20151008143519_add_admin_notification_email_setting.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddAdminNotificationEmailSetting < ActiveRecord::Migration def change add_column :application_settings, :admin_notification_email, :string diff --git a/db/migrate/20151013092124_add_artifacts_file_to_builds.rb b/db/migrate/20151013092124_add_artifacts_file_to_builds.rb index a54ac9d57a4..5a299f7b26d 100644 --- a/db/migrate/20151013092124_add_artifacts_file_to_builds.rb +++ b/db/migrate/20151013092124_add_artifacts_file_to_builds.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddArtifactsFileToBuilds < ActiveRecord::Migration def change add_column :ci_builds, :artifacts_file, :text diff --git a/db/migrate/20151019111551_fix_build_tags.rb b/db/migrate/20151019111551_fix_build_tags.rb index 8c05acfc190..299a24b0a7c 100644 --- a/db/migrate/20151019111551_fix_build_tags.rb +++ b/db/migrate/20151019111551_fix_build_tags.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class FixBuildTags < ActiveRecord::Migration def up execute("UPDATE taggings SET taggable_type='CommitStatus' WHERE taggable_type='Ci::Build'") diff --git a/db/migrate/20151019111703_fail_build_without_names.rb b/db/migrate/20151019111703_fail_build_without_names.rb index 362e31eb435..dcdb5d1b25d 100644 --- a/db/migrate/20151019111703_fail_build_without_names.rb +++ b/db/migrate/20151019111703_fail_build_without_names.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class FailBuildWithoutNames < ActiveRecord::Migration def up execute("UPDATE ci_builds SET status='failed' WHERE name IS NULL AND status='pending'") diff --git a/db/migrate/20151020173516_ci_limits_to_mysql.rb b/db/migrate/20151020173516_ci_limits_to_mysql.rb index 5314611cbcd..9bb960082f5 100644 --- a/db/migrate/20151020173516_ci_limits_to_mysql.rb +++ b/db/migrate/20151020173516_ci_limits_to_mysql.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class CiLimitsToMysql < ActiveRecord::Migration def change return unless ActiveRecord::Base.configurations[Rails.env]['adapter'] =~ /^mysql/ diff --git a/db/migrate/20151023112551_fail_build_with_empty_name.rb b/db/migrate/20151023112551_fail_build_with_empty_name.rb index 0666dfeaef4..41c0f0649cd 100644 --- a/db/migrate/20151023112551_fail_build_with_empty_name.rb +++ b/db/migrate/20151023112551_fail_build_with_empty_name.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class FailBuildWithEmptyName < ActiveRecord::Migration def up execute("UPDATE ci_builds SET status='failed' WHERE (name IS NULL OR name='') AND status='pending'") diff --git a/db/migrate/20151023144219_remove_satellites.rb b/db/migrate/20151023144219_remove_satellites.rb index 98fe0bd7d1d..e73f300028a 100644 --- a/db/migrate/20151023144219_remove_satellites.rb +++ b/db/migrate/20151023144219_remove_satellites.rb @@ -1,4 +1,3 @@ -# rubocop:disable all require 'fileutils' class RemoveSatellites < ActiveRecord::Migration diff --git a/db/migrate/20151103133339_add_shared_runners_setting.rb b/db/migrate/20151103133339_add_shared_runners_setting.rb index b5b34d4ca61..4231dfd5c2e 100644 --- a/db/migrate/20151103133339_add_shared_runners_setting.rb +++ b/db/migrate/20151103133339_add_shared_runners_setting.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddSharedRunnersSetting < ActiveRecord::Migration def up add_column :application_settings, :shared_runners_enabled, :boolean, default: true, null: false diff --git a/db/migrate/20151104105513_add_file_to_lfs_objects.rb b/db/migrate/20151104105513_add_file_to_lfs_objects.rb index 4e46ae8101c..7c57f3f0df6 100644 --- a/db/migrate/20151104105513_add_file_to_lfs_objects.rb +++ b/db/migrate/20151104105513_add_file_to_lfs_objects.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddFileToLfsObjects < ActiveRecord::Migration def change add_column :lfs_objects, :file, :string diff --git a/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb b/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb index 25106ace7e9..01d8c0f043e 100644 --- a/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb +++ b/db/migrate/20151109100728_add_max_artifacts_size_to_application_settings.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddMaxArtifactsSizeToApplicationSettings < ActiveRecord::Migration def change add_column :application_settings, :max_artifacts_size, :integer, default: 100, null: false diff --git a/db/migrate/20151110125604_add_import_error_to_project.rb b/db/migrate/20151110125604_add_import_error_to_project.rb index 793358c305e..7fc990f8d0a 100644 --- a/db/migrate/20151110125604_add_import_error_to_project.rb +++ b/db/migrate/20151110125604_add_import_error_to_project.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddImportErrorToProject < ActiveRecord::Migration def change add_column :projects, :import_error, :text diff --git a/db/migrate/20151201203948_raise_hook_url_limit.rb b/db/migrate/20151201203948_raise_hook_url_limit.rb index c490b7ace0f..98a7fca6f6f 100644 --- a/db/migrate/20151201203948_raise_hook_url_limit.rb +++ b/db/migrate/20151201203948_raise_hook_url_limit.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class RaiseHookUrlLimit < ActiveRecord::Migration def change change_column :web_hooks, :url, :string, limit: 2000 diff --git a/db/migrate/20151209144329_migrate_ci_web_hooks.rb b/db/migrate/20151209144329_migrate_ci_web_hooks.rb index 62a6d334f04..e1e4729f821 100644 --- a/db/migrate/20151209144329_migrate_ci_web_hooks.rb +++ b/db/migrate/20151209144329_migrate_ci_web_hooks.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class MigrateCiWebHooks < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers diff --git a/db/migrate/20151209145909_migrate_ci_emails.rb b/db/migrate/20151209145909_migrate_ci_emails.rb index 5de7b205fb1..e1d92f0157e 100644 --- a/db/migrate/20151209145909_migrate_ci_emails.rb +++ b/db/migrate/20151209145909_migrate_ci_emails.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class MigrateCiEmails < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers diff --git a/db/migrate/20151210030143_add_unlock_token_to_user.rb b/db/migrate/20151210030143_add_unlock_token_to_user.rb index d23c648f782..0ea66ba65df 100644 --- a/db/migrate/20151210030143_add_unlock_token_to_user.rb +++ b/db/migrate/20151210030143_add_unlock_token_to_user.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddUnlockTokenToUser < ActiveRecord::Migration def change add_column :users, :unlock_token, :string diff --git a/db/migrate/20151210072243_add_runners_registration_token_to_application_settings.rb b/db/migrate/20151210072243_add_runners_registration_token_to_application_settings.rb index 92c7b5befd2..00f88180e46 100644 --- a/db/migrate/20151210072243_add_runners_registration_token_to_application_settings.rb +++ b/db/migrate/20151210072243_add_runners_registration_token_to_application_settings.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddRunnersRegistrationTokenToApplicationSettings < ActiveRecord::Migration def change add_column :application_settings, :runners_registration_token, :string diff --git a/db/migrate/20151210125232_migrate_ci_slack_service.rb b/db/migrate/20151210125232_migrate_ci_slack_service.rb index fff130b7b10..e6dca4c0008 100644 --- a/db/migrate/20151210125232_migrate_ci_slack_service.rb +++ b/db/migrate/20151210125232_migrate_ci_slack_service.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class MigrateCiSlackService < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers diff --git a/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb b/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb index 824f6f84195..72fcebf2959 100644 --- a/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb +++ b/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class MigrateCiHipChatService < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers diff --git a/db/migrate/20151210125929_add_project_id_to_ci.rb b/db/migrate/20151210125929_add_project_id_to_ci.rb index b5de64b82ca..84273591fa2 100644 --- a/db/migrate/20151210125929_add_project_id_to_ci.rb +++ b/db/migrate/20151210125929_add_project_id_to_ci.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddProjectIdToCi < ActiveRecord::Migration def change add_column :ci_builds, :gl_project_id, :integer diff --git a/db/migrate/20151210125930_migrate_ci_to_project.rb b/db/migrate/20151210125930_migrate_ci_to_project.rb index bb6d74ae212..c32c7feb193 100644 --- a/db/migrate/20151210125930_migrate_ci_to_project.rb +++ b/db/migrate/20151210125930_migrate_ci_to_project.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class MigrateCiToProject < ActiveRecord::Migration def up migrate_project_id_for_table('ci_runner_projects') diff --git a/db/migrate/20151218154042_add_tfa_to_application_settings.rb b/db/migrate/20151218154042_add_tfa_to_application_settings.rb index afdaf76b917..dd95db775c5 100644 --- a/db/migrate/20151218154042_add_tfa_to_application_settings.rb +++ b/db/migrate/20151218154042_add_tfa_to_application_settings.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddTfaToApplicationSettings < ActiveRecord::Migration def change change_table :application_settings do |t| diff --git a/db/migrate/20151221234414_add_tfa_additional_fields.rb b/db/migrate/20151221234414_add_tfa_additional_fields.rb index c3e4aaa606a..c16df47932f 100644 --- a/db/migrate/20151221234414_add_tfa_additional_fields.rb +++ b/db/migrate/20151221234414_add_tfa_additional_fields.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddTfaAdditionalFields < ActiveRecord::Migration def change change_table :users do |t| diff --git a/db/migrate/20151224123230_rename_emojis.rb b/db/migrate/20151224123230_rename_emojis.rb index 2c24f3beeea..62d921dfdcc 100644 --- a/db/migrate/20151224123230_rename_emojis.rb +++ b/db/migrate/20151224123230_rename_emojis.rb @@ -1,4 +1,3 @@ -# rubocop:disable all # Migration type: online without errors (works on previous version and new one) class RenameEmojis < ActiveRecord::Migration def up diff --git a/db/migrate/20151228175719_add_recaptcha_to_application_settings.rb b/db/migrate/20151228175719_add_recaptcha_to_application_settings.rb index e0dd19b2b06..259fd0248d2 100644 --- a/db/migrate/20151228175719_add_recaptcha_to_application_settings.rb +++ b/db/migrate/20151228175719_add_recaptcha_to_application_settings.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddRecaptchaToApplicationSettings < ActiveRecord::Migration def change change_table :application_settings do |t| diff --git a/db/migrate/20151229102248_influxdb_udp_port_setting.rb b/db/migrate/20151229102248_influxdb_udp_port_setting.rb index 3e1bfd43899..ae0499f936d 100644 --- a/db/migrate/20151229102248_influxdb_udp_port_setting.rb +++ b/db/migrate/20151229102248_influxdb_udp_port_setting.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class InfluxdbUdpPortSetting < ActiveRecord::Migration def change add_column :application_settings, :metrics_port, :integer, default: 8089 diff --git a/db/migrate/20151230132518_add_artifacts_metadata_to_ci_build.rb b/db/migrate/20151230132518_add_artifacts_metadata_to_ci_build.rb index 4fcca06d905..6c282fc5039 100644 --- a/db/migrate/20151230132518_add_artifacts_metadata_to_ci_build.rb +++ b/db/migrate/20151230132518_add_artifacts_metadata_to_ci_build.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddArtifactsMetadataToCiBuild < ActiveRecord::Migration def change add_column :ci_builds, :artifacts_metadata, :text diff --git a/db/migrate/20151231152326_add_akismet_to_application_settings.rb b/db/migrate/20151231152326_add_akismet_to_application_settings.rb index 7b0fab6f557..3f52c758f9a 100644 --- a/db/migrate/20151231152326_add_akismet_to_application_settings.rb +++ b/db/migrate/20151231152326_add_akismet_to_application_settings.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddAkismetToApplicationSettings < ActiveRecord::Migration def change change_table :application_settings do |t| diff --git a/db/migrate/20160113111034_add_metrics_sample_interval.rb b/db/migrate/20160113111034_add_metrics_sample_interval.rb index c1041da818c..b741f5d2c75 100644 --- a/db/migrate/20160113111034_add_metrics_sample_interval.rb +++ b/db/migrate/20160113111034_add_metrics_sample_interval.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddMetricsSampleInterval < ActiveRecord::Migration def change add_column :application_settings, :metrics_sample_interval, :integer, diff --git a/db/migrate/20160118155830_add_sentry_to_application_settings.rb b/db/migrate/20160118155830_add_sentry_to_application_settings.rb index a6f715263ef..fa7ff9d9228 100644 --- a/db/migrate/20160118155830_add_sentry_to_application_settings.rb +++ b/db/migrate/20160118155830_add_sentry_to_application_settings.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddSentryToApplicationSettings < ActiveRecord::Migration def change change_table :application_settings do |t| diff --git a/db/migrate/20160120172143_add_base_commit_sha_to_merge_request_diffs.rb b/db/migrate/20160120172143_add_base_commit_sha_to_merge_request_diffs.rb index 3837208f81e..d6c6aa4a4e8 100644 --- a/db/migrate/20160120172143_add_base_commit_sha_to_merge_request_diffs.rb +++ b/db/migrate/20160120172143_add_base_commit_sha_to_merge_request_diffs.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddBaseCommitShaToMergeRequestDiffs < ActiveRecord::Migration def change add_column :merge_request_diffs, :base_commit_sha, :string diff --git a/db/migrate/20160128233227_change_lfs_objects_size_column.rb b/db/migrate/20160128233227_change_lfs_objects_size_column.rb index 645c0cdb192..e7fd1f71777 100644 --- a/db/migrate/20160128233227_change_lfs_objects_size_column.rb +++ b/db/migrate/20160128233227_change_lfs_objects_size_column.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class ChangeLfsObjectsSizeColumn < ActiveRecord::Migration def change change_column :lfs_objects, :size, :integer, limit: 8 diff --git a/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb b/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb index b10c0602e24..d3ea956952e 100644 --- a/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb +++ b/db/migrate/20160129135155_remove_dot_atom_path_ending_of_projects.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class RemoveDotAtomPathEndingOfProjects < ActiveRecord::Migration include Gitlab::ShellAdapter diff --git a/db/migrate/20160129155512_add_merge_commit_sha_to_merge_requests.rb b/db/migrate/20160129155512_add_merge_commit_sha_to_merge_requests.rb index 332b5a756e8..f0d94226514 100644 --- a/db/migrate/20160129155512_add_merge_commit_sha_to_merge_requests.rb +++ b/db/migrate/20160129155512_add_merge_commit_sha_to_merge_requests.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddMergeCommitShaToMergeRequests < ActiveRecord::Migration def change add_column :merge_requests, :merge_commit_sha, :string diff --git a/db/migrate/20160204144558_add_real_size_to_merge_request_diffs.rb b/db/migrate/20160204144558_add_real_size_to_merge_request_diffs.rb index 11b6ff31000..f996ae74dca 100644 --- a/db/migrate/20160204144558_add_real_size_to_merge_request_diffs.rb +++ b/db/migrate/20160204144558_add_real_size_to_merge_request_diffs.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddRealSizeToMergeRequestDiffs < ActiveRecord::Migration def change add_column :merge_request_diffs, :real_size, :string diff --git a/db/migrate/20160217100506_add_description_to_label.rb b/db/migrate/20160217100506_add_description_to_label.rb index af5af167470..eed6d1f236a 100644 --- a/db/migrate/20160217100506_add_description_to_label.rb +++ b/db/migrate/20160217100506_add_description_to_label.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddDescriptionToLabel < ActiveRecord::Migration def change add_column :labels, :description, :string diff --git a/db/migrate/20160217174422_add_note_to_tasks.rb b/db/migrate/20160217174422_add_note_to_tasks.rb index a9a2b77e423..da5cb2e05db 100644 --- a/db/migrate/20160217174422_add_note_to_tasks.rb +++ b/db/migrate/20160217174422_add_note_to_tasks.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddNoteToTasks < ActiveRecord::Migration def change add_reference :tasks, :note, index: true diff --git a/db/migrate/20160220123949_rename_tasks_to_todos.rb b/db/migrate/20160220123949_rename_tasks_to_todos.rb index f16b37537f3..30c10d27146 100644 --- a/db/migrate/20160220123949_rename_tasks_to_todos.rb +++ b/db/migrate/20160220123949_rename_tasks_to_todos.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class RenameTasksToTodos < ActiveRecord::Migration def change rename_table :tasks, :todos diff --git a/db/migrate/20160229193553_add_main_language_to_repository.rb b/db/migrate/20160229193553_add_main_language_to_repository.rb index ad5167b4c93..b5446c6a447 100644 --- a/db/migrate/20160229193553_add_main_language_to_repository.rb +++ b/db/migrate/20160229193553_add_main_language_to_repository.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddMainLanguageToRepository < ActiveRecord::Migration def change add_column :projects, :main_language, :string diff --git a/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb b/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb index 1f400566f9f..ffcd64266e3 100644 --- a/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb +++ b/db/migrate/20160302151724_add_import_credentials_to_project_import_data.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddImportCredentialsToProjectImportData < ActiveRecord::Migration def change add_column :project_import_data, :encrypted_credentials, :text diff --git a/db/migrate/20160307221555_disallow_blank_line_code_on_note.rb b/db/migrate/20160307221555_disallow_blank_line_code_on_note.rb index 10f2b8cc56a..49e787d9a9a 100644 --- a/db/migrate/20160307221555_disallow_blank_line_code_on_note.rb +++ b/db/migrate/20160307221555_disallow_blank_line_code_on_note.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class DisallowBlankLineCodeOnNote < ActiveRecord::Migration def up execute("UPDATE notes SET line_code = NULL WHERE line_code = ''") diff --git a/db/migrate/20160316192622_change_target_id_to_null_on_todos.rb b/db/migrate/20160316192622_change_target_id_to_null_on_todos.rb index 65e0e61c78f..6871b3920df 100644 --- a/db/migrate/20160316192622_change_target_id_to_null_on_todos.rb +++ b/db/migrate/20160316192622_change_target_id_to_null_on_todos.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class ChangeTargetIdToNullOnTodos < ActiveRecord::Migration def change change_column_null :todos, :target_id, true diff --git a/db/migrate/20160317092222_add_moved_to_to_issue.rb b/db/migrate/20160317092222_add_moved_to_to_issue.rb index 9dde668ddff..461e7fb3a9b 100644 --- a/db/migrate/20160317092222_add_moved_to_to_issue.rb +++ b/db/migrate/20160317092222_add_moved_to_to_issue.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddMovedToToIssue < ActiveRecord::Migration def change add_reference :issues, :moved_to, references: :issues diff --git a/db/migrate/20160324020319_remove_todos_for_deleted_issues.rb b/db/migrate/20160324020319_remove_todos_for_deleted_issues.rb index a9a851cfe63..1fff9759d1e 100644 --- a/db/migrate/20160324020319_remove_todos_for_deleted_issues.rb +++ b/db/migrate/20160324020319_remove_todos_for_deleted_issues.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class RemoveTodosForDeletedIssues < ActiveRecord::Migration def up execute <<-SQL diff --git a/db/migrate/20160328115649_migrate_new_notification_setting.rb b/db/migrate/20160328115649_migrate_new_notification_setting.rb index eb6b7d07219..3c81b2c37bf 100644 --- a/db/migrate/20160328115649_migrate_new_notification_setting.rb +++ b/db/migrate/20160328115649_migrate_new_notification_setting.rb @@ -1,4 +1,3 @@ -# rubocop:disable all # This migration will create one row of NotificationSetting for each Member row # It can take long time on big instances. # diff --git a/db/migrate/20160331133914_remove_todos_for_deleted_merge_requests.rb b/db/migrate/20160331133914_remove_todos_for_deleted_merge_requests.rb index b15af79b9b5..54cea964ff2 100644 --- a/db/migrate/20160331133914_remove_todos_for_deleted_merge_requests.rb +++ b/db/migrate/20160331133914_remove_todos_for_deleted_merge_requests.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class RemoveTodosForDeletedMergeRequests < ActiveRecord::Migration def up execute <<-SQL diff --git a/db/migrate/20160407120251_add_images_enabled_for_project.rb b/db/migrate/20160407120251_add_images_enabled_for_project.rb index fcffc98b47a..47f0ca8e8de 100644 --- a/db/migrate/20160407120251_add_images_enabled_for_project.rb +++ b/db/migrate/20160407120251_add_images_enabled_for_project.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddImagesEnabledForProject < ActiveRecord::Migration def change add_column :projects, :container_registry_enabled, :boolean diff --git a/db/migrate/20160413115152_add_token_to_web_hooks.rb b/db/migrate/20160413115152_add_token_to_web_hooks.rb index 628b1d51b30..f04225068cd 100644 --- a/db/migrate/20160413115152_add_token_to_web_hooks.rb +++ b/db/migrate/20160413115152_add_token_to_web_hooks.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddTokenToWebHooks < ActiveRecord::Migration def change add_column :web_hooks, :token, :string diff --git a/db/migrate/20160415062917_create_personal_access_tokens.rb b/db/migrate/20160415062917_create_personal_access_tokens.rb index c7b49870bf7..94650026994 100644 --- a/db/migrate/20160415062917_create_personal_access_tokens.rb +++ b/db/migrate/20160415062917_create_personal_access_tokens.rb @@ -1,4 +1,3 @@ -# rubocop:disable Migration/Datetime # rubocop:disable Migration/Timestamps class CreatePersonalAccessTokens < ActiveRecord::Migration def change diff --git a/db/migrate/20160415133440_add_shared_runners_text_to_application_settings.rb b/db/migrate/20160415133440_add_shared_runners_text_to_application_settings.rb index b53b9bc6c3d..d493044c67b 100644 --- a/db/migrate/20160415133440_add_shared_runners_text_to_application_settings.rb +++ b/db/migrate/20160415133440_add_shared_runners_text_to_application_settings.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddSharedRunnersTextToApplicationSettings < ActiveRecord::Migration def change add_column :application_settings, :shared_runners_text, :text diff --git a/db/migrate/20160416182152_convert_award_note_to_emoji_award.rb b/db/migrate/20160416182152_convert_award_note_to_emoji_award.rb index 95ee03611d9..50f159a80b1 100644 --- a/db/migrate/20160416182152_convert_award_note_to_emoji_award.rb +++ b/db/migrate/20160416182152_convert_award_note_to_emoji_award.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class ConvertAwardNoteToEmojiAward < ActiveRecord::Migration disable_ddl_transaction! diff --git a/db/migrate/20160419120017_add_metrics_packet_size.rb b/db/migrate/20160419120017_add_metrics_packet_size.rb index c759427c590..78c163d62ac 100644 --- a/db/migrate/20160419120017_add_metrics_packet_size.rb +++ b/db/migrate/20160419120017_add_metrics_packet_size.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddMetricsPacketSize < ActiveRecord::Migration def change add_column :application_settings, :metrics_packet_size, :integer, default: 1 diff --git a/db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb b/db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb index bf50616656c..facd33875ba 100644 --- a/db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb +++ b/db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddDisabledOauthSignInSourcesToApplicationSettings < ActiveRecord::Migration def change add_column :application_settings, :disabled_oauth_sign_in_sources, :text diff --git a/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb b/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb index c60892a6279..84e5e4eabe2 100644 --- a/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb +++ b/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddRunUntaggedToCiRunner < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers disable_ddl_transaction! diff --git a/db/migrate/20160508215820_add_type_to_notes.rb b/db/migrate/20160508215820_add_type_to_notes.rb index c1d07c9363f..58944d4e651 100644 --- a/db/migrate/20160508215820_add_type_to_notes.rb +++ b/db/migrate/20160508215820_add_type_to_notes.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddTypeToNotes < ActiveRecord::Migration def change add_column :notes, :type, :string diff --git a/db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb b/db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb index b6a5bea79b6..9d729fec189 100644 --- a/db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb +++ b/db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddHealthCheckAccessTokenToApplicationSettings < ActiveRecord::Migration def change add_column :application_settings, :health_check_access_token, :string diff --git a/db/migrate/20160527020117_remove_notification_settings_for_deleted_projects.rb b/db/migrate/20160527020117_remove_notification_settings_for_deleted_projects.rb index 3e26be7c09c..7910120b4e0 100644 --- a/db/migrate/20160527020117_remove_notification_settings_for_deleted_projects.rb +++ b/db/migrate/20160527020117_remove_notification_settings_for_deleted_projects.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class RemoveNotificationSettingsForDeletedProjects < ActiveRecord::Migration def up execute <<-SQL diff --git a/db/migrate/20160530150109_add_container_registry_token_expire_delay_to_application_settings.rb b/db/migrate/20160530150109_add_container_registry_token_expire_delay_to_application_settings.rb index d811fd5271e..e21376bd571 100644 --- a/db/migrate/20160530150109_add_container_registry_token_expire_delay_to_application_settings.rb +++ b/db/migrate/20160530150109_add_container_registry_token_expire_delay_to_application_settings.rb @@ -1,4 +1,3 @@ -# rubocop:disable all # This is ONLINE migration class AddContainerRegistryTokenExpireDelayToApplicationSettings < ActiveRecord::Migration diff --git a/db/migrate/20160603180330_remove_duplicated_notification_settings.rb b/db/migrate/20160603180330_remove_duplicated_notification_settings.rb index 4f4f58b1619..fe1c863b5b9 100644 --- a/db/migrate/20160603180330_remove_duplicated_notification_settings.rb +++ b/db/migrate/20160603180330_remove_duplicated_notification_settings.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class RemoveDuplicatedNotificationSettings < ActiveRecord::Migration def up duplicates = exec_query(%Q{ diff --git a/db/migrate/20160608155312_add_after_sign_up_text_to_application_settings.rb b/db/migrate/20160608155312_add_after_sign_up_text_to_application_settings.rb index 3c5d2ad910e..89826fb96cb 100644 --- a/db/migrate/20160608155312_add_after_sign_up_text_to_application_settings.rb +++ b/db/migrate/20160608155312_add_after_sign_up_text_to_application_settings.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class AddAfterSignUpTextToApplicationSettings < ActiveRecord::Migration def change add_column :application_settings, :after_sign_up_text, :text diff --git a/db/migrate/20160610204157_add_deployments.rb b/db/migrate/20160610204157_add_deployments.rb index 0e7e6e747a3..0ee0b1f5a86 100644 --- a/db/migrate/20160610204157_add_deployments.rb +++ b/db/migrate/20160610204157_add_deployments.rb @@ -1,7 +1,5 @@ # See http://doc.gitlab.com/ce/development/migration_style_guide.html # for more information on how to write migrations for GitLab. - -# rubocop:disable Migration/Datetime class AddDeployments < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers diff --git a/db/migrate/20160610204158_add_environments.rb b/db/migrate/20160610204158_add_environments.rb index 699cee2b246..534a73a5fb6 100644 --- a/db/migrate/20160610204158_add_environments.rb +++ b/db/migrate/20160610204158_add_environments.rb @@ -1,7 +1,5 @@ # See http://doc.gitlab.com/ce/development/migration_style_guide.html # for more information on how to write migrations for GitLab. - -# rubocop:disable Migration/Datetime class AddEnvironments < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers diff --git a/db/migrate/20160615173316_add_enabled_git_access_protocols_to_application_settings.rb b/db/migrate/20160615173316_add_enabled_git_access_protocols_to_application_settings.rb index 013904b3f4f..d0e6d8d1ea1 100644 --- a/db/migrate/20160615173316_add_enabled_git_access_protocols_to_application_settings.rb +++ b/db/migrate/20160615173316_add_enabled_git_access_protocols_to_application_settings.rb @@ -1,6 +1,5 @@ # See http://doc.gitlab.com/ce/development/migration_style_guide.html # for more information on how to write migrations for GitLab. -# rubocop:disable all class AddEnabledGitAccessProtocolsToApplicationSettings < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers diff --git a/db/migrate/20160616102642_remove_duplicated_keys.rb b/db/migrate/20160616102642_remove_duplicated_keys.rb index 180a75e0998..5e41cc53e32 100644 --- a/db/migrate/20160616102642_remove_duplicated_keys.rb +++ b/db/migrate/20160616102642_remove_duplicated_keys.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class RemoveDuplicatedKeys < ActiveRecord::Migration def up select_all("SELECT fingerprint FROM #{quote_table_name(:keys)} GROUP BY fingerprint HAVING COUNT(*) > 1").each do |row| diff --git a/db/migrate/20160824124900_add_table_issue_metrics.rb b/db/migrate/20160824124900_add_table_issue_metrics.rb index 30d35ef1db2..49be8bc949b 100644 --- a/db/migrate/20160824124900_add_table_issue_metrics.rb +++ b/db/migrate/20160824124900_add_table_issue_metrics.rb @@ -1,7 +1,5 @@ # See http://doc.gitlab.com/ce/development/migration_style_guide.html # for more information on how to write migrations for GitLab. - -# rubocop:disable Migration/Datetime # rubocop:disable Migration/Timestamps class AddTableIssueMetrics < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers diff --git a/db/migrate/20160825052008_add_table_merge_request_metrics.rb b/db/migrate/20160825052008_add_table_merge_request_metrics.rb index 56b39634dfd..3c9dcc08190 100644 --- a/db/migrate/20160825052008_add_table_merge_request_metrics.rb +++ b/db/migrate/20160825052008_add_table_merge_request_metrics.rb @@ -1,7 +1,5 @@ # See http://doc.gitlab.com/ce/development/migration_style_guide.html # for more information on how to write migrations for GitLab. - -# rubocop:disable Migration/Datetime # rubocop:disable Migration/Timestamps class AddTableMergeRequestMetrics < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers diff --git a/db/migrate/20161113184239_create_user_chat_names_table.rb b/db/migrate/20161113184239_create_user_chat_names_table.rb index 62ccb599f2e..7bead07fd76 100644 --- a/db/migrate/20161113184239_create_user_chat_names_table.rb +++ b/db/migrate/20161113184239_create_user_chat_names_table.rb @@ -1,4 +1,3 @@ -# rubocop:disable Migration/Datetime # rubocop:disable Migration/Timestamps class CreateUserChatNamesTable < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers diff --git a/db/migrate/20170130221926_create_uploads.rb b/db/migrate/20170130221926_create_uploads.rb index 4d9fa0bb692..6f06c5dd840 100644 --- a/db/migrate/20170130221926_create_uploads.rb +++ b/db/migrate/20170130221926_create_uploads.rb @@ -1,4 +1,3 @@ -# rubocop:disable Migration/Datetime class CreateUploads < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers diff --git a/db/migrate/20170222143317_drop_ci_projects.rb b/db/migrate/20170222143317_drop_ci_projects.rb index 9973e53501c..4db8658f36f 100644 --- a/db/migrate/20170222143317_drop_ci_projects.rb +++ b/db/migrate/20170222143317_drop_ci_projects.rb @@ -1,4 +1,3 @@ -# rubocop:disable Migration/Datetime class DropCiProjects < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers diff --git a/db/migrate/20170301205639_remove_unused_ci_tables_and_columns.rb b/db/migrate/20170301205639_remove_unused_ci_tables_and_columns.rb index 0535c2ddaf2..ee802ab34ca 100644 --- a/db/migrate/20170301205639_remove_unused_ci_tables_and_columns.rb +++ b/db/migrate/20170301205639_remove_unused_ci_tables_and_columns.rb @@ -1,5 +1,4 @@ # rubocop:disable Migration/RemoveColumn -# rubocop:disable Migration/Datetime class RemoveUnusedCiTablesAndColumns < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers diff --git a/db/migrate/20170329095907_create_ci_trigger_schedules.rb b/db/migrate/20170329095907_create_ci_trigger_schedules.rb index 06a2010db23..cfcfa27ebb5 100644 --- a/db/migrate/20170329095907_create_ci_trigger_schedules.rb +++ b/db/migrate/20170329095907_create_ci_trigger_schedules.rb @@ -1,4 +1,3 @@ -# rubocop:disable Migration/Datetime class CreateCiTriggerSchedules < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers diff --git a/db/migrate/20170402231018_remove_index_for_users_current_sign_in_at.rb b/db/migrate/20170402231018_remove_index_for_users_current_sign_in_at.rb index 84635fa39b9..42c90103262 100644 --- a/db/migrate/20170402231018_remove_index_for_users_current_sign_in_at.rb +++ b/db/migrate/20170402231018_remove_index_for_users_current_sign_in_at.rb @@ -1,7 +1,5 @@ # See http://doc.gitlab.com/ce/development/migration_style_guide.html # for more information on how to write migrations for GitLab. - -# rubocop:disable RemoveIndex class RemoveIndexForUsersCurrentSignInAt < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers diff --git a/db/migrate/20170425112128_create_pipeline_schedules_table.rb b/db/migrate/20170425112128_create_pipeline_schedules_table.rb index 4f9c56a1ad8..bd15b9eef19 100644 --- a/db/migrate/20170425112128_create_pipeline_schedules_table.rb +++ b/db/migrate/20170425112128_create_pipeline_schedules_table.rb @@ -1,4 +1,3 @@ -# rubocop:disable Migration/Datetime # rubocop:disable Migration/Timestamps class CreatePipelineSchedulesTable < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers diff --git a/db/migrate/20170502091007_markdown_cache_limits_to_mysql.rb b/db/migrate/20170502091007_markdown_cache_limits_to_mysql.rb index 008a94d8334..1c5d4997d40 100644 --- a/db/migrate/20170502091007_markdown_cache_limits_to_mysql.rb +++ b/db/migrate/20170502091007_markdown_cache_limits_to_mysql.rb @@ -1,2 +1 @@ -# rubocop:disable all require_relative 'markdown_cache_limits_to_mysql' diff --git a/db/migrate/20170531202042_rename_users_ldap_email_to_external_email.rb b/db/migrate/20170531202042_rename_users_ldap_email_to_external_email.rb index 470c3b8166c..f858611d24b 100644 --- a/db/migrate/20170531202042_rename_users_ldap_email_to_external_email.rb +++ b/db/migrate/20170531202042_rename_users_ldap_email_to_external_email.rb @@ -6,6 +6,7 @@ class RenameUsersLdapEmailToExternalEmail < ActiveRecord::Migration disable_ddl_transaction! def up + # rubocop:disable Migration/UpdateLargeTable rename_column_concurrently :users, :ldap_email, :external_email end diff --git a/db/migrate/20171106171453_add_timezone_to_issues_closed_at.rb b/db/migrate/20171106171453_add_timezone_to_issues_closed_at.rb index ad540b1e509..0f1e937545b 100644 --- a/db/migrate/20171106171453_add_timezone_to_issues_closed_at.rb +++ b/db/migrate/20171106171453_add_timezone_to_issues_closed_at.rb @@ -10,6 +10,7 @@ class AddTimezoneToIssuesClosedAt < ActiveRecord::Migration disable_ddl_transaction! def up + # rubocop:disable Migration/UpdateLargeTable change_column_type_concurrently(:issues, :closed_at, :datetime_with_timezone) end diff --git a/db/migrate/20180201145907_migrate_remaining_issues_closed_at.rb b/db/migrate/20180201145907_migrate_remaining_issues_closed_at.rb index 5a36dec6a9a..36a85b61968 100644 --- a/db/migrate/20180201145907_migrate_remaining_issues_closed_at.rb +++ b/db/migrate/20180201145907_migrate_remaining_issues_closed_at.rb @@ -25,6 +25,7 @@ class MigrateRemainingIssuesClosedAt < ActiveRecord::Migration # Due to some EE merge problems some environments may not have the # "closed_at_for_type_change" column. If this is the case we have no # other option than to migrate the data _right now_. + # rubocop:disable Migration/UpdateLargeTable change_column_type_concurrently(:issues, :closed_at, :datetime_with_timezone) cleanup_concurrent_column_type_change(:issues, :closed_at) end diff --git a/db/migrate/20180214093516_create_badges.rb b/db/migrate/20180214093516_create_badges.rb index 6559f834484..a1d77328f77 100644 --- a/db/migrate/20180214093516_create_badges.rb +++ b/db/migrate/20180214093516_create_badges.rb @@ -12,6 +12,7 @@ class CreateBadges < ActiveRecord::Migration t.timestamps_with_timezone null: false end + # rubocop:disable Migration/AddConcurrentForeignKey add_foreign_key :badges, :namespaces, column: :group_id, on_delete: :cascade end end diff --git a/db/migrate/20180227182112_add_group_id_to_boards_ce.rb b/db/migrate/20180227182112_add_group_id_to_boards_ce.rb index f54dd8d7687..5b2691b3a00 100644 --- a/db/migrate/20180227182112_add_group_id_to_boards_ce.rb +++ b/db/migrate/20180227182112_add_group_id_to_boards_ce.rb @@ -8,6 +8,7 @@ class AddGroupIdToBoardsCe < ActiveRecord::Migration def up return if group_id_exists? + # rubocop:disable Migration/AddConcurrentForeignKey add_column :boards, :group_id, :integer add_foreign_key :boards, :namespaces, column: :group_id, on_delete: :cascade add_concurrent_index :boards, :group_id @@ -18,6 +19,7 @@ class AddGroupIdToBoardsCe < ActiveRecord::Migration def down return unless group_id_exists? + # rubocop:disable Migration/RemoveIndex remove_foreign_key :boards, column: :group_id remove_index :boards, :group_id if index_exists? :boards, :group_id remove_column :boards, :group_id diff --git a/db/migrate/20180302152117_ensure_foreign_keys_on_clusters_applications.rb b/db/migrate/20180302152117_ensure_foreign_keys_on_clusters_applications.rb index 8298979e96a..e6cec39e61f 100644 --- a/db/migrate/20180302152117_ensure_foreign_keys_on_clusters_applications.rb +++ b/db/migrate/20180302152117_ensure_foreign_keys_on_clusters_applications.rb @@ -8,6 +8,7 @@ class EnsureForeignKeysOnClustersApplications < ActiveRecord::Migration disable_ddl_transaction! + # rubocop:disable Cop/InBatches def up existing = Clusters::Cluster .joins(:application_ingress) diff --git a/db/migrate/20180309160427_add_partial_indexes_on_todos.rb b/db/migrate/20180309160427_add_partial_indexes_on_todos.rb index 18a5c69df1b..671fa743cec 100644 --- a/db/migrate/20180309160427_add_partial_indexes_on_todos.rb +++ b/db/migrate/20180309160427_add_partial_indexes_on_todos.rb @@ -7,15 +7,16 @@ class AddPartialIndexesOnTodos < ActiveRecord::Migration # Set this constant to true if this migration requires downtime. DOWNTIME = false - disable_ddl_transaction! + disable_ddl_transaction! + + INDEX_NAME_PENDING = "index_todos_on_user_id_and_id_pending" + INDEX_NAME_DONE = "index_todos_on_user_id_and_id_done" - INDEX_NAME_PENDING="index_todos_on_user_id_and_id_pending" - INDEX_NAME_DONE="index_todos_on_user_id_and_id_done" - def up unless index_exists?(:todos, [:user_id, :id], name: INDEX_NAME_PENDING) add_concurrent_index(:todos, [:user_id, :id], where: "state='pending'", name: INDEX_NAME_PENDING) end + unless index_exists?(:todos, [:user_id, :id], name: INDEX_NAME_DONE) add_concurrent_index(:todos, [:user_id, :id], where: "state='done'", name: INDEX_NAME_DONE) end @@ -24,5 +25,5 @@ class AddPartialIndexesOnTodos < ActiveRecord::Migration def down remove_concurrent_index(:todos, [:user_id, :id], where: "state='pending'", name: INDEX_NAME_PENDING) remove_concurrent_index(:todos, [:user_id, :id], where: "state='done'", name: INDEX_NAME_DONE) - end + end end diff --git a/db/migrate/20180408143354_rename_users_rss_token_to_feed_token.rb b/db/migrate/20180408143354_rename_users_rss_token_to_feed_token.rb index 007cbebaf1b..e852d50b25e 100644 --- a/db/migrate/20180408143354_rename_users_rss_token_to_feed_token.rb +++ b/db/migrate/20180408143354_rename_users_rss_token_to_feed_token.rb @@ -6,6 +6,7 @@ class RenameUsersRssTokenToFeedToken < ActiveRecord::Migration disable_ddl_transaction! def up + # rubocop:disable Migration/UpdateLargeTable rename_column_concurrently :users, :rss_token, :feed_token end diff --git a/db/migrate/20180417090132_add_index_constraints_to_internal_id_table.rb b/db/migrate/20180417090132_add_index_constraints_to_internal_id_table.rb index 582b89a3948..58de795472a 100644 --- a/db/migrate/20180417090132_add_index_constraints_to_internal_id_table.rb +++ b/db/migrate/20180417090132_add_index_constraints_to_internal_id_table.rb @@ -26,6 +26,7 @@ class AddIndexConstraintsToInternalIdTable < ActiveRecord::Migration end private + def replace_index(table, columns, name:) temporary_name = "#{name}_old" diff --git a/db/migrate/20180418053107_add_index_to_ci_job_artifacts_file_store.rb b/db/migrate/20180418053107_add_index_to_ci_job_artifacts_file_store.rb index 1084ca14a34..ac91624c3d5 100644 --- a/db/migrate/20180418053107_add_index_to_ci_job_artifacts_file_store.rb +++ b/db/migrate/20180418053107_add_index_to_ci_job_artifacts_file_store.rb @@ -10,6 +10,7 @@ class AddIndexToCiJobArtifactsFileStore < ActiveRecord::Migration end def down + # rubocop:disable Migration/RemoveIndex remove_index :ci_job_artifacts, :file_store if index_exists?(:ci_job_artifacts, :file_store) end end diff --git a/db/migrate/20180424090541_add_enforce_terms_to_application_settings.rb b/db/migrate/20180424090541_add_enforce_terms_to_application_settings.rb index 306cd737771..f5afdb0e4e6 100644 --- a/db/migrate/20180424090541_add_enforce_terms_to_application_settings.rb +++ b/db/migrate/20180424090541_add_enforce_terms_to_application_settings.rb @@ -4,6 +4,7 @@ class AddEnforceTermsToApplicationSettings < ActiveRecord::Migration DOWNTIME = false def change + # rubocop:disable Migration/SaferBooleanColumn add_column :application_settings, :enforce_terms, :boolean, default: false end end diff --git a/db/migrate/20180425075446_create_term_agreements.rb b/db/migrate/20180425075446_create_term_agreements.rb index 22a9d7b574d..1fa2c8dd3be 100644 --- a/db/migrate/20180425075446_create_term_agreements.rb +++ b/db/migrate/20180425075446_create_term_agreements.rb @@ -21,6 +21,7 @@ class CreateTermAgreements < ActiveRecord::Migration end def down + # rubocop:disable Migration/RemoveIndex remove_index :term_agreements, name: 'term_agreements_unique_index' drop_table :term_agreements diff --git a/db/migrate/20180503131624_create_remote_mirrors.rb b/db/migrate/20180503131624_create_remote_mirrors.rb index 7800186455f..249882f8613 100644 --- a/db/migrate/20180503131624_create_remote_mirrors.rb +++ b/db/migrate/20180503131624_create_remote_mirrors.rb @@ -23,6 +23,7 @@ class CreateRemoteMirrors < ActiveRecord::Migration t.string :encrypted_credentials_iv t.string :encrypted_credentials_salt + # rubocop:disable Migration/Timestamps t.timestamps null: false end end diff --git a/db/migrate/20180503150427_add_index_to_namespaces_runners_token.rb b/db/migrate/20180503150427_add_index_to_namespaces_runners_token.rb index 4c4e576d49f..9e55690bd33 100644 --- a/db/migrate/20180503150427_add_index_to_namespaces_runners_token.rb +++ b/db/migrate/20180503150427_add_index_to_namespaces_runners_token.rb @@ -14,6 +14,7 @@ class AddIndexToNamespacesRunnersToken < ActiveRecord::Migration def down if index_exists?(:namespaces, :runners_token, unique: true) + # rubocop:disable Migration/RemoveIndex remove_index :namespaces, :runners_token end end diff --git a/db/migrate/20180503175054_add_indexes_to_project_mirror_data.rb b/db/migrate/20180503175054_add_indexes_to_project_mirror_data.rb index 17570269b2e..b59b941c815 100644 --- a/db/migrate/20180503175054_add_indexes_to_project_mirror_data.rb +++ b/db/migrate/20180503175054_add_indexes_to_project_mirror_data.rb @@ -11,6 +11,7 @@ class AddIndexesToProjectMirrorData < ActiveRecord::Migration end def down + # rubocop:disable Migration/RemoveIndex remove_index :project_mirror_data, :jid if index_exists? :project_mirror_data, :jid remove_index :project_mirror_data, :status if index_exists? :project_mirror_data, :status end diff --git a/db/migrate/20180503193542_add_indexes_to_remote_mirror.rb b/db/migrate/20180503193542_add_indexes_to_remote_mirror.rb index 9a9decffdab..4af42b4fb29 100644 --- a/db/migrate/20180503193542_add_indexes_to_remote_mirror.rb +++ b/db/migrate/20180503193542_add_indexes_to_remote_mirror.rb @@ -10,6 +10,7 @@ class AddIndexesToRemoteMirror < ActiveRecord::Migration end def down + # rubocop:disable Migration/RemoveIndex remove_index :remote_mirrors, :last_successful_update_at if index_exists? :remote_mirrors, :last_successful_update_at end end diff --git a/db/migrate/20180511090724_add_index_on_ci_runners_runner_type.rb b/db/migrate/20180511090724_add_index_on_ci_runners_runner_type.rb index 580f56007c7..f3ed20fd243 100644 --- a/db/migrate/20180511090724_add_index_on_ci_runners_runner_type.rb +++ b/db/migrate/20180511090724_add_index_on_ci_runners_runner_type.rb @@ -10,6 +10,7 @@ class AddIndexOnCiRunnersRunnerType < ActiveRecord::Migration end def down + # rubocop:disable Migration/RemoveIndex remove_index :ci_runners, :runner_type end end diff --git a/db/migrate/20180515005612_add_squash_to_merge_requests.rb b/db/migrate/20180515005612_add_squash_to_merge_requests.rb index f526b45bd4b..fd85e968acd 100644 --- a/db/migrate/20180515005612_add_squash_to_merge_requests.rb +++ b/db/migrate/20180515005612_add_squash_to_merge_requests.rb @@ -9,6 +9,7 @@ class AddSquashToMergeRequests < ActiveRecord::Migration def up unless column_exists?(:merge_requests, :squash) + # rubocop:disable Migration/UpdateLargeTable add_column_with_default :merge_requests, :squash, :boolean, default: false, allow_null: false end end diff --git a/db/migrate/20180515121227_create_notes_diff_files.rb b/db/migrate/20180515121227_create_notes_diff_files.rb index 7108bc1a64b..efcd3bb9c7e 100644 --- a/db/migrate/20180515121227_create_notes_diff_files.rb +++ b/db/migrate/20180515121227_create_notes_diff_files.rb @@ -16,6 +16,7 @@ class CreateNotesDiffFiles < ActiveRecord::Migration t.text :old_path, null: false end + # rubocop:disable Migration/AddConcurrentForeignKey add_foreign_key :note_diff_files, :notes, column: :diff_note_id, on_delete: :cascade end end diff --git a/db/migrate/20180521171529_increase_mysql_text_limit_for_gpg_keys.rb b/db/migrate/20180521171529_increase_mysql_text_limit_for_gpg_keys.rb index df84898003f..08ce8cc3094 100644 --- a/db/migrate/20180521171529_increase_mysql_text_limit_for_gpg_keys.rb +++ b/db/migrate/20180521171529_increase_mysql_text_limit_for_gpg_keys.rb @@ -1,2 +1 @@ -# rubocop:disable all require_relative 'gpg_keys_limits_to_mysql' diff --git a/db/migrate/20180529093006_ensure_remote_mirror_columns.rb b/db/migrate/20180529093006_ensure_remote_mirror_columns.rb index 290416cb61c..22e9482cb1d 100644 --- a/db/migrate/20180529093006_ensure_remote_mirror_columns.rb +++ b/db/migrate/20180529093006_ensure_remote_mirror_columns.rb @@ -6,6 +6,7 @@ class EnsureRemoteMirrorColumns < ActiveRecord::Migration disable_ddl_transaction! def up + # rubocop:disable Migration/Datetime add_column :remote_mirrors, :last_update_started_at, :datetime unless column_exists?(:remote_mirrors, :last_update_started_at) add_column :remote_mirrors, :remote_name, :string unless column_exists?(:remote_mirrors, :remote_name) diff --git a/db/migrate/20180531220618_change_default_value_for_dsa_key_restriction.rb b/db/migrate/20180531220618_change_default_value_for_dsa_key_restriction.rb index d0dcacc5b66..dbbbcd1f622 100644 --- a/db/migrate/20180531220618_change_default_value_for_dsa_key_restriction.rb +++ b/db/migrate/20180531220618_change_default_value_for_dsa_key_restriction.rb @@ -4,13 +4,13 @@ class ChangeDefaultValueForDsaKeyRestriction < ActiveRecord::Migration def up change_column :application_settings, :dsa_key_restriction, :integer, null: false, - default: -1 + default: -1 execute("UPDATE application_settings SET dsa_key_restriction = -1") end def down change_column :application_settings, :dsa_key_restriction, :integer, null: false, - default: 0 + default: 0 end end diff --git a/db/migrate/20180608110058_rename_merge_requests_allow_collaboration.rb b/db/migrate/20180608110058_rename_merge_requests_allow_collaboration.rb index dcbbef9bd4a..36f2a593fbe 100644 --- a/db/migrate/20180608110058_rename_merge_requests_allow_collaboration.rb +++ b/db/migrate/20180608110058_rename_merge_requests_allow_collaboration.rb @@ -11,6 +11,7 @@ class RenameMergeRequestsAllowCollaboration < ActiveRecord::Migration def up if column_exists?(:merge_requests, :allow_collaboration) + # rubocop:disable Migration/UpdateLargeTable rename_column_concurrently :merge_requests, :allow_collaboration, :allow_maintainer_to_push end end diff --git a/db/migrate/20180629153018_create_site_statistics.rb b/db/migrate/20180629153018_create_site_statistics.rb new file mode 100644 index 00000000000..085ce1ba64b --- /dev/null +++ b/db/migrate/20180629153018_create_site_statistics.rb @@ -0,0 +1,18 @@ +class CreateSiteStatistics < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def up + create_table :site_statistics do |t| + t.integer :repositories_count, default: 0, null: false + t.integer :wikis_count, default: 0, null: false + end + + execute('INSERT INTO site_statistics (id) VALUES(1)') + end + + def down + drop_table :site_statistics + end +end diff --git a/db/migrate/20180702124358_remove_orphaned_routes.rb b/db/migrate/20180702124358_remove_orphaned_routes.rb new file mode 100644 index 00000000000..6f6e289ba87 --- /dev/null +++ b/db/migrate/20180702124358_remove_orphaned_routes.rb @@ -0,0 +1,49 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class RemoveOrphanedRoutes < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + class Route < ActiveRecord::Base + self.table_name = 'routes' + include EachBatch + + def self.orphaned_namespace_routes + where(source_type: 'Namespace') + .where('NOT EXISTS ( SELECT 1 FROM namespaces WHERE namespaces.id = routes.source_id )') + end + + def self.orphaned_project_routes + where(source_type: 'Project') + .where('NOT EXISTS ( SELECT 1 FROM projects WHERE projects.id = routes.source_id )') + end + end + + def up + # Some of these queries can take up to 10 seconds to run on GitLab.com, + # which is pretty close to our 15 second statement timeout. To ensure a + # smooth deployment procedure we disable the statement timeouts for this + # migration, just in case. + disable_statement_timeout + + # On GitLab.com there are around 4000 orphaned project routes, and around + # 150 orphaned namespace routes. + [ + Route.orphaned_project_routes, + Route.orphaned_namespace_routes + ].each do |relation| + relation.each_batch(of: 1_000) do |batch| + batch.delete_all + end + end + end + + def down + # There is no way to restore orphaned routes, and this doesn't make any + # sense anyway. + end +end diff --git a/db/migrate/20180702134423_generate_missing_routes.rb b/db/migrate/20180702134423_generate_missing_routes.rb new file mode 100644 index 00000000000..994725f9bd1 --- /dev/null +++ b/db/migrate/20180702134423_generate_missing_routes.rb @@ -0,0 +1,143 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +# This migration generates missing routes for any projects and namespaces that +# don't already have a route. +# +# On GitLab.com this would insert 611 project routes, and 0 namespace routes. +# The exact number could vary per instance, so we take care of both just in +# case. +class GenerateMissingRoutes < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + class User < ActiveRecord::Base + self.table_name = 'users' + end + + class Route < ActiveRecord::Base + self.table_name = 'routes' + end + + module Routable + def build_full_path + if parent && path + parent.build_full_path + '/' + path + else + path + end + end + + def build_full_name + if parent && name + parent.human_name + ' / ' + name + else + name + end + end + + def human_name + build_full_name + end + + def attributes_for_insert + time = Time.zone.now + + { + # We can't use "self.class.name" here as that would include the + # migration namespace. + source_type: source_type_for_route, + source_id: id, + created_at: time, + updated_at: time, + name: build_full_name, + + # The route path might already be taken. Instead of trying to generate a + # new unique name on every conflict, we just append the row ID to the + # route path. + path: "#{build_full_path}-#{id}" + } + end + end + + class Project < ActiveRecord::Base + self.table_name = 'projects' + + include EachBatch + include GenerateMissingRoutes::Routable + + belongs_to :namespace, class_name: 'GenerateMissingRoutes::Namespace' + + has_one :route, + as: :source, + inverse_of: :source, + class_name: 'GenerateMissingRoutes::Route' + + alias_method :parent, :namespace + alias_attribute :parent_id, :namespace_id + + def self.without_routes + where( + 'NOT EXISTS ( + SELECT 1 + FROM routes + WHERE source_type = ? + AND source_id = projects.id + )', + 'Project' + ) + end + + def source_type_for_route + 'Project' + end + end + + class Namespace < ActiveRecord::Base + self.table_name = 'namespaces' + + include EachBatch + include GenerateMissingRoutes::Routable + + belongs_to :parent, class_name: 'GenerateMissingRoutes::Namespace' + belongs_to :owner, class_name: 'GenerateMissingRoutes::User' + + has_one :route, + as: :source, + inverse_of: :source, + class_name: 'GenerateMissingRoutes::Route' + + def self.without_routes + where( + 'NOT EXISTS ( + SELECT 1 + FROM routes + WHERE source_type = ? + AND source_id = namespaces.id + )', + 'Namespace' + ) + end + + def source_type_for_route + 'Namespace' + end + end + + def up + [Namespace, Project].each do |model| + model.without_routes.each_batch(of: 100) do |batch| + rows = batch.map(&:attributes_for_insert) + + Gitlab::Database.bulk_insert(:routes, rows) + end + end + end + + def down + # Removing routes we previously generated makes no sense. + end +end diff --git a/db/migrate/gpg_keys_limits_to_mysql.rb b/db/migrate/gpg_keys_limits_to_mysql.rb index 780340d0564..38729320d8c 100644 --- a/db/migrate/gpg_keys_limits_to_mysql.rb +++ b/db/migrate/gpg_keys_limits_to_mysql.rb @@ -1,5 +1,4 @@ class IncreaseMysqlTextLimitForGpgKeys < ActiveRecord::Migration - # Set this constant to true if this migration requires downtime. DOWNTIME = false diff --git a/db/migrate/limits_ci_build_trace_chunks_raw_data_for_mysql.rb b/db/migrate/limits_ci_build_trace_chunks_raw_data_for_mysql.rb index e1771912c3c..9fd23aae1e5 100644 --- a/db/migrate/limits_ci_build_trace_chunks_raw_data_for_mysql.rb +++ b/db/migrate/limits_ci_build_trace_chunks_raw_data_for_mysql.rb @@ -4,6 +4,6 @@ class LimitsCiBuildTraceChunksRawDataForMysql < ActiveRecord::Migration # Mysql needs MEDIUMTEXT type (up to 16MB) rather than TEXT (up to 64KB) # Because 'raw_data' is always capped by Ci::BuildTraceChunk::CHUNK_SIZE, which is 128KB - change_column :ci_build_trace_chunks, :raw_data, :binary, limit: 16.megabytes - 1 #MEDIUMTEXT + change_column :ci_build_trace_chunks, :raw_data, :binary, limit: 16.megabytes - 1 # MEDIUMTEXT end end diff --git a/db/migrate/limits_to_mysql.rb b/db/migrate/limits_to_mysql.rb index 8f8d8f27410..7507a4bb431 100644 --- a/db/migrate/limits_to_mysql.rb +++ b/db/migrate/limits_to_mysql.rb @@ -1,4 +1,3 @@ -# rubocop:disable all class LimitsToMysql < ActiveRecord::Migration def up return unless ActiveRecord::Base.configurations[Rails.env]['adapter'] =~ /^mysql/ diff --git a/db/optional_migrations/composite_primary_keys.rb b/db/optional_migrations/composite_primary_keys.rb index 0fd3fca52dd..d45705021b0 100644 --- a/db/optional_migrations/composite_primary_keys.rb +++ b/db/optional_migrations/composite_primary_keys.rb @@ -21,8 +21,8 @@ class CompositePrimaryKeysMigration < ActiveRecord::Migration Index.new(:merge_request_diff_commits, 'index_merge_request_diff_commits_on_mr_diff_id_and_order', %i(merge_request_diff_id relative_order)), Index.new(:project_authorizations, 'index_project_authorizations_on_user_id_project_id_access_level', %i(user_id project_id access_level)), Index.new(:push_event_payloads, 'index_push_event_payloads_on_event_id', %i(event_id)), - Index.new(:schema_migrations, 'unique_schema_migrations', %(version)), - ] + Index.new(:schema_migrations, 'unique_schema_migrations', %(version)) + ].freeze disable_ddl_transaction! @@ -45,6 +45,7 @@ class CompositePrimaryKeysMigration < ActiveRecord::Migration end private + def add_primary_key(index) execute "ALTER TABLE #{index.table} ADD PRIMARY KEY USING INDEX #{index.name}" end @@ -60,4 +61,3 @@ class CompositePrimaryKeysMigration < ActiveRecord::Migration execute "ALTER TABLE #{index.table} DROP CONSTRAINT IF EXISTS #{temp_index_name}" end end - diff --git a/db/post_migrate/20161221153951_rename_reserved_project_names.rb b/db/post_migrate/20161221153951_rename_reserved_project_names.rb index d322844e2fd..017c58477ac 100644 --- a/db/post_migrate/20161221153951_rename_reserved_project_names.rb +++ b/db/post_migrate/20161221153951_rename_reserved_project_names.rb @@ -124,7 +124,7 @@ class RenameReservedProjectNames < ActiveRecord::Migration def rename_project_row(project, path) project.respond_to?(:update_attributes) && - project.update_attributes(path: path) && + project.update(path: path) && project.respond_to?(:rename_repo) end end diff --git a/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb b/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb index 6a49450cc50..3e8ccfdb899 100644 --- a/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb +++ b/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb @@ -66,7 +66,7 @@ class RenameMoreReservedProjectNames < ActiveRecord::Migration def rename_project_row(project, path) project.respond_to?(:update_attributes) && - project.update_attributes(path: path) && + project.update(path: path) && project.respond_to?(:rename_repo) end end diff --git a/db/post_migrate/20170425130047_drop_ci_trigger_schedules_table.rb b/db/post_migrate/20170425130047_drop_ci_trigger_schedules_table.rb index 159b533eaaa..24750c58ef0 100644 --- a/db/post_migrate/20170425130047_drop_ci_trigger_schedules_table.rb +++ b/db/post_migrate/20170425130047_drop_ci_trigger_schedules_table.rb @@ -1,4 +1,3 @@ -# rubocop:disable Migration/Datetime class DropCiTriggerSchedulesTable < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers diff --git a/db/post_migrate/20170531203055_cleanup_users_ldap_email_rename.rb b/db/post_migrate/20170531203055_cleanup_users_ldap_email_rename.rb index 15edb402b86..a70e3985005 100644 --- a/db/post_migrate/20170531203055_cleanup_users_ldap_email_rename.rb +++ b/db/post_migrate/20170531203055_cleanup_users_ldap_email_rename.rb @@ -10,6 +10,7 @@ class CleanupUsersLdapEmailRename < ActiveRecord::Migration end def down + # rubocop:disable Migration/UpdateLargeTable rename_column_concurrently :users, :external_email, :ldap_email end end diff --git a/db/post_migrate/20170711145558_migrate_stages_statuses.rb b/db/post_migrate/20170711145558_migrate_stages_statuses.rb index aeb900354db..65755c0e824 100644 --- a/db/post_migrate/20170711145558_migrate_stages_statuses.rb +++ b/db/post_migrate/20170711145558_migrate_stages_statuses.rb @@ -28,6 +28,7 @@ class MigrateStagesStatuses < ActiveRecord::Migration def down disable_statement_timeout + # rubocop:disable Migration/UpdateLargeTable update_column_in_batches(:ci_stages, :status, nil) end end diff --git a/db/post_migrate/20170830150306_drop_events_for_migration_table.rb b/db/post_migrate/20170830150306_drop_events_for_migration_table.rb index 763ee9a810d..69a612ead40 100644 --- a/db/post_migrate/20170830150306_drop_events_for_migration_table.rb +++ b/db/post_migrate/20170830150306_drop_events_for_migration_table.rb @@ -18,7 +18,6 @@ class DropEventsForMigrationTable < ActiveRecord::Migration end end - # rubocop: disable Migration/Datetime def down create_table :events_for_migration do |t| t.string :target_type, index: true diff --git a/db/post_migrate/20171106154015_remove_issues_branch_name.rb b/db/post_migrate/20171106154015_remove_issues_branch_name.rb index 162b6bafab4..3d08225c96d 100644 --- a/db/post_migrate/20171106154015_remove_issues_branch_name.rb +++ b/db/post_migrate/20171106154015_remove_issues_branch_name.rb @@ -1,4 +1,3 @@ -# rubocop:disable Migration/RemoveColumn # See http://doc.gitlab.com/ce/development/migration_style_guide.html # for more information on how to write migrations for GitLab. diff --git a/db/post_migrate/20171106180641_cleanup_add_timezone_to_issues_closed_at.rb b/db/post_migrate/20171106180641_cleanup_add_timezone_to_issues_closed_at.rb index 88dd8f89ba6..53f376f216b 100644 --- a/db/post_migrate/20171106180641_cleanup_add_timezone_to_issues_closed_at.rb +++ b/db/post_migrate/20171106180641_cleanup_add_timezone_to_issues_closed_at.rb @@ -13,6 +13,7 @@ class CleanupAddTimezoneToIssuesClosedAt < ActiveRecord::Migration end # rubocop:disable Migration/Datetime + # rubocop:disable Migration/UpdateLargeTable def down change_column_type_concurrently(:issues, :closed_at, :datetime) end diff --git a/db/post_migrate/20171128214150_schedule_populate_merge_request_metrics_with_events_data.rb b/db/post_migrate/20171128214150_schedule_populate_merge_request_metrics_with_events_data.rb index fce1829c982..980f76e7d57 100644 --- a/db/post_migrate/20171128214150_schedule_populate_merge_request_metrics_with_events_data.rb +++ b/db/post_migrate/20171128214150_schedule_populate_merge_request_metrics_with_events_data.rb @@ -1,5 +1,4 @@ # frozen_string_literal: true -# rubocop:disable GitlabSecurity/SqlInjection class SchedulePopulateMergeRequestMetricsWithEventsData < ActiveRecord::Migration DOWNTIME = false diff --git a/db/post_migrate/20180223124427_build_user_interacted_projects_table.rb b/db/post_migrate/20180223124427_build_user_interacted_projects_table.rb index 9addd36dca6..8c8dbb1a043 100644 --- a/db/post_migrate/20180223124427_build_user_interacted_projects_table.rb +++ b/db/post_migrate/20180223124427_build_user_interacted_projects_table.rb @@ -43,8 +43,6 @@ class BuildUserInteractedProjectsTable < ActiveRecord::Migration end end - private - class PostgresStrategy < ActiveRecord::Migration include Gitlab::Database::MigrationHelpers @@ -79,6 +77,7 @@ class BuildUserInteractedProjectsTable < ActiveRecord::Migration end private + def insert_missing_records iteration = 0 records = 0 diff --git a/db/post_migrate/20180408143355_cleanup_users_rss_token_rename.rb b/db/post_migrate/20180408143355_cleanup_users_rss_token_rename.rb index bff83379087..3d77ff921c7 100644 --- a/db/post_migrate/20180408143355_cleanup_users_rss_token_rename.rb +++ b/db/post_migrate/20180408143355_cleanup_users_rss_token_rename.rb @@ -8,6 +8,7 @@ class CleanupUsersRssTokenRename < ActiveRecord::Migration end def down + # rubocop:disable Migration/UpdateLargeTable rename_column_concurrently :users, :feed_token, :rss_token end end diff --git a/db/post_migrate/20180424151928_fill_file_store.rb b/db/post_migrate/20180424151928_fill_file_store.rb index b41feb233be..03d54dab250 100644 --- a/db/post_migrate/20180424151928_fill_file_store.rb +++ b/db/post_migrate/20180424151928_fill_file_store.rb @@ -38,7 +38,7 @@ class FillFileStore < ActiveRecord::Migration def up # NOTE: Schedule background migrations that fill 'NULL' value by '1'(ObjectStorage::Store::LOCAL) on `file_store`, `store` columns - # + # # Here are the target columns # - ci_job_artifacts.file_store # - lfs_objects.file_store diff --git a/db/post_migrate/20180430143705_backfill_runner_type_for_ci_runners_post_migrate.rb b/db/post_migrate/20180430143705_backfill_runner_type_for_ci_runners_post_migrate.rb index 38af5aae924..0e6ec46e5f0 100644 --- a/db/post_migrate/20180430143705_backfill_runner_type_for_ci_runners_post_migrate.rb +++ b/db/post_migrate/20180430143705_backfill_runner_type_for_ci_runners_post_migrate.rb @@ -9,6 +9,7 @@ class BackfillRunnerTypeForCiRunnersPostMigrate < ActiveRecord::Migration disable_ddl_transaction! def up + # rubocop:disable Migration/UpdateColumnInBatches update_column_in_batches(:ci_runners, :runner_type, INSTANCE_RUNNER_TYPE) do |table, query| query.where(table[:is_shared].eq(true)).where(table[:runner_type].eq(nil)) end diff --git a/db/post_migrate/20180502134117_migrate_import_attributes_data_from_projects_to_project_mirror_data.rb b/db/post_migrate/20180502134117_migrate_import_attributes_data_from_projects_to_project_mirror_data.rb index e39cd33c414..08d7d64a2c5 100644 --- a/db/post_migrate/20180502134117_migrate_import_attributes_data_from_projects_to_project_mirror_data.rb +++ b/db/post_migrate/20180502134117_migrate_import_attributes_data_from_projects_to_project_mirror_data.rb @@ -34,5 +34,4 @@ class MigrateImportAttributesDataFromProjectsToProjectMirrorData < ActiveRecord: queue_background_migration_jobs_by_range_at_intervals(import_state, DOWN_MIGRATION, DELAY_INTERVAL, batch_size: BATCH_SIZE) end - end diff --git a/db/post_migrate/20180507083701_set_minimal_project_build_timeout.rb b/db/post_migrate/20180507083701_set_minimal_project_build_timeout.rb index d9d9e93f5a3..fb9616f0c07 100644 --- a/db/post_migrate/20180507083701_set_minimal_project_build_timeout.rb +++ b/db/post_migrate/20180507083701_set_minimal_project_build_timeout.rb @@ -8,6 +8,8 @@ class SetMinimalProjectBuildTimeout < ActiveRecord::Migration disable_ddl_transaction! def up + # rubocop:disable Migration/UpdateLargeTable + # rubocop:disable Migration/UpdateColumnInBatches update_column_in_batches(:projects, :build_timeout, MINIMUM_TIMEOUT) do |table, query| query.where(table[:build_timeout].lt(MINIMUM_TIMEOUT)) end diff --git a/db/post_migrate/20180523125103_cleanup_merge_requests_allow_maintainer_to_push_rename.rb b/db/post_migrate/20180523125103_cleanup_merge_requests_allow_maintainer_to_push_rename.rb index 7301bcf2c6c..7eca7394f5f 100644 --- a/db/post_migrate/20180523125103_cleanup_merge_requests_allow_maintainer_to_push_rename.rb +++ b/db/post_migrate/20180523125103_cleanup_merge_requests_allow_maintainer_to_push_rename.rb @@ -11,6 +11,7 @@ class CleanupMergeRequestsAllowMaintainerToPushRename < ActiveRecord::Migration def down if column_exists?(:merge_requests, :allow_collaboration) + # rubocop:disable Migration/UpdateLargeTable rename_column_concurrently :merge_requests, :allow_collaboration, :allow_maintainer_to_push end end diff --git a/db/post_migrate/20180704145007_update_project_indexes.rb b/db/post_migrate/20180704145007_update_project_indexes.rb index 193563b36db..0e2601ad4fa 100644 --- a/db/post_migrate/20180704145007_update_project_indexes.rb +++ b/db/post_migrate/20180704145007_update_project_indexes.rb @@ -14,7 +14,7 @@ class UpdateProjectIndexes < ActiveRecord::Migration [:repository_storage, :created_at], name: NEW_INDEX_NAME, where: 'last_repository_check_at IS NULL' - ) + ) end def down diff --git a/db/post_migrate/20180706223200_populate_site_statistics.rb b/db/post_migrate/20180706223200_populate_site_statistics.rb new file mode 100644 index 00000000000..e78e9eb900a --- /dev/null +++ b/db/post_migrate/20180706223200_populate_site_statistics.rb @@ -0,0 +1,25 @@ +class PopulateSiteStatistics < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + transaction do + execute('SET LOCAL statement_timeout TO 0') if Gitlab::Database.postgresql? # see https://gitlab.com/gitlab-org/gitlab-ce/issues/48967 + + execute("UPDATE site_statistics SET repositories_count = (SELECT COUNT(*) FROM projects)") + end + + transaction do + execute('SET LOCAL statement_timeout TO 0') if Gitlab::Database.postgresql? # see https://gitlab.com/gitlab-org/gitlab-ce/issues/48967 + + execute("UPDATE site_statistics SET wikis_count = (SELECT COUNT(*) FROM project_features WHERE wiki_access_level != 0)") + end + end + + def down + # No downside in keeping the counter up-to-date + end +end diff --git a/db/schema.rb b/db/schema.rb index 3db11d8447e..8ae0197d1b4 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -1841,6 +1841,11 @@ ActiveRecord::Schema.define(version: 20180722103201) do add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree add_index "services", ["template"], name: "index_services_on_template", using: :btree + create_table "site_statistics", force: :cascade do |t| + t.integer "repositories_count", default: 0, null: false + t.integer "wikis_count", default: 0, null: false + end + create_table "snippets", force: :cascade do |t| t.string "title" t.text "content" diff --git a/doc/administration/troubleshooting/debug.md b/doc/administration/troubleshooting/debug.md index 83a714810c1..7ae4f7c1515 100644 --- a/doc/administration/troubleshooting/debug.md +++ b/doc/administration/troubleshooting/debug.md @@ -66,6 +66,19 @@ On CentOS: sudo yum install gdb ``` +### rbtrace + +GitLab 11.2 ships with [rbtrace](https://github.com/tmm1/rbtrace), which +allows you to trace Ruby code, view all running threads, take memory dumps, +and more. However, this is not enabled by default. To enable it, define the +`ENABLE_RBTRACE` variable to the environment. For example, in Omnibus: + +```ruby +gitlab_rails['env'] = {"ENABLE_RBTRACE" => "1"} +``` + +Then reconfigure the system and restart Unicorn and Sidekiq. + ## Common Problems Many of the tips to diagnose issues below apply to many different situations. We'll use one diff --git a/doc/api/members.md b/doc/api/members.md index 8ebe464c359..7b228b92594 100644 --- a/doc/api/members.md +++ b/doc/api/members.md @@ -40,7 +40,9 @@ Example response: "username": "raymond_smith", "name": "Raymond Smith", "state": "active", - "created_at": "2012-10-22T14:13:35Z", + "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon", + "web_url": "http://192.168.1.8:3000/root", + "expires_at": "2012-10-22T14:13:35Z", "access_level": 30 }, { @@ -48,7 +50,65 @@ Example response: "username": "john_doe", "name": "John Doe", "state": "active", - "created_at": "2012-10-22T14:13:35Z", + "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon", + "web_url": "http://192.168.1.8:3000/root", + "expires_at": "2012-10-22T14:13:35Z", + "access_level": 30 + } +] +``` + +## List all members of a group or project including inherited members + +Gets a list of group or project members viewable by the authenticated user, including inherited members through ancestor groups. + +``` +GET /groups/:id/members/all +GET /projects/:id/members/all +``` + +| Attribute | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `id` | integer/string | yes | The ID or [URL-encoded path of the project or group](README.md#namespaced-path-encoding) owned by the authenticated user | +| `query` | string | no | A query string to search for members | + +```bash +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/:id/members/all +curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/:id/members/all +``` + +Example response: + +```json +[ + { + "id": 1, + "username": "raymond_smith", + "name": "Raymond Smith", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon", + "web_url": "http://192.168.1.8:3000/root", + "expires_at": "2012-10-22T14:13:35Z", + "access_level": 30 + }, + { + "id": 2, + "username": "john_doe", + "name": "John Doe", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon", + "web_url": "http://192.168.1.8:3000/root", + "expires_at": "2012-10-22T14:13:35Z", + "access_level": 30 + }, + { + "id": 3, + "username": "foo_bar", + "name": "Foo bar", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon", + "web_url": "http://192.168.1.8:3000/root", + "expires_at": "2012-11-22T14:13:35Z", "access_level": 30 } ] @@ -81,7 +141,8 @@ Example response: "username": "raymond_smith", "name": "Raymond Smith", "state": "active", - "created_at": "2012-10-22T14:13:35Z", + "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon", + "web_url": "http://192.168.1.8:3000/root", "access_level": 30, "expires_at": null } @@ -116,7 +177,9 @@ Example response: "username": "raymond_smith", "name": "Raymond Smith", "state": "active", - "created_at": "2012-10-22T14:13:35Z", + "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon", + "web_url": "http://192.168.1.8:3000/root", + "expires_at": "2012-10-22T14:13:35Z", "access_level": 30 } ``` @@ -150,7 +213,9 @@ Example response: "username": "raymond_smith", "name": "Raymond Smith", "state": "active", - "created_at": "2012-10-22T14:13:35Z", + "avatar_url": "https://www.gravatar.com/avatar/c2525a7f58ae3776070e44c106c48e15?s=80&d=identicon", + "web_url": "http://192.168.1.8:3000/root", + "expires_at": "2012-10-22T14:13:35Z", "access_level": 40 } ``` diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md index b4a2349844b..6c6e198a7c3 100644 --- a/doc/development/api_graphql_styleguide.md +++ b/doc/development/api_graphql_styleguide.md @@ -201,6 +201,148 @@ lot of dependant objects. To limit the amount of queries performed, we can use `BatchLoader`. +## Mutations + +Mutations are used to change any stored values, or to trigger +actions. In the same way a GET-request should not modify data, we +cannot modify data in a regular GraphQL-query. We can however in a +mutation. + +### Fields + +In the most common situations, a mutation would return 2 fields: + +- The resource being modified +- A list of errors explaining why the action could not be + performed. If the mutation succeeded, this list would be empty. + +By inheriting any new mutations from `Mutations::BaseMutation` the +`errors` field is automatically added. A `clientMutationId` field is +also added, this can be used by the client to identify the result of a +single mutation when multiple are performed within a single request. + +### Building Mutations + +Mutations live in `app/graphql/mutations` ideally grouped per +resources they are mutating, similar to our services. They should +inherit `Mutations::BaseMutation`. The fields defined on the mutation +will be returned as the result of the mutation. + +Always provide a consistent GraphQL-name to the mutation, this name is +used to generate the input types and the field the mutation is mounted +on. The name should look like `<Resource being modified><Mutation +class name>`, for example the `Mutations::MergeRequests::SetWip` +mutation has GraphQL name `MergeRequestSetWip`. + +Arguments required by the mutation can be defined as arguments +required for a field. These will be wrapped up in an input type for +the mutation. For example, the `Mutations::MergeRequests::SetWip` +with GraphQL-name `MergeRequestSetWip` defines these arguments: + +```ruby +argument :project_path, GraphQL::ID_TYPE, + required: true, + description: "The project the merge request to mutate is in" + +argument :iid, GraphQL::ID_TYPE, + required: true, + description: "The iid of the merge request to mutate" + +argument :wip, + GraphQL::BOOLEAN_TYPE, + required: false, + description: <<~DESC + Whether or not to set the merge request as a WIP. + If not passed, the value will be toggled. + DESC +``` + +This would automatically generate an input type called +`MergeRequestSetWipInput` with the 3 arguments we specified and the +`clientMutationId`. + +These arguments are then passed to the `resolve` method of a mutation +as keyword arguments. From here, we can call the service that will +modify the resource. + +The `resolve` method should then return a hash with the same field +names as defined on the mutation and an `errors` array. For example, +the `Mutations::MergeRequests::SetWip` defines a `merge_request` +field: + +```ruby +field :merge_request, + Types::MergeRequestType, + null: true, + description: "The merge request after mutation" +``` + +This means that the hash returned from `resolve` in this mutation +should look like this: + +```ruby +{ + # The merge request modified, this will be wrapped in the type + # defined on the field + merge_request: merge_request, + # An array if strings if the mutation failed after authorization + errors: merge_request.errors.full_messages +} +``` + +To make the mutation available it should be defined on the mutation +type that lives in `graphql/types/mutation_types`. The +`mount_mutation` helper method will define a field based on the +GraphQL-name of the mutation: + +```ruby +module Types + class MutationType < BaseObject + include Gitlab::Graphql::MountMutation + + graphql_name "Mutation" + + mount_mutation Mutations::MergeRequests::SetWip + end +end +``` + +Will generate a field called `mergeRequestSetWip` that +`Mutations::MergeRequests::SetWip` to be resolved. + +### Authorizing resources + +To authorize resources inside a mutation, we can include the +`Gitlab::Graphql::Authorize::AuthorizeResource` concern in the +mutation. + +This allows us to provide the required abilities on the mutation like +this: + +```ruby +module Mutations + module MergeRequests + class SetWip < Base + graphql_name 'MergeRequestSetWip' + + authorize :update_merge_request + end + end +end +``` + +We can then call `authorize!` in the `resolve` method, passing in the resource we +want to validate the abilities for. + +Alternatively, we can add a `find_object` method that will load the +object on the mutation. This would allow you to use the +`authorized_find!` and `authorized_find!` helper methods. + +When a user is not allowed to perform the action, or an object is not +found, we should raise a +`Gitlab::Graphql::Errors::ResourceNotAvailable` error. Which will be +correctly rendered to the clients. + ## Testing _full stack_ tests for a graphql query or mutation live in @@ -212,3 +354,35 @@ be used to test if the query renders valid results. Using the `GraphqlHelpers#all_graphql_fields_for`-helper, a query including all available fields can be constructed. This makes it easy to add a test rendering all possible fields for a query. + +To test GraphQL mutation requests, `GraphqlHelpers` provides 2 +helpers: `graphql_mutation` which takes the name of the mutation, and +a hash with the input for the mutation. This will return a struct with +a mutation query, and prepared variables. + +This struct can then be passed to the `post_graphql_mutation` helper, +that will post the request with the correct params, like a GraphQL +client would do. + +To access the response of a mutation, the `graphql_mutation_response` +helper is available. + +Using these helpers, we can build specs like this: + +```ruby +let(:mutation) do + graphql_mutation( + :merge_request_set_wip, + project_path: 'gitlab-org/gitlab-ce', + iid: '1', + wip: true + ) +end + +it 'returns a successfull response' do + post_graphql_mutation(mutation, current_user: user) + + expect(response).to have_gitlab_http_status(:success) + expect(graphql_mutation_response(:merge_request_set_wip)['errors']).to be_empty +end +``` diff --git a/lib/api/helpers/members_helpers.rb b/lib/api/helpers/members_helpers.rb index a50ea0b52aa..fed8846e505 100644 --- a/lib/api/helpers/members_helpers.rb +++ b/lib/api/helpers/members_helpers.rb @@ -10,6 +10,31 @@ module API def authorize_admin_source!(source_type, source) authorize! :"admin_#{source_type}", source end + + def find_all_members(source_type, source) + members = source_type == 'project' ? find_all_members_for_project(source) : find_all_members_for_group(source) + members.non_invite + .non_request + end + + def find_all_members_for_project(project) + shared_group_ids = project.project_group_links.pluck(:group_id) + project_group_ids = project.group&.self_and_ancestors&.pluck(:id) + source_ids = [project.id, project_group_ids, shared_group_ids] + .flatten + .compact + Member.includes(:user) + .joins(user: :project_authorizations) + .where(project_authorizations: { project_id: project.id }) + .where(source_id: source_ids) + end + + def find_all_members_for_group(group) + source_ids = group.self_and_ancestors.pluck(:id) + Member.includes(:user) + .where(source_id: source_ids) + .where(source_type: 'Namespace') + end end end end diff --git a/lib/api/members.rb b/lib/api/members.rb index 8b12986d09e..3d2220fed96 100644 --- a/lib/api/members.rb +++ b/lib/api/members.rb @@ -28,6 +28,23 @@ module API present members, with: Entities::Member end + desc 'Gets a list of group or project members viewable by the authenticated user, including those who gained membership through ancestor group.' do + success Entities::Member + end + params do + optional :query, type: String, desc: 'A query string to search for members' + use :pagination + end + get ":id/members/all" do + source = find_source(source_type, params[:id]) + + members = find_all_members(source_type, source) + members = members.includes(:user).references(:user).merge(User.search(params[:query])) if params[:query].present? + members = paginate(members) + + present members, with: Entities::Member + end + desc 'Gets a member of a group or project.' do success Entities::Member end diff --git a/lib/api/runner.rb b/lib/api/runner.rb index d0cc0945a5f..06c034444a1 100644 --- a/lib/api/runner.rb +++ b/lib/api/runner.rb @@ -108,8 +108,7 @@ module API if result.valid? if result.build - Gitlab::Metrics.add_event(:build_found, - project: result.build.project.full_path) + Gitlab::Metrics.add_event(:build_found) present result.build, with: Entities::JobRequest::Response else Gitlab::Metrics.add_event(:build_not_found) @@ -140,8 +139,7 @@ module API job.trace.set(params[:trace]) if params[:trace] - Gitlab::Metrics.add_event(:update_build, - project: job.project.full_path) + Gitlab::Metrics.add_event(:update_build) case params[:state].to_s when 'running' diff --git a/lib/gitlab/email/handler/create_issue_handler.rb b/lib/gitlab/email/handler/create_issue_handler.rb index 764f93f6d3d..fc8615afcae 100644 --- a/lib/gitlab/email/handler/create_issue_handler.rb +++ b/lib/gitlab/email/handler/create_issue_handler.rb @@ -36,10 +36,6 @@ module Gitlab @project ||= Project.find_by_full_path(project_path) end - def metrics_params - super.merge(project: project&.full_path) - end - private def create_issue diff --git a/lib/gitlab/email/handler/create_merge_request_handler.rb b/lib/gitlab/email/handler/create_merge_request_handler.rb index 2f864f2082b..2316e58c3fc 100644 --- a/lib/gitlab/email/handler/create_merge_request_handler.rb +++ b/lib/gitlab/email/handler/create_merge_request_handler.rb @@ -40,10 +40,6 @@ module Gitlab @project ||= Project.find_by_full_path(project_path) end - def metrics_params - super.merge(project: project&.full_path) - end - private def create_merge_request diff --git a/lib/gitlab/email/handler/create_note_handler.rb b/lib/gitlab/email/handler/create_note_handler.rb index 5791dbd0484..379b114e957 100644 --- a/lib/gitlab/email/handler/create_note_handler.rb +++ b/lib/gitlab/email/handler/create_note_handler.rb @@ -28,10 +28,6 @@ module Gitlab record_name: 'comment') end - def metrics_params - super.merge(project: project&.full_path) - end - private def author diff --git a/lib/gitlab/email/handler/unsubscribe_handler.rb b/lib/gitlab/email/handler/unsubscribe_handler.rb index ea80e21532e..56751e4e41e 100644 --- a/lib/gitlab/email/handler/unsubscribe_handler.rb +++ b/lib/gitlab/email/handler/unsubscribe_handler.rb @@ -20,10 +20,6 @@ module Gitlab noteable.unsubscribe(sent_notification.recipient) end - def metrics_params - super.merge(project: project&.full_path) - end - private def sent_notification diff --git a/lib/gitlab/git/conflict/resolver.rb b/lib/gitlab/git/conflict/resolver.rb index 0e4a973301f..6dc792c16b8 100644 --- a/lib/gitlab/git/conflict/resolver.rb +++ b/lib/gitlab/git/conflict/resolver.rb @@ -17,7 +17,7 @@ module Gitlab end rescue GRPC::FailedPrecondition => e raise Gitlab::Git::Conflict::Resolver::ConflictSideMissing.new(e.message) - rescue Rugged::ReferenceError, Rugged::OdbError, GRPC::BadStatus => e + rescue GRPC::BadStatus => e raise Gitlab::Git::CommandError.new(e) end diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 0356e8efc5c..eb02c7ac8e1 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -558,7 +558,9 @@ module Gitlab if is_enabled gitaly_operation_client.user_update_branch(branch_name, user, newrev, oldrev) else - OperationService.new(user, self).update_branch(branch_name, newrev, oldrev) + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + OperationService.new(user, self).update_branch(branch_name, newrev, oldrev) + end end end end @@ -832,18 +834,9 @@ module Gitlab Gitlab::Git.check_namespace!(source_repository) source_repository = RemoteRepository.new(source_repository) unless source_repository.is_a?(RemoteRepository) - message, status = GitalyClient.migrate(:fetch_ref) do |is_enabled| - if is_enabled - gitaly_fetch_ref(source_repository, source_ref: source_ref, target_ref: target_ref) - else - # When removing this code, also remove source_repository#path - # to remove deprecated method calls - local_fetch_ref(source_repository.path, source_ref: source_ref, target_ref: target_ref) - end - end - - # Make sure ref was created, and raise Rugged::ReferenceError when not - raise Rugged::ReferenceError, message if status != 0 + args = %W(fetch --no-tags -f #{GITALY_INTERNAL_URL} #{source_ref}:#{target_ref}) + message, status = run_git(args, env: source_repository.fetch_env) + raise Gitlab::Git::CommandError, message if status != 0 target_ref end @@ -1244,17 +1237,6 @@ module Gitlab gitaly_repository_client.apply_gitattributes(revision) end - def local_fetch_ref(source_path, source_ref:, target_ref:) - args = %W(fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref}) - run_git(args) - end - - def gitaly_fetch_ref(source_repository, source_ref:, target_ref:) - args = %W(fetch --no-tags -f #{GITALY_INTERNAL_URL} #{source_ref}:#{target_ref}) - - run_git(args, env: source_repository.fetch_env) - end - def gitaly_delete_refs(*ref_names) gitaly_ref_client.delete_refs(refs: ref_names) if ref_names.any? end diff --git a/lib/gitlab/git/repository_mirroring.rb b/lib/gitlab/git/repository_mirroring.rb index 8835bfb2481..65eb5cc18cf 100644 --- a/lib/gitlab/git/repository_mirroring.rb +++ b/lib/gitlab/git/repository_mirroring.rb @@ -6,7 +6,9 @@ module Gitlab if is_enabled gitaly_ref_client.remote_branches(remote_name) else - rugged_remote_branches(remote_name) + Gitlab::GitalyClient::StorageSettings.allow_disk_access do + rugged_remote_branches(remote_name) + end end end end diff --git a/lib/gitlab/github_import/importer/pull_requests_importer.rb b/lib/gitlab/github_import/importer/pull_requests_importer.rb index e70361c163b..a52866c4b08 100644 --- a/lib/gitlab/github_import/importer/pull_requests_importer.rb +++ b/lib/gitlab/github_import/importer/pull_requests_importer.rb @@ -43,7 +43,7 @@ module Gitlab Rails.logger .info("GitHub importer finished updating repository for #{pname}") - repository_updates_counter.increment(project: pname) + repository_updates_counter.increment end def update_repository?(pr) diff --git a/lib/gitlab/graphql/authorize.rb b/lib/gitlab/graphql/authorize.rb index 04f25c53e49..93a903915b0 100644 --- a/lib/gitlab/graphql/authorize.rb +++ b/lib/gitlab/graphql/authorize.rb @@ -10,7 +10,14 @@ module Gitlab end def required_permissions - @required_permissions ||= [] + # If the `#authorize` call is used on multiple classes, we add the + # permissions specified on a subclass, to the ones that were specified + # on it's superclass. + @required_permissions ||= if self.respond_to?(:superclass) && superclass.respond_to?(:required_permissions) + superclass.required_permissions.dup + else + [] + end end def authorize(*permissions) diff --git a/lib/gitlab/graphql/authorize/authorize_resource.rb b/lib/gitlab/graphql/authorize/authorize_resource.rb new file mode 100644 index 00000000000..40895686a8a --- /dev/null +++ b/lib/gitlab/graphql/authorize/authorize_resource.rb @@ -0,0 +1,46 @@ +module Gitlab + module Graphql + module Authorize + module AuthorizeResource + extend ActiveSupport::Concern + + included do + extend Gitlab::Graphql::Authorize + end + + def find_object(*args) + raise NotImplementedError, "Implement #find_object in #{self.class.name}" + end + + def authorized_find(*args) + object = find_object(*args) + + object if authorized?(object) + end + + def authorized_find!(*args) + object = find_object(*args) + authorize!(object) + + object + end + + def authorize!(object) + unless authorized?(object) + raise Gitlab::Graphql::Errors::ResourceNotAvailable, + "The resource that you are attempting to access does not exist or you don't have permission to perform this action" + end + end + + def authorized?(object) + self.class.required_permissions.all? do |ability| + # The actions could be performed across multiple objects. In which + # case the current user is common, and we could benefit from the + # caching in `DeclarativePolicy`. + Ability.allowed?(current_user, ability, object, scope: :user) + end + end + end + end + end +end diff --git a/lib/gitlab/graphql/errors.rb b/lib/gitlab/graphql/errors.rb index 1d8e8307ab9..f8c7ec24be1 100644 --- a/lib/gitlab/graphql/errors.rb +++ b/lib/gitlab/graphql/errors.rb @@ -3,6 +3,7 @@ module Gitlab module Errors BaseError = Class.new(GraphQL::ExecutionError) ArgumentError = Class.new(BaseError) + ResourceNotAvailable = Class.new(BaseError) end end end diff --git a/lib/gitlab/graphql/mount_mutation.rb b/lib/gitlab/graphql/mount_mutation.rb new file mode 100644 index 00000000000..8cab84d7a5f --- /dev/null +++ b/lib/gitlab/graphql/mount_mutation.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Gitlab + module Graphql + module MountMutation + extend ActiveSupport::Concern + + module ClassMethods + def mount_mutation(mutation_class) + # Using an underscored field name symbol will make `graphql-ruby` + # standardize the field name + field mutation_class.graphql_name.underscore.to_sym, + mutation: mutation_class + end + end + end + end +end diff --git a/lib/gitlab/import_export/merge_request_parser.rb b/lib/gitlab/import_export/merge_request_parser.rb index d0527f014a7..62a1833b39c 100644 --- a/lib/gitlab/import_export/merge_request_parser.rb +++ b/lib/gitlab/import_export/merge_request_parser.rb @@ -27,7 +27,11 @@ module Gitlab # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/1295 def fetch_ref - @project.repository.fetch_ref(@project.repository, source_ref: @diff_head_sha, target_ref: @merge_request.source_branch) + target_ref = Gitlab::Git::BRANCH_REF_PREFIX + @merge_request.source_branch + + unless @project.repository.fetch_source_branch!(@project.repository, @diff_head_sha, target_ref) + Rails.logger.warn("Import/Export warning: Failed to create #{target_ref} for MR: #{@merge_request.iid}") + end end def branch_exists?(branch_name) diff --git a/lib/gitlab/kubernetes/config_map.rb b/lib/gitlab/kubernetes/config_map.rb index 95e1054919d..8a8a59a9cd4 100644 --- a/lib/gitlab/kubernetes/config_map.rb +++ b/lib/gitlab/kubernetes/config_map.rb @@ -1,7 +1,7 @@ module Gitlab module Kubernetes class ConfigMap - def initialize(name, values) + def initialize(name, values = "") @name = name @values = values end @@ -13,6 +13,10 @@ module Gitlab resource end + def config_map_name + "values-content-configuration-#{name}" + end + private attr_reader :name, :values @@ -25,10 +29,6 @@ module Gitlab } end - def config_map_name - "values-content-configuration-#{name}" - end - def namespace Gitlab::Kubernetes::Helm::NAMESPACE end diff --git a/lib/gitlab/kubernetes/helm/api.rb b/lib/gitlab/kubernetes/helm/api.rb index 2edd34109ba..c4de9a398cc 100644 --- a/lib/gitlab/kubernetes/helm/api.rb +++ b/lib/gitlab/kubernetes/helm/api.rb @@ -8,9 +8,9 @@ module Gitlab end def install(command) - @namespace.ensure_exists! + namespace.ensure_exists! create_config_map(command) if command.config_map? - @kubeclient.create_pod(command.pod_resource) + kubeclient.create_pod(command.pod_resource) end ## @@ -20,23 +20,25 @@ module Gitlab # # values: "Pending", "Running", "Succeeded", "Failed", "Unknown" # - def installation_status(pod_name) - @kubeclient.get_pod(pod_name, @namespace.name).status.phase + def status(pod_name) + kubeclient.get_pod(pod_name, namespace.name).status.phase end - def installation_log(pod_name) - @kubeclient.get_pod_log(pod_name, @namespace.name).body + def log(pod_name) + kubeclient.get_pod_log(pod_name, namespace.name).body end - def delete_installation_pod!(pod_name) - @kubeclient.delete_pod(pod_name, @namespace.name) + def delete_pod!(pod_name) + kubeclient.delete_pod(pod_name, namespace.name) end private + attr_reader :kubeclient, :namespace + def create_config_map(command) command.config_map_resource.tap do |config_map_resource| - @kubeclient.create_config_map(config_map_resource) + kubeclient.create_config_map(config_map_resource) end end end diff --git a/lib/gitlab/prometheus/metric.rb b/lib/gitlab/prometheus/metric.rb index f54b2c6aaff..13cc59df795 100644 --- a/lib/gitlab/prometheus/metric.rb +++ b/lib/gitlab/prometheus/metric.rb @@ -3,7 +3,7 @@ module Gitlab class Metric include ActiveModel::Model - attr_accessor :title, :required_metrics, :weight, :y_label, :queries + attr_accessor :id, :title, :required_metrics, :weight, :y_label, :queries validates :title, :required_metrics, :weight, :y_label, :queries, presence: true diff --git a/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb b/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb index e677ec84cd4..8534afcc849 100644 --- a/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb +++ b/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb @@ -8,6 +8,7 @@ module Gitlab Deployment.find_by(id: deployment_id).try do |deployment| query_metrics( deployment.project, + deployment.environment, common_query_context( deployment.environment, timeframe_start: (deployment.created_at - 30.minutes).to_f, diff --git a/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb b/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb index 9273e69e158..e3af217b202 100644 --- a/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb +++ b/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb @@ -8,6 +8,7 @@ module Gitlab ::Environment.find_by(id: environment_id).try do |environment| query_metrics( environment.project, + environment, common_query_context(environment, timeframe_start: 8.hours.ago.to_f, timeframe_end: Time.now.to_f) ) end diff --git a/lib/gitlab/prometheus/queries/query_additional_metrics.rb b/lib/gitlab/prometheus/queries/query_additional_metrics.rb index f5879de1e94..3be35f189d0 100644 --- a/lib/gitlab/prometheus/queries/query_additional_metrics.rb +++ b/lib/gitlab/prometheus/queries/query_additional_metrics.rb @@ -2,7 +2,7 @@ module Gitlab module Prometheus module Queries module QueryAdditionalMetrics - def query_metrics(project, query_context) + def query_metrics(project, environment, query_context) matched_metrics(project).map(&query_group(query_context)) .select(&method(:group_with_any_metrics)) end @@ -14,12 +14,16 @@ module Gitlab lambda do |group| metrics = group.metrics.map do |metric| - { + metric_hsh = { title: metric.title, weight: metric.weight, y_label: metric.y_label, queries: metric.queries.map(&query_processor).select(&method(:query_with_result)) } + + metric_hsh[:id] = metric.id if metric.id + + metric_hsh end { diff --git a/locale/gitlab.pot b/locale/gitlab.pot index a313713a9a8..372032ef4a1 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -120,6 +120,11 @@ msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts msgstr[0] "" msgstr[1] "" +msgid "%{text} %{files}" +msgid_plural "%{text} %{files} files" +msgstr[0] "" +msgstr[1] "" + msgid "%{text} is available" msgstr "" @@ -1993,6 +1998,9 @@ msgstr "" msgid "Delete list" msgstr "" +msgid "Deleted" +msgstr "" + msgid "Deny" msgstr "" @@ -5547,10 +5555,8 @@ msgstr "" msgid "Up to date" msgstr "" -msgid "Update %{files}" -msgid_plural "Update %{files} files" -msgstr[0] "" -msgstr[1] "" +msgid "Update" +msgstr "" msgid "Update your group name, description, avatar, and other general settings." msgstr "" @@ -6011,9 +6017,6 @@ msgstr "" msgid "branch name" msgstr "" -msgid "ciReport|Show complete code vulnerabilities report" -msgstr "" - msgid "command line instructions" msgstr "" diff --git a/package.json b/package.json index 256ebc1fb6e..2a8761e32d6 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "webpack-prod": "NODE_ENV=production webpack --config config/webpack.config.js" }, "dependencies": { - "@gitlab-org/gitlab-svgs": "^1.26.0", + "@gitlab-org/gitlab-svgs": "^1.27.0", + "@gitlab-org/gitlab-ui": "1.0.5", "autosize": "^4.0.0", "axios": "^0.17.1", "babel-core": "^6.26.3", @@ -37,6 +38,7 @@ "core-js": "^2.4.1", "cropper": "^2.3.0", "css-loader": "^1.0.0", + "d3": "4.12.2", "d3-array": "^1.2.1", "d3-axis": "^1.0.8", "d3-brush": "^1.0.4", diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb index 0c8eca5229e..4c64270ce92 100644 --- a/qa/qa/runtime/browser.rb +++ b/qa/qa/runtime/browser.rb @@ -72,6 +72,7 @@ module QA Capybara::Selenium::Driver.new( app, browser: :chrome, + clear_local_storage: true, desired_capabilities: capabilities, options: options ) diff --git a/spec/controllers/projects/commits_controller_spec.rb b/spec/controllers/projects/commits_controller_spec.rb index d44048fdf55..a43bdd3ea80 100644 --- a/spec/controllers/projects/commits_controller_spec.rb +++ b/spec/controllers/projects/commits_controller_spec.rb @@ -9,6 +9,18 @@ describe Projects::CommitsController do project.add_maintainer(user) end + describe "GET commits_root" do + context "no ref is provided" do + it 'should redirect to the default branch of the project' do + get(:commits_root, + namespace_id: project.namespace, + project_id: project) + + expect(response).to redirect_to project_commits_path(project) + end + end + end + describe "GET show" do render_views diff --git a/spec/controllers/projects/labels_controller_spec.rb b/spec/controllers/projects/labels_controller_spec.rb index 273702e6d21..e03d23bcdf6 100644 --- a/spec/controllers/projects/labels_controller_spec.rb +++ b/spec/controllers/projects/labels_controller_spec.rb @@ -143,6 +143,14 @@ describe Projects::LabelsController do expect(GroupLabel.find_by(title: promoted_label_name)).not_to be_nil end + it 'renders label name without parsing it as HTML' do + label_1.update!(name: 'CCC<img src=x onerror=alert(document.domain)>') + + post :promote, namespace_id: project.namespace.to_param, project_id: project, id: label_1.to_param + + expect(flash[:notice]).to eq("CCC<img src=x onerror=alert(document.domain)> promoted to <a href=\"#{group_labels_path(project.group)}\"><u>group label</u></a>.") + end + context 'service raising InvalidRecord' do before do expect_any_instance_of(Labels::PromoteService).to receive(:execute) do |label| diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb index ea906cf7f32..6c2d1c7e92b 100644 --- a/spec/controllers/projects/milestones_controller_spec.rb +++ b/spec/controllers/projects/milestones_controller_spec.rb @@ -127,6 +127,14 @@ describe Projects::MilestonesController do expect(flash[:notice]).to eq("#{milestone.title} promoted to <a href=\"#{group_milestone_path(project.group, milestone.iid)}\"><u>group milestone</u></a>.") expect(response).to redirect_to(project_milestones_path(project)) end + + it 'renders milestone name without parsing it as HTML' do + milestone.update!(name: 'CCC<img src=x onerror=alert(document.domain)>') + + post :promote, namespace_id: project.namespace.id, project_id: project.id, id: milestone.iid + + expect(flash[:notice]).to eq("CCC promoted to <a href=\"#{group_milestone_path(project.group, milestone.iid)}\"><u>group milestone</u></a>.") + end end context 'promotion fails' do diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index 290fcd4f8e6..d89716b1b50 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -55,10 +55,8 @@ describe Projects::PipelinesController do stub_feature_flags(ci_pipeline_persisted_stages: false) end - it 'returns JSON with serialized pipelines', :request_store do - queries = ActiveRecord::QueryRecorder.new do - get_pipelines_index_json - end + it 'returns JSON with serialized pipelines' do + get_pipelines_index_json expect(response).to have_gitlab_http_status(:ok) expect(response).to match_response_schema('pipeline') @@ -73,8 +71,14 @@ describe Projects::PipelinesController do json_response.dig('pipelines', 0, 'details', 'stages').tap do |stages| expect(stages.count).to eq 3 end + end + + it 'does not execute N+1 queries' do + queries = ActiveRecord::QueryRecorder.new do + get_pipelines_index_json + end - expect(queries.count).to be_within(5).of(30) + expect(queries.count).to be <= 36 end end diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index fec1bea2751..1215b04913e 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -34,7 +34,7 @@ FactoryBot.define do builds_access_level = [evaluator.builds_access_level, evaluator.repository_access_level].min merge_requests_access_level = [evaluator.merge_requests_access_level, evaluator.repository_access_level].min - project.project_feature.update_columns( + project.project_feature.update( wiki_access_level: evaluator.wiki_access_level, builds_access_level: builds_access_level, snippets_access_level: evaluator.snippets_access_level, diff --git a/spec/factories/site_statistics.rb b/spec/factories/site_statistics.rb new file mode 100644 index 00000000000..dd8c795515a --- /dev/null +++ b/spec/factories/site_statistics.rb @@ -0,0 +1,7 @@ +FactoryBot.define do + factory :site_statistics, class: 'SiteStatistic' do + id 1 + repositories_count 999 + wikis_count 555 + end +end diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb index 35b1c46ecf6..83293c0ca7d 100644 --- a/spec/features/projects/jobs_spec.rb +++ b/spec/features/projects/jobs_spec.rb @@ -135,6 +135,20 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do end end + context 'sidebar' do + let(:job) { create(:ci_build, :success, :trace_live, pipeline: pipeline, name: '<img src=x onerror=alert(document.domain)>') } + + before do + visit project_job_path(project, job) + end + + it 'renders escaped tooltip name' do + page.within('aside.right-sidebar') do + expect(find('.active.build-job a')['data-title']).to eq('<img src="x"> - passed') + end + end + end + context 'when job is not running', :js do let(:job) { create(:ci_build, :success, :trace_artifact, pipeline: pipeline) } diff --git a/spec/features/users/add_email_to_existing_account.rb b/spec/features/users/add_email_to_existing_account.rb new file mode 100644 index 00000000000..4355f769429 --- /dev/null +++ b/spec/features/users/add_email_to_existing_account.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe 'AdditionalEmailToExistingAccount' do + describe 'add secondary email associated with account' do + let(:user) { create(:user) } + + it 'verifies confirmation of additional email' do + sign_in(user) + + email = create(:email, user: user) + visit email_confirmation_path(confirmation_token: email.confirmation_token) + expect(page).to have_content 'Your email address has been successfully confirmed.' + end + end +end diff --git a/spec/fixtures/emails/commands_in_reply.eml b/spec/fixtures/emails/commands_in_reply.eml index 712f6f797b4..6a467ccbbc6 100644 --- a/spec/fixtures/emails/commands_in_reply.eml +++ b/spec/fixtures/emails/commands_in_reply.eml @@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo> To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com> In-Reply-To: <issue_1@localhost> -References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> +References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost> Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' Mime-Version: 1.0 Content-Type: text/plain; diff --git a/spec/fixtures/emails/commands_only_reply.eml b/spec/fixtures/emails/commands_only_reply.eml index 2d2e2f94290..9b8d25c406e 100644 --- a/spec/fixtures/emails/commands_only_reply.eml +++ b/spec/fixtures/emails/commands_only_reply.eml @@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo> To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com> In-Reply-To: <issue_1@localhost> -References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> +References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost> Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' Mime-Version: 1.0 Content-Type: text/plain; diff --git a/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml b/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml index 39d5cefbc2a..609d9d9e93d 100644 --- a/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml +++ b/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references.eml @@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo> To: reply@appmail.adventuretime.ooo Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com> In-Reply-To: <issue_1@localhost> -References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> +References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost> Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' Mime-Version: 1.0 Content-Type: text/plain; diff --git a/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references_with_a_comma.eml b/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references_with_a_comma.eml index 6823db0cfc8..7be1ed5bf44 100644 --- a/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references_with_a_comma.eml +++ b/spec/fixtures/emails/reply_without_subaddressing_and_key_inside_references_with_a_comma.eml @@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo> To: reply@appmail.adventuretime.ooo Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com> In-Reply-To: <issue_1@localhost> -References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost>,<exchange@microsoft.com> +References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost>,<exchange@microsoft.com> Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' Mime-Version: 1.0 Content-Type: text/plain; diff --git a/spec/fixtures/emails/update_commands_only_reply.eml b/spec/fixtures/emails/update_commands_only_reply.eml index bb0d2b0e03a..927a62f6475 100644 --- a/spec/fixtures/emails/update_commands_only_reply.eml +++ b/spec/fixtures/emails/update_commands_only_reply.eml @@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo> To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com> In-Reply-To: <issue_1@localhost> -References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> +References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost> Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' Mime-Version: 1.0 Content-Type: text/plain; diff --git a/spec/fixtures/emails/valid_reply.eml b/spec/fixtures/emails/valid_reply.eml index 980e10a8812..5fbeebdb6b0 100644 --- a/spec/fixtures/emails/valid_reply.eml +++ b/spec/fixtures/emails/valid_reply.eml @@ -8,7 +8,7 @@ From: Jake the Dog <jake@adventuretime.ooo> To: reply+59d8df8370b7e95c5a49fbf86aeb2c93@appmail.adventuretime.ooo Message-ID: <CADkmRc+rNGAGGbV2iE5p918UVy4UyJqVcXRO2=otppgzduJSg@mail.gmail.com> In-Reply-To: <issue_1@localhost> -References: <issue_1@localhost> <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> +References: <reply-59d8df8370b7e95c5a49fbf86aeb2c93@localhost> <issue_1@localhost> Subject: re: [Discourse Meta] eviltrout posted in 'Adventure Time Sux' Mime-Version: 1.0 Content-Type: text/plain; diff --git a/spec/graphql/gitlab_schema_spec.rb b/spec/graphql/gitlab_schema_spec.rb index 515bbe78cb7..b9ddb427e85 100644 --- a/spec/graphql/gitlab_schema_spec.rb +++ b/spec/graphql/gitlab_schema_spec.rb @@ -18,8 +18,6 @@ describe GitlabSchema do end it 'has the base mutation' do - pending('Adding an empty mutation breaks the documentation explorer') - expect(described_class.mutation).to eq(::Types::MutationType.to_graphql) end diff --git a/spec/graphql/mutations/concerns/mutations/resolves_project_spec.rb b/spec/graphql/mutations/concerns/mutations/resolves_project_spec.rb new file mode 100644 index 00000000000..19f5a8907a2 --- /dev/null +++ b/spec/graphql/mutations/concerns/mutations/resolves_project_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe Mutations::ResolvesProject do + let(:mutation_class) do + Class.new(Mutations::BaseMutation) do + include Mutations::ResolvesProject + end + end + + let(:context) { double } + subject(:mutation) { mutation_class.new(object: nil, context: context) } + + it 'uses the ProjectsResolver to resolve projects by path' do + project = create(:project) + + expect(Resolvers::ProjectResolver).to receive(:new).with(object: nil, context: context).and_call_original + expect(mutation.resolve_project(full_path: project.full_path)).to eq(project) + end +end diff --git a/spec/graphql/mutations/merge_requests/set_wip_spec.rb b/spec/graphql/mutations/merge_requests/set_wip_spec.rb new file mode 100644 index 00000000000..e600abf3941 --- /dev/null +++ b/spec/graphql/mutations/merge_requests/set_wip_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' + +describe Mutations::MergeRequests::SetWip do + let(:merge_request) { create(:merge_request) } + let(:user) { create(:user) } + subject(:mutation) { described_class.new(object: nil, context: { current_user: user }) } + + describe '#resolve' do + let(:wip) { true } + let(:mutated_merge_request) { subject[:merge_request] } + subject { mutation.resolve(project_path: merge_request.project.full_path, iid: merge_request.iid, wip: wip) } + + it 'raises an error if the resource is not accessible to the user' do + expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + end + + context 'when the user can update the merge request' do + before do + merge_request.project.add_developer(user) + end + + it 'returns the merge request as a wip' do + expect(mutated_merge_request).to eq(merge_request) + expect(mutated_merge_request).to be_work_in_progress + expect(subject[:errors]).to be_empty + end + + it 'returns errors merge request could not be updated' do + # Make the merge request invalid + merge_request.allow_broken = true + merge_request.update!(source_project: nil) + + expect(subject[:errors]).not_to be_empty + end + + context 'when passing wip as false' do + let(:wip) { false } + + it 'removes `wip` from the title' do + merge_request.update(title: "WIP: working on it") + + expect(mutated_merge_request).not_to be_work_in_progress + end + + it 'does not do anything if the title did not start with wip' do + expect(mutated_merge_request).not_to be_work_in_progress + end + end + end + end +end diff --git a/spec/graphql/types/mutation_type_spec.rb b/spec/graphql/types/mutation_type_spec.rb new file mode 100644 index 00000000000..a67d83b1edf --- /dev/null +++ b/spec/graphql/types/mutation_type_spec.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Types::MutationType do + it 'is expected to have the MergeRequestSetWip' do + expect(described_class).to have_graphql_mutation(Mutations::MergeRequests::SetWip) + end +end diff --git a/spec/helpers/snippets_helper_spec.rb b/spec/helpers/snippets_helper_spec.rb index 0323ffb641c..ce5e037f88d 100644 --- a/spec/helpers/snippets_helper_spec.rb +++ b/spec/helpers/snippets_helper_spec.rb @@ -7,13 +7,13 @@ describe SnippetsHelper do it 'gives view raw button of embedded snippets for project snippets' do @snippet = create(:project_snippet, :public) - expect(embedded_snippet_raw_button.to_s).to eq("<a class=\"btn\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Open raw\" href=\"#{raw_project_snippet_url(@snippet.project, @snippet)}\">#{external_snippet_icon('doc_code')}</a>") + expect(embedded_snippet_raw_button.to_s).to eq("<a class=\"btn\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Open raw\" href=\"#{raw_project_snippet_url(@snippet.project, @snippet)}\">#{external_snippet_icon('doc-code')}</a>") end it 'gives view raw button of embedded snippets for personal snippets' do @snippet = create(:personal_snippet, :public) - expect(embedded_snippet_raw_button.to_s).to eq("<a class=\"btn\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Open raw\" href=\"#{raw_snippet_url(@snippet)}\">#{external_snippet_icon('doc_code')}</a>") + expect(embedded_snippet_raw_button.to_s).to eq("<a class=\"btn\" target=\"_blank\" rel=\"noopener noreferrer\" title=\"Open raw\" href=\"#{raw_snippet_url(@snippet)}\">#{external_snippet_icon('doc-code')}</a>") end end diff --git a/spec/javascripts/fixtures/graph.html.haml b/spec/javascripts/fixtures/graph.html.haml deleted file mode 100644 index 4fedb0f1ded..00000000000 --- a/spec/javascripts/fixtures/graph.html.haml +++ /dev/null @@ -1 +0,0 @@ -#js-pipeline-graph-vue{ data: { endpoint: "foo" } } diff --git a/spec/javascripts/helpers/vue_mount_component_helper.js b/spec/javascripts/helpers/vue_mount_component_helper.js index 5ba17ecf5b5..1057f0aca3e 100644 --- a/spec/javascripts/helpers/vue_mount_component_helper.js +++ b/spec/javascripts/helpers/vue_mount_component_helper.js @@ -15,4 +15,14 @@ export const mountComponentWithStore = (Component, { el, props, store }) => propsData: props || {}, }).$mount(el); +export const mountComponentWithSlots = (Component, { props, slots }) => { + const component = new Component({ + propsData: props || {}, + }); + + component.$slots = slots; + + return component.$mount(); +}; + export default mountComponent; diff --git a/spec/javascripts/ide/components/changed_file_icon_spec.js b/spec/javascripts/ide/components/changed_file_icon_spec.js index 541864e912e..7308219f705 100644 --- a/spec/javascripts/ide/components/changed_file_icon_spec.js +++ b/spec/javascripts/ide/components/changed_file_icon_spec.js @@ -33,14 +33,14 @@ describe('IDE changed file icon', () => { }); describe('changedIconClass', () => { - it('includes multi-file-modified when not a temp file', () => { - expect(vm.changedIconClass).toContain('multi-file-modified'); + it('includes ide-file-modified when not a temp file', () => { + expect(vm.changedIconClass).toContain('ide-file-modified'); }); - it('includes multi-file-addition when a temp file', () => { + it('includes ide-file-addition when a temp file', () => { vm.file.tempFile = true; - expect(vm.changedIconClass).toContain('multi-file-addition'); + expect(vm.changedIconClass).toContain('ide-file-addition'); }); }); }); diff --git a/spec/javascripts/ide/components/commit_sidebar/actions_spec.js b/spec/javascripts/ide/components/commit_sidebar/actions_spec.js index 27f10caccb1..3a5d6c8a90b 100644 --- a/spec/javascripts/ide/components/commit_sidebar/actions_spec.js +++ b/spec/javascripts/ide/components/commit_sidebar/actions_spec.js @@ -46,4 +46,12 @@ describe('IDE commit sidebar actions', () => { done(); }); }); + + describe('commitToCurrentBranchText', () => { + it('escapes current branch', () => { + vm.$store.state.currentBranchId = '<img src="x" />'; + + expect(vm.commitToCurrentBranchText).not.toContain('<img src="x" />'); + }); + }); }); diff --git a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js index bf96170f703..41d8bfff7e7 100644 --- a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js +++ b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js @@ -76,17 +76,29 @@ describe('Multi-file editor commit sidebar list item', () => { expect(vm.iconName).toBe('file-addition'); }); + + it('returns deletion', () => { + f.deleted = true; + + expect(vm.iconName).toBe('file-deletion'); + }); }); describe('iconClass', () => { it('returns modified when not a tempFile', () => { - expect(vm.iconClass).toContain('multi-file-modified'); + expect(vm.iconClass).toContain('ide-file-modified'); }); it('returns addition when not a tempFile', () => { f.tempFile = true; - expect(vm.iconClass).toContain('multi-file-addition'); + expect(vm.iconClass).toContain('ide-file-addition'); + }); + + it('returns deletion', () => { + f.deleted = true; + + expect(vm.iconClass).toContain('ide-file-deletion'); }); }); }); diff --git a/spec/javascripts/ide/components/new_dropdown/index_spec.js b/spec/javascripts/ide/components/new_dropdown/index_spec.js index 4d704b80209..092c405a70b 100644 --- a/spec/javascripts/ide/components/new_dropdown/index_spec.js +++ b/spec/javascripts/ide/components/new_dropdown/index_spec.js @@ -14,6 +14,7 @@ describe('new dropdown component', () => { branch: 'master', path: '', mouseOver: false, + type: 'tree', }); vm.$store.state.currentProjectId = 'abcproject'; @@ -67,4 +68,14 @@ describe('new dropdown component', () => { }); }); }); + + describe('delete entry', () => { + it('calls delete action', () => { + spyOn(vm, 'deleteEntry'); + + vm.$el.querySelectorAll('.dropdown-menu button')[3].click(); + + expect(vm.deleteEntry).toHaveBeenCalledWith(''); + }); + }); }); diff --git a/spec/javascripts/ide/components/repo_editor_spec.js b/spec/javascripts/ide/components/repo_editor_spec.js index 2256deb7dac..0e2e246defd 100644 --- a/spec/javascripts/ide/components/repo_editor_spec.js +++ b/spec/javascripts/ide/components/repo_editor_spec.js @@ -1,5 +1,6 @@ import Vue from 'vue'; import MockAdapter from 'axios-mock-adapter'; +import '~/behaviors/markdown/render_gfm'; import axios from '~/lib/utils/axios_utils'; import store from '~/ide/stores'; import repoEditor from '~/ide/components/repo_editor.vue'; @@ -25,6 +26,8 @@ describe('RepoEditor', () => { vm.$store.state.openFiles.push(f); Vue.set(vm.$store.state.entries, f.path, f); + spyOn(vm, 'getFileData').and.returnValue(Promise.resolve()); + vm.$mount(); Vue.nextTick(() => setTimeout(done)); diff --git a/spec/javascripts/ide/components/repo_file_spec.js b/spec/javascripts/ide/components/repo_file_spec.js index 156233653ab..f99d1f9890a 100644 --- a/spec/javascripts/ide/components/repo_file_spec.js +++ b/spec/javascripts/ide/components/repo_file_spec.js @@ -91,25 +91,6 @@ describe('RepoFile', () => { done(); }); }); - - it('disables action dropdown', done => { - createComponent({ - file: { - ...file('t4'), - type: 'tree', - branchId: 'master', - projectId: 'project', - }, - level: 0, - disableActionDropdown: true, - }); - - setTimeout(() => { - expect(vm.$el.querySelector('.ide-new-btn')).toBeNull(); - - done(); - }); - }); }); describe('locked file', () => { diff --git a/spec/javascripts/ide/components/repo_tab_spec.js b/spec/javascripts/ide/components/repo_tab_spec.js index fc0695a4263..278a0753322 100644 --- a/spec/javascripts/ide/components/repo_tab_spec.js +++ b/spec/javascripts/ide/components/repo_tab_spec.js @@ -93,13 +93,13 @@ describe('RepoTab', () => { Vue.nextTick() .then(() => { - expect(vm.$el.querySelector('.multi-file-modified')).toBeNull(); + expect(vm.$el.querySelector('.ide-file-modified')).toBeNull(); vm.$el.dispatchEvent(new Event('mouseout')); }) .then(Vue.nextTick) .then(() => { - expect(vm.$el.querySelector('.multi-file-modified')).not.toBeNull(); + expect(vm.$el.querySelector('.ide-file-modified')).not.toBeNull(); done(); }) diff --git a/spec/javascripts/ide/stores/actions/file_spec.js b/spec/javascripts/ide/stores/actions/file_spec.js index f570c0b16bd..72eb20bdc87 100644 --- a/spec/javascripts/ide/stores/actions/file_spec.js +++ b/spec/javascripts/ide/stores/actions/file_spec.js @@ -366,6 +366,23 @@ describe('IDE store file actions', () => { }); }); + describe('return JSON', () => { + beforeEach(() => { + mock.onGet(/(.*)/).replyOnce(200, JSON.stringify({ test: '123' })); + }); + + it('does not parse returned JSON', done => { + store + .dispatch('getRawFileData', { path: tmpFile.path }) + .then(() => { + expect(tmpFile.raw).toEqual('{"test":"123"}'); + + done(); + }) + .catch(done.fail); + }); + }); + describe('error', () => { beforeEach(() => { mock.onGet(/(.*)/).networkError(); diff --git a/spec/javascripts/ide/stores/actions_spec.js b/spec/javascripts/ide/stores/actions_spec.js index 8b665a6d79e..792a716565c 100644 --- a/spec/javascripts/ide/stores/actions_spec.js +++ b/spec/javascripts/ide/stores/actions_spec.js @@ -7,6 +7,7 @@ import actions, { updateActivityBarView, updateTempFlagForEntry, setErrorMessage, + deleteEntry, } from '~/ide/stores/actions'; import store from '~/ide/stores'; import * as types from '~/ide/stores/mutation_types'; @@ -457,4 +458,19 @@ describe('Multi-file store actions', () => { ); }); }); + + describe('deleteEntry', () => { + it('commits entry deletion', done => { + store.state.entries.path = 'testing'; + + testAction( + deleteEntry, + 'path', + store.state, + [{ type: types.DELETE_ENTRY, payload: 'path' }], + [{ type: 'burstUnusedSeal' }, { type: 'closeFile', payload: store.state.entries.path }], + done, + ); + }); + }); }); diff --git a/spec/javascripts/ide/stores/modules/commit/getters_spec.js b/spec/javascripts/ide/stores/modules/commit/getters_spec.js index 44c941d6dbb..3f4bf407a1f 100644 --- a/spec/javascripts/ide/stores/modules/commit/getters_spec.js +++ b/spec/javascripts/ide/stores/modules/commit/getters_spec.js @@ -123,6 +123,22 @@ describe('IDE commit module getters', () => { 'Update test-file, index.js files', ); }); + + it('returns commitMessage with deleted files', () => { + rootState[key].push( + { + path: 'test-file', + deleted: true, + }, + { + path: 'index.js', + }, + ); + + expect(getters.preBuiltCommitMessage(state, null, rootState)).toBe( + 'Update index.js\nDeleted test-file', + ); + }); }); }); }); diff --git a/spec/javascripts/ide/stores/mutations/file_spec.js b/spec/javascripts/ide/stores/mutations/file_spec.js index 52f83be8e8c..efd0d86552b 100644 --- a/spec/javascripts/ide/stores/mutations/file_spec.js +++ b/spec/javascripts/ide/stores/mutations/file_spec.js @@ -94,6 +94,35 @@ describe('IDE store file mutations', () => { expect(localFile.raw).toBe('testing'); }); + + it('adds raw data to open pending file', () => { + localState.openFiles.push({ + ...localFile, + pending: true, + }); + + mutations.SET_FILE_RAW_DATA(localState, { + file: localFile, + raw: 'testing', + }); + + expect(localState.openFiles[0].raw).toBe('testing'); + }); + + it('does not add raw data to open pending tempFile file', () => { + localState.openFiles.push({ + ...localFile, + pending: true, + tempFile: true, + }); + + mutations.SET_FILE_RAW_DATA(localState, { + file: localFile, + raw: 'testing', + }); + + expect(localState.openFiles[0].raw).not.toBe('testing'); + }); }); describe('SET_FILE_BASE_RAW_DATA', () => { @@ -205,6 +234,11 @@ describe('IDE store file mutations', () => { beforeEach(() => { localFile.content = 'test'; localFile.changed = true; + localState.currentProjectId = 'gitlab-ce'; + localState.currentBranchId = 'master'; + localState.trees['gitlab-ce/master'] = { + tree: [], + }; }); it('resets content and changed', () => { @@ -213,6 +247,36 @@ describe('IDE store file mutations', () => { expect(localFile.content).toBe(''); expect(localFile.changed).toBeFalsy(); }); + + it('adds to root tree if deleted', () => { + localFile.deleted = true; + + mutations.DISCARD_FILE_CHANGES(localState, localFile.path); + + expect(localState.trees['gitlab-ce/master'].tree).toEqual([ + { + ...localFile, + deleted: false, + }, + ]); + }); + + it('adds to parent tree if deleted', () => { + localFile.deleted = true; + localFile.parentPath = 'parentPath'; + localState.entries.parentPath = { + tree: [], + }; + + mutations.DISCARD_FILE_CHANGES(localState, localFile.path); + + expect(localState.entries.parentPath.tree).toEqual([ + { + ...localFile, + deleted: false, + }, + ]); + }); }); describe('ADD_FILE_TO_CHANGED', () => { diff --git a/spec/javascripts/ide/stores/mutations_spec.js b/spec/javascripts/ide/stores/mutations_spec.js index 98016f593aa..8b5f2d0bdfa 100644 --- a/spec/javascripts/ide/stores/mutations_spec.js +++ b/spec/javascripts/ide/stores/mutations_spec.js @@ -156,4 +156,61 @@ describe('Multi-file store mutations', () => { expect(localState.errorMessage).toBe('error'); }); }); + + describe('DELETE_ENTRY', () => { + beforeEach(() => { + localState.currentProjectId = 'gitlab-ce'; + localState.currentBranchId = 'master'; + localState.trees['gitlab-ce/master'] = { + tree: [], + }; + }); + + it('sets deleted flag', () => { + localState.entries.filePath = { + deleted: false, + }; + + mutations.DELETE_ENTRY(localState, 'filePath'); + + expect(localState.entries.filePath.deleted).toBe(true); + }); + + it('removes from root tree', () => { + localState.entries.filePath = { + path: 'filePath', + deleted: false, + }; + localState.trees['gitlab-ce/master'].tree.push(localState.entries.filePath); + + mutations.DELETE_ENTRY(localState, 'filePath'); + + expect(localState.trees['gitlab-ce/master'].tree).toEqual([]); + }); + + it('removes from parent tree', () => { + localState.entries.filePath = { + path: 'filePath', + deleted: false, + parentPath: 'parentPath', + }; + localState.entries.parentPath = { + tree: [localState.entries.filePath], + }; + + mutations.DELETE_ENTRY(localState, 'filePath'); + + expect(localState.entries.parentPath.tree).toEqual([]); + }); + + it('adds to changedFiles', () => { + localState.entries.filePath = { + deleted: false, + }; + + mutations.DELETE_ENTRY(localState, 'filePath'); + + expect(localState.changedFiles).toEqual([localState.entries.filePath]); + }); + }); }); diff --git a/spec/javascripts/ide/stores/utils_spec.js b/spec/javascripts/ide/stores/utils_spec.js index 6c5980cfae4..89db50b8874 100644 --- a/spec/javascripts/ide/stores/utils_spec.js +++ b/spec/javascripts/ide/stores/utils_spec.js @@ -86,6 +86,11 @@ describe('Multi-file store utils', () => { base64: true, lastCommitSha: '123456789', }, + { + ...file('deletedFile'), + path: 'deletedFile', + deleted: true, + }, ], currentBranchId: 'master', }; @@ -115,6 +120,13 @@ describe('Multi-file store utils', () => { encoding: 'base64', last_commit_id: '123456789', }, + { + action: 'delete', + file_path: 'deletedFile', + content: '', + encoding: 'text', + last_commit_id: undefined, + }, ], start_branch: undefined, }); @@ -173,4 +185,65 @@ describe('Multi-file store utils', () => { }); }); }); + + describe('commitActionForFile', () => { + it('returns deleted for deleted file', () => { + expect(utils.commitActionForFile({ deleted: true })).toBe('delete'); + }); + + it('returns create for tempFile', () => { + expect(utils.commitActionForFile({ tempFile: true })).toBe('create'); + }); + + it('returns update by default', () => { + expect(utils.commitActionForFile({})).toBe('update'); + }); + }); + + describe('getCommitFiles', () => { + it('returns flattened list of files and folders', () => { + const files = [ + { + path: 'a', + type: 'blob', + deleted: true, + }, + { + path: 'b', + type: 'tree', + deleted: true, + tree: [ + { + path: 'c', + type: 'blob', + }, + { + path: 'd', + type: 'blob', + }, + ], + }, + ]; + + const flattendFiles = utils.getCommitFiles(files); + + expect(flattendFiles).toEqual([ + { + path: 'a', + type: 'blob', + deleted: true, + }, + { + path: 'c', + type: 'blob', + deleted: true, + }, + { + path: 'd', + type: 'blob', + deleted: true, + }, + ]); + }); + }); }); diff --git a/spec/javascripts/pipelines/graph/dropdown_job_component_spec.js b/spec/javascripts/pipelines/graph/dropdown_job_component_spec.js new file mode 100644 index 00000000000..608a0d4be67 --- /dev/null +++ b/spec/javascripts/pipelines/graph/dropdown_job_component_spec.js @@ -0,0 +1,93 @@ +import Vue from 'vue'; +import component from '~/pipelines/components/graph/dropdown_job_component.vue'; +import mountComponent from 'spec/helpers/vue_mount_component_helper'; + +describe('dropdown job component', () => { + const Component = Vue.extend(component); + let vm; + + const mock = { + jobs: [ + { + id: 4256, + name: '<img src=x onerror=alert(document.domain)>', + status: { + icon: 'icon_status_success', + text: 'passed', + label: 'passed', + tooltip: 'passed', + group: 'success', + details_path: '/root/ci-mock/builds/4256', + has_details: true, + action: { + icon: 'retry', + title: 'Retry', + path: '/root/ci-mock/builds/4256/retry', + method: 'post', + }, + }, + }, + { + id: 4299, + name: 'test', + status: { + icon: 'icon_status_success', + text: 'passed', + label: 'passed', + tooltip: 'passed', + group: 'success', + details_path: '/root/ci-mock/builds/4299', + has_details: true, + action: { + icon: 'retry', + title: 'Retry', + path: '/root/ci-mock/builds/4299/retry', + method: 'post', + }, + }, + }, + ], + name: 'rspec:linux', + size: 2, + status: { + icon: 'icon_status_success', + text: 'passed', + label: 'passed', + tooltip: 'passed', + group: 'success', + details_path: '/root/ci-mock/builds/4256', + has_details: true, + action: { + icon: 'retry', + title: 'Retry', + path: '/root/ci-mock/builds/4256/retry', + method: 'post', + }, + }, + }; + + afterEach(() => { + vm.$destroy(); + }); + + beforeEach(() => { + vm = mountComponent(Component, { job: mock }); + }); + + it('renders button with job name and size', () => { + expect(vm.$el.querySelector('button').textContent).toContain(mock.name); + expect(vm.$el.querySelector('button').textContent).toContain(mock.size); + }); + + it('renders dropdown with jobs', () => { + expect(vm.$el.querySelectorAll('.scrollable-menu>ul>li').length).toEqual(mock.jobs.length); + }); + + it('escapes tooltip title', () => { + expect( + vm.$el.querySelector('.js-pipeline-graph-job-link').getAttribute('data-original-title'), + ).toEqual( + '<img src=x onerror=alert(document.domain)> - passed', + ); + }); +}); diff --git a/spec/javascripts/pipelines/graph/graph_component_spec.js b/spec/javascripts/pipelines/graph/graph_component_spec.js index 713baa65a17..b6fa4272c8b 100644 --- a/spec/javascripts/pipelines/graph/graph_component_spec.js +++ b/spec/javascripts/pipelines/graph/graph_component_spec.js @@ -1,37 +1,33 @@ import Vue from 'vue'; +import mountComponent from 'spec/helpers/vue_mount_component_helper'; import graphComponent from '~/pipelines/components/graph/graph_component.vue'; import graphJSON from './mock_data'; describe('graph component', () => { - preloadFixtures('static/graph.html.raw'); + const GraphComponent = Vue.extend(graphComponent); + let component; - let GraphComponent; - - beforeEach(() => { - loadFixtures('static/graph.html.raw'); - GraphComponent = Vue.extend(graphComponent); + afterEach(() => { + component.$destroy(); }); describe('while is loading', () => { it('should render a loading icon', () => { - const component = new GraphComponent({ - propsData: { - isLoading: true, - pipeline: {}, - }, - }).$mount('#js-pipeline-graph-vue'); + component = mountComponent(GraphComponent, { + isLoading: true, + pipeline: {}, + }); + expect(component.$el.querySelector('.loading-icon')).toBeDefined(); }); }); describe('with data', () => { it('should render the graph', () => { - const component = new GraphComponent({ - propsData: { - isLoading: false, - pipeline: graphJSON, - }, - }).$mount('#js-pipeline-graph-vue'); + component = mountComponent(GraphComponent, { + isLoading: false, + pipeline: graphJSON, + }); expect(component.$el.classList.contains('js-pipeline-graph')).toEqual(true); @@ -52,4 +48,15 @@ describe('graph component', () => { expect(component.$el.querySelector('.stage-column-list')).toBeDefined(); }); }); + + describe('capitalizeStageName', () => { + it('capitalizes and escapes stage name', () => { + component = mountComponent(GraphComponent, { + isLoading: false, + pipeline: graphJSON, + }); + + expect(component.$el.querySelector('.stage-column:nth-child(2) .stage-name').textContent.trim()).toEqual('Deploy <img src=x onerror=alert(document.domain)>'); + }); + }); }); diff --git a/spec/javascripts/pipelines/graph/job_component_spec.js b/spec/javascripts/pipelines/graph/job_component_spec.js index 56476253ad0..59f18d9397d 100644 --- a/spec/javascripts/pipelines/graph/job_component_spec.js +++ b/spec/javascripts/pipelines/graph/job_component_spec.js @@ -3,7 +3,7 @@ import jobComponent from '~/pipelines/components/graph/job_component.vue'; import mountComponent from 'spec/helpers/vue_mount_component_helper'; describe('pipeline graph job component', () => { - let JobComponent; + const JobComponent = Vue.extend(jobComponent); let component; const mockJob = { @@ -26,10 +26,6 @@ describe('pipeline graph job component', () => { }, }; - beforeEach(() => { - JobComponent = Vue.extend(jobComponent); - }); - afterEach(() => { component.$destroy(); }); @@ -165,4 +161,24 @@ describe('pipeline graph job component', () => { expect(component.$el.querySelector(tooltipBoundary)).toBeNull(); }); }); + + describe('tooltipText', () => { + it('escapes job name', () => { + component = mountComponent(JobComponent, { + job: { + id: 4259, + name: '<img src=x onerror=alert(document.domain)>', + status: { + icon: 'icon_status_success', + label: 'success', + tooltip: 'failed', + }, + }, + }); + + expect( + component.$el.querySelector('.js-job-component-tooltip').getAttribute('data-original-title'), + ).toEqual('<img src=x onerror=alert(document.domain)> - failed'); + }); + }); }); diff --git a/spec/javascripts/pipelines/graph/mock_data.js b/spec/javascripts/pipelines/graph/mock_data.js index b2161d54bce..a4a5d78f906 100644 --- a/spec/javascripts/pipelines/graph/mock_data.js +++ b/spec/javascripts/pipelines/graph/mock_data.js @@ -91,7 +91,7 @@ export default { dropdown_path: '/root/ci-mock/pipelines/123/stage.json?stage=test', }, { - name: 'deploy', + name: 'deploy <img src=x onerror=alert(document.domain)>', title: 'deploy: passed', groups: [ { diff --git a/spec/javascripts/pipelines/graph/stage_column_component_spec.js b/spec/javascripts/pipelines/graph/stage_column_component_spec.js index 9d1e71fd117..f6e6bd3132e 100644 --- a/spec/javascripts/pipelines/graph/stage_column_component_spec.js +++ b/spec/javascripts/pipelines/graph/stage_column_component_spec.js @@ -1,8 +1,11 @@ import Vue from 'vue'; import stageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue'; +import mountComponent from 'spec/helpers/vue_mount_component_helper'; describe('stage column component', () => { let component; + const StageColumnComponent = Vue.extend(stageColumnComponent); + const mockJob = { id: 4250, name: 'test', @@ -22,7 +25,6 @@ describe('stage column component', () => { }; beforeEach(() => { - const StageColumnComponent = Vue.extend(stageColumnComponent); const mockJobs = []; for (let i = 0; i < 3; i += 1) { @@ -31,12 +33,10 @@ describe('stage column component', () => { mockJobs.push(mockedJob); } - component = new StageColumnComponent({ - propsData: { - title: 'foo', - jobs: mockJobs, - }, - }).$mount(); + component = mountComponent(StageColumnComponent, { + title: 'foo', + jobs: mockJobs, + }); }); it('should render provided title', () => { @@ -46,4 +46,27 @@ describe('stage column component', () => { it('should render the provided jobs', () => { expect(component.$el.querySelectorAll('.builds-container > ul > li').length).toEqual(3); }); + + describe('jobId', () => { + it('escapes job name', () => { + component = mountComponent(StageColumnComponent, { + jobs: [ + { + id: 4259, + name: '<img src=x onerror=alert(document.domain)>', + status: { + icon: 'icon_status_success', + label: 'success', + tooltip: '<img src=x onerror=alert(document.domain)>', + }, + }, + ], + title: 'test', + }); + + expect( + component.$el.querySelector('.builds-container li').getAttribute('id'), + ).toEqual('ci-badge-<img src=x onerror=alert(document.domain)>'); + }); + }); }); diff --git a/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js b/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js index b58de607ece..9dff52a9d49 100644 --- a/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js +++ b/spec/javascripts/sidebar/components/time_tracking/time_tracker_spec.js @@ -1,4 +1,3 @@ -import $ from 'jquery'; import Vue from 'vue'; import TimeTracker from '~/sidebar/components/time_tracking/time_tracker.vue'; @@ -94,21 +93,14 @@ describe('Issuable Time Tracker', () => { describe('Remaining meter', () => { it('should display the remaining meter with the correct width', done => { Vue.nextTick(() => { - const meterWidth = vm.$el.querySelector('.time-tracking-comparison-pane .meter-fill') - .style.width; - const correctWidth = '5%'; - - expect(meterWidth).toBe(correctWidth); + expect(vm.$el.querySelector('.time-tracking-comparison-pane .progress[value="5"]')).not.toBeNull(); done(); }); }); it('should display the remaining meter with the correct background color when within estimate', done => { Vue.nextTick(() => { - const styledMeter = $(vm.$el).find( - '.time-tracking-comparison-pane .within_estimate .meter-fill', - ); - expect(styledMeter.length).toBe(1); + expect(vm.$el.querySelector('.time-tracking-comparison-pane .progress[variant="primary"]')).not.toBeNull(); done(); }); }); @@ -117,10 +109,7 @@ describe('Issuable Time Tracker', () => { vm.time_estimate = 100000; vm.time_spent = 20000000; Vue.nextTick(() => { - const styledMeter = $(vm.$el).find( - '.time-tracking-comparison-pane .over_estimate .meter-fill', - ); - expect(styledMeter.length).toBe(1); + expect(vm.$el.querySelector('.time-tracking-comparison-pane .progress[variant="danger"]')).not.toBeNull(); done(); }); }); diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js index bc00fdfd73c..59e472789e2 100644 --- a/spec/javascripts/test_bundle.js +++ b/spec/javascripts/test_bundle.js @@ -91,6 +91,19 @@ beforeEach(() => { Vue.http.interceptors = builtinVueHttpInterceptors.slice(); }); +let longRunningTestTimeoutHandle; + +beforeEach((done) => { + longRunningTestTimeoutHandle = setTimeout(() => { + done.fail('Test is running too long!'); + }, 1000); + done(); +}); + +afterEach(() => { + clearTimeout(longRunningTestTimeoutHandle); +}); + const axiosDefaultAdapter = getDefaultAdapter(); // render all of our tests diff --git a/spec/javascripts/vue_shared/components/bar_chart_spec.js b/spec/javascripts/vue_shared/components/bar_chart_spec.js new file mode 100644 index 00000000000..7e91cd6f63f --- /dev/null +++ b/spec/javascripts/vue_shared/components/bar_chart_spec.js @@ -0,0 +1,85 @@ +import Vue from 'vue'; +import BarChart from '~/vue_shared/components/bar_chart.vue'; +import mountComponent from 'spec/helpers/vue_mount_component_helper'; + +function getRandomArbitrary(min, max) { + return Math.random() * (max - min) + min; +} + +function generateRandomData(dataNumber) { + const randomGraphData = []; + + for (let i = 1; i <= dataNumber; i += 1) { + randomGraphData.push({ + name: `random ${i}`, + value: parseInt(getRandomArbitrary(1, 8), 10), + }); + } + + return randomGraphData; +} + +describe('Bar chart component', () => { + let barChart; + const graphData = generateRandomData(10); + + beforeEach(() => { + const BarChartComponent = Vue.extend(BarChart); + + barChart = mountComponent(BarChartComponent, { + graphData, + yAxisLabel: 'data', + }); + }); + + afterEach(() => { + barChart.$destroy(); + }); + + it('calculates the padding for even distribution across bars', () => { + barChart.vbWidth = 1000; + const result = barChart.calculatePadding(30); + + // since padding can't be higher than 1 and lower than 0 + // for more info: https://github.com/d3/d3-scale#band-scales + expect(result).not.toBeLessThan(0); + expect(result).not.toBeGreaterThan(1); + }); + + it('formats the tooltip title', () => { + const tooltipTitle = barChart.setTooltipTitle(barChart.graphData[0]); + + expect(tooltipTitle).toContain('random 1:'); + }); + + it('has a translates the bar graphs on across the X axis', () => { + barChart.panX = 100; + + expect(barChart.barTranslationTransform).toEqual('translate(100, 0)'); + }); + + it('translates the scroll indicator to the far right side', () => { + barChart.vbWidth = 500; + + expect(barChart.scrollIndicatorTransform).toEqual('translate(420, 0)'); + }); + + it('translates the x-axis to the bottom of the viewbox and pan coordinates', () => { + barChart.panX = 100; + barChart.vbHeight = 250; + + expect(barChart.xAxisLocation).toEqual('translate(100, 250)'); + }); + + it('Contains a total of 4 ticks across the y axis', () => { + const ticks = barChart.$el.querySelector('.y-axis').querySelectorAll('.tick').length; + + expect(ticks).toEqual(4); + }); + + it('rotates the x axis labels a total of 90 degress (CCW)', () => { + const xAxisLabel = barChart.$el.querySelector('.x-axis').querySelectorAll('text')[0]; + + expect(xAxisLabel.getAttribute('transform')).toEqual('rotate(-90)'); + }); +}); diff --git a/spec/javascripts/vue_shared/components/panel_resizer_spec.js b/spec/javascripts/vue_shared/components/panel_resizer_spec.js index 8efcb54659d..f1e62069462 100644 --- a/spec/javascripts/vue_shared/components/panel_resizer_spec.js +++ b/spec/javascripts/vue_shared/components/panel_resizer_spec.js @@ -29,7 +29,7 @@ describe('Panel Resizer component', () => { }); expect(vm.$el.tagName).toEqual('DIV'); - expect(vm.$el.getAttribute('class')).toBe('dragHandle dragleft'); + expect(vm.$el.getAttribute('class')).toBe('drag-handle drag-left'); expect(vm.$el.getAttribute('style')).toBe('cursor: ew-resize;'); }); @@ -40,7 +40,7 @@ describe('Panel Resizer component', () => { }); expect(vm.$el.tagName).toEqual('DIV'); - expect(vm.$el.getAttribute('class')).toBe('dragHandle dragright'); + expect(vm.$el.getAttribute('class')).toBe('drag-handle drag-right'); }); it('drag the resizer', () => { diff --git a/spec/javascripts/vue_shared/components/reports/report_issues_spec.js b/spec/javascripts/vue_shared/components/reports/report_issues_spec.js deleted file mode 100644 index e69de29bb2d..00000000000 --- a/spec/javascripts/vue_shared/components/reports/report_issues_spec.js +++ /dev/null diff --git a/spec/javascripts/vue_shared/components/reports/report_section_spec.js b/spec/javascripts/vue_shared/components/reports/report_section_spec.js index 07401181ffd..4e3986acb16 100644 --- a/spec/javascripts/vue_shared/components/reports/report_section_spec.js +++ b/spec/javascripts/vue_shared/components/reports/report_section_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; import reportSection from '~/vue_shared/components/reports/report_section.vue'; -import mountComponent from 'spec/helpers/vue_mount_component_helper'; +import mountComponent, { mountComponentWithSlots } from 'spec/helpers/vue_mount_component_helper'; describe('Report section', () => { let vm; @@ -23,7 +23,7 @@ describe('Report section', () => { describe('computed', () => { beforeEach(() => { vm = mountComponent(ReportSection, { - type: 'codequality', + component: '', status: 'SUCCESS', loadingText: 'Loading codeclimate report', errorText: 'foo', @@ -89,7 +89,7 @@ describe('Report section', () => { describe('when it is loading', () => { it('should render loading indicator', () => { vm = mountComponent(ReportSection, { - type: 'codequality', + component: '', status: 'LOADING', loadingText: 'Loading codeclimate report', errorText: 'foo', @@ -103,7 +103,7 @@ describe('Report section', () => { describe('with success status', () => { beforeEach(() => { vm = mountComponent(ReportSection, { - type: 'codequality', + component: '', status: 'SUCCESS', loadingText: 'Loading codeclimate report', errorText: 'foo', @@ -161,7 +161,7 @@ describe('Report section', () => { describe('with failed request', () => { it('should render error indicator', () => { vm = mountComponent(ReportSection, { - type: 'codequality', + component: '', status: 'ERROR', loadingText: 'Loading codeclimate report', errorText: 'Failed to load codeclimate report', @@ -171,4 +171,27 @@ describe('Report section', () => { expect(vm.$el.textContent.trim()).toEqual('Failed to load codeclimate report'); }); }); + + describe('with action buttons passed to the slot', () => { + beforeEach(() => { + vm = mountComponentWithSlots(ReportSection, { + props: { + status: 'SUCCESS', + successText: 'success', + hasIssues: true, + }, + slots: { + actionButtons: ['Action!'], + }, + }); + }); + + it('should render the passed button', () => { + expect(vm.$el.textContent.trim()).toContain('Action!'); + }); + + it('should still render the expand/collapse button', () => { + expect(vm.$el.querySelector('.js-collapse-btn').textContent.trim()).toEqual('Expand'); + }); + }); }); diff --git a/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb index b2e544e6fed..c51985f00a2 100644 --- a/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb +++ b/spec/lib/gitlab/github_import/importer/pull_requests_importer_spec.rb @@ -158,7 +158,6 @@ describe Gitlab::GithubImport::Importer::PullRequestsImporter do expect(importer.repository_updates_counter) .to receive(:increment) - .with(project: project.path_with_namespace) .and_call_original Timecop.freeze do diff --git a/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb b/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb new file mode 100644 index 00000000000..95bf7685ade --- /dev/null +++ b/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb @@ -0,0 +1,103 @@ +require 'spec_helper' + +describe Gitlab::Graphql::Authorize::AuthorizeResource do + let(:fake_class) do + Class.new do + include Gitlab::Graphql::Authorize::AuthorizeResource + + attr_reader :user, :found_object + + authorize :read_the_thing + + def initialize(user, found_object) + @user, @found_object = user, found_object + end + + def find_object + found_object + end + + def current_user + user + end + end + end + + let(:user) { build(:user) } + let(:project) { build(:project) } + subject(:loading_resource) { fake_class.new(user, project) } + + context 'when the user is allowed to perform the action' do + before do + allow(Ability).to receive(:allowed?).with(user, :read_the_thing, project, scope: :user) do + true + end + end + + describe '#authorized_find' do + it 'returns the object' do + expect(loading_resource.authorized_find).to eq(project) + end + end + + describe '#authorized_find!' do + it 'returns the object' do + expect(loading_resource.authorized_find!).to eq(project) + end + end + + describe '#authorize!' do + it 'does not raise an error' do + expect { loading_resource.authorize!(project) }.not_to raise_error + end + end + + describe '#authorized?' do + it 'is true' do + expect(loading_resource.authorized?(project)).to be(true) + end + end + end + + context 'when the user is not allowed to perform the action' do + before do + allow(Ability).to receive(:allowed?).with(user, :read_the_thing, project, scope: :user) do + false + end + end + + describe '#authorized_find' do + it 'returns `nil`' do + expect(loading_resource.authorized_find).to be_nil + end + end + + describe '#authorized_find!' do + it 'raises an error' do + expect { loading_resource.authorize!(project) }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + end + end + + describe '#authorize!' do + it 'does not raise an error' do + expect { loading_resource.authorize!(project) }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable) + end + end + + describe '#authorized?' do + it 'is false' do + expect(loading_resource.authorized?(project)).to be(false) + end + end + end + + context 'when the class does not define #find_object' do + let(:fake_class) do + Class.new { include Gitlab::Graphql::Authorize::AuthorizeResource } + end + + it 'raises a comprehensive error message' do + expect { fake_class.new.find_object }.to raise_error(/Implement #find_object in #{fake_class.name}/) + end + end +end diff --git a/spec/lib/gitlab/graphql/authorize_spec.rb b/spec/lib/gitlab/graphql/authorize_spec.rb new file mode 100644 index 00000000000..9c17a3b0e4b --- /dev/null +++ b/spec/lib/gitlab/graphql/authorize_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe Gitlab::Graphql::Authorize do + describe '#authorize' do + it 'adds permissions from subclasses to those of superclasses when used on classes' do + base_class = Class.new do + extend Gitlab::Graphql::Authorize + + authorize :base_authorization + end + sub_class = Class.new(base_class) do + authorize :sub_authorization + end + + expect(base_class.required_permissions).to contain_exactly(:base_authorization) + expect(sub_class.required_permissions) + .to contain_exactly(:base_authorization, :sub_authorization) + end + end +end diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index bac5693c830..a88ac0a091e 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -16,7 +16,7 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do @shared = @project.import_export_shared allow(@shared).to receive(:export_path).and_return('spec/lib/gitlab/import_export/') - allow_any_instance_of(Repository).to receive(:fetch_ref).and_return(true) + allow_any_instance_of(Repository).to receive(:fetch_source_branch!).and_return(true) allow_any_instance_of(Gitlab::Git::Repository).to receive(:branch_exists?).and_return(false) expect_any_instance_of(Gitlab::Git::Repository).to receive(:create_branch).with('feature', 'DCBA') diff --git a/spec/lib/gitlab/kubernetes/config_map_spec.rb b/spec/lib/gitlab/kubernetes/config_map_spec.rb index 33dfa461202..e253b291277 100644 --- a/spec/lib/gitlab/kubernetes/config_map_spec.rb +++ b/spec/lib/gitlab/kubernetes/config_map_spec.rb @@ -22,4 +22,10 @@ describe Gitlab::Kubernetes::ConfigMap do is_expected.to eq(resource) end end + + describe '#config_map_name' do + it 'returns the config_map name' do + expect(config_map.config_map_name).to eq("values-content-configuration-#{application.name}") + end + end end diff --git a/spec/lib/gitlab/kubernetes/helm/api_spec.rb b/spec/lib/gitlab/kubernetes/helm/api_spec.rb index aa7e43dfb16..6e9b4ca0869 100644 --- a/spec/lib/gitlab/kubernetes/helm/api_spec.rb +++ b/spec/lib/gitlab/kubernetes/helm/api_spec.rb @@ -49,33 +49,33 @@ describe Gitlab::Kubernetes::Helm::Api do end end - describe '#installation_status' do + describe '#status' do let(:phase) { Gitlab::Kubernetes::Pod::RUNNING } let(:pod) { Kubeclient::Resource.new(status: { phase: phase }) } # partial representation it 'fetches POD phase from kubernetes cluster' do expect(client).to receive(:get_pod).with(command.pod_name, gitlab_namespace).once.and_return(pod) - expect(subject.installation_status(command.pod_name)).to eq(phase) + expect(subject.status(command.pod_name)).to eq(phase) end end - describe '#installation_log' do + describe '#log' do let(:log) { 'some output' } let(:response) { RestClient::Response.new(log) } it 'fetches POD phase from kubernetes cluster' do expect(client).to receive(:get_pod_log).with(command.pod_name, gitlab_namespace).once.and_return(response) - expect(subject.installation_log(command.pod_name)).to eq(log) + expect(subject.log(command.pod_name)).to eq(log) end end - describe '#delete_installation_pod!' do + describe '#delete_pod!' do it 'deletes the POD from kubernetes cluster' do expect(client).to receive(:delete_pod).with(command.pod_name, gitlab_namespace).once - subject.delete_installation_pod!(command.pod_name) + subject.delete_pod!(command.pod_name) end end end diff --git a/spec/migrations/generate_missing_routes_spec.rb b/spec/migrations/generate_missing_routes_spec.rb new file mode 100644 index 00000000000..32515d353b0 --- /dev/null +++ b/spec/migrations/generate_missing_routes_spec.rb @@ -0,0 +1,84 @@ +require 'spec_helper' +require Rails.root.join('db', 'migrate', '20180702134423_generate_missing_routes.rb') + +describe GenerateMissingRoutes, :migration do + describe '#up' do + let(:namespaces) { table(:namespaces) } + let(:projects) { table(:projects) } + let(:routes) { table(:routes) } + + it 'creates routes for projects without a route' do + namespace = namespaces.create!(name: 'GitLab', path: 'gitlab') + + routes.create!( + path: 'gitlab', + source_type: 'Namespace', + source_id: namespace.id + ) + + project = projects.create!( + name: 'GitLab CE', + path: 'gitlab-ce', + namespace_id: namespace.id + ) + + described_class.new.up + + route = routes.where(source_type: 'Project').take + + expect(route.source_id).to eq(project.id) + expect(route.path).to eq("gitlab/gitlab-ce-#{project.id}") + end + + it 'creates routes for namespaces without a route' do + namespace = namespaces.create!(name: 'GitLab', path: 'gitlab') + + described_class.new.up + + route = routes.where(source_type: 'Namespace').take + + expect(route.source_id).to eq(namespace.id) + expect(route.path).to eq("gitlab-#{namespace.id}") + end + + it 'does not create routes for namespaces that already have a route' do + namespace = namespaces.create!(name: 'GitLab', path: 'gitlab') + + routes.create!( + path: 'gitlab', + source_type: 'Namespace', + source_id: namespace.id + ) + + described_class.new.up + + expect(routes.count).to eq(1) + end + + it 'does not create routes for projects that already have a route' do + namespace = namespaces.create!(name: 'GitLab', path: 'gitlab') + + routes.create!( + path: 'gitlab', + source_type: 'Namespace', + source_id: namespace.id + ) + + project = projects.create!( + name: 'GitLab CE', + path: 'gitlab-ce', + namespace_id: namespace.id + ) + + routes.create!( + path: 'gitlab/gitlab-ce', + source_type: 'Project', + source_id: project.id + ) + + described_class.new.up + + expect(routes.count).to eq(2) + end + end +end diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb index efd57040005..e4b61552033 100644 --- a/spec/models/clusters/applications/prometheus_spec.rb +++ b/spec/models/clusters/applications/prometheus_spec.rb @@ -34,6 +34,47 @@ describe Clusters::Applications::Prometheus do end end + describe '#ready' do + let(:project) { create(:project) } + let(:cluster) { create(:cluster, projects: [project]) } + + it 'returns true when installed' do + application = build(:clusters_applications_prometheus, :installed, cluster: cluster) + + expect(application).to be_ready + end + + it 'returns false when not_installable' do + application = build(:clusters_applications_prometheus, :not_installable, cluster: cluster) + + expect(application).not_to be_ready + end + + it 'returns false when installable' do + application = build(:clusters_applications_prometheus, :installable, cluster: cluster) + + expect(application).not_to be_ready + end + + it 'returns false when scheduled' do + application = build(:clusters_applications_prometheus, :scheduled, cluster: cluster) + + expect(application).not_to be_ready + end + + it 'returns false when installing' do + application = build(:clusters_applications_prometheus, :installing, cluster: cluster) + + expect(application).not_to be_ready + end + + it 'returns false when errored' do + application = build(:clusters_applications_prometheus, :errored, cluster: cluster) + + expect(application).not_to be_ready + end + end + describe '#prometheus_client' do context 'cluster is nil' do it 'returns nil' do @@ -102,15 +143,17 @@ describe Clusters::Applications::Prometheus do let(:kubeclient) { double('kubernetes client') } let(:prometheus) { create(:clusters_applications_prometheus) } - subject { prometheus.install_command } - - it { is_expected.to be_an_instance_of(Gitlab::Kubernetes::Helm::InstallCommand) } + it 'returns an instance of Gitlab::Kubernetes::Helm::InstallCommand' do + expect(prometheus.install_command).to be_an_instance_of(Gitlab::Kubernetes::Helm::InstallCommand) + end it 'should be initialized with 3 arguments' do - expect(subject.name).to eq('prometheus') - expect(subject.chart).to eq('stable/prometheus') - expect(subject.version).to eq('6.7.3') - expect(subject.values).to eq(prometheus.values) + command = prometheus.install_command + + expect(command.name).to eq('prometheus') + expect(command.chart).to eq('stable/prometheus') + expect(command.version).to eq('6.7.3') + expect(command.values).to eq(prometheus.values) end end diff --git a/spec/models/concerns/reactive_caching_spec.rb b/spec/models/concerns/reactive_caching_spec.rb index 0f156619e9e..79f75c0ffa0 100644 --- a/spec/models/concerns/reactive_caching_spec.rb +++ b/spec/models/concerns/reactive_caching_spec.rb @@ -125,6 +125,13 @@ describe ReactiveCaching, :use_clean_rails_memory_store_caching do go! end + it "calls a reactive_cache_updated only once if content did not change on subsequent update" do + expect(instance).to receive(:calculate_reactive_cache).twice + expect(instance).to receive(:reactive_cache_updated).once + + 2.times { instance.exclusively_update_reactive_cache! } + end + context 'and #calculate_reactive_cache raises an exception' do before do stub_reactive_cache(instance, "preexisting") diff --git a/spec/models/concerns/routable_spec.rb b/spec/models/concerns/routable_spec.rb index ed3e28fbeca..565266321d3 100644 --- a/spec/models/concerns/routable_spec.rb +++ b/spec/models/concerns/routable_spec.rb @@ -12,16 +12,6 @@ describe Group, 'Routable' do it { is_expected.to have_many(:redirect_routes).dependent(:destroy) } end - describe 'GitLab read-only instance' do - it 'does not save route if route is not present' do - group.route.path = '' - allow(Gitlab::Database).to receive(:read_only?).and_return(true) - expect(group).to receive(:update_route).and_call_original - - expect { group.full_path }.to change { Route.count }.by(0) - end - end - describe 'Callbacks' do it 'creates route record on create' do expect(group.route.path).to eq(group.path) @@ -131,29 +121,6 @@ describe Group, 'Routable' do it { expect(group.full_path).to eq(group.path) } it { expect(nested_group.full_path).to eq("#{group.full_path}/#{nested_group.path}") } - - context 'with RequestStore active', :request_store do - it 'does not load the route table more than once' do - group.expires_full_path_cache - expect(group).to receive(:uncached_full_path).once.and_call_original - - 3.times { group.full_path } - expect(group.full_path).to eq(group.path) - end - end - end - - describe '#expires_full_path_cache' do - context 'with RequestStore active', :request_store do - it 'expires the full_path cache' do - expect(group.full_path).to eq('foo') - - group.route.update(path: 'bar', name: 'bar') - group.expires_full_path_cache - - expect(group.full_path).to eq('bar') - end - end end describe '#full_name' do diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index c1b385aaf76..9b7f932ec3a 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -205,6 +205,34 @@ describe Namespace do expect(gitlab_shell.exists?(project.repository_storage, "#{namespace.path}/#{project.path}.git")).to be_truthy end + context 'when #write_projects_repository_config raises an error' do + context 'in test environment' do + it 'raises an exception' do + expect(namespace).to receive(:write_projects_repository_config).and_raise('foo') + + expect do + namespace.update(path: namespace.full_path + '_new') + end.to raise_error('foo') + end + end + + context 'in production environment' do + it 'does not cancel later callbacks' do + expect(namespace).to receive(:write_projects_repository_config).and_raise('foo') + expect(namespace).to receive(:move_dir).and_wrap_original do |m, *args| + move_dir_result = m.call(*args) + + expect(move_dir_result).to be_truthy # Must be truthy, or else later callbacks would be canceled + + move_dir_result + end + expect(Gitlab::Sentry).to receive(:should_raise?).and_return(false) # like prod + + namespace.update(path: namespace.full_path + '_new') + end + end + end + context 'with subgroups', :nested_groups do let(:parent) { create(:group, name: 'parent', path: 'parent') } let(:new_parent) { create(:group, name: 'new_parent', path: 'new_parent') } @@ -295,6 +323,16 @@ describe Namespace do parent.update(path: 'mygroup_new') + # Routes are loaded when creating the projects, so we need to manually + # reload them for the below code to be aware of the above UPDATE. + [ + project_in_parent_group, + hashed_project_in_subgroup, + legacy_project_in_subgroup + ].each do |project| + project.route.reload + end + expect(project_rugged(project_in_parent_group).config['gitlab.fullpath']).to eq "mygroup_new/#{project_in_parent_group.path}" expect(project_rugged(hashed_project_in_subgroup).config['gitlab.fullpath']).to eq "mygroup_new/mysubgroup/#{hashed_project_in_subgroup.path}" expect(project_rugged(legacy_project_in_subgroup).config['gitlab.fullpath']).to eq "mygroup_new/mysubgroup/#{legacy_project_in_subgroup.path}" diff --git a/spec/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb index cd7f77024da..10617edec0f 100644 --- a/spec/models/project_feature_spec.rb +++ b/spec/models/project_feature_spec.rb @@ -119,4 +119,46 @@ describe ProjectFeature do end end end + + context 'Site Statistics' do + set(:project_with_wiki) { create(:project, :wiki_enabled) } + set(:project_without_wiki) { create(:project, :wiki_disabled) } + + context 'when creating a project' do + it 'tracks wiki availability when wikis are enabled by default' do + expect { create(:project) }.to change { SiteStatistic.fetch.wikis_count }.by(1) + end + + it 'does not track wiki availability when wikis are disabled by default' do + expect { create(:project, :wiki_disabled) }.not_to change { SiteStatistic.fetch.wikis_count } + end + end + + context 'when updating a project_feature' do + it 'untracks wiki availability when disabling wiki access' do + expect { project_with_wiki.project_feature.update_attribute(:wiki_access_level, ProjectFeature::DISABLED) } + .to change { SiteStatistic.fetch.wikis_count }.by(-1) + end + + it 'tracks again wiki availability when re-enabling wiki access as public' do + expect { project_without_wiki.project_feature.update_attribute(:wiki_access_level, ProjectFeature::ENABLED) } + .to change { SiteStatistic.fetch.wikis_count }.by(1) + end + + it 'tracks again wiki availability when re-enabling wiki access as private' do + expect { project_without_wiki.project_feature.update_attribute(:wiki_access_level, ProjectFeature::PRIVATE) } + .to change { SiteStatistic.fetch.wikis_count }.by(1) + end + end + + context 'when removing a project' do + it 'untracks wiki availability when removing a project with previous wiki access' do + expect { project_with_wiki.destroy }.to change { SiteStatistic.fetch.wikis_count }.by(-1) + end + + it 'does not untrack wiki availability when removing a project without wiki access' do + expect { project_without_wiki.destroy }.not_to change { SiteStatistic.fetch.wikis_count } + end + end + end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index b0ec725bf70..b75ca91b007 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -102,6 +102,22 @@ describe Project do end end + context 'Site Statistics' do + context 'when creating a new project' do + it 'tracks project in SiteStatistic' do + expect { create(:project) }.to change { SiteStatistic.fetch.repositories_count }.by(1) + end + end + + context 'when deleting a project' do + it 'untracks project in SiteStatistic' do + project = create(:project) + + expect { project.destroy }.to change { SiteStatistic.fetch.repositories_count }.by(-1) + end + end + end + context 'updating cd_cd_settings' do it 'does not raise an error' do project = create(:project) @@ -2942,8 +2958,6 @@ describe Project do expect(project).to receive(:expire_caches_before_rename) - expect(project).to receive(:expires_full_path_cache) - project.rename_repo end @@ -3103,8 +3117,6 @@ describe Project do expect(project).to receive(:expire_caches_before_rename) - expect(project).to receive(:expires_full_path_cache) - project.rename_repo end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 5d64602ca56..52ec8dbe25a 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -1129,16 +1129,12 @@ describe Repository do end it 'raises Rugged::ReferenceError' do - raise_reference_error = raise_error(Rugged::ReferenceError) do |err| - expect(err.cause).to be_nil - end - expect do Gitlab::Git::OperationService.new(git_user, target_project.repository.raw_repository) .with_branch('feature', start_repository: project.repository.raw_repository, &:itself) - end.to raise_reference_error + end.to raise_error(Gitlab::Git::CommandError) end end diff --git a/spec/models/site_statistic_spec.rb b/spec/models/site_statistic_spec.rb new file mode 100644 index 00000000000..9b056fbf332 --- /dev/null +++ b/spec/models/site_statistic_spec.rb @@ -0,0 +1,83 @@ +require 'spec_helper' + +describe SiteStatistic do + describe '.fetch' do + context 'existing record' do + it 'returns existing SiteStatistic model' do + statistics = create(:site_statistics) + + expect(described_class.fetch).to be_a(described_class) + expect(described_class.fetch).to eq(statistics) + end + end + + context 'non existing record' do + it 'creates a new SiteStatistic model' do + expect(described_class.first).to be_nil + expect(described_class.fetch).to be_a(described_class) + end + end + end + + describe '.track' do + context 'with allowed attributes' do + let(:statistics) { create(:site_statistics) } + + it 'increases the attribute counter' do + expect { described_class.track('repositories_count') }.to change { statistics.reload.repositories_count }.by(1) + expect { described_class.track('wikis_count') }.to change { statistics.reload.wikis_count }.by(1) + end + + it 'doesnt increase the attribute counter when an exception happens during transaction' do + expect do + begin + described_class.transaction do + described_class.track('repositories_count') + + raise StandardError + end + rescue StandardError + # no-op + end + end.not_to change { statistics.reload.repositories_count } + end + end + + context 'with not allowed attributes' do + it 'returns error' do + expect { described_class.track('something_else') }.to raise_error(ArgumentError).with_message(/Invalid attribute: \'something_else\' to \'track\' method/) + end + end + end + + describe '.untrack' do + context 'with allowed attributes' do + let(:statistics) { create(:site_statistics) } + + it 'decreases the attribute counter' do + expect { described_class.untrack('repositories_count') }.to change { statistics.reload.repositories_count }.by(-1) + expect { described_class.untrack('wikis_count') }.to change { statistics.reload.wikis_count }.by(-1) + end + + it 'doesnt decrease the attribute counter when an exception happens during transaction' do + expect do + begin + described_class.transaction do + described_class.track('repositories_count') + + raise StandardError + end + rescue StandardError + # no-op + end + end.not_to change { described_class.fetch.repositories_count } + end + end + + context 'with not allowed attributes' do + it 'returns error' do + expect { described_class.untrack('something_else') }.to raise_error(ArgumentError).with_message(/Invalid attribute: \'something_else\' to \'untrack\' method/) + end + end + end +end diff --git a/spec/policies/concerns/policy_actor_spec.rb b/spec/policies/concerns/policy_actor_spec.rb new file mode 100644 index 00000000000..27db9710a38 --- /dev/null +++ b/spec/policies/concerns/policy_actor_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe PolicyActor do + it 'implements all the methods from user' do + methods = subject.instance_methods + + # User.instance_methods do not return all methods until an instance is + # initialized. So here we just use an instance + expect(build(:user).methods).to include(*methods) + end +end diff --git a/spec/requests/api/graphql/mutations/merge_requests/set_wip_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/set_wip_spec.rb new file mode 100644 index 00000000000..8f427d71a32 --- /dev/null +++ b/spec/requests/api/graphql/mutations/merge_requests/set_wip_spec.rb @@ -0,0 +1,68 @@ +require 'spec_helper' + +describe 'Setting WIP status of a merge request' do + include GraphqlHelpers + + let(:current_user) { create(:user) } + let(:merge_request) { create(:merge_request) } + let(:project) { merge_request.project } + let(:input) { { wip: true } } + + let(:mutation) do + variables = { + project_path: project.full_path, + iid: merge_request.iid + } + graphql_mutation(:merge_request_set_wip, variables.merge(input)) + end + + def mutation_response + graphql_mutation_response(:merge_request_set_wip) + end + + before do + project.add_developer(current_user) + end + + it 'returns an error if the user is not allowed to update the merge request' do + post_graphql_mutation(mutation, current_user: create(:user)) + + expect(graphql_errors).not_to be_empty + end + + it 'marks the merge request as WIP' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response['mergeRequest']['title']).to start_with('WIP:') + end + + it 'does not do anything if the merge request was already marked `WIP`' do + merge_request.update!(title: 'wip: hello world') + + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response['mergeRequest']['title']).to start_with('wip:') + end + + context 'when passing WIP false as input' do + let(:input) { { wip: false } } + + it 'does not do anything if the merge reqeust was not marked wip' do + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response['mergeRequest']['title']).not_to start_with(/wip\:/) + end + + it 'unmarks the merge request as `WIP`' do + merge_request.update!(title: 'wip: hello world') + + post_graphql_mutation(mutation, current_user: current_user) + + expect(response).to have_gitlab_http_status(:success) + expect(mutation_response['mergeRequest']['title']).not_to start_with('/wip\:/') + end + end +end diff --git a/spec/requests/api/members_spec.rb b/spec/requests/api/members_spec.rb index 01bbe7f5ec6..c621760b6c4 100644 --- a/spec/requests/api/members_spec.rb +++ b/spec/requests/api/members_spec.rb @@ -22,10 +22,16 @@ describe API::Members do end end - shared_examples 'GET /:sources/:id/members' do |source_type| - context "with :sources == #{source_type.pluralize}" do + shared_examples 'GET /:source_type/:id/members/(all)' do |source_type, all| + let(:members_url) do + "/#{source_type.pluralize}/#{source.id}/members".tap do |url| + url << "/all" if all + end + end + + context "with :source_type == #{source_type.pluralize}" do it_behaves_like 'a 404 response when source is private' do - let(:route) { get api("/#{source_type.pluralize}/#{source.id}/members", stranger) } + let(:route) { get api(members_url, stranger) } end %i[maintainer developer access_requester stranger].each do |type| @@ -33,7 +39,7 @@ describe API::Members do it 'returns 200' do user = public_send(type) - get api("/#{source_type.pluralize}/#{source.id}/members", user) + get api(members_url, user) expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers @@ -46,23 +52,23 @@ describe API::Members do it 'avoids N+1 queries' do # Establish baseline - get api("/#{source_type.pluralize}/#{source.id}/members", maintainer) + get api(members_url, maintainer) control = ActiveRecord::QueryRecorder.new do - get api("/#{source_type.pluralize}/#{source.id}/members", maintainer) + get api(members_url, maintainer) end project.add_developer(create(:user)) expect do - get api("/#{source_type.pluralize}/#{source.id}/members", maintainer) + get api(members_url, maintainer) end.not_to exceed_query_limit(control) end it 'does not return invitees' do create(:"#{source_type}_member", invite_token: '123', invite_email: 'test@abc.com', source: source, user: nil) - get api("/#{source_type.pluralize}/#{source.id}/members", developer) + get api(members_url, developer) expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers @@ -72,7 +78,7 @@ describe API::Members do end it 'finds members with query string' do - get api("/#{source_type.pluralize}/#{source.id}/members", developer), query: maintainer.username + get api(members_url, developer), query: maintainer.username expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers @@ -82,7 +88,7 @@ describe API::Members do end it 'finds all members with no query specified' do - get api("/#{source_type.pluralize}/#{source.id}/members", developer), query: '' + get api(members_url, developer), query: '' expect(response).to have_gitlab_http_status(200) expect(response).to include_pagination_headers @@ -93,8 +99,51 @@ describe API::Members do end end - shared_examples 'GET /:sources/:id/members/:user_id' do |source_type| - context "with :sources == #{source_type.pluralize}" do + describe 'GET /:source_type/:id/members/all', :nested_groups do + let(:nested_user) { create(:user) } + let(:project_user) { create(:user) } + let(:linked_group_user) { create(:user) } + let!(:project_group_link) { create(:project_group_link, project: project, group: linked_group) } + + let(:project) do + create(:project, :public, group: nested_group) do |project| + project.add_developer(project_user) + end + end + + let(:linked_group) do + create(:group) do |linked_group| + linked_group.add_developer(linked_group_user) + end + end + + let(:nested_group) do + create(:group, parent: group) do |nested_group| + nested_group.add_developer(nested_user) + end + end + + it 'finds all project members including inherited members' do + get api("/projects/#{project.id}/members/all", developer) + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.map { |u| u['id'] }).to match_array [maintainer.id, developer.id, nested_user.id, project_user.id, linked_group_user.id] + end + + it 'finds all group members including inherited members' do + get api("/groups/#{nested_group.id}/members/all", developer) + + expect(response).to have_gitlab_http_status(200) + expect(response).to include_pagination_headers + expect(json_response).to be_an Array + expect(json_response.map { |u| u['id'] }).to match_array [maintainer.id, developer.id, nested_user.id] + end + end + + shared_examples 'GET /:source_type/:id/members/:user_id' do |source_type| + context "with :source_type == #{source_type.pluralize}" do it_behaves_like 'a 404 response when source is private' do let(:route) { get api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", stranger) } end @@ -124,8 +173,8 @@ describe API::Members do end end - shared_examples 'POST /:sources/:id/members' do |source_type| - context "with :sources == #{source_type.pluralize}" do + shared_examples 'POST /:source_type/:id/members' do |source_type| + context "with :source_type == #{source_type.pluralize}" do it_behaves_like 'a 404 response when source is private' do let(:route) do post api("/#{source_type.pluralize}/#{source.id}/members", stranger), @@ -205,8 +254,8 @@ describe API::Members do end end - shared_examples 'PUT /:sources/:id/members/:user_id' do |source_type| - context "with :sources == #{source_type.pluralize}" do + shared_examples 'PUT /:source_type/:id/members/:user_id' do |source_type| + context "with :source_type == #{source_type.pluralize}" do it_behaves_like 'a 404 response when source is private' do let(:route) do put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", stranger), @@ -262,8 +311,8 @@ describe API::Members do end end - shared_examples 'DELETE /:sources/:id/members/:user_id' do |source_type| - context "with :sources == #{source_type.pluralize}" do + shared_examples 'DELETE /:source_type/:id/members/:user_id' do |source_type| + context "with :source_type == #{source_type.pluralize}" do it_behaves_like 'a 404 response when source is private' do let(:route) { delete api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", stranger) } end @@ -323,43 +372,45 @@ describe API::Members do end end - it_behaves_like 'GET /:sources/:id/members', 'project' do - let(:source) { project } - end + [false, true].each do |all| + it_behaves_like 'GET /:source_type/:id/members/(all)', 'project', all do + let(:source) { project } + end - it_behaves_like 'GET /:sources/:id/members', 'group' do - let(:source) { group } + it_behaves_like 'GET /:source_type/:id/members/(all)', 'group', all do + let(:source) { group } + end end - it_behaves_like 'GET /:sources/:id/members/:user_id', 'project' do + it_behaves_like 'GET /:source_type/:id/members/:user_id', 'project' do let(:source) { project } end - it_behaves_like 'GET /:sources/:id/members/:user_id', 'group' do + it_behaves_like 'GET /:source_type/:id/members/:user_id', 'group' do let(:source) { group } end - it_behaves_like 'POST /:sources/:id/members', 'project' do + it_behaves_like 'POST /:source_type/:id/members', 'project' do let(:source) { project } end - it_behaves_like 'POST /:sources/:id/members', 'group' do + it_behaves_like 'POST /:source_type/:id/members', 'group' do let(:source) { group } end - it_behaves_like 'PUT /:sources/:id/members/:user_id', 'project' do + it_behaves_like 'PUT /:source_type/:id/members/:user_id', 'project' do let(:source) { project } end - it_behaves_like 'PUT /:sources/:id/members/:user_id', 'group' do + it_behaves_like 'PUT /:source_type/:id/members/:user_id', 'group' do let(:source) { group } end - it_behaves_like 'DELETE /:sources/:id/members/:user_id', 'project' do + it_behaves_like 'DELETE /:source_type/:id/members/:user_id', 'project' do let(:source) { project } end - it_behaves_like 'DELETE /:sources/:id/members/:user_id', 'group' do + it_behaves_like 'DELETE /:source_type/:id/members/:user_id', 'group' do let(:source) { group } end diff --git a/spec/routing/admin_routing_spec.rb b/spec/routing/admin_routing_spec.rb index 179fc9733ad..98df5f787f7 100644 --- a/spec/routing/admin_routing_spec.rb +++ b/spec/routing/admin_routing_spec.rb @@ -79,7 +79,7 @@ end # edit_admin_hook GET /admin/hooks/:id(.:format) admin/hooks#edit describe Admin::HooksController, "routing" do it "to #test" do - expect(get("/admin/hooks/1/test")).to route_to('admin/hooks#test', id: '1') + expect(post("/admin/hooks/1/test")).to route_to('admin/hooks#test', id: '1') end it "to #index" do diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index 56d93095a85..70a7707826e 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -389,7 +389,7 @@ describe 'project routing' do # DELETE /:project_id/hooks/:id(.:format) hooks#destroy describe Projects::HooksController, 'routing' do it 'to #test' do - expect(get('/gitlab/gitlabhq/hooks/1/test')).to route_to('projects/hooks#test', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') + expect(post('/gitlab/gitlabhq/hooks/1/test')).to route_to('projects/hooks#test', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') end it_behaves_like 'RESTful project resources' do diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb index eb4235e3ee6..cf57776346a 100644 --- a/spec/serializers/pipeline_serializer_spec.rb +++ b/spec/serializers/pipeline_serializer_spec.rb @@ -125,7 +125,7 @@ describe PipelineSerializer do it 'verifies number of queries', :request_store do recorded = ActiveRecord::QueryRecorder.new { subject } - expect(recorded.count).to be_within(2).of(27) + expect(recorded.count).to be_within(2).of(31) expect(recorded.cached_count).to eq(0) end end @@ -144,7 +144,7 @@ describe PipelineSerializer do # pipeline. With the same ref this check is cached but if refs are # different then there is an extra query per ref # https://gitlab.com/gitlab-org/gitlab-ce/issues/46368 - expect(recorded.count).to be_within(2).of(30) + expect(recorded.count).to be_within(2).of(34) expect(recorded.cached_count).to eq(0) end end diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb index 7e85f599afb..1a85c52fc97 100644 --- a/spec/services/projects/transfer_service_spec.rb +++ b/spec/services/projects/transfer_service_spec.rb @@ -31,12 +31,6 @@ describe Projects::TransferService do transfer_project(project, user, group) end - it 'expires full_path cache' do - expect(project).to receive(:expires_full_path_cache) - - transfer_project(project, user, group) - end - it 'invalidates the user\'s personal_project_count cache' do expect(user).to receive(:invalidate_personal_projects_count) diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb index b9322975b5a..75827df80dc 100644 --- a/spec/support/helpers/graphql_helpers.rb +++ b/spec/support/helpers/graphql_helpers.rb @@ -1,4 +1,6 @@ module GraphqlHelpers + MutationDefinition = Struct.new(:query, :variables) + # makes an underscored string look like a fieldname # "merge_request" => "mergeRequest" def self.fieldnamerize(underscored_field_name) @@ -41,6 +43,37 @@ module GraphqlHelpers QUERY end + def graphql_mutation(name, input, fields = nil) + mutation_name = GraphqlHelpers.fieldnamerize(name) + input_variable_name = "$#{input_variable_name_for_mutation(name)}" + mutation_field = GitlabSchema.mutation.fields[mutation_name] + fields ||= all_graphql_fields_for(mutation_field.type) + + query = <<~MUTATION + mutation(#{input_variable_name}: #{mutation_field.arguments['input'].type}) { + #{mutation_name}(input: #{input_variable_name}) { + #{fields} + } + } + MUTATION + variables = variables_for_mutation(name, input) + + MutationDefinition.new(query, variables) + end + + def variables_for_mutation(name, input) + graphql_input = input.map { |name, value| [GraphqlHelpers.fieldnamerize(name), value] }.to_h + { input_variable_name_for_mutation(name) => graphql_input }.to_json + end + + def input_variable_name_for_mutation(mutation_name) + mutation_name = GraphqlHelpers.fieldnamerize(mutation_name) + mutation_field = GitlabSchema.mutation.fields[mutation_name] + input_type = field_type(mutation_field.arguments['input']) + + GraphqlHelpers.fieldnamerize(input_type) + end + def query_graphql_field(name, attributes = {}, fields = nil) fields ||= all_graphql_fields_for(name.classify) attributes = attributes_to_graphql(attributes) @@ -73,8 +106,12 @@ module GraphqlHelpers end.join(", ") end - def post_graphql(query, current_user: nil) - post api('/', current_user, version: 'graphql'), query: query + def post_graphql(query, current_user: nil, variables: nil) + post api('/', current_user, version: 'graphql'), query: query, variables: variables + end + + def post_graphql_mutation(mutation, current_user: nil) + post_graphql(mutation.query, current_user: current_user, variables: mutation.variables) end def graphql_data @@ -82,7 +119,11 @@ module GraphqlHelpers end def graphql_errors - json_response['data'] + json_response['errors'] + end + + def graphql_mutation_response(mutation_name) + graphql_data[GraphqlHelpers.fieldnamerize(mutation_name)] end def nested_fields?(field) @@ -102,10 +143,14 @@ module GraphqlHelpers end def field_type(field) - if field.type.respond_to?(:of_type) - field.type.of_type - else - field.type - end + field_type = field.type + + # The type could be nested. For example `[GraphQL::STRING_TYPE]`: + # - List + # - String! + # - String + field_type = field_type.of_type while field_type.respond_to?(:of_type) + + field_type end end diff --git a/spec/support/matchers/graphql_matchers.rb b/spec/support/matchers/graphql_matchers.rb index be6fa4c71a0..7be84838e00 100644 --- a/spec/support/matchers/graphql_matchers.rb +++ b/spec/support/matchers/graphql_matchers.rb @@ -34,6 +34,15 @@ RSpec::Matchers.define :have_graphql_field do |field_name| end end +RSpec::Matchers.define :have_graphql_mutation do |mutation_class| + match do |mutation_type| + field = mutation_type.fields[GraphqlHelpers.fieldnamerize(mutation_class.graphql_name)] + + expect(field).to be_present + expect(field.resolver).to eq(mutation_class) + end +end + RSpec::Matchers.define :have_graphql_arguments do |*expected| include GraphqlHelpers diff --git a/spec/support/prometheus/additional_metrics_shared_examples.rb b/spec/support/prometheus/additional_metrics_shared_examples.rb index c7c3346d39e..0fd67531c3b 100644 --- a/spec/support/prometheus/additional_metrics_shared_examples.rb +++ b/spec/support/prometheus/additional_metrics_shared_examples.rb @@ -25,7 +25,7 @@ RSpec.shared_examples 'additional metrics query' do shared_examples 'query context containing environment slug and filter' do it 'contains ci_environment_slug' do - expect(subject).to receive(:query_metrics).with(project, hash_including(ci_environment_slug: environment.slug)) + expect(subject).to receive(:query_metrics).with(project, environment, hash_including(ci_environment_slug: environment.slug)) subject.query(*query_params) end @@ -33,6 +33,7 @@ RSpec.shared_examples 'additional metrics query' do it 'contains environment filter' do expect(subject).to receive(:query_metrics).with( project, + environment, hash_including( environment_filter: "container_name!=\"POD\",environment=\"#{environment.slug}\"" ) @@ -50,7 +51,7 @@ RSpec.shared_examples 'additional metrics query' do it_behaves_like 'query context containing environment slug and filter' it 'query context contains kube_namespace' do - expect(subject).to receive(:query_metrics).with(project, hash_including(kube_namespace: kube_namespace)) + expect(subject).to receive(:query_metrics).with(project, environment, hash_including(kube_namespace: kube_namespace)) subject.query(*query_params) end @@ -74,7 +75,7 @@ RSpec.shared_examples 'additional metrics query' do it_behaves_like 'query context containing environment slug and filter' it 'query context contains empty kube_namespace' do - expect(subject).to receive(:query_metrics).with(project, hash_including(kube_namespace: '')) + expect(subject).to receive(:query_metrics).with(project, environment, hash_including(kube_namespace: '')) subject.query(*query_params) end diff --git a/spec/support/shared_examples/notify_shared_examples.rb b/spec/support/shared_examples/notify_shared_examples.rb index d176d3fa425..5beae005cf7 100644 --- a/spec/support/shared_examples/notify_shared_examples.rb +++ b/spec/support/shared_examples/notify_shared_examples.rb @@ -77,7 +77,7 @@ shared_examples 'a thread answer email with reply-by-email enabled' do aggregate_failures do is_expected.to have_header('Message-ID', /\A<.*@#{host}>\Z/) is_expected.to have_header('In-Reply-To', "<#{route_key}@#{host}>") - is_expected.to have_header('References', /\A<#{route_key}@#{host}> <reply\-.*@#{host}>\Z/ ) + is_expected.to have_header('References', /\A<reply\-.*@#{host}> <#{route_key}@#{host}>\Z/ ) is_expected.to have_subject(/^Re: /) end end diff --git a/spec/support/shared_examples/requests/graphql_shared_examples.rb b/spec/support/shared_examples/requests/graphql_shared_examples.rb index fe7b7bc306f..04140cad3f0 100644 --- a/spec/support/shared_examples/requests/graphql_shared_examples.rb +++ b/spec/support/shared_examples/requests/graphql_shared_examples.rb @@ -5,7 +5,7 @@ shared_examples 'a working graphql query' do it 'returns a successful response', :aggregate_failures do expect(response).to have_gitlab_http_status(:success) - expect(graphql_errors['errors']).to be_nil + expect(graphql_errors).to be_nil expect(json_response.keys).to include('data') end end diff --git a/spec/views/layouts/_head.html.haml_spec.rb b/spec/views/layouts/_head.html.haml_spec.rb index e8e6d2e7a75..9d1efcabb80 100644 --- a/spec/views/layouts/_head.html.haml_spec.rb +++ b/spec/views/layouts/_head.html.haml_spec.rb @@ -29,6 +29,39 @@ describe 'layouts/_head' do expect(rendered).to match(%{content="foo" http-equiv="refresh"}) end + context 'when an asset_host is set and feature is activated in the config it will' do + let(:asset_host) { 'http://assets' } + + before do + stub_feature_flags(asset_host_prefetch: true) + allow(ActionController::Base).to receive(:asset_host).and_return(asset_host) + end + + it 'add a link dns-prefetch tag' do + render + expect(rendered).to match('<link href="http://assets" rel="dns-prefetch">') + end + + it 'add a link preconnect tag' do + render + expect(rendered).to match('<link crossorigin="" href="http://assets" rel="preconnnect">') + end + end + + context 'when an asset_host is set and feature is not activated in the config it will' do + let(:asset_host) { 'http://assets' } + + before do + stub_feature_flags(asset_host_prefetch: false) + allow(ActionController::Base).to receive(:asset_host).and_return(asset_host) + end + + it 'not add a link dns-prefetch tag' do + render + expect(rendered).not_to match('<link href="http://assets" rel="dns-prefetch">') + end + end + def stub_helper_with_safe_string(method) allow_any_instance_of(PageLayoutHelper).to receive(method) .and_return(%q{foo" http-equiv="refresh}.html_safe) diff --git a/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb b/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb index 615462380e0..9c187bead0a 100644 --- a/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb +++ b/spec/workers/concerns/gitlab/github_import/object_importer_spec.rb @@ -51,7 +51,6 @@ describe Gitlab::GithubImport::ObjectImporter do expect(worker.counter) .to receive(:increment) - .with(project: 'foo/bar') .and_call_original worker.import(project, client, { 'number' => 10 }) diff --git a/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb b/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb index 48e7eaf32fc..5b1c6b6010a 100644 --- a/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb +++ b/spec/workers/gitlab/github_import/import_diff_note_worker_spec.rb @@ -33,7 +33,6 @@ describe Gitlab::GithubImport::ImportDiffNoteWorker do expect(worker.counter) .to receive(:increment) - .with(project: 'foo/bar') .and_call_original worker.import(project, client, hash) diff --git a/spec/workers/gitlab/github_import/import_issue_worker_spec.rb b/spec/workers/gitlab/github_import/import_issue_worker_spec.rb index 8cf6ac15919..ab070d6d081 100644 --- a/spec/workers/gitlab/github_import/import_issue_worker_spec.rb +++ b/spec/workers/gitlab/github_import/import_issue_worker_spec.rb @@ -36,7 +36,6 @@ describe Gitlab::GithubImport::ImportIssueWorker do expect(worker.counter) .to receive(:increment) - .with(project: 'foo/bar') .and_call_original worker.import(project, client, hash) diff --git a/spec/workers/gitlab/github_import/import_note_worker_spec.rb b/spec/workers/gitlab/github_import/import_note_worker_spec.rb index 677697c02df..3a30f06bb2d 100644 --- a/spec/workers/gitlab/github_import/import_note_worker_spec.rb +++ b/spec/workers/gitlab/github_import/import_note_worker_spec.rb @@ -31,7 +31,6 @@ describe Gitlab::GithubImport::ImportNoteWorker do expect(worker.counter) .to receive(:increment) - .with(project: 'foo/bar') .and_call_original worker.import(project, client, hash) diff --git a/spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb b/spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb index e287ddbe0d7..3cccd7cab21 100644 --- a/spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb +++ b/spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb @@ -42,7 +42,6 @@ describe Gitlab::GithubImport::ImportPullRequestWorker do expect(worker.counter) .to receive(:increment) - .with(project: 'foo/bar') .and_call_original worker.import(project, client, hash) diff --git a/yarn.lock b/yarn.lock index 85fdb150d34..21e62a6d36a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -78,9 +78,17 @@ lodash "^4.2.0" to-fast-properties "^2.0.0" -"@gitlab-org/gitlab-svgs@^1.26.0": - version "1.26.0" - resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.26.0.tgz#d89c633e866d031a9e4787b05eacc7144c3a7791" +"@gitlab-org/gitlab-svgs@^1.23.0", "@gitlab-org/gitlab-svgs@^1.27.0": + version "1.27.0" + resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.27.0.tgz#638e70399ebd59e503732177316bb9a18bf7a13f" + +"@gitlab-org/gitlab-ui@1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-ui/-/gitlab-ui-1.0.5.tgz#a64b402650494115c8b494a44b72c2d6fbf33fff" + dependencies: + "@gitlab-org/gitlab-svgs" "^1.23.0" + bootstrap-vue "^2.0.0-rc.11" + vue "^2.5.16" "@sindresorhus/is@^0.7.0": version "0.7.0" @@ -346,6 +354,10 @@ ansi-align@^2.0.0: dependencies: string-width "^2.0.0" +ansi-escapes@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" + ansi-escapes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92" @@ -1045,6 +1057,14 @@ babel-plugin-transform-strict-mode@^6.24.1: babel-runtime "^6.22.0" babel-types "^6.24.1" +babel-polyfill@6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.23.0.tgz#8364ca62df8eafb830499f699177466c3b03499d" + dependencies: + babel-runtime "^6.22.0" + core-js "^2.4.0" + regenerator-runtime "^0.10.0" + babel-preset-es2015@^6.24.1: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz#d44050d6bc2c9feea702aaf38d727a0210538939" @@ -1317,6 +1337,21 @@ boom@5.x.x: dependencies: hoek "4.x.x" +bootstrap-vue@^2.0.0-rc.11: + version "2.0.0-rc.11" + resolved "https://registry.yarnpkg.com/bootstrap-vue/-/bootstrap-vue-2.0.0-rc.11.tgz#47aaa6d2a8d390477de75e636d8ea652b1d03f59" + dependencies: + bootstrap "^4.1.1" + lodash.get "^4.4.2" + lodash.startcase "^4.4.0" + opencollective "^1.0.3" + popper.js "^1.12.9" + vue-functional-data-merge "^2.0.5" + +bootstrap@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.1.2.tgz#aee2a93472e61c471fc79fb475531dcbc87de326" + bootstrap@~4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.1.1.tgz#3aec85000fa619085da8d2e4983dfd67cf2114cb" @@ -1576,7 +1611,7 @@ center-align@^0.1.1: align-text "^0.1.3" lazy-cache "^1.0.3" -chalk@^1.1.1, chalk@^1.1.3: +chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" dependencies: @@ -1774,7 +1809,7 @@ combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -commander@^2.13.0, commander@^2.15.1, commander@^2.9.0: +commander@2, commander@^2.13.0, commander@^2.15.1, commander@^2.9.0: version "2.15.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" @@ -2070,15 +2105,15 @@ cyclist@~0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" -d3-array@^1.2.0, d3-array@^1.2.1: +d3-array@1, d3-array@1.2.1, d3-array@^1.2.0, d3-array@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.1.tgz#d1ca33de2f6ac31efadb8e050a021d7e2396d5dc" -d3-axis@^1.0.8: +d3-axis@1.0.8, d3-axis@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/d3-axis/-/d3-axis-1.0.8.tgz#31a705a0b535e65759de14173a31933137f18efa" -d3-brush@^1.0.4: +d3-brush@1.0.4, d3-brush@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-1.0.4.tgz#00c2f238019f24f6c0a194a26d41a1530ffe7bc4" dependencies: @@ -2088,44 +2123,103 @@ d3-brush@^1.0.4: d3-selection "1" d3-transition "1" -d3-collection@1: +d3-chord@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-1.0.4.tgz#7dec4f0ba886f713fe111c45f763414f6f74ca2c" + dependencies: + d3-array "1" + d3-path "1" + +d3-collection@1, d3-collection@1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.4.tgz#342dfd12837c90974f33f1cc0a785aea570dcdc2" -d3-color@1: +d3-color@1, d3-color@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.0.3.tgz#bc7643fca8e53a8347e2fbdaffa236796b58509b" -d3-dispatch@1: +d3-dispatch@1, d3-dispatch@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.3.tgz#46e1491eaa9b58c358fce5be4e8bed626e7871f8" -d3-drag@1: +d3-drag@1, d3-drag@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-1.2.1.tgz#df8dd4c502fb490fc7462046a8ad98a5c479282d" dependencies: d3-dispatch "1" d3-selection "1" -d3-ease@1: +d3-dsv@1, d3-dsv@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-1.0.8.tgz#907e240d57b386618dc56468bacfe76bf19764ae" + dependencies: + commander "2" + iconv-lite "0.4" + rw "1" + +d3-ease@1, d3-ease@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.3.tgz#68bfbc349338a380c44d8acc4fbc3304aa2d8c0e" -d3-format@1: +d3-force@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/d3-force/-/d3-force-1.1.0.tgz#cebf3c694f1078fcc3d4daf8e567b2fbd70d4ea3" + dependencies: + d3-collection "1" + d3-dispatch "1" + d3-quadtree "1" + d3-timer "1" + +d3-format@1, d3-format@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.2.1.tgz#4e19ecdb081a341dafaf5f555ee956bcfdbf167f" -d3-interpolate@1: +d3-geo@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.9.1.tgz#157e3b0f917379d0f73bebfff3be537f49fa7356" + dependencies: + d3-array "1" + +d3-hierarchy@1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-1.1.5.tgz#a1c845c42f84a206bcf1c01c01098ea4ddaa7a26" + +d3-interpolate@1, d3-interpolate@1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.1.6.tgz#2cf395ae2381804df08aa1bf766b7f97b5f68fb6" dependencies: d3-color "1" -d3-path@1: +d3-path@1, d3-path@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.5.tgz#241eb1849bd9e9e8021c0d0a799f8a0e8e441764" -d3-scale@^1.0.7: +d3-polygon@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-1.0.3.tgz#16888e9026460933f2b179652ad378224d382c62" + +d3-quadtree@1, d3-quadtree@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-1.0.3.tgz#ac7987e3e23fe805a990f28e1b50d38fcb822438" + +d3-queue@3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/d3-queue/-/d3-queue-3.0.7.tgz#c93a2e54b417c0959129d7d73f6cf7d4292e7618" + +d3-random@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-1.1.0.tgz#6642e506c6fa3a648595d2b2469788a8d12529d3" + +d3-request@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/d3-request/-/d3-request-1.0.6.tgz#a1044a9ef4ec28c824171c9379fae6d79474b19f" + dependencies: + d3-collection "1" + d3-dispatch "1" + d3-dsv "1" + xmlhttprequest "1" + +d3-scale@1.0.7, d3-scale@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-1.0.7.tgz#fa90324b3ea8a776422bd0472afab0b252a0945d" dependencies: @@ -2137,31 +2231,31 @@ d3-scale@^1.0.7: d3-time "1" d3-time-format "2" -d3-selection@1, d3-selection@^1.1.0, d3-selection@^1.2.0: +d3-selection@1, d3-selection@1.2.0, d3-selection@^1.1.0, d3-selection@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.2.0.tgz#1b8ec1c7cedadfb691f2ba20a4a3cfbeb71bbc88" -d3-shape@^1.2.0: +d3-shape@1.2.0, d3-shape@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.2.0.tgz#45d01538f064bafd05ea3d6d2cb748fd8c41f777" dependencies: d3-path "1" -d3-time-format@2, d3-time-format@^2.1.1: +d3-time-format@2, d3-time-format@2.1.1, d3-time-format@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.1.1.tgz#85b7cdfbc9ffca187f14d3c456ffda268081bb31" dependencies: d3-time "1" -d3-time@1, d3-time@^1.0.8: +d3-time@1, d3-time@1.0.8, d3-time@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.0.8.tgz#dbd2d6007bf416fe67a76d17947b784bffea1e84" -d3-timer@1: +d3-timer@1, d3-timer@1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.7.tgz#df9650ca587f6c96607ff4e60cc38229e8dd8531" -d3-transition@1: +d3-transition@1, d3-transition@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-1.1.1.tgz#d8ef89c3b848735b060e54a39b32aaebaa421039" dependencies: @@ -2172,10 +2266,59 @@ d3-transition@1: d3-selection "^1.1.0" d3-timer "1" +d3-voronoi@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/d3-voronoi/-/d3-voronoi-1.1.2.tgz#1687667e8f13a2d158c80c1480c5a29cb0d8973c" + +d3-zoom@1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-1.7.1.tgz#02f43b3c3e2db54f364582d7e4a236ccc5506b63" + dependencies: + d3-dispatch "1" + d3-drag "1" + d3-interpolate "1" + d3-selection "1" + d3-transition "1" + d3@3.5.17: version "3.5.17" resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8" +d3@4.12.2: + version "4.12.2" + resolved "https://registry.yarnpkg.com/d3/-/d3-4.12.2.tgz#12f775564c6a9de229f63db03446e2cb7bb56c8f" + dependencies: + d3-array "1.2.1" + d3-axis "1.0.8" + d3-brush "1.0.4" + d3-chord "1.0.4" + d3-collection "1.0.4" + d3-color "1.0.3" + d3-dispatch "1.0.3" + d3-drag "1.2.1" + d3-dsv "1.0.8" + d3-ease "1.0.3" + d3-force "1.1.0" + d3-format "1.2.1" + d3-geo "1.9.1" + d3-hierarchy "1.1.5" + d3-interpolate "1.1.6" + d3-path "1.0.5" + d3-polygon "1.0.3" + d3-quadtree "1.0.3" + d3-queue "3.0.7" + d3-random "1.1.0" + d3-request "1.0.6" + d3-scale "1.0.7" + d3-selection "1.2.0" + d3-shape "1.2.0" + d3-time "1.0.8" + d3-time-format "2.1.1" + d3-timer "1.0.7" + d3-transition "1.1.1" + d3-voronoi "1.1.2" + d3-zoom "1.7.1" + dagre-d3-renderer@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/dagre-d3-renderer/-/dagre-d3-renderer-0.4.24.tgz#b36ce2fe4ea20de43e7698627c6ede2a9f15ec45" @@ -2534,6 +2677,12 @@ encodeurl@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" +encoding@^0.1.11: + version "0.1.12" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" + dependencies: + iconv-lite "~0.4.13" + end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.1" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" @@ -3009,7 +3158,7 @@ extend@3, extend@^3.0.0, extend@~3.0.0, extend@~3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" -external-editor@^2.0.4: +external-editor@^2.0.1, external-editor@^2.0.4: version "2.2.0" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" dependencies: @@ -3771,6 +3920,12 @@ https-proxy-agent@^2.2.1: agent-base "^4.1.0" debug "^3.1.0" +iconv-lite@0.4: + version "0.4.23" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" + dependencies: + safer-buffer ">= 2.1.2 < 3" + iconv-lite@0.4.15: version "0.4.15" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb" @@ -3779,7 +3934,7 @@ iconv-lite@0.4.19, iconv-lite@^0.4.17: version "0.4.19" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" -iconv-lite@0.4.23, iconv-lite@^0.4.22, iconv-lite@^0.4.4: +iconv-lite@0.4.23, iconv-lite@^0.4.22, iconv-lite@^0.4.4, iconv-lite@~0.4.13: version "0.4.23" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" dependencies: @@ -3884,6 +4039,24 @@ ini@^1.3.4, ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" +inquirer@3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.0.6.tgz#e04aaa9d05b7a3cb9b0f407d04375f0447190347" + dependencies: + ansi-escapes "^1.1.0" + chalk "^1.0.0" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^2.0.1" + figures "^2.0.0" + lodash "^4.3.0" + mute-stream "0.0.7" + run-async "^2.2.0" + rx "^4.1.0" + string-width "^2.0.0" + strip-ansi "^3.0.0" + through "^2.3.6" + inquirer@^3.0.6: version "3.3.0" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" @@ -4173,7 +4346,7 @@ is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" -is-stream@^1.0.0, is-stream@^1.1.0: +is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -4662,6 +4835,10 @@ lodash.escaperegexp@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + lodash.kebabcase@4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" @@ -4674,6 +4851,10 @@ lodash.snakecase@4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" +lodash.startcase@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.startcase/-/lodash.startcase-4.4.0.tgz#9436e34ed26093ed7ffae1936144350915d9add8" + lodash.upperfirst@4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce" @@ -4954,7 +5135,7 @@ minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" -minimist@^1.1.3, minimist@^1.2.0: +minimist@1.2.0, minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" @@ -5098,6 +5279,13 @@ nice-try@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4" +node-fetch@1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04" + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + node-forge@0.6.33: version "0.6.33" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.6.33.tgz#463811879f573d45155ad6a9f43dc296e8e85ebc" @@ -5351,10 +5539,28 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" +opencollective@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/opencollective/-/opencollective-1.0.3.tgz#aee6372bc28144583690c3ca8daecfc120dd0ef1" + dependencies: + babel-polyfill "6.23.0" + chalk "1.1.3" + inquirer "3.0.6" + minimist "1.2.0" + node-fetch "1.6.3" + opn "4.0.2" + opener@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8" +opn@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/opn/-/opn-4.0.2.tgz#7abc22e644dff63b0a96d5ab7f2790c0f01abc95" + dependencies: + object-assign "^4.0.1" + pinkie-promise "^2.0.0" + opn@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/opn/-/opn-5.2.0.tgz#71fdf934d6827d676cecbea1531f95d354641225" @@ -5648,7 +5854,7 @@ pluralize@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" -popper.js@^1.14.3: +popper.js@^1.12.9, popper.js@^1.14.3: version "1.14.3" resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.14.3.tgz#1438f98d046acf7b4d78cd502bf418ac64d4f095" @@ -6056,6 +6262,10 @@ regenerate@^1.2.1: version "1.3.2" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260" +regenerator-runtime@^0.10.0: + version "0.10.5" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" + regenerator-runtime@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1" @@ -6293,6 +6503,10 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" +rw@1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" + rx-lite-aggregates@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" @@ -6303,6 +6517,10 @@ rx-lite@*, rx-lite@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" +rx@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" + rxjs@^6.1.0: version "6.2.1" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.2.1.tgz#246cebec189a6cbc143a3ef9f62d6f4c91813ca1" @@ -7457,6 +7675,10 @@ vue-eslint-parser@^2.0.3: esquery "^1.0.0" lodash "^4.17.4" +vue-functional-data-merge@^2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/vue-functional-data-merge/-/vue-functional-data-merge-2.0.6.tgz#f08055adfb92458debcf2ad10c3aa712277f7fc2" + vue-hot-reload-api@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.0.tgz#97976142405d13d8efae154749e88c4e358cf926" @@ -7772,6 +7994,10 @@ xmlhttprequest-ssl@~1.5.4: version "1.5.5" resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" +xmlhttprequest@1: + version "1.8.0" + resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" + xregexp@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" |