Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-10-11 18:09:43 +00:00
parent 14ae125e1c
commit 6e7be08ca5
62 changed files with 552 additions and 177 deletions

View file

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

View file

@ -161,7 +161,7 @@ function mountAssigneesComponent() {
fullPath,
issuableType,
issuableId: id,
allowMultipleAssignees: !el.dataset.maxAssignees,
allowMultipleAssignees: !el.dataset.maxAssignees || el.dataset.maxAssignees > 1,
editable,
},
scopedSlots: {

View file

@ -243,6 +243,7 @@ export default {
variant="confirm"
category="primary"
size="small"
data-qa-selector="dismiss_suggestion_popover_button"
@click="handleSuggestDismissed"
>
{{ __('Got it') }}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -3039,6 +3039,11 @@ class Project < ApplicationRecord
pages_domains.count < Gitlab::CurrentSettings.max_pages_custom_domains_per_project
end
# overridden in EE
def can_suggest_reviewers?
false
end
# overridden in EE
def suggested_reviewers_available?
false

View file

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

View file

@ -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
true
def invalid_assignees?(update_params)
return false unless Feature.enabled?(:limit_assignees_per_issuable)
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)

View file

@ -30,7 +30,6 @@ module Projects
end
def use_gitlab_service?
Feature.enabled?(:container_registry_new_cleanup_service, project) &&
container_repository.migrated? &&
container_repository.gitlab_api_client.supports_gitlab_api?
end

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -6,35 +6,85 @@ RSpec.describe FormHelper do
include Devise::Test::ControllerHelpers
describe '#dropdown_max_select' do
context "with the :limit_reviewer_and_assignee_size feature flag on" do
it 'correctly returns the max amount of reviewers or assignees to allow' do
max = MergeRequest::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS
let(:feature_flag) { :limit_reviewer_and_assignee_size }
expect(helper.dropdown_max_select({}))
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 = Issuable::MAX_NUMBER_OF_ASSIGNEES_OR_REVIEWERS
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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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