Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
		
							parent
							
								
									a466e9450d
								
							
						
					
					
						commit
						7fe1490a58
					
				|  | @ -75,6 +75,8 @@ rules: | |||
|         - sibling | ||||
|         - index | ||||
|       pathGroups: | ||||
|         - pattern: '@sentry/browser' | ||||
|           group: external | ||||
|         - pattern: ~/** | ||||
|           group: internal | ||||
|         - pattern: emojis/** | ||||
|  |  | |||
|  | @ -108,6 +108,9 @@ Dangerfile @gl-quality/eng-prod | |||
| /ee/app/models/project_alias.rb @patrickbajao | ||||
| /ee/lib/api/project_aliases.rb @patrickbajao | ||||
| 
 | ||||
| ^[Distribution] | ||||
| /lib/support/ @gitlab-org/distribution | ||||
| 
 | ||||
| # Secure & Threat Management ownership delineation | ||||
| # https://about.gitlab.com/handbook/engineering/development/threat-management/delineate-secure-threat-management.html#technical-boundaries | ||||
| ^[Threat Insights] | ||||
|  |  | |||
|  | @ -498,8 +498,16 @@ RSpec/FactoryBot/AvoidCreate: | |||
|   Include: | ||||
|     - 'spec/presenters/**/*.rb' | ||||
|     - 'spec/serializers/**/*.rb' | ||||
|     - 'spec/helpers/**/*.rb' | ||||
|     - 'spec/views/**/*.rb' | ||||
|     - 'spec/components/**/*.rb' | ||||
|     - 'spec/mailers/**/*.rb' | ||||
|     - 'ee/spec/presenters/**/*.rb' | ||||
|     - 'ee/spec/serializers/**/*.rb' | ||||
|     - 'ee/spec/helpers/**/*.rb' | ||||
|     - 'ee/spec/views/**/*.rb' | ||||
|     - 'ee/spec/components/**/*.rb' | ||||
|     - 'ee/spec/mailers/**/*.rb' | ||||
| 
 | ||||
| RSpec/FactoryBot/StrategyInCallback: | ||||
|   Enabled: true | ||||
|  |  | |||
|  | @ -1,6 +1,107 @@ | |||
| --- | ||||
| RSpec/FactoryBot/AvoidCreate: | ||||
|   Exclude: | ||||
|     - 'ee/spec/components/namespaces/free_user_cap/alert_component_spec.rb' | ||||
|     - 'ee/spec/components/namespaces/free_user_cap/non_owner_alert_component_spec.rb' | ||||
|     - 'ee/spec/components/namespaces/free_user_cap/preview_alert_component_spec.rb' | ||||
|     - 'ee/spec/components/namespaces/free_user_cap/usage_quota_alert_component_spec.rb' | ||||
|     - 'ee/spec/components/namespaces/free_user_cap/usage_quota_trial_alert_component_spec.rb' | ||||
|     - 'ee/spec/components/namespaces/storage/limit_alert_component_spec.rb' | ||||
|     - 'ee/spec/components/namespaces/storage/pre_enforcement_alert_component_spec.rb' | ||||
|     - 'ee/spec/components/namespaces/storage/project_pre_enforcement_alert_component_spec.rb' | ||||
|     - 'ee/spec/components/namespaces/storage/subgroup_pre_enforcement_alert_component_spec.rb' | ||||
|     - 'ee/spec/components/namespaces/storage/user_pre_enforcement_alert_component_spec.rb' | ||||
|     - 'ee/spec/helpers/admin/ip_restriction_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/application_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/billing_plans_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/boards_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/compliance_management/compliance_framework/group_settings_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/admin/identities_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/blob_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/branches_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/ci/pipeline_editor_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/ci/runners_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/dashboard_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/environments_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/events_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/feature_flags_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/gitlab_routing_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/graph_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/groups/analytics/cycle_analytics_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/groups/group_members_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/groups_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/hooks_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/integrations_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/invite_members_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/issuables_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/issues_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/labels_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/learn_gitlab_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/lock_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/namespace_user_cap_reached_alert_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/namespaces_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/operations_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/personal_access_tokens_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/projects/pipeline_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/projects/security/api_fuzzing_configuration_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/projects/security/configuration_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/projects/security/dast_configuration_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/projects/security/sast_configuration_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/releases_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/security_orchestration_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/subscribable_banner_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/todos_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/trial_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/users/callouts_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/welcome_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/ee/wiki_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/epics_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/gitlab_subscriptions/upcoming_reconciliation_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/groups/feature_discovery_moments_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/groups/security_features_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/incident_management/escalation_policy_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/incident_management/oncall_schedule_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/license_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/license_monitoring_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/manual_quarterly_co_term_banner_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/markup_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/notes_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/paid_feature_callout_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/path_locks_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/prevent_forking_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/projects/on_demand_scans_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/projects/project_members_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/projects/security/dast_profiles_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/projects/security/discover_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/projects_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/push_rules_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/routing/pseudonymization_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/search_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/seat_count_alert_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/security_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/subscriptions_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/timeboxes_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/trial_status_widget_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/users/identity_verification_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/users_helper_spec.rb' | ||||
|     - 'ee/spec/helpers/vulnerabilities_helper_spec.rb' | ||||
|     - 'ee/spec/mailers/ci_minutes_usage_mailer_spec.rb' | ||||
|     - 'ee/spec/mailers/credentials_inventory_mailer_spec.rb' | ||||
|     - 'ee/spec/mailers/devise_mailer_spec.rb' | ||||
|     - 'ee/spec/mailers/ee/emails/admin_notification_spec.rb' | ||||
|     - 'ee/spec/mailers/ee/emails/issues_spec.rb' | ||||
|     - 'ee/spec/mailers/ee/emails/merge_requests_spec.rb' | ||||
|     - 'ee/spec/mailers/ee/emails/profile_spec.rb' | ||||
|     - 'ee/spec/mailers/ee/emails/projects_spec.rb' | ||||
|     - 'ee/spec/mailers/emails/free_user_cap_spec.rb' | ||||
|     - 'ee/spec/mailers/emails/group_memberships_spec.rb' | ||||
|     - 'ee/spec/mailers/emails/in_product_marketing_spec.rb' | ||||
|     - 'ee/spec/mailers/emails/merge_commits_spec.rb' | ||||
|     - 'ee/spec/mailers/emails/namespace_storage_usage_mailer_spec.rb' | ||||
|     - 'ee/spec/mailers/emails/requirements_spec.rb' | ||||
|     - 'ee/spec/mailers/emails/user_cap_spec.rb' | ||||
|     - 'ee/spec/mailers/license_mailer_spec.rb' | ||||
|     - 'ee/spec/mailers/notify_spec.rb' | ||||
|     - 'ee/spec/presenters/approval_rule_presenter_spec.rb' | ||||
|     - 'ee/spec/presenters/audit_event_presenter_spec.rb' | ||||
|     - 'ee/spec/presenters/ci/build_runner_presenter_spec.rb' | ||||
|  | @ -90,6 +191,178 @@ RSpec/FactoryBot/AvoidCreate: | |||
|     - 'ee/spec/serializers/vulnerabilities/scanner_entity_spec.rb' | ||||
|     - 'ee/spec/serializers/vulnerability_entity_spec.rb' | ||||
|     - 'ee/spec/serializers/vulnerability_note_entity_spec.rb' | ||||
|     - 'ee/spec/views/admin/application_settings/_elasticsearch_form.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/admin/application_settings/_git_abuse_rate_limit.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/admin/application_settings/general.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/admin/dashboard/index.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/admin/groups/_form.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/admin/identities/index.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/admin/users/_credit_card_info.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/admin/users/index.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/admin/users/show.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/clusters/clusters/show.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/compliance_management/compliance_framework/_project_settings.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/groups/billings/index.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/groups/edit.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/groups/feature_discovery_moments/advanced_features_dashboard.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/groups/group_members/index.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/groups/hook_logs/show.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/groups/hooks/edit.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/groups/security/discover/show.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/groups/settings/_remove.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/groups/settings/reporting/show.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/layouts/_search.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/layouts/application.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/layouts/group.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/layouts/header/_current_user_dropdown.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/layouts/header/_new_dropdown.haml_spec.rb' | ||||
|     - 'ee/spec/views/layouts/header/_read_only_banner.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/layouts/header/help_dropdown/_cross_stage_fdm.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/layouts/project.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/projects/edit.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/projects/issues/show.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/projects/on_demand_scans/index.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/projects/pipelines/_tabs_content.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/projects/project_members/index.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/projects/security/corpus_management/show.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/projects/security/dast_profiles/show.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/projects/security/dast_scanner_profiles/edit.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/projects/security/dast_scanner_profiles/new.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/projects/security/dast_site_profiles/edit.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/projects/security/dast_site_profiles/new.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/projects/security/discover/show.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/projects/security/policies/index.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/projects/security/sast_configuration/show.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/projects/settings/subscriptions/_index.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/registrations/groups_projects/new.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/registrations/welcome/continuous_onboarding_getting_started.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/registrations/welcome/show.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/search/_category.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/shared/_clone_panel.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/shared/_kerberos_clone_button.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/shared/_mirror_status.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/shared/_mirror_update_button.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/shared/_namespace_user_cap_reached_alert.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/shared/billings/_eoa_bronze_plan_banner.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/shared/billings/_trial_status.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/shared/credentials_inventory/_expiry_date.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/shared/credentials_inventory/gpg_keys/_gpg_key.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/shared/credentials_inventory/personal_access_tokens/_personal_access_token.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/shared/credentials_inventory/resource_access_tokens/_resource_access_token.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/shared/credentials_inventory/ssh_keys/_ssh_key.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/shared/issuable/_approver_suggestion.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/shared/issuable/_sidebar.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/shared/labels/_create_label_help_text.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/shared/milestones/_milestone.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/shared/promotions/_promotion_link_project.html.haml_spec.rb' | ||||
|     - 'spec/components/diffs/overflow_warning_component_spec.rb' | ||||
|     - 'spec/components/diffs/stats_component_spec.rb' | ||||
|     - 'spec/components/pajamas/avatar_component_spec.rb' | ||||
|     - 'spec/helpers/admin/identities_helper_spec.rb' | ||||
|     - 'spec/helpers/admin/user_actions_helper_spec.rb' | ||||
|     - 'spec/helpers/analytics/cycle_analytics_helper_spec.rb' | ||||
|     - 'spec/helpers/appearances_helper_spec.rb' | ||||
|     - 'spec/helpers/application_helper_spec.rb' | ||||
|     - 'spec/helpers/application_settings_helper_spec.rb' | ||||
|     - 'spec/helpers/auth_helper_spec.rb' | ||||
|     - 'spec/helpers/auto_devops_helper_spec.rb' | ||||
|     - 'spec/helpers/avatars_helper_spec.rb' | ||||
|     - 'spec/helpers/award_emoji_helper_spec.rb' | ||||
|     - 'spec/helpers/blob_helper_spec.rb' | ||||
|     - 'spec/helpers/boards_helper_spec.rb' | ||||
|     - 'spec/helpers/branches_helper_spec.rb' | ||||
|     - 'spec/helpers/broadcast_messages_helper_spec.rb' | ||||
|     - 'spec/helpers/button_helper_spec.rb' | ||||
|     - 'spec/helpers/calendar_helper_spec.rb' | ||||
|     - 'spec/helpers/ci/builds_helper_spec.rb' | ||||
|     - 'spec/helpers/ci/jobs_helper_spec.rb' | ||||
|     - 'spec/helpers/ci/pipeline_editor_helper_spec.rb' | ||||
|     - 'spec/helpers/ci/pipelines_helper_spec.rb' | ||||
|     - 'spec/helpers/ci/runners_helper_spec.rb' | ||||
|     - 'spec/helpers/ci/secure_files_helper_spec.rb' | ||||
|     - 'spec/helpers/clusters_helper_spec.rb' | ||||
|     - 'spec/helpers/commits_helper_spec.rb' | ||||
|     - 'spec/helpers/diff_helper_spec.rb' | ||||
|     - 'spec/helpers/emails_helper_spec.rb' | ||||
|     - 'spec/helpers/environment_helper_spec.rb' | ||||
|     - 'spec/helpers/environments_helper_spec.rb' | ||||
|     - 'spec/helpers/events_helper_spec.rb' | ||||
|     - 'spec/helpers/feature_flags_helper_spec.rb' | ||||
|     - 'spec/helpers/gitlab_routing_helper_spec.rb' | ||||
|     - 'spec/helpers/graph_helper_spec.rb' | ||||
|     - 'spec/helpers/groups/group_members_helper_spec.rb' | ||||
|     - 'spec/helpers/groups/settings_helper_spec.rb' | ||||
|     - 'spec/helpers/groups_helper_spec.rb' | ||||
|     - 'spec/helpers/ide_helper_spec.rb' | ||||
|     - 'spec/helpers/import_helper_spec.rb' | ||||
|     - 'spec/helpers/integrations_helper_spec.rb' | ||||
|     - 'spec/helpers/invite_members_helper_spec.rb' | ||||
|     - 'spec/helpers/issuables_description_templates_helper_spec.rb' | ||||
|     - 'spec/helpers/issuables_helper_spec.rb' | ||||
|     - 'spec/helpers/issues_helper_spec.rb' | ||||
|     - 'spec/helpers/jira_connect_helper_spec.rb' | ||||
|     - 'spec/helpers/keyset_helper_spec.rb' | ||||
|     - 'spec/helpers/labels_helper_spec.rb' | ||||
|     - 'spec/helpers/lazy_image_tag_helper_spec.rb' | ||||
|     - 'spec/helpers/learn_gitlab_helper_spec.rb' | ||||
|     - 'spec/helpers/markup_helper_spec.rb' | ||||
|     - 'spec/helpers/members_helper_spec.rb' | ||||
|     - 'spec/helpers/merge_requests_helper_spec.rb' | ||||
|     - 'spec/helpers/namespaces_helper_spec.rb' | ||||
|     - 'spec/helpers/nav/top_nav_helper_spec.rb' | ||||
|     - 'spec/helpers/nav_helper_spec.rb' | ||||
|     - 'spec/helpers/notes_helper_spec.rb' | ||||
|     - 'spec/helpers/notifications_helper_spec.rb' | ||||
|     - 'spec/helpers/notify_helper_spec.rb' | ||||
|     - 'spec/helpers/operations_helper_spec.rb' | ||||
|     - 'spec/helpers/packages_helper_spec.rb' | ||||
|     - 'spec/helpers/profiles_helper_spec.rb' | ||||
|     - 'spec/helpers/projects/alert_management_helper_spec.rb' | ||||
|     - 'spec/helpers/projects/cluster_agents_helper_spec.rb' | ||||
|     - 'spec/helpers/projects/ml/experiments_helper_spec.rb' | ||||
|     - 'spec/helpers/projects/pages_helper_spec.rb' | ||||
|     - 'spec/helpers/projects/pipeline_helper_spec.rb' | ||||
|     - 'spec/helpers/projects/project_members_helper_spec.rb' | ||||
|     - 'spec/helpers/projects/security/configuration_helper_spec.rb' | ||||
|     - 'spec/helpers/projects/terraform_helper_spec.rb' | ||||
|     - 'spec/helpers/projects_helper_spec.rb' | ||||
|     - 'spec/helpers/releases_helper_spec.rb' | ||||
|     - 'spec/helpers/routing/pseudonymization_helper_spec.rb' | ||||
|     - 'spec/helpers/rss_helper_spec.rb' | ||||
|     - 'spec/helpers/search_helper_spec.rb' | ||||
|     - 'spec/helpers/snippets_helper_spec.rb' | ||||
|     - 'spec/helpers/storage_helper_spec.rb' | ||||
|     - 'spec/helpers/submodule_helper_spec.rb' | ||||
|     - 'spec/helpers/timeboxes_helper_spec.rb' | ||||
|     - 'spec/helpers/todos_helper_spec.rb' | ||||
|     - 'spec/helpers/tree_helper_spec.rb' | ||||
|     - 'spec/helpers/users/callouts_helper_spec.rb' | ||||
|     - 'spec/helpers/users/group_callouts_helper_spec.rb' | ||||
|     - 'spec/helpers/users_helper_spec.rb' | ||||
|     - 'spec/helpers/version_check_helper_spec.rb' | ||||
|     - 'spec/helpers/visibility_level_helper_spec.rb' | ||||
|     - 'spec/helpers/web_hooks/web_hooks_helper_spec.rb' | ||||
|     - 'spec/helpers/whats_new_helper_spec.rb' | ||||
|     - 'spec/helpers/wiki_helper_spec.rb' | ||||
|     - 'spec/helpers/wiki_page_version_helper_spec.rb' | ||||
|     - 'spec/mailers/abuse_report_mailer_spec.rb' | ||||
|     - 'spec/mailers/devise_mailer_spec.rb' | ||||
|     - 'spec/mailers/emails/auto_devops_spec.rb' | ||||
|     - 'spec/mailers/emails/groups_spec.rb' | ||||
|     - 'spec/mailers/emails/in_product_marketing_spec.rb' | ||||
|     - 'spec/mailers/emails/issues_spec.rb' | ||||
|     - 'spec/mailers/emails/merge_requests_spec.rb' | ||||
|     - 'spec/mailers/emails/pages_domains_spec.rb' | ||||
|     - 'spec/mailers/emails/pipelines_spec.rb' | ||||
|     - 'spec/mailers/emails/profile_spec.rb' | ||||
|     - 'spec/mailers/emails/projects_spec.rb' | ||||
|     - 'spec/mailers/emails/releases_spec.rb' | ||||
|     - 'spec/mailers/emails/service_desk_spec.rb' | ||||
|     - 'spec/mailers/notify_spec.rb' | ||||
|     - 'spec/mailers/previews_spec.rb' | ||||
|     - 'spec/mailers/repository_check_mailer_spec.rb' | ||||
|     - 'spec/presenters/alert_management/alert_presenter_spec.rb' | ||||
|     - 'spec/presenters/blob_presenter_spec.rb' | ||||
|     - 'spec/presenters/blobs/notebook_presenter_spec.rb' | ||||
|  | @ -280,3 +553,104 @@ RSpec/FactoryBot/AvoidCreate: | |||
|     - 'spec/serializers/user_serializer_spec.rb' | ||||
|     - 'spec/serializers/web_ide_terminal_entity_spec.rb' | ||||
|     - 'spec/serializers/web_ide_terminal_serializer_spec.rb' | ||||
|     - 'spec/views/admin/application_settings/_ci_cd.html.haml_spec.rb' | ||||
|     - 'spec/views/admin/application_settings/_eks.html.haml_spec.rb' | ||||
|     - 'spec/views/admin/application_settings/_jira_connect.html.haml_spec.rb' | ||||
|     - 'spec/views/admin/application_settings/_package_registry.html.haml_spec.rb' | ||||
|     - 'spec/views/admin/application_settings/_repository_check.html.haml_spec.rb' | ||||
|     - 'spec/views/admin/application_settings/ci_cd.html.haml_spec.rb' | ||||
|     - 'spec/views/admin/application_settings/general.html.haml_spec.rb' | ||||
|     - 'spec/views/admin/application_settings/repository.html.haml_spec.rb' | ||||
|     - 'spec/views/admin/broadcast_messages/index.html.haml_spec.rb' | ||||
|     - 'spec/views/admin/dashboard/index.html.haml_spec.rb' | ||||
|     - 'spec/views/admin/identities/index.html.haml_spec.rb' | ||||
|     - 'spec/views/admin/sessions/new.html.haml_spec.rb' | ||||
|     - 'spec/views/admin/sessions/two_factor.html.haml_spec.rb' | ||||
|     - 'spec/views/ci/status/_badge.html.haml_spec.rb' | ||||
|     - 'spec/views/ci/status/_icon.html.haml_spec.rb' | ||||
|     - 'spec/views/dashboard/projects/_blank_state_admin_welcome.haml_spec.rb' | ||||
|     - 'spec/views/dashboard/projects/_blank_state_welcome.html.haml_spec.rb' | ||||
|     - 'spec/views/events/event/_common.html.haml_spec.rb' | ||||
|     - 'spec/views/groups/_home_panel.html.haml_spec.rb' | ||||
|     - 'spec/views/groups/edit.html.haml_spec.rb' | ||||
|     - 'spec/views/groups/group_members/index.html.haml_spec.rb' | ||||
|     - 'spec/views/groups/new.html.haml_spec.rb' | ||||
|     - 'spec/views/help/instance_configuration.html.haml_spec.rb' | ||||
|     - 'spec/views/layouts/_search.html.haml_spec.rb' | ||||
|     - 'spec/views/layouts/application.html.haml_spec.rb' | ||||
|     - 'spec/views/layouts/devise.html.haml_spec.rb' | ||||
|     - 'spec/views/layouts/fullscreen.html.haml_spec.rb' | ||||
|     - 'spec/views/layouts/header/_new_dropdown.haml_spec.rb' | ||||
|     - 'spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb' | ||||
|     - 'spec/views/layouts/nav/sidebar/_profile.html.haml_spec.rb' | ||||
|     - 'spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb' | ||||
|     - 'spec/views/layouts/profile.html.haml_spec.rb' | ||||
|     - 'spec/views/layouts/terms.html.haml_spec.rb' | ||||
|     - 'spec/views/notify/approved_merge_request_email.html.haml_spec.rb' | ||||
|     - 'spec/views/notify/autodevops_disabled_email.text.erb_spec.rb' | ||||
|     - 'spec/views/notify/change_in_merge_request_draft_status_email.html.haml_spec.rb' | ||||
|     - 'spec/views/notify/change_in_merge_request_draft_status_email.text.erb_spec.rb' | ||||
|     - 'spec/views/notify/changed_milestone_email.html.haml_spec.rb' | ||||
|     - 'spec/views/notify/import_issues_csv_email.html.haml_spec.rb' | ||||
|     - 'spec/views/notify/pipeline_failed_email.text.erb_spec.rb' | ||||
|     - 'spec/views/notify/push_to_merge_request_email.text.haml_spec.rb' | ||||
|     - 'spec/views/profiles/audit_log.html.haml_spec.rb' | ||||
|     - 'spec/views/profiles/keys/_key.html.haml_spec.rb' | ||||
|     - 'spec/views/profiles/keys/_key_details.html.haml_spec.rb' | ||||
|     - 'spec/views/profiles/notifications/show.html.haml_spec.rb' | ||||
|     - 'spec/views/profiles/show.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/_files.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/_flash_messages.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/_home_panel.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/branches/index.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/commit/_commit_box.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/commit/branches.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/commit/show.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/commits/_commit.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/commits/show.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/diffs/_viewer.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/edit.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/empty.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/environments/terminal.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/hooks/edit.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/hooks/index.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/imports/new.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/issues/_issue.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/issues/_service_desk_info_content.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/issues/show.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/jobs/_build.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/jobs/_generic_commit_status.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/jobs/show.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/merge_requests/_commits.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/merge_requests/creations/_new_submit.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/merge_requests/edit.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/pages/new.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/pages/show.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/pages_domains/show.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/pipeline_schedules/_pipeline_schedule.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/pipelines/show.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/project_members/index.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/runners/_specific_runners.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/settings/integrations/edit.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/settings/merge_requests/show.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/settings/operations/show.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/tags/index.html.haml_spec.rb' | ||||
|     - 'spec/views/projects/tree/show.html.haml_spec.rb' | ||||
|     - 'spec/views/registrations/welcome/show.html.haml_spec.rb' | ||||
|     - 'spec/views/search/_results.html.haml_spec.rb' | ||||
|     - 'spec/views/shared/_label_row.html.haml_spec.rb' | ||||
|     - 'spec/views/shared/issuable/_sidebar.html.haml_spec.rb' | ||||
|     - 'spec/views/shared/milestones/_issuable.html.haml_spec.rb' | ||||
|     - 'spec/views/shared/milestones/_top.html.haml_spec.rb' | ||||
|     - 'spec/views/shared/nav/_sidebar.html.haml_spec.rb' | ||||
|     - 'spec/views/shared/notes/_form.html.haml_spec.rb' | ||||
|     - 'spec/views/shared/projects/_inactive_project_deletion_alert.html.haml_spec.rb' | ||||
|     - 'spec/views/shared/projects/_list.html.haml_spec.rb' | ||||
|     - 'spec/views/shared/projects/_project.html.haml_spec.rb' | ||||
|     - 'spec/views/shared/runners/_runner_details.html.haml_spec.rb' | ||||
|     - 'spec/views/shared/snippets/_snippet.html.haml_spec.rb' | ||||
|     - 'spec/views/shared/web_hooks/_web_hook_disabled_alert.html.haml_spec.rb' | ||||
|     - 'spec/views/shared/wikis/_sidebar.html.haml_spec.rb' | ||||
|  |  | |||
							
								
								
									
										4
									
								
								Gemfile
								
								
								
								
							
							
						
						
									
										4
									
								
								Gemfile
								
								
								
								
							|  | @ -15,7 +15,7 @@ gem 'bundler-checksum', '~> 0.1.0', path: 'vendor/gems/bundler-checksum', requir | |||
| # https://gitlab.com/gitlab-org/gitlab/-/issues/375713 | ||||
| gem 'rails', '~> 6.1.6.1' | ||||
| 
 | ||||
| gem 'bootsnap', '~> 1.14.0', require: false | ||||
| gem 'bootsnap', '~> 1.15.0', require: false | ||||
| 
 | ||||
| # Pin openssl to match the version bundled with our supported Rubies. | ||||
| # See https://stdgems.org/openssl/#gem-version. | ||||
|  | @ -372,6 +372,8 @@ group :development do | |||
|   gem 'better_errors', '~> 2.9.1' | ||||
| 
 | ||||
|   gem 'sprite-factory', '~> 1.7' | ||||
| 
 | ||||
|   gem "listen", "~> 3.7" | ||||
| end | ||||
| 
 | ||||
| group :development, :test do | ||||
|  |  | |||
|  | @ -57,7 +57,7 @@ | |||
| {"name":"bindata","version":"2.4.11","platform":"ruby","checksum":"c38e0c99ffcd80c10a0a7ae6c8586d2fe26bf245cbefac90bec8764523220f6a"}, | ||||
| {"name":"binding_ninja","version":"0.2.3","platform":"java","checksum":"bbcf70b211d6e397493bf57c249bbec6aaf28fa7dafeb78e447b1b2f0610484f"}, | ||||
| {"name":"binding_ninja","version":"0.2.3","platform":"ruby","checksum":"4a85550a0066ee4721506b4e150857486808e50c9ddfeed04bdc896bb61eca9d"}, | ||||
| {"name":"bootsnap","version":"1.14.0","platform":"ruby","checksum":"4c541735f628e6d6bb7284552ce14f63f75a6af238b23f43d2b07789b576de3f"}, | ||||
| {"name":"bootsnap","version":"1.15.0","platform":"ruby","checksum":"f246bb1152159098f5d5619b92e373c73db77769bf3e0c4b6336feeb934bc8d2"}, | ||||
| {"name":"bootstrap_form","version":"4.2.0","platform":"ruby","checksum":"f578b3c900d2cf15fab641064d357318b29e285bd5fdf090f903727912889710"}, | ||||
| {"name":"browser","version":"5.3.1","platform":"ruby","checksum":"62745301701ff2c6c5d32d077bb12532b20be261929dcb52c6781ed0d5658b3c"}, | ||||
| {"name":"builder","version":"3.2.4","platform":"ruby","checksum":"99caf08af60c8d7f3a6b004029c4c3c0bdaebced6c949165fe98f1db27fbbc10"}, | ||||
|  |  | |||
|  | @ -229,7 +229,7 @@ GEM | |||
|       rack (>= 0.9.0) | ||||
|     bindata (2.4.11) | ||||
|     binding_ninja (0.2.3) | ||||
|     bootsnap (1.14.0) | ||||
|     bootsnap (1.15.0) | ||||
|       msgpack (~> 1.2) | ||||
|     bootstrap_form (4.2.0) | ||||
|       actionpack (>= 5.0) | ||||
|  | @ -1602,7 +1602,7 @@ DEPENDENCIES | |||
|   benchmark-ips (~> 2.3.0) | ||||
|   benchmark-memory (~> 0.1) | ||||
|   better_errors (~> 2.9.1) | ||||
|   bootsnap (~> 1.14.0) | ||||
|   bootsnap (~> 1.15.0) | ||||
|   bootstrap_form (~> 4.2.0) | ||||
|   browser (~> 5.3.1) | ||||
|   bullet (~> 7.0.2) | ||||
|  | @ -1722,6 +1722,7 @@ DEPENDENCIES | |||
|   letter_opener_web (~> 2.0.0) | ||||
|   license_finder (~> 7.0) | ||||
|   licensee (~> 9.15) | ||||
|   listen (~> 3.7) | ||||
|   lockbox (~> 0.6.2) | ||||
|   lograge (~> 0.5) | ||||
|   loofah (~> 2.19.0) | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ const createSandbox = () => { | |||
|   const iframeEl = document.createElement('iframe'); | ||||
|   setAttributes(iframeEl, { | ||||
|     src: '/-/sandbox/swagger', | ||||
|     sandbox: 'allow-scripts allow-popups', | ||||
|     sandbox: 'allow-scripts allow-popups allow-forms', | ||||
|     frameBorder: 0, | ||||
|     width: '100%', | ||||
|     // The height will be adjusted dynamically.
 | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| import { __ } from '~/locale'; | ||||
| 
 | ||||
| // TODO: Remove in favor of https://gitlab.com/gitlab-org/gitlab/issues/35144
 | ||||
| export const IGNORE_ERRORS = [ | ||||
|   // Random plugins/extensions
 | ||||
|   'top.GLOBALS', | ||||
|  |  | |||
|  | @ -1,26 +1,34 @@ | |||
| import '../webpack'; | ||||
| 
 | ||||
| import * as Sentry from 'sentrybrowser7'; | ||||
| import SentryConfig from './sentry_config'; | ||||
| 
 | ||||
| const index = function index() { | ||||
|   // Configuration for newer versions of Sentry SDK (v7)
 | ||||
|   SentryConfig.init({ | ||||
|     dsn: gon.sentry_dsn, | ||||
|     environment: gon.sentry_environment, | ||||
|     currentUserId: gon.current_user_id, | ||||
|     whitelistUrls: | ||||
|     allowUrls: | ||||
|       process.env.NODE_ENV === 'production' | ||||
|         ? [gon.gitlab_url] | ||||
|         : [gon.gitlab_url, 'webpack-internal://'], | ||||
|     environment: gon.sentry_environment, | ||||
|     release: gon.revision, | ||||
|     tags: { | ||||
|       revision: gon.revision, | ||||
|       feature_category: gon.feature_category, | ||||
|     }, | ||||
|   }); | ||||
| 
 | ||||
|   return SentryConfig; | ||||
| }; | ||||
| 
 | ||||
| index(); | ||||
| 
 | ||||
| // The _Sentry object is globally exported so it can be used by
 | ||||
| //   ./sentry_browser_wrapper.js
 | ||||
| // This hack allows us to load a single version of `@sentry/browser`
 | ||||
| // in the browser, see app/views/layouts/_head.html.haml to find how it is imported.
 | ||||
| 
 | ||||
| // eslint-disable-next-line no-underscore-dangle
 | ||||
| window._Sentry = Sentry; | ||||
| 
 | ||||
| export default index; | ||||
|  |  | |||
|  | @ -0,0 +1,34 @@ | |||
| import '../webpack'; | ||||
| 
 | ||||
| import * as Sentry5 from 'sentrybrowser5'; | ||||
| import LegacySentryConfig from './legacy_sentry_config'; | ||||
| 
 | ||||
| const index = function index() { | ||||
|   // Configuration for legacy versions of Sentry SDK (v5)
 | ||||
|   LegacySentryConfig.init({ | ||||
|     dsn: gon.sentry_dsn, | ||||
|     currentUserId: gon.current_user_id, | ||||
|     whitelistUrls: | ||||
|       process.env.NODE_ENV === 'production' | ||||
|         ? [gon.gitlab_url] | ||||
|         : [gon.gitlab_url, 'webpack-internal://'], | ||||
|     environment: gon.sentry_environment, | ||||
|     release: gon.revision, | ||||
|     tags: { | ||||
|       revision: gon.revision, | ||||
|       feature_category: gon.feature_category, | ||||
|     }, | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| index(); | ||||
| 
 | ||||
| // The _Sentry object is globally exported so it can be used by
 | ||||
| //   ./sentry_browser_wrapper.js
 | ||||
| // This hack allows us to load a single version of `@sentry/browser`
 | ||||
| // in the browser, see app/views/layouts/_head.html.haml to find how it is imported.
 | ||||
| 
 | ||||
| // eslint-disable-next-line no-underscore-dangle
 | ||||
| window._Sentry = Sentry5; | ||||
| 
 | ||||
| export default index; | ||||
|  | @ -0,0 +1,64 @@ | |||
| import * as Sentry5 from 'sentrybrowser5'; | ||||
| import $ from 'jquery'; | ||||
| import { __ } from '~/locale'; | ||||
| import { IGNORE_ERRORS, DENY_URLS, SAMPLE_RATE } from './constants'; | ||||
| 
 | ||||
| const SentryConfig = { | ||||
|   IGNORE_ERRORS, | ||||
|   BLACKLIST_URLS: DENY_URLS, | ||||
|   SAMPLE_RATE, | ||||
|   init(options = {}) { | ||||
|     this.options = options; | ||||
| 
 | ||||
|     this.configure(); | ||||
|     this.bindSentryErrors(); | ||||
|     if (this.options.currentUserId) this.setUser(); | ||||
|   }, | ||||
| 
 | ||||
|   configure() { | ||||
|     const { dsn, release, tags, whitelistUrls, environment } = this.options; | ||||
| 
 | ||||
|     Sentry5.init({ | ||||
|       dsn, | ||||
|       release, | ||||
|       whitelistUrls, | ||||
|       environment, | ||||
|       ignoreErrors: this.IGNORE_ERRORS, // TODO: Remove in favor of https://gitlab.com/gitlab-org/gitlab/issues/35144
 | ||||
|       blacklistUrls: this.BLACKLIST_URLS, | ||||
|       sampleRate: SAMPLE_RATE, | ||||
|     }); | ||||
| 
 | ||||
|     Sentry5.setTags(tags); | ||||
|   }, | ||||
| 
 | ||||
|   setUser() { | ||||
|     Sentry5.setUser({ | ||||
|       id: this.options.currentUserId, | ||||
|     }); | ||||
|   }, | ||||
| 
 | ||||
|   bindSentryErrors() { | ||||
|     $(document).on('ajaxError.sentry', this.handleSentryErrors); | ||||
|   }, | ||||
| 
 | ||||
|   handleSentryErrors(event, req, config, err) { | ||||
|     const error = err || req.statusText; | ||||
|     const { responseText = __('Unknown response text') } = req; | ||||
|     const { type, url, data } = config; | ||||
|     const { status } = req; | ||||
| 
 | ||||
|     Sentry5.captureMessage(error, { | ||||
|       extra: { | ||||
|         type, | ||||
|         url, | ||||
|         data, | ||||
|         status, | ||||
|         response: responseText, | ||||
|         error, | ||||
|         event, | ||||
|       }, | ||||
|     }); | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| export default SentryConfig; | ||||
|  | @ -0,0 +1,27 @@ | |||
| // The _Sentry object is globally exported so it can be used here
 | ||||
| // This hack allows us to load a single version of `@sentry/browser`
 | ||||
| // in the browser (or none). See app/views/layouts/_head.html.haml
 | ||||
| // to find how it is imported.
 | ||||
| 
 | ||||
| // This module wraps methods used by our production code.
 | ||||
| // Each export is names as we cannot export the entire namespace from *.
 | ||||
| export const captureException = (...args) => { | ||||
|   // eslint-disable-next-line no-underscore-dangle
 | ||||
|   const Sentry = window._Sentry; | ||||
| 
 | ||||
|   Sentry?.captureException(...args); | ||||
| }; | ||||
| 
 | ||||
| export const captureMessage = (...args) => { | ||||
|   // eslint-disable-next-line no-underscore-dangle
 | ||||
|   const Sentry = window._Sentry; | ||||
| 
 | ||||
|   Sentry?.captureMessage(...args); | ||||
| }; | ||||
| 
 | ||||
| export const withScope = (...args) => { | ||||
|   // eslint-disable-next-line no-underscore-dangle
 | ||||
|   const Sentry = window._Sentry; | ||||
| 
 | ||||
|   Sentry?.withScope(...args); | ||||
| }; | ||||
|  | @ -1,30 +1,24 @@ | |||
| import * as Sentry from '@sentry/browser'; | ||||
| import $ from 'jquery'; | ||||
| import { __ } from '~/locale'; | ||||
| import * as Sentry from 'sentrybrowser7'; | ||||
| import { IGNORE_ERRORS, DENY_URLS, SAMPLE_RATE } from './constants'; | ||||
| 
 | ||||
| const SentryConfig = { | ||||
|   IGNORE_ERRORS, | ||||
|   BLACKLIST_URLS: DENY_URLS, | ||||
|   SAMPLE_RATE, | ||||
|   init(options = {}) { | ||||
|     this.options = options; | ||||
| 
 | ||||
|     this.configure(); | ||||
|     this.bindSentryErrors(); | ||||
|     if (this.options.currentUserId) this.setUser(); | ||||
|   }, | ||||
| 
 | ||||
|   configure() { | ||||
|     const { dsn, release, tags, whitelistUrls, environment } = this.options; | ||||
|     const { dsn, release, tags, allowUrls, environment } = this.options; | ||||
| 
 | ||||
|     Sentry.init({ | ||||
|       dsn, | ||||
|       release, | ||||
|       whitelistUrls, | ||||
|       allowUrls, | ||||
|       environment, | ||||
|       ignoreErrors: this.IGNORE_ERRORS, // TODO: Remove in favor of https://gitlab.com/gitlab-org/gitlab/issues/35144
 | ||||
|       blacklistUrls: this.BLACKLIST_URLS, | ||||
|       ignoreErrors: IGNORE_ERRORS, | ||||
|       denyUrls: DENY_URLS, | ||||
|       sampleRate: SAMPLE_RATE, | ||||
|     }); | ||||
| 
 | ||||
|  | @ -36,29 +30,6 @@ const SentryConfig = { | |||
|       id: this.options.currentUserId, | ||||
|     }); | ||||
|   }, | ||||
| 
 | ||||
|   bindSentryErrors() { | ||||
|     $(document).on('ajaxError.sentry', this.handleSentryErrors); | ||||
|   }, | ||||
| 
 | ||||
|   handleSentryErrors(event, req, config, err) { | ||||
|     const error = err || req.statusText; | ||||
|     const { responseText = __('Unknown response text') } = req; | ||||
|     const { type, url, data } = config; | ||||
|     const { status } = req; | ||||
| 
 | ||||
|     Sentry.captureMessage(error, { | ||||
|       extra: { | ||||
|         type, | ||||
|         url, | ||||
|         data, | ||||
|         status, | ||||
|         response: responseText, | ||||
|         error, | ||||
|         event, | ||||
|       }, | ||||
|     }); | ||||
|   }, | ||||
| }; | ||||
| 
 | ||||
| export default SentryConfig; | ||||
|  |  | |||
|  | @ -418,12 +418,12 @@ module Ci | |||
|     end | ||||
| 
 | ||||
|     def waiting_for_deployment_approval? | ||||
|       manual? && starts_environment? && deployment&.blocked? | ||||
|       manual? && deployment_job? && deployment&.blocked? | ||||
|     end | ||||
| 
 | ||||
|     def outdated_deployment? | ||||
|       strong_memoize(:outdated_deployment) do | ||||
|         starts_environment? && | ||||
|         deployment_job? && | ||||
|           incomplete? && | ||||
|           project.ci_forward_deployment_enabled? && | ||||
|           deployment&.older_than_last_successful_deployment? | ||||
|  | @ -527,7 +527,7 @@ module Ci | |||
|       environment.present? | ||||
|     end | ||||
| 
 | ||||
|     def starts_environment? | ||||
|     def deployment_job? | ||||
|       has_environment_keyword? && self.environment_action == 'start' | ||||
|     end | ||||
| 
 | ||||
|  | @ -998,7 +998,7 @@ module Ci | |||
| 
 | ||||
|     # Virtual deployment status depending on the environment status. | ||||
|     def deployment_status | ||||
|       return unless starts_environment? | ||||
|       return unless deployment_job? | ||||
| 
 | ||||
|       if success? | ||||
|         return successful_deployment_status | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ class BuildDetailsEntity < Ci::JobEntity | |||
|   expose :metadata, using: BuildMetadataEntity | ||||
|   expose :pipeline, using: Ci::PipelineEntity | ||||
| 
 | ||||
|   expose :deployment_status, if: -> (*) { build.starts_environment? } do | ||||
|   expose :deployment_status, if: -> (*) { build.deployment_job? } do | ||||
|     expose :deployment_status, as: :status | ||||
|     expose :persisted_environment, as: :environment do |build, options| | ||||
|       options.merge(deployment_details: false).yield_self do |opts| | ||||
|  |  | |||
|  | @ -28,7 +28,7 @@ module Deployments | |||
| 
 | ||||
|     def to_resource(build, environment) | ||||
|       return build.deployment if build.deployment | ||||
|       return unless build.starts_environment? | ||||
|       return unless build.deployment_job? | ||||
| 
 | ||||
|       deployment = ::Deployment.new(attributes(build, environment)) | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| module Groups | ||||
|   module GroupLinks | ||||
|     class CreateService < Groups::BaseService | ||||
|     class CreateService < ::Groups::BaseService | ||||
|       include GroupLinkable | ||||
| 
 | ||||
|       def initialize(group, shared_with_group, user, params) | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| module Groups | ||||
|   module GroupLinks | ||||
|     class DestroyService < BaseService | ||||
|     class DestroyService < ::Groups::BaseService | ||||
|       def execute(one_or_more_links, skip_authorization: false) | ||||
|         unless skip_authorization || group && can?(current_user, :admin_group_member, group) | ||||
|           return error('Not Found', 404) | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| module Groups | ||||
|   module GroupLinks | ||||
|     class UpdateService < BaseService | ||||
|     class UpdateService < ::Groups::BaseService | ||||
|       def initialize(group_link, user = nil) | ||||
|         super(group_link.shared_group, user) | ||||
| 
 | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ | |||
|     = render 'layouts/startup_css', { startup_filename: local_assigns.fetch(:startup_filename, nil) } | ||||
|   - else | ||||
|     - diffs_colors = user_diffs_colors | ||||
|     = stylesheet_link_tag "themes/#{user_application_theme_css_filename}" | ||||
|     = stylesheet_link_tag "themes/#{user_application_theme_css_filename}" if user_application_theme_css_filename | ||||
|     = render 'layouts/diffs_colors_css', diffs_colors if diffs_colors.present? || request.path == profile_preferences_path | ||||
| 
 | ||||
|   - if user_application_theme == 'gl-dark' | ||||
|  | @ -47,7 +47,10 @@ | |||
|   = Gon::Base.render_data(nonce: content_security_policy_nonce) | ||||
| 
 | ||||
|   = render_if_exists 'layouts/header/translations' | ||||
|   = webpack_bundle_tag "sentry" if Gitlab.config.sentry.enabled | ||||
|   - if Feature.enabled?(:enable_new_sentry_clientside_integration, current_user) && Gitlab::CurrentSettings.sentry_enabled | ||||
|     = webpack_bundle_tag 'sentry' | ||||
|   - elsif Gitlab.config.sentry.enabled | ||||
|     = webpack_bundle_tag 'legacy_sentry' | ||||
|   = webpack_bundle_tag 'performance_bar' if performance_bar_enabled? | ||||
| 
 | ||||
|   = yield :page_specific_javascripts | ||||
|  |  | |||
|  | @ -73,7 +73,7 @@ | |||
|       .form-group | ||||
|         = f.label :layout, class: 'label-bold' do | ||||
|           = s_('Preferences|Layout width') | ||||
|         = f.select :layout, layout_choices, {}, class: 'select2' | ||||
|         = f.select :layout, layout_choices, {}, class: 'gl-form-select custom-select' | ||||
|         .form-text.text-muted | ||||
|           = s_('Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout.').html_safe % { percentage: '100%' } | ||||
|       .form-group | ||||
|  | @ -88,7 +88,7 @@ | |||
|       .form-group | ||||
|         = f.label :project_view, class: 'label-bold' do | ||||
|           = s_('Preferences|Project overview content') | ||||
|         = f.select :project_view, project_view_choices, {}, class: 'select2' | ||||
|         = f.select :project_view, project_view_choices, {}, class: 'gl-form-select custom-select' | ||||
|         .form-text.text-muted | ||||
|           = s_('Preferences|Choose what content you want to see on a project’s overview page.') | ||||
|       .form-group | ||||
|  | @ -144,7 +144,7 @@ | |||
|       .form-group | ||||
|         = f.label :first_day_of_week, class: 'label-bold' do | ||||
|           = _('First day of the week') | ||||
|         = f.select :first_day_of_week, first_day_of_week_choices_with_default, {}, class: 'select2' | ||||
|         = f.select :first_day_of_week, first_day_of_week_choices_with_default, {}, class: 'gl-form-select custom-select' | ||||
|     .col-sm-12 | ||||
|       %hr | ||||
|   .row.js-preferences-form.js-search-settings-section | ||||
|  |  | |||
|  | @ -74,6 +74,10 @@ Rails.application.configure do | |||
|   # Do not log asset requests | ||||
|   config.assets.quiet = true | ||||
| 
 | ||||
|   # Use 'listen' gem to watch for file changes and improve performance | ||||
|   # See: https://guides.rubyonrails.org/configuring.html#config-file-watcher | ||||
|   config.file_watcher = ActiveSupport::EventedFileUpdateChecker | ||||
| 
 | ||||
|   # BetterErrors live shell (REPL) on every stack frame | ||||
|   BetterErrors::Middleware.allow_ip!("127.0.0.1/0") | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,8 @@ | |||
| --- | ||||
| name: gitlab_metrics_error_rate_sli | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/103976 | ||||
| rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/383071 | ||||
| milestone: '15.7' | ||||
| type: development | ||||
| group: group::scalability | ||||
| default_enabled: false | ||||
|  | @ -8,6 +8,10 @@ Gitlab::Cluster::LifecycleEvents.on_worker_start do | |||
|   Gitlab::Memory::ReportsDaemon.instance.start | ||||
| end | ||||
| 
 | ||||
| return unless Gitlab::Utils.to_boolean(ENV['GITLAB_MEMWD_DUMP_HEAP']) | ||||
| 
 | ||||
| Gitlab::Cluster::LifecycleEvents.on_worker_stop do | ||||
|   Gitlab::Memory::Reports::HeapDump.write_conditionally | ||||
|   Gitlab::Memory::Reporter.new.run_report( | ||||
|     Gitlab::Memory::Reports::HeapDump.new | ||||
|   ) | ||||
| end | ||||
|  |  | |||
|  | @ -160,6 +160,7 @@ function generateEntries() { | |||
|    */ | ||||
|   const manualEntries = { | ||||
|     default: defaultEntries, | ||||
|     legacy_sentry: './sentry/legacy_index.js', | ||||
|     sentry: './sentry/index.js', | ||||
|     performance_bar: './performance_bar/index.js', | ||||
|     jira_connect_app: './jira_connect/subscriptions/index.js', | ||||
|  | @ -174,6 +175,11 @@ function generateEntries() { | |||
| const alias = { | ||||
|   // Map Apollo client to apollo/client/core to prevent react related imports from being loaded
 | ||||
|   '@apollo/client$': '@apollo/client/core', | ||||
|   // Map Sentry calls to use local wrapper
 | ||||
|   '@sentry/browser$': path.join( | ||||
|     ROOT_PATH, | ||||
|     'app/assets/javascripts/sentry/sentry_browser_wrapper.js', | ||||
|   ), | ||||
|   '~': path.join(ROOT_PATH, 'app/assets/javascripts'), | ||||
|   emojis: path.join(ROOT_PATH, 'fixtures/emojis'), | ||||
|   empty_states: path.join(ROOT_PATH, 'app/views/shared/empty_states'), | ||||
|  |  | |||
|  | @ -0,0 +1,23 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class ChangeVulnerabilitiesStateTransitionsCommentLimit < Gitlab::Database::Migration[2.0] | ||||
|   disable_ddl_transaction! | ||||
| 
 | ||||
|   def up | ||||
|     add_text_limit( | ||||
|       :vulnerability_state_transitions, | ||||
|       :comment, | ||||
|       50_000, | ||||
|       constraint_name: check_constraint_name(:vulnerability_state_transitions, :comment, 'max_length_50000') | ||||
|     ) | ||||
|     remove_text_limit( | ||||
|       :vulnerability_state_transitions, | ||||
|       :comment, | ||||
|       constraint_name: 'check_fca4a7ca39' | ||||
|     ) | ||||
|   end | ||||
| 
 | ||||
|   def down | ||||
|     # no-op: this can fail if records with length > 255 (previous limit) show up | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,16 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class AddIndexForPathsOnNonProjects < Gitlab::Database::Migration[2.0] | ||||
|   TABLE_NAME = 'namespaces' | ||||
|   INDEX_NAME = 'index_namespaces_on_path_for_top_level_non_projects' | ||||
|   COLUMN = "(lower(path::text))" | ||||
|   CONDITIONS = "(parent_id IS NULL AND type::text <> 'Project'::text)" | ||||
| 
 | ||||
|   def up | ||||
|     prepare_async_index TABLE_NAME, COLUMN, name: INDEX_NAME, where: CONDITIONS | ||||
|   end | ||||
| 
 | ||||
|   def down | ||||
|     unprepare_async_index TABLE_NAME, COLUMN, name: INDEX_NAME | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,17 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class AddMetricsIndexToAuthenticationEvents < Gitlab::Database::Migration[2.0] | ||||
|   INDEX_NAME = 'index_successful_authentication_events_for_metrics' | ||||
|   disable_ddl_transaction! | ||||
| 
 | ||||
|   def up | ||||
|     add_concurrent_index :authentication_events, | ||||
|       %i[user_id provider created_at], | ||||
|       where: "result = 1", | ||||
|       name: INDEX_NAME | ||||
|   end | ||||
| 
 | ||||
|   def down | ||||
|     remove_concurrent_index_by_name :authentication_events, INDEX_NAME | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,18 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class RemoveResultIndexFromAuthenticationEvents < Gitlab::Database::Migration[2.0] | ||||
|   disable_ddl_transaction! | ||||
| 
 | ||||
|   INDEX_NAME = 'index_authentication_events_on_provider_user_id_created_at' | ||||
| 
 | ||||
|   def up | ||||
|     remove_concurrent_index_by_name :authentication_events, INDEX_NAME | ||||
|   end | ||||
| 
 | ||||
|   def down | ||||
|     add_concurrent_index :authentication_events, | ||||
|       [:provider, :user_id, :created_at], | ||||
|       where: 'result = 1', | ||||
|       name: INDEX_NAME | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1 @@ | |||
| f73bd76a9ad54932b1f4b880af225a49089fc6ea782d213a9fc608b3029cddab | ||||
|  | @ -0,0 +1 @@ | |||
| 3c9b8f6191297e95c47a0ae2e3da7725ce33daa2a702407e0256393774935b0b | ||||
|  | @ -0,0 +1 @@ | |||
| c1974d6763a85469f3d12fe4e51b1bc3b986cc335b7fe79b3875332d34a1b548 | ||||
|  | @ -0,0 +1 @@ | |||
| 401b563cf9f92627082bbc9850ab2fbe1d9806ced094fda99783c5d51e00fe1c | ||||
|  | @ -23254,7 +23254,7 @@ CREATE TABLE vulnerability_state_transitions ( | |||
|     comment text, | ||||
|     dismissal_reason smallint, | ||||
|     CONSTRAINT check_d1ca8ec043 CHECK ((from_state <> to_state)), | ||||
|     CONSTRAINT check_fca4a7ca39 CHECK ((char_length(comment) <= 255)) | ||||
|     CONSTRAINT check_fe2eb6a0f3 CHECK ((char_length(comment) <= 50000)) | ||||
| ); | ||||
| 
 | ||||
| CREATE SEQUENCE vulnerability_state_transitions_id_seq | ||||
|  | @ -28382,8 +28382,6 @@ CREATE UNIQUE INDEX index_audit_events_external_audit_on_verification_token ON a | |||
| 
 | ||||
| CREATE INDEX index_authentication_events_on_provider ON authentication_events USING btree (provider); | ||||
| 
 | ||||
| CREATE INDEX index_authentication_events_on_provider_user_id_created_at ON authentication_events USING btree (provider, user_id, created_at) WHERE (result = 1); | ||||
| 
 | ||||
| CREATE INDEX index_authentication_events_on_user_and_ip_address_and_result ON authentication_events USING btree (user_id, ip_address, result); | ||||
| 
 | ||||
| CREATE INDEX index_award_emoji_on_awardable_type_and_awardable_id ON award_emoji USING btree (awardable_type, awardable_id); | ||||
|  | @ -30942,6 +30940,8 @@ CREATE INDEX index_subscriptions_on_project_id ON subscriptions USING btree (pro | |||
| 
 | ||||
| CREATE UNIQUE INDEX index_subscriptions_on_subscribable_and_user_id_and_project_id ON subscriptions USING btree (subscribable_id, subscribable_type, user_id, project_id); | ||||
| 
 | ||||
| CREATE INDEX index_successful_authentication_events_for_metrics ON authentication_events USING btree (user_id, provider, created_at) WHERE (result = 1); | ||||
| 
 | ||||
| CREATE INDEX index_successful_deployments_on_cluster_id_and_environment_id ON deployments USING btree (cluster_id, environment_id) WHERE (status = 2); | ||||
| 
 | ||||
| CREATE UNIQUE INDEX index_suggestions_on_note_id_and_relative_order ON suggestions USING btree (note_id, relative_order); | ||||
|  |  | |||
|  | @ -296,8 +296,8 @@ could also be used, those load balancers have not been validated. | |||
| 
 | ||||
| ### Balancing algorithm | ||||
| 
 | ||||
| We recommend that a least-connection load balancing algorithm or equivalent | ||||
| is used wherever possible to ensure equal spread of calls to the nodes and good performance. | ||||
| You should use a least-connection load balancing algorithm or equivalent | ||||
| wherever possible to ensure equal spread of calls to the nodes and good performance. | ||||
| 
 | ||||
| We don't recommend the use of round-robin algorithms as they are known to not | ||||
| spread connections equally in practice. | ||||
|  |  | |||
|  | @ -211,7 +211,7 @@ To resolve the error, run `VACUUM` manually: | |||
| 
 | ||||
| The [database requirements](../../install/requirements.md#database) for GitLab include: | ||||
| 
 | ||||
| - Support for MySQL was removed in GitLab 12.1; [migrate to PostgreSQL](../../update/mysql_to_postgresql.md). | ||||
| - Support for MySQL was removed in [GitLab 12.1](../../update/index.md#1210). | ||||
| - Review and install the [required extension list](../../install/postgresql_extensions.md). | ||||
| 
 | ||||
| ### Serialization errors in the `production/sidekiq` log | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ You can also view or fork the complete [example source](https://gitlab.com/gitla | |||
| ## Initialize the module | ||||
| 
 | ||||
| 1. Open a terminal and navigate to the project's repository. | ||||
| 1. Run `npm init`. Name the module according to [the Package Registry's naming conventions](../../user/packages/npm_registry/index.md#package-naming-convention). For example, if the project's path is `gitlab-examples/semantic-release-npm`, name the module `@gitlab-examples/semantic-release-npm`. | ||||
| 1. Run `npm init`. Name the module according to [the Package Registry's naming conventions](../../user/packages/npm_registry/index.md#naming-convention). For example, if the project's path is `gitlab-examples/semantic-release-npm`, name the module `@gitlab-examples/semantic-release-npm`. | ||||
| 
 | ||||
| 1. Install the following npm packages: | ||||
| 
 | ||||
|  | @ -89,6 +89,7 @@ The default `before_script` generates a temporary `.npmrc` that is used to authe | |||
| As part of publishing a package, semantic-release increases the version number in `package.json`. For semantic-release to commit this change and push it back to GitLab, the pipeline requires a custom CI/CD variable named `GITLAB_TOKEN`. To create this variable: | ||||
| 
 | ||||
| <!-- markdownlint-disable MD044 --> | ||||
| 
 | ||||
| 1. On the top bar, on the top right, select your avatar. | ||||
| 1. On the left sidebar, select **Access Tokens**. | ||||
| 1. In the **Token name** box, enter a token name. | ||||
|  |  | |||
|  | @ -464,3 +464,15 @@ This guide describes how to migrate a Service Ping metric from [`lib/gitlab/usag | |||
| 1. Remove the code from [`lib/gitlab/usage_data.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage_data.rb) or [`ee/lib/ee/gitlab/usage_data.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/ee/gitlab/usage_data.rb). | ||||
| 
 | ||||
| 1. Remove the tests from [`spec/lib/gitlab/usage_data.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/lib/gitlab/usage_data_spec.rb) or [`ee/spec/lib/ee/gitlab/usage_data.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/spec/lib/ee/gitlab/usage_data_spec.rb). | ||||
| 
 | ||||
| ## Troubleshoot metrics | ||||
| 
 | ||||
| Sometimes metrics fail for reasons that are not immediately clear. The failures can be related to performance issues or other problems. | ||||
| The following pairing session video gives you an example of an investigation in to a real-world failing metric. | ||||
| 
 | ||||
| <div class="video-fallback"> | ||||
|   See the video from: <a href="https://www.youtube.com/watch?v=y_6m2POx2ug">Product Intelligence Office Hours Oct 27th</a> to learn more about the metrics troubleshooting process. | ||||
| </div> | ||||
| <figure class="video-container"> | ||||
|   <iframe src="https://www.youtube.com/embed/y_6m2POx2ug" frameborder="0" allowfullscreen="true"> </iframe> | ||||
| </figure> | ||||
|  |  | |||
|  | @ -77,8 +77,7 @@ process, such as PostgreSQL, which can have disastrous consequences. | |||
| 
 | ||||
| PostgreSQL is the only supported database, which is bundled with the Omnibus GitLab package. | ||||
| You can also use an [external PostgreSQL database](https://docs.gitlab.com/omnibus/settings/database.html#using-a-non-packaged-postgresql-database-management-server). | ||||
| Support for MySQL was removed in GitLab 12.1. Existing users using GitLab with | ||||
| MySQL/MariaDB are advised to [migrate to PostgreSQL](../update/mysql_to_postgresql.md) before upgrading. | ||||
| Support for MySQL was removed in [GitLab 12.1](../update/index.md#1210). | ||||
| 
 | ||||
| ### PostgreSQL Requirements | ||||
| 
 | ||||
|  |  | |||
|  | @ -60,7 +60,7 @@ Before you move your project to a group: | |||
| 
 | ||||
| - You must have the Owner role for the project. | ||||
| - Remove any [container images](../user/packages/container_registry/index.md#known-issues) | ||||
|   and [NPM packages](../user/packages/npm_registry/index.md#limitations). | ||||
| - Remove any npm packages. If you transfer a project to a different root namespace, the project must not contain any npm packages. When you update the path of a user or group, or transfer a subgroup or project, you must remove any npm packages first. You cannot update the root namespace of a project with npm packages. Make sure you update your .npmrc files to follow the naming convention and run npm publish if necessary. | ||||
| 
 | ||||
| Now you're ready to move your project: | ||||
| 
 | ||||
|  |  | |||
|  | @ -1177,9 +1177,14 @@ any downgrades would result to all sessions being invalidated and users are logg | |||
| 
 | ||||
| ### 12.1.0 | ||||
| 
 | ||||
| If you are planning to upgrade from `12.0.Z` to `12.10.Z`, it is necessary to | ||||
| perform an intermediary upgrade to `12.1.Z` before upgrading to `12.10.Z` to | ||||
| avoid issues like [#215141](https://gitlab.com/gitlab-org/gitlab/-/issues/215141). | ||||
| - If you are planning to upgrade from `12.0.Z` to `12.10.Z`, it is necessary to | ||||
|   perform an intermediary upgrade to `12.1.Z` before upgrading to `12.10.Z` to | ||||
|   avoid issues like [#215141](https://gitlab.com/gitlab-org/gitlab/-/issues/215141). | ||||
| 
 | ||||
| - Support for MySQL was removed in GitLab 12.1. Existing users using GitLab with | ||||
|   MySQL/MariaDB should | ||||
|   [migrate to PostgreSQL](https://gitlab.com/gitlab-org/gitlab/-/blob/v15.6.0-ee/doc/update/mysql_to_postgresql.md) | ||||
|   before upgrading. | ||||
| 
 | ||||
| ### 12.0.0 | ||||
| 
 | ||||
|  | @ -1265,8 +1270,6 @@ This issue is resolved in GitLab 15.3.3, so customers with the following configu | |||
| 
 | ||||
| ## Miscellaneous | ||||
| 
 | ||||
| - [MySQL to PostgreSQL](mysql_to_postgresql.md) guides you through migrating | ||||
|   your database from MySQL to PostgreSQL. | ||||
| - [Restoring from backup after a failed upgrade](restore_after_failure.md) | ||||
| - [Upgrading PostgreSQL Using Slony](upgrading_postgresql_using_slony.md), for | ||||
|   upgrading a PostgreSQL database with minimal downtime. | ||||
|  |  | |||
|  | @ -2,307 +2,10 @@ | |||
| stage: Data Stores | ||||
| group: Database | ||||
| info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments | ||||
| remove_date: '2023-02-28' | ||||
| redirect_to: 'index.md' | ||||
| --- | ||||
| 
 | ||||
| # Migrating from MySQL to PostgreSQL **(FREE SELF)** | ||||
| # Migrating from MySQL to PostgreSQL (removed) **(FREE SELF)** | ||||
| 
 | ||||
| This guide documents how to take a working GitLab instance that uses MySQL and | ||||
| migrate it to a PostgreSQL database. | ||||
| 
 | ||||
| ## Requirements | ||||
| 
 | ||||
| NOTE: | ||||
| Support for MySQL was removed in GitLab 12.1. This procedure should be performed | ||||
| **before** installing GitLab 12.1. | ||||
| 
 | ||||
| [pgLoader](https://pgloader.io/) 3.4.1+ is required, confirm with `pgloader -V`. | ||||
| 
 | ||||
| You can install it directly from your distribution, for example in | ||||
| Debian/Ubuntu: | ||||
| 
 | ||||
| 1. Search for the version: | ||||
| 
 | ||||
|    ```shell | ||||
|    apt-cache madison pgloader | ||||
|    ``` | ||||
| 
 | ||||
| 1. If the version is 3.4.1+, install it with: | ||||
| 
 | ||||
|    ```shell | ||||
|    sudo apt-get install pgloader | ||||
|    ``` | ||||
| 
 | ||||
|    If your distribution's version is too old, use PostgreSQL's repository: | ||||
| 
 | ||||
|    ```shell | ||||
|    # Add repository | ||||
|    sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' | ||||
| 
 | ||||
|    # Add key | ||||
|    sudo apt-get install wget ca-certificates | ||||
|    wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add - | ||||
| 
 | ||||
|    # Install package | ||||
|    sudo apt-get update | ||||
|    sudo apt-get install pgloader | ||||
|    ``` | ||||
| 
 | ||||
| For other distributions, follow the instructions in PostgreSQL's | ||||
| [download page](https://www.postgresql.org/download/) to add their repository | ||||
| and then install `pgloader`. | ||||
| 
 | ||||
| If you are migrating to a Docker based installation, you must install | ||||
| pgLoader in the container as it is not included in the container image. | ||||
| 
 | ||||
| 1. Start a shell session in the context of the running container: | ||||
| 
 | ||||
|    ```shell | ||||
|    docker exec -it gitlab bash | ||||
|    ``` | ||||
| 
 | ||||
| 1. Install pgLoader: | ||||
| 
 | ||||
|    ```shell | ||||
|    apt-get update | ||||
|    apt-get -y install pgloader | ||||
|    ``` | ||||
| 
 | ||||
| ## Omnibus GitLab installations | ||||
| 
 | ||||
| For [Omnibus GitLab packages](https://about.gitlab.com/install/), you first | ||||
| enable the bundled PostgreSQL: | ||||
| 
 | ||||
| 1. Stop GitLab: | ||||
| 
 | ||||
|    ```shell | ||||
|    sudo gitlab-ctl stop | ||||
|    ``` | ||||
| 
 | ||||
| 1. Edit `/etc/gitlab/gitlab.rb` to enable bundled PostgreSQL: | ||||
| 
 | ||||
|    ```ruby | ||||
|    postgresql['enable'] = true | ||||
|    ``` | ||||
| 
 | ||||
| 1. Edit `/etc/gitlab/gitlab.rb` to use the bundled PostgreSQL. Review all of the | ||||
|    settings beginning with `db_` (such as `gitlab_rails['db_adapter']`). To use | ||||
|    the default values, you can comment all of them out. | ||||
| 
 | ||||
| 1. [Reconfigure GitLab](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure) | ||||
|    for the changes to take effect. | ||||
| 
 | ||||
| 1. Start Puma and PostgreSQL so that we can prepare the schema: | ||||
| 
 | ||||
|    ```shell | ||||
|    sudo gitlab-ctl start puma | ||||
|    sudo gitlab-ctl start postgresql | ||||
|    ``` | ||||
| 
 | ||||
| 1. Run the following commands to prepare the schema: | ||||
| 
 | ||||
|    ```shell | ||||
|    sudo gitlab-rake db:create db:migrate | ||||
|    ``` | ||||
| 
 | ||||
| 1. Stop Puma to prevent other database access from interfering with the loading of data: | ||||
| 
 | ||||
|    ```shell | ||||
|    sudo gitlab-ctl stop puma | ||||
|    ``` | ||||
| 
 | ||||
| After these steps, you have a fresh PostgreSQL database with up-to-date schema. | ||||
| 
 | ||||
| Next, use `pgloader` to migrate the data from the old MySQL database to the | ||||
| new PostgreSQL one: | ||||
| 
 | ||||
| 1. Save the following snippet in a `commands.load` file, and edit with your | ||||
|    MySQL database `username`, `password` and `host`: | ||||
| 
 | ||||
|    ```sql | ||||
|    LOAD DATABASE | ||||
|         FROM mysql://username:password@host/gitlabhq_production | ||||
|         INTO postgresql://gitlab-psql@unix://var/opt/gitlab/postgresql:/gitlabhq_production | ||||
| 
 | ||||
|    WITH include no drop, truncate, disable triggers, create no tables, | ||||
|         create no indexes, preserve index names, no foreign keys, | ||||
|         data only | ||||
| 
 | ||||
|    SET MySQL PARAMETERS | ||||
|    net_read_timeout = '90', | ||||
|    net_write_timeout = '180' | ||||
| 
 | ||||
|    ALTER SCHEMA 'gitlabhq_production' RENAME TO 'public' | ||||
| 
 | ||||
|    ; | ||||
|    ``` | ||||
| 
 | ||||
| 1. Start the migration: | ||||
| 
 | ||||
|    ```shell | ||||
|    sudo -u gitlab-psql pgloader commands.load | ||||
|    ``` | ||||
| 
 | ||||
| 1. After the migration finishes, you should see a summary table that looks like | ||||
|    the following: | ||||
| 
 | ||||
|    ```plaintext | ||||
|                                     table name       read   imported     errors      total time | ||||
|    -----------------------------------------------  ---------  ---------  ---------  -------------- | ||||
|                                    fetch meta data        119        119          0          0.388s | ||||
|                                           Truncate        119        119          0          1.134s | ||||
|    -----------------------------------------------  ---------  ---------  ---------  -------------- | ||||
|                               public.abuse_reports          0          0          0          0.490s | ||||
|                                 public.appearances          0          0          0          0.488s | ||||
|                                           . | ||||
|                                           . | ||||
|                                           . | ||||
|                               public.web_hook_logs          0          0          0          1.080s | ||||
|    -----------------------------------------------  ---------  ---------  ---------  -------------- | ||||
|                            COPY Threads Completion          4          4          0          2.008s | ||||
|                                    Reset Sequences        113        113          0          0.304s | ||||
|                                   Install Comments          0          0          0          0.000s | ||||
|    -----------------------------------------------  ---------  ---------  ---------  -------------- | ||||
|                                  Total import time       1894       1894          0         12.497s | ||||
|    ``` | ||||
| 
 | ||||
|    If there is no output for more than 30 minutes, it's possible `pgloader` encountered an error. See | ||||
|    the [troubleshooting guide](#troubleshooting) for more details. | ||||
| 
 | ||||
| 1. Start GitLab: | ||||
| 
 | ||||
|    ```shell | ||||
|    sudo gitlab-ctl start | ||||
|    ``` | ||||
| 
 | ||||
| You can now verify that everything works as expected by visiting GitLab. | ||||
| 
 | ||||
| ## Source installations | ||||
| 
 | ||||
| For installations from source that use MySQL, you must first | ||||
| [install PostgreSQL and create a database](../install/installation.md#6-database). | ||||
| 
 | ||||
| After the database is created, go on with the following steps: | ||||
| 
 | ||||
| 1. Stop GitLab: | ||||
| 
 | ||||
|    ```shell | ||||
|    sudo service gitlab stop | ||||
|    ``` | ||||
| 
 | ||||
| 1. Switch database from MySQL to PostgreSQL | ||||
| 
 | ||||
|    ```shell | ||||
|    cd /home/git/gitlab | ||||
|    sudo -u git mv config/database.yml config/database.yml.bak | ||||
|    sudo -u git cp config/database.yml.postgresql config/database.yml | ||||
|    sudo -u git -H chmod o-rwx config/database.yml | ||||
|    ``` | ||||
| 
 | ||||
| 1. Install Gems related to PostgreSQL | ||||
| 
 | ||||
|    ```shell | ||||
|    sudo -u git -H rm .bundle/config | ||||
|    sudo -u git -H bundle install --deployment --without development test mysql aws kerberos | ||||
|    ``` | ||||
| 
 | ||||
| 1. Run the following commands to prepare the schema: | ||||
| 
 | ||||
|    ```shell | ||||
|    sudo -u git -H bundle exec rake db:create db:migrate RAILS_ENV=production | ||||
|    ``` | ||||
| 
 | ||||
| After these steps, you have a fresh PostgreSQL database with up-to-date schema. | ||||
| 
 | ||||
| Next, use `pgloader` to migrate the data from the old MySQL database to the | ||||
| new PostgreSQL one: | ||||
| 
 | ||||
| 1. Save the following snippet in a `commands.load` file, and edit with your | ||||
|    MySQL `username`, `password` and `host`: | ||||
| 
 | ||||
|    ```sql | ||||
|    LOAD DATABASE | ||||
|         FROM mysql://username:password@host/gitlabhq_production | ||||
|         INTO postgresql://postgres@unix://var/run/postgresql:/gitlabhq_production | ||||
| 
 | ||||
|    WITH include no drop, truncate, disable triggers, create no tables, | ||||
|         create no indexes, preserve index names, no foreign keys, | ||||
|         data only | ||||
| 
 | ||||
|    SET MySQL PARAMETERS | ||||
|    net_read_timeout = '90', | ||||
|    net_write_timeout = '180' | ||||
| 
 | ||||
|    ALTER SCHEMA 'gitlabhq_production' RENAME TO 'public' | ||||
| 
 | ||||
|    ; | ||||
|    ``` | ||||
| 
 | ||||
| 1. Start the migration: | ||||
| 
 | ||||
|    ```shell | ||||
|    sudo -u postgres pgloader commands.load | ||||
|    ``` | ||||
| 
 | ||||
| 1. After the migration finishes, you should see a summary table that looks like | ||||
|    the following: | ||||
| 
 | ||||
|    ```plaintext | ||||
|                                     table name       read   imported     errors      total time | ||||
|    -----------------------------------------------  ---------  ---------  ---------  -------------- | ||||
|                                    fetch meta data        119        119          0          0.388s | ||||
|                                           Truncate        119        119          0          1.134s | ||||
|    -----------------------------------------------  ---------  ---------  ---------  -------------- | ||||
|                               public.abuse_reports          0          0          0          0.490s | ||||
|                                 public.appearances          0          0          0          0.488s | ||||
|                                           . | ||||
|                                           . | ||||
|                                           . | ||||
|                               public.web_hook_logs          0          0          0          1.080s | ||||
|    -----------------------------------------------  ---------  ---------  ---------  -------------- | ||||
|                            COPY Threads Completion          4          4          0          2.008s | ||||
|                                    Reset Sequences        113        113          0          0.304s | ||||
|                                   Install Comments          0          0          0          0.000s | ||||
|    -----------------------------------------------  ---------  ---------  ---------  -------------- | ||||
|                                  Total import time       1894       1894          0         12.497s | ||||
|    ``` | ||||
| 
 | ||||
|    If there is no output for more than 30 minutes, it's possible `pgloader` encountered an error. See | ||||
|    the [troubleshooting guide](#troubleshooting) for more details. | ||||
| 
 | ||||
| 1. Start GitLab: | ||||
| 
 | ||||
|    ```shell | ||||
|    sudo service gitlab start | ||||
|    ``` | ||||
| 
 | ||||
| You can now verify that everything works as expected by visiting GitLab. | ||||
| 
 | ||||
| ## Troubleshooting | ||||
| 
 | ||||
| Sometimes, you might encounter some errors during or after the migration. | ||||
| 
 | ||||
| ### Database error permission denied | ||||
| 
 | ||||
| The PostgreSQL user that you use for the migration **must** have **superuser** privileges. | ||||
| Otherwise, you may see a similar message to the following: | ||||
| 
 | ||||
| ```plaintext | ||||
| debugger invoked on a CL-POSTGRES-ERROR:INSUFFICIENT-PRIVILEGE in thread | ||||
|     #<THREAD "lparallel" RUNNING {10078A3513}>: | ||||
|       Database error 42501: permission denied: "RI_ConstraintTrigger_a_20937" is a system trigger | ||||
|     QUERY: ALTER TABLE ci_builds DISABLE TRIGGER ALL; | ||||
|     2017-08-23T00:36:56.782000Z ERROR Database error 42501: permission denied: "RI_ConstraintTrigger_c_20864" is a system trigger | ||||
|     QUERY: ALTER TABLE approver_groups DISABLE TRIGGER ALL; | ||||
| ``` | ||||
| 
 | ||||
| ### 500 errors after the migration | ||||
| 
 | ||||
| If you experience 500 errors after the migration, try to clear the cache: | ||||
| 
 | ||||
| ```shell | ||||
| # Omnibus GitLab | ||||
| sudo gitlab-rake cache:clear | ||||
| 
 | ||||
| # Installations from source | ||||
| sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production | ||||
| ``` | ||||
| Support for MySQL was removed in GitLab 12.1. | ||||
|  |  | |||
|  | @ -6,250 +6,97 @@ info: To determine the technical writer assigned to the Stage/Group associated w | |||
| 
 | ||||
| # npm packages in the Package Registry **(FREE)** | ||||
| 
 | ||||
| > [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/221259) from GitLab Premium to GitLab Free in 13.3. | ||||
| For documentation of the specific API endpoints that the npm package manager client uses, see the [npm API documentation](../../../api/packages/npm.md). | ||||
| 
 | ||||
| Publish npm packages in your project's Package Registry. Then install the | ||||
| packages whenever you need to use them as a dependency. | ||||
| Learn how to build an [npm](../workflows/build_packages.md#npm) or [yarn](../workflows/build_packages.md#yarn) package. | ||||
| 
 | ||||
| Only [scoped](https://docs.npmjs.com/misc/scope/) packages are supported. | ||||
| Watch a [video demo](https://youtu.be/yvLxtkvsFDA) of how to publish npm packages to the GitLab Package Registry. | ||||
| 
 | ||||
| For documentation of the specific API endpoints that the npm package manager | ||||
| client uses, see the [npm API documentation](../../../api/packages/npm.md). | ||||
| ## Publish to GitLab Package Registry | ||||
| 
 | ||||
| ### Authentication to the Package Registry | ||||
| 
 | ||||
| You need an token to publish a package. There are different tokens available depending on what you're trying to achieve. For more information, review the [guidance on tokens](../../../user/packages/package_registry/index.md#authenticate-with-the-registry). | ||||
| 
 | ||||
| - If your organization uses two factor authentication (2FA), you must use a personal access token with the scope set to `api`. | ||||
| - If you are publishing a package via CI/CD pipelines, you must use a CI job token. | ||||
| 
 | ||||
| Create a token and save it to use later in the process. | ||||
| 
 | ||||
| ### Naming convention | ||||
| 
 | ||||
| Depending on how the package will be installed, you may need to adhere to the naming convention. | ||||
| 
 | ||||
| You can use one of two API endpoints to install packages: | ||||
| 
 | ||||
| - **Instance-level**: Use when you have many npm packages in different GitLab groups or in their own namespace. | ||||
| - **Project-level**: Use when you have few npm packages and they are not in the same GitLab group. | ||||
| 
 | ||||
| If you plan to install a package through the [project level](#install-from-the-project-level), then you do not have to adhere to the naming convention. | ||||
| 
 | ||||
| If you plan to install a package through the [instance level](#install-from-the-instance-level), then you must name your package with a [scope](https://docs.npmjs.com/misc/scope/). Scoped packages begin with a `@` have the format of `@owner/package-name`. You can set up the scope for your package in the `.npmrc` file and by using the `publishConfig` option in the `package.json`. | ||||
| 
 | ||||
| - The value used for the `@scope` is the root of the project that will host the packages and not the root | ||||
|   of the project with the source code of the package itself. The scope should be lowercase. | ||||
| - The package name can be anything you want | ||||
| 
 | ||||
| | Project URL                                             | Package Registry in | Scope     | Full package name      | | ||||
| | ------------------------------------------------------- | ------------------- | --------- | ---------------------- | | ||||
| | `https://gitlab.com/my-org/engineering-group/analytics` | Analytics           | `@my-org` | `@my-org/package-name` | | ||||
| 
 | ||||
| Make sure that the name of your package in the `package.json` file matches this convention: | ||||
| 
 | ||||
| ```shell | ||||
| "name": "@my-org/package-name" | ||||
| ``` | ||||
| 
 | ||||
| ## Publishing a package via the command line | ||||
| 
 | ||||
| ### Authenticating via the `.npmrc` | ||||
| 
 | ||||
| Create or edit the `.npmrc` file in the same directory as your `package.json`. Include the following lines in the `.npmrc` file: | ||||
| 
 | ||||
| ```shell | ||||
| @scope:registry=https://your_domain_name/api/v4/projects/your_project_id/packages/npm/ | ||||
| //your_domain_name/api/v4/projects/your_project_id/packages/npm/:_authToken="${NPM_TOKEN}" | ||||
| ``` | ||||
| 
 | ||||
| - Replace `@scope` with the [root level group](#naming-convention) of the project you're publishing to the package to. | ||||
| - Replace `your_domain_name` with your domain name, for example, `gitlab.com`. | ||||
| - Replace `your_project_id` is your project ID, found on the project's home page. | ||||
| - `"${NPM_TOKEN}"` will be associated with the token you created later in the process. | ||||
| 
 | ||||
| WARNING: | ||||
| Never hardcode GitLab tokens (or any tokens) directly in `.npmrc` files or any other files that can | ||||
| be committed to a repository. | ||||
| 
 | ||||
| Learn how to build an [npm](../workflows/build_packages.md#npm) or [yarn](../workflows/build_packages.md#yarn) package. | ||||
| ### Publishing a package via the command line | ||||
| 
 | ||||
| ## Use the GitLab endpoint for npm packages | ||||
| 
 | ||||
| To use the GitLab endpoint for npm packages, choose an option: | ||||
| 
 | ||||
| - **Project-level**: Use when you have few npm packages and they are not in | ||||
|   the same GitLab group. The [package naming convention](#package-naming-convention) is not enforced at this level. | ||||
|   Instead, you should use a [scope](https://docs.npmjs.com/cli/v6/using-npm/scope/) for your package. | ||||
|   When you use a scope, the registry URL is [updated](#authenticate-to-the-package-registry) only for that scope. | ||||
| - **Instance-level**: Use when you have many npm packages in different | ||||
|   GitLab groups or in their own namespace. Be sure to comply with the [package naming convention](#package-naming-convention). | ||||
| 
 | ||||
| Some features such as [publishing](#publish-an-npm-package) a package is only available on the project-level endpoint. | ||||
| 
 | ||||
| ## Authenticate to the Package Registry | ||||
| 
 | ||||
| You must authenticate with the Package Registry when the project | ||||
| is private. Public projects do not require authentication. | ||||
| 
 | ||||
| To authenticate, use one of the following: | ||||
| 
 | ||||
| - A [personal access token](../../../user/profile/personal_access_tokens.md) | ||||
|   (required for two-factor authentication (2FA)), with the scope set to `api`. | ||||
| - A [deploy token](../../project/deploy_tokens/index.md), with the scope set to `read_package_registry`, `write_package_registry`, or both. | ||||
| - It's not recommended, but you can use [OAuth tokens](../../../api/oauth2.md#resource-owner-password-credentials-flow). | ||||
|   Standard OAuth tokens cannot authenticate to the GitLab npm Registry. You must use a personal access token with OAuth headers. | ||||
| - A [CI job token](#authenticate-with-a-ci-job-token). | ||||
| - Your npm package name must be in the format of [`@scope/package-name`](#package-naming-convention). | ||||
|   It must match exactly, including the case. | ||||
| 
 | ||||
| ### Authenticate with a personal access token or deploy token | ||||
| 
 | ||||
| To authenticate with the Package Registry, you need a [personal access token](../../profile/personal_access_tokens.md) or [deploy token](../../project/deploy_tokens/index.md). | ||||
| 
 | ||||
| #### Project-level npm endpoint | ||||
| 
 | ||||
| To use the [project-level](#use-the-gitlab-endpoint-for-npm-packages) npm endpoint, set your npm configuration: | ||||
| Associate your [token](#authentication-to-the-package-registry) with the `"${NPM_TOKEN}"` in the `.npmrc`. Replace `your_token` with a deploy token, group access token, project access token, or personal access token. | ||||
| 
 | ||||
| ```shell | ||||
| # Set URL for your scoped packages. | ||||
| # For example package with name `@foo/bar` will use this URL for download | ||||
| npm config set @foo:registry https://gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/ | ||||
| 
 | ||||
| # Add the token for the scoped packages URL. Replace <your_project_id> | ||||
| # with the project where your package is located. | ||||
| npm config set -- '//gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/:_authToken' "<your_token>" | ||||
| NPM_TOKEN=your_token npm publish | ||||
| ``` | ||||
| 
 | ||||
| - `<your_project_id>` is your project ID, found on the project's home page. | ||||
| - `<your_token>` is your personal access token or deploy token. | ||||
| - Replace `gitlab.example.com` with your domain name. | ||||
| Your package should now publish to the Package Registry. | ||||
| 
 | ||||
| You should now be able to publish and install npm packages in your project. | ||||
| ## Publishing a package via a CI/CD pipeline | ||||
| 
 | ||||
| If you encounter an error with [Yarn](https://classic.yarnpkg.com/en/), view | ||||
| [troubleshooting steps](#troubleshooting). | ||||
| ### Authenticating via the `.npmrc` | ||||
| 
 | ||||
| #### Instance-level npm endpoint | ||||
| 
 | ||||
| NOTE: | ||||
| Note: Using `CI_JOB_TOKEN` to install npm packages with dependencies in another project will give you 404 errors. You can use a [personal access token](../../profile/personal_access_tokens.md) as a workaround. [GitLab-#352962](https://gitlab.com/gitlab-org/gitlab/-/issues/352962) proposes a fix to this bug. | ||||
| 
 | ||||
| To use the [instance-level](#use-the-gitlab-endpoint-for-npm-packages) npm endpoint, set your npm configuration: | ||||
| Create or edit the `.npmrc` file in the same directory as your `package.json` in a GitLab project. Include the following lines in the `.npmrc` file: | ||||
| 
 | ||||
| ```shell | ||||
| # Set URL for your scoped packages. | ||||
| # For example package with name `@foo/bar` will use this URL for download | ||||
| npm config set @foo:registry https://gitlab.example.com/api/v4/packages/npm/ | ||||
| 
 | ||||
| # Add the token for the scoped packages URL. This will allow you to download | ||||
| # `@foo/` packages from private projects. | ||||
| npm config set -- '//gitlab.example.com/api/v4/packages/npm/:_authToken' "<your_token>" | ||||
| @scope:registry=https://your_domain_name/api/v4/projects/your_project_id/packages/npm/ | ||||
| //your_domain_name/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN} | ||||
| ``` | ||||
| 
 | ||||
| - `<your_token>` is your personal access token or deploy token. | ||||
| - Replace `gitlab.example.com` with your domain name. | ||||
| - Replace `@scope` with the [root level group](#naming-convention) of the project you're publishing to the package to. | ||||
| - The `${CI_PROJECT_ID}` and `${CI_JOB_TOKEN}` are [predefined variables](../../../ci/variables/predefined_variables.md) that are available in the pipeline and do not need to be replaced. | ||||
| 
 | ||||
| You should now be able to install npm packages in your project. | ||||
| ### Publishing a package via a CI/CD pipeline | ||||
| 
 | ||||
| If you encounter an error with [Yarn](https://classic.yarnpkg.com/en/), view | ||||
| [troubleshooting steps](#troubleshooting). | ||||
| 
 | ||||
| ### Authenticate with a CI job token | ||||
| 
 | ||||
| > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9104) in GitLab 12.5. | ||||
| > - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/221259) from GitLab Premium to GitLab Free in 13.3. | ||||
| 
 | ||||
| If you're using npm with GitLab CI/CD, a CI job token can be used instead of a personal access token or deploy token. | ||||
| The token inherits the permissions of the user that generates the pipeline. | ||||
| 
 | ||||
| #### Project-level npm endpoint | ||||
| 
 | ||||
| To use the [project-level](#use-the-gitlab-endpoint-for-npm-packages) npm endpoint, add a corresponding section to your `.npmrc` file: | ||||
| 
 | ||||
| ```ini | ||||
| @foo:registry=https://gitlab.example.com/api/v4/projects/${CI_PROJECT_ID}/packages/npm/ | ||||
| //gitlab.example.com/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN} | ||||
| ``` | ||||
| 
 | ||||
| #### Instance-level npm endpoint | ||||
| 
 | ||||
| To use the [instance-level](#use-the-gitlab-endpoint-for-npm-packages) npm endpoint, add a corresponding section to your `.npmrc` file: | ||||
| 
 | ||||
| ```ini | ||||
| @foo:registry=https://gitlab.example.com/api/v4/packages/npm/ | ||||
| //gitlab.example.com/api/v4/packages/npm/:_authToken=${CI_JOB_TOKEN} | ||||
| ``` | ||||
| 
 | ||||
| #### Use variables to avoid hard-coding auth token values | ||||
| 
 | ||||
| To avoid hard-coding the `authToken` value, you may use a variable in its place: | ||||
| 
 | ||||
| ```shell | ||||
| npm config set -- '//gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/:_authToken' "${NPM_TOKEN}" | ||||
| npm config set -- '//gitlab.example.com/api/v4/packages/npm/:_authToken' "${NPM_TOKEN}" | ||||
| ``` | ||||
| 
 | ||||
| Then, you can run `npm publish` either locally or by using GitLab CI/CD. | ||||
| 
 | ||||
| - **Locally:** Export `NPM_TOKEN` before publishing: | ||||
| 
 | ||||
|   ```shell | ||||
|   NPM_TOKEN=<your_token> npm publish | ||||
|   ``` | ||||
| 
 | ||||
| - **GitLab CI/CD:** Set an `NPM_TOKEN` [CI/CD variable](../../../ci/variables/index.md) | ||||
|   under your project's **Settings > CI/CD > Variables**. | ||||
| 
 | ||||
| ## Working with private registries | ||||
| 
 | ||||
| When working with private repositories, you may want to configure additional settings to ensure a secure communication channel: | ||||
| 
 | ||||
| ```shell | ||||
| # Force npm to always require authentication when accessing the registry, even for GET requests. | ||||
| npm config set always-auth true | ||||
| ``` | ||||
| 
 | ||||
| ## Package naming convention | ||||
| 
 | ||||
| When you use the [instance-level endpoint](#use-the-gitlab-endpoint-for-npm-packages), only the packages with names in the format of `@scope/package-name` are available. | ||||
| 
 | ||||
| - The `@scope` is the root namespace of the GitLab project. To follow npm's convention, it should be | ||||
|   lowercase. However, the GitLab package registry allows for uppercase. Before GitLab 13.10, the | ||||
|   `@scope` had to be a case-sensitive match of the GitLab project's root namespace. This was | ||||
|   problematic because the npm public registry does not allow uppercase letters. GitLab 13.10 relaxes | ||||
|   this requirement and translates uppercase in the GitLab `@scope` to lowercase for npm. For | ||||
|   example, a package `@MyScope/package-name` in GitLab becomes `@myscope/package-name` for npm. | ||||
| - The `package-name` can be whatever you want. | ||||
| 
 | ||||
| NOTE: | ||||
| The value used for the `@scope` is the root of the project that will end up hosting the packages and not the root | ||||
| of the project with the source code of the package itself. For example, assume your package source code is located | ||||
| at `source-code-group/package-code` and deployed to a package registry inside `registries-group/registry-project`. | ||||
| In this case, the `@scope` needs to be `@registries-group` and not `@source-code-group`. | ||||
| 
 | ||||
| For example, if your project is `https://gitlab.example.com/my-org/engineering-group/team-amazing/analytics`, | ||||
| the root namespace is `my-org`. When you publish a package, it must have `my-org` as the scope. | ||||
| 
 | ||||
| | Project             | Package              | Supported | | ||||
| | ------------------- | -------------------- | --------- | | ||||
| | `my-org/bar`        | `@my-org/bar`        | Yes       | | ||||
| | `my-org/bar/baz`    | `@my-org/baz`        | Yes       | | ||||
| | `My-Org/Bar/baz`    | `@my-org/Baz`        | Yes       | | ||||
| | `My-Org/Bar/baz`    | `@My-Org/Baz`        | Yes       | | ||||
| | `my-org/bar/buz`    | `@my-org/anything`   | Yes       | | ||||
| | `gitlab-org/gitlab` | `@gitlab-org/gitlab` | Yes       | | ||||
| | `gitlab-org/gitlab` | `@foo/bar`           | No        | | ||||
| 
 | ||||
| In GitLab, this regex validates all package names from all package managers: | ||||
| 
 | ||||
| ```plaintext | ||||
| /\A\@?(([\w\-\.\+]*)\/)*([\w\-\.]+)@?(([\w\-\.\+]*)\/)*([\w\-\.]*)\z/ | ||||
| ``` | ||||
| 
 | ||||
| This regex allows almost all of the characters that npm allows, with a few exceptions (for example, `~` is not allowed). | ||||
| 
 | ||||
| The regex also allows for capital letters, while npm does not. | ||||
| 
 | ||||
| ## Limitations | ||||
| 
 | ||||
| When you update the path of a user or group, or transfer a subgroup or project, | ||||
| you must remove any npm packages first. You cannot update the root namespace | ||||
| of a project with npm packages. Make sure you update your `.npmrc` files to follow | ||||
| the naming convention and run `npm publish` if necessary. | ||||
| 
 | ||||
| ## Publish an npm package | ||||
| 
 | ||||
| Prerequisites: | ||||
| 
 | ||||
| - [Authenticate](#authenticate-to-the-package-registry) to the Package Registry. | ||||
| - Set a [project-level npm endpoint](#use-the-gitlab-endpoint-for-npm-packages). | ||||
| 
 | ||||
| To upload an npm package to your project, run this command: | ||||
| 
 | ||||
| ```shell | ||||
| npm publish | ||||
| ``` | ||||
| 
 | ||||
| To view the package, go to your project's **Packages and registries**. | ||||
| 
 | ||||
| You can also define `"publishConfig"` for your project in `package.json`. For example: | ||||
| 
 | ||||
| ```json | ||||
| { | ||||
|   "publishConfig": { | ||||
|     "@foo:registry": " https://gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/" | ||||
|   } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| This forces the package to publish only to the specified registry. | ||||
| 
 | ||||
| If you try to publish a package [with a name that already exists](#publishing-packages-with-the-same-name-or-version) within | ||||
| a given scope, you get a `403 Forbidden!` error. | ||||
| 
 | ||||
| ## Publish an npm package by using CI/CD | ||||
| 
 | ||||
| Prerequisites: | ||||
| 
 | ||||
| - [Authenticate](#authenticate-to-the-package-registry) to the Package Registry. | ||||
| - Set a [project-level npm endpoint](#use-the-gitlab-endpoint-for-npm-packages). | ||||
| - Your npm package name must be in the format of [`@scope/package-name`](#package-naming-convention). | ||||
|   It must match exactly, including the case. This is different than the | ||||
|   npm naming convention, but it is required to work with the GitLab Package Registry. | ||||
| 
 | ||||
| To work with npm commands within [GitLab CI/CD](../../../ci/index.md), you can use | ||||
| `CI_JOB_TOKEN` in place of the personal access token or deploy token in your commands. | ||||
| 
 | ||||
| An example `.gitlab-ci.yml` file for publishing npm packages: | ||||
| In the GitLab project that houses your `.npmrc` and `package.json`, edit or create a `.gitlab-ci.yml` file. For example: | ||||
| 
 | ||||
| ```yaml | ||||
| image: node:latest | ||||
|  | @ -262,115 +109,86 @@ deploy: | |||
|   script: | ||||
|     - echo "//${CI_SERVER_HOST}/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=${CI_JOB_TOKEN}">.npmrc | ||||
|     - npm publish | ||||
|   environment: production | ||||
| ``` | ||||
| 
 | ||||
| See the | ||||
| [Publish npm packages to the GitLab Package Registry using semantic-release](../../../ci/examples/semantic-release.md) | ||||
| step-by-step guide and demo project for a complete example. | ||||
| 
 | ||||
| ## Configure the GitLab npm registry with Yarn 2 | ||||
| 
 | ||||
| You can get started with Yarn 2 by following the [Yarn documentation](https://yarnpkg.com/getting-started/install/). | ||||
| 
 | ||||
| To publish and install with the project-level npm endpoint, set the following configuration in | ||||
| `.yarnrc.yml`: | ||||
| 
 | ||||
| ```yaml | ||||
| npmScopes: | ||||
|   foo: | ||||
|     npmRegistryServer: 'https://gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/' | ||||
|     npmPublishRegistry: 'https://gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/' | ||||
| 
 | ||||
| npmRegistries: | ||||
|   //gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/: | ||||
|     npmAlwaysAuth: true | ||||
|     npmAuthToken: '<your_token>' | ||||
| ``` | ||||
| 
 | ||||
| For the instance-level npm endpoint, use this Yarn 2 configuration in `.yarnrc.yml`: | ||||
| 
 | ||||
| ```yaml | ||||
| npmScopes: | ||||
|   foo: | ||||
|     npmRegistryServer: 'https://gitlab.example.com/api/v4/packages/npm/' | ||||
| 
 | ||||
| npmRegistries: | ||||
|   //gitlab.example.com/api/v4/packages/npm/: | ||||
|     npmAlwaysAuth: true | ||||
|     npmAuthToken: '<your_token>' | ||||
| ``` | ||||
| 
 | ||||
| In this configuration: | ||||
| 
 | ||||
| - Replace `<your_token>` with your personal access token or deploy token. | ||||
| - Replace `<your_project_id>` with your project's ID, which you can find on the project's home page. | ||||
| - Replace `gitlab.example.com` with your domain name. | ||||
| - Your scope is `foo`, without `@`. | ||||
| 
 | ||||
| ## Publishing packages with the same name or version | ||||
| 
 | ||||
| You cannot publish a package if a package of the same name and version already exists. | ||||
| You must delete the existing package first. | ||||
| 
 | ||||
| This rule has a different impact depending on the package name: | ||||
| 
 | ||||
| - For packages following the [naming convention](#package-naming-convention), you can't publish a | ||||
|   package with a duplicate name and version to the root namespace. | ||||
| - For packages not following the [naming convention](#package-naming-convention), you can't publish | ||||
|   a package with a duplicate name and version to the project you target with the upload. | ||||
| 
 | ||||
| This aligns with npmjs.org's behavior. However, npmjs.org does not ever let you publish | ||||
| the same version more than once, even if it has been deleted. | ||||
| 
 | ||||
| ## `package.json` limitations | ||||
| 
 | ||||
| You can't publish a package if its `package.json` file exceeds 20,000 characters. | ||||
| Your package should now publish to the Package Registry when the pipeline runs. | ||||
| 
 | ||||
| ## Install a package | ||||
| 
 | ||||
| npm packages are commonly-installed by using the `npm` or `yarn` commands | ||||
| in a JavaScript project. You can install a package from the scope of a project or instance. | ||||
| 
 | ||||
| If multiple packages have the same name and version, when you install a package, the most recently-published package is retrieved. | ||||
| 
 | ||||
| 1. Set the URL for scoped packages. | ||||
| You can install a package from a GitLab project or instance: | ||||
| 
 | ||||
|    For [instance-level endpoints](#use-the-gitlab-endpoint-for-npm-packages) run: | ||||
| - **Instance-level**: Use when you have many npm packages in different GitLab groups or in their own namespace. | ||||
| - **Project-level**: Use when you have few npm packages and they are not in the same GitLab group. | ||||
| 
 | ||||
| ### Install from the instance level | ||||
| 
 | ||||
| WARNING: | ||||
| In order to install a package from the instance level, the package must have been published following the scoped [naming convention](#naming-convention). | ||||
| 
 | ||||
| 1. Authenticate to the Package Registry | ||||
| 
 | ||||
|    If you would like to install a package from a private project, you will need to authenticate to the Package Registry. Skip this step if the project is not private. | ||||
| 
 | ||||
|    ```shell | ||||
|    npm config set @foo:registry https://gitlab.example.com/api/v4/packages/npm/ | ||||
|    npm config set -- //your_domain_name/api/v4/packages/npm/:_authToken=your_token | ||||
|    ``` | ||||
| 
 | ||||
|    - Replace `@foo` with your scope. | ||||
|    - Replace `gitlab.example.com` with your domain name. | ||||
|    - Replace `your_domain_name` with your domain name, for example, `gitlab.com`. | ||||
|    - Replace `your_token` with a deploy token, group access token, project access token, or personal access token. | ||||
| 
 | ||||
|    For [project-level endpoints](#use-the-gitlab-endpoint-for-npm-packages) run: | ||||
| 1. Set the registry | ||||
| 
 | ||||
|    ```shell | ||||
|    npm config set @foo:registry https://gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/ | ||||
|    npm config set @scope:registry https://your_domain_name.com/api/v4/packages/npm/ | ||||
|    ``` | ||||
| 
 | ||||
|    - Replace `@foo` with your scope. | ||||
|    - Replace `gitlab.example.com` with your domain name. | ||||
|    - Replace `<your_project_id>` with your project ID, found on the project's home page. | ||||
|    - Replace `@scope` with the [root level group](#naming-convention) of the project you're installing to the package from. | ||||
|    - Replace `your_domain_name` with your domain name, for example `gitlab.com`. | ||||
|    - Replace `your_token` with a deploy token, group access token, project access token, or personal access token. | ||||
| 
 | ||||
| 1. Ensure [authentication](#authenticate-to-the-package-registry) is configured. | ||||
| 
 | ||||
| 1. To install a package in your project, run: | ||||
| 1. Install the package | ||||
| 
 | ||||
|    ```shell | ||||
|    npm install @my-scope/my-package | ||||
|    npm install @scope/my-package | ||||
|    ``` | ||||
| 
 | ||||
|    Or if you're using Yarn: | ||||
| ### Install from the project level | ||||
| 
 | ||||
| 1. Authenticate to the Package Registry | ||||
| 
 | ||||
|    If you would like to install a package from a private project, you will need to authenticate to the Package Registry. Skip this step if the project is not private. | ||||
| 
 | ||||
|    ```shell | ||||
|    yarn add @my-scope/my-package | ||||
|    npm config set -- //your_domain_name/api/v4/projects/your_project_id/packages/npm/:_authToken=your_token | ||||
|    ``` | ||||
| 
 | ||||
| In [GitLab 12.9 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/55344), | ||||
| when an npm package is not found in the Package Registry, the request is forwarded to [npmjs.com](https://www.npmjs.com/). | ||||
|    - Replace `your_domain_name` with your domain name, for example, `gitlab.com`. | ||||
|    - Replace `your_project_id` is your project ID, found on the project's home page. | ||||
|    - Replace `your_token` with a deploy token, group access token, project access token, or personal access token. | ||||
| 
 | ||||
| 1. Set the registry | ||||
| 
 | ||||
|    ```shell | ||||
|    npm config set @scope:registry=https://your_domain_name/api/v4/projects/your_project_id/packages/npm/ | ||||
|    ``` | ||||
| 
 | ||||
|    - Replace `@scope` with the [root level group](#naming-convention) of the project you're installing to the package from. | ||||
|    - Replace `your_domain_name` with your domain name, for example, `gitlab.com`. | ||||
|    - Replace `your_project_id` is your project ID, found on the project's home page. | ||||
| 
 | ||||
| 1. Install the package | ||||
| 
 | ||||
|    ```shell | ||||
|    npm install @scope/my-package | ||||
|    ``` | ||||
| 
 | ||||
| ## Helpful hints | ||||
| 
 | ||||
| ### Package forwarding to npmjs.com | ||||
| 
 | ||||
| When an npm package is not found in the Package Registry, the request is forwarded to [npmjs.com](https://www.npmjs.com/). | ||||
| 
 | ||||
| Administrators can disable this behavior in the [Continuous Integration settings](../../admin_area/settings/continuous_integration.md). | ||||
| 
 | ||||
|  | @ -378,27 +196,16 @@ Administrators can disable this behavior in the [Continuous Integration settings | |||
| 
 | ||||
| You can route package requests to organizations and users outside of GitLab. | ||||
| 
 | ||||
| To do this, add lines to your `.npmrc` file. Replace `my-org` with the namespace or group that owns your project's repository, | ||||
| To do this, add lines to your `.npmrc` file. Replace `@my-other-org` with the namespace or group that owns your project's repository, | ||||
| and use your organization's URL. The name is case-sensitive and must match the name of your group or namespace exactly. | ||||
| 
 | ||||
| Use environment variables to set up your tokens: `export MY_TOKEN="<your token>"`. | ||||
| 
 | ||||
| ```shell | ||||
| @foo:registry=https://gitlab.example.com/api/v4/packages/npm/ | ||||
| //gitlab.example.com/api/v4/packages/npm/:_authToken=${MY_TOKEN} | ||||
| //gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/:_authToken=${MY_TOKEN} | ||||
| 
 | ||||
| @my-other-org:registry=https://gitlab.example.com/api/v4/packages/npm/ | ||||
| //gitlab.example.com/api/v4/packages/npm/:_authToken=${MY_TOKEN} | ||||
| //gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/:_authToken=${MY_TOKEN} | ||||
| @scope:registry=https://my_domain_name.com/api/v4/packages/npm/ | ||||
| @my-other-org:registry=https://my_domain_name.example.com/api/v4/packages/npm/ | ||||
| ``` | ||||
| 
 | ||||
| ### npm metadata | ||||
| 
 | ||||
| > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11867) in GitLab 12.6. | ||||
| > - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/221259) from GitLab Premium to GitLab Free in 13.3. | ||||
| > - [Improved](https://gitlab.com/gitlab-org/gitlab/-/issues/330929) in GitLab 14.5. | ||||
| 
 | ||||
| The GitLab Package Registry exposes the following attributes to the npm client. | ||||
| These are similar to the [abbreviated metadata format](https://github.com/npm/registry/blob/9e368cf6aaca608da5b2c378c0d53f475298b916/docs/responses/package-metadata.md#abbreviated-metadata-format): | ||||
| 
 | ||||
|  | @ -417,10 +224,7 @@ These are similar to the [abbreviated metadata format](https://github.com/npm/re | |||
|   - `engines` | ||||
|   - `_hasShrinkwrap` | ||||
| 
 | ||||
| ## Add npm distribution tags | ||||
| 
 | ||||
| > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9425) in GitLab 12.8. | ||||
| > - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/221259) from GitLab Premium to GitLab Free in 13.3. | ||||
| ### Add npm distribution tags | ||||
| 
 | ||||
| You can add [distribution tags](https://docs.npmjs.com/cli/dist-tag/) to newly-published packages. | ||||
| Tags are optional and can be assigned to only one package at a time. | ||||
|  | @ -443,87 +247,46 @@ View [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/258835) for deta | |||
| 
 | ||||
| Due to a bug in npm 6.9.0, deleting distribution tags fails. Make sure your npm version is 6.9.1 or later. | ||||
| 
 | ||||
| ### Supported CLI commands | ||||
| 
 | ||||
| The GitLab npm repository supports the following commands for the npm CLI (`npm`) and yarn CLI | ||||
| (`yarn`): | ||||
| 
 | ||||
| - `npm install`: Install npm packages. | ||||
| - `npm publish`: Publish an npm package to the registry. | ||||
| - `npm dist-tag add`: Add a dist-tag to an npm package. | ||||
| - `npm dist-tag ls`: List dist-tags for a package. | ||||
| - `npm dist-tag rm`: Delete a dist-tag. | ||||
| - `npm ci`: Install npm packages directly from your `package-lock.json` file. | ||||
| - `npm view`: Show package metadata. | ||||
| 
 | ||||
| ## Troubleshooting | ||||
| 
 | ||||
| When troubleshooting npm issues, first run the same command with the `--verbose` flag to confirm | ||||
| what registry you are hitting. | ||||
| 
 | ||||
| To improve performance, npm caches files related to a package. Note that npm doesn't remove data by | ||||
| itself. The cache grows as new packages are installed. If you encounter issues, clear the cache with | ||||
| this command: | ||||
| 
 | ||||
| ```shell | ||||
| npm cache clean --force | ||||
| ``` | ||||
| 
 | ||||
| ### Error running Yarn with the Package Registry for npm registry | ||||
| 
 | ||||
| If you are using [Yarn](https://classic.yarnpkg.com/en/) with the npm registry, you may get | ||||
| an error message like: | ||||
| 
 | ||||
| ```shell | ||||
| yarn install v1.15.2 | ||||
| warning package.json: No license field | ||||
| info No lockfile found. | ||||
| warning XXX: No license field | ||||
| [1/4] 🔍  Resolving packages... | ||||
| [2/4] 🚚  Fetching packages... | ||||
| error An unexpected error occurred: "https://gitlab.example.com/api/v4/projects/XXX/packages/npm/XXX/XXX/-/XXX/XXX-X.X.X.tgz: Request failed \"404 Not Found\"". | ||||
| info If you think this is a bug, please open a bug report with the information provided in "/Users/XXX/gitlab-migration/module-util/yarn-error.log". | ||||
| info Visit https://classic.yarnpkg.com/en/docs/cli/install for documentation about this command | ||||
| ``` | ||||
| 
 | ||||
| In this case, try adding this to your `.npmrc` file (and replace `<your_token>` | ||||
| with your personal access token or deploy token): | ||||
| 
 | ||||
| ```plaintext | ||||
| //gitlab.example.com/api/v4/projects/:_authToken=<your_token> | ||||
| ``` | ||||
| 
 | ||||
| You can also use `yarn config` instead of `npm config` when setting your auth-token dynamically: | ||||
| 
 | ||||
| ```shell | ||||
| yarn config set '//gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/:_authToken' "<your_token>" | ||||
| yarn config set '//gitlab.example.com/api/v4/packages/npm/:_authToken' "<your_token>" | ||||
| ``` | ||||
| 
 | ||||
| ### `npm publish` targets default npm registry (`registry.npmjs.org`) | ||||
| 
 | ||||
| Ensure that your package scope is set consistently in your `package.json` and `.npmrc` files. | ||||
| 
 | ||||
| For example, if your project name in GitLab is `foo/my-package`, then your `package.json` file | ||||
| For example, if your project name in GitLab is `@scope/my-package`, then your `package.json` file | ||||
| should look like: | ||||
| 
 | ||||
| ```json | ||||
| { | ||||
|   "name": "@foo/my-package", | ||||
|   "version": "1.0.0", | ||||
|   "description": "Example package for GitLab npm registry" | ||||
|   "name": "@scope/my-package" | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| And the `.npmrc` file should look like: | ||||
| 
 | ||||
| ```ini | ||||
| //gitlab.example.com/api/v4/projects/<your_project_id>/packages/npm/:_authToken=<your_token> | ||||
| //gitlab.example.com/api/v4/packages/npm/:_authToken=<your_token> | ||||
| @foo:registry=https://gitlab.example.com/api/v4/packages/npm/ | ||||
| ``` | ||||
| 
 | ||||
| ### `npm install` returns `Error: Failed to replace env in config: ${npm_TOKEN}` | ||||
| 
 | ||||
| You do not need a token to run `npm install` unless your project is private. The token is only required to publish. If the `.npmrc` file was checked in with a reference to `$npm_TOKEN`, you can remove it. If you prefer to leave the reference in, you must set a value prior to running `npm install` or set the value by using [GitLab CI/CD variables](../../../ci/variables/index.md): | ||||
| 
 | ||||
| ```shell | ||||
| NPM_TOKEN=<your_token> npm install | ||||
| @scope:registry=https://your_domain_name/api/v4/projects/your_project_id/packages/npm/ | ||||
| //your_domain_name/api/v4/projects/your_project_id/packages/npm/:_authToken="${NPM_TOKEN}" | ||||
| ``` | ||||
| 
 | ||||
| ### `npm install` returns `npm ERR! 403 Forbidden` | ||||
| 
 | ||||
| If you get this error, ensure that: | ||||
| 
 | ||||
| - The Package Registry is enabled in your project settings. Although the Package Registry is enabled | ||||
|   by default, it's possible to [disable it](../package_registry/index.md#disable-the-package-registry). | ||||
| - The Package Registry is enabled in your project settings. Although the Package Registry is enabled by default, it's possible to [disable it](../package_registry/index.md#disable-the-package-registry). | ||||
| - Your token is not expired and has appropriate permissions. | ||||
| - A package with the same name or version doesn't already exist within the given scope. | ||||
| - The scoped packages URL includes a trailing slash: | ||||
|  | @ -534,30 +297,25 @@ If you get this error, ensure that: | |||
| 
 | ||||
| If you get this error, one of the following problems could be causing it. | ||||
| 
 | ||||
| #### Package name does not meet the naming convention | ||||
| ### Package name does not meet the naming convention | ||||
| 
 | ||||
| Your package name may not meet the | ||||
| [`@scope/package-name` package naming convention](#package-naming-convention). | ||||
| Your package name may not meet the [`@scope/package-name` package naming convention](#naming-convention). | ||||
| 
 | ||||
| Ensure the name meets the convention exactly, including the case. | ||||
| Then try to publish again. | ||||
| Ensure the name meets the convention exactly, including the case. Then try to publish again. | ||||
| 
 | ||||
| #### Package already exists | ||||
| ### Package already exists | ||||
| 
 | ||||
| Your package has already been published to another project in the same | ||||
| root namespace and therefore cannot be published again using the same name. | ||||
| Your package has already been published to another project in the same root namespace and therefore cannot be published again using the same name. | ||||
| 
 | ||||
| This is also true even if the prior published package shares the same name, | ||||
| but not the version. | ||||
| This is also true even if the prior published package shares the same name, but not the version. | ||||
| 
 | ||||
| #### Package JSON file is too large | ||||
| ### Package JSON file is too large | ||||
| 
 | ||||
| Make sure that your `package.json` file does not [exceed `20,000` characters](#packagejson-limitations). | ||||
| Make sure that your `package.json` file does not exceed `20,000` characters. | ||||
| 
 | ||||
| ### `npm publish` returns `npm ERR! 500 Internal Server Error - PUT` | ||||
| 
 | ||||
| This is a [known issue](https://gitlab.com/gitlab-org/gitlab/-/issues/238950) in GitLab | ||||
| 13.3.x and later. The error in the logs will appear as: | ||||
| This is a [known issue](https://gitlab.com/gitlab-org/gitlab/-/issues/238950) in GitLab 13.3.x and later. The error in the logs will appear as: | ||||
| 
 | ||||
| ```plaintext | ||||
| >NoMethodError - undefined method `preferred_language' for #<Rack::Response | ||||
|  | @ -576,18 +334,3 @@ This is usually a permissions issue with either: | |||
|   is used. | ||||
| 
 | ||||
| In the latter case, ensure the bucket exists and GitLab has write access to it. | ||||
| 
 | ||||
| ## Supported CLI commands | ||||
| 
 | ||||
| The GitLab npm repository supports the following commands for the npm CLI (`npm`) and yarn CLI | ||||
| (`yarn`): | ||||
| 
 | ||||
| - `npm install`: Install npm packages. | ||||
| - `npm publish`: Publish an npm package to the registry. | ||||
| - `npm dist-tag add`: Add a dist-tag to an npm package. | ||||
| - `npm dist-tag ls`: List dist-tags for a package. | ||||
| - `npm dist-tag rm`: Delete a dist-tag. | ||||
| - `npm ci`: Install npm packages directly from your `package-lock.json` file. | ||||
| - `npm view`: Show package metadata. | ||||
| - `yarn add`: Install an npm package. | ||||
| - `yarn update`: Update your dependencies. | ||||
|  |  | |||
|  | @ -77,7 +77,7 @@ Learn more about using the GitLab Package Registry with CI/CD: | |||
| - [Conan](../conan_repository/index.md#publish-a-conan-package-by-using-cicd) | ||||
| - [Generic](../generic_packages/index.md#publish-a-generic-package-by-using-cicd) | ||||
| - [Maven](../maven_repository/index.md#create-maven-packages-with-gitlab-cicd) | ||||
| - [npm](../npm_registry/index.md#publish-an-npm-package-by-using-cicd) | ||||
| - [npm](../npm_registry/index.md#publishing-a-package-via-a-cicd-pipeline) | ||||
| - [NuGet](../nuget_repository/index.md#publish-a-nuget-package-by-using-cicd) | ||||
| - [PyPI](../pypi_repository/index.md#authenticate-with-a-ci-job-token) | ||||
| - [RubyGems](../rubygems_registry/index.md#authenticate-with-a-ci-job-token) | ||||
|  | @ -143,19 +143,19 @@ table's **Status** column. | |||
| 
 | ||||
| The Package Registry supports the following formats: | ||||
| 
 | ||||
| | Package type | GitLab version | Status | | ||||
| | ------------ | -------------- |------- | | ||||
| | [Maven](../maven_repository/index.md) | 11.3+ | GA | | ||||
| | [npm](../npm_registry/index.md) | 11.7+ | GA | | ||||
| | [NuGet](../nuget_repository/index.md) | 12.8+ | GA | | ||||
| | [PyPI](../pypi_repository/index.md) | 12.10+ | GA | | ||||
| | [Generic packages](../generic_packages/index.md) | 13.5+ | GA | | ||||
| | [Composer](../composer_repository/index.md) | 13.2+ | [Beta](https://gitlab.com/groups/gitlab-org/-/epics/6817) | | ||||
| | [Conan](../conan_repository/index.md) | 12.6+ | [Beta](https://gitlab.com/groups/gitlab-org/-/epics/6816) | | ||||
| | [Helm](../helm_repository/index.md) | 14.1+ | [Beta](https://gitlab.com/groups/gitlab-org/-/epics/6366) | | ||||
| | [Debian](../debian_repository/index.md) | 14.2+ | [Alpha](https://gitlab.com/groups/gitlab-org/-/epics/6057) | | ||||
| | [Go](../go_proxy/index.md) | 13.1+ | [Alpha](https://gitlab.com/groups/gitlab-org/-/epics/3043) | | ||||
| | [Ruby gems](../rubygems_registry/index.md) | 13.10+ | [Alpha](https://gitlab.com/groups/gitlab-org/-/epics/3200) | | ||||
| | Package type                                     | GitLab version | Status                                                     | | ||||
| | ------------------------------------------------ | -------------- | ---------------------------------------------------------- | | ||||
| | [Maven](../maven_repository/index.md)            | 11.3+          | GA                                                         | | ||||
| | [npm](../npm_registry/index.md)                  | 11.7+          | GA                                                         | | ||||
| | [NuGet](../nuget_repository/index.md)            | 12.8+          | GA                                                         | | ||||
| | [PyPI](../pypi_repository/index.md)              | 12.10+         | GA                                                         | | ||||
| | [Generic packages](../generic_packages/index.md) | 13.5+          | GA                                                         | | ||||
| | [Composer](../composer_repository/index.md)      | 13.2+          | [Beta](https://gitlab.com/groups/gitlab-org/-/epics/6817)  | | ||||
| | [Conan](../conan_repository/index.md)            | 12.6+          | [Beta](https://gitlab.com/groups/gitlab-org/-/epics/6816)  | | ||||
| | [Helm](../helm_repository/index.md)              | 14.1+          | [Beta](https://gitlab.com/groups/gitlab-org/-/epics/6366)  | | ||||
| | [Debian](../debian_repository/index.md)          | 14.2+          | [Alpha](https://gitlab.com/groups/gitlab-org/-/epics/6057) | | ||||
| | [Go](../go_proxy/index.md)                       | 13.1+          | [Alpha](https://gitlab.com/groups/gitlab-org/-/epics/3043) | | ||||
| | [Ruby gems](../rubygems_registry/index.md)       | 13.10+         | [Alpha](https://gitlab.com/groups/gitlab-org/-/epics/3200) | | ||||
| 
 | ||||
| [Status](../../../policy/alpha-beta-support.md): | ||||
| 
 | ||||
|  | @ -173,8 +173,8 @@ guides you through the process. | |||
| 
 | ||||
| <!-- vale gitlab.Spelling = NO --> | ||||
| 
 | ||||
| | Format | Status | | ||||
| | ------ | ------ | | ||||
| | Format    | Status                                                        | | ||||
| | --------- | ------------------------------------------------------------- | | ||||
| | Chef      | [#36889](https://gitlab.com/gitlab-org/gitlab/-/issues/36889) | | ||||
| | CocoaPods | [#36890](https://gitlab.com/gitlab-org/gitlab/-/issues/36890) | | ||||
| | Conda     | [#36891](https://gitlab.com/gitlab-org/gitlab/-/issues/36891) | | ||||
|  |  | |||
|  | @ -302,7 +302,7 @@ The npm version is shown in the output: | |||
|    ``` | ||||
| 
 | ||||
| 1. Enter responses to the questions. Ensure the **package name** follows | ||||
|    the [naming convention](../npm_registry/index.md#package-naming-convention) and is scoped to the project or group where the registry exists. | ||||
|    the [naming convention](../npm_registry/index.md#naming-convention) and is scoped to the project or group where the registry exists. | ||||
| 
 | ||||
| ## Yarn | ||||
| 
 | ||||
|  | @ -334,7 +334,7 @@ The Yarn version is shown in the output: | |||
|    ``` | ||||
| 
 | ||||
| 1. Enter responses to the questions. Ensure the **package name** follows | ||||
|    the [naming convention](../npm_registry/index.md#package-naming-convention) and is scoped to the | ||||
|    the [naming convention](../npm_registry/index.md#naming-convention) and is scoped to the | ||||
|    project or group where the registry exists. | ||||
| 
 | ||||
| A `package.json` file is created. | ||||
|  |  | |||
|  | @ -50,9 +50,9 @@ If you're using npm, create an `.npmrc` file. Add the appropriate URL for publis | |||
| packages to your project. Finally, add a section to your `package.json` file. | ||||
| 
 | ||||
| Follow the instructions in the | ||||
| [GitLab Package Registry npm documentation](../npm_registry/index.md#authenticate-to-the-package-registry). After | ||||
| [GitLab Package Registry npm documentation](../npm_registry/index.md#authentication-to-the-package-registry). After | ||||
| you do this, you can publish your npm package to your project using `npm publish`, as described in the | ||||
| [publishing packages](../npm_registry/index.md#publish-an-npm-package) section. | ||||
| [publishing packages](../npm_registry/index.md#publish-to-gitlab-package-registry) section. | ||||
| 
 | ||||
| ### Maven | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| --- | ||||
| stage: Manage | ||||
| group: Workspace | ||||
| info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments" | ||||
| info: 'To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments' | ||||
| type: reference, index, howto | ||||
| --- | ||||
| 
 | ||||
|  | @ -75,41 +75,44 @@ To configure visibility, features, and permissions for a project: | |||
| 
 | ||||
| Use the toggles to enable or disable features in the project. | ||||
| 
 | ||||
| | Option                           | More access limit options | Description   | | ||||
| |:---------------------------------|:--------------------------|:--------------| | ||||
| | **Issues**                       | ✓                         | Activates the GitLab issues tracker. | | ||||
| | **Repository**                   | ✓                         | Enables [repository](../repository/index.md) functionality | | ||||
| | Option                           | More access limit options | Description                                                                                                                                            | | ||||
| | :------------------------------- | :------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------- | | ||||
| | **Issues**                       | ✓                         | Activates the GitLab issues tracker.                                                                                                                   | | ||||
| | **Repository**                   | ✓                         | Enables [repository](../repository/index.md) functionality                                                                                             | | ||||
| | **Merge requests**               | ✓                         | Enables [merge request](../merge_requests/index.md) functionality; also see [Merge request settings](#configure-merge-request-settings-for-a-project). | | ||||
| | **Forks**                        | ✓                         | Enables [forking](../repository/forking_workflow.md) functionality. | | ||||
| | **Git Large File Storage (LFS)** |                           | Enables the use of [large files](../../../topics/git/lfs/index.md#git-large-file-storage-lfs). | | ||||
| | **Packages**                     |                           | Supports configuration of a [package registry](../../../administration/packages/index.md#gitlab-package-registry-administration) functionality. | | ||||
| | **CI/CD**                        | ✓                         | Enables [CI/CD](../../../ci/index.md) functionality. | | ||||
| | **Container Registry**           |                           | Activates a [registry](../../packages/container_registry/index.md) for your Docker images. | | ||||
| | **Analytics**                    | ✓                         | Enables [analytics](../../analytics/index.md). | | ||||
| | **Requirements**                 | ✓                         | Control access to [Requirements Management](../requirements/index.md). | | ||||
| | **Security & Compliance**        | ✓                         | Control access to [security features](../../application_security/index.md). | | ||||
| | **Wiki**                         | ✓                         | Enables a separate system for [documentation](../wiki/index.md). | | ||||
| | **Snippets**                     | ✓                         | Enables [sharing of code and text](../../snippets.md). | | ||||
| | **Pages**                        | ✓                         | Allows you to [publish static websites](../pages/index.md). | | ||||
| | **Metrics Dashboard**            | ✓                         | Control access to [metrics dashboard](../integrations/prometheus.md).                                                                                                                                                                    | | ||||
| | **Releases**                     | ✓                         | Control access to [Releases](../releases/index.md).                                                                                                                                                                                      | | ||||
| | **Forks**                        | ✓                         | Enables [forking](../repository/forking_workflow.md) functionality.                                                                                    | | ||||
| | **Git Large File Storage (LFS)** |                           | Enables the use of [large files](../../../topics/git/lfs/index.md#git-large-file-storage-lfs).                                                         | | ||||
| | **Packages**                     |                           | Supports configuration of a [package registry](../../../administration/packages/index.md#gitlab-package-registry-administration) functionality.        | | ||||
| | **CI/CD**                        | ✓                         | Enables [CI/CD](../../../ci/index.md) functionality.                                                                                                   | | ||||
| | **Container Registry**           |                           | Activates a [registry](../../packages/container_registry/index.md) for your Docker images.                                                             | | ||||
| | **Analytics**                    | ✓                         | Enables [analytics](../../analytics/index.md).                                                                                                         | | ||||
| | **Requirements**                 | ✓                         | Control access to [Requirements Management](../requirements/index.md).                                                                                 | | ||||
| | **Security & Compliance**        | ✓                         | Control access to [security features](../../application_security/index.md).                                                                            | | ||||
| | **Wiki**                         | ✓                         | Enables a separate system for [documentation](../wiki/index.md).                                                                                       | | ||||
| | **Snippets**                     | ✓                         | Enables [sharing of code and text](../../snippets.md).                                                                                                 | | ||||
| | **Pages**                        | ✓                         | Allows you to [publish static websites](../pages/index.md).                                                                                            | | ||||
| | **Metrics Dashboard**            | ✓                         | Control access to [metrics dashboard](../integrations/prometheus.md).                                                                                  | | ||||
| | **Releases**                     | ✓                         | Control access to [Releases](../releases/index.md).                                                                                                    | | ||||
| | **Environments**                 | ✓                         | Control access to [Environments and Deployments](../../../ci/environments/index.md).                                                                   | | ||||
| | **Feature flags**                | ✓                         | Control access to [Feature Flags](../../../operations/feature_flags.md).                                                                               | | ||||
| | **Monitor**                      | ✓                         | Control access to [Monitor](../../../operations/index.md) features.                                                                                            | | ||||
| | **Infrastructure**               | ✓                         | Control access to [Infrastructure](../../infrastructure/index.md) features.                                                                                                    | | ||||
| | **Monitor**                      | ✓                         | Control access to [Monitor](../../../operations/index.md) features.                                                                                    | | ||||
| | **Infrastructure**               | ✓                         | Control access to [Infrastructure](../../infrastructure/index.md) features.                                                                            | | ||||
| 
 | ||||
| When you disable a feature, the following additional features are also disabled: | ||||
| 
 | ||||
| - If you disable the **Issues** feature, project users cannot use: | ||||
| 
 | ||||
|   - **Issue Boards** | ||||
|   - **Service Desk** | ||||
|   - Project users can still access **Milestones** from merge requests. | ||||
| 
 | ||||
| - If you disable **Issues** and **Merge Requests**, project users cannot use: | ||||
| 
 | ||||
|   - **Labels** | ||||
|   - **Milestones** | ||||
| 
 | ||||
| - If you disable **Repository**, project users cannot access: | ||||
| 
 | ||||
|   - **Merge requests** | ||||
|   - **CI/CD** | ||||
|   - **Container Registry** | ||||
|  | @ -237,9 +240,7 @@ Prerequisites: | |||
| - You must be the Owner of the project you transfer. | ||||
| - The group must allow creation of new projects. | ||||
| - The project must not contain any [container images](../../packages/container_registry/index.md#known-issues). | ||||
|   - If you transfer a project to a different root namespace, | ||||
|     the project must not contain any | ||||
|     [NPM packages](../../packages/npm_registry/index.md#limitations). | ||||
| - Remove any npm packages. If you transfer a project to a different root namespace, the project must not contain any npm packages. When you update the path of a user or group, or transfer a subgroup or project, you must remove any npm packages first. You cannot update the root namespace of a project with npm packages. Make sure you update your .npmrc files to follow the naming convention and run npm publish if necessary. | ||||
| 
 | ||||
| To transfer a project: | ||||
| 
 | ||||
|  | @ -262,7 +263,7 @@ When you transfer a project from a namespace licensed for GitLab SaaS Premium or | |||
| 
 | ||||
| - [Project access tokens](../../../user/project/settings/project_access_tokens.md) are revoked | ||||
| - [Pipeline subscriptions](../../../ci/pipelines/index.md#trigger-a-pipeline-when-an-upstream-project-is-rebuilt) | ||||
| and [test cases](../../../ci/test_cases/index.md) are deleted. | ||||
|   and [test cases](../../../ci/test_cases/index.md) are deleted. | ||||
| 
 | ||||
| ## Delete a project | ||||
| 
 | ||||
|  |  | |||
|  | @ -63,6 +63,7 @@ module.exports = (path, options = {}) => { | |||
|     '^jest/(.*)$': '<rootDir>/spec/frontend/$1', | ||||
|     '^ee_else_ce_jest/(.*)$': '<rootDir>/spec/frontend/$1', | ||||
|     '^jquery$': '<rootDir>/node_modules/jquery/dist/jquery.slim.js', | ||||
|     '^@sentry/browser$': '<rootDir>/app/assets/javascripts/sentry/sentry_browser_wrapper.js', | ||||
|     ...extModuleNameMapper, | ||||
|   }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -184,6 +184,7 @@ module API | |||
|         mount ::API::BroadcastMessages | ||||
|         mount ::API::BulkImports | ||||
|         mount ::API::Ci::JobArtifacts | ||||
|         mount ::API::Groups | ||||
|         mount ::API::Ci::Jobs | ||||
|         mount ::API::Ci::ResourceGroups | ||||
|         mount ::API::Ci::Runner | ||||
|  | @ -198,6 +199,7 @@ module API | |||
|         mount ::API::Commits | ||||
|         mount ::API::CommitStatuses | ||||
|         mount ::API::ContainerRegistryEvent | ||||
|         mount ::API::ContainerRepositories | ||||
|         mount ::API::DependencyProxy | ||||
|         mount ::API::DeployKeys | ||||
|         mount ::API::DeployTokens | ||||
|  | @ -230,6 +232,7 @@ module API | |||
|         mount ::API::Keys | ||||
|         mount ::API::Lint | ||||
|         mount ::API::Markdown | ||||
|         mount ::API::Members | ||||
|         mount ::API::MergeRequestApprovals | ||||
|         mount ::API::MergeRequests | ||||
|         mount ::API::MergeRequestDiffs | ||||
|  | @ -294,7 +297,6 @@ module API | |||
|       mount ::API::ComposerPackages | ||||
|       mount ::API::ConanInstancePackages | ||||
|       mount ::API::ConanProjectPackages | ||||
|       mount ::API::ContainerRepositories | ||||
|       mount ::API::DebianGroupPackages | ||||
|       mount ::API::DebianProjectPackages | ||||
|       mount ::API::Discussions | ||||
|  | @ -304,11 +306,9 @@ module API | |||
|       mount ::API::GroupDebianDistributions | ||||
|       mount ::API::GroupLabels | ||||
|       mount ::API::GroupMilestones | ||||
|       mount ::API::Groups | ||||
|       mount ::API::Issues | ||||
|       mount ::API::Labels | ||||
|       mount ::API::MavenPackages | ||||
|       mount ::API::Members | ||||
|       mount ::API::Notes | ||||
|       mount ::API::NotificationSettings | ||||
|       mount ::API::NpmInstancePackages | ||||
|  |  | |||
|  | @ -14,12 +14,17 @@ module API | |||
| 
 | ||||
|     namespace 'registry' do | ||||
|       params do | ||||
|         requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' | ||||
|         requires :id, types: [String, Integer], desc: 'The ID of the repository' | ||||
|       end | ||||
|       resource :repositories, requirements: { id: /[0-9]*/ } do | ||||
|         desc 'Get a container repository' do | ||||
|           detail 'This feature was introduced in GitLab 13.6.' | ||||
|           success Entities::ContainerRegistry::Repository | ||||
|           failure [ | ||||
|             { code: 401, message: 'Unauthorized' }, | ||||
|             { code: 404, message: 'Repository Not Found' } | ||||
|           ] | ||||
|           tags %w[container_registry] | ||||
|         end | ||||
|         params do | ||||
|           optional :tags, type: Boolean, default: false, desc: 'Determines if tags should be included' | ||||
|  |  | |||
|  | @ -195,6 +195,8 @@ module API | |||
| 
 | ||||
|       desc 'Get a groups list' do | ||||
|         success Entities::Group | ||||
|         is_array true | ||||
|         tags %w[groups] | ||||
|       end | ||||
|       params do | ||||
|         use :group_list_params | ||||
|  | @ -207,6 +209,7 @@ module API | |||
| 
 | ||||
|       desc 'Create a group. Available only for users who can create groups.' do | ||||
|         success Entities::Group | ||||
|         tags %w[groups] | ||||
|       end | ||||
|       params do | ||||
|         requires :name, type: String, desc: 'The name of the group' | ||||
|  | @ -240,6 +243,7 @@ module API | |||
|     resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do | ||||
|       desc 'Update a group. Available only for users who can administrate groups.' do | ||||
|         success Entities::Group | ||||
|         tags %w[groups] | ||||
|       end | ||||
|       params do | ||||
|         optional :name, type: String, desc: 'The name of the group' | ||||
|  | @ -265,6 +269,7 @@ module API | |||
| 
 | ||||
|       desc 'Get a single group, with containing projects.' do | ||||
|         success Entities::GroupDetail | ||||
|         tags %w[groups] | ||||
|       end | ||||
|       params do | ||||
|         use :with_custom_attributes | ||||
|  | @ -278,7 +283,9 @@ module API | |||
|         present_group_details(params, group, with_projects: params[:with_projects]) | ||||
|       end | ||||
| 
 | ||||
|       desc 'Remove a group.' | ||||
|       desc 'Remove a group.' do | ||||
|         tags %w[groups] | ||||
|       end | ||||
|       delete ":id", feature_category: :subgroups, urgency: :low do | ||||
|         group = find_group!(params[:id]) | ||||
|         authorize! :admin_group, group | ||||
|  | @ -289,6 +296,8 @@ module API | |||
| 
 | ||||
|       desc 'Get a list of projects in this group.' do | ||||
|         success Entities::Project | ||||
|         is_array true | ||||
|         tags %w[groups] | ||||
|       end | ||||
|       params do | ||||
|         optional :archived, type: Boolean, desc: 'Limit by archived status' | ||||
|  | @ -329,6 +338,8 @@ module API | |||
| 
 | ||||
|       desc 'Get a list of shared projects in this group' do | ||||
|         success Entities::Project | ||||
|         is_array true | ||||
|         tags %w[groups] | ||||
|       end | ||||
|       params do | ||||
|         optional :archived, type: Boolean, desc: 'Limit by archived status' | ||||
|  | @ -357,6 +368,8 @@ module API | |||
| 
 | ||||
|       desc 'Get a list of subgroups in this group.' do | ||||
|         success Entities::Group | ||||
|         is_array true | ||||
|         tags %w[groups] | ||||
|       end | ||||
|       params do | ||||
|         use :group_list_params | ||||
|  | @ -369,6 +382,8 @@ module API | |||
| 
 | ||||
|       desc 'Get a list of descendant groups of this group.' do | ||||
|         success Entities::Group | ||||
|         is_array true | ||||
|         tags %w[groups] | ||||
|       end | ||||
|       params do | ||||
|         use :group_list_params | ||||
|  | @ -382,6 +397,7 @@ module API | |||
| 
 | ||||
|       desc 'Transfer a project to the group namespace. Available only for admin.' do | ||||
|         success Entities::GroupDetail | ||||
|         tags %w[groups] | ||||
|       end | ||||
|       params do | ||||
|         requires :project_id, type: String, desc: 'The ID or path of the project' | ||||
|  | @ -400,7 +416,11 @@ module API | |||
|         end | ||||
|       end | ||||
| 
 | ||||
|       desc 'Get the groups to where the current group can be transferred to' | ||||
|       desc 'Get the groups to where the current group can be transferred to' do | ||||
|         success Entities::Group | ||||
|         is_array true | ||||
|         tags %w[groups] | ||||
|       end | ||||
|       params do | ||||
|         optional :search, type: String, desc: 'Return list of namespaces matching the search criteria' | ||||
|         use :pagination | ||||
|  | @ -415,7 +435,9 @@ module API | |||
|         present_groups params, groups, serializer: Entities::PublicGroupDetails | ||||
|       end | ||||
| 
 | ||||
|       desc 'Transfer a group to a new parent group or promote a subgroup to a root group' | ||||
|       desc 'Transfer a group to a new parent group or promote a subgroup to a root group' do | ||||
|         tags %w[groups] | ||||
|       end | ||||
|       params do | ||||
|         optional :group_id, | ||||
|                  type: Integer, | ||||
|  | @ -440,6 +462,7 @@ module API | |||
| 
 | ||||
|       desc 'Share a group with a group' do | ||||
|         success Entities::GroupDetail | ||||
|         tags %w[groups] | ||||
|       end | ||||
|       params do | ||||
|         requires :group_id, type: Integer, desc: 'The ID of the group to share' | ||||
|  |  | |||
|  | @ -20,6 +20,8 @@ module API | |||
|       resource source_type.pluralize, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do | ||||
|         desc 'Gets a list of group or project members viewable by the authenticated user.' do | ||||
|           success Entities::Member | ||||
|           is_array true | ||||
|           tags %w[members] | ||||
|         end | ||||
|         params do | ||||
|           optional :query, type: String, desc: 'A query string to search for members' | ||||
|  | @ -42,6 +44,8 @@ module API | |||
| 
 | ||||
|         desc 'Gets a list of group or project members viewable by the authenticated user, including those who gained membership through ancestor group.' do | ||||
|           success Entities::Member | ||||
|           is_array true | ||||
|           tags %w[members] | ||||
|         end | ||||
|         params do | ||||
|           optional :query, type: String, desc: 'A query string to search for members' | ||||
|  | @ -63,6 +67,7 @@ module API | |||
| 
 | ||||
|         desc 'Gets a member of a group or project.' do | ||||
|           success Entities::Member | ||||
|           tags %w[members] | ||||
|         end | ||||
|         params do | ||||
|           requires :user_id, type: Integer, desc: 'The user ID of the member' | ||||
|  | @ -82,6 +87,7 @@ module API | |||
| 
 | ||||
|         desc 'Gets a member of a group or project, including those who gained membership through ancestor group' do | ||||
|           success Entities::Member | ||||
|           tags %w[members] | ||||
|         end | ||||
|         params do | ||||
|           requires :user_id, type: Integer, desc: 'The user ID of the member' | ||||
|  | @ -101,6 +107,7 @@ module API | |||
| 
 | ||||
|         desc 'Adds a member to a group or project.' do | ||||
|           success Entities::Member | ||||
|           tags %w[members] | ||||
|         end | ||||
|         params do | ||||
|           requires :access_level, type: Integer, desc: 'A valid access level (defaults: `30`, developer access level)' | ||||
|  | @ -126,6 +133,7 @@ module API | |||
| 
 | ||||
|         desc 'Updates a member of a group or project.' do | ||||
|           success Entities::Member | ||||
|           tags %w[members] | ||||
|         end | ||||
|         params do | ||||
|           requires :user_id, type: Integer, desc: 'The user ID of the new member' | ||||
|  | @ -153,7 +161,9 @@ module API | |||
|         end | ||||
|         # rubocop: enable CodeReuse/ActiveRecord | ||||
| 
 | ||||
|         desc 'Removes a user from a group or project.' | ||||
|         desc 'Removes a user from a group or project.' do | ||||
|           tags %w[members] | ||||
|         end | ||||
|         params do | ||||
|           requires :user_id, type: Integer, desc: 'The user ID of the member' | ||||
|           optional :skip_subresources, type: Boolean, default: false, | ||||
|  |  | |||
|  | @ -43,7 +43,10 @@ module Gitlab | |||
| 
 | ||||
|         allow_websocket_connections(directives) | ||||
|         allow_cdn(directives, Settings.gitlab.cdn_host) if Settings.gitlab.cdn_host.present? | ||||
|         allow_sentry(directives) if Gitlab.config.sentry&.enabled && Gitlab.config.sentry&.clientside_dsn | ||||
|         # Support for Sentry setup via configuration files will be removed in 16.0 | ||||
|         # in favor of Gitlab::CurrentSettings. | ||||
|         allow_legacy_sentry(directives) if Gitlab.config.sentry&.enabled && Gitlab.config.sentry&.clientside_dsn | ||||
|         allow_sentry(directives) if Gitlab::CurrentSettings.try(:sentry_enabled) && Gitlab::CurrentSettings.try(:sentry_clientside_dsn) | ||||
|         allow_framed_gitlab_paths(directives) | ||||
|         allow_customersdot(directives) if ENV['CUSTOMER_PORTAL_URL'].present? | ||||
|         allow_review_apps(directives) if ENV['REVIEW_APPS_ENABLED'] | ||||
|  | @ -135,13 +138,22 @@ module Gitlab | |||
|         append_to_directive(directives, 'frame_src', customersdot_host) | ||||
|       end | ||||
| 
 | ||||
|       def self.allow_sentry(directives) | ||||
|       def self.allow_legacy_sentry(directives) | ||||
|         # Support for Sentry setup via configuration files will be removed in 16.0 | ||||
|         # in favor of Gitlab::CurrentSettings. | ||||
|         sentry_dsn = Gitlab.config.sentry.clientside_dsn | ||||
|         sentry_uri = URI(sentry_dsn) | ||||
| 
 | ||||
|         append_to_directive(directives, 'connect_src', "#{sentry_uri.scheme}://#{sentry_uri.host}") | ||||
|       end | ||||
| 
 | ||||
|       def self.allow_sentry(directives) | ||||
|         sentry_dsn = Gitlab::CurrentSettings.sentry_clientside_dsn | ||||
|         sentry_uri = URI(sentry_dsn) | ||||
| 
 | ||||
|         append_to_directive(directives, 'connect_src', "#{sentry_uri.scheme}://#{sentry_uri.host}") | ||||
|       end | ||||
| 
 | ||||
|       def self.allow_letter_opener(directives) | ||||
|         append_to_directive(directives, 'frame_src', Gitlab::Utils.append_path(Gitlab.config.gitlab.url, '/rails/letter_opener/')) | ||||
|       end | ||||
|  |  | |||
|  | @ -14,41 +14,22 @@ module Gitlab | |||
| 
 | ||||
|       STATS_DEFAULT_FORMAT = :json | ||||
| 
 | ||||
|       FILENAME_PREFIX = 'jemalloc_stats' | ||||
| 
 | ||||
|       # Return jemalloc stats as a string. | ||||
|       def stats(format: STATS_DEFAULT_FORMAT) | ||||
|         verify_format!(format) | ||||
| 
 | ||||
|         with_malloc_stats_print do |stats_print| | ||||
|           StringIO.new.tap { |io| write_stats(stats_print, io, STATS_FORMATS[format]) }.string | ||||
|         end | ||||
|         dump_stats(StringIO.new, format: format).string | ||||
|       end | ||||
| 
 | ||||
|       # Write jemalloc stats to the given directory | ||||
|       # @param [String] path Directory path the dump will be put into | ||||
|       # @param [String] tmp_dir Directory path the dump will be streaming to. It is moved to `path` when finished. | ||||
|       # @param [String] format `json` or `txt` | ||||
|       # @param [String] filename_label Optional custom string that will be injected into the file name, e.g. `worker_0` | ||||
|       # @return [String] Full path to the resulting dump file | ||||
|       def dump_stats(path:, tmp_dir: Dir.tmpdir, format: STATS_DEFAULT_FORMAT, filename_label: nil) | ||||
|       # Streams jemalloc stats to the given IO object. | ||||
|       def dump_stats(io, format: STATS_DEFAULT_FORMAT) | ||||
|         verify_format!(format) | ||||
| 
 | ||||
|         format_settings = STATS_FORMATS[format] | ||||
|         tmp_file_path = File.join(tmp_dir, file_name(format_settings[:extension], filename_label)) | ||||
|         file_path = File.join(path, file_name(format_settings[:extension], filename_label)) | ||||
| 
 | ||||
|         with_malloc_stats_print do |stats_print| | ||||
|           File.open(tmp_file_path, 'wb') do |io| | ||||
|             write_stats(stats_print, io, format_settings) | ||||
|           end | ||||
|           write_stats(stats_print, io, format_settings) | ||||
|         end | ||||
| 
 | ||||
|         # On OSX, `with_malloc_stats_print` is no-op, and, as result, no file will be written | ||||
|         return unless File.exist?(tmp_file_path) | ||||
| 
 | ||||
|         FileUtils.mv(tmp_file_path, file_path) | ||||
|         file_path | ||||
|         io | ||||
|       end | ||||
| 
 | ||||
|       private | ||||
|  | @ -95,12 +76,6 @@ module Gitlab | |||
| 
 | ||||
|         stats_print.call(callback, nil, format[:options]) | ||||
|       end | ||||
| 
 | ||||
|       def file_name(extension, filename_label) | ||||
|         timestamp = Time.current.strftime('%Y-%m-%d.%H:%M:%S:%L') | ||||
| 
 | ||||
|         [FILENAME_PREFIX, timestamp, filename_label, extension].reject(&:blank?).join('.') | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -3,47 +3,91 @@ | |||
| module Gitlab | ||||
|   module Memory | ||||
|     class Reporter | ||||
|       def initialize | ||||
|       attr_reader :reports_path | ||||
| 
 | ||||
|       def initialize(reports_path: nil, logger: Gitlab::AppLogger) | ||||
|         @reports_path = reports_path || ENV["GITLAB_DIAGNOSTIC_REPORTS_PATH"] || Dir.mktmpdir | ||||
|         @logger = logger | ||||
| 
 | ||||
|         @worker_id = ::Prometheus::PidProvider.worker_id | ||||
|         @worker_uuid = SecureRandom.uuid | ||||
| 
 | ||||
|         init_prometheus_metrics | ||||
|       end | ||||
| 
 | ||||
|       def run_report(report) | ||||
|         @logger.info( | ||||
|           log_labels( | ||||
|             message: 'started', | ||||
|             perf_report: report.name | ||||
|           )) | ||||
| 
 | ||||
|         start_monotonic_time = Gitlab::Metrics::System.monotonic_time | ||||
|         start_thread_cpu_time = Gitlab::Metrics::System.thread_cpu_time | ||||
| 
 | ||||
|         file_path = report.run(report_id) | ||||
|         report_file = store_report(report) | ||||
| 
 | ||||
|         cpu_s = Gitlab::Metrics::System.thread_cpu_duration(start_thread_cpu_time) | ||||
|         duration_s = Gitlab::Metrics::System.monotonic_time - start_monotonic_time | ||||
| 
 | ||||
|         log_report(name: report.name, cpu_s: cpu_s, duration_s: duration_s, size: file_size(file_path)) | ||||
|         @logger.info( | ||||
|           log_labels( | ||||
|             message: 'finished', | ||||
|             perf_report: report.name, | ||||
|             cpu_s: cpu_s.round(2), | ||||
|             duration_s: duration_s.round(2), | ||||
|             perf_report_file: report_file, | ||||
|             perf_report_size_bytes: file_size(report_file) | ||||
|           )) | ||||
| 
 | ||||
|         @report_duration_counter.increment({ report: report.name }, duration_s) | ||||
| 
 | ||||
|         true | ||||
|       rescue StandardError => e | ||||
|         @logger.error( | ||||
|           log_labels( | ||||
|             message: 'failed', | ||||
|             perf_report: report.name, | ||||
|             error: e.inspect | ||||
|           )) | ||||
| 
 | ||||
|         false | ||||
|       end | ||||
| 
 | ||||
|       private | ||||
| 
 | ||||
|       def log_report(name:, duration_s:, cpu_s:, size:) | ||||
|         Gitlab::AppLogger.info( | ||||
|           message: 'finished', | ||||
|       def store_report(report) | ||||
|         # Store report in tmp subdir while it is still streaming. | ||||
|         # This will clearly separate finished reports from the files we are still writing to. | ||||
|         tmp_dir = File.join(@reports_path, 'tmp') | ||||
|         FileUtils.mkdir_p(tmp_dir) | ||||
| 
 | ||||
|         report_file = file_name(report) | ||||
|         tmp_file_path = File.join(tmp_dir, report_file) | ||||
| 
 | ||||
|         File.open(tmp_file_path, 'wb') do |io| | ||||
|           report.run(io) | ||||
|         end | ||||
| 
 | ||||
|         File.join(@reports_path, report_file).tap do |report_file_path| | ||||
|           FileUtils.mv(tmp_file_path, report_file_path) | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       def log_labels(**extra_labels) | ||||
|         { | ||||
|           pid: $$, | ||||
|           worker_id: worker_id, | ||||
|           perf_report: name, | ||||
|           duration_s: duration_s.round(2), | ||||
|           cpu_s: cpu_s.round(2), | ||||
|           perf_report_size_bytes: size, | ||||
|           worker_id: @worker_id, | ||||
|           perf_report_worker_uuid: @worker_uuid | ||||
|         ) | ||||
|         }.merge(extra_labels) | ||||
|       end | ||||
| 
 | ||||
|       def report_id | ||||
|         [worker_id, @worker_uuid].join(".") | ||||
|       end | ||||
|       def file_name(report) | ||||
|         timestamp = Time.current.strftime('%Y-%m-%d.%H:%M:%S:%L') | ||||
| 
 | ||||
|       def worker_id | ||||
|         ::Prometheus::PidProvider.worker_id | ||||
|         report_id = [@worker_id, @worker_uuid].join(".") | ||||
| 
 | ||||
|         [report.name, timestamp, report_id].reject(&:blank?).join('.') | ||||
|       end | ||||
| 
 | ||||
|       def file_size(file_path) | ||||
|  | @ -53,7 +97,7 @@ module Gitlab | |||
|       end | ||||
| 
 | ||||
|       def init_prometheus_metrics | ||||
|         default_labels = { pid: worker_id } | ||||
|         default_labels = { pid: @worker_id } | ||||
| 
 | ||||
|         @report_duration_counter = Gitlab::Metrics.counter( | ||||
|           :gitlab_diag_report_duration_seconds_total, | ||||
|  |  | |||
|  | @ -6,38 +6,24 @@ module Gitlab | |||
|       class HeapDump | ||||
|         class << self | ||||
|           def enqueue! | ||||
|             log_event('enqueue') | ||||
|             @write_heap_dump = true | ||||
|           end | ||||
| 
 | ||||
|           # This is a no-op currently and will be implemented at a later time in | ||||
|           # https://gitlab.com/gitlab-org/gitlab/-/issues/370077 | ||||
|           def write_conditionally | ||||
|             return false unless enqueued? | ||||
| 
 | ||||
|             log_event('write') | ||||
| 
 | ||||
|             true | ||||
|           end | ||||
| 
 | ||||
|           private | ||||
| 
 | ||||
|           def enqueued? | ||||
|             !!@write_heap_dump | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|           def log_event(message) | ||||
|             Gitlab::AppLogger.info( | ||||
|               message: message, | ||||
|               pid: $$, | ||||
|               worker_id: worker_id, | ||||
|               perf_report: 'heap_dump' | ||||
|             ) | ||||
|           end | ||||
|         def name | ||||
|           'heap_dump' | ||||
|         end | ||||
| 
 | ||||
|           def worker_id | ||||
|             ::Prometheus::PidProvider.worker_id | ||||
|           end | ||||
|         # This is a no-op currently and will be implemented at a later time in | ||||
|         # https://gitlab.com/gitlab-org/gitlab/-/issues/370077 | ||||
|         def run(writer) | ||||
|           return false unless self.class.enqueued? | ||||
| 
 | ||||
|           true | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|  |  | |||
|  | @ -4,72 +4,19 @@ module Gitlab | |||
|   module Memory | ||||
|     module Reports | ||||
|       class JemallocStats | ||||
|         # On prod, Jemalloc reports sizes were ~2.5 MB: | ||||
|         # https://gitlab.com/gitlab-com/gl-infra/reliability/-/issues/15993#note_1014767214 | ||||
|         # We configured 1GB emptyDir per pod: | ||||
|         # https://gitlab.com/gitlab-com/gl-infra/k8s-workloads/gitlab-com/-/merge_requests/1949 | ||||
|         # The pod will be evicted when the size limit is exceeded. We never want this to happen, for availability. | ||||
|         # | ||||
|         # With the default, we have a headroom (250*2.5MB=625<1000 MB) to fit into configured emptyDir. | ||||
|         # It would allow us to keep 3+ days worth of reports for 6 workers running every 2 hours: 3*6*12=216<250 | ||||
|         # | ||||
|         # The cleanup logic will be redundant after we'll implement the uploads, which would perform the cleanup. | ||||
|         DEFAULT_MAX_REPORTS_STORED = 250 | ||||
| 
 | ||||
|         def initialize(reports_path:) | ||||
|           @reports_path = reports_path | ||||
| 
 | ||||
|           # Store report in tmp subdir while it is still streaming. | ||||
|           # This will clearly separate finished reports from the files we are still writing to. | ||||
|           @tmp_dir = File.join(@reports_path, 'tmp') | ||||
|           FileUtils.mkdir_p(@tmp_dir) | ||||
|         end | ||||
| 
 | ||||
|         def name | ||||
|           'jemalloc_stats' | ||||
|         end | ||||
| 
 | ||||
|         def run(report_id) | ||||
|         def run(writer) | ||||
|           return unless active? | ||||
| 
 | ||||
|           Gitlab::Memory::Jemalloc.dump_stats(path: reports_path, | ||||
|                                               tmp_dir: @tmp_dir, | ||||
|                                               filename_label: report_id).tap do | ||||
|             cleanup | ||||
|           end | ||||
|           Gitlab::Memory::Jemalloc.dump_stats(writer) | ||||
|         end | ||||
| 
 | ||||
|         def active? | ||||
|           Feature.enabled?(:report_jemalloc_stats, type: :ops) | ||||
|         end | ||||
| 
 | ||||
|         private | ||||
| 
 | ||||
|         attr_reader :reports_path | ||||
| 
 | ||||
|         def cleanup | ||||
|           reports_files_modified_order[0...-max_reports_stored].each do |f| | ||||
|             File.unlink(f) if File.exist?(f) | ||||
|           rescue Errno::ENOENT | ||||
|             # Path does not exist: Ignore. We already check `File.exist?` | ||||
|             # Rescue to be extra safe, because each worker could perform a cleanup | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def reports_files_modified_order | ||||
|           pattern = File.join(reports_path, "#{Gitlab::Memory::Jemalloc::FILENAME_PREFIX}*") | ||||
| 
 | ||||
|           Dir.glob(pattern).sort_by do |f| | ||||
|             test('M', f) | ||||
|           rescue Errno::ENOENT | ||||
|             # Path does not exist: Return any timestamp to proceed with the sort | ||||
|             Time.current | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def max_reports_stored | ||||
|           ENV["GITLAB_DIAGNOSTIC_REPORTS_JEMALLOC_MAX_REPORTS_STORED"] || DEFAULT_MAX_REPORTS_STORED | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
|  |  | |||
|  | @ -7,9 +7,7 @@ module Gitlab | |||
|       DEFAULT_SLEEP_MAX_DELTA_S = 600 # 0..10 minutes | ||||
|       DEFAULT_SLEEP_BETWEEN_REPORTS_S = 120 # 2 minutes | ||||
| 
 | ||||
|       DEFAULT_REPORTS_PATH = Dir.tmpdir | ||||
| 
 | ||||
|       def initialize(reporter: Reporter.new, reports: nil, **options) | ||||
|       def initialize(reporter: nil, reports: nil, **options) | ||||
|         super | ||||
| 
 | ||||
|         @alive = true | ||||
|  | @ -21,16 +19,13 @@ module Gitlab | |||
|         @sleep_between_reports_s = | ||||
|           ENV['GITLAB_DIAGNOSTIC_REPORTS_SLEEP_BETWEEN_REPORTS_S']&.to_i || DEFAULT_SLEEP_BETWEEN_REPORTS_S | ||||
| 
 | ||||
|         @reports_path = | ||||
|           ENV["GITLAB_DIAGNOSTIC_REPORTS_PATH"] || DEFAULT_REPORTS_PATH | ||||
| 
 | ||||
|         @reporter = reporter | ||||
|         @reporter = reporter || Reporter.new | ||||
|         @reports = reports || [ | ||||
|           Gitlab::Memory::Reports::JemallocStats.new(reports_path: reports_path) | ||||
|           Gitlab::Memory::Reports::JemallocStats.new | ||||
|         ] | ||||
|       end | ||||
| 
 | ||||
|       attr_reader :sleep_s, :sleep_max_delta_s, :sleep_between_reports_s, :reports_path | ||||
|       attr_reader :sleep_s, :sleep_max_delta_s, :sleep_between_reports_s | ||||
| 
 | ||||
|       def run_thread | ||||
|         while alive | ||||
|  |  | |||
|  | @ -20,6 +20,10 @@ module Gitlab | |||
|       status.to_i.between?(200, 499) | ||||
|     end | ||||
| 
 | ||||
|     def self.server_error?(status) | ||||
|       status.to_i >= 500 | ||||
|     end | ||||
| 
 | ||||
|     # Tracks an event. | ||||
|     # | ||||
|     # See `Gitlab::Metrics::Transaction#add_event` for more details. | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ module Gitlab | |||
|       class << self | ||||
|         def initialize_request_slis! | ||||
|           Gitlab::Metrics::Sli::Apdex.initialize_sli(:rails_request, possible_request_labels) | ||||
|           initialize_rails_request_error_rate | ||||
|           Gitlab::Metrics::Sli::Apdex.initialize_sli(:graphql_query, possible_graphql_query_labels) | ||||
|         end | ||||
| 
 | ||||
|  | @ -13,6 +14,10 @@ module Gitlab | |||
|           Gitlab::Metrics::Sli::Apdex[:rails_request] | ||||
|         end | ||||
| 
 | ||||
|         def request_error_rate | ||||
|           Gitlab::Metrics::Sli::ErrorRate[:rails_request] | ||||
|         end | ||||
| 
 | ||||
|         def graphql_query_apdex | ||||
|           Gitlab::Metrics::Sli::Apdex[:graphql_query] | ||||
|         end | ||||
|  | @ -58,6 +63,12 @@ module Gitlab | |||
|             } | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         def initialize_rails_request_error_rate | ||||
|           return unless Feature.enabled?(:gitlab_metrics_error_rate_sli, type: :development) | ||||
| 
 | ||||
|           Gitlab::Metrics::Sli::ErrorRate.initialize_sli(:rails_request, possible_request_labels) | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
|  |  | |||
|  | @ -75,15 +75,16 @@ module Gitlab | |||
| 
 | ||||
|         begin | ||||
|           status, headers, body = @app.call(env) | ||||
|           return [status, headers, body] if health_endpoint | ||||
| 
 | ||||
|           elapsed = ::Gitlab::Metrics::System.monotonic_time - started | ||||
| 
 | ||||
|           if !health_endpoint && ::Gitlab::Metrics.record_duration_for_status?(status) | ||||
|           if ::Gitlab::Metrics.record_duration_for_status?(status) | ||||
|             elapsed = ::Gitlab::Metrics::System.monotonic_time - started | ||||
|             self.class.http_request_duration_seconds.observe({ method: method }, elapsed) | ||||
| 
 | ||||
|             record_apdex(env, elapsed) | ||||
|           end | ||||
| 
 | ||||
|           record_error(env, status) | ||||
| 
 | ||||
|           [status, headers, body] | ||||
|         rescue StandardError | ||||
|           self.class.rack_uncaught_errors_count.increment | ||||
|  | @ -124,6 +125,15 @@ module Gitlab | |||
|         ) | ||||
|       end | ||||
| 
 | ||||
|       def record_error(env, status) | ||||
|         return unless Feature.enabled?(:gitlab_metrics_error_rate_sli, type: :development) | ||||
| 
 | ||||
|         Gitlab::Metrics::RailsSlis.request_error_rate.increment( | ||||
|           labels: labels_from_context, | ||||
|           error: ::Gitlab::Metrics.server_error?(status) | ||||
|         ) | ||||
|       end | ||||
| 
 | ||||
|       def labels_from_context | ||||
|         { | ||||
|           feature_category: feature_category.presence || FEATURE_CATEGORY_DEFAULT, | ||||
|  |  | |||
|  | @ -54,7 +54,7 @@ module Gitlab | |||
|         return unless environment | ||||
| 
 | ||||
|         actions = environment.actions_for(to).select do |action| | ||||
|           action.starts_environment? | ||||
|           action.deployment_job? | ||||
|         end | ||||
| 
 | ||||
|         if actions.many? | ||||
|  |  | |||
|  | @ -60,7 +60,6 @@ | |||
|     "@gitlab/web-ide": "0.0.1-dev-20221114183058", | ||||
|     "@rails/actioncable": "6.1.4-7", | ||||
|     "@rails/ujs": "6.1.4-7", | ||||
|     "@sentry/browser": "5.30.0", | ||||
|     "@sourcegraph/code-host-integration": "0.0.84", | ||||
|     "@tiptap/core": "^2.0.0-beta.182", | ||||
|     "@tiptap/extension-blockquote": "^2.0.0-beta.29", | ||||
|  | @ -173,6 +172,8 @@ | |||
|     "remark-rehype": "^10.1.0", | ||||
|     "scrollparent": "^2.0.1", | ||||
|     "select2": "3.5.2-browserify", | ||||
|     "sentrybrowser5": "npm:@sentry/browser@5.30.0", | ||||
|     "sentrybrowser7": "npm:@sentry/browser@^7.21.1", | ||||
|     "sortablejs": "^1.10.2", | ||||
|     "string-hash": "1.1.3", | ||||
|     "style-loader": "^2.0.0", | ||||
|  |  | |||
|  | @ -1002,7 +1002,7 @@ RSpec.describe 'File blob', :js do | |||
|       end | ||||
| 
 | ||||
|       it 'renders sandboxed iframe' do | ||||
|         expected = %(<iframe src="/-/sandbox/swagger" sandbox="allow-scripts allow-popups" frameborder="0" width="100%" height="1000">) | ||||
|         expected = %(<iframe src="/-/sandbox/swagger" sandbox="allow-scripts allow-popups allow-forms" frameborder="0" width="100%" height="1000">) | ||||
|         expect(page.html).to include(expected) | ||||
|       end | ||||
|     end | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe "Admin::Projects" do | ||||
| RSpec.describe "Admin::Projects", feature_category: :permissions do | ||||
|   include AccessMatchers | ||||
| 
 | ||||
|   describe "GET /admin/projects" do | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe "Dashboard access" do | ||||
| RSpec.describe "Dashboard access", feature_category: :permissions do | ||||
|   include AccessMatchers | ||||
| 
 | ||||
|   describe "GET /dashboard" do | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'Internal Group access' do | ||||
| RSpec.describe 'Internal Group access', feature_category: :permissions do | ||||
|   include AccessMatchers | ||||
| 
 | ||||
|   let(:group)   { create(:group, :internal) } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'Private Group access' do | ||||
| RSpec.describe 'Private Group access', feature_category: :permissions do | ||||
|   include AccessMatchers | ||||
| 
 | ||||
|   let(:group)   { create(:group, :private) } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'Public Group access' do | ||||
| RSpec.describe 'Public Group access', feature_category: :permissions do | ||||
|   include AccessMatchers | ||||
| 
 | ||||
|   let(:group)   { create(:group, :public) } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe "Profile access" do | ||||
| RSpec.describe "Profile access", feature_category: :user_management do | ||||
|   include AccessMatchers | ||||
| 
 | ||||
|   describe "GET /-/profile/keys" do | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe "Internal Project Access" do | ||||
| RSpec.describe "Internal Project Access", feature_category: :permissions do | ||||
|   include AccessMatchers | ||||
| 
 | ||||
|   let_it_be(:project, reload: true) { create(:project, :internal, :repository, :with_namespace_settings) } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe "Private Project Access" do | ||||
| RSpec.describe "Private Project Access", feature_category: :permissions do | ||||
|   include AccessMatchers | ||||
| 
 | ||||
|   let_it_be(:project, reload: true) do | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe "Public Project Access" do | ||||
| RSpec.describe "Public Project Access", feature_category: :permissions do | ||||
|   include AccessMatchers | ||||
| 
 | ||||
|   let_it_be(:project, reload: true) do | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe "Internal Project Snippets Access" do | ||||
| RSpec.describe "Internal Project Snippets Access", feature_category: :permissions do | ||||
|   include AccessMatchers | ||||
| 
 | ||||
|   let_it_be(:project) { create(:project, :internal) } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe "Private Project Snippets Access" do | ||||
| RSpec.describe "Private Project Snippets Access", feature_category: :permissions do | ||||
|   include AccessMatchers | ||||
| 
 | ||||
|   let_it_be(:project) { create(:project, :private) } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe "Public Project Snippets Access" do | ||||
| RSpec.describe "Public Project Snippets Access", feature_category: :permissions do | ||||
|   include AccessMatchers | ||||
| 
 | ||||
|   let_it_be(:project) { create(:project, :public) } | ||||
|  |  | |||
|  | @ -3,26 +3,61 @@ | |||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'Sentry' do | ||||
|   let(:sentry_regex_path) { '\/sentry.*\.chunk\.js' } | ||||
|   context 'when enable_new_sentry_clientside_integration is disabled' do | ||||
|     before do | ||||
|       stub_feature_flags(enable_new_sentry_clientside_integration: false) | ||||
|     end | ||||
| 
 | ||||
|   it 'does not load sentry if sentry is disabled' do | ||||
|     allow(Gitlab.config.sentry).to receive(:enabled).and_return(false) | ||||
|     visit new_user_session_path | ||||
|     it 'does not load sentry if sentry is disabled' do | ||||
|       allow(Gitlab.config.sentry).to receive(:enabled).and_return(false) | ||||
| 
 | ||||
|     expect(has_requested_sentry).to eq(false) | ||||
|       visit new_user_session_path | ||||
| 
 | ||||
|       expect(has_requested_legacy_sentry).to eq(false) | ||||
|     end | ||||
| 
 | ||||
|     it 'loads legacy sentry if sentry config is enabled', :js do | ||||
|       allow(Gitlab.config.sentry).to receive(:enabled).and_return(true) | ||||
| 
 | ||||
|       visit new_user_session_path | ||||
| 
 | ||||
|       expect(has_requested_legacy_sentry).to eq(true) | ||||
|       expect(evaluate_script('window._Sentry.SDK_VERSION')).to match(%r{^5\.}) | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   it 'loads sentry if sentry is enabled' do | ||||
|     stub_sentry_settings | ||||
|   context 'when enable_new_sentry_clientside_integration is enabled' do | ||||
|     before do | ||||
|       stub_feature_flags(enable_new_sentry_clientside_integration: true) | ||||
|     end | ||||
| 
 | ||||
|     visit new_user_session_path | ||||
|     it 'does not load sentry if sentry settings are disabled' do | ||||
|       allow(Gitlab::CurrentSettings).to receive(:sentry_enabled).and_return(false) | ||||
| 
 | ||||
|     expect(has_requested_sentry).to eq(true) | ||||
|       visit new_user_session_path | ||||
| 
 | ||||
|       expect(has_requested_sentry).to eq(false) | ||||
|     end | ||||
| 
 | ||||
|     it 'loads sentry if sentry settings are enabled', :js do | ||||
|       allow(Gitlab::CurrentSettings).to receive(:sentry_enabled).and_return(true) | ||||
| 
 | ||||
|       visit new_user_session_path | ||||
| 
 | ||||
|       expect(has_requested_sentry).to eq(true) | ||||
|       expect(evaluate_script('window._Sentry.SDK_VERSION')).to match(%r{^7\.}) | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def has_requested_legacy_sentry | ||||
|     page.all('script', visible: false).one? do |elm| | ||||
|       elm[:src] =~ %r{/legacy_sentry.*\.chunk\.js\z} | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def has_requested_sentry | ||||
|     page.all('script', visible: false).one? do |elm| | ||||
|       elm[:src] =~ /#{sentry_regex_path}$/ | ||||
|       elm[:src] =~ %r{/sentry.*\.chunk\.js\z} | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'Embedded Snippets' do | ||||
| RSpec.describe 'Embedded Snippets', feature_category: :snippets do | ||||
|   let_it_be(:snippet) { create(:personal_snippet, :public, :repository) } | ||||
| 
 | ||||
|   let(:blobs) { snippet.blobs.first(3) } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'Explore Snippets' do | ||||
| RSpec.describe 'Explore Snippets', feature_category: :snippets do | ||||
|   let!(:public_snippet) { create(:personal_snippet, :public) } | ||||
|   let!(:internal_snippet) { create(:personal_snippet, :internal) } | ||||
|   let!(:private_snippet) { create(:personal_snippet, :private) } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'Internal Snippets', :js do | ||||
| RSpec.describe 'Internal Snippets', :js, feature_category: :snippets do | ||||
|   let(:internal_snippet) { create(:personal_snippet, :internal, :repository) } | ||||
|   let(:content) { internal_snippet.blobs.first.data.strip! } | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'Comments on personal snippets', :js do | ||||
| RSpec.describe 'Comments on personal snippets', :js, feature_category: :snippets do | ||||
|   include NoteInteractionHelpers | ||||
|   include Spec::Support::Helpers::ModalHelpers | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'Private Snippets', :js do | ||||
| RSpec.describe 'Private Snippets', :js, feature_category: :snippets do | ||||
|   let(:user) { create(:user) } | ||||
|   let(:private_snippet) { create(:personal_snippet, :repository, :private, author: user) } | ||||
|   let(:content) { private_snippet.blobs.first.data.strip! } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'Public Snippets', :js do | ||||
| RSpec.describe 'Public Snippets', :js, feature_category: :snippets do | ||||
|   let(:public_snippet) { create(:personal_snippet, :public, :repository) } | ||||
|   let(:content) { public_snippet.blobs.first.data.strip! } | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'Search Snippets', :js do | ||||
| RSpec.describe 'Search Snippets', :js, feature_category: :snippets do | ||||
|   it 'user searches for snippets by title' do | ||||
|     public_snippet = create(:personal_snippet, :public, title: 'Beginning and Middle') | ||||
|     private_snippet = create(:personal_snippet, :private, title: 'Middle and End') | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'Snippet', :js do | ||||
| RSpec.describe 'Snippet', :js, feature_category: :snippets do | ||||
|   let_it_be(:user) { create(:user) } | ||||
|   let_it_be(:snippet) { create(:personal_snippet, :public, :repository, author: user) } | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,7 +2,8 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'snippet editor with spam', skip: "Will be handled in https://gitlab.com/gitlab-org/gitlab/-/issues/217722" do | ||||
| RSpec.describe 'snippet editor with spam', skip: "Will be handled in https://gitlab.com/gitlab-org/gitlab/-/issues/217722", | ||||
|                                            feature_category: :snippets do | ||||
|   include_context 'includes Spam constants' | ||||
| 
 | ||||
|   let_it_be(:user) { create(:user) } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'User creates snippet', :js do | ||||
| RSpec.describe 'User creates snippet', :js, feature_category: :snippets do | ||||
|   include DropzoneHelper | ||||
|   include Spec::Support::Helpers::Features::SnippetSpecHelpers | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'User deletes snippet', :js do | ||||
| RSpec.describe 'User deletes snippet', :js, feature_category: :snippets do | ||||
|   let(:user) { create(:user) } | ||||
|   let(:content) { 'puts "test"' } | ||||
|   let(:snippet) { create(:personal_snippet, :repository, :public, content: content, author: user) } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'User edits snippet', :js do | ||||
| RSpec.describe 'User edits snippet', :js, feature_category: :snippets do | ||||
|   include DropzoneHelper | ||||
|   include Spec::Support::Helpers::Features::SnippetSpecHelpers | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'User Snippets' do | ||||
| RSpec.describe 'User Snippets', feature_category: :snippets do | ||||
|   let(:author) { create(:user) } | ||||
|   let!(:public_snippet) { create(:personal_snippet, :public, author: author, title: "This is a public snippet") } | ||||
|   let!(:internal_snippet) { create(:personal_snippet, :internal, author: author, title: "This is an internal snippet") } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'Developer creates tag' do | ||||
| RSpec.describe 'Developer creates tag', feature_category: :source_code_management do | ||||
|   let(:user) { create(:user) } | ||||
|   let(:group) { create(:group) } | ||||
|   let(:project) { create(:project, :repository, namespace: group) } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'Developer deletes tag', :js do | ||||
| RSpec.describe 'Developer deletes tag', :js, feature_category: :source_code_management do | ||||
|   let(:user) { create(:user) } | ||||
|   let(:group) { create(:group) } | ||||
|   let(:project) { create(:project, :repository, namespace: group) } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'Developer views tags' do | ||||
| RSpec.describe 'Developer views tags', feature_category: :source_code_management do | ||||
|   include RepoHelpers | ||||
| 
 | ||||
|   let(:user) { create(:user) } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'Maintainer deletes protected tag', :js do | ||||
| RSpec.describe 'Maintainer deletes protected tag', :js, feature_category: :source_code_management do | ||||
|   let(:user) { create(:user) } | ||||
|   let(:group) { create(:group) } | ||||
|   let(:project) { create(:project, :repository, namespace: group) } | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'User uploads avatar to group' do | ||||
| RSpec.describe 'User uploads avatar to group', feature_category: :users do | ||||
|   it 'they see the new avatar' do | ||||
|     user = create(:user) | ||||
|     group = create(:group) | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'User uploads avatar to profile' do | ||||
| RSpec.describe 'User uploads avatar to profile', feature_category: :users do | ||||
|   let!(:user) { create(:user) } | ||||
|   let(:avatar_file_path) { Rails.root.join('spec', 'fixtures', 'dk.png') } | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'User uploads file to note' do | ||||
| RSpec.describe 'User uploads file to note', feature_category: :team_planning do | ||||
|   include DropzoneHelper | ||||
| 
 | ||||
|   let(:user) { create(:user) } | ||||
|  |  | |||
|  | @ -21,7 +21,7 @@ describe('OpenAPI blob viewer', () => { | |||
| 
 | ||||
|   it('initializes SwaggerUI with the correct configuration', () => { | ||||
|     expect(document.body.innerHTML).toContain( | ||||
|       '<iframe src="/-/sandbox/swagger" sandbox="allow-scripts allow-popups" frameborder="0" width="100%" height="1000"></iframe>', | ||||
|       '<iframe src="/-/sandbox/swagger" sandbox="allow-scripts allow-popups allow-forms" frameborder="0" width="100%" height="1000"></iframe>', | ||||
|     ); | ||||
|   }); | ||||
| }); | ||||
|  |  | |||
|  | @ -61,6 +61,10 @@ describe('Clusters', () => { | |||
|   let captureException; | ||||
| 
 | ||||
|   beforeEach(() => { | ||||
|     jest.spyOn(Sentry, 'withScope').mockImplementation((fn) => { | ||||
|       const mockScope = { setTag: () => {} }; | ||||
|       fn(mockScope); | ||||
|     }); | ||||
|     captureException = jest.spyOn(Sentry, 'captureException'); | ||||
| 
 | ||||
|     mock = new MockAdapter(axios); | ||||
|  |  | |||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue