Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
4e0a9fbb9b
commit
cea1ec3ed5
|
|
@ -23,10 +23,6 @@ discover_duo_pro_hand_raise_lead_data:
|
|||
file: ee/app/helpers/gitlab_subscriptions/hand_raise_leads_helper.rb
|
||||
buy_addon_data:
|
||||
file: ee/app/helpers/subscriptions_helper.rb
|
||||
group_icon:
|
||||
file: app/helpers/avatars_helper.rb
|
||||
topic_icon:
|
||||
file: app/helpers/avatars_helper.rb
|
||||
can_view_namespace_catalog?:
|
||||
file: app/helpers/ci/catalog/resources_helper.rb
|
||||
js_ci_catalog_data:
|
||||
|
|
|
|||
12
.rubocop.yml
12
.rubocop.yml
|
|
@ -570,6 +570,18 @@ Gitlab/AvoidCurrentOrganization:
|
|||
- 'spec/**/*'
|
||||
- 'ee/spec/**/*'
|
||||
|
||||
Gitlab/DisallowCurrentOrganizationIdSafeNavigation:
|
||||
Description: 'Use `Current.organization.id` instead of `Current.organization&.id`. `Current.organization` is expected to be assigned.'
|
||||
Enabled: true
|
||||
Include:
|
||||
- 'app/**/*.rb'
|
||||
- 'ee/app/**/*.rb'
|
||||
- 'lib/**/*.rb'
|
||||
- 'ee/lib/**/*.rb'
|
||||
Exclude:
|
||||
# Exclude the cop's own spec file to prevent self-reporting.
|
||||
- 'spec/rubocop/cop/gitlab/disallow_current_organization_id_safe_navigation_spec.rb'
|
||||
|
||||
# See https://gitlab.com/groups/gitlab-org/-/epics/7374
|
||||
Gitlab/AvoidGitlabInstanceChecks:
|
||||
Enabled: true
|
||||
|
|
|
|||
|
|
@ -60,6 +60,10 @@
|
|||
"MergeRequest",
|
||||
"WorkItemWidgetCurrentUserTodos"
|
||||
],
|
||||
"CustomRoleInterface": [
|
||||
"AdminMemberRole",
|
||||
"MemberRole"
|
||||
],
|
||||
"DependencyInterface": [
|
||||
"Dependency",
|
||||
"DependencyAggregation"
|
||||
|
|
@ -144,6 +148,10 @@
|
|||
"PendingProjectMember",
|
||||
"ProjectMember"
|
||||
],
|
||||
"MemberRoleInterface": [
|
||||
"MemberRole",
|
||||
"StandardRole"
|
||||
],
|
||||
"NamespaceUnion": [
|
||||
"CiDeletedNamespace",
|
||||
"Namespace"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
fragment ContainerRepositoryTagProtection on ContainerProtectionAccessLevel {
|
||||
minimumAccessLevelForPush
|
||||
minimumAccessLevelForDelete
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
|
||||
#import "ee_else_ce/packages_and_registries/container_registry/explorer/graphql/fragments/container_repository_tag_protection.fragment.graphql"
|
||||
|
||||
query getContainerRepositoryTags(
|
||||
$id: ContainerRepositoryID!
|
||||
|
|
@ -40,9 +41,7 @@ query getContainerRepositoryTags(
|
|||
destroyContainerRepositoryTag
|
||||
}
|
||||
protection {
|
||||
minimumAccessLevelForPush
|
||||
minimumAccessLevelForDelete
|
||||
immutable
|
||||
...ContainerRepositoryTagProtection
|
||||
}
|
||||
referrers {
|
||||
artifactType
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
fragment ContainerProtectionTagRuleFragment on ContainerProtectionTagRule {
|
||||
id
|
||||
}
|
||||
|
|
@ -1,16 +1,17 @@
|
|||
#import "ee_else_ce/packages_and_registries/settings/project/graphql/fragments/container_protection_tag_rule.fragment.graphql"
|
||||
|
||||
query getProjectContainerProtectionTagRules($projectPath: ID!, $first: Int) {
|
||||
project(fullPath: $projectPath) {
|
||||
id
|
||||
containerProtectionTagRules(first: $first) {
|
||||
nodes {
|
||||
id
|
||||
tagNamePattern
|
||||
immutable
|
||||
minimumAccessLevelForPush
|
||||
minimumAccessLevelForDelete
|
||||
userPermissions {
|
||||
destroyContainerRegistryProtectionTagRule
|
||||
}
|
||||
...ContainerProtectionTagRuleFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -224,19 +224,19 @@ class ProjectsFinder < UnionFinder
|
|||
def by_marked_for_deletion_on(items)
|
||||
return items unless params[:marked_for_deletion_on].present?
|
||||
|
||||
items.by_marked_for_deletion_on(params[:marked_for_deletion_on])
|
||||
items.marked_for_deletion_on(params[:marked_for_deletion_on])
|
||||
end
|
||||
|
||||
def by_aimed_for_deletion(items)
|
||||
if ::Gitlab::Utils.to_boolean(params[:aimed_for_deletion])
|
||||
items.aimed_for_deletion(Date.current)
|
||||
items.self_or_ancestors_aimed_for_deletion
|
||||
else
|
||||
items
|
||||
end
|
||||
end
|
||||
|
||||
def by_not_aimed_for_deletion(items)
|
||||
params[:not_aimed_for_deletion].present? ? items.not_aimed_for_deletion : items
|
||||
params[:not_aimed_for_deletion].present? ? items.self_and_ancestors_not_aimed_for_deletion : items
|
||||
end
|
||||
|
||||
def by_last_activity_after(items)
|
||||
|
|
@ -280,14 +280,14 @@ class ProjectsFinder < UnionFinder
|
|||
|
||||
def by_archived(projects)
|
||||
if params[:non_archived]
|
||||
projects.non_archived
|
||||
projects.self_and_ancestors_non_archived
|
||||
elsif params.key?(:archived) && !params[:archived].nil?
|
||||
if params[:archived] == 'only'
|
||||
projects.archived
|
||||
projects.self_or_ancestors_archived
|
||||
elsif Gitlab::Utils.to_boolean(params[:archived])
|
||||
projects
|
||||
else
|
||||
projects.non_archived
|
||||
projects.self_and_ancestors_non_archived
|
||||
end
|
||||
else
|
||||
projects
|
||||
|
|
@ -310,15 +310,7 @@ class ProjectsFinder < UnionFinder
|
|||
def by_active(items)
|
||||
return items if params[:active].nil?
|
||||
|
||||
params[:active] ? active(items) : inactive(items)
|
||||
end
|
||||
|
||||
def active(items)
|
||||
items.non_archived.not_aimed_for_deletion
|
||||
end
|
||||
|
||||
def inactive(items)
|
||||
items.archived.or(items.aimed_for_deletion(Date.current))
|
||||
params[:active] ? items.self_and_ancestors_active : items.self_or_ancestors_inactive
|
||||
end
|
||||
|
||||
def finder_params
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ module Mutations
|
|||
|
||||
argument :minimum_access_level_for_delete,
|
||||
Types::ContainerRegistry::Protection::TagRuleAccessLevelEnum,
|
||||
required: false,
|
||||
required: true,
|
||||
description: copy_field_description(
|
||||
Types::ContainerRegistry::Protection::TagRuleType,
|
||||
:minimum_access_level_for_delete
|
||||
|
|
@ -37,7 +37,7 @@ module Mutations
|
|||
|
||||
argument :minimum_access_level_for_push,
|
||||
Types::ContainerRegistry::Protection::TagRuleAccessLevelEnum,
|
||||
required: false,
|
||||
required: true,
|
||||
description: copy_field_description(
|
||||
Types::ContainerRegistry::Protection::TagRuleType,
|
||||
:minimum_access_level_for_push
|
||||
|
|
@ -65,3 +65,5 @@ module Mutations
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
Mutations::ContainerRegistry::Protection::TagRule::Create.prepend_mod
|
||||
|
|
|
|||
|
|
@ -8,29 +8,22 @@ module Types
|
|||
|
||||
field :minimum_access_level_for_delete,
|
||||
Types::ContainerRegistry::Protection::TagRuleAccessLevelEnum,
|
||||
null: true,
|
||||
null: false,
|
||||
experiment: { milestone: '17.8' },
|
||||
description:
|
||||
'Minimum GitLab access level required to delete container image tags from the container repository. ' \
|
||||
'Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. ' \
|
||||
'If the value is `nil`, no access level can delete tags. '
|
||||
'Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. '
|
||||
|
||||
field :minimum_access_level_for_push,
|
||||
Types::ContainerRegistry::Protection::TagRuleAccessLevelEnum,
|
||||
null: true,
|
||||
null: false,
|
||||
experiment: { milestone: '17.8' },
|
||||
description:
|
||||
'Minimum GitLab access level required to push container image tags to the container repository. ' \
|
||||
'Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. ' \
|
||||
'If the value is `nil`, no access level can push tags. '
|
||||
|
||||
field :immutable,
|
||||
GraphQL::Types::Boolean,
|
||||
null: false,
|
||||
method: :immutable?,
|
||||
experiment: { milestone: '17.11' },
|
||||
description: 'Returns true when tag rule is for tag immutability. Otherwise, false.'
|
||||
'Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. '
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Types::ContainerRegistry::Protection::AccessLevelInterface.prepend_mod
|
||||
|
|
|
|||
|
|
@ -876,12 +876,8 @@ module Types
|
|||
end
|
||||
|
||||
def container_protection_tag_rules
|
||||
rules = object.container_registry_protection_tag_rules
|
||||
|
||||
return rules.mutable unless Feature.enabled?(:container_registry_immutable_tags, object)
|
||||
|
||||
# mutable tag rules come first before immutable
|
||||
rules.mutable + rules.immutable
|
||||
# Immutable tag rules are added in EE extension
|
||||
object.container_registry_protection_tag_rules.mutable
|
||||
end
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,14 +3,6 @@
|
|||
module AvatarsHelper
|
||||
DEFAULT_AVATAR_PATH = 'no_avatar.png'
|
||||
|
||||
def group_icon(group, options = {})
|
||||
source_icon(group, options)
|
||||
end
|
||||
|
||||
def topic_icon(topic, options = {})
|
||||
source_icon(topic, options)
|
||||
end
|
||||
|
||||
# Takes both user and email and returns the avatar_icon by
|
||||
# user (preferred) or email.
|
||||
def avatar_icon_for(user = nil, email = nil, size = nil, scale = 2, only_path: true)
|
||||
|
|
|
|||
|
|
@ -658,13 +658,30 @@ class Project < ApplicationRecord
|
|||
scope :not_hidden, -> { where(hidden: false) }
|
||||
scope :not_in_groups, ->(groups) { where.not(group: groups) }
|
||||
scope :by_not_in_root_id, ->(root_id) { joins(:project_namespace).where('namespaces.traversal_ids[1] NOT IN (?)', root_id) }
|
||||
|
||||
scope :aimed_for_deletion, -> { where.not(marked_for_deletion_at: nil).without_deleted }
|
||||
scope :self_or_ancestors_aimed_for_deletion, -> do
|
||||
left_joins(:group)
|
||||
.where.not(marked_for_deletion_at: nil)
|
||||
.or(where(Group.self_or_ancestors_deletion_schedule_subquery.exists))
|
||||
.without_deleted
|
||||
end
|
||||
|
||||
scope :not_aimed_for_deletion, -> { where(marked_for_deletion_at: nil).without_deleted }
|
||||
scope :aimed_for_deletion, ->(date) { where('marked_for_deletion_at <= ?', date).without_deleted }
|
||||
scope :with_deleting_user, -> { includes(:deleting_user) }
|
||||
scope :by_marked_for_deletion_on, ->(marked_for_deletion_on) do
|
||||
scope :self_and_ancestors_not_aimed_for_deletion, -> do
|
||||
left_joins(:group)
|
||||
.where(marked_for_deletion_at: nil)
|
||||
.where.not(Group.self_or_ancestors_deletion_schedule_subquery.exists)
|
||||
.without_deleted
|
||||
end
|
||||
|
||||
scope :marked_for_deletion_before, ->(date) { where('marked_for_deletion_at <= ?', date).without_deleted }
|
||||
scope :marked_for_deletion_on, ->(marked_for_deletion_on) do
|
||||
where(marked_for_deletion_at: marked_for_deletion_on)
|
||||
end
|
||||
|
||||
scope :with_deleting_user, -> { includes(:deleting_user) }
|
||||
|
||||
scope :with_storage_feature, ->(feature) do
|
||||
where(arel_table[:storage_version].gteq(HASHED_STORAGE_FEATURES[feature]))
|
||||
end
|
||||
|
|
@ -798,8 +815,24 @@ class Project < ApplicationRecord
|
|||
scope :starred_by, ->(user) { joins(:users_star_projects).where('users_star_projects.user_id': user.id) }
|
||||
scope :visible_to_user, ->(user) { where(id: user.authorized_projects.select(:id).reorder(nil)) }
|
||||
scope :visible_to_user_and_access_level, ->(user, access_level) { where(id: user.authorized_projects.where('project_authorizations.access_level >= ?', access_level).select(:id).reorder(nil)) }
|
||||
|
||||
scope :archived, -> { where(archived: true) }
|
||||
scope :self_or_ancestors_archived, -> do
|
||||
left_joins(:group)
|
||||
.where(archived: true)
|
||||
.or(where(Group.self_or_ancestors_archived_setting_subquery.exists))
|
||||
end
|
||||
|
||||
scope :non_archived, -> { where(archived: false) }
|
||||
scope :self_and_ancestors_non_archived, -> do
|
||||
left_joins(:group)
|
||||
.where(archived: false)
|
||||
.where.not(Group.self_or_ancestors_archived_setting_subquery.exists)
|
||||
end
|
||||
|
||||
scope :self_and_ancestors_active, -> { self_and_ancestors_non_archived.self_and_ancestors_not_aimed_for_deletion }
|
||||
scope :self_or_ancestors_inactive, -> { self_or_ancestors_archived.or(self_or_ancestors_aimed_for_deletion) }
|
||||
|
||||
scope :with_push, -> { joins(:events).merge(Event.pushed_action) }
|
||||
scope :with_project_feature, -> { joins('LEFT JOIN project_features ON projects.id = project_features.project_id') }
|
||||
scope :with_jira_dvcs_server, -> { joins(:feature_usage).merge(ProjectFeatureUsage.with_jira_dvcs_integration_enabled(cloud: false)) }
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class AdjournedProjectsDeletionCronWorker
|
|||
def perform
|
||||
deletion_cutoff = Gitlab::CurrentSettings.deletion_adjourned_period.days.ago.to_date
|
||||
|
||||
Project.with_route.with_deleting_user.aimed_for_deletion(deletion_cutoff).find_each(batch_size: 100).with_index do |project, index| # rubocop: disable CodeReuse/ActiveRecord -- existing class moved from EE to CE
|
||||
Project.with_route.with_deleting_user.marked_for_deletion_before(deletion_cutoff).find_each(batch_size: 100).with_index do |project, index| # rubocop: disable CodeReuse/ActiveRecord -- existing class moved from EE to CE
|
||||
delay = index * INTERVAL
|
||||
|
||||
with_context(project: project, user: project.deleting_user) do
|
||||
|
|
|
|||
|
|
@ -21,11 +21,11 @@ additional_properties:
|
|||
property:
|
||||
description: "a UUID that identifies a scan"
|
||||
value:
|
||||
description: "exit status of the analyer where 0 indicates success and 1 indicates error"
|
||||
description: "exit status of the analyzer where 0 indicates success and 1 indicates error"
|
||||
version:
|
||||
description: "version of the analyzer"
|
||||
exit_code:
|
||||
description: "exit code of the analyer"
|
||||
description: "exit code of the analyzer"
|
||||
override_count:
|
||||
description: "number of configured overrides"
|
||||
passthrough_count:
|
||||
|
|
@ -36,3 +36,5 @@ additional_properties:
|
|||
description: "scan time duration in seconds"
|
||||
file_count:
|
||||
description: "project size in terms of number of files"
|
||||
language_feature_usage:
|
||||
description: "counts of programming language features used in the project"
|
||||
|
|
|
|||
|
|
@ -639,6 +639,8 @@
|
|||
- 1
|
||||
- - namespaces_cascade_duo_features_enabled
|
||||
- 1
|
||||
- - namespaces_cascade_web_based_commit_signing_enabled
|
||||
- 1
|
||||
- - namespaces_free_user_cap_group_over_limit_notification
|
||||
- 1
|
||||
- - namespaces_process_sync_events
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddCreatedByIdToCustomStatuses < Gitlab::Database::Migration[2.3]
|
||||
milestone '18.1'
|
||||
|
||||
def change
|
||||
add_column :work_item_custom_statuses, :created_by_id, :bigint
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddCreatedByIdIndexToCustomStatuses < Gitlab::Database::Migration[2.3]
|
||||
disable_ddl_transaction!
|
||||
milestone '18.1'
|
||||
|
||||
INDEX_NAME = 'index_work_item_custom_statuses_on_created_by_id'
|
||||
|
||||
def up
|
||||
add_concurrent_index :work_item_custom_statuses, :created_by_id, name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :work_item_custom_statuses, name: INDEX_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddCreatedByForeignKeyToCustomStatuses < Gitlab::Database::Migration[2.3]
|
||||
disable_ddl_transaction!
|
||||
milestone '18.1'
|
||||
|
||||
def up
|
||||
add_concurrent_foreign_key :work_item_custom_statuses, :users,
|
||||
column: :created_by_id, on_delete: :nullify
|
||||
end
|
||||
|
||||
def down
|
||||
remove_foreign_key_if_exists :work_item_custom_statuses, column: :created_by_id
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddCreatedByIdToCustomLifecycles < Gitlab::Database::Migration[2.3]
|
||||
milestone '18.1'
|
||||
|
||||
def change
|
||||
add_column :work_item_custom_lifecycles, :created_by_id, :bigint
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddCreatedByIdIndexToCustomLifecycles < Gitlab::Database::Migration[2.3]
|
||||
disable_ddl_transaction!
|
||||
milestone '18.1'
|
||||
|
||||
INDEX_NAME = 'index_work_item_custom_lifecycles_on_created_by_id'
|
||||
|
||||
def up
|
||||
add_concurrent_index :work_item_custom_lifecycles, :created_by_id, name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :work_item_custom_lifecycles, name: INDEX_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddCreatedByForeignKeyToCustomLifecycles < Gitlab::Database::Migration[2.3]
|
||||
disable_ddl_transaction!
|
||||
milestone '18.1'
|
||||
|
||||
def up
|
||||
add_concurrent_foreign_key :work_item_custom_lifecycles, :users,
|
||||
column: :created_by_id, on_delete: :nullify
|
||||
end
|
||||
|
||||
def down
|
||||
remove_foreign_key_if_exists :work_item_custom_lifecycles, column: :created_by_id
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddUpdatedByIdToCustomStatuses < Gitlab::Database::Migration[2.3]
|
||||
milestone '18.1'
|
||||
|
||||
def change
|
||||
add_column :work_item_custom_statuses, :updated_by_id, :bigint
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddUpdatedByIdIndexToCustomStatuses < Gitlab::Database::Migration[2.3]
|
||||
disable_ddl_transaction!
|
||||
milestone '18.1'
|
||||
|
||||
INDEX_NAME = 'index_work_item_custom_statuses_on_updated_by_id'
|
||||
|
||||
def up
|
||||
add_concurrent_index :work_item_custom_statuses, :updated_by_id, name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :work_item_custom_statuses, name: INDEX_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddUpdatedByForeignKeyToCustomStatuses < Gitlab::Database::Migration[2.3]
|
||||
disable_ddl_transaction!
|
||||
milestone '18.1'
|
||||
|
||||
def up
|
||||
add_concurrent_foreign_key :work_item_custom_statuses, :users,
|
||||
column: :updated_by_id, on_delete: :nullify
|
||||
end
|
||||
|
||||
def down
|
||||
remove_foreign_key_if_exists :work_item_custom_statuses, column: :updated_by_id
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddUpdatedByIdToCustomLifecycles < Gitlab::Database::Migration[2.3]
|
||||
milestone '18.1'
|
||||
|
||||
def change
|
||||
add_column :work_item_custom_lifecycles, :updated_by_id, :bigint
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddUpdatedByIdIndexToCustomLifecycles < Gitlab::Database::Migration[2.3]
|
||||
disable_ddl_transaction!
|
||||
milestone '18.1'
|
||||
|
||||
INDEX_NAME = 'index_work_item_custom_lifecycles_on_updated_by_id'
|
||||
|
||||
def up
|
||||
add_concurrent_index :work_item_custom_lifecycles, :updated_by_id, name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :work_item_custom_lifecycles, name: INDEX_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddUpdatedByForeignKeyToCustomLifecycles < Gitlab::Database::Migration[2.3]
|
||||
disable_ddl_transaction!
|
||||
milestone '18.1'
|
||||
|
||||
def up
|
||||
add_concurrent_foreign_key :work_item_custom_lifecycles, :users,
|
||||
column: :updated_by_id, on_delete: :nullify
|
||||
end
|
||||
|
||||
def down
|
||||
remove_foreign_key_if_exists :work_item_custom_lifecycles, column: :updated_by_id
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RemoveIdxCiJobVariablesOnPartitionIdJobId < Gitlab::Database::Migration[2.3]
|
||||
milestone '18.1'
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
TABLE_NAME = :ci_job_variables
|
||||
INDEX_NAME = :index_ci_job_variables_on_partition_id_job_id
|
||||
COLUMNS = [:partition_id, :job_id]
|
||||
|
||||
def up
|
||||
remove_concurrent_index_by_name TABLE_NAME, name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
add_concurrent_index TABLE_NAME, COLUMNS, name: INDEX_NAME
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RemoveProjectFingerprintFromSecurityFindings < Gitlab::Database::Migration[2.3]
|
||||
include Gitlab::Database::PartitioningMigrationHelpers
|
||||
|
||||
disable_ddl_transaction!
|
||||
milestone '18.1'
|
||||
|
||||
def up
|
||||
remove_check_constraint :security_findings, 'check_b9508c6df8'
|
||||
remove_column :security_findings, :project_fingerprint
|
||||
end
|
||||
|
||||
def down
|
||||
add_column :security_findings, :project_fingerprint, :text, if_not_exists: true
|
||||
add_check_constraint :security_findings, 'char_length(project_fingerprint) <= 40', 'check_b9508c6df8'
|
||||
add_concurrent_partitioned_index :security_findings, :project_fingerprint,
|
||||
name: 'security_findings_project_fingerprint_idx'
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
8dc9aa1a310798758d7b115d50d20142af56c35af11ada707708a8d521389021
|
||||
|
|
@ -0,0 +1 @@
|
|||
363c5d217044ac211c3bdadf3d8d2e582f4c1c8c705ab5b6d2d141c18431e861
|
||||
|
|
@ -0,0 +1 @@
|
|||
76f7874268107ae4a9d8bdbbfcdf522a869726b67a87a95ca184fa0409519c83
|
||||
|
|
@ -0,0 +1 @@
|
|||
ccb4e487017f8742eaffb87048a6ea7c6b2bbb59a31b276abef1c4b921725f73
|
||||
|
|
@ -0,0 +1 @@
|
|||
cc592eedda6586088a1049c9ca27618a73b50281335ccac5ea4c425c0c5f9444
|
||||
|
|
@ -0,0 +1 @@
|
|||
e3a4587ea36f0c4b266a8e908b73e867e5df0afefa202417a439cf099a112e63
|
||||
|
|
@ -0,0 +1 @@
|
|||
a3f50469cd58afe7fb6012ff7b27eef52e45c966f7cffb0516d499b70b0de666
|
||||
|
|
@ -0,0 +1 @@
|
|||
f0c69d5d3bfdcc30df065db0c658ee86bbe4e28dc4eb50233308d947faaf0b3f
|
||||
|
|
@ -0,0 +1 @@
|
|||
4a373f4a79e4bf14b5509ce63a9fb79267246b2dfc03c83d20868142f6eaca9d
|
||||
|
|
@ -0,0 +1 @@
|
|||
5674253dd6f59541e4a396635aceffd7963bf4fef38bbebb58851ecd8b10b1ee
|
||||
|
|
@ -0,0 +1 @@
|
|||
e0ae0be50f1fd197267e524d0bed195be8692dec84c3ddb24ad5bf754fa26a02
|
||||
|
|
@ -0,0 +1 @@
|
|||
d8de1cea7fdafa9d31cab776eff7ea66015d253abed20056cff1fcfd4334561b
|
||||
|
|
@ -0,0 +1 @@
|
|||
a05765899ad07672f1c007ac86087e799aeb507328675566585cfd684878b4ba
|
||||
|
|
@ -0,0 +1 @@
|
|||
1826f2a6ec247577a18af2a0c6d14713ef817760ca0607c0a8f49badce883504
|
||||
|
|
@ -4965,15 +4965,13 @@ CREATE TABLE security_findings (
|
|||
scan_id bigint NOT NULL,
|
||||
scanner_id bigint NOT NULL,
|
||||
severity smallint NOT NULL,
|
||||
project_fingerprint text,
|
||||
deduplicated boolean DEFAULT false NOT NULL,
|
||||
uuid uuid,
|
||||
overridden_uuid uuid,
|
||||
partition_number integer DEFAULT 1 NOT NULL,
|
||||
finding_data jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
project_id bigint,
|
||||
CONSTRAINT check_6c2851a8c9 CHECK ((uuid IS NOT NULL)),
|
||||
CONSTRAINT check_b9508c6df8 CHECK ((char_length(project_fingerprint) <= 40))
|
||||
CONSTRAINT check_6c2851a8c9 CHECK ((uuid IS NOT NULL))
|
||||
)
|
||||
PARTITION BY LIST (partition_number);
|
||||
|
||||
|
|
@ -25788,6 +25786,8 @@ CREATE TABLE work_item_custom_lifecycles (
|
|||
created_at timestamp with time zone NOT NULL,
|
||||
updated_at timestamp with time zone NOT NULL,
|
||||
name text NOT NULL,
|
||||
created_by_id bigint,
|
||||
updated_by_id bigint,
|
||||
CONSTRAINT check_1feff2de99 CHECK ((char_length(name) <= 255))
|
||||
);
|
||||
|
||||
|
|
@ -25809,6 +25809,8 @@ CREATE TABLE work_item_custom_statuses (
|
|||
name text NOT NULL,
|
||||
description text,
|
||||
color text NOT NULL,
|
||||
created_by_id bigint,
|
||||
updated_by_id bigint,
|
||||
CONSTRAINT check_4789467800 CHECK ((char_length(color) <= 7)),
|
||||
CONSTRAINT check_720a7c4d24 CHECK ((char_length(name) <= 255)),
|
||||
CONSTRAINT check_8ea8b3c991 CHECK ((char_length(description) <= 255)),
|
||||
|
|
@ -34438,8 +34440,6 @@ CREATE INDEX index_ci_job_variables_on_job_id ON ci_job_variables USING btree (j
|
|||
|
||||
CREATE UNIQUE INDEX index_ci_job_variables_on_key_and_job_id ON ci_job_variables USING btree (key, job_id);
|
||||
|
||||
CREATE INDEX index_ci_job_variables_on_partition_id_job_id ON ci_job_variables USING btree (partition_id, job_id);
|
||||
|
||||
CREATE INDEX index_ci_job_variables_on_project_id ON ci_job_variables USING btree (project_id);
|
||||
|
||||
CREATE INDEX index_ci_minutes_additional_packs_on_namespace_id_purchase_xid ON ci_minutes_additional_packs USING btree (namespace_id, purchase_xid);
|
||||
|
|
@ -38204,10 +38204,18 @@ CREATE INDEX index_work_item_current_statuses_on_namespace_id ON work_item_curre
|
|||
|
||||
CREATE UNIQUE INDEX index_work_item_current_statuses_on_work_item_id ON work_item_current_statuses USING btree (work_item_id);
|
||||
|
||||
CREATE INDEX index_work_item_custom_lifecycles_on_created_by_id ON work_item_custom_lifecycles USING btree (created_by_id);
|
||||
|
||||
CREATE UNIQUE INDEX index_work_item_custom_lifecycles_on_namespace_id_and_name ON work_item_custom_lifecycles USING btree (namespace_id, name);
|
||||
|
||||
CREATE INDEX index_work_item_custom_lifecycles_on_updated_by_id ON work_item_custom_lifecycles USING btree (updated_by_id);
|
||||
|
||||
CREATE INDEX index_work_item_custom_statuses_on_created_by_id ON work_item_custom_statuses USING btree (created_by_id);
|
||||
|
||||
CREATE UNIQUE INDEX index_work_item_custom_statuses_on_namespace_id_and_lower_name ON work_item_custom_statuses USING btree (namespace_id, TRIM(BOTH FROM lower(name)));
|
||||
|
||||
CREATE INDEX index_work_item_custom_statuses_on_updated_by_id ON work_item_custom_statuses USING btree (updated_by_id);
|
||||
|
||||
CREATE INDEX index_work_item_hierarchy_restrictions_on_child_type_id ON work_item_hierarchy_restrictions USING btree (child_type_id);
|
||||
|
||||
CREATE UNIQUE INDEX index_work_item_hierarchy_restrictions_on_parent_and_child ON work_item_hierarchy_restrictions USING btree (parent_type_id, child_type_id);
|
||||
|
|
@ -38708,8 +38716,6 @@ CREATE INDEX scan_finding_approval_project_rule_index_created_at_project_id ON a
|
|||
|
||||
CREATE INDEX scan_finding_approval_project_rule_index_project_id ON approval_project_rules USING btree (project_id) WHERE (report_type = 4);
|
||||
|
||||
CREATE INDEX security_findings_project_fingerprint_idx ON ONLY security_findings USING btree (project_fingerprint);
|
||||
|
||||
CREATE INDEX security_findings_scan_id_deduplicated_idx ON ONLY security_findings USING btree (scan_id, deduplicated);
|
||||
|
||||
CREATE INDEX security_findings_scan_id_id_idx ON ONLY security_findings USING btree (scan_id, id);
|
||||
|
|
@ -42339,6 +42345,9 @@ ALTER TABLE ONLY deployment_approvals
|
|||
ALTER TABLE ONLY bulk_import_trackers
|
||||
ADD CONSTRAINT fk_2d0e051bc3 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY work_item_custom_lifecycles
|
||||
ADD CONSTRAINT fk_2d0f7ebf48 FOREIGN KEY (updated_by_id) REFERENCES users(id) ON DELETE SET NULL;
|
||||
|
||||
ALTER TABLE ONLY audit_events_instance_external_audit_event_destinations
|
||||
ADD CONSTRAINT fk_2d3ebd0fbc FOREIGN KEY (stream_destination_id) REFERENCES audit_events_instance_external_streaming_destinations(id) ON DELETE SET NULL;
|
||||
|
||||
|
|
@ -42708,6 +42717,9 @@ ALTER TABLE ONLY deploy_keys_projects
|
|||
ALTER TABLE ONLY merge_requests_approval_rules_groups
|
||||
ADD CONSTRAINT fk_59068f09e5 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY work_item_custom_statuses
|
||||
ADD CONSTRAINT fk_590e87b7c7 FOREIGN KEY (updated_by_id) REFERENCES users(id) ON DELETE SET NULL;
|
||||
|
||||
ALTER TABLE ONLY oauth_access_grants
|
||||
ADD CONSTRAINT fk_59cdb2323c FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
|
||||
|
||||
|
|
@ -42777,6 +42789,9 @@ ALTER TABLE ONLY user_achievements
|
|||
ALTER TABLE ONLY merge_requests
|
||||
ADD CONSTRAINT fk_6149611a04 FOREIGN KEY (assignee_id) REFERENCES users(id) ON DELETE SET NULL;
|
||||
|
||||
ALTER TABLE ONLY work_item_custom_lifecycles
|
||||
ADD CONSTRAINT fk_614a3cdb95 FOREIGN KEY (created_by_id) REFERENCES users(id) ON DELETE SET NULL;
|
||||
|
||||
ALTER TABLE ONLY member_approvals
|
||||
ADD CONSTRAINT fk_619f381144 FOREIGN KEY (member_role_id) REFERENCES member_roles(id) ON DELETE SET NULL;
|
||||
|
||||
|
|
@ -44139,6 +44154,9 @@ ALTER TABLE ONLY merge_requests_approval_rules
|
|||
ALTER TABLE ONLY clusters_managed_resources
|
||||
ADD CONSTRAINT fk_fad3c3b2e2 FOREIGN KEY (environment_id) REFERENCES environments(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY work_item_custom_statuses
|
||||
ADD CONSTRAINT fk_fb28a15e7b FOREIGN KEY (created_by_id) REFERENCES users(id) ON DELETE SET NULL;
|
||||
|
||||
ALTER TABLE ONLY agent_group_authorizations
|
||||
ADD CONSTRAINT fk_fb70782616 FOREIGN KEY (agent_id) REFERENCES cluster_agents(id) ON DELETE CASCADE;
|
||||
|
||||
|
|
|
|||
|
|
@ -4261,8 +4261,8 @@ Input type: `createContainerProtectionTagRuleInput`
|
|||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="mutationcreatecontainerprotectiontagruleclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
|
||||
| <a id="mutationcreatecontainerprotectiontagruleminimumaccesslevelfordelete"></a>`minimumAccessLevelForDelete` | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | Minimum GitLab access level required to delete container image tags from the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, no access level can delete tags. Introduced in GitLab 17.8: **Status**: Experiment. |
|
||||
| <a id="mutationcreatecontainerprotectiontagruleminimumaccesslevelforpush"></a>`minimumAccessLevelForPush` | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | Minimum GitLab access level required to push container image tags to the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, no access level can push tags. Introduced in GitLab 17.8: **Status**: Experiment. |
|
||||
| <a id="mutationcreatecontainerprotectiontagruleminimumaccesslevelfordelete"></a>`minimumAccessLevelForDelete` | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | Minimum GitLab access level required to delete container image tags from the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. Introduced in GitLab 17.8: **Status**: Experiment. If the value is `nil`, no access level can delete tags. |
|
||||
| <a id="mutationcreatecontainerprotectiontagruleminimumaccesslevelforpush"></a>`minimumAccessLevelForPush` | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | Minimum GitLab access level required to push container image tags to the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. Introduced in GitLab 17.8: **Status**: Experiment. If the value is `nil`, no access level can push tags. |
|
||||
| <a id="mutationcreatecontainerprotectiontagruleprojectpath"></a>`projectPath` | [`ID!`](#id) | Full path of the project containing the container image tags. |
|
||||
| <a id="mutationcreatecontainerprotectiontagruletagnamepattern"></a>`tagNamePattern` | [`String!`](#string) | The pattern that matches container image tags to protect. For example, `v1.*`. Wildcard character `*` allowed. Introduced in GitLab 17.8: **Status**: Experiment. |
|
||||
|
||||
|
|
@ -21197,9 +21197,9 @@ Represents an admin member role.
|
|||
| <a id="adminmemberroleeditpath"></a>`editPath` {{< icon name="warning-solid" >}} | [`String!`](#string) | **Introduced** in GitLab 16.11. **Status**: Experiment. Web UI path to edit the custom role. |
|
||||
| <a id="adminmemberroleenabledpermissions"></a>`enabledPermissions` {{< icon name="warning-solid" >}} | [`CustomizableAdminPermissionConnection!`](#customizableadminpermissionconnection) | **Introduced** in GitLab 17.7. **Status**: Experiment. Array of all permissions enabled for the custom role. |
|
||||
| <a id="adminmemberroleid"></a>`id` | [`ID!`](#id) | Role ID. |
|
||||
| <a id="adminmemberrolememberscount"></a>`membersCount` {{< icon name="warning-solid" >}} | [`Int`](#int) | **Introduced** in GitLab 17.3. **Status**: Experiment. Number of times the role has been directly assigned to a group or project member. |
|
||||
| <a id="adminmemberroleldapadminrolelinks"></a>`ldapAdminRoleLinks` {{< icon name="warning-solid" >}} | [`LdapAdminRoleLinkConnection`](#ldapadminrolelinkconnection) | **Introduced** in GitLab 18.1. **Status**: Experiment. LDAP admin role sync configurations that will assign the admin member role. |
|
||||
| <a id="adminmemberrolename"></a>`name` | [`String`](#string) | Role name. |
|
||||
| <a id="adminmemberroleuserscount"></a>`usersCount` {{< icon name="warning-solid" >}} | [`Int`](#int) | **Introduced** in GitLab 17.5. **Status**: Experiment. Number of users who have been directly assigned the role in at least one group or project. |
|
||||
| <a id="adminmemberroleuserscount"></a>`usersCount` {{< icon name="warning-solid" >}} | [`Int`](#int) | **Introduced** in GitLab 17.5. **Status**: Experiment. Number of users who have been directly assigned the admin member role. |
|
||||
|
||||
### `AgentConfiguration`
|
||||
|
||||
|
|
@ -24455,8 +24455,8 @@ Represents the most restrictive permissions for a container image tag.
|
|||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="containerprotectionaccesslevelimmutable"></a>`immutable` {{< icon name="warning-solid" >}} | [`Boolean!`](#boolean) | **Introduced** in GitLab 17.11. **Status**: Experiment. Returns true when tag rule is for tag immutability. Otherwise, false. |
|
||||
| <a id="containerprotectionaccesslevelminimumaccesslevelfordelete"></a>`minimumAccessLevelForDelete` {{< icon name="warning-solid" >}} | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to delete container image tags from the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, no access level can delete tags. |
|
||||
| <a id="containerprotectionaccesslevelminimumaccesslevelforpush"></a>`minimumAccessLevelForPush` {{< icon name="warning-solid" >}} | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to push container image tags to the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, no access level can push tags. |
|
||||
| <a id="containerprotectionaccesslevelminimumaccesslevelfordelete"></a>`minimumAccessLevelForDelete` {{< icon name="warning-solid" >}} | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to delete container image tags from the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. |
|
||||
| <a id="containerprotectionaccesslevelminimumaccesslevelforpush"></a>`minimumAccessLevelForPush` {{< icon name="warning-solid" >}} | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to push container image tags to the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. |
|
||||
|
||||
### `ContainerProtectionRepositoryRule`
|
||||
|
||||
|
|
@ -24481,8 +24481,8 @@ A container repository tag protection rule designed to prevent users with a cert
|
|||
| ---- | ---- | ----------- |
|
||||
| <a id="containerprotectiontagruleid"></a>`id` {{< icon name="warning-solid" >}} | [`ContainerRegistryProtectionTagRuleID!`](#containerregistryprotectiontagruleid) | **Introduced** in GitLab 17.8. **Status**: Experiment. ID of the container repository tag protection rule. |
|
||||
| <a id="containerprotectiontagruleimmutable"></a>`immutable` {{< icon name="warning-solid" >}} | [`Boolean!`](#boolean) | **Introduced** in GitLab 17.11. **Status**: Experiment. Returns true when tag rule is for tag immutability. Otherwise, false. |
|
||||
| <a id="containerprotectiontagruleminimumaccesslevelfordelete"></a>`minimumAccessLevelForDelete` {{< icon name="warning-solid" >}} | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to delete container image tags from the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, no access level can delete tags. |
|
||||
| <a id="containerprotectiontagruleminimumaccesslevelforpush"></a>`minimumAccessLevelForPush` {{< icon name="warning-solid" >}} | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to push container image tags to the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, no access level can push tags. |
|
||||
| <a id="containerprotectiontagruleminimumaccesslevelfordelete"></a>`minimumAccessLevelForDelete` {{< icon name="warning-solid" >}} | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to delete container image tags from the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. |
|
||||
| <a id="containerprotectiontagruleminimumaccesslevelforpush"></a>`minimumAccessLevelForPush` {{< icon name="warning-solid" >}} | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to push container image tags to the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. |
|
||||
| <a id="containerprotectiontagruletagnamepattern"></a>`tagNamePattern` {{< icon name="warning-solid" >}} | [`String!`](#string) | **Introduced** in GitLab 17.8. **Status**: Experiment. The pattern that matches container image tags to protect. For example, `v1.*`. Wildcard character `*` allowed. |
|
||||
| <a id="containerprotectiontagruleuserpermissions"></a>`userPermissions` | [`ContainerRegistryProtectionTagRulePermissions!`](#containerregistryprotectiontagrulepermissions) | Permissions for the current user on the resource. |
|
||||
|
||||
|
|
@ -48627,8 +48627,8 @@ Implementations:
|
|||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="accesslevelinterfaceimmutable"></a>`immutable` {{< icon name="warning-solid" >}} | [`Boolean!`](#boolean) | **Introduced** in GitLab 17.11. **Status**: Experiment. Returns true when tag rule is for tag immutability. Otherwise, false. |
|
||||
| <a id="accesslevelinterfaceminimumaccesslevelfordelete"></a>`minimumAccessLevelForDelete` {{< icon name="warning-solid" >}} | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to delete container image tags from the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, no access level can delete tags. |
|
||||
| <a id="accesslevelinterfaceminimumaccesslevelforpush"></a>`minimumAccessLevelForPush` {{< icon name="warning-solid" >}} | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to push container image tags to the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. If the value is `nil`, no access level can push tags. |
|
||||
| <a id="accesslevelinterfaceminimumaccesslevelfordelete"></a>`minimumAccessLevelForDelete` {{< icon name="warning-solid" >}} | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to delete container image tags from the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. |
|
||||
| <a id="accesslevelinterfaceminimumaccesslevelforpush"></a>`minimumAccessLevelForPush` {{< icon name="warning-solid" >}} | [`ContainerProtectionTagRuleAccessLevel`](#containerprotectiontagruleaccesslevel) | **Introduced** in GitLab 17.8. **Status**: Experiment. Minimum GitLab access level required to push container image tags to the container repository. Valid values include `MAINTAINER`, `OWNER`, or `ADMIN`. |
|
||||
|
||||
#### `AlertManagementIntegration`
|
||||
|
||||
|
|
@ -48870,6 +48870,20 @@ four standard [pagination arguments](#pagination-arguments):
|
|||
| ---- | ---- | ----------- |
|
||||
| <a id="currentusertodoscurrentusertodosstate"></a>`state` | [`TodoStateEnum`](#todostateenum) | State of the to-do items. |
|
||||
|
||||
#### `CustomRoleInterface`
|
||||
|
||||
Implementations:
|
||||
|
||||
- [`AdminMemberRole`](#adminmemberrole)
|
||||
- [`MemberRole`](#memberrole)
|
||||
|
||||
##### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="customroleinterfacecreatedat"></a>`createdAt` | [`Time!`](#time) | Timestamp of when the member role was created. |
|
||||
| <a id="customroleinterfaceeditpath"></a>`editPath` {{< icon name="warning-solid" >}} | [`String!`](#string) | **Introduced** in GitLab 16.11. **Status**: Experiment. Web UI path to edit the custom role. |
|
||||
|
||||
#### `DependencyInterface`
|
||||
|
||||
Implementations:
|
||||
|
|
@ -49110,6 +49124,20 @@ Returns [`UserMergeRequestInteraction`](#usermergerequestinteraction).
|
|||
| ---- | ---- | ----------- |
|
||||
| <a id="memberinterfacemergerequestinteractionid"></a>`id` | [`MergeRequestID!`](#mergerequestid) | Global ID of the merge request. |
|
||||
|
||||
#### `MemberRoleInterface`
|
||||
|
||||
Implementations:
|
||||
|
||||
- [`MemberRole`](#memberrole)
|
||||
- [`StandardRole`](#standardrole)
|
||||
|
||||
##### Fields
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="memberroleinterfacememberscount"></a>`membersCount` {{< icon name="warning-solid" >}} | [`Int`](#int) | **Introduced** in GitLab 17.3. **Status**: Experiment. Number of times the role has been directly assigned to a group or project member. |
|
||||
| <a id="memberroleinterfaceuserscount"></a>`usersCount` {{< icon name="warning-solid" >}} | [`Int`](#int) | **Introduced** in GitLab 17.5. **Status**: Experiment. Number of users who have been directly assigned the role in at least one group or project. |
|
||||
|
||||
#### `NamespacesLinkPaths`
|
||||
|
||||
Implementations:
|
||||
|
|
@ -49323,9 +49351,7 @@ Implementations:
|
|||
| <a id="roleinterfacedescription"></a>`description` | [`String`](#string) | Role description. |
|
||||
| <a id="roleinterfacedetailspath"></a>`detailsPath` {{< icon name="warning-solid" >}} | [`String`](#string) | **Introduced** in GitLab 17.4. **Status**: Experiment. URL path to the role details webpage. |
|
||||
| <a id="roleinterfaceid"></a>`id` | [`ID!`](#id) | Role ID. |
|
||||
| <a id="roleinterfacememberscount"></a>`membersCount` {{< icon name="warning-solid" >}} | [`Int`](#int) | **Introduced** in GitLab 17.3. **Status**: Experiment. Number of times the role has been directly assigned to a group or project member. |
|
||||
| <a id="roleinterfacename"></a>`name` | [`String`](#string) | Role name. |
|
||||
| <a id="roleinterfaceuserscount"></a>`usersCount` {{< icon name="warning-solid" >}} | [`Int`](#int) | **Introduced** in GitLab 17.5. **Status**: Experiment. Number of users who have been directly assigned the role in at least one group or project. |
|
||||
|
||||
#### `Service`
|
||||
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ somewhere within the worker:
|
|||
deletion_cutoff = Gitlab::CurrentSettings
|
||||
.deletion_adjourned_period.days.ago.to_date
|
||||
projects = Project.with_route.with_namespace
|
||||
.aimed_for_deletion(deletion_cutoff)
|
||||
.marked_for_deletion_before(deletion_cutoff)
|
||||
|
||||
projects.find_each(batch_size: 100).with_index do |project, index|
|
||||
delay = index * INTERVAL
|
||||
|
|
|
|||
|
|
@ -195,6 +195,11 @@ You can use GitLab compliance controls or external controls for framework requir
|
|||
GitLab compliance controls can be used in GitLab compliance frameworks. Controls are checks against the configuration or
|
||||
behavior of projects that are assigned to a compliance framework.
|
||||
|
||||
Combine GitLab compliance controls to help you meet
|
||||
[compliance standards](compliance_frameworks/compliance_standards.md).
|
||||
|
||||
<!-- Updates to control names must be reflected also in compliance_frameworks/compliance_standards.md -->
|
||||
|
||||
| Control name | Control ID | Description |
|
||||
|:---------------------------------------------------------|:-----------------------------------------------------------|:------------|
|
||||
| API security running | `scanner_api_security_running` | Ensures that [API security scanning](../application_security/api_security/_index.md) is configured and running in the project pipelines. |
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
stage: Software Supply Chain Security
|
||||
group: Compliance
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
|
||||
title: Compliance standards
|
||||
---
|
||||
|
||||
You can use [GitLab compliance controls](../compliance_frameworks.md#gitlab-compliance-controls) to help meet the
|
||||
requirements of many compliance standards.
|
||||
|
||||
## ISO 27001 compliance requirements
|
||||
|
||||
ISO 27001 is an internationally recognized standard that provides a framework for implementing and managing an
|
||||
Information Security Management System (ISMS).
|
||||
|
||||
The following table lists the requirements supported by GitLab for ISO 27001 and the controls for the requirements.
|
||||
|
||||
| ISO 27001 requirement | Description | Supported controls |
|
||||
|:----------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------|
|
||||
| 5.3 Segregation of duties | Conflicting duties and conflicting areas of responsibility shall be segregated. | <ul><li>At least two approvals</li><li>Author approved merge request is forbidden</li><li>Committers approved merge request is forbidden</li><li>Merge requests approval rules prevent editing</li></ul> |
|
||||
| 5.17 Authentication information | Allocation and management of authentication information should be controlled by a management process, including advising personnel on the appropriate handling of authentication information. | <ul><li>Secret detection running</li></ul> |
|
||||
| 5.18 Access rights | Access rights to information and other associated assets should be provisioned, reviewed, modified, and removed in accordance with the organization's topic-specific policy on and rules for access control. | <ul><li>At least two approvals</li><li>Author approved merge request is forbidden</li><li>Committers approved merge request is forbidden</li><li>Merge requests approval rules prevent editing</li></ul> |
|
||||
| 5.32 Intellectual property rights | The organization should implement appropriate procedures to protect intellectual property rights. | <ul><li>License compliance running</li></ul> |
|
||||
| 8.4 Access to source code | Read and write access to source code, development tools and software libraries shall be appropriately managed. | <ul><li>Default branch protected</li></ul> |
|
||||
| 8.8 Management of technical vulnerabilities | Information about technical vulnerabilities of information systems in use shall be obtained, the organization's exposure to such vulnerabilities shall be evaluated and appropriate measures shall be taken. | <ul><li>Dependency scanning running</li><li>Container scanning running</li><li>SAST running</li><li>DAST running</li><li>API security running</li><li>Fuzz testing running</li></ul> |
|
||||
| 8.28 Secure coding | Secure coding principles shall be applied to software development. | <ul><li>Dependency scanning running</li><li>Container scanning running</li><li>SAST running</li><li>DAST running</li><li>API security running</li><li>Secret detection running</li><li>Fuzz testing running</li></ul> |
|
||||
| 8.29 Security testing in development and acceptance | Security testing processes shall be defined and implemented in the development lifecycle. | <ul><li>Dependency scanning running</li><li>Container scanning running</li><li>SAST running</li><li>DAST running</li><li>API security running</li><li>Secret detection running</li><li>Fuzz testing running</li></ul> |
|
||||
| 8.32 Change management | Changes to information processing facilities and information systems shall be subject to change management procedures. | <ul><li>Default branch protected</li></ul> |
|
||||
|
|
@ -16,7 +16,9 @@ module Backup
|
|||
# Ignore the DROP errors; recent database dumps will use --if-exists with pg_dump
|
||||
/does not exist$/,
|
||||
# User may not have permissions to drop extensions or schemas
|
||||
/must be owner of/
|
||||
/must be owner of/,
|
||||
# PG16 introduced generally ignorable error `must be able to SET ROLE "gitlab-psql"`
|
||||
/must be able to SET ROLE "gitlab-psql"/i
|
||||
].freeze
|
||||
IGNORED_ERRORS_REGEXP = Regexp.union(IGNORED_ERRORS).freeze
|
||||
|
||||
|
|
|
|||
|
|
@ -37689,6 +37689,9 @@ msgstr ""
|
|||
msgid "MemberRole|Admin role is assigned to one or more users. Remove role from all users, then delete role."
|
||||
msgstr ""
|
||||
|
||||
msgid "MemberRole|Admin role is used by one or more LDAP synchronizations. Remove LDAP syncs, then delete role."
|
||||
msgstr ""
|
||||
|
||||
msgid "MemberRole|Are you sure you want to delete this role?"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module RuboCop
|
||||
module Cop
|
||||
module Gitlab
|
||||
# Discourages the use of `Current.organization&.id`.
|
||||
#
|
||||
# `Current.organization` is expected to be assigned in contexts where its ID is accessed.
|
||||
# If `Current.organization` is not assigned, attempting to access `id` directly
|
||||
# (i.e., `Current.organization.id`) will correctly raise a
|
||||
# `Current::OrganizationNotAssignedError`. Using the safe navigation operator (`&.id`)
|
||||
# prevents this error from being raised, potentially hiding issues where
|
||||
# `Current.organization` was not properly set up.
|
||||
#
|
||||
# This cop enforces the direct use of `Current.organization.id` to ensure
|
||||
# that `Current::OrganizationNotAssignedError` is raised if `Current.organization` is nil.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# # bad
|
||||
# id = Current.organization&.id
|
||||
# id = ::Current.organization&.id
|
||||
#
|
||||
# # good
|
||||
# # If Current.organization is expected to be present (which it is),
|
||||
# # this will raise Current::OrganizationNotAssignedError if it's unexpectedly nil,
|
||||
# # making the underlying issue visible.
|
||||
# id = Current.organization.id
|
||||
#
|
||||
class DisallowCurrentOrganizationIdSafeNavigation < RuboCop::Cop::Base
|
||||
extend AutoCorrector
|
||||
|
||||
MSG = 'Use `Current.organization.id` instead of `Current.organization&.id`. ' \
|
||||
'`Current.organization` is expected to be assigned.'
|
||||
|
||||
# @!method current_organization_safe_id?(node)
|
||||
def_node_matcher :current_organization_safe_id?, <<~PATTERN
|
||||
(csend
|
||||
(send
|
||||
(const {nil? | cbase} :Current) :organization) :id)
|
||||
PATTERN
|
||||
|
||||
def on_csend(node)
|
||||
return unless current_organization_safe_id?(node)
|
||||
|
||||
add_offense(node) do |corrector|
|
||||
operator_range = node.loc.operator
|
||||
|
||||
if operator_range.nil? && node.receiver && node.loc.selector
|
||||
# Fallback: If node.loc.operator is nil, try to determine the range
|
||||
# by looking at the space between the receiver and the method selector.
|
||||
receiver_end_pos = node.receiver.source_range.end_pos
|
||||
selector_begin_pos = node.loc.selector.begin_pos
|
||||
|
||||
if receiver_end_pos < selector_begin_pos
|
||||
# This range covers the characters between the end of the receiver
|
||||
# and the start of the selector, which should be the operator.
|
||||
operator_range = Parser::Source::Range.new(node.source_range.source_buffer,
|
||||
receiver_end_pos,
|
||||
selector_begin_pos)
|
||||
end
|
||||
end
|
||||
|
||||
corrector.replace(operator_range, '.') if operator_range
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -198,7 +198,10 @@ RSpec.describe ProjectsFinder, feature_category: :groups_and_projects do
|
|||
let_it_be(:aimed_for_deletion_project) { create(:project, :public, marked_for_deletion_at: 2.days.ago, pending_delete: false) }
|
||||
let_it_be(:pending_deletion_project) { create(:project, :public, marked_for_deletion_at: 1.month.ago, pending_delete: true) }
|
||||
|
||||
it { is_expected.to contain_exactly(aimed_for_deletion_project) }
|
||||
let_it_be(:group_aimed_for_deletion) { create(:group_with_deletion_schedule) }
|
||||
let_it_be(:group_aimed_for_deletion_project) { create(:project, :public, group: group_aimed_for_deletion) }
|
||||
|
||||
it { is_expected.to contain_exactly(aimed_for_deletion_project, group_aimed_for_deletion_project) }
|
||||
end
|
||||
|
||||
describe 'filter by not aimed for deletion' do
|
||||
|
|
@ -206,6 +209,9 @@ RSpec.describe ProjectsFinder, feature_category: :groups_and_projects do
|
|||
let_it_be(:aimed_for_deletion_project) { create(:project, :public, marked_for_deletion_at: 2.days.ago, pending_delete: false) }
|
||||
let_it_be(:pending_deletion_project) { create(:project, :public, marked_for_deletion_at: 1.month.ago, pending_delete: true) }
|
||||
|
||||
let_it_be(:group_aimed_for_deletion) { create(:group_with_deletion_schedule) }
|
||||
let_it_be(:group_aimed_for_deletion_project) { create(:project, :public, group: group_aimed_for_deletion) }
|
||||
|
||||
it { is_expected.to contain_exactly(public_project, internal_project) }
|
||||
end
|
||||
|
||||
|
|
@ -370,11 +376,16 @@ RSpec.describe ProjectsFinder, feature_category: :groups_and_projects do
|
|||
let_it_be(:archived_project) { create(:project, :archived, :public) }
|
||||
let_it_be(:for_deletion_project) { create(:project, :public, marked_for_deletion_at: Date.current) }
|
||||
|
||||
let_it_be(:archived_group_project) { create(:project, :public, group: create(:group, :archived)) }
|
||||
let_it_be(:for_deletion_group_project) do
|
||||
create(:project, :public, group: create(:group_with_deletion_schedule))
|
||||
end
|
||||
|
||||
where :test_params, :expected_projects do
|
||||
{} | [ref(:active_projects), ref(:archived_project), ref(:for_deletion_project)]
|
||||
{ active: nil } | [ref(:active_projects), ref(:archived_project), ref(:for_deletion_project)]
|
||||
{} | [ref(:active_projects), ref(:archived_project), ref(:for_deletion_project), ref(:archived_group_project), ref(:for_deletion_group_project)]
|
||||
{ active: nil } | [ref(:active_projects), ref(:archived_project), ref(:for_deletion_project), ref(:archived_group_project), ref(:for_deletion_group_project)]
|
||||
{ active: true } | [ref(:active_projects)]
|
||||
{ active: false } | [ref(:archived_project), ref(:for_deletion_project)]
|
||||
{ active: false } | [ref(:archived_project), ref(:for_deletion_project), ref(:archived_group_project), ref(:for_deletion_group_project)]
|
||||
end
|
||||
|
||||
with_them do
|
||||
|
|
@ -385,7 +396,8 @@ RSpec.describe ProjectsFinder, feature_category: :groups_and_projects do
|
|||
end
|
||||
|
||||
describe 'filter by archived' do
|
||||
let!(:archived_project) { create(:project, :public, :archived, name: 'E', path: 'E') }
|
||||
let_it_be(:archived_project) { create(:project, :public, :archived, name: 'E', path: 'E') }
|
||||
let_it_be(:archived_group_project) { create(:project, :public, group: create(:group, :archived)) }
|
||||
|
||||
context 'non_archived=true' do
|
||||
let(:params) { { non_archived: true } }
|
||||
|
|
@ -396,13 +408,13 @@ RSpec.describe ProjectsFinder, feature_category: :groups_and_projects do
|
|||
context 'non_archived=false' do
|
||||
let(:params) { { non_archived: false } }
|
||||
|
||||
it { is_expected.to match_array([public_project, internal_project, archived_project]) }
|
||||
it { is_expected.to match_array([public_project, internal_project, archived_project, archived_group_project]) }
|
||||
end
|
||||
|
||||
describe 'filter by archived only' do
|
||||
let(:params) { { archived: 'only' } }
|
||||
|
||||
it { is_expected.to eq([archived_project]) }
|
||||
it { is_expected.to eq([archived_project, archived_group_project]) }
|
||||
end
|
||||
|
||||
describe 'filter by archived for backward compatibility' do
|
||||
|
|
@ -414,7 +426,7 @@ RSpec.describe ProjectsFinder, feature_category: :groups_and_projects do
|
|||
describe 'filter by archived is present and is nil' do
|
||||
let(:params) { { archived: nil } }
|
||||
|
||||
it { is_expected.to match_array([public_project, internal_project, archived_project]) }
|
||||
it { is_expected.to match_array([public_project, internal_project, archived_project, archived_group_project]) }
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -2480,8 +2480,6 @@ Groups::MilestonesController
|
|||
returns not found
|
||||
|
||||
GroupsHelper
|
||||
group_icon
|
||||
returns an url for the avatar
|
||||
group_icon_url
|
||||
returns an url for the avatar
|
||||
gives default avatar_icon when no avatar is present
|
||||
|
|
|
|||
|
|
@ -32,18 +32,6 @@ RSpec.describe 'Container registry (JavaScript fixtures)', feature_category: :co
|
|||
}
|
||||
end
|
||||
|
||||
shared_examples 'container registry protection tag rules' do |fixture_suffix|
|
||||
before do
|
||||
create(:container_registry_protection_tag_rule, :immutable, project: project)
|
||||
end
|
||||
|
||||
it "graphql/#{project_container_protection_tag_rules_query_path}.#{fixture_suffix}.json" do
|
||||
post_graphql(query, current_user: user, variables: variables)
|
||||
|
||||
expect_graphql_errors_to_be_empty
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
stub_gitlab_api_client_to_support_gitlab_api(supported: true)
|
||||
end
|
||||
|
|
@ -85,18 +73,6 @@ RSpec.describe 'Container registry (JavaScript fixtures)', feature_category: :co
|
|||
end
|
||||
end
|
||||
|
||||
context 'with immutable tag protection rules' do
|
||||
it_behaves_like 'container registry protection tag rules', 'immutable_rules'
|
||||
end
|
||||
|
||||
context 'with immutable tag protection rules as maintainer' do
|
||||
before_all do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
it_behaves_like 'container registry protection tag rules', 'immutable_rules_maintainer'
|
||||
end
|
||||
|
||||
context 'with maximum number of tag protection rules' do
|
||||
before do
|
||||
5.times do |i|
|
||||
|
|
|
|||
|
|
@ -7,21 +7,15 @@ RSpec.describe GitlabSchema.types['ContainerProtectionAccessLevel'], feature_cat
|
|||
|
||||
specify { expect(described_class.description).to be_present }
|
||||
|
||||
describe 'minimum_access_level_for_push' do
|
||||
describe 'minimum_access_level_for_push', unless: Gitlab.ee? do
|
||||
subject { described_class.fields['minimumAccessLevelForPush'] }
|
||||
|
||||
it { is_expected.to have_nullable_graphql_type(Types::ContainerRegistry::Protection::TagRuleAccessLevelEnum) }
|
||||
it { is_expected.to have_non_null_graphql_type(Types::ContainerRegistry::Protection::TagRuleAccessLevelEnum) }
|
||||
end
|
||||
|
||||
describe 'minimum_access_level_for_delete' do
|
||||
describe 'minimum_access_level_for_delete', unless: Gitlab.ee? do
|
||||
subject { described_class.fields['minimumAccessLevelForDelete'] }
|
||||
|
||||
it { is_expected.to have_nullable_graphql_type(Types::ContainerRegistry::Protection::TagRuleAccessLevelEnum) }
|
||||
end
|
||||
|
||||
describe 'immutable' do
|
||||
subject { described_class.fields['immutable'] }
|
||||
|
||||
it { is_expected.to have_non_null_graphql_type(GraphQL::Types::Boolean) }
|
||||
it { is_expected.to have_non_null_graphql_type(Types::ContainerRegistry::Protection::TagRuleAccessLevelEnum) }
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -25,21 +25,15 @@ RSpec.describe GitlabSchema.types['ContainerProtectionTagRule'], feature_categor
|
|||
it { is_expected.to have_non_null_graphql_type(GraphQL::Types::String) }
|
||||
end
|
||||
|
||||
describe 'minimum_access_level_for_push' do
|
||||
describe 'minimum_access_level_for_push', unless: Gitlab.ee? do
|
||||
subject { described_class.fields['minimumAccessLevelForPush'] }
|
||||
|
||||
it { is_expected.to have_nullable_graphql_type(Types::ContainerRegistry::Protection::TagRuleAccessLevelEnum) }
|
||||
it { is_expected.to have_non_null_graphql_type(Types::ContainerRegistry::Protection::TagRuleAccessLevelEnum) }
|
||||
end
|
||||
|
||||
describe 'minimum_access_level_for_delete' do
|
||||
describe 'minimum_access_level_for_delete', unless: Gitlab.ee? do
|
||||
subject { described_class.fields['minimumAccessLevelForDelete'] }
|
||||
|
||||
it { is_expected.to have_nullable_graphql_type(Types::ContainerRegistry::Protection::TagRuleAccessLevelEnum) }
|
||||
end
|
||||
|
||||
describe 'immutable' do
|
||||
subject { described_class.fields['immutable'] }
|
||||
|
||||
it { is_expected.to have_non_null_graphql_type(GraphQL::Types::Boolean) }
|
||||
it { is_expected.to have_non_null_graphql_type(Types::ContainerRegistry::Protection::TagRuleAccessLevelEnum) }
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1578,29 +1578,10 @@ RSpec.describe GitlabSchema.types['Project'], feature_category: :groups_and_proj
|
|||
end
|
||||
end
|
||||
|
||||
describe 'container_protection_tag_rules' do
|
||||
describe 'container_protection_tag_rules', unless: Gitlab.ee? do
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
before_all do
|
||||
create(:container_registry_protection_tag_rule, :immutable,
|
||||
project: project,
|
||||
tag_name_pattern: 'immutable-1'
|
||||
)
|
||||
|
||||
create(:container_registry_protection_tag_rule,
|
||||
project: project,
|
||||
minimum_access_level_for_push: Gitlab::Access::MAINTAINER,
|
||||
minimum_access_level_for_delete: Gitlab::Access::OWNER,
|
||||
tag_name_pattern: 'mutable'
|
||||
)
|
||||
|
||||
create(:container_registry_protection_tag_rule, :immutable,
|
||||
project: project,
|
||||
tag_name_pattern: 'immutable-2'
|
||||
)
|
||||
end
|
||||
|
||||
let(:query) do
|
||||
%(
|
||||
query {
|
||||
|
|
@ -1618,52 +1599,37 @@ RSpec.describe GitlabSchema.types['Project'], feature_category: :groups_and_proj
|
|||
)
|
||||
end
|
||||
|
||||
subject { GitlabSchema.execute(query, context: { current_user: user }).as_json }
|
||||
before_all do
|
||||
create(:container_registry_protection_tag_rule, :immutable,
|
||||
project: project,
|
||||
tag_name_pattern: 'immutable-1'
|
||||
)
|
||||
|
||||
create(:container_registry_protection_tag_rule,
|
||||
project: project,
|
||||
minimum_access_level_for_push: Gitlab::Access::MAINTAINER,
|
||||
minimum_access_level_for_delete: Gitlab::Access::OWNER,
|
||||
tag_name_pattern: 'mutable'
|
||||
)
|
||||
end
|
||||
|
||||
subject do
|
||||
GitlabSchema.execute(query, context: { current_user: user })
|
||||
.as_json.dig('data', 'project', 'containerProtectionTagRules', 'nodes')
|
||||
end
|
||||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
it 'returns tag rules with mutable ones first' do
|
||||
result_nodes = subject.dig('data', 'project', 'containerProtectionTagRules', 'nodes')
|
||||
|
||||
expect(result_nodes.size).to eq(3)
|
||||
|
||||
expect(result_nodes[0]).to include(
|
||||
'tagNamePattern' => 'mutable',
|
||||
'minimumAccessLevelForPush' => 'MAINTAINER',
|
||||
'minimumAccessLevelForDelete' => 'OWNER'
|
||||
)
|
||||
|
||||
expect(result_nodes[1]).to include(
|
||||
'tagNamePattern' => 'immutable-1',
|
||||
'minimumAccessLevelForPush' => nil,
|
||||
'minimumAccessLevelForDelete' => nil
|
||||
)
|
||||
|
||||
expect(result_nodes[2]).to include(
|
||||
'tagNamePattern' => 'immutable-2',
|
||||
'minimumAccessLevelForPush' => nil,
|
||||
'minimumAccessLevelForDelete' => nil
|
||||
)
|
||||
end
|
||||
|
||||
context 'when the feature container_registry_immutable_tags is disabled' do
|
||||
before do
|
||||
stub_feature_flags(container_registry_immutable_tags: false)
|
||||
end
|
||||
|
||||
it 'only returns mutable tag rules' do
|
||||
result_nodes = subject.dig('data', 'project', 'containerProtectionTagRules', 'nodes')
|
||||
|
||||
expect(result_nodes.size).to eq(1)
|
||||
|
||||
expect(result_nodes[0]).to include(
|
||||
it do
|
||||
is_expected.to have_attributes(size: 1).and contain_exactly(
|
||||
a_hash_including(
|
||||
'tagNamePattern' => 'mutable',
|
||||
'minimumAccessLevelForPush' => 'MAINTAINER',
|
||||
'minimumAccessLevelForDelete' => 'OWNER'
|
||||
)
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,54 +7,6 @@ RSpec.describe AvatarsHelper, feature_category: :source_code_management do
|
|||
|
||||
let_it_be(:user) { create(:user) }
|
||||
|
||||
describe '#group_icon, #topic_icon' do
|
||||
shared_examples 'resource with a default avatar' do |source_type|
|
||||
it 'returns a default avatar div' do
|
||||
expect(public_send("#{source_type}_icon", *helper_args))
|
||||
.to match(%r{<span class="identicon bg\d+">F</span>})
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'resource with a custom avatar' do |source_type|
|
||||
it 'returns a custom avatar image' do
|
||||
expect(public_send("#{source_type}_icon", *helper_args))
|
||||
.to eq "<img src=\"#{resource.avatar.url}\" />"
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'Gitaly exception handling' do
|
||||
before do
|
||||
allow(resource).to receive(:avatar_url).and_raise(error_class)
|
||||
end
|
||||
|
||||
it_behaves_like 'resource with a default avatar', 'project'
|
||||
end
|
||||
|
||||
context 'when providing a group' do
|
||||
it_behaves_like 'resource with a default avatar', 'group' do
|
||||
let(:resource) { create(:group, name: 'foo') }
|
||||
let(:helper_args) { [resource] }
|
||||
end
|
||||
|
||||
it_behaves_like 'resource with a custom avatar', 'group' do
|
||||
let(:resource) { create(:group, avatar: File.open(uploaded_image_temp_path)) }
|
||||
let(:helper_args) { [resource] }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when providing a topic' do
|
||||
it_behaves_like 'resource with a default avatar', 'topic' do
|
||||
let(:resource) { create(:topic, name: 'foo') }
|
||||
let(:helper_args) { [resource] }
|
||||
end
|
||||
|
||||
it_behaves_like 'resource with a custom avatar', 'topic' do
|
||||
let(:resource) { create(:topic, avatar: File.open(uploaded_image_temp_path)) }
|
||||
let(:helper_args) { [resource] }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#avatar_icon_for' do
|
||||
let!(:user) { create(:user, avatar: File.open(uploaded_image_temp_path), email: 'bar@example.com') }
|
||||
let(:email) { 'foo@example.com' }
|
||||
|
|
|
|||
|
|
@ -669,6 +669,216 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr
|
|||
end
|
||||
end
|
||||
|
||||
describe 'scopes' do
|
||||
shared_examples 'includes projects in hierarchy marked for deletion' do
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:active_project) { create(:project, group: group) }
|
||||
let_it_be(:for_deletion_project) { create(:project, group: group, marked_for_deletion_at: Date.current) }
|
||||
|
||||
context 'when parent group is active' do
|
||||
it 'returns only projects marked for deletion' do
|
||||
expect(subject).to include(for_deletion_project)
|
||||
expect(subject).not_to include(active_project)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when parent group is marked for deletion' do
|
||||
let_it_be(:group_deletion_schedule) { create(:group_deletion_schedule, group: group) }
|
||||
|
||||
it 'returns all projects in the group' do
|
||||
expect(subject).to include(for_deletion_project, active_project)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'excludes projects in hierarchy marked for deletion' do
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:active_project) { create(:project, group: group) }
|
||||
let_it_be(:for_deletion_project) { create(:project, group: group, marked_for_deletion_at: Date.current) }
|
||||
|
||||
context 'when parent group is active' do
|
||||
it 'returns only active projects' do
|
||||
expect(subject).to include(active_project)
|
||||
expect(subject).not_to include(for_deletion_project)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when parent group is marked for deletion' do
|
||||
let_it_be(:group_deletion_schedule) { create(:group_deletion_schedule, group: group) }
|
||||
|
||||
it 'excludes all projects in the group' do
|
||||
expect(subject).not_to include(for_deletion_project, active_project)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'includes projects in archived hierarchy' do
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:active_project) { create(:project, group: group) }
|
||||
let_it_be(:archived_project) { create(:project, group: group, archived: true) }
|
||||
|
||||
context 'when parent group is active' do
|
||||
it 'returns only archived projects' do
|
||||
expect(subject).to include(archived_project)
|
||||
expect(subject).not_to include(active_project)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when parent group is archived' do
|
||||
before do
|
||||
group.archive
|
||||
end
|
||||
|
||||
it 'returns all projects in the group' do
|
||||
expect(subject).to include(archived_project, active_project)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'excludes projects in archived hierarchy' do
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:active_project) { create(:project, group: group) }
|
||||
let_it_be(:archived_project) { create(:project, group: group, archived: true) }
|
||||
|
||||
context 'when parent group is active' do
|
||||
it 'returns only active projects' do
|
||||
expect(subject).to include(active_project)
|
||||
expect(subject).not_to include(archived_project)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when parent group is archived' do
|
||||
before do
|
||||
group.archive
|
||||
end
|
||||
|
||||
it 'excludes all projects in the group' do
|
||||
expect(subject).not_to include(archived_project, active_project)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.aimed_for_deletion' do
|
||||
let_it_be(:active_project) { create(:project) }
|
||||
let_it_be(:for_deletion_project) { create(:project, marked_for_deletion_at: Date.current) }
|
||||
|
||||
it 'returns projects marked for deletion' do
|
||||
result = described_class.aimed_for_deletion
|
||||
|
||||
expect(result).to include(for_deletion_project)
|
||||
expect(result).not_to include(active_project)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.self_or_ancestors_aimed_for_deletion' do
|
||||
subject { described_class.self_or_ancestors_aimed_for_deletion }
|
||||
|
||||
it_behaves_like 'includes projects in hierarchy marked for deletion'
|
||||
end
|
||||
|
||||
describe '.not_aimed_for_deletion' do
|
||||
let_it_be(:active_project) { create(:project) }
|
||||
let_it_be(:for_deletion_project) { create(:project, marked_for_deletion_at: Date.current) }
|
||||
|
||||
it 'returns projects not marked for deletion' do
|
||||
result = described_class.not_aimed_for_deletion
|
||||
|
||||
expect(result).to include(active_project)
|
||||
expect(result).not_to include(for_deletion_project)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.self_and_ancestors_not_aimed_for_deletion' do
|
||||
subject { described_class.self_and_ancestors_not_aimed_for_deletion }
|
||||
|
||||
it_behaves_like 'excludes projects in hierarchy marked for deletion'
|
||||
end
|
||||
|
||||
describe '.marked_for_deletion_on' do
|
||||
let_it_be(:active_project) { create(:project) }
|
||||
let_it_be(:for_deletion_project) { create(:project, marked_for_deletion_at: Date.parse('2024-01-01')) }
|
||||
|
||||
context 'when date is provided' do
|
||||
it 'returns projects marked for deletion on that date' do
|
||||
result = described_class.marked_for_deletion_on(Date.parse('2024-01-01'))
|
||||
expect(result).to contain_exactly(for_deletion_project)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when date is nil' do
|
||||
it 'returns projects not marked for deletion' do
|
||||
result = described_class.marked_for_deletion_on(nil)
|
||||
expect(result).to contain_exactly(active_project)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.marked_for_deletion_before' do
|
||||
let_it_be(:cutoff_date) { 10.days.ago }
|
||||
let_it_be(:active_project) { create(:project) }
|
||||
let_it_be(:marked_after) { create(:project, marked_for_deletion_at: cutoff_date + 2.days) }
|
||||
let_it_be(:marked_before) { create(:project, marked_for_deletion_at: cutoff_date - 2.days) }
|
||||
let_it_be(:marked_on_date) { create(:project, marked_for_deletion_at: cutoff_date) }
|
||||
|
||||
it 'returns projects marked for deletion on or before the specified date' do
|
||||
result = described_class.marked_for_deletion_before(cutoff_date)
|
||||
|
||||
expect(result).to include(marked_before, marked_on_date)
|
||||
expect(result).not_to include(marked_after, active_project)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.archived' do
|
||||
let_it_be(:active_project) { create(:project, archived: false) }
|
||||
let_it_be(:archived_project) { create(:project, archived: true) }
|
||||
|
||||
it 'returns archived projects' do
|
||||
result = described_class.archived
|
||||
|
||||
expect(result).to include(archived_project)
|
||||
expect(result).not_to include(active_project)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.self_or_ancestors_archived' do
|
||||
subject { described_class.self_or_ancestors_archived }
|
||||
|
||||
it_behaves_like 'includes projects in archived hierarchy'
|
||||
end
|
||||
|
||||
describe '.non_archived' do
|
||||
let_it_be(:active_project) { create(:project, archived: false) }
|
||||
let_it_be(:archived_project) { create(:project, archived: true) }
|
||||
|
||||
it 'returns non-archived projects' do
|
||||
result = described_class.non_archived
|
||||
|
||||
expect(result).to include(active_project)
|
||||
expect(result).not_to include(archived_project)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.self_and_ancestors_non_archived' do
|
||||
subject { described_class.self_and_ancestors_non_archived }
|
||||
|
||||
it_behaves_like 'excludes projects in archived hierarchy'
|
||||
end
|
||||
|
||||
describe '.self_and_ancestors_active' do
|
||||
subject { described_class.self_and_ancestors_active }
|
||||
|
||||
it_behaves_like 'excludes projects in archived hierarchy'
|
||||
it_behaves_like 'excludes projects in hierarchy marked for deletion'
|
||||
end
|
||||
|
||||
describe '.self_or_ancestors_inactive' do
|
||||
subject { described_class.self_or_ancestors_inactive }
|
||||
|
||||
it_behaves_like 'includes projects in archived hierarchy'
|
||||
it_behaves_like 'includes projects in hierarchy marked for deletion'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'modules' do
|
||||
subject { described_class }
|
||||
|
||||
|
|
@ -2454,32 +2664,6 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr
|
|||
end
|
||||
end
|
||||
|
||||
describe '.not_aimed_for_deletion' do
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:delayed_deletion_project) { create(:project, marked_for_deletion_at: Date.current) }
|
||||
|
||||
it do
|
||||
expect(described_class.not_aimed_for_deletion).to contain_exactly(project)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.by_marked_for_deletion_on' do
|
||||
let_it_be(:project) { create(:project) }
|
||||
let_it_be(:marked_for_deletion_project) { create(:project, marked_for_deletion_at: Date.parse('2024-01-01')) }
|
||||
|
||||
context 'when marked_for_deletion_on is present' do
|
||||
it 'return projects marked for deletion' do
|
||||
expect(described_class.by_marked_for_deletion_on(Date.parse('2024-01-01'))).to contain_exactly(marked_for_deletion_project)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when marked_for_deletion_on is not present' do
|
||||
it 'return projects not marked for deletion' do
|
||||
expect(described_class.by_marked_for_deletion_on(nil)).to contain_exactly(project)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.sorted_by_similarity_desc' do
|
||||
let_it_be(:project_a) { create(:project, path: 'similar-1', name: 'similar-1', description: 'A similar project') }
|
||||
let_it_be_with_reload(:project_b) { create(:project, path: 'similar-2', name: 'similar-2', description: 'A related project') }
|
||||
|
|
|
|||
|
|
@ -720,45 +720,6 @@ RSpec.describe 'container repository details', feature_category: :container_regi
|
|||
}
|
||||
)
|
||||
end
|
||||
|
||||
context 'when there is an immutable rule' do
|
||||
before_all do
|
||||
create(
|
||||
:container_registry_protection_tag_rule,
|
||||
:immutable,
|
||||
project: project,
|
||||
tag_name_pattern: 'la'
|
||||
)
|
||||
end
|
||||
|
||||
it 'returns the maximum access fields from the matching protection rules' do
|
||||
subject
|
||||
|
||||
expect(tag_permissions_response).to eq(
|
||||
{
|
||||
'minimumAccessLevelForPush' => nil,
|
||||
'minimumAccessLevelForDelete' => nil
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
context 'when the feature container_registry_immutable_tags is disabled' do
|
||||
before do
|
||||
stub_feature_flags(container_registry_immutable_tags: false)
|
||||
end
|
||||
|
||||
it 'ignores the immutable rule' do
|
||||
subject
|
||||
|
||||
expect(tag_permissions_response).to eq(
|
||||
{
|
||||
'minimumAccessLevelForPush' => 'OWNER',
|
||||
'minimumAccessLevelForDelete' => 'OWNER'
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for tags destroyContainerRepositoryTag field' do
|
||||
|
|
|
|||
|
|
@ -83,16 +83,16 @@ RSpec.describe 'Creating the container registry tag protection rule', :aggregate
|
|||
it_behaves_like 'returning a GraphQL error', [/minimumAccessLevelForPush/, /minimumAccessLevelForDelete/]
|
||||
end
|
||||
|
||||
context 'with blank input for the field `minimumAccessLevelForPush`' do
|
||||
context 'with blank input for the field `minimumAccessLevelForPush`', unless: Gitlab.ee? do
|
||||
let(:input) { super().merge(minimum_access_level_for_push: nil) }
|
||||
|
||||
it_behaves_like 'returning a mutation error', 'Access levels should either both be present or both be nil'
|
||||
it_behaves_like 'returning a GraphQL error', /minimumAccessLevelForPush/
|
||||
end
|
||||
|
||||
context 'with blank input for the field `minimumAccessLevelForDelete`' do
|
||||
context 'with blank input for the field `minimumAccessLevelForDelete`', unless: Gitlab.ee? do
|
||||
let(:input) { super().merge(minimum_access_level_for_delete: nil) }
|
||||
|
||||
it_behaves_like 'returning a mutation error', 'Access levels should either both be present or both be nil'
|
||||
it_behaves_like 'returning a GraphQL error', /minimumAccessLevelForDelete/
|
||||
end
|
||||
|
||||
context 'with blank input field `tagNamePattern`' do
|
||||
|
|
|
|||
|
|
@ -38,20 +38,19 @@ RSpec.describe Mutations::ContainerRegistry::Protection::TagRule::Delete, :aggre
|
|||
|
||||
it 'responds with deleted container registry tag protection rule' do
|
||||
expect { post_graphql_mutation_request }
|
||||
.to change { ::ContainerRegistry::Protection::TagRule.count }.from(1).to(0)
|
||||
.to change { ::ContainerRegistry::Protection::TagRule.count }.by(-1)
|
||||
|
||||
expect(mutation_response).to include(
|
||||
'errors' => be_blank,
|
||||
'containerProtectionTagRule' => {
|
||||
'containerProtectionTagRule' => hash_including(
|
||||
'id' => container_protection_rule.to_global_id.to_s,
|
||||
'tagNamePattern' => container_protection_rule.tag_name_pattern,
|
||||
'minimumAccessLevelForDelete' => container_protection_rule.minimum_access_level_for_delete.upcase,
|
||||
'minimumAccessLevelForPush' => container_protection_rule.minimum_access_level_for_push.upcase,
|
||||
'immutable' => container_protection_rule.immutable?,
|
||||
'userPermissions' => {
|
||||
'destroyContainerRegistryProtectionTagRule' => true
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -13,10 +13,6 @@ RSpec.describe 'Updating the container registry tag protection rule', :aggregate
|
|||
|
||||
let_it_be(:current_user) { create(:user, maintainer_of: project) }
|
||||
|
||||
let(:container_protection_tag_rule_attributes) do
|
||||
build_stubbed(:container_protection_tag_rule, project: project)
|
||||
end
|
||||
|
||||
let(:mutation) do
|
||||
graphql_mutation(:update_container_protection_tag_rule, input,
|
||||
<<~QUERY
|
||||
|
|
@ -68,8 +64,8 @@ RSpec.describe 'Updating the container registry tag protection rule', :aggregate
|
|||
post_graphql_mutation_request.tap do
|
||||
expect(container_protection_tag_rule.reload).to have_attributes(
|
||||
tag_name_pattern: input[:tag_name_pattern],
|
||||
minimum_access_level_for_push: input[:minimum_access_level_for_push]&.downcase,
|
||||
minimum_access_level_for_delete: input[:minimum_access_level_for_delete]&.downcase
|
||||
minimum_access_level_for_push: input[:minimum_access_level_for_push].downcase,
|
||||
minimum_access_level_for_delete: input[:minimum_access_level_for_delete].downcase
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
@ -121,12 +117,6 @@ RSpec.describe 'Updating the container registry tag protection rule', :aggregate
|
|||
it_behaves_like 'returning a GraphQL error', /tagNamePattern can't be blank/
|
||||
end
|
||||
|
||||
context 'with blank input fields `minimumAccessLevelForPush` and `minimumAccessLevelForDelete`' do
|
||||
let(:input) { super().merge(minimum_access_level_for_push: nil, minimum_access_level_for_delete: nil) }
|
||||
|
||||
it_behaves_like 'a successful response'
|
||||
end
|
||||
|
||||
context 'with only `minimumAccessLevelForDelete` blank' do
|
||||
let(:input) { super().merge(minimum_access_level_for_delete: nil) }
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rubocop_spec_helper'
|
||||
require 'rubocop/rspec/support'
|
||||
require_relative '../../../../rubocop/cop/gitlab/disallow_current_organization_id_safe_navigation'
|
||||
|
||||
RSpec.describe RuboCop::Cop::Gitlab::DisallowCurrentOrganizationIdSafeNavigation, feature_category: :organization do
|
||||
include RuboCop::RSpec::ExpectOffense
|
||||
|
||||
let(:hardcoded_expected_message) do
|
||||
'Use `Current.organization.id` instead of `Current.organization&.id`. ' \
|
||||
'`Current.organization` is expected to be assigned.'
|
||||
end
|
||||
|
||||
context 'when `Current.organization&.id` is used' do
|
||||
it 'registers an offense and autocorrects `Current.organization&.id`' do
|
||||
expect_offense(<<~RUBY)
|
||||
id = Current.organization&.id
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^ #{hardcoded_expected_message}
|
||||
RUBY
|
||||
|
||||
expect_correction(<<~RUBY)
|
||||
id = Current.organization.id
|
||||
RUBY
|
||||
end
|
||||
|
||||
# This is the test case that was failing due to the spec's conditional logic.
|
||||
# Simplify it as follows:
|
||||
it 'registers an offense and autocorrects `::Current.organization&.id` (top-level constant)' do
|
||||
expect_offense(<<~RUBY)
|
||||
id = ::Current.organization&.id
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^ #{hardcoded_expected_message}
|
||||
RUBY
|
||||
|
||||
expect_correction(<<~RUBY)
|
||||
id = ::Current.organization.id
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'registers an offense and autocorrects when used in a condition' do
|
||||
expect_offense(<<~RUBY)
|
||||
if Current.organization&.id == 5
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^ #{hardcoded_expected_message}
|
||||
end
|
||||
RUBY
|
||||
|
||||
expect_correction(<<~RUBY)
|
||||
if Current.organization.id == 5
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
|
||||
context 'when related but non-offending patterns are used' do
|
||||
it 'does not register an offense for `Current.organization.id` (no safe navigation)' do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
id = Current.organization.id
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'does not register an offense for `other_object.organization&.id`' do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
id = other_object.organization&.id
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'does not register an offense for `Current.other_method&.id`' do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
id = Current.other_method&.id
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'does not register an offense for `Current.organization&.other_attribute`' do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
id = Current.organization&.other_attribute
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'does not register an offense for just `Current.organization`' do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
org = Current.organization
|
||||
RUBY
|
||||
end
|
||||
|
||||
it 'does not register an offense for a different safe navigation chain' do
|
||||
expect_no_offenses(<<~RUBY)
|
||||
name = Current.user&.name
|
||||
RUBY
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in New Issue