From 0076bbc67375ff1507e42ce479406daf92c0a6a2 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Fri, 28 Oct 2022 18:10:48 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- app/finders/issuable_finder.rb | 5 +- app/models/ci/build_metadata.rb | 7 +- app/models/concerns/ci/partitionable.rb | 9 +- .../concerns/ci/partitionable/switch.rb | 55 +++ app/models/concerns/issuable.rb | 4 + .../concerns/pg_full_text_searchable.rb | 47 +- app/models/issue.rb | 6 +- .../schedule_to_delete_review_apps_service.rb | 2 +- .../namespaces/root_statistics_worker.rb | 1 + ...g_use_ci_builds_metadata_routing_table.yml | 8 + config/locales/doorkeeper.zh-cn.yml | 122 +++++ ...e_migrate_shared_vulnerability_scanners.rb | 24 +- ...e_migrate_shared_vulnerability_scanners.rb | 44 ++ ...e_migrate_shared_vulnerability_scanners.rb | 29 ++ db/schema_migrations/20220919080303 | 1 + db/schema_migrations/20220919080304 | 1 + doc/administration/logs/index.md | 6 + .../blueprints/graphql_api/index.md | 2 +- doc/ci/runners/saas/macos_saas_runner.md | 2 +- doc/development/code_review.md | 2 +- doc/development/contributing/design.md | 2 +- doc/development/fe_guide/view_component.md | 2 +- doc/development/integrations/index.md | 4 +- ...meline_event_for_severity_change_v15_6.png | Bin 0 -> 9608 bytes .../incident_timeline_events.md | 15 + doc/topics/awesome_co.md | 2 +- doc/user/group/epics/manage_epics.md | 2 + doc/user/project/issues/design_management.md | 2 +- doc/user/project/quick_actions.md | 2 +- doc/user/project/requirements/index.md | 2 +- lib/api/api.rb | 2 +- lib/gitlab/quick_actions/issuable_actions.rb | 60 ++- lib/gitlab/quick_actions/issue_actions.rb | 17 - .../size_limiter/compressor.rb | 2 +- lib/gitlab/sidekiq_migrate_jobs.rb | 8 +- lib/gitlab/sql/pattern.rb | 30 +- locale/gitlab.pot | 10 +- package.json | 2 +- .../issue/check_mentions_for_xss_spec.rb | 2 +- rubocop/cop/gitlab/mark_used_feature_flags.rb | 2 +- scripts/review_apps/base-config.yaml | 58 +-- spec/db/schema_spec.rb | 1 + .../projects/pipelines/pipelines_spec.rb | 9 +- .../dynamic_content_spec.js.snap | 2 +- spec/lib/gitlab/sidekiq_migrate_jobs_spec.rb | 4 +- spec/lib/gitlab/sql/pattern_spec.rb | 40 +- ...rate_shared_vulnerability_scanners_spec.rb | 69 +++ ...rate_shared_vulnerability_scanners_spec.rb | 41 ++ ...rate_shared_vulnerability_scanners_spec.rb | 59 --- spec/models/ci/build_metadata_spec.rb | 22 + .../concerns/ci/partitionable/switch_spec.rb | 294 ++++++++++++ spec/models/concerns/ci/partitionable_spec.rb | 21 +- spec/models/concerns/issuable_spec.rb | 16 + .../concerns/pg_full_text_searchable_spec.rb | 25 +- .../gitlab/mark_used_feature_flags_spec.rb | 4 + .../finders/issues_finder_shared_examples.rb | 2 +- .../namespaces/root_statistics_worker_spec.rb | 8 + test.html | 439 ++++++++++++++++++ yarn.lock | 8 +- 59 files changed, 1439 insertions(+), 228 deletions(-) create mode 100644 app/models/concerns/ci/partitionable/switch.rb create mode 100644 config/feature_flags/development/ci_partitioning_use_ci_builds_metadata_routing_table.yml create mode 100644 config/locales/doorkeeper.zh-cn.yml create mode 100644 db/post_migrate/20220919080303_delete_migrate_shared_vulnerability_scanners.rb create mode 100644 db/post_migrate/20220919080304_reschedule_migrate_shared_vulnerability_scanners.rb create mode 100644 db/schema_migrations/20220919080303 create mode 100644 db/schema_migrations/20220919080304 create mode 100644 doc/operations/incident_management/img/timeline_event_for_severity_change_v15_6.png create mode 100644 spec/migrations/delete_migrate_shared_vulnerability_scanners_spec.rb create mode 100644 spec/migrations/reschedule_migrate_shared_vulnerability_scanners_spec.rb delete mode 100644 spec/migrations/schedule_migrate_shared_vulnerability_scanners_spec.rb create mode 100644 spec/models/concerns/ci/partitionable/switch_spec.rb create mode 100644 test.html diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 9f331d381aa..a6f5c826243 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -335,7 +335,7 @@ class IssuableFinder return items if items.is_a?(ActiveRecord::NullRelation) return items if Feature.enabled?(:disable_anonymous_search, type: :ops) && current_user.nil? - return items.pg_full_text_search(search) if use_full_text_search? + return items.pg_full_text_search(search, matched_columns: params[:in].to_s.split(',')) if use_full_text_search? if use_cte_for_search? cte = Gitlab::SQL::CTE.new(klass.table_name, items) @@ -348,8 +348,7 @@ class IssuableFinder # rubocop: enable CodeReuse/ActiveRecord def use_full_text_search? - params[:in].blank? && - klass.try(:pg_full_text_searchable_columns).present? && + klass.try(:pg_full_text_searchable_columns).present? && params[:search] =~ FULL_TEXT_SEARCH_TERM_REGEX && Feature.enabled?(:issues_full_text_search, params.project || params.group) end diff --git a/app/models/ci/build_metadata.rb b/app/models/ci/build_metadata.rb index ae35e0c8d43..879ff0321e3 100644 --- a/app/models/ci/build_metadata.rb +++ b/app/models/ci/build_metadata.rb @@ -5,6 +5,7 @@ module Ci # Data that should be persisted forever, should be stored with Ci::Build model. class BuildMetadata < Ci::ApplicationRecord BuildTimeout = Struct.new(:value, :source) + ROUTING_FEATURE_FLAG = :ci_partitioning_use_ci_builds_metadata_routing_table include Ci::Partitionable include Presentable @@ -14,7 +15,11 @@ module Ci self.table_name = 'ci_builds_metadata' self.primary_key = 'id' self.sequence_name = 'ci_builds_metadata_id_seq' - partitionable scope: :build + + partitionable scope: :build, through: { + table: :p_ci_builds_metadata, + flag: ROUTING_FEATURE_FLAG + } belongs_to :build, class_name: 'CommitStatus' belongs_to :project diff --git a/app/models/concerns/ci/partitionable.rb b/app/models/concerns/ci/partitionable.rb index df803180e77..68a6714c892 100644 --- a/app/models/concerns/ci/partitionable.rb +++ b/app/models/concerns/ci/partitionable.rb @@ -57,9 +57,14 @@ module Ci end class_methods do - private + def partitionable(scope:, through: nil) + if through + define_singleton_method(:routing_table_name) { through[:table] } + define_singleton_method(:routing_table_name_flag) { through[:flag] } + + include Partitionable::Switch + end - def partitionable(scope:) define_method(:partition_scope_value) do strong_memoize(:partition_scope_value) do next Ci::Pipeline.current_partition_value if respond_to?(:importing?) && importing? diff --git a/app/models/concerns/ci/partitionable/switch.rb b/app/models/concerns/ci/partitionable/switch.rb new file mode 100644 index 00000000000..032f34fba1f --- /dev/null +++ b/app/models/concerns/ci/partitionable/switch.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +module Ci + module Partitionable + module Switch + extend ActiveSupport::Concern + + # These methods are cached at the class level and depend on the value + # of `table_name`, changing that value resets them. + # `cached_find_by_statement` is used to cache SQL statements which can + # include the table name. + # + SWAPABLE_METHODS = %i[table_name quoted_table_name arel_table + predicate_builder cached_find_by_statement].freeze + + included do |base| + partitioned = Class.new(base) do + self.table_name = base.routing_table_name + + def self.routing_class? + true + end + end + + base.const_set(:Partitioned, partitioned) + end + + class_methods do + def routing_class? + false + end + + def routing_table_enabled? + return false if routing_class? + + ::Feature.enabled?(routing_table_name_flag) + end + + # We're delegating them to the `Partitioned` model. + # They do not require any check override since they come from AR core + # (are always defined) and we're using `super` to get the value. + # + SWAPABLE_METHODS.each do |name| + define_method(name) do |*args, &block| + if routing_table_enabled? + self::Partitioned.public_send(name, *args, &block) # rubocop: disable GitlabSecurity/PublicSend + else + super(*args, &block) + end + end + end + end + end + end +end diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index f8389865f91..cdbc7092b63 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -217,6 +217,10 @@ module Issuable false end + def supports_confidentiality? + false + end + def severity return IssuableSeverity::DEFAULT unless supports_severity? diff --git a/app/models/concerns/pg_full_text_searchable.rb b/app/models/concerns/pg_full_text_searchable.rb index 335fcec2611..f533791c2ee 100644 --- a/app/models/concerns/pg_full_text_searchable.rb +++ b/app/models/concerns/pg_full_text_searchable.rb @@ -25,6 +25,7 @@ module PgFullTextSearchable TSVECTOR_MAX_LENGTH = 1.megabyte.freeze TEXT_SEARCH_DICTIONARY = 'english' URL_SCHEME_REGEX = %r{(?<=\A|\W)\w+://(?=\w+)}.freeze + TSQUERY_DISALLOWED_CHARACTERS_REGEX = %r{[^a-zA-Z0-9 .@/\-"]}.freeze def update_search_data! tsvector_sql_nodes = self.class.pg_full_text_searchable_columns.map do |column, weight| @@ -102,21 +103,16 @@ module PgFullTextSearchable end end - def pg_full_text_search(search_term) + def pg_full_text_search(query, matched_columns: []) search_data_table = reflect_on_association(:search_data).klass.arel_table - # This fixes an inconsistency with how to_tsvector and websearch_to_tsquery process URLs - # See https://gitlab.com/gitlab-org/gitlab/-/issues/354784#note_905431920 - search_term = remove_url_scheme(search_term) - search_term = ActiveSupport::Inflector.transliterate(search_term) - joins(:search_data).where( Arel::Nodes::InfixOperation.new( '@@', search_data_table[:search_vector], Arel::Nodes::NamedFunction.new( - 'websearch_to_tsquery', - [Arel::Nodes.build_quoted(TEXT_SEARCH_DICTIONARY), Arel::Nodes.build_quoted(search_term)] + 'to_tsquery', + [Arel::Nodes.build_quoted(TEXT_SEARCH_DICTIONARY), build_tsquery(query, matched_columns)] ) ) ) @@ -124,8 +120,39 @@ module PgFullTextSearchable private - def remove_url_scheme(search_term) - search_term.gsub(URL_SCHEME_REGEX, '') + def build_tsquery(query, matched_columns) + # URLs get broken up into separate words when : is removed below, so we just remove the whole scheme. + query = remove_url_scheme(query) + # Remove accents from search term to match indexed data + query = ActiveSupport::Inflector.transliterate(query) + # Prevent users from using tsquery operators that can cause syntax errors. + query = filter_allowed_characters(query) + + weights = matched_columns.map do |column_name| + pg_full_text_searchable_columns[column_name] + end.compact.join + prefix_search_suffix = ":*#{weights}" + + tsquery = Gitlab::SQL::Pattern.split_query_to_search_terms(query).map do |search_term| + case search_term + when /\A\d+\z/ # Handles https://gitlab.com/gitlab-org/gitlab/-/issues/375337 + "(#{search_term + prefix_search_suffix} | -#{search_term + prefix_search_suffix})" + when /\s/ + search_term.split.map { |t| "#{t}:#{weights}" }.join(' <-> ') + else + search_term + prefix_search_suffix + end + end.join(' & ') + + Arel::Nodes.build_quoted(tsquery) + end + + def remove_url_scheme(query) + query.gsub(URL_SCHEME_REGEX, '') + end + + def filter_allowed_characters(query) + query.gsub(TSQUERY_DISALLOWED_CHARACTERS_REGEX, ' ') end end end diff --git a/app/models/issue.rb b/app/models/issue.rb index 61fc0264360..639bb3140f8 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -272,7 +272,7 @@ class Issue < ApplicationRecord end override :pg_full_text_search - def pg_full_text_search(search_term) + def pg_full_text_search(query, matched_columns: []) super.where('issue_search_data.project_id = issues.project_id') end end @@ -654,6 +654,10 @@ class Issue < ApplicationRecord Gitlab::EtagCaching::Store.new.touch(key) end + def supports_confidentiality? + true + end + private def due_date_after_start_date diff --git a/app/services/environments/schedule_to_delete_review_apps_service.rb b/app/services/environments/schedule_to_delete_review_apps_service.rb index b3b86689748..041b834f11b 100644 --- a/app/services/environments/schedule_to_delete_review_apps_service.rb +++ b/app/services/environments/schedule_to_delete_review_apps_service.rb @@ -58,7 +58,7 @@ module Environments else result.set_status( :bad_request, - error_message: "Failed to authorize deletions for some or all of the environments. Ask someone with more permissions to delete the environments." + error_message: "No environments found for scheduled deletion. Either your query did not match any environments (default parameters match environments that are 30 days or older), or you have insufficient permissions to delete matching environments." ) result.set_unprocessable_entries(failed) diff --git a/app/workers/namespaces/root_statistics_worker.rb b/app/workers/namespaces/root_statistics_worker.rb index e1271dae335..157a779dda6 100644 --- a/app/workers/namespaces/root_statistics_worker.rb +++ b/app/workers/namespaces/root_statistics_worker.rb @@ -11,6 +11,7 @@ module Namespaces queue_namespace :update_namespace_statistics feature_category :source_code_management idempotent! + deduplicate :until_executed, if_deduplicated: :reschedule_once def perform(namespace_id) namespace = Namespace.find(namespace_id) diff --git a/config/feature_flags/development/ci_partitioning_use_ci_builds_metadata_routing_table.yml b/config/feature_flags/development/ci_partitioning_use_ci_builds_metadata_routing_table.yml new file mode 100644 index 00000000000..71c2aa735a2 --- /dev/null +++ b/config/feature_flags/development/ci_partitioning_use_ci_builds_metadata_routing_table.yml @@ -0,0 +1,8 @@ +--- +name: ci_partitioning_use_ci_builds_metadata_routing_table +introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/100935" +rollout_issue_url: "https://gitlab.com/gitlab-org/gitlab/-/issues/378601" +milestone: '15.6' +type: development +group: "group::pipeline execution" +default_enabled: false diff --git a/config/locales/doorkeeper.zh-cn.yml b/config/locales/doorkeeper.zh-cn.yml new file mode 100644 index 00000000000..f9b37c43866 --- /dev/null +++ b/config/locales/doorkeeper.zh-cn.yml @@ -0,0 +1,122 @@ +zh-CN: + activerecord: + errors: + models: + application: + attributes: + redirect_uri: + fragment_present: '不能包含片段。' + invalid_uri: '必须是一个有效的 URI。' + relative_uri: '必须是一个绝对 URI。' + mongoid: + errors: + models: + application: + attributes: + redirect_uri: + fragment_present: '不能包含片段。' + invalid_uri: '必须是一个有效的 URI。' + relative_uri: '必须是一个绝对 URI。' + mongo_mapper: + errors: + models: + application: + attributes: + redirect_uri: + fragment_present: '不能包含片段。' + invalid_uri: '必须是一个有效的 URI。' + relative_uri: '必须是一个绝对 URI。' + doorkeeper: + errors: + messages: + # Common error messages + invalid_redirect_uri: '包含的重定向 URI 无效。' + unauthorized_client: '客户端无权使用此方法执行此请求。' + access_denied: '资源所有者或授权服务器拒绝了该请求。' + invalid_scope: '请求的范围无效、未知或格式不正确。' + server_error: '授权服务器遇到了意外情况,导致无法完成请求。' + unconfirmed_email: '在您登录之前,验证您的帐户配置文件中的电子邮件地址。' + temporarily_unavailable: '由于服务器临时超载或维护,授权服务器目前无法处理请求。' + + #configuration error messages + credential_flow_not_configured: '由于 Doorkeeper.configure.resource_owner_from_credentials 未配置,资源所有者密码凭证授予工作流失败。' + resource_owner_authenticator_not_configured: '由于 Doorkeeper.configure.resource_owner_authenticator 未配置,资源所有者查找失败。' + + # Access grant errors + unsupported_response_type: '授权服务器不支持此响应类型。' + + # Access token errors + invalid_client: '由于未知客户端、不包括客户端身份验证或不支持的身份验证方法,客户端身份验证失败。' + invalid_grant: '所提供的授权无效、过期、被撤销、与授权请求中使用的重定向 URI 不匹配,或者已向另一个客户端发出。' + unsupported_grant_type: '授权服务器不支持授权授予类型。' + + # Password Access token errors + invalid_resource_owner: '所提供的资源所有者凭证无效,或找不到资源所有者。' + + invalid_request: + unknown: '该请求缺少一个必需的参数,包括一个不支持的参数值,或在其他方面是错误的。' + missing_param: '缺少所需的参数:%{value}。' + not_support_pkce: '无效的 code_verifier 参数。服务器不支持 pkce。' + request_not_authorized: '请求需要授权。授权请求所需的参数缺失或无效。' + + invalid_token: + revoked: "访问令牌被撤销" + expired: "访问令牌过期" + unknown: "访问令牌无效" + scopes: + api: 访问经过验证的用户的 API + read_user: 读取已验证用户的个人信息 + read_repository: 允许对仓库进行只读访问 + write_repository: 允许对仓库进行读写访问 + read_registry: 授予读取容器镜像库镜像的权限 + openid: 使用 OpenID Connect 进行身份验证 + sudo: 作为系统中的任何用户执行 API 操作 + profile: 允许使用 OpenID Connect 只读访问用户的个人信息 + email: 允许使用 OpenID Connect 只读访问用户的主要电子邮件地址 + scope_desc: + api: + 授予对 API 的完全读/写访问权,包括所有群组和项目、容器镜像库和软件包库。 + read_api: + 授予对 API 的读访问权,包括所有群组和项目、容器镜像库和软件包库。 + read_user: + 通过 /user API端点授予对通过身份验证的用户概要的只读访问权,该端点包括用户名、公共电子邮件和全名。还授予对 /users 下的只读 API 端点的访问权。 + read_repository: + 使用 Git-over-HTTP 或 Repository Files API 授予对私有项目仓库的只读访问权。 + write_repository: + 使用 Git-over-HTTP (不使用 API)授予对私有项目上的仓库的读写访问权。 + read_registry: + 授予对私有项目上的容器镜像库镜像的只读访问权。 + write_registry: + 授予对私有项目上的容器镜像库镜像的写访问权。 + openid: + 授予使用 OpenID Connect 与 GitLab 进行身份验证的权限。还提供对用户配置文件和组成员关系的只读访问权限。 + sudo: + 当以管理员用户身份进行身份验证时,授予作为系统中任何用户执行 API 操作的权限。 + profile: + 使用 OpenID Connect 授予对用户配置文件数据的只读访问权。 + email: + 使用 OpenID Connect 授予对用户主电子邮件地址的只读访问权。 + project_access_token_scope_desc: + api: + 授予对限定范围的项目 API 的完全读写访问权。 + read_api: + 授予对限定范围的项目 API 的读访问权。 + read_repository: + 允许只读访问(拉取)到仓库。 + write_repository: + 允许对仓库的读写访问(拉取、推送)。 + read_registry: + 如果项目是私有的且需要授权,则允许读取(拉取)容器镜像库镜像。 + write_registry: + 允许写访问(推送)到容器镜像库。 + flash: + applications: + create: + notice: '创建应用成功。' + destroy: + notice: '删除应用成功。' + update: + notice: '更新应用成功。' + authorized_applications: + destroy: + notice: '应用被撤销访问权限。' diff --git a/db/post_migrate/20220802112102_schedule_migrate_shared_vulnerability_scanners.rb b/db/post_migrate/20220802112102_schedule_migrate_shared_vulnerability_scanners.rb index 92ca0998bae..724bd323169 100644 --- a/db/post_migrate/20220802112102_schedule_migrate_shared_vulnerability_scanners.rb +++ b/db/post_migrate/20220802112102_schedule_migrate_shared_vulnerability_scanners.rb @@ -1,34 +1,14 @@ # frozen_string_literal: true class ScheduleMigrateSharedVulnerabilityScanners < Gitlab::Database::Migration[2.0] - MIGRATION = "MigrateSharedVulnerabilityScanners" - TABLE_NAME = :vulnerability_occurrences - BATCH_COLUMN = :id - DELAY_INTERVAL = 5.minutes - BATCH_SIZE = 1000 - SUB_BATCH_SIZE = 100 - - BATCH_MIN_VALUE = 23658505 - BATCH_MAX_VALUE = 204428752 - disable_ddl_transaction! restrict_gitlab_migration gitlab_schema: :gitlab_main def up - queue_batched_background_migration( - MIGRATION, - TABLE_NAME, - BATCH_COLUMN, - job_interval: DELAY_INTERVAL, - batch_size: BATCH_SIZE, - max_batch_size: BATCH_SIZE, - sub_batch_size: SUB_BATCH_SIZE, - batch_min_value: BATCH_MIN_VALUE, - batch_max_value: BATCH_MAX_VALUE - ) + # no-op end def down - delete_batched_background_migration(MIGRATION, TABLE_NAME, BATCH_COLUMN, []) + # no-op end end diff --git a/db/post_migrate/20220919080303_delete_migrate_shared_vulnerability_scanners.rb b/db/post_migrate/20220919080303_delete_migrate_shared_vulnerability_scanners.rb new file mode 100644 index 00000000000..4aedfcf1699 --- /dev/null +++ b/db/post_migrate/20220919080303_delete_migrate_shared_vulnerability_scanners.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +class DeleteMigrateSharedVulnerabilityScanners < Gitlab::Database::Migration[2.0] + disable_ddl_transaction! + restrict_gitlab_migration gitlab_schema: :gitlab_main + + MIGRATION = "MigrateSharedVulnerabilityScanners" + TABLE_NAME = :vulnerability_occurrences + BATCH_COLUMN = :id + BATCH_SIZE = 250 + + class BatchedBackgroundMigration < MigrationRecord + self.table_name = "batched_background_migrations" + end + + class BatchedBackgroundMigrationJob < MigrationRecord + include ::EachBatch + + self.table_name = "batched_background_migration_jobs" + + belongs_to :batched_background_migration + end + + def up + return unless migration_id = BatchedBackgroundMigration.find_by(job_class_name: MIGRATION)&.id + + # rubocop:disable Style/SymbolProc + BatchedBackgroundMigrationJob + .where(batched_background_migration_id: migration_id) + .each_batch(of: BATCH_SIZE) do |relation| + relation.delete_all + end + # rubocop:enable Style/SymbolProc + + delete_batched_background_migration(MIGRATION, + TABLE_NAME, + BATCH_COLUMN, + []) + end + + def down + # no-op + end +end diff --git a/db/post_migrate/20220919080304_reschedule_migrate_shared_vulnerability_scanners.rb b/db/post_migrate/20220919080304_reschedule_migrate_shared_vulnerability_scanners.rb new file mode 100644 index 00000000000..69757085587 --- /dev/null +++ b/db/post_migrate/20220919080304_reschedule_migrate_shared_vulnerability_scanners.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class RescheduleMigrateSharedVulnerabilityScanners < Gitlab::Database::Migration[2.0] + MIGRATION = "MigrateSharedVulnerabilityScanners" + TABLE_NAME = :vulnerability_occurrences + BATCH_COLUMN = :id + DELAY_INTERVAL = 5.minutes + BATCH_SIZE = 1000 + SUB_BATCH_SIZE = 100 + + disable_ddl_transaction! + restrict_gitlab_migration gitlab_schema: :gitlab_main + + def up + queue_batched_background_migration( + MIGRATION, + TABLE_NAME, + BATCH_COLUMN, + job_interval: DELAY_INTERVAL, + batch_size: BATCH_SIZE, + max_batch_size: BATCH_SIZE, + sub_batch_size: SUB_BATCH_SIZE + ) + end + + def down + delete_batched_background_migration(MIGRATION, TABLE_NAME, BATCH_COLUMN, []) + end +end diff --git a/db/schema_migrations/20220919080303 b/db/schema_migrations/20220919080303 new file mode 100644 index 00000000000..081e25c4ed5 --- /dev/null +++ b/db/schema_migrations/20220919080303 @@ -0,0 +1 @@ +9a5ba202075e0022defd834184aa59c60980cdccf7f4111834af6a119713b4c2 \ No newline at end of file diff --git a/db/schema_migrations/20220919080304 b/db/schema_migrations/20220919080304 new file mode 100644 index 00000000000..263128018ca --- /dev/null +++ b/db/schema_migrations/20220919080304 @@ -0,0 +1 @@ +d5883d3edad5d8cc130f26feb4cc6fdb63e3b46c513ce463bdf7e45a8d7ffcdf \ No newline at end of file diff --git a/doc/administration/logs/index.md b/doc/administration/logs/index.md index 2bcda759442..8512d19c30c 100644 --- a/doc/administration/logs/index.md +++ b/doc/administration/logs/index.md @@ -340,6 +340,12 @@ associated SSH key can download the project in question by using a `git fetch` o - `params`: Key-value pairs passed in a query string or HTTP body (sensitive parameters, such as passwords and tokens, are filtered out) - `ua`: The User-Agent of the requester +NOTE: +As of [`Grape Logging`](https://github.com/aserafin/grape_logging) v1.8.4, +the `view_duration_s` is calculated by [`duration_s - db_duration_s`](https://github.com/aserafin/grape_logging/blob/v1.8.4/lib/grape_logging/middleware/request_logger.rb#L117-L119). +Therefore, `view_duration_s` can be affected by multiple different factors, like read-write +process on Redis or external HTTP, not only the serialization process. + ## `application.log` Depending on your installation method, this file is located at: diff --git a/doc/architecture/blueprints/graphql_api/index.md b/doc/architecture/blueprints/graphql_api/index.md index baa926c5847..4b446a78541 100644 --- a/doc/architecture/blueprints/graphql_api/index.md +++ b/doc/architecture/blueprints/graphql_api/index.md @@ -4,7 +4,7 @@ creation-date: "2021-01-07" authors: [ "@grzesiek" ] coach: "@kamil" approvers: [ "@dsatcher", "@deuley" ] -owning-stage: "~devops::ecosystem" +owning-stage: "~devops::manage" participating-stages: [] --- diff --git a/doc/ci/runners/saas/macos_saas_runner.md b/doc/ci/runners/saas/macos_saas_runner.md index 50c24349712..cd40aac25bc 100644 --- a/doc/ci/runners/saas/macos_saas_runner.md +++ b/doc/ci/runners/saas/macos_saas_runner.md @@ -14,7 +14,7 @@ Use these runners to build, test, and deploy apps for the Apple ecosystem (macOS of all the capabilities of the GitLab single DevOps platform and not have to manage or operate a build environment. -Jobs handled by macOS shared runners on GitLab.com **time out after 2 hours**, regardless of the timeout configured in a project. +Jobs handled by macOS shared runners on GitLab.com **time out after 3 hours**, regardless of the timeout configured in a project. ## Access request process diff --git a/doc/development/code_review.md b/doc/development/code_review.md index d0569b9ca6c..280af21a864 100644 --- a/doc/development/code_review.md +++ b/doc/development/code_review.md @@ -147,7 +147,7 @@ with [domain expertise](#domain-experts). | `~workhorse` changes | [Workhorse maintainer](https://about.gitlab.com/handbook/engineering/projects/#gitlab_maintainers_workhorse). | | `~frontend` changes (*1*) | [Frontend maintainer](https://about.gitlab.com/handbook/engineering/projects/#gitlab_maintainers_frontend). | | `~UX` user-facing changes (*3*) | [Product Designer](https://about.gitlab.com/handbook/engineering/projects/#gitlab_reviewers_UX). Refer to the [design and user interface guidelines](contributing/design.md) for details. | -| Adding a new JavaScript library (*1*) | - [Frontend foundations member](https://about.gitlab.com/direction/ecosystem/foundations/) if the library significantly increases the [bundle size](https://gitlab.com/gitlab-org/frontend/playground/webpack-memory-metrics/-/blob/master/doc/report.md).
- A [legal department member](https://about.gitlab.com/handbook/legal/) if the license used by the new library hasn't been approved for use in GitLab.

More information about license compatibility can be found in our [GitLab Licensing and Compatibility documentation](licensing.md). | +| Adding a new JavaScript library (*1*) | - [Frontend foundations member](https://about.gitlab.com/direction/manage/foundations/) if the library significantly increases the [bundle size](https://gitlab.com/gitlab-org/frontend/playground/webpack-memory-metrics/-/blob/master/doc/report.md).
- A [legal department member](https://about.gitlab.com/handbook/legal/) if the license used by the new library hasn't been approved for use in GitLab.

More information about license compatibility can be found in our [GitLab Licensing and Compatibility documentation](licensing.md). | | A new dependency or a file system change | - [Distribution team member](https://about.gitlab.com/company/team/). See how to work with the [Distribution team](https://about.gitlab.com/handbook/engineering/development/enablement/systems/distribution/#how-to-work-with-distribution) for more details.
- For Rubygems, request an [AppSec review](gemfile.md#request-an-appsec-review). | | `~documentation` or `~UI text` changes | [Technical writer](https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments) based on assignments in the appropriate [DevOps stage group](https://about.gitlab.com/handbook/product/categories/#devops-stages). | | Changes to development guidelines | Follow the [review process](development_processes.md#development-guidelines-review) and get the approvals accordingly. | diff --git a/doc/development/contributing/design.md b/doc/development/contributing/design.md index 9e54b92337a..a1f9fa2e457 100644 --- a/doc/development/contributing/design.md +++ b/doc/development/contributing/design.md @@ -64,7 +64,7 @@ Check visual design properties using your browser's _elements inspector_ ([Chrom guidelines. - _Optionally_ consider [dark mode](../../user/profile/preferences.md#dark-mode). [^1] - [^1]: You're not required to design for [dark mode](../../user/profile/preferences.md#dark-mode) while the feature is in [alpha](../../policy/alpha-beta-support.md#alpha-features). The [UX Foundations team](https://about.gitlab.com/direction/ecosystem/foundations/) plans to improve the dark mode in the future. Until we integrate [Pajamas](https://design.gitlab.com/) components into the product and the underlying design strategy is in place to support dark mode, we cannot guarantee that we won't introduce bugs and debt to this mode. At your discretion, evaluate the need to create dark mode patches. + [^1]: You're not required to design for [dark mode](../../user/profile/preferences.md#dark-mode) while the feature is in [alpha](../../policy/alpha-beta-support.md#alpha-features). The [UX Foundations team](https://about.gitlab.com/direction/manage/foundations/) plans to improve the dark mode in the future. Until we integrate [Pajamas](https://design.gitlab.com/) components into the product and the underlying design strategy is in place to support dark mode, we cannot guarantee that we won't introduce bugs and debt to this mode. At your discretion, evaluate the need to create dark mode patches. ### States diff --git a/doc/development/fe_guide/view_component.md b/doc/development/fe_guide/view_component.md index 662d1ad32fc..d9d2b6707f7 100644 --- a/doc/development/fe_guide/view_component.md +++ b/doc/development/fe_guide/view_component.md @@ -24,7 +24,7 @@ available as a ViewComponent in `app/components/pajamas`. NOTE: We are still in the process of creating these components, so not every Pajamas component is available as ViewComponent. -Reach out to the [Foundations team](https://about.gitlab.com/handbook/engineering/development/dev/ecosystem/foundations/) +Reach out to the [Foundations team](https://about.gitlab.com/handbook/engineering/development/dev/manage/foundations/) if the component you are looking for is not yet available. ### Available components diff --git a/doc/development/integrations/index.md b/doc/development/integrations/index.md index a0a81775391..2639da818c6 100644 --- a/doc/development/integrations/index.md +++ b/doc/development/integrations/index.md @@ -10,9 +10,9 @@ description: "GitLab's development guidelines for Integrations" This page provides development guidelines for implementing [GitLab integrations](../../user/project/integrations/index.md), which are part of our [main Rails project](https://gitlab.com/gitlab-org/gitlab). -Also see our [direction page](https://about.gitlab.com/direction/ecosystem/integrations/) for an overview of our strategy around integrations. +Also see our [direction page](https://about.gitlab.com/direction/manage/integrations/) for an overview of our strategy around integrations. -This guide is a work in progress. You're welcome to ping `@gitlab-org/ecosystem-stage/integrations` +This guide is a work in progress. You're welcome to ping `@gitlab-org/manage/integrations` if you need clarification or spot any outdated information. ## Add a new integration diff --git a/doc/operations/incident_management/img/timeline_event_for_severity_change_v15_6.png b/doc/operations/incident_management/img/timeline_event_for_severity_change_v15_6.png new file mode 100644 index 0000000000000000000000000000000000000000..121cc4b23cb612eb005f3a82dc07833b32f3f53b GIT binary patch literal 9608 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 z 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 [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/375280) in GitLab 15.6 [with a flag](../../administration/feature_flags.md) named `incident_timeline_events_for_severity`. Disabled by default. + +FLAG: +On self-managed GitLab, by default this feature is unavailable. To show the feature per user, +ask an administrator to [enable the feature flag](../../administration/feature_flags.md) named `incident_timeline_events_for_severity`. +On GitLab.com, this feature is not available. +This feature is not ready for production use. + +A new timeline event is created when someone [changes the severity](incidents.md#change-severity) +of an incident. + +![Incident timeline event for severity change](img/timeline_event_for_severity_change_v15_6.png) + ## Delete an event You can also delete timeline events. diff --git a/doc/topics/awesome_co.md b/doc/topics/awesome_co.md index 0d725f64f3a..49e39542b2b 100644 --- a/doc/topics/awesome_co.md +++ b/doc/topics/awesome_co.md @@ -1,5 +1,5 @@ --- -stage: Ecosystem +stage: Manage group: Foundations info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments description: AwesomeCo test data harness created by the Test Data Working Group https://about.gitlab.com/company/team/structure/working-groups/demo-test-data/ diff --git a/doc/user/group/epics/manage_epics.md b/doc/user/group/epics/manage_epics.md index d810724e130..b96670669a0 100644 --- a/doc/user/group/epics/manage_epics.md +++ b/doc/user/group/epics/manage_epics.md @@ -311,6 +311,8 @@ To make an epic confidential: - **In an existing epic:** on the right sidebar, select **Edit** next to **Confidentiality**, and then select **Turn on**. +In GitLab 15.6 and later, you can also use the `/confidential` [quick action](../../../user/project/quick_actions.md). + ## Manage issues assigned to an epic This section collects instructions for all the things you can do with [issues](../../project/issues/index.md) diff --git a/doc/user/project/issues/design_management.md b/doc/user/project/issues/design_management.md index 593557967ed..27d935d0ed1 100644 --- a/doc/user/project/issues/design_management.md +++ b/doc/user/project/issues/design_management.md @@ -4,7 +4,7 @@ group: Product Planning info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments --- -# Design Management **(FREE)** +# Design management **(FREE)** > - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/660) in GitLab 12.2. > - Support for SVGs [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12771) in GitLab 12.4. diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md index 2ff579b8f8f..7d59d93df56 100644 --- a/doc/user/project/quick_actions.md +++ b/doc/user/project/quick_actions.md @@ -65,7 +65,7 @@ threads. Some quick actions might not be available to all subscription tiers. | `/clear_weight` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Clear weight. | | `/clone [--with_notes]` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Clone the issue to given project, or the current one if no arguments are given ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9421) in GitLab 13.7). Copies as much data as possible as long as the target project contains equivalent labels, milestones, and so on. Does not copy comments or system notes unless `--with_notes` is provided as an argument. | | `/close` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Close. | -| `/confidential` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Make confidential. | +| `/confidential` | **{check-circle}** Yes | **{dotted-circle}** No | **{check-circle}** Yes | Mark issue or epic as confidential. Support for epics [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213741) in GitLab 15.6. | | `/copy_metadata ` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Copy labels and milestone from another merge request in the project. | | `/copy_metadata <#issue>` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Copy labels and milestone from another issue in the project. | | `/create_merge_request ` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Create a new merge request starting from the current issue. | diff --git a/doc/user/project/requirements/index.md b/doc/user/project/requirements/index.md index 400f03ef0fe..922accf9d28 100644 --- a/doc/user/project/requirements/index.md +++ b/doc/user/project/requirements/index.md @@ -5,7 +5,7 @@ group: Certify info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments --- -# Requirements Management **(ULTIMATE)** +# Requirements management **(ULTIMATE)** NOTE: In 14.4, Requirements was moved under **Issues**. diff --git a/lib/api/api.rb b/lib/api/api.rb index 9d3e568074a..d07e0bcf33c 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -177,6 +177,7 @@ module API mount ::API::UserCounts mount ::API::ProjectRepositoryStorageMoves mount ::API::SnippetRepositoryStorageMoves + mount ::API::Statistics add_open_api_documentation! end @@ -307,7 +308,6 @@ module API mount ::API::Settings mount ::API::SidekiqMetrics mount ::API::Snippets - mount ::API::Statistics mount ::API::Submodules mount ::API::Subscriptions mount ::API::Suggestions diff --git a/lib/gitlab/quick_actions/issuable_actions.rb b/lib/gitlab/quick_actions/issuable_actions.rb index 3b85d6952a1..0b37c80dc5f 100644 --- a/lib/gitlab/quick_actions/issuable_actions.rb +++ b/lib/gitlab/quick_actions/issuable_actions.rb @@ -12,16 +12,13 @@ module Gitlab included do # Issue, MergeRequest, Epic: quick actions definitions desc do - _('Close this %{quick_action_target}') % - { quick_action_target: quick_action_target.to_ability_name.humanize(capitalize: false) } + _('Close this %{quick_action_target}') % { quick_action_target: target_issuable_name } end explanation do - _('Closes this %{quick_action_target}.') % - { quick_action_target: quick_action_target.to_ability_name.humanize(capitalize: false) } + _('Closes this %{quick_action_target}.') % { quick_action_target: target_issuable_name } end execution_message do - _('Closed this %{quick_action_target}.') % - { quick_action_target: quick_action_target.to_ability_name.humanize(capitalize: false) } + _('Closed this %{quick_action_target}.') % { quick_action_target: target_issuable_name } end types ::Issuable condition do @@ -35,15 +32,15 @@ module Gitlab desc do _('Reopen this %{quick_action_target}') % - { quick_action_target: quick_action_target.to_ability_name.humanize(capitalize: false) } + { quick_action_target: target_issuable_name } end explanation do _('Reopens this %{quick_action_target}.') % - { quick_action_target: quick_action_target.to_ability_name.humanize(capitalize: false) } + { quick_action_target: target_issuable_name } end execution_message do _('Reopened this %{quick_action_target}.') % - { quick_action_target: quick_action_target.to_ability_name.humanize(capitalize: false) } + { quick_action_target: target_issuable_name } end types ::Issuable condition do @@ -170,12 +167,10 @@ module Gitlab desc { _('Subscribe') } explanation do - _('Subscribes to this %{quick_action_target}.') % - { quick_action_target: quick_action_target.to_ability_name.humanize(capitalize: false) } + _('Subscribes to this %{quick_action_target}.') % { quick_action_target: target_issuable_name } end execution_message do - _('Subscribed to this %{quick_action_target}.') % - { quick_action_target: quick_action_target.to_ability_name.humanize(capitalize: false) } + _('Subscribed to this %{quick_action_target}.') % { quick_action_target: target_issuable_name } end types ::Issuable condition do @@ -188,12 +183,10 @@ module Gitlab desc { _('Unsubscribe') } explanation do - _('Unsubscribes from this %{quick_action_target}.') % - { quick_action_target: quick_action_target.to_ability_name.humanize(capitalize: false) } + _('Unsubscribes from this %{quick_action_target}.') % { quick_action_target: target_issuable_name } end execution_message do - _('Unsubscribed from this %{quick_action_target}.') % - { quick_action_target: quick_action_target.to_ability_name.humanize(capitalize: false) } + _('Unsubscribed from this %{quick_action_target}.') % { quick_action_target: target_issuable_name } end types ::Issuable condition do @@ -266,6 +259,16 @@ module Gitlab end end + desc { _("Make %{type} confidential") % { type: target_issuable_name } } + explanation { _("Makes this %{type} confidential.") % { type: target_issuable_name } } + types ::Issuable + condition { quick_action_target.supports_confidentiality? && can_make_confidential? } + command :confidential do + @updates[:confidential] = true + + @execution_message[:confidential] = confidential_execution_message + end + private def find_severity(severity_param) @@ -315,6 +318,29 @@ module Gitlab _('Removed all labels.') end end + + def target_issuable_name + quick_action_target.to_ability_name.humanize(capitalize: false) + end + + def can_make_confidential? + confidentiality_not_supported = quick_action_target.respond_to?(:issue_type_supports?) && + !quick_action_target.issue_type_supports?(:confidentiality) + + return false if confidentiality_not_supported + + !quick_action_target.confidential? && current_user.can?(:set_confidentiality, quick_action_target) + end + + def confidential_execution_message + confidential_error_message.presence || _("Made this %{type} confidential.") % { type: target_issuable_name } + end + + def confidential_error_message + return unless quick_action_target.respond_to?(:confidentiality_errors) + + quick_action_target.confidentiality_errors.join("\n") + end end end end diff --git a/lib/gitlab/quick_actions/issue_actions.rb b/lib/gitlab/quick_actions/issue_actions.rb index 4883c649a62..e74c58e45b1 100644 --- a/lib/gitlab/quick_actions/issue_actions.rb +++ b/lib/gitlab/quick_actions/issue_actions.rb @@ -161,23 +161,6 @@ module Gitlab @execution_message[:move] = message end - desc { _('Make issue confidential') } - explanation do - _('Makes this issue confidential.') - end - execution_message do - _('Made this issue confidential.') - end - types Issue - condition do - quick_action_target.issue_type_supports?(:confidentiality) && - !quick_action_target.confidential? && - current_user.can?(:set_confidentiality, quick_action_target) - end - command :confidential do - @updates[:confidential] = true - end - desc { _('Create a merge request') } explanation do |branch_name = nil| if branch_name diff --git a/lib/gitlab/sidekiq_middleware/size_limiter/compressor.rb b/lib/gitlab/sidekiq_middleware/size_limiter/compressor.rb index bce295d8ba5..f7e0553e536 100644 --- a/lib/gitlab/sidekiq_middleware/size_limiter/compressor.rb +++ b/lib/gitlab/sidekiq_middleware/size_limiter/compressor.rb @@ -33,7 +33,7 @@ module Gitlab validate_args!(job) job.except!(ORIGINAL_SIZE_KEY, COMPRESSED_KEY) - job['args'] = Sidekiq.load_json(Zlib::Inflate.inflate(Base64.strict_decode64(job['args'].first))) + job['args'] = Gitlab::Json.load(Zlib::Inflate.inflate(Base64.strict_decode64(job['args'].first))) rescue Zlib::Error raise PayloadDecompressionError, 'Fail to decompress Sidekiq job payload' end diff --git a/lib/gitlab/sidekiq_migrate_jobs.rb b/lib/gitlab/sidekiq_migrate_jobs.rb index 8c1153d6112..9811e1d53d2 100644 --- a/lib/gitlab/sidekiq_migrate_jobs.rb +++ b/lib/gitlab/sidekiq_migrate_jobs.rb @@ -34,7 +34,7 @@ module Gitlab next unless job.match?(source_queues_regex) - job_hash = Sidekiq.load_json(job) + job_hash = Gitlab::Json.load(job) destination_queue = mappings[job_hash['class']] next unless mappings.has_key?(job_hash['class']) @@ -77,12 +77,12 @@ module Gitlab end job = conn.rpop "queue:#{queue_from}" - job_hash = Sidekiq.load_json job + job_hash = Gitlab::Json.load(job) next unless mappings.has_key?(job_hash['class']) destination_queue = mappings[job_hash['class']] job_hash['queue'] = destination_queue - conn.lpush("queue:#{destination_queue}", Sidekiq.dump_json(job_hash)) + conn.lpush("queue:#{destination_queue}", Gitlab::Json.dump(job_hash)) migrated += 1 rescue JSON::ParserError logger&.error("Unmarshal JSON payload from SidekiqMigrateJobs failed. Job: #{job}") @@ -101,7 +101,7 @@ module Gitlab removed = connection.zrem(sidekiq_set, job) if removed - connection.zadd(sidekiq_set, score, Sidekiq.dump_json(job_hash)) + connection.zadd(sidekiq_set, score, Gitlab::Json.dump(job_hash)) 1 else diff --git a/lib/gitlab/sql/pattern.rb b/lib/gitlab/sql/pattern.rb index ca7ae429986..0a8fb04dfc6 100644 --- a/lib/gitlab/sql/pattern.rb +++ b/lib/gitlab/sql/pattern.rb @@ -6,7 +6,7 @@ module Gitlab extend ActiveSupport::Concern MIN_CHARS_FOR_PARTIAL_MATCHING = 3 - REGEX_QUOTED_WORD = /(?<=\A| )"[^"]+"(?= |\z)/.freeze + REGEX_QUOTED_TERM = /(?<=\A| )"[^"]+"(?= |\z)/.freeze class_methods do def fuzzy_search(query, columns, use_minimum_char_limit: true) @@ -45,7 +45,7 @@ module Gitlab arel_column = column.is_a?(Arel::Attributes::Attribute) ? column : arel_table[column] - words = select_fuzzy_words(query, use_minimum_char_limit: use_minimum_char_limit) + words = select_fuzzy_terms(query, use_minimum_char_limit: use_minimum_char_limit) if words.any? words.map { |word| arel_column.matches(to_pattern(word, use_minimum_char_limit: use_minimum_char_limit)) }.reduce(:and) @@ -62,20 +62,22 @@ module Gitlab end end - def select_fuzzy_words(query, use_minimum_char_limit: true) - quoted_words = query.scan(REGEX_QUOTED_WORD) - - query = quoted_words.reduce(query) { |q, quoted_word| q.sub(quoted_word, '') } - - words = query.split - - quoted_words.map! { |quoted_word| quoted_word[1..-2] } - - words.concat(quoted_words) - - words.select { |word| partial_matching?(word, use_minimum_char_limit: use_minimum_char_limit) } + def select_fuzzy_terms(query, use_minimum_char_limit: true) + terms = Gitlab::SQL::Pattern.split_query_to_search_terms(query) + terms.select { |term| partial_matching?(term, use_minimum_char_limit: use_minimum_char_limit) } end end + + def self.split_query_to_search_terms(query) + quoted_terms = [] + + query = query.gsub(REGEX_QUOTED_TERM) do |quoted_term| + quoted_terms << quoted_term + "" + end + + query.split + quoted_terms.map { |quoted_term| quoted_term[1..-2] } + end end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 41e6d115263..7d9663108bf 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -24645,7 +24645,7 @@ msgstr "" msgid "MRDiff|Show full file" msgstr "" -msgid "Made this issue confidential." +msgid "Made this %{type} confidential." msgstr "" msgid "Mailgun" @@ -24666,6 +24666,9 @@ msgstr "" msgid "Maintenance mode" msgstr "" +msgid "Make %{type} confidential" +msgstr "" + msgid "Make adjustments to how your GitLab instance is set up." msgstr "" @@ -24675,9 +24678,6 @@ msgstr "" msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos." msgstr "" -msgid "Make issue confidential" -msgstr "" - msgid "Make sure you choose a strong, unique password." msgstr "" @@ -24687,7 +24687,7 @@ msgstr "" msgid "Make sure you save it - you won't be able to access it again." msgstr "" -msgid "Makes this issue confidential." +msgid "Makes this %{type} confidential." msgstr "" msgid "Manage %{workspace} labels" diff --git a/package.json b/package.json index 3e4f06437a5..363c5bd3763 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "@gitlab/at.js": "1.5.7", "@gitlab/favicon-overlay": "2.0.0", "@gitlab/svgs": "3.5.0", - "@gitlab/ui": "49.0.2", + "@gitlab/ui": "49.2.0", "@gitlab/visual-review-tools": "1.7.3", "@gitlab/web-ide": "0.0.1-dev-20220815034418", "@rails/actioncable": "6.1.4-7", diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb index 6e751100096..2f177d12389 100644 --- a/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb +++ b/qa/qa/specs/features/browser_ui/2_plan/issue/check_mentions_for_xss_spec.rb @@ -5,7 +5,7 @@ module QA let!(:user) do Resource::User.fabricate_via_api! do |user| user.name = "QA User %w[repository_storages_weighted], "AlertManagement::Alert" => %w[payload], "Ci::BuildMetadata" => %w[config_options config_variables], + "Ci::BuildMetadata::Partitioned" => %w[config_options config_variables id_tokens runtime_runner_features secrets], "ExperimentSubject" => %w[context], "ExperimentUser" => %w[context], "Geo::Event" => %w[payload], diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb index 1190b0f3558..eabbcd5e38e 100644 --- a/spec/features/projects/pipelines/pipelines_spec.rb +++ b/spec/features/projects/pipelines/pipelines_spec.rb @@ -342,11 +342,18 @@ RSpec.describe 'Pipelines', :js do end context 'when user played a delayed job immediately' do + let(:manual_action_selector) { '[data-testid="pipelines-manual-actions-dropdown"]' } + before do - find('[data-testid="pipelines-manual-actions-dropdown"]').click + find(manual_action_selector).click accept_gl_confirm do click_button 'delayed job 1' end + + # Wait for UI to transition to ensure a request has been made + within(manual_action_selector) { find('.gl-spinner') } + within(manual_action_selector) { find('[data-testid="play-icon"]') } + wait_for_requests end diff --git a/spec/frontend/vue_merge_request_widget/components/widget/__snapshots__/dynamic_content_spec.js.snap b/spec/frontend/vue_merge_request_widget/components/widget/__snapshots__/dynamic_content_spec.js.snap index 08424077269..41220aaf306 100644 --- a/spec/frontend/vue_merge_request_widget/components/widget/__snapshots__/dynamic_content_spec.js.snap +++ b/spec/frontend/vue_merge_request_widget/components/widget/__snapshots__/dynamic_content_spec.js.snap @@ -7,7 +7,7 @@ exports[`~/vue_merge_request_widget/components/widget/dynamic_content.vue render

Main text for the row

Optional link to display after text - + Badge is optional. Text to be displayed inside badge diff --git a/spec/lib/gitlab/sidekiq_migrate_jobs_spec.rb b/spec/lib/gitlab/sidekiq_migrate_jobs_spec.rb index e5c3ea5c27b..db6ae1fc45a 100644 --- a/spec/lib/gitlab/sidekiq_migrate_jobs_spec.rb +++ b/spec/lib/gitlab/sidekiq_migrate_jobs_spec.rb @@ -22,7 +22,7 @@ RSpec.describe Gitlab::SidekiqMigrateJobs, :clean_gitlab_redis_queues do let(:set_after) do Sidekiq.redis { |c| c.zrange(set_name, 0, -1, with_scores: true) } - .map { |item, score| [Sidekiq.load_json(item), score] } + .map { |item, score| [Gitlab::Json.load(item), score] } end context 'when the set is empty' do @@ -233,7 +233,7 @@ RSpec.describe Gitlab::SidekiqMigrateJobs, :clean_gitlab_redis_queues do def list_jobs(queue_name) Sidekiq.redis { |conn| conn.lrange("queue:#{queue_name}", 0, -1) } - .map { |item| Sidekiq.load_json item } + .map { |item| Gitlab::Json.load(item) } end def pre_migrate_checks; end diff --git a/spec/lib/gitlab/sql/pattern_spec.rb b/spec/lib/gitlab/sql/pattern_spec.rb index c12cdab0b9b..dd33fc9d764 100644 --- a/spec/lib/gitlab/sql/pattern_spec.rb +++ b/spec/lib/gitlab/sql/pattern_spec.rb @@ -104,14 +104,14 @@ RSpec.describe Gitlab::SQL::Pattern do end end - describe '.select_fuzzy_words' do - subject(:select_fuzzy_words) { Issue.select_fuzzy_words(query) } + describe '.select_fuzzy_terms' do + subject(:select_fuzzy_terms) { Issue.select_fuzzy_terms(query) } context 'with a word equal to 3 chars' do let(:query) { 'foo' } it 'returns array containing a word' do - expect(select_fuzzy_words).to match_array(['foo']) + expect(select_fuzzy_terms).to match_array(['foo']) end end @@ -119,7 +119,7 @@ RSpec.describe Gitlab::SQL::Pattern do let(:query) { 'fo' } it 'returns empty array' do - expect(select_fuzzy_words).to match_array([]) + expect(select_fuzzy_terms).to match_array([]) end end @@ -127,7 +127,7 @@ RSpec.describe Gitlab::SQL::Pattern do let(:query) { 'foo baz' } it 'returns array containing two words' do - expect(select_fuzzy_words).to match_array(%w[foo baz]) + expect(select_fuzzy_terms).to match_array(%w[foo baz]) end end @@ -135,7 +135,7 @@ RSpec.describe Gitlab::SQL::Pattern do let(:query) { 'foo baz' } it 'returns array containing two words' do - expect(select_fuzzy_words).to match_array(%w[foo baz]) + expect(select_fuzzy_terms).to match_array(%w[foo baz]) end end @@ -143,7 +143,19 @@ RSpec.describe Gitlab::SQL::Pattern do let(:query) { 'foo ba' } it 'returns array containing a word' do - expect(select_fuzzy_words).to match_array(['foo']) + expect(select_fuzzy_terms).to match_array(['foo']) + end + end + end + + describe '.split_query_to_search_terms' do + subject(:split_query_to_search_terms) { described_class.split_query_to_search_terms(query) } + + context 'with words separated by spaces' do + let(:query) { 'really bar baz' } + + it 'returns array containing individual words' do + expect(split_query_to_search_terms).to match_array(%w[really bar baz]) end end @@ -151,15 +163,15 @@ RSpec.describe Gitlab::SQL::Pattern do let(:query) { '"really bar"' } it 'returns array containing a multi-word' do - expect(select_fuzzy_words).to match_array(['really bar']) + expect(split_query_to_search_terms).to match_array(['really bar']) end end context 'with a multi-word surrounded by double quote and two words' do let(:query) { 'foo "really bar" baz' } - it 'returns array containing a multi-word and tow words' do - expect(select_fuzzy_words).to match_array(['foo', 'really bar', 'baz']) + it 'returns array containing a multi-word and two words' do + expect(split_query_to_search_terms).to match_array(['foo', 'really bar', 'baz']) end end @@ -167,7 +179,7 @@ RSpec.describe Gitlab::SQL::Pattern do let(:query) { 'foo"really bar"' } it 'returns array containing two words with double quote' do - expect(select_fuzzy_words).to match_array(['foo"really', 'bar"']) + expect(split_query_to_search_terms).to match_array(['foo"really', 'bar"']) end end @@ -175,15 +187,15 @@ RSpec.describe Gitlab::SQL::Pattern do let(:query) { '"really bar"baz' } it 'returns array containing two words with double quote' do - expect(select_fuzzy_words).to match_array(['"really', 'bar"baz']) + expect(split_query_to_search_terms).to match_array(['"really', 'bar"baz']) end end context 'with two multi-word surrounded by double quote and two words' do let(:query) { 'foo "really bar" baz "awesome feature"' } - it 'returns array containing two multi-words and tow words' do - expect(select_fuzzy_words).to match_array(['foo', 'really bar', 'baz', 'awesome feature']) + it 'returns array containing two multi-words and two words' do + expect(split_query_to_search_terms).to match_array(['foo', 'really bar', 'baz', 'awesome feature']) end end end diff --git a/spec/migrations/delete_migrate_shared_vulnerability_scanners_spec.rb b/spec/migrations/delete_migrate_shared_vulnerability_scanners_spec.rb new file mode 100644 index 00000000000..259b175cd19 --- /dev/null +++ b/spec/migrations/delete_migrate_shared_vulnerability_scanners_spec.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require "spec_helper" + +require_migration! + +RSpec.describe DeleteMigrateSharedVulnerabilityScanners, :migration do + let(:batched_background_migrations) { table(:batched_background_migrations) } + let(:batched_background_migration_jobs) { table(:batched_background_migration_jobs) } + + let(:migration) do + batched_background_migrations.create!(created_at: Time.zone.now, + updated_at: Time.zone.now, + min_value: 1, + max_value: 1, + batch_size: described_class::BATCH_SIZE, + sub_batch_size: 100, + interval: 300, + status: 3, + job_class_name: described_class::MIGRATION, + batch_class_name: "PrimaryKeyBatchingStrategy", + table_name: described_class::TABLE_NAME, + column_name: described_class::BATCH_COLUMN, + job_arguments: [], + pause_ms: 100, + max_batch_size: 1000, + gitlab_schema: "gitlab_main") + end + + let(:jobs) do + Array.new(10) do + batched_background_migration_jobs.create!(batched_background_migration_id: migration.id, + created_at: Time.zone.now, + updated_at: Time.zone.now, + min_value: 1, + max_value: 1, + batch_size: 1, + sub_batch_size: 1, + status: 0, + attempts: 0, + metrics: {}, + pause_ms: 100) + end + end + + describe "#up" do + it "deletes jobs" do + expect { migrate! }.to change(batched_background_migration_jobs, :count).from(jobs.count).to(0) + end + + it "deletes the migration" do + expect { migrate! }.to change { batched_background_migrations.find_by(id: migration.id) }.from(migration).to(nil) + end + + context "when background migration does not exist" do + before do + migration.destroy! + end + + it "does not delete jobs" do + expect { migrate! }.not_to change(batched_background_migration_jobs, :count) + end + + it "does not delete the migration" do + expect { migrate! }.not_to change { batched_background_migrations.find_by(id: migration.id) } + end + end + end +end diff --git a/spec/migrations/reschedule_migrate_shared_vulnerability_scanners_spec.rb b/spec/migrations/reschedule_migrate_shared_vulnerability_scanners_spec.rb new file mode 100644 index 00000000000..e8253f39c68 --- /dev/null +++ b/spec/migrations/reschedule_migrate_shared_vulnerability_scanners_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require "spec_helper" + +require_migration! + +RSpec.describe RescheduleMigrateSharedVulnerabilityScanners, :migration do + include Gitlab::Database::Migrations::BatchedBackgroundMigrationHelpers + + def connection + ApplicationRecord.connection + end + + describe "#up" do + before do + migrate! + end + + it "schedules" do + expect(described_class::MIGRATION).to have_scheduled_batched_migration( + table_name: described_class::TABLE_NAME, + column_name: described_class::BATCH_COLUMN, + interval: described_class::DELAY_INTERVAL, + batch_size: described_class::BATCH_SIZE, + max_batch_size: described_class::BATCH_SIZE, + sub_batch_size: described_class::SUB_BATCH_SIZE, + gitlab_schema: :gitlab_main + ) + end + end + + describe '#down' do + before do + schema_migrate_down! + end + + it "deletes" do + expect(described_class::MIGRATION).not_to have_scheduled_batched_migration + end + end +end diff --git a/spec/migrations/schedule_migrate_shared_vulnerability_scanners_spec.rb b/spec/migrations/schedule_migrate_shared_vulnerability_scanners_spec.rb deleted file mode 100644 index f00d6568b67..00000000000 --- a/spec/migrations/schedule_migrate_shared_vulnerability_scanners_spec.rb +++ /dev/null @@ -1,59 +0,0 @@ -# frozen_string_literal: true - -require "spec_helper" - -require_migration! - -RSpec.describe ScheduleMigrateSharedVulnerabilityScanners, :migration do - describe "#up" do - before do - migrate! - end - - it "schedules" do - expect(described_class::MIGRATION).to have_scheduled_batched_migration( - table_name: described_class::TABLE_NAME, - column_name: described_class::BATCH_COLUMN, - interval: described_class::DELAY_INTERVAL, - batch_size: described_class::BATCH_SIZE, - max_batch_size: described_class::BATCH_SIZE, - sub_batch_size: described_class::SUB_BATCH_SIZE, - gitlab_schema: :gitlab_main - ) - end - - describe "ID range" do - let(:expected_range) do - { min_value: described_class::BATCH_MIN_VALUE, - max_value: described_class::BATCH_MAX_VALUE } - end - - subject do - Gitlab::Database::BackgroundMigration::BatchedMigration - .for_configuration(:gitlab_main, - described_class::MIGRATION, - described_class::TABLE_NAME, - described_class::BATCH_COLUMN, - []) - end - - it "is set" do - # The `have_scheduled_batched_migration` matcher accepts the - # `batch_min_value` and `batch_max_value` keywords. However the respective - # column names are `min_value` and `max_value`. Hence the matcher cannot - # be used in this case, as it asserts the wrong attributes. - expect(subject).to all(have_attributes(expected_range)) - end - end - end - - describe '#down' do - before do - schema_migrate_down! - end - - it "deletes" do - expect(described_class::MIGRATION).not_to have_scheduled_batched_migration - end - end -end diff --git a/spec/models/ci/build_metadata_spec.rb b/spec/models/ci/build_metadata_spec.rb index 16cff72db64..b4c20637ce2 100644 --- a/spec/models/ci/build_metadata_spec.rb +++ b/spec/models/ci/build_metadata_spec.rb @@ -182,4 +182,26 @@ RSpec.describe Ci::BuildMetadata do end end end + + describe 'routing table switch' do + context 'with ff disabled' do + before do + stub_feature_flags(ci_partitioning_use_ci_builds_metadata_routing_table: false) + end + + it 'uses the legacy table' do + expect(described_class.table_name).to eq('ci_builds_metadata') + end + end + + context 'with ff enabled' do + before do + stub_feature_flags(ci_partitioning_use_ci_builds_metadata_routing_table: true) + end + + it 'uses the routing table' do + expect(described_class.table_name).to eq('p_ci_builds_metadata') + end + end + end end diff --git a/spec/models/concerns/ci/partitionable/switch_spec.rb b/spec/models/concerns/ci/partitionable/switch_spec.rb new file mode 100644 index 00000000000..09005489268 --- /dev/null +++ b/spec/models/concerns/ci/partitionable/switch_spec.rb @@ -0,0 +1,294 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::Partitionable::Switch, :aggregate_failures do + let(:model) do + Class.new(Ci::ApplicationRecord) do + self.primary_key = :id + self.table_name = :_test_ci_jobs_metadata + self.sequence_name = :_test_ci_jobs_metadata_id_seq + + def self.name + 'TestSwitchJobMetadata' + end + end + end + + let(:table_rollout_flag) { :ci_partitioning_use_test_routing_table } + + let(:partitioned_model) { model::Partitioned } + + let(:jobs_model) do + Class.new(Ci::ApplicationRecord) do + self.primary_key = :id + self.table_name = :_test_ci_jobs + + def self.name + 'TestSwitchJob' + end + end + end + + before do + allow(ActiveSupport::DescendantsTracker).to receive(:store_inherited) + + create_tables(<<~SQL) + CREATE TABLE _test_ci_jobs_metadata( + id serial NOT NULL PRIMARY KEY, + job_id int, + partition_id int NOT NULL DEFAULT 1, + expanded_environment_name text); + + CREATE TABLE _test_p_ci_jobs_metadata ( + LIKE _test_ci_jobs_metadata INCLUDING DEFAULTS + ) PARTITION BY LIST(partition_id); + + ALTER TABLE _test_p_ci_jobs_metadata + ADD CONSTRAINT _test_p_ci_jobs_metadata_id_partition_id + UNIQUE (id, partition_id); + + ALTER TABLE _test_p_ci_jobs_metadata + ATTACH PARTITION _test_ci_jobs_metadata FOR VALUES IN (1); + + CREATE TABLE _test_ci_jobs(id serial NOT NULL PRIMARY KEY); + SQL + + stub_const('Ci::Partitionable::Testing::PARTITIONABLE_MODELS', [model.name]) + + model.include(Ci::Partitionable) + + model.partitionable scope: ->(r) { 1 }, + through: { table: :_test_p_ci_jobs_metadata, flag: table_rollout_flag } + + model.belongs_to :job, anonymous_class: jobs_model + + jobs_model.has_one :metadata, anonymous_class: model, + foreign_key: :job_id, inverse_of: :job, + dependent: :destroy + + allow(Feature::Definition).to receive(:get).and_call_original + allow(Feature::Definition).to receive(:get).with(table_rollout_flag) + .and_return( + Feature::Definition.new("development/#{table_rollout_flag}.yml", + { type: 'development', name: table_rollout_flag } + ) + ) + end + + it { expect(model).not_to be_routing_class } + + it { expect(partitioned_model).to be_routing_class } + + it { expect(partitioned_model.table_name).to eq('_test_p_ci_jobs_metadata') } + + it { expect(partitioned_model.quoted_table_name).to eq('"_test_p_ci_jobs_metadata"') } + + it { expect(partitioned_model.arel_table.name).to eq('_test_p_ci_jobs_metadata') } + + it { expect(partitioned_model.sequence_name).to eq('_test_ci_jobs_metadata_id_seq') } + + context 'when switching the tables' do + before do + stub_feature_flags(table_rollout_flag => false) + end + + %i[table_name quoted_table_name arel_table predicate_builder].each do |name| + it "switches #{name} to routing table and rollbacks" do + old_value = model.public_send(name) + routing_value = partitioned_model.public_send(name) + + expect(old_value).not_to eq(routing_value) + + expect { stub_feature_flags(table_rollout_flag => true) } + .to change(model, name).from(old_value).to(routing_value) + + expect { stub_feature_flags(table_rollout_flag => false) } + .to change(model, name).from(routing_value).to(old_value) + end + end + + it 'can switch aggregate methods' do + rollout_and_rollback_flag( + -> { expect(sql { model.count }).to all match(/FROM "_test_ci_jobs_metadata"/) }, + -> { expect(sql { model.count }).to all match(/FROM "_test_p_ci_jobs_metadata"/) } + ) + end + + it 'can switch reads' do + rollout_and_rollback_flag( + -> { expect(sql { model.last }).to all match(/FROM "_test_ci_jobs_metadata"/) }, + -> { expect(sql { model.last }).to all match(/FROM "_test_p_ci_jobs_metadata"/) } + ) + end + + it 'can switch inserts' do + rollout_and_rollback_flag( + -> { + expect(sql(filter: /INSERT/) { model.create! }) + .to all match(/INSERT INTO "_test_ci_jobs_metadata"/) + }, + -> { + expect(sql(filter: /INSERT/) { model.create! }) + .to all match(/INSERT INTO "_test_p_ci_jobs_metadata"/) + } + ) + end + + it 'can switch deletes' do + 3.times { model.create! } + + rollout_and_rollback_flag( + -> { + expect(sql(filter: /DELETE/) { model.last.destroy! }) + .to all match(/DELETE FROM "_test_ci_jobs_metadata"/) + }, + -> { + expect(sql(filter: /DELETE/) { model.last.destroy! }) + .to all match(/DELETE FROM "_test_p_ci_jobs_metadata"/) + } + ) + end + + context 'with associations' do + let(:job) { jobs_model.create! } + + it 'reads' do + model.create!(job_id: job.id) + + rollout_and_rollback_flag( + -> { + expect(sql(filter: /jobs_metadata/) { jobs_model.find(job.id).metadata }) + .to all match(/FROM "_test_ci_jobs_metadata"/) + }, + -> { + expect(sql(filter: /jobs_metadata/) { jobs_model.find(job.id).metadata }) + .to all match(/FROM "_test_p_ci_jobs_metadata"/) + } + ) + end + + it 'writes' do + rollout_and_rollback_flag( + -> { + expect(sql(filter: /INSERT .* jobs_metadata/) { jobs_model.find(job.id).create_metadata! }) + .to all match(/INSERT INTO "_test_ci_jobs_metadata"/) + }, + -> { + expect(sql(filter: /INSERT .* jobs_metadata/) { jobs_model.find(job.id).create_metadata! }) + .to all match(/INSERT INTO "_test_p_ci_jobs_metadata"/) + } + ) + end + + it 'deletes' do + 3.times do + job = jobs_model.create! + job.create_metadata! + end + + rollout_and_rollback_flag( + -> { + expect(sql(filter: /DELETE .* jobs_metadata/) { jobs_model.last.destroy! }) + .to all match(/DELETE FROM "_test_ci_jobs_metadata"/) + }, + -> { + expect(sql(filter: /DELETE .* jobs_metadata/) { jobs_model.last.destroy! }) + .to all match(/DELETE FROM "_test_p_ci_jobs_metadata"/) + } + ) + end + + it 'can switch joins from jobs' do + rollout_and_rollback_flag( + -> { + expect(sql { jobs_model.joins(:metadata).last }) + .to all match(/INNER JOIN "_test_ci_jobs_metadata"/) + }, + -> { + expect(sql { jobs_model.joins(:metadata).last }) + .to all match(/INNER JOIN "_test_p_ci_jobs_metadata"/) + } + ) + end + + it 'can switch joins from metadata' do + rollout_and_rollback_flag( + -> { + expect(sql { model.joins(:job).last }) + .to all match(/FROM "_test_ci_jobs_metadata" INNER JOIN "_test_ci_jobs"/) + }, + -> { + expect(sql { model.joins(:job).last }) + .to all match(/FROM "_test_p_ci_jobs_metadata" INNER JOIN "_test_ci_jobs"/) + } + ) + end + + it 'preloads' do + job = jobs_model.create! + job.create_metadata! + + rollout_and_rollback_flag( + -> { + expect(sql(filter: /jobs_metadata/) { jobs_model.preload(:metadata).last }) + .to all match(/FROM "_test_ci_jobs_metadata"/) + }, + -> { + expect(sql(filter: /jobs_metadata/) { jobs_model.preload(:metadata).last }) + .to all match(/FROM "_test_p_ci_jobs_metadata"/) + } + ) + end + + context 'with nested attributes' do + before do + jobs_model.accepts_nested_attributes_for :metadata + end + + it 'writes' do + attrs = { metadata_attributes: { expanded_environment_name: 'test_env_name' } } + + rollout_and_rollback_flag( + -> { + expect(sql(filter: /INSERT .* jobs_metadata/) { jobs_model.create!(attrs) }) + .to all match(/INSERT INTO "_test_ci_jobs_metadata" .* 'test_env_name'/) + }, + -> { + expect(sql(filter: /INSERT .* jobs_metadata/) { jobs_model.create!(attrs) }) + .to all match(/INSERT INTO "_test_p_ci_jobs_metadata" .* 'test_env_name'/) + } + ) + end + end + end + end + + def rollout_and_rollback_flag(old, new) + # Load class and SQL statements cache + old.call + + stub_feature_flags(table_rollout_flag => true) + + # Test switch + new.call + + stub_feature_flags(table_rollout_flag => false) + + # Test that it can switch back in the same process + old.call + end + + def create_tables(table_sql) + Ci::ApplicationRecord.connection.execute(table_sql) + end + + def sql(filter: nil, &block) + result = ActiveRecord::QueryRecorder.new(&block) + result = result.log + + return result unless filter + + result.select { |statement| statement.match?(filter) } + end +end diff --git a/spec/models/concerns/ci/partitionable_spec.rb b/spec/models/concerns/ci/partitionable_spec.rb index d53501ccc3d..f3d33c971c7 100644 --- a/spec/models/concerns/ci/partitionable_spec.rb +++ b/spec/models/concerns/ci/partitionable_spec.rb @@ -3,9 +3,9 @@ require 'spec_helper' RSpec.describe Ci::Partitionable do - describe 'partitionable models inclusion' do - let(:ci_model) { Class.new(Ci::ApplicationRecord) } + let(:ci_model) { Class.new(Ci::ApplicationRecord) } + describe 'partitionable models inclusion' do subject { ci_model.include(described_class) } it 'raises an exception' do @@ -23,4 +23,21 @@ RSpec.describe Ci::Partitionable do end end end + + context 'with through options' do + before do + allow(ActiveSupport::DescendantsTracker).to receive(:store_inherited) + stub_const("#{described_class}::Testing::PARTITIONABLE_MODELS", [ci_model.name]) + + ci_model.include(described_class) + ci_model.partitionable scope: ->(r) { 1 }, + through: { table: :_test_table_name, flag: :some_flag } + end + + it { expect(ci_model.routing_table_name).to eq(:_test_table_name) } + + it { expect(ci_model.routing_table_name_flag).to eq(:some_flag) } + + it { expect(ci_model.ancestors).to include(described_class::Switch) } + end end diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index 8842a36f40a..43ec0559eb3 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -1055,6 +1055,22 @@ RSpec.describe Issuable do end end + describe '#supports_confidentiality?' do + where(:issuable_type, :supports_confidentiality) do + :issue | true + :incident | true + :merge_request | false + end + + with_them do + let(:issuable) { build_stubbed(issuable_type) } + + subject { issuable.supports_confidentiality? } + + it { is_expected.to eq(supports_confidentiality) } + end + end + describe '#severity' do subject { issuable.severity } diff --git a/spec/models/concerns/pg_full_text_searchable_spec.rb b/spec/models/concerns/pg_full_text_searchable_spec.rb index 3e42a3504ac..5a693f084e6 100644 --- a/spec/models/concerns/pg_full_text_searchable_spec.rb +++ b/spec/models/concerns/pg_full_text_searchable_spec.rb @@ -76,7 +76,7 @@ RSpec.describe PgFullTextSearchable do end describe '.pg_full_text_search' do - let(:english) { model_class.create!(project: project, title: 'title', description: 'something english') } + let(:english) { model_class.create!(project: project, title: 'title', description: 'something description english') } let(:with_accent) { model_class.create!(project: project, title: 'Jürgen', description: 'Ærøskøbing') } let(:japanese) { model_class.create!(project: project, title: '日本語 title', description: 'another english description') } @@ -90,8 +90,19 @@ RSpec.describe PgFullTextSearchable do expect(model_class.pg_full_text_search('title english')).to contain_exactly(english, japanese) end + it 'searches specified columns only' do + matching_object = model_class.create!(project: project, title: 'english', description: 'some description') + matching_object.update_search_data! + + expect(model_class.pg_full_text_search('english', matched_columns: %w(title))).to contain_exactly(matching_object) + end + + it 'uses prefix matching' do + expect(model_class.pg_full_text_search('tit eng')).to contain_exactly(english, japanese) + end + it 'searches for exact term with quotes' do - expect(model_class.pg_full_text_search('"something english"')).to contain_exactly(english) + expect(model_class.pg_full_text_search('"description english"')).to contain_exactly(english) end it 'ignores accents' do @@ -113,6 +124,16 @@ RSpec.describe PgFullTextSearchable do expect(model_class.pg_full_text_search('gopher://gitlab.com/gitlab-org/gitlab')).to contain_exactly(with_url) end end + + context 'when text has numbers preceded by a dash' do + let(:with_dash) { model_class.create!(project: project, title: 'issue with dash', description: 'ABC-123') } + + it 'allows searching by numbers only' do + with_dash.update_search_data! + + expect(model_class.pg_full_text_search('123')).to contain_exactly(with_dash) + end + end end describe '#update_search_data!' do diff --git a/spec/rubocop/cop/gitlab/mark_used_feature_flags_spec.rb b/spec/rubocop/cop/gitlab/mark_used_feature_flags_spec.rb index a3c9ae8916e..6e60889f737 100644 --- a/spec/rubocop/cop/gitlab/mark_used_feature_flags_spec.rb +++ b/spec/rubocop/cop/gitlab/mark_used_feature_flags_spec.rb @@ -194,6 +194,10 @@ RSpec.describe RuboCop::Cop::Gitlab::MarkUsedFeatureFlags do include_examples 'sets flag as used', 'FEATURE_FLAG = :foo', 'foo' end + describe 'ROUTING_FEATURE_FLAG = :foo' do + include_examples 'sets flag as used', 'ROUTING_FEATURE_FLAG = :foo', 'foo' + end + describe 'Worker `data_consistency` method' do include_examples 'sets flag as used', 'data_consistency :delayed, feature_flag: :foo', 'foo' include_examples 'does not set any flags as used', 'data_consistency :delayed' diff --git a/spec/support/shared_examples/finders/issues_finder_shared_examples.rb b/spec/support/shared_examples/finders/issues_finder_shared_examples.rb index f62c9c00006..8b3a344a841 100644 --- a/spec/support/shared_examples/finders/issues_finder_shared_examples.rb +++ b/spec/support/shared_examples/finders/issues_finder_shared_examples.rb @@ -585,7 +585,7 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context end context 'when full-text search is disabled' do - let(:search_term) { 'somet' } + let(:search_term) { 'ometh' } before do stub_feature_flags(issues_full_text_search: false) diff --git a/spec/workers/namespaces/root_statistics_worker_spec.rb b/spec/workers/namespaces/root_statistics_worker_spec.rb index 7b774da0bdc..f28a0815025 100644 --- a/spec/workers/namespaces/root_statistics_worker_spec.rb +++ b/spec/workers/namespaces/root_statistics_worker_spec.rb @@ -89,4 +89,12 @@ RSpec.describe Namespaces::RootStatisticsWorker, '#perform' do .not_to change { Namespace::AggregationSchedule.count } end end + + it 'has the `until_executed` deduplicate strategy' do + expect(described_class.get_deduplicate_strategy).to eq(:until_executed) + end + + it 'has an option to reschedule once if deduplicated' do + expect(described_class.get_deduplication_options).to include({ if_deduplicated: :reschedule_once }) + end end diff --git a/test.html b/test.html new file mode 100644 index 00000000000..9c508e519c5 --- /dev/null +++ b/test.html @@ -0,0 +1,439 @@ + + + + +term · Search · GitLab + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+
+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+
+
+ + + +
+

Search

+ +
+
+
+
+ + + + +
+
+
+ + + +
+
+ + +
+
+ + +
+
+
+
+
+
+
+ + + + + + + + + diff --git a/yarn.lock b/yarn.lock index b6dd915d203..2f7bc645901 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1113,10 +1113,10 @@ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.5.0.tgz#226240b7aa93db986f4c6f7738ca2a1846b5234d" integrity sha512-/djPsJzUY7i/FaydRVt3ZyXiFf5HGNo1rg2mfLn1EpXvT4zc2ag5ECwnYcPb97KgqFCJX6Tk+Ndu8Wh3GoOW1g== -"@gitlab/ui@49.0.2": - version "49.0.2" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-49.0.2.tgz#4d545fdb4165cf961176fb8972fb9bbfe6b15f1f" - integrity sha512-hleFBhGbNDItQVe/EavY8K1sAaeTsYZeOb2Z9+W1xafqOlruxOnM1fW3/Rjn9d2dEA8kNCwav1yTwH72wUE4rg== +"@gitlab/ui@49.2.0": + version "49.2.0" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-49.2.0.tgz#45eedbe943bccbb6d986d66bf7c6294c82e89366" + integrity sha512-S7jfYtmh2Z36bum48aqb+NFLl/WAqow5gOXfWjdl1lGXjpKZ27neJPTWfpYi2PRyhmPs8ptVg7zKaxXJMZ7cgA== dependencies: "@popperjs/core" "^2.11.2" bootstrap-vue "2.20.1"