Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-05-13 21:08:46 +00:00
parent 9adada1187
commit 8d9963a8e3
59 changed files with 1890 additions and 307 deletions

View File

@ -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

View File

@ -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'

View File

@ -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);
},
};

View File

@ -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;

View File

@ -15,6 +15,7 @@ module Projects
feature_category :tracing
def show
render_404 unless Feature.enabled?(:monitor_tracing, @project)
end
private

View File

@ -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

View File

@ -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')

View File

@ -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)

View File

@ -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) }

View File

@ -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

View File

@ -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'

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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({})

View File

@ -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

View File

@ -361,6 +361,8 @@
- 1
- - projects_git_garbage_collect
- 1
- - projects_inactive_projects_deletion_notification
- 1
- - projects_post_creation
- 1
- - projects_process_sync_events

View File

@ -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"

View File

@ -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]

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.). |

View File

@ -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

View File

@ -71,9 +71,7 @@ GITLAB_TRACING=opentracing://<driver>?<param_name>=<param_value>&<param_name_2>=
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.

View File

@ -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

View File

@ -127,7 +127,9 @@ or upgrade command:
```
1. Install the specific `gitlab-ee` package by using one of the following commands
and replacing `<version>` with the version you found in the previous step:
and replacing `<version>` 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

View File

@ -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:

View File

@ -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. | <!--- start_remove The following content will be removed on remove_date: '2022-08-22' -->
| `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. |
<!--- end_remove -->
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. |
<!--- start_remove The following content will be removed on remove_date: '2022-08-22' -->
### `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). |
<!--- end_remove -->
## `scan` action type
This action executes the selected `scan` with additional parameters when conditions for at least one

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 ""

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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") }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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