Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
62866a623e
commit
098444d917
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ export default {
|
|||
showAiActions() {
|
||||
return (
|
||||
this.resourceGlobalId &&
|
||||
this.glFeatures.openaiExperimentation &&
|
||||
(this.glFeatures.openaiExperimentation || this.glFeatures.aiGlobalSwitch) &&
|
||||
this.glFeatures.summarizeNotes
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -53,10 +53,6 @@ module PolicyActor
|
|||
false
|
||||
end
|
||||
|
||||
def security_policy_bot?
|
||||
false
|
||||
end
|
||||
|
||||
def automation_bot?
|
||||
false
|
||||
end
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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? }
|
||||
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1 @@
|
|||
0018bc2180eeb632d75132b6d82e959e772ff1e7d8966310858e304d07d4ec34
|
||||
|
|
@ -0,0 +1 @@
|
|||
e2b4cdafd6147740ad43c286d90f7feec9f70d66a510d58a3cc3c33b0d703b49
|
||||
|
|
@ -0,0 +1 @@
|
|||
d3d90178100e92cffe263715cdfc3c9ddcb47ce804f3ffd92d5bc4326de0244c
|
||||
|
|
@ -0,0 +1 @@
|
|||
840bc159c277271b66f4348c31d912485c04b8ee1b15227c96dcc690f6b93311
|
||||
|
|
@ -0,0 +1 @@
|
|||
9a560649866367e556cf841e20f981b6c09fe03d1054f0db37cb510fbfbaef13
|
||||
|
|
@ -0,0 +1 @@
|
|||
eba011de5a174a93e5159c765c093d3a6519111769a1ac09b2f996322cf3973e
|
||||
|
|
@ -0,0 +1 @@
|
|||
f9659a07b4c7b2d4508f1de231e759cf4e15e684ecaa4231ff6069b4ba203e20
|
||||
|
|
@ -0,0 +1 @@
|
|||
9df85930f78c6fa9e02252877d136aab3167a8ac1134cbd321c26f5958899f06
|
||||
|
|
@ -0,0 +1 @@
|
|||
0be5d3565d71dc9656fd90dbd404ea0314ff29f6da9ca9ef2d100bcc9515308b
|
||||
|
|
@ -0,0 +1 @@
|
|||
021dbeb0a8c5ebecfa647344b1e99dd1698ae3fb72a8857409551070b23f9f49
|
||||
|
|
@ -0,0 +1 @@
|
|||
71e2f63bf9a327f62d21c2407b9ccebe779e0fd881266467f180cf285edc326f
|
||||
|
|
@ -0,0 +1 @@
|
|||
1e9bf34cc708dd8637e4e636894fb9b7894c6d54832b3b42c88af17c4ed87532
|
||||
|
|
@ -0,0 +1 @@
|
|||
e2ee8bcb49b470bbea1874f6a63c9b7a2fd67ef4223cf5d358de1fca4e3f36be
|
||||
136
db/structure.sql
136
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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ""
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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}/)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Reference in New Issue