diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 64ee60d7eb8..323f085473c 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -11,13 +11,6 @@ Gitlab/PolicyRuleBoolean: Exclude: - 'ee/app/policies/ee/identity_provider_policy.rb' -# Offense count: 2352 -# Cop supports --auto-correct. -# Configuration parameters: Strict, EnforcedStyle, AllowedExplicitMatchers. -# SupportedStyles: inflected, explicit -RSpec/PredicateMatcher: - Enabled: false - # Offense count: 118 RSpec/RepeatedExampleGroupBody: Enabled: false diff --git a/.rubocop_todo/rspec/predicate_matcher.yml b/.rubocop_todo/rspec/predicate_matcher.yml new file mode 100644 index 00000000000..0d55c9b858e --- /dev/null +++ b/.rubocop_todo/rspec/predicate_matcher.yml @@ -0,0 +1,516 @@ +--- +# Cop supports --auto-correct. +RSpec/PredicateMatcher: + # Offense count: 2480 + # Temporarily disabled due to too many offenses + Enabled: false + Exclude: + - 'ee/spec/controllers/admin/elasticsearch_controller_spec.rb' + - 'ee/spec/controllers/admin/geo/projects_controller_spec.rb' + - 'ee/spec/controllers/ee/sent_notifications_controller_spec.rb' + - 'ee/spec/controllers/groups/group_members_controller_spec.rb' + - 'ee/spec/controllers/groups/ldaps_controller_spec.rb' + - 'ee/spec/controllers/projects_controller_spec.rb' + - 'ee/spec/elastic/migrate/migration_shared_examples.rb' + - 'ee/spec/features/admin/admin_settings_spec.rb' + - 'ee/spec/features/projects/members/member_is_removed_from_project_spec.rb' + - 'ee/spec/features/projects/mirror_spec.rb' + - 'ee/spec/features/signup_spec.rb' + - 'ee/spec/graphql/resolvers/path_locks_resolver_spec.rb' + - 'ee/spec/helpers/ee/groups_helper_spec.rb' + - 'ee/spec/helpers/ee/issues_helper_spec.rb' + - 'ee/spec/helpers/projects_helper_spec.rb' + - 'ee/spec/lib/ee/gitlab/auth/ldap/sync/group_spec.rb' + - 'ee/spec/lib/ee/gitlab/ci/pipeline/chain/validate/after_config_spec.rb' + - 'ee/spec/lib/ee/gitlab/ci/pipeline/quota/size_spec.rb' + - 'ee/spec/lib/ee/gitlab/database_spec.rb' + - 'ee/spec/lib/ee/gitlab/elastic/helper_spec.rb' + - 'ee/spec/lib/gitlab/auth/group_saml/gma_membership_enforcer_spec.rb' + - 'ee/spec/lib/gitlab/auth/group_saml/membership_enforcer_spec.rb' + - 'ee/spec/lib/gitlab/auth/ldap/access_spec.rb' + - 'ee/spec/lib/gitlab/checks/diff_check_spec.rb' + - 'ee/spec/lib/gitlab/email/handler/create_note_handler_spec.rb' + - 'ee/spec/lib/gitlab/geo/geo_node_status_check_spec.rb' + - 'ee/spec/lib/gitlab/geo/jwt_request_decoder_spec.rb' + - 'ee/spec/lib/gitlab/geo/replication/base_transfer_spec.rb' + - 'ee/spec/lib/gitlab/geo/replication/file_transfer_spec.rb' + - 'ee/spec/lib/gitlab/geo/replicator_spec.rb' + - 'ee/spec/lib/gitlab/geo_spec.rb' + - 'ee/spec/lib/gitlab/mirror_spec.rb' + - 'ee/spec/lib/gitlab/user_access_spec.rb' + - 'ee/spec/lib/system_check/geo/authorized_keys_flag_check_spec.rb' + - 'ee/spec/lib/system_check/geo/current_node_check_spec.rb' + - 'ee/spec/lib/system_check/geo/http_connection_check_spec.rb' + - 'ee/spec/models/allowed_email_domain_spec.rb' + - 'ee/spec/models/application_setting_spec.rb' + - 'ee/spec/models/approval_state_spec.rb' + - 'ee/spec/models/ci/minutes/notification_spec.rb' + - 'ee/spec/models/concerns/approval_rule_like_spec.rb' + - 'ee/spec/models/concerns/elastic/issue_spec.rb' + - 'ee/spec/models/concerns/elastic/note_spec.rb' + - 'ee/spec/models/concerns/elastic/project_spec.rb' + - 'ee/spec/models/concerns/geo/verification_state_spec.rb' + - 'ee/spec/models/dast_site_profile_spec.rb' + - 'ee/spec/models/ee/ci/runner_spec.rb' + - 'ee/spec/models/ee/group_spec.rb' + - 'ee/spec/models/ee/label_spec.rb' + - 'ee/spec/models/ee/list_spec.rb' + - 'ee/spec/models/ee/namespace_spec.rb' + - 'ee/spec/models/ee/user_spec.rb' + - 'ee/spec/models/epic_spec.rb' + - 'ee/spec/models/geo/container_repository_registry_spec.rb' + - 'ee/spec/models/geo/project_registry_spec.rb' + - 'ee/spec/models/geo_node_spec.rb' + - 'ee/spec/models/ip_restriction_spec.rb' + - 'ee/spec/models/issue_spec.rb' + - 'ee/spec/models/license_spec.rb' + - 'ee/spec/models/namespace_setting_spec.rb' + - 'ee/spec/models/note_spec.rb' + - 'ee/spec/models/path_lock_spec.rb' + - 'ee/spec/models/preloaders/environments/protected_environment_preloader_spec.rb' + - 'ee/spec/models/project_import_state_spec.rb' + - 'ee/spec/models/project_spec.rb' + - 'ee/spec/models/saml_provider_spec.rb' + - 'ee/spec/models/security/orchestration_policy_configuration_spec.rb' + - 'ee/spec/presenters/ci/minutes/quota_presenter_spec.rb' + - 'ee/spec/requests/api/boards_spec.rb' + - 'ee/spec/requests/api/graphql/mutations/epics/set_subscription_spec.rb' + - 'ee/spec/requests/api/groups_spec.rb' + - 'ee/spec/requests/api/members_spec.rb' + - 'ee/spec/requests/api/projects_spec.rb' + - 'ee/spec/services/approval_rules/params_filtering_service_spec.rb' + - 'ee/spec/services/audit_event_service_spec.rb' + - 'ee/spec/services/audit_events/register_runner_audit_event_service_spec.rb' + - 'ee/spec/services/ci/process_build_service_spec.rb' + - 'ee/spec/services/ci/runners/register_runner_service_spec.rb' + - 'ee/spec/services/ee/allowed_email_domains/update_service_spec.rb' + - 'ee/spec/services/ee/ip_restrictions/update_service_spec.rb' + - 'ee/spec/services/ee/issuable/bulk_update_service_spec.rb' + - 'ee/spec/services/geo/container_repository_sync_service_spec.rb' + - 'ee/spec/services/geo/event_service_spec.rb' + - 'ee/spec/services/geo/files_expire_service_spec.rb' + - 'ee/spec/services/geo/hashed_storage_attachments_migration_service_spec.rb' + - 'ee/spec/services/geo/move_repository_service_spec.rb' + - 'ee/spec/services/geo/repository_destroy_service_spec.rb' + - 'ee/spec/services/groups/mark_for_deletion_service_spec.rb' + - 'ee/spec/services/groups/restore_service_spec.rb' + - 'ee/spec/services/iterations/cadences/create_service_spec.rb' + - 'ee/spec/services/iterations/create_service_spec.rb' + - 'ee/spec/services/jira/requests/issues/list_service_spec.rb' + - 'ee/spec/services/milestones/promote_service_spec.rb' + - 'ee/spec/services/protected_environments/create_service_spec.rb' + - 'ee/spec/services/vulnerabilities/manually_create_service_spec.rb' + - 'ee/spec/services/vulnerability_exports/export_service_spec.rb' + - 'ee/spec/support/shared_examples/graphql/mutations/dast_on_demand_scans_shared_examples.rb' + - 'ee/spec/support/shared_examples/lib/gitlab/geo/geo_log_cursor_event_shared_examples.rb' + - 'ee/spec/support/shared_examples/models/concerns/elastic/limited_indexing_shared_examples.rb' + - 'ee/spec/support/shared_examples/models/concerns/verifiable_replicator_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/tasks/geo_rake_spec.rb' + - 'ee/spec/workers/concerns/elastic/indexing_control_spec.rb' + - 'ee/spec/workers/elastic/migration_worker_spec.rb' + - 'ee/spec/workers/geo/batch/project_registry_worker_spec.rb' + - 'qa/qa/specs/features/browser_ui/1_manage/project/create_project_badge_spec.rb' + - 'qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/12_geo/database_delete_replication_spec.rb' + - 'qa/qa/specs/features/ee/browser_ui/2_plan/epic/roadmap_spec.rb' + - 'qa/spec/runtime/env_spec.rb' + - 'qa/spec/runtime/feature_spec.rb' + - 'qa/spec/specs/helpers/context_selector_spec.rb' + - 'spec/components/diffs/overflow_warning_component_spec.rb' + - 'spec/controllers/admin/dev_ops_report_controller_spec.rb' + - 'spec/controllers/admin/topics/avatars_controller_spec.rb' + - 'spec/controllers/admin/users_controller_spec.rb' + - 'spec/controllers/application_controller_spec.rb' + - 'spec/controllers/concerns/checks_collaboration_spec.rb' + - 'spec/controllers/groups/avatars_controller_spec.rb' + - 'spec/controllers/groups/clusters_controller_spec.rb' + - 'spec/controllers/groups/group_members_controller_spec.rb' + - 'spec/controllers/groups/settings/applications_controller_spec.rb' + - 'spec/controllers/omniauth_callbacks_controller_spec.rb' + - 'spec/controllers/profiles/avatars_controller_spec.rb' + - 'spec/controllers/profiles_controller_spec.rb' + - 'spec/controllers/projects/avatars_controller_spec.rb' + - 'spec/controllers/projects/clusters_controller_spec.rb' + - 'spec/controllers/projects/issues_controller_spec.rb' + - 'spec/controllers/projects/jobs_controller_spec.rb' + - 'spec/controllers/projects/merge_requests_controller_spec.rb' + - 'spec/controllers/projects/pipelines_controller_spec.rb' + - 'spec/controllers/projects/project_members_controller_spec.rb' + - 'spec/controllers/projects_controller_spec.rb' + - 'spec/controllers/sent_notifications_controller_spec.rb' + - 'spec/controllers/sessions_controller_spec.rb' + - 'spec/controllers/snippets/notes_controller_spec.rb' + - 'spec/features/admin/admin_settings_spec.rb' + - 'spec/features/admin/users/user_spec.rb' + - 'spec/features/admin/users/users_spec.rb' + - 'spec/features/groups/members/request_access_spec.rb' + - 'spec/features/groups/share_lock_spec.rb' + - 'spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb' + - 'spec/features/password_reset_spec.rb' + - 'spec/features/profile_spec.rb' + - 'spec/features/profiles/emails_spec.rb' + - 'spec/features/projects/integrations/user_activates_mattermost_slash_command_spec.rb' + - 'spec/features/projects/jobs/user_browses_job_spec.rb' + - 'spec/features/projects/members/member_leaves_project_spec.rb' + - 'spec/features/projects/members/user_requests_access_spec.rb' + - 'spec/features/projects/pages/user_edits_settings_spec.rb' + - 'spec/features/projects/settings/forked_project_settings_spec.rb' + - 'spec/features/unsubscribe_links_spec.rb' + - 'spec/features/users/signup_spec.rb' + - 'spec/finders/group_descendants_finder_spec.rb' + - 'spec/finders/issues_finder_spec.rb' + - 'spec/finders/merge_request_target_project_finder_spec.rb' + - 'spec/helpers/application_helper_spec.rb' + - 'spec/helpers/application_settings_helper_spec.rb' + - 'spec/helpers/auth_helper_spec.rb' + - 'spec/helpers/blob_helper_spec.rb' + - 'spec/helpers/clusters_helper_spec.rb' + - 'spec/helpers/groups_helper_spec.rb' + - 'spec/helpers/issues_helper_spec.rb' + - 'spec/helpers/projects_helper_spec.rb' + - 'spec/helpers/recaptcha_helper_spec.rb' + - 'spec/helpers/sessions_helper_spec.rb' + - 'spec/lib/backup/files_spec.rb' + - 'spec/lib/bitbucket/connection_spec.rb' + - 'spec/lib/bitbucket/page_spec.rb' + - 'spec/lib/bitbucket/representation/pull_request_comment_spec.rb' + - 'spec/lib/bitbucket/representation/repo_spec.rb' + - 'spec/lib/bitbucket_server/page_spec.rb' + - 'spec/lib/bitbucket_server/paginator_spec.rb' + - 'spec/lib/bitbucket_server/representation/activity_spec.rb' + - 'spec/lib/bitbucket_server/representation/pull_request_comment_spec.rb' + - 'spec/lib/bitbucket_server/representation/pull_request_spec.rb' + - 'spec/lib/constraints/group_url_constrainer_spec.rb' + - 'spec/lib/constraints/project_url_constrainer_spec.rb' + - 'spec/lib/constraints/user_url_constrainer_spec.rb' + - 'spec/lib/feature_spec.rb' + - 'spec/lib/gitlab/auth/ip_rate_limiter_spec.rb' + - 'spec/lib/gitlab/auth/ldap/access_spec.rb' + - 'spec/lib/gitlab/auth/ldap/config_spec.rb' + - 'spec/lib/gitlab/auth/ldap/user_spec.rb' + - 'spec/lib/gitlab/auth/o_auth/provider_spec.rb' + - 'spec/lib/gitlab/auth/o_auth/user_spec.rb' + - 'spec/lib/gitlab/auth/result_spec.rb' + - 'spec/lib/gitlab/auth/two_factor_auth_verifier_spec.rb' + - 'spec/lib/gitlab/auth_spec.rb' + - 'spec/lib/gitlab/authorized_keys_spec.rb' + - 'spec/lib/gitlab/background_migration/backfill_jira_tracker_deployment_type2_spec.rb' + - 'spec/lib/gitlab/background_migration/legacy_upload_mover_spec.rb' + - 'spec/lib/gitlab/background_migration/legacy_uploads_migrator_spec.rb' + - 'spec/lib/gitlab/background_migration/merge_topics_with_same_name_spec.rb' + - 'spec/lib/gitlab/blob_helper_spec.rb' + - 'spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb' + - 'spec/lib/gitlab/checks/lfs_integrity_spec.rb' + - 'spec/lib/gitlab/ci/ansi2json/line_spec.rb' + - 'spec/lib/gitlab/ci/ansi2json/parser_spec.rb' + - 'spec/lib/gitlab/ci/config/entry/job_spec.rb' + - 'spec/lib/gitlab/ci/lint_spec.rb' + - 'spec/lib/gitlab/ci/matching/build_matcher_spec.rb' + - 'spec/lib/gitlab/ci/matching/runner_matcher_spec.rb' + - 'spec/lib/gitlab/ci/parsers/test/junit_spec.rb' + - 'spec/lib/gitlab/ci/pipeline/chain/limit/rate_limit_spec.rb' + - 'spec/lib/gitlab/ci/pipeline/chain/pipeline/process_spec.rb' + - 'spec/lib/gitlab/ci/reports/test_case_spec.rb' + - 'spec/lib/gitlab/ci/status/build/failed_spec.rb' + - 'spec/lib/gitlab/ci/status/build/waiting_for_approval_spec.rb' + - 'spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb' + - 'spec/lib/gitlab/ci/trace/archive_spec.rb' + - 'spec/lib/gitlab/ci/trace_spec.rb' + - 'spec/lib/gitlab/ci_access_spec.rb' + - 'spec/lib/gitlab/cleanup/orphan_job_artifact_files_batch_spec.rb' + - 'spec/lib/gitlab/cleanup/orphan_lfs_file_references_spec.rb' + - 'spec/lib/gitlab/cleanup/project_uploads_spec.rb' + - 'spec/lib/gitlab/content_security_policy/config_loader_spec.rb' + - 'spec/lib/gitlab/cross_project_access/check_collection_spec.rb' + - 'spec/lib/gitlab/cross_project_access/check_info_spec.rb' + - 'spec/lib/gitlab/current_settings_spec.rb' + - 'spec/lib/gitlab/database/load_balancing/sidekiq_server_middleware_spec.rb' + - 'spec/lib/gitlab/database/migration_helpers_spec.rb' + - 'spec/lib/gitlab/database/migration_spec.rb' + - 'spec/lib/gitlab/database/migrations/runner_spec.rb' + - 'spec/lib/gitlab/database/partitioning/time_partition_spec.rb' + - 'spec/lib/gitlab/database/postgresql_adapter/force_disconnectable_mixin_spec.rb' + - 'spec/lib/gitlab/database/reflection_spec.rb' + - 'spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb' + - 'spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb' + - 'spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb' + - 'spec/lib/gitlab/dependency_linker/cargo_toml_linker_spec.rb' + - 'spec/lib/gitlab/dependency_linker/cartfile_linker_spec.rb' + - 'spec/lib/gitlab/dependency_linker/composer_json_linker_spec.rb' + - 'spec/lib/gitlab/dependency_linker/gemfile_linker_spec.rb' + - 'spec/lib/gitlab/dependency_linker/gemspec_linker_spec.rb' + - 'spec/lib/gitlab/dependency_linker/go_mod_linker_spec.rb' + - 'spec/lib/gitlab/dependency_linker/go_sum_linker_spec.rb' + - 'spec/lib/gitlab/dependency_linker/godeps_json_linker_spec.rb' + - 'spec/lib/gitlab/dependency_linker/package_json_linker_spec.rb' + - 'spec/lib/gitlab/dependency_linker/podfile_linker_spec.rb' + - 'spec/lib/gitlab/dependency_linker/podspec_json_linker_spec.rb' + - 'spec/lib/gitlab/dependency_linker/podspec_linker_spec.rb' + - 'spec/lib/gitlab/dependency_linker/requirements_txt_linker_spec.rb' + - 'spec/lib/gitlab/deploy_key_access_spec.rb' + - 'spec/lib/gitlab/diff/custom_diff_spec.rb' + - 'spec/lib/gitlab/diff/file_spec.rb' + - 'spec/lib/gitlab/diff/position_spec.rb' + - 'spec/lib/gitlab/diff/rendered/notebook/diff_file_spec.rb' + - 'spec/lib/gitlab/email/handler/create_issue_handler_spec.rb' + - 'spec/lib/gitlab/email/handler/create_merge_request_handler_spec.rb' + - 'spec/lib/gitlab/email/handler/create_note_handler_spec.rb' + - 'spec/lib/gitlab/email/handler/service_desk_handler_spec.rb' + - 'spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb' + - 'spec/lib/gitlab/experimentation/group_types_spec.rb' + - 'spec/lib/gitlab/external_authorization_spec.rb' + - 'spec/lib/gitlab/fake_application_settings_spec.rb' + - 'spec/lib/gitlab/git/blob_spec.rb' + - 'spec/lib/gitlab/git/branch_spec.rb' + - 'spec/lib/gitlab/git/commit_spec.rb' + - 'spec/lib/gitlab/git/keep_around_spec.rb' + - 'spec/lib/gitlab/git/repository_spec.rb' + - 'spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb' + - 'spec/lib/gitlab/git/tag_spec.rb' + - 'spec/lib/gitlab/git/tree_spec.rb' + - 'spec/lib/gitlab/git_access_snippet_spec.rb' + - 'spec/lib/gitlab/git_post_receive_spec.rb' + - 'spec/lib/gitlab/gitaly_client/repository_service_spec.rb' + - 'spec/lib/gitlab/gitaly_client/storage_settings_spec.rb' + - 'spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb' + - 'spec/lib/gitlab/gl_repository/repo_type_spec.rb' + - 'spec/lib/gitlab/gpg/commit_spec.rb' + - 'spec/lib/gitlab/hashed_storage/migrator_spec.rb' + - 'spec/lib/gitlab/i18n/translation_entry_spec.rb' + - 'spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb' + - 'spec/lib/gitlab/import_export/fork_spec.rb' + - 'spec/lib/gitlab/import_export/group/tree_restorer_spec.rb' + - 'spec/lib/gitlab/import_export/snippet_repo_restorer_spec.rb' + - 'spec/lib/gitlab/import_export/snippet_repo_saver_spec.rb' + - 'spec/lib/gitlab/import_export/snippets_repo_saver_spec.rb' + - 'spec/lib/gitlab/kubernetes/deployment_spec.rb' + - 'spec/lib/gitlab/kubernetes/kube_client_spec.rb' + - 'spec/lib/gitlab/kubernetes/namespace_spec.rb' + - 'spec/lib/gitlab/legacy_github_import/pull_request_formatter_spec.rb' + - 'spec/lib/gitlab/mail_room/mail_room_spec.rb' + - 'spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb' + - 'spec/lib/gitlab/markdown_cache/field_data_spec.rb' + - 'spec/lib/gitlab/markup_helper_spec.rb' + - 'spec/lib/gitlab/metrics/prometheus_spec.rb' + - 'spec/lib/gitlab/null_request_store_spec.rb' + - 'spec/lib/gitlab/pagination/cursor_based_keyset_spec.rb' + - 'spec/lib/gitlab/pagination/keyset_spec.rb' + - 'spec/lib/gitlab/performance_bar_spec.rb' + - 'spec/lib/gitlab/project_transfer_spec.rb' + - 'spec/lib/gitlab/reference_extractor_spec.rb' + - 'spec/lib/gitlab/request_forgery_protection_spec.rb' + - 'spec/lib/gitlab/sanitizers/svg_spec.rb' + - 'spec/lib/gitlab/search/found_blob_spec.rb' + - 'spec/lib/gitlab/search/found_wiki_page_spec.rb' + - 'spec/lib/gitlab/service_desk_email_spec.rb' + - 'spec/lib/gitlab/shard_health_cache_spec.rb' + - 'spec/lib/gitlab/uploads_transfer_spec.rb' + - 'spec/lib/gitlab/usage/metric_definition_spec.rb' + - 'spec/lib/gitlab/usage/service_ping/legacy_metric_timing_decorator_spec.rb' + - 'spec/lib/gitlab/user_access_snippet_spec.rb' + - 'spec/lib/gitlab/user_access_spec.rb' + - 'spec/lib/gitlab/utils/sanitize_node_link_spec.rb' + - 'spec/lib/gitlab/view/presenter/base_spec.rb' + - 'spec/lib/gitlab/visibility_level_spec.rb' + - 'spec/lib/object_storage/direct_upload_spec.rb' + - 'spec/lib/sidebars/projects/menus/external_issue_tracker_menu_spec.rb' + - 'spec/lib/sidebars/projects/menus/external_wiki_menu_spec.rb' + - 'spec/lib/sidebars/projects/menus/shimo_menu_spec.rb' + - 'spec/lib/system_check/app/hashed_storage_all_projects_check_spec.rb' + - 'spec/lib/system_check/app/hashed_storage_enabled_check_spec.rb' + - 'spec/migrations/20210713042000_fix_ci_sources_pipelines_index_names_spec.rb' + - 'spec/migrations/20210907211557_finalize_ci_builds_bigint_conversion_spec.rb' + - 'spec/migrations/20220128155814_fix_approval_rules_code_owners_rule_type_index_spec.rb' + - 'spec/migrations/20220505174658_update_index_on_alerts_to_exclude_null_fingerprints_spec.rb' + - 'spec/models/blob_spec.rb' + - 'spec/models/blob_viewer/base_spec.rb' + - 'spec/models/ci/build_spec.rb' + - 'spec/models/ci/build_trace_chunk_spec.rb' + - 'spec/models/ci/job_artifact_spec.rb' + - 'spec/models/ci/pipeline_spec.rb' + - 'spec/models/ci/processable_spec.rb' + - 'spec/models/ci/runner_spec.rb' + - 'spec/models/clusters/agent_spec.rb' + - 'spec/models/commit_spec.rb' + - 'spec/models/concerns/access_requestable_spec.rb' + - 'spec/models/concerns/awardable_spec.rb' + - 'spec/models/concerns/chronic_duration_attribute_spec.rb' + - 'spec/models/concerns/ci/has_deployment_name_spec.rb' + - 'spec/models/concerns/featurable_spec.rb' + - 'spec/models/concerns/ignorable_columns_spec.rb' + - 'spec/models/concerns/integrations/has_data_fields_spec.rb' + - 'spec/models/concerns/issuable_spec.rb' + - 'spec/models/concerns/mentionable_spec.rb' + - 'spec/models/concerns/milestoneable_spec.rb' + - 'spec/models/concerns/resolvable_discussion_spec.rb' + - 'spec/models/concerns/resolvable_note_spec.rb' + - 'spec/models/concerns/routable_spec.rb' + - 'spec/models/concerns/spammable_spec.rb' + - 'spec/models/concerns/subscribable_spec.rb' + - 'spec/models/container_repository_spec.rb' + - 'spec/models/customer_relations/contact_spec.rb' + - 'spec/models/deploy_token_spec.rb' + - 'spec/models/diff_note_spec.rb' + - 'spec/models/diff_viewer/base_spec.rb' + - 'spec/models/email_spec.rb' + - 'spec/models/event_spec.rb' + - 'spec/models/gpg_key_spec.rb' + - 'spec/models/group_spec.rb' + - 'spec/models/hooks/web_hook_log_spec.rb' + - 'spec/models/identity_spec.rb' + - 'spec/models/integration_spec.rb' + - 'spec/models/integrations/base_issue_tracker_spec.rb' + - 'spec/models/integrations/base_third_party_wiki_spec.rb' + - 'spec/models/integrations/jira_spec.rb' + - 'spec/models/issue_spec.rb' + - 'spec/models/members/project_member_spec.rb' + - 'spec/models/merge_request_diff_spec.rb' + - 'spec/models/merge_request_spec.rb' + - 'spec/models/milestone_spec.rb' + - 'spec/models/namespace/aggregation_schedule_spec.rb' + - 'spec/models/namespace_setting_spec.rb' + - 'spec/models/namespace_spec.rb' + - 'spec/models/note_spec.rb' + - 'spec/models/postgresql/replication_slot_spec.rb' + - 'spec/models/project_feature_spec.rb' + - 'spec/models/project_spec.rb' + - 'spec/models/project_statistics_spec.rb' + - 'spec/models/project_team_spec.rb' + - 'spec/models/remote_mirror_spec.rb' + - 'spec/models/repository_spec.rb' + - 'spec/models/route_spec.rb' + - 'spec/models/sent_notification_spec.rb' + - 'spec/models/snippet_spec.rb' + - 'spec/models/todo_spec.rb' + - 'spec/models/upload_spec.rb' + - 'spec/models/uploads/local_spec.rb' + - 'spec/models/user_agent_detail_spec.rb' + - 'spec/models/user_spec.rb' + - 'spec/models/wiki_page_spec.rb' + - 'spec/policies/project_policy_spec.rb' + - 'spec/presenters/blob_presenter_spec.rb' + - 'spec/presenters/ci/build_presenter_spec.rb' + - 'spec/presenters/label_presenter_spec.rb' + - 'spec/requests/api/admin/instance_clusters_spec.rb' + - 'spec/requests/api/ci/jobs_spec.rb' + - 'spec/requests/api/ci/runner/jobs_request_post_spec.rb' + - 'spec/requests/api/ci/runners_spec.rb' + - 'spec/requests/api/features_spec.rb' + - 'spec/requests/api/group_clusters_spec.rb' + - 'spec/requests/api/integrations_spec.rb' + - 'spec/requests/api/internal/base_spec.rb' + - 'spec/requests/api/merge_requests_spec.rb' + - 'spec/requests/api/project_clusters_spec.rb' + - 'spec/requests/api/project_export_spec.rb' + - 'spec/requests/api/project_hooks_spec.rb' + - 'spec/requests/api/project_snippets_spec.rb' + - 'spec/requests/api/projects_spec.rb' + - 'spec/requests/api/resource_access_tokens_spec.rb' + - 'spec/requests/api/snippets_spec.rb' + - 'spec/requests/api/users_spec.rb' + - 'spec/requests/git_http_spec.rb' + - 'spec/requests/lfs_http_spec.rb' + - 'spec/serializers/pipeline_serializer_spec.rb' + - 'spec/services/branches/create_service_spec.rb' + - 'spec/services/ci/create_pipeline_service/needs_spec.rb' + - 'spec/services/ci/create_pipeline_service/rate_limit_spec.rb' + - 'spec/services/ci/delete_objects_service_spec.rb' + - 'spec/services/ci/destroy_pipeline_service_spec.rb' + - 'spec/services/ci/expire_pipeline_cache_service_spec.rb' + - 'spec/services/ci/job_artifacts/destroy_all_expired_service_spec.rb' + - 'spec/services/ci/job_artifacts/expire_project_build_artifacts_service_spec.rb' + - 'spec/services/ci/runners/register_runner_service_spec.rb' + - 'spec/services/clusters/destroy_service_spec.rb' + - 'spec/services/concerns/exclusive_lease_guard_spec.rb' + - 'spec/services/concerns/merge_requests/assigns_merge_params_spec.rb' + - 'spec/services/container_expiration_policies/cleanup_service_spec.rb' + - 'spec/services/container_expiration_policies/update_service_spec.rb' + - 'spec/services/customer_relations/contacts/update_service_spec.rb' + - 'spec/services/customer_relations/organizations/update_service_spec.rb' + - 'spec/services/deployments/older_deployments_drop_service_spec.rb' + - 'spec/services/draft_notes/publish_service_spec.rb' + - 'spec/services/environments/schedule_to_delete_review_apps_service_spec.rb' + - 'spec/services/groups/destroy_service_spec.rb' + - 'spec/services/groups/group_links/create_service_spec.rb' + - 'spec/services/groups/transfer_service_spec.rb' + - 'spec/services/groups/update_service_spec.rb' + - 'spec/services/issuable/bulk_update_service_spec.rb' + - 'spec/services/jira/requests/projects/list_service_spec.rb' + - 'spec/services/jira_import/users_importer_spec.rb' + - 'spec/services/merge_requests/build_service_spec.rb' + - 'spec/services/merge_requests/conflicts/list_service_spec.rb' + - 'spec/services/merge_requests/create_from_issue_service_spec.rb' + - 'spec/services/merge_requests/merge_service_spec.rb' + - 'spec/services/merge_requests/refresh_service_spec.rb' + - 'spec/services/metrics/dashboard/cluster_metrics_embed_service_spec.rb' + - 'spec/services/metrics/dashboard/panel_preview_service_spec.rb' + - 'spec/services/metrics/users_starred_dashboards/delete_service_spec.rb' + - 'spec/services/milestones/promote_service_spec.rb' + - 'spec/services/milestones/transfer_service_spec.rb' + - 'spec/services/namespaces/package_settings/update_service_spec.rb' + - 'spec/services/note_summary_spec.rb' + - 'spec/services/notes/build_service_spec.rb' + - 'spec/services/notes/quick_actions_service_spec.rb' + - 'spec/services/packages/debian/find_or_create_incoming_service_spec.rb' + - 'spec/services/packages/nuget/update_package_from_metadata_service_spec.rb' + - 'spec/services/projects/after_rename_service_spec.rb' + - 'spec/services/projects/cleanup_service_spec.rb' + - 'spec/services/projects/create_service_spec.rb' + - 'spec/services/projects/destroy_rollback_service_spec.rb' + - 'spec/services/projects/destroy_service_spec.rb' + - 'spec/services/projects/fork_service_spec.rb' + - 'spec/services/projects/hashed_storage/base_attachment_service_spec.rb' + - 'spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb' + - 'spec/services/projects/hashed_storage/migrate_repository_service_spec.rb' + - 'spec/services/projects/hashed_storage/rollback_attachments_service_spec.rb' + - 'spec/services/projects/hashed_storage/rollback_repository_service_spec.rb' + - 'spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb' + - 'spec/services/projects/lfs_pointers/lfs_object_download_list_service_spec.rb' + - 'spec/services/projects/update_pages_service_spec.rb' + - 'spec/services/projects/update_service_spec.rb' + - 'spec/services/releases/create_service_spec.rb' + - 'spec/services/repositories/destroy_rollback_service_spec.rb' + - 'spec/services/repositories/destroy_service_spec.rb' + - 'spec/services/repository_archive_clean_up_service_spec.rb' + - 'spec/services/resource_access_tokens/revoke_service_spec.rb' + - 'spec/services/snippets/create_service_spec.rb' + - 'spec/services/snippets/destroy_service_spec.rb' + - 'spec/services/snippets/update_service_spec.rb' + - 'spec/services/spam/akismet_service_spec.rb' + - 'spec/services/system_notes/issuables_service_spec.rb' + - 'spec/services/todo_service_spec.rb' + - 'spec/services/users/destroy_service_spec.rb' + - 'spec/support/shared_contexts/email_shared_context.rb' + - 'spec/support/shared_examples/ci/auto_merge_merge_requests_shared_examples.rb' + - 'spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb' + - 'spec/support/shared_examples/models/application_setting_shared_examples.rb' + - 'spec/support/shared_examples/models/cluster_application_core_shared_examples.rb' + - 'spec/support/shared_examples/models/concerns/timebox_shared_examples.rb' + - 'spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb' + - 'spec/support/shared_examples/models/member_shared_examples.rb' + - 'spec/support/shared_examples/models/note_access_check_shared_examples.rb' + - 'spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb' + - 'spec/support/shared_examples/uploaders/object_storage_shared_examples.rb' + - 'spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb' + - 'spec/tasks/gitlab/backup_rake_spec.rb' + - 'spec/tasks/gitlab/cleanup_rake_spec.rb' + - 'spec/uploaders/object_storage_spec.rb' + - 'spec/validators/any_field_validator_spec.rb' + - 'spec/validators/array_members_validator_spec.rb' + - 'spec/validators/cron_validator_spec.rb' + - 'spec/validators/namespace_path_validator_spec.rb' + - 'spec/validators/project_path_validator_spec.rb' + - 'spec/workers/bulk_imports/entity_worker_spec.rb' + - 'spec/workers/bulk_imports/pipeline_worker_spec.rb' + - 'spec/workers/ci/delete_objects_worker_spec.rb' + - 'spec/workers/concerns/worker_attributes_spec.rb' + - 'spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb' + - 'spec/workers/expire_build_instance_artifacts_worker_spec.rb' + - 'spec/workers/group_destroy_worker_spec.rb' + - 'spec/workers/hashed_storage/migrator_worker_spec.rb' + - 'spec/workers/hashed_storage/rollbacker_worker_spec.rb' + - 'spec/workers/project_destroy_worker_spec.rb' + - 'spec/workers/remote_mirror_notification_worker_spec.rb' + - 'spec/workers/x509_issuer_crl_check_worker_spec.rb' diff --git a/app/assets/javascripts/tracking/tracker.js b/app/assets/javascripts/tracking/tracker.js new file mode 100644 index 00000000000..9ad86e76b6e --- /dev/null +++ b/app/assets/javascripts/tracking/tracker.js @@ -0,0 +1,267 @@ +import { LOAD_ACTION_ATTR_SELECTOR } from './constants'; +import { dispatchSnowplowEvent } from './dispatch_snowplow_event'; +import getStandardContext from './get_standard_context'; +import { + getEventHandlers, + createEventPayload, + renameKey, + getReferrersCache, + addReferrersCacheEntry, +} from './utils'; + +export const Tracker = { + nonInitializedQueue: [], + initialized: false, + definitionsLoaded: false, + definitionsManifest: {}, + definitionsEventsQueue: [], + definitions: [], + ALLOWED_URL_HASHES: ['#diff', '#note'], + /** + * (Legacy) Determines if tracking is enabled at the user level. + * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/DNT. + * + * @returns {Boolean} + */ + trackable() { + return !['1', 'yes'].includes( + window.doNotTrack || navigator.doNotTrack || navigator.msDoNotTrack, + ); + }, + + /** + * Determines if Snowplow is available/enabled. + * + * @returns {Boolean} + */ + enabled() { + return typeof window.snowplow === 'function' && Tracker.trackable(); + }, + + /** + * Dispatches a structured event per our taxonomy: + * https://docs.gitlab.com/ee/development/snowplow/index.html#structured-event-taxonomy. + * + * If the library is not initialized and events are trying to be + * dispatched (data-attributes, load-events), they will be added + * to a queue to be flushed afterwards. + * + * If there is an error when using the library, it will return ´false´ + * and ´true´ otherwise. + * + * @param {...any} eventData defined event taxonomy + * @returns {Boolean} + */ + event(...eventData) { + if (!Tracker.enabled()) { + return false; + } + + if (!Tracker.initialized) { + Tracker.nonInitializedQueue.push(eventData); + return false; + } + + return dispatchSnowplowEvent(...eventData); + }, + + /** + * Preloads event definitions. + * + * @returns {undefined} + */ + loadDefinitions() { + // TODO: fetch definitions from the server and flush the queue + // See https://gitlab.com/gitlab-org/gitlab/-/issues/358256 + Tracker.definitionsLoaded = true; + + while (Tracker.definitionsEventsQueue.length) { + Tracker.dispatchFromDefinition(...Tracker.definitionsEventsQueue.shift()); + } + }, + + /** + * Dispatches a structured event with data from its event definition. + * + * @param {String} basename + * @param {Object} eventData + * @returns {Boolean} + */ + definition(basename, eventData = {}) { + if (!Tracker.enabled()) { + return false; + } + + if (!(basename in Tracker.definitionsManifest)) { + throw new Error(`Missing Snowplow event definition "${basename}"`); + } + + return Tracker.dispatchFromDefinition(basename, eventData); + }, + + /** + * Builds an event with data from a valid definition and sends it to + * Snowplow. If the definitions are not loaded, it pushes the data to a queue. + * + * @param {String} basename + * @param {Object} eventData + * @returns {Boolean} + */ + dispatchFromDefinition(basename, eventData) { + if (!Tracker.definitionsLoaded) { + Tracker.definitionsEventsQueue.push([basename, eventData]); + + return false; + } + + const eventDefinition = Tracker.definitions.find((definition) => definition.key === basename); + + return Tracker.event( + eventData.category ?? eventDefinition.category, + eventData.action ?? eventDefinition.action, + eventData, + ); + }, + + /** + * Dispatches any event emitted before initialization. + * + * @returns {undefined} + */ + flushPendingEvents() { + Tracker.initialized = true; + + while (Tracker.nonInitializedQueue.length) { + dispatchSnowplowEvent(...Tracker.nonInitializedQueue.shift()); + } + }, + + /** + * Attaches event handlers for data-attributes powered events. + * + * @param {String} category - the default category for all events + * @param {HTMLElement} parent - element containing data-attributes + * @returns {Array} + */ + bindDocument(category = document.body.dataset.page, parent = document) { + if (!Tracker.enabled() || parent.trackingBound) { + return []; + } + + // eslint-disable-next-line no-param-reassign + parent.trackingBound = true; + + const handlers = getEventHandlers(category, (...args) => Tracker.event(...args)); + handlers.forEach((event) => parent.addEventListener(event.name, event.func)); + + return handlers; + }, + + /** + * Attaches event handlers for load-events (on render). + * + * @param {String} category - the default category for all events + * @param {HTMLElement} parent - element containing event targets + * @returns {Array} + */ + trackLoadEvents(category = document.body.dataset.page, parent = document) { + if (!Tracker.enabled()) { + return []; + } + + const loadEvents = parent.querySelectorAll(LOAD_ACTION_ATTR_SELECTOR); + + loadEvents.forEach((element) => { + const { action, data } = createEventPayload(element); + Tracker.event(category, action, data); + }); + + return loadEvents; + }, + + /** + * Enable Snowplow automatic form tracking. + * The config param requires at least one array of either forms + * class names, or field name attributes. + * https://docs.gitlab.com/ee/development/snowplow/index.html#form-tracking. + * + * @param {Object} config + * @param {Array} contexts + * @returns {undefined} + */ + enableFormTracking(config, contexts = []) { + if (!Tracker.enabled()) { + return; + } + + if (!Array.isArray(config?.forms?.allow) && !Array.isArray(config?.fields?.allow)) { + // eslint-disable-next-line @gitlab/require-i18n-strings + throw new Error('Unable to enable form event tracking without allow rules.'); + } + + // Ignore default/standard schema + const standardContext = getStandardContext(); + const userProvidedContexts = contexts.filter( + (context) => context.schema !== standardContext.schema, + ); + + const mappedConfig = {}; + if (config.forms) { + mappedConfig.forms = renameKey(config.forms, 'allow', 'whitelist'); + } + + if (config.fields) { + mappedConfig.fields = renameKey(config.fields, 'allow', 'whitelist'); + } + + const enabler = () => window.snowplow('enableFormTracking', mappedConfig, userProvidedContexts); + + if (document.readyState === 'complete') { + enabler(); + } else { + document.addEventListener('readystatechange', () => { + if (document.readyState === 'complete') { + enabler(); + } + }); + } + }, + + /** + * Replaces the URL and referrer for the default web context + * if the replacements are available. + * + * @returns {undefined} + */ + setAnonymousUrls() { + const { snowplowPseudonymizedPageUrl: pageUrl } = window.gl; + + if (!pageUrl) { + return; + } + + const referrers = getReferrersCache(); + const pageLinks = Object.seal({ + url: pageUrl, + referrer: '', + originalUrl: window.location.href, + }); + + const appendHash = Tracker.ALLOWED_URL_HASHES.some((prefix) => + window.location.hash.startsWith(prefix), + ); + const customUrl = `${pageUrl}${appendHash ? window.location.hash : ''}`; + window.snowplow('setCustomUrl', customUrl); + + if (document.referrer) { + const node = referrers.find((links) => links.originalUrl === document.referrer); + + if (node) { + pageLinks.referrer = node.url; + window.snowplow('setReferrerUrl', pageLinks.referrer); + } + } + + addReferrersCacheEntry(referrers, pageLinks); + }, +}; diff --git a/app/assets/javascripts/tracking/tracking.js b/app/assets/javascripts/tracking/tracking.js index cf7ec1e5465..923aea433f1 100644 --- a/app/assets/javascripts/tracking/tracking.js +++ b/app/assets/javascripts/tracking/tracking.js @@ -1,271 +1,7 @@ -import { LOAD_ACTION_ATTR_SELECTOR } from './constants'; -import { dispatchSnowplowEvent } from './dispatch_snowplow_event'; -import getStandardContext from './get_standard_context'; -import { - getEventHandlers, - createEventPayload, - renameKey, - addExperimentContext, - getReferrersCache, - addReferrersCacheEntry, -} from './utils'; - -const ALLOWED_URL_HASHES = ['#diff', '#note']; - -export default class Tracking { - static nonInitializedQueue = []; - static initialized = false; - static definitionsLoaded = false; - static definitionsManifest = {}; - static definitionsEventsQueue = []; - static definitions = []; - - /** - * (Legacy) Determines if tracking is enabled at the user level. - * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/DNT. - * - * @returns {Boolean} - */ - static trackable() { - return !['1', 'yes'].includes( - window.doNotTrack || navigator.doNotTrack || navigator.msDoNotTrack, - ); - } - - /** - * Determines if Snowplow is available/enabled. - * - * @returns {Boolean} - */ - static enabled() { - return typeof window.snowplow === 'function' && this.trackable(); - } - - /** - * Dispatches a structured event per our taxonomy: - * https://docs.gitlab.com/ee/development/snowplow/index.html#structured-event-taxonomy. - * - * If the library is not initialized and events are trying to be - * dispatched (data-attributes, load-events), they will be added - * to a queue to be flushed afterwards. - * - * If there is an error when using the library, it will return ´false´ - * and ´true´ otherwise. - * - * @param {...any} eventData defined event taxonomy - * @returns {Boolean} - */ - static event(...eventData) { - if (!this.enabled()) { - return false; - } - - if (!this.initialized) { - this.nonInitializedQueue.push(eventData); - return false; - } - - return dispatchSnowplowEvent(...eventData); - } - - /** - * Preloads event definitions. - * - * @returns {undefined} - */ - static loadDefinitions() { - // TODO: fetch definitions from the server and flush the queue - // See https://gitlab.com/gitlab-org/gitlab/-/issues/358256 - this.definitionsLoaded = true; - - while (this.definitionsEventsQueue.length) { - this.dispatchFromDefinition(...this.definitionsEventsQueue.shift()); - } - } - - /** - * Dispatches a structured event with data from its event definition. - * - * @param {String} basename - * @param {Object} eventData - * @returns {Boolean} - */ - static definition(basename, eventData = {}) { - if (!this.enabled()) { - return false; - } - - if (!(basename in this.definitionsManifest)) { - throw new Error(`Missing Snowplow event definition "${basename}"`); - } - - return this.dispatchFromDefinition(basename, eventData); - } - - /** - * Builds an event with data from a valid definition and sends it to - * Snowplow. If the definitions are not loaded, it pushes the data to a queue. - * - * @param {String} basename - * @param {Object} eventData - * @returns {Boolean} - */ - static dispatchFromDefinition(basename, eventData) { - if (!this.definitionsLoaded) { - this.definitionsEventsQueue.push([basename, eventData]); - - return false; - } - - const eventDefinition = this.definitions.find((definition) => definition.key === basename); - - return this.event( - eventData.category ?? eventDefinition.category, - eventData.action ?? eventDefinition.action, - eventData, - ); - } - - /** - * Dispatches any event emitted before initialization. - * - * @returns {undefined} - */ - static flushPendingEvents() { - this.initialized = true; - - while (this.nonInitializedQueue.length) { - dispatchSnowplowEvent(...this.nonInitializedQueue.shift()); - } - } - - /** - * Attaches event handlers for data-attributes powered events. - * - * @param {String} category - the default category for all events - * @param {HTMLElement} parent - element containing data-attributes - * @returns {Array} - */ - static bindDocument(category = document.body.dataset.page, parent = document) { - if (!this.enabled() || parent.trackingBound) { - return []; - } - - // eslint-disable-next-line no-param-reassign - parent.trackingBound = true; - - const handlers = getEventHandlers(category, (...args) => this.event(...args)); - handlers.forEach((event) => parent.addEventListener(event.name, event.func)); - - return handlers; - } - - /** - * Attaches event handlers for load-events (on render). - * - * @param {String} category - the default category for all events - * @param {HTMLElement} parent - element containing event targets - * @returns {Array} - */ - static trackLoadEvents(category = document.body.dataset.page, parent = document) { - if (!this.enabled()) { - return []; - } - - const loadEvents = parent.querySelectorAll(LOAD_ACTION_ATTR_SELECTOR); - - loadEvents.forEach((element) => { - const { action, data } = createEventPayload(element); - this.event(category, action, data); - }); - - return loadEvents; - } - - /** - * Enable Snowplow automatic form tracking. - * The config param requires at least one array of either forms - * class names, or field name attributes. - * https://docs.gitlab.com/ee/development/snowplow/index.html#form-tracking. - * - * @param {Object} config - * @param {Array} contexts - * @returns {undefined} - */ - static enableFormTracking(config, contexts = []) { - if (!this.enabled()) { - return; - } - - if (!Array.isArray(config?.forms?.allow) && !Array.isArray(config?.fields?.allow)) { - // eslint-disable-next-line @gitlab/require-i18n-strings - throw new Error('Unable to enable form event tracking without allow rules.'); - } - - // Ignore default/standard schema - const standardContext = getStandardContext(); - const userProvidedContexts = contexts.filter( - (context) => context.schema !== standardContext.schema, - ); - - const mappedConfig = {}; - if (config.forms) { - mappedConfig.forms = renameKey(config.forms, 'allow', 'whitelist'); - } - - if (config.fields) { - mappedConfig.fields = renameKey(config.fields, 'allow', 'whitelist'); - } - - const enabler = () => window.snowplow('enableFormTracking', mappedConfig, userProvidedContexts); - - if (document.readyState === 'complete') { - enabler(); - } else { - document.addEventListener('readystatechange', () => { - if (document.readyState === 'complete') { - enabler(); - } - }); - } - } - - /** - * Replaces the URL and referrer for the default web context - * if the replacements are available. - * - * @returns {undefined} - */ - static setAnonymousUrls() { - const { snowplowPseudonymizedPageUrl: pageUrl } = window.gl; - - if (!pageUrl) { - return; - } - - const referrers = getReferrersCache(); - const pageLinks = Object.seal({ - url: pageUrl, - referrer: '', - originalUrl: window.location.href, - }); - - const appendHash = ALLOWED_URL_HASHES.some((prefix) => window.location.hash.startsWith(prefix)); - const customUrl = `${pageUrl}${appendHash ? window.location.hash : ''}`; - window.snowplow('setCustomUrl', customUrl); - - if (document.referrer) { - const node = referrers.find((links) => links.originalUrl === document.referrer); - - if (node) { - pageLinks.referrer = node.url; - window.snowplow('setReferrerUrl', pageLinks.referrer); - } - } - - addReferrersCacheEntry(referrers, pageLinks); - } +import { Tracker } from 'jh_else_ce/tracking/tracker'; +import { addExperimentContext } from './utils'; +const Tracking = Object.assign(Tracker, { /** * Returns an implementation of this class in the form of * a Vue mixin. @@ -273,7 +9,7 @@ export default class Tracking { * @param {Object} opts - default options for all events * @returns {Object} */ - static mixin(opts = {}) { + mixin(opts = {}) { return { computed: { trackingCategory() { @@ -297,5 +33,7 @@ export default class Tracking { }, }, }; - } -} + }, +}); + +export default Tracking; diff --git a/app/controllers/projects/tracings_controller.rb b/app/controllers/projects/tracings_controller.rb index 2bc0c590e8d..a4aac6aaa32 100644 --- a/app/controllers/projects/tracings_controller.rb +++ b/app/controllers/projects/tracings_controller.rb @@ -15,6 +15,7 @@ module Projects feature_category :tracing def show + render_404 unless Feature.enabled?(:monitor_tracing, @project) end private diff --git a/app/helpers/ci/secure_files_helper.rb b/app/helpers/ci/secure_files_helper.rb new file mode 100644 index 00000000000..30b2e12ac3b --- /dev/null +++ b/app/helpers/ci/secure_files_helper.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true +module Ci + module SecureFilesHelper + def show_secure_files_setting(project, user) + return false if user.nil? + + Feature.enabled?(:ci_secure_files, project) && user.can?(:read_secure_files, project) + end + end +end diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index d40f3784fa1..15d512ac589 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -647,6 +647,7 @@ class ApplicationSetting < ApplicationRecord reset_memoized_terms end after_commit :expire_performance_bar_allowed_user_ids_cache, if: -> { previous_changes.key?('performance_bar_allowed_group_id') } + after_commit :reset_deletion_warning_redis_key, if: :saved_change_to_inactive_projects_delete_after_months? def validate_grafana_url validate_url(parsed_grafana_url, :grafana_url, GRAFANA_URL_ERROR_MESSAGE) @@ -777,6 +778,10 @@ class ApplicationSetting < ApplicationRecord ) end end + + def reset_deletion_warning_redis_key + Gitlab::InactiveProjectsDeletionWarningTracker.reset_all + end end ApplicationSetting.prepend_mod_with('ApplicationSetting') diff --git a/app/models/event.rb b/app/models/event.rb index 8ae2f61f36d..7760be3e817 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -357,6 +357,8 @@ class Event < ApplicationRecord Project.unscoped.where(id: project_id) .where('last_activity_at <= ?', RESET_PROJECT_ACTIVITY_INTERVAL.ago) .touch_all(:last_activity_at, time: created_at) # rubocop: disable Rails/SkipsModelValidations + + Gitlab::InactiveProjectsDeletionWarningTracker.new(project.id).reset end def authored_by?(user) diff --git a/app/models/project.rb b/app/models/project.rb index 524bb072810..181b09faa11 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -744,6 +744,16 @@ class Project < ApplicationRecord Project.with(cte.to_arel).from(cte.alias_to(Project.arel_table)) end + def self.inactive + project_statistics = ::ProjectStatistics.arel_table + minimum_size_mb = ::Gitlab::CurrentSettings.inactive_projects_min_size_mb.megabytes + last_activity_cutoff = ::Gitlab::CurrentSettings.inactive_projects_send_warning_email_after_months.months.ago + + joins(:statistics) + .where((project_statistics[:storage_size]).gt(minimum_size_mb)) + .where('last_activity_at < ?', last_activity_cutoff) + end + scope :active, -> { joins(:issues, :notes, :merge_requests).order('issues.created_at, notes.created_at, merge_requests.created_at DESC') } scope :abandoned, -> { where('projects.last_activity_at < ?', 6.months.ago) } diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb index ff94f4c1451..4074b1d1182 100644 --- a/app/services/notes/create_service.rb +++ b/app/services/notes/create_service.rb @@ -113,6 +113,7 @@ module Notes track_note_creation_usage_for_issues(note) if note.for_issue? track_note_creation_usage_for_merge_requests(note) if note.for_merge_request? track_incident_action(user, note.noteable, 'incident_comment') if note.for_issue? + track_note_creation_in_ipynb(note) if Feature.enabled?(:notes_create_service_tracking, project) Gitlab::Tracking.event('Notes::CreateService', 'execute', **tracking_data_for(note)) @@ -135,6 +136,16 @@ module Notes def track_note_creation_usage_for_merge_requests(note) Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter.track_create_comment_action(note: note) end + + def should_track_ipynb_notes?(note) + Feature.enabled?(:ipynbdiff_notes_tracker) && note.respond_to?(:diff_file) && note.diff_file&.ipynb? + end + + def track_note_creation_in_ipynb(note) + return unless should_track_ipynb_notes?(note) + + Gitlab::UsageDataCounters::IpynbDiffActivityCounter.note_created(note) + end end end diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml index 8b186935a4d..87ca13a7bd6 100644 --- a/app/views/projects/settings/ci_cd/show.html.haml +++ b/app/views/projects/settings/ci_cd/show.html.haml @@ -109,3 +109,15 @@ = link_to _('Learn more'), help_page_path('ci/jobs/ci_job_token'), target: '_blank', rel: 'noopener noreferrer' .settings-content = render 'ci/token_access/index' + +- if show_secure_files_setting(@project, current_user) + %section.settings + .settings-header + %h4.settings-title + = _("Secure Files") + = button_to project_ci_secure_files_path(@project), method: :get, class: 'btn gl-button btn-default' do + = _('Manage') + %p + = _("Use Secure Files to store files used by your pipelines such as Android keystores, or Apple provisioning profiles and signing certificates.") + = link_to _('Learn more'), help_page_path('ci/secure_files/index'), target: '_blank', rel: 'noopener noreferrer' + diff --git a/app/views/projects/settings/operations/show.html.haml b/app/views/projects/settings/operations/show.html.haml index 63eb2814309..80c22604e49 100644 --- a/app/views/projects/settings/operations/show.html.haml +++ b/app/views/projects/settings/operations/show.html.haml @@ -15,10 +15,12 @@ = s_('Deprecations|Feature deprecation and removal') .gl-alert-body %p - = html_escape(s_('Deprecations|The metrics feature was deprecated in GitLab 14.7. The logs and tracing features were also deprecated in GitLab 14.7, and are %{removal_link_start} scheduled for removal %{link_end} in GitLab 15.0. For information on a possible replacement, %{opstrace_link_start} learn more about Opstrace %{link_end}.')) % {removal_link_start: removal_epic_link_start, opstrace_link_start: opstrace_link_start, link_end: link_end } + = html_escape(s_('Deprecations|The metrics feature was deprecated in GitLab 14.7.')) + = html_escape(s_('Deprecations|The logs and tracing features were also deprecated in GitLab 14.7, and are %{removal_link_start} scheduled for removal %{link_end} in GitLab 15.0.')) % {removal_link_start: removal_epic_link_start, link_end: link_end } if Feature.enabled?(:monitor_tracing, @project) + = html_escape(s_('Deprecations|For information on a possible replacement, %{opstrace_link_start} learn more about Opstrace %{link_end}.')) % {opstrace_link_start: opstrace_link_start, link_end: link_end } = render 'projects/settings/operations/metrics_dashboard' -= render 'projects/settings/operations/tracing' += render 'projects/settings/operations/tracing' if Feature.enabled?(:monitor_tracing, @project) = render 'projects/settings/operations/error_tracking' = render 'projects/settings/operations/alert_management' = render 'projects/settings/operations/incidents' diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index 17866ef7296..b90c810cb31 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -579,6 +579,15 @@ :weight: 1 :idempotent: :tags: [] +- :name: cronjob:projects_inactive_projects_deletion_cron + :worker_name: Projects::InactiveProjectsDeletionCronWorker + :feature_category: :compliance_management + :has_external_dependencies: + :urgency: :low + :resource_boundary: :unknown + :weight: 1 + :idempotent: true + :tags: [] - :name: cronjob:projects_schedule_refresh_build_artifacts_size_statistics :worker_name: Projects::ScheduleRefreshBuildArtifactsSizeStatisticsWorker :feature_category: :build_artifacts @@ -2803,6 +2812,15 @@ :weight: 1 :idempotent: :tags: [] +- :name: projects_inactive_projects_deletion_notification + :worker_name: Projects::InactiveProjectsDeletionNotificationWorker + :feature_category: :compliance_management + :has_external_dependencies: + :urgency: :low + :resource_boundary: :unknown + :weight: 1 + :idempotent: true + :tags: [] - :name: projects_post_creation :worker_name: Projects::PostCreationWorker :feature_category: :source_code_management diff --git a/app/workers/projects/inactive_projects_deletion_cron_worker.rb b/app/workers/projects/inactive_projects_deletion_cron_worker.rb new file mode 100644 index 00000000000..2c3f4191502 --- /dev/null +++ b/app/workers/projects/inactive_projects_deletion_cron_worker.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +module Projects + class InactiveProjectsDeletionCronWorker + include ApplicationWorker + include Gitlab::Utils::StrongMemoize + include CronjobQueue + + idempotent! + data_consistency :always + feature_category :compliance_management + + INTERVAL = 2.seconds.to_i + + def perform + return unless ::Gitlab::CurrentSettings.delete_inactive_projects? + + admin_user = User.admins.active.first + + return unless admin_user + + notified_inactive_projects = Gitlab::InactiveProjectsDeletionWarningTracker.notified_projects + + Project.inactive.without_deleted.find_each(batch_size: 100).with_index do |project, index| # rubocop: disable CodeReuse/ActiveRecord + next unless Feature.enabled?(:inactive_projects_deletion, project.root_namespace) + + delay = index * INTERVAL + + with_context(project: project, user: admin_user) do + deletion_warning_email_sent_on = notified_inactive_projects["project:#{project.id}"] + + if send_deletion_warning_email?(deletion_warning_email_sent_on, project) + send_notification(delay, project, admin_user) + elsif deletion_warning_email_sent_on && delete_due_to_inactivity?(deletion_warning_email_sent_on) + Gitlab::InactiveProjectsDeletionWarningTracker.new(project.id).reset + delete_project(project, admin_user) + end + end + end + end + + private + + def grace_months_after_deletion_notification + strong_memoize(:grace_months_after_deletion_notification) do + (::Gitlab::CurrentSettings.inactive_projects_delete_after_months - + ::Gitlab::CurrentSettings.inactive_projects_send_warning_email_after_months).months + end + end + + def send_deletion_warning_email?(deletion_warning_email_sent_on, project) + deletion_warning_email_sent_on.blank? + end + + def delete_due_to_inactivity?(deletion_warning_email_sent_on) + deletion_warning_email_sent_on < grace_months_after_deletion_notification.ago + end + + def deletion_date + grace_months_after_deletion_notification.from_now.to_date.to_s + end + + def delete_project(project, user) + ::Projects::DestroyService.new(project, user, {}).async_execute + end + + def send_notification(delay, project, user) + ::Projects::InactiveProjectsDeletionNotificationWorker.perform_in(delay, project.id, deletion_date) + end + end +end + +Projects::InactiveProjectsDeletionCronWorker.prepend_mod diff --git a/app/workers/projects/inactive_projects_deletion_notification_worker.rb b/app/workers/projects/inactive_projects_deletion_notification_worker.rb new file mode 100644 index 00000000000..0bf808fd753 --- /dev/null +++ b/app/workers/projects/inactive_projects_deletion_notification_worker.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Projects + class InactiveProjectsDeletionNotificationWorker + include ApplicationWorker + include ExceptionBacktrace + + idempotent! + data_consistency :sticky + sidekiq_options retry: 3 + feature_category :compliance_management + + def perform(project_id, deletion_date) + return if Gitlab::InactiveProjectsDeletionWarningTracker.new(project_id).notified? + + project = Project.find(project_id) + + notification_service.inactive_project_deletion_warning(project, deletion_date) + + Gitlab::InactiveProjectsDeletionWarningTracker.new(project_id).mark_notified + rescue ActiveRecord::RecordNotFound => error + Gitlab::ErrorTracking.log_exception(error, project_id: project_id) + end + + private + + def notification_service + @notification_service ||= NotificationService.new + end + end +end diff --git a/config/feature_flags/development/inactive_projects_deletion.yml b/config/feature_flags/development/inactive_projects_deletion.yml new file mode 100644 index 00000000000..e9bb91f62cc --- /dev/null +++ b/config/feature_flags/development/inactive_projects_deletion.yml @@ -0,0 +1,8 @@ +--- +name: inactive_projects_deletion +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85689 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/357968 +milestone: '15.0' +type: development +group: group::compliance +default_enabled: false diff --git a/config/feature_flags/development/ipynbdiff_notes_tracker.yml b/config/feature_flags/development/ipynbdiff_notes_tracker.yml new file mode 100644 index 00000000000..af471a3f6ee --- /dev/null +++ b/config/feature_flags/development/ipynbdiff_notes_tracker.yml @@ -0,0 +1,8 @@ +--- +name: ipynbdiff_notes_tracker +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85398 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/362232 +milestone: '15.0' +type: development +group: group::incubation +default_enabled: false diff --git a/config/feature_flags/development/monitor_tracing.yml b/config/feature_flags/development/monitor_tracing.yml new file mode 100644 index 00000000000..e4de215cc97 --- /dev/null +++ b/config/feature_flags/development/monitor_tracing.yml @@ -0,0 +1,8 @@ +--- +name: monitor_tracing +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85877 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/359904 +milestone: '15.0' +type: development +group: group::respond +default_enabled: false diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 24547c78d9e..5b15b119b3a 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -626,6 +626,9 @@ Settings.cron_jobs['clusters_integrations_check_prometheus_health_worker']['job_ Settings.cron_jobs['projects_schedule_refresh_build_artifacts_size_statistics_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['projects_schedule_refresh_build_artifacts_size_statistics_worker']['cron'] ||= '2/17 * * * *' Settings.cron_jobs['projects_schedule_refresh_build_artifacts_size_statistics_worker']['job_class'] = 'Projects::ScheduleRefreshBuildArtifactsSizeStatisticsWorker' +Settings.cron_jobs['inactive_projects_deletion_cron_worker'] ||= Settingslogic.new({}) +Settings.cron_jobs['inactive_projects_deletion_cron_worker']['cron'] ||= '0 1 * * *' +Settings.cron_jobs['inactive_projects_deletion_cron_worker']['job_class'] = 'Projects::InactiveProjectsDeletionCronWorker' Gitlab.ee do Settings.cron_jobs['analytics_devops_adoption_create_all_snapshots_worker'] ||= Settingslogic.new({}) diff --git a/config/metrics/aggregates/code_review.yml b/config/metrics/aggregates/code_review.yml index 004f155864e..a25222b8d6b 100644 --- a/config/metrics/aggregates/code_review.yml +++ b/config/metrics/aggregates/code_review.yml @@ -75,6 +75,12 @@ - 'i_code_review_post_merge_submit_cherry_pick_modal' - 'i_code_review_user_jetbrains_api_request' - 'i_code_review_user_gitlab_cli_api_request' + - 'i_code_review_user_create_note_in_ipynb_diff' + - 'i_code_review_user_create_note_in_ipynb_diff_mr' + - 'i_code_review_user_create_note_in_ipynb_diff_commit' + - 'i_code_review_create_note_in_ipynb_diff' + - 'i_code_review_create_note_in_ipynb_diff_mr' + - 'i_code_review_create_note_in_ipynb_diff_commit' - name: code_review_category_monthly_active_users operator: OR source: redis @@ -140,6 +146,12 @@ - 'i_code_review_post_merge_click_cherry_pick' - 'i_code_review_post_merge_submit_revert_modal' - 'i_code_review_post_merge_submit_cherry_pick_modal' + - 'i_code_review_user_create_note_in_ipynb_diff' + - 'i_code_review_user_create_note_in_ipynb_diff_mr' + - 'i_code_review_user_create_note_in_ipynb_diff_commit' + - 'i_code_review_create_note_in_ipynb_diff' + - 'i_code_review_create_note_in_ipynb_diff_mr' + - 'i_code_review_create_note_in_ipynb_diff_commit' - name: code_review_extension_category_monthly_active_users operator: OR source: redis diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml index 104580761d0..611bda1f58e 100644 --- a/config/sidekiq_queues.yml +++ b/config/sidekiq_queues.yml @@ -361,6 +361,8 @@ - 1 - - projects_git_garbage_collect - 1 +- - projects_inactive_projects_deletion_notification + - 1 - - projects_post_creation - 1 - - projects_process_sync_events diff --git a/data/removals/15_0/15-0-Pseudonymizer.yml b/data/removals/15_0/15-0-Pseudonymizer.yml new file mode 100644 index 00000000000..f3ad893e5dc --- /dev/null +++ b/data/removals/15_0/15-0-Pseudonymizer.yml @@ -0,0 +1,13 @@ +- name: "Pseudonymizer" + announcement_milestone: "14.7" + announcement_date: "2022-01-22" + removal_milestone: "15.0" + removal_date: "2022-05-22" + breaking_change: true + body: | # Do not modify this line, instead modify the lines below. + The Pseudonymizer feature is generally unused, can cause production issues with large databases, and can interfere with object storage development. + It was removed in GitLab 15.0. + stage: Enablement + tiers: [Free, Premium, Ultimate] + documentation_url: "https://docs.gitlab.com/ee/administration/pseudonymizer.html" + issue_url: "https://gitlab.com/gitlab-org/gitlab/-/issues/219952" diff --git a/data/removals/15_0/15-0-SLES-12-SP2.yml b/data/removals/15_0/15-0-SLES-12-SP2.yml new file mode 100644 index 00000000000..40c51fa5f95 --- /dev/null +++ b/data/removals/15_0/15-0-SLES-12-SP2.yml @@ -0,0 +1,10 @@ +- name: "SUSE Linux Enterprise Server 12 SP2" + announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated. + announcement_date: "2021-11-22" + removal_milestone: "15.0" # the milestone when this feature is planned to be removed + removal_date: "2022-05-22" # the date of the milestone release when this feature is planned to be removed + breaking_change: true + body: | # Do not modify this line, instead modify the lines below. + Long term service and support (LTSS) for SUSE Linux Enterprise Server (SLES) 12 SP2 [ended on March 31, 2021](https://www.suse.com/lifecycle/). The CA certificates on SP2 include the expired DST root certificate, and it's not getting new CA certificate package updates. We have implemented some [workarounds](https://gitlab.com/gitlab-org/gitlab-omnibus-builder/-/merge_requests/191), but we will not be able to continue to keep the build running properly. + stage: Enablement + tiers: [Free, Premium, Ultimate] diff --git a/data/removals/15_0/15-0-custom_hooks_dir.yml b/data/removals/15_0/15-0-custom_hooks_dir.yml new file mode 100644 index 00000000000..2c116a55e1c --- /dev/null +++ b/data/removals/15_0/15-0-custom_hooks_dir.yml @@ -0,0 +1,12 @@ +- name: "Move `custom_hooks_dir` setting from GitLab Shell to Gitaly" + announcement_milestone: "14.9" + announcement_date: "2022-03-22" + removal_milestone: "15.0" + removal_date: "2022-05-22" + breaking_change: true + body: | # Do not modify this line, instead modify the lines below. + The [`custom_hooks_dir`](https://docs.gitlab.com/ee/administration/server_hooks.html#create-a-global-server-hook-for-all-repositories) setting is now configured in Gitaly, and is removed from GitLab Shell in GitLab 15.0. + stage: Enablement + tiers: [Free, Premium, Ultimate] + issue_url: https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/4208 + documentation_url: https://docs.gitlab.com/ee/administration/server_hooks.html#create-a-global-server-hook-for-all-repositories diff --git a/data/removals/15_0/15-0-gitaly-internal-socket-dir.yml b/data/removals/15_0/15-0-gitaly-internal-socket-dir.yml new file mode 100644 index 00000000000..eedb50d2205 --- /dev/null +++ b/data/removals/15_0/15-0-gitaly-internal-socket-dir.yml @@ -0,0 +1,13 @@ +- name: "Support for `gitaly['internal_socket_dir']`" + announcement_milestone: "14.10" + announcement_date: "2022-04-22" + removal_milestone: "15.0" + removal_date: "2022-05-22" + breaking_change: true + body: | # Do not modify this line, instead modify the lines below. + Gitaly introduced a new directory that holds all runtime data Gitaly requires to operate correctly. This new directory replaces the old internal socket directory, and consequentially the usage of `gitaly['internal_socket_dir']` was deprecated in favor of `gitaly['runtime_dir']`. + + stage: Enablement + tiers: [Free, Premium, Ultimate] + issue_url: https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/6758 + documentation_url: https://docs.gitlab.com/omnibus/update/gitlab_15_changes.html#removing-support-for-gitalys-internal-socket-path diff --git a/data/removals/15_0/15-0-praefect-database-no-proxy.yml b/data/removals/15_0/15-0-praefect-database-no-proxy.yml new file mode 100644 index 00000000000..b363decc68e --- /dev/null +++ b/data/removals/15_0/15-0-praefect-database-no-proxy.yml @@ -0,0 +1,11 @@ +- name: "Move Gitaly Cluster Praefect `database_host_no_proxy` and `database_port_no_proxy configs`" + announcement_milestone: "14.0" + announcement_date: "2021-05-22" + removal_milestone: "15.0" + removal_date: "2022-05-22" + breaking_change: true + body: | # Do not modify this line, instead modify the lines below. + The Gitaly Cluster configuration keys for `praefect['database_host_no_proxy']` and `praefect['database_port_no_proxy']` are replaced with `praefect['database_direct_host']` and `praefect['database_direct_port']`. + stage: Enablement + tiers: [Free, Premium, Ultimate] + issue_url: https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/6150 diff --git a/db/migrate/20220506180411_add_index_to_vulnerability_feedback_finding_uuid.rb b/db/migrate/20220506180411_add_index_to_vulnerability_feedback_finding_uuid.rb index 31f53aa41cf..a6f01ce5a0e 100644 --- a/db/migrate/20220506180411_add_index_to_vulnerability_feedback_finding_uuid.rb +++ b/db/migrate/20220506180411_add_index_to_vulnerability_feedback_finding_uuid.rb @@ -7,7 +7,6 @@ class AddIndexToVulnerabilityFeedbackFindingUuid < Gitlab::Database::Migration[2 # We are indexing on UUID, a hash index should be smaller and faster # details on https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86808#note_943330140 - # rubocop:disable Migration/HashIndex def up add_concurrent_index :vulnerability_feedback, :finding_uuid, using: :hash, name: INDEX_NAME end @@ -15,5 +14,4 @@ class AddIndexToVulnerabilityFeedbackFindingUuid < Gitlab::Database::Migration[2 def down remove_concurrent_index :vulnerability_feedback, :finding_uuid, using: :hash, name: INDEX_NAME end - # rubocop:enable Migration/HashIndex end diff --git a/doc/administration/package_information/postgresql_versions.md b/doc/administration/package_information/postgresql_versions.md index fa23f8069ed..97f93663003 100644 --- a/doc/administration/package_information/postgresql_versions.md +++ b/doc/administration/package_information/postgresql_versions.md @@ -26,7 +26,7 @@ Read more about update policies and warnings in the PostgreSQL | GitLab version | PostgreSQL versions | Default version for fresh installs | Default version for upgrades | Notes | | -------------- | --------------------- | ---------------------------------- | ---------------------------- | ----- | -| 15.0 | 12.7, 13.3 | 13.3 | 13.3 | Users can manually upgrade to 13.3 following the [upgrade docs](https://docs.gitlab.com/omnibus/settings/database.html#gitlab-150-and-later). | +| 15.0 | 12.10, 13.6 | 13.6 | 13.6 | Users can manually upgrade to 13.6 following the [upgrade docs](https://docs.gitlab.com/omnibus/settings/database.html#gitlab-150-and-later). | | 14.1 | 12.7, 13.3 | 12.7 | 12.7 | PostgreSQL 13 available for fresh installations if not using [Geo](../geo/index.md#requirements-for-running-geo) or [Patroni](../postgresql/index.md#postgresql-replication-and-failover-with-omnibus-gitlab). | 14.0 | 12.7 | 12.7 | 12.7 | HA installations with repmgr are no longer supported and will be prevented from upgrading to Omnibus GitLab 14.0 | | 13.8 | 11.9, 12.4 | 12.4 | 12.4 | Package upgrades automatically performed PostgreSQL upgrade for nodes that are not part of a Geo or HA cluster.). | diff --git a/doc/development/contributing/issue_workflow.md b/doc/development/contributing/issue_workflow.md index fe1549e7f34..97c8c179e09 100644 --- a/doc/development/contributing/issue_workflow.md +++ b/doc/development/contributing/issue_workflow.md @@ -108,7 +108,7 @@ Group labels specify which [groups](https://about.gitlab.com/company/team/struct It's highly recommended to add a group label, as it's used by our triage automation to -[infer the correct stage label](https://about.gitlab.com/handbook/engineering/quality/triage-operations/#auto-labelling-of-issues). +[infer the correct stage label](https://about.gitlab.com/handbook/engineering/quality/triage-operations/#auto-labelling-of-issues-and-merge-requests). #### Naming and color convention diff --git a/doc/development/distributed_tracing.md b/doc/development/distributed_tracing.md index 680ac71f857..b4f347449cc 100644 --- a/doc/development/distributed_tracing.md +++ b/doc/development/distributed_tracing.md @@ -71,9 +71,7 @@ GITLAB_TRACING=opentracing://?=&= In this example, we have the following hypothetical values: -- `driver`: the driver. [GitLab supports - `jaeger`](../operations/tracing.md). In future, other - tracing implementations may also be supported. +- `driver`: the driver such a jaegar. - `param_name`, `param_value`: these are driver specific configuration values. Configuration parameters for Jaeger are documented [further on in this document](#2-configure-the-gitlab_tracing-environment-variable) they should be URL encoded. diff --git a/doc/operations/tracing.md b/doc/operations/tracing.md index bbc05bb0f9a..e68d7077328 100644 --- a/doc/operations/tracing.md +++ b/doc/operations/tracing.md @@ -4,14 +4,21 @@ group: Respond info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- -# Tracing (DEPRECATED) **(FREE)** +# Tracing (DEPRECATED) **(FREE SELF)** > - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/42645) from GitLab Ultimate to GitLab Free in 13.5. > - [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/346540) in GitLab 14.7. +> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/359904) behind a [feature flag](../administration/feature_flags.md) named `monitor_tracing` in GitLab 15.0. Disabled by default. WARNING: This feature is in its end-of-life process. It is [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/346540) -in GitLab 14.7, and is planned for removal in GitLab 15.0. +in GitLab 14.7. +It will be removed completely in GitLab 15.2. + +FLAG: +On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to [enable the feature flag](../administration/feature_flags.md) named `monitor_tracing`. +On GitLab.com, this feature is not available. +This feature is not recommended for production use. Tracing provides insight into the performance and health of a deployed application, tracking each function or microservice that handles a given request. Tracing makes it easy to understand the diff --git a/doc/update/package/index.md b/doc/update/package/index.md index 47e9a1761ab..faca633f446 100644 --- a/doc/update/package/index.md +++ b/doc/update/package/index.md @@ -127,7 +127,9 @@ or upgrade command: ``` 1. Install the specific `gitlab-ee` package by using one of the following commands - and replacing `` with the version you found in the previous step: + and replacing `` with the next supported version you would like to install + (make sure to review the [upgrade path](../index.md#upgrade-paths) to confirm the + version you're installing is part of a supported path): ```shell # Ubuntu/Debian diff --git a/doc/update/removals.md b/doc/update/removals.md index c9889bc31de..39c6e10ec36 100644 --- a/doc/update/removals.md +++ b/doc/update/removals.md @@ -216,6 +216,26 @@ In GitLab 13.0, we introduced new project and design replication details routes We have now removed the deprecated legacy names for approval status of license policy (`blacklisted`, `approved`) in the API queries and responses. If you are using our License Compliance API you should stop using the `approved` and `blacklisted` query parameters, they are now `allowed` and `denied`. In 15.0 the responses will also stop using `approved` and `blacklisted` so you may need to adjust any of your custom tools. +### Move Gitaly Cluster Praefect `database_host_no_proxy` and `database_port_no_proxy configs` + +WARNING: +This feature was changed or removed in 15.0 +as a [breaking change](https://docs.gitlab.com/ee/development/contributing/#breaking-changes). +Before updating GitLab, review the details carefully to determine if you need to make any +changes to your code, settings, or workflow. + +The Gitaly Cluster configuration keys for `praefect['database_host_no_proxy']` and `praefect['database_port_no_proxy']` are replaced with `praefect['database_direct_host']` and `praefect['database_direct_port']`. + +### Move `custom_hooks_dir` setting from GitLab Shell to Gitaly + +WARNING: +This feature was changed or removed in 15.0 +as a [breaking change](https://docs.gitlab.com/ee/development/contributing/#breaking-changes). +Before updating GitLab, review the details carefully to determine if you need to make any +changes to your code, settings, or workflow. + +The [`custom_hooks_dir`](https://docs.gitlab.com/ee/administration/server_hooks.html#create-a-global-server-hook-for-all-repositories) setting is now configured in Gitaly, and is removed from GitLab Shell in GitLab 15.0. + ### OAuth implicit grant WARNING: @@ -261,6 +281,17 @@ changes to your code, settings, or workflow. Allowing expired personal access tokens to be used is unusual from a security perspective and could create unusual situations where an expired key is unintentionally able to be used. Unexpected behavior in a security feature is inherently dangerous and so we now do not let expired personal access tokens be used. +### Pseudonymizer + +WARNING: +This feature was changed or removed in 15.0 +as a [breaking change](https://docs.gitlab.com/ee/development/contributing/#breaking-changes). +Before updating GitLab, review the details carefully to determine if you need to make any +changes to your code, settings, or workflow. + +The Pseudonymizer feature is generally unused, can cause production issues with large databases, and can interfere with object storage development. +It was removed in GitLab 15.0. + ### Remove Versions from PackageType WARNING: @@ -410,6 +441,16 @@ Version 3 was [announced in GitLab 14.6](https://about.gitlab.com/releases/2021/ If you rely on .NET 2.1 support being present in the analyzer image by default, you must take action as detailed in the [deprecation issue for this change](https://gitlab.com/gitlab-org/gitlab/-/issues/352553#breaking-change). +### SUSE Linux Enterprise Server 12 SP2 + +WARNING: +This feature was changed or removed in 15.0 +as a [breaking change](https://docs.gitlab.com/ee/development/contributing/#breaking-changes). +Before updating GitLab, review the details carefully to determine if you need to make any +changes to your code, settings, or workflow. + +Long term service and support (LTSS) for SUSE Linux Enterprise Server (SLES) 12 SP2 [ended on March 31, 2021](https://www.suse.com/lifecycle/). The CA certificates on SP2 include the expired DST root certificate, and it's not getting new CA certificate package updates. We have implemented some [workarounds](https://gitlab.com/gitlab-org/gitlab-omnibus-builder/-/merge_requests/191), but we will not be able to continue to keep the build running properly. + ### Sidekiq configuration for metrics and health checks WARNING: @@ -436,6 +477,16 @@ If you installed GitLab from source, verify manually that both servers are confi The Static Site Editor was deprecated in GitLab 14.7 and the feature is being removed in GitLab 15.0. Incoming requests to the Static Site Editor will be redirected and open the target file to edit in the Web IDE. Current users of the Static Site Editor can view the [documentation](https://docs.gitlab.com/ee/user/project/static_site_editor/) for more information, including how to remove the configuration files from existing projects. We will continue investing in improvements to the Markdown editing experience by [maturing the Content Editor](https://gitlab.com/groups/gitlab-org/-/epics/5401) and making it available as a way to edit content across GitLab. +### Support for `gitaly['internal_socket_dir']` + +WARNING: +This feature was changed or removed in 15.0 +as a [breaking change](https://docs.gitlab.com/ee/development/contributing/#breaking-changes). +Before updating GitLab, review the details carefully to determine if you need to make any +changes to your code, settings, or workflow. + +Gitaly introduced a new directory that holds all runtime data Gitaly requires to operate correctly. This new directory replaces the old internal socket directory, and consequentially the usage of `gitaly['internal_socket_dir']` was deprecated in favor of `gitaly['runtime_dir']`. + ### Support for legacy format of `config/database.yml` removed WARNING: diff --git a/doc/user/application_security/policies/scan-execution-policies.md b/doc/user/application_security/policies/scan-execution-policies.md index 9f68d2002b8..aa23ad30a73 100644 --- a/doc/user/application_security/policies/scan-execution-policies.md +++ b/doc/user/application_security/policies/scan-execution-policies.md @@ -85,7 +85,9 @@ This rule enforces the defined actions and schedules a scan on the provided date | `type` | `string` | `schedule` | The rule's type. | | `branches` | `array` of `string` | `*` or the branch's name | The branch the given policy applies to (supports wildcard). | | `cadence` | `string` | CRON expression (for example, `0 0 * * *`) | A whitespace-separated string containing five fields that represents the scheduled time. | -| `clusters` | `object` | | The cluster where the given policy enforces running selected scans (only for `container_scanning`/`cluster_image_scanning` scans). The key of the object is the name of the Kubernetes cluster configured for your project in GitLab. In the optionally provided value of the object, you can precisely select Kubernetes resources that are scanned. | +| `agents` | `object` | | The name of the [GitLab agents](../../clusters/agent/index.md) where [cluster image scanning](../../clusters/agent/vulnerabilities.md) will run. The key of the object is the name of the Kubernetes cluster configured for your project in GitLab. In the optionally provided value of the object, you can precisely select Kubernetes resources that are scanned. | +| `clusters` (removed) | `object` | | This field was [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/356465) in 15.0. Use the `agents` field instead. The cluster where the given policy enforces running selected scans (only for `container_scanning`/`cluster_image_scanning` scans). The key of the object is the name of the Kubernetes cluster configured for your project in GitLab. In the optionally provided value of the object, you can precisely select Kubernetes resources that are scanned. | + GitLab supports the following types of CRON syntax for the `cadence` field: @@ -94,7 +96,20 @@ GitLab supports the following types of CRON syntax for the `cadence` field: It is possible that other elements of the CRON syntax will work in the cadence field, however, GitLab does not officially test or support them. -### `cluster` schema +### `agent` schema + +Use this schema to define `agents` objects in the [`schedule` rule type](#schedule-rule-type). + +| Field | Type | Possible values | Description | +|--------------|---------------------|--------------------------|-------------| +| `namespaces` | `array` of `string` | | The namespace that is scanned. If empty, all namespaces will be scanned. | + + + +### `cluster` schema (removed) + +This schema was [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/356465) in 15.0. +Use the [`agent` schema](#agent-schema) instead. Use this schema to define `clusters` objects in the [`schedule` rule type](#schedule-rule-type). @@ -105,6 +120,8 @@ Use this schema to define `clusters` objects in the [`schedule` rule type](#sche | `namespaces` | `array` of `string` | | The namespace that is scanned (only the first value is currently supported). | | `kinds` | `array` of `string` | `deployment`/`daemonset` | The resource kind that should be scanned (only the first value is currently supported). | + + ## `scan` action type This action executes the selected `scan` with additional parameters when conditions for at least one diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md index ceee8c4e0b4..1e63472763d 100644 --- a/doc/user/project/settings/index.md +++ b/doc/user/project/settings/index.md @@ -560,10 +560,6 @@ Automatically [create](../../../operations/incident_management/incidents.md#crea Configure Error Tracking to discover and view [Sentry errors within GitLab](../../../operations/error_tracking.md). -### Jaeger tracing - -Add the URL of a Jaeger server to allow your users to [easily access the Jaeger UI from within GitLab](../../../operations/tracing.md). - ### Status Page **(ULTIMATE)** [Add Storage credentials](../../../operations/incident_management/status_page.md#sync-incidents-to-the-status-page) diff --git a/lib/gitlab/ci/templates/Jobs/SAST-IaC.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/SAST-IaC.gitlab-ci.yml index 488e7ec72fd..b6358eb0831 100644 --- a/lib/gitlab/ci/templates/Jobs/SAST-IaC.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Jobs/SAST-IaC.gitlab-ci.yml @@ -31,7 +31,7 @@ kics-iac-sast: image: name: "$SAST_ANALYZER_IMAGE" variables: - SAST_ANALYZER_IMAGE_TAG: 1 + SAST_ANALYZER_IMAGE_TAG: 2 SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/kics:$SAST_ANALYZER_IMAGE_TAG$SAST_IMAGE_SUFFIX" rules: - if: $SAST_DISABLED diff --git a/lib/gitlab/ci/templates/Jobs/SAST-IaC.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/SAST-IaC.latest.gitlab-ci.yml index 488e7ec72fd..b6358eb0831 100644 --- a/lib/gitlab/ci/templates/Jobs/SAST-IaC.latest.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Jobs/SAST-IaC.latest.gitlab-ci.yml @@ -31,7 +31,7 @@ kics-iac-sast: image: name: "$SAST_ANALYZER_IMAGE" variables: - SAST_ANALYZER_IMAGE_TAG: 1 + SAST_ANALYZER_IMAGE_TAG: 2 SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/kics:$SAST_ANALYZER_IMAGE_TAG$SAST_IMAGE_SUFFIX" rules: - if: $SAST_DISABLED diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb index d21cbdf0be0..d6ee21b93b6 100644 --- a/lib/gitlab/diff/file.rb +++ b/lib/gitlab/diff/file.rb @@ -386,6 +386,10 @@ module Gitlab strong_memoize(:rendered) { Rendered::Notebook::DiffFile.new(self) } end + def ipynb? + file_path.ends_with?('.ipynb') + end + private def diffable_by_attribute? @@ -415,10 +419,6 @@ module Gitlab new_file? || deleted_file? || content_changed? end - def ipynb? - file_path.ends_with?('.ipynb') - end - # We can't use Object#try because Blob doesn't inherit from Object, but # from BasicObject (via SimpleDelegator). def try_blobs(meth) diff --git a/lib/gitlab/inactive_projects_deletion_warning_tracker.rb b/lib/gitlab/inactive_projects_deletion_warning_tracker.rb new file mode 100644 index 00000000000..f3f8e774b4b --- /dev/null +++ b/lib/gitlab/inactive_projects_deletion_warning_tracker.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +module Gitlab + class InactiveProjectsDeletionWarningTracker + attr_reader :project_id + + DELETION_TRACKING_REDIS_KEY = 'inactive_projects_deletion_warning_email_notified' + + # Redis key 'inactive_projects_deletion_warning_email_notified' is a hash. It stores the date when the + # deletion warning notification email was sent for an inactive project. The fields and values look like: + # {"project:1"=>"2022-04-22", "project:5"=>"2022-04-22", "project:7"=>"2022-04-25"} + # @return [Hash] + def self.notified_projects + Gitlab::Redis::SharedState.with do |redis| + redis.hgetall(DELETION_TRACKING_REDIS_KEY) + end + end + + def self.reset_all + Gitlab::Redis::SharedState.with do |redis| + redis.del(DELETION_TRACKING_REDIS_KEY) + end + end + + def initialize(project_id) + @project_id = project_id + end + + def notified? + Gitlab::Redis::SharedState.with do |redis| + redis.hexists(DELETION_TRACKING_REDIS_KEY, "project:#{project_id}") + end + end + + def mark_notified + Gitlab::Redis::SharedState.with do |redis| + redis.hset(DELETION_TRACKING_REDIS_KEY, "project:#{project_id}", Date.current) + end + end + + def reset + Gitlab::Redis::SharedState.with do |redis| + redis.hdel(DELETION_TRACKING_REDIS_KEY, "project:#{project_id}") + end + end + end +end diff --git a/lib/gitlab/usage_data_counters/ipynb_diff_activity_counter.rb b/lib/gitlab/usage_data_counters/ipynb_diff_activity_counter.rb new file mode 100644 index 00000000000..a34ae909c82 --- /dev/null +++ b/lib/gitlab/usage_data_counters/ipynb_diff_activity_counter.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +# noinspection RubyConstantNamingConvention +module Gitlab + module UsageDataCounters + module IpynbDiffActivityCounter + NOTE_CREATED_IN_IPYNB_DIFF_ACTION = 'i_code_review_create_note_in_ipynb_diff' + USER_CREATED_NOTE_IN_IPYNB_DIFF_ACTION = 'i_code_review_user_create_note_in_ipynb_diff' + NOTE_CREATED_IN_IPYNB_DIFF_MR_ACTION = 'i_code_review_create_note_in_ipynb_diff_mr' + USER_CREATED_NOTE_IN_IPYNB_DIFF_MR_ACTION = 'i_code_review_user_create_note_in_ipynb_diff_mr' + NOTE_CREATED_IN_IPYNB_DIFF_COMMIT_ACTION = 'i_code_review_create_note_in_ipynb_diff_commit' + USER_CREATED_NOTE_IN_IPYNB_DIFF_COMMIT_ACTION = 'i_code_review_user_create_note_in_ipynb_diff_commit' + + class << self + def note_created(note) + return unless note.for_merge_request? || note.for_commit? + + if note.for_merge_request? + track(NOTE_CREATED_IN_IPYNB_DIFF_MR_ACTION, USER_CREATED_NOTE_IN_IPYNB_DIFF_MR_ACTION, note) + else + track(NOTE_CREATED_IN_IPYNB_DIFF_COMMIT_ACTION, USER_CREATED_NOTE_IN_IPYNB_DIFF_COMMIT_ACTION, note) + end + + track(NOTE_CREATED_IN_IPYNB_DIFF_ACTION, USER_CREATED_NOTE_IN_IPYNB_DIFF_ACTION, note) + end + + private + + def track(action, per_user_action, note) + Gitlab::UsageDataCounters::HLLRedisCounter.track_usage_event(action, note.id) + Gitlab::UsageDataCounters::HLLRedisCounter.track_usage_event(per_user_action, note.author_id) + end + end + end + end +end diff --git a/lib/gitlab/usage_data_counters/known_events/code_review_events.yml b/lib/gitlab/usage_data_counters/known_events/code_review_events.yml index c91b104a0c2..e3bb3f6fef3 100644 --- a/lib/gitlab/usage_data_counters/known_events/code_review_events.yml +++ b/lib/gitlab/usage_data_counters/known_events/code_review_events.yml @@ -173,6 +173,30 @@ redis_slot: code_review category: code_review aggregation: weekly +- name: i_code_review_create_note_in_ipynb_diff + redis_slot: code_review + category: code_review + aggregation: weekly +- name: i_code_review_user_create_note_in_ipynb_diff + redis_slot: code_review + category: code_review + aggregation: weekly +- name: i_code_review_create_note_in_ipynb_diff_mr + redis_slot: code_review + category: code_review + aggregation: weekly +- name: i_code_review_user_create_note_in_ipynb_diff_mr + redis_slot: code_review + category: code_review + aggregation: weekly +- name: i_code_review_create_note_in_ipynb_diff_commit + redis_slot: code_review + category: code_review + aggregation: weekly +- name: i_code_review_user_create_note_in_ipynb_diff_commit + redis_slot: code_review + category: code_review + aggregation: weekly # Diff settings events - name: i_code_review_click_diff_view_setting redis_slot: code_review diff --git a/lib/sidebars/projects/menus/monitor_menu.rb b/lib/sidebars/projects/menus/monitor_menu.rb index a42e0db55ee..c35bc1f5481 100644 --- a/lib/sidebars/projects/menus/monitor_menu.rb +++ b/lib/sidebars/projects/menus/monitor_menu.rb @@ -73,7 +73,8 @@ module Sidebars end def tracing_menu_item - if !can?(context.current_user, :read_environment, context.project) || + if !Feature.enabled?(:monitor_tracing, context.project) || + !can?(context.current_user, :read_environment, context.project) || !can?(context.current_user, :admin_project, context.project) return ::Sidebars::NilMenuItem.new(item_id: :tracing) end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index e989cd59861..b63b8d77f0c 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -12645,6 +12645,12 @@ msgstr "" msgid "Deprecations|For information on a possible replacement %{epicStart} learn more about Opstrace %{epicEnd}." msgstr "" +msgid "Deprecations|For information on a possible replacement, %{opstrace_link_start} learn more about Opstrace %{link_end}." +msgstr "" + +msgid "Deprecations|The logs and tracing features were also deprecated in GitLab 14.7, and are %{removal_link_start} scheduled for removal %{link_end} in GitLab 15.0." +msgstr "" + msgid "Deprecations|The logs and tracing features were deprecated in GitLab 14.7 and are %{epicStart} scheduled for removal %{epicEnd} in GitLab 15.0." msgstr "" @@ -12654,9 +12660,6 @@ msgstr "" msgid "Deprecations|The metrics feature was deprecated in GitLab 14.7." msgstr "" -msgid "Deprecations|The metrics feature was deprecated in GitLab 14.7. The logs and tracing features were also deprecated in GitLab 14.7, and are %{removal_link_start} scheduled for removal %{link_end} in GitLab 15.0. For information on a possible replacement, %{opstrace_link_start} learn more about Opstrace %{link_end}." -msgstr "" - msgid "Deprioritize label" msgstr "" @@ -23156,6 +23159,9 @@ msgstr "" msgid "Makes this issue confidential." msgstr "" +msgid "Manage" +msgstr "" + msgid "Manage %{workspace} labels" msgstr "" diff --git a/spec/controllers/projects/tracings_controller_spec.rb b/spec/controllers/projects/tracings_controller_spec.rb index 1f8a68cc861..80e21349e20 100644 --- a/spec/controllers/projects/tracings_controller_spec.rb +++ b/spec/controllers/projects/tracings_controller_spec.rb @@ -51,6 +51,16 @@ RSpec.describe Projects::TracingsController do it_behaves_like 'user with read access', :public it_behaves_like 'user with read access', :internal it_behaves_like 'user with read access', :private + + context 'feature flag disabled' do + before do + stub_feature_flags(monitor_tracing: false) + end + + it_behaves_like 'user without read access', :public + it_behaves_like 'user without read access', :internal + it_behaves_like 'user without read access', :private + end end context 'without maintainer role' do diff --git a/spec/features/projects/settings/secure_files_settings_spec.rb b/spec/features/projects/settings/secure_files_settings_spec.rb new file mode 100644 index 00000000000..c7c9cafc420 --- /dev/null +++ b/spec/features/projects/settings/secure_files_settings_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Secure Files Settings' do + let_it_be(:maintainer) { create(:user) } + let_it_be(:project) { create(:project, creator_id: maintainer.id) } + + before_all do + project.add_maintainer(maintainer) + end + + context 'when the :ci_secure_files feature flag is enabled' do + before do + stub_feature_flags(ci_secure_files: true) + + sign_in(user) + visit project_settings_ci_cd_path(project) + end + + context 'authenticated user with admin permissions' do + let(:user) { maintainer } + + it 'shows the secure files settings' do + expect(page).to have_content('Secure Files') + end + end + end + + context 'when the :ci_secure_files feature flag is disabled' do + before do + stub_feature_flags(ci_secure_files: false) + + sign_in(user) + visit project_settings_ci_cd_path(project) + end + + context 'authenticated user with admin permissions' do + let(:user) { maintainer } + + it 'does not shows the secure files settings' do + expect(page).not_to have_content('Secure Files') + end + end + end +end diff --git a/spec/helpers/ci/secure_files_helper_spec.rb b/spec/helpers/ci/secure_files_helper_spec.rb new file mode 100644 index 00000000000..02da44f56b2 --- /dev/null +++ b/spec/helpers/ci/secure_files_helper_spec.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::SecureFilesHelper do + let_it_be(:maintainer) { create(:user) } + let_it_be(:developer) { create(:user) } + let_it_be(:guest) { create(:user) } + let_it_be(:anonymous) { create(:user) } + let_it_be(:unconfirmed) { create(:user, :unconfirmed) } + let_it_be(:project) { create(:project, creator_id: maintainer.id) } + + before_all do + project.add_maintainer(maintainer) + project.add_developer(developer) + project.add_guest(guest) + end + + subject { helper.show_secure_files_setting(project, user) } + + describe '#show_secure_files_setting' do + context 'when the :ci_secure_files feature flag is enabled' do + before do + stub_feature_flags(ci_secure_files: true) + end + + context 'authenticated user with admin permissions' do + let(:user) { maintainer } + + it { is_expected.to be true } + end + + context 'authenticated user with read permissions' do + let(:user) { developer } + + it { is_expected.to be true } + end + + context 'authenticated user with guest permissions' do + let(:user) { guest } + + it { is_expected.to be false } + end + + context 'authenticated user with no permissions' do + let(:user) { anonymous } + + it { is_expected.to be false } + end + + context 'unconfirmed user' do + let(:user) { unconfirmed } + + it { is_expected.to be false } + end + + context 'unauthenticated user' do + let(:user) { nil } + + it { is_expected.to be false } + end + end + + context 'when the :ci_secure_files feature flag is disabled' do + before do + stub_feature_flags(ci_secure_files: false) + end + + context 'authenticated user with admin permissions' do + let(:user) { maintainer } + + it { is_expected.to be false } + end + end + end +end diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb index 0d7a183bb11..b7262629e0a 100644 --- a/spec/lib/gitlab/diff/file_spec.rb +++ b/spec/lib/gitlab/diff/file_spec.rb @@ -99,6 +99,22 @@ RSpec.describe Gitlab::Diff::File do end end + describe '#ipynb?' do + context 'is ipynb' do + let(:commit) { project.commit("532c837") } + + it 'is true' do + expect(diff_file.ipynb?).to be_truthy + end + end + + context 'is not ipynb' do + it 'is false' do + expect(diff_file.ipynb?).to be_falsey + end + end + end + describe '#has_renderable?' do context 'file is ipynb' do let(:commit) { project.commit("532c837") } diff --git a/spec/lib/gitlab/inactive_projects_deletion_warning_tracker_spec.rb b/spec/lib/gitlab/inactive_projects_deletion_warning_tracker_spec.rb new file mode 100644 index 00000000000..4eb2388f3f7 --- /dev/null +++ b/spec/lib/gitlab/inactive_projects_deletion_warning_tracker_spec.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe Gitlab::InactiveProjectsDeletionWarningTracker do + let_it_be(:project_id) { 1 } + + describe '.notified_projects', :clean_gitlab_redis_shared_state do + before do + freeze_time do + Gitlab::InactiveProjectsDeletionWarningTracker.new(project_id).mark_notified + end + end + + it 'returns the list of projects for which deletion warning email has been sent' do + expected_hash = { "project:1" => "#{Date.current}" } + + expect(Gitlab::InactiveProjectsDeletionWarningTracker.notified_projects).to eq(expected_hash) + end + end + + describe '.reset_all' do + before do + Gitlab::InactiveProjectsDeletionWarningTracker.new(project_id).mark_notified + end + + it 'deletes all the projects for which deletion warning email was sent' do + Gitlab::InactiveProjectsDeletionWarningTracker.reset_all + + expect(Gitlab::InactiveProjectsDeletionWarningTracker.notified_projects).to eq({}) + end + end + + describe '#notified?' do + before do + Gitlab::InactiveProjectsDeletionWarningTracker.new(project_id).mark_notified + end + + it 'returns true if the project has already been notified' do + expect(Gitlab::InactiveProjectsDeletionWarningTracker.new(project_id).notified?).to eq(true) + end + + it 'returns false if the project has not been notified' do + expect(Gitlab::InactiveProjectsDeletionWarningTracker.new(2).notified?).to eq(false) + end + end + + describe '#mark_notified' do + it 'marks the project as being notified' do + Gitlab::InactiveProjectsDeletionWarningTracker.new(project_id).mark_notified + + expect(Gitlab::InactiveProjectsDeletionWarningTracker.new(project_id).notified?).to eq(true) + end + end + + describe '#reset' do + before do + Gitlab::InactiveProjectsDeletionWarningTracker.new(project_id).mark_notified + end + + it 'resets the project as not being notified' do + Gitlab::InactiveProjectsDeletionWarningTracker.new(project_id).reset + + expect(Gitlab::InactiveProjectsDeletionWarningTracker.new(project_id).notified?).to eq(false) + end + end +end diff --git a/spec/lib/gitlab/usage_data_counters/ipynb_diff_activity_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/ipynb_diff_activity_counter_spec.rb new file mode 100644 index 00000000000..60c4424d2ae --- /dev/null +++ b/spec/lib/gitlab/usage_data_counters/ipynb_diff_activity_counter_spec.rb @@ -0,0 +1,107 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::UsageDataCounters::IpynbDiffActivityCounter, :clean_gitlab_redis_shared_state do + let(:user) { build(:user, id: 1) } + let(:for_mr) { false } + let(:for_commit) { false } + let(:first_note) { build(:note, author: user, id: 1) } + let(:second_note) { build(:note, author: user, id: 2) } + + before do + allow(first_note).to receive(:for_merge_request?).and_return(for_mr) + allow(second_note).to receive(:for_merge_request?).and_return(for_mr) + allow(first_note).to receive(:for_commit?).and_return(for_commit) + allow(second_note).to receive(:for_commit?).and_return(for_commit) + end + + subject do + described_class.note_created(first_note) + described_class.note_created(first_note) + described_class.note_created(second_note) + end + + shared_examples_for 'an action that tracks events' do + specify do + expect { 2.times { subject } } + .to change { event_count(action) }.by(2) + .and change { event_count(per_user_action) }.by(1) + end + end + + shared_examples_for 'an action that does not track events' do + specify do + expect { 2.times { subject } } + .to change { event_count(action) }.by(0) + .and change { event_count(per_user_action) }.by(0) + end + end + + describe '#track_note_created_in_ipynb_diff' do + context 'note is for commit' do + let(:for_commit) { true } + + it_behaves_like 'an action that tracks events' do + let(:action) {described_class::NOTE_CREATED_IN_IPYNB_DIFF_ACTION} + let(:per_user_action) {described_class::USER_CREATED_NOTE_IN_IPYNB_DIFF_ACTION} + end + + it_behaves_like 'an action that tracks events' do + let(:action) {described_class::NOTE_CREATED_IN_IPYNB_DIFF_COMMIT_ACTION} + let(:per_user_action) {described_class::USER_CREATED_NOTE_IN_IPYNB_DIFF_COMMIT_ACTION} + end + + it_behaves_like 'an action that does not track events' do + let(:action) {described_class::NOTE_CREATED_IN_IPYNB_DIFF_MR_ACTION} + let(:per_user_action) {described_class::USER_CREATED_NOTE_IN_IPYNB_DIFF_MR_ACTION} + end + end + + context 'note is for MR' do + let(:for_mr) { true } + + it_behaves_like 'an action that tracks events' do + let(:action) {described_class::NOTE_CREATED_IN_IPYNB_DIFF_MR_ACTION} + let(:per_user_action) {described_class::USER_CREATED_NOTE_IN_IPYNB_DIFF_MR_ACTION} + end + + it_behaves_like 'an action that tracks events' do + let(:action) {described_class::NOTE_CREATED_IN_IPYNB_DIFF_ACTION} + let(:per_user_action) {described_class::USER_CREATED_NOTE_IN_IPYNB_DIFF_ACTION} + end + + it_behaves_like 'an action that does not track events' do + let(:action) {described_class::NOTE_CREATED_IN_IPYNB_DIFF_COMMIT_ACTION} + let(:per_user_action) {described_class::USER_CREATED_NOTE_IN_IPYNB_DIFF_COMMIT_ACTION} + end + end + + context 'note is for neither MR nor Commit' do + it_behaves_like 'an action that does not track events' do + let(:action) {described_class::NOTE_CREATED_IN_IPYNB_DIFF_ACTION} + let(:per_user_action) {described_class::USER_CREATED_NOTE_IN_IPYNB_DIFF_ACTION} + end + + it_behaves_like 'an action that does not track events' do + let(:action) {described_class::NOTE_CREATED_IN_IPYNB_DIFF_MR_ACTION} + let(:per_user_action) {described_class::USER_CREATED_NOTE_IN_IPYNB_DIFF_MR_ACTION} + end + + it_behaves_like 'an action that does not track events' do + let(:action) {described_class::NOTE_CREATED_IN_IPYNB_DIFF_COMMIT_ACTION} + let(:per_user_action) {described_class::USER_CREATED_NOTE_IN_IPYNB_DIFF_COMMIT_ACTION} + end + end + end + + private + + def event_count(event_name) + Gitlab::UsageDataCounters::HLLRedisCounter.unique_events( + event_names: event_name, + start_date: 2.weeks.ago, + end_date: 2.weeks.from_now + ) + end +end diff --git a/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb b/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb index 61b60484692..b11c9db4e46 100644 --- a/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb +++ b/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb @@ -86,6 +86,14 @@ RSpec.describe Sidebars::Projects::Menus::MonitorMenu do let(:item_id) { :tracing } it_behaves_like 'access rights checks' + + context 'when feature disabled' do + before do + stub_feature_flags(monitor_tracing: false) + end + + specify { is_expected.to be_nil } + end end describe 'Error Tracking' do diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index dbced00754a..9ee08688056 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -1348,5 +1348,17 @@ RSpec.describe ApplicationSetting do it { is_expected.to validate_numericality_of(:inactive_projects_delete_after_months).is_greater_than(0) } it { is_expected.to validate_numericality_of(:inactive_projects_min_size_mb).is_greater_than_or_equal_to(0) } + + it "deletes the redis key used for tracking inactive projects deletion warning emails when setting is updated", + :clean_gitlab_redis_shared_state do + Gitlab::Redis::SharedState.with do |redis| + redis.hset("inactive_projects_deletion_warning_email_notified", "project:1", "2020-01-01") + end + + Gitlab::Redis::SharedState.with do |redis| + expect { setting.update!(inactive_projects_delete_after_months: 6) } + .to change { redis.hgetall('inactive_projects_deletion_warning_email_notified') }.to({}) + end + end end end diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 26e86207fd6..2c1bbfcb35f 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -834,7 +834,13 @@ RSpec.describe Event do end end - context 'when a project was updated more than 1 hour ago' do + context 'when a project was updated more than 1 hour ago', :clean_gitlab_redis_shared_state do + before do + ::Gitlab::Redis::SharedState.with do |redis| + redis.hset('inactive_projects_deletion_warning_email_notified', "project:#{project.id}", Date.current) + end + end + it 'updates the project' do project.touch(:last_activity_at, time: 1.year.ago) # rubocop: disable Rails/SkipsModelValidations @@ -845,6 +851,17 @@ RSpec.describe Event do expect(project.last_activity_at).to be_like_time(event.created_at) expect(project.updated_at).to be_like_time(event.created_at) end + + it "deletes the redis key for if the project was inactive" do + Gitlab::Redis::SharedState.with do |redis| + expect(redis).to receive(:hdel).with('inactive_projects_deletion_warning_email_notified', + "project:#{project.id}") + end + + project.touch(:last_activity_at, time: 1.year.ago) # rubocop: disable Rails/SkipsModelValidations + + create_push_event(project, project.first_owner) + end end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 7b01c653981..ed5b3d4e0be 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -8,6 +8,7 @@ RSpec.describe Project, factory_default: :keep do include ExternalAuthorizationServiceHelpers include ReloadHelpers include StubGitlabCalls + include ProjectHelpers using RSpec::Parameterized::TableSyntax let_it_be(:namespace) { create_default(:namespace).freeze } @@ -8267,6 +8268,28 @@ RSpec.describe Project, factory_default: :keep do it_behaves_like 'returns true if project is inactive' end + describe '.inactive' do + before do + stub_application_setting(inactive_projects_min_size_mb: 5) + stub_application_setting(inactive_projects_send_warning_email_after_months: 12) + end + + it 'returns projects that are inactive' do + create_project_with_statistics.tap do |project| + project.update!(last_activity_at: Time.current) + end + create_project_with_statistics.tap do |project| + project.update!(last_activity_at: 13.months.ago) + end + inactive_large_project = create_project_with_statistics(with_data: true, size_multiplier: 2.gigabytes) + .tap { |project| project.update!(last_activity_at: 2.years.ago) } + create_project_with_statistics(with_data: true, size_multiplier: 2.gigabytes) + .tap { |project| project.update!(last_activity_at: 1.month.ago) } + + expect(described_class.inactive).to contain_exactly(inactive_large_project) + end + end + private def finish_job(export_job) diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb index b0410123630..c72a9465f20 100644 --- a/spec/services/notes/create_service_spec.rb +++ b/spec/services/notes/create_service_spec.rb @@ -168,7 +168,6 @@ RSpec.describe Notes::CreateService do before do project_with_repo.add_maintainer(user) end - context 'when eligible to have a note diff file' do let(:new_opts) do opts.merge(in_reply_to_discussion_id: nil, @@ -196,6 +195,39 @@ RSpec.describe Notes::CreateService do described_class.new(project_with_repo, user, new_opts).execute(skip_capture_diff_note_position: true) end end + + it 'does not track ipynb note usage data' do + expect(::Gitlab::UsageDataCounters::IpynbDiffActivityCounter).not_to receive(:note_created) + + described_class.new(project_with_repo, user, new_opts).execute + end + + context 'is ipynb file' do + before do + allow_any_instance_of(::Gitlab::Diff::File).to receive(:ipynb?).and_return(true) + stub_feature_flags(ipynbdiff_notes_tracker: false) + end + + context ':ipynbdiff_notes_tracker is off' do + it 'does not track ipynb note usage data' do + expect(::Gitlab::UsageDataCounters::IpynbDiffActivityCounter).not_to receive(:note_created) + + described_class.new(project_with_repo, user, new_opts).execute + end + end + + context ':ipynbdiff_notes_tracker is on' do + before do + stub_feature_flags(ipynbdiff_notes_tracker: true) + end + + it 'tracks ipynb diff note creation' do + expect(::Gitlab::UsageDataCounters::IpynbDiffActivityCounter).to receive(:note_created) + + described_class.new(project_with_repo, user, new_opts).execute + end + end + end end context 'when DiffNote is a reply' do diff --git a/spec/support/helpers/project_helpers.rb b/spec/support/helpers/project_helpers.rb index 89f0163b4b6..2ea6405e48c 100644 --- a/spec/support/helpers/project_helpers.rb +++ b/spec/support/helpers/project_helpers.rb @@ -24,4 +24,20 @@ module ProjectHelpers project.update!(params) end + + def create_project_with_statistics(namespace = nil, with_data: false, size_multiplier: 1) + project = namespace.present? ? create(:project, namespace: namespace) : create(:project) + project.tap do |p| + create(:project_statistics, project: p, with_data: with_data, size_multiplier: size_multiplier) + end + end + + def grace_months_after_deletion_notification + (::Gitlab::CurrentSettings.inactive_projects_delete_after_months - + ::Gitlab::CurrentSettings.inactive_projects_send_warning_email_after_months).months + end + + def deletion_date + Date.parse(grace_months_after_deletion_notification.from_now.to_s).to_s + end end diff --git a/spec/workers/every_sidekiq_worker_spec.rb b/spec/workers/every_sidekiq_worker_spec.rb index adee70fbf87..ca858bcba19 100644 --- a/spec/workers/every_sidekiq_worker_spec.rb +++ b/spec/workers/every_sidekiq_worker_spec.rb @@ -389,6 +389,7 @@ RSpec.describe 'Every Sidekiq worker' do 'ProjectTemplateExportWorker' => false, 'ProjectUpdateRepositoryStorageWorker' => 3, 'Projects::GitGarbageCollectWorker' => false, + 'Projects::InactiveProjectsDeletionNotificationWorker' => 3, 'Projects::PostCreationWorker' => 3, 'Projects::ScheduleBulkRepositoryShardMovesWorker' => 3, 'Projects::UpdateRepositoryStorageWorker' => 3, diff --git a/spec/workers/projects/inactive_projects_deletion_cron_worker_spec.rb b/spec/workers/projects/inactive_projects_deletion_cron_worker_spec.rb new file mode 100644 index 00000000000..0e7b4ea504c --- /dev/null +++ b/spec/workers/projects/inactive_projects_deletion_cron_worker_spec.rb @@ -0,0 +1,139 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::InactiveProjectsDeletionCronWorker do + include ProjectHelpers + + describe "#perform" do + subject(:worker) { described_class.new } + + let_it_be(:admin_user) { create(:user, :admin) } + let_it_be(:non_admin_user) { create(:user) } + let_it_be(:new_blank_project) do + create_project_with_statistics.tap do |project| + project.update!(last_activity_at: Time.current) + end + end + + let_it_be(:inactive_blank_project) do + create_project_with_statistics.tap do |project| + project.update!(last_activity_at: 13.months.ago) + end + end + + let_it_be(:inactive_large_project) do + create_project_with_statistics(with_data: true, size_multiplier: 2.gigabytes) + .tap { |project| project.update!(last_activity_at: 2.years.ago) } + end + + let_it_be(:active_large_project) do + create_project_with_statistics(with_data: true, size_multiplier: 2.gigabytes) + .tap { |project| project.update!(last_activity_at: 1.month.ago) } + end + + before do + stub_application_setting(inactive_projects_min_size_mb: 5) + stub_application_setting(inactive_projects_send_warning_email_after_months: 12) + stub_application_setting(inactive_projects_delete_after_months: 14) + end + + context 'when delete inactive projects feature is disabled' do + before do + stub_application_setting(delete_inactive_projects: false) + end + + it 'does not invoke Projects::InactiveProjectsDeletionNotificationWorker' do + expect(::Projects::InactiveProjectsDeletionNotificationWorker).not_to receive(:perform_in) + expect(::Projects::DestroyService).not_to receive(:new) + + worker.perform + end + + it 'does not delete the inactive projects' do + worker.perform + + expect(inactive_large_project.reload.pending_delete).to eq(false) + end + end + + context 'when delete inactive projects feature is enabled' do + before do + stub_application_setting(delete_inactive_projects: true) + end + + context 'when feature flag is disabled' do + before do + stub_feature_flags(inactive_projects_deletion: false) + end + + it 'does not invoke Projects::InactiveProjectsDeletionNotificationWorker' do + expect(::Projects::InactiveProjectsDeletionNotificationWorker).not_to receive(:perform_in) + expect(::Projects::DestroyService).not_to receive(:new) + + worker.perform + end + + it 'does not delete the inactive projects' do + worker.perform + + expect(inactive_large_project.reload.pending_delete).to eq(false) + end + end + + context 'when feature flag is enabled', :clean_gitlab_redis_shared_state, :sidekiq_inline do + let_it_be(:delay) { anything } + + before do + stub_feature_flags(inactive_projects_deletion: true) + end + + it 'invokes Projects::InactiveProjectsDeletionNotificationWorker for inactive projects' do + Gitlab::Redis::SharedState.with do |redis| + expect(redis).to receive(:hset).with('inactive_projects_deletion_warning_email_notified', + "project:#{inactive_large_project.id}", Date.current) + end + expect(::Projects::InactiveProjectsDeletionNotificationWorker).to receive(:perform_in).with( + delay, inactive_large_project.id, deletion_date).and_call_original + expect(::Projects::DestroyService).not_to receive(:new) + + worker.perform + end + + it 'does not invoke InactiveProjectsDeletionNotificationWorker for already notified inactive projects' do + Gitlab::Redis::SharedState.with do |redis| + redis.hset('inactive_projects_deletion_warning_email_notified', "project:#{inactive_large_project.id}", + Date.current.to_s) + end + + expect(::Projects::InactiveProjectsDeletionNotificationWorker).not_to receive(:perform_in) + expect(::Projects::DestroyService).not_to receive(:new) + + worker.perform + end + + it 'invokes Projects::DestroyService for projects that are inactive even after being notified' do + Gitlab::Redis::SharedState.with do |redis| + redis.hset('inactive_projects_deletion_warning_email_notified', "project:#{inactive_large_project.id}", + 15.months.ago.to_date.to_s) + end + + expect(::Projects::InactiveProjectsDeletionNotificationWorker).not_to receive(:perform_in) + expect(::Projects::DestroyService).to receive(:new).with(inactive_large_project, admin_user, {}) + .at_least(:once).and_call_original + + worker.perform + + expect(inactive_large_project.reload.pending_delete).to eq(true) + + Gitlab::Redis::SharedState.with do |redis| + expect(redis.hget('inactive_projects_deletion_warning_email_notified', + "project:#{inactive_large_project.id}")).to be_nil + end + end + end + + it_behaves_like 'an idempotent worker' + end + end +end diff --git a/spec/workers/projects/inactive_projects_deletion_notification_worker_spec.rb b/spec/workers/projects/inactive_projects_deletion_notification_worker_spec.rb new file mode 100644 index 00000000000..3ddfec0d346 --- /dev/null +++ b/spec/workers/projects/inactive_projects_deletion_notification_worker_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::InactiveProjectsDeletionNotificationWorker do + describe "#perform" do + subject(:worker) { described_class.new } + + let_it_be(:deletion_date) { Date.current } + let_it_be(:non_existing_project_id) { non_existing_record_id } + let_it_be(:project) { create(:project) } + + it 'invokes NotificationService and calls inactive_project_deletion_warning' do + expect_next_instance_of(NotificationService) do |notification| + expect(notification).to receive(:inactive_project_deletion_warning).with(project, deletion_date) + end + + worker.perform(project.id, deletion_date) + end + + it 'adds the project_id to redis key that tracks the deletion warning emails' do + worker.perform(project.id, deletion_date) + + Gitlab::Redis::SharedState.with do |redis| + expect(redis.hget('inactive_projects_deletion_warning_email_notified', + "project:#{project.id}")).to eq(Date.current.to_s) + end + end + + it 'rescues and logs the exception if project does not exist' do + expect(Gitlab::ErrorTracking).to receive(:log_exception).with(instance_of(ActiveRecord::RecordNotFound), + { project_id: non_existing_project_id }) + + worker.perform(non_existing_project_id, deletion_date) + end + + it_behaves_like 'an idempotent worker' do + let(:job_args) { [project.id, deletion_date] } + end + end +end diff --git a/vendor/project_templates/cluster_management.tar.gz b/vendor/project_templates/cluster_management.tar.gz index 97e18dc859b..428d217a447 100644 Binary files a/vendor/project_templates/cluster_management.tar.gz and b/vendor/project_templates/cluster_management.tar.gz differ