summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-10-11 18:09:43 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-10-11 18:09:43 +0000
commit6e7be08ca5d6fac981284e7b1383b320a03d3a5d (patch)
tree5ae3da5b41f79107b86874c393a2799b7d1b1f3c
parent14ae125e1c59ca3e9b535938707831c986dbbc43 (diff)
downloadgitlab-ce-6e7be08ca5d6fac981284e7b1383b320a03d3a5d.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue6
-rw-r--r--app/assets/javascripts/sidebar/mount_sidebar.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/header.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/registry/registry_search.vue5
-rw-r--r--app/helpers/form_helper.rb17
-rw-r--r--app/helpers/wiki_helper.rb3
-rw-r--r--app/models/concerns/issuable.rb15
-rw-r--r--app/models/merge_request.rb14
-rw-r--r--app/models/project.rb5
-rw-r--r--app/services/merge_requests/update_assignees_service.rb12
-rw-r--r--app/services/notes/create_service.rb38
-rw-r--r--app/services/projects/container_repository/cleanup_tags_service.rb3
-rw-r--r--app/views/shared/issuable/_sidebar_assignees.html.haml2
-rw-r--r--app/views/shared/issuable/_sidebar_reviewers.html.haml2
-rw-r--r--config/feature_flags/development/limit_assignees_per_issuable.yml (renamed from config/feature_flags/development/container_registry_new_cleanup_service.yml)8
-rw-r--r--config/metrics/aggregates/common.yml62
-rw-r--r--config/metrics/counts_28d/20210216180511_incident_management_incidents_total_unique_counts.yml17
-rw-r--r--config/metrics/counts_28d/20220222215951_xmau_plan.yml9
-rw-r--r--config/metrics/counts_28d/20220222215952_xmau_project_management.yml9
-rw-r--r--config/metrics/counts_28d/20220222215955_users_work_items.yml9
-rw-r--r--config/metrics/counts_7d/20210216180515_incident_management_incidents_total_unique_counts.yml17
-rw-r--r--config/metrics/counts_7d/20220222215851_xmau_plan.yml11
-rw-r--r--config/metrics/counts_7d/20220222215852_xmau_project_management.yml9
-rw-r--r--config/metrics/counts_7d/20220222215855_users_work_items.yml9
-rw-r--r--config/sidekiq_queues.yml2
-rw-r--r--doc/api/graphql/reference/index.md1
-rw-r--r--doc/api/group_protected_environments.md4
-rw-r--r--doc/architecture/blueprints/rate_limiting/index.md2
-rw-r--r--doc/ci/environments/protected_environments.md6
-rw-r--r--doc/development/pipelines.md5
-rw-r--r--doc/development/sec/index.md2
-rw-r--r--doc/development/testing_guide/end_to_end/beginners_guide.md28
-rw-r--r--doc/development/value_stream_analytics/value_stream_analytics_aggregated_backend.md4
-rw-r--r--doc/development/work_items.md4
-rw-r--r--doc/subscriptions/gitlab_dedicated/index.md4
-rw-r--r--doc/user/application_security/get-started-security.md2
-rw-r--r--doc/user/application_security/img/secure_tools_and_cicd_stages.pngbin0 -> 42240 bytes
-rw-r--r--doc/user/application_security/index.md24
-rw-r--r--doc/user/application_security/policies/scan-execution-policies.md8
-rw-r--r--doc/user/group/import/index.md2
-rw-r--r--doc/user/group/saml_sso/scim_setup.md2
-rw-r--r--doc/user/group/settings/group_access_tokens.md2
-rw-r--r--doc/user/group/subgroups/index.md2
-rw-r--r--doc/user/workspace/index.md2
-rw-r--r--lib/gitlab/usage/metrics/instrumentations/work_items_activity_aggregated_metric.rb13
-rw-r--r--lib/tasks/gitlab/tw/codeowners.rake3
-rw-r--r--qa/qa/page/base.rb5
-rw-r--r--qa/qa/page/component/namespace_select.rb2
-rw-r--r--qa/qa/page/merge_request/new.rb1
-rw-r--r--qa/qa/page/merge_request/show.rb5
-rw-r--r--spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb4
-rw-r--r--spec/frontend/members/components/filter_sort/sort_dropdown_spec.js2
-rw-r--r--spec/helpers/form_helper_spec.rb68
-rw-r--r--spec/helpers/wiki_helper_spec.rb18
-rw-r--r--spec/lib/gitlab/usage/metrics/instrumentations/work_items_activity_aggregated_metric_spec.rb54
-rw-r--r--spec/models/concerns/issuable_spec.rb18
-rw-r--r--spec/models/project_spec.rb16
-rw-r--r--spec/services/merge_requests/update_assignees_service_spec.rb26
-rw-r--r--spec/services/projects/container_repository/cleanup_tags_service_spec.rb8
-rw-r--r--spec/support/shared_examples/quick_actions/issuable/max_issuable_examples.rb85
-rw-r--r--workhorse/go.mod2
-rw-r--r--workhorse/go.sum4
62 files changed, 550 insertions, 175 deletions
diff --git a/app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue b/app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue
index ce28283ccdf..01f145e0862 100644
--- a/app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue
+++ b/app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue
@@ -4,6 +4,7 @@ import { mapState } from 'vuex';
import { visitUrl } from '~/lib/utils/url_utility';
import { FIELDS } from '~/members/constants';
import { parseSortParam, buildSortHref } from '~/members/utils';
+import { SORT_DIRECTION_UI } from '~/search/sort/constants';
export default {
name: 'SortDropdown',
@@ -30,6 +31,9 @@ export default {
isAscending() {
return !this.sort.sortDesc;
},
+ sortDirectionData() {
+ return this.isAscending ? SORT_DIRECTION_UI.asc : SORT_DIRECTION_UI.desc;
+ },
filteredOptions() {
return FIELDS.filter(
(field) => this.tableSortableFields.includes(field.key) && field.sort,
@@ -70,7 +74,7 @@ export default {
data-testid="members-sort-dropdown"
:text="activeOptionLabel"
:is-ascending="isAscending"
- :sort-direction-tool-tip="__('Sort direction')"
+ :sort-direction-tool-tip="sortDirectionData.tooltip"
@sortDirectionChange="handleSortDirectionChange"
>
<gl-sorting-item
diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js
index 1cb3c30b9e0..9b5bad710dd 100644
--- a/app/assets/javascripts/sidebar/mount_sidebar.js
+++ b/app/assets/javascripts/sidebar/mount_sidebar.js
@@ -161,7 +161,7 @@ function mountAssigneesComponent() {
fullPath,
issuableType,
issuableId: id,
- allowMultipleAssignees: !el.dataset.maxAssignees,
+ allowMultipleAssignees: !el.dataset.maxAssignees || el.dataset.maxAssignees > 1,
editable,
},
scopedSlots: {
diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue
index 6019412b688..89fffdedbfd 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/header.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue
@@ -243,6 +243,7 @@ export default {
variant="confirm"
category="primary"
size="small"
+ data-qa-selector="dismiss_suggestion_popover_button"
@click="handleSuggestDismissed"
>
{{ __('Got it') }}
diff --git a/app/assets/javascripts/vue_shared/components/registry/registry_search.vue b/app/assets/javascripts/vue_shared/components/registry/registry_search.vue
index 1948a6778f4..8c9c7c63db1 100644
--- a/app/assets/javascripts/vue_shared/components/registry/registry_search.vue
+++ b/app/assets/javascripts/vue_shared/components/registry/registry_search.vue
@@ -1,6 +1,7 @@
<script>
import { GlSorting, GlSortingItem, GlFilteredSearch } from '@gitlab/ui';
import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants';
+import { SORT_DIRECTION_UI } from '~/search/sort/constants';
const ASCENDING_ORDER = 'asc';
const DESCENDING_ORDER = 'desc';
@@ -52,6 +53,9 @@ export default {
return acc;
}, {});
},
+ sortDirectionData() {
+ return this.isSortAscending ? SORT_DIRECTION_UI.asc : SORT_DIRECTION_UI.desc;
+ },
},
methods: {
generateQueryData({ sorting = {}, filter = [] } = {}) {
@@ -119,6 +123,7 @@ export default {
data-testid="registry-sort-dropdown"
:text="sortText"
:is-ascending="isSortAscending"
+ :sort-direction-tool-tip="sortDirectionData.tooltip"
@sortDirectionChange="onDirectionChange"
>
<gl-sorting-item
diff --git a/app/helpers/form_helper.rb b/app/helpers/form_helper.rb
index e8539f5997e..9e42aeea9ce 100644
--- a/app/helpers/form_helper.rb
+++ b/app/helpers/form_helper.rb
@@ -39,13 +39,13 @@ module FormHelper
end
end
- def dropdown_max_select(data)
- return data[:'max-select'] unless Feature.enabled?(:limit_reviewer_and_assignee_size)
+ def dropdown_max_select(data, feature_flag)
+ return data[:'max-select'] unless Feature.enabled?(feature_flag)
- if data[:'max-select'] && data[:'max-select'] < MergeRequest::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS
+ if data[:'max-select'] && data[:'max-select'] < ::Issuable::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS
data[:'max-select']
else
- MergeRequest::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS
+ ::Issuable::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS
end
end
@@ -162,7 +162,12 @@ module FormHelper
new_options[:title] = _('Select assignee(s)')
new_options[:data][:'dropdown-header'] = 'Assignee(s)'
- new_options[:data].delete(:'max-select')
+
+ if Feature.enabled?(:limit_assignees_per_issuable)
+ new_options[:data][:'max-select'] = ::Issuable::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS
+ else
+ new_options[:data].delete(:'max-select')
+ end
new_options
end
@@ -174,7 +179,7 @@ module FormHelper
new_options[:data][:'dropdown-header'] = _('Reviewer(s)')
if Feature.enabled?(:limit_reviewer_and_assignee_size)
- new_options[:data][:'max-select'] = MergeRequest::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS
+ new_options[:data][:'max-select'] = ::Issuable::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS
else
new_options[:data].delete(:'max-select')
end
diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb
index c42e3a5ff99..bded1b3a566 100644
--- a/app/helpers/wiki_helper.rb
+++ b/app/helpers/wiki_helper.rb
@@ -64,12 +64,13 @@ module WikiHelper
link_class = 'gl-button btn btn-default btn-icon has-tooltip reverse-sort-btn qa-reverse-sort rspec-reverse-sort'
reversed_direction = direction == 'desc' ? 'asc' : 'desc'
icon_class = direction == 'desc' ? 'highest' : 'lowest'
+ title = direction == 'desc' ? _('Sort direction: Descending') : _('Sort direction: Ascending')
link_options = { action: :pages, direction: reversed_direction }
link_options[:sort] = sort unless wiki.disable_sorting?
link_to(wiki_path(wiki, **link_options),
- type: 'button', class: link_class, title: _('Sort direction')) do
+ type: 'button', class: link_class, title: title) do
sprite_icon("sort-#{icon_class}")
end
end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 3a362f75c9c..f8389865f91 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -33,6 +33,7 @@ module Issuable
DESCRIPTION_LENGTH_MAX = 1.megabyte
DESCRIPTION_HTML_LENGTH_MAX = 5.megabytes
SEARCHABLE_FIELDS = %w(title description).freeze
+ MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS = 200
STATE_ID_MAP = {
opened: 1,
@@ -95,6 +96,7 @@ module Issuable
# to avoid breaking the existing Issuables which may have their descriptions longer
validates :description, length: { maximum: DESCRIPTION_LENGTH_MAX }, allow_blank: true, on: :create
validate :description_max_length_for_new_records_is_valid, on: :update
+ validate :validate_assignee_size_length, unless: :importing?
before_validation :truncate_description_on_import!
@@ -166,6 +168,11 @@ module Issuable
def locking_enabled?
false
end
+
+ def max_number_of_assignees_or_reviewers_message
+ # Assignees will be included in https://gitlab.com/gitlab-org/gitlab/-/issues/368936
+ format(_("total must be less than or equal to %{size}"), size: MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS)
+ end
end
# We want to use optimistic lock for cases when only title or description are involved
@@ -227,6 +234,14 @@ module Issuable
def truncate_description_on_import!
self.description = description&.slice(0, Issuable::DESCRIPTION_LENGTH_MAX) if importing?
end
+
+ def validate_assignee_size_length
+ return true unless Feature.enabled?(:limit_assignees_per_issuable)
+ return true unless assignees.size > MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS
+
+ errors.add :assignees,
+ -> (_object, _data) { self.class.max_number_of_assignees_or_reviewers_message }
+ end
end
class_methods do
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index ec791f91a3e..a3c1ef8bd79 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -41,8 +41,6 @@ class MergeRequest < ApplicationRecord
'Ci::CompareCodequalityReportsService' => ->(project) { true }
}.freeze
- MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS = 200
-
belongs_to :target_project, class_name: "Project"
belongs_to :source_project, class_name: "Project"
belongs_to :merge_user, class_name: "User"
@@ -294,7 +292,7 @@ class MergeRequest < ApplicationRecord
validate :validate_branches, unless: [:allow_broken, :importing?, :closed_or_merged_without_fork?]
validate :validate_fork, unless: :closed_or_merged_without_fork?
validate :validate_target_project, on: :create
- validate :validate_reviewer_and_assignee_size_length, unless: :importing?
+ validate :validate_reviewer_size_length, unless: :importing?
scope :by_source_or_target_branch, ->(branch_name) do
where("source_branch = :branch OR target_branch = :branch", branch: branch_name)
@@ -1014,18 +1012,12 @@ class MergeRequest < ApplicationRecord
'Source project is not a fork of the target project'
end
- def self.max_number_of_assignees_or_reviewers_message
- # Assignees will be included in https://gitlab.com/gitlab-org/gitlab/-/issues/368936
- _("total must be less than or equal to %{size}") % { size: MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS }
- end
-
- def validate_reviewer_and_assignee_size_length
- # Assigness will be added in a subsequent MR https://gitlab.com/gitlab-org/gitlab/-/issues/368936
+ def validate_reviewer_size_length
return true unless Feature.enabled?(:limit_reviewer_and_assignee_size)
return true unless reviewers.size > MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS
errors.add :reviewers,
- -> (_object, _data) { MergeRequest.max_number_of_assignees_or_reviewers_message }
+ -> (_object, _data) { self.class.max_number_of_assignees_or_reviewers_message }
end
def merge_ongoing?
diff --git a/app/models/project.rb b/app/models/project.rb
index 3d026351b4f..e6cc4d228f7 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -3040,6 +3040,11 @@ class Project < ApplicationRecord
end
# overridden in EE
+ def can_suggest_reviewers?
+ false
+ end
+
+ # overridden in EE
def suggested_reviewers_available?
false
end
diff --git a/app/services/merge_requests/update_assignees_service.rb b/app/services/merge_requests/update_assignees_service.rb
index a13db52e34b..79a3e9f3c22 100644
--- a/app/services/merge_requests/update_assignees_service.rb
+++ b/app/services/merge_requests/update_assignees_service.rb
@@ -18,7 +18,17 @@ module MergeRequests
return merge_request if old_ids.to_set == new_ids.to_set # no-change
attrs = update_attrs.merge(assignee_ids: new_ids)
- merge_request.update!(**attrs)
+
+ # We now have assignees validation on merge request
+ # If we use an update with bang, it will explode,
+ # instead we need to check if its valid then return if its not valid.
+ if Feature.enabled?(:limit_assignees_per_issuable)
+ merge_request.update(**attrs)
+
+ return merge_request unless merge_request.valid?
+ else
+ merge_request.update!(**attrs)
+ end
# Defer the more expensive operations (handle_assignee_changes) to the background
MergeRequests::HandleAssigneesChangeService
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index b7e6a50fa5c..1aaf7fb769a 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -88,12 +88,14 @@ module Notes
return if quick_actions_service.commands_executed_count.to_i == 0
if update_params.present?
- if check_for_reviewer_validity(message, update_params)
+ invalid_message = validate_commands(note, update_params)
+
+ if invalid_message
+ note.errors.add(:validation, invalid_message)
+ message = invalid_message
+ else
quick_actions_service.apply_updates(update_params, note)
note.commands_changes = update_params
- else
- message = "Reviewers #{MergeRequest.max_number_of_assignees_or_reviewers_message}"
- note.errors.add(:validation, message)
end
end
@@ -114,16 +116,36 @@ module Notes
}
end
- def check_for_reviewer_validity(message, update_params)
- return true unless Feature.enabled?(:limit_reviewer_and_assignee_size)
+ def validate_commands(note, update_params)
+ if invalid_reviewers?(update_params)
+ "Reviewers #{note.noteable.class.max_number_of_assignees_or_reviewers_message}"
+ elsif invalid_assignees?(update_params)
+ "Assignees #{note.noteable.class.max_number_of_assignees_or_reviewers_message}"
+ end
+ end
+
+ def invalid_reviewers?(update_params)
+ return false unless Feature.enabled?(:limit_reviewer_and_assignee_size)
if update_params.key?(:reviewer_ids)
possible_reviewers = update_params[:reviewer_ids]&.uniq&.size
- return false if possible_reviewers > MergeRequest::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS
+ possible_reviewers > ::Issuable::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS
+ else
+ false
end
+ end
+
+ def invalid_assignees?(update_params)
+ return false unless Feature.enabled?(:limit_assignees_per_issuable)
- true
+ if update_params.key?(:assignee_ids)
+ possible_assignees = update_params[:assignee_ids]&.uniq&.size
+
+ possible_assignees > ::Issuable::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS
+ else
+ false
+ end
end
def track_event(note, user)
diff --git a/app/services/projects/container_repository/cleanup_tags_service.rb b/app/services/projects/container_repository/cleanup_tags_service.rb
index 0b31ac0c877..cf2eb81e5f3 100644
--- a/app/services/projects/container_repository/cleanup_tags_service.rb
+++ b/app/services/projects/container_repository/cleanup_tags_service.rb
@@ -30,8 +30,7 @@ module Projects
end
def use_gitlab_service?
- Feature.enabled?(:container_registry_new_cleanup_service, project) &&
- container_repository.migrated? &&
+ container_repository.migrated? &&
container_repository.gitlab_api_client.supports_gitlab_api?
end
diff --git a/app/views/shared/issuable/_sidebar_assignees.html.haml b/app/views/shared/issuable/_sidebar_assignees.html.haml
index e9b04579808..62221fb8218 100644
--- a/app/views/shared/issuable/_sidebar_assignees.html.haml
+++ b/app/views/shared/issuable/_sidebar_assignees.html.haml
@@ -39,7 +39,7 @@
- data[:multi_select] = true
- data['dropdown-title'] = title
- data['dropdown-header'] = dropdown_options[:data][:'dropdown-header']
- - data['max-select'] = dropdown_options[:data][:'max-select'] if dropdown_options[:data][:'max-select']
+ - data['max-select'] = dropdown_max_select(dropdown_options[:data], :limit_assignees_per_issuable)
- options[:data].merge!(data)
= render 'shared/issuable/sidebar_user_dropdown',
diff --git a/app/views/shared/issuable/_sidebar_reviewers.html.haml b/app/views/shared/issuable/_sidebar_reviewers.html.haml
index 89efd5c11d1..771db8af6a8 100644
--- a/app/views/shared/issuable/_sidebar_reviewers.html.haml
+++ b/app/views/shared/issuable/_sidebar_reviewers.html.haml
@@ -39,7 +39,7 @@
- data[:suggested_reviewers_header] = dropdown_options[:data][:suggested_reviewers_header]
- data[:all_members_header] = dropdown_options[:data][:all_members_header]
- data[:show_suggested] = dropdown_options[:data][:show_suggested]
- - data['max-select'] = dropdown_max_select(dropdown_options[:data])
+ - data['max-select'] = dropdown_max_select(dropdown_options[:data], :limit_reviewer_and_assignee_size)
- options[:data].merge!(data)
= render 'shared/issuable/sidebar_user_dropdown',
diff --git a/config/feature_flags/development/container_registry_new_cleanup_service.yml b/config/feature_flags/development/limit_assignees_per_issuable.yml
index 0826abbf994..d950b8c2f09 100644
--- a/config/feature_flags/development/container_registry_new_cleanup_service.yml
+++ b/config/feature_flags/development/limit_assignees_per_issuable.yml
@@ -1,8 +1,8 @@
---
-name: container_registry_new_cleanup_service
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/98651
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/375037
+name: limit_assignees_per_issuable
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95673
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373237
milestone: '15.5'
type: development
-group: group::package
+group: group::code review
default_enabled: false
diff --git a/config/metrics/aggregates/common.yml b/config/metrics/aggregates/common.yml
deleted file mode 100644
index ca6c664a6e9..00000000000
--- a/config/metrics/aggregates/common.yml
+++ /dev/null
@@ -1,62 +0,0 @@
-# Aggregated metrics that include EE only event names within `events:` attribute have to be defined at ee/config/metrics/aggregates/common.yml
-# instead of this file.
-# - name: unique name of aggregated metric
-# operator: aggregation operator. Valid values are:
-# - "OR": counts unique elements that were observed triggering any of following events
-# - "AND": counts unique elements that were observed triggering all of following events
-# events: list of events names to aggregate into metric. All events in this list must have the same 'redis_slot' and 'aggregation' attributes
-# see from lib/gitlab/usage_data_counters/known_events/ for the list of valid events.
-# source: defines which datasource will be used to locate events that should be included in aggregated metric. Valid values are:
-# - database
-# - redis
-# time_frame: defines time frames for aggregated metrics:
-# - 7d - last 7 days
-# - 28d - last 28 days
-# - all - all historical available data, this time frame is not available for redis source
-# feature_flag: name of development feature flag that will be checked before metrics aggregation is performed.
-# Corresponding feature flag should have `default_enabled` attribute set to `false`.
-# This attribute is OPTIONAL and can be omitted, when `feature_flag` is missing no feature flag will be checked.
----
-- name: incident_management_incidents_total_unique_counts
- operator: OR
- source: redis
- time_frame: [7d, 28d]
- events:
- - 'incident_management_incident_created'
- - 'incident_management_incident_reopened'
- - 'incident_management_incident_closed'
- - 'incident_management_incident_assigned'
- - 'incident_management_incident_todo'
- - 'incident_management_incident_comment'
- - 'incident_management_incident_zoom_meeting'
- - 'incident_management_incident_published'
- - 'incident_management_incident_relate'
- - 'incident_management_incident_unrelate'
- - 'incident_management_incident_change_confidential'
-- name: xmau_plan
- operator: OR
- source: redis
- time_frame: [7d, 28d]
- events:
- - users_creating_work_items
- - users_updating_work_item_title
- - users_updating_work_item_dates
- feature_flag: track_work_items_activity
-- name: xmau_project_management
- operator: OR
- source: redis
- time_frame: [7d, 28d]
- events:
- - users_creating_work_items
- - users_updating_work_item_title
- - users_updating_work_item_dates
- feature_flag: track_work_items_activity
-- name: users_work_items
- operator: OR
- source: redis
- time_frame: [7d, 28d]
- events:
- - users_creating_work_items
- - users_updating_work_item_title
- - users_updating_work_item_dates
- feature_flag: track_work_items_activity
diff --git a/config/metrics/counts_28d/20210216180511_incident_management_incidents_total_unique_counts.yml b/config/metrics/counts_28d/20210216180511_incident_management_incidents_total_unique_counts.yml
index 3174894384a..b5afcfa7ae6 100644
--- a/config/metrics/counts_28d/20210216180511_incident_management_incidents_total_unique_counts.yml
+++ b/config/metrics/counts_28d/20210216180511_incident_management_incidents_total_unique_counts.yml
@@ -9,7 +9,24 @@ product_category: incident_management
value_type: number
status: active
time_frame: 28d
+instrumentation_class: AggregatedMetric
data_source: redis_hll
+options:
+ aggregate:
+ operator: OR
+ attribute: user_id
+ events:
+ - 'incident_management_incident_created'
+ - 'incident_management_incident_reopened'
+ - 'incident_management_incident_closed'
+ - 'incident_management_incident_assigned'
+ - 'incident_management_incident_todo'
+ - 'incident_management_incident_comment'
+ - 'incident_management_incident_zoom_meeting'
+ - 'incident_management_incident_published'
+ - 'incident_management_incident_relate'
+ - 'incident_management_incident_unrelate'
+ - 'incident_management_incident_change_confidential'
distribution:
- ce
- ee
diff --git a/config/metrics/counts_28d/20220222215951_xmau_plan.yml b/config/metrics/counts_28d/20220222215951_xmau_plan.yml
index aaa9558d5a6..9498c56e06f 100644
--- a/config/metrics/counts_28d/20220222215951_xmau_plan.yml
+++ b/config/metrics/counts_28d/20220222215951_xmau_plan.yml
@@ -10,7 +10,16 @@ status: active
milestone: '14.9'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81336
time_frame: 28d
+instrumentation_class: WorkItemsActivityAggregatedMetric
data_source: redis_hll
+options:
+ aggregate:
+ operator: OR
+ attribute: user_id
+ events:
+ - users_creating_work_items
+ - users_updating_work_item_title
+ - users_updating_work_item_dates
data_category: optional
distribution:
- ce
diff --git a/config/metrics/counts_28d/20220222215952_xmau_project_management.yml b/config/metrics/counts_28d/20220222215952_xmau_project_management.yml
index ede46c85292..a02616e06ce 100644
--- a/config/metrics/counts_28d/20220222215952_xmau_project_management.yml
+++ b/config/metrics/counts_28d/20220222215952_xmau_project_management.yml
@@ -10,7 +10,16 @@ status: active
milestone: '14.9'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81336
time_frame: 28d
+instrumentation_class: WorkItemsActivityAggregatedMetric
data_source: redis_hll
+options:
+ aggregate:
+ operator: OR
+ attribute: user_id
+ events:
+ - users_creating_work_items
+ - users_updating_work_item_title
+ - users_updating_work_item_dates
data_category: optional
distribution:
- ce
diff --git a/config/metrics/counts_28d/20220222215955_users_work_items.yml b/config/metrics/counts_28d/20220222215955_users_work_items.yml
index a0f892fcef2..3e6103a6dfd 100644
--- a/config/metrics/counts_28d/20220222215955_users_work_items.yml
+++ b/config/metrics/counts_28d/20220222215955_users_work_items.yml
@@ -10,7 +10,16 @@ status: active
milestone: '14.9'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81336
time_frame: 28d
+instrumentation_class: WorkItemsActivityAggregatedMetric
data_source: redis_hll
+options:
+ aggregate:
+ operator: OR
+ attribute: user_id
+ events:
+ - users_creating_work_items
+ - users_updating_work_item_title
+ - users_updating_work_item_dates
data_category: optional
distribution:
- ce
diff --git a/config/metrics/counts_7d/20210216180515_incident_management_incidents_total_unique_counts.yml b/config/metrics/counts_7d/20210216180515_incident_management_incidents_total_unique_counts.yml
index 6089b36401e..93228309855 100644
--- a/config/metrics/counts_7d/20210216180515_incident_management_incidents_total_unique_counts.yml
+++ b/config/metrics/counts_7d/20210216180515_incident_management_incidents_total_unique_counts.yml
@@ -9,7 +9,24 @@ product_category: incident_management
value_type: number
status: active
time_frame: 7d
+instrumentation_class: AggregatedMetric
data_source: redis_hll
+options:
+ aggregate:
+ operator: OR
+ attribute: user_id
+ events:
+ - 'incident_management_incident_created'
+ - 'incident_management_incident_reopened'
+ - 'incident_management_incident_closed'
+ - 'incident_management_incident_assigned'
+ - 'incident_management_incident_todo'
+ - 'incident_management_incident_comment'
+ - 'incident_management_incident_zoom_meeting'
+ - 'incident_management_incident_published'
+ - 'incident_management_incident_relate'
+ - 'incident_management_incident_unrelate'
+ - 'incident_management_incident_change_confidential'
distribution:
- ce
- ee
diff --git a/config/metrics/counts_7d/20220222215851_xmau_plan.yml b/config/metrics/counts_7d/20220222215851_xmau_plan.yml
index 170b1c595de..ebeaa6e5060 100644
--- a/config/metrics/counts_7d/20220222215851_xmau_plan.yml
+++ b/config/metrics/counts_7d/20220222215851_xmau_plan.yml
@@ -9,8 +9,17 @@ value_type: number
status: active
milestone: '14.9'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81336
-time_frame: 7d
+instrumentation_class: WorkItemsActivityAggregatedMetric
data_source: redis_hll
+time_frame: 7d
+options:
+ aggregate:
+ operator: OR
+ attribute: user_id
+ events:
+ - users_creating_work_items
+ - users_updating_work_item_title
+ - users_updating_work_item_dates
data_category: optional
distribution:
- ce
diff --git a/config/metrics/counts_7d/20220222215852_xmau_project_management.yml b/config/metrics/counts_7d/20220222215852_xmau_project_management.yml
index 061ee13722b..13f35df6530 100644
--- a/config/metrics/counts_7d/20220222215852_xmau_project_management.yml
+++ b/config/metrics/counts_7d/20220222215852_xmau_project_management.yml
@@ -10,7 +10,16 @@ status: active
milestone: '14.9'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81336
time_frame: 7d
+instrumentation_class: WorkItemsActivityAggregatedMetric
data_source: redis_hll
+options:
+ aggregate:
+ operator: OR
+ attribute: user_id
+ events:
+ - users_creating_work_items
+ - users_updating_work_item_title
+ - users_updating_work_item_dates
data_category: optional
distribution:
- ce
diff --git a/config/metrics/counts_7d/20220222215855_users_work_items.yml b/config/metrics/counts_7d/20220222215855_users_work_items.yml
index b749a7c9430..fec786715b7 100644
--- a/config/metrics/counts_7d/20220222215855_users_work_items.yml
+++ b/config/metrics/counts_7d/20220222215855_users_work_items.yml
@@ -10,7 +10,16 @@ status: active
milestone: '14.9'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81336
time_frame: 7d
+instrumentation_class: WorkItemsActivityAggregatedMetric
data_source: redis_hll
+options:
+ aggregate:
+ operator: OR
+ attribute: user_id
+ events:
+ - users_creating_work_items
+ - users_updating_work_item_title
+ - users_updating_work_item_dates
data_category: optional
distribution:
- ce
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index b1da557014a..04d0b35cc71 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -409,6 +409,8 @@
- 1
- - projects_refresh_build_artifacts_size_statistics
- 1
+- - projects_register_suggested_reviewers_project
+ - 1
- - projects_schedule_bulk_repository_shard_moves
- 1
- - projects_update_repository_storage
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index bd9905ca652..1dda66a2e43 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -2891,6 +2891,7 @@ Input type: `GitlabSubscriptionActivateInput`
| ---- | ---- | ----------- |
| <a id="mutationgitlabsubscriptionactivateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationgitlabsubscriptionactivateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+| <a id="mutationgitlabsubscriptionactivatefuturesubscriptions"></a>`futureSubscriptions` | [`[SubscriptionFutureEntry!]`](#subscriptionfutureentry) | Array of future subscriptions. |
| <a id="mutationgitlabsubscriptionactivatelicense"></a>`license` | [`CurrentLicense`](#currentlicense) | Current license. |
### `Mutation.groupUpdate`
diff --git a/doc/api/group_protected_environments.md b/doc/api/group_protected_environments.md
index d160ce2c6d5..4cf87cb4305 100644
--- a/doc/api/group_protected_environments.md
+++ b/doc/api/group_protected_environments.md
@@ -112,7 +112,7 @@ POST /groups/:id/protected_environments
| `approval_rules` | array | no | Array of access levels allowed to approve, with each described by a hash. One of `user_id`, `group_id` or `access_level`. They take the form of `{user_id: integer}`, `{group_id: integer}` or `{access_level: integer}` respectively. You can also specify the number of required approvals from the specified entity with `required_approvals` field. See [Multiple approval rules](../ci/environments/deployment_approvals.md#multiple-approval-rules) for more information. |
The assignable `user_id` are the users who belong to the given group with the Maintainer role (or above).
-The assignable `group_id` are the sub-groups under the given group.
+The assignable `group_id` are the subgroups under the given group.
```shell
curl --header 'Content-Type: application/json' --request POST --data '{"name": "production", "deploy_access_levels": [{"group_id": 9899826}]}' --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/22034114/protected_environments"
@@ -157,7 +157,7 @@ PUT /groups/:id/protected_environments/:name
To update:
- **`user_id`**: Ensure the updated user belongs to the given group with the Maintainer role (or above). You must also pass the `id` of either a `deploy_access_level` or `approval_rule` in the respective hash.
-- **`group_id`**: Ensure the updated group is a sub-group of the group this protected environment belongs to. You must also pass the `id` of either a `deploy_access_level` or `approval_rule` in the respective hash.
+- **`group_id`**: Ensure the updated group is a subgroup of the group this protected environment belongs to. You must also pass the `id` of either a `deploy_access_level` or `approval_rule` in the respective hash.
To delete:
diff --git a/doc/architecture/blueprints/rate_limiting/index.md b/doc/architecture/blueprints/rate_limiting/index.md
index 9f7ec278133..2ed66f22b53 100644
--- a/doc/architecture/blueprints/rate_limiting/index.md
+++ b/doc/architecture/blueprints/rate_limiting/index.md
@@ -65,7 +65,7 @@ Inc._
- There is no way to automatically notify a user when they are approaching thresholds.
- There is no single way to change limits for a namespace / project / user / customer.
- There is no single way to monitor limits through real-time metrics.
-- There is no framework for hierarchical limit configuration (instance / namespace / sub-group / project).
+- There is no framework for hierarchical limit configuration (instance / namespace / subgroup / project).
- We allow disabling rate-limiting for some marquee SaaS customers, but this
increases a risk for those same customers. We should instead be able to set
higher limits.
diff --git a/doc/ci/environments/protected_environments.md b/doc/ci/environments/protected_environments.md
index f7e64ab426b..c19e4e5a140 100644
--- a/doc/ci/environments/protected_environments.md
+++ b/doc/ci/environments/protected_environments.md
@@ -26,7 +26,7 @@ Maintainer role.
Prerequisites:
-- When granting the **Allowed to deploy** permission to a group or sub-group, the user configuring the protected environment must be a **direct member** of the group or sub-group to be added. Otherwise, the group or sub-group will not show up in the dropdown. For more information see [issue #345140](https://gitlab.com/gitlab-org/gitlab/-/issues/345140).
+- When granting the **Allowed to deploy** permission to a group or subgroup, the user configuring the protected environment must be a **direct member** of the group or subgroup to be added. Otherwise, the group or subgroup will not show up in the dropdown. For more information see [issue #345140](https://gitlab.com/gitlab-org/gitlab/-/issues/345140).
To protect an environment:
@@ -214,8 +214,8 @@ configured:
They do *not* have access to the CI/CD configurations in the
top-level group, so operators can ensure that the critical configuration won't
be accidentally changed by the developers.
-- For sub-groups and child projects:
- - Regarding [sub-groups](../../user/group/subgroups/index.md), if a higher
+- For subgroups and child projects:
+ - Regarding [subgroups](../../user/group/subgroups/index.md), if a higher
group has configured the group-level protected environment, the lower groups
cannot override it.
- [Project-level protected environments](#protecting-environments) can be
diff --git a/doc/development/pipelines.md b/doc/development/pipelines.md
index 047a2064f6f..360ebcc3e52 100644
--- a/doc/development/pipelines.md
+++ b/doc/development/pipelines.md
@@ -358,6 +358,11 @@ with latest `master`, and then it triggers a regular branch pipeline for
never be merged back to `master`. Any other Ruby 3 changes should go into
`master` directly, which should be compatible with Ruby 2.7.
+Previously, `ruby3-sync` was using a project token stored in `RUBY3_SYNC_TOKEN`
+(now backed up in `RUBY3_SYNC_TOKEN_NOT_USED`), however due to various
+permissions issues, we ended up using an access token from `gitlab-bot` so now
+`RUBY3_SYNC_TOKEN` is actually an access token from `gitlab-bot`.
+
### Long-term plan
We follow the [PostgreSQL versions shipped with Omnibus GitLab](../administration/package_information/postgresql_versions.md):
diff --git a/doc/development/sec/index.md b/doc/development/sec/index.md
index baea583e1ce..294dad4048a 100644
--- a/doc/development/sec/index.md
+++ b/doc/development/sec/index.md
@@ -45,7 +45,7 @@ flowchart LR
### Scanning
The scanning part is responsible for finding vulnerabilities in given resources, and exporting results.
-The scans are executed in CI/CD jobs via several small projects called [Analyzers](../../user/application_security/terminology/index.md#analyzer), which can be found in our [Analyzers sub-group](https://gitlab.com/gitlab-org/security-products/analyzers).
+The scans are executed in CI/CD jobs via several small projects called [Analyzers](../../user/application_security/terminology/index.md#analyzer), which can be found in our [Analyzers subgroup](https://gitlab.com/gitlab-org/security-products/analyzers).
The Analyzers are wrappers around security tools called [Scanners](../../user/application_security/terminology/index.md#scanner), developed internally or externally, to integrate them into GitLab.
The Analyzers are mainly written in Go.
diff --git a/doc/development/testing_guide/end_to_end/beginners_guide.md b/doc/development/testing_guide/end_to_end/beginners_guide.md
index bceb0d12fde..b81379d89b2 100644
--- a/doc/development/testing_guide/end_to_end/beginners_guide.md
+++ b/doc/development/testing_guide/end_to_end/beginners_guide.md
@@ -120,6 +120,22 @@ module QA
end
```
+### The `product_group` metadata
+
+Assign `product_group` metadata and specify what product group this test belongs to. In this case, `authentication_and_authorization`.
+
+```ruby
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Manage' do
+ describe 'Login', product_group: :authentication_and_authorization do
+
+ end
+ end
+end
+```
+
### The `it` blocks (examples)
Every test suite contains at least one `it` block (example). A good way to start
@@ -128,7 +144,7 @@ writing end-to-end tests is to write test case descriptions as `it` blocks:
```ruby
module QA
RSpec.describe 'Manage' do
- describe 'Login' do
+ describe 'Login', product_group: :authentication_and_authorization do
it 'can login' do
end
@@ -152,7 +168,7 @@ Begin by logging in.
module QA
RSpec.describe 'Manage' do
- describe 'Login' do
+ describe 'Login', product_group: :authentication_and_authorization do
it 'can login' do
Flow::Login.sign_in
@@ -175,7 +191,7 @@ should answer the question "What do we test?"
module QA
RSpec.describe 'Manage' do
- describe 'Login' do
+ describe 'Login', product_group: :authentication_and_authorization do
it 'can login' do
Flow::Login.sign_in
@@ -222,7 +238,7 @@ a call to `sign_in`.
module QA
RSpec.describe 'Manage' do
- describe 'Login' do
+ describe 'Login', product_group: :authentication_and_authorization do
before do
Flow::Login.sign_in
end
@@ -251,7 +267,7 @@ ensuring we now sign in at the beginning of each test.
## Test setup using resources and page objects
Next, let's test something other than Login. Let's test Issues, which are owned by the Plan
-stage, so [create a file](#identify-the-devops-stage) in
+stage and the Project Management Group, so [create a file](#identify-the-devops-stage) in
`qa/specs/features/browser_ui/3_create/issues` called `issues_spec.rb`.
```ruby
@@ -259,7 +275,7 @@ stage, so [create a file](#identify-the-devops-stage) in
module QA
RSpec.describe 'Plan' do
- describe 'Issues' do
+ describe 'Issues', product_group: :project_management do
let(:issue) do
Resource::Issue.fabricate_via_api! do |issue|
issue.title = 'My issue'
diff --git a/doc/development/value_stream_analytics/value_stream_analytics_aggregated_backend.md b/doc/development/value_stream_analytics/value_stream_analytics_aggregated_backend.md
index 3391c80aca0..0b7e1bd46da 100644
--- a/doc/development/value_stream_analytics/value_stream_analytics_aggregated_backend.md
+++ b/doc/development/value_stream_analytics/value_stream_analytics_aggregated_backend.md
@@ -43,7 +43,7 @@ Benefits of the aggregated VSA backend:
- Simpler database queries (fewer JOINs).
- Faster aggregations, only a single table is accessed.
- Possibility to introduce further aggregations for improving the first page load time.
-- Better performance for large groups (with many sub-groups, projects, issues and, merge requests).
+- Better performance for large groups (with many subgroups, projects, issues and, merge requests).
- Ready for database decomposition. The VSA related database tables could live in a separate
database with a minimal development effort.
- Ready for keyset pagination which can be useful for exporting the data.
@@ -165,7 +165,7 @@ Creation time always happens first, so this stage always reports negative durati
The data collection scans and processes all issues and merge requests records in the group
hierarchy, starting from the top-level group. This means that if a group only has one value stream
-in a sub-group, we nevertheless collect data of all issues and merge requests in the hierarchy of
+in a subgroup, we nevertheless collect data of all issues and merge requests in the hierarchy of
this group. This aims to simplify the data collection mechanism. Moreover, data research shows
that most group hierarchies have their stages configured on the top level.
diff --git a/doc/development/work_items.md b/doc/development/work_items.md
index 5f0ef88f200..eabad175bf7 100644
--- a/doc/development/work_items.md
+++ b/doc/development/work_items.md
@@ -88,8 +88,8 @@ so that in future we can allow users to define custom WITs, we will move the
to `work_item_types` will involve creating the set of WITs for all root-level groups.
NOTE:
-At first, defining a WIT will only be possible at the root-level group, which would then be inherited by sub-groups.
-We will investigate the possibility of defining new WITs at sub-group levels at a later iteration.
+At first, defining a WIT will only be possible at the root-level group, which would then be inherited by subgroups.
+We will investigate the possibility of defining new WITs at subgroup levels at a later iteration.
### Introducing work_item_types table
diff --git a/doc/subscriptions/gitlab_dedicated/index.md b/doc/subscriptions/gitlab_dedicated/index.md
index 2369d8a187d..ae70a673349 100644
--- a/doc/subscriptions/gitlab_dedicated/index.md
+++ b/doc/subscriptions/gitlab_dedicated/index.md
@@ -18,7 +18,8 @@ GitLab Dedicated enables you to offload the operational overhead of managing the
## Available features
-- Authentication: Support for instance-level [SAML OmniAuth](../../integration/saml.md) functionality. GitLab Dedicated acts as the service provider, and you must provide the necessary [configuration](../../integration/saml.md#general-setup) in order for GitLab to communicate with your IdP. This is provided during onboarding. SAML [request signing](../../integration/saml.md#request-signing-optional) is supported.
+- Authentication: Support for instance-level [SAML OmniAuth](../../integration/saml.md) functionality. GitLab Dedicated acts as the service provider, and you must provide the necessary [configuration](../../integration/saml.md#general-setup) in order for GitLab to communicate with your IdP. This is provided during onboarding.
+ - SAML [request signing](../../integration/saml.md#request-signing-optional), [group sync](../../user/group/saml_sso/group_sync.md#configure-saml-group-sync), and [SAML groups](../../integration/saml.md#saml-groups) are supported.
- Networking:
- Public connectivity with support for IP Allowlists. During onboarding, you can optionally specify a list of IP addresses that can access your Dedicated instance. Subsequently, when an IP not on the allowlist tries to access your instance the connection will be refused.
- Optional. Private connectivity via [AWS PrivateLink](https://aws.amazon.com/privatelink/).
@@ -43,6 +44,7 @@ Features that are not available but we plan to support in the future:
- FortiAuthenticator/FortiToken 2FA
- Reply-by email
- Service Desk
+- Any feature not listed [above](#available-features) which needs to be configured outside of the web interface.
Features that we do not plan to offer at all:
diff --git a/doc/user/application_security/get-started-security.md b/doc/user/application_security/get-started-security.md
index 41dc35dd8ce..b6213a98f91 100644
--- a/doc/user/application_security/get-started-security.md
+++ b/doc/user/application_security/get-started-security.md
@@ -14,7 +14,7 @@ The following steps will help you get the most from GitLab application security
1. Enable [Secret Detection](secret_detection/index.md) and [Dependency Scanning](dependency_scanning/index.md)
to identify any leaked secrets and vulnerable packages in your codebase.
- - For all security scanners, enable them by updating your `[.gitlab-ci.yml](../../ci/yaml/gitlab_ci_yaml.md)` directly on your `default` branch. This creates a baseline scan of your `default` branch, which is necessary for
+ - For all security scanners, enable them by updating your [`.gitlab-ci.yml`](../../ci/yaml/gitlab_ci_yaml.md) directly on your `default` branch. This creates a baseline scan of your `default` branch, which is necessary for
feature branch scans to be compared against. This allows [merge requests](../project/merge_requests/index.md)
to display only newly-introduced vulnerabilities. Otherwise, merge requests will display every
vulnerability in the branch, regardless of whether it was introduced by a change in the branch.
diff --git a/doc/user/application_security/img/secure_tools_and_cicd_stages.png b/doc/user/application_security/img/secure_tools_and_cicd_stages.png
new file mode 100644
index 00000000000..3dbfd835baf
--- /dev/null
+++ b/doc/user/application_security/img/secure_tools_and_cicd_stages.png
Binary files differ
diff --git a/doc/user/application_security/index.md b/doc/user/application_security/index.md
index ffd9bb1a783..e0eec53a782 100644
--- a/doc/user/application_security/index.md
+++ b/doc/user/application_security/index.md
@@ -32,6 +32,25 @@ schedule. Coverage includes:
- Vulnerabilities in a running web application.
- Infrastructure as code configuration.
+Each of the GitLab application security tools is relevant to specific stages of the feature development workflow.
+
+- Commit
+ - SAST
+ - Secret Detection
+ - IaC Scanning
+ - Dependency Scanning
+ - License Scanning
+ - Coverage-guided Fuzz Testing
+- Build
+ - Container Scanning
+- Test
+ - API Security
+ - DAST
+- Deploy
+ - Operational Container Scanning
+
+![CI/CD stages and matching GitLab application security tools](img/secure_tools_and_cicd_stages.png)
+
### Source code analysis
Source code analysis occurs on every code commit. Details of vulnerabilities detected are provided
@@ -48,7 +67,7 @@ Analysis of the web application occurs on every code commit. As part of the CI/C
application is built, deployed to a test environment, and subjected to the following tests:
- Test for known application vectors - [Dynamic Application Security Testing (DAST)](dast/index.md).
-- Analysis of APIs for known attack vectors - [DAST API](dast_api/index.md).
+- Analysis of APIs for known attack vectors - [API Security](dast_api/index.md).
- Analysis of web APIs for unknown bugs and vulnerabilities - [API fuzzing](api_fuzzing/index.md).
### Dependency analysis
@@ -66,7 +85,7 @@ For more details, see
[Dependency Scanning compared to Container Scanning](dependency_scanning/index.md#dependency-scanning-compared-to-container-scanning).
Additionally, dependencies in operational container images can be analyzed for vulnerabilities
-on a regular schedule or cadence. For more details, see [Cluster Image Scanning](../clusters/agent/vulnerabilities.md).
+on a regular schedule or cadence. For more details, see [Operational Container Scanning](../../user/clusters/agent/vulnerabilities.md).
### Infrastructure analysis
@@ -486,6 +505,7 @@ Feedback is welcome on our vision for [unifying the user experience for these tw
<!-- NOTE: The below subsection(`### Secure job failing with exit code 1`) documentation URL is referred in the [/gitlab-org/security-products/analyzers/command](https://gitlab.com/gitlab-org/security-products/analyzers/command/-/blob/main/command.go#L19) repository. If this section/subsection changes, please ensure to update the corresponding URL in the mentioned repository.
-->
+
### Secure job failing with exit code 1
WARNING:
diff --git a/doc/user/application_security/policies/scan-execution-policies.md b/doc/user/application_security/policies/scan-execution-policies.md
index ce0900aaf4e..da928c405a7 100644
--- a/doc/user/application_security/policies/scan-execution-policies.md
+++ b/doc/user/application_security/policies/scan-execution-policies.md
@@ -9,11 +9,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - Group-level security policies were [introduced](https://gitlab.com/groups/gitlab-org/-/epics/4425) in GitLab 15.2.
> - Group-level security policies were [enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/356258) in GitLab 15.4.
-Group, sub-group, or project owners can use scan execution policies to require that security scans run on a specified
-schedule or with the project (or multiple projects if the policy is defined at a group or sub-group level) pipeline. Required scans are injected into the CI pipeline as new jobs
+Group, subgroup, or project owners can use scan execution policies to require that security scans run on a specified
+schedule or with the project (or multiple projects if the policy is defined at a group or subgroup level) pipeline. Required scans are injected into the CI pipeline as new jobs
with a long, random job name. In the unlikely event of a job name collision, the security policy job overwrites
any pre-existing job in the pipeline. If a policy is created at the group-level, it will apply to every child
-project or sub-group. A group-level policy cannot be edited from a child project or sub-group.
+project or subgroup. A group-level policy cannot be edited from a child project or subgroup.
This feature has some overlap with [compliance framework pipelines](../../group/manage.md#configure-a-compliance-pipeline),
as we have not [unified the user experience for these two features](https://gitlab.com/groups/gitlab-org/-/epics/7312).
@@ -29,7 +29,7 @@ an error appears that states `chosen stage does not exist`.
## Scan execution policy editor
NOTE:
-Only group, sub-group, or project Owners have the [permissions](../../permissions.md#project-members-permissions)
+Only group, subgroup, or project Owners have the [permissions](../../permissions.md#project-members-permissions)
to select Security Policy Project.
Once your policy is complete, save it by selecting **Create via merge request**
diff --git a/doc/user/group/import/index.md b/doc/user/group/import/index.md
index b382b08dec9..ee50cfcf182 100644
--- a/doc/user/group/import/index.md
+++ b/doc/user/group/import/index.md
@@ -109,7 +109,7 @@ Items that are migrated to the target instance include:
- Namespace Settings
- Releases
- Milestones ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339422) in GitLab 15.0).
-- Sub-Groups
+- Subgroups
- Uploads
Any other items are **not** migrated.
diff --git a/doc/user/group/saml_sso/scim_setup.md b/doc/user/group/saml_sso/scim_setup.md
index 6b64288f5af..75160a53de3 100644
--- a/doc/user/group/saml_sso/scim_setup.md
+++ b/doc/user/group/saml_sso/scim_setup.md
@@ -207,7 +207,7 @@ For role information, please see the [Group SAML page](index.md#user-access-and-
### Blocking access
-To rescind access to the top-level group, all sub-groups, and projects, remove or deactivate the user
+To rescind access to the top-level group, all subgroups, and projects, remove or deactivate the user
on the identity provider. After the identity provider performs a sync, based on its configured schedule, the user's membership is revoked and they lose access.
NOTE:
diff --git a/doc/user/group/settings/group_access_tokens.md b/doc/user/group/settings/group_access_tokens.md
index 64cbe68a9d4..7ed36f8ab1e 100644
--- a/doc/user/group/settings/group_access_tokens.md
+++ b/doc/user/group/settings/group_access_tokens.md
@@ -147,7 +147,7 @@ The scope determines the actions you can perform when you authenticate with a gr
## Enable or disable group access token creation
-To enable or disable group access token creation for all sub-groups in a top-level group:
+To enable or disable group access token creation for all subgroups in a top-level group:
1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md
index 812888de5c2..58f5e476f26 100644
--- a/doc/user/group/subgroups/index.md
+++ b/doc/user/group/subgroups/index.md
@@ -201,7 +201,7 @@ role on an ancestor group, add the user to the subgroup again with a higher role
## Mention subgroups
Mentioning subgroups ([`@<subgroup_name>`](../../discussions/index.md#mentions)) in issues, commits, and merge requests
-notifies all direct members of that group. Inherited members of a sub-group are not notified by mentions. Mentioning works the same as for projects and groups, and you can choose the group
+notifies all direct members of that group. Inherited members of a subgroup are not notified by mentions. Mentioning works the same as for projects and groups, and you can choose the group
of people to be notified.
<!-- ## Troubleshooting
diff --git a/doc/user/workspace/index.md b/doc/user/workspace/index.md
index 7818a89abe4..d7e014672aa 100644
--- a/doc/user/workspace/index.md
+++ b/doc/user/workspace/index.md
@@ -26,7 +26,7 @@ everything you do as a GitLab administrator, including:
Our goal is to reach feature parity between SaaS and self-managed installations, with all
[Admin Area settings](/ee/user/admin_area/settings/index.md) moving to either:
-- Groups. Available in the Workspace, top-level group namespaces, and sub-groups.
+- Groups. Available in the Workspace, top-level group namespaces, and subgroups.
- Hardware Controls. For functionality that does not apply to groups, Hardware Controls are only
applicable to self-managed installations. There is one Hardware Controls section per installation.
diff --git a/lib/gitlab/usage/metrics/instrumentations/work_items_activity_aggregated_metric.rb b/lib/gitlab/usage/metrics/instrumentations/work_items_activity_aggregated_metric.rb
new file mode 100644
index 00000000000..d045265495a
--- /dev/null
+++ b/lib/gitlab/usage/metrics/instrumentations/work_items_activity_aggregated_metric.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Usage
+ module Metrics
+ module Instrumentations
+ class WorkItemsActivityAggregatedMetric < AggregatedMetric
+ available? { Feature.enabled?(:track_work_items_activity) }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/tasks/gitlab/tw/codeowners.rake b/lib/tasks/gitlab/tw/codeowners.rake
index c7353b9415d..3f11aefc74f 100644
--- a/lib/tasks/gitlab/tw/codeowners.rake
+++ b/lib/tasks/gitlab/tw/codeowners.rake
@@ -28,7 +28,6 @@ namespace :tw do
CodeOwnerRule.new('Compliance', '@eread'),
CodeOwnerRule.new('Composition Analysis', '@rdickenson'),
CodeOwnerRule.new('Configure', '@phillipwells'),
- CodeOwnerRule.new('Container Security', '@claytoncornell'),
CodeOwnerRule.new('Contributor Experience', '@eread'),
CodeOwnerRule.new('Conversion', '@kpaizee'),
CodeOwnerRule.new('Database', '@aqualls'),
@@ -58,6 +57,7 @@ namespace :tw do
CodeOwnerRule.new('Pipeline Execution', '@marcel.amirault'),
CodeOwnerRule.new('Pipeline Insights', '@marcel.amirault'),
CodeOwnerRule.new('Portfolio Management', '@msedlakjakubowski'),
+ CodeOwnerRule.new('Product Analytics', '@lciutacu'),
CodeOwnerRule.new('Product Intelligence', '@claytoncornell'),
CodeOwnerRule.new('Product Planning', '@msedlakjakubowski'),
CodeOwnerRule.new('Project Management', '@msedlakjakubowski'),
@@ -68,6 +68,7 @@ namespace :tw do
CodeOwnerRule.new('Respond', '@msedlakjakubowski'),
CodeOwnerRule.new('Runner', '@sselhorn'),
CodeOwnerRule.new('Pods', '@sselhorn'),
+ CodeOwnerRule.new('Security Policies', '@claytoncornell'),
CodeOwnerRule.new('Source Code', '@aqualls'),
CodeOwnerRule.new('Static Analysis', '@rdickenson'),
CodeOwnerRule.new('Style Guide', '@sselhorn'),
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index 03f753b1d61..81c518bb4c6 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -242,6 +242,11 @@ module QA
end
def fill_element(name, content)
+ # `click_element_coordinates` is used to ensure the element is focused.
+ # Without it, flakiness can occur on pages with GitLab keyboard shortcuts enabled,
+ # where certain keys trigger actions when typed elsewhere on the page.
+ click_element_coordinates(name)
+
find_element(name).set(content)
end
diff --git a/qa/qa/page/component/namespace_select.rb b/qa/qa/page/component/namespace_select.rb
index 399c265c424..41a9975003f 100644
--- a/qa/qa/page/component/namespace_select.rb
+++ b/qa/qa/page/component/namespace_select.rb
@@ -21,7 +21,7 @@ module QA
click_element :namespaces_list
within_element(:namespaces_list) do
- find_element(:namespaces_list_search).fill_in(with: item)
+ fill_element(:namespaces_list_search, item)
wait_for_requests
diff --git a/qa/qa/page/merge_request/new.rb b/qa/qa/page/merge_request/new.rb
index 79eb4f2d51b..909b37943ff 100644
--- a/qa/qa/page/merge_request/new.rb
+++ b/qa/qa/page/merge_request/new.rb
@@ -38,7 +38,6 @@ module QA
def click_diffs_tab
click_element(:diffs_tab)
- click_element(:dismiss_popover_button) if has_element?(:dismiss_popover_button, wait: 1)
end
def has_file?(file_name)
diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb
index 2587241ed18..e1add9ad434 100644
--- a/qa/qa/page/merge_request/show.rb
+++ b/qa/qa/page/merge_request/show.rb
@@ -108,6 +108,7 @@ module QA
view 'app/assets/javascripts/vue_shared/components/markdown/header.vue' do
element :suggestion_button
+ element :dismiss_suggestion_popover_button
end
view 'app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_header.vue' do
@@ -191,8 +192,11 @@ module QA
wait_until(sleep_interval: 5) do
has_css?('a[data-linenumber="1"]')
end
+
all_elements(:new_diff_line_link, minimum: 1).first.hover
click_element(:diff_comment_button)
+ click_element(:dismiss_suggestion_popover_button) if has_element?(:dismiss_suggestion_popover_button, wait: 1)
+
fill_element(:reply_field, text)
end
@@ -208,7 +212,6 @@ module QA
def click_diffs_tab
click_element(:diffs_tab)
- click_element(:dismiss_popover_button) if has_element?(:dismiss_popover_button, wait: 1)
end
def click_pipeline_link
diff --git a/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb b/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
index f5b5460769e..07d99a786ba 100644
--- a/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
+++ b/spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb
@@ -6,8 +6,8 @@ RSpec.describe 'Batch diffs', :js do
include MergeRequestDiffHelpers
include RepoHelpers
- let(:project) { create(:project, :repository) }
- let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'master', target_branch: 'empty-branch') }
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:merge_request) { create(:merge_request, source_project: project, source_branch: 'master', target_branch: 'empty-branch') }
before do
sign_in(project.first_owner)
diff --git a/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js b/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js
index 5581fd52458..ef3c8bde3cf 100644
--- a/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js
+++ b/spec/frontend/members/components/filter_sort/sort_dropdown_spec.js
@@ -45,7 +45,7 @@ describe('SortDropdown', () => {
const findSortingComponent = () => wrapper.findComponent(GlSorting);
const findSortDirectionToggle = () =>
- findSortingComponent().find('button[title="Sort direction"]');
+ findSortingComponent().find('button[title^="Sort direction"]');
const findDropdownToggle = () => wrapper.find('button[aria-haspopup="true"]');
const findDropdownItemByText = (text) =>
wrapper
diff --git a/spec/helpers/form_helper_spec.rb b/spec/helpers/form_helper_spec.rb
index 4b76c370810..14ff5d97057 100644
--- a/spec/helpers/form_helper_spec.rb
+++ b/spec/helpers/form_helper_spec.rb
@@ -6,35 +6,85 @@ RSpec.describe FormHelper do
include Devise::Test::ControllerHelpers
describe '#dropdown_max_select' do
+ let(:feature_flag) { :limit_reviewer_and_assignee_size }
+
context "with the :limit_reviewer_and_assignee_size feature flag on" do
+ before do
+ stub_feature_flags(feature_flag => true)
+ end
+
it 'correctly returns the max amount of reviewers or assignees to allow' do
- max = MergeRequest::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS
+ max = Issuable::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS
- expect(helper.dropdown_max_select({}))
+ expect(helper.dropdown_max_select({}, feature_flag))
.to eq(max)
- expect(helper.dropdown_max_select({ 'max-select'.to_sym => 5 }))
+ expect(helper.dropdown_max_select({ 'max-select'.to_sym => 5 }, feature_flag))
.to eq(5)
- expect(helper.dropdown_max_select({ 'max-select'.to_sym => max + 5 }))
+ expect(helper.dropdown_max_select({ 'max-select'.to_sym => max + 5 }, feature_flag))
.to eq(max)
end
end
context "with the :limit_reviewer_and_assignee_size feature flag off" do
before do
- stub_feature_flags(limit_reviewer_and_assignee_size: false)
+ stub_feature_flags(feature_flag => false)
end
it 'correctly returns the max amount of reviewers or assignees to allow' do
- expect(helper.dropdown_max_select({}))
+ expect(helper.dropdown_max_select({}, feature_flag))
.to eq(nil)
- expect(helper.dropdown_max_select({ 'max-select'.to_sym => 5 }))
+ expect(helper.dropdown_max_select({ 'max-select'.to_sym => 5 }, feature_flag))
.to eq(5)
- expect(helper.dropdown_max_select({ 'max-select'.to_sym => 120 }))
+ expect(helper.dropdown_max_select({ 'max-select'.to_sym => 120 }, feature_flag))
.to eq(120)
end
end
end
+ describe '#assignees_dropdown_options' do
+ let(:merge_request) { build(:merge_request) }
+
+ context "with the :limit_assignees_per_issuable feature flag on" do
+ context "with multiple assignees" do
+ it 'correctly returns the max amount of assignees to allow' do
+ allow(helper).to receive(:merge_request_supports_multiple_assignees?).and_return(true)
+
+ expect(helper.assignees_dropdown_options(:merge_request)[:data][:'max-select'])
+ .to eq(Issuable::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS)
+ end
+ end
+
+ context "with only 1 assignee" do
+ it 'correctly returns the max amount of assignees to allow' do
+ expect(helper.assignees_dropdown_options(:merge_request)[:data][:'max-select'])
+ .to eq(1)
+ end
+ end
+ end
+
+ context "with the :limit_assignees_per_issuable feature flag off" do
+ before do
+ stub_feature_flags(limit_assignees_per_issuable: false)
+ end
+
+ context "with multiple assignees" do
+ it 'correctly returns the max amount of assignees to allow' do
+ allow(helper).to receive(:merge_request_supports_multiple_assignees?).and_return(true)
+
+ expect(helper.assignees_dropdown_options(:merge_request)[:data][:'max-select'])
+ .to eq(nil)
+ end
+ end
+
+ context "with only 1 assignee" do
+ it 'correctly returns the max amount of assignees to allow' do
+ expect(helper.assignees_dropdown_options(:merge_request)[:data][:'max-select'])
+ .to eq(1)
+ end
+ end
+ end
+ end
+
describe '#reviewers_dropdown_options' do
let(:merge_request) { build(:merge_request) }
@@ -44,7 +94,7 @@ RSpec.describe FormHelper do
allow(helper).to receive(:merge_request_supports_multiple_reviewers?).and_return(true)
expect(helper.reviewers_dropdown_options(merge_request)[:data][:'max-select'])
- .to eq(MergeRequest::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS)
+ .to eq(Issuable::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS)
end
end
diff --git a/spec/helpers/wiki_helper_spec.rb b/spec/helpers/wiki_helper_spec.rb
index e798281ccff..06bc89b4950 100644
--- a/spec/helpers/wiki_helper_spec.rb
+++ b/spec/helpers/wiki_helper_spec.rb
@@ -79,14 +79,16 @@ RSpec.describe WikiHelper do
let(:classes) { "gl-button btn btn-default btn-icon has-tooltip reverse-sort-btn qa-reverse-sort rspec-reverse-sort" }
def expected_link(sort, direction, icon_class)
+ reversed_direction = direction == 'desc' ? 'asc' : 'desc'
path =
if sort
- "/#{wiki.project.full_path}/-/wikis/pages?direction=#{direction}&sort=#{sort}"
+ "/#{wiki.project.full_path}/-/wikis/pages?direction=#{reversed_direction}&sort=#{sort}"
else
- "/#{wiki.project.full_path}/-/wikis/pages?direction=#{direction}"
+ "/#{wiki.project.full_path}/-/wikis/pages?direction=#{reversed_direction}"
end
- helper.link_to(path, type: 'button', class: classes, title: 'Sort direction') do
+ title = direction == 'desc' ? _('Sort direction: Descending') : _('Sort direction: Ascending')
+ helper.link_to(path, type: 'button', class: classes, title: title) do
helper.sprite_icon("sort-#{icon_class}")
end
end
@@ -101,7 +103,7 @@ RSpec.describe WikiHelper do
let(:direction) { nil }
it 'renders with default values' do
- expect(wiki_link).to eq(expected_link('title', 'desc', 'lowest'))
+ expect(wiki_link).to eq(expected_link('title', 'asc', 'lowest'))
end
end
@@ -110,7 +112,7 @@ RSpec.describe WikiHelper do
let(:direction) { 'asc' }
it 'renders a link with opposite direction' do
- expect(wiki_link).to eq(expected_link('title', 'desc', 'lowest'))
+ expect(wiki_link).to eq(expected_link('title', 'aesc', 'lowest'))
end
end
@@ -119,7 +121,7 @@ RSpec.describe WikiHelper do
let(:direction) { 'desc' }
it 'renders a link with opposite direction' do
- expect(wiki_link).to eq(expected_link('created_at', 'asc', 'highest'))
+ expect(wiki_link).to eq(expected_link('created_at', 'desc', 'highest'))
end
end
end
@@ -134,7 +136,7 @@ RSpec.describe WikiHelper do
let(:direction) { 'asc' }
it 'ignores created_at and renders a link with opposite direction' do
- expect(wiki_link).to eq(expected_link(nil, 'desc', 'lowest'))
+ expect(wiki_link).to eq(expected_link(nil, 'asc', 'lowest'))
end
end
@@ -143,7 +145,7 @@ RSpec.describe WikiHelper do
let(:direction) { nil }
it 'renders with default values' do
- expect(wiki_link).to eq(expected_link(nil, 'desc', 'lowest'))
+ expect(wiki_link).to eq(expected_link(nil, 'asc', 'lowest'))
end
end
end
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/work_items_activity_aggregated_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/work_items_activity_aggregated_metric_spec.rb
new file mode 100644
index 00000000000..c13a5ba4d72
--- /dev/null
+++ b/spec/lib/gitlab/usage/metrics/instrumentations/work_items_activity_aggregated_metric_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Usage::Metrics::Instrumentations::WorkItemsActivityAggregatedMetric do
+ let(:metric_definition) do
+ {
+ data_source: 'redis_hll',
+ time_frame: '7d',
+ options: {
+ aggregate: {
+ operator: 'OR'
+ },
+ events: %w[
+ users_creating_work_items
+ users_updating_work_item_title
+ users_updating_work_item_dates
+ ]
+ }
+ }
+ end
+
+ around do |example|
+ freeze_time { example.run }
+ end
+
+ describe '#available?' do
+ it 'returns false without track_work_items_activity feature' do
+ stub_feature_flags(track_work_items_activity: false)
+
+ expect(described_class.new(metric_definition).available?).to eq(false)
+ end
+
+ it 'returns true with track_work_items_activity feature' do
+ stub_feature_flags(track_work_items_activity: true)
+
+ expect(described_class.new(metric_definition).available?).to eq(true)
+ end
+ end
+
+ describe '#value', :clean_gitlab_redis_shared_state do
+ let(:counter) { Gitlab::UsageDataCounters::HLLRedisCounter }
+
+ before do
+ counter.track_event(:users_creating_work_items, values: 1, time: 1.week.ago)
+ counter.track_event(:users_updating_work_item_title, values: 1, time: 1.week.ago)
+ counter.track_event(:users_updating_work_item_dates, values: 2, time: 1.week.ago)
+ end
+
+ it 'has correct value' do
+ expect(described_class.new(metric_definition).value).to eq 2
+ end
+ end
+end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 6763cc904b4..8842a36f40a 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -75,6 +75,24 @@ RSpec.describe Issuable do
it_behaves_like 'truncates the description to its allowed maximum length on import'
end
+
+ describe '#validate_assignee_length' do
+ let(:assignee_1) { create(:user) }
+ let(:assignee_2) { create(:user) }
+ let(:assignee_3) { create(:user) }
+
+ subject { create(:merge_request) }
+
+ before do
+ stub_const("Issuable::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS", 2)
+ end
+
+ it 'will not exceed the assignee limit' do
+ expect do
+ subject.update!(assignees: [assignee_1, assignee_2, assignee_3])
+ end.to raise_error(ActiveRecord::RecordInvalid)
+ end
+ end
end
describe "Scope" do
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index cf1fd115882..97e9c51e21e 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -8336,6 +8336,22 @@ RSpec.describe Project, factory_default: :keep do
end
end
+ describe '#can_suggest_reviewers?' do
+ let_it_be(:project) { create(:project) }
+
+ subject(:can_suggest_reviewers) { project.can_suggest_reviewers? }
+
+ it { is_expected.to be(false) }
+ end
+
+ describe '#suggested_reviewers_available?' do
+ let_it_be(:project) { create(:project) }
+
+ subject(:suggested_reviewers_available) { project.suggested_reviewers_available? }
+
+ it { is_expected.to be(false) }
+ end
+
private
def finish_job(export_job)
diff --git a/spec/services/merge_requests/update_assignees_service_spec.rb b/spec/services/merge_requests/update_assignees_service_spec.rb
index 3a0b17c2768..2d80d75a262 100644
--- a/spec/services/merge_requests/update_assignees_service_spec.rb
+++ b/spec/services/merge_requests/update_assignees_service_spec.rb
@@ -36,6 +36,20 @@ RSpec.describe MergeRequests::UpdateAssigneesService do
service.execute(merge_request)
end
+ shared_examples 'it updates and enqueues the job' do
+ it 'correctly updates the MR and enqueues the job' do
+ expect_next(MergeRequests::HandleAssigneesChangeService, project: project, current_user: user) do |service|
+ expect(service)
+ .to receive(:async_execute).with(merge_request, [user3], execute_hooks: true)
+ end
+
+ expect { update_merge_request }
+ .to change { merge_request.reload.assignees }.from([user3]).to(new_users)
+ .and change(merge_request, :updated_at)
+ .and change(merge_request, :updated_by).to(user)
+ end
+ end
+
shared_examples 'removing all assignees' do
it 'removes all assignees' do
expect(update_merge_request).to have_attributes(assignees: be_empty, errors: be_none)
@@ -73,16 +87,8 @@ RSpec.describe MergeRequests::UpdateAssigneesService do
it_behaves_like 'removing all assignees'
end
- it 'updates the MR, and queues the more expensive work for later' do
- expect_next(MergeRequests::HandleAssigneesChangeService, project: project, current_user: user) do |service|
- expect(service)
- .to receive(:async_execute).with(merge_request, [user3], execute_hooks: true)
- end
-
- expect { update_merge_request }
- .to change { merge_request.reload.assignees }.from([user3]).to([user2])
- .and change(merge_request, :updated_at)
- .and change(merge_request, :updated_by).to(user)
+ it_behaves_like 'it updates and enqueues the job' do
+ let(:new_users) { [user2] }
end
it 'does not update the assignees if they do not have access' do
diff --git a/spec/services/projects/container_repository/cleanup_tags_service_spec.rb b/spec/services/projects/container_repository/cleanup_tags_service_spec.rb
index c430bb6e913..8311c4e4d9b 100644
--- a/spec/services/projects/container_repository/cleanup_tags_service_spec.rb
+++ b/spec/services/projects/container_repository/cleanup_tags_service_spec.rb
@@ -86,14 +86,6 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService do
end
it_behaves_like 'calling service', ::Projects::ContainerRepository::Gitlab::CleanupTagsService, extra_log_data: { gitlab_cleanup_tags_service: true }
-
- context 'with container_registry_new_cleanup_service disabled' do
- before do
- stub_feature_flags(container_registry_new_cleanup_service: false)
- end
-
- it_behaves_like 'calling service', ::Projects::ContainerRepository::ThirdParty::CleanupTagsService, extra_log_data: { third_party_cleanup_tags_service: true }
- end
end
context 'not supporting the gitlab api' do
diff --git a/spec/support/shared_examples/quick_actions/issuable/max_issuable_examples.rb b/spec/support/shared_examples/quick_actions/issuable/max_issuable_examples.rb
new file mode 100644
index 00000000000..e725de8ad31
--- /dev/null
+++ b/spec/support/shared_examples/quick_actions/issuable/max_issuable_examples.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'does not exceed the issuable size limit' do
+ let(:user1) { create(:user) }
+ let(:user2) { create(:user) }
+ let(:user3) { create(:user) }
+
+ before do
+ project.add_maintainer(user)
+ project.add_maintainer(user1)
+ project.add_maintainer(user2)
+ project.add_maintainer(user3)
+ end
+
+ context 'when feature flag is turned on' do
+ context "when the number of users of issuable does exceed the limit" do
+ before do
+ stub_const("Issuable::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS", 2)
+ end
+
+ it 'will not add more than the allowed number of users' do
+ allow_next_instance_of(update_service) do |service|
+ expect(service).not_to receive(:execute)
+ end
+
+ note = described_class.new(project, user, opts.merge(
+ note: note_text,
+ noteable_type: noteable_type,
+ noteable_id: issuable.id,
+ confidential: false
+ )).execute
+
+ expect(note.errors[:validation]).to match_array([validation_message])
+ end
+ end
+
+ context "when the number of users does not exceed the limit" do
+ before do
+ stub_const("Issuable::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS", 6)
+ end
+
+ it 'calls execute and does not return an error' do
+ allow_next_instance_of(update_service) do |service|
+ expect(service).to receive(:execute).and_call_original
+ end
+
+ note = described_class.new(project, user, opts.merge(
+ note: note_text,
+ noteable_type: noteable_type,
+ noteable_id: issuable.id,
+ confidential: false
+ )).execute
+
+ expect(note.errors[:validation]).to be_empty
+ end
+ end
+ end
+
+ context 'when feature flag is off' do
+ before do
+ stub_feature_flags(feature_flag_hash)
+ end
+
+ context "when the number of users of issuable does exceed the limit" do
+ before do
+ stub_const("Issuable::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS", 2)
+ end
+
+ it 'will not add more than the allowed number of users' do
+ allow_next_instance_of(MergeRequests::UpdateService) do |service|
+ expect(service).to receive(:execute).and_call_original
+ end
+
+ note = described_class.new(project, user, opts.merge(
+ note: note_text,
+ noteable_type: 'MergeRequest',
+ noteable_id: issuable.id,
+ confidential: false
+ )).execute
+
+ expect(note.errors[:validation]).to be_empty
+ end
+ end
+ end
+end
diff --git a/workhorse/go.mod b/workhorse/go.mod
index b933692f997..664b63c3da4 100644
--- a/workhorse/go.mod
+++ b/workhorse/go.mod
@@ -26,7 +26,7 @@ require (
github.com/sirupsen/logrus v1.9.0
github.com/smartystreets/goconvey v1.7.2
github.com/stretchr/testify v1.8.0
- gitlab.com/gitlab-org/gitaly/v15 v15.4.0
+ gitlab.com/gitlab-org/gitaly/v15 v15.4.2
gitlab.com/gitlab-org/golang-archive-zip v0.1.1
gitlab.com/gitlab-org/labkit v1.16.0
gocloud.dev v0.26.0
diff --git a/workhorse/go.sum b/workhorse/go.sum
index 7e2996a7148..e07588706b9 100644
--- a/workhorse/go.sum
+++ b/workhorse/go.sum
@@ -949,8 +949,8 @@ github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
-gitlab.com/gitlab-org/gitaly/v15 v15.4.0 h1:fLGICSlAXbxxoat2dJt+DKrhRzdVkPXhMQUYKyFBDks=
-gitlab.com/gitlab-org/gitaly/v15 v15.4.0/go.mod h1:anANn2UwrECvFOEvLx8DkXYYDQ6g3+jmv0kP2VDYm70=
+gitlab.com/gitlab-org/gitaly/v15 v15.4.2 h1:evAILjEjT7M+pegcbP4QsViK4Hkt1I1IwJAr5AQjbdY=
+gitlab.com/gitlab-org/gitaly/v15 v15.4.2/go.mod h1:anANn2UwrECvFOEvLx8DkXYYDQ6g3+jmv0kP2VDYm70=
gitlab.com/gitlab-org/golang-archive-zip v0.1.1 h1:35k9giivbxwF03+8A05Cm8YoxoakU8FBCj5gysjCTCE=
gitlab.com/gitlab-org/golang-archive-zip v0.1.1/go.mod h1:ZDtqpWPGPB9qBuZnZDrKQjIdJtkN7ZAoVwhT6H2o2kE=
gitlab.com/gitlab-org/labkit v1.16.0 h1:Vm3NAMZ8RqAunXlvPWby3GJ2R35vsYGP6Uu0YjyMIlY=