Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-10-26 00:10:58 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-10-26 00:10:58 +0300
commit098444d917f660f870daa75e9166dcb735573ad4 (patch)
treebdffb43023060a8432b4a373fb3a789d7ab7b289
parent62866a623e24242c6f7a1a93dc2aca1467d6a6ae (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.rubocop_todo/style/format_string.yml1
-rw-r--r--app/assets/javascripts/ci/catalog/components/details/ci_resource_components.vue24
-rw-r--r--app/assets/javascripts/ci/runner/components/search_tokens/tag_token.vue2
-rw-r--r--app/assets/javascripts/notes/components/notes_activity_header.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/toggle/toggle_sidebar.vue10
-rw-r--r--app/graphql/mutations/packages/protection/rule/delete.rb40
-rw-r--r--app/graphql/types/mutation_type.rb1
-rw-r--r--app/helpers/wiki_helper.rb8
-rw-r--r--app/models/packages/nuget/symbol.rb3
-rw-r--r--app/policies/base_policy.rb2
-rw-r--r--app/policies/concerns/policy_actor.rb4
-rw-r--r--app/policies/global_policy.rb4
-rw-r--r--app/policies/project_policy.rb3
-rw-r--r--app/presenters/commit_status_presenter.rb1
-rw-r--r--app/services/packages/nuget/symbols/create_symbol_files_service.rb20
-rw-r--r--app/services/packages/nuget/symbols/extract_signature_and_checksum_service.rb (renamed from app/services/packages/nuget/symbols/extract_symbol_signature_service.rb)56
-rw-r--r--app/services/packages/protection/delete_rule_service.rb43
-rw-r--r--app/views/admin/application_settings/metrics_and_profiling.html.haml2
-rw-r--r--app/views/import/gitlab_projects/new.html.haml2
-rw-r--r--app/views/import/shared/_new_project_form.html.haml4
-rw-r--r--app/views/projects/_import_project_pane.html.haml2
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml2
-rw-r--r--config/feature_flags/development/ai_self_discover.yml8
-rw-r--r--db/docs/approval_group_rules.yml10
-rw-r--r--db/docs/approval_group_rules_groups.yml9
-rw-r--r--db/docs/approval_group_rules_protected_branches.yml9
-rw-r--r--db/docs/approval_group_rules_users.yml9
-rw-r--r--db/migrate/20230926092914_add_approval_group_rules.rb34
-rw-r--r--db/migrate/20230926092944_add_approval_group_rules_groups.rb18
-rw-r--r--db/migrate/20230926093004_add_approval_group_rules_users.rb18
-rw-r--r--db/migrate/20230926093025_add_approval_group_rules_protected_branches.rb21
-rw-r--r--db/migrate/20230926093101_add_fk_to_approval_rule_on_approval_group_rules_users.rb18
-rw-r--r--db/migrate/20230926093144_add_fk_to_user_on_approval_group_rules_users.rb15
-rw-r--r--db/migrate/20230926093211_add_fk_to_approval_rule_on_approval_group_rules_groups.rb16
-rw-r--r--db/migrate/20230926093251_add_fk_to_group_on_approval_group_rules_groups.rb15
-rw-r--r--db/migrate/20230926105440_add_fk_to_approval_rule_on_approval_group_rules_protected_branches.rb18
-rw-r--r--db/migrate/20230926105931_add_fk_to_protected_branch_on_approval_group_rules_protected_branches.rb16
-rw-r--r--db/migrate/20230928145555_add_fk_to_security_orchestration_policy_configuration_on_approval_group_rules.rb17
-rw-r--r--db/migrate/20230928145637_add_fk_to_scan_result_policy_on_approval_group_rules.rb16
-rw-r--r--db/migrate/20231019104211_add_file_sha256_to_packages_nuget_symbols.rb13
-rw-r--r--db/schema_migrations/202309260929141
-rw-r--r--db/schema_migrations/202309260929441
-rw-r--r--db/schema_migrations/202309260930041
-rw-r--r--db/schema_migrations/202309260930251
-rw-r--r--db/schema_migrations/202309260931011
-rw-r--r--db/schema_migrations/202309260931441
-rw-r--r--db/schema_migrations/202309260932111
-rw-r--r--db/schema_migrations/202309260932511
-rw-r--r--db/schema_migrations/202309261054401
-rw-r--r--db/schema_migrations/202309261059311
-rw-r--r--db/schema_migrations/202309281455551
-rw-r--r--db/schema_migrations/202309281456371
-rw-r--r--db/schema_migrations/202310191042111
-rw-r--r--db/structure.sql136
-rw-r--r--doc/api/graphql/reference/index.md31
-rw-r--r--doc/user/ai_features.md24
-rw-r--r--lib/gitlab/github_import/milestone_finder.rb18
-rw-r--r--locale/gitlab.pot9
-rw-r--r--package.json2
-rw-r--r--qa/qa/page/admin/settings/metrics_and_profiling.rb4
-rw-r--r--qa/qa/page/component/import/gitlab.rb9
-rw-r--r--qa/qa/page/component/import/selection.rb6
-rw-r--r--qa/qa/page/main/menu.rb2
-rw-r--r--qa/qa/page/project/web_ide/vscode.rb41
-rw-r--r--qa/qa/page/search/results.rb17
-rw-r--r--spec/factories/packages/nuget/symbol.rb1
-rw-r--r--spec/frontend/ci/catalog/components/details/ci_resource_components_spec.js10
-rw-r--r--spec/helpers/wiki_helper_spec.rb11
-rw-r--r--spec/lib/gitlab/github_import/milestone_finder_spec.rb57
-rw-r--r--spec/models/packages/nuget/symbol_spec.rb1
-rw-r--r--spec/policies/concerns/policy_actor_spec.rb6
-rw-r--r--spec/policies/global_policy_spec.rb7
-rw-r--r--spec/requests/api/graphql/mutations/packages/protection/rule/delete_spec.rb86
-rw-r--r--spec/services/packages/nuget/symbols/create_symbol_files_service_spec.rb46
-rw-r--r--spec/services/packages/nuget/symbols/extract_signature_and_checksum_service_spec.rb46
-rw-r--r--spec/services/packages/nuget/symbols/extract_symbol_signature_service_spec.rb23
-rw-r--r--spec/services/packages/protection/delete_rule_service_spec.rb92
-rw-r--r--spec/spec_helper.rb2
-rw-r--r--spec/support/shared_examples/services/protected_branches_shared_examples.rb31
-rw-r--r--yarn.lock8
80 files changed, 1084 insertions, 171 deletions
diff --git a/.rubocop_todo/style/format_string.yml b/.rubocop_todo/style/format_string.yml
index 002d1964d3f..101b16802f7 100644
--- a/.rubocop_todo/style/format_string.yml
+++ b/.rubocop_todo/style/format_string.yml
@@ -256,7 +256,6 @@ Style/FormatString:
- 'lib/gitlab/github_import/importer/single_endpoint_issue_events_importer.rb'
- 'lib/gitlab/github_import/issuable_finder.rb'
- 'lib/gitlab/github_import/label_finder.rb'
- - 'lib/gitlab/github_import/milestone_finder.rb'
- 'lib/gitlab/github_import/object_counter.rb'
- 'lib/gitlab/github_import/page_counter.rb'
- 'lib/gitlab/github_import/parallel_scheduling.rb'
diff --git a/app/assets/javascripts/ci/catalog/components/details/ci_resource_components.vue b/app/assets/javascripts/ci/catalog/components/details/ci_resource_components.vue
index 85dfa12c756..f494a3bd224 100644
--- a/app/assets/javascripts/ci/catalog/components/details/ci_resource_components.vue
+++ b/app/assets/javascripts/ci/catalog/components/details/ci_resource_components.vue
@@ -1,11 +1,12 @@
<script>
-import { GlLoadingIcon, GlTableLite } from '@gitlab/ui';
+import { GlButton, GlLoadingIcon, GlTableLite } from '@gitlab/ui';
import { createAlert } from '~/alert';
import { __, s__ } from '~/locale';
import getCiCatalogResourceComponents from '../../graphql/queries/get_ci_catalog_resource_components.query.graphql';
export default {
components: {
+ GlButton,
GlLoadingIcon,
GlTableLite,
},
@@ -70,6 +71,8 @@ export default {
},
],
i18n: {
+ copyText: __('Copy value'),
+ copyAriaText: __('Copy to clipboard'),
inputTitle: s__('CiCatalogComponent|Inputs'),
fetchError: s__("CiCatalogComponent|There was an error fetching this resource's components"),
},
@@ -88,7 +91,24 @@ export default {
>
<h3 class="gl-font-size-h2" data-testid="component-name">{{ component.name }}</h3>
<p class="gl-mt-5">{{ component.description }}</p>
- <pre class="gl-w-85p gl-py-4">{{ generateSnippet(component.path) }}</pre>
+ <div class="gl-display-flex">
+ <pre
+ class="gl-w-85p gl-py-4 gl-display-flex gl-justify-content-space-between gl-m-0 gl-border-r-none"
+ ><span>{{ generateSnippet(component.path) }}</span>
+ </pre>
+ <div class="gl--flex-center gl-bg-gray-10 gl-border gl-border-l-none">
+ <gl-button
+ class="gl-p-4! gl-mr-3!"
+ category="tertiary"
+ icon="copy-to-clipboard"
+ size="small"
+ :title="$options.i18n.copyText"
+ :data-clipboard-text="generateSnippet(component.path)"
+ data-testid="copy-to-clipboard"
+ :aria-label="$options.i18n.copyAriaText"
+ />
+ </div>
+ </div>
<div class="gl-mt-5">
<b class="gl-display-block gl-mb-4"> {{ $options.i18n.inputTitle }}</b>
<gl-table-lite :items="component.inputs.nodes" :fields="$options.fields">
diff --git a/app/assets/javascripts/ci/runner/components/search_tokens/tag_token.vue b/app/assets/javascripts/ci/runner/components/search_tokens/tag_token.vue
index dd1cca0a05c..1f61e878eb0 100644
--- a/app/assets/javascripts/ci/runner/components/search_tokens/tag_token.vue
+++ b/app/assets/javascripts/ci/runner/components/search_tokens/tag_token.vue
@@ -70,7 +70,7 @@ export default {
@fetch-suggestions="fetchTags"
v-on="$listeners"
>
- <template #view-token="{ viewTokenProps: { listeners, inputValue, activeTokenValue } }">
+ <template #view-token="{ viewTokenProps: { listeners = {}, inputValue, activeTokenValue } }">
<gl-token variant="search-value" :class="$options.RUNNER_TAG_BG_CLASS" v-on="listeners">
{{ activeTokenValue ? activeTokenValue.text : inputValue }}
</gl-token>
diff --git a/app/assets/javascripts/notes/components/notes_activity_header.vue b/app/assets/javascripts/notes/components/notes_activity_header.vue
index ce642733396..b4eeea8db02 100644
--- a/app/assets/javascripts/notes/components/notes_activity_header.vue
+++ b/app/assets/javascripts/notes/components/notes_activity_header.vue
@@ -40,7 +40,7 @@ export default {
showAiActions() {
return (
this.resourceGlobalId &&
- this.glFeatures.openaiExperimentation &&
+ (this.glFeatures.openaiExperimentation || this.glFeatures.aiGlobalSwitch) &&
this.glFeatures.summarizeNotes
);
},
diff --git a/app/assets/javascripts/sidebar/components/toggle/toggle_sidebar.vue b/app/assets/javascripts/sidebar/components/toggle/toggle_sidebar.vue
index ba0bf783315..7ce1ceb4bb8 100644
--- a/app/assets/javascripts/sidebar/components/toggle/toggle_sidebar.vue
+++ b/app/assets/javascripts/sidebar/components/toggle/toggle_sidebar.vue
@@ -1,6 +1,7 @@
<script>
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import { __ } from '~/locale';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
name: 'ToggleSidebar',
@@ -10,6 +11,7 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
+ mixins: [glFeatureFlagsMixin()],
props: {
collapsed: {
type: Boolean,
@@ -29,7 +31,13 @@ export default {
return this.collapsed ? 'chevron-double-lg-left' : 'chevron-double-lg-right';
},
allCssClasses() {
- return [this.cssClasses, { 'js-sidebar-collapsed': this.collapsed }];
+ return [
+ this.cssClasses,
+ {
+ 'js-sidebar-collapsed': this.collapsed,
+ 'gl-mt-2': this.glFeatures.notificationsTodosButtons,
+ },
+ ];
},
},
watch: {
diff --git a/app/graphql/mutations/packages/protection/rule/delete.rb b/app/graphql/mutations/packages/protection/rule/delete.rb
new file mode 100644
index 00000000000..bd0159d3c23
--- /dev/null
+++ b/app/graphql/mutations/packages/protection/rule/delete.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Packages
+ module Protection
+ module Rule
+ class Delete < ::Mutations::BaseMutation
+ graphql_name 'DeletePackagesProtectionRule'
+ description 'Deletes a protection rule for packages. ' \
+ 'Available only when feature flag `packages_protected_packages` is enabled.'
+
+ authorize :admin_package
+
+ argument :id,
+ ::Types::GlobalIDType[::Packages::Protection::Rule],
+ required: true,
+ description: 'Global ID of the package protection rule to delete.'
+
+ field :package_protection_rule,
+ Types::Packages::Protection::RuleType,
+ null: true,
+ description: 'Packages protection rule that was deleted successfully.'
+
+ def resolve(id:, **_kwargs)
+ if Feature.disabled?(:packages_protected_packages)
+ raise_resource_not_available_error!("'packages_protected_packages' feature flag is disabled")
+ end
+
+ package_protection_rule = authorized_find!(id: id)
+
+ response = ::Packages::Protection::DeleteRuleService.new(package_protection_rule,
+ current_user: current_user).execute
+
+ { package_protection_rule: response.payload[:package_protection_rule], errors: response.errors }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index 9c292404aed..475066e7279 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -173,6 +173,7 @@ module Types
extensions: [::Gitlab::Graphql::Limit::FieldCallCount => { limit: 1 }]
mount_mutation Mutations::Packages::DestroyFile
mount_mutation Mutations::Packages::Protection::Rule::Create, alpha: { milestone: '16.5' }
+ mount_mutation Mutations::Packages::Protection::Rule::Delete, alpha: { milestone: '16.6' }
mount_mutation Mutations::Packages::DestroyFiles
mount_mutation Mutations::Packages::Cleanup::Policy::Update
mount_mutation Mutations::Echo
diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb
index bd63381e9d1..eda789d5e55 100644
--- a/app/helpers/wiki_helper.rb
+++ b/app/helpers/wiki_helper.rb
@@ -68,14 +68,6 @@ module WikiHelper
render Pajamas::ButtonComponent.new(href: wiki_path(wiki, **link_options), icon: "sort-#{icon_class}", button_options: { class: link_class, title: title })
end
- def wiki_sort_title(key)
- if key == Wiki::CREATED_AT_ORDER
- s_("Wiki|Created date")
- else
- s_("Wiki|Title")
- end
- end
-
def wiki_empty_state_messages(wiki)
case wiki.container
when Project
diff --git a/app/models/packages/nuget/symbol.rb b/app/models/packages/nuget/symbol.rb
index 643b5552d84..3315f11b974 100644
--- a/app/models/packages/nuget/symbol.rb
+++ b/app/models/packages/nuget/symbol.rb
@@ -4,6 +4,7 @@ module Packages
module Nuget
class Symbol < ApplicationRecord
include FileStoreMounter
+ include ShaAttribute
belongs_to :package, -> { where(package_type: :nuget) }, inverse_of: :nuget_symbols
@@ -13,6 +14,8 @@ module Packages
validates :signature, uniqueness: { scope: :file_path }
validates :object_storage_key, uniqueness: true
+ sha256_attribute :file_sha256
+
mount_file_store_uploader SymbolUploader
before_validation :set_object_storage_key, on: :create
diff --git a/app/policies/base_policy.rb b/app/policies/base_policy.rb
index 1ec2495a661..462afbaa475 100644
--- a/app/policies/base_policy.rb
+++ b/app/policies/base_policy.rb
@@ -37,7 +37,7 @@ class BasePolicy < DeclarativePolicy::Base
desc "User is security policy bot"
with_options scope: :user, score: 0
- condition(:security_policy_bot) { @user&.security_policy_bot? }
+ condition(:security_policy_bot) { false }
desc "User is automation bot"
with_options scope: :user, score: 0
diff --git a/app/policies/concerns/policy_actor.rb b/app/policies/concerns/policy_actor.rb
index e000f1514e5..8fa09683b06 100644
--- a/app/policies/concerns/policy_actor.rb
+++ b/app/policies/concerns/policy_actor.rb
@@ -53,10 +53,6 @@ module PolicyActor
false
end
- def security_policy_bot?
- false
- end
-
def automation_bot?
false
end
diff --git a/app/policies/global_policy.rb b/app/policies/global_policy.rb
index 7594360a91c..175f86c9673 100644
--- a/app/policies/global_policy.rb
+++ b/app/policies/global_policy.rb
@@ -63,10 +63,6 @@ class GlobalPolicy < BasePolicy
prevent :access_git
end
- rule { security_policy_bot }.policy do
- enable :access_git
- end
-
rule { project_bot | service_account }.policy do
prevent :log_in
prevent :receive_notifications
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 0a93dcdff9f..28ba819cb60 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -38,9 +38,6 @@ class ProjectPolicy < BasePolicy
desc "User is a project bot"
condition(:project_bot) { user.project_bot? && team_member? }
- desc "User is a security policy bot on the project"
- condition(:security_policy_bot) { user&.security_policy_bot? && team_member? }
-
desc "Project is public"
condition(:public_project, scope: :subject, score: 0) { project.public? }
diff --git a/app/presenters/commit_status_presenter.rb b/app/presenters/commit_status_presenter.rb
index f6720546fab..0858fad1e1a 100644
--- a/app/presenters/commit_status_presenter.rb
+++ b/app/presenters/commit_status_presenter.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
class CommitStatusPresenter < Gitlab::View::Presenter::Delegated
CALLOUT_FAILURE_MESSAGES = {
unknown_failure: 'There is an unknown failure, please try again',
diff --git a/app/services/packages/nuget/symbols/create_symbol_files_service.rb b/app/services/packages/nuget/symbols/create_symbol_files_service.rb
index 03e14ba00e1..5f0b8762054 100644
--- a/app/services/packages/nuget/symbols/create_symbol_files_service.rb
+++ b/app/services/packages/nuget/symbols/create_symbol_files_service.rb
@@ -18,7 +18,7 @@ module Packages
process_symbol_entries
rescue ExtractionError => e
- Gitlab::ErrorTracking.log_exception(e, class: self.class.name, package_id: package.id)
+ Gitlab::ErrorTracking.track_exception(e, class: self.class.name, package_id: package.id)
end
private
@@ -31,7 +31,7 @@ module Packages
raise ExtractionError, 'too many symbol entries' if index >= SYMBOL_ENTRIES_LIMIT
entry.extract(tmp_file.path) { true }
- File.open(tmp_file.path) do |file|
+ File.open(tmp_file.path, 'rb') do |file|
create_symbol(entry.name, file)
end
end
@@ -43,25 +43,27 @@ module Packages
end
def create_symbol(path, file)
- signature = extract_signature(file.read(1.kilobyte))
- return if signature.blank?
+ signature, checksum = extract_signature_and_checksum(file)
+ return if signature.blank? || checksum.blank?
::Packages::Nuget::Symbol.create!(
package: package,
file: { tempfile: file, filename: path.downcase, content_type: CONTENT_TYPE },
file_path: path,
signature: signature,
- size: file.size
+ size: file.size,
+ file_sha256: checksum
)
rescue StandardError => e
- Gitlab::ErrorTracking.log_exception(e, class: self.class.name, package_id: package.id)
+ Gitlab::ErrorTracking.track_exception(e, class: self.class.name, package_id: package.id)
end
- def extract_signature(content_fragment)
- ExtractSymbolSignatureService
- .new(content_fragment)
+ def extract_signature_and_checksum(file)
+ ::Packages::Nuget::Symbols::ExtractSignatureAndChecksumService
+ .new(file)
.execute
.payload
+ .values_at(:signature, :checksum)
end
end
end
diff --git a/app/services/packages/nuget/symbols/extract_symbol_signature_service.rb b/app/services/packages/nuget/symbols/extract_signature_and_checksum_service.rb
index c2ccdb517b5..fd37d139145 100644
--- a/app/services/packages/nuget/symbols/extract_symbol_signature_service.rb
+++ b/app/services/packages/nuget/symbols/extract_signature_and_checksum_service.rb
@@ -3,45 +3,43 @@
module Packages
module Nuget
module Symbols
- class ExtractSymbolSignatureService
+ class ExtractSignatureAndChecksumService
include Gitlab::Utils::StrongMemoize
# More information about the GUID format can be found here:
# https://github.com/dotnet/symstore/blob/main/docs/specs/SSQP_Key_Conventions.md#key-formatting-basic-rules
GUID_START_INDEX = 7
- GUID_END_INDEX = 22
+ GUID_END_INDEX = 26
+ SIGNATURE_LENGTH = 16
+ TWENTY_ZEROED_BYTES = "\u0000" * 20
GUID_PARTS_LENGTHS = [4, 2, 2, 8].freeze
GUID_AGE_PART = 'FFFFFFFF'
TWO_CHARACTER_HEX_REGEX = /\h{2}/
+ GUID_CHUNK_SIZE = 256.bytes
+ SHA_CHUNK_SIZE = 16.kilobytes
# The extraction of the signature in this service is based on the following documentation:
# https://github.com/dotnet/symstore/blob/main/docs/specs/SSQP_Key_Conventions.md#portable-pdb-signature
- def initialize(symbol_content)
- @symbol_content = symbol_content
+ def initialize(file)
+ @file = file
end
def execute
return error_response unless signature
- ServiceResponse.success(payload: signature)
+ ServiceResponse.success(payload: { signature: signature, checksum: checksum })
end
private
- attr_reader :symbol_content
+ attr_reader :file
def signature
- # Find the index of the first occurrence of 'Blob'
- guid_index = symbol_content.index('Blob')
- return if guid_index.nil?
-
- # Extract the binary GUID from the symbol content
- guid = symbol_content[(guid_index + GUID_START_INDEX)..(guid_index + GUID_END_INDEX)]
- return if guid.nil?
+ return unless pdb_id
# Convert the GUID into an array of two-character hex strings
- guid = guid.unpack('H*').flat_map { |el| el.scan(TWO_CHARACTER_HEX_REGEX) }
+ guid = pdb_id.first(SIGNATURE_LENGTH).unpack('H*').flat_map { |el| el.scan(TWO_CHARACTER_HEX_REGEX) }
# Reorder the GUID parts based on arbitrary lengths
guid = GUID_PARTS_LENGTHS.map { |length| guid.shift(length) }
@@ -54,6 +52,36 @@ module Packages
end
strong_memoize_attr :signature
+ # https://github.com/dotnet/corefx/blob/master/src/System.Reflection.Metadata/specs/PE-COFF.md#portable-pdb-checksum
+ def checksum
+ sha = OpenSSL::Digest.new('SHA256')
+ count = 0
+ chunk = (+'').force_encoding(Encoding::BINARY)
+ file.rewind
+
+ while file.read(SHA_CHUNK_SIZE, chunk)
+ count += 1
+ chunk[pdb_id] = TWENTY_ZEROED_BYTES if count == 1
+ sha.update(chunk)
+ end
+
+ sha.hexdigest
+ end
+
+ def pdb_id
+ # The ID is located in the first 256 bytes of the symbol `.pdb` file
+ chunk = file.read(GUID_CHUNK_SIZE)
+ return unless chunk
+
+ # Find the index of the first occurrence of 'Blob'
+ guid_index = chunk.index('Blob')
+ return unless guid_index
+
+ # Extract the binary GUID from the symbol content
+ chunk[(guid_index + GUID_START_INDEX)..(guid_index + GUID_END_INDEX)]
+ end
+ strong_memoize_attr :pdb_id
+
def error_response
ServiceResponse.error(message: 'Could not find the signature in the symbol file')
end
diff --git a/app/services/packages/protection/delete_rule_service.rb b/app/services/packages/protection/delete_rule_service.rb
new file mode 100644
index 00000000000..a1fa111b57b
--- /dev/null
+++ b/app/services/packages/protection/delete_rule_service.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module Packages
+ module Protection
+ class DeleteRuleService
+ include Gitlab::Allowable
+
+ def initialize(package_protection_rule, current_user:)
+ if package_protection_rule.blank? || current_user.blank?
+ raise ArgumentError,
+ 'package_protection_rule and current_user must be set'
+ end
+
+ @package_protection_rule = package_protection_rule
+ @current_user = current_user
+ end
+
+ def execute
+ unless can?(current_user, :admin_package, package_protection_rule.project)
+ error_message = _('Unauthorized to delete a package protection rule')
+ return service_response_error(message: error_message)
+ end
+
+ deleted_package_protection_rule = package_protection_rule.destroy!
+
+ ServiceResponse.success(payload: { package_protection_rule: deleted_package_protection_rule })
+ rescue StandardError => e
+ service_response_error(message: e.message)
+ end
+
+ private
+
+ attr_reader :package_protection_rule, :current_user
+
+ def service_response_error(message:)
+ ServiceResponse.error(
+ message: message,
+ payload: { package_protection_rule: nil }
+ )
+ end
+ end
+ end
+end
diff --git a/app/views/admin/application_settings/metrics_and_profiling.html.haml b/app/views/admin/application_settings/metrics_and_profiling.html.haml
index ed1342be1fc..12b9df6719d 100644
--- a/app/views/admin/application_settings/metrics_and_profiling.html.haml
+++ b/app/views/admin/application_settings/metrics_and_profiling.html.haml
@@ -41,7 +41,7 @@
.settings-content
= render 'performance_bar'
-%section.settings.as-usage.no-animate#js-usage-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'usage_statistics_settings_content' } }
+%section.settings.as-usage.no-animate#js-usage-settings{ class: ('expanded' if expanded_by_default?), data: { testid: 'usage-statistics-settings-content' } }
.settings-header#usage-statistics
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
= _('Usage statistics')
diff --git a/app/views/import/gitlab_projects/new.html.haml b/app/views/import/gitlab_projects/new.html.haml
index 079123e989e..8e51eafd84c 100644
--- a/app/views/import/gitlab_projects/new.html.haml
+++ b/app/views/import/gitlab_projects/new.html.haml
@@ -21,7 +21,7 @@
= file_field_tag :file, class: ''
.row
.form-actions.col-sm-12
- = render Pajamas::ButtonComponent.new(type: :submit, variant: :confirm, button_options: { class: 'gl-mr-2', data: { qa_selector: 'import_project_button' }}) do
+ = render Pajamas::ButtonComponent.new(type: :submit, variant: :confirm, button_options: { class: 'gl-mr-2', data: { testid: 'import-project-button' }}) do
= _('Import project')
= render Pajamas::ButtonComponent.new(href: new_project_path) do
= _('Cancel')
diff --git a/app/views/import/shared/_new_project_form.html.haml b/app/views/import/shared/_new_project_form.html.haml
index 6000612a285..042d94ad1b6 100644
--- a/app/views/import/shared/_new_project_form.html.haml
+++ b/app/views/import/shared/_new_project_form.html.haml
@@ -1,7 +1,7 @@
.row
.form-group.project-name.col-sm-12
= label_tag :name, _('Project name'), class: 'label-bold'
- = text_field_tag :name, @name, placeholder: "My awesome project", class: "js-project-name form-control gl-form-input input-lg", autofocus: true, required: true, aria: { required: true }, data: { qa_selector: 'project_name_field' }
+ = text_field_tag :name, @name, placeholder: "My awesome project", class: "js-project-name form-control gl-form-input input-lg", autofocus: true, required: true, aria: { required: true }, data: { testid: 'project-name-field' }
.form-group.col-12.col-sm-6.gl-pr-0
= label_tag :namespace_id, _('Project URL'), class: 'label-bold'
.input-group.gl-flex-nowrap
@@ -21,4 +21,4 @@
.gl-align-self-center.gl-pl-5 /
.form-group.col-12.col-sm-6.project-path
= label_tag :path, _('Project slug'), class: 'label-bold'
- = text_field_tag :path, @path, placeholder: "my-awesome-project", class: "js-path-name form-control gl-form-input", required: true, aria: { required: true }, data: { qa_selector: 'project_slug_field' }
+ = text_field_tag :path, @path, placeholder: "my-awesome-project", class: "js-path-name form-control gl-form-input", required: true, aria: { required: true }
diff --git a/app/views/projects/_import_project_pane.html.haml b/app/views/projects/_import_project_pane.html.haml
index 6315c6dc52d..3e92ef25552 100644
--- a/app/views/projects/_import_project_pane.html.haml
+++ b/app/views/projects/_import_project_pane.html.haml
@@ -17,7 +17,7 @@
= html_escape(_("Importing GitLab projects? Migrating GitLab projects when migrating groups by direct transfer is in Beta. %{link_start}Learn more.%{link_end}")) % { link_start: docs_link, link_end: '</a>'.html_safe }
.import-buttons
- if gitlab_project_import_enabled?
- .import_gitlab_project.has-tooltip{ data: { container: 'body', qa_selector: 'gitlab_import_button' } }
+ .import_gitlab_project.has-tooltip{ data: { container: 'body', testid: 'gitlab-import-button' } }
= render Pajamas::ButtonComponent.new(href: '#', icon: 'tanuki', button_options: { class: 'btn_import_gitlab_project js-import-project-btn', data: { href: new_import_gitlab_project_path, platform: 'gitlab_export', **tracking_attrs_data(track_label, 'click_button', 'gitlab_export') } }) do
= _('GitLab export')
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 1392c7ab89f..f018e4f122e 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -16,7 +16,7 @@
%aside.right-sidebar.js-right-sidebar.js-issuable-sidebar{ data: { always_show_toggle: true, signed: { in: signed_in }, issuable_type: issuable_type }, class: "#{sidebar_gutter_collapsed_class(is_merge_request_with_flag)} #{'right-sidebar-merge-requests' if is_merge_request_with_flag}", 'aria-live' => 'polite', 'aria-label': issuable_type }
.issuable-sidebar{ class: "#{'is-merge-request' if is_merge_request_with_flag}" }
.issuable-sidebar-header{ class: "gl-pb-4! #{'gl-pb-2! gl-md-display-flex gl-justify-content-end gl-lg-display-none!' if is_merge_request_with_flag}" }
- = render Pajamas::ButtonComponent.new(button_options: { class: "gutter-toggle float-right js-sidebar-toggle has-tooltip gl-shadow-none! #{'gl-display-block' if moved_sidebar_enabled}", type: 'button', 'aria-label' => _('Toggle sidebar'), title: sidebar_gutter_tooltip_text, data: { container: 'body', placement: 'left', boundary: 'viewport' } }) do
+ = render Pajamas::ButtonComponent.new(button_options: { class: "gutter-toggle float-right js-sidebar-toggle has-tooltip gl-shadow-none! #{'gl-display-block' if moved_sidebar_enabled} #{'gl-mt-2' if notifications_todos_buttons_enabled?}" , type: 'button', 'aria-label' => _('Toggle sidebar'), title: sidebar_gutter_tooltip_text, data: { container: 'body', placement: 'left', boundary: 'viewport' } }) do
= sidebar_gutter_toggle_icon
- if signed_in
- if !is_merge_request_with_flag
diff --git a/config/feature_flags/development/ai_self_discover.yml b/config/feature_flags/development/ai_self_discover.yml
deleted file mode 100644
index ef5e2bc8926..00000000000
--- a/config/feature_flags/development/ai_self_discover.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ai_self_discover
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132267
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/425908
-milestone: '16.4'
-type: development
-group: group::ai framework
-default_enabled: false
diff --git a/db/docs/approval_group_rules.yml b/db/docs/approval_group_rules.yml
new file mode 100644
index 00000000000..35f75338480
--- /dev/null
+++ b/db/docs/approval_group_rules.yml
@@ -0,0 +1,10 @@
+---
+table_name: approval_group_rules
+classes:
+ - ApprovalRules::ApprovalGroupRule
+feature_categories:
+ - source_code_management
+description: Keeps approval group rules
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132651
+milestone: '16.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/approval_group_rules_groups.yml b/db/docs/approval_group_rules_groups.yml
new file mode 100644
index 00000000000..0599af6ac15
--- /dev/null
+++ b/db/docs/approval_group_rules_groups.yml
@@ -0,0 +1,9 @@
+---
+table_name: approval_group_rules_groups
+classes: []
+feature_categories:
+ - source_code_management
+description: Keeps connection between group and a group approval rule
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132651
+milestone: '16.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/approval_group_rules_protected_branches.yml b/db/docs/approval_group_rules_protected_branches.yml
new file mode 100644
index 00000000000..ac55f0980be
--- /dev/null
+++ b/db/docs/approval_group_rules_protected_branches.yml
@@ -0,0 +1,9 @@
+---
+table_name: approval_group_rules_protected_branches
+classes: []
+feature_categories:
+ - source_code_management
+description: Keeps relation between approval group rules and protected branches.
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132651
+milestone: '16.5'
+gitlab_schema: gitlab_main
diff --git a/db/docs/approval_group_rules_users.yml b/db/docs/approval_group_rules_users.yml
new file mode 100644
index 00000000000..67271d2a35d
--- /dev/null
+++ b/db/docs/approval_group_rules_users.yml
@@ -0,0 +1,9 @@
+---
+table_name: approval_group_rules_users
+classes: []
+feature_categories:
+ - source_code_management
+description: Keeps connection between user and a group approval rule
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132651
+milestone: '16.5'
+gitlab_schema: gitlab_main
diff --git a/db/migrate/20230926092914_add_approval_group_rules.rb b/db/migrate/20230926092914_add_approval_group_rules.rb
new file mode 100644
index 00000000000..c5f4a356df1
--- /dev/null
+++ b/db/migrate/20230926092914_add_approval_group_rules.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+class AddApprovalGroupRules < Gitlab::Database::Migration[2.1]
+ INDEX_GROUP_ID_TYPE_NAME = 'idx_on_approval_group_rules_group_id_type_name'
+ INDEX_ANY_APPROVER_TYPE = 'idx_on_approval_group_rules_any_approver_type'
+ INDEX_SECURITY_ORCHESTRATION_POLICY_CONFURATION = 'idx_on_approval_group_rules_security_orch_policy'
+ disable_ddl_transaction!
+
+ def up
+ create_table :approval_group_rules do |t|
+ t.references :group, references: :namespaces, null: false,
+ foreign_key: { to_table: :namespaces, on_delete: :cascade }, index: false
+ t.timestamps_with_timezone
+ t.integer :approvals_required, limit: 2, null: false, default: 0
+ t.integer :report_type, limit: 2, null: true, default: nil
+ t.integer :rule_type, limit: 2, null: false, default: 1
+ t.integer :security_orchestration_policy_configuration_id, limit: 5
+ t.integer :scan_result_policy_id, limit: 5, index: true
+ t.text :name, null: false, limit: 255
+
+ t.index [:group_id, :rule_type, :name], unique: true, name: INDEX_GROUP_ID_TYPE_NAME
+ t.index [:group_id, :rule_type], where: 'rule_type = 4', unique: true, name: INDEX_ANY_APPROVER_TYPE
+ t.index :security_orchestration_policy_configuration_id, name: INDEX_SECURITY_ORCHESTRATION_POLICY_CONFURATION
+ end
+
+ add_text_limit :approval_group_rules, :name, 255
+ end
+
+ def down
+ with_lock_retries do
+ drop_table :approval_group_rules
+ end
+ end
+end
diff --git a/db/migrate/20230926092944_add_approval_group_rules_groups.rb b/db/migrate/20230926092944_add_approval_group_rules_groups.rb
new file mode 100644
index 00000000000..52ac86737e6
--- /dev/null
+++ b/db/migrate/20230926092944_add_approval_group_rules_groups.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddApprovalGroupRulesGroups < Gitlab::Database::Migration[2.1]
+ INDEX_RULE_GROUP = 'idx_on_approval_group_rules_groups_rule_group'
+
+ def up
+ create_table :approval_group_rules_groups do |t|
+ t.bigint :approval_group_rule_id, null: false
+ t.bigint :group_id, null: false, index: true
+
+ t.index [:approval_group_rule_id, :group_id], unique: true, name: INDEX_RULE_GROUP
+ end
+ end
+
+ def down
+ drop_table :approval_group_rules_groups
+ end
+end
diff --git a/db/migrate/20230926093004_add_approval_group_rules_users.rb b/db/migrate/20230926093004_add_approval_group_rules_users.rb
new file mode 100644
index 00000000000..8c6d14ce9ac
--- /dev/null
+++ b/db/migrate/20230926093004_add_approval_group_rules_users.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddApprovalGroupRulesUsers < Gitlab::Database::Migration[2.1]
+ INDEX_RULE_USER = 'idx_on_approval_group_rules_users_rule_user'
+
+ def up
+ create_table :approval_group_rules_users do |t|
+ t.bigint :approval_group_rule_id, null: false
+ t.bigint :user_id, null: false, index: true
+
+ t.index [:approval_group_rule_id, :user_id], unique: true, name: INDEX_RULE_USER
+ end
+ end
+
+ def down
+ drop_table :approval_group_rules_users
+ end
+end
diff --git a/db/migrate/20230926093025_add_approval_group_rules_protected_branches.rb b/db/migrate/20230926093025_add_approval_group_rules_protected_branches.rb
new file mode 100644
index 00000000000..5f623ec9edb
--- /dev/null
+++ b/db/migrate/20230926093025_add_approval_group_rules_protected_branches.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class AddApprovalGroupRulesProtectedBranches < Gitlab::Database::Migration[2.1]
+ INDEX_RULE_PROTECTED_BRANCH = 'idx_on_approval_group_rules_protected_branch'
+ INDEX_APPROVAL_GROUP_RULE = 'idx_on_approval_group_rules'
+ INDEX_PROTECTED_BRANCH = 'idx_on_protected_branch'
+
+ def up
+ create_table :approval_group_rules_protected_branches do |t|
+ t.bigint :approval_group_rule_id, null: false
+ t.bigint :protected_branch_id, null: false
+
+ t.index :protected_branch_id, name: INDEX_PROTECTED_BRANCH
+ t.index [:approval_group_rule_id, :protected_branch_id], unique: true, name: INDEX_RULE_PROTECTED_BRANCH
+ end
+ end
+
+ def down
+ drop_table :approval_group_rules_protected_branches
+ end
+end
diff --git a/db/migrate/20230926093101_add_fk_to_approval_rule_on_approval_group_rules_users.rb b/db/migrate/20230926093101_add_fk_to_approval_rule_on_approval_group_rules_users.rb
new file mode 100644
index 00000000000..4c11542e9e6
--- /dev/null
+++ b/db/migrate/20230926093101_add_fk_to_approval_rule_on_approval_group_rules_users.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddFkToApprovalRuleOnApprovalGroupRulesUsers < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :approval_group_rules_users,
+ :approval_group_rules,
+ column: :approval_group_rule_id,
+ on_delete: :cascade
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key :approval_group_rules_users, column: :approval_group_rule_id
+ end
+ end
+end
diff --git a/db/migrate/20230926093144_add_fk_to_user_on_approval_group_rules_users.rb b/db/migrate/20230926093144_add_fk_to_user_on_approval_group_rules_users.rb
new file mode 100644
index 00000000000..30c08c8966d
--- /dev/null
+++ b/db/migrate/20230926093144_add_fk_to_user_on_approval_group_rules_users.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddFkToUserOnApprovalGroupRulesUsers < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :approval_group_rules_users, :users, column: :user_id, on_delete: :cascade
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key :approval_group_rules_users, column: :user_id
+ end
+ end
+end
diff --git a/db/migrate/20230926093211_add_fk_to_approval_rule_on_approval_group_rules_groups.rb b/db/migrate/20230926093211_add_fk_to_approval_rule_on_approval_group_rules_groups.rb
new file mode 100644
index 00000000000..44526150266
--- /dev/null
+++ b/db/migrate/20230926093211_add_fk_to_approval_rule_on_approval_group_rules_groups.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class AddFkToApprovalRuleOnApprovalGroupRulesGroups < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :approval_group_rules_groups, :approval_group_rules, column: :approval_group_rule_id,
+ on_delete: :cascade
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key :approval_group_rules_groups, column: :approval_group_rule_id
+ end
+ end
+end
diff --git a/db/migrate/20230926093251_add_fk_to_group_on_approval_group_rules_groups.rb b/db/migrate/20230926093251_add_fk_to_group_on_approval_group_rules_groups.rb
new file mode 100644
index 00000000000..2052993af05
--- /dev/null
+++ b/db/migrate/20230926093251_add_fk_to_group_on_approval_group_rules_groups.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddFkToGroupOnApprovalGroupRulesGroups < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :approval_group_rules_groups, :namespaces, column: :group_id, on_delete: :cascade
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key :approval_group_rules_groups, column: :group_id
+ end
+ end
+end
diff --git a/db/migrate/20230926105440_add_fk_to_approval_rule_on_approval_group_rules_protected_branches.rb b/db/migrate/20230926105440_add_fk_to_approval_rule_on_approval_group_rules_protected_branches.rb
new file mode 100644
index 00000000000..cd799656ac9
--- /dev/null
+++ b/db/migrate/20230926105440_add_fk_to_approval_rule_on_approval_group_rules_protected_branches.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddFkToApprovalRuleOnApprovalGroupRulesProtectedBranches < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :approval_group_rules_protected_branches,
+ :approval_group_rules,
+ column: :approval_group_rule_id,
+ on_delete: :cascade
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key :approval_group_rules_protected_branches, column: :approval_group_rule_id
+ end
+ end
+end
diff --git a/db/migrate/20230926105931_add_fk_to_protected_branch_on_approval_group_rules_protected_branches.rb b/db/migrate/20230926105931_add_fk_to_protected_branch_on_approval_group_rules_protected_branches.rb
new file mode 100644
index 00000000000..5804a8da4d8
--- /dev/null
+++ b/db/migrate/20230926105931_add_fk_to_protected_branch_on_approval_group_rules_protected_branches.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class AddFkToProtectedBranchOnApprovalGroupRulesProtectedBranches < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :approval_group_rules_protected_branches, :protected_branches,
+ column: :protected_branch_id, on_delete: :cascade
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key :approval_group_rules_protected_branches, column: :protected_branch_id
+ end
+ end
+end
diff --git a/db/migrate/20230928145555_add_fk_to_security_orchestration_policy_configuration_on_approval_group_rules.rb b/db/migrate/20230928145555_add_fk_to_security_orchestration_policy_configuration_on_approval_group_rules.rb
new file mode 100644
index 00000000000..2630adcf81f
--- /dev/null
+++ b/db/migrate/20230928145555_add_fk_to_security_orchestration_policy_configuration_on_approval_group_rules.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddFkToSecurityOrchestrationPolicyConfigurationOnApprovalGroupRules < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :approval_group_rules, :security_orchestration_policy_configurations,
+ column: :security_orchestration_policy_configuration_id, on_delete: :cascade
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key :approval_group_rules,
+ column: :security_orchestration_policy_configuration_id
+ end
+ end
+end
diff --git a/db/migrate/20230928145637_add_fk_to_scan_result_policy_on_approval_group_rules.rb b/db/migrate/20230928145637_add_fk_to_scan_result_policy_on_approval_group_rules.rb
new file mode 100644
index 00000000000..f30d03e0f62
--- /dev/null
+++ b/db/migrate/20230928145637_add_fk_to_scan_result_policy_on_approval_group_rules.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class AddFkToScanResultPolicyOnApprovalGroupRules < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :approval_group_rules, :scan_result_policies,
+ column: :scan_result_policy_id, on_delete: :cascade
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key :approval_group_rules, column: :scan_result_policy_id
+ end
+ end
+end
diff --git a/db/migrate/20231019104211_add_file_sha256_to_packages_nuget_symbols.rb b/db/migrate/20231019104211_add_file_sha256_to_packages_nuget_symbols.rb
new file mode 100644
index 00000000000..374fa91000d
--- /dev/null
+++ b/db/migrate/20231019104211_add_file_sha256_to_packages_nuget_symbols.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class AddFileSha256ToPackagesNugetSymbols < Gitlab::Database::Migration[2.1]
+ enable_lock_retries!
+
+ def up
+ add_column :packages_nuget_symbols, :file_sha256, :binary
+ end
+
+ def down
+ remove_column :packages_nuget_symbols, :file_sha256
+ end
+end
diff --git a/db/schema_migrations/20230926092914 b/db/schema_migrations/20230926092914
new file mode 100644
index 00000000000..391ade3aab0
--- /dev/null
+++ b/db/schema_migrations/20230926092914
@@ -0,0 +1 @@
+0018bc2180eeb632d75132b6d82e959e772ff1e7d8966310858e304d07d4ec34 \ No newline at end of file
diff --git a/db/schema_migrations/20230926092944 b/db/schema_migrations/20230926092944
new file mode 100644
index 00000000000..1fe6c64634a
--- /dev/null
+++ b/db/schema_migrations/20230926092944
@@ -0,0 +1 @@
+e2b4cdafd6147740ad43c286d90f7feec9f70d66a510d58a3cc3c33b0d703b49 \ No newline at end of file
diff --git a/db/schema_migrations/20230926093004 b/db/schema_migrations/20230926093004
new file mode 100644
index 00000000000..30407142a30
--- /dev/null
+++ b/db/schema_migrations/20230926093004
@@ -0,0 +1 @@
+d3d90178100e92cffe263715cdfc3c9ddcb47ce804f3ffd92d5bc4326de0244c \ No newline at end of file
diff --git a/db/schema_migrations/20230926093025 b/db/schema_migrations/20230926093025
new file mode 100644
index 00000000000..6a5ef092c69
--- /dev/null
+++ b/db/schema_migrations/20230926093025
@@ -0,0 +1 @@
+840bc159c277271b66f4348c31d912485c04b8ee1b15227c96dcc690f6b93311 \ No newline at end of file
diff --git a/db/schema_migrations/20230926093101 b/db/schema_migrations/20230926093101
new file mode 100644
index 00000000000..2ce67dab37f
--- /dev/null
+++ b/db/schema_migrations/20230926093101
@@ -0,0 +1 @@
+9a560649866367e556cf841e20f981b6c09fe03d1054f0db37cb510fbfbaef13 \ No newline at end of file
diff --git a/db/schema_migrations/20230926093144 b/db/schema_migrations/20230926093144
new file mode 100644
index 00000000000..b383692607f
--- /dev/null
+++ b/db/schema_migrations/20230926093144
@@ -0,0 +1 @@
+eba011de5a174a93e5159c765c093d3a6519111769a1ac09b2f996322cf3973e \ No newline at end of file
diff --git a/db/schema_migrations/20230926093211 b/db/schema_migrations/20230926093211
new file mode 100644
index 00000000000..9befd202129
--- /dev/null
+++ b/db/schema_migrations/20230926093211
@@ -0,0 +1 @@
+f9659a07b4c7b2d4508f1de231e759cf4e15e684ecaa4231ff6069b4ba203e20 \ No newline at end of file
diff --git a/db/schema_migrations/20230926093251 b/db/schema_migrations/20230926093251
new file mode 100644
index 00000000000..63bb045e437
--- /dev/null
+++ b/db/schema_migrations/20230926093251
@@ -0,0 +1 @@
+9df85930f78c6fa9e02252877d136aab3167a8ac1134cbd321c26f5958899f06 \ No newline at end of file
diff --git a/db/schema_migrations/20230926105440 b/db/schema_migrations/20230926105440
new file mode 100644
index 00000000000..957b7cbbbac
--- /dev/null
+++ b/db/schema_migrations/20230926105440
@@ -0,0 +1 @@
+0be5d3565d71dc9656fd90dbd404ea0314ff29f6da9ca9ef2d100bcc9515308b \ No newline at end of file
diff --git a/db/schema_migrations/20230926105931 b/db/schema_migrations/20230926105931
new file mode 100644
index 00000000000..f158665e529
--- /dev/null
+++ b/db/schema_migrations/20230926105931
@@ -0,0 +1 @@
+021dbeb0a8c5ebecfa647344b1e99dd1698ae3fb72a8857409551070b23f9f49 \ No newline at end of file
diff --git a/db/schema_migrations/20230928145555 b/db/schema_migrations/20230928145555
new file mode 100644
index 00000000000..860364e57cc
--- /dev/null
+++ b/db/schema_migrations/20230928145555
@@ -0,0 +1 @@
+71e2f63bf9a327f62d21c2407b9ccebe779e0fd881266467f180cf285edc326f \ No newline at end of file
diff --git a/db/schema_migrations/20230928145637 b/db/schema_migrations/20230928145637
new file mode 100644
index 00000000000..47d4c9b0593
--- /dev/null
+++ b/db/schema_migrations/20230928145637
@@ -0,0 +1 @@
+1e9bf34cc708dd8637e4e636894fb9b7894c6d54832b3b42c88af17c4ed87532 \ No newline at end of file
diff --git a/db/schema_migrations/20231019104211 b/db/schema_migrations/20231019104211
new file mode 100644
index 00000000000..cce6cc892da
--- /dev/null
+++ b/db/schema_migrations/20231019104211
@@ -0,0 +1 @@
+e2ee8bcb49b470bbea1874f6a63c9b7a2fd67ef4223cf5d358de1fca4e3f36be \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index da04d20ff84..043dd201d5a 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -12000,6 +12000,74 @@ CREATE SEQUENCE application_settings_id_seq
ALTER SEQUENCE application_settings_id_seq OWNED BY application_settings.id;
+CREATE TABLE approval_group_rules (
+ id bigint NOT NULL,
+ group_id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ approvals_required smallint DEFAULT 0 NOT NULL,
+ report_type smallint,
+ rule_type smallint DEFAULT 1 NOT NULL,
+ security_orchestration_policy_configuration_id bigint,
+ scan_result_policy_id bigint,
+ name text NOT NULL,
+ CONSTRAINT check_25d42add43 CHECK ((char_length(name) <= 255))
+);
+
+CREATE TABLE approval_group_rules_groups (
+ id bigint NOT NULL,
+ approval_group_rule_id bigint NOT NULL,
+ group_id bigint NOT NULL
+);
+
+CREATE SEQUENCE approval_group_rules_groups_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE approval_group_rules_groups_id_seq OWNED BY approval_group_rules_groups.id;
+
+CREATE SEQUENCE approval_group_rules_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE approval_group_rules_id_seq OWNED BY approval_group_rules.id;
+
+CREATE TABLE approval_group_rules_protected_branches (
+ id bigint NOT NULL,
+ approval_group_rule_id bigint NOT NULL,
+ protected_branch_id bigint NOT NULL
+);
+
+CREATE SEQUENCE approval_group_rules_protected_branches_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE approval_group_rules_protected_branches_id_seq OWNED BY approval_group_rules_protected_branches.id;
+
+CREATE TABLE approval_group_rules_users (
+ id bigint NOT NULL,
+ approval_group_rule_id bigint NOT NULL,
+ user_id bigint NOT NULL
+);
+
+CREATE SEQUENCE approval_group_rules_users_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE approval_group_rules_users_id_seq OWNED BY approval_group_rules_users.id;
+
CREATE TABLE approval_merge_request_rule_sources (
id bigint NOT NULL,
approval_merge_request_rule_id bigint NOT NULL,
@@ -20148,6 +20216,7 @@ CREATE TABLE packages_nuget_symbols (
file_path text NOT NULL,
signature text NOT NULL,
object_storage_key text NOT NULL,
+ file_sha256 bytea,
CONSTRAINT check_0e93ca58b7 CHECK ((char_length(file) <= 255)),
CONSTRAINT check_28b82b08fa CHECK ((char_length(object_storage_key) <= 255)),
CONSTRAINT check_30b0ef2ca2 CHECK ((char_length(file_path) <= 255)),
@@ -25924,6 +25993,14 @@ ALTER TABLE ONLY application_setting_terms ALTER COLUMN id SET DEFAULT nextval('
ALTER TABLE ONLY application_settings ALTER COLUMN id SET DEFAULT nextval('application_settings_id_seq'::regclass);
+ALTER TABLE ONLY approval_group_rules ALTER COLUMN id SET DEFAULT nextval('approval_group_rules_id_seq'::regclass);
+
+ALTER TABLE ONLY approval_group_rules_groups ALTER COLUMN id SET DEFAULT nextval('approval_group_rules_groups_id_seq'::regclass);
+
+ALTER TABLE ONLY approval_group_rules_protected_branches ALTER COLUMN id SET DEFAULT nextval('approval_group_rules_protected_branches_id_seq'::regclass);
+
+ALTER TABLE ONLY approval_group_rules_users ALTER COLUMN id SET DEFAULT nextval('approval_group_rules_users_id_seq'::regclass);
+
ALTER TABLE ONLY approval_merge_request_rule_sources ALTER COLUMN id SET DEFAULT nextval('approval_merge_request_rule_sources_id_seq'::regclass);
ALTER TABLE ONLY approval_merge_request_rules ALTER COLUMN id SET DEFAULT nextval('approval_merge_request_rules_id_seq'::regclass);
@@ -27725,6 +27802,18 @@ ALTER TABLE ONLY application_setting_terms
ALTER TABLE ONLY application_settings
ADD CONSTRAINT application_settings_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY approval_group_rules_groups
+ ADD CONSTRAINT approval_group_rules_groups_pkey PRIMARY KEY (id);
+
+ALTER TABLE ONLY approval_group_rules
+ ADD CONSTRAINT approval_group_rules_pkey PRIMARY KEY (id);
+
+ALTER TABLE ONLY approval_group_rules_protected_branches
+ ADD CONSTRAINT approval_group_rules_protected_branches_pkey PRIMARY KEY (id);
+
+ALTER TABLE ONLY approval_group_rules_users
+ ADD CONSTRAINT approval_group_rules_users_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY approval_merge_request_rule_sources
ADD CONSTRAINT approval_merge_request_rule_sources_pkey PRIMARY KEY (id);
@@ -31100,6 +31189,18 @@ CREATE INDEX idx_mrs_on_target_id_and_created_at_and_state_id ON merge_requests
CREATE UNIQUE INDEX idx_namespace_settings_on_default_compliance_framework_id ON namespace_settings USING btree (default_compliance_framework_id);
+CREATE UNIQUE INDEX idx_on_approval_group_rules_any_approver_type ON approval_group_rules USING btree (group_id, rule_type) WHERE (rule_type = 4);
+
+CREATE UNIQUE INDEX idx_on_approval_group_rules_group_id_type_name ON approval_group_rules USING btree (group_id, rule_type, name);
+
+CREATE UNIQUE INDEX idx_on_approval_group_rules_groups_rule_group ON approval_group_rules_groups USING btree (approval_group_rule_id, group_id);
+
+CREATE UNIQUE INDEX idx_on_approval_group_rules_protected_branch ON approval_group_rules_protected_branches USING btree (approval_group_rule_id, protected_branch_id);
+
+CREATE INDEX idx_on_approval_group_rules_security_orch_policy ON approval_group_rules USING btree (security_orchestration_policy_configuration_id);
+
+CREATE UNIQUE INDEX idx_on_approval_group_rules_users_rule_user ON approval_group_rules_users USING btree (approval_group_rule_id, user_id);
+
CREATE UNIQUE INDEX idx_on_compliance_management_frameworks_namespace_id_name ON compliance_management_frameworks USING btree (namespace_id, name);
CREATE UNIQUE INDEX idx_on_external_approval_rules_project_id_external_url ON external_approval_rules USING btree (project_id, external_url);
@@ -31110,6 +31211,8 @@ CREATE UNIQUE INDEX idx_on_external_status_checks_project_id_external_url ON ext
CREATE UNIQUE INDEX idx_on_external_status_checks_project_id_name ON external_status_checks USING btree (project_id, name);
+CREATE INDEX idx_on_protected_branch ON approval_group_rules_protected_branches USING btree (protected_branch_id);
+
CREATE INDEX idx_open_issues_on_project_and_confidential_and_author_and_id ON issues USING btree (project_id, confidential, author_id, id) WHERE (state_id = 1);
CREATE INDEX idx_packages_debian_group_component_files_on_architecture_id ON packages_debian_group_component_files USING btree (architecture_id);
@@ -31338,6 +31441,12 @@ CREATE INDEX index_application_settings_on_usage_stats_set_by_user_id ON applica
CREATE INDEX index_applicationsettings_on_instance_administration_project_id ON application_settings USING btree (instance_administration_project_id);
+CREATE INDEX index_approval_group_rules_groups_on_group_id ON approval_group_rules_groups USING btree (group_id);
+
+CREATE INDEX index_approval_group_rules_on_scan_result_policy_id ON approval_group_rules USING btree (scan_result_policy_id);
+
+CREATE INDEX index_approval_group_rules_users_on_user_id ON approval_group_rules_users USING btree (user_id);
+
CREATE UNIQUE INDEX index_approval_merge_request_rule_sources_1 ON approval_merge_request_rule_sources USING btree (approval_merge_request_rule_id);
CREATE INDEX index_approval_merge_request_rule_sources_2 ON approval_merge_request_rule_sources USING btree (approval_project_rule_id);
@@ -36775,6 +36884,9 @@ ALTER TABLE ONLY remote_development_agent_configs
ALTER TABLE ONLY dast_sites
ADD CONSTRAINT fk_0a57f2271b FOREIGN KEY (dast_site_validation_id) REFERENCES dast_site_validations(id) ON DELETE SET NULL;
+ALTER TABLE ONLY approval_group_rules_protected_branches
+ ADD CONSTRAINT fk_0b85e6c388 FOREIGN KEY (protected_branch_id) REFERENCES protected_branches(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY issue_customer_relations_contacts
ADD CONSTRAINT fk_0c0037f723 FOREIGN KEY (issue_id) REFERENCES issues(id) ON DELETE CASCADE;
@@ -36805,6 +36917,9 @@ ALTER TABLE ONLY vulnerabilities
ALTER TABLE ONLY vulnerabilities
ADD CONSTRAINT fk_131d289c65 FOREIGN KEY (milestone_id) REFERENCES milestones(id) ON DELETE SET NULL;
+ALTER TABLE ONLY approval_group_rules
+ ADD CONSTRAINT fk_1485c451e3 FOREIGN KEY (scan_result_policy_id) REFERENCES scan_result_policies(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY catalog_resource_versions
ADD CONSTRAINT fk_15376d917e FOREIGN KEY (release_id) REFERENCES releases(id) ON DELETE CASCADE;
@@ -37054,12 +37169,18 @@ ALTER TABLE ONLY user_achievements
ALTER TABLE ONLY vulnerability_reads
ADD CONSTRAINT fk_4f593f6c62 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+ALTER TABLE ONLY approval_group_rules_protected_branches
+ ADD CONSTRAINT fk_4f85f13b20 FOREIGN KEY (approval_group_rule_id) REFERENCES approval_group_rules(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY project_compliance_standards_adherence
ADD CONSTRAINT fk_4fd1d9d9b0 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE SET NULL;
ALTER TABLE ONLY vulnerability_reads
ADD CONSTRAINT fk_5001652292 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY approval_group_rules_groups
+ ADD CONSTRAINT fk_50edc8134e FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY alert_management_alerts
ADD CONSTRAINT fk_51ab4b6089 FOREIGN KEY (prometheus_alert_id) REFERENCES prometheus_alerts(id) ON DELETE CASCADE;
@@ -37135,6 +37256,9 @@ ALTER TABLE ONLY vulnerability_reads
ALTER TABLE ONLY merge_requests
ADD CONSTRAINT fk_641731faff FOREIGN KEY (updated_by_id) REFERENCES users(id) ON DELETE SET NULL;
+ALTER TABLE ONLY approval_group_rules
+ ADD CONSTRAINT fk_64450bea52 FOREIGN KEY (security_orchestration_policy_configuration_id) REFERENCES security_orchestration_policy_configurations(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY ci_pipeline_chat_data
ADD CONSTRAINT fk_64ebfab6b3 FOREIGN KEY (pipeline_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE;
@@ -37285,6 +37409,9 @@ ALTER TABLE ONLY packages_package_files
ALTER TABLE p_ci_builds
ADD CONSTRAINT fk_87f4cefcda FOREIGN KEY (upstream_pipeline_id) REFERENCES ci_pipelines(id) ON DELETE CASCADE;
+ALTER TABLE ONLY approval_group_rules_users
+ ADD CONSTRAINT fk_888a0df3b7 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY vulnerabilities
ADD CONSTRAINT fk_88b4d546ef FOREIGN KEY (start_date_sourcing_milestone_id) REFERENCES milestones(id) ON DELETE SET NULL;
@@ -37354,6 +37481,9 @@ ALTER TABLE ONLY protected_branch_merge_access_levels
ALTER TABLE ONLY notes
ADD CONSTRAINT fk_99e097b079 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY approval_group_rules_users
+ ADD CONSTRAINT fk_9a4b673183 FOREIGN KEY (approval_group_rule_id) REFERENCES approval_group_rules(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY import_failures
ADD CONSTRAINT fk_9a9b9ba21c FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
@@ -37759,6 +37889,9 @@ ALTER TABLE ONLY approval_project_rules
ALTER TABLE ONLY vulnerabilities
ADD CONSTRAINT fk_efb96ab1e2 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY approval_group_rules_groups
+ ADD CONSTRAINT fk_efff219a48 FOREIGN KEY (approval_group_rule_id) REFERENCES approval_group_rules(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY emails
ADD CONSTRAINT fk_emails_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
@@ -38584,6 +38717,9 @@ ALTER TABLE ONLY namespace_admin_notes
ALTER TABLE ONLY ci_runner_machines
ADD CONSTRAINT fk_rails_666b61f04f FOREIGN KEY (runner_id) REFERENCES ci_runners(id) ON DELETE CASCADE;
+ALTER TABLE ONLY approval_group_rules
+ ADD CONSTRAINT fk_rails_6727675176 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY jira_imports
ADD CONSTRAINT fk_rails_675d38c03b FOREIGN KEY (label_id) REFERENCES labels(id) ON DELETE SET NULL;
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 247a1c18992..af314f0c0df 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -3034,6 +3034,31 @@ Input type: `DeleteAnnotationInput`
| <a id="mutationdeleteannotationclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationdeleteannotationerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+### `Mutation.deletePackagesProtectionRule`
+
+Deletes a protection rule for packages. Available only when feature flag `packages_protected_packages` is enabled.
+
+WARNING:
+**Introduced** in 16.6.
+This feature is an Experiment. It can be changed or removed at any time.
+
+Input type: `DeletePackagesProtectionRuleInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationdeletepackagesprotectionruleclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationdeletepackagesprotectionruleid"></a>`id` | [`PackagesProtectionRuleID!`](#packagesprotectionruleid) | Global ID of the package protection rule to delete. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationdeletepackagesprotectionruleclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationdeletepackagesprotectionruleerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+| <a id="mutationdeletepackagesprotectionrulepackageprotectionrule"></a>`packageProtectionRule` | [`PackagesProtectionRule`](#packagesprotectionrule) | Packages protection rule that was deleted successfully. |
+
### `Mutation.designManagementDelete`
Input type: `DesignManagementDeleteInput`
@@ -30746,6 +30771,12 @@ A `PackagesPackageID` is a global ID. It is encoded as a string.
An example `PackagesPackageID` is: `"gid://gitlab/Packages::Package/1"`.
+### `PackagesProtectionRuleID`
+
+A `PackagesProtectionRuleID` is a global ID. It is encoded as a string.
+
+An example `PackagesProtectionRuleID` is: `"gid://gitlab/Packages::Protection::Rule/1"`.
+
### `PackagesPypiMetadatumID`
A `PackagesPypiMetadatumID` is a global ID. It is encoded as a string.
diff --git a/doc/user/ai_features.md b/doc/user/ai_features.md
index 0033ecd2949..de081a7f620 100644
--- a/doc/user/ai_features.md
+++ b/doc/user/ai_features.md
@@ -12,19 +12,19 @@ GitLab is creating AI-assisted features across our DevSecOps platform. These fea
| Feature | Purpose | Large Language Model | Current availability | Maturity |
|-|-|-|-|-|
| [Suggested Reviewers](project/merge_requests/reviews/index.md#gitlab-duo-suggested-reviewers) | Assists in creating faster and higher-quality reviews by automatically suggesting reviewers for your merge request. | GitLab creates a machine learning model for each project, which is used to generate reviewers <br><br> [View the issue](https://gitlab.com/gitlab-org/modelops/applied-ml/applied-ml-updates/-/issues/10) | SaaS only <br><br> Ultimate tier | [Generally Available (GA)](../policy/experiment-beta-support.md#generally-available-ga) |
-| [Code Suggestions](project/repository/code_suggestions/index.md) | Helps you write code more efficiently by viewing code suggestions as you type. | [`code-gecko`](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/code-completion) and [`code-bison`](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/code-generation) <br><br> [Anthropic's Claude](https://www.anthropic.com/product) model | SaaS <br> Self-managed <br><br> All tiers | [Beta](../policy/experiment-beta-support.md#beta) |
-| [Vulnerability summary](application_security/vulnerabilities/index.md#explaining-a-vulnerability) | Helps you remediate vulnerabilities more efficiently, uplevel your skills, and write more secure code. | [`text-bison`](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/text) <br><br> Anthropic's claude model if degraded performance | SaaS only <br><br> Ultimate tier | [Beta](../policy/experiment-beta-support.md#beta) |
-| [Code explanation](#explain-code-in-the-web-ui-with-code-explanation) | Helps you understand code by explaining it in English language. | [`codechat-bison`](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/code-chat) | SaaS only <br><br> Ultimate tier | [Experiment](../policy/experiment-beta-support.md#experiment) |
-| [GitLab Duo Chat](gitlab_duo_chat.md) | Process and generate text and code in a conversational manner. Helps you quickly identify useful information in large volumes of text in issues, epics, code, and GitLab documentation. | [Anthropic's claude model](https://www.anthropic.com/product) <br><br> [`textembedding-gecko`](https://cloud.google.com/vertex-ai/docs/generative-ai/embeddings/get-text-embeddings) | SaaS only <br><br> Ultimate tier | [Experiment](../policy/experiment-beta-support.md#experiment) |
+| [Code Suggestions](project/repository/code_suggestions/index.md) | Helps you write code more efficiently by viewing code suggestions as you type. | For Code Completion: Vertext AI Codey [`code-gecko`](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/code-completion) and Anthropic [`Claude-instant-1.2`](https://docs.anthropic.com/claude/reference/selecting-a-model) <br><br> For Code Generation: Vertext AI Codey [`code-bison`](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/code-generation) and Anthropic [`Claude-2`](https://docs.anthropic.com/claude/reference/selecting-a-model)| [SaaS: All tiers](project/repository/code_suggestions/saas.md) <br><br> [Self-managed: Premium and Ultimate with Cloud Licensing](project/repository/code_suggestions/self_managed.md) | [Beta](../policy/experiment-beta-support.md#beta) |
+| [Vulnerability summary](application_security/vulnerabilities/index.md#explaining-a-vulnerability) | Helps you remediate vulnerabilities more efficiently, boost your skills, and write more secure code. | Vertext AI Codey [`text-bison`](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/text) <br><br> Anthropic [`Claude-2`](https://docs.anthropic.com/claude/reference/selecting-a-model) if degraded performance | SaaS only <br><br> Ultimate tier | [Beta](../policy/experiment-beta-support.md#beta) |
+| [Code explanation](#explain-code-in-the-web-ui-with-code-explanation) | Helps you understand code by explaining it in English language. | Vertext AI Codey [`codechat-bison`](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/code-chat) | SaaS only <br><br> Ultimate tier | [Experiment](../policy/experiment-beta-support.md#experiment) |
+| [GitLab Duo Chat](gitlab_duo_chat.md) | Process and generate text and code in a conversational manner. Helps you quickly identify useful information in large volumes of text in issues, epics, code, and GitLab documentation. | Anthropic [`Claude-2`](https://docs.anthropic.com/claude/reference/selecting-a-model) <br><br> Vertext AI Codey [`textembedding-gecko`](https://cloud.google.com/vertex-ai/docs/generative-ai/embeddings/get-text-embeddings) | SaaS only <br><br> Ultimate tier | [Experiment](../policy/experiment-beta-support.md#experiment) |
| [Value stream forecasting](#forecast-deployment-frequency-with-value-stream-forecasting) | Assists you with predicting productivity metrics and identifying anomalies across your software development lifecycle. | Statistical forecasting | SaaS only <br> Self-managed <br><br> Ultimate tier | [Experiment](../policy/experiment-beta-support.md#experiment) |
-| [Discussion summary](#summarize-issue-discussions-with-discussion-summary) | Assists with quickly getting everyone up to speed on lengthy conversations to help ensure you are all on the same page. | [`text-bison`](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/text) | SaaS only <br><br> Ultimate tier | [Experiment](../policy/experiment-beta-support.md#experiment) |
-| [Merge request summary](project/merge_requests/ai_in_merge_requests.md#summarize-merge-request-changes) | Efficiently communicate the impact of your merge request changes. | [`text-bison`](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/text) | SaaS only <br><br> Ultimate tier | [Experiment](../policy/experiment-beta-support.md#experiment) |
-| [Code review summary](project/merge_requests/ai_in_merge_requests.md#summarize-my-merge-request-review) | Helps ease merge request handoff between authors and reviewers and help reviewers efficiently understand suggestions. | [`text-bison`](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/text) | SaaS only <br><br> Ultimate tier | [Experiment](../policy/experiment-beta-support.md#experiment) |
-| [Merge request template population](project/merge_requests/ai_in_merge_requests.md#fill-in-merge-request-templates) | Generate a description for the merge request based on the contents of the template. | [`text-bison`](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/text) | SaaS only <br><br> Ultimate tier | [Experiment](../policy/experiment-beta-support.md#experiment) |
-| [Test generation](project/merge_requests/ai_in_merge_requests.md#generate-suggested-tests-in-merge-requests) | Automates repetitive tasks and helps catch bugs early. | [`text-bison`](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/text) | SaaS only <br><br> Ultimate tier | [Experiment](../policy/experiment-beta-support.md#experiment) |
-| [Git suggestions](https://gitlab.com/gitlab-org/gitlab/-/issues/409636) | Helps you discover or recall Git commands when and where you need them. | [Google Vertex Codey APIs](https://cloud.google.com/vertex-ai/docs/generative-ai/code/code-models-overview) | SaaS only <br><br> Ultimate tier | [Experiment](../policy/experiment-beta-support.md#experiment) |
-| [Root cause analysis](#root-cause-analysis) | Assists you in determining the root cause for a pipeline failure and failed CI/CD build. | [Google Vertex Codey APIs](https://cloud.google.com/vertex-ai/docs/generative-ai/code/code-models-overview) | SaaS only <br><br> Ultimate tier | [Experiment](../policy/experiment-beta-support.md#experiment) |
-| [Issue description generation](#summarize-an-issue-with-issue-description-generation) | Generate issue descriptions. | OpenAI's GPT-3 | SaaS only <br><br> Ultimate tier | [Experiment](../policy/experiment-beta-support.md#experiment) |
+| [Discussion summary](#summarize-issue-discussions-with-discussion-summary) | Assists with quickly getting everyone up to speed on lengthy conversations to help ensure you are all on the same page. | Vertext AI Codey [`text-bison`](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/text) | SaaS only <br><br> Ultimate tier | [Experiment](../policy/experiment-beta-support.md#experiment) |
+| [Merge request summary](project/merge_requests/ai_in_merge_requests.md#summarize-merge-request-changes) | Efficiently communicate the impact of your merge request changes. | Vertext AI Codey [`text-bison`](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/text) | SaaS only <br><br> Ultimate tier | [Experiment](../policy/experiment-beta-support.md#experiment) |
+| [Code review summary](project/merge_requests/ai_in_merge_requests.md#summarize-my-merge-request-review) | Helps ease merge request handoff between authors and reviewers and help reviewers efficiently understand suggestions. | Vertext AI Codey [`text-bison`](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/text) | SaaS only <br><br> Ultimate tier | [Experiment](../policy/experiment-beta-support.md#experiment) |
+| [Merge request template population](project/merge_requests/ai_in_merge_requests.md#fill-in-merge-request-templates) | Generate a description for the merge request based on the contents of the template. | Vertext AI Codey [`text-bison`](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/text) | SaaS only <br><br> Ultimate tier | [Experiment](../policy/experiment-beta-support.md#experiment) |
+| [Test generation](project/merge_requests/ai_in_merge_requests.md#generate-suggested-tests-in-merge-requests) | Automates repetitive tasks and helps catch bugs early. | Vertext AI Codey [`text-bison`](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/text) | SaaS only <br><br> Ultimate tier | [Experiment](../policy/experiment-beta-support.md#experiment) |
+| [Git suggestions](https://gitlab.com/gitlab-org/gitlab/-/issues/409636) | Helps you discover or recall Git commands when and where you need them. | Vertext AI Codey [`codechat-bison`](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/code-chat) | SaaS only <br><br> Ultimate tier | [Experiment](../policy/experiment-beta-support.md#experiment) |
+| [Root cause analysis](#root-cause-analysis) | Assists you in determining the root cause for a pipeline failure and failed CI/CD build. | Vertext AI Codey [`text-bison`](https://cloud.google.com/vertex-ai/docs/generative-ai/model-reference/text) | SaaS only <br><br> Ultimate tier | [Experiment](../policy/experiment-beta-support.md#experiment) |
+| [Issue description generation](#summarize-an-issue-with-issue-description-generation) | Generate issue descriptions. | OpenAI's [`GPT-3`](https://platform.openai.com/docs/models/gpt-3) | SaaS only <br><br> Ultimate tier | [Experiment](../policy/experiment-beta-support.md#experiment) |
## Enable AI/ML features
diff --git a/lib/gitlab/github_import/milestone_finder.rb b/lib/gitlab/github_import/milestone_finder.rb
index d9290e36ea1..dcb679fda6d 100644
--- a/lib/gitlab/github_import/milestone_finder.rb
+++ b/lib/gitlab/github_import/milestone_finder.rb
@@ -7,6 +7,7 @@ module Gitlab
# The base cache key to use for storing/retrieving milestone IDs.
CACHE_KEY = 'github-import/milestone-finder/%{project}/%{iid}'
+ CACHE_OBJECT_NOT_FOUND = -1
# project - An instance of `Project`
def initialize(project)
@@ -18,7 +19,20 @@ module Gitlab
def id_for(issuable)
return unless issuable.milestone_number
- Gitlab::Cache::Import::Caching.read_integer(cache_key_for(issuable.milestone_number))
+ milestone_iid = issuable.milestone_number
+ cache_key = cache_key_for(milestone_iid)
+
+ val = Gitlab::Cache::Import::Caching.read_integer(cache_key)
+
+ return val if Feature.disabled?(:import_fallback_to_db_empty_cache, project)
+
+ return if val == CACHE_OBJECT_NOT_FOUND
+ return val if val.present?
+
+ object_id = project.milestones.by_iid(milestone_iid).pick(:id) || CACHE_OBJECT_NOT_FOUND
+
+ Gitlab::Cache::Import::Caching.write(cache_key, object_id)
+ object_id == CACHE_OBJECT_NOT_FOUND ? nil : object_id
end
# rubocop: disable CodeReuse/ActiveRecord
@@ -35,7 +49,7 @@ module Gitlab
# rubocop: enable CodeReuse/ActiveRecord
def cache_key_for(iid)
- CACHE_KEY % { project: project.id, iid: iid }
+ format(CACHE_KEY, project: project.id, iid: iid)
end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 6f1a247ba6c..685abd710b6 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -50704,6 +50704,9 @@ msgstr ""
msgid "Unauthorized to create an environment"
msgstr ""
+msgid "Unauthorized to delete a package protection rule"
+msgstr ""
+
msgid "Unauthorized to update the environment"
msgstr ""
@@ -53824,9 +53827,6 @@ msgstr ""
msgid "Wiki|Create New Page"
msgstr ""
-msgid "Wiki|Created date"
-msgstr ""
-
msgid "Wiki|Edit Page"
msgstr ""
@@ -53845,9 +53845,6 @@ msgstr ""
msgid "Wiki|The sidebar failed to load. You can reload the page to try again."
msgstr ""
-msgid "Wiki|Title"
-msgstr ""
-
msgid "Wiki|View All Pages"
msgstr ""
diff --git a/package.json b/package.json
index 6ab5e20903f..ef9148f2489 100644
--- a/package.json
+++ b/package.json
@@ -60,7 +60,7 @@
"@gitlab/cluster-client": "^2.0.0",
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/fonts": "^1.3.0",
- "@gitlab/svgs": "3.66.0",
+ "@gitlab/svgs": "3.67.0",
"@gitlab/ui": "66.36.1",
"@gitlab/visual-review-tools": "1.7.3",
"@gitlab/web-ide": "0.0.1-dev-20231004090414",
diff --git a/qa/qa/page/admin/settings/metrics_and_profiling.rb b/qa/qa/page/admin/settings/metrics_and_profiling.rb
index aa2399ba810..2e919dd171a 100644
--- a/qa/qa/page/admin/settings/metrics_and_profiling.rb
+++ b/qa/qa/page/admin/settings/metrics_and_profiling.rb
@@ -9,7 +9,7 @@ module QA
view 'app/views/admin/application_settings/metrics_and_profiling.html.haml' do
element 'performance-bar-settings-content'
- element :usage_statistics_settings_content
+ element 'usage-statistics-settings-content'
end
def expand_performance_bar(&block)
@@ -19,7 +19,7 @@ module QA
end
def expand_usage_statistics(&block)
- expand_content(:usage_statistics_settings_content) do
+ expand_content('usage-statistics-settings-content') do
Component::UsageStatistics.perform(&block)
end
end
diff --git a/qa/qa/page/component/import/gitlab.rb b/qa/qa/page/component/import/gitlab.rb
index 1cb8a099a70..6c5516dd2e2 100644
--- a/qa/qa/page/component/import/gitlab.rb
+++ b/qa/qa/page/component/import/gitlab.rb
@@ -11,17 +11,16 @@ module QA
super
base.view 'app/views/import/gitlab_projects/new.html.haml' do
- element :import_project_button
+ element 'import-project-button'
end
base.view 'app/views/import/shared/_new_project_form.html.haml' do
- element :project_name_field
- element :project_slug_field
+ element 'project-name-field'
end
end
def set_imported_project_name(name)
- fill_element(:project_name_field, name)
+ fill_element('project-name-field', name)
end
def attach_exported_file(path)
@@ -29,7 +28,7 @@ module QA
end
def click_import_gitlab_project
- click_element(:import_project_button)
+ click_element('import-project-button')
wait_until(reload: false) do
has_notice?("The project was successfully imported.") || has_element?('project-name-content')
diff --git a/qa/qa/page/component/import/selection.rb b/qa/qa/page/component/import/selection.rb
index db2ff74e0f8..bd3268c3b16 100644
--- a/qa/qa/page/component/import/selection.rb
+++ b/qa/qa/page/component/import/selection.rb
@@ -9,16 +9,16 @@ module QA
super
base.view 'app/views/projects/_import_project_pane.html.haml' do
- element :gitlab_import_button
+ element 'gitlab-import-button'
end
end
def click_gitlab
retry_until(reload: true, max_attempts: 10, message: 'Waiting for import source to be enabled') do
- has_element?(:gitlab_import_button)
+ has_element?('gitlab-import-button')
end
- click_element(:gitlab_import_button)
+ click_element('gitlab-import-button')
end
end
end
diff --git a/qa/qa/page/main/menu.rb b/qa/qa/page/main/menu.rb
index 2413166e120..9900343600a 100644
--- a/qa/qa/page/main/menu.rb
+++ b/qa/qa/page/main/menu.rb
@@ -112,7 +112,7 @@ module QA
end
def signed_in_as_user?(user)
- return false unless has_personal_area?
+ return false unless signed_in?
within_user_menu do
has_element?('user-profile-link', text: /#{user.username}/)
diff --git a/qa/qa/page/project/web_ide/vscode.rb b/qa/qa/page/project/web_ide/vscode.rb
index 74194b85ebe..0997064c19f 100644
--- a/qa/qa/page/project/web_ide/vscode.rb
+++ b/qa/qa/page/project/web_ide/vscode.rb
@@ -40,6 +40,10 @@ module QA
click_element('span[aria-label="New Folder..."]')
end
+ def has_committed_and_pushed_successfully?
+ page.has_css?('.span[title="Success! Your changes have been committed."]')
+ end
+
def click_upload_menu_item
click_element('span[aria-label="Upload..."]')
end
@@ -88,6 +92,10 @@ module QA
click_element('.monaco-button[title="Create new branch"]')
end
+ def click_continue_with_existing_branch
+ page.find('.monaco-button[title="Continue"]').click
+ end
+
def has_branch_input_field?
has_element?('input[aria-label="input"]')
end
@@ -103,6 +111,32 @@ module QA
page.within_frame(iframe, &block)
end
+ def click_new_file_menu_item
+ page.find('[aria-label="New File..."]').click
+ end
+
+ def switch_to_original_window
+ page.driver.browser.switch_to.window(page.driver.browser.window_handles.first)
+ end
+
+ def create_new_file_from_template(filename, template)
+ within_vscode_editor do
+ Support::Waiter.wait_until(max_duration: 20, retry_on_exception: true) do
+ click_new_file_menu_item
+ enter_new_file_text_input(filename)
+ page.within('div.editor-container') do
+ page.find('textarea.inputarea.monaco-mouse-cursor-text').send_keys(template)
+ end
+ page.has_content?(filename)
+ end
+ end
+ end
+
+ def enter_new_file_text_input(name)
+ page.find('.explorer-item-edited', visible: true)
+ send_keys(name, :enter)
+ end
+
# Used for stablility, due to feature_caching of vscode_web_ide
def wait_for_ide_to_load
page.driver.browser.switch_to.window(page.driver.browser.window_handles.last)
@@ -157,6 +191,13 @@ module QA
end
end
+ def push_to_existing_branch
+ within_vscode_editor do
+ click_continue_with_existing_branch
+ has_committed_and_pushed_successfully?
+ end
+ end
+
def push_to_new_branch
within_vscode_editor do
click_new_branch
diff --git a/qa/qa/page/search/results.rb b/qa/qa/page/search/results.rb
index 9e56d000070..f9e5e97b96f 100644
--- a/qa/qa/page/search/results.rb
+++ b/qa/qa/page/search/results.rb
@@ -20,21 +20,26 @@ module QA
end
def switch_to_code
- switch_to_tab(:code_tab)
+ click_element(:nav_item_link, submenu_item: 'Code')
end
def switch_to_projects
switch_to_tab(:projects_tab)
end
- def has_file_in_project?(file_name, project_name)
- has_element?(:result_item_content, text: "#{project_name}: #{file_name}")
+ def has_project_in_search_result?(project_name)
+ has_element?(:result_item_content, text: project_name)
end
- def has_file_with_content?(file_name, file_text)
- within_element_by_index(:result_item_content, 0) do
- break false unless has_element?(:file_title_content, text: file_name)
+ def has_file_in_project?(file_name, project_name)
+ within_element(:result_item_content, text: project_name) do
+ has_element?(:file_title_content, text: file_name)
+ end
+ end
+ def has_file_in_project_with_content?(file_text, file_path)
+ within_element(:result_item_content,
+ text: file_path) do
has_element?(:file_text_content, text: file_text)
end
end
diff --git a/spec/factories/packages/nuget/symbol.rb b/spec/factories/packages/nuget/symbol.rb
index 7ab1e026cda..665535de939 100644
--- a/spec/factories/packages/nuget/symbol.rb
+++ b/spec/factories/packages/nuget/symbol.rb
@@ -7,5 +7,6 @@ FactoryBot.define do
file_path { 'lib/net7.0/package.pdb' }
size { 100.bytes }
sequence(:signature) { |n| "b91a152048fc4b3883bf3cf73fbc03f#{n}FFFFFFFF" }
+ file_sha256 { 'dd1aaf26c557685cc37f93f53a2b6befb2c2e679f5ace6ec7a26d12086f358be' }
end
end
diff --git a/spec/frontend/ci/catalog/components/details/ci_resource_components_spec.js b/spec/frontend/ci/catalog/components/details/ci_resource_components_spec.js
index a41996d20b3..93f9f0b7e23 100644
--- a/spec/frontend/ci/catalog/components/details/ci_resource_components_spec.js
+++ b/spec/frontend/ci/catalog/components/details/ci_resource_components_spec.js
@@ -38,6 +38,7 @@ describe('CiResourceComponents', () => {
};
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+ const findCopyToClipboardButton = (i) => wrapper.findAllByTestId('copy-to-clipboard').at(i);
const findComponents = () => wrapper.findAllByTestId('component-section');
beforeEach(() => {
@@ -98,6 +99,15 @@ describe('CiResourceComponents', () => {
});
});
+ it('adds a copy-to-clipboard button', () => {
+ components.forEach((component, i) => {
+ const button = findCopyToClipboardButton(i);
+
+ expect(button.props().icon).toBe('copy-to-clipboard');
+ expect(button.attributes('data-clipboard-text')).toContain(component.path);
+ });
+ });
+
describe('inputs', () => {
it('renders the component parameter attributes', () => {
const [firstComponent] = components;
diff --git a/spec/helpers/wiki_helper_spec.rb b/spec/helpers/wiki_helper_spec.rb
index 6eaa603a43d..31bbd394ac1 100644
--- a/spec/helpers/wiki_helper_spec.rb
+++ b/spec/helpers/wiki_helper_spec.rb
@@ -115,17 +115,6 @@ RSpec.describe WikiHelper, feature_category: :wiki do
end
end
- describe '#wiki_sort_title' do
- it 'returns a title corresponding to a key' do
- expect(helper.wiki_sort_title('created_at')).to eq('Created date')
- expect(helper.wiki_sort_title('title')).to eq('Title')
- end
-
- it 'defaults to Title if a key is unknown' do
- expect(helper.wiki_sort_title('unknown')).to eq('Title')
- end
- end
-
describe '#wiki_page_tracking_context' do
let_it_be(:page) { create(:wiki_page, title: 'path/to/page 💩', content: '💩', format: :markdown) }
diff --git a/spec/lib/gitlab/github_import/milestone_finder_spec.rb b/spec/lib/gitlab/github_import/milestone_finder_spec.rb
index e7f47d334e8..62886981de1 100644
--- a/spec/lib/gitlab/github_import/milestone_finder_spec.rb
+++ b/spec/lib/gitlab/github_import/milestone_finder_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::GithubImport::MilestoneFinder, :clean_gitlab_redis_cache do
+RSpec.describe Gitlab::GithubImport::MilestoneFinder, :clean_gitlab_redis_cache, feature_category: :importers do
let_it_be(:project) { create(:project) }
let_it_be(:milestone) { create(:milestone, project: project) }
@@ -20,23 +20,72 @@ RSpec.describe Gitlab::GithubImport::MilestoneFinder, :clean_gitlab_redis_cache
expect(finder.id_for(issuable)).to eq(milestone.id)
end
- it 'returns nil for an empty cache key' do
+ it 'returns nil if object does not exist' do
+ missing_issuable = double(:issuable, milestone_number: 999)
+
+ expect(finder.id_for(missing_issuable)).to be_nil
+ end
+
+ it 'fetches object id from database if not in cache' do
key = finder.cache_key_for(milestone.iid)
Gitlab::Cache::Import::Caching.write(key, '')
- expect(finder.id_for(issuable)).to be_nil
+ expect(finder.id_for(issuable)).to eq(milestone.id)
end
it 'returns nil for an issuable with a non-existing milestone' do
expect(finder.id_for(double(:issuable, milestone_number: 5))).to be_nil
end
+
+ it 'returns nil and skips database read if cache has no record' do
+ key = finder.cache_key_for(milestone.iid)
+
+ Gitlab::Cache::Import::Caching.write(key, -1)
+
+ expect(finder.id_for(issuable)).to be_nil
+ end
end
context 'without a cache in place' do
- it 'returns nil' do
+ it 'caches the ID of a database row and returns the ID' do
+ expect(Gitlab::Cache::Import::Caching)
+ .to receive(:write)
+ .with("github-import/milestone-finder/#{project.id}/1", milestone.id)
+ .and_call_original
+
+ expect(finder.id_for(issuable)).to eq(milestone.id)
+ end
+ end
+
+ context 'with FF import_fallback_to_db_empty_cache disabled' do
+ before do
+ stub_feature_flags(import_fallback_to_db_empty_cache: false)
+ end
+
+ it 'returns nil if object does not exist' do
+ missing_issuable = double(:issuable, milestone_number: 999)
+
+ expect(finder.id_for(missing_issuable)).to be_nil
+ end
+
+ it 'does not fetch object id from database if not in cache' do
expect(finder.id_for(issuable)).to be_nil
end
+
+ it 'fetches object id from cache if present' do
+ finder.build_cache
+
+ expect(finder.id_for(issuable)).to eq(milestone.id)
+ end
+
+ it 'returns -1 if cache is -1' do
+ key = finder.cache_key_for(milestone.iid)
+
+ Gitlab::Cache::Import::Caching.write(key, -1)
+
+ expect(finder.id_for(issuable)).to eq(-1)
+ end
end
end
diff --git a/spec/models/packages/nuget/symbol_spec.rb b/spec/models/packages/nuget/symbol_spec.rb
index 52e95c11939..f43f3a3bdeb 100644
--- a/spec/models/packages/nuget/symbol_spec.rb
+++ b/spec/models/packages/nuget/symbol_spec.rb
@@ -6,6 +6,7 @@ RSpec.describe Packages::Nuget::Symbol, type: :model, feature_category: :package
subject(:symbol) { create(:nuget_symbol) }
it { is_expected.to be_a FileStoreMounter }
+ it { is_expected.to be_a ShaAttribute }
describe 'relationships' do
it { is_expected.to belong_to(:package).inverse_of(:nuget_symbols) }
diff --git a/spec/policies/concerns/policy_actor_spec.rb b/spec/policies/concerns/policy_actor_spec.rb
index 7fd9db67032..a38725d73d6 100644
--- a/spec/policies/concerns/policy_actor_spec.rb
+++ b/spec/policies/concerns/policy_actor_spec.rb
@@ -20,10 +20,4 @@ RSpec.describe PolicyActor, feature_category: :shared do
# initialized. So here we just use an instance
expect(build(:user).methods).to include(*methods)
end
-
- describe '#security_policy_bot?' do
- subject { PolicyActorTestClass.new.security_policy_bot? }
-
- it { is_expected.to eq(false) }
- end
end
diff --git a/spec/policies/global_policy_spec.rb b/spec/policies/global_policy_spec.rb
index 475e8f981dd..52fea8d782e 100644
--- a/spec/policies/global_policy_spec.rb
+++ b/spec/policies/global_policy_spec.rb
@@ -10,7 +10,6 @@ RSpec.describe GlobalPolicy, feature_category: :shared do
let_it_be(:service_account) { create(:user, :service_account) }
let_it_be(:migration_bot) { create(:user, :migration_bot) }
let_it_be(:security_bot) { create(:user, :security_bot) }
- let_it_be(:security_policy_bot) { create(:user, :security_policy_bot) }
let_it_be(:llm_bot) { create(:user, :llm_bot) }
let_it_be_with_reload(:current_user) { create(:user) }
let_it_be(:user) { create(:user) }
@@ -411,12 +410,6 @@ RSpec.describe GlobalPolicy, feature_category: :shared do
it { is_expected.to be_allowed(:access_git) }
end
- context 'security policy bot' do
- let(:current_user) { security_policy_bot }
-
- it { is_expected.to be_allowed(:access_git) }
- end
-
describe 'deactivated user' do
before do
current_user.deactivate
diff --git a/spec/requests/api/graphql/mutations/packages/protection/rule/delete_spec.rb b/spec/requests/api/graphql/mutations/packages/protection/rule/delete_spec.rb
new file mode 100644
index 00000000000..d5b602d4664
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/packages/protection/rule/delete_spec.rb
@@ -0,0 +1,86 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Deleting a package protection rule', :aggregate_failures, feature_category: :package_registry do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be_with_refind(:package_protection_rule) { create(:package_protection_rule, project: project) }
+ let_it_be(:current_user) { create(:user, maintainer_projects: [project]) }
+
+ let(:mutation) { graphql_mutation(:delete_packages_protection_rule, input) }
+ let(:mutation_response) { graphql_mutation_response(:delete_packages_protection_rule) }
+ let(:input) { { id: package_protection_rule.to_global_id } }
+
+ subject { post_graphql_mutation(mutation, current_user: current_user) }
+
+ shared_examples 'an erroneous reponse' do
+ it { subject.tap { expect(mutation_response).to be_blank } }
+ it { expect { subject }.not_to change { ::Packages::Protection::Rule.count } }
+ end
+
+ it_behaves_like 'a working GraphQL mutation'
+
+ it 'responds with deleted package protection rule' do
+ subject
+
+ expect(mutation_response).to include(
+ 'packageProtectionRule' => {
+ 'packageNamePattern' => package_protection_rule.package_name_pattern,
+ 'packageType' => package_protection_rule.package_type.upcase,
+ 'pushProtectedUpToAccessLevel' => package_protection_rule.push_protected_up_to_access_level.upcase
+ }, 'errors' => be_blank
+ )
+ end
+
+ it { is_expected.tap { expect_graphql_errors_to_be_empty } }
+ it { expect { subject }.to change { ::Packages::Protection::Rule.count }.from(1).to(0) }
+
+ context 'with existing package protection rule belonging to other project' do
+ let_it_be(:package_protection_rule) do
+ create(:package_protection_rule, package_name_pattern: 'protection_rule_other_project')
+ end
+
+ it_behaves_like 'an erroneous reponse'
+
+ it { subject.tap { expect_graphql_errors_to_include(/you don't have permission to perform this action/) } }
+ end
+
+ context 'with deleted package protection rule' do
+ let!(:package_protection_rule) do
+ create(:package_protection_rule, project: project, package_name_pattern: 'protection_rule_deleted').destroy!
+ end
+
+ it_behaves_like 'an erroneous reponse'
+
+ it { subject.tap { expect_graphql_errors_to_include(/you don't have permission to perform this action/) } }
+ end
+
+ context 'when current_user does not have permission' do
+ let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
+ let_it_be(:reporter) { create(:user).tap { |u| project.add_reporter(u) } }
+ let_it_be(:guest) { create(:user).tap { |u| project.add_guest(u) } }
+ let_it_be(:anonymous) { create(:user) }
+
+ where(:current_user) do
+ [ref(:developer), ref(:reporter), ref(:guest), ref(:anonymous)]
+ end
+
+ with_them do
+ it_behaves_like 'an erroneous reponse'
+
+ it { subject.tap { expect_graphql_errors_to_include(/you don't have permission to perform this action/) } }
+ end
+ end
+
+ context "when feature flag ':packages_protected_packages' disabled" do
+ before do
+ stub_feature_flags(packages_protected_packages: false)
+ end
+
+ it_behaves_like 'an erroneous reponse'
+
+ it { subject.tap { expect_graphql_errors_to_include(/'packages_protected_packages' feature flag is disabled/) } }
+ end
+end
diff --git a/spec/services/packages/nuget/symbols/create_symbol_files_service_spec.rb b/spec/services/packages/nuget/symbols/create_symbol_files_service_spec.rb
index 97bfc3e06a8..96cc46884af 100644
--- a/spec/services/packages/nuget/symbols/create_symbol_files_service_spec.rb
+++ b/spec/services/packages/nuget/symbols/create_symbol_files_service_spec.rb
@@ -15,9 +15,9 @@ RSpec.describe Packages::Nuget::Symbols::CreateSymbolFilesService, feature_categ
describe '#execute' do
subject { service.execute }
- shared_examples 'logs an error' do |error_class|
- it 'logs an error' do
- expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
+ shared_examples 'logging an error' do |error_class|
+ it 'logs the error' do
+ expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
an_instance_of(error_class),
class: described_class.name,
package_id: package.id
@@ -29,8 +29,8 @@ RSpec.describe Packages::Nuget::Symbols::CreateSymbolFilesService, feature_categ
context 'when symbol files are found' do
it 'creates a symbol record and extracts the signature' do
- expect_next_instance_of(Packages::Nuget::Symbols::ExtractSymbolSignatureService,
- instance_of(String)) do |service|
+ expect_next_instance_of(Packages::Nuget::Symbols::ExtractSignatureAndChecksumService,
+ instance_of(File)) do |service|
expect(service).to receive(:execute).and_call_original
end
@@ -47,13 +47,13 @@ RSpec.describe Packages::Nuget::Symbols::CreateSymbolFilesService, feature_categ
expect { subject }.not_to change { package.nuget_symbols.count }
end
- it_behaves_like 'logs an error', described_class::ExtractionError
+ it_behaves_like 'logging an error', described_class::ExtractionError
end
- context 'when creating a symbol record without a signature' do
+ context 'without a signature' do
before do
- allow_next_instance_of(Packages::Nuget::Symbols::ExtractSymbolSignatureService) do |instance|
- allow(instance).to receive(:execute).and_return(ServiceResponse.success(payload: nil))
+ allow_next_instance_of(Packages::Nuget::Symbols::ExtractSignatureAndChecksumService) do |instance|
+ allow(instance).to receive(:execute).and_return(ServiceResponse.success(payload: { signature: nil }))
end
end
@@ -64,12 +64,28 @@ RSpec.describe Packages::Nuget::Symbols::CreateSymbolFilesService, feature_categ
end
end
- context 'when creating duplicate symbol records' do
+ context 'without a checksum' do
+ before do
+ allow_next_instance_of(Packages::Nuget::Symbols::ExtractSignatureAndChecksumService) do |instance|
+ allow(instance).to receive(:execute).and_return(ServiceResponse.success(payload: { checksum: nil }))
+ end
+ end
+
+ it 'does not call create! on the symbol record' do
+ expect(::Packages::Nuget::Symbol).not_to receive(:create!)
+
+ subject
+ end
+ end
+
+ context 'with existing duplicate symbol records' do
let_it_be(:symbol) { create(:nuget_symbol, package: package) }
before do
- allow_next_instance_of(Packages::Nuget::Symbols::ExtractSymbolSignatureService) do |instance|
- allow(instance).to receive(:execute).and_return(ServiceResponse.success(payload: symbol.signature))
+ allow_next_instance_of(Packages::Nuget::Symbols::ExtractSignatureAndChecksumService) do |instance|
+ allow(instance).to receive(:execute).and_return(
+ ServiceResponse.success(payload: { signature: symbol.signature, checksum: symbol.file_sha256 })
+ )
end
end
@@ -77,7 +93,7 @@ RSpec.describe Packages::Nuget::Symbols::CreateSymbolFilesService, feature_categ
expect { subject }.not_to change { package.nuget_symbols.count }
end
- it_behaves_like 'logs an error', ActiveRecord::RecordInvalid
+ it_behaves_like 'logging an error', ActiveRecord::RecordInvalid
end
context 'when a symbol file has the wrong entry size' do
@@ -87,7 +103,7 @@ RSpec.describe Packages::Nuget::Symbols::CreateSymbolFilesService, feature_categ
end
end
- it_behaves_like 'logs an error', described_class::ExtractionError
+ it_behaves_like 'logging an error', described_class::ExtractionError
end
context 'when a symbol file has the wrong entry name' do
@@ -97,7 +113,7 @@ RSpec.describe Packages::Nuget::Symbols::CreateSymbolFilesService, feature_categ
end
end
- it_behaves_like 'logs an error', described_class::ExtractionError
+ it_behaves_like 'logging an error', described_class::ExtractionError
end
end
end
diff --git a/spec/services/packages/nuget/symbols/extract_signature_and_checksum_service_spec.rb b/spec/services/packages/nuget/symbols/extract_signature_and_checksum_service_spec.rb
new file mode 100644
index 00000000000..210b92dfce5
--- /dev/null
+++ b/spec/services/packages/nuget/symbols/extract_signature_and_checksum_service_spec.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::Nuget::Symbols::ExtractSignatureAndChecksumService, feature_category: :package_registry do
+ let_it_be(:symbol_file_path) { expand_fixture_path('packages/nuget/symbol/package.pdb') }
+ let(:symbol_file) { File.new(symbol_file_path) }
+
+ let(:service) { described_class.new(symbol_file) }
+
+ after do
+ symbol_file.close
+ end
+
+ describe '#execute' do
+ subject { service.execute }
+
+ context 'with a valid symbol file' do
+ it 'returns the signature and checksum' do
+ payload = subject.payload
+
+ expect(payload[:signature]).to eq('b91a152048fc4b3883bf3cf73fbc03f1FFFFFFFF')
+ expect(payload[:checksum]).to eq('20151ab9fc48384b83bf3cf73fbc03f1d49166cc356139845f290d1d315256c0')
+ end
+
+ it 'reads the file in chunks' do
+ expect(symbol_file).to receive(:read).with(described_class::GUID_CHUNK_SIZE).and_call_original
+ expect(symbol_file).to receive(:read).with(described_class::SHA_CHUNK_SIZE, instance_of(String))
+ .at_least(:once).and_call_original
+
+ subject
+ end
+ end
+
+ context 'with an invalid symbol file' do
+ before do
+ allow(symbol_file).to receive(:read).and_return('invalid')
+ end
+
+ it 'returns an error' do
+ expect(subject).to be_error
+ expect(subject.message).to eq('Could not find the signature in the symbol file')
+ end
+ end
+ end
+end
diff --git a/spec/services/packages/nuget/symbols/extract_symbol_signature_service_spec.rb b/spec/services/packages/nuget/symbols/extract_symbol_signature_service_spec.rb
deleted file mode 100644
index 87b0d00a0a7..00000000000
--- a/spec/services/packages/nuget/symbols/extract_symbol_signature_service_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Packages::Nuget::Symbols::ExtractSymbolSignatureService, feature_category: :package_registry do
- let_it_be(:symbol_file) { fixture_file('packages/nuget/symbol/package.pdb') }
-
- let(:service) { described_class.new(symbol_file) }
-
- describe '#execute' do
- subject { service.execute }
-
- context 'with a valid symbol file' do
- it { expect(subject.payload).to eq('b91a152048fc4b3883bf3cf73fbc03f1FFFFFFFF') }
- end
-
- context 'with corrupted data' do
- let(:symbol_file) { 'corrupted data' }
-
- it { expect(subject).to be_error }
- end
- end
-end
diff --git a/spec/services/packages/protection/delete_rule_service_spec.rb b/spec/services/packages/protection/delete_rule_service_spec.rb
new file mode 100644
index 00000000000..d64609d4df1
--- /dev/null
+++ b/spec/services/packages/protection/delete_rule_service_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::Protection::DeleteRuleService, '#execute', feature_category: :package_registry do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:current_user) { create(:user, maintainer_projects: [project]) }
+ let_it_be_with_refind(:package_protection_rule) { create(:package_protection_rule, project: project) }
+
+ subject { described_class.new(package_protection_rule, current_user: current_user).execute }
+
+ shared_examples 'a successful service response' do
+ it { is_expected.to be_success }
+
+ it {
+ is_expected.to have_attributes(
+ errors: be_blank,
+ message: be_blank,
+ payload: { package_protection_rule: package_protection_rule }
+ )
+ }
+
+ it { subject.tap { expect { package_protection_rule.reload }.to raise_error ActiveRecord::RecordNotFound } }
+ end
+
+ shared_examples 'an erroneous service response' do
+ it { is_expected.to be_error }
+ it { is_expected.to have_attributes(message: be_present, payload: { package_protection_rule: be_blank }) }
+
+ it do
+ expect { subject }.not_to change { Packages::Protection::Rule.count }
+
+ expect { package_protection_rule.reload }.not_to raise_error
+ end
+ end
+
+ it_behaves_like 'a successful service response'
+
+ it 'deletes the package protection rule in the database' do
+ expect { subject }
+ .to change { project.reload.package_protection_rules }.from([package_protection_rule]).to([])
+ .and change { ::Packages::Protection::Rule.count }.from(1).to(0)
+ end
+
+ context 'with deleted package protection rule' do
+ let!(:package_protection_rule) do
+ create(:package_protection_rule, project: project, package_name_pattern: 'protection_rule_deleted').destroy!
+ end
+
+ it_behaves_like 'a successful service response'
+ end
+
+ context 'when error occurs during delete operation' do
+ before do
+ allow(package_protection_rule).to receive(:destroy!).and_raise(StandardError.new('Some error'))
+ end
+
+ it_behaves_like 'an erroneous service response'
+
+ it { is_expected.to have_attributes message: /Some error/ }
+ end
+
+ context 'when current_user does not have permission' do
+ let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
+ let_it_be(:reporter) { create(:user).tap { |u| project.add_reporter(u) } }
+ let_it_be(:guest) { create(:user).tap { |u| project.add_guest(u) } }
+ let_it_be(:anonymous) { create(:user) }
+
+ where(:current_user) do
+ [ref(:developer), ref(:reporter), ref(:guest), ref(:anonymous)]
+ end
+
+ with_them do
+ it_behaves_like 'an erroneous service response'
+
+ it { is_expected.to have_attributes message: /Unauthorized to delete a package protection rule/ }
+ end
+ end
+
+ context 'without package protection rule' do
+ let(:package_protection_rule) { nil }
+
+ it { expect { subject }.to raise_error(ArgumentError) }
+ end
+
+ context 'without current_user' do
+ let(:current_user) { nil }
+ let(:package_protection_rule) { build_stubbed(:package_protection_rule, project: project) }
+
+ it { expect { subject }.to raise_error(ArgumentError) }
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index d08f0f2e2ba..e16fbc5cefd 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -301,6 +301,8 @@ RSpec.configure do |config|
# https://gitlab.com/gitlab-org/gitlab/-/issues/385453
stub_feature_flags(vscode_web_ide: false)
+ stub_feature_flags(ai_global_switch: false)
+
enable_rugged = example.metadata[:enable_rugged].present?
# Disable Rugged features by default
diff --git a/spec/support/shared_examples/services/protected_branches_shared_examples.rb b/spec/support/shared_examples/services/protected_branches_shared_examples.rb
index 15c63865720..51653a7460a 100644
--- a/spec/support/shared_examples/services/protected_branches_shared_examples.rb
+++ b/spec/support/shared_examples/services/protected_branches_shared_examples.rb
@@ -23,3 +23,34 @@ RSpec.shared_context 'with scan result policy blocking protected branches' do
stub_licensed_features(security_orchestration_policies: true)
end
end
+
+RSpec.shared_context 'with scan result policy preventing force pushing' do
+ include RepoHelpers
+
+ let(:policy_path) { Security::OrchestrationPolicyConfiguration::POLICY_PATH }
+ let(:default_branch) { policy_project.default_branch }
+ let(:prevent_force_pushing) { true }
+
+ let(:scan_result_policy) do
+ build(:scan_result_policy, branches: [branch_name],
+ approval_settings: { prevent_force_pushing: prevent_force_pushing })
+ end
+
+ let(:policy_yaml) do
+ build(:orchestration_policy_yaml, scan_result_policy: [scan_result_policy])
+ end
+
+ before do
+ create_file_in_repo(policy_project, default_branch, default_branch, policy_path, policy_yaml)
+ stub_licensed_features(security_orchestration_policies: true)
+ end
+
+ after do
+ policy_project.repository.delete_file(
+ policy_project.creator,
+ policy_path,
+ message: 'Automatically deleted policy',
+ branch_name: default_branch
+ )
+ end
+end
diff --git a/yarn.lock b/yarn.lock
index 31156439fea..2c0fe97b749 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1269,10 +1269,10 @@
stylelint-declaration-strict-value "1.9.2"
stylelint-scss "5.1.0"
-"@gitlab/svgs@3.66.0":
- version "3.66.0"
- resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.66.0.tgz#5dbe98f9811001942d78395756b9d7c588300c01"
- integrity sha512-FdkoMAprxjJJnl90GJYoCMeIpvCaYPNAnRkrlsmo7NY3Ce8fpRb/XE/ZakqULeadj82S7R1IRuTHYfWB06vVtA==
+"@gitlab/svgs@3.67.0":
+ version "3.67.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.67.0.tgz#72dc27e6b611acf6bdae2d7074fc413f24da6f1f"
+ integrity sha512-N2otHyWJqnUpJcP/lz//KdAD6VV6za7zv7QQqpD9v/p/dzCneyEc4t52g09ERHV71kv9/pWOVPqqwk9eQ2iz9A==
"@gitlab/ui@66.36.1":
version "66.36.1"