diff options
65 files changed, 719 insertions, 659 deletions
diff --git a/app/assets/javascripts/search/sidebar/components/app.vue b/app/assets/javascripts/search/sidebar/components/app.vue index 60de63c7d7a..4dd43487877 100644 --- a/app/assets/javascripts/search/sidebar/components/app.vue +++ b/app/assets/javascripts/search/sidebar/components/app.vue @@ -4,7 +4,7 @@ import ScopeNavigation from '~/search/sidebar/components/scope_navigation.vue'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { SCOPE_ISSUES, SCOPE_MERGE_REQUESTS, SCOPE_BLOB } from '../constants'; import ResultsFilters from './results_filters.vue'; -import LanguageFilter from './language_filter.vue'; +import LanguageFilter from './language_filter/index.vue'; export default { name: 'GlobalSearchSidebar', diff --git a/app/assets/javascripts/search/sidebar/components/checkbox_filter.vue b/app/assets/javascripts/search/sidebar/components/checkbox_filter.vue index f7873a994aa..08a943d58f8 100644 --- a/app/assets/javascripts/search/sidebar/components/checkbox_filter.vue +++ b/app/assets/javascripts/search/sidebar/components/checkbox_filter.vue @@ -1,10 +1,15 @@ <script> +import Vue from 'vue'; import { GlFormCheckboxGroup, GlFormCheckbox } from '@gitlab/ui'; import { mapState, mapActions, mapGetters } from 'vuex'; import { intersection } from 'lodash'; +import Tracking from '~/tracking'; import { NAV_LINK_COUNT_DEFAULT_CLASSES, LABEL_DEFAULT_CLASSES } from '../constants'; import { formatSearchResultCount } from '../../store/utils'; +export const TRACKING_LABEL_SET = 'set'; +export const TRACKING_LABEL_CHECKBOX = 'checkbox'; + export default { name: 'CheckboxFilter', components: { @@ -16,6 +21,10 @@ export default { type: Object, required: true, }, + trackingNamespace: { + type: String, + required: true, + }, }, computed: { ...mapState(['query']), @@ -30,8 +39,11 @@ export default { get() { return intersection(this.flatDataFilterValues, this.queryLanguageFilters); }, - set(value) { + async set(value) { this.setQuery({ key: this.filtersData?.filterParam, value }); + + await Vue.nextTick(); + this.trackSelectCheckbox(); }, }, labelCountClasses() { @@ -40,9 +52,15 @@ export default { }, methods: { ...mapActions(['setQuery']), - getFormatedCount(count) { + getFormattedCount(count) { return formatSearchResultCount(count); }, + trackSelectCheckbox() { + Tracking.event(this.trackingNamespace, TRACKING_LABEL_CHECKBOX, { + label: TRACKING_LABEL_SET, + property: this.selectedFilter, + }); + }, }, NAV_LINK_COUNT_DEFAULT_CLASSES, LABEL_DEFAULT_CLASSES, @@ -67,7 +85,7 @@ export default { {{ f.label }} </span> <span v-if="f.count" :class="labelCountClasses" data-testid="labelCount"> - {{ getFormatedCount(f.count) }} + {{ getFormattedCount(f.count) }} </span> </span> </gl-form-checkbox> diff --git a/app/assets/javascripts/search/sidebar/constants/language_filter_data.js b/app/assets/javascripts/search/sidebar/components/language_filter/data.js index df44a58a14b..df44a58a14b 100644 --- a/app/assets/javascripts/search/sidebar/constants/language_filter_data.js +++ b/app/assets/javascripts/search/sidebar/components/language_filter/data.js diff --git a/app/assets/javascripts/search/sidebar/components/language_filter.vue b/app/assets/javascripts/search/sidebar/components/language_filter/index.vue index b2f8d3e1f5f..4e3be9cdabc 100644 --- a/app/assets/javascripts/search/sidebar/components/language_filter.vue +++ b/app/assets/javascripts/search/sidebar/components/language_filter/index.vue @@ -3,10 +3,18 @@ import { GlButton, GlAlert, GlForm } from '@gitlab/ui'; import { mapState, mapActions, mapGetters } from 'vuex'; import { __, s__, sprintf } from '~/locale'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; -import { DEFAULT_ITEM_LENGTH, MAX_ITEM_LENGTH } from '../constants/language_filter_data'; -import { HR_DEFAULT_CLASSES, ONLY_SHOW_MD } from '../constants'; -import { convertFiltersData } from '../utils'; -import CheckboxFilter from './checkbox_filter.vue'; +import { HR_DEFAULT_CLASSES, ONLY_SHOW_MD } from '../../constants'; +import { convertFiltersData } from '../../utils'; +import CheckboxFilter from '../checkbox_filter.vue'; +import { + trackShowMore, + trackShowHasOverMax, + trackSubmitQuery, + trackResetQuery, + TRACKING_ACTION_SELECT, +} from './tracking'; + +import { DEFAULT_ITEM_LENGTH, MAX_ITEM_LENGTH } from './data'; export default { name: 'LanguageFilter', @@ -76,11 +84,21 @@ export default { ]), onShowMore() { this.showAll = true; + trackShowMore(); + + if (this.hasOverMax) { + trackShowHasOverMax(); + } + }, + submitQuery() { + trackSubmitQuery(); + this.applyQuery(); }, trimBuckets(length) { return this.languageAggregationBuckets.slice(0, length); }, cleanResetFilters() { + trackResetQuery(); if (this.currentUrlQueryHasLanguageFilters) { return this.resetLanguageQueryWithRedirect(); } @@ -89,6 +107,7 @@ export default { }, }, HR_DEFAULT_CLASSES, + TRACKING_ACTION_SELECT, }; </script> @@ -96,7 +115,7 @@ export default { <gl-form v-if="hasBuckets" class="gl-pt-5 gl-md-pt-0 language-filter-checkbox" - @submit.prevent="applyQuery" + @submit.prevent="submitQuery" > <hr :class="dividerClasses" /> <div @@ -104,7 +123,10 @@ export default { class="gl-overflow-x-hidden gl-overflow-y-auto" :class="{ 'language-filter-max-height': showAll }" > - <checkbox-filter :filters-data="filtersData" /> + <checkbox-filter + :filters-data="filtersData" + :tracking-namespace="$options.TRACKING_ACTION_SELECT" + /> <span v-if="showAll && hasOverMax" data-testid="has-over-max-text">{{ $options.i18n.showingMax }}</span> diff --git a/app/assets/javascripts/search/sidebar/components/language_filter/tracking.js b/app/assets/javascripts/search/sidebar/components/language_filter/tracking.js new file mode 100644 index 00000000000..db107830329 --- /dev/null +++ b/app/assets/javascripts/search/sidebar/components/language_filter/tracking.js @@ -0,0 +1,39 @@ +import Tracking from '~/tracking'; +import { MAX_ITEM_LENGTH } from './data'; + +export const TRACKING_CATEGORY = 'Language filters'; +export const TRACKING_LABEL_FILTERS = 'Filters'; + +export const TRACKING_LABEL_MAX = 'Max Shown'; +export const TRACKING_LABEL_SHOW_MORE = 'Show More'; +export const TRACKING_LABEL_APPLY = 'Apply Filters'; +export const TRACKING_LABEL_RESET = 'Reset Filters'; +export const TRACKING_LABEL_ALL = 'All Filters'; +export const TRACKING_PROPERTY_MAX = `More than ${MAX_ITEM_LENGTH} filters to show`; + +export const TRACKING_ACTION_CLICK = 'search:agreggations:language:click'; +export const TRACKING_ACTION_SHOW = 'search:agreggations:language:show'; + +// select is imported and used in checkbox_filter.vue +export const TRACKING_ACTION_SELECT = 'search:agreggations:language:select'; + +export const trackShowMore = () => + Tracking.event(TRACKING_ACTION_CLICK, TRACKING_LABEL_SHOW_MORE, { + label: TRACKING_LABEL_ALL, + }); + +export const trackShowHasOverMax = () => + Tracking.event(TRACKING_ACTION_SHOW, TRACKING_LABEL_FILTERS, { + label: TRACKING_LABEL_MAX, + property: TRACKING_PROPERTY_MAX, + }); + +export const trackSubmitQuery = () => + Tracking.event(TRACKING_ACTION_CLICK, TRACKING_LABEL_APPLY, { + label: TRACKING_CATEGORY, + }); + +export const trackResetQuery = () => + Tracking.event(TRACKING_ACTION_CLICK, TRACKING_LABEL_RESET, { + label: TRACKING_CATEGORY, + }); diff --git a/app/assets/javascripts/search/sidebar/constants/index.js b/app/assets/javascripts/search/sidebar/constants/index.js index 19b1ad0905b..395629dc70c 100644 --- a/app/assets/javascripts/search/sidebar/constants/index.js +++ b/app/assets/javascripts/search/sidebar/constants/index.js @@ -14,3 +14,5 @@ export const NAV_LINK_DEFAULT_CLASSES = [ export const NAV_LINK_COUNT_DEFAULT_CLASSES = ['gl-font-sm', 'gl-font-weight-normal']; export const HR_DEFAULT_CLASSES = ['gl-my-5', 'gl-mx-5', 'gl-border-gray-100']; export const ONLY_SHOW_MD = ['gl-display-none', 'gl-md-display-block']; + +export const TRACKING_LABEL_CHECKBOX = 'Checkbox'; diff --git a/app/assets/javascripts/search/sidebar/utils.js b/app/assets/javascripts/search/sidebar/utils.js index 4357d6202df..78e03fcdeee 100644 --- a/app/assets/javascripts/search/sidebar/utils.js +++ b/app/assets/javascripts/search/sidebar/utils.js @@ -1,4 +1,4 @@ -import { languageFilterData } from '~/search/sidebar/constants/language_filter_data'; +import { languageFilterData } from '~/search/sidebar/components/language_filter/data'; export const convertFiltersData = (rawBuckets) => rawBuckets.reduce( diff --git a/app/assets/javascripts/search/store/actions.js b/app/assets/javascripts/search/store/actions.js index da2bf4b602e..3d6ca2a6eee 100644 --- a/app/assets/javascripts/search/store/actions.js +++ b/app/assets/javascripts/search/store/actions.js @@ -4,7 +4,7 @@ import axios from '~/lib/utils/axios_utils'; import { visitUrl, setUrlParams } from '~/lib/utils/url_utility'; import { logError } from '~/lib/logger'; import { __ } from '~/locale'; -import { languageFilterData } from '~/search/sidebar/constants/language_filter_data'; +import { languageFilterData } from '~/search/sidebar/components/language_filter/data'; import { GROUPS_LOCAL_STORAGE_KEY, PROJECTS_LOCAL_STORAGE_KEY, SIDEBAR_PARAMS } from './constants'; import * as types from './mutation_types'; import { @@ -140,8 +140,7 @@ export const fetchLanguageAggregation = ({ commit, state }) => { commit(types.REQUEST_AGGREGATIONS); return axios .get(getAggregationsUrl()) - .then((result) => { - const { data } = result; + .then(({ data }) => { commit(types.RECEIVE_AGGREGATIONS_SUCCESS, prepareSearchAggregations(state, data)); }) .catch((e) => { diff --git a/app/assets/javascripts/search/store/constants.js b/app/assets/javascripts/search/store/constants.js index ba4fe85db9d..3f586c5fed8 100644 --- a/app/assets/javascripts/search/store/constants.js +++ b/app/assets/javascripts/search/store/constants.js @@ -1,6 +1,6 @@ import { stateFilterData } from '~/search/sidebar/constants/state_filter_data'; import { confidentialFilterData } from '~/search/sidebar/constants/confidential_filter_data'; -import { languageFilterData } from '~/search/sidebar/constants/language_filter_data'; +import { languageFilterData } from '~/search/sidebar/components/language_filter/data'; export const MAX_FREQUENT_ITEMS = 5; diff --git a/app/assets/javascripts/search/store/getters.js b/app/assets/javascripts/search/store/getters.js index 36d98233e28..0e387607af7 100644 --- a/app/assets/javascripts/search/store/getters.js +++ b/app/assets/javascripts/search/store/getters.js @@ -1,5 +1,5 @@ import { findKey, has } from 'lodash'; -import { languageFilterData } from '~/search/sidebar/constants/language_filter_data'; +import { languageFilterData } from '~/search/sidebar/components/language_filter/data'; import { GROUPS_LOCAL_STORAGE_KEY, PROJECTS_LOCAL_STORAGE_KEY } from './constants'; diff --git a/app/assets/javascripts/search/store/utils.js b/app/assets/javascripts/search/store/utils.js index 8e484e69646..9d1743e64ad 100644 --- a/app/assets/javascripts/search/store/utils.js +++ b/app/assets/javascripts/search/store/utils.js @@ -2,7 +2,7 @@ import { isEqual, orderBy } from 'lodash'; import AccessorUtilities from '~/lib/utils/accessor'; import { formatNumber } from '~/locale'; import { joinPaths } from '~/lib/utils/url_utility'; -import { languageFilterData } from '~/search/sidebar/constants/language_filter_data'; +import { languageFilterData } from '~/search/sidebar/components/language_filter/data'; import { MAX_FREQUENT_ITEMS, MAX_FREQUENCY, diff --git a/app/models/blob_viewer/composer_json.rb b/app/models/blob_viewer/composer_json.rb index 9d1376de0cb..aac7271242e 100644 --- a/app/models/blob_viewer/composer_json.rb +++ b/app/models/blob_viewer/composer_json.rb @@ -15,7 +15,7 @@ module BlobViewer end def package_name - @package_name ||= package_name_from_json('name') + @package_name ||= fetch_from_json('name') end def package_url diff --git a/app/models/blob_viewer/dependency_manager.rb b/app/models/blob_viewer/dependency_manager.rb index a3801025cd7..71bd90e7459 100644 --- a/app/models/blob_viewer/dependency_manager.rb +++ b/app/models/blob_viewer/dependency_manager.rb @@ -38,8 +38,10 @@ module BlobViewer end end - def package_name_from_json(key) - json_data[key] + def fetch_from_json(...) + json_data.dig(...) + rescue TypeError + nil end def package_name_from_method_call(name) diff --git a/app/models/blob_viewer/package_json.rb b/app/models/blob_viewer/package_json.rb index 1d10cc82a85..5350b6b0626 100644 --- a/app/models/blob_viewer/package_json.rb +++ b/app/models/blob_viewer/package_json.rb @@ -11,7 +11,7 @@ module BlobViewer end def yarn? - json_data['engines'].present? && json_data['engines']['yarn'].present? + fetch_from_json('engines', 'yarn').present? end def manager_url @@ -19,7 +19,7 @@ module BlobViewer end def package_name - @package_name ||= package_name_from_json('name') + @package_name ||= fetch_from_json('name') end def package_type @@ -33,11 +33,11 @@ module BlobViewer private def private? - !!json_data['private'] + !!fetch_from_json('private') end def homepage - url = json_data['homepage'] + url = fetch_from_json('homepage') url if Gitlab::UrlSanitizer.valid?(url) end diff --git a/app/models/blob_viewer/podspec_json.rb b/app/models/blob_viewer/podspec_json.rb index d3f6ae269da..d606f72376d 100644 --- a/app/models/blob_viewer/podspec_json.rb +++ b/app/models/blob_viewer/podspec_json.rb @@ -5,7 +5,7 @@ module BlobViewer self.file_types = %i(podspec_json) def package_name - @package_name ||= package_name_from_json('name') + @package_name ||= fetch_from_json('name') end end end diff --git a/app/views/admin/users/_profile.html.haml b/app/views/admin/users/_profile.html.haml index e90dab68b39..de48129dfe6 100644 --- a/app/views/admin/users/_profile.html.haml +++ b/app/views/admin/users/_profile.html.haml @@ -1,31 +1,32 @@ -.card - .card-header += render Pajamas::CardComponent.new(body_options: { class: 'gl-py-0' }) do |c| + - c.header do = _('Profile') - %ul.content-list - %li - %span.light= _('Member since') - %strong= user.created_at.to_s(:medium) - - unless user.public_email.blank? + - c.body do + %ul.content-list %li - %span.light= _('E-mail:') - %strong= link_to user.public_email, "mailto:#{user.public_email}" - - unless user.skype.blank? - %li - %span.light= _('Skype:') - %strong= link_to user.skype, "skype:#{user.skype}" - - unless user.linkedin.blank? - %li - %span.light= _('LinkedIn:') - %strong= link_to user.linkedin, "https://www.linkedin.com/in/#{user.linkedin}" - - unless user.twitter.blank? - %li - %span.light= _('Twitter:') - %strong= link_to user.twitter, "https://twitter.com/#{user.twitter}" - - unless user.website_url.blank? - %li - %span.light= _('Website:') - %strong= link_to user.short_website_url, user.full_website_url - - unless user.location.blank? - %li - %span.light= _('Location:') - %strong= user.location + %span.light= _('Member since') + %strong= user.created_at.to_s(:medium) + - unless user.public_email.blank? + %li + %span.light= _('E-mail:') + %strong= link_to user.public_email, "mailto:#{user.public_email}" + - unless user.skype.blank? + %li + %span.light= _('Skype:') + %strong= link_to user.skype, "skype:#{user.skype}" + - unless user.linkedin.blank? + %li + %span.light= _('LinkedIn:') + %strong= link_to user.linkedin, "https://www.linkedin.com/in/#{user.linkedin}" + - unless user.twitter.blank? + %li + %span.light= _('Twitter:') + %strong= link_to user.twitter, "https://twitter.com/#{user.twitter}" + - unless user.website_url.blank? + %li + %span.light= _('Website:') + %strong= link_to user.short_website_url, user.full_website_url + - unless user.location.blank? + %li + %span.light= _('Location:') + %strong= user.location diff --git a/config/feature_flags/development/git_abuse_rate_limit_feature_flag.yml b/config/feature_flags/development/git_abuse_rate_limit_feature_flag.yml deleted file mode 100644 index c71a5064b29..00000000000 --- a/config/feature_flags/development/git_abuse_rate_limit_feature_flag.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: git_abuse_rate_limit_feature_flag -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87872 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/364782 -milestone: '15.1' -type: development -group: group::anti-abuse -default_enabled: false diff --git a/config/initializers/net_http_response_patch.rb b/config/initializers/net_http_response_patch.rb index 4ffe227a3fd..4f3eaeec24a 100644 --- a/config/initializers/net_http_response_patch.rb +++ b/config/initializers/net_http_response_patch.rb @@ -24,10 +24,12 @@ module Net line = line.sub(/\s{0,10}\z/, '') break if line.empty? if line[0] == ?\s or line[0] == ?\t and value + # rubocop:disable Gitlab/NoCodeCoverageComment # :nocov: value << ' ' unless value.empty? value << line.strip # :nocov: + # rubocop:enable Gitlab/NoCodeCoverageComment else yield key, value if key key, value = line.strip.split(/\s{0,10}:\s{0,10}/, 2) diff --git a/data/deprecations/15-0-deprecate-postgresql-12.yml b/data/deprecations/15-0-deprecate-postgresql-12.yml index ec8e32f1c1d..21f0b3f0ee3 100644 --- a/data/deprecations/15-0-deprecate-postgresql-12.yml +++ b/data/deprecations/15-0-deprecate-postgresql-12.yml @@ -10,7 +10,7 @@ PostgreSQL 12 will be supported for the full GitLab 15 release cycle. PostgreSQL 13 will also be supported for instances that want to upgrade prior to GitLab 16.0. - Upgrading to PostgreSQL 13 is not yet supported for GitLab instances with Geo enabled. Geo support for PostgreSQL 13 will be announced in a minor release version of GitLab 15, after the process is fully supported and validated. For more information, read the Geo related verifications on the [support epic for PostgreSQL 13](https://gitlab.com/groups/gitlab-org/-/epics/3832). + Support for PostgreSQL 13 was added to Geo in GitLab 15.2. stage: Enablement tiers: [Free, Premium, Ultimate] issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/349185 diff --git a/data/deprecations/15-10-grafana-chart.yml b/data/deprecations/15-10-grafana-chart.yml new file mode 100644 index 00000000000..48070560bfb --- /dev/null +++ b/data/deprecations/15-10-grafana-chart.yml @@ -0,0 +1,37 @@ +# This is a template for announcing a feature deprecation or other important planned change. +# +# Please refer to the deprecation guidelines to confirm your understanding of GitLab's definitions. +# https://docs.gitlab.com/ee/development/deprecation_guidelines/#terminology +# +# Deprecations and other future breaking changes must be announced at least +# three releases prior to removal. +# +# Breaking changes must happen in a major release. +# +# See the OPTIONAL END OF SUPPORT FIELDS section below if an End of Support period also applies. +# +# For more information please refer to the handbook documentation here: +# https://about.gitlab.com/handbook/marketing/blog/release-posts/#deprecations-and-other-planned-breaking-change-announcements +# +# Please delete this line and above before submitting your merge request. +# +# REQUIRED FIELDS +# +- title: "Bundled Grafana Helm Chart is deprecated" # (required) Clearly explain the change, or planned change. For example, "The `confidential` field for a `Note` is deprecated" or "CI/CD job names will be limited to 250 characters." + announcement_milestone: "15.10" # (required) The milestone when this feature was first announced as deprecated. + removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed + breaking_change: true # (required) Change to false if this is not a breaking change. + reporter: twk3 # (required) GitLab username of the person reporting the change + stage: enablement # (required) String value of the stage that the feature was created in. e.g., Growth + issue_url: https://gitlab.com/gitlab-org/charts/gitlab/-/issues/4353 # (required) Link to the deprecation issue in GitLab + body: | # (required) Do not modify this line, instead modify the lines below. + The Grafana Helm chart that is bundled with the GitLab Helm Chart is deprecated and will be removed in the GitLab Helm Chart 7.0 release (releasing along with GitLab 16.0). + + The bundled Grafana Helm chart is an optional service that can be turned on to provide the Grafana UI connected to the GitLab Helm Chart's Prometheus metrics. + + The version of Grafana that the GitLab Helm Chart is currently providing is no longer a supported Grafana version. + If you're using the bundled Grafana, you should switch to the [newer chart version from Grafana Labs](https://artifacthub.io/packages/helm/grafana/grafana) + or a Grafana Operator from a trusted provider. + + In your new Grafana instance, you can [configure the GitLab provided Prometheus as a data source](https://docs.gitlab.com/ee/administration/monitoring/performance/grafana_configuration.html#integration-with-gitlab-ui) + and [connect Grafana to the GitLab UI](https://docs.gitlab.com/ee/administration/monitoring/performance/grafana_configuration.html#integration-with-gitlab-ui). diff --git a/data/deprecations/15-10-helm-chart-updates.yml b/data/deprecations/15-10-helm-chart-updates.yml new file mode 100644 index 00000000000..edfdf1b7ae0 --- /dev/null +++ b/data/deprecations/15-10-helm-chart-updates.yml @@ -0,0 +1,36 @@ +# This is a template for announcing a feature deprecation or other important planned change. +# +# Please refer to the deprecation guidelines to confirm your understanding of GitLab's definitions. +# https://docs.gitlab.com/ee/development/deprecation_guidelines/#terminology +# +# Deprecations and other future breaking changes must be announced at least +# three releases prior to removal. +# +# Breaking changes must happen in a major release. +# +# See the OPTIONAL END OF SUPPORT FIELDS section below if an End of Support period also applies. +# +# For more information please refer to the handbook documentation here: +# https://about.gitlab.com/handbook/marketing/blog/release-posts/#deprecations-and-other-planned-breaking-change-announcements +# +# Please delete this line and above before submitting your merge request. +# +# REQUIRED FIELDS +# +- title: "Major bundled Helm Chart updates for the GitLab Helm Chart" # (required) Clearly explain the change, or planned change. For example, "The `confidential` field for a `Note` is deprecated" or "CI/CD job names will be limited to 250 characters." + announcement_milestone: "15.10" # (required) The milestone when this feature was first announced as deprecated. + removal_milestone: "16.0" # (required) The milestone when this feature is planned to be removed + breaking_change: true # (required) Change to false if this is not a breaking change. + reporter: twk3 # (required) GitLab username of the person reporting the change + stage: enablement # (required) String value of the stage that the feature was created in. e.g., Growth + issue_url: https://gitlab.com/gitlab-org/charts/gitlab/-/issues/3442 # (required) Link to the deprecation issue in GitLab + body: | # (required) Do not modify this line, instead modify the lines below. + To coincide with GitLab 16.0, the GitLab Helm Chart will release the 7.0 major version. The following major bundled chart updates will be included: + + - In GitLab 16.0, [PostgreSQL 12 support is being removed, and PostgreSQL 13 is becoming the new minimum](#postgresql-12-deprecated). + - Installs using production-ready external databases will need to complete their migration to a newer PostgreSQL version before upgrading. + - Installs using the [non-production bundled PostgreSQL 12 chart](https://docs.gitlab.com/charts/installation/tools.html#postgresql) will have the chart upgraded to the new version. For more information, [see issue 4118](https://gitlab.com/gitlab-org/charts/gitlab/-/issues/4118) + - Installs using the [non-production bundled Redis chart](https://docs.gitlab.com/charts/installation/tools.html#redis) will have the chart upgraded to a newer version. For more information, [see issue 3375](https://gitlab.com/gitlab-org/charts/gitlab/-/issues/3375) + - Installs using the [bundled cert-manager chart](https://docs.gitlab.com/charts/installation/tls.html#option-1-cert-manager-and-lets-encrypt) will have the chart upgraded to a newer version. For more information, [see issue 4313](https://gitlab.com/gitlab-org/charts/gitlab/-/issues/4313) + + The full GitLab Helm Chart 7.0 upgrade steps will be available in the [upgrade docs](https://docs.gitlab.com/charts/installation/upgrade.html). diff --git a/doc/administration/auth/ldap/ldap_synchronization.md b/doc/administration/auth/ldap/ldap_synchronization.md index 59ed3a4153d..c61783e507e 100644 --- a/doc/administration/auth/ldap/ldap_synchronization.md +++ b/doc/administration/auth/ldap/ldap_synchronization.md @@ -7,7 +7,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w # LDAP synchronization **(PREMIUM SELF)** If you have [configured LDAP to work with GitLab](index.md), GitLab can automatically synchronize -users and groups. This process updates user and group information. +users and groups. + +LDAP synchronization updates existing GitLab user and group information. It does not create new GitLab users through LDAP. You can change when synchronization occurs. diff --git a/doc/administration/restart_gitlab.md b/doc/administration/restart_gitlab.md index 7996db3d1e1..e86541b7ced 100644 --- a/doc/administration/restart_gitlab.md +++ b/doc/administration/restart_gitlab.md @@ -126,3 +126,17 @@ kubectl delete pods -l release=<helm release name>,app=<component name> ``` The release name can be obtained from the output of the `helm list` command. + +## Docker installation + +If you change the configuration on your [Docker installation](../install/docker.md), for that change to take effect you must restart: + +- The main `gitlab` container. +- Any separate component containers. + +For example, if you deployed Sidekiq on a separate container, to restart the containers, run: + +```shell +sudo docker restart gitlab +sudo docker restart sidekiq +``` diff --git a/doc/architecture/blueprints/cells/pods-feature-admin-area.md b/doc/architecture/blueprints/cells/pods-feature-admin-area.md deleted file mode 100644 index 354a55cacac..00000000000 --- a/doc/architecture/blueprints/cells/pods-feature-admin-area.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -redirect_to: 'cells-feature-admin-area.md' -remove_date: '2023-06-14' ---- - -This document was moved to [another location](cells-feature-admin-area.md). - -<!-- This redirect file can be deleted after <2023-06-14>. --> -<!-- Redirects that point to other docs in the same project expire in three months. --> -<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. --> -<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/doc/architecture/blueprints/cells/pods-feature-agent-for-kubernetes.md b/doc/architecture/blueprints/cells/pods-feature-agent-for-kubernetes.md deleted file mode 100644 index c6a7e138508..00000000000 --- a/doc/architecture/blueprints/cells/pods-feature-agent-for-kubernetes.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -redirect_to: 'cells-feature-agent-for-kubernetes.md' -remove_date: '2023-06-14' ---- - -This document was moved to [another location](cells-feature-agent-for-kubernetes.md). - -<!-- This redirect file can be deleted after <2023-06-14>. --> -<!-- Redirects that point to other docs in the same project expire in three months. --> -<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. --> -<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/doc/architecture/blueprints/cells/pods-feature-backups.md b/doc/architecture/blueprints/cells/pods-feature-backups.md deleted file mode 100644 index 6927142f993..00000000000 --- a/doc/architecture/blueprints/cells/pods-feature-backups.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -redirect_to: 'cells-feature-backups.md' -remove_date: '2023-06-14' ---- - -This document was moved to [another location](cells-feature-backups.md). - -<!-- This redirect file can be deleted after <2023-06-14>. --> -<!-- Redirects that point to other docs in the same project expire in three months. --> -<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. --> -<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/doc/architecture/blueprints/cells/pods-feature-ci-runners.md b/doc/architecture/blueprints/cells/pods-feature-ci-runners.md deleted file mode 100644 index fd9c0094b2f..00000000000 --- a/doc/architecture/blueprints/cells/pods-feature-ci-runners.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -redirect_to: 'cells-feature-ci-runners.md' -remove_date: '2023-06-14' ---- - -This document was moved to [another location](cells-feature-ci-runners.md). - -<!-- This redirect file can be deleted after <2023-06-14>. --> -<!-- Redirects that point to other docs in the same project expire in three months. --> -<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. --> -<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/doc/architecture/blueprints/cells/pods-feature-container-registry.md b/doc/architecture/blueprints/cells/pods-feature-container-registry.md deleted file mode 100644 index f6a1aa997c3..00000000000 --- a/doc/architecture/blueprints/cells/pods-feature-container-registry.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -redirect_to: 'cells-feature-container-registry.md' -remove_date: '2023-06-14' ---- - -This document was moved to [another location](cells-feature-container-registry.md). - -<!-- This redirect file can be deleted after <2023-06-14>. --> -<!-- Redirects that point to other docs in the same project expire in three months. --> -<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. --> -<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/doc/architecture/blueprints/cells/pods-feature-contributions-forks.md b/doc/architecture/blueprints/cells/pods-feature-contributions-forks.md deleted file mode 100644 index 441a0da11b7..00000000000 --- a/doc/architecture/blueprints/cells/pods-feature-contributions-forks.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -redirect_to: 'cells-feature-contributions-forks.md' -remove_date: '2023-06-14' ---- - -This document was moved to [another location](cells-feature-contributions-forks.md). - -<!-- This redirect file can be deleted after <2023-06-14>. --> -<!-- Redirects that point to other docs in the same project expire in three months. --> -<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. --> -<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/doc/architecture/blueprints/cells/pods-feature-dashboard.md b/doc/architecture/blueprints/cells/pods-feature-dashboard.md deleted file mode 100644 index 9404f3b647b..00000000000 --- a/doc/architecture/blueprints/cells/pods-feature-dashboard.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -redirect_to: 'cells-feature-dashboard.md' -remove_date: '2023-06-14' ---- - -This document was moved to [another location](cells-feature-dashboard.md). - -<!-- This redirect file can be deleted after <2023-06-14>. --> -<!-- Redirects that point to other docs in the same project expire in three months. --> -<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. --> -<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/doc/architecture/blueprints/cells/pods-feature-data-migration.md b/doc/architecture/blueprints/cells/pods-feature-data-migration.md deleted file mode 100644 index 023b54ae5e4..00000000000 --- a/doc/architecture/blueprints/cells/pods-feature-data-migration.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -redirect_to: 'cells-feature-data-migration.md' -remove_date: '2023-06-14' ---- - -This document was moved to [another location](cells-feature-data-migration.md). - -<!-- This redirect file can be deleted after <2023-06-14>. --> -<!-- Redirects that point to other docs in the same project expire in three months. --> -<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. --> -<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/doc/architecture/blueprints/cells/pods-feature-database-sequences.md b/doc/architecture/blueprints/cells/pods-feature-database-sequences.md deleted file mode 100644 index 785289081bb..00000000000 --- a/doc/architecture/blueprints/cells/pods-feature-database-sequences.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -redirect_to: 'cells-feature-database-sequences.md' -remove_date: '2023-06-14' ---- - -This document was moved to [another location](cells-feature-database-sequences.md). - -<!-- This redirect file can be deleted after <2023-06-14>. --> -<!-- Redirects that point to other docs in the same project expire in three months. --> -<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. --> -<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/doc/architecture/blueprints/cells/pods-feature-git-access.md b/doc/architecture/blueprints/cells/pods-feature-git-access.md deleted file mode 100644 index bc44137f8d8..00000000000 --- a/doc/architecture/blueprints/cells/pods-feature-git-access.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -redirect_to: 'cells-feature-git-access.md' -remove_date: '2023-06-14' ---- - -This document was moved to [another location](cells-feature-git-access.md). - -<!-- This redirect file can be deleted after <2023-06-14>. --> -<!-- Redirects that point to other docs in the same project expire in three months. --> -<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. --> -<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/doc/architecture/blueprints/cells/pods-feature-gitlab-pages.md b/doc/architecture/blueprints/cells/pods-feature-gitlab-pages.md deleted file mode 100644 index 8043155b1a6..00000000000 --- a/doc/architecture/blueprints/cells/pods-feature-gitlab-pages.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -redirect_to: 'cells-feature-gitlab-pages.md' -remove_date: '2023-06-14' ---- - -This document was moved to [another location](cells-feature-gitlab-pages.md). - -<!-- This redirect file can be deleted after <2023-06-14>. --> -<!-- Redirects that point to other docs in the same project expire in three months. --> -<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. --> -<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/doc/architecture/blueprints/cells/pods-feature-global-search.md b/doc/architecture/blueprints/cells/pods-feature-global-search.md deleted file mode 100644 index d472b6a9ff6..00000000000 --- a/doc/architecture/blueprints/cells/pods-feature-global-search.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -redirect_to: 'cells-feature-global-search.md' -remove_date: '2023-06-14' ---- - -This document was moved to [another location](cells-feature-global-search.md). - -<!-- This redirect file can be deleted after <2023-06-14>. --> -<!-- Redirects that point to other docs in the same project expire in three months. --> -<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. --> -<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/doc/architecture/blueprints/cells/pods-feature-graphql.md b/doc/architecture/blueprints/cells/pods-feature-graphql.md deleted file mode 100644 index 23d7b0bb856..00000000000 --- a/doc/architecture/blueprints/cells/pods-feature-graphql.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -redirect_to: 'cells-feature-graphql.md' -remove_date: '2023-06-14' ---- - -This document was moved to [another location](cells-feature-graphql.md). - -<!-- This redirect file can be deleted after <2023-06-14>. --> -<!-- Redirects that point to other docs in the same project expire in three months. --> -<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. --> -<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/doc/architecture/blueprints/cells/pods-feature-organizations.md b/doc/architecture/blueprints/cells/pods-feature-organizations.md deleted file mode 100644 index 92ab37a21bf..00000000000 --- a/doc/architecture/blueprints/cells/pods-feature-organizations.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -redirect_to: 'cells-feature-organizations.md' -remove_date: '2023-06-14' ---- - -This document was moved to [another location](cells-feature-organizations.md). - -<!-- This redirect file can be deleted after <2023-06-14>. --> -<!-- Redirects that point to other docs in the same project expire in three months. --> -<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. --> -<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/doc/architecture/blueprints/cells/pods-feature-personal-namespaces.md b/doc/architecture/blueprints/cells/pods-feature-personal-namespaces.md deleted file mode 100644 index 0f05c7d83cb..00000000000 --- a/doc/architecture/blueprints/cells/pods-feature-personal-namespaces.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -redirect_to: 'cells-feature-personal-namespaces.md' -remove_date: '2023-06-14' ---- - -This document was moved to [another location](cells-feature-personal-namespaces.md). - -<!-- This redirect file can be deleted after <2023-06-14>. --> -<!-- Redirects that point to other docs in the same project expire in three months. --> -<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. --> -<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/doc/architecture/blueprints/cells/pods-feature-router-endpoints-classification.md b/doc/architecture/blueprints/cells/pods-feature-router-endpoints-classification.md deleted file mode 100644 index 91de121f694..00000000000 --- a/doc/architecture/blueprints/cells/pods-feature-router-endpoints-classification.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -redirect_to: 'cells-feature-router-endpoints-classification.md' -remove_date: '2023-06-14' ---- - -This document was moved to [another location](cells-feature-router-endpoints-classification.md). - -<!-- This redirect file can be deleted after <2023-06-14>. --> -<!-- Redirects that point to other docs in the same project expire in three months. --> -<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. --> -<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/doc/architecture/blueprints/cells/pods-feature-schema-changes.md b/doc/architecture/blueprints/cells/pods-feature-schema-changes.md deleted file mode 100644 index 5f7de0eb486..00000000000 --- a/doc/architecture/blueprints/cells/pods-feature-schema-changes.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -redirect_to: 'cells-feature-schema-changes.md' -remove_date: '2023-06-14' ---- - -This document was moved to [another location](cells-feature-schema-changes.md). - -<!-- This redirect file can be deleted after <2023-06-14>. --> -<!-- Redirects that point to other docs in the same project expire in three months. --> -<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. --> -<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/doc/architecture/blueprints/cells/pods-feature-secrets.md b/doc/architecture/blueprints/cells/pods-feature-secrets.md deleted file mode 100644 index 5c482e1c986..00000000000 --- a/doc/architecture/blueprints/cells/pods-feature-secrets.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -redirect_to: 'cells-feature-secrets.md' -remove_date: '2023-06-14' ---- - -This document was moved to [another location](cells-feature-secrets.md). - -<!-- This redirect file can be deleted after <2023-06-14>. --> -<!-- Redirects that point to other docs in the same project expire in three months. --> -<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. --> -<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/doc/architecture/blueprints/cells/pods-feature-snippets.md b/doc/architecture/blueprints/cells/pods-feature-snippets.md deleted file mode 100644 index 867c9b49955..00000000000 --- a/doc/architecture/blueprints/cells/pods-feature-snippets.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -redirect_to: 'cells-feature-snippets.md' -remove_date: '2023-06-14' ---- - -This document was moved to [another location](cells-feature-snippets.md). - -<!-- This redirect file can be deleted after <2023-06-14>. --> -<!-- Redirects that point to other docs in the same project expire in three months. --> -<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. --> -<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/doc/architecture/blueprints/cells/pods-feature-template.md b/doc/architecture/blueprints/cells/pods-feature-template.md deleted file mode 100644 index e1150c3426f..00000000000 --- a/doc/architecture/blueprints/cells/pods-feature-template.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -redirect_to: 'cells-feature-template.md' -remove_date: '2023-06-14' ---- - -This document was moved to [another location](cells-feature-template.md). - -<!-- This redirect file can be deleted after <2023-06-14>. --> -<!-- Redirects that point to other docs in the same project expire in three months. --> -<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. --> -<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/doc/architecture/blueprints/cells/pods-feature-uploads.md b/doc/architecture/blueprints/cells/pods-feature-uploads.md deleted file mode 100644 index 7280f70ebdb..00000000000 --- a/doc/architecture/blueprints/cells/pods-feature-uploads.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -redirect_to: 'cells-feature-uploads.md' -remove_date: '2023-06-14' ---- - -This document was moved to [another location](cells-feature-uploads.md). - -<!-- This redirect file can be deleted after <2023-06-14>. --> -<!-- Redirects that point to other docs in the same project expire in three months. --> -<!-- Redirects that point to docs in a different project or site (link is not relative and starts with `https:`) expire in one year. --> -<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html --> diff --git a/doc/development/i18n/proofreader.md b/doc/development/i18n/proofreader.md index 419f889240c..964bee8620e 100644 --- a/doc/development/i18n/proofreader.md +++ b/doc/development/i18n/proofreader.md @@ -19,6 +19,8 @@ are very appreciative of the work done by translators and proofreaders! - Tsegaselassie Tadesse - [GitLab](https://gitlab.com/tsega), [Crowdin](https://crowdin.com/profile/tsegaselassi) - Arabic - Proofreaders needed. +- Belarusian + - Anton Katsuba - [GitLab](https://gitlab.com/coinvariant), [Crowdin](https://crowdin.com/profile/aerialfiddle) - Bosnian - Haris Delalić - [GitLab](https://gitlab.com/haris.delalic), [Crowdin](https://crowdin.com/profile/haris.delalic) - Bulgarian diff --git a/doc/development/value_stream_analytics.md b/doc/development/value_stream_analytics.md index 634c337793f..5f500640841 100644 --- a/doc/development/value_stream_analytics.md +++ b/doc/development/value_stream_analytics.md @@ -221,6 +221,7 @@ graph LR; MergeRequestLastBuildFinished --> MergeRequestLabelRemoved; MergeRequestLabelAdded --> MergeRequestLabelAdded; MergeRequestLabelAdded --> MergeRequestLabelRemoved; + MergeRequestLabelAdded --> MergeRequestMerged; MergeRequestLabelRemoved --> MergeRequestLabelAdded; MergeRequestLabelRemoved --> MergeRequestLabelRemoved; ``` diff --git a/doc/update/deprecations.md b/doc/update/deprecations.md index d7bbf254f2a..f7c93f1281b 100644 --- a/doc/update/deprecations.md +++ b/doc/update/deprecations.md @@ -47,6 +47,29 @@ and [GraphQL](https://docs.gitlab.com/ee/api/graphql/removed_items.html) depreca <div class="deprecation removal-160 breaking-change"> +### Bundled Grafana Helm Chart is deprecated + +Planned removal: GitLab <span class="removal-milestone">16.0</span> <span class="removal-date"></span> + +WARNING: +This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/). +Review the details carefully before upgrading. + +The Grafana Helm chart that is bundled with the GitLab Helm Chart is deprecated and will be removed in the GitLab Helm Chart 7.0 release (releasing along with GitLab 16.0). + +The bundled Grafana Helm chart is an optional service that can be turned on to provide the Grafana UI connected to the GitLab Helm Chart's Prometheus metrics. + +The version of Grafana that the GitLab Helm Chart is currently providing is no longer a supported Grafana version. +If you're using the bundled Grafana, you should switch to the [newer chart version from Grafana Labs](https://artifacthub.io/packages/helm/grafana/grafana) +or a Grafana Operator from a trusted provider. + +In your new Grafana instance, you can [configure the GitLab provided Prometheus as a data source](https://docs.gitlab.com/ee/administration/monitoring/performance/grafana_configuration.html#integration-with-gitlab-ui) +and [connect Grafana to the GitLab UI](https://docs.gitlab.com/ee/administration/monitoring/performance/grafana_configuration.html#integration-with-gitlab-ui). + +</div> + +<div class="deprecation removal-160 breaking-change"> + ### Deprecated Consul http metrics Planned removal: GitLab <span class="removal-milestone">16.0</span> <span class="removal-date"></span> @@ -116,6 +139,28 @@ You should update to the new configuration structure as soon as possible using [the upgrade instructions](https://docs.gitlab.com/ee/update/#gitaly-omnibus-gitlab-configuration-structure-change). </div> + +<div class="deprecation removal-160 breaking-change"> + +### Major bundled Helm Chart updates for the GitLab Helm Chart + +Planned removal: GitLab <span class="removal-milestone">16.0</span> <span class="removal-date"></span> + +WARNING: +This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/). +Review the details carefully before upgrading. + +To coincide with GitLab 16.0, the GitLab Helm Chart will release the 7.0 major version. The following major bundled chart updates will be included: + +- In GitLab 16.0, [PostgreSQL 12 support is being removed, and PostgreSQL 13 is becoming the new minimum](#postgresql-12-deprecated). + - Installs using production-ready external databases will need to complete their migration to a newer PostgreSQL version before upgrading. + - Installs using the [non-production bundled PostgreSQL 12 chart](https://docs.gitlab.com/charts/installation/tools.html#postgresql) will have the chart upgraded to the new version. For more information, [see issue 4118](https://gitlab.com/gitlab-org/charts/gitlab/-/issues/4118) +- Installs using the [non-production bundled Redis chart](https://docs.gitlab.com/charts/installation/tools.html#redis) will have the chart upgraded to a newer version. For more information, [see issue 3375](https://gitlab.com/gitlab-org/charts/gitlab/-/issues/3375) +- Installs using the [bundled cert-manager chart](https://docs.gitlab.com/charts/installation/tls.html#option-1-cert-manager-and-lets-encrypt) will have the chart upgraded to a newer version. For more information, [see issue 4313](https://gitlab.com/gitlab-org/charts/gitlab/-/issues/4313) + +The full GitLab Helm Chart 7.0 upgrade steps will be available in the [upgrade docs](https://docs.gitlab.com/charts/installation/upgrade.html). + +</div> </div> <div class="announcement-milestone"> @@ -1988,7 +2033,7 @@ In GitLab 16.0, PostgreSQL 13 becomes the minimum required PostgreSQL version. PostgreSQL 12 will be supported for the full GitLab 15 release cycle. PostgreSQL 13 will also be supported for instances that want to upgrade prior to GitLab 16.0. -Upgrading to PostgreSQL 13 is not yet supported for GitLab instances with Geo enabled. Geo support for PostgreSQL 13 will be announced in a minor release version of GitLab 15, after the process is fully supported and validated. For more information, read the Geo related verifications on the [support epic for PostgreSQL 13](https://gitlab.com/groups/gitlab-org/-/epics/3832). +Support for PostgreSQL 13 was added to Geo in GitLab 15.2. </div> diff --git a/doc/user/admin_area/reporting/git_abuse_rate_limit.md b/doc/user/admin_area/reporting/git_abuse_rate_limit.md index 1dd9497fed9..a6c3ff3ae32 100644 --- a/doc/user/admin_area/reporting/git_abuse_rate_limit.md +++ b/doc/user/admin_area/reporting/git_abuse_rate_limit.md @@ -4,12 +4,10 @@ group: Anti-Abuse info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments --- -# Git abuse rate limit (administration) **(ULTIMATE)** +# Git abuse rate limit (administration) **(ULTIMATE SELF)** -> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/8066) in GitLab 15.2 [with a flag](../../../administration/feature_flags.md) named `git_abuse_rate_limit_feature_flag`. Disabled by default. - -FLAG: -On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `git_abuse_rate_limit_feature_flag`. On GitLab.com, this feature is available. +> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/8066) in GitLab 15.2 [with a flag](../../../administration/feature_flags.md) named `git_abuse_rate_limit_feature_flag`. Disabled by default. +> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/394996) in GitLab 15.10. Feature flag `git_abuse_rate_limit_feature_flag` removed. This is the administration documentation. For information about Git abuse rate limiting at the group level, see the [group-level documentation](../../group/reporting/git_abuse_rate_limit.md). @@ -17,14 +15,6 @@ Git abuse rate limiting is a feature to automatically [ban users](../moderate_us Git abuse rate limiting does not apply to instance administrators, [deploy tokens](../../../user/project/deploy_tokens/index.md), or [deploy keys](../../../user/project/deploy_keys/index.md). -## Automatic ban notifications - -If the `git_abuse_rate_limit_feature_flag` feature flag is enabled, selected users receive an email when a user is about to be banned. - -If automatic banning is disabled, a user is not banned automatically when they exceed the limit. However, notifications are still sent. You can use this setup to determine the correct values of the rate limit settings before enabling automatic banning. - -If automatic banning is enabled, an email notification is sent when a user is about to be banned, and the user is automatically banned from the GitLab instance. - ## Configure Git abuse rate limiting 1. On the top bar, select **Main menu > Admin**. @@ -38,6 +28,12 @@ If automatic banning is enabled, an email notification is sent when a user is ab 1. Optional. Turn on the **Automatically ban users from this namespace when they exceed the specified limits** toggle to enable automatic banning. 1. Select **Save changes**. +## Automatic ban notifications + +If automatic banning is disabled, a user is not banned automatically when they exceed the limit. However, notifications are still sent to the users listed under **Send notifications to**. You can use this setup to determine the correct values of the rate limit settings before enabling automatic banning. + +If automatic banning is enabled, an email notification is sent when a user is about to be banned, and the user is automatically banned from the GitLab instance. + ## Unban a user 1. On the top bar, select **Main menu > Admin**. diff --git a/doc/user/group/value_stream_analytics/index.md b/doc/user/group/value_stream_analytics/index.md index 57d4fd513ff..8ecedd11f16 100644 --- a/doc/user/group/value_stream_analytics/index.md +++ b/doc/user/group/value_stream_analytics/index.md @@ -29,6 +29,48 @@ Value stream analytics is also available for [projects](../../analytics/value_st ## How value stream analytics works +Value stream analytics calculates the duration of every stage of your software development process. + +Value stream analytics is made of three core objects: + +- A **value stream** contains a value stream stage list. +- Each value stream stage list contains one or more **stages**. +- Each stage has two **events**: start and stop. + +### Value stream stages + +A stage represents an event pair (start and end events) with additional metadata, such as the name of the stage. You can configure the stages in the pairing rules defined in the backend. + +### Value streams + +Value streams are container objects for the stages. You can have multiple value streams per group, to focus on different aspects of the DevOps lifecycle. + +### Value stream stage events + +Events are the smallest building blocks of the value stream analytics feature. A stage consists of a start event and an end event. + +The following stage events are available: + +- Issue closed +- Issue created +- Issue first added to board +- Issue first associated with milestone +- Issue first mentioned +- Issue label added +- Issue label removed +- MR closed +- MR merged +- MR created +- MR first commit time +- MR first deployed +- MR label added +- MR label removed +- MR last pipeline duration + +These events play a key role in the duration calculation, which is calculated by the formula: duration = end event time - start event time. + +To learn what start and end events can be paired, see [Validating start and end events](../../../development/value_stream_analytics.md#validating-start-and-end-events). + ### How value stream analytics aggregates data > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/335391) in GitLab 14.5. diff --git a/doc/user/project/integrations/webhook_events.md b/doc/user/project/integrations/webhook_events.md index 53177004888..b67049fe90b 100644 --- a/doc/user/project/integrations/webhook_events.md +++ b/doc/user/project/integrations/webhook_events.md @@ -1392,6 +1392,7 @@ Payload example: "build_started_at": null, "build_finished_at": null, "build_duration": null, + "build_queued_duration": 1095.588715, // duration in seconds "build_allow_failure": false, "build_failure_reason": "script_failure", "retries_count": 2, // the second retry of this job diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md index 230795222a0..852db56bf23 100644 --- a/doc/user/project/settings/import_export.md +++ b/doc/user/project/settings/import_export.md @@ -142,6 +142,8 @@ Items that are exported include: - Group members are exported as project members, as long as the user has the Maintainer role in the exported project's group, or is an administrator +### Items that are not exported + Items that are **not** exported include: - [Child pipeline history](https://gitlab.com/gitlab-org/gitlab/-/issues/221088) @@ -240,11 +242,11 @@ possible through a [professional services engagement](https://about.gitlab.com/s To help avoid abuse, by default, users are rate limited to: -| Request Type | Limit | -| ---------------- | ----- | -| Export | 6 projects per minute | -| Download export | 1 download per group per minute | -| Import | 6 projects per minute | +| Request type | Limit | +|:----------------|:--------------------------------| +| Export | 6 projects per minute | +| Download export | 1 download per group per minute | +| Import | 6 projects per minute | ## Related topics diff --git a/spec/frontend/search/mock_data.js b/spec/frontend/search/mock_data.js index 0aa4f0e1c84..9ca170d34e2 100644 --- a/spec/frontend/search/mock_data.js +++ b/spec/frontend/search/mock_data.js @@ -6,6 +6,7 @@ export const MOCK_QUERY = { state: 'all', confidential: null, group_id: 1, + language: ['C', 'JavaScript'], }; export const MOCK_GROUP = { @@ -193,214 +194,6 @@ export const MOCK_NAVIGATION_ACTION_MUTATION = { payload: { key: 'projects', count: '13' }, }; -export const MOCK_AGGREGATIONS = [ - { - name: 'language', - buckets: [ - { key: 'random-label-edumingos0', count: 1 }, - { key: 'random-label-rbourgourd1', count: 2 }, - { key: 'random-label-dfearnside2', count: 3 }, - { key: 'random-label-gewins3', count: 4 }, - { key: 'random-label-telverstone4', count: 5 }, - { key: 'random-label-ygerriets5', count: 6 }, - { key: 'random-label-lmoffet6', count: 7 }, - { key: 'random-label-ehinnerk7', count: 8 }, - { key: 'random-label-flanceley8', count: 9 }, - { key: 'random-label-adoyle9', count: 10 }, - { key: 'random-label-rmcgirla', count: 11 }, - { key: 'random-label-dwhellansb', count: 12 }, - { key: 'random-label-apitkethlyc', count: 13 }, - { key: 'random-label-senevoldsend', count: 14 }, - { key: 'random-label-tlardnare', count: 15 }, - { key: 'random-label-fcoilsf', count: 16 }, - { key: 'random-label-qgeckg', count: 17 }, - { key: 'random-label-rgrabenh', count: 18 }, - { key: 'random-label-lashardi', count: 19 }, - { key: 'random-label-sadamovitchj', count: 20 }, - { key: 'random-label-rlyddiardk', count: 21 }, - { key: 'random-label-jpoell', count: 22 }, - { key: 'random-label-kcharitym', count: 23 }, - { key: 'random-label-cbertenshawn', count: 24 }, - { key: 'random-label-jsturgeso', count: 25 }, - { key: 'random-label-ohouldcroftp', count: 26 }, - { key: 'random-label-rheijnenq', count: 27 }, - { key: 'random-label-snortheyr', count: 28 }, - { key: 'random-label-vpairpoints', count: 29 }, - { key: 'random-label-odavidovicit', count: 30 }, - { key: 'random-label-fmccartu', count: 31 }, - { key: 'random-label-cwansburyv', count: 32 }, - { key: 'random-label-bdimontw', count: 33 }, - { key: 'random-label-adocketx', count: 34 }, - { key: 'random-label-obavridgey', count: 35 }, - { key: 'random-label-jperezz', count: 36 }, - { key: 'random-label-gdeneve10', count: 37 }, - { key: 'random-label-rmckeand11', count: 38 }, - { key: 'random-label-kwestmerland12', count: 39 }, - { key: 'random-label-mpryer13', count: 40 }, - { key: 'random-label-rmcneil14', count: 41 }, - { key: 'random-label-ablondel15', count: 42 }, - { key: 'random-label-wbalducci16', count: 43 }, - { key: 'random-label-swigley17', count: 44 }, - { key: 'random-label-gferroni18', count: 45 }, - { key: 'random-label-icollings19', count: 46 }, - { key: 'random-label-wszymanski1a', count: 47 }, - { key: 'random-label-jelson1b', count: 48 }, - { key: 'random-label-fsambrook1c', count: 49 }, - { key: 'random-label-kconey1d', count: 50 }, - { key: 'random-label-agoodread1e', count: 51 }, - { key: 'random-label-nmewton1f', count: 52 }, - { key: 'random-label-gcodman1g', count: 53 }, - { key: 'random-label-rpoplee1h', count: 54 }, - { key: 'random-label-mhug1i', count: 55 }, - { key: 'random-label-ggowrie1j', count: 56 }, - { key: 'random-label-ctonepohl1k', count: 57 }, - { key: 'random-label-cstillman1l', count: 58 }, - { key: 'random-label-dcollyer1m', count: 59 }, - { key: 'random-label-idimelow1n', count: 60 }, - { key: 'random-label-djarley1o', count: 61 }, - { key: 'random-label-omclleese1p', count: 62 }, - { key: 'random-label-dstivers1q', count: 63 }, - { key: 'random-label-svose1r', count: 64 }, - { key: 'random-label-clanfare1s', count: 65 }, - { key: 'random-label-aport1t', count: 66 }, - { key: 'random-label-hcarlett1u', count: 67 }, - { key: 'random-label-dstillmann1v', count: 68 }, - { key: 'random-label-ncorpe1w', count: 69 }, - { key: 'random-label-mjacobsohn1x', count: 70 }, - { key: 'random-label-ycleiment1y', count: 71 }, - { key: 'random-label-owherton1z', count: 72 }, - { key: 'random-label-anowaczyk20', count: 73 }, - { key: 'random-label-rmckennan21', count: 74 }, - { key: 'random-label-cmoulding22', count: 75 }, - { key: 'random-label-sswate23', count: 76 }, - { key: 'random-label-cbarge24', count: 77 }, - { key: 'random-label-agrainger25', count: 78 }, - { key: 'random-label-ncosin26', count: 79 }, - { key: 'random-label-pkears27', count: 80 }, - { key: 'random-label-cmcarthur28', count: 81 }, - { key: 'random-label-jmantripp29', count: 82 }, - { key: 'random-label-cjekel2a', count: 83 }, - { key: 'random-label-hdilleway2b', count: 84 }, - { key: 'random-label-lbovaird2c', count: 85 }, - { key: 'random-label-mweld2d', count: 86 }, - { key: 'random-label-marnowitz2e', count: 87 }, - { key: 'random-label-nbertomieu2f', count: 88 }, - { key: 'random-label-mledward2g', count: 89 }, - { key: 'random-label-mhince2h', count: 90 }, - { key: 'random-label-baarons2i', count: 91 }, - { key: 'random-label-kfrancie2j', count: 92 }, - { key: 'random-label-ishooter2k', count: 93 }, - { key: 'random-label-glowmass2l', count: 94 }, - { key: 'random-label-rgeorgi2m', count: 95 }, - { key: 'random-label-bproby2n', count: 96 }, - { key: 'random-label-hsteffan2o', count: 97 }, - { key: 'random-label-doruane2p', count: 98 }, - { key: 'random-label-rlunny2q', count: 99 }, - { key: 'random-label-geles2r', count: 100 }, - { key: 'random-label-nmaggiore2s', count: 101 }, - { key: 'random-label-aboocock2t', count: 102 }, - { key: 'random-label-eguilbert2u', count: 103 }, - { key: 'random-label-emccutcheon2v', count: 104 }, - { key: 'random-label-hcowser2w', count: 105 }, - { key: 'random-label-dspeeding2x', count: 106 }, - { key: 'random-label-oseebright2y', count: 107 }, - { key: 'random-label-hpresdee2z', count: 108 }, - { key: 'random-label-pesseby30', count: 109 }, - { key: 'random-label-hpusey31', count: 110 }, - { key: 'random-label-dmanthorpe32', count: 111 }, - { key: 'random-label-natley33', count: 112 }, - { key: 'random-label-iferentz34', count: 113 }, - { key: 'random-label-adyble35', count: 114 }, - { key: 'random-label-dlockitt36', count: 115 }, - { key: 'random-label-acoxwell37', count: 116 }, - { key: 'random-label-amcgarvey38', count: 117 }, - { key: 'random-label-rmcgougan39', count: 118 }, - { key: 'random-label-mscole3a', count: 119 }, - { key: 'random-label-lmalim3b', count: 120 }, - { key: 'random-label-cends3c', count: 121 }, - { key: 'random-label-dmannie3d', count: 122 }, - { key: 'random-label-lgoodricke3e', count: 123 }, - { key: 'random-label-rcaghy3f', count: 124 }, - { key: 'random-label-mprozillo3g', count: 125 }, - { key: 'random-label-mcardnell3h', count: 126 }, - { key: 'random-label-gericssen3i', count: 127 }, - { key: 'random-label-fspooner3j', count: 128 }, - { key: 'random-label-achadney3k', count: 129 }, - { key: 'random-label-corchard3l', count: 130 }, - { key: 'random-label-lyerill3m', count: 131 }, - { key: 'random-label-jrusk3n', count: 132 }, - { key: 'random-label-lbonelle3o', count: 133 }, - { key: 'random-label-eduny3p', count: 134 }, - { key: 'random-label-mhutchence3q', count: 135 }, - { key: 'random-label-rmargeram3r', count: 136 }, - { key: 'random-label-smaudlin3s', count: 137 }, - { key: 'random-label-sfarrance3t', count: 138 }, - { key: 'random-label-eclendennen3u', count: 139 }, - { key: 'random-label-cyabsley3v', count: 140 }, - { key: 'random-label-ahensmans3w', count: 141 }, - { key: 'random-label-tsenchenko3x', count: 142 }, - { key: 'random-label-ryurchishin3y', count: 143 }, - { key: 'random-label-teby3z', count: 144 }, - { key: 'random-label-dvaillant40', count: 145 }, - { key: 'random-label-kpetyakov41', count: 146 }, - { key: 'random-label-cmorrison42', count: 147 }, - { key: 'random-label-ltwiddy43', count: 148 }, - { key: 'random-label-ineame44', count: 149 }, - { key: 'random-label-blucock45', count: 150 }, - { key: 'random-label-kdunsford46', count: 151 }, - { key: 'random-label-dducham47', count: 152 }, - { key: 'random-label-javramovitz48', count: 153 }, - { key: 'random-label-mascraft49', count: 154 }, - { key: 'random-label-bloughead4a', count: 155 }, - { key: 'random-label-sduckit4b', count: 156 }, - { key: 'random-label-hhardman4c', count: 157 }, - { key: 'random-label-cstaniforth4d', count: 158 }, - { key: 'random-label-jedney4e', count: 159 }, - { key: 'random-label-bobbard4f', count: 160 }, - { key: 'random-label-cgiraux4g', count: 161 }, - { key: 'random-label-tkiln4h', count: 162 }, - { key: 'random-label-jwansbury4i', count: 163 }, - { key: 'random-label-dquinlan4j', count: 164 }, - { key: 'random-label-hgindghill4k', count: 165 }, - { key: 'random-label-jjowle4l', count: 166 }, - { key: 'random-label-egambrell4m', count: 167 }, - { key: 'random-label-jmcgloughlin4n', count: 168 }, - { key: 'random-label-bbabb4o', count: 169 }, - { key: 'random-label-achuck4p', count: 170 }, - { key: 'random-label-tsyers4q', count: 171 }, - { key: 'random-label-jlandon4r', count: 172 }, - { key: 'random-label-wteather4s', count: 173 }, - { key: 'random-label-dfoskin4t', count: 174 }, - { key: 'random-label-gmorlon4u', count: 175 }, - { key: 'random-label-jseely4v', count: 176 }, - { key: 'random-label-cbrass4w', count: 177 }, - { key: 'random-label-fmanilo4x', count: 178 }, - { key: 'random-label-bfrangleton4y', count: 179 }, - { key: 'random-label-vbartkiewicz4z', count: 180 }, - { key: 'random-label-tclymer50', count: 181 }, - { key: 'random-label-pqueen51', count: 182 }, - { key: 'random-label-bpol52', count: 183 }, - { key: 'random-label-jclaeskens53', count: 184 }, - { key: 'random-label-cstranieri54', count: 185 }, - { key: 'random-label-drumbelow55', count: 186 }, - { key: 'random-label-wbrumham56', count: 187 }, - { key: 'random-label-azeal57', count: 188 }, - { key: 'random-label-msnooks58', count: 189 }, - { key: 'random-label-blapre59', count: 190 }, - { key: 'random-label-cduckers5a', count: 191 }, - { key: 'random-label-mgumary5b', count: 192 }, - { key: 'random-label-rtebbs5c', count: 193 }, - { key: 'random-label-eroe5d', count: 194 }, - { key: 'random-label-rconfait5e', count: 195 }, - { key: 'random-label-fsinderland5f', count: 196 }, - { key: 'random-label-tdallywater5g', count: 197 }, - { key: 'random-label-glindenman5h', count: 198 }, - { key: 'random-label-fbauser5i', count: 199 }, - { key: 'random-label-bdownton5j', count: 200 }, - ], - }, -]; - export const MOCK_LANGUAGE_AGGREGATIONS_BUCKETS = [ { key: 'random-label-edumingos0', count: 1 }, { key: 'random-label-rbourgourd1', count: 2 }, @@ -604,13 +397,27 @@ export const MOCK_LANGUAGE_AGGREGATIONS_BUCKETS = [ { key: 'random-label-bdownton5j', count: 200 }, ]; +export const MOCK_AGGREGATIONS = [ + { + name: 'language', + buckets: MOCK_LANGUAGE_AGGREGATIONS_BUCKETS, + }, +]; + +export const SORTED_MOCK_AGGREGATIONS = [ + { + name: 'language', + buckets: MOCK_LANGUAGE_AGGREGATIONS_BUCKETS.reverse(), + }, +]; + export const MOCK_RECEIVE_AGGREGATIONS_SUCCESS_MUTATION = [ { type: types.REQUEST_AGGREGATIONS, }, { type: types.RECEIVE_AGGREGATIONS_SUCCESS, - payload: MOCK_AGGREGATIONS, + payload: SORTED_MOCK_AGGREGATIONS, }, ]; diff --git a/spec/frontend/search/sidebar/components/app_spec.js b/spec/frontend/search/sidebar/components/app_spec.js index 8a35ae96d5e..963b73aeae5 100644 --- a/spec/frontend/search/sidebar/components/app_spec.js +++ b/spec/frontend/search/sidebar/components/app_spec.js @@ -5,7 +5,7 @@ import { MOCK_QUERY } from 'jest/search/mock_data'; import GlobalSearchSidebar from '~/search/sidebar/components/app.vue'; import ResultsFilters from '~/search/sidebar/components/results_filters.vue'; import ScopeNavigation from '~/search/sidebar/components/scope_navigation.vue'; -import LanguageFilter from '~/search/sidebar/components/language_filter.vue'; +import LanguageFilter from '~/search/sidebar/components/language_filter/index.vue'; Vue.use(Vuex); diff --git a/spec/frontend/search/sidebar/components/checkbox_filter_spec.js b/spec/frontend/search/sidebar/components/checkbox_filter_spec.js index f7b35c7bb14..7af588f059f 100644 --- a/spec/frontend/search/sidebar/components/checkbox_filter_spec.js +++ b/spec/frontend/search/sidebar/components/checkbox_filter_spec.js @@ -1,17 +1,22 @@ import { GlFormCheckboxGroup, GlFormCheckbox } from '@gitlab/ui'; import Vue from 'vue'; import Vuex from 'vuex'; +import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { MOCK_QUERY, MOCK_LANGUAGE_AGGREGATIONS_BUCKETS } from 'jest/search/mock_data'; -import CheckboxFilter from '~/search/sidebar/components/checkbox_filter.vue'; +import CheckboxFilter, { + TRACKING_LABEL_CHECKBOX, + TRACKING_LABEL_SET, +} from '~/search/sidebar/components/checkbox_filter.vue'; -import { languageFilterData } from '~/search/sidebar/constants/language_filter_data'; +import { languageFilterData } from '~/search/sidebar/components/language_filter/data'; import { convertFiltersData } from '~/search/sidebar/utils'; Vue.use(Vuex); describe('CheckboxFilter', () => { let wrapper; + let trackingSpy; const actionSpies = { setQuery: jest.fn(), @@ -23,9 +28,10 @@ describe('CheckboxFilter', () => { const defaultProps = { filtersData: convertFiltersData(MOCK_LANGUAGE_AGGREGATIONS_BUCKETS), + trackingNamespace: 'testNameSpace', }; - const createComponent = () => { + const createComponent = (Props = defaultProps) => { const store = new Vuex.Store({ state: { query: MOCK_QUERY, @@ -37,13 +43,13 @@ describe('CheckboxFilter', () => { wrapper = shallowMountExtended(CheckboxFilter, { store, propsData: { - ...defaultProps, + ...Props, }, }); }; - beforeEach(() => { - createComponent(); + afterEach(() => { + unmockTracking(); }); const findFormCheckboxGroup = () => wrapper.findComponent(GlFormCheckboxGroup); @@ -52,6 +58,11 @@ describe('CheckboxFilter', () => { const fintAllCheckboxLabelCounts = () => wrapper.findAllByTestId('labelCount'); describe('Renders correctly', () => { + beforeEach(() => { + createComponent(); + trackingSpy = mockTracking(undefined, undefined, jest.spyOn); + }); + it('renders form', () => { expect(findFormCheckboxGroup().exists()).toBe(true); }); @@ -76,15 +87,34 @@ describe('CheckboxFilter', () => { }); describe('actions', () => { - it('triggers setQuery', () => { - const filter = - defaultProps.filtersData.filters[Object.keys(defaultProps.filtersData.filters)[0]].value; - findFormCheckboxGroup().vm.$emit('input', filter); + const checkedLanguageName = MOCK_LANGUAGE_AGGREGATIONS_BUCKETS[0].key; + + beforeEach(async () => { + defaultProps.filtersData = convertFiltersData(MOCK_LANGUAGE_AGGREGATIONS_BUCKETS.slice(0, 3)); + CheckboxFilter.computed.selectedFilter.get = jest.fn(() => checkedLanguageName); + + createComponent(); + trackingSpy = mockTracking(undefined, undefined, jest.spyOn); + findFormCheckboxGroup().vm.$emit('input', checkedLanguageName); + }); + it('triggers setQuery', () => { expect(actionSpies.setQuery).toHaveBeenCalledWith(expect.any(Object), { key: languageFilterData.filterParam, - value: filter, + value: checkedLanguageName, }); }); + + it('sends tracking information when setQuery', () => { + findFormCheckboxGroup().vm.$emit('input', checkedLanguageName); + expect(trackingSpy).toHaveBeenCalledWith( + defaultProps.trackingNamespace, + TRACKING_LABEL_CHECKBOX, + { + label: TRACKING_LABEL_SET, + property: checkedLanguageName, + }, + ); + }); }); }); diff --git a/spec/frontend/search/sidebar/components/language_filter_spec.js b/spec/frontend/search/sidebar/components/language_filter_spec.js index 17656ba749b..5821def5b43 100644 --- a/spec/frontend/search/sidebar/components/language_filter_spec.js +++ b/spec/frontend/search/sidebar/components/language_filter_spec.js @@ -1,20 +1,35 @@ import { GlAlert, GlFormCheckbox, GlForm } from '@gitlab/ui'; import Vue, { nextTick } from 'vue'; import Vuex from 'vuex'; +import { mockTracking, unmockTracking } from 'helpers/tracking_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { MOCK_QUERY, MOCK_AGGREGATIONS, MOCK_LANGUAGE_AGGREGATIONS_BUCKETS, } from 'jest/search/mock_data'; -import LanguageFilter from '~/search/sidebar/components/language_filter.vue'; +import LanguageFilter from '~/search/sidebar/components/language_filter/index.vue'; import CheckboxFilter from '~/search/sidebar/components/checkbox_filter.vue'; -import { MAX_ITEM_LENGTH } from '~/search/sidebar/constants/language_filter_data'; + +import { + TRACKING_LABEL_SHOW_MORE, + TRACKING_CATEGORY, + TRACKING_PROPERTY_MAX, + TRACKING_LABEL_MAX, + TRACKING_LABEL_FILTERS, + TRACKING_ACTION_SHOW, + TRACKING_ACTION_CLICK, + TRACKING_LABEL_APPLY, + TRACKING_LABEL_ALL, +} from '~/search/sidebar/components/language_filter/tracking'; + +import { MAX_ITEM_LENGTH } from '~/search/sidebar/components/language_filter/data'; Vue.use(Vuex); describe('GlobalSearchSidebarLanguageFilter', () => { let wrapper; + let trackingSpy; const actionSpies = { fetchLanguageAggregation: jest.fn(), @@ -46,6 +61,10 @@ describe('GlobalSearchSidebarLanguageFilter', () => { }); }; + afterEach(() => { + unmockTracking(); + }); + const findForm = () => wrapper.findComponent(GlForm); const findCheckboxFilter = () => wrapper.findComponent(CheckboxFilter); const findApplyButton = () => wrapper.findByTestId('apply-button'); @@ -58,6 +77,7 @@ describe('GlobalSearchSidebarLanguageFilter', () => { describe('Renders correctly', () => { beforeEach(() => { createComponent(); + trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); }); it('renders form', () => { @@ -130,20 +150,40 @@ describe('GlobalSearchSidebarLanguageFilter', () => { describe('Show All button works', () => { beforeEach(() => { createComponent(); + trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); }); it(`renders ${MAX_ITEM_LENGTH} amount of items`, async () => { findShowMoreButton().vm.$emit('click'); + await nextTick(); + expect(findAllCheckboxes()).toHaveLength(MAX_ITEM_LENGTH); }); + it('sends tracking information when show more clicked', () => { + findShowMoreButton().vm.$emit('click'); + + expect(trackingSpy).toHaveBeenCalledWith(TRACKING_ACTION_CLICK, TRACKING_LABEL_SHOW_MORE, { + label: TRACKING_LABEL_ALL, + }); + }); + it(`renders more then ${MAX_ITEM_LENGTH} text`, async () => { findShowMoreButton().vm.$emit('click'); await nextTick(); expect(findHasOverMax().exists()).toBe(true); }); + it('sends tracking information when show more clicked and max item reached', () => { + findShowMoreButton().vm.$emit('click'); + + expect(trackingSpy).toHaveBeenCalledWith(TRACKING_ACTION_SHOW, TRACKING_LABEL_FILTERS, { + label: TRACKING_LABEL_MAX, + property: TRACKING_PROPERTY_MAX, + }); + }); + it(`doesn't render show more button after click`, async () => { findShowMoreButton().vm.$emit('click'); await nextTick(); @@ -154,6 +194,7 @@ describe('GlobalSearchSidebarLanguageFilter', () => { describe('actions', () => { beforeEach(() => { createComponent({}); + trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); }); it('uses getter languageAggregationBuckets', () => { @@ -169,5 +210,13 @@ describe('GlobalSearchSidebarLanguageFilter', () => { expect(actionSpies.applyQuery).toHaveBeenCalled(); }); + + it('sends tracking information clicking ApplyButton', () => { + findForm().vm.$emit('submit', { preventDefault: () => {} }); + + expect(trackingSpy).toHaveBeenCalledWith(TRACKING_ACTION_CLICK, TRACKING_LABEL_APPLY, { + label: TRACKING_CATEGORY, + }); + }); }); }); diff --git a/spec/frontend/vue_shared/components/vuex_module_provider_spec.js b/spec/frontend/vue_shared/components/vuex_module_provider_spec.js index acbb931b7b6..e136939cc03 100644 --- a/spec/frontend/vue_shared/components/vuex_module_provider_spec.js +++ b/spec/frontend/vue_shared/components/vuex_module_provider_spec.js @@ -3,10 +3,10 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import VuexModuleProvider from '~/vue_shared/components/vuex_module_provider.vue'; -const TestComponent = Vue.extend({ +const TestComponent = { inject: ['vuexModule'], template: `<div data-testid="vuexModule">{{ vuexModule }}</div> `, -}); +}; const TEST_VUEX_MODULE = 'testVuexModule'; diff --git a/spec/frontend/vue_shared/issuable/show/components/issuable_body_spec.js b/spec/frontend/vue_shared/issuable/show/components/issuable_body_spec.js index 7e665b7c76e..02e729a00bd 100644 --- a/spec/frontend/vue_shared/issuable/show/components/issuable_body_spec.js +++ b/spec/frontend/vue_shared/issuable/show/components/issuable_body_spec.js @@ -1,5 +1,5 @@ +import { GlLink } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; -import { nextTick } from 'vue'; import { useFakeDate } from 'helpers/fake_date'; import IssuableBody from '~/vue_shared/issuable/show/components/issuable_body.vue'; @@ -14,96 +14,76 @@ import { mockIssuableShowProps, mockIssuable } from '../mock_data'; jest.mock('~/autosave'); jest.mock('~/alert'); +jest.mock('~/task_list'); const issuableBodyProps = { ...mockIssuableShowProps, issuable: mockIssuable, }; -const createComponent = (propsData = issuableBodyProps) => - shallowMount(IssuableBody, { - propsData, - stubs: { - IssuableTitle, - IssuableDescription, - IssuableEditForm, - TimeAgoTooltip, - }, - slots: { - 'status-badge': 'Open', - 'edit-form-actions': ` - <button class="js-save">Save changes</button> - <button class="js-cancel">Cancel</button> - `, - }, - }); - describe('IssuableBody', () => { // Some assertions expect a date later than our default useFakeDate(2020, 11, 11); let wrapper; + const createComponent = (propsData = {}) => { + wrapper = shallowMount(IssuableBody, { + propsData: { + ...issuableBodyProps, + ...propsData, + }, + stubs: { + IssuableTitle, + IssuableDescription, + IssuableEditForm, + TimeAgoTooltip, + }, + slots: { + 'status-badge': 'Open', + 'edit-form-actions': ` + <button class="js-save">Save changes</button> + <button class="js-cancel">Cancel</button> + `, + }, + }); + }; + + const findUpdatedLink = () => wrapper.findComponent(GlLink); + const findIssuableEditForm = () => wrapper.findComponent(IssuableEditForm); + const findIssuableEditFormButton = (type) => findIssuableEditForm().find(`button.js-${type}`); + const findIssuableTitle = () => wrapper.findComponent(IssuableTitle); + beforeEach(() => { - wrapper = createComponent(); + createComponent(); + TaskList.mockClear(); }); describe('computed', () => { - describe('isUpdated', () => { - it.each` - updatedAt | returnValue - ${mockIssuable.updatedAt} | ${true} - ${null} | ${false} - ${''} | ${false} - `( - 'returns $returnValue when value of `updateAt` prop is `$updatedAt`', - async ({ updatedAt, returnValue }) => { - wrapper.setProps({ - issuable: { - ...mockIssuable, - updatedAt, - }, - }); - - await nextTick(); - - expect(wrapper.vm.isUpdated).toBe(returnValue); - }, - ); - }); - describe('updatedBy', () => { it('returns value of `issuable.updatedBy`', () => { - expect(wrapper.vm.updatedBy).toBe(mockIssuable.updatedBy); + expect(findUpdatedLink().text()).toBe(mockIssuable.updatedBy.name); + expect(findUpdatedLink().attributes('href')).toBe(mockIssuable.updatedBy.webUrl); }); }); }); describe('watchers', () => { describe('editFormVisible', () => { - it('calls initTaskList in nextTick', async () => { - jest.spyOn(wrapper.vm, 'initTaskList'); - wrapper.setProps({ - editFormVisible: true, - }); - - await nextTick(); - - wrapper.setProps({ + it('calls initTaskList in nextTick', () => { + createComponent({ editFormVisible: false, }); - await nextTick(); - - expect(wrapper.vm.initTaskList).toHaveBeenCalled(); + expect(TaskList).toHaveBeenCalled(); }); }); }); describe('mounted', () => { it('initializes TaskList instance when enabledEdit and enableTaskList props are true', () => { - expect(wrapper.vm.taskList instanceof TaskList).toBe(true); - expect(wrapper.vm.taskList).toMatchObject({ + createComponent(); + expect(TaskList).toHaveBeenCalledWith({ dataType: 'issue', fieldName: 'description', lockVersion: issuableBodyProps.taskListLockVersion, @@ -114,14 +94,12 @@ describe('IssuableBody', () => { }); it('does not initialize TaskList instance when either enabledEdit or enableTaskList prop is false', () => { - const wrapperNoTaskList = createComponent({ + createComponent({ ...issuableBodyProps, enableTaskList: false, }); - expect(wrapperNoTaskList.vm.taskList).not.toBeDefined(); - - wrapperNoTaskList.destroy(); + expect(TaskList).toHaveBeenCalledTimes(0); }); }); @@ -150,10 +128,8 @@ describe('IssuableBody', () => { describe('template', () => { it('renders issuable-title component', () => { - const titleEl = wrapper.findComponent(IssuableTitle); - - expect(titleEl.exists()).toBe(true); - expect(titleEl.props()).toMatchObject({ + expect(findIssuableTitle().exists()).toBe(true); + expect(findIssuableTitle().props()).toMatchObject({ issuable: issuableBodyProps.issuable, statusIcon: issuableBodyProps.statusIcon, enableEdit: issuableBodyProps.enableEdit, @@ -168,42 +144,37 @@ describe('IssuableBody', () => { }); it('renders issuable edit info', () => { - const editedEl = wrapper.find('small'); - - expect(editedEl.text()).toMatchInterpolatedText('Edited 3 months ago by Administrator'); + expect(wrapper.find('small').text()).toMatchInterpolatedText( + 'Edited 3 months ago by Administrator', + ); }); - it('renders issuable-edit-form when `editFormVisible` prop is true', async () => { - wrapper.setProps({ + it('renders issuable-edit-form when `editFormVisible` prop is true', () => { + createComponent({ editFormVisible: true, }); - await nextTick(); - - const editFormEl = wrapper.findComponent(IssuableEditForm); - expect(editFormEl.exists()).toBe(true); - expect(editFormEl.props()).toMatchObject({ + expect(findIssuableEditForm().exists()).toBe(true); + expect(findIssuableEditForm().props()).toMatchObject({ issuable: issuableBodyProps.issuable, enableAutocomplete: issuableBodyProps.enableAutocomplete, descriptionPreviewPath: issuableBodyProps.descriptionPreviewPath, descriptionHelpPath: issuableBodyProps.descriptionHelpPath, }); - expect(editFormEl.find('button.js-save').exists()).toBe(true); - expect(editFormEl.find('button.js-cancel').exists()).toBe(true); + expect(findIssuableEditFormButton('save').exists()).toBe(true); + expect(findIssuableEditFormButton('cancel').exists()).toBe(true); }); describe('events', () => { it('component emits `edit-issuable` event bubbled via issuable-title', () => { - const issuableTitle = wrapper.findComponent(IssuableTitle); - - issuableTitle.vm.$emit('edit-issuable'); + findIssuableTitle().vm.$emit('edit-issuable'); expect(wrapper.emitted('edit-issuable')).toHaveLength(1); }); it.each(['keydown-title', 'keydown-description'])( 'component emits `%s` event with event object and issuableMeta params via issuable-edit-form', - async (eventName) => { + (eventName) => { const eventObj = { preventDefault: jest.fn(), stopPropagation: jest.fn(), @@ -213,15 +184,11 @@ describe('IssuableBody', () => { issuableDescription: 'foobar', }; - wrapper.setProps({ + createComponent({ editFormVisible: true, }); - await nextTick(); - - const issuableEditForm = wrapper.findComponent(IssuableEditForm); - - issuableEditForm.vm.$emit(eventName, eventObj, issuableMeta); + findIssuableEditForm().vm.$emit(eventName, eventObj, issuableMeta); expect(wrapper.emitted(eventName)).toHaveLength(1); expect(wrapper.emitted(eventName)[0]).toMatchObject([eventObj, issuableMeta]); diff --git a/spec/initializers/net_http_response_patch_spec.rb b/spec/initializers/net_http_response_patch_spec.rb index 3bd0d8c3907..eee0747a02a 100644 --- a/spec/initializers/net_http_response_patch_spec.rb +++ b/spec/initializers/net_http_response_patch_spec.rb @@ -2,15 +2,15 @@ require 'spec_helper' -RSpec.describe 'Net::HTTPResponse patch header read timeout' do +RSpec.describe 'Net::HTTPResponse patch header read timeout', feature_category: :integrations do describe '.each_response_header' do let(:server_response) do - <<~EOS + <<~HTTP Content-Type: text/html Header-Two: foo Hello World - EOS + HTTP end before do @@ -30,14 +30,12 @@ RSpec.describe 'Net::HTTPResponse patch header read timeout' do end context 'when the response contains many consecutive spaces' do - before do + it 'has no regex backtracking issues' do expect(socket).to receive(:readuntil).and_return( "a: #{' ' * 100_000} b", '' ) - end - it 'has no regex backtracking issues' do Timeout.timeout(1) do each_response_header end diff --git a/spec/models/blob_viewer/package_json_spec.rb b/spec/models/blob_viewer/package_json_spec.rb index 1dcba3bcb4f..781623c0d3d 100644 --- a/spec/models/blob_viewer/package_json_spec.rb +++ b/spec/models/blob_viewer/package_json_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe BlobViewer::PackageJson do +RSpec.describe BlobViewer::PackageJson, feature_category: :source_code_management do include FakeBlobHelpers let(:project) { build_stubbed(:project) } @@ -59,6 +59,17 @@ RSpec.describe BlobViewer::PackageJson do expect(subject.manager_url).to eq("https://yarnpkg.com/") end end + + context 'when json is an array' do + let(:data) { '[]' } + + it 'does not raise an error', :aggregate_failures do + expect(subject).to receive(:prepare!) + + expect { subject.yarn? }.not_to raise_error + expect(subject.yarn?).to be_falsey + end + end end context 'npm' do diff --git a/spec/tooling/lib/tooling/find_tests_spec.rb b/spec/tooling/lib/tooling/find_tests_spec.rb new file mode 100644 index 00000000000..997d9d9a746 --- /dev/null +++ b/spec/tooling/lib/tooling/find_tests_spec.rb @@ -0,0 +1,155 @@ +# frozen_string_literal: true + +require 'tempfile' +require_relative '../../../../tooling/lib/tooling/find_tests' +require_relative '../../../support/helpers/stub_env' + +RSpec.describe Tooling::FindTests, feature_category: :tooling do + include StubENV + + attr_accessor :changes_file, :matching_tests_paths + + let(:instance) { described_class.new(changes_file, matching_tests_paths) } + let(:mock_test_file_finder) { instance_double(TestFileFinder::FileFinder) } + let(:new_matching_tests) { ["new_matching_spec.rb"] } + let(:changes_file_content) { "changed_file1 changed_file2" } + let(:matching_tests_paths_content) { "previously_matching_spec.rb" } + + around do |example| + self.changes_file = Tempfile.new('changes') + self.matching_tests_paths = Tempfile.new('matching_tests') + + # See https://ruby-doc.org/stdlib-1.9.3/libdoc/tempfile/rdoc/ + # Tempfile.html#class-Tempfile-label-Explicit+close + begin + example.run + ensure + changes_file.close + matching_tests_paths.close + changes_file.unlink + matching_tests_paths.unlink + end + end + + before do + allow(mock_test_file_finder).to receive(:use) + allow(mock_test_file_finder).to receive(:test_files).and_return(new_matching_tests) + allow(TestFileFinder::FileFinder).to receive(:new).and_return(mock_test_file_finder) + + stub_env( + 'RSPEC_TESTS_MAPPING_ENABLED' => nil, + 'RSPEC_TESTS_MAPPING_PATH' => '/tmp/does-not-exist.out' + ) + + # We write into the temp files initially, to later check how the code modified those files + File.write(changes_file, changes_file_content) + File.write(matching_tests_paths, matching_tests_paths_content) + end + + describe '#execute' do + subject { instance.execute } + + context 'when the matching_tests_paths file does not exist' do + before do + allow(File).to receive(:exist?).and_return(false) + allow(File).to receive(:write).with(matching_tests_paths, any_args) + end + + it 'creates an empty file' do + expect(File).to receive(:write).with(matching_tests_paths, '') + + subject + end + end + + context 'when the matching_tests_paths file already exists' do + it 'does not create an empty file' do + expect(File).not_to receive(:write).with(matching_tests_paths, '') + + subject + end + end + + it 'does not modify the content of the input file' do + expect { subject }.not_to change { File.read(changes_file) } + end + + it 'does not overwrite the output file' do + expect { subject }.to change { File.read(matching_tests_paths) } + .from(matching_tests_paths_content) + .to("#{matching_tests_paths_content} #{new_matching_tests.uniq.join(' ')}") + end + + it 'loads the tests.yml file with a pattern matching mapping' do + expect(TestFileFinder::MappingStrategies::PatternMatching).to receive(:load).with('tests.yml') + + subject + end + + context 'when RSPEC_TESTS_MAPPING_ENABLED env variable is set' do + before do + stub_env( + 'RSPEC_TESTS_MAPPING_ENABLED' => 'true', + 'RSPEC_TESTS_MAPPING_PATH' => 'crystalball-test/mapping.json' + ) + end + + it 'loads the direct matching pattern file' do + expect(TestFileFinder::MappingStrategies::DirectMatching) + .to receive(:load_json) + .with('crystalball-test/mapping.json') + + subject + end + end + + context 'when RSPEC_TESTS_MAPPING_ENABLED env variable is not set' do + let(:rspec_tests_mapping_enabled) { '' } + + before do + stub_env( + 'RSPEC_TESTS_MAPPING_ENABLED' => rspec_tests_mapping_enabled, + 'RSPEC_TESTS_MAPPING_PATH' => rspec_tests_mapping_path + ) + end + + context 'when RSPEC_TESTS_MAPPING_PATH is set' do + let(:rspec_tests_mapping_path) { 'crystalball-test/mapping.json' } + + it 'does not load the direct matching pattern file' do + expect(TestFileFinder::MappingStrategies::DirectMatching).not_to receive(:load_json) + + subject + end + end + + context 'when RSPEC_TESTS_MAPPING_PATH is not set' do + let(:rspec_tests_mapping_path) { nil } + + it 'does not load the direct matching pattern file' do + expect(TestFileFinder::MappingStrategies::DirectMatching).not_to receive(:load_json) + + subject + end + end + end + + context 'when the same spec is matching multiple times' do + let(:new_matching_tests) do + [ + "new_matching_spec.rb", + "duplicate_spec.rb", + "duplicate_spec.rb" + ] + end + + it 'writes uniquely matching specs to the output' do + subject + + expect(File.read(matching_tests_paths).split(' ')).to match_array( + matching_tests_paths_content.split(' ') + new_matching_tests.uniq + ) + end + end + end +end diff --git a/spec/tooling/lib/tooling/mappings/view_to_js_mappings_spec.rb b/spec/tooling/lib/tooling/mappings/view_to_js_mappings_spec.rb index eaa0124370d..1856069f69c 100644 --- a/spec/tooling/lib/tooling/mappings/view_to_js_mappings_spec.rb +++ b/spec/tooling/lib/tooling/mappings/view_to_js_mappings_spec.rb @@ -230,12 +230,12 @@ RSpec.describe Tooling::Mappings::ViewToJsMappings, feature_category: :tooling d = render partial: "subfolder/my-partial4" = render(partial:"subfolder/my-partial5", path: 'else') = render partial:"subfolder/my-partial6" - = render_if_exist("subfolder/my-partial7", path: 'else') - = render_if_exist "subfolder/my-partial8" - = render_if_exist(partial: "subfolder/my-partial9", path: 'else') - = render_if_exist partial: "subfolder/my-partial10" - = render_if_exist(partial:"subfolder/my-partial11", path: 'else') - = render_if_exist partial:"subfolder/my-partial12" + = render_if_exists("subfolder/my-partial7", path: 'else') + = render_if_exists "subfolder/my-partial8" + = render_if_exists(partial: "subfolder/my-partial9", path: 'else') + = render_if_exists partial: "subfolder/my-partial10" + = render_if_exists(partial:"subfolder/my-partial11", path: 'else') + = render_if_exists partial:"subfolder/my-partial12" End of file FILE diff --git a/spec/views/layouts/application.html.haml_spec.rb b/spec/views/layouts/application.html.haml_spec.rb index 527ba1498b9..d4d40a9ade9 100644 --- a/spec/views/layouts/application.html.haml_spec.rb +++ b/spec/views/layouts/application.html.haml_spec.rb @@ -6,10 +6,6 @@ RSpec.describe 'layouts/application' do let(:user) { create(:user) } before do - allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings) - allow(view).to receive(:experiment_enabled?).and_return(false) - allow(view).to receive(:session).and_return({}) - allow(view).to receive(:user_signed_in?).and_return(true) allow(view).to receive(:current_user).and_return(user) allow(view).to receive(:current_user_mode).and_return(Gitlab::Auth::CurrentUserMode.new(user)) end diff --git a/tooling/bin/find_tests b/tooling/bin/find_tests index 33834064f36..31afa4d598b 100755 --- a/tooling/bin/find_tests +++ b/tooling/bin/find_tests @@ -1,19 +1,9 @@ #!/usr/bin/env ruby # frozen_string_literal: true -require 'test_file_finder' +require_relative '../lib/tooling/find_tests' -changes = ARGV.shift -output_file = ARGV.shift +changes_file = ARGV.shift +matching_tests_paths = ARGV.shift -changed_files = File.read(changes).split(' ') - -tff = TestFileFinder::FileFinder.new(paths: changed_files).tap do |file_finder| - file_finder.use TestFileFinder::MappingStrategies::PatternMatching.load('tests.yml') - - if ENV['RSPEC_TESTS_MAPPING_ENABLED'] - file_finder.use TestFileFinder::MappingStrategies::DirectMatching.load_json(ENV['RSPEC_TESTS_MAPPING_PATH']) - end -end - -File.write(output_file, tff.test_files.uniq.join(' ')) +Tooling::FindTests.new(changes_file, matching_tests_paths).execute diff --git a/tooling/lib/tooling/find_tests.rb b/tooling/lib/tooling/find_tests.rb new file mode 100644 index 00000000000..4acbb5fcd6a --- /dev/null +++ b/tooling/lib/tooling/find_tests.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require 'test_file_finder' + +module Tooling + class FindTests + def initialize(changes_file, matching_tests_paths) + @matching_tests_paths = matching_tests_paths + @changed_files = File.read(changes_file).split(' ') + + File.write(matching_tests_paths, '') unless File.exist?(matching_tests_paths) + + @matching_tests = File.read(matching_tests_paths).split(' ') + end + + def execute + tff = TestFileFinder::FileFinder.new(paths: changed_files).tap do |file_finder| + file_finder.use TestFileFinder::MappingStrategies::PatternMatching.load('tests.yml') + + if ENV['RSPEC_TESTS_MAPPING_ENABLED'] == 'true' + file_finder.use TestFileFinder::MappingStrategies::DirectMatching.load_json(ENV['RSPEC_TESTS_MAPPING_PATH']) + end + end + + new_matching_tests = tff.test_files.uniq + File.write(matching_tests_paths, (matching_tests + new_matching_tests).join(' ')) + end + + private + + attr_reader :changed_files, :matching_tests, :matching_tests_paths + end +end diff --git a/tooling/lib/tooling/mappings/view_to_js_mappings.rb b/tooling/lib/tooling/mappings/view_to_js_mappings.rb index db80eb9bfe8..bf305d6738d 100644 --- a/tooling/lib/tooling/mappings/view_to_js_mappings.rb +++ b/tooling/lib/tooling/mappings/view_to_js_mappings.rb @@ -11,7 +11,7 @@ module Tooling HTML_ATTRIBUTE_VALUE_REGEXP = /js-[-\w]+/.freeze # Search for Rails partials included in an HTML file - RAILS_PARTIAL_INVOCATION_REGEXP = %r{(?:render|render_if_exist)(?: |\()(?:partial: ?)?['"]([\w/-]+)['"]}.freeze + RAILS_PARTIAL_INVOCATION_REGEXP = %r{(?:render|render_if_exists)(?: |\()(?:partial: ?)?['"]([\w/-]+)['"]}.freeze def initialize(view_base_folder: 'app/views', js_base_folder: 'app/assets/javascripts') @view_base_folders = folders_for_available_editions(view_base_folder) |