diff --git a/.rubocop_todo/gitlab/bounded_contexts.yml b/.rubocop_todo/gitlab/bounded_contexts.yml index 4264c369266..cb5b74c2e23 100644 --- a/.rubocop_todo/gitlab/bounded_contexts.yml +++ b/.rubocop_todo/gitlab/bounded_contexts.yml @@ -2694,7 +2694,6 @@ Gitlab/BoundedContexts: - 'ee/app/models/concerns/epics/metadata_cache_update.rb' - 'ee/app/models/concerns/filterable_by_test_reports.rb' - 'ee/app/models/concerns/health_status.rb' - - 'ee/app/models/concerns/singleton_record.rb' - 'ee/app/models/concerns/identity_verifiable.rb' - 'ee/app/models/concerns/incident_management/base_pending_escalation.rb' - 'ee/app/models/concerns/insights_feature.rb' @@ -2705,6 +2704,7 @@ Gitlab/BoundedContexts: - 'ee/app/models/concerns/product_analytics/schema_validator.rb' - 'ee/app/models/concerns/product_analytics_helpers.rb' - 'ee/app/models/concerns/scim_paginatable.rb' + - 'ee/app/models/concerns/singleton_record.rb' - 'ee/app/models/concerns/visible_approvable.rb' - 'ee/app/models/concerns/vulnerability_scopes.rb' - 'ee/app/models/dast/branch.rb' diff --git a/.rubocop_todo/gitlab/no_find_in_workers.yml b/.rubocop_todo/gitlab/no_find_in_workers.yml index 858204ad39c..90995a155a6 100644 --- a/.rubocop_todo/gitlab/no_find_in_workers.yml +++ b/.rubocop_todo/gitlab/no_find_in_workers.yml @@ -77,7 +77,6 @@ Gitlab/NoFindInWorkers: - 'ee/app/workers/create_github_webhook_worker.rb' - 'ee/app/workers/dependencies/export_worker.rb' - 'ee/app/workers/elastic/namespace_update_worker.rb' - - 'ee/app/workers/elastic/project_transfer_worker.rb' - 'ee/app/workers/gitlab/export/segmented_export_finalisation_worker.rb' - 'ee/app/workers/gitlab/export/segmented_export_worker.rb' - 'ee/app/workers/group_wikis/git_garbage_collect_worker.rb' diff --git a/.rubocop_todo/gitlab/rspec/avoid_create_default_organization.yml b/.rubocop_todo/gitlab/rspec/avoid_create_default_organization.yml index 1ac807fad09..63528fadac7 100644 --- a/.rubocop_todo/gitlab/rspec/avoid_create_default_organization.yml +++ b/.rubocop_todo/gitlab/rspec/avoid_create_default_organization.yml @@ -3,8 +3,6 @@ Gitlab/RSpec/AvoidCreateDefaultOrganization: Details: grace period Exclude: - 'ee/spec/finders/ai/code_suggestion_events_finder_spec.rb' - - 'ee/spec/lib/gitlab/auth/smartcard/ldap_certificate_spec.rb' - - 'ee/spec/requests/smartcard_controller_spec.rb' - 'spec/features/user_settings/personal_access_tokens_spec.rb' - 'spec/helpers/organizations/organization_helper_spec.rb' - 'spec/lib/gitlab/current/organization_spec.rb' diff --git a/.rubocop_todo/gitlab/rspec/misplaced_ee_spec_file.yml b/.rubocop_todo/gitlab/rspec/misplaced_ee_spec_file.yml index 1540f6c98b4..b423bb631aa 100644 --- a/.rubocop_todo/gitlab/rspec/misplaced_ee_spec_file.yml +++ b/.rubocop_todo/gitlab/rspec/misplaced_ee_spec_file.yml @@ -65,6 +65,7 @@ Gitlab/RSpec/MisplacedEeSpecFile: - 'ee/spec/graphql/types/gitlab_subscriptions/member_management/users_queued_for_role_promotion_type_spec.rb' - 'ee/spec/graphql/types/issue_connection_type_spec.rb' - 'ee/spec/graphql/types/issue_type_spec.rb' + - 'ee/spec/graphql/types/namespaces/user_level_permissions/group_namespace_user_level_permissions_type_spec.rb' - 'ee/spec/graphql/types/permission_types/group_spec.rb' - 'ee/spec/graphql/types/permission_types/namespaces/base_spec.rb' - 'ee/spec/graphql/types/permission_types/project_spec.rb' diff --git a/.rubocop_todo/layout/empty_line_after_magic_comment.yml b/.rubocop_todo/layout/empty_line_after_magic_comment.yml index 255a9114ddf..5027b466caf 100644 --- a/.rubocop_todo/layout/empty_line_after_magic_comment.yml +++ b/.rubocop_todo/layout/empty_line_after_magic_comment.yml @@ -305,7 +305,6 @@ Layout/EmptyLineAfterMagicComment: - 'lib/gitlab/auth/blocked_user_tracker.rb' - 'lib/gitlab/auth/otp/fortinet.rb' - 'lib/gitlab/ci/secure_files/mobile_provision.rb' - - 'lib/gitlab/cleanup/remote_uploads.rb' - 'lib/gitlab/database/migrations/background_migration_helpers.rb' - 'lib/gitlab/database/partitioning/detached_partition_dropper.rb' - 'lib/gitlab/diff/highlight_cache.rb' diff --git a/.rubocop_todo/layout/line_end_string_concatenation_indentation.yml b/.rubocop_todo/layout/line_end_string_concatenation_indentation.yml index 6a7791e4120..9ef8b0425ea 100644 --- a/.rubocop_todo/layout/line_end_string_concatenation_indentation.yml +++ b/.rubocop_todo/layout/line_end_string_concatenation_indentation.yml @@ -387,7 +387,6 @@ Layout/LineEndStringConcatenationIndentation: - 'qa/qa/scenario/shared_attributes.rb' - 'qa/qa/service/docker_run/product_analytics/browser_sdk_app.rb' - 'qa/qa/service/docker_run/product_analytics/dotnet_sdk_app.rb' - - 'qa/qa/specs/features/ee/api/10_govern/compliance_pipeline_spec.rb' - 'qa/qa/support/system_logs/kibana.rb' - 'qa/qa/tools/revoke_user_personal_access_tokens.rb' - 'qa/qa/tools/test_resources_handler.rb' diff --git a/.rubocop_todo/layout/line_length.yml b/.rubocop_todo/layout/line_length.yml index 7a551684212..1e39e713099 100644 --- a/.rubocop_todo/layout/line_length.yml +++ b/.rubocop_todo/layout/line_length.yml @@ -46,7 +46,6 @@ Layout/LineLength: - 'app/graphql/types/repository/blob_type.rb' - 'app/graphql/types/repository_type.rb' - 'app/graphql/types/snippets/visibility_scopes_enum.rb' - - 'app/graphql/types/todo_action_enum.rb' - 'app/helpers/application_helper.rb' - 'app/helpers/application_settings_helper.rb' - 'app/helpers/award_emoji_helper.rb' @@ -1827,7 +1826,6 @@ Layout/LineLength: - 'ee/spec/workers/iterations/roll_over_issues_worker_spec.rb' - 'ee/spec/workers/iterations_update_status_worker_spec.rb' - 'ee/spec/workers/merge_request_reset_approvals_worker_spec.rb' - - 'ee/spec/workers/new_epic_worker_spec.rb' - 'ee/spec/workers/repository_import_worker_spec.rb' - 'ee/spec/workers/security/orchestration_policy_rule_schedule_namespace_worker_spec.rb' - 'ee/spec/workers/security/orchestration_policy_rule_schedule_worker_spec.rb' @@ -1982,7 +1980,6 @@ Layout/LineLength: - 'lib/gitlab/auth/ldap/dn.rb' - 'lib/gitlab/auth/ldap/person.rb' - 'lib/gitlab/auth/o_auth/user.rb' - - 'lib/gitlab/auth/saml/auth_hash.rb' - 'lib/gitlab/auth/user_access_denied_reason.rb' - 'lib/gitlab/background_migration/backfill_issue_search_data.rb' - 'lib/gitlab/background_migration/backfill_iteration_cadence_id_for_boards.rb' diff --git a/.rubocop_todo/lint/empty_block.yml b/.rubocop_todo/lint/empty_block.yml index f5a6ba750cc..c46ac8f7879 100644 --- a/.rubocop_todo/lint/empty_block.yml +++ b/.rubocop_todo/lint/empty_block.yml @@ -87,7 +87,6 @@ Lint/EmptyBlock: - 'spec/lib/gitlab/database/shared_model_spec.rb' - 'spec/lib/gitlab/database/with_lock_retries_outside_transaction_spec.rb' - 'spec/lib/gitlab/database/with_lock_retries_spec.rb' - - 'spec/lib/gitlab/database_spec.rb' - 'spec/lib/gitlab/etag_caching/router/graphql_spec.rb' - 'spec/lib/gitlab/exclusive_lease_helpers_spec.rb' - 'spec/lib/gitlab/exclusive_lease_spec.rb' diff --git a/.rubocop_todo/lint/unused_method_argument.yml b/.rubocop_todo/lint/unused_method_argument.yml index 1012c897477..7ee25a52041 100644 --- a/.rubocop_todo/lint/unused_method_argument.yml +++ b/.rubocop_todo/lint/unused_method_argument.yml @@ -66,7 +66,6 @@ Lint/UnusedMethodArgument: - 'app/helpers/environment_helper.rb' - 'app/helpers/groups/group_members_helper.rb' - 'app/helpers/groups_helper.rb' - - 'app/helpers/invite_members_helper.rb' - 'app/helpers/issuables_helper.rb' - 'app/helpers/issues_helper.rb' - 'app/helpers/labels_helper.rb' diff --git a/.rubocop_todo/performance/map_compact.yml b/.rubocop_todo/performance/map_compact.yml index 4fe19051aa7..c83edc2b5c7 100644 --- a/.rubocop_todo/performance/map_compact.yml +++ b/.rubocop_todo/performance/map_compact.yml @@ -39,7 +39,6 @@ Performance/MapCompact: - 'config/initializers/trusted_proxies.rb' - 'config/initializers/wikicloth_redos_patch.rb' - 'ee/app/graphql/ee/types/issue_connection_type.rb' - - 'ee/app/graphql/resolvers/geo/registries_resolver.rb' - 'ee/app/models/app_sec/fuzzing/api/ci_configuration.rb' - 'ee/app/models/burndown.rb' - 'ee/app/models/concerns/ee/project_security_scanners_information.rb' diff --git a/.rubocop_todo/rails/date.yml b/.rubocop_todo/rails/date.yml index ba4cb28d459..f0815b13715 100644 --- a/.rubocop_todo/rails/date.yml +++ b/.rubocop_todo/rails/date.yml @@ -74,19 +74,14 @@ Rails/Date: - 'ee/spec/models/gitlab_subscription_spec.rb' - 'ee/spec/models/iteration_spec.rb' - 'ee/spec/requests/api/group_milestones_spec.rb' - - 'ee/spec/requests/api/group_service_accounts_spec.rb' - 'ee/spec/requests/api/issues_spec.rb' - 'ee/spec/requests/api/license_spec.rb' - 'ee/spec/requests/api/members_spec.rb' - 'ee/spec/requests/api/namespaces_spec.rb' - 'ee/spec/requests/api/project_milestones_spec.rb' - - 'ee/spec/requests/custom_roles/manage_group_access_tokens/request_spec.rb' - - 'ee/spec/requests/custom_roles/manage_project_access_tokens/request_spec.rb' - 'ee/spec/requests/git_http_spec.rb' - 'ee/spec/requests/gitlab_subscriptions/api/internal/upcoming_reconciliations_spec.rb' - 'ee/spec/requests/gitlab_subscriptions/api/internal/users_spec.rb' - - 'ee/spec/requests/groups/settings/access_tokens_controller_spec.rb' - - 'ee/spec/requests/projects/settings/access_tokens_controller_spec.rb' - 'ee/spec/services/app_sec/dast/profile_schedules/audit/update_service_spec.rb' - 'ee/spec/services/audit_event_service_spec.rb' - 'ee/spec/services/ci/minutes/additional_packs/create_service_spec.rb' @@ -180,15 +175,11 @@ Rails/Date: - 'spec/requests/api/graphql/mutations/work_items/update_spec.rb' - 'spec/requests/api/graphql/work_item_spec.rb' - 'spec/requests/api/internal/base_spec.rb' - - 'spec/requests/api/personal_access_tokens/self_rotation_spec.rb' - 'spec/requests/api/personal_access_tokens_spec.rb' - 'spec/requests/api/project_statistics_spec.rb' - - 'spec/requests/api/resource_access_tokens_spec.rb' - 'spec/requests/api/users_spec.rb' - 'spec/requests/git_http_spec.rb' - - 'spec/requests/groups/settings/access_tokens_controller_spec.rb' - 'spec/requests/jwt_controller_spec.rb' - - 'spec/requests/projects/settings/access_tokens_controller_spec.rb' - 'spec/requests/verifies_with_email_spec.rb' - 'spec/serializers/entity_date_helper_spec.rb' - 'spec/serializers/member_user_entity_spec.rb' diff --git a/.rubocop_todo/rails/helper_instance_variable.yml b/.rubocop_todo/rails/helper_instance_variable.yml index c4812f05ce4..84a3f273415 100644 --- a/.rubocop_todo/rails/helper_instance_variable.yml +++ b/.rubocop_todo/rails/helper_instance_variable.yml @@ -8,7 +8,6 @@ Rails/HelperInstanceVariable: - 'app/helpers/award_emoji_helper.rb' - 'app/helpers/blob_helper.rb' - 'app/helpers/boards_helper.rb' - - 'app/helpers/branches_helper.rb' - 'app/helpers/breadcrumbs_helper.rb' - 'app/helpers/ci/builds_helper.rb' - 'app/helpers/commits_helper.rb' diff --git a/.rubocop_todo/rspec/be_eq.yml b/.rubocop_todo/rspec/be_eq.yml index a868fa4698f..5da3ebaa734 100644 --- a/.rubocop_todo/rspec/be_eq.yml +++ b/.rubocop_todo/rspec/be_eq.yml @@ -7,7 +7,6 @@ RSpec/BeEq: - 'ee/spec/controllers/concerns/ee/routable_actions/sso_enforcement_redirect_spec.rb' - 'ee/spec/controllers/concerns/routable_actions_spec.rb' - 'ee/spec/controllers/ee/groups_controller_spec.rb' - - 'ee/spec/controllers/ee/profiles/preferences_controller_spec.rb' - 'ee/spec/controllers/ee/projects/blob_controller_spec.rb' - 'ee/spec/controllers/ee/projects/jobs_controller_spec.rb' - 'ee/spec/controllers/ee/projects/pages_controller_spec.rb' @@ -26,7 +25,6 @@ RSpec/BeEq: - 'ee/spec/features/admin/admin_emails_spec.rb' - 'ee/spec/features/admin/admin_settings_spec.rb' - 'ee/spec/features/admin/users/users_spec.rb' - - 'ee/spec/features/epics/epic_work_item_sync_spec.rb' - 'ee/spec/features/groups/settings/reporting_spec.rb' - 'ee/spec/features/issues/user_bulk_edits_issues_spec.rb' - 'ee/spec/features/projects/new_project_spec.rb' diff --git a/.rubocop_todo/rspec/context_wording.yml b/.rubocop_todo/rspec/context_wording.yml index e8311e30340..ae84da0e357 100644 --- a/.rubocop_todo/rspec/context_wording.yml +++ b/.rubocop_todo/rspec/context_wording.yml @@ -781,7 +781,6 @@ RSpec/ContextWording: - 'ee/spec/workers/incident_management/apply_incident_sla_exceeded_label_worker_spec.rb' - 'ee/spec/workers/incident_management/oncall_rotations/persist_shifts_job_spec.rb' - 'ee/spec/workers/merge_request_reset_approvals_worker_spec.rb' - - 'ee/spec/workers/new_epic_worker_spec.rb' - 'ee/spec/workers/personal_access_tokens/groups/policy_worker_spec.rb' - 'ee/spec/workers/personal_access_tokens/instance/policy_worker_spec.rb' - 'ee/spec/workers/post_receive_spec.rb' diff --git a/.rubocop_todo/rspec/feature_category.yml b/.rubocop_todo/rspec/feature_category.yml index 0b112a57a92..eda55ff10ab 100644 --- a/.rubocop_todo/rspec/feature_category.yml +++ b/.rubocop_todo/rspec/feature_category.yml @@ -92,7 +92,6 @@ RSpec/FeatureCategory: - 'ee/spec/graphql/ee/types/milestone_type_spec.rb' - 'ee/spec/graphql/ee/types/mutation_type_spec.rb' - 'ee/spec/graphql/ee/types/notes/noteable_interface_spec.rb' - - 'ee/spec/graphql/ee/types/repository/blob_type_spec.rb' - 'ee/spec/graphql/ee/types/user_merge_request_interaction_type_spec.rb' - 'ee/spec/graphql/mutations/app_sec/fuzzing/coverage/corpus/create_spec.rb' - 'ee/spec/graphql/mutations/audit_events/streaming/headers/destroy_spec.rb' @@ -928,7 +927,6 @@ RSpec/FeatureCategory: - 'ee/spec/services/app_sec/fuzzing/api/ci_configuration_create_service_spec.rb' - 'ee/spec/services/app_sec/fuzzing/coverage/corpuses/create_service_spec.rb' - 'ee/spec/services/application_settings/update_service_spec.rb' - - 'ee/spec/services/applications/create_service_spec.rb' - 'ee/spec/services/approval_rules/finalize_service_spec.rb' - 'ee/spec/services/approval_rules/merge_request_rule_destroy_service_spec.rb' - 'ee/spec/services/approval_rules/params_filtering_service_spec.rb' @@ -2979,10 +2977,7 @@ RSpec/FeatureCategory: - 'spec/lib/safe_zip/entry_spec.rb' - 'spec/lib/safe_zip/extract_params_spec.rb' - 'spec/lib/safe_zip/extract_spec.rb' - - 'spec/lib/security/ci_configuration/container_scanning_build_action_spec.rb' - 'spec/lib/security/ci_configuration/sast_build_action_spec.rb' - - 'spec/lib/security/ci_configuration/sast_iac_build_action_spec.rb' - - 'spec/lib/security/ci_configuration/secret_detection_build_action_spec.rb' - 'spec/lib/security/report_schema_version_matcher_spec.rb' - 'spec/lib/serializers/unsafe_json_spec.rb' - 'spec/lib/service_ping/devops_report_spec.rb' @@ -3685,7 +3680,6 @@ RSpec/FeatureCategory: - 'spec/serializers/user_serializer_spec.rb' - 'spec/serializers/web_ide_terminal_entity_spec.rb' - 'spec/serializers/web_ide_terminal_serializer_spec.rb' - - 'spec/services/applications/create_service_spec.rb' - 'spec/sidekiq/cron/job_gem_dependency_spec.rb' - 'spec/sidekiq_cluster/sidekiq_cluster_spec.rb' - 'spec/spam/concerns/has_spam_action_response_fields_spec.rb' diff --git a/.rubocop_todo/rspec/named_subject.yml b/.rubocop_todo/rspec/named_subject.yml index f083b76cdf3..61363d618e5 100644 --- a/.rubocop_todo/rspec/named_subject.yml +++ b/.rubocop_todo/rspec/named_subject.yml @@ -1382,7 +1382,6 @@ RSpec/NamedSubject: - 'spec/helpers/gitlab_routing_helper_spec.rb' - 'spec/helpers/groups/group_members_helper_spec.rb' - 'spec/helpers/groups_helper_spec.rb' - - 'spec/helpers/hooks_helper_spec.rb' - 'spec/helpers/integrations_helper_spec.rb' - 'spec/helpers/jira_connect_helper_spec.rb' - 'spec/helpers/labels_helper_spec.rb' @@ -1766,7 +1765,6 @@ RSpec/NamedSubject: - 'spec/lib/gitlab/ci/yaml_processor_spec.rb' - 'spec/lib/gitlab/cleanup/personal_access_tokens_spec.rb' - 'spec/lib/gitlab/cleanup/project_uploads_spec.rb' - - 'spec/lib/gitlab/cleanup/remote_uploads_spec.rb' - 'spec/lib/gitlab/closing_issue_extractor_spec.rb' - 'spec/lib/gitlab/cluster/lifecycle_events_spec.rb' - 'spec/lib/gitlab/cluster/rack_timeout_observer_spec.rb' diff --git a/.rubocop_todo/rspec/verified_doubles.yml b/.rubocop_todo/rspec/verified_doubles.yml index 8df1027a90c..91a5b806729 100644 --- a/.rubocop_todo/rspec/verified_doubles.yml +++ b/.rubocop_todo/rspec/verified_doubles.yml @@ -175,7 +175,6 @@ RSpec/VerifiedDoubles: - 'ee/spec/workers/iterations/roll_over_issues_worker_spec.rb' - 'ee/spec/workers/ldap_group_sync_worker_spec.rb' - 'ee/spec/workers/merge_request_reset_approvals_worker_spec.rb' - - 'ee/spec/workers/new_epic_worker_spec.rb' - 'qa/spec/git/repository_spec.rb' - 'qa/spec/page/base_spec.rb' - 'qa/spec/page/validator_spec.rb' diff --git a/.rubocop_todo/style/hash_each_methods.yml b/.rubocop_todo/style/hash_each_methods.yml index 3b41caa7f0c..3474729179c 100644 --- a/.rubocop_todo/style/hash_each_methods.yml +++ b/.rubocop_todo/style/hash_each_methods.yml @@ -88,7 +88,6 @@ Style/HashEachMethods: - 'lib/gitlab/utils/batch_loader.rb' - 'lib/tasks/gitlab/db.rake' - 'lib/tasks/gitlab/setup.rake' - - 'qa/qa/specs/features/ee/browser_ui/10_govern/change_vulnerability_status_spec.rb' - 'qa/spec/specs/runner_spec.rb' - 'scripts/generate_rspec_pipeline.rb' - 'spec/config/settings_spec.rb' diff --git a/.rubocop_todo/style/inline_disable_annotation.yml b/.rubocop_todo/style/inline_disable_annotation.yml index 593a8b78953..d3b64c9cb95 100644 --- a/.rubocop_todo/style/inline_disable_annotation.yml +++ b/.rubocop_todo/style/inline_disable_annotation.yml @@ -1302,7 +1302,6 @@ Style/InlineDisableAnnotation: - 'ee/app/workers/concerns/elastic/bulk_cron_worker.rb' - 'ee/app/workers/concerns/geo/base_registry_sync_worker.rb' - 'ee/app/workers/create_github_webhook_worker.rb' - - 'ee/app/workers/elastic/migration_worker.rb' - 'ee/app/workers/elastic/namespace_update_worker.rb' - 'ee/app/workers/elastic_cluster_reindexing_cron_worker.rb' - 'ee/app/workers/elastic_full_index_worker.rb' @@ -1609,7 +1608,6 @@ Style/InlineDisableAnnotation: - 'ee/spec/workers/ldap_all_groups_sync_worker_spec.rb' - 'ee/spec/workers/ldap_group_sync_worker_spec.rb' - 'ee/spec/workers/ldap_sync_worker_spec.rb' - - 'ee/spec/workers/new_epic_worker_spec.rb' - 'ee/spec/workers/projects/register_suggested_reviewers_project_worker_spec.rb' - 'ee/spec/workers/update_all_mirrors_worker_spec.rb' - 'lib/api/access_requests.rb' @@ -1626,7 +1624,6 @@ Style/InlineDisableAnnotation: - 'lib/api/ci/resource_groups.rb' - 'lib/api/ci/runners.rb' - 'lib/api/ci/secure_files.rb' - - 'lib/api/ci/triggers.rb' - 'lib/api/commit_statuses.rb' - 'lib/api/custom_attributes_endpoints.rb' - 'lib/api/dependency_proxy.rb' @@ -1787,7 +1784,6 @@ Style/InlineDisableAnnotation: - 'lib/gitlab/ci/trace/stream.rb' - 'lib/gitlab/cleanup/personal_access_tokens.rb' - 'lib/gitlab/cleanup/project_uploads.rb' - - 'lib/gitlab/cleanup/remote_uploads.rb' - 'lib/gitlab/cluster/lifecycle_events.rb' - 'lib/gitlab/cluster/mixins/puma_cluster.rb' - 'lib/gitlab/config/entry/composable_array.rb' @@ -2051,9 +2047,6 @@ Style/InlineDisableAnnotation: - 'qa/qa/service/shellout.rb' - 'qa/qa/specs/features/api/1_manage/migration/gitlab_migration_large_project_spec.rb' - 'qa/qa/specs/features/browser_ui/1_manage/integrations/jira/jira_basic_integration_spec.rb' - - 'qa/qa/specs/features/ee/browser_ui/10_govern/create_merge_request_with_secure_spec.rb' - - 'qa/qa/specs/features/ee/browser_ui/10_govern/group/group_audit_logs_1_spec.rb' - - 'qa/qa/specs/features/ee/browser_ui/10_govern/instance/instance_audit_logs_spec.rb' - 'qa/qa/specs/features/ee/browser_ui/1_manage/integrations/jira_issues_list_spec.rb' - 'qa/qa/specs/qa_deprecation_toolkit_env.rb' - 'qa/qa/support/fips.rb' diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index e7609168123..8e856013cb6 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -b47256232c83b9a8b5811cc27c22801ab63ce879 +9277be2d60b30145b4afcbb171db3009d0beff7a diff --git a/GITLAB_KAS_VERSION b/GITLAB_KAS_VERSION index 21047f66770..346bf99bcc4 100644 --- a/GITLAB_KAS_VERSION +++ b/GITLAB_KAS_VERSION @@ -1 +1 @@ -73665fe4d78f13031adb21ccd18fee421474b3e0 +7dd8b96b0d2229cad3ff85dc637ccda307062331 diff --git a/Gemfile.checksum b/Gemfile.checksum index 01ba5a601fb..6b13ddc17a0 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -62,7 +62,7 @@ {"name":"bigdecimal","version":"3.1.7","platform":"ruby","checksum":"e799b369a0005fc6d62eed7ef19139ac9bc319cc51470c637b9dcdf593600133"}, {"name":"bindata","version":"2.4.11","platform":"ruby","checksum":"c38e0c99ffcd80c10a0a7ae6c8586d2fe26bf245cbefac90bec8764523220f6a"}, {"name":"binding_of_caller","version":"1.0.0","platform":"ruby","checksum":"3aad25d1d538fc6e7972978f9bf512ccd992784009947c81633bea776713161d"}, -{"name":"bootsnap","version":"1.18.4","platform":"ruby","checksum":"ac4c42af397f7ee15521820198daeff545e4c360d2772c601fbdc2c07d92af55"}, +{"name":"bootsnap","version":"1.18.6","platform":"ruby","checksum":"0ae2393c1e911e38be0f24e9173e7be570c3650128251bf06240046f84a07d00"}, {"name":"browser","version":"5.3.1","platform":"ruby","checksum":"62745301701ff2c6c5d32d077bb12532b20be261929dcb52c6781ed0d5658b3c"}, {"name":"builder","version":"3.2.4","platform":"ruby","checksum":"99caf08af60c8d7f3a6b004029c4c3c0bdaebced6c949165fe98f1db27fbbc10"}, {"name":"bullet","version":"7.2.0","platform":"ruby","checksum":"3502c8a1b4f5db77fc8f6d3dd89a6a8c1a968219a45e12ae6cbaa9c09967ea89"}, diff --git a/Gemfile.lock b/Gemfile.lock index 0f4056af3ee..0e896e27203 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -406,7 +406,7 @@ GEM bindata (2.4.11) binding_of_caller (1.0.0) debug_inspector (>= 0.0.1) - bootsnap (1.18.4) + bootsnap (1.18.6) msgpack (~> 1.2) browser (5.3.1) builder (3.2.4) diff --git a/Gemfile.next.checksum b/Gemfile.next.checksum index 01ba5a601fb..6b13ddc17a0 100644 --- a/Gemfile.next.checksum +++ b/Gemfile.next.checksum @@ -62,7 +62,7 @@ {"name":"bigdecimal","version":"3.1.7","platform":"ruby","checksum":"e799b369a0005fc6d62eed7ef19139ac9bc319cc51470c637b9dcdf593600133"}, {"name":"bindata","version":"2.4.11","platform":"ruby","checksum":"c38e0c99ffcd80c10a0a7ae6c8586d2fe26bf245cbefac90bec8764523220f6a"}, {"name":"binding_of_caller","version":"1.0.0","platform":"ruby","checksum":"3aad25d1d538fc6e7972978f9bf512ccd992784009947c81633bea776713161d"}, -{"name":"bootsnap","version":"1.18.4","platform":"ruby","checksum":"ac4c42af397f7ee15521820198daeff545e4c360d2772c601fbdc2c07d92af55"}, +{"name":"bootsnap","version":"1.18.6","platform":"ruby","checksum":"0ae2393c1e911e38be0f24e9173e7be570c3650128251bf06240046f84a07d00"}, {"name":"browser","version":"5.3.1","platform":"ruby","checksum":"62745301701ff2c6c5d32d077bb12532b20be261929dcb52c6781ed0d5658b3c"}, {"name":"builder","version":"3.2.4","platform":"ruby","checksum":"99caf08af60c8d7f3a6b004029c4c3c0bdaebced6c949165fe98f1db27fbbc10"}, {"name":"bullet","version":"7.2.0","platform":"ruby","checksum":"3502c8a1b4f5db77fc8f6d3dd89a6a8c1a968219a45e12ae6cbaa9c09967ea89"}, diff --git a/Gemfile.next.lock b/Gemfile.next.lock index 0f4056af3ee..0e896e27203 100644 --- a/Gemfile.next.lock +++ b/Gemfile.next.lock @@ -406,7 +406,7 @@ GEM bindata (2.4.11) binding_of_caller (1.0.0) debug_inspector (>= 0.0.1) - bootsnap (1.18.4) + bootsnap (1.18.6) msgpack (~> 1.2) browser (5.3.1) builder (3.2.4) diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js index 703bf655b64..bee7f1cb6e0 100644 --- a/app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js +++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js @@ -1,4 +1,4 @@ -import { visitUrl, constructWebIDEPath } from '~/lib/utils/url_utility'; +import { visitUrl, constructWebIDEPath, appendLineRangeHashToUrl } from '~/lib/utils/url_utility'; import findAndFollowLink, { findAndFollowChildLink } from '~/lib/utils/navigation_utility'; import { GO_TO_PROJECT_OVERVIEW, @@ -54,7 +54,8 @@ export default class ShortcutsNavigation { iid: window.gl.mrWidgetData?.iid, }); if (path) { - visitUrl(path, true); + const url = appendLineRangeHashToUrl(path); + visitUrl(url, true); } } } diff --git a/app/assets/javascripts/ide/init_gitlab_web_ide.js b/app/assets/javascripts/ide/init_gitlab_web_ide.js index 731dbe8a528..f8a4df93f24 100644 --- a/app/assets/javascripts/ide/init_gitlab_web_ide.js +++ b/app/assets/javascripts/ide/init_gitlab_web_ide.js @@ -2,6 +2,7 @@ import { start } from '@gitlab/web-ide'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import csrf from '~/lib/utils/csrf'; import Tracking from '~/tracking'; +import { getLineRangeFromHash } from '~/lib/utils/url_utility'; import { getBaseConfig, getOAuthConfig, @@ -63,6 +64,8 @@ export const initGitlabWebIDE = async (el) => { const isLanguageServerEnabled = gon?.features?.webIdeLanguageServer || false; + const lineRange = getLineRangeFromHash(); + try { // See ClientOnlyConfig https://gitlab.com/gitlab-org/gitlab-web-ide/-/blob/main/packages/web-ide-types/src/config.ts#L17 await start(rootEl, { @@ -73,6 +76,7 @@ export const initGitlabWebIDE = async (el) => { projectPath, ref, filePath, + lineRange, mrId, mrTargetProject: getMRTargetProject(), forkInfo, diff --git a/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue b/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue index ab166b8fba1..447bed7f9d0 100644 --- a/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue +++ b/app/assets/javascripts/import_entities/import_projects/components/provider_repo_table_row.vue @@ -60,6 +60,8 @@ export default { return { isSelectedForReimport: false, showMembershipsModal: false, + userHasSelectedNamespace: false, + showNamespaceRequiredError: false, }; }, @@ -72,8 +74,13 @@ export default { }, showMembershipsWarning() { - const userNamespaceSelected = this.importTarget.targetNamespace === this.userNamespace; - return (this.isImportNotStarted || this.isSelectedForReimport) && userNamespaceSelected; + const usersNamespaceIsSelected = this.importTarget.targetNamespace === this.userNamespace; + + return this.isNotImporting && usersNamespaceIsSelected; + }, + + isNotImporting() { + return this.isImportNotStarted || this.isSelectedForReimport; }, isFinished() { @@ -128,6 +135,15 @@ export default { this.updateImportTarget({ newName: value }); }, }, + shouldBlockImportForNamespace() { + if (this.importTarget.targetNamespace) { + return false; + } + + return ( + !this.repo.importSource.target && this.isNotImporting && !this.userHasSelectedNamespace + ); + }, }, methods: { @@ -156,7 +172,9 @@ export default { }, onImportClick() { - if (this.showMembershipsWarning) { + if (this.shouldBlockImportForNamespace) { + this.showNamespaceRequiredError = true; + } else if (this.showMembershipsWarning) { this.showMembershipsModal = true; } else { this.handleImportRepo(); @@ -164,6 +182,8 @@ export default { }, onSelect(value) { + this.userHasSelectedNamespace = true; + this.showNamespaceRequiredError = false; this.updateImportTarget({ targetNamespace: value }); }, }, @@ -209,6 +229,7 @@ export default {
@@ -224,6 +245,14 @@ export default { data-testid="project-path-field" />
+ @@ -264,9 +293,11 @@ export default { (repoId) => { return { newName: repo.importSource.sanitizedName, - targetNamespace: state.defaultTargetNamespace, + targetNamespace: null, }; }; diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js index 6214db37166..a8a65284b55 100644 --- a/app/assets/javascripts/lib/utils/url_utility.js +++ b/app/assets/javascripts/lib/utils/url_utility.js @@ -831,3 +831,40 @@ export function stripRelativeUrlRootFromPath(path) { return joinPaths('/', path.substring(relativeUrlRoot.length)); } + +const LINE_RANGE_HASH_REGEX = /^#L(\d+)(?:-(\d+))?$/; + +/** + * Retrieves the line number range from the url hash if it exists. + * + * @param {string} [hash=window.location.hash] - The URL hash string to parse. Defaults to the current window's location hash. + * + * @returns {{beginning: number, end: number}| null} An object containing the line number range selected, + * otherwise returns null + */ +export const getLineRangeFromHash = (hash = window.location.hash) => { + const match = hash.match(LINE_RANGE_HASH_REGEX); + if (!match || !match[1]) return null; + + const beginning = parseInt(match[1], 10); + // If there's a second number (end of range), use it; otherwise use the start number + const end = match[2] ? parseInt(match[2], 10) : beginning; + + return { beginning, end }; +}; + +/** + * Appends the current window's line reference hash to a URL if it exists. + * + * @param {string} url - The base URL to which the line reference should be appended + * @param {string} [hash=window.location.hash] - The hash string to check and potentially append. Defaults to the current window's location hash. + * @returns {string} The URL with the line reference hash appended if the current window's + * hash matches the line reference pattern (#L or #L-), + * otherwise returns the original URL unchanged + */ +export function appendLineRangeHashToUrl(url, hash = window.location.hash) { + if (LINE_RANGE_HASH_REGEX.test(hash)) { + return url + hash; + } + return url; +} diff --git a/app/assets/javascripts/vue_shared/components/web_ide_link.vue b/app/assets/javascripts/vue_shared/components/web_ide_link.vue index f0fc7cbd649..5e64fe773e1 100644 --- a/app/assets/javascripts/vue_shared/components/web_ide_link.vue +++ b/app/assets/javascripts/vue_shared/components/web_ide_link.vue @@ -6,7 +6,7 @@ import { GlTooltipDirective, } from '@gitlab/ui'; import { s__, __ } from '~/locale'; -import { visitUrl } from '~/lib/utils/url_utility'; +import { visitUrl, appendLineRangeHashToUrl } from '~/lib/utils/url_utility'; import Tracking from '~/tracking'; import ConfirmForkModal from '~/vue_shared/components/web_ide/confirm_fork_modal.vue'; import { keysFor, GO_TO_PROJECT_WEBIDE } from '~/behaviors/shortcuts/keybindings'; @@ -233,7 +233,8 @@ export default { } : { handle: () => { - visitUrl(this.webIdeUrl, true); + const url = appendLineRangeHashToUrl(this.webIdeUrl); + visitUrl(url, true); }, }; diff --git a/app/views/import/_githubish_status.html.haml b/app/views/import/_githubish_status.html.haml index ad68a0587f2..b6d4dc6b9c9 100644 --- a/app/views/import/_githubish_status.html.haml +++ b/app/views/import/_githubish_status.html.haml @@ -3,7 +3,7 @@ - extra_data = local_assigns.fetch(:extra_data, {}) - filterable = local_assigns.fetch(:filterable, true) - paginatable = local_assigns.fetch(:paginatable, false) -- default_namespace_path = (local_assigns[:default_namespace] || current_user.namespace).full_path +- default_namespace_path = local_assigns[:default_namespace]&.full_path - cancel_path = local_assigns.fetch(:cancel_path, nil) - details_path = local_assigns.fetch(:details_path, nil) - provider_title = Gitlab::ImportSources.title(local_assigns.fetch(:provider)) diff --git a/db/migrate/20250523120208_add_schema_version_to_zoekt_node.rb b/db/migrate/20250523120208_add_schema_version_to_zoekt_node.rb new file mode 100644 index 00000000000..24cfdee0322 --- /dev/null +++ b/db/migrate/20250523120208_add_schema_version_to_zoekt_node.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddSchemaVersionToZoektNode < Gitlab::Database::Migration[2.3] + milestone '18.1' + + def change + add_column :zoekt_nodes, :schema_version, :smallint, null: false, default: 0 + end +end diff --git a/db/migrate/20250523120519_add_schema_version_to_zoekt_repository.rb b/db/migrate/20250523120519_add_schema_version_to_zoekt_repository.rb new file mode 100644 index 00000000000..e4a654cfea0 --- /dev/null +++ b/db/migrate/20250523120519_add_schema_version_to_zoekt_repository.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddSchemaVersionToZoektRepository < Gitlab::Database::Migration[2.3] + milestone '18.1' + + def change + add_column :zoekt_repositories, :schema_version, :smallint, null: false, default: 0 + end +end diff --git a/db/post_migrate/20250519065025_add_index_for_p_ci_pipeline_variables_on_project_id.rb b/db/post_migrate/20250519065025_add_index_for_p_ci_pipeline_variables_on_project_id.rb new file mode 100644 index 00000000000..46f0cf8f8b2 --- /dev/null +++ b/db/post_migrate/20250519065025_add_index_for_p_ci_pipeline_variables_on_project_id.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class AddIndexForPCiPipelineVariablesOnProjectId < Gitlab::Database::Migration[2.3] + include Gitlab::Database::PartitioningMigrationHelpers + + disable_ddl_transaction! + milestone '18.1' + + TABLE_NAME = :p_ci_pipeline_variables + INDEX_NAME = :index_p_ci_pipeline_variables_on_project_id + COLUMN_NAME = :project_id + + def up + add_concurrent_partitioned_index(TABLE_NAME, COLUMN_NAME, name: INDEX_NAME) + end + + def down + remove_concurrent_partitioned_index_by_name(TABLE_NAME, INDEX_NAME) + end +end diff --git a/db/schema_migrations/20250519065025 b/db/schema_migrations/20250519065025 new file mode 100644 index 00000000000..1d257edf5c7 --- /dev/null +++ b/db/schema_migrations/20250519065025 @@ -0,0 +1 @@ +c245a850227686ec455e5da05a62e72c37df499286f245211f1f54269c304e85 \ No newline at end of file diff --git a/db/schema_migrations/20250523120208 b/db/schema_migrations/20250523120208 new file mode 100644 index 00000000000..0a609d53794 --- /dev/null +++ b/db/schema_migrations/20250523120208 @@ -0,0 +1 @@ +66a16305b024955bbc525a03118280c7986218cfbadfdd6938f28ca614d72fb2 \ No newline at end of file diff --git a/db/schema_migrations/20250523120519 b/db/schema_migrations/20250523120519 new file mode 100644 index 00000000000..577aeaf32b4 --- /dev/null +++ b/db/schema_migrations/20250523120519 @@ -0,0 +1 @@ +695693b64127b2e75eb3e038a78dc172735819232a7907a61d965418b4a370d8 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 0e61c9caef5..728968be951 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -26366,6 +26366,7 @@ CREATE TABLE zoekt_nodes ( indexed_bytes bigint DEFAULT 0 NOT NULL, usable_storage_bytes bigint DEFAULT 0 NOT NULL, usable_storage_bytes_locked_until timestamp with time zone, + schema_version smallint DEFAULT 0 NOT NULL, CONSTRAINT check_32f39efba3 CHECK ((char_length(search_base_url) <= 1024)), CONSTRAINT check_38c354a3c2 CHECK ((char_length(index_base_url) <= 1024)) ); @@ -26409,6 +26410,7 @@ CREATE TABLE zoekt_repositories ( size_bytes bigint DEFAULT 0 NOT NULL, index_file_count integer DEFAULT 0 NOT NULL, retries_left smallint DEFAULT 10 NOT NULL, + schema_version smallint DEFAULT 0 NOT NULL, CONSTRAINT c_zoekt_repositories_on_project_id_and_project_identifier CHECK (((project_id IS NULL) OR (project_identifier = project_id))), CONSTRAINT c_zoekt_repositories_on_retries_left CHECK (((retries_left > 0) OR ((retries_left = 0) AND (state >= 200)))) ); @@ -36541,6 +36543,8 @@ CREATE INDEX index_p_ci_job_annotations_on_project_id ON ONLY p_ci_job_annotatio CREATE INDEX index_p_ci_job_artifact_reports_on_project_id ON ONLY p_ci_job_artifact_reports USING btree (project_id); +CREATE INDEX index_p_ci_pipeline_variables_on_project_id ON ONLY p_ci_pipeline_variables USING btree (project_id); + CREATE INDEX index_p_ci_pipelines_config_on_project_id ON ONLY p_ci_pipelines_config USING btree (project_id); CREATE INDEX index_p_ci_runner_machine_builds_on_project_id ON ONLY p_ci_runner_machine_builds USING btree (project_id); diff --git a/doc/administration/backup_restore/backup_gitlab.md b/doc/administration/backup_restore/backup_gitlab.md index 4794ae19f6a..9e872568669 100644 --- a/doc/administration/backup_restore/backup_gitlab.md +++ b/doc/administration/backup_restore/backup_gitlab.md @@ -27,16 +27,40 @@ For larger GitLab instances, alternative backup strategies include: ## Data included in a backup +GitLab provides a command-line interface to back up your entire instance. By default, the backup creates an archive in a single compressed tar file. This file includes: - Database data and configuration -- Git repositories, container registry images, and uploaded content -- Package registry data and CI/CD artifacts - Account and group settings -- Project and group wikis -- Project-level secure files -- External merge request diffs +- CI/CD artifacts and job logs +- Git repositories and LFS objects +- External merge request diffs ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/154914) in GitLab 17.1) +- Package registry data and container registry images +- Project and [group](../../user/project/wiki/group.md) wikis. +- Project-level attachments and uploads +- Secure Files ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121142) in GitLab 16.1) +- GitLab Pages content +- Terraform states +- Snippets + +## Data not included in a backup + +- [Mattermost data](../../integration/mattermost/_index.md#back-up-gitlab-mattermost) +- Redis (and thus Sidekiq jobs) +- [Object storage](#object-storage) on Linux package (Omnibus) / Docker / Self-compiled installations + +- [Global server hooks](../server_hooks.md#create-global-server-hooks-for-all-repositories) +- [File hooks](../file_hooks.md) +- GitLab configuration files (`/etc/gitlab`) +- TLS- and SSH-related keys and certificates +- Other system files + +{{< alert type="warning" >}} + +You are highly advised to read about [storing configuration files](#storing-configuration-files) to back up those separately. + +{{< /alert >}} ## Simple backup procedure @@ -196,41 +220,6 @@ GitLab uses Redis both as a cache store and to hold persistent data for our back Elasticsearch is an optional database for advanced search. It can improve search in both source-code level, and user generated content in issues, merge requests, and discussions. The [backup command](#backup-command) does _not_ back up Elasticsearch data. Elasticsearch data can be regenerated from PostgreSQL data after a restore. It is possible to [manually back up Elasticsearch](https://www.elastic.co/guide/en/elasticsearch/reference/current/snapshot-restore.html). -## Command-line interface - -GitLab provides a command-line interface to back up your entire instance, -including: - -- Database -- Attachments -- Git repositories data -- CI/CD job output logs -- CI/CD job artifacts -- LFS objects -- Terraform states -- Container registry images -- GitLab Pages content -- Packages -- Snippets -- [Group wikis](../../user/project/wiki/group.md) -- Project-level Secure Files ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121142) in GitLab 16.1) -- External merge request diffs ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/154914) in GitLab 17.1) - -Backups do not include: - -- [Mattermost data](../../integration/mattermost/_index.md#back-up-gitlab-mattermost) -- Redis (and thus Sidekiq jobs) -- [Object storage](#object-storage) on Linux package (Omnibus) / Docker / Self-compiled installations -- [Global server hooks](../server_hooks.md#create-global-server-hooks-for-all-repositories) -- [File hooks](../file_hooks.md) - -{{< alert type="warning" >}} - -GitLab does not back up any configuration files (`/etc/gitlab`), TLS keys and certificates, or system -files. You are highly advised to read about [storing configuration files](#storing-configuration-files). - -{{< /alert >}} - ### Requirements To be able to back up and restore, ensure that Rsync is installed on your @@ -815,7 +804,7 @@ You can back up specific repositories using the `REPOSITORIES_PATHS` option. Similarly, you can use `SKIP_REPOSITORIES_PATHS` to skip certain repositories. Both options accept a comma-separated list of project or group paths. If you specify a group path, all repositories in all projects in the group and -descendent groups are included or skipped, depending on which option you used. +descendant groups are included or skipped, depending on which option you used. For example, to back up all repositories for all projects in Group A (`group-a`), the repository for Project C in Group B (`group-b/project-c`), diff --git a/doc/administration/backup_restore/restore_gitlab.md b/doc/administration/backup_restore/restore_gitlab.md index 118b3e0c6ac..62ce5611471 100644 --- a/doc/administration/backup_restore/restore_gitlab.md +++ b/doc/administration/backup_restore/restore_gitlab.md @@ -65,10 +65,9 @@ Restore: ### Certain GitLab configuration must match the original backed up environment -You likely also want to restore your previous `/etc/gitlab/gitlab.rb` (for Linux package installations) +You likely want to separately restore your previous `/etc/gitlab/gitlab.rb` (for Linux package installations) or `/home/git/gitlab/config/gitlab.yml` (for self-compiled installations) and -any TLS keys, certificates (`/etc/gitlab/ssl`, `/etc/gitlab/trusted-certs`), or -[SSH host keys](https://superuser.com/questions/532040/copy-ssh-keys-from-one-server-to-another-server/532079#532079). +[any TLS or SSH keys and certificates](backup_gitlab.md#data-not-included-in-a-backup). Certain configuration is coupled to data in PostgreSQL. For example: @@ -453,7 +452,7 @@ that can cause data loss. The problem affects repositories that have been forked You can restore specific repositories using the `REPOSITORIES_PATHS` and the `SKIP_REPOSITORIES_PATHS` options. Both options accept a comma-separated list of project and group paths. If you specify a group path, all repositories in all projects in the group and -descendent groups are included or skipped, depending on which option you used. +descendant groups are included or skipped, depending on which option you used. Both the groups and projects must exist in the specified backup or on the target instance. {{< alert type="note" >}} diff --git a/doc/api/container_registry.md b/doc/api/container_registry.md index 76a87342699..5490d37b57b 100644 --- a/doc/api/container_registry.md +++ b/doc/api/container_registry.md @@ -448,7 +448,7 @@ curl --request DELETE \ ## Instance-wide endpoints -Beside the group- and project-specific GitLab APIs explained above, +Beside the group- and project-specific GitLab APIs explained previously, the container registry has its own endpoints. To query those, follow the Registry's built-in mechanism to obtain and use an [authentication token](https://distribution.github.io/distribution/spec/auth/token/). diff --git a/doc/api/dora/metrics.md b/doc/api/dora/metrics.md index f9048010f69..4b26d7e8c6a 100644 --- a/doc/api/dora/metrics.md +++ b/doc/api/dora/metrics.md @@ -99,7 +99,7 @@ Example response: ## The `value` field -For both the project and group-level endpoints above, the `value` field in the +For both the project and group-level endpoints described previously, the `value` field in the API response has a different meaning depending on the provided `metric` query parameter: diff --git a/doc/api/license.md b/doc/api/license.md index 69b7f2b358b..ab68ec43e32 100644 --- a/doc/api/license.md +++ b/doc/api/license.md @@ -134,7 +134,8 @@ Returns the following status codes: Example request: ```shell -curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/license/:id" +curl --header "PRIVATE-TOKEN: " \ +--url "https://gitlab.example.com/api/v4/license/:id" ``` Example response: @@ -175,7 +176,9 @@ POST /license | `license` | string | yes | The license string | ```shell -curl --request POST --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/license?license=eyJkYXRhIjoiMHM5Q...S01Udz09XG4ifQ==" +curl --request POST \ +--header "PRIVATE-TOKEN: " \ +--url "https://gitlab.example.com/api/v4/license?license=eyJkYXRhIjoiMHM5Q...S01Udz09XG4ifQ==" ``` Example response: @@ -221,7 +224,9 @@ DELETE /license/:id | `id` | integer | yes | ID of the GitLab license. | ```shell -curl --request DELETE --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/license/:id" +curl --request DELETE \ +--header "PRIVATE-TOKEN: " \ +--url "https://gitlab.example.com/api/v4/license/:id" ``` Returns: @@ -241,7 +246,9 @@ PUT /license/:id/refresh_billable_users | `id` | integer | yes | ID of the GitLab license. | ```shell -curl --request PUT --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/license/:id/refresh_billable_users" +curl --request PUT \ +--header "PRIVATE-TOKEN: " \ +--url "https://gitlab.example.com/api/v4/license/:id/refresh_billable_users" ``` Example response: @@ -271,7 +278,9 @@ GET /license/usage_export.csv ``` ```shell -curl --request GET --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/license/usage_export.csv" +curl --request GET \ +--header "PRIVATE-TOKEN: " \ +--url "https://gitlab.example.com/api/v4/license/usage_export.csv" ``` Example response: diff --git a/doc/api/project_snippets.md b/doc/api/project_snippets.md index f84316e2437..9ffaf1cf10c 100644 --- a/doc/api/project_snippets.md +++ b/doc/api/project_snippets.md @@ -116,7 +116,7 @@ curl --request POST "https://gitlab.com/api/v4/projects/:id/snippets" \ -d @snippet.json ``` -`snippet.json` used in the above example request: +`snippet.json` used in the previous example request: ```json { @@ -168,7 +168,7 @@ curl --request PUT "https://gitlab.com/api/v4/projects/:id/snippets/:snippet_id" -d @snippet.json ``` -`snippet.json` used in the above example request: +`snippet.json` used in the previous example request: ```json { diff --git a/doc/api/snippets.md b/doc/api/snippets.md index 3bfda6c0741..f851f889f43 100644 --- a/doc/api/snippets.md +++ b/doc/api/snippets.md @@ -245,7 +245,7 @@ curl --request POST "https://gitlab.example.com/api/v4/snippets" \ -d @snippet.json ``` -`snippet.json` used in the above example request: +`snippet.json` used in the previous example request: ```json { @@ -336,7 +336,7 @@ curl --request PUT "https://gitlab.example.com/api/v4/snippets/1" \ -d @snippet.json ``` -`snippet.json` used in the above example request: +`snippet.json` used in the previous example request: ```json { diff --git a/doc/development/documentation/styleguide/word_list.md b/doc/development/documentation/styleguide/word_list.md index 7261b959a8d..3aacd234a7c 100644 --- a/doc/development/documentation/styleguide/word_list.md +++ b/doc/development/documentation/styleguide/word_list.md @@ -891,17 +891,15 @@ use three periods with no space (`...`) instead of the `…` HTML entity or For more information, see [code blocks](_index.md#code-blocks). -Do not include any ellipses when documenting UI text. - For more information, see the [Microsoft Style Guide](https://learn.microsoft.com/en-us/style-guide/punctuation/ellipses). -Use: +Do not include any ellipses when documenting UI text. For example, use: -- **Create new** +- **Search or go to** Instead of: -- **Create new...** +- **Search or go to...** ## email diff --git a/doc/operations/incident_management/integrations.md b/doc/operations/incident_management/integrations.md index 6a9a89dfdf2..91be2af5a0a 100644 --- a/doc/operations/incident_management/integrations.md +++ b/doc/operations/incident_management/integrations.md @@ -363,7 +363,7 @@ curl --request POST \ {{< alert type="warning" >}} Using your authorization key in the URL is insecure, as it's visible in server logs. We recommend -using one of the above header options if your tooling supports it. +using one of the header options described previously if your tooling supports it. {{< /alert >}} diff --git a/doc/subscriptions/gitlab_com/_index.md b/doc/subscriptions/gitlab_com/_index.md index ac3bb2c413f..8e9b8ed4720 100644 --- a/doc/subscriptions/gitlab_com/_index.md +++ b/doc/subscriptions/gitlab_com/_index.md @@ -357,7 +357,7 @@ To purchase additional storage for your group on GitLab.com: After your payment is processed, the extra storage is available for your group namespace. -To confirm the available storage, follow the first three steps listed above. +To confirm the available storage, follow the first three steps listed previously. The **Purchased storage available** total is incremented by the amount purchased. All locked projects are unlocked and their excess usage is deducted from the additional storage. diff --git a/doc/user/analytics/value_streams_dashboard.md b/doc/user/analytics/value_streams_dashboard.md index c01bb5d92e3..8a29acc415d 100644 --- a/doc/user/analytics/value_streams_dashboard.md +++ b/doc/user/analytics/value_streams_dashboard.md @@ -35,7 +35,7 @@ The Value Streams Dashboard includes panels that visualize the following metrics With the Value Streams Dashboard, you can: -- Track and compare the above metrics over a period of time. +- Track and compare the metrics listed previously over a period of time. - Identify downward trends early on. - Understand security exposure. - Drill down into individual projects or metrics to take actions for improvement. diff --git a/doc/user/compliance/compliance_frameworks.md b/doc/user/compliance/compliance_frameworks.md index 2be93cb1b5a..55c3d380daa 100644 --- a/doc/user/compliance/compliance_frameworks.md +++ b/doc/user/compliance/compliance_frameworks.md @@ -182,15 +182,13 @@ If the import is successful, the new compliance framework appears in the list. A {{< /history >}} -In GitLab Ultimate, you can define specific **requirements** for a compliance framework. Requirements are made up of one or more **controls**, which are checks against the configuration or behavior of projects that are assigned the framework. There is maximum of 5 controls per requirement. +In GitLab Ultimate, you can define specific **requirements** for a compliance framework. Requirements are made up of one +or more **controls**, which are checks against the configuration or behavior of projects that are assigned the framework. There is maximum of 5 controls per requirement. -### Controls +Each control includes logic that GitLab uses during scheduled or triggered scans to evaluate a project's adherence. For +more details on how adherence is tracked, see [Compliance status report](compliance_center/compliance_status_report.md). -Each control includes logic that GitLab uses during scheduled or triggered scans to evaluate a project's adherence. For more details on how adherence is tracked, see [Compliance status report](compliance_center/compliance_status_report.md). - -#### GitLab controls - -The following controls are available to use in framework requirements: +You can use GitLab compliance controls or external controls for framework requirements. ### GitLab compliance controls @@ -262,7 +260,7 @@ behavior of projects that are assigned to a compliance framework. | Terraform enabled | `terraform_enabled` | Ensures that the [Terraform integration](../../administration/terraform_state.md) is enabled for the project. | | Vulnerabilities SLO days over threshold | `vulnerabilities_slo_days_over_threshold` | Ensures that [vulnerabilities are addressed](../application_security/vulnerabilities/_index.md) inside SLO thresholds (180 days). | -#### External controls +### External controls External controls are API calls to external systems that request the status of an external control or requirement. diff --git a/doc/user/feature_flags.md b/doc/user/feature_flags.md index 2619e995520..e5669f62d62 100644 --- a/doc/user/feature_flags.md +++ b/doc/user/feature_flags.md @@ -21,7 +21,7 @@ For help developing custom feature flags, see
If you don't see the feature flag tables below, view them at docs.gitlab.com.
- + diff --git a/doc/user/infrastructure/iac/terraform_state.md b/doc/user/infrastructure/iac/terraform_state.md index 1797645923a..d9a2c48f0e9 100644 --- a/doc/user/infrastructure/iac/terraform_state.md +++ b/doc/user/infrastructure/iac/terraform_state.md @@ -94,7 +94,7 @@ To configure GitLab CI/CD as a backend: `gitlab-tofu plan` commands. 1. Trigger the manual `deploy` job from the previous pipeline. This action runs the `gitlab-tofu apply` command, which provisions the defined infrastructure. -The output from the above commands should be viewable in the job logs. +The output from the previous commands should be viewable in the job logs. The `gitlab-tofu` CLI is a wrapper around the `tofu` CLI. diff --git a/doc/user/markdown.md b/doc/user/markdown.md index 34dc9492f16..430cd0f2069 100644 --- a/doc/user/markdown.md +++ b/doc/user/markdown.md @@ -860,10 +860,7 @@ URL references like `https://gitlab.com/gitlab-org/gitlab/-/issues/1234+s` are a To update the rendered references if the assignee, milestone, or health status changed: -- Edit the comment or description and save it. - -Issue [420807](https://gitlab.com/gitlab-org/gitlab/-/issues/420807) tracks improving how these -references refresh. +- Refresh the page. ### Comment preview on hover diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 08a69f875dd..1cbd0c28b23 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -32008,6 +32008,12 @@ msgstr "" msgid "ImportProjects|Requesting your %{provider} repositories failed" msgstr "" +msgid "ImportProjects|Select a destination namespace." +msgstr "" + +msgid "ImportProjects|Select namespace" +msgstr "" + msgid "ImportProjects|Select the repositories you want to import" msgstr "" diff --git a/package.json b/package.json index e7ff54148ae..ded02f5afd5 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "@mattiasbuelens/web-streams-adapter": "^0.1.0", "@rails/actioncable": "7.1.501", "@rails/ujs": "7.1.501", - "@sentry/browser": "9.19.0", + "@sentry/browser": "9.22.0", "@snowplow/browser-plugin-client-hints": "^3.24.2", "@snowplow/browser-plugin-form-tracking": "^3.24.2", "@snowplow/browser-plugin-ga-cookies": "^3.24.2", @@ -157,7 +157,7 @@ "graphiql": "^3.7.1", "graphql": "16.11.0", "graphql-tag": "^2.11.0", - "gridstack": "^11.1.2", + "gridstack": "^12.1.2", "highlight.js": "^11.11.1", "highlightjs-glimmer": "^2.2.2", "immer": "^9.0.15", diff --git a/spec/features/import/manifest_import_spec.rb b/spec/features/import/manifest_import_spec.rb index 175ae2fee8c..35c25966e87 100644 --- a/spec/features/import/manifest_import_spec.rb +++ b/spec/features/import/manifest_import_spec.rb @@ -34,7 +34,6 @@ RSpec.describe 'Import multiple repositories by uploading a manifest file', :js, page.within(second_row) do click_on 'Import' end - click_on 'Continue import' wait_for_requests diff --git a/spec/frontend/ide/init_gitlab_web_ide_spec.js b/spec/frontend/ide/init_gitlab_web_ide_spec.js index f815efed4d3..341cb11ea0e 100644 --- a/spec/frontend/ide/init_gitlab_web_ide_spec.js +++ b/spec/frontend/ide/init_gitlab_web_ide_spec.js @@ -31,6 +31,8 @@ const TEST_BRANCH_NAME = '12345-foo-patch'; const TEST_USER_PREFERENCES_PATH = '/user/preferences'; const TEST_GITLAB_WEB_IDE_PUBLIC_PATH = 'test/webpack/assets/gitlab-web-ide/public/path'; const TEST_FILE_PATH = 'foo/README.md'; +const TEST_LINE_NUMBER = { beginning: 42, end: 42 }; +const TEST_LINE_RANGE = { beginning: 42, end: 46 }; const TEST_MR_ID = '7'; const TEST_MR_TARGET_PROJECT = 'gitlab-org/the-real-gitlab'; const TEST_SIGN_IN_PATH = 'sign-in'; @@ -124,6 +126,7 @@ describe('ide/init_gitlab_web_ide', () => { projectPath: TEST_PROJECT_PATH, ref: TEST_BRANCH_NAME, filePath: TEST_FILE_PATH, + lineRange: null, mrId: TEST_MR_ID, mrTargetProject: '', forkInfo: null, @@ -193,6 +196,39 @@ describe('ide/init_gitlab_web_ide', () => { }); }); + describe('when URL has lineNumber in hash', () => { + beforeEach(() => { + setWindowLocation(`https://example.com/-/ide/path/to/file#L42`); + + createSubject(); + }); + + it('includes line number', () => { + expect(start).toHaveBeenCalledWith( + findRootElement(), + expect.objectContaining({ + lineRange: TEST_LINE_NUMBER, + }), + ); + }); + }); + + describe('when URL has lineRange in hash', () => { + beforeEach(() => { + setWindowLocation(`https://example.com/-/ide/path/to/file#L42-46`); + + createSubject(); + }); + it('includes line range', () => { + expect(start).toHaveBeenCalledWith( + findRootElement(), + expect.objectContaining({ + lineRange: TEST_LINE_RANGE, + }), + ); + }); + }); + describe('when forkInfo is in dataset', () => { beforeEach(() => { findRootElement().dataset.forkInfo = JSON.stringify(TEST_FORK_INFO); diff --git a/spec/frontend/import_entities/import_projects/components/provider_repo_table_row_spec.js b/spec/frontend/import_entities/import_projects/components/provider_repo_table_row_spec.js index a1355706566..ffe3953a405 100644 --- a/spec/frontend/import_entities/import_projects/components/provider_repo_table_row_spec.js +++ b/spec/frontend/import_entities/import_projects/components/provider_repo_table_row_spec.js @@ -1,4 +1,4 @@ -import { GlBadge, GlButton, GlModal } from '@gitlab/ui'; +import { GlBadge, GlButton } from '@gitlab/ui'; import Vue, { nextTick } from 'vue'; // eslint-disable-next-line no-restricted-imports import Vuex from 'vuex'; @@ -15,7 +15,7 @@ describe('ProviderRepoTableRow', () => { const cancelImport = jest.fn(); const setImportTarget = jest.fn(); const groupImportTarget = { - targetNamespace: 'target', + targetNamespace: null, newName: 'newName', }; @@ -45,7 +45,8 @@ describe('ProviderRepoTableRow', () => { const findImportStatus = () => wrapper.findComponent(ImportStatus); const findProviderLink = () => wrapper.findByTestId('provider-link'); const findMembershipsWarning = () => wrapper.findByTestId('memberships-warning'); - const findGlModal = () => wrapper.findComponent(GlModal); + const findMembershipsWarningModal = () => wrapper.findByTestId('memberships-warning-modal'); + const findNamespaceRequiredWarning = () => wrapper.findByTestId('namespace-required-warning'); const findCancelButton = () => { const buttons = wrapper @@ -97,12 +98,37 @@ describe('ProviderRepoTableRow', () => { expect(findImportTargetDropdown().exists()).toBe(true); }); + it('renders the dropdown without a default selected', () => { + expect(findImportTargetDropdown().props('selected')).toBe(null); + expect(findImportTargetDropdown().props().toggleText).toBe('Select namespace'); + }); + + describe('when no namespace is selected', () => { + it('shows namespace required warning when import button is clicked', async () => { + findImportButton().vm.$emit('click'); + await nextTick(); + + expect(findNamespaceRequiredWarning().text()).toBe('Select a destination namespace.'); + }); + + it('does not trigger import when clicking import button', async () => { + findImportButton().vm.$emit('click'); + await nextTick(); + + expect(fetchImport).not.toHaveBeenCalled(); + }); + }); + describe('when user namespace is selected as import target', () => { - beforeEach(() => { + beforeEach(async () => { mountComponent( { repo }, { storeOptions: { importTarget: { targetNamespace: userNamespace } } }, ); + + const dropdown = findImportTargetDropdown(); + dropdown.vm.$emit('select', userNamespace); + await nextTick(); }); it('shows memberships warning', () => { @@ -113,7 +139,7 @@ describe('ProviderRepoTableRow', () => { findImportButton().vm.$emit('click'); await nextTick(); - const modal = findGlModal(); + const modal = findMembershipsWarningModal(); expect(modal.props('title')).toBe( 'Are you sure you want to import the project to a personal namespace?', ); @@ -122,11 +148,15 @@ describe('ProviderRepoTableRow', () => { ); }); + it('does not show `missing namespace` warning', () => { + expect(findNamespaceRequiredWarning().exists()).toBe(false); + }); + it('triggers import when clicking modal primary button', async () => { findImportButton().vm.$emit('click'); await nextTick(); - findGlModal().vm.$emit('primary'); + findMembershipsWarningModal().vm.$emit('primary'); expect(fetchImport).toHaveBeenCalledWith(expect.anything(), { repoId: repo.importSource.id, @@ -136,53 +166,68 @@ describe('ProviderRepoTableRow', () => { }); describe('when group namespace is selected as import target', () => { + beforeEach(async () => { + const dropdown = findImportTargetDropdown(); + dropdown.vm.$emit('select', 'target'); + await nextTick(); + }); + it('does not show memberships warning', () => { expect(findMembershipsWarning().isVisible()).toBe(false); }); - it('does not show modal when import button is clicked', async () => { + it('does not show memberships modal when import button is clicked', async () => { findImportButton().vm.$emit('click'); await nextTick(); - expect(findGlModal().exists()).toBe(false); - }); - }); - - it('renders import button', () => { - expect(findImportButton().exists()).toBe(true); - }); - - it('imports repo when clicking import button', async () => { - findImportButton().vm.$emit('click'); - - await nextTick(); - - expect(fetchImport).toHaveBeenCalledWith(expect.anything(), { - repoId: repo.importSource.id, - optionalStages: {}, - }); - }); - - it('includes optionalStages to import', async () => { - const OPTIONAL_STAGES = { stage1: true, stage2: false }; - - mountComponent({ - repo, - optionalStages: OPTIONAL_STAGES, + expect(findMembershipsWarningModal().exists()).toBe(false); }); - findImportButton().vm.$emit('click'); - - await nextTick(); - - expect(fetchImport).toHaveBeenCalledWith(expect.anything(), { - repoId: repo.importSource.id, - optionalStages: OPTIONAL_STAGES, + it('does not show `missing namespace` warning when import button is clicked', async () => { + findImportButton().vm.$emit('click'); + await nextTick(); + expect(findNamespaceRequiredWarning().exists()).toBe(false); }); - }); - it('does not render re-import button', () => { - expect(findReimportButton().exists()).toBe(false); + it('renders import button', () => { + expect(findImportButton().exists()).toBe(true); + }); + + it('imports repo when clicking import button', async () => { + findImportButton().vm.$emit('click'); + + await nextTick(); + + expect(fetchImport).toHaveBeenCalledWith(expect.anything(), { + repoId: repo.importSource.id, + optionalStages: {}, + }); + }); + + it('includes optionalStages to import', async () => { + const OPTIONAL_STAGES = { stage1: true, stage2: false }; + + mountComponent({ + repo, + optionalStages: OPTIONAL_STAGES, + }); + + const dropdown = findImportTargetDropdown(); + dropdown.vm.$emit('select', 'target'); + await nextTick(); + + findImportButton().vm.$emit('click'); + await nextTick(); + + expect(fetchImport).toHaveBeenCalledWith(expect.anything(), { + repoId: repo.importSource.id, + optionalStages: OPTIONAL_STAGES, + }); + }); + + it('does not render re-import button', () => { + expect(findReimportButton().exists()).toBe(false); + }); }); }); @@ -290,7 +335,12 @@ describe('ProviderRepoTableRow', () => { await nextTick(); + findImportTargetDropdown().vm.$emit('select', 'some-namespace'); + await nextTick(); + findReimportButton().vm.$emit('click'); + await nextTick(); + expect(findNamespaceRequiredWarning().exists()).toBe(false); expect(fetchImport).toHaveBeenCalledWith(expect.anything(), { repoId: repo.importSource.id, diff --git a/spec/frontend/import_entities/import_projects/store/actions_spec.js b/spec/frontend/import_entities/import_projects/store/actions_spec.js index 918821dfa59..aa57ae9f041 100644 --- a/spec/frontend/import_entities/import_projects/store/actions_spec.js +++ b/spec/frontend/import_entities/import_projects/store/actions_spec.js @@ -55,7 +55,7 @@ describe('import_projects store actions', () => { let localState; const importRepoId = 1; const otherImportRepoId = 2; - const defaultTargetNamespace = 'default'; + const defaultTargetNamespace = null; const sanitizedName = 'sanitizedName'; const defaultImportTarget = { newName: sanitizedName, targetNamespace: defaultTargetNamespace }; diff --git a/spec/frontend/import_entities/import_projects/store/getters_spec.js b/spec/frontend/import_entities/import_projects/store/getters_spec.js index fced5670f25..e25a6e09928 100644 --- a/spec/frontend/import_entities/import_projects/store/getters_spec.js +++ b/spec/frontend/import_entities/import_projects/store/getters_spec.js @@ -99,7 +99,7 @@ describe('import_projects store getters', () => { expect(getImportTarget(localState)(IMPORTABLE_REPO.importSource.id)).toStrictEqual({ newName: IMPORTABLE_REPO.importSource.sanitizedName, - targetNamespace: localState.defaultTargetNamespace, + targetNamespace: null, }); }); diff --git a/spec/frontend/lib/utils/url_utility_spec.js b/spec/frontend/lib/utils/url_utility_spec.js index d883800018a..36cf2d8aa80 100644 --- a/spec/frontend/lib/utils/url_utility_spec.js +++ b/spec/frontend/lib/utils/url_utility_spec.js @@ -2,7 +2,11 @@ import setWindowLocation from 'helpers/set_window_location_helper'; import { TEST_HOST } from 'helpers/test_constants'; import * as urlUtils from '~/lib/utils/url_utility'; import { setGlobalAlerts } from '~/lib/utils/global_alerts'; -import { setLocationHash } from '~/lib/utils/url_utility'; +import { + appendLineRangeHashToUrl, + getLineRangeFromHash, + setLocationHash, +} from '~/lib/utils/url_utility'; import { validURLs, invalidURLs } from './mock_data'; jest.mock('~/lib/utils/global_alerts', () => ({ @@ -476,6 +480,61 @@ describe('URL utility', () => { }); }); + describe('getLineRangeFromHash', () => { + beforeEach(() => { + window.location.hash = ''; + }); + + it('returns null when no line number hash is present', () => { + expect(getLineRangeFromHash()).toBe(null); + }); + + it('returns line number when hash is present in URL', () => { + window.location.hash = '#L42'; + expect(getLineRangeFromHash()).toStrictEqual({ beginning: 42, end: 42 }); + }); + + it('returns line number range when hash is present in URL', () => { + window.location.hash = '#L10-42'; + expect(getLineRangeFromHash()).toStrictEqual({ beginning: 10, end: 42 }); + }); + + it('returns null when hash is not a line number', () => { + window.location.hash = '#L42InvalidHash123'; + expect(getLineRangeFromHash()).toBe(null); + }); + }); + + describe('appendLineRangeHashToUrl', () => { + beforeEach(() => { + window.location.hash = ''; + }); + + it('appends line number hash when present in URL', () => { + window.location.hash = '#L42'; + expect(appendLineRangeHashToUrl('https://example.com/ide')).toBe( + 'https://example.com/ide#L42', + ); + }); + + it('appends line number range hash when present in URL', () => { + window.location.hash = '#L10-42'; + expect(appendLineRangeHashToUrl('https://example.com/ide')).toBe( + 'https://example.com/ide#L10-42', + ); + }); + + it('returns original URL when no line number hash is present', () => { + window.location.hash = '#something-else'; + expect(appendLineRangeHashToUrl('https://example.com/ide')).toBe('https://example.com/ide'); + }); + + it('returns original URL when hash is not a line number', () => { + window.location.hash = '#L42InvalidHash123'; + expect(appendLineRangeHashToUrl('https://example.com/ide')).toBe('https://example.com/ide'); + }); + }); + describe('visitUrl', () => { let originalLocation; const mockUrl = 'http://example.com/page'; diff --git a/spec/frontend/vue_shared/components/web_ide_link_spec.js b/spec/frontend/vue_shared/components/web_ide_link_spec.js index 5e4a45b083b..0d408908f00 100644 --- a/spec/frontend/vue_shared/components/web_ide_link_spec.js +++ b/spec/frontend/vue_shared/components/web_ide_link_spec.js @@ -16,7 +16,7 @@ import { extendedWrapper, } from 'helpers/vue_test_utils_helper'; -import { visitUrl } from '~/lib/utils/url_utility'; +import { visitUrl, appendLineRangeHashToUrl } from '~/lib/utils/url_utility'; import getWritableForksQuery from '~/vue_shared/components/web_ide/get_writable_forks.query.graphql'; jest.mock('~/lib/utils/url_utility'); @@ -299,9 +299,12 @@ describe('vue_shared/components/web_ide_link', () => { }); it('when web ide button is clicked it opens in a new tab', async () => { + const transformation = 'foo'; + appendLineRangeHashToUrl.mockReturnValueOnce(transformation); findDisclosureDropdownItems().at(1).props().item.handle(); await nextTick(); - expect(visitUrl).toHaveBeenCalledWith(TEST_WEB_IDE_URL, true); + expect(appendLineRangeHashToUrl).toHaveBeenCalledWith(TEST_WEB_IDE_URL); + expect(visitUrl).toHaveBeenCalledWith(transformation, true); }); }); diff --git a/spec/support_specs/matchers/internal_events_matchers_spec.rb b/spec/support_specs/matchers/internal_events_matchers_spec.rb index 0a07e5092eb..4d87b222ffe 100644 --- a/spec/support_specs/matchers/internal_events_matchers_spec.rb +++ b/spec/support_specs/matchers/internal_events_matchers_spec.rb @@ -498,6 +498,200 @@ RSpec.describe 'Internal Events matchers', :clean_gitlab_redis_shared_state, fea .and change { user_1.reload.updated_at } .and not_change { User.count } end + + describe 'instrumentation and testing contexts' do + shared_examples 'works correctly' do + specify do + expect { subject } + .to trigger_internal_events('g_edit_by_sfe') + .and increment_usage_metrics('redis_hll_counters.ide_edit.g_edit_by_sfe_weekly') + end + end + + subject { track_event } + + context 'with retries', retry: 2 do + context "with independent failure after the event" do + subject do + track_event + + raise 'Fail first attempt, causing a retry' if RSpec.current_example.attempts == 0 + end + + it_behaves_like "works correctly" + end + + context "with independent failure before the event" do + subject do + raise 'Fail first attempt, causing a retry' if RSpec.current_example.attempts == 0 + + track_event + end + + it_behaves_like "works correctly" + end + + context "with criteria met only on retry" do + subject do + track_event if RSpec.current_example.attempts > 0 + end + + it_behaves_like "works correctly" + end + end + + context 'without clearing Redis state between examples', clean_gitlab_redis_shared_state: false do + it_behaves_like "works correctly" + end + + context 'with snowplow not stubbed by default', :do_not_stub_snowplow_by_default do + it_behaves_like "works correctly" + end + + context 'with aggregate_failures', :aggregate_failures do + context "with no failures" do + it_behaves_like "works correctly" + end + + context "with failures" do + it "fails" do + pending('This example should always fail. Protects against false positives.') + + # we can't test the error message because :aggregate_failures rescues errors + expect { nil }.to(trigger_internal_events('g_edit_by_sfe')) + expect { nil }.to(increment_usage_metrics('redis_hll_counters.ide_edit.g_edit_by_sfe_weekly')) + end + end + end + + context 'when triggered from controllers' do + context 'when using controller specs', type: :controller do + controller(BaseActionController) do + include ProductAnalyticsTracking + + track_internal_event :index, name: 'g_edit_by_sfe' + + def index + render html: 'index page' + end + + private + + def tracking_project_source; end + + def tracking_namespace_source; end + end + + subject do + sign_in user_1 + get :index + end + + it_behaves_like "works correctly" + end + + context 'when triggered from request specs', type: :request do + # ideally, this spec should be completed with a new controller generated inside the test + let_it_be(:project) { create(:project, :repository) } + let_it_be(:user) { create(:user, maintainer_of: project) } + + subject do + sign_in(user) + post namespace_project_create_blob_path( + namespace_id: project.namespace, + project_id: project, + id: 'master' + ), params: { + branch_name: 'master', + file_name: 'docs/EXAMPLE_FILE', + content: 'Added changes', + commit_message: 'Create CHANGELOG' + } + end + + it_behaves_like "works correctly" + end + + context 'when triggered from rails model lifecycle hooks' do + let(:test_model) do + Class.new(ApplicationRecord) do + self.table_name = :merge_requests + + after_initialize do + Gitlab::InternalEvents.track_event( + 'create_merge_request', + user: user, + project: project + ) + end + + attr_accessor :user, :project + end + end + + it 'works correctly' do + expect { test_model.new(user: user_1, project: project_1) } + .to trigger_internal_events('create_merge_request') + .with(user: user_1, project: project_1) + .and increment_usage_metrics('counts.merge_request_create') + end + end + + context 'when triggered from sidekiq worker', :sidekiq_inline do + let(:test_worker) do + Class.new do + # `include ApplicationWorker` raises errors for unnamed classes + def self.name + 'Gitlab::TestUsageWorker' + end + + include ApplicationWorker + + def perform(event, user_id, namespace_id) + Gitlab::InternalEvents.track_event( + event, + user: User.find(user_id), + namespace: Group.find(namespace_id) + ) + end + end + end + + subject do + stub_const('Gitlab::TestUsageWorker', test_worker) + test_worker.perform_async( + 'g_edit_by_sfe', + user_1.id, + group_1.id + ) + end + + it_behaves_like "works correctly" + end + + context 'when triggered from migrations' do + let(:migration) do + Class.new(Gitlab::Database::Migration[Gitlab::Database::Migration.current_version]) do + milestone '18.0' + + @_defining_file = 'db/migrate/00000000000000_example.rb' + + def up + Gitlab::InternalEvents.track_event( + 'g_edit_by_sfe', + user: User.first, + namespace: Group.first + ) + end + end + end + + subject { migration.new.up } + + it_behaves_like "works correctly" + end + end + end end context "when using the 'internal event tracking' shared example" do diff --git a/storybook/config/webpack.config.js b/storybook/config/webpack.config.js index d2cd42a71c6..44c87d9a544 100644 --- a/storybook/config/webpack.config.js +++ b/storybook/config/webpack.config.js @@ -153,7 +153,6 @@ module.exports = function storybookWebpackConfig({ config }) { ); config.resolve.alias = { ...config.resolve.alias, - gridstack: require.resolve('gridstack/dist/es5/gridstack.js'), '@cubejs-client/core': require.resolve('@cubejs-client/core/dist/cubejs-client-core.js'), uuid: require.resolve('uuid'), '/assets': path.resolve(__dirname, '../../app/assets'), diff --git a/yarn.lock b/yarn.lock index 623e3fde527..bae0e7d979f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2591,51 +2591,51 @@ resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== -"@sentry-internal/browser-utils@9.19.0": - version "9.19.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-9.19.0.tgz#b7df4f1de6e467e62f255967e441de68b5db5c2f" - integrity sha512-DlEHX4eIHe5yIuh/cFu9OiaFuk1CTnFK95zj61I7Q2fxmN43dIwC3xAAGJ/Hy+GDQi7kU+BiS2sudSHSTq81BA== +"@sentry-internal/browser-utils@9.22.0": + version "9.22.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-9.22.0.tgz#dd05ac2ed925b70a0ca9bdec472f2566761dad91" + integrity sha512-Ou1tBnVxFAIn8i9gvrWzRotNJQYiu3awNXpsFCw6qFwmiKAVPa6b13vCdolhXnrIiuR77jY1LQnKh9hXpoRzsg== dependencies: - "@sentry/core" "9.19.0" + "@sentry/core" "9.22.0" -"@sentry-internal/feedback@9.19.0": - version "9.19.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-9.19.0.tgz#a3bf7a7a14d615eb3b8130a97b2c077767a54b27" - integrity sha512-yixRrv4NfpjhFW56AuUTjVwZlignB9FWAXXyrmRP3SsFeJCFrAsSD8HOxV9RXNr9ePYl7MEU0Agi43YWhJsiAw== +"@sentry-internal/feedback@9.22.0": + version "9.22.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-9.22.0.tgz#760f86903fde91a840bc77fc104b39a873301983" + integrity sha512-zgMVkoC61fgi41zLcSZA59vOtKxcLrKBo1ECYhPD1hxEaneNqY5fhXDwlQBw96P5l2yqkgfX6YZtSdU4ejI9yA== dependencies: - "@sentry/core" "9.19.0" + "@sentry/core" "9.22.0" -"@sentry-internal/replay-canvas@9.19.0": - version "9.19.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-9.19.0.tgz#0f830ebadb7c49a3fb04457f1510aa7972445780" - integrity sha512-YC8yrOjuKSfQgGniJnzkdbFsWEPTlNpzeeYPTxS4ouH1FwfGrSkPmcddjor2YHaLfiuHHqQ/Vvq70n+zruJH7A== +"@sentry-internal/replay-canvas@9.22.0": + version "9.22.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-9.22.0.tgz#431e289a5db247265e640a1e7aecf9ff2dba6a70" + integrity sha512-EcG9IMSEalFe49kowBTJObWjof/iHteDwpyuAszsFDdQUYATrVUtwpwN7o52vDYWJud4arhjrQnMamIGxa79eQ== dependencies: - "@sentry-internal/replay" "9.19.0" - "@sentry/core" "9.19.0" + "@sentry-internal/replay" "9.22.0" + "@sentry/core" "9.22.0" -"@sentry-internal/replay@9.19.0": - version "9.19.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-9.19.0.tgz#a1068ba5ba8eee9cd2459081be6e11bd3469d401" - integrity sha512-i/X9brRchbAF25yjxLTI7E8eoESRPBgIyQOWoWRXXt2n51iBRTjLXSaEfGvjdN+qrMq/yd6nC1/UqJVxXHeIhA== +"@sentry-internal/replay@9.22.0": + version "9.22.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-9.22.0.tgz#e67ed6cee4cf481d428986e4419ee099669df9ef" + integrity sha512-9GOycoKbrclcRXfcbNV8svbmAsOS5R4wXBQmKF4pFLkmFA/lJv9kdZSNYkRvkrxdNfbMIJXP+DV9EqTZcryXig== dependencies: - "@sentry-internal/browser-utils" "9.19.0" - "@sentry/core" "9.19.0" + "@sentry-internal/browser-utils" "9.22.0" + "@sentry/core" "9.22.0" -"@sentry/browser@9.19.0": - version "9.19.0" - resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-9.19.0.tgz#17d5e2ef30e75083c56955cdbe5c2db5deabb542" - integrity sha512-efKfPQ0yQkdIkC7qJ5TIHxnecLNENGUYl1YD/TC8yyzW2JRf/3OYo5yg1hY2rhsP5RwQShXlT7uA03ABVIkA4A== +"@sentry/browser@9.22.0": + version "9.22.0" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-9.22.0.tgz#6eee0c047612b35e7ab4960350a57ffcc51f76f4" + integrity sha512-3TeRm74dvX0JdjX0AgkQa+22iUHwHnY+Q6M05NZ+tDeCNHGK/mEBTeqquS1oQX67jWyuvYmG3VV6RJUxtG9Paw== dependencies: - "@sentry-internal/browser-utils" "9.19.0" - "@sentry-internal/feedback" "9.19.0" - "@sentry-internal/replay" "9.19.0" - "@sentry-internal/replay-canvas" "9.19.0" - "@sentry/core" "9.19.0" + "@sentry-internal/browser-utils" "9.22.0" + "@sentry-internal/feedback" "9.22.0" + "@sentry-internal/replay" "9.22.0" + "@sentry-internal/replay-canvas" "9.22.0" + "@sentry/core" "9.22.0" -"@sentry/core@9.19.0": - version "9.19.0" - resolved "https://registry.yarnpkg.com/@sentry/core/-/core-9.19.0.tgz#b0c291494abd4fdefb8f77d200e381c3f7167c3f" - integrity sha512-I41rKpMJHHZb0z0Nja+Lxto6IkEEmX3uWjnECypF8Z1HIjeJB0+PXl8p/7TeaKYqw2J2GYcRTg7jQZDmvKle1w== +"@sentry/core@9.22.0": + version "9.22.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-9.22.0.tgz#b35113bcba24a6ae437c548c2cbac941302a60ef" + integrity sha512-ixvtKmPF42Y6ckGUbFlB54OWI75H2gO5UYHojO6eXFpS7xO3ZGgV/QH6wb40mWK+0w5XZ0233FuU9VpsuE6mKA== "@sinclair/typebox@^0.27.8": version "0.27.8" @@ -8557,10 +8557,10 @@ graphql@16.11.0: resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.11.0.tgz#96d17f66370678027fdf59b2d4c20b4efaa8a633" integrity sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw== -gridstack@^11.1.2: - version "11.1.2" - resolved "https://registry.yarnpkg.com/gridstack/-/gridstack-11.1.2.tgz#e72091e2883f7b37cbd150c218d38eebc9fc4f18" - integrity sha512-6wJ5RffnFchF63/Yhs6tcZcWxRG1EgCnxgejbQsAjQ6Qj8QqKjew73jPq5c1yCAiyEAsXxI2tOJ8lZABOAZxoQ== +gridstack@^12.1.2: + version "12.1.2" + resolved "https://registry.yarnpkg.com/gridstack/-/gridstack-12.1.2.tgz#784f6d55873bb48fa9230c1284f769c9fbf785a8" + integrity sha512-IC1mkm5xonhAnftwIxsG+B3bawxC61ciKWEvX15ExpVQPbNVN7O9aZZhM7Y/eE4JaIR8PXrdkjd12gMnwNYRLQ== gzip-size@^6.0.0: version "6.0.0"