From 8f3a9dbb94b5a9ae4570a22bbc2a75e7572407c8 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Fri, 19 Jan 2024 15:10:53 +0000 Subject: Add latest changes from gitlab-org/gitlab@master --- .rubocop_todo/gitlab/namespaced_class.yml | 1 - .rubocop_todo/layout/argument_alignment.yml | 2 - .rubocop_todo/layout/line_length.yml | 114 +----- .rubocop_todo/rspec/context_wording.yml | 1 - .rubocop_todo/rspec/expect_change.yml | 1 - .rubocop_todo/rspec/feature_category.yml | 2 - .rubocop_todo/rspec/named_subject.yml | 1 - GITLAB_SHELL_VERSION | 2 +- Gemfile | 3 +- Gemfile.checksum | 3 +- Gemfile.lock | 5 +- .../content_editor/services/upload_helpers.js | 25 +- .../components/merge_checks.vue | 12 +- .../filtered_search_bar/filtered_search_utils.js | 6 +- app/controllers/sent_notifications_controller.rb | 19 +- app/graphql/mutations/work_items/update_task.rb | 70 ---- app/graphql/types/mutation_type.rb | 1 - .../types/work_items/updated_task_input_type.rb | 11 - app/mailers/emails/service_desk.rb | 14 +- app/mailers/previews/notify_preview.rb | 3 +- app/models/sent_notification.rb | 1 + app/models/work_items/widgets/base.rb | 8 + app/models/work_items/widgets/hierarchy.rb | 14 + app/models/work_items/widgets/labels.rb | 4 + app/policies/group_policy.rb | 1 + .../sync_strategies/base_sync_strategy.rb | 2 +- app/services/issuable_base_service.rb | 5 + app/services/namespace_settings/update_service.rb | 4 + app/services/notification_service.rb | 10 +- app/services/work_items/create_service.rb | 15 + .../widgets/labels_service/base_service.rb | 25 ++ .../widgets/labels_service/create_service.rb | 18 + .../widgets/labels_service/update_service.rb | 12 +- app/views/projects/issues/_new_branch.html.haml | 11 +- .../click_house/concerns/consistency_worker.rb | 2 +- config/application.rb | 24 +- .../initializers_before_autoloader/004_zeitwerk.rb | 1 - db/docs/ci_freeze_periods.yml | 2 + db/docs/ci_resource_groups.yml | 2 + db/docs/deploy_keys_projects.yml | 10 +- db/docs/deployments.yml | 15 +- db/docs/dora_configurations.yml | 10 +- db/docs/environments.yml | 15 +- db/docs/group_deploy_keys_groups.yml | 10 +- db/docs/group_deploy_tokens.yml | 10 +- db/docs/project_deploy_tokens.yml | 10 +- ...e_email_participant_id_to_sent_notifications.rb | 9 + ..._notifications_on_issue_email_participant_id.rb | 17 + ...nt_notifications_to_issue_email_participants.rb | 23 ++ db/schema_migrations/20240104092321 | 1 + db/schema_migrations/20240104142200 | 1 + db/schema_migrations/20240104142216 | 1 + db/structure.sql | 8 +- doc/administration/gitaly/configure_gitaly.md | 28 +- doc/api/graphql/reference/index.md | 48 --- doc/api/groups.md | 6 +- doc/api/usage_data.md | 32 ++ .../lib/gitlab/http_v2/new_connection_adapter.rb | 2 +- lib/api/entities/group_detail.rb | 1 + lib/api/helpers/groups_helpers.rb | 1 + lib/api/usage_data.rb | 29 ++ lib/gitlab/click_house.rb | 11 + lib/gitlab/gitaly_client.rb | 2 + lib/gitlab/http.rb | 2 - lib/gitlab/http_connection_adapter.rb | 79 ---- lib/gitlab/import_export/remote_stream_upload.rb | 10 +- lib/gitlab/metrics/global_search_slis.rb | 5 +- .../strategies/click_house_migration.rb | 2 +- lib/tasks/gitlab/assets.rake | 2 + scripts/frontend/clean_css_assets.mjs | 72 ++++ scripts/frontend/compare_css_compilers.sh | 35 ++ .../sent_notifications_controller_spec.rb | 32 +- .../content_editor/extensions/attachment_spec.js | 398 ++++++++++++++++++--- .../mutations/work_items/update_task_spec.rb | 36 -- spec/lib/api/entities/group_detail_spec.rb | 46 ++- spec/lib/gitlab/click_house_spec.rb | 17 + spec/lib/gitlab/gitaly_client_spec.rb | 14 + spec/lib/gitlab/http_connection_adapter_spec.rb | 161 --------- spec/lib/gitlab/import_export/all_models.yml | 1 + .../import_export/remote_stream_upload_spec.rb | 26 +- spec/lib/gitlab/metrics/global_search_slis_spec.rb | 48 ++- spec/mailers/emails/service_desk_spec.rb | 24 +- spec/mailers/notify_spec.rb | 7 +- spec/models/sent_notification_spec.rb | 22 +- .../mutations/work_items/update_task_spec.rb | 85 ----- spec/requests/api/groups_spec.rb | 30 ++ spec/requests/api/usage_data_spec.rb | 58 +++ .../sync_strategies/base_sync_strategy_spec.rb | 4 +- .../sync_strategies/event_sync_strategy_spec.rb | 6 +- .../namespace_settings/update_service_spec.rb | 1 + spec/services/notification_service_spec.rb | 85 ++++- .../widgets/labels_service/create_service_spec.rb | 56 +++ spec/support/rspec_order_todo.yml | 3 - .../policies/group_policy_shared_context.rb | 1 + .../event_authors_consistency_cron_worker_spec.rb | 4 +- workhorse/internal/helper/command/command.go | 6 +- workhorse/internal/helper/command/command_test.go | 97 +++++ 97 files changed, 1418 insertions(+), 814 deletions(-) delete mode 100644 app/graphql/mutations/work_items/update_task.rb delete mode 100644 app/graphql/types/work_items/updated_task_input_type.rb create mode 100644 app/services/work_items/widgets/labels_service/base_service.rb create mode 100644 app/services/work_items/widgets/labels_service/create_service.rb create mode 100644 db/migrate/20240104092321_add_issue_email_participant_id_to_sent_notifications.rb create mode 100644 db/migrate/20240104142200_add_index_sent_notifications_on_issue_email_participant_id.rb create mode 100644 db/migrate/20240104142216_add_fk_on_sent_notifications_to_issue_email_participants.rb create mode 100644 db/schema_migrations/20240104092321 create mode 100644 db/schema_migrations/20240104142200 create mode 100644 db/schema_migrations/20240104142216 create mode 100644 lib/gitlab/click_house.rb delete mode 100644 lib/gitlab/http_connection_adapter.rb create mode 100755 scripts/frontend/clean_css_assets.mjs create mode 100755 scripts/frontend/compare_css_compilers.sh delete mode 100644 spec/graphql/mutations/work_items/update_task_spec.rb create mode 100644 spec/lib/gitlab/click_house_spec.rb delete mode 100644 spec/lib/gitlab/http_connection_adapter_spec.rb delete mode 100644 spec/requests/api/graphql/mutations/work_items/update_task_spec.rb create mode 100644 spec/services/work_items/widgets/labels_service/create_service_spec.rb create mode 100644 workhorse/internal/helper/command/command_test.go diff --git a/.rubocop_todo/gitlab/namespaced_class.yml b/.rubocop_todo/gitlab/namespaced_class.yml index 4d3c8eb4a90..4cc5f4ffef0 100644 --- a/.rubocop_todo/gitlab/namespaced_class.yml +++ b/.rubocop_todo/gitlab/namespaced_class.yml @@ -1136,7 +1136,6 @@ Gitlab/NamespacedClass: - 'lib/gitlab/highlight.rb' - 'lib/gitlab/hotlinking_detector.rb' - 'lib/gitlab/http.rb' - - 'lib/gitlab/http_connection_adapter.rb' - 'lib/gitlab/http_io.rb' - 'lib/gitlab/import_formatter.rb' - 'lib/gitlab/inactive_projects_deletion_warning_tracker.rb' diff --git a/.rubocop_todo/layout/argument_alignment.yml b/.rubocop_todo/layout/argument_alignment.yml index cf490823f76..5f90ff11fd3 100644 --- a/.rubocop_todo/layout/argument_alignment.yml +++ b/.rubocop_todo/layout/argument_alignment.yml @@ -147,7 +147,6 @@ Layout/ArgumentAlignment: - 'app/graphql/mutations/work_items/create_from_task.rb' - 'app/graphql/mutations/work_items/delete.rb' - 'app/graphql/mutations/work_items/update.rb' - - 'app/graphql/mutations/work_items/update_task.rb' - 'app/graphql/resolvers/admin/analytics/usage_trends/measurements_resolver.rb' - 'app/graphql/resolvers/alert_management/alert_resolver.rb' - 'app/graphql/resolvers/alert_management/alert_status_counts_resolver.rb' @@ -1214,7 +1213,6 @@ Layout/ArgumentAlignment: - 'lib/gitlab/graphql/expose_permissions.rb' - 'lib/gitlab/graphql/mount_mutation.rb' - 'lib/gitlab/graphql/negatable_arguments.rb' - - 'lib/gitlab/http_connection_adapter.rb' - 'lib/gitlab/import_export/base/relation_object_saver.rb' - 'lib/gitlab/import_export/importer.rb' - 'lib/gitlab/import_export/members_mapper.rb' diff --git a/.rubocop_todo/layout/line_length.yml b/.rubocop_todo/layout/line_length.yml index 4f51b3f614a..378c8adc69c 100644 --- a/.rubocop_todo/layout/line_length.yml +++ b/.rubocop_todo/layout/line_length.yml @@ -132,7 +132,6 @@ Layout/LineLength: - 'app/graphql/mutations/merge_requests/set_labels.rb' - 'app/graphql/mutations/merge_requests/set_locked.rb' - 'app/graphql/mutations/merge_requests/set_milestone.rb' - - 'app/graphql/mutations/metrics/dashboard/annotations/create.rb' - 'app/graphql/mutations/namespace/package_settings/update.rb' - 'app/graphql/mutations/notes/create/note.rb' - 'app/graphql/mutations/packages/destroy.rb' @@ -224,7 +223,6 @@ Layout/LineLength: - 'app/helpers/import_helper.rb' - 'app/helpers/in_product_marketing_helper.rb' - 'app/helpers/issuables_helper.rb' - - 'app/helpers/jira_connect_helper.rb' - 'app/helpers/labels_helper.rb' - 'app/helpers/merge_requests_helper.rb' - 'app/helpers/mirror_helper.rb' @@ -335,7 +333,6 @@ Layout/LineLength: - 'app/models/integrations/base_chat_notification.rb' - 'app/models/integrations/base_issue_tracker.rb' - 'app/models/integrations/bugzilla.rb' - - 'app/models/integrations/chat_message/deployment_message.rb' - 'app/models/integrations/chat_message/merge_message.rb' - 'app/models/integrations/chat_message/note_message.rb' - 'app/models/integrations/chat_message/pipeline_message.rb' @@ -348,7 +345,6 @@ Layout/LineLength: - 'app/models/integrations/emails_on_push.rb' - 'app/models/integrations/ewm.rb' - 'app/models/integrations/external_wiki.rb' - - 'app/models/integrations/hangouts_chat.rb' - 'app/models/integrations/harbor.rb' - 'app/models/integrations/jenkins.rb' - 'app/models/integrations/mattermost.rb' @@ -372,7 +368,6 @@ Layout/LineLength: - 'app/models/lfs_object.rb' - 'app/models/lfs_objects_project.rb' - 'app/models/list.rb' - - 'app/models/loose_foreign_keys/modification_tracker.rb' - 'app/models/member.rb' - 'app/models/merge_request.rb' - 'app/models/merge_request_assignee.rb' @@ -403,7 +398,6 @@ Layout/LineLength: - 'app/models/project_statistics.rb' - 'app/models/projects/topic.rb' - 'app/models/prometheus_metric.rb' - - 'app/models/protected_branch/push_access_level.rb' - 'app/models/release.rb' - 'app/models/releases/link.rb' - 'app/models/remote_mirror.rb' @@ -448,7 +442,6 @@ Layout/LineLength: - 'app/serializers/merge_request_sidebar_extras_entity.rb' - 'app/serializers/merge_request_widget_entity.rb' - 'app/serializers/note_entity.rb' - - 'app/serializers/project_note_entity.rb' - 'app/services/application_settings/update_service.rb' - 'app/services/auth/container_registry_authentication_service.rb' - 'app/services/auto_merge/merge_when_pipeline_succeeds_service.rb' @@ -460,7 +453,6 @@ Layout/LineLength: - 'app/services/bulk_imports/uploads_export_service.rb' - 'app/services/ci/create_pipeline_service.rb' - 'app/services/ci/drop_pipeline_service.rb' - - 'app/services/ci/generate_coverage_reports_service.rb' - 'app/services/ci/job_artifacts/expire_project_build_artifacts_service.rb' - 'app/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service.rb' - 'app/services/ci/pipelines/add_job_service.rb' @@ -504,11 +496,9 @@ Layout/LineLength: - 'app/services/groups/update_service.rb' - 'app/services/import/bitbucket_server_service.rb' - 'app/services/import/github_service.rb' - - 'app/services/import/validate_remote_git_endpoint_service.rb' - 'app/services/issuable/process_assignees.rb' - 'app/services/issuable_base_service.rb' - 'app/services/issuable_links/create_service.rb' - - 'app/services/issues/base_service.rb' - 'app/services/issues/clone_service.rb' - 'app/services/issues/close_service.rb' - 'app/services/issues/duplicate_service.rb' @@ -534,7 +524,6 @@ Layout/LineLength: - 'app/services/merge_requests/base_service.rb' - 'app/services/merge_requests/build_service.rb' - 'app/services/merge_requests/create_from_issue_service.rb' - - 'app/services/merge_requests/merge_base_service.rb' - 'app/services/merge_requests/merge_service.rb' - 'app/services/merge_requests/mergeability_check_service.rb' - 'app/services/merge_requests/push_options_handler_service.rb' @@ -581,7 +570,6 @@ Layout/LineLength: - 'app/services/projects/overwrite_project_service.rb' - 'app/services/projects/transfer_service.rb' - 'app/services/projects/unlink_fork_service.rb' - - 'app/services/projects/update_pages_service.rb' - 'app/services/projects/update_service.rb' - 'app/services/protected_branches/update_service.rb' - 'app/services/quick_actions/target_service.rb' @@ -675,7 +663,6 @@ Layout/LineLength: - 'config/routes/import.rb' - 'config/routes/project.rb' - 'config/routes/repository.rb' - - 'config/routes/uploads.rb' - 'config/routes/user.rb' - 'config/settings.rb' - 'danger/ci_config/Dangerfile' @@ -685,7 +672,6 @@ Layout/LineLength: - 'danger/roulette/Dangerfile' - 'danger/vue_shared_documentation/Dangerfile' - 'ee/app/controllers/admin/elasticsearch_controller.rb' - - 'ee/app/controllers/admin/geo/application_controller.rb' - 'ee/app/controllers/admin/licenses_controller.rb' - 'ee/app/controllers/concerns/audit_events/date_range.rb' - 'ee/app/controllers/concerns/credentials_inventory_actions.rb' @@ -730,7 +716,6 @@ Layout/LineLength: - 'ee/app/graphql/ee/mutations/ci/runner/update.rb' - 'ee/app/graphql/mutations/analytics/devops_adoption/enabled_namespaces/bulk_enable.rb' - 'ee/app/graphql/mutations/audit_events/external_audit_event_destinations/create.rb' - - 'ee/app/graphql/mutations/audit_events/external_audit_event_destinations/update.rb' - 'ee/app/graphql/mutations/boards/epic_boards/epic_move_list.rb' - 'ee/app/graphql/mutations/boards/scoped_issue_board_arguments.rb' - 'ee/app/graphql/mutations/compliance_management/frameworks/destroy.rb' @@ -807,7 +792,6 @@ Layout/LineLength: - 'ee/app/helpers/billing_plans_helper.rb' - 'ee/app/helpers/ee/application_helper.rb' - 'ee/app/helpers/ee/button_helper.rb' - - 'ee/app/helpers/ee/geo_helper.rb' - 'ee/app/helpers/ee/groups/settings_helper.rb' - 'ee/app/helpers/ee/groups_helper.rb' - 'ee/app/helpers/ee/import_helper.rb' @@ -818,7 +802,6 @@ Layout/LineLength: - 'ee/app/helpers/ee/merge_requests_helper.rb' - 'ee/app/helpers/ee/mirror_helper.rb' - 'ee/app/helpers/ee/notes_helper.rb' - - 'ee/app/helpers/ee/personal_access_tokens_helper.rb' - 'ee/app/helpers/ee/profiles_helper.rb' - 'ee/app/helpers/ee/projects_helper.rb' - 'ee/app/helpers/ee/search_helper.rb' @@ -878,7 +861,6 @@ Layout/LineLength: - 'ee/app/models/ee/packages/package_file.rb' - 'ee/app/models/ee/pages_deployment.rb' - 'ee/app/models/ee/project.rb' - - 'ee/app/models/ee/resource_label_event.rb' - 'ee/app/models/ee/snippet_repository.rb' - 'ee/app/models/ee/terraform/state_version.rb' - 'ee/app/models/ee/upload.rb' @@ -901,7 +883,6 @@ Layout/LineLength: - 'ee/app/models/merge_requests/compliance_violation.rb' - 'ee/app/models/merge_requests/external_status_check.rb' - 'ee/app/models/productivity_analytics.rb' - - 'ee/app/models/project_repository_state.rb' - 'ee/app/models/protected_environment.rb' - 'ee/app/models/requirements_management/requirement.rb' - 'ee/app/models/requirements_management/test_report.rb' @@ -952,7 +933,6 @@ Layout/LineLength: - 'ee/app/services/app_sec/dast/site_validations/runner_service.rb' - 'ee/app/services/audit_events/runner_audit_event_service.rb' - 'ee/app/services/audit_events/runner_custom_audit_event_service.rb' - - 'ee/app/services/audit_events/user_impersonation_group_audit_event_service.rb' - 'ee/app/services/auto_merge/add_to_merge_train_when_pipeline_succeeds_service.rb' - 'ee/app/services/boards/epics/create_service.rb' - 'ee/app/services/boards/epics/move_service.rb' @@ -1019,7 +999,6 @@ Layout/LineLength: - 'ee/app/services/incident_management/incidents/upload_metric_service.rb' - 'ee/app/services/incident_management/oncall_rotations/create_service.rb' - 'ee/app/services/incident_management/oncall_rotations/edit_service.rb' - - 'ee/app/services/incident_management/oncall_schedules/update_service.rb' - 'ee/app/services/incident_management/pending_escalations/process_service.rb' - 'ee/app/services/iterations/cadences/create_iterations_in_advance_service.rb' - 'ee/app/services/iterations/cadences/create_service.rb' @@ -1031,7 +1010,6 @@ Layout/LineLength: - 'ee/app/services/jira/jql_builder_service.rb' - 'ee/app/services/jira/requests/issues/list_service.rb' - 'ee/app/services/merge_requests/create_from_vulnerability_data_service.rb' - - 'ee/app/services/merge_trains/create_pipeline_service.rb' - 'ee/app/services/merge_trains/refresh_merge_request_service.rb' - 'ee/app/services/personal_access_tokens/rotation_verifier_service.rb' - 'ee/app/services/projects/mark_for_deletion_service.rb' @@ -1170,7 +1148,6 @@ Layout/LineLength: - 'ee/lib/ee/gitlab/background_migration/populate_resolved_on_default_branch_column.rb' - 'ee/lib/ee/gitlab/checks/push_rules/commit_check.rb' - 'ee/lib/ee/gitlab/checks/push_rules/file_size_check.rb' - - 'ee/lib/ee/gitlab/ci/config_ee.rb' - 'ee/lib/ee/gitlab/ci/pipeline/chain/create_cross_database_associations.rb' - 'ee/lib/ee/gitlab/ci/pipeline/chain/validate/after_config.rb' - 'ee/lib/ee/gitlab/ci/pipeline/chain/validate/security_orchestration_policy.rb' @@ -1212,7 +1189,6 @@ Layout/LineLength: - 'ee/lib/gitlab/auth/group_saml/xml_response.rb' - 'ee/lib/gitlab/authority_analyzer.rb' - 'ee/lib/gitlab/ci/config/security_orchestration_policies/processor.rb' - - 'ee/lib/gitlab/ci/reports/dependency_list/report.rb' - 'ee/lib/gitlab/ci/reports/security/locations/cluster_image_scanning.rb' - 'ee/lib/gitlab/contribution_analytics/data_collector.rb' - 'ee/lib/gitlab/elastic/group_search_results.rb' @@ -1237,7 +1213,6 @@ Layout/LineLength: - 'ee/lib/gitlab/insights/serializers/chartjs/bar_serializer.rb' - 'ee/lib/gitlab/insights/serializers/chartjs/multi_series_serializer.rb' - 'ee/lib/gitlab/insights/validators/params_validator.rb' - - 'ee/lib/gitlab/ip_restriction/enforcer.rb' - 'ee/lib/gitlab/manual_quarterly_co_term_banner.rb' - 'ee/lib/gitlab/sitemaps/generator.rb' - 'ee/lib/gitlab/status_page/storage/s3_client.rb' @@ -1287,14 +1262,12 @@ Layout/LineLength: - 'ee/spec/controllers/projects/pipelines_controller_spec.rb' - 'ee/spec/controllers/projects/protected_environments_controller_spec.rb' - 'ee/spec/controllers/projects/push_rules_controller_spec.rb' - - 'ee/spec/controllers/projects/security/configuration_controller_spec.rb' - 'ee/spec/controllers/projects/security/vulnerabilities_controller_spec.rb' - 'ee/spec/controllers/projects/subscriptions_controller_spec.rb' - 'ee/spec/controllers/projects/vulnerability_feedback_controller_spec.rb' - 'ee/spec/controllers/projects_controller_spec.rb' - 'ee/spec/controllers/subscriptions_controller_spec.rb' - 'ee/spec/elastic/migrate/migration_shared_examples.rb' - - 'ee/spec/factories/ci/builds.rb' - 'ee/spec/factories/ci/job_artifacts.rb' - 'ee/spec/factories/ci/pipelines.rb' - 'ee/spec/factories/ci/reports/license_scanning/report.rb' @@ -1349,7 +1322,6 @@ Layout/LineLength: - 'ee/spec/features/groups/security/compliance_dashboards_spec.rb' - 'ee/spec/features/integrations/jira/jira_issues_list_spec.rb' - 'ee/spec/features/issues/filtered_search/filter_issues_weight_spec.rb' - - 'ee/spec/features/issues/form_spec.rb' - 'ee/spec/features/labels_hierarchy_spec.rb' - 'ee/spec/features/merge_request/user_approves_spec.rb' - 'ee/spec/features/merge_request/user_approves_with_password_spec.rb' @@ -1361,12 +1333,10 @@ Layout/LineLength: - 'ee/spec/features/merge_request/user_sets_approvers_spec.rb' - 'ee/spec/features/merge_requests/user_filters_by_approvers_spec.rb' - 'ee/spec/features/merge_requests/user_views_all_merge_requests_spec.rb' - - 'ee/spec/features/merge_trains/user_adds_to_merge_train_when_pipeline_succeeds_spec.rb' - 'ee/spec/features/pending_group_memberships_spec.rb' - 'ee/spec/features/projects/audit_events_spec.rb' - 'ee/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb' - 'ee/spec/features/projects/feature_flags/user_updates_feature_flag_spec.rb' - - 'ee/spec/features/projects/integrations/user_activates_jira_spec.rb' - 'ee/spec/features/projects/iterations/iteration_cadences_list_spec.rb' - 'ee/spec/features/projects/iterations/user_views_iteration_spec.rb' - 'ee/spec/features/projects/licenses/maintainer_views_policies_spec.rb' @@ -1375,7 +1345,6 @@ Layout/LineLength: - 'ee/spec/features/projects/new_project_spec.rb' - 'ee/spec/features/projects/pipelines/pipeline_spec.rb' - 'ee/spec/features/projects/quality/test_case_list_spec.rb' - - 'ee/spec/features/projects/quality/test_case_show_spec.rb' - 'ee/spec/features/projects/requirements_management/requirements_list_spec.rb' - 'ee/spec/features/projects/settings/ee/service_desk_setting_spec.rb' - 'ee/spec/features/projects/settings/issues_settings_spec.rb' @@ -1435,7 +1404,6 @@ Layout/LineLength: - 'ee/spec/graphql/mutations/boards/epics/create_spec.rb' - 'ee/spec/graphql/mutations/boards/lists/update_limit_metrics_spec.rb' - 'ee/spec/graphql/mutations/compliance_management/frameworks/update_spec.rb' - - 'ee/spec/graphql/mutations/dast/profiles/run_spec.rb' - 'ee/spec/graphql/mutations/dast_on_demand_scans/create_spec.rb' - 'ee/spec/graphql/mutations/dast_scanner_profiles/update_spec.rb' - 'ee/spec/graphql/mutations/dast_site_profiles/create_spec.rb' @@ -1507,16 +1475,12 @@ Layout/LineLength: - 'ee/spec/helpers/ee/application_settings_helper_spec.rb' - 'ee/spec/helpers/ee/branches_helper_spec.rb' - 'ee/spec/helpers/ee/ci/pipelines_helper_spec.rb' - - 'ee/spec/helpers/ee/dashboard_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/groups/group_members_helper_spec.rb' - 'ee/spec/helpers/ee/groups_helper_spec.rb' - 'ee/spec/helpers/ee/integrations_helper_spec.rb' - - 'ee/spec/helpers/ee/issues_helper_spec.rb' - - 'ee/spec/helpers/ee/lock_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/dast_configuration_helper_spec.rb' @@ -1619,7 +1583,6 @@ Layout/LineLength: - 'ee/spec/lib/gitlab/ci/config/entry/job_spec.rb' - 'ee/spec/lib/gitlab/ci/config/security_orchestration_policies/processor_spec.rb' - 'ee/spec/lib/gitlab/ci/minutes/cost_factor_spec.rb' - - 'ee/spec/lib/gitlab/ci/minutes/runners_availability_spec.rb' - 'ee/spec/lib/gitlab/ci/parsers/license_compliance/license_scanning_spec.rb' - 'ee/spec/lib/gitlab/ci/parsers/security/cluster_image_scanning_spec.rb' - 'ee/spec/lib/gitlab/ci/parsers/security/container_scanning_spec.rb' @@ -1694,7 +1657,6 @@ Layout/LineLength: - 'ee/spec/lib/gitlab/usage/metrics/instrumentations/count_users_associating_group_milestones_to_releases_metric_spec.rb' - 'ee/spec/lib/gitlab/usage/metrics/instrumentations/count_users_creating_ci_builds_metric_spec.rb' - 'ee/spec/lib/gitlab/usage/metrics/instrumentations/license_metric_spec.rb' - - 'ee/spec/lib/gitlab/usage_data_metrics_spec.rb' - 'ee/spec/lib/gitlab_subscriptions/upcoming_reconciliation_entity_spec.rb' - 'ee/spec/lib/incident_management/oncall_shift_generator_spec.rb' - 'ee/spec/lib/omni_auth/strategies/group_saml_spec.rb' @@ -1762,16 +1724,19 @@ Layout/LineLength: - 'ee/spec/models/ee/audit_event_spec.rb' - 'ee/spec/models/ee/ci/job_artifact_spec.rb' - 'ee/spec/models/ee/ci/pipeline_artifact_spec.rb' + - 'ee/spec/models/ee/group_member_spec.rb' - 'ee/spec/models/ee/group_spec.rb' - 'ee/spec/models/ee/incident_management/project_incident_management_setting_spec.rb' - 'ee/spec/models/ee/integrations/jira_spec.rb' - 'ee/spec/models/ee/iterations/cadence_spec.rb' - 'ee/spec/models/ee/lfs_object_spec.rb' + - 'ee/spec/models/ee/member_spec.rb' - 'ee/spec/models/ee/merge_request_diff_spec.rb' - 'ee/spec/models/ee/namespace_spec.rb' - 'ee/spec/models/ee/namespace_statistics_spec.rb' - 'ee/spec/models/ee/preloaders/group_policy_preloader_spec.rb' - 'ee/spec/models/ee/project_authorization_spec.rb' + - 'ee/spec/models/ee/project_member_spec.rb' - 'ee/spec/models/ee/project_spec.rb' - 'ee/spec/models/ee/protected_branch_spec.rb' - 'ee/spec/models/ee/service_desk_setting_spec.rb' @@ -1790,7 +1755,6 @@ Layout/LineLength: - 'ee/spec/models/geo_node_status_spec.rb' - 'ee/spec/models/gitlab_subscription_spec.rb' - 'ee/spec/models/gitlab_subscriptions/features_spec.rb' - - 'ee/spec/models/ee/group_member_spec.rb' - 'ee/spec/models/historical_data_spec.rb' - 'ee/spec/models/incident_management/escalation_policy_spec.rb' - 'ee/spec/models/incident_management/escalation_rule_spec.rb' @@ -1803,7 +1767,6 @@ Layout/LineLength: - 'ee/spec/models/issue_spec.rb' - 'ee/spec/models/iteration_spec.rb' - 'ee/spec/models/license_spec.rb' - - 'ee/spec/models/ee/member_spec.rb' - 'ee/spec/models/merge_request_spec.rb' - 'ee/spec/models/merge_requests/compliance_violation_spec.rb' - 'ee/spec/models/merge_requests/external_status_check_spec.rb' @@ -1812,7 +1775,6 @@ Layout/LineLength: - 'ee/spec/models/packages/package_file_spec.rb' - 'ee/spec/models/project_import_data_spec.rb' - 'ee/spec/models/project_import_state_spec.rb' - - 'ee/spec/models/ee/project_member_spec.rb' - 'ee/spec/models/project_security_setting_spec.rb' - 'ee/spec/models/protected_environment_spec.rb' - 'ee/spec/models/protected_environments/approval_rule_spec.rb' @@ -1840,10 +1802,8 @@ Layout/LineLength: - 'ee/spec/models/vulnerabilities/statistic_spec.rb' - 'ee/spec/policies/group_policy_spec.rb' - 'ee/spec/policies/issuable_policy_spec.rb' - - 'ee/spec/policies/issue_policy_spec.rb' - 'ee/spec/policies/merge_request_policy_spec.rb' - 'ee/spec/policies/project_policy_spec.rb' - - 'ee/spec/policies/protected_branch_policy_spec.rb' - 'ee/spec/policies/vulnerabilities/export_policy_spec.rb' - 'ee/spec/policies/vulnerabilities/external_issue_link_policy_spec.rb' - 'ee/spec/presenters/audit_event_presenter_spec.rb' @@ -1860,7 +1820,6 @@ Layout/LineLength: - 'ee/spec/requests/admin/user_permission_exports_controller_spec.rb' - 'ee/spec/requests/api/analytics/code_review_analytics_spec.rb' - 'ee/spec/requests/api/audit_events_spec.rb' - - 'ee/spec/requests/api/branches_spec.rb' - 'ee/spec/requests/api/ci/jobs_spec.rb' - 'ee/spec/requests/api/ci/minutes_spec.rb' - 'ee/spec/requests/api/ci/pipelines_spec.rb' @@ -1870,7 +1829,6 @@ Layout/LineLength: - 'ee/spec/requests/api/elasticsearch_indexed_namespaces_spec.rb' - 'ee/spec/requests/api/epic_issues_spec.rb' - 'ee/spec/requests/api/epics_spec.rb' - - 'ee/spec/requests/api/geo_nodes_spec.rb' - 'ee/spec/requests/api/geo_spec.rb' - 'ee/spec/requests/api/graphql/analytics/devops_adoption/enabled_namespaces_spec.rb' - 'ee/spec/requests/api/graphql/boards/board_list_query_spec.rb' @@ -1878,7 +1836,6 @@ Layout/LineLength: - 'ee/spec/requests/api/graphql/boards/epic_boards_query_spec.rb' - 'ee/spec/requests/api/graphql/compliance_management/merge_requests/compliance_violations_spec.rb' - 'ee/spec/requests/api/graphql/current_user/groups_query_spec.rb' - - 'ee/spec/requests/api/graphql/epics/epic_resolver_spec.rb' - 'ee/spec/requests/api/graphql/group/dast_profile_schedule_spec.rb' - 'ee/spec/requests/api/graphql/group/epic/epic_aggregate_query_spec.rb' - 'ee/spec/requests/api/graphql/group/epics_spec.rb' @@ -1978,7 +1935,6 @@ Layout/LineLength: - 'ee/spec/requests/groups/group_members_controller_spec.rb' - 'ee/spec/requests/groups/roadmap_controller_spec.rb' - 'ee/spec/requests/groups/security/credentials_controller_spec.rb' - - 'ee/spec/requests/groups_controller_spec.rb' - 'ee/spec/requests/jwt_controller_spec.rb' - 'ee/spec/requests/lfs_http_spec.rb' - 'ee/spec/requests/projects/merge_requests_controller_spec.rb' @@ -2083,10 +2039,8 @@ Layout/LineLength: - 'ee/spec/services/ee/issues/move_service_spec.rb' - 'ee/spec/services/ee/issues/update_service_spec.rb' - 'ee/spec/services/ee/members/destroy_service_spec.rb' - - 'ee/spec/services/merge_requests/create_from_vulnerability_data_service_spec.rb' - 'ee/spec/services/ee/merge_requests/post_merge_service_spec.rb' - 'ee/spec/services/ee/merge_requests/refresh_service_spec.rb' - - 'ee/spec/services/ee/merge_requests/update_service_spec.rb' - 'ee/spec/services/ee/notes/update_service_spec.rb' - 'ee/spec/services/ee/notification_service_spec.rb' - 'ee/spec/services/ee/post_receive_service_spec.rb' @@ -2107,7 +2061,6 @@ Layout/LineLength: - 'ee/spec/services/epics/update_service_spec.rb' - 'ee/spec/services/external_status_checks/update_service_spec.rb' - 'ee/spec/services/geo/blob_download_service_spec.rb' - - 'ee/spec/services/geo/blob_upload_service_spec.rb' - 'ee/spec/services/geo/container_repository_sync_spec.rb' - 'ee/spec/services/geo/framework_repository_sync_service_spec.rb' - 'ee/spec/services/geo/hashed_storage_attachments_event_store_spec.rb' @@ -2140,6 +2093,7 @@ Layout/LineLength: - 'ee/spec/services/jira/requests/issues/list_service_spec.rb' - 'ee/spec/services/ldap_group_reset_service_spec.rb' - 'ee/spec/services/members/activate_service_spec.rb' + - 'ee/spec/services/merge_requests/create_from_vulnerability_data_service_spec.rb' - 'ee/spec/services/merge_requests/merge_service_spec.rb' - 'ee/spec/services/merge_requests/push_options_handler_service_spec.rb' - 'ee/spec/services/merge_requests/reset_approvals_service_spec.rb' @@ -2216,7 +2170,6 @@ Layout/LineLength: - 'ee/spec/services/vulnerability_feedback/create_service_spec.rb' - 'ee/spec/services/vulnerability_feedback/destroy_service_spec.rb' - 'ee/spec/services/vulnerability_scanners/list_service_spec.rb' - - 'ee/spec/services/wiki_pages/destroy_service_spec.rb' - 'ee/spec/support/features/redacted_search_results_examples.rb' - 'ee/spec/support/helpers/search_results_helpers.rb' - 'ee/spec/support/helpers/subscription_portal_helpers.rb' @@ -2240,19 +2193,16 @@ Layout/LineLength: - 'ee/spec/support/shared_examples/models/concerns/issuable_links_shared_examples.rb' - 'ee/spec/support/shared_examples/models/concerns/repository_replicator_strategy_shared_examples.rb' - 'ee/spec/support/shared_examples/models/concerns/verifiable_replicator_shared_examples.rb' - - 'ee/spec/support/shared_examples/models/geo_batcher_shared_examples.rb' - 'ee/spec/support/shared_examples/models/geo_verifiable_registry_shared_examples.rb' - 'ee/spec/support/shared_examples/models/member_shared_examples.rb' - 'ee/spec/support/shared_examples/models/protected_environments/authorizable_examples.rb' - 'ee/spec/support/shared_examples/policies/protected_environments_shared_examples.rb' - - 'ee/spec/support/shared_examples/quick_actions/issuable/assign_shared_examples.rb' - 'ee/spec/support/shared_examples/quick_actions/issuable/unassign_shared_examples.rb' - 'ee/spec/support/shared_examples/quick_actions/issue/page_quick_action_shared_examples.rb' - 'ee/spec/support/shared_examples/quick_actions/merge_request/assign_reviewer_shared_examples.rb' - 'ee/spec/support/shared_examples/quick_actions/merge_request/unassign_reviewer_shared_examples.rb' - 'ee/spec/support/shared_examples/requests/api/graphql/geo/registries_shared_examples.rb' - 'ee/spec/support/shared_examples/requests/api/project_approval_rules_api_shared_examples.rb' - - 'ee/spec/support/shared_examples/services/base_sync_service_shared_examples.rb' - 'ee/spec/support/shared_examples/services/boards/base_service_shared_examples.rb' - 'ee/spec/support/shared_examples/services/build_execute_shared_examples.rb' - 'ee/spec/support/shared_examples/services/ci/play_job_service_shared_examples.rb' @@ -2342,7 +2292,6 @@ Layout/LineLength: - 'lib/api/deploy_tokens.rb' - 'lib/api/deployments.rb' - 'lib/api/discussions.rb' - - 'lib/api/entities/system/broadcast_message.rb' - 'lib/api/entities/application_setting.rb' - 'lib/api/entities/basic_project_details.rb' - 'lib/api/entities/branch.rb' @@ -2548,7 +2497,6 @@ Layout/LineLength: - 'lib/gitlab/conflict/file.rb' - 'lib/gitlab/conflict/file_collection.rb' - 'lib/gitlab/content_security_policy/config_loader.rb' - - 'lib/gitlab/content_security_policy/directives.rb' - 'lib/gitlab/current_settings.rb' - 'lib/gitlab/cycle_analytics/summary/deploy.rb' - 'lib/gitlab/cycle_analytics/summary/deployment_frequency.rb' @@ -2599,7 +2547,6 @@ Layout/LineLength: - 'lib/gitlab/email/handler/create_issue_handler.rb' - 'lib/gitlab/email/handler/create_merge_request_handler.rb' - 'lib/gitlab/encrypted_command_base.rb' - - 'lib/gitlab/encrypted_configuration.rb' - 'lib/gitlab/endpoint_attributes/config.rb' - 'lib/gitlab/event_store/event.rb' - 'lib/gitlab/event_store/store.rb' @@ -2791,10 +2738,8 @@ Layout/LineLength: - 'qa/qa/ee/page/project/secure/show.rb' - 'qa/qa/flow/sign_up.rb' - 'qa/qa/git/repository.rb' - - 'qa/qa/page/component/ci_badge_link.rb' - 'qa/qa/page/component/issuable/sidebar.rb' - 'qa/qa/page/dashboard/snippet/index.rb' - - 'qa/qa/page/group/settings/group_deploy_tokens.rb' - 'qa/qa/page/group/settings/package_registries.rb' - 'qa/qa/page/merge_request/new.rb' - 'qa/qa/page/project/import/repo_by_url.rb' @@ -2818,7 +2763,6 @@ Layout/LineLength: - 'qa/qa/specs/features/api/1_manage/rate_limits_spec.rb' - 'qa/qa/specs/features/api/3_create/merge_request/push_options_labels_spec.rb' - 'qa/qa/specs/features/api/3_create/merge_request/push_options_mwps_spec.rb' - - 'qa/qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb' - 'qa/qa/specs/features/api/3_create/repository/files_spec.rb' - 'qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb' - 'qa/qa/specs/features/api/3_create/repository/push_postreceive_idempotent_spec.rb' @@ -2836,9 +2780,7 @@ Layout/LineLength: - 'qa/qa/specs/features/browser_ui/2_plan/transient/comment_on_discussion_spec.rb' - 'qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_via_template_spec.rb' - 'qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb' - - 'qa/qa/specs/features/browser_ui/3_create/merge_request/merge_when_pipeline_succeeds_spec.rb' - 'qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb' - - 'qa/qa/specs/features/browser_ui/3_create/merge_request/revert/reverting_merge_request_spec.rb' - 'qa/qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb' - 'qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb' - 'qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb' @@ -2863,13 +2805,9 @@ Layout/LineLength: - 'qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb' - 'qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_with_multiple_files_spec.rb' - 'qa/qa/specs/features/browser_ui/3_create/snippet/share_snippet_spec.rb' - - 'qa/qa/specs/features/browser_ui/4_verify/ci_variable/pipeline_with_protected_variable_spec.rb' - 'qa/qa/specs/features/browser_ui/4_verify/pipeline/include_local_config_file_paths_with_wildcard_spec.rb' - - 'qa/qa/specs/features/browser_ui/4_verify/pipeline/include_multiple_files_from_a_project_spec.rb' - 'qa/qa/specs/features/browser_ui/4_verify/pipeline/merge_mr_when_pipline_is_blocked_spec.rb' - - 'qa/qa/specs/features/browser_ui/4_verify/pipeline/pass_dotenv_variables_to_downstream_via_bridge_spec.rb' - 'qa/qa/specs/features/browser_ui/4_verify/pipeline/trigger_matrix_spec.rb' - - 'qa/qa/specs/features/browser_ui/4_verify/pipeline/update_ci_file_with_pipeline_editor_spec.rb' - 'qa/qa/specs/features/browser_ui/4_verify/runner/register_runner_spec.rb' - 'qa/qa/specs/features/browser_ui/5_package/package_registry/composer_registry_spec.rb' - 'qa/qa/specs/features/browser_ui/5_package/package_registry/conan_repository_spec.rb' @@ -2887,7 +2825,6 @@ Layout/LineLength: - 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/license/cloud_activation_spec.rb' - 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/license/license_spec.rb' - 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/user_registration_billing_spec.rb' - - 'qa/qa/specs/features/ee/browser_ui/13_secure/enable_scanning_from_configuration_spec.rb' - 'qa/qa/specs/features/ee/browser_ui/2_plan/burndown_chart/burndown_chart_spec.rb' - 'qa/qa/specs/features/ee/browser_ui/2_plan/custom_email/custom_email_spec.rb' - 'qa/qa/specs/features/ee/browser_ui/2_plan/epic/epics_management_spec.rb' @@ -3047,7 +2984,6 @@ Layout/LineLength: - 'spec/controllers/repositories/git_http_controller_spec.rb' - 'spec/controllers/repositories/lfs_storage_controller_spec.rb' - 'spec/controllers/search_controller_spec.rb' - - 'spec/controllers/sent_notifications_controller_spec.rb' - 'spec/controllers/sessions_controller_spec.rb' - 'spec/controllers/uploads_controller_spec.rb' - 'spec/db/schema_spec.rb' @@ -3068,7 +3004,6 @@ Layout/LineLength: - 'spec/factories/namespaces.rb' - 'spec/factories/notes.rb' - 'spec/factories/packages/package_files.rb' - - 'spec/factories/project_members.rb' - 'spec/factories/projects.rb' - 'spec/factories/usage_data.rb' - 'spec/features/action_cable_logging_spec.rb' @@ -3093,7 +3028,6 @@ Layout/LineLength: - 'spec/features/calendar_spec.rb' - 'spec/features/callouts/registration_enabled_spec.rb' - 'spec/features/commits_spec.rb' - - 'spec/features/contextual_sidebar_spec.rb' - 'spec/features/cycle_analytics_spec.rb' - 'spec/features/dashboard/projects_spec.rb' - 'spec/features/dashboard/todos/todos_spec.rb' @@ -3101,7 +3035,6 @@ Layout/LineLength: - 'spec/features/error_tracking/user_filters_errors_by_status_spec.rb' - 'spec/features/file_uploads/ci_artifact_spec.rb' - 'spec/features/file_uploads/maven_package_spec.rb' - - 'spec/features/file_uploads/multipart_invalid_uploads_spec.rb' - 'spec/features/frequently_visited_projects_and_groups_spec.rb' - 'spec/features/groups/board_spec.rb' - 'spec/features/groups/clusters/user_spec.rb' @@ -3154,7 +3087,6 @@ Layout/LineLength: - 'spec/features/merge_request/user_creates_merge_request_spec.rb' - 'spec/features/merge_request/user_expands_diff_spec.rb' - 'spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb' - - 'spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb' - 'spec/features/merge_request/user_posts_diff_notes_spec.rb' - 'spec/features/merge_request/user_posts_notes_spec.rb' - 'spec/features/merge_request/user_resolves_conflicts_spec.rb' @@ -3188,8 +3120,6 @@ Layout/LineLength: - 'spec/features/milestones/user_views_milestone_spec.rb' - 'spec/features/milestones/user_views_milestones_spec.rb' - 'spec/features/oauth_login_spec.rb' - - 'spec/features/user_settings/active_sessions_spec.rb' - - 'spec/features/profiles/password_spec.rb' - 'spec/features/profiles/two_factor_auths_spec.rb' - 'spec/features/profiles/user_edit_profile_spec.rb' - 'spec/features/projects/artifacts/file_spec.rb' @@ -3263,7 +3193,7 @@ Layout/LineLength: - 'spec/features/snippets/user_edits_snippet_spec.rb' - 'spec/features/task_lists_spec.rb' - 'spec/features/unsubscribe_links_spec.rb' - - 'spec/features/user_sorts_things_spec.rb' + - 'spec/features/user_settings/active_sessions_spec.rb' - 'spec/features/users/login_spec.rb' - 'spec/features/users/overview_spec.rb' - 'spec/features/users/user_browses_projects_on_user_page_spec.rb' @@ -3300,7 +3230,6 @@ Layout/LineLength: - 'spec/finders/packages/go/version_finder_spec.rb' - 'spec/finders/packages/group_packages_finder_spec.rb' - 'spec/finders/packages/maven/package_finder_spec.rb' - - 'spec/finders/packages/npm/package_finder_spec.rb' - 'spec/finders/packages/nuget/package_finder_spec.rb' - 'spec/finders/packages/packages_finder_spec.rb' - 'spec/finders/personal_access_tokens_finder_spec.rb' @@ -3405,7 +3334,6 @@ Layout/LineLength: - 'spec/helpers/admin/deploy_key_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/award_emoji_helper_spec.rb' - 'spec/helpers/blob_helper_spec.rb' @@ -3488,7 +3416,6 @@ Layout/LineLength: - 'spec/lib/banzai/filter/broadcast_message_sanitization_filter_spec.rb' - 'spec/lib/banzai/filter/external_link_filter_spec.rb' - 'spec/lib/banzai/filter/gollum_tags_filter_spec.rb' - - 'spec/lib/banzai/filter/image_link_filter_spec.rb' - 'spec/lib/banzai/filter/inline_diff_filter_spec.rb' - 'spec/lib/banzai/filter/kroki_filter_spec.rb' - 'spec/lib/banzai/filter/math_filter_spec.rb' @@ -3604,7 +3531,6 @@ Layout/LineLength: - 'spec/lib/gitlab/ci/config/external/file/template_spec.rb' - 'spec/lib/gitlab/ci/config/external/mapper_spec.rb' - 'spec/lib/gitlab/ci/config/external/processor_spec.rb' - - 'spec/lib/gitlab/ci/config/external/rules_spec.rb' - 'spec/lib/gitlab/ci/config/yaml/tags/reference_spec.rb' - 'spec/lib/gitlab/ci/cron_parser_spec.rb' - 'spec/lib/gitlab/ci/parsers/coverage/cobertura_spec.rb' @@ -3720,7 +3646,6 @@ Layout/LineLength: - 'spec/lib/gitlab/encoding_helper_spec.rb' - 'spec/lib/gitlab/encrypted_configuration_spec.rb' - 'spec/lib/gitlab/error_tracking/processor/grpc_error_processor_spec.rb' - - 'spec/lib/gitlab/error_tracking/processor/sanitize_error_message_processor_spec.rb' - 'spec/lib/gitlab/error_tracking/processor/sidekiq_processor_spec.rb' - 'spec/lib/gitlab/error_tracking/stack_trace_highlight_decorator_spec.rb' - 'spec/lib/gitlab/etag_caching/router/graphql_spec.rb' @@ -3729,13 +3654,11 @@ Layout/LineLength: - 'spec/lib/gitlab/form_builders/gitlab_ui_form_builder_spec.rb' - 'spec/lib/gitlab/git/base_error_spec.rb' - 'spec/lib/gitlab/git/commit_spec.rb' - - 'spec/lib/gitlab/git/compare_spec.rb' - 'spec/lib/gitlab/git/conflict/file_spec.rb' - 'spec/lib/gitlab/git/diff_spec.rb' - 'spec/lib/gitlab/git/raw_diff_change_spec.rb' - 'spec/lib/gitlab/git/remote_mirror_spec.rb' - 'spec/lib/gitlab/git/repository_spec.rb' - - 'spec/lib/gitlab/git/tree_spec.rb' - 'spec/lib/gitlab/git_access_snippet_spec.rb' - 'spec/lib/gitlab/git_access_spec.rb' - 'spec/lib/gitlab/gitaly_client/blobs_stitcher_spec.rb' @@ -3749,7 +3672,6 @@ Layout/LineLength: - 'spec/lib/gitlab/github_import/importer/diff_note_importer_spec.rb' - 'spec/lib/gitlab/github_import/importer/lfs_object_importer_spec.rb' - 'spec/lib/gitlab/github_import/object_counter_spec.rb' - - 'spec/lib/gitlab/github_import/user_finder_spec.rb' - 'spec/lib/gitlab/gl_repository/repo_type_spec.rb' - 'spec/lib/gitlab/gpg/commit_spec.rb' - 'spec/lib/gitlab/gpg/invalid_gpg_signature_updater_spec.rb' @@ -3777,12 +3699,10 @@ Layout/LineLength: - 'spec/lib/gitlab/import_export/import_failure_service_spec.rb' - 'spec/lib/gitlab/import_export/importer_spec.rb' - 'spec/lib/gitlab/import_export/json/ndjson_reader_spec.rb' - - 'spec/lib/gitlab/import_export/json/ndjson_writer_spec.rb' - 'spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb' - 'spec/lib/gitlab/import_export/members_mapper_spec.rb' - 'spec/lib/gitlab/import_export/project/export_task_spec.rb' - 'spec/lib/gitlab/import_export/project/import_task_spec.rb' - - 'spec/lib/gitlab/import_export/project/object_builder_spec.rb' - 'spec/lib/gitlab/import_export/project/relation_factory_spec.rb' - 'spec/lib/gitlab/import_export/project/sample/relation_tree_restorer_spec.rb' - 'spec/lib/gitlab/import_export/project/tree_restorer_spec.rb' @@ -3803,7 +3723,6 @@ Layout/LineLength: - 'spec/lib/gitlab/jira_import/labels_importer_spec.rb' - 'spec/lib/gitlab/jira_import_spec.rb' - 'spec/lib/gitlab/kas/client_spec.rb' - - 'spec/lib/gitlab/kas_spec.rb' - 'spec/lib/gitlab/kubernetes/deployment_spec.rb' - 'spec/lib/gitlab/kubernetes/kubeconfig/template_spec.rb' - 'spec/lib/gitlab/kubernetes/kubectl_cmd_spec.rb' @@ -3826,7 +3745,6 @@ Layout/LineLength: - 'spec/lib/gitlab/metrics/samplers/threads_sampler_spec.rb' - 'spec/lib/gitlab/metrics/subscribers/active_record_spec.rb' - 'spec/lib/gitlab/metrics/subscribers/load_balancing_spec.rb' - - 'spec/lib/gitlab/metrics/system_spec.rb' - 'spec/lib/gitlab/metrics/web_transaction_spec.rb' - 'spec/lib/gitlab/middleware/go_spec.rb' - 'spec/lib/gitlab/middleware/handle_malformed_strings_spec.rb' @@ -3907,7 +3825,6 @@ Layout/LineLength: - 'spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb' - 'spec/lib/gitlab/usage_data_counters/jetbrains_plugin_activity_unique_counter_spec.rb' - 'spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb' - - 'spec/lib/gitlab/usage_data_metrics_spec.rb' - 'spec/lib/gitlab/usage_data_non_sql_metrics_spec.rb' - 'spec/lib/gitlab/usage_data_queries_spec.rb' - 'spec/lib/gitlab/usage_data_spec.rb' @@ -3918,7 +3835,6 @@ Layout/LineLength: - 'spec/lib/gitlab/utils/usage_data_spec.rb' - 'spec/lib/gitlab/web_ide/config/entry/global_spec.rb' - 'spec/lib/gitlab/web_ide/config/entry/terminal_spec.rb' - - 'spec/lib/gitlab/webpack/file_loader_spec.rb' - 'spec/lib/gitlab/webpack/manifest_spec.rb' - 'spec/lib/gitlab/word_diff/parser_spec.rb' - 'spec/lib/gitlab/workhorse_spec.rb' @@ -3942,7 +3858,6 @@ Layout/LineLength: - 'spec/lib/sidebars/projects/menus/ci_cd_menu_spec.rb' - 'spec/lib/sidebars/projects/menus/external_issue_tracker_menu_spec.rb' - 'spec/lib/sidebars/projects/menus/hidden_menu_spec.rb' - - 'spec/lib/sidebars/projects/menus/monitor_menu_spec.rb' - 'spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb' - 'spec/lib/sidebars/projects/menus/security_compliance_menu_spec.rb' - 'spec/lib/uploaded_file_spec.rb' @@ -4003,7 +3918,6 @@ Layout/LineLength: - 'spec/models/concerns/partitioned_table_spec.rb' - 'spec/models/concerns/pg_full_text_searchable_spec.rb' - 'spec/models/concerns/project_features_compatibility_spec.rb' - - 'spec/models/concerns/prometheus_adapter_spec.rb' - 'spec/models/concerns/resolvable_discussion_spec.rb' - 'spec/models/concerns/resolvable_note_spec.rb' - 'spec/models/concerns/routable_spec.rb' @@ -4017,7 +3931,6 @@ Layout/LineLength: - 'spec/models/customer_relations/issue_contact_spec.rb' - 'spec/models/deploy_key_spec.rb' - 'spec/models/deployment_metrics_spec.rb' - - 'spec/models/design_management/design_spec.rb' - 'spec/models/diff_discussion_spec.rb' - 'spec/models/discussion_spec.rb' - 'spec/models/environment_spec.rb' @@ -4080,9 +3993,7 @@ Layout/LineLength: - 'spec/models/packages/nuget/dependency_link_metadatum_spec.rb' - 'spec/models/packages/package_file_spec.rb' - 'spec/models/packages/package_spec.rb' - - 'spec/models/pages/virtual_domain_spec.rb' - 'spec/models/personal_access_token_spec.rb' - - 'spec/models/postgresql/detached_partition_spec.rb' - 'spec/models/postgresql/replication_slot_spec.rb' - 'spec/models/preloaders/environments/deployment_preloader_spec.rb' - 'spec/models/preloaders/group_policy_preloader_spec.rb' @@ -4095,11 +4006,8 @@ Layout/LineLength: - 'spec/models/project_spec.rb' - 'spec/models/project_team_spec.rb' - 'spec/models/projects/build_artifacts_size_refresh_spec.rb' - - 'spec/models/projects/repository_storage_move_spec.rb' - - 'spec/models/projects/topic_spec.rb' - 'spec/models/projects/triggered_hooks_spec.rb' - 'spec/models/prometheus_metric_spec.rb' - - 'spec/models/protected_branch/push_access_level_spec.rb' - 'spec/models/protected_branch_spec.rb' - 'spec/models/redirect_route_spec.rb' - 'spec/models/release_highlight_spec.rb' @@ -4153,7 +4061,6 @@ Layout/LineLength: - 'spec/rack_servers/puma_spec.rb' - 'spec/requests/admin/background_migrations_controller_spec.rb' - 'spec/requests/api/access_requests_spec.rb' - - 'spec/requests/api/admin/broadcast_messages_spec.rb' - 'spec/requests/api/admin/instance_clusters_spec.rb' - 'spec/requests/api/admin/plan_limits_spec.rb' - 'spec/requests/api/admin/sidekiq_spec.rb' @@ -4220,7 +4127,6 @@ Layout/LineLength: - 'spec/requests/api/graphql/mutations/issues/update_spec.rb' - 'spec/requests/api/graphql/mutations/jira_import/start_spec.rb' - 'spec/requests/api/graphql/mutations/merge_requests/set_labels_spec.rb' - - 'spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb' - 'spec/requests/api/graphql/mutations/metrics/dashboard/annotations/delete_spec.rb' - 'spec/requests/api/graphql/mutations/namespace/package_settings/update_spec.rb' - 'spec/requests/api/graphql/mutations/releases/create_spec.rb' @@ -4388,7 +4294,6 @@ Layout/LineLength: - 'spec/services/alert_management/http_integrations/update_service_spec.rb' - 'spec/services/application_settings/update_service_spec.rb' - 'spec/services/authorized_project_update/find_records_due_for_refresh_service_spec.rb' - - 'spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb' - 'spec/services/boards/issues/create_service_spec.rb' - 'spec/services/boards/issues/list_service_spec.rb' - 'spec/services/boards/issues/move_service_spec.rb' @@ -4472,7 +4377,6 @@ Layout/LineLength: - 'spec/services/issuable/common_system_notes_service_spec.rb' - 'spec/services/issuable/destroy_service_spec.rb' - 'spec/services/issue_links/create_service_spec.rb' - - 'spec/services/issue_links/list_service_spec.rb' - 'spec/services/issues/after_create_service_spec.rb' - 'spec/services/issues/build_service_spec.rb' - 'spec/services/issues/clone_service_spec.rb' @@ -4552,7 +4456,6 @@ Layout/LineLength: - 'spec/services/packages/npm/create_package_service_spec.rb' - 'spec/services/packages/npm/create_tag_service_spec.rb' - 'spec/services/packages/nuget/create_dependency_service_spec.rb' - - 'spec/services/packages/nuget/metadata_extraction_service_spec.rb' - 'spec/services/packages/nuget/search_service_spec.rb' - 'spec/services/packages/nuget/update_package_from_metadata_service_spec.rb' - 'spec/services/packages/rubygems/process_gem_service_spec.rb' @@ -4583,7 +4486,6 @@ Layout/LineLength: - 'spec/services/projects/overwrite_project_service_spec.rb' - 'spec/services/projects/transfer_service_spec.rb' - 'spec/services/projects/unlink_fork_service_spec.rb' - - 'spec/services/projects/update_pages_service_spec.rb' - 'spec/services/projects/update_repository_storage_service_spec.rb' - 'spec/services/projects/update_service_spec.rb' - 'spec/services/protected_branches/create_service_spec.rb' @@ -4618,9 +4520,7 @@ Layout/LineLength: - 'spec/services/users/approve_service_spec.rb' - 'spec/services/users/ban_service_spec.rb' - 'spec/services/users/create_service_spec.rb' - - 'spec/services/users/reject_service_spec.rb' - 'spec/services/users/unban_service_spec.rb' - - 'spec/services/users/upsert_credit_card_validation_service_spec.rb' - 'spec/services/web_hooks/log_execution_service_spec.rb' - 'spec/services/work_items/create_and_link_service_spec.rb' - 'spec/services/work_items/create_from_task_service_spec.rb' @@ -4628,7 +4528,6 @@ Layout/LineLength: - 'spec/services/work_items/update_service_spec.rb' - 'spec/services/x509_certificate_revoke_service_spec.rb' - 'spec/sidekiq_cluster/sidekiq_cluster_spec.rb' - - 'spec/simplecov_env.rb' - 'spec/spec_helper.rb' - 'spec/support/atlassian/jira_connect/schemata.rb' - 'spec/support/capybara.rb' @@ -4696,7 +4595,6 @@ Layout/LineLength: - 'spec/support/shared_examples/features/manage_applications_shared_examples.rb' - 'spec/support/shared_examples/features/page_description_shared_examples.rb' - 'spec/support/shared_examples/features/sidebar/sidebar_milestone_shared_examples.rb' - - 'spec/support/shared_examples/features/variable_list_shared_examples.rb' - 'spec/support/shared_examples/features/wiki/user_views_asciidoc_page_with_includes_shared_examples.rb' - 'spec/support/shared_examples/features/wiki/user_views_wiki_empty_shared_examples.rb' - 'spec/support/shared_examples/features/wiki/user_views_wiki_page_shared_examples.rb' @@ -4848,7 +4746,6 @@ Layout/LineLength: - 'spec/uploaders/job_artifact_uploader_spec.rb' - 'spec/uploaders/namespace_file_uploader_spec.rb' - 'spec/uploaders/object_storage_spec.rb' - - 'spec/uploaders/pages/deployment_uploader_spec.rb' - 'spec/validators/import/gitlab_projects/remote_file_validator_spec.rb' - 'spec/validators/json_schema_validator_spec.rb' - 'spec/validators/nested_attributes_duplicates_validator_spec.rb' @@ -4881,7 +4778,6 @@ Layout/LineLength: - 'spec/workers/authorized_project_update/project_recalculate_per_user_worker_spec.rb' - 'spec/workers/authorized_project_update/user_refresh_from_replica_worker_spec.rb' - 'spec/workers/auto_devops/disable_worker_spec.rb' - - 'spec/workers/bulk_import_worker_spec.rb' - 'spec/workers/bulk_imports/export_request_worker_spec.rb' - 'spec/workers/ci/job_artifacts/expire_project_build_artifacts_worker_spec.rb' - 'spec/workers/ci/merge_requests/add_todo_when_build_fails_worker_spec.rb' diff --git a/.rubocop_todo/rspec/context_wording.yml b/.rubocop_todo/rspec/context_wording.yml index 78d1ffac5ac..0fd9bdf250c 100644 --- a/.rubocop_todo/rspec/context_wording.yml +++ b/.rubocop_todo/rspec/context_wording.yml @@ -2299,7 +2299,6 @@ RSpec/ContextWording: - 'spec/requests/api/graphql/mutations/work_items/create_from_task_spec.rb' - 'spec/requests/api/graphql/mutations/work_items/create_spec.rb' - 'spec/requests/api/graphql/mutations/work_items/update_spec.rb' - - 'spec/requests/api/graphql/mutations/work_items/update_task_spec.rb' - 'spec/requests/api/graphql/namespace_query_spec.rb' - 'spec/requests/api/graphql/packages/maven_spec.rb' - 'spec/requests/api/graphql/packages/package_spec.rb' diff --git a/.rubocop_todo/rspec/expect_change.yml b/.rubocop_todo/rspec/expect_change.yml index 2b178901b39..c5f56901f5e 100644 --- a/.rubocop_todo/rspec/expect_change.yml +++ b/.rubocop_todo/rspec/expect_change.yml @@ -287,7 +287,6 @@ RSpec/ExpectChange: - 'spec/requests/api/graphql/mutations/work_items/create_spec.rb' - 'spec/requests/api/graphql/mutations/work_items/delete_spec.rb' - 'spec/requests/api/graphql/mutations/work_items/update_spec.rb' - - 'spec/requests/api/graphql/mutations/work_items/update_task_spec.rb' - 'spec/requests/api/groups_spec.rb' - 'spec/requests/api/issues/post_projects_issues_spec.rb' - 'spec/requests/api/labels_spec.rb' diff --git a/.rubocop_todo/rspec/feature_category.yml b/.rubocop_todo/rspec/feature_category.yml index 7bcf93cb18a..df29ac4c8bd 100644 --- a/.rubocop_todo/rspec/feature_category.yml +++ b/.rubocop_todo/rspec/feature_category.yml @@ -1959,7 +1959,6 @@ RSpec/FeatureCategory: - 'spec/graphql/mutations/todos/restore_many_spec.rb' - 'spec/graphql/mutations/todos/restore_spec.rb' - 'spec/graphql/mutations/user_callouts/create_spec.rb' - - 'spec/graphql/mutations/work_items/update_task_spec.rb' - 'spec/graphql/resolvers/admin/analytics/usage_trends/measurements_resolver_spec.rb' - 'spec/graphql/resolvers/alert_management/alert_resolver_spec.rb' - 'spec/graphql/resolvers/alert_management/alert_status_counts_resolver_spec.rb' @@ -3559,7 +3558,6 @@ RSpec/FeatureCategory: - 'spec/lib/gitlab/hook_data/subgroup_builder_spec.rb' - 'spec/lib/gitlab/hook_data/user_builder_spec.rb' - 'spec/lib/gitlab/hotlinking_detector_spec.rb' - - 'spec/lib/gitlab/http_connection_adapter_spec.rb' - 'spec/lib/gitlab/http_io_spec.rb' - 'spec/lib/gitlab/i18n/metadata_entry_spec.rb' - 'spec/lib/gitlab/i18n/po_linter_spec.rb' diff --git a/.rubocop_todo/rspec/named_subject.yml b/.rubocop_todo/rspec/named_subject.yml index 2e0e343d868..a14d560a3eb 100644 --- a/.rubocop_todo/rspec/named_subject.yml +++ b/.rubocop_todo/rspec/named_subject.yml @@ -2225,7 +2225,6 @@ RSpec/NamedSubject: - 'spec/lib/gitlab/health_checks/probes/collection_spec.rb' - 'spec/lib/gitlab/health_checks/redis_spec.rb' - 'spec/lib/gitlab/hook_data/base_builder_spec.rb' - - 'spec/lib/gitlab/http_connection_adapter_spec.rb' - 'spec/lib/gitlab/http_io_spec.rb' - 'spec/lib/gitlab/import/database_helpers_spec.rb' - 'spec/lib/gitlab/import/merge_request_creator_spec.rb' diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION index 91c640ebc10..4589bfedddf 100644 --- a/GITLAB_SHELL_VERSION +++ b/GITLAB_SHELL_VERSION @@ -1 +1 @@ -14.32.0 +14.33.0 diff --git a/Gemfile b/Gemfile index 332ce1945ea..5df6c951fff 100644 --- a/Gemfile +++ b/Gemfile @@ -336,7 +336,8 @@ gem 'gitlab_chronic_duration', '~> 0.12' # rubocop:todo Gemfile/MissingFeatureCa gem 'rack-proxy', '~> 0.7.7' # rubocop:todo Gemfile/MissingFeatureCategory -gem 'sassc-rails', '~> 2.1.0' # rubocop:todo Gemfile/MissingFeatureCategory +gem 'sassc-rails', '~> 2.1.0', feature_category: :shared, require: false +gem 'cssbundling-rails', '1.3.3', feature_category: :shared, require: false gem 'autoprefixer-rails', '10.2.5.1' # rubocop:todo Gemfile/MissingFeatureCategory gem 'terser', '1.0.2' # rubocop:todo Gemfile/MissingFeatureCategory diff --git a/Gemfile.checksum b/Gemfile.checksum index 5e2879887c8..d867f9ff2c9 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -95,6 +95,7 @@ {"name":"creole","version":"0.5.0","platform":"ruby","checksum":"951701e2d80760f156b1cb2a93471ca97c076289becc067a33b745133ed32c03"}, {"name":"crystalball","version":"0.7.0","platform":"ruby","checksum":"6e729f372a5071daec877adb40c5df4cb25fe21f350635e2a9624373fc151ef2"}, {"name":"css_parser","version":"1.14.0","platform":"ruby","checksum":"f2ce6148cd505297b07bdbe7a5db4cce5cf530071f9b732b9a23538d6cdc0113"}, +{"name":"cssbundling-rails","version":"1.3.3","platform":"ruby","checksum":"4aa13311e52a40bc0eb32ca651f44db8df03df273553cf9aeb022570607e1855"}, {"name":"cvss-suite","version":"3.0.1","platform":"ruby","checksum":"b5ca9e9e94032a42fd0dc28c1e305378b62c949e35ed7111fc4a1d76f68ad3f9"}, {"name":"danger","version":"9.3.1","platform":"ruby","checksum":"9070fbac181eb45fb9b69ea25e6ea4faa86796ef33bf8d00346cab4385e51df5"}, {"name":"danger-gitlab","version":"8.0.0","platform":"ruby","checksum":"497dd7d0f6513913de651019223d8058cf494df10acbd17de92b175dfa04a3a8"}, @@ -581,7 +582,7 @@ {"name":"sanitize","version":"6.0.2","platform":"ruby","checksum":"48c4eb8e92bb1699056b6000986ac50fc9df82f458a941abf2c4d6759bccd5cf"}, {"name":"sassc","version":"2.4.0","platform":"ruby","checksum":"4c60a2b0a3b36685c83b80d5789401c2f678c1652e3288315a1551d811d9f83e"}, {"name":"sassc","version":"2.4.0","platform":"x64-mingw32","checksum":"8773b917cb52c7e92c94d4bf324c1c0be3e50d9092f9f5ed4c3c6e454b451c5e"}, -{"name":"sassc-rails","version":"2.1.0","platform":"ruby","checksum":"764dcc74e06930e3483caf0d595084d11f2b0fefd6539abf487cdddfba6cafa2"}, +{"name":"sassc-rails","version":"2.1.2","platform":"ruby","checksum":"5f4fdf3881fc9bdc8e856ffbd9850d70a2878866feae8114aa45996179952db5"}, {"name":"sawyer","version":"0.9.2","platform":"ruby","checksum":"fa3a72d62a4525517b18857ddb78926aab3424de0129be6772a8e2ba240e7aca"}, {"name":"sd_notify","version":"0.1.1","platform":"ruby","checksum":"cbc7ac6caa7cedd26b30a72b5eeb6f36050dc0752df263452ea24fb5a4ad3131"}, {"name":"seed-fu","version":"2.3.7","platform":"ruby","checksum":"f19673443e9af799b730e3d4eca6a89b39e5a36825015dffd00d02ea3365cf74"}, diff --git a/Gemfile.lock b/Gemfile.lock index 3185c8af8aa..4940a03d49e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -425,6 +425,8 @@ GEM git css_parser (1.14.0) addressable + cssbundling-rails (1.3.3) + railties (>= 6.0.0) cvss-suite (3.0.1) danger (9.3.1) claide (~> 1.0) @@ -1511,7 +1513,7 @@ GEM nokogiri (>= 1.12.0) sassc (2.4.0) ffi (~> 1.9) - sassc-rails (2.1.0) + sassc-rails (2.1.2) railties (>= 4.0.0) sassc (>= 2.0) sprockets (> 3.0) @@ -1843,6 +1845,7 @@ DEPENDENCIES countries (~> 4.0.0) creole (~> 0.5.0) crystalball (~> 0.7.0) + cssbundling-rails (= 1.3.3) csv_builder! cvss-suite (~> 3.0.1) database_cleaner-active_record (~> 2.1.0) diff --git a/app/assets/javascripts/content_editor/services/upload_helpers.js b/app/assets/javascripts/content_editor/services/upload_helpers.js index 960f28747b0..70fbb9bea42 100644 --- a/app/assets/javascripts/content_editor/services/upload_helpers.js +++ b/app/assets/javascripts/content_editor/services/upload_helpers.js @@ -1,3 +1,4 @@ +import { uniqueId } from 'lodash'; import { VARIANT_DANGER } from '~/alert'; import axios from '~/lib/utils/axios_utils'; import { __, sprintf } from '~/locale'; @@ -7,17 +8,17 @@ import { ALERT_EVENT } from '../constants'; const chain = (editor) => editor.chain().setMeta('preventAutolink', true); -const findUploadedFilePosition = (editor, filename) => { +const findUploadedFilePosition = (editor, fileId) => { let position; editor.view.state.doc.descendants((descendant, pos) => { - if (descendant.attrs.uploading === filename) { + if (descendant.attrs.uploading === fileId) { position = pos; return false; } for (const mark of descendant.marks) { - if (mark.type.name === 'link' && mark.attrs.uploading === filename) { + if (mark.type.name === 'link' && mark.attrs.uploading === fileId) { position = pos + 1; return false; } @@ -142,11 +143,12 @@ const uploadMedia = async ({ type, editor, file, uploadsPath, renderMarkdown, ev const objectUrl = URL.createObjectURL(file); const { selection } = editor.view.state; const currentNode = selection.$to.node(); + const fileId = uniqueId(type); let position = selection.to; let content = { type, - attrs: { uploading: file.name, src: objectUrl, alt: file.name }, + attrs: { uploading: fileId, src: objectUrl, alt: file.name }, }; let selectionIncrement = 0; @@ -170,9 +172,9 @@ const uploadMedia = async ({ type, editor, file, uploadsPath, renderMarkdown, ev }) .then(({ canonicalSrc }) => { // the position might have changed while uploading, so we need to find it again - position = findUploadedFilePosition(editor, file.name); + position = findUploadedFilePosition(editor, fileId); - uploadingStates[file.name] = true; + uploadingStates[fileId] = true; editor.view.dispatch( editor.state.tr.setMeta('preventAutolink', true).setNodeMarkup(position, undefined, { @@ -186,7 +188,7 @@ const uploadMedia = async ({ type, editor, file, uploadsPath, renderMarkdown, ev chain(editor).setNodeSelection(position).run(); }) .catch((e) => { - position = findUploadedFilePosition(editor, file.name); + position = findUploadedFilePosition(editor, fileId); chain(editor) .deleteRange({ from: position, to: position + 1 }) @@ -203,14 +205,15 @@ const uploadAttachment = async ({ editor, file, uploadsPath, renderMarkdown, eve const objectUrl = URL.createObjectURL(file); const { selection } = editor.view.state; const currentNode = selection.$to.node(); + const fileId = uniqueId('file'); - uploadingStates[file.name] = true; + uploadingStates[fileId] = true; let position = selection.to; let content = { type: 'text', text: file.name, - marks: [{ type: 'link', attrs: { href: objectUrl, uploading: file.name } }], + marks: [{ type: 'link', attrs: { href: objectUrl, uploading: fileId } }], }; // if the current node is not empty, we need to wrap the content in a new paragraph @@ -229,7 +232,7 @@ const uploadAttachment = async ({ editor, file, uploadsPath, renderMarkdown, eve }) .then(({ src, canonicalSrc }) => { // the position might have changed while uploading, so we need to find it again - position = findUploadedFilePosition(editor, file.name); + position = findUploadedFilePosition(editor, fileId); chain(editor) .setTextSelection(position) @@ -238,7 +241,7 @@ const uploadAttachment = async ({ editor, file, uploadsPath, renderMarkdown, eve .run(); }) .catch((e) => { - position = findUploadedFilePosition(editor, file.name); + position = findUploadedFilePosition(editor, fileId); chain(editor) .setTextSelection(position) diff --git a/app/assets/javascripts/vue_merge_request_widget/components/merge_checks.vue b/app/assets/javascripts/vue_merge_request_widget/components/merge_checks.vue index 89095a55a11..9afed170097 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/merge_checks.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/merge_checks.vue @@ -99,15 +99,11 @@ export default { return this.state.mergeabilityChecks || []; }, sortedChecks() { - return [...this.checks] - .sort((a, b) => { - if (a.status === 'FAILED' && b.status !== 'FAILED') return -1; - if (a.status === 'SUCCESS' && b.status !== 'SUCCESS') - return b.status === 'FAILED' ? 1 : -1; + const order = ['FAILED', 'SUCCESS']; - return 0; - }) - .filter((s) => s.status !== 'INACTIVE'); + return [...this.checks] + .filter((s) => s.status !== 'INACTIVE') + .sort((a, b) => order.indexOf(a.status) - order.indexOf(b.status)); }, failedChecks() { return this.checks.filter((c) => c.status.toLowerCase() === 'failed'); diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_utils.js b/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_utils.js index d33c0bb4708..ce4a46fe3dd 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_utils.js +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_utils.js @@ -76,9 +76,9 @@ export function processFilters(filters) { type = FILTERED_SEARCH_TERM; value = token; } else { - type = token.type; - operator = token.value.operator; - value = token.value.data; + type = token?.type; + operator = token?.value?.operator; + value = token?.value?.data; } if (!acc[type]) { diff --git a/app/controllers/sent_notifications_controller.rb b/app/controllers/sent_notifications_controller.rb index 4f61088ab17..8f178174835 100644 --- a/app/controllers/sent_notifications_controller.rb +++ b/app/controllers/sent_notifications_controller.rb @@ -11,15 +11,15 @@ class SentNotificationsController < ApplicationController return render_404 unless unsubscribe_prerequisites_met? - return unsubscribe_and_redirect if current_user || params[:force] + unsubscribe_and_redirect if current_user || params[:force] end private def unsubscribe_prerequisites_met? @sent_notification.present? && - @sent_notification.unsubscribable? && - noteable.present? + @sent_notification.unsubscribable? && + noteable.present? end def noteable @@ -29,9 +29,7 @@ class SentNotificationsController < ApplicationController def unsubscribe_and_redirect noteable.unsubscribe(@sent_notification.recipient, @sent_notification.project) - if noteable.is_a?(Issue) && @sent_notification.recipient_id == Users::Internal.support_bot.id - noteable.unsubscribe_email_participant(noteable.external_author) - end + unsubscribe_issue_email_participant flash[:notice] = _("You have been unsubscribed from this thread.") @@ -46,6 +44,15 @@ class SentNotificationsController < ApplicationController end end + def unsubscribe_issue_email_participant + return unless noteable.is_a?(Issue) + return unless @sent_notification.recipient_id == Users::Internal.support_bot.id + + # Unsubscribe external author for legacy reasons when no issue email participant is set + email = @sent_notification.issue_email_participant&.email || noteable.external_author + noteable.unsubscribe_email_participant(email) + end + def noteable_path(noteable) case noteable when Issue diff --git a/app/graphql/mutations/work_items/update_task.rb b/app/graphql/mutations/work_items/update_task.rb deleted file mode 100644 index d3df235f894..00000000000 --- a/app/graphql/mutations/work_items/update_task.rb +++ /dev/null @@ -1,70 +0,0 @@ -# frozen_string_literal: true - -module Mutations - module WorkItems - class UpdateTask < BaseMutation - graphql_name 'WorkItemUpdateTask' - description "Updates a work item's task by Global ID." - - include Mutations::SpamProtection - - authorize :read_work_item - - argument :id, ::Types::GlobalIDType[::WorkItem], - required: true, - description: 'Global ID of the work item.' - argument :task_data, ::Types::WorkItems::UpdatedTaskInputType, - required: true, - description: 'Arguments necessary to update a task.' - - field :task, Types::WorkItemType, - null: true, - description: 'Updated task.' - field :work_item, Types::WorkItemType, - null: true, - description: 'Updated work item.' - - def resolve(id:, task_data:) - task_data_hash = task_data.to_h - work_item = authorized_find!(id: id) - task = authorized_find_task!(task_data_hash[:id]) - - ::WorkItems::UpdateService.new( - container: task.project, - current_user: current_user, - params: task_data_hash.except(:id), - perform_spam_check: true - ).execute(task) - - check_spam_action_response!(task) - - response = { errors: errors_on_object(task) } - - if task.valid? - work_item.expire_etag_cache - - response.merge(work_item: work_item, task: task) - else - response - end - end - - private - - def authorized_find_task!(task_id) - task = task_id.find - - if current_user.can?(:update_work_item, task) - task - else - # Fail early if user cannot update task - raise_resource_not_available_error! - end - end - - def find_object(id:) - id.find - end - end - end -end diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb index 51ab6880b1a..94bb3055bc4 100644 --- a/app/graphql/types/mutation_type.rb +++ b/app/graphql/types/mutation_type.rb @@ -191,7 +191,6 @@ module Types mount_mutation Mutations::WorkItems::CreateFromTask, alpha: { milestone: '15.1' } mount_mutation Mutations::WorkItems::Delete, alpha: { milestone: '15.1' } mount_mutation Mutations::WorkItems::Update, alpha: { milestone: '15.1' } - mount_mutation Mutations::WorkItems::UpdateTask, alpha: { milestone: '15.1' } mount_mutation Mutations::WorkItems::Export, alpha: { milestone: '15.10' } mount_mutation Mutations::WorkItems::Convert, alpha: { milestone: '15.11' } mount_mutation Mutations::WorkItems::LinkedItems::Add, alpha: { milestone: '16.3' } diff --git a/app/graphql/types/work_items/updated_task_input_type.rb b/app/graphql/types/work_items/updated_task_input_type.rb deleted file mode 100644 index 9f8afa2ff1b..00000000000 --- a/app/graphql/types/work_items/updated_task_input_type.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -module Types - module WorkItems - class UpdatedTaskInputType < BaseInputObject - graphql_name 'WorkItemUpdatedTaskInput' - - include Mutations::WorkItems::UpdateArguments - end - end -end diff --git a/app/mailers/emails/service_desk.rb b/app/mailers/emails/service_desk.rb index 64e6122f9c7..58890fd29c5 100644 --- a/app/mailers/emails/service_desk.rb +++ b/app/mailers/emails/service_desk.rb @@ -37,7 +37,7 @@ module Emails def service_desk_new_note_email(issue_id, note_id, recipient) @note = Note.find(note_id) - setup_service_desk_mail(issue_id) + setup_service_desk_mail(issue_id, recipient) # Prepare uploads for text replacement in markdown content setup_service_desk_attachments @@ -49,7 +49,7 @@ module Emails options = { from: email_sender, - to: recipient, + to: recipient.email, subject: subject_base, **service_desk_template_content_options('new_note') } @@ -119,14 +119,20 @@ module Emails private - def setup_service_desk_mail(issue_id) + def setup_service_desk_mail(issue_id, issue_email_participant = nil) @issue = Issue.find(issue_id) @project = @issue.project @support_bot = Users::Internal.support_bot @service_desk_setting = @project.service_desk_setting - @sent_notification = SentNotification.record(@issue, @support_bot.id, reply_key) + if issue_email_participant.blank? && @issue.external_author.present? + issue_email_participant = @issue.issue_email_participants.find_by_email(@issue.external_author) + end + + @sent_notification = SentNotification.record(@issue, @support_bot.id, reply_key, { + issue_email_participant: issue_email_participant + }) end def service_desk_template_content_options(email_type) diff --git a/app/mailers/previews/notify_preview.rb b/app/mailers/previews/notify_preview.rb index c3987d85381..c7d6f2843de 100644 --- a/app/mailers/previews/notify_preview.rb +++ b/app/mailers/previews/notify_preview.rb @@ -243,8 +243,9 @@ class NotifyPreview < ActionMailer::Preview def service_desk_new_note_email cleanup do note = create_note(noteable_type: 'Issue', noteable_id: issue.id, note: 'Issue note content') + participant = IssueEmailParticipant.create!(issue: issue, email: 'user@example.com') - Notify.service_desk_new_note_email(issue.id, note.id, 'someone@gitlab.com').message + Notify.service_desk_new_note_email(issue.id, note.id, participant).message end end diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb index f3a0479d3b7..66da5369d38 100644 --- a/app/models/sent_notification.rb +++ b/app/models/sent_notification.rb @@ -4,6 +4,7 @@ class SentNotification < ApplicationRecord belongs_to :project belongs_to :noteable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations belongs_to :recipient, class_name: "User" + belongs_to :issue_email_participant validates :recipient, presence: true validates :reply_key, presence: true, uniqueness: true diff --git a/app/models/work_items/widgets/base.rb b/app/models/work_items/widgets/base.rb index c4e87decdbf..a3051f17158 100644 --- a/app/models/work_items/widgets/base.rb +++ b/app/models/work_items/widgets/base.rb @@ -15,10 +15,18 @@ module WorkItems [] end + def self.sync_params + [] + end + def self.process_quick_action_param(param_name, value) { param_name => value } end + def self.process_sync_params(params) + params + end + def self.callback_class WorkItems::Callbacks.const_get(name.demodulize, false) rescue NameError diff --git a/app/models/work_items/widgets/hierarchy.rb b/app/models/work_items/widgets/hierarchy.rb index fc6714f1e08..1d888dd71a8 100644 --- a/app/models/work_items/widgets/hierarchy.rb +++ b/app/models/work_items/widgets/hierarchy.rb @@ -23,6 +23,10 @@ module WorkItems [:set_parent, :add_child] end + def self.sync_params + [:parent] + end + def self.process_quick_action_param(param_name, value) return super unless param_name.in?(quick_action_params) && value.present? @@ -30,6 +34,16 @@ module WorkItems return { children: value } if param_name == :add_child end + + def self.process_sync_params(params) + parent_param = params.fetch(:parent, nil) + + if parent_param&.work_item + { parent: parent_param.work_item } + else + {} + end + end end end end diff --git a/app/models/work_items/widgets/labels.rb b/app/models/work_items/widgets/labels.rb index e8b36156fec..0b5a231deff 100644 --- a/app/models/work_items/widgets/labels.rb +++ b/app/models/work_items/widgets/labels.rb @@ -13,6 +13,10 @@ module WorkItems def self.quick_action_params [:add_label_ids, :remove_label_ids, :label_ids] end + + def self.sync_params + [:label_ids] + end end end end diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index 6ec0a46518a..cef97a42f11 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -243,6 +243,7 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy enable :destroy_deploy_token enable :update_runners_registration_token enable :owner_access + enable :update_git_access_protocol enable :read_billing enable :edit_billing diff --git a/app/services/click_house/sync_strategies/base_sync_strategy.rb b/app/services/click_house/sync_strategies/base_sync_strategy.rb index 58c2161b83c..54f0f084d05 100644 --- a/app/services/click_house/sync_strategies/base_sync_strategy.rb +++ b/app/services/click_house/sync_strategies/base_sync_strategy.rb @@ -41,7 +41,7 @@ module ClickHouse private def enabled? - ClickHouse::Client.database_configured?(:main) + Gitlab::ClickHouse.configured? end def context diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb index 0240d0184ac..27c52fc7303 100644 --- a/app/services/issuable_base_service.rb +++ b/app/services/issuable_base_service.rb @@ -227,6 +227,7 @@ class IssuableBaseService < ::BaseContainerService def create(issuable, skip_system_notes: false) initialize_callbacks!(issuable) + prepare_create_params(issuable) handle_quick_actions(issuable) filter_params(issuable) @@ -289,6 +290,10 @@ class IssuableBaseService < ::BaseContainerService # To be overridden by subclasses end + def prepare_create_params(issuable) + # To be overridden by subclasses + end + def after_update(issuable, old_associations) handle_description_updated(issuable) handle_label_changes(issuable, old_associations[:labels]) diff --git a/app/services/namespace_settings/update_service.rb b/app/services/namespace_settings/update_service.rb index 92766bc0267..f6f59738d44 100644 --- a/app/services/namespace_settings/update_service.rb +++ b/app/services/namespace_settings/update_service.rb @@ -31,6 +31,10 @@ module NamespaceSettings param_key: :default_branch_protection_defaults, user_policy: :update_default_branch_protection ) + validate_settings_param_for_root_group( + param_key: :enabled_git_access_protocol, + user_policy: :update_git_access_protocol + ) handle_default_branch_protection unless settings_params[:default_branch_protection].blank? diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 36431c1cbde..3c40707d0c6 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -446,14 +446,18 @@ class NotificationService return unless note.project.service_desk_enabled? issue = note.noteable - recipients = issue.email_participants_emails + recipients = issue.issue_email_participants return unless recipients.any? - support_bot = Users::Internal.support_bot - recipients.delete(issue.external_author) if note.author == support_bot + # Only populated if note is from external participant + note_external_author = note.note_metadata&.email_participant&.downcase recipients.each do |recipient| + # Don't send Service Desk notification if the recipient is the author of the note. + # We store emails as-is but compare downcased versions. + next if recipient.email.downcase == note_external_author + mailer.service_desk_new_note_email(issue.id, note.id, recipient).deliver_later Gitlab::Metrics::BackgroundTransaction.current&.add_event(:service_desk_new_note_email) end diff --git a/app/services/work_items/create_service.rb b/app/services/work_items/create_service.rb index f9eadc3fb60..e1e6063c8ac 100644 --- a/app/services/work_items/create_service.rb +++ b/app/services/work_items/create_service.rb @@ -53,6 +53,21 @@ module WorkItems end end + def prepare_create_params(work_item) + execute_widgets( + work_item: work_item, + callback: :prepare_create_params, + widget_params: @widget_params, + service_params: params + ) + + super + end + + def parent + container + end + private override :handle_quick_actions diff --git a/app/services/work_items/widgets/labels_service/base_service.rb b/app/services/work_items/widgets/labels_service/base_service.rb new file mode 100644 index 00000000000..2d679c1f18c --- /dev/null +++ b/app/services/work_items/widgets/labels_service/base_service.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module WorkItems + module Widgets + module LabelsService + class BaseService < WorkItems::Widgets::BaseService + private + + def prepare_params(params: {}, permitted_params: []) + clear_label_params(params) if new_type_excludes_widget? + + return if params.blank? + return unless has_permission?(:set_work_item_metadata) + + service_params.merge!(params.slice(*permitted_params)) + end + + def clear_label_params(params) + params[:remove_label_ids] = @work_item.labels.map(&:id) + params[:add_label_ids] = [] + end + end + end + end +end diff --git a/app/services/work_items/widgets/labels_service/create_service.rb b/app/services/work_items/widgets/labels_service/create_service.rb new file mode 100644 index 00000000000..bed6be173cc --- /dev/null +++ b/app/services/work_items/widgets/labels_service/create_service.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module WorkItems + module Widgets + module LabelsService + class CreateService < BaseService + def prepare_create_params(params: {}) + prepare_params(params: params, permitted_params: %i[add_label_ids remove_label_ids label_ids]) + end + + def clear_label_params(params) + params[:add_label_ids] = [] + params[:label_ids] = [] + end + end + end + end +end diff --git a/app/services/work_items/widgets/labels_service/update_service.rb b/app/services/work_items/widgets/labels_service/update_service.rb index b0791571924..780451e3eae 100644 --- a/app/services/work_items/widgets/labels_service/update_service.rb +++ b/app/services/work_items/widgets/labels_service/update_service.rb @@ -3,17 +3,9 @@ module WorkItems module Widgets module LabelsService - class UpdateService < WorkItems::Widgets::BaseService + class UpdateService < BaseService def prepare_update_params(params: {}) - if new_type_excludes_widget? - params[:remove_label_ids] = @work_item.labels.map(&:id) - params[:add_label_ids] = [] - end - - return if params.blank? - return unless has_permission?(:set_work_item_metadata) - - service_params.merge!(params.slice(:add_label_ids, :remove_label_ids)) + prepare_params(params: params, permitted_params: %i[add_label_ids remove_label_ids]) end end end diff --git a/app/views/projects/issues/_new_branch.html.haml b/app/views/projects/issues/_new_branch.html.haml index 68de9c44e38..2f2f67ce3cd 100644 --- a/app/views/projects/issues/_new_branch.html.haml +++ b/app/views/projects/issues/_new_branch.html.haml @@ -48,18 +48,17 @@ %label{ for: 'new-branch-name' } = _('Branch name') %input#new-branch-name.js-branch-name.form-control.gl-form-input{ type: 'text', placeholder: "#{@issue.to_branch_name}", value: "#{@issue.to_branch_name}" } - %span.js-branch-message.form-text + %span.js-branch-message.form-text.gl-font-sm .form-group %label{ for: 'source-name' } = _('Source (branch or tag)') %input#source-name.js-ref.ref.form-control.gl-form-input{ type: 'text', placeholder: "#{@project.default_branch}", value: "#{@project.default_branch}", data: { value: "#{@project.default_branch}" } } - %span.js-ref-message.form-text + %span.js-ref-message.form-text.gl-font-sm - .form-group - = render Pajamas::ButtonComponent.new(variant: :confirm, button_options: { class: 'js-create-target', data: { action: 'create-mr' } }) do - = create_mr_text + = render Pajamas::ButtonComponent.new(variant: :confirm, button_options: { class: 'js-create-target', data: { action: 'create-mr' } }) do + = create_mr_text - if can_create_confidential_merge_request? - %p.gl-text-orange-500.js-exposed-info-warning.gl-display-none + %p.gl-text-orange-500.gl-font-sm.js-exposed-info-warning.gl-display-none = _('This may expose confidential information as the selected fork is in another namespace that can have other members.') diff --git a/app/workers/click_house/concerns/consistency_worker.rb b/app/workers/click_house/concerns/consistency_worker.rb index 5fa1608ea2f..3ed0188ba88 100644 --- a/app/workers/click_house/concerns/consistency_worker.rb +++ b/app/workers/click_house/concerns/consistency_worker.rb @@ -66,7 +66,7 @@ module ClickHouse end def enabled? - ClickHouse::Client.database_configured?(:main) && Feature.enabled?(:event_sync_worker_for_click_house) + Gitlab::ClickHouse.configured? && Feature.enabled?(:event_sync_worker_for_click_house) end def runtime_limiter diff --git a/config/application.rb b/config/application.rb index 0634bbf5165..bdcb544d9b4 100644 --- a/config/application.rb +++ b/config/application.rb @@ -11,6 +11,7 @@ require 'action_view/railtie' require 'action_mailer/railtie' require 'action_cable/engine' require 'rails/test_unit/railtie' +require 'sprockets/railtie' require 'gitlab/utils/all' @@ -254,6 +255,19 @@ module Gitlab config.active_record.has_many_inversing = false config.active_record.belongs_to_required_by_default = false + # Switch between cssbundling-rails and sassc-rails conditionally + # To activate cssbundling-rails you can set `USE_NEW_CSS_PIPELINE=1` + # For more context, see: https://gitlab.com/gitlab-org/gitlab/-/issues/438278 + # Need to be loaded before initializers + config.before_configuration do + if Gitlab::Utils.to_boolean(ENV["USE_NEW_CSS_PIPELINE"]) + require 'cssbundling-rails' + else + require 'fileutils' + require 'sassc-rails' + end + end + # Enable the asset pipeline config.assets.enabled = true @@ -542,7 +556,7 @@ module Gitlab # sprocket-rails adds some precompile assets we actually do not need. # - # It copies all _non_ js and CSS files from the app/assets/ older. + # It copies all _non_ js and CSS files from the app/assets/ folder. # # In our case this copies for example: Vue, Markdown and Graphql, which we do not need # for production. @@ -606,6 +620,14 @@ module Gitlab end end + # Add `app/assets/builds` as the highest precedence to find assets + # This is required if cssbundling-rails is used, but should not affect sassc-rails. it would be empty + if defined?(Cssbundling) + initializer :add_cssbundling_output_dir, after: :prefer_specialized_assets do |app| + app.config.assets.paths.unshift("#{config.root}/app/assets/builds") + end + end + # We run the contents of active_record.clear_active_connections again # because we connect to database from routes # https://github.com/rails/rails/blob/fdf840f69a2e33d78a9d40b91d9b7fddb76711e9/activerecord/lib/active_record/railtie.rb#L308 diff --git a/config/initializers_before_autoloader/004_zeitwerk.rb b/config/initializers_before_autoloader/004_zeitwerk.rb index 2d54ab87dca..f7dafb8dbde 100644 --- a/config/initializers_before_autoloader/004_zeitwerk.rb +++ b/config/initializers_before_autoloader/004_zeitwerk.rb @@ -35,7 +35,6 @@ Rails.autoloaders.each do |autoloader| 'html_parser' => 'HTMLParser', 'html_gitlab' => 'HTMLGitlab', 'http' => 'HTTP', - 'http_connection_adapter' => 'HTTPConnectionAdapter', 'http_clone_enabled_check' => 'HTTPCloneEnabledCheck', 'hangouts_chat_http_override' => 'HangoutsChatHTTPOverride', 'chunked_io' => 'ChunkedIO', diff --git a/db/docs/ci_freeze_periods.yml b/db/docs/ci_freeze_periods.yml index 0267f925149..4a64dcd44f8 100644 --- a/db/docs/ci_freeze_periods.yml +++ b/db/docs/ci_freeze_periods.yml @@ -8,3 +8,5 @@ description: https://docs.gitlab.com/ee/ci/environments/deployment_safety.html#p introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29162 milestone: '13.0' gitlab_schema: gitlab_ci +sharding_key: + project_id: projects diff --git a/db/docs/ci_resource_groups.yml b/db/docs/ci_resource_groups.yml index ea66284ccd1..480fddb6e08 100644 --- a/db/docs/ci_resource_groups.yml +++ b/db/docs/ci_resource_groups.yml @@ -8,3 +8,5 @@ description: https://docs.gitlab.com/ee/ci/resource_groups/ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20950 milestone: '12.7' gitlab_schema: gitlab_ci +sharding_key: + project_id: projects diff --git a/db/docs/deploy_keys_projects.yml b/db/docs/deploy_keys_projects.yml index 5d3591f1b4f..7c63d8c41e3 100644 --- a/db/docs/deploy_keys_projects.yml +++ b/db/docs/deploy_keys_projects.yml @@ -7,4 +7,12 @@ feature_categories: description: https://docs.gitlab.com/ee/user/project/deploy_keys/ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/a735ce2aa7da72242629a4452c33e7a1900fdd62 milestone: "<6.0" -gitlab_schema: gitlab_main +gitlab_schema: gitlab_main_cell +allow_cross_joins: +- gitlab_main_clusterwide +allow_cross_transactions: +- gitlab_main_clusterwide +allow_cross_foreign_keys: +- gitlab_main_clusterwide +sharding_key: + project_id: projects diff --git a/db/docs/deployments.yml b/db/docs/deployments.yml index 3fc477efced..113d0816669 100644 --- a/db/docs/deployments.yml +++ b/db/docs/deployments.yml @@ -4,9 +4,16 @@ classes: - Deployment feature_categories: - continuous_delivery -description: >- - Stores metadata related to a deployment CI Build, including user, environment, status, and SHA. - See https://docs.gitlab.com/ee/ci/environments/ for more details. +description: Stores metadata related to a deployment CI Build, including user, environment, + status, and SHA. See https://docs.gitlab.com/ee/ci/environments/ for more details. introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/907c0e6796b69f9577c147dd489cf55748c749ac milestone: '8.9' -gitlab_schema: gitlab_main +gitlab_schema: gitlab_main_cell +allow_cross_joins: +- gitlab_main_clusterwide +allow_cross_transactions: +- gitlab_main_clusterwide +allow_cross_foreign_keys: +- gitlab_main_clusterwide +sharding_key: + project_id: projects diff --git a/db/docs/dora_configurations.yml b/db/docs/dora_configurations.yml index 63114ba6f80..6268660158a 100644 --- a/db/docs/dora_configurations.yml +++ b/db/docs/dora_configurations.yml @@ -7,4 +7,12 @@ feature_categories: description: Stores project specific configurations for DORA4 calculations. introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96561 milestone: '15.4' -gitlab_schema: gitlab_main +gitlab_schema: gitlab_main_cell +allow_cross_joins: +- gitlab_main_clusterwide +allow_cross_transactions: +- gitlab_main_clusterwide +allow_cross_foreign_keys: +- gitlab_main_clusterwide +sharding_key: + project_id: projects diff --git a/db/docs/environments.yml b/db/docs/environments.yml index d7fcce52898..36abfc31e97 100644 --- a/db/docs/environments.yml +++ b/db/docs/environments.yml @@ -4,9 +4,16 @@ classes: - Environment feature_categories: - continuous_delivery -description: >- - Project-level deployment target and metadata. - See https://docs.gitlab.com/ee/ci/environments/ for more details. +description: Project-level deployment target and metadata. See https://docs.gitlab.com/ee/ci/environments/ + for more details. introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/907c0e6796b69f9577c147dd489cf55748c749ac milestone: '8.9' -gitlab_schema: gitlab_main +gitlab_schema: gitlab_main_cell +allow_cross_joins: +- gitlab_main_clusterwide +allow_cross_transactions: +- gitlab_main_clusterwide +allow_cross_foreign_keys: +- gitlab_main_clusterwide +sharding_key: + project_id: projects diff --git a/db/docs/group_deploy_keys_groups.yml b/db/docs/group_deploy_keys_groups.yml index 129be2af5df..615f30384b3 100644 --- a/db/docs/group_deploy_keys_groups.yml +++ b/db/docs/group_deploy_keys_groups.yml @@ -7,4 +7,12 @@ feature_categories: description: https://docs.gitlab.com/ee/user/project/deploy_keys/ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/32901 milestone: '13.2' -gitlab_schema: gitlab_main +gitlab_schema: gitlab_main_cell +allow_cross_joins: +- gitlab_main_clusterwide +allow_cross_transactions: +- gitlab_main_clusterwide +allow_cross_foreign_keys: +- gitlab_main_clusterwide +sharding_key: + group_id: namespaces diff --git a/db/docs/group_deploy_tokens.yml b/db/docs/group_deploy_tokens.yml index 450f67c57b1..bdc22d32061 100644 --- a/db/docs/group_deploy_tokens.yml +++ b/db/docs/group_deploy_tokens.yml @@ -7,4 +7,12 @@ feature_categories: description: https://docs.gitlab.com/ee/user/project/deploy_tokens/ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23460 milestone: '12.8' -gitlab_schema: gitlab_main +gitlab_schema: gitlab_main_cell +allow_cross_joins: +- gitlab_main_clusterwide +allow_cross_transactions: +- gitlab_main_clusterwide +allow_cross_foreign_keys: +- gitlab_main_clusterwide +sharding_key: + group_id: namespaces diff --git a/db/docs/project_deploy_tokens.yml b/db/docs/project_deploy_tokens.yml index 80bca84bf4c..fe8d896ddbc 100644 --- a/db/docs/project_deploy_tokens.yml +++ b/db/docs/project_deploy_tokens.yml @@ -7,4 +7,12 @@ feature_categories: description: https://docs.gitlab.com/ee/user/project/deploy_tokens/ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/8315861c9a50675b4f4f4ca536f0da90f27994f3 milestone: '10.7' -gitlab_schema: gitlab_main +gitlab_schema: gitlab_main_cell +allow_cross_joins: +- gitlab_main_clusterwide +allow_cross_transactions: +- gitlab_main_clusterwide +allow_cross_foreign_keys: +- gitlab_main_clusterwide +sharding_key: + project_id: projects diff --git a/db/migrate/20240104092321_add_issue_email_participant_id_to_sent_notifications.rb b/db/migrate/20240104092321_add_issue_email_participant_id_to_sent_notifications.rb new file mode 100644 index 00000000000..9b5d7612b00 --- /dev/null +++ b/db/migrate/20240104092321_add_issue_email_participant_id_to_sent_notifications.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AddIssueEmailParticipantIdToSentNotifications < Gitlab::Database::Migration[2.2] + milestone '16.9' + + def change + add_column :sent_notifications, :issue_email_participant_id, :bigint, null: true + end +end diff --git a/db/migrate/20240104142200_add_index_sent_notifications_on_issue_email_participant_id.rb b/db/migrate/20240104142200_add_index_sent_notifications_on_issue_email_participant_id.rb new file mode 100644 index 00000000000..c9b75529470 --- /dev/null +++ b/db/migrate/20240104142200_add_index_sent_notifications_on_issue_email_participant_id.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class AddIndexSentNotificationsOnIssueEmailParticipantId < Gitlab::Database::Migration[2.2] + disable_ddl_transaction! + + milestone '16.9' + + INDEX_NAME = 'index_sent_notifications_on_issue_email_participant_id' + + def up + add_concurrent_index :sent_notifications, :issue_email_participant_id, name: INDEX_NAME + end + + def down + remove_concurrent_index_by_name :sent_notifications, INDEX_NAME + end +end diff --git a/db/migrate/20240104142216_add_fk_on_sent_notifications_to_issue_email_participants.rb b/db/migrate/20240104142216_add_fk_on_sent_notifications_to_issue_email_participants.rb new file mode 100644 index 00000000000..c3f1a28ded3 --- /dev/null +++ b/db/migrate/20240104142216_add_fk_on_sent_notifications_to_issue_email_participants.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class AddFkOnSentNotificationsToIssueEmailParticipants < Gitlab::Database::Migration[2.2] + disable_ddl_transaction! + + milestone '16.9' + + def up + add_concurrent_foreign_key( + :sent_notifications, + :issue_email_participants, + column: :issue_email_participant_id, + on_delete: :nullify, + validate: false + ) + end + + def down + with_lock_retries do + remove_foreign_key_if_exists :sent_notifications, column: :issue_email_participant_id + end + end +end diff --git a/db/schema_migrations/20240104092321 b/db/schema_migrations/20240104092321 new file mode 100644 index 00000000000..a0250e57bab --- /dev/null +++ b/db/schema_migrations/20240104092321 @@ -0,0 +1 @@ +947ad53db5666956141f4fe05ba0d49377720b481e88b5b9057302dc6670b1af \ No newline at end of file diff --git a/db/schema_migrations/20240104142200 b/db/schema_migrations/20240104142200 new file mode 100644 index 00000000000..e491d148e96 --- /dev/null +++ b/db/schema_migrations/20240104142200 @@ -0,0 +1 @@ +97d3dccf21797a0fb1077c93a85691f6c29d3ad0492da6795521bc63dae3cb56 \ No newline at end of file diff --git a/db/schema_migrations/20240104142216 b/db/schema_migrations/20240104142216 new file mode 100644 index 00000000000..866f82e926d --- /dev/null +++ b/db/schema_migrations/20240104142216 @@ -0,0 +1 @@ +26d307bd716dfea4803f877182a9891dfcc4af4997f62cb91e430d7fcd7ac2c8 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 7584786bd31..14b6be62159 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -23901,7 +23901,8 @@ CREATE TABLE sent_notifications ( commit_id character varying, reply_key character varying NOT NULL, in_reply_to_discussion_id character varying, - id bigint NOT NULL + id bigint NOT NULL, + issue_email_participant_id bigint ); CREATE SEQUENCE sent_notifications_id_seq @@ -35360,6 +35361,8 @@ CREATE UNIQUE INDEX index_security_trainings_on_unique_project_id ON security_tr CREATE INDEX index_self_managed_prometheus_alert_events_on_environment_id ON self_managed_prometheus_alert_events USING btree (environment_id); +CREATE INDEX index_sent_notifications_on_issue_email_participant_id ON sent_notifications USING btree (issue_email_participant_id); + CREATE INDEX index_sent_notifications_on_noteable_type_noteable_id ON sent_notifications USING btree (noteable_id) WHERE ((noteable_type)::text = 'Issue'::text); CREATE UNIQUE INDEX index_sent_notifications_on_reply_key ON sent_notifications USING btree (reply_key); @@ -38614,6 +38617,9 @@ ALTER TABLE ONLY issue_customer_relations_contacts ALTER TABLE ONLY ssh_signatures ADD CONSTRAINT fk_7d2f93996c FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; +ALTER TABLE ONLY sent_notifications + ADD CONSTRAINT fk_7d7663e36a FOREIGN KEY (issue_email_participant_id) REFERENCES issue_email_participants(id) ON DELETE SET NULL NOT VALID; + ALTER TABLE ONLY labels ADD CONSTRAINT fk_7de4989a69 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; diff --git a/doc/administration/gitaly/configure_gitaly.md b/doc/administration/gitaly/configure_gitaly.md index eb620ff7413..d492d3d32b3 100644 --- a/doc/administration/gitaly/configure_gitaly.md +++ b/doc/administration/gitaly/configure_gitaly.md @@ -136,16 +136,14 @@ To avoid downtime while rotating the Gitaly token, you can temporarily disable a Gitaly and GitLab use two shared secrets for authentication: -- _Gitaly token_: used to authenticate gRPC requests to Gitaly -- _GitLab Shell token_: used for authentication callbacks from GitLab Shell to the GitLab internal API - -Configure authentication in one of two ways: +- _Gitaly token_: used to authenticate gRPC requests to Gitaly. +- _GitLab Shell token_: used for authentication callbacks from GitLab Shell to the GitLab internal API. ::Tabs :::TabTitle Linux package (Omnibus) -To configure the _Gitaly token_, edit `/etc/gitlab/gitlab.rb`: +1. To configure the _Gitaly token_, edit `/etc/gitlab/gitlab.rb`: ```ruby gitaly['configuration'] = { @@ -157,20 +155,20 @@ To configure the _Gitaly token_, edit `/etc/gitlab/gitlab.rb`: } ``` -Configure the _GitLab Shell token_ in one of two ways. +1. Configure the _GitLab Shell token_ in one of two ways: -Method 1 (recommended): + - Method 1 (recommended): -Copy `/etc/gitlab/gitlab-secrets.json` from the Gitaly client to same path on the Gitaly servers - (and any other Gitaly clients). + Copy `/etc/gitlab/gitlab-secrets.json` from the Gitaly client to same path on the Gitaly servers + (and any other Gitaly clients). -Method 2: + - Method 2: -Edit `/etc/gitlab/gitlab.rb`: + Edit `/etc/gitlab/gitlab.rb`: - ```ruby - gitlab_shell['secret_token'] = 'shellsecret' - ``` + ```ruby + gitlab_shell['secret_token'] = 'shellsecret' + ``` :::TabTitle Self-compiled (source) @@ -206,7 +204,7 @@ Updates to example must be made at: - All reference architecture pages --> -Configure Gitaly server in one of two ways: +Configure Gitaly server. ::Tabs diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index ab52f1ceb2a..584bdb3526a 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -8741,33 +8741,6 @@ Input type: `WorkItemUpdateInput` | `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | | `workItem` | [`WorkItem`](#workitem) | Updated work item. | -### `Mutation.workItemUpdateTask` - -Updates a work item's task by Global ID. - -WARNING: -**Introduced** in 15.1. -This feature is an Experiment. It can be changed or removed at any time. - -Input type: `WorkItemUpdateTaskInput` - -#### Arguments - -| Name | Type | Description | -| ---- | ---- | ----------- | -| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | -| `id` | [`WorkItemID!`](#workitemid) | Global ID of the work item. | -| `taskData` | [`WorkItemUpdatedTaskInput!`](#workitemupdatedtaskinput) | Arguments necessary to update a task. | - -#### Fields - -| Name | Type | Description | -| ---- | ---- | ----------- | -| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | -| `errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. | -| `task` | [`WorkItem`](#workitem) | Updated task. | -| `workItem` | [`WorkItem`](#workitem) | Updated work item. | - ### `Mutation.workspaceCreate` WARNING: @@ -34787,27 +34760,6 @@ Attributes for value stream stage. | `title` | [`String!`](#string) | Full string of the task to be replaced. New title for the created work item. | | `workItemTypeId` | [`WorkItemsTypeID!`](#workitemstypeid) | Global ID of the work item type used to create the new work item. | -### `WorkItemUpdatedTaskInput` - -#### Arguments - -| Name | Type | Description | -| ---- | ---- | ----------- | -| `assigneesWidget` | [`WorkItemWidgetAssigneesInput`](#workitemwidgetassigneesinput) | Input for assignees widget. | -| `awardEmojiWidget` | [`WorkItemWidgetAwardEmojiUpdateInput`](#workitemwidgetawardemojiupdateinput) | Input for emoji reactions widget. | -| `confidential` | [`Boolean`](#boolean) | Sets the work item confidentiality. | -| `currentUserTodosWidget` | [`WorkItemWidgetCurrentUserTodosInput`](#workitemwidgetcurrentusertodosinput) | Input for to-dos widget. | -| `descriptionWidget` | [`WorkItemWidgetDescriptionInput`](#workitemwidgetdescriptioninput) | Input for description widget. | -| `hierarchyWidget` | [`WorkItemWidgetHierarchyUpdateInput`](#workitemwidgethierarchyupdateinput) | Input for hierarchy widget. | -| `id` | [`WorkItemID!`](#workitemid) | Global ID of the work item. | -| `labelsWidget` | [`WorkItemWidgetLabelsUpdateInput`](#workitemwidgetlabelsupdateinput) | Input for labels widget. | -| `milestoneWidget` | [`WorkItemWidgetMilestoneInput`](#workitemwidgetmilestoneinput) | Input for milestone widget. | -| `notesWidget` | [`WorkItemWidgetNotesInput`](#workitemwidgetnotesinput) | Input for notes widget. | -| `notificationsWidget` | [`WorkItemWidgetNotificationsUpdateInput`](#workitemwidgetnotificationsupdateinput) | Input for notifications widget. | -| `startAndDueDateWidget` | [`WorkItemWidgetStartAndDueDateUpdateInput`](#workitemwidgetstartandduedateupdateinput) | Input for start and due date widget. | -| `stateEvent` | [`WorkItemStateEvent`](#workitemstateevent) | Close or reopen a work item. | -| `title` | [`String`](#string) | Title of the work item. | - ### `WorkItemWidgetAssigneesInput` #### Arguments diff --git a/doc/api/groups.md b/doc/api/groups.md index 90820727040..2cfe6e0ead7 100644 --- a/doc/api/groups.md +++ b/doc/api/groups.md @@ -520,7 +520,7 @@ Example response: Get all details of a group. This endpoint can be accessed without authentication if the group is publicly accessible. In case the user that requests is an administrator -if the group is publicly accessible. With authentication, it returns the `runners_token` +if the group is publicly accessible. With authentication, it returns the `runners_token` and `enabled_git_access_protocol` for the group too, if the user is an administrator or group owner. ```plaintext @@ -568,6 +568,7 @@ Example response: "runners_token": "ba324ca7b1c77fc20bb9", "file_template_project_id": 1, "parent_id": null, + "enabled_git_access_protocol": "all", "created_at": "2020-01-15T12:36:29.590Z", "shared_with_groups": [ { @@ -829,6 +830,7 @@ Parameters: | `default_branch_protection` | integer | no | See [Options for `default_branch_protection`](#options-for-default_branch_protection). Default to the global level default branch protection setting. | | `default_branch_protection_defaults` | hash | no | See [Options for `default_branch_protection_defaults`](#options-for-default_branch_protection_defaults). | | `description` | string | no | The group's description. | +| `enabled_git_access_protocol` | string | no | Enabled protocols for Git access. Allowed values are: `ssh`, `http`, and `all` to allow both protocols. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/436618) in GitLab 16.9. | | `emails_disabled` | boolean | no | _([Deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127899) in GitLab 16.5.)_ Disable email notifications. Use `emails_enabled` instead. | | `emails_enabled` | boolean | no | Enable email notifications. | | `lfs_enabled` | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group. | @@ -1001,6 +1003,7 @@ PUT /groups/:id | `default_branch_protection` | integer | no | See [Options for `default_branch_protection`](#options-for-default_branch_protection). | | `default_branch_protection_defaults` | hash | no | See [Options for `default_branch_protection_defaults`](#options-for-default_branch_protection_defaults). | | `description` | string | no | The description of the group. | +| `enabled_git_access_protocol` | string | no | Enabled protocols for Git access. Allowed values are: `ssh`, `http`, and `all` to allow both protocols. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/436618) in GitLab 16.9. | | `emails_disabled` | boolean | no | _([Deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/127899) in GitLab 16.5.)_ Disable email notifications. Use `emails_enabled` instead. | | `emails_enabled` | boolean | no | Enable email notifications. | | `lfs_enabled` | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group. | @@ -1060,6 +1063,7 @@ Example response: "full_path": "h5bp", "file_template_project_id": 1, "parent_id": null, + "enabled_git_access_protocol": "all", "created_at": "2020-01-15T12:36:29.590Z", "prevent_sharing_groups_outside_hierarchy": false, "projects": [ // Deprecated and will be removed in API v5 diff --git a/doc/api/usage_data.md b/doc/api/usage_data.md index 41a9901665d..b0e7a1d2998 100644 --- a/doc/api/usage_data.md +++ b/doc/api/usage_data.md @@ -8,6 +8,38 @@ info: To determine the technical writer assigned to the Stage/Group associated w The Service Data API is associated with [Service Ping](../development/internal_analytics/service_ping/index.md). +## Export Service Ping data + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/141446) in GitLab 16.9. + +Requires a Personal Access Token with `read_service_ping` scope. + +Returns the JSON payload collected in Service Ping. If no payload data is available in the application cache, it returns empty response. +If payload data is empty, make sure the [Service Ping feature is enabled](../administration/settings/usage_statistics.md#enable-or-disable-service-ping) and +wait for the cron job to be executed, or [generate payload data manually](../development/internal_analytics/service_ping/troubleshooting.md#generate-service-ping). + +Example request: + +```shell +curl --header "PRIVATE-TOKEN: " "https://gitlab.example.com/api/v4/usage_data/service_ping" +``` + +Example response: + +```json + "recorded_at": "2024-01-15T23:33:50.387Z", + "license": {}, + "counts": { + "assignee_lists": 0, + "ci_builds": 463, + "ci_external_pipelines": 0, + "ci_pipeline_config_auto_devops": 0, + "ci_pipeline_config_repository": 0, + "ci_triggers": 0, + "ci_pipeline_schedules": 0 +... +``` + ## Export metric definitions as a single YAML file > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57270) in GitLab 13.11. diff --git a/gems/gitlab-http/lib/gitlab/http_v2/new_connection_adapter.rb b/gems/gitlab-http/lib/gitlab/http_v2/new_connection_adapter.rb index ee4be97dc6d..6a1f1f0f5e3 100644 --- a/gems/gitlab-http/lib/gitlab/http_v2/new_connection_adapter.rb +++ b/gems/gitlab-http/lib/gitlab/http_v2/new_connection_adapter.rb @@ -62,7 +62,7 @@ module Gitlab def validate_url_with_proxy!(url) UrlBlocker.validate_url_with_proxy!(url, **url_blocker_options) rescue UrlBlocker::BlockedUrlError => e - raise HTTP_V2::BlockedUrlError, "URL is blocked: #{e.message}" + raise BlockedUrlError, "URL is blocked: #{e.message}" end def url_blocker_options diff --git a/lib/api/entities/group_detail.rb b/lib/api/entities/group_detail.rb index f3d64315203..9f3ced7d717 100644 --- a/lib/api/entities/group_detail.rb +++ b/lib/api/entities/group_detail.rb @@ -7,6 +7,7 @@ module API SharedGroupWithGroup.represent(group.shared_with_group_links_visible_to_user(options[:current_user])) end expose :runners_token, if: ->(_, options) { options[:user_can_admin_group] } + expose :enabled_git_access_protocol, if: ->(group, options) { group.root? && options[:user_can_admin_group] } expose :prevent_sharing_groups_outside_hierarchy, if: ->(group) { group.root? && group.namespace_settings.present? } diff --git a/lib/api/helpers/groups_helpers.rb b/lib/api/helpers/groups_helpers.rb index fbe13bfe8f7..1861ef7c402 100644 --- a/lib/api/helpers/groups_helpers.rb +++ b/lib/api/helpers/groups_helpers.rb @@ -35,6 +35,7 @@ module API optional :developer_can_initial_push, type: Boolean, desc: 'Allow developers to initial push' end optional :shared_runners_setting, type: String, values: ::Namespace::SHARED_RUNNERS_SETTINGS, desc: 'Enable/disable shared runners for the group and its subgroups and projects' + optional :enabled_git_access_protocol, type: String, values: %w[ssh http all], desc: 'Allow only the selected protocols to be used for Git access.' end params :optional_params_ee do diff --git a/lib/api/usage_data.rb b/lib/api/usage_data.rb index 0d1c6cb2281..67bf2725988 100644 --- a/lib/api/usage_data.rb +++ b/lib/api/usage_data.rb @@ -2,6 +2,8 @@ module API class UsageData < ::API::Base + include APIGuard + before { authenticate_non_get! } feature_category :service_ping @@ -12,6 +14,33 @@ module API forbidden!('Invalid CSRF token is provided') unless verified_request? end + resource :service_ping do + allow_access_with_scope :read_service_ping + + before do + authenticated_as_admin! + end + + desc 'Get the latest ServicePing payload' do + detail 'Introduces in Gitlab 16.9. Requires Personal Access Token with read_service_ping scope.' + success code: 200 + failure [ + { code: 401, message: '401 Unauthorized' }, + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not found' } + ] + tags %w[usage_data] + produces ['application/json'] + end + + get do + content_type 'application/json' + + Rails.cache.fetch(Gitlab::Usage::ServicePingReport::CACHE_KEY) || + ::RawUsageData.for_current_reporting_cycle.first&.payload || {} + end + end + desc 'Track usage data event' do detail 'This feature was introduced in GitLab 13.4.' success code: 200 diff --git a/lib/gitlab/click_house.rb b/lib/gitlab/click_house.rb new file mode 100644 index 00000000000..81468ab2875 --- /dev/null +++ b/lib/gitlab/click_house.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Gitlab + module ClickHouse + DATABASES = [:main].freeze + + def self.configured? + DATABASES.all? { |db| ::ClickHouse::Client.database_configured?(db) } + end + end +end diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb index 6dee9a404f4..3ee46019a06 100644 --- a/lib/gitlab/gitaly_client.rb +++ b/lib/gitlab/gitaly_client.rb @@ -491,6 +491,8 @@ module Gitlab private_class_method :increment_call_count def self.decrement_call_count(key) + return unless Gitlab::SafeRequestStore[key] + Gitlab::SafeRequestStore[key] -= 1 end private_class_method :decrement_call_count diff --git a/lib/gitlab/http.rb b/lib/gitlab/http.rb index 958b415e18f..593ea4f721e 100644 --- a/lib/gitlab/http.rb +++ b/lib/gitlab/http.rb @@ -6,8 +6,6 @@ # the usages to the new gem. # -require_relative 'http_connection_adapter' - module Gitlab class HTTP BlockedUrlError = Gitlab::HTTP_V2::BlockedUrlError diff --git a/lib/gitlab/http_connection_adapter.rb b/lib/gitlab/http_connection_adapter.rb deleted file mode 100644 index 8e9a63a9f7f..00000000000 --- a/lib/gitlab/http_connection_adapter.rb +++ /dev/null @@ -1,79 +0,0 @@ -# frozen_string_literal: true - -# This class is part of the Gitlab::HTTP wrapper. It handles local requests and header timeouts -# -# 1. Local requests -# Depending on the value of the global setting allow_local_requests_from_web_hooks_and_services, -# this adapter will allow/block connection to internal IPs and/or urls. -# -# This functionality can be overridden by providing the setting the option -# allow_local_requests = true in the request. For example: -# Gitlab::HTTP.get('http://www.gitlab.com', allow_local_requests: true) -# -# This option will take precedence over the global setting. -# -# 2. Header timeouts -# When the use_read_total_timeout option is used, that means the receiver -# of the HTTP request cannot be trusted. Gitlab::BufferedIo will be used, -# to read header data. It is a modified version of Net::BufferedIO that -# raises a timeout error if reading header data takes too much time. - -require_relative 'utils/override' - -module Gitlab - class HTTPConnectionAdapter < HTTParty::ConnectionAdapter - extend ::Gitlab::Utils::Override - - override :connection - def connection - result = validate_url_with_proxy!(uri) - @uri = result.uri - hostname = result.hostname - - http = super - http.hostname_override = hostname if hostname - - unless result.use_proxy - http.proxy_from_env = false - http.proxy_address = nil - end - - gitlab_http = Gitlab::NetHttpAdapter.new(http.address, http.port) - - http.instance_variables.each do |variable| - gitlab_http.instance_variable_set(variable, http.instance_variable_get(variable)) - end - - gitlab_http - end - - private - - def validate_url_with_proxy!(url) - Gitlab::UrlBlocker.validate_url_with_proxy!( - url, allow_local_network: allow_local_requests?, - allow_localhost: allow_local_requests?, - allow_object_storage: allow_object_storage?, - dns_rebind_protection: dns_rebind_protection?, - schemes: %w[http https]) - rescue Gitlab::HTTP_V2::UrlBlocker::BlockedUrlError => e - raise Gitlab::HTTP::BlockedUrlError, "URL is blocked: #{e.message}" - end - - def allow_local_requests? - options.fetch(:allow_local_requests, allow_settings_local_requests?) - end - - def allow_object_storage? - options.fetch(:allow_object_storage, false) - end - - def dns_rebind_protection? - Gitlab::CurrentSettings.dns_rebinding_protection_enabled? - end - - def allow_settings_local_requests? - Gitlab::CurrentSettings.allow_local_requests_from_web_hooks_and_services? - end - end -end diff --git a/lib/gitlab/import_export/remote_stream_upload.rb b/lib/gitlab/import_export/remote_stream_upload.rb index 1fb3faf0767..8f761cf37bb 100644 --- a/lib/gitlab/import_export/remote_stream_upload.rb +++ b/lib/gitlab/import_export/remote_stream_upload.rb @@ -77,7 +77,10 @@ module Gitlab attr_reader :download_url, :upload_url, :upload_method, :upload_content_type, :logger def receive_data(uri) - http = Gitlab::HTTPConnectionAdapter.new(URI(uri), {}).connection + http = Gitlab::HTTP_V2::NewConnectionAdapter.new(URI(uri), { + allow_local_requests: Gitlab::CurrentSettings.allow_local_requests_from_web_hooks_and_services?, + dns_rebind_protection: Gitlab::CurrentSettings.dns_rebinding_protection_enabled? + }).connection http.start do request = Net::HTTP::Get.new(uri) @@ -95,7 +98,10 @@ module Gitlab end def send_data(uri, content_length, chunks) - http = Gitlab::HTTPConnectionAdapter.new(URI(uri), {}).connection + http = Gitlab::HTTP_V2::NewConnectionAdapter.new(URI(uri), { + allow_local_requests: Gitlab::CurrentSettings.allow_local_requests_from_web_hooks_and_services?, + dns_rebind_protection: Gitlab::CurrentSettings.dns_rebinding_protection_enabled? + }).connection http.start do request = upload_request_class(upload_method).new(uri) diff --git a/lib/gitlab/metrics/global_search_slis.rb b/lib/gitlab/metrics/global_search_slis.rb index 530bebd72ab..1da0ad6797b 100644 --- a/lib/gitlab/metrics/global_search_slis.rb +++ b/lib/gitlab/metrics/global_search_slis.rb @@ -8,6 +8,7 @@ module Gitlab # gathered on 25-10-2022 # from https://log.gprd.gitlab.net/goto/0c89cd80-23af-11ed-8656-f5f2137823ba (internal only) BASIC_CONTENT_TARGET_S = 8.812 + BASIC_MR_TARGET_S = 15 BASIC_CODE_TARGET_S = 27.538 ADVANCED_CONTENT_TARGET_S = 2.452 ADVANCED_CODE_TARGET_S = 15.52 @@ -35,7 +36,9 @@ module Gitlab private def duration_target(search_type, search_scope) - if search_type == 'basic' && content_search?(search_scope) + if search_type == 'basic' && search_scope == 'merge_requests' + BASIC_MR_TARGET_S + elsif search_type == 'basic' && content_search?(search_scope) BASIC_CONTENT_TARGET_S elsif search_type == 'basic' && code_search?(search_scope) BASIC_CODE_TARGET_S diff --git a/lib/gitlab/sidekiq_middleware/pause_control/strategies/click_house_migration.rb b/lib/gitlab/sidekiq_middleware/pause_control/strategies/click_house_migration.rb index adeb0524567..c1d33885a40 100644 --- a/lib/gitlab/sidekiq_middleware/pause_control/strategies/click_house_migration.rb +++ b/lib/gitlab/sidekiq_middleware/pause_control/strategies/click_house_migration.rb @@ -9,7 +9,7 @@ module Gitlab def should_pause? return false unless Feature.enabled?(:pause_clickhouse_workers_during_migration) - ClickHouse::MigrationSupport::ExclusiveLock.pause_workers? + ::ClickHouse::MigrationSupport::ExclusiveLock.pause_workers? end end end diff --git a/lib/tasks/gitlab/assets.rake b/lib/tasks/gitlab/assets.rake index b8a6e701876..16b0f5bedba 100644 --- a/lib/tasks/gitlab/assets.rake +++ b/lib/tasks/gitlab/assets.rake @@ -101,6 +101,8 @@ namespace :gitlab do # gettext:compile needs to run before rake:assets:precompile because # app/assets/javascripts/locale/**/app.js are pre-compiled by Sprockets Gitlab::TaskHelpers.invoke_and_time_task('gettext:compile') + # Skip Yarn Install when using Cssbundling + Rake::Task["css:install"].clear if defined?(Cssbundling) Gitlab::TaskHelpers.invoke_and_time_task('rake:assets:precompile') log_path = ENV['WEBPACK_COMPILE_LOG_PATH'] diff --git a/scripts/frontend/clean_css_assets.mjs b/scripts/frontend/clean_css_assets.mjs new file mode 100755 index 00000000000..de89b9820f7 --- /dev/null +++ b/scripts/frontend/clean_css_assets.mjs @@ -0,0 +1,72 @@ +#!/usr/bin/env node + +import { argv, cwd } from 'node:process'; +import { join, resolve, relative, dirname } from 'node:path'; +import { mkdir, stat, readFile, writeFile } from 'node:fs/promises'; +import glob from 'glob'; +import * as esbuild from 'esbuild'; +import prettier from 'prettier'; + +/** + * VISION: This script could be made more generalizable, to be able to + * "normalize" our complete asset folder in order to easily diff them. + * + * It might even be great to have support for using MRs/Pipelines, etc. + * + * normalize_assets.mjs https://gitlab.com/gitlab-org/gitlab/-/pipelines/1143467234 tmp/current_master + * normalize_assets.mjs https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140611 tmp/after_change + */ + +/** + * 1. this function removes the `hash` from the file name + * (sprockets is unhappy compiling without hash) + * 2. Minifies the css, to remove comments and normalize things like + * `#ffffff` and `#fff` or `.5rem` and `0.5rem` + * 3. Prettifies it again, to make it diffable + */ +async function cleanUpCSSFile(sourceFile, sourceDir, targetDir) { + const targetFile = join(targetDir, relative(sourceDir, sourceFile)).replace( + /-[a-f0-9]{20,}.css$/, + '.css', + ); + await mkdir(dirname(targetFile), { recursive: true }); + + const content = await readFile(sourceFile, 'utf-8'); + const minified = await esbuild.transform(content, { + minify: true, + loader: 'css', + }); + const pretty = await prettier.format(minified.code, { parser: 'css' }); + console.log(`Copied ${relative(cwd(), sourceFile)} to \n\t${relative(cwd(), targetFile)}`); + return writeFile(targetFile, pretty, 'utf-8'); +} + +async function main() { + const [, , sourceDirRel, targetDirRel] = argv; + + if (!sourceDirRel || !targetDirRel) { + throw new Error('Please start this script like with two parameters: '); + } + + const sourceDir = resolve(cwd(), sourceDirRel); + + const s = await stat(sourceDir); + if (!s.isDirectory()) { + throw new Error(`sourcePath ${sourceDir} is not a directory`); + } + + const targetDir = resolve(cwd(), targetDirRel); + + const cssFiles = glob.sync(join(sourceDir, '**/*.css')); + + return Promise.all( + cssFiles.map((sourceFile) => cleanUpCSSFile(sourceFile, sourceDir, targetDir)), + ); +} + +try { + await main(); +} catch (e) { + console.error(e); + process.exitCode = 1; +} diff --git a/scripts/frontend/compare_css_compilers.sh b/scripts/frontend/compare_css_compilers.sh new file mode 100755 index 00000000000..faf20871678 --- /dev/null +++ b/scripts/frontend/compare_css_compilers.sh @@ -0,0 +1,35 @@ +#!/bin/bash +set -euo pipefail +IFS=$'\n\t' + +if [ ! -d "glfm_specification" ] || [ ! -f "GITALY_SERVER_VERSION" ]; then + echo 'Please run this from the gitlab root folder with `./scripts/frontend/compare_css_compilers.sh`' + exit 1 +fi + +function clean_up { + rm -rf public/assets + rm -rf app/assets/builds/* + rm -rf tmp/cache/assets +} + +rm -rf tmp/css_compare +clean_up + +export SKIP_YARN_INSTALL=1 + +echo "Compiling with sassc-rails" +export USE_NEW_CSS_PIPELINE=0 +time bin/rails assets:precompile +scripts/frontend/clean_css_assets.mjs public/assets tmp/css_compare/sassc-rails + +clean_up + +export USE_NEW_CSS_PIPELINE=1 +echo "Compiling with dart-sass" +time bin/rails assets:precompile +scripts/frontend/clean_css_assets.mjs public/assets tmp/css_compare/cssbundling + +clean_up + +echo 'You now can run `diff -u tmp/css_compare/sassc-rails tmp/css_compare/cssbundling` to diff the two' diff --git a/spec/controllers/sent_notifications_controller_spec.rb b/spec/controllers/sent_notifications_controller_spec.rb index 190c00092b6..3061697551e 100644 --- a/spec/controllers/sent_notifications_controller_spec.rb +++ b/spec/controllers/sent_notifications_controller_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe SentNotificationsController do +RSpec.describe SentNotificationsController, feature_category: :shared do let(:user) { create(:user) } let(:project) { create(:project, :public) } let(:private_project) { create(:project, :private) } @@ -235,7 +235,9 @@ RSpec.describe SentNotificationsController do end end - let(:sent_notification) { create(:sent_notification, project: project, noteable: merge_request, recipient: user) } + let(:sent_notification) do + create(:sent_notification, project: project, noteable: merge_request, recipient: user) + end before do unsubscribe @@ -299,12 +301,36 @@ RSpec.describe SentNotificationsController do end context 'when support bot is the notification recipient' do - let(:sent_notification) { create(:sent_notification, project: target_project, noteable: noteable, recipient: Users::Internal.support_bot) } + let(:sent_notification) do + create(:sent_notification, + project: target_project, noteable: noteable, recipient: Users::Internal.support_bot) + end it 'deletes the external author on the issue' do expect { unsubscribe }.to change { issue.issue_email_participants.count }.by(-1) end + context 'when sent_notification contains issue_email_participant' do + let!(:other_issue_email_participant) do + create(:issue_email_participant, issue: issue, email: 'other@example.com') + end + + let(:sent_notification) do + create(:sent_notification, + project: target_project, + noteable: noteable, + recipient: Users::Internal.support_bot, + issue_email_participant: other_issue_email_participant + ) + end + + it 'deletes the connected issue email participant' do + expect { unsubscribe }.to change { issue.issue_email_participants.count }.by(-1) + # Ensure external author is still present + expect(issue.email_participants_emails).to contain_exactly(email) + end + end + context 'when noteable is not an issue' do let(:noteable) { merge_request } diff --git a/spec/frontend/content_editor/extensions/attachment_spec.js b/spec/frontend/content_editor/extensions/attachment_spec.js index f037ac520fe..18ce6c9ab59 100644 --- a/spec/frontend/content_editor/extensions/attachment_spec.js +++ b/spec/frontend/content_editor/extensions/attachment_spec.js @@ -126,12 +126,12 @@ describe('content_editor/extensions/attachment', () => { describe.each` nodeType | html | file | mediaType - ${'image (png)'} | ${PROJECT_WIKI_ATTACHMENT_IMAGE_HTML} | ${imageFile} | ${(attrs) => image(attrs)} - ${'image (svg)'} | ${PROJECT_WIKI_ATTACHMENT_IMAGE_SVG_HTML} | ${imageFileSvg} | ${(attrs) => image(attrs)} + ${'image'} | ${PROJECT_WIKI_ATTACHMENT_IMAGE_HTML} | ${imageFile} | ${(attrs) => image(attrs)} + ${'image'} | ${PROJECT_WIKI_ATTACHMENT_IMAGE_SVG_HTML} | ${imageFileSvg} | ${(attrs) => image(attrs)} ${'audio'} | ${PROJECT_WIKI_ATTACHMENT_AUDIO_HTML} | ${audioFile} | ${(attrs) => audio(attrs)} ${'video'} | ${PROJECT_WIKI_ATTACHMENT_VIDEO_HTML} | ${videoFile} | ${(attrs) => video(attrs)} ${'drawioDiagram'} | ${PROJECT_WIKI_ATTACHMENT_DRAWIO_DIAGRAM_HTML} | ${drawioDiagramFile} | ${(attrs) => drawioDiagram(attrs)} - `('when the file is $nodeType', ({ html, file, mediaType }) => { + `('when the file is $nodeType', ({ nodeType, html, file, mediaType }) => { beforeEach(() => { renderMarkdown.mockResolvedValue(html); }); @@ -149,7 +149,13 @@ describe('content_editor/extensions/attachment', () => { it('inserts a media content with src set to the encoded content and uploading=file_name', async () => { const expectedDoc = doc( - p(mediaType({ uploading: file.name, src: blobUrl, alt: file.name })), + p( + mediaType({ + uploading: expect.stringMatching(new RegExp(`${nodeType}[0-9]+`)), + src: blobUrl, + alt: file.name, + }), + ), ); await expectDocumentAfterTransaction({ @@ -248,7 +254,12 @@ describe('content_editor/extensions/attachment', () => { it('inserts a link with a blob url', async () => { const expectedDoc = doc( - p(link({ uploading: attachmentFile.name, href: blobUrl }, 'test-file.zip')), + p( + link( + { uploading: expect.stringMatching(/file[0-9]+/), href: blobUrl }, + 'test-file.zip', + ), + ), ); await expectDocumentAfterTransaction({ @@ -334,68 +345,228 @@ describe('content_editor/extensions/attachment', () => { }; it.each([ - [1, () => doc(p(link({ href: blobUrl, uploading: 'test-file.zip' }, 'test-file.zip')))], + [ + 1, + () => + doc( + p( + link( + { href: blobUrl, uploading: expect.stringMatching(/file[0-9]+/) }, + 'test-file.zip', + ), + ), + ), + ], [ 2, () => doc( - p(link({ href: blobUrl, uploading: 'test-file.zip' }, 'test-file.zip')), - p(image({ alt: 'test-file.png', src: blobUrl, uploading: 'test-file.png' })), + p( + link( + { href: blobUrl, uploading: expect.stringMatching(/file[0-9]+/) }, + 'test-file.zip', + ), + ), + p( + image({ + alt: 'test-file.png', + src: blobUrl, + uploading: expect.stringMatching(/image[0-9]+/), + }), + ), ), ], [ 3, () => doc( - p(link({ href: blobUrl, uploading: 'test-file.zip' }, 'test-file.zip')), - p(image({ alt: 'test-file.png', src: blobUrl, uploading: 'test-file.png' })), - p(video({ alt: 'test-file.mp4', src: blobUrl, uploading: 'test-file.mp4' })), + p( + link( + { href: blobUrl, uploading: expect.stringMatching(/file[0-9]+/) }, + 'test-file.zip', + ), + ), + p( + image({ + alt: 'test-file.png', + src: blobUrl, + uploading: expect.stringMatching(/image[0-9]+/), + }), + ), + p( + video({ + alt: 'test-file.mp4', + src: blobUrl, + uploading: expect.stringMatching(/video[0-9]+/), + }), + ), ), ], [ 4, () => doc( - p(link({ href: blobUrl, uploading: 'test-file.zip' }, 'test-file.zip')), - p(image({ alt: 'test-file.png', src: blobUrl, uploading: 'test-file.png' })), - p(video({ alt: 'test-file.mp4', src: blobUrl, uploading: 'test-file.mp4' })), - p(link({ href: blobUrl, uploading: 'test-file1.zip' }, 'test-file1.zip')), + p( + link( + { href: blobUrl, uploading: expect.stringMatching(/file[0-9]+/) }, + 'test-file.zip', + ), + ), + p( + image({ + alt: 'test-file.png', + src: blobUrl, + uploading: expect.stringMatching(/image[0-9]+/), + }), + ), + p( + video({ + alt: 'test-file.mp4', + src: blobUrl, + uploading: expect.stringMatching(/video[0-9]+/), + }), + ), + p( + link( + { href: blobUrl, uploading: expect.stringMatching(/file[0-9]+/) }, + 'test-file1.zip', + ), + ), ), ], [ 5, () => doc( - p(link({ href: blobUrl, uploading: 'test-file.zip' }, 'test-file.zip')), - p(image({ alt: 'test-file.png', src: blobUrl, uploading: 'test-file.png' })), - p(video({ alt: 'test-file.mp4', src: blobUrl, uploading: 'test-file.mp4' })), - p(link({ href: blobUrl, uploading: 'test-file1.zip' }, 'test-file1.zip')), - p(link({ href: blobUrl, uploading: 'test-file2.zip' }, 'test-file2.zip')), + p( + link( + { href: blobUrl, uploading: expect.stringMatching(/file[0-9]+/) }, + 'test-file.zip', + ), + ), + p( + image({ + alt: 'test-file.png', + src: blobUrl, + uploading: expect.stringMatching(/image[0-9]+/), + }), + ), + p( + video({ + alt: 'test-file.mp4', + src: blobUrl, + uploading: expect.stringMatching(/video[0-9]+/), + }), + ), + p( + link( + { href: blobUrl, uploading: expect.stringMatching(/file[0-9]+/) }, + 'test-file1.zip', + ), + ), + p( + link( + { href: blobUrl, uploading: expect.stringMatching(/file[0-9]+/) }, + 'test-file2.zip', + ), + ), ), ], [ 6, () => doc( - p(link({ href: blobUrl, uploading: 'test-file.zip' }, 'test-file.zip')), - p(image({ alt: 'test-file.png', src: blobUrl, uploading: 'test-file.png' })), - p(video({ alt: 'test-file.mp4', src: blobUrl, uploading: 'test-file.mp4' })), - p(link({ href: blobUrl, uploading: 'test-file1.zip' }, 'test-file1.zip')), - p(link({ href: blobUrl, uploading: 'test-file2.zip' }, 'test-file2.zip')), - p(video({ alt: 'test-file1.mp4', src: blobUrl, uploading: 'test-file1.mp4' })), + p( + link( + { href: blobUrl, uploading: expect.stringMatching(/file[0-9]+/) }, + 'test-file.zip', + ), + ), + p( + image({ + alt: 'test-file.png', + src: blobUrl, + uploading: expect.stringMatching(/image[0-9]+/), + }), + ), + p( + video({ + alt: 'test-file.mp4', + src: blobUrl, + uploading: expect.stringMatching(/video[0-9]+/), + }), + ), + p( + link( + { href: blobUrl, uploading: expect.stringMatching(/file[0-9]+/) }, + 'test-file1.zip', + ), + ), + p( + link( + { href: blobUrl, uploading: expect.stringMatching(/file[0-9]+/) }, + 'test-file2.zip', + ), + ), + p( + video({ + alt: 'test-file1.mp4', + src: blobUrl, + uploading: expect.stringMatching(/video[0-9]+/), + }), + ), ), ], [ 7, () => doc( - p(link({ href: blobUrl, uploading: 'test-file.zip' }, 'test-file.zip')), - p(image({ alt: 'test-file.png', src: blobUrl, uploading: 'test-file.png' })), - p(video({ alt: 'test-file.mp4', src: blobUrl, uploading: 'test-file.mp4' })), - p(link({ href: blobUrl, uploading: 'test-file1.zip' }, 'test-file1.zip')), - p(link({ href: blobUrl, uploading: 'test-file2.zip' }, 'test-file2.zip')), - p(video({ alt: 'test-file1.mp4', src: blobUrl, uploading: 'test-file1.mp4' })), - p(audio({ alt: 'test-file.mp3', src: blobUrl, uploading: 'test-file.mp3' })), + p( + link( + { href: blobUrl, uploading: expect.stringMatching(/file[0-9]+/) }, + 'test-file.zip', + ), + ), + p( + image({ + alt: 'test-file.png', + src: blobUrl, + uploading: expect.stringMatching(/image[0-9]+/), + }), + ), + p( + video({ + alt: 'test-file.mp4', + src: blobUrl, + uploading: expect.stringMatching(/video[0-9]+/), + }), + ), + p( + link( + { href: blobUrl, uploading: expect.stringMatching(/file[0-9]+/) }, + 'test-file1.zip', + ), + ), + p( + link( + { href: blobUrl, uploading: expect.stringMatching(/file[0-9]+/) }, + 'test-file2.zip', + ), + ), + p( + video({ + alt: 'test-file1.mp4', + src: blobUrl, + uploading: expect.stringMatching(/video[0-9]+/), + }), + ), + p( + audio({ + alt: 'test-file.mp3', + src: blobUrl, + uploading: expect.stringMatching(/audio[0-9]+/), + }), + ), ), ], [ @@ -412,12 +583,46 @@ describe('content_editor/extensions/attachment', () => { 'test-file.zip', ), ), - p(image({ alt: 'test-file.png', src: blobUrl, uploading: 'test-file.png' })), - p(video({ alt: 'test-file.mp4', src: blobUrl, uploading: 'test-file.mp4' })), - p(link({ href: blobUrl, uploading: 'test-file1.zip' }, 'test-file1.zip')), - p(link({ href: blobUrl, uploading: 'test-file2.zip' }, 'test-file2.zip')), - p(video({ alt: 'test-file1.mp4', src: blobUrl, uploading: 'test-file1.mp4' })), - p(audio({ alt: 'test-file.mp3', src: blobUrl, uploading: 'test-file.mp3' })), + p( + image({ + alt: 'test-file.png', + src: blobUrl, + uploading: expect.stringMatching(/image[0-9]+/), + }), + ), + p( + video({ + alt: 'test-file.mp4', + src: blobUrl, + uploading: expect.stringMatching(/video[0-9]+/), + }), + ), + p( + link( + { href: blobUrl, uploading: expect.stringMatching(/file[0-9]+/) }, + 'test-file1.zip', + ), + ), + p( + link( + { href: blobUrl, uploading: expect.stringMatching(/file[0-9]+/) }, + 'test-file2.zip', + ), + ), + p( + video({ + alt: 'test-file1.mp4', + src: blobUrl, + uploading: expect.stringMatching(/video[0-9]+/), + }), + ), + p( + audio({ + alt: 'test-file.mp3', + src: blobUrl, + uploading: expect.stringMatching(/audio[0-9]+/), + }), + ), ), ], [ @@ -442,11 +647,39 @@ describe('content_editor/extensions/attachment', () => { uploading: false, }), ), - p(video({ alt: 'test-file.mp4', src: blobUrl, uploading: 'test-file.mp4' })), - p(link({ href: blobUrl, uploading: 'test-file1.zip' }, 'test-file1.zip')), - p(link({ href: blobUrl, uploading: 'test-file2.zip' }, 'test-file2.zip')), - p(video({ alt: 'test-file1.mp4', src: blobUrl, uploading: 'test-file1.mp4' })), - p(audio({ alt: 'test-file.mp3', src: blobUrl, uploading: 'test-file.mp3' })), + p( + video({ + alt: 'test-file.mp4', + src: blobUrl, + uploading: expect.stringMatching(/video[0-9]+/), + }), + ), + p( + link( + { href: blobUrl, uploading: expect.stringMatching(/file[0-9]+/) }, + 'test-file1.zip', + ), + ), + p( + link( + { href: blobUrl, uploading: expect.stringMatching(/file[0-9]+/) }, + 'test-file2.zip', + ), + ), + p( + video({ + alt: 'test-file1.mp4', + src: blobUrl, + uploading: expect.stringMatching(/video[0-9]+/), + }), + ), + p( + audio({ + alt: 'test-file.mp3', + src: blobUrl, + uploading: expect.stringMatching(/audio[0-9]+/), + }), + ), ), ], [ @@ -479,10 +712,32 @@ describe('content_editor/extensions/attachment', () => { uploading: false, }), ), - p(link({ href: blobUrl, uploading: 'test-file1.zip' }, 'test-file1.zip')), - p(link({ href: blobUrl, uploading: 'test-file2.zip' }, 'test-file2.zip')), - p(video({ alt: 'test-file1.mp4', src: blobUrl, uploading: 'test-file1.mp4' })), - p(audio({ alt: 'test-file.mp3', src: blobUrl, uploading: 'test-file.mp3' })), + p( + link( + { href: blobUrl, uploading: expect.stringMatching(/file[0-9]+/) }, + 'test-file1.zip', + ), + ), + p( + link( + { href: blobUrl, uploading: expect.stringMatching(/file[0-9]+/) }, + 'test-file2.zip', + ), + ), + p( + video({ + alt: 'test-file1.mp4', + src: blobUrl, + uploading: expect.stringMatching(/video[0-9]+/), + }), + ), + p( + audio({ + alt: 'test-file.mp3', + src: blobUrl, + uploading: expect.stringMatching(/audio[0-9]+/), + }), + ), ), ], [ @@ -525,9 +780,26 @@ describe('content_editor/extensions/attachment', () => { 'test-file1.zip', ), ), - p(link({ href: blobUrl, uploading: 'test-file2.zip' }, 'test-file2.zip')), - p(video({ alt: 'test-file1.mp4', src: blobUrl, uploading: 'test-file1.mp4' })), - p(audio({ alt: 'test-file.mp3', src: blobUrl, uploading: 'test-file.mp3' })), + p( + link( + { href: blobUrl, uploading: expect.stringMatching(/file[0-9]+/) }, + 'test-file2.zip', + ), + ), + p( + video({ + alt: 'test-file1.mp4', + src: blobUrl, + uploading: expect.stringMatching(/video[0-9]+/), + }), + ), + p( + audio({ + alt: 'test-file.mp3', + src: blobUrl, + uploading: expect.stringMatching(/audio[0-9]+/), + }), + ), ), ], [ @@ -580,8 +852,20 @@ describe('content_editor/extensions/attachment', () => { 'test-file2.zip', ), ), - p(video({ alt: 'test-file1.mp4', src: blobUrl, uploading: 'test-file1.mp4' })), - p(audio({ alt: 'test-file.mp3', src: blobUrl, uploading: 'test-file.mp3' })), + p( + video({ + alt: 'test-file1.mp4', + src: blobUrl, + uploading: expect.stringMatching(/video[0-9]+/), + }), + ), + p( + audio({ + alt: 'test-file.mp3', + src: blobUrl, + uploading: expect.stringMatching(/audio[0-9]+/), + }), + ), ), ], [ @@ -642,7 +926,13 @@ describe('content_editor/extensions/attachment', () => { uploading: false, }), ), - p(audio({ alt: 'test-file.mp3', src: blobUrl, uploading: 'test-file.mp3' })), + p( + audio({ + alt: 'test-file.mp3', + src: blobUrl, + uploading: expect.stringMatching(/audio[0-9]+/), + }), + ), ), ], [ diff --git a/spec/graphql/mutations/work_items/update_task_spec.rb b/spec/graphql/mutations/work_items/update_task_spec.rb deleted file mode 100644 index cb37a72bbdd..00000000000 --- a/spec/graphql/mutations/work_items/update_task_spec.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Mutations::WorkItems::UpdateTask do - let_it_be(:project) { create(:project) } - let_it_be(:developer) { create(:user).tap { |user| project.add_developer(user) } } - let_it_be(:referenced_work_item, refind: true) { create(:work_item, project: project, title: 'REFERENCED') } - let_it_be(:parent_work_item) do - create(:work_item, project: project, description: "- [ ] #{referenced_work_item.to_reference}+") - end - - let(:task_params) { { title: 'UPDATED' } } - let(:task_input) { { id: referenced_work_item.to_global_id }.merge(task_params) } - let(:input) { { id: parent_work_item.to_global_id, task_data: task_input } } - let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) } - - describe '#resolve' do - subject(:resolve) do - mutation.resolve(**input) - end - - context 'when user has sufficient permissions' do - let(:current_user) { developer } - - it 'expires etag cache for parent work item' do - allow(WorkItem).to receive(:find).and_call_original - allow(WorkItem).to receive(:find).with(parent_work_item.id.to_s).and_return(parent_work_item) - - expect(parent_work_item).to receive(:expire_etag_cache) - - resolve - end - end - end -end diff --git a/spec/lib/api/entities/group_detail_spec.rb b/spec/lib/api/entities/group_detail_spec.rb index 8fcb120c809..f3200b28c4d 100644 --- a/spec/lib/api/entities/group_detail_spec.rb +++ b/spec/lib/api/entities/group_detail_spec.rb @@ -2,18 +2,50 @@ require 'spec_helper' -RSpec.describe API::Entities::GroupDetail do +RSpec.describe API::Entities::GroupDetail, feature_category: :groups_and_projects do describe '#as_json' do - it 'includes prevent_sharing_groups_outside_hierarchy for a root group' do - group = create(:group) + subject { described_class.new(group, options).as_json } - expect(described_class.new(group).as_json).to include(prevent_sharing_groups_outside_hierarchy: false) + let_it_be(:root_group) { create(:group) } + let_it_be(:subgroup) { create(:group, :nested) } + + let(:options) { {} } + + describe '#prevent_sharing_groups_outside_hierarchy' do + context 'for a root group' do + let(:group) { root_group } + + it { is_expected.to include(:prevent_sharing_groups_outside_hierarchy) } + end + + context 'for a subgroup' do + let(:group) { subgroup } + + it { is_expected.not_to include(:prevent_sharing_groups_outside_hierarchy) } + end end - it 'excludes prevent_sharing_groups_outside_hierarchy for a subgroup' do - subgroup = build(:group, :nested) + describe '#enabled_git_access_protocol' do + using RSpec::Parameterized::TableSyntax + + where(:group, :can_admin_group, :includes_field) do + ref(:root_group) | false | false + ref(:root_group) | true | true + ref(:subgroup) | false | false + ref(:subgroup) | true | false + end + + with_them do + let(:options) { { user_can_admin_group: can_admin_group } } - expect(described_class.new(subgroup).as_json.keys).not_to include(:prevent_sharing_groups_outside_hierarchy) + it 'verifies presence of the field' do + if includes_field + is_expected.to include(:enabled_git_access_protocol) + else + is_expected.not_to include(:enabled_git_access_protocol) + end + end + end end end end diff --git a/spec/lib/gitlab/click_house_spec.rb b/spec/lib/gitlab/click_house_spec.rb new file mode 100644 index 00000000000..3241eca34ae --- /dev/null +++ b/spec/lib/gitlab/click_house_spec.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::ClickHouse, feature_category: :database do + context 'when ClickHouse is not configured' do + it 'returns false' do + expect(described_class).not_to be_configured + end + end + + context 'when ClickHouse is configured', :click_house do + it 'returns false' do + expect(described_class).to be_configured + end + end +end diff --git a/spec/lib/gitlab/gitaly_client_spec.rb b/spec/lib/gitlab/gitaly_client_spec.rb index 796fe75521a..5ff3bc20bc1 100644 --- a/spec/lib/gitlab/gitaly_client_spec.rb +++ b/spec/lib/gitlab/gitaly_client_spec.rb @@ -365,6 +365,20 @@ RSpec.describe Gitlab::GitalyClient, feature_category: :gitaly do it 'returns the result of the allow_n_plus_1_calls block' do expect(described_class.allow_n_plus_1_calls { "result" }).to eq("result") end + + context 'when the `gitaly_call_count_exception_block_depth` key is not present' do + before do + allow(Gitlab::SafeRequestStore).to receive(:[]).with(:gitaly_call_count_exception_block_depth).and_return(0, 1, nil) + allow(Gitlab::SafeRequestStore).to receive(:+) + allow(Gitlab::SafeRequestStore).to receive(:-) + end + + it 'does not decrement call count' do + expect(Gitlab::SafeRequestStore).not_to have_received(:-) + + described_class.allow_n_plus_1_calls { "result" } + end + end end context 'when RequestStore is not active' do diff --git a/spec/lib/gitlab/http_connection_adapter_spec.rb b/spec/lib/gitlab/http_connection_adapter_spec.rb deleted file mode 100644 index fac0c1a2a9f..00000000000 --- a/spec/lib/gitlab/http_connection_adapter_spec.rb +++ /dev/null @@ -1,161 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Gitlab::HTTPConnectionAdapter, feature_category: :shared do - include StubRequests - - let(:uri) { URI('https://example.org') } - let(:options) { {} } - - subject(:connection) { described_class.new(uri, options).connection } - - describe '#connection' do - before do - stub_all_dns('https://example.org', ip_address: '93.184.216.34') - end - - context 'when local requests are allowed' do - let(:options) { { allow_local_requests: true } } - - it 'sets up the connection' do - expect(connection).to be_a(Gitlab::NetHttpAdapter) - expect(connection.address).to eq('93.184.216.34') - expect(connection.hostname_override).to eq('example.org') - expect(connection.addr_port).to eq('example.org') - expect(connection.port).to eq(443) - end - end - - context 'when local requests are not allowed' do - let(:options) { { allow_local_requests: false } } - - it 'sets up the connection' do - expect(connection).to be_a(Gitlab::NetHttpAdapter) - expect(connection.address).to eq('93.184.216.34') - expect(connection.hostname_override).to eq('example.org') - expect(connection.addr_port).to eq('example.org') - expect(connection.port).to eq(443) - end - - context 'when it is a request to local network' do - let(:uri) { URI('http://172.16.0.0/12') } - - it 'raises error' do - expect { subject }.to raise_error( - Gitlab::HTTP::BlockedUrlError, - "URL is blocked: Requests to the local network are not allowed" - ) - end - - context 'when local request allowed' do - let(:options) { { allow_local_requests: true } } - - it 'sets up the connection' do - expect(connection).to be_a(Gitlab::NetHttpAdapter) - expect(connection.address).to eq('172.16.0.0') - expect(connection.hostname_override).to be(nil) - expect(connection.addr_port).to eq('172.16.0.0') - expect(connection.port).to eq(80) - end - end - end - - context 'when it is a request to local address' do - let(:uri) { URI('http://127.0.0.1') } - - it 'raises error' do - expect { subject }.to raise_error( - Gitlab::HTTP::BlockedUrlError, - "URL is blocked: Requests to localhost are not allowed" - ) - end - - context 'when local request allowed' do - let(:options) { { allow_local_requests: true } } - - it 'sets up the connection' do - expect(connection).to be_a(Gitlab::NetHttpAdapter) - expect(connection.address).to eq('127.0.0.1') - expect(connection.hostname_override).to be(nil) - expect(connection.addr_port).to eq('127.0.0.1') - expect(connection.port).to eq(80) - end - end - end - - context 'when port different from URL scheme is used' do - let(:uri) { URI('https://example.org:8080') } - - it 'sets up the addr_port accordingly' do - expect(connection).to be_a(Gitlab::NetHttpAdapter) - expect(connection.address).to eq('93.184.216.34') - expect(connection.hostname_override).to eq('example.org') - expect(connection.addr_port).to eq('example.org:8080') - expect(connection.port).to eq(8080) - end - end - end - - context 'when DNS rebinding protection is disabled' do - before do - stub_application_setting(dns_rebinding_protection_enabled: false) - end - - it 'sets up the connection' do - expect(connection).to be_a(Gitlab::NetHttpAdapter) - expect(connection.address).to eq('example.org') - expect(connection.hostname_override).to eq(nil) - expect(connection.addr_port).to eq('example.org') - expect(connection.port).to eq(443) - end - end - - context 'when proxy is enabled' do - before do - stub_env('http_proxy', 'http://proxy.example.com') - end - - it 'proxy stays configured' do - expect(connection.proxy?).to be true - expect(connection.proxy_from_env?).to be true - expect(connection.proxy_address).to eq('proxy.example.com') - end - - context 'when no_proxy matches the request' do - before do - stub_env('no_proxy', 'example.org') - end - - it 'proxy is disabled' do - expect(connection.proxy?).to be false - expect(connection.proxy_from_env?).to be false - expect(connection.proxy_address).to be nil - end - end - - context 'when no_proxy does not match the request' do - before do - stub_env('no_proxy', 'example.com') - end - - it 'proxy stays configured' do - expect(connection.proxy?).to be true - expect(connection.proxy_from_env?).to be true - expect(connection.proxy_address).to eq('proxy.example.com') - end - end - end - - context 'when URL scheme is not HTTP/HTTPS' do - let(:uri) { URI('ssh://example.org') } - - it 'raises error' do - expect { subject }.to raise_error( - Gitlab::HTTP::BlockedUrlError, - "URL is blocked: Only allowed schemes are http, https" - ) - end - end - end -end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 6d5c17176dc..9e83b7d85e9 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -1010,6 +1010,7 @@ epic: - user_note_authors - boards_epic_user_preferences - epic_board_positions +- work_item epic_issue: - epic - issue diff --git a/spec/lib/gitlab/import_export/remote_stream_upload_spec.rb b/spec/lib/gitlab/import_export/remote_stream_upload_spec.rb index 3d9d6e1b96b..c27bc7b5fe1 100644 --- a/spec/lib/gitlab/import_export/remote_stream_upload_spec.rb +++ b/spec/lib/gitlab/import_export/remote_stream_upload_spec.rb @@ -23,10 +23,14 @@ RSpec.describe Gitlab::ImportExport::RemoteStreamUpload do describe '#execute' do context 'when download request and upload request return 200' do - it 'uploads the downloaded content' do + before do + stub_application_setting(allow_local_requests_from_web_hooks_and_services: true) + stub_application_setting(dns_rebinding_protection_enabled: false) stub_request(:get, download_url).to_return(status: 200, body: 'ABC', headers: { 'Content-Length' => 3 }) stub_request(:post, upload_url) + end + it 'uploads the downloaded content' do subject.execute expect( @@ -35,6 +39,16 @@ RSpec.describe Gitlab::ImportExport::RemoteStreamUpload do ) ).to have_been_made end + + it 'calls the connection adapter twice with required args' do + expect(Gitlab::HTTP_V2::NewConnectionAdapter) + .to receive(:new).twice.with(instance_of(URI::HTTP), { + allow_local_requests: true, + dns_rebind_protection: false + }).and_call_original + + subject.execute + end end context 'when upload method is put' do @@ -87,7 +101,7 @@ RSpec.describe Gitlab::ImportExport::RemoteStreamUpload do it 'raises error' do expect { subject.execute }.to raise_error( - Gitlab::HTTP::BlockedUrlError, + Gitlab::HTTP_V2::BlockedUrlError, "URL is blocked: Requests to localhost are not allowed" ) end @@ -113,7 +127,7 @@ RSpec.describe Gitlab::ImportExport::RemoteStreamUpload do it 'raises error' do expect { subject.execute }.to raise_error( - Gitlab::HTTP::BlockedUrlError, + Gitlab::HTTP_V2::BlockedUrlError, "URL is blocked: Requests to the local network are not allowed" ) end @@ -141,7 +155,7 @@ RSpec.describe Gitlab::ImportExport::RemoteStreamUpload do stub_request(:get, download_url) expect { subject.execute }.to raise_error( - Gitlab::HTTP::BlockedUrlError, + Gitlab::HTTP_V2::BlockedUrlError, "URL is blocked: Requests to localhost are not allowed" ) end @@ -167,7 +181,7 @@ RSpec.describe Gitlab::ImportExport::RemoteStreamUpload do it 'raises error' do expect { subject.execute }.to raise_error( - Gitlab::HTTP::BlockedUrlError, + Gitlab::HTTP_V2::BlockedUrlError, "URL is blocked: Requests to the local network are not allowed" ) end @@ -191,7 +205,7 @@ RSpec.describe Gitlab::ImportExport::RemoteStreamUpload do stub_full_request(upload_url, ip_address: '127.0.0.1', method: upload_method) expect { subject.execute }.to raise_error( - Gitlab::HTTP::BlockedUrlError, + Gitlab::HTTP_V2::BlockedUrlError, "URL is blocked: Requests to localhost are not allowed" ) end diff --git a/spec/lib/gitlab/metrics/global_search_slis_spec.rb b/spec/lib/gitlab/metrics/global_search_slis_spec.rb index 68793db6e41..9aa4f3b106e 100644 --- a/spec/lib/gitlab/metrics/global_search_slis_spec.rb +++ b/spec/lib/gitlab/metrics/global_search_slis_spec.rb @@ -87,6 +87,10 @@ RSpec.describe Gitlab::Metrics::GlobalSearchSlis, feature_category: :global_sear end describe '#record_apdex' do + before do + allow(::Gitlab::ApplicationContext).to receive(:current_context_attribute).with(:caller_id).and_return('end') + end + where(:search_type, :code_search, :duration_target) do 'basic' | false | 8.812 'basic' | true | 27.538 @@ -96,10 +100,6 @@ RSpec.describe Gitlab::Metrics::GlobalSearchSlis, feature_category: :global_sear end with_them do - before do - allow(::Gitlab::ApplicationContext).to receive(:current_context_attribute).with(:caller_id).and_return('end') - end - let(:search_scope) { code_search ? 'blobs' : 'issues' } it 'increments the global_search SLI as a success if the elapsed time is within the target' do @@ -144,6 +144,46 @@ RSpec.describe Gitlab::Metrics::GlobalSearchSlis, feature_category: :global_sear ) end end + + context 'when the search scope is merge_requests and the search type is basic' do + it 'increments the global_search SLI as a success if the elapsed time is within the target' do + expect(Gitlab::Metrics::Sli::Apdex[:global_search]).to receive(:increment).with( + labels: { + search_type: 'basic', + search_level: 'global', + search_scope: 'merge_requests', + endpoint_id: 'end' + }, + success: true + ) + + described_class.record_apdex( + elapsed: 14, + search_type: 'basic', + search_level: 'global', + search_scope: 'merge_requests' + ) + end + + it 'increments the global_search SLI as a failure if the elapsed time is not within the target' do + expect(Gitlab::Metrics::Sli::Apdex[:global_search]).to receive(:increment).with( + labels: { + search_type: 'basic', + search_level: 'global', + search_scope: 'merge_requests', + endpoint_id: 'end' + }, + success: false + ) + + described_class.record_apdex( + elapsed: 16, + search_type: 'basic', + search_level: 'global', + search_scope: 'merge_requests' + ) + end + end end describe '#record_error_rate' do diff --git a/spec/mailers/emails/service_desk_spec.rb b/spec/mailers/emails/service_desk_spec.rb index 3ed531a16bc..6653a599602 100644 --- a/spec/mailers/emails/service_desk_spec.rb +++ b/spec/mailers/emails/service_desk_spec.rb @@ -19,13 +19,10 @@ RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do let_it_be(:credential) { create(:service_desk_custom_email_credential, project: project) } let_it_be(:verification) { create(:service_desk_custom_email_verification, project: project) } let_it_be(:service_desk_setting) { create(:service_desk_setting, project: project, custom_email: 'user@example.com') } + let_it_be(:issue_email_participant) { create(:issue_email_participant, issue: issue, email: email) } let(:template) { double(content: template_content) } - before_all do - issue.issue_email_participants.create!(email: email) - end - before do # Because we use global project and custom email instances, make sure # custom email is disabled in all regular cases to avoid flakiness. @@ -50,6 +47,9 @@ RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do expect(subject.parts[1].body.to_s).to include(expected_html) expect(subject.parts[1].content_type).to include('text/html') + + # Sets issue email participant in sent notification + expect(issue.reset.sent_notifications.first.issue_email_participant).to eq(issue_email_participant) end end @@ -209,6 +209,10 @@ RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do let_it_be(:expected_html) { expected_text } + before do + issue.update!(external_author: email) + end + subject { ServiceEmailClass.service_desk_thank_you_email(issue.id) } it_behaves_like 'a service desk notification email' @@ -272,9 +276,9 @@ RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do let(:other_issue) { create(:issue, project: project, description: full_issue_reference) } let(:template_content) { '%{ISSUE_DESCRIPTION}' } - let(:expected_template_html) { "

#{full_issue_reference}

" } + let(:expected_template_html) { full_issue_reference } - subject { ServiceEmailClass.service_desk_thank_you_email(other_issue.id) } + subject(:message) { ServiceEmailClass.service_desk_thank_you_email(other_issue.id) } before do expect(Gitlab::Template::ServiceDeskTemplate).to receive(:find) @@ -285,7 +289,7 @@ RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do end it 'does not render GitLab-specific-reference links with title attribute' do - is_expected.to have_body_text(expected_template_html) + expect(message.body.to_s).to include(expected_template_html) end end end @@ -326,7 +330,7 @@ RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do let_it_be(:expected_html) { 'My note' } let_it_be(:note) { create(:note_on_issue, noteable: issue, project: project, note: expected_text) } - subject { ServiceEmailClass.service_desk_new_note_email(issue.id, note.id, email) } + subject { ServiceEmailClass.service_desk_new_note_email(issue.id, note.id, issue_email_participant) } it_behaves_like 'a service desk notification email' it_behaves_like 'read template from repository', 'new_note' @@ -466,7 +470,7 @@ RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do let_it_be(:upload_path_1) { "/uploads/#{path_1}" } let_it_be(:note) { create(:note_on_issue, noteable: issue, project: project, note: "a new comment with [#{filename}](#{upload_path}) [#{filename_1}](#{upload_path_1})") } - context 'when all uploads processed correct' do + context 'when all uploads processed correct' do # rubocop:disable RSpec/MultipleMemoizedHelpers -- Avoid duplication with heavy use of helpers before do allow_next_instance_of(FileUploader) do |instance| allow(instance).to receive(:size).and_return(5.megabytes) @@ -521,7 +525,7 @@ RSpec.describe Emails::ServiceDesk, feature_category: :service_desk do end context 'when custom email is enabled' do - subject { Notify.service_desk_new_note_email(issue.id, note.id, email) } + subject { Notify.service_desk_new_note_email(issue.id, note.id, issue_email_participant) } it_behaves_like 'a service desk notification email that uses custom email' end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 34311a8ae22..618717a61df 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -1538,9 +1538,12 @@ RSpec.describe Notify, feature_category: :code_review_workflow do end context 'for service desk issues' do + let_it_be(:issue_email_participant) do + create(:issue_email_participant, issue: issue, email: 'service.desk@example.com') + end + before do issue.update!(external_author: 'service.desk@example.com') - issue.issue_email_participants.create!(email: 'service.desk@example.com') end describe 'thank you email', feature_category: :service_desk do @@ -1615,7 +1618,7 @@ RSpec.describe Notify, feature_category: :code_review_workflow do describe 'new note email', feature_category: :service_desk do let_it_be(:first_note) { create(:discussion_note_on_issue, note: 'Hello world') } - subject { described_class.service_desk_new_note_email(issue.id, first_note.id, 'service.desk@example.com') } + subject { described_class.service_desk_new_note_email(issue.id, first_note.id, issue_email_participant) } it_behaves_like 'an unsubscribeable thread' it_behaves_like 'appearance header and footer enabled' diff --git a/spec/models/sent_notification_spec.rb b/spec/models/sent_notification_spec.rb index 5b31e8e5e3c..8da87c2349a 100644 --- a/spec/models/sent_notification_spec.rb +++ b/spec/models/sent_notification_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe SentNotification, :request_store do +RSpec.describe SentNotification, :request_store, feature_category: :shared do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project) } @@ -40,6 +40,12 @@ RSpec.describe SentNotification, :request_store do end end + describe ' associations' do + subject { build(:sent_notification) } + + it { is_expected.to belong_to(:issue_email_participant) } + end + shared_examples 'a successful sent notification' do it 'creates a new SentNotification' do expect { subject }.to change { described_class.count }.by(1) @@ -61,6 +67,20 @@ RSpec.describe SentNotification, :request_store do it_behaves_like 'a successful sent notification' it_behaves_like 'a non-sticky write' + + context 'with issue email participant' do + let!(:issue_email_participant) { create(:issue_email_participant, issue: issue) } + + subject(:sent_notification) do + described_class.record(issue, user.id, described_class.reply_key, { + issue_email_participant: issue_email_participant + }) + end + + it 'saves the issue_email_participant' do + expect(sent_notification.issue_email_participant).to eq(issue_email_participant) + end + end end describe '.record_note' do diff --git a/spec/requests/api/graphql/mutations/work_items/update_task_spec.rb b/spec/requests/api/graphql/mutations/work_items/update_task_spec.rb deleted file mode 100644 index 717de983871..00000000000 --- a/spec/requests/api/graphql/mutations/work_items/update_task_spec.rb +++ /dev/null @@ -1,85 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'Update a work item task', feature_category: :team_planning do - include GraphqlHelpers - - let_it_be(:project) { create(:project) } - let_it_be(:developer) { create(:user).tap { |user| project.add_developer(user) } } - let_it_be(:unauthorized_work_item) { create(:work_item) } - let_it_be(:referenced_work_item, refind: true) { create(:work_item, project: project, title: 'REFERENCED') } - let_it_be(:parent_work_item) do - create(:work_item, project: project, description: "- [ ] #{referenced_work_item.to_reference}+") - end - - let(:task) { referenced_work_item } - let(:work_item) { parent_work_item } - let(:task_params) { { 'title' => 'UPDATED' } } - let(:task_input) { { 'id' => task.to_global_id.to_s }.merge(task_params) } - let(:input) { { 'id' => work_item.to_global_id.to_s, 'taskData' => task_input } } - let(:mutation) { graphql_mutation(:workItemUpdateTask, input, nil, ['productAnalyticsState']) } - let(:mutation_response) { graphql_mutation_response(:work_item_update_task) } - - context 'the user is not allowed to read a work item' do - let(:current_user) { create(:user) } - - it_behaves_like 'a mutation that returns a top-level access error' - end - - context 'when user has permissions to update a work item' do - let(:current_user) { developer } - - it 'updates the work item and invalidates markdown cache on the original work item' do - expect do - post_graphql_mutation(mutation, current_user: current_user) - work_item.reload - referenced_work_item.reload - end.to change(referenced_work_item, :title).from(referenced_work_item.title).to('UPDATED') - - expect(response).to have_gitlab_http_status(:success) - expect(mutation_response).to include( - 'workItem' => hash_including( - 'title' => work_item.title, - 'descriptionHtml' => a_string_including('UPDATED') - ), - 'task' => hash_including( - 'title' => 'UPDATED' - ) - ) - end - - context 'when providing invalid task params' do - let(:task_params) { { 'title' => '' } } - - it 'makes no changes to the DB and returns an error message' do - expect do - post_graphql_mutation(mutation, current_user: current_user) - work_item.reload - task.reload - end.to not_change(task, :title).and( - not_change(work_item, :description_html) - ) - - expect(mutation_response['errors']).to contain_exactly("Title can't be blank") - end - end - - context 'when user cannot update the task' do - let(:task) { unauthorized_work_item } - - it_behaves_like 'a mutation that returns a top-level access error' - end - - it_behaves_like 'has spam protection' do - let(:mutation_class) { ::Mutations::WorkItems::UpdateTask } - end - end - - context 'when user does not have permissions to update a work item' do - let(:current_user) { developer } - let(:work_item) { unauthorized_work_item } - - it_behaves_like 'a mutation that returns a top-level access error' - end -end diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 6b949962e53..f2876ddee1b 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -991,6 +991,23 @@ RSpec.describe API::Groups, feature_category: :groups_and_projects do end end + context 'updating the `enabled_git_access_protocol` attribute' do + %w[ssh http all].each do |protocol| + context "with #{protocol}" do + subject do + put api("/groups/#{group1.id}", user1), params: { enabled_git_access_protocol: protocol } + end + + it 'updates the attribute', :aggregate_failures do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response['enabled_git_access_protocol']).to eq(protocol) + end + end + end + end + context 'malicious group name' do subject { put api("/groups/#{group1.id}", user1), params: { name: "" } } @@ -2058,6 +2075,19 @@ RSpec.describe API::Groups, feature_category: :groups_and_projects do end end + context 'when creating a group with `enabled_git_access_protocol' do + let(:params) { attributes_for_group_api enabled_git_access_protocol: 'all' } + + subject { post api("/groups", user3), params: params } + + it 'creates group with the specified Git access protocol', :aggregate_failures do + subject + + expect(response).to have_gitlab_http_status(:created) + expect(json_response['enabled_git_access_protocol']).to eq(nil) + end + end + it "does not create group, duplicate", :aggregate_failures do post api("/groups", user3), params: { name: 'Duplicate Test', path: group2.path } diff --git a/spec/requests/api/usage_data_spec.rb b/spec/requests/api/usage_data_spec.rb index 37fa75a812c..eb0192d773b 100644 --- a/spec/requests/api/usage_data_spec.rb +++ b/spec/requests/api/usage_data_spec.rb @@ -5,6 +5,64 @@ require 'spec_helper' RSpec.describe API::UsageData, feature_category: :service_ping do let_it_be(:user) { create(:user) } + describe 'GET /usage_data/service_ping' do + let(:endpoint) { '/usage_data/service_ping' } + + context 'when feature flag is disabled' do + before do + stub_feature_flags(usage_data_api: false) + end + + it 'returns not_found' do + get api(endpoint) + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'without authentication' do + it 'returns 401 response' do + get api(endpoint) + + expect(response).to have_gitlab_http_status(:unauthorized) + end + end + + context 'when authenticated as non-admin' do + let(:user) { create(:user) } + + it 'returns 403' do + get api(endpoint, user) + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + + context 'when authenticated as an admin using read_service_ping access token' do + let(:scopes) { [Gitlab::Auth::READ_SERVICE_PING_SCOPE] } + let(:personal_access_token) { create(:personal_access_token, user: user, scopes: scopes) } + + before do + allow(Ability).to receive(:allowed?).and_return(true) + end + + it 'returns 200' do + get api(endpoint, personal_access_token: personal_access_token) + + expect(response).to have_gitlab_http_status(:ok) + end + + it 'returns service ping payload' do + usage_data = { 'key' => 'value' } + allow(Rails.cache).to receive(:fetch).and_return(usage_data) + + get api(endpoint, personal_access_token: personal_access_token) + + expect(response.body).to eq(usage_data.to_json) + end + end + end + describe 'POST /usage_data/increment_counter' do let(:endpoint) { '/usage_data/increment_counter' } let(:known_event) { "diff_searches" } diff --git a/spec/services/click_house/sync_strategies/base_sync_strategy_spec.rb b/spec/services/click_house/sync_strategies/base_sync_strategy_spec.rb index eb9324fd24b..af1785a7eb7 100644 --- a/spec/services/click_house/sync_strategies/base_sync_strategy_spec.rb +++ b/spec/services/click_house/sync_strategies/base_sync_strategy_spec.rb @@ -128,7 +128,7 @@ RSpec.describe ClickHouse::SyncStrategies::BaseSyncStrategy, feature_category: : context 'when clickhouse is not configured' do before do - allow(ClickHouse::Client.configuration).to receive(:databases).and_return({}) + allow(Gitlab::ClickHouse).to receive(:configured?).and_return(false) end it 'skips execution' do @@ -138,7 +138,7 @@ RSpec.describe ClickHouse::SyncStrategies::BaseSyncStrategy, feature_category: : context 'when exclusive lease error happens' do it 'skips execution' do - allow(ClickHouse::Client.configuration).to receive(:databases).and_return({ main: :some_db }) + allow(Gitlab::ClickHouse).to receive(:configured?).and_return(true) expect(strategy).to receive(:in_lock).and_raise(Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError) expect(execute).to eq({ status: :skipped }) diff --git a/spec/services/click_house/sync_strategies/event_sync_strategy_spec.rb b/spec/services/click_house/sync_strategies/event_sync_strategy_spec.rb index 05fcf6ddeb3..80f5b7b30f7 100644 --- a/spec/services/click_house/sync_strategies/event_sync_strategy_spec.rb +++ b/spec/services/click_house/sync_strategies/event_sync_strategy_spec.rb @@ -95,7 +95,7 @@ RSpec.describe ClickHouse::SyncStrategies::EventSyncStrategy, feature_category: describe '#enabled?' do context 'when the clickhouse database is configured the feature flag is enabled' do before do - allow(ClickHouse::Client.configuration).to receive(:databases).and_return({ main: :some_db }) + allow(Gitlab::ClickHouse).to receive(:configured?).and_return(true) stub_feature_flags(event_sync_worker_for_click_house: true) end @@ -106,7 +106,7 @@ RSpec.describe ClickHouse::SyncStrategies::EventSyncStrategy, feature_category: context 'when the clickhouse database is not configured' do before do - allow(ClickHouse::Client.configuration).to receive(:databases).and_return({}) + allow(Gitlab::ClickHouse).to receive(:configured?).and_return(false) end it 'returns false' do @@ -116,7 +116,7 @@ RSpec.describe ClickHouse::SyncStrategies::EventSyncStrategy, feature_category: context 'when the feature flag is disabled' do before do - allow(ClickHouse::Client.configuration).to receive(:databases).and_return({ main: :some_db }) + allow(Gitlab::ClickHouse).to receive(:configured?).and_return(true) stub_feature_flags(event_sync_worker_for_click_house: false) end diff --git a/spec/services/namespace_settings/update_service_spec.rb b/spec/services/namespace_settings/update_service_spec.rb index 413a551ca0c..9473b958957 100644 --- a/spec/services/namespace_settings/update_service_spec.rb +++ b/spec/services/namespace_settings/update_service_spec.rb @@ -145,6 +145,7 @@ RSpec.describe NamespaceSettings::UpdateService, feature_category: :groups_and_p where(:setting_key, :setting_changes_from, :setting_changes_to) do :prevent_sharing_groups_outside_hierarchy | false | true :new_user_signups_cap | nil | 100 + :enabled_git_access_protocol | 'all' | 'ssh' end with_them do diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 15e7f794795..587e5ed25d5 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -581,7 +581,7 @@ RSpec.describe NotificationService, :mailer, feature_category: :team_planning do end describe 'Notes' do - context 'issue note' do + describe 'issue note' do let_it_be(:project) { create(:project, :private) } let_it_be_with_reload(:issue) { create(:issue, project: project, assignees: [assignee]) } let_it_be(:mentioned_issue) { create(:issue, assignees: issue.assignees) } @@ -591,26 +591,28 @@ RSpec.describe NotificationService, :mailer, feature_category: :team_planning do subject { notification.new_note(note) } - context 'issue_email_participants' do + describe 'issue_email_participants' do before do allow(Notify).to receive(:service_desk_new_note_email) - .with(Integer, Integer, String).and_return(mailer) + .with(Integer, Integer, IssueEmailParticipant).and_return(mailer) - allow(::Gitlab::Email::IncomingEmail).to receive(:enabled?) { true } - allow(::Gitlab::Email::IncomingEmail).to receive(:supports_wildcard?) { true } + allow(::Gitlab::Email::IncomingEmail).to receive(:enabled?).and_return(true) + allow(::Gitlab::Email::IncomingEmail).to receive(:supports_wildcard?).and_return(true) end - let(:subject) { described_class.new } let(:mailer) { double(deliver_later: true) } let(:issue) { create(:issue, author: Users::Internal.support_bot) } let(:project) { issue.project } let(:note) { create(:note, noteable: issue, project: project) } + subject(:notification_service) { described_class.new } + shared_examples 'notification with exact metric events' do |number_of_events| it 'adds metric event' do metric_transaction = double('Gitlab::Metrics::WebTransaction', increment: true, observe: true) allow(::Gitlab::Metrics::BackgroundTransaction).to receive(:current).and_return(metric_transaction) - expect(metric_transaction).to receive(:add_event).with(:service_desk_new_note_email).exactly(number_of_events).times + expect(metric_transaction).to receive(:add_event) + .with(:service_desk_new_note_email).exactly(number_of_events).times subject.new_note(note) end @@ -638,9 +640,9 @@ RSpec.describe NotificationService, :mailer, feature_category: :team_planning do it 'sends the email' do expect(Notify).to receive(:service_desk_new_note_email) - .with(issue.id, note.id, issue.external_author) + .with(issue.id, note.id, issue_email_participant) - subject.new_note(note) + notification_service.new_note(note) end it_behaves_like 'notification with exact metric events', 1 @@ -652,6 +654,71 @@ RSpec.describe NotificationService, :mailer, feature_category: :team_planning do it_behaves_like 'no participants are notified' end + + context 'with multiple external participants' do + let!(:other_external_participant) { issue.issue_email_participants.create!(email: 'user@example.com') } + + it 'sends emails' do + expect(Notify).to receive(:service_desk_new_note_email) + .with(issue.id, note.id, IssueEmailParticipant).twice + + notification_service.new_note(note) + end + + context 'when note is from an external participant' do + shared_examples 'only sends one Service Desk notification email' do + it 'sends one email' do + expect(Notify).not_to receive(:service_desk_new_note_email) + .with(issue.id, note.id, non_recipient) + + expect(Notify).to receive(:service_desk_new_note_email) + .with(issue.id, note.id, recipient) + + notification_service.new_note(note) + end + end + + let!(:note) do + create( + :note_on_issue, + author: Users::Internal.support_bot, + noteable: issue, + project_id: issue.project_id, + note: '@mention referenced, @unsubscribed_mentioned and @outsider also' + ) + end + + context 'and the note is from the external issue author' do + let(:non_recipient) { issue_email_participant } + let(:recipient) { other_external_participant } + let!(:note_metadata) do + create(:note_metadata, note: note, email_participant: issue_email_participant.email) + end + + it_behaves_like 'only sends one Service Desk notification email' + end + + context 'and the note is from another external participant' do + let(:non_recipient) { other_external_participant } + let(:recipient) { issue_email_participant } + let!(:note_metadata) do + create(:note_metadata, note: note, email_participant: other_external_participant.email) + end + + it_behaves_like 'only sends one Service Desk notification email' + + context 'and the external note auhor email has different format' do + let(:non_recipient) { other_external_participant } + let(:recipient) { issue_email_participant } + let!(:note_metadata) do + create(:note_metadata, note: note, email_participant: 'USER@example.com') + end + + it_behaves_like 'only sends one Service Desk notification email' + end + end + end + end end context 'do exist and note is confidential' do diff --git a/spec/services/work_items/widgets/labels_service/create_service_spec.rb b/spec/services/work_items/widgets/labels_service/create_service_spec.rb new file mode 100644 index 00000000000..70b185b9301 --- /dev/null +++ b/spec/services/work_items/widgets/labels_service/create_service_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe WorkItems::Widgets::LabelsService::CreateService, feature_category: :team_planning do + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, group: group) } + let_it_be(:label1) { create(:label, project: project) } + let_it_be(:label2) { create(:label, project: project) } + let_it_be(:label3) { create(:label, project: project) } + let_it_be(:current_user) { create(:user).tap { |user| project.add_reporter(user) } } + + let(:work_item) { create(:work_item, project: project, labels: [label1, label2]) } + let(:widget) { work_item.widgets.find { |widget| widget.is_a?(WorkItems::Widgets::Labels) } } + let(:service) { described_class.new(widget: widget, current_user: current_user) } + + describe '#prepare_create_params' do + context 'when params are set' do + let(:params) { { add_label_ids: [label1.id], label_ids: [label2.id] } } + + it "sets params correctly" do + expect(service.prepare_create_params(params: params)).to include( + { + add_label_ids: match_array([label1.id]), + label_ids: match_array([label2.id]) + } + ) + end + + context "and user doesn't have permissions to update labels" do + let_it_be(:current_user) { create(:user) } + + it 'removes label params' do + expect(service.prepare_create_params(params: params)).to be_nil + end + end + end + + context 'when widget does not exist in new type' do + let(:params) { {} } + + before do + allow(service).to receive(:new_type_excludes_widget?).and_return(true) + end + + it "sets label params as empty" do + expect(service.prepare_create_params(params: params)).to include( + { + add_label_ids: [], + label_ids: [] + } + ) + end + end + end +end diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml index 66730340f84..91d423fcd67 100644 --- a/spec/support/rspec_order_todo.yml +++ b/spec/support/rspec_order_todo.yml @@ -4340,7 +4340,6 @@ - './spec/graphql/mutations/todos/restore_many_spec.rb' - './spec/graphql/mutations/todos/restore_spec.rb' - './spec/graphql/mutations/user_callouts/create_spec.rb' -- './spec/graphql/mutations/work_items/update_task_spec.rb' - './spec/graphql/resolvers/admin/analytics/usage_trends/measurements_resolver_spec.rb' - './spec/graphql/resolvers/alert_management/alert_resolver_spec.rb' - './spec/graphql/resolvers/alert_management/alert_status_counts_resolver_spec.rb' @@ -6221,7 +6220,6 @@ - './spec/lib/gitlab/hook_data/subgroup_builder_spec.rb' - './spec/lib/gitlab/hook_data/user_builder_spec.rb' - './spec/lib/gitlab/hotlinking_detector_spec.rb' -- './spec/lib/gitlab/http_connection_adapter_spec.rb' - './spec/lib/gitlab/http_io_spec.rb' - './spec/lib/gitlab/http_spec.rb' - './spec/lib/gitlab/i18n/metadata_entry_spec.rb' @@ -7924,7 +7922,6 @@ - './spec/requests/api/graphql/mutations/work_items/create_spec.rb' - './spec/requests/api/graphql/mutations/work_items/delete_spec.rb' - './spec/requests/api/graphql/mutations/work_items/update_spec.rb' -- './spec/requests/api/graphql/mutations/work_items/update_task_spec.rb' - './spec/requests/api/graphql/namespace/package_settings_spec.rb' - './spec/requests/api/graphql/namespace/projects_spec.rb' - './spec/requests/api/graphql/namespace_query_spec.rb' diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb index c6ea665a160..ec0d1e1a976 100644 --- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb +++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb @@ -78,6 +78,7 @@ RSpec.shared_context 'GroupPolicy context' do read_billing edit_billing admin_member_access_request + update_git_access_protocol ] end diff --git a/spec/workers/click_house/event_authors_consistency_cron_worker_spec.rb b/spec/workers/click_house/event_authors_consistency_cron_worker_spec.rb index 4d7e0e138e9..a66657c639f 100644 --- a/spec/workers/click_house/event_authors_consistency_cron_worker_spec.rb +++ b/spec/workers/click_house/event_authors_consistency_cron_worker_spec.rb @@ -7,7 +7,7 @@ RSpec.describe ClickHouse::EventAuthorsConsistencyCronWorker, feature_category: context 'when ClickHouse is disabled' do it 'does nothing' do - allow(ClickHouse::Client).to receive(:database_configured?).and_return(false) + allow(Gitlab::ClickHouse).to receive(:configured?).and_return(false) expect(worker).not_to receive(:log_extra_metadata_on_done) @@ -17,7 +17,7 @@ RSpec.describe ClickHouse::EventAuthorsConsistencyCronWorker, feature_category: context 'when the event_sync_worker_for_click_house feature flag is off' do it 'does nothing' do - allow(ClickHouse::Client).to receive(:database_configured?).and_return(true) + allow(Gitlab::ClickHouse).to receive(:configured?).and_return(true) stub_feature_flags(event_sync_worker_for_click_house: false) expect(worker).not_to receive(:log_extra_metadata_on_done) diff --git a/workhorse/internal/helper/command/command.go b/workhorse/internal/helper/command/command.go index 59c8c9a3db2..67d6838b170 100644 --- a/workhorse/internal/helper/command/command.go +++ b/workhorse/internal/helper/command/command.go @@ -15,9 +15,9 @@ func ExitStatus(err error) (int, bool) { } } -func KillProcessGroup(cmd *exec.Cmd) { +func KillProcessGroup(cmd *exec.Cmd) error { if cmd == nil { - return + return nil } if p := cmd.Process; p != nil && p.Pid > 0 { @@ -26,5 +26,5 @@ func KillProcessGroup(cmd *exec.Cmd) { } // reap our child process - cmd.Wait() + return cmd.Wait() } diff --git a/workhorse/internal/helper/command/command_test.go b/workhorse/internal/helper/command/command_test.go new file mode 100644 index 00000000000..2f25f09373c --- /dev/null +++ b/workhorse/internal/helper/command/command_test.go @@ -0,0 +1,97 @@ +package command + +import ( + "errors" + "os/exec" + "testing" + + "github.com/stretchr/testify/require" +) + +type ErrorWithExitCode struct { + exitCode int +} + +func (e ErrorWithExitCode) Error() string { + return "Error that responds to ExitCode()" +} + +func (e ErrorWithExitCode) ExitCode() int { + return e.exitCode +} + +func TestExitStatus(t *testing.T) { + tests := []struct { + name string + err error + exitCode int + ok bool + }{ + { + name: "error responds to ExitCode()", + err: ErrorWithExitCode{exitCode: 0}, + exitCode: 0, + ok: true, + }, + { + name: "error is not nil", + err: errors.New("some generic error"), + exitCode: -1, + ok: false, + }, + { + name: "else", + err: nil, + exitCode: 0, + ok: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + exitCode, ok := ExitStatus(tt.err) + + require.Equal(t, tt.exitCode, exitCode) + require.Equal(t, tt.ok, ok) + }) + } +} + +func TestKillProcessGroup(t *testing.T) { + tests := []struct { + name string + cmd *exec.Cmd + start bool + err error + }{ + { + name: "command is nil", + cmd: nil, + start: false, + err: nil, + }, + { + name: "command not started", + cmd: exec.Command("sleep"), + start: false, + err: errors.New(""), + }, + { + name: "command started", + cmd: exec.Command("sleep"), + start: true, + err: &exec.ExitError{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.start == true { + tt.cmd.Start() + } + + err := KillProcessGroup(tt.cmd) + require.IsType(t, tt.err, err) + }) + } +} -- cgit v1.2.3