Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.eslintrc.yml2
-rw-r--r--.gitlab/CODEOWNERS3
-rw-r--r--.rubocop.yml8
-rw-r--r--.rubocop_todo/rspec/factory_bot/avoid_create.yml374
-rw-r--r--Gemfile4
-rw-r--r--Gemfile.checksum2
-rw-r--r--Gemfile.lock5
-rw-r--r--app/assets/javascripts/blob/openapi/index.js2
-rw-r--r--app/assets/javascripts/sentry/constants.js1
-rw-r--r--app/assets/javascripts/sentry/index.js16
-rw-r--r--app/assets/javascripts/sentry/legacy_index.js34
-rw-r--r--app/assets/javascripts/sentry/legacy_sentry_config.js64
-rw-r--r--app/assets/javascripts/sentry/sentry_browser_wrapper.js27
-rw-r--r--app/assets/javascripts/sentry/sentry_config.js39
-rw-r--r--app/models/ci/build.rb8
-rw-r--r--app/serializers/build_details_entity.rb2
-rw-r--r--app/services/deployments/create_for_build_service.rb2
-rw-r--r--app/services/groups/group_links/create_service.rb2
-rw-r--r--app/services/groups/group_links/destroy_service.rb2
-rw-r--r--app/services/groups/group_links/update_service.rb2
-rw-r--r--app/views/layouts/_head.html.haml7
-rw-r--r--app/views/profiles/preferences/show.html.haml6
-rw-r--r--config/environments/development.rb4
-rw-r--r--config/feature_flags/development/gitlab_metrics_error_rate_sli.yml8
-rw-r--r--config/initializers/diagnostic_reports.rb6
-rw-r--r--config/webpack.config.js6
-rw-r--r--db/post_migrate/20221121155850_change_vulnerabilities_state_transitions_comment_limit.rb23
-rw-r--r--db/post_migrate/20221122155149_add_index_for_paths_on_non_projects.rb16
-rw-r--r--db/post_migrate/20221125222221_add_metrics_index_to_authentication_events.rb17
-rw-r--r--db/post_migrate/20221125222341_remove_result_index_from_authentication_events.rb18
-rw-r--r--db/schema_migrations/202211211558501
-rw-r--r--db/schema_migrations/202211221551491
-rw-r--r--db/schema_migrations/202211252222211
-rw-r--r--db/schema_migrations/202211252223411
-rw-r--r--db/structure.sql6
-rw-r--r--doc/administration/reference_architectures/50k_users.md4
-rw-r--r--doc/administration/troubleshooting/postgresql.md2
-rw-r--r--doc/ci/examples/semantic-release.md3
-rw-r--r--doc/development/service_ping/metrics_instrumentation.md12
-rw-r--r--doc/install/requirements.md3
-rw-r--r--doc/tutorials/move_personal_project_to_a_group.md2
-rw-r--r--doc/update/index.md13
-rw-r--r--doc/update/mysql_to_postgresql.md305
-rw-r--r--doc/user/packages/npm_registry/index.md499
-rw-r--r--doc/user/packages/package_registry/index.md32
-rw-r--r--doc/user/packages/workflows/build_packages.md4
-rw-r--r--doc/user/packages/workflows/project_registry.md4
-rw-r--r--doc/user/project/settings/index.md49
-rw-r--r--jest.config.base.js1
-rw-r--r--lib/api/api.rb6
-rw-r--r--lib/api/container_repositories.rb7
-rw-r--r--lib/api/groups.rb29
-rw-r--r--lib/api/members.rb12
-rw-r--r--lib/gitlab/content_security_policy/config_loader.rb16
-rw-r--r--lib/gitlab/memory/jemalloc.rb35
-rw-r--r--lib/gitlab/memory/reporter.rb80
-rw-r--r--lib/gitlab/memory/reports/heap_dump.rb34
-rw-r--r--lib/gitlab/memory/reports/jemalloc_stats.rb57
-rw-r--r--lib/gitlab/memory/reports_daemon.rb13
-rw-r--r--lib/gitlab/metrics.rb4
-rw-r--r--lib/gitlab/metrics/rails_slis.rb11
-rw-r--r--lib/gitlab/metrics/requests_rack_middleware.rb18
-rw-r--r--lib/gitlab/slash_commands/deploy.rb2
-rw-r--r--package.json3
-rw-r--r--spec/features/projects/blobs/blob_show_spec.rb2
-rw-r--r--spec/features/security/admin_access_spec.rb2
-rw-r--r--spec/features/security/dashboard_access_spec.rb2
-rw-r--r--spec/features/security/group/internal_access_spec.rb2
-rw-r--r--spec/features/security/group/private_access_spec.rb2
-rw-r--r--spec/features/security/group/public_access_spec.rb2
-rw-r--r--spec/features/security/profile_access_spec.rb2
-rw-r--r--spec/features/security/project/internal_access_spec.rb2
-rw-r--r--spec/features/security/project/private_access_spec.rb2
-rw-r--r--spec/features/security/project/public_access_spec.rb2
-rw-r--r--spec/features/security/project/snippet/internal_access_spec.rb2
-rw-r--r--spec/features/security/project/snippet/private_access_spec.rb2
-rw-r--r--spec/features/security/project/snippet/public_access_spec.rb2
-rw-r--r--spec/features/sentry_js_spec.rb55
-rw-r--r--spec/features/snippets/embedded_snippet_spec.rb2
-rw-r--r--spec/features/snippets/explore_spec.rb2
-rw-r--r--spec/features/snippets/internal_snippet_spec.rb2
-rw-r--r--spec/features/snippets/notes_on_personal_snippets_spec.rb2
-rw-r--r--spec/features/snippets/private_snippets_spec.rb2
-rw-r--r--spec/features/snippets/public_snippets_spec.rb2
-rw-r--r--spec/features/snippets/search_snippets_spec.rb2
-rw-r--r--spec/features/snippets/show_spec.rb2
-rw-r--r--spec/features/snippets/spam_snippets_spec.rb3
-rw-r--r--spec/features/snippets/user_creates_snippet_spec.rb2
-rw-r--r--spec/features/snippets/user_deletes_snippet_spec.rb2
-rw-r--r--spec/features/snippets/user_edits_snippet_spec.rb2
-rw-r--r--spec/features/snippets/user_snippets_spec.rb2
-rw-r--r--spec/features/tags/developer_creates_tag_spec.rb2
-rw-r--r--spec/features/tags/developer_deletes_tag_spec.rb2
-rw-r--r--spec/features/tags/developer_views_tags_spec.rb2
-rw-r--r--spec/features/tags/maintainer_deletes_protected_tag_spec.rb2
-rw-r--r--spec/features/uploads/user_uploads_avatar_to_group_spec.rb2
-rw-r--r--spec/features/uploads/user_uploads_avatar_to_profile_spec.rb2
-rw-r--r--spec/features/uploads/user_uploads_file_to_note_spec.rb2
-rw-r--r--spec/frontend/blob/openapi/index_spec.js2
-rw-r--r--spec/frontend/clusters_list/components/clusters_spec.js4
-rw-r--r--spec/frontend/clusters_list/store/actions_spec.js4
-rw-r--r--spec/frontend/sentry/index_spec.js60
-rw-r--r--spec/frontend/sentry/legacy_index_spec.js64
-rw-r--r--spec/frontend/sentry/legacy_sentry_config_spec.js215
-rw-r--r--spec/frontend/sentry/sentry_browser_wrapper_spec.js59
-rw-r--r--spec/frontend/sentry/sentry_config_spec.js131
-rw-r--r--spec/initializers/diagnostic_reports_spec.rb32
-rw-r--r--spec/lib/gitlab/content_security_policy/config_loader_spec.rb58
-rw-r--r--spec/lib/gitlab/gon_helper_spec.rb4
-rw-r--r--spec/lib/gitlab/memory/jemalloc_spec.rb51
-rw-r--r--spec/lib/gitlab/memory/reporter_spec.rb139
-rw-r--r--spec/lib/gitlab/memory/reports/heap_dump_spec.rb24
-rw-r--r--spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb94
-rw-r--r--spec/lib/gitlab/memory/reports_daemon_spec.rb12
-rw-r--r--spec/lib/gitlab/metrics/rails_slis_spec.rb29
-rw-r--r--spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb96
-rw-r--r--spec/lib/gitlab/metrics_spec.rb24
-rw-r--r--spec/models/ci/build_spec.rb4
-rw-r--r--spec/requests/api/container_repositories_spec.rb7
-rw-r--r--spec/requests/jira_connect/subscriptions_controller_spec.rb2
-rw-r--r--yarn.lock52
121 files changed, 1934 insertions, 1335 deletions
diff --git a/.eslintrc.yml b/.eslintrc.yml
index 88fbf37780a..d2bae1b21b3 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -75,6 +75,8 @@ rules:
- sibling
- index
pathGroups:
+ - pattern: '@sentry/browser'
+ group: external
- pattern: ~/**
group: internal
- pattern: emojis/**
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS
index 3199739a33f..9cbe00d82de 100644
--- a/.gitlab/CODEOWNERS
+++ b/.gitlab/CODEOWNERS
@@ -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]
diff --git a/.rubocop.yml b/.rubocop.yml
index 143a7db9ae7..2367cef34b8 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -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
diff --git a/.rubocop_todo/rspec/factory_bot/avoid_create.yml b/.rubocop_todo/rspec/factory_bot/avoid_create.yml
index e5881913467..9f9cb409b23 100644
--- a/.rubocop_todo/rspec/factory_bot/avoid_create.yml
+++ b/.rubocop_todo/rspec/factory_bot/avoid_create.yml
@@ -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'
diff --git a/Gemfile b/Gemfile
index 4453c65695a..fd79c756466 100644
--- a/Gemfile
+++ b/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
diff --git a/Gemfile.checksum b/Gemfile.checksum
index 0aa7313db84..73608acb159 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -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"},
diff --git a/Gemfile.lock b/Gemfile.lock
index 65dd5a64f3f..94610975c23 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -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)
diff --git a/app/assets/javascripts/blob/openapi/index.js b/app/assets/javascripts/blob/openapi/index.js
index 24a54358de5..8cfdc00bb40 100644
--- a/app/assets/javascripts/blob/openapi/index.js
+++ b/app/assets/javascripts/blob/openapi/index.js
@@ -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.
diff --git a/app/assets/javascripts/sentry/constants.js b/app/assets/javascripts/sentry/constants.js
index fd96da5faf6..5531c4f56db 100644
--- a/app/assets/javascripts/sentry/constants.js
+++ b/app/assets/javascripts/sentry/constants.js
@@ -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',
diff --git a/app/assets/javascripts/sentry/index.js b/app/assets/javascripts/sentry/index.js
index 176745b4177..5539a061726 100644
--- a/app/assets/javascripts/sentry/index.js
+++ b/app/assets/javascripts/sentry/index.js
@@ -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;
diff --git a/app/assets/javascripts/sentry/legacy_index.js b/app/assets/javascripts/sentry/legacy_index.js
new file mode 100644
index 00000000000..604b982e128
--- /dev/null
+++ b/app/assets/javascripts/sentry/legacy_index.js
@@ -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;
diff --git a/app/assets/javascripts/sentry/legacy_sentry_config.js b/app/assets/javascripts/sentry/legacy_sentry_config.js
new file mode 100644
index 00000000000..50a943886db
--- /dev/null
+++ b/app/assets/javascripts/sentry/legacy_sentry_config.js
@@ -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;
diff --git a/app/assets/javascripts/sentry/sentry_browser_wrapper.js b/app/assets/javascripts/sentry/sentry_browser_wrapper.js
new file mode 100644
index 00000000000..0382827f82c
--- /dev/null
+++ b/app/assets/javascripts/sentry/sentry_browser_wrapper.js
@@ -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);
+};
diff --git a/app/assets/javascripts/sentry/sentry_config.js b/app/assets/javascripts/sentry/sentry_config.js
index 4c5b8dbad5a..ed8a55b7d44 100644
--- a/app/assets/javascripts/sentry/sentry_config.js
+++ b/app/assets/javascripts/sentry/sentry_config.js
@@ -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;
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index a60813a36a7..9ad1331b948 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -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
diff --git a/app/serializers/build_details_entity.rb b/app/serializers/build_details_entity.rb
index dc7b5e95361..1caa9720c08 100644
--- a/app/serializers/build_details_entity.rb
+++ b/app/serializers/build_details_entity.rb
@@ -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|
diff --git a/app/services/deployments/create_for_build_service.rb b/app/services/deployments/create_for_build_service.rb
index 7bc0ea88910..b58aa50a66f 100644
--- a/app/services/deployments/create_for_build_service.rb
+++ b/app/services/deployments/create_for_build_service.rb
@@ -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))
diff --git a/app/services/groups/group_links/create_service.rb b/app/services/groups/group_links/create_service.rb
index 56ddf3ec0b4..52180c39972 100644
--- a/app/services/groups/group_links/create_service.rb
+++ b/app/services/groups/group_links/create_service.rb
@@ -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)
diff --git a/app/services/groups/group_links/destroy_service.rb b/app/services/groups/group_links/destroy_service.rb
index 4d74b5f32e2..d1f16775ab3 100644
--- a/app/services/groups/group_links/destroy_service.rb
+++ b/app/services/groups/group_links/destroy_service.rb
@@ -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)
diff --git a/app/services/groups/group_links/update_service.rb b/app/services/groups/group_links/update_service.rb
index a1411de36d6..244ec2254a8 100644
--- a/app/services/groups/group_links/update_service.rb
+++ b/app/services/groups/group_links/update_service.rb
@@ -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)
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 61b8dd146e3..aff7c1a6b05 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -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
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index a1d6ef3fec5..c11e7c6ea32 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -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
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 8f266f2660c..71376b74cfa 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -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")
diff --git a/config/feature_flags/development/gitlab_metrics_error_rate_sli.yml b/config/feature_flags/development/gitlab_metrics_error_rate_sli.yml
new file mode 100644
index 00000000000..30b872343ce
--- /dev/null
+++ b/config/feature_flags/development/gitlab_metrics_error_rate_sli.yml
@@ -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
diff --git a/config/initializers/diagnostic_reports.rb b/config/initializers/diagnostic_reports.rb
index 7e96c266b23..64ddc98c9fb 100644
--- a/config/initializers/diagnostic_reports.rb
+++ b/config/initializers/diagnostic_reports.rb
@@ -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
diff --git a/config/webpack.config.js b/config/webpack.config.js
index d79e6e12b39..c8a159605d1 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -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'),
diff --git a/db/post_migrate/20221121155850_change_vulnerabilities_state_transitions_comment_limit.rb b/db/post_migrate/20221121155850_change_vulnerabilities_state_transitions_comment_limit.rb
new file mode 100644
index 00000000000..b75216ee413
--- /dev/null
+++ b/db/post_migrate/20221121155850_change_vulnerabilities_state_transitions_comment_limit.rb
@@ -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
diff --git a/db/post_migrate/20221122155149_add_index_for_paths_on_non_projects.rb b/db/post_migrate/20221122155149_add_index_for_paths_on_non_projects.rb
new file mode 100644
index 00000000000..e9a90844550
--- /dev/null
+++ b/db/post_migrate/20221122155149_add_index_for_paths_on_non_projects.rb
@@ -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
diff --git a/db/post_migrate/20221125222221_add_metrics_index_to_authentication_events.rb b/db/post_migrate/20221125222221_add_metrics_index_to_authentication_events.rb
new file mode 100644
index 00000000000..2d3181dea67
--- /dev/null
+++ b/db/post_migrate/20221125222221_add_metrics_index_to_authentication_events.rb
@@ -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
diff --git a/db/post_migrate/20221125222341_remove_result_index_from_authentication_events.rb b/db/post_migrate/20221125222341_remove_result_index_from_authentication_events.rb
new file mode 100644
index 00000000000..97fb4b320d1
--- /dev/null
+++ b/db/post_migrate/20221125222341_remove_result_index_from_authentication_events.rb
@@ -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
diff --git a/db/schema_migrations/20221121155850 b/db/schema_migrations/20221121155850
new file mode 100644
index 00000000000..33c41d1b35e
--- /dev/null
+++ b/db/schema_migrations/20221121155850
@@ -0,0 +1 @@
+f73bd76a9ad54932b1f4b880af225a49089fc6ea782d213a9fc608b3029cddab \ No newline at end of file
diff --git a/db/schema_migrations/20221122155149 b/db/schema_migrations/20221122155149
new file mode 100644
index 00000000000..46a4270e5ed
--- /dev/null
+++ b/db/schema_migrations/20221122155149
@@ -0,0 +1 @@
+3c9b8f6191297e95c47a0ae2e3da7725ce33daa2a702407e0256393774935b0b \ No newline at end of file
diff --git a/db/schema_migrations/20221125222221 b/db/schema_migrations/20221125222221
new file mode 100644
index 00000000000..9235ef557b7
--- /dev/null
+++ b/db/schema_migrations/20221125222221
@@ -0,0 +1 @@
+c1974d6763a85469f3d12fe4e51b1bc3b986cc335b7fe79b3875332d34a1b548 \ No newline at end of file
diff --git a/db/schema_migrations/20221125222341 b/db/schema_migrations/20221125222341
new file mode 100644
index 00000000000..5f4a29202e1
--- /dev/null
+++ b/db/schema_migrations/20221125222341
@@ -0,0 +1 @@
+401b563cf9f92627082bbc9850ab2fbe1d9806ced094fda99783c5d51e00fe1c \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 766a5a738b0..33a261fb753 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -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);
diff --git a/doc/administration/reference_architectures/50k_users.md b/doc/administration/reference_architectures/50k_users.md
index 854113eebc3..9b425199220 100644
--- a/doc/administration/reference_architectures/50k_users.md
+++ b/doc/administration/reference_architectures/50k_users.md
@@ -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.
diff --git a/doc/administration/troubleshooting/postgresql.md b/doc/administration/troubleshooting/postgresql.md
index 6aabe9a5e3d..451e1666bd2 100644
--- a/doc/administration/troubleshooting/postgresql.md
+++ b/doc/administration/troubleshooting/postgresql.md
@@ -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
diff --git a/doc/ci/examples/semantic-release.md b/doc/ci/examples/semantic-release.md
index 8f0321517ab..1fa526e787a 100644
--- a/doc/ci/examples/semantic-release.md
+++ b/doc/ci/examples/semantic-release.md
@@ -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.
diff --git a/doc/development/service_ping/metrics_instrumentation.md b/doc/development/service_ping/metrics_instrumentation.md
index a9f236819fe..5cc8a7811d7 100644
--- a/doc/development/service_ping/metrics_instrumentation.md
+++ b/doc/development/service_ping/metrics_instrumentation.md
@@ -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>
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index f581a1c50f9..7fcb371c178 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -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
diff --git a/doc/tutorials/move_personal_project_to_a_group.md b/doc/tutorials/move_personal_project_to_a_group.md
index 08aa8fc5326..c50d94c3b17 100644
--- a/doc/tutorials/move_personal_project_to_a_group.md
+++ b/doc/tutorials/move_personal_project_to_a_group.md
@@ -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:
diff --git a/doc/update/index.md b/doc/update/index.md
index 84dfcdeea95..c8a6f9be20d 100644
--- a/doc/update/index.md
+++ b/doc/update/index.md
@@ -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.
diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md
index 14914eee27c..ad36a9ff534 100644
--- a/doc/update/mysql_to_postgresql.md
+++ b/doc/update/mysql_to_postgresql.md
@@ -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.
diff --git a/doc/user/packages/npm_registry/index.md b/doc/user/packages/npm_registry/index.md
index 5d2efc52ba9..f82210c0ef5 100644
--- a/doc/user/packages/npm_registry/index.md
+++ b/doc/user/packages/npm_registry/index.md
@@ -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.
-
-Publish npm packages in your project's Package Registry. Then install the
-packages whenever you need to use them as a dependency.
-
-Only [scoped](https://docs.npmjs.com/misc/scope/) packages are supported.
-
-For documentation of the specific API endpoints that the npm package manager
-client uses, see the [npm API documentation](../../../api/packages/npm.md).
-
-WARNING:
-Never hardcode GitLab tokens (or any tokens) directly in `.npmrc` files or any other files that can
-be committed to a repository.
+For documentation of the specific API endpoints that the npm package manager client uses, see the [npm API documentation](../../../api/packages/npm.md).
Learn how to build an [npm](../workflows/build_packages.md#npm) or [yarn](../workflows/build_packages.md#yarn) package.
-## Use the GitLab endpoint for npm packages
-
-To use the GitLab endpoint for npm packages, choose an option:
+Watch a [video demo](https://youtu.be/yvLxtkvsFDA) of how to publish npm packages to the GitLab Package Registry.
-- **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).
+## Publish to GitLab Package Registry
-Some features such as [publishing](#publish-an-npm-package) a package is only available on the project-level endpoint.
+### Authentication to the Package Registry
-## Authenticate 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).
-You must authenticate with the Package Registry when the project
-is private. Public projects do not require authentication.
+- 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.
-To authenticate, use one of the following:
+Create a token and save it to use later in the process.
-- 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.
+### Naming convention
-### Authenticate with a personal access token or deploy token
+Depending on how the package will be installed, you may need to adhere to the naming convention.
-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).
+You can use one of two API endpoints to install packages:
-#### Project-level npm endpoint
+- **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.
-To use the [project-level](#use-the-gitlab-endpoint-for-npm-packages) npm endpoint, set your npm configuration:
+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.
-```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>"
-```
-
-- `<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.
+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`.
-You should now be able to publish and install npm packages in your project.
+- 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
-If you encounter an error with [Yarn](https://classic.yarnpkg.com/en/), view
-[troubleshooting steps](#troubleshooting).
+| Project URL | Package Registry in | Scope | Full package name |
+| ------------------------------------------------------- | ------------------- | --------- | ---------------------- |
+| `https://gitlab.com/my-org/engineering-group/analytics` | Analytics | `@my-org` | `@my-org/package-name` |
-#### 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:
+Make sure that the name of your package in the `package.json` file matches this convention:
```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>"
+"name": "@my-org/package-name"
```
-- `<your_token>` is your personal access token or deploy token.
-- Replace `gitlab.example.com` with your domain name.
-
-You should now be able to install npm packages in your project.
+## Publishing a package via the command line
-If you encounter an error with [Yarn](https://classic.yarnpkg.com/en/), view
-[troubleshooting steps](#troubleshooting).
+### Authenticating via the `.npmrc`
-### 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:
+Create or edit the `.npmrc` file in the same directory as your `package.json`. Include the following lines in the `.npmrc` file:
```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}"
+@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}"
```
-Then, you can run `npm publish` either locally or by using GitLab CI/CD.
+- 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.
-- **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**.
+WARNING:
+Never hardcode GitLab tokens (or any tokens) directly in `.npmrc` files or any other files that can
+be committed to a repository.
-## Working with private registries
+### Publishing a package via the command line
-When working with private repositories, you may want to configure additional settings to ensure a secure communication channel:
+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
-# 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/
+NPM_TOKEN=your_token npm publish
```
-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
+Your package should now publish to the Package Registry.
-Prerequisites:
+## Publishing a package via a CI/CD pipeline
-- [Authenticate](#authenticate-to-the-package-registry) to the Package Registry.
-- Set a [project-level npm endpoint](#use-the-gitlab-endpoint-for-npm-packages).
+### Authenticating via the `.npmrc`
-To upload an npm package to your project, run this command:
+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
-npm publish
+@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}
```
-To view the package, go to your project's **Packages and registries**.
+- 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 can also define `"publishConfig"` for your project in `package.json`. For example:
+### Publishing a package via a CI/CD pipeline
-```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
+Your package should now publish to the Package Registry when the pipeline runs.
-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`:
+## Install a package
-```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>'
-```
+If multiple packages have the same name and version, when you install a package, the most recently-published package is retrieved.
-For the instance-level npm endpoint, use this Yarn 2 configuration in `.yarnrc.yml`:
+You can install a package from a GitLab project or instance:
-```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>'
-```
+- **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.
-In this configuration:
+### Install from the instance level
-- 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 `@`.
+WARNING:
+In order to install a package from the instance level, the package must have been published following the scoped [naming convention](#naming-convention).
-## Publishing packages with the same name or version
+1. Authenticate to the Package Registry
-You cannot publish a package if a package of the same name and version already exists.
-You must delete the existing package first.
+ 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.
-This rule has a different impact depending on the package name:
+ ```shell
+ npm config set -- //your_domain_name/api/v4/packages/npm/:_authToken=your_token
+ ```
-- 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.
+ - 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.
-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.
+1. Set the registry
-## `package.json` limitations
+ ```shell
+ npm config set @scope:registry https://your_domain_name.com/api/v4/packages/npm/
+ ```
-You can't publish a package if its `package.json` file exceeds 20,000 characters.
+ - 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.
-## Install a package
+1. Install the 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.
+ ```shell
+ npm install @scope/my-package
+ ```
-If multiple packages have the same name and version, when you install a package, the most recently-published package is retrieved.
+### Install from the project level
-1. Set the URL for scoped packages.
+1. Authenticate to the Package Registry
- For [instance-level endpoints](#use-the-gitlab-endpoint-for-npm-packages) run:
+ 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/projects/your_project_id/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_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.
- 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/api/v4/projects/your_project_id/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.
-
-1. Ensure [authentication](#authenticate-to-the-package-registry) is configured.
+ - 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. 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:
+## Helpful hints
- ```shell
- yarn add @my-scope/my-package
- ```
+### Package forwarding to npmjs.com
-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/).
+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.
-## 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):
+### Supported CLI commands
-```plaintext
-//gitlab.example.com/api/v4/projects/:_authToken=<your_token>
-```
+The GitLab npm repository supports the following commands for the npm CLI (`npm`) and yarn CLI
+(`yarn`):
-You can also use `yarn config` instead of `npm config` when setting your auth-token dynamically:
+- `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.
-```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>"
-```
+## Troubleshooting
### `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.
diff --git a/doc/user/packages/package_registry/index.md b/doc/user/packages/package_registry/index.md
index 65ff715a4ee..5e7f3e6ae79 100644
--- a/doc/user/packages/package_registry/index.md
+++ b/doc/user/packages/package_registry/index.md
@@ -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) |
diff --git a/doc/user/packages/workflows/build_packages.md b/doc/user/packages/workflows/build_packages.md
index ec971195ea9..eab1e4392e3 100644
--- a/doc/user/packages/workflows/build_packages.md
+++ b/doc/user/packages/workflows/build_packages.md
@@ -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.
diff --git a/doc/user/packages/workflows/project_registry.md b/doc/user/packages/workflows/project_registry.md
index df4b087f6d5..9e6c2877ca5 100644
--- a/doc/user/packages/workflows/project_registry.md
+++ b/doc/user/packages/workflows/project_registry.md
@@ -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
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index 13a8e981934..45f84eee4a7 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -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
diff --git a/jest.config.base.js b/jest.config.base.js
index 30e11122f81..56473d1643f 100644
--- a/jest.config.base.js
+++ b/jest.config.base.js
@@ -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,
};
diff --git a/lib/api/api.rb b/lib/api/api.rb
index 9952941c9c6..8611910406a 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -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
diff --git a/lib/api/container_repositories.rb b/lib/api/container_repositories.rb
index f2dd1fa21fd..b6b5fe10332 100644
--- a/lib/api/container_repositories.rb
+++ b/lib/api/container_repositories.rb
@@ -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'
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index cb67848247e..f9842c01db2 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -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'
diff --git a/lib/api/members.rb b/lib/api/members.rb
index f4e38207aca..76f4364106b 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -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,
diff --git a/lib/gitlab/content_security_policy/config_loader.rb b/lib/gitlab/content_security_policy/config_loader.rb
index 29e8e631fb7..8b1298d0561 100644
--- a/lib/gitlab/content_security_policy/config_loader.rb
+++ b/lib/gitlab/content_security_policy/config_loader.rb
@@ -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
diff --git a/lib/gitlab/memory/jemalloc.rb b/lib/gitlab/memory/jemalloc.rb
index 81c4be0f7fc..6025e6ab6f2 100644
--- a/lib/gitlab/memory/jemalloc.rb
+++ b/lib/gitlab/memory/jemalloc.rb
@@ -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
diff --git a/lib/gitlab/memory/reporter.rb b/lib/gitlab/memory/reporter.rb
index b4f3f950b95..91956cb1ce2 100644
--- a/lib/gitlab/memory/reporter.rb
+++ b/lib/gitlab/memory/reporter.rb
@@ -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')
+
+ report_id = [@worker_id, @worker_uuid].join(".")
- def worker_id
- ::Prometheus::PidProvider.worker_id
+ [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,
diff --git a/lib/gitlab/memory/reports/heap_dump.rb b/lib/gitlab/memory/reports/heap_dump.rb
index b81464ed5cf..07b2b94285b 100644
--- a/lib/gitlab/memory/reports/heap_dump.rb
+++ b/lib/gitlab/memory/reports/heap_dump.rb
@@ -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
diff --git a/lib/gitlab/memory/reports/jemalloc_stats.rb b/lib/gitlab/memory/reports/jemalloc_stats.rb
index 2e5a053031e..cfda409594f 100644
--- a/lib/gitlab/memory/reports/jemalloc_stats.rb
+++ b/lib/gitlab/memory/reports/jemalloc_stats.rb
@@ -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
diff --git a/lib/gitlab/memory/reports_daemon.rb b/lib/gitlab/memory/reports_daemon.rb
index 9a044dbd06c..9bbfe81116d 100644
--- a/lib/gitlab/memory/reports_daemon.rb
+++ b/lib/gitlab/memory/reports_daemon.rb
@@ -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
diff --git a/lib/gitlab/metrics.rb b/lib/gitlab/metrics.rb
index 6d7ecb53ec3..e99761a0459 100644
--- a/lib/gitlab/metrics.rb
+++ b/lib/gitlab/metrics.rb
@@ -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.
diff --git a/lib/gitlab/metrics/rails_slis.rb b/lib/gitlab/metrics/rails_slis.rb
index 71da0085c8c..9fd4eec479e 100644
--- a/lib/gitlab/metrics/rails_slis.rb
+++ b/lib/gitlab/metrics/rails_slis.rb
@@ -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
diff --git a/lib/gitlab/metrics/requests_rack_middleware.rb b/lib/gitlab/metrics/requests_rack_middleware.rb
index d7fe983c553..d26361ca12f 100644
--- a/lib/gitlab/metrics/requests_rack_middleware.rb
+++ b/lib/gitlab/metrics/requests_rack_middleware.rb
@@ -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,
diff --git a/lib/gitlab/slash_commands/deploy.rb b/lib/gitlab/slash_commands/deploy.rb
index 9fcefd99f81..16a4875be91 100644
--- a/lib/gitlab/slash_commands/deploy.rb
+++ b/lib/gitlab/slash_commands/deploy.rb
@@ -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?
diff --git a/package.json b/package.json
index a85dd77a0ad..5f007de92b0 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb
index e01382cf31f..03680f681ba 100644
--- a/spec/features/projects/blobs/blob_show_spec.rb
+++ b/spec/features/projects/blobs/blob_show_spec.rb
@@ -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
diff --git a/spec/features/security/admin_access_spec.rb b/spec/features/security/admin_access_spec.rb
index 8070ae066e7..de81444ed71 100644
--- a/spec/features/security/admin_access_spec.rb
+++ b/spec/features/security/admin_access_spec.rb
@@ -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
diff --git a/spec/features/security/dashboard_access_spec.rb b/spec/features/security/dashboard_access_spec.rb
index 5430329d47d..948a4567624 100644
--- a/spec/features/security/dashboard_access_spec.rb
+++ b/spec/features/security/dashboard_access_spec.rb
@@ -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
diff --git a/spec/features/security/group/internal_access_spec.rb b/spec/features/security/group/internal_access_spec.rb
index 755f170a93e..904431b4a0f 100644
--- a/spec/features/security/group/internal_access_spec.rb
+++ b/spec/features/security/group/internal_access_spec.rb
@@ -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) }
diff --git a/spec/features/security/group/private_access_spec.rb b/spec/features/security/group/private_access_spec.rb
index f733145b5e3..3d56468a1c9 100644
--- a/spec/features/security/group/private_access_spec.rb
+++ b/spec/features/security/group/private_access_spec.rb
@@ -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) }
diff --git a/spec/features/security/group/public_access_spec.rb b/spec/features/security/group/public_access_spec.rb
index 90de2b58044..ac6b8a8ddd1 100644
--- a/spec/features/security/group/public_access_spec.rb
+++ b/spec/features/security/group/public_access_spec.rb
@@ -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) }
diff --git a/spec/features/security/profile_access_spec.rb b/spec/features/security/profile_access_spec.rb
index 301efd2d99b..991ff115d3d 100644
--- a/spec/features/security/profile_access_spec.rb
+++ b/spec/features/security/profile_access_spec.rb
@@ -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
diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb
index 48cee4b1f19..e35e7ed742b 100644
--- a/spec/features/security/project/internal_access_spec.rb
+++ b/spec/features/security/project/internal_access_spec.rb
@@ -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) }
diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb
index c06b1e5da54..59ddb18ae8a 100644
--- a/spec/features/security/project/private_access_spec.rb
+++ b/spec/features/security/project/private_access_spec.rb
@@ -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
diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb
index d2112430638..425691001f2 100644
--- a/spec/features/security/project/public_access_spec.rb
+++ b/spec/features/security/project/public_access_spec.rb
@@ -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
diff --git a/spec/features/security/project/snippet/internal_access_spec.rb b/spec/features/security/project/snippet/internal_access_spec.rb
index ab080f0a460..b7dcc5f31d3 100644
--- a/spec/features/security/project/snippet/internal_access_spec.rb
+++ b/spec/features/security/project/snippet/internal_access_spec.rb
@@ -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) }
diff --git a/spec/features/security/project/snippet/private_access_spec.rb b/spec/features/security/project/snippet/private_access_spec.rb
index 1e0afc09b74..0ae45abb7ec 100644
--- a/spec/features/security/project/snippet/private_access_spec.rb
+++ b/spec/features/security/project/snippet/private_access_spec.rb
@@ -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) }
diff --git a/spec/features/security/project/snippet/public_access_spec.rb b/spec/features/security/project/snippet/public_access_spec.rb
index f734f7ba9e2..b98f665c0dc 100644
--- a/spec/features/security/project/snippet/public_access_spec.rb
+++ b/spec/features/security/project/snippet/public_access_spec.rb
@@ -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) }
diff --git a/spec/features/sentry_js_spec.rb b/spec/features/sentry_js_spec.rb
index 1d277ba7b3c..2e46dc459ec 100644
--- a/spec/features/sentry_js_spec.rb
+++ b/spec/features/sentry_js_spec.rb
@@ -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
+
+ expect(has_requested_legacy_sentry).to eq(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 'loads legacy sentry if sentry config is enabled', :js do
+ allow(Gitlab.config.sentry).to receive(:enabled).and_return(true)
- expect(has_requested_sentry).to eq(false)
+ 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
+
+ it 'does not load sentry if sentry settings are disabled' do
+ allow(Gitlab::CurrentSettings).to receive(:sentry_enabled).and_return(false)
- visit new_user_session_path
+ visit new_user_session_path
- expect(has_requested_sentry).to eq(true)
+ 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
diff --git a/spec/features/snippets/embedded_snippet_spec.rb b/spec/features/snippets/embedded_snippet_spec.rb
index 90d877d29b7..7bd61824494 100644
--- a/spec/features/snippets/embedded_snippet_spec.rb
+++ b/spec/features/snippets/embedded_snippet_spec.rb
@@ -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) }
diff --git a/spec/features/snippets/explore_spec.rb b/spec/features/snippets/explore_spec.rb
index b62c35bf96e..d0f4d888d94 100644
--- a/spec/features/snippets/explore_spec.rb
+++ b/spec/features/snippets/explore_spec.rb
@@ -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) }
diff --git a/spec/features/snippets/internal_snippet_spec.rb b/spec/features/snippets/internal_snippet_spec.rb
index 2fcd11c2a47..5169cc2a0f8 100644
--- a/spec/features/snippets/internal_snippet_spec.rb
+++ b/spec/features/snippets/internal_snippet_spec.rb
@@ -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! }
diff --git a/spec/features/snippets/notes_on_personal_snippets_spec.rb b/spec/features/snippets/notes_on_personal_snippets_spec.rb
index 8d55a7a64f4..b9d5e56d432 100644
--- a/spec/features/snippets/notes_on_personal_snippets_spec.rb
+++ b/spec/features/snippets/notes_on_personal_snippets_spec.rb
@@ -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
diff --git a/spec/features/snippets/private_snippets_spec.rb b/spec/features/snippets/private_snippets_spec.rb
index 7ff27419cf7..198ea11972d 100644
--- a/spec/features/snippets/private_snippets_spec.rb
+++ b/spec/features/snippets/private_snippets_spec.rb
@@ -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! }
diff --git a/spec/features/snippets/public_snippets_spec.rb b/spec/features/snippets/public_snippets_spec.rb
index 0f27d96d8e9..627a12aa765 100644
--- a/spec/features/snippets/public_snippets_spec.rb
+++ b/spec/features/snippets/public_snippets_spec.rb
@@ -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! }
diff --git a/spec/features/snippets/search_snippets_spec.rb b/spec/features/snippets/search_snippets_spec.rb
index d18729d080a..002fe05fcb0 100644
--- a/spec/features/snippets/search_snippets_spec.rb
+++ b/spec/features/snippets/search_snippets_spec.rb
@@ -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')
diff --git a/spec/features/snippets/show_spec.rb b/spec/features/snippets/show_spec.rb
index 2103d362f94..6cce60fbef3 100644
--- a/spec/features/snippets/show_spec.rb
+++ b/spec/features/snippets/show_spec.rb
@@ -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) }
diff --git a/spec/features/snippets/spam_snippets_spec.rb b/spec/features/snippets/spam_snippets_spec.rb
index 3748a916780..9e74df3ac05 100644
--- a/spec/features/snippets/spam_snippets_spec.rb
+++ b/spec/features/snippets/spam_snippets_spec.rb
@@ -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) }
diff --git a/spec/features/snippets/user_creates_snippet_spec.rb b/spec/features/snippets/user_creates_snippet_spec.rb
index fd95516090a..4aa099e5737 100644
--- a/spec/features/snippets/user_creates_snippet_spec.rb
+++ b/spec/features/snippets/user_creates_snippet_spec.rb
@@ -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
diff --git a/spec/features/snippets/user_deletes_snippet_spec.rb b/spec/features/snippets/user_deletes_snippet_spec.rb
index e896f7eb25b..23436a8933c 100644
--- a/spec/features/snippets/user_deletes_snippet_spec.rb
+++ b/spec/features/snippets/user_deletes_snippet_spec.rb
@@ -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) }
diff --git a/spec/features/snippets/user_edits_snippet_spec.rb b/spec/features/snippets/user_edits_snippet_spec.rb
index a04c59b53d2..561f1cce26b 100644
--- a/spec/features/snippets/user_edits_snippet_spec.rb
+++ b/spec/features/snippets/user_edits_snippet_spec.rb
@@ -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
diff --git a/spec/features/snippets/user_snippets_spec.rb b/spec/features/snippets/user_snippets_spec.rb
index bb733431b22..9839f8a472a 100644
--- a/spec/features/snippets/user_snippets_spec.rb
+++ b/spec/features/snippets/user_snippets_spec.rb
@@ -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") }
diff --git a/spec/features/tags/developer_creates_tag_spec.rb b/spec/features/tags/developer_creates_tag_spec.rb
index 5657115fb3c..39d34a5ae64 100644
--- a/spec/features/tags/developer_creates_tag_spec.rb
+++ b/spec/features/tags/developer_creates_tag_spec.rb
@@ -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) }
diff --git a/spec/features/tags/developer_deletes_tag_spec.rb b/spec/features/tags/developer_deletes_tag_spec.rb
index efd4b42c136..76cf3aa691d 100644
--- a/spec/features/tags/developer_deletes_tag_spec.rb
+++ b/spec/features/tags/developer_deletes_tag_spec.rb
@@ -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) }
diff --git a/spec/features/tags/developer_views_tags_spec.rb b/spec/features/tags/developer_views_tags_spec.rb
index 57e1f7da04e..0057ff1a5f3 100644
--- a/spec/features/tags/developer_views_tags_spec.rb
+++ b/spec/features/tags/developer_views_tags_spec.rb
@@ -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) }
diff --git a/spec/features/tags/maintainer_deletes_protected_tag_spec.rb b/spec/features/tags/maintainer_deletes_protected_tag_spec.rb
index 0bf9645c2fb..ce518b962cd 100644
--- a/spec/features/tags/maintainer_deletes_protected_tag_spec.rb
+++ b/spec/features/tags/maintainer_deletes_protected_tag_spec.rb
@@ -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) }
diff --git a/spec/features/uploads/user_uploads_avatar_to_group_spec.rb b/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
index 8daa869a6e3..78cede77fea 100644
--- a/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
+++ b/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
@@ -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)
diff --git a/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb b/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
index 02f9d57fcfe..fb62b5eadc5 100644
--- a/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
+++ b/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
@@ -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') }
diff --git a/spec/features/uploads/user_uploads_file_to_note_spec.rb b/spec/features/uploads/user_uploads_file_to_note_spec.rb
index 2547e2d274c..e5ad62592ae 100644
--- a/spec/features/uploads/user_uploads_file_to_note_spec.rb
+++ b/spec/features/uploads/user_uploads_file_to_note_spec.rb
@@ -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) }
diff --git a/spec/frontend/blob/openapi/index_spec.js b/spec/frontend/blob/openapi/index_spec.js
index 17e718df495..d9d65258516 100644
--- a/spec/frontend/blob/openapi/index_spec.js
+++ b/spec/frontend/blob/openapi/index_spec.js
@@ -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>',
);
});
});
diff --git a/spec/frontend/clusters_list/components/clusters_spec.js b/spec/frontend/clusters_list/components/clusters_spec.js
index a3f42c1f161..e8e705a6384 100644
--- a/spec/frontend/clusters_list/components/clusters_spec.js
+++ b/spec/frontend/clusters_list/components/clusters_spec.js
@@ -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);
diff --git a/spec/frontend/clusters_list/store/actions_spec.js b/spec/frontend/clusters_list/store/actions_spec.js
index 09b1f80ff9b..1deebf8b75a 100644
--- a/spec/frontend/clusters_list/store/actions_spec.js
+++ b/spec/frontend/clusters_list/store/actions_spec.js
@@ -17,6 +17,10 @@ describe('Clusters store actions', () => {
describe('reportSentryError', () => {
beforeEach(() => {
+ jest.spyOn(Sentry, 'withScope').mockImplementation((fn) => {
+ const mockScope = { setTag: () => {} };
+ fn(mockScope);
+ });
captureException = jest.spyOn(Sentry, 'captureException');
});
diff --git a/spec/frontend/sentry/index_spec.js b/spec/frontend/sentry/index_spec.js
index d1f098112e8..2dd528a8a1c 100644
--- a/spec/frontend/sentry/index_spec.js
+++ b/spec/frontend/sentry/index_spec.js
@@ -1,17 +1,20 @@
import index from '~/sentry/index';
+
+import LegacySentryConfig from '~/sentry/legacy_sentry_config';
import SentryConfig from '~/sentry/sentry_config';
-describe('SentryConfig options', () => {
+describe('Sentry init', () => {
+ let originalGon;
+
const dsn = 'https://123@sentry.gitlab.test/123';
- const currentUserId = 'currentUserId';
- const gitlabUrl = 'gitlabUrl';
const environment = 'test';
+ const currentUserId = '1';
+ const gitlabUrl = 'gitlabUrl';
const revision = 'revision';
const featureCategory = 'my_feature_category';
- let indexReturnValue;
-
beforeEach(() => {
+ originalGon = window.gon;
window.gon = {
sentry_dsn: dsn,
sentry_environment: environment,
@@ -21,28 +24,41 @@ describe('SentryConfig options', () => {
feature_category: featureCategory,
};
- process.env.HEAD_COMMIT_SHA = revision;
-
+ jest.spyOn(LegacySentryConfig, 'init').mockImplementation();
jest.spyOn(SentryConfig, 'init').mockImplementation();
+ });
- indexReturnValue = index();
+ afterEach(() => {
+ window.gon = originalGon;
});
- it('should init with .sentryDsn, .currentUserId, .whitelistUrls and environment', () => {
- expect(SentryConfig.init).toHaveBeenCalledWith({
- dsn,
- currentUserId,
- whitelistUrls: [gitlabUrl, 'webpack-internal://'],
- environment,
- release: revision,
- tags: {
- revision,
- feature_category: featureCategory,
- },
- });
+ it('exports new version of Sentry in the global object', () => {
+ // eslint-disable-next-line no-underscore-dangle
+ expect(window._Sentry.SDK_VERSION).not.toMatch(/^5\./);
});
- it('should return SentryConfig', () => {
- expect(indexReturnValue).toBe(SentryConfig);
+ describe('when called', () => {
+ beforeEach(() => {
+ index();
+ });
+
+ it('configures sentry', () => {
+ expect(SentryConfig.init).toHaveBeenCalledTimes(1);
+ expect(SentryConfig.init).toHaveBeenCalledWith({
+ dsn,
+ currentUserId,
+ allowUrls: [gitlabUrl, 'webpack-internal://'],
+ environment,
+ release: revision,
+ tags: {
+ revision,
+ feature_category: featureCategory,
+ },
+ });
+ });
+
+ it('does not configure legacy sentry', () => {
+ expect(LegacySentryConfig.init).not.toHaveBeenCalled();
+ });
});
});
diff --git a/spec/frontend/sentry/legacy_index_spec.js b/spec/frontend/sentry/legacy_index_spec.js
new file mode 100644
index 00000000000..5c336f8392e
--- /dev/null
+++ b/spec/frontend/sentry/legacy_index_spec.js
@@ -0,0 +1,64 @@
+import index from '~/sentry/legacy_index';
+
+import LegacySentryConfig from '~/sentry/legacy_sentry_config';
+import SentryConfig from '~/sentry/sentry_config';
+
+describe('Sentry init', () => {
+ let originalGon;
+
+ const dsn = 'https://123@sentry.gitlab.test/123';
+ const environment = 'test';
+ const currentUserId = '1';
+ const gitlabUrl = 'gitlabUrl';
+ const revision = 'revision';
+ const featureCategory = 'my_feature_category';
+
+ beforeEach(() => {
+ originalGon = window.gon;
+ window.gon = {
+ sentry_dsn: dsn,
+ sentry_environment: environment,
+ current_user_id: currentUserId,
+ gitlab_url: gitlabUrl,
+ revision,
+ feature_category: featureCategory,
+ };
+
+ jest.spyOn(LegacySentryConfig, 'init').mockImplementation();
+ jest.spyOn(SentryConfig, 'init').mockImplementation();
+ });
+
+ afterEach(() => {
+ window.gon = originalGon;
+ });
+
+ it('exports legacy version of Sentry in the global object', () => {
+ // eslint-disable-next-line no-underscore-dangle
+ expect(window._Sentry.SDK_VERSION).toMatch(/^5\./);
+ });
+
+ describe('when called', () => {
+ beforeEach(() => {
+ index();
+ });
+
+ it('configures legacy sentry', () => {
+ expect(LegacySentryConfig.init).toHaveBeenCalledTimes(1);
+ expect(LegacySentryConfig.init).toHaveBeenCalledWith({
+ dsn,
+ currentUserId,
+ whitelistUrls: [gitlabUrl, 'webpack-internal://'],
+ environment,
+ release: revision,
+ tags: {
+ revision,
+ feature_category: featureCategory,
+ },
+ });
+ });
+
+ it('does not configure new sentry', () => {
+ expect(SentryConfig.init).not.toHaveBeenCalled();
+ });
+ });
+});
diff --git a/spec/frontend/sentry/legacy_sentry_config_spec.js b/spec/frontend/sentry/legacy_sentry_config_spec.js
new file mode 100644
index 00000000000..fe90cb49074
--- /dev/null
+++ b/spec/frontend/sentry/legacy_sentry_config_spec.js
@@ -0,0 +1,215 @@
+import * as Sentry5 from 'sentrybrowser5';
+import LegacySentryConfig from '~/sentry/legacy_sentry_config';
+
+describe('LegacySentryConfig', () => {
+ describe('IGNORE_ERRORS', () => {
+ it('should be an array of strings', () => {
+ const areStrings = LegacySentryConfig.IGNORE_ERRORS.every(
+ (error) => typeof error === 'string',
+ );
+
+ expect(areStrings).toBe(true);
+ });
+ });
+
+ describe('BLACKLIST_URLS', () => {
+ it('should be an array of regexps', () => {
+ const areRegExps = LegacySentryConfig.BLACKLIST_URLS.every((url) => url instanceof RegExp);
+
+ expect(areRegExps).toBe(true);
+ });
+ });
+
+ describe('SAMPLE_RATE', () => {
+ it('should be a finite number', () => {
+ expect(typeof LegacySentryConfig.SAMPLE_RATE).toEqual('number');
+ });
+ });
+
+ describe('init', () => {
+ const options = {
+ currentUserId: 1,
+ };
+
+ beforeEach(() => {
+ jest.spyOn(LegacySentryConfig, 'configure');
+ jest.spyOn(LegacySentryConfig, 'bindSentryErrors');
+ jest.spyOn(LegacySentryConfig, 'setUser');
+
+ LegacySentryConfig.init(options);
+ });
+
+ it('should set the options property', () => {
+ expect(LegacySentryConfig.options).toEqual(options);
+ });
+
+ it('should call the configure method', () => {
+ expect(LegacySentryConfig.configure).toHaveBeenCalled();
+ });
+
+ it('should call the error bindings method', () => {
+ expect(LegacySentryConfig.bindSentryErrors).toHaveBeenCalled();
+ });
+
+ it('should call setUser', () => {
+ expect(LegacySentryConfig.setUser).toHaveBeenCalled();
+ });
+
+ it('should not call setUser if there is no current user ID', () => {
+ LegacySentryConfig.setUser.mockClear();
+ options.currentUserId = undefined;
+
+ LegacySentryConfig.init(options);
+
+ expect(LegacySentryConfig.setUser).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('configure', () => {
+ const sentryConfig = {};
+ const options = {
+ dsn: 'https://123@sentry.gitlab.test/123',
+ whitelistUrls: ['//gitlabUrl', 'webpack-internal://'],
+ environment: 'test',
+ release: 'revision',
+ tags: {
+ revision: 'revision',
+ feature_category: 'my_feature_category',
+ },
+ };
+
+ beforeEach(() => {
+ jest.spyOn(Sentry5, 'init').mockImplementation();
+ jest.spyOn(Sentry5, 'setTags').mockImplementation();
+
+ sentryConfig.options = options;
+ sentryConfig.IGNORE_ERRORS = 'ignore_errors';
+ sentryConfig.BLACKLIST_URLS = 'blacklist_urls';
+
+ LegacySentryConfig.configure.call(sentryConfig);
+ });
+
+ it('should call Sentry5.init', () => {
+ expect(Sentry5.init).toHaveBeenCalledWith({
+ dsn: options.dsn,
+ release: options.release,
+ sampleRate: 0.95,
+ whitelistUrls: options.whitelistUrls,
+ environment: 'test',
+ ignoreErrors: sentryConfig.IGNORE_ERRORS,
+ blacklistUrls: sentryConfig.BLACKLIST_URLS,
+ });
+ });
+
+ it('should call Sentry5.setTags', () => {
+ expect(Sentry5.setTags).toHaveBeenCalledWith(options.tags);
+ });
+
+ it('should set environment from options', () => {
+ sentryConfig.options.environment = 'development';
+
+ LegacySentryConfig.configure.call(sentryConfig);
+
+ expect(Sentry5.init).toHaveBeenCalledWith({
+ dsn: options.dsn,
+ release: options.release,
+ sampleRate: 0.95,
+ whitelistUrls: options.whitelistUrls,
+ environment: 'development',
+ ignoreErrors: sentryConfig.IGNORE_ERRORS,
+ blacklistUrls: sentryConfig.BLACKLIST_URLS,
+ });
+ });
+ });
+
+ describe('setUser', () => {
+ let sentryConfig;
+
+ beforeEach(() => {
+ sentryConfig = { options: { currentUserId: 1 } };
+ jest.spyOn(Sentry5, 'setUser');
+
+ LegacySentryConfig.setUser.call(sentryConfig);
+ });
+
+ it('should call .setUser', () => {
+ expect(Sentry5.setUser).toHaveBeenCalledWith({
+ id: sentryConfig.options.currentUserId,
+ });
+ });
+ });
+
+ describe('handleSentryErrors', () => {
+ let event;
+ let req;
+ let config;
+ let err;
+
+ beforeEach(() => {
+ event = {};
+ req = { status: 'status', responseText: 'Unknown response text', statusText: 'statusText' };
+ config = { type: 'type', url: 'url', data: 'data' };
+ err = {};
+
+ jest.spyOn(Sentry5, 'captureMessage');
+
+ LegacySentryConfig.handleSentryErrors(event, req, config, err);
+ });
+
+ it('should call Sentry5.captureMessage', () => {
+ expect(Sentry5.captureMessage).toHaveBeenCalledWith(err, {
+ extra: {
+ type: config.type,
+ url: config.url,
+ data: config.data,
+ status: req.status,
+ response: req.responseText,
+ error: err,
+ event,
+ },
+ });
+ });
+
+ describe('if no err is provided', () => {
+ beforeEach(() => {
+ LegacySentryConfig.handleSentryErrors(event, req, config);
+ });
+
+ it('should use req.statusText as the error value', () => {
+ expect(Sentry5.captureMessage).toHaveBeenCalledWith(req.statusText, {
+ extra: {
+ type: config.type,
+ url: config.url,
+ data: config.data,
+ status: req.status,
+ response: req.responseText,
+ error: req.statusText,
+ event,
+ },
+ });
+ });
+ });
+
+ describe('if no req.responseText is provided', () => {
+ beforeEach(() => {
+ req.responseText = undefined;
+
+ LegacySentryConfig.handleSentryErrors(event, req, config, err);
+ });
+
+ it('should use `Unknown response text` as the response', () => {
+ expect(Sentry5.captureMessage).toHaveBeenCalledWith(err, {
+ extra: {
+ type: config.type,
+ url: config.url,
+ data: config.data,
+ status: req.status,
+ response: 'Unknown response text',
+ error: err,
+ event,
+ },
+ });
+ });
+ });
+ });
+});
diff --git a/spec/frontend/sentry/sentry_browser_wrapper_spec.js b/spec/frontend/sentry/sentry_browser_wrapper_spec.js
new file mode 100644
index 00000000000..f4d646bab78
--- /dev/null
+++ b/spec/frontend/sentry/sentry_browser_wrapper_spec.js
@@ -0,0 +1,59 @@
+import * as Sentry from '~/sentry/sentry_browser_wrapper';
+
+const mockError = new Error('error!');
+const mockMsg = 'msg!';
+const mockFn = () => {};
+
+describe('SentryBrowserWrapper', () => {
+ afterEach(() => {
+ // eslint-disable-next-line no-underscore-dangle
+ delete window._Sentry;
+ });
+
+ describe('when _Sentry is not defined', () => {
+ it('methods fail silently', () => {
+ expect(() => {
+ Sentry.captureException(mockError);
+ Sentry.captureMessage(mockMsg);
+ Sentry.withScope(mockFn);
+ }).not.toThrow();
+ });
+ });
+
+ describe('when _Sentry is defined', () => {
+ let mockCaptureException;
+ let mockCaptureMessage;
+ let mockWithScope;
+
+ beforeEach(async () => {
+ mockCaptureException = jest.fn();
+ mockCaptureMessage = jest.fn();
+ mockWithScope = jest.fn();
+
+ // eslint-disable-next-line no-underscore-dangle
+ window._Sentry = {
+ captureException: mockCaptureException,
+ captureMessage: mockCaptureMessage,
+ withScope: mockWithScope,
+ };
+ });
+
+ it('captureException is called', () => {
+ Sentry.captureException(mockError);
+
+ expect(mockCaptureException).toHaveBeenCalledWith(mockError);
+ });
+
+ it('captureMessage is called', () => {
+ Sentry.captureMessage(mockMsg);
+
+ expect(mockCaptureMessage).toHaveBeenCalledWith(mockMsg);
+ });
+
+ it('withScope is called', () => {
+ Sentry.withScope(mockFn);
+
+ expect(mockWithScope).toHaveBeenCalledWith(mockFn);
+ });
+ });
+});
diff --git a/spec/frontend/sentry/sentry_config_spec.js b/spec/frontend/sentry/sentry_config_spec.js
index 9f67b681b8d..44acbee9b38 100644
--- a/spec/frontend/sentry/sentry_config_spec.js
+++ b/spec/frontend/sentry/sentry_config_spec.js
@@ -1,29 +1,9 @@
-import * as Sentry from '@sentry/browser';
+import * as Sentry from 'sentrybrowser7';
+import { IGNORE_ERRORS, DENY_URLS, SAMPLE_RATE } from '~/sentry/constants';
+
import SentryConfig from '~/sentry/sentry_config';
describe('SentryConfig', () => {
- describe('IGNORE_ERRORS', () => {
- it('should be an array of strings', () => {
- const areStrings = SentryConfig.IGNORE_ERRORS.every((error) => typeof error === 'string');
-
- expect(areStrings).toBe(true);
- });
- });
-
- describe('BLACKLIST_URLS', () => {
- it('should be an array of regexps', () => {
- const areRegExps = SentryConfig.BLACKLIST_URLS.every((url) => url instanceof RegExp);
-
- expect(areRegExps).toBe(true);
- });
- });
-
- describe('SAMPLE_RATE', () => {
- it('should be a finite number', () => {
- expect(typeof SentryConfig.SAMPLE_RATE).toEqual('number');
- });
- });
-
describe('init', () => {
const options = {
currentUserId: 1,
@@ -31,7 +11,6 @@ describe('SentryConfig', () => {
beforeEach(() => {
jest.spyOn(SentryConfig, 'configure');
- jest.spyOn(SentryConfig, 'bindSentryErrors');
jest.spyOn(SentryConfig, 'setUser');
SentryConfig.init(options);
@@ -45,19 +24,13 @@ describe('SentryConfig', () => {
expect(SentryConfig.configure).toHaveBeenCalled();
});
- it('should call the error bindings method', () => {
- expect(SentryConfig.bindSentryErrors).toHaveBeenCalled();
- });
-
it('should call setUser', () => {
expect(SentryConfig.setUser).toHaveBeenCalled();
});
it('should not call setUser if there is no current user ID', () => {
SentryConfig.setUser.mockClear();
- options.currentUserId = undefined;
-
- SentryConfig.init(options);
+ SentryConfig.init({ currentUserId: undefined });
expect(SentryConfig.setUser).not.toHaveBeenCalled();
});
@@ -67,7 +40,7 @@ describe('SentryConfig', () => {
const sentryConfig = {};
const options = {
dsn: 'https://123@sentry.gitlab.test/123',
- whitelistUrls: ['//gitlabUrl', 'webpack-internal://'],
+ allowUrls: ['//gitlabUrl', 'webpack-internal://'],
environment: 'test',
release: 'revision',
tags: {
@@ -81,8 +54,6 @@ describe('SentryConfig', () => {
jest.spyOn(Sentry, 'setTags').mockImplementation();
sentryConfig.options = options;
- sentryConfig.IGNORE_ERRORS = 'ignore_errors';
- sentryConfig.BLACKLIST_URLS = 'blacklist_urls';
SentryConfig.configure.call(sentryConfig);
});
@@ -91,11 +62,11 @@ describe('SentryConfig', () => {
expect(Sentry.init).toHaveBeenCalledWith({
dsn: options.dsn,
release: options.release,
- sampleRate: 0.95,
- whitelistUrls: options.whitelistUrls,
- environment: 'test',
- ignoreErrors: sentryConfig.IGNORE_ERRORS,
- blacklistUrls: sentryConfig.BLACKLIST_URLS,
+ sampleRate: SAMPLE_RATE,
+ allowUrls: options.allowUrls,
+ environment: options.environment,
+ ignoreErrors: IGNORE_ERRORS,
+ denyUrls: DENY_URLS,
});
});
@@ -111,11 +82,11 @@ describe('SentryConfig', () => {
expect(Sentry.init).toHaveBeenCalledWith({
dsn: options.dsn,
release: options.release,
- sampleRate: 0.95,
- whitelistUrls: options.whitelistUrls,
+ sampleRate: SAMPLE_RATE,
+ allowUrls: options.allowUrls,
environment: 'development',
- ignoreErrors: sentryConfig.IGNORE_ERRORS,
- blacklistUrls: sentryConfig.BLACKLIST_URLS,
+ ignoreErrors: IGNORE_ERRORS,
+ denyUrls: DENY_URLS,
});
});
});
@@ -136,78 +107,4 @@ describe('SentryConfig', () => {
});
});
});
-
- describe('handleSentryErrors', () => {
- let event;
- let req;
- let config;
- let err;
-
- beforeEach(() => {
- event = {};
- req = { status: 'status', responseText: 'Unknown response text', statusText: 'statusText' };
- config = { type: 'type', url: 'url', data: 'data' };
- err = {};
-
- jest.spyOn(Sentry, 'captureMessage');
-
- SentryConfig.handleSentryErrors(event, req, config, err);
- });
-
- it('should call Sentry.captureMessage', () => {
- expect(Sentry.captureMessage).toHaveBeenCalledWith(err, {
- extra: {
- type: config.type,
- url: config.url,
- data: config.data,
- status: req.status,
- response: req.responseText,
- error: err,
- event,
- },
- });
- });
-
- describe('if no err is provided', () => {
- beforeEach(() => {
- SentryConfig.handleSentryErrors(event, req, config);
- });
-
- it('should use req.statusText as the error value', () => {
- expect(Sentry.captureMessage).toHaveBeenCalledWith(req.statusText, {
- extra: {
- type: config.type,
- url: config.url,
- data: config.data,
- status: req.status,
- response: req.responseText,
- error: req.statusText,
- event,
- },
- });
- });
- });
-
- describe('if no req.responseText is provided', () => {
- beforeEach(() => {
- req.responseText = undefined;
-
- SentryConfig.handleSentryErrors(event, req, config, err);
- });
-
- it('should use `Unknown response text` as the response', () => {
- expect(Sentry.captureMessage).toHaveBeenCalledWith(err, {
- extra: {
- type: config.type,
- url: config.url,
- data: config.data,
- status: req.status,
- response: 'Unknown response text',
- error: err,
- event,
- },
- });
- });
- });
- });
});
diff --git a/spec/initializers/diagnostic_reports_spec.rb b/spec/initializers/diagnostic_reports_spec.rb
index 2022076072b..20d0b2714f0 100644
--- a/spec/initializers/diagnostic_reports_spec.rb
+++ b/spec/initializers/diagnostic_reports_spec.rb
@@ -28,10 +28,10 @@ RSpec.describe 'diagnostic reports' do
end
let(:report_daemon) { instance_double(Gitlab::Memory::ReportsDaemon) }
+ let(:reporter) { instance_double(Gitlab::Memory::Reporter) }
it 'modifies worker startup hooks, starts Gitlab::Memory::ReportsDaemon' do
expect(Gitlab::Cluster::LifecycleEvents).to receive(:on_worker_start).and_call_original
- expect(Gitlab::Cluster::LifecycleEvents).to receive(:on_worker_stop)
expect_next_instance_of(Gitlab::Memory::ReportsDaemon) do |daemon|
expect(daemon).to receive(:start)
end
@@ -39,14 +39,30 @@ RSpec.describe 'diagnostic reports' do
load_initializer
end
- it 'writes scheduled heap dumps in on_worker_stop' do
- expect(Gitlab::Cluster::LifecycleEvents).to receive(:on_worker_start)
- expect(Gitlab::Cluster::LifecycleEvents).to receive(:on_worker_stop).and_call_original
- expect(Gitlab::Memory::Reports::HeapDump).to receive(:write_conditionally)
+ context 'when GITLAB_MEMWD_DUMP_HEAP is set' do
+ before do
+ stub_env('GITLAB_MEMWD_DUMP_HEAP', '1')
+ end
- load_initializer
- # This is necessary because this hook normally fires during worker shutdown.
- Gitlab::Cluster::LifecycleEvents.do_worker_stop
+ it 'writes scheduled heap dumps in on_worker_stop' do
+ expect(Gitlab::Cluster::LifecycleEvents).to receive(:on_worker_start)
+ expect(Gitlab::Cluster::LifecycleEvents).to receive(:on_worker_stop).and_call_original
+ expect(Gitlab::Memory::Reporter).to receive(:new).and_return(reporter)
+ expect(reporter).to receive(:run_report).with(an_instance_of(Gitlab::Memory::Reports::HeapDump))
+
+ load_initializer
+ # This is necessary because this hook normally fires during worker shutdown.
+ Gitlab::Cluster::LifecycleEvents.do_worker_stop
+ end
+ end
+
+ context 'when GITLAB_MEMWD_DUMP_HEAP is not set' do
+ it 'does not write heap dumps' do
+ expect(Gitlab::Cluster::LifecycleEvents).to receive(:on_worker_start)
+ expect(Gitlab::Cluster::LifecycleEvents).not_to receive(:on_worker_stop)
+
+ load_initializer
+ end
end
end
diff --git a/spec/lib/gitlab/content_security_policy/config_loader_spec.rb b/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
index aadfb41a46e..88bffd41947 100644
--- a/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
+++ b/spec/lib/gitlab/content_security_policy/config_loader_spec.rb
@@ -102,13 +102,65 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
end
context 'when sentry is configured' do
+ let(:legacy_dsn) { 'dummy://abc@legacy-sentry.example.com/1' }
+ let(:dsn) { 'dummy://def@sentry.example.com/2' }
+
before do
- stub_sentry_settings
stub_config_setting(host: 'gitlab.example.com')
end
- it 'adds sentry path to CSP without user' do
- expect(directives['connect_src']).to eq("'self' ws://gitlab.example.com dummy://example.com")
+ context 'when legacy sentry is configured' do
+ before do
+ allow(Gitlab.config.sentry).to receive(:enabled).and_return(true)
+ allow(Gitlab.config.sentry).to receive(:clientside_dsn).and_return(legacy_dsn)
+ allow(Gitlab::CurrentSettings).to receive(:sentry_enabled).and_return(false)
+ end
+
+ it 'adds legacy sentry path to CSP' do
+ expect(directives['connect_src']).to eq("'self' ws://gitlab.example.com dummy://legacy-sentry.example.com")
+ end
+ end
+
+ context 'when sentry is configured' do
+ before do
+ allow(Gitlab.config.sentry).to receive(:enabled).and_return(false)
+ allow(Gitlab::CurrentSettings).to receive(:sentry_enabled).and_return(true)
+ allow(Gitlab::CurrentSettings).to receive(:sentry_clientside_dsn).and_return(dsn)
+ end
+
+ it 'adds new sentry path to CSP' do
+ expect(directives['connect_src']).to eq("'self' ws://gitlab.example.com dummy://sentry.example.com")
+ end
+ end
+
+ context 'when sentry settings are from older schemas and sentry setting are missing' do
+ before do
+ allow(Gitlab.config.sentry).to receive(:enabled).and_return(false)
+
+ allow(Gitlab::CurrentSettings).to receive(:respond_to?).with(:sentry_enabled).and_return(false)
+ allow(Gitlab::CurrentSettings).to receive(:sentry_enabled).and_raise(NoMethodError)
+
+ allow(Gitlab::CurrentSettings).to receive(:respond_to?).with(:sentry_clientside_dsn).and_return(false)
+ allow(Gitlab::CurrentSettings).to receive(:sentry_clientside_dsn).and_raise(NoMethodError)
+ end
+
+ it 'config is backwards compatible, does not add sentry path to CSP' do
+ expect(directives['connect_src']).to eq("'self' ws://gitlab.example.com")
+ end
+ end
+
+ context 'when legacy sentry and sentry are both configured' do
+ before do
+ allow(Gitlab.config.sentry).to receive(:enabled).and_return(true)
+ allow(Gitlab.config.sentry).to receive(:clientside_dsn).and_return(legacy_dsn)
+
+ allow(Gitlab::CurrentSettings).to receive(:sentry_enabled).and_return(true)
+ allow(Gitlab::CurrentSettings).to receive(:sentry_clientside_dsn).and_return(dsn)
+ end
+
+ it 'adds both sentry paths to CSP' do
+ expect(directives['connect_src']).to eq("'self' ws://gitlab.example.com dummy://legacy-sentry.example.com dummy://sentry.example.com")
+ end
end
end
diff --git a/spec/lib/gitlab/gon_helper_spec.rb b/spec/lib/gitlab/gon_helper_spec.rb
index 5a1fcc5e2dc..f8fb998b84d 100644
--- a/spec/lib/gitlab/gon_helper_spec.rb
+++ b/spec/lib/gitlab/gon_helper_spec.rb
@@ -44,7 +44,7 @@ RSpec.describe Gitlab::GonHelper do
let(:clientside_dsn) { 'https://xxx@sentry.example.com/1' }
let(:environment) { 'staging' }
- describe 'sentry integration' do
+ describe 'legacy sentry integration' do
before do
stub_config(sentry: { enabled: true, clientside_dsn: clientside_dsn, environment: environment })
end
@@ -57,7 +57,7 @@ RSpec.describe Gitlab::GonHelper do
end
end
- describe 'new sentry integration' do
+ describe 'sentry integration' do
before do
stub_application_setting(sentry_enabled: true)
stub_application_setting(sentry_clientside_dsn: clientside_dsn)
diff --git a/spec/lib/gitlab/memory/jemalloc_spec.rb b/spec/lib/gitlab/memory/jemalloc_spec.rb
index 9986af4bc13..8cce2278f8e 100644
--- a/spec/lib/gitlab/memory/jemalloc_spec.rb
+++ b/spec/lib/gitlab/memory/jemalloc_spec.rb
@@ -1,15 +1,14 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'tmpdir'
+require 'tempfile'
RSpec.describe Gitlab::Memory::Jemalloc do
- let(:outdir) { Dir.mktmpdir }
- let(:tmp_outdir) { Dir.mktmpdir }
+ let(:outfile) { Tempfile.new }
after do
- FileUtils.rm_f(outdir)
- FileUtils.rm_f(tmp_outdir)
+ outfile.close
+ outfile.unlink
end
context 'when jemalloc is loaded' do
@@ -31,12 +30,11 @@ RSpec.describe Gitlab::Memory::Jemalloc do
describe '.dump_stats' do
it 'writes stats JSON file' do
- file_path = described_class.dump_stats(path: outdir, tmp_dir: tmp_outdir, format: format)
+ described_class.dump_stats(outfile, format: format)
- file = Dir.entries(outdir).find { |e| e.match(/jemalloc_stats\..*\.json$/) }
- expect(file).not_to be_nil
- expect(file_path).to eq(File.join(outdir, file))
- expect(File.read(file_path)).to eq(output)
+ outfile.rewind
+
+ expect(outfile.read).to eq(output)
end
end
end
@@ -56,25 +54,12 @@ RSpec.describe Gitlab::Memory::Jemalloc do
end
describe '.dump_stats' do
- shared_examples 'writes stats text file' do |filename_label, filename_pattern|
- it do
- described_class.dump_stats(
- path: outdir, tmp_dir: tmp_outdir, format: format, filename_label: filename_label)
-
- file = Dir.entries(outdir).find { |e| e.match(filename_pattern) }
- expect(file).not_to be_nil
- expect(File.read(File.join(outdir, file))).to eq(output)
- end
- end
+ it 'writes stats text file' do
+ described_class.dump_stats(outfile, format: format)
- context 'when custom filename label is passed' do
- include_examples 'writes stats text file',
- 'puma_0.some-uuid',
- /jemalloc_stats\..*\.puma_0\.some-uuid\.txt$/
- end
+ outfile.rewind
- context 'when custom filename label is not passed' do
- include_examples 'writes stats text file', nil, /jemalloc_stats\..*\.txt$/
+ expect(outfile.read).to eq(output)
end
end
end
@@ -93,7 +78,7 @@ RSpec.describe Gitlab::Memory::Jemalloc do
describe '.dump_stats' do
it 'raises an error' do
expect do
- described_class.dump_stats(path: outdir, tmp_dir: tmp_outdir, format: format)
+ described_class.dump_stats(outfile, format: format)
end.to raise_error(/format must be one of/)
end
end
@@ -106,18 +91,18 @@ RSpec.describe Gitlab::Memory::Jemalloc do
end
describe '.stats' do
- it 'returns nil' do
- expect(described_class.stats).to be_nil
+ it 'returns empty string' do
+ expect(described_class.stats).to be_empty
end
end
describe '.dump_stats' do
it 'does nothing' do
- stub_env('LD_PRELOAD', nil)
+ described_class.dump_stats(outfile)
- described_class.dump_stats(path: outdir, tmp_dir: tmp_outdir)
+ outfile.rewind
- expect(Dir.empty?(outdir)).to be(true)
+ expect(outfile.read).to be_empty
end
end
end
diff --git a/spec/lib/gitlab/memory/reporter_spec.rb b/spec/lib/gitlab/memory/reporter_spec.rb
index 7924b37cb4b..ad6e556b3dd 100644
--- a/spec/lib/gitlab/memory/reporter_spec.rb
+++ b/spec/lib/gitlab/memory/reporter_spec.rb
@@ -3,75 +3,144 @@
require 'spec_helper'
RSpec.describe Gitlab::Memory::Reporter, :aggregate_failures do
- subject(:reporter) { described_class.new }
-
let(:fake_report) do
Class.new do
- attr_reader :did_run
-
def name
'fake_report'
end
- def run(report_id)
- @did_run = true
- '/path/to/report'
+ def run(writer)
+ writer << 'I ran'
end
end
end
+ let(:logger) { instance_double(::Logger) }
let(:report) { fake_report.new }
- describe '#run_report' do
+ after do
+ FileUtils.rm_rf(reports_path)
+ end
+
+ describe '#run_report', time_travel_to: '2020-02-02 10:30:45 0000' do
let(:report_duration_counter) { instance_double(::Prometheus::Client::Counter) }
let(:file_size) { 1_000_000 }
+ let(:report_file) { "#{reports_path}/fake_report.2020-02-02.10:30:45:000.worker_1.abc123" }
before do
+ allow(SecureRandom).to receive(:uuid).and_return('abc123')
+
allow(Gitlab::Metrics).to receive(:counter).and_return(report_duration_counter)
allow(report_duration_counter).to receive(:increment)
allow(::Prometheus::PidProvider).to receive(:worker_id).and_return('worker_1')
- allow(File).to receive(:size).with('/path/to/report').and_return(file_size)
+ allow(File).to receive(:size).with(report_file).and_return(file_size)
- allow(SecureRandom).to receive(:uuid).and_return('abc123')
+ allow(logger).to receive(:info)
end
- it 'runs the given report' do
- expect { reporter.run_report(report) }.to change { report.did_run }.from(nil).to(true)
- end
+ shared_examples 'runs and stores reports' do
+ it 'runs the given report and returns true' do
+ expect(reporter.run_report(report)).to be(true)
- it 'logs duration and other metrics' do
- expect(Gitlab::AppLogger).to receive(:info).with(
- hash_including(
- :duration_s,
- :cpu_s,
- perf_report_size_bytes: file_size,
- message: 'finished',
- pid: Process.pid,
- worker_id: 'worker_1',
- perf_report_worker_uuid: 'abc123',
- perf_report: 'fake_report'
- ))
-
- reporter.run_report(report)
+ expect(File.read(report_file)).to eq('I ran')
+ end
+
+ it 'logs start and finish event' do
+ expect(logger).to receive(:info).ordered.with(
+ hash_including(
+ message: 'started',
+ pid: Process.pid,
+ worker_id: 'worker_1',
+ perf_report_worker_uuid: 'abc123',
+ perf_report: 'fake_report'
+ ))
+ expect(logger).to receive(:info).ordered.with(
+ hash_including(
+ :duration_s,
+ :cpu_s,
+ perf_report_file: report_file,
+ perf_report_size_bytes: file_size,
+ message: 'finished',
+ pid: Process.pid,
+ worker_id: 'worker_1',
+ perf_report_worker_uuid: 'abc123',
+ perf_report: 'fake_report'
+ ))
+
+ reporter.run_report(report)
+ end
+
+ it 'increments Prometheus duration counter' do
+ expect(report_duration_counter).to receive(:increment).with({ report: 'fake_report' }, an_instance_of(Float))
+
+ reporter.run_report(report)
+ end
+
+ context 'when the report returns invalid file path' do
+ before do
+ allow(File).to receive(:size).with(report_file).and_raise(Errno::ENOENT)
+ end
+
+ it 'logs `0` as `perf_report_size_bytes`' do
+ expect(logger).to receive(:info).ordered.with(
+ hash_including(message: 'started')
+ )
+ expect(logger).to receive(:info).ordered.with(
+ hash_including(message: 'finished', perf_report_size_bytes: 0)
+ )
+
+ reporter.run_report(report)
+ end
+ end
+
+ context 'when an error occurs' do
+ before do
+ allow(report).to receive(:run).and_raise(RuntimeError.new('report failed'))
+ end
+
+ it 'logs the error and returns false' do
+ expect(logger).to receive(:info).ordered.with(hash_including(message: 'started'))
+ expect(logger).to receive(:error).ordered.with(
+ hash_including(
+ message: 'failed', error: '#<RuntimeError: report failed>'
+ ))
+
+ expect(reporter.run_report(report)).to be(false)
+ end
+ end
end
- it 'increments Prometheus duration counter' do
- expect(report_duration_counter).to receive(:increment).with({ report: 'fake_report' }, an_instance_of(Float))
+ context 'when reports path is specified directly' do
+ let(:reports_path) { Dir.mktmpdir }
+
+ subject(:reporter) { described_class.new(reports_path: reports_path, logger: logger) }
- reporter.run_report(report)
+ it_behaves_like 'runs and stores reports'
end
- context 'when the report returns invalid file path' do
+ context 'when reports path is specified via environment' do
+ let(:reports_path) { Dir.mktmpdir }
+
+ subject(:reporter) { described_class.new(logger: logger) }
+
before do
- allow(File).to receive(:size).with('/path/to/report').and_raise(Errno::ENOENT)
+ stub_env('GITLAB_DIAGNOSTIC_REPORTS_PATH', reports_path)
end
- it 'logs `0` as `perf_report_size_bytes`' do
- expect(Gitlab::AppLogger).to receive(:info).with(hash_including(perf_report_size_bytes: 0))
+ it_behaves_like 'runs and stores reports'
+ end
- reporter.run_report(report)
+ context 'when reports path is not specified' do
+ let(:reports_path) { reporter.reports_path }
+
+ subject(:reporter) { described_class.new(logger: logger) }
+
+ it 'defaults to a temporary location' do
+ expect(reports_path).not_to be_empty
end
+
+ it_behaves_like 'runs and stores reports'
end
end
end
diff --git a/spec/lib/gitlab/memory/reports/heap_dump_spec.rb b/spec/lib/gitlab/memory/reports/heap_dump_spec.rb
index 2532b8c5295..9cf455b3202 100644
--- a/spec/lib/gitlab/memory/reports/heap_dump_spec.rb
+++ b/spec/lib/gitlab/memory/reports/heap_dump_spec.rb
@@ -3,20 +3,34 @@
require 'spec_helper'
RSpec.describe Gitlab::Memory::Reports::HeapDump do
- describe '.write_conditionally' do
- subject(:call) { described_class.write_conditionally }
+ # Copy this class so we do not mess with its state.
+ let(:klass) { described_class.dup }
+
+ subject(:report) { klass.new }
+
+ describe '#name' do
+ # This is a bit silly, but it caused code coverage failures.
+ it 'is set' do
+ expect(report.name).to eq('heap_dump')
+ end
+ end
+
+ describe '#run' do
+ subject(:run) { report.run(writer) }
+
+ let(:writer) { StringIO.new }
context 'when no heap dump is enqueued' do
it 'does nothing and returns false' do
- expect(call).to be(false)
+ expect(run).to be(false)
end
end
context 'when a heap dump is enqueued' do
it 'does nothing and returns true' do
- described_class.enqueue!
+ klass.enqueue!
- expect(call).to be(true)
+ expect(run).to be(true)
end
end
end
diff --git a/spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb b/spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb
index ee4c6004c52..ce06c270a05 100644
--- a/spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb
+++ b/spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb
@@ -3,96 +3,16 @@
require 'spec_helper'
RSpec.describe Gitlab::Memory::Reports::JemallocStats do
- let_it_be(:outdir) { Dir.mktmpdir }
+ subject(:jemalloc_stats) { described_class.new }
- let(:jemalloc_stats) { described_class.new(reports_path: outdir) }
-
- after do
- FileUtils.rm_f(outdir)
- end
+ let(:writer) { StringIO.new }
describe '.run' do
context 'when :report_jemalloc_stats ops FF is enabled' do
- let(:worker_id) { 'puma_1' }
- let(:report_name) { 'report.json' }
- let(:report_path) { File.join(outdir, report_name) }
-
- before do
- allow(Prometheus::PidProvider).to receive(:worker_id).and_return(worker_id)
- end
-
- it 'invokes Jemalloc.dump_stats and returns file path' do
- expect(Gitlab::Memory::Jemalloc)
- .to receive(:dump_stats)
- .with(path: outdir,
- tmp_dir: File.join(outdir, '/tmp'),
- filename_label: 'test_report')
- .and_return(report_path)
-
- expect(jemalloc_stats.run('test_report')).to eq(report_path)
- end
-
- describe 'reports cleanup' do
- let(:jemalloc_stats) { described_class.new(reports_path: outdir) }
-
- before do
- stub_env('GITLAB_DIAGNOSTIC_REPORTS_JEMALLOC_MAX_REPORTS_STORED', 3)
- allow(Gitlab::Memory::Jemalloc).to receive(:dump_stats)
- end
-
- context 'when number of reports exceeds `max_reports_stored`' do
- let_it_be(:reports) do
- now = Time.current
-
- (1..5).map do |i|
- Tempfile.new("jemalloc_stats.#{i}.worker_#{i}.#{Time.current.to_i}.json", outdir).tap do |f|
- FileUtils.touch(f, mtime: (now + i.second).to_i)
- end
- end
- end
-
- after do
- reports.each do |f|
- f.close
- f.unlink
- rescue Errno::ENOENT
- # Some of the files are already unlinked by the code we test; Ignore
- end
- end
-
- it 'keeps only `max_reports_stored` total newest files' do
- expect { jemalloc_stats.run('test_report') }
- .to change { Dir.entries(outdir).count { |e| e.match(/jemalloc_stats.*/) } }
- .from(5).to(3)
-
- # Keeps only the newest reports
- expect(reports.last(3).all? { |r| File.exist?(r) }).to be true
- end
- end
-
- context 'when number of reports does not exceed `max_reports_stored`' do
- let_it_be(:reports) do
- now = Time.current
-
- (1..3).map do |i|
- Tempfile.new("jemalloc_stats.#{i}.worker_#{i}.#{Time.current.to_i}.json", outdir).tap do |f|
- FileUtils.touch(f, mtime: (now + i.second).to_i)
- end
- end
- end
-
- after do
- reports.each do |f|
- f.close
- f.unlink
- end
- end
+ it 'dumps jemalloc stats to the given writer' do
+ expect(Gitlab::Memory::Jemalloc).to receive(:dump_stats).with(writer)
- it 'does not remove any reports' do
- expect { jemalloc_stats.run('test_report') }
- .not_to change { Dir.entries(outdir).count { |e| e.match(/jemalloc_stats.*/) } }
- end
- end
+ jemalloc_stats.run(writer)
end
end
@@ -101,10 +21,10 @@ RSpec.describe Gitlab::Memory::Reports::JemallocStats do
stub_feature_flags(report_jemalloc_stats: false)
end
- it 'does not run the report and returns nil' do
+ it 'does not run the report' do
expect(Gitlab::Memory::Jemalloc).not_to receive(:dump_stats)
- expect(jemalloc_stats.run('test_report')).to be_nil
+ jemalloc_stats.run(writer)
end
end
end
diff --git a/spec/lib/gitlab/memory/reports_daemon_spec.rb b/spec/lib/gitlab/memory/reports_daemon_spec.rb
index b6be4eac919..91c36c87253 100644
--- a/spec/lib/gitlab/memory/reports_daemon_spec.rb
+++ b/spec/lib/gitlab/memory/reports_daemon_spec.rb
@@ -6,14 +6,8 @@ RSpec.describe Gitlab::Memory::ReportsDaemon, :aggregate_failures do
let(:reporter) { instance_double(Gitlab::Memory::Reporter) }
let(:reports) { nil }
- let_it_be(:tmp_dir) { Dir.mktmpdir }
-
subject(:daemon) { described_class.new(reporter: reporter, reports: reports) }
- after(:all) do
- FileUtils.remove_entry(tmp_dir)
- end
-
describe '#run_thread' do
before do
# make sleep no-op
@@ -58,8 +52,9 @@ RSpec.describe Gitlab::Memory::ReportsDaemon, :aggregate_failures do
context 'sleep timers logic' do
it 'wakes up every (fixed interval + defined delta), sleeps between reports each cycle' do
stub_env('GITLAB_DIAGNOSTIC_REPORTS_SLEEP_MAX_DELTA_S', 1) # rand(1) == 0, so we will have fixed sleep interval
- daemon = described_class.new
+ daemon = described_class.new(reporter: reporter, reports: reports)
allow(daemon).to receive(:alive).and_return(true, true, false)
+ allow(reporter).to receive(:run_report)
expect(daemon).to receive(:sleep).with(described_class::DEFAULT_SLEEP_S).ordered
expect(daemon).to receive(:sleep).with(described_class::DEFAULT_SLEEP_BETWEEN_REPORTS_S).ordered
@@ -85,7 +80,6 @@ RSpec.describe Gitlab::Memory::ReportsDaemon, :aggregate_failures do
expect(daemon.sleep_s).to eq(described_class::DEFAULT_SLEEP_S)
expect(daemon.sleep_max_delta_s).to eq(described_class::DEFAULT_SLEEP_MAX_DELTA_S)
expect(daemon.sleep_between_reports_s).to eq(described_class::DEFAULT_SLEEP_BETWEEN_REPORTS_S)
- expect(daemon.reports_path).to eq(described_class::DEFAULT_REPORTS_PATH)
end
end
@@ -94,7 +88,6 @@ RSpec.describe Gitlab::Memory::ReportsDaemon, :aggregate_failures do
stub_env('GITLAB_DIAGNOSTIC_REPORTS_SLEEP_S', 100)
stub_env('GITLAB_DIAGNOSTIC_REPORTS_SLEEP_MAX_DELTA_S', 50)
stub_env('GITLAB_DIAGNOSTIC_REPORTS_SLEEP_BETWEEN_REPORTS_S', 2)
- stub_env('GITLAB_DIAGNOSTIC_REPORTS_PATH', tmp_dir)
end
it 'uses provided values' do
@@ -103,7 +96,6 @@ RSpec.describe Gitlab::Memory::ReportsDaemon, :aggregate_failures do
expect(daemon.sleep_s).to eq(100)
expect(daemon.sleep_max_delta_s).to eq(50)
expect(daemon.sleep_between_reports_s).to eq(2)
- expect(daemon.reports_path).to eq(tmp_dir)
end
end
end
diff --git a/spec/lib/gitlab/metrics/rails_slis_spec.rb b/spec/lib/gitlab/metrics/rails_slis_spec.rb
index b30eb57101f..9da102fb8b8 100644
--- a/spec/lib/gitlab/metrics/rails_slis_spec.rb
+++ b/spec/lib/gitlab/metrics/rails_slis_spec.rb
@@ -14,8 +14,8 @@ RSpec.describe Gitlab::Metrics::RailsSlis do
end
describe '.initialize_request_slis!' do
- it "initializes the SLI for all possible endpoints if they weren't", :aggregate_failures do
- possible_labels = [
+ let(:possible_labels) do
+ [
{
endpoint_id: "GET /api/:version/version",
feature_category: :not_owned,
@@ -27,17 +27,32 @@ RSpec.describe Gitlab::Metrics::RailsSlis do
request_urgency: :default
}
]
+ end
- possible_graphql_labels = ['graphql:foo', 'graphql:bar', 'graphql:unknown'].map do |endpoint_id|
+ let(:possible_graphql_labels) do
+ ['graphql:foo', 'graphql:bar', 'graphql:unknown'].map do |endpoint_id|
{
endpoint_id: endpoint_id,
feature_category: nil,
query_urgency: ::Gitlab::EndpointAttributes::DEFAULT_URGENCY.name
}
end
+ end
+
+ it "initializes the SLI for all possible endpoints if they weren't", :aggregate_failures do
+ expect(Gitlab::Metrics::Sli::Apdex).to receive(:initialize_sli).with(:rails_request, array_including(*possible_labels)).and_call_original
+ expect(Gitlab::Metrics::Sli::Apdex).to receive(:initialize_sli).with(:graphql_query, array_including(*possible_graphql_labels)).and_call_original
+ expect(Gitlab::Metrics::Sli::ErrorRate).to receive(:initialize_sli).with(:rails_request, array_including(*possible_labels)).and_call_original
+
+ described_class.initialize_request_slis!
+ end
+
+ it "initializes the SLI for all possible endpoints if they weren't given error rate feature flag is disabled", :aggregate_failures do
+ stub_feature_flags(gitlab_metrics_error_rate_sli: false)
expect(Gitlab::Metrics::Sli::Apdex).to receive(:initialize_sli).with(:rails_request, array_including(*possible_labels)).and_call_original
expect(Gitlab::Metrics::Sli::Apdex).to receive(:initialize_sli).with(:graphql_query, array_including(*possible_graphql_labels)).and_call_original
+ expect(Gitlab::Metrics::Sli::ErrorRate).not_to receive(:initialize_sli)
described_class.initialize_request_slis!
end
@@ -51,6 +66,14 @@ RSpec.describe Gitlab::Metrics::RailsSlis do
end
end
+ describe '.request_error' do
+ it 'returns the initialized request error rate SLI object' do
+ described_class.initialize_request_slis!
+
+ expect(described_class.request_error_rate).to be_initialized
+ end
+ end
+
describe '.graphql_query_apdex' do
it 'returns the initialized request apdex SLI object' do
described_class.initialize_request_slis!
diff --git a/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb b/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb
index ed78548ef62..56ba880c906 100644
--- a/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb
+++ b/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb
@@ -38,6 +38,20 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
expect(described_class).to receive_message_chain(:http_request_duration_seconds, :observe).with({ method: 'get' }, a_positive_execution_time)
expect(Gitlab::Metrics::RailsSlis.request_apdex).to receive(:increment)
.with(labels: { feature_category: 'unknown', endpoint_id: 'unknown', request_urgency: :default }, success: true)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment)
+ .with(labels: { feature_category: 'unknown', endpoint_id: 'unknown' }, error: false)
+
+ subject.call(env)
+ end
+
+ it 'does not track error rate when feature flag is disabled' do
+ stub_feature_flags(gitlab_metrics_error_rate_sli: false)
+
+ expect(described_class).to receive_message_chain(:http_requests_total, :increment).with(method: 'get', status: '200', feature_category: 'unknown')
+ expect(described_class).to receive_message_chain(:http_request_duration_seconds, :observe).with({ method: 'get' }, a_positive_execution_time)
+ expect(Gitlab::Metrics::RailsSlis.request_apdex).to receive(:increment)
+ .with(labels: { feature_category: 'unknown', endpoint_id: 'unknown', request_urgency: :default }, success: true)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).not_to receive(:increment)
subject.call(env)
end
@@ -84,10 +98,23 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
context '@app.call returns an error code' do
let(:status) { '500' }
- it 'tracks count but not duration or apdex' do
+ it 'tracks count and error rate but not duration and apdex' do
expect(described_class).to receive_message_chain(:http_requests_total, :increment).with(method: 'get', status: '500', feature_category: 'unknown')
expect(described_class).not_to receive(:http_request_duration_seconds)
expect(Gitlab::Metrics::RailsSlis).not_to receive(:request_apdex)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment)
+ .with(labels: { feature_category: 'unknown', endpoint_id: 'unknown' }, error: true)
+
+ subject.call(env)
+ end
+
+ it 'does not track error rate when feature flag is disabled' do
+ stub_feature_flags(gitlab_metrics_error_rate_sli: false)
+
+ expect(described_class).to receive_message_chain(:http_requests_total, :increment).with(method: 'get', status: '500', feature_category: 'unknown')
+ expect(described_class).not_to receive(:http_request_duration_seconds)
+ expect(Gitlab::Metrics::RailsSlis).not_to receive(:request_apdex)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).not_to receive(:increment)
subject.call(env)
end
@@ -108,6 +135,7 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
expect(described_class).to receive_message_chain(:http_requests_total, :increment).with(method: 'get', status: 'undefined', feature_category: 'unknown')
expect(described_class.http_request_duration_seconds).not_to receive(:observe)
expect(Gitlab::Metrics::RailsSlis).not_to receive(:request_apdex)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).not_to receive(:increment)
expect { subject.call(env) }.to raise_error(StandardError)
end
@@ -124,6 +152,8 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
expect(described_class).not_to receive(:http_health_requests_total)
expect(Gitlab::Metrics::RailsSlis.request_apdex)
.to receive(:increment).with(labels: { feature_category: 'team_planning', endpoint_id: 'IssuesController#show', request_urgency: :default }, success: true)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment)
+ .with(labels: { feature_category: 'team_planning', endpoint_id: 'IssuesController#show' }, error: false)
subject.call(env)
end
@@ -134,6 +164,7 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
expect(described_class).to receive_message_chain(:http_health_requests_total, :increment).with(method: 'get', status: '200')
expect(described_class).not_to receive(:http_requests_total)
expect(Gitlab::Metrics::RailsSlis).not_to receive(:request_apdex)
+ expect(Gitlab::Metrics::RailsSlis).not_to receive(:request_error_rate)
subject.call(env)
end
@@ -147,8 +178,9 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
it 'adds the feature category to the labels for http_requests_total' do
expect(described_class).to receive_message_chain(:http_requests_total, :increment).with(method: 'get', status: 'undefined', feature_category: 'team_planning')
- expect(Gitlab::Metrics::RailsSlis).not_to receive(:request_apdex)
+ expect(Gitlab::Metrics::RailsSlis).not_to receive(:request_apdex)
+ expect(Gitlab::Metrics::RailsSlis).not_to receive(:request_error_rate)
expect { subject.call(env) }.to raise_error(StandardError)
end
end
@@ -159,6 +191,8 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
expect(described_class).not_to receive(:http_health_requests_total)
expect(Gitlab::Metrics::RailsSlis.request_apdex).to receive(:increment)
.with(labels: { feature_category: 'unknown', endpoint_id: 'unknown', request_urgency: :default }, success: true)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment)
+ .with(labels: { feature_category: 'unknown', endpoint_id: 'unknown' }, error: false)
subject.call(env)
end
@@ -214,6 +248,14 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
},
success: success
)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment).with(
+ labels: {
+ feature_category: 'hello_world',
+ endpoint_id: 'GET /projects/:id/archive'
+ },
+ error: false
+ )
+
subject.call(env)
end
end
@@ -247,6 +289,14 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
},
success: success
)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment).with(
+ labels: {
+ feature_category: 'hello_world',
+ endpoint_id: 'AnonymousController#index'
+ },
+ error: false
+ )
+
subject.call(env)
end
end
@@ -273,6 +323,13 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
},
success: true
)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment).with(
+ labels: {
+ feature_category: 'unknown',
+ endpoint_id: 'unknown'
+ },
+ error: false
+ )
subject.call(env)
allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(100, 101)
@@ -284,6 +341,13 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
},
success: false
)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment).with(
+ labels: {
+ feature_category: 'unknown',
+ endpoint_id: 'unknown'
+ },
+ error: false
+ )
subject.call(env)
end
end
@@ -307,6 +371,13 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
},
success: true
)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment).with(
+ labels: {
+ feature_category: 'unknown',
+ endpoint_id: 'unknown'
+ },
+ error: false
+ )
subject.call(env)
allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(100, 101)
@@ -318,6 +389,13 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
},
success: false
)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment).with(
+ labels: {
+ feature_category: 'unknown',
+ endpoint_id: 'unknown'
+ },
+ error: false
+ )
subject.call(env)
end
end
@@ -337,6 +415,13 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
},
success: true
)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment).with(
+ labels: {
+ feature_category: 'unknown',
+ endpoint_id: 'unknown'
+ },
+ error: false
+ )
subject.call(env)
allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(100, 101)
@@ -348,6 +433,13 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
},
success: false
)
+ expect(Gitlab::Metrics::RailsSlis.request_error_rate).to receive(:increment).with(
+ labels: {
+ feature_category: 'unknown',
+ endpoint_id: 'unknown'
+ },
+ error: false
+ )
subject.call(env)
end
end
diff --git a/spec/lib/gitlab/metrics_spec.rb b/spec/lib/gitlab/metrics_spec.rb
index 366843a4c03..dbd6c07ef75 100644
--- a/spec/lib/gitlab/metrics_spec.rb
+++ b/spec/lib/gitlab/metrics_spec.rb
@@ -101,14 +101,32 @@ RSpec.describe Gitlab::Metrics do
401 | true
nil | false
500 | false
- 503 | false
- '100' | false
- '201' | true
+ 503 | false
'nothing' | false
end
with_them do
specify { expect(described_class.record_duration_for_status?(status)).to be(should_record) }
+ specify { expect(described_class.record_duration_for_status?(status.to_s)).to be(should_record) }
+ end
+ end
+
+ describe '.server_error?' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:status, :should_record) do
+ 100 | false
+ 200 | false
+ 401 | false
+ 500 | true
+ 503 | true
+ nil | false
+ 'nothing' | false
+ end
+
+ with_them do
+ specify { expect(described_class.server_error?(status)).to be(should_record) }
+ specify { expect(described_class.server_error?(status.to_s)).to be(should_record) }
end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index c03bb61f1f8..52d35f6d79d 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -1753,8 +1753,8 @@ RSpec.describe Ci::Build do
end
end
- describe '#starts_environment?' do
- subject { build.starts_environment? }
+ describe '#deployment_job?' do
+ subject { build.deployment_job? }
context 'when environment is defined' do
before do
diff --git a/spec/requests/api/container_repositories_spec.rb b/spec/requests/api/container_repositories_spec.rb
index 90f0243dbfc..0f0c88bef74 100644
--- a/spec/requests/api/container_repositories_spec.rb
+++ b/spec/requests/api/container_repositories_spec.rb
@@ -75,6 +75,13 @@ RSpec.describe API::ContainerRepositories do
expect(json_response['id']).to eq(repository.id)
expect(response.body).to include('tags')
+ expect(json_response['tags']).to eq(repository.tags.map do |tag|
+ {
+ "location" => tag.location,
+ "name" => tag.name,
+ "path" => tag.path
+ }
+ end)
end
context 'with a network error' do
diff --git a/spec/requests/jira_connect/subscriptions_controller_spec.rb b/spec/requests/jira_connect/subscriptions_controller_spec.rb
index 73a825d471a..1a47c9d8139 100644
--- a/spec/requests/jira_connect/subscriptions_controller_spec.rb
+++ b/spec/requests/jira_connect/subscriptions_controller_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe JiraConnect::SubscriptionsController do
+RSpec.describe JiraConnect::SubscriptionsController, feature_category: :integrations do
describe 'GET /-/jira_connect/subscriptions' do
let_it_be(:installation) { create(:jira_connect_installation, instance_url: 'http://self-managed-gitlab.com') }
let(:qsh) do
diff --git a/yarn.lock b/yarn.lock
index 82f0d01c399..27a6deff96a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1651,16 +1651,6 @@
resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-6.1.4-7.tgz#ef0b83ef40f64bc6704e13ae6624236a4a91fa6f"
integrity sha512-842WcLh0BErNgGE8rdqNh31VnqGQcklPQ7RXzQfA0ilQNZcU7AO+t576g1m//18Lk8m7cXZ8fIKA1YB41LKWAQ==
-"@sentry/browser@5.30.0":
- version "5.30.0"
- resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.30.0.tgz#c28f49d551db3172080caef9f18791a7fd39e3b3"
- integrity sha512-rOb58ZNVJWh1VuMuBG1mL9r54nZqKeaIlwSlvzJfc89vyfd7n6tQ1UXMN383QBz/MS5H5z44Hy5eE+7pCrYAfw==
- dependencies:
- "@sentry/core" "5.30.0"
- "@sentry/types" "5.30.0"
- "@sentry/utils" "5.30.0"
- tslib "^1.9.3"
-
"@sentry/core@5.30.0":
version "5.30.0"
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.30.0.tgz#6b203664f69e75106ee8b5a2fe1d717379b331f3"
@@ -1672,6 +1662,15 @@
"@sentry/utils" "5.30.0"
tslib "^1.9.3"
+"@sentry/core@7.21.1":
+ version "7.21.1"
+ resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.21.1.tgz#d0423282d90875625802dfe380f9657e9242b72b"
+ integrity sha512-Og5wEEsy24fNvT/T7IKjcV4EvVK5ryY2kxbJzKY6GU2eX+i+aBl+n/vp7U0Es351C/AlTkS+0NOUsp2TQQFxZA==
+ dependencies:
+ "@sentry/types" "7.21.1"
+ "@sentry/utils" "7.21.1"
+ tslib "^1.9.3"
+
"@sentry/hub@5.30.0":
version "5.30.0"
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.30.0.tgz#2453be9b9cb903404366e198bd30c7ca74cdc100"
@@ -1695,6 +1694,11 @@
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.30.0.tgz#19709bbe12a1a0115bc790b8942917da5636f402"
integrity sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==
+"@sentry/types@7.21.1":
+ version "7.21.1"
+ resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.21.1.tgz#408a7b95a66ddc30c4359979594e03bee8f9fbdc"
+ integrity sha512-3/IKnd52Ol21amQvI+kz+WB76s8/LR5YvFJzMgIoI2S8d82smIr253zGijRXxHPEif8kMLX4Yt+36VzrLxg6+A==
+
"@sentry/utils@5.30.0":
version "5.30.0"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.30.0.tgz#9a5bd7ccff85ccfe7856d493bffa64cabc41e980"
@@ -1703,6 +1707,14 @@
"@sentry/types" "5.30.0"
tslib "^1.9.3"
+"@sentry/utils@7.21.1":
+ version "7.21.1"
+ resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.21.1.tgz#96582345178015fd32fe9159c25c44ccf2f99d2a"
+ integrity sha512-F0W0AAi8tgtTx6ApZRI2S9HbXEA9ENX1phTZgdNNWcMFm1BNbc21XEwLqwXBNjub5nlA6CE8xnjXRgdZKx4kzQ==
+ dependencies:
+ "@sentry/types" "7.21.1"
+ tslib "^1.9.3"
+
"@sinonjs/commons@^1.7.0":
version "1.8.1"
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217"
@@ -10882,6 +10894,26 @@ send@0.17.2:
range-parser "~1.2.1"
statuses "~1.5.0"
+"sentrybrowser5@npm:@sentry/browser@5.30.0":
+ version "5.30.0"
+ resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-5.30.0.tgz#c28f49d551db3172080caef9f18791a7fd39e3b3"
+ integrity sha512-rOb58ZNVJWh1VuMuBG1mL9r54nZqKeaIlwSlvzJfc89vyfd7n6tQ1UXMN383QBz/MS5H5z44Hy5eE+7pCrYAfw==
+ dependencies:
+ "@sentry/core" "5.30.0"
+ "@sentry/types" "5.30.0"
+ "@sentry/utils" "5.30.0"
+ tslib "^1.9.3"
+
+"sentrybrowser7@npm:@sentry/browser@^7.21.1":
+ version "7.21.1"
+ resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.21.1.tgz#bffa3ea19050c06400107d2297b9802f9719f98b"
+ integrity sha512-cS2Jz2+fs9+4pJqLJPtYqGyY97ywJDWAWIR1Yla3hs1QQuH6m0Nz3ojZD1gE2eKH9mHwkGbnNAh+hHcrYrfGzw==
+ dependencies:
+ "@sentry/core" "7.21.1"
+ "@sentry/types" "7.21.1"
+ "@sentry/utils" "7.21.1"
+ tslib "^1.9.3"
+
serialize-javascript@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61"