diff options
137 files changed, 1202 insertions, 454 deletions
diff --git a/.eslintrc.yml b/.eslintrc.yml index 8d34efc9a6b..0639228fe51 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -1,6 +1,6 @@ extends: - - '@gitlab' - - plugin:promise/recommended + - plugin:@gitlab/default + - plugin:@gitlab/i18n - plugin:no-jquery/slim - plugin:no-jquery/deprecated-3.4 globals: @@ -16,9 +16,6 @@ settings: webpack: config: './config/webpack.config.js' rules: - "@gitlab/i18n/no-non-i18n-strings": error - "@gitlab/vue-i18n/no-bare-strings": error - "@gitlab/vue-i18n/no-bare-attribute-strings": error import/no-commonjs: error no-underscore-dangle: - error @@ -54,4 +51,4 @@ overrides: - files: - '**/spec/**/*' rules: - "@gitlab/i18n/no-non-i18n-strings": off + "@gitlab/require-i18n-strings": off diff --git a/.gitlab/merge_request_templates/New static analysis check.md b/.gitlab/merge_request_templates/New static analysis check.md index b89b6f7dbc4..8bbb3effb1c 100644 --- a/.gitlab/merge_request_templates/New static analysis check.md +++ b/.gitlab/merge_request_templates/New static analysis check.md @@ -8,7 +8,6 @@ Please describe the proposal and add a link to the source (for example, http://w - [ ] Make sure this MR enables a static analysis check rule for new usage but ignores current offenses -- [ ] Create a follow-up issue to fix the current offenses as a separate iteration: ISSUE_LINK - [ ] Mention this proposal in the relevant Slack channels (e.g. `#development`, `#backend`, `#frontend`) - [ ] If there is a choice to make between two potential styles, set up an emoji vote in the MR: - CHOICE_A: :a: @@ -17,6 +16,7 @@ Please describe the proposal and add a link to the source (for example, http://w - [ ] The MR doesn't have significant objections, and is getting a majority of :+1: vs :-1: (remember that [we don't need to reach a consensus](https://about.gitlab.com/handbook/values/#collaboration-is-not-consensus)) - [ ] (If applicable) One style is getting a majority of vote (compared to the other choice) - [ ] (If applicable) Update the MR with the chosen style +- [ ] Create a follow-up issue to fix the current offenses as a separate iteration: ISSUE_LINK - [ ] Follow the [review process](https://docs.gitlab.com/ee/development/code_review.html) as usual - [ ] Once approved and merged by a maintainer, mention it again: - [ ] In the relevant Slack channels (e.g. `#development`, `#backend`, `#frontend`) diff --git a/.overcommit.yml.example b/.overcommit.yml.example index b957a8bbd2a..4e6d084a95d 100644 --- a/.overcommit.yml.example +++ b/.overcommit.yml.example @@ -15,8 +15,8 @@ # # Uncomment the following lines to make the configuration take effect. -# Make sure to run `cd tooling/overcommit && make && cd -` -gemfile: 'tooling/overcommit/gems.rb' +# Make sure to run `make -C tooling/overcommit` +gemfile: 'tooling/overcommit/Gemfile' PostCheckout: BundleInstall: @@ -475,7 +475,7 @@ gem 'lograge', '~> 0.5' gem 'grape_logging', '~> 1.7' # DNS Lookup -gem 'gitlab-net-dns', '~> 0.9.1' +gem 'gitlab-net-dns', '~> 0.9.1', require: 'net/dns' # Countries list gem 'countries', '~> 3.0' diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js index 0e403d023df..67164997bd8 100644 --- a/app/assets/javascripts/awards_handler.js +++ b/app/assets/javascripts/awards_handler.js @@ -1,4 +1,4 @@ -/* eslint-disable class-methods-use-this, @gitlab/i18n/no-non-i18n-strings */ +/* eslint-disable class-methods-use-this, @gitlab/require-i18n-strings */ import $ from 'jquery'; import _ from 'underscore'; diff --git a/app/assets/javascripts/badges/components/badge.vue b/app/assets/javascripts/badges/components/badge.vue index 00c0334db77..f9dd153eba0 100644 --- a/app/assets/javascripts/badges/components/badge.vue +++ b/app/assets/javascripts/badges/components/badge.vue @@ -4,7 +4,7 @@ import Icon from '~/vue_shared/components/icon.vue'; export default { // name: 'Badge' is a false positive: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/25 - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings name: 'Badge', components: { Icon, diff --git a/app/assets/javascripts/behaviors/markdown/nodes/playable.js b/app/assets/javascripts/behaviors/markdown/nodes/playable.js index 9209c69d04a..9a2c9c3c9b0 100644 --- a/app/assets/javascripts/behaviors/markdown/nodes/playable.js +++ b/app/assets/javascripts/behaviors/markdown/nodes/playable.js @@ -1,5 +1,5 @@ /* eslint-disable class-methods-use-this */ -/* eslint-disable @gitlab/i18n/no-non-i18n-strings */ +/* eslint-disable @gitlab/require-i18n-strings */ import { Node } from 'tiptap'; import { defaultMarkdownSerializer } from 'prosemirror-markdown'; diff --git a/app/assets/javascripts/boards/components/board.js b/app/assets/javascripts/boards/components/board.js index 67046715e9b..b68a6ad0ef5 100644 --- a/app/assets/javascripts/boards/components/board.js +++ b/app/assets/javascripts/boards/components/board.js @@ -36,6 +36,7 @@ export default Vue.extend({ list: { type: Object, default: () => ({}), + required: false, }, disabled: { type: Boolean, @@ -94,7 +95,7 @@ export default Vue.extend({ return this.list.type !== ListType.blank && this.list.type !== ListType.promotion; }, uniqueKey() { - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings return `boards.${this.boardId}.${this.list.type}.${this.list.id}`; }, helpLink() { diff --git a/app/assets/javascripts/boards/components/board_card.vue b/app/assets/javascripts/boards/components/board_card.vue index 0fc60528eb6..5735c8ded3d 100644 --- a/app/assets/javascripts/boards/components/board_card.vue +++ b/app/assets/javascripts/boards/components/board_card.vue @@ -13,29 +13,36 @@ export default { list: { type: Object, default: () => ({}), + required: false, }, issue: { type: Object, default: () => ({}), + required: false, }, issueLinkBase: { type: String, default: '', + required: false, }, disabled: { type: Boolean, default: false, + required: false, }, index: { type: Number, default: 0, + required: false, }, rootPath: { type: String, default: '', + required: false, }, groupId: { type: Number, + required: false, }, }, data() { diff --git a/app/assets/javascripts/boards/components/board_delete.js b/app/assets/javascripts/boards/components/board_delete.js index a06db359c94..cc15dc82db9 100644 --- a/app/assets/javascripts/boards/components/board_delete.js +++ b/app/assets/javascripts/boards/components/board_delete.js @@ -7,6 +7,7 @@ export default Vue.extend({ list: { type: Object, default: () => ({}), + required: false, }, }, methods: { diff --git a/app/assets/javascripts/boards/components/board_sidebar.js b/app/assets/javascripts/boards/components/board_sidebar.js index 9b67126bee2..a3a9753f1b5 100644 --- a/app/assets/javascripts/boards/components/board_sidebar.js +++ b/app/assets/javascripts/boards/components/board_sidebar.js @@ -34,6 +34,7 @@ export default Vue.extend({ currentUser: { type: Object, default: () => ({}), + required: false, }, }, data() { diff --git a/app/assets/javascripts/boards/components/boards_selector.vue b/app/assets/javascripts/boards/components/boards_selector.vue index fbf487357a5..f2c976be7ae 100644 --- a/app/assets/javascripts/boards/components/boards_selector.vue +++ b/app/assets/javascripts/boards/components/boards_selector.vue @@ -43,6 +43,7 @@ export default { throttleDuration: { type: Number, default: 200, + required: false, }, boardBaseUrl: { type: String, diff --git a/app/assets/javascripts/boards/components/modal/header.vue b/app/assets/javascripts/boards/components/modal/header.vue index 8cd4840d3d6..a42e691dcf3 100644 --- a/app/assets/javascripts/boards/components/modal/header.vue +++ b/app/assets/javascripts/boards/components/modal/header.vue @@ -1,5 +1,5 @@ <script> -/* eslint-disable @gitlab/vue-i18n/no-bare-strings */ +/* eslint-disable @gitlab/vue-require-i18n-strings */ import { __ } from '~/locale'; import ModalFilters from './filters'; import ModalTabs from './tabs.vue'; diff --git a/app/assets/javascripts/boards/components/modal/tabs.vue b/app/assets/javascripts/boards/components/modal/tabs.vue index 7430fc96654..ed67206218e 100644 --- a/app/assets/javascripts/boards/components/modal/tabs.vue +++ b/app/assets/javascripts/boards/components/modal/tabs.vue @@ -1,5 +1,5 @@ <script> -/* eslint-disable @gitlab/vue-i18n/no-bare-strings */ +/* eslint-disable @gitlab/vue-require-i18n-strings */ import ModalStore from '../../stores/modal_store'; import modalMixin from '../../mixins/modal_mixins'; diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js index d4f4df3ad75..34598d66f45 100644 --- a/app/assets/javascripts/boards/stores/actions.js +++ b/app/assets/javascripts/boards/stores/actions.js @@ -1,5 +1,5 @@ const notImplemented = () => { - /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ + /* eslint-disable-next-line @gitlab/require-i18n-strings */ throw new Error('Not implemented!'); }; diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js index 09eb8bb9b98..7a287400265 100644 --- a/app/assets/javascripts/boards/stores/mutations.js +++ b/app/assets/javascripts/boards/stores/mutations.js @@ -1,7 +1,7 @@ import * as mutationTypes from './mutation_types'; const notImplemented = () => { - /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ + /* eslint-disable-next-line @gitlab/require-i18n-strings */ throw new Error('Not implemented!'); }; diff --git a/app/assets/javascripts/clusters/components/application_row.vue b/app/assets/javascripts/clusters/components/application_row.vue index f8bf778b9e7..53bc079a4e1 100644 --- a/app/assets/javascripts/clusters/components/application_row.vue +++ b/app/assets/javascripts/clusters/components/application_row.vue @@ -1,6 +1,6 @@ <script> /* eslint-disable vue/require-default-prop */ -/* eslint-disable @gitlab/vue-i18n/no-bare-strings */ +/* eslint-disable @gitlab/vue-require-i18n-strings */ import { GlLink, GlModalDirective } from '@gitlab/ui'; import { s__, __, sprintf } from '~/locale'; import eventHub from '../event_hub'; @@ -95,6 +95,7 @@ export default { updateable: { type: Boolean, default: true, + required: false, }, updateSuccessful: { type: Boolean, diff --git a/app/assets/javascripts/clusters/components/knative_domain_editor.vue b/app/assets/javascripts/clusters/components/knative_domain_editor.vue index 66c8297cb75..30efbe2e0f7 100644 --- a/app/assets/javascripts/clusters/components/knative_domain_editor.vue +++ b/app/assets/javascripts/clusters/components/knative_domain_editor.vue @@ -22,6 +22,7 @@ export default { ingressDnsHelpPath: { type: String, default: '', + required: false, }, }, computed: { diff --git a/app/assets/javascripts/cycle_analytics/components/stage_code_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_code_component.vue index 55e5f4ffad2..6b757c6972a 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_code_component.vue +++ b/app/assets/javascripts/cycle_analytics/components/stage_code_component.vue @@ -13,10 +13,12 @@ export default { items: { type: Array, default: () => [], + required: false, }, stage: { type: Object, default: () => ({}), + required: false, }, }, }; diff --git a/app/assets/javascripts/cycle_analytics/components/stage_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_component.vue index c4f5172df3b..cc7ae74dd3a 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_component.vue +++ b/app/assets/javascripts/cycle_analytics/components/stage_component.vue @@ -13,10 +13,12 @@ export default { items: { type: Array, default: () => [], + required: false, }, stage: { type: Object, default: () => ({}), + required: false, }, }, }; diff --git a/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue index a295c8b496b..d61e6995551 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue +++ b/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue @@ -15,10 +15,12 @@ export default { items: { type: Array, default: () => [], + required: false, }, stage: { type: Object, default: () => ({}), + required: false, }, }, }; diff --git a/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue index c5146c3bf88..2a507b7e601 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue +++ b/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue @@ -16,10 +16,12 @@ export default { items: { type: Array, default: () => [], + required: false, }, stage: { type: Object, default: () => ({}), + required: false, }, }, computed: { diff --git a/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue index 35721384210..caff6f9c349 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue +++ b/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue @@ -15,10 +15,12 @@ export default { items: { type: Array, default: () => [], + required: false, }, stage: { type: Object, default: () => ({}), + required: false, }, }, computed: { diff --git a/app/assets/javascripts/diffs/components/commit_item.vue b/app/assets/javascripts/diffs/components/commit_item.vue index cfffccd54eb..9d4edd84f25 100644 --- a/app/assets/javascripts/diffs/components/commit_item.vue +++ b/app/assets/javascripts/diffs/components/commit_item.vue @@ -48,7 +48,7 @@ export default { }, authorUrl() { // name: 'mailto:' is a false positive: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/26#possible-false-positives - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings return this.author.web_url || `mailto:${this.commit.author_email}`; }, authorAvatar() { diff --git a/app/assets/javascripts/diffs/components/hidden_files_warning.vue b/app/assets/javascripts/diffs/components/hidden_files_warning.vue index 035c2b3b11e..ad0ca4fa402 100644 --- a/app/assets/javascripts/diffs/components/hidden_files_warning.vue +++ b/app/assets/javascripts/diffs/components/hidden_files_warning.vue @@ -1,5 +1,5 @@ <script> -/* eslint-disable @gitlab/vue-i18n/no-bare-strings */ +/* eslint-disable @gitlab/vue-require-i18n-strings */ export default { props: { total: { diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue index 3096ccad0aa..dc489c804e9 100644 --- a/app/assets/javascripts/environments/components/environment_item.vue +++ b/app/assets/javascripts/environments/components/environment_item.vue @@ -1,5 +1,5 @@ <script> -/* eslint-disable @gitlab/vue-i18n/no-bare-strings */ +/* eslint-disable @gitlab/vue-require-i18n-strings */ import _ from 'underscore'; import { GlTooltipDirective } from '@gitlab/ui'; import { __, sprintf } from '~/locale'; @@ -342,7 +342,7 @@ export default { isLastDeployment() { // name: 'last?' is a false positive: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/26#possible-false-positives // Vue i18n ESLint rules issue: https://gitlab.com/gitlab-org/gitlab-foss/issues/63560 - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings return this.model && this.model.last_deployment && this.model.last_deployment['last?']; }, diff --git a/app/assets/javascripts/environments/components/environment_rollback.vue b/app/assets/javascripts/environments/components/environment_rollback.vue index 6279bbc83ee..577e480d210 100644 --- a/app/assets/javascripts/environments/components/environment_rollback.vue +++ b/app/assets/javascripts/environments/components/environment_rollback.vue @@ -24,6 +24,7 @@ export default { isLastDeployment: { type: Boolean, default: true, + required: false, }, environment: { diff --git a/app/assets/javascripts/environments/components/stop_environment_modal.vue b/app/assets/javascripts/environments/components/stop_environment_modal.vue index 43ebd7b2824..3caf723442e 100644 --- a/app/assets/javascripts/environments/components/stop_environment_modal.vue +++ b/app/assets/javascripts/environments/components/stop_environment_modal.vue @@ -1,5 +1,5 @@ <script> -/* eslint-disable @gitlab/vue-i18n/no-bare-strings */ +/* eslint-disable @gitlab/vue-require-i18n-strings */ import { GlTooltipDirective } from '@gitlab/ui'; import DeprecatedModal2 from '~/vue_shared/components/deprecated_modal_2.vue'; import { s__, sprintf } from '~/locale'; diff --git a/app/assets/javascripts/error_tracking/utils.js b/app/assets/javascripts/error_tracking/utils.js index 3c382ccd1aa..d1cd70a72fa 100644 --- a/app/assets/javascripts/error_tracking/utils.js +++ b/app/assets/javascripts/error_tracking/utils.js @@ -1,4 +1,4 @@ -/* eslint-disable @gitlab/i18n/no-non-i18n-strings, import/prefer-default-export */ +/* eslint-disable @gitlab/require-i18n-strings, import/prefer-default-export */ /** * Tracks snowplow event when User clicks on error link to Sentry diff --git a/app/assets/javascripts/error_tracking_settings/components/error_tracking_form.vue b/app/assets/javascripts/error_tracking_settings/components/error_tracking_form.vue index 9f77fe8cd59..0be42519092 100644 --- a/app/assets/javascripts/error_tracking_settings/components/error_tracking_form.vue +++ b/app/assets/javascripts/error_tracking_settings/components/error_tracking_form.vue @@ -24,7 +24,7 @@ export default { <label class="label-bold" for="error-tracking-api-host">{{ __('Sentry API URL') }}</label> <div class="row"> <div class="col-8 col-md-9 gl-pr-0"> - <!-- eslint-disable @gitlab/vue-i18n/no-bare-attribute-strings --> + <!-- eslint-disable @gitlab/vue-require-i18n-attribute-strings --> <gl-form-input id="error-tracking-api-host" :value="apiHost" @@ -39,7 +39,7 @@ export default { ) }} </p> - <!-- eslint-enable @gitlab/vue-i18n/no-bare-attribute-strings --> + <!-- eslint-enable @gitlab/vue-require-i18n-attribute-strings --> </div> </div> </div> diff --git a/app/assets/javascripts/ide/components/branches/item.vue b/app/assets/javascripts/ide/components/branches/item.vue index 5c048749060..58a0631ee0d 100644 --- a/app/assets/javascripts/ide/components/branches/item.vue +++ b/app/assets/javascripts/ide/components/branches/item.vue @@ -1,5 +1,5 @@ <script> -/* eslint-disable @gitlab/vue-i18n/no-bare-strings */ +/* eslint-disable @gitlab/vue-require-i18n-strings */ import Icon from '~/vue_shared/components/icon.vue'; import Timeago from '~/vue_shared/components/time_ago_tooltip.vue'; import router from '../../ide_router'; 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 e49d96efe50..e70e251c117 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue @@ -38,7 +38,7 @@ export default { computed: { iconName() { // name: '-solid' is a false positive: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/26#possible-false-positives - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings const suffix = this.stagedList ? '-solid' : ''; return `${getCommitIconMap(this.file).icon}${suffix}`; diff --git a/app/assets/javascripts/ide/components/ide_status_bar.vue b/app/assets/javascripts/ide/components/ide_status_bar.vue index 7ce33fd2278..5585343f367 100644 --- a/app/assets/javascripts/ide/components/ide_status_bar.vue +++ b/app/assets/javascripts/ide/components/ide_status_bar.vue @@ -1,5 +1,5 @@ <script> -/* eslint-disable @gitlab/vue-i18n/no-bare-strings */ +/* eslint-disable @gitlab/vue-require-i18n-strings */ import { mapActions, mapState, mapGetters } from 'vuex'; import IdeStatusList from 'ee_else_ce/ide/components/ide_status_list.vue'; import IdeStatusMr from './ide_status_mr.vue'; diff --git a/app/assets/javascripts/ide/components/panes/collapsible_sidebar.vue b/app/assets/javascripts/ide/components/panes/collapsible_sidebar.vue index d5a123edb80..7f65d089148 100644 --- a/app/assets/javascripts/ide/components/panes/collapsible_sidebar.vue +++ b/app/assets/javascripts/ide/components/panes/collapsible_sidebar.vue @@ -48,7 +48,7 @@ export default { }, }), namespace() { - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings return `${this.side}Pane`; }, tabs() { diff --git a/app/assets/javascripts/issuable_suggestions/components/item.vue b/app/assets/javascripts/issuable_suggestions/components/item.vue index 9f3508fb937..76e4fac5107 100644 --- a/app/assets/javascripts/issuable_suggestions/components/item.vue +++ b/app/assets/javascripts/issuable_suggestions/components/item.vue @@ -1,5 +1,5 @@ <script> -/* eslint-disable @gitlab/vue-i18n/no-bare-strings */ +/* eslint-disable @gitlab/vue-require-i18n-strings */ import { uniqueId } from 'lodash'; import { GlLink, GlTooltip, GlTooltipDirective } from '@gitlab/ui'; import { __ } from '~/locale'; diff --git a/app/assets/javascripts/issue_show/components/edit_actions.vue b/app/assets/javascripts/issue_show/components/edit_actions.vue index ce867f16acf..588ae655de4 100644 --- a/app/assets/javascripts/issue_show/components/edit_actions.vue +++ b/app/assets/javascripts/issue_show/components/edit_actions.vue @@ -1,5 +1,5 @@ <script> -/* eslint-disable @gitlab/vue-i18n/no-bare-strings */ +/* eslint-disable @gitlab/vue-require-i18n-strings */ import { __, sprintf } from '~/locale'; import updateMixin from '../mixins/update'; import eventHub from '../event_hub'; diff --git a/app/assets/javascripts/issue_show/components/edited.vue b/app/assets/javascripts/issue_show/components/edited.vue index 2c92324d292..64f61a1b88e 100644 --- a/app/assets/javascripts/issue_show/components/edited.vue +++ b/app/assets/javascripts/issue_show/components/edited.vue @@ -1,5 +1,5 @@ <script> -/* eslint-disable @gitlab/vue-i18n/no-bare-strings */ +/* eslint-disable @gitlab/vue-require-i18n-strings */ import timeAgoTooltip from '../../vue_shared/components/time_ago_tooltip.vue'; export default { diff --git a/app/assets/javascripts/issue_show/components/fields/description_template.vue b/app/assets/javascripts/issue_show/components/fields/description_template.vue index bc3c81d479e..6d8a9950b6d 100644 --- a/app/assets/javascripts/issue_show/components/fields/description_template.vue +++ b/app/assets/javascripts/issue_show/components/fields/description_template.vue @@ -1,5 +1,5 @@ <script> -/* eslint-disable @gitlab/vue-i18n/no-bare-strings */ +/* eslint-disable @gitlab/vue-require-i18n-strings */ import $ from 'jquery'; import IssuableTemplateSelectors from '../../../templates/issuable_template_selectors'; diff --git a/app/assets/javascripts/jobs/components/commit_block.vue b/app/assets/javascripts/jobs/components/commit_block.vue index 8156f26ffb1..88649ddbdb7 100644 --- a/app/assets/javascripts/jobs/components/commit_block.vue +++ b/app/assets/javascripts/jobs/components/commit_block.vue @@ -1,5 +1,5 @@ <script> -/* eslint-disable @gitlab/vue-i18n/no-bare-strings */ +/* eslint-disable @gitlab/vue-require-i18n-strings */ import { GlLink } from '@gitlab/ui'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; diff --git a/app/assets/javascripts/lib/utils/autosave.js b/app/assets/javascripts/lib/utils/autosave.js index 37896626053..56df2532528 100644 --- a/app/assets/javascripts/lib/utils/autosave.js +++ b/app/assets/javascripts/lib/utils/autosave.js @@ -29,5 +29,5 @@ export const updateDraft = (autosaveKey, text) => { }; export const getDiscussionReplyKey = (noteableType, discussionId) => - /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ + /* eslint-disable-next-line @gitlab/require-i18n-strings */ ['Note', capitalizeFirstCharacter(noteableType), discussionId, 'Reply'].join('/'); diff --git a/app/assets/javascripts/lib/utils/datetime_range.js b/app/assets/javascripts/lib/utils/datetime_range.js index 6d4e21cf386..9275b9e74e1 100644 --- a/app/assets/javascripts/lib/utils/datetime_range.js +++ b/app/assets/javascripts/lib/utils/datetime_range.js @@ -10,7 +10,7 @@ const durationToMillis = duration => { if (Object.entries(duration).length === 1 && Number.isFinite(duration.seconds)) { return secondsToMilliseconds(duration.seconds); } - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings throw new Error('Invalid duration: only `seconds` is supported'); }; @@ -131,7 +131,7 @@ const convertOpenToFixed = ({ anchor, direction }) => { * Handles invalid date ranges */ const handleInvalidRange = () => { - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings throw new Error('The input range does not have the right format.'); }; diff --git a/app/assets/javascripts/lib/utils/notify.js b/app/assets/javascripts/lib/utils/notify.js index 8db08099b3f..aa7884846a3 100644 --- a/app/assets/javascripts/lib/utils/notify.js +++ b/app/assets/javascripts/lib/utils/notify.js @@ -13,7 +13,7 @@ function notificationGranted(message, opts, onclick) { } function notifyPermissions() { - /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ + /* eslint-disable-next-line @gitlab/require-i18n-strings */ if ('Notification' in window) { return Notification.requestPermission(); } @@ -25,7 +25,7 @@ function notifyMe(message, body, icon, onclick) { icon, }; // Let's check if the browser supports notifications - /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ + /* eslint-disable-next-line @gitlab/require-i18n-strings */ if (!('Notification' in window)) { // do nothing } else if (Notification.permission === 'granted') { diff --git a/app/assets/javascripts/lib/utils/unit_format/formatter_factory.js b/app/assets/javascripts/lib/utils/unit_format/formatter_factory.js index 98bcb8348e2..5d3dd79850e 100644 --- a/app/assets/javascripts/lib/utils/unit_format/formatter_factory.js +++ b/app/assets/javascripts/lib/utils/unit_format/formatter_factory.js @@ -111,7 +111,7 @@ export const scaledSIFormatter = (unit = '', prefixOffset = 0) => { }); if (!units.length) { - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings throw new RangeError('The unit cannot be converted, please try a different scale'); } @@ -122,7 +122,7 @@ export const scaledSIFormatter = (unit = '', prefixOffset = 0) => { * Returns a function that formats a number scaled using SI units notation. */ export const scaledBinaryFormatter = (unit = '', prefixOffset = 0) => { - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings const multiplicative = ['Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi']; const symbols = ['', ...multiplicative]; @@ -131,7 +131,7 @@ export const scaledBinaryFormatter = (unit = '', prefixOffset = 0) => { }); if (!units.length) { - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings throw new RangeError('The unit cannot be converted, please try a different scale'); } diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index 4d3a61a7627..81b2e9f13a5 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -49,7 +49,7 @@ window.$ = jQuery; // Add nonce to jQuery script handler jQuery.ajaxSetup({ converters: { - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings, func-names + // eslint-disable-next-line @gitlab/require-i18n-strings, func-names 'text script': function(text) { jQuery.globalEval(text, { nonce: getCspNonceValue() }); return text; diff --git a/app/assets/javascripts/monitoring/components/charts/stacked_column.vue b/app/assets/javascripts/monitoring/components/charts/stacked_column.vue index 56a747b9d1f..66ba20c125f 100644 --- a/app/assets/javascripts/monitoring/components/charts/stacked_column.vue +++ b/app/assets/javascripts/monitoring/components/charts/stacked_column.vue @@ -68,7 +68,7 @@ export default { } }) .catch(e => { - // eslint-disable-next-line no-console, @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line no-console, @gitlab/require-i18n-strings console.error('SVG could not be rendered correctly: ', e); }); }, diff --git a/app/assets/javascripts/monitoring/components/charts/time_series.vue b/app/assets/javascripts/monitoring/components/charts/time_series.vue index cba0a6da6a9..f3cbdffec64 100644 --- a/app/assets/javascripts/monitoring/components/charts/time_series.vue +++ b/app/assets/javascripts/monitoring/components/charts/time_series.vue @@ -327,7 +327,7 @@ export default { } }) .catch(e => { - // eslint-disable-next-line no-console, @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line no-console, @gitlab/require-i18n-strings console.error('SVG could not be rendered correctly: ', e); }); }, diff --git a/app/assets/javascripts/monitoring/components/panel_type.vue b/app/assets/javascripts/monitoring/components/panel_type.vue index 4573ec58ab8..ba92b72a71d 100644 --- a/app/assets/javascripts/monitoring/components/panel_type.vue +++ b/app/assets/javascripts/monitoring/components/panel_type.vue @@ -95,7 +95,7 @@ export default { csvText() { const chartData = this.graphData.metrics[0].result[0].values; const yLabel = this.graphData.y_label; - const header = `timestamp,${yLabel}\r\n`; // eslint-disable-line @gitlab/i18n/no-non-i18n-strings + const header = `timestamp,${yLabel}\r\n`; // eslint-disable-line @gitlab/require-i18n-strings return chartData.reduce((csv, data) => { const row = data.join(','); return `${csv}${row}\r\n`; diff --git a/app/assets/javascripts/monitoring/utils.js b/app/assets/javascripts/monitoring/utils.js index 6694ae2f157..6a46c7e67e4 100644 --- a/app/assets/javascripts/monitoring/utils.js +++ b/app/assets/javascripts/monitoring/utils.js @@ -28,7 +28,7 @@ export const graphDataValidatorForValues = (isValues, graphData) => { ); }; -/* eslint-disable @gitlab/i18n/no-non-i18n-strings */ +/* eslint-disable @gitlab/require-i18n-strings */ /** * Checks that element that triggered event is located on cluster health check dashboard * @param {HTMLElement} element to check against diff --git a/app/assets/javascripts/mr_popover/components/mr_popover.vue b/app/assets/javascripts/mr_popover/components/mr_popover.vue index bbc2feae812..e6bf7a6ec02 100644 --- a/app/assets/javascripts/mr_popover/components/mr_popover.vue +++ b/app/assets/javascripts/mr_popover/components/mr_popover.vue @@ -1,5 +1,5 @@ <script> -/* eslint-disable @gitlab/vue-i18n/no-bare-strings */ +/* eslint-disable @gitlab/vue-require-i18n-strings */ import { GlPopover, GlSkeletonLoading } from '@gitlab/ui'; import CiIcon from '../../vue_shared/components/ci_icon.vue'; import timeagoMixin from '../../vue_shared/mixins/timeago'; @@ -8,7 +8,7 @@ import { mrStates, humanMRStates } from '../constants'; export default { // name: 'MRPopover' is a false positive: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/25 - name: 'MRPopover', // eslint-disable-line @gitlab/i18n/no-non-i18n-strings + name: 'MRPopover', // eslint-disable-line @gitlab/require-i18n-strings components: { GlPopover, GlSkeletonLoading, @@ -102,11 +102,11 @@ export default { <ci-icon v-if="detailedStatus" :status="detailedStatus" /> </div> <h5 class="my-2">{{ mergeRequestTitle }}</h5> - <!-- eslint-disable @gitlab/vue-i18n/no-bare-strings --> + <!-- eslint-disable @gitlab/vue-require-i18n-strings --> <div class="text-secondary"> {{ `${projectPath}!${mergeRequestIID}` }} </div> - <!-- eslint-enable @gitlab/vue-i18n/no-bare-strings --> + <!-- eslint-enable @gitlab/vue-require-i18n-strings --> </div> </gl-popover> </template> diff --git a/app/assets/javascripts/new_branch_form.js b/app/assets/javascripts/new_branch_form.js index 918c6e408a2..be3ea4e680c 100644 --- a/app/assets/javascripts/new_branch_form.js +++ b/app/assets/javascripts/new_branch_form.js @@ -1,4 +1,4 @@ -/* eslint-disable func-names, consistent-return, no-return-assign, no-else-return, @gitlab/i18n/no-non-i18n-strings */ +/* eslint-disable func-names, consistent-return, no-return-assign, no-else-return, @gitlab/require-i18n-strings */ import $ from 'jquery'; import RefSelectDropdown from './ref_select_dropdown'; diff --git a/app/assets/javascripts/notebook/cells/code/index.vue b/app/assets/javascripts/notebook/cells/code/index.vue index 470d8c87d59..e1ef9aa6d79 100644 --- a/app/assets/javascripts/notebook/cells/code/index.vue +++ b/app/assets/javascripts/notebook/cells/code/index.vue @@ -29,6 +29,7 @@ export default { metadata: { type: Object, default: () => ({}), + required: false, }, }, computed: { diff --git a/app/assets/javascripts/notebook/cells/markdown.vue b/app/assets/javascripts/notebook/cells/markdown.vue index 753aa96bb55..dab27cf8269 100644 --- a/app/assets/javascripts/notebook/cells/markdown.vue +++ b/app/assets/javascripts/notebook/cells/markdown.vue @@ -43,7 +43,7 @@ renderer.paragraph = t => { if (typeof katex !== 'undefined') { const katexString = text .replace(/&/g, '&') - .replace(/&=&/g, '\\space=\\space') // eslint-disable-line @gitlab/i18n/no-non-i18n-strings + .replace(/&=&/g, '\\space=\\space') // eslint-disable-line @gitlab/require-i18n-strings .replace(/<(\/?)em>/g, '_'); const regex = new RegExp(katexRegexString, 'gi'); const matchLocation = katexString.search(regex); diff --git a/app/assets/javascripts/notebook/cells/output/image.vue b/app/assets/javascripts/notebook/cells/output/image.vue index 842d9e8da0d..065f5def83c 100644 --- a/app/assets/javascripts/notebook/cells/output/image.vue +++ b/app/assets/javascripts/notebook/cells/output/image.vue @@ -25,7 +25,7 @@ export default { }, computed: { imgSrc() { - return `data:${this.outputType};base64,${this.rawCode}`; // eslint-disable-line @gitlab/i18n/no-non-i18n-strings + return `data:${this.outputType};base64,${this.rawCode}`; // eslint-disable-line @gitlab/require-i18n-strings }, showOutput() { return this.index === 0; diff --git a/app/assets/javascripts/notebook/cells/output/index.vue b/app/assets/javascripts/notebook/cells/output/index.vue index d8b0e099bc4..61626f7aaf5 100644 --- a/app/assets/javascripts/notebook/cells/output/index.vue +++ b/app/assets/javascripts/notebook/cells/output/index.vue @@ -22,6 +22,7 @@ export default { metadata: { type: Object, default: () => ({}), + required: false, }, }, methods: { diff --git a/app/assets/javascripts/notebook/index.vue b/app/assets/javascripts/notebook/index.vue index 4a3c1a28279..44dc1856e49 100644 --- a/app/assets/javascripts/notebook/index.vue +++ b/app/assets/javascripts/notebook/index.vue @@ -39,7 +39,7 @@ export default { }, methods: { cellType(type) { - return `${type}-cell`; // eslint-disable-line @gitlab/i18n/no-non-i18n-strings + return `${type}-cell`; // eslint-disable-line @gitlab/require-i18n-strings }, }, }; diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue index fe22737c7fc..c3915ef299b 100644 --- a/app/assets/javascripts/notes/components/diff_with_note.vue +++ b/app/assets/javascripts/notes/components/diff_with_note.vue @@ -1,5 +1,5 @@ <script> -/* eslint-disable @gitlab/vue-i18n/no-bare-strings */ +/* eslint-disable @gitlab/vue-require-i18n-strings */ import { mapState, mapActions } from 'vuex'; import { GlSkeletonLoading } from '@gitlab/ui'; import DiffFileHeader from '~/diffs/components/diff_file_header.vue'; diff --git a/app/assets/javascripts/notes/components/note_edited_text.vue b/app/assets/javascripts/notes/components/note_edited_text.vue index 1af5af5c470..7c052320c98 100644 --- a/app/assets/javascripts/notes/components/note_edited_text.vue +++ b/app/assets/javascripts/notes/components/note_edited_text.vue @@ -1,5 +1,5 @@ <script> -/* eslint-disable @gitlab/vue-i18n/no-bare-strings */ +/* eslint-disable @gitlab/vue-require-i18n-strings */ import timeAgoTooltip from '../../vue_shared/components/time_ago_tooltip.vue'; export default { diff --git a/app/assets/javascripts/operation_settings/components/external_dashboard.vue b/app/assets/javascripts/operation_settings/components/external_dashboard.vue index 8b6467bc0f6..3594e30bd69 100644 --- a/app/assets/javascripts/operation_settings/components/external_dashboard.vue +++ b/app/assets/javascripts/operation_settings/components/external_dashboard.vue @@ -54,14 +54,14 @@ export default { :description="s__('ExternalMetrics|Enter the URL of the dashboard you want to link to')" > <!-- placeholder with a url is a false positive --> - <!-- eslint-disable @gitlab/vue-i18n/no-bare-attribute-strings --> + <!-- eslint-disable @gitlab/vue-require-i18n-attribute-strings --> <gl-form-input id="full-dashboard-url" v-model="userDashboardUrl" placeholder="https://my-org.gitlab.io/my-dashboards" @keydown.enter.native.prevent="updateExternalDashboardUrl" /> - <!-- eslint-enable @gitlab/vue-i18n/no-bare-attribute-strings --> + <!-- eslint-enable @gitlab/vue-require-i18n-attribute-strings --> </gl-form-group> <gl-button variant="success" @click="updateExternalDashboardUrl"> {{ __('Save Changes') }} diff --git a/app/assets/javascripts/pages/admin/users/index.js b/app/assets/javascripts/pages/admin/users/index.js index bc96e88351b..86c4b4f4f48 100644 --- a/app/assets/javascripts/pages/admin/users/index.js +++ b/app/assets/javascripts/pages/admin/users/index.js @@ -19,7 +19,7 @@ function loadModalsConfigurationFromHtml(modalsElement) { const modalsConfiguration = {}; if (!modalsElement) { - /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ + /* eslint-disable-next-line @gitlab/require-i18n-strings */ throw new Error('Modals content element not found!'); } diff --git a/app/assets/javascripts/performance_bar/index.js b/app/assets/javascripts/performance_bar/index.js index 7b373a8ce22..a294f3f36a6 100644 --- a/app/assets/javascripts/performance_bar/index.js +++ b/app/assets/javascripts/performance_bar/index.js @@ -1,4 +1,4 @@ -/* eslint-disable @gitlab/i18n/no-non-i18n-strings */ +/* eslint-disable @gitlab/require-i18n-strings */ import Vue from 'vue'; import axios from '~/lib/utils/axios_utils'; diff --git a/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue index 3f07b77ed32..2212428ced5 100644 --- a/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue +++ b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue @@ -1,5 +1,5 @@ <script> -/* eslint-disable @gitlab/vue-i18n/no-bare-strings */ +/* eslint-disable @gitlab/vue-require-i18n-strings */ import { GlLink, GlTooltipDirective } from '@gitlab/ui'; import Icon from '~/vue_shared/components/icon.vue'; diff --git a/app/assets/javascripts/registry/explorer/components/project_empty_state.vue b/app/assets/javascripts/registry/explorer/components/project_empty_state.vue index 53853b4b9fb..556df10ea5b 100644 --- a/app/assets/javascripts/registry/explorer/components/project_empty_state.vue +++ b/app/assets/javascripts/registry/explorer/components/project_empty_state.vue @@ -14,15 +14,15 @@ export default { computed: { ...mapState(['config']), dockerBuildCommand() { - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings return `docker build -t ${this.config.repositoryUrl} .`; }, dockerPushCommand() { - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings return `docker push ${this.config.repositoryUrl}`; }, dockerLoginCommand() { - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings return `docker login ${this.config.registryHostUrlWithPort}`; }, }, diff --git a/app/assets/javascripts/registry/list/components/project_empty_state.vue b/app/assets/javascripts/registry/list/components/project_empty_state.vue index 80ef31004c8..900498ed03d 100644 --- a/app/assets/javascripts/registry/list/components/project_empty_state.vue +++ b/app/assets/javascripts/registry/list/components/project_empty_state.vue @@ -37,15 +37,15 @@ export default { }, computed: { dockerBuildCommand() { - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings return `docker build -t ${this.repositoryUrl} .`; }, dockerPushCommand() { - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings return `docker push ${this.repositoryUrl}`; }, dockerLoginCommand() { - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings return `docker login ${this.registryHostUrlWithPort}`; }, noContainerImagesText() { diff --git a/app/assets/javascripts/reports/components/report_link.vue b/app/assets/javascripts/reports/components/report_link.vue index e32e1ac49ca..f285b526a54 100644 --- a/app/assets/javascripts/reports/components/report_link.vue +++ b/app/assets/javascripts/reports/components/report_link.vue @@ -1,5 +1,5 @@ <script> -/* eslint-disable @gitlab/vue-i18n/no-bare-strings */ +/* eslint-disable @gitlab/vue-require-i18n-strings */ export default { name: 'ReportIssueLink', props: { diff --git a/app/assets/javascripts/repository/components/table/row.vue b/app/assets/javascripts/repository/components/table/row.vue index f3e6e3686a3..88a7e3e3a68 100644 --- a/app/assets/javascripts/repository/components/table/row.vue +++ b/app/assets/javascripts/repository/components/table/row.vue @@ -151,7 +151,7 @@ export default { > {{ fullPath }} </component> - <!-- eslint-disable-next-line @gitlab/vue-i18n/no-bare-strings --> + <!-- eslint-disable-next-line @gitlab/vue-require-i18n-strings --> <gl-badge v-if="lfsOid" variant="default" class="label-lfs ml-1">LFS</gl-badge> <template v-if="isSubmodule"> @ <gl-link :href="submoduleTreeUrl" class="commit-sha">{{ shortSha }}</gl-link> diff --git a/app/assets/javascripts/repository/graphql.js b/app/assets/javascripts/repository/graphql.js index 265df20636b..0c68b5a599b 100644 --- a/app/assets/javascripts/repository/graphql.js +++ b/app/assets/javascripts/repository/graphql.js @@ -39,7 +39,7 @@ const defaultClient = createDefaultClient( cacheConfig: { fragmentMatcher, dataIdFromObject: obj => { - /* eslint-disable @gitlab/i18n/no-non-i18n-strings */ + /* eslint-disable @gitlab/require-i18n-strings */ // eslint-disable-next-line no-underscore-dangle switch (obj.__typename) { // We need to create a dynamic ID for each entry @@ -55,7 +55,7 @@ const defaultClient = createDefaultClient( // eslint-disable-next-line no-underscore-dangle return obj.id || obj._id; } - /* eslint-enable @gitlab/i18n/no-non-i18n-strings */ + /* eslint-enable @gitlab/require-i18n-strings */ }, }, }, diff --git a/app/assets/javascripts/repository/utils/title.js b/app/assets/javascripts/repository/utils/title.js index 9c4b334a1ce..442f6c5d741 100644 --- a/app/assets/javascripts/repository/utils/title.js +++ b/app/assets/javascripts/repository/utils/title.js @@ -9,7 +9,7 @@ export const setTitle = (pathMatch, ref, project) => { const path = pathMatch.replace(/^\//, ''); const isEmpty = path === ''; - /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ + /* eslint-disable-next-line @gitlab/require-i18n-strings */ document.title = `${isEmpty ? 'Files' : path} · ${ref} · ${project} ${DEFAULT_TITLE}`; }; diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js index 8d888a574d8..3eaa34c8a93 100644 --- a/app/assets/javascripts/search_autocomplete.js +++ b/app/assets/javascripts/search_autocomplete.js @@ -420,11 +420,11 @@ export class SearchAutocomplete { onClick(item, $el, e) { if (window.location.pathname.indexOf(item.url) !== -1) { if (!e.metaKey) e.preventDefault(); - /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ + /* eslint-disable-next-line @gitlab/require-i18n-strings */ if (item.category === 'Projects') { this.projectInputEl.val(item.id); } - /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ + // eslint-disable-next-line @gitlab/require-i18n-strings if (item.category === 'Groups') { this.groupInputEl.val(item.id); } diff --git a/app/assets/javascripts/sidebar/components/assignees/assignees.vue b/app/assets/javascripts/sidebar/components/assignees/assignees.vue index d9739e8d197..f16b16a6837 100644 --- a/app/assets/javascripts/sidebar/components/assignees/assignees.vue +++ b/app/assets/javascripts/sidebar/components/assignees/assignees.vue @@ -4,7 +4,7 @@ import UncollapsedAssigneeList from '../assignees/uncollapsed_assignee_list.vue' export default { // name: 'Assignees' is a false positive: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/26#possible-false-positives - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings name: 'Assignees', components: { CollapsedAssigneeList, diff --git a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue index 682ca600b6a..1e8a31fff81 100644 --- a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue +++ b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue @@ -40,6 +40,7 @@ export default { limitToHours: { type: Boolean, default: false, + required: false, }, rootPath: { type: String, diff --git a/app/assets/javascripts/sidebar/queries/updateStatus.mutation.graphql b/app/assets/javascripts/sidebar/queries/updateStatus.mutation.graphql index 27a5cff12c7..b45b6b46c8f 100644 --- a/app/assets/javascripts/sidebar/queries/updateStatus.mutation.graphql +++ b/app/assets/javascripts/sidebar/queries/updateStatus.mutation.graphql @@ -1,7 +1,8 @@ -mutation ($projectPath: ID!, $iid: String!, $healthStatus: HealthStatus) { - updateIssue(input: { projectPath: $projectPath, iid: $iid, healthStatus: $healthStatus}) { +mutation($projectPath: ID!, $iid: String!, $healthStatus: HealthStatus) { + updateIssue(input: { projectPath: $projectPath, iid: $iid, healthStatus: $healthStatus }) { issue { healthStatus } + errors } } diff --git a/app/assets/javascripts/snippets/components/snippet_header.vue b/app/assets/javascripts/snippets/components/snippet_header.vue index 36ba6eeecbd..bbe4c33fa43 100644 --- a/app/assets/javascripts/snippets/components/snippet_header.vue +++ b/app/assets/javascripts/snippets/components/snippet_header.vue @@ -137,7 +137,10 @@ export default { mutation: DeleteSnippetMutation, variables: { id: this.snippet.id }, }) - .then(() => { + .then(({ data }) => { + if (data?.destroySnippet?.errors) { + throw new Error(data?.destroySnippet?.errors[0]); + } this.isDeleting = false; this.errorMessage = undefined; this.closeDeleteModal(); diff --git a/app/assets/javascripts/tracking.js b/app/assets/javascripts/tracking.js index ab5acd83b01..09fe952e5f0 100644 --- a/app/assets/javascripts/tracking.js +++ b/app/assets/javascripts/tracking.js @@ -54,7 +54,7 @@ export default class Tracking { static event(category = document.body.dataset.page, action = 'generic', data = {}) { if (!this.enabled()) return false; - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings if (!category) throw new Error('Tracking: no category provided for tracking.'); const { label, property, value, context } = data; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment.vue index 9c476d5b2e0..f497936e299 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment.vue @@ -5,7 +5,7 @@ import { MANUAL_DEPLOY, WILL_DEPLOY, CREATED } from './constants'; export default { // name: 'Deployment' is a false positive: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/26#possible-false-positives - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings name: 'Deployment', components: { DeploymentActions, diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue index c8e652a1305..7279aaf0809 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue @@ -1,5 +1,5 @@ <script> -/* eslint-disable @gitlab/vue-i18n/no-bare-strings */ +/* eslint-disable @gitlab/vue-require-i18n-strings */ import { GlLoadingIcon } from '@gitlab/ui'; import Flash from '~/flash'; import tooltip from '~/vue_shared/directives/tooltip'; @@ -86,7 +86,7 @@ export default { .then(res => res.data) .then(data => { // False positive i18n lint: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/26 - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings if (data.message === 'Branch was deleted') { eventHub.$emit('MRWidgetUpdateRequested', () => { this.isMakingRequest = false; diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index 8b12e8ffb73..084deee042b 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -42,7 +42,7 @@ import { setFaviconOverlay } from '../lib/utils/common_utils'; export default { el: '#js-vue-mr-widget', // False positive i18n lint: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/25 - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings name: 'MRWidget', components: { Loading, diff --git a/app/assets/javascripts/vue_shared/components/changed_file_icon.vue b/app/assets/javascripts/vue_shared/components/changed_file_icon.vue index 1bd320d81e8..60e41a16854 100644 --- a/app/assets/javascripts/vue_shared/components/changed_file_icon.vue +++ b/app/assets/javascripts/vue_shared/components/changed_file_icon.vue @@ -40,7 +40,7 @@ export default { computed: { changedIcon() { // False positive i18n lint: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/26 - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings const suffix = this.file.staged && this.showStagedIcon ? '-solid' : ''; return `${getCommitIconMap(this.file).icon}${suffix}`; diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/content_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/content_viewer.vue index 66155ddcdd9..2f5e5f35064 100644 --- a/app/assets/javascripts/vue_shared/components/content_viewer/content_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/content_viewer/content_viewer.vue @@ -8,6 +8,7 @@ export default { content: { type: String, default: '', + required: false, }, path: { type: String, diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue index 6a4a834337a..9f98943f6b4 100644 --- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue @@ -16,6 +16,7 @@ export default { renderInfo: { type: Boolean, default: true, + required: false, }, innerCssClasses: { type: [Array, Object, String], diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue index 36cbb230d30..bf25ed96f35 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/header.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue @@ -39,10 +39,10 @@ export default { mdTable() { return [ // False positive i18n lint: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/26 - '| header | header |', // eslint-disable-line @gitlab/i18n/no-non-i18n-strings + '| header | header |', // eslint-disable-line @gitlab/require-i18n-strings '| ------ | ------ |', - '| cell | cell |', // eslint-disable-line @gitlab/i18n/no-non-i18n-strings - '| cell | cell |', // eslint-disable-line @gitlab/i18n/no-non-i18n-strings + '| cell | cell |', // eslint-disable-line @gitlab/require-i18n-strings + '| cell | cell |', // eslint-disable-line @gitlab/require-i18n-strings ].join('\n'); }, mdSuggestion() { diff --git a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue index 4da99e00165..486d4f6b609 100644 --- a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue +++ b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue @@ -1,5 +1,5 @@ <script> -/* eslint-disable @gitlab/vue-i18n/no-bare-strings */ +/* eslint-disable @gitlab/vue-require-i18n-strings */ import { GlLink, GlLoadingIcon } from '@gitlab/ui'; export default { diff --git a/app/assets/javascripts/vue_shared/components/project_avatar/default.vue b/app/assets/javascripts/vue_shared/components/project_avatar/default.vue index 881b5059d2a..4bc70870767 100644 --- a/app/assets/javascripts/vue_shared/components/project_avatar/default.vue +++ b/app/assets/javascripts/vue_shared/components/project_avatar/default.vue @@ -15,6 +15,7 @@ export default { size: { type: Number, default: 40, + required: false, }, }, computed: { diff --git a/app/assets/javascripts/vue_shared/components/select2_select.vue b/app/assets/javascripts/vue_shared/components/select2_select.vue index eb741d238b5..c90bd4da6c2 100644 --- a/app/assets/javascripts/vue_shared/components/select2_select.vue +++ b/app/assets/javascripts/vue_shared/components/select2_select.vue @@ -4,7 +4,7 @@ import 'select2'; export default { // False positive i18n lint: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/26 - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings name: 'Select2Select', props: { options: { diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue index 0e401a9f7aa..44cc11a6aaa 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue @@ -77,12 +77,12 @@ export default { }, enableScopedLabels: { type: Boolean, - require: false, + required: false, default: false, }, scopedLabelsDocumentationLink: { type: String, - require: false, + required: false, default: '#', }, }, diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_button.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_button.vue index 1eed8907bb7..c3bc61d0053 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_button.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_button.vue @@ -33,12 +33,12 @@ export default { }, enableScopedLabels: { type: Boolean, - require: false, + required: false, default: false, }, scopedLabelsDocumentationLink: { type: String, - require: false, + required: false, default: '#', }, }, diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/actions.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/actions.js index 145ec7dc566..e6053628eca 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/actions.js +++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/actions.js @@ -45,7 +45,7 @@ export const createLabel = ({ state, dispatch }, label) => { dispatch('receiveCreateLabelSuccess'); dispatch('toggleDropdownContentsCreateView'); } else { - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings throw new Error('Error Creating Label'); } }) diff --git a/app/assets/javascripts/vue_shared/components/smart_virtual_list.vue b/app/assets/javascripts/vue_shared/components/smart_virtual_list.vue index 49f987bb619..0fb7fd6cd38 100644 --- a/app/assets/javascripts/vue_shared/components/smart_virtual_list.vue +++ b/app/assets/javascripts/vue_shared/components/smart_virtual_list.vue @@ -8,9 +8,9 @@ export default { size: { type: Number, required: true }, length: { type: Number, required: true }, remain: { type: Number, required: true }, - rtag: { type: String, default: 'div' }, - wtag: { type: String, default: 'div' }, - wclass: { type: String, default: null }, + rtag: { type: String, default: 'div', required: false }, + wtag: { type: String, default: 'div', required: false }, + wclass: { type: String, default: null, required: false }, }, }; </script> diff --git a/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue b/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue index ea564d1b2f2..ec077197c9c 100644 --- a/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue +++ b/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue @@ -83,7 +83,7 @@ export default { }, barStyle(percent) { // False positive i18n lint: https://gitlab.com/gitlab-org/frontend/eslint-plugin-i18n/issues/26 - // eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings + // eslint-disable-next-line @gitlab/require-i18n-strings return `width: ${percent}%;`; }, getTooltip(label, count) { diff --git a/app/controllers/admin/concerns/authenticates_2fa_for_admin_mode.rb b/app/controllers/concerns/authenticates_2fa_for_admin_mode.rb index c6fd1d55e51..c6fd1d55e51 100644 --- a/app/controllers/admin/concerns/authenticates_2fa_for_admin_mode.rb +++ b/app/controllers/concerns/authenticates_2fa_for_admin_mode.rb diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb index 699aa51e6c8..bd9efef94f8 100644 --- a/app/graphql/types/group_type.rb +++ b/app/graphql/types/group_type.rb @@ -51,6 +51,7 @@ module Types Types::BoardType.connection_type, null: true, description: 'Boards of the group', + max_page_size: 2000, resolver: Resolvers::BoardsResolver field :board, diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb index 1142459f6eb..5c0b9182ac5 100644 --- a/app/graphql/types/project_type.rb +++ b/app/graphql/types/project_type.rb @@ -184,6 +184,7 @@ module Types Types::BoardType.connection_type, null: true, description: 'Boards of the project', + max_page_size: 2000, resolver: Resolvers::BoardsResolver field :board, diff --git a/app/models/issue.rb b/app/models/issue.rb index 145807457a1..3d389013985 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -68,6 +68,7 @@ class Issue < ApplicationRecord scope :order_closest_future_date, -> { reorder(Arel.sql('CASE WHEN issues.due_date >= CURRENT_DATE THEN 0 ELSE 1 END ASC, ABS(CURRENT_DATE - issues.due_date) ASC')) } scope :order_relative_position_asc, -> { reorder(::Gitlab::Database.nulls_last_order('relative_position', 'ASC')) } scope :order_closed_date_desc, -> { reorder(closed_at: :desc) } + scope :order_created_at_desc, -> { reorder(created_at: :desc) } scope :preload_associated_models, -> { preload(:labels, project: :namespace) } scope :with_api_entity_associations, -> { preload(:timelogs, :assignees, :author, :notes, :labels, project: [:route, { namespace: :route }] ) } diff --git a/changelogs/unreleased/208923-enable-batch-counting-for-some-individual-queries-4.yml b/changelogs/unreleased/208923-enable-batch-counting-for-some-individual-queries-4.yml new file mode 100644 index 00000000000..07e7b71a787 --- /dev/null +++ b/changelogs/unreleased/208923-enable-batch-counting-for-some-individual-queries-4.yml @@ -0,0 +1,5 @@ +--- +title: Optimize projects_service_active queries performance in usage data +merge_request: 27093 +author: +type: performance diff --git a/changelogs/unreleased/ref-params-validator.yml b/changelogs/unreleased/ref-params-validator.yml new file mode 100644 index 00000000000..a4ac8e30e19 --- /dev/null +++ b/changelogs/unreleased/ref-params-validator.yml @@ -0,0 +1,5 @@ +--- +title: Add grape custom validator for git reference params +merge_request: 26102 +author: Rajendra Kadam +type: added diff --git a/config/application.rb b/config/application.rb index 14e92bf5905..e1be913b5c3 100644 --- a/config/application.rb +++ b/config/application.rb @@ -32,6 +32,8 @@ module Gitlab config.active_record.sqlite3.represent_boolean_as_integer = true + config.autoloader = :zeitwerk + # Sidekiq uses eager loading, but directories not in the standard Rails # directories must be added to the eager load paths: # https://github.com/mperham/sidekiq/wiki/FAQ#why-doesnt-sidekiq-autoload-my-rails-application-code diff --git a/config/environments/development.rb b/config/environments/development.rb index 25d57467060..c42d7127a22 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -42,7 +42,7 @@ Rails.application.configure do config.action_mailer.raise_delivery_errors = true # Don't make a mess when bootstrapping a development environment config.action_mailer.perform_deliveries = (ENV['BOOTSTRAP'] != '1') - config.action_mailer.preview_path = 'app/mailers/previews' + config.action_mailer.preview_path = Rails.root.join('app', 'mailers', 'previews') config.eager_load = false diff --git a/config/environments/test.rb b/config/environments/test.rb index 71cd5200415..f8fb7f60f0d 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -43,7 +43,7 @@ Rails.application.configure do # Print deprecation notices to the stderr config.active_support.deprecation = :stderr - config.eager_load = true + config.eager_load = false config.cache_store = :null_store diff --git a/config/initializers/2_gitlab.rb b/config/initializers/2_gitlab.rb deleted file mode 100644 index 8b7f245b7b0..00000000000 --- a/config/initializers/2_gitlab.rb +++ /dev/null @@ -1 +0,0 @@ -require_dependency 'gitlab' diff --git a/config/initializers_before_autoloader/002_zeitwerk.rb b/config/initializers_before_autoloader/002_zeitwerk.rb new file mode 100644 index 00000000000..df0c16b0bc0 --- /dev/null +++ b/config/initializers_before_autoloader/002_zeitwerk.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +Rails.autoloaders.each do |autoloader| + # We need to ignore these since these are non-Ruby files + # that do not define Ruby classes / modules + autoloader.ignore(Rails.root.join('lib/support')) + # Ignore generators since these are loaded manually by Rails + autoloader.ignore(Rails.root.join('lib/generators')) + autoloader.ignore(Rails.root.join('ee/lib/generators')) if Gitlab.ee? + # Mailer previews are also loaded manually by Rails + autoloader.ignore(Rails.root.join('app/mailers/previews')) + autoloader.ignore(Rails.root.join('ee/app/mailers/previews')) if Gitlab.ee? + # Ignore these files because these are only used in Rake tasks + # and are not available in production + autoloader.ignore(Rails.root.join('lib/gitlab/graphql/docs')) + + autoloader.inflector.inflect( + 'authenticates_2fa_for_admin_mode' => 'Authenticates2FAForAdminMode', + 'api' => 'API', + 'api_guard' => 'APIGuard', + 'group_api_compatibility' => 'GroupAPICompatibility', + 'project_api_compatibility' => 'ProjectAPICompatibility', + 'cte' => 'CTE', + 'recursive_cte' => 'RecursiveCTE', + 'cidr' => 'CIDR', + 'cli' => 'CLI', + 'dn' => 'DN', + 'hmac_token' => 'HMACToken', + 'html' => 'HTML', + 'html_parser' => 'HTMLParser', + 'html_gitlab' => 'HTMLGitlab', + 'http' => 'HTTP', + 'http_connection_adapter' => 'HTTPConnectionAdapter', + 'http_clone_enabled_check' => 'HTTPCloneEnabledCheck', + 'chunked_io' => 'ChunkedIO', + 'http_io' => 'HttpIO', + 'json' => 'JSON', + 'json_formatter' => 'JSONFormatter', + 'json_web_token' => 'JSONWebToken', + 'as_json' => 'AsJSON', + 'ldap_key' => 'LDAPKey', + 'mr_note' => 'MRNote', + 'pdf' => 'PDF', + 'rsa_token' => 'RSAToken', + 'san_extension' => 'SANExtension', + 'sca' => 'SCA', + 'spdx' => 'SPDX', + 'sql' => 'SQL', + 'ssh_key' => 'SSHKey', + 'ssh_key_with_user' => 'SSHKeyWithUser', + 'ssh_public_key' => 'SSHPublicKey', + 'git_push_ssh_proxy' => 'GitPushSSHProxy', + 'git_user_default_ssh_config_check' => 'GitUserDefaultSSHConfigCheck', + 'binary_stl' => 'BinarySTL', + 'text_stl' => 'TextSTL', + 'svg' => 'SVG', + 'function_uri' => 'FunctionURI' + ) +end diff --git a/config/settings.rb b/config/settings.rb index 144a068ef2a..a9e91ce22d7 100644 --- a/config/settings.rb +++ b/config/settings.rb @@ -3,12 +3,6 @@ require 'settingslogic' require 'digest/md5' -# We can not use `Rails.root` here, as this file might be loaded without the -# full Rails environment being loaded. We can not use `require_relative` either, -# as Rails uses `load` for `require_dependency` (used when loading the Rails -# environment). This could then lead to this file being loaded twice. -require_dependency File.expand_path('../lib/gitlab', __dir__) - class Settings < Settingslogic source ENV.fetch('GITLAB_CONFIG') { Pathname.new(File.expand_path('..', __dir__)).join('config/gitlab.yml') } namespace ENV.fetch('GITLAB_ENV') { Rails.env } diff --git a/db/migrate/20200312125121_add_index_on_active_and_template_and_type_and_id_to_services.rb b/db/migrate/20200312125121_add_index_on_active_and_template_and_type_and_id_to_services.rb new file mode 100644 index 00000000000..3a2390bb6a1 --- /dev/null +++ b/db/migrate/20200312125121_add_index_on_active_and_template_and_type_and_id_to_services.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class AddIndexOnActiveAndTemplateAndTypeAndIdToServices < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + INDEX_NAME = 'index_services_on_type_and_id_and_template_when_active' + + disable_ddl_transaction! + + def up + add_concurrent_index :services, [:type, :id, :template], where: 'active = TRUE', name: INDEX_NAME + end + + def down + remove_concurrent_index :services, INDEX_NAME + end +end diff --git a/db/schema.rb b/db/schema.rb index 9edc1d9853c..db9ba2ce8f5 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -3977,6 +3977,7 @@ ActiveRecord::Schema.define(version: 2020_03_13_123934) do t.boolean "instance", default: false, null: false t.index ["project_id", "type"], name: "index_services_on_project_id_and_type" t.index ["template"], name: "index_services_on_template" + t.index ["type", "id", "template"], name: "index_services_on_type_and_id_and_template_when_active", where: "(active = true)" t.index ["type", "instance"], name: "index_services_on_type_and_instance", unique: true, where: "(instance IS TRUE)" t.index ["type", "template"], name: "index_services_on_type_and_template", unique: true, where: "(template IS TRUE)" t.index ["type"], name: "index_services_on_type" diff --git a/doc/administration/gitaly/img/praefect_architecture_v12_10.png b/doc/administration/gitaly/img/praefect_architecture_v12_10.png Binary files differnew file mode 100644 index 00000000000..7b8f1138b23 --- /dev/null +++ b/doc/administration/gitaly/img/praefect_architecture_v12_10.png diff --git a/doc/administration/gitaly/img/praefect_architecture_v12_9.png b/doc/administration/gitaly/img/praefect_architecture_v12_9.png Binary files differdeleted file mode 100644 index b68e495cb17..00000000000 --- a/doc/administration/gitaly/img/praefect_architecture_v12_9.png +++ /dev/null diff --git a/doc/administration/gitaly/praefect.md b/doc/administration/gitaly/praefect.md index 4e8dc533f44..9fb61d93f73 100644 --- a/doc/administration/gitaly/praefect.md +++ b/doc/administration/gitaly/praefect.md @@ -28,7 +28,7 @@ reference architecture additionally requires: - 1 PostgreSQL server (PostgreSQL 9.6 or newer) - 3 Gitaly nodes (1 primary, 2 secondary) -![Alpha architecture diagram](img/praefect_architecture_v12_9.png) +![Alpha architecture diagram](img/praefect_architecture_v12_10.png) See the [design document](https://gitlab.com/gitlab-org/gitaly/-/blob/master/doc/design_ha.md) diff --git a/doc/administration/index.md b/doc/administration/index.md index 4ca03fa2669..2ab4b3f710e 100644 --- a/doc/administration/index.md +++ b/doc/administration/index.md @@ -200,6 +200,7 @@ Learn how to install, configure, update, and maintain your GitLab instance. - [Log system](logs.md): Where to look for logs. - [Sidekiq Troubleshooting](troubleshooting/sidekiq.md): Debug when Sidekiq appears hung and is not processing jobs. - [Troubleshooting Elasticsearch](troubleshooting/elasticsearch.md) +- [Navigating GitLab via Rails console](troubleshooting/navigating_gitlab_via_rails_console.md) - [GitLab application limits](instance_limits.md) ### Support Team Docs diff --git a/doc/administration/troubleshooting/navigating_gitlab_via_rails_console.md b/doc/administration/troubleshooting/navigating_gitlab_via_rails_console.md new file mode 100644 index 00000000000..01c3da80481 --- /dev/null +++ b/doc/administration/troubleshooting/navigating_gitlab_via_rails_console.md @@ -0,0 +1,469 @@ +# Navigating GitLab via Rails console + +At the heart of GitLab is a web application [built using the Ruby on Rails +framework](https://about.gitlab.com/blog/2018/10/29/why-we-use-rails-to-build-gitlab/). +Thanks to this, we also get access to the amazing tools built right into Rails. +In this guide, we'll introduce the [Rails console](https://docs.gitlab.com/omnibus/maintenance/#starting-a-rails-console-session) +and the basics of interacting with your GitLab instance from the command line. + +CAUTION: **CAUTION:** +The Rails console interacts directly with your GitLab instance. In many cases, +there are no handrails to prevent you from permanently modifying, corrupting +or destroying production data. If you would like to explore the Rails console +with no consequences, you are strongly advised to do so in a test environment. + +This guide is targeted at GitLab system administrators who are troubleshooting +a problem or need to retrieve some data that can only be done through direct +access of the GitLab application. Basic knowledge of Ruby is needed (try [this +30-minute tutorial](https://try.ruby-lang.org/) for a quick introduction). +Rails experience is helpful to have but not a must. + +## Starting a Rails console session + +Omnibus GitLab comes with a convenient wrapper command which automatically loads +the production GitLab environment: + +```shell +sudo gitlab-rails console +``` + +For source installations, you'll have to instead run: + +```shell +sudo -u git -H bundle exec rails console RAILS_ENV=production +``` + +Further code examples will all take place inside the Rails console and also +assume an Omnibus GitLab installation. + +## Active Record objects + +### Looking up database-persisted objects + +Under the hood, Rails uses [Active Record](https://guides.rubyonrails.org/active_record_basics.html), +an object-relational mapping system, to read, write and map application objects +to the PostgreSQL database. These mappings are handled by Active Record models, +which are Ruby classes defined in a Rails app. For GitLab, the model classes +can be found at `/opt/gitlab/embedded/service/gitlab-rails/app/models`. + +Let's enable debug logging for Active Record so we can see the underlying +database queries made: + +```ruby +ActiveRecord::Base.logger = Logger.new(STDOUT) +``` + +Now, let's try retrieving a user from the database: + +```ruby +user = User.find(1) +``` + +Which would return: + +```ruby +D, [2020-03-05T16:46:25.571238 #910] DEBUG -- : User Load (1.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 LIMIT 1 +=> #<User id:1 @root> +``` + +We can see that we've queried the `users` table in the database for a row whose +`id` column has the value `1`, and Active Record has translated that database +record into a Ruby object that we can interact with. Try some of the following: + +- `user.username` +- `user.created_at` +- `user.admin` + +By convention, column names are directly translated into Ruby object attributes, +so you should be able to do `user.<column_name>` to view the attribute's value. + +Also by convention, Active Record class names (singular and in camel case) map +directly onto table names (plural and in snake case) and vice versa. For example, +the `users` table maps to the `User` class, while the `application_settings` +table maps to the `ApplicationSetting` class. + +You can find a list of tables and column names in the Rails database schema, +available at `/opt/gitlab/embedded/service/gitlab-rails/db/schema.rb`. + +You can also look up an object from the database by attribute name: + +```ruby +user = User.find_by(username: 'root') +``` + +Which would return: + +```ruby +D, [2020-03-05T17:03:24.696493 #910] DEBUG -- : User Load (2.1ms) SELECT "users".* FROM "users" WHERE "users"."username" = 'root' LIMIT 1 +=> #<User id:1 @root> +``` + +Give the following a try: + +- `User.find_by(email: 'admin@example.com')` +- `User.where.not(admin: true)` +- `User.where('created_at < ?', 7.days.ago)` + +Did you notice that the last two commands returned an `ActiveRecord::Relation` +object that appeared to contain multiple `User` objects? + +Up to now, we've been using `.find` or `.find_by`, which are designed to return +only a single object (notice the `LIMIT 1` in the generated SQL query?). +`.where` is used when it is desirable to get a collection of objects. + +Let's get a collection of non-admin users and see what we can do with it: + +```ruby +users = User.where.not(admin: true) +``` + +Which would return: + +```ruby +D, [2020-03-05T17:11:16.845387 #910] DEBUG -- : User Load (2.8ms) SELECT "users".* FROM "users" WHERE "users"."admin" != TRUE LIMIT 11 +=> #<ActiveRecord::Relation [#<User id:3 @support-bot>, #<User id:7 @alert-bot>, #<User id:5 @carrie>, #<User id:4 @bernice>, #<User id:2 @anne>]> +``` + +Now, try the following: + +- `users.count` +- `users.order(created_at: :desc)` +- `users.where(username: 'support-bot')` + +In the last command, we see that we can chain `.where` statements to generate +more complex queries. Notice also that while the collection returned contains +only a single object, we cannot directly interact with it: + +```ruby +users.where(username: 'support-bot').username +``` + +Which would return: + +```ruby +Traceback (most recent call last): + 1: from (irb):37 +D, [2020-03-05T17:18:25.637607 #910] DEBUG -- : User Load (1.6ms) SELECT "users".* FROM "users" WHERE "users"."admin" != TRUE AND "users"."username" = 'support-bot' LIMIT 11 +NoMethodError (undefined method `username' for #<ActiveRecord::Relation [#<User id:3 @support-bot>]>) +Did you mean? by_username +``` + +We need to retrieve the single object from the collection by using the `.first` +method to get the first item in the collection: + +```ruby +users.where(username: 'support-bot').first.username +``` + +We now get the result we wanted: + +```ruby +D, [2020-03-05T17:18:30.406047 #910] DEBUG -- : User Load (2.6ms) SELECT "users".* FROM "users" WHERE "users"."admin" != TRUE AND "users"."username" = 'support-bot' ORDER BY "users"."id" ASC LIMIT 1 +=> "support-bot" +``` + +For more on different ways to retrieve data from the database using Active +Record, please see the [Active Record Query Interface documentation](https://guides.rubyonrails.org/active_record_querying.html). + +### Modifying Active Record objects + +In the previous section, we learned about retrieving database records using +Active Record. Now, we'll learn how to write changes to the database. + +First, let's retrieve the `root` user: + +```ruby +user = User.find_by(username: 'root') +``` + +Next, let's try updating the user's password: + +```ruby +user.password = 'password' +user.save +``` + +Which would return: + +```ruby +Enqueued ActionMailer::DeliveryJob (Job ID: 05915c4e-c849-4e14-80bb-696d5ae22065) to Sidekiq(mailers) with arguments: "DeviseMailer", "password_change", "deliver_now", #<GlobalID:0x00007f42d8ccebe8 @uri=#<URI::GID gid://gitlab/User/1>> +=> true +``` + +Here, we see that the `.save` command returned `true`, indicating that the +password change was successfully saved to the database. + +We also see that the save operation triggered some other action -- in this case +a background job to deliver an email notification. This is an example of an +[Active Record callback](https://guides.rubyonrails.org/active_record_callbacks.html) +-- code which is designated to run in response to events in the Active Record +object life cycle. This is also why using the Rails console is preferred when +direct changes to data is necessary as changes made via direct database queries +will not trigger these callbacks. + +It's also possible to update attributes in a single line: + +```ruby +user.update(password: 'password') +``` + +Or update multiple attributes at once: + +```ruby +user.update(password: 'password', email: 'hunter2@example.com') +``` + +Now, let's try something different: + +```ruby +# Retrieve the object again so we get its latest state +user = User.find_by(username: 'root') +user.password = 'password' +user.password_confirmation = 'hunter2' +user.save +``` + +This returns `false`, indicating that the changes we made were not saved to the +database. You can probably guess why, but let's find out for sure: + +```ruby +user.save! +``` + +This should return: + +```ruby +Traceback (most recent call last): + 1: from (irb):64 +ActiveRecord::RecordInvalid (Validation failed: Password confirmation doesn't match Password) +``` + +Aha! We've tripped an [Active Record Validation](https://guides.rubyonrails.org/active_record_validations.html). +Validations are business logic put in place at the application-level to prevent +unwanted data from being saved to the database and in most cases come with +helpful messages letting you know how to fix the problem inputs. + +We can also add the bang (Ruby speak for `!`) to `.update`: + +```ruby +user.update!(password: 'password', password_confirmation: 'hunter2') +``` + +In Ruby, method names ending with `!` are commonly known as "bang methods". By +convention, the bang indicates that the method directly modifies the object it +is acting on, as opposed to returning the transformed result and leaving the +underlying object untouched. For Active Record methods that write to the +database, bang methods also serve an additional function: they raise an +explicit exception whenever an error occurs, instead of just returning `false`. + +We can also skip validations entirely: + +```ruby +# Retrieve the object again so we get its latest state +user = User.find_by(username: 'root') +user.password = 'password' +user.password_confirmation = 'hunter2' +user.save!(validate: false) +``` + +This is not recommended, as validations are usually put in place to ensure the +integrity and consistency of user-provided data. + +Note that a validation error will prevent the entire object from being saved to +the database. We'll see a little of this in the next section. If you're getting +a mysterious red banner in the GitLab UI when submitting a form, this can often +be the fastest way to get to the root of the problem. + +### Interacting with Active Record objects + +At the end of the day, Active Record objects are just normal Ruby objects. As +such, we can define methods on them which perform arbitrary actions. + +For example, GitLab developers have added some methods which help with +two-factor authentication: + +```ruby +def disable_two_factor! + transaction do + update( + otp_required_for_login: false, + encrypted_otp_secret: nil, + encrypted_otp_secret_iv: nil, + encrypted_otp_secret_salt: nil, + otp_grace_period_started_at: nil, + otp_backup_codes: nil + ) + self.u2f_registrations.destroy_all # rubocop: disable DestroyAll + end +end + +def two_factor_enabled? + two_factor_otp_enabled? || two_factor_u2f_enabled? +end +``` + +(See: `/opt/gitlab/embedded/service/gitlab-rails/app/models/user.rb`) + +We can then use these methods on any user object: + +```ruby +user = User.find_by(username: 'root') +user.two_factor_enabled? +user.disable_two_factor! +``` + +Some methods are defined by gems, or Ruby software packages, which GitLab uses. +For example, the [StateMachines](https://github.com/state-machines/state_machines-activerecord) +gem which GitLab uses to manage user state: + +```ruby +state_machine :state, initial: :active do + event :block do + + ... + + event :activate do + + ... + +end +``` + +Give it a try: + +```ruby +user = User.find_by(username: 'root') +user.state +user.block +user.state +user.activate +user.state +``` + +Earlier, we mentioned that a validation error will prevent the entire object +from being saved to the database. Let's see how this can have unexpected +interactions: + +```ruby +user.password = 'password' +user.password_confirmation = 'hunter2' +user.block +``` + +We get `false` returned! Let's find out what happened by adding a bang as we did +earlier: + +```ruby +user.block! +``` + +Which would return: + +```ruby +Traceback (most recent call last): + 1: from (irb):87 +StateMachines::InvalidTransition (Cannot transition state via :block from :active (Reason(s): Password confirmation doesn't match Password)) +``` + +We see that a validation error from what feels like a completely separate +attribute comes back to haunt us when we try to update the user in any way. + +In practical terms, we sometimes see this happen with GitLab admin settings -- +validations are sometimes added or changed in a GitLab update, resulting in +previously saved settings now failing validation. Because you can only update +a subset of settings at once through the UI, in this case the only way to get +back to a good state is direct manipulation via Rails console. + +### Commonly used Active Record models and how to look up objects + +**Get a user by primary email address or username:** + +```ruby +User.find_by(email: 'admin@example.com') +User.find_by(username: 'root') +``` + +**Get a user by primary OR secondary email address:** + +```ruby +User.find_by_any_email('user@example.com') +``` + +Note: `find_by_any_email` is a custom method added by GitLab developers rather +than a Rails-provided default method. + +**Get a collection of admin users:** + +```ruby +User.admins +``` + +Note: `admins` is a [scope convenience method](https://guides.rubyonrails.org/active_record_querying.html#scopes) +which does `where(admin: true)` under the hood. + +**Get a project by its path:** + +```ruby +Project.find_by_full_path('group/subgroup/project') +``` + +Note: `find_by_full_path` is a custom method added by GitLab developers rather +than a Rails-provided default method. + +**Get a project's issue or merge request by its numeric ID:** + +```ruby +project = Project.find_by_full_path('group/subgroup/project') +project.issues.find_by(iid: 42) +project.merge_requests.find_by(iid: 42) +``` + +Note: `iid` means "internal ID" and is how we keep issue and merge request IDs +scoped to each GitLab project. + +**Get a group by its path:** + +```ruby +Group.find_by_full_path('group/subgroup') +``` + +**Get a group's related groups:** + +```ruby +group = Group.find_by_full_path('group/subgroup') + +# Get a group's parent group +group.parent + +# Get a group's child groups +group.children +``` + +**Get a group's projects:** + +```ruby +group = Group.find_by_full_path('group/subgroup') + +# Get group's immediate child projects +group.projects + +# Get group's child projects, including those in sub-groups +group.all_projects +``` + +**Get CI pipeline or builds:** + +```ruby +Ci::Pipeline.find(4151) +Ci::Build.find(66124) +``` + +Note: The pipeline and job #ID numbers increment globally across your GitLab +instance, so there's no need to use an internal ID attribute to look them up, +unlike with issues or merge requests. + +**Get the current application settings object:** + +```ruby +ApplicationSetting.current +``` diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql index e3f988016fe..4c5bce005d5 100644 --- a/doc/api/graphql/reference/gitlab_schema.graphql +++ b/doc/api/graphql/reference/gitlab_schema.graphql @@ -776,11 +776,16 @@ type Design implements DesignFields & Noteable { id: ID! """ - The URL of the image + The URL of the full-sized image """ image: String! """ + The URL of the design resized to fit within the bounds of 432x230. This will be `null` if the image has not been generated + """ + imageV432x230: String + + """ The issue the design belongs to """ issue: Issue! @@ -891,11 +896,16 @@ type DesignAtVersion implements DesignFields { id: ID! """ - The URL of the image + The URL of the full-sized image """ image: String! """ + The URL of the design resized to fit within the bounds of 432x230. This will be `null` if the image has not been generated + """ + imageV432x230: String + + """ The issue the design belongs to """ issue: Issue! @@ -1144,11 +1154,16 @@ interface DesignFields { id: ID! """ - The URL of the image + The URL of the full-sized image """ image: String! """ + The URL of the design resized to fit within the bounds of 432x230. This will be `null` if the image has not been generated + """ + imageV432x230: String + + """ The issue the design belongs to """ issue: Issue! diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json index 3d941d9cc69..94c0c17d218 100644 --- a/doc/api/graphql/reference/gitlab_schema.json +++ b/doc/api/graphql/reference/gitlab_schema.json @@ -2247,7 +2247,7 @@ }, { "name": "image", - "description": "The URL of the image", + "description": "The URL of the full-sized image", "args": [ ], @@ -2264,6 +2264,20 @@ "deprecationReason": null }, { + "name": "imageV432x230", + "description": "The URL of the design resized to fit within the bounds of 432x230. This will be `null` if the image has not been generated", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "issue", "description": "The issue the design belongs to", "args": [ @@ -2583,7 +2597,7 @@ }, { "name": "image", - "description": "The URL of the image", + "description": "The URL of the full-sized image", "args": [ ], @@ -2600,6 +2614,20 @@ "deprecationReason": null }, { + "name": "imageV432x230", + "description": "The URL of the design resized to fit within the bounds of 432x230. This will be `null` if the image has not been generated", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "issue", "description": "The issue the design belongs to", "args": [ @@ -3326,7 +3354,7 @@ }, { "name": "image", - "description": "The URL of the image", + "description": "The URL of the full-sized image", "args": [ ], @@ -3343,6 +3371,20 @@ "deprecationReason": null }, { + "name": "imageV432x230", + "description": "The URL of the design resized to fit within the bounds of 432x230. This will be `null` if the image has not been generated", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "issue", "description": "The issue the design belongs to", "args": [ diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 38067b275d5..a5eeab127d0 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -170,7 +170,8 @@ A single design | `filename` | String! | The filename of the design | | `fullPath` | String! | The full path to the design file | | `id` | ID! | The ID of this design | -| `image` | String! | The URL of the image | +| `image` | String! | The URL of the full-sized image | +| `imageV432x230` | String | The URL of the design resized to fit within the bounds of 432x230. This will be `null` if the image has not been generated | | `issue` | Issue! | The issue the design belongs to | | `notesCount` | Int! | The total count of user-created notes for this design | | `project` | Project! | The project the design belongs to | @@ -187,7 +188,8 @@ A design pinned to a specific version. The image field reflects the design as of | `filename` | String! | The filename of the design | | `fullPath` | String! | The full path to the design file | | `id` | ID! | The ID of this design | -| `image` | String! | The URL of the image | +| `image` | String! | The URL of the full-sized image | +| `imageV432x230` | String | The URL of the design resized to fit within the bounds of 432x230. This will be `null` if the image has not been generated | | `issue` | Issue! | The issue the design belongs to | | `notesCount` | Int! | The total count of user-created notes for this design | | `project` | Project! | The project the design belongs to | diff --git a/doc/development/contributing/style_guides.md b/doc/development/contributing/style_guides.md index 770726f4563..9a0b654f47f 100644 --- a/doc/development/contributing/style_guides.md +++ b/doc/development/contributing/style_guides.md @@ -17,7 +17,7 @@ static analysis offenses before committing locally. In your GitLab source directory run: ```shell -cd tooling/overcommit && make && cd - +make -C tooling/overcommit ``` Then before a commit is created, Overcommit will automatically check for diff --git a/lib/api/helpers/custom_validators.rb b/lib/api/helpers/custom_validators.rb index 4c15c1d01cd..b4523d7b436 100644 --- a/lib/api/helpers/custom_validators.rb +++ b/lib/api/helpers/custom_validators.rb @@ -56,6 +56,35 @@ module API message: "should be an array, 'None' or 'Any'" end end + + class GitRef < Grape::Validations::Base + # There are few checks that a Git reference should pass through to be valid reference. + # The link contains some rules that have been added to this validator. + # https://mirrors.edge.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html + # We have skipped some checks that are optional and can be skipped for exception. + # We also check for control characters, More info on ctrl chars - https://ruby-doc.org/core-2.7.0/Regexp.html#class-Regexp-label-Character+Classes + INVALID_CHARS = Regexp.union('..', '\\', '@', '@{', ' ', '~', '^', ':', '*', '?', '[', /[[:cntrl:]]/).freeze + GIT_REF_LENGTH = (1..1024).freeze + + def validate_param!(attr_name, params) + revision = params[attr_name] + + return unless invalid_character?(revision) + + raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], + message: 'should be a valid reference path' + end + + private + + def invalid_character?(revision) + revision.nil? || + revision.start_with?('-') || + revision.end_with?('.') || + GIT_REF_LENGTH.exclude?(revision.length) || + INVALID_CHARS.match?(revision) + end + end end end end @@ -65,3 +94,4 @@ Grape::Validations.register_validator(:git_sha, ::API::Helpers::CustomValidators Grape::Validations.register_validator(:absence, ::API::Helpers::CustomValidators::Absence) Grape::Validations.register_validator(:integer_none_any, ::API::Helpers::CustomValidators::IntegerNoneAny) Grape::Validations.register_validator(:array_none_any, ::API::Helpers::CustomValidators::ArrayNoneAny) +Grape::Validations.register_validator(:git_ref, ::API::Helpers::CustomValidators::GitRef) diff --git a/lib/gitlab/graphql/docs/helper.rb b/lib/gitlab/graphql/docs/helper.rb index 0dd28b32511..46f253e08e8 100644 --- a/lib/gitlab/graphql/docs/helper.rb +++ b/lib/gitlab/graphql/docs/helper.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -return if Rails.env.production? - module Gitlab module Graphql module Docs diff --git a/lib/gitlab/graphql/docs/renderer.rb b/lib/gitlab/graphql/docs/renderer.rb index 6abd56c89c6..fe950de7d13 100644 --- a/lib/gitlab/graphql/docs/renderer.rb +++ b/lib/gitlab/graphql/docs/renderer.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -return if Rails.env.production? +require 'gitlab/graphql/docs/helper' module Gitlab module Graphql diff --git a/lib/gitlab/import_export/relation_tree_restorer.rb b/lib/gitlab/import_export/relation_tree_restorer.rb index 466cb03862e..88cf346d8ec 100644 --- a/lib/gitlab/import_export/relation_tree_restorer.rb +++ b/lib/gitlab/import_export/relation_tree_restorer.rb @@ -28,7 +28,7 @@ module Gitlab update_params! bulk_inserts_enabled = @importable.class == ::Project && - Feature.enabled?(:import_bulk_inserts, @importable.group) + Feature.enabled?(:import_bulk_inserts, @importable.group, default_enabled: true) BulkInsertableAssociations.with_bulk_insert(enabled: bulk_inserts_enabled) do fix_ci_pipelines_not_sorted_on_legacy_project_json! create_relations! diff --git a/lib/gitlab/metrics/exporter/base_exporter.rb b/lib/gitlab/metrics/exporter/base_exporter.rb index 7111835c85a..ff8b8bf2237 100644 --- a/lib/gitlab/metrics/exporter/base_exporter.rb +++ b/lib/gitlab/metrics/exporter/base_exporter.rb @@ -1,5 +1,8 @@ # frozen_string_literal: true +require 'webrick' +require 'prometheus/client/rack/exporter' + module Gitlab module Metrics module Exporter diff --git a/lib/gitlab/set_cache.rb b/lib/gitlab/set_cache.rb index 2e72855824a..d1151a431bb 100644 --- a/lib/gitlab/set_cache.rb +++ b/lib/gitlab/set_cache.rb @@ -14,7 +14,10 @@ module Gitlab "#{key}:set" end + # Returns the number of keys deleted by Redis def expire(*keys) + return 0 if keys.empty? + with do |redis| keys = keys.map { |key| cache_key(key) } unlink_or_delete(redis, keys) diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb index b9cd4d74914..4457dffd603 100644 --- a/lib/gitlab/usage_data.rb +++ b/lib/gitlab/usage_data.rb @@ -98,7 +98,7 @@ module Gitlab projects_imported_from_github: count(Project.where(import_type: 'github')), projects_with_repositories_enabled: count(ProjectFeature.where('repository_access_level > ?', ProjectFeature::DISABLED)), projects_with_error_tracking_enabled: count(::ErrorTracking::ProjectErrorTrackingSetting.where(enabled: true)), - projects_with_alerts_service_enabled: count(AlertsService.active, batch: false), + projects_with_alerts_service_enabled: count(AlertsService.active), protected_branches: count(ProtectedBranch), releases: count(Release), remote_mirrors: count(RemoteMirror), diff --git a/lib/tasks/gitlab/graphql.rake b/lib/tasks/gitlab/graphql.rake index 5a583183924..4e7d462e850 100644 --- a/lib/tasks/gitlab/graphql.rake +++ b/lib/tasks/gitlab/graphql.rake @@ -3,6 +3,7 @@ return if Rails.env.production? require 'graphql/rake_task' +require 'gitlab/graphql/docs/renderer' namespace :gitlab do OUTPUT_DIR = Rails.root.join("doc/api/graphql/reference") diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 16a07a52d62..97cbe7faeb4 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -2247,6 +2247,9 @@ msgstr "" msgid "Archive project" msgstr "" +msgid "Archived" +msgstr "" + msgid "Archived project! Repository and other project resources are read only" msgstr "" @@ -8776,6 +8779,9 @@ msgstr "" msgid "Filter" msgstr "" +msgid "Filter by %{issuable_type} that are currently archived." +msgstr "" + msgid "Filter by %{issuable_type} that are currently closed." msgstr "" @@ -13112,6 +13118,9 @@ msgstr "" msgid "New release" msgstr "" +msgid "New requirement" +msgstr "" + msgid "New runners registration token has been generated!" msgstr "" diff --git a/package.json b/package.json index c699da02441..11e5da73c23 100644 --- a/package.json +++ b/package.json @@ -146,7 +146,7 @@ }, "devDependencies": { "@babel/plugin-transform-modules-commonjs": "^7.8.3", - "@gitlab/eslint-config": "^3.0.0", + "@gitlab/eslint-plugin": "^2.0.0", "@vue/test-utils": "^1.0.0-beta.30", "axios-mock-adapter": "^1.15.0", "babel-jest": "^24.1.0", @@ -215,4 +215,4 @@ "node": ">=10.13.0", "yarn": "^1.10.0" } -}
\ No newline at end of file +} diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb index 9c99f3ee377..2cb6a76b6b3 100644 --- a/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb +++ b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb @@ -17,7 +17,7 @@ module QA end end - context 'when using attachments in comments', :object_storage, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/issues/205408', type: :flaky } do + context 'when using attachments in comments', :object_storage, quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/issues/205408', type: :bug } do let(:gif_file_name) { 'banana_sample.gif' } let(:file_to_attach) do File.absolute_path(File.join('spec', 'fixtures', gif_file_name)) diff --git a/scripts/static-analysis b/scripts/static-analysis index ede29b85b8d..c07923cd837 100755 --- a/scripts/static-analysis +++ b/scripts/static-analysis @@ -1,7 +1,6 @@ #!/usr/bin/env ruby # We don't have auto-loading here -require_relative '../lib/gitlab' require_relative '../lib/gitlab/popen' require_relative '../lib/gitlab/popen/runner' diff --git a/spec/factories/usage_data.rb b/spec/factories/usage_data.rb new file mode 100644 index 00000000000..9560e076ae4 --- /dev/null +++ b/spec/factories/usage_data.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :usage_data, class: 'Gitlab::UsageData' do + skip_create # non-model factories (i.e. without #save) + + initialize_with do + projects = create_list(:project, 4) + create(:board, project: projects[0]) + create(:jira_service, project: projects[0]) + create(:jira_service, :without_properties_callback, project: projects[1]) + create(:jira_service, :jira_cloud_service, project: projects[2]) + create(:jira_service, :without_properties_callback, project: projects[3], + properties: { url: 'https://mysite.atlassian.net' }) + create(:prometheus_service, project: projects[1]) + create(:service, project: projects[0], type: 'SlackSlashCommandsService', active: true) + create(:service, project: projects[1], type: 'SlackService', active: true) + create(:service, project: projects[2], type: 'SlackService', active: true) + create(:service, project: projects[2], type: 'MattermostService', active: false) + create(:service, :template, type: 'MattermostService', active: true) + create(:service, project: projects[2], type: 'CustomIssueTrackerService', active: true) + create(:project_error_tracking_setting, project: projects[0]) + create(:project_error_tracking_setting, project: projects[1], enabled: false) + create(:alerts_service, project: projects[0]) + create(:alerts_service, :inactive, project: projects[1]) + create_list(:issue, 2, project: projects[0], author: User.alert_bot) + create_list(:issue, 2, project: projects[1], author: User.alert_bot) + create_list(:issue, 4, project: projects[0]) + create(:zoom_meeting, project: projects[0], issue: projects[0].issues[0], issue_status: :added) + create_list(:zoom_meeting, 2, project: projects[0], issue: projects[0].issues[1], issue_status: :removed) + create(:zoom_meeting, project: projects[0], issue: projects[0].issues[2], issue_status: :added) + create_list(:zoom_meeting, 2, project: projects[0], issue: projects[0].issues[2], issue_status: :removed) + create(:sentry_issue, issue: projects[0].issues[0]) + + # Enabled clusters + gcp_cluster = create(:cluster_provider_gcp, :created).cluster + create(:cluster_provider_aws, :created) + create(:cluster_platform_kubernetes) + create(:cluster, :group) + + # Disabled clusters + create(:cluster, :disabled) + create(:cluster, :group, :disabled) + create(:cluster, :group, :disabled) + + # Applications + create(:clusters_applications_helm, :installed, cluster: gcp_cluster) + create(:clusters_applications_ingress, :installed, cluster: gcp_cluster) + create(:clusters_applications_cert_manager, :installed, cluster: gcp_cluster) + create(:clusters_applications_prometheus, :installed, cluster: gcp_cluster) + create(:clusters_applications_crossplane, :installed, cluster: gcp_cluster) + create(:clusters_applications_runner, :installed, cluster: gcp_cluster) + create(:clusters_applications_knative, :installed, cluster: gcp_cluster) + create(:clusters_applications_elastic_stack, :installed, cluster: gcp_cluster) + create(:clusters_applications_jupyter, :installed, cluster: gcp_cluster) + + create(:grafana_integration, project: projects[0], enabled: true) + create(:grafana_integration, project: projects[1], enabled: true) + create(:grafana_integration, project: projects[2], enabled: false) + + ProjectFeature.first.update_attribute('repository_access_level', 0) + end + end +end diff --git a/spec/fast_spec_helper.rb b/spec/fast_spec_helper.rb index 1a8af335244..366c201bfe3 100644 --- a/spec/fast_spec_helper.rb +++ b/spec/fast_spec_helper.rb @@ -11,6 +11,9 @@ require_relative '../config/settings' require_relative 'support/rspec' require 'active_support/all' -ActiveSupport::Dependencies.autoload_paths << 'lib' -ActiveSupport::Dependencies.autoload_paths << 'ee/lib' +unless ActiveSupport::Dependencies.autoload_paths.frozen? + ActiveSupport::Dependencies.autoload_paths << 'lib' + ActiveSupport::Dependencies.autoload_paths << 'ee/lib' +end + ActiveSupport::XmlMini.backend = 'Nokogiri' diff --git a/spec/frontend/snippets/components/snippet_header_spec.js b/spec/frontend/snippets/components/snippet_header_spec.js index 5cf20119189..c74f7615538 100644 --- a/spec/frontend/snippets/components/snippet_header_spec.js +++ b/spec/frontend/snippets/components/snippet_header_spec.js @@ -32,7 +32,7 @@ describe('Snippet header component', () => { const errorMsg = 'Foo bar'; const err = { message: errorMsg }; - const resolveMutate = jest.fn(() => Promise.resolve()); + const resolveMutate = jest.fn(() => Promise.resolve({ data: {} })); const rejectMutation = jest.fn(() => Promise.reject(err)); const mutationTypes = { diff --git a/spec/lib/api/helpers/custom_validators_spec.rb b/spec/lib/api/helpers/custom_validators_spec.rb index 66b86d0a055..a4f2cd3452c 100644 --- a/spec/lib/api/helpers/custom_validators_spec.rb +++ b/spec/lib/api/helpers/custom_validators_spec.rb @@ -61,6 +61,47 @@ describe API::Helpers::CustomValidators do end end + describe API::Helpers::CustomValidators::GitRef do + subject do + described_class.new(['test'], {}, false, scope.new) + end + + context 'valid revision param' do + it 'does not raise a validation error' do + expect_no_validation_error('test' => '4e963fe') + expect_no_validation_error('test' => 'foo/bar/baz') + expect_no_validation_error('test' => "heads/fu\303\237") + expect_no_validation_error('test' => 'a' * 1024) + end + end + + context "revision param contains invalid chars" do + it 'raises a validation error' do + expect_validation_error('test' => '-4e963fe') + expect_validation_error('test' => '4e963fe..ed4ef') + expect_validation_error('test' => '4e96\3fe') + expect_validation_error('test' => '4e96@3fe') + expect_validation_error('test' => '4e9@{63fe') + expect_validation_error('test' => '4e963 fe') + expect_validation_error('test' => '4e96~3fe') + expect_validation_error('test' => '^4e963fe') + expect_validation_error('test' => '4:e963fe') + expect_validation_error('test' => '4e963fe.') + expect_validation_error('test' => 'heads/foo..bar') + expect_validation_error('test' => 'foo/bar/.') + expect_validation_error('test' => 'heads/v@{ation') + expect_validation_error('test' => 'refs/heads/foo.') + expect_validation_error('test' => 'heads/foo\bar') + expect_validation_error('test' => 'heads/f[/bar') + expect_validation_error('test' => "heads/foo\t") + expect_validation_error('test' => "heads/foo\177") + expect_validation_error('test' => "#{'a' * 1025}") + expect_validation_error('test' => nil) + expect_validation_error('test' => '') + end + end + end + describe API::Helpers::CustomValidators::FilePath do subject do described_class.new(['test'], {}, false, scope.new) diff --git a/spec/lib/gitlab/fogbugz_import/importer_spec.rb b/spec/lib/gitlab/fogbugz_import/importer_spec.rb index 9e67047eeda..c78659ff49b 100644 --- a/spec/lib/gitlab/fogbugz_import/importer_spec.rb +++ b/spec/lib/gitlab/fogbugz_import/importer_spec.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'spec_helper' +require 'fogbugz' describe Gitlab::FogbugzImport::Importer do let(:project) { create(:project_empty_repo) } diff --git a/spec/lib/gitlab/graphql/docs/renderer_spec.rb b/spec/lib/gitlab/graphql/docs/renderer_spec.rb index 5ba70bb8f0a..c94fe059b92 100644 --- a/spec/lib/gitlab/graphql/docs/renderer_spec.rb +++ b/spec/lib/gitlab/graphql/docs/renderer_spec.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'spec_helper' +require 'gitlab/graphql/docs/renderer' describe Gitlab::Graphql::Docs::Renderer do describe '#contents' do diff --git a/spec/lib/gitlab/repository_set_cache_spec.rb b/spec/lib/gitlab/repository_set_cache_spec.rb index c5d95e53120..6221d6fb45f 100644 --- a/spec/lib/gitlab/repository_set_cache_spec.rb +++ b/spec/lib/gitlab/repository_set_cache_spec.rb @@ -88,6 +88,12 @@ describe Gitlab::RepositorySetCache, :clean_gitlab_redis_cache do end end + context 'no keys' do + let(:keys) { [] } + + it { is_expected.to eq(0) } + end + context "unlink isn't supported" do before do allow_any_instance_of(Redis).to receive(:unlink) { raise ::Redis::CommandError } diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index 21117f11f63..fb60ac955de 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -2,299 +2,127 @@ require 'spec_helper' -describe Gitlab::UsageData do - let(:projects) { create_list(:project, 4) } - let!(:board) { create(:board, project: projects[0]) } +describe Gitlab::UsageData, :aggregate_failures do + include UsageDataHelpers before do allow(ActiveRecord::Base.connection).to receive(:transaction_open?).and_return(false) end - [true, false].each do |usage_ping_batch_counter_on| - describe "when the feature flag usage_ping_batch_counter is set to #{usage_ping_batch_counter_on}" do + + shared_examples "usage data execution" do + describe '#data' do + let!(:ud) { build(:usage_data) } + before do - stub_feature_flags(usage_ping_batch_counter: usage_ping_batch_counter_on) + allow(Gitlab::GrafanaEmbedUsageData).to receive(:issue_count).and_return(2) end - describe '#data' do - before do - create(:jira_service, project: projects[0]) - create(:jira_service, :without_properties_callback, project: projects[1]) - create(:jira_service, :jira_cloud_service, project: projects[2]) - create(:jira_service, :without_properties_callback, project: projects[3], - properties: { url: 'https://mysite.atlassian.net' }) - create(:prometheus_service, project: projects[1]) - create(:service, project: projects[0], type: 'SlackSlashCommandsService', active: true) - create(:service, project: projects[1], type: 'SlackService', active: true) - create(:service, project: projects[2], type: 'SlackService', active: true) - create(:service, project: projects[2], type: 'MattermostService', active: false) - create(:service, :template, type: 'MattermostService', active: true) - create(:service, project: projects[2], type: 'CustomIssueTrackerService', active: true) - create(:project_error_tracking_setting, project: projects[0]) - create(:project_error_tracking_setting, project: projects[1], enabled: false) - create(:alerts_service, project: projects[0]) - create(:alerts_service, :inactive, project: projects[1]) - create_list(:issue, 2, project: projects[0], author: User.alert_bot) - create_list(:issue, 2, project: projects[1], author: User.alert_bot) - create_list(:issue, 4, project: projects[0]) - create(:zoom_meeting, project: projects[0], issue: projects[0].issues[0], issue_status: :added) - create_list(:zoom_meeting, 2, project: projects[0], issue: projects[0].issues[1], issue_status: :removed) - create(:zoom_meeting, project: projects[0], issue: projects[0].issues[2], issue_status: :added) - create_list(:zoom_meeting, 2, project: projects[0], issue: projects[0].issues[2], issue_status: :removed) - create(:sentry_issue, issue: projects[0].issues[0]) - - # Enabled clusters - gcp_cluster = create(:cluster_provider_gcp, :created).cluster - create(:cluster_provider_aws, :created) - create(:cluster_platform_kubernetes) - create(:cluster, :group) - - # Disabled clusters - create(:cluster, :disabled) - create(:cluster, :group, :disabled) - create(:cluster, :group, :disabled) - - # Applications - create(:clusters_applications_helm, :installed, cluster: gcp_cluster) - create(:clusters_applications_ingress, :installed, cluster: gcp_cluster) - create(:clusters_applications_cert_manager, :installed, cluster: gcp_cluster) - create(:clusters_applications_prometheus, :installed, cluster: gcp_cluster) - create(:clusters_applications_crossplane, :installed, cluster: gcp_cluster) - create(:clusters_applications_runner, :installed, cluster: gcp_cluster) - create(:clusters_applications_knative, :installed, cluster: gcp_cluster) - create(:clusters_applications_elastic_stack, :installed, cluster: gcp_cluster) - create(:clusters_applications_jupyter, :installed, cluster: gcp_cluster) - - create(:grafana_integration, project: projects[0], enabled: true) - create(:grafana_integration, project: projects[1], enabled: true) - create(:grafana_integration, project: projects[2], enabled: false) - - allow(Gitlab::GrafanaEmbedUsageData).to receive(:issue_count).and_return(2) - - ProjectFeature.first.update_attribute('repository_access_level', 0) - end + subject { described_class.data } - subject { described_class.data } - - it 'gathers usage data', :aggregate_failures do - expect(subject.keys).to include(*%i( - active_user_count - counts - recorded_at - edition - version - installation_type - uuid - hostname - mattermost_enabled - signup_enabled - ldap_enabled - gravatar_enabled - omniauth_enabled - reply_by_email_enabled - container_registry_enabled - dependency_proxy_enabled - gitlab_shared_runners_enabled - gitlab_pages - git - gitaly - database - avg_cycle_analytics - influxdb_metrics_enabled - prometheus_metrics_enabled - web_ide_clientside_preview_enabled - ingress_modsecurity_enabled - )) - end + it 'gathers usage data' do + expect(subject.keys).to include(*UsageDataHelpers::USAGE_DATA_KEYS) + end - it 'gathers usage counts' do - smau_keys = %i( - snippet_create - snippet_update - snippet_comment - merge_request_comment - merge_request_create - commit_comment - wiki_pages_create - wiki_pages_update - wiki_pages_delete - web_ide_views - web_ide_commits - web_ide_merge_requests - web_ide_previews - navbar_searches - cycle_analytics_views - productivity_analytics_views - source_code_pushes - ) + it 'gathers usage counts' do + count_data = subject[:counts] - expected_keys = %i( - assignee_lists - boards - ci_builds - ci_internal_pipelines - ci_external_pipelines - ci_pipeline_config_auto_devops - ci_pipeline_config_repository - ci_runners - ci_triggers - ci_pipeline_schedules - auto_devops_enabled - auto_devops_disabled - deploy_keys - deployments - successful_deployments - failed_deployments - environments - clusters - clusters_enabled - project_clusters_enabled - group_clusters_enabled - clusters_disabled - project_clusters_disabled - group_clusters_disabled - clusters_platforms_eks - clusters_platforms_gke - clusters_platforms_user - clusters_applications_helm - clusters_applications_ingress - clusters_applications_cert_managers - clusters_applications_prometheus - clusters_applications_crossplane - clusters_applications_runner - clusters_applications_knative - clusters_applications_elastic_stack - clusters_applications_jupyter - in_review_folder - grafana_integrated_projects - groups - issues - issues_created_from_gitlab_error_tracking_ui - issues_with_associated_zoom_link - issues_using_zoom_quick_actions - issues_with_embedded_grafana_charts_approx - incident_issues - keys - label_lists - labels - lfs_objects - merge_requests - milestone_lists - milestones - notes - pool_repositories - projects - projects_imported_from_github - projects_asana_active - projects_jira_active - projects_jira_server_active - projects_jira_cloud_active - projects_slack_notifications_active - projects_slack_slash_active - projects_slack_active - projects_slack_slash_commands_active - projects_custom_issue_tracker_active - projects_mattermost_active - projects_prometheus_active - projects_with_repositories_enabled - projects_with_error_tracking_enabled - projects_with_alerts_service_enabled - pages_domains - protected_branches - releases - remote_mirrors - snippets - suggestions - todos - uploads - web_hooks - ).push(*smau_keys) - - count_data = subject[:counts] - - expect(count_data[:boards]).to eq(1) - expect(count_data[:projects]).to eq(4) - expect(count_data.values_at(*smau_keys)).to all(be_an(Integer)) - expect(count_data.keys).to include(*expected_keys) - expect(expected_keys - count_data.keys).to be_empty - end + expect(count_data[:boards]).to eq(1) + expect(count_data[:projects]).to eq(4) + expect(count_data.values_at(*UsageDataHelpers::SMAU_KEYS)).to all(be_an(Integer)) + expect(count_data.keys).to include(*UsageDataHelpers::COUNTS_KEYS) + expect(UsageDataHelpers::COUNTS_KEYS - count_data.keys).to be_empty + end - it 'gathers projects data correctly', :aggregate_failures do - count_data = subject[:counts] - - expect(count_data[:projects]).to eq(4) - expect(count_data[:projects_asana_active]).to eq(0) - expect(count_data[:projects_prometheus_active]).to eq(1) - expect(count_data[:projects_jira_active]).to eq(4) - expect(count_data[:projects_jira_server_active]).to eq(2) - expect(count_data[:projects_jira_cloud_active]).to eq(2) - expect(count_data[:projects_slack_notifications_active]).to eq(2) - expect(count_data[:projects_slack_slash_active]).to eq(1) - expect(count_data[:projects_slack_active]).to eq(2) - expect(count_data[:projects_slack_slash_commands_active]).to eq(1) - expect(count_data[:projects_custom_issue_tracker_active]).to eq(1) - expect(count_data[:projects_mattermost_active]).to eq(0) - expect(count_data[:projects_with_repositories_enabled]).to eq(3) - expect(count_data[:projects_with_error_tracking_enabled]).to eq(1) - expect(count_data[:projects_with_alerts_service_enabled]).to eq(1) - expect(count_data[:issues_created_from_gitlab_error_tracking_ui]).to eq(1) - expect(count_data[:issues_with_associated_zoom_link]).to eq(2) - expect(count_data[:issues_using_zoom_quick_actions]).to eq(3) - expect(count_data[:issues_with_embedded_grafana_charts_approx]).to eq(2) - expect(count_data[:incident_issues]).to eq(4) - - expect(count_data[:clusters_enabled]).to eq(4) - expect(count_data[:project_clusters_enabled]).to eq(3) - expect(count_data[:group_clusters_enabled]).to eq(1) - expect(count_data[:clusters_disabled]).to eq(3) - expect(count_data[:project_clusters_disabled]).to eq(1) - expect(count_data[:group_clusters_disabled]).to eq(2) - expect(count_data[:group_clusters_enabled]).to eq(1) - expect(count_data[:clusters_platforms_eks]).to eq(1) - expect(count_data[:clusters_platforms_gke]).to eq(1) - expect(count_data[:clusters_platforms_user]).to eq(1) - expect(count_data[:clusters_applications_helm]).to eq(1) - expect(count_data[:clusters_applications_ingress]).to eq(1) - expect(count_data[:clusters_applications_cert_managers]).to eq(1) - expect(count_data[:clusters_applications_crossplane]).to eq(1) - expect(count_data[:clusters_applications_prometheus]).to eq(1) - expect(count_data[:clusters_applications_runner]).to eq(1) - expect(count_data[:clusters_applications_knative]).to eq(1) - expect(count_data[:clusters_applications_elastic_stack]).to eq(1) - expect(count_data[:grafana_integrated_projects]).to eq(2) - expect(count_data[:clusters_applications_jupyter]).to eq(1) - end + it 'gathers projects data correctly' do + count_data = subject[:counts] + + expect(count_data[:projects]).to eq(4) + expect(count_data[:projects_asana_active]).to eq(0) + expect(count_data[:projects_prometheus_active]).to eq(1) + expect(count_data[:projects_jira_active]).to eq(4) + expect(count_data[:projects_jira_server_active]).to eq(2) + expect(count_data[:projects_jira_cloud_active]).to eq(2) + expect(count_data[:projects_slack_notifications_active]).to eq(2) + expect(count_data[:projects_slack_slash_active]).to eq(1) + expect(count_data[:projects_slack_active]).to eq(2) + expect(count_data[:projects_slack_slash_commands_active]).to eq(1) + expect(count_data[:projects_custom_issue_tracker_active]).to eq(1) + expect(count_data[:projects_mattermost_active]).to eq(0) + expect(count_data[:projects_with_repositories_enabled]).to eq(3) + expect(count_data[:projects_with_error_tracking_enabled]).to eq(1) + expect(count_data[:projects_with_alerts_service_enabled]).to eq(1) + expect(count_data[:issues_created_from_gitlab_error_tracking_ui]).to eq(1) + expect(count_data[:issues_with_associated_zoom_link]).to eq(2) + expect(count_data[:issues_using_zoom_quick_actions]).to eq(3) + expect(count_data[:issues_with_embedded_grafana_charts_approx]).to eq(2) + expect(count_data[:incident_issues]).to eq(4) + + expect(count_data[:clusters_enabled]).to eq(4) + expect(count_data[:project_clusters_enabled]).to eq(3) + expect(count_data[:group_clusters_enabled]).to eq(1) + expect(count_data[:clusters_disabled]).to eq(3) + expect(count_data[:project_clusters_disabled]).to eq(1) + expect(count_data[:group_clusters_disabled]).to eq(2) + expect(count_data[:group_clusters_enabled]).to eq(1) + expect(count_data[:clusters_platforms_eks]).to eq(1) + expect(count_data[:clusters_platforms_gke]).to eq(1) + expect(count_data[:clusters_platforms_user]).to eq(1) + expect(count_data[:clusters_applications_helm]).to eq(1) + expect(count_data[:clusters_applications_ingress]).to eq(1) + expect(count_data[:clusters_applications_cert_managers]).to eq(1) + expect(count_data[:clusters_applications_crossplane]).to eq(1) + expect(count_data[:clusters_applications_prometheus]).to eq(1) + expect(count_data[:clusters_applications_runner]).to eq(1) + expect(count_data[:clusters_applications_knative]).to eq(1) + expect(count_data[:clusters_applications_elastic_stack]).to eq(1) + expect(count_data[:grafana_integrated_projects]).to eq(2) + expect(count_data[:clusters_applications_jupyter]).to eq(1) + end - it 'works when queries time out' do - allow_any_instance_of(ActiveRecord::Relation) - .to receive(:count).and_raise(ActiveRecord::StatementInvalid.new('')) + it 'works when queries time out' do + allow_any_instance_of(ActiveRecord::Relation) + .to receive(:count).and_raise(ActiveRecord::StatementInvalid.new('')) - expect { subject }.not_to raise_error - end + expect { subject }.not_to raise_error end + end - describe '#usage_data_counters' do - subject { described_class.usage_data_counters } + describe '#usage_data_counters' do + subject { described_class.usage_data_counters } - it { is_expected.to all(respond_to :totals) } + it { is_expected.to all(respond_to :totals) } - describe 'the results of calling #totals on all objects in the array' do - subject { described_class.usage_data_counters.map(&:totals) } + describe 'the results of calling #totals on all objects in the array' do + subject { described_class.usage_data_counters.map(&:totals) } - it { is_expected.to all(be_a Hash) } - it { is_expected.to all(have_attributes(keys: all(be_a Symbol), values: all(be_a Integer))) } - end + it { is_expected.to all(be_a Hash) } + it { is_expected.to all(have_attributes(keys: all(be_a Symbol), values: all(be_a Integer))) } + end - it 'does not have any conflicts' do - all_keys = subject.flat_map { |counter| counter.totals.keys } + it 'does not have any conflicts' do + all_keys = subject.flat_map { |counter| counter.totals.keys } - expect(all_keys.size).to eq all_keys.to_set.size - end + expect(all_keys.size).to eq all_keys.to_set.size end + end + describe '#license_usage_data' do + subject { described_class.license_usage_data } + + it 'gathers license data' do + expect(subject[:uuid]).to eq(Gitlab::CurrentSettings.uuid) + expect(subject[:version]).to eq(Gitlab::VERSION) + expect(subject[:installation_type]).to eq('gitlab-development-kit') + expect(subject[:active_user_count]).to eq(User.active.size) + expect(subject[:recorded_at]).to be_a(Time) + end + end + + context 'when not relying on database records' do describe '#features_usage_data_ce' do subject { described_class.features_usage_data_ce } - it 'gathers feature usage data', :aggregate_failures do + it 'gathers feature usage data' do expect(subject[:mattermost_enabled]).to eq(Gitlab.config.mattermost.enabled) expect(subject[:signup_enabled]).to eq(Gitlab::CurrentSettings.allow_signup?) expect(subject[:ldap_enabled]).to eq(Gitlab.config.ldap.enabled) @@ -311,7 +139,7 @@ describe Gitlab::UsageData do describe '#components_usage_data' do subject { described_class.components_usage_data } - it 'gathers components usage data', :aggregate_failures do + it 'gathers components usage data' do expect(subject[:gitlab_pages][:enabled]).to eq(Gitlab.config.pages.enabled) expect(subject[:gitlab_pages][:version]).to eq(Gitlab::Pages::VERSION) expect(subject[:git][:version]).to eq(Gitlab::Git.version) @@ -360,18 +188,6 @@ describe Gitlab::UsageData do end end - describe '#license_usage_data' do - subject { described_class.license_usage_data } - - it 'gathers license data', :aggregate_failures do - expect(subject[:uuid]).to eq(Gitlab::CurrentSettings.uuid) - expect(subject[:version]).to eq(Gitlab::VERSION) - expect(subject[:installation_type]).to eq('gitlab-development-kit') - expect(subject[:active_user_count]).to eq(User.active.count) - expect(subject[:recorded_at]).to be_a(Time) - end - end - describe '#count' do let(:relation) { double(:relation) } @@ -405,4 +221,20 @@ describe Gitlab::UsageData do end end end + + context 'when usage usage_ping_batch_counter is true' do + before do + stub_feature_flags(usage_ping_batch_counter: true) + end + + it_behaves_like 'usage data execution' + end + + context 'when usage usage_ping_batch_counter is false' do + before do + stub_feature_flags(usage_ping_batch_counter: false) + end + + it_behaves_like 'usage data execution' + end end diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index 66b298bb36f..6f12d72c723 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -313,19 +313,16 @@ describe Issue do end describe '#moved?' do - let(:issue) { create(:issue) } - - subject { issue.moved? } + context 'when issue has not been moved' do + subject { build_stubbed(:issue) } - context 'issue not moved' do - it { is_expected.to eq false } + it { is_expected.not_to be_moved } end - context 'issue already moved' do - let(:moved_to_issue) { create(:issue) } - let(:issue) { create(:issue, moved_to: moved_to_issue) } + context 'when issue has already been moved' do + subject { build_stubbed(:issue, moved_to: build_stubbed(:issue)) } - it { is_expected.to eq true } + it { is_expected.to be_moved } end end diff --git a/spec/services/service_response_spec.rb b/spec/services/service_response_spec.rb index a6567f52c6f..d477a99b609 100644 --- a/spec/services/service_response_spec.rb +++ b/spec/services/service_response_spec.rb @@ -1,8 +1,7 @@ # frozen_string_literal: true require 'fast_spec_helper' - -ActiveSupport::Dependencies.autoload_paths << 'app/services' +require_relative '../../app/services/service_response' describe ServiceResponse do describe '.success' do diff --git a/spec/support/helpers/usage_data_helpers.rb b/spec/support/helpers/usage_data_helpers.rb new file mode 100644 index 00000000000..0fa1c40bd67 --- /dev/null +++ b/spec/support/helpers/usage_data_helpers.rb @@ -0,0 +1,134 @@ +# frozen_string_literal: true + +module UsageDataHelpers + SMAU_KEYS = %i( + snippet_create + snippet_update + snippet_comment + merge_request_comment + merge_request_create + commit_comment + wiki_pages_create + wiki_pages_update + wiki_pages_delete + web_ide_views + web_ide_commits + web_ide_merge_requests + web_ide_previews + navbar_searches + cycle_analytics_views + productivity_analytics_views + source_code_pushes + ).freeze + + COUNTS_KEYS = %i( + assignee_lists + boards + ci_builds + ci_internal_pipelines + ci_external_pipelines + ci_pipeline_config_auto_devops + ci_pipeline_config_repository + ci_runners + ci_triggers + ci_pipeline_schedules + auto_devops_enabled + auto_devops_disabled + deploy_keys + deployments + successful_deployments + failed_deployments + environments + clusters + clusters_enabled + project_clusters_enabled + group_clusters_enabled + clusters_disabled + project_clusters_disabled + group_clusters_disabled + clusters_platforms_eks + clusters_platforms_gke + clusters_platforms_user + clusters_applications_helm + clusters_applications_ingress + clusters_applications_cert_managers + clusters_applications_prometheus + clusters_applications_crossplane + clusters_applications_runner + clusters_applications_knative + clusters_applications_elastic_stack + clusters_applications_jupyter + in_review_folder + grafana_integrated_projects + groups + issues + issues_created_from_gitlab_error_tracking_ui + issues_with_associated_zoom_link + issues_using_zoom_quick_actions + issues_with_embedded_grafana_charts_approx + incident_issues + keys + label_lists + labels + lfs_objects + merge_requests + milestone_lists + milestones + notes + pool_repositories + projects + projects_imported_from_github + projects_asana_active + projects_jira_active + projects_jira_server_active + projects_jira_cloud_active + projects_slack_notifications_active + projects_slack_slash_active + projects_slack_active + projects_slack_slash_commands_active + projects_custom_issue_tracker_active + projects_mattermost_active + projects_prometheus_active + projects_with_repositories_enabled + projects_with_error_tracking_enabled + projects_with_alerts_service_enabled + pages_domains + protected_branches + releases + remote_mirrors + snippets + suggestions + todos + uploads + web_hooks + ).push(*SMAU_KEYS) + + USAGE_DATA_KEYS = %i( + active_user_count + counts + recorded_at + edition + version + installation_type + uuid + hostname + mattermost_enabled + signup_enabled + ldap_enabled + gravatar_enabled + omniauth_enabled + reply_by_email_enabled + container_registry_enabled + dependency_proxy_enabled + gitlab_shared_runners_enabled + gitlab_pages + git + gitaly + database + avg_cycle_analytics + influxdb_metrics_enabled + prometheus_metrics_enabled + web_ide_clientside_preview_enabled + ingress_modsecurity_enabled + ).freeze +end diff --git a/tooling/overcommit/gems.rb b/tooling/overcommit/Gemfile index c563a23275a..5525662e43e 100644 --- a/tooling/overcommit/gems.rb +++ b/tooling/overcommit/Gemfile @@ -1,6 +1,6 @@ # frozen_string_literal: true -# Make sure to run `bundle install --gemfile=tooling/overcommit/gems.rb` when you update this file. +# Make sure to run `make` (in this directory) when you update this file. source 'https://rubygems.org' gem 'overcommit' diff --git a/tooling/overcommit/gems.locked b/tooling/overcommit/Gemfile.lock index 15b80426c03..15b80426c03 100644 --- a/tooling/overcommit/gems.locked +++ b/tooling/overcommit/Gemfile.lock diff --git a/yarn.lock b/yarn.lock index a443fb11287..7342879af10 100644 --- a/yarn.lock +++ b/yarn.lock @@ -766,13 +766,11 @@ resolved "https://registry.yarnpkg.com/@gitlab/at.js/-/at.js-1.5.5.tgz#5f6bfe6baaef360daa9b038fa78798d7a6a916b4" integrity sha512-282Dn3SPVsUHVDhMsXgfnv+Rzog0uxecjttxGRQvxh25es1+xvkGQFsvJfkSKJ3X1kHVkSjKf+Tt5Rra+Jhp9g== -"@gitlab/eslint-config@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@gitlab/eslint-config/-/eslint-config-3.0.0.tgz#9a93662ffefb7792d5d0d96d876c316f2c393315" - integrity sha512-wlMpcl4q4hTnvmdPB9yuD+ZIi39P2ZCSfp3LQYSvbSXCF3POfyjRRe4tK7cerKrawjmJmII8YvNnIg2Ip9eKnQ== +"@gitlab/eslint-plugin@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@gitlab/eslint-plugin/-/eslint-plugin-2.0.0.tgz#4eedd16cf95cf82dc359c1b220d4f5a08361df9c" + integrity sha512-ctmsGnCuokhfh/5goLdz3NdBIUpwTMkx/17QxxutxkWW7yOGMPIY8Na+WhjnUSdst8Wjwzexc+snbh5NMs8H/A== dependencies: - "@gitlab/eslint-plugin-i18n" "^1.1.0" - "@gitlab/eslint-plugin-vue-i18n" "^2.0.0" babel-eslint "^10.0.3" eslint-config-airbnb-base "^14.0.0" eslint-config-prettier "^6.10.0" @@ -780,20 +778,7 @@ eslint-plugin-filenames "^1.3.2" eslint-plugin-import "^2.20.1" eslint-plugin-promise "^4.2.1" - eslint-plugin-vue "^6.0.1" - -"@gitlab/eslint-plugin-i18n@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@gitlab/eslint-plugin-i18n/-/eslint-plugin-i18n-1.1.0.tgz#e494d599e644ce3a094ea85f87dbbda41a924c5e" - integrity sha512-Cwm7sLtQnUDqvxE9Ez8UMslyosPCpMVLxBnFb+2n6QcBZmXRao4aNSVRkmlsDZYgYegWhOGn3Qq3MLy4BSqauQ== - dependencies: - requireindex "~1.1.0" - -"@gitlab/eslint-plugin-vue-i18n@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@gitlab/eslint-plugin-vue-i18n/-/eslint-plugin-vue-i18n-2.0.0.tgz#5a01912d9d5a7524539d678f09cac6fa57d6d838" - integrity sha512-z72ysu5AgjL/oWPckvNrMWUnkcFJhtZT0oqeSgN97NyktrvYMMpAOjSSeu2Nv+HLTD5vk9w/fG3zskp/qgT+lw== - dependencies: + eslint-plugin-vue "^6.2.1" vue-eslint-parser "^7.0.0" "@gitlab/svgs@^1.113.0": @@ -4408,11 +4393,12 @@ eslint-plugin-promise@^4.2.1: resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a" integrity sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw== -eslint-plugin-vue@^6.0.1: - version "6.1.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-6.1.2.tgz#4b05c28c83c0ec912669b64dbd998bb8bf692ef6" - integrity sha512-M75oAB+2a/LNkLKRbeEaS07EjzjIUaV7/hYoHAfRFeeF8ZMmCbahUn8nQLsLP85mkar24+zDU3QW2iT1JRsACw== +eslint-plugin-vue@^6.2.1: + version "6.2.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-6.2.2.tgz#27fecd9a3a24789b0f111ecdd540a9e56198e0fe" + integrity sha512-Nhc+oVAHm0uz/PkJAWscwIT4ijTrK5fqNqz9QB1D35SbbuMG1uB6Yr5AJpvPSWg+WOw7nYNswerYh0kOk64gqQ== dependencies: + natural-compare "^1.4.0" semver "^5.6.0" vue-eslint-parser "^7.0.0" @@ -9872,11 +9858,6 @@ require-package-name@^2.0.1: resolved "https://registry.yarnpkg.com/require-package-name/-/require-package-name-2.0.1.tgz#c11e97276b65b8e2923f75dabf5fb2ef0c3841b9" integrity sha1-wR6XJ2tluOKSP3Xav1+y7ww4Qbk= -requireindex@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.1.0.tgz#e5404b81557ef75db6e49c5a72004893fe03e162" - integrity sha1-5UBLgVV+91225JxacgBIk/4D4WI= - requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" |