diff --git a/.gitlab/ci/release-environments/main.gitlab-ci.yml b/.gitlab/ci/release-environments/main.gitlab-ci.yml
index b343fac9f60..8400208301c 100644
--- a/.gitlab/ci/release-environments/main.gitlab-ci.yml
+++ b/.gitlab/ci/release-environments/main.gitlab-ci.yml
@@ -18,6 +18,10 @@ stages:
- GIT_DEPTH
- GIT_STRATEGY
+workflow:
+ auto_cancel:
+ on_new_commit: none
+
variables:
GIT_DEPTH: 20
GIT_STRATEGY: fetch
@@ -46,6 +50,15 @@ release-environments-deploy-env:
expire_in: 7 days
when: always
+release-environments-update-resource-group:
+ stage: prepare
+ script:
+ # Make sure pipelines run in order
+ # See https://docs.gitlab.com/ee/ci/resource_groups/index.html#change-the-process-mode
+ - |
+ curl --request PUT --data "process_mode=oldest_first" --header "PRIVATE-TOKEN:${ENVIRONMENT_API_TOKEN}" \
+ "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/resource_groups/release-environment-${CI_COMMIT_REF_SLUG}"
+
release-environments-notification-start:
stage: start
extends: .inherit_variables
@@ -67,6 +80,7 @@ release-environments-deploy:
branch: main
strategy: depend
needs: ["release-environments-deploy-env"]
+ resource_group: release-environment-${CI_COMMIT_REF_SLUG}
release-environments-qa:
stage: qa
@@ -80,6 +94,7 @@ release-environments-qa:
GITLAB_INITIAL_ROOT_PASSWORD: "${RELEASE_ENVIRONMENTS_ROOT_PASSWORD}"
QA_PRAEFECT_REPOSITORY_STORAGE: "default"
SIGNUP_DISABLED: "true"
+ resource_group: release-environment-${CI_COMMIT_REF_SLUG}
release-environments-notification-failure:
stage: finish
diff --git a/.rubocop_todo/layout/argument_alignment.yml b/.rubocop_todo/layout/argument_alignment.yml
index 92efe9f06e8..f29ebfdf96a 100644
--- a/.rubocop_todo/layout/argument_alignment.yml
+++ b/.rubocop_todo/layout/argument_alignment.yml
@@ -51,18 +51,6 @@ Layout/ArgumentAlignment:
- 'ee/app/graphql/mutations/security_policy/assign_security_policy_project.rb'
- 'ee/app/graphql/mutations/security_policy/commit_scan_execution_policy.rb'
- 'ee/app/graphql/mutations/security_policy/create_security_policy_project.rb'
- - 'ee/app/graphql/types/dast/profile_branch_type.rb'
- - 'ee/app/graphql/types/dast/profile_cadence_input_type.rb'
- - 'ee/app/graphql/types/dast/profile_cadence_type.rb'
- - 'ee/app/graphql/types/dast/profile_schedule_input_type.rb'
- - 'ee/app/graphql/types/dast/profile_type.rb'
- - 'ee/app/graphql/types/dast/site_profile_auth_input_type.rb'
- - 'ee/app/graphql/types/dast/site_profile_auth_type.rb'
- - 'ee/app/graphql/types/dast_scanner_profile_type.rb'
- - 'ee/app/graphql/types/dast_site_profile_type.rb'
- - 'ee/app/graphql/types/dast_site_validation_type.rb'
- - 'ee/app/graphql/types/deployments/approval_status_enum.rb'
- - 'ee/app/graphql/types/deployments/approval_summary_status_enum.rb'
- 'ee/app/graphql/types/deployments/approval_summary_type.rb'
- 'ee/app/graphql/types/deployments/approval_type.rb'
- 'ee/app/graphql/types/epic_descendant_weight_sum_type.rb'
diff --git a/.rubocop_todo/lint/symbol_conversion.yml b/.rubocop_todo/lint/symbol_conversion.yml
index 7c11d4014fe..c79852103d5 100644
--- a/.rubocop_todo/lint/symbol_conversion.yml
+++ b/.rubocop_todo/lint/symbol_conversion.yml
@@ -115,7 +115,6 @@ Lint/SymbolConversion:
- 'lib/gitlab/metrics/transaction.rb'
- 'lib/gitlab/usage_data/topology.rb'
- 'lib/gitlab/usage_data_counters/base_counter.rb'
- - 'lib/gitlab/usage_data_counters/note_counter.rb'
- 'lib/peek/views/detailed_view.rb'
- 'qa/qa/ee/page/workspace/action.rb'
- 'qa/qa/ee/page/workspace/list.rb'
diff --git a/.rubocop_todo/rspec/feature_category.yml b/.rubocop_todo/rspec/feature_category.yml
index e1b8fd50f4e..736f20f1984 100644
--- a/.rubocop_todo/rspec/feature_category.yml
+++ b/.rubocop_todo/rspec/feature_category.yml
@@ -3963,9 +3963,7 @@ RSpec/FeatureCategory:
- 'spec/lib/gitlab/usage_data_counters/jetbrains_plugin_activity_unique_counter_spec.rb'
- 'spec/lib/gitlab/usage_data_counters/kubernetes_agent_counter_spec.rb'
- 'spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb'
- - 'spec/lib/gitlab/usage_data_counters/merge_request_counter_spec.rb'
- 'spec/lib/gitlab/usage_data_counters/merge_request_widget_extension_counter_spec.rb'
- - 'spec/lib/gitlab/usage_data_counters/note_counter_spec.rb'
- 'spec/lib/gitlab/usage_data_counters/package_event_counter_spec.rb'
- 'spec/lib/gitlab/usage_data_counters/quick_action_activity_unique_counter_spec.rb'
- 'spec/lib/gitlab/usage_data_counters/redis_counter_spec.rb'
diff --git a/.rubocop_todo/style/class_and_module_children.yml b/.rubocop_todo/style/class_and_module_children.yml
index f05a2c61859..810beef953d 100644
--- a/.rubocop_todo/style/class_and_module_children.yml
+++ b/.rubocop_todo/style/class_and_module_children.yml
@@ -505,7 +505,6 @@ Style/ClassAndModuleChildren:
- 'lib/gitlab/instrumentation/elasticsearch_transport.rb'
- 'lib/gitlab/usage_data_counters/base_counter.rb'
- 'lib/gitlab/usage_data_counters/ci_template_unique_counter.rb'
- - 'lib/gitlab/usage_data_counters/note_counter.rb'
- 'lib/release_highlights/validator/entry.rb'
- 'qa/qa/page/component/project/templates.rb'
- 'scripts/perf/gc/print_gc_stats.rb'
diff --git a/app/assets/javascripts/ci/job_details/index.js b/app/assets/javascripts/ci/job_details/index.js
index a0c6971a939..7e14572b717 100644
--- a/app/assets/javascripts/ci/job_details/index.js
+++ b/app/assets/javascripts/ci/job_details/index.js
@@ -33,6 +33,7 @@ export const initJobDetails = () => {
testReportSummaryUrl,
pipelineTestReportUrl,
logViewerPath,
+ duoFeaturesEnabled,
} = el.dataset;
const fullScreenAPIAvailable = document.fullscreenEnabled;
@@ -55,6 +56,7 @@ export const initJobDetails = () => {
projectPath,
retryOutdatedJobDocsUrl,
aiRootCauseAnalysisAvailable: parseBoolean(aiRootCauseAnalysisAvailable),
+ duoFeaturesEnabled: parseBoolean(duoFeaturesEnabled),
pipelineTestReportUrl,
},
render(h) {
diff --git a/app/assets/javascripts/issues/show/components/header_actions.vue b/app/assets/javascripts/issues/show/components/header_actions.vue
index 7c4b4838a3d..5007818f4fe 100644
--- a/app/assets/javascripts/issues/show/components/header_actions.vue
+++ b/app/assets/javascripts/issues/show/components/header_actions.vue
@@ -207,6 +207,14 @@ export default {
showDropdownTooltip() {
return !this.isDesktopDropdownVisible ? this.dropdownText : '';
},
+ promoteToEpicItem() {
+ return {
+ text: __('Promote to epic'),
+ extraAttrs: {
+ disabled: this.isToggleStateButtonLoading,
+ },
+ };
+ },
},
created() {
eventHub.$on('toggle.issuable.state', this.toggleIssueState);
@@ -361,9 +369,11 @@ export default {
{{ buttonText }}
-
- {{ __('Promote to epic') }}
-
+
@@ -461,12 +471,10 @@ export default {
- {{ __('Promote to epic') }}
-
+ />
diff --git a/app/assets/stylesheets/framework/popup.scss b/app/assets/stylesheets/framework/popup.scss
index c31c317ff21..9789a404639 100644
--- a/app/assets/stylesheets/framework/popup.scss
+++ b/app/assets/stylesheets/framework/popup.scss
@@ -1,13 +1,13 @@
.popup {
@include triangle(
- $gray-lighter,
+ $gray-10,
$gray-50,
$popup-triangle-size,
$popup-triangle-border-size
);
padding: $gl-padding;
- background-color: $gray-lighter;
+ background-color: $gray-10;
border: 1px solid $gray-50;
border-radius: $gl-border-radius-base;
box-shadow: 0 5px 8px $popup-box-shadow-color;
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index f680a21881b..7399289df5e 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -87,7 +87,6 @@ $purple: #6d49cb !default;
$purple-light: #ede8fb !default;
$gray-light: $gray-10 !default;
-$gray-lighter: lighten($gray-50, 4) !default;
/*
* UI elements
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index c93a7619a07..0fc872300e2 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -12,7 +12,7 @@
.issue-token-reference {
margin-right: 1px;
- background-color: $gray-lighter;
+ background-color: $gray-10;
transition: background $general-hover-transition-duration $general-hover-transition-curve, color $general-hover-transition-duration $general-hover-transition-curve;
.issue-token:hover &,
diff --git a/app/assets/stylesheets/themes/_dark.scss b/app/assets/stylesheets/themes/_dark.scss
index 62da46921bd..dcee17c6c27 100644
--- a/app/assets/stylesheets/themes/_dark.scss
+++ b/app/assets/stylesheets/themes/_dark.scss
@@ -1,7 +1,6 @@
@import '@gitlab/ui/src/tokens/build/scss/tokens.dark';
$gray-light: lighten($gray-10, 2);
-$gray-lighter: darken($gray-50, 4);
$black-normal: $gray-900;
diff --git a/app/services/merge_requests/after_create_service.rb b/app/services/merge_requests/after_create_service.rb
index 7bb4bea1a31..fdbcc03f288 100644
--- a/app/services/merge_requests/after_create_service.rb
+++ b/app/services/merge_requests/after_create_service.rb
@@ -43,7 +43,11 @@ module MergeRequests
todo_service.new_merge_request(merge_request, current_user)
merge_request.cache_merge_request_closes_issues!(current_user)
- Gitlab::UsageDataCounters::MergeRequestCounter.count(:create)
+ Gitlab::InternalEvents.track_event(
+ 'create_merge_request',
+ user: current_user,
+ project: merge_request.target_project
+ )
link_lfs_objects(merge_request)
end
diff --git a/app/services/notes/base_service.rb b/app/services/notes/base_service.rb
index 6c64f5b586c..8265fda10a1 100644
--- a/app/services/notes/base_service.rb
+++ b/app/services/notes/base_service.rb
@@ -13,12 +13,13 @@ module Notes
end
def increment_usage_counter(note)
- if note.noteable_type == 'Commit'
+ case note.noteable_type
+ when 'Commit'
track_internal_event('create_commit_note', project: project, user: current_user)
- elsif note.noteable_type == 'Snippet'
+ when 'Snippet'
track_internal_event('create_snippet_note', project: project, user: current_user)
- else
- Gitlab::UsageDataCounters::NoteCounter.count(:create, note.noteable_type)
+ when 'MergeRequest'
+ track_internal_event('create_merge_request_note', project: project, user: current_user)
end
end
end
diff --git a/config/events/create_merge_request.yml b/config/events/create_merge_request.yml
new file mode 100644
index 00000000000..d51c517ae33
--- /dev/null
+++ b/config/events/create_merge_request.yml
@@ -0,0 +1,20 @@
+---
+description: Tracks creation of merge requests
+internal_events: true
+action: create_merge_request
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: create
+product_group: code_review
+milestone: '17.1'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151474
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
diff --git a/config/events/create_merge_request_note.yml b/config/events/create_merge_request_note.yml
new file mode 100644
index 00000000000..b58207f41d3
--- /dev/null
+++ b/config/events/create_merge_request_note.yml
@@ -0,0 +1,20 @@
+---
+description: A note was created for a merge request
+internal_events: true
+action: create_merge_request_note
+identifiers:
+- project
+- user
+- namespace
+product_section: dev
+product_stage: create
+product_group: code_review
+milestone: '17.1'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151474
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_all/20210216175041_merge_request_comment.yml b/config/metrics/counts_all/20210216175041_merge_request_comment.yml
index 312f486aa67..e069709db72 100644
--- a/config/metrics/counts_all/20210216175041_merge_request_comment.yml
+++ b/config/metrics/counts_all/20210216175041_merge_request_comment.yml
@@ -8,11 +8,9 @@ product_group: code_review
value_type: number
status: active
time_frame: all
-data_source: redis
-instrumentation_class: RedisMetric
-options:
- prefix: note
- event: create_mergerequest
+data_source: internal_events
+events:
+- name: create_merge_request_note
distribution:
- ce
- ee
diff --git a/config/metrics/counts_all/20210216175043_merge_request_create.yml b/config/metrics/counts_all/20210216175043_merge_request_create.yml
index 2f524d9030a..471812eadfa 100644
--- a/config/metrics/counts_all/20210216175043_merge_request_create.yml
+++ b/config/metrics/counts_all/20210216175043_merge_request_create.yml
@@ -8,11 +8,9 @@ product_group: code_review
value_type: number
status: active
time_frame: all
-data_source: redis
-instrumentation_class: RedisMetric
-options:
- prefix: merge_request
- event: create
+data_source: internal_events
+events:
+- name: create_merge_request
distribution:
- ce
- ee
diff --git a/db/docs/agent_activity_events.yml b/db/docs/agent_activity_events.yml
index 260a3e9d1a4..d5b966347e3 100644
--- a/db/docs/agent_activity_events.yml
+++ b/db/docs/agent_activity_events.yml
@@ -7,5 +7,15 @@ feature_categories:
description: Historical timeline events belonging to a cluster agent
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74577
milestone: '14.6'
-gitlab_schema: gitlab_main
-sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/461521
+gitlab_schema: gitlab_main_cell
+allow_cross_foreign_keys:
+- gitlab_main_clusterwide
+desired_sharding_key:
+ agent_project_id:
+ references: projects
+ backfill_via:
+ parent:
+ foreign_key: agent_id
+ table: cluster_agents
+ sharding_key: project_id
+ belongs_to: agent
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 21accb57210..f92146914c4 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -18979,10 +18979,10 @@ Represents a product analytics dashboard.
| `configurationProject` | [`Project`](#project) | Project which contains the dashboard definition. |
| `description` | [`String`](#string) | Description of the dashboard. |
| `errors` | [`[String!]`](#string) | Errors on yaml definition. |
-| `panels` | [`CustomizableDashboardPanelConnection!`](#customizabledashboardpanelconnection) | Panels shown on the dashboard. (see [Connections](#connections)) |
+| `panels` | [`CustomizableDashboardPanelConnection`](#customizabledashboardpanelconnection) | Panels shown on the dashboard. (see [Connections](#connections)) |
| `slug` | [`String!`](#string) | Slug of the dashboard. |
| `status` **{warning-solid}** | [`String`](#string) | **Introduced** in GitLab 17.0. **Status**: Experiment. Status of the dashboard. |
-| `title` | [`String!`](#string) | Title of the dashboard. |
+| `title` | [`String`](#string) | Title of the dashboard. |
| `userDefined` | [`Boolean!`](#boolean) | Indicates whether the dashboard is user-defined or provided by GitLab. |
### `CustomizableDashboardPanel`
@@ -18995,8 +18995,8 @@ Represents a product analytics dashboard panel.
| ---- | ---- | ----------- |
| `gridAttributes` | [`JSON`](#json) | Description of the position and size of the panel. |
| `queryOverrides` | [`JSON`](#json) | Overrides for the visualization query object. |
-| `title` | [`String!`](#string) | Title of the panel. |
-| `visualization` | [`CustomizableDashboardVisualization!`](#customizabledashboardvisualization) | Visualization of the panel. |
+| `title` | [`String`](#string) | Title of the panel. |
+| `visualization` | [`CustomizableDashboardVisualization`](#customizabledashboardvisualization) | Visualization of the panel. |
### `CustomizableDashboardVisualization`
diff --git a/doc/user/ai_features.md b/doc/user/ai_features.md
index 796d040d48d..555544c9c13 100644
--- a/doc/user/ai_features.md
+++ b/doc/user/ai_features.md
@@ -43,9 +43,18 @@ DETAILS:
- Processes and generates text and code in a conversational manner.
Helps you quickly identify useful information in large volumes of text in issues, epics, code, and GitLab documentation.
-- LLM: Anthropic [`claude-3-sonnet-20240229`](https://docs.anthropic.com/en/docs/models-overview#claude-3-a-new-generation-of-ai) and Vertex AI Codey [`textembedding-gecko`](https://cloud.google.com/vertex-ai/generative-ai/docs/embeddings/get-text-embeddings)
+- LLM: Anthropic [`claude-3-sonnet-20240229`](https://docs.anthropic.com/en/docs/models-overview#claude-3-a-new-generation-of-ai),
+ Anthropic [`claude-3-haiku-20240307`](https://docs.anthropic.com/en/docs/models-overview#claude-3-a-new-generation-of-ai),
+ [`claude-2.1`](https://docs.anthropic.com/en/docs/legacy-model-guide#anthropics-legacy-models),
+ and [Vertex AI Search](https://cloud.google.com/enterprise-search).
- [View documentation](gitlab_duo_chat.md).
+NOTE:
+The LLM for GitLab Duo Chat depends on the question asked. For more information, see
+the [Duo Chat examples](gitlab_duo_chat_examples.md).
+For self-managed, the models also depend on your GitLab version.
+For the most benefit, use the latest GitLab version whenever possible.
+
### Suggested Reviewers
DETAILS:
diff --git a/doc/user/analytics/analytics_dashboards.md b/doc/user/analytics/analytics_dashboards.md
index b901f76d296..e2330d8ea82 100644
--- a/doc/user/analytics/analytics_dashboards.md
+++ b/doc/user/analytics/analytics_dashboards.md
@@ -313,6 +313,10 @@ If the error persists:
- Check that your configurations match the [dashboard JSON schema](#define-a-dashboard) defined in `ee/app/validators/json_schemas/analytics_dashboard.json`.
- For product analytics, make sure your [admin and project settings](../product_analytics/index.md#project-level-settings) are set up correctly.
+### `Invalid dashboard configuration`
+
+If the dashboard displays a global error message that the configuration is invalid, check that your configurations match the [dashboard JSON schema](#define-a-dashboard) defined in `ee/app/validators/json_schemas/analytics_dashboard.json`.
+
### `Invalid visualization configuration`
If a dashboard panel displays a message that the visualization configuration is invalid,
diff --git a/doc/user/analytics/value_streams_dashboard.md b/doc/user/analytics/value_streams_dashboard.md
index d5dcbfc9ecc..403e5288426 100644
--- a/doc/user/analytics/value_streams_dashboard.md
+++ b/doc/user/analytics/value_streams_dashboard.md
@@ -276,10 +276,13 @@ After you have set up the project, set up the configuration file:
| `height` (subfield of `gridAttributes`) | Height of the panel |
```yaml
-# title - Change the title of the Value Streams Dashboard. [optional]
+# version - The latest version of the analytics dashboard schema
+version: '2'
+
+# title - Change the title of the Value Streams Dashboard.
title: 'Custom Dashboard title'
-# description - Change the description of the Value Streams Dashboard. [optional]
+# description - Change the description of the Value Streams Dashboard.
description: 'Custom description'
# panels - List of panels that contain panel settings.
diff --git a/doc/user/application_security/vulnerability_report/pipeline.md b/doc/user/application_security/vulnerability_report/pipeline.md
index 97857443282..ad8658c797d 100644
--- a/doc/user/application_security/vulnerability_report/pipeline.md
+++ b/doc/user/application_security/vulnerability_report/pipeline.md
@@ -75,11 +75,10 @@ For each finding you can:
- Create an issue for the finding.
- Dismiss the finding.
-When you merge the merge request's branch into the target branch, all reported findings are
-in the [vulnerability report](index.md). Scan results in pipelines executed on the
+When you merge the merge request's branch into the target branch, all reported findings from the next pipeline to run on the default branch are shown in the [vulnerability report](index.md). Scan results in pipelines executed on the
default branch are incorporated after the pipeline finishes, according to the following table:
-| Existing vulnerability status | Dismissed in pipeline? | New vulnerability status |
+| Existing vulnerability status | Dismissed from pipeline security tab? | New vulnerability status |
|:------------------------------|:-----------------------|:-------------------------|
| any | Yes | Dismissed |
| Dismissed | any | Dismissed |
diff --git a/doc/user/gitlab_duo_chat_examples.md b/doc/user/gitlab_duo_chat_examples.md
index ddd87a6d913..ed1dbc04460 100644
--- a/doc/user/gitlab_duo_chat_examples.md
+++ b/doc/user/gitlab_duo_chat_examples.md
@@ -17,7 +17,7 @@ DETAILS:
**Tier:** Freely available for Premium and Ultimate for a limited time. In the future, will require Premium or Ultimate with [GitLab Duo Pro or Duo Enterprise](../subscriptions/subscription-add-ons.md).
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
**Editors:** GitLab UI, Web IDE, VS Code, and JetBrains IDEs
-**LLMs:** Anthopic: [`claude-3-sonnet-20240229`](https://docs.anthropic.com/en/docs/models-overview#claude-3-a-new-generation-of-ai), Vertex AI Codey [`textembedding-gecko`](https://cloud.google.com/vertex-ai/generative-ai/docs/embeddings/get-text-embeddings)
+**LLMs:** Anthopic: [`claude-3-sonnet-20240229`](https://docs.anthropic.com/en/docs/models-overview#claude-3-a-new-generation-of-ai), [Vertex AI Search](https://cloud.google.com/enterprise-search)
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/117695) for GitLab.com in GitLab 16.0.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/451215) ability to ask doc-related questions on self-managed in GitLab 17.0 [with a flag](../administration/feature_flags.md) named `ai_gateway_docs_search`. Disabled by default.
diff --git a/lib/gitlab/tracking/event_definition.rb b/lib/gitlab/tracking/event_definition.rb
index fc5d909679d..1be7714d9f9 100644
--- a/lib/gitlab/tracking/event_definition.rb
+++ b/lib/gitlab/tracking/event_definition.rb
@@ -67,6 +67,7 @@ module Gitlab
Error type: #{error['type']}
Data: #{error['data']}
Path: #{error['data_pointer']}
+ Details: #{error['details']}
ERROR_MSG
end
end
diff --git a/lib/gitlab/usage_data_counters.rb b/lib/gitlab/usage_data_counters.rb
index fd31a7d7c17..9e483ac9b84 100644
--- a/lib/gitlab/usage_data_counters.rb
+++ b/lib/gitlab/usage_data_counters.rb
@@ -4,10 +4,8 @@ module Gitlab
module UsageDataCounters
COUNTERS = [
PackageEventCounter,
- MergeRequestCounter,
DiffsCounter,
KubernetesAgentCounter,
- NoteCounter,
WebIdeCounter,
MergeRequestWidgetExtensionCounter
].freeze
diff --git a/lib/gitlab/usage_data_counters/merge_request_counter.rb b/lib/gitlab/usage_data_counters/merge_request_counter.rb
deleted file mode 100644
index e786e595f77..00000000000
--- a/lib/gitlab/usage_data_counters/merge_request_counter.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module UsageDataCounters
- class MergeRequestCounter < BaseCounter
- KNOWN_EVENTS = %w[create].freeze
- PREFIX = 'merge_request'
- end
- end
-end
diff --git a/lib/gitlab/usage_data_counters/note_counter.rb b/lib/gitlab/usage_data_counters/note_counter.rb
deleted file mode 100644
index 7059fb34683..00000000000
--- a/lib/gitlab/usage_data_counters/note_counter.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab::UsageDataCounters
- class NoteCounter < BaseCounter
- KNOWN_EVENTS = %w[create].freeze
- PREFIX = 'note'
- COUNTABLE_TYPES = %w[MergeRequest].freeze
-
- class << self
- def redis_key(event, noteable_type)
- "#{super(event)}_#{noteable_type}".upcase
- end
-
- def count(event, noteable_type)
- return unless countable?(noteable_type)
-
- increment(redis_key(event, noteable_type))
- end
-
- def read(event, noteable_type)
- return 0 unless countable?(noteable_type)
-
- total_count(redis_key(event, noteable_type))
- end
-
- def totals
- COUNTABLE_TYPES.to_h do |countable_type|
- [counter_key(countable_type), read(:create, countable_type)]
- end
- end
-
- def fallback_totals
- COUNTABLE_TYPES.to_h { |counter_key| [counter_key(counter_key), -1] }
- end
-
- private
-
- def counter_key(countable_type)
- "#{countable_type.underscore}_comment".to_sym
- end
-
- def countable?(noteable_type)
- COUNTABLE_TYPES.include?(noteable_type.to_s)
- end
- end
- end
-end
diff --git a/lib/gitlab/usage_data_counters/total_counter_redis_key_overrides.yml b/lib/gitlab/usage_data_counters/total_counter_redis_key_overrides.yml
index 7e5ef4f080a..c2ff692fec6 100644
--- a/lib/gitlab/usage_data_counters/total_counter_redis_key_overrides.yml
+++ b/lib/gitlab/usage_data_counters/total_counter_redis_key_overrides.yml
@@ -32,3 +32,5 @@
'{event_counters}_create_design_management_design': USAGE_DESIGN_MANAGEMENT_DESIGNS_CREATE
'{event_counters}_update_design_management_design': USAGE_DESIGN_MANAGEMENT_DESIGNS_UPDATE
'{event_counters}_delete_design_management_design': USAGE_DESIGN_MANAGEMENT_DESIGNS_DELETE
+'{event_counters}_create_merge_request': USAGE_MERGE_REQUEST_CREATE
+'{event_counters}_create_merge_request_note': USAGE_NOTE_CREATE_MERGEREQUEST
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index df1778b1d71..ef5bcf07585 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -5944,6 +5944,9 @@ msgstr ""
msgid "Analytics|Host"
msgstr ""
+msgid "Analytics|Invalid dashboard configuration"
+msgstr ""
+
msgid "Analytics|Invalid visualization configuration"
msgstr ""
@@ -14356,6 +14359,9 @@ msgstr ""
msgid "Containers"
msgstr ""
+msgid "Contains errors"
+msgstr ""
+
msgid "Contains only whitespace changes."
msgstr ""
diff --git a/qa/Gemfile b/qa/Gemfile
index 62a57335155..84e2e7c5e51 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -3,7 +3,7 @@
source 'https://rubygems.org'
gem 'gitlab-qa', '~> 14', '>= 14.10.0', require: 'gitlab/qa'
-gem 'gitlab_quality-test_tooling', '~> 1.25.0', require: false
+gem 'gitlab_quality-test_tooling', '~> 1.26.0', require: false
gem 'gitlab-utils', path: '../gems/gitlab-utils'
gem 'activesupport', '~> 7.0.8.1' # This should stay in sync with the root's Gemfile
gem 'allure-rspec', '~> 2.24.5'
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index 317b8ee3fb1..bb6f069f58f 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -138,7 +138,7 @@ GEM
rainbow (>= 3, < 4)
table_print (= 1.5.7)
zeitwerk (>= 2, < 3)
- gitlab_quality-test_tooling (1.25.0)
+ gitlab_quality-test_tooling (1.26.0)
activesupport (>= 6.1, < 7.2)
amatch (~> 0.4.1)
gitlab (~> 4.19)
@@ -378,7 +378,7 @@ DEPENDENCIES
gitlab-cng!
gitlab-qa (~> 14, >= 14.10.0)
gitlab-utils!
- gitlab_quality-test_tooling (~> 1.25.0)
+ gitlab_quality-test_tooling (~> 1.26.0)
influxdb-client (~> 3.1)
junit_merge (~> 0.1.2)
knapsack (~> 4.0)
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/license_detection_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/license_detection_spec.rb
index fe2caf8c494..fa51e07729c 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/license_detection_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/license_detection_spec.rb
@@ -35,7 +35,7 @@ module QA
end
end
- context 'on a project with an unrecognized LICENSE',
+ context 'on a project with an unrecognized LICENSE', :blocking,
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/366843' do
it_behaves_like 'project license detection' do
let(:license_file_name) { 'other' }
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_first_file_in_web_ide_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_first_file_in_web_ide_spec.rb
index 314585fd9e1..e76bc86bc89 100644
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_first_file_in_web_ide_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_first_file_in_web_ide_spec.rb
@@ -15,7 +15,7 @@ module QA
context 'when a file with the same name already exists' do
let(:file_name) { 'README.md' }
- it 'throws an error', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/432899' do
+ it 'throws an error', :blocking, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/432899' do
Page::Project::WebIDE::VSCode.perform do |ide|
ide.create_new_file(file_name)
diff --git a/qa/qa/support/api.rb b/qa/qa/support/api.rb
index c2a9740a02e..362cc9139a1 100644
--- a/qa/qa/support/api.rb
+++ b/qa/qa/support/api.rb
@@ -10,6 +10,7 @@ module QA
HTTP_STATUS_NO_CONTENT = 204
HTTP_STATUS_ACCEPTED = 202
HTTP_STATUS_PERMANENT_REDIRECT = 308
+ HTTP_STATUS_UNAUTHORIZED = 401
HTTP_STATUS_NOT_FOUND = 404
HTTP_STATUS_TOO_MANY_REQUESTS = 429
HTTP_STATUS_SERVER_ERROR = 500
diff --git a/qa/qa/tools/delete_projects.rb b/qa/qa/tools/delete_projects.rb
index cb77b94a1ce..8bb55182ae8 100644
--- a/qa/qa/tools/delete_projects.rb
+++ b/qa/qa/tools/delete_projects.rb
@@ -88,7 +88,7 @@ module QA
def resource_exists?(project)
response = get(resource_request(project))
- if response.code == 404
+ if response.code == HTTP_STATUS_NOT_FOUND
logger.info("Project #{project[:path_with_namespace]} is no longer available\n")
false
else
diff --git a/qa/qa/tools/delete_resource_base.rb b/qa/qa/tools/delete_resource_base.rb
index 191de6f2a80..f49f3a0f2c0 100644
--- a/qa/qa/tools/delete_resource_base.rb
+++ b/qa/qa/tools/delete_resource_base.rb
@@ -86,7 +86,7 @@ module QA
return log_failure(resource, response) unless mark_for_deletion_possible?(resource)
@permanently_delete ? delete_permanently(resource) : log_marked_for_deletion(resource)
- elsif response&.code == 404
+ elsif response&.code == HTTP_STATUS_NOT_FOUND
log_permanent_deletion(resource)
else
log_failure(resource, response)
@@ -98,6 +98,7 @@ module QA
unless user_response.code == HTTP_STATUS_OK
logger.error("Request for #{qa_username} returned (#{user_response.code}): `#{user_response}` ")
+ exit 1 if user_response.code == HTTP_STATUS_UNAUTHORIZED
return
end
@@ -135,6 +136,7 @@ module QA
resources.concat(parse_body(response).select { |r| Date.parse(r[:created_at]) < @delete_before })
else
logger.error("Request for #{@type} returned (#{response.code}): `#{response}` ")
+ exit 1 if response.code == HTTP_STATUS_UNAUTHORIZED
end
page_no = response.headers[:x_next_page].to_s
@@ -263,7 +265,7 @@ module QA
# @return [Boolean]
def permanently_deleted?(resource)
response = get(resource_request(resource))
- response.code == 404
+ response.code == HTTP_STATUS_NOT_FOUND
end
# Prints failed deletion attempts
@@ -305,11 +307,12 @@ module QA
def wait_for_resource_deletion(resource, permanent = false)
wait_until(max_duration: 60, sleep_interval: 1, raise_on_failure: false) do
response = get(resource_request(resource))
+ deleted = response&.code == HTTP_STATUS_NOT_FOUND
if permanent
- response&.code == 404
+ deleted
else
- response&.code == 404 || (success?(response&.code) && marked_for_deletion?(parse_body(response)))
+ deleted || (success?(response&.code) && marked_for_deletion?(parse_body(response)))
end
end
end
diff --git a/qa/qa/tools/delete_subgroups.rb b/qa/qa/tools/delete_subgroups.rb
index b33f4ce2a1f..96bd6723f88 100644
--- a/qa/qa/tools/delete_subgroups.rb
+++ b/qa/qa/tools/delete_subgroups.rb
@@ -89,7 +89,7 @@ module QA
def resource_exists?(subgroup)
response = get(resource_request(subgroup))
- if response.code == 404
+ if response.code == HTTP_STATUS_NOT_FOUND
logger.info("Subgroup #{subgroup[:full_path]} is no longer available\n")
false
else
diff --git a/qa/qa/tools/delete_user_projects.rb b/qa/qa/tools/delete_user_projects.rb
index 55f15644292..a6b1b8df836 100644
--- a/qa/qa/tools/delete_user_projects.rb
+++ b/qa/qa/tools/delete_user_projects.rb
@@ -111,6 +111,7 @@ module QA
def fetch_qa_username(user_id)
response = get Runtime::API::Request.new(@api_client, "/users/#{user_id}").url
+ exit 1 if response.code == HTTP_STATUS_UNAUTHORIZED
parsed_response = parse_body(response)
parsed_response[:username]
end
@@ -118,7 +119,7 @@ module QA
def resource_exists?(project)
response = get(resource_request(project))
- if response.code == 404
+ if response.code == HTTP_STATUS_NOT_FOUND
logger.info("Project #{project[:path_with_namespace]} is no longer available\n")
false
else
diff --git a/qa/qa/tools/lib/group.rb b/qa/qa/tools/lib/group.rb
index 9200f61396a..e4676c87fe5 100644
--- a/qa/qa/tools/lib/group.rb
+++ b/qa/qa/tools/lib/group.rb
@@ -11,8 +11,9 @@ module QA
group_search_response = get Runtime::API::Request.new(api_client, "/groups/#{group_name}").url
- if group_search_response.code != 200
+ if group_search_response.code != HTTP_STATUS_OK
logger.error("Response code #{group_search_response.code}: #{group_search_response.body}")
+ exit 1 if group_search_response.code == HTTP_STATUS_UNAUTHORIZED
return
end
diff --git a/spec/frontend/issues/show/components/header_actions_spec.js b/spec/frontend/issues/show/components/header_actions_spec.js
index 5c4d90e60c0..d42e6d51c00 100644
--- a/spec/frontend/issues/show/components/header_actions_spec.js
+++ b/spec/frontend/issues/show/components/header_actions_spec.js
@@ -137,6 +137,7 @@ describe('HeaderActions component', () => {
const findReportAbuseButton = () => wrapper.findByTestId('report-abuse-item');
const findCopyRefenceDropdownItem = () => wrapper.findByTestId('copy-reference');
const findCopyEmailItem = () => wrapper.findByTestId('copy-email');
+ const findPromoteToEpicButton = () => wrapper.findByTestId('promote-button');
const findModal = () => wrapper.findComponent(GlModal);
@@ -359,7 +360,35 @@ describe('HeaderActions component', () => {
});
});
- describe('when "Promote to epic" button is clicked', () => {
+ describe('"Promote to epic" button behavior', () => {
+ describe('default', () => {
+ it('the button is enabled when the user can promote to epic', () => {
+ wrapper = mountComponent();
+
+ expect(findPromoteToEpicButton().props('item')).toMatchObject({
+ extraAttrs: { disabled: false },
+ text: 'Promote to epic',
+ });
+ });
+ });
+
+ describe('when request is in flight', () => {
+ it('disables the promote option', async () => {
+ wrapper = mountComponent({
+ promoteToEpicHandler: promoteToEpicMutationSuccessResponseHandler,
+ });
+
+ findPromoteToEpicButton().vm.$emit('action');
+
+ await nextTick();
+
+ expect(findPromoteToEpicButton().props('item')).toMatchObject({
+ extraAttrs: { disabled: true },
+ text: 'Promote to epic',
+ });
+ });
+ });
+
describe('when response is successful', () => {
beforeEach(async () => {
visitUrlSpy = jest.spyOn(urlUtility, 'visitUrl').mockReturnValue({});
diff --git a/spec/lib/gitlab/usage_data_counters/merge_request_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/merge_request_counter_spec.rb
deleted file mode 100644
index d16c73e9312..00000000000
--- a/spec/lib/gitlab/usage_data_counters/merge_request_counter_spec.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::UsageDataCounters::MergeRequestCounter do
- it_behaves_like 'a redis usage counter', 'Merge Request', :create
-
- it_behaves_like 'a redis usage counter with totals', :merge_request, create: 5
-end
diff --git a/spec/lib/gitlab/usage_data_counters/note_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/note_counter_spec.rb
deleted file mode 100644
index 549f529a7ce..00000000000
--- a/spec/lib/gitlab/usage_data_counters/note_counter_spec.rb
+++ /dev/null
@@ -1,78 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::UsageDataCounters::NoteCounter, :clean_gitlab_redis_shared_state do
- shared_examples 'a note usage counter' do |event, noteable_type|
- describe ".count(#{event})" do
- it "increments the Note #{event} counter by 1" do
- expect do
- described_class.count(event, noteable_type)
- end.to change { described_class.read(event, noteable_type) }.by 1
- end
- end
-
- describe ".read(#{event})" do
- event_count = 5
-
- it "returns the total number of #{event} events" do
- event_count.times do
- described_class.count(event, noteable_type)
- end
-
- expect(described_class.read(event, noteable_type)).to eq(event_count)
- end
- end
- end
-
- it_behaves_like 'a note usage counter', :create, 'MergeRequest'
-
- describe '.totals' do
- let(:combinations) do
- [
- [:create, 'MergeRequest', 4]
- ]
- end
-
- let(:expected_totals) do
- { merge_request_comment: 4 }
- end
-
- before do
- combinations.each do |event, noteable_type, n|
- n.times do
- described_class.count(event, noteable_type)
- end
- end
- end
-
- it 'can report all totals' do
- expect(described_class.totals).to include(expected_totals)
- end
- end
-
- describe 'unknown events or noteable_type' do
- using RSpec::Parameterized::TableSyntax
-
- let(:unknown_event_error) { Gitlab::UsageDataCounters::BaseCounter::UnknownEvent }
-
- where(:event, :noteable_type, :expected_count, :should_raise) do
- :create | 'MergeRequest' | 1 | false
- :wibble | 'MergeRequest' | 0 | true
- :create | 'Issue' | 0 | false
- :wibble | 'Issue' | 0 | false
- end
-
- with_them do
- it 'handles event' do
- if should_raise
- expect { described_class.count(event, noteable_type) }.to raise_error(unknown_event_error)
- else
- described_class.count(event, noteable_type)
-
- expect(described_class.read(event, noteable_type)).to eq(expected_count)
- end
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/usage_data_queries_spec.rb b/spec/lib/gitlab/usage_data_queries_spec.rb
index a631828e547..a6d06b9c698 100644
--- a/spec/lib/gitlab/usage_data_queries_spec.rb
+++ b/spec/lib/gitlab/usage_data_queries_spec.rb
@@ -40,11 +40,6 @@ RSpec.describe Gitlab::UsageDataQueries do
describe '.redis_usage_data' do
subject(:redis_usage_data) { described_class.redis_usage_data { 42 } }
- it 'returns a stringified class for redis_usage_data with a counter call' do
- expect(described_class.redis_usage_data(Gitlab::UsageDataCounters::MergeRequestCounter))
- .to eq(redis_usage_data_counter: "Gitlab::UsageDataCounters::MergeRequestCounter")
- end
-
it 'returns a placeholder string for redis_usage_data with a block' do
is_expected.to include(:redis_usage_data_block)
expect(redis_usage_data[:redis_usage_data_block]).to eq('non-SQL usage data block')
diff --git a/spec/lib/gitlab/utils/usage_data_spec.rb b/spec/lib/gitlab/utils/usage_data_spec.rb
index 57da24ff1bd..3ae33b53691 100644
--- a/spec/lib/gitlab/utils/usage_data_spec.rb
+++ b/spec/lib/gitlab/utils/usage_data_spec.rb
@@ -502,18 +502,18 @@ RSpec.describe Gitlab::Utils::UsageData do
context 'with counter given' do
context 'when gets an error' do
- subject { described_class.redis_usage_data(::Gitlab::UsageDataCounters::MergeRequestCounter) }
+ subject { described_class.redis_usage_data(::Gitlab::UsageDataCounters::PackageEventCounter) }
- let(:fallback) { ::Gitlab::UsageDataCounters::MergeRequestCounter.fallback_totals }
- let(:failing_class) { ::Gitlab::UsageDataCounters::MergeRequestCounter }
+ let(:fallback) { ::Gitlab::UsageDataCounters::PackageEventCounter.fallback_totals }
+ let(:failing_class) { ::Gitlab::UsageDataCounters::PackageEventCounter }
let(:failing_method) { :totals }
it_behaves_like 'failing hardening method', ::Redis::CommandError
end
it 'returns the totals when couter is given' do
- allow(::Gitlab::UsageDataCounters::MergeRequestCounter).to receive(:totals).and_return({ merge_request_create: 2 })
- expect(described_class.redis_usage_data(::Gitlab::UsageDataCounters::MergeRequestCounter)).to eql({ merge_request_create: 2 })
+ allow(::Gitlab::UsageDataCounters::PackageEventCounter).to receive(:totals).and_return({ merge_request_create: 2 })
+ expect(described_class.redis_usage_data(::Gitlab::UsageDataCounters::PackageEventCounter)).to eql({ merge_request_create: 2 })
end
end
end
diff --git a/spec/services/draft_notes/publish_service_spec.rb b/spec/services/draft_notes/publish_service_spec.rb
index 3822752b90b..b2c18be63c0 100644
--- a/spec/services/draft_notes/publish_service_spec.rb
+++ b/spec/services/draft_notes/publish_service_spec.rb
@@ -234,7 +234,7 @@ RSpec.describe DraftNotes::PublishService, feature_category: :code_review_workfl
recorder = ActiveRecord::QueryRecorder.new(skip_cached: false) { publish }
- expect(recorder.count).not_to be > 105
+ expect(recorder.count).not_to be > 106
end
end
diff --git a/spec/services/merge_requests/after_create_service_spec.rb b/spec/services/merge_requests/after_create_service_spec.rb
index e2f000c754d..493091efbfe 100644
--- a/spec/services/merge_requests/after_create_service_spec.rb
+++ b/spec/services/merge_requests/after_create_service_spec.rb
@@ -170,10 +170,12 @@ RSpec.describe MergeRequests::AfterCreateService, feature_category: :code_review
end
end
- it 'increments the usage data counter of create event' do
- counter = Gitlab::UsageDataCounters::MergeRequestCounter
+ it_behaves_like 'internal event tracking' do
+ let(:user) { merge_request.author }
+ let(:event) { 'create_merge_request' }
+ let(:project) { merge_request.project }
- expect { execute_service }.to change { counter.read(:create) }.by(1)
+ subject(:track_event) { execute_service }
end
context 'todos' do
@@ -247,12 +249,6 @@ RSpec.describe MergeRequests::AfterCreateService, feature_category: :code_review
end
end
- it 'tracks merge request creation in usage data' do
- expect(Gitlab::UsageDataCounters::MergeRequestCounter).to receive(:count).with(:create)
-
- execute_service
- end
-
it 'calls MergeRequests::LinkLfsObjectsService#execute' do
service = instance_spy(MergeRequests::LinkLfsObjectsService)
allow(MergeRequests::LinkLfsObjectsService).to receive(:new).with(project: merge_request.target_project).and_return(service)
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index 8fe7db415bb..c0393d1e434 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -714,6 +714,18 @@ RSpec.describe Notes::CreateService, feature_category: :team_planning do
let(:event) { 'create_snippet_note' }
let(:category) { described_class.to_s }
+ context 'merge request' do
+ let(:merge_request) { create(:merge_request) }
+ let(:opts) { { note: 'reply', noteable_type: 'MergeRequest', noteable_id: merge_request.id, project: merge_request.project } }
+
+ it_behaves_like 'internal event tracking' do
+ let(:event) { 'create_merge_request_note' }
+ let(:project) { merge_request.project }
+
+ subject(:track_event) { described_class.new(merge_request.project, user, opts).execute }
+ end
+ end
+
context 'snippet note' do
let(:snippet) { create(:project_snippet, project: project) }
let(:opts) { { note: 'reply', noteable_type: 'Snippet', noteable_id: snippet.id, project: project } }
diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml
index 575ac74ad05..316a5e12c3d 100644
--- a/spec/support/rspec_order_todo.yml
+++ b/spec/support/rspec_order_todo.yml
@@ -6607,9 +6607,7 @@
- './spec/lib/gitlab/usage_data_counters/jetbrains_plugin_activity_unique_counter_spec.rb'
- './spec/lib/gitlab/usage_data_counters/kubernetes_agent_counter_spec.rb'
- './spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb'
-- './spec/lib/gitlab/usage_data_counters/merge_request_counter_spec.rb'
- './spec/lib/gitlab/usage_data_counters/merge_request_widget_extension_counter_spec.rb'
-- './spec/lib/gitlab/usage_data_counters/note_counter_spec.rb'
- './spec/lib/gitlab/usage_data_counters/package_event_counter_spec.rb'
- './spec/lib/gitlab/usage_data_counters/quick_action_activity_unique_counter_spec.rb'
- './spec/lib/gitlab/usage_data_counters/redis_counter_spec.rb'