Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-10-28 18:10:48 +00:00
parent 8966e39395
commit 0076bbc673
59 changed files with 1439 additions and 228 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -217,6 +217,10 @@ module Issuable
false
end
def supports_confidentiality?
false
end
def severity
return IssuableSeverity::DEFAULT unless supports_severity?

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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: '应用被撤销访问权限。'

View File

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

View File

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

View File

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

View File

@ -0,0 +1 @@
9a5ba202075e0022defd834184aa59c60980cdccf7f4111834af6a119713b4c2

View File

@ -0,0 +1 @@
d5883d3edad5d8cc130f26feb4cc6fdb63e3b46c513ce463bdf7e45a8d7ffcdf

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@ -74,6 +74,21 @@ To create a timeline event from a comment on the incident:
The comment is shown on the incident timeline as a timeline event.
### When incident severity changes
> [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.

View File

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

View File

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

View File

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

View File

@ -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 <path/to/project> [--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 <!merge_request>` | **{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 <branch name>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Create a new merge request starting from the current issue. |

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,7 +5,7 @@ module QA
let!(:user) do
Resource::User.fabricate_via_api! do |user|
user.name = "QA User <img src=x onerror=alert(2)&lt;img src=x onerror=alert(1)&gt;"
user.password = "test1234"
user.password = "pw_#{SecureRandom.hex(12)}"
user.api_client = Runtime::API::Client.as_admin
end
end

View File

@ -59,7 +59,7 @@ module RuboCop
def on_casgn(node)
_, lhs_name, rhs = *node
save_used_feature_flag(rhs.value) if lhs_name == :FEATURE_FLAG
save_used_feature_flag(rhs.value) if lhs_name.to_s.end_with?('FEATURE_FLAG')
end
def on_send(node)

View File

@ -23,12 +23,12 @@ gitlab:
resources:
requests:
cpu: 1200m
memory: 600M
memory: 600Mi
limits:
cpu: 1800m
memory: 1000M
memory: 1000Mi
persistence:
size: 10G
size: 10Gi
storageClass: ssd
nodeSelector:
preemptible: "false"
@ -42,18 +42,18 @@ gitlab:
resources:
requests:
cpu: 200m
memory: 450M
memory: 450Mi
limits:
cpu: 400m
memory: 900M
memory: 900Mi
gitlab-shell:
resources:
requests:
cpu: 500m
memory: 100M
memory: 100Mi
limits:
cpu: 750m
memory: 150M
memory: 150Mi
maxReplicas: 3
hpa:
targetAverageValue: 500m
@ -64,28 +64,28 @@ gitlab:
resources:
requests:
cpu: 855m
memory: 1927M
memory: 1927Mi
limits:
cpu: 1282m
memory: 2890M
memory: 2890Mi
hpa:
targetAverageValue: 650m
toolbox:
resources:
requests:
cpu: 300m
memory: 1927M
memory: 1927Mi
limits:
cpu: 450m
memory: 2890M
memory: 2890Mi
webservice:
resources:
requests:
cpu: 746m
memory: 2809M
memory: 2809Mi
limits:
cpu: 1119m
memory: 4214M
memory: 4214Mi
deployment:
readinessProbe:
initialDelaySeconds: 5 # Default is 0
@ -95,10 +95,10 @@ gitlab:
resources:
requests:
cpu: 400m
memory: 75M
memory: 75Mi
limits:
cpu: 600m
memory: 113M
memory: 113Mi
readinessProbe:
initialDelaySeconds: 5 # Default is 0
periodSeconds: 15 # Default is 10
@ -107,10 +107,10 @@ gitlab-runner:
resources:
requests:
cpu: 675m
memory: 100M
memory: 100Mi
limits:
cpu: 1015m
memory: 150M
memory: 150Mi
nodeSelector:
preemptible: "true"
podAnnotations:
@ -119,10 +119,10 @@ minio:
resources:
requests:
cpu: 9m
memory: 128M
memory: 128Mi
limits:
cpu: 15m
memory: 280M
memory: 280Mi
nodeSelector:
preemptible: "true"
podAnnotations:
@ -134,10 +134,10 @@ nginx-ingress:
resources:
requests:
cpu: 300m
memory: 450M
memory: 450Mi
limits:
cpu: 600m
memory: 675M
memory: 675Mi
service:
enableHttp: false
livenessProbe:
@ -150,10 +150,10 @@ nginx-ingress:
resources:
requests:
cpu: 5m
memory: 12M
memory: 12Mi
limits:
cpu: 10m
memory: 24M
memory: 24Mi
nodeSelector:
preemptible: "true"
postgresql:
@ -162,10 +162,10 @@ postgresql:
resources:
requests:
cpu: 600m
memory: 1000M
memory: 1000Mi
limits:
cpu: 1300m
memory: 1600M
memory: 1600Mi
master:
nodeSelector:
preemptible: "false"
@ -179,10 +179,10 @@ redis:
resources:
requests:
cpu: 100m
memory: 60M
memory: 60Mi
limits:
cpu: 200m
memory: 130M
memory: 130Mi
master:
nodeSelector:
preemptible: "true"
@ -194,9 +194,9 @@ registry:
resources:
requests:
cpu: 100m
memory: 30M
memory: 30Mi
limits:
cpu: 200m
memory: 45M
memory: 45Mi
nodeSelector:
preemptible: "true"

View File

@ -214,6 +214,7 @@ RSpec.describe 'Database schema' do
"ApplicationSetting" => %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],

View File

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

View File

@ -7,7 +7,7 @@ exports[`~/vue_merge_request_widget/components/widget/dynamic_content.vue render
<p class=\\"gl-mb-0\\">Main text for the row</p>
<gl-link-stub href=\\"https://gitlab.com\\">Optional link to display after text</gl-link-stub>
<!---->
<gl-badge-stub size=\\"md\\" variant=\\"info\\">
<gl-badge-stub size=\\"md\\" variant=\\"info\\" iconsize=\\"md\\">
Badge is optional. Text to be displayed inside badge
</gl-badge-stub>
<actions-stub widget=\\"MyWidget\\" tertiarybuttons=\\"\\" class=\\"gl-ml-auto gl-pl-3\\"></actions-stub>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

439
test.html Normal file

File diff suppressed because one or more lines are too long

View File

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