Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-10-07 12:11:57 +00:00
parent 2844d4de69
commit f84bebbaed
53 changed files with 1348 additions and 1067 deletions

View File

@ -13,7 +13,7 @@ variables:
LFS_VERSION: "2.9"
NODE_VERSION: "20.12"
OS_VERSION: "bookworm"
RUBY_VERSION_DEFAULT: "3.1.5"
RUBY_VERSION_DEFAULT: "3.2.5"
RUBY_VERSION_NEXT: "3.2.5"
RUBYGEMS_VERSION: "3.4"
RUST_VERSION: "1.73"

View File

@ -1 +1 @@
v0.0.9
v0.0.11

View File

@ -1 +1 @@
7e5bb4f9a8ad504982f3e9e11604a0de3d827c44
867c6a7fc2edd401bf11471712c10581771a40d2

View File

@ -35,11 +35,7 @@ export default {
</script>
<template>
<gl-empty-state
:svg-path="noManifestsIllustration"
:svg-height="null"
:title="$options.i18n.noManifestTitle"
>
<gl-empty-state :svg-path="noManifestsIllustration" :title="$options.i18n.noManifestTitle">
<template #description>
<p class="gl-mb-5">
<gl-sprintf :message="$options.i18n.emptyText">

View File

@ -58,6 +58,5 @@
@import 'framework/card';
@import 'framework/source_editor';
@import 'framework/diffs';
@import 'framework/new_card';
@import 'framework/tanuki_bot';
@import 'framework/crud';

View File

@ -1,165 +0,0 @@
.gl-new-card {
margin-top: $gl-spacing-scale-5;
background-color: var(--gl-background-color-subtle);
border-width: $gl-border-size-1;
border-style: solid;
border-color: $gray-100;
border-radius: $gl-border-radius-base;
&.is-collapsed .gl-new-card-header {
border-bottom: 0;
border-radius: $gl-border-radius-base;
}
}
.gl-new-card-header {
padding-left: $gl-spacing-scale-5;
padding-right: $gl-spacing-scale-5;
padding-top: $gl-spacing-scale-4;
padding-bottom: $gl-spacing-scale-4;
display: flex;
justify-content: space-between;
background-color: var(--gl-background-color-default);
border-bottom-width: $gl-border-size-1;
border-bottom-style: solid;
border-bottom-color: $gray-100;
border-top-left-radius: $gl-border-radius-base;
border-top-right-radius: $gl-border-radius-base;
}
.gl-new-card-title-wrapper {
display: flex;
flex-grow: 1;
}
.gl-new-card-title {
display: flex;
font-size: $gl-font-size;
font-weight: $gl-font-weight-bold;
position: relative;
margin: 0;
line-height: $gl-line-height-24;
}
.gl-new-card-count {
margin-left: $gl-spacing-scale-3;
margin-right: $gl-spacing-scale-3;
font-size: $gl-font-size;
font-weight: $gl-font-weight-bold;
color: $gray-500;
display: inline-flex;
align-items: center;
}
.gl-new-card-description {
font-size: $gl-font-size-sm;
color: $gray-500;
margin: 0;
}
.gl-new-card-toggle {
padding-left: $gl-spacing-scale-3;
margin-left: $gl-spacing-scale-3;
margin-right: -$gl-spacing-scale-2;
border-left-width: $gl-border-size-1;
border-left-style: solid;
border-left-color: $gray-100;
}
.gl-new-card-body {
border-bottom-left-radius: $gl-border-radius-base;
border-bottom-right-radius: $gl-border-radius-base;
padding-left: $gl-spacing-scale-3;
padding-right: $gl-spacing-scale-3;
padding-top: 0;
padding-bottom: 0;
}
.gl-new-card-content {
padding-left: $gl-spacing-scale-2;
padding-right: $gl-spacing-scale-2;
padding-top: $gl-spacing-scale-3;
padding-bottom: $gl-spacing-scale-3;
}
.gl-new-card-empty {
padding: $gl-spacing-scale-2;
margin-bottom: 0;
color: $gray-500;
}
.gl-new-card-footer {
background-color: var(--gl-background-color-default);
}
.gl-new-card-add-form {
padding: $gl-spacing-scale-4;
margin-top: $gl-spacing-scale-2;
margin-bottom: $gl-spacing-scale-2;
background-color: var(--gl-background-color-default);
border-width: $gl-border-size-1;
border-style: solid;
border-color: $gray-100;
border-radius: $gl-border-radius-base;
}
.gl-new-card-body {
// Table adjustments
@mixin new-card-table-adjustments {
tbody > tr {
&:first-of-type > td[data-label],
&:first-of-type > td:first-of-type:last-of-type {
border-top-width: 0;
}
&:last-of-type td:not(:last-of-type) {
border-bottom-width: $gl-border-size-1;
}
> td[data-label] {
border-left: 0;
border-left-style: none;
border-right: 0;
border-right-style: none;
}
> th {
border-top-width: $gl-border-size-1;
border-bottom-width: 0;
}
&::after {
background-color: $white;
}
&:last-child::after {
display: none;
}
}
}
table.b-table-stacked-sm,
table.b-table-stacked-md {
margin-bottom: 0;
tr:first-of-type th {
border-top-width: 0;
}
tr:last-of-type td {
border-bottom-width: 0;
}
}
table.gl-table.b-table.b-table-stacked-sm {
@include gl-media-breakpoint-down(sm) {
@include new-card-table-adjustments;
}
}
table.gl-table.b-table.b-table-stacked-md {
@include gl-media-breakpoint-down(md) {
@include new-card-table-adjustments;
}
}
}

View File

@ -253,6 +253,16 @@ ul.related-merge-requests > li gl-emoji {
}
}
.issuable-header-slide-enter-active,
.issuable-header-slide-leave-active {
@apply gl-transition-all;
}
.issuable-header-slide-enter,
.issuable-header-slide-leave-to {
transform: translateY(-100%);
}
.issuable-sticky-header-visible {
--issuable-sticky-header-height: 40px;
}

View File

@ -42,7 +42,7 @@
.settings-section,
.settings-section-no-bottom ~ .settings-section {
padding-top: 0 !important;
padding-top: 0;
}
// Fix for sticky header when there is no search bar.
@ -50,12 +50,9 @@
padding-top: $gl-spacing-scale-3;
}
.settings-section ~ .settings-section {
padding-top: $gl-spacing-scale-6;
}
.settings-section:not(.settings-section-no-bottom) ~ .settings-section {
@include gl-border-t;
padding-top: $gl-spacing-scale-6;
}
.settings-section-no-bottom::after {

View File

@ -12,6 +12,7 @@ class ContainerRepository < ApplicationRecord
REQUIRING_CLEANUP_STATUSES = %i[cleanup_unscheduled cleanup_scheduled].freeze
MAX_TAGS_PAGES = 2000
MAX_DELETION_FAILURES = 10
# The Registry client uses JWT token to authenticate to Registry. We cache the client using expiration
# time of JWT token. However it's possible that the token is valid but by the time the request is made to
@ -23,6 +24,8 @@ class ContainerRepository < ApplicationRecord
validates :name, length: { minimum: 0, allow_nil: false }
validates :name, uniqueness: { scope: :project_id }
validates :failed_deletion_count, presence: true
validates :failed_deletion_count, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: MAX_DELETION_FAILURES }
enum status: { delete_scheduled: 0, delete_failed: 1, delete_ongoing: 2 }
enum expiration_policy_cleanup_status: { cleanup_unscheduled: 0, cleanup_scheduled: 1, cleanup_unfinished: 2, cleanup_ongoing: 3 }
@ -82,8 +85,9 @@ class ContainerRepository < ApplicationRecord
(Gitlab::CurrentSettings.container_registry_token_expire_delay * 60) - AUTH_TOKEN_USAGE_RESERVED_TIME_IN_SECS
end
class << self
alias_method :pending_destruction, :delete_scheduled # needed by Packages::Destructible
# needed by Packages::Destructible
def self.pending_destruction
delete_scheduled.where('next_delete_attempt_at IS NULL OR next_delete_attempt_at < ?', Time.zone.now)
end
# rubocop: disable CodeReuse/ServiceClass
@ -253,18 +257,38 @@ class ContainerRepository < ApplicationRecord
def set_delete_ongoing_status
now = Time.zone.now
update_columns(
values = {
status: :delete_ongoing,
delete_started_at: now,
status_updated_at: now
)
}
values[:next_delete_attempt_at] = nil if Feature.enabled?(:set_delete_failed_container_repository, project)
update_columns(values)
end
def set_delete_scheduled_status
update_columns(
values = {
status: :delete_scheduled,
delete_started_at: nil,
status_updated_at: Time.zone.now
}
if Feature.enabled?(:set_delete_failed_container_repository, project)
values[:failed_deletion_count] = failed_deletion_count + 1
values[:next_delete_attempt_at] = next_delete_attempt_with_delay
end
update_columns(values)
end
def set_delete_failed_status
update_columns(
status: :delete_failed,
delete_started_at: nil,
status_updated_at: Time.zone.now
)
end
@ -320,6 +344,10 @@ class ContainerRepository < ApplicationRecord
gitlab_api_client.repository_details(self.path, sizing: :self)
end
strong_memoize_attr :gitlab_api_client_repository_details
def next_delete_attempt_with_delay(now = Time.zone.now)
now + (2**failed_deletion_count).minutes
end
end
ContainerRepository.prepend_mod_with('ContainerRepository')

View File

@ -1,7 +1,7 @@
- page_title _('DevOps Reports')
- add_page_specific_style 'page_bundles/dev_ops_reports'
.gl-mt-3{ data: { event_tracking_load: 'true', event_tracking: 'view_admin_dev_ops_reports_pageload' } }
%div{ data: { event_tracking_load: 'true', event_tracking: 'view_admin_dev_ops_reports_pageload' } }
- if show_adoption?
= render_if_exists 'admin/dev_ops_report/devops_tabs'
- else

View File

@ -1,7 +1,7 @@
= gitlab_ui_form_for [:admin, @group] do |f|
= form_errors(@group)
= render ::Layouts::HorizontalSectionComponent.new(options: { class: 'gl-pb-5 gl-mb-6' }) do |c|
- c.with_title { _('Naming, visibility') }
= render ::Layouts::SettingsSectionComponent.new(_('Naming, visibility')) do |c|
- c.with_description do
= _('Update your group name, description, avatar, and visibility.')
= link_to _('Learn more about groups.'), help_page_path('user/group/index.md')
@ -13,8 +13,7 @@
= render 'shared/choose_avatar_button', f: f
= render 'shared/visibility_level', f: f, visibility_level: visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group, with_label: false
= render ::Layouts::HorizontalSectionComponent.new(options: { class: 'gl-pb-3 gl-mb-6' }) do |c|
- c.with_title { _('Permissions and group features') }
= render ::Layouts::SettingsSectionComponent.new(_('Permissions and group features')) do |c|
- c.with_description do
= _('Configure advanced permissions, Large File Storage, two-factor authentication, and CI/CD settings.')
- c.with_body do
@ -25,8 +24,7 @@
= render 'groups/group_admin_settings', f: f
= render_if_exists 'namespaces/shared_runners_minutes_settings', group: @group, form: f
= render ::Layouts::HorizontalSectionComponent.new(border: false, options: { class: 'gl-pb-3' }) do |c|
- c.with_title { _('Admin notes') }
= render ::Layouts::SettingsSectionComponent.new(_('Admin notes')) do |c|
- c.with_body do
= render 'shared/admin/admin_note_form', f: f
@ -34,13 +32,13 @@
= render Pajamas::AlertComponent.new(dismissible: false) do |c|
- c.with_body do
= render 'shared/group_tips'
.gl-mt-5
.settings-sticky-footer.gl-flex.gl-gap-3
= f.submit _('Create group'), pajamas_button: true
= render Pajamas::ButtonComponent.new(href: admin_groups_path) do
= _('Cancel')
- else
.gl-mt-5
.settings-sticky-footer.gl-flex.gl-gap-3
= f.submit _('Save changes'), data: { testid: 'save-changes-button' }, pajamas_button: true
= render Pajamas::ButtonComponent.new(href: admin_group_path(@group)) do
= _('Cancel')

View File

@ -3,6 +3,5 @@
- breadcrumb_title _("Edit")
- page_title _("Edit"), @group.name, _("Groups")
%h1.page-title.gl-text-size-h-display= _('Edit group: %{group_name}') % { group_name: @group.name }
%hr
= render ::Layouts::PageHeadingComponent.new(_('Edit group: %{group_name}') % { group_name: @group.name })
= render 'form', visibility_level: @group.visibility_level

View File

@ -2,7 +2,7 @@
#js-dependency-proxy{ data: { group_path: @group.full_path,
endpoint: group_dependency_proxy_path(@group),
no_manifests_illustration: image_path('illustrations/docker-empty-state.svg'),
no_manifests_illustration: image_path('illustrations/status/status-nothing-md.svg'),
group_id: @group.id,
settings_path: group_settings_packages_and_registries_path(@group),
can_clear_cache: can?(current_user, :admin_group, @group).to_s } }

View File

@ -28,14 +28,14 @@ module ContainerRegistry
log_delete_tags_service_result(next_container_repository, result)
if result[:status] == :error || next_container_repository.tags_count != 0
return next_container_repository.set_delete_scheduled_status
return update_next_container_repository_status
end
next_container_repository.destroy!
audit_event(next_container_repository)
rescue StandardError => exception
next_container_repository&.set_delete_scheduled_status
update_next_container_repository_status
Gitlab::ErrorTracking.log_exception(exception, class: self.class.name)
end
@ -50,6 +50,17 @@ module ContainerRegistry
private
def update_next_container_repository_status
return unless next_container_repository
if next_container_repository.failed_deletion_count >= ContainerRepository::MAX_DELETION_FAILURES &&
Feature.enabled?(:set_delete_failed_container_repository, next_container_repository.project)
next_container_repository.set_delete_failed_status
else
next_container_repository.set_delete_scheduled_status
end
end
def delete_tags
service = Projects::ContainerRepository::CleanupTagsService.new(
container_repository: next_container_repository,

View File

@ -0,0 +1,9 @@
---
name: set_delete_failed_container_repository
feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/480652
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/166119
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/490354
milestone: '17.5'
group: group::container registry
type: gitlab_com_derisk
default_enabled: false

View File

@ -0,0 +1,12 @@
---
table_name: duo_workflows_events
classes:
- Ai::DuoWorkflows::Event
feature_categories:
- duo_workflow
description:
introduced_by_url:
milestone: '17.5'
gitlab_schema: gitlab_main_cell
sharding_key:
project_id: projects

View File

@ -10,4 +10,13 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104267
milestone: '15.7'
allow_cross_foreign_keys:
- gitlab_main_clusterwide
sharding_key_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/457485
desired_sharding_key:
project_id:
references: projects
backfill_via:
parent:
foreign_key: candidate_id
table: ml_candidates
sharding_key: project_id
belongs_to: candidate
desired_sharding_key_migration_job_name: BackfillMlCandidateMetadataProjectId

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
class AddFailedDeletionCountAndNextDeleteAtToContainerRepositories < Gitlab::Database::Migration[2.2]
milestone '17.5'
def change
add_column :container_repositories, :failed_deletion_count,
:integer, default: 0, null: false, if_not_exists: true
add_column :container_repositories, :next_delete_attempt_at,
:datetime_with_timezone, if_not_exists: true
end
end

View File

@ -0,0 +1,20 @@
# frozen_string_literal: true
class AddIndexOnStatusAndNextDeleteAttemptAtForContainerRepositories < Gitlab::Database::Migration[2.2]
disable_ddl_transaction!
milestone '17.5'
def up
add_concurrent_index :container_repositories,
:next_delete_attempt_at,
name: :index_container_repositories_on_next_delete_attempt_at,
where: 'status = 0' # status: :delete_scheduled
end
def down
remove_concurrent_index :container_repositories,
:next_delete_attempt_at,
name: :index_container_repositories_on_next_delete_attempt_at
end
end

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
class CreateDuoWorkflowEventsTable < Gitlab::Database::Migration[2.2]
milestone '17.5'
def change
create_table :duo_workflows_events do |t| # rubocop:disable Migration/EnsureFactoryForTable -- https://gitlab.com/gitlab-org/gitlab/-/issues/468630
t.references :workflow, foreign_key: { to_table: :duo_workflows_workflows, on_delete: :cascade }, null: false,
index: true
t.references :project, foreign_key: { to_table: :projects, on_delete: :cascade }, null: false,
index: true
t.timestamps_with_timezone null: false
t.integer :event_type, limit: 2, null: false
t.integer :event_status, limit: 2, null: false
t.text :message, null: true, limit: 255
end
end
end

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
class AddPositiveConstraintOnFailedDeletionCount < Gitlab::Database::Migration[2.2]
milestone '17.5'
disable_ddl_transaction!
CONSTRAINT_NAME = 'check_container_repositories_non_negative_failed_deletion_count'
def up
add_check_constraint :container_repositories, 'failed_deletion_count >= 0', CONSTRAINT_NAME
end
def down
remove_check_constraint :container_repositories, CONSTRAINT_NAME
end
end

View File

@ -0,0 +1 @@
969c717f1283a8bfa1aae3baeeb0d10937e44b0abeaaa5c7c6b2c0d83ca0616e

View File

@ -0,0 +1 @@
51ccc7d1800dc5b9375ce10dee78a8d3bb80987729cbfee63bb1f2c1465d4a1f

View File

@ -0,0 +1 @@
7d6d9ced4a6c945367f4c89af8c554d52115dfebfef67a401d31c1eba979fa32

View File

@ -0,0 +1 @@
4ba2b49684b3e3b3f8fa28891c9cff8e323373768481f4ed6a421e87e99dc0b7

View File

@ -9759,7 +9759,10 @@ CREATE TABLE container_repositories (
expiration_policy_completed_at timestamp with time zone,
last_cleanup_deleted_tags_count integer,
delete_started_at timestamp with time zone,
status_updated_at timestamp with time zone
status_updated_at timestamp with time zone,
failed_deletion_count integer DEFAULT 0 NOT NULL,
next_delete_attempt_at timestamp with time zone,
CONSTRAINT check_container_repositories_non_negative_failed_deletion_count CHECK ((failed_deletion_count >= 0))
);
CREATE SEQUENCE container_repositories_id_seq
@ -10905,6 +10908,27 @@ CREATE SEQUENCE duo_workflows_checkpoints_id_seq
ALTER SEQUENCE duo_workflows_checkpoints_id_seq OWNED BY duo_workflows_checkpoints.id;
CREATE TABLE duo_workflows_events (
id bigint NOT NULL,
workflow_id bigint NOT NULL,
project_id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
event_type smallint NOT NULL,
event_status smallint NOT NULL,
message text,
CONSTRAINT check_d96965e118 CHECK ((char_length(message) <= 255))
);
CREATE SEQUENCE duo_workflows_events_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE duo_workflows_events_id_seq OWNED BY duo_workflows_events.id;
CREATE TABLE duo_workflows_workflows (
id bigint NOT NULL,
user_id bigint NOT NULL,
@ -22253,6 +22277,8 @@ ALTER TABLE ONLY draft_notes ALTER COLUMN id SET DEFAULT nextval('draft_notes_id
ALTER TABLE ONLY duo_workflows_checkpoints ALTER COLUMN id SET DEFAULT nextval('duo_workflows_checkpoints_id_seq'::regclass);
ALTER TABLE ONLY duo_workflows_events ALTER COLUMN id SET DEFAULT nextval('duo_workflows_events_id_seq'::regclass);
ALTER TABLE ONLY duo_workflows_workflows ALTER COLUMN id SET DEFAULT nextval('duo_workflows_workflows_id_seq'::regclass);
ALTER TABLE ONLY early_access_program_tracking_events ALTER COLUMN id SET DEFAULT nextval('early_access_program_tracking_events_id_seq'::regclass);
@ -24438,6 +24464,9 @@ ALTER TABLE ONLY draft_notes
ALTER TABLE ONLY duo_workflows_checkpoints
ADD CONSTRAINT duo_workflows_checkpoints_pkey PRIMARY KEY (id);
ALTER TABLE ONLY duo_workflows_events
ADD CONSTRAINT duo_workflows_events_pkey PRIMARY KEY (id);
ALTER TABLE ONLY duo_workflows_workflows
ADD CONSTRAINT duo_workflows_workflows_pkey PRIMARY KEY (id);
@ -28545,6 +28574,8 @@ CREATE INDEX index_container_expiration_policies_on_next_run_at_and_enabled ON c
CREATE INDEX index_container_registry_data_repair_details_on_status ON container_registry_data_repair_details USING btree (status);
CREATE INDEX index_container_repositories_on_next_delete_attempt_at ON container_repositories USING btree (next_delete_attempt_at) WHERE (status = 0);
CREATE INDEX index_container_repositories_on_project_id_and_id ON container_repositories USING btree (project_id, id);
CREATE UNIQUE INDEX index_container_repositories_on_project_id_and_name ON container_repositories USING btree (project_id, name);
@ -28811,6 +28842,10 @@ CREATE INDEX index_draft_notes_on_project_id ON draft_notes USING btree (project
CREATE INDEX index_duo_workflows_checkpoints_on_project_id ON duo_workflows_checkpoints USING btree (project_id);
CREATE INDEX index_duo_workflows_events_on_project_id ON duo_workflows_events USING btree (project_id);
CREATE INDEX index_duo_workflows_events_on_workflow_id ON duo_workflows_events USING btree (workflow_id);
CREATE UNIQUE INDEX index_duo_workflows_workflow_checkpoints_unique_thread ON duo_workflows_checkpoints USING btree (workflow_id, thread_ts);
CREATE INDEX index_duo_workflows_workflows_on_project_id ON duo_workflows_workflows USING btree (project_id);
@ -36675,6 +36710,9 @@ ALTER TABLE ONLY ai_agent_version_attachments
ALTER TABLE ONLY operations_feature_flag_scopes
ADD CONSTRAINT fk_rails_a50a04d0a4 FOREIGN KEY (feature_flag_id) REFERENCES operations_feature_flags(id) ON DELETE CASCADE;
ALTER TABLE ONLY duo_workflows_events
ADD CONSTRAINT fk_rails_a55845e9fa FOREIGN KEY (workflow_id) REFERENCES duo_workflows_workflows(id) ON DELETE CASCADE;
ALTER TABLE ONLY packages_helm_file_metadata
ADD CONSTRAINT fk_rails_a559865345 FOREIGN KEY (package_file_id) REFERENCES packages_package_files(id) ON DELETE CASCADE;
@ -36744,6 +36782,9 @@ ALTER TABLE ONLY packages_composer_metadata
ALTER TABLE ONLY user_phone_number_validations
ADD CONSTRAINT fk_rails_ad6686f3d8 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
ALTER TABLE ONLY duo_workflows_events
ADD CONSTRAINT fk_rails_ae183590f0 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY analytics_cycle_analytics_group_stages
ADD CONSTRAINT fk_rails_ae5da3409b FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;

View File

@ -120,6 +120,6 @@ Outbound communications from the following features are not silenced by Silent M
| [File hooks](../file_hooks.md) | |
| [Server hooks](../server_hooks.md) | |
| [Advanced search](../../integration/advanced_search/elasticsearch.md) | If two GitLab instances are using the same Advanced Search instance, then they can both modify Search data. This is a split-brain scenario which can occur for example after promoting a secondary Geo site while the primary Geo site is live. |
| [Snowplow](../../user/product_analytics/index.md) | There is [a proposal to silence these requests](https://gitlab.com/gitlab-org/gitlab/-/issues/409661). |
| [Snowplow](../../operations/product_analytics/index.md) | There is [a proposal to silence these requests](https://gitlab.com/gitlab-org/gitlab/-/issues/409661). |
| [Deprecated Kubernetes Connections](../../user/clusters/agent/index.md) | There is [a proposal to silence these requests](https://gitlab.com/gitlab-org/gitlab/-/issues/396470). |
| [Container registry webhooks](../packages/container_registry.md#configure-container-registry-notifications) | There is [a proposal to silence these requests](https://gitlab.com/gitlab-org/gitlab/-/issues/409682). |

View File

@ -20110,6 +20110,7 @@ Represents a ComplianceFramework associated with a Project.
| <a id="complianceframeworkid"></a>`id` | [`ID!`](#id) | Compliance framework ID. |
| <a id="complianceframeworkname"></a>`name` | [`String!`](#string) | Name of the compliance framework. |
| <a id="complianceframeworkpipelineconfigurationfullpath"></a>`pipelineConfigurationFullPath` **{warning-solid}** | [`String`](#string) | **Deprecated** in GitLab 17.4. Use pipeline execution policies instead. |
| <a id="complianceframeworkpipelineexecutionpolicies"></a>`pipelineExecutionPolicies` | [`PipelineExecutionPolicyConnection`](#pipelineexecutionpolicyconnection) | Pipeline Execution Policies of the compliance framework. (see [Connections](#connections)) |
| <a id="complianceframeworkprojects"></a>`projects` | [`ProjectConnection`](#projectconnection) | Projects associated with the compliance framework. (see [Connections](#connections)) |
| <a id="complianceframeworkscanexecutionpolicies"></a>`scanExecutionPolicies` | [`ScanExecutionPolicyConnection`](#scanexecutionpolicyconnection) | Scan Execution Policies of the compliance framework. (see [Connections](#connections)) |
| <a id="complianceframeworkscanresultpolicies"></a>`scanResultPolicies` | [`ScanResultPolicyConnection`](#scanresultpolicyconnection) | Scan Result Policies of the compliance framework. (see [Connections](#connections)) |

View File

@ -224,7 +224,7 @@ parameter in the `projects` REST API endpoint.
### To `git clone` a private project's repository
You can use the job token to authenticate and clone a repository from a private project
in a CI/CD job. For example:
in a CI/CD job. Use `gitlab-ci-token` as the user, and the value of the job token as the password. For example:
```shell
git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.example.com/<namespace>/<project>

View File

@ -127,3 +127,7 @@ To create the required database objects execute:
```shell
sudo gitlab-rake gitlab:clickhouse:migrate
```
### Enable ClickHouse for Analytics
Now that your GitLab instance is connected to ClickHouse, you can enable features to use ClickHouse by [enabling ClickHouse for Analytics](../administration/analytics.md).

View File

@ -1,15 +1,15 @@
---
stage: Monitor
group: Platform Insights
description: Track errors, application performance issues, and manage incident response.
description: Track errors, application performance issues, customer behavior patterns and manage incident response.
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Monitor your application
Visualize and analyze errors, traces, metrics and logs collected from your application and its infrastructure. Monitor, identify and resolve performance issues and incidents collaboratively.
Visualize and analyze errors, traces, metrics, logs, product analytics events collected from your application and its infrastructure. Monitor, identify, and resolve performance and customer behavior related issues and incidents collaboratively.
| | | |
|--|--|--|
| [**Getting started**](../user/get_started/get_started_monitoring.md)<br>Overview of how features fit together. | [**Error tracking**](error_tracking.md)<br>Error tracking, logging, debugging, data retention. | [**Distributed tracing**](tracing.md)<br>Monitoring, troubleshooting, performance analysis, request tracking. |
| [**Metrics**](metrics.md)<br>Monitoring, visualization, aggregation, analysis, retention. | [**Logs**](logs.md)<br>Centralized logging, analysis, configuration, viewing, filtering. | [**Incident management**](incident_management/index.md)<br>Alert handling, response coordination, escalation procedures. |
| [**Metrics**](metrics.md) and [**Logs**](logs.md)<br>Monitoring, visualization, aggregation, retention. Centralized logging, analysis, configuration, filtering. | [**Product Analytics**](product_analytics/index.md)<br>Monitor and analyze your customer's behavior and core usage patterns within your applications. | [**Incident management**](incident_management/index.md)<br>Alert handling, response coordination, escalation procedures. |

View File

@ -0,0 +1,463 @@
---
stage: Monitor
group: Platform Insights
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Product analytics
DETAILS:
**Tier:** Ultimate
**Offering:** GitLab.com, Self-managed
**Status:** Beta
> - Introduced in GitLab 15.4 as an [experiment](../../policy/experiment-beta-support.md#experiment) feature [with a flag](../../administration/feature_flags.md) named `cube_api_proxy`. Disabled by default.
> - `cube_api_proxy` changed to reference only the [product analytics API](../../api/product_analytics.md) in GitLab 15.6.
> - `cube_api_proxy` removed and replaced with `product_analytics_internal_preview` in GitLab 15.10.
> - `product_analytics_internal_preview` replaced with `product_analytics_dashboards` in GitLab 15.11.
> - Snowplow integration [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/398253) in GitLab 15.11 [with a flag](../../administration/feature_flags.md) named `product_analytics_snowplow_support`. Disabled by default.
> - Snowplow integration feature flag `product_analytics_snowplow_support` [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/130228) in GitLab 16.4.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/414865) from GitLab self-managed to GitLab.com in 16.7.
> - Enabled in GitLab 16.7 as a [beta](../../policy/experiment-beta-support.md#beta) feature.
> - `product_analytics_dashboards` [enabled](https://gitlab.com/gitlab-org/gitlab/-/issues/398653) by default in GitLab 16.11.
> - [Enabled on self-managed and GitLab Dedicated](https://gitlab.com/gitlab-org/gitlab/-/issues/444345) in GitLab 16.11.
> - Feature flag `product_analytics_dashboards` [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/454059) in GitLab 17.1.
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/167192) to beta and feature flag `product_analytics_admin_settings` added in GitLab 17.5.
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/167296) to beta and feature flag `product_analytics_features` added in GitLab 17.5.
FLAG:
The availability of this feature is controlled by a feature flag.
For more information, see the history.
This feature is not ready for production use.
The product analytics feature empowers you to track user behavior and gain insights into how your
applications are used and how users interact with your product.
By using the data collected with product analytics in GitLab, you can better understand your users,
identify friction points in funnels, make data-driven product decisions, and ultimately build better
products that drive user engagement and business growth.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an overview of the product analytics setup and functionality,
watch the [Product Analytics walkthrough videos](https://www.youtube.com/playlist?list=PL05JrBw4t0Kqfb4oLOFKkXxNrBJzDQ3sL&feature=shared).
For more information about the vision and development of product analytics, see the [group direction page](https://about.gitlab.com/direction/monitor/product-analytics/).
To leave feedback about product analytics bugs or functionality:
- Comment on [issue 391970](https://gitlab.com/gitlab-org/gitlab/-/issues/391970).
- Create an issue with the `group::product analytics` label.
## How product analytics works
Product analytics uses the following tools:
- [**Snowplow**](https://docs.snowplow.io/docs/) - A developer-first engine for collecting behavioral data and passing it through to ClickHouse.
- [**ClickHouse**](../../integration/clickhouse.md) - A database suited to store, query, and retrieve analytical data.
- [**Cube**](https://cube.dev/docs/product/introduction) - A universal semantic layer that provides an API to run queries against the data stored in ClickHouse.
The following diagram illustrates the product analytics flow:
```mermaid
%%{init: { "fontFamily": "GitLab Sans" }}%%
flowchart TB
accTitle: Product Analytics flow
accDescr: How data is collected, processed, and visualized in dashboards.
subgraph Event collection
A([SDK]) --Send user data--> B[Snowplow Collector]
B --Pass data--> C[Snowplow Enricher]
end
subgraph Data warehouse
C --Transform and enrich data--> D([ClickHouse])
end
subgraph Data visualization
F([Dashboards with panels/visualizations])
F --Request data--> G[Product Analytics API]
G --Run Cube queries with pre-aggregations--> H[Cube]
H --Get data--> D
D --Return results--> H
H --Transform data to be rendered--> G
G --Return data--> F
end
```
## Enable product analytics
> - Introduced in GitLab 15.6 [with a flag](../../administration/feature_flags.md) named `cube_api_proxy`. Disabled by default.
> - Moved behind a [flag](../../administration/feature_flags.md) named `product_analytics_admin_settings` in GitLab 15.7. Disabled by default.
> - Feature flag `cube_api_proxy` removed and replaced with `product_analytics_internal_preview` in GitLab 15.10.
> - Feature flag `product_analytics_internal_preview` replaced with `product_analytics_dashboards` in GitLab 15.11.
> - Feature flag `product_analytics_admin_settings` [enabled](https://gitlab.com/gitlab-org/gitlab/-/issues/385602) by default in GitLab 16.11.
> - Feature flag `product_analytics_admin_settings` [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/454342) in GitLab 17.1.
To track events in your project's applications,
you must enable and configure product analytics.
### Product analytics provider
Your GitLab instance connects to a product analytics provider.
A product analytics provider is the collection of services required to receive,
process, store and query your analytics data.
::Tabs
:::TabTitle GitLab-managed provider
DETAILS:
**Offering:** GitLab.com
On GitLab.com you can use a GitLab-managed provider offered only in the Google Cloud Platform zone `us-central-1`. This service is offered only in beta.
If GitLab manages your product analytics provider, then your analytics data is retained for one year.
You can request to delete your data at any time by [contacting support](https://about.gitlab.com/support/#contact-support).
:::TabTitle Self-managed provider
>[Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/117804) in GitLab 16.0.
A self-managed product analytics provider is a deployed instance of the
[product analytics Helm charts](https://gitlab.com/gitlab-org/analytics-section/product-analytics/helm-charts).
On GitLab.com, the self-managed provider details are defined in [project-level settings](#project-level-settings).
On GitLab self-managed and GitLab Dedicated, you must define the self-managed analytics provider in [instance-level settings](#instance-level-settings).
If you need different providers for different projects, you can define additional analytics providers in [project-level settings](#project-level-settings).
::EndTabs
### Instance-level settings
**Offering:** Self-managed, GitLab Dedicated
Prerequisites:
- You must have administrator access for the instance.
NOTE:
These instance-level settings are required to enable product analytics on GitLab self-managed and GitLab Dedicated,
and cascade to all projects by default.
To enable product analytics on your instance:
1. On the left sidebar, at the bottom, select **Admin**.
1. Select **Settings > Analytics**.
1. Enter the configuration values.
1. Select **Save changes**.
### Project-level settings
If you want to have a product analytics instance with a different configuration for your project,
you can override the instance-level settings defined by the administrator on a per-project basis.
Prerequisites:
- You must have at least the Maintainer role for the project or group the project belongs to.
- The project must be in a group namespace.
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Settings > Analytics**.
1. Expand **Data sources** and enter the configuration values.
1. Select **Save changes**.
## Onboard a GitLab project
> - Minimum required role [changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/154089/) in GitLab 17.1.
Prerequisites:
- You must have at least the Maintainer role for the project or group the project belongs to.
Onboarding a GitLab project means preparing it to receive events that are used for product analytics.
To onboard a project:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Analyze > Analytics dashboards**.
1. Under **Product analytics**, select **Set up**.
Then continue with the setup depending on the provider type.
::Tabs
:::TabTitle GitLab-managed provider
Prerequisites:
- You must have access to the [GitLab-managed provider](#product-analytics-provider).
1. Select the **I agree to event collection and processing in this region** checkbox.
1. Select **Connect GitLab-managed provider**.
1. Remove already configured project-level settings for a self-managed provider:
1. Select **Go to analytics settings**.
1. Expand **Data sources** and remove the configuration values.
1. Select **Save changes**.
1. Select **Analyze > Analytics dashboards**.
1. Under **Product analytics**, select **Set up**.
1. Select **Connect GitLab-managed provider**.
Your instance is being created, and the project onboarded.
:::TabTitle Self-managed provider
1. Select **Connect your own provider**.
1. Configure project-level settings for your self-managed provider:
1. Select **Go to analytics settings**.
1. Expand **Data sources** and enter the configuration values.
1. Select **Save changes**.
1. Select **Analyze > Analytics dashboards**.
1. Under **Product analytics**, select **Set up**.
1. Select **Connect your own provider**.
Your instance is being created, and the project onboarded.
::EndTabs
## Instrument your application
You can instrument code to collect data by using [tracking SDKs](instrumentation/index.md).
## Product analytics dashboards
> - Introduced in GitLab 15.5 [with a flag](../../administration/feature_flags.md) named `product_analytics_internal_preview`. Disabled by default.
Product analytics dashboards are a subset of dashboards under [Analytics dashboards](../../user/analytics/analytics_dashboards.md).
Specifically, product analytics dashboards and visualizations use the `cube_analytics` data type.
The `cube_analytics` data type connects to the Cube instance defined when [product analytics was enabled](#enable-product-analytics).
All filters and queries are sent to the Cube instance, and the returned data is processed by the
product analytics data source to be rendered by the appropriate visualizations.
Data table visualizations from `cube_analytics` have an additional configuration option for rendering `links`.
This option is an array of objects, each with `text` and `href` properties to specify the dimensions to be used in links.
If `href` contains multiple dimensions, values are joined into a single URL.
View an [example](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/validators/json_schemas/analytics_visualization.json?ref_type=heads#L112).
### Filling missing data
> - Introduced in GitLab 16.3 [with a flag](../../administration/feature_flags.md) named `product_analytics_dashboards`. Disabled by default.
When [exporting data](#raw-data-export) or [viewing dashboards](../../user/analytics/analytics_dashboards.md#view-project-dashboards),
if there is no data for a given day, the missing data is autofilled with `0`.
The autofill approach has both benefits and limitations.
- Benefits:
- The visualization's day axis matches the selected date range, removing ambiguity about missing data.
- Data exports have rows for the entire date range, making data analysis easier.
- Limitations:
- The `day` [granularity](https://cube.dev/docs/product/apis-integrations/rest-api/query-format) must be used.
All other granularities are not supported.
- Only date ranges defined by the [`inDateRange`](https://cube.dev/docs/product/apis-integrations/rest-api/query-format#indaterange) filter are filled.
- The date selector in the UI already uses this filter.
- The filling of data ignores the query-defined limit. If you set a limit of 10 data points over 20 days, it
returns 20 data points, with the missing data filled by `0`. [Issue 417231](https://gitlab.com/gitlab-org/gitlab/-/issues/417231) proposes a solution to this limitation.
## Funnel analysis
Use funnel analysis to understand the flow of users through your application, and where
users drop out of a predefined flow (for example, a checkout process or ticket purchase).
Each project can define an unlimited number of funnels.
Like dashboards, funnels are defined with the GitLab YAML schema
and stored in the `.gitlab/analytics/funnels/` directory of a project repository.
If a repository has a custom dashboards pointer project that points to another repository,
funnels must be defined in the pointer project.
### Create a funnel dashboard
To create a funnel dashboard, you must first create a funnel definition file and a visualization.
Each funnel must have a custom visualization defined for it.
When funnel definitions and visualizations are ready,
you can [create a custom dashboard](../../user/analytics/analytics_dashboards.md#create-a-custom-dashboard)
to visualize funnel analysis behavior.
#### Create a funnel definition
1. In the `.gitlab/analytics/` directory, create a directory named `funnels`.
1. In the new `.gitlab/analytics/funnels` directory, create a funnel definition YAML file.
Funnel definitions must include the key `seconds_to_convert` and an array of `steps`.
| Key | Description |
|----------------------|----------------------------------------------------------|
| `seconds_to_convert` | The number of seconds a user has to complete the funnel. |
| `steps` | An array of funnel steps. |
Each step must include the keys `name`, `target`, and `action`.
| Key | Description |
|----------|------------------------------------------------------------------------------------------|
| `name` | The name of the step. This should be a unique slug. |
| `action` | The action performed. (Only `pageview` is supported.) |
| `target` | The target of the step. (Because only `pageview` is supported, this should be a path.) |
The following example defines a funnel that tracks users who completed a purchase within one hour by going through three target pages:
```yaml
seconds_to_convert: 3600
steps:
- name: view_page_1
target: '/page1.html'
action: 'pageview'
- name: view_page_2
target: '/page2.html'
action: 'pageview'
- name: view_page_3
target: '/page3.html'
action: 'pageview'
```
#### Create a funnel visualization
To create funnel visualizations, follow the steps for [defining a chart visualization](../../user/analytics/analytics_dashboards.md#define-a-chart-visualization).
Funnel visualizations support the measure `count` and the dimension `step`.
The following example defines a column chart that visualizes the number of users who reached different steps in a funnel:
```yaml
version: 1
type: ColumnChart
data:
type: cube_analytics
query:
measures:
- FUNNEL_NAME.count
dimensions:
- FUNNEL_NAME.step
limit: 100
timezone: UTC
timeDimensions: []
options:
xAxis:
name: Step
type: category
yAxis:
name: Total
type: value
```
NOTE:
The funnel name defined in the YAML definition is converted to a slug that can be referenced in visualization definitions.
For example, the funnel name `Successful Conversions` is converted to `successful_conversions`.
### Query a funnel
You can [query the funnel data with the REST API](../../api/product_analytics.md#send-query-request-to-cube).
To do this, you can use the example query body below, where you need to replace `FUNNEL_NAME` with your funnel's name.
NOTE:
The name of a funnel is generated from the filename of the funnel definition YAML file,
by separating words with underscores and removing special characters.
For example, for a funnel definition file in `.gitlab/analytics/funnels/Successful Conversions.yaml`
the funnel name is `successful_conversions`.
This funnel name can be referenced in visualization definitions.
NOTE:
The `afterDate` filter is not supported. Use `beforeDate` or `inDateRange`.
```json
{
"query": {
"measures": [
"FUNNEL_NAME.count"
],
"order": {
"FUNNEL_NAME.count": "desc"
},
"filters": [
{
"member": "FUNNEL_NAME.date",
"operator": "beforeDate",
"values": [
"2023-02-01"
]
}
],
"dimensions": [
"FUNNEL_NAME.step"
]
}
}
```
## Raw data export
Exporting the raw event data from the underlying storage engine can help you debug and create datasets for data analysis.
Because Cube acts as an abstraction layer between the raw data and the API, the exported raw data has some caveats:
- Data is grouped by the selected dimensions. Therefore, the exported data might be incomplete, unless including both `utcTime` and `userAnonymousId`.
- Data is by default limited to 10,000 rows, but you can increase the limit to maximum 50,000 rows. If your dataset has more than 50,000 rows, you must paginate through the results by using the `limit` and `offset` parameters.
- Data is always returned in JSON format. If you need it in a different format, you need to convert the JSON to the required format using a scripting language of your choice.
[Issue 391683](https://gitlab.com/gitlab-org/gitlab/-/issues/391683) tracks efforts to implement a more scalable export solution.
### Export raw data with Cube queries
You can [query the raw data with the REST API](../../api/product_analytics.md#send-query-request-to-cube),
and convert the JSON output to any required format.
To export the raw data for a specific dimension, pass a list of dimensions to the `dimensions` key.
For example, the following query outputs the raw data for the attributes listed:
```json
POST /api/v4/projects/PROJECT_ID/product_analytics/request/load?queryType=multi
{
"query":{
"dimensions": [
"TrackedEvents.docEncoding",
"TrackedEvents.docHost",
"TrackedEvents.docPath",
"TrackedEvents.docSearch",
"TrackedEvents.eventType",
"TrackedEvents.localTzOffset",
"TrackedEvents.pageTitle",
"TrackedEvents.src",
"TrackedEvents.utcTime",
"TrackedEvents.vpSize"
],
"order": {
"TrackedEvents.apiKey": "asc"
}
}
}
```
If the request is successful, the returned JSON includes an array of rows of results.
## View product analytics usage quota
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/424153) in GitLab 16.6 [with a flag](../../administration/feature_flags.md) named `product_analytics_usage_quota`. Disabled by default.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/427838) in GitLab 16.7. Feature flag `product_analytics_usage_quota` removed.
Product analytics usage quota is calculated from the number of events received from instrumented applications.
To view product analytics usage quota:
1. On the left sidebar, select **Search or go to** and find your group.
1. Select **Settings > Usage quota**.
1. Select the **Product analytics** tab.
The tab displays the monthly totals for the group and a breakdown of usage per project.
The current month displays events counted to date.
The usage quota excludes projects that are not onboarded with product analytics.
## Best practices
- Define key metrics and goals from the start. Decide what questions you want to answer so you know how to use collected data.
- Use event data from all stages of the user journey. This data provides a comprehensive view of the user experience.
- Build dashboards aligned with team needs. Different teams need different data insights.
- Review dashboards regularly. This way, you can verify customer outcomes, identify trends in data, and update visualizations.
- Export raw data periodically. Dashboards provide only an overview of a subset of data, so you should export the data for a deeper analysis.
## Troubleshooting
### No events are collected
Check your [instrumentation details](#enable-product-analytics),
and make sure product analytics is enabled and set up correctly.
### Access to product analytics is restricted
Check that you are connected to a [product analytics provider](#product-analytics-provider).

View File

@ -0,0 +1,280 @@
---
stage: Monitor
group: Analytics Instrumentation
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Browser SDK
This SDK is for instrumenting web sites and applications to send data for the GitLab [product analytics functionality](../index.md).
## How to use the Browser SDK
### Using the NPM package
Add the NPM package to your package JSON using your preferred package manager:
::Tabs
:::TabTitle yarn
```shell
yarn add @gitlab/application-sdk-browser
```
:::TabTitle npm
```shell
npm i @gitlab/application-sdk-browser
```
::EndTabs
Then, for browser usage import the client SDK:
```javascript
import { glClientSDK } from '@gitlab/application-sdk-browser';
this.glClient = glClientSDK({ appId, host });
```
### Using the script directly
Add the script to the page and assign the client SDK to `window`:
```html
<script src="https://unpkg.com/@gitlab/application-sdk-browser/dist/gl-sdk.min.js"></script>
<script>
window.glClient = window.glSDK.glClientSDK({
appId: 'YOUR_APP_ID',
host: 'YOUR_HOST',
});
</script>
```
You can use a specific version of the SDK like this:
```html
<script src="https://unpkg.com/@gitlab/application-sdk-browser@0.2.5/dist/gl-sdk.min.js"></script>
```
## Browser SDK initialization options
Apart from `appId` and `host`, you can configure the Browser SDK with the following options:
```typescript
interface GitLabClientSDKOptions {
appId: string;
host: string;
hasCookieConsent?: boolean;
trackerId?: string;
pagePingTracking?:
| boolean
| {
minimumVisitLength?: number;
heartbeatDelay?: number;
};
plugins?: AllowedPlugins;
}
```
| Option | Description |
| :---------------------------- | :---------- |
| `appId` | The ID provided by the GitLab Project Analytics setup guide. This ID ensures your data is sent to your analytics instance. |
| `host` | The GitLab Project Analytics instance provided by the setup guide. |
| `hasCookieConsent` | Whether to use cookies to identify unique users. Set to `false` by default. When `false`, users are considered anonymous users. No cookies or other storage mechanisms are used to identify users. |
| `trackerId` | Used to differentiate between multiple trackers running on the same page or application, because each tracker instance can be configured differently to capture different sets of data. This identifier helps ensure that the data sent to the collector is correctly associated with the correct tracker configuration. Default value is `gitlab`. |
| `pagePingTracking` | Option to track user engagement on your website or application by sending periodic events while a user is actively browsing a page. Page pings provide valuable insight into how users interact with your content, such as how long they spend on a page, which sections they are viewing, and whether they are scrolling. `pagePingTracking` can be boolean or an object. As a boolean, set to `true` it enables page ping with default options, and set to `false` it disables page ping tracking. As an object, it has two options: `minimumVisitLength` (the minimum time that must have elapsed before the first heartbeat) and `heartbeatDelay` (the interval at which the callback is fired). |
| `plugins` | Specify which plugins to enable or disable. By default all plugins are enabled. |
### Plugins
- `Client Hints`: An alternative to tracking the User Agent, which is particularly useful in browsers that are freezing the User Agent string.
Enabling this plugin will automatically capture the following context:
For example,
[iglu:org.ietf/http_client_hints/jsonschema/1-0-0](https://github.com/snowplow/iglu-central/blob/master/schemas/org.ietf/http_client_hints/jsonschema/1-0-0)
has the following configuration:
```json
{
"isMobile":false,
"brands":[
{
"brand":"Google Chrome",
"version":"89"
},
{
"brand":"Chromium",
"version":"89"
}
]
}
```
- `Link Click Tracking`: With this plugin, the tracker adds click event listeners to all link elements. Link clicks are tracked as self-describing events. Each link-click event captures the link's `href` attribute. The event also has fields for the link's ID, classes, and target (where the linked document is opened, such as a new tab or new window).
- `Performance Timing`: It collects performance-related data from a user's browser using the `Navigation Timing API`. This API provides detailed information about the various stages of loading a web page, such as domain lookup, connection time, content download, and rendering times. This plugin helps to gather insights into how well a website performs for users, identify potential performance bottlenecks, and improve the overall user experience.
- `Error Tracking`: It helps to capture and track errors that occur on a website or application. By monitoring these errors, you can gain insights into potential issues with code or third-party libraries, which can help to improve the overall user experience, and maintain the quality of the website or application.
By default all plugins are enabled. You can disable or enable these plugins through the `plugins` object:
```typescript
const tracker = glClientSDK({
...options,
plugins: {
clientHints: true,
linkTracking: true,
performanceTiming: true,
errorTracking: true,
},
});
```
## Methods
### `identify`
Used to associate a user and their attributes with the session and tracking events.
```javascript
glClient.identify(userId, userAttributes);
```
| Property | Type | Description |
| :--------------- | :-------------------------- | :---------------------------------------------------------------------------- |
| `userId` | `String` | The user identifier your application uses to identify individual users. |
| `userAttributes` | `Object`/`Null`/`undefined` | The user attributes that need to be added to the session and tracking events. |
### `page`
Used to trigger a pageview event.
```javascript
glClient.page(eventAttributes);
```
| Property | Type | Description |
| :---------------- | :-------------------------- | :---------------------------------------------------------------- |
| `eventAttributes` | `Object`/`Null`/`undefined` | The event attributes that need to be added to the pageview event. |
The `eventAttributes` object supports the following optional properties:
| Property | Type | Description |
|:------------------|:------------|:------------|
| `title` | `String` | Override the default page title. |
| `contextCallback` | `Function` | A callback that fires on the page view. |
| `context` | `Object` | Add context (additional information) on the page view. |
| `timestamp` | `timestamp` | Set the true timestamp or overwrite the device-sent timestamp on an event. |
### `track`
Used to trigger a custom event.
```javascript
glClient.track(eventName, eventAttributes);
```
| Property | Type | Description |
| :---------------- | :-------------------------- | :--------------------------------------------------------------- |
| `eventName` | `String` | The name of the custom event. |
| `eventAttributes` | `Object`/`Null`/`undefined` | The event attributes that need to be added to the tracked event. |
### `refreshLinkClickTracking`
`enableLinkClickTracking` tracks only clicks on links that exist when the page has loaded. To track new links added to the page after it has been loaded, use `refreshLinkClickTracking`.
```javascript
glClient.refreshLinkClickTracking();
```
### `trackError`
NOTE:
`trackError` is supported on the Browser SDK, but the resulting events are not used or available.
Used to capture errors. This works only when the `errorTracking` plugin is enabled. The [plugin](#plugins) is enabled by default.
```javascript
glClient.trackError(eventAttributes);
```
For example, `trackError` can be used in `try...catch` like below:
```javascript
try {
// Call the function that throws an error
throwError();
} catch (error) {
glClient.trackError({
message: error.message, // "This is a custom error"
filename: error.fileName || 'unknown', // The file in which the error occurred (e.g., "index.html")
lineno: error.lineNumber || 0, // The line number where the error occurred (e.g., 2)
colno: error.columnNumber || 0, // The column number where the error occurred (e.g., 6)
error: error, // The Error object itself
});
}
```
| Property | Type | Description |
| :---------------- | :------- | :------------------------------------------------------------------------------------------------------------------- |
| `eventAttributes` | `Object` | The event attributes that need to be added to the tracked event. `message` is a mandatory key in `eventAttributes`. |
### `addCookieConsent`
`addCookieConsent` is used to allow tracking of user identifiers via cookies. By default `hasCookieConsent` is false, and no user identifiers are passed. To enable tracking of user identifiers, call the `addCookieConsent` method. This step is not needed if you intialized the Browser SDK with `hasCookieConsent` set to true.
```javascript
glClient.addCookieConsent();
```
### `setCustomUrl`
Used to set a custom URL for tracking.
```javascript
glClient.setCustomUrl(url);
```
| Property | Type | Description |
| :------- | :------- | :------------------------------------------------ |
| `url` | `String` | The custom URL that you want to set for tracking. |
### `setReferrerUrl`
Used to set a referrer URL for tracking.
```javascript
glClient.setReferrerUrl(url);
```
| Property | Type | Description |
| :------- | :------- | :-------------------------------------------------- |
| `url` | `String` | The referrer URL that you want to set for tracking. |
### `setDocumentTitle`
Used to override the document title.
```javascript
glClient.setDocumentTitle(title);
```
| Property | Type | Description |
| :------- | :------- | :--------------------------------- |
| `title` | `String` | The document title you want to set. |
## Contribute
If you would like to contribute to Browser SDK, follow the [contributing guide](https://gitlab.com/gitlab-org/analytics-section/product-analytics/gl-application-sdk-js/-/blob/main/docs/Contributing.md).
## Troubleshooting
If the Browser SDK is not sending events, or behaving in an unexpected way, take the following actions:
1. Verify that the `appId` and host values in the options object are correct.
1. Check if any browser privacy settings, extensions, or ad blockers are interfering with the Browser SDK.
For more information and assistance, see the [Snowplow documentation](https://docs.snowplow.io/docs/collecting-data/collecting-from-own-applications/javascript-trackers/web-tracker/)
or contact the [Analytics Instrumentation team](https://handbook.gitlab.com/handbook/engineering/development/analytics/analytics-instrumentation/#team-members).

View File

@ -0,0 +1,18 @@
---
stage: Monitor
group: Analytics Instrumentation
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Instrumentation
To instrument an application to send events to GitLab product analytics you can use one of the following language and platform specific tracking SDKs:
- [Browser SDK](browser_sdk.md)
- [Ruby SDK](https://gitlab.com/gitlab-org/analytics-section/product-analytics/gl-application-sdk-rb)
- [Python SDK](https://gitlab.com/gitlab-org/analytics-section/product-analytics/gl-application-sdk-python)
- [Node SDK](https://gitlab.com/gitlab-org/analytics-section/product-analytics/gl-application-sdk-node)
- [.NET SDK](https://gitlab.com/gitlab-org/analytics-section/product-analytics/gl-application-sdk-dotnet)
- [Snowplow SDK](https://docs.snowplow.io/docs/collecting-data/collecting-from-own-applications/) is compatible, but not supported by GitLab.
If you are interested in other SDKs to be supported, please comment in [issue 391970](https://gitlab.com/gitlab-org/gitlab/-/issues/391970).

View File

@ -29,7 +29,7 @@ Here's an overview of what we're going to do:
To follow along this tutorial, you must:
- [Enable product analytics](../../user/product_analytics/index.md#enable-product-analytics) for your instance.
- [Enable product analytics](../../operations/product_analytics/index.md#enable-product-analytics) for your instance.
- Have the Owner role for the group you create the project in.
## Create a project from a template
@ -74,7 +74,7 @@ Your project is now onboarded and ready for your application to start sending ev
To collect and send usage events to GitLab, you must include a code snippet in your website.
You can choose from several platform and technology-specific tracking SDKs to integrate with your application.
For this example website, we use the [Browser SDK](../../user/product_analytics/instrumentation/browser_sdk.md).
For this example website, we use the [Browser SDK](../../operations/product_analytics/instrumentation/browser_sdk.md).
To instrument your new website:

View File

@ -24,7 +24,7 @@ filters and visualizations to query and retrieve results.
Analytics dashboards use the following data sources:
- [Product analytics](../product_analytics/index.md)
- [Product analytics](../../operations/product_analytics/index.md)
- [Value Stream Management](../analytics/value_streams_dashboard.md)
You can also add [custom visualization data sources](../../development/fe_guide/customizable_dashboards.md#adding-a-new-visualization-data-source).
@ -37,7 +37,7 @@ You cannot edit the built-in dashboards, but you can create custom dashboards wi
### Product analytics dashboards
When [product analytics](../product_analytics/index.md) is enabled and onboarded, two built-in dashboards are available:
When product analytics is enabled and onboarded, two built-in dashboards are available:
- **Audience** displays metrics related to traffic, such as the number of users and sessions.
- **Behavior** displays metrics related to user activity, such as the number of page views and events.
@ -78,7 +78,7 @@ You can use the dashboard designer to:
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/425048) in GitLab 16.7. Feature flag `combined_analytics_visualization_editor` removed.
NOTE:
This feature is only compatible with the [product analytics](../product_analytics/index.md) data source.
This feature is only compatible with the product analytics data source.
You can use the visualization designer to:
@ -327,7 +327,7 @@ If the dashboard displays a global error message that data could not be loaded,
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.
- For product analytics, make sure your [admin and project settings](../../operations/product_analytics/index.md#project-level-settings) are set up correctly.
### `Invalid dashboard configuration`
@ -343,9 +343,9 @@ defined in `ee/app/validators/json_schemas/analytics_visualization.json`.
If a dashboard panel displays an error message:
- Make sure your [Cube query](../product_analytics/index.md#product-analytics-dashboards) and
- Make sure your [Cube query](../../operations/product_analytics/index.md#product-analytics-dashboards) and
[visualization](../analytics/analytics_dashboards.md#define-a-chart-visualization) configurations are set up correctly.
- For [product analytics](../product_analytics/index.md), also check that your visualization's Cube query is valid.
- For product analytics, also check that your visualization's Cube query is valid.
### Generate visualization with GitLab Duo returns unexpected results

View File

@ -26,7 +26,7 @@ Use these features to gain insights into your overall software development lifec
| [Usage trends](../../administration/analytics/usage_trends.md) | Overview of instance data and changes in data volume over time. | **{dotted-circle}** No | **{dotted-circle}** No | **{check-circle}** Yes |
| [Instance-level analytics](../../administration/analytics/index.md) | Aggregated analytics across GitLab about multiple projects and groups in one place. | **{dotted-circle}** No | **{dotted-circle}** No | **{check-circle}** Yes |
| [Insights](../project/insights/index.md) | Customizable reports to explore issues, merged merge requests, and triage hygiene. | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No |
| [Product analytics](../product_analytics/index.md) | Understanding how users behave and interact with your product.| **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No |
| [Product analytics](../../operations/product_analytics/index.md) | Understanding how users behave and interact with your product.| **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No |
| [Analytics dashboards](analytics_dashboards.md) | Built-in and customizable dashboards to visualize collected data. | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No |
### Productivity analytics

View File

@ -1,463 +1,13 @@
---
stage: Monitor
group: Platform Insights
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
redirect_to: '../../operations/product_analytics/index.md'
remove_date: '2025-01-04'
---
# Product analytics
<!-- markdownlint-disable -->
DETAILS:
**Tier:** Ultimate
**Offering:** GitLab.com, Self-managed
**Status:** Beta
This document was moved to [another location](../../operations/product_analytics/index.md).
> - Introduced in GitLab 15.4 as an [experiment](../../policy/experiment-beta-support.md#experiment) feature [with a flag](../../administration/feature_flags.md) named `cube_api_proxy`. Disabled by default.
> - `cube_api_proxy` changed to reference only the [product analytics API](../../api/product_analytics.md) in GitLab 15.6.
> - `cube_api_proxy` removed and replaced with `product_analytics_internal_preview` in GitLab 15.10.
> - `product_analytics_internal_preview` replaced with `product_analytics_dashboards` in GitLab 15.11.
> - Snowplow integration [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/398253) in GitLab 15.11 [with a flag](../../administration/feature_flags.md) named `product_analytics_snowplow_support`. Disabled by default.
> - Snowplow integration feature flag `product_analytics_snowplow_support` [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/130228) in GitLab 16.4.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/414865) from GitLab self-managed to GitLab.com in 16.7.
> - Enabled in GitLab 16.7 as a [beta](../../policy/experiment-beta-support.md#beta) feature.
> - `product_analytics_dashboards` [enabled](https://gitlab.com/gitlab-org/gitlab/-/issues/398653) by default in GitLab 16.11.
> - [Enabled on self-managed and GitLab Dedicated](https://gitlab.com/gitlab-org/gitlab/-/issues/444345) in GitLab 16.11.
> - Feature flag `product_analytics_dashboards` [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/454059) in GitLab 17.1.
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/167192) to beta and feature flag `product_analytics_admin_settings` added in GitLab 17.5.
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/167296) to beta and feature flag `product_analytics_features` added in GitLab 17.5.
FLAG:
The availability of this feature is controlled by a feature flag.
For more information, see the history.
This feature is not ready for production use.
The product analytics feature empowers you to track user behavior and gain insights into how your
applications are used and how users interact with your product.
By using the data collected with product analytics in GitLab, you can better understand your users,
identify friction points in funnels, make data-driven product decisions, and ultimately build better
products that drive user engagement and business growth.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an overview of the product analytics setup and functionality,
watch the [Product Analytics walkthrough videos](https://www.youtube.com/playlist?list=PL05JrBw4t0Kqfb4oLOFKkXxNrBJzDQ3sL&feature=shared).
For more information about the vision and development of product analytics, see the [group direction page](https://about.gitlab.com/direction/monitor/product-analytics/).
To leave feedback about product analytics bugs or functionality:
- Comment on [issue 391970](https://gitlab.com/gitlab-org/gitlab/-/issues/391970).
- Create an issue with the `group::product analytics` label.
## How product analytics works
Product analytics uses the following tools:
- [**Snowplow**](https://docs.snowplow.io/docs/) - A developer-first engine for collecting behavioral data and passing it through to ClickHouse.
- [**ClickHouse**](../../integration/clickhouse.md) - A database suited to store, query, and retrieve analytical data.
- [**Cube**](https://cube.dev/docs/product/introduction) - A universal semantic layer that provides an API to run queries against the data stored in ClickHouse.
The following diagram illustrates the product analytics flow:
```mermaid
%%{init: { "fontFamily": "GitLab Sans" }}%%
flowchart TB
accTitle: Product Analytics flow
accDescr: How data is collected, processed, and visualized in dashboards.
subgraph Event collection
A([SDK]) --Send user data--> B[Snowplow Collector]
B --Pass data--> C[Snowplow Enricher]
end
subgraph Data warehouse
C --Transform and enrich data--> D([ClickHouse])
end
subgraph Data visualization
F([Dashboards with panels/visualizations])
F --Request data--> G[Product Analytics API]
G --Run Cube queries with pre-aggregations--> H[Cube]
H --Get data--> D
D --Return results--> H
H --Transform data to be rendered--> G
G --Return data--> F
end
```
## Enable product analytics
> - Introduced in GitLab 15.6 [with a flag](../../administration/feature_flags.md) named `cube_api_proxy`. Disabled by default.
> - Moved behind a [flag](../../administration/feature_flags.md) named `product_analytics_admin_settings` in GitLab 15.7. Disabled by default.
> - Feature flag `cube_api_proxy` removed and replaced with `product_analytics_internal_preview` in GitLab 15.10.
> - Feature flag `product_analytics_internal_preview` replaced with `product_analytics_dashboards` in GitLab 15.11.
> - Feature flag `product_analytics_admin_settings` [enabled](https://gitlab.com/gitlab-org/gitlab/-/issues/385602) by default in GitLab 16.11.
> - Feature flag `product_analytics_admin_settings` [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/454342) in GitLab 17.1.
To track events in your project's applications,
you must enable and configure product analytics.
### Product analytics provider
Your GitLab instance connects to a product analytics provider.
A product analytics provider is the collection of services required to receive,
process, store and query your analytics data.
::Tabs
:::TabTitle GitLab-managed provider
DETAILS:
**Offering:** GitLab.com
On GitLab.com you can use a GitLab-managed provider offered only in the Google Cloud Platform zone `us-central-1`. This service is offered only in beta.
If GitLab manages your product analytics provider, then your analytics data is retained for one year.
You can request to delete your data at any time by [contacting support](https://about.gitlab.com/support/#contact-support).
:::TabTitle Self-managed provider
>[Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/117804) in GitLab 16.0.
A self-managed product analytics provider is a deployed instance of the
[product analytics Helm charts](https://gitlab.com/gitlab-org/analytics-section/product-analytics/helm-charts).
On GitLab.com, the self-managed provider details are defined in [project-level settings](#project-level-settings).
On GitLab self-managed and GitLab Dedicated, you must define the self-managed analytics provider in [instance-level settings](#instance-level-settings).
If you need different providers for different projects, you can define additional analytics providers in [project-level settings](#project-level-settings).
::EndTabs
### Instance-level settings
**Offering:** Self-managed, GitLab Dedicated
Prerequisites:
- You must have administrator access for the instance.
NOTE:
These instance-level settings are required to enable product analytics on GitLab self-managed and GitLab Dedicated,
and cascade to all projects by default.
To enable product analytics on your instance:
1. On the left sidebar, at the bottom, select **Admin**.
1. Select **Settings > Analytics**.
1. Enter the configuration values.
1. Select **Save changes**.
### Project-level settings
If you want to have a product analytics instance with a different configuration for your project,
you can override the instance-level settings defined by the administrator on a per-project basis.
Prerequisites:
- You must have at least the Maintainer role for the project or group the project belongs to.
- The project must be in a group namespace.
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Settings > Analytics**.
1. Expand **Data sources** and enter the configuration values.
1. Select **Save changes**.
## Onboard a GitLab project
> - Minimum required role [changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/154089/) in GitLab 17.1.
Prerequisites:
- You must have at least the Maintainer role for the project or group the project belongs to.
Onboarding a GitLab project means preparing it to receive events that are used for product analytics.
To onboard a project:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Analyze > Analytics dashboards**.
1. Under **Product analytics**, select **Set up**.
Then continue with the setup depending on the provider type.
::Tabs
:::TabTitle GitLab-managed provider
Prerequisites:
- You must have access to the [GitLab-managed provider](#product-analytics-provider).
1. Select the **I agree to event collection and processing in this region** checkbox.
1. Select **Connect GitLab-managed provider**.
1. Remove already configured project-level settings for a self-managed provider:
1. Select **Go to analytics settings**.
1. Expand **Data sources** and remove the configuration values.
1. Select **Save changes**.
1. Select **Analyze > Analytics dashboards**.
1. Under **Product analytics**, select **Set up**.
1. Select **Connect GitLab-managed provider**.
Your instance is being created, and the project onboarded.
:::TabTitle Self-managed provider
1. Select **Connect your own provider**.
1. Configure project-level settings for your self-managed provider:
1. Select **Go to analytics settings**.
1. Expand **Data sources** and enter the configuration values.
1. Select **Save changes**.
1. Select **Analyze > Analytics dashboards**.
1. Under **Product analytics**, select **Set up**.
1. Select **Connect your own provider**.
Your instance is being created, and the project onboarded.
::EndTabs
## Instrument your application
You can instrument code to collect data by using [tracking SDKs](instrumentation/index.md).
## Product analytics dashboards
> - Introduced in GitLab 15.5 [with a flag](../../administration/feature_flags.md) named `product_analytics_internal_preview`. Disabled by default.
Product analytics dashboards are a subset of dashboards under [Analytics dashboards](../analytics/analytics_dashboards.md).
Specifically, product analytics dashboards and visualizations use the `cube_analytics` data type.
The `cube_analytics` data type connects to the Cube instance defined when [product analytics was enabled](#enable-product-analytics).
All filters and queries are sent to the Cube instance, and the returned data is processed by the
product analytics data source to be rendered by the appropriate visualizations.
Data table visualizations from `cube_analytics` have an additional configuration option for rendering `links`.
This option is an array of objects, each with `text` and `href` properties to specify the dimensions to be used in links.
If `href` contains multiple dimensions, values are joined into a single URL.
View an [example](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/validators/json_schemas/analytics_visualization.json?ref_type=heads#L112).
### Filling missing data
> - Introduced in GitLab 16.3 [with a flag](../../administration/feature_flags.md) named `product_analytics_dashboards`. Disabled by default.
When [exporting data](#raw-data-export) or [viewing dashboards](../analytics/analytics_dashboards.md#view-project-dashboards),
if there is no data for a given day, the missing data is autofilled with `0`.
The autofill approach has both benefits and limitations.
- Benefits:
- The visualization's day axis matches the selected date range, removing ambiguity about missing data.
- Data exports have rows for the entire date range, making data analysis easier.
- Limitations:
- The `day` [granularity](https://cube.dev/docs/product/apis-integrations/rest-api/query-format) must be used.
All other granularities are not supported.
- Only date ranges defined by the [`inDateRange`](https://cube.dev/docs/product/apis-integrations/rest-api/query-format#indaterange) filter are filled.
- The date selector in the UI already uses this filter.
- The filling of data ignores the query-defined limit. If you set a limit of 10 data points over 20 days, it
returns 20 data points, with the missing data filled by `0`. [Issue 417231](https://gitlab.com/gitlab-org/gitlab/-/issues/417231) proposes a solution to this limitation.
## Funnel analysis
Use funnel analysis to understand the flow of users through your application, and where
users drop out of a predefined flow (for example, a checkout process or ticket purchase).
Each project can define an unlimited number of funnels.
Like dashboards, funnels are defined with the GitLab YAML schema
and stored in the `.gitlab/analytics/funnels/` directory of a project repository.
If a repository has a custom dashboards pointer project that points to another repository,
funnels must be defined in the pointer project.
### Create a funnel dashboard
To create a funnel dashboard, you must first create a funnel definition file and a visualization.
Each funnel must have a custom visualization defined for it.
When funnel definitions and visualizations are ready,
you can [create a custom dashboard](../analytics/analytics_dashboards.md#create-a-custom-dashboard)
to visualize funnel analysis behavior.
#### Create a funnel definition
1. In the `.gitlab/analytics/` directory, create a directory named `funnels`.
1. In the new `.gitlab/analytics/funnels` directory, create a funnel definition YAML file.
Funnel definitions must include the key `seconds_to_convert` and an array of `steps`.
| Key | Description |
|----------------------|----------------------------------------------------------|
| `seconds_to_convert` | The number of seconds a user has to complete the funnel. |
| `steps` | An array of funnel steps. |
Each step must include the keys `name`, `target`, and `action`.
| Key | Description |
|----------|------------------------------------------------------------------------------------------|
| `name` | The name of the step. This should be a unique slug. |
| `action` | The action performed. (Only `pageview` is supported.) |
| `target` | The target of the step. (Because only `pageview` is supported, this should be a path.) |
The following example defines a funnel that tracks users who completed a purchase within one hour by going through three target pages:
```yaml
seconds_to_convert: 3600
steps:
- name: view_page_1
target: '/page1.html'
action: 'pageview'
- name: view_page_2
target: '/page2.html'
action: 'pageview'
- name: view_page_3
target: '/page3.html'
action: 'pageview'
```
#### Create a funnel visualization
To create funnel visualizations, follow the steps for [defining a chart visualization](../analytics/analytics_dashboards.md#define-a-chart-visualization).
Funnel visualizations support the measure `count` and the dimension `step`.
The following example defines a column chart that visualizes the number of users who reached different steps in a funnel:
```yaml
version: 1
type: ColumnChart
data:
type: cube_analytics
query:
measures:
- FUNNEL_NAME.count
dimensions:
- FUNNEL_NAME.step
limit: 100
timezone: UTC
timeDimensions: []
options:
xAxis:
name: Step
type: category
yAxis:
name: Total
type: value
```
NOTE:
The funnel name defined in the YAML definition is converted to a slug that can be referenced in visualization definitions.
For example, the funnel name `Successful Conversions` is converted to `successful_conversions`.
### Query a funnel
You can [query the funnel data with the REST API](../../api/product_analytics.md#send-query-request-to-cube).
To do this, you can use the example query body below, where you need to replace `FUNNEL_NAME` with your funnel's name.
NOTE:
The name of a funnel is generated from the filename of the funnel definition YAML file,
by separating words with underscores and removing special characters.
For example, for a funnel definition file in `.gitlab/analytics/funnels/Successful Conversions.yaml`
the funnel name is `successful_conversions`.
This funnel name can be referenced in visualization definitions.
NOTE:
The `afterDate` filter is not supported. Use `beforeDate` or `inDateRange`.
```json
{
"query": {
"measures": [
"FUNNEL_NAME.count"
],
"order": {
"FUNNEL_NAME.count": "desc"
},
"filters": [
{
"member": "FUNNEL_NAME.date",
"operator": "beforeDate",
"values": [
"2023-02-01"
]
}
],
"dimensions": [
"FUNNEL_NAME.step"
]
}
}
```
## Raw data export
Exporting the raw event data from the underlying storage engine can help you debug and create datasets for data analysis.
Because Cube acts as an abstraction layer between the raw data and the API, the exported raw data has some caveats:
- Data is grouped by the selected dimensions. Therefore, the exported data might be incomplete, unless including both `utcTime` and `userAnonymousId`.
- Data is by default limited to 10,000 rows, but you can increase the limit to maximum 50,000 rows. If your dataset has more than 50,000 rows, you must paginate through the results by using the `limit` and `offset` parameters.
- Data is always returned in JSON format. If you need it in a different format, you need to convert the JSON to the required format using a scripting language of your choice.
[Issue 391683](https://gitlab.com/gitlab-org/gitlab/-/issues/391683) tracks efforts to implement a more scalable export solution.
### Export raw data with Cube queries
You can [query the raw data with the REST API](../../api/product_analytics.md#send-query-request-to-cube),
and convert the JSON output to any required format.
To export the raw data for a specific dimension, pass a list of dimensions to the `dimensions` key.
For example, the following query outputs the raw data for the attributes listed:
```json
POST /api/v4/projects/PROJECT_ID/product_analytics/request/load?queryType=multi
{
"query":{
"dimensions": [
"TrackedEvents.docEncoding",
"TrackedEvents.docHost",
"TrackedEvents.docPath",
"TrackedEvents.docSearch",
"TrackedEvents.eventType",
"TrackedEvents.localTzOffset",
"TrackedEvents.pageTitle",
"TrackedEvents.src",
"TrackedEvents.utcTime",
"TrackedEvents.vpSize"
],
"order": {
"TrackedEvents.apiKey": "asc"
}
}
}
```
If the request is successful, the returned JSON includes an array of rows of results.
## View product analytics usage quota
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/424153) in GitLab 16.6 [with a flag](../../administration/feature_flags.md) named `product_analytics_usage_quota`. Disabled by default.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/427838) in GitLab 16.7. Feature flag `product_analytics_usage_quota` removed.
Product analytics usage quota is calculated from the number of events received from instrumented applications.
To view product analytics usage quota:
1. On the left sidebar, select **Search or go to** and find your group.
1. Select **Settings > Usage quota**.
1. Select the **Product analytics** tab.
The tab displays the monthly totals for the group and a breakdown of usage per project.
The current month displays events counted to date.
The usage quota excludes projects that are not onboarded with product analytics.
## Best practices
- Define key metrics and goals from the start. Decide what questions you want to answer so you know how to use collected data.
- Use event data from all stages of the user journey. This data provides a comprehensive view of the user experience.
- Build dashboards aligned with team needs. Different teams need different data insights.
- Review dashboards regularly. This way, you can verify customer outcomes, identify trends in data, and update visualizations.
- Export raw data periodically. Dashboards provide only an overview of a subset of data, so you should export the data for a deeper analysis.
## Troubleshooting
### No events are collected
Check your [instrumentation details](#enable-product-analytics),
and make sure product analytics is enabled and set up correctly.
### Access to product analytics is restricted
Check that you are connected to a [product analytics provider](#product-analytics-provider).
<!-- This redirect file can be deleted after <2025-01-04>. -->
<!-- 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 (for example, link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->

View File

@ -1,280 +1,13 @@
---
stage: Monitor
group: Analytics Instrumentation
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
redirect_to: '../../../operations/product_analytics/instrumentation/browser_sdk.md'
remove_date: '2025-01-04'
---
# Browser SDK
<!-- markdownlint-disable -->
This SDK is for instrumenting web sites and applications to send data for the GitLab [product analytics functionality](../index.md).
This document was moved to [another location](../../../operations/product_analytics/instrumentation/browser_sdk.md).
## How to use the Browser SDK
### Using the NPM package
Add the NPM package to your package JSON using your preferred package manager:
::Tabs
:::TabTitle yarn
```shell
yarn add @gitlab/application-sdk-browser
```
:::TabTitle npm
```shell
npm i @gitlab/application-sdk-browser
```
::EndTabs
Then, for browser usage import the client SDK:
```javascript
import { glClientSDK } from '@gitlab/application-sdk-browser';
this.glClient = glClientSDK({ appId, host });
```
### Using the script directly
Add the script to the page and assign the client SDK to `window`:
```html
<script src="https://unpkg.com/@gitlab/application-sdk-browser/dist/gl-sdk.min.js"></script>
<script>
window.glClient = window.glSDK.glClientSDK({
appId: 'YOUR_APP_ID',
host: 'YOUR_HOST',
});
</script>
```
You can use a specific version of the SDK like this:
```html
<script src="https://unpkg.com/@gitlab/application-sdk-browser@0.2.5/dist/gl-sdk.min.js"></script>
```
## Browser SDK initialization options
Apart from `appId` and `host`, you can configure the Browser SDK with the following options:
```typescript
interface GitLabClientSDKOptions {
appId: string;
host: string;
hasCookieConsent?: boolean;
trackerId?: string;
pagePingTracking?:
| boolean
| {
minimumVisitLength?: number;
heartbeatDelay?: number;
};
plugins?: AllowedPlugins;
}
```
| Option | Description |
| :---------------------------- | :---------- |
| `appId` | The ID provided by the GitLab Project Analytics setup guide. This ID ensures your data is sent to your analytics instance. |
| `host` | The GitLab Project Analytics instance provided by the setup guide. |
| `hasCookieConsent` | Whether to use cookies to identify unique users. Set to `false` by default. When `false`, users are considered anonymous users. No cookies or other storage mechanisms are used to identify users. |
| `trackerId` | Used to differentiate between multiple trackers running on the same page or application, because each tracker instance can be configured differently to capture different sets of data. This identifier helps ensure that the data sent to the collector is correctly associated with the correct tracker configuration. Default value is `gitlab`. |
| `pagePingTracking` | Option to track user engagement on your website or application by sending periodic events while a user is actively browsing a page. Page pings provide valuable insight into how users interact with your content, such as how long they spend on a page, which sections they are viewing, and whether they are scrolling. `pagePingTracking` can be boolean or an object. As a boolean, set to `true` it enables page ping with default options, and set to `false` it disables page ping tracking. As an object, it has two options: `minimumVisitLength` (the minimum time that must have elapsed before the first heartbeat) and `heartbeatDelay` (the interval at which the callback is fired). |
| `plugins` | Specify which plugins to enable or disable. By default all plugins are enabled. |
### Plugins
- `Client Hints`: An alternative to tracking the User Agent, which is particularly useful in browsers that are freezing the User Agent string.
Enabling this plugin will automatically capture the following context:
For example,
[iglu:org.ietf/http_client_hints/jsonschema/1-0-0](https://github.com/snowplow/iglu-central/blob/master/schemas/org.ietf/http_client_hints/jsonschema/1-0-0)
has the following configuration:
```json
{
"isMobile":false,
"brands":[
{
"brand":"Google Chrome",
"version":"89"
},
{
"brand":"Chromium",
"version":"89"
}
]
}
```
- `Link Click Tracking`: With this plugin, the tracker adds click event listeners to all link elements. Link clicks are tracked as self-describing events. Each link-click event captures the link's `href` attribute. The event also has fields for the link's ID, classes, and target (where the linked document is opened, such as a new tab or new window).
- `Performance Timing`: It collects performance-related data from a user's browser using the `Navigation Timing API`. This API provides detailed information about the various stages of loading a web page, such as domain lookup, connection time, content download, and rendering times. This plugin helps to gather insights into how well a website performs for users, identify potential performance bottlenecks, and improve the overall user experience.
- `Error Tracking`: It helps to capture and track errors that occur on a website or application. By monitoring these errors, you can gain insights into potential issues with code or third-party libraries, which can help to improve the overall user experience, and maintain the quality of the website or application.
By default all plugins are enabled. You can disable or enable these plugins through the `plugins` object:
```typescript
const tracker = glClientSDK({
...options,
plugins: {
clientHints: true,
linkTracking: true,
performanceTiming: true,
errorTracking: true,
},
});
```
## Methods
### `identify`
Used to associate a user and their attributes with the session and tracking events.
```javascript
glClient.identify(userId, userAttributes);
```
| Property | Type | Description |
| :--------------- | :-------------------------- | :---------------------------------------------------------------------------- |
| `userId` | `String` | The user identifier your application uses to identify individual users. |
| `userAttributes` | `Object`/`Null`/`undefined` | The user attributes that need to be added to the session and tracking events. |
### `page`
Used to trigger a pageview event.
```javascript
glClient.page(eventAttributes);
```
| Property | Type | Description |
| :---------------- | :-------------------------- | :---------------------------------------------------------------- |
| `eventAttributes` | `Object`/`Null`/`undefined` | The event attributes that need to be added to the pageview event. |
The `eventAttributes` object supports the following optional properties:
| Property | Type | Description |
|:------------------|:------------|:------------|
| `title` | `String` | Override the default page title. |
| `contextCallback` | `Function` | A callback that fires on the page view. |
| `context` | `Object` | Add context (additional information) on the page view. |
| `timestamp` | `timestamp` | Set the true timestamp or overwrite the device-sent timestamp on an event. |
### `track`
Used to trigger a custom event.
```javascript
glClient.track(eventName, eventAttributes);
```
| Property | Type | Description |
| :---------------- | :-------------------------- | :--------------------------------------------------------------- |
| `eventName` | `String` | The name of the custom event. |
| `eventAttributes` | `Object`/`Null`/`undefined` | The event attributes that need to be added to the tracked event. |
### `refreshLinkClickTracking`
`enableLinkClickTracking` tracks only clicks on links that exist when the page has loaded. To track new links added to the page after it has been loaded, use `refreshLinkClickTracking`.
```javascript
glClient.refreshLinkClickTracking();
```
### `trackError`
NOTE:
`trackError` is supported on the Browser SDK, but the resulting events are not used or available.
Used to capture errors. This works only when the `errorTracking` plugin is enabled. The [plugin](#plugins) is enabled by default.
```javascript
glClient.trackError(eventAttributes);
```
For example, `trackError` can be used in `try...catch` like below:
```javascript
try {
// Call the function that throws an error
throwError();
} catch (error) {
glClient.trackError({
message: error.message, // "This is a custom error"
filename: error.fileName || 'unknown', // The file in which the error occurred (e.g., "index.html")
lineno: error.lineNumber || 0, // The line number where the error occurred (e.g., 2)
colno: error.columnNumber || 0, // The column number where the error occurred (e.g., 6)
error: error, // The Error object itself
});
}
```
| Property | Type | Description |
| :---------------- | :------- | :------------------------------------------------------------------------------------------------------------------- |
| `eventAttributes` | `Object` | The event attributes that need to be added to the tracked event. `message` is a mandatory key in `eventAttributes`. |
### `addCookieConsent`
`addCookieConsent` is used to allow tracking of user identifiers via cookies. By default `hasCookieConsent` is false, and no user identifiers are passed. To enable tracking of user identifiers, call the `addCookieConsent` method. This step is not needed if you intialized the Browser SDK with `hasCookieConsent` set to true.
```javascript
glClient.addCookieConsent();
```
### `setCustomUrl`
Used to set a custom URL for tracking.
```javascript
glClient.setCustomUrl(url);
```
| Property | Type | Description |
| :------- | :------- | :------------------------------------------------ |
| `url` | `String` | The custom URL that you want to set for tracking. |
### `setReferrerUrl`
Used to set a referrer URL for tracking.
```javascript
glClient.setReferrerUrl(url);
```
| Property | Type | Description |
| :------- | :------- | :-------------------------------------------------- |
| `url` | `String` | The referrer URL that you want to set for tracking. |
### `setDocumentTitle`
Used to override the document title.
```javascript
glClient.setDocumentTitle(title);
```
| Property | Type | Description |
| :------- | :------- | :--------------------------------- |
| `title` | `String` | The document title you want to set. |
## Contribute
If you would like to contribute to Browser SDK, follow the [contributing guide](https://gitlab.com/gitlab-org/analytics-section/product-analytics/gl-application-sdk-js/-/blob/main/docs/Contributing.md).
## Troubleshooting
If the Browser SDK is not sending events, or behaving in an unexpected way, take the following actions:
1. Verify that the `appId` and host values in the options object are correct.
1. Check if any browser privacy settings, extensions, or ad blockers are interfering with the Browser SDK.
For more information and assistance, see the [Snowplow documentation](https://docs.snowplow.io/docs/collecting-data/collecting-from-own-applications/javascript-trackers/web-tracker/)
or contact the [Analytics Instrumentation team](https://handbook.gitlab.com/handbook/engineering/development/analytics/analytics-instrumentation/#team-members).
<!-- This redirect file can be deleted after <2025-01-04>. -->
<!-- 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 (for example, link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->

View File

@ -1,18 +1,13 @@
---
stage: Monitor
group: Analytics Instrumentation
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
redirect_to: '../../../operations/product_analytics/instrumentation/index.md'
remove_date: '2025-01-04'
---
# Instrumentation
<!-- markdownlint-disable -->
To instrument an application to send events to GitLab product analytics you can use one of the following language and platform specific tracking SDKs:
This document was moved to [another location](../../../operations/product_analytics/instrumentation/index.md).
- [Browser SDK](browser_sdk.md)
- [Ruby SDK](https://gitlab.com/gitlab-org/analytics-section/product-analytics/gl-application-sdk-rb)
- [Python SDK](https://gitlab.com/gitlab-org/analytics-section/product-analytics/gl-application-sdk-python)
- [Node SDK](https://gitlab.com/gitlab-org/analytics-section/product-analytics/gl-application-sdk-node)
- [.NET SDK](https://gitlab.com/gitlab-org/analytics-section/product-analytics/gl-application-sdk-dotnet)
- [Snowplow SDK](https://docs.snowplow.io/docs/collecting-data/collecting-from-own-applications/) is compatible, but not supported by GitLab.
If you are interested in other SDKs to be supported, please comment in [issue 391970](https://gitlab.com/gitlab-org/gitlab/-/issues/391970).
<!-- This redirect file can be deleted after <2025-01-04>. -->
<!-- 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 (for example, link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->

View File

@ -331,18 +331,6 @@ remote_development:
workspaces_per_user_quota: 3
```
## Configuring user access with remote development
You can configure the `user_access` module to access the connected Kubernetes cluster with your GitLab credentials.
This module is configured and runs independently of the `remote_development` module.
Be careful when configuring both `user_access` and `remote_development` in the same GitLab agent.
The `remote_development` clusters manage user credentials (such as personal access tokens) as Kubernetes Secrets.
Any misconfiguration in `user_access` might cause this private data to be accessible over the Kubernetes API.
For more information about configuring `user_access`, see
[Configure Kubernetes access](../../user/clusters/agent/user_access.md#configure-kubernetes-access).
### `use_kubernetes_user_namespaces`
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/13983) in GitLab 17.4.
@ -475,3 +463,15 @@ A valid label value:
For more information about `labels`, see
[Labels](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/).
## Configuring user access with remote development
You can configure the `user_access` module to access the connected Kubernetes cluster with your GitLab credentials.
This module is configured and runs independently of the `remote_development` module.
Be careful when configuring both `user_access` and `remote_development` in the same GitLab agent.
The `remote_development` clusters manage user credentials (such as personal access tokens) as Kubernetes Secrets.
Any misconfiguration in `user_access` might cause this private data to be accessible over the Kubernetes API.
For more information about configuring `user_access`, see
[Configure Kubernetes access](../../user/clusters/agent/user_access.md#configure-kubernetes-access).

View File

@ -2,80 +2,86 @@
stage: Create
group: Remote Development
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
description: "Set up the GitLab agent for workspaces."
description: "Set up the GitLab agent to create and manage workspaces in a project."
---
# Tutorial: Set up the GitLab agent
In this tutorial, you'll learn how to set up a cluster agent for remote development workspace.
The goal is to configure the workspace project (WP) with the agent project (AP), so that users in a root group (RG) or nested group (NG) can create and manage workspaces.
For this use, we'll use the group/project layout illustrated in the diagram below.
This tutorial shows you how to set up the GitLab agent so users
can create and manage workspaces in a project.
For this tutorial, the following hierarchy is used:
```mermaid
%%{init: {'theme':'neutral'}}%%
graph TD;
classDef active fill:lightgreen, stroke:#green, color:green, stroke-width:1px;
rootGroup[Root Group - RG]
nestedGroup[Nested group - NG]
p1[Workspace Project - WP]
p2[Agent Project - AP]
ag[Agent remotedev]
topGroup[Top-level group]
subGroup[Subgroup]
workspaceProject[Workspace project]
agentProject[Agent project]
workspaceAgent[Workspace agent]
rootGroup --> nestedGroup
topGroup --> subGroup
nestedGroup --> p1
nestedGroup --> p2
p2 -.- ag
subGroup --> workspaceProject
subGroup --> agentProject
agentProject -.- workspaceAgent
class p1 active;
class workspaceProject active;
```
To set up the agent for the workspace project, you have to:
To set up the GitLab agent, you're going to:
1. [Configure the `remote_development` module in the AP](#configure-the-remote_development-module-in-ap)
1. [Allow the AP for workspace use in the NG](#allow-the-ap-for-workspace-use-in-the-ng)
1. [Grant workspace users necessary permissions](#grant-workspace-users-necessary-permissions)
1. [Configure the `remote_development` module in the agent project](#configure-the-remote_development-module-in-the-agent-project).
1. [Allow the GitLab agent in a group](#allow-the-gitlab-agent-in-a-group).
1. [Grant workspace users the necessary permissions](#grant-workspace-users-the-necessary-permissions).
## Prerequisites
- The [workspace infrastructure](configuration.md#set-up-workspace-infrastructure) must be set up.
- You must be an administrator or group owner.
- Your [workspace infrastructure](configuration.md#set-up-workspace-infrastructure) must be set up.
- You must have administrator access to the instance or the Owner role for the group.
## Configure the `remote_development` module in AP
## Configure the `remote_development` module in the agent project
1. On the left sidebar, select **Search or go to** and find the AP.
1. In the project, create a file `.gitlab/agents/{agentName}/config.yaml` (where `agentName` is the name of the agent created when setting up the workspace infrastructure).
1. In the `config.yaml` file, use the following configuration for [workspace settings](gitlab_agent_configuration.md#workspace-settings):
To configure the `remote_development` module in the agent project:
```yaml
remote_development:
enabled: true
# reference only, please update to actual dns_zone to the load balancer exposed by the Ingress controller.
dns_zone: workspaces.localdev.me
```
1. On the left sidebar, select **Search or go to** and find your project.
1. In your project, create a `.gitlab/agents/<agentName>/config.yaml` file.
`agentName` is the name of the agent you configured when you set up the workspace infrastructure.
1. In `config.yaml`, use the following configuration for [workspace settings](gitlab_agent_configuration.md#workspace-settings):
## Allow the AP for workspace use in the NG
```yaml
remote_development:
enabled: true
dns_zone: "<workspaces.example.dev>" # DNS zone of the URL where workspaces are available
```
When the agent has the `remote_development` module configured, the next step is to allow the agent for workspace creation in a parent group of the workspace project.
Next, you'll allow the GitLab agent in a group.
With the current design, allowing the AP with either the NG or RG (any parent group of the WP), would both make the agent available for the WP. Why should we choose NG over RG?
## Allow the GitLab agent in a group
Because any nested projects under the selected group can use the agent after configuration, so you must carefully consider the group where you allow an agent for workspaces.
When you allow an agent in a group, the group and its subgroups can use that agent.
Carefully consider the group where you allow the GitLab agent.
To allow NG with AP:
To allow the GitLab agent in a group:
1. On the left sidebar, select **Search or go to** and find the NG group.
1. On the left sidebar, select **Search or go to** and find your group.
1. On the left sidebar, select **Settings > Workspaces**.
1. In the **Group agents** section, select the **All agents** tab.
1. From the list of available agents, find the agent with status **Blocked**, and select **Allow**.
1. For the GitLab agent, select **Allow**.
1. On the confirmation dialog, select **Allow agent**.
## Grant workspace users necessary permissions
Now it's time to grant workspace users the necessary permissions to create and manage workspaces.
One final step is to grant users, who would create and manage WP workspaces, necessary permissions.
## Grant workspace users the necessary permissions
To manage WP workspaces by using AP as the agent, users must have at least the Developer role for both AP and WP. For more information about [user roles](../permissions.md) and [how to add users to a project](../project/members/index.md#add-users-to-a-project), or [how to add users to a group](../group/index.md#add-users-to-a-group).
You can now grant users with at least the Developer role for the workspace and agent projects
the necessary permissions to create and manage workspaces.
You're all set! WP is allowed with AP. Users with necessary permissions can go to the WP workspaces tab and create or manage workspaces as they like.
To grant workspace users the necessary permissions, see:
- [Add users to a project](../project/members/index.md#add-users-to-a-project).
- [Add users to a group](../group/index.md#add-users-to-a-group).
You've done it! Users can now create and manage workspaces in a project.

View File

@ -127,6 +127,7 @@ namespace :gitlab do
log_path_message += "\nYou can inspect the webpack full log here: #{ENV['CI_JOB_URL']}/artifacts/file/#{log_path}" if ENV['CI_JOB_URL']
end
ENV['NODE_OPTIONS'] = '--max-old-space-size=8192' if ENV.has_key?('CI')
ENV['NODE_OPTIONS'] = '--max-old-space-size=16384' if ENV['GITLAB_LARGE_RUNNER_OPTIONAL'] == "saas-linux-large-amd64"
unless system(cmd)

View File

@ -74,7 +74,7 @@
"@gitlab/fonts": "^1.3.0",
"@gitlab/query-language": "^0.0.5-a-20241003",
"@gitlab/svgs": "3.117.0",
"@gitlab/ui": "94.5.0",
"@gitlab/ui": "95.1.1",
"@gitlab/web-ide": "^0.0.1-dev-20240909013227",
"@mattiasbuelens/web-streams-adapter": "^0.1.0",
"@rails/actioncable": "7.0.8-4",

View File

@ -48,7 +48,6 @@ module Gitlab
create_cluster
update_server_url
install_metrics_server
log("Cluster '#{name}' created", :success)
rescue Helpers::Shell::CommandFailure
# Exit cleanly without stacktrace if shell command fails

View File

@ -84,11 +84,11 @@ RSpec.describe Gitlab::Cng::Kind::Cluster do
it "creates cluster with ci specific configuration", :aggregate_failures do
expect { cluster.create }.to output(/Cluster '#{name}' created/).to_stdout
expect(helm).to have_received(:add_helm_chart).with(
expect(helm).not_to have_received(:add_helm_chart).with(
"metrics-server",
"https://kubernetes-sigs.github.io/metrics-server/"
)
expect(helm).to have_received(:upgrade).with(
expect(helm).not_to have_received(:upgrade).with(
"metrics-server",
"metrics-server/metrics-server",
namespace: "kube-system",

View File

@ -745,7 +745,7 @@ module QA
#
# @return [Boolean]
def parallel_run?
enabled?(ENV["QA_PARALLEL_RUN"], default: false)
ENV["TEST_ENV_NUMBER"].present?
end
# Execute tests in multiple parallel processes

View File

@ -42,7 +42,6 @@ module QA
def set_environment!
ENV.store("NO_KNAPSACK", "true")
ENV.store("QA_PARALLEL_RUN", "true")
return if ENV["QA_GITLAB_URL"].present?

View File

@ -53,7 +53,7 @@ module InternalEventsCli
https://docs.gitlab.com/ee/user/analytics/
#{format_warning('Customer wants usage data for their own products:')}
https://docs.gitlab.com/ee/user/product_analytics/
https://docs.gitlab.com/ee/operations/product_analytics/
TEXT
EVENT_TRACKING_EXAMPLES = <<~TEXT

View File

@ -4,6 +4,7 @@ FactoryBot.define do
factory :container_repository do
sequence(:name) { |n| "test_image_#{n}" }
project
next_delete_attempt_at { nil }
transient do
tags { [] }

View File

@ -782,7 +782,7 @@ RSpec.describe ContainerRepository, :aggregate_failures, feature_category: :cont
end
describe '#set_delete_ongoing_status', :freeze_time do
let_it_be(:repository) { create(:container_repository) }
let(:repository) { create(:container_repository, next_delete_attempt_at: Time.zone.now) }
subject { repository.set_delete_ongoing_status }
@ -790,16 +790,83 @@ RSpec.describe ContainerRepository, :aggregate_failures, feature_category: :cont
expect { subject }.to change(repository, :status).from(nil).to('delete_ongoing')
.and change(repository, :delete_started_at).from(nil).to(Time.zone.now)
.and change(repository, :status_updated_at).from(nil).to(Time.zone.now)
.and change(repository, :next_delete_attempt_at).to(nil)
end
context 'when the feature set_delete_failed_container_repository is disabled' do
before do
stub_feature_flags(set_delete_failed_container_repository: false)
end
it 'updates deletion status attributes', :freeze_time do
expect { subject }.to change(repository, :status).from(nil).to('delete_ongoing')
.and change(repository, :delete_started_at).from(nil).to(Time.zone.now)
.and change(repository, :status_updated_at).from(nil).to(Time.zone.now)
.and not_change(repository, :next_delete_attempt_at)
expect(repository.updated_at).to eq(Time.zone.now)
end
end
end
describe '#set_delete_scheduled_status', :freeze_time do
let_it_be(:repository) { create(:container_repository, :status_delete_ongoing, delete_started_at: 3.minutes.ago) }
subject { repository.set_delete_scheduled_status }
let_it_be_with_reload(:repository) do
create(
:container_repository,
:status_delete_ongoing,
delete_started_at: 3.minutes.ago
)
end
where(:current_failed_count, :new_failed_count, :minutes_delay) do
0 | 1 | 1
1 | 2 | 2
2 | 3 | 4
3 | 4 | 8
4 | 5 | 16
5 | 6 | 32
end
with_them do
before do
repository.update!(failed_deletion_count: current_failed_count)
end
it 'updates delete attributes' do
expect { subject }.to change(repository, :status).from('delete_ongoing').to('delete_scheduled')
.and change(repository, :delete_started_at).to(nil)
.and change(repository, :failed_deletion_count).from(current_failed_count).to(new_failed_count)
.and change(repository, :next_delete_attempt_at).to(minutes_delay.minute.from_now)
expect(repository.status_updated_at).to eq(Time.zone.now)
end
context 'when the feature set_delete_failed_container_repository is disabled' do
before do
stub_feature_flags(set_delete_failed_container_repository: false)
end
it 'updates delete attributes' do
expect { subject }.to change(repository, :status).from('delete_ongoing').to('delete_scheduled')
.and change(repository, :delete_started_at).to(nil)
.and not_change(repository, :failed_deletion_count)
.and not_change(repository, :next_delete_attempt_at)
expect(repository.status_updated_at).to eq(Time.zone.now)
end
end
end
end
describe '#set_delete_failed_status', :freeze_time do
let_it_be(:repository) { create(:container_repository, :status_delete_ongoing, delete_started_at: 3.minutes.ago) }
subject { repository.set_delete_failed_status }
it 'updates delete attributes' do
expect { subject }.to change(repository, :status).from('delete_ongoing').to('delete_scheduled')
expect { subject }.to change(repository, :status).from('delete_ongoing').to('delete_failed')
.and change(repository, :delete_started_at).to(nil)
.and change(repository, :status_updated_at).to(Time.zone.now)
end
@ -825,6 +892,26 @@ RSpec.describe ContainerRepository, :aggregate_failures, feature_category: :cont
end
end
describe '.pending_destruction' do
let_it_be(:delete_failed_repository) { create(:container_repository, :status_delete_failed) }
let_it_be(:delete_ongoing_repository) { create(:container_repository, :status_delete_ongoing) }
let_it_be(:delete_scheduled_in_the_future) { create(:container_repository, :status_delete_scheduled, next_delete_attempt_at: 2.hours.from_now) }
let_it_be(:delete_scheduled_in_the_past) { create(:container_repository, :status_delete_scheduled, next_delete_attempt_at: 2.hours.ago) }
let_it_be(:delete_scheduled_no_next_delete_attempt_at) { create(:container_repository, :status_delete_scheduled, next_delete_attempt_at: nil) }
it 'returns repositories that are delete_scheduled and next_delete_attempt_at is nil or has_passed' do
expect(described_class.pending_destruction).to include(
delete_scheduled_in_the_past,
delete_scheduled_no_next_delete_attempt_at
)
expect(described_class.pending_destruction).not_to include(
delete_failed_repository,
delete_ongoing_repository,
delete_scheduled_in_the_future
)
end
end
describe '.build_from_path' do
let(:registry_path) do
ContainerRegistry::Path.new(project.full_path + '/some/image')

View File

@ -34,7 +34,52 @@ RSpec.describe ContainerRegistry::DeleteContainerRepositoryWorker, :aggregate_fa
.and_return(cleanup_tags_service_double)
end
it 'picks and destroys the delete scheduled container repository' do
shared_examples 'setting the correct status based on failed_deletion_count' do
context 'when the failed_deletion_count is less than the max' do
let(:set_status_method) { :set_delete_scheduled_status }
let(:status_after_execution) { 'delete_scheduled' }
before do
container_repository.update!(failed_deletion_count: ContainerRepository::MAX_DELETION_FAILURES - 1)
end
it_behaves_like 'not deleting the repository and setting the correct status'
end
context 'when the failed_deletion_count has reached the max' do
let(:set_status_method) { :set_delete_failed_status }
let(:status_after_execution) { 'delete_failed' }
before do
container_repository.update!(failed_deletion_count: ContainerRepository::MAX_DELETION_FAILURES)
end
it_behaves_like 'not deleting the repository and setting the correct status'
end
end
shared_examples 'setting the status to delete_scheduled regardless of failed_deletion_count' do
let(:set_status_method) { :set_delete_scheduled_status }
let(:status_after_execution) { 'delete_scheduled' }
context 'when the failed_deletion_count is less than the max' do
before do
container_repository.update!(failed_deletion_count: ContainerRepository::MAX_DELETION_FAILURES - 1)
end
it_behaves_like 'not deleting the repository and setting the correct status'
end
context 'when the failed_deletion_count has reached the max' do
before do
container_repository.update!(failed_deletion_count: ContainerRepository::MAX_DELETION_FAILURES)
end
it_behaves_like 'not deleting the repository and setting the correct status'
end
end
it 'picks and destroys the next container repository for destruction' do
expect_next_pending_destruction_container_repository do |repo|
expect_logs_on(repo, tags_size_before_delete: 100, deleted_tags_size: 0)
expect(repo).to receive(:destroy!).and_call_original
@ -43,49 +88,78 @@ RSpec.describe ContainerRegistry::DeleteContainerRepositoryWorker, :aggregate_fa
expect(ContainerRepository.all).to contain_exactly(second_container_repository)
end
context 'with an error during the tags cleanup' do
let(:cleanup_tags_service_response) { { status: :error, original_size: 100, deleted_size: 0 } }
context 'when an error happens before reaching repository.destroy!' do
shared_examples 'not deleting the repository and setting the correct status' do
it 'does not delete the repository and sets the correct status' do
expect_next_pending_destruction_container_repository do |repo|
expect_logs_on(repo, tags_size_before_delete: 100, deleted_tags_size: 0)
expect(repo).to receive(set_status_method).and_call_original
expect(repo).not_to receive(:destroy!)
end
it 'does not delete the container repository' do
expect_next_pending_destruction_container_repository do |repo|
expect_logs_on(repo, tags_size_before_delete: 100, deleted_tags_size: 0)
expect(repo).to receive(:set_delete_scheduled_status).and_call_original
expect(repo).not_to receive(:destroy!)
expect(container_repository.reload.status).to eq('delete_scheduled')
expect { perform_work }.to not_change(ContainerRepository, :count)
expect(container_repository.reload.status).to eq(status_after_execution)
expect(container_repository.delete_started_at).to eq(nil)
end
end
context 'with an error during the tags cleanup' do
let(:cleanup_tags_service_response) { { status: :error, original_size: 100, deleted_size: 0 } }
it_behaves_like 'setting the correct status based on failed_deletion_count'
context 'when the feature set_delete_failed_container_repository is disabled' do
before do
stub_feature_flags(set_delete_failed_container_repository: false)
end
it_behaves_like 'setting the status to delete_scheduled regardless of failed_deletion_count'
end
end
context 'with tags left to destroy' do
let(:tags_count) { 10 }
it_behaves_like 'setting the correct status based on failed_deletion_count'
context 'when the feature set_delete_failed_container_repository is disabled' do
before do
stub_feature_flags(set_delete_failed_container_repository: false)
end
it_behaves_like 'setting the status to delete_scheduled regardless of failed_deletion_count'
end
expect { perform_work }.to not_change(ContainerRepository, :count)
.and not_change { container_repository.reload.status }
expect(container_repository.delete_started_at).to eq(nil)
end
end
context 'with an error during the destroy' do
it 'does not delete the container repository' do
expect_next_pending_destruction_container_repository do |repo|
expect_logs_on(repo, tags_size_before_delete: 100, deleted_tags_size: 0)
expect(repo).to receive(:destroy!).and_raise('Error!')
expect(repo).to receive(:set_delete_scheduled_status).and_call_original
end
context 'with an error happening during container_repository.destroy' do
shared_examples 'not deleting the repository and setting the correct status' do
it 'does not delete the repository and sets the correct status' do
expect_next_pending_destruction_container_repository do |repo|
expect_logs_on(repo, tags_size_before_delete: 100, deleted_tags_size: 0)
expect(repo).to receive(set_status_method).and_call_original
expect(repo).to receive(:destroy!).and_raise('Error!')
end
expect(::Gitlab::ErrorTracking).to receive(:log_exception)
.with(instance_of(RuntimeError), class: described_class.name)
expect { perform_work }.to not_change(ContainerRepository, :count)
.and not_change { container_repository.reload.status }
expect(container_repository.delete_started_at).to eq(nil)
expect(::Gitlab::ErrorTracking).to receive(:log_exception)
.with(instance_of(RuntimeError), class: described_class.name)
expect(container_repository.reload.status).to eq('delete_scheduled')
expect { perform_work }.to not_change(ContainerRepository, :count)
expect(container_repository.reload.status).to eq(status_after_execution)
expect(container_repository.delete_started_at).to eq(nil)
end
end
end
context 'with tags left to destroy' do
let(:tags_count) { 10 }
it_behaves_like 'setting the correct status based on failed_deletion_count'
it 'does not delete the container repository' do
expect_next_pending_destruction_container_repository do |repo|
expect(repo).not_to receive(:destroy!)
expect(repo).to receive(:set_delete_scheduled_status).and_call_original
context 'when the feature set_delete_failed_container_repository is disabled' do
before do
stub_feature_flags(set_delete_failed_container_repository: false)
end
expect { perform_work }.to not_change(ContainerRepository, :count)
.and not_change { container_repository.reload.status }
expect(container_repository.delete_started_at).to eq(nil)
it_behaves_like 'setting the status to delete_scheduled regardless of failed_deletion_count'
end
end

View File

@ -1362,10 +1362,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.117.0.tgz#a9a45949d73e91f278e019b46220feb63105dd48"
integrity sha512-nBFWh2UN+pFl7nBQUgaUtHRChrZSXdRNmv1J49QPLwyJYWuq51YEBXQW5mPAlvB1BGfiNJPSHSGxWALFZBI5WA==
"@gitlab/ui@94.5.0":
version "94.5.0"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-94.5.0.tgz#162f63b90293108d961b467726aefb9dca4602f2"
integrity sha512-ps1f76XnpTZjzMjbElqHIeV7eA4ZLKlvqwee3QlO+pYG9mehaD93liMxW2b0NV6v6HlWbe2/fx9x/MtDD2xJZA==
"@gitlab/ui@95.1.1":
version "95.1.1"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-95.1.1.tgz#8ab54b1bf3bcf320c6ead0c80ead8520ab7265a6"
integrity sha512-r5svjPFBtigKCSjcIJb920IFaBgjZ3zpUxGZIFhXC0JZ3UO9cAHyZvGT5oYdwNOjN/7Xab9BiG9mhqCGoxrvNQ==
dependencies:
"@floating-ui/dom" "1.4.3"
echarts "^5.3.2"
@ -1373,6 +1373,7 @@
lodash "^4.17.20"
marked "^12.0.0"
marked-bidi "^1.0.8"
merge-cobertura "^1.0.2"
popper.js "^1.16.1"
portal-vue "^2.1.7"
vue-functional-data-merge "^3.1.0"
@ -7179,6 +7180,13 @@ fast-mersenne-twister@1.0.2:
resolved "https://registry.yarnpkg.com/fast-mersenne-twister/-/fast-mersenne-twister-1.0.2.tgz#5ead7caf3ace592a5789d11767732bd81cbaaa56"
integrity sha512-IaClPxsoBu3MxGpcURyjV8otT5Bj4ARoK0KBCJGnEVnD1A/qclL5eIeYiUuwG/WWJPxL1jlK61HTm2T6SBmvBQ==
fast-xml-parser@^4.4.1:
version "4.5.0"
resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz#2882b7d01a6825dfdf909638f2de0256351def37"
integrity sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==
dependencies:
strnum "^1.0.5"
fastest-levenshtein@^1.0.12, fastest-levenshtein@^1.0.16:
version "1.0.16"
resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5"
@ -10022,6 +10030,15 @@ meow@^13.0.0:
resolved "https://registry.yarnpkg.com/meow/-/meow-13.1.0.tgz#62995b0e8c3951739fe6e0a4becdd4d0df23eb37"
integrity sha512-o5R/R3Tzxq0PJ3v3qcQJtSvSE9nKOLSAaDuuoMzDVuGTwHdccMWcYomh9Xolng2tjT6O/Y83d+0coVGof6tqmA==
merge-cobertura@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/merge-cobertura/-/merge-cobertura-1.0.2.tgz#16bed05d1c6bd4803ef8781275026ba7770b754f"
integrity sha512-SlKYojFjSfDNR6W7rDEPIcnox3W4nhhjp/EQpNeCFhA87Y7RlEGwXbzN68HUNotFKUVbmD+lV3x7VW3OIZVB2g==
dependencies:
fast-xml-parser "^4.4.1"
minimist "^1.2.8"
source-map-support "^0.5.21"
merge-descriptors@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
@ -10495,7 +10512,7 @@ minimatch@^9.0.1, minimatch@^9.0.4:
dependencies:
brace-expansion "^2.0.1"
minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6:
minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6, minimist@^1.2.8:
version "1.2.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
@ -12957,7 +12974,7 @@ source-map-resolve@^0.5.0:
source-map-url "^0.4.0"
urix "^0.1.0"
source-map-support@0.5.13, source-map-support@~0.5.12:
source-map-support@0.5.13:
version "0.5.13"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932"
integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==
@ -12965,6 +12982,14 @@ source-map-support@0.5.13, source-map-support@~0.5.12:
buffer-from "^1.0.0"
source-map "^0.6.0"
source-map-support@^0.5.21, source-map-support@~0.5.12:
version "0.5.21"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
dependencies:
buffer-from "^1.0.0"
source-map "^0.6.0"
source-map-url@^0.4.0:
version "0.4.1"
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56"
@ -13140,7 +13165,16 @@ string-length@^4.0.1:
char-regex "^1.0.2"
strip-ansi "^6.0.0"
"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@ -13193,7 +13227,7 @@ string_decoder@^1.0.0, string_decoder@^1.1.1, string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@ -13207,6 +13241,13 @@ strip-ansi@^5.2.0:
dependencies:
ansi-regex "^4.1.0"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^7.0.1, strip-ansi@^7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
@ -13246,6 +13287,11 @@ strip-json-comments@^3.1.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
strnum@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db"
integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==
style-loader@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-2.0.0.tgz#9669602fd4690740eaaec137799a03addbbc393c"
@ -14898,7 +14944,7 @@ worker-loader@^3.0.8:
loader-utils "^2.0.0"
schema-utils "^3.0.0"
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@ -14916,6 +14962,15 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"