summaryrefslogtreecommitdiff
path: root/lib/gitlab
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-05-19 15:44:42 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-05-19 15:44:42 +0000
commit4555e1b21c365ed8303ffb7a3325d773c9b8bf31 (patch)
tree5423a1c7516cffe36384133ade12572cf709398d /lib/gitlab
parente570267f2f6b326480d284e0164a6464ba4081bc (diff)
downloadgitlab-ce-4555e1b21c365ed8303ffb7a3325d773c9b8bf31.tar.gz
Add latest changes from gitlab-org/gitlab@13-12-stable-eev13.12.0-rc42
Diffstat (limited to 'lib/gitlab')
-rw-r--r--lib/gitlab/access.rb2
-rw-r--r--lib/gitlab/alert_management/payload.rb2
-rw-r--r--lib/gitlab/alert_management/payload/base.rb4
-rw-r--r--lib/gitlab/alert_management/payload/generic.rb2
-rw-r--r--lib/gitlab/analytics/cycle_analytics/average.rb5
-rw-r--r--lib/gitlab/analytics/cycle_analytics/base_query_builder.rb21
-rw-r--r--lib/gitlab/analytics/cycle_analytics/data_collector.rb21
-rw-r--r--lib/gitlab/analytics/cycle_analytics/median.rb5
-rw-r--r--lib/gitlab/analytics/cycle_analytics/records_fetcher.rb4
-rw-r--r--lib/gitlab/analytics/cycle_analytics/sorting.rb40
-rw-r--r--lib/gitlab/analytics/cycle_analytics/stage_events.rb2
-rw-r--r--lib/gitlab/analytics/cycle_analytics/stage_events/code_stage_start.rb5
-rw-r--r--lib/gitlab/analytics/cycle_analytics/stage_events/issue_created.rb4
-rw-r--r--lib/gitlab/analytics/cycle_analytics/stage_events/issue_deployed_to_production.rb7
-rw-r--r--lib/gitlab/analytics/cycle_analytics/stage_events/issue_first_mentioned_in_commit.rb4
-rw-r--r--lib/gitlab/analytics/cycle_analytics/stage_events/issue_stage_end.rb5
-rw-r--r--lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_created.rb4
-rw-r--r--lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_first_deployed_to_production.rb4
-rw-r--r--lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_finished.rb4
-rw-r--r--lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_started.rb4
-rw-r--r--lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_merged.rb4
-rw-r--r--lib/gitlab/analytics/cycle_analytics/stage_events/metrics_based_stage_event.rb7
-rw-r--r--lib/gitlab/analytics/cycle_analytics/stage_events/plan_stage_start.rb5
-rw-r--r--lib/gitlab/analytics/cycle_analytics/stage_events/stage_event.rb12
-rw-r--r--lib/gitlab/analytics/cycle_analytics/stage_query_helpers.rb26
-rw-r--r--lib/gitlab/api_authentication/token_locator.rb57
-rw-r--r--lib/gitlab/api_authentication/token_resolver.rb48
-rw-r--r--lib/gitlab/application_context.rb2
-rw-r--r--lib/gitlab/application_rate_limiter.rb1
-rw-r--r--lib/gitlab/artifacts/migration_helper.rb33
-rw-r--r--lib/gitlab/auth.rb6
-rw-r--r--lib/gitlab/auth/auth_finders.rb4
-rw-r--r--lib/gitlab/auth/database/authentication.rb1
-rw-r--r--lib/gitlab/auth/ldap/access.rb2
-rw-r--r--lib/gitlab/auth/ldap/adapter.rb2
-rw-r--r--lib/gitlab/auth/ldap/config.rb6
-rw-r--r--lib/gitlab/auth/ldap/person.rb2
-rw-r--r--lib/gitlab/auth/ldap/user.rb2
-rw-r--r--lib/gitlab/auth/o_auth/auth_hash.rb4
-rw-r--r--lib/gitlab/auth/o_auth/user.rb4
-rw-r--r--lib/gitlab/auth/result.rb2
-rw-r--r--lib/gitlab/auth/saml/config.rb2
-rw-r--r--lib/gitlab/auth/saml/user.rb2
-rw-r--r--lib/gitlab/authorized_keys.rb2
-rw-r--r--lib/gitlab/background_migration/backfill_namespace_traversal_ids_children.rb76
-rw-r--r--lib/gitlab/background_migration/backfill_namespace_traversal_ids_roots.rb51
-rw-r--r--lib/gitlab/background_migration/backfill_snippet_repositories.rb6
-rw-r--r--lib/gitlab/background_migration/backfill_version_data_from_gitaly.rb2
-rw-r--r--lib/gitlab/background_migration/calculate_wiki_sizes.rb2
-rw-r--r--lib/gitlab/background_migration/copy_column_using_background_migration_job.rb37
-rw-r--r--lib/gitlab/background_migration/drop_invalid_vulnerabilities.rb37
-rw-r--r--lib/gitlab/background_migration/fill_valid_time_for_pages_domain_certificate.rb2
-rw-r--r--lib/gitlab/background_migration/fix_orphan_promoted_issues.rb2
-rw-r--r--lib/gitlab/background_migration/fix_ruby_object_in_audit_events.rb2
-rw-r--r--lib/gitlab/background_migration/generate_gitlab_subscriptions.rb2
-rw-r--r--lib/gitlab/background_migration/migrate_approver_to_approval_rules.rb2
-rw-r--r--lib/gitlab/background_migration/migrate_approver_to_approval_rules_check_progress.rb2
-rw-r--r--lib/gitlab/background_migration/migrate_approver_to_approval_rules_in_batch.rb2
-rw-r--r--lib/gitlab/background_migration/migrate_devops_segments_to_groups.rb2
-rw-r--r--lib/gitlab/background_migration/migrate_project_taggings_context_from_tags_to_topics.rb21
-rw-r--r--lib/gitlab/background_migration/migrate_security_scans.rb2
-rw-r--r--lib/gitlab/background_migration/move_container_registry_enabled_to_project_feature.rb17
-rw-r--r--lib/gitlab/background_migration/move_epic_issues_after_epics.rb2
-rw-r--r--lib/gitlab/background_migration/populate_any_approval_rule_for_merge_requests.rb2
-rw-r--r--lib/gitlab/background_migration/populate_any_approval_rule_for_projects.rb2
-rw-r--r--lib/gitlab/background_migration/populate_namespace_statistics.rb2
-rw-r--r--lib/gitlab/background_migration/populate_personal_snippet_statistics.rb2
-rw-r--r--lib/gitlab/background_migration/populate_project_snippet_statistics.rb4
-rw-r--r--lib/gitlab/background_migration/populate_resolved_on_default_branch_column.rb2
-rw-r--r--lib/gitlab/background_migration/populate_uuids_for_security_findings.rb2
-rw-r--r--lib/gitlab/background_migration/populate_vulnerability_feedback_pipeline_id.rb2
-rw-r--r--lib/gitlab/background_migration/populate_vulnerability_historical_statistics.rb2
-rw-r--r--lib/gitlab/background_migration/prune_orphaned_geo_events.rb2
-rw-r--r--lib/gitlab/background_migration/recalculate_project_authorizations.rb32
-rw-r--r--lib/gitlab/background_migration/remove_duplicate_cs_findings.rb2
-rw-r--r--lib/gitlab/background_migration/remove_duplicated_cs_findings_without_vulnerability_id.rb2
-rw-r--r--lib/gitlab/background_migration/remove_inaccessible_epic_todos.rb2
-rw-r--r--lib/gitlab/background_migration/remove_undefined_occurrence_confidence_level.rb2
-rw-r--r--lib/gitlab/background_migration/remove_undefined_occurrence_severity_level.rb2
-rw-r--r--lib/gitlab/background_migration/remove_undefined_vulnerability_confidence_level.rb2
-rw-r--r--lib/gitlab/background_migration/remove_undefined_vulnerability_severity_level.rb2
-rw-r--r--lib/gitlab/background_migration/sync_blocking_issues_count.rb2
-rw-r--r--lib/gitlab/background_migration/update_location_fingerprint_for_container_scanning_findings.rb2
-rw-r--r--lib/gitlab/background_migration/update_timelogs_project_id.rb44
-rw-r--r--lib/gitlab/background_migration/update_vulnerabilities_from_dismissal_feedback.rb2
-rw-r--r--lib/gitlab/background_migration/update_vulnerabilities_to_dismissed.rb2
-rw-r--r--lib/gitlab/background_migration/update_vulnerability_confidence.rb2
-rw-r--r--lib/gitlab/background_migration/user_mentions/models/namespace.rb2
-rw-r--r--lib/gitlab/bare_repository_import/importer.rb4
-rw-r--r--lib/gitlab/blob_helper.rb16
-rw-r--r--lib/gitlab/cache.rb18
-rw-r--r--lib/gitlab/changelog/committer.rb2
-rw-r--r--lib/gitlab/changelog/parser.rb2
-rw-r--r--lib/gitlab/chat/responder.rb4
-rw-r--r--lib/gitlab/checks/base_checker.rb2
-rw-r--r--lib/gitlab/checks/change_access.rb2
-rw-r--r--lib/gitlab/checks/diff_check.rb2
-rw-r--r--lib/gitlab/checks/matching_merge_request.rb2
-rw-r--r--lib/gitlab/ci/ansi2html.rb6
-rw-r--r--lib/gitlab/ci/build/cache.rb31
-rw-r--r--lib/gitlab/ci/build/releaser.rb11
-rw-r--r--lib/gitlab/ci/config.rb7
-rw-r--r--lib/gitlab/ci/config/entry/cache.rb110
-rw-r--r--lib/gitlab/ci/config/entry/caches.rb40
-rw-r--r--lib/gitlab/ci/config/entry/default.rb2
-rw-r--r--lib/gitlab/ci/config/entry/job.rb4
-rw-r--r--lib/gitlab/ci/config/entry/need.rb2
-rw-r--r--lib/gitlab/ci/config/entry/needs.rb2
-rw-r--r--lib/gitlab/ci/config/entry/root.rb2
-rw-r--r--lib/gitlab/ci/features.rb20
-rw-r--r--lib/gitlab/ci/jwt.rb2
-rw-r--r--lib/gitlab/ci/parsers.rb6
-rw-r--r--lib/gitlab/ci/parsers/coverage/cobertura.rb2
-rw-r--r--lib/gitlab/ci/parsers/terraform/tfplan.rb2
-rw-r--r--lib/gitlab/ci/parsers/test/junit.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/config/content.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/config/process.rb3
-rw-r--r--lib/gitlab/ci/pipeline/chain/helpers.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/limit/activity.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/limit/job_activity.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/limit/size.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/skip.rb9
-rw-r--r--lib/gitlab/ci/pipeline/chain/validate/abilities.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/validate/external.rb4
-rw-r--r--lib/gitlab/ci/pipeline/chain/validate/security_orchestration_policy.rb25
-rw-r--r--lib/gitlab/ci/pipeline/metrics.rb22
-rw-r--r--lib/gitlab/ci/queue/metrics.rb2
-rw-r--r--lib/gitlab/ci/reports/codequality_mr_diff.rb6
-rw-r--r--lib/gitlab/ci/reports/test_failure_history.rb2
-rw-r--r--lib/gitlab/ci/status/build/failed.rb6
-rw-r--r--lib/gitlab/ci/status/core.rb2
-rw-r--r--lib/gitlab/ci/syntax_templates/Artifacts example.gitlab-ci.yml52
-rw-r--r--lib/gitlab/ci/syntax_templates/Before_script and after_script example.gitlab-ci.yml36
-rw-r--r--lib/gitlab/ci/syntax_templates/Manual jobs example.gitlab-ci.yml53
-rw-r--r--lib/gitlab/ci/syntax_templates/Multi-stage pipeline example.gitlab-ci.yml33
-rw-r--r--lib/gitlab/ci/syntax_templates/Variables example.gitlab-ci.yml47
-rw-r--r--lib/gitlab/ci/templates/Getting-started.yml39
-rw-r--r--lib/gitlab/ci/templates/Indeni.Cloudrail.gitlab-ci.yml (renamed from lib/gitlab/ci/templates/Indeni.Cloudrail.gitlab-ci-.yml)8
-rw-r--r--lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.latest.gitlab-ci.yml77
-rw-r--r--lib/gitlab/ci/templates/Jobs/Build.gitlab-ci.yml9
-rw-r--r--lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml24
-rw-r--r--lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Security/API-Fuzzing.latest.gitlab-ci.yml256
-rw-r--r--lib/gitlab/ci/templates/Security/DAST-API.gitlab-ci.yml48
-rw-r--r--lib/gitlab/ci/templates/Security/DAST.latest.gitlab-ci.yml86
-rw-r--r--lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml12
-rw-r--r--lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml33
-rw-r--r--lib/gitlab/ci/templates/Security/Secure-Binaries.gitlab-ci.yml20
-rw-r--r--lib/gitlab/ci/templates/Terraform.gitlab-ci.yml6
-rw-r--r--lib/gitlab/ci/templates/Verify/Browser-Performance.latest.gitlab-ci.yml52
-rw-r--r--lib/gitlab/ci/trace.rb2
-rw-r--r--lib/gitlab/ci/trace/stream.rb2
-rw-r--r--lib/gitlab/ci/yaml_processor.rb2
-rw-r--r--lib/gitlab/class_attributes.rb16
-rw-r--r--lib/gitlab/cleanup/orphan_job_artifact_files.rb2
-rw-r--r--lib/gitlab/cleanup/orphan_job_artifact_files_batch.rb2
-rw-r--r--lib/gitlab/cleanup/project_uploads.rb4
-rw-r--r--lib/gitlab/cluster/lifecycle_events.rb2
-rw-r--r--lib/gitlab/conan_token.rb3
-rw-r--r--lib/gitlab/consul/internal.rb4
-rw-r--r--lib/gitlab/content_security_policy/config_loader.rb48
-rw-r--r--lib/gitlab/current_settings.rb2
-rw-r--r--lib/gitlab/cycle_analytics/summary/base.rb4
-rw-r--r--lib/gitlab/cycle_analytics/summary/deploy.rb2
-rw-r--r--lib/gitlab/data_builder/build.rb1
-rw-r--r--lib/gitlab/data_builder/deployment.rb3
-rw-r--r--lib/gitlab/data_builder/pipeline.rb3
-rw-r--r--lib/gitlab/database.rb65
-rw-r--r--lib/gitlab/database/as_with_materialized.rb14
-rw-r--r--lib/gitlab/database/background_migration/batch_optimizer.rb67
-rw-r--r--lib/gitlab/database/background_migration/batched_job.rb27
-rw-r--r--lib/gitlab/database/background_migration/batched_migration.rb41
-rw-r--r--lib/gitlab/database/background_migration/batched_migration_runner.rb24
-rw-r--r--lib/gitlab/database/background_migration/batched_migration_wrapper.rb44
-rw-r--r--lib/gitlab/database/background_migration_job.rb2
-rw-r--r--lib/gitlab/database/consistency.rb2
-rw-r--r--lib/gitlab/database/loose_index_scan_distinct_count.rb4
-rw-r--r--lib/gitlab/database/migration_helpers.rb155
-rw-r--r--lib/gitlab/database/migration_helpers/cascading_namespace_settings.rb76
-rw-r--r--lib/gitlab/database/migrations/instrumentation.rb7
-rw-r--r--lib/gitlab/database/migrations/observers.rb3
-rw-r--r--lib/gitlab/database/migrations/observers/query_log.rb27
-rw-r--r--lib/gitlab/database/partitioning/partition_creator.rb2
-rw-r--r--lib/gitlab/database/partitioning_migration_helpers/index_helpers.rb2
-rw-r--r--lib/gitlab/database/postgres_hll/batch_distinct_counter.rb6
-rw-r--r--lib/gitlab/database/reindexing/concurrent_reindex.rb20
-rw-r--r--lib/gitlab/database/reindexing/coordinator.rb2
-rw-r--r--lib/gitlab/database/reindexing/grafana_notifier.rb2
-rw-r--r--lib/gitlab/database/rename_table_helpers.rb33
-rw-r--r--lib/gitlab/database/schema_cache_with_renamed_table.rb55
-rw-r--r--lib/gitlab/database/with_lock_retries.rb4
-rw-r--r--lib/gitlab/database/with_lock_retries_outside_transaction.rb41
-rw-r--r--lib/gitlab/default_branch.rb10
-rw-r--r--lib/gitlab/diff/file_collection/base.rb2
-rw-r--r--lib/gitlab/diff/highlight.rb19
-rw-r--r--lib/gitlab/doctor/secrets.rb2
-rw-r--r--lib/gitlab/email/handler/create_issue_handler.rb10
-rw-r--r--lib/gitlab/email/handler/create_merge_request_handler.rb4
-rw-r--r--lib/gitlab/email/handler/reply_processing.rb2
-rw-r--r--lib/gitlab/email/handler/service_desk_handler.rb20
-rw-r--r--lib/gitlab/email/message/in_product_marketing.rb19
-rw-r--r--lib/gitlab/email/message/in_product_marketing/base.rb154
-rw-r--r--lib/gitlab/email/message/in_product_marketing/create.rb101
-rw-r--r--lib/gitlab/email/message/in_product_marketing/helper.rb44
-rw-r--r--lib/gitlab/email/message/in_product_marketing/team.rb80
-rw-r--r--lib/gitlab/email/message/in_product_marketing/trial.rb75
-rw-r--r--lib/gitlab/email/message/in_product_marketing/verify.rb93
-rw-r--r--lib/gitlab/email/receiver.rb84
-rw-r--r--lib/gitlab/email/reply_parser.rb2
-rw-r--r--lib/gitlab/email/service_desk_receiver.rb15
-rw-r--r--lib/gitlab/encoding_helper.rb31
-rw-r--r--lib/gitlab/encrypted_configuration.rb4
-rw-r--r--lib/gitlab/error_tracking.rb3
-rw-r--r--lib/gitlab/error_tracking/context_payload_generator.rb2
-rw-r--r--lib/gitlab/error_tracking/processor/context_payload_processor.rb11
-rw-r--r--lib/gitlab/error_tracking/processor/grpc_error_processor.rb76
-rw-r--r--lib/gitlab/error_tracking/processor/sidekiq_processor.rb29
-rw-r--r--lib/gitlab/etag_caching/router/graphql.rb5
-rw-r--r--lib/gitlab/etag_caching/router/restful.rb2
-rw-r--r--lib/gitlab/exclusive_lease.rb2
-rw-r--r--lib/gitlab/experimentation.rb10
-rw-r--r--lib/gitlab/experimentation/controller_concern.rb19
-rw-r--r--lib/gitlab/external_authorization/client.rb2
-rw-r--r--lib/gitlab/fake_application_settings.rb2
-rw-r--r--lib/gitlab/faraday/error_callback.rb2
-rw-r--r--lib/gitlab/favicon.rb2
-rw-r--r--lib/gitlab/file_hook.rb2
-rw-r--r--lib/gitlab/fogbugz_import/repository.rb2
-rw-r--r--lib/gitlab/git/blob.rb4
-rw-r--r--lib/gitlab/git/branch.rb4
-rw-r--r--lib/gitlab/git/commit.rb2
-rw-r--r--lib/gitlab/git/conflict/resolver.rb4
-rw-r--r--lib/gitlab/git/repository.rb16
-rw-r--r--lib/gitlab/git/rugged_impl/repository.rb2
-rw-r--r--lib/gitlab/git/wiki.rb26
-rw-r--r--lib/gitlab/git/wraps_gitaly_errors.rb8
-rw-r--r--lib/gitlab/git_access.rb4
-rw-r--r--lib/gitlab/git_access_design.rb2
-rw-r--r--lib/gitlab/git_access_snippet.rb2
-rw-r--r--lib/gitlab/git_access_wiki.rb2
-rw-r--r--lib/gitlab/gitaly_client/blob_service.rb2
-rw-r--r--lib/gitlab/gitaly_client/blobs_stitcher.rb10
-rw-r--r--lib/gitlab/gitaly_client/call.rb2
-rw-r--r--lib/gitlab/gitaly_client/operation_service.rb4
-rw-r--r--lib/gitlab/gitaly_client/ref_service.rb2
-rw-r--r--lib/gitlab/gitaly_client/remote_service.rb19
-rw-r--r--lib/gitlab/gitaly_client/repository_service.rb2
-rw-r--r--lib/gitlab/gitaly_client/storage_settings.rb2
-rw-r--r--lib/gitlab/gitaly_client/wiki_service.rb34
-rw-r--r--lib/gitlab/github_import/client.rb2
-rw-r--r--lib/gitlab/github_import/importer/diff_note_importer.rb3
-rw-r--r--lib/gitlab/github_import/importer/note_importer.rb3
-rw-r--r--lib/gitlab/github_import/importer/pull_request_importer.rb3
-rw-r--r--lib/gitlab/github_import/importer/pull_request_review_importer.rb10
-rw-r--r--lib/gitlab/github_import/importer/pull_requests_merged_by_importer.rb8
-rw-r--r--lib/gitlab/github_import/importer/pull_requests_reviews_importer.rb19
-rw-r--r--lib/gitlab/github_import/markdown_text.rb17
-rw-r--r--lib/gitlab/github_import/parallel_importer.rb2
-rw-r--r--lib/gitlab/github_import/parallel_scheduling.rb2
-rw-r--r--lib/gitlab/github_import/representation/issue.rb1
-rw-r--r--lib/gitlab/github_import/representation/lfs_object.rb7
-rw-r--r--lib/gitlab/github_import/representation/pull_request.rb1
-rw-r--r--lib/gitlab/github_import/representation/user.rb6
-rw-r--r--lib/gitlab/github_import/user_finder.rb2
-rw-r--r--lib/gitlab/gl_repository/repo_type.rb2
-rw-r--r--lib/gitlab/golang.rb6
-rw-r--r--lib/gitlab/gon_helper.rb2
-rw-r--r--lib/gitlab/gpg.rb2
-rw-r--r--lib/gitlab/grape_logging/loggers/route_logger.rb2
-rw-r--r--lib/gitlab/graphql/deprecation.rb2
-rw-r--r--lib/gitlab/graphql/docs/helper.rb401
-rw-r--r--lib/gitlab/graphql/docs/renderer.rb11
-rw-r--r--lib/gitlab/graphql/docs/templates/default.md.haml144
-rw-r--r--lib/gitlab/graphql/pagination/keyset/connection.rb4
-rw-r--r--lib/gitlab/graphql/pagination/keyset/generic_keyset_pagination.rb13
-rw-r--r--lib/gitlab/graphql/pagination/keyset/order_info.rb10
-rw-r--r--lib/gitlab/graphql/pagination/keyset/query_builder.rb4
-rw-r--r--lib/gitlab/graphql/present.rb6
-rw-r--r--lib/gitlab/graphql/present/field_extension.rb3
-rw-r--r--lib/gitlab/graphql/queries.rb7
-rw-r--r--lib/gitlab/graphql/query_analyzers/logger_analyzer.rb4
-rw-r--r--lib/gitlab/graphql/variables.rb2
-rw-r--r--lib/gitlab/group_search_results.rb2
-rw-r--r--lib/gitlab/hashed_storage/migrator.rb4
-rw-r--r--lib/gitlab/health_checks/probes/collection.rb2
-rw-r--r--lib/gitlab/health_checks/simple_abstract_check.rb2
-rw-r--r--lib/gitlab/highlight.rb35
-rw-r--r--lib/gitlab/hook_data/group_member_builder.rb2
-rw-r--r--lib/gitlab/hook_data/issue_builder.rb2
-rw-r--r--lib/gitlab/hook_data/key_builder.rb46
-rw-r--r--lib/gitlab/hook_data/project_builder.rb57
-rw-r--r--lib/gitlab/hook_data/user_builder.rb2
-rw-r--r--lib/gitlab/i18n.rb56
-rw-r--r--lib/gitlab/import_export.rb18
-rw-r--r--lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb2
-rw-r--r--lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb2
-rw-r--r--lib/gitlab/import_export/after_export_strategy_builder.rb2
-rw-r--r--lib/gitlab/import_export/attributes_finder.rb6
-rw-r--r--lib/gitlab/import_export/avatar_restorer.rb2
-rw-r--r--lib/gitlab/import_export/avatar_saver.rb2
-rw-r--r--lib/gitlab/import_export/base/relation_factory.rb3
-rw-r--r--lib/gitlab/import_export/command_line_util.rb13
-rw-r--r--lib/gitlab/import_export/decompressed_archive_size_validator.rb2
-rw-r--r--lib/gitlab/import_export/error.rb12
-rw-r--r--lib/gitlab/import_export/file_importer.rb18
-rw-r--r--lib/gitlab/import_export/group/import_export.yml2
-rw-r--r--lib/gitlab/import_export/group/legacy_import_export.yml2
-rw-r--r--lib/gitlab/import_export/group/legacy_tree_restorer.rb2
-rw-r--r--lib/gitlab/import_export/group/legacy_tree_saver.rb4
-rw-r--r--lib/gitlab/import_export/group/tree_restorer.rb4
-rw-r--r--lib/gitlab/import_export/group/tree_saver.rb2
-rw-r--r--lib/gitlab/import_export/importer.rb6
-rw-r--r--lib/gitlab/import_export/json/legacy_reader.rb4
-rw-r--r--lib/gitlab/import_export/json/streaming_serializer.rb88
-rw-r--r--lib/gitlab/import_export/lfs_restorer.rb6
-rw-r--r--lib/gitlab/import_export/lfs_saver.rb2
-rw-r--r--lib/gitlab/import_export/members_mapper.rb2
-rw-r--r--lib/gitlab/import_export/merge_request_parser.rb2
-rw-r--r--lib/gitlab/import_export/project/import_export.yml29
-rw-r--r--lib/gitlab/import_export/project/relation_factory.rb19
-rw-r--r--lib/gitlab/import_export/project/tree_restorer.rb2
-rw-r--r--lib/gitlab/import_export/project/tree_saver.rb2
-rw-r--r--lib/gitlab/import_export/reader.rb2
-rw-r--r--lib/gitlab/import_export/relation_tree_restorer.rb9
-rw-r--r--lib/gitlab/import_export/repo_restorer.rb4
-rw-r--r--lib/gitlab/import_export/repo_saver.rb2
-rw-r--r--lib/gitlab/import_export/saver.rb2
-rw-r--r--lib/gitlab/import_export/shared.rb2
-rw-r--r--lib/gitlab/import_export/snippet_repo_restorer.rb2
-rw-r--r--lib/gitlab/import_export/statistics_restorer.rb2
-rw-r--r--lib/gitlab/import_export/uploads_manager.rb4
-rw-r--r--lib/gitlab/import_export/uploads_restorer.rb2
-rw-r--r--lib/gitlab/import_export/uploads_saver.rb2
-rw-r--r--lib/gitlab/import_export/version_checker.rb8
-rw-r--r--lib/gitlab/import_export/version_saver.rb2
-rw-r--r--lib/gitlab/import_export/wiki_repo_saver.rb2
-rw-r--r--lib/gitlab/import_sources.rb2
-rw-r--r--lib/gitlab/incident_management/pager_duty/incident_issue_description.rb2
-rw-r--r--lib/gitlab/instrumentation/redis_cluster_validator.rb2
-rw-r--r--lib/gitlab/integrations/sti_type.rb57
-rw-r--r--lib/gitlab/issuable_metadata.rb2
-rw-r--r--lib/gitlab/jira/http_client.rb2
-rw-r--r--lib/gitlab/jira_import/issues_importer.rb2
-rw-r--r--lib/gitlab/jira_import/labels_importer.rb2
-rw-r--r--lib/gitlab/json.rb4
-rw-r--r--lib/gitlab/jwt_token.rb70
-rw-r--r--lib/gitlab/kas.rb22
-rw-r--r--lib/gitlab/kubernetes/kube_client.rb2
-rw-r--r--lib/gitlab/legacy_github_import/importer.rb12
-rw-r--r--lib/gitlab/legacy_github_import/label_formatter.rb2
-rw-r--r--lib/gitlab/lfs/client.rb7
-rw-r--r--lib/gitlab/local_and_remote_storage_migration/artifact_migrater.rb17
-rw-r--r--lib/gitlab/local_and_remote_storage_migration/base_migrater.rb57
-rw-r--r--lib/gitlab/local_and_remote_storage_migration/pages_deployment_migrater.rb17
-rw-r--r--lib/gitlab/markdown_cache.rb8
-rw-r--r--lib/gitlab/memory/instrumentation.rb15
-rw-r--r--lib/gitlab/metrics/dashboard/errors.rb2
-rw-r--r--lib/gitlab/metrics/dashboard/stages/base_stage.rb6
-rw-r--r--lib/gitlab/metrics/dashboard/stages/cluster_endpoint_inserter.rb8
-rw-r--r--lib/gitlab/metrics/dashboard/stages/metric_endpoint_inserter.rb4
-rw-r--r--lib/gitlab/metrics/dashboard/stages/variable_endpoint_inserter.rb2
-rw-r--r--lib/gitlab/metrics/requests_rack_middleware.rb2
-rw-r--r--lib/gitlab/metrics/samplers/base_sampler.rb2
-rw-r--r--lib/gitlab/metrics/samplers/database_sampler.rb2
-rw-r--r--lib/gitlab/metrics/subscribers/active_record.rb2
-rw-r--r--lib/gitlab/metrics/subscribers/rack_attack.rb3
-rw-r--r--lib/gitlab/metrics/web_transaction.rb2
-rw-r--r--lib/gitlab/middleware/rack_multipart_tempfile_factory.rb4
-rw-r--r--lib/gitlab/middleware/read_only/controller.rb2
-rw-r--r--lib/gitlab/middleware/speedscope.rb78
-rw-r--r--lib/gitlab/multi_collection_paginator.rb2
-rw-r--r--lib/gitlab/nav/top_nav_menu_builder.rb35
-rw-r--r--lib/gitlab/nav/top_nav_menu_item.rb27
-rw-r--r--lib/gitlab/nav/top_nav_view_model_builder.rb27
-rw-r--r--lib/gitlab/object_hierarchy.rb38
-rw-r--r--lib/gitlab/omniauth_initializer.rb2
-rw-r--r--lib/gitlab/otp_key_rotator.rb4
-rw-r--r--lib/gitlab/pages/migration_helper.rb53
-rw-r--r--lib/gitlab/pages/settings.rb6
-rw-r--r--lib/gitlab/pages/stores/local_store.rb15
-rw-r--r--lib/gitlab/pagination/keyset/iterator.rb40
-rw-r--r--lib/gitlab/pagination/keyset/order.rb33
-rw-r--r--lib/gitlab/pagination/keyset/simple_order_builder.rb137
-rw-r--r--lib/gitlab/patch/draw_route.rb4
-rw-r--r--lib/gitlab/patch/prependable.rb7
-rw-r--r--lib/gitlab/path_regex.rb2
-rw-r--r--lib/gitlab/performance_bar.rb4
-rw-r--r--lib/gitlab/performance_bar/stats.rb2
-rw-r--r--lib/gitlab/phabricator_import/conduit/client.rb2
-rw-r--r--lib/gitlab/phabricator_import/conduit/response.rb2
-rw-r--r--lib/gitlab/phabricator_import/importer.rb2
-rw-r--r--lib/gitlab/project_template.rb2
-rw-r--r--lib/gitlab/prometheus/adapter.rb8
-rw-r--r--lib/gitlab/prometheus/additional_metrics_parser.rb2
-rw-r--r--lib/gitlab/prometheus/metric_group.rb2
-rw-r--r--lib/gitlab/prometheus/queries/query_additional_metrics.rb2
-rw-r--r--lib/gitlab/prometheus_client.rb2
-rw-r--r--lib/gitlab/quick_actions/issue_actions.rb2
-rw-r--r--lib/gitlab/quick_actions/merge_request_actions.rb2
-rw-r--r--lib/gitlab/quick_actions/spend_time_and_date_separator.rb2
-rw-r--r--lib/gitlab/quick_actions/substitution_definition.rb2
-rw-r--r--lib/gitlab/rack_attack.rb40
-rw-r--r--lib/gitlab/rack_attack/request.rb57
-rw-r--r--lib/gitlab/redis/boolean.rb8
-rw-r--r--lib/gitlab/redis/hll.rb2
-rw-r--r--lib/gitlab/redis/wrapper.rb2
-rw-r--r--lib/gitlab/reference_counter.rb2
-rw-r--r--lib/gitlab/regex.rb22
-rw-r--r--lib/gitlab/relative_positioning.rb2
-rw-r--r--lib/gitlab/relative_positioning/item_context.rb8
-rw-r--r--lib/gitlab/repo_path.rb2
-rw-r--r--lib/gitlab/repository_size_checker.rb2
-rw-r--r--lib/gitlab/repository_url_builder.rb2
-rw-r--r--lib/gitlab/request_profiler/middleware.rb2
-rw-r--r--lib/gitlab/route_map.rb2
-rw-r--r--lib/gitlab/routing.rb2
-rw-r--r--lib/gitlab/runtime.rb10
-rw-r--r--lib/gitlab/sanitizers/exif.rb2
-rw-r--r--lib/gitlab/search/parsed_query.rb4
-rw-r--r--lib/gitlab/search_context.rb2
-rw-r--r--lib/gitlab/search_results.rb2
-rw-r--r--lib/gitlab/shell.rb4
-rw-r--r--lib/gitlab/sidekiq_config.rb14
-rw-r--r--lib/gitlab/sidekiq_config/dummy_worker.rb7
-rw-r--r--lib/gitlab/sidekiq_config/worker.rb12
-rw-r--r--lib/gitlab/sidekiq_config/worker_matcher.rb7
-rw-r--r--lib/gitlab/sidekiq_config/worker_router.rb107
-rw-r--r--lib/gitlab/sidekiq_daemon/memory_killer.rb4
-rw-r--r--lib/gitlab/sidekiq_logging/structured_logger.rb6
-rw-r--r--lib/gitlab/sidekiq_middleware.rb2
-rw-r--r--lib/gitlab/sidekiq_middleware/server_metrics.rb2
-rw-r--r--lib/gitlab/sidekiq_middleware/size_limiter/exceed_limit_error.rb2
-rw-r--r--lib/gitlab/sidekiq_migrate_jobs.rb72
-rw-r--r--lib/gitlab/sidekiq_status.rb2
-rw-r--r--lib/gitlab/slash_commands/issue_close.rb2
-rw-r--r--lib/gitlab/slash_commands/issue_move.rb2
-rw-r--r--lib/gitlab/slash_commands/issue_new.rb2
-rw-r--r--lib/gitlab/slash_commands/presenters/issue_base.rb2
-rw-r--r--lib/gitlab/snippet_search_results.rb2
-rw-r--r--lib/gitlab/spamcheck/client.rb105
-rw-r--r--lib/gitlab/stack_prof.rb136
-rw-r--r--lib/gitlab/static_site_editor/config/generated_config.rb6
-rw-r--r--lib/gitlab/subscription_portal.rb7
-rw-r--r--lib/gitlab/suggestions/suggestion_set.rb4
-rw-r--r--lib/gitlab/task_helpers.rb2
-rw-r--r--lib/gitlab/tcp_checker.rb2
-rw-r--r--lib/gitlab/template/gitlab_ci_syntax_yml_template.rb29
-rw-r--r--lib/gitlab/template/gitlab_ci_yml_template.rb2
-rw-r--r--lib/gitlab/terraform_registry_token.rb13
-rw-r--r--lib/gitlab/throttle.rb14
-rw-r--r--lib/gitlab/time_tracking_formatter.rb4
-rw-r--r--lib/gitlab/tracking.rb2
-rw-r--r--lib/gitlab/tracking/docs/helper.rb67
-rw-r--r--lib/gitlab/tracking/docs/renderer.rb32
-rw-r--r--lib/gitlab/tracking/docs/templates/default.md.haml35
-rw-r--r--lib/gitlab/tracking/event_definition.rb81
-rw-r--r--lib/gitlab/tracking/standard_context.rb5
-rw-r--r--lib/gitlab/tree_summary.rb2
-rw-r--r--lib/gitlab/untrusted_regexp.rb2
-rw-r--r--lib/gitlab/uploads/migration_helper.rb2
-rw-r--r--lib/gitlab/url_builder.rb4
-rw-r--r--lib/gitlab/usage/docs/helper.rb2
-rw-r--r--lib/gitlab/usage/docs/templates/default.md.haml4
-rw-r--r--lib/gitlab/usage/metric_definition.rb12
-rw-r--r--lib/gitlab/usage/metrics/aggregates/aggregate.rb65
-rw-r--r--lib/gitlab/usage/metrics/aggregates/sources/calculations/intersection.rb76
-rw-r--r--lib/gitlab/usage/metrics/aggregates/sources/postgres_hll.rb1
-rw-r--r--lib/gitlab/usage/metrics/aggregates/sources/redis_hll.rb1
-rw-r--r--lib/gitlab/usage/metrics/instrumentations/base_metric.rb19
-rw-r--r--lib/gitlab/usage/metrics/instrumentations/count_boards_metric.rb15
-rw-r--r--lib/gitlab/usage/metrics/instrumentations/count_issues_metric.rb18
-rw-r--r--lib/gitlab/usage/metrics/instrumentations/count_users_creating_issues_metric.rb15
-rw-r--r--lib/gitlab/usage/metrics/instrumentations/count_users_using_approve_quick_action_metric.rb13
-rw-r--r--lib/gitlab/usage/metrics/instrumentations/database_metric.rb68
-rw-r--r--lib/gitlab/usage/metrics/instrumentations/generic_metric.rb32
-rw-r--r--lib/gitlab/usage/metrics/instrumentations/hostname_metric.rb15
-rw-r--r--lib/gitlab/usage/metrics/instrumentations/redis_hll_metric.rb45
-rw-r--r--lib/gitlab/usage/metrics/instrumentations/uuid_metric.rb15
-rw-r--r--lib/gitlab/usage/metrics/key_path_processor.rb27
-rw-r--r--lib/gitlab/usage_data.rb93
-rw-r--r--lib/gitlab/usage_data/topology.rb4
-rw-r--r--lib/gitlab/usage_data_counters/counter_events/package_events.yml3
-rw-r--r--lib/gitlab/usage_data_counters/editor_unique_counter.rb1
-rw-r--r--lib/gitlab/usage_data_counters/hll_redis_counter.rb8
-rw-r--r--lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb2
-rw-r--r--lib/gitlab/usage_data_counters/known_events/analytics.yml85
-rw-r--r--lib/gitlab/usage_data_counters/known_events/code_review_events.yml97
-rw-r--r--lib/gitlab/usage_data_counters/known_events/common.yml103
-rw-r--r--lib/gitlab/usage_data_counters/known_events/ecosystem.yml10
-rw-r--r--lib/gitlab/usage_data_counters/known_events/epic_board_events.yml22
-rw-r--r--lib/gitlab/usage_data_counters/known_events/epic_events.yml42
-rw-r--r--lib/gitlab/usage_data_counters/known_events/package_events.yml8
-rw-r--r--lib/gitlab/usage_data_counters/known_events/quickactions.yml67
-rw-r--r--lib/gitlab/usage_data_counters/kubernetes_agent_counter.rb24
-rw-r--r--lib/gitlab/usage_data_counters/quick_action_activity_unique_counter.rb1
-rw-r--r--lib/gitlab/usage_data_metrics.rb28
-rw-r--r--lib/gitlab/usage_data_non_sql_metrics.rb11
-rw-r--r--lib/gitlab/usage_data_queries.rb36
-rw-r--r--lib/gitlab/utils.rb2
-rw-r--r--lib/gitlab/utils/override.rb4
-rw-r--r--lib/gitlab/utils/usage_data.rb54
-rw-r--r--lib/gitlab/verify/batch_verifier.rb16
-rw-r--r--lib/gitlab/view/presenter/delegated.rb2
-rw-r--r--lib/gitlab/web_ide/config/entry/global.rb2
-rw-r--r--lib/gitlab/webpack/manifest.rb4
-rw-r--r--lib/gitlab/x509/signature.rb6
-rw-r--r--lib/gitlab/x509/tag.rb2
507 files changed, 5906 insertions, 2367 deletions
diff --git a/lib/gitlab/access.rb b/lib/gitlab/access.rb
index 830980f0997..6afcd745d4e 100644
--- a/lib/gitlab/access.rb
+++ b/lib/gitlab/access.rb
@@ -159,4 +159,4 @@ module Gitlab
end
end
-Gitlab::Access.prepend_if_ee('EE::Gitlab::Access')
+Gitlab::Access.prepend_mod_with('Gitlab::Access')
diff --git a/lib/gitlab/alert_management/payload.rb b/lib/gitlab/alert_management/payload.rb
index a1063001330..1b67b91e839 100644
--- a/lib/gitlab/alert_management/payload.rb
+++ b/lib/gitlab/alert_management/payload.rb
@@ -49,4 +49,4 @@ module Gitlab
end
end
-Gitlab::AlertManagement::Payload.prepend_if_ee('EE::Gitlab::AlertManagement::Payload')
+Gitlab::AlertManagement::Payload.prepend_mod_with('Gitlab::AlertManagement::Payload')
diff --git a/lib/gitlab/alert_management/payload/base.rb b/lib/gitlab/alert_management/payload/base.rb
index 786c5bf675b..5e535ded439 100644
--- a/lib/gitlab/alert_management/payload/base.rb
+++ b/lib/gitlab/alert_management/payload/base.rb
@@ -130,7 +130,7 @@ module Gitlab
strong_memoize(:environment) do
next unless environment_name
- EnvironmentsFinder
+ ::Environments::EnvironmentsFinder
.new(project, nil, { name: environment_name })
.execute
.first
@@ -193,7 +193,7 @@ module Gitlab
def parse_time(value)
Time.parse(value).utc
- rescue ArgumentError
+ rescue ArgumentError, TypeError
end
def parse_integer(value)
diff --git a/lib/gitlab/alert_management/payload/generic.rb b/lib/gitlab/alert_management/payload/generic.rb
index e2db9b62dd5..15238b5e50f 100644
--- a/lib/gitlab/alert_management/payload/generic.rb
+++ b/lib/gitlab/alert_management/payload/generic.rb
@@ -27,4 +27,4 @@ module Gitlab
end
end
-Gitlab::AlertManagement::Payload::Generic.prepend_if_ee('EE::Gitlab::AlertManagement::Payload::Generic')
+Gitlab::AlertManagement::Payload::Generic.prepend_mod_with('Gitlab::AlertManagement::Payload::Generic')
diff --git a/lib/gitlab/analytics/cycle_analytics/average.rb b/lib/gitlab/analytics/cycle_analytics/average.rb
index a449b71b165..7140d31d536 100644
--- a/lib/gitlab/analytics/cycle_analytics/average.rb
+++ b/lib/gitlab/analytics/cycle_analytics/average.rb
@@ -7,9 +7,10 @@ module Gitlab
include Gitlab::Utils::StrongMemoize
include StageQueryHelpers
- def initialize(stage:, query:)
+ def initialize(stage:, query:, params: {})
@stage = stage
@query = query
+ @params = params
end
def seconds
@@ -22,7 +23,7 @@ module Gitlab
private
- attr_reader :stage
+ attr_reader :stage, :params
# rubocop: disable CodeReuse/ActiveRecord
def select_average
diff --git a/lib/gitlab/analytics/cycle_analytics/base_query_builder.rb b/lib/gitlab/analytics/cycle_analytics/base_query_builder.rb
index 4dec71b35e8..c7987d63153 100644
--- a/lib/gitlab/analytics/cycle_analytics/base_query_builder.rb
+++ b/lib/gitlab/analytics/cycle_analytics/base_query_builder.rb
@@ -5,6 +5,7 @@ module Gitlab
module CycleAnalytics
class BaseQueryBuilder
include Gitlab::CycleAnalytics::MetricsTables
+ include StageQueryHelpers
delegate :subject_class, to: :stage
@@ -13,17 +14,19 @@ module Gitlab
Issue.to_s => IssuesFinder
}.freeze
+ DEFAULT_END_EVENT_FILTER = :finished
+
def initialize(stage:, params: {})
@stage = stage
@params = build_finder_params(params)
+ @params[:state] = :opened if in_progress?
end
# rubocop: disable CodeReuse/ActiveRecord
def build
query = finder.execute
query = stage.start_event.apply_query_customization(query)
- query = stage.end_event.apply_query_customization(query)
- query.where(duration_condition)
+ apply_end_event_query_customization(query)
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -46,6 +49,7 @@ module Gitlab
def build_finder_params(params)
{}.tap do |finder_params|
finder_params[:current_user] = params[:current_user]
+ finder_params[:end_event_filter] = params[:end_event_filter] || DEFAULT_END_EVENT_FILTER
add_parent_model_params!(finder_params)
add_time_range_params!(finder_params, params[:from], params[:to])
@@ -62,9 +66,20 @@ module Gitlab
finder_params[:created_after] = from || 30.days.ago
finder_params[:created_before] = to if to
end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def apply_end_event_query_customization(query)
+ if in_progress?
+ stage.end_event.apply_negated_query_customization(query)
+ else
+ query = stage.end_event.apply_query_customization(query)
+ query.where(duration_condition)
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
end
end
end
end
-Gitlab::Analytics::CycleAnalytics::BaseQueryBuilder.prepend_if_ee('EE::Gitlab::Analytics::CycleAnalytics::BaseQueryBuilder')
+Gitlab::Analytics::CycleAnalytics::BaseQueryBuilder.prepend_mod_with('Gitlab::Analytics::CycleAnalytics::BaseQueryBuilder')
diff --git a/lib/gitlab/analytics/cycle_analytics/data_collector.rb b/lib/gitlab/analytics/cycle_analytics/data_collector.rb
index 10a008a76d5..56179533ffb 100644
--- a/lib/gitlab/analytics/cycle_analytics/data_collector.rb
+++ b/lib/gitlab/analytics/cycle_analytics/data_collector.rb
@@ -12,6 +12,8 @@ module Gitlab
class DataCollector
include Gitlab::Utils::StrongMemoize
+ MAX_COUNT = 1001
+
delegate :serialized_records, to: :records_fetcher
def initialize(stage:, params: {})
@@ -27,13 +29,19 @@ module Gitlab
def median
strong_memoize(:median) do
- Median.new(stage: stage, query: query)
+ Median.new(stage: stage, query: query, params: params)
end
end
def average
strong_memoize(:average) do
- Average.new(stage: stage, query: query)
+ Average.new(stage: stage, query: query, params: params)
+ end
+ end
+
+ def count
+ strong_memoize(:count) do
+ limit_count
end
end
@@ -44,9 +52,16 @@ module Gitlab
def query
BaseQueryBuilder.new(stage: stage, params: params).build
end
+
+ # Limiting the maximum number of records so the COUNT(*) query stays efficient for large groups.
+ # COUNT = 1001, show 1000+ on the UI
+ # COUNT < 1001, show the actual number on the UI
+ def limit_count
+ query.limit(MAX_COUNT).count
+ end
end
end
end
end
-Gitlab::Analytics::CycleAnalytics::DataCollector.prepend_if_ee('EE::Gitlab::Analytics::CycleAnalytics::DataCollector')
+Gitlab::Analytics::CycleAnalytics::DataCollector.prepend_mod_with('Gitlab::Analytics::CycleAnalytics::DataCollector')
diff --git a/lib/gitlab/analytics/cycle_analytics/median.rb b/lib/gitlab/analytics/cycle_analytics/median.rb
index 6c0450ac9e5..5775d0324c6 100644
--- a/lib/gitlab/analytics/cycle_analytics/median.rb
+++ b/lib/gitlab/analytics/cycle_analytics/median.rb
@@ -6,9 +6,10 @@ module Gitlab
class Median
include StageQueryHelpers
- def initialize(stage:, query:)
+ def initialize(stage:, query:, params: {})
@stage = stage
@query = query
+ @params = params
end
# rubocop: disable CodeReuse/ActiveRecord
@@ -26,7 +27,7 @@ module Gitlab
private
- attr_reader :stage
+ attr_reader :stage, :params
def percentile_cont
percentile_cont_ordering = Arel::Nodes::UnaryOperation.new(Arel::Nodes::SqlLiteral.new('ORDER BY'), duration)
diff --git a/lib/gitlab/analytics/cycle_analytics/records_fetcher.rb b/lib/gitlab/analytics/cycle_analytics/records_fetcher.rb
index b4752ed9e5b..9a37a41ff81 100644
--- a/lib/gitlab/analytics/cycle_analytics/records_fetcher.rb
+++ b/lib/gitlab/analytics/cycle_analytics/records_fetcher.rb
@@ -124,7 +124,7 @@ module Gitlab
def time_columns
[
stage.start_event.timestamp_projection.as('start_event_timestamp'),
- stage.end_event.timestamp_projection.as('end_event_timestamp'),
+ end_event_timestamp_projection.as('end_event_timestamp'),
round_duration_to_seconds.as('total_time')
]
end
@@ -133,4 +133,4 @@ module Gitlab
end
end
-Gitlab::Analytics::CycleAnalytics::RecordsFetcher.prepend_if_ee('EE::Gitlab::Analytics::CycleAnalytics::RecordsFetcher')
+Gitlab::Analytics::CycleAnalytics::RecordsFetcher.prepend_mod_with('Gitlab::Analytics::CycleAnalytics::RecordsFetcher')
diff --git a/lib/gitlab/analytics/cycle_analytics/sorting.rb b/lib/gitlab/analytics/cycle_analytics/sorting.rb
index 828879d466d..c399bac423b 100644
--- a/lib/gitlab/analytics/cycle_analytics/sorting.rb
+++ b/lib/gitlab/analytics/cycle_analytics/sorting.rb
@@ -4,23 +4,35 @@ module Gitlab
module Analytics
module CycleAnalytics
class Sorting
+ include StageQueryHelpers
+
+ def initialize(stage:, query:, params: {})
+ @stage = stage
+ @query = query
+ @params = params
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
- SORTING_OPTIONS = {
- end_event: {
- asc: -> (query, stage) { query.reorder(stage.end_event.timestamp_projection.asc) },
- desc: -> (query, stage) { query.reorder(stage.end_event.timestamp_projection.desc) }
- }.freeze,
- duration: {
- asc: -> (query, stage) { query.reorder(Arel::Nodes::Subtraction.new(stage.end_event.timestamp_projection, stage.start_event.timestamp_projection).asc) },
- desc: -> (query, stage) { query.reorder(Arel::Nodes::Subtraction.new(stage.end_event.timestamp_projection, stage.start_event.timestamp_projection).desc) }
- }.freeze
- }.freeze
- # rubocop: enable CodeReuse/ActiveRecord,
+ def apply(sort, direction)
+ sorting_options = {
+ end_event: {
+ asc: -> { query.reorder(end_event_timestamp_projection.asc) },
+ desc: -> { query.reorder(end_event_timestamp_projection.desc) }
+ },
+ duration: {
+ asc: -> { query.reorder(duration.asc) },
+ desc: -> { query.reorder(duration.desc) }
+ }
+ }
- def self.apply(query, stage, sort, direction)
- sort_lambda = SORTING_OPTIONS.dig(sort, direction) || SORTING_OPTIONS.dig(:end_event, :desc)
- sort_lambda.call(query, stage)
+ sort_lambda = sorting_options.dig(sort, direction) || sorting_options.dig(:end_event, :desc)
+ sort_lambda.call
end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ private
+
+ attr_reader :stage, :query, :params
end
end
end
diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events.rb b/lib/gitlab/analytics/cycle_analytics/stage_events.rb
index 02b1024b8b3..b7a11bc0418 100644
--- a/lib/gitlab/analytics/cycle_analytics/stage_events.rb
+++ b/lib/gitlab/analytics/cycle_analytics/stage_events.rb
@@ -85,4 +85,4 @@ module Gitlab
end
end
-Gitlab::Analytics::CycleAnalytics::StageEvents.prepend_if_ee('::EE::Gitlab::Analytics::CycleAnalytics::StageEvents')
+Gitlab::Analytics::CycleAnalytics::StageEvents.prepend_mod_with('Gitlab::Analytics::CycleAnalytics::StageEvents')
diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/code_stage_start.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/code_stage_start.rb
index 4bb225b63f1..8e87245e62b 100644
--- a/lib/gitlab/analytics/cycle_analytics/stage_events/code_stage_start.rb
+++ b/lib/gitlab/analytics/cycle_analytics/stage_events/code_stage_start.rb
@@ -17,11 +17,6 @@ module Gitlab
MergeRequest
end
- def timestamp_projection
- Arel::Nodes::NamedFunction.new('COALESCE', column_list)
- end
-
- override :column_list
def column_list
[
issue_metrics_table[:first_mentioned_in_commit_at],
diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/issue_created.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/issue_created.rb
index a159580b7bd..30b457b667c 100644
--- a/lib/gitlab/analytics/cycle_analytics/stage_events/issue_created.rb
+++ b/lib/gitlab/analytics/cycle_analytics/stage_events/issue_created.rb
@@ -17,8 +17,8 @@ module Gitlab
Issue
end
- def timestamp_projection
- issue_table[:created_at]
+ def column_list
+ [issue_table[:created_at]]
end
end
end
diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/issue_deployed_to_production.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/issue_deployed_to_production.rb
index 3e93e60e686..4ca3c19051e 100644
--- a/lib/gitlab/analytics/cycle_analytics/stage_events/issue_deployed_to_production.rb
+++ b/lib/gitlab/analytics/cycle_analytics/stage_events/issue_deployed_to_production.rb
@@ -17,13 +17,8 @@ module Gitlab
Issue
end
- def timestamp_projection
- mr_metrics_table[:first_deployed_to_production_at]
- end
-
- override :column_list
def column_list
- [timestamp_projection]
+ [mr_metrics_table[:first_deployed_to_production_at]]
end
# rubocop: disable CodeReuse/ActiveRecord
diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/issue_first_mentioned_in_commit.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/issue_first_mentioned_in_commit.rb
index a3b7fa16daf..aa509e8c4d2 100644
--- a/lib/gitlab/analytics/cycle_analytics/stage_events/issue_first_mentioned_in_commit.rb
+++ b/lib/gitlab/analytics/cycle_analytics/stage_events/issue_first_mentioned_in_commit.rb
@@ -17,8 +17,8 @@ module Gitlab
Issue
end
- def timestamp_projection
- issue_metrics_table[:first_mentioned_in_commit_at]
+ def column_list
+ [issue_metrics_table[:first_mentioned_in_commit_at]]
end
end
end
diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/issue_stage_end.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/issue_stage_end.rb
index 7c1f4436c93..284d8534b96 100644
--- a/lib/gitlab/analytics/cycle_analytics/stage_events/issue_stage_end.rb
+++ b/lib/gitlab/analytics/cycle_analytics/stage_events/issue_stage_end.rb
@@ -17,11 +17,6 @@ module Gitlab
Issue
end
- def timestamp_projection
- Arel::Nodes::NamedFunction.new('COALESCE', column_list)
- end
-
- override :column_list
def column_list
[
issue_metrics_table[:first_associated_with_milestone_at],
diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_created.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_created.rb
index 013e068e479..31249ae2036 100644
--- a/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_created.rb
+++ b/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_created.rb
@@ -17,8 +17,8 @@ module Gitlab
MergeRequest
end
- def timestamp_projection
- mr_table[:created_at]
+ def column_list
+ [mr_table[:created_at]]
end
end
end
diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_first_deployed_to_production.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_first_deployed_to_production.rb
index 654d0befbc3..4c0e9b61e64 100644
--- a/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_first_deployed_to_production.rb
+++ b/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_first_deployed_to_production.rb
@@ -17,8 +17,8 @@ module Gitlab
MergeRequest
end
- def timestamp_projection
- mr_metrics_table[:first_deployed_to_production_at]
+ def column_list
+ [mr_metrics_table[:first_deployed_to_production_at]]
end
# rubocop: disable CodeReuse/ActiveRecord
diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_finished.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_finished.rb
index a0b1c12756f..178fe03d7db 100644
--- a/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_finished.rb
+++ b/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_finished.rb
@@ -17,8 +17,8 @@ module Gitlab
MergeRequest
end
- def timestamp_projection
- mr_metrics_table[:latest_build_finished_at]
+ def column_list
+ [mr_metrics_table[:latest_build_finished_at]]
end
end
end
diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_started.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_started.rb
index da3b5cdfaa4..95e59cd29a6 100644
--- a/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_started.rb
+++ b/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_started.rb
@@ -17,8 +17,8 @@ module Gitlab
MergeRequest
end
- def timestamp_projection
- mr_metrics_table[:latest_build_started_at]
+ def column_list
+ [mr_metrics_table[:latest_build_started_at]]
end
end
end
diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_merged.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_merged.rb
index e67a6f7eea6..00ac2e7d56c 100644
--- a/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_merged.rb
+++ b/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_merged.rb
@@ -17,8 +17,8 @@ module Gitlab
MergeRequest
end
- def timestamp_projection
- mr_metrics_table[:merged_at]
+ def column_list
+ [mr_metrics_table[:merged_at]]
end
end
end
diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/metrics_based_stage_event.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/metrics_based_stage_event.rb
index fe477490648..fd30ab5277d 100644
--- a/lib/gitlab/analytics/cycle_analytics/stage_events/metrics_based_stage_event.rb
+++ b/lib/gitlab/analytics/cycle_analytics/stage_events/metrics_based_stage_event.rb
@@ -11,7 +11,12 @@ module Gitlab
end
# rubocop: enable CodeReuse/ActiveRecord
- override :column_list
+ # rubocop: disable CodeReuse/ActiveRecord
+ def apply_negated_query_customization(query)
+ super.joins(:metrics)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
def column_list
[timestamp_projection]
end
diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/plan_stage_start.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/plan_stage_start.rb
index bddc326de71..9b4cbc9090c 100644
--- a/lib/gitlab/analytics/cycle_analytics/stage_events/plan_stage_start.rb
+++ b/lib/gitlab/analytics/cycle_analytics/stage_events/plan_stage_start.rb
@@ -17,11 +17,6 @@ module Gitlab
Issue
end
- def timestamp_projection
- Arel::Nodes::NamedFunction.new('COALESCE', column_list)
- end
-
- override :column_list
def column_list
[
issue_metrics_table[:first_associated_with_milestone_at],
diff --git a/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event.rb b/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event.rb
index cfc9300a710..530e53f9d10 100644
--- a/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event.rb
+++ b/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event.rb
@@ -34,14 +34,16 @@ module Gitlab
# Each StageEvent must expose a timestamp or a timestamp like expression in order to build a range query.
# Example: get me all the Issue records between start event end end event
def timestamp_projection
- raise NotImplementedError
+ columns = column_list
+
+ columns.one? ? columns.first : Arel::Nodes::NamedFunction.new('COALESCE', columns)
end
# List of columns that are referenced in the `timestamp_projection` expression
# Example timestamp projection: COALESCE(issue_metrics.created_at, issue_metrics.updated_at)
# Expected column list: issue_metrics.created_at, issue_metrics.updated_at
def column_list
- []
+ raise NotImplementedError
end
# Optionally a StageEvent may apply additional filtering or join other tables on the base query.
@@ -49,6 +51,12 @@ module Gitlab
query
end
+ # rubocop: disable CodeReuse/ActiveRecord
+ def apply_negated_query_customization(query)
+ query.where(timestamp_projection.eq(nil))
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
def self.label_based?
false
end
diff --git a/lib/gitlab/analytics/cycle_analytics/stage_query_helpers.rb b/lib/gitlab/analytics/cycle_analytics/stage_query_helpers.rb
index 777a8278e6e..11fe1dde12f 100644
--- a/lib/gitlab/analytics/cycle_analytics/stage_query_helpers.rb
+++ b/lib/gitlab/analytics/cycle_analytics/stage_query_helpers.rb
@@ -18,22 +18,30 @@ module Gitlab
def duration
Arel::Nodes::Subtraction.new(
- stage.end_event.timestamp_projection,
+ end_event_timestamp_projection,
stage.start_event.timestamp_projection
)
end
+ def end_event_timestamp_projection
+ if in_progress?
+ Arel::Nodes::NamedFunction.new('TO_TIMESTAMP', [Time.current.to_i])
+ else
+ stage.end_event.timestamp_projection
+ end
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def order_by(query, sort, direction, extra_columns_to_select = [:id])
- ordered_query = Gitlab::Analytics::CycleAnalytics::Sorting.apply(query, stage, sort, direction)
+ ordered_query = Gitlab::Analytics::CycleAnalytics::Sorting.new(stage: stage, query: query, params: params).apply(sort, direction)
# When filtering for more than one label, postgres requires the columns in ORDER BY to be present in the GROUP BY clause
if requires_grouping?
- column_list = [
- *extra_columns_to_select,
- *stage.end_event.column_list,
- *stage.start_event.column_list
- ]
+ column_list = [].tap do |array|
+ array.concat(extra_columns_to_select)
+ array.concat(stage.end_event.column_list) unless in_progress?
+ array.concat(stage.start_event.column_list)
+ end
ordered_query = ordered_query.group(column_list)
end
@@ -45,6 +53,10 @@ module Gitlab
def requires_grouping?
Array(params[:label_name]).size > 1
end
+
+ def in_progress?
+ params[:end_event_filter] == :in_progress
+ end
end
end
end
diff --git a/lib/gitlab/api_authentication/token_locator.rb b/lib/gitlab/api_authentication/token_locator.rb
index 09039f3fc43..df342905d2e 100644
--- a/lib/gitlab/api_authentication/token_locator.rb
+++ b/lib/gitlab/api_authentication/token_locator.rb
@@ -10,7 +10,17 @@ module Gitlab
attr_reader :location
- validates :location, inclusion: { in: %i[http_basic_auth http_token] }
+ validates :location, inclusion: {
+ in: %i[
+ http_basic_auth
+ http_token
+ http_bearer_token
+ http_deploy_token_header
+ http_job_token_header
+ http_private_token_header
+ token_param
+ ]
+ }
def initialize(location)
@location = location
@@ -23,6 +33,16 @@ module Gitlab
extract_from_http_basic_auth request
when :http_token
extract_from_http_token request
+ when :http_bearer_token
+ extract_from_http_bearer_token request
+ when :http_deploy_token_header
+ extract_from_http_deploy_token_header request
+ when :http_job_token_header
+ extract_from_http_job_token_header request
+ when :http_private_token_header
+ extract_from_http_private_token_header request
+ when :token_param
+ extract_from_token_param request
end
end
@@ -41,6 +61,41 @@ module Gitlab
UsernameAndPassword.new(nil, password)
end
+
+ def extract_from_http_bearer_token(request)
+ password = request.headers['Authorization']
+ return unless password.present?
+
+ UsernameAndPassword.new(nil, password.split(' ').last)
+ end
+
+ def extract_from_http_deploy_token_header(request)
+ password = request.headers['Deploy-Token']
+ return unless password.present?
+
+ UsernameAndPassword.new(nil, password)
+ end
+
+ def extract_from_http_job_token_header(request)
+ password = request.headers['Job-Token']
+ return unless password.present?
+
+ UsernameAndPassword.new(nil, password)
+ end
+
+ def extract_from_http_private_token_header(request)
+ password = request.headers['Private-Token']
+ return unless password.present?
+
+ UsernameAndPassword.new(nil, password)
+ end
+
+ def extract_from_token_param(request)
+ password = request.query_parameters['token']
+ return unless password.present?
+
+ UsernameAndPassword.new(nil, password)
+ end
end
end
end
diff --git a/lib/gitlab/api_authentication/token_resolver.rb b/lib/gitlab/api_authentication/token_resolver.rb
index 9234837cdf7..dd9039e37f6 100644
--- a/lib/gitlab/api_authentication/token_resolver.rb
+++ b/lib/gitlab/api_authentication/token_resolver.rb
@@ -15,9 +15,14 @@ module Gitlab
personal_access_token
job_token
deploy_token
+ personal_access_token_from_jwt
+ deploy_token_from_jwt
+ job_token_from_jwt
]
}
+ UsernameAndPassword = ::Gitlab::APIAuthentication::TokenLocator::UsernameAndPassword
+
def initialize(token_type)
@token_type = token_type
validate!
@@ -56,6 +61,15 @@ module Gitlab
when :deploy_token_with_username
resolve_deploy_token_with_username raw
+
+ when :personal_access_token_from_jwt
+ resolve_personal_access_token_from_jwt raw
+
+ when :deploy_token_from_jwt
+ resolve_deploy_token_from_jwt raw
+
+ when :job_token_from_jwt
+ resolve_job_token_from_jwt raw
end
end
@@ -116,6 +130,33 @@ module Gitlab
end
end
+ def resolve_personal_access_token_from_jwt(raw)
+ with_jwt_token(raw) do |jwt_token|
+ break unless jwt_token['token'].is_a?(Integer)
+
+ pat = ::PersonalAccessToken.find(jwt_token['token'])
+ break unless pat
+
+ pat
+ end
+ end
+
+ def resolve_deploy_token_from_jwt(raw)
+ with_jwt_token(raw) do |jwt_token|
+ break unless jwt_token['token'].is_a?(String)
+
+ resolve_deploy_token(UsernameAndPassword.new(nil, jwt_token['token']))
+ end
+ end
+
+ def resolve_job_token_from_jwt(raw)
+ with_jwt_token(raw) do |jwt_token|
+ break unless jwt_token['token'].is_a?(String)
+
+ resolve_job_token(UsernameAndPassword.new(nil, jwt_token['token']))
+ end
+ end
+
def with_personal_access_token(raw, &block)
pat = ::PersonalAccessToken.find_by_token(raw.password)
return unless pat
@@ -136,6 +177,13 @@ module Gitlab
yield(job)
end
+
+ def with_jwt_token(raw, &block)
+ jwt_token = ::Gitlab::JWTToken.decode(raw.password)
+ raise ::Gitlab::Auth::UnauthorizedError unless jwt_token
+
+ yield(jwt_token)
+ end
end
end
end
diff --git a/lib/gitlab/application_context.rb b/lib/gitlab/application_context.rb
index ceda82cb6f6..601f2175cfc 100644
--- a/lib/gitlab/application_context.rb
+++ b/lib/gitlab/application_context.rb
@@ -135,4 +135,4 @@ module Gitlab
end
end
-Gitlab::ApplicationContext.prepend_if_ee('EE::Gitlab::ApplicationContext')
+Gitlab::ApplicationContext.prepend_mod_with('Gitlab::ApplicationContext')
diff --git a/lib/gitlab/application_rate_limiter.rb b/lib/gitlab/application_rate_limiter.rb
index f74edf2b767..f91a56a0cd2 100644
--- a/lib/gitlab/application_rate_limiter.rb
+++ b/lib/gitlab/application_rate_limiter.rb
@@ -34,6 +34,7 @@ module Gitlab
group_import: { threshold: -> { application_settings.group_import_limit }, interval: 1.minute },
group_testing_hook: { threshold: 5, interval: 1.minute },
profile_add_new_email: { threshold: 5, interval: 1.minute },
+ web_hook_calls: { interval: 1.minute },
profile_resend_email_confirmation: { threshold: 5, interval: 1.minute },
update_environment_canary_ingress: { threshold: 1, interval: 1.minute },
auto_rollback_deployment: { threshold: 1, interval: 3.minutes }
diff --git a/lib/gitlab/artifacts/migration_helper.rb b/lib/gitlab/artifacts/migration_helper.rb
deleted file mode 100644
index 4f047ab3ea8..00000000000
--- a/lib/gitlab/artifacts/migration_helper.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Artifacts
- class MigrationHelper
- def migrate_to_remote_storage(&block)
- artifacts = ::Ci::JobArtifact.with_files_stored_locally
- migrate(artifacts, ObjectStorage::Store::REMOTE, &block)
- end
-
- def migrate_to_local_storage(&block)
- artifacts = ::Ci::JobArtifact.with_files_stored_remotely
- migrate(artifacts, ObjectStorage::Store::LOCAL, &block)
- end
-
- private
-
- def batch_size
- ENV.fetch('MIGRATION_BATCH_SIZE', 10).to_i
- end
-
- def migrate(artifacts, store, &block)
- artifacts.find_each(batch_size: batch_size) do |artifact| # rubocop:disable CodeReuse/ActiveRecord
- artifact.file.migrate!(store)
-
- yield artifact if block
- rescue => e
- raise StandardError.new("Failed to transfer artifact of type #{artifact.file_type} and ID #{artifact.id} with error: #{e.message}")
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/auth.rb b/lib/gitlab/auth.rb
index 1f5cce249d8..c6997288b65 100644
--- a/lib/gitlab/auth.rb
+++ b/lib/gitlab/auth.rb
@@ -29,7 +29,7 @@ module Gitlab
CI_JOB_USER = 'gitlab-ci-token'
class << self
- prepend_if_ee('EE::Gitlab::Auth') # rubocop: disable Cop/InjectEnterpriseEditionModule
+ prepend_mod_with('Gitlab::Auth') # rubocop: disable Cop/InjectEnterpriseEditionModule
def omniauth_enabled?
Gitlab.config.omniauth.enabled
@@ -156,9 +156,9 @@ module Gitlab
underscored_service = matched_login['service'].underscore
- if Service.available_services_names.include?(underscored_service)
+ if Integration.available_services_names.include?(underscored_service)
# We treat underscored_service as a trusted input because it is included
- # in the Service.available_services_names allowlist.
+ # in the Integration.available_services_names allowlist.
service = project.public_send("#{underscored_service}_service") # rubocop:disable GitlabSecurity/PublicSend
if service && service.activated? && service.valid_token?(password)
diff --git a/lib/gitlab/auth/auth_finders.rb b/lib/gitlab/auth/auth_finders.rb
index 6f6ac79c16b..416e36c7ccb 100644
--- a/lib/gitlab/auth/auth_finders.rb
+++ b/lib/gitlab/auth/auth_finders.rb
@@ -160,7 +160,7 @@ module Gitlab
case AccessTokenValidationService.new(access_token, request: request).validate(scopes: scopes)
when AccessTokenValidationService::INSUFFICIENT_SCOPE
- raise InsufficientScopeError.new(scopes)
+ raise InsufficientScopeError, scopes
when AccessTokenValidationService::EXPIRED
raise ExpiredError
when AccessTokenValidationService::REVOKED
@@ -321,4 +321,4 @@ module Gitlab
end
end
-Gitlab::Auth::AuthFinders.prepend_if_ee('::EE::Gitlab::Auth::AuthFinders')
+Gitlab::Auth::AuthFinders.prepend_mod_with('Gitlab::Auth::AuthFinders')
diff --git a/lib/gitlab/auth/database/authentication.rb b/lib/gitlab/auth/database/authentication.rb
index c0dc2b0875f..bf35a9abe41 100644
--- a/lib/gitlab/auth/database/authentication.rb
+++ b/lib/gitlab/auth/database/authentication.rb
@@ -9,6 +9,7 @@ module Gitlab
class Authentication < Gitlab::Auth::OAuth::Authentication
def login(login, password)
return false unless Gitlab::CurrentSettings.password_authentication_enabled_for_git?
+ return false if user.password_based_login_forbidden?
return user if user&.valid_password?(password)
end
diff --git a/lib/gitlab/auth/ldap/access.rb b/lib/gitlab/auth/ldap/access.rb
index 66d20ee2b59..62a817d7c4d 100644
--- a/lib/gitlab/auth/ldap/access.rb
+++ b/lib/gitlab/auth/ldap/access.rb
@@ -117,4 +117,4 @@ module Gitlab
end
end
-Gitlab::Auth::Ldap::Access.prepend_if_ee('::EE::Gitlab::Auth::Ldap::Access')
+Gitlab::Auth::Ldap::Access.prepend_mod_with('Gitlab::Auth::Ldap::Access')
diff --git a/lib/gitlab/auth/ldap/adapter.rb b/lib/gitlab/auth/ldap/adapter.rb
index 7f85d3b1cd3..3853709698b 100644
--- a/lib/gitlab/auth/ldap/adapter.rb
+++ b/lib/gitlab/auth/ldap/adapter.rb
@@ -141,4 +141,4 @@ module Gitlab
end
end
-Gitlab::Auth::Ldap::Adapter.prepend_if_ee('::EE::Gitlab::Auth::Ldap::Adapter')
+Gitlab::Auth::Ldap::Adapter.prepend_mod_with('Gitlab::Auth::Ldap::Adapter')
diff --git a/lib/gitlab/auth/ldap/config.rb b/lib/gitlab/auth/ldap/config.rb
index 97e4f921228..441f0d14b39 100644
--- a/lib/gitlab/auth/ldap/config.rb
+++ b/lib/gitlab/auth/ldap/config.rb
@@ -59,7 +59,7 @@ module Gitlab
end
def self.invalid_provider(provider)
- raise InvalidProvider.new("Unknown provider (#{provider}). Available providers: #{providers}")
+ raise InvalidProvider, "Unknown provider (#{provider}). Available providers: #{providers}"
end
def self.encrypted_secrets
@@ -288,7 +288,7 @@ module Gitlab
def secrets
@secrets ||= self.class.encrypted_secrets[@provider.delete_prefix('ldap').to_sym]
- rescue => e
+ rescue StandardError => e
Gitlab::AppLogger.error "LDAP encrypted secrets are invalid: #{e.inspect}"
nil
@@ -320,4 +320,4 @@ module Gitlab
end
end
-Gitlab::Auth::Ldap::Config.prepend_if_ee('::EE::Gitlab::Auth::Ldap::Config')
+Gitlab::Auth::Ldap::Config.prepend_mod_with('Gitlab::Auth::Ldap::Config')
diff --git a/lib/gitlab/auth/ldap/person.rb b/lib/gitlab/auth/ldap/person.rb
index 102820d6bd5..79e1937478c 100644
--- a/lib/gitlab/auth/ldap/person.rb
+++ b/lib/gitlab/auth/ldap/person.rb
@@ -121,4 +121,4 @@ module Gitlab
end
end
-Gitlab::Auth::Ldap::Person.prepend_if_ee('::EE::Gitlab::Auth::Ldap::Person')
+Gitlab::Auth::Ldap::Person.prepend_mod_with('Gitlab::Auth::Ldap::Person')
diff --git a/lib/gitlab/auth/ldap/user.rb b/lib/gitlab/auth/ldap/user.rb
index 814c17b7e44..d134350775d 100644
--- a/lib/gitlab/auth/ldap/user.rb
+++ b/lib/gitlab/auth/ldap/user.rb
@@ -49,4 +49,4 @@ module Gitlab
end
end
-Gitlab::Auth::Ldap::User.prepend_if_ee('::EE::Gitlab::Auth::Ldap::User')
+Gitlab::Auth::Ldap::User.prepend_mod_with('Gitlab::Auth::Ldap::User')
diff --git a/lib/gitlab/auth/o_auth/auth_hash.rb b/lib/gitlab/auth/o_auth/auth_hash.rb
index 46ff6b2ccab..2ec75669d24 100644
--- a/lib/gitlab/auth/o_auth/auth_hash.rb
+++ b/lib/gitlab/auth/o_auth/auth_hash.rb
@@ -81,7 +81,7 @@ module Gitlab
# Get the first part of the email address (before @)
# In addition in removes illegal characters
def generate_username(email)
- email.match(/^[^@]*/)[0].mb_chars.normalize(:kd).gsub(/[^\x00-\x7F]/, '').to_s
+ email.match(/^[^@]*/)[0].mb_chars.unicode_normalize(:nfkd).gsub(/[^\x00-\x7F]/, '').to_s
end
def generate_temporarily_email(username)
@@ -92,4 +92,4 @@ module Gitlab
end
end
-Gitlab::Auth::OAuth::AuthHash.prepend_if_ee('::EE::Gitlab::Auth::OAuth::AuthHash')
+Gitlab::Auth::OAuth::AuthHash.prepend_mod_with('Gitlab::Auth::OAuth::AuthHash')
diff --git a/lib/gitlab/auth/o_auth/user.rb b/lib/gitlab/auth/o_auth/user.rb
index fe1bf730e76..523452d1074 100644
--- a/lib/gitlab/auth/o_auth/user.rb
+++ b/lib/gitlab/auth/o_auth/user.rb
@@ -115,6 +115,8 @@ module Gitlab
log.info "Correct LDAP account has been found. identity to user: #{gl_user.username}."
gl_user.identities.build(provider: ldap_person.provider, extern_uid: ldap_person.dn)
end
+
+ identity
end
def find_or_build_ldap_user
@@ -292,4 +294,4 @@ module Gitlab
end
end
-Gitlab::Auth::OAuth::User.prepend_if_ee('::EE::Gitlab::Auth::OAuth::User')
+Gitlab::Auth::OAuth::User.prepend_mod_with('Gitlab::Auth::OAuth::User')
diff --git a/lib/gitlab/auth/result.rb b/lib/gitlab/auth/result.rb
index 757a0e671c3..da874524826 100644
--- a/lib/gitlab/auth/result.rb
+++ b/lib/gitlab/auth/result.rb
@@ -25,4 +25,4 @@ module Gitlab
end
end
-Gitlab::Auth::Result.prepend_if_ee('::EE::Gitlab::Auth::Result')
+Gitlab::Auth::Result.prepend_mod_with('Gitlab::Auth::Result')
diff --git a/lib/gitlab/auth/saml/config.rb b/lib/gitlab/auth/saml/config.rb
index 67a53fa3205..3f13a264b0a 100644
--- a/lib/gitlab/auth/saml/config.rb
+++ b/lib/gitlab/auth/saml/config.rb
@@ -30,4 +30,4 @@ module Gitlab
end
end
-Gitlab::Auth::Saml::Config.prepend_if_ee('::EE::Gitlab::Auth::Saml::Config')
+Gitlab::Auth::Saml::Config.prepend_mod_with('Gitlab::Auth::Saml::Config')
diff --git a/lib/gitlab/auth/saml/user.rb b/lib/gitlab/auth/saml/user.rb
index 37bc3f9bed0..205d5fe0015 100644
--- a/lib/gitlab/auth/saml/user.rb
+++ b/lib/gitlab/auth/saml/user.rb
@@ -62,4 +62,4 @@ module Gitlab
end
end
-Gitlab::Auth::Saml::User.prepend_if_ee('::EE::Gitlab::Auth::Saml::User')
+Gitlab::Auth::Saml::User.prepend_mod_with('Gitlab::Auth::Saml::User')
diff --git a/lib/gitlab/authorized_keys.rb b/lib/gitlab/authorized_keys.rb
index 50cd15b7a10..e7eba65bea8 100644
--- a/lib/gitlab/authorized_keys.rb
+++ b/lib/gitlab/authorized_keys.rb
@@ -161,7 +161,7 @@ module Gitlab
end
def strip(key)
- key.split(/[ ]+/)[0, 2].join(' ')
+ key.split(/ +/)[0, 2].join(' ')
end
end
end
diff --git a/lib/gitlab/background_migration/backfill_namespace_traversal_ids_children.rb b/lib/gitlab/background_migration/backfill_namespace_traversal_ids_children.rb
new file mode 100644
index 00000000000..79e7a2f2279
--- /dev/null
+++ b/lib/gitlab/background_migration/backfill_namespace_traversal_ids_children.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # A job to set namespaces.traversal_ids in sub-batches, of all namespaces with
+ # a parent and not already set.
+ # rubocop:disable Style/Documentation
+ class BackfillNamespaceTraversalIdsChildren
+ class Namespace < ActiveRecord::Base
+ include ::EachBatch
+
+ self.table_name = 'namespaces'
+
+ scope :base_query, -> { where.not(parent_id: nil) }
+ end
+
+ PAUSE_SECONDS = 0.1
+
+ def perform(start_id, end_id, sub_batch_size)
+ batch_query = Namespace.base_query.where(id: start_id..end_id)
+ batch_query.each_batch(of: sub_batch_size) do |sub_batch|
+ first, last = sub_batch.pluck(Arel.sql('min(id), max(id)')).first
+ ranged_query = Namespace.unscoped.base_query.where(id: first..last)
+
+ update_sql = <<~SQL
+ UPDATE namespaces
+ SET traversal_ids = calculated_ids.traversal_ids
+ FROM #{calculated_traversal_ids(ranged_query)} calculated_ids
+ WHERE namespaces.id = calculated_ids.id
+ AND namespaces.traversal_ids = '{}'
+ SQL
+ ActiveRecord::Base.connection.execute(update_sql)
+
+ sleep PAUSE_SECONDS
+ end
+
+ # We have to add all arguments when marking a job as succeeded as they
+ # are all used to track the job by `queue_background_migration_jobs_by_range_at_intervals`
+ mark_job_as_succeeded(start_id, end_id, sub_batch_size)
+ end
+
+ private
+
+ # Calculate the ancestor path for a given set of namespaces.
+ def calculated_traversal_ids(batch)
+ <<~SQL
+ (
+ WITH RECURSIVE cte(source_id, namespace_id, parent_id, height) AS (
+ (
+ SELECT batch.id, batch.id, batch.parent_id, 1
+ FROM (#{batch.to_sql}) AS batch
+ )
+ UNION ALL
+ (
+ SELECT cte.source_id, n.id, n.parent_id, cte.height+1
+ FROM namespaces n, cte
+ WHERE n.id = cte.parent_id
+ )
+ )
+ SELECT flat_hierarchy.source_id as id,
+ array_agg(flat_hierarchy.namespace_id ORDER BY flat_hierarchy.height DESC) as traversal_ids
+ FROM (SELECT * FROM cte FOR UPDATE) flat_hierarchy
+ GROUP BY flat_hierarchy.source_id
+ )
+ SQL
+ end
+
+ def mark_job_as_succeeded(*arguments)
+ Gitlab::Database::BackgroundMigrationJob.mark_all_as_succeeded(
+ 'BackfillNamespaceTraversalIdsChildren',
+ arguments
+ )
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/backfill_namespace_traversal_ids_roots.rb b/lib/gitlab/background_migration/backfill_namespace_traversal_ids_roots.rb
new file mode 100644
index 00000000000..1c0a83285a6
--- /dev/null
+++ b/lib/gitlab/background_migration/backfill_namespace_traversal_ids_roots.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # A job to set namespaces.traversal_ids in sub-batches, of all namespaces
+ # without a parent and not already set.
+ # rubocop:disable Style/Documentation
+ class BackfillNamespaceTraversalIdsRoots
+ class Namespace < ActiveRecord::Base
+ include ::EachBatch
+
+ self.table_name = 'namespaces'
+
+ scope :base_query, -> { where(parent_id: nil) }
+ end
+
+ PAUSE_SECONDS = 0.1
+
+ def perform(start_id, end_id, sub_batch_size)
+ ranged_query = Namespace.base_query
+ .where(id: start_id..end_id)
+ .where("traversal_ids = '{}'")
+
+ ranged_query.each_batch(of: sub_batch_size) do |sub_batch|
+ first, last = sub_batch.pluck(Arel.sql('min(id), max(id)')).first
+
+ # The query need to be reconstructed because .each_batch modifies the default scope
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/330510
+ Namespace.unscoped
+ .base_query
+ .where(id: first..last)
+ .where("traversal_ids = '{}'")
+ .update_all('traversal_ids = ARRAY[id]')
+
+ sleep PAUSE_SECONDS
+ end
+
+ mark_job_as_succeeded(start_id, end_id, sub_batch_size)
+ end
+
+ private
+
+ def mark_job_as_succeeded(*arguments)
+ Gitlab::Database::BackgroundMigrationJob.mark_all_as_succeeded(
+ 'BackfillNamespaceTraversalIdsRoots',
+ arguments
+ )
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/backfill_snippet_repositories.rb b/lib/gitlab/background_migration/backfill_snippet_repositories.rb
index 8befade8c3a..6f37f1846d2 100644
--- a/lib/gitlab/background_migration/backfill_snippet_repositories.rb
+++ b/lib/gitlab/background_migration/backfill_snippet_repositories.rb
@@ -36,7 +36,7 @@ module Gitlab
create_repository_and_files(snippet)
logger.info(message: 'Snippet Migration: repository created and migrated', snippet: snippet.id)
- rescue => e
+ rescue StandardError => e
set_file_path_error(e)
set_signature_error(e)
@@ -68,7 +68,7 @@ module Gitlab
# Removing the db record
def destroy_snippet_repository(snippet)
snippet.snippet_repository&.delete
- rescue => e
+ rescue StandardError => e
logger.error(message: "Snippet Migration: error destroying snippet repository. Reason: #{e.message}", snippet: snippet.id)
end
@@ -78,7 +78,7 @@ module Gitlab
snippet.repository.remove
snippet.repository.expire_exists_cache
- rescue => e
+ rescue StandardError => e
logger.error(message: "Snippet Migration: error deleting repository. Reason: #{e.message}", snippet: snippet.id)
end
diff --git a/lib/gitlab/background_migration/backfill_version_data_from_gitaly.rb b/lib/gitlab/background_migration/backfill_version_data_from_gitaly.rb
index 83d60d2db19..41f7f7f2f24 100644
--- a/lib/gitlab/background_migration/backfill_version_data_from_gitaly.rb
+++ b/lib/gitlab/background_migration/backfill_version_data_from_gitaly.rb
@@ -10,4 +10,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::BackfillVersionDataFromGitaly.prepend_if_ee('EE::Gitlab::BackgroundMigration::BackfillVersionDataFromGitaly')
+Gitlab::BackgroundMigration::BackfillVersionDataFromGitaly.prepend_mod_with('Gitlab::BackgroundMigration::BackfillVersionDataFromGitaly')
diff --git a/lib/gitlab/background_migration/calculate_wiki_sizes.rb b/lib/gitlab/background_migration/calculate_wiki_sizes.rb
index 76598f6e2a6..7b334b9c1d0 100644
--- a/lib/gitlab/background_migration/calculate_wiki_sizes.rb
+++ b/lib/gitlab/background_migration/calculate_wiki_sizes.rb
@@ -9,7 +9,7 @@ module Gitlab
.where(id: start_id..stop_id)
.includes(project: [:route, :group, namespace: [:owner]]).find_each do |statistics|
statistics.refresh!(only: [:wiki_size])
- rescue => e
+ rescue StandardError => e
Gitlab::AppLogger.error "Failed to update wiki statistics. id: #{statistics.id} message: #{e.message}"
end
end
diff --git a/lib/gitlab/background_migration/copy_column_using_background_migration_job.rb b/lib/gitlab/background_migration/copy_column_using_background_migration_job.rb
index b89ea7dc250..529b8cdf8d4 100644
--- a/lib/gitlab/background_migration/copy_column_using_background_migration_job.rb
+++ b/lib/gitlab/background_migration/copy_column_using_background_migration_job.rb
@@ -2,8 +2,8 @@
module Gitlab
module BackgroundMigration
- # Background migration that updates the value of a
- # column using the value of another column in the same table.
+ # Background migration that updates the value of one or more
+ # columns using the value of other columns in the same table.
#
# - The {start_id, end_id} arguments are at the start so that it can be used
# with `queue_batched_background_migration`
@@ -16,8 +16,6 @@ module Gitlab
class CopyColumnUsingBackgroundMigrationJob
include Gitlab::Database::DynamicModelHelpers
- PAUSE_SECONDS = 0.1
-
# start_id - The start ID of the range of rows to update.
# end_id - The end ID of the range of rows to update.
# batch_table - The name of the table that contains the columns.
@@ -25,20 +23,26 @@ module Gitlab
# sub_batch_size - We don't want updates to take more than ~100ms
# This allows us to run multiple smaller batches during
# the minimum 2.minute interval that we can schedule jobs
- # copy_from - The column containing the data to copy.
- # copy_to - The column to copy the data to.
- def perform(start_id, end_id, batch_table, batch_column, sub_batch_size, copy_from, copy_to)
- quoted_copy_from = connection.quote_column_name(copy_from)
- quoted_copy_to = connection.quote_column_name(copy_to)
+ # pause_ms - The number of milliseconds to sleep between each subbatch execution.
+ # copy_from - List of columns containing the data to copy.
+ # copy_to - List of columns to copy the data to. Order must match the order in `copy_from`.
+ def perform(start_id, end_id, batch_table, batch_column, sub_batch_size, pause_ms, copy_from, copy_to)
+ copy_from = Array.wrap(copy_from)
+ copy_to = Array.wrap(copy_to)
+
+ raise ArgumentError, 'number of source and destination columns must match' unless copy_from.count == copy_to.count
+
+ assignment_clauses = column_assignment_clauses(copy_from, copy_to)
parent_batch_relation = relation_scoped_to_range(batch_table, batch_column, start_id, end_id)
parent_batch_relation.each_batch(column: batch_column, of: sub_batch_size) do |sub_batch|
batch_metrics.time_operation(:update_all) do
- sub_batch.update_all("#{quoted_copy_to}=#{quoted_copy_from}")
+ sub_batch.update_all(assignment_clauses)
end
- sleep(PAUSE_SECONDS)
+ pause_ms = 0 if pause_ms < 0
+ sleep(pause_ms * 0.001)
end
end
@@ -55,6 +59,17 @@ module Gitlab
def relation_scoped_to_range(source_table, source_key_column, start_id, stop_id)
define_batchable_model(source_table).where(source_key_column => start_id..stop_id)
end
+
+ def column_assignment_clauses(copy_from, copy_to)
+ assignments = copy_from.zip(copy_to).map do |from_column, to_column|
+ from_column = connection.quote_column_name(from_column)
+ to_column = connection.quote_column_name(to_column)
+
+ "#{to_column} = #{from_column}"
+ end
+
+ assignments.join(', ')
+ end
end
end
end
diff --git a/lib/gitlab/background_migration/drop_invalid_vulnerabilities.rb b/lib/gitlab/background_migration/drop_invalid_vulnerabilities.rb
new file mode 100644
index 00000000000..293530f6536
--- /dev/null
+++ b/lib/gitlab/background_migration/drop_invalid_vulnerabilities.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+# rubocop: disable Style/Documentation
+class Gitlab::BackgroundMigration::DropInvalidVulnerabilities
+ # rubocop: disable Gitlab/NamespacedClass
+ class Vulnerability < ActiveRecord::Base
+ self.table_name = "vulnerabilities"
+ has_many :findings, class_name: 'VulnerabilitiesFinding', inverse_of: :vulnerability
+ end
+
+ class VulnerabilitiesFinding < ActiveRecord::Base
+ self.table_name = "vulnerability_occurrences"
+ belongs_to :vulnerability, class_name: 'Vulnerability', inverse_of: :findings, foreign_key: 'vulnerability_id'
+ end
+ # rubocop: enable Gitlab/NamespacedClass
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def perform(start_id, end_id)
+ Vulnerability
+ .where(id: start_id..end_id)
+ .left_joins(:findings)
+ .where(vulnerability_occurrences: { vulnerability_id: nil })
+ .delete_all
+
+ mark_job_as_succeeded(start_id, end_id)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ private
+
+ def mark_job_as_succeeded(*arguments)
+ Gitlab::Database::BackgroundMigrationJob.mark_all_as_succeeded(
+ 'DropInvalidVulnerabilities',
+ arguments
+ )
+ end
+end
diff --git a/lib/gitlab/background_migration/fill_valid_time_for_pages_domain_certificate.rb b/lib/gitlab/background_migration/fill_valid_time_for_pages_domain_certificate.rb
index c0099d44b5a..7b5c32e3d6d 100644
--- a/lib/gitlab/background_migration/fill_valid_time_for_pages_domain_certificate.rb
+++ b/lib/gitlab/background_migration/fill_valid_time_for_pages_domain_certificate.rb
@@ -24,7 +24,7 @@ module Gitlab
certificate_valid_not_before: domain.x509&.not_before&.iso8601,
certificate_valid_not_after: domain.x509&.not_after&.iso8601
)
- rescue => e
+ rescue StandardError => e
Gitlab::AppLogger.error "Failed to update pages domain certificate valid time. id: #{domain.id}, message: #{e.message}"
end
end
diff --git a/lib/gitlab/background_migration/fix_orphan_promoted_issues.rb b/lib/gitlab/background_migration/fix_orphan_promoted_issues.rb
index d6ec56ae19e..c50bf430d92 100644
--- a/lib/gitlab/background_migration/fix_orphan_promoted_issues.rb
+++ b/lib/gitlab/background_migration/fix_orphan_promoted_issues.rb
@@ -10,4 +10,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::FixOrphanPromotedIssues.prepend_if_ee('EE::Gitlab::BackgroundMigration::FixOrphanPromotedIssues')
+Gitlab::BackgroundMigration::FixOrphanPromotedIssues.prepend_mod_with('Gitlab::BackgroundMigration::FixOrphanPromotedIssues')
diff --git a/lib/gitlab/background_migration/fix_ruby_object_in_audit_events.rb b/lib/gitlab/background_migration/fix_ruby_object_in_audit_events.rb
index 46921a070c3..47a68c61fcc 100644
--- a/lib/gitlab/background_migration/fix_ruby_object_in_audit_events.rb
+++ b/lib/gitlab/background_migration/fix_ruby_object_in_audit_events.rb
@@ -10,4 +10,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::FixRubyObjectInAuditEvents.prepend_if_ee('EE::Gitlab::BackgroundMigration::FixRubyObjectInAuditEvents')
+Gitlab::BackgroundMigration::FixRubyObjectInAuditEvents.prepend_mod_with('Gitlab::BackgroundMigration::FixRubyObjectInAuditEvents')
diff --git a/lib/gitlab/background_migration/generate_gitlab_subscriptions.rb b/lib/gitlab/background_migration/generate_gitlab_subscriptions.rb
index 85bcf8558f2..160e6d2fe8b 100644
--- a/lib/gitlab/background_migration/generate_gitlab_subscriptions.rb
+++ b/lib/gitlab/background_migration/generate_gitlab_subscriptions.rb
@@ -10,4 +10,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::GenerateGitlabSubscriptions.prepend_if_ee('EE::Gitlab::BackgroundMigration::GenerateGitlabSubscriptions')
+Gitlab::BackgroundMigration::GenerateGitlabSubscriptions.prepend_mod_with('Gitlab::BackgroundMigration::GenerateGitlabSubscriptions')
diff --git a/lib/gitlab/background_migration/migrate_approver_to_approval_rules.rb b/lib/gitlab/background_migration/migrate_approver_to_approval_rules.rb
index 27b984b4531..ba66721f65c 100644
--- a/lib/gitlab/background_migration/migrate_approver_to_approval_rules.rb
+++ b/lib/gitlab/background_migration/migrate_approver_to_approval_rules.rb
@@ -12,4 +12,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::MigrateApproverToApprovalRules.prepend_if_ee('EE::Gitlab::BackgroundMigration::MigrateApproverToApprovalRules')
+Gitlab::BackgroundMigration::MigrateApproverToApprovalRules.prepend_mod_with('Gitlab::BackgroundMigration::MigrateApproverToApprovalRules')
diff --git a/lib/gitlab/background_migration/migrate_approver_to_approval_rules_check_progress.rb b/lib/gitlab/background_migration/migrate_approver_to_approval_rules_check_progress.rb
index 053b7363286..4899c50b9cf 100644
--- a/lib/gitlab/background_migration/migrate_approver_to_approval_rules_check_progress.rb
+++ b/lib/gitlab/background_migration/migrate_approver_to_approval_rules_check_progress.rb
@@ -10,4 +10,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::MigrateApproverToApprovalRulesCheckProgress.prepend_if_ee('EE::Gitlab::BackgroundMigration::MigrateApproverToApprovalRulesCheckProgress')
+Gitlab::BackgroundMigration::MigrateApproverToApprovalRulesCheckProgress.prepend_mod_with('Gitlab::BackgroundMigration::MigrateApproverToApprovalRulesCheckProgress')
diff --git a/lib/gitlab/background_migration/migrate_approver_to_approval_rules_in_batch.rb b/lib/gitlab/background_migration/migrate_approver_to_approval_rules_in_batch.rb
index 130f97b09d7..2855566d7e8 100644
--- a/lib/gitlab/background_migration/migrate_approver_to_approval_rules_in_batch.rb
+++ b/lib/gitlab/background_migration/migrate_approver_to_approval_rules_in_batch.rb
@@ -10,4 +10,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::MigrateApproverToApprovalRulesInBatch.prepend_if_ee('EE::Gitlab::BackgroundMigration::MigrateApproverToApprovalRulesInBatch')
+Gitlab::BackgroundMigration::MigrateApproverToApprovalRulesInBatch.prepend_mod_with('Gitlab::BackgroundMigration::MigrateApproverToApprovalRulesInBatch')
diff --git a/lib/gitlab/background_migration/migrate_devops_segments_to_groups.rb b/lib/gitlab/background_migration/migrate_devops_segments_to_groups.rb
index de2d9909961..d85f980d3f1 100644
--- a/lib/gitlab/background_migration/migrate_devops_segments_to_groups.rb
+++ b/lib/gitlab/background_migration/migrate_devops_segments_to_groups.rb
@@ -10,4 +10,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::MigrateDevopsSegmentsToGroups.prepend_if_ee('EE::Gitlab::BackgroundMigration::MigrateDevopsSegmentsToGroups')
+Gitlab::BackgroundMigration::MigrateDevopsSegmentsToGroups.prepend_mod_with('Gitlab::BackgroundMigration::MigrateDevopsSegmentsToGroups')
diff --git a/lib/gitlab/background_migration/migrate_project_taggings_context_from_tags_to_topics.rb b/lib/gitlab/background_migration/migrate_project_taggings_context_from_tags_to_topics.rb
new file mode 100644
index 00000000000..68bbd3cfebb
--- /dev/null
+++ b/lib/gitlab/background_migration/migrate_project_taggings_context_from_tags_to_topics.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # The class to migrate the context of project taggings from `tags` to `topics`
+ class MigrateProjectTaggingsContextFromTagsToTopics
+ # Temporary AR table for taggings
+ class Tagging < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'taggings'
+ end
+
+ def perform(start_id, stop_id)
+ Tagging.where(taggable_type: 'Project', context: 'tags', id: start_id..stop_id).each_batch(of: 500) do |relation|
+ relation.update_all(context: 'topics')
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/migrate_security_scans.rb b/lib/gitlab/background_migration/migrate_security_scans.rb
index 189a150cb87..0ae984f2dbc 100644
--- a/lib/gitlab/background_migration/migrate_security_scans.rb
+++ b/lib/gitlab/background_migration/migrate_security_scans.rb
@@ -10,4 +10,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::MigrateSecurityScans.prepend_if_ee('EE::Gitlab::BackgroundMigration::MigrateSecurityScans')
+Gitlab::BackgroundMigration::MigrateSecurityScans.prepend_mod_with('Gitlab::BackgroundMigration::MigrateSecurityScans')
diff --git a/lib/gitlab/background_migration/move_container_registry_enabled_to_project_feature.rb b/lib/gitlab/background_migration/move_container_registry_enabled_to_project_feature.rb
index 9ecf53317d0..c01545e5dca 100644
--- a/lib/gitlab/background_migration/move_container_registry_enabled_to_project_feature.rb
+++ b/lib/gitlab/background_migration/move_container_registry_enabled_to_project_feature.rb
@@ -8,18 +8,15 @@ module Gitlab
class MoveContainerRegistryEnabledToProjectFeature
MAX_BATCH_SIZE = 300
- module Migratable
- # Migration model namespace isolated from application code.
- class ProjectFeature < ActiveRecord::Base
- ENABLED = 20
- DISABLED = 0
- end
- end
+ ENABLED = 20
+ DISABLED = 0
def perform(from_id, to_id)
(from_id..to_id).each_slice(MAX_BATCH_SIZE) do |batch|
process_batch(batch.first, batch.last)
end
+
+ Gitlab::Database::BackgroundMigrationJob.mark_all_as_succeeded('MoveContainerRegistryEnabledToProjectFeature', [from_id, to_id])
end
private
@@ -37,9 +34,9 @@ module Gitlab
<<~SQL
UPDATE project_features
SET container_registry_access_level = (CASE p.container_registry_enabled
- WHEN true THEN #{ProjectFeature::ENABLED}
- WHEN false THEN #{ProjectFeature::DISABLED}
- ELSE #{ProjectFeature::DISABLED}
+ WHEN true THEN #{ENABLED}
+ WHEN false THEN #{DISABLED}
+ ELSE #{DISABLED}
END)
FROM projects p
WHERE project_id = p.id AND
diff --git a/lib/gitlab/background_migration/move_epic_issues_after_epics.rb b/lib/gitlab/background_migration/move_epic_issues_after_epics.rb
index dc982e703d1..174994c7862 100644
--- a/lib/gitlab/background_migration/move_epic_issues_after_epics.rb
+++ b/lib/gitlab/background_migration/move_epic_issues_after_epics.rb
@@ -10,4 +10,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::MoveEpicIssuesAfterEpics.prepend_if_ee('EE::Gitlab::BackgroundMigration::MoveEpicIssuesAfterEpics')
+Gitlab::BackgroundMigration::MoveEpicIssuesAfterEpics.prepend_mod_with('Gitlab::BackgroundMigration::MoveEpicIssuesAfterEpics')
diff --git a/lib/gitlab/background_migration/populate_any_approval_rule_for_merge_requests.rb b/lib/gitlab/background_migration/populate_any_approval_rule_for_merge_requests.rb
index c3c0db2495c..890a43800c9 100644
--- a/lib/gitlab/background_migration/populate_any_approval_rule_for_merge_requests.rb
+++ b/lib/gitlab/background_migration/populate_any_approval_rule_for_merge_requests.rb
@@ -11,4 +11,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::PopulateAnyApprovalRuleForMergeRequests.prepend_if_ee('EE::Gitlab::BackgroundMigration::PopulateAnyApprovalRuleForMergeRequests')
+Gitlab::BackgroundMigration::PopulateAnyApprovalRuleForMergeRequests.prepend_mod_with('Gitlab::BackgroundMigration::PopulateAnyApprovalRuleForMergeRequests')
diff --git a/lib/gitlab/background_migration/populate_any_approval_rule_for_projects.rb b/lib/gitlab/background_migration/populate_any_approval_rule_for_projects.rb
index 2243c7531c0..ac7ed18ba14 100644
--- a/lib/gitlab/background_migration/populate_any_approval_rule_for_projects.rb
+++ b/lib/gitlab/background_migration/populate_any_approval_rule_for_projects.rb
@@ -11,4 +11,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::PopulateAnyApprovalRuleForProjects.prepend_if_ee('EE::Gitlab::BackgroundMigration::PopulateAnyApprovalRuleForProjects')
+Gitlab::BackgroundMigration::PopulateAnyApprovalRuleForProjects.prepend_mod_with('Gitlab::BackgroundMigration::PopulateAnyApprovalRuleForProjects')
diff --git a/lib/gitlab/background_migration/populate_namespace_statistics.rb b/lib/gitlab/background_migration/populate_namespace_statistics.rb
index e352ae71de6..e873ad412f2 100644
--- a/lib/gitlab/background_migration/populate_namespace_statistics.rb
+++ b/lib/gitlab/background_migration/populate_namespace_statistics.rb
@@ -13,4 +13,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::PopulateNamespaceStatistics.prepend_if_ee('EE::Gitlab::BackgroundMigration::PopulateNamespaceStatistics')
+Gitlab::BackgroundMigration::PopulateNamespaceStatistics.prepend_mod_with('Gitlab::BackgroundMigration::PopulateNamespaceStatistics')
diff --git a/lib/gitlab/background_migration/populate_personal_snippet_statistics.rb b/lib/gitlab/background_migration/populate_personal_snippet_statistics.rb
index e8f436b183e..ed7ffce8018 100644
--- a/lib/gitlab/background_migration/populate_personal_snippet_statistics.rb
+++ b/lib/gitlab/background_migration/populate_personal_snippet_statistics.rb
@@ -33,7 +33,7 @@ module Gitlab
def update_namespace_statistics(namespace)
Namespaces::StatisticsRefresherService.new.execute(namespace)
- rescue => e
+ rescue StandardError => e
error_message("Error updating statistics for namespace #{namespace.id}: #{e.message}")
end
diff --git a/lib/gitlab/background_migration/populate_project_snippet_statistics.rb b/lib/gitlab/background_migration/populate_project_snippet_statistics.rb
index 7659b63271f..37af320f044 100644
--- a/lib/gitlab/background_migration/populate_project_snippet_statistics.rb
+++ b/lib/gitlab/background_migration/populate_project_snippet_statistics.rb
@@ -11,12 +11,12 @@ module Gitlab
namespace_snippets.group_by(&:project).each do |project, snippets|
upsert_snippet_statistics(snippets)
update_project_statistics(project)
- rescue
+ rescue StandardError
error_message("Error updating statistics for project #{project.id}")
end
update_namespace_statistics(namespace_snippets.first.project.root_namespace)
- rescue => e
+ rescue StandardError => e
error_message("Error updating statistics for namespace #{namespace_id}: #{e.message}")
end
end
diff --git a/lib/gitlab/background_migration/populate_resolved_on_default_branch_column.rb b/lib/gitlab/background_migration/populate_resolved_on_default_branch_column.rb
index eb72ef1de33..e95955c450d 100644
--- a/lib/gitlab/background_migration/populate_resolved_on_default_branch_column.rb
+++ b/lib/gitlab/background_migration/populate_resolved_on_default_branch_column.rb
@@ -9,4 +9,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::PopulateResolvedOnDefaultBranchColumn.prepend_if_ee('EE::Gitlab::BackgroundMigration::PopulateResolvedOnDefaultBranchColumn')
+Gitlab::BackgroundMigration::PopulateResolvedOnDefaultBranchColumn.prepend_mod_with('Gitlab::BackgroundMigration::PopulateResolvedOnDefaultBranchColumn')
diff --git a/lib/gitlab/background_migration/populate_uuids_for_security_findings.rb b/lib/gitlab/background_migration/populate_uuids_for_security_findings.rb
index 4aff9d1e2c1..175966b940d 100644
--- a/lib/gitlab/background_migration/populate_uuids_for_security_findings.rb
+++ b/lib/gitlab/background_migration/populate_uuids_for_security_findings.rb
@@ -15,4 +15,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::PopulateUuidsForSecurityFindings.prepend_if_ee('::EE::Gitlab::BackgroundMigration::PopulateUuidsForSecurityFindings')
+Gitlab::BackgroundMigration::PopulateUuidsForSecurityFindings.prepend_mod_with('Gitlab::BackgroundMigration::PopulateUuidsForSecurityFindings')
diff --git a/lib/gitlab/background_migration/populate_vulnerability_feedback_pipeline_id.rb b/lib/gitlab/background_migration/populate_vulnerability_feedback_pipeline_id.rb
index fc79f7125e3..8241fea66db 100644
--- a/lib/gitlab/background_migration/populate_vulnerability_feedback_pipeline_id.rb
+++ b/lib/gitlab/background_migration/populate_vulnerability_feedback_pipeline_id.rb
@@ -10,4 +10,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::PopulateVulnerabilityFeedbackPipelineId.prepend_if_ee('EE::Gitlab::BackgroundMigration::PopulateVulnerabilityFeedbackPipelineId')
+Gitlab::BackgroundMigration::PopulateVulnerabilityFeedbackPipelineId.prepend_mod_with('Gitlab::BackgroundMigration::PopulateVulnerabilityFeedbackPipelineId')
diff --git a/lib/gitlab/background_migration/populate_vulnerability_historical_statistics.rb b/lib/gitlab/background_migration/populate_vulnerability_historical_statistics.rb
index 2e81b1615d8..9a9f23e29ea 100644
--- a/lib/gitlab/background_migration/populate_vulnerability_historical_statistics.rb
+++ b/lib/gitlab/background_migration/populate_vulnerability_historical_statistics.rb
@@ -11,4 +11,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::PopulateVulnerabilityHistoricalStatistics.prepend_if_ee('EE::Gitlab::BackgroundMigration::PopulateVulnerabilityHistoricalStatistics')
+Gitlab::BackgroundMigration::PopulateVulnerabilityHistoricalStatistics.prepend_mod_with('Gitlab::BackgroundMigration::PopulateVulnerabilityHistoricalStatistics')
diff --git a/lib/gitlab/background_migration/prune_orphaned_geo_events.rb b/lib/gitlab/background_migration/prune_orphaned_geo_events.rb
index 8b16db8be35..0efbe72775c 100644
--- a/lib/gitlab/background_migration/prune_orphaned_geo_events.rb
+++ b/lib/gitlab/background_migration/prune_orphaned_geo_events.rb
@@ -14,4 +14,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::PruneOrphanedGeoEvents.prepend_if_ee('EE::Gitlab::BackgroundMigration::PruneOrphanedGeoEvents')
+Gitlab::BackgroundMigration::PruneOrphanedGeoEvents.prepend_mod_with('Gitlab::BackgroundMigration::PruneOrphanedGeoEvents')
diff --git a/lib/gitlab/background_migration/recalculate_project_authorizations.rb b/lib/gitlab/background_migration/recalculate_project_authorizations.rb
index 3d2ce9fc10c..6a250a96c94 100644
--- a/lib/gitlab/background_migration/recalculate_project_authorizations.rb
+++ b/lib/gitlab/background_migration/recalculate_project_authorizations.rb
@@ -5,37 +5,7 @@ module Gitlab
# rubocop:disable Style/Documentation
class RecalculateProjectAuthorizations
def perform(user_ids)
- user_ids.each do |user_id|
- user = User.find_by(id: user_id)
-
- next unless user
-
- service = Users::RefreshAuthorizedProjectsService.new(
- user,
- incorrect_auth_found_callback:
- ->(project_id, access_level) do
- logger.info(message: 'Removing ProjectAuthorizations',
- user_id: user.id,
- project_id: project_id,
- access_level: access_level)
- end,
- missing_auth_found_callback:
- ->(project_id, access_level) do
- logger.info(message: 'Creating ProjectAuthorizations',
- user_id: user.id,
- project_id: project_id,
- access_level: access_level)
- end
- )
-
- service.execute
- end
- end
-
- private
-
- def logger
- @logger ||= Gitlab::BackgroundMigration::Logger.build
+ # no-op
end
end
end
diff --git a/lib/gitlab/background_migration/remove_duplicate_cs_findings.rb b/lib/gitlab/background_migration/remove_duplicate_cs_findings.rb
index cc9b0329556..17ef6dec4c0 100644
--- a/lib/gitlab/background_migration/remove_duplicate_cs_findings.rb
+++ b/lib/gitlab/background_migration/remove_duplicate_cs_findings.rb
@@ -10,4 +10,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::RemoveDuplicateCsFindings.prepend_if_ee('EE::Gitlab::BackgroundMigration::RemoveDuplicateCsFindings')
+Gitlab::BackgroundMigration::RemoveDuplicateCsFindings.prepend_mod_with('Gitlab::BackgroundMigration::RemoveDuplicateCsFindings')
diff --git a/lib/gitlab/background_migration/remove_duplicated_cs_findings_without_vulnerability_id.rb b/lib/gitlab/background_migration/remove_duplicated_cs_findings_without_vulnerability_id.rb
index cd305adc7cd..e5772fc7375 100644
--- a/lib/gitlab/background_migration/remove_duplicated_cs_findings_without_vulnerability_id.rb
+++ b/lib/gitlab/background_migration/remove_duplicated_cs_findings_without_vulnerability_id.rb
@@ -10,4 +10,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::RemoveDuplicatedCsFindingsWithoutVulnerabilityId.prepend_if_ee('EE::Gitlab::BackgroundMigration::RemoveDuplicatedCsFindingsWithoutVulnerabilityId')
+Gitlab::BackgroundMigration::RemoveDuplicatedCsFindingsWithoutVulnerabilityId.prepend_mod_with('Gitlab::BackgroundMigration::RemoveDuplicatedCsFindingsWithoutVulnerabilityId')
diff --git a/lib/gitlab/background_migration/remove_inaccessible_epic_todos.rb b/lib/gitlab/background_migration/remove_inaccessible_epic_todos.rb
index 74c48b237cc..cb6a600a525 100644
--- a/lib/gitlab/background_migration/remove_inaccessible_epic_todos.rb
+++ b/lib/gitlab/background_migration/remove_inaccessible_epic_todos.rb
@@ -10,4 +10,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::RemoveInaccessibleEpicTodos.prepend_if_ee('EE::Gitlab::BackgroundMigration::RemoveInaccessibleEpicTodos')
+Gitlab::BackgroundMigration::RemoveInaccessibleEpicTodos.prepend_mod_with('Gitlab::BackgroundMigration::RemoveInaccessibleEpicTodos')
diff --git a/lib/gitlab/background_migration/remove_undefined_occurrence_confidence_level.rb b/lib/gitlab/background_migration/remove_undefined_occurrence_confidence_level.rb
index 3920e8dc2de..540ffc6f548 100644
--- a/lib/gitlab/background_migration/remove_undefined_occurrence_confidence_level.rb
+++ b/lib/gitlab/background_migration/remove_undefined_occurrence_confidence_level.rb
@@ -10,4 +10,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::RemoveUndefinedOccurrenceConfidenceLevel.prepend_if_ee('EE::Gitlab::BackgroundMigration::RemoveUndefinedOccurrenceConfidenceLevel')
+Gitlab::BackgroundMigration::RemoveUndefinedOccurrenceConfidenceLevel.prepend_mod_with('Gitlab::BackgroundMigration::RemoveUndefinedOccurrenceConfidenceLevel')
diff --git a/lib/gitlab/background_migration/remove_undefined_occurrence_severity_level.rb b/lib/gitlab/background_migration/remove_undefined_occurrence_severity_level.rb
index f137e41c728..cecb385afa0 100644
--- a/lib/gitlab/background_migration/remove_undefined_occurrence_severity_level.rb
+++ b/lib/gitlab/background_migration/remove_undefined_occurrence_severity_level.rb
@@ -10,4 +10,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::RemoveUndefinedOccurrenceSeverityLevel.prepend_if_ee('EE::Gitlab::BackgroundMigration::RemoveUndefinedOccurrenceSeverityLevel')
+Gitlab::BackgroundMigration::RemoveUndefinedOccurrenceSeverityLevel.prepend_mod_with('Gitlab::BackgroundMigration::RemoveUndefinedOccurrenceSeverityLevel')
diff --git a/lib/gitlab/background_migration/remove_undefined_vulnerability_confidence_level.rb b/lib/gitlab/background_migration/remove_undefined_vulnerability_confidence_level.rb
index f6ea61f4502..4be61bfb689 100644
--- a/lib/gitlab/background_migration/remove_undefined_vulnerability_confidence_level.rb
+++ b/lib/gitlab/background_migration/remove_undefined_vulnerability_confidence_level.rb
@@ -10,4 +10,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::RemoveUndefinedVulnerabilityConfidenceLevel.prepend_if_ee('EE::Gitlab::BackgroundMigration::RemoveUndefinedVulnerabilityConfidenceLevel')
+Gitlab::BackgroundMigration::RemoveUndefinedVulnerabilityConfidenceLevel.prepend_mod_with('Gitlab::BackgroundMigration::RemoveUndefinedVulnerabilityConfidenceLevel')
diff --git a/lib/gitlab/background_migration/remove_undefined_vulnerability_severity_level.rb b/lib/gitlab/background_migration/remove_undefined_vulnerability_severity_level.rb
index 95540cd5f49..1ea483f929f 100644
--- a/lib/gitlab/background_migration/remove_undefined_vulnerability_severity_level.rb
+++ b/lib/gitlab/background_migration/remove_undefined_vulnerability_severity_level.rb
@@ -10,4 +10,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::RemoveUndefinedVulnerabilitySeverityLevel.prepend_if_ee('EE::Gitlab::BackgroundMigration::RemoveUndefinedVulnerabilitySeverityLevel')
+Gitlab::BackgroundMigration::RemoveUndefinedVulnerabilitySeverityLevel.prepend_mod_with('Gitlab::BackgroundMigration::RemoveUndefinedVulnerabilitySeverityLevel')
diff --git a/lib/gitlab/background_migration/sync_blocking_issues_count.rb b/lib/gitlab/background_migration/sync_blocking_issues_count.rb
index 6262320128c..49a632952fb 100644
--- a/lib/gitlab/background_migration/sync_blocking_issues_count.rb
+++ b/lib/gitlab/background_migration/sync_blocking_issues_count.rb
@@ -10,4 +10,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::SyncBlockingIssuesCount.prepend_if_ee('EE::Gitlab::BackgroundMigration::SyncBlockingIssuesCount')
+Gitlab::BackgroundMigration::SyncBlockingIssuesCount.prepend_mod_with('Gitlab::BackgroundMigration::SyncBlockingIssuesCount')
diff --git a/lib/gitlab/background_migration/update_location_fingerprint_for_container_scanning_findings.rb b/lib/gitlab/background_migration/update_location_fingerprint_for_container_scanning_findings.rb
index 651df36fcfd..054b918dade 100644
--- a/lib/gitlab/background_migration/update_location_fingerprint_for_container_scanning_findings.rb
+++ b/lib/gitlab/background_migration/update_location_fingerprint_for_container_scanning_findings.rb
@@ -10,4 +10,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::UpdateLocationFingerprintForContainerScanningFindings.prepend_if_ee('EE::Gitlab::BackgroundMigration::UpdateLocationFingerprintForContainerScanningFindings')
+Gitlab::BackgroundMigration::UpdateLocationFingerprintForContainerScanningFindings.prepend_mod_with('Gitlab::BackgroundMigration::UpdateLocationFingerprintForContainerScanningFindings')
diff --git a/lib/gitlab/background_migration/update_timelogs_project_id.rb b/lib/gitlab/background_migration/update_timelogs_project_id.rb
new file mode 100644
index 00000000000..24c9967b88e
--- /dev/null
+++ b/lib/gitlab/background_migration/update_timelogs_project_id.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # Class to populate project_id for timelogs
+ class UpdateTimelogsProjectId
+ BATCH_SIZE = 1000
+
+ def perform(start_id, stop_id)
+ (start_id..stop_id).step(BATCH_SIZE).each do |offset|
+ update_issue_timelogs(offset, offset + BATCH_SIZE)
+ update_merge_request_timelogs(offset, offset + BATCH_SIZE)
+ end
+ end
+
+ def update_issue_timelogs(batch_start, batch_stop)
+ execute(<<~SQL)
+ UPDATE timelogs
+ SET project_id = issues.project_id
+ FROM issues
+ WHERE issues.id = timelogs.issue_id
+ AND timelogs.id BETWEEN #{batch_start} AND #{batch_stop}
+ AND timelogs.project_id IS NULL;
+ SQL
+ end
+
+ def update_merge_request_timelogs(batch_start, batch_stop)
+ execute(<<~SQL)
+ UPDATE timelogs
+ SET project_id = merge_requests.target_project_id
+ FROM merge_requests
+ WHERE merge_requests.id = timelogs.merge_request_id
+ AND timelogs.id BETWEEN #{batch_start} AND #{batch_stop}
+ AND timelogs.project_id IS NULL;
+ SQL
+ end
+
+ def execute(sql)
+ @connection ||= ::ActiveRecord::Base.connection
+ @connection.execute(sql)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/update_vulnerabilities_from_dismissal_feedback.rb b/lib/gitlab/background_migration/update_vulnerabilities_from_dismissal_feedback.rb
index bfe9f673b53..1cc03f061fb 100644
--- a/lib/gitlab/background_migration/update_vulnerabilities_from_dismissal_feedback.rb
+++ b/lib/gitlab/background_migration/update_vulnerabilities_from_dismissal_feedback.rb
@@ -10,4 +10,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::UpdateVulnerabilitiesFromDismissalFeedback.prepend_if_ee('EE::Gitlab::BackgroundMigration::UpdateVulnerabilitiesFromDismissalFeedback')
+Gitlab::BackgroundMigration::UpdateVulnerabilitiesFromDismissalFeedback.prepend_mod_with('Gitlab::BackgroundMigration::UpdateVulnerabilitiesFromDismissalFeedback')
diff --git a/lib/gitlab/background_migration/update_vulnerabilities_to_dismissed.rb b/lib/gitlab/background_migration/update_vulnerabilities_to_dismissed.rb
index a2940cba6fa..60adb6b7e3e 100644
--- a/lib/gitlab/background_migration/update_vulnerabilities_to_dismissed.rb
+++ b/lib/gitlab/background_migration/update_vulnerabilities_to_dismissed.rb
@@ -10,4 +10,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::UpdateVulnerabilitiesToDismissed.prepend_if_ee('EE::Gitlab::BackgroundMigration::UpdateVulnerabilitiesToDismissed')
+Gitlab::BackgroundMigration::UpdateVulnerabilitiesToDismissed.prepend_mod_with('Gitlab::BackgroundMigration::UpdateVulnerabilitiesToDismissed')
diff --git a/lib/gitlab/background_migration/update_vulnerability_confidence.rb b/lib/gitlab/background_migration/update_vulnerability_confidence.rb
index 6ffaa836f3c..40d29978dd4 100644
--- a/lib/gitlab/background_migration/update_vulnerability_confidence.rb
+++ b/lib/gitlab/background_migration/update_vulnerability_confidence.rb
@@ -10,4 +10,4 @@ module Gitlab
end
end
-Gitlab::BackgroundMigration::UpdateVulnerabilityConfidence.prepend_if_ee('EE::Gitlab::BackgroundMigration::UpdateVulnerabilityConfidence')
+Gitlab::BackgroundMigration::UpdateVulnerabilityConfidence.prepend_mod_with('Gitlab::BackgroundMigration::UpdateVulnerabilityConfidence')
diff --git a/lib/gitlab/background_migration/user_mentions/models/namespace.rb b/lib/gitlab/background_migration/user_mentions/models/namespace.rb
index a2b50c41f4a..d76d06606ee 100644
--- a/lib/gitlab/background_migration/user_mentions/models/namespace.rb
+++ b/lib/gitlab/background_migration/user_mentions/models/namespace.rb
@@ -38,4 +38,4 @@ module Gitlab
end
end
-Namespace.prepend_if_ee('::EE::Namespace')
+Namespace.prepend_mod_with('Namespace')
diff --git a/lib/gitlab/bare_repository_import/importer.rb b/lib/gitlab/bare_repository_import/importer.rb
index ab7a08ffef9..44106897df8 100644
--- a/lib/gitlab/bare_repository_import/importer.rb
+++ b/lib/gitlab/bare_repository_import/importer.rb
@@ -13,7 +13,7 @@ module Gitlab
repos_to_import = Dir.glob(import_path + '**/*.git')
unless user = User.admins.order_id_asc.first
- raise NoAdminError.new('No admin user found to import repositories')
+ raise NoAdminError, 'No admin user found to import repositories'
end
repos_to_import.each do |repo_path|
@@ -92,7 +92,7 @@ module Gitlab
end
true
- rescue => e
+ rescue StandardError => e
log " * Failed to move repo: #{e.message}".color(:red)
false
diff --git a/lib/gitlab/blob_helper.rb b/lib/gitlab/blob_helper.rb
index 57d632afd74..c5b183d113d 100644
--- a/lib/gitlab/blob_helper.rb
+++ b/lib/gitlab/blob_helper.rb
@@ -38,7 +38,7 @@ module Gitlab
# If Charlock says its binary
else
- detect_encoding[:type] == :binary
+ find_encoding[:type] == :binary
end
end
@@ -137,23 +137,25 @@ module Gitlab
end
def ruby_encoding
- if hash = detect_encoding
+ if hash = find_encoding
hash[:ruby_encoding]
end
end
def encoding
- if hash = detect_encoding
+ if hash = find_encoding
hash[:encoding]
end
end
- def detect_encoding
- @detect_encoding ||= CharlockHolmes::EncodingDetector.new.detect(data) if data # rubocop:disable Gitlab/ModuleWithInstanceVariables
- end
-
def empty?
data.nil? || data == ""
end
+
+ private
+
+ def find_encoding
+ @find_encoding ||= Gitlab::EncodingHelper.detect_encoding(data) if data # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ end
end
end
diff --git a/lib/gitlab/cache.rb b/lib/gitlab/cache.rb
new file mode 100644
index 00000000000..90a0c38ff7b
--- /dev/null
+++ b/lib/gitlab/cache.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Cache
+ class << self
+ # Utility method for performing a fetch but only
+ # once per request, storing the returned value in
+ # the request store, if active.
+ def fetch_once(key, **kwargs)
+ Gitlab::SafeRequestStore.fetch(key) do
+ Rails.cache.fetch(key, **kwargs) do
+ yield
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/changelog/committer.rb b/lib/gitlab/changelog/committer.rb
index 31661650eff..52c355478c5 100644
--- a/lib/gitlab/changelog/committer.rb
+++ b/lib/gitlab/changelog/committer.rb
@@ -55,7 +55,7 @@ module Gitlab
result = service.execute
- raise Error.new(result[:message]) if result[:status] != :success
+ raise Error, result[:message] if result[:status] != :success
end
end
diff --git a/lib/gitlab/changelog/parser.rb b/lib/gitlab/changelog/parser.rb
index a4c8da283cd..fac6fc19148 100644
--- a/lib/gitlab/changelog/parser.rb
+++ b/lib/gitlab/changelog/parser.rb
@@ -169,7 +169,7 @@ module Gitlab
# We raise a custom error so it's easier to catch different changelog
# related errors. In addition, this ensures the caller of this method
# doesn't depend on a Parslet specific error class.
- raise Error.new("Failed to parse the template: #{ex.message}")
+ raise Error, "Failed to parse the template: #{ex.message}"
end
end
end
diff --git a/lib/gitlab/chat/responder.rb b/lib/gitlab/chat/responder.rb
index 6267fbc20e2..53a625e9d43 100644
--- a/lib/gitlab/chat/responder.rb
+++ b/lib/gitlab/chat/responder.rb
@@ -11,9 +11,9 @@ module Gitlab
#
# build - A `Ci::Build` that executed a chat command.
def self.responder_for(build)
- service = build.pipeline.chat_data&.chat_name&.service
+ integration = build.pipeline.chat_data&.chat_name&.integration
- if (responder = service.try(:chat_responder))
+ if (responder = integration.try(:chat_responder))
responder.new(build)
end
end
diff --git a/lib/gitlab/checks/base_checker.rb b/lib/gitlab/checks/base_checker.rb
index 0045d8a4113..68873610408 100644
--- a/lib/gitlab/checks/base_checker.rb
+++ b/lib/gitlab/checks/base_checker.rb
@@ -57,4 +57,4 @@ module Gitlab
end
end
-Gitlab::Checks::BaseChecker.prepend_if_ee('EE::Gitlab::Checks::BaseChecker')
+Gitlab::Checks::BaseChecker.prepend_mod_with('Gitlab::Checks::BaseChecker')
diff --git a/lib/gitlab/checks/change_access.rb b/lib/gitlab/checks/change_access.rb
index 67c777f67a7..a2c3de3e775 100644
--- a/lib/gitlab/checks/change_access.rb
+++ b/lib/gitlab/checks/change_access.rb
@@ -54,4 +54,4 @@ module Gitlab
end
end
-Gitlab::Checks::ChangeAccess.prepend_if_ee('EE::Gitlab::Checks::ChangeAccess')
+Gitlab::Checks::ChangeAccess.prepend_mod_with('Gitlab::Checks::ChangeAccess')
diff --git a/lib/gitlab/checks/diff_check.rb b/lib/gitlab/checks/diff_check.rb
index b146fea66b9..a05181ab58e 100644
--- a/lib/gitlab/checks/diff_check.rb
+++ b/lib/gitlab/checks/diff_check.rb
@@ -76,4 +76,4 @@ module Gitlab
end
end
-Gitlab::Checks::DiffCheck.prepend_if_ee('EE::Gitlab::Checks::DiffCheck')
+Gitlab::Checks::DiffCheck.prepend_mod_with('Gitlab::Checks::DiffCheck')
diff --git a/lib/gitlab/checks/matching_merge_request.rb b/lib/gitlab/checks/matching_merge_request.rb
index db7af0088d0..2635ad04770 100644
--- a/lib/gitlab/checks/matching_merge_request.rb
+++ b/lib/gitlab/checks/matching_merge_request.rb
@@ -21,4 +21,4 @@ module Gitlab
end
end
-Gitlab::Checks::MatchingMergeRequest.prepend_if_ee('EE::Gitlab::Checks::MatchingMergeRequest')
+Gitlab::Checks::MatchingMergeRequest.prepend_mod_with('Gitlab::Checks::MatchingMergeRequest')
diff --git a/lib/gitlab/ci/ansi2html.rb b/lib/gitlab/ci/ansi2html.rb
index 1fac00337a3..97988d8aa13 100644
--- a/lib/gitlab/ci/ansi2html.rb
+++ b/lib/gitlab/ci/ansi2html.rb
@@ -30,6 +30,8 @@ module Gitlab
Converter.new.convert(ansi, state)
end
+ Result = Struct.new(:html, :state, :append, :truncated, :offset, :size, :total, keyword_init: true) # rubocop:disable Lint/StructNewOverride
+
class Converter
def on_0(_)
reset
@@ -278,9 +280,7 @@ module Gitlab
close_open_tags
- # TODO: replace OpenStruct with a better type
- # https://gitlab.com/gitlab-org/gitlab/issues/34305
- OpenStruct.new(
+ Ansi2html::Result.new(
html: @out.force_encoding(Encoding.default_external),
state: state,
append: append,
diff --git a/lib/gitlab/ci/build/cache.rb b/lib/gitlab/ci/build/cache.rb
index 4fcb5168847..375e6b4a96f 100644
--- a/lib/gitlab/ci/build/cache.rb
+++ b/lib/gitlab/ci/build/cache.rb
@@ -7,39 +7,22 @@ module Gitlab
include ::Gitlab::Utils::StrongMemoize
def initialize(cache, pipeline)
- if multiple_cache_per_job?
- cache = Array.wrap(cache)
- @cache = cache.map do |cache|
- Gitlab::Ci::Pipeline::Seed::Build::Cache
- .new(pipeline, cache)
- end
- else
- @cache = Gitlab::Ci::Pipeline::Seed::Build::Cache
- .new(pipeline, cache)
+ cache = Array.wrap(cache)
+ @cache = cache.map do |cache|
+ Gitlab::Ci::Pipeline::Seed::Build::Cache
+ .new(pipeline, cache)
end
end
def cache_attributes
strong_memoize(:cache_attributes) do
- if multiple_cache_per_job?
- if @cache.empty?
- {}
- else
- { options: { cache: @cache.map(&:attributes) } }
- end
+ if @cache.empty?
+ {}
else
- @cache.build_attributes
+ { options: { cache: @cache.map(&:attributes) } }
end
end
end
-
- private
-
- def multiple_cache_per_job?
- strong_memoize(:multiple_cache_per_job) do
- ::Gitlab::Ci::Features.multiple_cache_per_job?
- end
- end
end
end
end
diff --git a/lib/gitlab/ci/build/releaser.rb b/lib/gitlab/ci/build/releaser.rb
index facb5f619bd..9720bb1123a 100644
--- a/lib/gitlab/ci/build/releaser.rb
+++ b/lib/gitlab/ci/build/releaser.rb
@@ -18,8 +18,9 @@ module Gitlab
command = BASE_COMMAND.dup
single_flags.each { |k, v| command.concat(" --#{k.to_s.dasherize} \"#{v}\"") }
array_commands.each { |k, v| v.each { |elem| command.concat(" --#{k.to_s.singularize.dasherize} \"#{elem}\"") } }
+ asset_links.each { |link| command.concat(" --assets-link #{stringified_json(link)}") }
- [command]
+ [command.freeze]
end
private
@@ -31,6 +32,14 @@ module Gitlab
def array_commands
config.slice(*ARRAY_FLAGS)
end
+
+ def asset_links
+ config.dig(:assets, :links) || []
+ end
+
+ def stringified_json(object)
+ "#{object.to_json.to_json}"
+ end
end
end
end
diff --git a/lib/gitlab/ci/config.rb b/lib/gitlab/ci/config.rb
index 23b0c93a3ee..9c6428d701c 100644
--- a/lib/gitlab/ci/config.rb
+++ b/lib/gitlab/ci/config.rb
@@ -17,13 +17,14 @@ module Gitlab
Config::Yaml::Tags::TagError
].freeze
- attr_reader :root, :context, :ref
+ attr_reader :root, :context, :ref, :source
- def initialize(config, project: nil, sha: nil, user: nil, parent_pipeline: nil, ref: nil)
+ def initialize(config, project: nil, sha: nil, user: nil, parent_pipeline: nil, ref: nil, source: nil)
@context = build_context(project: project, sha: sha, user: user, parent_pipeline: parent_pipeline)
@context.set_deadline(TIMEOUT_SECONDS)
@ref = ref
+ @source = source
@config = expand_config(config)
@@ -128,4 +129,4 @@ module Gitlab
end
end
-Gitlab::Ci::Config.prepend_if_ee('EE::Gitlab::Ci::ConfigEE')
+Gitlab::Ci::Config.prepend_mod_with('Gitlab::Ci::ConfigEE')
diff --git a/lib/gitlab/ci/config/entry/cache.rb b/lib/gitlab/ci/config/entry/cache.rb
index f9688c500d2..ab79add688b 100644
--- a/lib/gitlab/ci/config/entry/cache.rb
+++ b/lib/gitlab/ci/config/entry/cache.rb
@@ -4,88 +4,52 @@ module Gitlab
module Ci
class Config
module Entry
- ##
- # Entry that represents a cache configuration
- #
- class Cache < ::Gitlab::Config::Entry::Simplifiable
- strategy :Caches, if: -> (config) { Feature.enabled?(:multiple_cache_per_job, default_enabled: :yaml) }
- strategy :Cache, if: -> (config) { Feature.disabled?(:multiple_cache_per_job, default_enabled: :yaml) }
-
- class Caches < ::Gitlab::Config::Entry::ComposableArray
- include ::Gitlab::Config::Entry::Validatable
-
- MULTIPLE_CACHE_LIMIT = 4
-
- validations do
- validate do
- unless config.is_a?(Hash) || config.is_a?(Array)
- errors.add(:config, 'can only be a Hash or an Array')
- end
-
- if config.is_a?(Array) && config.count > MULTIPLE_CACHE_LIMIT
- errors.add(:config, "no more than #{MULTIPLE_CACHE_LIMIT} caches can be created")
- end
- end
- end
-
- def initialize(*args)
- super
-
- @key = nil
- end
-
- def composable_class
- Entry::Cache::Cache
+ class Cache < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Configurable
+ include ::Gitlab::Config::Entry::Validatable
+ include ::Gitlab::Config::Entry::Attributable
+
+ ALLOWED_KEYS = %i[key untracked paths when policy].freeze
+ ALLOWED_POLICY = %w[pull-push push pull].freeze
+ DEFAULT_POLICY = 'pull-push'
+ ALLOWED_WHEN = %w[on_success on_failure always].freeze
+ DEFAULT_WHEN = 'on_success'
+
+ validations do
+ validates :config, type: Hash, allowed_keys: ALLOWED_KEYS
+ validates :policy,
+ inclusion: { in: ALLOWED_POLICY, message: 'should be pull-push, push, or pull' },
+ allow_blank: true
+
+ with_options allow_nil: true do
+ validates :when,
+ inclusion: {
+ in: ALLOWED_WHEN,
+ message: 'should be on_success, on_failure or always'
+ }
end
end
- class Cache < ::Gitlab::Config::Entry::Node
- include ::Gitlab::Config::Entry::Configurable
- include ::Gitlab::Config::Entry::Validatable
- include ::Gitlab::Config::Entry::Attributable
-
- ALLOWED_KEYS = %i[key untracked paths when policy].freeze
- ALLOWED_POLICY = %w[pull-push push pull].freeze
- DEFAULT_POLICY = 'pull-push'
- ALLOWED_WHEN = %w[on_success on_failure always].freeze
- DEFAULT_WHEN = 'on_success'
+ entry :key, Entry::Key,
+ description: 'Cache key used to define a cache affinity.'
- validations do
- validates :config, type: Hash, allowed_keys: ALLOWED_KEYS
- validates :policy,
- inclusion: { in: ALLOWED_POLICY, message: 'should be pull-push, push, or pull' },
- allow_blank: true
-
- with_options allow_nil: true do
- validates :when,
- inclusion: {
- in: ALLOWED_WHEN,
- message: 'should be on_success, on_failure or always'
- }
- end
- end
+ entry :untracked, ::Gitlab::Config::Entry::Boolean,
+ description: 'Cache all untracked files.'
- entry :key, Entry::Key,
- description: 'Cache key used to define a cache affinity.'
+ entry :paths, Entry::Paths,
+ description: 'Specify which paths should be cached across builds.'
- entry :untracked, ::Gitlab::Config::Entry::Boolean,
- description: 'Cache all untracked files.'
+ attributes :policy, :when
- entry :paths, Entry::Paths,
- description: 'Specify which paths should be cached across builds.'
+ def value
+ result = super
- attributes :policy, :when
+ result[:key] = key_value
+ result[:policy] = policy || DEFAULT_POLICY
+ # Use self.when to avoid conflict with reserved word
+ result[:when] = self.when || DEFAULT_WHEN
- def value
- result = super
-
- result[:key] = key_value
- result[:policy] = policy || DEFAULT_POLICY
- # Use self.when to avoid conflict with reserved word
- result[:when] = self.when || DEFAULT_WHEN
-
- result
- end
+ result
end
class UnknownStrategy < ::Gitlab::Config::Entry::Node
diff --git a/lib/gitlab/ci/config/entry/caches.rb b/lib/gitlab/ci/config/entry/caches.rb
new file mode 100644
index 00000000000..75240599c9c
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/caches.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module Entry
+ ##
+ # Entry that represents caches configuration
+ #
+ class Caches < ::Gitlab::Config::Entry::ComposableArray
+ include ::Gitlab::Config::Entry::Validatable
+
+ MULTIPLE_CACHE_LIMIT = 4
+
+ validations do
+ validate do
+ unless config.is_a?(Hash) || config.is_a?(Array)
+ errors.add(:config, 'can only be a Hash or an Array')
+ end
+
+ if config.is_a?(Array) && config.count > MULTIPLE_CACHE_LIMIT
+ errors.add(:config, "no more than #{MULTIPLE_CACHE_LIMIT} caches can be created")
+ end
+ end
+ end
+
+ def initialize(*args)
+ super
+
+ @key = nil
+ end
+
+ def composable_class
+ Entry::Cache
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/entry/default.rb b/lib/gitlab/ci/config/entry/default.rb
index ab493ff7d78..eaaf9f69102 100644
--- a/lib/gitlab/ci/config/entry/default.rb
+++ b/lib/gitlab/ci/config/entry/default.rb
@@ -37,7 +37,7 @@ module Gitlab
description: 'Script that will be executed after each job.',
inherit: true
- entry :cache, Entry::Cache,
+ entry :cache, Entry::Caches,
description: 'Configure caching between build jobs.',
inherit: true
diff --git a/lib/gitlab/ci/config/entry/job.rb b/lib/gitlab/ci/config/entry/job.rb
index a20b802be58..c8e8f0bc1fc 100644
--- a/lib/gitlab/ci/config/entry/job.rb
+++ b/lib/gitlab/ci/config/entry/job.rb
@@ -64,7 +64,7 @@ module Gitlab
description: 'Commands that will be executed when finishing job.',
inherit: true
- entry :cache, Entry::Cache,
+ entry :cache, Entry::Caches,
description: 'Cache definition for this job.',
inherit: true
@@ -200,4 +200,4 @@ module Gitlab
end
end
-::Gitlab::Ci::Config::Entry::Job.prepend_if_ee('::EE::Gitlab::Ci::Config::Entry::Job')
+::Gitlab::Ci::Config::Entry::Job.prepend_mod_with('Gitlab::Ci::Config::Entry::Job')
diff --git a/lib/gitlab/ci/config/entry/need.rb b/lib/gitlab/ci/config/entry/need.rb
index b3cf0f9e0fd..29dc48c7b42 100644
--- a/lib/gitlab/ci/config/entry/need.rb
+++ b/lib/gitlab/ci/config/entry/need.rb
@@ -118,4 +118,4 @@ module Gitlab
end
end
-::Gitlab::Ci::Config::Entry::Need.prepend_if_ee('::EE::Gitlab::Ci::Config::Entry::Need')
+::Gitlab::Ci::Config::Entry::Need.prepend_mod_with('Gitlab::Ci::Config::Entry::Need')
diff --git a/lib/gitlab/ci/config/entry/needs.rb b/lib/gitlab/ci/config/entry/needs.rb
index dd01cfeedff..11b202ddde9 100644
--- a/lib/gitlab/ci/config/entry/needs.rb
+++ b/lib/gitlab/ci/config/entry/needs.rb
@@ -56,4 +56,4 @@ module Gitlab
end
end
-::Gitlab::Ci::Config::Entry::Needs.prepend_if_ee('::EE::Gitlab::Ci::Config::Entry::Needs')
+::Gitlab::Ci::Config::Entry::Needs.prepend_mod_with('Gitlab::Ci::Config::Entry::Needs')
diff --git a/lib/gitlab/ci/config/entry/root.rb b/lib/gitlab/ci/config/entry/root.rb
index 54ef84b965a..e6290ef2479 100644
--- a/lib/gitlab/ci/config/entry/root.rb
+++ b/lib/gitlab/ci/config/entry/root.rb
@@ -61,7 +61,7 @@ module Gitlab
description: 'Deprecated: stages for this pipeline.',
reserved: true
- entry :cache, Entry::Cache,
+ entry :cache, Entry::Caches,
description: 'Configure caching between build jobs.',
reserved: true
diff --git a/lib/gitlab/ci/features.rb b/lib/gitlab/ci/features.rb
index 12e182b38fc..c8e4d9ed763 100644
--- a/lib/gitlab/ci/features.rb
+++ b/lib/gitlab/ci/features.rb
@@ -18,9 +18,8 @@ module Gitlab
Feature.enabled?(:ci_pipeline_status_omit_commit_sha_in_cache_key, project, default_enabled: true)
end
- # Remove in https://gitlab.com/gitlab-org/gitlab/-/issues/224199
- def self.store_pipeline_messages?(project)
- ::Feature.enabled?(:ci_store_pipeline_messages, project, default_enabled: true)
+ def self.merge_base_pipeline_for_metrics_comparison?(project)
+ Feature.enabled?(:merge_base_pipeline_for_metrics_comparison, project, default_enabled: :yaml)
end
def self.raise_job_rules_without_workflow_rules_warning?
@@ -47,22 +46,17 @@ module Gitlab
::Feature.enabled?(:ci_trace_log_invalid_chunks, project, type: :ops, default_enabled: false)
end
- def self.validate_build_dependencies?(project)
- ::Feature.enabled?(:ci_validate_build_dependencies, project, default_enabled: :yaml) &&
- ::Feature.disabled?(:ci_validate_build_dependencies_override, project)
- end
-
def self.display_quality_on_mr_diff?(project)
- ::Feature.enabled?(:codequality_mr_diff, project, default_enabled: false)
- end
-
- def self.multiple_cache_per_job?
- ::Feature.enabled?(:multiple_cache_per_job, default_enabled: :yaml)
+ ::Feature.enabled?(:codequality_mr_diff, project, default_enabled: :yaml)
end
def self.gldropdown_tags_enabled?
::Feature.enabled?(:gldropdown_tags, default_enabled: :yaml)
end
+
+ def self.background_pipeline_retry_endpoint?(project)
+ ::Feature.enabled?(:background_pipeline_retry_endpoint, project)
+ end
end
end
end
diff --git a/lib/gitlab/ci/jwt.rb b/lib/gitlab/ci/jwt.rb
index a6ae249fa58..0b94debb24e 100644
--- a/lib/gitlab/ci/jwt.rb
+++ b/lib/gitlab/ci/jwt.rb
@@ -123,4 +123,4 @@ module Gitlab
end
end
-Gitlab::Ci::Jwt.prepend_if_ee('::EE::Gitlab::Ci::Jwt')
+Gitlab::Ci::Jwt.prepend_mod_with('Gitlab::Ci::Jwt')
diff --git a/lib/gitlab/ci/parsers.rb b/lib/gitlab/ci/parsers.rb
index 2baa8faf849..3469537a2e2 100644
--- a/lib/gitlab/ci/parsers.rb
+++ b/lib/gitlab/ci/parsers.rb
@@ -15,8 +15,8 @@ module Gitlab
}
end
- def self.fabricate!(file_type, *args)
- parsers.fetch(file_type.to_sym).new(*args)
+ def self.fabricate!(file_type, *args, **kwargs)
+ parsers.fetch(file_type.to_sym).new(*args, **kwargs)
rescue KeyError
raise ParserNotFoundError, "Cannot find any parser matching file type '#{file_type}'"
end
@@ -28,4 +28,4 @@ module Gitlab
end
end
-Gitlab::Ci::Parsers.prepend_if_ee('::EE::Gitlab::Ci::Parsers')
+Gitlab::Ci::Parsers.prepend_mod_with('Gitlab::Ci::Parsers')
diff --git a/lib/gitlab/ci/parsers/coverage/cobertura.rb b/lib/gitlab/ci/parsers/coverage/cobertura.rb
index eb3adf713d4..d6b3af674a6 100644
--- a/lib/gitlab/ci/parsers/coverage/cobertura.rb
+++ b/lib/gitlab/ci/parsers/coverage/cobertura.rb
@@ -121,7 +121,7 @@ module Gitlab
# Using `Integer()` here to raise exception on invalid values
[Integer(line["number"]), Integer(line["hits"])]
end
- rescue
+ rescue StandardError
raise InvalidLineInformationError, "Line information had invalid values"
end
diff --git a/lib/gitlab/ci/parsers/terraform/tfplan.rb b/lib/gitlab/ci/parsers/terraform/tfplan.rb
index abfbe18e23f..f9afa58f915 100644
--- a/lib/gitlab/ci/parsers/terraform/tfplan.rb
+++ b/lib/gitlab/ci/parsers/terraform/tfplan.rb
@@ -19,7 +19,7 @@ module Gitlab
end
rescue JSON::ParserError
terraform_reports.add_plan(job_id, invalid_tfplan(:invalid_json_format, job_details))
- rescue
+ rescue StandardError
details = job_details || {}
plan_name = job_id || 'failed_tf_plan'
terraform_reports.add_plan(plan_name, invalid_tfplan(:unknown_error, details))
diff --git a/lib/gitlab/ci/parsers/test/junit.rb b/lib/gitlab/ci/parsers/test/junit.rb
index 50cd703da4a..ca7fbde6713 100644
--- a/lib/gitlab/ci/parsers/test/junit.rb
+++ b/lib/gitlab/ci/parsers/test/junit.rb
@@ -31,7 +31,7 @@ module Gitlab
def ensure_test_cases_limited!(total_parsed, limit)
return unless limit > 0 && total_parsed > limit
- raise JunitParserError.new("number of test cases exceeded the limit of #{limit}")
+ raise JunitParserError, "number of test cases exceeded the limit of #{limit}"
end
def all_cases(root, parent = nil, &blk)
diff --git a/lib/gitlab/ci/pipeline/chain/config/content.rb b/lib/gitlab/ci/pipeline/chain/config/content.rb
index a7680f6e593..3c150ca26bb 100644
--- a/lib/gitlab/ci/pipeline/chain/config/content.rb
+++ b/lib/gitlab/ci/pipeline/chain/config/content.rb
@@ -52,4 +52,4 @@ module Gitlab
end
end
-Gitlab::Ci::Pipeline::Chain::Config::Content.prepend_if_ee('EE::Gitlab::Ci::Pipeline::Chain::Config::Content')
+Gitlab::Ci::Pipeline::Chain::Config::Content.prepend_mod_with('Gitlab::Ci::Pipeline::Chain::Config::Content')
diff --git a/lib/gitlab/ci/pipeline/chain/config/process.rb b/lib/gitlab/ci/pipeline/chain/config/process.rb
index 8f1c49563f2..49ec1250a5f 100644
--- a/lib/gitlab/ci/pipeline/chain/config/process.rb
+++ b/lib/gitlab/ci/pipeline/chain/config/process.rb
@@ -16,6 +16,7 @@ module Gitlab
project: project,
ref: @pipeline.ref,
sha: @pipeline.sha,
+ source: @pipeline.source,
user: current_user,
parent_pipeline: parent_pipeline
}
@@ -31,7 +32,7 @@ module Gitlab
@pipeline.merged_yaml = result.merged_yaml
- rescue => ex
+ rescue StandardError => ex
Gitlab::ErrorTracking.track_exception(ex,
project_id: project.id,
sha: @pipeline.sha
diff --git a/lib/gitlab/ci/pipeline/chain/helpers.rb b/lib/gitlab/ci/pipeline/chain/helpers.rb
index 9988b6f18ed..09158bf8bfd 100644
--- a/lib/gitlab/ci/pipeline/chain/helpers.rb
+++ b/lib/gitlab/ci/pipeline/chain/helpers.rb
@@ -19,6 +19,8 @@ module Gitlab
# polluted with other unrelated errors (e.g. state machine)
# https://gitlab.com/gitlab-org/gitlab/-/issues/220823
pipeline.errors.add(:base, message)
+
+ pipeline.errors.full_messages
end
def warning(message)
diff --git a/lib/gitlab/ci/pipeline/chain/limit/activity.rb b/lib/gitlab/ci/pipeline/chain/limit/activity.rb
index 3c64278e305..ef9235477db 100644
--- a/lib/gitlab/ci/pipeline/chain/limit/activity.rb
+++ b/lib/gitlab/ci/pipeline/chain/limit/activity.rb
@@ -20,4 +20,4 @@ module Gitlab
end
end
-Gitlab::Ci::Pipeline::Chain::Limit::Activity.prepend_if_ee('EE::Gitlab::Ci::Pipeline::Chain::Limit::Activity')
+Gitlab::Ci::Pipeline::Chain::Limit::Activity.prepend_mod_with('Gitlab::Ci::Pipeline::Chain::Limit::Activity')
diff --git a/lib/gitlab/ci/pipeline/chain/limit/job_activity.rb b/lib/gitlab/ci/pipeline/chain/limit/job_activity.rb
index 2e8b437252f..3706dd0b9f6 100644
--- a/lib/gitlab/ci/pipeline/chain/limit/job_activity.rb
+++ b/lib/gitlab/ci/pipeline/chain/limit/job_activity.rb
@@ -20,4 +20,4 @@ module Gitlab
end
end
-Gitlab::Ci::Pipeline::Chain::Limit::JobActivity.prepend_if_ee('EE::Gitlab::Ci::Pipeline::Chain::Limit::JobActivity')
+Gitlab::Ci::Pipeline::Chain::Limit::JobActivity.prepend_mod_with('Gitlab::Ci::Pipeline::Chain::Limit::JobActivity')
diff --git a/lib/gitlab/ci/pipeline/chain/limit/size.rb b/lib/gitlab/ci/pipeline/chain/limit/size.rb
index 739648840e9..761bdb1c484 100644
--- a/lib/gitlab/ci/pipeline/chain/limit/size.rb
+++ b/lib/gitlab/ci/pipeline/chain/limit/size.rb
@@ -20,4 +20,4 @@ module Gitlab
end
end
-Gitlab::Ci::Pipeline::Chain::Limit::Size.prepend_if_ee('EE::Gitlab::Ci::Pipeline::Chain::Limit::Size')
+Gitlab::Ci::Pipeline::Chain::Limit::Size.prepend_mod_with('Gitlab::Ci::Pipeline::Chain::Limit::Size')
diff --git a/lib/gitlab/ci/pipeline/chain/skip.rb b/lib/gitlab/ci/pipeline/chain/skip.rb
index df92e229f12..e4e4f4f484a 100644
--- a/lib/gitlab/ci/pipeline/chain/skip.rb
+++ b/lib/gitlab/ci/pipeline/chain/skip.rb
@@ -11,7 +11,14 @@ module Gitlab
def perform!
if skipped?
- @pipeline.skip if @command.save_incompleted
+ if @command.save_incompleted
+ # Project iid must be called outside a transaction, so we ensure it is set here
+ # otherwise it may be set within the state transition transaction of the skip call
+ # which it will lock the InternalId row for the whole transaction
+ @pipeline.ensure_project_iid!
+
+ @pipeline.skip
+ end
end
end
diff --git a/lib/gitlab/ci/pipeline/chain/validate/abilities.rb b/lib/gitlab/ci/pipeline/chain/validate/abilities.rb
index 55c125e03d5..1c1f7abb6f6 100644
--- a/lib/gitlab/ci/pipeline/chain/validate/abilities.rb
+++ b/lib/gitlab/ci/pipeline/chain/validate/abilities.rb
@@ -57,4 +57,4 @@ module Gitlab
end
end
-Gitlab::Ci::Pipeline::Chain::Validate::Abilities.prepend_if_ee('EE::Gitlab::Ci::Pipeline::Chain::Validate::Abilities')
+Gitlab::Ci::Pipeline::Chain::Validate::Abilities.prepend_mod_with('Gitlab::Ci::Pipeline::Chain::Validate::Abilities')
diff --git a/lib/gitlab/ci/pipeline/chain/validate/external.rb b/lib/gitlab/ci/pipeline/chain/validate/external.rb
index 6149d2f04d7..539b44513f0 100644
--- a/lib/gitlab/ci/pipeline/chain/validate/external.rb
+++ b/lib/gitlab/ci/pipeline/chain/validate/external.rb
@@ -54,7 +54,7 @@ module Gitlab
else
raise InvalidResponseCode, "Unsupported response code received from Validation Service: #{response_code}"
end
- rescue => ex
+ rescue StandardError => ex
Gitlab::ErrorTracking.track_exception(ex, project_id: project.id)
true
@@ -147,4 +147,4 @@ module Gitlab
end
end
-Gitlab::Ci::Pipeline::Chain::Validate::External.prepend_if_ee('EE::Gitlab::Ci::Pipeline::Chain::Validate::External')
+Gitlab::Ci::Pipeline::Chain::Validate::External.prepend_mod_with('Gitlab::Ci::Pipeline::Chain::Validate::External')
diff --git a/lib/gitlab/ci/pipeline/chain/validate/security_orchestration_policy.rb b/lib/gitlab/ci/pipeline/chain/validate/security_orchestration_policy.rb
new file mode 100644
index 00000000000..e3588aa3027
--- /dev/null
+++ b/lib/gitlab/ci/pipeline/chain/validate/security_orchestration_policy.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Pipeline
+ module Chain
+ module Validate
+ class SecurityOrchestrationPolicy < Chain::Base
+ include Chain::Helpers
+
+ def perform!
+ # no-op
+ end
+
+ def break?
+ false
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+Gitlab::Ci::Pipeline::Chain::Validate::SecurityOrchestrationPolicy.prepend_mod_with('Gitlab::Ci::Pipeline::Chain::Validate::SecurityOrchestrationPolicy')
diff --git a/lib/gitlab/ci/pipeline/metrics.rb b/lib/gitlab/ci/pipeline/metrics.rb
index 6cb6fd3920d..84b88374a7f 100644
--- a/lib/gitlab/ci/pipeline/metrics.rb
+++ b/lib/gitlab/ci/pipeline/metrics.rb
@@ -13,6 +13,13 @@ module Gitlab
::Gitlab::Metrics.histogram(name, comment, labels, buckets)
end
+ def self.pipeline_security_orchestration_policy_processing_duration_histogram
+ name = :gitlab_ci_pipeline_security_orchestration_policy_processing_duration_seconds
+ comment = 'Pipeline security orchestration policy processing duration'
+
+ ::Gitlab::Metrics.histogram(name, comment)
+ end
+
def self.pipeline_size_histogram
name = :gitlab_ci_pipeline_size_builds
comment = 'Pipeline size'
@@ -56,6 +63,21 @@ module Gitlab
Gitlab::Metrics.counter(name, comment)
end
+
+ def ci_minutes_exceeded_builds_counter
+ name = :ci_minutes_exceeded_builds_counter
+ comment = 'Count of builds dropped due to CI minutes exceeded'
+
+ Gitlab::Metrics.counter(name, comment)
+ end
+
+ def self.gitlab_ci_difference_live_vs_actual_minutes
+ name = :gitlab_ci_difference_live_vs_actual_minutes
+ comment = 'Comparison between CI minutes consumption from live tracking vs actual consumption'
+ labels = {}
+ buckets = [-120.0, -60.0, -30.0, -10.0, -5.0, -3.0, -1.0, 0.0, 1.0, 3.0, 5.0, 10.0, 30.0, 60.0, 120.0]
+ ::Gitlab::Metrics.histogram(name, comment, labels, buckets)
+ end
end
end
end
diff --git a/lib/gitlab/ci/queue/metrics.rb b/lib/gitlab/ci/queue/metrics.rb
index 7ecb9a1db16..46e4373ec85 100644
--- a/lib/gitlab/ci/queue/metrics.rb
+++ b/lib/gitlab/ci/queue/metrics.rb
@@ -10,7 +10,7 @@ module Gitlab
QUEUE_ACTIVE_RUNNERS_BUCKETS = [1, 3, 10, 30, 60, 300, 900, 1800, 3600].freeze
QUEUE_DEPTH_TOTAL_BUCKETS = [1, 2, 3, 5, 8, 16, 32, 50, 100, 250, 500, 1000, 2000, 5000].freeze
QUEUE_SIZE_TOTAL_BUCKETS = [1, 5, 10, 50, 100, 500, 1000, 2000, 5000, 7500, 10000, 15000, 20000].freeze
- QUEUE_PROCESSING_DURATION_SECONDS_BUCKETS = [0.01, 0.05, 0.1, 0.3, 0.5, 1, 5, 10, 30, 60, 180, 300].freeze
+ QUEUE_PROCESSING_DURATION_SECONDS_BUCKETS = [0.01, 0.05, 0.1, 0.3, 0.5, 1, 5, 10, 15, 20, 30, 60].freeze
METRICS_SHARD_TAG_PREFIX = 'metrics_shard::'
DEFAULT_METRICS_SHARD = 'default'
diff --git a/lib/gitlab/ci/reports/codequality_mr_diff.rb b/lib/gitlab/ci/reports/codequality_mr_diff.rb
index e60a075e3f5..0595b6f966a 100644
--- a/lib/gitlab/ci/reports/codequality_mr_diff.rb
+++ b/lib/gitlab/ci/reports/codequality_mr_diff.rb
@@ -6,8 +6,8 @@ module Gitlab
class CodequalityMrDiff
attr_reader :files
- def initialize(raw_report)
- @raw_report = raw_report
+ def initialize(new_errors)
+ @new_errors = new_errors
@files = {}
build_report!
end
@@ -15,7 +15,7 @@ module Gitlab
private
def build_report!
- codequality_files = @raw_report.all_degradations.each_with_object({}) do |degradation, codequality_files|
+ codequality_files = @new_errors.each_with_object({}) do |degradation, codequality_files|
unless codequality_files[degradation.dig(:location, :path)].present?
codequality_files[degradation.dig(:location, :path)] = []
end
diff --git a/lib/gitlab/ci/reports/test_failure_history.rb b/lib/gitlab/ci/reports/test_failure_history.rb
index 37d0da38065..c110dbf98be 100644
--- a/lib/gitlab/ci/reports/test_failure_history.rb
+++ b/lib/gitlab/ci/reports/test_failure_history.rb
@@ -13,7 +13,7 @@ module Gitlab
def load!
recent_failures_count.each do |key_hash, count|
- failed_junit_tests[key_hash].set_recent_failures(count, project.default_branch_or_master)
+ failed_junit_tests[key_hash].set_recent_failures(count, project.default_branch_or_main)
end
end
diff --git a/lib/gitlab/ci/status/build/failed.rb b/lib/gitlab/ci/status/build/failed.rb
index 787dee3b267..cbd72f54ff4 100644
--- a/lib/gitlab/ci/status/build/failed.rb
+++ b/lib/gitlab/ci/status/build/failed.rb
@@ -20,6 +20,7 @@ module Gitlab
scheduler_failure: 'scheduler failure',
data_integrity_failure: 'data integrity failure',
forward_deployment_failure: 'forward deployment failure',
+ pipeline_loop_detected: 'job would create infinitely looping pipelines',
invalid_bridge_trigger: 'downstream pipeline trigger definition is invalid',
downstream_bridge_project_not_found: 'downstream project could not be found',
insufficient_bridge_permissions: 'no permissions to trigger downstream pipeline',
@@ -28,7 +29,8 @@ module Gitlab
secrets_provider_not_found: 'secrets provider can not be found',
reached_max_descendant_pipelines_depth: 'reached maximum depth of child pipelines',
project_deleted: 'pipeline project was deleted',
- user_blocked: 'pipeline user was blocked'
+ user_blocked: 'pipeline user was blocked',
+ ci_quota_exceeded: 'no more CI minutes available'
}.freeze
private_constant :REASONS
@@ -68,4 +70,4 @@ module Gitlab
end
end
-Gitlab::Ci::Status::Build::Failed.prepend_if_ee('::EE::Gitlab::Ci::Status::Build::Failed')
+Gitlab::Ci::Status::Build::Failed.prepend_mod_with('Gitlab::Ci::Status::Build::Failed')
diff --git a/lib/gitlab/ci/status/core.rb b/lib/gitlab/ci/status/core.rb
index 4779c8d3d53..e7ed2081f6a 100644
--- a/lib/gitlab/ci/status/core.rb
+++ b/lib/gitlab/ci/status/core.rb
@@ -11,6 +11,8 @@ module Gitlab
attr_reader :subject, :user
+ delegate :cache_key, to: :subject
+
def initialize(subject, user)
@subject = subject
@user = user
diff --git a/lib/gitlab/ci/syntax_templates/Artifacts example.gitlab-ci.yml b/lib/gitlab/ci/syntax_templates/Artifacts example.gitlab-ci.yml
deleted file mode 100644
index 7182b96594d..00000000000
--- a/lib/gitlab/ci/syntax_templates/Artifacts example.gitlab-ci.yml
+++ /dev/null
@@ -1,52 +0,0 @@
-#
-# You can use artifacts to pass data to jobs in later stages.
-# For more information, see https://docs.gitlab.com/ee/ci/pipelines/job_artifacts.html
-#
-
-stages:
- - build
- - test
- - deploy
-
-build-job:
- stage: build
- script:
- - echo "This job might build an important file, and pass it to later jobs."
- - echo "This is the content of the important file" > important-file.txt
- artifacts:
- paths:
- - important-file.txt
-
-test-job-with-artifacts:
- stage: test
- script:
- - echo "This job uses the artifact from the job in the earlier stage."
- - cat important-file.txt
- - echo "It creates another file, and adds it to the artifacts."
- - echo "This is a second important file" > important-file2.txt
- artifacts:
- paths:
- - important-file2.txt
-
-test-job-with-no-artifacts:
- stage: test
- dependencies: [] # Use to skip downloading any artifacts
- script:
- - echo "This job does not get the artifacts from other jobs."
- - cat important-file.txt || exit 0
-
-deploy-job-with-all-artifacts:
- stage: deploy
- script:
- - echo "By default, jobs download all available artifacts."
- - cat important-file.txt
- - cat important-file2.txt
-
-deploy-job-with-1-artifact:
- stage: deploy
- dependencies:
- - build-job # Download artifacts from only this job
- script:
- - echo "You can configure a job to download artifacts from only certain jobs."
- - cat important-file.txt
- - cat important-file2.txt || exit 0
diff --git a/lib/gitlab/ci/syntax_templates/Before_script and after_script example.gitlab-ci.yml b/lib/gitlab/ci/syntax_templates/Before_script and after_script example.gitlab-ci.yml
deleted file mode 100644
index 382bac09ed7..00000000000
--- a/lib/gitlab/ci/syntax_templates/Before_script and after_script example.gitlab-ci.yml
+++ /dev/null
@@ -1,36 +0,0 @@
-#
-# You can define common tasks and run them before or after the main scripts in jobs.
-# For more information, see:
-# - https://docs.gitlab.com/ee/ci/yaml/README.html#before_script
-# - https://docs.gitlab.com/ee/ci/yaml/README.html#after_script
-#
-
-stages:
- - test
-
-default:
- before_script:
- - echo "This script runs before the main script in every job, unless the job overrides it."
- - echo "It may set up common dependencies, for example."
- after_script:
- - echo "This script runs after the main script in every job, unless the job overrides it."
- - echo "It may do some common final clean up tasks"
-
-job-standard:
- stage: test
- script:
- - echo "This job uses both of the globally defined before and after scripts."
-
-job-override-before:
- stage: test
- before_script:
- - echo "Use a different before_script in this job."
- script:
- - echo "This job uses its own before_script, and the global after_script."
-
-job-override-after:
- stage: test
- after_script:
- - echo "Use a different after_script in this job."
- script:
- - echo "This job uses its own after_script, and the global before_script."
diff --git a/lib/gitlab/ci/syntax_templates/Manual jobs example.gitlab-ci.yml b/lib/gitlab/ci/syntax_templates/Manual jobs example.gitlab-ci.yml
deleted file mode 100644
index 5f27def74c9..00000000000
--- a/lib/gitlab/ci/syntax_templates/Manual jobs example.gitlab-ci.yml
+++ /dev/null
@@ -1,53 +0,0 @@
-#
-# A manual job is a type of job that is not executed automatically and must be explicitly started by a user.
-# To make a job manual, add when: manual to its configuration.
-# For more information, see https://docs.gitlab.com/ee/ci/yaml/README.html#whenmanual
-#
-
-stages:
- - build
- - test
- - deploy
-
-build-job:
- stage: build
- script:
- - echo "This job is not a manual job"
-
-manual-build:
- stage: build
- script:
- - echo "This manual job passes after you trigger it."
- when: manual
-
-manual-build-allowed-to-fail:
- stage: build
- script:
- - echo "This manual job fails after you trigger it."
- - echo "It is allowed to fail, so the pipeline does not fail.
- when: manual
- allow_failure: true # Default behavior
-
-test-job:
- stage: test
- script:
- - echo "This is a normal test job"
- - echo "It runs when the when the build stage completes."
- - echo "It does not need to wait for the manual jobs in the build stage to run."
-
-manual-test-not-allowed-to-fail:
- stage: test
- script:
- - echo "This manual job fails after you trigger it."
- - echo "It is NOT allowed to fail, so the pipeline is marked as failed
- - echo "when this job completes."
- - exit 1
- when: manual
- allow_failure: false # Optional behavior
-
-deploy-job:
- stage: deploy
- script:
- - echo "This is a normal deploy job"
- - echo "If a manual job that isn't allowed to fail ran in an earlier stage and failed,
- - echo "this job does not run".
diff --git a/lib/gitlab/ci/syntax_templates/Multi-stage pipeline example.gitlab-ci.yml b/lib/gitlab/ci/syntax_templates/Multi-stage pipeline example.gitlab-ci.yml
deleted file mode 100644
index aced628aacb..00000000000
--- a/lib/gitlab/ci/syntax_templates/Multi-stage pipeline example.gitlab-ci.yml
+++ /dev/null
@@ -1,33 +0,0 @@
-#
-# A pipeline is composed of independent jobs that run scripts, grouped into stages.
-# Stages run in sequential order, but jobs within stages run in parallel.
-# For more information, see: https://docs.gitlab.com/ee/ci/yaml/README.html#stages
-#
-
-stages:
- - build
- - test
- - deploy
-
-build-job:
- stage: build
- script:
- - echo "This job runs in the build stage, which runs first."
-
-test-job1:
- stage: test
- script:
- - echo "This job runs in the test stage."
- - echo "It only starts when the job in the build stage completes successfully."
-
-test-job2:
- stage: test
- script:
- - echo "This job also runs in the test stage."
- - echo "This job can run at the same time as test-job2."
-
-deploy-job:
- stage: deploy
- script:
- - echo "This job runs in the deploy stage."
- - echo "It only runs when both jobs in the test stage complete successfully"
diff --git a/lib/gitlab/ci/syntax_templates/Variables example.gitlab-ci.yml b/lib/gitlab/ci/syntax_templates/Variables example.gitlab-ci.yml
deleted file mode 100644
index 2b8cf7bab44..00000000000
--- a/lib/gitlab/ci/syntax_templates/Variables example.gitlab-ci.yml
+++ /dev/null
@@ -1,47 +0,0 @@
-#
-# Variables can be used to for more dynamic behavior in jobs and scripts.
-# For more information, see https://docs.gitlab.com/ee/ci/variables/README.html
-#
-
-stages:
- - test
-
-variables:
- VAR1: "Variable 1 defined globally"
-
-use-a-variable:
- stage: test
- script:
- - echo "You can use variables in jobs."
- - echo "The content of 'VAR1' is = $VAR1"
-
-override-a-variable:
- stage: test
- variables:
- VAR1: "Variable 1 was overriden in in the job."
- script:
- - echo "You can override global variables in jobs."
- - echo "The content of 'VAR1' is = $VAR1"
-
-define-a-new-variable:
- stage: test
- variables:
- VAR2: "Variable 2 is new and defined in the job only."
- script:
- - echo "You can mix global variables with variables defined in jobs."
- - echo "The content of 'VAR1' is = $VAR1"
- - echo "The content of 'VAR2' is = $VAR2"
-
-incorrect-variable-usage:
- stage: test
- script:
- - echo "You can't use variables only defined in other jobs."
- - echo "The content of 'VAR2' is = $VAR2"
-
-predefined-variables:
- stage: test
- script:
- - echo "Some variables are predefined by GitLab CI/CD, for example:"
- - echo "The commit author's username is $GITLAB_USER_LOGIN"
- - echo "The commit branch is $CI_COMMIT_BRANCH"
- - echo "The project path is $CI_PROJECT_PATH"
diff --git a/lib/gitlab/ci/templates/Getting-started.yml b/lib/gitlab/ci/templates/Getting-started.yml
new file mode 100644
index 00000000000..4dc88418671
--- /dev/null
+++ b/lib/gitlab/ci/templates/Getting-started.yml
@@ -0,0 +1,39 @@
+# This is a sample GitLab CI/CD configuration file that should run without any modifications.
+# It demonstrates a basic 3 stage CI/CD pipeline. Instead of real tests or scripts,
+# it uses echo commands to simulate the pipeline execution.
+#
+# A pipeline is composed of independent jobs that run scripts, grouped into stages.
+# Stages run in sequential order, but jobs within stages run in parallel.
+#
+# For more information, see: https://docs.gitlab.com/ee/ci/yaml/README.html#stages
+
+stages: # List of stages for jobs, and their order of execution
+ - build
+ - test
+ - deploy
+
+build-job: # This job runs in the build stage, which runs first.
+ stage: build
+ script:
+ - echo "Compiling the code..."
+ - echo "Compile complete.
+
+unit-test-job: # This job runs in the test stage.
+ stage: test # It only starts when the job in the build stage completes successfully.
+ script:
+ - echo "Running unit tests... This will take about 60 seconds."
+ - sleep 60
+ - echo "Code coverage is 90%"
+
+lint-test-job: # This job also runs in the test stage.
+ stage: test # It can run at the same time as unit-test-job (in parallel).
+ script:
+ - echo "Linting code... This will take about 10 seconds."
+ - sleep 10
+ - echo "No lint issues found."
+
+deploy-job: # This job runs in the deploy stage.
+ stage: deploy # It only runs when *both* jobs in the test stage complete successfully.
+ script:
+ - echo "Deploying application..."
+ - echo "Application successfully deployed."
diff --git a/lib/gitlab/ci/templates/Indeni.Cloudrail.gitlab-ci-.yml b/lib/gitlab/ci/templates/Indeni.Cloudrail.gitlab-ci.yml
index c7fb1321055..7f33d048c1e 100644
--- a/lib/gitlab/ci/templates/Indeni.Cloudrail.gitlab-ci-.yml
+++ b/lib/gitlab/ci/templates/Indeni.Cloudrail.gitlab-ci.yml
@@ -29,12 +29,8 @@ default:
before_script:
- cd ${CI_PROJECT_DIR}/my_folder_with_terraform_content
-stages:
- - init_and_plan
- - cloudrail
-
init_and_plan:
- stage: init_and_plan
+ stage: build
image: registry.gitlab.com/gitlab-org/terraform-images/releases/0.13
rules:
- if: $SAST_DISABLED
@@ -52,7 +48,7 @@ init_and_plan:
- ./**/.terraform
cloudrail_scan:
- stage: cloudrail
+ stage: test
image: indeni/cloudrail-cli:1.2.44
rules:
- if: $SAST_DISABLED
diff --git a/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.latest.gitlab-ci.yml
new file mode 100644
index 00000000000..5216a46745c
--- /dev/null
+++ b/lib/gitlab/ci/templates/Jobs/Browser-Performance-Testing.latest.gitlab-ci.yml
@@ -0,0 +1,77 @@
+# Read more about the feature here: https://docs.gitlab.com/ee/user/project/merge_requests/browser_performance_testing.html
+
+browser_performance:
+ stage: performance
+ image: docker:19.03.12
+ allow_failure: true
+ variables:
+ DOCKER_TLS_CERTDIR: ""
+ SITESPEED_IMAGE: sitespeedio/sitespeed.io
+ SITESPEED_VERSION: 14.1.0
+ SITESPEED_OPTIONS: ''
+ services:
+ - docker:19.03.12-dind
+ script:
+ - |
+ if ! docker info &>/dev/null; then
+ if [ -z "$DOCKER_HOST" -a "$KUBERNETES_PORT" ]; then
+ export DOCKER_HOST='tcp://localhost:2375'
+ fi
+ fi
+ - export CI_ENVIRONMENT_URL=$(cat environment_url.txt)
+ - mkdir gitlab-exporter
+ # Busybox wget does not support proxied HTTPS, get the real thing.
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/287611.
+ - (env | grep -i _proxy= 2>&1 >/dev/null) && apk --no-cache add wget
+ - wget -O gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/1.1.0/index.js
+ - mkdir sitespeed-results
+ - |
+ function propagate_env_vars() {
+ CURRENT_ENV=$(printenv)
+
+ for VAR_NAME; do
+ echo $CURRENT_ENV | grep "${VAR_NAME}=" > /dev/null && echo "--env $VAR_NAME "
+ done
+ }
+ - |
+ if [ -f .gitlab-urls.txt ]
+ then
+ sed -i -e 's@^@'"$CI_ENVIRONMENT_URL"'@' .gitlab-urls.txt
+ docker run \
+ $(propagate_env_vars \
+ auto_proxy \
+ https_proxy \
+ http_proxy \
+ no_proxy \
+ AUTO_PROXY \
+ HTTPS_PROXY \
+ HTTP_PROXY \
+ NO_PROXY \
+ ) \
+ --shm-size=1g --rm -v "$(pwd)":/sitespeed.io $SITESPEED_IMAGE:$SITESPEED_VERSION --plugins.add ./gitlab-exporter --cpu --outputFolder sitespeed-results .gitlab-urls.txt $SITESPEED_OPTIONS
+ else
+ docker run \
+ $(propagate_env_vars \
+ auto_proxy \
+ https_proxy \
+ http_proxy \
+ no_proxy \
+ AUTO_PROXY \
+ HTTPS_PROXY \
+ HTTP_PROXY \
+ NO_PROXY \
+ ) \
+ --shm-size=1g --rm -v "$(pwd)":/sitespeed.io $SITESPEED_IMAGE:$SITESPEED_VERSION --plugins.add ./gitlab-exporter --cpu --outputFolder sitespeed-results "$CI_ENVIRONMENT_URL" $SITESPEED_OPTIONS
+ fi
+ - mv sitespeed-results/data/performance.json browser-performance.json
+ artifacts:
+ paths:
+ - sitespeed-results/
+ reports:
+ browser_performance: browser-performance.json
+ rules:
+ - if: '$CI_KUBERNETES_ACTIVE == null || $CI_KUBERNETES_ACTIVE == ""'
+ when: never
+ - if: '$PERFORMANCE_DISABLED'
+ when: never
+ - if: '$CI_COMMIT_TAG || $CI_COMMIT_BRANCH'
diff --git a/lib/gitlab/ci/templates/Jobs/Build.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Build.gitlab-ci.yml
index 1c25d9d583b..abcb347b146 100644
--- a/lib/gitlab/ci/templates/Jobs/Build.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Build.gitlab-ci.yml
@@ -1,10 +1,11 @@
build:
stage: build
- image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-build-image:v0.4.0"
+ image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-build-image:v0.6.0"
variables:
DOCKER_TLS_CERTDIR: ""
services:
- - docker:19.03.12-dind
+ - name: "docker:20.10.6-dind"
+ command: ['--tls=false', '--host=tcp://0.0.0.0:2375']
script:
- |
if [[ -z "$CI_COMMIT_TAG" ]]; then
@@ -16,6 +17,8 @@ build:
fi
- /build/build.sh
rules:
+ - if: '$BUILD_DISABLED'
+ when: never
- if: '$AUTO_DEVOPS_PLATFORM_TARGET == "EC2"'
when: never
- if: '$CI_COMMIT_TAG || $CI_COMMIT_BRANCH'
@@ -26,4 +29,6 @@ build_artifact:
- printf "To build your project, please create a build_artifact job into your .gitlab-ci.yml file.\nMore information at https://docs.gitlab.com/ee/ci/cloud_deployment\n"
- exit 1
rules:
+ - if: '$BUILD_DISABLED'
+ when: never
- if: '$AUTO_DEVOPS_PLATFORM_TARGET == "EC2"'
diff --git a/lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml b/lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml
index 3faf07546de..45bddb1bc6a 100644
--- a/lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml
@@ -46,27 +46,23 @@ review:
name: review/$CI_COMMIT_REF_NAME
url: http://$CI_PROJECT_NAME-$CI_ENVIRONMENT_SLUG.$OPENSHIFT_DOMAIN
on_stop: stop-review
- only:
- - branches
- except:
- - master
+ rules:
+ - if: $CI_COMMIT_BRANCH && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
stop-review:
<<: *deploy
stage: cleanup
script:
- oc delete all -l "app=$APP"
- when: manual
variables:
APP: review-$CI_COMMIT_REF_NAME
GIT_STRATEGY: none
environment:
name: review/$CI_COMMIT_REF_NAME
action: stop
- only:
- - branches
- except:
- - master
+ rules:
+ - if: $CI_COMMIT_BRANCH && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
+ when: manual
staging:
<<: *deploy
@@ -77,8 +73,8 @@ staging:
environment:
name: staging
url: http://$CI_PROJECT_NAME-staging.$OPENSHIFT_DOMAIN
- only:
- - master
+ rules:
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
production:
<<: *deploy
@@ -86,9 +82,9 @@ production:
variables:
APP: production
APP_HOST: $CI_PROJECT_NAME.$OPENSHIFT_DOMAIN
- when: manual
environment:
name: production
url: http://$CI_PROJECT_NAME.$OPENSHIFT_DOMAIN
- only:
- - master
+ rules:
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+ when: manual
diff --git a/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml
index bf42cd52605..90fad1550ff 100644
--- a/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml
@@ -11,6 +11,7 @@ stages:
- fuzz
variables:
+ SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
FUZZAPI_PROFILE: Quick
FUZZAPI_VERSION: "1.6"
FUZZAPI_CONFIG: .gitlab-api-fuzzing.yml
@@ -24,7 +25,7 @@ variables:
# available (non 500 response to HTTP(s))
FUZZAPI_SERVICE_START_TIMEOUT: "300"
#
- FUZZAPI_IMAGE: registry.gitlab.com/gitlab-org/security-products/analyzers/api-fuzzing:${FUZZAPI_VERSION}-engine
+ FUZZAPI_IMAGE: ${SECURE_ANALYZERS_PREFIX}/api-fuzzing:${FUZZAPI_VERSION}
#
apifuzzer_fuzz_unlicensed:
diff --git a/lib/gitlab/ci/templates/Security/API-Fuzzing.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/API-Fuzzing.latest.gitlab-ci.yml
index 215029dc952..8fa33026011 100644
--- a/lib/gitlab/ci/templates/Security/API-Fuzzing.latest.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/API-Fuzzing.latest.gitlab-ci.yml
@@ -5,266 +5,30 @@
# How to set: https://docs.gitlab.com/ee/ci/yaml/#variables
variables:
- FUZZAPI_PROFILE: Quick
- FUZZAPI_VERSION: latest
- FUZZAPI_CONFIG: .gitlab-api-fuzzing.yml
- FUZZAPI_TIMEOUT: 30
- FUZZAPI_REPORT: gl-api-fuzzing-report.json
- FUZZAPI_REPORT_ASSET_PATH: assets
- #
- FUZZAPI_D_NETWORK: testing-net
- #
- # Wait up to 5 minutes for API Fuzzer and target url to become
- # available (non 500 response to HTTP(s))
- FUZZAPI_SERVICE_START_TIMEOUT: "300"
- #
- FUZZAPI_IMAGE: registry.gitlab.com/gitlab-org/security-products/analyzers/api-fuzzing:${FUZZAPI_VERSION}-engine
- #
-
-apifuzzer_fuzz_unlicensed:
- stage: fuzz
- allow_failure: true
- rules:
- - if: '$GITLAB_FEATURES !~ /\bapi_fuzzing\b/ && $API_FUZZING_DISABLED == null'
- - when: never
- script:
- - |
- echo "Error: Your GitLab project is not licensed for API Fuzzing."
- - exit 1
+ FUZZAPI_VERSION: "1"
+ SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
+ FUZZAPI_IMAGE: ${SECURE_ANALYZERS_PREFIX}/api-fuzzing:${FUZZAPI_VERSION}
apifuzzer_fuzz:
stage: fuzz
- image:
- name: $FUZZAPI_IMAGE
- entrypoint: ["/bin/bash", "-l", "-c"]
- variables:
- FUZZAPI_PROJECT: $CI_PROJECT_PATH
- FUZZAPI_API: http://localhost:80
- FUZZAPI_NEW_REPORT: 1
- FUZZAPI_LOG_SCANNER: gl-apifuzzing-api-scanner.log
- TZ: America/Los_Angeles
- allow_failure: true
- rules:
- - if: $FUZZAPI_D_TARGET_IMAGE
- when: never
- - if: $FUZZAPI_D_WORKER_IMAGE
- when: never
- - if: $API_FUZZING_DISABLED
- when: never
- - if: $API_FUZZING_DISABLED_FOR_DEFAULT_BRANCH &&
- $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
- when: never
- - if: $CI_COMMIT_BRANCH && $GITLAB_FEATURES =~ /\bapi_fuzzing\b/
- script:
- #
- # Validate options
- - |
- if [ "$FUZZAPI_HAR$FUZZAPI_OPENAPI$FUZZAPI_POSTMAN_COLLECTION" == "" ]; then \
- echo "Error: One of FUZZAPI_HAR, FUZZAPI_OPENAPI, or FUZZAPI_POSTMAN_COLLECTION must be provided."; \
- echo "See https://docs.gitlab.com/ee/user/application_security/api_fuzzing/ for information on how to configure API Fuzzing."; \
- exit 1; \
- fi
- #
- # Run user provided pre-script
- - sh -c "$FUZZAPI_PRE_SCRIPT"
- #
- # Make sure asset path exists
- - mkdir -p $FUZZAPI_REPORT_ASSET_PATH
- #
- # Start API Security background process
- - dotnet /peach/Peach.Web.dll &> $FUZZAPI_LOG_SCANNER &
- - APISEC_PID=$!
- #
- # Start scanning
- - worker-entry
- #
- # Run user provided post-script
- - sh -c "$FUZZAPI_POST_SCRIPT"
- #
- # Shutdown API Security
- - kill $APISEC_PID
- - wait $APISEC_PID
- #
- artifacts:
- when: always
- paths:
- - $FUZZAPI_REPORT_ASSET_PATH
- - $FUZZAPI_REPORT
- - $FUZZAPI_LOG_SCANNER
- reports:
- api_fuzzing: $FUZZAPI_REPORT
-
-apifuzzer_fuzz_dnd:
- stage: fuzz
- image: docker:19.03.12
- variables:
- DOCKER_DRIVER: overlay2
- DOCKER_TLS_CERTDIR: ""
- FUZZAPI_PROJECT: $CI_PROJECT_PATH
- FUZZAPI_API: http://apifuzzer:80
+ image: $FUZZAPI_IMAGE
allow_failure: true
rules:
- - if: $FUZZAPI_D_TARGET_IMAGE == null && $FUZZAPI_D_WORKER_IMAGE == null
- when: never
- if: $API_FUZZING_DISABLED
when: never
- if: $API_FUZZING_DISABLED_FOR_DEFAULT_BRANCH &&
$CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
when: never
- - if: $CI_COMMIT_BRANCH && $GITLAB_FEATURES =~ /\bapi_fuzzing\b/
- services:
- - docker:19.03.12-dind
+ - if: $CI_COMMIT_BRANCH
script:
- #
- #
- - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- #
- - docker network create --driver bridge $FUZZAPI_D_NETWORK
- #
- # Run user provided pre-script
- - sh -c "$FUZZAPI_PRE_SCRIPT"
- #
- # Make sure asset path exists
- - mkdir -p $FUZZAPI_REPORT_ASSET_PATH
- #
- # Start peach testing engine container
- - |
- docker run -d \
- --name apifuzzer \
- --network $FUZZAPI_D_NETWORK \
- -e Proxy:Port=8000 \
- -e TZ=America/Los_Angeles \
- -e GITLAB_FEATURES \
- -p 80:80 \
- -p 8000:8000 \
- -p 514:514 \
- --restart=no \
- $FUZZAPI_IMAGE \
- dotnet /peach/Peach.Web.dll
- #
- # Start target container
- - |
- if [ "$FUZZAPI_D_TARGET_IMAGE" != "" ]; then \
- docker run -d \
- --name target \
- --network $FUZZAPI_D_NETWORK \
- $FUZZAPI_D_TARGET_ENV \
- $FUZZAPI_D_TARGET_PORTS \
- $FUZZAPI_D_TARGET_VOLUME \
- --restart=no \
- $FUZZAPI_D_TARGET_IMAGE \
- ; fi
- #
- # Start worker container if provided
- - |
- if [ "$FUZZAPI_D_WORKER_IMAGE" != "" ]; then \
- echo "Starting worker image $FUZZAPI_D_WORKER_IMAGE"; \
- docker run \
- --name worker \
- --network $FUZZAPI_D_NETWORK \
- -e FUZZAPI_API=http://apifuzzer:80 \
- -e FUZZAPI_PROJECT \
- -e FUZZAPI_PROFILE \
- -e FUZZAPI_CONFIG \
- -e FUZZAPI_REPORT \
- -e FUZZAPI_REPORT_ASSET_PATH \
- -e FUZZAPI_NEW_REPORT=1 \
- -e FUZZAPI_HAR \
- -e FUZZAPI_OPENAPI \
- -e FUZZAPI_POSTMAN_COLLECTION \
- -e FUZZAPI_POSTMAN_COLLECTION_VARIABLES \
- -e FUZZAPI_TARGET_URL \
- -e FUZZAPI_OVERRIDES_FILE \
- -e FUZZAPI_OVERRIDES_ENV \
- -e FUZZAPI_OVERRIDES_CMD \
- -e FUZZAPI_OVERRIDES_INTERVAL \
- -e FUZZAPI_TIMEOUT \
- -e FUZZAPI_VERBOSE \
- -e FUZZAPI_SERVICE_START_TIMEOUT \
- -e FUZZAPI_HTTP_USERNAME \
- -e FUZZAPI_HTTP_PASSWORD \
- -e CI_PROJECT_URL \
- -e CI_JOB_ID \
- -e CI_COMMIT_BRANCH=${CI_COMMIT_BRANCH} \
- $FUZZAPI_D_WORKER_ENV \
- $FUZZAPI_D_WORKER_PORTS \
- $FUZZAPI_D_WORKER_VOLUME \
- --restart=no \
- $FUZZAPI_D_WORKER_IMAGE \
- ; fi
- #
- # Start API Fuzzing provided worker if no other worker present
- - |
- if [ "$FUZZAPI_D_WORKER_IMAGE" == "" ]; then \
- if [ "$FUZZAPI_HAR$FUZZAPI_OPENAPI$FUZZAPI_POSTMAN_COLLECTION" == "" ]; then \
- echo "Error: One of FUZZAPI_HAR, FUZZAPI_OPENAPI, or FUZZAPI_POSTMAN_COLLECTION must be provided."; \
- echo "See https://docs.gitlab.com/ee/user/application_security/api_fuzzing/ for information on how to configure API Fuzzing."; \
- exit 1; \
- fi; \
- docker run \
- --name worker \
- --network $FUZZAPI_D_NETWORK \
- -e TZ=America/Los_Angeles \
- -e FUZZAPI_API=http://apifuzzer:80 \
- -e FUZZAPI_PROJECT \
- -e FUZZAPI_PROFILE \
- -e FUZZAPI_CONFIG \
- -e FUZZAPI_REPORT \
- -e FUZZAPI_REPORT_ASSET_PATH \
- -e FUZZAPI_NEW_REPORT=1 \
- -e FUZZAPI_HAR \
- -e FUZZAPI_OPENAPI \
- -e FUZZAPI_POSTMAN_COLLECTION \
- -e FUZZAPI_POSTMAN_COLLECTION_VARIABLES \
- -e FUZZAPI_TARGET_URL \
- -e FUZZAPI_OVERRIDES_FILE \
- -e FUZZAPI_OVERRIDES_ENV \
- -e FUZZAPI_OVERRIDES_CMD \
- -e FUZZAPI_OVERRIDES_INTERVAL \
- -e FUZZAPI_TIMEOUT \
- -e FUZZAPI_VERBOSE \
- -e FUZZAPI_SERVICE_START_TIMEOUT \
- -e FUZZAPI_HTTP_USERNAME \
- -e FUZZAPI_HTTP_PASSWORD \
- -e CI_PROJECT_URL \
- -e CI_JOB_ID \
- -v $CI_PROJECT_DIR:/app \
- -v `pwd`/$FUZZAPI_REPORT_ASSET_PATH:/app/$FUZZAPI_REPORT_ASSET_PATH:rw \
- -p 81:80 \
- -p 8001:8000 \
- -p 515:514 \
- --restart=no \
- $FUZZAPI_IMAGE \
- worker-entry \
- ; fi
- #
- # Propagate exit code from api fuzzing scanner (if any)
- - if [[ $(docker inspect apifuzzer --format='{{.State.ExitCode}}') != "0" ]]; then echo "API Fuzzing scanner exited with an error. Logs are available as job artifacts."; exit 1; fi
- #
- # Run user provided post-script
- - sh -c "$FUZZAPI_POST_SCRIPT"
- #
- after_script:
- #
- # Shutdown all containers
- - echo "Stopping all containers"
- - if [ "$FUZZAPI_D_TARGET_IMAGE" != "" ]; then docker stop target; fi
- - docker stop worker
- - docker stop apifuzzer
- #
- # Save docker logs
- - docker logs apifuzzer &> gl-api_fuzzing-logs.log
- - if [ "$FUZZAPI_D_TARGET_IMAGE" != "" ]; then docker logs target &> gl-api_fuzzing-target-logs.log; fi
- - docker logs worker &> gl-api_fuzzing-worker-logs.log
- #
+ - /peach/analyzer-fuzz-api
artifacts:
when: always
paths:
- - ./gl-api_fuzzing*.log
- - ./gl-api_fuzzing*.zip
- - $FUZZAPI_REPORT_ASSET_PATH
- - $FUZZAPI_REPORT
+ - gl-assets
+ - gl-api-fuzzing-report.json
+ - gl-*.log
reports:
- api_fuzzing: $FUZZAPI_REPORT
+ api_fuzzing: gl-api-fuzzing-report.json
# end
diff --git a/lib/gitlab/ci/templates/Security/DAST-API.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/DAST-API.gitlab-ci.yml
new file mode 100644
index 00000000000..b40c4e982f7
--- /dev/null
+++ b/lib/gitlab/ci/templates/Security/DAST-API.gitlab-ci.yml
@@ -0,0 +1,48 @@
+# To use this template, add the following to your .gitlab-ci.yml file:
+#
+# include:
+# template: DAST-API.gitlab-ci.yml
+#
+# You also need to add a `dast` stage to your `stages:` configuration. A sample configuration for DAST API:
+#
+# stages:
+# - build
+# - test
+# - deploy
+# - dast
+
+# Read more about this feature here: https://docs.gitlab.com/ee/user/application_security/dast_api/index.html
+
+# Configure the scanning tool with CI/CD variables (https://docs.gitlab.com/ee/ci/variables/README.html)
+# List of variables available to configure the DAST API scanning tool:
+# https://docs.gitlab.com/ee/user/application_security/dast_api/index.html#available-cicd-variables
+
+variables:
+ # Setting this variable affects all Security templates
+ # (SAST, Dependency Scanning, ...)
+ SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
+ #
+ DAST_API_VERSION: "1"
+ DAST_API_IMAGE: $SECURE_ANALYZERS_PREFIX/api-fuzzing:$DAST_API_VERSION
+
+dast_api:
+ stage: dast
+ image: $DAST_API_IMAGE
+ allow_failure: true
+ rules:
+ - if: $DAST_API_DISABLED
+ when: never
+ - if: $DAST_API_DISABLED_FOR_DEFAULT_BRANCH &&
+ $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
+ when: never
+ - if: $CI_COMMIT_BRANCH
+ script:
+ - /peach/analyzer-dast-api
+ artifacts:
+ when: always
+ paths:
+ - gl-assets
+ - gl-dast-api-report.json
+ - gl-*.log
+ reports:
+ dast: gl-dast-api-report.json
diff --git a/lib/gitlab/ci/templates/Security/DAST.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/DAST.latest.gitlab-ci.yml
index 533f8bb25f8..b6282da18a4 100644
--- a/lib/gitlab/ci/templates/Security/DAST.latest.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/DAST.latest.gitlab-ci.yml
@@ -22,19 +22,6 @@ variables:
# Setting this variable will affect all Security templates
# (SAST, Dependency Scanning, ...)
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
- #
- DAST_API_PROFILE: Full
- DAST_API_VERSION: latest
- DAST_API_CONFIG: .gitlab-dast-api.yml
- DAST_API_TIMEOUT: 30
- DAST_API_REPORT: gl-dast-api-report.json
- DAST_API_REPORT_ASSET_PATH: assets
- #
- # Wait up to 5 minutes for API Security and target url to become
- # available (non 500 response to HTTP(s))
- DAST_API_SERVICE_START_TIMEOUT: "300"
- #
- DAST_API_IMAGE: registry.gitlab.com/gitlab-org/security-products/analyzers/api-fuzzing:${DAST_API_VERSION}-engine
dast:
stage: dast
@@ -51,11 +38,6 @@ dast:
reports:
dast: gl-dast-report.json
rules:
- - if: $DAST_API_BETA && ( $DAST_API_SPECIFICATION ||
- $DAST_API_OPENAPI ||
- $DAST_API_POSTMAN_COLLECTION ||
- $DAST_API_HAR )
- when: never
- if: $DAST_DISABLED
when: never
- if: $DAST_DISABLED_FOR_DEFAULT_BRANCH &&
@@ -71,72 +53,4 @@ dast:
- if: $CI_COMMIT_BRANCH &&
$DAST_WEBSITE
- if: $CI_COMMIT_BRANCH &&
- $DAST_API_BETA == null &&
$DAST_API_SPECIFICATION
-
-dast_api:
- stage: dast
- image:
- name: $DAST_API_IMAGE
- entrypoint: ["/bin/bash", "-l", "-c"]
- variables:
- API_SECURITY_MODE: DAST
- DAST_API_NEW_REPORT: 1
- DAST_API_PROJECT: $CI_PROJECT_PATH
- DAST_API_API: http://127.0.0.1:5000
- DAST_API_LOG_SCANNER: gl-dast-api-scanner.log
- TZ: America/Los_Angeles
- allow_failure: true
- rules:
- - if: $DAST_API_BETA == null
- when: never
- - if: $DAST_DISABLED
- when: never
- - if: $DAST_DISABLED_FOR_DEFAULT_BRANCH &&
- $CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
- when: never
- - if: $CI_DEFAULT_BRANCH != $CI_COMMIT_REF_NAME &&
- $REVIEW_DISABLED &&
- $DAST_API_SPECIFICATION == null &&
- $DAST_API_OPENAPI == null &&
- $DAST_API_POSTMAN_COLLECTION == null &&
- $DAST_API_HAR == null
- when: never
- - if: $DAST_API_SPECIFICATION == null &&
- $DAST_API_OPENAPI == null &&
- $DAST_API_POSTMAN_COLLECTION == null &&
- $DAST_API_HAR == null
- when: never
- - if: $CI_COMMIT_BRANCH &&
- $GITLAB_FEATURES =~ /\bdast\b/
- script:
- #
- # Run user provided pre-script
- - sh -c "$DAST_API_PRE_SCRIPT"
- #
- # Make sure asset path exists
- - mkdir -p $DAST_API_REPORT_ASSET_PATH
- #
- # Start API Security background process
- - dotnet /peach/Peach.Web.dll &> $DAST_API_LOG_SCANNER &
- - APISEC_PID=$!
- #
- # Start scanning
- - worker-entry
- #
- # Run user provided post-script
- - sh -c "$DAST_API_POST_SCRIPT"
- #
- # Shutdown API Security
- - kill $APISEC_PID
- - wait $APISEC_PID
- #
- artifacts:
- when: always
- paths:
- - $DAST_API_REPORT_ASSET_PATH
- - $DAST_API_REPORT
- - $DAST_API_LOG_SCANNER
- - gl-*.log
- reports:
- dast: $DAST_API_REPORT
diff --git a/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml
index 3039d64514b..53d68c24d26 100644
--- a/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml
@@ -8,8 +8,8 @@ variables:
# Setting this variable will affect all Security templates
# (SAST, Dependency Scanning, ...)
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers"
-
DS_DEFAULT_ANALYZERS: "bundler-audit, retire.js, gemnasium, gemnasium-maven, gemnasium-python"
+ DS_EXCLUDED_ANALYZERS: ""
DS_EXCLUDED_PATHS: "spec, test, tests, tmp"
DS_MAJOR_VERSION: 2
@@ -45,6 +45,8 @@ gemnasium-dependency_scanning:
rules:
- if: $DEPENDENCY_SCANNING_DISABLED
when: never
+ - if: $DS_EXCLUDED_ANALYZERS =~ /gemnasium([^-]|$)/
+ when: never
- if: $CI_COMMIT_BRANCH &&
$GITLAB_FEATURES =~ /\bdependency_scanning\b/ &&
$DS_DEFAULT_ANALYZERS =~ /gemnasium([^-]|$)/
@@ -71,6 +73,8 @@ gemnasium-maven-dependency_scanning:
rules:
- if: $DEPENDENCY_SCANNING_DISABLED
when: never
+ - if: $DS_EXCLUDED_ANALYZERS =~ /gemnasium-maven/
+ when: never
- if: $CI_COMMIT_BRANCH &&
$GITLAB_FEATURES =~ /\bdependency_scanning\b/ &&
$DS_DEFAULT_ANALYZERS =~ /gemnasium-maven/
@@ -92,6 +96,8 @@ gemnasium-python-dependency_scanning:
rules:
- if: $DEPENDENCY_SCANNING_DISABLED
when: never
+ - if: $DS_EXCLUDED_ANALYZERS =~ /gemnasium-python/
+ when: never
- if: $CI_COMMIT_BRANCH &&
$GITLAB_FEATURES =~ /\bdependency_scanning\b/ &&
$DS_DEFAULT_ANALYZERS =~ /gemnasium-python/
@@ -120,6 +126,8 @@ bundler-audit-dependency_scanning:
rules:
- if: $DEPENDENCY_SCANNING_DISABLED
when: never
+ - if: $DS_EXCLUDED_ANALYZERS =~ /bundler-audit/
+ when: never
- if: $CI_COMMIT_BRANCH &&
$GITLAB_FEATURES =~ /\bdependency_scanning\b/ &&
$DS_DEFAULT_ANALYZERS =~ /bundler-audit/
@@ -138,6 +146,8 @@ retire-js-dependency_scanning:
rules:
- if: $DEPENDENCY_SCANNING_DISABLED
when: never
+ - if: $DS_EXCLUDED_ANALYZERS =~ /retire.js/
+ when: never
- if: $CI_COMMIT_BRANCH &&
$GITLAB_FEATURES =~ /\bdependency_scanning\b/ &&
$DS_DEFAULT_ANALYZERS =~ /retire.js/
diff --git a/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
index 3ebccfbba4a..a8d45e80356 100644
--- a/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
@@ -155,13 +155,8 @@ gosec-sast:
exists:
- '**/*.go'
-mobsf-android-sast:
+.mobsf-sast:
extends: .sast-analyzer
- services:
- # this version must match with analyzer version mentioned in: https://gitlab.com/gitlab-org/security-products/analyzers/mobsf/-/blob/master/Dockerfile
- # Unfortunately, we need to keep track of mobsf version in 2 different places for now.
- - name: opensecurity/mobile-security-framework-mobsf:v3.4.0
- alias: mobsf
image:
name: "$SAST_ANALYZER_IMAGE"
variables:
@@ -169,7 +164,9 @@ mobsf-android-sast:
# override the analyzer image with a custom value. This may be subject to change or
# breakage across GitLab releases.
SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/mobsf:$SAST_ANALYZER_IMAGE_TAG"
- MOBSF_API_KEY: key
+
+mobsf-android-sast:
+ extends: .mobsf-sast
rules:
- if: $SAST_DISABLED
when: never
@@ -179,23 +176,11 @@ mobsf-android-sast:
$SAST_DEFAULT_ANALYZERS =~ /mobsf/ &&
$SAST_EXPERIMENTAL_FEATURES == 'true'
exists:
+ - '**/*.apk'
- '**/AndroidManifest.xml'
mobsf-ios-sast:
- extends: .sast-analyzer
- services:
- # this version must match with analyzer version mentioned in: https://gitlab.com/gitlab-org/security-products/analyzers/mobsf/-/blob/master/Dockerfile
- # Unfortunately, we need to keep track of mobsf version in 2 different places for now.
- - name: opensecurity/mobile-security-framework-mobsf:v3.4.0
- alias: mobsf
- image:
- name: "$SAST_ANALYZER_IMAGE"
- variables:
- # SAST_ANALYZER_IMAGE is an undocumented variable used internally to allow QA to
- # override the analyzer image with a custom value. This may be subject to change or
- # breakage across GitLab releases.
- SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/mobsf:$SAST_ANALYZER_IMAGE_TAG"
- MOBSF_API_KEY: key
+ extends: .mobsf-sast
rules:
- if: $SAST_DISABLED
when: never
@@ -205,6 +190,7 @@ mobsf-ios-sast:
$SAST_DEFAULT_ANALYZERS =~ /mobsf/ &&
$SAST_EXPERIMENTAL_FEATURES == 'true'
exists:
+ - '**/*.ipa'
- '**/*.xcodeproj/*'
nodejs-scan-sast:
@@ -292,15 +278,14 @@ semgrep-sast:
# SAST_ANALYZER_IMAGE is an undocumented variable used internally to allow QA to
# override the analyzer image with a custom value. This may be subject to change or
# breakage across GitLab releases.
- SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/semgrep:latest"
+ SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/semgrep:$SAST_ANALYZER_IMAGE_TAG"
rules:
- if: $SAST_DISABLED
when: never
- if: $SAST_EXCLUDED_ANALYZERS =~ /semgrep/
when: never
- if: $CI_COMMIT_BRANCH &&
- $SAST_DEFAULT_ANALYZERS =~ /semgrep/ &&
- $SAST_EXPERIMENTAL_FEATURES == 'true'
+ $SAST_DEFAULT_ANALYZERS =~ /semgrep/
exists:
- '**/*.py'
- '**/*.js'
diff --git a/lib/gitlab/ci/templates/Security/Secure-Binaries.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Secure-Binaries.gitlab-ci.yml
index 232c320562b..ac975fbbeab 100644
--- a/lib/gitlab/ci/templates/Security/Secure-Binaries.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/Secure-Binaries.gitlab-ci.yml
@@ -13,11 +13,11 @@
variables:
SECURE_BINARIES_ANALYZERS: >-
- bandit, brakeman, gosec, spotbugs, flawfinder, phpcs-security-audit, security-code-scan, nodejs-scan, eslint, secrets, sobelow, pmd-apex, kubesec,
+ bandit, brakeman, gosec, spotbugs, flawfinder, phpcs-security-audit, security-code-scan, nodejs-scan, eslint, secrets, sobelow, pmd-apex, kubesec, semgrep,
bundler-audit, retire.js, gemnasium, gemnasium-maven, gemnasium-python,
klar, clair-vulnerabilities-db,
license-finder,
- dast
+ dast, api-fuzzing
SECURE_BINARIES_DOWNLOAD_IMAGES: "true"
SECURE_BINARIES_PUSH_IMAGES: "true"
@@ -134,6 +134,13 @@ secrets:
variables:
SECURE_BINARIES_ANALYZER_VERSION: "3"
+semgrep:
+ extends: .download_images
+ only:
+ variables:
+ - $SECURE_BINARIES_DOWNLOAD_IMAGES == "true" &&
+ $SECURE_BINARIES_ANALYZERS =~ /\bsemgrep\b/
+
sobelow:
extends: .download_images
only:
@@ -241,3 +248,12 @@ dast:
variables:
- $SECURE_BINARIES_DOWNLOAD_IMAGES == "true" &&
$SECURE_BINARIES_ANALYZERS =~ /\bdast\b/
+
+api-fuzzing:
+ extends: .download_images
+ variables:
+ SECURE_BINARIES_ANALYZER_VERSION: "1"
+ only:
+ variables:
+ - $SECURE_BINARIES_DOWNLOAD_IMAGES == "true" &&
+ $SECURE_BINARIES_ANALYZERS =~ /\bapi-fuzzing\b/
diff --git a/lib/gitlab/ci/templates/Terraform.gitlab-ci.yml b/lib/gitlab/ci/templates/Terraform.gitlab-ci.yml
index 7e2828d010f..6b9db1c2e0f 100644
--- a/lib/gitlab/ci/templates/Terraform.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Terraform.gitlab-ci.yml
@@ -56,6 +56,6 @@ apply:
- terraform apply -input=false $PLAN
dependencies:
- plan
- when: manual
- only:
- - master
+ rules:
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+ when: manual
diff --git a/lib/gitlab/ci/templates/Verify/Browser-Performance.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Verify/Browser-Performance.latest.gitlab-ci.yml
new file mode 100644
index 00000000000..f0621165f8a
--- /dev/null
+++ b/lib/gitlab/ci/templates/Verify/Browser-Performance.latest.gitlab-ci.yml
@@ -0,0 +1,52 @@
+# Read more about the feature here: https://docs.gitlab.com/ee/user/project/merge_requests/browser_performance_testing.html
+
+stages:
+ - build
+ - test
+ - deploy
+ - performance
+
+browser_performance:
+ stage: performance
+ image: docker:git
+ variables:
+ URL: ''
+ SITESPEED_IMAGE: sitespeedio/sitespeed.io
+ SITESPEED_VERSION: 14.1.0
+ SITESPEED_OPTIONS: ''
+ services:
+ - docker:stable-dind
+ script:
+ - mkdir gitlab-exporter
+ # Busybox wget does not support proxied HTTPS, get the real thing.
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/287611.
+ - (env | grep -i _proxy= 2>&1 >/dev/null) && apk --no-cache add wget
+ - wget -O ./gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/1.1.0/index.js
+ - mkdir sitespeed-results
+ - |
+ function propagate_env_vars() {
+ CURRENT_ENV=$(printenv)
+
+ for VAR_NAME; do
+ echo $CURRENT_ENV | grep "${VAR_NAME}=" > /dev/null && echo "--env $VAR_NAME "
+ done
+ }
+ - |
+ docker run \
+ $(propagate_env_vars \
+ auto_proxy \
+ https_proxy \
+ http_proxy \
+ no_proxy \
+ AUTO_PROXY \
+ HTTPS_PROXY \
+ HTTP_PROXY \
+ NO_PROXY \
+ ) \
+ --shm-size=1g --rm -v "$(pwd)":/sitespeed.io $SITESPEED_IMAGE:$SITESPEED_VERSION --plugins.add ./gitlab-exporter --cpu --outputFolder sitespeed-results $URL $SITESPEED_OPTIONS
+ - mv sitespeed-results/data/performance.json browser-performance.json
+ artifacts:
+ paths:
+ - sitespeed-results/
+ reports:
+ browser_performance: browser-performance.json
diff --git a/lib/gitlab/ci/trace.rb b/lib/gitlab/ci/trace.rb
index c25c4339c35..c4757edf74e 100644
--- a/lib/gitlab/ci/trace.rb
+++ b/lib/gitlab/ci/trace.rb
@@ -317,4 +317,4 @@ module Gitlab
end
end
-::Gitlab::Ci::Trace.prepend_if_ee('EE::Gitlab::Ci::Trace')
+::Gitlab::Ci::Trace.prepend_mod_with('Gitlab::Ci::Trace')
diff --git a/lib/gitlab/ci/trace/stream.rb b/lib/gitlab/ci/trace/stream.rb
index 618438c8887..fdc598c025a 100644
--- a/lib/gitlab/ci/trace/stream.rb
+++ b/lib/gitlab/ci/trace/stream.rb
@@ -93,7 +93,7 @@ module Gitlab
end
nil
- rescue
+ rescue StandardError
# if bad regex or something goes wrong we dont want to interrupt transition
# so we just silently ignore error for now
end
diff --git a/lib/gitlab/ci/yaml_processor.rb b/lib/gitlab/ci/yaml_processor.rb
index dc4951f76bb..a8c1002f2b9 100644
--- a/lib/gitlab/ci/yaml_processor.rb
+++ b/lib/gitlab/ci/yaml_processor.rb
@@ -141,7 +141,7 @@ module Gitlab
end
def error!(message)
- raise ValidationError.new(message)
+ raise ValidationError, message
end
end
end
diff --git a/lib/gitlab/class_attributes.rb b/lib/gitlab/class_attributes.rb
index 6560c97b2e6..6eea7590cbd 100644
--- a/lib/gitlab/class_attributes.rb
+++ b/lib/gitlab/class_attributes.rb
@@ -14,6 +14,18 @@ module Gitlab
class_attributes[name] || superclass_attributes(name)
end
+ def set_class_attribute(name, value)
+ class_attributes[name] = value
+
+ after_hooks.each(&:call)
+
+ value
+ end
+
+ def after_set_class_attribute(&block)
+ after_hooks << block
+ end
+
private
def class_attributes
@@ -25,6 +37,10 @@ module Gitlab
superclass.get_class_attribute(name)
end
+
+ def after_hooks
+ @after_hooks ||= []
+ end
end
end
end
diff --git a/lib/gitlab/cleanup/orphan_job_artifact_files.rb b/lib/gitlab/cleanup/orphan_job_artifact_files.rb
index 48a1ab23fc2..05dfdcd4486 100644
--- a/lib/gitlab/cleanup/orphan_job_artifact_files.rb
+++ b/lib/gitlab/cleanup/orphan_job_artifact_files.rb
@@ -139,4 +139,4 @@ module Gitlab
end
end
-Gitlab::Cleanup::OrphanJobArtifactFiles.prepend_if_ee('EE::Gitlab::Cleanup::OrphanJobArtifactFiles')
+Gitlab::Cleanup::OrphanJobArtifactFiles.prepend_mod_with('Gitlab::Cleanup::OrphanJobArtifactFiles')
diff --git a/lib/gitlab/cleanup/orphan_job_artifact_files_batch.rb b/lib/gitlab/cleanup/orphan_job_artifact_files_batch.rb
index 4b1d16eb974..e222f2834ee 100644
--- a/lib/gitlab/cleanup/orphan_job_artifact_files_batch.rb
+++ b/lib/gitlab/cleanup/orphan_job_artifact_files_batch.rb
@@ -79,4 +79,4 @@ module Gitlab
end
end
-Gitlab::Cleanup::OrphanJobArtifactFilesBatch.prepend_if_ee('EE::Gitlab::Cleanup::OrphanJobArtifactFilesBatch')
+Gitlab::Cleanup::OrphanJobArtifactFilesBatch.prepend_mod_with('Gitlab::Cleanup::OrphanJobArtifactFilesBatch')
diff --git a/lib/gitlab/cleanup/project_uploads.rb b/lib/gitlab/cleanup/project_uploads.rb
index 77231665e7e..ed4b363416c 100644
--- a/lib/gitlab/cleanup/project_uploads.rb
+++ b/lib/gitlab/cleanup/project_uploads.rb
@@ -44,7 +44,7 @@ module Gitlab
return unless upload && upload.local? && upload.model
upload.absolute_path
- rescue => e
+ rescue StandardError => e
logger.error e.message
# absolute_path depends on a lot of code. If it doesn't work, then it
@@ -72,7 +72,7 @@ module Gitlab
FileUtils.mv(path, new_path)
"Did #{action}"
- rescue => e
+ rescue StandardError => e
"Error during #{action}: #{e.inspect}"
end
end
diff --git a/lib/gitlab/cluster/lifecycle_events.rb b/lib/gitlab/cluster/lifecycle_events.rb
index 3c71ca9fcf0..b3dc59466ec 100644
--- a/lib/gitlab/cluster/lifecycle_events.rb
+++ b/lib/gitlab/cluster/lifecycle_events.rb
@@ -154,7 +154,7 @@ module Gitlab
hooks.each do |hook|
hook.call
- rescue => e
+ rescue StandardError => e
Gitlab::ErrorTracking.track_exception(e, type: 'LifecycleEvents', hook: hook)
warn("ERROR: The hook #{name} failed with exception (#{e.class}) \"#{e.message}\".")
diff --git a/lib/gitlab/conan_token.rb b/lib/gitlab/conan_token.rb
index c3d90aa78fb..d0560807f45 100644
--- a/lib/gitlab/conan_token.rb
+++ b/lib/gitlab/conan_token.rb
@@ -8,6 +8,7 @@
module Gitlab
class ConanToken
HMAC_KEY = 'gitlab-conan-packages'
+ CONAN_TOKEN_EXPIRE_TIME = 1.day.freeze
attr_reader :access_token_id, :user_id
@@ -57,7 +58,7 @@ module Gitlab
JSONWebToken::HMACToken.new(self.class.secret).tap do |token|
token['access_token'] = access_token_id
token['user_id'] = user_id
- token.expire_time = token.issued_at + 1.hour
+ token.expire_time = token.issued_at + CONAN_TOKEN_EXPIRE_TIME
end
end
end
diff --git a/lib/gitlab/consul/internal.rb b/lib/gitlab/consul/internal.rb
index 3afc24ddab9..1994369dee9 100644
--- a/lib/gitlab/consul/internal.rb
+++ b/lib/gitlab/consul/internal.rb
@@ -57,7 +57,7 @@ module Gitlab
def parse_response_body(body)
Gitlab::Json.parse(body)
- rescue
+ rescue StandardError
raise Consul::Internal::UnexpectedResponseError
end
@@ -69,7 +69,7 @@ module Gitlab
raise Consul::Internal::SSLError
rescue Errno::ECONNREFUSED
raise Consul::Internal::ECONNREFUSED
- rescue
+ rescue StandardError
raise Consul::Internal::UnexpectedResponseError
end
end
diff --git a/lib/gitlab/content_security_policy/config_loader.rb b/lib/gitlab/content_security_policy/config_loader.rb
index ff844645b11..6f6147f0f32 100644
--- a/lib/gitlab/content_security_policy/config_loader.rb
+++ b/lib/gitlab/content_security_policy/config_loader.rb
@@ -8,11 +8,33 @@ module Gitlab
media_src object_src report_uri script_src style_src worker_src).freeze
def self.default_settings_hash
- {
- 'enabled' => false,
+ settings_hash = {
+ 'enabled' => true,
'report_only' => false,
- 'directives' => DIRECTIVES.each_with_object({}) { |directive, hash| hash[directive] = nil }
+ 'directives' => {
+ 'default_src' => "'self'",
+ 'base_uri' => "'self'",
+ 'child_src' => "'none'",
+ 'connect_src' => "'self'",
+ 'font_src' => "'self'",
+ 'form_action' => "'self' https: http:",
+ 'frame_ancestors' => "'self'",
+ 'frame_src' => "'self' https://www.recaptcha.net/ https://content.googleapis.com https://content-compute.googleapis.com https://content-cloudbilling.googleapis.com https://content-cloudresourcemanager.googleapis.com",
+ 'img_src' => "'self' data: blob: http: https:",
+ 'manifest_src' => "'self'",
+ 'media_src' => "'self'",
+ 'script_src' => "'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.recaptcha.net https://apis.google.com",
+ 'style_src' => "'self' 'unsafe-inline'",
+ 'worker_src' => "'self'",
+ 'object_src' => "'none'",
+ 'report_uri' => nil
+ }
}
+
+ allow_webpack_dev_server(settings_hash) if Rails.env.development?
+ allow_cdn(settings_hash) if ENV['GITLAB_CDN_HOST'].present?
+
+ settings_hash
end
def initialize(csp_directives)
@@ -38,6 +60,26 @@ module Gitlab
arguments.strip.split(' ').map(&:strip)
end
+
+ def self.allow_webpack_dev_server(settings_hash)
+ secure = Settings.webpack.dev_server['https']
+ host_and_port = "#{Settings.webpack.dev_server['host']}:#{Settings.webpack.dev_server['port']}"
+ http_url = "#{secure ? 'https' : 'http'}://#{host_and_port}"
+ ws_url = "#{secure ? 'wss' : 'ws'}://#{host_and_port}"
+
+ append_to_directive(settings_hash, 'connect_src', "#{http_url} #{ws_url}")
+ end
+
+ def self.allow_cdn(settings_hash)
+ cdn_host = ENV['GITLAB_CDN_HOST']
+
+ append_to_directive(settings_hash, 'script_src', cdn_host)
+ append_to_directive(settings_hash, 'style_src', cdn_host)
+ end
+
+ def self.append_to_directive(settings_hash, directive, text)
+ settings_hash['directives'][directive] = "#{settings_hash['directives'][directive]} #{text}".strip
+ end
end
end
end
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index 55f381fcb64..7f55734f796 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -43,7 +43,7 @@ module Gitlab
begin
::ApplicationSetting.cached
- rescue
+ rescue StandardError
# In case Redis isn't running
# or the Redis UNIX socket file is not available
# or the DB is not running (we use migrations in the cache key)
diff --git a/lib/gitlab/cycle_analytics/summary/base.rb b/lib/gitlab/cycle_analytics/summary/base.rb
index a825d48fb77..67ad75652b0 100644
--- a/lib/gitlab/cycle_analytics/summary/base.rb
+++ b/lib/gitlab/cycle_analytics/summary/base.rb
@@ -11,11 +11,11 @@ module Gitlab
end
def title
- raise NotImplementedError.new("Expected #{self.name} to implement title")
+ raise NotImplementedError, "Expected #{self.name} to implement title"
end
def value
- raise NotImplementedError.new("Expected #{self.name} to implement value")
+ raise NotImplementedError, "Expected #{self.name} to implement value"
end
end
end
diff --git a/lib/gitlab/cycle_analytics/summary/deploy.rb b/lib/gitlab/cycle_analytics/summary/deploy.rb
index c247ef0d2a7..e5bf6ef616f 100644
--- a/lib/gitlab/cycle_analytics/summary/deploy.rb
+++ b/lib/gitlab/cycle_analytics/summary/deploy.rb
@@ -16,7 +16,7 @@ module Gitlab
def deployments_count
DeploymentsFinder
- .new(project: @project, finished_after: @from, finished_before: @to, status: :success)
+ .new(project: @project, finished_after: @from, finished_before: @to, status: :success, order_by: :finished_at)
.execute
.count
end
diff --git a/lib/gitlab/data_builder/build.rb b/lib/gitlab/data_builder/build.rb
index 0e4fc8efa95..4c31f986be5 100644
--- a/lib/gitlab/data_builder/build.rb
+++ b/lib/gitlab/data_builder/build.rb
@@ -30,6 +30,7 @@ module Gitlab
build_started_at: build.started_at,
build_finished_at: build.finished_at,
build_duration: build.duration,
+ build_queued_duration: build.queued_duration,
build_allow_failure: build.allow_failure,
build_failure_reason: build.failure_reason,
pipeline_id: commit.id,
diff --git a/lib/gitlab/data_builder/deployment.rb b/lib/gitlab/data_builder/deployment.rb
index 87ebe832862..f50ca5119b7 100644
--- a/lib/gitlab/data_builder/deployment.rb
+++ b/lib/gitlab/data_builder/deployment.rb
@@ -5,7 +5,7 @@ module Gitlab
module Deployment
extend self
- def build(deployment)
+ def build(deployment, status_changed_at)
# Deployments will not have a deployable when created using the API.
deployable_url =
if deployment.deployable
@@ -15,6 +15,7 @@ module Gitlab
{
object_kind: 'deployment',
status: deployment.status,
+ status_changed_at: status_changed_at,
deployable_id: deployment.deployable_id,
deployable_url: deployable_url,
environment: deployment.environment.name,
diff --git a/lib/gitlab/data_builder/pipeline.rb b/lib/gitlab/data_builder/pipeline.rb
index a56029c0d1d..766eaf54afe 100644
--- a/lib/gitlab/data_builder/pipeline.rb
+++ b/lib/gitlab/data_builder/pipeline.rb
@@ -31,6 +31,7 @@ module Gitlab
created_at: pipeline.created_at,
finished_at: pipeline.finished_at,
duration: pipeline.duration,
+ queued_duration: pipeline.queued_duration,
variables: pipeline.variables.map(&:hook_attrs)
}
end
@@ -59,6 +60,8 @@ module Gitlab
created_at: build.created_at,
started_at: build.started_at,
finished_at: build.finished_at,
+ duration: build.duration,
+ queued_duration: build.queued_duration,
when: build.when,
manual: build.action?,
allow_failure: build.allow_failure,
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index 3dc8976d8c5..59249c8bc1f 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -2,6 +2,16 @@
module Gitlab
module Database
+ # This constant is used when renaming tables concurrently.
+ # If you plan to rename a table using the `rename_table_safely` method, add your table here one milestone before the rename.
+ # Example:
+ # TABLES_TO_BE_RENAMED = {
+ # 'old_name' => 'new_name'
+ # }.freeze
+ TABLES_TO_BE_RENAMED = {
+ 'analytics_instance_statistics_measurements' => 'analytics_usage_trends_measurements'
+ }.freeze
+
# Minimum PostgreSQL version requirement per documentation:
# https://docs.gitlab.com/ee/install/requirements.html#postgresql-requirements
MINIMUM_POSTGRES_VERSION = 11
@@ -35,8 +45,27 @@ module Gitlab
# It does not include the default public schema
EXTRA_SCHEMAS = [DYNAMIC_PARTITIONS_SCHEMA, STATIC_PARTITIONS_SCHEMA].freeze
+ DEFAULT_POOL_HEADROOM = 10
+
+ # We configure the database connection pool size automatically based on the
+ # configured concurrency. We also add some headroom, to make sure we don't run
+ # out of connections when more threads besides the 'user-facing' ones are
+ # running.
+ #
+ # Read more about this in doc/development/database/client_side_connection_pool.md
+ def self.default_pool_size
+ headroom = (ENV["DB_POOL_HEADROOM"].presence || DEFAULT_POOL_HEADROOM).to_i
+
+ Gitlab::Runtime.max_threads + headroom
+ end
+
def self.config
- ActiveRecord::Base.configurations[Rails.env]
+ default_config_hash = ActiveRecord::Base.configurations.find_db_config(Rails.env)&.config || {}
+
+ default_config_hash.with_indifferent_access.tap do |hash|
+ # Match config/initializers/database_config.rb
+ hash[:pool] ||= default_pool_size
+ end
end
def self.username
@@ -123,6 +152,16 @@ module Gitlab
# ignore - happens when Rake tasks yet have to create a database, e.g. for testing
end
+ def self.nulls_order(field, direction = :asc, nulls_order = :nulls_last)
+ raise ArgumentError unless [:nulls_last, :nulls_first].include?(nulls_order)
+ raise ArgumentError unless [:asc, :desc].include?(direction)
+
+ case nulls_order
+ when :nulls_last then nulls_last_order(field, direction)
+ when :nulls_first then nulls_first_order(field, direction)
+ end
+ end
+
def self.nulls_last_order(field, direction = 'ASC')
Arel.sql("#{field} #{direction} NULLS LAST")
end
@@ -204,23 +243,13 @@ module Gitlab
# pool_size - The size of the DB pool.
# host - An optional host name to use instead of the default one.
def self.create_connection_pool(pool_size, host = nil, port = nil)
- env = Rails.env
- original_config = ActiveRecord::Base.configurations.to_h
-
- env_config = original_config[env].merge('pool' => pool_size)
- env_config['host'] = host if host
- env_config['port'] = port if port
-
- config = ActiveRecord::DatabaseConfigurations.new(
- original_config.merge(env => env_config)
- )
+ original_config = Gitlab::Database.config
- spec =
- ActiveRecord::
- ConnectionAdapters::
- ConnectionSpecification::Resolver.new(config).spec(env.to_sym)
+ env_config = original_config.merge(pool: pool_size)
+ env_config[:host] = host if host
+ env_config[:port] = port if port
- ActiveRecord::ConnectionAdapters::ConnectionPool.new(spec)
+ ActiveRecord::ConnectionAdapters::ConnectionHandler.new.establish_connection(env_config)
end
def self.connection
@@ -246,7 +275,7 @@ module Gitlab
connection
true
- rescue
+ rescue StandardError
false
end
@@ -347,4 +376,4 @@ module Gitlab
end
end
-Gitlab::Database.prepend_if_ee('EE::Gitlab::Database')
+Gitlab::Database.prepend_mod_with('Gitlab::Database')
diff --git a/lib/gitlab/database/as_with_materialized.rb b/lib/gitlab/database/as_with_materialized.rb
index 7c45f416638..e7e3c1766a9 100644
--- a/lib/gitlab/database/as_with_materialized.rb
+++ b/lib/gitlab/database/as_with_materialized.rb
@@ -3,19 +3,15 @@
module Gitlab
module Database
# This class is a special Arel node which allows optionally define the `MATERIALIZED` keyword for CTE and Recursive CTE queries.
- class AsWithMaterialized < Arel::Nodes::Binary
+ class AsWithMaterialized < Arel::Nodes::As
extend Gitlab::Utils::StrongMemoize
- MATERIALIZED = Arel.sql(' MATERIALIZED')
- EMPTY_STRING = Arel.sql('')
- attr_reader :expr
+ MATERIALIZED = 'MATERIALIZED '
def initialize(left, right, materialized: true)
- @expr = if materialized && self.class.materialized_supported?
- MATERIALIZED
- else
- EMPTY_STRING
- end
+ if materialized && self.class.materialized_supported?
+ right.prepend(MATERIALIZED)
+ end
super(left, right)
end
diff --git a/lib/gitlab/database/background_migration/batch_optimizer.rb b/lib/gitlab/database/background_migration/batch_optimizer.rb
new file mode 100644
index 00000000000..0668490dda8
--- /dev/null
+++ b/lib/gitlab/database/background_migration/batch_optimizer.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module BackgroundMigration
+ # This is an optimizer for throughput of batched migration jobs
+ #
+ # The underyling mechanic is based on the concept of time efficiency:
+ # time efficiency = job duration / interval
+ # Ideally, this is close but lower than 1 - so we're using time efficiently.
+ #
+ # We aim to land in the 90%-98% range, which gives the database a little breathing room
+ # in between.
+ #
+ # The optimizer is based on calculating the exponential moving average of time efficiencies
+ # for the last N jobs. If we're outside the range, we add 10% to or decrease by 20% of the batch size.
+ class BatchOptimizer
+ # Target time efficiency for a job
+ # Time efficiency is defined as: job duration / interval
+ TARGET_EFFICIENCY = (0.9..0.95).freeze
+
+ # Lower and upper bound for the batch size
+ ALLOWED_BATCH_SIZE = (1_000..2_000_000).freeze
+
+ # Limit for the multiplier of the batch size
+ MAX_MULTIPLIER = 1.2
+
+ # When smoothing time efficiency, use this many jobs
+ NUMBER_OF_JOBS = 20
+
+ # Smoothing factor for exponential moving average
+ EMA_ALPHA = 0.4
+
+ attr_reader :migration, :number_of_jobs, :ema_alpha
+
+ def initialize(migration, number_of_jobs: NUMBER_OF_JOBS, ema_alpha: EMA_ALPHA)
+ @migration = migration
+ @number_of_jobs = number_of_jobs
+ @ema_alpha = ema_alpha
+ end
+
+ def optimize!
+ return unless Feature.enabled?(:optimize_batched_migrations, type: :ops, default_enabled: :yaml)
+
+ if multiplier = batch_size_multiplier
+ migration.batch_size = (migration.batch_size * multiplier).to_i.clamp(ALLOWED_BATCH_SIZE)
+ migration.save!
+ end
+ end
+
+ private
+
+ def batch_size_multiplier
+ efficiency = migration.smoothed_time_efficiency(number_of_jobs: number_of_jobs, alpha: ema_alpha)
+
+ return if efficiency.nil? || efficiency == 0
+
+ # We hit the range - no change
+ return if TARGET_EFFICIENCY.include?(efficiency)
+
+ # Assumption: time efficiency is linear in the batch size
+ [TARGET_EFFICIENCY.max / efficiency, MAX_MULTIPLIER].min
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/background_migration/batched_job.rb b/lib/gitlab/database/background_migration/batched_job.rb
index 3b624df2bfd..869b97b8ac0 100644
--- a/lib/gitlab/database/background_migration/batched_job.rb
+++ b/lib/gitlab/database/background_migration/batched_job.rb
@@ -4,10 +4,23 @@ module Gitlab
module Database
module BackgroundMigration
class BatchedJob < ActiveRecord::Base # rubocop:disable Rails/ApplicationRecord
+ include FromUnion
+
self.table_name = :batched_background_migration_jobs
+ MAX_ATTEMPTS = 3
+ STUCK_JOBS_TIMEOUT = 1.hour.freeze
+
belongs_to :batched_migration, foreign_key: :batched_background_migration_id
+ scope :active, -> { where(status: [:pending, :running]) }
+ scope :stuck, -> { active.where('updated_at <= ?', STUCK_JOBS_TIMEOUT.ago) }
+ scope :retriable, -> {
+ failed_jobs = where(status: :failed).where('attempts < ?', MAX_ATTEMPTS)
+
+ from_union([failed_jobs, self.stuck])
+ }
+
enum status: {
pending: 0,
running: 1,
@@ -15,8 +28,22 @@ module Gitlab
succeeded: 3
}
+ scope :successful_in_execution_order, -> { where.not(finished_at: nil).succeeded.order(:finished_at) }
+
delegate :aborted?, :job_class, :table_name, :column_name, :job_arguments,
to: :batched_migration, prefix: :migration
+
+ attribute :pause_ms, :integer, default: 100
+
+ def time_efficiency
+ return unless succeeded?
+ return unless finished_at && started_at
+
+ duration = finished_at - started_at
+
+ # TODO: Switch to individual job interval (prereq: https://gitlab.com/gitlab-org/gitlab/-/issues/328801)
+ duration.to_f / batched_migration.interval
+ end
end
end
end
diff --git a/lib/gitlab/database/background_migration/batched_migration.rb b/lib/gitlab/database/background_migration/batched_migration.rb
index 4aa33ed7946..e85162f355e 100644
--- a/lib/gitlab/database/background_migration/batched_migration.rb
+++ b/lib/gitlab/database/background_migration/batched_migration.rb
@@ -20,9 +20,12 @@ module Gitlab
paused: 0,
active: 1,
aborted: 2,
- finished: 3
+ finished: 3,
+ failed: 4
}
+ attribute :pause_ms, :integer, default: 100
+
def self.active_migration
active.queue_order.first
end
@@ -35,7 +38,13 @@ module Gitlab
end
def create_batched_job!(min, max)
- batched_jobs.create!(min_value: min, max_value: max, batch_size: batch_size, sub_batch_size: sub_batch_size)
+ batched_jobs.create!(
+ min_value: min,
+ max_value: max,
+ batch_size: batch_size,
+ sub_batch_size: sub_batch_size,
+ pause_ms: pause_ms
+ )
end
def next_min_value
@@ -58,12 +67,40 @@ module Gitlab
write_attribute(:batch_class_name, class_name.demodulize)
end
+ def migrated_tuple_count
+ batched_jobs.succeeded.sum(:batch_size)
+ end
+
def prometheus_labels
@prometheus_labels ||= {
migration_id: id,
migration_identifier: "%s/%s.%s" % [job_class_name, table_name, column_name]
}
end
+
+ def smoothed_time_efficiency(number_of_jobs: 10, alpha: 0.2)
+ jobs = batched_jobs.successful_in_execution_order.reverse_order.limit(number_of_jobs)
+
+ return if jobs.size < number_of_jobs
+
+ efficiencies = jobs.map(&:time_efficiency).reject(&:nil?).each_with_index
+
+ dividend = efficiencies.reduce(0) do |total, (job_eff, i)|
+ total + job_eff * (1 - alpha)**i
+ end
+
+ divisor = efficiencies.reduce(0) do |total, (job_eff, i)|
+ total + (1 - alpha)**i
+ end
+
+ return if divisor == 0
+
+ (dividend / divisor).round(2)
+ end
+
+ def optimize!
+ BatchOptimizer.new(self).optimize!
+ end
end
end
end
diff --git a/lib/gitlab/database/background_migration/batched_migration_runner.rb b/lib/gitlab/database/background_migration/batched_migration_runner.rb
index cf8b61f5feb..67fe6c536e6 100644
--- a/lib/gitlab/database/background_migration/batched_migration_runner.rb
+++ b/lib/gitlab/database/background_migration/batched_migration_runner.rb
@@ -19,8 +19,10 @@ module Gitlab
#
# Note that this method is primarily intended to called by a scheduled worker.
def run_migration_job(active_migration)
- if next_batched_job = create_next_batched_job!(active_migration)
+ if next_batched_job = find_or_create_next_batched_job(active_migration)
migration_wrapper.perform(next_batched_job)
+
+ active_migration.optimize!
else
finish_active_migration(active_migration)
end
@@ -46,12 +48,12 @@ module Gitlab
attr_reader :migration_wrapper
- def create_next_batched_job!(active_migration)
- next_batch_range = find_next_batch_range(active_migration)
-
- return if next_batch_range.nil?
-
- active_migration.create_batched_job!(next_batch_range.min, next_batch_range.max)
+ def find_or_create_next_batched_job(active_migration)
+ if next_batch_range = find_next_batch_range(active_migration)
+ active_migration.create_batched_job!(next_batch_range.min, next_batch_range.max)
+ else
+ active_migration.batched_jobs.retriable.first
+ end
end
def find_next_batch_range(active_migration)
@@ -80,7 +82,13 @@ module Gitlab
end
def finish_active_migration(active_migration)
- active_migration.finished!
+ return if active_migration.batched_jobs.active.exists?
+
+ if active_migration.batched_jobs.failed.exists?
+ active_migration.failed!
+ else
+ active_migration.finished!
+ end
end
end
end
diff --git a/lib/gitlab/database/background_migration/batched_migration_wrapper.rb b/lib/gitlab/database/background_migration/batched_migration_wrapper.rb
index c276f8ce75b..e37df102872 100644
--- a/lib/gitlab/database/background_migration/batched_migration_wrapper.rb
+++ b/lib/gitlab/database/background_migration/batched_migration_wrapper.rb
@@ -19,10 +19,10 @@ module Gitlab
execute_batch(batch_tracking_record)
batch_tracking_record.status = :succeeded
- rescue => e
+ rescue Exception # rubocop:disable Lint/RescueException
batch_tracking_record.status = :failed
- raise e
+ raise
ensure
finish_tracking_execution(batch_tracking_record)
track_prometheus_metrics(batch_tracking_record)
@@ -31,7 +31,7 @@ module Gitlab
private
def start_tracking_execution(tracking_record)
- tracking_record.update!(attempts: tracking_record.attempts + 1, status: :running, started_at: Time.current)
+ tracking_record.update!(attempts: tracking_record.attempts + 1, status: :running, started_at: Time.current, finished_at: nil, metrics: {})
end
def execute_batch(tracking_record)
@@ -43,6 +43,7 @@ module Gitlab
tracking_record.migration_table_name,
tracking_record.migration_column_name,
tracking_record.sub_batch_size,
+ tracking_record.pause_ms,
*tracking_record.migration_job_arguments)
if job_instance.respond_to?(:batch_metrics)
@@ -61,11 +62,12 @@ module Gitlab
metric_for(:gauge_batch_size).set(base_labels, tracking_record.batch_size)
metric_for(:gauge_sub_batch_size).set(base_labels, tracking_record.sub_batch_size)
+ metric_for(:gauge_interval).set(base_labels, tracking_record.batched_migration.interval)
+ metric_for(:gauge_job_duration).set(base_labels, (tracking_record.finished_at - tracking_record.started_at).to_i)
metric_for(:counter_updated_tuples).increment(base_labels, tracking_record.batch_size)
-
- # Time efficiency: Ratio of duration to interval (ideal: less than, but close to 1)
- efficiency = (tracking_record.finished_at - tracking_record.started_at).to_i / migration.interval.to_f
- metric_for(:histogram_time_efficiency).observe(base_labels, efficiency)
+ metric_for(:gauge_migrated_tuples).set(base_labels, tracking_record.batched_migration.migrated_tuple_count)
+ metric_for(:gauge_total_tuple_count).set(base_labels, tracking_record.batched_migration.total_tuple_count)
+ metric_for(:gauge_last_update_time).set(base_labels, Time.current.to_i)
if metrics = tracking_record.metrics
metrics['timings']&.each do |key, timings|
@@ -94,21 +96,35 @@ module Gitlab
:batched_migration_job_sub_batch_size,
'Sub-batch size for a batched migration job'
),
+ gauge_interval: Gitlab::Metrics.gauge(
+ :batched_migration_job_interval_seconds,
+ 'Interval for a batched migration job'
+ ),
+ gauge_job_duration: Gitlab::Metrics.gauge(
+ :batched_migration_job_duration_seconds,
+ 'Duration for a batched migration job'
+ ),
counter_updated_tuples: Gitlab::Metrics.counter(
:batched_migration_job_updated_tuples_total,
'Number of tuples updated by batched migration job'
),
+ gauge_migrated_tuples: Gitlab::Metrics.gauge(
+ :batched_migration_migrated_tuples_total,
+ 'Total number of tuples migrated by a batched migration'
+ ),
histogram_timings: Gitlab::Metrics.histogram(
- :batched_migration_job_duration_seconds,
- 'Timings for a batched migration job',
+ :batched_migration_job_query_duration_seconds,
+ 'Query timings for a batched migration job',
{},
[0.1, 0.25, 0.5, 1, 5].freeze
),
- histogram_time_efficiency: Gitlab::Metrics.histogram(
- :batched_migration_job_time_efficiency,
- 'Ratio of job duration to interval',
- {},
- [0.5, 0.9, 1, 1.5, 2].freeze
+ gauge_total_tuple_count: Gitlab::Metrics.gauge(
+ :batched_migration_total_tuple_count,
+ 'Total tuple count the migration needs to touch'
+ ),
+ gauge_last_update_time: Gitlab::Metrics.gauge(
+ :batched_migration_last_update_time_seconds,
+ 'Unix epoch time in seconds'
)
}
end
diff --git a/lib/gitlab/database/background_migration_job.rb b/lib/gitlab/database/background_migration_job.rb
index 1b9d7cbc9a1..1121793917b 100644
--- a/lib/gitlab/database/background_migration_job.rb
+++ b/lib/gitlab/database/background_migration_job.rb
@@ -9,7 +9,7 @@ module Gitlab
scope :for_migration_class, -> (class_name) { where(class_name: normalize_class_name(class_name)) }
scope :for_migration_execution, -> (class_name, arguments) do
- for_migration_class(class_name).where('arguments = ?', arguments.to_json)
+ for_migration_class(class_name).where('arguments = ?', arguments.to_json) # rubocop:disable Rails/WhereEquals
end
scope :for_partitioning_migration, -> (class_name, table_name) do
diff --git a/lib/gitlab/database/consistency.rb b/lib/gitlab/database/consistency.rb
index b7d06a26ddb..e99ea7a3232 100644
--- a/lib/gitlab/database/consistency.rb
+++ b/lib/gitlab/database/consistency.rb
@@ -28,4 +28,4 @@ module Gitlab
end
end
-::Gitlab::Database::Consistency.singleton_class.prepend_if_ee('EE::Gitlab::Database::Consistency')
+::Gitlab::Database::Consistency.singleton_class.prepend_mod_with('Gitlab::Database::Consistency')
diff --git a/lib/gitlab/database/loose_index_scan_distinct_count.rb b/lib/gitlab/database/loose_index_scan_distinct_count.rb
index 884f4d47ff8..26be07f91c4 100644
--- a/lib/gitlab/database/loose_index_scan_distinct_count.rb
+++ b/lib/gitlab/database/loose_index_scan_distinct_count.rb
@@ -11,7 +11,7 @@ module Gitlab
# This query will read each element in the index matching the project_id filter.
# If for a project_id has 100_000 issues, all 100_000 elements will be read.
#
- # A loose index scan will read only one entry from the index for each project_id to reduce the number of disk reads.
+ # A loose index scan will only read one entry from the index for each project_id to reduce the number of disk reads.
#
# Usage:
#
@@ -94,7 +94,7 @@ module Gitlab
elsif column.is_a?(Arel::Attributes::Attribute)
column
else
- raise ColumnConfigurationError.new("Cannot transform the column: #{column.inspect}, please provide the column name as string")
+ raise ColumnConfigurationError, "Cannot transform the column: #{column.inspect}, please provide the column name as string"
end
end
end
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index d06a73da8ac..3a94e109d2a 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -5,6 +5,7 @@ module Gitlab
module MigrationHelpers
include Migrations::BackgroundMigrationHelpers
include DynamicModelHelpers
+ include Migrations::RenameTableHelpers
# https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
MAX_IDENTIFIER_NAME_LENGTH = 63
@@ -51,7 +52,7 @@ module Gitlab
allow_null: options[:null]
)
else
- add_column(table_name, column_name, :datetime_with_timezone, options)
+ add_column(table_name, column_name, :datetime_with_timezone, **options)
end
end
end
@@ -143,13 +144,13 @@ module Gitlab
options = options.merge({ algorithm: :concurrently })
- if index_exists?(table_name, column_name, options)
+ if index_exists?(table_name, column_name, **options)
Gitlab::AppLogger.warn "Index not created because it already exists (this may be due to an aborted migration or similar): table_name: #{table_name}, column_name: #{column_name}"
return
end
disable_statement_timeout do
- add_index(table_name, column_name, options)
+ add_index(table_name, column_name, **options)
end
end
@@ -169,13 +170,13 @@ module Gitlab
options = options.merge({ algorithm: :concurrently })
- unless index_exists?(table_name, column_name, options)
+ unless index_exists?(table_name, column_name, **options)
Gitlab::AppLogger.warn "Index not removed because it does not exist (this may be due to an aborted migration or similar): table_name: #{table_name}, column_name: #{column_name}"
return
end
disable_statement_timeout do
- remove_index(table_name, options.merge({ column: column_name }))
+ remove_index(table_name, **options.merge({ column: column_name }))
end
end
@@ -205,7 +206,7 @@ module Gitlab
end
disable_statement_timeout do
- remove_index(table_name, options.merge({ name: index_name }))
+ remove_index(table_name, **options.merge({ name: index_name }))
end
end
@@ -565,7 +566,7 @@ module Gitlab
check_trigger_permissions!(table)
- remove_rename_triggers_for_postgresql(table, trigger_name)
+ remove_rename_triggers(table, trigger_name)
remove_column(table, new)
end
@@ -576,8 +577,19 @@ module Gitlab
# table - The name of the table to install the trigger in.
# old_column - The name of the old column.
# new_column - The name of the new column.
- def install_rename_triggers(table, old_column, new_column)
- install_rename_triggers_for_postgresql(table, old_column, new_column)
+ # trigger_name - The name of the trigger to use (optional).
+ def install_rename_triggers(table, old, new, trigger_name: nil)
+ Gitlab::Database::UnidirectionalCopyTrigger.on_table(table).create(old, new, trigger_name: trigger_name)
+ end
+
+ # Removes the triggers used for renaming a column concurrently.
+ def remove_rename_triggers(table, trigger)
+ Gitlab::Database::UnidirectionalCopyTrigger.on_table(table).drop(trigger)
+ end
+
+ # Returns the (base) name to use for triggers when renaming columns.
+ def rename_trigger_name(table, old, new)
+ Gitlab::Database::UnidirectionalCopyTrigger.on_table(table).name(old, new)
end
# Changes the type of a column concurrently.
@@ -663,7 +675,7 @@ module Gitlab
install_rename_triggers(table, column, temp_column)
end
- rescue
+ rescue StandardError
# create_column_from can not run inside a transaction, which means
# that there is a risk that if any of the operations that follow it
# fail, we'll be left with an inconsistent schema
@@ -690,7 +702,7 @@ module Gitlab
check_trigger_permissions!(table)
- remove_rename_triggers_for_postgresql(table, trigger_name)
+ remove_rename_triggers(table, trigger_name)
remove_column(table, old)
end
@@ -905,7 +917,11 @@ module Gitlab
end
end
- # Initializes the conversion of an integer column to bigint
+ def convert_to_bigint_column(column)
+ "#{column}_convert_to_bigint"
+ end
+
+ # Initializes the conversion of a set of integer columns to bigint
#
# It can be used for converting both a Primary Key and any Foreign Keys
# that may reference it or any other integer column that we may want to
@@ -923,14 +939,14 @@ module Gitlab
# Note: this helper is intended to be used in a regular (pre-deployment) migration.
#
# This helper is part 1 of a multi-step migration process:
- # 1. initialize_conversion_of_integer_to_bigint to create the new column and database triggers
+ # 1. initialize_conversion_of_integer_to_bigint to create the new columns and database trigger
# 2. backfill_conversion_of_integer_to_bigint to copy historic data using background migrations
# 3. remaining steps TBD, see #288005
#
# table - The name of the database table containing the column
- # column - The name of the column that we want to convert to bigint.
+ # columns - The name, or array of names, of the column(s) that we want to convert to bigint.
# primary_key - The name of the primary key column (most often :id)
- def initialize_conversion_of_integer_to_bigint(table, column, primary_key: :id)
+ def initialize_conversion_of_integer_to_bigint(table, columns, primary_key: :id)
unless table_exists?(table)
raise "Table #{table} does not exist"
end
@@ -939,34 +955,54 @@ module Gitlab
raise "Column #{primary_key} does not exist on #{table}"
end
- unless column_exists?(table, column)
- raise "Column #{column} does not exist on #{table}"
+ columns = Array.wrap(columns)
+ columns.each do |column|
+ next if column_exists?(table, column)
+
+ raise ArgumentError, "Column #{column} does not exist on #{table}"
end
check_trigger_permissions!(table)
- old_column = column_for(table, column)
- tmp_column = "#{column}_convert_to_bigint"
+ conversions = columns.to_h { |column| [column, convert_to_bigint_column(column)] }
with_lock_retries do
- if (column.to_s == primary_key.to_s) || !old_column.null
- # If the column to be converted is either a PK or is defined as NOT NULL,
- # set it to `NOT NULL DEFAULT 0` and we'll copy paste the correct values bellow
- # That way, we skip the expensive validation step required to add
- # a NOT NULL constraint at the end of the process
- add_column(table, tmp_column, :bigint, default: old_column.default || 0, null: false)
- else
- add_column(table, tmp_column, :bigint, default: old_column.default)
+ conversions.each do |(source_column, temporary_name)|
+ column = column_for(table, source_column)
+
+ if (column.name.to_s == primary_key.to_s) || !column.null
+ # If the column to be converted is either a PK or is defined as NOT NULL,
+ # set it to `NOT NULL DEFAULT 0` and we'll copy paste the correct values bellow
+ # That way, we skip the expensive validation step required to add
+ # a NOT NULL constraint at the end of the process
+ add_column(table, temporary_name, :bigint, default: column.default || 0, null: false)
+ else
+ add_column(table, temporary_name, :bigint, default: column.default)
+ end
end
- install_rename_triggers(table, column, tmp_column)
+ install_rename_triggers(table, conversions.keys, conversions.values)
end
end
- # Backfills the new column used in the conversion of an integer column to bigint using background migrations.
+ # Reverts `initialize_conversion_of_integer_to_bigint`
+ #
+ # table - The name of the database table containing the columns
+ # columns - The name, or array of names, of the column(s) that we're converting to bigint.
+ def revert_initialize_conversion_of_integer_to_bigint(table, columns)
+ columns = Array.wrap(columns)
+ temporary_columns = columns.map { |column| convert_to_bigint_column(column) }
+
+ trigger_name = rename_trigger_name(table, columns, temporary_columns)
+ remove_rename_triggers(table, trigger_name)
+
+ temporary_columns.each { |column| remove_column(table, column) }
+ end
+
+ # Backfills the new columns used in an integer-to-bigint conversion using background migrations.
#
# - This helper should be called from a post-deployment migration.
- # - In order for this helper to work properly, the new column must be first initialized with
+ # - In order for this helper to work properly, the new columns must be first initialized with
# the `initialize_conversion_of_integer_to_bigint` helper.
# - It tracks the scheduled background jobs through Gitlab::Database::BackgroundMigration::BatchedMigration,
# which allows a more thorough check that all jobs succeeded in the
@@ -976,12 +1012,12 @@ module Gitlab
# deployed (including background job changes) before we begin processing the background migration.
#
# This helper is part 2 of a multi-step migration process:
- # 1. initialize_conversion_of_integer_to_bigint to create the new column and database triggers
+ # 1. initialize_conversion_of_integer_to_bigint to create the new columns and database trigger
# 2. backfill_conversion_of_integer_to_bigint to copy historic data using background migrations
# 3. remaining steps TBD, see #288005
#
# table - The name of the database table containing the column
- # column - The name of the column that we want to convert to bigint.
+ # columns - The name, or an array of names, of the column(s) we want to convert to bigint.
# primary_key - The name of the primary key column (most often :id)
# batch_size - The number of rows to schedule in a single background migration
# sub_batch_size - The smaller batches that will be used by each scheduled job
@@ -1001,7 +1037,7 @@ module Gitlab
# between the scheduled jobs
def backfill_conversion_of_integer_to_bigint(
table,
- column,
+ columns,
primary_key: :id,
batch_size: 20_000,
sub_batch_size: 1000,
@@ -1016,46 +1052,43 @@ module Gitlab
raise "Column #{primary_key} does not exist on #{table}"
end
- unless column_exists?(table, column)
- raise "Column #{column} does not exist on #{table}"
- end
+ conversions = Array.wrap(columns).to_h do |column|
+ raise ArgumentError, "Column #{column} does not exist on #{table}" unless column_exists?(table, column)
- tmp_column = "#{column}_convert_to_bigint"
+ temporary_name = convert_to_bigint_column(column)
+ raise ArgumentError, "Column #{temporary_name} does not exist on #{table}" unless column_exists?(table, temporary_name)
- unless column_exists?(table, tmp_column)
- raise 'The temporary column does not exist, initialize it with `initialize_conversion_of_integer_to_bigint`'
+ [column, temporary_name]
end
- batched_migration = queue_batched_background_migration(
+ queue_batched_background_migration(
'CopyColumnUsingBackgroundMigrationJob',
table,
primary_key,
- column,
- tmp_column,
+ conversions.keys,
+ conversions.values,
job_interval: interval,
batch_size: batch_size,
sub_batch_size: sub_batch_size)
-
- if perform_background_migration_inline?
- # To ensure the schema is up to date immediately we perform the
- # migration inline in dev / test environments.
- Gitlab::Database::BackgroundMigration::BatchedMigrationRunner.new.run_entire_migration(batched_migration)
- end
end
- # Performs a concurrent column rename when using PostgreSQL.
- def install_rename_triggers_for_postgresql(table, old, new, trigger_name: nil)
- Gitlab::Database::UnidirectionalCopyTrigger.on_table(table).create(old, new, trigger_name: trigger_name)
- end
+ # Reverts `backfill_conversion_of_integer_to_bigint`
+ #
+ # table - The name of the database table containing the column
+ # columns - The name, or an array of names, of the column(s) we want to convert to bigint.
+ # primary_key - The name of the primary key column (most often :id)
+ def revert_backfill_conversion_of_integer_to_bigint(table, columns, primary_key: :id)
+ columns = Array.wrap(columns)
- # Removes the triggers used for renaming a PostgreSQL column concurrently.
- def remove_rename_triggers_for_postgresql(table, trigger)
- Gitlab::Database::UnidirectionalCopyTrigger.on_table(table).drop(trigger)
- end
+ conditions = ActiveRecord::Base.sanitize_sql([
+ 'job_class_name = :job_class_name AND table_name = :table_name AND column_name = :column_name AND job_arguments = :job_arguments',
+ job_class_name: 'CopyColumnUsingBackgroundMigrationJob',
+ table_name: table,
+ column_name: primary_key,
+ job_arguments: [columns, columns.map { |column| convert_to_bigint_column(column) }].to_json
+ ])
- # Returns the (base) name to use for triggers when renaming columns.
- def rename_trigger_name(table, old, new)
- Gitlab::Database::UnidirectionalCopyTrigger.on_table(table).name(old, new)
+ execute("DELETE FROM batched_background_migrations WHERE #{conditions}")
end
# Returns an Array containing the indexes for the given column
@@ -1162,8 +1195,8 @@ module Gitlab
end
end
- def remove_foreign_key_without_error(*args)
- remove_foreign_key(*args)
+ def remove_foreign_key_without_error(*args, **kwargs)
+ remove_foreign_key(*args, **kwargs)
rescue ArgumentError
end
diff --git a/lib/gitlab/database/migration_helpers/cascading_namespace_settings.rb b/lib/gitlab/database/migration_helpers/cascading_namespace_settings.rb
new file mode 100644
index 00000000000..eecf96acb30
--- /dev/null
+++ b/lib/gitlab/database/migration_helpers/cascading_namespace_settings.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module MigrationHelpers
+ module CascadingNamespaceSettings
+ include Gitlab::Database::MigrationHelpers
+
+ # Creates the four required columns that constitutes a single cascading
+ # namespace settings attribute. This helper is only appropriate if the
+ # setting is not already present as a non-cascading attribute.
+ #
+ # Creates the `setting_name` column along with the `lock_setting_name`
+ # column in both `namespace_settings` and `application_settings`.
+ #
+ # This helper is not reversible and must be defined in conjunction with
+ # `remove_cascading_namespace_setting` in separate up and down directions.
+ #
+ # setting_name - The name of the cascading attribute - same as defined
+ # in `NamespaceSetting` with the `cascading_attr` method.
+ # type - The column type for the setting itself (:boolean, :integer, etc.)
+ # options - Standard Rails column options hash. Accepts keys such as
+ # `null` and `default`.
+ #
+ # `null` and `default` options will only be applied to the `application_settings`
+ # column. In most cases, a non-null default value should be specified.
+ def add_cascading_namespace_setting(setting_name, type, **options)
+ lock_column_name = "lock_#{setting_name}".to_sym
+
+ check_cascading_namespace_setting_consistency(setting_name, lock_column_name)
+
+ namespace_options = options.merge(null: true, default: nil)
+
+ with_lock_retries do
+ add_column(:namespace_settings, setting_name, type, namespace_options)
+ add_column(:namespace_settings, lock_column_name, :boolean, default: false, null: false)
+ end
+
+ add_column(:application_settings, setting_name, type, options)
+ add_column(:application_settings, lock_column_name, :boolean, default: false, null: false)
+ end
+
+ def remove_cascading_namespace_setting(setting_name)
+ lock_column_name = "lock_#{setting_name}".to_sym
+
+ with_lock_retries do
+ remove_column(:namespace_settings, setting_name) if column_exists?(:namespace_settings, setting_name)
+ remove_column(:namespace_settings, lock_column_name) if column_exists?(:namespace_settings, lock_column_name)
+ end
+
+ remove_column(:application_settings, setting_name) if column_exists?(:application_settings, setting_name)
+ remove_column(:application_settings, lock_column_name) if column_exists?(:application_settings, lock_column_name)
+ end
+
+ private
+
+ def check_cascading_namespace_setting_consistency(setting_name, lock_name)
+ existing_columns = []
+
+ %w(namespace_settings application_settings).each do |table|
+ existing_columns << "#{table}.#{setting_name}" if column_exists?(table.to_sym, setting_name)
+ existing_columns << "#{table}.#{lock_name}" if column_exists?(table.to_sym, lock_name)
+ end
+
+ return if existing_columns.empty?
+
+ raise <<~ERROR
+ One or more cascading namespace columns already exist. `add_cascading_namespace_setting` helper
+ can only be used for new settings, when none of the required columns already exist.
+ Existing columns: #{existing_columns.join(', ')}
+ ERROR
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/migrations/instrumentation.rb b/lib/gitlab/database/migrations/instrumentation.rb
index 959028ce00b..e9ef80d5198 100644
--- a/lib/gitlab/database/migrations/instrumentation.rb
+++ b/lib/gitlab/database/migrations/instrumentation.rb
@@ -4,6 +4,9 @@ module Gitlab
module Database
module Migrations
class Instrumentation
+ RESULT_DIR = Rails.root.join('tmp', 'migration-testing').freeze
+ STATS_FILENAME = 'migration-stats.json'
+
attr_reader :observations
def initialize(observers = ::Gitlab::Database::Migrations::Observers.all_observers)
@@ -21,7 +24,7 @@ module Gitlab
observation.walltime = Benchmark.realtime do
yield
- rescue => e
+ rescue StandardError => e
exception = e
observation.success = false
end
@@ -47,7 +50,7 @@ module Gitlab
def on_each_observer(&block)
observers.each do |observer|
yield observer
- rescue => e
+ rescue StandardError => e
Gitlab::AppLogger.error("Migration observer #{observer.class} failed with: #{e}")
end
end
diff --git a/lib/gitlab/database/migrations/observers.rb b/lib/gitlab/database/migrations/observers.rb
index 592993aeac5..b65a303ef30 100644
--- a/lib/gitlab/database/migrations/observers.rb
+++ b/lib/gitlab/database/migrations/observers.rb
@@ -7,7 +7,8 @@ module Gitlab
def self.all_observers
[
TotalDatabaseSizeChange.new,
- QueryStatistics.new
+ QueryStatistics.new,
+ QueryLog.new
]
end
end
diff --git a/lib/gitlab/database/migrations/observers/query_log.rb b/lib/gitlab/database/migrations/observers/query_log.rb
new file mode 100644
index 00000000000..45df07fe391
--- /dev/null
+++ b/lib/gitlab/database/migrations/observers/query_log.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module Migrations
+ module Observers
+ class QueryLog < MigrationObserver
+ def before
+ @logger_was = ActiveRecord::Base.logger
+ @log_file_path = File.join(Instrumentation::RESULT_DIR, 'current.log')
+ @logger = Logger.new(@log_file_path)
+ ActiveRecord::Base.logger = @logger
+ end
+
+ def after
+ ActiveRecord::Base.logger = @logger_was
+ @logger.close
+ end
+
+ def record(observation)
+ File.rename(@log_file_path, File.join(Instrumentation::RESULT_DIR, "#{observation.migration}.log"))
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/partitioning/partition_creator.rb b/lib/gitlab/database/partitioning/partition_creator.rb
index 547e0b9b957..d4b2b8d50e2 100644
--- a/lib/gitlab/database/partitioning/partition_creator.rb
+++ b/lib/gitlab/database/partitioning/partition_creator.rb
@@ -38,7 +38,7 @@ module Gitlab
create(model, partitions_to_create)
end
- rescue => e
+ rescue StandardError => e
Gitlab::AppLogger.error("Failed to create partition(s) for #{model.table_name}: #{e.class}: #{e.message}")
end
end
diff --git a/lib/gitlab/database/partitioning_migration_helpers/index_helpers.rb b/lib/gitlab/database/partitioning_migration_helpers/index_helpers.rb
index 0bc1343acca..c0cc97de276 100644
--- a/lib/gitlab/database/partitioning_migration_helpers/index_helpers.rb
+++ b/lib/gitlab/database/partitioning_migration_helpers/index_helpers.rb
@@ -40,7 +40,7 @@ module Gitlab
end
with_lock_retries do
- add_index(table_name, column_names, options)
+ add_index(table_name, column_names, **options)
end
end
diff --git a/lib/gitlab/database/postgres_hll/batch_distinct_counter.rb b/lib/gitlab/database/postgres_hll/batch_distinct_counter.rb
index e8b49c7f62c..aa46b98be5d 100644
--- a/lib/gitlab/database/postgres_hll/batch_distinct_counter.rb
+++ b/lib/gitlab/database/postgres_hll/batch_distinct_counter.rb
@@ -69,10 +69,8 @@ module Gitlab
hll_buckets = Buckets.new
while batch_start <= finish
- begin
- hll_buckets.merge_hash!(hll_buckets_for_batch(batch_start, batch_start + batch_size))
- batch_start += batch_size
- end
+ hll_buckets.merge_hash!(hll_buckets_for_batch(batch_start, batch_start + batch_size))
+ batch_start += batch_size
sleep(SLEEP_TIME_IN_SECONDS)
end
diff --git a/lib/gitlab/database/reindexing/concurrent_reindex.rb b/lib/gitlab/database/reindexing/concurrent_reindex.rb
index a6fe7d61a4f..7e2dd55d21b 100644
--- a/lib/gitlab/database/reindexing/concurrent_reindex.rb
+++ b/lib/gitlab/database/reindexing/concurrent_reindex.rb
@@ -11,7 +11,14 @@ module Gitlab
PG_IDENTIFIER_LENGTH = 63
TEMPORARY_INDEX_PREFIX = 'tmp_reindex_'
REPLACED_INDEX_PREFIX = 'old_reindex_'
- STATEMENT_TIMEOUT = 6.hours
+ STATEMENT_TIMEOUT = 9.hours
+
+ # When dropping an index, we acquire a SHARE UPDATE EXCLUSIVE lock,
+ # which only conflicts with DDL and vacuum. We therefore execute this with a rather
+ # high lock timeout and a long pause in between retries. This is an alternative to
+ # setting a high statement timeout, which would lead to a long running query with effects
+ # on e.g. vacuum.
+ REMOVE_INDEX_RETRY_CONFIG = [[1.minute, 9.minutes]] * 30
attr_reader :index, :logger
@@ -70,7 +77,7 @@ module Gitlab
ensure
begin
remove_index(index.schema, replacement_index_name)
- rescue => e
+ rescue StandardError => e
logger.error(e)
end
end
@@ -95,7 +102,13 @@ module Gitlab
def remove_index(schema, name)
logger.info("Removing index #{schema}.#{name}")
- set_statement_timeout do
+ retries = Gitlab::Database::WithLockRetriesOutsideTransaction.new(
+ timing_configuration: REMOVE_INDEX_RETRY_CONFIG,
+ klass: self.class,
+ logger: logger
+ )
+
+ retries.run(raise_on_exhaustion: false) do
connection.execute(<<~SQL)
DROP INDEX CONCURRENTLY
IF EXISTS #{quote_table_name(schema)}.#{quote_table_name(name)}
@@ -121,7 +134,6 @@ module Gitlab
def with_lock_retries(&block)
arguments = { klass: self.class, logger: logger }
-
Gitlab::Database::WithLockRetries.new(**arguments).run(raise_on_exhaustion: true, &block)
end
diff --git a/lib/gitlab/database/reindexing/coordinator.rb b/lib/gitlab/database/reindexing/coordinator.rb
index 7a7d17ca196..d68f47b5b6c 100644
--- a/lib/gitlab/database/reindexing/coordinator.rb
+++ b/lib/gitlab/database/reindexing/coordinator.rb
@@ -42,7 +42,7 @@ module Gitlab
def perform_for(index, action)
ConcurrentReindex.new(index).perform
- rescue
+ rescue StandardError
action.state = :failed
raise
diff --git a/lib/gitlab/database/reindexing/grafana_notifier.rb b/lib/gitlab/database/reindexing/grafana_notifier.rb
index b1e5ecb9ade..f4ea59deb50 100644
--- a/lib/gitlab/database/reindexing/grafana_notifier.rb
+++ b/lib/gitlab/database/reindexing/grafana_notifier.rb
@@ -53,7 +53,7 @@ module Gitlab
log_error("Response code #{response.code}") unless success
success
- rescue => err
+ rescue StandardError => err
log_error(err)
false
diff --git a/lib/gitlab/database/rename_table_helpers.rb b/lib/gitlab/database/rename_table_helpers.rb
new file mode 100644
index 00000000000..7f5af038c6d
--- /dev/null
+++ b/lib/gitlab/database/rename_table_helpers.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module RenameTableHelpers
+ def rename_table_safely(old_table_name, new_table_name)
+ with_lock_retries do
+ rename_table(old_table_name, new_table_name)
+ execute("CREATE VIEW #{old_table_name} AS SELECT * FROM #{new_table_name}")
+ end
+ end
+
+ def undo_rename_table_safely(old_table_name, new_table_name)
+ with_lock_retries do
+ execute("DROP VIEW IF EXISTS #{old_table_name}")
+ rename_table(new_table_name, old_table_name)
+ end
+ end
+
+ def finalize_table_rename(old_table_name, new_table_name)
+ with_lock_retries do
+ execute("DROP VIEW IF EXISTS #{old_table_name}")
+ end
+ end
+
+ def undo_finalize_table_rename(old_table_name, new_table_name)
+ with_lock_retries do
+ execute("CREATE VIEW #{old_table_name} AS SELECT * FROM #{new_table_name}")
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/schema_cache_with_renamed_table.rb b/lib/gitlab/database/schema_cache_with_renamed_table.rb
new file mode 100644
index 00000000000..28123edd708
--- /dev/null
+++ b/lib/gitlab/database/schema_cache_with_renamed_table.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module SchemaCacheWithRenamedTable
+ # Override methods in ActiveRecord::ConnectionAdapters::SchemaCache
+
+ def clear!
+ super
+
+ clear_renamed_tables_cache!
+ end
+
+ def clear_data_source_cache!(name)
+ super(name)
+
+ clear_renamed_tables_cache!
+ end
+
+ def primary_keys(table_name)
+ super(underlying_table(table_name))
+ end
+
+ def columns(table_name)
+ super(underlying_table(table_name))
+ end
+
+ def columns_hash(table_name)
+ super(underlying_table(table_name))
+ end
+
+ def indexes(table_name)
+ super(underlying_table(table_name))
+ end
+
+ private
+
+ def underlying_table(table_name)
+ renamed_tables_cache.fetch(table_name, table_name)
+ end
+
+ def renamed_tables_cache
+ @renamed_tables ||= begin
+ Gitlab::Database::TABLES_TO_BE_RENAMED.select do |old_name, new_name|
+ ActiveRecord::Base.connection.view_exists?(old_name)
+ end
+ end
+ end
+
+ def clear_renamed_tables_cache!
+ @renamed_tables = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/with_lock_retries.rb b/lib/gitlab/database/with_lock_retries.rb
index 3fb52d786ad..bbf8f133f0f 100644
--- a/lib/gitlab/database/with_lock_retries.rb
+++ b/lib/gitlab/database/with_lock_retries.rb
@@ -92,7 +92,7 @@ module Gitlab
end
begin
- run_block_with_transaction
+ run_block_with_lock_timeout
rescue ActiveRecord::LockWaitTimeout
if retry_with_lock_timeout?
disable_idle_in_transaction_timeout if ActiveRecord::Base.connection.transaction_open?
@@ -121,7 +121,7 @@ module Gitlab
block.call
end
- def run_block_with_transaction
+ def run_block_with_lock_timeout
ActiveRecord::Base.transaction(requires_new: true) do
execute("SET LOCAL lock_timeout TO '#{current_lock_timeout_in_ms}ms'")
diff --git a/lib/gitlab/database/with_lock_retries_outside_transaction.rb b/lib/gitlab/database/with_lock_retries_outside_transaction.rb
new file mode 100644
index 00000000000..175cc493e36
--- /dev/null
+++ b/lib/gitlab/database/with_lock_retries_outside_transaction.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ # This retry method behaves similar to WithLockRetries
+ # except it does not wrap itself into a transaction scope.
+ #
+ # In our context, this is only useful if directly connected to
+ # PostgreSQL. When going through pgbouncer, this method **won't work**
+ # as it relies on using `SET` outside transactions (and hence can be
+ # multiplexed across different connections).
+ class WithLockRetriesOutsideTransaction < WithLockRetries
+ private
+
+ def run_block_with_lock_timeout
+ execute("SET lock_timeout TO '#{current_lock_timeout_in_ms}ms'")
+
+ log(message: 'Lock timeout is set', current_iteration: current_iteration, lock_timeout_in_ms: current_lock_timeout_in_ms)
+
+ run_block
+
+ log(message: 'Migration finished', current_iteration: current_iteration, lock_timeout_in_ms: current_lock_timeout_in_ms)
+ end
+
+ def run_block_without_lock_timeout
+ log(message: "Couldn't acquire lock to perform the migration", current_iteration: current_iteration)
+ log(message: "Executing without lock timeout", current_iteration: current_iteration)
+
+ disable_lock_timeout
+
+ run_block
+
+ log(message: 'Migration finished', current_iteration: current_iteration)
+ end
+
+ def disable_lock_timeout
+ execute("SET lock_timeout TO '0'")
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/default_branch.rb b/lib/gitlab/default_branch.rb
new file mode 100644
index 00000000000..6bd9a5675c4
--- /dev/null
+++ b/lib/gitlab/default_branch.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+# Class is used while we're migrating from master to main
+module Gitlab
+ module DefaultBranch
+ def self.value(object: nil)
+ Feature.enabled?(:main_branch_over_master, object, default_enabled: :yaml) ? 'main' : 'master'
+ end
+ end
+end
diff --git a/lib/gitlab/diff/file_collection/base.rb b/lib/gitlab/diff/file_collection/base.rb
index 627abfbfe7e..9ed03c05f0b 100644
--- a/lib/gitlab/diff/file_collection/base.rb
+++ b/lib/gitlab/diff/file_collection/base.rb
@@ -117,8 +117,6 @@ module Gitlab
end
def sort_diffs(diffs)
- return diffs unless Feature.enabled?(:sort_diffs, project, default_enabled: :yaml)
-
Gitlab::Diff::FileCollectionSorter.new(diffs).sort
end
end
diff --git a/lib/gitlab/diff/highlight.rb b/lib/gitlab/diff/highlight.rb
index 8385bbbb3de..6a41ed0f29e 100644
--- a/lib/gitlab/diff/highlight.rb
+++ b/lib/gitlab/diff/highlight.rb
@@ -3,6 +3,8 @@
module Gitlab
module Diff
class Highlight
+ PREFIX_REGEXP = /\A(.)/.freeze
+
attr_reader :diff_file, :diff_lines, :repository, :project
delegate :old_path, :new_path, :old_sha, :new_sha, to: :diff_file, prefix: :diff
@@ -85,6 +87,7 @@ module Gitlab
def highlight_line(diff_line)
return unless diff_file && diff_file.diff_refs
+ return diff_line_highlighting(diff_line, plain: true) if blobs_too_large?
if Feature.enabled?(:diff_line_syntax_highlighting, project, default_enabled: :yaml)
diff_line_highlighting(diff_line)
@@ -93,16 +96,17 @@ module Gitlab
end
end
- def diff_line_highlighting(diff_line)
+ def diff_line_highlighting(diff_line, plain: false)
rich_line = syntax_highlighter(diff_line).highlight(
diff_line.text(prefix: false),
+ plain: plain,
context: { line_number: diff_line.line }
- )&.html_safe
+ )
# Only update text if line is found. This will prevent
# issues with submodules given the line only exists in diff content.
if rich_line
- line_prefix = diff_line.text =~ /\A(.)/ ? Regexp.last_match(1) : ' '
+ line_prefix = diff_line.text =~ PREFIX_REGEXP ? Regexp.last_match(1) : ' '
rich_line.prepend(line_prefix).concat("\n")
end
end
@@ -131,7 +135,7 @@ module Gitlab
# Only update text if line is found. This will prevent
# issues with submodules given the line only exists in diff content.
if rich_line
- line_prefix = diff_line.text =~ /\A(.)/ ? Regexp.last_match(1) : ' '
+ line_prefix = diff_line.text =~ PREFIX_REGEXP ? Regexp.last_match(1) : ' '
"#{line_prefix}#{rich_line}".html_safe
end
end
@@ -156,6 +160,13 @@ module Gitlab
blob.load_all_data!
blob.present.highlight.lines
end
+
+ def blobs_too_large?
+ return false unless Feature.enabled?(:limited_diff_highlighting, project, default_enabled: :yaml)
+ return true if Gitlab::Highlight.too_large?(diff_file.old_blob&.size)
+
+ Gitlab::Highlight.too_large?(diff_file.new_blob&.size)
+ end
end
end
end
diff --git a/lib/gitlab/doctor/secrets.rb b/lib/gitlab/doctor/secrets.rb
index 31c5dded3ff..1a1e9fafb1e 100644
--- a/lib/gitlab/doctor/secrets.rb
+++ b/lib/gitlab/doctor/secrets.rb
@@ -77,7 +77,7 @@ module Gitlab
true
rescue OpenSSL::Cipher::CipherError, TypeError
false
- rescue => e
+ rescue StandardError => e
logger.debug "> Something went wrong for #{data.class.name}[#{data.id}].#{attr}: #{e}".color(:red)
false
diff --git a/lib/gitlab/email/handler/create_issue_handler.rb b/lib/gitlab/email/handler/create_issue_handler.rb
index 22fc8addcd9..e927a5641e5 100644
--- a/lib/gitlab/email/handler/create_issue_handler.rb
+++ b/lib/gitlab/email/handler/create_issue_handler.rb
@@ -56,10 +56,12 @@ module Gitlab
def create_issue
Issues::CreateService.new(
- project,
- author,
- title: mail.subject,
- description: message_including_reply
+ project: project,
+ current_user: author,
+ params: {
+ title: mail.subject,
+ description: message_including_reply
+ }
).execute
end
diff --git a/lib/gitlab/email/handler/create_merge_request_handler.rb b/lib/gitlab/email/handler/create_merge_request_handler.rb
index e8071bcafd0..df12aea1988 100644
--- a/lib/gitlab/email/handler/create_merge_request_handler.rb
+++ b/lib/gitlab/email/handler/create_merge_request_handler.rb
@@ -61,7 +61,7 @@ module Gitlab
private
def build_merge_request
- MergeRequests::BuildService.new(project, author, merge_request_params).execute
+ MergeRequests::BuildService.new(project: project, current_user: author, params: merge_request_params).execute
end
def create_merge_request
@@ -78,7 +78,7 @@ module Gitlab
if merge_request.errors.any?
merge_request
else
- MergeRequests::CreateService.new(project, author).create(merge_request)
+ MergeRequests::CreateService.new(project: project, current_user: author).create(merge_request)
end
end
diff --git a/lib/gitlab/email/handler/reply_processing.rb b/lib/gitlab/email/handler/reply_processing.rb
index 9e476dd4e2b..63334169c8e 100644
--- a/lib/gitlab/email/handler/reply_processing.rb
+++ b/lib/gitlab/email/handler/reply_processing.rb
@@ -100,4 +100,4 @@ module Gitlab
end
end
-Gitlab::Email::Handler::ReplyProcessing.prepend_if_ee('::EE::Gitlab::Email::Handler::ReplyProcessing')
+Gitlab::Email::Handler::ReplyProcessing.prepend_mod_with('Gitlab::Email::Handler::ReplyProcessing')
diff --git a/lib/gitlab/email/handler/service_desk_handler.rb b/lib/gitlab/email/handler/service_desk_handler.rb
index 80e8b726099..cab3538a447 100644
--- a/lib/gitlab/email/handler/service_desk_handler.rb
+++ b/lib/gitlab/email/handler/service_desk_handler.rb
@@ -38,7 +38,7 @@ module Gitlab
if from_address
add_email_participant
- send_thank_you_email!
+ send_thank_you_email
end
end
@@ -77,12 +77,14 @@ module Gitlab
def create_issue!
@issue = Issues::CreateService.new(
- project,
- User.support_bot,
- title: mail.subject,
- description: message_including_template,
- confidential: true,
- external_author: from_address
+ project: project,
+ current_user: User.support_bot,
+ params: {
+ title: mail.subject,
+ description: message_including_template,
+ confidential: true,
+ external_author: from_address
+ }
).execute
raise InvalidIssueError unless @issue.persisted?
@@ -92,8 +94,8 @@ module Gitlab
end
end
- def send_thank_you_email!
- Notify.service_desk_thank_you_email(@issue.id).deliver_later!
+ def send_thank_you_email
+ Notify.service_desk_thank_you_email(@issue.id).deliver_later
end
def message_including_template
diff --git a/lib/gitlab/email/message/in_product_marketing.rb b/lib/gitlab/email/message/in_product_marketing.rb
new file mode 100644
index 00000000000..d538238f26f
--- /dev/null
+++ b/lib/gitlab/email/message/in_product_marketing.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Email
+ module Message
+ module InProductMarketing
+ UnknownTrackError = Class.new(StandardError)
+
+ TRACKS = [:create, :verify, :team, :trial].freeze
+
+ def self.for(track)
+ raise UnknownTrackError unless TRACKS.include?(track)
+
+ "Gitlab::Email::Message::InProductMarketing::#{track.to_s.classify}".constantize
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/email/message/in_product_marketing/base.rb b/lib/gitlab/email/message/in_product_marketing/base.rb
new file mode 100644
index 00000000000..6341a7c7596
--- /dev/null
+++ b/lib/gitlab/email/message/in_product_marketing/base.rb
@@ -0,0 +1,154 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Email
+ module Message
+ module InProductMarketing
+ class Base
+ include Gitlab::Email::Message::InProductMarketing::Helper
+ include Gitlab::Routing
+
+ attr_accessor :format
+
+ def initialize(group:, series:, format: :html)
+ raise ArgumentError, "Only #{total_series} series available for this track." unless series.between?(0, total_series - 1)
+
+ @group = group
+ @series = series
+ @format = format
+ end
+
+ def subject_line
+ raise NotImplementedError
+ end
+
+ def tagline
+ raise NotImplementedError
+ end
+
+ def title
+ raise NotImplementedError
+ end
+
+ def subtitle
+ raise NotImplementedError
+ end
+
+ def body_line1
+ raise NotImplementedError
+ end
+
+ def body_line2
+ raise NotImplementedError
+ end
+
+ def cta_text
+ raise NotImplementedError
+ end
+
+ def cta_link
+ case format
+ when :html
+ link_to cta_text, group_email_campaigns_url(group, track: track, series: series), target: '_blank', rel: 'noopener noreferrer'
+ else
+ [cta_text, group_email_campaigns_url(group, track: track, series: series)].join(' >> ')
+ end
+ end
+
+ def unsubscribe
+ parts = Gitlab.com? ? unsubscribe_com : unsubscribe_self_managed(track, series)
+
+ case format
+ when :html
+ parts.join(' ')
+ else
+ parts.join("\n" + ' ' * 16)
+ end
+ end
+
+ def progress
+ if Gitlab.com?
+ s_('InProductMarketing|This is email %{current_series} of %{total_series} in the %{track} series.') % { current_series: series + 1, total_series: total_series, track: track.to_s.humanize }
+ else
+ s_('InProductMarketing|This is email %{current_series} of %{total_series} in the %{track} series. To disable notification emails sent by your local GitLab instance, either contact your administrator or %{unsubscribe_link}.') % { current_series: series + 1, total_series: total_series, track: track.to_s.humanize, unsubscribe_link: unsubscribe_link }
+ end
+ end
+
+ def address
+ s_('InProductMarketing|%{strong_start}GitLab Inc.%{strong_end} 268 Bush Street, #350, San Francisco, CA 94104, USA').html_safe % strong_options
+ end
+
+ def footer_links
+ links = [
+ [s_('InProductMarketing|Blog'), 'https://about.gitlab.com/blog'],
+ [s_('InProductMarketing|Twitter'), 'https://twitter.com/gitlab'],
+ [s_('InProductMarketing|Facebook'), 'https://www.facebook.com/gitlab'],
+ [s_('InProductMarketing|YouTube'), 'https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg']
+ ]
+ case format
+ when :html
+ links.map do |text, link|
+ link_to(text, link)
+ end
+ else
+ '| ' + links.map do |text, link|
+ [text, link].join(' ')
+ end.join("\n| ")
+ end
+ end
+
+ def logo_path
+ ["mailers/in_product_marketing", "#{track}-#{series}.png"].join('/')
+ end
+
+ protected
+
+ attr_reader :group, :series
+
+ def total_series
+ 3
+ end
+
+ private
+
+ def track
+ self.class.name.demodulize.downcase.to_sym
+ end
+
+ def unsubscribe_com
+ [
+ s_('InProductMarketing|If you no longer wish to receive marketing emails from us,'),
+ s_('InProductMarketing|you may %{unsubscribe_link} at any time.') % { unsubscribe_link: unsubscribe_link }
+ ]
+ end
+
+ def unsubscribe_self_managed(track, series)
+ [
+ s_('InProductMarketing|To opt out of these onboarding emails, %{unsubscribe_link}.') % { unsubscribe_link: unsubscribe_link },
+ s_("InProductMarketing|If you don't want to receive marketing emails directly from GitLab, %{marketing_preference_link}.") % { marketing_preference_link: marketing_preference_link(track, series) }
+ ]
+ end
+
+ def unsubscribe_link
+ unsubscribe_url = Gitlab.com? ? '%tag_unsubscribe_url%' : profile_notifications_url
+
+ link(s_('InProductMarketing|unsubscribe'), unsubscribe_url)
+ end
+
+ def marketing_preference_link(track, series)
+ params = {
+ utm_source: 'SM',
+ utm_medium: 'email',
+ utm_campaign: 'onboarding',
+ utm_term: "#{track}_#{series}"
+ }
+
+ preference_link = "https://about.gitlab.com/company/preference-center/?#{params.to_query}"
+
+ link(s_('InProductMarketing|update your preferences'), preference_link)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/email/message/in_product_marketing/create.rb b/lib/gitlab/email/message/in_product_marketing/create.rb
new file mode 100644
index 00000000000..5d3cac0a121
--- /dev/null
+++ b/lib/gitlab/email/message/in_product_marketing/create.rb
@@ -0,0 +1,101 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Email
+ module Message
+ module InProductMarketing
+ class Create < Base
+ def subject_line
+ [
+ s_('InProductMarketing|Create a project in GitLab in 5 minutes'),
+ s_('InProductMarketing|Import your project and code from GitHub, Bitbucket and others'),
+ s_('InProductMarketing|Understand repository mirroring')
+ ][series]
+ end
+
+ def tagline
+ [
+ s_('InProductMarketing|Get started today'),
+ s_('InProductMarketing|Get our import guides'),
+ s_('InProductMarketing|Need an alternative to importing?')
+ ][series]
+ end
+
+ def title
+ [
+ s_('InProductMarketing|Take your first steps with GitLab'),
+ s_('InProductMarketing|Start by importing your projects'),
+ s_('InProductMarketing|How (and why) mirroring makes sense')
+ ][series]
+ end
+
+ def subtitle
+ [
+ s_('InProductMarketing|Dig in and create a project and a repo'),
+ s_("InProductMarketing|Here's what you need to know"),
+ s_('InProductMarketing|Try it out')
+ ][series]
+ end
+
+ def body_line1
+ [
+ s_("InProductMarketing|To understand and get the most out of GitLab, start at the beginning and %{project_link}. In GitLab, repositories are part of a project, so after you've created your project you can go ahead and %{repo_link}.") % { project_link: project_link, repo_link: repo_link },
+ s_("InProductMarketing|Making the switch? It's easier than you think to import your projects into GitLab. Move %{github_link}, or import something %{bitbucket_link}.") % { github_link: github_link, bitbucket_link: bitbucket_link },
+ s_("InProductMarketing|Sometimes you're not ready to make a full transition to a new tool. If you're not ready to fully commit, %{mirroring_link} gives you a safe way to try out GitLab in parallel with your current tool.") % { mirroring_link: mirroring_link }
+ ][series]
+ end
+
+ def body_line2
+ [
+ s_("InProductMarketing|That's all it takes to get going with GitLab, but if you're new to working with Git, check out our %{basics_link} for helpful tips and tricks for getting started.") % { basics_link: basics_link },
+ s_("InProductMarketing|Have a different instance you'd like to import? Here's our %{import_link}.") % { import_link: import_link },
+ s_("InProductMarketing|It's also possible to simply %{external_repo_link} in order to take advantage of GitLab's CI/CD.") % { external_repo_link: external_repo_link }
+ ][series]
+ end
+
+ def cta_text
+ [
+ s_('InProductMarketing|Create your first project!'),
+ s_('InProductMarketing|Master the art of importing!'),
+ s_('InProductMarketing|Understand your project options')
+ ][series]
+ end
+
+ private
+
+ def project_link
+ link(s_('InProductMarketing|create a project'), help_page_url('gitlab-basics/create-project'))
+ end
+
+ def repo_link
+ link(s_('InProductMarketing|set up a repo'), help_page_url('user/project/repository/index', anchor: 'create-a-repository'))
+ end
+
+ def github_link
+ link(s_('InProductMarketing|GitHub Enterprise projects to GitLab'), help_page_url('integration/github'))
+ end
+
+ def bitbucket_link
+ link(s_('InProductMarketing|from Bitbucket'), help_page_url('user/project/import/bitbucket_server'))
+ end
+
+ def mirroring_link
+ link(s_('InProductMarketing|repository mirroring'), help_page_url('user/project/repository/repository_mirroring'))
+ end
+
+ def basics_link
+ link(s_('InProductMarketing|Git basics'), help_page_url('gitlab-basics/README'))
+ end
+
+ def import_link
+ link(s_('InProductMarketing|comprehensive guide'), help_page_url('user/project/import/index'))
+ end
+
+ def external_repo_link
+ link(s_('InProductMarketing|connect an external repository'), new_project_url(anchor: 'cicd_for_external_repo'))
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/email/message/in_product_marketing/helper.rb b/lib/gitlab/email/message/in_product_marketing/helper.rb
new file mode 100644
index 00000000000..4780e08322a
--- /dev/null
+++ b/lib/gitlab/email/message/in_product_marketing/helper.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Email
+ module Message
+ module InProductMarketing
+ module Helper
+ include ActionView::Context
+ include ActionView::Helpers::TagHelper
+ include ActionView::Helpers::UrlHelper
+
+ private
+
+ def list(array)
+ case format
+ when :html
+ tag.ul { array.map { |item| tag.li item} }
+ else
+ '- ' + array.join("\n- ")
+ end
+ end
+
+ def strong_options
+ case format
+ when :html
+ { strong_start: '<b>'.html_safe, strong_end: '</b>'.html_safe }
+ else
+ { strong_start: '', strong_end: '' }
+ end
+ end
+
+ def link(text, link)
+ case format
+ when :html
+ link_to text, link
+ else
+ "#{text} (#{link})"
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/email/message/in_product_marketing/team.rb b/lib/gitlab/email/message/in_product_marketing/team.rb
new file mode 100644
index 00000000000..46c2797e534
--- /dev/null
+++ b/lib/gitlab/email/message/in_product_marketing/team.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Email
+ module Message
+ module InProductMarketing
+ class Team < Base
+ def subject_line
+ [
+ s_('InProductMarketing|Working in GitLab = more efficient'),
+ s_("InProductMarketing|Multiple owners, confusing workstreams? We've got you covered"),
+ s_('InProductMarketing|Your teams can be more efficient')
+ ][series]
+ end
+
+ def tagline
+ [
+ s_('InProductMarketing|Invite your colleagues to join in less than one minute'),
+ s_('InProductMarketing|Get your team set up on GitLab'),
+ nil
+ ][series]
+ end
+
+ def title
+ [
+ s_('InProductMarketing|Team work makes the dream work'),
+ s_('InProductMarketing|*GitLab*, noun: a synonym for efficient teams'),
+ s_('InProductMarketing|Find out how your teams are really doing')
+ ][series]
+ end
+
+ def subtitle
+ [
+ s_('InProductMarketing|Actually, GitLab makes the team work (better)'),
+ s_('InProductMarketing|Our tool brings all the things together'),
+ s_("InProductMarketing|It's all in the stats")
+ ][series]
+ end
+
+ def body_line1
+ [
+ [
+ s_('InProductMarketing|Did you know teams that use GitLab are far more efficient?'),
+ list([
+ s_('InProductMarketing|Goldman Sachs went from 1 build every two weeks to thousands of builds a day'),
+ s_('InProductMarketing|Ticketmaster decreased their CI build time by 15X')
+ ])
+ ].join("\n"),
+ s_("InProductMarketing|We know a thing or two about efficiency and we don't want to keep that to ourselves. Sign up for a free trial of GitLab Ultimate and your teams will be on it from day one."),
+ [
+ s_('InProductMarketing|Stop wondering and use GitLab to answer questions like:'),
+ list([
+ s_('InProductMarketing|How long does it take us to close issues/MRs by types like feature requests, bugs, tech debt, security?'),
+ s_('InProductMarketing|How many days does it take our team to complete various tasks?'),
+ s_('InProductMarketing|What does our value stream timeline look like from product to development to review and production?')
+ ])
+ ].join("\n")
+ ][series]
+ end
+
+ def body_line2
+ [
+ s_('InProductMarketing|Invite your colleagues and start shipping code faster.'),
+ s_("InProductMarketing|Streamline code review, know at a glance who's unavailable, communicate in comments or in email and integrate with Slack so everyone's on the same page."),
+ s_('InProductMarketing|When your team is on GitLab these answers are a click away.')
+ ][series]
+ end
+
+ def cta_text
+ [
+ s_('InProductMarketing|Invite your colleagues today'),
+ s_('InProductMarketing|Invite your team in less than 60 seconds'),
+ s_('InProductMarketing|Invite your team now')
+ ][series]
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/email/message/in_product_marketing/trial.rb b/lib/gitlab/email/message/in_product_marketing/trial.rb
new file mode 100644
index 00000000000..d87dc5c1b81
--- /dev/null
+++ b/lib/gitlab/email/message/in_product_marketing/trial.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Email
+ module Message
+ module InProductMarketing
+ class Trial < Base
+ def subject_line
+ [
+ s_('InProductMarketing|Go farther with GitLab'),
+ s_('InProductMarketing|Automated security scans directly within GitLab'),
+ s_('InProductMarketing|Take your source code management to the next level')
+ ][series]
+ end
+
+ def tagline
+ [
+ s_('InProductMarketing|Start a free trial of GitLab Ultimate – no CC required'),
+ s_('InProductMarketing|Improve app security with a 30-day trial'),
+ s_('InProductMarketing|Start with a GitLab Ultimate free trial')
+ ][series]
+ end
+
+ def title
+ [
+ s_('InProductMarketing|Give us one minute...'),
+ s_("InProductMarketing|Security that's integrated into your development lifecycle"),
+ s_('InProductMarketing|Improve code quality and streamline reviews')
+ ][series]
+ end
+
+ def subtitle
+ [
+ s_('InProductMarketing|...and you can get a free trial of GitLab Ultimate'),
+ s_('InProductMarketing|Try GitLab Ultimate for free'),
+ s_('InProductMarketing|Better code in less time')
+ ][series]
+ end
+
+ def body_line1
+ [
+ [
+ s_("InProductMarketing|GitLab's premium tiers are designed to make you, your team and your application more efficient and more secure with features including but not limited to:"),
+ list([
+ s_('InProductMarketing|%{strong_start}Company wide portfolio management%{strong_end} — including multi-level epics, scoped labels').html_safe % strong_options,
+ s_('InProductMarketing|%{strong_start}Multiple approval roles%{strong_end} — including code owners and required merge approvals').html_safe % strong_options,
+ s_('InProductMarketing|%{strong_start}Advanced application security%{strong_end} — including SAST, DAST scanning, FUZZ testing, dependency scanning, license compliance, secrete detection').html_safe % strong_options,
+ s_('InProductMarketing|%{strong_start}Executive level insights%{strong_end} — including reporting on productivity, tasks by type, days to completion, value stream').html_safe % strong_options
+ ])
+ ].join("\n"),
+ s_('InProductMarketing|GitLab provides static application security testing (SAST), dynamic application security testing (DAST), container scanning, and dependency scanning to help you deliver secure applications along with license compliance.'),
+ s_('InProductMarketing|By enabling code owners and required merge approvals the right person will review the right MR. This is a win-win: cleaner code and a more efficient review process.')
+ ][series]
+ end
+
+ def body_line2
+ [
+ s_('InProductMarketing|Start a GitLab Ultimate trial today in less than one minute, no credit card required.'),
+ s_('InProductMarketing|Get started today with a 30-day GitLab Ultimate trial, no credit card required.'),
+ s_('InProductMarketing|Code owners and required merge approvals are part of the paid tiers of GitLab. You can start a free 30-day trial of GitLab Ultimate and enable these features in less than 5 minutes with no credit card required.')
+ ][series]
+ end
+
+ def cta_text
+ [
+ s_('InProductMarketing|Start a trial'),
+ s_('InProductMarketing|Beef up your security'),
+ s_('InProductMarketing|Start your trial now!')
+ ][series]
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/email/message/in_product_marketing/verify.rb b/lib/gitlab/email/message/in_product_marketing/verify.rb
new file mode 100644
index 00000000000..d563de6c77e
--- /dev/null
+++ b/lib/gitlab/email/message/in_product_marketing/verify.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Email
+ module Message
+ module InProductMarketing
+ class Verify < Base
+ def subject_line
+ [
+ s_('InProductMarketing|Feel the need for speed?'),
+ s_('InProductMarketing|3 ways to dive into GitLab CI/CD'),
+ s_('InProductMarketing|Explore the power of GitLab CI/CD')
+ ][series]
+ end
+
+ def tagline
+ [
+ s_('InProductMarketing|Use GitLab CI/CD'),
+ s_('InProductMarketing|Test, create, deploy'),
+ s_('InProductMarketing|Are your runners ready?')
+ ][series]
+ end
+
+ def title
+ [
+ s_('InProductMarketing|Rapid development, simplified'),
+ s_('InProductMarketing|Get started with GitLab CI/CD'),
+ s_('InProductMarketing|Launch GitLab CI/CD in 20 minutes or less')
+ ][series]
+ end
+
+ def subtitle
+ [
+ s_('InProductMarketing|How to build and test faster'),
+ s_('InProductMarketing|Explore the options'),
+ s_('InProductMarketing|Follow our steps')
+ ][series]
+ end
+
+ def body_line1
+ [
+ s_("InProductMarketing|Tired of wrestling with disparate tool chains, information silos and inefficient processes? GitLab's CI/CD is built on a DevOps platform with source code management, planning, monitoring and more ready to go. Find out %{ci_link}.") % { ci_link: ci_link },
+ s_("InProductMarketing|GitLab's CI/CD makes software development easier. Don't believe us? Here are three ways you can take it for a fast (and satisfying) test drive:"),
+ s_("InProductMarketing|Get going with CI/CD quickly using our %{quick_start_link}. Start with an available runner and then create a CI .yml file – it's really that easy.") % { quick_start_link: quick_start_link }
+ ][series]
+ end
+
+ def body_line2
+ [
+ nil,
+ list([
+ s_('InProductMarketing|Start by %{performance_link}').html_safe % { performance_link: performance_link },
+ s_('InProductMarketing|Move on to easily creating a Pages website %{ci_template_link}').html_safe % { ci_template_link: ci_template_link },
+ s_('InProductMarketing|And finally %{deploy_link} a Python application.').html_safe % { deploy_link: deploy_link }
+ ]),
+ nil
+ ][series]
+ end
+
+ def cta_text
+ [
+ s_('InProductMarketing|Get to know GitLab CI/CD'),
+ s_('InProductMarketing|Try it yourself'),
+ s_('InProductMarketing|Explore GitLab CI/CD')
+ ][series]
+ end
+
+ private
+
+ def ci_link
+ link(s_('InProductMarketing|how easy it is to get started'), help_page_url('ci/README'))
+ end
+
+ def quick_start_link
+ link(s_('InProductMarketing|quick start guide'), help_page_url('ci/quick_start/README'))
+ end
+
+ def performance_link
+ link(s_('InProductMarketing|testing browser performance'), help_page_url('user/project/merge_requests/browser_performance_testing'))
+ end
+
+ def ci_template_link
+ link(s_('InProductMarketing|using a CI/CD template'), help_page_url('user/project/pages/getting_started/pages_ci_cd_template'))
+ end
+
+ def deploy_link
+ link(s_('InProductMarketing|test and deploy'), help_page_url('ci/examples/test-and-deploy-python-application-to-heroku'))
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/email/receiver.rb b/lib/gitlab/email/receiver.rb
index f5e47b43a9a..71db8ab6067 100644
--- a/lib/gitlab/email/receiver.rb
+++ b/lib/gitlab/email/receiver.rb
@@ -6,6 +6,8 @@ require_dependency 'gitlab/email/handler'
module Gitlab
module Email
class Receiver
+ include Gitlab::Utils::StrongMemoize
+
def initialize(raw)
@raw = raw
end
@@ -13,11 +15,7 @@ module Gitlab
def execute
raise EmptyEmailError if @raw.blank?
- mail = build_mail
-
- ignore_auto_reply!(mail)
-
- handler = find_handler(mail)
+ ignore_auto_reply!
raise UnknownIncomingEmail unless handler
@@ -26,13 +24,33 @@ module Gitlab
end
end
+ def mail_metadata
+ {
+ mail_uid: mail.message_id,
+ from_address: mail.from,
+ to_address: mail.to,
+ mail_key: mail_key,
+ references: Array(mail.references),
+ delivered_to: delivered_to.map(&:value),
+ envelope_to: envelope_to.map(&:value),
+ x_envelope_to: x_envelope_to.map(&:value)
+ }
+ end
+
private
- def find_handler(mail)
- mail_key = extract_mail_key(mail)
+ def handler
+ strong_memoize(:handler) { find_handler }
+ end
+
+ def find_handler
Handler.for(mail, mail_key)
end
+ def mail
+ strong_memoize(:mail) { build_mail }
+ end
+
def build_mail
Mail::Message.new(@raw)
rescue Encoding::UndefinedConversionError,
@@ -40,22 +58,24 @@ module Gitlab
raise EmailUnparsableError, e
end
- def extract_mail_key(mail)
- key_from_to_header(mail) || key_from_additional_headers(mail)
+ def mail_key
+ strong_memoize(:mail_key) do
+ key_from_to_header || key_from_additional_headers
+ end
end
- def key_from_to_header(mail)
+ def key_from_to_header
mail.to.find do |address|
key = Gitlab::IncomingEmail.key_from_address(address)
break key if key
end
end
- def key_from_additional_headers(mail)
- find_key_from_references(mail) ||
- find_key_from_delivered_to_header(mail) ||
- find_key_from_envelope_to_header(mail) ||
- find_key_from_x_envelope_to_header(mail)
+ def key_from_additional_headers
+ find_key_from_references ||
+ find_key_from_delivered_to_header ||
+ find_key_from_envelope_to_header ||
+ find_key_from_x_envelope_to_header
end
def ensure_references_array(references)
@@ -71,41 +91,53 @@ module Gitlab
end
end
- def find_key_from_references(mail)
+ def find_key_from_references
ensure_references_array(mail.references).find do |mail_id|
key = Gitlab::IncomingEmail.key_from_fallback_message_id(mail_id)
break key if key
end
end
- def find_key_from_delivered_to_header(mail)
- Array(mail[:delivered_to]).find do |header|
+ def delivered_to
+ Array(mail[:delivered_to])
+ end
+
+ def envelope_to
+ Array(mail[:envelope_to])
+ end
+
+ def x_envelope_to
+ Array(mail[:x_envelope_to])
+ end
+
+ def find_key_from_delivered_to_header
+ delivered_to.find do |header|
key = Gitlab::IncomingEmail.key_from_address(header.value)
break key if key
end
end
- def find_key_from_envelope_to_header(mail)
- Array(mail[:envelope_to]).find do |header|
+ def find_key_from_envelope_to_header
+ envelope_to.find do |header|
key = Gitlab::IncomingEmail.key_from_address(header.value)
break key if key
end
end
- def find_key_from_x_envelope_to_header(mail)
- Array(mail[:x_envelope_to]).find do |header|
+ def find_key_from_x_envelope_to_header
+ x_envelope_to.find do |header|
key = Gitlab::IncomingEmail.key_from_address(header.value)
break key if key
end
end
- def ignore_auto_reply!(mail)
- if auto_submitted?(mail) || auto_replied?(mail)
+ def ignore_auto_reply!
+ if auto_submitted? || auto_replied?
raise AutoGeneratedEmailError
end
end
- def auto_submitted?(mail)
+ def auto_submitted?
# Mail::Header#[] is case-insensitive
auto_submitted = mail.header['Auto-Submitted']&.value
@@ -114,7 +146,7 @@ module Gitlab
auto_submitted && auto_submitted != 'no'
end
- def auto_replied?(mail)
+ def auto_replied?
autoreply = mail.header['X-Autoreply']&.value
autoreply && autoreply == 'yes'
diff --git a/lib/gitlab/email/reply_parser.rb b/lib/gitlab/email/reply_parser.rb
index dc44e9d7481..7579f3d8680 100644
--- a/lib/gitlab/email/reply_parser.rb
+++ b/lib/gitlab/email/reply_parser.rb
@@ -68,7 +68,7 @@ module Gitlab
else
object.body.to_s
end
- rescue
+ rescue StandardError
nil
end
end
diff --git a/lib/gitlab/email/service_desk_receiver.rb b/lib/gitlab/email/service_desk_receiver.rb
index 1ee5c10097b..133c4ee4b45 100644
--- a/lib/gitlab/email/service_desk_receiver.rb
+++ b/lib/gitlab/email/service_desk_receiver.rb
@@ -5,14 +5,19 @@ module Gitlab
class ServiceDeskReceiver < Receiver
private
- def find_handler(mail)
- key = service_desk_key(mail)
- return unless key
+ def find_handler
+ return unless service_desk_key
- Gitlab::Email::Handler::ServiceDeskHandler.new(mail, nil, service_desk_key: key)
+ Gitlab::Email::Handler::ServiceDeskHandler.new(mail, nil, service_desk_key: service_desk_key)
end
- def service_desk_key(mail)
+ def service_desk_key
+ strong_memoize(:service_desk_key) do
+ find_service_desk_key
+ end
+ end
+
+ def find_service_desk_key
mail.to.find do |address|
key = ::Gitlab::ServiceDeskEmail.key_from_address(address)
break key if key
diff --git a/lib/gitlab/encoding_helper.rb b/lib/gitlab/encoding_helper.rb
index 7b79de00c66..8ee53d0de28 100644
--- a/lib/gitlab/encoding_helper.rb
+++ b/lib/gitlab/encoding_helper.rb
@@ -20,7 +20,7 @@ module Gitlab
return message if message.valid_encoding?
# return message if message type is binary
- detect = CharlockHolmes::EncodingDetector.detect(message)
+ detect = detect_encoding(message)
return message.force_encoding("BINARY") if detect_binary?(message, detect)
if detect && detect[:encoding] && detect[:confidence] > ENCODING_CONFIDENCE_THRESHOLD
@@ -37,16 +37,30 @@ module Gitlab
"--broken encoding: #{encoding}"
end
+ def detect_encoding(data, limit: CharlockHolmes::EncodingDetector::DEFAULT_BINARY_SCAN_LEN, cache_key: nil)
+ return if data.nil?
+
+ if Feature.enabled?(:cached_encoding_detection, type: :development, default_enabled: :yaml)
+ return CharlockHolmes::EncodingDetector.new(limit).detect(data) unless cache_key.present?
+
+ Rails.cache.fetch([:detect_binary, CharlockHolmes::VERSION, cache_key], expires_in: 1.week) do
+ CharlockHolmes::EncodingDetector.new(limit).detect(data)
+ end
+ else
+ CharlockHolmes::EncodingDetector.new(limit).detect(data)
+ end
+ end
+
def detect_binary?(data, detect = nil)
- detect ||= CharlockHolmes::EncodingDetector.detect(data)
+ detect ||= detect_encoding(data)
detect && detect[:type] == :binary && detect[:confidence] == 100
end
- def detect_libgit2_binary?(data)
- # EncodingDetector checks the first 1024 * 1024 bytes for NUL byte, libgit2 checks
- # only the first 8000 (https://github.com/libgit2/libgit2/blob/2ed855a9e8f9af211e7274021c2264e600c0f86b/src/filter.h#L15),
- # which is what we use below to keep a consistent behavior.
- detect = CharlockHolmes::EncodingDetector.new(8000).detect(data)
+ # EncodingDetector checks the first 1024 * 1024 bytes for NUL byte, libgit2 checks
+ # only the first 8000 (https://github.com/libgit2/libgit2/blob/2ed855a9e8f9af211e7274021c2264e600c0f86b/src/filter.h#L15),
+ # which is what we use below to keep a consistent behavior.
+ def detect_libgit2_binary?(data, cache_key: nil)
+ detect = detect_encoding(data, limit: 8000, cache_key: cache_key)
detect && detect[:type] == :binary
end
@@ -54,7 +68,8 @@ module Gitlab
message = force_encode_utf8(message)
return message if message.valid_encoding?
- detect = CharlockHolmes::EncodingDetector.detect(message)
+ detect = detect_encoding(message)
+
if detect && detect[:encoding]
begin
CharlockHolmes::Converter.convert(message, detect[:encoding], 'UTF-8')
diff --git a/lib/gitlab/encrypted_configuration.rb b/lib/gitlab/encrypted_configuration.rb
index fe49af3ab33..6b64281e631 100644
--- a/lib/gitlab/encrypted_configuration.rb
+++ b/lib/gitlab/encrypted_configuration.rb
@@ -65,7 +65,7 @@ module Gitlab
contents = deserialize(read)
- raise InvalidConfigError.new unless contents.is_a?(Hash)
+ raise InvalidConfigError unless contents.is_a?(Hash)
@config = contents.deep_symbolize_keys
end
@@ -115,7 +115,7 @@ module Gitlab
end
def handle_missing_key!
- raise MissingKeyError.new if @key.nil?
+ raise MissingKeyError if @key.nil?
end
end
end
diff --git a/lib/gitlab/error_tracking.rb b/lib/gitlab/error_tracking.rb
index 47d361fb95c..38ac5d9af74 100644
--- a/lib/gitlab/error_tracking.rb
+++ b/lib/gitlab/error_tracking.rb
@@ -31,9 +31,6 @@ module Gitlab
# Sanitize fields based on those sanitized from Rails.
config.sanitize_fields = Rails.application.config.filter_parameters.map(&:to_s)
- config.processors << ::Gitlab::ErrorTracking::Processor::SidekiqProcessor
- config.processors << ::Gitlab::ErrorTracking::Processor::GrpcErrorProcessor
- config.processors << ::Gitlab::ErrorTracking::Processor::ContextPayloadProcessor
# Sanitize authentication headers
config.sanitize_http_headers = %w[Authorization Private-Token]
diff --git a/lib/gitlab/error_tracking/context_payload_generator.rb b/lib/gitlab/error_tracking/context_payload_generator.rb
index c99283b3d20..3d0a707608f 100644
--- a/lib/gitlab/error_tracking/context_payload_generator.rb
+++ b/lib/gitlab/error_tracking/context_payload_generator.rb
@@ -49,7 +49,7 @@ module Gitlab
# Static tags that are set on application start
def extra_tags_from_env
Gitlab::Json.parse(ENV.fetch('GITLAB_SENTRY_EXTRA_TAGS', '{}')).to_hash
- rescue => e
+ rescue StandardError => e
Gitlab::AppLogger.debug("GITLAB_SENTRY_EXTRA_TAGS could not be parsed as JSON: #{e.class.name}: #{e.message}")
{}
diff --git a/lib/gitlab/error_tracking/processor/context_payload_processor.rb b/lib/gitlab/error_tracking/processor/context_payload_processor.rb
index 758f6aa11d7..9559d6807da 100644
--- a/lib/gitlab/error_tracking/processor/context_payload_processor.rb
+++ b/lib/gitlab/error_tracking/processor/context_payload_processor.rb
@@ -3,21 +3,12 @@
module Gitlab
module ErrorTracking
module Processor
- class ContextPayloadProcessor < ::Raven::Processor
+ module ContextPayloadProcessor
# This processor is added to inject application context into Sentry
# events generated by Sentry built-in integrations. When the
# integrations are re-implemented and use Gitlab::ErrorTracking, this
# processor should be removed.
- def process(payload)
- return payload if ::Feature.enabled?(:sentry_processors_before_send, default_enabled: :yaml)
-
- context_payload = Gitlab::ErrorTracking::ContextPayloadGenerator.generate(nil, {})
- payload.deep_merge!(context_payload)
- end
-
def self.call(event)
- return event unless ::Feature.enabled?(:sentry_processors_before_send, default_enabled: :yaml)
-
Gitlab::ErrorTracking::ContextPayloadGenerator.generate(nil, {}).each do |key, value|
event.public_send(key).deep_merge!(value) # rubocop:disable GitlabSecurity/PublicSend
end
diff --git a/lib/gitlab/error_tracking/processor/grpc_error_processor.rb b/lib/gitlab/error_tracking/processor/grpc_error_processor.rb
index 419098dbd09..e2a9192806f 100644
--- a/lib/gitlab/error_tracking/processor/grpc_error_processor.rb
+++ b/lib/gitlab/error_tracking/processor/grpc_error_processor.rb
@@ -3,22 +3,11 @@
module Gitlab
module ErrorTracking
module Processor
- class GrpcErrorProcessor < ::Raven::Processor
+ module GrpcErrorProcessor
DEBUG_ERROR_STRING_REGEX = RE2('(.*) debug_error_string:(.*)')
- def process(payload)
- return payload if ::Feature.enabled?(:sentry_processors_before_send, default_enabled: :yaml)
-
- self.class.process_first_exception_value(payload)
- self.class.process_custom_fingerprint(payload)
-
- payload
- end
-
class << self
def call(event)
- return event unless ::Feature.enabled?(:sentry_processors_before_send, default_enabled: :yaml)
-
process_first_exception_value(event)
process_custom_fingerprint(event)
@@ -27,8 +16,9 @@ module Gitlab
# Sentry can report multiple exceptions in an event. Sanitize
# only the first one since that's what is used for grouping.
- def process_first_exception_value(event_or_payload)
- exceptions = exceptions(event_or_payload)
+ def process_first_exception_value(event)
+ # Better in new version, will be event.exception.values
+ exceptions = event.instance_variable_get(:@interfaces)[:exception]&.values
return unless exceptions.is_a?(Array)
@@ -36,18 +26,21 @@ module Gitlab
return unless valid_exception?(exception)
- exception_type, raw_message = type_and_value(exception)
+ raw_message = exception.value
- return unless exception_type&.start_with?('GRPC::')
+ return unless exception.type&.start_with?('GRPC::')
return unless raw_message.present?
message, debug_str = split_debug_error_string(raw_message)
- set_new_values!(event_or_payload, exception, message, debug_str)
+ # Worse in new version, no setter! Have to poke at the
+ # instance variable
+ exception.value = message if message
+ event.extra[:grpc_debug_error_string] = debug_str if debug_str
end
def process_custom_fingerprint(event)
- fingerprint = fingerprint(event)
+ fingerprint = event.fingerprint
return event unless custom_grpc_fingerprint?(fingerprint)
@@ -71,61 +64,14 @@ module Gitlab
[match[1], match[2]]
end
- # The below methods can be removed once we remove the
- # sentry_processors_before_send feature flag, and we can
- # assume we always have an Event object
- def exceptions(event_or_payload)
- case event_or_payload
- when Raven::Event
- # Better in new version, will be event_or_payload.exception.values
- event_or_payload.instance_variable_get(:@interfaces)[:exception]&.values
- when Hash
- event_or_payload.dig(:exception, :values)
- end
- end
-
def valid_exception?(exception)
case exception
when Raven::SingleExceptionInterface
exception&.value
- when Hash
- true
else
false
end
end
-
- def type_and_value(exception)
- case exception
- when Raven::SingleExceptionInterface
- [exception.type, exception.value]
- when Hash
- exception.values_at(:type, :value)
- end
- end
-
- def set_new_values!(event_or_payload, exception, message, debug_str)
- case event_or_payload
- when Raven::Event
- # Worse in new version, no setter! Have to poke at the
- # instance variable
- exception.value = message if message
- event_or_payload.extra[:grpc_debug_error_string] = debug_str if debug_str
- when Hash
- exception[:value] = message if message
- extra = event_or_payload[:extra] || {}
- extra[:grpc_debug_error_string] = debug_str if debug_str
- end
- end
-
- def fingerprint(event_or_payload)
- case event_or_payload
- when Raven::Event
- event_or_payload.fingerprint
- when Hash
- event_or_payload[:fingerprint]
- end
- end
end
end
end
diff --git a/lib/gitlab/error_tracking/processor/sidekiq_processor.rb b/lib/gitlab/error_tracking/processor/sidekiq_processor.rb
index 93310745ece..0d2f673d73c 100644
--- a/lib/gitlab/error_tracking/processor/sidekiq_processor.rb
+++ b/lib/gitlab/error_tracking/processor/sidekiq_processor.rb
@@ -5,7 +5,7 @@ require 'set'
module Gitlab
module ErrorTracking
module Processor
- class SidekiqProcessor < ::Raven::Processor
+ module SidekiqProcessor
FILTERED_STRING = '[FILTERED]'
class << self
@@ -29,7 +29,7 @@ module Gitlab
@permitted_arguments_for_worker[klass] ||=
begin
klass.constantize&.loggable_arguments&.to_set
- rescue
+ rescue StandardError
Set.new
end
end
@@ -42,8 +42,6 @@ module Gitlab
end
def call(event)
- return event unless ::Feature.enabled?(:sentry_processors_before_send, default_enabled: :yaml)
-
sidekiq = event&.extra&.dig(:sidekiq)
return event unless sidekiq
@@ -64,29 +62,6 @@ module Gitlab
event
end
end
-
- def process(value, key = nil)
- return value if ::Feature.enabled?(:sentry_processors_before_send, default_enabled: :yaml)
-
- sidekiq = value.dig(:extra, :sidekiq)
-
- return value unless sidekiq
-
- sidekiq = sidekiq.deep_dup
- sidekiq.delete(:jobstr)
-
- # 'args' in this hash => from Gitlab::ErrorTracking.track_*
- # 'args' in :job => from default error handler
- job_holder = sidekiq.key?('args') ? sidekiq : sidekiq[:job]
-
- if job_holder['args']
- job_holder['args'] = self.class.filter_arguments(job_holder['args'], job_holder['class']).to_a
- end
-
- value[:extra][:sidekiq] = sidekiq
-
- value
- end
end
end
end
diff --git a/lib/gitlab/etag_caching/router/graphql.rb b/lib/gitlab/etag_caching/router/graphql.rb
index f1737f0ce5a..2b8639b9411 100644
--- a/lib/gitlab/etag_caching/router/graphql.rb
+++ b/lib/gitlab/etag_caching/router/graphql.rb
@@ -12,6 +12,11 @@ module Gitlab
%r(\Apipelines/id/\d+\z),
'pipelines_graph',
'continuous_integration'
+ ],
+ [
+ %r(\Apipelines/sha/\w{7,40}\z),
+ 'ci_editor',
+ 'pipeline_authoring'
]
].map(&method(:build_route)).freeze
diff --git a/lib/gitlab/etag_caching/router/restful.rb b/lib/gitlab/etag_caching/router/restful.rb
index 08c20e30a48..fba4b9e433a 100644
--- a/lib/gitlab/etag_caching/router/restful.rb
+++ b/lib/gitlab/etag_caching/router/restful.rb
@@ -109,4 +109,4 @@ module Gitlab
end
end
-Gitlab::EtagCaching::Router::Restful.prepend_if_ee('EE::Gitlab::EtagCaching::Router::Restful')
+Gitlab::EtagCaching::Router::Restful.prepend_mod_with('Gitlab::EtagCaching::Router::Restful')
diff --git a/lib/gitlab/exclusive_lease.rb b/lib/gitlab/exclusive_lease.rb
index ef0236f8275..6749bd6ca60 100644
--- a/lib/gitlab/exclusive_lease.rb
+++ b/lib/gitlab/exclusive_lease.rb
@@ -113,4 +113,4 @@ module Gitlab
end
end
-Gitlab::ExclusiveLease.prepend_if_ee('EE::Gitlab::ExclusiveLease')
+Gitlab::ExclusiveLease.prepend_mod_with('Gitlab::ExclusiveLease')
diff --git a/lib/gitlab/experimentation.rb b/lib/gitlab/experimentation.rb
index 145bb6d7b8f..e4233b8a935 100644
--- a/lib/gitlab/experimentation.rb
+++ b/lib/gitlab/experimentation.rb
@@ -34,10 +34,6 @@
module Gitlab
module Experimentation
EXPERIMENTS = {
- invite_members_version_b: {
- tracking_category: 'Growth::Expansion::Experiment::InviteMembersVersionB',
- use_backwards_compatible_subject_index: true
- },
invite_members_empty_group_version_a: {
tracking_category: 'Growth::Expansion::Experiment::InviteMembersEmptyGroupVersionA',
use_backwards_compatible_subject_index: true
@@ -55,10 +51,6 @@ module Gitlab
trial_during_signup: {
tracking_category: 'Growth::Conversion::Experiment::TrialDuringSignup'
},
- ci_syntax_templates_b: {
- tracking_category: 'Growth::Activation::Experiment::CiSyntaxTemplates',
- rollout_strategy: :user
- },
invite_members_new_dropdown: {
tracking_category: 'Growth::Expansion::Experiment::InviteMembersNewDropdown'
},
@@ -154,7 +146,7 @@ module Gitlab
elsif subject.respond_to?(:to_s)
subject.to_s
else
- raise ArgumentError.new('Subject must respond to `to_global_id` or `to_s`')
+ raise ArgumentError, 'Subject must respond to `to_global_id` or `to_s`'
end
end
end
diff --git a/lib/gitlab/experimentation/controller_concern.rb b/lib/gitlab/experimentation/controller_concern.rb
index 248abfeada5..e53689eb89b 100644
--- a/lib/gitlab/experimentation/controller_concern.rb
+++ b/lib/gitlab/experimentation/controller_concern.rb
@@ -19,13 +19,18 @@ module Gitlab
end
def set_experimentation_subject_id_cookie
- return if cookies[:experimentation_subject_id].present?
-
- cookies.permanent.signed[:experimentation_subject_id] = {
- value: SecureRandom.uuid,
- secure: ::Gitlab.config.gitlab.https,
- httponly: true
- }
+ if Gitlab.dev_env_or_com?
+ return if cookies[:experimentation_subject_id].present?
+
+ cookies.permanent.signed[:experimentation_subject_id] = {
+ value: SecureRandom.uuid,
+ secure: ::Gitlab.config.gitlab.https,
+ httponly: true
+ }
+ else
+ # We set the cookie before, although experiments are not conducted on self managed instances.
+ cookies.delete(:experimentation_subject_id)
+ end
end
def push_frontend_experiment(experiment_key, subject: nil)
diff --git a/lib/gitlab/external_authorization/client.rb b/lib/gitlab/external_authorization/client.rb
index 582051010d3..43f7f042592 100644
--- a/lib/gitlab/external_authorization/client.rb
+++ b/lib/gitlab/external_authorization/client.rb
@@ -24,7 +24,7 @@ module Gitlab
)
::Gitlab::ExternalAuthorization::Response.new(response)
rescue *Gitlab::HTTP::HTTP_ERRORS => e
- raise ::Gitlab::ExternalAuthorization::RequestFailed.new(e)
+ raise ::Gitlab::ExternalAuthorization::RequestFailed, e
end
private
diff --git a/lib/gitlab/fake_application_settings.rb b/lib/gitlab/fake_application_settings.rb
index 71d2b2396f8..211c0967f89 100644
--- a/lib/gitlab/fake_application_settings.rb
+++ b/lib/gitlab/fake_application_settings.rb
@@ -33,4 +33,4 @@ module Gitlab
end
end
-Gitlab::FakeApplicationSettings.prepend_if_ee('EE::Gitlab::FakeApplicationSettings')
+Gitlab::FakeApplicationSettings.prepend_mod_with('Gitlab::FakeApplicationSettings')
diff --git a/lib/gitlab/faraday/error_callback.rb b/lib/gitlab/faraday/error_callback.rb
index f99be5b4d04..9b436c3a08e 100644
--- a/lib/gitlab/faraday/error_callback.rb
+++ b/lib/gitlab/faraday/error_callback.rb
@@ -28,7 +28,7 @@ module Gitlab
def call(env)
@app.call(env)
- rescue => e
+ rescue StandardError => e
@options.callback&.call(env, e)
raise
diff --git a/lib/gitlab/favicon.rb b/lib/gitlab/favicon.rb
index ce1370bab0f..721518c6fcc 100644
--- a/lib/gitlab/favicon.rb
+++ b/lib/gitlab/favicon.rb
@@ -61,4 +61,4 @@ module Gitlab
end
end
-Gitlab::Favicon.prepend_if_ee('EE::Gitlab::Favicon')
+Gitlab::Favicon.prepend_mod_with('Gitlab::Favicon')
diff --git a/lib/gitlab/file_hook.rb b/lib/gitlab/file_hook.rb
index 55eba2858fb..e398a3f9585 100644
--- a/lib/gitlab/file_hook.rb
+++ b/lib/gitlab/file_hook.rb
@@ -28,7 +28,7 @@ module Gitlab
exit_status = result.status&.exitstatus
[exit_status == 0, result.stderr]
- rescue => e
+ rescue StandardError => e
[false, e.message]
end
end
diff --git a/lib/gitlab/fogbugz_import/repository.rb b/lib/gitlab/fogbugz_import/repository.rb
index b958dcf6cbf..4a5152021b4 100644
--- a/lib/gitlab/fogbugz_import/repository.rb
+++ b/lib/gitlab/fogbugz_import/repository.rb
@@ -26,7 +26,7 @@ module Gitlab
end
def path
- safe_name.gsub(/[\s]/, '_')
+ safe_name.gsub(/\s/, '_')
end
end
end
diff --git a/lib/gitlab/git/blob.rb b/lib/gitlab/git/blob.rb
index 5d91eb605e8..1c8e55ecf50 100644
--- a/lib/gitlab/git/blob.rb
+++ b/lib/gitlab/git/blob.rb
@@ -110,8 +110,8 @@ module Gitlab
end
end
- def binary?(data)
- EncodingHelper.detect_libgit2_binary?(data)
+ def binary?(data, cache_key: nil)
+ EncodingHelper.detect_libgit2_binary?(data, cache_key: cache_key)
end
def size_could_be_lfs?(size)
diff --git a/lib/gitlab/git/branch.rb b/lib/gitlab/git/branch.rb
index 9447cfa0fb6..fbe52db9c0b 100644
--- a/lib/gitlab/git/branch.rb
+++ b/lib/gitlab/git/branch.rb
@@ -28,6 +28,10 @@ module Gitlab
def state
active? ? :active : :stale
end
+
+ def cache_key
+ "branch:" + Digest::SHA1.hexdigest([name, target, dereferenced_target&.sha].join(':'))
+ end
end
end
end
diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb
index 51baed32935..a863b952390 100644
--- a/lib/gitlab/git/commit.rb
+++ b/lib/gitlab/git/commit.rb
@@ -263,7 +263,7 @@ module Gitlab
def has_zero_stats?
stats.total == 0
- rescue
+ rescue StandardError
true
end
diff --git a/lib/gitlab/git/conflict/resolver.rb b/lib/gitlab/git/conflict/resolver.rb
index 26e82643a4c..751184b23df 100644
--- a/lib/gitlab/git/conflict/resolver.rb
+++ b/lib/gitlab/git/conflict/resolver.rb
@@ -20,9 +20,9 @@ module Gitlab
gitaly_conflicts_client(@target_repository).list_conflict_files.to_a
end
rescue GRPC::FailedPrecondition => e
- raise Gitlab::Git::Conflict::Resolver::ConflictSideMissing.new(e.message)
+ raise Gitlab::Git::Conflict::Resolver::ConflictSideMissing, e.message
rescue GRPC::BadStatus => e
- raise Gitlab::Git::CommandError.new(e)
+ raise Gitlab::Git::CommandError, e
end
def resolve_conflicts(source_repository, resolution, source_branch:, target_branch:)
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 3361cee733b..102fe60f2cb 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -89,9 +89,9 @@ module Gitlab
def root_ref
gitaly_ref_client.default_branch_name
rescue GRPC::NotFound => e
- raise NoRepository.new(e.message)
+ raise NoRepository, e.message
rescue GRPC::Unknown => e
- raise Gitlab::Git::CommandError.new(e.message)
+ raise Gitlab::Git::CommandError, e.message
end
def exists?
@@ -348,7 +348,7 @@ module Gitlab
limit = options[:limit]
if limit == 0 || !limit.is_a?(Integer)
- raise ArgumentError.new("invalid Repository#log limit: #{limit.inspect}")
+ raise ArgumentError, "invalid Repository#log limit: #{limit.inspect}"
end
wrapped_gitaly_errors do
@@ -414,7 +414,7 @@ module Gitlab
end
end
rescue ArgumentError => e
- raise Gitlab::Git::Repository::GitError.new(e)
+ raise Gitlab::Git::Repository::GitError, e
end
# Returns the SHA of the most recent common ancestor of +from+ and +to+
@@ -700,11 +700,11 @@ module Gitlab
end
end
- def find_remote_root_ref(remote_name)
- return unless remote_name.present?
+ def find_remote_root_ref(remote_name, remote_url, authorization = nil)
+ return unless remote_name.present? && remote_url.present?
wrapped_gitaly_errors do
- gitaly_remote_client.find_remote_root_ref(remote_name)
+ gitaly_remote_client.find_remote_root_ref(remote_name, remote_url, authorization)
end
end
@@ -836,7 +836,7 @@ module Gitlab
def fsck
msg, status = gitaly_repository_client.fsck
- raise GitError.new("Could not fsck repository: #{msg}") unless status == 0
+ raise GitError, "Could not fsck repository: #{msg}" unless status == 0
end
def create_from_bundle(bundle_path)
diff --git a/lib/gitlab/git/rugged_impl/repository.rb b/lib/gitlab/git/rugged_impl/repository.rb
index 8679d977773..ea10b4e7cd8 100644
--- a/lib/gitlab/git/rugged_impl/repository.rb
+++ b/lib/gitlab/git/rugged_impl/repository.rb
@@ -31,7 +31,7 @@ module Gitlab
def rugged
@rugged ||= ::Rugged::Repository.new(path, alternates: alternate_object_directories)
rescue ::Rugged::RepositoryError, ::Rugged::OSError
- raise ::Gitlab::Git::Repository::NoRepository.new('no repository for such path')
+ raise ::Gitlab::Git::Repository::NoRepository, 'no repository for such path'
end
def cleanup
diff --git a/lib/gitlab/git/wiki.rb b/lib/gitlab/git/wiki.rb
index 75d6b949874..5616b61de07 100644
--- a/lib/gitlab/git/wiki.rb
+++ b/lib/gitlab/git/wiki.rb
@@ -73,12 +73,6 @@ module Gitlab
end
end
- def delete_page(page_path, commit_details)
- wrapped_gitaly_errors do
- gitaly_delete_page(page_path, commit_details)
- end
- end
-
def update_page(page_path, title, format, content, commit_details)
wrapped_gitaly_errors do
gitaly_update_page(page_path, title, format, content, commit_details)
@@ -102,22 +96,6 @@ module Gitlab
end
end
- # options:
- # :page - The Integer page number.
- # :per_page - The number of items per page.
- # :limit - Total number of items to return.
- def page_versions(page_path, options = {})
- versions = wrapped_gitaly_errors do
- gitaly_wiki_client.page_versions(page_path, options)
- end
-
- # Gitaly uses gollum-lib to get the versions. Gollum defaults to 20
- # per page, but also fetches 20 if `limit` or `per_page` < 20.
- # Slicing returns an array with the expected number of items.
- slice_bound = options[:limit] || options[:per_page] || DEFAULT_PAGINATION
- versions[0..slice_bound]
- end
-
def count_page_versions(page_path)
@repository.count_commits(ref: 'HEAD', path: page_path)
end
@@ -140,10 +118,6 @@ module Gitlab
gitaly_wiki_client.update_page(page_path, title, format, content, commit_details)
end
- def gitaly_delete_page(page_path, commit_details)
- gitaly_wiki_client.delete_page(page_path, commit_details)
- end
-
def gitaly_find_page(title:, version: nil, dir: nil)
return unless title.present?
diff --git a/lib/gitlab/git/wraps_gitaly_errors.rb b/lib/gitlab/git/wraps_gitaly_errors.rb
index 2009683d32c..1d34f3c8eb2 100644
--- a/lib/gitlab/git/wraps_gitaly_errors.rb
+++ b/lib/gitlab/git/wraps_gitaly_errors.rb
@@ -6,13 +6,13 @@ module Gitlab
def wrapped_gitaly_errors(&block)
yield block
rescue GRPC::NotFound => e
- raise Gitlab::Git::Repository::NoRepository.new(e)
+ raise Gitlab::Git::Repository::NoRepository, e
rescue GRPC::InvalidArgument => e
- raise ArgumentError.new(e)
+ raise ArgumentError, e
rescue GRPC::DeadlineExceeded => e
- raise Gitlab::Git::CommandTimedOut.new(e)
+ raise Gitlab::Git::CommandTimedOut, e
rescue GRPC::BadStatus => e
- raise Gitlab::Git::CommandError.new(e)
+ raise Gitlab::Git::CommandError, e
end
end
end
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index 31e4755192e..b5e7220889e 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -22,7 +22,7 @@ module Gitlab
auth_download: 'You are not allowed to download code.',
deploy_key_upload: 'This deploy key does not have write access to this project.',
no_repo: 'A repository for this project does not exist yet.',
- project_not_found: 'The project you were looking for could not be found.',
+ project_not_found: "The project you were looking for could not be found or you don't have permission to view it.",
command_not_allowed: "The command you're trying to execute is not allowed.",
upload_pack_disabled_over_http: 'Pulling over HTTP is not allowed.',
receive_pack_disabled_over_http: 'Pushing over HTTP is not allowed.',
@@ -538,4 +538,4 @@ module Gitlab
end
end
-Gitlab::GitAccess.prepend_if_ee('EE::Gitlab::GitAccess')
+Gitlab::GitAccess.prepend_mod_with('Gitlab::GitAccess')
diff --git a/lib/gitlab/git_access_design.rb b/lib/gitlab/git_access_design.rb
index 6bea9fe53b3..bf89c01305a 100644
--- a/lib/gitlab/git_access_design.rb
+++ b/lib/gitlab/git_access_design.rb
@@ -32,4 +32,4 @@ module Gitlab
end
end
-Gitlab::GitAccessDesign.prepend_if_ee('EE::Gitlab::GitAccessDesign')
+Gitlab::GitAccessDesign.prepend_mod_with('Gitlab::GitAccessDesign')
diff --git a/lib/gitlab/git_access_snippet.rb b/lib/gitlab/git_access_snippet.rb
index 88a75f72840..9a431dc7088 100644
--- a/lib/gitlab/git_access_snippet.rb
+++ b/lib/gitlab/git_access_snippet.rb
@@ -141,4 +141,4 @@ module Gitlab
end
end
-Gitlab::GitAccessSnippet.prepend_if_ee('EE::Gitlab::GitAccessSnippet')
+Gitlab::GitAccessSnippet.prepend_mod_with('Gitlab::GitAccessSnippet')
diff --git a/lib/gitlab/git_access_wiki.rb b/lib/gitlab/git_access_wiki.rb
index 3011b794b8f..0963eb6b72a 100644
--- a/lib/gitlab/git_access_wiki.rb
+++ b/lib/gitlab/git_access_wiki.rb
@@ -48,4 +48,4 @@ module Gitlab
end
end
-Gitlab::GitAccessWiki.prepend_if_ee('EE::Gitlab::GitAccessWiki')
+Gitlab::GitAccessWiki.prepend_mod_with('Gitlab::GitAccessWiki')
diff --git a/lib/gitlab/gitaly_client/blob_service.rb b/lib/gitlab/gitaly_client/blob_service.rb
index 19a473e4785..affd3986381 100644
--- a/lib/gitlab/gitaly_client/blob_service.rb
+++ b/lib/gitlab/gitaly_client/blob_service.rb
@@ -115,7 +115,7 @@ module Gitlab
# necessary graph walk to detect only new LFS pointers and instead scan
# through all quarantined objects.
git_env = ::Gitlab::Git::HookEnv.all(@gitaly_repo.gl_repository)
- if Feature.enabled?(:lfs_integrity_inspect_quarantined_objects, @project, default_enabled: :yaml) && git_env['GIT_OBJECT_DIRECTORY_RELATIVE'].present?
+ if git_env['GIT_OBJECT_DIRECTORY_RELATIVE'].present?
repository = @gitaly_repo.dup
repository.git_alternate_object_directories = Google::Protobuf::RepeatedField.new(:string)
diff --git a/lib/gitlab/gitaly_client/blobs_stitcher.rb b/lib/gitlab/gitaly_client/blobs_stitcher.rb
index f860d8ce517..2f6d146b5c4 100644
--- a/lib/gitlab/gitaly_client/blobs_stitcher.rb
+++ b/lib/gitlab/gitaly_client/blobs_stitcher.rb
@@ -19,9 +19,9 @@ module Gitlab
yield new_blob(current_blob_data) if current_blob_data
current_blob_data = msg.to_h.slice(:oid, :path, :size, :revision, :mode)
- current_blob_data[:data] = msg.data.dup
+ current_blob_data[:data_parts] = [msg.data]
else
- current_blob_data[:data] << msg.data
+ current_blob_data[:data_parts] << msg.data
end
end
@@ -31,6 +31,8 @@ module Gitlab
private
def new_blob(blob_data)
+ data = blob_data[:data_parts].join
+
Gitlab::Git::Blob.new(
id: blob_data[:oid],
mode: blob_data[:mode].to_s(8),
@@ -38,8 +40,8 @@ module Gitlab
path: blob_data[:path],
size: blob_data[:size],
commit_id: blob_data[:revision],
- data: blob_data[:data],
- binary: Gitlab::Git::Blob.binary?(blob_data[:data])
+ data: data,
+ binary: Gitlab::Git::Blob.binary?(data, cache_key: blob_data[:oid])
)
end
end
diff --git a/lib/gitlab/gitaly_client/call.rb b/lib/gitlab/gitaly_client/call.rb
index 4bb184bee2f..3fe3702cfe1 100644
--- a/lib/gitlab/gitaly_client/call.rb
+++ b/lib/gitlab/gitaly_client/call.rb
@@ -30,7 +30,7 @@ module Gitlab
store_timings
response
end
- rescue => err
+ rescue StandardError => err
store_timings
raise err
end
diff --git a/lib/gitlab/gitaly_client/operation_service.rb b/lib/gitlab/gitaly_client/operation_service.rb
index 5ce1b1f0c87..fd794acb4dd 100644
--- a/lib/gitlab/gitaly_client/operation_service.rb
+++ b/lib/gitlab/gitaly_client/operation_service.rb
@@ -59,7 +59,7 @@ module Gitlab
:user_create_branch, request, timeout: GitalyClient.long_timeout)
if response.pre_receive_error.present?
- raise Gitlab::Git::PreReceiveError.new(response.pre_receive_error)
+ raise Gitlab::Git::PreReceiveError, response.pre_receive_error
end
branch = response.branch
@@ -159,7 +159,7 @@ module Gitlab
branch_update = second_response.branch_update
return if branch_update.nil?
- raise Gitlab::Git::CommitError.new('failed to apply merge to branch') unless branch_update.commit_id.present?
+ raise Gitlab::Git::CommitError, 'failed to apply merge to branch' unless branch_update.commit_id.present?
Gitlab::Git::OperationService::BranchUpdate.from_gitaly(branch_update)
ensure
diff --git a/lib/gitlab/gitaly_client/ref_service.rb b/lib/gitlab/gitaly_client/ref_service.rb
index 97b6813c080..ac2db99ee01 100644
--- a/lib/gitlab/gitaly_client/ref_service.rb
+++ b/lib/gitlab/gitaly_client/ref_service.rb
@@ -292,7 +292,7 @@ module Gitlab
end
def invalid_ref!(message)
- raise Gitlab::Git::Repository::InvalidRef.new(message)
+ raise Gitlab::Git::Repository::InvalidRef, message
end
end
end
diff --git a/lib/gitlab/gitaly_client/remote_service.rb b/lib/gitlab/gitaly_client/remote_service.rb
index 06aaf460751..04dd394a2bd 100644
--- a/lib/gitlab/gitaly_client/remote_service.rb
+++ b/lib/gitlab/gitaly_client/remote_service.rb
@@ -43,11 +43,20 @@ module Gitlab
GitalyClient.call(@storage, :remote_service, :remove_remote, request, timeout: GitalyClient.long_timeout).result
end
- def find_remote_root_ref(remote_name)
- request = Gitaly::FindRemoteRootRefRequest.new(
- repository: @gitaly_repo,
- remote: remote_name
- )
+ # The remote_name parameter is deprecated and will be removed soon.
+ def find_remote_root_ref(remote_name, remote_url, authorization)
+ request = if Feature.enabled?(:find_remote_root_refs_inmemory, default_enabled: :yaml)
+ Gitaly::FindRemoteRootRefRequest.new(
+ repository: @gitaly_repo,
+ remote_url: remote_url,
+ http_authorization_header: authorization
+ )
+ else
+ Gitaly::FindRemoteRootRefRequest.new(
+ repository: @gitaly_repo,
+ remote: remote_name
+ )
+ end
response = GitalyClient.call(@storage, :remote_service,
:find_remote_root_ref, request, timeout: GitalyClient.medium_timeout)
diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb
index a93f4071efc..d2dbd456180 100644
--- a/lib/gitlab/gitaly_client/repository_service.rb
+++ b/lib/gitlab/gitaly_client/repository_service.rb
@@ -319,7 +319,7 @@ module Gitlab
response = GitalyClient.call(@storage, :repository_service, :calculate_checksum, request, timeout: GitalyClient.fast_timeout)
response.checksum.presence
rescue GRPC::DataLoss => e
- raise Gitlab::Git::Repository::InvalidRepository.new(e)
+ raise Gitlab::Git::Repository::InvalidRepository, e
end
def raw_changes_between(from, to)
diff --git a/lib/gitlab/gitaly_client/storage_settings.rb b/lib/gitlab/gitaly_client/storage_settings.rb
index dd9e3d5d28b..f66dc3010ea 100644
--- a/lib/gitlab/gitaly_client/storage_settings.rb
+++ b/lib/gitlab/gitaly_client/storage_settings.rb
@@ -34,7 +34,7 @@ module Gitlab
return false if rugged_enabled?
!temporarily_allowed?(ALLOW_KEY)
- rescue
+ rescue StandardError
false # Err on the side of caution, don't break gitlab for people
end
diff --git a/lib/gitlab/gitaly_client/wiki_service.rb b/lib/gitlab/gitaly_client/wiki_service.rb
index fecc2b7023d..3613cd01122 100644
--- a/lib/gitlab/gitaly_client/wiki_service.rb
+++ b/lib/gitlab/gitaly_client/wiki_service.rb
@@ -64,16 +64,6 @@ module Gitlab
GitalyClient.call(@repository.storage, :wiki_service, :wiki_update_page, enum, timeout: GitalyClient.medium_timeout)
end
- def delete_page(page_path, commit_details)
- request = Gitaly::WikiDeletePageRequest.new(
- repository: @gitaly_repo,
- page_path: encode_binary(page_path),
- commit_details: gitaly_commit_details(commit_details)
- )
-
- GitalyClient.call(@repository.storage, :wiki_service, :wiki_delete_page, request, timeout: GitalyClient.medium_timeout)
- end
-
def find_page(title:, version: nil, dir: nil)
request = Gitaly::WikiFindPageRequest.new(
repository: @gitaly_repo,
@@ -129,30 +119,6 @@ module Gitlab
pages
end
- # options:
- # :page - The Integer page number.
- # :per_page - The number of items per page.
- # :limit - Total number of items to return.
- def page_versions(page_path, options)
- request = Gitaly::WikiGetPageVersionsRequest.new(
- repository: @gitaly_repo,
- page_path: encode_binary(page_path),
- page: options[:page] || 1,
- per_page: options[:per_page] || Gitlab::Git::Wiki::DEFAULT_PAGINATION
- )
-
- stream = GitalyClient.call(@repository.storage, :wiki_service, :wiki_get_page_versions, request, timeout: GitalyClient.medium_timeout)
-
- versions = []
- stream.each do |message|
- message.versions.each do |version|
- versions << new_wiki_page_version(version)
- end
- end
-
- versions
- end
-
private
# If a block is given and the yielded value is truthy, iteration will be
diff --git a/lib/gitlab/github_import/client.rb b/lib/gitlab/github_import/client.rb
index 328f1f742c5..138716b1b53 100644
--- a/lib/gitlab/github_import/client.rb
+++ b/lib/gitlab/github_import/client.rb
@@ -70,7 +70,7 @@ module Gitlab
end
def pull_request_reviews(repo_name, iid)
- with_rate_limit { octokit.pull_request_reviews(repo_name, iid) }
+ each_object(:pull_request_reviews, repo_name, iid)
end
# Returns the details of a GitHub repository.
diff --git a/lib/gitlab/github_import/importer/diff_note_importer.rb b/lib/gitlab/github_import/importer/diff_note_importer.rb
index 53b17f77ccd..d2f5af63621 100644
--- a/lib/gitlab/github_import/importer/diff_note_importer.rb
+++ b/lib/gitlab/github_import/importer/diff_note_importer.rb
@@ -21,8 +21,7 @@ module Gitlab
author_id, author_found = user_finder.author_id_for(note)
- note_body =
- MarkdownText.format(note.note, note.author, author_found)
+ note_body = MarkdownText.format(note.note, note.author, author_found)
attributes = {
noteable_type: 'MergeRequest',
diff --git a/lib/gitlab/github_import/importer/note_importer.rb b/lib/gitlab/github_import/importer/note_importer.rb
index 41f179d275b..ae9996d81ef 100644
--- a/lib/gitlab/github_import/importer/note_importer.rb
+++ b/lib/gitlab/github_import/importer/note_importer.rb
@@ -21,8 +21,7 @@ module Gitlab
author_id, author_found = user_finder.author_id_for(note)
- note_body =
- MarkdownText.format(note.note, note.author, author_found)
+ note_body = MarkdownText.format(note.note, note.author, author_found)
attributes = {
noteable_type: note.noteable_type,
diff --git a/lib/gitlab/github_import/importer/pull_request_importer.rb b/lib/gitlab/github_import/importer/pull_request_importer.rb
index f09e0bd9806..3c17ea1195e 100644
--- a/lib/gitlab/github_import/importer/pull_request_importer.rb
+++ b/lib/gitlab/github_import/importer/pull_request_importer.rb
@@ -44,8 +44,7 @@ module Gitlab
def create_merge_request
author_id, author_found = user_finder.author_id_for(pull_request)
- description = MarkdownText
- .format(pull_request.description, pull_request.author, author_found)
+ description = MarkdownText.format(pull_request.description, pull_request.author, author_found)
attributes = {
iid: pull_request.iid,
diff --git a/lib/gitlab/github_import/importer/pull_request_review_importer.rb b/lib/gitlab/github_import/importer/pull_request_review_importer.rb
index 9f495913897..f476ee13392 100644
--- a/lib/gitlab/github_import/importer/pull_request_review_importer.rb
+++ b/lib/gitlab/github_import/importer/pull_request_review_importer.rb
@@ -36,12 +36,12 @@ module Gitlab
def add_complementary_review_note!(author_id)
return if review.note.empty? && !review.approval?
- note = "*Created by %{login}*\n\n%{note}" % {
- note: review_note_content,
- login: review.author.login
- }
+ note_body = MarkdownText.format(
+ review_note_content,
+ review.author
+ )
- add_note!(author_id, note)
+ add_note!(author_id, note_body)
end
def review_note_content
diff --git a/lib/gitlab/github_import/importer/pull_requests_merged_by_importer.rb b/lib/gitlab/github_import/importer/pull_requests_merged_by_importer.rb
index 466288fde4c..94472cd341e 100644
--- a/lib/gitlab/github_import/importer/pull_requests_merged_by_importer.rb
+++ b/lib/gitlab/github_import/importer/pull_requests_merged_by_importer.rb
@@ -22,14 +22,18 @@ module Gitlab
:pull_requests_merged_by
end
- def id_for_already_imported_cache(pr)
- pr.number
+ def id_for_already_imported_cache(merge_request)
+ merge_request.id
end
def each_object_to_import
project.merge_requests.with_state(:merged).find_each do |merge_request|
+ next if already_imported?(merge_request)
+
pull_request = client.pull_request(project.import_source, merge_request.iid)
yield(pull_request)
+
+ mark_as_imported(merge_request)
end
end
end
diff --git a/lib/gitlab/github_import/importer/pull_requests_reviews_importer.rb b/lib/gitlab/github_import/importer/pull_requests_reviews_importer.rb
index 6d1b588f0e0..827027203ff 100644
--- a/lib/gitlab/github_import/importer/pull_requests_reviews_importer.rb
+++ b/lib/gitlab/github_import/importer/pull_requests_reviews_importer.rb
@@ -22,17 +22,22 @@ module Gitlab
:pull_request_reviews
end
- def id_for_already_imported_cache(review)
- review.github_id
+ def id_for_already_imported_cache(merge_request)
+ merge_request.id
end
def each_object_to_import
project.merge_requests.find_each do |merge_request|
- reviews = client.pull_request_reviews(project.import_source, merge_request.iid)
- reviews.each do |review|
- review.merge_request_id = merge_request.id
- yield(review)
- end
+ next if already_imported?(merge_request)
+
+ client
+ .pull_request_reviews(project.import_source, merge_request.iid)
+ .each do |review|
+ review.merge_request_id = merge_request.id
+ yield(review)
+ end
+
+ mark_as_imported(merge_request)
end
end
end
diff --git a/lib/gitlab/github_import/markdown_text.rb b/lib/gitlab/github_import/markdown_text.rb
index b25c4f7becf..e5f4dabe42d 100644
--- a/lib/gitlab/github_import/markdown_text.rb
+++ b/lib/gitlab/github_import/markdown_text.rb
@@ -3,7 +3,7 @@
module Gitlab
module GithubImport
class MarkdownText
- attr_reader :text, :author, :exists
+ include Gitlab::EncodingHelper
def self.format(*args)
new(*args).to_s
@@ -19,10 +19,19 @@ module Gitlab
end
def to_s
- if exists
- text
- else
+ # Gitlab::EncodingHelper#clean remove `null` chars from the string
+ clean(format)
+ end
+
+ private
+
+ attr_reader :text, :author, :exists
+
+ def format
+ if author&.login.present? && !exists
"*Created by: #{author.login}*\n\n#{text}"
+ else
+ text
end
end
end
diff --git a/lib/gitlab/github_import/parallel_importer.rb b/lib/gitlab/github_import/parallel_importer.rb
index 1b4750da868..2429fa4de1d 100644
--- a/lib/gitlab/github_import/parallel_importer.rb
+++ b/lib/gitlab/github_import/parallel_importer.rb
@@ -40,4 +40,4 @@ module Gitlab
end
end
-Gitlab::GithubImport::ParallelImporter.prepend_if_ee('::EE::Gitlab::GithubImport::ParallelImporter')
+Gitlab::GithubImport::ParallelImporter.prepend_mod_with('Gitlab::GithubImport::ParallelImporter')
diff --git a/lib/gitlab/github_import/parallel_scheduling.rb b/lib/gitlab/github_import/parallel_scheduling.rb
index 51859010ec3..92f9e8a646d 100644
--- a/lib/gitlab/github_import/parallel_scheduling.rb
+++ b/lib/gitlab/github_import/parallel_scheduling.rb
@@ -48,7 +48,7 @@ module Gitlab
info(project.id, message: "importer finished")
retval
- rescue => e
+ rescue StandardError => e
error(project.id, e)
raise e
diff --git a/lib/gitlab/github_import/representation/issue.rb b/lib/gitlab/github_import/representation/issue.rb
index f3071b3e2b3..0e04b5ad57f 100644
--- a/lib/gitlab/github_import/representation/issue.rb
+++ b/lib/gitlab/github_import/representation/issue.rb
@@ -25,6 +25,7 @@ module Gitlab
hash = {
iid: issue.number,
+ github_id: issue.number,
title: issue.title,
description: issue.body,
milestone_number: issue.milestone&.number,
diff --git a/lib/gitlab/github_import/representation/lfs_object.rb b/lib/gitlab/github_import/representation/lfs_object.rb
index a4606173f49..41723759645 100644
--- a/lib/gitlab/github_import/representation/lfs_object.rb
+++ b/lib/gitlab/github_import/representation/lfs_object.rb
@@ -13,7 +13,12 @@ module Gitlab
# Builds a lfs_object
def self.from_api_response(lfs_object)
- new({ oid: lfs_object.oid, link: lfs_object.link, size: lfs_object.size })
+ new(
+ oid: lfs_object.oid,
+ link: lfs_object.link,
+ size: lfs_object.size,
+ github_id: lfs_object.oid
+ )
end
# Builds a new lfs_object using a Hash that was built from a JSON payload.
diff --git a/lib/gitlab/github_import/representation/pull_request.rb b/lib/gitlab/github_import/representation/pull_request.rb
index be192762e05..e4f54fcc833 100644
--- a/lib/gitlab/github_import/representation/pull_request.rb
+++ b/lib/gitlab/github_import/representation/pull_request.rb
@@ -25,6 +25,7 @@ module Gitlab
hash = {
iid: pr.number,
+ github_id: pr.number,
title: pr.title,
description: pr.body,
source_branch: pr.head.ref,
diff --git a/lib/gitlab/github_import/representation/user.rb b/lib/gitlab/github_import/representation/user.rb
index e00dcfca33d..d97b90b6291 100644
--- a/lib/gitlab/github_import/representation/user.rb
+++ b/lib/gitlab/github_import/representation/user.rb
@@ -15,7 +15,11 @@ module Gitlab
#
# user - An instance of `Sawyer::Resource` containing the user details.
def self.from_api_response(user)
- new(id: user.id, login: user.login)
+ new(
+ id: user.id,
+ github_id: user.id,
+ login: user.login
+ )
end
# Builds a user using a Hash that was built from a JSON payload.
diff --git a/lib/gitlab/github_import/user_finder.rb b/lib/gitlab/github_import/user_finder.rb
index 34d1231b9a5..8d584415202 100644
--- a/lib/gitlab/github_import/user_finder.rb
+++ b/lib/gitlab/github_import/user_finder.rb
@@ -63,7 +63,7 @@ module Gitlab
#
# user - An instance of `Gitlab::GithubImport::Representation::User`.
def user_id_for(user)
- find(user.id, user.login)
+ find(user.id, user.login) if user.present?
end
# Returns the GitLab ID for the given GitHub ID or username.
diff --git a/lib/gitlab/gl_repository/repo_type.rb b/lib/gitlab/gl_repository/repo_type.rb
index 4b1f4fcc2a2..05278b2dd35 100644
--- a/lib/gitlab/gl_repository/repo_type.rb
+++ b/lib/gitlab/gl_repository/repo_type.rb
@@ -81,4 +81,4 @@ module Gitlab
end
end
-Gitlab::GlRepository::RepoType.prepend_if_ee('EE::Gitlab::GlRepository::RepoType')
+Gitlab::GlRepository::RepoType.prepend_mod_with('Gitlab::GlRepository::RepoType')
diff --git a/lib/gitlab/golang.rb b/lib/gitlab/golang.rb
index 31b7a198b92..1b625a3a514 100644
--- a/lib/gitlab/golang.rb
+++ b/lib/gitlab/golang.rb
@@ -69,13 +69,13 @@ module Gitlab
# Error messages are based on the responses of proxy.golang.org
# Verify that the SHA fragment references a commit
- raise ArgumentError.new 'invalid pseudo-version: unknown commit' unless commit
+ raise ArgumentError, 'invalid pseudo-version: unknown commit' unless commit
# Require the SHA fragment to be 12 characters long
- raise ArgumentError.new 'invalid pseudo-version: revision is shorter than canonical' unless version.commit_id.length == 12
+ raise ArgumentError, 'invalid pseudo-version: revision is shorter than canonical' unless version.commit_id.length == 12
# Require the timestamp to match that of the commit
- raise ArgumentError.new 'invalid pseudo-version: does not match version-control timestamp' unless commit.committed_date.strftime('%Y%m%d%H%M%S') == version.timestamp
+ raise ArgumentError, 'invalid pseudo-version: does not match version-control timestamp' unless commit.committed_date.strftime('%Y%m%d%H%M%S') == version.timestamp
commit
end
diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb
index 08c17058fcb..1fd210c521e 100644
--- a/lib/gitlab/gon_helper.rb
+++ b/lib/gitlab/gon_helper.rb
@@ -80,4 +80,4 @@ module Gitlab
end
end
-Gitlab::GonHelper.prepend_if_ee('EE::Gitlab::GonHelper')
+Gitlab::GonHelper.prepend_mod_with('Gitlab::GonHelper')
diff --git a/lib/gitlab/gpg.rb b/lib/gitlab/gpg.rb
index b1494cf8cf2..3d9b06855ff 100644
--- a/lib/gitlab/gpg.rb
+++ b/lib/gitlab/gpg.rb
@@ -133,7 +133,7 @@ module Gitlab
Retriable.retriable(max_elapsed_time: cleanup_time, base_interval: 0.1, tries: 15) do
FileUtils.remove_entry(tmp_dir) if File.exist?(tmp_dir)
end
- rescue => e
+ rescue StandardError => e
raise CleanupError, e
end
diff --git a/lib/gitlab/grape_logging/loggers/route_logger.rb b/lib/gitlab/grape_logging/loggers/route_logger.rb
index f3146b4dfd9..7cbd2340e85 100644
--- a/lib/gitlab/grape_logging/loggers/route_logger.rb
+++ b/lib/gitlab/grape_logging/loggers/route_logger.rb
@@ -13,7 +13,7 @@ module Gitlab
return {} unless route
{ route: route }
- rescue
+ rescue StandardError
# endpoint.route calls env[Grape::Env::GRAPE_ROUTING_ARGS][:route_info]
# but env[Grape::Env::GRAPE_ROUTING_ARGS] is nil in the case of a 405 response
# so we're rescuing exceptions and bailing out
diff --git a/lib/gitlab/graphql/deprecation.rb b/lib/gitlab/graphql/deprecation.rb
index e0176e2d6e0..8b73eeb4e52 100644
--- a/lib/gitlab/graphql/deprecation.rb
+++ b/lib/gitlab/graphql/deprecation.rb
@@ -41,7 +41,7 @@ module Gitlab
parts = [
"#{deprecated_in(format: :markdown)}.",
reason_text,
- replacement.then { |r| "Use: `#{r}`." if r }
+ replacement.then { |r| "Use: [`#{r}`](##{r.downcase.tr('.', '')})." if r }
].compact
case context
diff --git a/lib/gitlab/graphql/docs/helper.rb b/lib/gitlab/graphql/docs/helper.rb
index f4173e26224..b598b605141 100644
--- a/lib/gitlab/graphql/docs/helper.rb
+++ b/lib/gitlab/graphql/docs/helper.rb
@@ -5,11 +5,52 @@ return if Rails.env.production?
module Gitlab
module Graphql
module Docs
+ # We assume a few things about the schema. We use the graphql-ruby gem, which enforces:
+ # - All mutations have a single input field named 'input'
+ # - All mutations have a payload type, named after themselves
+ # - All mutations have an input type, named after themselves
+ # If these things change, then some of this code will break. Such places
+ # are guarded with an assertion that our assumptions are not violated.
+ ViolatedAssumption = Class.new(StandardError)
+
+ SUGGESTED_ACTION = <<~MSG
+ We expect it to be impossible to violate our assumptions about
+ how mutation arguments work.
+
+ If that is not the case, then something has probably changed in the
+ way we generate our schema, perhaps in the library we use: graphql-ruby
+
+ Please ask for help in the #f_graphql or #backend channels.
+ MSG
+
+ CONNECTION_ARGS = %w[after before first last].to_set
+
+ FIELD_HEADER = <<~MD
+ #### Fields
+
+ | Name | Type | Description |
+ | ---- | ---- | ----------- |
+ MD
+
+ ARG_HEADER = <<~MD
+ # Arguments
+
+ | Name | Type | Description |
+ | ---- | ---- | ----------- |
+ MD
+
+ CONNECTION_NOTE = <<~MD
+ This field returns a [connection](#connections). It accepts the
+ four standard [pagination arguments](#connection-pagination-arguments):
+ `before: String`, `after: String`, `first: Int`, `last: Int`.
+ MD
+
# Helper with functions to be used by HAML templates
# This includes graphql-docs gem helpers class.
# You can check the included module on: https://github.com/gjtorikian/graphql-docs/blob/v1.6.0/lib/graphql-docs/helpers.rb
module Helper
include GraphQLDocs::Helpers
+ include Gitlab::Utils::StrongMemoize
def auto_generated_comment
<<-MD.strip_heredoc
@@ -30,44 +71,52 @@ module Gitlab
# Template methods:
# Methods that return chunks of Markdown for insertion into the document
- def render_name_and_description(object, owner: nil, level: 3)
- content = []
+ def render_full_field(field, heading_level: 3, owner: nil)
+ conn = connection?(field)
+ args = field[:arguments].reject { |arg| conn && CONNECTION_ARGS.include?(arg[:name]) }
+ arg_owner = [owner, field[:name]]
+
+ chunks = [
+ render_name_and_description(field, level: heading_level, owner: owner),
+ render_return_type(field),
+ render_input_type(field),
+ render_connection_note(field),
+ render_argument_table(heading_level, args, arg_owner),
+ render_return_fields(field, owner: owner)
+ ]
+
+ join(:block, chunks)
+ end
- content << "#{'#' * level} `#{object[:name]}`"
+ def render_argument_table(level, args, owner)
+ arg_header = ('#' * level) + ARG_HEADER
+ render_field_table(arg_header, args, owner)
+ end
- if object[:description].present?
- desc = object[:description].strip
- desc += '.' unless desc.ends_with?('.')
- end
+ def render_name_and_description(object, owner: nil, level: 3)
+ content = []
- if object[:is_deprecated]
- owner = Array.wrap(owner)
- deprecation = schema_deprecation(owner, object[:name])
- content << (deprecation&.original_description || desc)
- content << render_deprecation(object, owner, :block)
- else
- content << desc
- end
+ heading = '#' * level
+ name = [owner, object[:name]].compact.join('.')
- content.compact.join("\n\n")
- end
+ content << "#{heading} `#{name}`"
+ content << render_description(object, owner, :block)
- def render_return_type(query)
- "Returns #{render_field_type(query[:type])}.\n"
+ join(:block, content)
end
- def sorted_by_name(objects)
- return [] unless objects.present?
+ def render_object_fields(fields, owner:, level_bump: 0)
+ return if fields.blank?
- objects.sort_by { |o| o[:name] }
- end
+ (with_args, no_args) = fields.partition { |f| args?(f) }
+ type_name = owner[:name] if owner
+ header_prefix = '#' * level_bump
+ sections = [
+ render_simple_fields(no_args, type_name, header_prefix),
+ render_fields_with_arguments(with_args, type_name, header_prefix)
+ ]
- def render_field(field, owner)
- render_row(
- render_name(field, owner),
- render_field_type(field[:type]),
- render_description(field, owner, :inline)
- )
+ join(:block, sections)
end
def render_enum_value(enum, value)
@@ -82,104 +131,302 @@ module Gitlab
# Methods that return parts of the schema, or related information:
- # We are ignoring connections and built in types for now,
- # they should be added when queries are generated.
- def objects
- object_types = graphql_object_types.select do |object_type|
- !object_type[:name]["__"]
- end
+ def connection_object_types
+ objects.select { |t| t[:is_edge] || t[:is_connection] }
+ end
+
+ def object_types
+ objects.reject { |t| t[:is_edge] || t[:is_connection] || t[:is_payload] }
+ end
+
+ def interfaces
+ graphql_interface_types.map { |t| t.merge(fields: t[:fields] + t[:connections]) }
+ end
- object_types.each do |type|
- type[:fields] += type[:connections]
+ def fields_of(type_name)
+ graphql_operation_types
+ .find { |type| type[:name] == type_name }
+ .values_at(:fields, :connections)
+ .flatten
+ .then { |fields| sorted_by_name(fields) }
+ end
+
+ # Place the arguments of the input types on the mutation itself.
+ # see: `#input_types` - this method must not call `#input_types` to avoid mutual recursion
+ def mutations
+ @mutations ||= sorted_by_name(graphql_mutation_types).map do |t|
+ inputs = t[:input_fields]
+ input = inputs.first
+ name = t[:name]
+
+ assert!(inputs.one?, "Expected exactly 1 input field named #{name}. Found #{inputs.count} instead.")
+ assert!(input[:name] == 'input', "Expected the input of #{name} to be named 'input'")
+
+ input_type_name = input[:type][:name]
+ input_type = graphql_input_object_types.find { |t| t[:name] == input_type_name }
+ assert!(input_type.present?, "Cannot find #{input_type_name} for #{name}.input")
+
+ arguments = input_type[:input_fields]
+ seen_type!(input_type_name)
+ t.merge(arguments: arguments)
end
end
- def queries
- graphql_operation_types.find { |type| type[:name] == 'Query' }.to_h.values_at(:fields, :connections).flatten
+ # We assume that the mutations have been processed first, marking their
+ # inputs as `seen_type?`
+ def input_types
+ mutations # ensure that mutations have seen their inputs first
+ graphql_input_object_types.reject { |t| seen_type?(t[:name]) }
end
- # We ignore the built-in enum types.
+ # We ignore the built-in enum types, and sort values by name
def enums
- graphql_enum_types.select do |enum_type|
- !enum_type[:name].in?(%w[__DirectiveLocation __TypeKind])
- end
+ graphql_enum_types
+ .reject { |type| type[:values].empty? }
+ .reject { |enum_type| enum_type[:name].start_with?('__') }
+ .map { |type| type.merge(values: sorted_by_name(type[:values])) }
end
private # DO NOT CALL THESE METHODS IN TEMPLATES
# Template methods
+ def render_return_type(query)
+ return unless query[:type] # for example, mutations
+
+ "Returns #{render_field_type(query[:type])}."
+ end
+
+ def render_simple_fields(fields, type_name, header_prefix)
+ render_field_table(header_prefix + FIELD_HEADER, fields, type_name)
+ end
+
+ def render_fields_with_arguments(fields, type_name, header_prefix)
+ return if fields.empty?
+
+ level = 5 + header_prefix.length
+ sections = sorted_by_name(fields).map do |f|
+ render_full_field(f, heading_level: level, owner: type_name)
+ end
+
+ <<~MD.chomp
+ #{header_prefix}#### Fields with arguments
+
+ #{join(:block, sections)}
+ MD
+ end
+
+ def render_field_table(header, fields, owner)
+ return if fields.empty?
+
+ fields = sorted_by_name(fields)
+ header + join(:table, fields.map { |f| render_field(f, owner) })
+ end
+
+ def render_field(field, owner)
+ render_row(
+ render_name(field, owner),
+ render_field_type(field[:type]),
+ render_description(field, owner, :inline)
+ )
+ end
+
+ def render_return_fields(mutation, owner:)
+ fields = mutation[:return_fields]
+ return if fields.blank?
+
+ name = owner.to_s + mutation[:name]
+ render_object_fields(fields, owner: { name: name })
+ end
+
+ def render_connection_note(field)
+ return unless connection?(field)
+
+ CONNECTION_NOTE.chomp
+ end
+
def render_row(*values)
"| #{values.map { |val| val.to_s.squish }.join(' | ')} |"
end
def render_name(object, owner = nil)
rendered_name = "`#{object[:name]}`"
- rendered_name += ' **{warning-solid}**' if object[:is_deprecated]
- rendered_name
+ rendered_name += ' **{warning-solid}**' if deprecated?(object, owner)
+
+ return rendered_name unless owner
+
+ owner = Array.wrap(owner).join('')
+ id = (owner + object[:name]).downcase
+
+ %(<a id="#{id}"></a>) + rendered_name
end
# Returns the object description. If the object has been deprecated,
# the deprecation reason will be returned in place of the description.
def render_description(object, owner = nil, context = :block)
- owner = Array.wrap(owner)
- return render_deprecation(object, owner, context) if object[:is_deprecated]
- return if object[:description].blank?
+ if deprecated?(object, owner)
+ render_deprecation(object, owner, context)
+ else
+ render_description_of(object, owner, context)
+ end
+ end
+
+ def deprecated?(object, owner)
+ return true if object[:is_deprecated] # only populated for fields, not arguments!
+
+ key = [*Array.wrap(owner), object[:name]].join('.')
+ deprecations.key?(key)
+ end
+
+ def render_description_of(object, owner, context = nil)
+ desc = if object[:is_edge]
+ base = object[:name].chomp('Edge')
+ "The edge type for [`#{base}`](##{base.downcase})."
+ elsif object[:is_connection]
+ base = object[:name].chomp('Connection')
+ "The connection type for [`#{base}`](##{base.downcase})."
+ else
+ object[:description]&.strip
+ end
+
+ return if desc.blank?
- desc = object[:description].strip
desc += '.' unless desc.ends_with?('.')
+ see = doc_reference(object, owner)
+ desc += " #{see}" if see
+ desc += " (see [Connections](#connections))" if connection?(object) && context != :block
desc
end
+ def doc_reference(object, owner)
+ field = schema_field(owner, object[:name]) if owner
+ return unless field
+
+ ref = field.try(:doc_reference)
+ return if ref.blank?
+
+ parts = ref.to_a.map do |(title, url)|
+ "[#{title.strip}](#{url.strip})"
+ end
+
+ "See #{parts.join(', ')}."
+ end
+
def render_deprecation(object, owner, context)
+ buff = []
deprecation = schema_deprecation(owner, object[:name])
- return deprecation.markdown(context: context) if deprecation
- reason = object[:deprecation_reason] || 'Use of this is deprecated.'
- "**Deprecated:** #{reason}"
+ buff << (deprecation&.original_description || render_description_of(object, owner)) if context == :block
+ buff << if deprecation
+ deprecation.markdown(context: context)
+ else
+ "**Deprecated:** #{object[:deprecation_reason]}"
+ end
+
+ join(context, buff)
end
def render_field_type(type)
"[`#{type[:info]}`](##{type[:name].downcase})"
end
+ def join(context, chunks)
+ chunks.compact!
+ return if chunks.blank?
+
+ case context
+ when :block
+ chunks.join("\n\n")
+ when :inline
+ chunks.join(" ").squish.presence
+ when :table
+ chunks.join("\n")
+ end
+ end
+
# Queries
+ def sorted_by_name(objects)
+ return [] unless objects.present?
+
+ objects.sort_by { |o| o[:name] }
+ end
+
+ def connection?(field)
+ type_name = field.dig(:type, :name)
+ type_name.present? && type_name.ends_with?('Connection')
+ end
+
+ # We are ignoring connections and built in types for now,
+ # they should be added when queries are generated.
+ def objects
+ strong_memoize(:objects) do
+ mutations = schema.mutation&.fields&.keys&.to_set || []
+
+ graphql_object_types
+ .reject { |object_type| object_type[:name]["__"] || object_type[:name] == 'Subscription' } # We ignore introspection and subscription types.
+ .map do |type|
+ name = type[:name]
+ type.merge(
+ is_edge: name.ends_with?('Edge'),
+ is_connection: name.ends_with?('Connection'),
+ is_payload: name.ends_with?('Payload') && mutations.include?(name.chomp('Payload').camelcase(:lower)),
+ fields: type[:fields] + type[:connections]
+ )
+ end
+ end
+ end
+
+ def args?(field)
+ args = field[:arguments]
+ return false if args.blank?
+ return true unless connection?(field)
+
+ args.any? { |arg| CONNECTION_ARGS.exclude?(arg[:name]) }
+ end
+
# returns the deprecation information for a field or argument
# See: Gitlab::Graphql::Deprecation
def schema_deprecation(type_name, field_name)
- schema_member(type_name, field_name)&.deprecation
- end
-
- # Return a part of the schema.
- #
- # This queries the Schema by owner and name to find:
- #
- # - fields (e.g. `schema_member('Query', 'currentUser')`)
- # - arguments (e.g. `schema_member(['Query', 'project], 'fullPath')`)
- def schema_member(type_name, field_name)
- type_name = Array.wrap(type_name)
- if type_name.size == 2
- arg_name = field_name
- type_name, field_name = type_name
- else
- type_name = type_name.first
- arg_name = nil
- end
+ key = [*Array.wrap(type_name), field_name].join('.')
+ deprecations[key]
+ end
- return if type_name.nil? || field_name.nil?
+ def render_input_type(query)
+ input_field = query[:input_fields]&.first
+ return unless input_field
+ "Input type: `#{input_field[:type][:name]}`"
+ end
+
+ def schema_field(type_name, field_name)
type = schema.types[type_name]
return unless type && type.kind.fields?
- field = type.fields[field_name]
- return field if arg_name.nil?
+ type.fields[field_name]
+ end
+
+ def deprecations
+ strong_memoize(:deprecations) do
+ mapping = {}
+
+ schema.types.each do |type_name, type|
+ next unless type.kind.fields?
- args = field.arguments
- is_mutation = field.mutation && field.mutation <= ::Mutations::BaseMutation
- args = args['input'].type.unwrap.arguments if is_mutation
+ type.fields.each do |field_name, field|
+ mapping["#{type_name}.#{field_name}"] = field.try(:deprecation)
+ field.arguments.each do |arg_name, arg|
+ mapping["#{type_name}.#{field_name}.#{arg_name}"] = arg.try(:deprecation)
+ end
+ end
+ end
+
+ mapping.compact
+ end
+ end
- args[arg_name]
+ def assert!(claim, message)
+ raise ViolatedAssumption, "#{message}\n#{SUGGESTED_ACTION}" unless claim
end
end
end
diff --git a/lib/gitlab/graphql/docs/renderer.rb b/lib/gitlab/graphql/docs/renderer.rb
index 497567f9389..ae0898e6198 100644
--- a/lib/gitlab/graphql/docs/renderer.rb
+++ b/lib/gitlab/graphql/docs/renderer.rb
@@ -24,6 +24,7 @@ module Gitlab
@layout = Haml::Engine.new(File.read(template))
@parsed_schema = GraphQLDocs::Parser.new(schema.graphql_definition, {}).parse
@schema = schema
+ @seen = Set.new
end
def contents
@@ -37,6 +38,16 @@ module Gitlab
FileUtils.mkdir_p(@output_dir)
File.write(filename, contents)
end
+
+ private
+
+ def seen_type?(name)
+ @seen.include?(name)
+ end
+
+ def seen_type!(name)
+ @seen << name
+ end
end
end
end
diff --git a/lib/gitlab/graphql/docs/templates/default.md.haml b/lib/gitlab/graphql/docs/templates/default.md.haml
index fe73297d0d9..7d42fb3a9f8 100644
--- a/lib/gitlab/graphql/docs/templates/default.md.haml
+++ b/lib/gitlab/graphql/docs/templates/default.md.haml
@@ -17,7 +17,9 @@
Items (fields, enums, etc) that have been removed according to our [deprecation process](../index.md#deprecation-and-removal-process) can be found
in [Removed Items](../removed_items.md).
- <!-- vale gitlab.Spelling = NO -->
+ <!-- vale off -->
+ <!-- Docs linting disabled after this line. -->
+ <!-- See https://docs.gitlab.com/ee/development/documentation/testing.html#disable-vale-tests -->
\
:plain
@@ -26,17 +28,81 @@
The `Query` type contains the API's top-level entry points for all executable queries.
\
-- sorted_by_name(queries).each do |query|
- = render_name_and_description(query, owner: 'Query')
+- fields_of('Query').each do |field|
+ = render_full_field(field, heading_level: 3, owner: 'Query')
+ \
+
+:plain
+ ## `Mutation` type
+
+ The `Mutation` type contains all the mutations you can execute.
+
+ All mutations receive their arguments in a single input object named `input`, and all mutations
+ support at least a return field `errors` containing a list of error messages.
+
+ All input objects may have a `clientMutationId: String` field, identifying the mutation.
+
+ For example:
+
+ ```graphql
+ mutation($id: NoteableID!, $body: String!) {
+ createNote(input: { noteableId: $id, body: $body }) {
+ errors
+ }
+ }
+ ```
+\
+
+- mutations.each do |field|
+ = render_full_field(field, heading_level: 3, owner: 'Mutation')
+ \
+
+:plain
+ ## Connections
+
+ Some types in our schema are `Connection` types - they represent a paginated
+ collection of edges between two nodes in the graph. These follow the
+ [Relay cursor connections specification](https://relay.dev/graphql/connections.htm).
+
+ ### Pagination arguments {#connection-pagination-arguments}
+
+ All connection fields support the following pagination arguments:
+
+ | Name | Type | Description |
+ |------|------|-------------|
+ | `after` | [`String`](#string) | Returns the elements in the list that come after the specified cursor. |
+ | `before` | [`String`](#string) | Returns the elements in the list that come before the specified cursor. |
+ | `first` | [`Int`](#int) | Returns the first _n_ elements from the list. |
+ | `last` | [`Int`](#int) | Returns the last _n_ elements from the list. |
+
+ Since these arguments are common to all connection fields, they are not repeated for each connection.
+
+ ### Connection fields
+
+ All connections have at least the following fields:
+
+ | Name | Type | Description |
+ |------|------|-------------|
+ | `pageInfo` | [`PageInfo!`](#pageinfo) | Pagination information. |
+ | `edges` | `[edge!]` | The edges. |
+ | `nodes` | `[item!]` | The items in the current page. |
+
+ The precise type of `Edge` and `Item` depends on the kind of connection. A
+ [`ProjectConnection`](#projectconnection) will have nodes that have the type
+ [`[Project!]`](#project), and edges that have the type [`ProjectEdge`](#projectedge).
+
+ ### Connection types
+
+ Some of the types in the schema exist solely to model connections. Each connection
+ has a distinct, named type, with a distinct named edge type. These are listed separately
+ below.
+\
+
+- connection_object_types.each do |type|
+ = render_name_and_description(type, level: 4)
+ \
+ = render_object_fields(type[:fields], owner: type, level_bump: 1)
\
- = render_return_type(query)
- - unless query[:arguments].empty?
- ~ "#### Arguments\n"
- ~ "| Name | Type | Description |"
- ~ "| ---- | ---- | ----------- |"
- - sorted_by_name(query[:arguments]).each do |argument|
- = render_field(argument, query[:type][:name])
- \
:plain
## Object types
@@ -44,22 +110,20 @@
Object types represent the resources that the GitLab GraphQL API can return.
They contain _fields_. Each field has its own type, which will either be one of the
basic GraphQL [scalar types](https://graphql.org/learn/schema/#scalar-types)
- (e.g.: `String` or `Boolean`) or other object types.
+ (e.g.: `String` or `Boolean`) or other object types. Fields may have arguments.
+ Fields with arguments are exactly like top-level queries, and are listed beneath
+ the table of fields for each object type.
For more information, see
[Object Types and Fields](https://graphql.org/learn/schema/#object-types-and-fields)
on `graphql.org`.
\
-- objects.each do |type|
- - unless type[:fields].empty?
- = render_name_and_description(type)
- \
- ~ "| Field | Type | Description |"
- ~ "| ----- | ---- | ----------- |"
- - sorted_by_name(type[:fields]).each do |field|
- = render_field(field, type[:name])
- \
+- object_types.each do |type|
+ = render_name_and_description(type)
+ \
+ = render_object_fields(type[:fields], owner: type)
+ \
:plain
## Enumeration types
@@ -73,14 +137,13 @@
\
- enums.each do |enum|
- - unless enum[:values].empty?
- = render_name_and_description(enum)
- \
- ~ "| Value | Description |"
- ~ "| ----- | ----------- |"
- - sorted_by_name(enum[:values]).each do |value|
- = render_enum_value(enum, value)
- \
+ = render_name_and_description(enum)
+ \
+ ~ "| Value | Description |"
+ ~ "| ----- | ----------- |"
+ - enum[:values].each do |value|
+ = render_enum_value(enum, value)
+ \
:plain
## Scalar types
@@ -133,7 +196,7 @@
### Interfaces
\
-- graphql_interface_types.each do |type|
+- interfaces.each do |type|
= render_name_and_description(type, level: 4)
\
Implementations:
@@ -141,8 +204,21 @@
- type[:implemented_by].each do |type_name|
~ "- [`#{type_name}`](##{type_name.downcase})"
\
- ~ "| Field | Type | Description |"
- ~ "| ----- | ---- | ----------- |"
- - sorted_by_name(type[:fields] + type[:connections]).each do |field|
- = render_field(field, type[:name])
+ = render_object_fields(type[:fields], owner: type, level_bump: 1)
+ \
+
+:plain
+ ## Input types
+
+ Types that may be used as arguments (all scalar types may also
+ be used as arguments).
+
+ Only general use input types are listed here. For mutation input types,
+ see the associated mutation type above.
+\
+
+- input_types.each do |type|
+ = render_name_and_description(type)
+ \
+ = render_argument_table(3, type[:input_fields], type[:name])
\
diff --git a/lib/gitlab/graphql/pagination/keyset/connection.rb b/lib/gitlab/graphql/pagination/keyset/connection.rb
index e525996ec10..61903c566f0 100644
--- a/lib/gitlab/graphql/pagination/keyset/connection.rb
+++ b/lib/gitlab/graphql/pagination/keyset/connection.rb
@@ -114,7 +114,7 @@ module Gitlab
def limited_nodes
strong_memoize(:limited_nodes) do
if first && last
- raise Gitlab::Graphql::Errors::ArgumentError.new("Can only provide either `first` or `last`, not both")
+ raise Gitlab::Graphql::Errors::ArgumentError, "Can only provide either `first` or `last`, not both"
end
if last
@@ -158,7 +158,7 @@ module Gitlab
def ordered_items
strong_memoize(:ordered_items) do
unless items.primary_key.present?
- raise ArgumentError.new('Relation must have a primary key')
+ raise ArgumentError, 'Relation must have a primary key'
end
list = OrderInfo.build_order_list(items)
diff --git a/lib/gitlab/graphql/pagination/keyset/generic_keyset_pagination.rb b/lib/gitlab/graphql/pagination/keyset/generic_keyset_pagination.rb
index 318c6e1734f..f1b74999897 100644
--- a/lib/gitlab/graphql/pagination/keyset/generic_keyset_pagination.rb
+++ b/lib/gitlab/graphql/pagination/keyset/generic_keyset_pagination.rb
@@ -10,6 +10,8 @@ module Gitlab
extend ActiveSupport::Concern
def ordered_items
+ raise ArgumentError, 'Relation must have a primary key' unless items.primary_key.present?
+
return super unless Gitlab::Pagination::Keyset::Order.keyset_aware?(items)
items
@@ -40,6 +42,17 @@ module Gitlab
sliced = slice_nodes(sliced, after, :after) if after.present?
sliced
end
+
+ def items
+ original_items = super
+ return original_items if Gitlab::Pagination::Keyset::Order.keyset_aware?(original_items) || Feature.disabled?(:new_graphql_keyset_pagination)
+
+ strong_memoize(:generic_keyset_pagination_items) do
+ rebuilt_items_with_keyset_order, success = Gitlab::Pagination::Keyset::SimpleOrderBuilder.build(original_items)
+
+ success ? rebuilt_items_with_keyset_order : original_items
+ end
+ end
end
end
end
diff --git a/lib/gitlab/graphql/pagination/keyset/order_info.rb b/lib/gitlab/graphql/pagination/keyset/order_info.rb
index 0494329bfd9..57e85ebe7f6 100644
--- a/lib/gitlab/graphql/pagination/keyset/order_info.rb
+++ b/lib/gitlab/graphql/pagination/keyset/order_info.rb
@@ -36,24 +36,24 @@ module Gitlab
def self.validate_ordering(relation, order_list)
if order_list.empty?
- raise ArgumentError.new('A minimum of 1 ordering field is required')
+ raise ArgumentError, 'A minimum of 1 ordering field is required'
end
if order_list.count > 2
# Keep in mind an order clause for primary key is added if one is not present
# lib/gitlab/graphql/pagination/keyset/connection.rb:97
- raise ArgumentError.new('A maximum of 2 ordering fields are allowed')
+ raise ArgumentError, 'A maximum of 2 ordering fields are allowed'
end
# make sure the last ordering field is non-nullable
attribute_name = order_list.last&.attribute_name
if relation.columns_hash[attribute_name].null
- raise ArgumentError.new("Column `#{attribute_name}` must not allow NULL")
+ raise ArgumentError, "Column `#{attribute_name}` must not allow NULL"
end
if order_list.last.attribute_name != relation.primary_key
- raise ArgumentError.new("Last ordering field must be the primary key, `#{relation.primary_key}`")
+ raise ArgumentError, "Last ordering field must be the primary key, `#{relation.primary_key}`"
end
end
@@ -121,4 +121,4 @@ module Gitlab
end
end
-Gitlab::Graphql::Pagination::Keyset::OrderInfo.prepend_if_ee('EE::Gitlab::Graphql::Pagination::Keyset::OrderInfo')
+Gitlab::Graphql::Pagination::Keyset::OrderInfo.prepend_mod_with('Gitlab::Graphql::Pagination::Keyset::OrderInfo')
diff --git a/lib/gitlab/graphql/pagination/keyset/query_builder.rb b/lib/gitlab/graphql/pagination/keyset/query_builder.rb
index ee9c902c735..a2f53ae83dd 100644
--- a/lib/gitlab/graphql/pagination/keyset/query_builder.rb
+++ b/lib/gitlab/graphql/pagination/keyset/query_builder.rb
@@ -12,7 +12,7 @@ module Gitlab
@before_or_after = before_or_after
if order_list.empty?
- raise ArgumentError.new('No ordering scopes have been supplied')
+ raise ArgumentError, 'No ordering scopes have been supplied'
end
end
@@ -49,7 +49,7 @@ module Gitlab
end
if order_list.count == 1 && attr_values.first.nil?
- raise Gitlab::Graphql::Errors::ArgumentError.new('Before/after cursor invalid: `nil` was provided as only sortable value')
+ raise Gitlab::Graphql::Errors::ArgumentError, 'Before/after cursor invalid: `nil` was provided as only sortable value'
end
if order_list.count == 1 || attr_values.first.present?
diff --git a/lib/gitlab/graphql/present.rb b/lib/gitlab/graphql/present.rb
index fdaf075eb25..3608cb4c0e8 100644
--- a/lib/gitlab/graphql/present.rb
+++ b/lib/gitlab/graphql/present.rb
@@ -10,14 +10,14 @@ module Gitlab
end
def self.presenter_class
- @presenter_class
+ @presenter_class || superclass.try(:presenter_class)
end
def self.present(object, attrs)
- klass = @presenter_class
+ klass = presenter_class
return object if !klass || object.is_a?(klass)
- @presenter_class.new(object, **attrs)
+ klass.new(object, **attrs)
end
end
diff --git a/lib/gitlab/graphql/present/field_extension.rb b/lib/gitlab/graphql/present/field_extension.rb
index 2e211b70d35..050a3a276ea 100644
--- a/lib/gitlab/graphql/present/field_extension.rb
+++ b/lib/gitlab/graphql/present/field_extension.rb
@@ -13,7 +13,8 @@ module Gitlab
# inner Schema::Object#object. This depends on whether the field
# has a @resolver_proc or not.
if object.is_a?(::Types::BaseObject)
- object.present(field.owner, attrs)
+ type = field.owner.kind.abstract? ? object.class : field.owner
+ object.present(type, attrs)
yield(object, arguments)
else
# This is the legacy code-path, hit if the field has a @resolver_proc
diff --git a/lib/gitlab/graphql/queries.rb b/lib/gitlab/graphql/queries.rb
index 74f55abccbc..5d3a9245427 100644
--- a/lib/gitlab/graphql/queries.rb
+++ b/lib/gitlab/graphql/queries.rb
@@ -264,7 +264,7 @@ module Gitlab
definitions = []
::Find.find(root.to_s) do |path|
- definitions << Definition.new(path, fragments) if query?(path)
+ definitions << Definition.new(path, fragments) if query_for_gitlab_schema?(path)
end
definitions
@@ -288,10 +288,11 @@ module Gitlab
@known_failures.fetch('filenames', []).any? { |known_failure| path.to_s.ends_with?(known_failure) }
end
- def self.query?(path)
+ def self.query_for_gitlab_schema?(path)
path.ends_with?('.graphql') &&
!path.ends_with?('.fragment.graphql') &&
- !path.ends_with?('typedefs.graphql')
+ !path.ends_with?('typedefs.graphql') &&
+ !/.*\.customer\.(query|mutation)\.graphql$/.match?(path)
end
end
end
diff --git a/lib/gitlab/graphql/query_analyzers/logger_analyzer.rb b/lib/gitlab/graphql/query_analyzers/logger_analyzer.rb
index c6f22e0bd4f..b8d2f5b0f29 100644
--- a/lib/gitlab/graphql/query_analyzers/logger_analyzer.rb
+++ b/lib/gitlab/graphql/query_analyzers/logger_analyzer.rb
@@ -16,7 +16,7 @@ module Gitlab
query_string: query.query_string,
variables: variables
})
- rescue => e
+ rescue StandardError => e
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)
default_initial_values(query)
end
@@ -41,7 +41,7 @@ module Gitlab
RequestStore.store[:graphql_logs] ||= []
RequestStore.store[:graphql_logs] << memo
GraphqlLogger.info(memo.except!(:time_started, :query))
- rescue => e
+ rescue StandardError => e
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)
end
diff --git a/lib/gitlab/graphql/variables.rb b/lib/gitlab/graphql/variables.rb
index 1c6fb011012..e17ca56d022 100644
--- a/lib/gitlab/graphql/variables.rb
+++ b/lib/gitlab/graphql/variables.rb
@@ -32,7 +32,7 @@ module Gitlab
raise Invalid, "Unexpected parameter: #{ambiguous_param}"
end
rescue JSON::ParserError => e
- raise Invalid.new(e)
+ raise Invalid, e
end
end
end
diff --git a/lib/gitlab/group_search_results.rb b/lib/gitlab/group_search_results.rb
index dd872caee0e..4eea96f8344 100644
--- a/lib/gitlab/group_search_results.rb
+++ b/lib/gitlab/group_search_results.rb
@@ -39,4 +39,4 @@ module Gitlab
end
end
-Gitlab::GroupSearchResults.prepend_if_ee('EE::Gitlab::GroupSearchResults')
+Gitlab::GroupSearchResults.prepend_mod_with('Gitlab::GroupSearchResults')
diff --git a/lib/gitlab/hashed_storage/migrator.rb b/lib/gitlab/hashed_storage/migrator.rb
index b57560544c8..912e2ee99e9 100644
--- a/lib/gitlab/hashed_storage/migrator.rb
+++ b/lib/gitlab/hashed_storage/migrator.rb
@@ -66,7 +66,7 @@ module Gitlab
Gitlab::AppLogger.info "Starting storage migration of #{project.full_path} (ID=#{project.id})..."
project.migrate_to_hashed_storage!
- rescue => err
+ rescue StandardError => err
Gitlab::AppLogger.error("#{err.message} migrating storage of #{project.full_path} (ID=#{project.id}), trace - #{err.backtrace}")
end
@@ -77,7 +77,7 @@ module Gitlab
Gitlab::AppLogger.info "Starting storage rollback of #{project.full_path} (ID=#{project.id})..."
project.rollback_to_legacy_storage!
- rescue => err
+ rescue StandardError => err
Gitlab::AppLogger.error("#{err.message} rolling-back storage of #{project.full_path} (ID=#{project.id}), trace - #{err.backtrace}")
end
diff --git a/lib/gitlab/health_checks/probes/collection.rb b/lib/gitlab/health_checks/probes/collection.rb
index b34e4273d85..76ad1c84214 100644
--- a/lib/gitlab/health_checks/probes/collection.rb
+++ b/lib/gitlab/health_checks/probes/collection.rb
@@ -20,7 +20,7 @@ module Gitlab
success ? 200 : 503,
status(success).merge(payload(readiness))
)
- rescue => e
+ rescue StandardError => e
exception_payload = { message: "#{e.class} : #{e.message}" }
Probes::Status.new(
diff --git a/lib/gitlab/health_checks/simple_abstract_check.rb b/lib/gitlab/health_checks/simple_abstract_check.rb
index ae99768b7b4..432d5d5e5ea 100644
--- a/lib/gitlab/health_checks/simple_abstract_check.rb
+++ b/lib/gitlab/health_checks/simple_abstract_check.rb
@@ -16,7 +16,7 @@ module Gitlab
else
HealthChecks::Result.new(name, false, "unexpected #{human_name} check result: #{check_result}")
end
- rescue => e
+ rescue StandardError => e
HealthChecks::Result.new(name, false, "unexpected #{human_name} check result: #{e}")
end
diff --git a/lib/gitlab/highlight.rb b/lib/gitlab/highlight.rb
index 765d3dfca56..e4857280969 100644
--- a/lib/gitlab/highlight.rb
+++ b/lib/gitlab/highlight.rb
@@ -4,13 +4,20 @@ module Gitlab
class Highlight
TIMEOUT_BACKGROUND = 30.seconds
TIMEOUT_FOREGROUND = 1.5.seconds
- MAXIMUM_TEXT_HIGHLIGHT_SIZE = 512.kilobytes
def self.highlight(blob_name, blob_content, language: nil, plain: false)
new(blob_name, blob_content, language: language)
.highlight(blob_content, continue: false, plain: plain)
end
+ def self.too_large?(size)
+ return false unless size.to_i > Gitlab.config.extra['maximum_text_highlight_size_kilobytes']
+
+ over_highlight_size_limit.increment(source: "text highlighter") if Feature.enabled?(:track_file_size_over_highlight_limit)
+
+ true
+ end
+
attr_reader :blob_name
def initialize(blob_name, blob_content, language: nil)
@@ -23,7 +30,7 @@ module Gitlab
def highlight(text, continue: false, plain: false, context: {})
@context = context
- plain ||= text.length > MAXIMUM_TEXT_HIGHLIGHT_SIZE
+ plain ||= self.class.too_large?(text.length)
highlighted_text = highlight_text(text, continue: continue, plain: plain)
highlighted_text = link_dependencies(text, highlighted_text) if blob_name
@@ -65,9 +72,11 @@ module Gitlab
tokens = lexer.lex(text, continue: continue)
Timeout.timeout(timeout_time) { @formatter.format(tokens, context.merge(tag: tag)).html_safe }
rescue Timeout::Error => e
+ add_highlight_timeout_metric
+
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)
highlight_plain(text)
- rescue
+ rescue StandardError
highlight_plain(text)
end
@@ -78,5 +87,25 @@ module Gitlab
def link_dependencies(text, highlighted_text)
Gitlab::DependencyLinker.link(blob_name, text, highlighted_text)
end
+
+ def add_highlight_timeout_metric
+ return unless Feature.enabled?(:track_highlight_timeouts)
+
+ highlight_timeout.increment(source: Gitlab::Runtime.sidekiq? ? "background" : "foreground")
+ end
+
+ def highlight_timeout
+ @highlight_timeout ||= Gitlab::Metrics.counter(
+ :highlight_timeout,
+ 'Counts the times highlights have timed out'
+ )
+ end
+
+ def self.over_highlight_size_limit
+ @over_highlight_size_limit ||= Gitlab::Metrics.counter(
+ :over_highlight_size_limit,
+ 'Count the times files have been over the highlight size limit'
+ )
+ end
end
end
diff --git a/lib/gitlab/hook_data/group_member_builder.rb b/lib/gitlab/hook_data/group_member_builder.rb
index 32cfd032ffe..2998550a4b5 100644
--- a/lib/gitlab/hook_data/group_member_builder.rb
+++ b/lib/gitlab/hook_data/group_member_builder.rb
@@ -62,4 +62,4 @@ module Gitlab
end
end
-Gitlab::HookData::GroupMemberBuilder.prepend_if_ee('EE::Gitlab::HookData::GroupMemberBuilder')
+Gitlab::HookData::GroupMemberBuilder.prepend_mod_with('Gitlab::HookData::GroupMemberBuilder')
diff --git a/lib/gitlab/hook_data/issue_builder.rb b/lib/gitlab/hook_data/issue_builder.rb
index f38012c9804..d5595e80bdf 100644
--- a/lib/gitlab/hook_data/issue_builder.rb
+++ b/lib/gitlab/hook_data/issue_builder.rb
@@ -58,4 +58,4 @@ module Gitlab
end
end
-Gitlab::HookData::IssueBuilder.prepend_if_ee('EE::Gitlab::HookData::IssueBuilder')
+Gitlab::HookData::IssueBuilder.prepend_mod_with('Gitlab::HookData::IssueBuilder')
diff --git a/lib/gitlab/hook_data/key_builder.rb b/lib/gitlab/hook_data/key_builder.rb
new file mode 100644
index 00000000000..8eaf4dfd762
--- /dev/null
+++ b/lib/gitlab/hook_data/key_builder.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module HookData
+ class KeyBuilder < BaseBuilder
+ alias_method :key, :object
+
+ # Sample data
+ # {
+ # event_name: "key_create",
+ # created_at: "2021-04-19T06:13:24Z",
+ # updated_at: "2021-04-19T06:13:24Z",
+ # key: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQClDn/5BaESHlSb3NxQtiUc0BXgK6lsqdAUIdS3lwZ2gbACDhtoLYnc+qhZ4b8gWzE+2A8RmkvLe98T7noRoW4DAYs67NSqMs/kXd2ESPNV8qqv0u7tCxPz+c7DaYp2oC/avlxVQ2AeULZLCEwalYZ7irde0EZMeTwNIRu5s88gOw== dummy@gitlab.com",
+ # id: 1,
+ # username: "johndoe"
+ # }
+
+ def build(event)
+ [
+ event_data(event),
+ timestamps_data,
+ key_data,
+ user_data
+ ].reduce(:merge)
+ end
+
+ private
+
+ def key_data
+ {
+ key: key.key,
+ id: key.id
+ }
+ end
+
+ def user_data
+ user = key.user
+ return {} unless user
+
+ {
+ username: user.username
+ }
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/hook_data/project_builder.rb b/lib/gitlab/hook_data/project_builder.rb
new file mode 100644
index 00000000000..65c237f743f
--- /dev/null
+++ b/lib/gitlab/hook_data/project_builder.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module HookData
+ class ProjectBuilder < BaseBuilder
+ alias_method :project, :object
+
+ # Sample data
+ # {
+ # event_name: "project_rename",
+ # created_at: "2021-04-19T07:05:36Z",
+ # updated_at: "2021-04-19T07:05:36Z",
+ # name: "my_project",
+ # path: "my_project",
+ # path_with_namespace: "namespace2/my_project",
+ # project_id: 1,
+ # owner_name: "John",
+ # owner_email: "user1@example.org",
+ # project_visibility: "internal",
+ # old_path_with_namespace: "old-path-with-namespace"
+ # }
+
+ def build(event)
+ [
+ event_data(event),
+ timestamps_data,
+ project_data,
+ event_specific_project_data(event)
+ ].reduce(:merge)
+ end
+
+ private
+
+ def project_data
+ owner = project.owner
+
+ {
+ name: project.name,
+ path: project.path,
+ path_with_namespace: project.full_path,
+ project_id: project.id,
+ owner_name: owner.name,
+ owner_email: owner.respond_to?(:email) ? owner.email : "",
+ project_visibility: project.visibility.downcase
+ }
+ end
+
+ def event_specific_project_data(event)
+ return {} unless event == :rename || event == :transfer
+
+ {
+ old_path_with_namespace: project.old_path_with_namespace
+ }
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/hook_data/user_builder.rb b/lib/gitlab/hook_data/user_builder.rb
index 537245e948f..54f03b863e5 100644
--- a/lib/gitlab/hook_data/user_builder.rb
+++ b/lib/gitlab/hook_data/user_builder.rb
@@ -50,4 +50,4 @@ module Gitlab
end
end
-Gitlab::HookData::UserBuilder.prepend_if_ee('EE::Gitlab::HookData::UserBuilder')
+Gitlab::HookData::UserBuilder.prepend_mod_with('Gitlab::HookData::UserBuilder')
diff --git a/lib/gitlab/i18n.rb b/lib/gitlab/i18n.rb
index 3b19ae3d7ff..023dbd1c601 100644
--- a/lib/gitlab/i18n.rb
+++ b/lib/gitlab/i18n.rb
@@ -4,20 +4,6 @@ module Gitlab
module I18n
extend self
- # Languages with less then 2% of available translations will not
- # be available in the UI.
- # https://gitlab.com/gitlab-org/gitlab/-/issues/221012
- NOT_AVAILABLE_IN_UI = %w[
- fil_PH
- pl_PL
- nl_NL
- id_ID
- cs_CZ
- bg
- eo
- gl_ES
- ].freeze
-
AVAILABLE_LANGUAGES = {
'bg' => 'Bulgarian - български',
'cs_CZ' => 'Czech - čeština',
@@ -42,9 +28,49 @@ module Gitlab
'zh_HK' => 'Chinese, Traditional (Hong Kong) - 繁體中文 (香港)',
'zh_TW' => 'Chinese, Traditional (Taiwan) - 繁體中文 (台灣)'
}.freeze
+ private_constant :AVAILABLE_LANGUAGES
+
+ # Languages with less then MINIMUM_TRANSLATION_LEVEL% of available translations will not
+ # be available in the UI.
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/221012
+ MINIMUM_TRANSLATION_LEVEL = 2
+
+ # Currently monthly updated manually by ~group::import PM.
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/18923
+ TRANSLATION_LEVELS = {
+ 'bg' => 1,
+ 'cs_CZ' => 1,
+ 'de' => 19,
+ 'en' => 100,
+ 'eo' => 1,
+ 'es' => 41,
+ 'fil_PH' => 1,
+ 'fr' => 14,
+ 'gl_ES' => 1,
+ 'id_ID' => 0,
+ 'it' => 2,
+ 'ja' => 45,
+ 'ko' => 14,
+ 'nl_NL' => 1,
+ 'pl_PL' => 1,
+ 'pt_BR' => 22,
+ 'ru' => 32,
+ 'tr_TR' => 17,
+ 'uk' => 43,
+ 'zh_CN' => 72,
+ 'zh_HK' => 3,
+ 'zh_TW' => 4
+ }.freeze
+ private_constant :TRANSLATION_LEVELS
def selectable_locales
- AVAILABLE_LANGUAGES.reject { |key, _value| NOT_AVAILABLE_IN_UI.include? key }
+ AVAILABLE_LANGUAGES.reject do |code, _name|
+ percentage_translated_for(code) < MINIMUM_TRANSLATION_LEVEL
+ end
+ end
+
+ def percentage_translated_for(code)
+ TRANSLATION_LEVELS.fetch(code, 0)
end
def available_locales
diff --git a/lib/gitlab/import_export.rb b/lib/gitlab/import_export.rb
index c4867746b0f..231f2a977c0 100644
--- a/lib/gitlab/import_export.rb
+++ b/lib/gitlab/import_export.rb
@@ -99,11 +99,19 @@ module Gitlab
def group_config_file
Rails.root.join('lib/gitlab/import_export/group/import_export.yml')
end
+
+ def group_wiki_repo_bundle_filename(group_id)
+ "#{group_id}.wiki.bundle"
+ end
+
+ def group_wiki_repo_bundle_path(shared, filename)
+ File.join(shared.export_path, 'repositories', filename)
+ end
+
+ def group_wiki_repo_bundle_full_path(shared, group_id)
+ group_wiki_repo_bundle_path(shared, group_wiki_repo_bundle_filename(group_id))
+ end
end
end
-Gitlab::ImportExport.prepend_if_ee('EE::Gitlab::ImportExport')
-
-# The methods in `Gitlab::ImportExport::GroupHelper` should be available as both
-# instance and class methods.
-Gitlab::ImportExport.extend_if_ee('Gitlab::ImportExport::GroupHelper')
+Gitlab::ImportExport.prepend_mod
diff --git a/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb b/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb
index b30258123d4..b43d0a0c3eb 100644
--- a/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb
+++ b/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy.rb
@@ -42,7 +42,7 @@ module Gitlab
strategy_execute
true
- rescue => e
+ rescue StandardError => e
project.import_export_shared.error(e)
false
ensure
diff --git a/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb b/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb
index e2dba831661..1e8009d29c2 100644
--- a/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb
+++ b/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb
@@ -28,7 +28,7 @@ module Gitlab
def handle_response_error(response)
unless response.success?
- raise StrategyError.new("Error uploading the project. Code #{response.code}: #{response.message}")
+ raise StrategyError, "Error uploading the project. Code #{response.code}: #{response.message}"
end
end
diff --git a/lib/gitlab/import_export/after_export_strategy_builder.rb b/lib/gitlab/import_export/after_export_strategy_builder.rb
index 37394f46a99..d7b30f46903 100644
--- a/lib/gitlab/import_export/after_export_strategy_builder.rb
+++ b/lib/gitlab/import_export/after_export_strategy_builder.rb
@@ -12,7 +12,7 @@ module Gitlab
klass = strategy_klass.constantize rescue nil
unless klass && klass < AfterExportStrategies::BaseAfterExportStrategy
- raise StrategyNotFoundError.new("Strategy #{strategy_klass} not found")
+ raise StrategyNotFoundError, "Strategy #{strategy_klass} not found"
end
klass.new(**attributes.symbolize_keys)
diff --git a/lib/gitlab/import_export/attributes_finder.rb b/lib/gitlab/import_export/attributes_finder.rb
index 1e98595bb07..4abc3da1190 100644
--- a/lib/gitlab/import_export/attributes_finder.rb
+++ b/lib/gitlab/import_export/attributes_finder.rb
@@ -3,7 +3,7 @@
module Gitlab
module ImportExport
class AttributesFinder
- attr_reader :tree, :included_attributes, :excluded_attributes, :methods, :preloads
+ attr_reader :tree, :included_attributes, :excluded_attributes, :methods, :preloads, :export_reorders
def initialize(config:)
@tree = config[:tree] || {}
@@ -11,6 +11,7 @@ module Gitlab
@excluded_attributes = config[:excluded_attributes] || {}
@methods = config[:methods] || {}
@preloads = config[:preloads] || {}
+ @export_reorders = config[:export_reorders] || {}
end
def find_root(model_key)
@@ -33,7 +34,8 @@ module Gitlab
except: @excluded_attributes[model_key],
methods: @methods[model_key],
include: resolve_model_tree(model_tree),
- preload: resolve_preloads(model_key, model_tree)
+ preload: resolve_preloads(model_key, model_tree),
+ export_reorder: @export_reorders[model_key]
}.compact
end
diff --git a/lib/gitlab/import_export/avatar_restorer.rb b/lib/gitlab/import_export/avatar_restorer.rb
index be1b97bd7a7..01ff99798d5 100644
--- a/lib/gitlab/import_export/avatar_restorer.rb
+++ b/lib/gitlab/import_export/avatar_restorer.rb
@@ -13,7 +13,7 @@ module Gitlab
@project.avatar = File.open(avatar_export_file)
@project.save!
- rescue => e
+ rescue StandardError => e
@shared.error(e)
false
end
diff --git a/lib/gitlab/import_export/avatar_saver.rb b/lib/gitlab/import_export/avatar_saver.rb
index 47ca898c690..7534ab5a9ce 100644
--- a/lib/gitlab/import_export/avatar_saver.rb
+++ b/lib/gitlab/import_export/avatar_saver.rb
@@ -16,7 +16,7 @@ module Gitlab
shared: @shared,
relative_export_path: 'avatar'
).save
- rescue => e
+ rescue StandardError => e
@shared.error(e)
false
end
diff --git a/lib/gitlab/import_export/base/relation_factory.rb b/lib/gitlab/import_export/base/relation_factory.rb
index 05a4a8f4c93..959ece4b903 100644
--- a/lib/gitlab/import_export/base/relation_factory.rb
+++ b/lib/gitlab/import_export/base/relation_factory.rb
@@ -44,8 +44,9 @@ module Gitlab
relation_name.to_s.constantize
end
- def initialize(relation_sym:, relation_hash:, members_mapper:, object_builder:, user:, importable:, excluded_keys: [])
+ def initialize(relation_sym:, relation_index:, relation_hash:, members_mapper:, object_builder:, user:, importable:, excluded_keys: [])
@relation_name = self.class.overrides[relation_sym]&.to_sym || relation_sym
+ @relation_index = relation_index
@relation_hash = relation_hash.except('noteable_id')
@members_mapper = members_mapper
@object_builder = object_builder
diff --git a/lib/gitlab/import_export/command_line_util.rb b/lib/gitlab/import_export/command_line_util.rb
index 2f8769e261d..ace9d83dc9a 100644
--- a/lib/gitlab/import_export/command_line_util.rb
+++ b/lib/gitlab/import_export/command_line_util.rb
@@ -14,6 +14,19 @@ module Gitlab
untar_with_options(archive: archive, dir: dir, options: 'zxf')
end
+ def gzip(dir:, filename:)
+ filepath = File.join(dir, filename)
+ cmd = %W(gzip #{filepath})
+
+ _, status = Gitlab::Popen.popen(cmd)
+
+ if status == 0
+ status
+ else
+ raise Gitlab::ImportExport::Error.file_compression_error
+ end
+ end
+
def mkdir_p(path)
FileUtils.mkdir_p(path, mode: DEFAULT_DIR_MODE)
FileUtils.chmod(DEFAULT_DIR_MODE, path)
diff --git a/lib/gitlab/import_export/decompressed_archive_size_validator.rb b/lib/gitlab/import_export/decompressed_archive_size_validator.rb
index 37f1bdc3009..2baf2c61f7c 100644
--- a/lib/gitlab/import_export/decompressed_archive_size_validator.rb
+++ b/lib/gitlab/import_export/decompressed_archive_size_validator.rb
@@ -61,7 +61,7 @@ module Gitlab
Process.kill(-1, pgrp) if pgrp
false
- rescue => e
+ rescue StandardError => e
log_error(e.message)
Process.kill(-1, pgrp) if pgrp
diff --git a/lib/gitlab/import_export/error.rb b/lib/gitlab/import_export/error.rb
index f11b7a0a298..4af6b03fe94 100644
--- a/lib/gitlab/import_export/error.rb
+++ b/lib/gitlab/import_export/error.rb
@@ -3,12 +3,20 @@
module Gitlab
module ImportExport
class Error < StandardError
- def self.permission_error(user, importable)
+ def self.permission_error(user, object)
self.new(
"User with ID: %s does not have required permissions for %s: %s with ID: %s" %
- [user.id, importable.class.name, importable.name, importable.id]
+ [user.id, object.class.name, object.name, object.id]
)
end
+
+ def self.unsupported_object_type_error
+ self.new('Unknown object type')
+ end
+
+ def self.file_compression_error
+ self.new('File compression failed')
+ end
end
end
end
diff --git a/lib/gitlab/import_export/file_importer.rb b/lib/gitlab/import_export/file_importer.rb
index 51d58aae54f..4b3258f8caa 100644
--- a/lib/gitlab/import_export/file_importer.rb
+++ b/lib/gitlab/import_export/file_importer.rb
@@ -33,7 +33,7 @@ module Gitlab
validate_decompressed_archive_size if Feature.enabled?(:validate_import_decompressed_archive_size)
decompress_archive
end
- rescue => e
+ rescue StandardError => e
@shared.error(e)
false
ensure
@@ -57,7 +57,7 @@ module Gitlab
def decompress_archive
result = untar_zxf(archive: @archive_file, dir: @shared.export_path)
- raise ImporterError.new("Unable to decompress #{@archive_file} into #{@shared.export_path}") unless result
+ raise ImporterError, "Unable to decompress #{@archive_file} into #{@shared.export_path}" unless result
result
end
@@ -67,7 +67,17 @@ module Gitlab
@archive_file = File.join(@shared.archive_path, Gitlab::ImportExport.export_filename(exportable: @importable))
- download_or_copy_upload(@importable.import_export_upload.import_file, @archive_file)
+ remote_download_or_download_or_copy_upload
+ end
+
+ def remote_download_or_download_or_copy_upload
+ import_export_upload = @importable.import_export_upload
+
+ if import_export_upload.remote_import_url.present?
+ download(import_export_upload.remote_import_url, @archive_file)
+ else
+ download_or_copy_upload(import_export_upload.import_file, @archive_file)
+ end
end
def remove_symlinks
@@ -87,7 +97,7 @@ module Gitlab
end
def validate_decompressed_archive_size
- raise ImporterError.new(_('Decompressed archive size validation failed.')) unless size_validator.valid?
+ raise ImporterError, _('Decompressed archive size validation failed.') unless size_validator.valid?
end
def size_validator
diff --git a/lib/gitlab/import_export/group/import_export.yml b/lib/gitlab/import_export/group/import_export.yml
index e30206dc509..aceb4821a06 100644
--- a/lib/gitlab/import_export/group/import_export.yml
+++ b/lib/gitlab/import_export/group/import_export.yml
@@ -58,6 +58,8 @@ methods:
preloads:
+export_reorders:
+
# EE specific relationships and settings to include. All of this will be merged
# into the previous structures if EE is used.
ee:
diff --git a/lib/gitlab/import_export/group/legacy_import_export.yml b/lib/gitlab/import_export/group/legacy_import_export.yml
index 5008639077c..19611e1b010 100644
--- a/lib/gitlab/import_export/group/legacy_import_export.yml
+++ b/lib/gitlab/import_export/group/legacy_import_export.yml
@@ -60,6 +60,8 @@ methods:
preloads:
+export_reorders:
+
# EE specific relationships and settings to include. All of this will be merged
# into the previous structures if EE is used.
ee:
diff --git a/lib/gitlab/import_export/group/legacy_tree_restorer.rb b/lib/gitlab/import_export/group/legacy_tree_restorer.rb
index 5499b79cee6..2b95c098b59 100644
--- a/lib/gitlab/import_export/group/legacy_tree_restorer.rb
+++ b/lib/gitlab/import_export/group/legacy_tree_restorer.rb
@@ -45,7 +45,7 @@ module Gitlab
return false if @shared.errors.any?
true
- rescue => e
+ rescue StandardError => e
@shared.error(e)
false
end
diff --git a/lib/gitlab/import_export/group/legacy_tree_saver.rb b/lib/gitlab/import_export/group/legacy_tree_saver.rb
index 7ab81c09885..0f74fabeac3 100644
--- a/lib/gitlab/import_export/group/legacy_tree_saver.rb
+++ b/lib/gitlab/import_export/group/legacy_tree_saver.rb
@@ -19,7 +19,7 @@ module Gitlab
tree_saver.save(group_tree, @shared.export_path, ImportExport.group_filename)
true
- rescue => e
+ rescue StandardError => e
@shared.error(e)
false
end
@@ -35,7 +35,7 @@ module Gitlab
end
group_tree
- rescue => e
+ rescue StandardError => e
@shared.error(e)
end
diff --git a/lib/gitlab/import_export/group/tree_restorer.rb b/lib/gitlab/import_export/group/tree_restorer.rb
index 925ab6680ba..ea7de4cc896 100644
--- a/lib/gitlab/import_export/group/tree_restorer.rb
+++ b/lib/gitlab/import_export/group/tree_restorer.rb
@@ -26,7 +26,7 @@ module Gitlab
end
true
- rescue => e
+ rescue StandardError => e
shared.error(e)
false
end
@@ -74,7 +74,7 @@ module Gitlab
group = create_group(group_attributes)
restore_group(group, group_attributes)
- rescue => e
+ rescue StandardError => e
import_failure_service.log_import_failure(
source: 'process_child',
relation_key: 'group',
diff --git a/lib/gitlab/import_export/group/tree_saver.rb b/lib/gitlab/import_export/group/tree_saver.rb
index d538de33c51..0f588a55f9d 100644
--- a/lib/gitlab/import_export/group/tree_saver.rb
+++ b/lib/gitlab/import_export/group/tree_saver.rb
@@ -25,7 +25,7 @@ module Gitlab
json_writer.write_relation_array('groups', '_all', all_groups)
true
- rescue => e
+ rescue StandardError => e
@shared.error(e)
false
ensure
diff --git a/lib/gitlab/import_export/importer.rb b/lib/gitlab/import_export/importer.rb
index 390909efe36..c2510bbe938 100644
--- a/lib/gitlab/import_export/importer.rb
+++ b/lib/gitlab/import_export/importer.rb
@@ -21,15 +21,15 @@ module Gitlab
if import_file && check_version! && restorers.all?(&:restore) && overwrite_project
project
else
- raise Projects::ImportService::Error.new(shared.errors.to_sentence)
+ raise Projects::ImportService::Error, shared.errors.to_sentence
end
- rescue => e
+ rescue StandardError => e
# If some exception was raised could mean that the SnippetsRepoRestorer
# was not called. This would leave us with snippets without a repository.
# This is a state we don't want them to be, so we better delete them.
remove_non_migrated_snippets
- raise Projects::ImportService::Error.new(e.message)
+ raise Projects::ImportService::Error, e.message
ensure
remove_base_tmp_dir
remove_import_file
diff --git a/lib/gitlab/import_export/json/legacy_reader.rb b/lib/gitlab/import_export/json/legacy_reader.rb
index 12d6458aedc..f29c0a44188 100644
--- a/lib/gitlab/import_export/json/legacy_reader.rb
+++ b/lib/gitlab/import_export/json/legacy_reader.rb
@@ -28,9 +28,9 @@ module Gitlab
def read_hash
ActiveSupport::JSON.decode(IO.read(@path))
- rescue => e
+ rescue StandardError => e
Gitlab::ErrorTracking.log_exception(e)
- raise Gitlab::ImportExport::Error.new('Incorrect JSON format')
+ raise Gitlab::ImportExport::Error, 'Incorrect JSON format'
end
end
diff --git a/lib/gitlab/import_export/json/streaming_serializer.rb b/lib/gitlab/import_export/json/streaming_serializer.rb
index 05b7679e0ff..ec42c5e51c0 100644
--- a/lib/gitlab/import_export/json/streaming_serializer.rb
+++ b/lib/gitlab/import_export/json/streaming_serializer.rb
@@ -38,16 +38,6 @@ module Gitlab
end
end
- private
-
- attr_reader :json_writer, :relations_schema, :exportable
-
- def serialize_root
- attributes = exportable.as_json(
- relations_schema.merge(include: nil, preloads: nil))
- json_writer.write_attributes(@exportable_path, attributes)
- end
-
def serialize_relation(definition)
raise ArgumentError, 'definition needs to be Hash' unless definition.is_a?(Hash)
raise ArgumentError, 'definition needs to have exactly one Hash element' unless definition.one?
@@ -64,17 +54,22 @@ module Gitlab
end
end
+ private
+
+ attr_reader :json_writer, :relations_schema, :exportable
+
+ def serialize_root
+ attributes = exportable.as_json(
+ relations_schema.merge(include: nil, preloads: nil))
+ json_writer.write_attributes(@exportable_path, attributes)
+ end
+
def serialize_many_relations(key, records, options)
enumerator = Enumerator.new do |items|
key_preloads = preloads&.dig(key)
- records = records.preload(key_preloads) if key_preloads
- records.in_batches(of: batch_size) do |batch| # rubocop:disable Cop/InBatches
- # order each batch by its primary key to ensure
- # consistent and predictable ordering of each exported relation
- # as additional `WHERE` clauses can impact the order in which data is being
- # returned by database when no `ORDER` is specified
- batch = batch.reorder(batch.klass.primary_key)
+ batch(records, key) do |batch|
+ batch = batch.preload(key_preloads) if key_preloads
batch.each do |record|
items << Raw.new(record.to_json(options))
@@ -85,6 +80,29 @@ module Gitlab
json_writer.write_relation_array(@exportable_path, key, enumerator)
end
+ def batch(relation, key)
+ opts = { of: batch_size }
+ order_by = reorders(relation, key)
+
+ # we need to sort issues by non primary key column(relative_position)
+ # and `in_batches` does not support that
+ if order_by
+ scope = relation.reorder(order_by)
+
+ Gitlab::Pagination::Keyset::Iterator.new(scope: scope, use_union_optimization: true).each_batch(**opts) do |batch|
+ yield batch
+ end
+ else
+ relation.in_batches(**opts) do |batch| # rubocop:disable Cop/InBatches
+ # order each batch by its primary key to ensure
+ # consistent and predictable ordering of each exported relation
+ # as additional `WHERE` clauses can impact the order in which data is being
+ # returned by database when no `ORDER` is specified
+ yield batch.reorder(batch.klass.primary_key)
+ end
+ end
+ end
+
def serialize_many_each(key, records, options)
enumerator = Enumerator.new do |items|
records.each do |record|
@@ -112,6 +130,42 @@ module Gitlab
def batch_size
@batch_size ||= self.class.batch_size(@exportable)
end
+
+ def reorders(relation, key)
+ export_reorder = relations_schema[:export_reorder]&.dig(key)
+ return unless export_reorder
+
+ custom_reorder(relation.klass, export_reorder)
+ end
+
+ def custom_reorder(klass, order_by)
+ arel_table = klass.arel_table
+ column = order_by[:column] || klass.primary_key
+ direction = order_by[:direction] || :asc
+ nulls_position = order_by[:nulls_position] || :nulls_last
+
+ arel_order_classes = ::Gitlab::Pagination::Keyset::ColumnOrderDefinition::AREL_ORDER_CLASSES.invert
+ reverse_direction = ::Gitlab::Pagination::Keyset::ColumnOrderDefinition::REVERSED_ORDER_DIRECTIONS[direction]
+ reverse_nulls_position = ::Gitlab::Pagination::Keyset::ColumnOrderDefinition::REVERSED_NULL_POSITIONS[nulls_position]
+ order_expression = ::Gitlab::Database.nulls_order(column, direction, nulls_position)
+ reverse_order_expression = ::Gitlab::Database.nulls_order(column, reverse_direction, reverse_nulls_position)
+
+ ::Gitlab::Pagination::Keyset::Order.build([
+ ::Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: column,
+ column_expression: arel_table[column],
+ order_expression: order_expression,
+ reversed_order_expression: reverse_order_expression,
+ order_direction: direction,
+ nullable: nulls_position,
+ distinct: false
+ ),
+ ::Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: klass.primary_key,
+ order_expression: arel_order_classes[direction].new(arel_table[klass.primary_key.to_sym])
+ )
+ ])
+ end
end
end
end
diff --git a/lib/gitlab/import_export/lfs_restorer.rb b/lib/gitlab/import_export/lfs_restorer.rb
index ef83cdf24b1..d73ae1410a3 100644
--- a/lib/gitlab/import_export/lfs_restorer.rb
+++ b/lib/gitlab/import_export/lfs_restorer.rb
@@ -20,7 +20,7 @@ module Gitlab
end
true
- rescue => e
+ rescue StandardError => e
shared.error(e)
false
end
@@ -73,8 +73,8 @@ module Gitlab
begin
json = IO.read(lfs_json_path)
ActiveSupport::JSON.decode(json)
- rescue
- raise Gitlab::ImportExport::Error.new('Incorrect JSON format')
+ rescue StandardError
+ raise Gitlab::ImportExport::Error, 'Incorrect JSON format'
end
end
diff --git a/lib/gitlab/import_export/lfs_saver.rb b/lib/gitlab/import_export/lfs_saver.rb
index 4964b8b16f4..47acd49d529 100644
--- a/lib/gitlab/import_export/lfs_saver.rb
+++ b/lib/gitlab/import_export/lfs_saver.rb
@@ -27,7 +27,7 @@ module Gitlab
write_lfs_json
true
- rescue => e
+ rescue StandardError => e
shared.error(e)
false
diff --git a/lib/gitlab/import_export/members_mapper.rb b/lib/gitlab/import_export/members_mapper.rb
index 6b37683ea68..ff972cf9352 100644
--- a/lib/gitlab/import_export/members_mapper.rb
+++ b/lib/gitlab/import_export/members_mapper.rb
@@ -52,7 +52,7 @@ module Gitlab
@importable.members.destroy_all # rubocop: disable Cop/DestroyAll
relation_class.create!(user: @user, access_level: highest_access_level, source_id: @importable.id, importing: true)
- rescue => e
+ rescue StandardError => e
raise e, "Error adding importer user to #{@importable.class} members. #{e.message}"
end
diff --git a/lib/gitlab/import_export/merge_request_parser.rb b/lib/gitlab/import_export/merge_request_parser.rb
index 4643742b607..3910afef108 100644
--- a/lib/gitlab/import_export/merge_request_parser.rb
+++ b/lib/gitlab/import_export/merge_request_parser.rb
@@ -40,7 +40,7 @@ module Gitlab
# the commits are missing.
def create_source_branch
@project.repository.create_branch(@merge_request.source_branch, @diff_head_sha)
- rescue => err
+ rescue StandardError => err
Gitlab::Import::Logger.warn(
message: 'Import warning: Failed to create source branch',
source_branch: @merge_request.source_branch,
diff --git a/lib/gitlab/import_export/project/import_export.yml b/lib/gitlab/import_export/project/import_export.yml
index 42d32593cbd..d000c331b6d 100644
--- a/lib/gitlab/import_export/project/import_export.yml
+++ b/lib/gitlab/import_export/project/import_export.yml
@@ -132,6 +132,7 @@ excluded_attributes:
- :avatar
- :import_type
- :import_source
+ - :integrations
- :mirror
- :runners_token
- :runners_token_encrypted
@@ -152,6 +153,7 @@ excluded_attributes:
- :bfg_object_map
- :detected_repository_languages
- :tag_list
+ - :topic_list
- :mirror_user_id
- :mirror_trigger_builds
- :only_mirror_protected_branches
@@ -261,6 +263,8 @@ excluded_attributes:
- :resource_group_id
- :waiting_for_resource_at
- :processed
+ - :id_convert_to_bigint
+ - :stage_id_convert_to_bigint
sentry_issue:
- :issue_id
push_event_payload:
@@ -393,6 +397,8 @@ methods:
- :state
preloads:
+ issues:
+ project: :route
statuses:
# TODO: We cannot preload tags, as they are not part of `GenericCommitStatus`
# tags: # needed by tag_list
@@ -402,6 +408,29 @@ preloads:
target_project: # needed by target_branch_sha
assignees: # needed by assigne_id that is implemented by DeprecatedAssignee
+# Specify a custom export reordering for a given relationship
+# For example for issues we use a custom export reordering by relative_position, so that on import, we can reset the
+# relative position value, but still keep the issues order to the order in which issues were in the exported project.
+# By default the ordering of relations is done by PK.
+# column - specify the column by which to reorder, by default it is relation's PK
+# direction - specify the ordering direction :asc or :desc, default :asc
+# nulls_position - specify where would null values be positioned. Because custom ordering column can contain nulls we
+# need to also specify where would the nulls be placed. It can be :nulls_last or :nulls_first, defaults
+# to :nulls_last
+# Example:
+# export_reorders:
+# project:
+# issues:
+# column: :relative_position
+# direction: :asc
+# nulls_position: :nulls_last
+export_reorders:
+ project:
+ issues:
+ column: :relative_position
+ direction: :asc
+ nulls_position: :nulls_last
+
# EE specific relationships and settings to include. All of this will be merged
# into the previous structures if EE is used.
ee:
diff --git a/lib/gitlab/import_export/project/relation_factory.rb b/lib/gitlab/import_export/project/relation_factory.rb
index ae92228276e..4678396f97e 100644
--- a/lib/gitlab/import_export/project/relation_factory.rb
+++ b/lib/gitlab/import_export/project/relation_factory.rb
@@ -80,6 +80,7 @@ module Gitlab
when :notes then setup_note
when :'Ci::Pipeline' then setup_pipeline
when *BUILD_MODELS then setup_build
+ when :issues then setup_issue
end
update_project_references
@@ -135,6 +136,22 @@ module Gitlab
end
end
+ def setup_issue
+ @relation_hash['relative_position'] = compute_relative_position
+ end
+
+ def compute_relative_position
+ return unless max_relative_position
+
+ max_relative_position + (@relation_index + 1) * Gitlab::RelativePositioning::IDEAL_DISTANCE
+ end
+
+ def max_relative_position
+ Rails.cache.fetch("import:#{@importable.model_name.plural}:#{@importable.id}:hierarchy_max_issues_relative_position", expires_in: 24.hours) do
+ ::RelativePositioning.mover.context(Issue.in_projects(@importable.root_ancestor.all_projects).first)&.max_relative_position || ::Gitlab::RelativePositioning::START_POSITION
+ end
+ end
+
def legacy_trigger?
@relation_name == :'Ci::Trigger' && @relation_hash['owner_id'].nil?
end
@@ -158,4 +175,4 @@ module Gitlab
end
end
-Gitlab::ImportExport::Project::RelationFactory.prepend_if_ee('::EE::Gitlab::ImportExport::Project::RelationFactory')
+Gitlab::ImportExport::Project::RelationFactory.prepend_mod_with('Gitlab::ImportExport::Project::RelationFactory')
diff --git a/lib/gitlab/import_export/project/tree_restorer.rb b/lib/gitlab/import_export/project/tree_restorer.rb
index fb9e5be1877..113502b4e3c 100644
--- a/lib/gitlab/import_export/project/tree_restorer.rb
+++ b/lib/gitlab/import_export/project/tree_restorer.rb
@@ -39,7 +39,7 @@ module Gitlab
else
false
end
- rescue => e
+ rescue StandardError => e
@shared.error(e)
false
end
diff --git a/lib/gitlab/import_export/project/tree_saver.rb b/lib/gitlab/import_export/project/tree_saver.rb
index 80dacf2eb20..16012f3c0c0 100644
--- a/lib/gitlab/import_export/project/tree_saver.rb
+++ b/lib/gitlab/import_export/project/tree_saver.rb
@@ -22,7 +22,7 @@ module Gitlab
).execute
true
- rescue => e
+ rescue StandardError => e
@shared.error(e)
false
ensure
diff --git a/lib/gitlab/import_export/reader.rb b/lib/gitlab/import_export/reader.rb
index 8d36d05ca6f..b9a1aee3b8e 100644
--- a/lib/gitlab/import_export/reader.rb
+++ b/lib/gitlab/import_export/reader.rb
@@ -35,7 +35,7 @@ module Gitlab
def tree_by_key(key)
attributes_finder.find_root(key)
- rescue => e
+ rescue StandardError => e
@shared.error(e)
false
end
diff --git a/lib/gitlab/import_export/relation_tree_restorer.rb b/lib/gitlab/import_export/relation_tree_restorer.rb
index 8bc87ecb071..46b82240ef7 100644
--- a/lib/gitlab/import_export/relation_tree_restorer.rb
+++ b/lib/gitlab/import_export/relation_tree_restorer.rb
@@ -48,7 +48,7 @@ module Gitlab
@importable.reload # rubocop:disable Cop/ActiveRecordAssociationReload
true
- rescue => e
+ rescue StandardError => e
@shared.error(e)
false
end
@@ -81,7 +81,7 @@ module Gitlab
relation_object.save!
log_relation_creation(@importable, relation_key, relation_object)
end
- rescue => e
+ rescue StandardError => e
import_failure_service.log_import_failure(
source: 'process_relation_item!',
relation_key: relation_key,
@@ -155,7 +155,7 @@ module Gitlab
transform_sub_relations!(data_hash, sub_relation_key, sub_relation_definition, relation_index)
end
- relation = @relation_factory.create(**relation_factory_params(relation_key, data_hash))
+ relation = @relation_factory.create(**relation_factory_params(relation_key, relation_index, data_hash))
if relation && !relation.valid?
@shared.logger.warn(
@@ -221,8 +221,9 @@ module Gitlab
importable_class.to_s.downcase.to_sym
end
- def relation_factory_params(relation_key, data_hash)
+ def relation_factory_params(relation_key, relation_index, data_hash)
{
+ relation_index: relation_index,
relation_sym: relation_key.to_sym,
relation_hash: data_hash,
importable: @importable,
diff --git a/lib/gitlab/import_export/repo_restorer.rb b/lib/gitlab/import_export/repo_restorer.rb
index 998da3e4afb..1c6629cf942 100644
--- a/lib/gitlab/import_export/repo_restorer.rb
+++ b/lib/gitlab/import_export/repo_restorer.rb
@@ -22,7 +22,7 @@ module Gitlab
update_importable_repository_info
true
- rescue => e
+ rescue StandardError => e
shared.error(e)
false
end
@@ -52,4 +52,4 @@ module Gitlab
end
end
-Gitlab::ImportExport::RepoRestorer.prepend_if_ee('EE::Gitlab::ImportExport::RepoRestorer')
+Gitlab::ImportExport::RepoRestorer.prepend_mod_with('Gitlab::ImportExport::RepoRestorer')
diff --git a/lib/gitlab/import_export/repo_saver.rb b/lib/gitlab/import_export/repo_saver.rb
index 0fdd0722b65..fae07039139 100644
--- a/lib/gitlab/import_export/repo_saver.rb
+++ b/lib/gitlab/import_export/repo_saver.rb
@@ -40,7 +40,7 @@ module Gitlab
mkdir_p(File.dirname(bundle_full_path))
repository.bundle_to_disk(bundle_full_path)
- rescue => e
+ rescue StandardError => e
shared.error(e)
false
end
diff --git a/lib/gitlab/import_export/saver.rb b/lib/gitlab/import_export/saver.rb
index bb2bbda4bd6..bec709f4a36 100644
--- a/lib/gitlab/import_export/saver.rb
+++ b/lib/gitlab/import_export/saver.rb
@@ -27,7 +27,7 @@ module Gitlab
@shared.error(Gitlab::ImportExport::Error.new(error_message))
false
end
- rescue => e
+ rescue StandardError => e
@shared.error(e)
false
ensure
diff --git a/lib/gitlab/import_export/shared.rb b/lib/gitlab/import_export/shared.rb
index 09ed4eb568d..f295ab38de0 100644
--- a/lib/gitlab/import_export/shared.rb
+++ b/lib/gitlab/import_export/shared.rb
@@ -90,7 +90,7 @@ module Gitlab
when 'Group'
@exportable.full_path
else
- raise Gitlab::ImportExport::Error.new("Unsupported Exportable Type #{@exportable&.class}")
+ raise Gitlab::ImportExport::Error, "Unsupported Exportable Type #{@exportable&.class}"
end
end
diff --git a/lib/gitlab/import_export/snippet_repo_restorer.rb b/lib/gitlab/import_export/snippet_repo_restorer.rb
index 2d0aa05fc3c..cb13972f8f2 100644
--- a/lib/gitlab/import_export/snippet_repo_restorer.rb
+++ b/lib/gitlab/import_export/snippet_repo_restorer.rb
@@ -23,7 +23,7 @@ module Gitlab
end
true
- rescue => e
+ rescue StandardError => e
shared.error(e)
false
end
diff --git a/lib/gitlab/import_export/statistics_restorer.rb b/lib/gitlab/import_export/statistics_restorer.rb
index 3fafb01c37c..a3ad5edc2cc 100644
--- a/lib/gitlab/import_export/statistics_restorer.rb
+++ b/lib/gitlab/import_export/statistics_restorer.rb
@@ -10,7 +10,7 @@ module Gitlab
def restore
@project.statistics.refresh!
- rescue => e
+ rescue StandardError => e
@shared.error(e)
false
end
diff --git a/lib/gitlab/import_export/uploads_manager.rb b/lib/gitlab/import_export/uploads_manager.rb
index 2f15cdd7506..ad19508fb99 100644
--- a/lib/gitlab/import_export/uploads_manager.rb
+++ b/lib/gitlab/import_export/uploads_manager.rb
@@ -17,7 +17,7 @@ module Gitlab
copy_project_uploads
true
- rescue => e
+ rescue StandardError => e
@shared.error(e)
false
end
@@ -30,7 +30,7 @@ module Gitlab
end
true
- rescue => e
+ rescue StandardError => e
@shared.error(e)
false
end
diff --git a/lib/gitlab/import_export/uploads_restorer.rb b/lib/gitlab/import_export/uploads_restorer.rb
index 5f422dcbefa..741c6555aad 100644
--- a/lib/gitlab/import_export/uploads_restorer.rb
+++ b/lib/gitlab/import_export/uploads_restorer.rb
@@ -8,7 +8,7 @@ module Gitlab
project: @project,
shared: @shared
).restore
- rescue => e
+ rescue StandardError => e
@shared.error(e)
false
end
diff --git a/lib/gitlab/import_export/uploads_saver.rb b/lib/gitlab/import_export/uploads_saver.rb
index be1066c30b2..9f58609fa17 100644
--- a/lib/gitlab/import_export/uploads_saver.rb
+++ b/lib/gitlab/import_export/uploads_saver.rb
@@ -13,7 +13,7 @@ module Gitlab
project: @project,
shared: @shared
).save
- rescue => e
+ rescue StandardError => e
@shared.error(e)
false
end
diff --git a/lib/gitlab/import_export/version_checker.rb b/lib/gitlab/import_export/version_checker.rb
index 48f5b558e52..5ec9db00d0a 100644
--- a/lib/gitlab/import_export/version_checker.rb
+++ b/lib/gitlab/import_export/version_checker.rb
@@ -14,7 +14,7 @@ module Gitlab
def check!
version = File.open(version_file, &:readline)
verify_version!(version)
- rescue => e
+ rescue StandardError => e
@shared.error(e)
false
end
@@ -27,7 +27,7 @@ module Gitlab
def verify_version!(version)
if different_version?(version)
- raise Gitlab::ImportExport::Error.new("Import version mismatch: Required #{Gitlab::ImportExport.version} but was #{version}")
+ raise Gitlab::ImportExport::Error, "Import version mismatch: Required #{Gitlab::ImportExport.version} but was #{version}"
else
true
end
@@ -35,13 +35,13 @@ module Gitlab
def different_version?(version)
Gem::Version.new(version) != Gem::Version.new(Gitlab::ImportExport.version)
- rescue => e
+ rescue StandardError => e
Gitlab::Import::Logger.error(
message: 'Import error',
error: e.message
)
- raise Gitlab::ImportExport::Error.new('Incorrect VERSION format')
+ raise Gitlab::ImportExport::Error, 'Incorrect VERSION format'
end
end
end
diff --git a/lib/gitlab/import_export/version_saver.rb b/lib/gitlab/import_export/version_saver.rb
index dab8bbf539d..e8f68f93af0 100644
--- a/lib/gitlab/import_export/version_saver.rb
+++ b/lib/gitlab/import_export/version_saver.rb
@@ -15,7 +15,7 @@ module Gitlab
File.write(version_file, Gitlab::ImportExport.version, mode: 'w')
File.write(gitlab_version_file, Gitlab::VERSION, mode: 'w')
File.write(gitlab_revision_file, Gitlab.revision, mode: 'w')
- rescue => e
+ rescue StandardError => e
@shared.error(e)
false
end
diff --git a/lib/gitlab/import_export/wiki_repo_saver.rb b/lib/gitlab/import_export/wiki_repo_saver.rb
index 4b1cf4915e4..162b474bfeb 100644
--- a/lib/gitlab/import_export/wiki_repo_saver.rb
+++ b/lib/gitlab/import_export/wiki_repo_saver.rb
@@ -20,4 +20,4 @@ module Gitlab
end
end
-Gitlab::ImportExport::WikiRepoSaver.prepend_if_ee('EE::Gitlab::ImportExport::WikiRepoSaver')
+Gitlab::ImportExport::WikiRepoSaver.prepend_mod_with('Gitlab::ImportExport::WikiRepoSaver')
diff --git a/lib/gitlab/import_sources.rb b/lib/gitlab/import_sources.rb
index 95c002edf0a..c9f5005cede 100644
--- a/lib/gitlab/import_sources.rb
+++ b/lib/gitlab/import_sources.rb
@@ -25,7 +25,7 @@ module Gitlab
].freeze
class << self
- prepend_if_ee('EE::Gitlab::ImportSources') # rubocop: disable Cop/InjectEnterpriseEditionModule
+ prepend_mod_with('Gitlab::ImportSources') # rubocop: disable Cop/InjectEnterpriseEditionModule
def options
import_table.to_h { |importer| [importer.title, importer.name] }
diff --git a/lib/gitlab/incident_management/pager_duty/incident_issue_description.rb b/lib/gitlab/incident_management/pager_duty/incident_issue_description.rb
index 768c8bb4cbb..6aeeb1d31aa 100644
--- a/lib/gitlab/incident_management/pager_duty/incident_issue_description.rb
+++ b/lib/gitlab/incident_management/pager_duty/incident_issue_description.rb
@@ -33,7 +33,7 @@ module Gitlab
def incident_created_at
Time.zone.parse(incident_payload['created_at'])
- rescue
+ rescue StandardError
Time.current.utc # PagerDuty provides time in UTC
end
diff --git a/lib/gitlab/instrumentation/redis_cluster_validator.rb b/lib/gitlab/instrumentation/redis_cluster_validator.rb
index 644a5fc4fff..005751fb0db 100644
--- a/lib/gitlab/instrumentation/redis_cluster_validator.rb
+++ b/lib/gitlab/instrumentation/redis_cluster_validator.rb
@@ -62,7 +62,7 @@ module Gitlab
end
if key_slots.uniq.many? # rubocop: disable CodeReuse/ActiveRecord
- raise CrossSlotError.new("Redis command #{command_name} arguments hash to different slots. See https://docs.gitlab.com/ee/development/redis.html#multi-key-commands")
+ raise CrossSlotError, "Redis command #{command_name} arguments hash to different slots. See https://docs.gitlab.com/ee/development/redis.html#multi-key-commands"
end
end
diff --git a/lib/gitlab/integrations/sti_type.rb b/lib/gitlab/integrations/sti_type.rb
new file mode 100644
index 00000000000..e6ea98e6d66
--- /dev/null
+++ b/lib/gitlab/integrations/sti_type.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Integrations
+ class StiType < ActiveRecord::Type::String
+ NAMESPACED_INTEGRATIONS = Set.new(%w(
+ Asana Assembla Bamboo Campfire Confluence Datadog EmailsOnPush
+ )).freeze
+
+ def cast(value)
+ new_cast(value) || super
+ end
+
+ def serialize(value)
+ new_serialize(value) || super
+ end
+
+ def deserialize(value)
+ value
+ end
+
+ def changed?(original_value, value, _new_value_before_type_cast)
+ original_value != serialize(value)
+ end
+
+ def changed_in_place?(original_value_for_database, value)
+ original_value_for_database != serialize(value)
+ end
+
+ private
+
+ def new_cast(value)
+ value = prepare_value(value)
+ return unless value
+
+ stripped_name = value.delete_suffix('Service')
+ return unless NAMESPACED_INTEGRATIONS.include?(stripped_name)
+
+ "Integrations::#{stripped_name}"
+ end
+
+ def new_serialize(value)
+ value = prepare_value(value)
+ return unless value&.starts_with?('Integrations::')
+
+ "#{value.delete_prefix('Integrations::')}Service"
+ end
+
+ # Returns value cast to a `String`, or `nil` if value is `nil`.
+ def prepare_value(value)
+ return value if value.nil? || value.is_a?(String)
+
+ value.to_s
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/issuable_metadata.rb b/lib/gitlab/issuable_metadata.rb
index f96c937aec3..d0702fb5c7d 100644
--- a/lib/gitlab/issuable_metadata.rb
+++ b/lib/gitlab/issuable_metadata.rb
@@ -98,4 +98,4 @@ module Gitlab
end
end
-Gitlab::IssuableMetadata.prepend_if_ee('EE::Gitlab::IssuableMetadata')
+Gitlab::IssuableMetadata.prepend_mod_with('Gitlab::IssuableMetadata')
diff --git a/lib/gitlab/jira/http_client.rb b/lib/gitlab/jira/http_client.rb
index f0b08bb6b6a..3e7659db240 100644
--- a/lib/gitlab/jira/http_client.rb
+++ b/lib/gitlab/jira/http_client.rb
@@ -12,7 +12,7 @@ module Gitlab
def request(*args)
result = make_request(*args)
- raise JIRA::HTTPError.new(result.response) unless result.response.is_a?(Net::HTTPSuccess)
+ raise JIRA::HTTPError, result.response unless result.response.is_a?(Net::HTTPSuccess)
result
end
diff --git a/lib/gitlab/jira_import/issues_importer.rb b/lib/gitlab/jira_import/issues_importer.rb
index 26fa01755d1..8a03162f111 100644
--- a/lib/gitlab/jira_import/issues_importer.rb
+++ b/lib/gitlab/jira_import/issues_importer.rb
@@ -70,7 +70,7 @@ module Gitlab
# These ids are cleaned-up when import finishes.
# see Gitlab::JiraImport::Stage::FinishImportWorker
mark_as_imported(jira_issue.id)
- rescue => ex
+ rescue StandardError => ex
# handle exceptionn here and skip the failed to import issue, instead of
# failing to import the entire batch of issues
diff --git a/lib/gitlab/jira_import/labels_importer.rb b/lib/gitlab/jira_import/labels_importer.rb
index 6e6842e06bf..046dc3fd04f 100644
--- a/lib/gitlab/jira_import/labels_importer.rb
+++ b/lib/gitlab/jira_import/labels_importer.rb
@@ -47,7 +47,7 @@ module Gitlab
Gitlab::JiraImport::HandleLabelsService.new(project, response['values']).execute
response['isLast']
- rescue => e
+ rescue StandardError => e
Gitlab::ErrorTracking.track_exception(e, project_id: project.id, request: request)
end
end
diff --git a/lib/gitlab/json.rb b/lib/gitlab/json.rb
index b51c0a33457..561cd4509b1 100644
--- a/lib/gitlab/json.rb
+++ b/lib/gitlab/json.rb
@@ -84,7 +84,7 @@ module Gitlab
Oj.load(string, opts)
rescue Oj::ParseError, Encoding::UndefinedConversionError => ex
- raise parser_error.new(ex)
+ raise parser_error, ex
end
# Take a Ruby object and convert it to a string. This method varies
@@ -169,7 +169,7 @@ module Gitlab
# @return [Boolean]
def feature_table_exists?
Feature::FlipperFeature.table_exists?
- rescue
+ rescue StandardError
false
end
end
diff --git a/lib/gitlab/jwt_token.rb b/lib/gitlab/jwt_token.rb
new file mode 100644
index 00000000000..11bc5479b6e
--- /dev/null
+++ b/lib/gitlab/jwt_token.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+module Gitlab
+ class JWTToken < JSONWebToken::HMACToken
+ HMAC_ALGORITHM = 'SHA256'
+ HMAC_KEY = 'gitlab-jwt'
+ HMAC_EXPIRES_IN = 5.minutes.freeze
+
+ class << self
+ def decode(jwt)
+ payload = super(jwt, secret).first
+
+ new.tap do |jwt_token|
+ jwt_token.id = payload.delete('jti')
+ jwt_token.issued_at = payload.delete('iat')
+ jwt_token.not_before = payload.delete('nbf')
+ jwt_token.expire_time = payload.delete('exp')
+
+ payload.each do |key, value|
+ jwt_token[key] = value
+ end
+ end
+ rescue JWT::DecodeError, JWT::ExpiredSignature, JWT::ImmatureSignature => ex
+ # we want to log and return on expired and errored tokens
+ Gitlab::ErrorTracking.track_exception(ex)
+ nil
+ end
+
+ def secret
+ OpenSSL::HMAC.hexdigest(
+ HMAC_ALGORITHM,
+ ::Settings.attr_encrypted_db_key_base,
+ HMAC_KEY
+ )
+ end
+ end
+
+ def initialize
+ super(self.class.secret)
+ self.expire_time = self.issued_at + HMAC_EXPIRES_IN.to_i
+ end
+
+ def ==(other)
+ self.id == other.id &&
+ self.payload == other.payload
+ end
+
+ def issued_at=(value)
+ super(convert_time(value))
+ end
+
+ def not_before=(value)
+ super(convert_time(value))
+ end
+
+ def expire_time=(value)
+ super(convert_time(value))
+ end
+
+ private
+
+ def convert_time(value)
+ # JSONWebToken::Token truncates subsecond precision causing comparisons to
+ # fail unless we truncate it here first
+ value = value.to_i if value.is_a?(Float)
+ value = Time.zone.at(value) if value.is_a?(Integer)
+ value
+ end
+ end
+end
diff --git a/lib/gitlab/kas.rb b/lib/gitlab/kas.rb
index 7a674cb5c21..7b2c792ebca 100644
--- a/lib/gitlab/kas.rb
+++ b/lib/gitlab/kas.rb
@@ -3,6 +3,7 @@
module Gitlab
module Kas
INTERNAL_API_REQUEST_HEADER = 'Gitlab-Kas-Api-Request'
+ VERSION_FILE = 'GITLAB_KAS_VERSION'
JWT_ISSUER = 'gitlab-kas'
include JwtAuthenticatable
@@ -29,6 +30,27 @@ module Gitlab
Feature.enabled?(:kubernetes_agent_on_gitlab_com, project, default_enabled: :yaml)
end
+
+ # Return GitLab KAS version
+ #
+ # @return [String] version
+ def version
+ @_version ||= Rails.root.join(VERSION_FILE).read.chomp
+ end
+
+ # Return GitLab KAS external_url
+ #
+ # @return [String] external_url
+ def external_url
+ Gitlab.config.gitlab_kas.external_url
+ end
+
+ # Return whether GitLab KAS is enabled
+ #
+ # @return [Boolean] external_url
+ def enabled?
+ !!Gitlab.config['gitlab_kas']&.fetch('enabled', false)
+ end
end
end
end
diff --git a/lib/gitlab/kubernetes/kube_client.rb b/lib/gitlab/kubernetes/kube_client.rb
index a25f005d81e..6caebf445e5 100644
--- a/lib/gitlab/kubernetes/kube_client.rb
+++ b/lib/gitlab/kubernetes/kube_client.rb
@@ -116,7 +116,7 @@ module Gitlab
{ status: :authentication_failure, connection_error: :authentication_error }
rescue Kubeclient::HttpError => e
{ status: kubeclient_error_status(e.message), connection_error: :http_error }
- rescue => e
+ rescue StandardError => e
Gitlab::ErrorTracking.track_exception(e, cluster_id: cluster_id)
{ status: :unknown_failure, connection_error: :unknown_error }
diff --git a/lib/gitlab/legacy_github_import/importer.rb b/lib/gitlab/legacy_github_import/importer.rb
index a17e3b1ad5c..fc5834613fd 100644
--- a/lib/gitlab/legacy_github_import/importer.rb
+++ b/lib/gitlab/legacy_github_import/importer.rb
@@ -94,7 +94,7 @@ module Gitlab
labels.each do |raw|
gh_label = LabelFormatter.new(project, raw)
gh_label.create!
- rescue => e
+ rescue StandardError => e
errors << { type: :label, url: Gitlab::UrlSanitizer.sanitize(gh_label.url), errors: e.message }
end
end
@@ -107,7 +107,7 @@ module Gitlab
milestones.each do |raw|
gh_milestone = MilestoneFormatter.new(project, raw)
gh_milestone.create!
- rescue => e
+ rescue StandardError => e
errors << { type: :milestone, url: Gitlab::UrlSanitizer.sanitize(gh_milestone.url), errors: e.message }
end
end
@@ -128,7 +128,7 @@ module Gitlab
end
apply_labels(issuable, raw)
- rescue => e
+ rescue StandardError => e
errors << { type: :issue, url: Gitlab::UrlSanitizer.sanitize(gh_issue.url), errors: e.message }
end
end
@@ -153,7 +153,7 @@ module Gitlab
if project.gitea_import?
apply_labels(merge_request, raw)
end
- rescue => e
+ rescue StandardError => e
errors << { type: :pull_request, url: Gitlab::UrlSanitizer.sanitize(gh_pull_request.url), errors: e.message }
ensure
clean_up_restored_branches(gh_pull_request)
@@ -236,7 +236,7 @@ module Gitlab
next unless issuable
issuable.notes.create!(comment.attributes)
- rescue => e
+ rescue StandardError => e
errors << { type: :comment, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message }
end
end
@@ -280,7 +280,7 @@ module Gitlab
releases.each do |raw|
gh_release = ReleaseFormatter.new(project, raw)
gh_release.create! if gh_release.valid?
- rescue => e
+ rescue StandardError => e
errors << { type: :release, url: Gitlab::UrlSanitizer.sanitize(gh_release.url), errors: e.message }
end
end
diff --git a/lib/gitlab/legacy_github_import/label_formatter.rb b/lib/gitlab/legacy_github_import/label_formatter.rb
index 0b6e4612843..415b1b8878f 100644
--- a/lib/gitlab/legacy_github_import/label_formatter.rb
+++ b/lib/gitlab/legacy_github_import/label_formatter.rb
@@ -20,7 +20,7 @@ module Gitlab
service = ::Labels::FindOrCreateService.new(nil, project, params)
label = service.execute(skip_authorization: true)
- raise ActiveRecord::RecordInvalid.new(label) unless label.persisted?
+ raise ActiveRecord::RecordInvalid, label unless label.persisted?
label
end
diff --git a/lib/gitlab/lfs/client.rb b/lib/gitlab/lfs/client.rb
index 825d7399190..a05e8107cad 100644
--- a/lib/gitlab/lfs/client.rb
+++ b/lib/gitlab/lfs/client.rb
@@ -43,7 +43,7 @@ module Gitlab
body = Gitlab::Json.parse(rsp.body)
transfer = body.fetch('transfer', 'basic')
- raise UnsupportedTransferError.new(transfer.inspect) unless transfer == 'basic'
+ raise UnsupportedTransferError, transfer.inspect unless transfer == 'basic'
body
end
@@ -97,7 +97,10 @@ module Gitlab
end
def basic_auth
- return unless credentials[:auth_method] == "password"
+ # Some legacy credentials have a nil auth_method, which means password
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/328674
+ return unless credentials.fetch(:auth_method, 'password') == 'password'
+ return if credentials.empty?
{ username: credentials[:user], password: credentials[:password] }
end
diff --git a/lib/gitlab/local_and_remote_storage_migration/artifact_migrater.rb b/lib/gitlab/local_and_remote_storage_migration/artifact_migrater.rb
new file mode 100644
index 00000000000..b25305382b2
--- /dev/null
+++ b/lib/gitlab/local_and_remote_storage_migration/artifact_migrater.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module LocalAndRemoteStorageMigration
+ class ArtifactMigrater < Gitlab::LocalAndRemoteStorageMigration::BaseMigrater
+ private
+
+ def items_with_files_stored_locally
+ ::Ci::JobArtifact.with_files_stored_locally
+ end
+
+ def items_with_files_stored_remotely
+ ::Ci::JobArtifact.with_files_stored_remotely
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/local_and_remote_storage_migration/base_migrater.rb b/lib/gitlab/local_and_remote_storage_migration/base_migrater.rb
new file mode 100644
index 00000000000..f859d293e76
--- /dev/null
+++ b/lib/gitlab/local_and_remote_storage_migration/base_migrater.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module LocalAndRemoteStorageMigration
+ class BaseMigrater
+ def initialize(logger = nil)
+ @logger = logger
+ end
+
+ def migrate_to_remote_storage
+ logger.info('Starting transfer to remote storage')
+
+ migrate(items_with_files_stored_locally, ObjectStorage::Store::REMOTE)
+ end
+
+ def migrate_to_local_storage
+ logger.info('Starting transfer to local storage')
+
+ migrate(items_with_files_stored_remotely, ObjectStorage::Store::LOCAL)
+ end
+
+ private
+
+ attr_reader :logger
+
+ def batch_size
+ ENV.fetch('MIGRATION_BATCH_SIZE', 10).to_i
+ end
+
+ def migrate(items, store)
+ items.find_each(batch_size: batch_size) do |item| # rubocop:disable CodeReuse/ActiveRecord
+ item.file.migrate!(store)
+
+ log_success(item, store)
+ rescue StandardError => e
+ log_error(e, item)
+ end
+ end
+
+ def log_success(item, store)
+ logger.info("Transferred #{item.class.name} ID #{item.id} of type #{item.file_type} with size #{item.size} to #{storage_label(store)} storage")
+ end
+
+ def log_error(err, item)
+ logger.warn("Failed to transfer #{item.class.name} of type #{item.file_type} and ID #{item.id} with error: #{err.message}")
+ end
+
+ def storage_label(store)
+ if store == ObjectStorage::Store::LOCAL
+ 'local'
+ else
+ 'object'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/local_and_remote_storage_migration/pages_deployment_migrater.rb b/lib/gitlab/local_and_remote_storage_migration/pages_deployment_migrater.rb
new file mode 100644
index 00000000000..70437936332
--- /dev/null
+++ b/lib/gitlab/local_and_remote_storage_migration/pages_deployment_migrater.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module LocalAndRemoteStorageMigration
+ class PagesDeploymentMigrater < Gitlab::LocalAndRemoteStorageMigration::BaseMigrater
+ private
+
+ def items_with_files_stored_locally
+ ::PagesDeployment.with_files_stored_locally
+ end
+
+ def items_with_files_stored_remotely
+ ::PagesDeployment.with_files_stored_remotely
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/markdown_cache.rb b/lib/gitlab/markdown_cache.rb
index 3ec5f2339b5..d0702190ac0 100644
--- a/lib/gitlab/markdown_cache.rb
+++ b/lib/gitlab/markdown_cache.rb
@@ -2,8 +2,12 @@
module Gitlab
module MarkdownCache
- # Increment this number every time the renderer changes its output
- CACHE_COMMONMARK_VERSION = 27
+ # Increment this number every time the renderer changes its output.
+ # Changing this value puts strain on the database, as every row with
+ # cached markdown needs to be updated. As a result, this line should
+ # not be changed.
+ # See: https://gitlab.com/gitlab-org/gitlab/-/issues/330313
+ CACHE_COMMONMARK_VERSION = 28
CACHE_COMMONMARK_VERSION_START = 10
BaseError = Class.new(StandardError)
diff --git a/lib/gitlab/memory/instrumentation.rb b/lib/gitlab/memory/instrumentation.rb
index 8f9f6d19ce8..e800fe14cf1 100644
--- a/lib/gitlab/memory/instrumentation.rb
+++ b/lib/gitlab/memory/instrumentation.rb
@@ -45,9 +45,12 @@ module Gitlab
end
# This method returns a hash with the following keys:
- # - mem_objects: a number of allocated heap slots (as reflected by GC)
- # - mem_mallocs: a number of malloc calls
- # - mem_bytes: a number of bytes allocated with a mallocs tied to heap slots
+ # - mem_objects: number of allocated heap slots (as reflected by GC)
+ # - mem_mallocs: number of malloc calls
+ # - mem_bytes: number of bytes allocated by malloc for objects that did not fit
+ # into a heap slot
+ # - mem_total_bytes: number of bytes allocated for both objects consuming an object slot
+ # and objects that required a malloc (mem_malloc_bytes)
def self.measure_thread_memory_allocations(previous)
return unless available?
return unless previous
@@ -56,9 +59,13 @@ module Gitlab
return unless current
# calculate difference in a memory allocations
- previous.to_h do |key, value|
+ result = previous.to_h do |key, value|
[KEY_MAPPING.fetch(key), current[key].to_i - value]
end
+
+ result[:mem_total_bytes] = result[:mem_bytes] + result[:mem_objects] * GC::INTERNAL_CONSTANTS[:RVALUE_SIZE]
+
+ result
end
def self.with_memory_allocations
diff --git a/lib/gitlab/metrics/dashboard/errors.rb b/lib/gitlab/metrics/dashboard/errors.rb
index 07ddd315bcc..1a951172f74 100644
--- a/lib/gitlab/metrics/dashboard/errors.rb
+++ b/lib/gitlab/metrics/dashboard/errors.rb
@@ -33,7 +33,7 @@ module Gitlab
end
def panels_not_found!(opts)
- raise PanelNotFoundError.new(_("No panels matching properties %{opts}") % { opts: opts })
+ raise PanelNotFoundError, _("No panels matching properties %{opts}") % { opts: opts }
end
end
end
diff --git a/lib/gitlab/metrics/dashboard/stages/base_stage.rb b/lib/gitlab/metrics/dashboard/stages/base_stage.rb
index ee2d36621b4..c2a8a88108f 100644
--- a/lib/gitlab/metrics/dashboard/stages/base_stage.rb
+++ b/lib/gitlab/metrics/dashboard/stages/base_stage.rb
@@ -23,15 +23,15 @@ module Gitlab
protected
def missing_panel_groups!
- raise Errors::LayoutError.new('Top-level key :panel_groups must be an array')
+ raise Errors::LayoutError, 'Top-level key :panel_groups must be an array'
end
def missing_panels!
- raise Errors::LayoutError.new('Each "panel_group" must define an array :panels')
+ raise Errors::LayoutError, 'Each "panel_group" must define an array :panels'
end
def missing_metrics!
- raise Errors::LayoutError.new('Each "panel" must define an array :metrics')
+ raise Errors::LayoutError, 'Each "panel" must define an array :metrics'
end
def for_metrics
diff --git a/lib/gitlab/metrics/dashboard/stages/cluster_endpoint_inserter.rb b/lib/gitlab/metrics/dashboard/stages/cluster_endpoint_inserter.rb
index a12082b704c..2c17982d299 100644
--- a/lib/gitlab/metrics/dashboard/stages/cluster_endpoint_inserter.rb
+++ b/lib/gitlab/metrics/dashboard/stages/cluster_endpoint_inserter.rb
@@ -39,7 +39,7 @@ module Gitlab
end
def error!(message)
- raise Errors::DashboardProcessingError.new(message)
+ raise Errors::DashboardProcessingError, message
end
def group_url(metric)
@@ -67,14 +67,14 @@ module Gitlab
def query_for_metric(metric)
query = metric[query_type(metric)]
- raise Errors::MissingQueryError.new('Each "metric" must define one of :query or :query_range') unless query
+ raise Errors::MissingQueryError, 'Each "metric" must define one of :query or :query_range' unless query
query
end
def verify_params
- raise Errors::DashboardProcessingError.new(_('Cluster is required for Stages::ClusterEndpointInserter')) unless params[:cluster]
- raise Errors::DashboardProcessingError.new(_('Cluster type must be specificed for Stages::ClusterEndpointInserter')) unless params[:cluster_type]
+ raise Errors::DashboardProcessingError, _('Cluster is required for Stages::ClusterEndpointInserter') unless params[:cluster]
+ raise Errors::DashboardProcessingError, _('Cluster type must be specificed for Stages::ClusterEndpointInserter') unless params[:cluster_type]
end
end
end
diff --git a/lib/gitlab/metrics/dashboard/stages/metric_endpoint_inserter.rb b/lib/gitlab/metrics/dashboard/stages/metric_endpoint_inserter.rb
index dd85bd0beb1..d885d978524 100644
--- a/lib/gitlab/metrics/dashboard/stages/metric_endpoint_inserter.rb
+++ b/lib/gitlab/metrics/dashboard/stages/metric_endpoint_inserter.rb
@@ -6,7 +6,7 @@ module Gitlab
module Stages
class MetricEndpointInserter < BaseStage
def transform!
- raise Errors::DashboardProcessingError.new(_('Environment is required for Stages::MetricEndpointInserter')) unless params[:environment]
+ raise Errors::DashboardProcessingError, _('Environment is required for Stages::MetricEndpointInserter') unless params[:environment]
for_metrics do |metric|
metric[:prometheus_endpoint_path] = endpoint_for_metric(metric)
@@ -43,7 +43,7 @@ module Gitlab
def query_for_metric(metric)
query = metric[query_type(metric)]
- raise Errors::MissingQueryError.new('Each "metric" must define one of :query or :query_range') unless query
+ raise Errors::MissingQueryError, 'Each "metric" must define one of :query or :query_range' unless query
# We need to remove any newlines since our UrlBlocker does not allow
# multiline URLs.
diff --git a/lib/gitlab/metrics/dashboard/stages/variable_endpoint_inserter.rb b/lib/gitlab/metrics/dashboard/stages/variable_endpoint_inserter.rb
index 20e7fe477e5..b3ce0b79675 100644
--- a/lib/gitlab/metrics/dashboard/stages/variable_endpoint_inserter.rb
+++ b/lib/gitlab/metrics/dashboard/stages/variable_endpoint_inserter.rb
@@ -8,7 +8,7 @@ module Gitlab
VARIABLE_TYPE_METRIC_LABEL_VALUES = 'metric_label_values'
def transform!
- raise Errors::DashboardProcessingError.new(_('Environment is required for Stages::VariableEndpointInserter')) unless params[:environment]
+ raise Errors::DashboardProcessingError, _('Environment is required for Stages::VariableEndpointInserter') unless params[:environment]
for_variables do |variable_name, variable|
if variable.is_a?(Hash) && variable[:type] == VARIABLE_TYPE_METRIC_LABEL_VALUES
diff --git a/lib/gitlab/metrics/requests_rack_middleware.rb b/lib/gitlab/metrics/requests_rack_middleware.rb
index 23d7eb67312..19a835b9fc4 100644
--- a/lib/gitlab/metrics/requests_rack_middleware.rb
+++ b/lib/gitlab/metrics/requests_rack_middleware.rb
@@ -83,7 +83,7 @@ module Gitlab
end
[status, headers, body]
- rescue
+ rescue StandardError
RequestsRackMiddleware.rack_uncaught_errors_count.increment
raise
ensure
diff --git a/lib/gitlab/metrics/samplers/base_sampler.rb b/lib/gitlab/metrics/samplers/base_sampler.rb
index 7f9055fed5d..258aa93be38 100644
--- a/lib/gitlab/metrics/samplers/base_sampler.rb
+++ b/lib/gitlab/metrics/samplers/base_sampler.rb
@@ -22,7 +22,7 @@ module Gitlab
def safe_sample
sample
- rescue => e
+ rescue StandardError => e
Gitlab::AppLogger.warn("#{self.class}: #{e}, stopping")
stop
end
diff --git a/lib/gitlab/metrics/samplers/database_sampler.rb b/lib/gitlab/metrics/samplers/database_sampler.rb
index c0336a4d0fb..0a0ac6c5386 100644
--- a/lib/gitlab/metrics/samplers/database_sampler.rb
+++ b/lib/gitlab/metrics/samplers/database_sampler.rb
@@ -55,4 +55,4 @@ module Gitlab
end
end
-Gitlab::Metrics::Samplers::DatabaseSampler.prepend_if_ee('EE::Gitlab::Metrics::Samplers::DatabaseSampler')
+Gitlab::Metrics::Samplers::DatabaseSampler.prepend_mod_with('Gitlab::Metrics::Samplers::DatabaseSampler')
diff --git a/lib/gitlab/metrics/subscribers/active_record.rb b/lib/gitlab/metrics/subscribers/active_record.rb
index 0d1cd641ffe..3db3317e833 100644
--- a/lib/gitlab/metrics/subscribers/active_record.rb
+++ b/lib/gitlab/metrics/subscribers/active_record.rb
@@ -87,4 +87,4 @@ module Gitlab
end
end
-Gitlab::Metrics::Subscribers::ActiveRecord.prepend_if_ee('EE::Gitlab::Metrics::Subscribers::ActiveRecord')
+Gitlab::Metrics::Subscribers::ActiveRecord.prepend_mod_with('Gitlab::Metrics::Subscribers::ActiveRecord')
diff --git a/lib/gitlab/metrics/subscribers/rack_attack.rb b/lib/gitlab/metrics/subscribers/rack_attack.rb
index 2791a39fb16..1c7767f5ca9 100644
--- a/lib/gitlab/metrics/subscribers/rack_attack.rb
+++ b/lib/gitlab/metrics/subscribers/rack_attack.rb
@@ -19,7 +19,8 @@ module Gitlab
:throttle_authenticated_api,
:throttle_authenticated_web,
:throttle_authenticated_protected_paths_api,
- :throttle_authenticated_protected_paths_web
+ :throttle_authenticated_protected_paths_web,
+ :throttle_authenticated_packages_api
].freeze
PAYLOAD_KEYS = [
diff --git a/lib/gitlab/metrics/web_transaction.rb b/lib/gitlab/metrics/web_transaction.rb
index 1811389a744..ee9e6f449d3 100644
--- a/lib/gitlab/metrics/web_transaction.rb
+++ b/lib/gitlab/metrics/web_transaction.rb
@@ -57,7 +57,7 @@ module Gitlab
begin
route = endpoint.route
- rescue
+ rescue StandardError
# endpoint.route is calling env[Grape::Env::GRAPE_ROUTING_ARGS][:route_info]
# but env[Grape::Env::GRAPE_ROUTING_ARGS] is nil in the case of a 405 response
# so we're rescuing exceptions and bailing out
diff --git a/lib/gitlab/middleware/rack_multipart_tempfile_factory.rb b/lib/gitlab/middleware/rack_multipart_tempfile_factory.rb
index d16c068c3c0..1686c3324b4 100644
--- a/lib/gitlab/middleware/rack_multipart_tempfile_factory.rb
+++ b/lib/gitlab/middleware/rack_multipart_tempfile_factory.rb
@@ -14,9 +14,7 @@ module Gitlab
end
def call(env)
- if ENV['GITLAB_TEMPFILE_IMMEDIATE_UNLINK'] == '1'
- env[Rack::RACK_MULTIPART_TEMPFILE_FACTORY] = FACTORY
- end
+ env[Rack::RACK_MULTIPART_TEMPFILE_FACTORY] = FACTORY
@app.call(env)
end
diff --git a/lib/gitlab/middleware/read_only/controller.rb b/lib/gitlab/middleware/read_only/controller.rb
index 226ef2041b2..65c08664a2b 100644
--- a/lib/gitlab/middleware/read_only/controller.rb
+++ b/lib/gitlab/middleware/read_only/controller.rb
@@ -153,4 +153,4 @@ module Gitlab
end
end
-Gitlab::Middleware::ReadOnly::Controller.prepend_if_ee('EE::Gitlab::Middleware::ReadOnly::Controller')
+Gitlab::Middleware::ReadOnly::Controller.prepend_mod_with('Gitlab::Middleware::ReadOnly::Controller')
diff --git a/lib/gitlab/middleware/speedscope.rb b/lib/gitlab/middleware/speedscope.rb
new file mode 100644
index 00000000000..74f334d9ab3
--- /dev/null
+++ b/lib/gitlab/middleware/speedscope.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Middleware
+ class Speedscope
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ request = ActionDispatch::Request.new(env)
+
+ return @app.call(env) unless rendering_flamegraph?(request)
+
+ body = nil
+
+ ::Gitlab::SafeRequestStore[:capturing_flamegraph] = true
+
+ require 'stackprof'
+
+ begin
+ flamegraph = ::StackProf.run(
+ mode: :wall,
+ raw: true,
+ aggregate: false,
+ interval: ::Gitlab::StackProf::DEFAULT_INTERVAL_US
+ ) do
+ _, _, body = @app.call(env)
+ end
+ ensure
+ body.close if body.respond_to?(:close)
+ end
+
+ render_flamegraph(flamegraph, request)
+ end
+
+ private
+
+ def rendering_flamegraph?(request)
+ request.params['performance_bar'] == 'flamegraph' && ::Gitlab::PerformanceBar.allowed_for_user?(request.env['warden']&.user)
+ end
+
+ def render_flamegraph(graph, request)
+ headers = { 'Content-Type' => 'text/html' }
+ path = request.env['PATH_INFO'].sub('//', '/')
+
+ speedscope_path = ::Gitlab::Utils.append_path(::Gitlab.config.gitlab.relative_url_root, '/-/speedscope/index.html')
+
+ html = <<~HTML
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <style>
+ body { margin: 0; height: 100vh; }
+ #speedscope-iframe { width: 100%; height: 100%; border: none; }
+ </style>
+ </head>
+ <body>
+ <script type="text/javascript" nonce="#{request.content_security_policy_nonce}">
+ var graph = #{Gitlab::Json.generate(graph)};
+ var json = JSON.stringify(graph);
+ var blob = new Blob([json], { type: 'text/plain' });
+ var objUrl = encodeURIComponent(URL.createObjectURL(blob));
+ var iframe = document.createElement('IFRAME');
+ iframe.setAttribute('id', 'speedscope-iframe');
+ document.body.appendChild(iframe);
+ var iframeUrl = '#{speedscope_path}#profileURL=' + objUrl + '&title=' + 'Flamegraph for #{CGI.escape(path)}';
+ iframe.setAttribute('src', iframeUrl);
+ </script>
+ </body>
+ </html>
+ HTML
+
+ [200, headers, [html]]
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/multi_collection_paginator.rb b/lib/gitlab/multi_collection_paginator.rb
index 002171854ad..87cc0a0d3d2 100644
--- a/lib/gitlab/multi_collection_paginator.rb
+++ b/lib/gitlab/multi_collection_paginator.rb
@@ -5,7 +5,7 @@ module Gitlab
attr_reader :first_collection, :second_collection, :per_page
def initialize(*collections, per_page: nil)
- raise ArgumentError.new('Only 2 collections are supported') if collections.size != 2
+ raise ArgumentError, 'Only 2 collections are supported' if collections.size != 2
@per_page = (per_page || Kaminari.config.default_per_page).to_i
@first_collection, @second_collection = collections
diff --git a/lib/gitlab/nav/top_nav_menu_builder.rb b/lib/gitlab/nav/top_nav_menu_builder.rb
new file mode 100644
index 00000000000..721ae1889b8
--- /dev/null
+++ b/lib/gitlab/nav/top_nav_menu_builder.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Nav
+ class TopNavMenuBuilder
+ def initialize
+ @primary = []
+ @secondary = []
+ end
+
+ def add_primary_menu_item(**args)
+ add_menu_item(dest: @primary, **args)
+ end
+
+ def add_secondary_menu_item(**args)
+ add_menu_item(dest: @secondary, **args)
+ end
+
+ def build
+ {
+ primary: @primary,
+ secondary: @secondary
+ }
+ end
+
+ private
+
+ def add_menu_item(dest:, **args)
+ item = ::Gitlab::Nav::TopNavMenuItem.build(**args)
+
+ dest.push(item)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/nav/top_nav_menu_item.rb b/lib/gitlab/nav/top_nav_menu_item.rb
new file mode 100644
index 00000000000..ee11f1f4560
--- /dev/null
+++ b/lib/gitlab/nav/top_nav_menu_item.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Nav
+ class TopNavMenuItem
+ # We want to have all keyword arguments for type safety.
+ # Ordinarily we could introduce a params object, but that's kind of what
+ # this is already :/. We could also take a hash and manually check every
+ # entry, but it's much more maintainable to do rely on native Ruby.
+ # rubocop: disable Metrics/ParameterLists
+ def self.build(id:, title:, active: false, icon: '', href: '', method: nil, view: '', css_class: '', data: {})
+ {
+ id: id,
+ title: title,
+ active: active,
+ icon: icon,
+ href: href,
+ method: method,
+ view: view.to_s,
+ css_class: css_class,
+ data: data
+ }
+ end
+ # rubocop: enable Metrics/ParameterLists
+ end
+ end
+end
diff --git a/lib/gitlab/nav/top_nav_view_model_builder.rb b/lib/gitlab/nav/top_nav_view_model_builder.rb
new file mode 100644
index 00000000000..60f5b267071
--- /dev/null
+++ b/lib/gitlab/nav/top_nav_view_model_builder.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Nav
+ class TopNavViewModelBuilder
+ def initialize
+ @menu_builder = ::Gitlab::Nav::TopNavMenuBuilder.new
+ @views = {}
+ end
+
+ delegate :add_primary_menu_item, :add_secondary_menu_item, to: :@menu_builder
+
+ def add_view(name, props)
+ @views[name] = props
+ end
+
+ def build
+ menu = @menu_builder.build
+
+ menu.merge({
+ views: @views,
+ activeTitle: _('Menu')
+ })
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/object_hierarchy.rb b/lib/gitlab/object_hierarchy.rb
index 9a74266693b..e6e7d97d296 100644
--- a/lib/gitlab/object_hierarchy.rb
+++ b/lib/gitlab/object_hierarchy.rb
@@ -7,18 +7,19 @@ module Gitlab
class ObjectHierarchy
DEPTH_COLUMN = :depth
- attr_reader :ancestors_base, :descendants_base, :model, :options
+ attr_reader :ancestors_base, :descendants_base, :model, :options, :unscoped_model
# ancestors_base - An instance of ActiveRecord::Relation for which to
# get parent objects.
# descendants_base - An instance of ActiveRecord::Relation for which to
# get child objects. If omitted, ancestors_base is used.
def initialize(ancestors_base, descendants_base = ancestors_base, options: {})
- raise ArgumentError.new("Model of ancestors_base does not match model of descendants_base") if ancestors_base.model != descendants_base.model
+ raise ArgumentError, "Model of ancestors_base does not match model of descendants_base" if ancestors_base.model != descendants_base.model
@ancestors_base = ancestors_base
@descendants_base = descendants_base
@model = ancestors_base.model
+ @unscoped_model = @model.unscoped
@options = options
end
@@ -70,23 +71,23 @@ module Gitlab
# if hierarchy_order is given, the calculated `depth` should be present in SELECT
if expose_depth
- recursive_query = base_and_ancestors_cte(upto, hierarchy_order).apply_to(model.all).distinct
- read_only(model.from(Arel::Nodes::As.new(recursive_query.arel, objects_table)).order(depth: hierarchy_order))
+ recursive_query = base_and_ancestors_cte(upto, hierarchy_order).apply_to(unscoped_model.all).distinct
+ read_only(unscoped_model.from(Arel::Nodes::As.new(recursive_query.arel, objects_table)).order(depth: hierarchy_order))
else
- recursive_query = base_and_ancestors_cte(upto).apply_to(model.all)
+ recursive_query = base_and_ancestors_cte(upto).apply_to(unscoped_model.all)
if skip_ordering?
recursive_query = recursive_query.distinct
else
recursive_query = recursive_query.reselect(*recursive_query.arel.projections, 'ROW_NUMBER() OVER () as depth').distinct
- recursive_query = model.from(Arel::Nodes::As.new(recursive_query.arel, objects_table))
+ recursive_query = unscoped_model.from(Arel::Nodes::As.new(recursive_query.arel, objects_table))
recursive_query = remove_depth_and_maintain_order(recursive_query, hierarchy_order: hierarchy_order)
end
read_only(recursive_query)
end
else
- recursive_query = base_and_ancestors_cte(upto, hierarchy_order).apply_to(model.all)
+ recursive_query = base_and_ancestors_cte(upto, hierarchy_order).apply_to(unscoped_model.all)
recursive_query = recursive_query.order(depth: hierarchy_order) if hierarchy_order
read_only(recursive_query)
end
@@ -103,23 +104,23 @@ module Gitlab
if use_distinct?
# Always calculate `depth`, remove it later if with_depth is false
if with_depth
- base_cte = base_and_descendants_cte(with_depth: true).apply_to(model.all).distinct
- read_only(model.from(Arel::Nodes::As.new(base_cte.arel, objects_table)).order(depth: :asc))
+ base_cte = base_and_descendants_cte(with_depth: true).apply_to(unscoped_model.all).distinct
+ read_only(unscoped_model.from(Arel::Nodes::As.new(base_cte.arel, objects_table)).order(depth: :asc))
else
- base_cte = base_and_descendants_cte.apply_to(model.all)
+ base_cte = base_and_descendants_cte.apply_to(unscoped_model.all)
if skip_ordering?
base_cte = base_cte.distinct
else
base_cte = base_cte.reselect(*base_cte.arel.projections, 'ROW_NUMBER() OVER () as depth').distinct
- base_cte = model.from(Arel::Nodes::As.new(base_cte.arel, objects_table))
+ base_cte = unscoped_model.from(Arel::Nodes::As.new(base_cte.arel, objects_table))
base_cte = remove_depth_and_maintain_order(base_cte, hierarchy_order: :asc)
end
read_only(base_cte)
end
else
- read_only(base_and_descendants_cte(with_depth: with_depth).apply_to(model.all))
+ read_only(base_and_descendants_cte(with_depth: with_depth).apply_to(unscoped_model.all))
end
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -154,16 +155,15 @@ module Gitlab
ancestors_table = ancestors.alias_to(objects_table)
descendants_table = descendants.alias_to(objects_table)
- ancestors_scope = model.unscoped.from(ancestors_table)
- descendants_scope = model.unscoped.from(descendants_table)
+ ancestors_scope = unscoped_model.from(ancestors_table)
+ descendants_scope = unscoped_model.from(descendants_table)
if use_distinct?
ancestors_scope = ancestors_scope.distinct
descendants_scope = descendants_scope.distinct
end
- relation = model
- .unscoped
+ relation = unscoped_model
.with
.recursive(ancestors.to_arel, descendants.to_arel)
.from_union([
@@ -215,7 +215,7 @@ module Gitlab
cte << base_query
# Recursively get all the ancestors of the base set.
- parent_query = model
+ parent_query = unscoped_model
.from(from_tables(cte))
.where(ancestor_conditions(cte))
.except(:order)
@@ -248,7 +248,7 @@ module Gitlab
cte << base_query
# Recursively get all the descendants of the base set.
- descendants_query = model
+ descendants_query = unscoped_model
.from(from_tables(cte))
.where(descendant_conditions(cte))
.except(:order)
@@ -298,4 +298,4 @@ module Gitlab
end
end
-Gitlab::ObjectHierarchy.prepend_if_ee('EE::Gitlab::ObjectHierarchy')
+Gitlab::ObjectHierarchy.prepend_mod_with('Gitlab::ObjectHierarchy')
diff --git a/lib/gitlab/omniauth_initializer.rb b/lib/gitlab/omniauth_initializer.rb
index 541f9b06842..3e14e1789bb 100644
--- a/lib/gitlab/omniauth_initializer.rb
+++ b/lib/gitlab/omniauth_initializer.rb
@@ -123,4 +123,4 @@ module Gitlab
end
end
-Gitlab::OmniauthInitializer.prepend_if_ee('::EE::Gitlab::OmniauthInitializer')
+Gitlab::OmniauthInitializer.prepend_mod_with('Gitlab::OmniauthInitializer')
diff --git a/lib/gitlab/otp_key_rotator.rb b/lib/gitlab/otp_key_rotator.rb
index 1d3200aa099..b65c8613d00 100644
--- a/lib/gitlab/otp_key_rotator.rb
+++ b/lib/gitlab/otp_key_rotator.rb
@@ -32,8 +32,8 @@ module Gitlab
def rotate!(old_key:, new_key:)
old_key ||= Gitlab::Application.secrets.otp_key_base
- raise ArgumentError.new("Old key is the same as the new key") if old_key == new_key
- raise ArgumentError.new("New key is too short! Must be 256 bits") if new_key.size < 64
+ raise ArgumentError, "Old key is the same as the new key" if old_key == new_key
+ raise ArgumentError, "New key is too short! Must be 256 bits" if new_key.size < 64
write_csv do |csv|
ActiveRecord::Base.transaction do
diff --git a/lib/gitlab/pages/migration_helper.rb b/lib/gitlab/pages/migration_helper.rb
deleted file mode 100644
index 8f8667fafd9..00000000000
--- a/lib/gitlab/pages/migration_helper.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Pages
- class MigrationHelper
- def initialize(logger = nil)
- @logger = logger
- end
-
- def migrate_to_remote_storage
- deployments = ::PagesDeployment.with_files_stored_locally
- migrate(deployments, ObjectStorage::Store::REMOTE)
- end
-
- def migrate_to_local_storage
- deployments = ::PagesDeployment.with_files_stored_remotely
- migrate(deployments, ObjectStorage::Store::LOCAL)
- end
-
- private
-
- def batch_size
- ENV.fetch('MIGRATION_BATCH_SIZE', 10).to_i
- end
-
- def migrate(deployments, store)
- deployments.find_each(batch_size: batch_size) do |deployment| # rubocop:disable CodeReuse/ActiveRecord
- deployment.file.migrate!(store)
-
- log_success(deployment, store)
- rescue => e
- log_error(e, deployment)
- end
- end
-
- def log_success(deployment, store)
- logger.info("Transferred deployment ID #{deployment.id} of type #{deployment.file_type} with size #{deployment.size} to #{storage_label(store)} storage")
- end
-
- def log_error(err, deployment)
- logger.warn("Failed to transfer deployment of type #{deployment.file_type} and ID #{deployment.id} with error: #{err.message}")
- end
-
- def storage_label(store)
- if store == ObjectStorage::Store::LOCAL
- 'local'
- else
- 'object'
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/pages/settings.rb b/lib/gitlab/pages/settings.rb
index be71018e851..b35683c9dec 100644
--- a/lib/gitlab/pages/settings.rb
+++ b/lib/gitlab/pages/settings.rb
@@ -11,10 +11,6 @@ module Gitlab
super
end
- def local_store
- @local_store ||= ::Gitlab::Pages::Stores::LocalStore.new(super)
- end
-
private
def disk_access_denied?
@@ -25,7 +21,7 @@ module Gitlab
def report_denied_disk_access
raise DiskAccessDenied if disk_access_denied?
- rescue => e
+ rescue StandardError => e
::Gitlab::ErrorTracking.track_exception(e)
end
end
diff --git a/lib/gitlab/pages/stores/local_store.rb b/lib/gitlab/pages/stores/local_store.rb
deleted file mode 100644
index 68a7ebaceff..00000000000
--- a/lib/gitlab/pages/stores/local_store.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Pages
- module Stores
- class LocalStore < ::SimpleDelegator
- def enabled
- return false unless Feature.enabled?(:pages_update_legacy_storage, default_enabled: true)
-
- super
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/pagination/keyset/iterator.rb b/lib/gitlab/pagination/keyset/iterator.rb
new file mode 100644
index 00000000000..3bc8c0bf616
--- /dev/null
+++ b/lib/gitlab/pagination/keyset/iterator.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Pagination
+ module Keyset
+ class Iterator
+ def initialize(scope:, use_union_optimization: false)
+ @scope = scope
+ @order = Gitlab::Pagination::Keyset::Order.extract_keyset_order_object(scope)
+ @use_union_optimization = use_union_optimization
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def each_batch(of: 1000)
+ cursor_attributes = {}
+
+ loop do
+ current_scope = scope.dup.limit(of)
+ relation = order
+ .apply_cursor_conditions(current_scope, cursor_attributes, { use_union_optimization: @use_union_optimization })
+ .reorder(order)
+ .limit(of)
+
+ yield relation
+
+ last_record = relation.last
+ break unless last_record
+
+ cursor_attributes = order.cursor_attributes_for_node(last_record)
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ private
+
+ attr_reader :scope, :order
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/pagination/keyset/order.rb b/lib/gitlab/pagination/keyset/order.rb
index e596e1bac9d..cef3a7b291a 100644
--- a/lib/gitlab/pagination/keyset/order.rb
+++ b/lib/gitlab/pagination/keyset/order.rb
@@ -135,7 +135,7 @@ module Gitlab
#
# (id < 3 AND created_at IS NULL) OR (created_at IS NOT NULL)
def build_where_values(values)
- return if values.blank?
+ return [] if values.blank?
verify_incoming_values!(values)
@@ -156,13 +156,26 @@ module Gitlab
end
end
- build_or_query(where_values)
+ where_values
+ end
+
+ def where_values_with_or_query(values)
+ build_or_query(build_where_values(values.with_indifferent_access))
end
# rubocop: disable CodeReuse/ActiveRecord
- def apply_cursor_conditions(scope, values = {})
+ def apply_cursor_conditions(scope, values = {}, options = { use_union_optimization: false })
+ values ||= {}
+ transformed_values = values.with_indifferent_access
scope = apply_custom_projections(scope)
- scope.where(build_where_values(values.with_indifferent_access))
+
+ where_values = build_where_values(transformed_values)
+
+ if options[:use_union_optimization] && where_values.size > 1
+ build_union_query(scope, where_values).reorder(self)
+ else
+ scope.where(build_or_query(where_values)) # rubocop: disable CodeReuse/ActiveRecord
+ end
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -170,6 +183,8 @@ module Gitlab
self.class.build(column_definitions.map(&:reverse))
end
+ alias_method :to_sql, :to_s
+
private
# Adds extra columns to the SELECT clause
@@ -210,11 +225,19 @@ module Gitlab
end
def build_or_query(expressions)
- or_expression = expressions.reduce { |or_expression, expression| Arel::Nodes::Or.new(or_expression, expression) }
+ return [] if expressions.blank?
+ or_expression = expressions.reduce { |or_expression, expression| Arel::Nodes::Or.new(or_expression, expression) }
Arel::Nodes::Grouping.new(or_expression)
end
+ def build_union_query(scope, where_values)
+ scopes = where_values.map do |where_value|
+ scope.dup.where(where_value).reorder(self) # rubocop: disable CodeReuse/ActiveRecord
+ end
+ scope.model.from_union(scopes, remove_duplicates: false, remove_order: false)
+ end
+
def to_sql_literal(column_definitions)
column_definitions.map do |column_definition|
if column_definition.order_expression.respond_to?(:to_sql)
diff --git a/lib/gitlab/pagination/keyset/simple_order_builder.rb b/lib/gitlab/pagination/keyset/simple_order_builder.rb
new file mode 100644
index 00000000000..5ac5737c3be
--- /dev/null
+++ b/lib/gitlab/pagination/keyset/simple_order_builder.rb
@@ -0,0 +1,137 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Pagination
+ module Keyset
+ # This class transforms the `order()` values from an Activerecord scope into a
+ # Gitlab::Pagination::Keyset::Order instance so the query later can be used in
+ # keyset pagination.
+ #
+ # Return values:
+ # [transformed_scope, true] # true indicates that the new scope was successfully built
+ # [orginal_scope, false] # false indicates that the order values are not supported in this class
+ class SimpleOrderBuilder
+ def self.build(scope)
+ new(scope: scope).build
+ end
+
+ def initialize(scope:)
+ @scope = scope
+ @order_values = scope.order_values
+ @model_class = scope.model
+ @arel_table = @model_class.arel_table
+ @primary_key = @model_class.primary_key
+ end
+
+ def build
+ order = if order_values.empty?
+ primary_key_descending_order
+ elsif ordered_by_primary_key?
+ primary_key_order
+ elsif ordered_by_other_column?
+ column_with_tie_breaker_order
+ elsif ordered_by_other_column_with_tie_breaker?
+ tie_breaker_attribute = order_values.second
+
+ tie_breaker_column_order = Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: model_class.primary_key,
+ order_expression: tie_breaker_attribute
+ )
+
+ column_with_tie_breaker_order(tie_breaker_column_order)
+ end
+
+ order ? [scope.reorder!(order), true] : [scope, false] # [scope, success]
+ end
+
+ private
+
+ attr_reader :scope, :order_values, :model_class, :arel_table, :primary_key
+
+ def primary_key_descending_order
+ Gitlab::Pagination::Keyset::Order.build([
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: model_class.primary_key,
+ order_expression: arel_table[primary_key].desc
+ )
+ ])
+ end
+
+ def primary_key_order
+ Gitlab::Pagination::Keyset::Order.build([
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: model_class.primary_key,
+ order_expression: order_values.first
+ )
+ ])
+ end
+
+ def column_with_tie_breaker_order(tie_breaker_column_order = default_tie_breaker_column_order)
+ order_expression = order_values.first
+ attribute_name = order_expression.expr.name
+
+ column_nullable = model_class.columns.find { |column| column.name == attribute_name }.null
+
+ nullable = if column_nullable && order_expression.is_a?(Arel::Nodes::Ascending)
+ :nulls_last
+ elsif column_nullable && order_expression.is_a?(Arel::Nodes::Descending)
+ :nulls_first
+ else
+ :not_nullable
+ end
+
+ Gitlab::Pagination::Keyset::Order.build([
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: attribute_name,
+ order_expression: order_expression,
+ nullable: nullable,
+ distinct: false
+ ),
+ tie_breaker_column_order
+ ])
+ end
+
+ def ordered_by_primary_key?
+ return unless order_values.one?
+
+ attribute = order_values.first.try(:expr)
+
+ return unless attribute
+
+ arel_table[primary_key].to_s == attribute.to_s
+ end
+
+ def ordered_by_other_column?
+ return unless order_values.one?
+
+ attribute = order_values.first.try(:expr)
+
+ return unless attribute
+ return unless attribute.try(:name)
+
+ model_class.column_names.include?(attribute.name.to_s)
+ end
+
+ def ordered_by_other_column_with_tie_breaker?
+ return unless order_values.size == 2
+
+ attribute = order_values.first.try(:expr)
+ tie_breaker_attribute = order_values.second.try(:expr)
+
+ return unless attribute
+ return unless tie_breaker_attribute
+
+ model_class.column_names.include?(attribute.name.to_s) &&
+ arel_table[primary_key].to_s == tie_breaker_attribute.to_s
+ end
+
+ def default_tie_breaker_column_order
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: model_class.primary_key,
+ order_expression: arel_table[primary_key].desc
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/patch/draw_route.rb b/lib/gitlab/patch/draw_route.rb
index f5fcd5c6093..61b25065e8f 100644
--- a/lib/gitlab/patch/draw_route.rb
+++ b/lib/gitlab/patch/draw_route.rb
@@ -10,7 +10,7 @@ module Gitlab
def draw(routes_name)
drawn_any = draw_ee(routes_name) | draw_ce(routes_name)
- drawn_any || raise(RoutesNotFound.new("Cannot find #{routes_name}"))
+ drawn_any || raise(RoutesNotFound, "Cannot find #{routes_name}")
end
def draw_ce(routes_name)
@@ -37,4 +37,4 @@ module Gitlab
end
end
-Gitlab::Patch::DrawRoute.prepend_if_ee('EE::Gitlab::Patch::DrawRoute')
+Gitlab::Patch::DrawRoute.prepend_mod_with('Gitlab::Patch::DrawRoute')
diff --git a/lib/gitlab/patch/prependable.rb b/lib/gitlab/patch/prependable.rb
index dde78cd9178..1ed341e1c26 100644
--- a/lib/gitlab/patch/prependable.rb
+++ b/lib/gitlab/patch/prependable.rb
@@ -21,7 +21,12 @@ module Gitlab
def prepend_features(base)
return false if prepended?(base)
- super
+ # Rails 6.1 allows prepending of the modules, but it doesn't
+ # work well when both modules extend ActiveSupport::Concern
+ # https://github.com/rails/rails/pull/42067
+ #
+ # Let's keep our own implementation, until the issue is fixed
+ Module.instance_method(:prepend_features).bind(self).call(base)
if const_defined?(:ClassMethods)
klass_methods = const_get(:ClassMethods, false)
diff --git a/lib/gitlab/path_regex.rb b/lib/gitlab/path_regex.rb
index 2ff23980ebd..8618d2da77c 100644
--- a/lib/gitlab/path_regex.rb
+++ b/lib/gitlab/path_regex.rb
@@ -288,4 +288,4 @@ module Gitlab
end
end
-Gitlab::PathRegex.prepend_if_ee('EE::Gitlab::PathRegex')
+Gitlab::PathRegex.prepend_mod_with('Gitlab::PathRegex')
diff --git a/lib/gitlab/performance_bar.rb b/lib/gitlab/performance_bar.rb
index e26309b5dfd..10b13e7f55f 100644
--- a/lib/gitlab/performance_bar.rb
+++ b/lib/gitlab/performance_bar.rb
@@ -7,10 +7,10 @@ module Gitlab
EXPIRY_TIME_L2_CACHE = 5.minutes
def self.enabled_for_request?
- Gitlab::SafeRequestStore[:peek_enabled]
+ !Gitlab::SafeRequestStore[:capturing_flamegraph] && Gitlab::SafeRequestStore[:peek_enabled]
end
- def self.enabled_for_user?(user = nil)
+ def self.allowed_for_user?(user = nil)
return true if Rails.env.development?
return true if user&.admin?
return false unless user && allowed_group_id
diff --git a/lib/gitlab/performance_bar/stats.rb b/lib/gitlab/performance_bar/stats.rb
index c2a4602fd16..103cd65cb4b 100644
--- a/lib/gitlab/performance_bar/stats.rb
+++ b/lib/gitlab/performance_bar/stats.rb
@@ -20,7 +20,7 @@ module Gitlab
return unless data
log_sql_queries(id, data)
- rescue => err
+ rescue StandardError => err
logger.error(message: "failed to process request id #{id}: #{err.message}")
end
diff --git a/lib/gitlab/phabricator_import/conduit/client.rb b/lib/gitlab/phabricator_import/conduit/client.rb
index 4469a3f5849..5945cde9618 100644
--- a/lib/gitlab/phabricator_import/conduit/client.rb
+++ b/lib/gitlab/phabricator_import/conduit/client.rb
@@ -13,7 +13,7 @@ module Gitlab
Response.parse!(response)
rescue *Gitlab::HTTP::HTTP_ERRORS => e
# Wrap all errors from the API into an API-error.
- raise ApiError.new(e)
+ raise ApiError, e
end
private
diff --git a/lib/gitlab/phabricator_import/conduit/response.rb b/lib/gitlab/phabricator_import/conduit/response.rb
index 1b03cfa05e6..26037ba183e 100644
--- a/lib/gitlab/phabricator_import/conduit/response.rb
+++ b/lib/gitlab/phabricator_import/conduit/response.rb
@@ -18,7 +18,7 @@ module Gitlab
response
rescue JSON::JSONError => e
- raise ResponseError.new(e)
+ raise ResponseError, e
end
def initialize(json)
diff --git a/lib/gitlab/phabricator_import/importer.rb b/lib/gitlab/phabricator_import/importer.rb
index ac85b96de08..0666fa0df01 100644
--- a/lib/gitlab/phabricator_import/importer.rb
+++ b/lib/gitlab/phabricator_import/importer.rb
@@ -22,7 +22,7 @@ module Gitlab
schedule_first_tasks_page
true
- rescue => e
+ rescue StandardError => e
fail_import(e.message)
false
diff --git a/lib/gitlab/project_template.rb b/lib/gitlab/project_template.rb
index 32d3eeb8cd2..8875e6320c7 100644
--- a/lib/gitlab/project_template.rb
+++ b/lib/gitlab/project_template.rb
@@ -87,4 +87,4 @@ module Gitlab
end
end
-Gitlab::ProjectTemplate.prepend_if_ee('EE::Gitlab::ProjectTemplate')
+Gitlab::ProjectTemplate.prepend_mod_with('Gitlab::ProjectTemplate')
diff --git a/lib/gitlab/prometheus/adapter.rb b/lib/gitlab/prometheus/adapter.rb
index 76e65d29c7a..45438d9bf7c 100644
--- a/lib/gitlab/prometheus/adapter.rb
+++ b/lib/gitlab/prometheus/adapter.rb
@@ -19,13 +19,11 @@ module Gitlab
end
def cluster_prometheus_adapter
- if cluster&.integration_prometheus
- return cluster.integration_prometheus
- end
-
application = cluster&.application_prometheus
+ return application if application&.available?
- application if application&.available?
+ integration = cluster&.integration_prometheus
+ integration if integration&.available?
end
private
diff --git a/lib/gitlab/prometheus/additional_metrics_parser.rb b/lib/gitlab/prometheus/additional_metrics_parser.rb
index ee3d98f3602..f5eb27b6916 100644
--- a/lib/gitlab/prometheus/additional_metrics_parser.rb
+++ b/lib/gitlab/prometheus/additional_metrics_parser.rb
@@ -14,7 +14,7 @@ module Gitlab
private
def validate!(obj)
- raise ParsingError.new(obj.errors.full_messages.join('\n')) unless obj.valid?
+ raise ParsingError, obj.errors.full_messages.join('\n') unless obj.valid?
end
def group_from_entry(entry)
diff --git a/lib/gitlab/prometheus/metric_group.rb b/lib/gitlab/prometheus/metric_group.rb
index 4a39260a340..020d4cf74a3 100644
--- a/lib/gitlab/prometheus/metric_group.rb
+++ b/lib/gitlab/prometheus/metric_group.rb
@@ -31,4 +31,4 @@ module Gitlab
end
end
-Gitlab::Prometheus::MetricGroup.prepend_if_ee('EE::Gitlab::Prometheus::MetricGroup')
+Gitlab::Prometheus::MetricGroup.prepend_mod_with('Gitlab::Prometheus::MetricGroup')
diff --git a/lib/gitlab/prometheus/queries/query_additional_metrics.rb b/lib/gitlab/prometheus/queries/query_additional_metrics.rb
index d24b98e790b..a870bb6bc5f 100644
--- a/lib/gitlab/prometheus/queries/query_additional_metrics.rb
+++ b/lib/gitlab/prometheus/queries/query_additional_metrics.rb
@@ -98,4 +98,4 @@ module Gitlab
end
end
-Gitlab::Prometheus::Queries::QueryAdditionalMetrics.prepend_if_ee('EE::Gitlab::Prometheus::Queries::QueryAdditionalMetrics')
+Gitlab::Prometheus::Queries::QueryAdditionalMetrics.prepend_mod_with('Gitlab::Prometheus::Queries::QueryAdditionalMetrics')
diff --git a/lib/gitlab/prometheus_client.rb b/lib/gitlab/prometheus_client.rb
index 0fcf63d03fc..8182dbad4f8 100644
--- a/lib/gitlab/prometheus_client.rb
+++ b/lib/gitlab/prometheus_client.rb
@@ -47,7 +47,7 @@ module Gitlab
# From Prometheus docs: This endpoint returns 200 when Prometheus is ready to serve traffic (i.e. respond to queries).
response.code == 200
- rescue => e
+ rescue StandardError => e
raise PrometheusClient::UnexpectedResponseError, "#{e.message}"
end
diff --git a/lib/gitlab/quick_actions/issue_actions.rb b/lib/gitlab/quick_actions/issue_actions.rb
index 012e495502f..ff17ecf8024 100644
--- a/lib/gitlab/quick_actions/issue_actions.rb
+++ b/lib/gitlab/quick_actions/issue_actions.rb
@@ -267,7 +267,7 @@ module Gitlab
private
def zoom_link_service
- Issues::ZoomLinkService.new(quick_action_target, current_user)
+ Issues::ZoomLinkService.new(project: quick_action_target.project, current_user: current_user, params: { issue: quick_action_target })
end
end
end
diff --git a/lib/gitlab/quick_actions/merge_request_actions.rb b/lib/gitlab/quick_actions/merge_request_actions.rb
index 6a404c34044..f3c6315cd6a 100644
--- a/lib/gitlab/quick_actions/merge_request_actions.rb
+++ b/lib/gitlab/quick_actions/merge_request_actions.rb
@@ -148,7 +148,7 @@ module Gitlab
quick_action_target.persisted? && quick_action_target.can_be_approved_by?(current_user)
end
command :approve do
- success = MergeRequests::ApprovalService.new(quick_action_target.project, current_user).execute(quick_action_target)
+ success = MergeRequests::ApprovalService.new(project: quick_action_target.project, current_user: current_user).execute(quick_action_target)
next unless success
diff --git a/lib/gitlab/quick_actions/spend_time_and_date_separator.rb b/lib/gitlab/quick_actions/spend_time_and_date_separator.rb
index 4a62e83e8e9..03b2a1086bb 100644
--- a/lib/gitlab/quick_actions/spend_time_and_date_separator.rb
+++ b/lib/gitlab/quick_actions/spend_time_and_date_separator.rb
@@ -19,7 +19,7 @@ module Gitlab
def execute
return if @spend_arg.blank?
- return [get_time, DateTime.now.to_date] unless date_present?
+ return [get_time, DateTime.current] unless date_present?
return unless valid_date?
[get_time, get_date]
diff --git a/lib/gitlab/quick_actions/substitution_definition.rb b/lib/gitlab/quick_actions/substitution_definition.rb
index 24b4e3c62b3..2cc4a6d90ce 100644
--- a/lib/gitlab/quick_actions/substitution_definition.rb
+++ b/lib/gitlab/quick_actions/substitution_definition.rb
@@ -13,7 +13,7 @@ module Gitlab
return unless content
all_names.each do |a_name|
- content = content.sub(%r{/#{a_name}(?![\S]) ?(.*)$}i, execute_block(action_block, context, '\1'))
+ content = content.sub(%r{/#{a_name}(?!\S) ?(.*)$}i, execute_block(action_block, context, '\1'))
end
content
diff --git a/lib/gitlab/rack_attack.rb b/lib/gitlab/rack_attack.rb
index ae3c89c3565..175f32bd4c6 100644
--- a/lib/gitlab/rack_attack.rb
+++ b/lib/gitlab/rack_attack.rb
@@ -83,16 +83,13 @@ module Gitlab
def self.configure_throttles(rack_attack)
throttle_or_track(rack_attack, 'throttle_unauthenticated', Gitlab::Throttle.unauthenticated_options) do |req|
- if !req.should_be_skipped? &&
- Gitlab::Throttle.settings.throttle_unauthenticated_enabled &&
- req.unauthenticated?
+ if req.throttle_unauthenticated?
req.ip
end
end
throttle_or_track(rack_attack, 'throttle_authenticated_api', Gitlab::Throttle.authenticated_api_options) do |req|
- if req.api_request? &&
- Gitlab::Throttle.settings.throttle_authenticated_api_enabled
+ if req.throttle_authenticated_api?
req.throttled_user_id([:api])
end
end
@@ -107,40 +104,41 @@ module Gitlab
end
throttle_or_track(rack_attack, 'throttle_authenticated_web', Gitlab::Throttle.authenticated_web_options) do |req|
- if req.web_request? &&
- Gitlab::Throttle.settings.throttle_authenticated_web_enabled
+ if req.throttle_authenticated_web?
req.throttled_user_id([:api, :rss, :ics])
end
end
throttle_or_track(rack_attack, 'throttle_unauthenticated_protected_paths', Gitlab::Throttle.protected_paths_options) do |req|
- if req.post? &&
- !req.should_be_skipped? &&
- req.protected_path? &&
- Gitlab::Throttle.protected_paths_enabled? &&
- req.unauthenticated?
+ if req.throttle_unauthenticated_protected_paths?
req.ip
end
end
throttle_or_track(rack_attack, 'throttle_authenticated_protected_paths_api', Gitlab::Throttle.protected_paths_options) do |req|
- if req.post? &&
- req.api_request? &&
- req.protected_path? &&
- Gitlab::Throttle.protected_paths_enabled?
+ if req.throttle_authenticated_protected_paths_api?
req.throttled_user_id([:api])
end
end
throttle_or_track(rack_attack, 'throttle_authenticated_protected_paths_web', Gitlab::Throttle.protected_paths_options) do |req|
- if req.post? &&
- req.web_request? &&
- req.protected_path? &&
- Gitlab::Throttle.protected_paths_enabled?
+ if req.throttle_authenticated_protected_paths_web?
req.throttled_user_id([:api, :rss, :ics])
end
end
+ throttle_or_track(rack_attack, 'throttle_unauthenticated_packages_api', Gitlab::Throttle.unauthenticated_packages_api_options) do |req|
+ if req.throttle_unauthenticated_packages_api?
+ req.ip
+ end
+ end
+
+ throttle_or_track(rack_attack, 'throttle_authenticated_packages_api', Gitlab::Throttle.authenticated_packages_api_options) do |req|
+ if req.throttle_authenticated_packages_api?
+ req.throttled_user_id([:api])
+ end
+ end
+
rack_attack.safelist('throttle_bypass_header') do |req|
Gitlab::Throttle.bypass_header.present? &&
req.get_header(Gitlab::Throttle.bypass_header) == '1'
@@ -173,4 +171,4 @@ module Gitlab
end
end
end
-::Gitlab::RackAttack.prepend_if_ee('::EE::Gitlab::RackAttack')
+::Gitlab::RackAttack.prepend_mod_with('Gitlab::RackAttack')
diff --git a/lib/gitlab/rack_attack/request.rb b/lib/gitlab/rack_attack/request.rb
index bd6d2e016b4..7fee6a1b43d 100644
--- a/lib/gitlab/rack_attack/request.rb
+++ b/lib/gitlab/rack_attack/request.rb
@@ -58,6 +58,57 @@ module Gitlab
path =~ protected_paths_regex
end
+ def throttle_unauthenticated?
+ !should_be_skipped? &&
+ !throttle_unauthenticated_packages_api? &&
+ Gitlab::Throttle.settings.throttle_unauthenticated_enabled &&
+ unauthenticated?
+ end
+
+ def throttle_authenticated_api?
+ api_request? &&
+ !throttle_authenticated_packages_api? &&
+ Gitlab::Throttle.settings.throttle_authenticated_api_enabled
+ end
+
+ def throttle_authenticated_web?
+ web_request? &&
+ Gitlab::Throttle.settings.throttle_authenticated_web_enabled
+ end
+
+ def throttle_unauthenticated_protected_paths?
+ post? &&
+ !should_be_skipped? &&
+ protected_path? &&
+ Gitlab::Throttle.protected_paths_enabled? &&
+ unauthenticated?
+ end
+
+ def throttle_authenticated_protected_paths_api?
+ post? &&
+ api_request? &&
+ protected_path? &&
+ Gitlab::Throttle.protected_paths_enabled?
+ end
+
+ def throttle_authenticated_protected_paths_web?
+ post? &&
+ web_request? &&
+ protected_path? &&
+ Gitlab::Throttle.protected_paths_enabled?
+ end
+
+ def throttle_unauthenticated_packages_api?
+ packages_api_path? &&
+ Gitlab::Throttle.settings.throttle_unauthenticated_packages_api_enabled &&
+ unauthenticated?
+ end
+
+ def throttle_authenticated_packages_api?
+ packages_api_path? &&
+ Gitlab::Throttle.settings.throttle_authenticated_packages_api_enabled
+ end
+
private
def authenticated_user_id(request_formats)
@@ -75,7 +126,11 @@ module Gitlab
def protected_paths_regex
Regexp.union(protected_paths.map { |path| /\A#{Regexp.escape(path)}/ })
end
+
+ def packages_api_path?
+ path =~ ::Gitlab::Regex::Packages::API_PATH_REGEX
+ end
end
end
end
-::Gitlab::RackAttack::Request.prepend_if_ee('::EE::Gitlab::RackAttack::Request')
+::Gitlab::RackAttack::Request.prepend_mod_with('Gitlab::RackAttack::Request')
diff --git a/lib/gitlab/redis/boolean.rb b/lib/gitlab/redis/boolean.rb
index 9b0b20fc2be..cd0877c5b13 100644
--- a/lib/gitlab/redis/boolean.rb
+++ b/lib/gitlab/redis/boolean.rb
@@ -50,7 +50,7 @@ module Gitlab
# @return [String] the encoded boolean
# @raise [NotABooleanError] if the value isn't true or false
def encode(value)
- raise NotABooleanError.new(value) unless bool?(value)
+ raise NotABooleanError, value unless bool?(value)
[LABEL, to_string(value)].join(DELIMITER)
end
@@ -61,11 +61,11 @@ module Gitlab
# @return [Boolean] true or false
# @raise [NotAnEncodedBooleanStringError] if the provided value isn't an encoded boolean
def decode(value)
- raise NotAnEncodedBooleanStringError.new(value.class) unless value.is_a?(String)
+ raise NotAnEncodedBooleanStringError, value.class unless value.is_a?(String)
label, bool_str = *value.split(DELIMITER, 2)
- raise NotAnEncodedBooleanStringError.new(label) unless label == LABEL
+ raise NotAnEncodedBooleanStringError, label unless label == LABEL
from_string(bool_str)
end
@@ -99,7 +99,7 @@ module Gitlab
end
def from_string(str)
- raise NotAnEncodedBooleanStringError.new(str) unless [TRUE_STR, FALSE_STR].include?(str)
+ raise NotAnEncodedBooleanStringError, str unless [TRUE_STR, FALSE_STR].include?(str)
str == TRUE_STR
end
diff --git a/lib/gitlab/redis/hll.rb b/lib/gitlab/redis/hll.rb
index 010a6b59da5..0d04545688b 100644
--- a/lib/gitlab/redis/hll.rb
+++ b/lib/gitlab/redis/hll.rb
@@ -46,7 +46,7 @@ module Gitlab
def validate_key!(key)
return if KEY_REGEX.match?(key)
- raise KeyFormatError.new("Invalid key format. #{key} key should have changeable parts in curly braces. See https://docs.gitlab.com/ee/development/redis.html#multi-key-commands")
+ raise KeyFormatError, "Invalid key format. #{key} key should have changeable parts in curly braces. See https://docs.gitlab.com/ee/development/redis.html#multi-key-commands"
end
end
end
diff --git a/lib/gitlab/redis/wrapper.rb b/lib/gitlab/redis/wrapper.rb
index 6f80c7d439f..94ab67ef08a 100644
--- a/lib/gitlab/redis/wrapper.rb
+++ b/lib/gitlab/redis/wrapper.rb
@@ -142,7 +142,7 @@ module Gitlab
def fetch_config
return false unless self.class._raw_config
- yaml = YAML.load(self.class._raw_config)
+ yaml = YAML.safe_load(self.class._raw_config, aliases: true)
# If the file has content but it's invalid YAML, `load` returns false
if yaml
diff --git a/lib/gitlab/reference_counter.rb b/lib/gitlab/reference_counter.rb
index c2fa2e1330a..f41e42b9e9c 100644
--- a/lib/gitlab/reference_counter.rb
+++ b/lib/gitlab/reference_counter.rb
@@ -84,7 +84,7 @@ module Gitlab
Gitlab::Redis::SharedState.with { |redis| yield(redis) }
true
- rescue => e
+ rescue StandardError => e
Gitlab::AppLogger.warn("GitLab: An unexpected error occurred in writing to Redis: #{e}")
false
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index 488ba04f87c..ccb4f6e1097 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -6,6 +6,8 @@ module Gitlab
CONAN_RECIPE_FILES = %w[conanfile.py conanmanifest.txt conan_sources.tgz conan_export.tgz].freeze
CONAN_PACKAGE_FILES = %w[conaninfo.txt conanmanifest.txt conan_package.tgz].freeze
+ API_PATH_REGEX = %r{^/api/v\d+/(projects/[^/]+/|groups?/[^/]+/-/)?packages/[A-Za-z]+}.freeze
+
def conan_package_reference_regex
@conan_package_reference_regex ||= %r{\A[A-Za-z0-9]+\z}.freeze
end
@@ -75,6 +77,10 @@ module Gitlab
/x.freeze
end
+ def terraform_module_package_name_regex
+ @terraform_module_package_name_regex ||= %r{\A[-a-z0-9]+\/[-a-z0-9]+\z}.freeze
+ end
+
def pypi_version_regex
# See the official regex: https://github.com/pypa/packaging/blob/16.7/packaging/version.py#L159
@@ -123,6 +129,18 @@ module Gitlab
@debian_component_regex ||= %r{#{debian_distribution_regex}}.freeze
end
+ def helm_channel_regex
+ @helm_channel_regex ||= %r{\A[-\.\_a-zA-Z0-9]+\z}.freeze
+ end
+
+ def helm_package_regex
+ @helm_package_regex ||= %r{#{helm_channel_regex}}.freeze
+ end
+
+ def helm_version_regex
+ @helm_version_regex ||= %r{#{prefixed_semver_regex}}.freeze
+ end
+
def unbounded_semver_regex
# See the official regex: https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
@@ -135,7 +153,7 @@ module Gitlab
end
def semver_regex
- @semver_regex ||= Regexp.new("\\A#{::Gitlab::Regex.unbounded_semver_regex.source}\\z", ::Gitlab::Regex.unbounded_semver_regex.options)
+ @semver_regex ||= Regexp.new("\\A#{::Gitlab::Regex.unbounded_semver_regex.source}\\z", ::Gitlab::Regex.unbounded_semver_regex.options).freeze
end
# These partial semver regexes are intended for use in composing other
@@ -235,7 +253,7 @@ module Gitlab
# used as a routing constraint.
#
def container_registry_tag_regex
- @container_registry_tag_regex ||= /[\w][\w.-]{0,127}/
+ @container_registry_tag_regex ||= /\w[\w.-]{0,127}/
end
def environment_name_regex_chars
diff --git a/lib/gitlab/relative_positioning.rb b/lib/gitlab/relative_positioning.rb
index e2cbe4b2de0..c2a73b7cfe5 100644
--- a/lib/gitlab/relative_positioning.rb
+++ b/lib/gitlab/relative_positioning.rb
@@ -13,7 +13,9 @@ module Gitlab
MIN_GAP = 2
NoSpaceLeft = Class.new(StandardError)
+ InvalidPosition = Class.new(StandardError)
IllegalRange = Class.new(ArgumentError)
+ IssuePositioningDisabled = Class.new(StandardError)
def self.range(lhs, rhs)
if lhs && rhs
diff --git a/lib/gitlab/relative_positioning/item_context.rb b/lib/gitlab/relative_positioning/item_context.rb
index 8f5495ece5e..1e738aef9b0 100644
--- a/lib/gitlab/relative_positioning/item_context.rb
+++ b/lib/gitlab/relative_positioning/item_context.rb
@@ -129,6 +129,14 @@ module Gitlab
neighbour(sib)
end
+ def at_position(position)
+ item = scoped_items.find_by(relative_position: position)
+
+ raise InvalidPosition, 'No item found at the specified position' if item.nil?
+
+ neighbour(item)
+ end
+
def shift_left
move_sequence_before(true)
object.reset_relative_position
diff --git a/lib/gitlab/repo_path.rb b/lib/gitlab/repo_path.rb
index 46c84107e0f..42b94d5cf3b 100644
--- a/lib/gitlab/repo_path.rb
+++ b/lib/gitlab/repo_path.rb
@@ -110,4 +110,4 @@ module Gitlab
end
end
-Gitlab::RepoPath.singleton_class.prepend_if_ee('EE::Gitlab::RepoPath::ClassMethods')
+Gitlab::RepoPath.singleton_class.prepend_mod_with('Gitlab::RepoPath::ClassMethods')
diff --git a/lib/gitlab/repository_size_checker.rb b/lib/gitlab/repository_size_checker.rb
index 0ed31176dd8..2afc5e8d668 100644
--- a/lib/gitlab/repository_size_checker.rb
+++ b/lib/gitlab/repository_size_checker.rb
@@ -56,4 +56,4 @@ module Gitlab
end
end
-Gitlab::RepositorySizeChecker.prepend_if_ee('EE::Gitlab::RepositorySizeChecker')
+Gitlab::RepositorySizeChecker.prepend_mod_with('Gitlab::RepositorySizeChecker')
diff --git a/lib/gitlab/repository_url_builder.rb b/lib/gitlab/repository_url_builder.rb
index a2d0d50d20b..ed9a298ee8c 100644
--- a/lib/gitlab/repository_url_builder.rb
+++ b/lib/gitlab/repository_url_builder.rb
@@ -10,7 +10,7 @@ module Gitlab
when :http
http_url(path)
else
- raise NotImplementedError.new("No URL builder defined for protocol #{protocol}")
+ raise NotImplementedError, "No URL builder defined for protocol #{protocol}"
end
end
diff --git a/lib/gitlab/request_profiler/middleware.rb b/lib/gitlab/request_profiler/middleware.rb
index 7050aee3847..acdf8d4541f 100644
--- a/lib/gitlab/request_profiler/middleware.rb
+++ b/lib/gitlab/request_profiler/middleware.rb
@@ -90,7 +90,7 @@ module Gitlab
File.open(file_path, 'wb') do |file|
yield(file)
end
- rescue
+ rescue StandardError
FileUtils.rm(file_path)
end
end
diff --git a/lib/gitlab/route_map.rb b/lib/gitlab/route_map.rb
index a555bf1d812..65c7f4b39e5 100644
--- a/lib/gitlab/route_map.rb
+++ b/lib/gitlab/route_map.rb
@@ -7,7 +7,7 @@ module Gitlab
def initialize(data)
begin
entries = YAML.safe_load(data)
- rescue
+ rescue StandardError
raise FormatError, 'Route map is not valid YAML'
end
diff --git a/lib/gitlab/routing.rb b/lib/gitlab/routing.rb
index cad127922df..fd9fb8ab7e2 100644
--- a/lib/gitlab/routing.rb
+++ b/lib/gitlab/routing.rb
@@ -30,7 +30,7 @@ module Gitlab
rescue URI::InvalidURIError => e
# If url is invalid, raise custom error,
# which can be ignored by monitoring tools.
- raise ActionController::RoutingError.new(e.message)
+ raise ActionController::RoutingError, e.message
end
end
diff --git a/lib/gitlab/runtime.rb b/lib/gitlab/runtime.rb
index 968ef06b085..b0bcea0ca69 100644
--- a/lib/gitlab/runtime.rb
+++ b/lib/gitlab/runtime.rb
@@ -26,13 +26,9 @@ module Gitlab
if matches.one?
matches.first
elsif matches.none?
- raise UnknownProcessError.new(
- "Failed to identify runtime for process #{Process.pid} (#{$0})"
- )
+ raise UnknownProcessError, "Failed to identify runtime for process #{Process.pid} (#{$0})"
else
- raise AmbiguousProcessError.new(
- "Ambiguous runtime #{matches} for process #{Process.pid} (#{$0})"
- )
+ raise AmbiguousProcessError, "Ambiguous runtime #{matches} for process #{Process.pid} (#{$0})"
end
end
@@ -91,7 +87,7 @@ module Gitlab
def max_threads
threads = 1 # main thread
- if puma?
+ if puma? && Puma.respond_to?(:cli_config)
threads += Puma.cli_config.options[:max_threads]
elsif sidekiq?
# An extra thread for the poller in Sidekiq Cron:
diff --git a/lib/gitlab/sanitizers/exif.rb b/lib/gitlab/sanitizers/exif.rb
index eec50deb61e..f607aff9d29 100644
--- a/lib/gitlab/sanitizers/exif.rb
+++ b/lib/gitlab/sanitizers/exif.rb
@@ -71,7 +71,7 @@ module Gitlab
relation.find_each(**find_params) do |upload|
clean(upload.retrieve_uploader, dry_run: dry_run)
sleep sleep_time if sleep_time
- rescue => err
+ rescue StandardError => err
logger.error "failed to sanitize #{upload_ref(upload)}: #{err.message}"
logger.debug err.backtrace.join("\n ")
end
diff --git a/lib/gitlab/search/parsed_query.rb b/lib/gitlab/search/parsed_query.rb
index 5d5d407c172..a397ce935cb 100644
--- a/lib/gitlab/search/parsed_query.rb
+++ b/lib/gitlab/search/parsed_query.rb
@@ -50,11 +50,11 @@ module Gitlab
when :including then including
when :excluding then excluding
else
- raise ArgumentError.new(type)
+ raise ArgumentError, type
end
end
end
end
end
-Gitlab::Search::ParsedQuery.prepend_if_ee('EE::Gitlab::Search::ParsedQuery')
+Gitlab::Search::ParsedQuery.prepend_mod_with('Gitlab::Search::ParsedQuery')
diff --git a/lib/gitlab/search_context.rb b/lib/gitlab/search_context.rb
index 0323220690a..04ef2be87f8 100644
--- a/lib/gitlab/search_context.rb
+++ b/lib/gitlab/search_context.rb
@@ -164,4 +164,4 @@ module Gitlab
end
end
-Gitlab::SearchContext::Builder.prepend_ee_mod
+Gitlab::SearchContext::Builder.prepend_mod
diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb
index d0beb74c289..678c0b396ef 100644
--- a/lib/gitlab/search_results.rb
+++ b/lib/gitlab/search_results.rb
@@ -236,4 +236,4 @@ module Gitlab
end
end
-Gitlab::SearchResults.prepend_if_ee('EE::Gitlab::SearchResults')
+Gitlab::SearchResults.prepend_mod_with('Gitlab::SearchResults')
diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb
index 3419989c110..d26e1a34a9f 100644
--- a/lib/gitlab/shell.rb
+++ b/lib/gitlab/shell.rb
@@ -92,7 +92,7 @@ module Gitlab
Gitlab::Git::Repository.new(storage, "#{disk_path}.git", nil, nil).rename("#{new_disk_path}.git")
true
- rescue => e
+ rescue StandardError => e
Gitlab::ErrorTracking.track_exception(e, path: disk_path, new_path: new_disk_path, storage: storage)
false
@@ -115,7 +115,7 @@ module Gitlab
Gitlab::Git::Repository.new(storage, "#{disk_path}.git", nil, nil).remove
true
- rescue => e
+ rescue StandardError => e
Gitlab::AppLogger.warn("Repository does not exist: #{e} at: #{disk_path}.git")
Gitlab::ErrorTracking.track_exception(e, path: disk_path, storage: storage)
diff --git a/lib/gitlab/sidekiq_config.rb b/lib/gitlab/sidekiq_config.rb
index 78d45b5f3f0..16a0619daf6 100644
--- a/lib/gitlab/sidekiq_config.rb
+++ b/lib/gitlab/sidekiq_config.rb
@@ -21,8 +21,18 @@ module Gitlab
# invalid class name. We keep it in the YAML file for safety, just
# in case anything does get scheduled to run there.
DEFAULT_WORKERS = {
- '_' => DummyWorker.new('default', weight: 1, tags: []),
- 'ActionMailer::MailDeliveryJob' => DummyWorker.new('mailers', feature_category: :issue_tracking, urgency: 'low', weight: 2, tags: [])
+ '_' => DummyWorker.new(
+ queue: 'default',
+ weight: 1, tags: []
+ ),
+ 'ActionMailer::MailDeliveryJob' => DummyWorker.new(
+ name: 'ActionMailer::MailDeliveryJob',
+ queue: 'mailers',
+ feature_category: :issue_tracking,
+ urgency: 'low',
+ weight: 2,
+ tags: []
+ )
}.transform_values { |worker| Gitlab::SidekiqConfig::Worker.new(worker, ee: false) }.freeze
class << self
diff --git a/lib/gitlab/sidekiq_config/dummy_worker.rb b/lib/gitlab/sidekiq_config/dummy_worker.rb
index 7568840410b..ef0dce0cf84 100644
--- a/lib/gitlab/sidekiq_config/dummy_worker.rb
+++ b/lib/gitlab/sidekiq_config/dummy_worker.rb
@@ -4,9 +4,9 @@ module Gitlab
module SidekiqConfig
# For queues that don't have explicit workers - default and mailers
class DummyWorker
- attr_accessor :queue
-
ATTRIBUTE_METHODS = {
+ queue: :queue,
+ name: :name,
feature_category: :get_feature_category,
has_external_dependencies: :worker_has_external_dependencies?,
urgency: :get_urgency,
@@ -16,8 +16,7 @@ module Gitlab
tags: :get_tags
}.freeze
- def initialize(queue, attributes = {})
- @queue = queue
+ def initialize(attributes = {})
@attributes = attributes
end
diff --git a/lib/gitlab/sidekiq_config/worker.rb b/lib/gitlab/sidekiq_config/worker.rb
index 46fa0aa5be1..aea4209f631 100644
--- a/lib/gitlab/sidekiq_config/worker.rb
+++ b/lib/gitlab/sidekiq_config/worker.rb
@@ -6,10 +6,9 @@ module Gitlab
include Comparable
attr_reader :klass
- delegate :feature_category_not_owned?, :get_feature_category, :get_tags,
- :get_urgency, :get_weight, :get_worker_resource_boundary,
- :idempotent?, :queue, :queue_namespace,
- :worker_has_external_dependencies?,
+ delegate :feature_category_not_owned?, :get_feature_category, :get_sidekiq_options,
+ :get_tags, :get_urgency, :get_weight, :get_worker_resource_boundary,
+ :idempotent?, :queue, :queue_namespace, :worker_has_external_dependencies?,
to: :klass
def initialize(klass, ee:)
@@ -47,6 +46,7 @@ module Gitlab
def to_yaml
{
name: queue,
+ worker_name: klass.name,
feature_category: get_feature_category,
has_external_dependencies: worker_has_external_dependencies?,
urgency: get_urgency,
@@ -64,6 +64,10 @@ module Gitlab
def queue_and_weight
[queue, get_weight]
end
+
+ def retries
+ get_sidekiq_options['retry']
+ end
end
end
end
diff --git a/lib/gitlab/sidekiq_config/worker_matcher.rb b/lib/gitlab/sidekiq_config/worker_matcher.rb
index fe5ac10c65a..d615d5ecba4 100644
--- a/lib/gitlab/sidekiq_config/worker_matcher.rb
+++ b/lib/gitlab/sidekiq_config/worker_matcher.rb
@@ -10,6 +10,7 @@ module Gitlab
QUERY_TERM_REGEX = %r{^(\w+)(!?=)([\w:#{QUERY_CONCATENATE_OPERATOR}]+)}.freeze
QUERY_PREDICATES = {
+ worker_name: :to_s,
feature_category: :to_sym,
has_external_dependencies: lambda { |value| value == 'true' },
name: :to_s,
@@ -50,7 +51,7 @@ module Gitlab
def predicate_for_term(term)
match = term.match(QUERY_TERM_REGEX)
- raise InvalidTerm.new("Invalid term: #{term}") unless match
+ raise InvalidTerm, "Invalid term: #{term}" unless match
_, lhs, op, rhs = *match
@@ -66,14 +67,14 @@ module Gitlab
else
# This is unreachable because InvalidTerm will be raised instead, but
# keeping it allows to guard against that changing in future.
- raise UnknownOperator.new("Unknown operator: #{op}")
+ raise UnknownOperator, "Unknown operator: #{op}"
end
end
def predicate_factory(lhs, values)
values_block = QUERY_PREDICATES[lhs.to_sym]
- raise UnknownPredicate.new("Unknown predicate: #{lhs}") unless values_block
+ raise UnknownPredicate, "Unknown predicate: #{lhs}" unless values_block
lambda do |queue|
comparator = Array(queue[lhs.to_sym]).to_set
diff --git a/lib/gitlab/sidekiq_config/worker_router.rb b/lib/gitlab/sidekiq_config/worker_router.rb
new file mode 100644
index 00000000000..946296a24d3
--- /dev/null
+++ b/lib/gitlab/sidekiq_config/worker_router.rb
@@ -0,0 +1,107 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module SidekiqConfig
+ class WorkerRouter
+ InvalidRoutingRuleError = Class.new(StandardError)
+ RuleEvaluator = Struct.new(:matcher, :queue_name)
+
+ def self.queue_name_from_worker_name(worker_klass)
+ base_queue_name =
+ worker_klass.name
+ .delete_prefix('Gitlab::')
+ .delete_suffix('Worker')
+ .underscore
+ .tr('/', '_')
+ [worker_klass.queue_namespace, base_queue_name].compact.join(':')
+ end
+
+ def self.global
+ @global_worker_router ||= new(::Gitlab.config.sidekiq.routing_rules)
+ rescue InvalidRoutingRuleError, ::Gitlab::SidekiqConfig::WorkerMatcher::UnknownPredicate => e
+ ::Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)
+
+ @global_worker_router = new([])
+ end
+
+ # call-seq:
+ # router = WorkerRouter.new([
+ # ["resource_boundary=cpu", 'cpu_boundary'],
+ # ["feature_category=pages", nil],
+ # ["feature_category=source_code_management", ''],
+ # ["*", "default"]
+ # ])
+ # router.route(ACpuBoundaryWorker) # Return "cpu_boundary"
+ # router.route(JustAPagesWorker) # Return "just_a_pages_worker"
+ # router.route(PostReceive) # Return "post_receive"
+ # router.route(RandomWorker) # Return "default"
+ #
+ # This class is responsible for routing a Sidekiq worker to a certain
+ # queue defined in the input routing rules. The input routing rules, as
+ # described above, is an order-matter array of tuples [query, queue_name].
+ #
+ # - The query syntax is the same as the "queue selector" detailedly
+ # denoted in doc/administration/operations/extra_sidekiq_processes.md.
+ #
+ # - The queue_name must be a valid Sidekiq queue name. If the queue name
+ # is nil, or an empty string, the worker is routed to the queue generated
+ # by the name of the worker instead.
+ #
+ # Rules are evaluated from first to last, and as soon as we find a match
+ # for a given worker we stop processing for that worker (first match
+ # wins). If the worker doesn't match any rule, it falls back the queue
+ # name generated from the worker name
+ #
+ # For further information, please visit:
+ # https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/1016
+ #
+ def initialize(routing_rules)
+ @rule_evaluators = parse_routing_rules(routing_rules)
+ end
+
+ def route(worker_klass)
+ # A medium representation to ensure the backward-compatibility of
+ # WorkerMatcher
+ worker_metadata = generate_worker_metadata(worker_klass)
+ @rule_evaluators.each do |evaluator|
+ if evaluator.matcher.match?(worker_metadata)
+ return evaluator.queue_name.presence || queue_name_from_worker_name(worker_klass)
+ end
+ end
+
+ queue_name_from_worker_name(worker_klass)
+ end
+
+ private
+
+ def parse_routing_rules(routing_rules)
+ raise InvalidRoutingRuleError, 'The set of routing rule must be an array' unless routing_rules.is_a?(Array)
+
+ routing_rules.map do |rule_tuple|
+ raise InvalidRoutingRuleError, "Routing rule `#{rule_tuple.inspect}` is invalid" unless valid_routing_rule?(rule_tuple)
+
+ selector, destination_queue = rule_tuple
+ RuleEvaluator.new(
+ ::Gitlab::SidekiqConfig::WorkerMatcher.new(selector),
+ destination_queue
+ )
+ end
+ end
+
+ def valid_routing_rule?(rule_tuple)
+ rule_tuple.is_a?(Array) && rule_tuple.length == 2
+ end
+
+ def generate_worker_metadata(worker_klass)
+ # The ee indicator here is insignificant and irrelevant to the matcher.
+ # Plus, it's not easy to determine whether a worker is **only**
+ # available in EE.
+ ::Gitlab::SidekiqConfig::Worker.new(worker_klass, ee: false).to_yaml
+ end
+
+ def queue_name_from_worker_name(worker_klass)
+ self.class.queue_name_from_worker_name(worker_klass)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/sidekiq_daemon/memory_killer.rb b/lib/gitlab/sidekiq_daemon/memory_killer.rb
index 8793a672693..113076a6a75 100644
--- a/lib/gitlab/sidekiq_daemon/memory_killer.rb
+++ b/lib/gitlab/sidekiq_daemon/memory_killer.rb
@@ -73,7 +73,7 @@ module Gitlab
begin
sleep(CHECK_INTERVAL_SECONDS)
restart_sidekiq unless rss_within_range?
- rescue => e
+ rescue StandardError => e
log_exception(e, __method__)
rescue Exception => e # rubocop:disable Lint/RescueException
log_exception(e, __method__ )
@@ -249,7 +249,7 @@ module Gitlab
def get_job_options(job, key, default)
job[:worker_class].sidekiq_options.fetch(key, default)
- rescue
+ rescue StandardError
default
end
diff --git a/lib/gitlab/sidekiq_logging/structured_logger.rb b/lib/gitlab/sidekiq_logging/structured_logger.rb
index b1fb3771c78..87fb36d04e9 100644
--- a/lib/gitlab/sidekiq_logging/structured_logger.rb
+++ b/lib/gitlab/sidekiq_logging/structured_logger.rb
@@ -30,7 +30,7 @@ module Gitlab
Sidekiq.logger.warn log_job_done(job, started_time, base_payload, job_exception.cause || job_exception)
raise
- rescue => job_exception
+ rescue StandardError => job_exception
Sidekiq.logger.warn log_job_done(job, started_time, base_payload, job_exception)
raise
@@ -39,7 +39,7 @@ module Gitlab
private
def add_instrumentation_keys!(job, output_payload)
- output_payload.merge!(job[:instrumentation].stringify_keys)
+ output_payload.merge!(job[:instrumentation].stringify_keys) if job[:instrumentation]
end
def add_logging_extras!(job, output_payload)
@@ -70,6 +70,8 @@ module Gitlab
message = base_message(payload)
+ payload['database_chosen'] = job[:database_chosen] if job[:database_chosen]
+
if job_exception
payload['message'] = "#{message}: fail: #{payload['duration_s']} sec"
payload['job_status'] = 'fail'
diff --git a/lib/gitlab/sidekiq_middleware.rb b/lib/gitlab/sidekiq_middleware.rb
index 563a105484d..c5b980769f0 100644
--- a/lib/gitlab/sidekiq_middleware.rb
+++ b/lib/gitlab/sidekiq_middleware.rb
@@ -44,4 +44,4 @@ module Gitlab
end
end
-Gitlab::SidekiqMiddleware.singleton_class.prepend_if_ee('EE::Gitlab::SidekiqMiddleware')
+Gitlab::SidekiqMiddleware.singleton_class.prepend_mod_with('Gitlab::SidekiqMiddleware')
diff --git a/lib/gitlab/sidekiq_middleware/server_metrics.rb b/lib/gitlab/sidekiq_middleware/server_metrics.rb
index f5fee8050ac..474afffcf93 100644
--- a/lib/gitlab/sidekiq_middleware/server_metrics.rb
+++ b/lib/gitlab/sidekiq_middleware/server_metrics.rb
@@ -119,4 +119,4 @@ module Gitlab
end
end
-Gitlab::SidekiqMiddleware::ServerMetrics.prepend_if_ee('EE::Gitlab::SidekiqMiddleware::ServerMetrics')
+Gitlab::SidekiqMiddleware::ServerMetrics.prepend_mod_with('Gitlab::SidekiqMiddleware::ServerMetrics')
diff --git a/lib/gitlab/sidekiq_middleware/size_limiter/exceed_limit_error.rb b/lib/gitlab/sidekiq_middleware/size_limiter/exceed_limit_error.rb
index da6c903ccae..540159e8a72 100644
--- a/lib/gitlab/sidekiq_middleware/size_limiter/exceed_limit_error.rb
+++ b/lib/gitlab/sidekiq_middleware/size_limiter/exceed_limit_error.rb
@@ -13,7 +13,7 @@ module Gitlab
@size = size
@size_limit = size_limit
- super "#{@worker_class} job exceeds payload size limit (#{size}/#{size_limit})"
+ super "#{@worker_class} job exceeds payload size limit"
end
def sentry_extra_data
diff --git a/lib/gitlab/sidekiq_migrate_jobs.rb b/lib/gitlab/sidekiq_migrate_jobs.rb
new file mode 100644
index 00000000000..62d62bf82c4
--- /dev/null
+++ b/lib/gitlab/sidekiq_migrate_jobs.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+module Gitlab
+ class SidekiqMigrateJobs
+ LOG_FREQUENCY = 1_000
+
+ attr_reader :sidekiq_set, :logger
+
+ def initialize(sidekiq_set, logger: nil)
+ @sidekiq_set = sidekiq_set
+ @logger = logger
+ end
+
+ # mappings is a hash of WorkerClassName => target_queue_name
+ def execute(mappings)
+ source_queues_regex = Regexp.union(mappings.keys)
+ cursor = 0
+ scanned = 0
+ migrated = 0
+
+ estimated_size = Sidekiq.redis { |c| c.zcard(sidekiq_set) }
+ logger&.info("Processing #{sidekiq_set} set. Estimated size: #{estimated_size}.")
+
+ begin
+ cursor, jobs = Sidekiq.redis { |c| c.zscan(sidekiq_set, cursor) }
+
+ jobs.each do |(job, score)|
+ if scanned > 0 && scanned % LOG_FREQUENCY == 0
+ logger&.info("In progress. Scanned records: #{scanned}. Migrated records: #{migrated}.")
+ end
+
+ scanned += 1
+
+ next unless job.match?(source_queues_regex)
+
+ job_hash = Sidekiq.load_json(job)
+ destination_queue = mappings[job_hash['class']]
+
+ next unless mappings.has_key?(job_hash['class'])
+ next if job_hash['queue'] == destination_queue
+
+ job_hash['queue'] = destination_queue
+
+ migrated += migrate_job(job, score, job_hash)
+ end
+ end while cursor.to_i != 0
+
+ logger&.info("Done. Scanned records: #{scanned}. Migrated records: #{migrated}.")
+
+ {
+ scanned: scanned,
+ migrated: migrated
+ }
+ end
+
+ private
+
+ def migrate_job(job, score, job_hash)
+ Sidekiq.redis do |connection|
+ removed = connection.zrem(sidekiq_set, job)
+
+ if removed
+ connection.zadd(sidekiq_set, score, Sidekiq.dump_json(job_hash))
+
+ 1
+ else
+ 0
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/sidekiq_status.rb b/lib/gitlab/sidekiq_status.rb
index 2293e2adee1..623fdd89456 100644
--- a/lib/gitlab/sidekiq_status.rb
+++ b/lib/gitlab/sidekiq_status.rb
@@ -66,7 +66,7 @@ module Gitlab
def self.num_running(job_ids)
responses = self.job_status(job_ids)
- responses.select(&:present?).count
+ responses.count(&:present?)
end
# Returns the number of jobs that have completed.
diff --git a/lib/gitlab/slash_commands/issue_close.rb b/lib/gitlab/slash_commands/issue_close.rb
index 5fcc86e91c4..3dad7216983 100644
--- a/lib/gitlab/slash_commands/issue_close.rb
+++ b/lib/gitlab/slash_commands/issue_close.rb
@@ -29,7 +29,7 @@ module Gitlab
private
def close_issue(issue:)
- Issues::CloseService.new(project, current_user).execute(issue)
+ Issues::CloseService.new(project: project, current_user: current_user).execute(issue)
end
def presenter(issue)
diff --git a/lib/gitlab/slash_commands/issue_move.rb b/lib/gitlab/slash_commands/issue_move.rb
index d2f1f130b38..0612663017c 100644
--- a/lib/gitlab/slash_commands/issue_move.rb
+++ b/lib/gitlab/slash_commands/issue_move.rb
@@ -29,7 +29,7 @@ module Gitlab
return Gitlab::SlashCommands::Presenters::Access.new.not_found
end
- new_issue = Issues::MoveService.new(project, current_user)
+ new_issue = Issues::MoveService.new(project: project, current_user: current_user)
.execute(old_issue, target_project)
presenter(new_issue).present(old_issue)
diff --git a/lib/gitlab/slash_commands/issue_new.rb b/lib/gitlab/slash_commands/issue_new.rb
index 48379031537..99a056c97fc 100644
--- a/lib/gitlab/slash_commands/issue_new.rb
+++ b/lib/gitlab/slash_commands/issue_new.rb
@@ -33,7 +33,7 @@ module Gitlab
private
def create_issue(title:, description:)
- Issues::CreateService.new(project, current_user, title: title, description: description).execute
+ Issues::CreateService.new(project: project, current_user: current_user, params: { title: title, description: description }).execute
end
def presenter(issue)
diff --git a/lib/gitlab/slash_commands/presenters/issue_base.rb b/lib/gitlab/slash_commands/presenters/issue_base.rb
index 017fb8a62c4..f6f6e3d7fc6 100644
--- a/lib/gitlab/slash_commands/presenters/issue_base.rb
+++ b/lib/gitlab/slash_commands/presenters/issue_base.rb
@@ -50,4 +50,4 @@ module Gitlab
end
end
-Gitlab::SlashCommands::Presenters::IssueBase.prepend_if_ee('EE::Gitlab::SlashCommands::Presenters::IssueBase')
+Gitlab::SlashCommands::Presenters::IssueBase.prepend_mod_with('Gitlab::SlashCommands::Presenters::IssueBase')
diff --git a/lib/gitlab/snippet_search_results.rb b/lib/gitlab/snippet_search_results.rb
index 41ec19f0da8..581d6b738f3 100644
--- a/lib/gitlab/snippet_search_results.rb
+++ b/lib/gitlab/snippet_search_results.rb
@@ -45,4 +45,4 @@ module Gitlab
end
end
-Gitlab::SnippetSearchResults.prepend_if_ee('::EE::Gitlab::SnippetSearchResults')
+Gitlab::SnippetSearchResults.prepend_mod_with('Gitlab::SnippetSearchResults')
diff --git a/lib/gitlab/spamcheck/client.rb b/lib/gitlab/spamcheck/client.rb
new file mode 100644
index 00000000000..6afc21be4e0
--- /dev/null
+++ b/lib/gitlab/spamcheck/client.rb
@@ -0,0 +1,105 @@
+# frozen_string_literal: true
+require 'spamcheck'
+
+module Gitlab
+ module Spamcheck
+ class Client
+ include ::Spam::SpamConstants
+ DEFAULT_TIMEOUT_SECS = 2
+
+ VERDICT_MAPPING = {
+ ::Spamcheck::SpamVerdict::Verdict::ALLOW => ALLOW,
+ ::Spamcheck::SpamVerdict::Verdict::CONDITIONAL_ALLOW => CONDITIONAL_ALLOW,
+ ::Spamcheck::SpamVerdict::Verdict::DISALLOW => DISALLOW,
+ ::Spamcheck::SpamVerdict::Verdict::BLOCK => BLOCK_USER,
+ ::Spamcheck::SpamVerdict::Verdict::NOOP => NOOP
+ }.freeze
+
+ ACTION_MAPPING = {
+ create: ::Spamcheck::Action::CREATE,
+ update: ::Spamcheck::Action::UPDATE
+ }.freeze
+
+ def initialize
+ @endpoint_url = Gitlab::CurrentSettings.current_application_settings.spam_check_endpoint_url
+
+ # remove the `grpc://` as it's only useful to ensure we're expecting to
+ # connect with Spamcheck
+ @endpoint_url = @endpoint_url.gsub(%r(^grpc:\/\/), '')
+
+ creds =
+ if Rails.env.development? || Rails.env.test?
+ :this_channel_is_insecure
+ else
+ GRPC::Core::ChannelCredentials.new
+ end
+
+ @stub = ::Spamcheck::SpamcheckService::Stub.new(@endpoint_url, creds,
+ timeout: DEFAULT_TIMEOUT_SECS)
+ end
+
+ def issue_spam?(spam_issue:, user:, context: {})
+ issue = build_issue_protobuf(issue: spam_issue, user: user, context: context)
+
+ response = @stub.check_for_spam_issue(issue,
+ metadata: { 'authorization' =>
+ Gitlab::CurrentSettings.spam_check_api_key })
+ verdict = convert_verdict_to_gitlab_constant(response.verdict)
+ [verdict, response.extra_attributes.to_h, response.error]
+ end
+
+ private
+
+ def convert_verdict_to_gitlab_constant(verdict)
+ VERDICT_MAPPING.fetch(::Spamcheck::SpamVerdict::Verdict.resolve(verdict), verdict)
+ end
+
+ def build_issue_protobuf(issue:, user:, context:)
+ issue_pb = ::Spamcheck::Issue.new
+ issue_pb.title = issue.spam_title || ''
+ issue_pb.description = issue.spam_description || ''
+ issue_pb.created_at = convert_to_pb_timestamp(issue.created_at) if issue.created_at
+ issue_pb.updated_at = convert_to_pb_timestamp(issue.updated_at) if issue.updated_at
+ issue_pb.user_in_project = user.authorized_project?(issue.project)
+ issue_pb.project = build_project_protobuf(issue)
+ issue_pb.action = ACTION_MAPPING.fetch(context.fetch(:action)) if context.has_key?(:action)
+ issue_pb.user = build_user_protobuf(user)
+ issue_pb
+ end
+
+ def build_user_protobuf(user)
+ user_pb = ::Spamcheck::User.new
+ user_pb.username = user.username
+ user_pb.org = user.organization || ''
+ user_pb.created_at = convert_to_pb_timestamp(user.created_at)
+
+ user_pb.emails << build_email(user.email, user.confirmed?)
+
+ user.emails.each do |email|
+ user_pb.emails << build_email(email.email, email.confirmed?)
+ end
+
+ user_pb
+ end
+
+ def build_email(email, verified)
+ email_pb = ::Spamcheck::User::Email.new
+ email_pb.email = email
+ email_pb.verified = verified
+ email_pb
+ end
+
+ def build_project_protobuf(issue)
+ project_pb = ::Spamcheck::Project.new
+ project_pb.project_id = issue.project_id
+ project_pb.project_path = issue.project.full_path
+ project_pb
+ end
+
+ def convert_to_pb_timestamp(ar_timestamp)
+ Google::Protobuf::Timestamp.new(seconds: ar_timestamp.to_time.to_i,
+ nanos: ar_timestamp.to_time.nsec)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/stack_prof.rb b/lib/gitlab/stack_prof.rb
new file mode 100644
index 00000000000..4b7d93c91ce
--- /dev/null
+++ b/lib/gitlab/stack_prof.rb
@@ -0,0 +1,136 @@
+# frozen_string_literal: true
+
+# trigger stackprof by sending a SIGUSR2 signal
+#
+# Docs: https://docs.gitlab.com/ee/development/performance.html#production
+
+module Gitlab
+ class StackProf
+ DEFAULT_FILE_PREFIX = Dir.tmpdir
+ DEFAULT_TIMEOUT_SEC = 30
+ DEFAULT_MODE = :cpu
+ # Sample interval as a frequency in microseconds (~99hz); appropriate for CPU profiles
+ DEFAULT_INTERVAL_US = 10_100
+ # Sample interval in event occurrences (n = every nth event); appropriate for allocation profiles
+ DEFAULT_INTERVAL_EVENTS = 100
+
+ # this is a workaround for sidekiq, which defines its own SIGUSR2 handler.
+ # by defering to the sidekiq startup event, we get to set up our own
+ # handler late enough.
+ # see also: https://github.com/mperham/sidekiq/pull/4653
+ def self.install
+ require 'stackprof'
+ require 'tmpdir'
+
+ if Gitlab::Runtime.sidekiq?
+ Sidekiq.configure_server do |config|
+ config.on :startup do
+ on_worker_start
+ end
+ end
+ else
+ Gitlab::Cluster::LifecycleEvents.on_worker_start do
+ on_worker_start
+ end
+ end
+ end
+
+ def self.on_worker_start
+ log_event('listening for SIGUSR2 signal')
+
+ # create a pipe in order to propagate signal out of the signal handler
+ # see also: https://cr.yp.to/docs/selfpipe.html
+ read, write = IO.pipe
+
+ # create a separate thread that polls for signals on the pipe.
+ #
+ # this way we do not execute in signal handler context, which
+ # lifts restrictions and also serializes the calls in a thread-safe
+ # manner.
+ #
+ # it's very similar to a goroutine and channel design.
+ #
+ # another nice benefit of this method is that we can timeout the
+ # IO.select call, allowing the profile to automatically stop after
+ # a given interval (by default 30 seconds), avoiding unbounded memory
+ # growth from a profile that was started and never stopped.
+ t = Thread.new do
+ timeout_s = ENV['STACKPROF_TIMEOUT_S']&.to_i || DEFAULT_TIMEOUT_SEC
+ current_timeout_s = nil
+ loop do
+ read.getbyte if IO.select([read], nil, nil, current_timeout_s)
+
+ if ::StackProf.running?
+ stackprof_file_prefix = ENV['STACKPROF_FILE_PREFIX'] || DEFAULT_FILE_PREFIX
+ stackprof_out_file = "#{stackprof_file_prefix}/stackprof.#{Process.pid}.#{SecureRandom.hex(6)}.profile"
+
+ log_event(
+ 'stopping profile',
+ profile_filename: stackprof_out_file,
+ profile_timeout_s: timeout_s
+ )
+
+ ::StackProf.stop
+ ::StackProf.results(stackprof_out_file)
+ current_timeout_s = nil
+ else
+ mode = ENV['STACKPROF_MODE']&.to_sym || DEFAULT_MODE
+ interval = ENV['STACKPROF_INTERVAL']&.to_i
+ interval ||= (mode == :object ? DEFAULT_INTERVAL_EVENTS : DEFAULT_INTERVAL_US)
+
+ log_event(
+ 'starting profile',
+ profile_mode: mode,
+ profile_interval: interval,
+ profile_timeout: timeout_s
+ )
+
+ ::StackProf.start(
+ mode: mode,
+ raw: Gitlab::Utils.to_boolean(ENV['STACKPROF_RAW'] || 'true'),
+ interval: interval
+ )
+ current_timeout_s = timeout_s
+ end
+ end
+ rescue StandardError => e
+ log_event("stackprof failed: #{e}")
+ end
+ t.abort_on_exception = true
+
+ # in the case of puma, this will override the existing SIGUSR2 signal handler
+ # that can be used to trigger a restart.
+ #
+ # puma cluster has two types of restarts:
+ # * SIGUSR1: phased restart
+ # * SIGUSR2: restart
+ #
+ # phased restart is not supported in our configuration, because we use
+ # preload_app. this means we will always perform a normal restart.
+ # additionally, phased restart is not supported when sending a SIGUSR2
+ # directly to a puma worker (as opposed to the master process).
+ #
+ # the result is that the behaviour of SIGUSR1 and SIGUSR2 is identical in
+ # our configuration, and we can always use a SIGUSR1 to perform a restart.
+ #
+ # thus, it is acceptable for us to re-appropriate the SIGUSR2 signal, and
+ # override the puma behaviour.
+ #
+ # see also:
+ # * https://github.com/puma/puma/blob/master/docs/signals.md#puma-signals
+ # * https://github.com/phusion/unicorn/blob/master/SIGNALS
+ # * https://github.com/mperham/sidekiq/wiki/Signals
+ Signal.trap('SIGUSR2') do
+ write.write('.')
+ end
+ end
+
+ def self.log_event(event, labels = {})
+ Gitlab::AppJsonLogger.info({
+ event: 'stackprof',
+ message: event,
+ pid: Process.pid
+ }.merge(labels.compact))
+ end
+ end
+end
diff --git a/lib/gitlab/static_site_editor/config/generated_config.rb b/lib/gitlab/static_site_editor/config/generated_config.rb
index 0a2cee75af7..1555c3469a5 100644
--- a/lib/gitlab/static_site_editor/config/generated_config.rb
+++ b/lib/gitlab/static_site_editor/config/generated_config.rb
@@ -42,11 +42,11 @@ module Gitlab
end
def supported_content?
- master_branch? && extension_supported? && file_exists?
+ branch_supported? && extension_supported? && file_exists?
end
- def master_branch?
- ref == 'master'
+ def branch_supported?
+ ref.in?(%w[master main])
end
def extension_supported?
diff --git a/lib/gitlab/subscription_portal.rb b/lib/gitlab/subscription_portal.rb
index 3072210d7c8..ab2e1404cd2 100644
--- a/lib/gitlab/subscription_portal.rb
+++ b/lib/gitlab/subscription_portal.rb
@@ -9,8 +9,13 @@ module Gitlab
def self.subscriptions_url
ENV.fetch('CUSTOMER_PORTAL_URL', default_subscriptions_url)
end
+
+ def self.payment_form_url
+ "#{self.subscriptions_url}/payment_forms/cc_validation"
+ end
end
end
-Gitlab::SubscriptionPortal.prepend_if_jh('JH::Gitlab::SubscriptionPortal')
+Gitlab::SubscriptionPortal.prepend_mod
Gitlab::SubscriptionPortal::SUBSCRIPTIONS_URL = Gitlab::SubscriptionPortal.subscriptions_url.freeze
+Gitlab::SubscriptionPortal::PAYMENT_FORM_URL = Gitlab::SubscriptionPortal.payment_form_url.freeze
diff --git a/lib/gitlab/suggestions/suggestion_set.rb b/lib/gitlab/suggestions/suggestion_set.rb
index f9a635734a3..53885cdbf19 100644
--- a/lib/gitlab/suggestions/suggestion_set.rb
+++ b/lib/gitlab/suggestions/suggestion_set.rb
@@ -39,6 +39,10 @@ module Gitlab
@file_paths ||= suggestions.map(&:file_path).uniq
end
+ def authors
+ suggestions.map { |suggestion| suggestion.note.author }.uniq
+ end
+
private
def first_suggestion
diff --git a/lib/gitlab/task_helpers.rb b/lib/gitlab/task_helpers.rb
index db3c058184c..1ceccc64ec0 100644
--- a/lib/gitlab/task_helpers.rb
+++ b/lib/gitlab/task_helpers.rb
@@ -109,7 +109,7 @@ module Gitlab
def run_command!(command)
output, status = Gitlab::Popen.popen(command)
- raise Gitlab::TaskFailedError.new(output) unless status == 0
+ raise Gitlab::TaskFailedError, output unless status == 0
output
end
diff --git a/lib/gitlab/tcp_checker.rb b/lib/gitlab/tcp_checker.rb
index f37a044b607..a07a2e8786b 100644
--- a/lib/gitlab/tcp_checker.rb
+++ b/lib/gitlab/tcp_checker.rb
@@ -30,7 +30,7 @@ module Gitlab
end
true
- rescue => err
+ rescue StandardError => err
@error = err
false
diff --git a/lib/gitlab/template/gitlab_ci_syntax_yml_template.rb b/lib/gitlab/template/gitlab_ci_syntax_yml_template.rb
deleted file mode 100644
index 3bf3a28d3c5..00000000000
--- a/lib/gitlab/template/gitlab_ci_syntax_yml_template.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Template
- class GitlabCiSyntaxYmlTemplate < BaseTemplate
- class << self
- def extension
- '.gitlab-ci.yml'
- end
-
- def categories
- {
- 'General' => ''
- }
- end
-
- def base_dir
- Rails.root.join('lib/gitlab/ci/syntax_templates')
- end
-
- def finder(project = nil)
- Gitlab::Template::Finders::GlobalTemplateFinder.new(
- self.base_dir, self.extension, self.categories
- )
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/template/gitlab_ci_yml_template.rb b/lib/gitlab/template/gitlab_ci_yml_template.rb
index 01158cafc4f..e1ca4b5ff6a 100644
--- a/lib/gitlab/template/gitlab_ci_yml_template.rb
+++ b/lib/gitlab/template/gitlab_ci_yml_template.rb
@@ -59,4 +59,4 @@ module Gitlab
end
end
-Gitlab::Template::GitlabCiYmlTemplate.prepend_if_ee('::EE::Gitlab::Template::GitlabCiYmlTemplate')
+Gitlab::Template::GitlabCiYmlTemplate.prepend_mod_with('Gitlab::Template::GitlabCiYmlTemplate')
diff --git a/lib/gitlab/terraform_registry_token.rb b/lib/gitlab/terraform_registry_token.rb
new file mode 100644
index 00000000000..ae7df49835f
--- /dev/null
+++ b/lib/gitlab/terraform_registry_token.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Gitlab
+ class TerraformRegistryToken < JWTToken
+ class << self
+ def from_token(token)
+ new.tap do |terraform_registry_token|
+ terraform_registry_token['token'] = token.try(:token).presence || token.try(:id).presence
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/throttle.rb b/lib/gitlab/throttle.rb
index 520075012e8..8f045021088 100644
--- a/lib/gitlab/throttle.rb
+++ b/lib/gitlab/throttle.rb
@@ -49,6 +49,20 @@ module Gitlab
{ limit: limit_proc, period: period_proc }
end
+ def self.unauthenticated_packages_api_options
+ limit_proc = proc { |req| settings.throttle_unauthenticated_packages_api_requests_per_period }
+ period_proc = proc { |req| settings.throttle_unauthenticated_packages_api_period_in_seconds.seconds }
+
+ { limit: limit_proc, period: period_proc }
+ end
+
+ def self.authenticated_packages_api_options
+ limit_proc = proc { |req| settings.throttle_authenticated_packages_api_requests_per_period }
+ period_proc = proc { |req| settings.throttle_authenticated_packages_api_period_in_seconds.seconds }
+
+ { limit: limit_proc, period: period_proc }
+ end
+
def self.rate_limiting_response_text
(settings.rate_limiting_response_text.presence || DEFAULT_RATE_LIMITING_RESPONSE_TEXT) + "\n"
end
diff --git a/lib/gitlab/time_tracking_formatter.rb b/lib/gitlab/time_tracking_formatter.rb
index b15cb85dde0..bfdfb01093f 100644
--- a/lib/gitlab/time_tracking_formatter.rb
+++ b/lib/gitlab/time_tracking_formatter.rb
@@ -15,7 +15,7 @@ module Gitlab
ChronicDuration.parse(
string,
CUSTOM_DAY_AND_MONTH_LENGTH.merge(default_unit: 'hours'))
- rescue
+ rescue StandardError
nil
end
@@ -30,7 +30,7 @@ module Gitlab
format: :short,
limit_to_hours: limit_to_hours_setting,
weeks: true))
- rescue
+ rescue StandardError
nil
end
diff --git a/lib/gitlab/tracking.rb b/lib/gitlab/tracking.rb
index b16ae39bcee..5fb360296b7 100644
--- a/lib/gitlab/tracking.rb
+++ b/lib/gitlab/tracking.rb
@@ -14,7 +14,7 @@ module Gitlab
snowplow.event(category, action, label: label, property: property, value: value, context: contexts)
product_analytics.event(category, action, label: label, property: property, value: value, context: contexts)
- rescue => error
+ rescue StandardError => error
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(error, snowplow_category: category, snowplow_action: action)
end
diff --git a/lib/gitlab/tracking/docs/helper.rb b/lib/gitlab/tracking/docs/helper.rb
new file mode 100644
index 00000000000..81874aac9a5
--- /dev/null
+++ b/lib/gitlab/tracking/docs/helper.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Tracking
+ module Docs
+ # Helper with functions to be used by HAML templates
+ module Helper
+ def auto_generated_comment
+ <<-MARKDOWN.strip_heredoc
+ ---
+ stage: Growth
+ group: Product Intelligence
+ 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/#designated-technical-writers
+ ---
+
+ <!---
+ This documentation is auto generated by a script.
+
+ Please do not edit this file directly, check generate_metrics_dictionary task on lib/tasks/gitlab/usage_data.rake.
+ --->
+
+ <!-- vale gitlab.Spelling = NO -->
+ MARKDOWN
+ end
+
+ def render_description(object)
+ return 'Missing description' unless object.description.present?
+
+ object.description
+ end
+
+ def render_event_taxonomy(object)
+ headers = %w[category action label property value]
+ values = %i[category action label property_description value_description]
+ values = values.map { |key| backtick(object.attributes[key]) }
+ values = values.join(" | ")
+
+ [
+ "| #{headers.join(" | ")} |",
+ "#{'|---' * headers.size}|",
+ "| #{values} |"
+ ].join("\n")
+ end
+
+ def md_link_to(anchor_text, url)
+ "[#{anchor_text}](#{url})"
+ end
+
+ def render_owner(object)
+ "Owner: #{backtick(object.product_group)}"
+ end
+
+ def render_tiers(object)
+ "Tiers: #{object.tiers.map(&method(:backtick)).join(', ')}"
+ end
+
+ def render_yaml_definition_path(object)
+ "YAML definition: #{backtick(object.yaml_path)}"
+ end
+
+ def backtick(string)
+ "`#{string}`"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/tracking/docs/renderer.rb b/lib/gitlab/tracking/docs/renderer.rb
new file mode 100644
index 00000000000..184b935c2ba
--- /dev/null
+++ b/lib/gitlab/tracking/docs/renderer.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Tracking
+ module Docs
+ class Renderer
+ include Gitlab::Tracking::Docs::Helper
+ DICTIONARY_PATH = Rails.root.join('doc', 'development', 'snowplow')
+ TEMPLATE_PATH = Rails.root.join('lib', 'gitlab', 'tracking', 'docs', 'templates', 'default.md.haml')
+
+ def initialize(event_definitions)
+ @layout = Haml::Engine.new(File.read(TEMPLATE_PATH))
+ @event_definitions = event_definitions.sort
+ end
+
+ def contents
+ # Render and remove an extra trailing new line
+ @contents ||= @layout.render(self, event_definitions: @event_definitions).sub!(/\n(?=\Z)/, '')
+ end
+
+ def write
+ filename = DICTIONARY_PATH.join('dictionary.md').to_s
+
+ FileUtils.mkdir_p(DICTIONARY_PATH)
+ File.write(filename, contents)
+
+ filename
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/tracking/docs/templates/default.md.haml b/lib/gitlab/tracking/docs/templates/default.md.haml
new file mode 100644
index 00000000000..568f56590fa
--- /dev/null
+++ b/lib/gitlab/tracking/docs/templates/default.md.haml
@@ -0,0 +1,35 @@
+= auto_generated_comment
+
+:plain
+ # Event Dictionary
+
+ This file is autogenerated, please do not edit it directly.
+
+ To generate these files from the GitLab repository, run:
+
+ ```shell
+ bundle exec rake gitlab:snowplow:generate_event_dictionary
+ ```
+
+ The Event Dictionary is based on the following event definition YAML files:
+
+ - [`config/events`](https://gitlab.com/gitlab-org/gitlab/-/tree/f9a404301ca22d038e7b9a9eb08d9c1bbd6c4d84/config/events)
+ - [`ee/config/events`](https://gitlab.com/gitlab-org/gitlab/-/tree/f9a404301ca22d038e7b9a9eb08d9c1bbd6c4d84/ee/config/events)
+
+ ## Event definitions
+
+\
+- event_definitions.each do |_path, object|
+
+ = "### `#{object.category} #{object.action}`"
+ \
+ = render_event_taxonomy(object)
+ \
+ = render_description(object)
+ \
+ = render_yaml_definition_path(object)
+ \
+ = render_owner(object)
+ \
+ = render_tiers(object)
+ \
diff --git a/lib/gitlab/tracking/event_definition.rb b/lib/gitlab/tracking/event_definition.rb
new file mode 100644
index 00000000000..8f70c8ecab7
--- /dev/null
+++ b/lib/gitlab/tracking/event_definition.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Tracking
+ InvalidEventError = Class.new(RuntimeError)
+
+ class EventDefinition
+ EVENT_SCHEMA_PATH = Rails.root.join('config', 'events', 'schema.json')
+ BASE_REPO_PATH = 'https://gitlab.com/gitlab-org/gitlab/-/blob/master'
+ SCHEMA = ::JSONSchemer.schema(Pathname.new(EVENT_SCHEMA_PATH))
+
+ attr_reader :path
+ attr_reader :attributes
+
+ class << self
+ def paths
+ @paths ||= [Rails.root.join('config', 'events', '*.yml'), Rails.root.join('ee', 'config', 'events', '*.yml')]
+ end
+
+ def definitions
+ paths.each_with_object({}) do |glob_path, definitions|
+ load_all_from_path!(definitions, glob_path)
+ end
+ end
+
+ private
+
+ def load_from_file(path)
+ definition = File.read(path)
+ definition = YAML.safe_load(definition)
+ definition.deep_symbolize_keys!
+
+ self.new(path, definition).tap(&:validate!)
+ rescue StandardError => e
+ Gitlab::ErrorTracking.track_and_raise_for_dev_exception(Gitlab::Tracking::InvalidEventError.new(e.message))
+ end
+
+ def load_all_from_path!(definitions, glob_path)
+ Dir.glob(glob_path).each do |path|
+ definition = load_from_file(path)
+ definitions[definition.path] = definition
+ end
+ end
+ end
+
+ def initialize(path, opts = {})
+ @path = path
+ @attributes = opts
+ end
+
+ def to_h
+ attributes
+ end
+ alias_method :to_dictionary, :to_h
+
+ def yaml_path
+ path.delete_prefix(Rails.root.to_s)
+ end
+
+ def validate!
+ SCHEMA.validate(attributes.stringify_keys).each do |error|
+ error_message = <<~ERROR_MSG
+ Error type: #{error['type']}
+ Data: #{error['data']}
+ Path: #{error['data_pointer']}
+ Details: #{error['details']}
+ Definition file: #{path}
+ ERROR_MSG
+
+ Gitlab::ErrorTracking.track_and_raise_for_dev_exception(Gitlab::Tracking::InvalidEventError.new(error_message))
+ end
+ end
+
+ private
+
+ def method_missing(method, *args)
+ attributes[method] || super
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/tracking/standard_context.rb b/lib/gitlab/tracking/standard_context.rb
index da030649f76..7902f96dfa6 100644
--- a/lib/gitlab/tracking/standard_context.rb
+++ b/lib/gitlab/tracking/standard_context.rb
@@ -3,10 +3,12 @@
module Gitlab
module Tracking
class StandardContext
- GITLAB_STANDARD_SCHEMA_URL = 'iglu:com.gitlab/gitlab_standard/jsonschema/1-0-4'
+ GITLAB_STANDARD_SCHEMA_URL = 'iglu:com.gitlab/gitlab_standard/jsonschema/1-0-5'
GITLAB_RAILS_SOURCE = 'gitlab-rails'
def initialize(namespace: nil, project: nil, user: nil, **extra)
+ @namespace = namespace
+ @plan = @namespace&.actual_plan_name
@extra = extra
end
@@ -36,6 +38,7 @@ module Gitlab
{
environment: environment,
source: source,
+ plan: @plan,
extra: @extra
}
end
diff --git a/lib/gitlab/tree_summary.rb b/lib/gitlab/tree_summary.rb
index 86cd91f0a32..85f0ba1fd25 100644
--- a/lib/gitlab/tree_summary.rb
+++ b/lib/gitlab/tree_summary.rb
@@ -161,4 +161,4 @@ module Gitlab
end
end
-Gitlab::TreeSummary.prepend_if_ee('::EE::Gitlab::TreeSummary')
+Gitlab::TreeSummary.prepend_mod_with('Gitlab::TreeSummary')
diff --git a/lib/gitlab/untrusted_regexp.rb b/lib/gitlab/untrusted_regexp.rb
index 706c0925302..09236a7f1f0 100644
--- a/lib/gitlab/untrusted_regexp.rb
+++ b/lib/gitlab/untrusted_regexp.rb
@@ -22,7 +22,7 @@ module Gitlab
@regexp = RE2::Regexp.new(pattern, log_errors: false)
- raise RegexpError.new(regexp.error) unless regexp.ok?
+ raise RegexpError, regexp.error unless regexp.ok?
end
def replace_all(text, rewrite)
diff --git a/lib/gitlab/uploads/migration_helper.rb b/lib/gitlab/uploads/migration_helper.rb
index b610d2a10c6..deab2cd43a6 100644
--- a/lib/gitlab/uploads/migration_helper.rb
+++ b/lib/gitlab/uploads/migration_helper.rb
@@ -76,4 +76,4 @@ module Gitlab
end
end
-Gitlab::Uploads::MigrationHelper.prepend_if_ee('EE::Gitlab::Uploads::MigrationHelper')
+Gitlab::Uploads::MigrationHelper.prepend_mod_with('Gitlab::Uploads::MigrationHelper')
diff --git a/lib/gitlab/url_builder.rb b/lib/gitlab/url_builder.rb
index f98c488bbe5..a242f718b16 100644
--- a/lib/gitlab/url_builder.rb
+++ b/lib/gitlab/url_builder.rb
@@ -49,7 +49,7 @@ module Gitlab
when ::DesignManagement::Design
design_url(object, **options)
else
- raise NotImplementedError.new("No URL builder defined for #{object.inspect}")
+ raise NotImplementedError, "No URL builder defined for #{object.inspect}"
end
end
# rubocop:enable Metrics/CyclomaticComplexity
@@ -127,4 +127,4 @@ module Gitlab
end
end
-::Gitlab::UrlBuilder.prepend_if_ee('EE::Gitlab::UrlBuilder')
+::Gitlab::UrlBuilder.prepend_mod_with('Gitlab::UrlBuilder')
diff --git a/lib/gitlab/usage/docs/helper.rb b/lib/gitlab/usage/docs/helper.rb
index 6b185a5a1e9..c2e5d467dbb 100644
--- a/lib/gitlab/usage/docs/helper.rb
+++ b/lib/gitlab/usage/docs/helper.rb
@@ -18,8 +18,6 @@ module Gitlab
Please do not edit this file directly, check generate_metrics_dictionary task on lib/tasks/gitlab/usage_data.rake.
--->
-
- <!-- vale gitlab.Spelling = NO -->
MARKDOWN
end
diff --git a/lib/gitlab/usage/docs/templates/default.md.haml b/lib/gitlab/usage/docs/templates/default.md.haml
index 26f1aa4396d..8911ac2ed1a 100644
--- a/lib/gitlab/usage/docs/templates/default.md.haml
+++ b/lib/gitlab/usage/docs/templates/default.md.haml
@@ -19,6 +19,10 @@
Each table includes a `milestone`, which corresponds to the GitLab version when the metric
was released.
+ <!-- vale off -->
+ <!-- Docs linting disabled after this line. -->
+ <!-- See https://docs.gitlab.com/ee/development/documentation/testing.html#disable-vale-tests -->
+
## Metrics Definitions
\
diff --git a/lib/gitlab/usage/metric_definition.rb b/lib/gitlab/usage/metric_definition.rb
index 9c4255a7c92..ccd2c69e2e7 100644
--- a/lib/gitlab/usage/metric_definition.rb
+++ b/lib/gitlab/usage/metric_definition.rb
@@ -26,11 +26,11 @@ module Gitlab
def json_schema_path
return '' unless has_json_schema?
- "#{BASE_REPO_PATH}/#{attributes[:object_json_schema]}"
+ "#{BASE_REPO_PATH}/#{attributes[:value_json_schema]}"
end
def has_json_schema?
- attributes[:value_type] == 'object' && attributes[:object_json_schema].present?
+ attributes[:value_type] == 'object' && attributes[:value_json_schema].present?
end
def yaml_path
@@ -65,6 +65,10 @@ module Gitlab
@definitions ||= load_all!
end
+ def all
+ @all ||= definitions.map { |_key_path, definition| definition }
+ end
+
def schemer
@schemer ||= ::JSONSchemer.schema(Pathname.new(METRIC_SCHEMA_PATH))
end
@@ -87,7 +91,7 @@ module Gitlab
definition.deep_symbolize_keys!
self.new(path, definition).tap(&:validate!)
- rescue => e
+ rescue StandardError => e
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(Gitlab::Usage::Metric::InvalidMetricError.new(e.message))
end
@@ -117,4 +121,4 @@ module Gitlab
end
end
-Gitlab::Usage::MetricDefinition.prepend_if_ee('EE::Gitlab::Usage::MetricDefinition')
+Gitlab::Usage::MetricDefinition.prepend_mod_with('Gitlab::Usage::MetricDefinition')
diff --git a/lib/gitlab/usage/metrics/aggregates/aggregate.rb b/lib/gitlab/usage/metrics/aggregates/aggregate.rb
index f77c8cab39c..4c40bfbc06f 100644
--- a/lib/gitlab/usage/metrics/aggregates/aggregate.rb
+++ b/lib/gitlab/usage/metrics/aggregates/aggregate.rb
@@ -83,7 +83,7 @@ module Gitlab
when UNION_OF_AGGREGATED_METRICS
source.calculate_metrics_union(metric_names: aggregation[:events], start_date: start_date, end_date: end_date, recorded_at: recorded_at)
when INTERSECTION_OF_AGGREGATED_METRICS
- calculate_metrics_intersections(source: source, metric_names: aggregation[:events], start_date: start_date, end_date: end_date)
+ source.calculate_metrics_intersections(metric_names: aggregation[:events], start_date: start_date, end_date: end_date, recorded_at: recorded_at)
else
Gitlab::ErrorTracking
.track_and_raise_for_dev_exception(UnknownAggregationOperator.new("Events should be aggregated with one of operators #{ALLOWED_METRICS_AGGREGATIONS}"))
@@ -94,67 +94,6 @@ module Gitlab
Gitlab::Utils::UsageData::FALLBACK
end
- # calculate intersection of 'n' sets based on inclusion exclusion principle https://en.wikipedia.org/wiki/Inclusion%E2%80%93exclusion_principle
- # this method will be extracted to dedicated module with https://gitlab.com/gitlab-org/gitlab/-/issues/273391
- def calculate_metrics_intersections(source:, metric_names:, start_date:, end_date:, subset_powers_cache: Hash.new({}))
- # calculate power of intersection of all given metrics from inclusion exclusion principle
- # |A + B + C| = (|A| + |B| + |C|) - (|A & B| + |A & C| + .. + |C & D|) + (|A & B & C|) =>
- # |A & B & C| = - (|A| + |B| + |C|) + (|A & B| + |A & C| + .. + |C & D|) + |A + B + C|
- # |A + B + C + D| = (|A| + |B| + |C| + |D|) - (|A & B| + |A & C| + .. + |C & D|) + (|A & B & C| + |B & C & D|) - |A & B & C & D| =>
- # |A & B & C & D| = (|A| + |B| + |C| + |D|) - (|A & B| + |A & C| + .. + |C & D|) + (|A & B & C| + |B & C & D|) - |A + B + C + D|
-
- # calculate each components of equation except for the last one |A & B & C & D| = (|A| + |B| + |C| + |D|) - (|A & B| + |A & C| + .. + |C & D|) + (|A & B & C| + |B & C & D|) - ...
- subset_powers_data = subsets_intersection_powers(source, metric_names, start_date, end_date, subset_powers_cache)
-
- # calculate last component of the equation |A & B & C & D| = .... - |A + B + C + D|
- power_of_union_of_all_metrics = begin
- subset_powers_cache[metric_names.size][metric_names.join('_+_')] ||= \
- source.calculate_metrics_union(metric_names: metric_names, start_date: start_date, end_date: end_date, recorded_at: recorded_at)
- end
-
- # in order to determine if part of equation (|A & B & C|, |A & B & C & D|), that represents the intersection that we need to calculate,
- # is positive or negative in particular equation we need to determine if number of subsets is even or odd. Please take a look at two examples below
- # |A + B + C| = (|A| + |B| + |C|) - (|A & B| + |A & C| + .. + |C & D|) + |A & B & C| =>
- # |A & B & C| = - (|A| + |B| + |C|) + (|A & B| + |A & C| + .. + |C & D|) + |A + B + C|
- # |A + B + C + D| = (|A| + |B| + |C| + |D|) - (|A & B| + |A & C| + .. + |C & D|) + (|A & B & C| + |B & C & D|) - |A & B & C & D| =>
- # |A & B & C & D| = (|A| + |B| + |C| + |D|) - (|A & B| + |A & C| + .. + |C & D|) + (|A & B & C| + |B & C & D|) - |A + B + C + D|
- subset_powers_size_even = subset_powers_data.size.even?
-
- # sum all components of equation except for the last one |A & B & C & D| = (|A| + |B| + |C| + |D|) - (|A & B| + |A & C| + .. + |C & D|) + (|A & B & C| + |B & C & D|) - ... =>
- sum_of_all_subset_powers = sum_subset_powers(subset_powers_data, subset_powers_size_even)
-
- # add last component of the equation |A & B & C & D| = sum_of_all_subset_powers - |A + B + C + D|
- sum_of_all_subset_powers + (subset_powers_size_even ? power_of_union_of_all_metrics : -power_of_union_of_all_metrics)
- end
-
- def sum_subset_powers(subset_powers_data, subset_powers_size_even)
- sum_without_sign = subset_powers_data.to_enum.with_index.sum do |value, index|
- (index + 1).odd? ? value : -value
- end
-
- (subset_powers_size_even ? -1 : 1) * sum_without_sign
- end
-
- def subsets_intersection_powers(source, metric_names, start_date, end_date, subset_powers_cache)
- subset_sizes = (1...metric_names.size)
-
- subset_sizes.map do |subset_size|
- if subset_size > 1
- # calculate sum of powers of intersection between each subset (with given size) of metrics: #|A + B + C + D| = ... - (|A & B| + |A & C| + .. + |C & D|)
- metric_names.combination(subset_size).sum do |metrics_subset|
- subset_powers_cache[subset_size][metrics_subset.join('_&_')] ||=
- calculate_metrics_intersections(source: source, metric_names: metrics_subset, start_date: start_date, end_date: end_date, subset_powers_cache: subset_powers_cache)
- end
- else
- # calculate sum of powers of each set (metric) alone #|A + B + C + D| = (|A| + |B| + |C| + |D|) - ...
- metric_names.sum do |metric|
- subset_powers_cache[subset_size][metric] ||= \
- source.calculate_metrics_union(metric_names: metric, start_date: start_date, end_date: end_date, recorded_at: recorded_at)
- end
- end
- end
- end
-
def load_metrics(wildcard)
Dir[wildcard].each_with_object([]) do |path, metrics|
metrics.push(*load_yaml_from_path(path))
@@ -170,4 +109,4 @@ module Gitlab
end
end
-Gitlab::Usage::Metrics::Aggregates::Aggregate.prepend_if_ee('EE::Gitlab::Usage::Metrics::Aggregates::Aggregate')
+Gitlab::Usage::Metrics::Aggregates::Aggregate.prepend_mod_with('Gitlab::Usage::Metrics::Aggregates::Aggregate')
diff --git a/lib/gitlab/usage/metrics/aggregates/sources/calculations/intersection.rb b/lib/gitlab/usage/metrics/aggregates/sources/calculations/intersection.rb
new file mode 100644
index 00000000000..dabf757c8a7
--- /dev/null
+++ b/lib/gitlab/usage/metrics/aggregates/sources/calculations/intersection.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Usage
+ module Metrics
+ module Aggregates
+ module Sources
+ module Calculations
+ module Intersection
+ def calculate_metrics_intersections(metric_names:, start_date:, end_date:, recorded_at:, subset_powers_cache: Hash.new({}))
+ # calculate power of intersection of all given metrics from inclusion exclusion principle
+ # |A + B + C| = (|A| + |B| + |C|) - (|A & B| + |A & C| + .. + |C & D|) + (|A & B & C|) =>
+ # |A & B & C| = - (|A| + |B| + |C|) + (|A & B| + |A & C| + .. + |C & D|) + |A + B + C|
+ # |A + B + C + D| = (|A| + |B| + |C| + |D|) - (|A & B| + |A & C| + .. + |C & D|) + (|A & B & C| + |B & C & D|) - |A & B & C & D| =>
+ # |A & B & C & D| = (|A| + |B| + |C| + |D|) - (|A & B| + |A & C| + .. + |C & D|) + (|A & B & C| + |B & C & D|) - |A + B + C + D|
+
+ # calculate each components of equation except for the last one |A & B & C & D| = (|A| + |B| + |C| + |D|) - (|A & B| + |A & C| + .. + |C & D|) + (|A & B & C| + |B & C & D|) - ...
+ subset_powers_data = subsets_intersection_powers(metric_names, start_date, end_date, recorded_at, subset_powers_cache)
+
+ # calculate last component of the equation |A & B & C & D| = .... - |A + B + C + D|
+ power_of_union_of_all_metrics = begin
+ subset_powers_cache[metric_names.size][metric_names.join('_+_')] ||= \
+ calculate_metrics_union(metric_names: metric_names, start_date: start_date, end_date: end_date, recorded_at: recorded_at)
+ end
+
+ # in order to determine if part of equation (|A & B & C|, |A & B & C & D|), that represents the intersection that we need to calculate,
+ # is positive or negative in particular equation we need to determine if number of subsets is even or odd. Please take a look at two examples below
+ # |A + B + C| = (|A| + |B| + |C|) - (|A & B| + |A & C| + .. + |C & D|) + |A & B & C| =>
+ # |A & B & C| = - (|A| + |B| + |C|) + (|A & B| + |A & C| + .. + |C & D|) + |A + B + C|
+ # |A + B + C + D| = (|A| + |B| + |C| + |D|) - (|A & B| + |A & C| + .. + |C & D|) + (|A & B & C| + |B & C & D|) - |A & B & C & D| =>
+ # |A & B & C & D| = (|A| + |B| + |C| + |D|) - (|A & B| + |A & C| + .. + |C & D|) + (|A & B & C| + |B & C & D|) - |A + B + C + D|
+ subset_powers_size_even = subset_powers_data.size.even?
+
+ # sum all components of equation except for the last one |A & B & C & D| = (|A| + |B| + |C| + |D|) - (|A & B| + |A & C| + .. + |C & D|) + (|A & B & C| + |B & C & D|) - ... =>
+ sum_of_all_subset_powers = sum_subset_powers(subset_powers_data, subset_powers_size_even)
+
+ # add last component of the equation |A & B & C & D| = sum_of_all_subset_powers - |A + B + C + D|
+ sum_of_all_subset_powers + (subset_powers_size_even ? power_of_union_of_all_metrics : -power_of_union_of_all_metrics)
+ end
+
+ private
+
+ def subsets_intersection_powers(metric_names, start_date, end_date, recorded_at, subset_powers_cache)
+ subset_sizes = (1...metric_names.size)
+
+ subset_sizes.map do |subset_size|
+ if subset_size > 1
+ # calculate sum of powers of intersection between each subset (with given size) of metrics: #|A + B + C + D| = ... - (|A & B| + |A & C| + .. + |C & D|)
+ metric_names.combination(subset_size).sum do |metrics_subset|
+ subset_powers_cache[subset_size][metrics_subset.join('_&_')] ||=
+ calculate_metrics_intersections(metric_names: metrics_subset, start_date: start_date, end_date: end_date, recorded_at: recorded_at, subset_powers_cache: subset_powers_cache)
+ end
+ else
+ # calculate sum of powers of each set (metric) alone #|A + B + C + D| = (|A| + |B| + |C| + |D|) - ...
+ metric_names.sum do |metric|
+ subset_powers_cache[subset_size][metric] ||= \
+ calculate_metrics_union(metric_names: metric, start_date: start_date, end_date: end_date, recorded_at: recorded_at)
+ end
+ end
+ end
+ end
+
+ def sum_subset_powers(subset_powers_data, subset_powers_size_even)
+ sum_without_sign = subset_powers_data.to_enum.with_index.sum do |value, index|
+ (index + 1).odd? ? value : -value
+ end
+
+ (subset_powers_size_even ? -1 : 1) * sum_without_sign
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/usage/metrics/aggregates/sources/postgres_hll.rb b/lib/gitlab/usage/metrics/aggregates/sources/postgres_hll.rb
index a01efbdb1a6..3069afab147 100644
--- a/lib/gitlab/usage/metrics/aggregates/sources/postgres_hll.rb
+++ b/lib/gitlab/usage/metrics/aggregates/sources/postgres_hll.rb
@@ -6,6 +6,7 @@ module Gitlab
module Aggregates
module Sources
class PostgresHll
+ extend Calculations::Intersection
class << self
def calculate_metrics_union(metric_names:, start_date:, end_date:, recorded_at:)
time_period = start_date && end_date ? (start_date..end_date) : nil
diff --git a/lib/gitlab/usage/metrics/aggregates/sources/redis_hll.rb b/lib/gitlab/usage/metrics/aggregates/sources/redis_hll.rb
index f3a4dcf1e31..009b8e62543 100644
--- a/lib/gitlab/usage/metrics/aggregates/sources/redis_hll.rb
+++ b/lib/gitlab/usage/metrics/aggregates/sources/redis_hll.rb
@@ -8,6 +8,7 @@ module Gitlab
UnionNotAvailable = Class.new(AggregatedMetricError)
class RedisHll
+ extend Calculations::Intersection
def self.calculate_metrics_union(metric_names:, start_date:, end_date:, recorded_at: nil)
union = Gitlab::UsageDataCounters::HLLRedisCounter
.calculate_events_union(event_names: metric_names, start_date: start_date, end_date: end_date)
diff --git a/lib/gitlab/usage/metrics/instrumentations/base_metric.rb b/lib/gitlab/usage/metrics/instrumentations/base_metric.rb
new file mode 100644
index 00000000000..29b44f2bd0a
--- /dev/null
+++ b/lib/gitlab/usage/metrics/instrumentations/base_metric.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Usage
+ module Metrics
+ module Instrumentations
+ class BaseMetric
+ include Gitlab::Utils::UsageData
+
+ attr_reader :time_frame
+
+ def initialize(time_frame:)
+ @time_frame = time_frame
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/usage/metrics/instrumentations/count_boards_metric.rb b/lib/gitlab/usage/metrics/instrumentations/count_boards_metric.rb
new file mode 100644
index 00000000000..4e1ba027bca
--- /dev/null
+++ b/lib/gitlab/usage/metrics/instrumentations/count_boards_metric.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Usage
+ module Metrics
+ module Instrumentations
+ class CountBoardsMetric < DatabaseMetric
+ operation :count
+
+ relation { Board }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/usage/metrics/instrumentations/count_issues_metric.rb b/lib/gitlab/usage/metrics/instrumentations/count_issues_metric.rb
new file mode 100644
index 00000000000..34247f4f6dd
--- /dev/null
+++ b/lib/gitlab/usage/metrics/instrumentations/count_issues_metric.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Usage
+ module Metrics
+ module Instrumentations
+ class CountIssuesMetric < DatabaseMetric
+ operation :count
+
+ start { Issue.minimum(:id) }
+ finish { Issue.maximum(:id) }
+
+ relation { Issue }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/usage/metrics/instrumentations/count_users_creating_issues_metric.rb b/lib/gitlab/usage/metrics/instrumentations/count_users_creating_issues_metric.rb
new file mode 100644
index 00000000000..c8331ce5b31
--- /dev/null
+++ b/lib/gitlab/usage/metrics/instrumentations/count_users_creating_issues_metric.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Usage
+ module Metrics
+ module Instrumentations
+ class CountUsersCreatingIssuesMetric < DatabaseMetric
+ operation :distinct_count, column: :author_id
+
+ relation { Issue }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/usage/metrics/instrumentations/count_users_using_approve_quick_action_metric.rb b/lib/gitlab/usage/metrics/instrumentations/count_users_using_approve_quick_action_metric.rb
new file mode 100644
index 00000000000..9c92f2e9595
--- /dev/null
+++ b/lib/gitlab/usage/metrics/instrumentations/count_users_using_approve_quick_action_metric.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Usage
+ module Metrics
+ module Instrumentations
+ class CountUsersUsingApproveQuickActionMetric < RedisHLLMetric
+ event_names :i_quickactions_approve
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/usage/metrics/instrumentations/database_metric.rb b/lib/gitlab/usage/metrics/instrumentations/database_metric.rb
new file mode 100644
index 00000000000..f83f90dea03
--- /dev/null
+++ b/lib/gitlab/usage/metrics/instrumentations/database_metric.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Usage
+ module Metrics
+ module Instrumentations
+ class DatabaseMetric < BaseMetric
+ # Usage Example
+ #
+ # class CountUsersCreatingIssuesMetric < DatabaseMetric
+ # operation :distinct_count, column: :author_id
+ #
+ # relation do |database_time_constraints|
+ # ::Issue.where(database_time_constraints)
+ # end
+ # end
+ class << self
+ def start(&block)
+ @metric_start = block
+ end
+
+ def finish(&block)
+ @metric_finish = block
+ end
+
+ def relation(&block)
+ @metric_relation = block
+ end
+
+ def operation(symbol, column: nil)
+ @metric_operation = symbol
+ @column = column
+ end
+
+ attr_reader :metric_operation, :metric_relation, :metric_start, :metric_finish, :column
+ end
+
+ def value
+ method(self.class.metric_operation)
+ .call(relation,
+ self.class.column,
+ start: self.class.metric_start&.call,
+ finish: self.class.metric_finish&.call)
+ end
+
+ def relation
+ self.class.metric_relation.call.where(time_constraints)
+ end
+
+ private
+
+ def time_constraints
+ case time_frame
+ when '28d'
+ { created_at: 30.days.ago..2.days.ago }
+ when 'all'
+ {}
+ when 'none'
+ nil
+ else
+ raise "Unknown time frame: #{time_frame} for DatabaseMetric"
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/usage/metrics/instrumentations/generic_metric.rb b/lib/gitlab/usage/metrics/instrumentations/generic_metric.rb
new file mode 100644
index 00000000000..7c97cc37d17
--- /dev/null
+++ b/lib/gitlab/usage/metrics/instrumentations/generic_metric.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Usage
+ module Metrics
+ module Instrumentations
+ class GenericMetric < BaseMetric
+ # Usage example
+ #
+ # class UuidMetric < GenericMetric
+ # value do
+ # Gitlab::CurrentSettings.uuid
+ # end
+ # end
+ class << self
+ def value(&block)
+ @metric_value = block
+ end
+
+ attr_reader :metric_value
+ end
+
+ def value
+ alt_usage_data do
+ self.class.metric_value.call
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/usage/metrics/instrumentations/hostname_metric.rb b/lib/gitlab/usage/metrics/instrumentations/hostname_metric.rb
new file mode 100644
index 00000000000..3364c330cca
--- /dev/null
+++ b/lib/gitlab/usage/metrics/instrumentations/hostname_metric.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Usage
+ module Metrics
+ module Instrumentations
+ class HostnameMetric < GenericMetric
+ value do
+ Gitlab.config.gitlab.host
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/usage/metrics/instrumentations/redis_hll_metric.rb b/lib/gitlab/usage/metrics/instrumentations/redis_hll_metric.rb
new file mode 100644
index 00000000000..140d56f0d42
--- /dev/null
+++ b/lib/gitlab/usage/metrics/instrumentations/redis_hll_metric.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Usage
+ module Metrics
+ module Instrumentations
+ class RedisHLLMetric < BaseMetric
+ # Usage example
+ #
+ # class CountUsersVisitingAnalyticsValuestreamMetric < RedisHLLMetric
+ # event_names :g_analytics_valuestream
+ # end
+ class << self
+ def event_names(events = nil)
+ @metric_events = events
+ end
+
+ attr_reader :metric_events
+ end
+
+ def value
+ redis_usage_data do
+ event_params = time_constraints.merge(event_names: self.class.metric_events)
+
+ Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(**event_params)
+ end
+ end
+
+ private
+
+ def time_constraints
+ case time_frame
+ when '28d'
+ { start_date: 4.weeks.ago.to_date, end_date: Date.current }
+ when '7d'
+ { start_date: 7.days.ago.to_date, end_date: Date.current }
+ else
+ raise "Unknown time frame: #{time_frame} for TimeConstraint"
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/usage/metrics/instrumentations/uuid_metric.rb b/lib/gitlab/usage/metrics/instrumentations/uuid_metric.rb
new file mode 100644
index 00000000000..58547b5383a
--- /dev/null
+++ b/lib/gitlab/usage/metrics/instrumentations/uuid_metric.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Usage
+ module Metrics
+ module Instrumentations
+ class UuidMetric < GenericMetric
+ value do
+ Gitlab::CurrentSettings.uuid
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/usage/metrics/key_path_processor.rb b/lib/gitlab/usage/metrics/key_path_processor.rb
new file mode 100644
index 00000000000..dbe574d5838
--- /dev/null
+++ b/lib/gitlab/usage/metrics/key_path_processor.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Usage
+ module Metrics
+ class KeyPathProcessor
+ class << self
+ def process(key_path, value)
+ unflatten(key_path.split('.'), value)
+ end
+
+ private
+
+ def unflatten(keys, value)
+ loop do
+ value = { keys.pop.to_sym => value }
+
+ break if keys.blank?
+ end
+
+ value
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index b36ca38cd64..b1ba529d4a4 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -98,7 +98,6 @@ module Gitlab
ci_external_pipelines: count(::Ci::Pipeline.external),
ci_pipeline_config_auto_devops: count(::Ci::Pipeline.auto_devops_source),
ci_pipeline_config_repository: count(::Ci::Pipeline.repository_source),
- ci_runners: count(::Ci::Runner),
ci_triggers: count(::Ci::Trigger),
ci_pipeline_schedules: count(::Ci::PipelineSchedule),
auto_devops_enabled: count(::ProjectAutoDevops.enabled),
@@ -164,7 +163,6 @@ module Gitlab
projects_with_repositories_enabled: count(ProjectFeature.where('repository_access_level > ?', ProjectFeature::DISABLED)),
projects_with_tracing_enabled: count(ProjectTracingSetting),
projects_with_error_tracking_enabled: count(::ErrorTracking::ProjectErrorTrackingSetting.where(enabled: true)),
- projects_with_alerts_service_enabled: count(Service.active.where(type: 'AlertsService')),
projects_with_alerts_created: distinct_count(::AlertManagement::Alert, :project_id),
projects_with_enabled_alert_integrations: distinct_count(::AlertManagement::HttpIntegration.active, :project_id),
projects_with_prometheus_alerts: distinct_count(PrometheusAlert, :project_id),
@@ -186,12 +184,14 @@ module Gitlab
merge_requests: count(MergeRequest),
notes: count(Note)
}.merge(
+ runners_usage,
services_usage,
usage_counters,
user_preferences_usage,
ingress_modsecurity_usage,
container_expiration_policies_usage,
- service_desk_counts
+ service_desk_counts,
+ email_campaign_counts
).tap do |data|
data[:snippets] = add(data[:personal_snippets], data[:project_snippets])
end
@@ -199,6 +199,18 @@ module Gitlab
end
# rubocop: enable Metrics/AbcSize
+ def runners_usage
+ {
+ ci_runners: count(::Ci::Runner),
+ ci_runners_instance_type_active: count(::Ci::Runner.instance_type.active),
+ ci_runners_group_type_active: count(::Ci::Runner.group_type.active),
+ ci_runners_project_type_active: count(::Ci::Runner.project_type.active),
+ ci_runners_instance_type_active_online: count(::Ci::Runner.instance_type.active.online),
+ ci_runners_group_type_active_online: count(::Ci::Runner.group_type.active.online),
+ ci_runners_project_type_active_online: count(::Ci::Runner.project_type.active.online)
+ }
+ end
+
def snowplow_event_counts(time_period)
return {} unless report_snowplow_events?
@@ -243,7 +255,8 @@ module Gitlab
{
settings: {
ldap_encrypted_secrets_enabled: alt_usage_data(fallback: nil) { Gitlab::Auth::Ldap::Config.encrypted_secrets.active? },
- operating_system: alt_usage_data(fallback: nil) { operating_system }
+ operating_system: alt_usage_data(fallback: nil) { operating_system },
+ gitaly_apdex: alt_usage_data { gitaly_apdex }
}
}
end
@@ -414,13 +427,15 @@ module Gitlab
def services_usage
# rubocop: disable UsageData/LargeTable:
- Service.available_services_names.each_with_object({}) do |service_name, response|
- response["projects_#{service_name}_active".to_sym] = count(Service.active.where.not(project: nil).where(type: "#{service_name}_service".camelize))
- response["groups_#{service_name}_active".to_sym] = count(Service.active.where.not(group: nil).where(type: "#{service_name}_service".camelize))
- response["templates_#{service_name}_active".to_sym] = count(Service.active.where(template: true, type: "#{service_name}_service".camelize))
- response["instances_#{service_name}_active".to_sym] = count(Service.active.where(instance: true, type: "#{service_name}_service".camelize))
- response["projects_inheriting_#{service_name}_active".to_sym] = count(Service.active.where.not(project: nil).where.not(inherit_from_id: nil).where(type: "#{service_name}_service".camelize))
- response["groups_inheriting_#{service_name}_active".to_sym] = count(Service.active.where.not(group: nil).where.not(inherit_from_id: nil).where(type: "#{service_name}_service".camelize))
+ Integration.available_services_names(include_dev: false).each_with_object({}) do |service_name, response|
+ service_type = Integration.service_name_to_type(service_name)
+
+ response["projects_#{service_name}_active".to_sym] = count(Integration.active.where.not(project: nil).where(type: service_type))
+ response["groups_#{service_name}_active".to_sym] = count(Integration.active.where.not(group: nil).where(type: service_type))
+ response["templates_#{service_name}_active".to_sym] = count(Integration.active.where(template: true, type: service_type))
+ response["instances_#{service_name}_active".to_sym] = count(Integration.active.where(instance: true, type: service_type))
+ response["projects_inheriting_#{service_name}_active".to_sym] = count(Integration.active.where.not(project: nil).where.not(inherit_from_id: nil).where(type: service_type))
+ response["groups_inheriting_#{service_name}_active".to_sym] = count(Integration.active.where.not(group: nil).where.not(inherit_from_id: nil).where(type: service_type))
end.merge(jira_usage, jira_import_usage)
# rubocop: enable UsageData/LargeTable:
end
@@ -435,18 +450,10 @@ module Gitlab
projects_jira_dvcs_server_active: count(ProjectFeatureUsage.with_jira_dvcs_integration_enabled(cloud: false))
}
- # rubocop: disable UsageData/LargeTable:
- JiraService.active.includes(:jira_tracker_data).find_in_batches(batch_size: 100) do |services|
- counts = services.group_by do |service|
- # TODO: Simplify as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
- service_url = service.data_fields&.url || (service.properties && service.properties['url'])
- service_url&.include?('.atlassian.net') ? :cloud : :server
- end
+ jira_service_data_hash = jira_service_data
+ results[:projects_jira_server_active] = jira_service_data_hash[:projects_jira_server_active]
+ results[:projects_jira_cloud_active] = jira_service_data_hash[:projects_jira_cloud_active]
- results[:projects_jira_server_active] += counts[:server].size if counts[:server]
- results[:projects_jira_cloud_active] += counts[:cloud].size if counts[:cloud]
- end
- # rubocop: enable UsageData/LargeTable:
results
rescue ActiveRecord::StatementInvalid
{ projects_jira_server_active: FALLBACK, projects_jira_cloud_active: FALLBACK }
@@ -570,7 +577,11 @@ module Gitlab
projects_with_disable_overriding_approvers_per_merge_request: count(::Project.where(time_period.merge(disable_overriding_approvers_per_merge_request: true))),
projects_without_disable_overriding_approvers_per_merge_request: count(::Project.where(time_period.merge(disable_overriding_approvers_per_merge_request: [false, nil]))),
remote_mirrors: distinct_count(::Project.with_remote_mirrors.where(time_period), :creator_id),
- snippets: distinct_count(::Snippet.where(time_period), :author_id)
+ snippets: distinct_count(::Snippet.where(time_period), :author_id),
+ suggestions: distinct_count(::Note.with_suggestions.where(time_period),
+ :author_id,
+ start: minimum_id(::User),
+ finish: maximum_id(::User))
}.tap do |h|
if time_period.present?
h[:merge_requests_users] = merge_requests_users(time_period)
@@ -597,7 +608,7 @@ module Gitlab
unique_users_all_imports: unique_users_all_imports(time_period),
bulk_imports: {
gitlab: DEPRECATED_VALUE,
- gitlab_v1: count(::BulkImport.where(time_period, source_type: :gitlab))
+ gitlab_v1: count(::BulkImport.where(**time_period, source_type: :gitlab))
},
project_imports: project_imports(time_period),
issue_imports: issue_imports(time_period),
@@ -767,6 +778,16 @@ module Gitlab
private
+ def gitaly_apdex
+ with_prometheus_client(verify: false, fallback: FALLBACK) do |client|
+ result = client.query('avg_over_time(gitlab_usage_ping:gitaly_apdex:ratio_avg_over_time_5m[1w])').first
+
+ break FALLBACK unless result
+
+ result['value'].last.to_f
+ end
+ end
+
def aggregated_metrics
@aggregated_metrics ||= ::Gitlab::Usage::Metrics::Aggregates::Aggregate.new(recorded_at)
end
@@ -825,6 +846,28 @@ module Gitlab
end
# rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
+ def email_campaign_counts
+ # rubocop:disable UsageData/LargeTable
+ sent_emails = count(Users::InProductMarketingEmail.group(:track, :series))
+ clicked_emails = count(Users::InProductMarketingEmail.where.not(cta_clicked_at: nil).group(:track, :series))
+
+ series_amount = Namespaces::InProductMarketingEmailsService::INTERVAL_DAYS.count
+
+ Users::InProductMarketingEmail.tracks.keys.each_with_object({}) do |track, result|
+ # rubocop: enable UsageData/LargeTable:
+ 0.upto(series_amount - 1).map do |series|
+ # When there is an error with the query and it's not the Hash we expect, we return what we got from `count`.
+ sent_count = sent_emails.is_a?(Hash) ? sent_emails.fetch([track, series], 0) : sent_emails
+ clicked_count = clicked_emails.is_a?(Hash) ? clicked_emails.fetch([track, series], 0) : clicked_emails
+
+ result["in_product_marketing_email_#{track}_#{series}_sent"] = sent_count
+ result["in_product_marketing_email_#{track}_#{series}_cta_clicked"] = clicked_count
+ end
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
def unique_visit_service
strong_memoize(:unique_visit_service) do
::Gitlab::Analytics::UniqueVisits.new
@@ -955,4 +998,4 @@ module Gitlab
end
end
-Gitlab::UsageData.prepend_if_ee('EE::Gitlab::UsageData')
+Gitlab::UsageData.prepend_mod_with('Gitlab::UsageData')
diff --git a/lib/gitlab/usage_data/topology.rb b/lib/gitlab/usage_data/topology.rb
index 7f7854c3eb1..b823d6cc2bf 100644
--- a/lib/gitlab/usage_data/topology.rb
+++ b/lib/gitlab/usage_data/topology.rb
@@ -47,7 +47,7 @@ module Gitlab
nodes: topology_node_data(client)
}.compact
end
- rescue => e
+ rescue StandardError => e
@failures << CollectionFailure.new('other', e.class.to_s)
{}
@@ -183,7 +183,7 @@ module Gitlab
@failures << CollectionFailure.new(query_name, 'empty_result')
fallback
- rescue => e
+ rescue StandardError => e
@failures << CollectionFailure.new(query_name, e.class.to_s)
fallback
end
diff --git a/lib/gitlab/usage_data_counters/counter_events/package_events.yml b/lib/gitlab/usage_data_counters/counter_events/package_events.yml
index e1648245f3f..dd66a40a48f 100644
--- a/lib/gitlab/usage_data_counters/counter_events/package_events.yml
+++ b/lib/gitlab/usage_data_counters/counter_events/package_events.yml
@@ -47,3 +47,6 @@
- i_package_tag_delete_package
- i_package_tag_pull_package
- i_package_tag_push_package
+- i_package_terraform_module_delete_package
+- i_package_terraform_module_pull_package
+- i_package_terraform_module_push_package
diff --git a/lib/gitlab/usage_data_counters/editor_unique_counter.rb b/lib/gitlab/usage_data_counters/editor_unique_counter.rb
index bef3fc7b504..bc0126cd893 100644
--- a/lib/gitlab/usage_data_counters/editor_unique_counter.rb
+++ b/lib/gitlab/usage_data_counters/editor_unique_counter.rb
@@ -50,7 +50,6 @@ module Gitlab
private
def track_unique_action(action, author, time)
- return unless Feature.enabled?(:track_editor_edit_actions, default_enabled: true)
return unless author
Gitlab::UsageDataCounters::HLLRedisCounter.track_event(action, values: author.id, time: time)
diff --git a/lib/gitlab/usage_data_counters/hll_redis_counter.rb b/lib/gitlab/usage_data_counters/hll_redis_counter.rb
index a8691169fb8..833eebd5d04 100644
--- a/lib/gitlab/usage_data_counters/hll_redis_counter.rb
+++ b/lib/gitlab/usage_data_counters/hll_redis_counter.rb
@@ -132,7 +132,7 @@ module Gitlab
return unless feature_enabled?(event)
Gitlab::Redis::HLL.add(key: redis_key(event, time, context), value: values, expiry: expiry(event))
- rescue => e
+ rescue StandardError => e
# Ignore any exceptions unless is dev or test env
# The application flow should not be blocked by erros in tracking
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)
@@ -232,8 +232,8 @@ module Gitlab
# Compose the key in order to store events daily or weekly
def redis_key(event, time, context = '')
- raise UnknownEvent.new("Unknown event #{event[:name]}") unless known_events_names.include?(event[:name].to_s)
- raise UnknownAggregation.new("Use :daily or :weekly aggregation") unless ALLOWED_AGGREGATIONS.include?(event[:aggregation].to_sym)
+ raise UnknownEvent, "Unknown event #{event[:name]}" unless known_events_names.include?(event[:name].to_s)
+ raise UnknownAggregation, "Use :daily or :weekly aggregation" unless ALLOWED_AGGREGATIONS.include?(event[:aggregation].to_sym)
key = apply_slot(event)
key = apply_time_aggregation(key, time, event)
@@ -277,4 +277,4 @@ module Gitlab
end
end
-Gitlab::UsageDataCounters::HLLRedisCounter.prepend_if_ee('EE::Gitlab::UsageDataCounters::HLLRedisCounter')
+Gitlab::UsageDataCounters::HLLRedisCounter.prepend_mod_with('Gitlab::UsageDataCounters::HLLRedisCounter')
diff --git a/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb b/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb
index 6f5f878501f..083de402175 100644
--- a/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb
+++ b/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb
@@ -154,4 +154,4 @@ module Gitlab
end
end
-Gitlab::UsageDataCounters::IssueActivityUniqueCounter.prepend_if_ee('EE::Gitlab::UsageDataCounters::IssueActivityUniqueCounter')
+Gitlab::UsageDataCounters::IssueActivityUniqueCounter.prepend_mod_with('Gitlab::UsageDataCounters::IssueActivityUniqueCounter')
diff --git a/lib/gitlab/usage_data_counters/known_events/analytics.yml b/lib/gitlab/usage_data_counters/known_events/analytics.yml
new file mode 100644
index 00000000000..e4f20b61901
--- /dev/null
+++ b/lib/gitlab/usage_data_counters/known_events/analytics.yml
@@ -0,0 +1,85 @@
+- name: users_viewing_analytics_group_devops_adoption
+ category: analytics
+ redis_slot: analytics
+ aggregation: weekly
+ feature_flag: track_unique_visits
+- name: i_analytics_dev_ops_adoption
+ category: analytics
+ redis_slot: analytics
+ aggregation: weekly
+ feature_flag: track_unique_visits
+- name: i_analytics_dev_ops_score
+ category: analytics
+ redis_slot: analytics
+ aggregation: weekly
+ feature_flag: track_unique_visits
+- name: p_analytics_merge_request
+ category: analytics
+ redis_slot: analytics
+ aggregation: weekly
+ feature_flag: track_unique_visits
+- name: i_analytics_instance_statistics
+ category: analytics
+ redis_slot: analytics
+ aggregation: weekly
+ feature_flag: track_unique_visits
+- name: g_analytics_contribution
+ category: analytics
+ redis_slot: analytics
+ aggregation: weekly
+ feature_flag: track_unique_visits
+- name: g_analytics_insights
+ category: analytics
+ redis_slot: analytics
+ aggregation: weekly
+ feature_flag: track_unique_visits
+- name: g_analytics_issues
+ category: analytics
+ redis_slot: analytics
+ aggregation: weekly
+ feature_flag: track_unique_visits
+- name: g_analytics_productivity
+ category: analytics
+ redis_slot: analytics
+ aggregation: weekly
+ feature_flag: track_unique_visits
+- name: g_analytics_valuestream
+ category: analytics
+ redis_slot: analytics
+ aggregation: weekly
+ feature_flag: track_unique_visits
+- name: p_analytics_pipelines
+ category: analytics
+ redis_slot: analytics
+ aggregation: weekly
+ feature_flag: track_unique_visits
+- name: p_analytics_code_reviews
+ category: analytics
+ redis_slot: analytics
+ aggregation: weekly
+ feature_flag: track_unique_visits
+- name: p_analytics_valuestream
+ category: analytics
+ redis_slot: analytics
+ aggregation: weekly
+ feature_flag: track_unique_visits
+- name: p_analytics_insights
+ category: analytics
+ redis_slot: analytics
+ aggregation: weekly
+ feature_flag: track_unique_visits
+- name: p_analytics_issues
+ category: analytics
+ redis_slot: analytics
+ aggregation: weekly
+ feature_flag: track_unique_visits
+- name: p_analytics_repo
+ category: analytics
+ redis_slot: analytics
+ aggregation: weekly
+ feature_flag: track_unique_visits
+- name: i_analytics_cohorts
+ category: analytics
+ redis_slot: analytics
+ aggregation: weekly
+ feature_flag: track_unique_visits
diff --git a/lib/gitlab/usage_data_counters/known_events/code_review_events.yml b/lib/gitlab/usage_data_counters/known_events/code_review_events.yml
index 18c5dc73de2..cc89fbd5caf 100644
--- a/lib/gitlab/usage_data_counters/known_events/code_review_events.yml
+++ b/lib/gitlab/usage_data_counters/known_events/code_review_events.yml
@@ -3,204 +3,219 @@
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_mr_diffs
- name: i_code_review_user_single_file_diffs
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_single_file_diffs
- name: i_code_review_mr_single_file_diffs
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_mr_single_file_diffs
- name: i_code_review_user_toggled_task_item_status
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_toggled_task_item_status
- name: i_code_review_user_create_mr
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_create_mr
- name: i_code_review_user_close_mr
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_close_mr
- name: i_code_review_user_reopen_mr
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_reopen_mr
- name: i_code_review_user_approve_mr
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_approve_mr
- name: i_code_review_user_unapprove_mr
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_unapprove_mr
- name: i_code_review_user_resolve_thread
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_resolve_thread
- name: i_code_review_user_unresolve_thread
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_unresolve_thread
- name: i_code_review_edit_mr_title
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_edit_mr_title
- name: i_code_review_edit_mr_desc
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_edit_mr_desc
- name: i_code_review_user_merge_mr
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_merge_mr
- name: i_code_review_user_create_mr_comment
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_create_mr_comment
- name: i_code_review_user_edit_mr_comment
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_edit_mr_comment
- name: i_code_review_user_remove_mr_comment
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_remove_mr_comment
- name: i_code_review_user_create_review_note
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_create_review_note
- name: i_code_review_user_publish_review
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_publish_review
- name: i_code_review_user_create_multiline_mr_comment
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_create_multiline_mr_comment
- name: i_code_review_user_edit_multiline_mr_comment
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_edit_multiline_mr_comment
- name: i_code_review_user_remove_multiline_mr_comment
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_remove_multiline_mr_comment
- name: i_code_review_user_add_suggestion
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_add_suggestion
- name: i_code_review_user_apply_suggestion
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_apply_suggestion
- name: i_code_review_user_assigned
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_assigned
- name: i_code_review_user_marked_as_draft
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_marked_as_draft
- name: i_code_review_user_unmarked_as_draft
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_unmarked_as_draft
- name: i_code_review_user_review_requested
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_review_requested
- name: i_code_review_user_approval_rule_added
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_approval_rule_added
- name: i_code_review_user_approval_rule_deleted
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_approval_rule_deleted
- name: i_code_review_user_approval_rule_edited
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_approval_rule_edited
- name: i_code_review_user_vs_code_api_request
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_vs_code_api_request
- name: i_code_review_user_create_mr_from_issue
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_create_mr_from_issue
- name: i_code_review_user_mr_discussion_locked
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_mr_discussion_locked
- name: i_code_review_user_mr_discussion_unlocked
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_mr_discussion_unlocked
- name: i_code_review_user_time_estimate_changed
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_time_estimate_changed
- name: i_code_review_user_time_spent_changed
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_time_spent_changed
- name: i_code_review_user_assignees_changed
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_assignees_changed
- name: i_code_review_user_reviewers_changed
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_reviewers_changed
- name: i_code_review_user_milestone_changed
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_milestone_changed
- name: i_code_review_user_labels_changed
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_labels_changed
+# Diff settings events
+- name: i_code_review_click_single_file_mode_setting
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: diff_settings_usage_data
+- name: i_code_review_click_file_browser_setting
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: diff_settings_usage_data
+- name: i_code_review_click_whitespace_setting
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: diff_settings_usage_data
+- name: i_code_review_diff_view_inline
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: diff_settings_usage_data
+- name: i_code_review_diff_view_parallel
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: diff_settings_usage_data
+- name: i_code_review_file_browser_tree_view
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: diff_settings_usage_data
+- name: i_code_review_file_browser_list_view
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: diff_settings_usage_data
+- name: i_code_review_diff_show_whitespace
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: diff_settings_usage_data
+- name: i_code_review_diff_hide_whitespace
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: diff_settings_usage_data
+- name: i_code_review_diff_single_file
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: diff_settings_usage_data
+- name: i_code_review_diff_multiple_files
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+ feature_flag: diff_settings_usage_data
diff --git a/lib/gitlab/usage_data_counters/known_events/common.yml b/lib/gitlab/usage_data_counters/known_events/common.yml
index 077864032e8..f2504396cc4 100644
--- a/lib/gitlab/usage_data_counters/known_events/common.yml
+++ b/lib/gitlab/usage_data_counters/known_events/common.yml
@@ -24,92 +24,6 @@
category: compliance
redis_slot: compliance
aggregation: weekly
- feature_flag: usage_data_a_compliance_audit_events_api
-# Analytics category
-- name: g_analytics_contribution
- category: analytics
- redis_slot: analytics
- aggregation: weekly
- feature_flag: track_unique_visits
-- name: g_analytics_insights
- category: analytics
- redis_slot: analytics
- aggregation: weekly
- feature_flag: track_unique_visits
-- name: g_analytics_issues
- category: analytics
- redis_slot: analytics
- aggregation: weekly
- feature_flag: track_unique_visits
-- name: g_analytics_productivity
- category: analytics
- redis_slot: analytics
- aggregation: weekly
- feature_flag: track_unique_visits
-- name: g_analytics_valuestream
- category: analytics
- redis_slot: analytics
- aggregation: weekly
- feature_flag: track_unique_visits
-- name: p_analytics_pipelines
- category: analytics
- redis_slot: analytics
- aggregation: weekly
- feature_flag: track_unique_visits
-- name: p_analytics_code_reviews
- category: analytics
- redis_slot: analytics
- aggregation: weekly
- feature_flag: track_unique_visits
-- name: p_analytics_valuestream
- category: analytics
- redis_slot: analytics
- aggregation: weekly
- feature_flag: track_unique_visits
-- name: p_analytics_insights
- category: analytics
- redis_slot: analytics
- aggregation: weekly
- feature_flag: track_unique_visits
-- name: p_analytics_issues
- category: analytics
- redis_slot: analytics
- aggregation: weekly
- feature_flag: track_unique_visits
-- name: p_analytics_repo
- category: analytics
- redis_slot: analytics
- aggregation: weekly
- feature_flag: track_unique_visits
-- name: i_analytics_cohorts
- category: analytics
- redis_slot: analytics
- aggregation: weekly
- feature_flag: track_unique_visits
-- name: i_analytics_dev_ops_score
- category: analytics
- redis_slot: analytics
- aggregation: weekly
- feature_flag: track_unique_visits
-- name: i_analytics_dev_ops_adoption
- category: analytics
- redis_slot: analytics
- aggregation: weekly
- feature_flag: track_unique_visits
-- name: g_analytics_merge_request
- category: analytics
- redis_slot: analytics
- aggregation: weekly
- feature_flag: track_unique_visits
-- name: p_analytics_merge_request
- category: analytics
- redis_slot: analytics
- aggregation: weekly
- feature_flag: track_unique_visits
-- name: i_analytics_instance_statistics
- category: analytics
- redis_slot: analytics
- aggregation: weekly
feature_flag: track_unique_visits
- name: g_edit_by_web_ide
category: ide_edit
@@ -139,17 +53,14 @@
category: search
redis_slot: search
aggregation: weekly
- feature_flag: search_track_unique_users
- name: i_search_advanced
category: search
redis_slot: search
aggregation: weekly
- feature_flag: search_track_unique_users
- name: i_search_paid
category: search
redis_slot: search
aggregation: weekly
- feature_flag: search_track_unique_users
- name: wiki_action
category: source_code
aggregation: daily
@@ -175,52 +86,42 @@
redis_slot: incident_management
category: incident_management
aggregation: weekly
- feature_flag: usage_data_incident_management_alert_status_changed
- name: incident_management_alert_assigned
redis_slot: incident_management
category: incident_management
aggregation: weekly
- feature_flag: usage_data_incident_management_alert_assigned
- name: incident_management_alert_todo
redis_slot: incident_management
category: incident_management
aggregation: weekly
- feature_flag: usage_data_incident_management_alert_todo
- name: incident_management_incident_created
redis_slot: incident_management
category: incident_management
aggregation: weekly
- feature_flag: usage_data_incident_management_incident_created
- name: incident_management_incident_reopened
redis_slot: incident_management
category: incident_management
aggregation: weekly
- feature_flag: usage_data_incident_management_incident_reopened
- name: incident_management_incident_closed
redis_slot: incident_management
category: incident_management
aggregation: weekly
- feature_flag: usage_data_incident_management_incident_closed
- name: incident_management_incident_assigned
redis_slot: incident_management
category: incident_management
aggregation: weekly
- feature_flag: usage_data_incident_management_incident_assigned
- name: incident_management_incident_todo
redis_slot: incident_management
category: incident_management
aggregation: weekly
- feature_flag: usage_data_incident_management_incident_todo
- name: incident_management_incident_comment
redis_slot: incident_management
category: incident_management
aggregation: weekly
- feature_flag: usage_data_incident_management_incident_comment
- name: incident_management_incident_zoom_meeting
redis_slot: incident_management
category: incident_management
aggregation: weekly
- feature_flag: usage_data_incident_management_incident_zoom_meeting
- name: incident_management_incident_published
redis_slot: incident_management
category: incident_management
@@ -230,23 +131,19 @@
redis_slot: incident_management
category: incident_management
aggregation: weekly
- feature_flag: usage_data_incident_management_incident_relate
- name: incident_management_incident_unrelate
redis_slot: incident_management
category: incident_management
aggregation: weekly
- feature_flag: usage_data_incident_management_incident_unrelate
- name: incident_management_incident_change_confidential
redis_slot: incident_management
category: incident_management
aggregation: weekly
- feature_flag: usage_data_incident_management_incident_change_confidential
# Incident management alerts
- name: incident_management_alert_create_incident
redis_slot: incident_management
category: incident_management_alerts
aggregation: weekly
- feature_flag: usage_data_incident_management_alert_create_incident
# Incident management on-call
- name: i_incident_management_oncall_notification_sent
redis_slot: incident_management
diff --git a/lib/gitlab/usage_data_counters/known_events/ecosystem.yml b/lib/gitlab/usage_data_counters/known_events/ecosystem.yml
index 1c765bb1830..adc5ba36ad7 100644
--- a/lib/gitlab/usage_data_counters/known_events/ecosystem.yml
+++ b/lib/gitlab/usage_data_counters/known_events/ecosystem.yml
@@ -24,45 +24,35 @@
category: ecosystem
redis_slot: ecosystem
aggregation: weekly
- feature_flag: usage_data_track_ecosystem_slack_service
- name: i_ecosystem_slack_service_push_notification
category: ecosystem
redis_slot: ecosystem
aggregation: weekly
- feature_flag: usage_data_track_ecosystem_slack_service
- name: i_ecosystem_slack_service_deployment_notification
category: ecosystem
redis_slot: ecosystem
aggregation: weekly
- feature_flag: usage_data_track_ecosystem_slack_service
- name: i_ecosystem_slack_service_wiki_page_notification
category: ecosystem
redis_slot: ecosystem
aggregation: weekly
- feature_flag: usage_data_track_ecosystem_slack_service
- name: i_ecosystem_slack_service_merge_request_notification
category: ecosystem
redis_slot: ecosystem
aggregation: weekly
- feature_flag: usage_data_track_ecosystem_slack_service
- name: i_ecosystem_slack_service_note_notification
category: ecosystem
redis_slot: ecosystem
aggregation: weekly
- feature_flag: usage_data_track_ecosystem_slack_service
- name: i_ecosystem_slack_service_tag_push_notification
category: ecosystem
redis_slot: ecosystem
aggregation: weekly
- feature_flag: usage_data_track_ecosystem_slack_service
- name: i_ecosystem_slack_service_confidential_note_notification
category: ecosystem
redis_slot: ecosystem
aggregation: weekly
- feature_flag: usage_data_track_ecosystem_slack_service
- name: i_ecosystem_slack_service_confidential_issue_notification
category: ecosystem
redis_slot: ecosystem
aggregation: weekly
- feature_flag: usage_data_track_ecosystem_slack_service
-
diff --git a/lib/gitlab/usage_data_counters/known_events/epic_board_events.yml b/lib/gitlab/usage_data_counters/known_events/epic_board_events.yml
new file mode 100644
index 00000000000..281db441829
--- /dev/null
+++ b/lib/gitlab/usage_data_counters/known_events/epic_board_events.yml
@@ -0,0 +1,22 @@
+# Epic board events
+#
+# We are using the same slot of issue events 'project_management' for
+# epic events to allow data aggregation.
+# More information in: https://gitlab.com/gitlab-org/gitlab/-/issues/322405
+- name: g_project_management_users_creating_epic_boards
+ category: epic_boards_usage
+ redis_slot: project_management
+ aggregation: daily
+ feature_flag: track_epic_boards_activity
+
+- name: g_project_management_users_viewing_epic_boards
+ category: epic_boards_usage
+ redis_slot: project_management
+ aggregation: daily
+ feature_flag: track_epic_boards_activity
+
+- name: g_project_management_users_updating_epic_board_names
+ category: epic_boards_usage
+ redis_slot: project_management
+ aggregation: daily
+ feature_flag: track_epic_boards_activity
diff --git a/lib/gitlab/usage_data_counters/known_events/epic_events.yml b/lib/gitlab/usage_data_counters/known_events/epic_events.yml
index 80460dbe4d2..d1864cd569b 100644
--- a/lib/gitlab/usage_data_counters/known_events/epic_events.yml
+++ b/lib/gitlab/usage_data_counters/known_events/epic_events.yml
@@ -9,6 +9,20 @@
aggregation: daily
feature_flag: track_epics_activity
+# content change events
+
+- name: project_management_users_unchecking_epic_task
+ category: epics_usage
+ redis_slot: project_management
+ aggregation: daily
+ feature_flag: track_epics_activity
+
+- name: project_management_users_checking_epic_task
+ category: epics_usage
+ redis_slot: project_management
+ aggregation: daily
+ feature_flag: track_epics_activity
+
- name: g_project_management_users_updating_epic_titles
category: epics_usage
redis_slot: project_management
@@ -41,6 +55,20 @@
aggregation: daily
feature_flag: track_epics_activity
+# emoji
+
+- name: g_project_management_users_awarding_epic_emoji
+ category: epics_usage
+ redis_slot: project_management
+ aggregation: daily
+ feature_flag: track_epics_activity
+
+- name: g_project_management_users_removing_epic_emoji
+ category: epics_usage
+ redis_slot: project_management
+ aggregation: daily
+ feature_flag: track_epics_activity
+
# start date events
- name: g_project_management_users_setting_epic_start_date_as_fixed
@@ -81,6 +109,8 @@
aggregation: daily
feature_flag: track_epics_activity
+# relationships
+
- name: g_project_management_epic_issue_added
category: epics_usage
redis_slot: project_management
@@ -99,6 +129,12 @@
aggregation: daily
feature_flag: track_epics_activity
+- name: g_project_management_users_updating_epic_parent
+ category: epics_usage
+ redis_slot: project_management
+ aggregation: daily
+ feature_flag: track_epics_activity
+
- name: g_project_management_epic_closed
category: epics_usage
redis_slot: project_management
@@ -140,3 +176,9 @@
redis_slot: project_management
aggregation: daily
feature_flag: track_epics_activity
+
+- name: g_project_management_epic_cross_referenced
+ category: epics_usage
+ redis_slot: project_management
+ aggregation: daily
+ feature_flag: track_epics_activity
diff --git a/lib/gitlab/usage_data_counters/known_events/package_events.yml b/lib/gitlab/usage_data_counters/known_events/package_events.yml
index b7e583003c8..d8ad2b538d6 100644
--- a/lib/gitlab/usage_data_counters/known_events/package_events.yml
+++ b/lib/gitlab/usage_data_counters/known_events/package_events.yml
@@ -95,3 +95,11 @@
category: user_packages
aggregation: weekly
redis_slot: package
+- name: i_package_terraform_module_deploy_token
+ category: deploy_token_packages
+ aggregation: weekly
+ redis_slot: package
+- name: i_package_terraform_module_user
+ category: user_packages
+ aggregation: weekly
+ redis_slot: package
diff --git a/lib/gitlab/usage_data_counters/known_events/quickactions.yml b/lib/gitlab/usage_data_counters/known_events/quickactions.yml
index 0fe65afb237..c1eabb352f7 100644
--- a/lib/gitlab/usage_data_counters/known_events/quickactions.yml
+++ b/lib/gitlab/usage_data_counters/known_events/quickactions.yml
@@ -3,334 +3,267 @@
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_assign_single
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_assign_multiple
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_assign_self
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_assign_reviewer
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_award
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_board_move
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_child_epic
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_clear_weight
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_clone
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_close
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_confidential
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_copy_metadata_merge_request
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_copy_metadata_issue
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_create_merge_request
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_done
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_draft
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_due
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_duplicate
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_epic
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_estimate
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_iteration
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_label
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_lock
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_merge
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_milestone
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_move
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_parent_epic
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_promote
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_publish
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_reassign
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_reassign_reviewer
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_rebase
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_relabel
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_relate
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_remove_child_epic
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_remove_due_date
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_remove_epic
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_remove_estimate
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_remove_iteration
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_remove_milestone
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_remove_parent_epic
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_remove_time_spent
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_remove_zoom
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_reopen
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_shrug
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_spend_subtract
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_spend_add
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_submit_review
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_subscribe
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_tableflip
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_tag
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_target_branch
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_title
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_todo
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_unassign_specific
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_unassign_all
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_unassign_reviewer
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_unlabel_specific
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_unlabel_all
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_unlock
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_unsubscribe
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_weight
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_wip
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_zoom
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_invite_email_single
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
- name: i_quickactions_invite_email_multiple
category: quickactions
redis_slot: quickactions
aggregation: weekly
- feature_flag: usage_data_track_quickactions
diff --git a/lib/gitlab/usage_data_counters/kubernetes_agent_counter.rb b/lib/gitlab/usage_data_counters/kubernetes_agent_counter.rb
index eae42bdc4a1..8b9ca0fc220 100644
--- a/lib/gitlab/usage_data_counters/kubernetes_agent_counter.rb
+++ b/lib/gitlab/usage_data_counters/kubernetes_agent_counter.rb
@@ -4,17 +4,27 @@ module Gitlab
module UsageDataCounters
class KubernetesAgentCounter < BaseCounter
PREFIX = 'kubernetes_agent'
- KNOWN_EVENTS = %w[gitops_sync].freeze
+ KNOWN_EVENTS = %w[gitops_sync k8s_api_proxy_request].freeze
class << self
- def increment_gitops_sync(incr)
- raise ArgumentError, 'must be greater than or equal to zero' if incr < 0
+ def increment_event_counts(events)
+ validate!(events)
- # rather then hitting redis for this no-op, we return early
- # note: redis returns the increment, so we mimic this here
- return 0 if incr == 0
+ events.each do |event, incr|
+ # rather then hitting redis for this no-op, we return early
+ next if incr == 0
- increment_by(redis_key(:gitops_sync), incr)
+ increment_by(redis_key(event), incr)
+ end
+ end
+
+ private
+
+ def validate!(events)
+ events.each do |event, incr|
+ raise ArgumentError, "unknown event #{event}" unless event.in?(KNOWN_EVENTS)
+ raise ArgumentError, "#{event} count must be greater than or equal to zero" if incr < 0
+ end
end
end
end
diff --git a/lib/gitlab/usage_data_counters/quick_action_activity_unique_counter.rb b/lib/gitlab/usage_data_counters/quick_action_activity_unique_counter.rb
index ed3df7dcf75..557179ad57a 100644
--- a/lib/gitlab/usage_data_counters/quick_action_activity_unique_counter.rb
+++ b/lib/gitlab/usage_data_counters/quick_action_activity_unique_counter.rb
@@ -7,7 +7,6 @@ module Gitlab
# Tracks the quick action with name `name`.
# `args` is expected to be a single string, will be split internally when necessary.
def track_unique_action(name, args:, user:)
- return unless Feature.enabled?(:usage_data_track_quickactions, default_enabled: :yaml)
return unless user
args ||= ''
diff --git a/lib/gitlab/usage_data_metrics.rb b/lib/gitlab/usage_data_metrics.rb
new file mode 100644
index 00000000000..e181da01229
--- /dev/null
+++ b/lib/gitlab/usage_data_metrics.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Gitlab
+ class UsageDataMetrics
+ class << self
+ # Build the Usage Ping JSON payload from metrics YAML definitions which have instrumentation class set
+ def uncached_data
+ ::Gitlab::Usage::MetricDefinition.all.map do |definition|
+ instrumentation_class = definition.attributes[:instrumentation_class]
+
+ if instrumentation_class.present?
+ metric_value = "Gitlab::Usage::Metrics::Instrumentations::#{instrumentation_class}".constantize.new(time_frame: definition.attributes[:time_frame]).value
+
+ metric_payload(definition.key_path, metric_value)
+ else
+ {}
+ end
+ end.reduce({}, :deep_merge)
+ end
+
+ private
+
+ def metric_payload(key_path, value)
+ ::Gitlab::Usage::Metrics::KeyPathProcessor.process(key_path, value)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/usage_data_non_sql_metrics.rb b/lib/gitlab/usage_data_non_sql_metrics.rb
index 1f72bf4ce26..bc72a96a468 100644
--- a/lib/gitlab/usage_data_non_sql_metrics.rb
+++ b/lib/gitlab/usage_data_non_sql_metrics.rb
@@ -25,10 +25,17 @@ module Gitlab
SQL_METRIC_DEFAULT
end
- def maximum_id(model)
+ def maximum_id(model, column = nil)
end
- def minimum_id(model)
+ def minimum_id(model, column = nil)
+ end
+
+ def jira_service_data
+ {
+ projects_jira_server_active: 0,
+ projects_jira_cloud_active: 0
+ }
end
end
end
diff --git a/lib/gitlab/usage_data_queries.rb b/lib/gitlab/usage_data_queries.rb
index c0dfae88fc7..1c776501fdb 100644
--- a/lib/gitlab/usage_data_queries.rb
+++ b/lib/gitlab/usage_data_queries.rb
@@ -25,6 +25,27 @@ module Gitlab
relation.select(relation.all.table[column].sum).to_sql
end
+ # rubocop: disable CodeReuse/ActiveRecord
+ def histogram(relation, column, buckets:, bucket_size: buckets.size)
+ count_grouped = relation.group(column).select(Arel.star.count.as('count_grouped'))
+ cte = Gitlab::SQL::CTE.new(:count_cte, count_grouped)
+
+ bucket_segments = bucket_size - 1
+ width_bucket = Arel::Nodes::NamedFunction
+ .new('WIDTH_BUCKET', [cte.table[:count_grouped], buckets.first, buckets.last, bucket_segments])
+ .as('buckets')
+
+ query = cte
+ .table
+ .project(width_bucket, cte.table[:count])
+ .group('buckets')
+ .order('buckets')
+ .with(cte.to_arel)
+
+ query.to_sql
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
# For estimated distinct count use exact query instead of hll
# buckets query, because it can't be used to obtain estimations without
# supplementary ruby code present in Gitlab::Database::PostgresHll::BatchDistinctCounter
@@ -36,10 +57,21 @@ module Gitlab
'SELECT ' + args.map {|arg| "(#{arg})" }.join(' + ')
end
- def maximum_id(model)
+ def maximum_id(model, column = nil)
+ end
+
+ def minimum_id(model, column = nil)
+ end
+
+ def jira_service_data
+ {
+ projects_jira_server_active: 0,
+ projects_jira_cloud_active: 0
+ }
end
- def minimum_id(model)
+ def epics_deepest_relationship_level
+ { epics_deepest_relationship_level: 0 }
end
private
diff --git a/lib/gitlab/utils.rb b/lib/gitlab/utils.rb
index c1a57566640..d70e5c3594c 100644
--- a/lib/gitlab/utils.rb
+++ b/lib/gitlab/utils.rb
@@ -16,7 +16,7 @@ module Gitlab
path_regex = /(\A(\.{1,2})\z|\A\.\.[\/\\]|[\/\\]\.\.\z|[\/\\]\.\.[\/\\]|\n)/
if path.match?(path_regex)
- raise PathTraversalAttackError.new('Invalid path')
+ raise PathTraversalAttackError, 'Invalid path'
end
path
diff --git a/lib/gitlab/utils/override.rb b/lib/gitlab/utils/override.rb
index c92865636d0..39670a835a6 100644
--- a/lib/gitlab/utils/override.rb
+++ b/lib/gitlab/utils/override.rb
@@ -43,12 +43,12 @@ module Gitlab
instance_method_defined?(parent, method_name)
end
- raise NotImplementedError.new("#{klass}\##{method_name} doesn't exist!") unless overridden_parent
+ raise NotImplementedError, "#{klass}\##{method_name} doesn't exist!" unless overridden_parent
super_method_arity = find_direct_method(overridden_parent, method_name).arity
unless arity_compatible?(sub_method_arity, super_method_arity)
- raise NotImplementedError.new("#{subject}\##{method_name} has arity of #{sub_method_arity}, but #{overridden_parent}\##{method_name} has arity of #{super_method_arity}")
+ raise NotImplementedError, "#{subject}\##{method_name} has arity of #{sub_method_arity}, but #{overridden_parent}\##{method_name} has arity of #{super_method_arity}"
end
end
diff --git a/lib/gitlab/utils/usage_data.rb b/lib/gitlab/utils/usage_data.rb
index efa2f7a943f..b1ccdcb1df0 100644
--- a/lib/gitlab/utils/usage_data.rb
+++ b/lib/gitlab/utils/usage_data.rb
@@ -121,7 +121,7 @@ module Gitlab
count_grouped = relation.group(column).select(Arel.star.count.as('count_grouped'))
cte = Gitlab::SQL::CTE.new(:count_cte, count_grouped)
- # For example, 9 segements gives 10 buckets
+ # For example, 9 segments gives 10 buckets
bucket_segments = bucket_size - 1
width_bucket = Arel::Nodes::NamedFunction
@@ -171,7 +171,7 @@ module Gitlab
else
value
end
- rescue
+ rescue StandardError
fallback
end
@@ -188,7 +188,7 @@ module Gitlab
return fallback unless client
yield client
- rescue
+ rescue StandardError
fallback
end
@@ -210,20 +210,54 @@ module Gitlab
Gitlab::UsageDataCounters::HLLRedisCounter.track_event(event_name.to_s, values: values)
end
- def maximum_id(model)
- key = :"#{model.name.downcase}_maximum_id"
+ def maximum_id(model, column = nil)
+ key = :"#{model.name.downcase.gsub('::', '_')}_maximum_id"
+ column_to_read = column || :id
+
strong_memoize(key) do
- model.maximum(:id)
+ model.maximum(column_to_read)
end
end
- def minimum_id(model)
- key = :"#{model.name.downcase}_minimum_id"
+ # rubocop: disable UsageData/LargeTable:
+ def jira_service_data
+ data = {
+ projects_jira_server_active: 0,
+ projects_jira_cloud_active: 0
+ }
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ JiraService.active.includes(:jira_tracker_data).find_in_batches(batch_size: 100) do |services|
+ counts = services.group_by do |service|
+ # TODO: Simplify as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
+ service_url = service.data_fields&.url || (service.properties && service.properties['url'])
+ service_url&.include?('.atlassian.net') ? :cloud : :server
+ end
+
+ data[:projects_jira_server_active] += counts[:server].size if counts[:server]
+ data[:projects_jira_cloud_active] += counts[:cloud].size if counts[:cloud]
+ end
+
+ data
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: enable UsageData/LargeTable:
+
+ def minimum_id(model, column = nil)
+ key = :"#{model.name.downcase.gsub('::', '_')}_minimum_id"
+ column_to_read = column || :id
+
strong_memoize(key) do
- model.minimum(:id)
+ model.minimum(column_to_read)
end
end
+ def epics_deepest_relationship_level
+ # rubocop: disable UsageData/LargeTable
+ { epics_deepest_relationship_level: ::Epic.deepest_relationship_level.to_i }
+ # rubocop: enable UsageData/LargeTable
+ end
+
private
def prometheus_client(verify:)
@@ -237,7 +271,7 @@ module Gitlab
api_url = "#{scheme}://#{server_address}"
client = Gitlab::PrometheusClient.new(api_url, allow_local_requests: true, verify: verify)
break client if client.ready?
- rescue
+ rescue StandardError
nil
end
end
diff --git a/lib/gitlab/verify/batch_verifier.rb b/lib/gitlab/verify/batch_verifier.rb
index fc114a4e9dd..71d106db742 100644
--- a/lib/gitlab/verify/batch_verifier.rb
+++ b/lib/gitlab/verify/batch_verifier.rb
@@ -24,11 +24,11 @@ module Gitlab
end
def name
- raise NotImplementedError.new
+ raise NotImplementedError
end
def describe(_object)
- raise NotImplementedError.new
+ raise NotImplementedError
end
private
@@ -39,7 +39,7 @@ module Gitlab
def verify(object)
local?(object) ? verify_local(object) : verify_remote(object)
- rescue => err
+ rescue StandardError => err
failure(object, err.inspect)
end
@@ -77,27 +77,27 @@ module Gitlab
# This should return an ActiveRecord::Relation suitable for calling #in_batches on
def all_relation
- raise NotImplementedError.new
+ raise NotImplementedError
end
# Should return true if the object is stored locally
def local?(_object)
- raise NotImplementedError.new
+ raise NotImplementedError
end
# The checksum we expect the object to have
def expected_checksum(_object)
- raise NotImplementedError.new
+ raise NotImplementedError
end
# The freshly-recalculated checksum of the object
def actual_checksum(_object)
- raise NotImplementedError.new
+ raise NotImplementedError
end
# Be sure to perform a hard check of the remote object (don't just check DB value)
def remote_object_exists?(object)
- raise NotImplementedError.new
+ raise NotImplementedError
end
end
end
diff --git a/lib/gitlab/view/presenter/delegated.rb b/lib/gitlab/view/presenter/delegated.rb
index 4a90ab758fb..d14f8cc4e5e 100644
--- a/lib/gitlab/view/presenter/delegated.rb
+++ b/lib/gitlab/view/presenter/delegated.rb
@@ -11,7 +11,7 @@ module Gitlab
attributes.each do |key, value|
if subject.respond_to?(key)
- raise CannotOverrideMethodError.new("#{subject} already respond to #{key}!")
+ raise CannotOverrideMethodError, "#{subject} already respond to #{key}!"
end
define_singleton_method(key) { value }
diff --git a/lib/gitlab/web_ide/config/entry/global.rb b/lib/gitlab/web_ide/config/entry/global.rb
index 2c67c7d02d4..2939095fd0f 100644
--- a/lib/gitlab/web_ide/config/entry/global.rb
+++ b/lib/gitlab/web_ide/config/entry/global.rb
@@ -30,4 +30,4 @@ module Gitlab
end
end
-::Gitlab::WebIde::Config::Entry::Global.prepend_if_ee('EE::Gitlab::WebIde::Config::Entry::Global')
+::Gitlab::WebIde::Config::Entry::Global.prepend_mod_with('Gitlab::WebIde::Config::Entry::Global')
diff --git a/lib/gitlab/webpack/manifest.rb b/lib/gitlab/webpack/manifest.rb
index 9c967d99e3a..b73c2ebb578 100644
--- a/lib/gitlab/webpack/manifest.rb
+++ b/lib/gitlab/webpack/manifest.rb
@@ -102,13 +102,13 @@ module Gitlab
rescue OpenSSL::SSL::SSLError, EOFError => e
ssl_status = Gitlab.config.webpack.dev_server.https ? ' over SSL' : ''
raise ManifestLoadError.new("Could not connect to webpack-dev-server at #{uri}#{ssl_status}.\n\nIs SSL enabled? Check that settings in `gitlab.yml` and webpack-dev-server match.", e)
- rescue => e
+ rescue StandardError => e
raise ManifestLoadError.new("Could not load manifest from webpack-dev-server at #{uri}.\n\nIs webpack-dev-server running? Try running `gdk status webpack` or `gdk tail webpack`.", e)
end
def load_static_manifest
File.read(static_manifest_path)
- rescue => e
+ rescue StandardError => e
raise ManifestLoadError.new("Could not load compiled manifest from #{static_manifest_path}.\n\nHave you run `rake gitlab:assets:compile`?", e)
end
diff --git a/lib/gitlab/x509/signature.rb b/lib/gitlab/x509/signature.rb
index edff1540cb3..c83213e973b 100644
--- a/lib/gitlab/x509/signature.rb
+++ b/lib/gitlab/x509/signature.rb
@@ -72,7 +72,7 @@ module Gitlab
pkcs7_text = pkcs7_text.sub('-----END SIGNED MESSAGE-----', '-----END PKCS7-----')
OpenSSL::PKCS7.new(pkcs7_text)
- rescue
+ rescue StandardError
nil
end
end
@@ -87,7 +87,7 @@ module Gitlab
def valid_signature?
p7.verify([], cert_store, signed_text, OpenSSL::PKCS7::NOVERIFY)
- rescue
+ rescue StandardError
nil
end
@@ -104,7 +104,7 @@ module Gitlab
else
nil
end
- rescue
+ rescue StandardError
nil
end
diff --git a/lib/gitlab/x509/tag.rb b/lib/gitlab/x509/tag.rb
index 48582c17764..ad85b200130 100644
--- a/lib/gitlab/x509/tag.rb
+++ b/lib/gitlab/x509/tag.rb
@@ -23,7 +23,7 @@ module Gitlab
def signature_text
@raw_tag.message.slice(@raw_tag.message.index("-----BEGIN SIGNED MESSAGE-----")..-1)
- rescue
+ rescue StandardError
nil
end